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 }