-
Notifications
You must be signed in to change notification settings - Fork 6.2k
8353578: Refactor existing usage of internal HKDF impl to use the KDF API #24393
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cfa422e
320c49a
eaa0737
38b2da7
ebc635c
cc7fd3a
825aa22
5693ede
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| /* | ||
| * Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved. | ||
| * Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved. | ||
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
| * | ||
| * This code is free software; you can redistribute it and/or modify it | ||
|
|
@@ -37,11 +37,13 @@ | |
| import java.util.Objects; | ||
| import javax.crypto.*; | ||
| import javax.crypto.spec.SecretKeySpec; | ||
| import javax.crypto.spec.HKDFParameterSpec; | ||
|
|
||
| import sun.security.jca.JCAUtil; | ||
| import sun.security.ssl.HKDF; | ||
| import sun.security.util.*; | ||
|
|
||
| import jdk.internal.access.SharedSecrets; | ||
|
|
||
| // Implementing DHKEM defined inside https://www.rfc-editor.org/rfc/rfc9180.html, | ||
| // without the AuthEncap and AuthDecap functions | ||
| public class DHKEM implements KEMSpi { | ||
|
|
@@ -153,19 +155,19 @@ public KeyPair derive(int kem_id) { | |
| private enum Params { | ||
|
|
||
| P256(0x10, 32, 32, 2 * 32 + 1, | ||
| "ECDH", "EC", CurveDB.P_256, "SHA-256"), | ||
| "ECDH", "EC", CurveDB.P_256, "HKDF-SHA256"), | ||
|
|
||
| P384(0x11, 48, 48, 2 * 48 + 1, | ||
| "ECDH", "EC", CurveDB.P_384, "SHA-384"), | ||
| "ECDH", "EC", CurveDB.P_384, "HKDF-SHA384"), | ||
|
|
||
| P521(0x12, 64, 66, 2 * 66 + 1, | ||
| "ECDH", "EC", CurveDB.P_521, "SHA-512"), | ||
| "ECDH", "EC", CurveDB.P_521, "HKDF-SHA512"), | ||
|
|
||
| X25519(0x20, 32, 32, 32, | ||
| "XDH", "XDH", NamedParameterSpec.X25519, "SHA-256"), | ||
| "XDH", "XDH", NamedParameterSpec.X25519, "HKDF-SHA256"), | ||
|
|
||
| X448(0x21, 64, 56, 56, | ||
| "XDH", "XDH", NamedParameterSpec.X448, "SHA-512"), | ||
| "XDH", "XDH", NamedParameterSpec.X448, "HKDF-SHA512"), | ||
| ; | ||
|
|
||
| private final int kem_id; | ||
|
|
@@ -247,10 +249,17 @@ private byte[] DH(PrivateKey skE, PublicKey pkR) | |
|
|
||
| private byte[] ExtractAndExpand(byte[] dh, byte[] kem_context) | ||
| throws NoSuchAlgorithmException, InvalidKeyException { | ||
| HKDF kdf = new HKDF(hkdfAlgorithm); | ||
| SecretKey eae_prk = LabeledExtract(kdf, suiteId, null, EAE_PRK, dh); | ||
| return LabeledExpand(kdf, suiteId, eae_prk, SHARED_SECRET, | ||
| kem_context, Nsecret); | ||
| KDF hkdf = KDF.getInstance(hkdfAlgorithm); | ||
| SecretKey eae_prk = LabeledExtract(hkdf, suiteId, EAE_PRK, dh); | ||
| try { | ||
| return LabeledExpand(hkdf, suiteId, eae_prk, SHARED_SECRET, | ||
| kem_context, Nsecret); | ||
| } finally { | ||
| if (eae_prk instanceof SecretKeySpec s) { | ||
| SharedSecrets.getJavaxCryptoSpecAccess() | ||
| .clearSecretKeySpec(s); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private PublicKey getPublicKey(PrivateKey sk) | ||
|
|
@@ -277,31 +286,41 @@ private PublicKey getPublicKey(PrivateKey sk) | |
|
|
||
| // For KAT tests only. See RFC9180DeriveKeyPairSR. | ||
| public KeyPair deriveKeyPair(byte[] ikm) throws Exception { | ||
| HKDF kdf = new HKDF(hkdfAlgorithm); | ||
| SecretKey dkp_prk = LabeledExtract(kdf, suiteId, null, DKP_PRK, ikm); | ||
| if (isEC()) { | ||
| NamedCurve curve = (NamedCurve) spec; | ||
| BigInteger sk = BigInteger.ZERO; | ||
| int counter = 0; | ||
| while (sk.signum() == 0 || sk.compareTo(curve.getOrder()) >= 0) { | ||
| if (counter > 255) { | ||
| throw new RuntimeException(); | ||
| } | ||
| byte[] bytes = LabeledExpand(kdf, suiteId, dkp_prk, | ||
| CANDIDATE, I2OSP(counter, 1), Nsk); | ||
| // bitmask is defined to be 0xFF for P-256 and P-384, and 0x01 for P-521 | ||
| if (this == Params.P521) { | ||
| bytes[0] = (byte) (bytes[0] & 0x01); | ||
| KDF hkdf = KDF.getInstance(hkdfAlgorithm); | ||
| SecretKey dkp_prk = LabeledExtract(hkdf, suiteId, DKP_PRK, ikm); | ||
| try { | ||
| if (isEC()) { | ||
| NamedCurve curve = (NamedCurve) spec; | ||
| BigInteger sk = BigInteger.ZERO; | ||
| int counter = 0; | ||
| while (sk.signum() == 0 || | ||
| sk.compareTo(curve.getOrder()) >= 0) { | ||
| if (counter > 255) { | ||
| throw new RuntimeException(); | ||
| } | ||
| byte[] bytes = LabeledExpand(hkdf, suiteId, dkp_prk, | ||
| CANDIDATE, I2OSP(counter, 1), Nsk); | ||
| // bitmask is defined to be 0xFF for P-256 and P-384, | ||
| // and 0x01 for P-521 | ||
| if (this == Params.P521) { | ||
| bytes[0] = (byte) (bytes[0] & 0x01); | ||
| } | ||
| sk = new BigInteger(1, (bytes)); | ||
| counter = counter + 1; | ||
| } | ||
| sk = new BigInteger(1, (bytes)); | ||
| counter = counter + 1; | ||
| PrivateKey k = DeserializePrivateKey(sk.toByteArray()); | ||
| return new KeyPair(getPublicKey(k), k); | ||
| } else { | ||
| byte[] sk = LabeledExpand(hkdf, suiteId, dkp_prk, SK, EMPTY, | ||
| Nsk); | ||
| PrivateKey k = DeserializePrivateKey(sk); | ||
| return new KeyPair(getPublicKey(k), k); | ||
| } | ||
| } finally { | ||
| if (dkp_prk instanceof SecretKeySpec s) { | ||
| SharedSecrets.getJavaxCryptoSpecAccess() | ||
| .clearSecretKeySpec(s); | ||
| } | ||
| PrivateKey k = DeserializePrivateKey(sk.toByteArray()); | ||
| return new KeyPair(getPublicKey(k), k); | ||
| } else { | ||
| byte[] sk = LabeledExpand(kdf, suiteId, dkp_prk, SK, EMPTY, Nsk); | ||
| PrivateKey k = DeserializePrivateKey(sk); | ||
| return new KeyPair(getPublicKey(k), k); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -380,18 +399,32 @@ private static byte[] I2OSP(int n, int w) { | |
| } | ||
| } | ||
|
|
||
| private static SecretKey LabeledExtract(HKDF kdf, byte[] suite_id, | ||
| byte[] salt, byte[] label, byte[] ikm) throws InvalidKeyException { | ||
| return kdf.extract(salt, | ||
| new SecretKeySpec(concat(HPKE_V1, suite_id, label, ikm), "IKM"), | ||
| "HKDF-PRK"); | ||
| private static SecretKey LabeledExtract(KDF hkdf, byte[] suite_id, | ||
| byte[] label, byte[] ikm) throws InvalidKeyException { | ||
| SecretKeySpec s = new SecretKeySpec(concat(HPKE_V1, suite_id, label, | ||
| ikm), "IKM"); | ||
| try { | ||
| HKDFParameterSpec spec = | ||
| HKDFParameterSpec.ofExtract().addIKM(s).extractOnly(); | ||
| return hkdf.deriveKey("Generic", spec); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't done much with DHKEM yet, but should the returned key have algorithm name of "Generic," or something more descriptive like the previous "HKDF-PRK"?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Me neither. However, given
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is any specific salt needed here like in TLS?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should chat next week about an issue Weijun raised and the algorithm names in the Exporters. |
||
| } catch (InvalidAlgorithmParameterException | | ||
| NoSuchAlgorithmException e) { | ||
| throw new InvalidKeyException(e.getMessage(), e); | ||
| } finally { | ||
| SharedSecrets.getJavaxCryptoSpecAccess().clearSecretKeySpec(s); | ||
| } | ||
| } | ||
|
|
||
| private static byte[] LabeledExpand(HKDF kdf, byte[] suite_id, | ||
| private static byte[] LabeledExpand(KDF hkdf, byte[] suite_id, | ||
| SecretKey prk, byte[] label, byte[] info, int L) | ||
| throws InvalidKeyException { | ||
| byte[] labeled_info = concat(I2OSP(L, 2), HPKE_V1, | ||
| suite_id, label, info); | ||
| return kdf.expand(prk, labeled_info, L, "NONE").getEncoded(); | ||
| byte[] labeled_info = concat(I2OSP(L, 2), HPKE_V1, suite_id, label, | ||
| info); | ||
| try { | ||
| return hkdf.deriveData(HKDFParameterSpec.expandOnly( | ||
| prk, labeled_info, L)); | ||
| } catch (InvalidAlgorithmParameterException iape) { | ||
| throw new InvalidKeyException(iape.getMessage(), iape); | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wish we could use
s.destroy()here instead.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, it'd be nice. I reopened https://bugs.openjdk.org/browse/JDK-8160206 and we can address this separately.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or in the meantime:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, thanks for the suggestion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what I did in my Exporter code. Assuming you go in first, I'll update mine to use your Util method.