github.com/datachainlab/burrow@v0.25.0/crypto/public_key.go (about) 1 package crypto 2 3 import ( 4 "crypto/sha256" 5 "encoding/json" 6 "fmt" 7 8 "github.com/btcsuite/btcd/btcec" 9 "github.com/tendermint/tendermint/crypto/tmhash" 10 hex "github.com/tmthrgd/go-hex" 11 "golang.org/x/crypto/ed25519" 12 "golang.org/x/crypto/ripemd160" 13 ) 14 15 const ( 16 MaxPublicKeyLength = btcec.PubKeyBytesLenCompressed 17 PublicKeyFixedWidthEncodingLength = MaxPublicKeyLength + 1 18 ) 19 20 type PublicKeyJSON struct { 21 CurveType string 22 PublicKey string 23 } 24 25 // Returns the length in bytes of the public key 26 func PublicKeyLength(curveType CurveType) int { 27 switch curveType { 28 case CurveTypeEd25519: 29 return ed25519.PublicKeySize 30 case CurveTypeSecp256k1: 31 return btcec.PubKeyBytesLenCompressed 32 default: 33 // Other functions rely on this 34 return 0 35 } 36 } 37 38 func (p PublicKey) IsSet() bool { 39 return p.CurveType != CurveTypeUnset && p.IsValid() 40 } 41 42 func (p PublicKey) MarshalJSON() ([]byte, error) { 43 jStruct := PublicKeyJSON{ 44 CurveType: p.CurveType.String(), 45 PublicKey: hex.EncodeUpperToString(p.PublicKey), 46 } 47 txt, err := json.Marshal(jStruct) 48 return txt, err 49 } 50 51 func (p PublicKey) MarshalText() ([]byte, error) { 52 return p.MarshalJSON() 53 } 54 55 func (p *PublicKey) UnmarshalJSON(text []byte) error { 56 var jStruct PublicKeyJSON 57 err := json.Unmarshal(text, &jStruct) 58 if err != nil { 59 return err 60 } 61 CurveType, err := CurveTypeFromString(jStruct.CurveType) 62 if err != nil { 63 return err 64 } 65 bs, err := hex.DecodeString(jStruct.PublicKey) 66 if err != nil { 67 return err 68 } 69 p.CurveType = CurveType 70 p.PublicKey = bs 71 return nil 72 } 73 74 func (p *PublicKey) UnmarshalText(text []byte) error { 75 return p.UnmarshalJSON(text) 76 } 77 78 func (p PublicKey) IsValid() bool { 79 publicKeyLength := PublicKeyLength(p.CurveType) 80 return publicKeyLength != 0 && publicKeyLength == len(p.PublicKey) 81 } 82 83 func (p PublicKey) Verify(msg []byte, signature *Signature) error { 84 switch p.CurveType { 85 case CurveTypeUnset: 86 return fmt.Errorf("public key is unset") 87 case CurveTypeEd25519: 88 if ed25519.Verify(p.PublicKey.Bytes(), msg, signature.Signature) { 89 return nil 90 } 91 return fmt.Errorf("'%X' is not a valid ed25519 signature for message: %X", signature, msg) 92 case CurveTypeSecp256k1: 93 pub, err := btcec.ParsePubKey(p.PublicKey, btcec.S256()) 94 if err != nil { 95 return fmt.Errorf("could not parse secp256k1 public key: %v", err) 96 } 97 sig, err := btcec.ParseDERSignature(signature.Signature, btcec.S256()) 98 if err != nil { 99 return fmt.Errorf("could not parse DER signature for secp256k1 key: %v", err) 100 } 101 if sig.Verify(msg, pub) { 102 return nil 103 } 104 return fmt.Errorf("'%X' is not a valid secp256k1 signature for message: %X", signature, msg) 105 default: 106 return fmt.Errorf("invalid curve type") 107 } 108 } 109 110 func (p PublicKey) GetAddress() Address { 111 switch p.CurveType { 112 case CurveTypeEd25519: 113 addr, _ := AddressFromBytes(tmhash.Sum(p.PublicKey)) 114 return addr 115 case CurveTypeSecp256k1: 116 sha := sha256.New() 117 sha.Write(p.PublicKey[:]) 118 119 hash := ripemd160.New() 120 hash.Write(sha.Sum(nil)) 121 addr, _ := AddressFromBytes(hash.Sum(nil)) 122 return addr 123 default: 124 panic(fmt.Sprintf("unknown CurveType %d", p.CurveType)) 125 } 126 } 127 128 func (p PublicKey) AddressHashType() string { 129 switch p.CurveType { 130 case CurveTypeEd25519: 131 return "go-crypto-0.5.0" 132 case CurveTypeSecp256k1: 133 return "btc" 134 default: 135 return "" 136 } 137 } 138 139 func (p PublicKey) String() string { 140 return hex.EncodeUpperToString(p.PublicKey) 141 } 142 143 // Produces a binary encoding of the CurveType byte plus 144 // the public key for padded to a fixed width on the right 145 func (p PublicKey) EncodeFixedWidth() []byte { 146 encoded := make([]byte, PublicKeyFixedWidthEncodingLength) 147 encoded[0] = p.CurveType.Byte() 148 copy(encoded[1:], p.PublicKey) 149 return encoded 150 } 151 152 func DecodePublicKeyFixedWidth(bs []byte) (PublicKey, error) { 153 const errHeader = "DecodePublicKeyFixedWidth():" 154 if len(bs) != PublicKeyFixedWidthEncodingLength { 155 return PublicKey{}, fmt.Errorf("%s expected exactly %d bytes but got %d bytes", 156 errHeader, PublicKeyFixedWidthEncodingLength, len(bs)) 157 } 158 curveType := CurveType(bs[0]) 159 return PublicKeyFromBytes(bs[1:PublicKeyLength(curveType)+1], curveType) 160 }