Added ECDH and encryption/decryption support
This commit is contained in:
parent
007bee5ec8
commit
58f29ad939
6 changed files with 489 additions and 1 deletions
|
@ -47,6 +47,16 @@ $ go get github.com/btcsuite/btcd/btcec
|
||||||
Demonstrates verifying a secp256k1 signature against a public key that is
|
Demonstrates verifying a secp256k1 signature against a public key that is
|
||||||
first parsed from raw bytes. The signature is also parsed from raw bytes.
|
first parsed from raw bytes. The signature is also parsed from raw bytes.
|
||||||
|
|
||||||
|
* [Encryption]
|
||||||
|
(http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--EncryptMessage)
|
||||||
|
Demonstrates encrypting a message for a public key that is first parsed from
|
||||||
|
raw bytes, then decrypting it using the corresponding private key.
|
||||||
|
|
||||||
|
* [Decryption]
|
||||||
|
(http://godoc.org/github.com/btcsuite/btcd/btcec#example-package--DecryptMessage)
|
||||||
|
Demonstrates decrypting a message using a private key that is first parsed
|
||||||
|
from raw bytes.
|
||||||
|
|
||||||
## GPG Verification Key
|
## GPG Verification Key
|
||||||
|
|
||||||
All official release tags are signed by Conformal so users can ensure the code
|
All official release tags are signed by Conformal so users can ensure the code
|
||||||
|
|
216
btcec/ciphering.go
Normal file
216
btcec/ciphering.go
Normal file
|
@ -0,0 +1,216 @@
|
||||||
|
// Copyright (c) 2015 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"
|
||||||
|
"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
|
||||||
|
// private 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. Additionaly,
|
||||||
|
// 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 !bytes.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
|
||||||
|
}
|
176
btcec/ciphering_test.go
Normal file
176
btcec/ciphering_test.go
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
// Copyright (c) 2015 The btcsuite developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package btcec_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/hex"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/btcsuite/btcd/btcec"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGenerateSharedSecret(t *testing.T) {
|
||||||
|
privKey1, err := btcec.NewPrivateKey(btcec.S256())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("private key generation error: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
privKey2, err := btcec.NewPrivateKey(btcec.S256())
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("private key generation error: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
secret1 := btcec.GenerateSharedSecret(privKey1, privKey2.PubKey())
|
||||||
|
secret2 := btcec.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 := btcec.NewPrivateKey(btcec.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 := btcec.Encrypt(privkey.PubKey(), in)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("failed to encrypt:", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dec, err := btcec.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, _ := btcec.PrivKeyFromBytes(btcec.S256(), pb)
|
||||||
|
|
||||||
|
in := []byte("This is just a test.")
|
||||||
|
out, _ := hex.DecodeString("b0d66e5adaa5ed4e2f0ca68e17b8f2fc02ca002009e3" +
|
||||||
|
"3487e7fa4ab505cf34d98f131be7bd258391588ca7804acb30251e71a04e0020ecf" +
|
||||||
|
"df0f84608f8add82d7353af780fbb28868c713b7813eb4d4e61f7b75d7534dd9856" +
|
||||||
|
"9b0ba77cf14348fcff80fee10e11981f1b4be372d93923e9178972f69937ec850ed" +
|
||||||
|
"6c3f11ff572ddd5b2bedf9f9c0b327c54da02a28fcdce1f8369ffec")
|
||||||
|
|
||||||
|
dec, err := btcec.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 := btcec.NewPrivateKey(btcec.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 = btcec.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 = btcec.TstRemovePKCSPadding(test.in)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("removePKCSPadding #%d did not get error", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -86,3 +86,83 @@ func Example_verifySignature() {
|
||||||
// Output:
|
// Output:
|
||||||
// Signature Verified? true
|
// 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
|
||||||
|
}
|
||||||
|
|
|
@ -74,3 +74,9 @@ func NewFieldVal() *fieldVal {
|
||||||
func TstNonceRFC6979(privkey *big.Int, hash []byte) *big.Int {
|
func TstNonceRFC6979(privkey *big.Int, hash []byte) *big.Int {
|
||||||
return nonceRFC6979(privkey, hash)
|
return nonceRFC6979(privkey, hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TstRemovePKCSPadding makes the internal removePKCSPadding function available
|
||||||
|
// to the test package.
|
||||||
|
func TstRemovePKCSPadding(src []byte) ([]byte, error) {
|
||||||
|
return removePKCSPadding(src)
|
||||||
|
}
|
||||||
|
|
|
@ -107,7 +107,7 @@ func ParsePubKey(pubKeyStr []byte, curve *KoblitzCurve) (key *PublicKey, err err
|
||||||
return nil, fmt.Errorf("pubkey Y parameter is >= to P")
|
return nil, fmt.Errorf("pubkey Y parameter is >= to P")
|
||||||
}
|
}
|
||||||
if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) {
|
if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) {
|
||||||
return nil, fmt.Errorf("pubkey isn't on secp265k1 curve")
|
return nil, fmt.Errorf("pubkey isn't on secp256k1 curve")
|
||||||
}
|
}
|
||||||
return &pubkey, nil
|
return &pubkey, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue