github.com/mavryk-network/mvgo@v1.19.9/mavryk/crypto.go (about)

     1  // Copyright (c) 2021 Blockwatch Data Inc.
     2  // Author: alex@blockwatch.cc
     3  
     4  package mavryk
     5  
     6  import (
     7  	"fmt"
     8  	"math/big"
     9  
    10  	"crypto/ecdsa"
    11  	"crypto/elliptic"
    12  	"crypto/rand"
    13  	"crypto/sha512"
    14  
    15  	"github.com/decred/dcrd/dcrec/secp256k1/v4"
    16  	"golang.org/x/crypto/nacl/secretbox"
    17  	"golang.org/x/crypto/pbkdf2"
    18  )
    19  
    20  // ecNormalizeSignature ensures strict compliance with the EC spec by returning
    21  // S mod n for the appropriate keys curve.
    22  //
    23  // Details:
    24  //
    25  //	Step #6 of the ECDSA algorithm [x] defines an `S` value mod n[0],
    26  //	but most signers (OpenSSL, SoftHSM, YubiHSM) don't return a strict modulo.
    27  //	This variability was exploited with transaction malleability in Bitcoin,
    28  //	leading to BIP#62.  BIP#62 Rule #5[1] requires that signatures return a
    29  //	strict S = ... mod n which this function forces implemented in btcd here [2]
    30  //	[0]: https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm
    31  //	[1]: https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#new-rules
    32  //	[2]: https://github.com/btcsuite/btcd/blob/master/btcec/signature.go#L49
    33  //
    34  // See also Ecadlabs Signatory:
    35  // https://github.com/ecadlabs/signatory/blob/f57871c2300cb5a53236ea5fcb4f203012b4fe41/pkg/cryptoutils/crypto.go#L17
    36  func ecNormalizeSignature(r, s *big.Int, c elliptic.Curve) (*big.Int, *big.Int) {
    37  	r = new(big.Int).Set(r)
    38  	s = new(big.Int).Set(s)
    39  
    40  	order := c.Params().N
    41  	quo := new(big.Int).Quo(order, new(big.Int).SetInt64(2))
    42  	if s.Cmp(quo) > 0 {
    43  		s = s.Sub(order, s)
    44  	}
    45  	return r, s
    46  }
    47  
    48  func ecSign(sk *ecdsa.PrivateKey, hash []byte) ([]byte, error) {
    49  	r, s, err := ecdsa.Sign(rand.Reader, sk, hash)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  	// normalize
    54  	r, s = ecNormalizeSignature(r, s, sk.Curve)
    55  	// serialize
    56  	buf := make([]byte, 64)
    57  	r.FillBytes(buf[:32])
    58  	s.FillBytes(buf[32:])
    59  	return buf, nil
    60  }
    61  
    62  func ecVerifySignature(pk *ecdsa.PublicKey, hash []byte, sig Signature) bool {
    63  	r := new(big.Int).SetBytes(sig.Data[:32])
    64  	s := new(big.Int).SetBytes(sig.Data[32:])
    65  	return ecdsa.Verify(pk, hash, r, s)
    66  }
    67  
    68  func ecPrivateKeyFromBytes(b []byte, curve elliptic.Curve) (key *ecdsa.PrivateKey, err error) {
    69  	k := new(big.Int).SetBytes(b)
    70  	curveOrder := curve.Params().N
    71  	if k.Cmp(curveOrder) >= 0 {
    72  		return nil, fmt.Errorf("tezos: invalid private key for curve %s", curve.Params().Name)
    73  	}
    74  
    75  	priv := &ecdsa.PrivateKey{
    76  		PublicKey: ecdsa.PublicKey{
    77  			Curve: curve,
    78  		},
    79  		D: k,
    80  	}
    81  
    82  	// https://cs.opensource.google/go/go/+/refs/tags/go1.17.5:src/crypto/ecdsa/ecdsa.go;l=149
    83  	priv.PublicKey.X, priv.PublicKey.Y = curve.ScalarBaseMult(k.Bytes())
    84  	return priv, nil
    85  }
    86  
    87  // See https://github.com/golang/go/blob/master/src/crypto/elliptic/elliptic.go
    88  func ecUnmarshalCompressed(curve elliptic.Curve, data []byte) (pk *ecdsa.PublicKey, err error) {
    89  	byteLen := (curve.Params().BitSize + 7) / 8
    90  	if len(data) != 1+byteLen {
    91  		return nil, fmt.Errorf("tezos: (%s) invalid public key length: %d", curve.Params().Name, len(data))
    92  	}
    93  	if data[0] != 2 && data[0] != 3 { // compressed form
    94  		return nil, fmt.Errorf("tezos: (%s) invalid public key compression", curve.Params().Name)
    95  	}
    96  	p := curve.Params().P
    97  	x := new(big.Int).SetBytes(data[1:])
    98  	if x.Cmp(p) >= 0 {
    99  		return nil, fmt.Errorf("tezos: (%s) invalid public key", curve.Params().Name)
   100  	}
   101  
   102  	// secp256k1 polynomial: x³ + b
   103  	// P-* polynomial: x³ - 3x + b
   104  	y := new(big.Int).Mul(x, x)
   105  	y.Mul(y, x)
   106  	if curve != secp256k1.S256() {
   107  		x1 := new(big.Int).Lsh(x, 1)
   108  		x1.Add(x1, x)
   109  		y.Sub(y, x1)
   110  	}
   111  	y.Add(y, curve.Params().B)
   112  	y.Mod(y, curve.Params().P)
   113  	y.ModSqrt(y, p)
   114  
   115  	if y == nil {
   116  		return nil, fmt.Errorf("tezos: (%s) invalid public key", curve.Params().Name)
   117  	}
   118  	if byte(y.Bit(0)) != data[0]&1 {
   119  		y.Neg(y).Mod(y, p)
   120  	}
   121  	if !curve.IsOnCurve(x, y) {
   122  		return nil, fmt.Errorf("tezos: (%s) invalid public key", curve.Params().Name)
   123  	}
   124  
   125  	pk = &ecdsa.PublicKey{
   126  		Curve: curve,
   127  		X:     x,
   128  		Y:     y,
   129  	}
   130  	return
   131  }
   132  
   133  const (
   134  	encIterations = 32768
   135  	encKeyLen     = 32
   136  )
   137  
   138  func decryptPrivateKey(enc []byte, fn PassphraseFunc) ([]byte, error) {
   139  	if fn == nil {
   140  		return nil, ErrPassphrase
   141  	}
   142  	passphrase, err := fn()
   143  	if err != nil {
   144  		return nil, err
   145  	}
   146  	if len(passphrase) == 0 {
   147  		return nil, ErrPassphrase
   148  	}
   149  
   150  	salt, box := enc[:8], enc[8:]
   151  	secretboxKey := pbkdf2.Key(passphrase, salt, encIterations, encKeyLen, sha512.New)
   152  
   153  	var (
   154  		tmp   [32]byte
   155  		nonce [24]byte // implicitly 0x00..
   156  	)
   157  	copy(tmp[:], secretboxKey)
   158  	dec, ok := secretbox.Open(nil, box, &nonce, &tmp)
   159  	if !ok {
   160  		return nil, fmt.Errorf("tezos: private key decrypt failed")
   161  	}
   162  	return dec, nil
   163  }
   164  
   165  func encryptPrivateKey(key []byte, fn PassphraseFunc) ([]byte, error) {
   166  	if fn == nil {
   167  		return nil, ErrPassphrase
   168  	}
   169  	passphrase, err := fn()
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  	if len(passphrase) == 0 {
   174  		return nil, ErrPassphrase
   175  	}
   176  
   177  	salt := make([]byte, 8)
   178  	_, err = rand.Read(salt)
   179  	if err != nil {
   180  		return nil, err
   181  	}
   182  	secretboxKey := pbkdf2.Key(passphrase, salt, encIterations, encKeyLen, sha512.New)
   183  
   184  	var (
   185  		tmp   [32]byte
   186  		nonce [24]byte // implicitly 0x00..
   187  	)
   188  	copy(tmp[:], secretboxKey)
   189  	enc := secretbox.Seal(nil, key, &nonce, &tmp)
   190  	return append(salt, enc...), nil
   191  }