github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/network/p2p/keyutils/keyTranslator.go (about) 1 package keyutils 2 3 import ( 4 goecdsa "crypto/ecdsa" 5 "crypto/elliptic" 6 "crypto/x509" 7 "fmt" 8 "math/big" 9 10 lcrypto "github.com/libp2p/go-libp2p/core/crypto" 11 lcrypto_pb "github.com/libp2p/go-libp2p/core/crypto/pb" 12 "github.com/libp2p/go-libp2p/core/peer" 13 "github.com/onflow/crypto" 14 ) 15 16 // This module is meant to help libp2p <-> flow public key conversions 17 // Flow's Network Public Keys and LibP2P's public keys are a marshalling standard away from each other and inter-convertible. 18 // Libp2p supports keys as ECDSA public Keys on either the NIST P-256 curve or the "Bitcoin" secp256k1 curve, see https://github.com/libp2p/specs/blob/master/peer-ids/peer-ids.md#peer-ids 19 // libp2p represents the P-256 keys as ASN1-DER and the secp256k1 keys as X9.62 encodings in compressed format 20 // 21 // While Flow's key types supports both secp256k1 and P-256 keys (under crypto/ecdsa), note that Flow's networking keys are always P-256 keys. 22 // Flow represents both the P-256 keys and the secp256k1 keys in uncompressed representation, but their byte serializations (Encode) do not include the X9.62 compression bit 23 // Flow also makes a X9.62 compressed format (with compression bit) accessible (EncodeCompressed) 24 25 // assigns a big.Int input to a Go ecdsa private key 26 func setPrKey(c elliptic.Curve, k *big.Int) *goecdsa.PrivateKey { 27 priv := new(goecdsa.PrivateKey) 28 priv.PublicKey.Curve = c 29 priv.D = k 30 priv.PublicKey.X, priv.PublicKey.Y = c.ScalarBaseMult(k.Bytes()) 31 return priv 32 } 33 34 // assigns two big.Int inputs to a Go ecdsa public key 35 func setPubKey(c elliptic.Curve, x *big.Int, y *big.Int) *goecdsa.PublicKey { 36 pub := new(goecdsa.PublicKey) 37 pub.Curve = c 38 pub.X = x 39 pub.Y = y 40 return pub 41 } 42 43 // Both Flow and LibP2P define a crypto package with their own abstraction of Keys 44 // These utility functions convert a Flow crypto key to a LibP2P key (Flow --> LibP2P) 45 46 // PeerIDFromFlowPublicKey converts a Flow public key to a LibP2P peer ID. 47 func PeerIDFromFlowPublicKey(networkPubKey crypto.PublicKey) (pid peer.ID, err error) { 48 pk, err := LibP2PPublicKeyFromFlow(networkPubKey) 49 if err != nil { 50 err = fmt.Errorf("failed to convert Flow key to LibP2P key: %w", err) 51 return 52 } 53 54 pid, err = peer.IDFromPublicKey(pk) 55 if err != nil { 56 err = fmt.Errorf("failed to convert LibP2P key to peer ID: %w", err) 57 return 58 } 59 60 return 61 } 62 63 // LibP2PPrivKeyFromFlow converts a Flow private key to a LibP2P Private key 64 func LibP2PPrivKeyFromFlow(fpk crypto.PrivateKey) (lcrypto.PrivKey, error) { 65 // get the signature algorithm 66 keyType, err := keyType(fpk.Algorithm()) 67 if err != nil { 68 return nil, err 69 } 70 71 // based on the signature algorithm, get the appropriate libp2p unmarshaller 72 um, ok := lcrypto.PrivKeyUnmarshallers[keyType] 73 if !ok { 74 return nil, lcrypto.ErrBadKeyType 75 } 76 77 // get the raw dump of the flow key 78 bytes := fpk.Encode() 79 80 // in the case of NIST curves, the raw bytes need to be converted to x509 bytes 81 // to accommodate libp2p unmarshaller 82 if keyType == lcrypto_pb.KeyType_ECDSA { 83 var k big.Int 84 k.SetBytes(bytes) 85 goKey := setPrKey(elliptic.P256(), &k) 86 bytes, err = x509.MarshalECPrivateKey(goKey) 87 if err != nil { 88 return nil, lcrypto.ErrBadKeyType 89 } 90 } 91 return um(bytes) 92 } 93 94 // LibP2PPublicKeyFromFlow converts a Flow public key to a LibP2P public key 95 func LibP2PPublicKeyFromFlow(fpk crypto.PublicKey) (lcrypto.PubKey, error) { 96 keyType, err := keyType(fpk.Algorithm()) 97 if err != nil { 98 return nil, err 99 } 100 um, ok := lcrypto.PubKeyUnmarshallers[keyType] 101 if !ok { 102 return nil, lcrypto.ErrBadKeyType 103 } 104 105 tempBytes := fpk.Encode() 106 107 // at this point, keytype is either KeyType_ECDSA or KeyType_Secp256k1 108 // and can't hold another value 109 var bytes []byte 110 if keyType == lcrypto_pb.KeyType_ECDSA { 111 var x, y big.Int 112 x.SetBytes(tempBytes[:len(tempBytes)/2]) 113 y.SetBytes(tempBytes[len(tempBytes)/2:]) 114 goKey := setPubKey(elliptic.P256(), &x, &y) 115 bytes, err = x509.MarshalPKIXPublicKey(goKey) 116 if err != nil { 117 return nil, lcrypto.ErrBadKeyType 118 } 119 } else if keyType == lcrypto_pb.KeyType_Secp256k1 { 120 bytes = make([]byte, crypto.PubKeyLenECDSASecp256k1+1) // libp2p requires an extra byte 121 bytes[0] = 4 // signals uncompressed form as specified in section 4.3.6/7 of ANSI X9.62. 122 copy(bytes[1:], tempBytes) 123 } 124 125 return um(bytes) 126 } 127 128 // This converts some libp2p PubKeys to a flow PublicKey 129 // - the supported key types are ECDSA P-256 and ECDSA Secp256k1 public keys, 130 // - libp2p also supports RSA and Ed25519 keys, which Flow doesn't, their conversion will return an error. 131 func FlowPublicKeyFromLibP2P(lpk lcrypto.PubKey) (crypto.PublicKey, error) { 132 133 switch ktype := lpk.Type(); ktype { 134 case lcrypto_pb.KeyType_ECDSA: 135 pubB, err := lpk.Raw() 136 if err != nil { 137 return nil, lcrypto.ErrBadKeyType 138 } 139 key, err := x509.ParsePKIXPublicKey(pubB) 140 if err != nil { 141 // impossible to decode from ASN1.DER 142 return nil, lcrypto.ErrBadKeyType 143 } 144 cryptoKey, ok := key.(*goecdsa.PublicKey) 145 if !ok { 146 // not recognized as crypto.P-256 147 return nil, lcrypto.ErrNotECDSAPubKey 148 } 149 // ferrying through DecodePublicKey to get the curve checks 150 pk_uncompressed := elliptic.Marshal(cryptoKey, cryptoKey.X, cryptoKey.Y) 151 // the first bit is the compression bit of X9.62 152 pubKey, err := crypto.DecodePublicKey(crypto.ECDSAP256, pk_uncompressed[1:]) 153 if err != nil { 154 return nil, lcrypto.ErrNotECDSAPubKey 155 } 156 return pubKey, nil 157 case lcrypto_pb.KeyType_Secp256k1: 158 // libp2p uses the compressed representation, flow the uncompressed one 159 lpk_secp256k1, ok := lpk.(*lcrypto.Secp256k1PublicKey) 160 if !ok { 161 return nil, lcrypto.ErrBadKeyType 162 } 163 secpBytes, err := lpk_secp256k1.Raw() 164 if err != nil { // this never errors 165 return nil, lcrypto.ErrBadKeyType 166 } 167 pk, err := crypto.DecodePublicKeyCompressed(crypto.ECDSASecp256k1, secpBytes) 168 if err != nil { 169 return nil, lcrypto.ErrNotECDSAPubKey 170 } 171 return pk, nil 172 default: 173 return nil, lcrypto.ErrBadKeyType 174 } 175 176 } 177 178 // keyType translates Flow signing algorithm constants to the corresponding LibP2P constants 179 func keyType(sa crypto.SigningAlgorithm) (lcrypto_pb.KeyType, error) { 180 switch sa { 181 case crypto.ECDSAP256: 182 return lcrypto_pb.KeyType_ECDSA, nil 183 case crypto.ECDSASecp256k1: 184 return lcrypto_pb.KeyType_Secp256k1, nil 185 default: 186 return -1, lcrypto.ErrBadKeyType 187 } 188 }