8298127: HSS/LMS Signature Verification

Reviewed-by: weijun, mullan
This commit is contained in:
Ferenc Rakoczi 2023-06-01 12:31:07 +00:00 committed by Weijun Wang
parent a6109bf1ea
commit 050425b141
5 changed files with 924 additions and 5 deletions

View file

@ -0,0 +1,848 @@
/*
* 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 sun.security.provider;
import sun.security.util.*;
import sun.security.x509.AlgorithmId;
import sun.security.x509.X509Key;
import java.io.*;
import java.security.*;
import java.security.SecureRandom;
import java.security.spec.*;
import java.util.Arrays;
/**
* Implementation of the Hierarchical Signature System using the
* Leighton-Micali Signatures (HSS/LMS) as described in RFC 8554 and
* NIST Special publication 800-208.
*/
public final class HSS extends SignatureSpi {
private HSSPublicKey pubKey;
private ByteArrayOutputStream messageStream;
@Override
@Deprecated
protected void engineSetParameter(String param, Object value) {
throw new InvalidParameterException("No settable parameters exist for HSS/LMS");
}
@Override
@Deprecated
protected AlgorithmParameters engineGetParameter(String param) {
throw new InvalidParameterException("No parameters exist for HSS/LMS");
}
@Override
protected void engineInitSign(PrivateKey privateKey)
throws InvalidKeyException {
throw new InvalidKeyException("HSS/LMS signing is not supported");
}
@Override
protected void engineInitSign(PrivateKey prk, SecureRandom sr)
throws InvalidKeyException {
throw new InvalidKeyException("HSS/LMS signing is not supported");
}
// This will never be called because engineInitSign unconditionally
// throws an exception
@Override
protected byte[] engineSign() throws SignatureException {
throw new SignatureException("HSS/LMS signing is not supported");
}
@Override
protected void engineInitVerify(PublicKey publicKey)
throws InvalidKeyException {
HSSPublicKey pub;
if (publicKey instanceof HSSPublicKey p) {
pub = p;
} else {
KeyFactoryImpl factory = new HSS.KeyFactoryImpl();
Key pk = factory.engineTranslateKey(publicKey);
pub = (HSSPublicKey) pk;
}
pubKey = pub;
messageStream = new ByteArrayOutputStream();
}
@Override
protected void engineUpdate(byte data) {
messageStream.write(data);
}
@Override
protected void engineUpdate(byte[] data, int off, int len) {
messageStream.write(data, off, len);
}
@Override
protected boolean engineVerify(byte[] signature)
throws SignatureException {
boolean result = true;
try {
HSSSignature sig = new HSSSignature(signature, pubKey);
LMSPublicKey lmsPubKey = pubKey.lmsPublicKey;
for (int i = 0; i < sig.Nspk; i++) {
byte[] keyArr = sig.pubList[i].keyArray();
result &= LMSUtils.lmsVerify(lmsPubKey, sig.siglist[i], keyArr);
lmsPubKey = sig.pubList[i];
}
result &= LMSUtils.lmsVerify(
lmsPubKey, sig.siglist[sig.Nspk], messageStream.toByteArray());
} finally {
messageStream.reset();
}
return result;
}
@Override
protected void engineSetParameter(AlgorithmParameterSpec params)
throws InvalidAlgorithmParameterException {
if (params != null) {
throw new InvalidAlgorithmParameterException("No parameters accepted");
}
}
@Override
protected AlgorithmParameters engineGetParameters() {
return null;
}
static class LMSPublicKey {
final int type;
final int otsType;
final LMSParams lmsParams;
private final byte[] I;
private final byte[] T1;
LMSPublicKey(byte[] keyArray, int offset, boolean checkExactLength)
throws InvalidKeyException {
int inLen = keyArray.length - offset;
if (inLen < 8) {
throw new InvalidKeyException("LMS public key is too short");
}
type = LMSUtils.fourBytesToInt(keyArray, offset);
otsType = LMSUtils.fourBytesToInt(keyArray, offset + 4);
LMOTSParams lmotsParams;
try {
lmsParams = LMSParams.of(type);
lmotsParams = LMOTSParams.of(otsType);
} catch (IllegalArgumentException e) {
throw new InvalidKeyException(e.getMessage());
}
int m = lmsParams.m;
if ((inLen < (24 + m)) || (checkExactLength && (inLen != (24 + m))) ||
!lmsParams.hasSameHash(lmotsParams)) {
throw new InvalidKeyException("Wrong LMS public key length");
}
I = Arrays.copyOfRange(keyArray, offset + 8, offset + 8 + 16);
T1 = Arrays.copyOfRange(keyArray, offset + 24, offset + 24 + m);
}
void getI(byte[] arr, int pos) {
System.arraycopy(I, 0, arr, pos, 16);
}
boolean isT1(byte[] arr, int pos) {
int m = lmsParams.m;
int diff = 0;
for (int i = 0; i < m; i++) {
diff |= (T1[i] ^ arr[pos + i]);
}
return (diff == 0);
}
byte[] keyArray() {
byte[] result = new byte[keyArrayLength()];
LMSUtils.intToFourBytes(type, result, 0);
LMSUtils.intToFourBytes(otsType, result, 4);
System.arraycopy(I, 0, result, 8, 16);
System.arraycopy(T1, 0, result, 24, lmsParams.m);
return result;
}
int keyArrayLength() {
return 24 + lmsParams.m;
}
}
static class LMSUtils {
static final int LMS_RESERVED = 0;
static final int LMS_SHA256_M32_H5 = 5;
static final int LMS_SHA256_M32_H10 = 6;
static final int LMS_SHA256_M32_H15 = 7;
static final int LMS_SHA256_M32_H20 = 8;
static final int LMS_SHA256_M32_H25 = 9;
static String lmsType(int type) {
String typeStr;
switch (type) {
case LMS_RESERVED: typeStr = "LMS_RESERVED"; break;
case LMS_SHA256_M32_H5: typeStr = "LMS_SHA256_M32_H5"; break;
case LMS_SHA256_M32_H10: typeStr = "LMS_SHA256_M32_H10"; break;
case LMS_SHA256_M32_H15: typeStr = "LMS_SHA256_M32_H15"; break;
case LMS_SHA256_M32_H20: typeStr = "LMS_SHA256_M32_H20"; break;
case LMS_SHA256_M32_H25: typeStr = "LMS_SHA256_M32_H25"; break;
default: typeStr = "unrecognized";
}
return typeStr;
}
static final int LMOTS_RESERVED = 0;
static final int LMOTS_SHA256_N32_W1 = 1;
static final int LMOTS_SHA256_N32_W2 = 2;
static final int LMOTS_SHA256_N32_W4 = 3;
static final int LMOTS_SHA256_N32_W8 = 4;
static String lmotsType(int type) {
String typeStr;
switch (type) {
case LMOTS_RESERVED: typeStr = "LMOTS_RESERVED"; break;
case LMOTS_SHA256_N32_W1: typeStr = "LMOTS_SHA256_N32_W1"; break;
case LMOTS_SHA256_N32_W2: typeStr = "LMOTS_SHA256_N32_W2"; break;
case LMOTS_SHA256_N32_W4: typeStr = "LMOTS_SHA256_N32_W4"; break;
case LMOTS_SHA256_N32_W8: typeStr = "LMOTS_SHA256_N32_W8"; break;
default: typeStr = "unrecognized";
}
return typeStr;
}
static int fourBytesToInt(byte[] arr, int i) {
return ((arr[i] & 0xff) << 24) |
((arr[i + 1] & 0xff) << 16) |
((arr[i + 2] & 0xff) << 8) |
(arr[i + 3] & 0xff);
}
static void intToFourBytes(int i, byte[] arr, int pos) {
arr[pos] = (byte) (i >> 24);
arr[pos + 1] = (byte) (i >> 16);
arr[pos + 2] = (byte) (i >> 8);
arr[pos + 3] = (byte) i;
}
static boolean lmsVerify(
LMSPublicKey lmsPublicKey, LMSignature sig, byte[] message)
throws SignatureException {
if ((sig.sigOtsType != lmsPublicKey.otsType) ||
(sig.sigLmType != lmsPublicKey.type)) {
return false;
}
LMOTSignature lmotSig = sig.lmotSig;
LMOTSParams lmotsParams = lmotSig.lmotsParams;
int q = sig.q;
int m = lmsPublicKey.lmsParams.m;
int hashAlg_m = lmsPublicKey.lmsParams.hashAlg_m;
int n = lmotsParams.n;
try {
byte[] otsPkCandidate =
lmotsParams.lmotsPubKeyCandidate(sig, message, lmsPublicKey);
int nodeNum = lmsPublicKey.lmsParams.twoPowh + q;
int tmp0MsgLen = 22 + n;
int tmpLoopMsgLen = 22 + m + hashAlg_m;
byte[] tmpMsg = new byte[Integer.max(tmp0MsgLen, tmpLoopMsgLen)];
lmsPublicKey.getI(tmpMsg, 0);
MessageDigest md =
MessageDigest.getInstance(lmsPublicKey.lmsParams.hashAlgStr);
LMSUtils.intToFourBytes(nodeNum, tmpMsg, 16);
tmpMsg[20] = (byte) 0x82; // D_LEAF = 0x8282
tmpMsg[21] = (byte) 0x82;
System.arraycopy(otsPkCandidate, 0, tmpMsg, 22, n);
md.update(tmpMsg, 0, tmp0MsgLen);
if ((nodeNum & 1) == 1) {
md.digest(tmpMsg, 22 + m, hashAlg_m);
} else {
md.digest(tmpMsg, 22, hashAlg_m);
}
tmpMsg[20] = (byte) 0x83; // D_INTR = 0x8383
tmpMsg[21] = (byte) 0x83;
int i = 0;
while (nodeNum > 1) {
LMSUtils.intToFourBytes(nodeNum / 2, tmpMsg, 16);
if ((nodeNum & 1) == 1) {
sig.getPath(i, tmpMsg, 22);
} else {
sig.getPath(i, tmpMsg, 22 + m);
}
md.update(tmpMsg, 0, 22 + 2 * m);
nodeNum /= 2;
if ((nodeNum & 1) == 1) {
md.digest(tmpMsg, 22 + m, hashAlg_m);
} else {
md.digest(tmpMsg, 22, hashAlg_m);
}
i++;
}
return lmsPublicKey.isT1(tmpMsg, 22 + m);
} catch (NoSuchAlgorithmException | DigestException e) {
throw new ProviderException(e);
}
}
}
static class LMOTSignature {
final int otSigType;
final LMOTSParams lmotsParams;
private final int n;
private final byte[] C;
private final byte[][] y;
LMOTSignature(byte[] sigArray, LMOTSParams lmotsParams)
throws InvalidParameterException {
int inLen = sigArray.length;
if (inLen < 4) {
throw new InvalidParameterException("OTS signature is too short");
}
otSigType = lmotsParams.lmotSigType;
this.lmotsParams = lmotsParams;
n = lmotsParams.n;
int p = lmotsParams.p;
if (inLen != (4 + n * (p + 1))) {
throw new InvalidParameterException("OTS signature has incorrect length");
}
C = Arrays.copyOfRange(sigArray, 4, 4 + n);
int pStart = 4 + n;
y = new byte[p][n];
for (int i = 0; i < p; i++) {
y[i] = Arrays.copyOfRange(sigArray, pStart, pStart + n);
pStart += n;
}
}
void getC(byte[] arr, int pos) {
System.arraycopy(C, 0, arr, pos, n);
}
void getY(int i, byte[] arr, int pos) {
System.arraycopy(y[i], 0, arr, pos, n);
}
}
static class LMSParams {
final int m; // the number of bytes used from the hash output
final int hashAlg_m = 32; // output length of the LMS tree hash function
final int h; // height of the LMS tree
final int twoPowh;
final String hashAlgStr;
LMSParams(int m, int h, String hashAlgStr) {
this.m = m;
this.h = h;
this.hashAlgStr = hashAlgStr;
twoPowh = 1 << h;
}
static LMSParams of(int type) {
int m;
int h;
String hashAlgStr;
switch (type) {
case LMSUtils.LMS_SHA256_M32_H5:
m = 32;
h = 5;
hashAlgStr = "SHA-256";
break;
case LMSUtils.LMS_SHA256_M32_H10:
m = 32;
h = 10;
hashAlgStr = "SHA-256";
break;
case LMSUtils.LMS_SHA256_M32_H15:
m = 32;
h = 15;
hashAlgStr = "SHA-256";
break;
case LMSUtils.LMS_SHA256_M32_H20:
m = 32;
h = 20;
hashAlgStr = "SHA-256";
break;
case LMSUtils.LMS_SHA256_M32_H25:
m = 32;
h = 25;
hashAlgStr = "SHA-256";
break;
default:
throw new IllegalArgumentException("Unsupported or bad LMS type");
}
return new LMSParams(m, h, hashAlgStr);
}
boolean hasSameHash(LMSParams other) {
return other.hashAlgStr.equals(hashAlgStr) && (other.m == m);
}
boolean hasSameHash(LMOTSParams lmotsParams) {
return lmotsParams.hashAlgName.equals(hashAlgStr) &&
(lmotsParams.n == m);
}
}
static class LMSignature {
final int sigLmType;
final int sigOtsType;
private final byte[] qArr;
final int q; // serial number of the LMS key being used for this signature
final LMOTSignature lmotSig;
final int n; // output length of the hash function used in the OTS
final int p; // number of hash chains in the signature
final int m; // output length of the hash function used in the Merkle tree
final int h; // height of the Merkle tree
private final byte[][] path;
LMSignature(byte[] sigArray, int offset, boolean checkExactLen)
throws SignatureException {
int inLen = sigArray.length - offset;
if (inLen < 8) {
throw new SignatureException("LMS signature is too short");
}
LMOTSParams lmotsParams;
q = LMSUtils.fourBytesToInt(sigArray, offset);
qArr = Arrays.copyOfRange(sigArray, offset, offset + 4);
sigOtsType = LMSUtils.fourBytesToInt(sigArray, offset + 4);
try {
lmotsParams = LMOTSParams.of(sigOtsType);
} catch (IllegalArgumentException e) {
throw new SignatureException(e);
}
n = lmotsParams.n;
p = lmotsParams.p;
if (inLen < (12 + n * (p + 1))) {
throw new SignatureException("LMS signature is too short");
}
int otsSigLen = 4 + n * (p + 1);
byte[] otSigArr = Arrays.copyOfRange(
sigArray, offset + 4, offset + 4 + otsSigLen);
lmotSig = new LMOTSignature(otSigArr, lmotsParams);
int sigTypePos = offset + 4 + otsSigLen;
sigLmType = LMSUtils.fourBytesToInt(sigArray, sigTypePos);
LMSParams lmsParams;
try {
lmsParams = LMSParams.of(sigLmType);
} catch (IllegalArgumentException e) {
throw new SignatureException(e);
}
m = lmsParams.m;
h = lmsParams.h;
int sigArrLen = (12 + n * (p + 1) + m * h);
if ((q >= (1 << h)) ||
(inLen < sigArrLen) ||
(checkExactLen && (inLen != sigArrLen))) {
throw new SignatureException("LMS signature length is incorrect");
}
int pStart = offset + 12 + n * (p + 1);
path = new byte[h][m];
for (int i = 0; i < h; i++) {
path[i] = Arrays.copyOfRange(sigArray, pStart, pStart + m);
pStart += m;
}
}
int sigArrayLength() {
return 12 + n * (p + 1) + m * h;
}
void getQArr(byte[] arr, int pos) {
System.arraycopy(qArr, 0, arr, pos, 4);
}
void getPath(int i, byte[] arr, int pos) {
System.arraycopy(path[i], 0, arr, pos, m);
}
}
static class LMOTSParams {
final int lmotSigType;
final int n; // the number of bytes used from the hash output
final int hashAlg_n = 32; // the output length of the hash function
final int w;
final int twoPowWMinus1;
final int ls;
final int p;
final String hashAlgName;
// The initial buffer image for the lmotsPubKeyCandidate() function.
// In that function a clone of this buffer is fed into the
// hash function as input to the implDigestFixedLengthPreprocessed()
// function (which is basically an allocation and padding computation
// free digest() function, so we can avoid the update()-digest()
// sequence) which is parametrized so that the digest output is copied
// back into the buffer. This way, we avoid memory allocations and some
// computations that would have to be done otherwise.
final byte[] hashBuf;
// Precomputed block for SHA256 when the message size is 55 bytes
// (i.e. when SHA256 is used)
private static final byte[] hashbufSha256_32 = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, (byte) 0x80,
0, 0, 0, 0, 0, 0, 1, (byte) 0xb8
};
private LMOTSParams(
int lmotSigType, int hLen, int w,
int ls, int p, String hashAlgName) {
this.lmotSigType = lmotSigType;
this.n = hLen;
this.w = w;
this.ls = ls;
this.p = p;
twoPowWMinus1 = (1 << w) - 1;
this.hashAlgName = hashAlgName;
hashBuf = hashbufSha256_32;
}
static LMOTSParams of(int lmotsType) {
LMOTSParams params;
switch (lmotsType) {
case LMSUtils.LMOTS_SHA256_N32_W1:
params = new LMOTSParams(
lmotsType, 32, 1, 7, 265, "SHA-256");
break;
case LMSUtils.LMOTS_SHA256_N32_W2:
params = new LMOTSParams(
lmotsType, 32, 2, 6, 133, "SHA-256");
break;
case LMSUtils.LMOTS_SHA256_N32_W4:
params = new LMOTSParams(
lmotsType, 32, 4, 4, 67, "SHA-256");
break;
case LMSUtils.LMOTS_SHA256_N32_W8:
params = new LMOTSParams(
lmotsType, 32, 8, 0, 34, "SHA-256");
break;
default:
throw new IllegalArgumentException(
"Unsupported or bad OTS Algorithm Identifier.");
}
return params;
}
int coef(byte[] S, int i) {
return (twoPowWMinus1 &
(S[i * w / 8] >> (8 - (w * (i % (8 / w)) + w))));
}
private void addCksm(byte[] S) {
int len = n;
int sum = 0;
int numSlices = len * 8 / w;
for (int i = 0; i < numSlices; i++) {
sum += twoPowWMinus1 - coef(S, i);
}
sum = sum << ls;
S[len] = (byte) (sum >> 8);
S[len + 1] = (byte) (sum & 0xff);
}
void digestFixedLengthPreprocessed(
SHA2.SHA256 sha256, byte[] input, int inLen,
byte[] output, int outOffset, int outLen) {
sha256.implDigestFixedLengthPreprocessed(
input, inLen, output, outOffset, outLen);
}
byte[] lmotsPubKeyCandidate(
LMSignature lmSig, byte[] message, LMSPublicKey pKey)
throws SignatureException {
LMOTSignature lmOtSig = lmSig.lmotSig;
if (lmOtSig.otSigType != pKey.otsType) {
throw new SignatureException(
"OTS public key type and OTS signature type do not match");
}
byte[] preQ = new byte[22 + hashAlg_n];
pKey.getI(preQ, 0);
lmSig.getQArr(preQ, 16);
preQ[20] = (byte) 0x81; // D_MSG = 0x8181
preQ[21] = (byte) 0x81;
lmOtSig.getC(preQ, 22);
MessageDigest md;
try {
md = MessageDigest.getInstance(hashAlgName);
} catch (NoSuchAlgorithmException e) { // This should not happen
throw new ProviderException("Digest implementation not found", e);
}
byte[] result;
try {
md.update(preQ, 0, 22 + n);
md.update(message);
byte[] QWithChecksum = new byte[hashAlg_n + 2];
md.digest(QWithChecksum, 0, hashAlg_n);
// the MessageDigest object has now been reset
addCksm(QWithChecksum);
byte[] preCandidate = new byte[22 + (p - 1) * n + hashAlg_n];
pKey.getI(preCandidate, 0);
lmSig.getQArr(preCandidate, 16);
preCandidate[20] = (byte) 0x80; // D_PBLC = 0x8080
preCandidate[21] = (byte) 0x80;
byte[] preZi = hashBuf.clone();
int hashLen = hashBuf.length;
SHA2.SHA256 sha256 = new SHA2.SHA256();
pKey.getI(preZi, 0);
lmSig.getQArr(preZi, 16);
int twoPowWMinus2 = twoPowWMinus1 - 1;
for (int i = 0; i < p; i++) {
int a = coef(QWithChecksum, i);
if (a == twoPowWMinus1) {
lmOtSig.getY(i, preCandidate, 22 + i * n);
} else {
preZi[20] = (byte) (i >> 8);
preZi[21] = (byte) i;
lmOtSig.getY(i, preZi, 23);
}
for (int j = a; j < twoPowWMinus1; j++) {
preZi[22] = (byte) j;
if (j < twoPowWMinus2) {
digestFixedLengthPreprocessed(
sha256, preZi, hashLen, preZi, 23, n);
} else {
digestFixedLengthPreprocessed(
sha256, preZi, hashLen, preCandidate, 22 + i * n, n);
}
}
}
md.update(preCandidate, 0, 22 + p * n);
result = md.digest();
} catch (DigestException e) { // This should not happen
throw new ProviderException("Digest failed", e);
}
if (n != hashAlg_n) {
result = Arrays.copyOfRange(result, 0, n);
}
return result;
}
}
public static class KeyFactoryImpl extends KeyFactorySpi {
@Override
protected PublicKey engineGeneratePublic(KeySpec keySpec)
throws InvalidKeySpecException {
if (keySpec instanceof X509EncodedKeySpec x509Spec) {
try {
return new HSSPublicKey(
x509Spec.getEncoded(), true);
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException(e);
}
} else if (keySpec instanceof RawKeySpec rawSpec) {
try {
return new HSSPublicKey(rawSpec.getKeyArr(), false);
} catch (InvalidKeyException e) {
throw new InvalidKeySpecException(e);
}
}
throw new InvalidKeySpecException("Unrecognized KeySpec");
}
@Override
protected PrivateKey engineGeneratePrivate(KeySpec keySpec)
throws InvalidKeySpecException {
throw new InvalidKeySpecException(
"Private key generation is not supported");
}
@Override
protected <T extends KeySpec> T engineGetKeySpec(Key key, Class<T> keySpec)
throws InvalidKeySpecException {
if (key == null) {
throw new InvalidKeySpecException("key should not be null");
}
if (key.getFormat().equals("X.509") &&
key.getAlgorithm().equalsIgnoreCase("HSS/LMS")) {
if (keySpec.isAssignableFrom(X509EncodedKeySpec.class)) {
return keySpec.cast(new X509EncodedKeySpec(key.getEncoded()));
}
throw new InvalidKeySpecException(
"keySpec is not an X509EncodedKeySpec");
}
throw new InvalidKeySpecException("Wrong key format or key algorithm");
}
@Override
protected Key engineTranslateKey(Key key) throws InvalidKeyException {
if (key == null) {
throw new InvalidKeyException("key cannot be null");
}
PublicKey pKey;
try {
// Check if key originates from this factory
if (key instanceof HSSPublicKey) {
return key;
}
// Convert key to spec
X509EncodedKeySpec x509EncodedKeySpec
= engineGetKeySpec(key, X509EncodedKeySpec.class);
// Create key from spec, and return it
pKey = engineGeneratePublic(x509EncodedKeySpec);
} catch (InvalidKeySpecException e) {
throw new InvalidKeyException(e);
}
return pKey;
}
}
static class HSSPublicKey extends X509Key implements Serializable {
@Serial
private static final long serialVersionUID = 21;
private transient int L;
private transient LMSPublicKey lmsPublicKey;
HSSPublicKey(byte[] keyArray, boolean x509Encoded)
throws InvalidKeyException {
if (x509Encoded) {
decode(keyArray);
if (!KnownOIDs.HSSLMS.value().equals(algid.getOID().toString()) ||
(algid.getParameters() != null)) {
throw new InvalidKeyException("X509Key is not an HSS key");
}
} else {
int inLen = keyArray.length;
if (inLen < 4) {
throw new InvalidKeyException("HSS public key too short");
}
L = LMSUtils.fourBytesToInt(keyArray, 0);
lmsPublicKey =
new LMSPublicKey(
Arrays.copyOfRange(keyArray, 4, keyArray.length),
0, true);
algid = new AlgorithmId(ObjectIdentifier.of(KnownOIDs.HSSLMS));
byte[] derEncodedKeyarray =
new DerOutputStream()
.putOctetString(keyArray)
.toByteArray();
this.setKey(new BitArray(
8 * derEncodedKeyarray.length, derEncodedKeyarray));
}
}
@Override
public String toString() {
HexDumpEncoder encoder = new HexDumpEncoder();
return "HSS/LMS public key, number of layers: " + L +
", LMS type: " + LMSUtils.lmsType(lmsPublicKey.type) +
",\nOTS type: " + LMSUtils.lmotsType(lmsPublicKey.otsType) +
", byte array representation:\n" +
encoder.encode(getKey().toByteArray());
}
/**
* Parse the key. Called by X509Key.
*/
@Override
protected void parseKeyBits() throws InvalidKeyException {
byte[] keyArray = getKey().toByteArray();
if ((keyArray[0] != DerValue.tag_OctetString) || (keyArray[1] != keyArray.length -2)) {
throw new InvalidKeyException("Bad X509Key");
}
L = LMSUtils.fourBytesToInt(keyArray, 2);
lmsPublicKey = new LMSPublicKey(keyArray, 6, true);
}
@java.io.Serial
private Object writeReplace() throws java.io.ObjectStreamException {
return new KeyRep(KeyRep.Type.PUBLIC,
getAlgorithm(),
getFormat(),
getEncoded());
}
@java.io.Serial
private void readObject(java.io.ObjectInputStream s)
throws java.io.ObjectStreamException {
throw new InvalidObjectException(
"HSS public keys are not directly deserializable");
}
}
static class HSSSignature {
private final int Nspk;
private final LMSignature[] siglist;
private final LMSPublicKey[] pubList;
HSSSignature(byte[] sigArr, HSSPublicKey pubKey)
throws SignatureException {
if (sigArr.length < 4) {
throw new SignatureException("HSS signature is too short");
}
Nspk = LMSUtils.fourBytesToInt(sigArr, 0);
if (Nspk + 1 != pubKey.L) {
throw new SignatureException(
"HSS signature and public key have different tree heights");
}
siglist = new LMSignature[Nspk + 1];
pubList = new LMSPublicKey[Nspk];
int index = 4;
try {
for (int i = 0; i < Nspk; i++) {
siglist[i] = new LMSignature(sigArr, index, false);
index += siglist[i].sigArrayLength();
pubList[i] = new LMSPublicKey(sigArr, index, false);
if (!pubKey
.lmsPublicKey
.lmsParams
.hasSameHash(pubList[i].lmsParams)) {
throw new SignatureException(
"Digest algorithm in public key and Signature do not match");
}
index += pubList[i].keyArrayLength();
}
siglist[Nspk] = new LMSignature(sigArr, index, true);
} catch (InvalidKeyException e) {
throw new SignatureException("Invalid key in HSS signature", e);
}
}
}
}

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2002, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -48,6 +48,7 @@ import static sun.security.provider.ByteArrayAccess.*;
abstract class SHA2 extends DigestBase { abstract class SHA2 extends DigestBase {
private static final int ITERATION = 64; private static final int ITERATION = 64;
private static final int BLOCKSIZE = 64;
// Constants for each round // Constants for each round
private static final int[] ROUND_CONSTS = { private static final int[] ROUND_CONSTS = {
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
@ -81,7 +82,7 @@ abstract class SHA2 extends DigestBase {
* Creates a new SHA object. * Creates a new SHA object.
*/ */
SHA2(String name, int digestLength, int[] initialHashes) { SHA2(String name, int digestLength, int[] initialHashes) {
super(name, digestLength, 64); super(name, digestLength, BLOCKSIZE);
this.initialHashes = initialHashes; this.initialHashes = initialHashes;
state = new int[8]; state = new int[8];
resetHashes(); resetHashes();
@ -115,6 +116,17 @@ abstract class SHA2 extends DigestBase {
i2bBig(state, 0, out, ofs, engineGetDigestLength()); i2bBig(state, 0, out, ofs, engineGetDigestLength());
} }
protected void implDigestFixedLengthPreprocessed(
byte[] input, int inLen, byte[] output, int outOffset, int outLen) {
implReset();
for (int ofs = 0; ofs < inLen; ofs += BLOCKSIZE) {
implCompress0(input, ofs);
}
i2bBig(state, 0, output, outOffset, outLen);
}
/** /**
* Process the current block to update the state variable state. * Process the current block to update the state variable state.
*/ */
@ -129,7 +141,7 @@ abstract class SHA2 extends DigestBase {
// Checks similar to those performed by the method 'b2iBig64' // Checks similar to those performed by the method 'b2iBig64'
// are sufficient for the case when the method 'implCompress0' is // are sufficient for the case when the method 'implCompress0' is
// replaced with a compiler intrinsic. // replaced with a compiler intrinsic.
Preconditions.checkFromIndexSize(ofs, 64, buf.length, Preconditions.AIOOBE_FORMATTER); Preconditions.checkFromIndexSize(ofs, BLOCKSIZE, buf.length, Preconditions.AIOOBE_FORMATTER);
} }
// The method 'implCompressImpl' seems not to use its parameters. // The method 'implCompressImpl' seems not to use its parameters.

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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -185,6 +185,10 @@ public final class SunEntries {
"sun.security.provider.DSA$SHA3_384withDSAinP1363Format"); "sun.security.provider.DSA$SHA3_384withDSAinP1363Format");
add(p, "Signature", "SHA3-512withDSAinP1363Format", add(p, "Signature", "SHA3-512withDSAinP1363Format",
"sun.security.provider.DSA$SHA3_512withDSAinP1363Format"); "sun.security.provider.DSA$SHA3_512withDSAinP1363Format");
attrs.clear();
attrs.put("ImplementedIn", "Software");
addWithAlias(p, "Signature", "HSS/LMS", "sun.security.provider.HSS", attrs);
/* /*
* Key Pair Generator engines * Key Pair Generator engines
*/ */
@ -214,6 +218,8 @@ public final class SunEntries {
*/ */
addWithAlias(p, "KeyFactory", "DSA", addWithAlias(p, "KeyFactory", "DSA",
"sun.security.provider.DSAKeyFactory", attrs); "sun.security.provider.DSAKeyFactory", attrs);
addWithAlias(p, "KeyFactory", "HSS/LMS",
"sun.security.provider.HSS$KeyFactoryImpl", attrs);
/* /*
* Digest engines * Digest engines

View file

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -239,6 +239,7 @@ public enum KnownOIDs {
TimeStampTokenInfo("1.2.840.113549.1.9.16.1.4"), TimeStampTokenInfo("1.2.840.113549.1.9.16.1.4"),
SigningCertificate("1.2.840.113549.1.9.16.2.12"), SigningCertificate("1.2.840.113549.1.9.16.2.12"),
SignatureTimestampToken("1.2.840.113549.1.9.16.2.14"), SignatureTimestampToken("1.2.840.113549.1.9.16.2.14"),
HSSLMS("1.2.840.113549.1.9.16.3.17", "HSS/LMS"),
CHACHA20_POLY1305("1.2.840.113549.1.9.16.3.18", "CHACHA20-POLY1305"), CHACHA20_POLY1305("1.2.840.113549.1.9.16.3.18", "CHACHA20-POLY1305"),
FriendlyName("1.2.840.113549.1.9.20"), FriendlyName("1.2.840.113549.1.9.20"),
LocalKeyID("1.2.840.113549.1.9.21"), LocalKeyID("1.2.840.113549.1.9.21"),

View file

@ -0,0 +1,52 @@
/*
* 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 sun.security.util;
import java.security.spec.KeySpec;
/**
* This is a KeySpec that is used to specify a key by its byte array implementation.
* It is intended to be used in testing algorithms where the algorithm specification
* describes the key in this form.
*/
public class RawKeySpec implements KeySpec {
private final byte[] keyArr;
/**
* The sole constructor.
* @param key contains the key as a byte array
*/
public RawKeySpec(byte[] key) {
keyArr = key.clone();
}
/**
* Getter function.
* @return a copy of the key bits
*/
public byte[] getKeyArr() {
return keyArr.clone();
}
}