diff --git a/api/net.go b/api/net.go index 632644160..dcb7c9066 100644 --- a/api/net.go +++ b/api/net.go @@ -63,7 +63,7 @@ type GetTCertBatchRequestNet struct { GetTCertBatchRequest // KeySigs is an optional array of public keys and corresponding signatures. // If not set, the server generates it's own keys based on a key derivation function - // which cryptographically relates the TCert to an ECert. + // which cryptographically relates the TCerts to an ECert. KeySigs []KeySig `json:"key_sigs,omitempty"` } diff --git a/lib/identity.go b/lib/identity.go index 51c8ad76f..94bb30fb3 100644 --- a/lib/identity.go +++ b/lib/identity.go @@ -25,6 +25,8 @@ import ( "github.com/cloudflare/cfssl/signer" "github.com/hyperledger/fabric-ca/api" "github.com/hyperledger/fabric-ca/util" + "github.com/hyperledger/fabric/bccsp" + "github.com/hyperledger/fabric/bccsp/factory" ) func newIdentity(client *Client, name string, key []byte, cert []byte) *Identity { @@ -40,6 +42,7 @@ type Identity struct { name string ecert *Signer client *Client + CSP bccsp.BCCSP } // GetName returns the identity name @@ -190,10 +193,29 @@ func (i *Identity) addTokenAuthHdr(req *http.Request, body []byte) error { log.Debug("adding token-based authorization header") cert := i.ecert.cert key := i.ecert.key - token, err := util.CreateToken(cert, key, body) + if i.CSP == nil { + csp, error := getDefaultBCCSPInstance() + if error != nil { + return fmt.Errorf("Default BCCSP instance failed with error : %s", error) + } + i.CSP = csp + } + token, err := util.CreateToken(i.CSP, cert, key, body) if err != nil { return fmt.Errorf("Failed to add token authorization header: %s", err) } req.Header.Set("authorization", token) return nil } + +func getDefaultBCCSPInstance() (bccsp.BCCSP, error) { + defaultBccsp, bccspError := factory.GetDefault() + if bccspError != nil { + return nil, fmt.Errorf("BCCSP initialiazation failed with error : %s", bccspError) + } + if defaultBccsp == nil { + return nil, errors.New("Cannot get default instance of BCCSP") + } + + return defaultBccsp, nil +} diff --git a/lib/serverauth.go b/lib/serverauth.go index 03ec4cc7f..bdb4372ae 100644 --- a/lib/serverauth.go +++ b/lib/serverauth.go @@ -120,7 +120,7 @@ func (ah *fcaAuthHandler) serveHTTP(w http.ResponseWriter, r *http.Request) erro } r.Body = ioutil.NopCloser(bytes.NewReader(body)) // verify token - cert, err2 := util.VerifyToken(authHdr, body) + cert, err2 := util.VerifyToken(MyCSP, authHdr, body) if err2 != nil { log.Debugf("Failed to verify token: %s", err2) return authError diff --git a/lib/serverrevoke.go b/lib/serverrevoke.go index 2b2d1f0a6..becb9f850 100644 --- a/lib/serverrevoke.go +++ b/lib/serverrevoke.go @@ -59,7 +59,7 @@ func (h *revokeHandler) Handle(w http.ResponseWriter, r *http.Request) error { } r.Body.Close() - cert, err := util.VerifyToken(authHdr, body) + cert, err := util.VerifyToken(MyCSP, authHdr, body) if err != nil { return authErr(w, err) } diff --git a/testdata/ec-key.ski b/testdata/ec-key.ski new file mode 100644 index 000000000..6cc688c1b --- /dev/null +++ b/testdata/ec-key.ski @@ -0,0 +1,3 @@ +-----BEGIN BCCSP SKI----- +sLJGcSFzmXHJlmULJ9Ne8//jZlTKnS8dsZvbQu4i27c= +-----END BCCSP SKI----- diff --git a/util/util.go b/util/util.go index 8232e6811..0bff48813 100644 --- a/util/util.go +++ b/util/util.go @@ -18,13 +18,8 @@ package util import ( "bytes" - "crypto" "crypto/ecdsa" - "crypto/rand" - "crypto/rsa" - "crypto/sha512" "crypto/x509" - "encoding/asn1" "encoding/base64" "encoding/json" "encoding/pem" @@ -42,6 +37,7 @@ import ( "time" "github.com/cloudflare/cfssl/log" + "github.com/hyperledger/fabric/bccsp" "github.com/jmoiron/sqlx" "github.com/spf13/viper" ) @@ -156,7 +152,8 @@ func DERCertToPEM(der []byte) []byte { // @param cert The pem-encoded certificate // @param key The pem-encoded key // @param body The body of an HTTP request -func CreateToken(cert []byte, key []byte, body []byte) (string, error) { +func CreateToken(csp bccsp.BCCSP, cert []byte, key []byte, body []byte) (string, error) { + block, _ := pem.Decode(cert) if block == nil { return "", errors.New("Failed to PEM decode certificate") @@ -169,14 +166,17 @@ func CreateToken(cert []byte, key []byte, body []byte) (string, error) { var token string + //The RSA Key Gen is commented right now as there is bccsp does switch publicKey.(type) { - case *rsa.PublicKey: - token, err = GenRSAToken(cert, key, body) - if err != nil { - return "", err - } + /* + case *rsa.PublicKey: + token, err = GenRSAToken(csp, cert, key, body) + if err != nil { + return "", err + } + */ case *ecdsa.PublicKey: - token, err = GenECDSAToken(cert, key, body) + token, err = GenECDSAToken(csp, cert, key, body) if err != nil { return "", err } @@ -185,7 +185,9 @@ func CreateToken(cert []byte, key []byte, body []byte) (string, error) { } //GenRSAToken signs the http body and cert with RSA using RSA private key -func GenRSAToken(cert []byte, key []byte, body []byte) (string, error) { +// @csp : BCCSP instance +/* +func GenRSAToken(csp bccsp.BCCSP, cert []byte, key []byte, body []byte) (string, error) { privKey, err := GetRSAPrivateKey(key) if err != nil { return "", err @@ -203,30 +205,36 @@ func GenRSAToken(cert []byte, key []byte, body []byte) (string, error) { b64sig := B64Encode(RSAsignature) token := b64cert + "." + b64sig - return token, nil + return token, nil } +*/ //GenECDSAToken signs the http body and cert with ECDSA using EC private key -func GenECDSAToken(cert []byte, key []byte, body []byte) (string, error) { - privKey, err := GetECPrivateKey(key) +func GenECDSAToken(csp bccsp.BCCSP, cert []byte, key []byte, body []byte) (string, error) { + + sk, err := GetKeyFromBytes(csp, key) if err != nil { return "", err } + b64body := B64Encode(body) b64cert := B64Encode(cert) bodyAndcert := b64body + "." + b64cert - hash := sha512.New384() - hash.Write([]byte(bodyAndcert)) - h := hash.Sum(nil) - r, s, err := ecdsa.Sign(rand.Reader, privKey, h) - if err != nil { - return "", fmt.Errorf("Failed in ecdsa.Sign: %s", err) + + digest, digestError := csp.Hash([]byte(bodyAndcert), &bccsp.SHAOpts{}) + if digestError != nil { + return "", fmt.Errorf("Hash operation on %s\t failed with error : %s", bodyAndcert, digestError) } - ECsignature, err := asn1.Marshal(ECDSASignature{r, s}) - if err != nil { - return "", fmt.Errorf("Failed in asn1.Marshal: %s", err) + + ecSignature, signatureError := csp.Sign(sk, digest, nil) + if signatureError != nil { + return "", fmt.Errorf("BCCSP signature generation failed with error :%s", err) + } + if len(ecSignature) == 0 { + return "", errors.New("BCCSP signature creation failed. Signature must be different than nil") } - b64sig := B64Encode(ECsignature) + + b64sig := B64Encode(ecSignature) token := b64cert + "." + b64sig return token, nil @@ -235,7 +243,11 @@ func GenECDSAToken(cert []byte, key []byte, body []byte) (string, error) { // VerifyToken verifies token signed by either ECDSA or RSA and // returns the associated user ID -func VerifyToken(token string, body []byte) (*x509.Certificate, error) { +func VerifyToken(csp bccsp.BCCSP, token string, body []byte) (*x509.Certificate, error) { + + if csp == nil { + return nil, errors.New("BCCSP instance is not present") + } x509Cert, b64Cert, b64Sig, err := DecodeToken(token) if err != nil { return nil, err @@ -246,27 +258,30 @@ func VerifyToken(token string, body []byte) (*x509.Certificate, error) { } b64Body := B64Encode(body) sigString := b64Body + "." + b64Cert - publicKey := x509Cert.PublicKey - hash := sha512.New384() - hash.Write([]byte(sigString)) - h := hash.Sum(nil) - switch publicKey.(type) { - case *rsa.PublicKey: - err := rsa.VerifyPKCS1v15(publicKey.(*rsa.PublicKey), crypto.SHA384, h[:], sig) - if err != nil { - return nil, err - } - case *ecdsa.PublicKey: - ecdsaSignature := new(ECDSASignature) - _, err := asn1.Unmarshal(sig, ecdsaSignature) - if err != nil { - return nil, fmt.Errorf("Failed to unmarshal EC signature to R and S: %s", err) - } - verified := ecdsa.Verify(publicKey.(*ecdsa.PublicKey), h, ecdsaSignature.R, ecdsaSignature.S) - if !verified { - return nil, errors.New("token verification failed (ecdsa.Verify failed)") - } + + pk2, err := csp.KeyImport(x509Cert, &bccsp.X509PublicKeyImportOpts{Temporary: false}) + if err != nil { + return nil, fmt.Errorf("Public Key import into BCCSP failed with error : %s", err) } + if pk2 == nil { + return nil, errors.New("Public Key Cannot be imported into BCCSP") + } + //bccsp.X509PublicKeyImportOpts + //Using default hash algo + digest, digestError := csp.Hash([]byte(sigString), &bccsp.SHAOpts{}) + if digestError != nil { + return nil, fmt.Errorf("Message digest failed with error : %s", digestError) + } + + valid, validErr := csp.Verify(pk2, sig, digest, nil) + + if validErr != nil { + return nil, fmt.Errorf("Token Signature validation failed with error : %s ", validErr) + } + if !valid { + return nil, errors.New("Token Signature Validation failed") + } + return x509Cert, nil } @@ -309,6 +324,9 @@ func GetECPrivateKey(raw []byte) (*ecdsa.PrivateKey, error) { } //GetRSAPrivateKey get *rsa.PrivateKey from key pem +// This function is commented out as there is no +// adequate support for RSA +/* func GetRSAPrivateKey(raw []byte) (*rsa.PrivateKey, error) { decoded, _ := pem.Decode(raw) if decoded == nil { @@ -320,6 +338,7 @@ func GetRSAPrivateKey(raw []byte) (*rsa.PrivateKey, error) { } return RSAprivKey, nil } +*/ // B64Encode base64 encodes bytes func B64Encode(buf []byte) string { @@ -483,3 +502,29 @@ func GetUser() (string, string, error) { return eid, pass, nil } + +// GetKeyFromBytes returns a BCCSP key given a byte buffer. The byte buffer +// should always contain the SKI and not the real private key; however, +// until we have complete BCCSP integration, we tolerate it being the real +// private key. +func GetKeyFromBytes(csp bccsp.BCCSP, key []byte) (bccsp.Key, error) { + + // This should succeed if key is an SKI + sk, err := csp.GetKey(key) + if err == nil { + return sk, nil + } + + // Nope, try handling as a private key itself + pk, err := GetECPrivateKey(key) + if err != nil { + return nil, err + } + + pkb, err := x509.MarshalECPrivateKey(pk) + if err != nil { + return nil, fmt.Errorf("Failed to marshal EC private key: %s", err) + } + + return csp.KeyImport(pkb, &bccsp.ECDSAPrivateKeyImportOpts{Temporary: false}) +} diff --git a/util/util_test.go b/util/util_test.go index 8b71f4349..7a76ff61a 100644 --- a/util/util_test.go +++ b/util/util_test.go @@ -17,11 +17,15 @@ limitations under the License. package util import ( + "errors" + "fmt" "io/ioutil" "os" "path/filepath" "testing" + "github.com/hyperledger/fabric/bccsp" + "github.com/hyperledger/fabric/bccsp/factory" _ "github.com/mattn/go-sqlite3" "github.com/spf13/viper" ) @@ -41,62 +45,165 @@ func TestECCreateToken(t *testing.T) { cert, _ := ioutil.ReadFile(getPath("ec.pem")) privKey, _ := ioutil.ReadFile(getPath("ec-key.pem")) body := []byte("request byte array") - ECtoken, err := CreateToken(cert, privKey, body) + + csp, error := getDefaultBCCSPInstance() + if error != nil { + t.Errorf("Default BCCSP instance failed with error %s", error) + } + + ECtoken, err := CreateToken(csp, cert, privKey, body) if err != nil { t.Fatalf("CreatToken failed: %s", err) } - _, err = VerifyToken(ECtoken, body) + + _, err = VerifyToken(csp, ECtoken, body) if err != nil { t.Fatalf("VerifyToken failed: %s", err) } + + _, err = VerifyToken(nil, ECtoken, body) + if err == nil { + t.Fatal("VerifyToken should have failed as no instance of csp is passed") + } + + _, err = VerifyToken(csp, "", body) + if err == nil { + t.Fatal("VerifyToken should have failed as no EC Token is passed") + } + + _, err = VerifyToken(csp, ECtoken, nil) + if err == nil { + t.Fatal("VerifyToken should have failed as no EC Token is passed") + } + + verifiedByte := []byte("TEST") + body = append(body, verifiedByte[0]) + _, err = VerifyToken(csp, ECtoken, body) + if err == nil { + t.Fatal("VerifyToken should have failed as body was tampered") + } + + ski, skierror := ioutil.ReadFile(getPath("ec-key.ski")) + if skierror != nil { + t.Fatalf("SKI File Read failed with error : %s", skierror) + } + ECtoken, err = CreateToken(csp, ski, privKey, body) + if (err == nil) || (ECtoken != "") { + t.Fatal("CreatToken should have failed as certificate passed is not correct") + } +} + +func TestGetX509CertFromPem(t *testing.T) { + + certBuffer, error := ioutil.ReadFile(getPath("ec.pem")) + if error != nil { + t.Fatalf("Certificate File Read from file failed with error : %s", error) + } + certificate, err := GetX509CertificateFromPEM(certBuffer) + if err != nil { + t.Fatalf("GetX509CertificateFromPEM failed with error : %s", err) + } + if certificate == nil { + t.Fatal("Certificate cannot be nil") + } + + skiBuffer, skiError := ioutil.ReadFile(getPath("ec-key.ski")) + if skiError != nil { + t.Fatalf("SKI File read failed with error : %s", skiError) + } + + certificate, err = GetX509CertificateFromPEM(skiBuffer) + if err == nil { + t.Fatal("GetX509CertificateFromPEM should have failed as bytes passed was not in correct format") + } + if certificate != nil { + t.Fatalf("GetX509CertificateFromPEM should have failed as bytes passed was not in correct format") + } + } +// This test case has been removed temporarily +// as BCCSP does not have support for RSA private key import +/* func TestRSACreateToken(t *testing.T) { cert, _ := ioutil.ReadFile(getPath("rsa.pem")) privKey, _ := ioutil.ReadFile(getPath("rsa-key.pem")) body := []byte("request byte array") - RSAtoken, err := CreateToken(cert, privKey, body) + + csp, error := getDefaultBCCSPInstance() + if error != nil { + t.Errorf("Default BCCSP instance failed with error %s", error) + } + + RSAtoken, err := CreateToken(csp, cert, privKey, body) if err != nil { - t.Fatalf("CreatToken failed: %s", err) + t.Fatalf("CreatToken failed with error : %s", err) } - _, err = VerifyToken(RSAtoken, body) + + _, err = VerifyToken(csp, RSAtoken, body) if err != nil { - t.Fatalf("VerifyToken failed: %s", err) + t.Fatalf("VerifyToken failed with error : %s", err) } } +*/ func TestCreateTokenDiffKey(t *testing.T) { cert, _ := ioutil.ReadFile(getPath("ec.pem")) privKey, _ := ioutil.ReadFile(getPath("rsa-key.pem")) body := []byte("request byte array") - _, err := CreateToken(cert, privKey, body) + + csp, error := getDefaultBCCSPInstance() + if error != nil { + t.Errorf("Default BCCSP instance failed with error %s", error) + } + _, err := CreateToken(csp, cert, privKey, body) if err == nil { t.Fatalf("TestCreateTokenDiffKey passed but should have failed") } } +// TestCreateTokenDiffKey2 has been commeted out right now +// As there BCCSP does not have support fot RSA private Key +// import. This will be uncommented when the support is in. +/* func TestCreateTokenDiffKey2(t *testing.T) { cert, _ := ioutil.ReadFile(getPath("rsa.pem")) privKey, _ := ioutil.ReadFile(getPath("ec-key.pem")) body := []byte("request byte array") - _, err := CreateToken(cert, privKey, body) + + csp, error := getDefaultBCCSPInstance() + if error != nil { + t.Errorf("Default BCCSP instance failed with error %s", error) + } + _, err := CreateToken(csp, cert, privKey, body) if err == nil { t.Fatalf("TestCreateTokenDiffKey2 passed but should have failed") } } +*/ func TestEmptyToken(t *testing.T) { body := []byte("request byte array") - _, err := VerifyToken("", body) + + csp, error := getDefaultBCCSPInstance() + if error != nil { + t.Errorf("Default BCCSP instance failed with error %s", error) + } + _, err := VerifyToken(csp, "", body) if err == nil { t.Fatalf("TestEmptyToken passed but should have failed") } } func TestEmptyCert(t *testing.T) { - cert, _ := ioutil.ReadFile(getPath("rsa.pem")) + cert, _ := ioutil.ReadFile(getPath("ec.pem")) body := []byte("request byte array") - _, err := CreateToken(cert, []byte(""), body) + + csp, error := getDefaultBCCSPInstance() + if error != nil { + t.Errorf("Default BCCSP instance failed with error %s", error) + } + _, err := CreateToken(csp, cert, []byte(""), body) if err == nil { t.Fatalf("TestEmptyCert passed but should have failed") } @@ -105,7 +212,13 @@ func TestEmptyCert(t *testing.T) { func TestEmptyKey(t *testing.T) { privKey, _ := ioutil.ReadFile(getPath("ec-key.pem")) body := []byte("request byte array") - _, err := CreateToken([]byte(""), privKey, body) + + csp, error := getDefaultBCCSPInstance() + if error != nil { + t.Errorf("Default BCCSP instance failed with error %s", error) + } + + _, err := CreateToken(csp, []byte(""), privKey, body) if err == nil { t.Fatalf("TestEmptyKey passed but should have failed") } @@ -114,7 +227,13 @@ func TestEmptyKey(t *testing.T) { func TestEmptyBody(t *testing.T) { cert, _ := ioutil.ReadFile(getPath("ec.pem")) privKey, _ := ioutil.ReadFile(getPath("ec-key.pem")) - _, err := CreateToken(cert, privKey, []byte("")) + + csp, error := getDefaultBCCSPInstance() + if error != nil { + t.Errorf("Default BCCSP instance failed with error %s", error) + } + + _, err := CreateToken(csp, cert, privKey, []byte("")) if err != nil { t.Fatalf("CreateToken failed: %s", err) } @@ -170,12 +289,20 @@ func TestGetDefaultConfigFile(t *testing.T) { os.Setenv("HOME", "/tmp") - defConfigFile := filepath.Join("/tmp", ".fabric-ca-client/fabric-ca-client-config.yaml") - defConfig := GetDefaultConfigFile("fabric-ca-client") - if defConfigFile != defConfig { - t.Errorf("Incorrect default config (%s) path retrieved", defConfig) + expected := filepath.Join("/tmp", ".fabric-ca-client/fabric-ca-client-config.yaml") + real := GetDefaultConfigFile("fabric-ca-client") + if real != expected { + t.Errorf("Incorrect default config path retrieved; expected %s but found %s", + expected, real) } + os.Setenv("FABRIC_CA_HOME", "/tmp") + expected = filepath.Join("/tmp", "fabric-ca-server-config.yaml") + real = GetDefaultConfigFile("fabric-ca-server") + if real != expected { + t.Errorf("Incorrect default config path retrieved; expected %s but found %s", + expected, real) + } } func TestUnmarshal(t *testing.T) { @@ -280,3 +407,15 @@ func makeFileAbs(t *testing.T, file, dir, expect string) { t.Errorf("Absolute of file=%s with dir=%s expected %s but was %s", file, dir, expect, path) } } + +func getDefaultBCCSPInstance() (bccsp.BCCSP, error) { + defaultBccsp, bccspError := factory.GetDefault() + if bccspError != nil { + return nil, fmt.Errorf("BCCSP initialiazation failed with error : %s", bccspError) + } + if defaultBccsp == nil { + return nil, errors.New("Cannot get default instance of BCCSP") + } + + return defaultBccsp, nil +}