github.com/dashpay/godash@v0.0.0-20160726055534-e038a21e0e3d/btcec/ciphering.go (about)

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