From ac138c3e42c7d6566bb6511f14bce7d8bc73cafb Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Wed, 17 Mar 2021 16:04:11 +0200 Subject: [PATCH 1/5] Add Schnorr explictly in names of functions, and remove the zeros checks in favor of init bool --- schnorr_keypair.go | 42 ++++++++++++++++++------------------------ schnorr_pubkey.go | 33 ++++++++++++--------------------- secp256k1.go | 3 +++ 3 files changed, 33 insertions(+), 45 deletions(-) diff --git a/schnorr_keypair.go b/schnorr_keypair.go index 28f494c..67c1c94 100644 --- a/schnorr_keypair.go +++ b/schnorr_keypair.go @@ -12,13 +12,11 @@ import ( "unsafe" ) -// errZeroedKeyPair is the error returned when using a zeroed pubkey -var errZeroedKeyPair = errors.New("the key pair is zeroed, which isn't a valid SchnorrKeyPair") - // SchnorrKeyPair is a type representing a pair of Secp256k1 private and public keys. // This can be used to create Schnorr signatures type SchnorrKeyPair struct { keypair C.secp256k1_keypair + init bool } // SerializedPrivateKey is a byte array representing the storage representation of a SchnorrKeyPair @@ -34,10 +32,10 @@ func (key SchnorrKeyPair) String() string { return key.SerializePrivateKey().String() } -// DeserializePrivateKey returns a SchnorrKeyPair type from a 32 byte private key. +// DeserializeSchnorrPrivateKey returns a SchnorrKeyPair type from a 32 byte private key. // will verify it's a valid private key(Group Order > key > 0) -func DeserializePrivateKey(data *SerializedPrivateKey) (*SchnorrKeyPair, error) { - key := SchnorrKeyPair{} +func DeserializeSchnorrPrivateKey(data *SerializedPrivateKey) (*SchnorrKeyPair, error) { + key := SchnorrKeyPair{init: true} cPtrPrivateKey := (*C.uchar)(&data[0]) ret := C.secp256k1_keypair_create(context, &key.keypair, cPtrPrivateKey) @@ -47,9 +45,9 @@ func DeserializePrivateKey(data *SerializedPrivateKey) (*SchnorrKeyPair, error) return &key, nil } -// DeserializePrivateKeyFromSlice returns a SchnorrKeyPair type from a serialized private key slice. +// DeserializeSchnorrPrivateKeyFromSlice returns a SchnorrKeyPair type from a serialized private key slice. // will verify that it's 32 byte and it's a valid private key(Group Order > key > 0) -func DeserializePrivateKeyFromSlice(data []byte) (key *SchnorrKeyPair, err error) { +func DeserializeSchnorrPrivateKeyFromSlice(data []byte) (key *SchnorrKeyPair, err error) { if len(data) != SerializedPrivateKeySize { return nil, errors.Errorf("invalid private key length got %d, expected %d", len(data), SerializedPrivateKeySize) @@ -57,18 +55,18 @@ func DeserializePrivateKeyFromSlice(data []byte) (key *SchnorrKeyPair, err error serializedKey := &SerializedPrivateKey{} copy(serializedKey[:], data) - return DeserializePrivateKey(serializedKey) + return DeserializeSchnorrPrivateKey(serializedKey) } -// GeneratePrivateKey generates a random valid private key from `crypto/rand` -func GeneratePrivateKey() (key *SchnorrKeyPair, err error) { +// GenerateSchnorrKeyPair generates a random valid private key from `crypto/rand` +func GenerateSchnorrKeyPair() (key *SchnorrKeyPair, err error) { rawKey := SerializedPrivateKey{} for { n, err := rand.Read(rawKey[:]) if err != nil || n != len(rawKey) { return nil, err } - key, err = DeserializePrivateKey(&rawKey) + key, err = DeserializeSchnorrPrivateKey(&rawKey) if err == nil { return key, nil } @@ -89,8 +87,8 @@ func (key *SchnorrKeyPair) SerializePrivateKey() *SerializedPrivateKey { // Add a tweak to the public key by doing `key + tweak % Group Order` and adjust the pub/priv keys according to parity. this adds it in place. // This is meant for creating BIP-32(HD) wallets func (key *SchnorrKeyPair) Add(tweak [32]byte) error { - if key.isZeroed() { - return errors.WithStack(errZeroedKeyPair) + if !key.init { + return errors.WithStack(errNonInitializedKey) } cPtrTweak := (*C.uchar)(&tweak[0]) ret := C.secp256k1_keypair_xonly_tweak_add(context, &key.keypair, cPtrTweak) @@ -107,10 +105,10 @@ func (key *SchnorrKeyPair) SchnorrPublicKey() (*SchnorrPublicKey, error) { } func (key *SchnorrKeyPair) schnorrPublicKeyInternal() (pubkey *SchnorrPublicKey, wasOdd bool, err error) { - if key.isZeroed() { - return nil, false, errors.WithStack(errZeroedKeyPair) + if !key.init { + return nil, false, errors.WithStack(errNonInitializedKey) } - pubkey = &SchnorrPublicKey{} + pubkey = &SchnorrPublicKey{init: true} cParity := C.int(42) ret := C.secp256k1_keypair_xonly_pub(context, &pubkey.pubkey, &cParity, &key.keypair) if ret != 1 { @@ -133,8 +131,8 @@ func (key *SchnorrKeyPair) SchnorrSign(hash *Hash) (*SchnorrSignature, error) { } func (key *SchnorrKeyPair) schnorrSignInternal(hash *Hash, auxiliaryRand *[32]byte) (*SchnorrSignature, error) { - if key.isZeroed() { - return nil, errors.WithStack(errZeroedKeyPair) + if !key.init { + return nil, errors.WithStack(errNonInitializedKey) } signature := SchnorrSignature{} cPtrSig := (*C.uchar)(&signature.signature[0]) @@ -142,13 +140,9 @@ func (key *SchnorrKeyPair) schnorrSignInternal(hash *Hash, auxiliaryRand *[32]by cPtrAux := unsafe.Pointer(auxiliaryRand) ret := C.secp256k1_schnorrsig_sign(context, cPtrSig, cPtrHash, &key.keypair, C.secp256k1_nonce_function_bip340, cPtrAux) if ret != 1 { - return nil, errors.New("failed Signing. You should call `DeserializePrivateKey` before calling this") + return nil, errors.New("failed Signing. You should call `DeserializeSchnorrPrivateKey` before calling this") } return &signature, nil - -} -func (key *SchnorrKeyPair) isZeroed() bool { - return isZeroed(key.keypair.data[:32]) || isZeroed(key.keypair.data[32:64]) || isZeroed(key.keypair.data[64:]) } func parityBitToBool(parity C.int) bool { diff --git a/schnorr_pubkey.go b/schnorr_pubkey.go index 2c06e0d..cdba866 100644 --- a/schnorr_pubkey.go +++ b/schnorr_pubkey.go @@ -12,13 +12,11 @@ import ( // SerializedSchnorrPublicKeySize defines the length in bytes of a SerializedSchnorrPublicKey const SerializedSchnorrPublicKeySize = 32 -// errZeroedPubkey is the error returned when using a zeroed pubkey -var errZeroedPubkey = errors.New("the public key is zeroed, which isn't a valid SchnorrPublicKey") - // SchnorrPublicKey is a PublicKey type used to sign and verify Schnorr signatures. // The struct itself is an opaque data type that should only be created via the supplied methods. type SchnorrPublicKey struct { pubkey C.secp256k1_xonly_pubkey + init bool } // SerializedSchnorrPublicKey is a is a byte array representing the storage representation of a compressed or uncompressed SchnorrPublicKey @@ -33,18 +31,18 @@ func (key *SchnorrPublicKey) IsEqual(target *SchnorrPublicKey) bool { return false } serializedKey, err1 := key.Serialize() - if err1 != nil && !errors.Is(err1, errZeroedPubkey) { + if err1 != nil && !errors.Is(err1, errNonInitializedKey) { panic(errors.Wrap(err1, "Unexpected error when serrializing key")) } serializedTarget, err2 := target.Serialize() - if err2 != nil && !errors.Is(err2, errZeroedPubkey) { + if err2 != nil && !errors.Is(err2, errNonInitializedKey) { panic(errors.Wrap(err1, "Unexpected error when serrializing key")) } - if errors.Is(err1, errZeroedPubkey) && errors.Is(err2, errZeroedPubkey) { // They're both zeroed, shouldn't happen if a constructor is used. + if errors.Is(err1, errNonInitializedKey) && errors.Is(err2, errNonInitializedKey) { // They're both zeroed, shouldn't happen if a constructor is used. return true } - if errors.Is(err1, errZeroedPubkey) || errors.Is(err2, errZeroedPubkey) { // Only one of them is zeroed + if errors.Is(err1, errNonInitializedKey) || errors.Is(err2, errNonInitializedKey) { // Only one of them is zeroed return false } return *serializedKey == *serializedTarget @@ -79,7 +77,7 @@ func DeserializeSchnorrPubKey(serializedPubKey []byte) (*SchnorrPublicKey, error if len(serializedPubKey) != SerializedSchnorrPublicKeySize { return nil, errors.New(fmt.Sprintf("serializedPubKey has to be %d bytes, instead got :%d", SerializedSchnorrPublicKeySize, len(serializedPubKey))) } - key := SchnorrPublicKey{} + key := SchnorrPublicKey{init: true} cPtr := (*C.uchar)(&serializedPubKey[0]) ret := C.secp256k1_xonly_pubkey_parse(C.secp256k1_context_no_precomp, &key.pubkey, cPtr) if ret != 1 { @@ -90,12 +88,11 @@ func DeserializeSchnorrPubKey(serializedPubKey []byte) (*SchnorrPublicKey, error // Serialize serializes a schnorr public key func (key *SchnorrPublicKey) Serialize() (*SerializedSchnorrPublicKey, error) { + if !key.init { + return nil, errors.WithStack(errNonInitializedKey) + } serialized := SerializedSchnorrPublicKey{} cPtr := (*C.uchar)(&serialized[0]) - if isZeroed(key.pubkey.data[:]) { - return nil, errZeroedPubkey - } - ret := C.secp256k1_xonly_pubkey_serialize(C.secp256k1_context_no_precomp, cPtr, &key.pubkey) if ret != 1 { panic("failed serializing a pubkey. Should never happen (upstream promise to return 1)") @@ -111,6 +108,9 @@ func (key *SchnorrPublicKey) Add(tweak [32]byte) error { } func (key *SchnorrPublicKey) addInternal(tweak [32]byte) (bool, error) { + if !key.init { + return false, errors.WithStack(errNonInitializedKey) + } cPtrTweak := (*C.uchar)(&tweak[0]) fullKey := C.secp256k1_pubkey{} ret := C.secp256k1_xonly_pubkey_tweak_add(context, &fullKey, &key.pubkey, cPtrTweak) @@ -124,12 +124,3 @@ func (key *SchnorrPublicKey) addInternal(tweak [32]byte) (bool, error) { } return parityBitToBool(cParity), nil } - -func isZeroed(slice []C.uchar) bool { - for _, byte := range slice { - if byte != 0 { - return false - } - } - return true -} diff --git a/secp256k1.go b/secp256k1.go index 4b0710e..a765f71 100644 --- a/secp256k1.go +++ b/secp256k1.go @@ -38,6 +38,9 @@ func init() { } } +// errNonInitializedKey is the error returned when using a zeroed pubkey +var errNonInitializedKey = errors.New("the key isn't initialized, you should use the generate/deserialize functions") + const ( // HashSize of array used to store hashes. See Hash. HashSize = 32 From 62f247d4d8b82304bdadc7074be3f832daafe178 Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Wed, 17 Mar 2021 16:04:32 +0200 Subject: [PATCH 2/5] Add ECDSA support --- ecdsa_privkey.go | 137 +++++++++++++++++++++++++++++++++++++++++++++ ecdsa_pubkey.go | 131 +++++++++++++++++++++++++++++++++++++++++++ ecdsa_signature.go | 78 ++++++++++++++++++++++++++ 3 files changed, 346 insertions(+) create mode 100644 ecdsa_privkey.go create mode 100644 ecdsa_pubkey.go create mode 100644 ecdsa_signature.go diff --git a/ecdsa_privkey.go b/ecdsa_privkey.go new file mode 100644 index 0000000..d7fcc57 --- /dev/null +++ b/ecdsa_privkey.go @@ -0,0 +1,137 @@ +package secp256k1 + +// #include "./depend/secp256k1/include/secp256k1.h" +import "C" +import ( + "crypto/rand" + "github.com/pkg/errors" + "unsafe" +) + +// ECDSAPrivateKey is a type representing a Secp256k1 private key. +// This private key can be used to create Schnorr/ECDSA signatures +type ECDSAPrivateKey struct { + privateKey [SerializedPrivateKeySize]byte + init bool +} + +// ECDSAPublicKey generates a PublicKey for the corresponding private key. +func (key *ECDSAPrivateKey) ECDSAPublicKey() (*ECDSAPublicKey, error) { + if !key.init { + return nil, errors.WithStack(errNonInitializedKey) + } + pubkey := ECDSAPublicKey{init: true} + cPtrPrivateKey := (*C.uchar)(&key.privateKey[0]) + ret := C.secp256k1_ec_pubkey_create(context, &pubkey.pubkey, cPtrPrivateKey) + if ret != 1 { + return nil, errors.New("failed Generating an ECDSAPublicKey. You should call `DeserializeECDSAPrivateKey` before calling this") + } + return &pubkey, nil +} + +// ECDSASign creates an ECDSA signature using the private key and the input hashed message. +// Notice: the [32] byte array *MUST* be a hash of a message. +func (key *ECDSAPrivateKey) ECDSASign(hash *Hash) (*ECDSASignature, error) { + var auxilaryRand [32]byte + n, err := rand.Read(auxilaryRand[:]) + if err != nil || n != len(auxilaryRand) { + return nil, err + } + return key.ecdsaSignInternal(hash, &auxilaryRand) +} + +func (key *ECDSAPrivateKey) ecdsaSignInternal(hash *Hash, auxiliaryRand *[32]byte) (*ECDSASignature, error) { + if !key.init { + return nil, errNonInitializedKey + } + signature := ECDSASignature{} + cPtrHash := (*C.uchar)(&hash[0]) + cPtrPrivKey := (*C.uchar)(&key.privateKey[0]) + cPtrAux := unsafe.Pointer(auxiliaryRand) + ret := C.secp256k1_ecdsa_sign(context, &signature.signature, cPtrHash, cPtrPrivKey, C.secp256k1_nonce_function_rfc6979, cPtrAux) + if ret != 1 { + return nil, errors.New("failed Signing. You should call `DeserializeECDSAPrivateKey` before calling this") + } + return &signature, nil +} + +// String returns the ECDSAPrivateKey as the hexadecimal string +func (key ECDSAPrivateKey) String() string { + return key.Serialize().String() +} + +// DeserializeECDSAPrivateKey returns a ECDSAPrivateKey type from a 32 byte private key. +// will verify it's a valid private key(Group Order > key > 0) +func DeserializeECDSAPrivateKey(data *SerializedPrivateKey) (key *ECDSAPrivateKey, err error) { + cPtr := (*C.uchar)(&data[0]) + + ret := C.secp256k1_ec_seckey_verify(C.secp256k1_context_no_precomp, cPtr) + if ret != 1 { + return nil, errors.New("invalid ECDSAPrivateKey (zero or bigger than the group order)") + } + + return &ECDSAPrivateKey{privateKey: *data, init: true}, nil +} + +// DeserializeECDSAPrivateKeyFromSlice returns a ECDSAPrivateKey type from a serialized private key slice. +// will verify that it's 32 byte and it's a valid private key(Group Order > key > 0) +func DeserializeECDSAPrivateKeyFromSlice(data []byte) (key *ECDSAPrivateKey, err error) { + if len(data) != SerializedPrivateKeySize { + return nil, errors.Errorf("invalid ECDSA private key length got %d, expected %d", len(data), + SerializedPrivateKeySize) + } + + serializedKey := &SerializedPrivateKey{} + copy(serializedKey[:], data) + return DeserializeECDSAPrivateKey(serializedKey) +} + +// GenerateECDSAPrivateKey generates a random valid private key from `crypto/rand` +func GenerateECDSAPrivateKey() (key *ECDSAPrivateKey, err error) { + key = &ECDSAPrivateKey{init: true} + cPtr := (*C.uchar)(&key.privateKey[0]) + for { + n, tmpErr := rand.Read(key.privateKey[:]) + if tmpErr != nil || n != len(key.privateKey) { + return nil, tmpErr + } + ret := C.secp256k1_ec_seckey_verify(C.secp256k1_context_no_precomp, cPtr) + if ret == 1 { + return + } + } +} + +// Serialize a private key +func (key *ECDSAPrivateKey) Serialize() *SerializedPrivateKey { + ret := SerializedPrivateKey(key.privateKey) + return &ret +} + +// Negate a private key in place. +func (key *ECDSAPrivateKey) Negate() error { + if !key.init { + return errNonInitializedKey + } + cPtr := (*C.uchar)(&key.privateKey[0]) + ret := C.secp256k1_ec_privkey_negate(C.secp256k1_context_no_precomp, cPtr) + if ret != 1 { + panic("Failed Negating the private key. Should never happen") + } + return nil +} + +// Add a tweak to the public key by doing `key + tweak % Group Order`. this adds it in place. +// This is meant for creating BIP-32(HD) wallets +func (key *ECDSAPrivateKey) Add(tweak [32]byte) error { + if !key.init { + return errNonInitializedKey + } + cPtrKey := (*C.uchar)(&key.privateKey[0]) + cPtrTweak := (*C.uchar)(&tweak[0]) + ret := C.secp256k1_ec_privkey_tweak_add(C.secp256k1_context_no_precomp, cPtrKey, cPtrTweak) + if ret != 1 { + return errors.New("failed Adding to private key. Tweak is bigger than the order or the complement of the private key") + } + return nil +} diff --git a/ecdsa_pubkey.go b/ecdsa_pubkey.go new file mode 100644 index 0000000..9008653 --- /dev/null +++ b/ecdsa_pubkey.go @@ -0,0 +1,131 @@ +package secp256k1 + +// #include "./depend/secp256k1/include/secp256k1.h" +import "C" +import ( + "encoding/hex" + "fmt" + "github.com/pkg/errors" +) + +// SerializedECDSAPublicKeySize defines the length in bytes of a SerializedECDSAPublicKey +const SerializedECDSAPublicKeySize = 33 + +// ECDSAPublicKey is a PublicKey type used to sign and verify ECDSA signatures. +// The struct itself is an opaque data type that should only be created via the supplied methods. +type ECDSAPublicKey struct { + pubkey C.secp256k1_pubkey + init bool +} + +// SerializedECDSAPublicKey is a is a byte array representing the storage representation of a compressed or uncompressed ECDSAPublicKey +type SerializedECDSAPublicKey [SerializedECDSAPublicKeySize]byte + +// IsEqual returns true if target is the same as key. +func (key *ECDSAPublicKey) IsEqual(target *ECDSAPublicKey) bool { + if key == nil && target == nil { + return true + } + if key == nil || target == nil { + return false + } + serializedKey, err1 := key.Serialize() + if err1 != nil && !errors.Is(err1, errNonInitializedKey) { + panic(errors.Wrap(err1, "Unexpected error when serrializing key")) + } + serializedTarget, err2 := target.Serialize() + if err2 != nil && !errors.Is(err2, errNonInitializedKey) { + panic(errors.Wrap(err1, "Unexpected error when serrializing key")) + } + + if errors.Is(err1, errNonInitializedKey) && errors.Is(err2, errNonInitializedKey) { // They're both zeroed, shouldn't happen if a constructor is used. + return true + } + if errors.Is(err1, errNonInitializedKey) || errors.Is(err2, errNonInitializedKey) { // Only one of them is zeroed + return false + } + return *serializedKey == *serializedTarget +} + +// String returns the SerializedECDSAPublicKey as a hexadecimal string +func (serialized SerializedECDSAPublicKey) String() string { + return hex.EncodeToString(serialized[:]) +} + +// String returns the ECDSAPublicKey as the hexadecimal string +func (key ECDSAPublicKey) String() string { + serialized, err := key.Serialize() + if err != nil { // This can only happen if the user calls this function skipping a constructor. i.e. `ECDSAPublicKey{}.String()` + return "" + } + return serialized.String() +} + +// ECDSAVerify verifies a ECDSA signature using the public key and the input hashed message. +// Notice: the [32] byte array *MUST* be a hash of a message you hashed yourself. +func (key *ECDSAPublicKey) ECDSAVerify(hash *Hash, signature *ECDSASignature) bool { + cPtrHash := (*C.uchar)(&hash[0]) + return C.secp256k1_ecdsa_verify(context, &signature.signature, cPtrHash, &key.pubkey) == 1 +} + +// DeserializeECDSAPubKey deserializes a serialized ECDSA public key, verifying it's valid. +// it supports both compressed(33 bytes) and uncompressed(65 bytes) public keys. +// it does not support hybrid(65 bytes) keys. +func DeserializeECDSAPubKey(serializedPubKey []byte) (*ECDSAPublicKey, error) { + if len(serializedPubKey) != SerializedECDSAPublicKeySize { + return nil, errors.New(fmt.Sprintf("serializedPubKey has to be %d bytes, instead got :%d", SerializedECDSAPublicKeySize, len(serializedPubKey))) + } + key := ECDSAPublicKey{init: true} + cPtr := (*C.uchar)(&serializedPubKey[0]) + ret := C.secp256k1_ec_pubkey_parse(context, &key.pubkey, cPtr, SerializedECDSAPublicKeySize) + if ret != 1 { + return nil, errors.New("failed parsing the public key") + } + return &key, nil +} + +// Serialize serializes a ECDSA public key +func (key *ECDSAPublicKey) Serialize() (*SerializedECDSAPublicKey, error) { + if !key.init { + return nil, errors.WithStack(errNonInitializedKey) + } + serialized := SerializedECDSAPublicKey{} + cPtr := (*C.uchar)(&serialized[0]) + cLen := C.size_t(SerializedECDSAPublicKeySize) + + ret := C.secp256k1_ec_pubkey_serialize(context, cPtr, &cLen, &key.pubkey, C.SECP256K1_EC_COMPRESSED) + if ret != 1 { + panic("failed serializing a pubkey. Should never happen (upstream promise to return 1)") + } + if cLen != SerializedECDSAPublicKeySize { + panic("Returned length should be 33 because we passed SECP256K1_EC_COMPRESSED") + } + return &serialized, nil +} + +// Add a tweak to the public key by doing `key + tweak*Generator`. this adds it in place. +// This is meant for creating BIP-32(HD) wallets +func (key *ECDSAPublicKey) Add(tweak [32]byte) error { + if !key.init { + return errors.WithStack(errNonInitializedKey) + } + cPtrTweak := (*C.uchar)(&tweak[0]) + ret := C.secp256k1_ec_pubkey_tweak_add(context, &key.pubkey, cPtrTweak) + if ret != 1 { + return errors.New("failed adding to the public key. Tweak is bigger than the order or the complement of the private key") + } + return nil +} + +// Negate a public key in place. +// Equivalent to negating the private key and then generating the public key. +func (key *ECDSAPublicKey) Negate() error { + if !key.init { + return errors.WithStack(errNonInitializedKey) + } + ret := C.secp256k1_ec_pubkey_negate(C.secp256k1_context_no_precomp, &key.pubkey) + if ret != 1 { + panic("failed Negating the public key. Should never happen") + } + return nil +} diff --git a/ecdsa_signature.go b/ecdsa_signature.go new file mode 100644 index 0000000..ed89450 --- /dev/null +++ b/ecdsa_signature.go @@ -0,0 +1,78 @@ +package secp256k1 + +// #include "./depend/secp256k1/include/secp256k1.h" +import "C" +import ( + "encoding/hex" + "github.com/pkg/errors" +) + +const ( + // SerializedECDSASignatureSize defines the length in bytes of SerializedECDSASignature + SerializedECDSASignatureSize = 64 +) + +// ECDSASignature is a type representing a ECDSA Signature. +// The struct itself is an opaque data type that should only be created via the supplied methods. +type ECDSASignature struct { + signature C.secp256k1_ecdsa_signature +} + +// SerializedECDSASignature is a is a byte array representing the storage representation of a ECDSASignature +type SerializedECDSASignature [SerializedECDSASignatureSize]byte + +// IsEqual returns true if target is the same as signature. +func (signature *ECDSASignature) IsEqual(target *ECDSASignature) bool { + if signature == nil && target == nil { + return true + } + if signature == nil || target == nil { + return false + } + return *signature.Serialize() == *target.Serialize() +} + +// String returns the SerializedECDSASignature as the hexadecimal string +func (serialized SerializedECDSASignature) String() string { + return hex.EncodeToString(serialized[:]) +} + +// String returns the ECDSASignature as the hexadecimal string +func (signature ECDSASignature) String() string { + return signature.Serialize().String() +} + +// Serialize returns a 64 byte serialized signature +func (signature *ECDSASignature) Serialize() *SerializedECDSASignature { + serialized := SerializedECDSASignature{} + cPtr := (*C.uchar)(&serialized[0]) + ret := C.secp256k1_ecdsa_signature_serialize_compact(C.secp256k1_context_no_precomp, cPtr, &signature.signature) + if ret != 1 { + panic("failed serializing a signature. Should never happen (upstream promise to return 1)") + } + return &serialized +} + +// DeserializeECDSASignature deserializes a 64 byte serialized ECDSA signature into a ECDSASignature type. +func DeserializeECDSASignature(serializedSignature *SerializedECDSASignature) (*ECDSASignature, error) { + signature := ECDSASignature{} + cPtr := (*C.uchar)(&serializedSignature[0]) + ret := C.secp256k1_ecdsa_signature_parse_compact(C.secp256k1_context_no_precomp, &signature.signature, cPtr) + if ret != 1 { + return nil, errors.New("failed parsing the ECDSA signature") + } + return &signature, nil +} + +// DeserializeECDSASignatureFromSlice returns a ECDSASignature type from a serialized signature slice. +// will verify that it's SerializedECDSASignatureSize bytes long +func DeserializeECDSASignatureFromSlice(data []byte) (signature *ECDSASignature, err error) { + if len(data) != SerializedECDSASignatureSize { + return nil, errors.Errorf("invalid ECDSA signature length got %d, expected %d", len(data), + SerializedECDSASignatureSize) + } + + serialized := SerializedECDSASignature{} + copy(serialized[:], data) + return DeserializeECDSASignature(&serialized) +} From 40a096621544440a7105e46dcf615b3faccdd70f Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Wed, 17 Mar 2021 16:04:54 +0200 Subject: [PATCH 3/5] Add abstraction over ecdsa/schnorr in the tests --- interfaces_test.go | 281 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 281 insertions(+) create mode 100644 interfaces_test.go diff --git a/interfaces_test.go b/interfaces_test.go new file mode 100644 index 0000000..68b44c1 --- /dev/null +++ b/interfaces_test.go @@ -0,0 +1,281 @@ +package secp256k1 + +import ( + "fmt" + "math/rand" + "testing" +) + +type alogirthmInterface interface { + EmptyPrivKey() privateKeyInterface + EmptyPubKey() publicKeyInterface + EmptySignature() signatureInterface + fmt.Stringer +} + +type privateKeyInterface interface { + GenerateNew(testing.TB, *rand.Rand) privateKeyInterface + Sign(*Hash) (signatureInterface, error) + Add([32]byte) error + PublicKey() (publicKeyInterface, bool, error) + Serialize() *[32]byte + DeserializeNew([]byte) (privateKeyInterface, error) + SetBytes([]byte) + IsEqual(privateKeyInterface) bool + Clone() privateKeyInterface + fmt.Stringer +} +type publicKeyInterface interface { + Verify(*Hash, signatureInterface) bool + Add([32]byte) (isNegated bool, err error) + Serialize() ([]byte, error) + DeserializeNew([]byte) (publicKeyInterface, error) + IsEqual(other publicKeyInterface) bool + Clone() publicKeyInterface + fmt.Stringer +} +type signatureInterface interface { + Serialize() *[64]byte + DeserializeNew([]byte) (signatureInterface, error) + IsEqual(signatureInterface) bool + Clone() signatureInterface + fmt.Stringer +} + +type schnorr struct{} + +func (s schnorr) EmptyPrivKey() privateKeyInterface { return new(schnorrPrivKey) } +func (s schnorr) EmptyPubKey() publicKeyInterface { return new(schnorrPubkey) } +func (s schnorr) EmptySignature() signatureInterface { return new(schnorrSignature) } +func (s schnorr) String() string { return "schnorr" } + +type ecdsa struct{} + +func (s ecdsa) EmptyPrivKey() privateKeyInterface { return new(ecdsaPrivKey) } +func (s ecdsa) EmptyPubKey() publicKeyInterface { return new(ecdsaPubkey) } +func (s ecdsa) EmptySignature() signatureInterface { return new(ecdsaSignature) } +func (s ecdsa) String() string { return "ecdsa" } + +type schnorrPrivKey SchnorrKeyPair + +func (s *schnorrPrivKey) GenerateNew(t testing.TB, r *rand.Rand) privateKeyInterface { + buf := fastGenerateTweak(t, r) + keypair, err := DeserializeSchnorrPrivateKey((*SerializedPrivateKey)(buf)) + if err != nil { + t.Fatalf("A valid tweak should be a valid private key: '%s'", err) + } + return (*schnorrPrivKey)(keypair) +} +func (s *schnorrPrivKey) Sign(hash *Hash) (signatureInterface, error) { + sig, err := (*SchnorrKeyPair)(s).SchnorrSign(hash) + return (*schnorrSignature)(sig), err +} +func (s *schnorrPrivKey) Add(tweak [32]byte) error { + return (*SchnorrKeyPair)(s).Add(tweak) +} +func (s *schnorrPrivKey) PublicKey() (publicKeyInterface, bool, error) { + pubkey, isNegated, err := (*SchnorrKeyPair)(s).schnorrPublicKeyInternal() + return (*schnorrPubkey)(pubkey), isNegated, err +} +func (s *schnorrPrivKey) Serialize() *[32]byte { + ret := (*SchnorrKeyPair)(s).SerializePrivateKey() + return (*[32]byte)(ret) +} +func (s *schnorrPrivKey) DeserializeNew(bytes []byte) (privateKeyInterface, error) { + key, err := DeserializeSchnorrPrivateKeyFromSlice(bytes) + return (*schnorrPrivKey)(key), err +} +func (s *schnorrPrivKey) SetBytes(bytes []byte) { + for i := 0; i < len(bytes); i++ { + (*SchnorrKeyPair)(s).keypair.data[i] = _Ctype_uchar(bytes[i]) + } +} +func (s *schnorrPrivKey) IsEqual(other privateKeyInterface) bool { + otherKey, ok := other.(*schnorrPrivKey) + if !ok { + return false + } + return *s == *otherKey +} +func (s schnorrPrivKey) Clone() privateKeyInterface { + return &s +} +func (s schnorrPrivKey) String() string { + return (SchnorrKeyPair)(s).String() +} + +type ecdsaPrivKey ECDSAPrivateKey + +func (s *ecdsaPrivKey) GenerateNew(t testing.TB, r *rand.Rand) privateKeyInterface { + buf := fastGenerateTweak(t, r) + privkey, err := DeserializeECDSAPrivateKey((*SerializedPrivateKey)(buf)) + if err != nil { + t.Fatalf("A valid tweak should be a valid private key: '%s'", err) + } + return (*ecdsaPrivKey)(privkey) +} +func (s *ecdsaPrivKey) Sign(hash *Hash) (signatureInterface, error) { + sig, err := (*ECDSAPrivateKey)(s).ECDSASign(hash) + return (*ecdsaSignature)(sig), err +} +func (s *ecdsaPrivKey) Add(tweak [32]byte) error { + return (*ECDSAPrivateKey)(s).Add(tweak) +} +func (s *ecdsaPrivKey) PublicKey() (publicKeyInterface, bool, error) { + pubkey, err := (*ECDSAPrivateKey)(s).ECDSAPublicKey() + // ECDSA is never negated + return (*ecdsaPubkey)(pubkey), false, err +} + +func (s *ecdsaPrivKey) Serialize() *[32]byte { + ret := (*ECDSAPrivateKey)(s).Serialize() + return (*[32]byte)(ret) +} +func (s *ecdsaPrivKey) DeserializeNew(bytes []byte) (privateKeyInterface, error) { + key, err := DeserializeECDSAPrivateKeyFromSlice(bytes) + return (*ecdsaPrivKey)(key), err +} +func (s *ecdsaPrivKey) SetBytes(bytes []byte) { + copy((*ECDSAPrivateKey)(s).privateKey[:], bytes) +} +func (s *ecdsaPrivKey) IsEqual(other privateKeyInterface) bool { + otherKey, ok := other.(*ecdsaPrivKey) + if !ok { + return false + } + return *s == *otherKey +} +func (s ecdsaPrivKey) Clone() privateKeyInterface { + return &s +} +func (s ecdsaPrivKey) String() string { + return (ECDSAPrivateKey)(s).String() +} + +type schnorrPubkey SchnorrPublicKey + +func (s *schnorrPubkey) Verify(hash *Hash, sig signatureInterface) bool { + return (*SchnorrPublicKey)(s).SchnorrVerify(hash, (*SchnorrSignature)(sig.(*schnorrSignature))) +} +func (s *schnorrPubkey) Add(tweak [32]byte) (isNegated bool, err error) { + return (*SchnorrPublicKey)(s).addInternal(tweak) +} +func (s *schnorrPubkey) Serialize() ([]byte, error) { + serialized, err := (*SchnorrPublicKey)(s).Serialize() + if err != nil { + return nil, err + } + return serialized[:], nil +} +func (s *schnorrPubkey) DeserializeNew(bytes []byte) (publicKeyInterface, error) { + key, err := DeserializeSchnorrPubKey(bytes) + return (*schnorrPubkey)(key), err +} +func (s *schnorrPubkey) IsEqual(other publicKeyInterface) bool { + if other == nil { + other = (*schnorrPubkey)(nil) + } + otherKey, ok := other.(*schnorrPubkey) + if !ok { + return false + } + return (*SchnorrPublicKey)(s).IsEqual((*SchnorrPublicKey)(otherKey)) + +} +func (s schnorrPubkey) Clone() publicKeyInterface { + return &s +} +func (s schnorrPubkey) String() string { + return (SchnorrPublicKey)(s).String() +} + +type ecdsaPubkey ECDSAPublicKey + +func (s *ecdsaPubkey) Verify(hash *Hash, sig signatureInterface) bool { + return (*ECDSAPublicKey)(s).ECDSAVerify(hash, (*ECDSASignature)(sig.(*ecdsaSignature))) +} +func (s *ecdsaPubkey) Add(tweak [32]byte) (isNegated bool, err error) { + // ECDSA Add never negates. + return false, (*ECDSAPublicKey)(s).Add(tweak) +} +func (s *ecdsaPubkey) Serialize() ([]byte, error) { + serialized, err := (*ECDSAPublicKey)(s).Serialize() + if err != nil { + return nil, err + } + return serialized[:], nil +} +func (s *ecdsaPubkey) DeserializeNew(bytes []byte) (publicKeyInterface, error) { + key, err := DeserializeECDSAPubKey(bytes) + return (*ecdsaPubkey)(key), err +} +func (s *ecdsaPubkey) IsEqual(other publicKeyInterface) bool { + if other == nil { + other = (*ecdsaPubkey)(nil) + } + otherKey, ok := other.(*ecdsaPubkey) + if !ok { + return false + } + return (*ECDSAPublicKey)(s).IsEqual((*ECDSAPublicKey)(otherKey)) +} +func (s ecdsaPubkey) Clone() publicKeyInterface { + return &s +} +func (s ecdsaPubkey) String() string { + return (ECDSAPublicKey)(s).String() +} + +type ecdsaSignature ECDSASignature + +func (s *ecdsaSignature) Serialize() *[64]byte { + return (*[64]byte)((*ECDSASignature)(s).Serialize()) +} +func (s *ecdsaSignature) DeserializeNew(bytes []byte) (signatureInterface, error) { + key, err := DeserializeECDSASignatureFromSlice(bytes) + return (*ecdsaSignature)(key), err +} +func (s *ecdsaSignature) IsEqual(other signatureInterface) bool { + if other == nil { + other = (*ecdsaSignature)(nil) + } + otherKey, ok := other.(*ecdsaSignature) + if !ok { + return false + } + return (*ECDSASignature)(s).IsEqual((*ECDSASignature)(otherKey)) + +} +func (s ecdsaSignature) Clone() signatureInterface { + return &s +} +func (s ecdsaSignature) String() string { + return (ECDSASignature)(s).String() +} + +type schnorrSignature SchnorrSignature + +func (s *schnorrSignature) Serialize() *[64]byte { + return (*[64]byte)((*SchnorrSignature)(s).Serialize()) +} +func (s *schnorrSignature) DeserializeNew(bytes []byte) (signatureInterface, error) { + key, err := DeserializeSchnorrSignatureFromSlice(bytes) + return (*schnorrSignature)(key), err +} +func (s *schnorrSignature) IsEqual(other signatureInterface) bool { + if other == nil { + other = (*schnorrSignature)(nil) + } + otherKey, ok := other.(*schnorrSignature) + if !ok { + return false + } + return (*SchnorrSignature)(s).IsEqual((*SchnorrSignature)(otherKey)) + +} +func (s schnorrSignature) Clone() signatureInterface { + return &s +} +func (s schnorrSignature) String() string { + return (SchnorrSignature)(s).String() +} From 28a56f42094ddc8155630809d550cc0e9d472641 Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Wed, 17 Mar 2021 16:05:18 +0200 Subject: [PATCH 4/5] Refactor the tests to run over both schnorr and ecdsa --- secp256k1_test.go | 853 ++++++++++++++++++-------------------------- testvectors_test.go | 196 ++++++++++ 2 files changed, 542 insertions(+), 507 deletions(-) create mode 100644 testvectors_test.go diff --git a/secp256k1_test.go b/secp256k1_test.go index 08c480f..be83531 100644 --- a/secp256k1_test.go +++ b/secp256k1_test.go @@ -1,16 +1,27 @@ package secp256k1 import ( - "encoding/hex" "fmt" "math/big" "math/rand" + "reflect" "testing" ) const loopsN = 150 var Secp256k1Order = new(big.Int).SetBytes([]byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 254, 186, 174, 220, 230, 175, 72, 160, 59, 191, 210, 94, 140, 208, 54, 65, 65}) +var algorithms = []alogirthmInterface{new(schnorr), new(ecdsa)} + +func ForAllAlgorithms(t *testing.T, testFunc func(*testing.T, *rand.Rand, alogirthmInterface)) { + for _, alg := range algorithms { + algCopy := alg + t.Run(algCopy.String(), func(t *testing.T) { + t.Parallel() + testFunc(t, rand.New(rand.NewSource(42)), algCopy) + }) + } +} func intTo32Bytes(i *big.Int) [32]byte { res := [32]byte{} @@ -27,545 +38,375 @@ func negateSecp256k1Tweak(tweak []byte) { copy(tweak, res[:]) } -func fastGenerateTweak(t *testing.T, r *rand.Rand) *[32]byte { +func fastGenerateTweak(t testing.TB, r *rand.Rand) *[32]byte { buf := [32]byte{} for { n, err := r.Read(buf[:]) if err != nil || n != len(buf) { t.Fatalf("Failed generating 32 random bytes '%s'", err) } - _, err = DeserializePrivateKey((*SerializedPrivateKey)(&buf)) + _, err = DeserializeSchnorrPrivateKey((*SerializedPrivateKey)(&buf)) if err == nil { return &buf } } } -func fastGenerateKeyPair(t *testing.T, r *rand.Rand) (key *SchnorrKeyPair) { - buf := fastGenerateTweak(t, r) - keypair, err := DeserializePrivateKey((*SerializedPrivateKey)(buf)) - if err != nil { - t.Fatalf("A valid tweak should be a valid private key: '%s'", err) - } - return keypair -} - -func setPrivateKey(keypair *SchnorrKeyPair, bytes []byte) { - for i := 0; i < len(bytes); i++ { - keypair.keypair.data[i] = _Ctype_uchar(bytes[i]) - } -} - func TestParseSerializePrivateKey(t *testing.T) { - r := rand.New(rand.NewSource(1)) - for i := 0; i < loopsN; i++ { - keypair := fastGenerateKeyPair(t, r) - - serialized := keypair.SerializePrivateKey() - privkey2, err := DeserializePrivateKey(serialized) - if err != nil { - t.Errorf("Failed parsing privateKey '%s'", err) + ForAllAlgorithms(t, func(t *testing.T, r *rand.Rand, alg alogirthmInterface) { + for i := 0; i < loopsN; i++ { + privkey := alg.EmptyPrivKey().GenerateNew(t, r) + serialized := privkey.Serialize() + privkey2, err := alg.EmptyPrivKey().DeserializeNew(serialized[:]) + if err != nil { + t.Errorf("Failed parsing privateKey '%s'", err) + } + if !privkey.IsEqual(privkey2) { + t.Errorf("Privkeys aren't equal '%s' '%s'", privkey, privkey2) + } } + }) - if *keypair != *privkey2 { - t.Errorf("Privkeys aren't equal '%s' '%s'", privkey2, keypair) - } - } } func TestGeneratePrivateKey(t *testing.T) { - _, err := GeneratePrivateKey() + _, err := GenerateSchnorrKeyPair() + if err != nil { + t.Errorf("Failed generating a privatekey '%s'", err) + } + _, err = GenerateECDSAPrivateKey() if err != nil { t.Errorf("Failed generating a privatekey '%s'", err) } } func TestPrivateKey_Add_Fail(t *testing.T) { - r := rand.New(rand.NewSource(1)) - keypair := fastGenerateKeyPair(t, r) - privkeyInverse := keypair.SerializePrivateKey() - negateSecp256k1Tweak(privkeyInverse[:]) - err := keypair.Add(*privkeyInverse) - if err == nil { - t.Errorf("Adding the inverse of itself should fail, '%s', '%s', '%s'", keypair, privkeyInverse, err) - } - keypair = fastGenerateKeyPair(t, r) - oufOfBounds := [32]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} - err = keypair.Add(oufOfBounds) - if err == nil { - t.Errorf("Adding a tweak bigger than the order should fail, '%s', '%x' '%s'", keypair, oufOfBounds, err) - } + ForAllAlgorithms(t, func(t *testing.T, r *rand.Rand, alg alogirthmInterface) { + for i := 0; i < loopsN; i++ { + privkey := alg.EmptyPrivKey().GenerateNew(t, r) + privkeyInverse := privkey.Serialize() + _, isNegated, err := privkey.PublicKey() + if err != nil { + t.Fatalf("Failed converting privkey to pubkey: %s", err) + } + // If the key is already being negated then the serialized one is already the inverse of the privkey. + if !isNegated { + negateSecp256k1Tweak(privkeyInverse[:]) + } + err = privkey.Add(*privkeyInverse) + if err == nil { + t.Errorf("Adding the inverse of itself should fail, '%s', '%x', '%s'", privkey, privkeyInverse, err) + } + privkey = alg.EmptyPrivKey().GenerateNew(t, r) + oufOfBounds := [32]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} + err = privkey.Add(oufOfBounds) + if err == nil { + t.Errorf("Adding a tweak bigger than the order should fail, '%s', '%x' '%s'", privkey, oufOfBounds, err) + } + } + }) } func TestPrivateKey_Add(t *testing.T) { - r := rand.New(rand.NewSource(1)) - keypair := fastGenerateKeyPair(t, r) - pubkey, wasOdd, err := keypair.schnorrPublicKeyInternal() - if err != nil { - t.Fatal(err) - } - privkeyBig := new(big.Int).SetBytes(keypair.SerializePrivateKey()[:]) - seedBig := big.Int{} - - for i := 0; i < loopsN; i++ { - if wasOdd { // Schnorr secret keys are always even - privkeyBig.Neg(privkeyBig) - } - seed := *fastGenerateTweak(t, r) - seedBig.SetBytes(seed[:]) - - privkeyBig.Add(privkeyBig, &seedBig) - privkeyBig.Mod(privkeyBig, Secp256k1Order) - err := keypair.Add(seed) + ForAllAlgorithms(t, func(t *testing.T, r *rand.Rand, alg alogirthmInterface) { + privkey := alg.EmptyPrivKey().GenerateNew(t, r) + pubkey, wasOdd, err := privkey.PublicKey() if err != nil { - t.Fatalf("failed adding seed: '%s' to key: '%s'", seed, keypair) - } - wasOdd, err = pubkey.addInternal(seed) - if err != nil { // This shouldn't fail if the same operation for the privateKey didn't fail. t.Fatal(err) } + privkeyBig := new(big.Int).SetBytes(privkey.Serialize()[:]) + seedBig := big.Int{} - tmpPubKey, err := keypair.SchnorrPublicKey() - if err != nil { - t.Fatalf("Failed generating pubkey from '%s'. '%s'", keypair, err) - } - - if intTo32Bytes(privkeyBig) != *keypair.SerializePrivateKey() { - t.Fatalf("Add operation failed, i=%d '%x' != '%s'", i, intTo32Bytes(privkeyBig), keypair.SerializePrivateKey()) - } - if !pubkey.IsEqual(tmpPubKey) { - t.Fatalf("tweaked pubkey '%s' doesn't match tweaked privateKey '%s', '%s'", pubkey, tmpPubKey, keypair) - } - } -} - -func TestParseSchnorrPubKeyFail(t *testing.T) { - zeros := [32]byte{} - max := [32]byte{} - for i := range max { - max[i] = 0xff - } - _, err := DeserializeSchnorrPubKey(zeros[:]) - if err == nil { - t.Errorf("Shouldn't parse 32 zeros as a pubkey '%x'", zeros) - } - _, err = DeserializeSchnorrPubKey(zeros[:30]) - if err == nil { - t.Errorf("Shouldn't parse 30 zeros as a pubkey '%x'", zeros[:30]) - } - _, err = DeserializeSchnorrPubKey(max[:]) - if err == nil { - t.Errorf("Shouldn't parse 32 0xFF as a pubkey '%x' (it's above the field order)", max) - } -} - -func TestSchnorrPublicKey_SerializeFail(t *testing.T) { - pubkey := SchnorrPublicKey{} - _, err := pubkey.Serialize() - if err == nil { - t.Errorf("Zeroed public key isn't serializable as compressed") - } -} - -func TestBadPrivateKeyPublicKeyFail(t *testing.T) { - r := rand.New(rand.NewSource(1)) - goodKeyPair := fastGenerateKeyPair(t, r) - goodPublicKey, err := goodKeyPair.SchnorrPublicKey() - if err != nil { - t.Fatalf("Failed generating pubkey from: '%s'. '%s'", goodKeyPair, err) - } - goodPublicKeyBackup := *goodPublicKey - goodKeyPairBackup := *goodKeyPair - msg := Hash(*fastGenerateTweak(t, r)) - keypair := SchnorrKeyPair{} - var zeros32 [32]byte - - _, err1 := keypair.SchnorrPublicKey() - _, err2 := keypair.SchnorrSign(&msg) - err3 := keypair.Add(zeros32) - _, err4 := DeserializePrivateKey(keypair.SerializePrivateKey()) - if err1 == nil || err2 == nil || err3 == nil || err4 == nil { - t.Errorf("A zeroed key is invalid, err1: '%s', err2: '%s', err3: '%s', err4: '%s'", err1, err2, err3, err4) - } + for i := 0; i < loopsN; i++ { + if wasOdd { // Schnorr secret keys are always even, so if libsecp negated the key then we should too. + privkeyBig.Neg(privkeyBig) + } + seed := *fastGenerateTweak(t, r) + seedBig.SetBytes(seed[:]) - err5 := goodKeyPair.Add(zeros32) - err6 := goodPublicKey.Add(zeros32) - if err5 != nil || err6 != nil { - t.Errorf("It should be possible to add zero to a key, err4: '%s', err5: '%s'", err5, err6) - } + privkeyBig.Add(privkeyBig, &seedBig) + privkeyBig.Mod(privkeyBig, Secp256k1Order) + err := privkey.Add(seed) + if err != nil { + t.Fatalf("failed adding seed: '%s' to key: '%s'", seed, privkey) + } + wasOdd, err = pubkey.Add(seed) + if err != nil { // This shouldn't fail if the same operation for the privateKey didn't fail. + t.Fatal(err) + } - setPrivateKey(&keypair, Secp256k1Order.Bytes()) - _, err1 = keypair.SchnorrPublicKey() - _, err2 = keypair.SchnorrSign(&msg) - _, err3 = DeserializePrivateKey(keypair.SerializePrivateKey()) - *goodKeyPair = goodKeyPairBackup - *goodPublicKey = goodPublicKeyBackup - err4 = goodKeyPair.Add(intTo32Bytes(Secp256k1Order)) - err5 = goodPublicKey.Add(intTo32Bytes(Secp256k1Order)) - if err1 == nil || err2 == nil || err3 == nil || err4 == nil || err5 == nil { - t.Errorf("the group order isn't a valid key, err1: '%s', err2: '%s', err3: '%s', err4: '%s', err5: '%s'", err1, err2, err3, err4, err5) - } - keypair = *fastGenerateKeyPair(t, r) - orderPlusOne := new(big.Int).SetInt64(1) - orderPlusOne.Add(orderPlusOne, Secp256k1Order) - setPrivateKey(&keypair, orderPlusOne.Bytes()) - orderPlusOneArray := intTo32Bytes(orderPlusOne) - _, err = DeserializePrivateKeyFromSlice(orderPlusOneArray[:]) - if err1 == nil || err2 == nil || err3 == nil || err4 == nil { - t.Errorf("A key bigger than the group order isn't a valid key, err: '%s'", err) - } - *goodKeyPair = goodKeyPairBackup - *goodPublicKey = goodPublicKeyBackup + tmpPubKey, _, err := privkey.PublicKey() + if err != nil { + t.Fatalf("Failed generating pubkey from '%s'. '%s'", privkey, err) + } - err1 = goodKeyPair.Add(intTo32Bytes(orderPlusOne)) - err2 = goodPublicKey.Add(intTo32Bytes(orderPlusOne)) - if err1 == nil || err2 == nil { - t.Errorf("A tweak bigger than the group order isn't a valid tweak, err1: '%s', err2: '%s'", err1, err2) - } - orderMinusOne := new(big.Int).Sub(Secp256k1Order, new(big.Int).SetInt64(1)) - orderPlusOne = nil - setPrivateKey(&keypair, orderMinusOne.Bytes()) - _, err1 = keypair.SchnorrPublicKey() - _, err2 = keypair.SchnorrSign(&msg) - _, err3 = DeserializePrivateKey(keypair.SerializePrivateKey()) - *goodKeyPair = goodKeyPairBackup - *goodPublicKey = goodPublicKeyBackup - err4 = goodKeyPair.Add(intTo32Bytes(orderMinusOne)) - err5 = goodPublicKey.Add(intTo32Bytes(orderMinusOne)) - if err1 != nil || err2 != nil || err3 != nil || err4 != nil || err5 != nil { - t.Errorf("Group order - 1 should be a valid key, err1: '%s', err2: '%s', err3: '%s', err4: '%s', err5: '%s'", err1, err2, err3, err4, err5) - } + if intTo32Bytes(privkeyBig) != *privkey.Serialize() { + t.Fatalf("Add operation failed, i=%d '%x' != '%x'", i, intTo32Bytes(privkeyBig), privkey.Serialize()) + } + if !pubkey.IsEqual(tmpPubKey) { + t.Fatalf("tweaked pubkey '%s' doesn't match tweaked privateKey '%s', '%s'", pubkey, tmpPubKey, privkey) + } + } + }) } -func TestParseSchnorrPubKey(t *testing.T) { - r := rand.New(rand.NewSource(1)) - for i := 0; i < loopsN; i++ { - keypair := fastGenerateKeyPair(t, r) - pubkey, err := keypair.SchnorrPublicKey() - if err != nil { - t.Errorf("Failed Generating a pubkey from privateKey: '%s'. '%s'", keypair, err) +func TestParsePublicKeyFail(t *testing.T) { + ForAllAlgorithms(t, func(t *testing.T, r *rand.Rand, alg alogirthmInterface) { + pubkeyAlg := alg.EmptyPubKey() + zeros := [32]byte{} + max := [32]byte{} + for i := range max { + max[i] = 0xff } - serializedPubkey, err := pubkey.Serialize() - if err != nil { - t.Errorf("Failed serializing the key: %s, error: '%s'", pubkey, err) + _, err := pubkeyAlg.DeserializeNew(zeros[:]) + if err == nil { + t.Errorf("Shouldn't parse 32 zeros as a pubkey '%x'", zeros) } - pubkeyNew1, err := DeserializeSchnorrPubKey(serializedPubkey[:]) - if err != nil { - t.Errorf("Failed Parsing the compressed public key from keypair: '%s'. '%s'", pubkeyNew1, err) + _, err = pubkeyAlg.DeserializeNew(zeros[:30]) + if err == nil { + t.Errorf("Shouldn't parse 30 zeros as a pubkey '%x'", zeros[:30]) } - if !pubkey.IsEqual(pubkeyNew1) { - t.Errorf("Pubkeys aren't the same: '%s', '%s',", pubkey, pubkeyNew1) + _, err = pubkeyAlg.DeserializeNew(max[:]) + if err == nil { + t.Errorf("Shouldn't parse 32 0xFF as a pubkey '%x' (it's above the field order)", max) } - } + }) } -func TestSignVerifyParseSchnorr(t *testing.T) { - r := rand.New(rand.NewSource(1)) - for i := 0; i < loopsN; i++ { - keypair := fastGenerateKeyPair(t, r) - - pubkey, err := keypair.SchnorrPublicKey() - if err != nil { - t.Errorf("Failed generating a pubkey, privateKey: '%s', error: %s", keypair, err) - } - msg := Hash{} - n, err := r.Read(msg[:]) - if err != nil || n != 32 { - t.Errorf("Failed generating a msg. read: '%d' bytes. .'%s'", n, err) - } - sig1, err := keypair.SchnorrSign(&msg) - if err != nil { - t.Errorf("Failed signing schnorr: key: '%s', msg: '%s', error: '%s'", keypair, msg, err) - } - sig2, err := keypair.SchnorrSign(&msg) - if err != nil { - t.Errorf("Failed signing schnorr: key: '%s', msg: '%s', error: '%s'", keypair, msg, err) - } - if *sig1 == *sig2 { - t.Errorf("Signing uses auxilary randomness, the odds of 2 signatures being the same is 1/2^128 '%s' '%s'", sig1, sig2) - } - serialized := sig1.Serialize() - sigDeserialized := DeserializeSchnorrSignature(serialized) - if *sig1 != *sigDeserialized { - t.Errorf("Failed Deserializing schnorr signature '%s'", serialized) - } - if !pubkey.SchnorrVerify(&msg, sig1) { - t.Errorf("Failed verifying schnorr signature privateKey: '%s' pubkey: '%s' signature: '%s'", keypair, pubkey, sig1) - } - if !pubkey.SchnorrVerify(&msg, sig2) { - t.Errorf("Failed verifying schnorr signature privateKey: '%s' pubkey: '%s' signature: '%s'", keypair, pubkey, sig2) +func TestPublicKey_SerializeFail(t *testing.T) { + ForAllAlgorithms(t, func(t *testing.T, r *rand.Rand, alg alogirthmInterface) { + pubkeyAlg := alg.EmptyPubKey() + _, err := pubkeyAlg.Serialize() + if err == nil { + t.Errorf("Zeroed public key isn't serializable as compressed") } - } + }) } -// decodeHex decodes the passed hex string and returns the resulting bytes. It -// panics if an error occurs. This is only used in the tests as a helper since -// the only way it can fail is if there is an error in the test source code. -func decodeHex(hexStr string) []byte { - b, err := hex.DecodeString(hexStr) - if err != nil { - panic("invalid hex string in test source: err " + err.Error() + - ", hex: " + hexStr) - } - - return b +func TestBadPrivateKeyPublicKeyFail(t *testing.T) { + ForAllAlgorithms(t, func(t *testing.T, r *rand.Rand, alg alogirthmInterface) { + goodPrivKey := alg.EmptyPrivKey().GenerateNew(t, r) + goodPublicKey, _, err := goodPrivKey.PublicKey() + if err != nil { + t.Fatalf("Failed generating pubkey from: '%s'. '%s'", goodPrivKey, err) + } + goodPublicKeyBackup := goodPublicKey.Clone() + goodPrivKeyBackup := goodPrivKey.Clone() + msg := Hash(*fastGenerateTweak(t, r)) + privkey := alg.EmptyPrivKey() + var zeros32 [32]byte + + _, _, err1 := privkey.PublicKey() + _, err2 := privkey.Sign(&msg) + err3 := privkey.Add(zeros32) + _, err4 := alg.EmptyPrivKey().DeserializeNew(privkey.Serialize()[:]) + if err1 == nil || err2 == nil || err3 == nil || err4 == nil { + t.Errorf("A zeroed key is invalid, err1: '%s', err2: '%s', err3: '%s', err4: '%s'", err1, err2, err3, err4) + } + + err5 := goodPrivKey.Add(zeros32) + _, err6 := goodPublicKey.Add(zeros32) + if err5 != nil || err6 != nil { + t.Errorf("It should be possible to add zero to a key, err4: '%s', err5: '%s'", err5, err6) + } + + privkey.SetBytes(Secp256k1Order.Bytes()) + _, _, err1 = privkey.PublicKey() + _, err2 = privkey.Sign(&msg) + _, err3 = alg.EmptyPrivKey().DeserializeNew(privkey.Serialize()[:]) + goodPrivKey = goodPrivKeyBackup.Clone() + goodPublicKey = goodPublicKeyBackup.Clone() + err4 = goodPrivKey.Add(intTo32Bytes(Secp256k1Order)) + _, err5 = goodPublicKey.Add(intTo32Bytes(Secp256k1Order)) + if err1 == nil || err2 == nil || err3 == nil || err4 == nil || err5 == nil { + t.Errorf("the group order isn't a valid key, err1: '%s', err2: '%s', err3: '%s', err4: '%s', err5: '%s'", err1, err2, err3, err4, err5) + } + privkey = alg.EmptyPrivKey().GenerateNew(t, r) + orderPlusOne := new(big.Int).SetInt64(1) + orderPlusOne.Add(orderPlusOne, Secp256k1Order) + privkey.SetBytes(orderPlusOne.Bytes()) + orderPlusOneArray := intTo32Bytes(orderPlusOne) + _, err = alg.EmptyPrivKey().DeserializeNew(orderPlusOneArray[:]) + if err1 == nil || err2 == nil || err3 == nil || err4 == nil { + t.Errorf("A key bigger than the group order isn't a valid key, err: '%s'", err) + } + goodPrivKey = goodPrivKeyBackup.Clone() + goodPublicKey = goodPublicKeyBackup.Clone() + + err1 = goodPrivKey.Add(intTo32Bytes(orderPlusOne)) + _, err2 = goodPublicKey.Add(intTo32Bytes(orderPlusOne)) + if err1 == nil || err2 == nil { + t.Errorf("A tweak bigger than the group order isn't a valid tweak, err1: '%s', err2: '%s'", err1, err2) + } + orderMinusOne := new(big.Int).Sub(Secp256k1Order, new(big.Int).SetInt64(1)) + orderPlusOne = nil + privkey.SetBytes(orderMinusOne.Bytes()) + _, _, err1 = privkey.PublicKey() + _, err2 = privkey.Sign(&msg) + _, err3 = alg.EmptyPrivKey().DeserializeNew(privkey.Serialize()[:]) + goodPrivKey = goodPrivKeyBackup.Clone() + goodPublicKey = goodPublicKeyBackup.Clone() + err4 = goodPrivKey.Add(intTo32Bytes(orderMinusOne)) + _, err5 = goodPublicKey.Add(intTo32Bytes(orderMinusOne)) + if err1 != nil || err2 != nil || err3 != nil || err4 != nil || err5 != nil { + t.Errorf("Group order - 1 should be a valid key, err1: '%s', err2: '%s', err3: '%s', err4: '%s', err5: '%s'", err1, err2, err3, err4, err5) + } + }) } -func TestSchnorrSignatureVerify(t *testing.T) { - // Test vectors taken from https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv - tests := []struct { - secKey []byte - pubKey []byte - auxRand []byte - message []byte - signature []byte - valid bool - }{ - { - decodeHex("0000000000000000000000000000000000000000000000000000000000000003"), - decodeHex("F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9"), - decodeHex("0000000000000000000000000000000000000000000000000000000000000000"), - decodeHex("0000000000000000000000000000000000000000000000000000000000000000"), - decodeHex("E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0"), - true, - }, - { - decodeHex("B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF"), - decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), - decodeHex("0000000000000000000000000000000000000000000000000000000000000001"), - decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), - decodeHex("6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A"), - true, - }, - { - decodeHex("C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9"), - decodeHex("DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8"), - decodeHex("C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906"), - decodeHex("7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C"), - decodeHex("5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7"), - true, - }, - { // test fails if msg is reduced modulo p or n - decodeHex("0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710"), - decodeHex("25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517"), - decodeHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), - decodeHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), - decodeHex("7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3"), - true, - }, - { - nil, - decodeHex("D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9"), - nil, - decodeHex("4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703"), - decodeHex("00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4"), - true, - }, - { //public key not on the curve - nil, - decodeHex("EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34"), - nil, - decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), - decodeHex("6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"), - false, - }, - { // has_even_y(R) is false - nil, - decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), - nil, - decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), - decodeHex("FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2"), - false, - }, - { // negated message - nil, - decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), - nil, - decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), - decodeHex("1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD"), - false, - }, - { // negated s value - nil, - decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), - nil, - decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), - decodeHex("6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6"), - false, - }, - { // sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 0 - nil, - decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), - nil, - decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), - decodeHex("0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051"), - false, - }, - { // sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 1 - nil, - decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), - nil, - decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), - decodeHex("00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197"), - false, - }, - { // sig[0:32] is not an X coordinate on the curve - nil, - decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), - nil, - decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), - decodeHex("4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"), - false, - }, - { // sig[0:32] is equal to field size - nil, - decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), - nil, - decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), - decodeHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"), - false, - }, - { // sig[32:64] is equal to curve order - nil, - decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), - nil, - decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), - decodeHex("6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"), - false, - }, - { // public key is not a valid X coordinate because it exceeds the field size - nil, - decodeHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30"), - nil, - decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), - decodeHex("6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"), - false, - }, - } - - msg32 := Hash{} - for i, test := range tests { - sig, err := DeserializeSchnorrSignatureFromSlice(test.signature) - if err != nil { - t.Fatal(err) - } - err = msg32.SetBytes(test.message) - if err != nil { - t.Fatal(err) - } - pubkey, err := DeserializeSchnorrPubKey(test.pubKey) - if err != nil && test.valid { - t.Fatalf("Schnorr test vector '%d': %s, pubkey: %x", i, err, test.pubKey) - } - if err == nil { - valid := pubkey.SchnorrVerify(&msg32, sig) - if valid != test.valid { - t.Errorf("Schnorr test vector '%d' expected verification: '%t', got: '%t'", i, valid, test.valid) +func TestParsePubKey(t *testing.T) { + ForAllAlgorithms(t, func(t *testing.T, r *rand.Rand, alg alogirthmInterface) { + for i := 0; i < loopsN; i++ { + privkey := alg.EmptyPrivKey().GenerateNew(t, r) + pubkey, _, err := privkey.PublicKey() + if err != nil { + t.Errorf("Failed Generating a pubkey from privateKey: '%s'. '%s'", privkey, err) } - } - if test.secKey != nil { - var auxRandPtr *[32]byte - keypair, err := DeserializePrivateKeyFromSlice(test.secKey) + serializedPubkey, err := pubkey.Serialize() if err != nil { - t.Fatal(err) + t.Errorf("Failed serializing the key: %s, error: '%s'", pubkey, err) } - if test.auxRand != nil { - auxRandPtr = &[32]byte{} - if len(auxRandPtr) != len(test.auxRand) { - t.Fatalf("Schnorr test vector '%d': invalid auxilary randomness length: %d != %d", i, len(auxRandPtr), len(test.auxRand)) - } - copy(auxRandPtr[:], test.auxRand) + pubkeyNew1, err := pubkey.DeserializeNew(serializedPubkey[:]) + if err != nil { + t.Errorf("Failed Parsing the compressed public key from keypair: '%s'. '%s'", pubkeyNew1, err) + } + if !pubkey.IsEqual(pubkeyNew1) { + t.Errorf("Pubkeys aren't the same: '%s', '%s',", pubkey, pubkeyNew1) } + } + }) +} - newSig, err := keypair.schnorrSignInternal(&msg32, auxRandPtr) +func TestSignVerifyParse(t *testing.T) { + ForAllAlgorithms(t, func(t *testing.T, r *rand.Rand, alg alogirthmInterface) { + for i := 0; i < loopsN; i++ { + privkey := alg.EmptyPrivKey().GenerateNew(t, r) + pubkey, _, err := privkey.PublicKey() if err != nil { - t.Fatal(err) + t.Errorf("Failed generating a pubkey, privateKey: '%s', error: %s", privkey, err) + } + msg := Hash{} + n, err := r.Read(msg[:]) + if err != nil || n != 32 { + t.Errorf("Failed generating a msg. read: '%d' bytes. .'%s'", n, err) + } + sig1, err := privkey.Sign(&msg) + if err != nil { + t.Errorf("Failed signing: key: '%s', msg: '%s', error: '%s'", privkey, msg, err) + } + sig2, err := privkey.Sign(&msg) + if err != nil { + t.Errorf("Failed signing: key: '%s', msg: '%s', error: '%s'", privkey, msg, err) + } + if sig1.IsEqual(sig2) { + t.Errorf("Signing uses auxilary randomness, the odds of 2 signatures being the same is 1/2^128 '%s' '%s'", sig1, sig2) + } + serialized := sig1.Serialize() + sigDeserialized, err := sig2.DeserializeNew(serialized[:]) + if err != nil { + t.Errorf("Failed deserializing sig: '%s', error: '%s'", serialized, err) + } + if !sig1.IsEqual(sigDeserialized) { + t.Errorf("Failed Deserializing signatureInterface '%s'", serialized) + } + if !pubkey.Verify(&msg, sig1) { + t.Errorf("Failed verifying signatureInterface privateKey: '%s' pubkey: '%s' signatureInterface: '%s'", privkey, pubkey, sig1) } - if !newSig.IsEqual(sig) { - t.Errorf("Schnorr test vector '%d' expected sig: %s. got: %s", i, sig, newSig) + if !pubkey.Verify(&msg, sig2) { + t.Errorf("Failed verifying signatureInterface privateKey: '%s' pubkey: '%s' signatureInterface: '%s'", privkey, pubkey, sig2) } } - } + }) } -func TestSchnorrPublicKey_IsEqual(t *testing.T) { - r := rand.New(rand.NewSource(1)) - goodKeyPair := fastGenerateKeyPair(t, r) - goodPublicKey, err := goodKeyPair.SchnorrPublicKey() - if err != nil { - t.Fatalf("Failed generating pubkey from: '%s'. '%s'", goodKeyPair, err) - } - badPublicKey := SchnorrPublicKey{} - if badPublicKey.IsEqual(goodPublicKey) { - t.Errorf("Empty publickey shouldn't be equal to good one") - } - if !badPublicKey.IsEqual(&SchnorrPublicKey{}) { - t.Errorf("Empty publickey should be equal to another empty pubkey") - } - var nilPubKey *SchnorrPublicKey = nil - if nilPubKey.IsEqual(goodPublicKey) { - t.Errorf("nil publickey shouldn't be equal to good one") - } +func TestPublicKey_IsEqual(t *testing.T) { + ForAllAlgorithms(t, func(t *testing.T, r *rand.Rand, alg alogirthmInterface) { + goodPrivKey := alg.EmptyPrivKey().GenerateNew(t, r) + goodPublicKey, _, err := goodPrivKey.PublicKey() + if err != nil { + t.Fatalf("Failed generating pubkey from: '%s'. '%s'", goodPrivKey, err) + } + badPublicKey := alg.EmptyPubKey() + if badPublicKey.IsEqual(goodPublicKey) { + t.Errorf("Empty publickey shouldn't be equal to good one") + } + if !badPublicKey.IsEqual(alg.EmptyPubKey()) { + t.Errorf("Empty publickey should be equal to another empty pubkey") + } + ty := reflect.TypeOf(goodPublicKey) + nilPubKey := reflect.Zero(ty).Interface().(publicKeyInterface) + if nilPubKey.IsEqual(goodPublicKey) { + t.Fatalf("nil publickey shouldn't be equal to good one") + } - if !nilPubKey.IsEqual(nil) { - t.Errorf("two nil pubkeys should be equal") - } + if !nilPubKey.IsEqual(nil) { + t.Fatalf("two nil pubkeys should be equal") + } - copyGoodPubkey := *goodPublicKey - if !copyGoodPubkey.IsEqual(goodPublicKey) { - t.Errorf("A pubkey and its copy should be the same") - } - goodKeyPair2 := fastGenerateKeyPair(t, r) - goodPublicKey2, err := goodKeyPair2.SchnorrPublicKey() - if err != nil { - t.Fatalf("Failed generating pubkey from: '%s'. '%s'", goodKeyPair2, err) - } + copyGoodPubkey := goodPublicKey.Clone() + if !copyGoodPubkey.IsEqual(goodPublicKey) { + t.Errorf("A pubkey and its copy should be the same") + } + goodPrivKey2 := alg.EmptyPrivKey().GenerateNew(t, r) + goodPublicKey2, _, err := goodPrivKey2.PublicKey() + if err != nil { + t.Fatalf("Failed generating pubkey from: '%s'. '%s'", goodPrivKey2, err) + } - if goodPublicKey.IsEqual(goodPublicKey2) { - t.Errorf("'%s' shouldn't be equal to %s", goodPublicKey, goodPublicKey2) - } + if goodPublicKey.IsEqual(goodPublicKey2) { + t.Errorf("'%s' shouldn't be equal to %s", goodPublicKey, goodPublicKey2) + } + }) } -func TestSchnorrSignature_IsEqual(t *testing.T) { - r := rand.New(rand.NewSource(1)) - serializedSig := SerializedSchnorrSignature{} - n, err := r.Read(serializedSig[:]) - if err != nil || n != len(serializedSig) { - t.Errorf("Failed generating a random signature. read: '%d' bytes.. '%s'", n, err) - } - goodSignature := DeserializeSchnorrSignature(&serializedSig) +func TestSignature_IsEqual(t *testing.T) { + ForAllAlgorithms(t, func(t *testing.T, r *rand.Rand, alg alogirthmInterface) { + var serializedSig [64]byte + n, err := r.Read(serializedSig[:]) + if err != nil || n != len(serializedSig) { + t.Errorf("Failed generating a random signatureInterface. read: '%d' bytes.. '%s'", n, err) + } + goodSignature, err := alg.EmptySignature().DeserializeNew(serializedSig[:]) + if err != nil { + t.Fatalf("Failed deserializing signatureInterface: %s", goodSignature) + } - emptySignature := SchnorrSignature{} - if emptySignature.IsEqual(goodSignature) { - t.Errorf("Empty signature shouldn't be equal to good one") - } - if !emptySignature.IsEqual(&SchnorrSignature{}) { - t.Errorf("Empty signature should be equal to another empty signature") - } - var nilSignature *SchnorrSignature = nil - if nilSignature.IsEqual(goodSignature) { - t.Errorf("nil signature shouldn't be equal to good one") - } + emptySignature := alg.EmptySignature() + if emptySignature.IsEqual(goodSignature) { + t.Errorf("Empty signatureInterface shouldn't be equal to good one") + } + if !emptySignature.IsEqual(alg.EmptySignature()) { + t.Errorf("Empty signatureInterface should be equal to another empty signatureInterface") + } + ty := reflect.TypeOf(alg.EmptySignature()) + nilSignature := reflect.Zero(ty).Interface().(signatureInterface) + if nilSignature.IsEqual(goodSignature) { + t.Errorf("nil signatureInterface shouldn't be equal to good one") + } - if !nilSignature.IsEqual(nil) { - t.Errorf("two nil signatures should be equal") - } + if !nilSignature.IsEqual(nil) { + t.Errorf("two nil signatures should be equal") + } - copyGoodSignature := *goodSignature - if !copyGoodSignature.IsEqual(goodSignature) { - t.Errorf("A signature and its copy should be the same") - } + copyGoodSignature := goodSignature.Clone() + if !copyGoodSignature.IsEqual(goodSignature) { + t.Errorf("A signatureInterface and its copy should be the same") + } - serializedSig2 := SerializedSchnorrSignature{} - n, err = r.Read(serializedSig2[:]) - if err != nil || n != len(serializedSig2) { - t.Errorf("Failed generating a random signature. read: '%d' bytes.. '%s'", n, err) - } + var serializedSig2 [64]byte + n, err = r.Read(serializedSig2[:]) + if err != nil || n != len(serializedSig2) { + t.Errorf("Failed generating a random signatureInterface. read: '%d' bytes.. '%s'", n, err) + } - goodSignature2 := DeserializeSchnorrSignature(&serializedSig2) - if goodSignature.IsEqual(goodSignature2) { - t.Errorf("'%s' shouldn't be equal to %s", goodSignature, goodSignature2) - } + goodSignature2, err := alg.EmptySignature().DeserializeNew(serializedSig2[:]) + if err != nil { + t.Fatalf("Failed deserializing signature: %s", serializedSig) + } + if goodSignature.IsEqual(goodSignature2) { + t.Errorf("'%s' shouldn't be equal to %s", goodSignature, goodSignature2) + } + }) } func TestHash_IsEqual(t *testing.T) { @@ -608,40 +449,38 @@ func TestHash_IsEqual(t *testing.T) { } } -func BenchmarkSchnorrVerify(b *testing.B) { - b.ReportAllocs() - r := rand.New(rand.NewSource(1)) - sigs := make([]*SchnorrSignature, b.N) - msgs := make([]Hash, b.N) - pubkeys := make([]SchnorrPublicKey, b.N) - for i := 0; i < b.N; i++ { - msg := Hash{} - n, err := r.Read(msg[:]) - if err != nil || n != 32 { - panic(fmt.Sprintf("benchmark failed: '%s', n: %d", err, n)) - } - privkey, err := GeneratePrivateKey() - if err != nil { - panic("benchmark failed: " + err.Error()) - } - sigTmp, err := privkey.SchnorrSign(&msg) - if err != nil { - panic("benchmark failed: " + err.Error()) - } - sigs[i] = sigTmp - pubkeyTmp, err := privkey.SchnorrPublicKey() - if err != nil { - panic("benchmark failed: " + err.Error()) - } - pubkeys[i] = *pubkeyTmp - msgs[i] = msg - } - b.ResetTimer() - sum := 0 - for i := 0; i < b.N; i++ { - ret := pubkeys[i].SchnorrVerify(&msgs[i], sigs[i]) - if ret { - sum++ - } +func BenchmarkVerify(b *testing.B) { + for _, alg := range algorithms { + algCopy := alg + b.Run(algCopy.String(), func(b *testing.B) { + r := rand.New(rand.NewSource(1)) + sigs := make([]signatureInterface, loopsN) + msgs := make([]Hash, loopsN) + pubkeys := make([]publicKeyInterface, loopsN) + for i := 0; i < loopsN; i++ { + msg := Hash{} + n, err := r.Read(msg[:]) + if err != nil || n != 32 { + panic(fmt.Sprintf("benchmark failed: '%s', n: %d", err, n)) + } + privkey := alg.EmptyPrivKey().GenerateNew(b, r) + sigTmp, err := privkey.Sign(&msg) + if err != nil { + panic("benchmark failed: " + err.Error()) + } + sigs[i] = sigTmp + pubkeyTmp, _, err := privkey.PublicKey() + if err != nil { + panic("benchmark failed: " + err.Error()) + } + pubkeys[i] = pubkeyTmp + msgs[i] = msg + } + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + pubkeys[i%loopsN].Verify(&msgs[i%loopsN], sigs[i%loopsN]) + } + }) } } diff --git a/testvectors_test.go b/testvectors_test.go new file mode 100644 index 0000000..b8476a8 --- /dev/null +++ b/testvectors_test.go @@ -0,0 +1,196 @@ +package secp256k1 + +import ( + "encoding/hex" + "testing" +) + +// decodeHex decodes the passed hex string and returns the resulting bytes. It +// panics if an error occurs. This is only used in the tests as a helper since +// the only way it can fail is if there is an error in the test source code. +func decodeHex(hexStr string) []byte { + b, err := hex.DecodeString(hexStr) + if err != nil { + panic("invalid hex string in test source: err " + err.Error() + + ", hex: " + hexStr) + } + + return b +} + +func TestSchnorrSignatureVerify(t *testing.T) { + // Test vectors taken from https://github.com/bitcoin/bips/blob/master/bip-0340/test-vectors.csv + tests := []struct { + secKey []byte + pubKey []byte + auxRand []byte + message []byte + signature []byte + valid bool + }{ + { + decodeHex("0000000000000000000000000000000000000000000000000000000000000003"), + decodeHex("F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9"), + decodeHex("0000000000000000000000000000000000000000000000000000000000000000"), + decodeHex("0000000000000000000000000000000000000000000000000000000000000000"), + decodeHex("E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0"), + true, + }, + { + decodeHex("B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF"), + decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + decodeHex("0000000000000000000000000000000000000000000000000000000000000001"), + decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + decodeHex("6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A"), + true, + }, + { + decodeHex("C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9"), + decodeHex("DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8"), + decodeHex("C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906"), + decodeHex("7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C"), + decodeHex("5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7"), + true, + }, + { // test fails if msg is reduced modulo p or n + decodeHex("0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710"), + decodeHex("25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517"), + decodeHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), + decodeHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), + decodeHex("7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3"), + true, + }, + { + nil, + decodeHex("D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9"), + nil, + decodeHex("4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703"), + decodeHex("00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4"), + true, + }, + { //public key not on the curve + nil, + decodeHex("EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34"), + nil, + decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + decodeHex("6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"), + false, + }, + { // has_even_y(R) is false + nil, + decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + nil, + decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + decodeHex("FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2"), + false, + }, + { // negated message + nil, + decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + nil, + decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + decodeHex("1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD"), + false, + }, + { // negated s value + nil, + decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + nil, + decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + decodeHex("6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6"), + false, + }, + { // sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 0 + nil, + decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + nil, + decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + decodeHex("0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051"), + false, + }, + { // sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 1 + nil, + decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + nil, + decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + decodeHex("00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197"), + false, + }, + { // sig[0:32] is not an X coordinate on the curve + nil, + decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + nil, + decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + decodeHex("4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"), + false, + }, + { // sig[0:32] is equal to field size + nil, + decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + nil, + decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + decodeHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"), + false, + }, + { // sig[32:64] is equal to curve order + nil, + decodeHex("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659"), + nil, + decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + decodeHex("6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"), + false, + }, + { // public key is not a valid X coordinate because it exceeds the field size + nil, + decodeHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30"), + nil, + decodeHex("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89"), + decodeHex("6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B"), + false, + }, + } + + msg32 := Hash{} + for i, test := range tests { + sig, err := DeserializeSchnorrSignatureFromSlice(test.signature) + if err != nil { + t.Fatal(err) + } + err = msg32.SetBytes(test.message) + if err != nil { + t.Fatal(err) + } + pubkey, err := DeserializeSchnorrPubKey(test.pubKey) + if err != nil && test.valid { + t.Fatalf("Schnorr test vector '%d': %s, pubkey: %x", i, err, test.pubKey) + } + if err == nil { + valid := pubkey.SchnorrVerify(&msg32, sig) + if valid != test.valid { + t.Errorf("Schnorr test vector '%d' expected verification: '%t', got: '%t'", i, valid, test.valid) + } + } + if test.secKey != nil { + var auxRandPtr *[32]byte + keypair, err := DeserializeSchnorrPrivateKeyFromSlice(test.secKey) + if err != nil { + t.Fatal(err) + } + if test.auxRand != nil { + auxRandPtr = &[32]byte{} + if len(auxRandPtr) != len(test.auxRand) { + t.Fatalf("Schnorr test vector '%d': invalid auxilary randomness length: %d != %d", i, len(auxRandPtr), len(test.auxRand)) + } + copy(auxRandPtr[:], test.auxRand) + } + + newSig, err := keypair.schnorrSignInternal(&msg32, auxRandPtr) + if err != nil { + t.Fatal(err) + } + if !newSig.IsEqual(sig) { + t.Errorf("Schnorr test vector '%d' expected sig: %s. got: %s", i, sig, newSig) + } + } + } +} From 5382a48da94ceda65d1de94690168bca36fcbbf6 Mon Sep 17 00:00:00 2001 From: Elichai Turkel Date: Thu, 18 Mar 2021 15:06:09 +0200 Subject: [PATCH 5/5] Fix comments and refactor some checks --- ecdsa_privkey.go | 10 ++++++---- ecdsa_pubkey.go | 6 +++--- schnorr_keypair.go | 5 ++++- schnorr_pubkey.go | 4 +--- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/ecdsa_privkey.go b/ecdsa_privkey.go index d7fcc57..88de9b5 100644 --- a/ecdsa_privkey.go +++ b/ecdsa_privkey.go @@ -8,8 +8,7 @@ import ( "unsafe" ) -// ECDSAPrivateKey is a type representing a Secp256k1 private key. -// This private key can be used to create Schnorr/ECDSA signatures +// ECDSAPrivateKey is a type representing a Secp256k1 ECDSA private key. type ECDSAPrivateKey struct { privateKey [SerializedPrivateKeySize]byte init bool @@ -92,12 +91,15 @@ func GenerateECDSAPrivateKey() (key *ECDSAPrivateKey, err error) { cPtr := (*C.uchar)(&key.privateKey[0]) for { n, tmpErr := rand.Read(key.privateKey[:]) - if tmpErr != nil || n != len(key.privateKey) { + if tmpErr != nil { return nil, tmpErr } + if n != len(key.privateKey) { + panic("The standard library promises that this should never happen") + } ret := C.secp256k1_ec_seckey_verify(C.secp256k1_context_no_precomp, cPtr) if ret == 1 { - return + return key, nil } } } diff --git a/ecdsa_pubkey.go b/ecdsa_pubkey.go index 9008653..7360d25 100644 --- a/ecdsa_pubkey.go +++ b/ecdsa_pubkey.go @@ -18,7 +18,7 @@ type ECDSAPublicKey struct { init bool } -// SerializedECDSAPublicKey is a is a byte array representing the storage representation of a compressed or uncompressed ECDSAPublicKey +// SerializedECDSAPublicKey is a is a byte array representing the storage representation of a compressed ECDSAPublicKey type SerializedECDSAPublicKey [SerializedECDSAPublicKeySize]byte // IsEqual returns true if target is the same as key. @@ -69,8 +69,8 @@ func (key *ECDSAPublicKey) ECDSAVerify(hash *Hash, signature *ECDSASignature) bo } // DeserializeECDSAPubKey deserializes a serialized ECDSA public key, verifying it's valid. -// it supports both compressed(33 bytes) and uncompressed(65 bytes) public keys. -// it does not support hybrid(65 bytes) keys. +// it supports only compressed(33 bytes) public keys. +// it does not support uncompressed(65 bytes) or hybrid(65 bytes) keys. func DeserializeECDSAPubKey(serializedPubKey []byte) (*ECDSAPublicKey, error) { if len(serializedPubKey) != SerializedECDSAPublicKeySize { return nil, errors.New(fmt.Sprintf("serializedPubKey has to be %d bytes, instead got :%d", SerializedECDSAPublicKeySize, len(serializedPubKey))) diff --git a/schnorr_keypair.go b/schnorr_keypair.go index 67c1c94..da2d744 100644 --- a/schnorr_keypair.go +++ b/schnorr_keypair.go @@ -63,9 +63,12 @@ func GenerateSchnorrKeyPair() (key *SchnorrKeyPair, err error) { rawKey := SerializedPrivateKey{} for { n, err := rand.Read(rawKey[:]) - if err != nil || n != len(rawKey) { + if err != nil { return nil, err } + if n != len(rawKey) { + panic("The standard library promises that this should never happen") + } key, err = DeserializeSchnorrPrivateKey(&rawKey) if err == nil { return key, nil diff --git a/schnorr_pubkey.go b/schnorr_pubkey.go index cdba866..bc9955c 100644 --- a/schnorr_pubkey.go +++ b/schnorr_pubkey.go @@ -19,7 +19,7 @@ type SchnorrPublicKey struct { init bool } -// SerializedSchnorrPublicKey is a is a byte array representing the storage representation of a compressed or uncompressed SchnorrPublicKey +// SerializedSchnorrPublicKey is a is a byte array representing the storage representation of a SchnorrPublicKey type SerializedSchnorrPublicKey [SerializedSchnorrPublicKeySize]byte // IsEqual returns true if target is the same as key. @@ -71,8 +71,6 @@ func (key *SchnorrPublicKey) SchnorrVerify(hash *Hash, signature *SchnorrSignatu } // DeserializeSchnorrPubKey deserializes a serialized schnorr public key, verifying it's valid. -// it supports both compressed(33 bytes) and uncompressed(65 bytes) public keys. -// it does not support hybrid(65 bytes) keys. func DeserializeSchnorrPubKey(serializedPubKey []byte) (*SchnorrPublicKey, error) { if len(serializedPubKey) != SerializedSchnorrPublicKeySize { return nil, errors.New(fmt.Sprintf("serializedPubKey has to be %d bytes, instead got :%d", SerializedSchnorrPublicKeySize, len(serializedPubKey)))