github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/crypto/keys/private_key.go (about) 1 package keys 2 3 import ( 4 "crypto/ecdsa" 5 "crypto/elliptic" 6 "crypto/rand" 7 "crypto/sha256" 8 "crypto/x509" 9 "encoding/hex" 10 "fmt" 11 "math/big" 12 13 "github.com/decred/dcrd/dcrec/secp256k1/v4" 14 "github.com/nspcc-dev/neo-go/pkg/crypto/hash" 15 "github.com/nspcc-dev/neo-go/pkg/util" 16 "github.com/nspcc-dev/neo-go/pkg/util/slice" 17 "github.com/nspcc-dev/rfc6979" 18 ) 19 20 // PrivateKey represents a Neo private key and provides a high level API around 21 // ecdsa.PrivateKey. 22 type PrivateKey struct { 23 ecdsa.PrivateKey 24 } 25 26 // NewPrivateKey creates a new random Secp256r1 private key. 27 func NewPrivateKey() (*PrivateKey, error) { 28 return newPrivateKeyOnCurve(elliptic.P256()) 29 } 30 31 // NewSecp256k1PrivateKey creates a new random Secp256k1 private key. 32 func NewSecp256k1PrivateKey() (*PrivateKey, error) { 33 return newPrivateKeyOnCurve(secp256k1.S256()) 34 } 35 36 // newPrivateKeyOnCurve creates a new random private key using curve c. 37 func newPrivateKeyOnCurve(c elliptic.Curve) (*PrivateKey, error) { 38 pk, err := ecdsa.GenerateKey(c, rand.Reader) 39 if err != nil { 40 return nil, err 41 } 42 return &PrivateKey{*pk}, nil 43 } 44 45 // NewPrivateKeyFromHex returns a Secp256k1 PrivateKey created from the 46 // given hex string. 47 func NewPrivateKeyFromHex(str string) (*PrivateKey, error) { 48 b, err := hex.DecodeString(str) 49 if err != nil { 50 return nil, err 51 } 52 defer slice.Clean(b) 53 return NewPrivateKeyFromBytes(b) 54 } 55 56 // NewPrivateKeyFromBytes returns a NEO Secp256r1 PrivateKey from the given 57 // byte slice. 58 func NewPrivateKeyFromBytes(b []byte) (*PrivateKey, error) { 59 if len(b) != 32 { 60 return nil, fmt.Errorf( 61 "invalid byte length: expected %d bytes got %d", 32, len(b), 62 ) 63 } 64 var ( 65 c = elliptic.P256() 66 d = new(big.Int).SetBytes(b) 67 ) 68 69 x, y := c.ScalarBaseMult(b) 70 71 return &PrivateKey{ 72 ecdsa.PrivateKey{ 73 PublicKey: ecdsa.PublicKey{ 74 Curve: c, 75 X: x, 76 Y: y, 77 }, 78 D: d, 79 }, 80 }, nil 81 } 82 83 // NewPrivateKeyFromASN1 returns a NEO Secp256k1 PrivateKey from the ASN.1 84 // serialized key. 85 func NewPrivateKeyFromASN1(b []byte) (*PrivateKey, error) { 86 privkey, err := x509.ParseECPrivateKey(b) 87 if err != nil { 88 return nil, err 89 } 90 return &PrivateKey{*privkey}, nil 91 } 92 93 // PublicKey derives the public key from the private key. 94 func (p *PrivateKey) PublicKey() *PublicKey { 95 result := PublicKey(p.PrivateKey.PublicKey) 96 return &result 97 } 98 99 // NewPrivateKeyFromWIF returns a NEO PrivateKey from the given 100 // WIF (wallet import format). 101 func NewPrivateKeyFromWIF(wif string) (*PrivateKey, error) { 102 w, err := WIFDecode(wif, WIFVersion) 103 if err != nil { 104 return nil, err 105 } 106 return w.PrivateKey, nil 107 } 108 109 // WIF returns the (wallet import format) of the PrivateKey. 110 // Good documentation about this process can be found here: 111 // https://en.bitcoin.it/wiki/Wallet_import_format 112 func (p *PrivateKey) WIF() string { 113 pb := p.Bytes() 114 defer slice.Clean(pb) 115 w, err := WIFEncode(pb, WIFVersion, true) 116 // The only way WIFEncode() can fail is if we're to give it a key of 117 // wrong size, but we have a proper key here, aren't we? 118 if err != nil { 119 panic(err) 120 } 121 return w 122 } 123 124 // Destroy wipes the contents of the private key from memory. Any operations 125 // with the key after call to Destroy have undefined behavior. 126 func (p *PrivateKey) Destroy() { 127 bits := p.D.Bits() 128 for i := range bits { 129 bits[i] = 0 130 } 131 } 132 133 // Address derives the public NEO address that is coupled with the private key, and 134 // returns it as a string. 135 func (p *PrivateKey) Address() string { 136 pk := p.PublicKey() 137 return pk.Address() 138 } 139 140 // GetScriptHash returns verification script hash for the public key associated with 141 // the private key. 142 func (p *PrivateKey) GetScriptHash() util.Uint160 { 143 pk := p.PublicKey() 144 return pk.GetScriptHash() 145 } 146 147 // Sign signs arbitrary length data using the private key. It uses SHA256 to 148 // calculate hash and then SignHash to create a signature (so you can save on 149 // hash calculation if you already have it). 150 func (p *PrivateKey) Sign(data []byte) []byte { 151 var digest = sha256.Sum256(data) 152 153 return p.SignHash(digest) 154 } 155 156 // SignHash signs a particular hash with the private key. 157 func (p *PrivateKey) SignHash(digest util.Uint256) []byte { 158 r, s := rfc6979.SignECDSA(&p.PrivateKey, digest[:], sha256.New) 159 return getSignatureSlice(p.PrivateKey.Curve, r, s) 160 } 161 162 // SignHashable signs some Hashable item for the network specified using 163 // hash.NetSha256() with the private key. 164 func (p *PrivateKey) SignHashable(net uint32, hh hash.Hashable) []byte { 165 return p.SignHash(hash.NetSha256(net, hh)) 166 } 167 168 func getSignatureSlice(curve elliptic.Curve, r, s *big.Int) []byte { 169 params := curve.Params() 170 curveOrderByteSize := params.P.BitLen() / 8 171 signature := make([]byte, curveOrderByteSize*2) 172 _ = r.FillBytes(signature[:curveOrderByteSize]) 173 _ = s.FillBytes(signature[curveOrderByteSize:]) 174 175 return signature 176 } 177 178 // String implements the stringer interface. 179 func (p *PrivateKey) String() string { 180 return hex.EncodeToString(p.Bytes()) 181 } 182 183 // Bytes returns the underlying bytes of the PrivateKey. 184 func (p *PrivateKey) Bytes() []byte { 185 result := make([]byte, 32) 186 _ = p.D.FillBytes(result) 187 188 return result 189 }