8297878: KEM: Implementation

Reviewed-by: ascarpino, mullan
This commit is contained in:
Weijun Wang 2023-05-30 16:29:19 +00:00
parent 21af8bae38
commit 6b90b0519e
12 changed files with 2324 additions and 21 deletions

View file

@ -0,0 +1,395 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.crypto.provider;
import sun.security.jca.JCAUtil;
import sun.security.ssl.HKDF;
import sun.security.util.*;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.interfaces.*;
import java.security.spec.*;
import java.util.Arrays;
import java.util.Objects;
// Implementing DHKEM defined inside https://www.rfc-editor.org/rfc/rfc9180.html,
// without the AuthEncap and AuthDecap functions
public class DHKEM implements KEMSpi {
private static final byte[] KEM = new byte[]
{'K', 'E', 'M'};
private static final byte[] EAE_PRK = new byte[]
{'e', 'a', 'e', '_', 'p', 'r', 'k'};
private static final byte[] SHARED_SECRET = new byte[]
{'s', 'h', 'a', 'r', 'e', 'd', '_', 's', 'e', 'c', 'r', 'e', 't'};
private static final byte[] DKP_PRK = new byte[]
{'d', 'k', 'p', '_', 'p', 'r', 'k'};
private static final byte[] CANDIDATE = new byte[]
{'c', 'a', 'n', 'd', 'i', 'd', 'a', 't', 'e'};
private static final byte[] SK = new byte[]
{'s', 'k'};
private static final byte[] HPKE_V1 = new byte[]
{'H', 'P', 'K', 'E', '-', 'v', '1'};
private static final byte[] EMPTY = new byte[0];
private record Handler(Params params, SecureRandom secureRandom,
PrivateKey skR, PublicKey pkR)
implements EncapsulatorSpi, DecapsulatorSpi {
@Override
public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) {
Objects.checkFromToIndex(from, to, params.Nsecret);
Objects.requireNonNull(algorithm, "null algorithm");
KeyPair kpE = params.generateKeyPair(secureRandom);
PrivateKey skE = kpE.getPrivate();
PublicKey pkE = kpE.getPublic();
byte[] pkEm = params.SerializePublicKey(pkE);
byte[] pkRm = params.SerializePublicKey(pkR);
byte[] kem_context = concat(pkEm, pkRm);
try {
byte[] dh = params.DH(skE, pkR);
byte[] key = params.ExtractAndExpand(dh, kem_context);
return new KEM.Encapsulated(
new SecretKeySpec(key, from, to - from, algorithm),
pkEm, null);
} catch (Exception e) {
throw new ProviderException("internal error", e);
}
}
@Override
public SecretKey engineDecapsulate(byte[] encapsulation,
int from, int to, String algorithm) throws DecapsulateException {
Objects.checkFromToIndex(from, to, params.Nsecret);
Objects.requireNonNull(algorithm, "null algorithm");
Objects.requireNonNull(encapsulation, "null encapsulation");
if (encapsulation.length != params.Npk) {
throw new DecapsulateException("incorrect encapsulation size");
}
try {
PublicKey pkE = params.DeserializePublicKey(encapsulation);
byte[] dh = params.DH(skR, pkE);
byte[] pkRm = params.SerializePublicKey(pkR);
byte[] kem_context = concat(encapsulation, pkRm);
byte[] key = params.ExtractAndExpand(dh, kem_context);
return new SecretKeySpec(key, from, to - from, algorithm);
} catch (IOException | InvalidKeyException e) {
throw new DecapsulateException("Cannot decapsulate", e);
} catch (Exception e) {
throw new ProviderException("internal error", e);
}
}
@Override
public int engineSecretSize() {
return params.Nsecret;
}
@Override
public int engineEncapsulationSize() {
return params.Npk;
}
}
// Not really a random. For KAT test only. It generates key pair from ikm.
public static class RFC9180DeriveKeyPairSR extends SecureRandom {
static final long serialVersionUID = 0L;
private final byte[] ikm;
public RFC9180DeriveKeyPairSR(byte[] ikm) {
super(null, null); // lightest constructor
this.ikm = ikm;
}
public KeyPair derive(Params params) {
try {
return params.deriveKeyPair(ikm);
} catch (Exception e) {
throw new UnsupportedOperationException(e);
}
}
public KeyPair derive(int kem_id) {
Params params = Arrays.stream(Params.values())
.filter(p -> p.kem_id == kem_id)
.findFirst()
.orElseThrow();
return derive(params);
}
}
private enum Params {
P256(0x10, 32, 32, 2 * 32 + 1,
"ECDH", "EC", CurveDB.P_256, "SHA-256"),
P384(0x11, 48, 48, 2 * 48 + 1,
"ECDH", "EC", CurveDB.P_384, "SHA-384"),
P521(0x12, 64, 66, 2 * 66 + 1,
"ECDH", "EC", CurveDB.P_521, "SHA-512"),
X25519(0x20, 32, 32, 32,
"XDH", "XDH", NamedParameterSpec.X25519, "SHA-256"),
X448(0x21, 64, 56, 56,
"XDH", "XDH", NamedParameterSpec.X448, "SHA-512"),
;
private final int kem_id;
private final int Nsecret;
private final int Nsk;
private final int Npk;
private final String kaAlgorithm;
private final String keyAlgorithm;
private final AlgorithmParameterSpec spec;
private final String hkdfAlgorithm;
private final byte[] suiteId;
Params(int kem_id, int Nsecret, int Nsk, int Npk,
String kaAlgorithm, String keyAlgorithm, AlgorithmParameterSpec spec,
String hkdfAlgorithm) {
this.kem_id = kem_id;
this.spec = spec;
this.Nsecret = Nsecret;
this.Nsk = Nsk;
this.Npk = Npk;
this.kaAlgorithm = kaAlgorithm;
this.keyAlgorithm = keyAlgorithm;
this.hkdfAlgorithm = hkdfAlgorithm;
suiteId = concat(KEM, I2OSP(kem_id, 2));
}
private boolean isEC() {
return this == P256 || this == P384 || this == P521;
}
private KeyPair generateKeyPair(SecureRandom sr) {
if (sr instanceof RFC9180DeriveKeyPairSR r9) {
return r9.derive(this);
}
try {
KeyPairGenerator g = KeyPairGenerator.getInstance(keyAlgorithm);
g.initialize(spec, sr);
return g.generateKeyPair();
} catch (Exception e) {
throw new ProviderException("internal error", e);
}
}
private byte[] SerializePublicKey(PublicKey k) {
if (isEC()) {
ECPoint w = ((ECPublicKey) k).getW();
return ECUtil.encodePoint(w, ((NamedCurve) spec).getCurve());
} else {
byte[] uArray = ((XECPublicKey) k).getU().toByteArray();
ArrayUtil.reverse(uArray);
return Arrays.copyOf(uArray, Npk);
}
}
private PublicKey DeserializePublicKey(byte[] data)
throws IOException, NoSuchAlgorithmException, InvalidKeySpecException {
KeySpec keySpec;
if (isEC()) {
NamedCurve curve = (NamedCurve) this.spec;
keySpec = new ECPublicKeySpec(
ECUtil.decodePoint(data, curve.getCurve()), curve);
} else {
data = data.clone();
ArrayUtil.reverse(data);
keySpec = new XECPublicKeySpec(
this.spec, new BigInteger(1, data));
}
return KeyFactory.getInstance(keyAlgorithm).generatePublic(keySpec);
}
private byte[] DH(PrivateKey skE, PublicKey pkR)
throws NoSuchAlgorithmException, InvalidKeyException {
KeyAgreement ka = KeyAgreement.getInstance(kaAlgorithm);
ka.init(skE);
ka.doPhase(pkR, true);
return ka.generateSecret();
}
private byte[] ExtractAndExpand(byte[] dh, byte[] kem_context)
throws NoSuchAlgorithmException, InvalidKeyException {
HKDF kdf = new HKDF(hkdfAlgorithm);
SecretKey eae_prk = LabeledExtract(kdf, suiteId, null, EAE_PRK, dh);
return LabeledExpand(kdf, suiteId, eae_prk, SHARED_SECRET,
kem_context, Nsecret);
}
private PublicKey getPublicKey(PrivateKey sk)
throws InvalidKeyException {
if (!(sk instanceof InternalPrivateKey)) {
try {
KeyFactory kf = KeyFactory.getInstance(keyAlgorithm, "SunEC");
sk = (PrivateKey) kf.translateKey(sk);
} catch (Exception e) {
throw new InvalidKeyException("Error translating key", e);
}
}
if (sk instanceof InternalPrivateKey ik) {
try {
return ik.calculatePublicKey();
} catch (UnsupportedOperationException e) {
throw new InvalidKeyException("Error retrieving key", e);
}
} else {
// Should not happen, unless SunEC goes wrong
throw new ProviderException("Unknown key");
}
}
// For KAT tests only. See RFC9180DeriveKeyPairSR.
public KeyPair deriveKeyPair(byte[] ikm) throws Exception {
HKDF kdf = new HKDF(hkdfAlgorithm);
SecretKey dkp_prk = LabeledExtract(kdf, suiteId, null, DKP_PRK, ikm);
if (isEC()) {
NamedCurve curve = (NamedCurve) spec;
BigInteger sk = BigInteger.ZERO;
int counter = 0;
while (sk.signum() == 0 || sk.compareTo(curve.getOrder()) >= 0) {
if (counter > 255) {
throw new RuntimeException();
}
byte[] bytes = LabeledExpand(kdf, suiteId, dkp_prk,
CANDIDATE, I2OSP(counter, 1), Nsk);
// bitmask is defined to be 0xFF for P-256 and P-384, and 0x01 for P-521
if (this == Params.P521) {
bytes[0] = (byte) (bytes[0] & 0x01);
}
sk = new BigInteger(1, (bytes));
counter = counter + 1;
}
PrivateKey k = DeserializePrivateKey(sk.toByteArray());
return new KeyPair(getPublicKey(k), k);
} else {
byte[] sk = LabeledExpand(kdf, suiteId, dkp_prk, SK, EMPTY, Nsk);
PrivateKey k = DeserializePrivateKey(sk);
return new KeyPair(getPublicKey(k), k);
}
}
private PrivateKey DeserializePrivateKey(byte[] data) throws Exception {
KeySpec keySpec = isEC()
? new ECPrivateKeySpec(new BigInteger(1, (data)), (NamedCurve) spec)
: new XECPrivateKeySpec(spec, data);
return KeyFactory.getInstance(keyAlgorithm).generatePrivate(keySpec);
}
}
private static SecureRandom getSecureRandom(SecureRandom userSR) {
return userSR != null ? userSR : JCAUtil.getSecureRandom();
}
@Override
public EncapsulatorSpi engineNewEncapsulator(
PublicKey pk, AlgorithmParameterSpec spec, SecureRandom secureRandom)
throws InvalidAlgorithmParameterException, InvalidKeyException {
if (pk == null) {
throw new InvalidKeyException("input key is null");
}
if (spec != null) {
throw new InvalidAlgorithmParameterException("no spec needed");
}
Params params = paramsFromKey(pk);
return new Handler(params, getSecureRandom(secureRandom), null, pk);
}
@Override
public DecapsulatorSpi engineNewDecapsulator(PrivateKey sk, AlgorithmParameterSpec spec)
throws InvalidAlgorithmParameterException, InvalidKeyException {
if (sk == null) {
throw new InvalidKeyException("input key is null");
}
if (spec != null) {
throw new InvalidAlgorithmParameterException("no spec needed");
}
Params params = paramsFromKey(sk);
return new Handler(params, null, sk, params.getPublicKey(sk));
}
private Params paramsFromKey(Key k) throws InvalidKeyException {
if (k instanceof ECKey eckey) {
if (ECUtil.equals(eckey.getParams(), CurveDB.P_256)) {
return Params.P256;
} else if (ECUtil.equals(eckey.getParams(), CurveDB.P_384)) {
return Params.P384;
} else if (ECUtil.equals(eckey.getParams(), CurveDB.P_521)) {
return Params.P521;
}
} else if (k instanceof XECKey xkey
&& xkey.getParams() instanceof NamedParameterSpec ns) {
if (ns.getName().equals("X25519")) {
return Params.X25519;
} else if (ns.getName().equals("X448")) {
return Params.X448;
}
}
throw new InvalidKeyException("Unsupported key");
}
private static byte[] concat(byte[]... inputs) {
ByteArrayOutputStream o = new ByteArrayOutputStream();
Arrays.stream(inputs).forEach(o::writeBytes);
return o.toByteArray();
}
private static byte[] I2OSP(int n, int w) {
assert n < 256;
assert w == 1 || w == 2;
if (w == 1) {
return new byte[] { (byte) n };
} else {
return new byte[] { (byte) (n >> 8), (byte) n };
}
}
private static SecretKey LabeledExtract(HKDF kdf, byte[] suite_id,
byte[] salt, byte[] label, byte[] ikm) throws InvalidKeyException {
return kdf.extract(salt,
new SecretKeySpec(concat(HPKE_V1, suite_id, label, ikm), "IKM"),
"HKDF-PRK");
}
private static byte[] LabeledExpand(HKDF kdf, byte[] suite_id,
SecretKey prk, byte[] label, byte[] info, int L)
throws InvalidKeyException {
byte[] labeled_info = concat(I2OSP(L, 2), HPKE_V1,
suite_id, label, info);
return kdf.expand(prk, labeled_info, L, "NONE").getEncoded();
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -74,6 +74,10 @@ import static sun.security.util.SecurityProviderConstants.*;
*
* - HMAC-MD5, HMAC-SHA1, HMAC with SHA2 family and SHA3 family of digests
*
* - JCEKS KeyStore
*
* - DHKEM
*
*/
public final class SunJCE extends Provider {
@ -743,6 +747,15 @@ public final class SunJCE extends Provider {
ps("KeyStore", "JCEKS",
"com.sun.crypto.provider.JceKeyStore");
/*
* KEMs
*/
attrs.clear();
attrs.put("ImplementedIn", "Software");
attrs.put("SupportedKeyClasses", "java.security.interfaces.ECKey" +
"|java.security.interfaces.XECKey");
ps("KEM", "DHKEM", "com.sun.crypto.provider.DHKEM", null, attrs);
/*
* SSL/TLS mechanisms
*

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 1996, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -1599,6 +1599,7 @@ public abstract class Provider extends Properties {
addEngine("KeyAgreement", true, null);
addEngine("KeyGenerator", false, null);
addEngine("SecretKeyFactory", false, null);
addEngine("KEM", true, null);
// JSSE
addEngine("KeyManagerFactory", false, null);
addEngine("SSLContext", false, null);

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.crypto;
import java.security.GeneralSecurityException;
/**
* An exception that is thrown by the
* {@link javax.crypto.KEM.Decapsulator#decapsulate} method to denote an
* error during decapsulation.
*
* @since 21
*/
public class DecapsulateException extends GeneralSecurityException {
@java.io.Serial
private static final long serialVersionUID = 21L;
/**
* Creates a {@code DecapsulateException} with the specified
* detail message.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
*/
public DecapsulateException(String message) {
super(message);
}
/**
* Creates a {@code DecapsulateException} with the specified
* detail message and cause.
*
* @param message the detail message (which is saved for later retrieval
* by the {@link #getMessage()} method).
* @param cause the cause (which is saved for later retrieval by the
* {@link #getCause()} method). (A {@code null} value is permitted,
* and indicates that the cause is nonexistent or unknown.)
*/
public DecapsulateException(String message, Throwable cause) {
super(message, cause);
}
}

View file

@ -0,0 +1,737 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.crypto;
import sun.security.jca.GetInstance;
import java.security.*;
import java.security.InvalidAlgorithmParameterException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.List;
import java.util.Objects;
/**
* This class provides the functionality of a Key Encapsulation Mechanism (KEM).
* A KEM can be used to secure symmetric keys using asymmetric or public key
* cryptography between two parties. The sender calls the encapsulate method
* to generate a secret key and a key encapsulation message, and the receiver
* calls the decapsulate method to recover the same secret key from
* the key encapsulation message.
* <p>
* The {@code getInstance} method creates a new {@code KEM} object that
* implements the specified algorithm.
* <p>
* A {@code KEM} object is immutable. It is safe to call multiple
* {@code newEncapsulator} and {@code newDecapsulator} methods on the
* same {@code KEM} object at the same time.
* <p>
* If a provider is not specified in the {@code getInstance} method when
* instantiating a {@code KEM} object, the {@code newEncapsulator} and
* {@code newDecapsulator} methods may return encapsulators or decapsulators
* from different providers. The provider selected is based on the parameters
* passed to the {@code newEncapsulator} or {@code newDecapsulator} methods:
* the private or public key and the optional {@code AlgorithmParameterSpec}.
* The {@link Encapsulator#providerName} and {@link Decapsulator#providerName}
* methods return the name of the selected provider.
* <p>
* {@code Encapsulator} and {@code Decapsulator} objects are also immutable.
* It is safe to invoke multiple {@code encapsulate} and {@code decapsulate}
* methods on the same {@code Encapsulator} or {@code Decapsulator} object
* at the same time. Each invocation of {@code encapsulate} will generate a
* new shared secret and key encapsulation message.
* <p>
*
* Example:
* {@snippet lang = java:
* // Receiver side
* var kpg = KeyPairGenerator.getInstance("X25519");
* var kp = kpg.generateKeyPair();
*
* // Sender side
* var kem1 = KEM.getInstance("DHKEM");
* var sender = kem1.newEncapsulator(kp.getPublic());
* var encapsulated = sender.encapsulate();
* var k1 = encapsulated.key();
*
* // Receiver side
* var kem2 = KEM.getInstance("DHKEM");
* var receiver = kem2.newDecapsulator(kp.getPrivate());
* var k2 = receiver.decapsulate(encapsulated.encapsulation());
*
* assert Arrays.equals(k1.getEncoded(), k2.getEncoded());
* }
*
* @since 21
*/
public final class KEM {
/**
* This class specifies the return value of the encapsulate method of
* a Key Encapsulation Mechanism (KEM), which includes the shared secret
* (as a {@code SecretKey}), the key encapsulation message,
* and optional parameters.
* <p>
* Note: the key encapsulation message can be also referred to as ciphertext.
*
* @see #newEncapsulator(PublicKey, AlgorithmParameterSpec, SecureRandom)
* @see Encapsulator#encapsulate(int, int, String)
*
* @since 21
*/
public static final class Encapsulated {
private final SecretKey key;
private final byte[] encapsulation;
private final byte[] params;
/**
* Constructs an {@code Encapsulated} object.
*
* @param key the shared secret as a key, must not be {@code null}.
* @param encapsulation the key encapsulation message, must not
* be {@code null}. The contents of the array are copied
* to protect against subsequent modification.
* @param params optional parameters, can be {@code null}.
* The contents of the array are copied to protect
* against subsequent modification.
* @throws NullPointerException if {@code key} or {@code encapsulation}
* is {@code null}
*/
public Encapsulated(SecretKey key, byte[] encapsulation, byte[] params) {
Objects.requireNonNull(key);
Objects.requireNonNull(encapsulation);
this.key = key;
this.encapsulation = encapsulation.clone();
this.params = params == null ? null : params.clone();
}
/**
* Returns the {@code SecretKey}.
*
* @return the secret key
*/
public SecretKey key() {
return key;
}
/**
* Returns the key encapsulation message.
*
* @return the key encapsulation message. A new copy of the byte array
* is returned.
*/
public byte[] encapsulation() {
return encapsulation.clone();
}
/**
* Returns the optional parameters in a byte array.
*
* @return the optional parameters in a byte array or {@code null}
* if not specified. A new copy of the byte array is returned.
*/
public byte[] params() {
return params == null ? null : params.clone();
}
}
/**
* An encapsulator, generated by {@link #newEncapsulator} on the KEM
* sender side.
* <p>
* This class represents the key encapsulation function of a KEM.
* Each invocation of the {@code encapsulate} method generates a
* new secret key and key encapsulation message that is returned
* in an {@link Encapsulated} object.
*
* @since 21
*/
public static final class Encapsulator {
private final KEMSpi.EncapsulatorSpi e;
private final Provider p;
private Encapsulator(KEMSpi.EncapsulatorSpi e, Provider p) {
assert e != null;
assert p != null;
this.e = e;
this.p = p;
}
/**
* Returns the name of the provider.
*
* @return the name of the provider
*/
public String providerName() {
return p.getName();
}
/**
* The key encapsulation function.
* <p>
* This method is equivalent to
* {@code encapsulate(0, secretSize(), "Generic")}. This combination
* of arguments must be supported by every implementation.
* <p>
* The generated secret key is usually passed to a key derivation
* function (KDF) as the input keying material.
*
* @return a {@link Encapsulated} object containing the shared
* secret, key encapsulation message, and optional parameters.
* The shared secret is a {@code SecretKey} containing all of
* the bytes of the secret, and an algorithm name of "Generic".
*/
public Encapsulated encapsulate() {
return encapsulate(0, secretSize(), "Generic");
}
/**
* The key encapsulation function.
* <p>
* Each invocation of this method generates a new secret key and key
* encapsulation message that is returned in an {@link Encapsulated} object.
* <p>
* An implementation may choose to not support arbitrary combinations
* of {@code from}, {@code to}, and {@code algorithm}.
*
* @param from the initial index of the shared secret byte array
* to be returned, inclusive
* @param to the final index of the shared secret byte array
* to be returned, exclusive
* @param algorithm the algorithm name for the secret key that is returned
* @return a {@link Encapsulated} object containing a portion of
* the shared secret, key encapsulation message, and optional
* parameters. The portion of the shared secret is a
* {@code SecretKey} containing the bytes of the secret
* ranging from {@code from} to {@code to}, exclusive,
* and an algorithm name as specified. For example,
* {@code encapsulate(0, 16, "AES")} uses the first 16 bytes
* of the shared secret as a 128-bit AES key.
* @throws IndexOutOfBoundsException if {@code from < 0},
* {@code from > to}, or {@code to > secretSize()}
* @throws NullPointerException if {@code algorithm} is {@code null}
* @throws UnsupportedOperationException if the combination of
* {@code from}, {@code to}, and {@code algorithm}
* is not supported by the encapsulator
*/
public Encapsulated encapsulate(int from, int to, String algorithm) {
return e.engineEncapsulate(from, to, algorithm);
}
/**
* Returns the size of the shared secret.
* <p>
* This method can be called to find out the length of the shared secret
* before {@code encapsulate} is called or if the obtained
* {@code SecretKey} is not extractable.
*
* @return the size of the shared secret
*/
public int secretSize() {
int result = e.engineSecretSize();
assert result >= 0 && result != Integer.MAX_VALUE
: "invalid engineSecretSize result";
return result;
}
/**
* Returns the size of the key encapsulation message.
* <p>
* This method can be called to find out the length of the encapsulation
* message before {@code encapsulate} is called.
*
* @return the size of the key encapsulation message
*/
public int encapsulationSize() {
int result = e.engineEncapsulationSize();
assert result >= 0 && result != Integer.MAX_VALUE
: "invalid engineEncapsulationSize result";
return result;
}
}
/**
* A decapsulator, generated by {@link #newDecapsulator} on the KEM
* receiver side.
* <p>
* This class represents the key decapsulation function of a KEM.
* An invocation of the {@code decapsulate} method recovers the
* secret key from the key encapsulation message.
*
* @since 21
*/
public static final class Decapsulator {
private final KEMSpi.DecapsulatorSpi d;
private final Provider p;
private Decapsulator(KEMSpi.DecapsulatorSpi d, Provider p) {
assert d != null;
assert p != null;
this.d = d;
this.p = p;
}
/**
* Returns the name of the provider.
*
* @return the name of the provider
*/
public String providerName() {
return p.getName();
}
/**
* The key decapsulation function.
* <p>
* This method is equivalent to
* {@code decapsulate(encapsulation, 0, secretSize(), "Generic")}. This
* combination of arguments must be supported by every implementation.
* <p>
* The generated secret key is usually passed to a key derivation
* function (KDF) as the input keying material.
*
* @param encapsulation the key encapsulation message from the sender.
* The size must be equal to the value returned by
* {@link #encapsulationSize()}, or a {@code DecapsulateException}
* will be thrown.
* @return the shared secret as a {@code SecretKey} with
* an algorithm name of "Generic"
* @throws DecapsulateException if an error occurs during the
* decapsulation process
* @throws NullPointerException if {@code encapsulation} is {@code null}
*/
public SecretKey decapsulate(byte[] encapsulation) throws DecapsulateException {
return decapsulate(encapsulation, 0, secretSize(), "Generic");
}
/**
* The key decapsulation function.
* <p>
* An invocation of this method recovers the secret key from the key
* encapsulation message.
* <p>
* An implementation may choose to not support arbitrary combinations
* of {@code from}, {@code to}, and {@code algorithm}.
*
* @param encapsulation the key encapsulation message from the sender.
* The size must be equal to the value returned by
* {@link #encapsulationSize()}, or a {@code DecapsulateException}
* will be thrown.
* @param from the initial index of the shared secret byte array
* to be returned, inclusive
* @param to the final index of the shared secret byte array
* to be returned, exclusive
* @param algorithm the algorithm name for the secret key that is returned
* @return a portion of the shared secret as a {@code SecretKey}
* containing the bytes of the secret ranging from {@code from}
* to {@code to}, exclusive, and an algorithm name as specified.
* For example, {@code decapsulate(encapsulation, secretSize()
* - 16, secretSize(), "AES")} uses the last 16 bytes
* of the shared secret as a 128-bit AES key.
* @throws DecapsulateException if an error occurs during the
* decapsulation process
* @throws IndexOutOfBoundsException if {@code from < 0},
* {@code from > to}, or {@code to > secretSize()}
* @throws NullPointerException if {@code encapsulation} or
* {@code algorithm} is {@code null}
* @throws UnsupportedOperationException if the combination of
* {@code from}, {@code to}, and {@code algorithm}
* is not supported by the decapsulator
*/
public SecretKey decapsulate(byte[] encapsulation,
int from, int to, String algorithm)
throws DecapsulateException {
return d.engineDecapsulate(
encapsulation,
from, to,
algorithm);
}
/**
* Returns the size of the shared secret.
* <p>
* This method can be called to find out the length of the shared secret
* before {@code decapsulate} is called or if the obtained
* {@code SecretKey} is not extractable.
*
* @return the size of the shared secret
*/
public int secretSize() {
int result = d.engineSecretSize();
assert result >= 0 && result != Integer.MAX_VALUE
: "invalid engineSecretSize result";
return result;
}
/**
* Returns the size of the key encapsulation message.
* <p>
* This method can be used to extract the encapsulation message
* from a longer byte array if no length information is provided
* by a higher level protocol.
*
* @return the size of the key encapsulation message
*/
public int encapsulationSize() {
int result = d.engineEncapsulationSize();
assert result >= 0 && result != Integer.MAX_VALUE
: "invalid engineEncapsulationSize result";
return result;
}
}
private static final class DelayedKEM {
private final Provider.Service[] list; // non empty array
private DelayedKEM(Provider.Service[] list) {
this.list = list;
}
private Encapsulator newEncapsulator(PublicKey publicKey,
AlgorithmParameterSpec spec, SecureRandom secureRandom)
throws InvalidAlgorithmParameterException, InvalidKeyException {
if (publicKey == null) {
throw new InvalidKeyException("input key is null");
}
RuntimeException re = null;
InvalidAlgorithmParameterException iape = null;
InvalidKeyException ike = null;
NoSuchAlgorithmException nsae = null;
for (Provider.Service service : list) {
if (!service.supportsParameter(publicKey)) {
continue;
}
try {
KEMSpi spi = (KEMSpi) service.newInstance(null);
return new Encapsulator(
spi.engineNewEncapsulator(publicKey, spec, secureRandom),
service.getProvider());
} catch (NoSuchAlgorithmException e) {
nsae = merge(nsae, e);
} catch (InvalidAlgorithmParameterException e) {
iape = merge(iape, e);
} catch (InvalidKeyException e) {
ike = merge(ike, e);
} catch (RuntimeException e) {
re = merge(re, e);
}
}
if (iape != null) throw iape;
if (ike != null) throw ike;
if (nsae != null) {
throw new InvalidKeyException("No installed provider found", nsae);
}
throw new InvalidKeyException("No installed provider supports this key: "
+ publicKey.getClass().getName(), re);
}
private static <T extends Exception> T merge(T e1, T e2) {
if (e1 == null) {
return e2;
} else {
e1.addSuppressed(e2);
return e1;
}
}
private Decapsulator newDecapsulator(PrivateKey privateKey, AlgorithmParameterSpec spec)
throws InvalidAlgorithmParameterException, InvalidKeyException {
if (privateKey == null) {
throw new InvalidKeyException("input key is null");
}
RuntimeException re = null;
InvalidAlgorithmParameterException iape = null;
InvalidKeyException ike = null;
NoSuchAlgorithmException nsae = null;
for (Provider.Service service : list) {
if (!service.supportsParameter(privateKey)) {
continue;
}
try {
KEMSpi spi = (KEMSpi) service.newInstance(null);
return new Decapsulator(
spi.engineNewDecapsulator(privateKey, spec),
service.getProvider());
} catch (NoSuchAlgorithmException e) {
nsae = merge(nsae, e);
} catch (InvalidAlgorithmParameterException e) {
iape = merge(iape, e);
} catch (InvalidKeyException e) {
ike = merge(ike, e);
} catch (RuntimeException e) {
re = merge(re, e);
}
}
if (iape != null) throw iape;
if (ike != null) throw ike;
if (nsae != null) {
throw new InvalidKeyException("No installed provider found", nsae);
}
throw new InvalidKeyException("No installed provider supports this key: "
+ privateKey.getClass().getName(), re);
}
}
// If delayed provider selection is needed
private final DelayedKEM delayed;
// otherwise
private final KEMSpi spi;
private final Provider provider;
private final String algorithm;
private KEM(String algorithm, KEMSpi spi, Provider provider) {
assert spi != null;
assert provider != null;
this.delayed = null;
this.spi = spi;
this.provider = provider;
this.algorithm = algorithm;
}
private KEM(String algorithm, DelayedKEM delayed) {
assert delayed != null;
this.delayed = delayed;
this.spi = null;
this.provider = null;
this.algorithm = algorithm;
}
/**
* Returns a {@code KEM} object that implements the specified algorithm.
*
* @param algorithm the name of the KEM algorithm.
* See the {@code KEM} section in the <a href=
* "{@docRoot}/../specs/security/standard-names.html#kem-algorithms">
* Java Security Standard Algorithm Names Specification</a>
* for information about standard KEM algorithm names.
* @return the new {@code KEM} object
* @throws NoSuchAlgorithmException if no {@code Provider} supports a
* {@code KEM} implementation for the specified algorithm
* @throws NullPointerException if {@code algorithm} is {@code null}
*/
public static KEM getInstance(String algorithm)
throws NoSuchAlgorithmException {
List<Provider.Service> list = GetInstance.getServices(
"KEM",
Objects.requireNonNull(algorithm, "null algorithm name"));
if (list.isEmpty()) {
throw new NoSuchAlgorithmException(algorithm + " KEM not available");
}
return new KEM(algorithm, new DelayedKEM(list.toArray(new Provider.Service[0])));
}
/**
* Returns a {@code KEM} object that implements the specified algorithm
* from the specified security provider.
*
* @param algorithm the name of the KEM algorithm.
* See the {@code KEM} section in the <a href=
* "{@docRoot}/../specs/security/standard-names.html#kem-algorithms">
* Java Security Standard Algorithm Names Specification</a>
* for information about standard KEM algorithm names.
* @param provider the provider. If {@code null}, this method is equivalent
* to {@link #getInstance(String)}.
* @return the new {@code KEM} object
* @throws NoSuchAlgorithmException if a {@code provider} is specified and
* it does not support the specified KEM algorithm,
* or if {@code provider} is {@code null} and there is no provider
* that supports a KEM implementation of the specified algorithm
* @throws NullPointerException if {@code algorithm} is {@code null}
*/
public static KEM getInstance(String algorithm, Provider provider)
throws NoSuchAlgorithmException {
if (provider == null) {
return getInstance(algorithm);
}
GetInstance.Instance instance = GetInstance.getInstance(
"KEM",
KEMSpi.class,
Objects.requireNonNull(algorithm, "null algorithm name"),
provider);
return new KEM(algorithm, (KEMSpi) instance.impl, instance.provider);
}
/**
* Returns a {@code KEM} object that implements the specified algorithm
* from the specified security provider.
*
* @param algorithm the name of the KEM algorithm.
* See the {@code KEM} section in the <a href=
* "{@docRoot}/../specs/security/standard-names.html#kem-algorithms">
* Java Security Standard Algorithm Names Specification</a>
* for information about standard KEM algorithm names.
* @param provider the provider. If {@code null}, this method is equivalent
* to {@link #getInstance(String)}.
* @return the new {@code KEM} object
* @throws NoSuchAlgorithmException if a {@code provider} is specified and
* it does not support the specified KEM algorithm,
* or if {@code provider} is {@code null} and there is no provider
* that supports a KEM implementation of the specified algorithm
* @throws NoSuchProviderException if the specified provider is not
* registered in the security provider list
* @throws NullPointerException if {@code algorithm} is {@code null}
*/
public static KEM getInstance(String algorithm, String provider)
throws NoSuchAlgorithmException, NoSuchProviderException {
if (provider == null) {
return getInstance(algorithm);
}
GetInstance.Instance instance = GetInstance.getInstance(
"KEM",
KEMSpi.class,
Objects.requireNonNull(algorithm, "null algorithm name"),
provider);
return new KEM(algorithm, (KEMSpi) instance.impl, instance.provider);
}
/**
* Creates a KEM encapsulator on the KEM sender side.
* <p>
* This method is equivalent to {@code newEncapsulator(publicKey, null, null)}.
*
* @param publicKey the receiver's public key, must not be {@code null}
* @return the encapsulator for this key
* @throws InvalidKeyException if {@code publicKey} is {@code null} or invalid
* @throws UnsupportedOperationException if this method is not supported
* because an {@code AlgorithmParameterSpec} must be provided
*/
public Encapsulator newEncapsulator(PublicKey publicKey)
throws InvalidKeyException {
try {
return newEncapsulator(publicKey, null, null);
} catch (InvalidAlgorithmParameterException e) {
throw new UnsupportedOperationException(
"AlgorithmParameterSpec must be provided", e);
}
}
/**
* Creates a KEM encapsulator on the KEM sender side.
* <p>
* This method is equivalent to {@code newEncapsulator(publicKey, null, secureRandom)}.
*
* @param publicKey the receiver's public key, must not be {@code null}
* @param secureRandom the source of randomness for encapsulation.
* If {@code} null, a default one from the
* implementation will be used.
* @return the encapsulator for this key
* @throws InvalidKeyException if {@code publicKey} is {@code null} or invalid
* @throws UnsupportedOperationException if this method is not supported
* because an {@code AlgorithmParameterSpec} must be provided
*/
public Encapsulator newEncapsulator(PublicKey publicKey, SecureRandom secureRandom)
throws InvalidKeyException {
try {
return newEncapsulator(publicKey, null, secureRandom);
} catch (InvalidAlgorithmParameterException e) {
throw new UnsupportedOperationException(
"AlgorithmParameterSpec must be provided", e);
}
}
/**
* Creates a KEM encapsulator on the KEM sender side.
* <p>
* An algorithm can define an {@code AlgorithmParameterSpec} child class to
* provide extra information in this method. This is especially useful if
* the same key can be used to derive shared secrets in different ways.
* If any extra information inside this object needs to be transmitted along
* with the key encapsulation message so that the receiver is able to create
* a matching decapsulator, it will be included as a byte array in the
* {@link Encapsulated#params} field inside the encapsulation output.
* In this case, the security provider should provide an
* {@code AlgorithmParameters} implementation using the same algorithm name
* as the KEM. The receiver can initiate such an {@code AlgorithmParameters}
* instance with the {@code params} byte array received and recover
* an {@code AlgorithmParameterSpec} object to be used in its
* {@link #newDecapsulator(PrivateKey, AlgorithmParameterSpec)} call.
*
* @param publicKey the receiver's public key, must not be {@code null}
* @param spec the optional parameter, can be {@code null}
* @param secureRandom the source of randomness for encapsulation.
* If {@code} null, a default one from the
* implementation will be used.
* @return the encapsulator for this key
* @throws InvalidAlgorithmParameterException if {@code spec} is invalid
* or one is required but {@code spec} is {@code null}
* @throws InvalidKeyException if {@code publicKey} is {@code null} or invalid
*/
public Encapsulator newEncapsulator(PublicKey publicKey,
AlgorithmParameterSpec spec, SecureRandom secureRandom)
throws InvalidAlgorithmParameterException, InvalidKeyException {
return delayed != null
? delayed.newEncapsulator(publicKey, spec, secureRandom)
: new Encapsulator(spi.engineNewEncapsulator(publicKey, spec, secureRandom), provider);
}
/**
* Creates a KEM decapsulator on the KEM receiver side.
* <p>
* This method is equivalent to {@code newDecapsulator(privateKey, null)}.
*
* @param privateKey the receiver's private key, must not be {@code null}
* @return the decapsulator for this key
* @throws InvalidKeyException if {@code privateKey} is {@code null} or invalid
* @throws UnsupportedOperationException if this method is not supported
* because an {@code AlgorithmParameterSpec} must be provided
*/
public Decapsulator newDecapsulator(PrivateKey privateKey)
throws InvalidKeyException {
try {
return newDecapsulator(privateKey, null);
} catch (InvalidAlgorithmParameterException e) {
throw new UnsupportedOperationException(e);
}
}
/**
* Creates a KEM decapsulator on the KEM receiver side.
*
* @param privateKey the receiver's private key, must not be {@code null}
* @param spec the parameter, can be {@code null}
* @return the decapsulator for this key
* @throws InvalidAlgorithmParameterException if {@code spec} is invalid
* or one is required but {@code spec} is {@code null}
* @throws InvalidKeyException if {@code privateKey} is {@code null} or invalid
*/
public Decapsulator newDecapsulator(PrivateKey privateKey, AlgorithmParameterSpec spec)
throws InvalidAlgorithmParameterException, InvalidKeyException {
return delayed != null
? delayed.newDecapsulator(privateKey, spec)
: new Decapsulator(spi.engineNewDecapsulator(privateKey, spec), provider);
}
/**
* Returns the name of the algorithm for this {@code KEM} object.
*
* @return the name of the algorithm for this {@code KEM} object.
*/
public String getAlgorithm() {
return this.algorithm;
}
}

View file

@ -0,0 +1,256 @@
/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.crypto;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
/**
* This class defines the Service Provider Interface (SPI) for the {@link KEM}
* class. A security provider implements this interface to provide an
* implementation of a Key Encapsulation Mechanism (KEM) algorithm.
* <p>
* A KEM algorithm may support a family of configurations. Each configuration
* may accept different types of keys, cryptographic primitives, and sizes of
* shared secrets and key encapsulation messages. A configuration is defined
* by the KEM algorithm name, the key it uses, and an optional
* {@code AlgorithmParameterSpec} argument that is specified when creating
* an encapsulator or decapsulator. The result of calling
* {@link #engineNewEncapsulator} or {@link #engineNewDecapsulator} must return
* an encapsulator or decapsulator that maps to a single configuration,
* where its {@code engineSecretSize()} and {@code engineEncapsulationSize()}
* methods return constant values.
* <p>
* A {@code KEMSpi} implementation must be immutable. It must be safe to
* call multiple {@code engineNewEncapsulator} and {@code engineNewDecapsulator}
* methods at the same time.
* <p>
* {@code EncapsulatorSpi} and {@code DecapsulatorSpi} implementations must also
* be immutable. It must be safe to invoke multiple {@code encapsulate} and
* {@code decapsulate} methods at the same time. Each invocation of
* {@code encapsulate} should generate a new shared secret and key
* encapsulation message.
* <p>
* For example,
* {@snippet lang = java:
* public static class MyKEMImpl implements KEMSpi {
*
* @Override
* public KEMSpi.EncapsulatorSpi engineNewEncapsulator(PublicKey publicKey,
* AlgorithmParameterSpec spec, SecureRandom secureRandom)
* throws InvalidAlgorithmParameterException, InvalidKeyException {
* if (!checkPublicKey(publicKey)) {
* throw new InvalidKeyException("unsupported key");
* }
* if (!checkParameters(spec)) {
* throw new InvalidAlgorithmParameterException("unsupported params");
* }
* return new MyEncapsulator(publicKey, spec, secureRandom);
* }
*
* class MyEncapsulator implements KEMSpi.EncapsulatorSpi {
* MyEncapsulator(PublicKey publicKey, AlgorithmParameterSpec spec,
* SecureRandom secureRandom){
* this.spec = spec != null ? spec : getDefaultParameters();
* this.secureRandom = secureRandom != null
* ? secureRandom
* : getDefaultSecureRandom();
* this.publicKey = publicKey;
* }
*
* @Override
* public KEM.Encapsulated encapsulate(int from, int to, String algorithm) {
* byte[] encapsulation;
* byte[] secret;
* // calculating...
* return new KEM.Encapsulated(
* new SecretKeySpec(secret, from, to - from, algorithm),
* encapsulation, null);
* }
*
* // ...
* }
*
* // ...
* }
* }
*
* @see KEM
* @since 21
*/
public interface KEMSpi {
/**
* The KEM encapsulator implementation, generated by
* {@link #engineNewEncapsulator} on the KEM sender side.
*
* @see KEM.Encapsulator
*
* @since 21
*/
interface EncapsulatorSpi {
/**
* The key encapsulation function.
* <p>
* Each invocation of this method must generate a new secret key and key
* encapsulation message that is returned in an {@link KEM.Encapsulated} object.
* <p>
* An implementation must support the case where {@code from} is 0,
* {@code to} is the same as the return value of {@code secretSize()},
* and {@code algorithm} is "Generic".
*
* @param from the initial index of the shared secret byte array
* to be returned, inclusive
* @param to the final index of the shared secret byte array
* to be returned, exclusive
* @param algorithm the algorithm name for the secret key that is returned
* @return an {@link KEM.Encapsulated} object containing a portion of
* the shared secret as a key with the specified algorithm,
* key encapsulation message, and optional parameters.
* @throws IndexOutOfBoundsException if {@code from < 0},
* {@code from > to}, or {@code to > secretSize()}
* @throws NullPointerException if {@code algorithm} is {@code null}
* @throws UnsupportedOperationException if the combination of
* {@code from}, {@code to}, and {@code algorithm}
* is not supported by the encapsulator
* @see KEM.Encapsulated
* @see KEM.Encapsulator#encapsulate(int, int, String)
*/
KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm);
/**
* Returns the size of the shared secret.
*
* @return the size of the shared secret as a finite non-negative integer
* @see KEM.Encapsulator#secretSize()
*/
int engineSecretSize();
/**
* Returns the size of the key encapsulation message.
*
* @return the size of the key encapsulation message as a finite non-negative integer
* @see KEM.Encapsulator#encapsulationSize()
*/
int engineEncapsulationSize();
}
/**
* The KEM decapsulator implementation, generated by
* {@link #engineNewDecapsulator} on the KEM receiver side.
*
* @see KEM.Decapsulator
*
* @since 21
*/
interface DecapsulatorSpi {
/**
* The key decapsulation function.
* <p>
* An invocation of this method recovers the secret key from the key
* encapsulation message.
* <p>
* An implementation must support the case where {@code from} is 0,
* {@code to} is the same as the return value of {@code secretSize()},
* and {@code algorithm} is "Generic".
*
* @param encapsulation the key encapsulation message from the sender.
* The size must be equal to the value returned by
* {@link #engineEncapsulationSize()} ()}, or a
* {@code DecapsulateException} must be thrown.
* @param from the initial index of the shared secret byte array
* to be returned, inclusive
* @param to the final index of the shared secret byte array
* to be returned, exclusive
* @param algorithm the algorithm name for the secret key that is returned
* @return a portion of the shared secret as a {@code SecretKey} with
* the specified algorithm
* @throws DecapsulateException if an error occurs during the
* decapsulation process
* @throws IndexOutOfBoundsException if {@code from < 0},
* {@code from > to}, or {@code to > secretSize()}
* @throws NullPointerException if {@code encapsulation} or
* {@code algorithm} is {@code null}
* @throws UnsupportedOperationException if the combination of
* {@code from}, {@code to}, and {@code algorithm}
* is not supported by the decapsulator
* @see KEM.Decapsulator#decapsulate(byte[], int, int, String)
*/
SecretKey engineDecapsulate(byte[] encapsulation, int from, int to, String algorithm)
throws DecapsulateException;
/**
* Returns the size of the shared secret.
*
* @return the size of the shared secret as a finite non-negative integer
* @see KEM.Decapsulator#secretSize()
*/
int engineSecretSize();
/**
* Returns the size of the key encapsulation message.
*
* @return the size of the key encapsulation message as a finite non-negative integer
* @see KEM.Decapsulator#encapsulationSize()
*/
int engineEncapsulationSize();
}
/**
* Creates a KEM encapsulator on the KEM sender side.
*
* @param publicKey the receiver's public key, must not be {@code null}
* @param spec the optional parameter, can be {@code null}
* @param secureRandom the source of randomness for encapsulation.
* If {@code null}, the implementation must provide
* a default one.
* @return the encapsulator for this key
* @throws InvalidAlgorithmParameterException if {@code spec} is invalid
* or one is required but {@code spec} is {@code null}
* @throws InvalidKeyException if {@code publicKey} is {@code null} or invalid
* @see KEM#newEncapsulator(PublicKey, AlgorithmParameterSpec, SecureRandom)
*/
EncapsulatorSpi engineNewEncapsulator(PublicKey publicKey,
AlgorithmParameterSpec spec, SecureRandom secureRandom)
throws InvalidAlgorithmParameterException, InvalidKeyException;
/**
* Creates a KEM decapsulator on the KEM receiver side.
*
* @param privateKey the receiver's private key, must not be {@code null}
* @param spec the optional parameter, can be {@code null}
* @return the decapsulator for this key
* @throws InvalidAlgorithmParameterException if {@code spec} is invalid
* or one is required but {@code spec} is {@code null}
* @throws InvalidKeyException if {@code privateKey} is {@code null} or invalid
* @see KEM#newDecapsulator(PrivateKey, AlgorithmParameterSpec)
*/
DecapsulatorSpi engineNewDecapsulator(PrivateKey privateKey, AlgorithmParameterSpec spec)
throws InvalidAlgorithmParameterException, InvalidKeyException;
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2018, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -43,7 +43,7 @@ import java.util.Objects;
* digest algorithm will be used by the HMAC function as part of the HKDF
* derivation process.
*/
final class HKDF {
public final class HKDF {
private final Mac hmacObj;
private final int hmacLen;
@ -57,7 +57,7 @@ final class HKDF {
* @throws NoSuchAlgorithmException if that message digest algorithm does
* not have an HMAC variant supported on any available provider.
*/
HKDF(String hashAlg) throws NoSuchAlgorithmException {
public HKDF(String hashAlg) throws NoSuchAlgorithmException {
Objects.requireNonNull(hashAlg,
"Must provide underlying HKDF Digest algorithm.");
String hmacAlg = "Hmac" + hashAlg.replace("-", "");
@ -82,7 +82,7 @@ final class HKDF {
* @throws InvalidKeyException if the {@code salt} parameter cannot be
* used to initialize the underlying HMAC.
*/
SecretKey extract(SecretKey salt, SecretKey inputKey, String keyAlg)
public SecretKey extract(SecretKey salt, SecretKey inputKey, String keyAlg)
throws InvalidKeyException {
if (salt == null) {
salt = new SecretKeySpec(new byte[hmacLen], "HKDF-Salt");
@ -110,7 +110,7 @@ final class HKDF {
* @throws InvalidKeyException if the {@code salt} parameter cannot be
* used to initialize the underlying HMAC.
*/
SecretKey extract(byte[] salt, SecretKey inputKey, String keyAlg)
public SecretKey extract(byte[] salt, SecretKey inputKey, String keyAlg)
throws InvalidKeyException {
if (salt == null) {
salt = new byte[hmacLen];
@ -133,7 +133,7 @@ final class HKDF {
* @throws InvalidKeyException if the underlying HMAC operation cannot
* be initialized using the provided {@code pseudoRandKey} object.
*/
SecretKey expand(SecretKey pseudoRandKey, byte[] info, int outLen,
public SecretKey expand(SecretKey pseudoRandKey, byte[] info, int outLen,
String keyAlg) throws InvalidKeyException {
byte[] kdfOutput;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2006, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@ -39,6 +39,11 @@ import java.util.*;
* @author Andreas Sterbenz
*/
public class CurveDB {
public static final NamedCurve P_256;
public static final NamedCurve P_384;
public static final NamedCurve P_521;
private static final int P = 1; // prime curve
private static final int B = 2; // binary curve
private static final int PD = 5; // prime curve, mark as default
@ -109,7 +114,7 @@ public class CurveDB {
return new BigInteger(s, 16);
}
private static void add(KnownOIDs o, int type, String sfield,
private static NamedCurve add(KnownOIDs o, int type, String sfield,
String a, String b, String x, String y, String n, int h) {
BigInteger p = bi(sfield);
ECField field;
@ -143,6 +148,8 @@ public class CurveDB {
// the curve is marked as a default curve.
lengthMap.put(len, params);
}
return params;
}
static {
@ -255,7 +262,7 @@ public class CurveDB {
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",
1);
add(KnownOIDs.secp256r1, PD,
P_256 = add(KnownOIDs.secp256r1, PD,
"FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
"FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
"5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
@ -264,7 +271,7 @@ public class CurveDB {
"FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
1);
add(KnownOIDs.secp384r1, PD,
P_384 = add(KnownOIDs.secp384r1, PD,
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
"B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
@ -273,7 +280,7 @@ public class CurveDB {
"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
1);
add(KnownOIDs.secp521r1, PD,
P_521 = add(KnownOIDs.secp521r1, PD,
"01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
"01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC",
"0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00",