github.com/trustbloc/kms-go@v1.1.2/crypto/tinkcrypto/primitive/composite/ecdh/ecdh_factory_test.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package ecdh 8 9 import ( 10 "crypto/ed25519" 11 "crypto/rand" 12 "encoding/base64" 13 "encoding/json" 14 "fmt" 15 "testing" 16 17 "github.com/golang/protobuf/proto" 18 "github.com/google/tink/go/aead" 19 "github.com/google/tink/go/core/primitiveset" 20 hybrid "github.com/google/tink/go/hybrid/subtle" 21 "github.com/google/tink/go/keyset" 22 commonpb "github.com/google/tink/go/proto/common_go_proto" 23 tinkpb "github.com/google/tink/go/proto/tink_go_proto" 24 "github.com/google/tink/go/signature" 25 "github.com/google/tink/go/subtle/random" 26 "github.com/google/tink/go/testkeyset" 27 "github.com/google/tink/go/testutil" 28 "github.com/stretchr/testify/require" 29 30 "github.com/trustbloc/kms-go/util/cryptoutil" 31 32 "github.com/trustbloc/kms-go/crypto/tinkcrypto/primitive/composite" 33 ecdhpb "github.com/trustbloc/kms-go/crypto/tinkcrypto/primitive/proto/ecdh_aead_go_proto" 34 ) 35 36 func TestECDHESFactory(t *testing.T) { 37 c := commonpb.EllipticCurveType_NIST_P256 38 primaryPtFmt := commonpb.EcPointFormat_UNCOMPRESSED 39 rawPtFmt := commonpb.EcPointFormat_COMPRESSED 40 primaryEncT := aead.AES128GCMKeyTemplate() 41 rawEncT := aead.AES256GCMKeyTemplate() 42 kt := ecdhpb.KeyType_EC 43 cek := random.GetRandomBytes(32) 44 45 primaryPrivProto := generateECDHAEADPrivateKey(t, c, primaryPtFmt, kt, primaryEncT, cek) 46 47 sPrimaryPriv, err := proto.Marshal(primaryPrivProto) 48 require.NoError(t, err) 49 50 primaryPrivKey := testutil.NewKey( 51 testutil.NewKeyData(nistpECDHKWPrivateKeyTypeURL, sPrimaryPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE), 52 tinkpb.KeyStatusType_ENABLED, 8, tinkpb.OutputPrefixType_RAW) 53 54 rawPrivProto := generateECDHAEADPrivateKey(t, c, rawPtFmt, kt, rawEncT, cek) 55 56 sRawPriv, err := proto.Marshal(rawPrivProto) 57 require.NoError(t, err) 58 59 rawPrivKey := testutil.NewKey( 60 testutil.NewKeyData(nistpECDHKWPrivateKeyTypeURL, sRawPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE), 61 tinkpb.KeyStatusType_ENABLED, 11, tinkpb.OutputPrefixType_RAW) 62 63 x25519XChachaPrivProto := generateECDHAEADPrivateKey(t, commonpb.EllipticCurveType_CURVE25519, primaryPtFmt, 64 ecdhpb.KeyType_OKP, aead.XChaCha20Poly1305KeyTemplate(), cek) 65 66 sX25519XChachaPriv, err := proto.Marshal(x25519XChachaPrivProto) 67 require.NoError(t, err) 68 69 x25519XChachaPrivKey := testutil.NewKey( 70 testutil.NewKeyData(x25519ECDHKWPrivateKeyTypeURL, sX25519XChachaPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE), 71 tinkpb.KeyStatusType_ENABLED, 15, tinkpb.OutputPrefixType_RAW) 72 73 privKeys := []*tinkpb.Keyset_Key{primaryPrivKey, rawPrivKey, x25519XChachaPrivKey} 74 privKeyset := testutil.NewKeyset(privKeys[0].KeyId, privKeys) 75 khPriv, err := testkeyset.NewHandle(privKeyset) 76 require.NoError(t, err) 77 78 xPrivKeys := []*tinkpb.Keyset_Key{x25519XChachaPrivKey, rawPrivKey, primaryPrivKey} 79 xPrivKeyset := testutil.NewKeyset(xPrivKeys[0].KeyId, xPrivKeys) 80 xKHPriv, err := testkeyset.NewHandle(xPrivKeyset) 81 require.NoError(t, err) 82 83 khPub, err := khPriv.Public() 84 require.NoError(t, err) 85 86 xKHPub, err := xKHPriv.Public() 87 require.NoError(t, err) 88 89 e, err := NewECDHEncrypt(khPub) 90 require.NoError(t, err) 91 92 d, err := NewECDHDecrypt(khPriv) 93 require.NoError(t, err) 94 95 xe, err := NewECDHEncrypt(xKHPub) 96 require.NoError(t, err) 97 98 xd, err := NewECDHDecrypt(xKHPriv) 99 require.NoError(t, err) 100 101 for i := 0; i < 1000; i++ { 102 pt := random.GetRandomBytes(20) 103 aadRndNb := random.GetRandomBytes(10) 104 105 // single recipient requires aad to be a valid marshaled JSON base64URL encoded 106 // since the encryption primitive now appends the recipient's headers to aad prior to encryption 107 // this is not required for multiple recipient encryption since aad does not include recipients headers. 108 aadJSON, err := json.Marshal(aadRndNb) 109 require.NoError(t, err) 110 111 aad, err := json.Marshal(&map[string]interface{}{"someFiled": json.RawMessage(aadJSON)}) 112 require.NoError(t, err) 113 114 aadStr := base64.RawURLEncoding.EncodeToString(aad) 115 aad = []byte(aadStr) 116 117 ct, err := e.Encrypt(pt, aad) 118 require.NoError(t, err) 119 120 xct, err := xe.Encrypt(pt, aad) 121 require.NoError(t, err) 122 123 // encrypt for single recipient will generate new AAD for recipient, extract it from ct 124 encData := &composite.EncryptedData{} 125 err = json.Unmarshal(ct, encData) 126 require.NoError(t, err) 127 128 gotpt, err := d.Decrypt(ct, aad) 129 require.NoError(t, err) 130 131 xgotpt, err := xd.Decrypt(xct, aad) 132 require.NoError(t, err) 133 134 require.EqualValues(t, pt, gotpt) 135 require.EqualValues(t, pt, xgotpt) 136 } 137 } 138 139 // ecdhAEADPublicKey returns a EcdhAeadPublicKey with specified parameters. 140 func ecdhAEADPublicKey(t *testing.T, c commonpb.EllipticCurveType, ptfmt commonpb.EcPointFormat, kt ecdhpb.KeyType, 141 encT *tinkpb.KeyTemplate, x, y, cek []byte) *ecdhpb.EcdhAeadPublicKey { 142 t.Helper() 143 144 return &ecdhpb.EcdhAeadPublicKey{ 145 Version: 0, 146 Params: &ecdhpb.EcdhAeadParams{ 147 KwParams: &ecdhpb.EcdhKwParams{ 148 CurveType: c, 149 KeyType: kt, 150 }, 151 EncParams: &ecdhpb.EcdhAeadEncParams{ 152 AeadEnc: encT, 153 CEK: cek, 154 }, 155 EcPointFormat: ptfmt, 156 }, 157 X: x, 158 Y: y, 159 } 160 } 161 162 // ecdhesAEADPrivateKey returns a EcdhAeadPrivateKey with specified parameters. 163 func ecdhesAEADPrivateKey(t *testing.T, p *ecdhpb.EcdhAeadPublicKey, d []byte) *ecdhpb.EcdhAeadPrivateKey { 164 t.Helper() 165 166 return &ecdhpb.EcdhAeadPrivateKey{ 167 Version: 0, 168 PublicKey: p, 169 KeyValue: d, 170 } 171 } 172 173 // generateECDHAEADPrivateKey generates a new EC key pair and returns the private key proto. 174 func generateECDHAEADPrivateKey(t *testing.T, c commonpb.EllipticCurveType, ptfmt commonpb.EcPointFormat, 175 kt ecdhpb.KeyType, encT *tinkpb.KeyTemplate, cek []byte) *ecdhpb.EcdhAeadPrivateKey { 176 t.Helper() 177 178 if ecdhpb.KeyType_OKP.String() == kt.String() { 179 return buildXChachaKey(t, ptfmt, encT, c, cek) 180 } 181 182 curve, err := hybrid.GetCurve(c.String()) 183 require.NoError(t, err) 184 185 pvt, err := hybrid.GenerateECDHKeyPair(curve) 186 require.NoError(t, err) 187 188 pubKey := ecdhAEADPublicKey(t, c, ptfmt, kt, encT, pvt.PublicKey.Point.X.Bytes(), pvt.PublicKey.Point.Y.Bytes(), 189 cek) 190 191 return ecdhesAEADPrivateKey(t, pubKey, pvt.D.Bytes()) 192 } 193 194 func buildXChachaKey(t *testing.T, ptfmt commonpb.EcPointFormat, encT *tinkpb.KeyTemplate, c commonpb.EllipticCurveType, 195 cek []byte) *ecdhpb.EcdhAeadPrivateKey { 196 pub, pvt, err := ed25519.GenerateKey(rand.Reader) 197 require.NoError(t, err) 198 199 x25519Pub, err := cryptoutil.PublicEd25519toCurve25519(pub) 200 require.NoError(t, err) 201 202 x25519Pvt, err := cryptoutil.SecretEd25519toCurve25519(pvt) 203 require.NoError(t, err) 204 205 params := &ecdhpb.EcdhAeadParams{ 206 KwParams: &ecdhpb.EcdhKwParams{ 207 KeyType: ecdhpb.KeyType_OKP, 208 CurveType: c, 209 }, 210 EncParams: &ecdhpb.EcdhAeadEncParams{ 211 AeadEnc: encT, 212 CEK: cek, 213 }, 214 EcPointFormat: ptfmt, 215 } 216 217 return &ecdhpb.EcdhAeadPrivateKey{ 218 Version: 0, 219 KeyValue: x25519Pvt, 220 PublicKey: &ecdhpb.EcdhAeadPublicKey{ 221 Version: 0, 222 Params: params, 223 X: x25519Pub, 224 }, 225 } 226 } 227 228 func TestECDHESFactoryWithBadKeysetType(t *testing.T) { 229 c := commonpb.EllipticCurveType_NIST_P384 230 primaryPtFmt := commonpb.EcPointFormat_UNCOMPRESSED 231 rawPtFmt := commonpb.EcPointFormat_COMPRESSED 232 primaryEncT := aead.AES128GCMKeyTemplate() 233 rawEncT := aead.AES256GCMKeyTemplate() 234 kt := ecdhpb.KeyType_EC 235 cek := random.GetRandomBytes(32) 236 237 primaryPrivProto := generateECDHAEADPrivateKey(t, c, primaryPtFmt, kt, primaryEncT, cek) 238 239 sPrimaryPriv, err := proto.Marshal(primaryPrivProto) 240 require.NoError(t, err) 241 242 primaryPrivKey := testutil.NewKey( 243 testutil.NewKeyData(nistpECDHKWPrivateKeyTypeURL, sPrimaryPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE), 244 tinkpb.KeyStatusType_ENABLED, 8, tinkpb.OutputPrefixType_RAW) 245 246 rawPrivProto := generateECDHAEADPrivateKey(t, c, rawPtFmt, kt, rawEncT, cek) 247 248 sRawPriv, err := proto.Marshal(rawPrivProto) 249 require.NoError(t, err) 250 251 rawPrivKey := testutil.NewKey( 252 testutil.NewKeyData(nistpECDHKWPrivateKeyTypeURL, sRawPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE), 253 tinkpb.KeyStatusType_ENABLED, 11, tinkpb.OutputPrefixType_RAW) 254 255 badPrivKeyProto, err := testutil.GenerateECIESAEADHKDFPrivateKey(c, commonpb.HashType_SHA256, primaryPtFmt, 256 aead.AES256GCMKeyTemplate(), []byte("some salt")) 257 require.NoError(t, err) 258 259 sBadKeyPriv, err := proto.Marshal(badPrivKeyProto) 260 require.NoError(t, err) 261 262 badKeyURLKeyTypeURL := "type.bad.type.url" 263 badPrivKey := testutil.NewKey( 264 testutil.NewKeyData(badKeyURLKeyTypeURL, sBadKeyPriv, tinkpb.KeyData_ASYMMETRIC_PRIVATE), 265 tinkpb.KeyStatusType_ENABLED, 12, tinkpb.OutputPrefixType_RAW) 266 267 privKeys := []*tinkpb.Keyset_Key{primaryPrivKey, rawPrivKey, badPrivKey} 268 privKeyset := testutil.NewKeyset(privKeys[0].KeyId, privKeys) 269 khPriv, err := testkeyset.NewHandle(privKeyset) 270 require.NoError(t, err) 271 require.NotEmpty(t, khPriv) 272 273 // calling Public() with a keyset containing an invalid key type should fail 274 _, err = khPriv.Public() 275 require.EqualError(t, err, fmt.Sprintf("keyset.Handle: registry.GetKeyManager: unsupported key type: %s", 276 badKeyURLKeyTypeURL)) 277 278 // creating new primitives with an invalid keyset (should be public keyset) should fail 279 e, err := NewECDHEncrypt(khPriv) 280 require.EqualError(t, err, fmt.Sprintf("ecdh_factory: cannot obtain primitive set: "+ 281 "registry.PrimitivesWithKeyManager: cannot get primitive from key: registry.GetKeyManager: "+ 282 "unsupported key type: %s", 283 badKeyURLKeyTypeURL)) 284 require.Empty(t, e) 285 286 // creating new primitives with a keyset containing an invalid key type should fail 287 d, err := NewECDHDecrypt(khPriv) 288 require.EqualError(t, err, fmt.Sprintf("ecdh_factory: cannot obtain primitive set: "+ 289 "registry.PrimitivesWithKeyManager: cannot get primitive from key: registry.GetKeyManager: "+ 290 "unsupported key type: %s", 291 badKeyURLKeyTypeURL)) 292 require.Empty(t, d) 293 } 294 295 func TestNewEncryptPrimitiveSetFail(t *testing.T) { 296 kh, err := keyset.NewHandle(signature.ECDSAP256KeyTemplate()) 297 require.NoError(t, err) 298 299 primitiveSet, err := kh.Primitives() 300 require.NoError(t, err) 301 302 // calling newEncryptPrimitiveSet with non CompositeEncrypt primitiveSet should fail 303 encPrimitiveSet, err := newEncryptPrimitiveSet(primitiveSet) 304 require.EqualError(t, err, "ecdh_factory: not a CompositeEncrypt primitive") 305 require.Nil(t, encPrimitiveSet) 306 307 validKH, err := keyset.NewHandle(NISTP256ECDHKWKeyTemplate()) 308 require.NoError(t, err) 309 310 validPubKH, err := validKH.Public() 311 require.NoError(t, err) 312 313 // primitives of a valid Public keyset.Handle do Encrypt() (while Private Handle do Decrypt()) 314 primitiveSet2, err := validPubKH.Primitives() 315 require.NoError(t, err) 316 317 // ensure calling newEncryptPrimitiveSet is successful with valid primitiveSet2 318 encPrimitiveSet, err = newEncryptPrimitiveSet(primitiveSet2) 319 require.NoError(t, err) 320 require.NotEmpty(t, encPrimitiveSet) 321 322 // create ECDSA key and add it to primitiveSet2 323 key := testutil.NewRandomECDSAPrivateKey(commonpb.HashType_SHA256, commonpb.EllipticCurveType_NIST_P256) 324 serializedKey, err := proto.Marshal(key) 325 require.NoError(t, err) 326 327 keyData := testutil.NewKeyData(testutil.ECDSASignerTypeURL, 328 serializedKey, 329 tinkpb.KeyData_ASYMMETRIC_PRIVATE) 330 privKey := testutil.NewKey(keyData, tinkpb.KeyStatusType_ENABLED, 109, tinkpb.OutputPrefixType_TINK) 331 332 // add invalid (signing) primitive to primitiveSet2 333 _, err = primitiveSet2.Add(primitiveSet.Primary.Primitive, privKey) 334 require.NoError(t, err) 335 336 // calling newEncryptPrimitiveSet with primitiveSet containing bad primitive entry should fail 337 encPrimitiveSet, err = newEncryptPrimitiveSet(primitiveSet2) 338 require.EqualError(t, err, "ecdh_factory: not a CompositeEncrypt primitive") 339 require.Nil(t, encPrimitiveSet) 340 } 341 342 func TestEncryptPrimitiveSetFail(t *testing.T) { 343 validKH, err := keyset.NewHandle(NISTP256ECDHKWKeyTemplate()) 344 require.NoError(t, err) 345 346 validPubKH, err := validKH.Public() 347 require.NoError(t, err) 348 349 // primitives of a valid Public keyset.Handle do Encrypt() (while Private Handle do Decrypt()) 350 primitiveSet, err := validPubKH.Primitives() 351 require.NoError(t, err) 352 353 // ensure calling newEncryptPrimitiveSet is successful with valid primitiveSet 354 encPrimitiveSet, err := newEncryptPrimitiveSet(primitiveSet) 355 require.NoError(t, err) 356 require.NotEmpty(t, encPrimitiveSet) 357 358 // Encrypt should fail as key set of primitive set do not have cek set 359 _, err = encPrimitiveSet.Encrypt([]byte("plaintext"), []byte("aad")) 360 require.EqualError(t, err, "ecdhAEADCompositeEncrypt: missing cek") 361 362 // create ECDSA key and set encPrimitiveSet's primary primitive to the ECDSA's primary 363 kh, err := keyset.NewHandle(signature.ECDSAP256KeyTemplate()) 364 require.NoError(t, err) 365 366 sigPS, err := kh.Primitives() 367 require.NoError(t, err) 368 369 encPrimitiveSet.ps.Primary = sigPS.Primary 370 371 // encrypting with signing key should fail 372 _, err = encPrimitiveSet.Encrypt([]byte("plaintext"), []byte("aad")) 373 require.EqualError(t, err, "ecdh_factory: not a CompositeEncrypt primitive") 374 } 375 376 func TestNewDecryptPrimitiveSetFail(t *testing.T) { 377 kh, err := keyset.NewHandle(signature.ECDSAP256KeyTemplate()) 378 require.NoError(t, err) 379 380 primitiveSet, err := kh.Primitives() 381 require.NoError(t, err) 382 383 // calling newEncryptPrimitiveSet with non CompositeEncrypt primitiveSet should fail 384 decPrimitiveSet, err := newDecryptPrimitiveSet(primitiveSet) 385 require.EqualError(t, err, "ecdh_factory: not a CompositeDecrypt primitive") 386 require.Nil(t, decPrimitiveSet) 387 388 validKH, err := keyset.NewHandle(NISTP256ECDHKWKeyTemplate()) 389 require.NoError(t, err) 390 391 // primitives of a valid Private keyset.Handle do Decrypt() (while Public Handle do Encrypt()) 392 primitiveSet2, err := validKH.Primitives() 393 require.NoError(t, err) 394 395 // ensure calling newDecryptPrimitiveSet is successful with valid primitiveSet2 396 decPrimitiveSet, err = newDecryptPrimitiveSet(primitiveSet2) 397 require.NoError(t, err) 398 require.NotEmpty(t, decPrimitiveSet) 399 400 // create ECDSA key and add it to primitiveSet2 401 key := testutil.NewRandomECDSAPrivateKey(commonpb.HashType_SHA256, commonpb.EllipticCurveType_NIST_P256) 402 serializedKey, err := proto.Marshal(key) 403 require.NoError(t, err) 404 405 keyData := testutil.NewKeyData(testutil.ECDSASignerTypeURL, 406 serializedKey, 407 tinkpb.KeyData_ASYMMETRIC_PRIVATE) 408 privKey := testutil.NewKey(keyData, tinkpb.KeyStatusType_ENABLED, 109, tinkpb.OutputPrefixType_TINK) 409 410 // add invalid (signing) primitive to primitiveSet2 411 _, err = primitiveSet2.Add(primitiveSet.Primary.Primitive, privKey) 412 require.NoError(t, err) 413 414 // calling newEncryptPrimitiveSet with primitiveSet containing bad primitive entry should fail 415 decPrimitiveSet, err = newDecryptPrimitiveSet(primitiveSet2) 416 require.EqualError(t, err, "ecdh_factory: not a CompositeDecrypt primitive") 417 require.Nil(t, decPrimitiveSet) 418 } 419 420 func TestDecryptPrimitiveSetFail(t *testing.T) { 421 validKH, err := keyset.NewHandle(NISTP256ECDHKWKeyTemplate()) 422 require.NoError(t, err) 423 424 validPubKH, err := validKH.Public() 425 require.NoError(t, err) 426 427 // primitives of a valid Private keyset.Handle do Decrypt() (while Public Handle do Encrypt()) 428 primitiveSet, err := validKH.Primitives() 429 require.NoError(t, err) 430 431 // ensure calling newEncryptPrimitiveSet is successful with valid primitiveSet 432 decPrimitiveSet, err := newDecryptPrimitiveSet(primitiveSet) 433 require.NoError(t, err) 434 require.NotEmpty(t, decPrimitiveSet) 435 436 // primitives of a valid Public Handle do Encrypt() so it should fail for newDecryptPrimitiveSet 437 primitiveSetBad, err := validPubKH.Primitives() 438 require.NoError(t, err) 439 440 // ensure calling newEncryptPrimitiveSet fails with invalid primitiveSetBad 441 _, err = newDecryptPrimitiveSet(primitiveSetBad) 442 require.EqualError(t, err, "ecdh_factory: not a CompositeDecrypt primitive") 443 444 // Decrypt invalid cipher should fail 445 _, err = decPrimitiveSet.Decrypt([]byte("plaintext"), []byte("aad")) 446 require.EqualError(t, err, "ecdh_factory: decryption failed") 447 448 // create ECDSA key and set decPrimitiveSet's primary primtive to the ECDSA's primary 449 kh, err := keyset.NewHandle(signature.ECDSAP256KeyTemplate()) 450 require.NoError(t, err) 451 452 sigPS, err := kh.Primitives() 453 require.NoError(t, err) 454 455 // try decrypt with invalid primitive as RAW prefix (type set fail) 456 decPrimitiveSet.ps.Entries[""] = []*primitiveset.Entry{sigPS.Primary} 457 decPrimitiveSet.ps.Primary = sigPS.Primary 458 459 _, err = decPrimitiveSet.Decrypt([]byte("plaintext"), []byte("aad")) 460 require.EqualError(t, err, "ecdh_factory: not a CompositeDecrypt primitive") 461 462 // try decrypt with invalid primitive and prefix (type set fail) 463 decPrimitiveSet.ps.Entries["12345"] = []*primitiveset.Entry{sigPS.Primary} 464 decPrimitiveSet.ps.Primary = sigPS.Primary 465 466 _, err = decPrimitiveSet.Decrypt([]byte("12345plaintext"), []byte("aad")) 467 require.EqualError(t, err, "ecdh_factory: not a CompositeDecrypt primitive") 468 469 // try decrypt with valid primitiveset with raw prefix and a non raw prefix (decryption fail with valid type) 470 primitiveSet, err = validKH.Primitives() 471 require.NoError(t, err) 472 473 decPrimitiveSet.ps.Entries[""] = []*primitiveset.Entry{primitiveSet.Primary} 474 decPrimitiveSet.ps.Primary = primitiveSet.Primary 475 decPrimitiveSet.ps.Entries["12345"] = []*primitiveset.Entry{primitiveSet.Primary} 476 decPrimitiveSet.ps.Primary = primitiveSet.Primary 477 478 _, err = decPrimitiveSet.Decrypt([]byte("12345plaintext"), []byte("aad")) 479 require.EqualError(t, err, "ecdh_factory: decryption failed") 480 }