-
Notifications
You must be signed in to change notification settings - Fork 0
Add secp256k1eth pkcs11 support [1/4] #24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,10 +9,10 @@ import ( | |||||||||||
| "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" | ||||||||||||
| ) | ||||||||||||
|
|
||||||||||||
| // decodeRS parses a canonical DER ECDSA signature into (r, s) as mod-N scalars | ||||||||||||
| // decodeRSDER parses a canonical DER ECDSA signature into (r, s) as mod-N scalars | ||||||||||||
| // and normalizes s to low-S. ModNScalar reduces mod the curve order, so range | ||||||||||||
| // validation rides on the overflow flag from SetByteSlice. | ||||||||||||
| func decodeRS(der []byte) (r, s secp256k1.ModNScalar, err error) { | ||||||||||||
| func decodeRSDER(der []byte) (r, s secp256k1.ModNScalar, err error) { | ||||||||||||
| var sig struct{ R, S *big.Int } | ||||||||||||
| rest, err := asn1.Unmarshal(der, &sig) | ||||||||||||
| if err != nil { | ||||||||||||
|
|
@@ -34,10 +34,39 @@ func decodeRS(der []byte) (r, s secp256k1.ModNScalar, err error) { | |||||||||||
| return r, s, nil | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // decodeRSCompact parses a raw fixed-width r‖s signature (2×32 bytes) | ||||||||||||
| // into (r, s) as mod-N scalars and normalizes s to low-S. | ||||||||||||
| func decodeRSCompact(rs []byte) (r, s secp256k1.ModNScalar, err error) { | ||||||||||||
| if len(rs) != 64 { | ||||||||||||
| return r, s, fmt.Errorf("ecdsasig: raw signature must be 64 bytes, got %d", len(rs)) | ||||||||||||
| } | ||||||||||||
| if r.SetByteSlice(rs[:32]) || s.SetByteSlice(rs[32:]) { | ||||||||||||
| return r, s, fmt.Errorf("ecdsasig: signature r or s is >= curve order") | ||||||||||||
| } | ||||||||||||
| if r.IsZero() || s.IsZero() { | ||||||||||||
| return r, s, fmt.Errorf("ecdsasig: signature r and s must be nonzero") | ||||||||||||
| } | ||||||||||||
| // Low-S: collapse the malleable high half so the result is canonical. | ||||||||||||
| if s.IsOverHalfOrder() { | ||||||||||||
| s.Negate() | ||||||||||||
| } | ||||||||||||
| return r, s, nil | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // RecoverCompact decodes an r‖s signature (2x32 bytes) | ||||||||||||
| // and returns r‖s‖v (65 bytes) with recovery byte and low-S normalized. | ||||||||||||
| func RecoverCompact(rs, digest []byte, pub *secp256k1.PublicKey) ([]byte, error) { | ||||||||||||
| r, s, err := decodeRSCompact(rs) | ||||||||||||
| if err != nil { | ||||||||||||
| return nil, err | ||||||||||||
| } | ||||||||||||
| return recoverSig(r, s, digest, pub) | ||||||||||||
| } | ||||||||||||
|
Comment on lines
+56
to
+64
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||
|
|
||||||||||||
| // ConsensusSig converts a DER (r,s) signature into the 64-byte r‖s low-S form | ||||||||||||
| // cometbft secp256k1 consensus verification requires. | ||||||||||||
| func ConsensusSig(der []byte) ([]byte, error) { | ||||||||||||
| r, s, err := decodeRS(der) | ||||||||||||
| r, s, err := decodeRSDER(der) | ||||||||||||
| if err != nil { | ||||||||||||
| return nil, err | ||||||||||||
| } | ||||||||||||
|
|
@@ -48,20 +77,26 @@ func ConsensusSig(der []byte) ([]byte, error) { | |||||||||||
| return out, nil | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // RecoverableSig converts a DER (r,s) signature into the 65-byte | ||||||||||||
| // RecoverDER converts a DER (r,s) signature into the 65-byte | ||||||||||||
| // r‖s‖v recoverable form. Since the underlying signer does not return v, | ||||||||||||
| // it is found by trial-recovering pub from the (low-S normalized) signature | ||||||||||||
| // with each candidate. | ||||||||||||
| // An error is returned if neither candidate recovers pub, including the X-overflow case | ||||||||||||
| // (recid 2/3), which the SignerService 0/1-recovery-id protocol cannot carry. | ||||||||||||
| func RecoverableSig(der, digest []byte, pub *secp256k1.PublicKey) ([]byte, error) { | ||||||||||||
| func RecoverDER(der, digest []byte, pub *secp256k1.PublicKey) ([]byte, error) { | ||||||||||||
| if len(digest) != 32 { | ||||||||||||
| return nil, fmt.Errorf("ecdsasig: digest must be 32 bytes, got %d", len(digest)) | ||||||||||||
| } | ||||||||||||
| r, s, err := decodeRS(der) | ||||||||||||
| r, s, err := decodeRSDER(der) | ||||||||||||
| if err != nil { | ||||||||||||
| return nil, err | ||||||||||||
| } | ||||||||||||
| return recoverSig(r, s, digest, pub) | ||||||||||||
| } | ||||||||||||
|
|
||||||||||||
| // recoverSig takes r,s ModNScalar as well as the associated pubkey and | ||||||||||||
| // returns 65 byte r‖s‖v signature with low-S normalized and recover byte set | ||||||||||||
| func recoverSig(r, s secp256k1.ModNScalar, digest []byte, pub *secp256k1.PublicKey) ([]byte, error) { | ||||||||||||
| rb, sb := r.Bytes(), s.Bytes() | ||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
|
|
||||||||||||
| // decred compact form is <27+recid>‖R‖S; isCompressedKey=false ⇒ no +4. | ||||||||||||
|
|
||||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -5,6 +5,8 @@ import ( | |||||
| "fmt" | ||||||
|
|
||||||
| "github.com/cosmos/kms/config" | ||||||
| "github.com/cosmos/kms/signing/ecdsasig" | ||||||
| "github.com/decred/dcrd/dcrec/secp256k1/v4" | ||||||
| "github.com/miekg/pkcs11" | ||||||
| ) | ||||||
|
|
||||||
|
|
@@ -23,7 +25,7 @@ type keyAlgo struct { | |||||
| name config.Algorithm | ||||||
| mechanism func() []*pkcs11.Mechanism | ||||||
| decodePub func(ckaECPoint []byte) ([]byte, error) | ||||||
| fixSig func(raw []byte) ([]byte, error) | ||||||
| fixSig func(raw, digest, pub []byte) ([]byte, error) | ||||||
| } | ||||||
|
|
||||||
| // algos is the registry of supported key algorithms, keyed by the config | ||||||
|
|
@@ -33,10 +35,24 @@ var algos = map[config.Algorithm]keyAlgo{ | |||||
| name: config.AlgoED25519, | ||||||
| mechanism: func() []*pkcs11.Mechanism { return []*pkcs11.Mechanism{pkcs11.NewMechanism(ckmEDDSA, nil)} }, | ||||||
| decodePub: decodeEd25519Pub, | ||||||
| fixSig: func(raw []byte) ([]byte, error) { return raw, nil }, | ||||||
| fixSig: func(raw, digest, pub []byte) ([]byte, error) { return raw, nil }, | ||||||
| }, | ||||||
| config.AlgoSecp256k1Eth: { | ||||||
| name: config.AlgoSecp256k1Eth, | ||||||
| mechanism: func() []*pkcs11.Mechanism { return []*pkcs11.Mechanism{pkcs11.NewMechanism(pkcs11.CKM_ECDSA, nil)} }, | ||||||
| decodePub: decodeSecp256k1Pub, | ||||||
| fixSig: recoverSig, | ||||||
| }, | ||||||
| } | ||||||
|
|
||||||
| func recoverSig(raw, digest, pub []byte) ([]byte, error) { | ||||||
| dpub, err := secp256k1.ParsePubKey(pub) | ||||||
| if err != nil { | ||||||
| return nil, fmt.Errorf("parse secp256k1 public key: %w", err) | ||||||
| } | ||||||
| return ecdsasig.RecoverCompact(raw, digest, dpub) | ||||||
| } | ||||||
|
|
||||||
| // decodeEd25519Pub turns a CKA_EC_POINT value into aa byte array. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time! |
||||||
| // PKCS#11 v3.0 encodes the point as a DER OCTET STRING wrapping the 32-byte key | ||||||
| // (0x04 0x20 <32 bytes>); some tokens return the raw 32 bytes. Both are accepted. | ||||||
|
|
@@ -51,3 +67,21 @@ func decodeEd25519Pub(ckaECPoint []byte) ([]byte, error) { | |||||
| } | ||||||
| return raw, nil | ||||||
| } | ||||||
|
|
||||||
| // decodeSecp256k1Pub turns a CKA_EC_POINT value into the 33-byte compressed | ||||||
| // public key. PKCS#11 encodes the point as a DER OCTET STRING wrapping the | ||||||
| // SEC1 point (0x04‖X‖Y) either uncompressed or compressed. Both are accepted. | ||||||
| func decodeSecp256k1Pub(ckaECPoint []byte) ([]byte, error) { | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably want some tests for this |
||||||
| raw := ckaECPoint | ||||||
| // DER OCTET STRING (0x04) of length 65 uncompressed, or 33 compressed) | ||||||
| // wrapping the SEC1 point. A bare uncompressed point also starts with x04 | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| // but is 65 bytes, never 67 or 35. | ||||||
| if wrapped := len(raw) - 2; (wrapped == 65 || wrapped == 33) && raw[0] == 0x04 && int(raw[1]) == wrapped { | ||||||
| raw = raw[2:] | ||||||
| } | ||||||
| pub, err := secp256k1.ParsePubKey(raw) | ||||||
| if err != nil { | ||||||
| return nil, fmt.Errorf("secp256k1 CKA_EC_POINT: %w", err) | ||||||
| } | ||||||
| return pub.SerializeCompressed(), nil | ||||||
| } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for my knowledge, what is the difference here between or when would i want to use
RecoverCompactvsRecoverDER? They both seem to be doing the same thing (taking a 64 byte (r||s) signature and recovering the v, returning (r||s||v). I'm assuming the input signature differs in some way but I can't exactly tell how from the comments. (this is just a skill issue question)