github.com/mit-dci/lit@v0.0.0-20221102210550-8c3d3b49f2ce/crypto/koblitz/pubkey.go (about) 1 // Copyright (c) 2013-2014 The btcsuite developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package koblitz 6 7 import ( 8 "crypto/ecdsa" 9 "errors" 10 "fmt" 11 "math/big" 12 ) 13 14 // These constants define the lengths of serialized public keys. 15 const ( 16 PubKeyBytesLenCompressed = 33 17 PubKeyBytesLenUncompressed = 65 18 PubKeyBytesLenHybrid = 65 19 ) 20 21 func isOdd(a *big.Int) bool { 22 return a.Bit(0) == 1 23 } 24 25 // decompressPoint decompresses a point on the given curve given the X point and 26 // the solution to use. 27 func decompressPoint(curve *KoblitzCurve, x *big.Int, ybit bool) (*big.Int, error) { 28 // TODO(oga) This will probably only work for secp256k1 due to 29 // optimizations. 30 31 // Y = +-sqrt(x^3 + B) 32 x3 := new(big.Int).Mul(x, x) 33 x3.Mul(x3, x) 34 x3.Add(x3, curve.Params().B) 35 36 // now calculate sqrt mod p of x2 + B 37 // This code used to do a full sqrt based on tonelli/shanks, 38 // but this was replaced by the algorithms referenced in 39 // https://bitcointalk.org/index.php?topic=162805.msg1712294#msg1712294 40 y := new(big.Int).Exp(x3, curve.QPlus1Div4(), curve.Params().P) 41 42 if ybit != isOdd(y) { 43 y.Sub(curve.Params().P, y) 44 } 45 if ybit != isOdd(y) { 46 return nil, fmt.Errorf("ybit doesn't match oddness") 47 } 48 return y, nil 49 } 50 51 const ( 52 pubkeyCompressed byte = 0x2 // y_bit + x coord 53 pubkeyUncompressed byte = 0x4 // x coord + y coord 54 pubkeyHybrid byte = 0x6 // y_bit + x coord + y coord 55 ) 56 57 // ParsePubKey parses a public key for a koblitz curve from a bytestring into a 58 // ecdsa.Publickey, verifying that it is valid. It supports compressed, 59 // uncompressed and hybrid signature formats. 60 func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *PublicKey, err error) { 61 pubkey := PublicKey{} 62 pubkey.Curve = curve 63 64 if len(pubKeyStr) == 0 { 65 return nil, errors.New("pubkey string is empty") 66 } 67 68 format := pubKeyStr[0] 69 ybit := (format & 0x1) == 0x1 70 format &= ^byte(0x1) 71 72 switch len(pubKeyStr) { 73 case PubKeyBytesLenUncompressed: 74 if format != pubkeyUncompressed && format != pubkeyHybrid { 75 return nil, fmt.Errorf("invalid magic in pubkey str: "+ 76 "%d", pubKeyStr[0]) 77 } 78 79 pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33]) 80 pubkey.Y = new(big.Int).SetBytes(pubKeyStr[33:]) 81 // hybrid keys have extra information, make use of it. 82 if format == pubkeyHybrid && ybit != isOdd(pubkey.Y) { 83 return nil, fmt.Errorf("ybit doesn't match oddness") 84 } 85 case PubKeyBytesLenCompressed: 86 // format is 0x2 | solution, <X coordinate> 87 // solution determines which solution of the curve we use. 88 /// y^2 = x^3 + Curve.B 89 if format != pubkeyCompressed { 90 return nil, fmt.Errorf("invalid magic in compressed "+ 91 "pubkey string: %d", pubKeyStr[0]) 92 } 93 pubkey.X = new(big.Int).SetBytes(pubKeyStr[1:33]) 94 pubkey.Y, err = decompressPoint(curve, pubkey.X, ybit) 95 if err != nil { 96 return nil, err 97 } 98 default: // wrong! 99 return nil, fmt.Errorf("invalid pub key length %d", 100 len(pubKeyStr)) 101 } 102 103 if pubkey.X.Cmp(pubkey.Curve.Params().P) >= 0 { 104 return nil, fmt.Errorf("pubkey X parameter is >= to P") 105 } 106 if pubkey.Y.Cmp(pubkey.Curve.Params().P) >= 0 { 107 return nil, fmt.Errorf("pubkey Y parameter is >= to P") 108 } 109 if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) { 110 return nil, fmt.Errorf("pubkey isn't on secp256k1 curve") 111 } 112 return &pubkey, nil 113 } 114 115 // PublicKey is an ecdsa.PublicKey with additional functions to 116 // serialize in uncompressed, compressed, and hybrid formats. 117 type PublicKey ecdsa.PublicKey 118 119 // ToECDSA returns the public key as a *ecdsa.PublicKey. 120 func (p *PublicKey) ToECDSA() *ecdsa.PublicKey { 121 return (*ecdsa.PublicKey)(p) 122 } 123 124 // SerializeUncompressed serializes a public key in a 65-byte uncompressed 125 // format. 126 func (p *PublicKey) SerializeUncompressed() []byte { 127 b := make([]byte, 0, PubKeyBytesLenUncompressed) 128 b = append(b, pubkeyUncompressed) 129 b = paddedAppend(32, b, p.X.Bytes()) 130 return paddedAppend(32, b, p.Y.Bytes()) 131 } 132 133 // SerializeCompressed serializes a public key in a 33-byte compressed format. 134 func (p *PublicKey) SerializeCompressed() []byte { 135 b := make([]byte, 0, PubKeyBytesLenCompressed) 136 format := pubkeyCompressed 137 if isOdd(p.Y) { 138 format |= 0x1 139 } 140 b = append(b, format) 141 return paddedAppend(32, b, p.X.Bytes()) 142 } 143 144 // SerializeHybrid serializes a public key in a 65-byte hybrid format. 145 func (p *PublicKey) SerializeHybrid() []byte { 146 b := make([]byte, 0, PubKeyBytesLenHybrid) 147 format := pubkeyHybrid 148 if isOdd(p.Y) { 149 format |= 0x1 150 } 151 b = append(b, format) 152 b = paddedAppend(32, b, p.X.Bytes()) 153 return paddedAppend(32, b, p.Y.Bytes()) 154 } 155 156 // IsEqual compares this PublicKey instance to the one passed, returning true if 157 // both PublicKeys are equivalent. A PublicKey is equivalent to another, if they 158 // both have the same X and Y coordinate. 159 func (p *PublicKey) IsEqual(otherPubKey *PublicKey) bool { 160 return p.X.Cmp(otherPubKey.X) == 0 && 161 p.Y.Cmp(otherPubKey.Y) == 0 162 } 163 164 // paddedAppend appends the src byte slice to dst, returning the new slice. 165 // If the length of the source is smaller than the passed size, leading zero 166 // bytes are appended to the dst slice before appending src. 167 func paddedAppend(size uint, dst, src []byte) []byte { 168 for i := 0; i < int(size)-len(src); i++ { 169 dst = append(dst, 0) 170 } 171 return append(dst, src...) 172 }