From 51dc7610649f0a81e7ac997d10b11f5583ec14aa Mon Sep 17 00:00:00 2001 From: Angelo De Caro Date: Tue, 6 Dec 2016 14:11:29 +0100 Subject: [PATCH] BCCSP: Removing dependency to crypto/primitives package This change-set removes all the dependencies to the crypto/primitives package. The bccsp is now self-contained. It does not depend on any other fabric package. This change-set comes in the context of: https://jira.hyperledger.org/browse/FAB-354 Notice that no new methods have been introduced. Only required methods have been copied from the crypto/primitives package. Change-Id: I6be03eb4f345592ab1a89e544ec2be278cc7103c Signed-off-by: Angelo De Caro --- core/crypto/bccsp/signer/signer.go | 4 +- core/crypto/bccsp/sw/aes.go | 152 ++++++++++++ core/crypto/bccsp/sw/ecdsa.go | 23 ++ core/crypto/bccsp/sw/ecdsakey.go | 6 +- core/crypto/bccsp/sw/fileks.go | 16 +- core/crypto/bccsp/sw/impl.go | 26 +- core/crypto/bccsp/sw/impl_test.go | 22 +- core/crypto/bccsp/sw/rsakey.go | 4 +- core/crypto/bccsp/utils/errs.go | 26 ++ core/crypto/bccsp/utils/io.go | 69 ++++++ core/crypto/bccsp/utils/keys.go | 365 +++++++++++++++++++++++++++++ core/crypto/bccsp/utils/slice.go | 25 ++ core/crypto/bccsp/utils/x509.go | 26 ++ 13 files changed, 722 insertions(+), 42 deletions(-) create mode 100644 core/crypto/bccsp/sw/aes.go create mode 100644 core/crypto/bccsp/sw/ecdsa.go create mode 100644 core/crypto/bccsp/utils/errs.go create mode 100644 core/crypto/bccsp/utils/io.go create mode 100644 core/crypto/bccsp/utils/keys.go create mode 100644 core/crypto/bccsp/utils/slice.go create mode 100644 core/crypto/bccsp/utils/x509.go diff --git a/core/crypto/bccsp/signer/signer.go b/core/crypto/bccsp/signer/signer.go index ee252a36a6a..a9fef7e63de 100644 --- a/core/crypto/bccsp/signer/signer.go +++ b/core/crypto/bccsp/signer/signer.go @@ -22,7 +22,7 @@ import ( "io" "github.com/hyperledger/fabric/core/crypto/bccsp" - "github.com/hyperledger/fabric/core/crypto/primitives" + "github.com/hyperledger/fabric/core/crypto/bccsp/utils" ) // CryptoSigner is the BCCSP-based implementation of a crypto.Signer @@ -56,7 +56,7 @@ func (s *CryptoSigner) Init(csp bccsp.BCCSP, key bccsp.Key) error { return fmt.Errorf("Failed marshalling public key [%s]", err) } - pk, err := primitives.DERToPublicKey(raw) + pk, err := utils.DERToPublicKey(raw) if err != nil { return fmt.Errorf("Failed marshalling public key [%s]", err) } diff --git a/core/crypto/bccsp/sw/aes.go b/core/crypto/bccsp/sw/aes.go new file mode 100644 index 00000000000..968c62ff039 --- /dev/null +++ b/core/crypto/bccsp/sw/aes.go @@ -0,0 +1,152 @@ +/* +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. +*/ +/* +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 sw + +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/core/crypto/bccsp/sw/ecdsa.go b/core/crypto/bccsp/sw/ecdsa.go new file mode 100644 index 00000000000..723212dd570 --- /dev/null +++ b/core/crypto/bccsp/sw/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 sw + +import "math/big" + +// ECDSASignature represents an ECDSA signature +type ecdsaSignature struct { + R, S *big.Int +} diff --git a/core/crypto/bccsp/sw/ecdsakey.go b/core/crypto/bccsp/sw/ecdsakey.go index f75b56393c6..2bc70e8364b 100644 --- a/core/crypto/bccsp/sw/ecdsakey.go +++ b/core/crypto/bccsp/sw/ecdsakey.go @@ -25,7 +25,7 @@ import ( "errors" "github.com/hyperledger/fabric/core/crypto/bccsp" - "github.com/hyperledger/fabric/core/crypto/primitives" + "github.com/hyperledger/fabric/core/crypto/bccsp/utils" ) type ecdsaPrivateKey struct { @@ -40,7 +40,7 @@ func (k *ecdsaPrivateKey) Bytes() (raw []byte, err error) { // SKI returns the subject key identifier of this key. func (k *ecdsaPrivateKey) SKI() (ski []byte) { - raw, _ := primitives.PrivateKeyToDER(k.privKey) + raw, _ := utils.PrivateKeyToDER(k.privKey) // TODO: Error should not be thrown. Anyway, move the marshalling at initialization. hash := sha256.New() @@ -82,7 +82,7 @@ func (k *ecdsaPublicKey) Bytes() (raw []byte, err error) { // SKI returns the subject key identifier of this key. func (k *ecdsaPublicKey) SKI() (ski []byte) { - raw, _ := primitives.PublicKeyToPEM(k.pubKey, nil) + raw, _ := utils.PublicKeyToPEM(k.pubKey, nil) // TODO: Error should not be thrown. Anyway, move the marshalling at initialization. hash := sha256.New() diff --git a/core/crypto/bccsp/sw/fileks.go b/core/crypto/bccsp/sw/fileks.go index 887dc100ddb..29444df761d 100644 --- a/core/crypto/bccsp/sw/fileks.go +++ b/core/crypto/bccsp/sw/fileks.go @@ -20,8 +20,6 @@ import ( "os" "sync" - "github.com/hyperledger/fabric/core/crypto/utils" - "errors" "strings" @@ -32,7 +30,7 @@ import ( "path/filepath" "github.com/hyperledger/fabric/core/crypto/bccsp" - "github.com/hyperledger/fabric/core/crypto/primitives" + "github.com/hyperledger/fabric/core/crypto/bccsp/utils" ) // FileBasedKeyStore is a folder-based KeyStore. @@ -234,7 +232,7 @@ func (ks *FileBasedKeyStore) getSuffix(alias string) string { } func (ks *FileBasedKeyStore) storePrivateKey(alias string, privateKey interface{}) error { - rawKey, err := primitives.PrivateKeyToPEM(privateKey, ks.pwd) + rawKey, err := utils.PrivateKeyToPEM(privateKey, ks.pwd) if err != nil { logger.Errorf("Failed converting private key to PEM [%s]: [%s]", alias, err) return err @@ -250,7 +248,7 @@ func (ks *FileBasedKeyStore) storePrivateKey(alias string, privateKey interface{ } func (ks *FileBasedKeyStore) storePublicKey(alias string, publicKey interface{}) error { - rawKey, err := primitives.PublicKeyToPEM(publicKey, ks.pwd) + rawKey, err := utils.PublicKeyToPEM(publicKey, ks.pwd) if err != nil { logger.Errorf("Failed converting public key to PEM [%s]: [%s]", alias, err) return err @@ -266,7 +264,7 @@ func (ks *FileBasedKeyStore) storePublicKey(alias string, publicKey interface{}) } func (ks *FileBasedKeyStore) storeKey(alias string, key []byte) error { - pem, err := primitives.AEStoEncryptedPEM(key, ks.pwd) + pem, err := utils.AEStoEncryptedPEM(key, ks.pwd) if err != nil { logger.Errorf("Failed converting key to PEM [%s]: [%s]", alias, err) return err @@ -292,7 +290,7 @@ func (ks *FileBasedKeyStore) loadPrivateKey(alias string) (interface{}, error) { return nil, err } - privateKey, err := primitives.PEMtoPrivateKey(raw, ks.pwd) + privateKey, err := utils.PEMtoPrivateKey(raw, ks.pwd) if err != nil { logger.Errorf("Failed parsing private key [%s]: [%s].", alias, err.Error()) @@ -313,7 +311,7 @@ func (ks *FileBasedKeyStore) loadPublicKey(alias string) (interface{}, error) { return nil, err } - privateKey, err := primitives.PEMtoPublicKey(raw, ks.pwd) + privateKey, err := utils.PEMtoPublicKey(raw, ks.pwd) if err != nil { logger.Errorf("Failed parsing private key [%s]: [%s].", alias, err.Error()) @@ -334,7 +332,7 @@ func (ks *FileBasedKeyStore) loadKey(alias string) ([]byte, error) { return nil, err } - key, err := primitives.PEMtoAES(pem, ks.pwd) + key, err := utils.PEMtoAES(pem, ks.pwd) if err != nil { logger.Errorf("Failed parsing key [%s]: [%s]", alias, err) diff --git a/core/crypto/bccsp/sw/impl.go b/core/crypto/bccsp/sw/impl.go index 942467fd319..2e19357fd5f 100644 --- a/core/crypto/bccsp/sw/impl.go +++ b/core/crypto/bccsp/sw/impl.go @@ -36,8 +36,7 @@ import ( "crypto/sha512" "github.com/hyperledger/fabric/core/crypto/bccsp" - "github.com/hyperledger/fabric/core/crypto/primitives" - "github.com/hyperledger/fabric/core/crypto/utils" + "github.com/hyperledger/fabric/core/crypto/bccsp/utils" "github.com/op/go-logging" "golang.org/x/crypto/sha3" ) @@ -76,9 +75,6 @@ func New(securityLevel int, hashFamily string, keyStore bccsp.KeyStore) (bccsp.B } // SoftwareBasedBCCSP is the software-based implementation of the BCCSP. -// It uses util code in the primitives package but does not depend on the -// initialization of that package. -// It can be configured via viper. type impl struct { conf *config ks bccsp.KeyStore @@ -118,7 +114,7 @@ func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { k = &ecdsaPrivateKey{lowLevelKey} case *bccsp.AESKeyGenOpts: - lowLevelKey, err := primitives.GetRandomBytes(csp.conf.aesBitLength) + lowLevelKey, err := GetRandomBytes(csp.conf.aesBitLength) if err != nil { return nil, fmt.Errorf("Failed generating AES key [%s]", err) @@ -127,7 +123,7 @@ func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { k = &aesPrivateKey{lowLevelKey, false} case *bccsp.AES256KeyGenOpts: - lowLevelKey, err := primitives.GetRandomBytes(32) + lowLevelKey, err := GetRandomBytes(32) if err != nil { return nil, fmt.Errorf("Failed generating AES 256 key [%s]", err) @@ -136,7 +132,7 @@ func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { k = &aesPrivateKey{lowLevelKey, false} case *bccsp.AES192KeyGenOpts: - lowLevelKey, err := primitives.GetRandomBytes(24) + lowLevelKey, err := GetRandomBytes(24) if err != nil { return nil, fmt.Errorf("Failed generating AES 192 key [%s]", err) @@ -145,7 +141,7 @@ func (csp *impl) KeyGen(opts bccsp.KeyGenOpts) (k bccsp.Key, err error) { k = &aesPrivateKey{lowLevelKey, false} case *bccsp.AES128KeyGenOpts: - lowLevelKey, err := primitives.GetRandomBytes(16) + lowLevelKey, err := GetRandomBytes(16) if err != nil { return nil, fmt.Errorf("Failed generating AES 128 key [%s]", err) @@ -412,7 +408,7 @@ func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.K return nil, errors.New("[ECDSAPKIXPublicKeyImportOpts] Invalid raw. It must not be nil.") } - lowLevelKey, err := primitives.DERToPublicKey(der) + lowLevelKey, err := utils.DERToPublicKey(der) if err != nil { return nil, fmt.Errorf("Failed converting PKIX to ECDSA public key [%s]", err) } @@ -445,7 +441,7 @@ func (csp *impl) KeyImport(raw interface{}, opts bccsp.KeyImportOpts) (k bccsp.K return nil, errors.New("[ECDSADERPrivateKeyImportOpts] Invalid raw. It must not be nil.") } - lowLevelKey, err := primitives.DERToPrivateKey(der) + lowLevelKey, err := utils.DERToPrivateKey(der) if err != nil { return nil, fmt.Errorf("Failed converting PKIX to ECDSA public key [%s]", err) } @@ -629,7 +625,7 @@ func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.Signer // Check key type switch k.(type) { case *ecdsaPrivateKey: - ecdsaSignature := new(primitives.ECDSASignature) + ecdsaSignature := new(ecdsaSignature) _, err := asn1.Unmarshal(signature, ecdsaSignature) if err != nil { return false, fmt.Errorf("Failed unmashalling signature [%s]", err) @@ -637,7 +633,7 @@ func (csp *impl) Verify(k bccsp.Key, signature, digest []byte, opts bccsp.Signer return ecdsa.Verify(&(k.(*ecdsaPrivateKey).privKey.PublicKey), digest, ecdsaSignature.R, ecdsaSignature.S), nil case *ecdsaPublicKey: - ecdsaSignature := new(primitives.ECDSASignature) + ecdsaSignature := new(ecdsaSignature) _, err := asn1.Unmarshal(signature, ecdsaSignature) if err != nil { return false, fmt.Errorf("Failed unmashalling signature [%s]", err) @@ -692,7 +688,7 @@ func (csp *impl) Encrypt(k bccsp.Key, plaintext []byte, opts bccsp.EncrypterOpts switch opts.(type) { case *bccsp.AESCBCPKCS7ModeOpts, bccsp.AESCBCPKCS7ModeOpts: // AES in CBC mode with PKCS7 padding - return primitives.CBCPKCS7Encrypt(k.(*aesPrivateKey).privKey, plaintext) + return AESCBCPKCS7Encrypt(k.(*aesPrivateKey).privKey, plaintext) default: return nil, fmt.Errorf("Mode not recognized [%s]", opts) } @@ -716,7 +712,7 @@ func (csp *impl) Decrypt(k bccsp.Key, ciphertext []byte, opts bccsp.DecrypterOpt switch opts.(type) { case *bccsp.AESCBCPKCS7ModeOpts, bccsp.AESCBCPKCS7ModeOpts: // AES in CBC mode with PKCS7 padding - return primitives.CBCPKCS7Decrypt(k.(*aesPrivateKey).privKey, ciphertext) + return AESCBCPKCS7Decrypt(k.(*aesPrivateKey).privKey, ciphertext) default: return nil, fmt.Errorf("Mode not recognized [%s]", opts) } diff --git a/core/crypto/bccsp/sw/impl_test.go b/core/crypto/bccsp/sw/impl_test.go index 6feb13aa0d5..c1dbd94c3db 100644 --- a/core/crypto/bccsp/sw/impl_test.go +++ b/core/crypto/bccsp/sw/impl_test.go @@ -42,7 +42,7 @@ import ( "github.com/hyperledger/fabric/core/crypto/bccsp" "github.com/hyperledger/fabric/core/crypto/bccsp/signer" - "github.com/hyperledger/fabric/core/crypto/primitives" + "github.com/hyperledger/fabric/core/crypto/bccsp/utils" "golang.org/x/crypto/sha3" ) @@ -790,7 +790,7 @@ func TestECDSAKeyImportFromECDSAPublicKey(t *testing.T) { t.Fatalf("Failed getting ECDSA raw public key [%s]", err) } - pub, err := primitives.DERToPublicKey(pkRaw) + pub, err := utils.DERToPublicKey(pkRaw) if err != nil { t.Fatalf("Failed converting raw to ecdsa.PublicKey [%s]", err) } @@ -835,7 +835,7 @@ func TestECDSAKeyImportFromECDSAPrivateKey(t *testing.T) { } // Import the ecdsa.PrivateKey - priv, err := primitives.PrivateKeyToDER(key) + priv, err := utils.PrivateKeyToDER(key) if err != nil { t.Fatalf("Failed converting raw to ecdsa.PrivateKey [%s]", err) } @@ -849,7 +849,7 @@ func TestECDSAKeyImportFromECDSAPrivateKey(t *testing.T) { } // Import the ecdsa.PublicKey - pub, err := primitives.PublicKeyToDER(&key.PublicKey) + pub, err := utils.PublicKeyToDER(&key.PublicKey) if err != nil { t.Fatalf("Failed converting raw to ecdsa.PublicKey [%s]", err) } @@ -967,7 +967,7 @@ func TestKeyImportFromX509ECDSAPublicKey(t *testing.T) { t.Fatalf("Failed getting ECDSA raw public key [%s]", err) } - pub, err := primitives.DERToPublicKey(pkRaw) + pub, err := utils.DERToPublicKey(pkRaw) if err != nil { t.Fatalf("Failed converting raw to ECDSA.PublicKey [%s]", err) } @@ -977,7 +977,7 @@ func TestKeyImportFromX509ECDSAPublicKey(t *testing.T) { t.Fatalf("Failed generating self-signed certificate [%s]", err) } - cert, err := primitives.DERToX509Certificate(certRaw) + cert, err := utils.DERToX509Certificate(certRaw) if err != nil { t.Fatalf("Failed generating X509 certificate object from raw [%s]", err) } @@ -1163,7 +1163,7 @@ func TestHMACKeyDerivOverAES256Key(t *testing.T) { func TestAES256KeyImport(t *testing.T) { - raw, err := primitives.GetRandomBytes(32) + raw, err := GetRandomBytes(32) if err != nil { t.Fatalf("Failed generating AES key [%s]", err) } @@ -1253,7 +1253,7 @@ func TestAES256KeyGenSKI(t *testing.T) { func TestSHA(t *testing.T) { for i := 0; i < 100; i++ { - b, err := primitives.GetRandomBytes(i) + b, err := GetRandomBytes(i) if err != nil { t.Fatalf("Failed getting random bytes [%s]", err) } @@ -1560,7 +1560,7 @@ func TestRSAKeyImportFromRSAPublicKey(t *testing.T) { t.Fatalf("Failed getting RSA raw public key [%s]", err) } - pub, err := primitives.DERToPublicKey(pkRaw) + pub, err := utils.DERToPublicKey(pkRaw) if err != nil { t.Fatalf("Failed converting raw to RSA.PublicKey [%s]", err) } @@ -1678,7 +1678,7 @@ func TestKeyImportFromX509RSAPublicKey(t *testing.T) { t.Fatalf("Failed getting RSA raw public key [%s]", err) } - pub, err := primitives.DERToPublicKey(pkRaw) + pub, err := utils.DERToPublicKey(pkRaw) if err != nil { t.Fatalf("Failed converting raw to RSA.PublicKey [%s]", err) } @@ -1688,7 +1688,7 @@ func TestKeyImportFromX509RSAPublicKey(t *testing.T) { t.Fatalf("Failed generating self-signed certificate [%s]", err) } - cert, err := primitives.DERToX509Certificate(certRaw) + cert, err := utils.DERToX509Certificate(certRaw) if err != nil { t.Fatalf("Failed generating X509 certificate object from raw [%s]", err) } diff --git a/core/crypto/bccsp/sw/rsakey.go b/core/crypto/bccsp/sw/rsakey.go index 394ec935ecc..a56e28b60d8 100644 --- a/core/crypto/bccsp/sw/rsakey.go +++ b/core/crypto/bccsp/sw/rsakey.go @@ -25,7 +25,7 @@ import ( "errors" "github.com/hyperledger/fabric/core/crypto/bccsp" - "github.com/hyperledger/fabric/core/crypto/primitives" + "github.com/hyperledger/fabric/core/crypto/bccsp/utils" ) type rsaPrivateKey struct { @@ -88,7 +88,7 @@ func (k *rsaPublicKey) Bytes() (raw []byte, err error) { // SKI returns the subject key identifier of this key. func (k *rsaPublicKey) SKI() (ski []byte) { - raw, _ := primitives.PublicKeyToPEM(k.pubKey, nil) + raw, _ := utils.PublicKeyToPEM(k.pubKey, nil) // TODO: Error should not be thrown. Anyway, move the marshalling at initialization. hash := sha256.New() diff --git a/core/crypto/bccsp/utils/errs.go b/core/crypto/bccsp/utils/errs.go new file mode 100644 index 00000000000..3164d8759fd --- /dev/null +++ b/core/crypto/bccsp/utils/errs.go @@ -0,0 +1,26 @@ +/* +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 utils + +// ErrToString converts and error to a string. If the error is nil, it returns the string "" +func ErrToString(err error) string { + if err != nil { + return err.Error() + } + + return "" +} diff --git a/core/crypto/bccsp/utils/io.go b/core/crypto/bccsp/utils/io.go new file mode 100644 index 00000000000..bf61ce19570 --- /dev/null +++ b/core/crypto/bccsp/utils/io.go @@ -0,0 +1,69 @@ +/* +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 utils + +import ( + "io" + "os" +) + +// DirMissingOrEmpty checks is a directory is missin or empty +func DirMissingOrEmpty(path string) (bool, error) { + dirExists, err := DirExists(path) + if err != nil { + return false, err + } + if !dirExists { + return true, nil + } + + dirEmpty, err := DirEmpty(path) + if err != nil { + return false, err + } + if dirEmpty { + return true, nil + } + return false, nil +} + +// DirExists checks if a directory exists +func DirExists(path string) (bool, error) { + _, err := os.Stat(path) + if err == nil { + return true, nil + } + if os.IsNotExist(err) { + return false, nil + } + return false, err +} + +// DirEmpty checks if a directory is empty +func DirEmpty(path string) (bool, error) { + f, err := os.Open(path) + if err != nil { + return false, err + } + defer f.Close() + + _, err = f.Readdir(1) + if err == io.EOF { + return true, nil + } + return false, err +} diff --git a/core/crypto/bccsp/utils/keys.go b/core/crypto/bccsp/utils/keys.go new file mode 100644 index 00000000000..1629c3de01f --- /dev/null +++ b/core/crypto/bccsp/utils/keys.go @@ -0,0 +1,365 @@ +/* +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 utils + +import ( + "crypto/ecdsa" + "crypto/rand" + "crypto/rsa" + "crypto/x509" + "encoding/pem" + "errors" + "fmt" +) + +// PrivateKeyToDER marshals a private key to der +func PrivateKeyToDER(privateKey *ecdsa.PrivateKey) ([]byte, error) { + if privateKey == nil { + return nil, errors.New("Invalid ecdsa private key. It must be different from nil.") + } + + return x509.MarshalECPrivateKey(privateKey) +} + +// PrivateKeyToPEM converts a private key to PEM +func PrivateKeyToPEM(privateKey interface{}, pwd []byte) ([]byte, error) { + // Validate inputs + if len(pwd) != 0 { + return PrivateKeyToEncryptedPEM(privateKey, pwd) + } + + switch k := privateKey.(type) { + case *ecdsa.PrivateKey: + if k == nil { + return nil, errors.New("Invalid ecdsa private key. It must be different from nil.") + } + + raw, err := x509.MarshalECPrivateKey(k) + + if err != nil { + return nil, err + } + + return pem.EncodeToMemory( + &pem.Block{ + Type: "ECDSA PRIVATE KEY", + Bytes: raw, + }, + ), nil + case *rsa.PrivateKey: + if k == nil { + return nil, errors.New("Invalid rsa private key. It must be different from nil.") + } + + raw := x509.MarshalPKCS1PrivateKey(k) + + return pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PRIVATE KEY", + Bytes: raw, + }, + ), nil + default: + return nil, errors.New("Invalid key type. It must be *ecdsa.PrivateKey or *rsa.PrivateKey") + } +} + +// PrivateKeyToEncryptedPEM converts a private key to an encrypted PEM +func PrivateKeyToEncryptedPEM(privateKey interface{}, pwd []byte) ([]byte, error) { + switch k := privateKey.(type) { + case *ecdsa.PrivateKey: + if k == nil { + return nil, errors.New("Invalid ecdsa private key. It must be different from nil.") + } + + raw, err := x509.MarshalECPrivateKey(k) + + if err != nil { + return nil, err + } + + block, err := x509.EncryptPEMBlock( + rand.Reader, + "ECDSA PRIVATE KEY", + raw, + pwd, + x509.PEMCipherAES256) + + if err != nil { + return nil, err + } + + return pem.EncodeToMemory(block), nil + + default: + return nil, errors.New("Invalid key type. It must be *ecdsa.PrivateKey") + } +} + +// DERToPrivateKey unmarshals a der to private key +func DERToPrivateKey(der []byte) (key interface{}, err error) { + + if key, err = x509.ParsePKCS1PrivateKey(der); err == nil { + return key, nil + } + + if key, err = x509.ParsePKCS8PrivateKey(der); err == nil { + switch key.(type) { + case *rsa.PrivateKey, *ecdsa.PrivateKey: + return + default: + return nil, errors.New("Found unknown private key type in PKCS#8 wrapping") + } + } + + if key, err = x509.ParseECPrivateKey(der); err == nil { + return + } + + return nil, errors.New("Invalid key type. The DER must contain an rsa.PrivareKey or ecdsa.PrivateKey") +} + +// PEMtoPrivateKey unmarshals a pem to private key +func PEMtoPrivateKey(raw []byte, pwd []byte) (interface{}, error) { + if len(raw) == 0 { + return nil, errors.New("Invalid PEM. It must be diffrent from nil.") + } + block, _ := pem.Decode(raw) + if block == nil { + return nil, fmt.Errorf("Failed decoding PEM. Block must be diffrent from nil. [% x]", raw) + } + + // TODO: derive from header the type of the key + + if x509.IsEncryptedPEMBlock(block) { + if len(pwd) == 0 { + return nil, errors.New("Encrypted Key. Need a password") + } + + decrypted, err := x509.DecryptPEMBlock(block, pwd) + if err != nil { + return nil, fmt.Errorf("Failed PEM decryption [%s]", err) + } + + key, err := DERToPrivateKey(decrypted) + if err != nil { + return nil, err + } + return key, err + } + + cert, err := DERToPrivateKey(block.Bytes) + if err != nil { + return nil, err + } + return cert, err +} + +// PEMtoAES extracts from the PEM an AES key +func PEMtoAES(raw []byte, pwd []byte) ([]byte, error) { + if len(raw) == 0 { + return nil, errors.New("Invalid PEM. It must be diffrent from nil.") + } + block, _ := pem.Decode(raw) + if block == nil { + return nil, fmt.Errorf("Failed decoding PEM. Block must be different from nil. [% x]", raw) + } + + if x509.IsEncryptedPEMBlock(block) { + if len(pwd) == 0 { + return nil, errors.New("Encrypted Key. Password must be different fom nil") + } + + decrypted, err := x509.DecryptPEMBlock(block, pwd) + if err != nil { + return nil, fmt.Errorf("Failed PEM decryption. [%s]", err) + } + return decrypted, nil + } + + return block.Bytes, nil +} + +// AEStoPEM encapsulates an AES key in the PEM format +func AEStoPEM(raw []byte) []byte { + return pem.EncodeToMemory(&pem.Block{Type: "AES PRIVATE KEY", Bytes: raw}) +} + +// AEStoEncryptedPEM encapsulates an AES key in the encrypted PEM format +func AEStoEncryptedPEM(raw []byte, pwd []byte) ([]byte, error) { + if len(raw) == 0 { + return nil, errors.New("Invalid aes key. It must be different from nil") + } + if len(pwd) == 0 { + return AEStoPEM(raw), nil + } + + block, err := x509.EncryptPEMBlock( + rand.Reader, + "AES PRIVATE KEY", + raw, + pwd, + x509.PEMCipherAES256) + + if err != nil { + return nil, err + } + + return pem.EncodeToMemory(block), nil +} + +// PublicKeyToPEM marshals a public key to the pem format +func PublicKeyToPEM(publicKey interface{}, pwd []byte) ([]byte, error) { + if len(pwd) != 0 { + return PublicKeyToEncryptedPEM(publicKey, pwd) + } + + switch k := publicKey.(type) { + case *ecdsa.PublicKey: + if k == nil { + return nil, errors.New("Invalid ecdsa public key. It must be different from nil.") + } + + PubASN1, err := x509.MarshalPKIXPublicKey(k) + if err != nil { + return nil, err + } + + return pem.EncodeToMemory( + &pem.Block{ + Type: "ECDSA PUBLIC KEY", + Bytes: PubASN1, + }, + ), nil + case *rsa.PublicKey: + if k == nil { + return nil, errors.New("Invalid rsa public key. It must be different from nil.") + } + + PubASN1, err := x509.MarshalPKIXPublicKey(k) + if err != nil { + return nil, err + } + + return pem.EncodeToMemory( + &pem.Block{ + Type: "RSA PUBLIC KEY", + Bytes: PubASN1, + }, + ), nil + + default: + return nil, errors.New("Invalid key type. It must be *ecdsa.PublicKey or *rsa.PublicKey") + } +} + +// PublicKeyToDER marshals a public key to the der format +func PublicKeyToDER(publicKey interface{}) ([]byte, error) { + switch k := publicKey.(type) { + case *ecdsa.PublicKey: + if k == nil { + return nil, errors.New("Invalid ecdsa public key. It must be different from nil.") + } + + PubASN1, err := x509.MarshalPKIXPublicKey(k) + if err != nil { + return nil, err + } + + return PubASN1, nil + + default: + return nil, errors.New("Invalid key type. It must be *ecdsa.PublicKey") + } +} + +// PublicKeyToEncryptedPEM converts a public key to encrypted pem +func PublicKeyToEncryptedPEM(publicKey interface{}, pwd []byte) ([]byte, error) { + switch k := publicKey.(type) { + case *ecdsa.PublicKey: + if k == nil { + return nil, errors.New("Invalid ecdsa public key. It must be different from nil.") + } + raw, err := x509.MarshalPKIXPublicKey(k) + + if err != nil { + return nil, err + } + + block, err := x509.EncryptPEMBlock( + rand.Reader, + "ECDSA PUBLIC KEY", + raw, + pwd, + x509.PEMCipherAES256) + + if err != nil { + return nil, err + } + + return pem.EncodeToMemory(block), nil + + default: + return nil, errors.New("Invalid key type. It must be *ecdsa.PublicKey") + } +} + +// PEMtoPublicKey unmarshals a pem to public key +func PEMtoPublicKey(raw []byte, pwd []byte) (interface{}, error) { + if len(raw) == 0 { + return nil, errors.New("Invalid PEM. It must be diffrent from nil.") + } + block, _ := pem.Decode(raw) + if block == nil { + return nil, fmt.Errorf("Failed decoding. Block must be different from nil. [% x]", raw) + } + + // TODO: derive from header the type of the key + if x509.IsEncryptedPEMBlock(block) { + if len(pwd) == 0 { + return nil, errors.New("Encrypted Key. Password must be different from nil") + } + + decrypted, err := x509.DecryptPEMBlock(block, pwd) + if err != nil { + return nil, fmt.Errorf("Failed PEM decryption. [%s]", err) + } + + key, err := DERToPublicKey(decrypted) + if err != nil { + return nil, err + } + return key, err + } + + cert, err := DERToPublicKey(block.Bytes) + if err != nil { + return nil, err + } + return cert, err +} + +// DERToPublicKey unmarshals a der to public key +func DERToPublicKey(raw []byte) (pub interface{}, err error) { + if len(raw) == 0 { + return nil, errors.New("Invalid DER. It must be diffrent from nil.") + } + + key, err := x509.ParsePKIXPublicKey(raw) + + return key, err +} diff --git a/core/crypto/bccsp/utils/slice.go b/core/crypto/bccsp/utils/slice.go new file mode 100644 index 00000000000..59e5374c386 --- /dev/null +++ b/core/crypto/bccsp/utils/slice.go @@ -0,0 +1,25 @@ +/* +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 utils + +// Clone clones the passed slice +func Clone(src []byte) []byte { + clone := make([]byte, len(src)) + copy(clone, src) + + return clone +} diff --git a/core/crypto/bccsp/utils/x509.go b/core/crypto/bccsp/utils/x509.go new file mode 100644 index 00000000000..43b51ce867c --- /dev/null +++ b/core/crypto/bccsp/utils/x509.go @@ -0,0 +1,26 @@ +/* +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 utils + +import ( + "crypto/x509" +) + +// DERToX509Certificate converts der to x509 +func DERToX509Certificate(asn1Data []byte) (*x509.Certificate, error) { + return x509.ParseCertificate(asn1Data) +}