From d812dc782e37372eef62c30a0cc71cb29395261b Mon Sep 17 00:00:00 2001 From: Volodymyr Paprotski Date: Mon, 9 Jan 2017 16:29:35 -0500 Subject: [PATCH] Introduce placeholder for PKCS11 CSP This is simply a copy of the SW CSP: - sw directory -> pkcs11 directory - swfactory.go -> pkcs11factory.go Not much changed: pkcs11factory.go uses PKCS11 instead of "Software" or "SW". And new package name. The code is currently un-reachable (except by copied tests). Next set of changes will add - configuration: probably integrate what Keith did for https://gerrit.hyperledger.org/r/#/c/3735/2 - replace (operation at a time) SW operations with PKCS11 operations Change-Id: I46f35b92be9303e52d66fdbc0962990efb47104e Signed-off-by: Volodymyr Paprotski --- bccsp/factory/pkcs11factory.go | 87 ++ bccsp/pkcs11/aes.go | 137 +++ bccsp/pkcs11/aes_test.go | 478 +++++++++ bccsp/pkcs11/aeskey.go | 65 ++ bccsp/pkcs11/conf.go | 85 ++ bccsp/pkcs11/dummyks.go | 44 + bccsp/pkcs11/ecdsa.go | 23 + bccsp/pkcs11/ecdsakey.go | 120 +++ bccsp/pkcs11/fileks.go | 397 +++++++ bccsp/pkcs11/fileks_test.go | 66 ++ bccsp/pkcs11/impl.go | 728 +++++++++++++ bccsp/pkcs11/impl_test.go | 1798 ++++++++++++++++++++++++++++++++ bccsp/pkcs11/rsakey.go | 136 +++ 13 files changed, 4164 insertions(+) create mode 100644 bccsp/factory/pkcs11factory.go create mode 100644 bccsp/pkcs11/aes.go create mode 100644 bccsp/pkcs11/aes_test.go create mode 100644 bccsp/pkcs11/aeskey.go create mode 100644 bccsp/pkcs11/conf.go create mode 100644 bccsp/pkcs11/dummyks.go create mode 100644 bccsp/pkcs11/ecdsa.go create mode 100644 bccsp/pkcs11/ecdsakey.go create mode 100644 bccsp/pkcs11/fileks.go create mode 100644 bccsp/pkcs11/fileks_test.go create mode 100644 bccsp/pkcs11/impl.go create mode 100644 bccsp/pkcs11/impl_test.go create mode 100644 bccsp/pkcs11/rsakey.go diff --git a/bccsp/factory/pkcs11factory.go b/bccsp/factory/pkcs11factory.go new file mode 100644 index 00000000000..358288e7990 --- /dev/null +++ b/bccsp/factory/pkcs11factory.go @@ -0,0 +1,87 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package factory + +import ( + "errors" + "fmt" + "sync" + + "github.com/hyperledger/fabric/bccsp" + "github.com/hyperledger/fabric/bccsp/pkcs11" +) + +const ( + // PKCS11BasedFactoryName is the name of the factory of the hsm-based BCCSP implementation + PKCS11BasedFactoryName = "P11" +) + +// PKCS11Factory is the factory of the HSM-based BCCSP. +type PKCS11Factory struct { + initOnce sync.Once + bccsp bccsp.BCCSP + err error +} + +// Name returns the name of this factory +func (f *PKCS11Factory) Name() string { + return PKCS11BasedFactoryName +} + +// Get returns an instance of BCCSP using Opts. +func (f *PKCS11Factory) Get(opts Opts) (bccsp.BCCSP, error) { + // Validate arguments + if opts == nil { + return nil, errors.New("Invalid opts. It must not be nil.") + } + + if opts.FactoryName() != f.Name() { + return nil, fmt.Errorf("Invalid Provider Name [%s]. Opts must refer to [%s].", opts.FactoryName(), f.Name()) + } + + pkcs11Opts, ok := opts.(*PKCS11Opts) + if !ok { + return nil, errors.New("Invalid opts. They must be of type PKCS11Opts.") + } + + if !opts.Ephemeral() { + f.initOnce.Do(func() { + f.bccsp, f.err = pkcs11.New(pkcs11Opts.SecLevel, pkcs11Opts.HashFamily, pkcs11Opts.KeyStore) + return + }) + return f.bccsp, f.err + } + + return pkcs11.New(pkcs11Opts.SecLevel, pkcs11Opts.HashFamily, pkcs11Opts.KeyStore) +} + +// PKCS11Opts contains options for the P11Factory +type PKCS11Opts struct { + Ephemeral_ bool + SecLevel int + HashFamily string + KeyStore bccsp.KeyStore +} + +// FactoryName returns the name of the provider +func (o *PKCS11Opts) FactoryName() string { + return PKCS11BasedFactoryName +} + +// Ephemeral returns true if the CSP has to be ephemeral, false otherwise +func (o *PKCS11Opts) Ephemeral() bool { + return o.Ephemeral_ +} diff --git a/bccsp/pkcs11/aes.go b/bccsp/pkcs11/aes.go new file mode 100644 index 00000000000..3f06035ecb6 --- /dev/null +++ b/bccsp/pkcs11/aes.go @@ -0,0 +1,137 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pkcs11 + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "errors" + "fmt" + "io" +) + +// GetRandomBytes returns len random looking bytes +func GetRandomBytes(len int) ([]byte, error) { + buffer := make([]byte, len) + + n, err := rand.Read(buffer) + if err != nil { + return nil, err + } + if n != len { + return nil, fmt.Errorf("Buffer not filled. Requested [%d], got [%d]", len, n) + } + + return buffer, nil +} + +func pkcs7Padding(src []byte) []byte { + padding := aes.BlockSize - len(src)%aes.BlockSize + padtext := bytes.Repeat([]byte{byte(padding)}, padding) + return append(src, padtext...) +} + +func pkcs7UnPadding(src []byte) ([]byte, error) { + length := len(src) + unpadding := int(src[length-1]) + + if unpadding > aes.BlockSize || unpadding == 0 { + return nil, errors.New("Invalid pkcs7 padding (unpadding > aes.BlockSize || unpadding == 0)") + } + + pad := src[len(src)-unpadding:] + for i := 0; i < unpadding; i++ { + if pad[i] != byte(unpadding) { + return nil, errors.New("Invalid pkcs7 padding (pad[i] != unpadding)") + } + } + + return src[:(length - unpadding)], nil +} + +func aesCBCEncrypt(key, s []byte) ([]byte, error) { + if len(s)%aes.BlockSize != 0 { + return nil, errors.New("Invalid plaintext. It must be a multiple of the block size") + } + + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + ciphertext := make([]byte, aes.BlockSize+len(s)) + iv := ciphertext[:aes.BlockSize] + if _, err := io.ReadFull(rand.Reader, iv); err != nil { + return nil, err + } + + mode := cipher.NewCBCEncrypter(block, iv) + mode.CryptBlocks(ciphertext[aes.BlockSize:], s) + + return ciphertext, nil +} + +func aesCBCDecrypt(key, src []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + if len(src) < aes.BlockSize { + return nil, errors.New("Invalid ciphertext. It must be a multiple of the block size") + } + iv := src[:aes.BlockSize] + src = src[aes.BlockSize:] + + if len(src)%aes.BlockSize != 0 { + return nil, errors.New("Invalid ciphertext. It must be a multiple of the block size") + } + + mode := cipher.NewCBCDecrypter(block, iv) + + mode.CryptBlocks(src, src) + + return src, nil +} + +// AESCBCPKCS7Encrypt combines CBC encryption and PKCS7 padding +func AESCBCPKCS7Encrypt(key, src []byte) ([]byte, error) { + // First pad + tmp := pkcs7Padding(src) + + // Then encrypt + return aesCBCEncrypt(key, tmp) +} + +// AESCBCPKCS7Decrypt combines CBC decryption and PKCS7 unpadding +func AESCBCPKCS7Decrypt(key, src []byte) ([]byte, error) { + // First decrypt + pt, err := aesCBCDecrypt(key, src) + if err != nil { + return nil, err + } + + // Then remove padding + original, err := pkcs7UnPadding(pt) + if err != nil { + return nil, err + } + + return original, nil +} diff --git a/bccsp/pkcs11/aes_test.go b/bccsp/pkcs11/aes_test.go new file mode 100644 index 00000000000..15380e64511 --- /dev/null +++ b/bccsp/pkcs11/aes_test.go @@ -0,0 +1,478 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package pkcs11 + +import ( + "bytes" + "crypto/aes" + "crypto/rand" + "math/big" + "testing" + + "github.com/hyperledger/fabric/bccsp/utils" +) + +// TestCBCPKCS7EncryptCBCPKCS7Decrypt encrypts using CBCPKCS7Encrypt and decrypts using CBCPKCS7Decrypt. +func TestCBCPKCS7EncryptCBCPKCS7Decrypt(t *testing.T) { + + // Note: The purpose of this test is not to test AES-256 in CBC mode's strength + // ... but rather to verify the code wrapping/unwrapping the cipher. + key := make([]byte, 32) + rand.Reader.Read(key) + + // 123456789012345678901234567890123456789012 + var ptext = []byte("a message with arbitrary length (42 bytes)") + + encrypted, encErr := AESCBCPKCS7Encrypt(key, ptext) + if encErr != nil { + t.Fatalf("Error encrypting '%s': %s", ptext, encErr) + } + + decrypted, dErr := AESCBCPKCS7Decrypt(key, encrypted) + if dErr != nil { + t.Fatalf("Error decrypting the encrypted '%s': %v", ptext, dErr) + } + + if string(ptext[:]) != string(decrypted[:]) { + t.Fatal("Decrypt( Encrypt( ptext ) ) != ptext: Ciphertext decryption with the same key must result in the original plaintext!") + } + +} + +// TestPKCS7Padding verifies the PKCS#7 padding, using a human readable plaintext. +func TestPKCS7Padding(t *testing.T) { + + // 0 byte/length ptext + ptext := []byte("") + expected := []byte{16, 16, 16, 16, + 16, 16, 16, 16, + 16, 16, 16, 16, + 16, 16, 16, 16} + result := pkcs7Padding(ptext) + + if !bytes.Equal(expected, result) { + t.Fatal("Padding error! Expected: ", expected, "', received: '", result, "'") + } + + // 1 byte/length ptext + ptext = []byte("1") + expected = []byte{'1', 15, 15, 15, + 15, 15, 15, 15, + 15, 15, 15, 15, + 15, 15, 15, 15} + result = pkcs7Padding(ptext) + + if !bytes.Equal(expected, result) { + t.Fatal("Padding error! Expected: '", expected, "', received: '", result, "'") + } + + // 2 byte/length ptext + ptext = []byte("12") + expected = []byte{'1', '2', 14, 14, + 14, 14, 14, 14, + 14, 14, 14, 14, + 14, 14, 14, 14} + result = pkcs7Padding(ptext) + + if !bytes.Equal(expected, result) { + t.Fatal("Padding error! Expected: '", expected, "', received: '", result, "'") + } + + // 3 to aes.BlockSize-1 byte plaintext + ptext = []byte("1234567890ABCDEF") + for i := 3; i < aes.BlockSize; i++ { + result := pkcs7Padding(ptext[:i]) + + padding := aes.BlockSize - i + expectedPadding := bytes.Repeat([]byte{byte(padding)}, padding) + expected = append(ptext[:i], expectedPadding...) + + if !bytes.Equal(result, expected) { + t.Fatal("Padding error! Expected: '", expected, "', received: '", result, "'") + } + + } + + // aes.BlockSize length ptext + ptext = bytes.Repeat([]byte{byte('x')}, aes.BlockSize) + result = pkcs7Padding(ptext) + + expectedPadding := bytes.Repeat([]byte{byte(aes.BlockSize)}, aes.BlockSize) + expected = append(ptext, expectedPadding...) + + if len(result) != 2*aes.BlockSize { + t.Fatal("Padding error: expected the length of the returned slice to be 2 times aes.BlockSize") + } + + if !bytes.Equal(expected, result) { + t.Fatal("Padding error! Expected: '", expected, "', received: '", result, "'") + } + +} + +// TestPKCS7UnPadding verifies the PKCS#7 unpadding, using a human readable plaintext. +func TestPKCS7UnPadding(t *testing.T) { + + // 0 byte/length ptext + expected := []byte("") + ptext := []byte{16, 16, 16, 16, + 16, 16, 16, 16, + 16, 16, 16, 16, + 16, 16, 16, 16} + + result, _ := pkcs7UnPadding(ptext) + + if !bytes.Equal(expected, result) { + t.Fatal("UnPadding error! Expected: '", expected, "', received: '", result, "'") + } + + // 1 byte/length ptext + expected = []byte("1") + ptext = []byte{'1', 15, 15, 15, + 15, 15, 15, 15, + 15, 15, 15, 15, + 15, 15, 15, 15} + + result, _ = pkcs7UnPadding(ptext) + + if !bytes.Equal(expected, result) { + t.Fatal("UnPadding error! Expected: '", expected, "', received: '", result, "'") + } + + // 2 byte/length ptext + expected = []byte("12") + ptext = []byte{'1', '2', 14, 14, + 14, 14, 14, 14, + 14, 14, 14, 14, + 14, 14, 14, 14} + + result, _ = pkcs7UnPadding(ptext) + + if !bytes.Equal(expected, result) { + t.Fatal("UnPadding error! Expected: '", expected, "', received: '", result, "'") + } + + // 3 to aes.BlockSize-1 byte plaintext + base := []byte("1234567890ABCDEF") + for i := 3; i < aes.BlockSize; i++ { + iPad := aes.BlockSize - i + padding := bytes.Repeat([]byte{byte(iPad)}, iPad) + ptext = append(base[:i], padding...) + + expected := base[:i] + result, _ := pkcs7UnPadding(ptext) + + if !bytes.Equal(result, expected) { + t.Fatal("UnPadding error! Expected: '", expected, "', received: '", result, "'") + } + + } + + // aes.BlockSize length ptext + expected = bytes.Repeat([]byte{byte('x')}, aes.BlockSize) + padding := bytes.Repeat([]byte{byte(aes.BlockSize)}, aes.BlockSize) + ptext = append(expected, padding...) + + result, _ = pkcs7UnPadding(ptext) + + if !bytes.Equal(expected, result) { + t.Fatal("UnPadding error! Expected: '", expected, "', received: '", result, "'") + } +} + +// TestCBCEncryptCBCPKCS7Decrypt_BlockSizeLengthPlaintext verifies that CBCPKCS7Decrypt returns an error +// when attempting to decrypt ciphertext of an irreproducible length. +func TestCBCEncryptCBCPKCS7Decrypt_BlockSizeLengthPlaintext(t *testing.T) { + + // One of the purposes of this test is to also document and clarify the expected behavior, i.e., that an extra + // block is appended to the message at the padding stage, as per the spec of PKCS#7 v1.5 [see RFC-2315 p.21] + key := make([]byte, 32) + rand.Reader.Read(key) + + // 1234567890123456 + var ptext = []byte("a 16 byte messag") + + encrypted, encErr := aesCBCEncrypt(key, ptext) + if encErr != nil { + t.Fatalf("Error encrypting '%s': %v", ptext, encErr) + } + + decrypted, dErr := AESCBCPKCS7Decrypt(key, encrypted) + if dErr == nil { + t.Fatalf("Expected an error decrypting ptext '%s'. Decrypted to '%v'", dErr, decrypted) + } +} + +// TestCBCPKCS7EncryptCBCDecrypt_ExpectingCorruptMessage verifies that CBCDecrypt can decrypt the unpadded +// version of the ciphertext, of a message of BlockSize length. +func TestCBCPKCS7EncryptCBCDecrypt_ExpectingCorruptMessage(t *testing.T) { + + // One of the purposes of this test is to also document and clarify the expected behavior, i.e., that an extra + // block is appended to the message at the padding stage, as per the spec of PKCS#7 v1.5 [see RFC-2315 p.21] + key := make([]byte, 32) + rand.Reader.Read(key) + + // 0123456789ABCDEF + var ptext = []byte("a 16 byte messag") + + encrypted, encErr := AESCBCPKCS7Encrypt(key, ptext) + if encErr != nil { + t.Fatalf("Error encrypting ptext %v", encErr) + } + + decrypted, dErr := aesCBCDecrypt(key, encrypted) + if dErr != nil { + t.Fatalf("Error encrypting ptext %v, %v", dErr, decrypted) + } + + if string(ptext[:]) != string(decrypted[:aes.BlockSize]) { + t.Log("ptext: ", ptext) + t.Log("decrypted: ", decrypted[:aes.BlockSize]) + t.Fatal("Encryption->Decryption with same key should result in original ptext") + } + + if !bytes.Equal(decrypted[aes.BlockSize:], bytes.Repeat([]byte{byte(aes.BlockSize)}, aes.BlockSize)) { + t.Fatal("Expected extra block with padding in encrypted ptext", decrypted) + } + +} + +// TestCBCPKCS7Encrypt_EmptyPlaintext encrypts and pad an empty ptext. Verifying as well that the ciphertext length is as expected. +func TestCBCPKCS7Encrypt_EmptyPlaintext(t *testing.T) { + + key := make([]byte, 32) + rand.Reader.Read(key) + + t.Log("Generated key: ", key) + + var emptyPlaintext = []byte("") + t.Log("Plaintext length: ", len(emptyPlaintext)) + + ciphertext, encErr := AESCBCPKCS7Encrypt(key, emptyPlaintext) + if encErr != nil { + t.Fatalf("Error encrypting '%v'", encErr) + } + + // Expected ciphertext length: 32 (=32) + // As part of the padding, at least one block gets encrypted (while the first block is the IV) + const expectedLength = aes.BlockSize + aes.BlockSize + if len(ciphertext) != expectedLength { + t.Fatalf("Wrong ciphertext length. Expected %d, recieved %d", expectedLength, len(ciphertext)) + } + + t.Log("Ciphertext length: ", len(ciphertext)) + t.Log("Cipher: ", ciphertext) +} + +// TestCBCEncrypt_EmptyPlaintext encrypts an empty message. Verifying as well that the ciphertext length is as expected. +func TestCBCEncrypt_EmptyPlaintext(t *testing.T) { + + key := make([]byte, 32) + rand.Reader.Read(key) + t.Log("Generated key: ", key) + + var emptyPlaintext = []byte("") + t.Log("Message length: ", len(emptyPlaintext)) + + ciphertext, encErr := aesCBCEncrypt(key, emptyPlaintext) + if encErr != nil { + } + + t.Log("Ciphertext length: ", len(ciphertext)) + + // Expected cipher length: aes.BlockSize, the first and only block is the IV + var expectedLength = aes.BlockSize + + if len(ciphertext) != expectedLength { + t.Fatalf("Wrong ciphertext length. Expected: '%d', received: '%d'", expectedLength, len(ciphertext)) + } + t.Log("Ciphertext: ", ciphertext) +} + +// TestCBCPKCS7Encrypt_VerifyRandomIVs encrypts twice with same key. The first 16 bytes should be different if IV is generated randomly. +func TestCBCPKCS7Encrypt_VerifyRandomIVs(t *testing.T) { + + key := make([]byte, aes.BlockSize) + rand.Reader.Read(key) + t.Log("Key 1", key) + + var ptext = []byte("a message to encrypt") + + ciphertext1, err := AESCBCPKCS7Encrypt(key, ptext) + if err != nil { + t.Fatalf("Error encrypting '%s': %s", ptext, err) + } + + // Expecting a different IV if same message is encrypted with same key + ciphertext2, err := AESCBCPKCS7Encrypt(key, ptext) + if err != nil { + t.Fatalf("Error encrypting '%s': %s", ptext, err) + } + + iv1 := ciphertext1[:aes.BlockSize] + iv2 := ciphertext2[:aes.BlockSize] + + t.Log("Ciphertext1: ", iv1) + t.Log("Ciphertext2: ", iv2) + t.Log("bytes.Equal: ", bytes.Equal(iv1, iv2)) + + if bytes.Equal(iv1, iv2) { + t.Fatal("Error: ciphertexts contain identical initialization vectors (IVs)") + } +} + +// TestCBCPKCS7Encrypt_CorrectCiphertextLengthCheck verifies that the returned ciphertext lengths are as expected. +func TestCBCPKCS7Encrypt_CorrectCiphertextLengthCheck(t *testing.T) { + + key := make([]byte, aes.BlockSize) + rand.Reader.Read(key) + + // length of message (in bytes) == aes.BlockSize (16 bytes) + // The expected cipher length = IV length (1 block) + 1 block message + + var ptext = []byte("0123456789ABCDEF") + + for i := 1; i < aes.BlockSize; i++ { + ciphertext, err := AESCBCPKCS7Encrypt(key, ptext[:i]) + if err != nil { + t.Fatal("Error encrypting '", ptext, "'") + } + + expectedLength := aes.BlockSize + aes.BlockSize + if len(ciphertext) != expectedLength { + t.Fatalf("Incorrect ciphertext incorrect: expected '%d', received '%d'", expectedLength, len(ciphertext)) + } + } +} + +// TestCBCEncryptCBCDecrypt_KeyMismatch attempts to decrypt with a different key than the one used for encryption. +func TestCBCEncryptCBCDecrypt_KeyMismatch(t *testing.T) { + + // Generate a random key + key := make([]byte, aes.BlockSize) + rand.Reader.Read(key) + + // Clone & tamper with the key + wrongKey := make([]byte, aes.BlockSize) + copy(wrongKey, key[:]) + wrongKey[0] = key[0] + 1 + + var ptext = []byte("1234567890ABCDEF") + encrypted, encErr := aesCBCEncrypt(key, ptext) + if encErr != nil { + t.Fatalf("Error encrypting '%s': %v", ptext, encErr) + } + + decrypted, decErr := aesCBCDecrypt(wrongKey, encrypted) + if decErr != nil { + t.Fatalf("Error decrypting '%s': %v", ptext, decErr) + } + + if string(ptext[:]) == string(decrypted[:]) { + t.Fatal("Decrypting a ciphertext with a different key than the one used for encrypting it - should not result in the original plaintext.") + } +} + +// TestCBCEncryptCBCDecrypt encrypts with CBCEncrypt and decrypt with CBCDecrypt. +func TestCBCEncryptCBCDecrypt(t *testing.T) { + + key := make([]byte, 32) + rand.Reader.Read(key) + + // 1234567890123456 + var ptext = []byte("a 16 byte messag") + + encrypted, encErr := aesCBCEncrypt(key, ptext) + if encErr != nil { + t.Fatalf("Error encrypting '%s': %v", ptext, encErr) + } + + decrypted, decErr := aesCBCDecrypt(key, encrypted) + if decErr != nil { + t.Fatalf("Error decrypting '%s': %v", ptext, decErr) + } + + if string(ptext[:]) != string(decrypted[:]) { + t.Fatal("Encryption->Decryption with same key should result in the original plaintext.") + } +} + +// TestAESRelatedUtilFunctions tests various functions commonly used in fabric wrt AES +func TestAESRelatedUtilFunctions(t *testing.T) { + + key, err := GetRandomBytes(32) + if err != nil { + t.Fatalf("Failed generating AES key [%s]", err) + } + + for i := 1; i < 100; i++ { + l, err := rand.Int(rand.Reader, big.NewInt(1024)) + if err != nil { + t.Fatalf("Failed generating AES key [%s]", err) + } + msg, err := GetRandomBytes(int(l.Int64()) + 1) + if err != nil { + t.Fatalf("Failed generating AES key [%s]", err) + } + + ct, err := AESCBCPKCS7Encrypt(key, msg) + if err != nil { + t.Fatalf("Failed encrypting [%s]", err) + } + + msg2, err := AESCBCPKCS7Decrypt(key, ct) + if err != nil { + t.Fatalf("Failed decrypting [%s]", err) + } + + if 0 != bytes.Compare(msg, msg2) { + t.Fatalf("Wrong decryption output [%x][%x]", msg, msg2) + } + + } + +} + +// TestVariousAESKeyEncoding tests some AES <-> PEM conversions +func TestVariousAESKeyEncoding(t *testing.T) { + key, err := GetRandomBytes(32) + if err != nil { + t.Fatalf("Failed generating AES key [%s]", err) + } + + // PEM format + pem := utils.AEStoPEM(key) + keyFromPEM, err := utils.PEMtoAES(pem, nil) + if err != nil { + t.Fatalf("Failed converting PEM to AES key [%s]", err) + } + if 0 != bytes.Compare(key, keyFromPEM) { + t.Fatalf("Failed converting PEM to AES key. Keys are different [%x][%x]", key, keyFromPEM) + } + + // Encrypted PEM format + pem, err = utils.AEStoEncryptedPEM(key, []byte("passwd")) + if err != nil { + t.Fatalf("Failed converting AES key to Encrypted PEM [%s]", err) + } + keyFromPEM, err = utils.PEMtoAES(pem, []byte("passwd")) + if err != nil { + t.Fatalf("Failed converting encrypted PEM to AES key [%s]", err) + } + if 0 != bytes.Compare(key, keyFromPEM) { + t.Fatalf("Failed converting encrypted PEM to AES key. Keys are different [%x][%x]", key, keyFromPEM) + } +} diff --git a/bccsp/pkcs11/aeskey.go b/bccsp/pkcs11/aeskey.go new file mode 100644 index 00000000000..3a5f899046c --- /dev/null +++ b/bccsp/pkcs11/aeskey.go @@ -0,0 +1,65 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package pkcs11 + +import ( + "errors" + + "crypto/sha256" + + "github.com/hyperledger/fabric/bccsp" +) + +type aesPrivateKey struct { + privKey []byte + exportable bool +} + +// Bytes converts this key to its byte representation, +// if this operation is allowed. +func (k *aesPrivateKey) Bytes() (raw []byte, err error) { + if k.exportable { + return k.privKey, nil + } + + return nil, errors.New("Not supported.") +} + +// SKI returns the subject key identifier of this key. +func (k *aesPrivateKey) SKI() (ski []byte) { + hash := sha256.New() + hash.Write([]byte{0x01}) + hash.Write(k.privKey) + return hash.Sum(nil) +} + +// Symmetric returns true if this key is a symmetric key, +// false if this key is asymmetric +func (k *aesPrivateKey) Symmetric() bool { + return true +} + +// Private returns true if this key is a private key, +// false otherwise. +func (k *aesPrivateKey) Private() bool { + return true +} + +// PublicKey returns the corresponding public key part of an asymmetric public/private key pair. +// This method returns an error in symmetric key schemes. +func (k *aesPrivateKey) PublicKey() (bccsp.Key, error) { + return nil, errors.New("Cannot call this method on a symmetric key.") +} diff --git a/bccsp/pkcs11/conf.go b/bccsp/pkcs11/conf.go new file mode 100644 index 00000000000..d11230de865 --- /dev/null +++ b/bccsp/pkcs11/conf.go @@ -0,0 +1,85 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package pkcs11 + +import ( + "crypto/elliptic" + "crypto/sha256" + "crypto/sha512" + "fmt" + "hash" + + "golang.org/x/crypto/sha3" +) + +type config struct { + keyStorePath string + securityLevel int + hashFamily string + + ellipticCurve elliptic.Curve + hashFunction func() hash.Hash + aesBitLength int + rsaBitLength int +} + +func (conf *config) setSecurityLevel(securityLevel int, hashFamily string) (err error) { + switch hashFamily { + case "SHA2": + err = conf.setSecurityLevelSHA2(securityLevel) + case "SHA3": + err = conf.setSecurityLevelSHA3(securityLevel) + default: + err = fmt.Errorf("Hash Family not supported [%s]", hashFamily) + } + return +} + +func (conf *config) setSecurityLevelSHA2(level int) (err error) { + switch level { + case 256: + conf.ellipticCurve = elliptic.P256() + conf.hashFunction = sha256.New + conf.rsaBitLength = 2048 + conf.aesBitLength = 32 + case 384: + conf.ellipticCurve = elliptic.P384() + conf.hashFunction = sha512.New384 + conf.rsaBitLength = 3072 + conf.aesBitLength = 32 + default: + err = fmt.Errorf("Security level not supported [%d]", level) + } + return +} + +func (conf *config) setSecurityLevelSHA3(level int) (err error) { + switch level { + case 256: + conf.ellipticCurve = elliptic.P256() + conf.hashFunction = sha3.New256 + conf.rsaBitLength = 2048 + conf.aesBitLength = 32 + case 384: + conf.ellipticCurve = elliptic.P384() + conf.hashFunction = sha3.New384 + conf.rsaBitLength = 3072 + conf.aesBitLength = 32 + default: + err = fmt.Errorf("Security level not supported [%d]", level) + } + return +} diff --git a/bccsp/pkcs11/dummyks.go b/bccsp/pkcs11/dummyks.go new file mode 100644 index 00000000000..c28544032e3 --- /dev/null +++ b/bccsp/pkcs11/dummyks.go @@ -0,0 +1,44 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pkcs11 + +import ( + "errors" + + "github.com/hyperledger/fabric/bccsp" +) + +// DummyKeyStore is a read-only KeyStore that neither loads nor stores keys. +type DummyKeyStore struct { +} + +// ReadOnly returns true if this KeyStore is read only, false otherwise. +// If ReadOnly is true then StoreKey will fail. +func (ks *DummyKeyStore) ReadOnly() bool { + return true +} + +// GetKey returns a key object whose SKI is the one passed. +func (ks *DummyKeyStore) GetKey(ski []byte) (k bccsp.Key, err error) { + return nil, errors.New("Key not found. This is a dummy KeyStore") +} + +// StoreKey stores the key k in this KeyStore. +// If this KeyStore is read only then the method will fail. +func (ks *DummyKeyStore) StoreKey(k bccsp.Key) (err error) { + return errors.New("Cannot store key. This is a dummy read-only KeyStore") +} diff --git a/bccsp/pkcs11/ecdsa.go b/bccsp/pkcs11/ecdsa.go new file mode 100644 index 00000000000..452b6b5d196 --- /dev/null +++ b/bccsp/pkcs11/ecdsa.go @@ -0,0 +1,23 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package pkcs11 + +import "math/big" + +// ECDSASignature represents an ECDSA signature +type ecdsaSignature struct { + R, S *big.Int +} diff --git a/bccsp/pkcs11/ecdsakey.go b/bccsp/pkcs11/ecdsakey.go new file mode 100644 index 00000000000..3a05fb8ee5f --- /dev/null +++ b/bccsp/pkcs11/ecdsakey.go @@ -0,0 +1,120 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package pkcs11 + +import ( + "crypto/ecdsa" + "crypto/x509" + "fmt" + + "crypto/sha256" + + "errors" + + "crypto/elliptic" + + "github.com/hyperledger/fabric/bccsp" +) + +type ecdsaPrivateKey struct { + privKey *ecdsa.PrivateKey +} + +// Bytes converts this key to its byte representation, +// if this operation is allowed. +func (k *ecdsaPrivateKey) Bytes() (raw []byte, err error) { + return nil, errors.New("Not supported.") +} + +// SKI returns the subject key identifier of this key. +func (k *ecdsaPrivateKey) SKI() (ski []byte) { + if k.privKey == nil { + return nil + } + + // Marshall the public key + raw := elliptic.Marshal(k.privKey.Curve, k.privKey.PublicKey.X, k.privKey.PublicKey.Y) + + // Hash it + hash := sha256.New() + hash.Write(raw) + return hash.Sum(nil) +} + +// Symmetric returns true if this key is a symmetric key, +// false if this key is asymmetric +func (k *ecdsaPrivateKey) Symmetric() bool { + return false +} + +// Private returns true if this key is a private key, +// false otherwise. +func (k *ecdsaPrivateKey) Private() bool { + return true +} + +// PublicKey returns the corresponding public key part of an asymmetric public/private key pair. +// This method returns an error in symmetric key schemes. +func (k *ecdsaPrivateKey) PublicKey() (bccsp.Key, error) { + return &ecdsaPublicKey{&k.privKey.PublicKey}, nil +} + +type ecdsaPublicKey struct { + pubKey *ecdsa.PublicKey +} + +// Bytes converts this key to its byte representation, +// if this operation is allowed. +func (k *ecdsaPublicKey) Bytes() (raw []byte, err error) { + raw, err = x509.MarshalPKIXPublicKey(k.pubKey) + if err != nil { + return nil, fmt.Errorf("Failed marshalling key [%s]", err) + } + return +} + +// SKI returns the subject key identifier of this key. +func (k *ecdsaPublicKey) SKI() (ski []byte) { + if k.pubKey == nil { + return nil + } + + // Marshall the public key + raw := elliptic.Marshal(k.pubKey.Curve, k.pubKey.X, k.pubKey.Y) + + // Hash it + hash := sha256.New() + hash.Write(raw) + return hash.Sum(nil) +} + +// Symmetric returns true if this key is a symmetric key, +// false if this key is asymmetric +func (k *ecdsaPublicKey) Symmetric() bool { + return false +} + +// Private returns true if this key is a private key, +// false otherwise. +func (k *ecdsaPublicKey) Private() bool { + return false +} + +// PublicKey returns the corresponding public key part of an asymmetric public/private key pair. +// This method returns an error in symmetric key schemes. +func (k *ecdsaPublicKey) PublicKey() (bccsp.Key, error) { + return k, nil +} diff --git a/bccsp/pkcs11/fileks.go b/bccsp/pkcs11/fileks.go new file mode 100644 index 00000000000..142d18468f0 --- /dev/null +++ b/bccsp/pkcs11/fileks.go @@ -0,0 +1,397 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package pkcs11 + +import ( + "io/ioutil" + "os" + "sync" + + "errors" + "strings" + + "crypto/ecdsa" + "crypto/rsa" + "encoding/hex" + "fmt" + "path/filepath" + + "github.com/hyperledger/fabric/bccsp" + "github.com/hyperledger/fabric/bccsp/utils" +) + +// FileBasedKeyStore is a folder-based KeyStore. +// Each key is stored in a separated file whose name contains the key's SKI +// and flags to identity the key's type. All the keys are stored in +// a folder whose path is provided at initialization time. +// The KeyStore can be initialized with a password, this password +// is used to encrypt and decrypt the files storing the keys. +// A KeyStore can be read only to avoid the overwriting of keys. +type FileBasedKeyStore struct { + path string + + readOnly bool + isOpen bool + + pwd []byte + + // Sync + m sync.Mutex +} + +// Init initializes this KeyStore with a password, a path to a folder +// where the keys are stored and a read only flag. +// Each key is stored in a separated file whose name contains the key's SKI +// and flags to identity the key's type. +// If the KeyStore is initialized with a password, this password +// is used to encrypt and decrypt the files storing the keys. +// The pwd can be nil for non-encrypted KeyStores. If an encrypted +// key-store is initialized without a password, then retrieving keys from the +// KeyStore will fail. +// A KeyStore can be read only to avoid the overwriting of keys. +func (ks *FileBasedKeyStore) Init(pwd []byte, path string, readOnly bool) error { + // Validate inputs + // pwd can be nil + + if len(path) == 0 { + return errors.New("An invalid KeyStore path provided. Path cannot be an empty string.") + } + + ks.m.Lock() + defer ks.m.Unlock() + + if ks.isOpen { + return errors.New("KeyStore already initilized.") + } + + ks.path = path + ks.pwd = utils.Clone(pwd) + + err := ks.createKeyStoreIfNotExists() + if err != nil { + return err + } + + err = ks.openKeyStore() + if err != nil { + return err + } + + ks.readOnly = readOnly + + return nil +} + +// ReadOnly returns true if this KeyStore is read only, false otherwise. +// If ReadOnly is true then StoreKey will fail. +func (ks *FileBasedKeyStore) ReadOnly() bool { + return ks.readOnly +} + +// GetKey returns a key object whose SKI is the one passed. +func (ks *FileBasedKeyStore) GetKey(ski []byte) (k bccsp.Key, err error) { + // Validate arguments + if len(ski) == 0 { + return nil, errors.New("Invalid SKI. Cannot be of zero length.") + } + + suffix := ks.getSuffix(hex.EncodeToString(ski)) + + switch suffix { + case "key": + // Load the key + key, err := ks.loadKey(hex.EncodeToString(ski)) + if err != nil { + return nil, fmt.Errorf("Failed loading key [%x] [%s]", ski, err) + } + + return &aesPrivateKey{key, false}, nil + case "sk": + // Load the private key + key, err := ks.loadPrivateKey(hex.EncodeToString(ski)) + if err != nil { + return nil, fmt.Errorf("Failed loading secret key [%x] [%s]", ski, err) + } + + switch key.(type) { + case *ecdsa.PrivateKey: + return &ecdsaPrivateKey{key.(*ecdsa.PrivateKey)}, nil + case *rsa.PrivateKey: + return &rsaPrivateKey{key.(*rsa.PrivateKey)}, nil + default: + return nil, errors.New("Secret key type not recognized") + } + case "pk": + // Load the public key + key, err := ks.loadPublicKey(hex.EncodeToString(ski)) + if err != nil { + return nil, fmt.Errorf("Failed loading public key [%x] [%s]", ski, err) + } + + switch key.(type) { + case *ecdsa.PublicKey: + return &ecdsaPublicKey{key.(*ecdsa.PublicKey)}, nil + case *rsa.PublicKey: + return &rsaPublicKey{key.(*rsa.PublicKey)}, nil + default: + return nil, errors.New("Public key type not recognized") + } + default: + return nil, errors.New("Key type not recognized") + } +} + +// StoreKey stores the key k in this KeyStore. +// If this KeyStore is read only then the method will fail. +func (ks *FileBasedKeyStore) StoreKey(k bccsp.Key) (err error) { + if ks.readOnly { + return errors.New("Read only KeyStore.") + } + + if k == nil { + return errors.New("Invalid key. It must be different from nil.") + } + switch k.(type) { + case *ecdsaPrivateKey: + kk := k.(*ecdsaPrivateKey) + + err = ks.storePrivateKey(hex.EncodeToString(k.SKI()), kk.privKey) + if err != nil { + return fmt.Errorf("Failed storing ECDSA private key [%s]", err) + } + + case *ecdsaPublicKey: + kk := k.(*ecdsaPublicKey) + + err = ks.storePublicKey(hex.EncodeToString(k.SKI()), kk.pubKey) + if err != nil { + return fmt.Errorf("Failed storing ECDSA public key [%s]", err) + } + + case *rsaPrivateKey: + kk := k.(*rsaPrivateKey) + + err = ks.storePrivateKey(hex.EncodeToString(k.SKI()), kk.privKey) + if err != nil { + return fmt.Errorf("Failed storing RSA private key [%s]", err) + } + + case *rsaPublicKey: + kk := k.(*rsaPublicKey) + + err = ks.storePublicKey(hex.EncodeToString(k.SKI()), kk.pubKey) + if err != nil { + return fmt.Errorf("Failed storing RSA public key [%s]", err) + } + + case *aesPrivateKey: + kk := k.(*aesPrivateKey) + + err = ks.storeKey(hex.EncodeToString(k.SKI()), kk.privKey) + if err != nil { + return fmt.Errorf("Failed storing AES key [%s]", err) + } + + default: + return fmt.Errorf("Key type not reconigned [%s]", k) + } + + return +} + +func (ks *FileBasedKeyStore) getSuffix(alias string) string { + files, _ := ioutil.ReadDir(ks.path) + for _, f := range files { + if strings.HasPrefix(f.Name(), alias) { + if strings.HasSuffix(f.Name(), "sk") { + return "sk" + } + if strings.HasSuffix(f.Name(), "pk") { + return "pk" + } + if strings.HasSuffix(f.Name(), "key") { + return "key" + } + break + } + } + return "" +} + +func (ks *FileBasedKeyStore) storePrivateKey(alias string, privateKey interface{}) error { + rawKey, err := utils.PrivateKeyToPEM(privateKey, ks.pwd) + if err != nil { + logger.Errorf("Failed converting private key to PEM [%s]: [%s]", alias, err) + return err + } + + err = ioutil.WriteFile(ks.getPathForAlias(alias, "sk"), rawKey, 0700) + if err != nil { + logger.Errorf("Failed storing private key [%s]: [%s]", alias, err) + return err + } + + return nil +} + +func (ks *FileBasedKeyStore) storePublicKey(alias string, publicKey interface{}) error { + rawKey, err := utils.PublicKeyToPEM(publicKey, ks.pwd) + if err != nil { + logger.Errorf("Failed converting public key to PEM [%s]: [%s]", alias, err) + return err + } + + err = ioutil.WriteFile(ks.getPathForAlias(alias, "pk"), rawKey, 0700) + if err != nil { + logger.Errorf("Failed storing private key [%s]: [%s]", alias, err) + return err + } + + return nil +} + +func (ks *FileBasedKeyStore) storeKey(alias string, key []byte) error { + pem, err := utils.AEStoEncryptedPEM(key, ks.pwd) + if err != nil { + logger.Errorf("Failed converting key to PEM [%s]: [%s]", alias, err) + return err + } + + err = ioutil.WriteFile(ks.getPathForAlias(alias, "key"), pem, 0700) + if err != nil { + logger.Errorf("Failed storing key [%s]: [%s]", alias, err) + return err + } + + return nil +} + +func (ks *FileBasedKeyStore) loadPrivateKey(alias string) (interface{}, error) { + path := ks.getPathForAlias(alias, "sk") + logger.Debugf("Loading private key [%s] at [%s]...", alias, path) + + raw, err := ioutil.ReadFile(path) + if err != nil { + logger.Errorf("Failed loading private key [%s]: [%s].", alias, err.Error()) + + return nil, err + } + + privateKey, err := utils.PEMtoPrivateKey(raw, ks.pwd) + if err != nil { + logger.Errorf("Failed parsing private key [%s]: [%s].", alias, err.Error()) + + return nil, err + } + + return privateKey, nil +} + +func (ks *FileBasedKeyStore) loadPublicKey(alias string) (interface{}, error) { + path := ks.getPathForAlias(alias, "pk") + logger.Debugf("Loading public key [%s] at [%s]...", alias, path) + + raw, err := ioutil.ReadFile(path) + if err != nil { + logger.Errorf("Failed loading public key [%s]: [%s].", alias, err.Error()) + + return nil, err + } + + privateKey, err := utils.PEMtoPublicKey(raw, ks.pwd) + if err != nil { + logger.Errorf("Failed parsing private key [%s]: [%s].", alias, err.Error()) + + return nil, err + } + + return privateKey, nil +} + +func (ks *FileBasedKeyStore) loadKey(alias string) ([]byte, error) { + path := ks.getPathForAlias(alias, "key") + logger.Debugf("Loading key [%s] at [%s]...", alias, path) + + pem, err := ioutil.ReadFile(path) + if err != nil { + logger.Errorf("Failed loading key [%s]: [%s].", alias, err.Error()) + + return nil, err + } + + key, err := utils.PEMtoAES(pem, ks.pwd) + if err != nil { + logger.Errorf("Failed parsing key [%s]: [%s]", alias, err) + + return nil, err + } + + return key, nil +} + +func (ks *FileBasedKeyStore) close() error { + ks.isOpen = false + logger.Debug("Closing keystore...done!") + return nil +} + +func (ks *FileBasedKeyStore) createKeyStoreIfNotExists() error { + // Check keystore directory + ksPath := ks.path + missing, err := utils.DirMissingOrEmpty(ksPath) + logger.Infof("KeyStore path [%s] missing [%t]: [%s]", ksPath, missing, utils.ErrToString(err)) + + if missing { + err := ks.createKeyStore() + if err != nil { + logger.Errorf("Failed creating KeyStore At [%s]: [%s]", ksPath, err.Error()) + return nil + } + } + + return nil +} + +func (ks *FileBasedKeyStore) createKeyStore() error { + // Create keystore directory root if it doesn't exist yet + ksPath := ks.path + logger.Debugf("Creating KeyStore at [%s]...", ksPath) + + os.MkdirAll(ksPath, 0755) + + logger.Debugf("KeyStore created at [%s].", ksPath) + return nil +} + +func (ks *FileBasedKeyStore) deleteKeyStore() error { + logger.Debugf("Removing KeyStore at [%s].", ks.path) + + return os.RemoveAll(ks.path) +} + +func (ks *FileBasedKeyStore) openKeyStore() error { + if ks.isOpen { + return nil + } + + logger.Debugf("KeyStore opened at [%s]...done", ks.path) + + return nil +} + +func (ks *FileBasedKeyStore) getPathForAlias(alias, suffix string) string { + return filepath.Join(ks.path, alias+"_"+suffix) +} diff --git a/bccsp/pkcs11/fileks_test.go b/bccsp/pkcs11/fileks_test.go new file mode 100644 index 00000000000..59ec0098f8f --- /dev/null +++ b/bccsp/pkcs11/fileks_test.go @@ -0,0 +1,66 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package pkcs11 + +import ( + "fmt" + "os" + "path/filepath" + "testing" +) + +func TestInvalidStoreKey(t *testing.T) { + ks := &FileBasedKeyStore{} + if err := ks.Init(nil, filepath.Join(os.TempDir(), "bccspks"), false); err != nil { + fmt.Printf("Failed initiliazing KeyStore [%s]", err) + os.Exit(-1) + } + + err := ks.StoreKey(nil) + if err == nil { + t.Fatal("Error should be different from nil in this case") + } + + err = ks.StoreKey(&ecdsaPrivateKey{nil}) + if err == nil { + t.Fatal("Error should be different from nil in this case") + } + + err = ks.StoreKey(&ecdsaPublicKey{nil}) + if err == nil { + t.Fatal("Error should be different from nil in this case") + } + + err = ks.StoreKey(&rsaPublicKey{nil}) + if err == nil { + t.Fatal("Error should be different from nil in this case") + } + + err = ks.StoreKey(&rsaPrivateKey{nil}) + if err == nil { + t.Fatal("Error should be different from nil in this case") + } + + err = ks.StoreKey(&aesPrivateKey{nil, false}) + if err == nil { + t.Fatal("Error should be different from nil in this case") + } + + err = ks.StoreKey(&aesPrivateKey{nil, true}) + if err == nil { + t.Fatal("Error should be different from nil in this case") + } +} diff --git a/bccsp/pkcs11/impl.go b/bccsp/pkcs11/impl.go new file mode 100644 index 00000000000..5b6b6b60193 --- /dev/null +++ b/bccsp/pkcs11/impl.go @@ -0,0 +1,728 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package pkcs11 + +import ( + "crypto/ecdsa" + "crypto/rand" + "encoding/asn1" + "errors" + "fmt" + "math/big" + + "crypto/rsa" + + "hash" + + "crypto/x509" + + "crypto/hmac" + + "crypto/elliptic" + "crypto/sha256" + "crypto/sha512" + + "github.com/hyperledger/fabric/bccsp" + "github.com/hyperledger/fabric/bccsp/utils" + "github.com/op/go-logging" + "golang.org/x/crypto/sha3" +) + +var ( + logger = logging.MustGetLogger("SW_BCCSP") +) + +// NewDefaultSecurityLevel returns a new instance of the software-based BCCSP +// at security level 256, hash family SHA2 and using FolderBasedKeyStore as KeyStore. +func NewDefaultSecurityLevel(keyStorePath string) (bccsp.BCCSP, error) { + ks := &FileBasedKeyStore{} + if err := ks.Init(nil, keyStorePath, false); err != nil { + return nil, fmt.Errorf("Failed initializing key store [%s]", err) + } + + return New(256, "SHA2", ks) +} + +// NewDefaultSecurityLevel returns a new instance of the software-based BCCSP +// at security level 256, hash family SHA2 and using the passed KeyStore. +func NewDefaultSecurityLevelWithKeystore(keyStore bccsp.KeyStore) (bccsp.BCCSP, error) { + return New(256, "SHA2", keyStore) +} + +// New returns a new instance of the software-based BCCSP +// set at the passed security level, hash family and KeyStore. +func New(securityLevel int, hashFamily string, keyStore bccsp.KeyStore) (bccsp.BCCSP, error) { + // Init config + conf := &config{} + err := conf.setSecurityLevel(securityLevel, hashFamily) + if err != nil { + return nil, fmt.Errorf("Failed initializing configuration [%s]", err) + } + + // Check KeyStore + if keyStore == nil { + return nil, errors.New("Invalid bccsp.KeyStore instance. It must be different from nil.") + } + + return &impl{conf, keyStore}, nil +} + +// SoftwareBasedBCCSP is the software-based implementation of the BCCSP. +type impl struct { + conf *config + ks bccsp.KeyStore +} + +// KeyGen generates a key using opts. +func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { + // Validate arguments + if opts == nil { + return nil, errors.New("Invalid Opts parameter. It must not be nil.") + } + + // Parse algorithm + switch opts.(type) { + case *bccsp.ECDSAKeyGenOpts: + lowLevelKey, err := ecdsa.GenerateKey(csp.conf.ellipticCurve, rand.Reader) + if err != nil { + return nil, fmt.Errorf("Failed generating ECDSA key [%s]", err) + } + + k = &ecdsaPrivateKey{lowLevelKey} + + case *bccsp.ECDSAP256KeyGenOpts: + lowLevelKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + return nil, fmt.Errorf("Failed generating ECDSA P256 key [%s]", err) + } + + k = &ecdsaPrivateKey{lowLevelKey} + + case *bccsp.ECDSAP384KeyGenOpts: + lowLevelKey, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader) + if err != nil { + return nil, fmt.Errorf("Failed generating ECDSA P384 key [%s]", err) + } + + k = &ecdsaPrivateKey{lowLevelKey} + + case *bccsp.AESKeyGenOpts: + lowLevelKey, err := GetRandomBytes(csp.conf.aesBitLength) + + if err != nil { + return nil, fmt.Errorf("Failed generating AES key [%s]", err) + } + + k = &aesPrivateKey{lowLevelKey, false} + + case *bccsp.AES256KeyGenOpts: + lowLevelKey, err := GetRandomBytes(32) + + if err != nil { + return nil, fmt.Errorf("Failed generating AES 256 key [%s]", err) + } + + k = &aesPrivateKey{lowLevelKey, false} + + case *bccsp.AES192KeyGenOpts: + lowLevelKey, err := GetRandomBytes(24) + + if err != nil { + return nil, fmt.Errorf("Failed generating AES 192 key [%s]", err) + } + + k = &aesPrivateKey{lowLevelKey, false} + + case *bccsp.AES128KeyGenOpts: + lowLevelKey, err := GetRandomBytes(16) + + if err != nil { + return nil, fmt.Errorf("Failed generating AES 128 key [%s]", err) + } + + k = &aesPrivateKey{lowLevelKey, false} + + case *bccsp.RSAKeyGenOpts: + lowLevelKey, err := rsa.GenerateKey(rand.Reader, csp.conf.rsaBitLength) + + if err != nil { + return nil, fmt.Errorf("Failed generating RSA key [%s]", err) + } + + k = &rsaPrivateKey{lowLevelKey} + + case *bccsp.RSA1024KeyGenOpts: + lowLevelKey, err := rsa.GenerateKey(rand.Reader, 1024) + + if err != nil { + return nil, fmt.Errorf("Failed generating RSA 1024 key [%s]", err) + } + + k = &rsaPrivateKey{lowLevelKey} + + case *bccsp.RSA2048KeyGenOpts: + lowLevelKey, err := rsa.GenerateKey(rand.Reader, 2048) + + if err != nil { + return nil, fmt.Errorf("Failed generating RSA 2048 key [%s]", err) + } + + k = &rsaPrivateKey{lowLevelKey} + + case *bccsp.RSA3072KeyGenOpts: + lowLevelKey, err := rsa.GenerateKey(rand.Reader, 3072) + + if err != nil { + return nil, fmt.Errorf("Failed generating RSA 3072 key [%s]", err) + } + + k = &rsaPrivateKey{lowLevelKey} + + case *bccsp.RSA4096KeyGenOpts: + lowLevelKey, err := rsa.GenerateKey(rand.Reader, 4096) + + if err != nil { + return nil, fmt.Errorf("Failed generating RSA 4096 key [%s]", err) + } + + k = &rsaPrivateKey{lowLevelKey} + + default: + return nil, fmt.Errorf("Unrecognized KeyGenOpts provided [%s]", opts.Algorithm()) + } + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.StoreKey(k) + if err != nil { + return nil, fmt.Errorf("Failed storing key [%s]. [%s]", opts.Algorithm(), err) + } + } + + return k, nil +} + +// KeyDeriv derives a key from k using opts. +// The opts argument should be appropriate for the primitive used. +func (csp *impl) KeyDeriv(k bccsp.Key, opts bccsp.KeyDerivOpts) (dk bccsp.Key, err error) { + // Validate arguments + if k == nil { + return nil, errors.New("Invalid Key. It must not be nil.") + } + + // Derive key + switch k.(type) { + case *ecdsaPrivateKey: + // Validate opts + if opts == nil { + return nil, errors.New("Invalid Opts parameter. It must not be nil.") + } + + ecdsaK := k.(*ecdsaPrivateKey) + + switch opts.(type) { + + // Re-randomized an ECDSA private key + case *bccsp.ECDSAReRandKeyOpts: + reRandOpts := opts.(*bccsp.ECDSAReRandKeyOpts) + tempSK := &ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{ + Curve: ecdsaK.privKey.Curve, + X: new(big.Int), + Y: new(big.Int), + }, + D: new(big.Int), + } + + var k = new(big.Int).SetBytes(reRandOpts.ExpansionValue()) + var one = new(big.Int).SetInt64(1) + n := new(big.Int).Sub(ecdsaK.privKey.Params().N, one) + k.Mod(k, n) + k.Add(k, one) + + tempSK.D.Add(ecdsaK.privKey.D, k) + tempSK.D.Mod(tempSK.D, ecdsaK.privKey.PublicKey.Params().N) + + // Compute temporary public key + tempX, tempY := ecdsaK.privKey.PublicKey.ScalarBaseMult(k.Bytes()) + tempSK.PublicKey.X, tempSK.PublicKey.Y = + tempSK.PublicKey.Add( + ecdsaK.privKey.PublicKey.X, ecdsaK.privKey.PublicKey.Y, + tempX, tempY, + ) + + // Verify temporary public key is a valid point on the reference curve + isOn := tempSK.Curve.IsOnCurve(tempSK.PublicKey.X, tempSK.PublicKey.Y) + if !isOn { + return nil, errors.New("Failed temporary public key IsOnCurve check. This is an foreign key.") + } + + reRandomizedKey := &ecdsaPrivateKey{tempSK} + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.StoreKey(reRandomizedKey) + if err != nil { + return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) + } + } + + return reRandomizedKey, nil + + default: + return nil, fmt.Errorf("Unrecognized KeyDerivOpts provided [%s]", opts.Algorithm()) + + } + case *aesPrivateKey: + // Validate opts + if opts == nil { + return nil, errors.New("Invalid Opts parameter. It must not be nil.") + } + + aesK := k.(*aesPrivateKey) + + switch opts.(type) { + case *bccsp.HMACTruncated256AESDeriveKeyOpts: + hmacOpts := opts.(*bccsp.HMACTruncated256AESDeriveKeyOpts) + + mac := hmac.New(csp.conf.hashFunction, aesK.privKey) + mac.Write(hmacOpts.Argument()) + hmacedKey := &aesPrivateKey{mac.Sum(nil)[:csp.conf.aesBitLength], false} + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.StoreKey(hmacedKey) + if err != nil { + return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) + } + } + + return hmacedKey, nil + + case *bccsp.HMACDeriveKeyOpts: + + hmacOpts := opts.(*bccsp.HMACDeriveKeyOpts) + + mac := hmac.New(csp.conf.hashFunction, aesK.privKey) + mac.Write(hmacOpts.Argument()) + hmacedKey := &aesPrivateKey{mac.Sum(nil), true} + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.StoreKey(hmacedKey) + if err != nil { + return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) + } + } + + return hmacedKey, nil + + default: + return nil, fmt.Errorf("Unrecognized KeyDerivOpts provided [%s]", opts.Algorithm()) + + } + + default: + return nil, fmt.Errorf("Key type not recognized [%s]", k) + } +} + +// KeyImport imports a key from its raw representation using opts. +// The opts argument should be appropriate for the primitive used. +func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.Key, err error) { + // Validate arguments + if raw == nil { + return nil, errors.New("Invalid raw. Cannot be nil") + } + + if opts == nil { + return nil, errors.New("Invalid Opts parameter. It must not be nil.") + } + + switch opts.(type) { + + case *bccsp.AES256ImportKeyOpts: + aesRaw, ok := raw.([]byte) + if !ok { + return nil, errors.New("[AES256ImportKeyOpts] Invalid raw material. Expected byte array.") + } + + if len(aesRaw) != 32 { + return nil, fmt.Errorf("[AES256ImportKeyOpts] Invalid Key Length [%d]. Must be 32 bytes", len(aesRaw)) + } + + aesK := &aesPrivateKey{utils.Clone(aesRaw), false} + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.StoreKey(aesK) + if err != nil { + return nil, fmt.Errorf("Failed storing AES key [%s]", err) + } + } + + return aesK, nil + + case *bccsp.HMACImportKeyOpts: + aesRaw, ok := raw.([]byte) + if !ok { + return nil, errors.New("[HMACImportKeyOpts] Invalid raw material. Expected byte array.") + } + + if len(aesRaw) == 0 { + return nil, errors.New("[HMACImportKeyOpts] Invalid raw. It must not be nil.") + } + + aesK := &aesPrivateKey{utils.Clone(aesRaw), false} + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.StoreKey(aesK) + if err != nil { + return nil, fmt.Errorf("Failed storing AES key [%s]", err) + } + } + + return aesK, nil + + case *bccsp.ECDSAPKIXPublicKeyImportOpts: + der, ok := raw.([]byte) + if !ok { + return nil, errors.New("[ECDSAPKIXPublicKeyImportOpts] Invalid raw material. Expected byte array.") + } + + if len(der) == 0 { + return nil, errors.New("[ECDSAPKIXPublicKeyImportOpts] Invalid raw. It must not be nil.") + } + + lowLevelKey, err := utils.DERToPublicKey(der) + if err != nil { + return nil, fmt.Errorf("Failed converting PKIX to ECDSA public key [%s]", err) + } + + ecdsaPK, ok := lowLevelKey.(*ecdsa.PublicKey) + if !ok { + return nil, errors.New("Failed casting to ECDSA public key. Invalid raw material.") + } + + k = &ecdsaPublicKey{ecdsaPK} + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.StoreKey(k) + if err != nil { + return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) + } + } + + return k, nil + + case *bccsp.ECDSAPrivateKeyImportOpts: + der, ok := raw.([]byte) + if !ok { + return nil, errors.New("[ECDSADERPrivateKeyImportOpts] Invalid raw material. Expected byte array.") + } + + if len(der) == 0 { + return nil, errors.New("[ECDSADERPrivateKeyImportOpts] Invalid raw. It must not be nil.") + } + + lowLevelKey, err := utils.DERToPrivateKey(der) + if err != nil { + return nil, fmt.Errorf("Failed converting PKIX to ECDSA public key [%s]", err) + } + + ecdsaSK, ok := lowLevelKey.(*ecdsa.PrivateKey) + if !ok { + return nil, errors.New("Failed casting to ECDSA public key. Invalid raw material.") + } + + k = &ecdsaPrivateKey{ecdsaSK} + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.StoreKey(k) + if err != nil { + return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) + } + } + + return k, nil + + case *bccsp.ECDSAGoPublicKeyImportOpts: + lowLevelKey, ok := raw.(*ecdsa.PublicKey) + if !ok { + return nil, errors.New("[ECDSAGoPublicKeyImportOpts] Invalid raw material. Expected *ecdsa.PublicKey.") + } + + k = &ecdsaPublicKey{lowLevelKey} + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.StoreKey(k) + if err != nil { + return nil, fmt.Errorf("Failed storing ECDSA key [%s]", err) + } + } + + return k, nil + + case *bccsp.RSAGoPublicKeyImportOpts: + lowLevelKey, ok := raw.(*rsa.PublicKey) + if !ok { + return nil, errors.New("[RSAGoPublicKeyImportOpts] Invalid raw material. Expected *rsa.PublicKey.") + } + + k = &rsaPublicKey{lowLevelKey} + + // If the key is not Ephemeral, store it. + if !opts.Ephemeral() { + // Store the key + err = csp.ks.StoreKey(k) + if err != nil { + return nil, fmt.Errorf("Failed storing RSA publi key [%s]", err) + } + } + + return k, nil + + case *bccsp.X509PublicKeyImportOpts: + x509Cert, ok := raw.(*x509.Certificate) + if !ok { + return nil, errors.New("[X509PublicKeyImportOpts] Invalid raw material. Expected *x509.Certificate.") + } + + pk := x509Cert.PublicKey + + switch pk.(type) { + case *ecdsa.PublicKey: + return csp.KeyImport(pk, &bccsp.ECDSAGoPublicKeyImportOpts{Temporary: opts.Ephemeral()}) + case *rsa.PublicKey: + return csp.KeyImport(pk, &bccsp.RSAGoPublicKeyImportOpts{Temporary: opts.Ephemeral()}) + default: + return nil, errors.New("Certificate public key type not recognized. Supported keys: [ECDSA, RSA]") + } + + default: + return nil, errors.New("Import Key Options not recognized") + } +} + +// GetKey returns the key this CSP associates to +// the Subject Key Identifier ski. +func (csp *impl) GetKey(ski []byte) (k bccsp.Key, err error) { + return csp.ks.GetKey(ski) +} + +// Hash hashes messages msg using options opts. +func (csp *impl) Hash(msg []byte, opts bccsp.HashOpts) (digest []byte, err error) { + var h hash.Hash + if opts == nil { + h = csp.conf.hashFunction() + } else { + switch opts.(type) { + case *bccsp.SHAOpts: + h = csp.conf.hashFunction() + case *bccsp.SHA256Opts: + h = sha256.New() + case *bccsp.SHA384Opts: + h = sha512.New384() + case *bccsp.SHA3_256Opts: + h = sha3.New256() + case *bccsp.SHA3_384Opts: + h = sha3.New384() + default: + return nil, fmt.Errorf("Algorithm not recognized [%s]", opts.Algorithm()) + } + } + + h.Write(msg) + return h.Sum(nil), nil +} + +// GetHash returns and instance of hash.Hash using options opts. +// If opts is nil then the default hash function is returned. +func (csp *impl) GetHash(opts bccsp.HashOpts) (h hash.Hash, err error) { + if opts == nil { + return csp.conf.hashFunction(), nil + } + + switch opts.(type) { + case *bccsp.SHAOpts: + return csp.conf.hashFunction(), nil + case *bccsp.SHA256Opts: + return sha256.New(), nil + case *bccsp.SHA384Opts: + return sha512.New384(), nil + case *bccsp.SHA3_256Opts: + return sha3.New256(), nil + case *bccsp.SHA3_384Opts: + return sha3.New384(), nil + default: + return nil, fmt.Errorf("Algorithm not recognized [%s]", opts.Algorithm()) + } +} + +// Sign signs digest using key k. +// The opts argument should be appropriate for the primitive used. +// +// Note that when a signature of a hash of a larger message is needed, +// the caller is responsible for hashing the larger message and passing +// the hash (as digest). +func (csp *impl) Sign(k bccsp.Key, digest []byte, opts bccsp.SignerOpts) (signature []byte, err error) { + // Validate arguments + if k == nil { + return nil, errors.New("Invalid Key. It must not be nil.") + } + if len(digest) == 0 { + return nil, errors.New("Invalid digest. Cannot be empty.") + } + + // Check key type + switch k.(type) { + case *ecdsaPrivateKey: + return k.(*ecdsaPrivateKey).privKey.Sign(rand.Reader, digest, nil) + case *rsaPrivateKey: + if opts == nil { + return nil, errors.New("Invalid options. Nil.") + } + + return k.(*rsaPrivateKey).privKey.Sign(rand.Reader, digest, opts) + default: + return nil, fmt.Errorf("Key type not recognized [%s]", k) + } +} + +// Verify verifies signature against key k and digest +func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.SignerOpts) (valid bool, err error) { + // Validate arguments + if k == nil { + return false, errors.New("Invalid Key. It must not be nil.") + } + if len(signature) == 0 { + return false, errors.New("Invalid signature. Cannot be empty.") + } + if len(digest) == 0 { + return false, errors.New("Invalid digest. Cannot be empty.") + } + + // Check key type + switch k.(type) { + case *ecdsaPrivateKey: + ecdsaSignature := new(ecdsaSignature) + _, err := asn1.Unmarshal(signature, ecdsaSignature) + if err != nil { + return false, fmt.Errorf("Failed unmashalling signature [%s]", err) + } + + return ecdsa.Verify(&(k.(*ecdsaPrivateKey).privKey.PublicKey), digest, ecdsaSignature.R, ecdsaSignature.S), nil + case *ecdsaPublicKey: + ecdsaSignature := new(ecdsaSignature) + _, err := asn1.Unmarshal(signature, ecdsaSignature) + if err != nil { + return false, fmt.Errorf("Failed unmashalling signature [%s]", err) + } + + return ecdsa.Verify(k.(*ecdsaPublicKey).pubKey, digest, ecdsaSignature.R, ecdsaSignature.S), nil + case *rsaPrivateKey: + if opts == nil { + return false, errors.New("Invalid options. It must not be nil.") + } + switch opts.(type) { + case *rsa.PSSOptions: + err := rsa.VerifyPSS(&(k.(*rsaPrivateKey).privKey.PublicKey), + (opts.(*rsa.PSSOptions)).Hash, + digest, signature, opts.(*rsa.PSSOptions)) + + return err == nil, err + default: + return false, fmt.Errorf("Opts type not recognized [%s]", opts) + } + case *rsaPublicKey: + if opts == nil { + return false, errors.New("Invalid options. It must not be nil.") + } + switch opts.(type) { + case *rsa.PSSOptions: + err := rsa.VerifyPSS(k.(*rsaPublicKey).pubKey, + (opts.(*rsa.PSSOptions)).Hash, + digest, signature, opts.(*rsa.PSSOptions)) + + return err == nil, err + default: + return false, fmt.Errorf("Opts type not recognized [%s]", opts) + } + default: + return false, fmt.Errorf("Key type not recognized [%s]", k) + } +} + +// Encrypt encrypts plaintext using key k. +// The opts argument should be appropriate for the primitive used. +func (csp *impl) Encrypt(k bccsp.Key, plaintext []byte, opts bccsp.EncrypterOpts) (ciphertext []byte, err error) { + // Validate arguments + if k == nil { + return nil, errors.New("Invalid Key. It must not be nil.") + } + + // Check key type + switch k.(type) { + case *aesPrivateKey: + // check for mode + switch opts.(type) { + case *bccsp.AESCBCPKCS7ModeOpts, bccsp.AESCBCPKCS7ModeOpts: + // AES in CBC mode with PKCS7 padding + return AESCBCPKCS7Encrypt(k.(*aesPrivateKey).privKey, plaintext) + default: + return nil, fmt.Errorf("Mode not recognized [%s]", opts) + } + default: + return nil, fmt.Errorf("Key type not recognized [%s]", k) + } +} + +// Decrypt decrypts ciphertext using key k. +// The opts argument should be appropriate for the primitive used. +func (csp *impl) Decrypt(k bccsp.Key, ciphertext []byte, opts bccsp.DecrypterOpts) (plaintext []byte, err error) { + // Validate arguments + if k == nil { + return nil, errors.New("Invalid Key. It must not be nil.") + } + + // Check key type + switch k.(type) { + case *aesPrivateKey: + // check for mode + switch opts.(type) { + case *bccsp.AESCBCPKCS7ModeOpts, bccsp.AESCBCPKCS7ModeOpts: + // AES in CBC mode with PKCS7 padding + return AESCBCPKCS7Decrypt(k.(*aesPrivateKey).privKey, ciphertext) + default: + return nil, fmt.Errorf("Mode not recognized [%s]", opts) + } + default: + return nil, fmt.Errorf("Key type not recognized [%s]", k) + } +} diff --git a/bccsp/pkcs11/impl_test.go b/bccsp/pkcs11/impl_test.go new file mode 100644 index 00000000000..5bc577c7260 --- /dev/null +++ b/bccsp/pkcs11/impl_test.go @@ -0,0 +1,1798 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package pkcs11 + +import ( + "bytes" + "os" + "testing" + + "crypto" + "crypto/rsa" + + "crypto/rand" + "crypto/x509" + "crypto/x509/pkix" + "encoding/asn1" + "math/big" + "net" + "time" + + "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha256" + + "fmt" + + "crypto/sha512" + "hash" + + "github.com/hyperledger/fabric/bccsp" + "github.com/hyperledger/fabric/bccsp/signer" + "github.com/hyperledger/fabric/bccsp/utils" + "golang.org/x/crypto/sha3" +) + +var ( + currentKS bccsp.KeyStore + currentBCCSP bccsp.BCCSP + currentTestConfig testConfig +) + +type testConfig struct { + securityLevel int + hashFamily string +} + +func TestMain(m *testing.M) { + ks := &FileBasedKeyStore{} + if err := ks.Init(nil, os.TempDir(), false); err != nil { + fmt.Printf("Failed initiliazing KeyStore [%s]", err) + os.Exit(-1) + } + currentKS = ks + + tests := []testConfig{ + {256, "SHA2"}, + {256, "SHA3"}, + {384, "SHA2"}, + {384, "SHA3"}, + } + + for _, config := range tests { + var err error + currentTestConfig = config + currentBCCSP, err = New(config.securityLevel, config.hashFamily, currentKS) + if err != nil { + fmt.Printf("Failed initiliazing BCCSP at [%d, %s]: [%s]", config.securityLevel, config.hashFamily, err) + os.Exit(-1) + } + ret := m.Run() + if ret != 0 { + fmt.Printf("Failed testing at [%d, %s]", config.securityLevel, config.hashFamily) + os.Exit(-1) + } + } + os.Exit(0) +} + +func TestInvalidNewParameter(t *testing.T) { + r, err := New(0, "SHA2", currentKS) + if err == nil { + t.Fatal("Error should be different from nil in this case") + } + if r != nil { + t.Fatal("Return value should be equal to nil in this case") + } + + r, err = New(256, "SHA8", currentKS) + if err == nil { + t.Fatal("Error should be different from nil in this case") + } + if r != nil { + t.Fatal("Return value should be equal to nil in this case") + } + + r, err = New(256, "SHA2", nil) + if err == nil { + t.Fatal("Error should be different from nil in this case") + } + if r != nil { + t.Fatal("Return value should be equal to nil in this case") + } + + r, err = New(0, "SHA3", nil) + if err == nil { + t.Fatal("Error should be different from nil in this case") + } + if r != nil { + t.Fatal("Return value should be equal to nil in this case") + } + + r, err = NewDefaultSecurityLevel("") + if err == nil { + t.Fatal("Error should be different from nil in this case") + } + if r != nil { + t.Fatal("Return value should be equal to nil in this case") + } +} + +func TestInvalidSKI(t *testing.T) { + k, err := currentBCCSP.GetKey(nil) + if err == nil { + t.Fatal("Error should be different from nil in this case") + } + if k != nil { + t.Fatal("Return value should be equal to nil in this case") + } + + k, err = currentBCCSP.GetKey([]byte{0, 1, 2, 3, 4, 5, 6}) + if err == nil { + t.Fatal("Error should be different from nil in this case") + } + if k != nil { + t.Fatal("Return value should be equal to nil in this case") + } +} + +func TestKeyGenECDSAOpts(t *testing.T) { + // Curve P256 + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAP256KeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA P256 key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating ECDSA P256 key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating ECDSA P256 key. Key should be private") + } + if k.Symmetric() { + t.Fatal("Failed generating ECDSA P256 key. Key should be asymmetric") + } + + ecdsaKey := k.(*ecdsaPrivateKey).privKey + if !elliptic.P256().IsOnCurve(ecdsaKey.X, ecdsaKey.Y) { + t.Fatal("P256 generated key in invalid. The public key must be on the P256 curve.") + } + if elliptic.P256() != ecdsaKey.Curve { + t.Fatal("P256 generated key in invalid. The curve must be P256.") + } + if ecdsaKey.D.Cmp(big.NewInt(0)) == 0 { + t.Fatal("P256 generated key in invalid. Private key must be different from 0.") + } + + // Curve P384 + k, err = currentBCCSP.KeyGen(&bccsp.ECDSAP384KeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA P384 key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating ECDSA P384 key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating ECDSA P384 key. Key should be private") + } + if k.Symmetric() { + t.Fatal("Failed generating ECDSA P384 key. Key should be asymmetric") + } + + ecdsaKey = k.(*ecdsaPrivateKey).privKey + if !elliptic.P384().IsOnCurve(ecdsaKey.X, ecdsaKey.Y) { + t.Fatal("P256 generated key in invalid. The public key must be on the P384 curve.") + } + if elliptic.P384() != ecdsaKey.Curve { + t.Fatal("P256 generated key in invalid. The curve must be P384.") + } + if ecdsaKey.D.Cmp(big.NewInt(0)) == 0 { + t.Fatal("P256 generated key in invalid. Private key must be different from 0.") + } + +} + +func TestKeyGenRSAOpts(t *testing.T) { + // 1024 + k, err := currentBCCSP.KeyGen(&bccsp.RSA1024KeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA 1024 key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating RSA 1024 key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating RSA 1024 key. Key should be private") + } + if k.Symmetric() { + t.Fatal("Failed generating RSA 1024 key. Key should be asymmetric") + } + + rsaKey := k.(*rsaPrivateKey).privKey + if rsaKey.N.BitLen() != 1024 { + t.Fatal("1024 RSA generated key in invalid. Modulus be of length 1024.") + } + if rsaKey.D.Cmp(big.NewInt(0)) == 0 { + t.Fatal("1024 RSA generated key in invalid. Private key must be different from 0.") + } + if rsaKey.E < 3 { + t.Fatal("1024 RSA generated key in invalid. Private key must be different from 0.") + } + + // 2048 + k, err = currentBCCSP.KeyGen(&bccsp.RSA2048KeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA 2048 key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating RSA 2048 key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating RSA 2048 key. Key should be private") + } + if k.Symmetric() { + t.Fatal("Failed generating RSA 2048 key. Key should be asymmetric") + } + + rsaKey = k.(*rsaPrivateKey).privKey + if rsaKey.N.BitLen() != 2048 { + t.Fatal("2048 RSA generated key in invalid. Modulus be of length 2048.") + } + if rsaKey.D.Cmp(big.NewInt(0)) == 0 { + t.Fatal("2048 RSA generated key in invalid. Private key must be different from 0.") + } + if rsaKey.E < 3 { + t.Fatal("2048 RSA generated key in invalid. Private key must be different from 0.") + } + + /* + // Skipping these tests because they take too much time to run. + // 3072 + k, err = currentBCCSP.KeyGen(&bccsp.RSA3072KeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA 3072 key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating RSA 3072 key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating RSA 3072 key. Key should be private") + } + if k.Symmetric() { + t.Fatal("Failed generating RSA 3072 key. Key should be asymmetric") + } + + rsaKey = k.(*rsaPrivateKey).privKey + if rsaKey.N.BitLen() != 3072 { + t.Fatal("3072 RSA generated key in invalid. Modulus be of length 3072.") + } + if rsaKey.D.Cmp(big.NewInt(0)) == 0 { + t.Fatal("3072 RSA generated key in invalid. Private key must be different from 0.") + } + if rsaKey.E < 3 { + t.Fatal("3072 RSA generated key in invalid. Private key must be different from 0.") + } + + // 4096 + k, err = currentBCCSP.KeyGen(&bccsp.RSA4096KeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA 4096 key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating RSA 4096 key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating RSA 4096 key. Key should be private") + } + if k.Symmetric() { + t.Fatal("Failed generating RSA 4096 key. Key should be asymmetric") + } + + rsaKey = k.(*rsaPrivateKey).privKey + if rsaKey.N.BitLen() != 4096 { + t.Fatal("4096 RSA generated key in invalid. Modulus be of length 4096.") + } + if rsaKey.D.Cmp(big.NewInt(0)) == 0 { + t.Fatal("4096 RSA generated key in invalid. Private key must be different from 0.") + } + if rsaKey.E < 3 { + t.Fatal("4096 RSA generated key in invalid. Private key must be different from 0.") + } + */ +} + +func TestKeyGenAESOpts(t *testing.T) { + // AES 128 + k, err := currentBCCSP.KeyGen(&bccsp.AES128KeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating AES 128 key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating AES 128 key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating AES 128 key. Key should be private") + } + if !k.Symmetric() { + t.Fatal("Failed generating AES 128 key. Key should be symmetric") + } + + aesKey := k.(*aesPrivateKey).privKey + if len(aesKey) != 16 { + t.Fatal("AES Key generated key in invalid. The key must have length 16.") + } + + // AES 192 + k, err = currentBCCSP.KeyGen(&bccsp.AES192KeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating AES 192 key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating AES 192 key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating AES 192 key. Key should be private") + } + if !k.Symmetric() { + t.Fatal("Failed generating AES 192 key. Key should be symmetric") + } + + aesKey = k.(*aesPrivateKey).privKey + if len(aesKey) != 24 { + t.Fatal("AES Key generated key in invalid. The key must have length 16.") + } + + // AES 256 + k, err = currentBCCSP.KeyGen(&bccsp.AES256KeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating AES 256 key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating AES 256 key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating AES 256 key. Key should be private") + } + if !k.Symmetric() { + t.Fatal("Failed generating AES 256 key. Key should be symmetric") + } + + aesKey = k.(*aesPrivateKey).privKey + if len(aesKey) != 32 { + t.Fatal("AES Key generated key in invalid. The key must have length 16.") + } +} + +func TestHashOpts(t *testing.T) { + msg := []byte("abcd") + + // SHA256 + digest1, err := currentBCCSP.Hash(msg, &bccsp.SHA256Opts{}) + if err != nil { + t.Fatalf("Failed computing SHA256 [%s]", err) + } + + h := sha256.New() + h.Write(msg) + digest2 := h.Sum(nil) + + if !bytes.Equal(digest1, digest2) { + t.Fatalf("Different SHA256 computed. [%x][%x]", digest1, digest2) + } + + // SHA384 + digest1, err = currentBCCSP.Hash(msg, &bccsp.SHA384Opts{}) + if err != nil { + t.Fatalf("Failed computing SHA384 [%s]", err) + } + + h = sha512.New384() + h.Write(msg) + digest2 = h.Sum(nil) + + if !bytes.Equal(digest1, digest2) { + t.Fatalf("Different SHA384 computed. [%x][%x]", digest1, digest2) + } + + // SHA3_256O + digest1, err = currentBCCSP.Hash(msg, &bccsp.SHA3_256Opts{}) + if err != nil { + t.Fatalf("Failed computing SHA3_256 [%s]", err) + } + + h = sha3.New256() + h.Write(msg) + digest2 = h.Sum(nil) + + if !bytes.Equal(digest1, digest2) { + t.Fatalf("Different SHA3_256 computed. [%x][%x]", digest1, digest2) + } + + // SHA3_384 + digest1, err = currentBCCSP.Hash(msg, &bccsp.SHA3_384Opts{}) + if err != nil { + t.Fatalf("Failed computing SHA3_384 [%s]", err) + } + + h = sha3.New384() + h.Write(msg) + digest2 = h.Sum(nil) + + if !bytes.Equal(digest1, digest2) { + t.Fatalf("Different SHA3_384 computed. [%x][%x]", digest1, digest2) + } +} + +func TestECDSAKeyGenEphemeral(t *testing.T) { + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: true}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating ECDSA key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating ECDSA key. Key should be private") + } + if k.Symmetric() { + t.Fatal("Failed generating ECDSA key. Key should be asymmetric") + } + raw, err := k.Bytes() + if err == nil { + t.Fatal("Failed marshalling to bytes. Marshalling must fail.") + } + if len(raw) != 0 { + t.Fatal("Failed marshalling to bytes. Output should be 0 bytes") + } + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting corresponding public key [%s]", err) + } + if pk == nil { + t.Fatal("Public key must be different from nil.") + } +} + +func TestECDSAPrivateKeySKI(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + ski := k.SKI() + if len(ski) == 0 { + t.Fatal("SKI not valid. Zero length.") + } +} + +func TestECDSAKeyGenNonEphemeral(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating ECDSA key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating ECDSA key. Key should be private") + } + if k.Symmetric() { + t.Fatal("Failed generating ECDSA key. Key should be asymmetric") + } +} + +func TestECDSAGetKeyBySKI(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + k2, err := currentBCCSP.GetKey(k.SKI()) + if err != nil { + t.Fatalf("Failed getting ECDSA key [%s]", err) + } + if k2 == nil { + t.Fatal("Failed getting ECDSA key. Key must be different from nil") + } + if !k2.Private() { + t.Fatal("Failed getting ECDSA key. Key should be private") + } + if k2.Symmetric() { + t.Fatal("Failed getting ECDSA key. Key should be asymmetric") + } + + // Check that the SKIs are the same + if !bytes.Equal(k.SKI(), k2.SKI()) { + t.Fatalf("SKIs are different [%x]!=[%x]", k.SKI(), k2.SKI()) + } +} + +func TestECDSAPublicKeyFromPrivateKey(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting public key from private ECDSA key [%s]", err) + } + if pk == nil { + t.Fatal("Failed getting public key from private ECDSA key. Key must be different from nil") + } + if pk.Private() { + t.Fatal("Failed generating ECDSA key. Key should be public") + } + if pk.Symmetric() { + t.Fatal("Failed generating ECDSA key. Key should be asymmetric") + } +} + +func TestECDSAPublicKeyBytes(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting public key from private ECDSA key [%s]", err) + } + + raw, err := pk.Bytes() + if err != nil { + t.Fatalf("Failed marshalling ECDSA public key [%s]", err) + } + if len(raw) == 0 { + t.Fatal("Failed marshalling ECDSA public key. Zero length") + } +} + +func TestECDSAPublicKeySKI(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting public key from private ECDSA key [%s]", err) + } + + ski := pk.SKI() + if len(ski) == 0 { + t.Fatal("SKI not valid. Zero length.") + } +} + +func TestECDSAKeyReRand(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + reRandomizedKey, err := currentBCCSP.KeyDeriv(k, &bccsp.ECDSAReRandKeyOpts{Temporary: false, Expansion: []byte{1}}) + if err != nil { + t.Fatalf("Failed re-randomizing ECDSA key [%s]", err) + } + if k == nil { + t.Fatal("Failed re-randomizing ECDSA key. Re-randomized Key must be different from nil") + } + if !reRandomizedKey.Private() { + t.Fatal("Failed re-randomizing ECDSA key. Re-randomized Key should be private") + } + if reRandomizedKey.Symmetric() { + t.Fatal("Failed re-randomizing ECDSA key. Re-randomized Key should be asymmetric") + } +} + +func TestECDSASign(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + msg := []byte("Hello World") + + digest, err := currentBCCSP.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := currentBCCSP.Sign(k, digest, nil) + if err != nil { + t.Fatalf("Failed generating ECDSA signature [%s]", err) + } + if len(signature) == 0 { + t.Fatal("Failed generating ECDSA key. Signature must be different from nil") + } +} + +func TestECDSAVerify(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + msg := []byte("Hello World") + + digest, err := currentBCCSP.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := currentBCCSP.Sign(k, digest, nil) + if err != nil { + t.Fatalf("Failed generating ECDSA signature [%s]", err) + } + + valid, err := currentBCCSP.Verify(k, signature, digest, nil) + if err != nil { + t.Fatalf("Failed verifying ECDSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying ECDSA signature. Signature not valid.") + } + + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting corresponding public key [%s]", err) + } + + valid, err = currentBCCSP.Verify(pk, signature, digest, nil) + if err != nil { + t.Fatalf("Failed verifying ECDSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying ECDSA signature. Signature not valid.") + } + + // Store public key + err = currentKS.StoreKey(pk) + if err != nil { + t.Fatalf("Failed storing corresponding public key [%s]", err) + } + + pk2, err := currentKS.GetKey(pk.SKI()) + if err != nil { + t.Fatalf("Failed retrieving corresponding public key [%s]", err) + } + + valid, err = currentBCCSP.Verify(pk2, signature, digest, nil) + if err != nil { + t.Fatalf("Failed verifying ECDSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying ECDSA signature. Signature not valid.") + } +} + +func TestECDSAKeyDeriv(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + reRandomizedKey, err := currentBCCSP.KeyDeriv(k, &bccsp.ECDSAReRandKeyOpts{Temporary: false, Expansion: []byte{1}}) + if err != nil { + t.Fatalf("Failed re-randomizing ECDSA key [%s]", err) + } + + msg := []byte("Hello World") + + digest, err := currentBCCSP.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := currentBCCSP.Sign(reRandomizedKey, digest, nil) + if err != nil { + t.Fatalf("Failed generating ECDSA signature [%s]", err) + } + + valid, err := currentBCCSP.Verify(reRandomizedKey, signature, digest, nil) + if err != nil { + t.Fatalf("Failed verifying ECDSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying ECDSA signature. Signature not valid.") + } +} + +func TestECDSAKeyImportFromExportedKey(t *testing.T) { + + // Generate an ECDSA key + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + // Export the public key + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting ECDSA public key [%s]", err) + } + + pkRaw, err := pk.Bytes() + if err != nil { + t.Fatalf("Failed getting ECDSA raw public key [%s]", err) + } + + // Import the exported public key + pk2, err := currentBCCSP.KeyImport(pkRaw, &bccsp.ECDSAPKIXPublicKeyImportOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed importing ECDSA public key [%s]", err) + } + if pk2 == nil { + t.Fatal("Failed importing ECDSA public key. Return BCCSP key cannot be nil.") + } + + // Sign and verify with the imported public key + msg := []byte("Hello World") + + digest, err := currentBCCSP.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := currentBCCSP.Sign(k, digest, nil) + if err != nil { + t.Fatalf("Failed generating ECDSA signature [%s]", err) + } + + valid, err := currentBCCSP.Verify(pk2, signature, digest, nil) + if err != nil { + t.Fatalf("Failed verifying ECDSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying ECDSA signature. Signature not valid.") + } +} + +func TestECDSAKeyImportFromECDSAPublicKey(t *testing.T) { + + // Generate an ECDSA key + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + // Export the public key + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting ECDSA public key [%s]", err) + } + + pkRaw, err := pk.Bytes() + if err != nil { + t.Fatalf("Failed getting ECDSA raw public key [%s]", err) + } + + pub, err := utils.DERToPublicKey(pkRaw) + if err != nil { + t.Fatalf("Failed converting raw to ecdsa.PublicKey [%s]", err) + } + + // Import the ecdsa.PublicKey + pk2, err := currentBCCSP.KeyImport(pub, &bccsp.ECDSAGoPublicKeyImportOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed importing ECDSA public key [%s]", err) + } + if pk2 == nil { + t.Fatal("Failed importing ECDSA public key. Return BCCSP key cannot be nil.") + } + + // Sign and verify with the imported public key + msg := []byte("Hello World") + + digest, err := currentBCCSP.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := currentBCCSP.Sign(k, digest, nil) + if err != nil { + t.Fatalf("Failed generating ECDSA signature [%s]", err) + } + + valid, err := currentBCCSP.Verify(pk2, signature, digest, nil) + if err != nil { + t.Fatalf("Failed verifying ECDSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying ECDSA signature. Signature not valid.") + } +} + +func TestECDSAKeyImportFromECDSAPrivateKey(t *testing.T) { + + // Generate an ECDSA key, default is P256 + key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + // Import the ecdsa.PrivateKey + priv, err := utils.PrivateKeyToDER(key) + if err != nil { + t.Fatalf("Failed converting raw to ecdsa.PrivateKey [%s]", err) + } + + sk, err := currentBCCSP.KeyImport(priv, &bccsp.ECDSAPrivateKeyImportOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed importing ECDSA private key [%s]", err) + } + if sk == nil { + t.Fatal("Failed importing ECDSA private key. Return BCCSP key cannot be nil.") + } + + // Import the ecdsa.PublicKey + pub, err := utils.PublicKeyToDER(&key.PublicKey) + if err != nil { + t.Fatalf("Failed converting raw to ecdsa.PublicKey [%s]", err) + } + + pk, err := currentBCCSP.KeyImport(pub, &bccsp.ECDSAPKIXPublicKeyImportOpts{Temporary: false}) + + if err != nil { + t.Fatalf("Failed importing ECDSA public key [%s]", err) + } + if pk == nil { + t.Fatal("Failed importing ECDSA public key. Return BCCSP key cannot be nil.") + } + + // Sign and verify with the imported public key + msg := []byte("Hello World") + + digest, err := currentBCCSP.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := currentBCCSP.Sign(sk, digest, nil) + if err != nil { + t.Fatalf("Failed generating ECDSA signature [%s]", err) + } + + valid, err := currentBCCSP.Verify(pk, signature, digest, nil) + if err != nil { + t.Fatalf("Failed verifying ECDSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying ECDSA signature. Signature not valid.") + } +} + +func TestKeyImportFromX509ECDSAPublicKey(t *testing.T) { + + // Generate an ECDSA key + k, err := currentBCCSP.KeyGen(&bccsp.ECDSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating ECDSA key [%s]", err) + } + + // Generate a self-signed certificate + testExtKeyUsage := []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth} + testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{2, 59, 1}} + extraExtensionData := []byte("extra extension") + commonName := "test.example.com" + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: commonName, + Organization: []string{"Σ Acme Co"}, + Country: []string{"US"}, + ExtraNames: []pkix.AttributeTypeAndValue{ + { + Type: []int{2, 5, 4, 42}, + Value: "Gopher", + }, + // This should override the Country, above. + { + Type: []int{2, 5, 4, 6}, + Value: "NL", + }, + }, + }, + NotBefore: time.Now().Add(-1 * time.Hour), + NotAfter: time.Now().Add(1 * time.Hour), + + SignatureAlgorithm: x509.ECDSAWithSHA256, + + SubjectKeyId: []byte{1, 2, 3, 4}, + KeyUsage: x509.KeyUsageCertSign, + + ExtKeyUsage: testExtKeyUsage, + UnknownExtKeyUsage: testUnknownExtKeyUsage, + + BasicConstraintsValid: true, + IsCA: true, + + OCSPServer: []string{"http://ocurrentBCCSP.example.com"}, + IssuingCertificateURL: []string{"http://crt.example.com/ca1.crt"}, + + DNSNames: []string{"test.example.com"}, + EmailAddresses: []string{"gopher@golang.org"}, + IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1).To4(), net.ParseIP("2001:4860:0:2001::68")}, + + PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}}, + PermittedDNSDomains: []string{".example.com", "example.com"}, + + CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"}, + + ExtraExtensions: []pkix.Extension{ + { + Id: []int{1, 2, 3, 4}, + Value: extraExtensionData, + }, + }, + } + + cryptoSigner := &signer.CryptoSigner{} + err = cryptoSigner.Init(currentBCCSP, k) + if err != nil { + t.Fatalf("Failed initializing CyrptoSigner [%s]", err) + } + + // Export the public key + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting ECDSA public key [%s]", err) + } + + pkRaw, err := pk.Bytes() + if err != nil { + t.Fatalf("Failed getting ECDSA raw public key [%s]", err) + } + + pub, err := utils.DERToPublicKey(pkRaw) + if err != nil { + t.Fatalf("Failed converting raw to ECDSA.PublicKey [%s]", err) + } + + certRaw, err := x509.CreateCertificate(rand.Reader, &template, &template, pub, cryptoSigner) + if err != nil { + t.Fatalf("Failed generating self-signed certificate [%s]", err) + } + + cert, err := utils.DERToX509Certificate(certRaw) + if err != nil { + t.Fatalf("Failed generating X509 certificate object from raw [%s]", err) + } + + // Import the certificate's public key + pk2, err := currentBCCSP.KeyImport(cert, &bccsp.X509PublicKeyImportOpts{Temporary: false}) + + if err != nil { + t.Fatalf("Failed importing ECDSA public key [%s]", err) + } + if pk2 == nil { + t.Fatal("Failed importing ECDSA public key. Return BCCSP key cannot be nil.") + } + + // Sign and verify with the imported public key + msg := []byte("Hello World") + + digest, err := currentBCCSP.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := currentBCCSP.Sign(k, digest, nil) + if err != nil { + t.Fatalf("Failed generating ECDSA signature [%s]", err) + } + + valid, err := currentBCCSP.Verify(pk2, signature, digest, nil) + if err != nil { + t.Fatalf("Failed verifying ECDSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying ECDSA signature. Signature not valid.") + } +} + +func TestAESKeyGen(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.AESKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating AES_256 key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating AES_256 key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating AES_256 key. Key should be private") + } + if !k.Symmetric() { + t.Fatal("Failed generating AES_256 key. Key should be symmetric") + } + + pk, err := k.PublicKey() + if err == nil { + t.Fatal("Error should be different from nil in this case") + } + if pk != nil { + t.Fatal("Return value should be equal to nil in this case") + } +} + +func TestAESEncrypt(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.AESKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating AES_256 key [%s]", err) + } + + ct, err := currentBCCSP.Encrypt(k, []byte("Hello World"), &bccsp.AESCBCPKCS7ModeOpts{}) + if err != nil { + t.Fatalf("Failed encrypting [%s]", err) + } + if len(ct) == 0 { + t.Fatal("Failed encrypting. Nil ciphertext") + } +} + +func TestAESDecrypt(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.AESKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating AES_256 key [%s]", err) + } + + msg := []byte("Hello World") + + ct, err := currentBCCSP.Encrypt(k, msg, &bccsp.AESCBCPKCS7ModeOpts{}) + if err != nil { + t.Fatalf("Failed encrypting [%s]", err) + } + + pt, err := currentBCCSP.Decrypt(k, ct, bccsp.AESCBCPKCS7ModeOpts{}) + if err != nil { + t.Fatalf("Failed decrypting [%s]", err) + } + if len(ct) == 0 { + t.Fatal("Failed decrypting. Nil plaintext") + } + + if !bytes.Equal(msg, pt) { + t.Fatalf("Failed decrypting. Decrypted plaintext is different from the original. [%x][%x]", msg, pt) + } +} + +func TestHMACTruncated256KeyDerivOverAES256Key(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.AESKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating AES_256 key [%s]", err) + } + + hmcaedKey, err := currentBCCSP.KeyDeriv(k, &bccsp.HMACTruncated256AESDeriveKeyOpts{Temporary: false, Arg: []byte{1}}) + if err != nil { + t.Fatalf("Failed HMACing AES_256 key [%s]", err) + } + if k == nil { + t.Fatal("Failed HMACing AES_256 key. HMACed Key must be different from nil") + } + if !hmcaedKey.Private() { + t.Fatal("Failed HMACing AES_256 key. HMACed Key should be private") + } + if !hmcaedKey.Symmetric() { + t.Fatal("Failed HMACing AES_256 key. HMACed Key should be asymmetric") + } + raw, err := hmcaedKey.Bytes() + if err == nil { + t.Fatal("Failed marshalling to bytes. Operation must be forbidden") + } + if len(raw) != 0 { + t.Fatal("Failed marshalling to bytes. Operation must return 0 bytes") + } + + msg := []byte("Hello World") + + ct, err := currentBCCSP.Encrypt(hmcaedKey, msg, &bccsp.AESCBCPKCS7ModeOpts{}) + if err != nil { + t.Fatalf("Failed encrypting [%s]", err) + } + + pt, err := currentBCCSP.Decrypt(hmcaedKey, ct, bccsp.AESCBCPKCS7ModeOpts{}) + if err != nil { + t.Fatalf("Failed decrypting [%s]", err) + } + if len(ct) == 0 { + t.Fatal("Failed decrypting. Nil plaintext") + } + + if !bytes.Equal(msg, pt) { + t.Fatalf("Failed decrypting. Decrypted plaintext is different from the original. [%x][%x]", msg, pt) + } + +} + +func TestHMACKeyDerivOverAES256Key(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.AESKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating AES_256 key [%s]", err) + } + + hmcaedKey, err := currentBCCSP.KeyDeriv(k, &bccsp.HMACDeriveKeyOpts{Temporary: false, Arg: []byte{1}}) + + if err != nil { + t.Fatalf("Failed HMACing AES_256 key [%s]", err) + } + if k == nil { + t.Fatal("Failed HMACing AES_256 key. HMACed Key must be different from nil") + } + if !hmcaedKey.Private() { + t.Fatal("Failed HMACing AES_256 key. HMACed Key should be private") + } + if !hmcaedKey.Symmetric() { + t.Fatal("Failed HMACing AES_256 key. HMACed Key should be asymmetric") + } + raw, err := hmcaedKey.Bytes() + if err != nil { + t.Fatalf("Failed marshalling to bytes [%s]", err) + } + if len(raw) == 0 { + t.Fatal("Failed marshalling to bytes. 0 bytes") + } +} + +func TestAES256KeyImport(t *testing.T) { + + raw, err := GetRandomBytes(32) + if err != nil { + t.Fatalf("Failed generating AES key [%s]", err) + } + + k, err := currentBCCSP.KeyImport(raw, &bccsp.AES256ImportKeyOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed importing AES_256 key [%s]", err) + } + if k == nil { + t.Fatal("Failed importing AES_256 key. Imported Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed HMACing AES_256 key. Imported Key should be private") + } + if !k.Symmetric() { + t.Fatal("Failed HMACing AES_256 key. Imported Key should be asymmetric") + } + raw, err = k.Bytes() + if err == nil { + t.Fatal("Failed marshalling to bytes. Marshalling must fail.") + } + if len(raw) != 0 { + t.Fatal("Failed marshalling to bytes. Output should be 0 bytes") + } + + msg := []byte("Hello World") + + ct, err := currentBCCSP.Encrypt(k, msg, &bccsp.AESCBCPKCS7ModeOpts{}) + if err != nil { + t.Fatalf("Failed encrypting [%s]", err) + } + + pt, err := currentBCCSP.Decrypt(k, ct, bccsp.AESCBCPKCS7ModeOpts{}) + if err != nil { + t.Fatalf("Failed decrypting [%s]", err) + } + if len(ct) == 0 { + t.Fatal("Failed decrypting. Nil plaintext") + } + + if !bytes.Equal(msg, pt) { + t.Fatalf("Failed decrypting. Decrypted plaintext is different from the original. [%x][%x]", msg, pt) + } +} + +func TestAES256KeyImportBadPaths(t *testing.T) { + + _, err := currentBCCSP.KeyImport(nil, &bccsp.AES256ImportKeyOpts{Temporary: false}) + if err == nil { + t.Fatal("Failed importing key. Must fail on importing nil key") + } + + _, err = currentBCCSP.KeyImport([]byte{1}, &bccsp.AES256ImportKeyOpts{Temporary: false}) + if err == nil { + t.Fatal("Failed importing key. Must fail on importing a key with an invalid length") + } +} + +func TestAES256KeyGenSKI(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.AESKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating AES_256 key [%s]", err) + } + + k2, err := currentBCCSP.GetKey(k.SKI()) + if err != nil { + t.Fatalf("Failed getting AES_256 key [%s]", err) + } + if k2 == nil { + t.Fatal("Failed getting AES_256 key. Key must be different from nil") + } + if !k2.Private() { + t.Fatal("Failed getting AES_256 key. Key should be private") + } + if !k2.Symmetric() { + t.Fatal("Failed getting AES_256 key. Key should be symmetric") + } + + // Check that the SKIs are the same + if !bytes.Equal(k.SKI(), k2.SKI()) { + t.Fatalf("SKIs are different [%x]!=[%x]", k.SKI(), k2.SKI()) + } + +} + +func TestSHA(t *testing.T) { + + for i := 0; i < 100; i++ { + b, err := GetRandomBytes(i) + if err != nil { + t.Fatalf("Failed getting random bytes [%s]", err) + } + + h1, err := currentBCCSP.Hash(b, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing SHA [%s]", err) + } + + var h hash.Hash + switch currentTestConfig.hashFamily { + case "SHA2": + switch currentTestConfig.securityLevel { + case 256: + h = sha256.New() + case 384: + h = sha512.New384() + default: + t.Fatalf("Invalid security level [%d]", currentTestConfig.securityLevel) + } + case "SHA3": + switch currentTestConfig.securityLevel { + case 256: + h = sha3.New256() + case 384: + h = sha3.New384() + default: + t.Fatalf("Invalid security level [%d]", currentTestConfig.securityLevel) + } + default: + t.Fatalf("Invalid hash family [%s]", currentTestConfig.hashFamily) + } + + h.Write(b) + h2 := h.Sum(nil) + if !bytes.Equal(h1, h2) { + t.Fatalf("Discrempancy found in HASH result [%x], [%x]!=[%x]", b, h1, h2) + } + } +} + +func TestRSAKeyGenEphemeral(t *testing.T) { + k, err := currentBCCSP.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: true}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating RSA key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating RSA key. Key should be private") + } + if k.Symmetric() { + t.Fatal("Failed generating RSA key. Key should be asymmetric") + } + + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed generating RSA corresponding public key [%s]", err) + } + if pk == nil { + t.Fatal("PK must be diffrent from nil") + } + + b, err := k.Bytes() + if err == nil { + t.Fatal("Secret keys cannot be exported. It must fail in this case") + } + if len(b) != 0 { + t.Fatal("Secret keys cannot be exported. It must be nil") + } + +} + +func TestRSAPublicKeyInvalidBytes(t *testing.T) { + rsaKey := &rsaPublicKey{nil} + b, err := rsaKey.Bytes() + if err == nil { + t.Fatal("It must fail in this case") + } + if len(b) != 0 { + t.Fatal("It must be nil") + } +} + +func TestRSAPrivateKeySKI(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + ski := k.SKI() + if len(ski) == 0 { + t.Fatal("SKI not valid. Zero length.") + } +} + +func TestRSAKeyGenNonEphemeral(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + if k == nil { + t.Fatal("Failed generating RSA key. Key must be different from nil") + } + if !k.Private() { + t.Fatal("Failed generating RSA key. Key should be private") + } + if k.Symmetric() { + t.Fatal("Failed generating RSA key. Key should be asymmetric") + } +} + +func TestRSAGetKeyBySKI(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + k2, err := currentBCCSP.GetKey(k.SKI()) + if err != nil { + t.Fatalf("Failed getting RSA key [%s]", err) + } + if k2 == nil { + t.Fatal("Failed getting RSA key. Key must be different from nil") + } + if !k2.Private() { + t.Fatal("Failed getting RSA key. Key should be private") + } + if k2.Symmetric() { + t.Fatal("Failed getting RSA key. Key should be asymmetric") + } + + // Check that the SKIs are the same + if !bytes.Equal(k.SKI(), k2.SKI()) { + t.Fatalf("SKIs are different [%x]!=[%x]", k.SKI(), k2.SKI()) + } +} + +func TestRSAPublicKeyFromPrivateKey(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting public key from private RSA key [%s]", err) + } + if pk == nil { + t.Fatal("Failed getting public key from private RSA key. Key must be different from nil") + } + if pk.Private() { + t.Fatal("Failed generating RSA key. Key should be public") + } + if pk.Symmetric() { + t.Fatal("Failed generating RSA key. Key should be asymmetric") + } +} + +func TestRSAPublicKeyBytes(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting public key from private RSA key [%s]", err) + } + + raw, err := pk.Bytes() + if err != nil { + t.Fatalf("Failed marshalling RSA public key [%s]", err) + } + if len(raw) == 0 { + t.Fatal("Failed marshalling RSA public key. Zero length") + } +} + +func TestRSAPublicKeySKI(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting public key from private RSA key [%s]", err) + } + + ski := pk.SKI() + if len(ski) == 0 { + t.Fatal("SKI not valid. Zero length.") + } +} + +func TestRSASign(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + msg := []byte("Hello World") + + digest, err := currentBCCSP.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := currentBCCSP.Sign(k, digest, &rsa.PSSOptions{SaltLength: 32, Hash: getCryptoHashIndex(t)}) + if err != nil { + t.Fatalf("Failed generating RSA signature [%s]", err) + } + if len(signature) == 0 { + t.Fatal("Failed generating RSA key. Signature must be different from nil") + } +} + +func TestRSAVerify(t *testing.T) { + + k, err := currentBCCSP.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + msg := []byte("Hello World") + + digest, err := currentBCCSP.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := currentBCCSP.Sign(k, digest, &rsa.PSSOptions{SaltLength: 32, Hash: getCryptoHashIndex(t)}) + if err != nil { + t.Fatalf("Failed generating RSA signature [%s]", err) + } + + valid, err := currentBCCSP.Verify(k, signature, digest, &rsa.PSSOptions{SaltLength: 32, Hash: getCryptoHashIndex(t)}) + if err != nil { + t.Fatalf("Failed verifying RSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying RSA signature. Signature not valid.") + } + + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting corresponding public key [%s]", err) + } + + valid, err = currentBCCSP.Verify(pk, signature, digest, &rsa.PSSOptions{SaltLength: 32, Hash: getCryptoHashIndex(t)}) + if err != nil { + t.Fatalf("Failed verifying RSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying RSA signature. Signature not valid.") + } + + // Store public key + err = currentKS.StoreKey(pk) + if err != nil { + t.Fatalf("Failed storing corresponding public key [%s]", err) + } + + pk2, err := currentKS.GetKey(pk.SKI()) + if err != nil { + t.Fatalf("Failed retrieving corresponding public key [%s]", err) + } + + valid, err = currentBCCSP.Verify(pk2, signature, digest, &rsa.PSSOptions{SaltLength: 32, Hash: getCryptoHashIndex(t)}) + if err != nil { + t.Fatalf("Failed verifying RSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying RSA signature. Signature not valid.") + } + +} + +func TestRSAKeyImportFromRSAPublicKey(t *testing.T) { + + // Generate an RSA key + k, err := currentBCCSP.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + // Export the public key + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting RSA public key [%s]", err) + } + + pkRaw, err := pk.Bytes() + if err != nil { + t.Fatalf("Failed getting RSA raw public key [%s]", err) + } + + pub, err := utils.DERToPublicKey(pkRaw) + if err != nil { + t.Fatalf("Failed converting raw to RSA.PublicKey [%s]", err) + } + + // Import the RSA.PublicKey + pk2, err := currentBCCSP.KeyImport(pub, &bccsp.RSAGoPublicKeyImportOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed importing RSA public key [%s]", err) + } + if pk2 == nil { + t.Fatal("Failed importing RSA public key. Return BCCSP key cannot be nil.") + } + + // Sign and verify with the imported public key + msg := []byte("Hello World") + + digest, err := currentBCCSP.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := currentBCCSP.Sign(k, digest, &rsa.PSSOptions{SaltLength: 32, Hash: getCryptoHashIndex(t)}) + if err != nil { + t.Fatalf("Failed generating RSA signature [%s]", err) + } + + valid, err := currentBCCSP.Verify(pk2, signature, digest, &rsa.PSSOptions{SaltLength: 32, Hash: getCryptoHashIndex(t)}) + if err != nil { + t.Fatalf("Failed verifying RSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying RSA signature. Signature not valid.") + } +} + +func TestKeyImportFromX509RSAPublicKey(t *testing.T) { + + // Generate an RSA key + k, err := currentBCCSP.KeyGen(&bccsp.RSAKeyGenOpts{Temporary: false}) + if err != nil { + t.Fatalf("Failed generating RSA key [%s]", err) + } + + // Generate a self-signed certificate + testExtKeyUsage := []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth} + testUnknownExtKeyUsage := []asn1.ObjectIdentifier{[]int{1, 2, 3}, []int{2, 59, 1}} + extraExtensionData := []byte("extra extension") + commonName := "test.example.com" + template := x509.Certificate{ + SerialNumber: big.NewInt(1), + Subject: pkix.Name{ + CommonName: commonName, + Organization: []string{"Σ Acme Co"}, + Country: []string{"US"}, + ExtraNames: []pkix.AttributeTypeAndValue{ + { + Type: []int{2, 5, 4, 42}, + Value: "Gopher", + }, + // This should override the Country, above. + { + Type: []int{2, 5, 4, 6}, + Value: "NL", + }, + }, + }, + NotBefore: time.Now().Add(-1 * time.Hour), + NotAfter: time.Now().Add(1 * time.Hour), + + SignatureAlgorithm: x509.SHA256WithRSA, + + SubjectKeyId: []byte{1, 2, 3, 4}, + KeyUsage: x509.KeyUsageCertSign, + + ExtKeyUsage: testExtKeyUsage, + UnknownExtKeyUsage: testUnknownExtKeyUsage, + + BasicConstraintsValid: true, + IsCA: true, + + OCSPServer: []string{"http://ocurrentBCCSP.example.com"}, + IssuingCertificateURL: []string{"http://crt.example.com/ca1.crt"}, + + DNSNames: []string{"test.example.com"}, + EmailAddresses: []string{"gopher@golang.org"}, + IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1).To4(), net.ParseIP("2001:4860:0:2001::68")}, + + PolicyIdentifiers: []asn1.ObjectIdentifier{[]int{1, 2, 3}}, + PermittedDNSDomains: []string{".example.com", "example.com"}, + + CRLDistributionPoints: []string{"http://crl1.example.com/ca1.crl", "http://crl2.example.com/ca1.crl"}, + + ExtraExtensions: []pkix.Extension{ + { + Id: []int{1, 2, 3, 4}, + Value: extraExtensionData, + }, + }, + } + + cryptoSigner := &signer.CryptoSigner{} + err = cryptoSigner.Init(currentBCCSP, k) + if err != nil { + t.Fatalf("Failed initializing CyrptoSigner [%s]", err) + } + + // Export the public key + pk, err := k.PublicKey() + if err != nil { + t.Fatalf("Failed getting RSA public key [%s]", err) + } + + pkRaw, err := pk.Bytes() + if err != nil { + t.Fatalf("Failed getting RSA raw public key [%s]", err) + } + + pub, err := utils.DERToPublicKey(pkRaw) + if err != nil { + t.Fatalf("Failed converting raw to RSA.PublicKey [%s]", err) + } + + certRaw, err := x509.CreateCertificate(rand.Reader, &template, &template, pub, cryptoSigner) + if err != nil { + t.Fatalf("Failed generating self-signed certificate [%s]", err) + } + + cert, err := utils.DERToX509Certificate(certRaw) + if err != nil { + t.Fatalf("Failed generating X509 certificate object from raw [%s]", err) + } + + // Import the certificate's public key + pk2, err := currentBCCSP.KeyImport(cert, &bccsp.X509PublicKeyImportOpts{Temporary: false}) + + if err != nil { + t.Fatalf("Failed importing RSA public key [%s]", err) + } + if pk2 == nil { + t.Fatal("Failed importing RSA public key. Return BCCSP key cannot be nil.") + } + + // Sign and verify with the imported public key + msg := []byte("Hello World") + + digest, err := currentBCCSP.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + signature, err := currentBCCSP.Sign(k, digest, &rsa.PSSOptions{SaltLength: 32, Hash: getCryptoHashIndex(t)}) + if err != nil { + t.Fatalf("Failed generating RSA signature [%s]", err) + } + + valid, err := currentBCCSP.Verify(pk2, signature, digest, &rsa.PSSOptions{SaltLength: 32, Hash: getCryptoHashIndex(t)}) + if err != nil { + t.Fatalf("Failed verifying RSA signature [%s]", err) + } + if !valid { + t.Fatal("Failed verifying RSA signature. Signature not valid.") + } +} + +func TestGetHashAndHashCompatibility(t *testing.T) { + + msg1 := []byte("abcd") + msg2 := []byte("efgh") + msg := []byte("abcdefgh") + + digest1, err := currentBCCSP.Hash(msg, &bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + digest2, err := currentBCCSP.Hash(msg, nil) + if err != nil { + t.Fatalf("Failed computing HASH [%s]", err) + } + + if !bytes.Equal(digest1, digest2) { + t.Fatalf("Different hash computed. [%x][%x]", digest1, digest2) + } + + h, err := currentBCCSP.GetHash(nil) + if err != nil { + t.Fatalf("Failed getting hash.Hash instance [%s]", err) + } + h.Write(msg1) + h.Write(msg2) + digest3 := h.Sum(nil) + + h2, err := currentBCCSP.GetHash(&bccsp.SHAOpts{}) + if err != nil { + t.Fatalf("Failed getting SHA hash.Hash instance [%s]", err) + } + h2.Write(msg1) + h2.Write(msg2) + digest4 := h2.Sum(nil) + + if !bytes.Equal(digest3, digest4) { + t.Fatalf("Different hash computed. [%x][%x]", digest3, digest4) + } + + if !bytes.Equal(digest1, digest3) { + t.Fatalf("Different hash computed. [%x][%x]", digest1, digest3) + } +} + +func getCryptoHashIndex(t *testing.T) crypto.Hash { + switch currentTestConfig.hashFamily { + case "SHA2": + switch currentTestConfig.securityLevel { + case 256: + return crypto.SHA256 + case 384: + return crypto.SHA384 + default: + t.Fatalf("Invalid security level [%d]", currentTestConfig.securityLevel) + } + case "SHA3": + switch currentTestConfig.securityLevel { + case 256: + return crypto.SHA3_256 + case 384: + return crypto.SHA3_384 + default: + t.Fatalf("Invalid security level [%d]", currentTestConfig.securityLevel) + } + default: + t.Fatalf("Invalid hash family [%s]", currentTestConfig.hashFamily) + } + + return crypto.SHA3_256 +} diff --git a/bccsp/pkcs11/rsakey.go b/bccsp/pkcs11/rsakey.go new file mode 100644 index 00000000000..c9c0e257059 --- /dev/null +++ b/bccsp/pkcs11/rsakey.go @@ -0,0 +1,136 @@ +/* +Copyright IBM Corp. 2016 All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package pkcs11 + +import ( + "crypto/rsa" + "crypto/x509" + "fmt" + + "crypto/sha256" + + "errors" + + "encoding/asn1" + "math/big" + + "github.com/hyperledger/fabric/bccsp" +) + +// rsaPublicKey reflects the ASN.1 structure of a PKCS#1 public key. +type rsaPublicKeyASN struct { + N *big.Int + E int +} + +type rsaPrivateKey struct { + privKey *rsa.PrivateKey +} + +// Bytes converts this key to its byte representation, +// if this operation is allowed. +func (k *rsaPrivateKey) Bytes() (raw []byte, err error) { + return nil, errors.New("Not supported.") +} + +// SKI returns the subject key identifier of this key. +func (k *rsaPrivateKey) SKI() (ski []byte) { + if k.privKey == nil { + return nil + } + + // Marshall the public key + raw, _ := asn1.Marshal(rsaPublicKeyASN{ + N: k.privKey.N, + E: k.privKey.E, + }) + + // Hash it + hash := sha256.New() + hash.Write(raw) + return hash.Sum(nil) +} + +// Symmetric returns true if this key is a symmetric key, +// false is this key is asymmetric +func (k *rsaPrivateKey) Symmetric() bool { + return false +} + +// Private returns true if this key is an asymmetric private key, +// false otherwise. +func (k *rsaPrivateKey) Private() bool { + return true +} + +// PublicKey returns the corresponding public key part of an asymmetric public/private key pair. +// This method returns an error in symmetric key schemes. +func (k *rsaPrivateKey) PublicKey() (bccsp.Key, error) { + return &rsaPublicKey{&k.privKey.PublicKey}, nil +} + +type rsaPublicKey struct { + pubKey *rsa.PublicKey +} + +// Bytes converts this key to its byte representation, +// if this operation is allowed. +func (k *rsaPublicKey) Bytes() (raw []byte, err error) { + if k.pubKey == nil { + return nil, errors.New("Failed marshalling key. Key is nil.") + } + raw, err = x509.MarshalPKIXPublicKey(k.pubKey) + if err != nil { + return nil, fmt.Errorf("Failed marshalling key [%s]", err) + } + return +} + +// SKI returns the subject key identifier of this key. +func (k *rsaPublicKey) SKI() (ski []byte) { + if k.pubKey == nil { + return nil + } + + // Marshall the public key + raw, _ := asn1.Marshal(rsaPublicKeyASN{ + N: k.pubKey.N, + E: k.pubKey.E, + }) + + // Hash it + hash := sha256.New() + hash.Write(raw) + return hash.Sum(nil) +} + +// Symmetric returns true if this key is a symmetric key, +// false is this key is asymmetric +func (k *rsaPublicKey) Symmetric() bool { + return false +} + +// Private returns true if this key is an asymmetric private key, +// false otherwise. +func (k *rsaPublicKey) Private() bool { + return false +} + +// PublicKey returns the corresponding public key part of an asymmetric public/private key pair. +// This method returns an error in symmetric key schemes. +func (k *rsaPublicKey) PublicKey() (bccsp.Key, error) { + return k, nil +}