diff options
Diffstat (limited to 'vendor/github.com/keybase/go-crypto/openpgp/ecdh/ecdh.go')
-rw-r--r-- | vendor/github.com/keybase/go-crypto/openpgp/ecdh/ecdh.go | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/vendor/github.com/keybase/go-crypto/openpgp/ecdh/ecdh.go b/vendor/github.com/keybase/go-crypto/openpgp/ecdh/ecdh.go new file mode 100644 index 0000000000..64c18d0b34 --- /dev/null +++ b/vendor/github.com/keybase/go-crypto/openpgp/ecdh/ecdh.go @@ -0,0 +1,282 @@ +package ecdh + +import ( + "bytes" + "crypto" + "crypto/aes" + "crypto/elliptic" + "encoding/binary" + "errors" + "github.com/keybase/go-crypto/curve25519" + "io" + "math/big" +) + +type PublicKey struct { + elliptic.Curve + X, Y *big.Int +} + +type PrivateKey struct { + PublicKey + X *big.Int +} + +// KDF implements Key Derivation Function as described in +// https://tools.ietf.org/html/rfc6637#section-7 +func (e *PublicKey) KDF(S []byte, kdfParams []byte, hash crypto.Hash) []byte { + sLen := (e.Curve.Params().P.BitLen() + 7) / 8 + buf := new(bytes.Buffer) + buf.Write([]byte{0, 0, 0, 1}) + if sLen > len(S) { + // zero-pad the S. If we got invalid S (bigger than curve's + // P), we are going to produce invalid key. Garbage in, + // garbage out. + buf.Write(make([]byte, sLen-len(S))) + } + buf.Write(S) + buf.Write(kdfParams) + + hashw := hash.New() + + hashw.Write(buf.Bytes()) + key := hashw.Sum(nil) + + return key +} + +// AESKeyUnwrap implements RFC 3394 Key Unwrapping. See +// http://tools.ietf.org/html/rfc3394#section-2.2.1 +// Note: The second described algorithm ("index-based") is implemented +// here. +func AESKeyUnwrap(key, cipherText []byte) ([]byte, error) { + if len(cipherText)%8 != 0 { + return nil, errors.New("cipherText must by a multiple of 64 bits") + } + + cipher, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + nblocks := len(cipherText)/8 - 1 + + // 1) Initialize variables. + // - Set A = C[0] + var A [aes.BlockSize]byte + copy(A[:8], cipherText[:8]) + + // For i = 1 to n + // Set R[i] = C[i] + R := make([]byte, len(cipherText)-8) + copy(R, cipherText[8:]) + + // 2) Compute intermediate values. + for j := 5; j >= 0; j-- { + for i := nblocks - 1; i >= 0; i-- { + // B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i + // A = MSB(64, B) + t := uint64(nblocks*j + i + 1) + At := binary.BigEndian.Uint64(A[:8]) ^ t + binary.BigEndian.PutUint64(A[:8], At) + + copy(A[8:], R[i*8:i*8+8]) + cipher.Decrypt(A[:], A[:]) + + // R[i] = LSB(B, 64) + copy(R[i*8:i*8+8], A[8:]) + } + } + + // 3) Output results. + // If A is an appropriate initial value (see 2.2.3), + for i := 0; i < 8; i++ { + if A[i] != 0xA6 { + return nil, errors.New("Failed to unwrap key (A is not IV)") + } + } + + return R, nil +} + +// AESKeyWrap implements RFC 3394 Key Wrapping. See +// https://tools.ietf.org/html/rfc3394#section-2.2.2 +// Note: The second described algorithm ("index-based") is implemented +// here. +func AESKeyWrap(key, plainText []byte) ([]byte, error) { + if len(plainText)%8 != 0 { + return nil, errors.New("plainText must be a multiple of 64 bits") + } + + cipher, err := aes.NewCipher(key) // NewCipher checks key size + if err != nil { + return nil, err + } + + nblocks := len(plainText) / 8 + + // 1) Initialize variables. + var A [aes.BlockSize]byte + // Section 2.2.3.1 -- Initial Value + // http://tools.ietf.org/html/rfc3394#section-2.2.3.1 + for i := 0; i < 8; i++ { + A[i] = 0xA6 + } + + // For i = 1 to n + // Set R[i] = P[i] + R := make([]byte, len(plainText)) + copy(R, plainText) + + // 2) Calculate intermediate values. + for j := 0; j <= 5; j++ { + for i := 0; i < nblocks; i++ { + // B = AES(K, A | R[i]) + copy(A[8:], R[i*8:i*8+8]) + cipher.Encrypt(A[:], A[:]) + + // (Assume B = A) + // A = MSB(64, B) ^ t where t = (n*j)+1 + t := uint64(j*nblocks + i + 1) + At := binary.BigEndian.Uint64(A[:8]) ^ t + binary.BigEndian.PutUint64(A[:8], At) + + // R[i] = LSB(64, B) + copy(R[i*8:i*8+8], A[8:]) + } + } + + // 3) Output results. + // Set C[0] = A + // For i = 1 to n + // C[i] = R[i] + return append(A[:8], R...), nil +} + +// PadBuffer pads byte buffer buf to a length being multiple of +// blockLen. Additional bytes appended to the buffer have value of the +// number padded bytes. E.g. if the buffer is 3 bytes short of being +// 40 bytes total, the appended bytes will be [03, 03, 03]. +func PadBuffer(buf []byte, blockLen int) []byte { + padding := blockLen - (len(buf) % blockLen) + if padding == 0 { + return buf + } + + padBuf := make([]byte, padding) + for i := 0; i < padding; i++ { + padBuf[i] = byte(padding) + } + + return append(buf, padBuf...) +} + +// UnpadBuffer verifies that buffer contains proper padding and +// returns buffer without the padding, or nil if the padding was +// invalid. +func UnpadBuffer(buf []byte, dataLen int) []byte { + padding := len(buf) - dataLen + outBuf := buf[:dataLen] + + for i := dataLen; i < len(buf); i++ { + if buf[i] != byte(padding) { + // Invalid padding - bail out + return nil + } + } + + return outBuf +} + +func (e *PublicKey) Encrypt(random io.Reader, kdfParams []byte, plain []byte, hash crypto.Hash, kdfKeySize int) (Vx *big.Int, Vy *big.Int, C []byte, err error) { + // Vx, Vy - encryption key + + // Note for Curve 25519 - curve25519 library already does key + // clamping in scalarMult, so we can use generic random scalar + // generation from elliptic. + priv, Vx, Vy, err := elliptic.GenerateKey(e.Curve, random) + if err != nil { + return nil, nil, nil, err + } + + // Sx, Sy - shared secret + Sx, _ := e.Curve.ScalarMult(e.X, e.Y, priv) + + // Encrypt the payload with KDF-ed S as the encryption key. Pass + // the ciphertext along with V to the recipient. Recipient can + // generate S using V and their priv key, and then KDF(S), on + // their own, to get encryption key and decrypt the ciphertext, + // revealing encryption key for symmetric encryption later. + + plain = PadBuffer(plain, 8) + key := e.KDF(Sx.Bytes(), kdfParams, hash) + + // Take only as many bytes from key as the key length (the hash + // result might be bigger) + encrypted, err := AESKeyWrap(key[:kdfKeySize], plain) + + return Vx, Vy, encrypted, nil +} + +func (e *PrivateKey) DecryptShared(X, Y *big.Int) []byte { + Sx, _ := e.Curve.ScalarMult(X, Y, e.X.Bytes()) + return Sx.Bytes() +} + +func countBits(buffer []byte) int { + var headerLen int + switch buffer[0] { + case 0x4: + headerLen = 3 + case 0x40: + headerLen = 7 + default: + // Unexpected header - but we can still count the bits. + val := buffer[0] + headerLen = 0 + for val > 0 { + val = val / 2 + headerLen++ + } + } + + return headerLen + (len(buffer)-1)*8 +} + +// elliptic.Marshal and elliptic.Unmarshal only marshals uncompressed +// 0x4 MPI types. These functions will check if the curve is cv25519, +// and if so, use 0x40 compressed type to (un)marshal. Otherwise, +// elliptic.(Un)marshal will be called. + +// Marshal encodes point into either 0x4 uncompressed point form, or +// 0x40 compressed point for Curve 25519. +func Marshal(curve elliptic.Curve, x, y *big.Int) (buf []byte, bitSize int) { + // NOTE: Read more about MPI encoding in the RFC: + // https://tools.ietf.org/html/rfc4880#section-3.2 + + // We are required to encode size in bits, counting from the most- + // significant non-zero bit. So assuming that the buffer never + // starts with 0x00, we only need to count bits in the first byte + // - and in current implentation it will always be 0x4 or 0x40. + + cv, ok := curve25519.ToCurve25519(curve) + if ok { + buf = cv.MarshalType40(x, y) + } else { + buf = elliptic.Marshal(curve, x, y) + } + + return buf, countBits(buf) +} + +// Unmarshal converts point, serialized by Marshal, into x, y pair. +// For 0x40 compressed points (for Curve 25519), y will always be 0. +// It is an error if point is not on the curve, On error, x = nil. +func Unmarshal(curve elliptic.Curve, data []byte) (x, y *big.Int) { + cv, ok := curve25519.ToCurve25519(curve) + if ok { + return cv.UnmarshalType40(data) + } + + return elliptic.Unmarshal(curve, data) +} |