blob: 2731b3460af3b3082b71bd23d514b5fe16024c52 [file]
/*
* Copyright (c) 2024, 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 sun.security.provider;
import sun.security.pkcs.NamedPKCS8Key;
import sun.security.x509.NamedX509Key;
import javax.crypto.DecapsulateException;
import javax.crypto.KEM;
import javax.crypto.KEMSpi;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.PrivateKey;
import java.security.ProviderException;
import java.security.PublicKey;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.NamedParameterSpec;
import java.util.Arrays;
import java.util.Objects;
/// A base class for all `KEM` implementations that can be
/// configured with a named parameter set. See [NamedKeyPairGenerator]
/// for more details.
public abstract class NamedKEM implements KEMSpi {
private final String fname; // family name
private final String[] pnames; // allowed parameter set name (at least one)
/// Creates a new `NamedKEM` object.
///
/// @param fname the family name
/// @param pnames the standard parameter set names, at least one is needed.
protected NamedKEM(String fname, String... pnames) {
if (fname == null) {
throw new AssertionError("fname cannot be null");
}
if (pnames == null || pnames.length == 0) {
throw new AssertionError("pnames cannot be null or empty");
}
this.fname = fname;
this.pnames = pnames;
}
@Override
public EncapsulatorSpi engineNewEncapsulator(PublicKey publicKey,
AlgorithmParameterSpec spec, SecureRandom secureRandom)
throws InvalidAlgorithmParameterException, InvalidKeyException {
if (spec != null) {
throw new InvalidAlgorithmParameterException(
"The " + fname + " algorithm does not take any parameters");
}
// translate also check the key
var nk = (NamedX509Key) new NamedKeyFactory(fname, pnames)
.engineTranslateKey(publicKey);
var pk = nk.getRawBytes();
return getKeyConsumerImpl(this, nk.getParams(), pk,
implCheckPublicKey(nk.getParams().getName(), pk), secureRandom);
}
@Override
public DecapsulatorSpi engineNewDecapsulator(
PrivateKey privateKey, AlgorithmParameterSpec spec)
throws InvalidAlgorithmParameterException, InvalidKeyException {
if (spec != null) {
throw new InvalidAlgorithmParameterException(
"The " + fname + " algorithm does not take any parameters");
}
// translate also check the key
var nk = (NamedPKCS8Key) new NamedKeyFactory(fname, pnames)
.engineTranslateKey(privateKey);
var sk = nk.getRawBytes();
return getKeyConsumerImpl(this, nk.getParams(), sk,
implCheckPrivateKey(nk.getParams().getName(), sk), null);
}
// We don't have a flag on whether key is public key or private key.
// The correct method should always be called.
private record KeyConsumerImpl(NamedKEM kem, String name, int sslen,
int clen, byte[] key, Object k2, SecureRandom sr)
implements KEMSpi.EncapsulatorSpi, KEMSpi.DecapsulatorSpi {
@Override
public SecretKey engineDecapsulate(byte[] encapsulation, int from, int to,
String algorithm) throws DecapsulateException {
if (encapsulation.length != clen) {
throw new DecapsulateException("Invalid key encapsulation message length");
}
var ss = kem.implDecapsulate(name, key, k2, encapsulation);
try {
return new SecretKeySpec(ss,
from, to - from, algorithm);
} finally {
Arrays.fill(ss, (byte)0);
}
}
@Override
public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) {
var enc = kem.implEncapsulate(name, key, k2, sr);
try {
return new KEM.Encapsulated(
new SecretKeySpec(enc[1],
from, to - from, algorithm),
enc[0],
null);
} finally {
Arrays.fill(enc[1], (byte)0);
}
}
@Override
public int engineSecretSize() {
return sslen;
}
@Override
public int engineEncapsulationSize() {
return clen;
}
}
private static KeyConsumerImpl getKeyConsumerImpl(NamedKEM kem,
NamedParameterSpec nps, byte[] key, Object k2, SecureRandom sr) {
String name = nps.getName();
return new KeyConsumerImpl(kem, name, kem.implSecretSize(name), kem.implEncapsulationSize(name),
key, k2, sr);
}
/// User-defined encap function.
///
/// @param name parameter name
/// @param pk public key in raw bytes
/// @param pk2 parsed public key, `null` if none. See [#implCheckPublicKey].
/// @param sr SecureRandom object, `null` if not initialized
/// @return the key encapsulation message and the shared key (in this order)
/// @throws ProviderException if there is an internal error
protected abstract byte[][] implEncapsulate(String name, byte[] pk, Object pk2, SecureRandom sr);
/// User-defined decap function.
///
/// @param name parameter name
/// @param sk private key in raw bytes
/// @param sk2 parsed private key, `null` if none. See [#implCheckPrivateKey].
/// @param encap the key encapsulation message
/// @return the shared key
/// @throws ProviderException if there is an internal error
/// @throws DecapsulateException if there is another error
protected abstract byte[] implDecapsulate(String name, byte[] sk, Object sk2, byte[] encap)
throws DecapsulateException;
/// User-defined function returning shared secret key length.
///
/// @param name parameter name
/// @return shared secret key length
/// @throws ProviderException if there is an internal error
protected abstract int implSecretSize(String name);
/// User-defined function returning key encapsulation message length.
///
/// @param name parameter name
/// @return key encapsulation message length
/// @throws ProviderException if there is an internal error
protected abstract int implEncapsulationSize(String name);
/// User-defined function to validate a public key.
///
/// This method will be called in `newEncapsulator`. This gives the provider a chance to
/// reject the key so an `InvalidKeyException` can be thrown earlier.
/// An implementation can optionally return a "parsed key" as an `Object` value.
/// This object will be passed into the [#implEncapsulate] method along with the raw key.
///
/// The default implementation returns `null`.
///
/// @param name parameter name
/// @param pk public key in raw bytes
/// @return a parsed key, `null` if none.
/// @throws InvalidKeyException if the key is invalid
protected Object implCheckPublicKey(String name, byte[] pk) throws InvalidKeyException {
return null;
}
/// User-defined function to validate a private key.
///
/// This method will be called in `newDecapsulator`. This gives the provider a chance to
/// reject the key so an `InvalidKeyException` can be thrown earlier.
/// An implementation can optionally return a "parsed key" as an `Object` value.
/// This object will be passed into the [#implDecapsulate] method along with the raw key.
///
/// The default implementation returns `null`.
///
/// @param name parameter name
/// @param sk private key in raw bytes
/// @return a parsed key, `null` if none.
/// @throws InvalidKeyException if the key is invalid
protected Object implCheckPrivateKey(String name, byte[] sk) throws InvalidKeyException {
return null;
}
}