Source File
signature.go
Belonging Package
github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa
// Copyright (c) 2013-2014 The btcsuite developers// Copyright (c) 2015-2024 The Decred developers// Use of this source code is governed by an ISC// license that can be found in the LICENSE file.package ecdsaimport ()// References:// [GECC]: Guide to Elliptic Curve Cryptography (Hankerson, Menezes, Vanstone)//// [ISO/IEC 8825-1]: Information technology — ASN.1 encoding rules:// Specification of Basic Encoding Rules (BER), Canonical Encoding Rules// (CER) and Distinguished Encoding Rules (DER)//// [SEC1]: Elliptic Curve Cryptography (May 31, 2009, Version 2.0)// https://www.secg.org/sec1-v2.pdfvar (// zero32 is an array of 32 bytes used for the purposes of zeroing and is// defined here to avoid extra allocations.zero32 = [32]byte{}// orderAsFieldVal is the order of the secp256k1 curve group stored as a// field value. It is provided here to avoid the need to create it multiple// times.orderAsFieldVal = func() secp256k1.FieldVal {var secp256k1.FieldVal.SetByteSlice(secp256k1.Params().N.Bytes())return}())const (// asn1SequenceID is the ASN.1 identifier for a sequence and is used when// parsing and serializing signatures encoded with the Distinguished// Encoding Rules (DER) format per section 10 of [ISO/IEC 8825-1].asn1SequenceID = 0x30// asn1IntegerID is the ASN.1 identifier for an integer and is used when// parsing and serializing signatures encoded with the Distinguished// Encoding Rules (DER) format per section 10 of [ISO/IEC 8825-1].asn1IntegerID = 0x02)// Signature is a type representing an ECDSA signature.type Signature struct {r secp256k1.ModNScalars secp256k1.ModNScalar}// NewSignature instantiates a new signature given some r and s values.func (, *secp256k1.ModNScalar) *Signature {return &Signature{*, *}}// R returns the r value of the signature.func ( *Signature) () secp256k1.ModNScalar {return .r}// S returns the s value of the signature.func ( *Signature) () secp256k1.ModNScalar {return .s}// Serialize returns the ECDSA signature in the Distinguished Encoding Rules// (DER) format per section 10 of [ISO/IEC 8825-1] and such that the S component// of the signature is less than or equal to the half order of the group.//// Note that the serialized bytes returned do not include the appended hash type// used in Decred signature scripts.func ( *Signature) () []byte {// The format of a DER encoded signature is as follows://// 0x30 <total length> 0x02 <length of R> <R> 0x02 <length of S> <S>// - 0x30 is the ASN.1 identifier for a sequence.// - Total length is 1 byte and specifies length of all remaining data.// - 0x02 is the ASN.1 identifier that specifies an integer follows.// - Length of R is 1 byte and specifies how many bytes R occupies.// - R is the arbitrary length big-endian encoded number which// represents the R value of the signature. DER encoding dictates// that the value must be encoded using the minimum possible number// of bytes. This implies the first byte can only be null if the// highest bit of the next byte is set in order to prevent it from// being interpreted as a negative number.// - 0x02 is once again the ASN.1 integer identifier.// - Length of S is 1 byte and specifies how many bytes S occupies.// - S is the arbitrary length big-endian encoded number which// represents the S value of the signature. The encoding rules are// identical as those for R.// Ensure the S component of the signature is less than or equal to the half// order of the group because both S and its negation are valid signatures// modulo the order, so this forces a consistent choice to reduce signature// malleability.:= new(secp256k1.ModNScalar).Set(&.s)if .IsOverHalfOrder() {.Negate()}// Serialize the R and S components of the signature into their fixed// 32-byte big-endian encoding. Note that the extra leading zero byte is// used to ensure it is canonical per DER and will be stripped if needed// below.var , [33]byte.r.PutBytesUnchecked([1:33]).PutBytesUnchecked([1:33])// Ensure the encoded bytes for the R and S components are canonical per DER// by trimming all leading zero bytes so long as the next byte does not have// the high bit set and it's not the final byte., := [:], [:]for len() > 1 && [0] == 0x00 && [1]&0x80 == 0 {= [1:]}for len() > 1 && [0] == 0x00 && [1]&0x80 == 0 {= [1:]}// Total length of returned signature is 1 byte for each magic and length// (6 total), plus lengths of R and S.:= 6 + len() + len():= make([]byte, 0, )= append(, asn1SequenceID)= append(, byte(-2))= append(, asn1IntegerID)= append(, byte(len()))= append(, ...)= append(, asn1IntegerID)= append(, byte(len()))= append(, ...)return}// zeroArray32 zeroes the provided 32-byte buffer.func zeroArray32( *[32]byte) {copy([:], zero32[:])}// fieldToModNScalar converts a field value to scalar modulo the group order and// returns the scalar along with either 1 if it was reduced (aka it overflowed)// or 0 otherwise.//// Note that a bool is not used here because it is not possible in Go to convert// from a bool to numeric value in constant time and many constant-time// operations require a numeric value.func fieldToModNScalar( *secp256k1.FieldVal) (secp256k1.ModNScalar, uint32) {var [32]byte.PutBytes(&)var secp256k1.ModNScalar:= .SetBytes(&)zeroArray32(&)return ,}// modNScalarToField converts a scalar modulo the group order to a field value.func modNScalarToField( *secp256k1.ModNScalar) secp256k1.FieldVal {var [32]byte.PutBytes(&)var secp256k1.FieldVal.SetBytes(&)return}// Verify returns whether or not the signature is valid for the provided hash// and secp256k1 public key.func ( *Signature) ( []byte, *secp256k1.PublicKey) bool {// The algorithm for verifying an ECDSA signature is given as algorithm 4.30// in [GECC].//// The following is a paraphrased version for reference://// G = curve generator// N = curve order// Q = public key// m = message// R, S = signature//// 1. Fail if R and S are not in [1, N-1]// 2. e = H(m)// 3. w = S^-1 mod N// 4. u1 = e * w mod N// u2 = R * w mod N// 5. X = u1G + u2Q// 6. Fail if X is the point at infinity// 7. x = X.x mod N (X.x is the x coordinate of X)// 8. Verified if x == R//// However, since all group operations are done internally in Jacobian// projective space, the algorithm is modified slightly here in order to// avoid an expensive inversion back into affine coordinates at step 7.// Credits to Greg Maxwell for originally suggesting this optimization.//// Ordinarily, step 7 involves converting the x coordinate to affine by// calculating x = x / z^2 (mod P) and then calculating the remainder as// x = x (mod N). Then step 8 compares it to R.//// Note that since R is the x coordinate mod N from a random point that was// originally mod P, and the cofactor of the secp256k1 curve is 1, there are// only two possible x coordinates that the original random point could have// been to produce R: x, where x < N, and x+N, where x+N < P.//// This implies that the signature is valid if either:// a) R == X.x / X.z^2 (mod P)// => R * X.z^2 == X.x (mod P)// --or--// b) R + N < P && R + N == X.x / X.z^2 (mod P)// => R + N < P && (R + N) * X.z^2 == X.x (mod P)//// Therefore the following modified algorithm is used://// 1. Fail if R and S are not in [1, N-1]// 2. e = H(m)// 3. w = S^-1 mod N// 4. u1 = e * w mod N// u2 = R * w mod N// 5. X = u1G + u2Q// 6. Fail if X is the point at infinity// 7. z = (X.z)^2 mod P (X.z is the z coordinate of X)// 8. Verified if R * z == X.x (mod P)// 9. Fail if R + N >= P// 10. Verified if (R + N) * z == X.x (mod P)// Step 1.//// Fail if R and S are not in [1, N-1].if .r.IsZero() || .s.IsZero() {return false}// Step 2.//// e = H(m)var secp256k1.ModNScalar.SetByteSlice()// Step 3.//// w = S^-1 mod N:= new(secp256k1.ModNScalar).InverseValNonConst(&.s)// Step 4.//// u1 = e * w mod N// u2 = R * w mod N:= new(secp256k1.ModNScalar).Mul2(&, ):= new(secp256k1.ModNScalar).Mul2(&.r, )// Step 5.//// X = u1G + u2Qvar , , , secp256k1.JacobianPoint.AsJacobian(&)secp256k1.ScalarBaseMultNonConst(, &)secp256k1.ScalarMultNonConst(, &, &)secp256k1.AddNonConst(&, &, &)// Step 6.//// Fail if X is the point at infinityif (.X.IsZero() && .Y.IsZero()) || .Z.IsZero() {return false}// Step 7.//// z = (X.z)^2 mod P (X.z is the z coordinate of X):= new(secp256k1.FieldVal).SquareVal(&.Z)// Step 8.//// Verified if R * z == X.x (mod P):= modNScalarToField(&.r):= new(secp256k1.FieldVal).Mul2(&, ).Normalize()if .Equals(&.X) {return true}// Step 9.//// Fail if R + N >= Pif .IsGtOrEqPrimeMinusOrder() {return false}// Step 10.//// Verified if (R + N) * z == X.x (mod P).Add(&orderAsFieldVal).Mul2(&, ).Normalize()return .Equals(&.X)}// IsEqual compares this Signature instance to the one passed, returning true if// both Signatures are equivalent. A signature is equivalent to another, if// they both have the same scalar value for R and S.func ( *Signature) ( *Signature) bool {return .r.Equals(&.r) && .s.Equals(&.s)}// ParseDERSignature parses a signature in the Distinguished Encoding Rules// (DER) format per section 10 of [ISO/IEC 8825-1] and enforces the following// additional restrictions specific to secp256k1://// - The R and S values must be in the valid range for secp256k1 scalars:// - Negative values are rejected// - Zero is rejected// - Values greater than or equal to the secp256k1 group order are rejectedfunc ( []byte) (*Signature, error) {// The format of a DER encoded signature for secp256k1 is as follows://// 0x30 <total length> 0x02 <length of R> <R> 0x02 <length of S> <S>// - 0x30 is the ASN.1 identifier for a sequence// - Total length is 1 byte and specifies length of all remaining data// - 0x02 is the ASN.1 identifier that specifies an integer follows// - Length of R is 1 byte and specifies how many bytes R occupies// - R is the arbitrary length big-endian encoded number which// represents the R value of the signature. DER encoding dictates// that the value must be encoded using the minimum possible number// of bytes. This implies the first byte can only be null if the// highest bit of the next byte is set in order to prevent it from// being interpreted as a negative number.// - 0x02 is once again the ASN.1 integer identifier// - Length of S is 1 byte and specifies how many bytes S occupies// - S is the arbitrary length big-endian encoded number which// represents the S value of the signature. The encoding rules are// identical as those for R.//// NOTE: The DER specification supports specifying lengths that can occupy// more than 1 byte, however, since this is specific to secp256k1// signatures, all lengths will be a single byte.const (// minSigLen is the minimum length of a DER encoded signature and is// when both R and S are 1 byte each.//// 0x30 + <1-byte> + 0x02 + 0x01 + <byte> + 0x2 + 0x01 + <byte>= 8// maxSigLen is the maximum length of a DER encoded signature and is// when both R and S are 33 bytes each. It is 33 bytes because a// 256-bit integer requires 32 bytes and an additional leading null byte// might be required if the high bit is set in the value.//// 0x30 + <1-byte> + 0x02 + 0x21 + <33 bytes> + 0x2 + 0x21 + <33 bytes>= 72// sequenceOffset is the byte offset within the signature of the// expected ASN.1 sequence identifier.= 0// dataLenOffset is the byte offset within the signature of the expected// total length of all remaining data in the signature.= 1// rTypeOffset is the byte offset within the signature of the ASN.1// identifier for R and is expected to indicate an ASN.1 integer.= 2// rLenOffset is the byte offset within the signature of the length of// R.= 3// rOffset is the byte offset within the signature of R.= 4)// The signature must adhere to the minimum and maximum allowed length.:= len()if < {:= fmt.Sprintf("malformed signature: too short: %d < %d", ,)return nil, signatureError(ErrSigTooShort, )}if > {:= fmt.Sprintf("malformed signature: too long: %d > %d", ,)return nil, signatureError(ErrSigTooLong, )}// The signature must start with the ASN.1 sequence identifier.if [] != asn1SequenceID {:= fmt.Sprintf("malformed signature: format has wrong type: %#x",[])return nil, signatureError(ErrSigInvalidSeqID, )}// The signature must indicate the correct amount of data for all elements// related to R and S.if int([]) != -2 {:= fmt.Sprintf("malformed signature: bad length: %d != %d",[], -2)return nil, signatureError(ErrSigInvalidDataLen, )}// Calculate the offsets of the elements related to S and ensure S is inside// the signature.//// rLen specifies the length of the big-endian encoded number which// represents the R value of the signature.//// sTypeOffset is the offset of the ASN.1 identifier for S and, like its R// counterpart, is expected to indicate an ASN.1 integer.//// sLenOffset and sOffset are the byte offsets within the signature of the// length of S and S itself, respectively.:= int([]):= +:= + 1if >= {:= "malformed signature: S type indicator missing"return nil, signatureError(ErrSigMissingSTypeID, )}if >= {:= "malformed signature: S length missing"return nil, signatureError(ErrSigMissingSLen, )}// The lengths of R and S must match the overall length of the signature.//// sLen specifies the length of the big-endian encoded number which// represents the S value of the signature.:= + 1:= int([])if + != {:= "malformed signature: invalid S length"return nil, signatureError(ErrSigInvalidSLen, )}// R elements must be ASN.1 integers.if [] != asn1IntegerID {:= fmt.Sprintf("malformed signature: R integer marker: %#x != %#x",[], asn1IntegerID)return nil, signatureError(ErrSigInvalidRIntID, )}// Zero-length integers are not allowed for R.if == 0 {:= "malformed signature: R length is zero"return nil, signatureError(ErrSigZeroRLen, )}// R must not be negative.if []&0x80 != 0 {:= "malformed signature: R is negative"return nil, signatureError(ErrSigNegativeR, )}// Null bytes at the start of R are not allowed, unless R would otherwise be// interpreted as a negative number.if > 1 && [] == 0x00 && [+1]&0x80 == 0 {:= "malformed signature: R value has too much padding"return nil, signatureError(ErrSigTooMuchRPadding, )}// S elements must be ASN.1 integers.if [] != asn1IntegerID {:= fmt.Sprintf("malformed signature: S integer marker: %#x != %#x",[], asn1IntegerID)return nil, signatureError(ErrSigInvalidSIntID, )}// Zero-length integers are not allowed for S.if == 0 {:= "malformed signature: S length is zero"return nil, signatureError(ErrSigZeroSLen, )}// S must not be negative.if []&0x80 != 0 {:= "malformed signature: S is negative"return nil, signatureError(ErrSigNegativeS, )}// Null bytes at the start of S are not allowed, unless S would otherwise be// interpreted as a negative number.if > 1 && [] == 0x00 && [+1]&0x80 == 0 {:= "malformed signature: S value has too much padding"return nil, signatureError(ErrSigTooMuchSPadding, )}// The signature is validly encoded per DER at this point, however, enforce// additional restrictions to ensure R and S are in the range [1, N-1] since// valid ECDSA signatures are required to be in that range per spec.//// Also note that while the overflow checks are required to make use of the// specialized mod N scalar type, rejecting zero here is not strictly// required because it is also checked when verifying the signature, but// there really isn't a good reason not to fail early here on signatures// that do not conform to the ECDSA spec.// Strip leading zeroes from R.:= [ : +]for len() > 0 && [0] == 0x00 {= [1:]}// R must be in the range [1, N-1]. Notice the check for the maximum number// of bytes is required because SetByteSlice truncates as noted in its// comment so it could otherwise fail to detect the overflow.var secp256k1.ModNScalarif len() > 32 {:= "invalid signature: R is larger than 256 bits"return nil, signatureError(ErrSigRTooBig, )}if := .SetByteSlice(); {:= "invalid signature: R >= group order"return nil, signatureError(ErrSigRTooBig, )}if .IsZero() {:= "invalid signature: R is 0"return nil, signatureError(ErrSigRIsZero, )}// Strip leading zeroes from S.:= [ : +]for len() > 0 && [0] == 0x00 {= [1:]}// S must be in the range [1, N-1]. Notice the check for the maximum number// of bytes is required because SetByteSlice truncates as noted in its// comment so it could otherwise fail to detect the overflow.var secp256k1.ModNScalarif len() > 32 {:= "invalid signature: S is larger than 256 bits"return nil, signatureError(ErrSigSTooBig, )}if := .SetByteSlice(); {:= "invalid signature: S >= group order"return nil, signatureError(ErrSigSTooBig, )}if .IsZero() {:= "invalid signature: S is 0"return nil, signatureError(ErrSigSIsZero, )}// Create and return the signature.return NewSignature(&, &), nil}// sign generates an ECDSA signature over the secp256k1 curve for the provided// hash (which should be the result of hashing a larger message) using the given// nonce and private key and returns it along with an additional public key// recovery code and success indicator. Upon success, the produced signature is// deterministic (same message, nonce, and key yield the same signature) and// canonical in accordance with BIP0062.//// Note that signRFC6979 makes use of this function as it is the primary ECDSA// signing logic. It differs in that it accepts a nonce to use when signing and// may not successfully produce a valid signature for the given nonce. It is// primarily separated for testing purposes.func sign(, *secp256k1.ModNScalar, []byte) (*Signature, byte, bool) {// The algorithm for producing an ECDSA signature is given as algorithm 4.29// in [GECC].//// The following is a paraphrased version for reference://// G = curve generator// N = curve order// d = private key// m = message// r, s = signature//// 1. Select random nonce k in [1, N-1]// 2. Compute kG// 3. r = kG.x mod N (kG.x is the x coordinate of the point kG)// Repeat from step 1 if r = 0// 4. e = H(m)// 5. s = k^-1(e + dr) mod N// Repeat from step 1 if s = 0// 6. Return (r,s)//// This is slightly modified here to conform to RFC6979 and BIP 62 as// follows://// A. Instead of selecting a random nonce in step 1, use RFC6979 to generate// a deterministic nonce in [1, N-1] parameterized by the private key,// message being signed, and an iteration count for the repeat cases// B. Negate s calculated in step 5 if it is > N/2// This is done because both s and its negation are valid signatures// modulo the curve order N, so it forces a consistent choice to reduce// signature malleability// NOTE: Step 1 is performed by the caller.//// Step 2.//// Compute kG//// Note that the point must be in affine coordinates.:=var secp256k1.JacobianPointsecp256k1.ScalarBaseMultNonConst(, &).ToAffine()// Step 3.//// r = kG.x mod N// Repeat from step 1 if r = 0, := fieldToModNScalar(&.X)if .IsZero() {return nil, 0, false}// Since the secp256k1 curve has a cofactor of 1, when recovering a// public key from an ECDSA signature over it, there are four possible// candidates corresponding to the following cases://// 1) The X coord of the random point is < N and its Y coord even// 2) The X coord of the random point is < N and its Y coord is odd// 3) The X coord of the random point is >= N and its Y coord is even// 4) The X coord of the random point is >= N and its Y coord is odd//// Rather than forcing the recovery procedure to check all possible// cases, this creates a recovery code that uniquely identifies which of// the cases apply by making use of 2 bits. Bit 0 identifies the// oddness case and Bit 1 identifies the overflow case (aka when the X// coord >= N).//// It is also worth noting that making use of Hasse's theorem shows// there are around log_2((p-n)/p) ~= -127.65 ~= 1 in 2^127 points where// the X coordinate is >= N. It is not possible to calculate these// points since that would require breaking the ECDLP, but, in practice// this strongly implies with extremely high probability that there are// only a few actual points for which this case is true.:= byte(<<1) | byte(.Y.IsOddBit())// Step 4.//// e = H(m)//// Note that this actually sets e = H(m) mod N which is correct since// it is only used in step 5 which itself is mod N.var secp256k1.ModNScalar.SetByteSlice()// Step 5 with modification B.//// s = k^-1(e + dr) mod N// Repeat from step 1 if s = 0// s = -s if s > N/2:= new(secp256k1.ModNScalar).InverseValNonConst():= new(secp256k1.ModNScalar).Mul2(, &).Add(&).Mul()if .IsZero() {return nil, 0, false}if .IsOverHalfOrder() {.Negate()// Negating s corresponds to the random point that would have been// generated by -k (mod N), which necessarily has the opposite// oddness since N is prime, thus flip the pubkey recovery code// oddness bit accordingly.^= 0x01}// Step 6.//// Return (r,s)return NewSignature(&, ), , true}// signRFC6979 generates a deterministic ECDSA signature according to RFC 6979// and BIP0062 and returns it along with an additional public key recovery code// for efficiently recovering the public key from the signature.func signRFC6979( *secp256k1.PrivateKey, []byte) (*Signature, byte) {// The algorithm for producing an ECDSA signature is given as algorithm 4.29// in [GECC].//// The following is a paraphrased version for reference://// G = curve generator// N = curve order// d = private key// m = message// r, s = signature//// 1. Select random nonce k in [1, N-1]// 2. Compute kG// 3. r = kG.x mod N (kG.x is the x coordinate of the point kG)// Repeat from step 1 if r = 0// 4. e = H(m)// 5. s = k^-1(e + dr) mod N// Repeat from step 1 if s = 0// 6. Return (r,s)//// This is slightly modified here to conform to RFC6979 and BIP 62 as// follows://// A. Instead of selecting a random nonce in step 1, use RFC6979 to generate// a deterministic nonce in [1, N-1] parameterized by the private key,// message being signed, and an iteration count for the repeat cases// B. Negate s calculated in step 5 if it is > N/2// This is done because both s and its negation are valid signatures// modulo the curve order N, so it forces a consistent choice to reduce// signature malleability:= &.Keyvar [32]byte.PutBytes(&)defer zeroArray32(&)for := uint32(0); ; ++ {// Step 1 with modification A.//// Generate a deterministic nonce in [1, N-1] parameterized by the// private key, message being signed, and iteration count.:= secp256k1.NonceRFC6979([:], , nil, nil, )// Steps 2-6., , := sign(, , ).Zero()if ! {continue}return ,}}// Sign generates an ECDSA signature over the secp256k1 curve for the provided// hash (which should be the result of hashing a larger message) using the given// private key. The produced signature is deterministic (same message and same// key yield the same signature) and canonical in accordance with RFC6979 and// BIP0062.func ( *secp256k1.PrivateKey, []byte) *Signature {, := signRFC6979(, )return}const (// compactSigSize is the size of a compact signature. It consists of a// compact signature recovery code byte followed by the R and S components// serialized as 32-byte big-endian values. 1+32*2 = 65.// for the R and S components. 1+32+32=65.compactSigSize = 65// compactSigMagicOffset is a value used when creating the compact signature// recovery code inherited from Bitcoin and has no meaning, but has been// retained for compatibility. For historical purposes, it was originally// picked to avoid a binary representation that would allow compact// signatures to be mistaken for other components.compactSigMagicOffset = 27// compactSigCompPubKey is a value used when creating the compact signature// recovery code to indicate the original public key was compressed.compactSigCompPubKey = 4// pubKeyRecoveryCodeOddnessBit specifies the bit that indicates the oddess// of the Y coordinate of the random point calculated when creating a// signature.pubKeyRecoveryCodeOddnessBit = 1 << 0// pubKeyRecoveryCodeOverflowBit specifies the bit that indicates the X// coordinate of the random point calculated when creating a signature was// >= N, where N is the order of the group.pubKeyRecoveryCodeOverflowBit = 1 << 1)// SignCompact produces a compact ECDSA signature over the secp256k1 curve for// the provided hash (which should be the result of hashing a larger message)// using the given private key. The isCompressedKey parameter specifies if the// produced signature should reference a compressed public key or not.//// Compact signature format:// <1-byte compact sig recovery code><32-byte R><32-byte S>//// The compact sig recovery code is the value 27 + public key recovery code + 4// if the compact signature was created with a compressed public key.func ( *secp256k1.PrivateKey, []byte, bool) []byte {// Create the signature and associated pubkey recovery code and calculate// the compact signature recovery code., := signRFC6979(, ):= compactSigMagicOffset +if {+= compactSigCompPubKey}// Output <compactSigRecoveryCode><32-byte R><32-byte S>.var [compactSigSize]byte[0] =.r.PutBytesUnchecked([1:33]).s.PutBytesUnchecked([33:65])return [:]}// RecoverCompact attempts to recover the secp256k1 public key from the provided// compact signature and message hash. It first verifies the signature, and, if// the signature matches then the recovered public key will be returned as well// as a boolean indicating whether or not the original key was compressed.func (, []byte) (*secp256k1.PublicKey, bool, error) {// The following is very loosely based on the information and algorithm that// describes recovering a public key from and ECDSA signature in section// 4.1.6 of [SEC1].//// Given the following parameters://// G = curve generator// N = group order// P = field prime// Q = public key// m = message// e = hash of the message// r, s = signature// X = random point used when creating signature whose x coordinate is r//// The equation to recover a public key candidate from an ECDSA signature// is:// Q = r^-1(sX - eG).//// This can be verified by plugging it in for Q in the sig verification// equation:// X = s^-1(eG + rQ) (mod N)// => s^-1(eG + r(r^-1(sX - eG))) (mod N)// => s^-1(eG + sX - eG) (mod N)// => s^-1(sX) (mod N)// => X (mod N)//// However, note that since r is the x coordinate mod N from a random point// that was originally mod P, and the cofactor of the secp256k1 curve is 1,// there are four possible points that the original random point could have// been to produce r: (r,y), (r,-y), (r+N,y), and (r+N,-y). At least 2 of// those points will successfully verify, and all 4 will successfully verify// when the original x coordinate was in the range [N+1, P-1], but in any// case, only one of them corresponds to the original private key used.//// The method described by section 4.1.6 of [SEC1] to determine which one is// the correct one involves calculating each possibility as a candidate// public key and comparing the candidate to the authentic public key. It// also hints that it is possible to generate the signature in a such a// way that only one of the candidate public keys is viable.//// A more efficient approach that is specific to the secp256k1 curve is used// here instead which is to produce a "pubkey recovery code" when signing// that uniquely identifies which of the 4 possibilities is correct for the// original random point and using that to recover the pubkey directly as// follows://// 1. Fail if r and s are not in [1, N-1]// 2. Convert r to integer mod P// 3. If pubkey recovery code overflow bit is set:// 3.1 Fail if r + N >= P// 3.2 r = r + N (mod P)// 4. y = +sqrt(r^3 + 7) (mod P)// 4.1 Fail if y does not exist// 4.2 y = -y if needed to match pubkey recovery code oddness bit// 5. X = (r, y)// 6. e = H(m) mod N// 7. w = r^-1 mod N// 8. u1 = -(e * w) mod N// u2 = s * w mod N// 9. Q = u1G + u2X// 10. Fail if Q is the point at infinity// A compact signature consists of a recovery byte followed by the R and// S components serialized as 32-byte big-endian values.if len() != compactSigSize {:= fmt.Sprintf("malformed signature: wrong size: %d != %d",len(), compactSigSize)return nil, false, signatureError(ErrSigInvalidLen, )}// Parse and validate the compact signature recovery code.const (= compactSigMagicOffset= compactSigMagicOffset + compactSigCompPubKey + 3):= [0]if < || > {:= fmt.Sprintf("invalid signature: public key recovery code %d is "+"not in the valid range [%d, %d]", , ,)return nil, false, signatureError(ErrSigInvalidRecoveryCode, )}-= compactSigMagicOffset:= &compactSigCompPubKey != 0:= & 3// Step 1.//// Parse and validate the R and S signature components.//// Fail if r and s are not in [1, N-1].var , secp256k1.ModNScalarif := .SetByteSlice([1:33]); {:= "invalid signature: R >= group order"return nil, false, signatureError(ErrSigRTooBig, )}if .IsZero() {:= "invalid signature: R is 0"return nil, false, signatureError(ErrSigRIsZero, )}if := .SetByteSlice([33:]); {:= "invalid signature: S >= group order"return nil, false, signatureError(ErrSigSTooBig, )}if .IsZero() {:= "invalid signature: S is 0"return nil, false, signatureError(ErrSigSIsZero, )}// Step 2.//// Convert r to integer mod P.:= modNScalarToField(&)// Step 3.//// If pubkey recovery code overflow bit is set:if &pubKeyRecoveryCodeOverflowBit != 0 {// Step 3.1.//// Fail if r + N >= P//// Either the signature or the recovery code must be invalid if the// recovery code overflow bit is set and adding N to the R component// would exceed the field prime since R originally came from the X// coordinate of a random point on the curve.if .IsGtOrEqPrimeMinusOrder() {:= "invalid signature: signature R + N >= P"return nil, false, signatureError(ErrSigOverflowsPrime, )}// Step 3.2.//// r = r + N (mod P).Add(&orderAsFieldVal)}.Normalize()// Step 4.//// y = +sqrt(r^3 + 7) (mod P)// Fail if y does not exist.// y = -y if needed to match pubkey recovery code oddness bit//// The signature must be invalid if the calculation fails because the X// coord originally came from a random point on the curve which means there// must be a Y coord that satisfies the equation for a valid signature.:= &pubKeyRecoveryCodeOddnessBit != 0var secp256k1.FieldValif := secp256k1.DecompressY(&, , &); ! {:= "invalid signature: not for a valid curve point"return nil, false, signatureError(ErrPointNotOnCurve, )}// Step 5.//// X = (r, y)var secp256k1.JacobianPoint.X.Set(&).Y.Set(&).Z.SetInt(1)// Step 6.//// e = H(m) mod Nvar secp256k1.ModNScalar.SetByteSlice()// Step 7.//// w = r^-1 mod N:= new(secp256k1.ModNScalar).InverseValNonConst(&)// Step 8.//// u1 = -(e * w) mod N// u2 = s * w mod N:= new(secp256k1.ModNScalar).Mul2(&, ).Negate():= new(secp256k1.ModNScalar).Mul2(&, )// Step 9.//// Q = u1G + u2Xvar , , secp256k1.JacobianPointsecp256k1.ScalarBaseMultNonConst(, &)secp256k1.ScalarMultNonConst(, &, &)secp256k1.AddNonConst(&, &, &)// Step 10.//// Fail if Q is the point at infinity.//// Either the signature or the pubkey recovery code must be invalid if the// recovered pubkey is the point at infinity.if (.X.IsZero() && .Y.IsZero()) || .Z.IsZero() {:= "invalid signature: recovered pubkey is the point at infinity"return nil, false, signatureError(ErrPointNotOnCurve, )}// Notice that the public key is in affine coordinates..ToAffine():= secp256k1.NewPublicKey(&.X, &.Y)return , , nil}
![]() |
The pages are generated with Golds v0.8.2. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |