github.com/MetalBlockchain/metalgo@v1.11.9/utils/crypto/secp256k1/secp256k1.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package secp256k1 5 6 import ( 7 "errors" 8 "fmt" 9 "strings" 10 11 "github.com/decred/dcrd/dcrec/secp256k1/v4/ecdsa" 12 13 "github.com/MetalBlockchain/metalgo/cache" 14 "github.com/MetalBlockchain/metalgo/ids" 15 "github.com/MetalBlockchain/metalgo/utils/cb58" 16 "github.com/MetalBlockchain/metalgo/utils/hashing" 17 18 stdecdsa "crypto/ecdsa" 19 secp256k1 "github.com/decred/dcrd/dcrec/secp256k1/v4" 20 ) 21 22 const ( 23 // SignatureLen is the number of bytes in a secp2561k recoverable signature 24 SignatureLen = 65 25 26 // PrivateKeyLen is the number of bytes in a secp2561k recoverable private 27 // key 28 PrivateKeyLen = 32 29 30 // PublicKeyLen is the number of bytes in a secp2561k recoverable public key 31 PublicKeyLen = 33 32 33 // from the decred library: 34 // compactSigMagicOffset is a value used when creating the compact signature 35 // recovery code inherited from Bitcoin and has no meaning, but has been 36 // retained for compatibility. For historical purposes, it was originally 37 // picked to avoid a binary representation that would allow compact 38 // signatures to be mistaken for other components. 39 compactSigMagicOffset = 27 40 41 PrivateKeyPrefix = "PrivateKey-" 42 nullStr = "null" 43 ) 44 45 var ( 46 ErrInvalidSig = errors.New("invalid signature") 47 errCompressed = errors.New("wasn't expecting a compressed key") 48 errMissingQuotes = errors.New("first and last characters should be quotes") 49 errMissingKeyPrefix = fmt.Errorf("private key missing %s prefix", PrivateKeyPrefix) 50 errInvalidPrivateKeyLength = fmt.Errorf("private key has unexpected length, expected %d", PrivateKeyLen) 51 errInvalidPublicKeyLength = fmt.Errorf("public key has unexpected length, expected %d", PublicKeyLen) 52 errInvalidSigLen = errors.New("invalid signature length") 53 errMutatedSig = errors.New("signature was mutated from its original format") 54 ) 55 56 func NewPrivateKey() (*PrivateKey, error) { 57 k, err := secp256k1.GeneratePrivateKey() 58 return &PrivateKey{sk: k}, err 59 } 60 61 func ToPublicKey(b []byte) (*PublicKey, error) { 62 if len(b) != PublicKeyLen { 63 return nil, errInvalidPublicKeyLength 64 } 65 66 key, err := secp256k1.ParsePubKey(b) 67 return &PublicKey{ 68 pk: key, 69 bytes: b, 70 }, err 71 } 72 73 func ToPrivateKey(b []byte) (*PrivateKey, error) { 74 if len(b) != PrivateKeyLen { 75 return nil, errInvalidPrivateKeyLength 76 } 77 return &PrivateKey{ 78 sk: secp256k1.PrivKeyFromBytes(b), 79 bytes: b, 80 }, nil 81 } 82 83 func RecoverPublicKey(msg, sig []byte) (*PublicKey, error) { 84 return RecoverPublicKeyFromHash(hashing.ComputeHash256(msg), sig) 85 } 86 87 func RecoverPublicKeyFromHash(hash, sig []byte) (*PublicKey, error) { 88 if err := verifySECP256K1RSignatureFormat(sig); err != nil { 89 return nil, err 90 } 91 92 sig, err := sigToRawSig(sig) 93 if err != nil { 94 return nil, err 95 } 96 97 rawPubkey, compressed, err := ecdsa.RecoverCompact(sig, hash) 98 if err != nil { 99 return nil, ErrInvalidSig 100 } 101 102 if compressed { 103 return nil, errCompressed 104 } 105 106 return &PublicKey{pk: rawPubkey}, nil 107 } 108 109 type RecoverCache struct { 110 cache.LRU[ids.ID, *PublicKey] 111 } 112 113 func (r *RecoverCache) RecoverPublicKey(msg, sig []byte) (*PublicKey, error) { 114 return r.RecoverPublicKeyFromHash(hashing.ComputeHash256(msg), sig) 115 } 116 117 func (r *RecoverCache) RecoverPublicKeyFromHash(hash, sig []byte) (*PublicKey, error) { 118 cacheBytes := make([]byte, len(hash)+len(sig)) 119 copy(cacheBytes, hash) 120 copy(cacheBytes[len(hash):], sig) 121 id := hashing.ComputeHash256Array(cacheBytes) 122 if cachedPublicKey, ok := r.Get(id); ok { 123 return cachedPublicKey, nil 124 } 125 126 pubKey, err := RecoverPublicKeyFromHash(hash, sig) 127 if err != nil { 128 return nil, err 129 } 130 131 r.Put(id, pubKey) 132 return pubKey, nil 133 } 134 135 type PublicKey struct { 136 pk *secp256k1.PublicKey 137 addr ids.ShortID 138 bytes []byte 139 } 140 141 func (k *PublicKey) Verify(msg, sig []byte) bool { 142 return k.VerifyHash(hashing.ComputeHash256(msg), sig) 143 } 144 145 func (k *PublicKey) VerifyHash(hash, sig []byte) bool { 146 pk, err := RecoverPublicKeyFromHash(hash, sig) 147 if err != nil { 148 return false 149 } 150 return k.Address() == pk.Address() 151 } 152 153 // ToECDSA returns the ecdsa representation of this public key 154 func (k *PublicKey) ToECDSA() *stdecdsa.PublicKey { 155 return k.pk.ToECDSA() 156 } 157 158 func (k *PublicKey) Address() ids.ShortID { 159 if k.addr == ids.ShortEmpty { 160 addr, err := ids.ToShortID(hashing.PubkeyBytesToAddress(k.Bytes())) 161 if err != nil { 162 panic(err) 163 } 164 k.addr = addr 165 } 166 return k.addr 167 } 168 169 func (k *PublicKey) Bytes() []byte { 170 if k.bytes == nil { 171 k.bytes = k.pk.SerializeCompressed() 172 } 173 return k.bytes 174 } 175 176 type PrivateKey struct { 177 sk *secp256k1.PrivateKey 178 pk *PublicKey 179 bytes []byte 180 } 181 182 func (k *PrivateKey) PublicKey() *PublicKey { 183 if k.pk == nil { 184 k.pk = &PublicKey{pk: k.sk.PubKey()} 185 } 186 return k.pk 187 } 188 189 func (k *PrivateKey) Address() ids.ShortID { 190 return k.PublicKey().Address() 191 } 192 193 func (k *PrivateKey) Sign(msg []byte) ([]byte, error) { 194 return k.SignHash(hashing.ComputeHash256(msg)) 195 } 196 197 func (k *PrivateKey) SignHash(hash []byte) ([]byte, error) { 198 sig := ecdsa.SignCompact(k.sk, hash, false) // returns [v || r || s] 199 return rawSigToSig(sig) 200 } 201 202 // ToECDSA returns the ecdsa representation of this private key 203 func (k *PrivateKey) ToECDSA() *stdecdsa.PrivateKey { 204 return k.sk.ToECDSA() 205 } 206 207 func (k *PrivateKey) Bytes() []byte { 208 if k.bytes == nil { 209 k.bytes = k.sk.Serialize() 210 } 211 return k.bytes 212 } 213 214 func (k *PrivateKey) String() string { 215 // We assume that the maximum size of a byte slice that 216 // can be stringified is at least the length of a SECP256K1 private key 217 keyStr, _ := cb58.Encode(k.Bytes()) 218 return PrivateKeyPrefix + keyStr 219 } 220 221 func (k *PrivateKey) MarshalJSON() ([]byte, error) { 222 return []byte(`"` + k.String() + `"`), nil 223 } 224 225 func (k *PrivateKey) MarshalText() ([]byte, error) { 226 return []byte(k.String()), nil 227 } 228 229 func (k *PrivateKey) UnmarshalJSON(b []byte) error { 230 str := string(b) 231 if str == nullStr { // If "null", do nothing 232 return nil 233 } else if len(str) < 2 { 234 return errMissingQuotes 235 } 236 237 lastIndex := len(str) - 1 238 if str[0] != '"' || str[lastIndex] != '"' { 239 return errMissingQuotes 240 } 241 242 strNoQuotes := str[1:lastIndex] 243 if !strings.HasPrefix(strNoQuotes, PrivateKeyPrefix) { 244 return errMissingKeyPrefix 245 } 246 247 strNoPrefix := strNoQuotes[len(PrivateKeyPrefix):] 248 keyBytes, err := cb58.Decode(strNoPrefix) 249 if err != nil { 250 return err 251 } 252 if len(keyBytes) != PrivateKeyLen { 253 return errInvalidPrivateKeyLength 254 } 255 256 *k = PrivateKey{ 257 sk: secp256k1.PrivKeyFromBytes(keyBytes), 258 bytes: keyBytes, 259 } 260 return nil 261 } 262 263 func (k *PrivateKey) UnmarshalText(text []byte) error { 264 return k.UnmarshalJSON(text) 265 } 266 267 // raw sig has format [v || r || s] whereas the sig has format [r || s || v] 268 func rawSigToSig(sig []byte) ([]byte, error) { 269 if len(sig) != SignatureLen { 270 return nil, errInvalidSigLen 271 } 272 recCode := sig[0] 273 copy(sig, sig[1:]) 274 sig[SignatureLen-1] = recCode - compactSigMagicOffset 275 return sig, nil 276 } 277 278 // sig has format [r || s || v] whereas the raw sig has format [v || r || s] 279 func sigToRawSig(sig []byte) ([]byte, error) { 280 if len(sig) != SignatureLen { 281 return nil, errInvalidSigLen 282 } 283 newSig := make([]byte, SignatureLen) 284 newSig[0] = sig[SignatureLen-1] + compactSigMagicOffset 285 copy(newSig[1:], sig) 286 return newSig, nil 287 } 288 289 // verifies the signature format in format [r || s || v] 290 func verifySECP256K1RSignatureFormat(sig []byte) error { 291 if len(sig) != SignatureLen { 292 return errInvalidSigLen 293 } 294 295 var s secp256k1.ModNScalar 296 s.SetByteSlice(sig[32:64]) 297 if s.IsOverHalfOrder() { 298 return errMutatedSig 299 } 300 return nil 301 }