From bb75ea5020eea51a10d0264a4c14858d9d5b28b9 Mon Sep 17 00:00:00 2001 From: Dan Aharoni Date: Mon, 11 Nov 2019 11:01:02 +0200 Subject: [PATCH] [NOD-414] Remove AES encryption/decryption from btcd (#460) --- btcec/ciphering.go | 216 ---------------------------------------- btcec/ciphering_test.go | 174 -------------------------------- btcec/example_test.go | 80 --------------- 3 files changed, 470 deletions(-) delete mode 100644 btcec/ciphering.go delete mode 100644 btcec/ciphering_test.go diff --git a/btcec/ciphering.go b/btcec/ciphering.go deleted file mode 100644 index 7859e1829..000000000 --- a/btcec/ciphering.go +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright (c) 2015-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcec - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/hmac" - "crypto/rand" - "crypto/sha256" - "crypto/sha512" - "github.com/pkg/errors" - "io" -) - -var ( - // ErrInvalidMAC occurs when Message Authentication Check (MAC) fails - // during decryption. This happens because of either invalid private key or - // corrupt ciphertext. - ErrInvalidMAC = errors.New("invalid mac hash") - - // errInputTooShort occurs when the input ciphertext to the Decrypt - // function is less than 134 bytes long. - errInputTooShort = errors.New("ciphertext too short") - - // errUnsupportedCurve occurs when the first two bytes of the encrypted - // text aren't 0x02CA (= 712 = secp256k1, from OpenSSL). - errUnsupportedCurve = errors.New("unsupported curve") - - errInvalidXLength = errors.New("invalid X length, must be 32") - errInvalidYLength = errors.New("invalid Y length, must be 32") - errInvalidPadding = errors.New("invalid PKCS#7 padding") - - // 0x02CA = 714 - ciphCurveBytes = [2]byte{0x02, 0xCA} - // 0x20 = 32 - ciphCoordLength = [2]byte{0x00, 0x20} -) - -// GenerateSharedSecret generates a shared secret based on a private key and a -// public key using Diffie-Hellman key exchange (ECDH) (RFC 4753). -// RFC5903 Section 9 states we should only return x. -func GenerateSharedSecret(privkey *PrivateKey, pubkey *PublicKey) []byte { - x, _ := pubkey.Curve.ScalarMult(pubkey.X, pubkey.Y, privkey.D.Bytes()) - return x.Bytes() -} - -// Encrypt encrypts data for the target public key using AES-256-CBC. It also -// generates a private key (the pubkey of which is also in the output). The only -// supported curve is secp256k1. The `structure' that it encodes everything into -// is: -// -// struct { -// // Initialization Vector used for AES-256-CBC -// IV [16]byte -// // Public Key: curve(2) + len_of_pubkeyX(2) + pubkeyX + -// // len_of_pubkeyY(2) + pubkeyY (curve = 714) -// PublicKey [70]byte -// // Cipher text -// Data []byte -// // HMAC-SHA-256 Message Authentication Code -// HMAC [32]byte -// } -// -// The primary aim is to ensure byte compatibility with Pyelliptic. Also, refer -// to section 5.8.1 of ANSI X9.63 for rationale on this format. -func Encrypt(pubkey *PublicKey, in []byte) ([]byte, error) { - ephemeral, err := NewPrivateKey(S256()) - if err != nil { - return nil, err - } - ecdhKey := GenerateSharedSecret(ephemeral, pubkey) - derivedKey := sha512.Sum512(ecdhKey) - keyE := derivedKey[:32] - keyM := derivedKey[32:] - - paddedIn := addPKCSPadding(in) - // IV + Curve params/X/Y + padded plaintext/ciphertext + HMAC-256 - out := make([]byte, aes.BlockSize+70+len(paddedIn)+sha256.Size) - iv := out[:aes.BlockSize] - if _, err = io.ReadFull(rand.Reader, iv); err != nil { - return nil, err - } - // start writing public key - pb := ephemeral.PubKey().SerializeUncompressed() - offset := aes.BlockSize - - // curve and X length - copy(out[offset:offset+4], append(ciphCurveBytes[:], ciphCoordLength[:]...)) - offset += 4 - // X - copy(out[offset:offset+32], pb[1:33]) - offset += 32 - // Y length - copy(out[offset:offset+2], ciphCoordLength[:]) - offset += 2 - // Y - copy(out[offset:offset+32], pb[33:]) - offset += 32 - - // start encryption - block, err := aes.NewCipher(keyE) - if err != nil { - return nil, err - } - mode := cipher.NewCBCEncrypter(block, iv) - mode.CryptBlocks(out[offset:len(out)-sha256.Size], paddedIn) - - // start HMAC-SHA-256 - hm := hmac.New(sha256.New, keyM) - hm.Write(out[:len(out)-sha256.Size]) // everything is hashed - copy(out[len(out)-sha256.Size:], hm.Sum(nil)) // write checksum - - return out, nil -} - -// Decrypt decrypts data that was encrypted using the Encrypt function. -func Decrypt(priv *PrivateKey, in []byte) ([]byte, error) { - // IV + Curve params/X/Y + 1 block + HMAC-256 - if len(in) < aes.BlockSize+70+aes.BlockSize+sha256.Size { - return nil, errInputTooShort - } - - // read iv - iv := in[:aes.BlockSize] - offset := aes.BlockSize - - // start reading pubkey - if !bytes.Equal(in[offset:offset+2], ciphCurveBytes[:]) { - return nil, errUnsupportedCurve - } - offset += 2 - - if !bytes.Equal(in[offset:offset+2], ciphCoordLength[:]) { - return nil, errInvalidXLength - } - offset += 2 - - xBytes := in[offset : offset+32] - offset += 32 - - if !bytes.Equal(in[offset:offset+2], ciphCoordLength[:]) { - return nil, errInvalidYLength - } - offset += 2 - - yBytes := in[offset : offset+32] - offset += 32 - - pb := make([]byte, 65) - pb[0] = byte(0x04) // uncompressed - copy(pb[1:33], xBytes) - copy(pb[33:], yBytes) - // check if (X, Y) lies on the curve and create a Pubkey if it does - pubkey, err := ParsePubKey(pb, S256()) - if err != nil { - return nil, err - } - - // check for cipher text length - if (len(in)-aes.BlockSize-offset-sha256.Size)%aes.BlockSize != 0 { - return nil, errInvalidPadding // not padded to 16 bytes - } - - // read hmac - messageMAC := in[len(in)-sha256.Size:] - - // generate shared secret - ecdhKey := GenerateSharedSecret(priv, pubkey) - derivedKey := sha512.Sum512(ecdhKey) - keyE := derivedKey[:32] - keyM := derivedKey[32:] - - // verify mac - hm := hmac.New(sha256.New, keyM) - hm.Write(in[:len(in)-sha256.Size]) // everything is hashed - expectedMAC := hm.Sum(nil) - if !hmac.Equal(messageMAC, expectedMAC) { - return nil, ErrInvalidMAC - } - - // start decryption - block, err := aes.NewCipher(keyE) - if err != nil { - return nil, err - } - mode := cipher.NewCBCDecrypter(block, iv) - // same length as ciphertext - plaintext := make([]byte, len(in)-offset-sha256.Size) - mode.CryptBlocks(plaintext, in[offset:len(in)-sha256.Size]) - - return removePKCSPadding(plaintext) -} - -// Implement PKCS#7 padding with block size of 16 (AES block size). - -// addPKCSPadding adds padding to a block of data -func addPKCSPadding(src []byte) []byte { - padding := aes.BlockSize - len(src)%aes.BlockSize - padtext := bytes.Repeat([]byte{byte(padding)}, padding) - return append(src, padtext...) -} - -// removePKCSPadding removes padding from data that was added with addPKCSPadding -func removePKCSPadding(src []byte) ([]byte, error) { - length := len(src) - padLength := int(src[length-1]) - if padLength > aes.BlockSize || length < aes.BlockSize { - return nil, errInvalidPadding - } - - return src[:length-padLength], nil -} diff --git a/btcec/ciphering_test.go b/btcec/ciphering_test.go deleted file mode 100644 index 819f18846..000000000 --- a/btcec/ciphering_test.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) 2015-2016 The btcsuite developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package btcec - -import ( - "bytes" - "encoding/hex" - "testing" -) - -func TestGenerateSharedSecret(t *testing.T) { - privKey1, err := NewPrivateKey(S256()) - if err != nil { - t.Errorf("private key generation error: %s", err) - return - } - privKey2, err := NewPrivateKey(S256()) - if err != nil { - t.Errorf("private key generation error: %s", err) - return - } - - secret1 := GenerateSharedSecret(privKey1, privKey2.PubKey()) - secret2 := GenerateSharedSecret(privKey2, privKey1.PubKey()) - - if !bytes.Equal(secret1, secret2) { - t.Errorf("ECDH failed, secrets mismatch - first: %x, second: %x", - secret1, secret2) - } -} - -// Test 1: Encryption and decryption -func TestCipheringBasic(t *testing.T) { - privkey, err := NewPrivateKey(S256()) - if err != nil { - t.Fatal("failed to generate private key") - } - - in := []byte("Hey there dude. How are you doing? This is a test.") - - out, err := Encrypt(privkey.PubKey(), in) - if err != nil { - t.Fatal("failed to encrypt:", err) - } - - dec, err := Decrypt(privkey, out) - if err != nil { - t.Fatal("failed to decrypt:", err) - } - - if !bytes.Equal(in, dec) { - t.Error("decrypted data doesn't match original") - } -} - -// Test 2: Byte compatibility with Pyelliptic -func TestCiphering(t *testing.T) { - pb, _ := hex.DecodeString("fe38240982f313ae5afb3e904fb8215fb11af1200592b" + - "fca26c96c4738e4bf8f") - privkey, _ := PrivKeyFromBytes(S256(), pb) - - in := []byte("This is just a test.") - out, _ := hex.DecodeString("b0d66e5adaa5ed4e2f0ca68e17b8f2fc02ca002009e3" + - "3487e7fa4ab505cf34d98f131be7bd258391588ca7804acb30251e71a04e0020ecf" + - "df0f84608f8add82d7353af780fbb28868c713b7813eb4d4e61f7b75d7534dd9856" + - "9b0ba77cf14348fcff80fee10e11981f1b4be372d93923e9178972f69937ec850ed" + - "6c3f11ff572ddd5b2bedf9f9c0b327c54da02a28fcdce1f8369ffec") - - dec, err := Decrypt(privkey, out) - if err != nil { - t.Fatal("failed to decrypt:", err) - } - - if !bytes.Equal(in, dec) { - t.Error("decrypted data doesn't match original") - } -} - -func TestCipheringErrors(t *testing.T) { - privkey, err := NewPrivateKey(S256()) - if err != nil { - t.Fatal("failed to generate private key") - } - - tests1 := []struct { - ciphertext []byte // input ciphertext - }{ - {bytes.Repeat([]byte{0x00}, 133)}, // errInputTooShort - {bytes.Repeat([]byte{0x00}, 134)}, // errUnsupportedCurve - {bytes.Repeat([]byte{0x02, 0xCA}, 134)}, // errInvalidXLength - {bytes.Repeat([]byte{0x02, 0xCA, 0x00, 0x20}, 134)}, // errInvalidYLength - {[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IV - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0xCA, 0x00, 0x20, // curve and X length - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // X - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x20, // Y length - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Y - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ciphertext - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MAC - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }}, // invalid pubkey - {[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IV - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0xCA, 0x00, 0x20, // curve and X length - 0x11, 0x5C, 0x42, 0xE7, 0x57, 0xB2, 0xEF, 0xB7, // X - 0x67, 0x1C, 0x57, 0x85, 0x30, 0xEC, 0x19, 0x1A, - 0x13, 0x59, 0x38, 0x1E, 0x6A, 0x71, 0x12, 0x7A, - 0x9D, 0x37, 0xC4, 0x86, 0xFD, 0x30, 0xDA, 0xE5, - 0x00, 0x20, // Y length - 0x7E, 0x76, 0xDC, 0x58, 0xF6, 0x93, 0xBD, 0x7E, // Y - 0x70, 0x10, 0x35, 0x8C, 0xE6, 0xB1, 0x65, 0xE4, - 0x83, 0xA2, 0x92, 0x10, 0x10, 0xDB, 0x67, 0xAC, - 0x11, 0xB1, 0xB5, 0x1B, 0x65, 0x19, 0x53, 0xD2, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ciphertext - // padding not aligned to 16 bytes - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MAC - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }}, // errInvalidPadding - {[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // IV - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0xCA, 0x00, 0x20, // curve and X length - 0x11, 0x5C, 0x42, 0xE7, 0x57, 0xB2, 0xEF, 0xB7, // X - 0x67, 0x1C, 0x57, 0x85, 0x30, 0xEC, 0x19, 0x1A, - 0x13, 0x59, 0x38, 0x1E, 0x6A, 0x71, 0x12, 0x7A, - 0x9D, 0x37, 0xC4, 0x86, 0xFD, 0x30, 0xDA, 0xE5, - 0x00, 0x20, // Y length - 0x7E, 0x76, 0xDC, 0x58, 0xF6, 0x93, 0xBD, 0x7E, // Y - 0x70, 0x10, 0x35, 0x8C, 0xE6, 0xB1, 0x65, 0xE4, - 0x83, 0xA2, 0x92, 0x10, 0x10, 0xDB, 0x67, 0xAC, - 0x11, 0xB1, 0xB5, 0x1B, 0x65, 0x19, 0x53, 0xD2, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ciphertext - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // MAC - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - }}, // ErrInvalidMAC - } - - for i, test := range tests1 { - _, err = Decrypt(privkey, test.ciphertext) - if err == nil { - t.Errorf("Decrypt #%d did not get error", i) - } - } - - // test error from removePKCSPadding - tests2 := []struct { - in []byte // input data - }{ - {bytes.Repeat([]byte{0x11}, 17)}, - {bytes.Repeat([]byte{0x07}, 15)}, - } - for i, test := range tests2 { - _, err = removePKCSPadding(test.in) - if err == nil { - t.Errorf("removePKCSPadding #%d did not get error", i) - } - } -} diff --git a/btcec/example_test.go b/btcec/example_test.go index b634a8801..6c6cf3a61 100644 --- a/btcec/example_test.go +++ b/btcec/example_test.go @@ -86,83 +86,3 @@ func Example_verifySignature() { // Output: // Signature Verified? true } - -// This example demonstrates encrypting a message for a public key that is first -// parsed from raw bytes, then decrypting it using the corresponding private key. -func Example_encryptMessage() { - // Decode the hex-encoded pubkey of the recipient. - pubKeyBytes, err := hex.DecodeString("04115c42e757b2efb7671c578530ec191a1" + - "359381e6a71127a9d37c486fd30dae57e76dc58f693bd7e7010358ce6b165e483a29" + - "21010db67ac11b1b51b651953d2") // uncompressed pubkey - if err != nil { - fmt.Println(err) - return - } - pubKey, err := btcec.ParsePubKey(pubKeyBytes, btcec.S256()) - if err != nil { - fmt.Println(err) - return - } - - // Encrypt a message decryptable by the private key corresponding to pubKey - message := "test message" - ciphertext, err := btcec.Encrypt(pubKey, []byte(message)) - if err != nil { - fmt.Println(err) - return - } - - // Decode the hex-encoded private key. - pkBytes, err := hex.DecodeString("a11b0a4e1a132305652ee7a8eb7848f6ad" + - "5ea381e3ce20a2c086a2e388230811") - if err != nil { - fmt.Println(err) - return - } - // note that we already have corresponding pubKey - privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) - - // Try decrypting and verify if it's the same message. - plaintext, err := btcec.Decrypt(privKey, ciphertext) - if err != nil { - fmt.Println(err) - return - } - - fmt.Println(string(plaintext)) - - // Output: - // test message -} - -// This example demonstrates decrypting a message using a private key that is -// first parsed from raw bytes. -func Example_decryptMessage() { - // Decode the hex-encoded private key. - pkBytes, err := hex.DecodeString("a11b0a4e1a132305652ee7a8eb7848f6ad" + - "5ea381e3ce20a2c086a2e388230811") - if err != nil { - fmt.Println(err) - return - } - - privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), pkBytes) - - ciphertext, err := hex.DecodeString("35f644fbfb208bc71e57684c3c8b437402ca" + - "002047a2f1b38aa1a8f1d5121778378414f708fe13ebf7b4a7bb74407288c1958969" + - "00207cf4ac6057406e40f79961c973309a892732ae7a74ee96cd89823913b8b8d650" + - "a44166dc61ea1c419d47077b748a9c06b8d57af72deb2819d98a9d503efc59fc8307" + - "d14174f8b83354fac3ff56075162") - - // Try decrypting the message. - plaintext, err := btcec.Decrypt(privKey, ciphertext) - if err != nil { - fmt.Println(err) - return - } - - fmt.Println(string(plaintext)) - - // Output: - // test message -}