CARVIEW |
Navigation Menu
-
Notifications
You must be signed in to change notification settings - Fork 6.1k
8325448: Hybrid Public Key Encryption #18411
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
base: master
Are you sure you want to change the base?
Conversation
๐ Welcome back weijun! A progress list of the required criteria for merging this PR into |
โ This change is not yet ready to be integrated. |
@wangweij This pull request has been inactive for more than 8 weeks and will be automatically closed if another 8 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
/csr |
@wangweij has indicated that a compatibility and specification (CSR) request is needed for this pull request. @wangweij please create a CSR request for issue JDK-8325448 with the correct fix version. This pull request cannot be integrated until the CSR request is approved. |
@wangweij This pull request has been inactive for more than 8 weeks and will be automatically closed if another 8 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
@wangweij This pull request has been inactive for more than 16 weeks and will now be automatically closed. If you would like to continue working on this pull request in the future, feel free to reopen it! This can be done using the |
/open |
@wangweij This pull request is now open |
@wangweij This pull request has been inactive for more than 8 weeks and will be automatically closed if another 8 weeks passes without any activity. To avoid this, simply add a new comment to the pull request. Feel free to ask for assistance if you need help with progressing this pull request towards integration! |
Webrevs
|
Now receiver must provide all algorithm identifiers at cipher initialization. Calling |
src/java.base/share/classes/com/sun/crypto/provider/HPKEParameters.java
Outdated
Show resolved
Hide resolved
โฆtifiers specified
src/java.base/share/classes/javax/crypto/spec/HPKEParameterSpec.java
Outdated
Show resolved
Hide resolved
src/java.base/share/classes/javax/crypto/spec/HPKEParameterSpec.java
Outdated
Show resolved
Hide resolved
src/java.base/share/classes/javax/crypto/spec/HPKEParameterSpec.java
Outdated
Show resolved
Hide resolved
src/java.base/share/classes/javax/crypto/spec/snippet-files/PackageSnippets.java
Outdated
Show resolved
Hide resolved
src/java.base/share/classes/javax/crypto/spec/snippet-files/PackageSnippets.java
Outdated
Show resolved
Hide resolved
src/java.base/share/classes/javax/crypto/spec/snippet-files/PackageSnippets.java
Outdated
Show resolved
Hide resolved
* Example: | ||
* {@snippet lang=java class="PackageSnippets" region="hpke-spec-example"} | ||
* | ||
* @implNote |
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.
Making this implementation specific means that other providers could in theory choose different defaults, which reduces compatibility but an application could never be sure, or even know if this is for algorithms in RFC 9180. These are probably the most reasonable defaults for RFC 9180 compliant implementations. Did you consider making these defaults a requirement of HPKE implementations? I also wonder if "HPKE" is too general. If there is ever a new HPKE spec with say a new KEM or KDF algorithm for EC/XDH keys, would it be called "HPKE2"?
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.
Consider adding a String or Enum argument to of()
with the name of the profile, ex "RFC9180".
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 can add a sentence saying if an implementation does not provide default numeric algorithm identifiers then an exception will be thrown if of()
is used by the sender.
I still think it's useful to provide defaults. Now that the recipient requires the numeric algorithm identifiers to be provided, at least this will no longer be an interop issue between implementations. As for future new KEM or KDF algorithms for EC/XDH keys, I believe they will have different numeric algorithm identifiers and users can just specify them so there will be need for "HPKE2".
In fact, suppose the current kem_id
for XDH is found insecure one day and a new one is defined, we can update the @implNote
to make the new one the default. Those using of()
will automatically switch to the safer one and there is no need to update the code. That said, this does need both sides supporting the new kem_id
.
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โd prefer requiring callers to explicitly specify the three algorithm identifiers rather than introducing profile names. There are several reasons for this:
- Clarity and consistency: These identifiers are standardized and maintained by IANA in a single registry, making them familiar and unambiguous for all HPKE implementers.
- Profiles are not precise enough: RFC 9180 allows multiple combinations of algorithm identifiers for a single key type. We'd still need to define what the default is within this profile, which defeats the purpose of using the profile name as a shortcut.
- Profiles are mainly for new key types: Future profiles will most likely be introduced for new key algorithms (e.g., "RFC9180" for EC/XDH, "draft-connolly-cfrg-xwing-kem" for X-Wing, and "draft-connolly-cfrg-hpke-mlkem" for ML-KEM). Unless a profile defines new combinations for existing key types, itโs not necessary to require users to select among profile names at all. On the other hand, I assume we donโt want to introduce default profiles for key algorithms.
* Example: | ||
* {@snippet lang=java class="PackageSnippets" region="hpke-spec-example"} | ||
* | ||
* @implNote |
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.
Consider adding a String or Enum argument to of()
with the name of the profile, ex "RFC9180".
.info("app_info".getBytes(StandardCharsets.UTF_8)); | ||
senderCipher.init(Cipher.ENCRYPT_MODE, kp.getPublic(), ps); | ||
|
||
// Retrieve the actual parameters used from the sender. |
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.
"from the sender" sound like it is being retrieved from the sender side over the network. Suggest replacing this with "from the senderCipher".
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.
How about "Extracts the actual parameters from senderCipher
"?
HPKEParameterSpec actual = senderCipher.getParameters() | ||
.getParameterSpec(HPKEParameterSpec.class); | ||
|
||
// Retrieve the key encapsulation message (the KEM output) from the sender. |
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.
Same comment as above about "from the sender".
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.
How about "Extracts the key encapsulation message (the KEM output) from senderCipher
"?
.getParameterSpec(HPKEParameterSpec.class); | ||
|
||
// Retrieve the key encapsulation message (the KEM output) from the sender. | ||
// It can also be retrieved using sender.getIV(). |
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.
s/sender/senderCipher/
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.
Correct.
// It can also be retrieved using sender.getIV(). | ||
byte[] kemEncap = actual.encapsulation(); | ||
|
||
// The HPKE recipient side is initialized with its own private key, |
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 a question, not a comment. How does the recipient know what algorithm identifiers to use? Would these be exchanged as part of a protocol that used HPKE?
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've looked at several protocols that uses HPKE:
- For (Oblivious DNS over HTTPS)[https://datatracker.ietf.org/doc/html/rfc9230#name-configuration-and-public-ke], server advertises its public keys each in a
ObliviousDoHConfig
structure with supported algorithm identifiers. - For (TLS Encrypted Client Hello)[https://datatracker.ietf.org/doc/html/draft-ietf-tls-esni-24#name-encrypted-clienthello-confi], the server advertises its public keys, each with a
kem_id
and a set of supportedkdf_id
andaead_id
, identified by aconfig_id
, in anECHConfig
structure. Client will tell server whichconfig_id
it uses.
So it seems each side chooses the exact algorithm identifiers and even the sender does not require default values.
Cipher recipientCipher = Cipher.getInstance("HPKE"); | ||
HPKEParameterSpec pr = HPKEParameterSpec | ||
.of(actual.kem_id(), actual.kdf_id(), actual.aead_id()) | ||
.info("app_info".getBytes(StandardCharsets.UTF_8)) |
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.
Question, not necessarily a comment. Why is info also needed for decryption? Isn't it only needed on the encryption side when deriving the key?
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.
Both side uses info
to create the key schedule.
@wangweij this pull request can not be integrated into git checkout 8325448
git fetch https://git.openjdk.org/jdk.git master
git merge FETCH_HEAD
# resolve conflicts and follow the instructions given by git merge
git commit -m "Merge master"
git push |
@wangweij This pull request has been inactive for more than 4 weeks and will be automatically closed if another 4 weeks passes without any activity. To avoid this, simply issue a |
@wangweij This pull request has been inactive for more than 8 weeks and will now be automatically closed. If you would like to continue working on this pull request in the future, feel free to reopen it! This can be done using the |
/open |
@wangweij This pull request is now open |
Mailing list message from Sebastian Stenzel on security-dev: Hi Weijun, I?m probably a bit late to the party but nevertheless I hope this finds attention: To anyone in this mailing list unfamiliar with HPKE: The encapsulation process yields a tuple: A shared secret (the key agreement result, if you will) as well as an encapsulation of said key, that is being sent to the receiver. Therefore, I don?t think `Cipher` is an appropriate public API for HPKE. The current attempt to make it fit the Cipher API (which can?t return tuples), the encapsulation is received via `cipher.getIV()`. Which is, in my opinion, a misusage and a very confusing one: First of all, `getIV()` returns the ciphertext, usually the result of `doFinal()`. And secondly, as the latter is already used for the shared secret, `getIV()`?s behaviour depends on the state of the Cipher. Given that HPKE will probably rise in relevance quickly, I strongly advise giving it its own public API that aligns with the spec. Something like this: ``` Encapsulated encap(PublicKey recipientKey); SecretKey decap(byte[] enc, PrivateKey recipientKey); Best regards, -------------- next part -------------- |
Hi Sebastian, the API you suggested is only the KEM step, and it should be made internal inside HPKE. At the end of the day, HPKE is still a cipher. I understand the key encapsulation message (aka, KEM ciphertext) is different from a traditional IV, but they share some key characteristics: 1) generated by the sender after initialization, 2) cryptographically random, 3) then made public, 4) has critical impact on encryption result. |
I am sorry, I got the names mixed up - too many different standards I was recently reviewing ๐. Nevertheless, the final result of HPKE is still a tuple, with the encapsulation from the KEM step being required for the receiver to decrypt the message (or "decapsulate" and then "openโ, to use the KEM and AEAD wording ๐). All your points make sense, though. The main difference is, that (so far) all IVs or nonces have to be generated beforehand in order to feed them into the cipher. As opposed to now being a byproduct of the cipher. Maybe I just have to get used to the fact that this doesnโt strictly need to be this way. |
It's only a tuple for a single-shot encryption. The general operation is still one encapsulation followed by multiple "seal" outputs. As for the IV, I think it's not uncommon that the cipher generates a random one. |
Implement HPKE as defined in https://datatracker.ietf.org/doc/rfc9180/.
Progress
Issue
Reviewing
Using
git
Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk.git pull/18411/head:pull/18411
$ git checkout pull/18411
Update a local copy of the PR:
$ git checkout pull/18411
$ git pull https://git.openjdk.org/jdk.git pull/18411/head
Using Skara CLI tools
Checkout this PR locally:
$ git pr checkout 18411
View PR using the GUI difftool:
$ git pr show -t 18411
Using diff file
Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/18411.diff
Using Webrev
Link to Webrev Comment