From 263831de952cb2b44601a6f1d8d7be0ea51481b8 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 21 Dec 2023 11:29:03 +0100 Subject: [PATCH] Add BoringSSL based native HPKE implementation Motivation: While we already have a BouncyCastle based HPKE implementation that we can use we should also offer an implementation which is faster and so reduce the performance overhead. Modifications: Add native implementation based on BoringSSL. Result: Fixes https://github.com/netty/netty-incubator-codec-ohttp/issues/5 --- .github/workflows/ci-pr.yml | 6 +- .github/workflows/codeql-analysis.yml | 7 +- .../BouncyCastleAEADCryptoContext.java | 2 - .../bouncycastle/BouncyCastleHPKEContext.java | 5 - .../BouncyCastleOHttpCryptoProvider.java | 28 + codec-ohttp-hpke-classes-boringssl/pom.xml | 75 +++ .../codec/hpke/boringssl/BoringSSL.java | 191 ++++++ .../hpke/boringssl/BoringSSLAEADContext.java | 81 +++ .../BoringSSLAsymmetricCipherKeyPair.java | 65 ++ .../BoringSSLAsymmetricKeyParameter.java | 64 ++ .../boringssl/BoringSSLCryptoContext.java | 51 ++ .../boringssl/BoringSSLCryptoOperation.java | 88 +++ .../codec/hpke/boringssl/BoringSSLHPKE.java | 65 ++ .../hpke/boringssl/BoringSSLHPKEContext.java | 54 ++ .../BoringSSLHPKERecipientContext.java | 49 ++ .../boringssl/BoringSSLHPKESenderContext.java | 60 ++ ...LNativeStaticallyReferencedJniMethods.java | 27 + .../BoringSSLOHttpCryptoProvider.java | 230 +++++++ codec-ohttp-hpke-native-boringssl/pom.xml | 603 ++++++++++++++++++ ...tty_incubator_codec_ohttp_hpke_boringssl.c | 407 ++++++++++++ ...tty_incubator_codec_ohttp_hpke_boringssl.h | 20 + .../main/native-package/m4/custom.m4.template | 39 ++ .../vs2010.custom.props.template | 26 + .../codec/hpke/OHttpCryptoProvider.java | 30 + docker/Dockerfile.centos7 | 41 +- pom.xml | 2 + 26 files changed, 2304 insertions(+), 12 deletions(-) create mode 100644 codec-ohttp-hpke-classes-boringssl/pom.xml create mode 100644 codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSL.java create mode 100644 codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLAEADContext.java create mode 100644 codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLAsymmetricCipherKeyPair.java create mode 100644 codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLAsymmetricKeyParameter.java create mode 100644 codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLCryptoContext.java create mode 100644 codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLCryptoOperation.java create mode 100644 codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKE.java create mode 100644 codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKEContext.java create mode 100644 codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKERecipientContext.java create mode 100644 codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKESenderContext.java create mode 100644 codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLNativeStaticallyReferencedJniMethods.java create mode 100644 codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLOHttpCryptoProvider.java create mode 100644 codec-ohttp-hpke-native-boringssl/pom.xml create mode 100644 codec-ohttp-hpke-native-boringssl/src/main/c/netty_incubator_codec_ohttp_hpke_boringssl.c create mode 100644 codec-ohttp-hpke-native-boringssl/src/main/c/netty_incubator_codec_ohttp_hpke_boringssl.h create mode 100644 codec-ohttp-hpke-native-boringssl/src/main/native-package/m4/custom.m4.template create mode 100644 codec-ohttp-hpke-native-boringssl/src/main/native-package/vs2010.custom.props.template diff --git a/.github/workflows/ci-pr.yml b/.github/workflows/ci-pr.yml index bd1261a..984a8ce 100644 --- a/.github/workflows/ci-pr.yml +++ b/.github/workflows/ci-pr.yml @@ -43,8 +43,12 @@ jobs: key: verify-cache-m2-repository-${{ hashFiles('**/pom.xml') }} restore-keys: | verify-cache-m2-repository- + + - name: Install tools / libraries + run: sudo apt-get update && sudo apt-get -y install autoconf automake libtool make tar cmake perl ninja-build git + - name: Verify with Maven - run: ./mvnw -B --file pom.xml verify -DskipTests=true -DskipH3Spec=true + run: ./mvnw -B --file pom.xml verify -DskipTests=true build: runs-on: ubuntu-latest diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 511256f..738698c 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: strategy: fail-fast: false matrix: - language: [ 'java' ] + language: [ 'cpp', 'java' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more... # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection @@ -63,8 +63,11 @@ jobs: # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main + - name: Install tools / libraries + run: sudo apt-get update && sudo apt-get -y install autoconf automake libtool make tar cmake perl ninja-build git + - name: Build project - run: ./mvnw clean package -DskipTests=true -DskipH3Spec=true + run: ./mvnw clean package -DskipTests=true - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v2 diff --git a/codec-ohttp-hpke-bouncycastle/src/main/java/io/netty/incubator/codec/hpke/bouncycastle/BouncyCastleAEADCryptoContext.java b/codec-ohttp-hpke-bouncycastle/src/main/java/io/netty/incubator/codec/hpke/bouncycastle/BouncyCastleAEADCryptoContext.java index 1081fe6..de08508 100644 --- a/codec-ohttp-hpke-bouncycastle/src/main/java/io/netty/incubator/codec/hpke/bouncycastle/BouncyCastleAEADCryptoContext.java +++ b/codec-ohttp-hpke-bouncycastle/src/main/java/io/netty/incubator/codec/hpke/bouncycastle/BouncyCastleAEADCryptoContext.java @@ -18,7 +18,6 @@ import io.netty.buffer.ByteBuf; import io.netty.incubator.codec.hpke.AEADContext; import io.netty.incubator.codec.hpke.CryptoException; -import io.netty.incubator.codec.hpke.CryptoContext; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.hpke.AEAD; @@ -26,7 +25,6 @@ final class BouncyCastleAEADCryptoContext implements AEADContext { private final BouncyCastleCryptoOperation open; private final BouncyCastleCryptoOperation seal; - private boolean closed; BouncyCastleAEADCryptoContext(AEAD aead) { diff --git a/codec-ohttp-hpke-bouncycastle/src/main/java/io/netty/incubator/codec/hpke/bouncycastle/BouncyCastleHPKEContext.java b/codec-ohttp-hpke-bouncycastle/src/main/java/io/netty/incubator/codec/hpke/bouncycastle/BouncyCastleHPKEContext.java index 1d4ef16..07411ca 100644 --- a/codec-ohttp-hpke-bouncycastle/src/main/java/io/netty/incubator/codec/hpke/bouncycastle/BouncyCastleHPKEContext.java +++ b/codec-ohttp-hpke-bouncycastle/src/main/java/io/netty/incubator/codec/hpke/bouncycastle/BouncyCastleHPKEContext.java @@ -15,12 +15,7 @@ */ package io.netty.incubator.codec.hpke.bouncycastle; -import io.netty.buffer.ByteBuf; -import io.netty.incubator.codec.hpke.CryptoException; import io.netty.incubator.codec.hpke.HPKEContext; -import org.bouncycastle.crypto.InvalidCipherTextException; - -import java.nio.ByteBuffer; abstract class BouncyCastleHPKEContext implements HPKEContext { protected final org.bouncycastle.crypto.hpke.HPKEContext context; diff --git a/codec-ohttp-hpke-bouncycastle/src/main/java/io/netty/incubator/codec/hpke/bouncycastle/BouncyCastleOHttpCryptoProvider.java b/codec-ohttp-hpke-bouncycastle/src/main/java/io/netty/incubator/codec/hpke/bouncycastle/BouncyCastleOHttpCryptoProvider.java index 3e0702c..a8fff97 100644 --- a/codec-ohttp-hpke-bouncycastle/src/main/java/io/netty/incubator/codec/hpke/bouncycastle/BouncyCastleOHttpCryptoProvider.java +++ b/codec-ohttp-hpke-bouncycastle/src/main/java/io/netty/incubator/codec/hpke/bouncycastle/BouncyCastleOHttpCryptoProvider.java @@ -35,9 +35,17 @@ import org.bouncycastle.util.encoders.Hex; import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; public final class BouncyCastleOHttpCryptoProvider implements OHttpCryptoProvider { + private static final List SUPPORTED_AEAD_LIST = Collections.unmodifiableList(Arrays.asList(AEAD.values())); + private static final List SUPPORTED_MODE_LIST = Collections.unmodifiableList(Arrays.asList(Mode.values())); + private static final List SUPPORTED_KEM_LIST = Collections.unmodifiableList(Arrays.asList(KEM.values())); + private static final List SUPPORTED_KDF_LIST = Collections.unmodifiableList(Arrays.asList(KDF.values())); + public static final BouncyCastleOHttpCryptoProvider INSTANCE = new BouncyCastleOHttpCryptoProvider(); private BouncyCastleOHttpCryptoProvider() { } @@ -180,4 +188,24 @@ private static ECDomainParameters ecDomainParameters(KEM kem) { throw new IllegalArgumentException("invalid kem: " + kem); } } + + @Override + public List supportedAEAD() { + return SUPPORTED_AEAD_LIST; + } + + @Override + public List supportedKEM() { + return SUPPORTED_KEM_LIST; + } + + @Override + public List supportedKDF() { + return SUPPORTED_KDF_LIST; + } + + @Override + public List supportedMode() { + return SUPPORTED_MODE_LIST; + } } diff --git a/codec-ohttp-hpke-classes-boringssl/pom.xml b/codec-ohttp-hpke-classes-boringssl/pom.xml new file mode 100644 index 0000000..60d84e9 --- /dev/null +++ b/codec-ohttp-hpke-classes-boringssl/pom.xml @@ -0,0 +1,75 @@ + + + + 4.0.0 + + io.netty.incubator + netty-incubator-codec-parent-ohttp + 0.0.3.Final-SNAPSHOT + + + netty-incubator-ohttp-hpke-classes-boringssl + 0.0.3.Final-SNAPSHOT + Netty/Incubator/Codec/OHTTP/HPKE/Classes/BoringSSL + + jar + + + io.netty.incubator.codec.hpke.classes.boringssl + + + + + + + maven-jar-plugin + 3.2.0 + + + default-jar + + + + true + + + ${javaModuleName} + + true + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + + + + + + io.netty + netty-common + + + io.netty.incubator + netty-incubator-codec-ohttp-hpke + ${project.version} + + + \ No newline at end of file diff --git a/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSL.java b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSL.java new file mode 100644 index 0000000..375189c --- /dev/null +++ b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSL.java @@ -0,0 +1,191 @@ +/* + * Copyright 2023 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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 io.netty.incubator.codec.hpke.boringssl; + +import io.netty.buffer.ByteBuf; +import io.netty.util.internal.ClassInitializerUtil; +import io.netty.util.internal.NativeLibraryLoader; +import io.netty.util.internal.PlatformDependent; + +import java.nio.ByteBuffer; +import java.util.Arrays; + +final class BoringSSL { + + static { + // Preload all classes that will be used in the OnLoad(...) function of JNI to eliminate the possiblity of a + // class-loader deadlock. This is a workaround for https://github.com/netty/netty/issues/11209. + + // This needs to match all the classes that are loaded via NETTY_JNI_UTIL_LOAD_CLASS or looked up via + // NETTY_JNI_UTIL_FIND_CLASS. + ClassInitializerUtil.tryLoadClasses(BoringSSL.class); + + try { + // First, try calling a side-effect free JNI method to see if the library was already + // loaded by the application. + BoringSSLNativeStaticallyReferencedJniMethods.EVP_hpke_x25519_hkdf_sha256(); + } catch (UnsatisfiedLinkError ignore) { + // The library was not previously loaded, load it now. + loadNativeLibrary(); + } + } + + private static void loadNativeLibrary() { + // This needs to be kept in sync with what is defined in netty_incubator_codec_ohttp_hpke_boringssl.c + String libName = "netty_incubator_codec_ohttp_hpke_boringssl"; + ClassLoader cl = PlatformDependent.getClassLoader(BoringSSL.class); + + if (!PlatformDependent.isAndroid()) { + libName += '_' + PlatformDependent.normalizedOs() + + '_' + PlatformDependent.normalizedArch(); + } + + try { + NativeLibraryLoader.load(libName, cl); + } catch (UnsatisfiedLinkError e) { + throw e; + } + } + + static final long EVP_hpke_x25519_hkdf_sha256 = + BoringSSLNativeStaticallyReferencedJniMethods.EVP_hpke_x25519_hkdf_sha256(); + static final long EVP_hpke_hkdf_sha256 = + BoringSSLNativeStaticallyReferencedJniMethods.EVP_hpke_hkdf_sha256(); + static final long EVP_hpke_aes_128_gcm = + BoringSSLNativeStaticallyReferencedJniMethods.EVP_hpke_aes_128_gcm(); + static final long EVP_hpke_aes_256_gcm = + BoringSSLNativeStaticallyReferencedJniMethods.EVP_hpke_aes_256_gcm(); + static final long EVP_hpke_chacha20_poly1305 = + BoringSSLNativeStaticallyReferencedJniMethods.EVP_hpke_chacha20_poly1305(); + + static final int EVP_AEAD_DEFAULT_TAG_LENGTH = + BoringSSLNativeStaticallyReferencedJniMethods.EVP_AEAD_DEFAULT_TAG_LENGTH(); + + static native long EVP_HPKE_CTX_new(); + static native long EVP_HPKE_CTX_cleanup(long ctx); + static native long EVP_HPKE_CTX_free(long ctx); + + // TODO: Do we also need the auth methods ? + static native byte[] EVP_HPKE_CTX_setup_sender( + long ctx, long kem, long kdf, long aead, byte[] peer_public_key, byte[] info); + static native byte[] EVP_HPKE_CTX_setup_sender_with_seed_for_testing( + long ctx, long kem, long kdf, long aead, byte[] peer_public_key, byte[] info, byte[] seed); + static native int EVP_HPKE_CTX_setup_recipient( + long ctx, long key, long kdf, long aead, byte[] enc, byte[] info); + + static native int EVP_HPKE_CTX_open( + long ctx, long out, int max_out_len, long in, int in_len, long ad, int ad_len); + static native int EVP_HPKE_CTX_seal( + long ctx, long out, int max_out_len, long in, int in_len, long ad, int ad_len); + static native int EVP_HPKE_CTX_export( + long ctx, long out, int secret_len, long context, int context_len); + + static native int EVP_HPKE_CTX_max_overhead(long ctx); + + static native long EVP_HPKE_KEY_new(); + static native void EVP_HPKE_KEY_free(long key); + static native void EVP_HPKE_KEY_cleanup(long key); + + static native int EVP_HPKE_KEY_init(long key, long kem, byte[] priv_key); + static native byte[] EVP_HPKE_KEY_public_key(long key); + static native byte[] EVP_HPKE_KEY_private_key(long key); + + static native int EVP_HPKE_KEM_public_key_len(long kem); + + static native long memory_address(ByteBuffer buffer); + + static native int EVP_AEAD_key_length(long aead); + static native int EVP_AEAD_nonce_length(long aead); + static native int EVP_AEAD_max_overhead(long aead); + static native int EVP_AEAD_max_tag_len(long aead); + + static native long EVP_AEAD_CTX_new(long aead, byte[] key, int tag_len); + static native void EVP_AEAD_CTX_cleanup(long ctx); + static native void EVP_AEAD_CTX_free(long ctx); + + static native int EVP_AEAD_CTX_seal( + long ctx, long out, int max_out_len, long nonce, int nonce_len, long in, int in_len, long ad, int ad_len); + + static native int EVP_AEAD_CTX_open( + long ctx, long out, int max_out_len, long nonce, int nonce_len, long in, int in_len, long ad, int ad_len); + + static long memory_address(ByteBuf buffer) { + if (buffer.hasMemoryAddress()) { + return buffer.memoryAddress(); + } + return memory_address(buffer.internalNioBuffer(0, buffer.capacity())); + } + + static long EVP_HPKE_CTX_new_or_throw() { + long ctx = BoringSSL.EVP_HPKE_CTX_new(); + if (ctx == -1) { + throw new IllegalStateException("Unable to allocate EVP_HPKE_CTX"); + } + return ctx; + } + + static void EVP_HPKE_CTX_cleanup_and_free(long ctx) { + if (ctx != -1) { + BoringSSL.EVP_HPKE_CTX_cleanup(ctx); + BoringSSL.EVP_HPKE_CTX_free(ctx); + } + } + + static long EVP_HPKE_KEY_new_or_throw() { + long key = BoringSSL.EVP_HPKE_KEY_new(); + if (key == -1) { + throw new IllegalStateException("Unable to allocate EVP_HPKE_KEY"); + } + return key; + } + + static long EVP_HPKE_KEY_new_and_init_or_throw(long kem, byte[] privateKeyBytes) { + long key = EVP_HPKE_KEY_new_or_throw(); + EVP_HPKE_KEY_init_or_throw(key, kem, privateKeyBytes); + return key; + } + + static void EVP_HPKE_KEY_init_or_throw(long key, long kem, byte[] privateKeyBytes) { + if (BoringSSL.EVP_HPKE_KEY_init(key, kem, privateKeyBytes) != 1) { + throw new IllegalArgumentException( + "privateKeyBytes does not contain a valid private key: " + Arrays.toString(privateKeyBytes)); + } + } + + static void EVP_HPKE_KEY_cleanup_and_free(long key) { + if (key != -1) { + BoringSSL.EVP_HPKE_KEY_cleanup(key); + BoringSSL.EVP_HPKE_KEY_free(key); + } + } + + static long EVP_AEAD_CTX_new_or_throw(long aead, byte[] key, int tagLen) { + long ctx = BoringSSL.EVP_AEAD_CTX_new(aead, key, tagLen); + if (ctx == -1) { + throw new IllegalStateException("Unable to allocate EVP_AEAD_CTX"); + } + return ctx; + } + + static void EVP_AEAD_CTX_cleanup_and_free(long ctx) { + if (ctx != -1) { + BoringSSL.EVP_AEAD_CTX_cleanup(ctx); + BoringSSL.EVP_AEAD_CTX_free(ctx); + } + } +} + + diff --git a/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLAEADContext.java b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLAEADContext.java new file mode 100644 index 0000000..33e4ef2 --- /dev/null +++ b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLAEADContext.java @@ -0,0 +1,81 @@ +/* + * Copyright 2023 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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 io.netty.incubator.codec.hpke.boringssl; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.incubator.codec.hpke.AEADContext; +import io.netty.incubator.codec.hpke.CryptoException; + +final class BoringSSLAEADContext extends BoringSSLCryptoContext implements AEADContext { + + private final ByteBuf baseNonce; + private final long baseNonceAddress; + private final int baseNonceLen; + private final int AEAD_max_overhead; + + private final BoringSSLCryptoOperation seal = new BoringSSLCryptoOperation() { + @Override + int maxOutLen(long ctx, int inReadable) { + return AEAD_max_overhead + inReadable; + } + + @Override + int execute(long ctx, long ad, int adLen, long in, int inLen, long out, int outLen) { + return BoringSSL.EVP_AEAD_CTX_seal(ctx, out, outLen, baseNonceAddress, baseNonceLen, in, inLen, ad, adLen); + } + }; + + private final BoringSSLCryptoOperation open = new BoringSSLCryptoOperation() { + @Override + int maxOutLen(long ctx, int inReadable) { + return inReadable; + } + + @Override + int execute(long ctx, long ad, int adLen, long in, int inLen, long out, int outLen) { + return BoringSSL.EVP_AEAD_CTX_open(ctx, out, outLen, baseNonceAddress, baseNonceLen, in, inLen, ad, adLen); + } + }; + + BoringSSLAEADContext(long ctx, int AEAD_max_overhead, byte[] baseNonce) { + super(ctx); + this.baseNonce = Unpooled.directBuffer(baseNonce.length).writeBytes(baseNonce); + this.baseNonceAddress = BoringSSL.memory_address(this.baseNonce); + this.baseNonceLen = this.baseNonce.readableBytes(); + this.AEAD_max_overhead = AEAD_max_overhead; + } + + @Override + protected void destroyCtx(long ctx) { + baseNonce.release(); + BoringSSL.EVP_AEAD_CTX_cleanup_and_free(ctx); + } + + @Override + public void open(ByteBuf aad, ByteBuf ct, ByteBuf out) throws CryptoException { + if (!open.execute(checkClosedAndReturnCtx(), aad, ct, out)) { + throw new CryptoException("open(...) failed"); + } + } + + @Override + public void seal(ByteBuf aad, ByteBuf pt, ByteBuf out) throws CryptoException { + if (!seal.execute(checkClosedAndReturnCtx(), aad, pt, out)) { + throw new CryptoException("seal(...) failed"); + } + } +} diff --git a/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLAsymmetricCipherKeyPair.java b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLAsymmetricCipherKeyPair.java new file mode 100644 index 0000000..f9a9454 --- /dev/null +++ b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLAsymmetricCipherKeyPair.java @@ -0,0 +1,65 @@ +/* + * Copyright 2023 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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 io.netty.incubator.codec.hpke.boringssl; + +import io.netty.incubator.codec.hpke.AsymmetricCipherKeyPair; +import io.netty.incubator.codec.hpke.AsymmetricKeyParameter; + +// TODO: Maybe expose sub-type which is ReferenceCounted and so allows to take ownership of EVP_HPKE_KEY. +final class BoringSSLAsymmetricCipherKeyPair implements AsymmetricCipherKeyPair { + + private final BoringSSLAsymmetricKeyParameter privateKey; + private final BoringSSLAsymmetricKeyParameter publicKey; + + BoringSSLAsymmetricCipherKeyPair(byte[] privateKeyBytes, byte[] publicKeyBytes) { + privateKey = new BoringSSLAsymmetricKeyParameter(privateKeyBytes, true); + publicKey = new BoringSSLAsymmetricKeyParameter(publicKeyBytes, false); + } + + @Override + public BoringSSLAsymmetricKeyParameter publicParameters() { + return publicKey; + } + + @Override + public BoringSSLAsymmetricKeyParameter privateParameters() { + return privateKey; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + BoringSSLAsymmetricCipherKeyPair that = (BoringSSLAsymmetricCipherKeyPair) o; + if (!privateKey.equals(that.privateKey)) { + return false; + } + return publicKey.equals(that.publicKey); + } + + @Override + public int hashCode() { + int result = privateKey.hashCode(); + result = 31 * result + publicKey.hashCode(); + return result; + } +} + diff --git a/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLAsymmetricKeyParameter.java b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLAsymmetricKeyParameter.java new file mode 100644 index 0000000..ff8b562 --- /dev/null +++ b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLAsymmetricKeyParameter.java @@ -0,0 +1,64 @@ +/* + * Copyright 2023 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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 io.netty.incubator.codec.hpke.boringssl; + +import io.netty.incubator.codec.hpke.AsymmetricKeyParameter; + +import java.util.Arrays; + +final class BoringSSLAsymmetricKeyParameter implements AsymmetricKeyParameter { + + final byte[] bytes; + private final boolean isPrivate; + + BoringSSLAsymmetricKeyParameter(byte[] bytes, boolean isPrivate) { + this.bytes = bytes; + this.isPrivate = isPrivate; + } + + @Override + public byte[] encoded() { + return bytes.clone(); + } + + @Override + public boolean isPrivate() { + return isPrivate; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + BoringSSLAsymmetricKeyParameter that = (BoringSSLAsymmetricKeyParameter) o; + if (isPrivate != that.isPrivate) { + return false; + } + return Arrays.equals(bytes, that.bytes); + } + + @Override + public int hashCode() { + int result = Arrays.hashCode(bytes); + result = 31 * result + (isPrivate ? 1 : 0); + return result; + } +} diff --git a/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLCryptoContext.java b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLCryptoContext.java new file mode 100644 index 0000000..e7a2de4 --- /dev/null +++ b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLCryptoContext.java @@ -0,0 +1,51 @@ +/* + * Copyright 2023 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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 io.netty.incubator.codec.hpke.boringssl; + +import io.netty.incubator.codec.hpke.CryptoContext; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * Abstract base class for BoringSSL based {@link CryptoContext}. + */ +abstract class BoringSSLCryptoContext implements CryptoContext { + + // We use an AtomicLong to reduce the possibility of crashing after the user called close(). + private final AtomicLong ctxRef; + + BoringSSLCryptoContext(long ctx) { + assert ctx != -1; + this.ctxRef = new AtomicLong(ctx); + } + protected final long checkClosedAndReturnCtx() { + long ctx = ctxRef.get(); + if (ctx == -1) { + throw new IllegalStateException(getClass().getSimpleName() + " closed"); + } + return ctx; + } + + @Override + public final void close() { + long ctx = ctxRef.getAndSet(-1); + if (ctx != -1) { + destroyCtx(ctx); + } + } + + protected abstract void destroyCtx(long ctx); +} diff --git a/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLCryptoOperation.java b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLCryptoOperation.java new file mode 100644 index 0000000..2139e5e --- /dev/null +++ b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLCryptoOperation.java @@ -0,0 +1,88 @@ +/* + * Copyright 2023 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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 io.netty.incubator.codec.hpke.boringssl; + +import io.netty.buffer.ByteBuf; + +abstract class BoringSSLCryptoOperation { + + final boolean execute(long ctx, ByteBuf aad, ByteBuf in, ByteBuf out) { + ByteBuf directAad = null; + ByteBuf directIn = null; + ByteBuf directOut = null; + try { + directAad = directReadable(aad); + directIn = directReadable(in); + + int maxOutLen = maxOutLen(ctx, in.readableBytes()); + directOut = directWritable(out, maxOutLen); + + long directAadAddress = BoringSSL.memory_address(directAad) + directAad.readerIndex(); + int directAddReadableBytes = directAad.readableBytes(); + long directInAddress = BoringSSL.memory_address(directIn) + directIn.readerIndex(); + int directInReadableBytes = directIn.readableBytes(); + long directOutAddress = BoringSSL.memory_address(directOut) + directOut.writerIndex(); + int directOutWritableBytes = directOut.writableBytes(); + int result = execute(ctx, directAadAddress, directAddReadableBytes, + directInAddress, directInReadableBytes, + directOutAddress, directOutWritableBytes); + if (result <= 0) { + return false; + } + aad.skipBytes(directAddReadableBytes); + in.skipBytes(directInReadableBytes); + // Move the writerIndex. + directOut.writerIndex(directOut.writerIndex() + result); + if (out != directOut) { + // If we allocated a temporary buffer we need to also copy over the result. + out.writeBytes(directOut); + } + return true; + } finally { + // Release temporary copies if any. + releaseIfNotTheSameInstance(aad, directAad); + releaseIfNotTheSameInstance(in, directIn); + releaseIfNotTheSameInstance(out, directOut); + } + } + + abstract int maxOutLen(long ctx, int inReadable); + + abstract int execute(long ctx, long ad, int adLen, long in, int inLen, long out, int outLen); + + private static ByteBuf directReadable(ByteBuf in) { + if (in.isDirect()) { + return in; + } + ByteBuf directIn = in.alloc().directBuffer(in.readableBytes()); + directIn.writeBytes(in, in.readerIndex(), in.readableBytes()); + return directIn; + } + + private static ByteBuf directWritable(ByteBuf out, int minWritable) { + if (out.isDirect()) { + out.ensureWritable(minWritable); + return out; + } + return out.alloc().directBuffer(minWritable); + } + + private static void releaseIfNotTheSameInstance(ByteBuf buf, ByteBuf maybeOther) { + if (maybeOther != null && maybeOther != buf) { + maybeOther.release(); + } + } +} diff --git a/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKE.java b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKE.java new file mode 100644 index 0000000..bc19a50 --- /dev/null +++ b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKE.java @@ -0,0 +1,65 @@ +/* + * Copyright 2023 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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 io.netty.incubator.codec.hpke.boringssl; + +public final class BoringSSLHPKE { + + @SuppressWarnings("unchecked") + private static final Throwable UNAVAILABILITY_CAUSE; + + static { + Throwable cause = null; + + try { + long ctx = BoringSSL.EVP_HPKE_KEY_new(); + BoringSSL.EVP_HPKE_CTX_cleanup_and_free(ctx); + } catch (Throwable error) { + cause = error; + } + + UNAVAILABILITY_CAUSE = cause; + } + + /** + * Returns {@code true} if and only if the HPKE implementation is usable on the running platform is available. + * + * @return {@code true} if this HPKE implementation can be used on the current platform, {@code false} otherwise. + */ + public static boolean isAvailable() { + return UNAVAILABILITY_CAUSE == null; + } + + /** + * Ensure that HPKE implementation is usable on the running platform is available. + * + * @throws UnsatisfiedLinkError if unavailable + */ + public static void ensureAvailability() { + if (UNAVAILABILITY_CAUSE != null) { + throw (Error) new UnsatisfiedLinkError( + "failed to load the required native library").initCause(UNAVAILABILITY_CAUSE); + } + } + + /** + * Returns the cause of unavailability. + * + * @return the cause if unavailable. {@code null} if available. + */ + public static Throwable unavailabilityCause() { + return UNAVAILABILITY_CAUSE; + } +} diff --git a/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKEContext.java b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKEContext.java new file mode 100644 index 0000000..077a16c --- /dev/null +++ b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKEContext.java @@ -0,0 +1,54 @@ +/* + * Copyright 2023 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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 io.netty.incubator.codec.hpke.boringssl; + +import io.netty.incubator.codec.hpke.HPKEContext; + +/** + * BoringSSL based {@link HPKEContext}. + */ +class BoringSSLHPKEContext extends BoringSSLCryptoContext implements HPKEContext { + + BoringSSLHPKEContext(long hpkeCtx) { + super(hpkeCtx); + } + + @Override + public final byte[] export(byte[] exportContext, int length) { + long ctx = checkClosedAndReturnCtx(); + // TODO: implement me + throw new UnsupportedOperationException(); + } + + @Override + public final byte[] extract(byte[] salt, byte[] ikm) { + long ctx = checkClosedAndReturnCtx(); + // TODO: implement me + throw new UnsupportedOperationException(); + } + + @Override + public final byte[] expand(byte[] prk, byte[] info, int length) { + long ctx = checkClosedAndReturnCtx(); + // TODO: implement me + throw new UnsupportedOperationException(); + } + + @Override + protected final void destroyCtx(long ctx) { + BoringSSL.EVP_HPKE_CTX_cleanup_and_free(ctx); + } +} diff --git a/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKERecipientContext.java b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKERecipientContext.java new file mode 100644 index 0000000..b94d5d9 --- /dev/null +++ b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKERecipientContext.java @@ -0,0 +1,49 @@ +/* + * Copyright 2023 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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 io.netty.incubator.codec.hpke.boringssl; + +import io.netty.buffer.ByteBuf; +import io.netty.incubator.codec.hpke.CryptoException; +import io.netty.incubator.codec.hpke.HPKERecipientContext; + +/** + * BoringSSL based {@link HPKERecipientContext}. + */ +final class BoringSSLHPKERecipientContext extends BoringSSLHPKEContext implements HPKERecipientContext { + // See https://github.com/google/boringssl/blob/ac45226f8d8223d70ed37cf81df5f03aea1d533c/include/openssl/hpke.h#L290 + private static final BoringSSLCryptoOperation OPEN = new BoringSSLCryptoOperation() { + @Override + int maxOutLen(long ctx, int inReadable) { + return inReadable; + } + + @Override + int execute(long ctx, long ad, int adLen, long in, int inLen, long out, int outLen) { + return BoringSSL.EVP_HPKE_CTX_open(ctx, out, outLen, in, inLen, ad, adLen); + } + }; + + BoringSSLHPKERecipientContext(long hpkeCtx) { + super(hpkeCtx); + } + + @Override + public void open(ByteBuf aad, ByteBuf ct, ByteBuf out) throws CryptoException { + if (!OPEN.execute(checkClosedAndReturnCtx(), aad, ct, out)) { + throw new CryptoException("open(...) failed"); + } + } +} diff --git a/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKESenderContext.java b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKESenderContext.java new file mode 100644 index 0000000..55c6cb5 --- /dev/null +++ b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLHPKESenderContext.java @@ -0,0 +1,60 @@ +/* + * Copyright 2023 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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 io.netty.incubator.codec.hpke.boringssl; + +import io.netty.buffer.ByteBuf; +import io.netty.incubator.codec.hpke.CryptoException; +import io.netty.incubator.codec.hpke.HPKERecipientContext; +import io.netty.incubator.codec.hpke.HPKESenderContext; + +/** + * BoringSSL based {@link HPKESenderContext}. + */ +final class BoringSSLHPKESenderContext extends BoringSSLHPKEContext + implements HPKESenderContext { + + // See https://github.com/google/boringssl/blob/ac45226f8d8223d70ed37cf81df5f03aea1d533c/include/openssl/hpke.h#L303 + private static final BoringSSLCryptoOperation SEAL = new BoringSSLCryptoOperation() { + @Override + int maxOutLen(long ctx, int inReadable) { + return BoringSSL.EVP_HPKE_CTX_max_overhead(ctx) + inReadable; + } + + @Override + int execute(long ctx, long ad, int adLen, long in, int inLen, long out, int outLen) { + return BoringSSL.EVP_HPKE_CTX_seal(ctx, out, outLen, in, inLen, ad, adLen); + } + }; + + private final byte[] encapsulation; + + BoringSSLHPKESenderContext(long hpkeCtx, byte[] encapsulation) { + super(hpkeCtx); + this.encapsulation = encapsulation; + } + + @Override + public byte[] encapsulation() { + return encapsulation.clone(); + } + + @Override + public void seal(ByteBuf aad, ByteBuf pt, ByteBuf out) throws CryptoException { + if (!SEAL.execute(checkClosedAndReturnCtx(), aad, pt, out)) { + throw new CryptoException("seal(...) failed"); + } + } +} diff --git a/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLNativeStaticallyReferencedJniMethods.java b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLNativeStaticallyReferencedJniMethods.java new file mode 100644 index 0000000..a975a22 --- /dev/null +++ b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLNativeStaticallyReferencedJniMethods.java @@ -0,0 +1,27 @@ +/* + * Copyright 2023 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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 io.netty.incubator.codec.hpke.boringssl; + +final class BoringSSLNativeStaticallyReferencedJniMethods { + static native long EVP_hpke_x25519_hkdf_sha256(); + static native long EVP_hpke_hkdf_sha256(); + static native long EVP_hpke_aes_128_gcm(); + static native long EVP_hpke_aes_256_gcm(); + static native long EVP_hpke_chacha20_poly1305(); + static native int EVP_AEAD_DEFAULT_TAG_LENGTH(); + + private BoringSSLNativeStaticallyReferencedJniMethods() { } +} diff --git a/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLOHttpCryptoProvider.java b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLOHttpCryptoProvider.java new file mode 100644 index 0000000..74fc506 --- /dev/null +++ b/codec-ohttp-hpke-classes-boringssl/src/main/java/io/netty/incubator/codec/hpke/boringssl/BoringSSLOHttpCryptoProvider.java @@ -0,0 +1,230 @@ +/* + * Copyright 2023 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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 io.netty.incubator.codec.hpke.boringssl; + + +import io.netty.incubator.codec.hpke.AEADContext; +import io.netty.incubator.codec.hpke.AsymmetricCipherKeyPair; +import io.netty.incubator.codec.hpke.AsymmetricKeyParameter; +import io.netty.incubator.codec.hpke.HPKERecipientContext; +import io.netty.incubator.codec.hpke.HPKESenderContext; +import io.netty.incubator.codec.hpke.OHttpCryptoProvider; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * BoringSSL based {@link OHttpCryptoProvider}. {@link BoringSSLHPKE#ensureAvailability()} or + * {@link BoringSSLHPKE#isAvailable()} should be used before accessing {@link #INSTANCE} to ensure + * the native library can be loaded on the used platform. + */ +public final class BoringSSLOHttpCryptoProvider implements OHttpCryptoProvider { + + private static final List SUPPORTED_AEAD_LIST = Collections.unmodifiableList(Arrays.asList(AEAD.values())); + private static final List SUPPORTED_MODE_LIST = Collections.singletonList(Mode.Base); + private static final List SUPPORTED_KEM_LIST = Collections.singletonList(KEM.X25519_SHA256); + private static final List SUPPORTED_KDF_LIST = Collections.singletonList(KDF.HKDF_SHA256); + + public static final BoringSSLOHttpCryptoProvider INSTANCE = new BoringSSLOHttpCryptoProvider(); + + private BoringSSLOHttpCryptoProvider() { } + + @Override + public AEADContext setupAEAD(AEAD aead, byte[] key, byte[] baseNonce) { + long boringSSLAead = boringSSLAEAD(aead); + int keyLength = BoringSSL.EVP_AEAD_key_length(boringSSLAead); + if (keyLength != key.length) { + throw new IllegalArgumentException("key length must be: " + keyLength); + } + int nounceLength = BoringSSL.EVP_AEAD_nonce_length(boringSSLAead); + if (nounceLength != baseNonce.length) { + throw new IllegalArgumentException("baseNonce length must be: " + nounceLength); + } + + int maxOverhead = BoringSSL.EVP_AEAD_max_overhead(boringSSLAead); + long ctx = BoringSSL.EVP_AEAD_CTX_new_or_throw(boringSSLAead, key, BoringSSL.EVP_AEAD_DEFAULT_TAG_LENGTH); + try { + BoringSSLAEADContext aeadCtx = new BoringSSLAEADContext(ctx, maxOverhead, baseNonce); + ctx = -1; + return aeadCtx; + } finally { + if (ctx != -1) { + BoringSSL.EVP_AEAD_CTX_cleanup_and_free(ctx); + } + } + } + + private static long boringSSLKDF(KDF kdf) { + if (kdf != KDF.HKDF_SHA256) { + throw new IllegalArgumentException("KDF not supported: "+ kdf); + } + return BoringSSL.EVP_hpke_hkdf_sha256; + } + + private static long boringSSLKEM(KEM kem) { + if (kem != KEM.X25519_SHA256) { + throw new IllegalArgumentException("KEM not supported: "+ kem); + } + return BoringSSL.EVP_hpke_x25519_hkdf_sha256; + } + + private static long boringSSLAEAD(AEAD aead) { + switch (aead) { + case AES_GCM128: + return BoringSSL.EVP_hpke_aes_128_gcm; + case AES_GCM256: + return BoringSSL.EVP_hpke_aes_256_gcm; + case CHACHA20_POLY1305: + return BoringSSL.EVP_hpke_chacha20_poly1305; + default: + throw new IllegalArgumentException("AEAD not supported: " + aead); + } + } + + private static void validateMode(Mode mode) { + // TODO: Also support AUTH + if (mode != Mode.Base) { + throw new IllegalArgumentException("Mode not supported: " + mode); + } + } + + @Override + public HPKESenderContext setupHPKEBaseS( + Mode mode, KEM kem, KDF kdf, AEAD aead, AsymmetricKeyParameter pkR, + byte[] info, AsymmetricCipherKeyPair kpE) { + validateMode(mode); + long boringSSLKem = boringSSLKEM(kem); + long boringSSLKdf = boringSSLKDF(kdf); + long boringSSLAead = boringSSLAEAD(aead); + final byte[] pkRBytes = encodedAsymmetricKeyParameter(pkR); + final byte[] encapsulation; + long ctx = BoringSSL.EVP_HPKE_CTX_new_or_throw(); + try { + if (kpE == null) { + encapsulation = BoringSSL.EVP_HPKE_CTX_setup_sender( + ctx, boringSSLKem, boringSSLKdf, boringSSLAead, pkRBytes, info); + } else { + encapsulation = BoringSSL.EVP_HPKE_CTX_setup_sender_with_seed_for_testing( + ctx, boringSSLKem, boringSSLKdf, boringSSLAead, pkRBytes, info, + // As we only support X25519 it is the right thing to just use the private key as seed. + // See https://github.com/google/boringssl/blob/master/include/openssl/hpke.h#L235C44-L235C50 + encodedAsymmetricKeyParameter(kpE.privateParameters())); + } + if (encapsulation == null) { + throw new IllegalStateException("Unable to init EVP_HPKE_CTX"); + } + BoringSSLHPKESenderContext hpkeCtx = + new BoringSSLHPKESenderContext(ctx, encapsulation); + ctx = -1; + return hpkeCtx; + } finally { + if (ctx != -1) { + BoringSSL.EVP_HPKE_CTX_cleanup_and_free(ctx); + } + } + } + + private static byte[] encodedAsymmetricKeyParameter(AsymmetricKeyParameter parameter) { + if (parameter instanceof BoringSSLAsymmetricKeyParameter) { + // No copy needed. + return ((BoringSSLAsymmetricKeyParameter) parameter).bytes; + } + return parameter.encoded(); + } + + @Override + public HPKERecipientContext setupHPKEBaseR(Mode mode, KEM kem, KDF kdf, AEAD aead, byte[] enc, + AsymmetricCipherKeyPair skR, byte[] info) { + validateMode(mode); + // Validate that KEM is supported by BoringSSL + long boringSSLKem = boringSSLKEM(kem); + long boringSSLKdf = boringSSLKDF(kdf); + long boringSSLAead = boringSSLAEAD(aead); + + long ctx = -1; + long key = -1; + try { + byte[] privateKeyBytes = encodedAsymmetricKeyParameter(skR.privateParameters()); + key = BoringSSL.EVP_HPKE_KEY_new_and_init_or_throw(boringSSLKem, privateKeyBytes); + ctx = BoringSSL.EVP_HPKE_CTX_new_or_throw(); + if (BoringSSL.EVP_HPKE_CTX_setup_recipient(ctx, key, boringSSLKdf, boringSSLAead, enc, info) != -1) { + throw new IllegalStateException("Unable to init EVP_HPKE_CTX"); + } + + BoringSSLHPKERecipientContext hpkeCtx = new BoringSSLHPKERecipientContext(ctx); + ctx = -1; + return hpkeCtx; + } finally { + BoringSSL.EVP_HPKE_KEY_cleanup_and_free(key); + if (ctx != -1) { + BoringSSL.EVP_HPKE_CTX_cleanup_and_free(ctx); + } + } + } + + @Override + public AsymmetricCipherKeyPair deserializePrivateKey(KEM kem, byte[] privateKeyBytes, byte[] publicKeyBytes) { + // Validate that KEM is supported by BoringSSL + long boringSSLKem = boringSSLKEM(kem); + + long key = -1; + try { + key = BoringSSL.EVP_HPKE_KEY_new_and_init_or_throw(boringSSLKem, privateKeyBytes); + byte[] extractedPublicKey = BoringSSL.EVP_HPKE_KEY_public_key(key); + if (!Arrays.equals(publicKeyBytes, extractedPublicKey)) { + throw new IllegalArgumentException( + "publicKeyBytes does not contain a valid public key: " + Arrays.toString(publicKeyBytes)); + } + // No need to clone extractedPublicKey as it was returned by our native call. + return new BoringSSLAsymmetricCipherKeyPair(privateKeyBytes.clone(), extractedPublicKey); + } finally { + BoringSSL.EVP_HPKE_KEY_cleanup_and_free(key); + } + } + + @Override + public AsymmetricKeyParameter deserializePublicKey(KEM kem, byte[] publicKeyBytes) { + // Validate that KEM is supported by BoringSSL. + long boringSSLKem = boringSSLKEM(kem); + // The best we can do is to check if the length is correct. + if (BoringSSL.EVP_HPKE_KEM_public_key_len(boringSSLKem) != publicKeyBytes.length) { + throw new IllegalArgumentException( + "publicKeyBytes does not contain a valid public key: " + Arrays.toString(publicKeyBytes)); + } + return new BoringSSLAsymmetricKeyParameter(publicKeyBytes.clone(), false); + } + + @Override + public List supportedAEAD() { + return SUPPORTED_AEAD_LIST; + } + + @Override + public List supportedKEM() { + return SUPPORTED_KEM_LIST; + } + + @Override + public List supportedKDF() { + return SUPPORTED_KDF_LIST; + } + + @Override + public List supportedMode() { + return SUPPORTED_MODE_LIST; + } +} diff --git a/codec-ohttp-hpke-native-boringssl/pom.xml b/codec-ohttp-hpke-native-boringssl/pom.xml new file mode 100644 index 0000000..ca0b4ed --- /dev/null +++ b/codec-ohttp-hpke-native-boringssl/pom.xml @@ -0,0 +1,603 @@ + + + + 4.0.0 + + io.netty.incubator + netty-incubator-codec-parent-ohttp + 0.0.3.Final-SNAPSHOT + + + netty-incubator-ohttp-hpke-native-boringssl + 0.0.3.Final-SNAPSHOT + Netty/Incubator/Codec/OHTTP/HPKE/Native/BoringSSL + + ${packaging.type} + + + ${os.detected.name}.${os.detected.arch} + ${javaModuleName}.${javaModuleNameClassifier} + io.netty.incubator.codec.hpke.boringssl + + io.netty.incubator.netty-incubator-codec-ohttp-hpke-classes-native + ${project.basedir}/src/main/c + ${project.build.directory}/native-lib-only + false + jar + ${os.detected.name}-${os.detected.arch} + netty_quiche_${os.detected.name}_${os.detected.arch} + ${project.build.directory}/netty-jni-util/ + ${project.build.directory}/boringssl-source + ${boringsslSourceDir}/build-target + ${project.build.directory}/boringssl + ${boringsslHomeDir}/build + ${boringsslHomeDir}/include + https://boringssl.googlesource.com/boringssl + + chromium-stable + dd5219451c3ce26221762a15d867edf43b463bb2 + + ${project.build.directory}/generated-sources + ${project.build.directory}/template + + + + + + + + + + + + + + + + + mac + + + mac + + + + + 10.12 + + -Wa,--noexecstack -mmacosx-version-min=${macosxDeploymentTarget} + -mmacosx-version-min=${macosxDeploymentTarget} + ${extraCflags} -O3 -fno-omit-frame-pointer + + ${extraCxxflags} -O3 -fno-omit-frame-pointer -Wno-error=range-loop-analysis + libssl.a + libcrypto.a + -platform_version,macos,${macosxDeploymentTarget},${macosxDeploymentTarget} -mmacosx-version-min=${macosxDeploymentTarget} + MACOSX_DEPLOYMENT_TARGET=${macosxDeploymentTarget} + META-INF/native/lib${jniLibName}.jnilib;osname=macos;osname=macosx;processor=${os.detected.arch} + + + + mac-m1-cross-compile + + netty_quiche_osx_aarch_64 + osx-aarch_64 + osx.aarch_64 + 11.0 + -target arm64-apple-macos11 + -target arm64-apple-macos11 + + -Wa,--noexecstack -target arm64-apple-macos11 + -DCMAKE_SYSTEM_PROCESSOR=arm64 -DCMAKE_OSX_ARCHITECTURES=arm64 + ${extraCflags} -O3 -fno-omit-frame-pointer + + ${extraCxxflags} -O3 -fno-omit-frame-pointer -Wno-error=range-loop-analysis + libssl.a + libcrypto.a + -arch arm64 -platform_version,macos,${macosxDeploymentTarget},${macosxDeploymentTarget} + --host=aarch64-apple-darwin + MACOSX_DEPLOYMENT_TARGET=${macosxDeploymentTarget} + META-INF/native/lib${jniLibName}.jnilib;osname=macos;osname=macosx;processor=aarch64 + + true + mac + + + + mac-intel-cross-compile + + netty_quiche_osx_x86_64 + osx-x86_64 + osx.x86_64 + 10.12 + -target x86_64-apple-macos10.12 -mmacosx-version-min=${macosxDeploymentTarget} + -target x86_64-apple-macos10.12 + + -Wa,--noexecstack -target x86_64-apple-macos10.12 -mmacosx-version-min=${macosxDeploymentTarget} + -DCMAKE_SYSTEM_PROCESSOR=x86_64 -DCMAKE_OSX_ARCHITECTURES=x86_64 + ${extraCflags} -O3 -fno-omit-frame-pointer + + ${extraCxxflags} -O3 -fno-omit-frame-pointer -Wno-error=range-loop-analysis + libssl.a + libcrypto.a + -arch x86_64 -platform_version,macos,${macosxDeploymentTarget},${macosxDeploymentTarget} -mmacosx-version-min=${macosxDeploymentTarget} + --host=x86_64-apple-darwin + MACOSX_DEPLOYMENT_TARGET=${macosxDeploymentTarget} + META-INF/native/lib${jniLibName}.jnilib;osname=macos;osname=macosx;processor=x86_64 + + true + mac + + + + linux + + + linux + + + + -O3 -fno-omit-frame-pointer + -O3 -fno-omit-frame-pointer + + -Wa,--noexecstack + ${extraCflags} + + ${extraCxxflags} -Wno-error=maybe-uninitialized -Wno-error=shadow -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS + libssl.a + libcrypto.a + -Wl,--strip-debug -Wl,--exclude-libs,ALL -Wl,-lrt + META-INF/native/lib${jniLibName}.so;osname=linux;processor=${os.detected.arch} + + + + linux-aarch64 + + -O3 -fno-omit-frame-pointer + -O3 -fno-omit-frame-pointer + + -Wa,--noexecstack + ${extraCflags} + + ${extraCxxflags} -Wno-error=maybe-uninitialized -Wno-error=shadow -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS + libssl.a + libcrypto.a + -Wl,--strip-debug -Wl,--exclude-libs,ALL + META-INF/native/lib${jniLibName}.so;osname=linux;processor=aarch64 + netty_quiche_linux_aarch_64 + linux-aarch_64 + linux.aarch_64 + --host=aarch64-linux-gnu + CC=aarch64-none-linux-gnu-gcc + -DCMAKE_SYSTEM_NAME=Linux -DCMAKE_SYSTEM_PROCESSOR=aarch64 -DCMAKE_C_COMPILER=aarch64-none-linux-gnu-gcc -DCMAKE_CXX_COMPILER=aarch64-none-linux-gnu-g++ + + true + linux + + + + + + + + kr.motd.maven + os-maven-plugin + 1.7.0 + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + generate-sources + + add-source + + + + ${nativeSourceDirectory} + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + + unpack + generate-sources + + unpack-dependencies + + + io.netty + netty-jni-util + sources + ${jniUtilIncludeDir} + **.h,**.c + false + true + + + + + + maven-antrun-plugin + + + + + build-boringssl + generate-sources + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + copy-src + generate-sources + + run + + + + + + + + + + + + + + + + + + + setup-template + generate-sources + + run + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + copy-native-lib-and-license + process-test-resources + + run + + + + + + + + + + + + + + + + + + + + + + + + copy-manifest + prepare-package + + run + + + + + + + + + + + + + org.apache.felix + maven-bundle-plugin + + + generate-manifest + process-classes + + manifest + + + + jar + bundle + + + ${project.groupId}.* + ${boringsslCommitSha} + ${boringsslBranch} + + + + + + + + maven-source-plugin + + + + + 2 + ${project.name} + ${project.groupId}.${project.artifactId}.source + ${project.organization.name} + ${parsedVersion.osgiVersion} + ${project.groupId}.${project.artifactId};version="${parsedVersion.osgiVersion}";roots:="." + + + + + + + attach-sources + prepare-package + + jar-no-fork + + + + attach-test-sources + prepare-package + + test-jar-no-fork + + + + + + + org.fusesource.hawtjni + hawtjni-maven-plugin + + + generate-native-lib + + ${jniLibName} + ${generatedSourcesDir} + ${templateDir} + msbuild + true + v142 + ${nativeLibOnlyDir} + true + + ${extraConfigureArg} + ${extraConfigureArg2} + --libdir=${project.build.directory}/native-build/target/lib + + + + generate + build + + + + + + + maven-jar-plugin + + + default-jar + + + + META-INF/native/** + META-INF/license/** + META-INF/NOTICE.txt + META-INF/LICENSE.txt + + + + true + + + ${javaModuleName} + + true + ${project.build.directory}/manifests/MANIFEST.MF + + + + + + native-jar + + jar + + + + + true + + + ${javaModuleNameWithClassifier} + ${fragmentHost} + ${bundleNativeCode} + + true + ${project.build.directory}/manifests/MANIFEST-native.MF + + ${jni.classifier} + + + + + + + + + + io.netty + netty-jni-util + ${netty.jni-util.version} + sources + true + + + diff --git a/codec-ohttp-hpke-native-boringssl/src/main/c/netty_incubator_codec_ohttp_hpke_boringssl.c b/codec-ohttp-hpke-native-boringssl/src/main/c/netty_incubator_codec_ohttp_hpke_boringssl.c new file mode 100644 index 0000000..6251ae7 --- /dev/null +++ b/codec-ohttp-hpke-native-boringssl/src/main/c/netty_incubator_codec_ohttp_hpke_boringssl.c @@ -0,0 +1,407 @@ +/* + * Copyright 2023 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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. + */ +#include +#include +#include +#include +#include + +#include "netty_jni_util.h" +#include "netty_incubator_codec_ohttp_hpke_boringssl.h" + +#include +#include + +// Add define if NETTY_QUIC_BUILD_STATIC is defined so it is picked up in netty_jni_util.c +#ifdef NETTY_OHTTP_HPKE_BORINGSSL_BUILD_STATIC +#define NETTY_JNI_UTIL_BUILD_STATIC +#endif + +#define STATICALLY_CLASSNAME "io/netty/incubator/codec/hpke/boringssl/BoringSSLNativeStaticallyReferencedJniMethods" +#define BORINGSSL_CLASSNAME "io/netty/incubator/codec/hpke/boringssl/BoringSSL" +#define LIBRARYNAME "netty_incubator_codec_ohttp_hpke_boringssl" + +static char const* staticPackagePrefix = NULL; + +static jbyteArray to_byte_array(JNIEnv* env, int result, const uint8_t* data, size_t out_len) { + if (result == 1) { + jbyteArray array = (*env)->NewByteArray(env, out_len); + (*env)->SetByteArrayRegion (env, array, 0, out_len, (jbyte *) data); + return array; + } + return NULL; +} + +static jlong netty_incubator_codec_ohttp_hpke_boringssl_EVP_hpke_x25519_hkdf_sha256(JNIEnv* env, jclass clazz) { + return (jlong) EVP_hpke_x25519_hkdf_sha256(); +} + +static jlong netty_incubator_codec_ohttp_hpke_boringssl_EVP_hpke_hkdf_sha256(JNIEnv* env, jclass clazz) { + return (jlong) EVP_hpke_hkdf_sha256(); +} + +static jlong netty_incubator_codec_ohttp_hpke_boringssl_EVP_hpke_aes_128_gcm(JNIEnv* env, jclass clazz) { + return (jlong) EVP_hpke_aes_128_gcm(); +} + +static jlong netty_incubator_codec_ohttp_hpke_boringssl_EVP_hpke_aes_256_gcm(JNIEnv* env, jclass clazz) { + return (jlong) EVP_hpke_aes_256_gcm(); +} + +static jlong netty_incubator_codec_ohttp_hpke_boringssl_EVP_hpke_chacha20_poly1305(JNIEnv* env, jclass clazz) { + return (jlong) EVP_hpke_chacha20_poly1305(); +} + +static jint netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_DEFAULT_TAG_LENGTH(JNIEnv* env, jclass clazz) { + return (jint) EVP_AEAD_DEFAULT_TAG_LENGTH; +} + +static jlong netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_new(JNIEnv* env, jclass clazz) { + return (jlong) EVP_HPKE_CTX_new(); +} + +static void netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_cleanup(JNIEnv* env, jclass clazz, jlong ctx) { + EVP_HPKE_CTX_cleanup((EVP_HPKE_CTX *) ctx); +} + +static void netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_free(JNIEnv* env, jclass clazz, jlong ctx) { + EVP_HPKE_CTX_free((EVP_HPKE_CTX *) ctx); +} + +static jbyteArray netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_setup_sender( + JNIEnv* env, jclass clazz, jlong ctx, jlong kem, jlong kdf, jlong aead, + jbyteArray peer_public_key_bytes, jbyteArray info_bytes) { + uint8_t out_enc[EVP_HPKE_MAX_ENC_LENGTH]; + size_t out_enc_len; + + size_t peer_public_key_len = (size_t) (*env)->GetArrayLength(env, peer_public_key_bytes); + const uint8_t *peer_public_key = (const uint8_t*) (*env)->GetByteArrayElements(env, peer_public_key_bytes, 0); + + size_t info_len = (size_t) (*env)->GetArrayLength(env, info_bytes); + const uint8_t *info = (const uint8_t*) (*env)->GetByteArrayElements(env, info_bytes, 0); + + int result = EVP_HPKE_CTX_setup_sender((EVP_HPKE_CTX *) ctx, (uint8_t *) out_enc, &out_enc_len, EVP_HPKE_MAX_ENC_LENGTH, + (const EVP_HPKE_KEM *) kem, (const EVP_HPKE_KDF *) kdf, (const EVP_HPKE_AEAD *) aead, + peer_public_key, peer_public_key_len, info, info_len); + + (*env)->ReleaseByteArrayElements(env, peer_public_key_bytes, (jbyte *) peer_public_key, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, info_bytes, (jbyte *) info, JNI_ABORT); + + return to_byte_array(env, result, (const uint8_t *) out_enc, out_enc_len); +} + +static jbyteArray netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_setup_sender_with_seed_for_testing( + JNIEnv* env, jclass clazz, jlong ctx, jlong kem, jlong kdf, jlong aead, + jbyteArray peer_public_key_bytes, jbyteArray info_bytes, jbyteArray seed_bytes) { + uint8_t out_enc[EVP_HPKE_MAX_ENC_LENGTH]; + size_t out_enc_len; + + size_t peer_public_key_len = (size_t) (*env)->GetArrayLength(env, peer_public_key_bytes); + const uint8_t *peer_public_key = (const uint8_t*) (*env)->GetByteArrayElements(env, peer_public_key_bytes, 0); + + size_t info_len = (size_t) (*env)->GetArrayLength(env, info_bytes); + const uint8_t *info = (const uint8_t*) (*env)->GetByteArrayElements(env, info_bytes, 0); + + size_t seed_len = (size_t) (*env)->GetArrayLength(env, seed_bytes); + const uint8_t *seed = (const uint8_t*) (*env)->GetByteArrayElements(env, seed_bytes, 0); + + int result = EVP_HPKE_CTX_setup_sender_with_seed_for_testing((EVP_HPKE_CTX *) ctx, (uint8_t *) out_enc, &out_enc_len, EVP_HPKE_MAX_ENC_LENGTH, + (const EVP_HPKE_KEM *) kem, (const EVP_HPKE_KDF *) kdf, (const EVP_HPKE_AEAD *) aead, + (const uint8_t *) peer_public_key, (size_t) peer_public_key_len, (const uint8_t *) info, (size_t) info_len, + (const uint8_t *) seed, (size_t) seed_len); + + (*env)->ReleaseByteArrayElements(env, peer_public_key_bytes, (jbyte *) peer_public_key, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, info_bytes, (jbyte *) info, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, seed_bytes, (jbyte *) seed, JNI_ABORT); + + return to_byte_array(env, result, (const uint8_t *) out_enc, out_enc_len); +} + +static jint netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_setup_recipient( + JNIEnv* env, jclass clazz, jlong ctx, jlong key, jlong kdf, + jlong aead, jbyteArray enc_bytes, + jbyteArray info_bytes) { + size_t enc_len = (size_t) (*env)->GetArrayLength(env, enc_bytes); + const uint8_t *enc = (const uint8_t*) (*env)->GetByteArrayElements(env, enc_bytes, 0); + size_t info_len = (size_t) (*env)->GetArrayLength(env, enc_bytes); + const uint8_t *info = (const uint8_t*) (*env)->GetByteArrayElements(env, info_bytes, 0); + + int result = EVP_HPKE_CTX_setup_recipient((EVP_HPKE_CTX *) ctx, (const EVP_HPKE_KEY *) key, (const EVP_HPKE_KDF *)kdf, + (const EVP_HPKE_AEAD *) aead, enc, enc_len, info, info_len); + + (*env)->ReleaseByteArrayElements(env, enc_bytes, (jbyte *) enc, JNI_ABORT); + (*env)->ReleaseByteArrayElements(env, info_bytes, (jbyte *) info, JNI_ABORT); + + return result; +} + +static jint netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_open( + JNIEnv* env, jclass clazz, jlong ctx, jlong out, + jint max_out_len, jlong in, jint in_len, jlong ad, jint ad_len) { + size_t out_len; + + int result = EVP_HPKE_CTX_open((EVP_HPKE_CTX *) ctx, (uint8_t *) out, &out_len, (size_t) max_out_len, + (const uint8_t *) in, (size_t) in_len, (const uint8_t *) ad, (size_t) ad_len); + return result == 1 ? (jint) out_len : -1; +} + +static jint netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_seal( + JNIEnv* env, jclass clazz, jlong ctx, jlong out, + jint max_out_len, jlong in, jint in_len, jlong ad, jint ad_len) { + size_t out_len; + + int result = EVP_HPKE_CTX_seal((EVP_HPKE_CTX *) ctx, (uint8_t *) out, &out_len, (size_t) max_out_len, + (const uint8_t *) in, (size_t) in_len, (const uint8_t *) ad, (size_t) ad_len); + + return result == 1 ? (jint) out_len : -1; +} + +static jint netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_export( + JNIEnv* env, jclass clazz, jlong ctx, jlong out, + jint secret_len, jlong context, jint context_len) { + + return (jint) EVP_HPKE_CTX_export((const EVP_HPKE_CTX *) ctx, (uint8_t *) out, (size_t) secret_len, + (const uint8_t *) context, (size_t) context_len); +} + + +static jint netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_max_overhead(JNIEnv* env, jclass clazz, jlong ctx) { + return (jint) EVP_HPKE_CTX_max_overhead((EVP_HPKE_CTX *) ctx); +} + +static jlong netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_KEY_new(JNIEnv* env, jclass clazz) { + return (jlong) EVP_HPKE_KEY_new(); +} + +static void netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_KEY_free(JNIEnv* env, jclass clazz, jlong key) { + EVP_HPKE_KEY_free((EVP_HPKE_KEY *) key); +} + +static jint netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_KEY_init(JNIEnv* env, jclass clazz, jlong key, jlong kem, jbyteArray priv_key_array) { + size_t priv_key_len = (size_t)(*env)->GetArrayLength(env, priv_key_array); + const uint8_t *priv_key = (const uint8_t*) (*env)->GetByteArrayElements(env, priv_key_array, 0); + + int result = EVP_HPKE_KEY_init((EVP_HPKE_KEY *) key, (const EVP_HPKE_KEM *) kem, priv_key, priv_key_len); + + (*env)->ReleaseByteArrayElements(env, priv_key_array, (jbyte *) priv_key, JNI_ABORT); + return (jint) result; +} + +static jbyteArray netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_KEY_public_key(JNIEnv* env, jclass clazz, jlong key) { + uint8_t out[EVP_HPKE_MAX_PUBLIC_KEY_LENGTH]; + size_t out_len; + + int result = EVP_HPKE_KEY_public_key((const EVP_HPKE_KEY *) key, (uint8_t *) out, &out_len, EVP_HPKE_MAX_PUBLIC_KEY_LENGTH); + return to_byte_array(env, result, (const uint8_t *) out, out_len); +} + +static jbyteArray netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_KEY_private_key(JNIEnv* env, jclass clazz, jlong key) { + uint8_t out[EVP_HPKE_MAX_PRIVATE_KEY_LENGTH]; + size_t out_len; + + int result = EVP_HPKE_KEY_private_key((const EVP_HPKE_KEY *) key, (uint8_t *) out, &out_len, EVP_HPKE_MAX_PRIVATE_KEY_LENGTH); + return to_byte_array(env, result, (const uint8_t *) out, out_len); +} + +static jint netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_KEM_public_key_len(JNIEnv* env, jclass clazz, jlong kem) { + return (jint) EVP_HPKE_KEM_public_key_len((EVP_HPKE_KEM *) kem); +} + +static jlong netty_incubator_codec_ohttp_hpke_boringssl_memory_address(JNIEnv* env, jclass clazz, jobject buffer) { + return (jlong) (*env)->GetDirectBufferAddress(env, buffer); +} + + +static jint netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_key_length(JNIEnv* env, jclass clazz, jlong aead) { + return (jint) EVP_AEAD_key_length((EVP_AEAD *) aead); +} +static jint netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_nonce_length(JNIEnv* env, jclass clazz, jlong aead) { + return (jint) EVP_AEAD_nonce_length((EVP_AEAD *) aead); +} +static jint netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_max_overhead(JNIEnv* env, jclass clazz, jlong aead) { + return (jint) EVP_AEAD_max_overhead((EVP_AEAD *) aead); +} +static jint netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_max_tag_len(JNIEnv* env, jclass clazz, jlong aead) { + return (jint) EVP_AEAD_max_tag_len((EVP_AEAD *) aead); +} + +static jlong netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_CTX_new(JNIEnv* env, jclass clazz, jlong aead, jbyteArray key_array, jint tag_len) { + size_t key_len = (size_t)(*env)->GetArrayLength(env, key_array); + const uint8_t *key = (const uint8_t*) (*env)->GetByteArrayElements(env, key_array, 0); + + EVP_AEAD_CTX* ctx = EVP_AEAD_CTX_new((const EVP_AEAD *) aead, key, key_len, (size_t) tag_len); + + (*env)->ReleaseByteArrayElements(env, key_array, (jbyte *) key, JNI_ABORT); + return (jlong) ctx; +} + +static void netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_CTX_cleanup(JNIEnv* env, jclass clazz, jlong ctx) { + EVP_AEAD_CTX_cleanup((EVP_AEAD_CTX *) ctx); +} + +static void netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_CTX_free(JNIEnv* env, jclass clazz, jlong ctx) { + EVP_AEAD_CTX_free((EVP_AEAD_CTX *) ctx); +} + +static jint netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_CTX_seal(JNIEnv* env, jclass clazz, jlong ctx, + jlong out, jint max_out_len, jlong nonce, jint nonce_len, + jlong in, jint in_len, jlong ad, jint ad_len) { + + size_t out_len; + int result = EVP_AEAD_CTX_seal((const EVP_AEAD_CTX *) ctx, (uint8_t *) out, &out_len, (size_t) max_out_len, + (const uint8_t *) nonce, (size_t) nonce_len, + (const uint8_t *) in, (size_t) in_len, + (const uint8_t *) ad, (size_t) ad_len); + + return result == 1 ? (jint) out_len : -1; +} + +static jint netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_CTX_open(JNIEnv* env, jclass clazz, jlong ctx, + jlong out, jint max_out_len, jlong nonce, jint nonce_len, + jlong in, jint in_len, jlong ad, jint ad_len) { + + size_t out_len; + int result = EVP_AEAD_CTX_open((const EVP_AEAD_CTX *) ctx, (uint8_t *) out, &out_len, (size_t) max_out_len, + (const uint8_t *) nonce, (size_t) nonce_len, + (const uint8_t *) in, (size_t) in_len, + (const uint8_t *) ad, (size_t) ad_len); + + return result == 1 ? (jint) out_len : -1; +} + +// JNI Registered Methods End + +// JNI Method Registration Table Begin +static const JNINativeMethod statically_referenced_fixed_method_table[] = { + { "EVP_hpke_x25519_hkdf_sha256", "()J", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_hpke_x25519_hkdf_sha256 }, + { "EVP_hpke_hkdf_sha256", "()J", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_hpke_hkdf_sha256 }, + { "EVP_hpke_aes_128_gcm", "()J", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_hpke_aes_128_gcm }, + { "EVP_hpke_aes_256_gcm", "()J", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_hpke_aes_256_gcm }, + { "EVP_hpke_chacha20_poly1305", "()J", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_hpke_chacha20_poly1305 }, + { "EVP_AEAD_DEFAULT_TAG_LENGTH", "()I", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_DEFAULT_TAG_LENGTH } +}; + +static const jint statically_referenced_fixed_method_table_size = sizeof(statically_referenced_fixed_method_table) / sizeof(statically_referenced_fixed_method_table[0]); +static const JNINativeMethod fixed_method_table[] = { + { "EVP_HPKE_CTX_new", "()J", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_new }, + { "EVP_HPKE_CTX_cleanup", "(J)V", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_cleanup }, + { "EVP_HPKE_CTX_free", "(J)V", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_free }, + { "EVP_HPKE_CTX_setup_sender", "(JJJJ[B[B)[B", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_setup_sender }, + { "EVP_HPKE_CTX_setup_sender_with_seed_for_testing", "(JJJJ[B[B[B)[B", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_setup_sender_with_seed_for_testing }, + { "EVP_HPKE_CTX_setup_recipient", "(JJJJ[B[B)I", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_setup_recipient }, + { "EVP_HPKE_CTX_open", "(JJIJIJI)I", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_open }, + { "EVP_HPKE_CTX_seal", "(JJIJIJI)I", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_seal }, + { "EVP_HPKE_CTX_export", "(JJIJI)I", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_export }, + { "EVP_HPKE_CTX_max_overhead", "(J)I", (void * ) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_CTX_max_overhead }, + { "EVP_HPKE_KEY_new", "()J", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_KEY_new }, + { "EVP_HPKE_KEY_free", "(J)V", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_KEY_free }, + { "EVP_HPKE_KEY_init", "(JJJI)I", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_KEY_init }, + { "EVP_HPKE_KEY_public_key", "(J)[B", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_KEY_public_key }, + { "EVP_HPKE_KEY_private_key", "(J)[B", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_KEY_private_key }, + { "EVP_HPKE_KEM_public_key_len", "(J)I", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_HPKE_KEM_public_key_len }, + { "memory_address", "(Ljava/nio/ByteBuffer;)J", (void *) netty_incubator_codec_ohttp_hpke_boringssl_memory_address }, + + { "EVP_AEAD_key_length", "(J)I", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_key_length }, + { "EVP_AEAD_nonce_length", "(J)I", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_nonce_length }, + { "EVP_AEAD_max_overhead", "(J)I", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_max_overhead }, + { "EVP_AEAD_max_tag_len", "(J)I", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_max_tag_len }, + + { "EVP_AEAD_CTX_new", "(J[BI)J", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_CTX_new }, + { "EVP_AEAD_CTX_cleanup", "(J)V", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_CTX_cleanup }, + { "EVP_AEAD_CTX_free", "(J)V", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_CTX_free }, + { "EVP_AEAD_CTX_seal", "(JJIJIJIJI)I", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_CTX_seal }, + { "EVP_AEAD_CTX_open", "(JJIJIJIJI)I", (void *) netty_incubator_codec_ohttp_hpke_boringssl_EVP_AEAD_CTX_open } + +}; + +static const jint fixed_method_table_size = sizeof(fixed_method_table) / sizeof(fixed_method_table[0]); + +// JNI Method Registration Table End + +// IMPORTANT: If you add any NETTY_JNI_UTIL_LOAD_CLASS or NETTY_JNI_UTIL_FIND_CLASS calls you also need to update +// Quiche to reflect that. +static jint netty_incubator_codec_ohttp_hpke_boringssl_JNI_OnLoad(JNIEnv* env, char const* packagePrefix) { + int ret = JNI_ERR; + int staticallyRegistered = 0; + int nativeRegistered = 0; + + // We must register the statically referenced methods first! + if (netty_jni_util_register_natives(env, + packagePrefix, + STATICALLY_CLASSNAME, + statically_referenced_fixed_method_table, + statically_referenced_fixed_method_table_size) != 0) { + goto done; + } + staticallyRegistered = 1; + + if (netty_jni_util_register_natives(env, + packagePrefix, + BORINGSSL_CLASSNAME, + fixed_method_table, + fixed_method_table_size) != 0) { + goto done; + } + nativeRegistered = 1; + + // Initialize this module + + staticPackagePrefix = packagePrefix; + + ret = NETTY_JNI_UTIL_JNI_VERSION; +done: + if (ret == JNI_ERR) { + if (staticallyRegistered == 1) { + netty_jni_util_unregister_natives(env, packagePrefix, STATICALLY_CLASSNAME); + } + if (nativeRegistered == 1) { + netty_jni_util_unregister_natives(env, packagePrefix, BORINGSSL_CLASSNAME); + } + } + return ret; +} + +static void netty_incubator_codec_ohttp_hpke_boringssl_JNI_OnUnload(JNIEnv* env) { + netty_jni_util_unregister_natives(env, staticPackagePrefix, STATICALLY_CLASSNAME); + netty_jni_util_unregister_natives(env, staticPackagePrefix, BORINGSSL_CLASSNAME); + free((void*) staticPackagePrefix); + staticPackagePrefix = NULL; +} + +// Invoked by the JVM when statically linked + +// We build with -fvisibility=hidden so ensure we mark everything that needs to be visible with JNIEXPORT +// https://mail.openjdk.java.net/pipermail/core-libs-dev/2013-February/014549.html + +// Invoked by the JVM when statically linked +JNIEXPORT jint JNI_OnLoad_netty_incubator_codec_ohttp_hpke_boringssl(JavaVM* vm, void* reserved) { + return netty_jni_util_JNI_OnLoad(vm, reserved, LIBRARYNAME, netty_incubator_codec_ohttp_hpke_boringssl_JNI_OnLoad); +} + +// Invoked by the JVM when statically linked +JNIEXPORT void JNI_OnUnload_netty_incubator_codec_ohttp_hpke_boringssl(JavaVM* vm, void* reserved) { + netty_jni_util_JNI_OnUnload(vm, reserved, netty_incubator_codec_ohttp_hpke_boringssl_JNI_OnUnload); +} + +#ifndef NETTY_OHTTP_HPKE_BORINGSSL_BUILD_STATIC +JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { + return netty_jni_util_JNI_OnLoad(vm, reserved, LIBRARYNAME, netty_incubator_codec_ohttp_hpke_boringssl_JNI_OnLoad); +} + +JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved) { + netty_jni_util_JNI_OnUnload(vm, reserved, netty_incubator_codec_ohttp_hpke_boringssl_JNI_OnUnload); +} +#endif /* NETTY_OHTTP_HPKE_BORINGSSL_BUILD_STATIC */ diff --git a/codec-ohttp-hpke-native-boringssl/src/main/c/netty_incubator_codec_ohttp_hpke_boringssl.h b/codec-ohttp-hpke-native-boringssl/src/main/c/netty_incubator_codec_ohttp_hpke_boringssl.h new file mode 100644 index 0000000..70aacec --- /dev/null +++ b/codec-ohttp-hpke-native-boringssl/src/main/c/netty_incubator_codec_ohttp_hpke_boringssl.h @@ -0,0 +1,20 @@ +/* + * Copyright 2023 The Netty Project + * + * The Netty Project licenses this file to you 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: + * + * https://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. + */ +#ifndef NETTY_OHTTP_HPKE_BORINGSSL_H_ +#define NETTY_OHTTP_HPKE_BORINGSSL_H_ + + +#endif /* NETTY_OHTTP_HPKE_BORINGSSL_H_ */ diff --git a/codec-ohttp-hpke-native-boringssl/src/main/native-package/m4/custom.m4.template b/codec-ohttp-hpke-native-boringssl/src/main/native-package/m4/custom.m4.template new file mode 100644 index 0000000..56d614a --- /dev/null +++ b/codec-ohttp-hpke-native-boringssl/src/main/native-package/m4/custom.m4.template @@ -0,0 +1,39 @@ +dnl --------------------------------------------------------------------------- +dnl Copyright 2023 The Netty Project +dnl +dnl Licensed under the Apache License, Version 2.0 (the "License"); +dnl you may not use this file except in compliance with the License. +dnl You may obtain a copy of the License at +dnl +dnl http://www.apache.org/licenses/LICENSE-2.0 +dnl +dnl Unless required by applicable law or agreed to in writing, software +dnl distributed under the License is distributed on an "AS IS" BASIS, +dnl WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +dnl See the License for the specific language governing permissions and +dnl limitations under the License. +dnl --------------------------------------------------------------------------- + +AC_DEFUN([CUSTOM_M4_SETUP], +[ + dnl Ensure we only expose what we really need + case $host in + *-darwin*) + LDFLAGS="$LDFLAGS -Wl,-exported_symbol,_JNI_*" + ;; + *linux*) + LDFLAGS="$LDFLAGS -Wl,--exclude-libs,ALL" + ;; + *) + ;; + esac + + dnl Update the compiler/linker flags + CFLAGS="$CFLAGS -std=gnu99 -fvisibility=hidden -Werror -fno-omit-frame-pointer -Wunused -Wno-unused-value -O3 -I@BORINGSSL_INCLUDE_DIR@ -I@QUICHE_INCLUDE_DIR@ @EXTRA_CFLAGS@" + CXXFLAGS="$CXXFLAGS" + LDFLAGS="$LDFLAGS -L@BORINGSSL_LIB_DIR@ -lssl -lcrypto @EXTRA_LDFLAGS@" + AC_SUBST(CFLAGS) + AC_SUBST(CXXFLAGS) + AC_SUBST(LDFLAGS) +]) + diff --git a/codec-ohttp-hpke-native-boringssl/src/main/native-package/vs2010.custom.props.template b/codec-ohttp-hpke-native-boringssl/src/main/native-package/vs2010.custom.props.template new file mode 100644 index 0000000..352f1a9 --- /dev/null +++ b/codec-ohttp-hpke-native-boringssl/src/main/native-package/vs2010.custom.props.template @@ -0,0 +1,26 @@ + + + + + + $(JAVA_HOME)\include;$(JAVA_HOME)\include\win32;@BORINGSSL_INCLUDE_DIR@;%(AdditionalIncludeDirectories) + + + ntdll.lib;ws2_32.lib;crypt32.lib;bcrypt.lib;userenv.lib;@BORINGSSL_LIB_DIR@\@SSL_LIB@;@BORINGSSL_LIB_DIR@\@CRYPTO_LIB@;%(AdditionalDependencies) + + + \ No newline at end of file diff --git a/codec-ohttp-hpke/src/main/java/io/netty/incubator/codec/hpke/OHttpCryptoProvider.java b/codec-ohttp-hpke/src/main/java/io/netty/incubator/codec/hpke/OHttpCryptoProvider.java index c361236..a36912c 100644 --- a/codec-ohttp-hpke/src/main/java/io/netty/incubator/codec/hpke/OHttpCryptoProvider.java +++ b/codec-ohttp-hpke/src/main/java/io/netty/incubator/codec/hpke/OHttpCryptoProvider.java @@ -15,6 +15,8 @@ */ package io.netty.incubator.codec.hpke; +import java.util.List; + /** * Provides methods to handle Hybrid Public Key Encryption * for oHTTP. Because of that the functionality is limited to what is needed for oHTTP. @@ -80,6 +82,34 @@ HPKERecipientContext setupHPKEBaseR(Mode mode, KEM kem, KDF kdf, AEAD aead, byte */ AsymmetricKeyParameter deserializePublicKey(KEM kem, byte[] publicKeyBytes); + /** + * Returns an immutable {@link List} of all supported {@link AEAD}s. + * + * @return supported {@link AEAD}s. + */ + List supportedAEAD(); + + /** + * Returns an immutable {@link List} of all supported {@link KEM}s. + * + * @return supported {@link KEM}s. + */ + List supportedKEM(); + + /** + * Returns an immutable {@link List} of all supported {@link KDF}s. + * + * @return supported {@link KDF}s. + */ + List supportedKDF(); + + /** + * Returns an immutable {@link List} of all supported {@link Mode}s. + * + * @return supported {@link Mode}s. + */ + List supportedMode(); + /** * Hybrid Public Key Encryption */ diff --git a/docker/Dockerfile.centos7 b/docker/Dockerfile.centos7 index 929d209..57d6606 100644 --- a/docker/Dockerfile.centos7 +++ b/docker/Dockerfile.centos7 @@ -1,20 +1,44 @@ FROM centos:7 ENV SOURCE_DIR /root/source +ENV LIBS_DIR /root/libs +ENV CMAKE_VERSION_BASE 3.26 +ENV CMAKE_VERSION $CMAKE_VERSION_BASE.4 +ENV NINJA_VERSION 1.7.2 +ENV GO_VERSION 1.9.3 +ENV MAVEN_VERSION 3.9.1 # install dependencies RUN yum install -y \ + apr-devel \ + autoconf \ + automake \ bzip2 \ git \ + glibc-devel \ gnupg \ + libtool \ + lsb-core \ + make \ + perl \ tar \ unzip \ wget \ - zip + zip \ + zlib-devel RUN mkdir $SOURCE_DIR WORKDIR $SOURCE_DIR +RUN yum install -y centos-release-scl + +RUN yum -y install devtoolset-11-gcc devtoolset-11-gcc-c++ +RUN echo 'source /opt/rh/devtoolset-11/enable' >> ~/.bashrc + +RUN wget -q https://github.com/ninja-build/ninja/releases/download/v$NINJA_VERSION/ninja-linux.zip && unzip ninja-linux.zip && mkdir -p /opt/ninja-$NINJA_VERSION/bin && mv ninja /opt/ninja-$NINJA_VERSION/bin && echo 'PATH=/opt/ninja-$NINJA_VERSION/bin:$PATH' >> ~/.bashrc +RUN wget -q https://storage.googleapis.com/golang/go$GO_VERSION.linux-amd64.tar.gz && tar zxf go$GO_VERSION.linux-amd64.tar.gz && mv go /opt/ && echo 'PATH=/opt/go/bin:$PATH' >> ~/.bashrc && echo 'export GOROOT=/opt/go/' >> ~/.bashrc +RUN curl -s https://cmake.org/files/v$CMAKE_VERSION_BASE/cmake-$CMAKE_VERSION-linux-x86_64.tar.gz --output cmake-$CMAKE_VERSION-linux-x86_64.tar.gz && tar zvxf cmake-$CMAKE_VERSION-linux-x86_64.tar.gz && mv cmake-$CMAKE_VERSION-linux-x86_64 /opt/ && echo 'PATH=/opt/cmake-$CMAKE_VERSION-linux-x86_64/bin:$PATH' >> ~/.bashrc + # Downloading and installing SDKMAN! RUN curl -s "https://get.sdkman.io" | bash @@ -24,8 +48,21 @@ ENV JAVA_VERSION $java_version # Installing Java removing some unnecessary SDKMAN files RUN bash -c "source $HOME/.sdkman/bin/sdkman-init.sh && \ yes | sdk install java $JAVA_VERSION && \ + yes | sdk install maven $MAVEN_VERSION && \ rm -rf $HOME/.sdkman/archives/* && \ rm -rf $HOME/.sdkman/tmp/*" RUN echo 'export JAVA_HOME="/root/.sdkman/candidates/java/current"' >> ~/.bashrc -RUN echo 'PATH=$JAVA_HOME/bin:$PATH' >> ~/.bashrc \ No newline at end of file +RUN echo 'PATH=$JAVA_HOME/bin:$PATH' >> ~/.bashrc + +# Prepare our own build +ENV PATH /root/.sdkman/candidates/maven/current:$PATH +ENV JAVA_HOME /root/.sdkman/candidates/java/ + +# Cleanup +RUN rm -rf $SOURCE_DIR +RUN yum clean all && \ + rm -rf /var/cache/yum + +# when the JDK is GraalVM install native-image +RUN if [ -O /root/.sdkman/candidates/java/current/bin/gu ]; then /root/.sdkman/candidates/java/current/bin/gu install native-image; else echo "Not GraalVM, skip installation of native-image" ; fi diff --git a/pom.xml b/pom.xml index fbb4c02..46e6285 100644 --- a/pom.xml +++ b/pom.xml @@ -70,6 +70,8 @@ codec-bhttp codec-ohttp-hpke codec-ohttp-hpke-bouncycastle + codec-ohttp-hpke-classes-boringssl + codec-ohttp-hpke-native-boringssl