aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPete Bentley <prb@google.com>2024-02-14 19:10:46 +0000
committerPete Bentley <prb@google.com>2024-02-21 12:46:08 +0000
commit764e82c4deb6dc8230dba904b62ff7dff1bd7950 (patch)
treed8a3c9d87af0f6c7dea4973c351959fa33435380
parente7b7dd6ca18c3cb6a91d16d23f1b95eaf834d226 (diff)
downloadlibcore-764e82c4deb6dc8230dba904b62ff7dff1bd7950.tar.gz
Public HPKE API.
Provides an SPI for APIs to make use of HPKE implementations available in the platform, and a KeySpec allowing "raw" format XDH keys. Bug: 323357598 Test: atest API-Coverage-Bug: 326202329 Change-Id: Ia9e9977208156bd38c59e6502203110641fa5962
-rw-r--r--api/current.txt20
-rw-r--r--luni/annotations/flagged_api/android/crypto/hpke/HpkeSpi.annotated.java49
-rw-r--r--luni/annotations/flagged_api/android/crypto/hpke/XdhKeySpec.annotated.java41
-rw-r--r--luni/src/main/java/android/crypto/hpke/HpkeSpi.java194
-rw-r--r--luni/src/main/java/android/crypto/hpke/XdhKeySpec.java67
-rw-r--r--luni/src/test/java/libcore/android/crypto/hpke/XdhKeySpecTest.java57
-rw-r--r--non_openjdk_java_files.bp2
7 files changed, 430 insertions, 0 deletions
diff --git a/api/current.txt b/api/current.txt
index 2ca5f34648f..c0af166c375 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -1,4 +1,24 @@
// Signature format: 2.0
+package android.crypto.hpke {
+
+ @FlaggedApi("com.android.libcore.hpke_v_apis") public interface HpkeSpi {
+ method @FlaggedApi("com.android.libcore.hpke_v_apis") @NonNull public byte[] engineExport(int, @Nullable byte[]);
+ method @FlaggedApi("com.android.libcore.hpke_v_apis") public void engineInitRecipient(@NonNull byte[], @NonNull java.security.PrivateKey, @Nullable byte[], @Nullable java.security.PublicKey, @Nullable byte[], @Nullable byte[]) throws java.security.InvalidKeyException;
+ method @FlaggedApi("com.android.libcore.hpke_v_apis") public void engineInitSender(@NonNull java.security.PublicKey, @Nullable byte[], @Nullable java.security.PrivateKey, @Nullable byte[], @Nullable byte[]) throws java.security.InvalidKeyException;
+ method @FlaggedApi("com.android.libcore.hpke_v_apis") public void engineInitSenderWithSeed(@NonNull java.security.PublicKey, @Nullable byte[], @Nullable java.security.PrivateKey, @Nullable byte[], @Nullable byte[], @NonNull byte[]) throws java.security.InvalidKeyException;
+ method @FlaggedApi("com.android.libcore.hpke_v_apis") @NonNull public byte[] engineOpen(@NonNull byte[], @Nullable byte[]) throws java.security.GeneralSecurityException;
+ method @FlaggedApi("com.android.libcore.hpke_v_apis") @NonNull public byte[] engineSeal(@NonNull byte[], @Nullable byte[]);
+ method @FlaggedApi("com.android.libcore.hpke_v_apis") @NonNull public byte[] getEncapsulated();
+ }
+
+ @FlaggedApi("com.android.libcore.hpke_v_apis") public final class XdhKeySpec extends java.security.spec.EncodedKeySpec {
+ ctor @FlaggedApi("com.android.libcore.hpke_v_apis") public XdhKeySpec(@NonNull byte[]);
+ method @FlaggedApi("com.android.libcore.hpke_v_apis") @NonNull public String getFormat();
+ method @FlaggedApi("com.android.libcore.hpke_v_apis") @NonNull public byte[] getKey();
+ }
+
+}
+
package android.system {
public final class ErrnoException extends java.lang.Exception {
diff --git a/luni/annotations/flagged_api/android/crypto/hpke/HpkeSpi.annotated.java b/luni/annotations/flagged_api/android/crypto/hpke/HpkeSpi.annotated.java
new file mode 100644
index 00000000000..112cac1398a
--- /dev/null
+++ b/luni/annotations/flagged_api/android/crypto/hpke/HpkeSpi.annotated.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.crypto.hpke;
+
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+
+@android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_HPKE_V_APIS)
+@SuppressWarnings({"unchecked", "deprecation", "all"})
+public interface HpkeSpi {
+
+@android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_HPKE_V_APIS)
+public void engineInitSender(PublicKey recipientKey, byte[] info, PrivateKey senderKey, byte[] psk, byte[] psk_id);
+
+@android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_HPKE_V_APIS)
+public void engineInitSenderWithSeed(PublicKey recipientKey, byte[] info, PrivateKey senderKey, byte[] psk, byte[] psk_id, byte[] sKe);
+
+@android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_HPKE_V_APIS)
+public void engineInitRecipient(byte[] encapsulated, PrivateKey recipientKey, byte[] info, PublicKey senderKey, byte[] psk, byte[] psk_id);
+
+@android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_HPKE_V_APIS)
+public byte[] engineSeal(byte[] plaintext, byte[] aad);
+
+@android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_HPKE_V_APIS)
+public byte[] engineOpen(byte[] ciphertext, byte[] aad);
+
+@android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_HPKE_V_APIS)
+public byte[] engineExport(int length, byte[] context);
+
+@android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_HPKE_V_APIS)
+public byte[] getEncapsulated();
+}
diff --git a/luni/annotations/flagged_api/android/crypto/hpke/XdhKeySpec.annotated.java b/luni/annotations/flagged_api/android/crypto/hpke/XdhKeySpec.annotated.java
new file mode 100644
index 00000000000..5e44f555de3
--- /dev/null
+++ b/luni/annotations/flagged_api/android/crypto/hpke/XdhKeySpec.annotated.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.crypto.hpke;
+
+import java.security.spec.EncodedKeySpec;
+
+@android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_HPKE_V_APIS)
+@SuppressWarnings({"unchecked", "deprecation", "all"})
+public final class XdhKeySpec extends EncodedKeySpec {
+
+@android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_HPKE_V_APIS)
+public XdhKeySpec(byte[] encoded) { throw new RuntimeException("Stub!"); }
+
+@android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_HPKE_V_APIS)
+public String getFormat() { throw new RuntimeException("Stub!"); }
+
+@android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_HPKE_V_APIS)
+public byte[] getKey() { throw new RuntimeException("Stub!"); }
+
+@android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_HPKE_V_APIS)
+public boolean equals(Object o) { throw new RuntimeException("Stub!"); }
+
+@android.annotation.FlaggedApi(com.android.libcore.Flags.FLAG_HPKE_V_APIS)
+public int hashCode() { throw new RuntimeException("Stub!"); }
+}
+
diff --git a/luni/src/main/java/android/crypto/hpke/HpkeSpi.java b/luni/src/main/java/android/crypto/hpke/HpkeSpi.java
new file mode 100644
index 00000000000..0abe0326daa
--- /dev/null
+++ b/luni/src/main/java/android/crypto/hpke/HpkeSpi.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.crypto.hpke;
+
+import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import libcore.util.NonNull;
+import libcore.util.Nullable;
+
+
+/**
+ * Service Provider Interface for HPKE client API classes to communicate with implementations
+ * of HPKE as described in RFC 9180.
+ * <p>
+ * There are no standard Java Cryptography Architecture names or interface classes for HPKE,
+ * but instances of this class can be obtained by calling
+ * {@code Provider.getService("ConscryptHpke", String SuiteName)} where {@code suiteName}
+ * is the name of the HPKE suite, e.g.
+ * {@code "DHKEM_X25519_HKDF_SHA256/HKDF_SHA256/AES_128_GCM"}.
+ */
+public interface HpkeSpi {
+ /**
+ * Initialises an HPKE SPI in one of the sender modes described in RFC 9180.
+ * <p>
+ * If {@code psk} and {@code psk_id} are supplied then Pre-Shared Key Authentication
+ * will be used.
+ * <p>
+ * If {@code senderKey} is supplied then Asymmetric Key Authentication will be used.
+ * <p>
+ * If neither is supplied then "base" mode (no sender authentication) will be used.
+ * <p>
+ * Note that only base mode is currently supported on Android.
+ * <p>
+ * Public and private keys must be supplied in a format that can be used by the
+ * implementation. An instance of the {@code "XDH"} {@link java.security.KeyFactory} can
+ * be used to translate {@code KeySpecs} or keys from another {@link java.security.Provider}
+ *
+ * @param recipientKey public key of the recipient
+ * @param info application-supplied information, may be null or empty
+ * @param senderKey private key of the sender, for symmetric auth modes only, else null
+ * @param psk pre-shared key, for PSK auth modes only, else null
+ * @param psk_id pre-shared key ID, for PSK auth modes only, else null
+ * @throws InvalidKeyException if recipientKey is null or an unsupported key format
+ * @throws UnsupportedOperationException if mode is not a supported HPKE mode
+ * @throws IllegalStateException if this SPI has already been initialised
+ */
+ void engineInitSender(
+ @NonNull PublicKey recipientKey,
+ @Nullable byte[] info,
+ @Nullable PrivateKey senderKey,
+ @Nullable byte[] psk,
+ @Nullable byte[] psk_id)
+ throws InvalidKeyException;
+
+ /**
+ * Initialises an HPKE SPI in one of the sender modes described in RFC 9180 with
+ * a predefined random seed to allow testing against known test vectors.
+ * <p>
+ * This mode provides absolutely no security and should only be used for testing
+ * purposes.
+ * <p>
+ * If {@code psk} and {@code psk_id} are supplied then Pre-Shared Key Authentication
+ * will be used.
+ * <p>
+ * If {@code senderKey} is supplied then Asymmetric Key Authentication will be used.
+ * <p>
+ * If neither is supplied then "base" mode (no sender authentication) will be used.
+ * <p>
+ * Note that only base mode is currently supported on Android.
+ * <p>
+ * Public and private keys must be supplied in a format that can be used by the
+ * implementation. An instance of the {@code "XDH"} {@link java.security.KeyFactory} can
+ * be used to translate {@code KeySpecs} or keys from another {@link java.security.Provider}
+ *
+ *
+ * @param recipientKey public key of the recipient
+ * @param info application-supplied information, may be null or empty
+ * @param senderKey private key of the sender, for symmetric auth modes only, else null
+ * @param psk pre-shared key, for PSK auth modes only, else null
+ * @param psk_id pre-shared key ID, for PSK auth modes only, else null
+ * @param sKe Predetermined random seed, should only be usesd for validation against
+ * known test vectors
+ * @throws InvalidKeyException if recipientKey is null or an unsupported key format or senderKey
+ * is an unsupported key format
+ * @throws UnsupportedOperationException if mode is not a supported HPKE mode
+ * @throws IllegalStateException if this SPI has already been initialised
+ */
+ void engineInitSenderWithSeed(
+ @NonNull PublicKey recipientKey,
+ @Nullable byte[] info,
+ @Nullable PrivateKey senderKey,
+ @Nullable byte[] psk,
+ @Nullable byte[] psk_id,
+ @NonNull byte[] sKe)
+ throws InvalidKeyException;
+
+ /**
+ * Initialises an HPKE SPI in one of the sender modes described in RFC 9180.
+ * <p>
+ * If {@code psk} and {@code psk_id} are supplied then Pre-Shared Key Authentication
+ * will be used.
+ * <p>
+ * If {@code senderKey} is supplied then Asymmetric Key Authentication will be used.
+ * <p>
+ * If neither is supplied then "base" mode (no sender authentication) will be used.
+ * <p>
+ * Note that only base mode is currently supported on Android.
+ * <p>
+ * Public and private keys must be supplied in a format that can be used by the
+ * implementation. An instance of the {@code "XDH"} {@link java.security.KeyFactory} can
+ * be used to translate {@code KeySpecs} or keys from another {@link java.security.Provider}
+ *
+ * @param encapsulated encapsulated ephemeral key from a sender
+ * @param recipientKey private key of the recipient
+ * @param info application-supplied information, may be null or empty
+ * @param senderKey public key of sender, for asymmetric auth modes only, else null
+ * @param psk pre-shared key, for PSK auth modes only, else null
+ * @param psk_id pre-shared key ID, for PSK auth modes only, else null
+ * @throws InvalidKeyException if recipientKey is null or an unsupported key format or senderKey
+ * is an unsupported key format
+ * @throws UnsupportedOperationException if mode is not a supported HPKE mode
+ * @throws IllegalStateException if this SPI has already been initialised
+ */
+ void engineInitRecipient(
+ @NonNull byte[] encapsulated,
+ @NonNull PrivateKey recipientKey,
+ @Nullable byte[] info,
+ @Nullable PublicKey senderKey,
+ @Nullable byte[] psk,
+ @Nullable byte[] psk_id)
+ throws InvalidKeyException;
+
+ /**
+ * Seals a message, using the internal key schedule maintained by an HPKE sender SPI.
+ *
+ * @param plaintext the plaintext
+ * @param aad optional associated data, may be null or empty
+ * @return the ciphertext
+ * @throws NullPointerException if the plaintext is null
+ * @throws IllegalStateException if this SPI has not been initialised or if it was initialised
+ * as a recipient
+ */
+ @NonNull byte[] engineSeal(@NonNull byte[] plaintext, @Nullable byte[] aad);
+
+ /**
+ * Opens a message, using the internal key schedule maintained by an HPKE recipient SPI.
+ *
+ * @param ciphertext the ciphertext
+ * @param aad optional associated data, may be null or empty
+ * @return the plaintext
+ * @throws IllegalStateException if this SPI has not been initialised or if it was initialised
+ * as a sender
+ * @throws GeneralSecurityException on decryption failures
+ */
+ @NonNull byte[] engineOpen(@NonNull byte[] ciphertext, @Nullable byte[] aad)
+ throws GeneralSecurityException;
+
+ /**
+ * Exports secret key material from this SPI as described in RFC 9180.
+ *
+ * @param length expected output length
+ * @param context optional context string, may be null or empty
+ * @return exported value
+ * @throws IllegalArgumentException if the length is not valid for the KDF in use
+ * @throws IllegalStateException if this SPI has not been initialised
+ *
+ */
+ @NonNull byte[] engineExport(int length, @Nullable byte[] context);
+
+ /**
+ * Returns the encapsulated key material for an HPKE sender.
+ *
+ * @return the key material
+ * @throws IllegalStateException if this SPI has not been initialised or if it was initialised
+ * as a recipient
+ */
+ @NonNull byte[] getEncapsulated();
+}
diff --git a/luni/src/main/java/android/crypto/hpke/XdhKeySpec.java b/luni/src/main/java/android/crypto/hpke/XdhKeySpec.java
new file mode 100644
index 00000000000..39627471cc5
--- /dev/null
+++ b/luni/src/main/java/android/crypto/hpke/XdhKeySpec.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.crypto.hpke;
+
+import java.security.spec.EncodedKeySpec;
+import java.util.Arrays;
+import java.util.Objects;
+import libcore.util.NonNull;
+
+/**
+ * External Diffie–Hellman (XDH) key spec holding either a public or private key.
+ * <p>
+ * Subclasses {@code EncodedKeySpec} using the non-Standard "raw" format. The XdhKeyFactory
+ * class utilises this in order to create XDH keys from raw bytes and to return them
+ * as an XdhKeySpec allowing the raw key material to be extracted from an XDH key.
+ */
+public final class XdhKeySpec extends EncodedKeySpec {
+ /**
+ * Creates an instance of {@link XdhKeySpec} by passing a public or private key in its raw
+ * format.
+ */
+ public XdhKeySpec(@NonNull byte[] encoded) {
+ super(encoded);
+ }
+
+ @Override
+ @NonNull public String getFormat() {
+ return "raw";
+ }
+
+ /**
+ * Returns the public or private key in its raw format.
+ *
+ * @return key in its raw format.
+ */
+ @NonNull public byte[] getKey() {
+ return getEncoded();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof EncodedKeySpec)) return false;
+ EncodedKeySpec that = (EncodedKeySpec) o;
+ return (getFormat().equals(that.getFormat())
+ && (Arrays.equals(getEncoded(), that.getEncoded())));
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getFormat(), Arrays.hashCode(getEncoded()));
+ }
+}
diff --git a/luni/src/test/java/libcore/android/crypto/hpke/XdhKeySpecTest.java b/luni/src/test/java/libcore/android/crypto/hpke/XdhKeySpecTest.java
new file mode 100644
index 00000000000..4187253f37d
--- /dev/null
+++ b/luni/src/test/java/libcore/android/crypto/hpke/XdhKeySpecTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package libcore.android.crypto.hpke;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import android.crypto.hpke.XdhKeySpec;
+
+import java.util.Random;
+
+@RunWith(JUnit4.class)
+public class XdhKeySpecTest {
+ byte[] keyBytes = new byte[32];
+
+ @Before
+ public void setup() {
+ Random random = new Random();
+ random.nextBytes(keyBytes);
+ }
+
+ @Test
+ public void create() {
+ XdhKeySpec spec = new XdhKeySpec(keyBytes);
+ assertEquals("raw", spec.getFormat().toLowerCase());
+ assertArrayEquals(keyBytes, spec.getEncoded());
+ assertArrayEquals(keyBytes, spec.getKey());
+ }
+
+ @Test
+ public void equality() {
+ XdhKeySpec spec1 = new XdhKeySpec(keyBytes);
+ XdhKeySpec spec2 = new XdhKeySpec(keyBytes);
+ assertEquals(spec1, spec2);
+ assertNotSame(spec1, spec2);
+ }
+}
diff --git a/non_openjdk_java_files.bp b/non_openjdk_java_files.bp
index 0877a5009ce..bfe5ad8bad6 100644
--- a/non_openjdk_java_files.bp
+++ b/non_openjdk_java_files.bp
@@ -137,6 +137,8 @@ filegroup {
name: "non_openjdk_javadoc_luni_files",
srcs: [
"luni/src/main/java/android/compat/Compatibility.java",
+ "luni/src/main/java/android/crypto/hpke/HpkeSpi.java",
+ "luni/src/main/java/android/crypto/hpke/XdhKeySpec.java",
"luni/src/main/java/android/system/ErrnoException.java",
"luni/src/main/java/android/system/GaiException.java",
"luni/src/main/java/android/system/IcmpHeaders.java",