ciphering.go raw

   1  // Copyright (c) 2015-2016 The btcsuite developers
   2  // Use of this source code is governed by an ISC
   3  // license that can be found in the LICENSE file.
   4  
   5  package ecc
   6  
   7  import (
   8  	"bytes"
   9  	"crypto/aes"
  10  	"crypto/cipher"
  11  	"crypto/hmac"
  12  	"crypto/rand"
  13  	"crypto/sha256"
  14  	"crypto/sha512"
  15  	"errors"
  16  	"io"
  17  )
  18  
  19  var (
  20  	// ErrInvalidMAC occurs when Message Authentication Check (MAC) fails
  21  	// during decryption. This happens because of either invalid private key or
  22  	// corrupt ciphertext.
  23  	ErrInvalidMAC = errors.New("invalid mac hash")
  24  
  25  	// errInputTooShort occurs when the input ciphertext to the Decrypt
  26  	// function is less than 134 bytes long.
  27  	errInputTooShort = errors.New("ciphertext too short")
  28  
  29  	// errUnsupportedCurve occurs when the first two bytes of the encrypted
  30  	// text aren't 0x02CA (= 712 = secp256k1, from OpenSSL).
  31  	errUnsupportedCurve = errors.New("unsupported curve")
  32  
  33  	errInvalidXLength = errors.New("invalid X length, must be 32")
  34  	errInvalidYLength = errors.New("invalid Y length, must be 32")
  35  	errInvalidPadding = errors.New("invalid PKCS#7 padding")
  36  
  37  	// 0x02CA = 714
  38  	ciphCurveBytes = [2]byte{0x02, 0xCA}
  39  	// 0x20 = 32
  40  	ciphCoordLength = [2]byte{0x00, 0x20}
  41  )
  42  
  43  // GenerateSharedSecret generates a shared secret based on a private key and a
  44  // public key using Diffie-Hellman key exchange (ECDH) (RFC 4753).
  45  // RFC5903 Section 9 states we should only return x.
  46  func GenerateSharedSecret(privkey *PrivateKey, pubkey *PublicKey) []byte {
  47  	x, _ := pubkey.Curve.ScalarMult(pubkey.X, pubkey.Y, privkey.D.Bytes())
  48  	return x.Bytes()
  49  }
  50  
  51  // Encrypt encrypts data for the target public key using AES-256-CBC. It also
  52  // generates a private key (the pubkey of which is also in the output). The only
  53  // supported curve is secp256k1. The `structure' that it encodes everything into
  54  // is:
  55  //
  56  //	struct {
  57  //		// Initialization Vector used for AES-256-CBC
  58  //		IV [16]byte
  59  //		// Public Key: curve(2) + len_of_pubkeyX(2) + pubkeyX +
  60  //		// len_of_pubkeyY(2) + pubkeyY (curve = 714)
  61  //		PublicKey [70]byte
  62  //		// Cipher text
  63  //		Data []byte
  64  //		// HMAC-SHA-256 Message Authentication Code
  65  //		HMAC [32]byte
  66  //	}
  67  //
  68  // The primary aim is to ensure byte compatibility with Pyelliptic.  Also, refer
  69  // to section 5.8.1 of ANSI X9.63 for rationale on this format.
  70  func Encrypt(pubkey *PublicKey, in []byte) ([]byte, error) {
  71  	ephemeral, err := NewPrivateKey(S256())
  72  	if err != nil {
  73  		return nil, err
  74  	}
  75  	ecdhKey := GenerateSharedSecret(ephemeral, pubkey)
  76  	derivedKey := sha512.Sum512(ecdhKey)
  77  	keyE := derivedKey[:32]
  78  	keyM := derivedKey[32:]
  79  
  80  	paddedIn := addPKCSPadding(in)
  81  	// IV + Curve params/X/Y + padded plaintext/ciphertext + HMAC-256
  82  	out := make([]byte, aes.BlockSize+70+len(paddedIn)+sha256.Size)
  83  	iv := out[:aes.BlockSize]
  84  	if _, err = io.ReadFull(rand.Reader, iv); err != nil {
  85  		return nil, err
  86  	}
  87  	// start writing public key
  88  	pb := ephemeral.PubKey().SerializeUncompressed()
  89  	offset := aes.BlockSize
  90  
  91  	// curve and X length
  92  	copy(out[offset:offset+4], append(ciphCurveBytes[:], ciphCoordLength[:]...))
  93  	offset += 4
  94  	// X
  95  	copy(out[offset:offset+32], pb[1:33])
  96  	offset += 32
  97  	// Y length
  98  	copy(out[offset:offset+2], ciphCoordLength[:])
  99  	offset += 2
 100  	// Y
 101  	copy(out[offset:offset+32], pb[33:])
 102  	offset += 32
 103  
 104  	// start encryption
 105  	block, err := aes.NewCipher(keyE)
 106  	if err != nil {
 107  		return nil, err
 108  	}
 109  	mode := cipher.NewCBCEncrypter(block, iv)
 110  	mode.CryptBlocks(out[offset:len(out)-sha256.Size], paddedIn)
 111  
 112  	// start HMAC-SHA-256
 113  	hm := hmac.New(sha256.New, keyM)
 114  	hm.Write(out[:len(out)-sha256.Size])          // everything is hashed
 115  	copy(out[len(out)-sha256.Size:], hm.Sum(nil)) // write checksum
 116  
 117  	return out, nil
 118  }
 119  
 120  // Decrypt decrypts data that was encrypted using the Encrypt function.
 121  func Decrypt(priv *PrivateKey, in []byte) ([]byte, error) {
 122  	// IV + Curve params/X/Y + 1 block + HMAC-256
 123  	if len(in) < aes.BlockSize+70+aes.BlockSize+sha256.Size {
 124  		return nil, errInputTooShort
 125  	}
 126  
 127  	// read iv
 128  	iv := in[:aes.BlockSize]
 129  	offset := aes.BlockSize
 130  
 131  	// start reading pubkey
 132  	if !bytes.Equal(in[offset:offset+2], ciphCurveBytes[:]) {
 133  		return nil, errUnsupportedCurve
 134  	}
 135  	offset += 2
 136  
 137  	if !bytes.Equal(in[offset:offset+2], ciphCoordLength[:]) {
 138  		return nil, errInvalidXLength
 139  	}
 140  	offset += 2
 141  
 142  	xBytes := in[offset : offset+32]
 143  	offset += 32
 144  
 145  	if !bytes.Equal(in[offset:offset+2], ciphCoordLength[:]) {
 146  		return nil, errInvalidYLength
 147  	}
 148  	offset += 2
 149  
 150  	yBytes := in[offset : offset+32]
 151  	offset += 32
 152  
 153  	pb := make([]byte, 65)
 154  	pb[0] = byte(0x04) // uncompressed
 155  	copy(pb[1:33], xBytes)
 156  	copy(pb[33:], yBytes)
 157  	// check if (X, Y) lies on the curve and create a Pubkey if it does
 158  	pubkey, err := ParsePubKey(pb, S256())
 159  	if err != nil {
 160  		return nil, err
 161  	}
 162  
 163  	// check for cipher text length
 164  	if (len(in)-aes.BlockSize-offset-sha256.Size)%aes.BlockSize != 0 {
 165  		return nil, errInvalidPadding // not padded to 16 bytes
 166  	}
 167  
 168  	// read hmac
 169  	messageMAC := in[len(in)-sha256.Size:]
 170  
 171  	// generate shared secret
 172  	ecdhKey := GenerateSharedSecret(priv, pubkey)
 173  	derivedKey := sha512.Sum512(ecdhKey)
 174  	keyE := derivedKey[:32]
 175  	keyM := derivedKey[32:]
 176  
 177  	// verify mac
 178  	hm := hmac.New(sha256.New, keyM)
 179  	hm.Write(in[:len(in)-sha256.Size]) // everything is hashed
 180  	expectedMAC := hm.Sum(nil)
 181  	if !hmac.Equal(messageMAC, expectedMAC) {
 182  		return nil, ErrInvalidMAC
 183  	}
 184  
 185  	// start decryption
 186  	block, err := aes.NewCipher(keyE)
 187  	if err != nil {
 188  		return nil, err
 189  	}
 190  	mode := cipher.NewCBCDecrypter(block, iv)
 191  	// same length as ciphertext
 192  	plaintext := make([]byte, len(in)-offset-sha256.Size)
 193  	mode.CryptBlocks(plaintext, in[offset:len(in)-sha256.Size])
 194  
 195  	return removePKCSPadding(plaintext)
 196  }
 197  
 198  // Implement PKCS#7 padding with block size of 16 (AES block size).
 199  
 200  // addPKCSPadding adds padding to a block of data
 201  func addPKCSPadding(src []byte) []byte {
 202  	padding := aes.BlockSize - len(src)%aes.BlockSize
 203  	padtext := bytes.Repeat([]byte{byte(padding)}, padding)
 204  	return append(src, padtext...)
 205  }
 206  
 207  // removePKCSPadding removes padding from data that was added with addPKCSPadding
 208  func removePKCSPadding(src []byte) ([]byte, error) {
 209  	length := len(src)
 210  	padLength := int(src[length-1])
 211  	if padLength > aes.BlockSize || length < aes.BlockSize {
 212  		return nil, errInvalidPadding
 213  	}
 214  
 215  	return src[:length-padLength], nil
 216  }
 217