-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #10 from kaspanet/ecdsa
Add ECDSA support
- Loading branch information
Showing
9 changed files
with
1,209 additions
and
556 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
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 ECDSA private key. | ||
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 { | ||
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 key, nil | ||
} | ||
} | ||
} | ||
|
||
// 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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 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 "<Invalid ECDSAPublicKey>" | ||
} | ||
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 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))) | ||
} | ||
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
Oops, something went wrong.