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 })