Skip to content

Commit

Permalink
Addressed PR #1146 comments
Browse files Browse the repository at this point in the history
  • Loading branch information
Jorge Saldivar committed Jul 19, 2023
1 parent a759cbb commit 4b03bea
Show file tree
Hide file tree
Showing 11 changed files with 597 additions and 661 deletions.
396 changes: 194 additions & 202 deletions common/src/jni/main/cpp/conscrypt/native_crypto.cc

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,17 @@
* <p>
* HPKE allows multiple cryptographic operations to be done based on a given setup transaction.
* Therefore, the following APIs are stateful after setting up the transaction:
* <li>
* <ul>{@link Hpke#open(byte[], byte[])} </ul>
* <ul>{@link Hpke#seal(byte[], byte[])}</ul>
* <ul>{@link Hpke#export(int, byte[])} </ul>
* </li>
* <ul>
* <li>{@link HpkeContext#open(byte[], byte[])} </li>
* <li>{@link HpkeContext#seal(byte[], byte[])}</li>
* <li>{@link HpkeContext#export(int, byte[])} </li>
* </ul>
*
* @see <a href="https://www.rfc-editor.org/rfc/rfc9180.html#hpke-export">HPKE RFC 9180</a>
*/
public class Hpke {
/** Represents the previous setup transaction mode. */
private enum Mode {
public class HpkeContext {
/** Represents the participant configured. */
private enum SetupParticipant {
NONE,
SENDER,
RECIPIENT
Expand All @@ -47,61 +47,55 @@ private enum Mode {
private EVP_HPKE_CTX mCtx;
private byte[] mEnc;

private Mode mSetupMode;
private SetupParticipant mSetupParticipant;

/**
* Constructor defining the HPKE scheme to be used. Current supported schemes are:
* <li>
* <ul>{@link HpkeSuite#DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_128_GCM}</ul>
* <ul>{@link HpkeSuite#DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_AES_256_GCM}</ul>
* <ul>{@link HpkeSuite#DHKEM_X25519_HKDF_SHA256_HKDF_SHA256_CHACHA20POLY1305}</ul>
* </li>
* Constructor defining the HPKE scheme to be used.
*
* @param hpkeSuite KEM, KDF, and AEAD scheme to be used
*/
public Hpke(HpkeSuite hpkeSuite) {
public HpkeContext(HpkeSuite hpkeSuite) {
mHpkeSuite = hpkeSuite;
mSetupMode = Mode.NONE;
mSetupParticipant = SetupParticipant.NONE;
mContextHelper = new HpkeContextHelper();
}

/**
* Creates an instance of {@link Hpke} meant for testing only by supplying an additional
* Creates an instance of {@link HpkeContext} meant for testing only by supplying an additional
* {@link HpkeContextHelper}.
*/
@Internal
public static Hpke createForTestingOnly(HpkeSuite hpkeSuite, HpkeContextHelper contextHelper) {
final Hpke hpke = new Hpke(hpkeSuite);
hpke.mContextHelper = contextHelper;
return hpke;
public static HpkeContext createForTestingOnly(HpkeSuite hpkeSuite, HpkeContextHelper contextHelper) {
final HpkeContext hpkeContext = new HpkeContext(hpkeSuite);
hpkeContext.mContextHelper = contextHelper;
return hpkeContext;
}

/**
* Hybrid Public Key Encryption (HPKE) decryption.
* <p>
* Returns a plaintext in a byte array given a required ciphertext and an optional associated
* data (aad). The API has a pre-requirement, an API call to set up[Mode]Recipient is needed so
* the API could be properly initialized. Note: This API is stateful.
* The API has a pre-requirement, an API call to set up[Mode]Recipient is needed so the API could
* be properly initialized. This API is stateful.
* <p><p>
* Multiple messages decryption:<p>
* If decrypting multiple messages that were encrypted using the same context in a sequence,
* they must be decrypted in the same order as well. The API setup[Mode]Recipient must be called
* just once before multiple calls to this API.
* <p><p>
* Single-shot decryption:<p>
* Single message decryption:<p>
* If decryption a single message, the API setup[Mode]Recipient must be called
* before every single message. Note: Calling setup[Mode][Sender|Recipient] resets the HPKE
* context.
*
* @param ciphertext contains the encrypted plaintext
* @param aad optional associated data
* @return plaintext in a byte array.
* @param ciphertext contains the encrypted plaintext.
* @param aad optional associated data.
* @return plaintext.
* @throws IllegalStateException if the API setup[Mode]Recipient hasn't been called or if an
* issue happened while performing decryption operation (an issue
* could occur most likely if the keys configured are not valid).
*/
public byte[] open(byte[] ciphertext, byte[] aad) {
if (mSetupMode != Mode.RECIPIENT) {
if (mSetupParticipant != SetupParticipant.RECIPIENT) {
throw new IllegalStateException("Setup recipient needs to be called before decryption");
}

Expand All @@ -117,68 +111,61 @@ public byte[] open(byte[] ciphertext, byte[] aad) {
/**
* Hybrid Public Key Encryption (HPKE) encryption.
* <p>
* Returns a {@link HpkeResult} wrapper holding the resulting encapsulated key (enc) and
* ciphertext given a required plaintext and an optional associated data (aad). The API has a
* pre-requirement, an API call to set up[Mode]Sender is needed so the API could be properly
* initialized. Note: This API is stateful.
* The API has a pre-requirement, an API call to setup[Mode]Sender is needed so the API could be
* properly initialized. This API is stateful.
* <p><p>
* Multiple messages encryption:<p>
* If encrypting multiple messages that are expected to be decrypted in the same sequence as how
* they are encrypted, the API setup[Mode]Sender must be called just once before multiple calls
* to this API.
* <p><p>
* Single-shot encryption:<p>
* Single message encryption:<p>
* If encrypting a single message, the API setup[Mode]Sender must be called
* before every single message. Note: Calling setup[Mode][Sender|Recipient] resets the HPKE
* context.
* before every single message. Note: Calling setup[Mode][Sender|Recipient] resets the HPKE state.
*
* @param plaintext message that will be encrypted
* @param aad optional associated data
* @return HpkeResult wrapping the resulting encapsulated key (enc) and ciphertext.
* @param plaintext message that will be encrypted.
* @param aad optional associated data.
* @return ciphertext.
* @throws IllegalStateException if the API setup[Mode]Sender hasn't been called.
*/
public HpkeResult seal(byte[] plaintext, byte[] aad) {
if (mSetupMode != Mode.SENDER) {
public byte[] seal(byte[] plaintext, byte[] aad) {
if (mSetupParticipant != SetupParticipant.SENDER) {
throw new IllegalStateException("Setup sender needs to be called before encryption");
}

Preconditions.checkNotNull(plaintext, "plaintext");
final byte[] ciphertext = NativeCrypto.EVP_HPKE_CTX_seal(mCtx, plaintext, aad);
return new HpkeResult(mEnc, ciphertext);
return NativeCrypto.EVP_HPKE_CTX_seal(mCtx, plaintext, aad);
}

/**
* Hybrid Public Key Encryption (HPKE) secret exports.
* <p>
* Returns a {@link HpkeResult} wrapper holding the resulting encapsulated key (enc) and
* ciphertext given a required exporter desired output length and an optional exporter context.
* The API has a pre-requirement, an API call to setup[Mode][Sender|Recipient] is needed so the
* API could be properly initialized.
*
* @param length expected output length
* @param exporterContext optional exporter context
* @return HpkeResult wrapping the resulting encapsulated key (enc) and the exported value.
* @param length expected output length.
* @param exporterContext optional exporter context.
* @return exported value.
* @throws IllegalArgumentException if the length is not valid based on the KDF specs.
* @throws IllegalStateException if the API setup[Mode][Sender|Recipient] hasn't been called.
*/
public HpkeResult export(int length, byte[] exporterContext) {
if (mSetupMode == Mode.NONE) {
public byte[] export(int length, byte[] exporterContext) {
if (mSetupParticipant == SetupParticipant.NONE) {
throw new IllegalStateException(
"Setup sender or recipient needs to be called before export");
}

mHpkeSuite.getKdf().validateExportLength(length);
final byte[] export = NativeCrypto.EVP_HPKE_CTX_export(mCtx, exporterContext, length);
return new HpkeResult(mEnc, export);
return NativeCrypto.EVP_HPKE_CTX_export(mCtx, exporterContext, length);
}

/**
* Initializes the internal HPKE context for the recipient using BASE (0x00) mode. Call this API
* before decrypting or exporting.
*
* @param enc encapsulated key matching the KEM private key spec
* @param privateKey private key (secret key) matching the KEM private key spec
* @param info optional application-supplied information
* @param enc encapsulated key matching the KEM private key spec.
* @param privateKey private key (secret key) matching the KEM private key spec.
* @param info optional application-supplied information.
* @throws IllegalArgumentException if providing an invalid encapsulated key (enc) or a private
* key with invalid length not matching the KEM specs.
* @throws IllegalStateException if an issue is encountered while setting up the recipient
Expand All @@ -189,10 +176,10 @@ public HpkeResult export(int length, byte[] exporterContext) {
* modes</a>
*/
public void setupBaseRecipient(byte[] enc, PrivateKey privateKey, byte[] info) {
mHpkeSuite.getKem().validateEnc(enc);
final byte[] sk = mHpkeSuite.getKem().validateAndGetPrivateKey(privateKey);
mHpkeSuite.getKem().validateEncLength(enc);
final byte[] sk = mHpkeSuite.getKem().validateLengthAndGetRawPrivateKey(privateKey);

mSetupMode = Mode.RECIPIENT;
mSetupParticipant = SetupParticipant.RECIPIENT;
mEnc = enc;
try {
mCtx = (EVP_HPKE_CTX) NativeCrypto.EVP_HPKE_CTX_setup_recipient(
Expand All @@ -208,8 +195,9 @@ public void setupBaseRecipient(byte[] enc, PrivateKey privateKey, byte[] info) {
* Initializes the internal HPKE context for the sender using BASE (0x00) mode. Call this API
* before encrypting or exporting.
*
* @param publicKey public key matching the KEM public key spec
* @param info optional application-supplied information
* @param publicKey public key matching the KEM public key spec.
* @param info optional application-supplied information.
* @return encapsulated key.
* @throws IllegalArgumentException if providing a public key with invalid length not matching
* the KEM specs.
* @throws IllegalStateException if an issue is encountered while setting up the sender
Expand All @@ -219,15 +207,16 @@ public void setupBaseRecipient(byte[] enc, PrivateKey privateKey, byte[] info) {
* href="https://www.rfc-editor.org/rfc/rfc9180.html#name-hybrid-public-key-encryptio">HPKE
* modes</a>
*/
public void setupBaseSender(PublicKey publicKey, byte[] info) {
final byte[] pk = mHpkeSuite.getKem().validateAndGetPublicKey(publicKey);
public byte[] setupBaseSender(PublicKey publicKey, byte[] info) {
final byte[] pk = mHpkeSuite.getKem().validateLengthAndGetRawPublicKey(publicKey);

mSetupMode = Mode.SENDER;
mSetupParticipant = SetupParticipant.SENDER;
try {
final Object[] result = mContextHelper.setupSenderBase(mHpkeSuite.getKem().getId(),
mHpkeSuite.getKdf().getId(), mHpkeSuite.getAead().getId(), pk, info);
mCtx = (EVP_HPKE_CTX) result[0];
mEnc = (byte[]) result[1];
return mEnc.clone();
} catch (Exception e) {
throw new IllegalStateException(
"Error while setting up base sender with the keys provided", e);
Expand Down
2 changes: 1 addition & 1 deletion common/src/main/java/org/conscrypt/HpkeContextHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
package org.conscrypt;

/**
* Separating {@link Hpke} logic for testing purposes. Testing could override these functions
* Separating {@link HpkeContext} logic for testing purposes. Testing could override these functions
* to call different implementations meant for testing as well.
*/
@Internal
Expand Down
62 changes: 0 additions & 62 deletions common/src/main/java/org/conscrypt/HpkeResult.java

This file was deleted.

Loading

0 comments on commit 4b03bea

Please sign in to comment.