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  }