Skip to content

Commit

Permalink
Merge pull request #2058 from JonathanLevi/caECAPrefactoring
Browse files Browse the repository at this point in the history
2040: separate the ECAP functionality from the ECA (eca.go -> ecap.go)
  • Loading branch information
srderson committed Jun 29, 2016
2 parents 6db5a24 + fb49c6b commit d171cc4
Show file tree
Hide file tree
Showing 2 changed files with 269 additions and 242 deletions.
242 changes: 0 additions & 242 deletions membersrvc/ca/eca.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,31 +17,20 @@ limitations under the License.
package ca

import (
"bytes"
"crypto/ecdsa"
"crypto/rand"
"crypto/subtle"
"crypto/x509"
"crypto/x509/pkix"
"database/sql"
"encoding/asn1"
"encoding/base64"
"encoding/pem"
"errors"
"google/protobuf"
"io/ioutil"
"math/big"
"strconv"
"strings"
"time"

"github.com/hyperledger/fabric/core/crypto/primitives/ecies"

"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/core/crypto/primitives"
pb "github.com/hyperledger/fabric/membersrvc/protos"
"github.com/spf13/viper"
"golang.org/x/net/context"
"google.golang.org/grpc"
)

Expand All @@ -59,12 +48,6 @@ type ECA struct {
obcPriv, obcPub []byte
}

// ECAP serves the public GRPC interface of the ECA.
//
type ECAP struct {
eca *ECA
}

func initializeECATables(db *sql.DB) error {
return initializeCommonTables(db)
}
Expand Down Expand Up @@ -212,228 +195,3 @@ func (eca *ECA) startECAP(srv *grpc.Server) {
func (eca *ECA) startECAA(srv *grpc.Server) {
pb.RegisterECAAServer(srv, &ECAA{eca})
}

// ReadCACertificate reads the certificate of the ECA.
//
func (ecap *ECAP) ReadCACertificate(ctx context.Context, in *pb.Empty) (*pb.Cert, error) {
Trace.Println("gRPC ECAP:ReadCACertificate")

return &pb.Cert{Cert: ecap.eca.raw}, nil
}

func (ecap *ECAP) fetchAttributes(cert *pb.Cert) error {
//TODO we are creating a new client connection per each ecert request. We should implement a connections pool.
sock, acaP, err := GetACAClient()
if err != nil {
return err
}
defer sock.Close()

req := &pb.ACAFetchAttrReq{
Ts: &google_protobuf.Timestamp{Seconds: time.Now().Unix(), Nanos: 0},
ECert: cert,
Signature: nil}

var rawReq []byte
rawReq, err = proto.Marshal(req)
if err != nil {
return err
}

var r, s *big.Int

r, s, err = primitives.ECDSASignDirect(ecap.eca.priv, rawReq)

if err != nil {
return err
}

R, _ := r.MarshalText()
S, _ := s.MarshalText()

req.Signature = &pb.Signature{Type: pb.CryptoType_ECDSA, R: R, S: S}

resp, err := acaP.FetchAttributes(context.Background(), req)
if err != nil {
return err
}

if resp.Status != pb.ACAFetchAttrResp_FAILURE {
return nil
}
return errors.New("Error fetching attributes.")
}

// CreateCertificatePair requests the creation of a new enrollment certificate pair by the ECA.
//
func (ecap *ECAP) CreateCertificatePair(ctx context.Context, in *pb.ECertCreateReq) (*pb.ECertCreateResp, error) {
Trace.Println("gRPC ECAP:CreateCertificate")

// validate token
var tok, prev []byte
var role, state int
var enrollID string

id := in.Id.Id
err := ecap.eca.readUser(id).Scan(&role, &tok, &state, &prev, &enrollID)
if err != nil {
errMsg := "Identity lookup error: " + err.Error()
Trace.Println(errMsg)
return nil, errors.New(errMsg)
}
if !bytes.Equal(tok, in.Tok.Tok) {
Trace.Printf("id or token mismatch: id=%s\n", id)
return nil, errors.New("Identity or token does not match.")
}

ekey, err := x509.ParsePKIXPublicKey(in.Enc.Key)
if err != nil {
return nil, err
}

fetchResult := pb.FetchAttrsResult{Status: pb.FetchAttrsResult_SUCCESS, Msg: ""}
switch {
case state == 0:
// initial request, create encryption challenge
tok = []byte(randomString(12))

_, err = ecap.eca.db.Exec("UPDATE Users SET token=?, state=?, key=? WHERE id=?", tok, 1, in.Enc.Key, id)
if err != nil {
Error.Println(err)
return nil, err
}

spi := ecies.NewSPI()
eciesKey, err := spi.NewPublicKey(nil, ekey.(*ecdsa.PublicKey))
if err != nil {
return nil, err
}

ecies, err := spi.NewAsymmetricCipherFromPublicKey(eciesKey)
if err != nil {
return nil, err
}

out, err := ecies.Process(tok)

return &pb.ECertCreateResp{Certs: nil, Chain: nil, Pkchain: nil, Tok: &pb.Token{Tok: out}}, err

case state == 1:
// ensure that the same encryption key is signed that has been used for the challenge
if subtle.ConstantTimeCompare(in.Enc.Key, prev) != 1 {
return nil, errors.New("Encryption keys do not match.")
}

// validate request signature
sig := in.Sig
in.Sig = nil

r, s := big.NewInt(0), big.NewInt(0)
r.UnmarshalText(sig.R)
s.UnmarshalText(sig.S)

if in.Sign.Type != pb.CryptoType_ECDSA {
return nil, errors.New("Unsupported (signing) key type.")
}
skey, err := x509.ParsePKIXPublicKey(in.Sign.Key)
if err != nil {
return nil, err
}

hash := primitives.NewHash()
raw, _ := proto.Marshal(in)
hash.Write(raw)
if ecdsa.Verify(skey.(*ecdsa.PublicKey), hash.Sum(nil), r, s) == false {
return nil, errors.New("Signature verification failed.")
}

// create new certificate pair
ts := time.Now().Add(-1 * time.Minute).UnixNano()

spec := NewDefaultCertificateSpecWithCommonName(id, enrollID, skey.(*ecdsa.PublicKey), x509.KeyUsageDigitalSignature, pkix.Extension{Id: ECertSubjectRole, Critical: true, Value: []byte(strconv.Itoa(ecap.eca.readRole(id)))})
sraw, err := ecap.eca.createCertificateFromSpec(spec, ts, nil, true)
if err != nil {
Error.Println(err)
return nil, err
}

spec = NewDefaultCertificateSpecWithCommonName(id, enrollID, ekey.(*ecdsa.PublicKey), x509.KeyUsageDataEncipherment, pkix.Extension{Id: ECertSubjectRole, Critical: true, Value: []byte(strconv.Itoa(ecap.eca.readRole(id)))})
eraw, err := ecap.eca.createCertificateFromSpec(spec, ts, nil, true)
if err != nil {
ecap.eca.db.Exec("DELETE FROM Certificates Where id=?", id)
Error.Println(err)
return nil, err
}

_, err = ecap.eca.db.Exec("UPDATE Users SET state=? WHERE id=?", 2, id)
if err != nil {
ecap.eca.db.Exec("DELETE FROM Certificates Where id=?", id)
Error.Println(err)
return nil, err
}

var obcECKey []byte
if role == int(pb.Role_VALIDATOR) {
obcECKey = ecap.eca.obcPriv
} else {
obcECKey = ecap.eca.obcPub
}
if role == int(pb.Role_CLIENT) {
//Only client have to fetch attributes.
if viper.GetBool("aca.enabled") {
err = ecap.fetchAttributes(&pb.Cert{Cert: sraw})
if err != nil {
fetchResult = pb.FetchAttrsResult{Status: pb.FetchAttrsResult_FAILURE, Msg: err.Error()}

}
}
}

return &pb.ECertCreateResp{Certs: &pb.CertPair{Sign: sraw, Enc: eraw}, Chain: &pb.Token{Tok: ecap.eca.obcKey}, Pkchain: obcECKey, Tok: nil, FetchResult: &fetchResult}, nil
}

return nil, errors.New("Invalid (=expired) certificate creation token provided.")
}

// ReadCertificatePair reads an enrollment certificate pair from the ECA.
//
func (ecap *ECAP) ReadCertificatePair(ctx context.Context, in *pb.ECertReadReq) (*pb.CertPair, error) {
Trace.Println("gRPC ECAP:ReadCertificate")

rows, err := ecap.eca.readCertificates(in.Id.Id)
defer rows.Close()

hasResults := false
var certs [][]byte
if err == nil {
for rows.Next() {
hasResults = true
var raw []byte
err = rows.Scan(&raw)
certs = append(certs, raw)
}
err = rows.Err()
}

if !hasResults {
return nil, errors.New("No certificates for the given identity were found.")
}
return &pb.CertPair{Sign: certs[0], Enc: certs[1]}, err
}

// ReadCertificateByHash reads a single enrollment certificate by hash from the ECA.
//
func (ecap *ECAP) ReadCertificateByHash(ctx context.Context, hash *pb.Hash) (*pb.Cert, error) {
Trace.Println("gRPC ECAP:ReadCertificateByHash")

raw, err := ecap.eca.readCertificateByHash(hash.Hash)
return &pb.Cert{Cert: raw}, err
}

// RevokeCertificatePair revokes a certificate pair from the ECA. Not yet implemented.
//
func (ecap *ECAP) RevokeCertificatePair(context.Context, *pb.ECertRevokeReq) (*pb.CAStatus, error) {
Trace.Println("gRPC ECAP:RevokeCertificate")

return nil, errors.New("ECAP:RevokeCertificate method not (yet) implemented")
}

0 comments on commit d171cc4

Please sign in to comment.