Skip to content

Commit 5705b36

Browse files
committed
add consnter info to router config
add router info to consenter config router's certificate pinning in SubmitConfig add configSubmitter to router, without forwarding config requests Signed-off-by: Dor.Katzelnick <Dor.Katzelnick@ibm.com>
1 parent 4f32c71 commit 5705b36

File tree

11 files changed

+176
-81
lines changed

11 files changed

+176
-81
lines changed

common/utils/pem_utils.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package utils
8+
9+
import (
10+
"crypto/ecdsa"
11+
"crypto/x509"
12+
"encoding/pem"
13+
"fmt"
14+
"os"
15+
)
16+
17+
func ReadPem(path string) ([]byte, error) {
18+
if path == "" {
19+
return nil, fmt.Errorf("failed reading pem file, path is empty")
20+
}
21+
data, err := os.ReadFile(path)
22+
if err != nil {
23+
return nil, fmt.Errorf("failed reading a pem file from %s, err: %v", path, err)
24+
}
25+
26+
return data, nil
27+
}
28+
29+
func blockToPublicKey(block *pem.Block) []byte {
30+
cert, err := x509.ParseCertificate(block.Bytes)
31+
if err != nil {
32+
panic(fmt.Sprintf("Failed parsing consenter signing certificate: %v", err))
33+
}
34+
35+
pubKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
36+
if !ok {
37+
panic(fmt.Sprintf("Failed parsing consenter public key: %v", err))
38+
}
39+
40+
publicKeyBytes, err := x509.MarshalPKIXPublicKey(pubKey)
41+
if err != nil {
42+
panic(fmt.Sprintf("Failed marshaling consenter public key: %v", err))
43+
}
44+
45+
pemPublicKey := pem.EncodeToMemory(&pem.Block{
46+
Type: "PUBLIC KEY",
47+
Bytes: publicKeyBytes,
48+
})
49+
50+
return pemPublicKey
51+
}
52+
53+
func GetPublicKeyFromCertificate(nodeCert []byte) []byte {
54+
// Fetch public key from signing certificate
55+
// NOTE: ARMA's new configuration now uses certificates, which inherently contain the public key, instead of a separate public key field.
56+
// To ensure backward compatibility until the full new config integration, the public key it enabled.
57+
block, _ := pem.Decode(nodeCert)
58+
if block == nil || block.Bytes == nil {
59+
panic("Failed decoding consenter signing certificate")
60+
}
61+
62+
var pemPublicKey []byte
63+
if block.Type == "CERTIFICATE" {
64+
pemPublicKey = blockToPublicKey(block)
65+
}
66+
67+
if block.Type == "PUBLIC KEY" {
68+
pemPublicKey = nodeCert
69+
}
70+
71+
return pemPublicKey
72+
}
File renamed without changes.

common/utils/read_pem.go

Lines changed: 0 additions & 24 deletions
This file was deleted.

config/config.go

Lines changed: 35 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ SPDX-License-Identifier: Apache-2.0
77
package config
88

99
import (
10-
"crypto/ecdsa"
11-
"crypto/x509"
12-
"encoding/pem"
1310
"fmt"
11+
"net"
1412
"os"
1513
"path/filepath"
1614
"sort"
@@ -254,6 +252,7 @@ func (config *Configuration) ExtractRouterConfig(configBlock *common.Block) *nod
254252
ListenAddress: config.LocalConfig.NodeLocalConfig.GeneralConfig.ListenAddress + ":" + strconv.Itoa(int(config.LocalConfig.NodeLocalConfig.GeneralConfig.ListenPort)),
255253
ConfigStorePath: config.LocalConfig.NodeLocalConfig.FileStore.Path,
256254
Shards: config.ExtractShards(),
255+
Consenter: config.ExtractConsenterInParty(),
257256
NumOfConnectionsForBatcher: config.LocalConfig.NodeLocalConfig.RouterParams.NumberOfConnectionsPerBatcher,
258257
NumOfgRPCStreamsPerConnection: config.LocalConfig.NodeLocalConfig.RouterParams.NumberOfStreamsPerConnection,
259258
UseTLS: config.LocalConfig.TLSConfig.Enabled,
@@ -333,6 +332,7 @@ func (config *Configuration) ExtractConsenterConfig() *nodeconfig.ConsenterNodeC
333332
consenterConfig := &nodeconfig.ConsenterNodeConfig{
334333
Shards: config.ExtractShards(),
335334
Consenters: config.ExtractConsenters(),
335+
Router: config.ExtractRouterInParty(),
336336
Directory: config.LocalConfig.NodeLocalConfig.FileStore.Path,
337337
ListenAddress: config.LocalConfig.NodeLocalConfig.GeneralConfig.ListenAddress + ":" + strconv.Itoa(int(config.LocalConfig.NodeLocalConfig.GeneralConfig.ListenPort)),
338338
PartyId: config.LocalConfig.NodeLocalConfig.PartyID,
@@ -394,22 +394,7 @@ func (config *Configuration) ExtractShards() []nodeconfig.ShardInfo {
394394
for _, batcher := range party.BatchersConfig {
395395
shardId := types.ShardID(batcher.ShardID)
396396

397-
// Fetch public key from signing certificate
398-
// NOTE: ARMA's new configuration uses certificates, which inherently contain the public key, instead of a separate public key field.
399-
// To ensure backward compatibility until the full new config integration, the public key it enabled.
400-
block, _ := pem.Decode(batcher.SignCert)
401-
if block == nil || block.Bytes == nil {
402-
panic("Failed decoding batcher signing certificate")
403-
}
404-
405-
var pemPublicKey []byte
406-
if block.Type == "CERTIFICATE" {
407-
pemPublicKey = blockToPublicKey(block)
408-
}
409-
410-
if block.Type == "PUBLIC KEY" {
411-
pemPublicKey = batcher.SignCert
412-
}
397+
pemPublicKey := utils.GetPublicKeyFromCertificate(batcher.SignCert)
413398

414399
batcher := nodeconfig.BatcherInfo{
415400
PartyID: types.PartyID(party.PartyID),
@@ -447,22 +432,7 @@ func (config *Configuration) ExtractConsenters() []nodeconfig.ConsenterInfo {
447432
tlsCACertsCollection = append(tlsCACertsCollection, ca)
448433
}
449434

450-
// Fetch public key from signing certificate
451-
// NOTE: ARMA's new configuration now uses certificates, which inherently contain the public key, instead of a separate public key field.
452-
// To ensure backward compatibility until the full new config integration, the public key it enabled.
453-
block, _ := pem.Decode(party.ConsenterConfig.SignCert)
454-
if block == nil || block.Bytes == nil {
455-
panic("Failed decoding consenter signing certificate")
456-
}
457-
458-
var pemPublicKey []byte
459-
if block.Type == "CERTIFICATE" {
460-
pemPublicKey = blockToPublicKey(block)
461-
}
462-
463-
if block.Type == "PUBLIC KEY" {
464-
pemPublicKey = party.ConsenterConfig.SignCert
465-
}
435+
pemPublicKey := utils.GetPublicKeyFromCertificate(party.ConsenterConfig.SignCert)
466436

467437
consenterInfo := nodeconfig.ConsenterInfo{
468438
PartyID: types.PartyID(party.PartyID),
@@ -476,28 +446,43 @@ func (config *Configuration) ExtractConsenters() []nodeconfig.ConsenterInfo {
476446
return consenters
477447
}
478448

479-
func blockToPublicKey(block *pem.Block) []byte {
480-
cert, err := x509.ParseCertificate(block.Bytes)
481-
if err != nil {
482-
panic(fmt.Sprintf("Failed parsing consenter signing certificate: %v", err))
449+
func (config *Configuration) ExtractRouterInParty() nodeconfig.RouterInfo {
450+
partyID := config.LocalConfig.NodeLocalConfig.PartyID
451+
var party *protos.PartyConfig
452+
for _, p := range config.SharedConfig.PartiesConfig {
453+
if types.PartyID(p.PartyID) == partyID {
454+
party = p
455+
}
456+
}
457+
if party == nil {
458+
panic("failed to extract router from config")
483459
}
484460

485-
pubKey, ok := cert.PublicKey.(*ecdsa.PublicKey)
486-
if !ok {
487-
panic(fmt.Sprintf("Failed parsing consenter public key: %v", err))
461+
routerConfig := party.RouterConfig
462+
463+
var tlsCACertsCollection []nodeconfig.RawBytes
464+
for _, ca := range party.TLSCACerts {
465+
tlsCACertsCollection = append(tlsCACertsCollection, ca)
488466
}
489467

490-
publicKeyBytes, err := x509.MarshalPKIXPublicKey(pubKey)
491-
if err != nil {
492-
panic(fmt.Sprintf("Failed marshaling consenter public key: %v", err))
468+
routerInfo := nodeconfig.RouterInfo{
469+
PartyID: partyID,
470+
Endpoint: net.JoinHostPort(routerConfig.Host, strconv.Itoa(int(routerConfig.Port))),
471+
TLSCACerts: tlsCACertsCollection,
472+
TLSCert: routerConfig.TlsCert,
493473
}
494474

495-
pemPublicKey := pem.EncodeToMemory(&pem.Block{
496-
Type: "PUBLIC KEY",
497-
Bytes: publicKeyBytes,
498-
})
475+
return routerInfo
476+
}
499477

500-
return pemPublicKey
478+
func (config *Configuration) ExtractConsenterInParty() nodeconfig.ConsenterInfo {
479+
consenterInfos := config.ExtractConsenters()
480+
for _, consenter := range consenterInfos {
481+
if consenter.PartyID == config.LocalConfig.NodeLocalConfig.PartyID {
482+
return consenter
483+
}
484+
}
485+
panic("failed to extract consenter from config")
501486
}
502487

503488
func (config *Configuration) extractBundleFromConfigBlock(configBlock *common.Block) channelconfig.Resources {

node/config/config.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,13 @@ type ConsenterInfo struct {
6565
TLSCACerts []RawBytes
6666
}
6767

68+
type RouterInfo struct {
69+
PartyID types.PartyID
70+
Endpoint string
71+
TLSCACerts []RawBytes
72+
TLSCert RawBytes
73+
}
74+
6875
type RouterNodeConfig struct {
6976
// Private config
7077
PartyID types.PartyID
@@ -74,6 +81,7 @@ type RouterNodeConfig struct {
7481
ConfigStorePath string
7582
// Shared config
7683
Shards []ShardInfo
84+
Consenter ConsenterInfo
7785
NumOfConnectionsForBatcher int
7886
NumOfgRPCStreamsPerConnection int
7987
UseTLS bool
@@ -141,6 +149,7 @@ type ConsenterNodeConfig struct {
141149
// Shared config
142150
Shards []ShardInfo
143151
Consenters []ConsenterInfo
152+
Router RouterInfo
144153
Directory string
145154
ListenAddress string
146155
// Private config

node/config/config_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,15 @@ func TestRouterNodeConfigToYaml(t *testing.T) {
2525
{2, "127.0.0.1:7051", []RawBytes{{1, 2, 3}, {4, 5, 6}}, RawBytes("BatcherPubKey-2"), RawBytes("TLS CERT")},
2626
}
2727

28+
consenter := ConsenterInfo{1, "127.0.0.1:7050", RawBytes("ConsenterPubKey-1"), []RawBytes{{1, 2, 3}, {4, 5, 6}}}
29+
2830
shards := []ShardInfo{{ShardId: 1, Batchers: batchers}}
2931
rnc := &RouterNodeConfig{
3032
TLSCertificateFile: []byte("tls cert"),
3133
TLSPrivateKeyFile: []byte("tls key"),
3234
PartyID: 1,
3335
Shards: shards,
36+
Consenter: consenter,
3437
NumOfConnectionsForBatcher: 1,
3538
NumOfgRPCStreamsPerConnection: 2,
3639
}
@@ -87,10 +90,12 @@ func TestConsenterNodeConfigToYaml(t *testing.T) {
8790
}
8891
shards := []ShardInfo{{ShardId: 1, Batchers: batchers}}
8992
consenters := []ConsenterInfo{{1, "127.0.0.1:7050", RawBytes("ConsenterPubKey-1"), []RawBytes{{1, 2, 3}, {4, 5, 6}}}}
93+
router := RouterInfo{1, "127.0.0.1:7050", []RawBytes{{1, 2, 3}, {4, 5, 6}}, RawBytes("ConsenterPubKey-1")}
9094

9195
cnc := &ConsenterNodeConfig{
9296
Shards: shards,
9397
Consenters: consenters,
98+
Router: router,
9499
PartyId: 1,
95100
TLSPrivateKeyFile: RawBytes("TlsPrivateKey"),
96101
TLSCertificateFile: RawBytes("TlsCertKey"),

node/consensus/consensus.go

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"encoding/asn1"
1313
"encoding/base64"
1414
"encoding/hex"
15+
"encoding/pem"
1516
"fmt"
1617
"io"
1718
"math/rand"
@@ -23,6 +24,7 @@ import (
2324
"github.com/hyperledger/fabric-protos-go-apiv2/common"
2425
"github.com/hyperledger/fabric-protos-go-apiv2/orderer"
2526
arma_types "github.com/hyperledger/fabric-x-orderer/common/types"
27+
"github.com/hyperledger/fabric-x-orderer/node"
2628
"github.com/hyperledger/fabric-x-orderer/node/comm"
2729
"github.com/hyperledger/fabric-x-orderer/node/config"
2830
"github.com/hyperledger/fabric-x-orderer/node/consensus/badb"
@@ -142,9 +144,14 @@ func (c *Consensus) NotifyEvent(stream protos.Consensus_NotifyEventServer) error
142144
}
143145

144146
// SubmitConfig is used to submit a config request from the router in the consenter's party.
145-
// TODO - add certificate pinning of the router, and forward the request.
146-
func (sc *Consensus) SubmitConfig(ctx context.Context, request *protos.Request) (*protos.SubmitResponse, error) {
147-
return nil, fmt.Errorf("SubmitConfig not implemented")
147+
func (c *Consensus) SubmitConfig(ctx context.Context, request *protos.Request) (*protos.SubmitResponse, error) {
148+
if err := c.validateRouterFromContext(ctx); err != nil {
149+
return nil, errors.Wrap(err, "failed to validate router from context")
150+
}
151+
152+
c.Logger.Infof("Received config request from router %s", c.Config.Router.Endpoint)
153+
154+
return &protos.SubmitResponse{Error: "SubmitConfig is not implemented in consenter", TraceId: request.TraceId}, nil
148155
}
149156

150157
func (c *Consensus) SubmitRequest(req []byte) error {
@@ -562,3 +569,25 @@ func (c *Consensus) verifyCE(req []byte) (smartbft_types.RequestInfo, *state.Con
562569
return smartbft_types.RequestInfo{}, ce, fmt.Errorf("empty control event")
563570
}
564571
}
572+
573+
func (c *Consensus) validateRouterFromContext(ctx context.Context) error {
574+
// extract the client certificate from the context
575+
cert := node.ExtractCertificateFromContext(ctx)
576+
if cert == nil {
577+
return errors.New("error: access denied; could not extract certificate from context")
578+
}
579+
580+
// extract the router certificate from the ConsenterNodeConfig
581+
rawRouterCert := c.Config.Router.TLSCert
582+
pemBlock, _ := pem.Decode(rawRouterCert)
583+
if pemBlock == nil || pemBlock.Bytes == nil {
584+
return errors.New("error decoding router TLS certificate")
585+
}
586+
587+
// compare the two certificates
588+
if !bytes.Equal(pemBlock.Bytes, cert.Raw) {
589+
c.Logger.Errorf("error: access denied. The client certificate does not match the router's certificate. \n client's certificate: \n %s \n %x \n ", node.CertificateToString(cert), cert.Raw)
590+
return errors.New("error: access denied. The client certificate does not match the router's certificate")
591+
}
592+
return nil
593+
}

0 commit comments

Comments
 (0)