2

I'm trying to parse an EC Point

04410478ed041c12ddaf693958f91f1174e0c790b2ff580ddca39bc2a4f78ad041dc379aaefe27cede2fa7601f90e3f397938ee53268564e346ac7a58aac8c28ca5415

to a ecdsa.PublickKey struct with the following code:

    x, y := readECPoint(elliptic.P256(), point)
    if x == nil || y == nil {
        panic("unable to parse the public key")
    }
    pubKey := &ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y}

    pubKeyBytes := elliptic.Marshal(curve, pubKey.X, pubKey.Y)

Browsing on github I noticed this piece of code, which made sense to me:

func readECPoint(curve elliptic.Curve, ecpoint []byte) (*big.Int, *big.Int) {
    x, y := elliptic.Unmarshal(curve, ecpoint)
    if x == nil {
        // http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/os/pkcs11-curr-v2.40-os.html#_ftn1
        // PKCS#11 v2.20 specified that the CKA_EC_POINT was to be store in a DER-encoded
        // OCTET STRING.
        var point asn1.RawValue
        asn1.Unmarshal(ecpoint, &point)
        if len(point.Bytes) > 0 {
            x, y = elliptic.Unmarshal(curve, point.Bytes)
        }
    }
    return x, y
}

Other things I tried were:

curve := new(secp256k1.BitCurve)
x, y := curve.Unmarshal(point)

However all those previous code always returns with x=nil and y=nil

I know the KeyPair was generated in an HSM with the bitcoin curve

asn1.ObjectIdentifier 1.3.132.0.10

Am I missing something else to properly parse an EC Point from a bitcoin/ethereum curve?

Jorge Alvarado
  • 2,664
  • 22
  • 33
  • 1
    [P256](https://golang.org/pkg/crypto/elliptic/#P256) from the _elliptic_ package denotes NIST P-256 aka secp256r1. The curve used by Bitcoin is secp256**k**1. So if the posted public key is a point on the Bitcoin curve, it is understandable that the code fails. Inconsistent with both is the OID 1.3.132.0.34, which denotes secp384r1 (aka P-384), [here](http://oid-info.com/cgi-bin/display?oid=1.3.132.0.34&a=display). – Topaco Mar 14 '21 at 09:57
  • 2
    For secp256k1 the public key in uncompressed format should have a length of 65 bytes, but the posted key has a length of 67 bytes. The first two bytes are probably the ASN.1 encoding for an octet string (0x04) of length 65 bytes (0x41), so the actual key starts from the 3rd byte: 0478ed... Thus, my suggestion would be to use a library that supports secp256k1 and apply the 65 bytes key 0478ed... – Topaco Mar 14 '21 at 10:00
  • @Topaco yes you're right, that was a typo from a different OID, I updated the question with the right value, and as you mentioned, I had to find the specific curve struct to build the pub key and indeed it worked. – Jorge Alvarado Mar 14 '21 at 10:03

2 Answers2

1

The first code snippet seems wrong, because elliptic.P256() returns a Curve which implements NIST P-256 also known as secp256r1 or prime256v1. But the bitcoin/etherium curve uses secp256k1 format, which is different.

I suggest to use the go-etherium package to create the secp256k1 curve.

I also found a problem with your key. It has a length of 67, but the size of the public key must be 65 (see the source code). As Topaco pointed out, the first two bytes are probably the ASN.1 encoding for an octet string (0x04) of length 65 bytes (0x41), so the actual key starts from the 3rd byte.

I wrote this minimal example that parses your key using go-etherium:

package main

import (
    "encoding/hex"
    "fmt"

    "github.com/ethereum/go-ethereum/crypto/secp256k1"
)

func main() {
    c := *secp256k1.S256()
    data, err := hex.DecodeString("04410478ed041c12ddaf693958f91f1174e0c790b2ff580ddca39bc2a4f78ad041dc379aaefe27cede2fa7601f90e3f397938ee53268564e346ac7a58aac8c28ca5415")
    if err != nil {
        panic(err)
    }
    x, y := c.Unmarshal(data[2:])
    fmt.Printf("%v\n%v\n", x, y)
}

Output:

54696312948195154784868816119600719747374302831648955727311433079671352319031
69965364187890201668291488802478374883818025489090614849107393112609590498325
jubnzv
  • 1,526
  • 2
  • 8
  • 20
0

I finally found a way, though more manual than what I thought, maybe someone has a better way?

curve := new(secp256k1.BitCurve)

    pubKey := ecdsa.PublicKey{
        Curve: curve,
        X:     &big.Int{},
        Y:     &big.Int{},
    }
    xBytes := point[3:35] //the point array has 67 bytes, ignore the first 2
    yBytes := point[35:]

    pubKey.X = new(big.Int).SetBytes(xBytes)
    pubKey.Y = new(big.Int).SetBytes(yBytes)
Jorge Alvarado
  • 2,664
  • 22
  • 33