|
| 1 | +package armageddon |
| 2 | + |
| 3 | +import ( |
| 4 | + "crypto/ecdsa" |
| 5 | + "crypto/elliptic" |
| 6 | + "crypto/rand" |
| 7 | + "crypto/sha256" |
| 8 | + "crypto/x509" |
| 9 | + "fmt" |
| 10 | + "math/big" |
| 11 | + "os" |
| 12 | + |
| 13 | + "github.com/hyperledger/fabric-protos-go-apiv2/common" |
| 14 | + "github.com/hyperledger/fabric-x-orderer/internal/cryptogen/ca" |
| 15 | + "github.com/hyperledger/fabric-x-orderer/node/crypto" |
| 16 | +) |
| 17 | + |
| 18 | +// SignedTransactionService holds signed transactions with the cryptographic keys. |
| 19 | +type SignedTransactionService struct { |
| 20 | + txs []*common.Envelope |
| 21 | + privateKey *ecdsa.PrivateKey |
| 22 | + certificate *x509.Certificate |
| 23 | +} |
| 24 | + |
| 25 | +func NewSignedTransactionService(numOfTxs int, txSize int) (*SignedTransactionService, error) { |
| 26 | + // Create private key and certificate used to sign and verify txs |
| 27 | + pk, cert, err := createSignerPKAndCert() |
| 28 | + if err != nil { |
| 29 | + return nil, err |
| 30 | + } |
| 31 | + |
| 32 | + // Create signed transactions |
| 33 | + txs, err := createSignedTransactions(numOfTxs, txSize, (*crypto.ECDSASigner)(pk)) |
| 34 | + if err != nil { |
| 35 | + return nil, err |
| 36 | + } |
| 37 | + |
| 38 | + service := &SignedTransactionService{ |
| 39 | + txs: txs, |
| 40 | + privateKey: pk, |
| 41 | + certificate: cert, |
| 42 | + } |
| 43 | + |
| 44 | + return service, nil |
| 45 | +} |
| 46 | + |
| 47 | +// GetRandomTransactionIndex returns a random number in [0, len(txs)). |
| 48 | +func (sts *SignedTransactionService) GetRandomTransactionIndex() (int, error) { |
| 49 | + n, err := rand.Int(rand.Reader, big.NewInt(int64(len(sts.txs)))) |
| 50 | + return int(n.Int64()), err |
| 51 | +} |
| 52 | + |
| 53 | +// VerifyTransaction verifies the transaction[txIndex] in the transactions list. |
| 54 | +func (sts *SignedTransactionService) VerifyTransaction(txIndex int) bool { |
| 55 | + // Get the transaction from the list |
| 56 | + envelope := sts.txs[txIndex] |
| 57 | + digest := sha256.Sum256(envelope.Payload) |
| 58 | + |
| 59 | + // Extract public key from the cert |
| 60 | + publicKey := sts.certificate.PublicKey.(*ecdsa.PublicKey) |
| 61 | + |
| 62 | + // Verify the signature over the digest with the public key |
| 63 | + valid := ecdsa.VerifyASN1(publicKey, digest[:], envelope.Signature) |
| 64 | + return valid |
| 65 | +} |
| 66 | + |
| 67 | +// createSignerPKAndCert create a CA, private key and a certificate. |
| 68 | +// NOTE: this function is based on Fabric internal methods. |
| 69 | +func createSignerPKAndCert() (*ecdsa.PrivateKey, *x509.Certificate, error) { |
| 70 | + // Create a fake CA |
| 71 | + dir, err := os.MkdirTemp("", "ca") |
| 72 | + if err != nil { |
| 73 | + return nil, nil, fmt.Errorf("failed to create a temp dir, err: %s", err) |
| 74 | + } |
| 75 | + defer os.RemoveAll(dir) |
| 76 | + |
| 77 | + signCA, err := ca.NewCA(dir, "signCA", "ca", "US", "California", "San Francisco", "ARMA", "addr", "12345", "ecdsa") |
| 78 | + if err != nil { |
| 79 | + return nil, nil, fmt.Errorf("failed to create a fake CA, err: %s", err) |
| 80 | + } |
| 81 | + |
| 82 | + // Create private key |
| 83 | + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) |
| 84 | + if err != nil { |
| 85 | + return nil, nil, fmt.Errorf("failed to create a private key, err: %s", err) |
| 86 | + } |
| 87 | + |
| 88 | + // Issue a certificate with the public key associated to the generated private key (the certificate contains the public key) |
| 89 | + cert, err := signCA.SignCertificate(dir, "signer", nil, nil, getPublicKey(privateKey), x509.KeyUsageDigitalSignature, []x509.ExtKeyUsage{}) |
| 90 | + if err != nil { |
| 91 | + return nil, nil, fmt.Errorf("failed to create a certificate, err: %s", err) |
| 92 | + } |
| 93 | + |
| 94 | + return privateKey, cert, err |
| 95 | +} |
| 96 | + |
| 97 | +func createSignedTransactions(numOfTxs int, txSize int, signer *crypto.ECDSASigner) ([]*common.Envelope, error) { |
| 98 | + sessionNumber := make([]byte, 16) |
| 99 | + _, err := rand.Read(sessionNumber) |
| 100 | + if err != nil { |
| 101 | + return nil, fmt.Errorf("failed to create a session number, err: %s", err) |
| 102 | + } |
| 103 | + |
| 104 | + txs := make([]*common.Envelope, numOfTxs) |
| 105 | + for i := 0; i < numOfTxs; i++ { |
| 106 | + payload := createPayload(i, txSize, sessionNumber) |
| 107 | + signedTx, err := signTransaction(payload, signer) |
| 108 | + if err != nil { |
| 109 | + return nil, fmt.Errorf("failed to sign transaction %d", i) |
| 110 | + } |
| 111 | + txs[i] = signedTx |
| 112 | + } |
| 113 | + |
| 114 | + return txs, nil |
| 115 | +} |
| 116 | + |
| 117 | +func createPayload(txNum int, txSize int, sessionNumber []byte) []byte { |
| 118 | + payload := prepareTx(txNum, txSize, sessionNumber) |
| 119 | + return payload |
| 120 | +} |
| 121 | + |
| 122 | +func signTransaction(payload []byte, signer *crypto.ECDSASigner) (*common.Envelope, error) { |
| 123 | + signature, err := signer.Sign(payload) |
| 124 | + if err != nil { |
| 125 | + return nil, err |
| 126 | + } |
| 127 | + |
| 128 | + envelope := &common.Envelope{ |
| 129 | + Payload: payload, |
| 130 | + Signature: signature, |
| 131 | + } |
| 132 | + return envelope, nil |
| 133 | +} |
0 commit comments