diff --git a/src/java.base/share/classes/sun/security/provider/DigestBase.java b/src/java.base/share/classes/sun/security/provider/DigestBase.java index 2aaf0a2fac6..0bb15ef3efe 100644 --- a/src/java.base/share/classes/sun/security/provider/DigestBase.java +++ b/src/java.base/share/classes/sun/security/provider/DigestBase.java @@ -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"); + } } diff --git a/src/java.base/share/classes/sun/security/provider/HSS.java b/src/java.base/share/classes/sun/security/provider/HSS.java index c1cb5ed6a30..50afba7cab8 100644 --- a/src/java.base/share/classes/sun/security/provider/HSS.java +++ b/src/java.base/share/classes/sun/security/provider/HSS.java @@ -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); } } } diff --git a/src/java.base/share/classes/sun/security/provider/SHA2.java b/src/java.base/share/classes/sun/security/provider/SHA2.java index e966e6b77f8..7d8c2840de9 100644 --- a/src/java.base/share/classes/sun/security/provider/SHA2.java +++ b/src/java.base/share/classes/sun/security/provider/SHA2.java @@ -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(); diff --git a/src/java.base/share/classes/sun/security/provider/SHA3.java b/src/java.base/share/classes/sun/security/provider/SHA3.java index a096cac5f50..0578645c1cd 100644 --- a/src/java.base/share/classes/sun/security/provider/SHA3.java +++ b/src/java.base/share/classes/sun/security/provider/SHA3.java @@ -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); diff --git a/src/java.base/share/classes/sun/security/util/KeyUtil.java b/src/java.base/share/classes/sun/security/util/KeyUtil.java index 942a91d61b8..e9dabdc5b06 100644 --- a/src/java.base/share/classes/sun/security/util/KeyUtil.java +++ b/src/java.base/share/classes/sun/security/util/KeyUtil.java @@ -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) { diff --git a/test/jdk/sun/security/provider/hss/TestHSS.java b/test/jdk/sun/security/provider/hss/TestHSS.java index 8056855cc1a..48019d4e465 100644 --- a/test/jdk/sun/security/provider/hss/TestHSS.java +++ b/test/jdk/sun/security/provider/hss/TestHSS.java @@ -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