Skip to content

Commit

Permalink
Merge pull request #10 from kaspanet/ecdsa
Browse files Browse the repository at this point in the history
Add ECDSA support
  • Loading branch information
someone235 authored Mar 18, 2021
2 parents f8b3e63 + 5382a48 commit 2cb239d
Show file tree
Hide file tree
Showing 9 changed files with 1,209 additions and 556 deletions.
139 changes: 139 additions & 0 deletions ecdsa_privkey.go
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
}
131 changes: 131 additions & 0 deletions ecdsa_pubkey.go
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
}
78 changes: 78 additions & 0 deletions ecdsa_signature.go
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)
}
Loading

0 comments on commit 2cb239d

Please sign in to comment.