191 lines
4.7 KiB
Go
191 lines
4.7 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package configtx
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/ecdsa"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"crypto/x509"
|
|
"encoding/asn1"
|
|
"encoding/pem"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
cb "github.com/hyperledger/fabric-protos-go/common"
|
|
mb "github.com/hyperledger/fabric-protos-go/msp"
|
|
)
|
|
|
|
// SigningIdentity is an MSP Identity that can be used to sign configuration
|
|
// updates.
|
|
type SigningIdentity struct {
|
|
Certificate *x509.Certificate
|
|
PrivateKey crypto.PrivateKey
|
|
MSPID string
|
|
}
|
|
|
|
type ecdsaSignature struct {
|
|
R, S *big.Int
|
|
}
|
|
|
|
// Public returns the public key associated with this signing
|
|
// identity's certificate.
|
|
func (s *SigningIdentity) Public() crypto.PublicKey {
|
|
return s.Certificate.PublicKey
|
|
}
|
|
|
|
// Sign performs ECDSA sign with this signing identity's private key on the
|
|
// given message hashed using SHA-256. It ensures signatures are created with
|
|
// Low S values since Fabric normalizes all signatures to Low S.
|
|
// See https://github.com/bitcoin/bips/blob/master/bip-0146.mediawiki#low_s
|
|
// for more detail.
|
|
func (s *SigningIdentity) Sign(reader io.Reader, msg []byte, opts crypto.SignerOpts) (signature []byte, err error) {
|
|
switch pk := s.PrivateKey.(type) {
|
|
case *ecdsa.PrivateKey:
|
|
hasher := sha256.New()
|
|
hasher.Write(msg)
|
|
digest := hasher.Sum(nil)
|
|
|
|
rr, ss, err := ecdsa.Sign(reader, pk, digest)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// ensure Low S signatures
|
|
sig := toLowS(
|
|
pk.PublicKey,
|
|
ecdsaSignature{
|
|
R: rr,
|
|
S: ss,
|
|
},
|
|
)
|
|
|
|
return asn1.Marshal(sig)
|
|
default:
|
|
return nil, fmt.Errorf("signing with private key of type %T not supported", pk)
|
|
}
|
|
}
|
|
|
|
// toLows normalizes all signatures to a canonical form where s is at most
|
|
// half the order of the curve. By doing so, it compliant with what Fabric
|
|
// expected as well as protect against signature malleability attacks.
|
|
func toLowS(key ecdsa.PublicKey, sig ecdsaSignature) ecdsaSignature {
|
|
// calculate half order of the curve
|
|
halfOrder := new(big.Int).Div(key.Curve.Params().N, big.NewInt(2))
|
|
// check if s is greater than half order of curve
|
|
if sig.S.Cmp(halfOrder) == 1 {
|
|
// Set s to N - s so that s will be less than or equal to half order
|
|
sig.S.Sub(key.Params().N, sig.S)
|
|
}
|
|
|
|
return sig
|
|
}
|
|
|
|
// CreateConfigSignature creates a config signature for the the given configuration
|
|
// update using the specified signing identity.
|
|
func (s *SigningIdentity) CreateConfigSignature(marshaledUpdate []byte) (*cb.ConfigSignature, error) {
|
|
signatureHeader, err := s.signatureHeader()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("creating signature header: %v", err)
|
|
}
|
|
|
|
header, err := proto.Marshal(signatureHeader)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshaling signature header: %v", err)
|
|
}
|
|
|
|
configSignature := &cb.ConfigSignature{
|
|
SignatureHeader: header,
|
|
}
|
|
|
|
configSignature.Signature, err = s.Sign(
|
|
rand.Reader,
|
|
concatenateBytes(configSignature.SignatureHeader, marshaledUpdate),
|
|
nil,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("signing config update: %v", err)
|
|
}
|
|
|
|
return configSignature, nil
|
|
}
|
|
|
|
// SignEnvelope signs an envelope using the SigningIdentity.
|
|
func (s *SigningIdentity) SignEnvelope(e *cb.Envelope) error {
|
|
signatureHeader, err := s.signatureHeader()
|
|
if err != nil {
|
|
return fmt.Errorf("creating signature header: %v", err)
|
|
}
|
|
|
|
sHeader, err := proto.Marshal(signatureHeader)
|
|
if err != nil {
|
|
return fmt.Errorf("marshaling signature header: %v", err)
|
|
}
|
|
|
|
payload := &cb.Payload{}
|
|
err = proto.Unmarshal(e.Payload, payload)
|
|
if err != nil {
|
|
return fmt.Errorf("unmarshaling envelope payload: %v", err)
|
|
}
|
|
payload.Header.SignatureHeader = sHeader
|
|
|
|
payloadBytes, err := proto.Marshal(payload)
|
|
if err != nil {
|
|
return fmt.Errorf("marshaling payload: %v", err)
|
|
}
|
|
|
|
sig, err := s.Sign(rand.Reader, payloadBytes, nil)
|
|
if err != nil {
|
|
return fmt.Errorf("signing envelope payload: %v", err)
|
|
}
|
|
|
|
e.Payload = payloadBytes
|
|
e.Signature = sig
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *SigningIdentity) signatureHeader() (*cb.SignatureHeader, error) {
|
|
pemBytes := pem.EncodeToMemory(&pem.Block{
|
|
Type: "CERTIFICATE",
|
|
Bytes: s.Certificate.Raw,
|
|
})
|
|
|
|
idBytes, err := proto.Marshal(&mb.SerializedIdentity{
|
|
Mspid: s.MSPID,
|
|
IdBytes: pemBytes,
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshaling serialized identity: %v", err)
|
|
}
|
|
|
|
nonce, err := newNonce()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &cb.SignatureHeader{
|
|
Creator: idBytes,
|
|
Nonce: nonce,
|
|
}, nil
|
|
}
|
|
|
|
// newNonce generates a 24-byte nonce using the crypto/rand package.
|
|
func newNonce() ([]byte, error) {
|
|
nonce := make([]byte, 24)
|
|
|
|
_, err := rand.Read(nonce)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get random bytes: %v", err)
|
|
}
|
|
|
|
return nonce, nil
|
|
}
|