1// Copyright 2017 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// Package ecdh implements ECDH encryption, suitable for OpenPGP, 6// as specified in RFC 6637, section 8. 7package ecdh 8 9import ( 10 "errors" 11 "io" 12 "math/big" 13 14 "golang.org/x/crypto/curve25519" 15 "golang.org/x/crypto/openpgp/aes/keywrap" 16 "golang.org/x/crypto/openpgp/internal/ecc" 17) 18 19// Generates a private-public key-pair. 20// 'priv' is a private key; a scalar belonging to the set 21// 2^{254} + 8 * [0, 2^{251}), in order to avoid the small subgroup of the 22// curve. 'pub' is simply 'priv' * G where G is the base point. 23// See https://cr.yp.to/ecdh.html and RFC7748, sec 5. 24func x25519GenerateKeyPairBytes(rand io.Reader) (priv [32]byte, pub [32]byte, err error) { 25 var n, helper = new(big.Int), new(big.Int) 26 n.SetUint64(1) 27 n.Lsh(n, 252) 28 helper.SetString("27742317777372353535851937790883648493", 10) 29 n.Add(n, helper) 30 31 for true { 32 _, err = io.ReadFull(rand, priv[:]) 33 if err != nil { 34 return 35 } 36 // The following ensures that the private key is a number of the form 37 // 2^{254} + 8 * [0, 2^{251}), in order to avoid the small subgroup of 38 // of the curve. 39 priv[0] &= 248 40 priv[31] &= 127 41 priv[31] |= 64 42 43 // If the scalar is out of range, sample another random number. 44 if new(big.Int).SetBytes(priv[:]).Cmp(n) >= 0 { 45 continue 46 } 47 48 curve25519.ScalarBaseMult(&pub, &priv) 49 return 50 } 51 return 52} 53 54// X25519GenerateKey samples the key pair according to the correct distribution. 55// It also sets the given key-derivation function and returns the *PrivateKey 56// object along with an error. 57func X25519GenerateKey(rand io.Reader, kdf KDF) (priv *PrivateKey, err error) { 58 ci := ecc.FindByName("Curve25519") 59 priv = new(PrivateKey) 60 priv.PublicKey.Curve = ci.Curve 61 d, pubKey, err := x25519GenerateKeyPairBytes(rand) 62 if err != nil { 63 return nil, err 64 } 65 priv.PublicKey.KDF = kdf 66 priv.D = make([]byte, 32) 67 copyReversed(priv.D, d[:]) 68 priv.PublicKey.CurveType = ci.CurveType 69 priv.PublicKey.Curve = ci.Curve 70 /* 71 * Note that ECPoint.point differs from the definition of public keys in 72 * [Curve25519] in two ways: (1) the byte-ordering is big-endian, which is 73 * more uniform with how big integers are represented in TLS, and (2) there 74 * is an additional length byte (so ECpoint.point is actually 33 bytes), 75 * again for uniformity (and extensibility). 76 */ 77 var encodedKey = make([]byte, 33) 78 encodedKey[0] = 0x40 79 copy(encodedKey[1:], pubKey[:]) 80 priv.PublicKey.X = new(big.Int).SetBytes(encodedKey[:]) 81 priv.PublicKey.Y = new(big.Int) 82 return priv, nil 83} 84 85func X25519Encrypt(random io.Reader, pub *PublicKey, msg, curveOID, fingerprint []byte) (vsG, c []byte, err error) { 86 d, ephemeralKey, err := x25519GenerateKeyPairBytes(random) 87 if err != nil { 88 return nil, nil, err 89 } 90 var pubKey [32]byte 91 92 if pub.X.BitLen() > 33*264 { 93 return nil, nil, errors.New("ecdh: invalid key") 94 } 95 copy(pubKey[:], pub.X.Bytes()[1:]) 96 97 var zb [32]byte 98 curve25519.ScalarBaseMult(&zb, &d) 99 curve25519.ScalarMult(&zb, &d, &pubKey) 100 z, err := buildKey(pub, zb[:], curveOID, fingerprint, false, false) 101 102 if err != nil { 103 return nil, nil, err 104 } 105 106 if c, err = keywrap.Wrap(z, msg); err != nil { 107 return nil, nil, err 108 } 109 110 var vsg [33]byte 111 vsg[0] = 0x40 112 copy(vsg[1:], ephemeralKey[:]) 113 114 return vsg[:], c, nil 115} 116 117func X25519Decrypt(priv *PrivateKey, vsG, m, curveOID, fingerprint []byte) (msg []byte, err error) { 118 var zb, d, ephemeralKey [32]byte 119 if len(vsG) != 33 || vsG[0] != 0x40 { 120 return nil, errors.New("ecdh: invalid key") 121 } 122 copy(ephemeralKey[:], vsG[1:33]) 123 124 copyReversed(d[:], priv.D) 125 curve25519.ScalarBaseMult(&zb, &d) 126 curve25519.ScalarMult(&zb, &d, &ephemeralKey) 127 128 var c []byte 129 130 for i := 0; i < 3; i++ { 131 // Try buildKey three times for compat, see comments in buildKey. 132 z, err := buildKey(&priv.PublicKey, zb[:], curveOID, fingerprint, i == 1, i == 2) 133 if err != nil { 134 return nil, err 135 } 136 137 res, err := keywrap.Unwrap(z, m) 138 if i == 2 && err != nil { 139 // Only return an error after we've tried all variants of buildKey. 140 return nil, err 141 } 142 143 c = res 144 if err == nil { 145 break 146 } 147 } 148 149 return c[:len(c)-int(c[len(c)-1])], nil 150} 151 152func copyReversed(out []byte, in []byte) { 153 l := len(in) 154 for i := 0; i < l; i++ { 155 out[i] = in[l-i-1] 156 } 157} 158