github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/packer/authcrypt/pack_test.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package authcrypt 8 9 import ( 10 "bytes" 11 "crypto/ecdsa" 12 "crypto/ed25519" 13 "crypto/elliptic" 14 "encoding/json" 15 "errors" 16 "fmt" 17 "io" 18 "strings" 19 "testing" 20 21 "github.com/go-jose/go-jose/v3" 22 "github.com/golang/protobuf/proto" 23 hybrid "github.com/google/tink/go/hybrid/subtle" 24 "github.com/google/tink/go/keyset" 25 commonpb "github.com/google/tink/go/proto/common_go_proto" 26 tinkpb "github.com/google/tink/go/proto/tink_go_proto" 27 "github.com/stretchr/testify/require" 28 29 ecdhpb "github.com/hyperledger/aries-framework-go/component/kmscrypto/crypto/tinkcrypto/primitive/proto/ecdh_aead_go_proto" 30 31 "github.com/hyperledger/aries-framework-go/pkg/common/log" 32 cryptoapi "github.com/hyperledger/aries-framework-go/pkg/crypto" 33 "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto" 34 "github.com/hyperledger/aries-framework-go/pkg/crypto/tinkcrypto/primitive/composite/keyio" 35 "github.com/hyperledger/aries-framework-go/pkg/didcomm/transport" 36 afgjose "github.com/hyperledger/aries-framework-go/pkg/doc/jose" 37 "github.com/hyperledger/aries-framework-go/pkg/doc/util/kmsdidkey" 38 mockvdr "github.com/hyperledger/aries-framework-go/pkg/internal/gomocks/framework/aries/api/vdr" 39 "github.com/hyperledger/aries-framework-go/pkg/kms" 40 "github.com/hyperledger/aries-framework-go/pkg/kms/localkms" 41 mockkms "github.com/hyperledger/aries-framework-go/pkg/mock/kms" 42 mockprovider "github.com/hyperledger/aries-framework-go/pkg/mock/provider" 43 mockstorage "github.com/hyperledger/aries-framework-go/pkg/mock/storage" 44 "github.com/hyperledger/aries-framework-go/pkg/secretlock/noop" 45 spilog "github.com/hyperledger/aries-framework-go/spi/log" 46 ) 47 48 func TestAuthcryptPackerSuccess(t *testing.T) { 49 k := createKMS(t) 50 51 tests := []struct { 52 name string 53 keyType kms.KeyType 54 encAlg afgjose.EncAlg 55 cty string 56 }{ 57 { 58 name: "authcrypt using NISTP256ECDHKW and AES128CBC+HMAC-SHA256", 59 keyType: kms.NISTP256ECDHKWType, 60 encAlg: afgjose.A128CBCHS256, 61 cty: transport.MediaTypeV1PlaintextPayload, 62 }, 63 { 64 name: "authcrypt using NISTP384ECDHKW and AES192CBC+HMAC-SHA384", 65 keyType: kms.NISTP384ECDHKWType, 66 encAlg: afgjose.A192CBCHS384, 67 cty: transport.MediaTypeV1PlaintextPayload, 68 }, 69 { 70 name: "authcrypt using NISTP521ECDHKW and AES256CBC+HMAC-SHA512", 71 keyType: kms.NISTP521ECDHKWType, 72 encAlg: afgjose.A256CBCHS512, 73 cty: transport.MediaTypeV1PlaintextPayload, 74 }, 75 { 76 name: "authcrypt using X25519ECDHKWType and AES128CBC+HMAC-SHA256", 77 keyType: kms.X25519ECDHKWType, 78 encAlg: afgjose.A128CBCHS256ALG, 79 cty: transport.MediaTypeV1PlaintextPayload, 80 }, 81 { 82 name: "authcrypt using NISTP256ECDHKW and XChacha20Poly1305", 83 keyType: kms.NISTP256ECDHKW, 84 encAlg: afgjose.XC20P, 85 cty: transport.MediaTypeV1PlaintextPayload, 86 }, 87 { 88 name: "authcrypt using NISTP384ECDHKW and XChacha20Poly1305", 89 keyType: kms.NISTP384ECDHKW, 90 encAlg: afgjose.XC20P, 91 cty: transport.MediaTypeV1PlaintextPayload, 92 }, 93 { 94 name: "authcrypt using NISTP521ECDHKW and XChacha20Poly1305", 95 keyType: kms.NISTP521ECDHKW, 96 encAlg: afgjose.XC20P, 97 cty: transport.MediaTypeV1PlaintextPayload, 98 }, 99 { 100 name: "authcrypt using X25519ECDHKWType and XChacha20Poly1305", 101 keyType: kms.X25519ECDHKWType, 102 encAlg: afgjose.XC20P, 103 cty: transport.MediaTypeV1PlaintextPayload, 104 }, 105 { 106 name: "authcrypt using NISTP256ECDHKW and AES192CBC+HMAC-SHA384", 107 keyType: kms.NISTP256ECDHKWType, 108 encAlg: afgjose.A192CBCHS384, 109 cty: transport.MediaTypeV1PlaintextPayload, 110 }, 111 { 112 name: "authcrypt using X25519ECDHKW and XChacha20Poly1305 without cty", 113 keyType: kms.X25519ECDHKWType, 114 encAlg: afgjose.XC20P, 115 cty: transport.MediaTypeV1PlaintextPayload, 116 }, 117 { 118 name: "authcrypt using NISTP256ECDHKW and XChacha20Poly1305 without cty", 119 keyType: kms.NISTP256ECDHKWType, 120 encAlg: afgjose.XC20P, 121 cty: transport.MediaTypeV1PlaintextPayload, 122 }, 123 { 124 name: "authcrypt using X25519ECDHKW and AES256-CBC+SHA512", 125 keyType: kms.X25519ECDHKWType, 126 encAlg: afgjose.A256CBCHS512, 127 cty: transport.MediaTypeV1PlaintextPayload, 128 }, 129 } 130 131 t.Parallel() 132 133 for _, tt := range tests { 134 tc := tt 135 t.Run(fmt.Sprintf("running %s", tc.name), func(t *testing.T) { 136 t.Logf("authcrypt packing - creating kid %s key...", tc.keyType) 137 skid, sDIDKey, mSenderPubKey, _ := createAndMarshalKeyByKeyType(t, k, tc.keyType) 138 139 t.Logf("authcrypt packing - creating recipient %s keys...", tc.keyType) 140 _, recDIDKeys, recipientsKeys, keyHandles := createRecipientsByKeyType(t, k, 3, tc.keyType) 141 142 log.SetLevel("aries-framework/pkg/didcomm/packer/authcrypt", spilog.DEBUG) 143 144 cryptoSvc, err := tinkcrypto.New() 145 require.NoError(t, err) 146 147 authPacker, err := New(newMockProvider(k, cryptoSvc), tc.encAlg) 148 require.NoError(t, err) 149 150 origMsg := []byte("secret message") 151 ct, err := authPacker.Pack(tc.cty, origMsg, []byte(skid+"."+sDIDKey), recipientsKeys) 152 require.NoError(t, err) 153 154 jweStr, err := prettyPrint(ct) 155 require.NoError(t, err) 156 t.Logf("* authcrypt JWE: %s", jweStr) 157 158 msg, err := authPacker.Unpack(ct) 159 require.NoError(t, err) 160 161 recKey, err := exportPubKeyBytes(keyHandles[0], recDIDKeys[0]) 162 require.NoError(t, err) 163 164 senderPubKey := &cryptoapi.PublicKey{} 165 166 err = json.Unmarshal(mSenderPubKey, senderPubKey) 167 require.NoError(t, err) 168 169 senderPubKey.KID = sDIDKey // match packer value. 170 171 mSenderPubKey, err = json.Marshal(senderPubKey) 172 require.NoError(t, err) 173 174 require.EqualValues(t, &transport.Envelope{Message: origMsg, FromKey: mSenderPubKey, ToKey: recKey}, msg) 175 176 jweJSON, err := afgjose.Deserialize(string(ct)) 177 require.NoError(t, err) 178 179 verifyJWETypes(t, tc.cty, jweJSON.ProtectedHeaders) 180 181 // try with only 1 recipient to force compact JWE serialization 182 ct, err = authPacker.Pack(tc.cty, origMsg, []byte(skid+"."+sDIDKey), [][]byte{recipientsKeys[0]}) 183 require.NoError(t, err) 184 185 t.Logf("* authcrypt JWE Compact serialization (using first recipient only): %s", ct) 186 187 jweJSON, err = afgjose.Deserialize(string(ct)) 188 require.NoError(t, err) 189 190 jweStr, err = jweJSON.FullSerialize(json.Marshal) 191 require.NoError(t, err) 192 t.Logf("* authcrypt Flattened JWE JSON serialization (using first recipient only): %s", jweStr) 193 194 msg, err = authPacker.Unpack(ct) 195 require.NoError(t, err) 196 197 require.EqualValues(t, &transport.Envelope{Message: origMsg, FromKey: mSenderPubKey, ToKey: recKey}, msg) 198 199 verifyJWETypes(t, tc.cty, jweJSON.ProtectedHeaders) 200 }) 201 } 202 } 203 204 func verifyJWETypes(t *testing.T, cty string, jweHeader afgjose.Headers) { 205 encodingType, ok := jweHeader.Type() 206 require.True(t, ok) 207 208 require.Equal(t, transport.MediaTypeV2EncryptedEnvelope, encodingType) 209 210 contentType, ok := jweHeader.ContentType() 211 require.True(t, contentType == "" || contentType != "" && ok) 212 213 require.Equal(t, cty, contentType) 214 } 215 216 func TestAuthcryptPackerUsingKeysWithDifferentCurvesSuccess(t *testing.T) { 217 k := createKMS(t) 218 _, recDIDKeys, recipientsKey1, keyHandles1 := createRecipients(t, k, 1) 219 // since authcrypt does ECDH kw using the sender key, the recipient keys must be on the same curve (for NIST P keys) 220 // and the same key type (for NIST P / X25519 keys) as the sender's. 221 // this is why recipient keys with different curves/type are not supported for authcrypt. 222 //nolint:dogsled 223 _, _, recipientsKey2, _ := createRecipients(t, k, 1) // can't create key with kms.NISTP384ECDHKW 224 //nolint:dogsled 225 _, _, recipientsKey3, _ := createRecipients(t, k, 1) // can't create key with kms.NISTP521ECDHKW 226 227 recipientsKeys := make([][]byte, 3) 228 recipientsKeys[0] = make([]byte, len(recipientsKey1[0])) 229 recipientsKeys[1] = make([]byte, len(recipientsKey2[0])) 230 recipientsKeys[2] = make([]byte, len(recipientsKey3[0])) 231 232 copy(recipientsKeys[0], recipientsKey1[0]) 233 copy(recipientsKeys[1], recipientsKey2[0]) 234 copy(recipientsKeys[2], recipientsKey3[0]) 235 236 cty := transport.MediaTypeV1PlaintextPayload 237 238 skid, sDIDKey, mSenderPubKey, _ := createAndMarshalKey(t, k) 239 240 cryptoSvc, err := tinkcrypto.New() 241 require.NoError(t, err) 242 243 authPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A256CBCHS512) 244 require.NoError(t, err) 245 246 origMsg := []byte("secret message") 247 ct, err := authPacker.Pack(cty, origMsg, []byte(skid+"."+sDIDKey), recipientsKeys) 248 require.NoError(t, err) 249 250 t.Logf("authcrypt JWE: %s", ct) 251 252 msg, err := authPacker.Unpack(ct) 253 require.NoError(t, err) 254 255 recKey, err := exportPubKeyBytes(keyHandles1[0], recDIDKeys[0]) 256 require.NoError(t, err) 257 258 senderPubKey := &cryptoapi.PublicKey{} 259 260 err = json.Unmarshal(mSenderPubKey, senderPubKey) 261 require.NoError(t, err) 262 263 senderPubKey.KID = sDIDKey // match packer value. 264 265 mSenderPubKey, err = json.Marshal(senderPubKey) 266 require.NoError(t, err) 267 268 require.EqualValues(t, &transport.Envelope{ 269 Message: origMsg, 270 FromKey: mSenderPubKey, 271 ToKey: recKey, 272 }, msg) 273 274 // try with only 1 recipient 275 ct, err = authPacker.Pack(cty, origMsg, []byte(skid+"."+sDIDKey), [][]byte{recipientsKeys[0]}) 276 require.NoError(t, err) 277 278 msg, err = authPacker.Unpack(ct) 279 require.NoError(t, err) 280 281 senderPubKey = &cryptoapi.PublicKey{} 282 283 err = json.Unmarshal(mSenderPubKey, senderPubKey) 284 require.NoError(t, err) 285 286 senderPubKey.KID = sDIDKey // match packer value. 287 288 mSenderPubKey, err = json.Marshal(senderPubKey) 289 require.NoError(t, err) 290 291 require.EqualValues(t, &transport.Envelope{ 292 Message: origMsg, 293 FromKey: mSenderPubKey, 294 ToKey: recKey, 295 }, msg) 296 297 jweJSON, err := afgjose.Deserialize(string(ct)) 298 require.NoError(t, err) 299 300 verifyJWETypes(t, cty, jweJSON.ProtectedHeaders) 301 } 302 303 func TestAuthcryptPackerFail(t *testing.T) { 304 cty := transport.MediaTypeV1PlaintextPayload 305 k := createKMS(t) 306 307 cryptoSvc, err := tinkcrypto.New() 308 require.NoError(t, err) 309 310 skid, sDIDKey, _, _ := createAndMarshalKey(t, k) 311 skidB := []byte(skid + "." + sDIDKey) 312 313 t.Run("new Pack fail with nil crypto service", func(t *testing.T) { 314 _, err = New(newMockProvider(k, nil), afgjose.A128CBCHS256) 315 require.EqualError(t, err, "authcrypt: failed to create packer because crypto service is empty") 316 }) 317 318 t.Run("new Pack fail with invalid encryption algorithm", func(t *testing.T) { 319 _, err = New(newMockProvider(k, cryptoSvc), "invalidAlg") 320 require.EqualError(t, err, "authcrypt: unsupported content encrytpion algorithm: invalidAlg") 321 }) 322 323 t.Run("new Pack fail with nil kms", func(t *testing.T) { 324 _, err = New(newMockProvider(nil, cryptoSvc), afgjose.A128CBCHS256) 325 require.EqualError(t, err, "authcrypt: failed to create packer because KMS is empty") 326 }) 327 328 _, _, recipientsKeys, _ := createRecipients(t, k, 10) //nolint:dogsled 329 origMsg := []byte("secret message") 330 authPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A256CBCHS512) 331 require.NoError(t, err) 332 333 t.Run("unpack fail with bad recipient key", func(t *testing.T) { 334 _, _, keys, _ := createRecipients(t, k, 1) 335 keys[0] = []byte(strings.Replace(string(keys[0]), "did:key:", "invalid", 1)) 336 var ct []byte 337 ct, err = authPacker.Pack(cty, origMsg, []byte(skid+"."+sDIDKey), keys) 338 require.NoError(t, err) 339 _, err = authPacker.Unpack(ct) 340 require.Contains(t, err.Error(), "invalid kid format, must be a did:key") 341 }) 342 343 t.Run("pack fail with empty recipients keys", func(t *testing.T) { 344 _, err = authPacker.Pack(cty, origMsg, nil, nil) 345 require.EqualError(t, err, "authcrypt Pack: empty recipientsPubKeys") 346 }) 347 348 t.Run("pack fail with invalid recipients keys", func(t *testing.T) { 349 _, err = authPacker.Pack(cty, origMsg, nil, [][]byte{[]byte("invalid")}) 350 require.EqualError(t, err, "authcrypt Pack: failed to convert recipient keys: invalid character 'i' "+ 351 "looking for beginning of value") 352 }) 353 354 t.Run("pack fail with invalid encAlg", func(t *testing.T) { 355 invalidAlg := "invalidAlg" 356 invalidAuthPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A256CBCHS512) 357 require.NoError(t, err) 358 359 invalidAuthPacker.encAlg = afgjose.EncAlg(invalidAlg) 360 _, err = invalidAuthPacker.Pack(cty, origMsg, skidB, recipientsKeys) 361 require.EqualError(t, err, fmt.Sprintf("authcrypt Pack: failed to new JWEEncrypt instance: encryption"+ 362 " algorithm '%s' not supported", invalidAlg)) 363 }) 364 365 t.Run("pack fail with KMS can't get kid key", func(t *testing.T) { 366 badKMSStoreProvider := mockstorage.NewCustomMockStoreProvider( 367 &mockstorage.MockStore{ErrGet: errors.New("bad fake key ID")}) 368 p, err := mockkms.NewProviderForKMS(badKMSStoreProvider, &noop.NoLock{}) 369 require.NoError(t, err) 370 371 badKMS, err := localkms.New("local-lock://test/key/uri", p) 372 require.NoError(t, err) 373 374 badAuthPacker, err := New(newMockProvider(badKMS, cryptoSvc), afgjose.A128CBCHS256) 375 require.NoError(t, err) 376 377 _, err = badAuthPacker.Pack(cty, origMsg, skidB, recipientsKeys) 378 require.Contains(t, fmt.Sprintf("%v", err), "bad fake key ID") 379 }) 380 381 t.Run("pack success but unpack fails with invalid payload format", func(t *testing.T) { 382 validAuthPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A192CBCHS384) 383 require.NoError(t, err) 384 385 _, err = validAuthPacker.Pack(cty, origMsg, skidB, recipientsKeys) 386 require.NoError(t, err) 387 388 _, err = validAuthPacker.Unpack([]byte("invalid jwe envelope")) 389 require.Error(t, err) 390 require.Contains(t, err.Error(), "authcrypt Unpack: failed to deserialize JWE message: invalid compact "+ 391 "JWE: it must have five parts") 392 }) 393 394 t.Run("pack success but unpack fails with invalid payload auth (iv) data", func(t *testing.T) { 395 validAuthPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A192CBCHS384) 396 require.NoError(t, err) 397 398 var s []byte 399 400 s, err = validAuthPacker.Pack(cty, origMsg, skidB, recipientsKeys) 401 require.NoError(t, err) 402 403 ivStartIndex := bytes.Index(s, []byte("\"iv\"")) 404 ivEndIndex := ivStartIndex + 6 + bytes.Index(s[ivStartIndex+6:], []byte("\"")) 405 sTrail := make([]byte, len(s[ivEndIndex:])) 406 copy(sTrail, s[ivEndIndex:]) 407 s = append(s[:ivStartIndex+6], []byte("K3ORqVx392nLcdJveUl_Jg")...) // invalid base64 iv causes decryption error 408 s = append(s, sTrail...) 409 410 _, err = validAuthPacker.Unpack(s) 411 require.Error(t, err) 412 require.Contains(t, err.Error(), "authcrypt Unpack: failed to decrypt JWE envelope: ecdh_factory: "+ 413 "decryption failed") 414 }) 415 416 t.Run("pack success but unpack fails with missing keyID in protectedHeader", func(t *testing.T) { 417 validAuthPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A192CBCHS384) 418 require.NoError(t, err) 419 420 ct, err := validAuthPacker.Pack(cty, origMsg, skidB, [][]byte{recipientsKeys[0]}) 421 require.NoError(t, err) 422 423 jwe, err := afgjose.Deserialize(string(ct)) 424 require.NoError(t, err) 425 426 delete(jwe.ProtectedHeaders, afgjose.HeaderKeyID) 427 428 newCT, err := jwe.CompactSerialize(json.Marshal) 429 require.NoError(t, err) 430 431 _, err = validAuthPacker.Unpack([]byte(newCT)) 432 require.EqualError(t, err, "authcrypt Unpack: single recipient missing 'KID' in jwe.ProtectHeaders") 433 }) 434 435 t.Run("pack success but unpack fails with missing kid in kms", func(t *testing.T) { 436 kids, _, newRecKeys, _ := createRecipients(t, k, 2) 437 validAuthPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A128CBCHS256) 438 require.NoError(t, err) 439 440 ct, err := validAuthPacker.Pack(cty, origMsg, skidB, newRecKeys) 441 require.NoError(t, err) 442 443 // rotate keys to update keyID and force a failure 444 _, _, err = k.Rotate(kms.NISTP256ECDHKWType, kids[0]) 445 require.NoError(t, err) 446 447 _, _, err = k.Rotate(kms.NISTP256ECDHKWType, kids[1]) 448 require.NoError(t, err) 449 450 _, err = validAuthPacker.Unpack(ct) 451 require.EqualError(t, err, "authcrypt Unpack: no matching recipient in envelope") 452 }) 453 454 t.Run("pack success but unpack fails with missing kms in packer", func(t *testing.T) { 455 kids, _, newRecKeys, _ := createRecipients(t, k, 2) 456 validAuthPacker, err := New(newMockProvider(k, cryptoSvc), afgjose.A128CBCHS256) 457 require.NoError(t, err) 458 459 ct, err := validAuthPacker.Pack(cty, origMsg, skidB, newRecKeys) 460 require.NoError(t, err) 461 462 // rotate keys to update keyID and force a failure 463 _, _, err = k.Rotate(kms.NISTP256ECDHKWType, kids[0]) 464 require.NoError(t, err) 465 466 _, _, err = k.Rotate(kms.NISTP256ECDHKWType, kids[1]) 467 require.NoError(t, err) 468 469 // mock kms get error 470 validAuthPacker.kms = &mockkms.KeyManager{GetKeyErr: errors.New("get error")} 471 472 _, err = validAuthPacker.Unpack(ct) 473 require.EqualError(t, err, "authcrypt Unpack: failed to get key from kms: get error") 474 }) 475 } 476 477 func exportPubKeyBytes(keyHandle *keyset.Handle, kid string) ([]byte, error) { 478 pubKH, err := keyHandle.Public() 479 if err != nil { 480 return nil, err 481 } 482 483 buf := new(bytes.Buffer) 484 pubKeyWriter := keyio.NewWriter(buf) 485 486 err = pubKH.WriteWithNoSecrets(pubKeyWriter) 487 if err != nil { 488 return nil, err 489 } 490 491 pubKey := &cryptoapi.PublicKey{} 492 493 err = json.Unmarshal(buf.Bytes(), pubKey) 494 if err != nil { 495 return nil, err 496 } 497 498 pubKey.KID = kid 499 500 return json.Marshal(pubKey) 501 } 502 503 // createRecipients and return their public key, jwk kid, didKey and keyset.Handle. 504 func createRecipients(t *testing.T, k *localkms.LocalKMS, 505 recipientsCount int) ([]string, []string, [][]byte, []*keyset.Handle) { 506 return createRecipientsByKeyType(t, k, recipientsCount, kms.NISTP256ECDHKW) 507 } 508 509 func createRecipientsByKeyType(t *testing.T, k *localkms.LocalKMS, recipientsCount int, 510 kt kms.KeyType) ([]string, []string, [][]byte, []*keyset.Handle) { 511 t.Helper() 512 513 var ( 514 r [][]byte 515 rKH []*keyset.Handle 516 kids []string 517 didKeys []string 518 ) 519 520 for i := 0; i < recipientsCount; i++ { 521 kid, didKey, marshalledPubKey, kh := createAndMarshalKeyByKeyType(t, k, kt) 522 523 r = append(r, marshalledPubKey) 524 rKH = append(rKH, kh) 525 kids = append(kids, kid) 526 didKeys = append(didKeys, didKey) 527 } 528 529 return kids, didKeys, r, rKH 530 } 531 532 // createAndMarshalKey creates a new recipient keyset.Handle, extracts public key, marshals it and returns 533 // both marshalled public key and original recipient keyset.Handle. 534 func createAndMarshalKey(t *testing.T, k *localkms.LocalKMS) (string, string, []byte, *keyset.Handle) { 535 return createAndMarshalKeyByKeyType(t, k, kms.NISTP256ECDHKWType) 536 } 537 538 func createAndMarshalKeyByKeyType(t *testing.T, k *localkms.LocalKMS, 539 kt kms.KeyType) (string, string, []byte, *keyset.Handle) { 540 t.Helper() 541 542 kid, keyHandle, err := k.Create(kt) 543 require.NoError(t, err) 544 545 kh, ok := keyHandle.(*keyset.Handle) 546 require.True(t, ok) 547 548 pubKeyBytes, err := exportPubKeyBytes(kh, kid) 549 require.NoError(t, err) 550 551 key := &cryptoapi.PublicKey{} 552 err = json.Unmarshal(pubKeyBytes, key) 553 require.NoError(t, err) 554 555 // used with marshalled *crypto.PublicKey for encryption keys (it parses 'pubKeyBytes'). 556 didKey, err := kmsdidkey.BuildDIDKeyByKeyType(pubKeyBytes, kt) 557 require.NoError(t, err) 558 559 key.KID = didKey 560 mKey, err := json.Marshal(key) 561 require.NoError(t, err) 562 563 printKey(t, mKey, kh, kid, didKey) 564 565 return kid, didKey, mKey, kh 566 } 567 568 func printKey(t *testing.T, mPubKey []byte, kh *keyset.Handle, kid, didKey string) { 569 t.Helper() 570 571 extractKey, err := extractPrivKey(kh) 572 require.NoError(t, err) 573 574 switch keyType := extractKey.(type) { 575 case *hybrid.ECPrivateKey: 576 t.Logf("** EC key: %s, \n\t kms kid: %s, \n\t jwe kid (did:key):%s", getPrintedECPrivKey(t, keyType), kid, 577 didKey) 578 case []byte: 579 pubKey := new(cryptoapi.PublicKey) 580 err := json.Unmarshal(mPubKey, pubKey) 581 require.NoError(t, err) 582 583 fullKey := append(keyType, pubKey.X...) 584 t.Logf("** X25519 key: %s, \n\t kms kid: %s, \n\t jwe kid (did:key):%s", getPrintedX25519PrivKey(t, fullKey), kid, 585 didKey) 586 default: 587 t.Errorf("not supported key type: %s", keyType) 588 } 589 } 590 591 func prettyPrint(msg []byte) (string, error) { 592 var prettyJSON bytes.Buffer 593 594 err := json.Indent(&prettyJSON, msg, "", "\t") 595 if err != nil { 596 return "", err 597 } 598 599 return prettyJSON.String(), nil 600 } 601 602 func getPrintedECPrivKey(t *testing.T, privKeyType *hybrid.ECPrivateKey) string { 603 jwk := jose.JSONWebKey{ 604 Key: &ecdsa.PrivateKey{ 605 PublicKey: ecdsa.PublicKey{ 606 Curve: privKeyType.PublicKey.Curve, 607 X: privKeyType.PublicKey.Point.X, 608 Y: privKeyType.PublicKey.Point.Y, 609 }, 610 D: privKeyType.D, 611 }, 612 } 613 614 jwkByte, err := jwk.MarshalJSON() 615 require.NoError(t, err) 616 617 jwkStr, err := prettyPrint(jwkByte) 618 require.NoError(t, err) 619 620 return jwkStr 621 } 622 623 func getPrintedX25519PrivKey(t *testing.T, privKeyType ed25519.PrivateKey) string { 624 jwk := jose.JSONWebKey{ 625 Key: privKeyType, 626 } 627 628 jwkByte, err := jwk.MarshalJSON() 629 require.NoError(t, err) 630 631 jwkStr, err := prettyPrint(jwkByte) 632 require.NoError(t, err) 633 634 return strings.Replace(jwkStr, "Ed25519", "X25519", 1) 635 } 636 637 func extractPrivKey(kh *keyset.Handle) (interface{}, error) { 638 nistPECDHKWPrivateKeyTypeURL := "type.hyperledger.org/hyperledger.aries.crypto.tink.NistPEcdhKwPrivateKey" 639 x25519ECDHKWPrivateKeyTypeURL := "type.hyperledger.org/hyperledger.aries.crypto.tink.X25519EcdhKwPrivateKey" 640 buf := new(bytes.Buffer) 641 w := &privKeyWriter{w: buf} 642 nAEAD := &noopAEAD{} 643 644 if kh == nil { 645 return nil, fmt.Errorf("extractPrivKey: kh is nil") 646 } 647 648 err := kh.Write(w, nAEAD) 649 if err != nil { 650 return nil, fmt.Errorf("extractPrivKey: retrieving private key failed: %w", err) 651 } 652 653 ks := new(tinkpb.Keyset) 654 655 err = proto.Unmarshal(buf.Bytes(), ks) 656 if err != nil { 657 return nil, errors.New("extractPrivKey: invalid private key") 658 } 659 660 primaryKey := ks.Key[0] 661 662 switch primaryKey.KeyData.TypeUrl { 663 case nistPECDHKWPrivateKeyTypeURL: 664 pbKey := new(ecdhpb.EcdhAeadPrivateKey) 665 666 err = proto.Unmarshal(primaryKey.KeyData.Value, pbKey) 667 if err != nil { 668 return nil, errors.New("extractPrivKey: invalid key in keyset") 669 } 670 671 var c elliptic.Curve 672 673 c, err = hybrid.GetCurve(pbKey.PublicKey.Params.KwParams.CurveType.String()) 674 if err != nil { 675 return nil, fmt.Errorf("extractPrivKey: invalid key: %w", err) 676 } 677 678 return hybrid.GetECPrivateKey(c, pbKey.KeyValue), nil 679 case x25519ECDHKWPrivateKeyTypeURL: 680 pbKey := new(ecdhpb.EcdhAeadPrivateKey) 681 682 err = proto.Unmarshal(primaryKey.KeyData.Value, pbKey) 683 if err != nil { 684 return nil, errors.New("extractPrivKey: invalid key in keyset") 685 } 686 687 if pbKey.PublicKey.Params.KwParams.CurveType.String() != commonpb.EllipticCurveType_CURVE25519.String() { 688 return nil, errors.New("extractPrivKey: invalid key curve") 689 } 690 691 return pbKey.KeyValue, nil 692 } 693 694 return nil, fmt.Errorf("extractPrivKey: can't extract unsupported private key '%s'", primaryKey.KeyData.TypeUrl) 695 } 696 697 type noopAEAD struct{} 698 699 func (n noopAEAD) Encrypt(plaintext, additionalData []byte) ([]byte, error) { 700 return plaintext, nil 701 } 702 703 func (n noopAEAD) Decrypt(ciphertext, additionalData []byte) ([]byte, error) { 704 return ciphertext, nil 705 } 706 707 type privKeyWriter struct { 708 w io.Writer 709 } 710 711 // Write writes the public keyset to the underlying w.Writer. It's not used in this implementation. 712 func (p *privKeyWriter) Write(_ *tinkpb.Keyset) error { 713 return fmt.Errorf("privKeyWriter: write function not supported") 714 } 715 716 // WriteEncrypted writes the encrypted keyset to the underlying w.Writer. 717 func (p *privKeyWriter) WriteEncrypted(ks *tinkpb.EncryptedKeyset) error { 718 return write(p.w, ks) 719 } 720 721 func write(w io.Writer, ks *tinkpb.EncryptedKeyset) error { 722 // we write EncryptedKeyset directly without decryption since noopAEAD was used to write *keyset.Handle 723 _, e := w.Write(ks.EncryptedKeyset) 724 return e 725 } 726 727 func createKMS(t *testing.T) *localkms.LocalKMS { 728 t.Helper() 729 730 p, err := mockkms.NewProviderForKMS(mockstorage.NewMockStoreProvider(), &noop.NoLock{}) 731 require.NoError(t, err) 732 733 k, err := localkms.New("local-lock://test/key/uri", p) 734 require.NoError(t, err) 735 736 return k 737 } 738 739 func newMockProvider(customKMS kms.KeyManager, 740 customCrypto cryptoapi.Crypto) *mockprovider.Provider { 741 return &mockprovider.Provider{ 742 KMSValue: customKMS, 743 CryptoValue: customCrypto, 744 VDRegistryValue: &mockvdr.MockRegistry{}, 745 } 746 }