github.com/emmansun/gmsm@v0.29.1/ecdh/ecdh.go (about) 1 // Package ecdh implements Elliptic Curve Diffie-Hellman / SM2-MQV over 2 // SM2 curve. 3 package ecdh 4 5 import ( 6 "crypto" 7 "crypto/subtle" 8 "hash" 9 "io" 10 "sync" 11 12 "github.com/emmansun/gmsm/sm3" 13 ) 14 15 type Curve interface { 16 // GenerateKey generates a new PrivateKey from rand. 17 GenerateKey(rand io.Reader) (*PrivateKey, error) 18 19 // NewPrivateKey checks that key is valid and returns a PrivateKey. 20 // 21 // For NIST curves, this follows SEC 1, Version 2.0, Section 2.3.6, which 22 // amounts to decoding the bytes as a fixed length big endian integer and 23 // checking that the result is lower than the order of the curve. The zero 24 // private key is also rejected, as the encoding of the corresponding public 25 // key would be irregular. 26 // 27 // For X25519, this only checks the scalar length. Adversarially selected 28 // private keys can cause ECDH to return an error. 29 NewPrivateKey(key []byte) (*PrivateKey, error) 30 31 // NewPublicKey checks that key is valid and returns a PublicKey. 32 // 33 // For NIST curves, this decodes an uncompressed point according to SEC 1, 34 // Version 2.0, Section 2.3.4. Compressed encodings and the point at 35 // infinity are rejected. 36 // 37 // For X25519, this only checks the u-coordinate length. Adversarially 38 // selected public keys can cause ECDH to return an error. 39 NewPublicKey(key []byte) (*PublicKey, error) 40 41 // ecdh performs a ECDH exchange and returns the shared secret. It's exposed 42 // as the PrivateKey.ECDH method. 43 // 44 // The private method also allow us to expand the ECDH interface with more 45 // methods in the future without breaking backwards compatibility. 46 ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error) 47 48 // sm2mqv performs a SM2 specific style ECMQV exchange and return the shared secret. 49 sm2mqv(sLocal, eLocal *PrivateKey, sRemote, eRemote *PublicKey) (*PublicKey, error) 50 51 // sm2za ZA = H256(ENTLA || IDA || a || b || xG || yG || xA || yA). 52 // Compliance with GB/T 32918.2-2016 5.5 53 sm2za(md hash.Hash, pub *PublicKey, uid []byte) ([]byte, error) 54 55 // privateKeyToPublicKey converts a PrivateKey to a PublicKey. It's exposed 56 // as the PrivateKey.PublicKey method. 57 // 58 // This method always succeeds: for X25519, it might output the all-zeroes 59 // value (unlike the ECDH method); for NIST curves, it would only fail for 60 // the zero private key, which is rejected by NewPrivateKey. 61 // 62 // The private method also allow us to expand the ECDH interface with more 63 // methods in the future without breaking backwards compatibility. 64 privateKeyToPublicKey(*PrivateKey) *PublicKey 65 } 66 67 // PublicKey is an ECDH public key, usually a peer's ECDH share sent over the wire. 68 // 69 // These keys can be parsed with [smx509.ParsePKIXPublicKey] and encoded 70 // with [smx509.MarshalPKIXPublicKey]. For SM2 curve, it then needs to 71 // be converted with [sm2.PublicKeyToECDH] after parsing. 72 type PublicKey struct { 73 curve Curve 74 publicKey []byte 75 } 76 77 // Bytes returns a copy of the encoding of the public key. 78 func (k *PublicKey) Bytes() []byte { 79 // Copy the public key to a fixed size buffer that can get allocated on the 80 // caller's stack after inlining. 81 var buf [133]byte 82 return append(buf[:0], k.publicKey...) 83 } 84 85 // Equal returns whether x represents the same public key as k. 86 // 87 // Note that there can be equivalent public keys with different encodings which 88 // would return false from this check but behave the same way as inputs to ECDH. 89 // 90 // This check is performed in constant time as long as the key types and their 91 // curve match. 92 func (k *PublicKey) Equal(x crypto.PublicKey) bool { 93 xx, ok := x.(*PublicKey) 94 if !ok { 95 return false 96 } 97 return k.curve == xx.curve && 98 subtle.ConstantTimeCompare(k.publicKey, xx.publicKey) == 1 99 } 100 101 func (k *PublicKey) Curve() Curve { 102 return k.curve 103 } 104 105 // SM2ZA ZA = H256(ENTLA || IDA || a || b || xG || yG || xA || yA). 106 // Compliance with GB/T 32918.2-2016 5.5 107 func (k *PublicKey) SM2ZA(md hash.Hash, uid []byte) ([]byte, error) { 108 return k.curve.sm2za(md, k, uid) 109 } 110 111 // SM2SharedKey performs SM2 key derivation to generate shared keying data, the uv was generated by SM2MQV. 112 func (uv *PublicKey) SM2SharedKey(isResponder bool, kenLen int, sPub, sRemote *PublicKey, uid []byte, remoteUID []byte) ([]byte, error) { 113 var buffer [128]byte 114 copy(buffer[:], uv.publicKey[1:]) 115 peerZ, err := sRemote.SM2ZA(sm3.New(), remoteUID) 116 if err != nil { 117 return nil, err 118 } 119 z, err := sPub.SM2ZA(sm3.New(), uid) 120 if err != nil { 121 return nil, err 122 } 123 if isResponder { 124 copy(buffer[64:], peerZ) 125 copy(buffer[96:], z) 126 } else { 127 copy(buffer[64:], z) 128 copy(buffer[96:], peerZ) 129 } 130 131 return sm3.Kdf(buffer[:], kenLen), nil 132 } 133 134 // PrivateKey is an ECDH private key, usually kept secret. 135 // 136 // These keys can be parsed with [smx509.ParsePKCS8PrivateKey] and encoded 137 // with [smx509.MarshalPKCS8PrivateKey]. For SM2 curve, it then needs to 138 // be converted with [sm2.PrivateKey.ECDH] after parsing. 139 type PrivateKey struct { 140 curve Curve 141 privateKey []byte 142 // publicKey is set under publicKeyOnce, to allow loading private keys with 143 // NewPrivateKey without having to perform a scalar multiplication. 144 publicKey *PublicKey 145 publicKeyOnce sync.Once 146 } 147 148 // ECDH performs a ECDH exchange and returns the shared secret. 149 // 150 // For NIST curves, this performs ECDH as specified in SEC 1, Version 2.0, 151 // Section 3.3.1, and returns the x-coordinate encoded according to SEC 1, 152 // Version 2.0, Section 2.3.5. The result is never the point at infinity. 153 // 154 // For X25519, this performs ECDH as specified in RFC 7748, Section 6.1. If 155 // the result is the all-zero value, ECDH returns an error. 156 func (k *PrivateKey) ECDH(remote *PublicKey) ([]byte, error) { 157 return k.curve.ecdh(k, remote) 158 } 159 160 // SM2MQV performs a SM2 specific style ECMQV exchange and return the shared secret. 161 func (k *PrivateKey) SM2MQV(eLocal *PrivateKey, sRemote, eRemote *PublicKey) (*PublicKey, error) { 162 return k.curve.sm2mqv(k, eLocal, sRemote, eRemote) 163 } 164 165 // Bytes returns a copy of the encoding of the private key. 166 func (k *PrivateKey) Bytes() []byte { 167 // Copy the private key to a fixed size buffer that can get allocated on the 168 // caller's stack after inlining. 169 var buf [66]byte 170 return append(buf[:0], k.privateKey...) 171 } 172 173 // Equal returns whether x represents the same private key as k. 174 // 175 // Note that there can be equivalent private keys with different encodings which 176 // would return false from this check but behave the same way as inputs to ECDH. 177 // 178 // This check is performed in constant time as long as the key types and their 179 // curve match. 180 func (k *PrivateKey) Equal(x crypto.PrivateKey) bool { 181 xx, ok := x.(*PrivateKey) 182 if !ok { 183 return false 184 } 185 return k.curve == xx.curve && 186 subtle.ConstantTimeCompare(k.privateKey, xx.privateKey) == 1 187 } 188 189 func (k *PrivateKey) Curve() Curve { 190 return k.curve 191 } 192 193 func (k *PrivateKey) PublicKey() *PublicKey { 194 k.publicKeyOnce.Do(func() { 195 k.publicKey = k.curve.privateKeyToPublicKey(k) 196 }) 197 return k.publicKey 198 } 199 200 // Public implements the implicit interface of all standard library private 201 // keys. See the docs of crypto.PrivateKey. 202 func (k *PrivateKey) Public() crypto.PublicKey { 203 return k.PublicKey() 204 }