github.com/trustbloc/kms-go@v1.1.2/crypto/tinkcrypto/wrap_support_test.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package tinkcrypto 8 9 import ( 10 "crypto/ecdsa" 11 "crypto/elliptic" 12 _ "embed" 13 "encoding/base64" 14 "encoding/hex" 15 "encoding/json" 16 "testing" 17 18 "github.com/stretchr/testify/require" 19 "golang.org/x/crypto/chacha20poly1305" 20 "golang.org/x/crypto/curve25519" 21 22 "github.com/trustbloc/kms-go/util/cryptoutil" 23 ) 24 25 func Test_ecKWSupportFailures(t *testing.T) { 26 ecKW := &ecKWSupport{} 27 28 _, err := ecKW.wrap("badCipherBlockType", []byte("")) 29 require.EqualError(t, err, "wrap support: EC wrap with invalid cipher block type") 30 31 _, err = ecKW.unwrap("badCipherBlockType", []byte("")) 32 require.EqualError(t, err, "unwrap support: EC wrap with invalid cipher block type") 33 34 _, err = ecKW.deriveSender1Pu("", nil, nil, nil, "badEphemeralPrivKeyType", nil, nil, 0) 35 require.EqualError(t, err, "deriveSender1Pu: ephemeral key not ECDSA type") 36 37 _, err = ecKW.deriveSender1Pu("", nil, nil, nil, &ecdsa.PrivateKey{}, "badSenderPrivKeyType", nil, 0) 38 require.EqualError(t, err, "deriveSender1Pu: sender key not ECDSA type") 39 40 _, err = ecKW.deriveSender1Pu("", nil, nil, nil, &ecdsa.PrivateKey{}, &ecdsa.PrivateKey{}, "badSenderPrivKeyType", 0) 41 require.EqualError(t, err, "deriveSender1Pu: recipient key not ECDSA type") 42 43 _, err = ecKW.deriveSender1Pu("", nil, nil, nil, &ecdsa.PrivateKey{ 44 PublicKey: ecdsa.PublicKey{Curve: elliptic.P256()}, 45 }, &ecdsa.PrivateKey{ 46 PublicKey: ecdsa.PublicKey{Curve: elliptic.P521()}, 47 }, &ecdsa.PublicKey{Curve: elliptic.P521()}, 0) 48 require.EqualError(t, err, "deriveSender1Pu: recipient, sender and ephemeral key are not on the same curve") 49 50 _, err = ecKW.deriveSender1Pu("", nil, nil, nil, &ecdsa.PrivateKey{ 51 PublicKey: ecdsa.PublicKey{Curve: elliptic.P521()}, 52 }, &ecdsa.PrivateKey{ 53 PublicKey: ecdsa.PublicKey{Curve: elliptic.P256()}, 54 }, &ecdsa.PublicKey{Curve: elliptic.P521()}, 0) 55 require.EqualError(t, err, "deriveSender1Pu: recipient, sender and ephemeral key are not on the same curve") 56 57 _, err = ecKW.deriveRecipient1Pu("", nil, nil, nil, "badEphemeralPrivKeyType", nil, nil, 0) 58 require.EqualError(t, err, "deriveRecipient1Pu: ephemeral key not ECDSA type") 59 60 _, err = ecKW.deriveRecipient1Pu("", nil, nil, nil, &ecdsa.PublicKey{}, "badSenderPrivKeyType", nil, 0) 61 require.EqualError(t, err, "deriveRecipient1Pu: sender key not ECDSA type") 62 63 _, err = ecKW.deriveRecipient1Pu("", nil, nil, nil, &ecdsa.PublicKey{}, &ecdsa.PublicKey{}, "badSenderPrivKeyType", 0) 64 require.EqualError(t, err, "deriveRecipient1Pu: recipient key not ECDSA type") 65 66 _, err = ecKW.deriveRecipient1Pu("", nil, nil, nil, &ecdsa.PublicKey{Curve: elliptic.P521()}, 67 &ecdsa.PublicKey{Curve: elliptic.P521()}, &ecdsa.PrivateKey{PublicKey: ecdsa.PublicKey{Curve: elliptic.P256()}}, 0) 68 require.EqualError(t, err, "deriveRecipient1Pu: recipient, sender and ephemeral key are not on the same curve") 69 70 _, err = ecKW.deriveRecipient1Pu("", nil, nil, nil, &ecdsa.PublicKey{Curve: elliptic.P521()}, 71 &ecdsa.PublicKey{Curve: elliptic.P256()}, &ecdsa.PrivateKey{PublicKey: ecdsa.PublicKey{Curve: elliptic.P521()}}, 0) 72 require.EqualError(t, err, "deriveRecipient1Pu: recipient, sender and ephemeral key are not on the same curve") 73 } 74 75 func Test_okpKWSupportFailures(t *testing.T) { 76 okpKW := &okpKWSupport{} 77 78 _, err := okpKW.getCurve("") 79 require.EqualError(t, err, "getCurve: not implemented for OKP KW support") 80 81 _, err = okpKW.createPrimitive([]byte("kekWithBadSize")) 82 require.EqualError(t, err, "createPrimitive: failed to create OKP primitive: chacha20poly1305: bad key length") 83 84 _, err = okpKW.wrap("badCipherBlockType", []byte("")) 85 require.EqualError(t, err, "wrap support: OKP wrap with invalid primitive type") 86 87 _, err = okpKW.unwrap("badCipherBlockType", []byte("")) 88 require.EqualError(t, err, "unwrap support: OKP unwrap with invalid primitive type") 89 90 kek, err := okpKW.generateKey(nil) 91 require.NoError(t, err) 92 93 kekBytes, ok := kek.([]byte) 94 require.True(t, ok) 95 96 XC20PPrimitive, err := okpKW.createPrimitive(kekBytes) 97 require.NoError(t, err) 98 99 _, err = okpKW.unwrap(XC20PPrimitive, []byte("")) 100 require.EqualError(t, err, "unwrap support: OKP unwrap invalid key") 101 102 _, err = okpKW.unwrap(XC20PPrimitive, []byte("badEncryptedKeyLargerThankNonceSize")) 103 require.EqualError(t, err, "unwrap support: OKP failed to unwrap key: chacha20poly1305: message authentication failed") 104 105 _, err = okpKW.deriveSender1Pu("", nil, nil, nil, "badEphemeralPrivKeyType", nil, nil, 0) 106 require.EqualError(t, err, "deriveSender1Pu: ephemeral key not OKP type") 107 108 _, err = okpKW.deriveSender1Pu("", nil, nil, nil, []byte{}, "badSenderPrivKeyType", nil, 0) 109 require.EqualError(t, err, "deriveSender1Pu: sender key not OKP type") 110 111 _, err = okpKW.deriveSender1Pu("", nil, nil, nil, []byte{}, []byte{}, "badSenderPrivKeyType", 0) 112 require.EqualError(t, err, "deriveSender1Pu: recipient key not OKP type") 113 114 _, err = okpKW.deriveSender1Pu("", nil, nil, nil, []byte{}, []byte{}, []byte{}, 0) 115 require.EqualError(t, err, 116 "deriveSender1Pu: deriveECDHX25519: crypto/ecdh: bad X25519 remote ECDH input: low order point") 117 118 derivedKEK, err := curve25519.X25519(kekBytes, curve25519.Basepoint) 119 require.NoError(t, err) 120 121 // lowOrderPoint from golang.org/x/crypto/curve25519. Causes ED25519 key derivation to fail. 122 // https://github.com/golang/crypto/blob/f4817d981/curve25519/vectors_test.go#L10 123 lowOrderPoint := []byte{ 124 0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 125 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 126 } 127 128 _, err = okpKW.deriveSender1Pu("", nil, nil, nil, derivedKEK, kekBytes, lowOrderPoint, 0) 129 require.EqualError(t, err, 130 "deriveSender1Pu: deriveECDHX25519: crypto/ecdh: bad X25519 remote ECDH input: low order point") 131 // can't reproduce key derivation error with sender key because recipient public key as lowOrderPoint fails for 132 // ephemeral key derivation. ie sender key derivation failure only fails if ephemeral key derivation fails. 133 134 _, err = okpKW.deriveRecipient1Pu("", nil, nil, nil, "badEphemeralPrivKeyType", nil, nil, 0) 135 require.EqualError(t, err, "deriveRecipient1Pu: ephemeral key not OKP type") 136 137 _, err = okpKW.deriveRecipient1Pu("", nil, nil, nil, []byte{}, "badSenderPrivKeyType", nil, 0) 138 require.EqualError(t, err, "deriveRecipient1Pu: sender key not OKP type") 139 140 _, err = okpKW.deriveRecipient1Pu("", nil, nil, nil, []byte{}, []byte{}, "badSenderPrivKeyType", 0) 141 require.EqualError(t, err, "deriveRecipient1Pu: recipient key not OKP type") 142 143 _, err = okpKW.deriveRecipient1Pu("", nil, nil, nil, []byte{}, []byte{}, []byte{}, 0) 144 require.EqualError(t, err, 145 "deriveRecipient1Pu: deriveECDHX25519: crypto/ecdh: bad X25519 remote ECDH input: low order point") 146 } 147 148 type mockKey struct { 149 Kty string `json:"kty,omitempty"` 150 Crv string `json:"crv,omitempty"` 151 X string `json:"x,omitempty"` 152 Y string `json:"y,omitempty"` 153 D string `json:"d,omitempty"` 154 } 155 156 type mockProtectedHeader struct { 157 Alg string `json:"alg,omitempty"` 158 Enc string `json:"enc,omitempty"` 159 Apu string `json:"apu,omitempty"` 160 Apv string `json:"apv,omitempty"` 161 Epk mockKey `json:"epk,omitempty"` 162 } 163 164 type ref1PU struct { 165 ZeHex string `json:"zeHex,omitempty"` 166 ZsHex string `json:"zsHex,omitempty"` 167 ZHex string `json:"zHex,omitempty"` 168 Sender1PUKDFHex string `json:"sender1puKdfHex,omitempty"` 169 Sender1PUKWB64 string `json:"sender1puKwB64,omitempty"` 170 } 171 172 func refJWKtoOKPKey(t *testing.T, jwkM string) (*[chacha20poly1305.KeySize]byte, *[chacha20poly1305.KeySize]byte) { 173 t.Helper() 174 175 jwk := &mockKey{} 176 err := json.Unmarshal([]byte(jwkM), jwk) 177 require.NoError(t, err) 178 179 x, err := base64.RawURLEncoding.DecodeString(jwk.X) 180 require.NoError(t, err) 181 182 d, err := base64.RawURLEncoding.DecodeString(jwk.D) 183 require.NoError(t, err) 184 185 x32 := new([chacha20poly1305.KeySize]byte) 186 copy(x32[:], x) 187 188 d32 := new([chacha20poly1305.KeySize]byte) 189 copy(d32[:], d) 190 191 return x32, d32 192 } 193 194 // nolint:gochecknoglobals // embedded test data 195 var ( 196 // test vector retrieved from: 197 //nolint:lll 198 // (github: https://github.com/NeilMadden/jose-ecdh-1pu/blob/master/draft-madden-jose-ecdh-1pu-04/draft-madden-jose-ecdh-1pu-04.txt#L740) 199 // (ietf draft: https://datatracker.ietf.org/doc/html/draft-madden-jose-ecdh-1pu-04#appendix-B) 200 //go:embed testdata/alice_key_ref.json 201 aliceKeyRef string 202 //go:embed testdata/bob_key_ref.json 203 bobKeyRef string 204 //go:embed testdata/charlie_key_ref.json 205 charlieKeyRef string 206 //go:embed testdata/alice_epk_ref.json 207 aliceEPKRef string 208 //go:embed testdata/protected_headers_ref.json 209 protectedHeadersRef string 210 //go:embed testdata/ecdh_1pu_bob.json 211 ecdh1puBobRef string 212 //go:embed testdata/ecdh_1pu_charlie.json 213 ecdh1puCharlieRef string 214 ) 215 216 // TestDeriveReferenceKey uses the test vector in the 1PU draft found at: 217 // (github: https://github.com/NeilMadden/jose-ecdh-1pu/blob/master/draft-madden-jose-ecdh-1pu-03.txt#L459) 218 // (ietf draft: https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-03#appendix-A) 219 // to validate the ECDH-1PU key derivation. 220 func TestDeriveReferenceKey(t *testing.T) { 221 tag, err := base64.RawURLEncoding.DecodeString("HLb4fTlm8spGmij3RyOs2gJ4DpHM4hhVRwdF_hGb3WQ") 222 require.NoError(t, err) 223 224 cek, err := hex.DecodeString("fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0efeeedecebeae9e8e7e6e5e4e3e2e1e0dfdedddcdbdad9d8" + 225 "d7d6d5d4d3d2d1d0cfcecdcccbcac9c8c7c6c5c4c3c2c1c0") 226 require.NoError(t, err) 227 228 ref1PUBobData := &ref1PU{} 229 err = json.Unmarshal([]byte(ecdh1puBobRef), ref1PUBobData) 230 require.NoError(t, err) 231 232 ref1PUCharlieData := &ref1PU{} 233 err = json.Unmarshal([]byte(ecdh1puCharlieRef), ref1PUCharlieData) 234 require.NoError(t, err) 235 236 _, alicePrivKeyRefOKP := refJWKtoOKPKey(t, aliceKeyRef) 237 bobPubKeyRefOKP, _ := refJWKtoOKPKey(t, bobKeyRef) 238 charliePubKeyRefOKP, _ := refJWKtoOKPKey(t, charlieKeyRef) 239 _, alicePrivKeyEPKRefOKP := refJWKtoOKPKey(t, aliceEPKRef) 240 241 protectedHeaderRefJWK := &mockProtectedHeader{} 242 err = json.Unmarshal([]byte(protectedHeadersRef), protectedHeaderRefJWK) 243 require.NoError(t, err) 244 245 apuRef, err := base64.RawURLEncoding.DecodeString(protectedHeaderRefJWK.Apu) // "Alice" 246 require.NoError(t, err) 247 248 apvRef, err := base64.RawURLEncoding.DecodeString(protectedHeaderRefJWK.Apv) // "Bob and Charlie" 249 require.NoError(t, err) 250 251 zeBobRef, err := hex.DecodeString(ref1PUBobData.ZeHex) 252 require.NoError(t, err) 253 254 zeCharlieRef, err := hex.DecodeString(ref1PUCharlieData.ZeHex) 255 require.NoError(t, err) 256 257 t.Run("test derive Ze for Bob", func(t *testing.T) { 258 ze, e := cryptoutil.DeriveECDHX25519(alicePrivKeyEPKRefOKP, bobPubKeyRefOKP) 259 require.NoError(t, e) 260 require.EqualValues(t, zeBobRef, ze) 261 262 zeHEX := hex.EncodeToString(ze) 263 require.EqualValues(t, ref1PUBobData.ZeHex, zeHEX) 264 }) 265 266 t.Run("test derive Ze for Charlie", func(t *testing.T) { 267 ze, e := cryptoutil.DeriveECDHX25519(alicePrivKeyEPKRefOKP, charliePubKeyRefOKP) 268 require.NoError(t, e) 269 270 zeHEX := hex.EncodeToString(ze) 271 require.EqualValues(t, ref1PUCharlieData.ZeHex, zeHEX) 272 require.EqualValues(t, zeCharlieRef, ze) 273 }) 274 275 zsBobRef, err := hex.DecodeString(ref1PUBobData.ZsHex) 276 require.NoError(t, err) 277 278 t.Run("test derive Zs for Bob", func(t *testing.T) { 279 zs, e := cryptoutil.DeriveECDHX25519(alicePrivKeyRefOKP, bobPubKeyRefOKP) 280 require.NoError(t, e) 281 282 zsHEX := hex.EncodeToString(zs) 283 require.EqualValues(t, ref1PUBobData.ZsHex, zsHEX) 284 require.EqualValues(t, zsBobRef, zs) 285 }) 286 287 zsCharlieRef, err := hex.DecodeString(ref1PUCharlieData.ZsHex) 288 require.NoError(t, err) 289 290 t.Run("test derive Zs for Charlie", func(t *testing.T) { 291 zs, e := cryptoutil.DeriveECDHX25519(alicePrivKeyRefOKP, charliePubKeyRefOKP) 292 require.NoError(t, e) 293 294 zsHEX := hex.EncodeToString(zs) 295 require.EqualValues(t, ref1PUCharlieData.ZsHex, zsHEX) 296 require.EqualValues(t, zsCharlieRef, zs) 297 }) 298 299 zBob, err := hex.DecodeString(ref1PUBobData.ZHex) 300 require.NoError(t, err) 301 require.EqualValues(t, append(zeBobRef, zsBobRef...), zBob) 302 303 zCharlie, err := hex.DecodeString(ref1PUCharlieData.ZHex) 304 require.NoError(t, err) 305 require.EqualValues(t, append(zeCharlieRef, zsCharlieRef...), zCharlie) 306 307 onePUKDFBobFromHex, err := hex.DecodeString(ref1PUBobData.Sender1PUKDFHex) 308 require.NoError(t, err) 309 310 onePUKDFCharlieFromHex, err := hex.DecodeString(ref1PUCharlieData.Sender1PUKDFHex) 311 require.NoError(t, err) 312 313 okpWrapper := okpKWSupport{} 314 315 t.Run("test KDF for Bob", func(t *testing.T) { 316 sender1PUWithBobKDF, e := okpWrapper.deriveSender1Pu(protectedHeaderRefJWK.Alg, apuRef, apvRef, tag, 317 alicePrivKeyEPKRefOKP[:], alicePrivKeyRefOKP[:], bobPubKeyRefOKP[:], 32) 318 require.NoError(t, e) 319 require.EqualValues(t, onePUKDFBobFromHex, sender1PUWithBobKDF) 320 }) 321 322 t.Run("test KDF for Charlie", func(t *testing.T) { 323 sender1PUWithCharlieKDF, e := okpWrapper.deriveSender1Pu(protectedHeaderRefJWK.Alg, apuRef, apvRef, tag, 324 alicePrivKeyEPKRefOKP[:], alicePrivKeyRefOKP[:], charliePubKeyRefOKP[:], 32) 325 require.NoError(t, e) 326 require.EqualValues(t, onePUKDFCharlieFromHex, sender1PUWithCharlieKDF) 327 }) 328 329 // Appendix B example uses "A128KW" key wrapping. 330 ecKW := &ecKWSupport{} 331 332 t.Run("test key wrap for Bob", func(t *testing.T) { 333 bobAESBlock, err := ecKW.createPrimitive(onePUKDFBobFromHex) 334 require.NoError(t, err) 335 336 onePUKWBobFromB64, err := base64.RawURLEncoding.DecodeString(ref1PUBobData.Sender1PUKWB64) 337 require.NoError(t, err) 338 339 bobEncryptedKey, err := ecKW.wrap(bobAESBlock, cek) 340 require.NoError(t, err) 341 require.EqualValues(t, onePUKWBobFromB64, bobEncryptedKey) 342 343 bobDecryptedCEK, err := ecKW.unwrap(bobAESBlock, onePUKWBobFromB64) 344 require.NoError(t, err) 345 require.EqualValues(t, cek, bobDecryptedCEK) 346 }) 347 348 t.Run("test key wrap for Charlie", func(t *testing.T) { 349 charlieAESBlock, err := ecKW.createPrimitive(onePUKDFCharlieFromHex) 350 require.NoError(t, err) 351 352 onePUKWCharlieFromB64, err := base64.RawURLEncoding.DecodeString(ref1PUCharlieData.Sender1PUKWB64) 353 require.NoError(t, err) 354 355 charlieEncryptedKey, err := ecKW.wrap(charlieAESBlock, cek) 356 require.NoError(t, err) 357 require.EqualValues(t, onePUKWCharlieFromB64, charlieEncryptedKey) 358 359 charlieDecryptedCEK, err := ecKW.unwrap(charlieAESBlock, onePUKWCharlieFromB64) 360 require.NoError(t, err) 361 require.EqualValues(t, cek, charlieDecryptedCEK) 362 }) 363 }