123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316 |
- 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)
- }
-
- func GenerateKey(curve elliptic.Curve, random io.Reader) (priv *PrivateKey, err error) {
- var privBytes []byte
- var Vx, Vy *big.Int
-
- if _, ok := curve25519.ToCurve25519(curve); ok {
- privBytes = make([]byte, 32)
- _, err = io.ReadFull(random, privBytes)
- if err != nil {
- return nil, err
- }
-
- // NOTE: PGP expect scalars in reverse order than Curve 25519
- // go library. That's why this trimming is backwards compared
- // to curve25519.go
- privBytes[31] &= 248
- privBytes[0] &= 127
- privBytes[0] |= 64
-
- Vx,Vy = curve.ScalarBaseMult(privBytes)
- } else {
- privBytes, Vx, Vy, err = elliptic.GenerateKey(curve, random)
- if err != nil {
- return nil, err
- }
- }
-
- priv = &PrivateKey{}
- priv.X = new(big.Int).SetBytes(privBytes)
- priv.PublicKey.Curve = curve
- priv.PublicKey.X = Vx
- priv.PublicKey.Y = Vy
- return priv, nil
- }
|