github.com/s7techlab/cckit@v0.10.5/extensions/encryption/encryption_test.go (about)

     1  package encryption_test
     2  
     3  import (
     4  	"crypto/rand"
     5  	"encoding/base64"
     6  	"testing"
     7  
     8  	"github.com/hyperledger/fabric-protos-go/peer"
     9  	. "github.com/onsi/ginkgo"
    10  	. "github.com/onsi/gomega"
    11  
    12  	"github.com/s7techlab/cckit/examples/payment"
    13  	"github.com/s7techlab/cckit/examples/payment/schema"
    14  	"github.com/s7techlab/cckit/extensions/encryption"
    15  	"github.com/s7techlab/cckit/extensions/encryption/testdata"
    16  	enctest "github.com/s7techlab/cckit/extensions/encryption/testing"
    17  	identitytestdata "github.com/s7techlab/cckit/identity/testdata"
    18  	"github.com/s7techlab/cckit/state"
    19  	"github.com/s7techlab/cckit/state/mapping"
    20  	testcc "github.com/s7techlab/cckit/testing"
    21  	expectcc "github.com/s7techlab/cckit/testing/expect"
    22  )
    23  
    24  func TestEncryption(t *testing.T) {
    25  	RegisterFailHandler(Fail)
    26  	RunSpecs(t, "Router suite")
    27  }
    28  
    29  var (
    30  	Owner = identitytestdata.Certificates[0].MustIdentity(`SOME_MSP`)
    31  )
    32  
    33  var (
    34  	encryptOnDemandPaymentCC            *testcc.MockStub
    35  	encryptPaymentCC                    *testcc.MockStub
    36  	encryptPaymentCCWithEncStateContext *testcc.MockStub
    37  	externalCC                          *testcc.MockStub
    38  
    39  	encCCInvoker *enctest.MockStub
    40  
    41  	pType  = `SALE`
    42  	encKey []byte
    43  
    44  	// fixtures
    45  	pID1           = `id-1`
    46  	pAmount1 int32 = 111
    47  
    48  	pID2           = `id-2`
    49  	pAmount2 int32 = 222
    50  
    51  	pID3           = `id-3`
    52  	pAmount3 int32 = 333
    53  
    54  	encryptedPType, encryptedPId1 []byte
    55  	payment1                      *schema.Payment
    56  	encPayment1                   []byte
    57  	paymentMapper                 mapping.StateMapper
    58  	encPaymentNamespace           []byte
    59  	err                           error
    60  )
    61  
    62  var _ = Describe(`Router`, func() {
    63  
    64  	BeforeSuite(func() {
    65  		// Create encode key. In real case it can be calculated via ECDH
    66  		encKey = make([]byte, 32)
    67  		_, err = rand.Read(encKey)
    68  
    69  		Expect(err).NotTo(HaveOccurred())
    70  
    71  		//Create chaincode mock
    72  		encryptOnDemandPaymentCC = testcc.NewMockStub(
    73  			`paymentsEncOnDemand`,
    74  			payment.NewEncryptOnDemandPaymentCC())
    75  
    76  		encryptPaymentCC = testcc.NewMockStub(
    77  			`paymentsEnc`,
    78  			payment.NewEncryptPaymentCC())
    79  
    80  		encryptPaymentCCWithEncStateContext = testcc.NewMockStub(
    81  			`paymentsEncWithContext`,
    82  			payment.NewEncryptedPaymentCCWithEncStateContext())
    83  
    84  		encCCInvoker = enctest.NewMockStub(encryptPaymentCCWithEncStateContext, encKey)
    85  		encCCInvoker.DecryptInvokeResponse = true
    86  
    87  		externalCC = testcc.NewMockStub(`external`,
    88  			testdata.NewExternaldCC(`paymentsEncWithContext`, `payment-channel`))
    89  
    90  		// external cc have access to encrypted payment chaincode
    91  		externalCC.MockPeerChaincode(
    92  			`paymentsEncWithContext/payment-channel`,
    93  			encryptPaymentCCWithEncStateContext)
    94  
    95  		encryptedPType, err = encryption.Encrypt(encKey, pType)
    96  		Expect(err).To(BeNil())
    97  
    98  		encryptedPId1, err = encryption.Encrypt(encKey, pID1)
    99  		Expect(err).To(BeNil())
   100  
   101  		payment1 = &schema.Payment{
   102  			Type:   pType,
   103  			Id:     pID1,
   104  			Amount: pAmount1,
   105  		}
   106  
   107  		encPayment1, err = encryption.Encrypt(encKey, payment1)
   108  		Expect(err).To(BeNil())
   109  
   110  		paymentMapper, _ = payment.StateMappings.Get(&schema.Payment{})
   111  		Expect(err).To(BeNil())
   112  
   113  		// we know that Payment namespace contain only one part
   114  		encPaymentNamespace, err = encryption.Encrypt(encKey, paymentMapper.Namespace()[0])
   115  		Expect(err).To(BeNil())
   116  	})
   117  
   118  	Describe("Encrypting in demand with state mapping", func() {
   119  
   120  		It("Allow to init encrypt-on-demand payment chaincode without key in transient map", func() {
   121  			expectcc.ResponseOk(encryptOnDemandPaymentCC.Init())
   122  		})
   123  
   124  		It("Disallow to create encrypted payment providing unencrypted arguments", func() {
   125  			expectcc.ResponseError(
   126  				encryptOnDemandPaymentCC.WithTransient(encryption.TransientMapWithKey(encKey)).
   127  					Invoke(`paymentCreate`, pType, pID1, pAmount1), `args: decryption error`)
   128  		})
   129  
   130  		It("Allow to create encrypted payment", func() {
   131  			// encrypt all arguments
   132  			args, err := encryption.EncryptArgs(encKey, `paymentCreate`, pType, pID1, pAmount1)
   133  			Expect(err).To(BeNil())
   134  			Expect(len(args)).To(Equal(4))
   135  
   136  			Expect(err).To(BeNil())
   137  			Expect(args[1]).To(Equal(encryptedPType))
   138  			// second argument is encoded payment id
   139  			Expect(args[2]).To(Equal(encryptedPId1))
   140  
   141  			// invoke chaincode with encoded args and encKey via transientMap, receives encoded payment id
   142  			ccPId := expectcc.PayloadBytes(
   143  				encryptOnDemandPaymentCC.WithTransient(encryption.TransientMapWithKey(encKey)).
   144  					InvokeBytes(args...), encryptedPId1)
   145  
   146  			encEvent := <-encryptOnDemandPaymentCC.ChaincodeEventsChannel
   147  			decryptedEvent := encryption.MustDecryptEvent(encKey, encEvent)
   148  
   149  			Expect(decryptedEvent.Payload).To(BeEquivalentTo(
   150  				testcc.MustProtoMarshal(&schema.PaymentEvent{Type: pType, Id: pID1, Amount: pAmount1})))
   151  
   152  			Expect(decryptedEvent.EventName).To(Equal(`PaymentEvent`))
   153  
   154  			decryptedPaymentID, err := encryption.Decrypt(encKey, ccPId)
   155  			Expect(err).To(BeNil())
   156  			Expect(string(decryptedPaymentID)).To(Equal(pID1))
   157  		})
   158  
   159  		It("Allow to get encrypted payment by type and id", func() {
   160  			// encrypt all arguments
   161  			args, err := encryption.EncryptArgs(encKey, `paymentGet`, pType, pID1)
   162  			Expect(err).To(BeNil())
   163  			Expect(len(args)).To(Equal(3))
   164  
   165  			// Check that value is encrypted in chaincode state - use debugStateGet func
   166  			// without providing key in transient map - so we need to provide encrypted key
   167  			// and cause we dont't require key - state also returns unencrypted
   168  			expectcc.PayloadBytes(encryptOnDemandPaymentCC.Invoke(`debugStateGet`, []string{
   169  				base64.StdEncoding.EncodeToString(encPaymentNamespace),
   170  				base64.StdEncoding.EncodeToString(encryptedPType),
   171  				base64.StdEncoding.EncodeToString(encryptedPId1)}), encPayment1)
   172  
   173  			//returns unencrypted
   174  			paymentFromCC := expectcc.PayloadIs(
   175  				encryptOnDemandPaymentCC.WithTransient(encryption.TransientMapWithKey(encKey)).InvokeBytes(args...),
   176  				&schema.Payment{}).(*schema.Payment)
   177  
   178  			Expect(paymentFromCC).To(Equal(payment1))
   179  		})
   180  
   181  		It("Allow to get encrypted payments by type as unencrypted values", func() {
   182  			// MockInvoke sets key in transient map and encrypt input arguments
   183  			payments := expectcc.PayloadIs(
   184  				enctest.MockInvoke(encryptOnDemandPaymentCC, encKey, `paymentList`, pType),
   185  				&schema.PaymentList{}).(*schema.PaymentList)
   186  
   187  			Expect(payments.Items).To(HaveLen(1))
   188  			// Returned value is not encrypted
   189  			Expect(payments.Items[0].Id).To(Equal(pID1))
   190  		})
   191  
   192  		It("Allow to invoke with non encrypted data", func() {
   193  			expectcc.ResponseOk(encryptOnDemandPaymentCC.Invoke(`paymentCreate`, pType, pID2, pAmount2))
   194  		})
   195  
   196  		It("Allow to get non encrypted payment by type and id", func() {
   197  			//returns unencrypted
   198  			paymentFromCC := expectcc.PayloadIs(
   199  				encryptOnDemandPaymentCC.Query(`paymentGet`, pType, pID2), &schema.Payment{}).(*schema.Payment)
   200  
   201  			Expect(paymentFromCC.Id).To(Equal(pID2))
   202  		})
   203  	})
   204  
   205  	Describe("Encrypting required", func() {
   206  
   207  		It("Disallow to init without key in transient map", func() {
   208  			expectcc.ResponseError(encryptPaymentCC.Init())
   209  		})
   210  
   211  		It("Allow to init with key in transient map", func() {
   212  			expectcc.ResponseOk(encryptPaymentCC.WithTransient(encryption.TransientMapWithKey(encKey)).Init())
   213  		})
   214  
   215  		It("Disallow to create payment without providing key in encryptPaymentCC ", func() {
   216  			expectcc.ResponseError(encryptPaymentCC.Invoke(`paymentCreate`, pType, pID3, pAmount3),
   217  				encryption.ErrKeyNotDefinedInTransientMap)
   218  		})
   219  
   220  		It("Allow to create payment providing key in encryptPaymentCC ", func() {
   221  			// encode all arguments
   222  			expectcc.ResponseOk(enctest.MockInvoke(encryptPaymentCC, encKey, `paymentCreate`, pType, pID3, pAmount3))
   223  		})
   224  	})
   225  
   226  	Describe("Encrypted state context", func() {
   227  		It("Allow to init encrypt payment chaincodes", func() {
   228  			expectcc.ResponseOk(encryptPaymentCCWithEncStateContext.WithTransient(encryption.TransientMapWithKey(encKey)).Init())
   229  		})
   230  		//
   231  		It("Allow to create payment providing key in encryptPaymentCC ", func(done Done) {
   232  			events, closer := encryptPaymentCCWithEncStateContext.EventSubscription()
   233  
   234  			responsePayment := expectcc.PayloadIs(
   235  				// encCCInvoker encrypts args before passing to cc invoke and pass key in transient map
   236  				encCCInvoker.From(Owner).Invoke(`paymentCreate`, pType, pID1, pAmount3),
   237  				&schema.Payment{}).(*schema.Payment)
   238  
   239  			// we use encryption.MockStub DecryptInvokeResponse feature
   240  			Expect(responsePayment.Id).To(Equal(pID1))
   241  			Expect(responsePayment.Type).To(Equal(pType))
   242  			Expect(responsePayment.Amount).To(Equal(pAmount3))
   243  
   244  			//event name and payload is encrypted with key
   245  			Expect(<-events).To(BeEquivalentTo(encryption.MustEncryptEvent(encKey, &peer.ChaincodeEvent{
   246  				ChaincodeId: encCCInvoker.MockStub.Name,
   247  				TxId:        encCCInvoker.MockStub.LastTxID,
   248  				EventName:   `PaymentEvent`,
   249  				Payload: testcc.MustProtoMarshal(&schema.PaymentEvent{
   250  					Type:   pType,
   251  					Id:     pID1,
   252  					Amount: pAmount3,
   253  				}),
   254  			})))
   255  
   256  			_ = closer()
   257  			close(done)
   258  		}, 0.2)
   259  
   260  		It("Allow to get payment by type and id", func() {
   261  			// encCCInvoker encrypts args before passing to cc invoke and pass key in transient map
   262  			paymentFromCC := expectcc.PayloadIs(
   263  				encCCInvoker.From(Owner).Query(`paymentGet`, pType, pID1), &schema.Payment{}).(*schema.Payment)
   264  
   265  			//returned payload is unencrypted
   266  			Expect(paymentFromCC.Id).To(Equal(pID1))
   267  			Expect(paymentFromCC.Type).To(Equal(pType))
   268  			Expect(paymentFromCC.Amount).To(Equal(pAmount3))
   269  		})
   270  
   271  		It("Allow to get payment providing key using debugStateGet", func() {
   272  			// we didn't provide encrypting key,
   273  			// chaincode use ArgsDecrypt middleware, requiring key in transient map
   274  			// but we add exception for method debugStateGet
   275  			// for all chaincode methods, except stateGet @see example/payments/cc_enc_context_with_mapping.go
   276  			expectcc.ResponseOk(encCCInvoker.MockStub.Invoke(`debugStateGet`, []string{
   277  				base64.StdEncoding.EncodeToString(encPaymentNamespace),
   278  				base64.StdEncoding.EncodeToString(encryptedPType),
   279  				base64.StdEncoding.EncodeToString(encryptedPId1)}))
   280  		})
   281  
   282  		It("Allow to get encrypted payments by type as unencrypted values", func() {
   283  			paymentsFromCC := expectcc.PayloadIs(
   284  				encCCInvoker.Query(`paymentList`, pType), &schema.PaymentList{}).(*schema.PaymentList)
   285  
   286  			Expect(paymentsFromCC.Items).To(HaveLen(1))
   287  			// Returned value is not encrypted
   288  			Expect(paymentsFromCC.Items[0].Id).To(Equal(pID1))
   289  			Expect(paymentsFromCC.Items[0].Type).To(Equal(pType))
   290  			Expect(paymentsFromCC.Items[0].Amount).To(Equal(pAmount3))
   291  		})
   292  
   293  		It("Disallow to get payment by type and id without providing encrypting key in transient map", func() {
   294  			expectcc.ResponseError(encCCInvoker.MockStub.From(Owner).Query(`paymentGet`, pType, pID1),
   295  				encryption.ErrKeyNotDefinedInTransientMap)
   296  		})
   297  
   298  		It("Disallow to get non existent payment by type and id providing encrypting key in transient map", func() {
   299  			// key in error is not encrypted
   300  			expectcc.ResponseError(encCCInvoker.From(Owner).Query(`paymentGet`, pType, pID1+`NoExists`),
   301  				state.ErrKeyNotFound.Error()+`: Payment | SALE | id-1NoExists`)
   302  		})
   303  
   304  		It("Allow to get payment by type and id", func() {
   305  			//returns unencrypted
   306  			paymentFromCC := expectcc.PayloadIs(encCCInvoker.From(Owner).Query(`paymentGet`, pType, pID1),
   307  				&schema.Payment{}).(*schema.Payment)
   308  			Expect(paymentFromCC.Id).To(Equal(pID1))
   309  			Expect(paymentFromCC.Type).To(Equal(pType))
   310  			Expect(paymentFromCC.Amount).To(Equal(pAmount3))
   311  		})
   312  
   313  		It("Allow to get payment via external chaincode", func() {
   314  
   315  			paymentFromExtCC := expectcc.PayloadIs(externalCC.WithTransient(encryption.
   316  				TransientMapWithKey(encKey)).Query(`checkPayment`, pType, pID1), &schema.Payment{}).(*schema.Payment)
   317  			Expect(paymentFromExtCC.Id).To(Equal(pID1))
   318  			Expect(paymentFromExtCC.Type).To(Equal(pType))
   319  			Expect(paymentFromExtCC.Amount).To(Equal(pAmount3))
   320  		})
   321  
   322  	})
   323  })