mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-03-14 03:22:06 +01:00
MINOR: jwt: Add ecdh-es+axxxkw support in jwt_decrypt_jwk converter
This builds on the ECDH-ES processing and simply requires an extra AES Key Wrap operation between the built key and the token's CEK.
This commit is contained in:
parent
32d9af559f
commit
3925bb8efc
@ -95,7 +95,7 @@ haproxy h1 -conf {
|
||||
http-request set-var(txn.decrypted) var(txn.jwe),jwt_decrypt_jwk(txn.jwk)
|
||||
|
||||
.if ssllib_name_startswith(AWS-LC)
|
||||
acl aws_unmanaged var(txn.jwe),jwt_header_query('$.alg') -m str "A128KW"
|
||||
acl aws_unmanaged var(txn.jwe),jwt_header_query('$.alg') -m end "A128KW" -m end "A192KW"
|
||||
http-request set-var(txn.decrypted) str("AWS-LC UNMANAGED") if aws_unmanaged
|
||||
.endif
|
||||
|
||||
@ -277,3 +277,28 @@ client c9 -connect ${h1_mainfe_sock} {
|
||||
expect resp.http.x-decrypted == "Random test message for ECDH-ES encrypted tokens"
|
||||
|
||||
} -run
|
||||
|
||||
# ECDH-ES+A___KW
|
||||
client c10 -connect ${h1_mainfe_sock} {
|
||||
|
||||
# ECDH-ES+A128KW
|
||||
txreq -url "/jwk" -hdr "Authorization: Bearer eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImVuYyI6IkExMjhDQkMtSFMyNTYiLCJlcGsiOnsiY3J2IjoiUC0yNTYiLCJrdHkiOiJFQyIsIngiOiJtc2poQktWNW5oNnBjdjhoRnR0UDlFVXRzaURzWG83T3RCekVZYkVJM1EwIiwieSI6IloxQ3FPQlEya1RNR1lENWdMUWJCaHB0MzRKRkR3dW5TX2ZzSmhsMlc1OWcifX0.5l7YaATvAWFJnWK_HsBPmawJ0RMqrkiwyZ9xAuiYCFSiqWWSr8D82A.0sa1s5V2RcDf0FW6hA1lig.z2DVLxtHeY1fPp6dJHiHEuHLVIQHQ10GfYXeFxwNE7JGyto-D3K1elHQn0Yq4Pitaheja21gnXkJajXhOA0rwQ.YmpToFWmj8XQrXMeXTa9eQ" \
|
||||
-hdr "X-JWK: {\"crv\":\"P-256\",\"d\":\"6qbbYYII1zqqmlDHhTwJt-JYBe-ELI02yAecAx-nD4w\",\"kty\":\"EC\",\"x\":\"bASil7YpthReLltIsaJCaRrE7XtLCRVtOpGtdPO0jH0\",\"y\":\"9xj9qfSrVKFqN3lnaNDXAclGGnfmU_j7xsEocZdYmPs\"}"
|
||||
rxresp
|
||||
expect resp.http.x-decrypted ~ "(Random test message for ECDH-ES encrypted tokens|AWS-LC UNMANAGED)"
|
||||
|
||||
|
||||
# ECDH-ES+A192KW
|
||||
txreq -url "/jwk" -hdr "Authorization: Bearer eyJhbGciOiJFQ0RILUVTK0ExOTJLVyIsImVuYyI6IkExOTJDQkMtSFMzODQiLCJlcGsiOnsiY3J2IjoiUC0zODQiLCJrdHkiOiJFQyIsIngiOiJDcTd3Y0MzUm92VFRZSTMzLU9DcXBocjFlN1NzeEZWY0dOQXhEOEpWZHBRQmROaGg3Z2dLNTJKVkJ1RF9uZXVHIiwieSI6IjlaLU1MV09TQ3VZd0JZVTEtcTd2YUREWUZ1WFhqc1EwSmxpWllLVmdOU0dqVHVLY3VXQnJHemV2RzZEeGgyRHQifX0.75lt6Ixq6UhlN8uiaEphy8SiqEVsuD4Rc3QbFcmP7MJUTyt15LcZ3y-M7TJeNBh3Ajy_6K2WooU.cO9tUaQ2eVo0tIuOqb5_Bw.HQ6DqnLhW2Ad0c78WFGgwCStefYdL37xmh2Fa2mCsVNW5q0K3-xeDHYuIP9Q5xBYEY70U6wV5a0iVN87ii_iMA.feLteQh1ickYVJ2ZZ2whoVzNGRHgUpjp" \
|
||||
-hdr "X-JWK: {\"alg\":\"ECDH-ES+A192KW\",\"crv\":\"P-384\",\"d\":\"pj6xIezfwtUakkkLtbRQ9FmN6uN1YJ-TSBkWn4awuDfWiHgqpQHA7_L95Hjks1cK\",\"key_ops\":[\"wrapKey\",\"unwrapKey\"],\"kty\":\"EC\",\"x\":\"JO3ojbUYOzoSb-7lAy-c7VhDIjhEtg4zrPn_NJKuGhat-cuI1c4LvOj3n8p3j4bn\",\"y\":\"CA3i4pN7t6liWxQXyxdDp9t79B8uWuubGADJuGn_2_yl6pufhnQ30OBA590fOtEm\"}"
|
||||
rxresp
|
||||
expect resp.http.x-decrypted ~ "(Random test message for ECDH-ES\\+A192KW encrypted tokens|AWS-LC UNMANAGED)"
|
||||
|
||||
|
||||
# ECDH-ES+A256KW
|
||||
txreq -url "/jwk" -hdr "Authorization: Bearer eyJhbGciOiJFQ0RILUVTK0EyNTZLVyIsImVuYyI6IkEyNTZDQkMtSFM1MTIiLCJlcGsiOnsiY3J2IjoiUC01MjEiLCJrdHkiOiJFQyIsIngiOiJBTFZuZXN6Tl93WVJSWVYtblp3dy1sSkVDTXB2eE1iSENXX3BjY3EyWlF2eFdsNzVKdm5TM3lKbjgzcTE1MlpnWU4zTTB4SUhzQmw1empWZS02OGR4TThwIiwieSI6IkFUX2pGel94RGt0VFY4WWYzZlo1MnRvbE5QWkwwNXlwa0dVTThPWFRNZTBaaVNfYnIzaS0xNHFlWG1OcjA3TFFjNUZMX1VTQkE5WmlyWGRaZkVLUnFqNmEifX0.MqGFvMzpIlwQHeXgPucBkXmS2BaXr2ByUugzD31XrPtxwlWw96vOmfcjSHvda2FGJ1u6InaMMVZMMp75P6AF0kvk8vuM7QF2.kHYblcqwHgXv0xRQrLHwoA.gwFUyTx3RRHWvmqyUL5N6W8HcwbNc1hPTImQPoCNPv6rkhzV1obikVj7sNuTh3Po0nBu2QCKrt-GjJTlD4Q5kw.Q_YZWSkVVxv1rcpySgENN3ZPp-chIYoCGC070kkqiXc" \
|
||||
-hdr "X-JWK: {\"alg\":\"ECDH-ES+A256KW\",\"crv\":\"P-521\",\"d\":\"AGGLpIzSL1jE34wGa-owWCVt2rgk8j3jqh33QQFKwYCJ9abp3vROyQ-dNv6j6PjrnF1EFyY9dDzChNpWmzoOZAp3\",\"key_ops\":[\"wrapKey\",\"unwrapKey\"],\"kty\":\"EC\",\"x\":\"AD0EIUE6Bt_TDcyOPM6VchRocp7AFSeVd6XkVALWf8AFebeMgKIvJsCsGeRdPTO3vWWrR5AOvvpiBfurb9M9Tus-\",\"y\":\"AOeI5d0iF463g3DolhmVFn6MWk764ONuXRexLApjN-Q6_RkcnCieRSZzqqSPMYuEn-N3i4aYfiEPZV0jk8oZKQMQ\"}"
|
||||
rxresp
|
||||
expect resp.http.x-decrypted == "Random test message for ECDH-ES+A256KW encrypted tokens"
|
||||
|
||||
} -run
|
||||
|
||||
95
src/jwe.c
95
src/jwe.c
@ -39,9 +39,9 @@ typedef enum {
|
||||
JWE_ALG_A256KW,
|
||||
JWE_ALG_DIR,
|
||||
JWE_ALG_ECDH_ES,
|
||||
// JWE_ALG_ECDH_ES_A128KW,
|
||||
// JWE_ALG_ECDH_ES_A192KW,
|
||||
// JWE_ALG_ECDH_ES_A256KW,
|
||||
JWE_ALG_ECDH_ES_A128KW,
|
||||
JWE_ALG_ECDH_ES_A192KW,
|
||||
JWE_ALG_ECDH_ES_A256KW,
|
||||
JWE_ALG_A128GCMKW,
|
||||
JWE_ALG_A192GCMKW,
|
||||
JWE_ALG_A256GCMKW,
|
||||
@ -59,9 +59,9 @@ struct alg_enc jwe_algs[] = {
|
||||
{ "A256KW", JWE_ALG_A256KW },
|
||||
{ "dir", JWE_ALG_DIR },
|
||||
{ "ECDH-ES", JWE_ALG_ECDH_ES },
|
||||
{ "ECDH-ES+A128KW", JWE_ALG_UNMANAGED },
|
||||
{ "ECDH-ES+A192KW", JWE_ALG_UNMANAGED },
|
||||
{ "ECDH-ES+A256KW", JWE_ALG_UNMANAGED },
|
||||
{ "ECDH-ES+A128KW", JWE_ALG_ECDH_ES_A128KW },
|
||||
{ "ECDH-ES+A192KW", JWE_ALG_ECDH_ES_A192KW },
|
||||
{ "ECDH-ES+A256KW", JWE_ALG_ECDH_ES_A256KW },
|
||||
{ "A128GCMKW", JWE_ALG_A128GCMKW },
|
||||
{ "A192GCMKW", JWE_ALG_A192GCMKW },
|
||||
{ "A256GCMKW", JWE_ALG_A256GCMKW },
|
||||
@ -237,6 +237,9 @@ static int parse_jose(struct buffer *decoded_jose, int *alg, int *enc, struct jo
|
||||
|
||||
switch (*alg) {
|
||||
case JWE_ALG_ECDH_ES:
|
||||
case JWE_ALG_ECDH_ES_A128KW:
|
||||
case JWE_ALG_ECDH_ES_A192KW:
|
||||
case JWE_ALG_ECDH_ES_A256KW:
|
||||
ec = 1;
|
||||
break;
|
||||
case JWE_ALG_A128GCMKW:
|
||||
@ -944,15 +947,25 @@ static int do_decrypt_cek_ec(struct buffer *cek, struct buffer *decrypted_cek, E
|
||||
int key_size = 0;
|
||||
struct buffer *derived_secret = NULL;
|
||||
struct buffer *otherinfo = NULL;
|
||||
struct buffer *tmpbuf = NULL;
|
||||
const char *alg_id = NULL;
|
||||
|
||||
jwe_alg kw_alg = JWE_ALG_UNMANAGED;
|
||||
|
||||
int ecdhes = 0;
|
||||
unsigned char *concatkdf_ptr = NULL;
|
||||
size_t *concatkdf_len = 0;
|
||||
|
||||
/* rfc7518#section-4.6.2
|
||||
* Key derivation is performed using the Concat KDF, as defined in
|
||||
* Section 5.8.1 of [NIST.800-56A], where the Digest Method is SHA-256. */
|
||||
const EVP_MD *md = EVP_sha256();
|
||||
int hashlen = EVP_MD_size(md);
|
||||
EVP_MD_CTX *ctx = NULL;
|
||||
int keydatalen = 0;
|
||||
int counter = 0;
|
||||
int offset = 0;
|
||||
int reps = 0;
|
||||
|
||||
switch(crypt_alg) {
|
||||
case JWE_ALG_ECDH_ES:
|
||||
@ -981,6 +994,18 @@ static int do_decrypt_cek_ec(struct buffer *cek, struct buffer *decrypted_cek, E
|
||||
if (!alg_id)
|
||||
goto end;
|
||||
break;
|
||||
case JWE_ALG_ECDH_ES_A128KW:
|
||||
key_size = 128;
|
||||
kw_alg = JWE_ALG_A128KW;
|
||||
break;
|
||||
case JWE_ALG_ECDH_ES_A192KW:
|
||||
key_size = 192;
|
||||
kw_alg = JWE_ALG_A192KW;
|
||||
break;
|
||||
case JWE_ALG_ECDH_ES_A256KW:
|
||||
key_size = 256;
|
||||
kw_alg = JWE_ALG_A256KW;
|
||||
break;
|
||||
default:
|
||||
goto end;
|
||||
}
|
||||
@ -1011,34 +1036,50 @@ static int do_decrypt_cek_ec(struct buffer *cek, struct buffer *decrypted_cek, E
|
||||
|
||||
/* Data derivation as in Section 5.8.1 of [NIST.800-56A]
|
||||
* https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf
|
||||
*
|
||||
* For ECDH-ES the buffer built after the concatKDF operation will be
|
||||
* used directly to decrypt the contents. When ECDH-ES+AES Key Wrap is
|
||||
* used we must wrap the cek with the built buffer using the right AES
|
||||
* KW algorithm.
|
||||
*/
|
||||
if (ecdhes) {
|
||||
/* The decrypted cek to be used for actual data decrypt
|
||||
* operation will be built in the following block. */
|
||||
int hashlen = EVP_MD_size(md);
|
||||
if (!ecdhes) {
|
||||
tmpbuf = alloc_trash_chunk();
|
||||
if (!tmpbuf)
|
||||
goto end;
|
||||
concatkdf_ptr = (unsigned char*)tmpbuf->area;
|
||||
concatkdf_len = &tmpbuf->data;
|
||||
} else {
|
||||
concatkdf_ptr = (unsigned char*)decrypted_cek->area;
|
||||
concatkdf_len = &decrypted_cek->data;
|
||||
}
|
||||
|
||||
int keydatalen = (key_size >> 3);
|
||||
/* The decrypted cek to be used for actual data decrypt
|
||||
* operation will be built in the following block. */
|
||||
keydatalen = (key_size >> 3);
|
||||
reps = keydatalen / hashlen;
|
||||
|
||||
int reps = keydatalen / hashlen;
|
||||
int counter = 0;
|
||||
int offset = 0;
|
||||
for (counter = 0; counter <= reps; ++counter) {
|
||||
|
||||
for (counter = 0; counter <= reps; ++counter) {
|
||||
uint32_t be_counter = htonl(counter+1);
|
||||
|
||||
uint32_t be_counter = htonl(counter+1);
|
||||
if (EVP_DigestInit_ex(ctx, md, NULL) != 1 ||
|
||||
EVP_DigestUpdate(ctx, (char*)&be_counter, sizeof(be_counter)) != 1 ||
|
||||
EVP_DigestUpdate(ctx, b_orig(derived_secret), b_data(derived_secret)) != 1 ||
|
||||
EVP_DigestUpdate(ctx, b_orig(otherinfo), b_data(otherinfo)) != 1 ||
|
||||
EVP_DigestFinal_ex(ctx, concatkdf_ptr + offset, NULL) != 1)
|
||||
goto end;
|
||||
|
||||
if (EVP_DigestInit_ex(ctx, md, NULL) != 1 ||
|
||||
EVP_DigestUpdate(ctx, (char*)&be_counter, sizeof(be_counter)) != 1 ||
|
||||
EVP_DigestUpdate(ctx, b_orig(derived_secret), b_data(derived_secret)) != 1 ||
|
||||
EVP_DigestUpdate(ctx, b_orig(otherinfo), b_data(otherinfo)) != 1 ||
|
||||
EVP_DigestFinal_ex(ctx, (unsigned char*)(decrypted_cek->area + offset), NULL) != 1)
|
||||
goto end;
|
||||
offset += hashlen;
|
||||
|
||||
offset += hashlen;
|
||||
}
|
||||
|
||||
}
|
||||
*concatkdf_len = keydatalen;
|
||||
|
||||
decrypted_cek->data = keydatalen;
|
||||
if (!ecdhes) {
|
||||
/* Need to used the previously generated key to wrap the CEK
|
||||
* with the "A128KW", "A192KW", or "A256KW" algorithms. */
|
||||
if (!decrypt_cek_aeskw(cek, decrypted_cek, tmpbuf, kw_alg))
|
||||
goto end;
|
||||
}
|
||||
|
||||
retval = 0;
|
||||
@ -1046,6 +1087,7 @@ static int do_decrypt_cek_ec(struct buffer *cek, struct buffer *decrypted_cek, E
|
||||
end:
|
||||
free_trash_chunk(derived_secret);
|
||||
free_trash_chunk(otherinfo);
|
||||
free_trash_chunk(tmpbuf);
|
||||
EVP_MD_CTX_free(ctx);
|
||||
return retval;
|
||||
}
|
||||
@ -1860,6 +1902,9 @@ static int sample_conv_jwt_decrypt_jwk(const struct arg *args, struct sample *sm
|
||||
oct = 1;
|
||||
break;
|
||||
case JWE_ALG_ECDH_ES:
|
||||
case JWE_ALG_ECDH_ES_A128KW:
|
||||
case JWE_ALG_ECDH_ES_A192KW:
|
||||
case JWE_ALG_ECDH_ES_A256KW:
|
||||
ec = 1;
|
||||
break;
|
||||
default:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user