github.com/status-im/status-go@v1.1.0/api/multiformat/utils.go (about) 1 package multiformat 2 3 import ( 4 "crypto/elliptic" 5 "errors" 6 "fmt" 7 "math/big" 8 9 "github.com/ethereum/go-ethereum/crypto/secp256k1" 10 11 bls12381 "github.com/kilic/bls12-381" 12 13 "github.com/multiformats/go-multibase" 14 "github.com/multiformats/go-varint" 15 ) 16 17 const ( 18 secp256k1KeyType = 0xe7 19 bls12p381g1KeyType = 0xea 20 bls12p381g2KeyType = 0xeb 21 legacyKeyLength = 132 22 ) 23 24 // SerializePublicKey serialises a non-serialised multibase encoded multicodec identified EC public key 25 // For details on usage see specs https://specs.status.im/spec/2#public-key-serialization 26 func SerializePublicKey(key, outputBase string) (string, error) { 27 dKey, err := multibaseDecode(key) 28 if err != nil { 29 return "", err 30 } 31 32 kt, i, err := getPublicKeyType(dKey) 33 if err != nil { 34 return "", err 35 } 36 37 cpk, err := compressPublicKey(dKey[i:], kt) 38 if err != nil { 39 return "", err 40 } 41 42 cpk = prependKeyIdentifier(cpk, kt, i) 43 44 return multibaseEncode(outputBase, cpk) 45 } 46 47 // DeserializePublicKey deserialise a serialised multibase encoded multicodec identified EC public key 48 // For details on usage see specs https://specs.status.im/spec/2#public-key-serialization 49 func DeserializePublicKey(key, outputBase string) (string, error) { 50 cpk, err := multibaseDecode(key) 51 if err != nil { 52 return "", err 53 } 54 55 kt, i, err := getPublicKeyType(cpk) 56 if err != nil { 57 return "", err 58 } 59 60 pk, err := decompressPublicKey(cpk[i:], kt) 61 if err != nil { 62 return "", err 63 } 64 65 pk = prependKeyIdentifier(pk, kt, i) 66 67 return multibaseEncode(outputBase, pk) 68 } 69 70 // SerializeLegacyKey converts a secp251k1 uncompressed key to 71 // a base58 compressed key 72 func SerializeLegacyKey(key string) (string, error) { 73 if len(key) != legacyKeyLength { 74 return "", errors.New("invalid key length") 75 } 76 77 keyWithPrefix := fmt.Sprintf("0x%x01%s", secp256k1KeyType, key[2:]) 78 return SerializePublicKey(keyWithPrefix, "z") 79 } 80 81 // DeserializeCompressedKey converts a base58 compressed key to 82 // a secp251k1 uncompressed key 83 func DeserializeCompressedKey(key string) (string, error) { 84 if len(key) == 0 { 85 return "", errors.New("invalid key length") 86 } 87 deserialisedKey, err := DeserializePublicKey(key, "f") 88 if err != nil { 89 return "", err 90 } 91 92 return "0x" + deserialisedKey[5:], nil 93 } 94 95 // getPublicKeyType wrapper for the `varint.FromUvarint()` func 96 func getPublicKeyType(key []byte) (uint64, int, error) { 97 return varint.FromUvarint(key) 98 } 99 100 // prependKeyIdentifier prepends an Unsigned Variable Integer (uvarint) to a given []byte 101 func prependKeyIdentifier(key []byte, kt uint64, ktl int) []byte { 102 buf := make([]byte, ktl) 103 varint.PutUvarint(buf, kt) 104 105 key = append(buf, key...) 106 return key 107 } 108 109 // compressPublicKey serves as logic switch function to parse key data for compression based on the given keyType 110 func compressPublicKey(key []byte, keyType uint64) ([]byte, error) { 111 switch keyType { 112 case secp256k1KeyType: 113 return compressSecp256k1PublicKey(key) 114 115 case bls12p381g1KeyType: 116 return compressBls12p381g1PublicKey(key) 117 118 case bls12p381g2KeyType: 119 return compressBls12p381g2PublicKey(key) 120 121 default: 122 return nil, fmt.Errorf("unsupported public key type '%X'", keyType) 123 } 124 } 125 126 // compressSecp256k1PublicKey is a dedicated key compression function for secp256k1 pks 127 func compressSecp256k1PublicKey(key []byte) ([]byte, error) { 128 x, y := elliptic.Unmarshal(secp256k1.S256(), key) 129 130 if err := isSecp256k1XYValid(key, x, y); err != nil { 131 return nil, err 132 } 133 134 cpk := secp256k1.CompressPubkey(x, y) 135 136 return cpk, nil 137 } 138 139 // compressBls12p381g1PublicKey is a dedicated key compression function for bls12 381 g1 pks 140 func compressBls12p381g1PublicKey(key []byte) ([]byte, error) { 141 g1 := bls12381.NewG1() 142 143 // Generate the G1 point 144 pg1, err := g1.FromBytes(key) 145 if err != nil { 146 return nil, err 147 } 148 149 cpk := g1.ToCompressed(pg1) 150 return cpk, nil 151 } 152 153 // compressBls12p381g1PublicKey is a dedicated key compression function for bls12 381 g2 pks 154 func compressBls12p381g2PublicKey(key []byte) ([]byte, error) { 155 g2 := bls12381.NewG2() 156 157 // Generate the G2 point 158 pg2, err := g2.FromBytes(key) 159 if err != nil { 160 return nil, err 161 } 162 163 cpk := g2.ToCompressed(pg2) 164 return cpk, nil 165 } 166 167 // decompressPublicKey serves as logic switch function to parse key data for decompression based on the given keyType 168 func decompressPublicKey(key []byte, keyType uint64) ([]byte, error) { 169 switch keyType { 170 case secp256k1KeyType: 171 return decompressSecp256k1PublicKey(key) 172 173 case bls12p381g1KeyType: 174 return decompressBls12p381g1PublicKey(key) 175 176 case bls12p381g2KeyType: 177 return decompressBls12p381g2PublicKey(key) 178 179 default: 180 return nil, fmt.Errorf("unsupported public key type '%X'", keyType) 181 } 182 } 183 184 // decompressSecp256k1PublicKey is a dedicated key decompression function for secp256k1 pks 185 func decompressSecp256k1PublicKey(key []byte) ([]byte, error) { 186 x, y := secp256k1.DecompressPubkey(key) 187 188 if err := isSecp256k1XYValid(key, x, y); err != nil { 189 return nil, err 190 } 191 192 k := elliptic.Marshal(secp256k1.S256(), x, y) 193 194 return k, nil 195 } 196 197 // isSecp256k1XYValid checks if a given x and y coordinate is nil, returns an error if either x or y is nil 198 // secp256k1.DecompressPubkey will not return an error if a compressed pk fails decompression and instead returns 199 // nil x, y coordinates 200 func isSecp256k1XYValid(key []byte, x, y *big.Int) error { 201 if x == nil || y == nil { 202 return fmt.Errorf("invalid public key format, '%b'", key) 203 } 204 205 return nil 206 } 207 208 // decompressBls12p381g1PublicKey is a dedicated key decompression function for bls12 381 g1 pks 209 func decompressBls12p381g1PublicKey(key []byte) ([]byte, error) { 210 g1 := bls12381.NewG1() 211 pg1, err := g1.FromCompressed(key) 212 if err != nil { 213 return nil, err 214 } 215 216 pk := g1.ToUncompressed(pg1) 217 return pk, nil 218 } 219 220 // decompressBls12p381g2PublicKey is a dedicated key decompression function for bls12 381 g2 pks 221 func decompressBls12p381g2PublicKey(key []byte) ([]byte, error) { 222 g2 := bls12381.NewG2() 223 pg2, err := g2.FromCompressed(key) 224 if err != nil { 225 return nil, err 226 } 227 228 pk := g2.ToUncompressed(pg2) 229 return pk, nil 230 } 231 232 // multibaseEncode wraps `multibase.Encode()` extending the base functionality to support `0x` prefixed strings 233 func multibaseEncode(base string, data []byte) (string, error) { 234 if base == "0x" { 235 base = "f" 236 } 237 return multibase.Encode(multibase.Encoding(base[0]), data) 238 } 239 240 // multibaseDecode wraps `multibase.Decode()` extending the base functionality to support `0x` prefixed strings 241 func multibaseDecode(data string) ([]byte, error) { 242 if data[0:2] == "0x" { 243 data = "f" + data[2:] 244 } 245 246 _, dd, err := multibase.Decode(data) 247 return dd, err 248 }