mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 14:54:52 +02:00
8297878: KEM: Implementation
Reviewed-by: ascarpino, mullan
This commit is contained in:
parent
21af8bae38
commit
6b90b0519e
12 changed files with 2324 additions and 21 deletions
|
@ -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);
|
||||
}
|
||||
}
|
737
src/java.base/share/classes/javax/crypto/KEM.java
Normal file
737
src/java.base/share/classes/javax/crypto/KEM.java
Normal 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;
|
||||
}
|
||||
}
|
256
src/java.base/share/classes/javax/crypto/KEMSpi.java
Normal file
256
src/java.base/share/classes/javax/crypto/KEMSpi.java
Normal 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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue