8369917: LMS/HSS RFC 9858 Support

Reviewed-by: weijun
This commit is contained in:
Mark Powers 2026-04-13 20:37:18 +00:00
parent 121165ec91
commit 531dad0cd7
6 changed files with 408 additions and 142 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2003, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2003, 2026, 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
@ -242,4 +242,21 @@ abstract class DigestBase extends MessageDigestSpi implements Cloneable {
padding = new byte[136];
padding[0] = (byte)0x80;
}
/**
* Digest block-length bytes in a single operation.
* Subclasses are expected to override this method. It is intended
* for fixed-length short input where input includes padding bytes.
* @param input byte array to be digested
* @param inLen the length of the input
* @param output the output buffer
* @param outOffset the offset into output buffer where digest should be written
* @param outLen the length of the output buffer
* @throws UnsupportedOperationException if a subclass does not override this method
*/
void implDigestFixedLengthPreprocessed (
byte[] input, int inLen, byte[] output, int outOffset, int outLen)
throws UnsupportedOperationException {
throw new UnsupportedOperationException("should not be here");
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2026, 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
@ -24,16 +24,22 @@
*/
package sun.security.provider;
import java.io.ByteArrayOutputStream;
import java.io.InvalidObjectException;
import java.io.Serial;
import java.io.Serializable;
import java.security.SecureRandom;
import java.security.*;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Arrays;
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
@ -196,42 +202,94 @@ public final class HSS extends SignatureSpi {
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 final int LMS_SHA256_M32_H5 = 0x05;
static final int LMS_SHA256_M32_H10 = 0x06;
static final int LMS_SHA256_M32_H15 = 0x07;
static final int LMS_SHA256_M32_H20 = 0x08;
static final int LMS_SHA256_M32_H25 = 0x09;
static final int LMS_SHA256_M24_H5 = 0x0a;
static final int LMS_SHA256_M24_H10 = 0x0b;
static final int LMS_SHA256_M24_H15 = 0x0c;
static final int LMS_SHA256_M24_H20 = 0x0d;
static final int LMS_SHA256_M24_H25 = 0x0e;
static final int LMS_SHAKE_M32_H5 = 0x0f;
static final int LMS_SHAKE_M32_H10 = 0x10;
static final int LMS_SHAKE_M32_H15 = 0x11;
static final int LMS_SHAKE_M32_H20 = 0x12;
static final int LMS_SHAKE_M32_H25 = 0x13;
static final int LMS_SHAKE_M24_H5 = 0x14;
static final int LMS_SHAKE_M24_H10 = 0x15;
static final int LMS_SHAKE_M24_H15 = 0x16;
static final int LMS_SHAKE_M24_H20 = 0x17;
static final int LMS_SHAKE_M24_H25 = 0x18;
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";
}
String typeStr = switch (type) {
case LMS_RESERVED -> "LMS_RESERVED";
case LMS_SHA256_M32_H5 -> "LMS_SHA256_M32_H5";
case LMS_SHA256_M32_H10 -> "LMS_SHA256_M32_H10";
case LMS_SHA256_M32_H15 -> "LMS_SHA256_M32_H15";
case LMS_SHA256_M32_H20 -> "LMS_SHA256_M32_H20";
case LMS_SHA256_M32_H25 -> "LMS_SHA256_M32_H25";
case LMS_SHA256_M24_H5 -> "LMS_SHA256_M24_H5";
case LMS_SHA256_M24_H10 -> "LMS_SHA256_M24_H10";
case LMS_SHA256_M24_H15 -> "LMS_SHA256_M24_H15";
case LMS_SHA256_M24_H20 -> "LMS_SHA256_M24_H20";
case LMS_SHA256_M24_H25 -> "LMS_SHA256_M24_H25";
case LMS_SHAKE_M32_H5 -> "LMS_SHAKE_M32_H5";
case LMS_SHAKE_M32_H10 -> "LMS_SHAKE_M32_H10";
case LMS_SHAKE_M32_H15 -> "LMS_SHAKE_M32_H15";
case LMS_SHAKE_M32_H20 -> "LMS_SHAKE_M32_H20";
case LMS_SHAKE_M32_H25 -> "LMS_SHAKE_M32_H25";
case LMS_SHAKE_M24_H5 -> "LMS_SHAKE_M24_H5";
case LMS_SHAKE_M24_H10 -> "LMS_SHAKE_M24_H10";
case LMS_SHAKE_M24_H15 -> "LMS_SHAKE_M24_H15";
case LMS_SHAKE_M24_H20 -> "LMS_SHAKE_M24_H20";
case LMS_SHAKE_M24_H25 -> "LMS_SHAKE_M24_H25";
default -> "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 final int LMOTS_SHA256_N32_W1 = 0x01;
static final int LMOTS_SHA256_N32_W2 = 0x02;
static final int LMOTS_SHA256_N32_W4 = 0x03;
static final int LMOTS_SHA256_N32_W8 = 0x04;
static final int LMOTS_SHA256_N24_W1 = 0x05;
static final int LMOTS_SHA256_N24_W2 = 0x06;
static final int LMOTS_SHA256_N24_W4 = 0x07;
static final int LMOTS_SHA256_N24_W8 = 0x08;
static final int LMOTS_SHAKE_N32_W1 = 0x09;
static final int LMOTS_SHAKE_N32_W2 = 0x0a;
static final int LMOTS_SHAKE_N32_W4 = 0x0b;
static final int LMOTS_SHAKE_N32_W8 = 0x0c;
static final int LMOTS_SHAKE_N24_W1 = 0x0d;
static final int LMOTS_SHAKE_N24_W2 = 0x0e;
static final int LMOTS_SHAKE_N24_W4 = 0x0f;
static final int LMOTS_SHAKE_N24_W8 = 0x10;
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";
}
String typeStr = switch (type) {
case LMOTS_RESERVED -> "LMOTS_RESERVED";
case LMOTS_SHA256_N32_W1 -> "LMOTS_SHA256_N32_W1";
case LMOTS_SHA256_N32_W2 -> "LMOTS_SHA256_N32_W2";
case LMOTS_SHA256_N32_W4 -> "LMOTS_SHA256_N32_W4";
case LMOTS_SHA256_N32_W8 -> "LMOTS_SHA256_N32_W8";
case LMOTS_SHA256_N24_W1 -> "LMOTS_SHA256_N24_W1";
case LMOTS_SHA256_N24_W2 -> "LMOTS_SHA256_N24_W2";
case LMOTS_SHA256_N24_W4 -> "LMOTS_SHA256_N24_W4";
case LMOTS_SHA256_N24_W8 -> "LMOTS_SHA256_N24_W8";
case LMOTS_SHAKE_N32_W1 -> "LMOTS_SHAKE_N32_W1";
case LMOTS_SHAKE_N32_W2 -> "LMOTS_SHAKE_N32_W2";
case LMOTS_SHAKE_N32_W4 -> "LMOTS_SHAKE_N32_W4";
case LMOTS_SHAKE_N32_W8 -> "LMOTS_SHAKE_N32_W8";
case LMOTS_SHAKE_N24_W1 -> "LMOTS_SHAKE_N24_W1";
case LMOTS_SHAKE_N24_W2 -> "LMOTS_SHAKE_N24_W2";
case LMOTS_SHAKE_N24_W4 -> "LMOTS_SHAKE_N24_W4";
case LMOTS_SHAKE_N24_W8 -> "LMOTS_SHAKE_N24_W8";
default -> "unrecognized";
};
return typeStr;
}
@ -352,53 +410,65 @@ public final class HSS extends SignatureSpi {
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 hashAlg_m; // 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) {
private LMSParams(int m, int h, String hashAlgStr, int hashAlg_m) {
this.m = m;
this.h = h;
this.hashAlgStr = hashAlgStr;
this.hashAlg_m = hashAlg_m;
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:
LMSParams params = switch (type) {
case LMSUtils.LMS_SHA256_M32_H5 ->
new LMSParams(32, 5, "SHA-256", 32);
case LMSUtils.LMS_SHA256_M32_H10 ->
new LMSParams(32, 10, "SHA-256", 32);
case LMSUtils.LMS_SHA256_M32_H15 ->
new LMSParams(32, 15, "SHA-256", 32);
case LMSUtils.LMS_SHA256_M32_H20 ->
new LMSParams(32, 20, "SHA-256", 32);
case LMSUtils.LMS_SHA256_M32_H25 ->
new LMSParams(32, 25, "SHA-256", 32);
case LMSUtils.LMS_SHA256_M24_H5 ->
new LMSParams(24, 5, "SHA-256", 32);
case LMSUtils.LMS_SHA256_M24_H10 ->
new LMSParams(24, 10, "SHA-256", 32);
case LMSUtils.LMS_SHA256_M24_H15 ->
new LMSParams(24, 15, "SHA-256", 32);
case LMSUtils.LMS_SHA256_M24_H20 ->
new LMSParams(24, 20, "SHA-256", 32);
case LMSUtils.LMS_SHA256_M24_H25 ->
new LMSParams(24, 25, "SHA-256", 32);
case LMSUtils.LMS_SHAKE_M32_H5 ->
new LMSParams(32, 5, "SHAKE256-512", 64);
case LMSUtils.LMS_SHAKE_M32_H10 ->
new LMSParams(32, 10, "SHAKE256-512", 64);
case LMSUtils.LMS_SHAKE_M32_H15 ->
new LMSParams(32, 15, "SHAKE256-512", 64);
case LMSUtils.LMS_SHAKE_M32_H20 ->
new LMSParams(32, 20, "SHAKE256-512", 64);
case LMSUtils.LMS_SHAKE_M32_H25 ->
new LMSParams(32, 25, "SHAKE256-512", 64);
case LMSUtils.LMS_SHAKE_M24_H5 ->
new LMSParams(24, 5, "SHAKE256-512", 64);
case LMSUtils.LMS_SHAKE_M24_H10 ->
new LMSParams(24, 10, "SHAKE256-512", 64);
case LMSUtils.LMS_SHAKE_M24_H15 ->
new LMSParams(24, 15, "SHAKE256-512", 64);
case LMSUtils.LMS_SHAKE_M24_H20 ->
new LMSParams(24, 20, "SHAKE256-512", 64);
case LMSUtils.LMS_SHAKE_M24_H25 ->
new LMSParams(24, 25, "SHAKE256-512", 64);
default ->
throw new IllegalArgumentException("Unsupported or bad LMS type");
}
return new LMSParams(m, h, hashAlgStr);
};
return params;
}
boolean hasSameHash(LMSParams other) {
@ -495,7 +565,7 @@ public final class HSS extends SignatureSpi {
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
int hashAlg_n; // the output length of the hash function
final int w;
final int twoPowWMinus1;
final int ls;
@ -511,6 +581,7 @@ public final class HSS extends SignatureSpi {
// 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 = {
@ -523,10 +594,64 @@ public final class HSS extends SignatureSpi {
0, 0, 0, 0, 0, 0, 0, (byte) 0x80,
0, 0, 0, 0, 0, 0, 1, (byte) 0xb8
};
// Precomputed block for SHA256 when the message size is 47 bytes
// (i.e. when SHA256-192 is used)
private static final byte[] hashbufSha256_24 = {
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, 0, 0,
0, 0, 0, 0, 0, 0, 1, 0x78
};
// Precomputed block for SHAKE256 when the message size is 55 bytes
// (i.e. when SHAKE256 is used)
private static final byte[] hashbufShake256_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) 0x1F,
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, 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
};
// Precomputed block for SHAKE256 when the message size is 47 bytes
// (i.e. when SHAKE256-192 is used)
private static final byte[] hashbufShake256_24 = {
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) 0x1F,
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, 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
};
private LMOTSParams(
int lmotSigType, int hLen, int w,
int ls, int p, String hashAlgName) {
int ls, int p, String hashAlgName, int hashAlg_n) {
this.lmotSigType = lmotSigType;
this.n = hLen;
this.w = w;
@ -534,32 +659,60 @@ public final class HSS extends SignatureSpi {
this.p = p;
twoPowWMinus1 = (1 << w) - 1;
this.hashAlgName = hashAlgName;
hashBuf = hashbufSha256_32;
this.hashAlg_n = hashAlg_n;
hashBuf = switch (hashAlgName) {
case "SHAKE256-512" -> {
yield this.n == 24 ?
hashbufShake256_24 : hashbufShake256_32;
}
case "SHA-256" -> {
yield this.n == 24 ?
hashbufSha256_24 : hashbufSha256_32;
}
default ->
throw new IllegalArgumentException(
"Unknown hash algorithm "+hashAlgName);
};
}
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:
LMOTSParams params = switch (lmotsType) {
case LMSUtils.LMOTS_SHA256_N32_W1 ->
new LMOTSParams(lmotsType, 32, 1, 7, 265, "SHA-256", 32);
case LMSUtils.LMOTS_SHA256_N32_W2 ->
new LMOTSParams(lmotsType, 32, 2, 6, 133, "SHA-256", 32);
case LMSUtils.LMOTS_SHA256_N32_W4 ->
new LMOTSParams(lmotsType, 32, 4, 4, 67, "SHA-256", 32);
case LMSUtils.LMOTS_SHA256_N32_W8 ->
new LMOTSParams(lmotsType, 32, 8, 0, 34, "SHA-256", 32);
case LMSUtils.LMOTS_SHA256_N24_W1 ->
new LMOTSParams(lmotsType, 24, 1, 8, 200, "SHA-256", 32);
case LMSUtils.LMOTS_SHA256_N24_W2 ->
new LMOTSParams(lmotsType, 24, 2, 6, 101, "SHA-256", 32);
case LMSUtils.LMOTS_SHA256_N24_W4 ->
new LMOTSParams(lmotsType, 24, 4, 4, 51, "SHA-256", 32);
case LMSUtils.LMOTS_SHA256_N24_W8 ->
new LMOTSParams(lmotsType, 24, 8, 0, 26, "SHA-256", 32);
case LMSUtils.LMOTS_SHAKE_N32_W1 ->
new LMOTSParams(lmotsType, 32, 1, 7, 265, "SHAKE256-512", 64);
case LMSUtils.LMOTS_SHAKE_N32_W2 ->
new LMOTSParams(lmotsType, 32, 2, 6, 133, "SHAKE256-512", 64);
case LMSUtils.LMOTS_SHAKE_N32_W4 ->
new LMOTSParams(lmotsType, 32, 4, 4, 67, "SHAKE256-512", 64);
case LMSUtils.LMOTS_SHAKE_N32_W8 ->
new LMOTSParams(lmotsType, 32, 8, 0, 34, "SHAKE256-512", 64);
case LMSUtils.LMOTS_SHAKE_N24_W1 ->
new LMOTSParams(lmotsType, 24, 1, 8, 200, "SHAKE256-512", 64);
case LMSUtils.LMOTS_SHAKE_N24_W2 ->
new LMOTSParams(lmotsType, 24, 2, 6, 101, "SHAKE256-512", 64);
case LMSUtils.LMOTS_SHAKE_N24_W4 ->
new LMOTSParams(lmotsType, 24, 4, 4, 51, "SHAKE256-512", 64);
case LMSUtils.LMOTS_SHAKE_N24_W8 ->
new LMOTSParams(lmotsType, 24, 8, 0, 26, "SHAKE256-512", 64);
default ->
throw new IllegalArgumentException(
"Unsupported or bad OTS Algorithm Identifier.");
}
};
return params;
}
@ -580,13 +733,6 @@ public final class HSS extends SignatureSpi {
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 {
@ -625,7 +771,13 @@ public final class HSS extends SignatureSpi {
byte[] preZi = hashBuf.clone();
int hashLen = hashBuf.length;
SHA2.SHA256 sha256 = new SHA2.SHA256();
DigestBase db;
if (hashAlgName.startsWith("SHAKE")) {
db = new SHA3.SHAKE256Hash();
} else {
db = new SHA2.SHA256();
}
pKey.getI(preZi, 0);
lmSig.getQArr(preZi, 16);
@ -643,11 +795,11 @@ public final class HSS extends SignatureSpi {
for (int j = a; j < twoPowWMinus1; j++) {
preZi[22] = (byte) j;
if (j < twoPowWMinus2) {
digestFixedLengthPreprocessed(
sha256, preZi, hashLen, preZi, 23, n);
db.implDigestFixedLengthPreprocessed(preZi,
hashLen, preZi, 23, n);
} else {
digestFixedLengthPreprocessed(
sha256, preZi, hashLen, preCandidate, 22 + i * n, n);
db.implDigestFixedLengthPreprocessed(preZi,
hashLen, preCandidate, 22 + i * n, n);
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2002, 2023, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2002, 2026, 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
@ -117,6 +117,7 @@ abstract class SHA2 extends DigestBase {
}
@Override
protected void implDigestFixedLengthPreprocessed(
byte[] input, int inLen, byte[] output, int outOffset, int outLen) {
implReset();

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2016, 2026, 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
@ -98,6 +98,15 @@ public abstract class SHA3 extends DigestBase {
this.suffix = suffix;
}
@Override
protected void implDigestFixedLengthPreprocessed(
byte[] input, int inLen, byte[] output, int outOffset, int outLen) {
implReset();
implCompress(input, 0);
implDigest0(output, outOffset, outLen);
}
private void implCompressCheck(byte[] b, int ofs) {
Objects.requireNonNull(b);
Preconditions.checkIndex(ofs + blockSize - 1, b.length, Preconditions.AIOOBE_FORMATTER);
@ -136,9 +145,6 @@ public abstract class SHA3 extends DigestBase {
* DigestBase calls implReset() when necessary.
*/
void implDigest(byte[] out, int ofs) {
// Moving this allocation to the block where it is used causes a little
// performance drop, that is why it is here.
byte[] byteState = new byte[8];
if (engineGetDigestLength() == 0) {
// This is an XOF, so the digest() call is illegal.
throw new ProviderException("Calling digest() is not allowed in an XOF");
@ -146,8 +152,12 @@ public abstract class SHA3 extends DigestBase {
finishAbsorb();
implDigest0(out, ofs, engineGetDigestLength());
}
void implDigest0(byte[] out, int ofs, int outLen) {
int availableBytes = blockSize;
int numBytes = engineGetDigestLength();
int numBytes = outLen;
while (numBytes > availableBytes) {
for (int i = 0; i < availableBytes / 8; i++) {
@ -163,6 +173,10 @@ public abstract class SHA3 extends DigestBase {
asLittleEndian.set(out, ofs, state[i]);
ofs += 8;
}
// Moving this allocation to the block where it is used causes a little
// performance drop, that is why it is here.
byte[] byteState = new byte[8];
if (numBytes % 8 != 0) {
asLittleEndian.set(byteState, 0, state[numLongs]);
System.arraycopy(byteState, 0, out, ofs, numBytes % 8);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2012, 2026, 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
@ -444,15 +444,16 @@ public final class KeyUtil {
// is the LMS public key for the top-level tree.
// Section 5.3: LMS public key is u32str(type) || u32str(otstype) || I || T[1]
// Section 8: type is the numeric identifier for an LMS specification.
// This RFC defines 5 SHA-256 based types, value from 5 to 9.
if (rawKey.length < 8) {
throw new NoSuchAlgorithmException("Cannot decode public key");
}
int num = ((rawKey[4] & 0xff) << 24) + ((rawKey[5] & 0xff) << 16)
+ ((rawKey[6] & 0xff) << 8) + (rawKey[7] & 0xff);
return switch (num) {
// RFC 8554 only supports SHA_256 hash algorithm
// RFC 8554 only supports SHA_256 hash algorithms
case 5, 6, 7, 8, 9 -> AlgorithmId.SHA256_oid;
// RFC 9858 supports SHAKE_256 hash algorithms
case 15, 16, 17, 18, 19 -> AlgorithmId.SHAKE256_512_oid;
default -> throw new NoSuchAlgorithmException("Unknown LMS type: " + num);
};
} catch (IOException e) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, 2025, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2026, 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
@ -23,7 +23,7 @@
/*
* @test
* @bug 8298127 8347596
* @bug 8298127 8347596 8369917
* @library /test/lib
* @summary tests for HSS/LMS provider
* @modules java.base/sun.security.util
@ -194,6 +194,8 @@ public class TestHSS {
static TestCase[] TestCases = new TestCase[] {
// Test Case #1
// RFC 8554 Test Case 1
// LM_SHA256_M32_H5, LMOTS_SHA256_N32_W8
// LM_SHA256_M32_H5, LMOTS_SHA256_N32_W8
new TestCase(
null, // exception
true, // expected result
@ -307,6 +309,8 @@ public class TestHSS {
// Test Case #2
// RFC 8554 Test Case 2
// LM_SHA256_M32_H10, LMOTS_SHA256_N32_W4
// LM_SHA256_M32_H5, LMOTS_SHA256_N32_W8
new TestCase(
null, // exception
true, // expected result
@ -456,11 +460,11 @@ public class TestHSS {
),
// Test Case #3
// Additional Parameter sets for LMS Hash-Based Signatures (fluhrer)
// This test should fail because SHA256_M24 is supported.
// RFC 9858 A.1
// LMS_SHA256_M24_H5, LMOTS_SHA256_N24_W8
new TestCase(
new InvalidKeySpecException(),
false, // expected result
null, // exception
true, // expected result
decode("""
00000001
0000000a
@ -502,11 +506,11 @@ public class TestHSS {
),
// Test Case #4
// Additional Parameter sets for LMS Hash-Based Signatures (fluhrer)
// This test should fail because SHAKE is not supported.
// RFC 9858 A.2
// LMS_SHAKE_M24_H5, LMOTS_SHAKE_N24_W8
new TestCase(
new InvalidKeySpecException(),
false, // expected result
null, // exception
true, // expected result
decode("""
00000001
00000014
@ -549,11 +553,11 @@ public class TestHSS {
),
// Test Case #5
// Additional Parameter sets for LMS Hash-Based Signatures (fluhrer)
// This test should fail because SHAKE is not supported.
// RFC 9858 A.3
// LMS_SHAKE_M32_H5, LMOTS_SHAKE_N32_W8
new TestCase(
new InvalidKeySpecException(),
false, // expected result
null, // exception
true, // expected result
decode("""
00000001
0000000f
@ -611,6 +615,83 @@ public class TestHSS {
),
// Test Case #6
// RFC 9858 A.4
// LMS_SHA256_M24_H20, LMOTS_SHA256_N24_W4
new TestCase(
null, // exception
true, // expected result
decode("""
00000001
0000000d
00000007
404142434445464748494a4b4c4d4e4f9c08a50d170406869892802ee4142fcd
eac990f110c2460c"""),
decode("""
54657374206d65737361676520666f72205348413235362f31393220773d34
"""),
decode("""
00000000
00000064
00000007
853fa6e1a65fef076acd2485505b93be9aeb2641e3d3805c1887f26f4bcdb6ac
0337b76fa5d6603834287e010b20516f7c336df2134c0a981f1ec2bb7baee516
e91e67d3bd16c8d945a7f2be4fd84a604ae3743efc609ee0e69572e9c6d4a682
50e877b75d3cae63e9d5c15a32bb3cd17045f6b3e195284fdd1ee3cfbe18f1cb
d06ef3e7af34b1844d42dac453115a4507ed525cec120d054b403c61a7e5034f
ac4be6ef5412d194d4b6bbc0ae6cd3fe9993d583ee06f4030bc832efec24d1f7
13f5088731b91a98491fa3adf1b322bce26df24c8415e3a46bdfe07a6fd48e6d
951515758cd6434991098bf6949249fca338ec235871dd564998d07d9b1b1b8d
644e657fee8039da8fe195d129faddb12d543b86b0ab8cf6f26c121783f3b828
d03f793b42909272f688e4ef6d46e82bdd1a02b1ff86c3b79920b2e6f19faf75
c623242f1f2c549f84fb2f4c3ffead3120d97baea507467bb2da79f132bbe15b
596fdfcb70983107ebca2597de9d55bd83bcae5c28a85259dadb354859986e60
c8afa0b10bd08a8f9ed9b1ede3377075fe0ae36349f7d2ed7bfc9ece0d4cd697
2059329419feaf3b9a1045b6cfa4ae89b1cea8950aea4af870d1a3a3909ebc5a
3013d6deb927abc0f95093e83cb36a9c1d6f13add19268ac7a0371f8335b0952
a57fdb0141d55d937dd6ebb08fee8a5cf426ac97d54ee7aa17e6c57be5e62a52
a6b1b986730d3a3aad8a7d327ddf883e6bc7b636eb2a5c4f2a635ae5bada5418
d43dfedb69c0a0209334fac89d420d6ad5a2e1df95d26a1bfeb99a5e8455061b
fdf2d6e8394caf8a4be699b8afa38e524d4053330af478f85bf33d3ca3a35bc9
6987282bd513a8f6a52db9ba36aa90882b3bf573fa275449d8d49eb30bed2bb1
7a0ecc7d8a20807f2ea3dd37acd46c713cc2ac9d01a20a30d6832eef86a1e26d
1cad7761bf4130a6565572766026509deeddaf46b605452b218a4e137a7ce063
b546a35c52510f0ea2cac879192ec443e43b37c5ffa23da7a7fc254324a3de70
5c771794f10ea356e5a747e5146fd804a47719803c185b380e34b8dcc8269c2b
073d86b2307cf90c6c3ef9271f2d53df2579f0c4cfb632db37a9025965f70b46
16673228e98644be6576417b7a97f104350259e7f697408cdf8cf81a3e774162
6ccdb87ad8531264cb5ceb7c8c097cec505091a3ee3a826c54f78169abc2e7d0
a318dac10250ba940e51e79a3f572fb32bf442be6fd81267946e6387f9a8c705
d945c653f2684655e3fa6b9ee311d8a091bef9898292fa272fb8761f066c23d8
7aa10d67871cc5419c843b796855c51ad1272e9264acd2035a82b12c2ddbc85a
dfcd7c22366a36495349391dbf0001064b8f6b28365445d733e48f1b058a6cb3
e71bbb8df3e90406299894f4ca682943ceeba410b33b07716ffc18d6eab75f2d
6372f1133605fa3c3ed66f2d8f7c5abe59e87d4500965e347523d73cb356c144
827aaa22b1c72a15293c7400e02aaefcf36f68a8246900e6e6228e7ad19d1450
c23434f1e45043dc2b6db57f20d8f5b344d4162aa651333287cd8bf8fac41c78
d61fe2929209bfe2dc5a2f80205c043b22e540a29f0ea0a5ff529e55bf1dfe42
96fc4bb4ac2e875322ab115db479fe979d64f78409af4ec3ad3b758fff83af1b
9c48e90ca39366f426c2fb921df55c72786a9217723945a1ac1a66af7def4f8b
367001732cce0e5bac91ac9d603807f8bab105b46d315d4cb88feb1c8686884b
0000000d
13d1a8ef00c5811c15c4d774fdcf75155315aff53ebdff8fb6a54f12c165963d
d5690cc9842b0e2190afc5443497584c832155599d00aced84bb3b59170396f7
db4fa84aa8577f76cf9367d6e99d3d5be3555d7156b004f2002f505681b1ad22
9b9b46a666672aa8ee662c3a0456a9adda7a44fbaca46789577dcd36dc5cdff3
4b864d0a32492a0acbcaa6c011748f205b91ab2ab84f2333fb3e3b9acaecdac3
8b58aa5f32e718e225631ed6674cccb8c119acbd4992ab3130a6e912deec5983
5ab52fbc549430f8b403e4a2a51cc7f46fc143d365763aa1708fd25bcd657a79
0e54718d970906242a3b8a97dff18e91a44c4ba818a8dd2d242251265b023b82
6077eb740f6682e6c4ada2b85a67988d406132c2ad899099e44cfe610c3a5af7
0b406224411a59597e5dda0f31cd16c914b67e96141661f0074f43eb02273481
bc324ded26c64f2388559d8c8bd0ef8b34ca4afebfac2a689b4246c264241488
dcf922350dc44f7bc09d57dc1126291b2318810e0f44801c071e572fd032c780
f44c9503a4c03c37417dc96422ba0849c37956f9fd5d33ea4fcab84276effec6
52ca77d7d47ac93c633d99e0a236f03d5587d1990ffaef737fced1f5cdd8f373
844e9f316aad41a0b12302639f83a2d74c9fe30d305a942bc0c30352a5e44dfb
""")
),
// Test Case #7
// LMSigParameters.lms_sha256_m32_h15, LMOtsParameters.sha256_n32_w8
new TestCase(
null, // exception
@ -713,7 +794,7 @@ public class TestHSS {
""")
),
// Test Case #7
// Test Case #8
// LMSigParameters.lms_sha256_m32_h20, LMOtsParameters.sha256_n32_w8
new TestCase(
null, // exception
@ -821,7 +902,7 @@ public class TestHSS {
""")
),
// Test Case #8
// Test Case #9
// LMSigParameters.lms_sha256_m32_h15, LMOtsParameters.sha256_n32_w4
new TestCase(
null, // exception
@ -957,7 +1038,7 @@ public class TestHSS {
""")
),
// Test Case #9
// Test Case #10
// LMSigParameters.lms_sha256_m32_h20, LMOtsParameters.sha256_n32_w4
new TestCase(
null, // exception
@ -1098,7 +1179,7 @@ public class TestHSS {
""")
),
// Test Case #10
// Test Case #11
// LMSigParameters.lms_sha256_m32_h15, LMOtsParameters.sha256_n32_w4
// LMSigParameters.lms_sha256_m32_h10, LMOtsParameters.sha256_n32_w4
new TestCase(
@ -1320,7 +1401,7 @@ public class TestHSS {
""")
),
// Test Case #11
// Test Case #12
// LMSigParameters.lms_sha256_m32_h15, LMOtsParameters.sha256_n32_w4
// LMSigParameters.lms_sha256_m32_h15, LMOtsParameters.sha256_n32_w4
new TestCase(
@ -1547,7 +1628,7 @@ public class TestHSS {
""")
),
// Test Case #12
// Test Case #13
// LMSigParameters.lms_sha256_m32_h20, LMOtsParameters.sha256_n32_w4
// LMSigParameters.lms_sha256_m32_h10, LMOtsParameters.sha256_n32_w4
new TestCase(
@ -1774,7 +1855,7 @@ public class TestHSS {
""")
),
// Test Case #13
// Test Case #14
// LMSigParameters.lms_sha256_m32_h20, LMOtsParameters.sha256_n32_w4
// LMSigParameters.lms_sha256_m32_h15, LMOtsParameters.sha256_n32_w4
new TestCase(
@ -2006,7 +2087,7 @@ public class TestHSS {
""")
),
// Test Case #14
// Test Case #15
// LMS signature length is incorrect
new TestCase(
new SignatureException(),
@ -2119,7 +2200,7 @@ public class TestHSS {
""")
),
// Test Case #15
// Test Case #16
// HSS signature and public key have different tree heights
new TestCase(
new SignatureException(),
@ -2232,7 +2313,7 @@ public class TestHSS {
""")
),
// Test Case #16
// Test Case #17
// bad signature
new TestCase(
null, // exception
@ -2345,7 +2426,7 @@ public class TestHSS {
""")
),
// Test Case #17
// Test Case #18
// Invalid key in HSS signature
new TestCase(
new SignatureException(),
@ -2458,7 +2539,7 @@ public class TestHSS {
""")
),
// Test Case #18
// Test Case #19
// LMS signature is too short
new TestCase(
new SignatureException(),
@ -2485,7 +2566,7 @@ public class TestHSS {
965a25bfd37f196b9073f3d4a232feb6""")
),
// Test Case #19
// Test Case #20
// bad signature
new TestCase(
null, // exception