Skip to content

Commit

Permalink
Fabric-CA bccsp integration for VerifyToken
Browse files Browse the repository at this point in the history
Implements FAB 2014
https://jira.hyperledger.org/browse/FAB-2014

The implementation removed support of RSA
for the time being.Will provide support of
RSA later.

Client BCCSP integration is more of workaround
as client BCCSP is not being instantiated the
way it should have been. The reason being :
1) The focus of change set is primarily server
side support and
2)This implementation is not aimed to support
for Go Client

Fixed lint issue

Rebased

Resolved merge conflict

Commented out RSA code
Add some more test cases to improve test coverage
Change-Id: Ia7f198fe3a4dfbbb60f355e8b063f885e5edc845
Signed-off-by: ASHUTOSH KUMAR <[email protected]>
Signed-off-by: Keith Smith <[email protected]>
  • Loading branch information
ashku authored and Keith Smith committed Feb 21, 2017
1 parent ec63940 commit c280fa3
Show file tree
Hide file tree
Showing 7 changed files with 277 additions and 68 deletions.
2 changes: 1 addition & 1 deletion api/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}

Expand Down
24 changes: 23 additions & 1 deletion lib/identity.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -40,6 +42,7 @@ type Identity struct {
name string
ecert *Signer
client *Client
CSP bccsp.BCCSP
}

// GetName returns the identity name
Expand Down Expand Up @@ -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
}
2 changes: 1 addition & 1 deletion lib/serverauth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion lib/serverrevoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
3 changes: 3 additions & 0 deletions testdata/ec-key.ski
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN BCCSP SKI-----
sLJGcSFzmXHJlmULJ9Ne8//jZlTKnS8dsZvbQu4i27c=
-----END BCCSP SKI-----
139 changes: 92 additions & 47 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -42,6 +37,7 @@ import (
"time"

"github.com/cloudflare/cfssl/log"
"github.com/hyperledger/fabric/bccsp"
"github.com/jmoiron/sqlx"
"github.com/spf13/viper"
)
Expand Down Expand Up @@ -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")
Expand All @@ -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
}
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
}

Expand Down Expand Up @@ -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 {
Expand All @@ -320,6 +338,7 @@ func GetRSAPrivateKey(raw []byte) (*rsa.PrivateKey, error) {
}
return RSAprivKey, nil
}
*/

// B64Encode base64 encodes bytes
func B64Encode(buf []byte) string {
Expand Down Expand Up @@ -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})
}
Loading

0 comments on commit c280fa3

Please sign in to comment.