From 0d587a5792cfcdc389f9a1ce721f24324b096f90 Mon Sep 17 00:00:00 2001 From: Rune Flobakk Date: Sun, 17 Nov 2024 16:09:32 +0100 Subject: [PATCH 001/890] Add BOM artifact Fixes #899 --- bom/build.gradle | 31 +++++++++++++++++++++++++++++++ build.gradle | 5 ++--- settings.gradle | 1 + 3 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 bom/build.gradle diff --git a/bom/build.gradle b/bom/build.gradle new file mode 100644 index 0000000000..8fa2226cd0 --- /dev/null +++ b/bom/build.gradle @@ -0,0 +1,31 @@ +plugins { + id("java-platform") + id("maven-publish") +} + +description = "BouncyCastle Bill of Materials (BOM)" + +dependencies { + constraints { + api(project(":core")) + api(project(":util")) + api(project(":pg")) + api(project(":pkix")) + api(project(":prov")) + api(project(":tls")) + api(project(":test")) + api(project(":mls")) + api(project(":mail")) + api(project(":jmail")) + } +} + +publishing { + publications { + mavenJava(MavenPublication) { + groupId = 'org.bouncycastle' + artifactId = "bc-bom-$vmrange" + from components.javaPlatform + } + } +} diff --git a/build.gradle b/build.gradle index d648c8bedf..14d6738c3e 100644 --- a/build.gradle +++ b/build.gradle @@ -53,7 +53,7 @@ ext { // this needs to go here, otherwise it can't find config apply plugin: 'io.spring.nohttp' -allprojects { +configure(allprojects.findAll {it.name != 'bom'}) { apply plugin: 'java' apply plugin: 'idea' apply plugin: 'checkstyle' @@ -183,8 +183,7 @@ ext { } - -subprojects { +configure(subprojects.findAll {it.name != 'bom'}) { apply plugin: 'eclipse' apply plugin: 'maven-publish' diff --git a/settings.gradle b/settings.gradle index c54949b153..cf2608deed 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,4 @@ +include "bom" include "core" include "util" include "pg" From 573d0723cbdd1ab71c22ae2f3037dbff3a63b6a8 Mon Sep 17 00:00:00 2001 From: gefeili Date: Sat, 11 Jan 2025 15:02:05 +1030 Subject: [PATCH 002/890] TODO: Ascon --- .../crypto/engines/AEADBufferBaseEngine.java | 2 +- .../crypto/engines/AsconAEAD128.java | 25 +- .../crypto/engines/AsconBaseEngine.java | 322 +++--------------- .../crypto/engines/AsconEngine.java | 13 +- .../bouncycastle/crypto/test/AsconTest.java | 8 +- 5 files changed, 78 insertions(+), 292 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 7199105f71..9a8a6b675e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -310,7 +310,7 @@ protected boolean checkData() } } - private void finishAAD(State nextState) + protected void finishAAD(State nextState) { // State indicates whether we ever received AAD switch (m_state) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java index 7c32bd12eb..9a82079f96 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java @@ -24,12 +24,13 @@ public AsconAEAD128() KEY_SIZE = 16; IV_SIZE = 16; MAC_SIZE = 16; - ASCON_AEAD_RATE = 16; + AADBufferSize = BlockSize = 16; ASCON_IV = 0x00001000808c0001L; algorithmName = "Ascon-AEAD128"; nr = 8; - m_bufferSizeDecrypt = ASCON_AEAD_RATE + MAC_SIZE; + m_bufferSizeDecrypt = BlockSize + MAC_SIZE; m_buf = new byte[m_bufferSizeDecrypt]; + m_aad = new byte[BlockSize]; dsep = -9223372036854775808L; //0x80L << 56 } @@ -65,15 +66,25 @@ protected void ascon_aeadinit() protected void processFinalAadBlock() { - Arrays.fill(m_buf, m_bufPos, m_buf.length, (byte) 0); - if (m_bufPos >= 8) // ASCON_AEAD_RATE == 16 is implied + if (m_aadPos == BlockSize) { - x0 ^= Pack.littleEndianToLong(m_buf, 0); - x1 ^= Pack.littleEndianToLong(m_buf, 8) ^ pad(m_bufPos); + x0 ^= loadBytes(m_aad, 0); + if (BlockSize == 16) + { + x1 ^= loadBytes(m_aad, 8 ); + } + m_aadPos -= BlockSize; + p(nr); + } + Arrays.fill(m_aad, m_aadPos, AADBufferSize, (byte)0); + if (m_aadPos >= 8) // ASCON_AEAD_RATE == 16 is implied + { + x0 ^= Pack.littleEndianToLong(m_aad, 0); + x1 ^= Pack.littleEndianToLong(m_aad, 8) ^ pad(m_aadPos); } else { - x0 ^= Pack.littleEndianToLong(m_buf, 0) ^ pad(m_bufPos); + x0 ^= Pack.littleEndianToLong(m_aad, 0) ^ pad(m_aadPos); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index 37832c9af8..4d1845164c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -3,29 +3,12 @@ import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.OutputLengthException; -import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Longs; abstract class AsconBaseEngine - extends AEADBaseEngine + extends AEADBufferBaseEngine { - protected enum State - { - Uninitialized, - EncInit, - EncAad, - EncData, - EncFinal, - DecInit, - DecAad, - DecData, - DecFinal, - } - - - protected State m_state = State.Uninitialized; protected int nr; - protected int ASCON_AEAD_RATE; protected long K0; protected long K1; protected long N0; @@ -37,8 +20,6 @@ protected enum State protected long x3; protected long x4; protected int m_bufferSizeDecrypt; - protected byte[] m_buf; - protected int m_bufPos = 0; protected long dsep; //domain separation protected abstract long pad(int i); @@ -85,50 +66,7 @@ protected void p(int nr) protected abstract void ascon_aeadinit(); - protected void checkAAD() - { - switch (m_state) - { - case DecInit: - m_state = State.DecAad; - break; - case EncInit: - m_state = State.EncAad; - break; - case DecAad: - case EncAad: - break; - case EncFinal: - throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption"); - default: - throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); - } - } - - protected boolean checkData() - { - switch (m_state) - { - case DecInit: - case DecAad: - finishAAD(State.DecData); - return false; - case EncInit: - case EncAad: - finishAAD(State.EncData); - return true; - case DecData: - return false; - case EncData: - return true; - case EncFinal: - throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption"); - default: - throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); - } - } - - private void finishAAD(State nextState) + protected void finishAAD(State nextState) { // State indicates whether we ever received AAD switch (m_state) @@ -143,7 +81,7 @@ private void finishAAD(State nextState) } // domain separation x4 ^= dsep; - m_bufPos = 0; + m_aadPos = 0; m_state = nextState; } @@ -156,17 +94,42 @@ private void finishAAD(State nextState) protected void processBufferAAD(byte[] buffer, int inOff) { x0 ^= loadBytes(buffer, inOff); - if (ASCON_AEAD_RATE == 16) + if (BlockSize == 16) { x1 ^= loadBytes(buffer, 8 + inOff); } p(nr); } + @Override + protected void processFinalBlock(byte[] output, int outOff) + { + + } + + @Override + protected void processFinalAAD() + { + processFinalAadBlock(); + p(nr); + } + + @Override + protected void processBuffer(byte[] input, int inOff, byte[] output, int outOff) + { + if (forEncryption) + { + processBufferEncrypt(input, inOff, output, outOff); + } + else + { + processBufferDecrypt(input, inOff, output, outOff); + } + } protected void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { - if (outOff + ASCON_AEAD_RATE > output.length) + if (outOff + BlockSize > output.length) { throw new OutputLengthException("output buffer too short"); } @@ -174,7 +137,7 @@ protected void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, in setBytes(x0 ^ t0, output, outOff); x0 = t0; - if (ASCON_AEAD_RATE == 16) + if (BlockSize == 16) { long t1 = loadBytes(buffer, bufOff + 8); setBytes(x1 ^ t1, output, outOff + 8); @@ -185,14 +148,14 @@ protected void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, in protected void processBufferEncrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { - if (outOff + ASCON_AEAD_RATE > output.length) + if (outOff + BlockSize > output.length) { throw new OutputLengthException("output buffer too short"); } x0 ^= loadBytes(buffer, bufOff); setBytes(x0, output, outOff); - if (ASCON_AEAD_RATE == 16) + if (BlockSize == 16) { x1 ^= loadBytes(buffer, bufOff + 8); setBytes(x1, output, outOff + 8); @@ -200,143 +163,6 @@ protected void processBufferEncrypt(byte[] buffer, int bufOff, byte[] output, in p(nr); } - public void processAADByte(byte in) - { - checkAAD(); - m_buf[m_bufPos] = in; - if (++m_bufPos == ASCON_AEAD_RATE) - { - processBufferAAD(m_buf, 0); - m_bufPos = 0; - } - } - - public void processAADBytes(byte[] inBytes, int inOff, int len) - { - if ((inOff + len) > inBytes.length) - { - throw new DataLengthException("input buffer too short"); - } - // Don't enter AAD state until we actually get input - if (len <= 0) - { - return; - } - checkAAD(); - if (m_bufPos > 0) - { - int available = ASCON_AEAD_RATE - m_bufPos; - if (len < available) - { - System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return; - } - System.arraycopy(inBytes, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - processBufferAAD(m_buf, 0); - //m_bufPos = 0; - } - while (len >= ASCON_AEAD_RATE) - { - processBufferAAD(inBytes, inOff); - inOff += ASCON_AEAD_RATE; - len -= ASCON_AEAD_RATE; - } - System.arraycopy(inBytes, inOff, m_buf, 0, len); - m_bufPos = len; - } - - public int processBytes(byte[] inBytes, int inOff, int len, byte[] outBytes, int outOff) - throws DataLengthException - { - if ((inOff + len) > inBytes.length) - { - throw new DataLengthException("input buffer too short"); - } - boolean forEncryption = checkData(); - int resultLength = 0; - - if (forEncryption) - { - if (m_bufPos > 0) - { - int available = ASCON_AEAD_RATE - m_bufPos; - if (len < available) - { - System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } - - System.arraycopy(inBytes, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - - processBufferEncrypt(m_buf, 0, outBytes, outOff); - resultLength = ASCON_AEAD_RATE; - //m_bufPos = 0; - } - - while (len >= ASCON_AEAD_RATE) - { - processBufferEncrypt(inBytes, inOff, outBytes, outOff + resultLength); - inOff += ASCON_AEAD_RATE; - len -= ASCON_AEAD_RATE; - resultLength += ASCON_AEAD_RATE; - } - } - else - { - int available = m_bufferSizeDecrypt - m_bufPos; - if (len < available) - { - System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } - - // NOTE: Need 'while' here because ASCON_AEAD_RATE < CRYPTO_ABYTES in some parameter sets - while (m_bufPos >= ASCON_AEAD_RATE) - { - processBufferDecrypt(m_buf, 0, outBytes, outOff + resultLength); - m_bufPos -= ASCON_AEAD_RATE; - System.arraycopy(m_buf, ASCON_AEAD_RATE, m_buf, 0, m_bufPos); - resultLength += ASCON_AEAD_RATE; - - available += ASCON_AEAD_RATE; - if (len < available) - { - System.arraycopy(inBytes, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return resultLength; - } - } - - available = ASCON_AEAD_RATE - m_bufPos; - System.arraycopy(inBytes, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - processBufferDecrypt(m_buf, 0, outBytes, outOff + resultLength); - resultLength += ASCON_AEAD_RATE; - //m_bufPos = 0; - - while (len >= m_bufferSizeDecrypt) - { - processBufferDecrypt(inBytes, inOff, outBytes, outOff + resultLength); - inOff += ASCON_AEAD_RATE; - len -= ASCON_AEAD_RATE; - resultLength += ASCON_AEAD_RATE; - } - } - - System.arraycopy(inBytes, inOff, m_buf, 0, len); - m_bufPos = len; - - return resultLength; - } - public int doFinal(byte[] outBytes, int outOff) throws IllegalStateException, InvalidCipherTextException, DataLengthException { @@ -349,6 +175,12 @@ public int doFinal(byte[] outBytes, int outOff) { throw new OutputLengthException("output buffer too short"); } + if (m_bufPos == BlockSize) + { + processBufferEncrypt(m_buf, 0, outBytes, outOff); + m_bufPos -= BlockSize; + outOff += BlockSize; + } processFinalEncrypt(m_buf, m_bufPos, outBytes, outOff); mac = new byte[MAC_SIZE]; setBytes(x3, mac, 0); @@ -368,9 +200,15 @@ public int doFinal(byte[] outBytes, int outOff) { throw new OutputLengthException("output buffer too short"); } + if (m_bufPos == BlockSize) + { + processBufferDecrypt(m_buf, 0, outBytes, outOff); + m_bufPos -= BlockSize; + outOff += BlockSize; + } processFinalDecrypt(m_buf, m_bufPos, outBytes, outOff); - x3 ^= loadBytes(m_buf, m_bufPos); - x4 ^= loadBytes(m_buf, m_bufPos + 8); + x3 ^= loadBytes(m_buf, resultLength); + x4 ^= loadBytes(m_buf, resultLength + 8); if ((x3 | x4) != 0L) { throw new InvalidCipherTextException("mac check in " + getAlgorithmName() + " failed"); @@ -380,73 +218,9 @@ public int doFinal(byte[] outBytes, int outOff) return resultLength; } - public int getUpdateOutputSize(int len) - { - int total = Math.max(0, len); - switch (m_state) - { - case DecInit: - case DecAad: - total = Math.max(0, total - MAC_SIZE); - break; - case DecData: - case DecFinal: - total = Math.max(0, total + m_bufPos - MAC_SIZE); - break; - case EncData: - case EncFinal: - total += m_bufPos; - break; - default: - break; - } - return total - total % ASCON_AEAD_RATE; - } - - public int getOutputSize(int len) - { - int total = Math.max(0, len); - - switch (m_state) - { - case DecInit: - case DecAad: - return Math.max(0, total - MAC_SIZE); - case DecData: - case DecFinal: - return Math.max(0, total + m_bufPos - MAC_SIZE); - case EncData: - case EncFinal: - return total + m_bufPos + MAC_SIZE; - default: - return total + MAC_SIZE; - } - } - - protected void reset(boolean clearMac) { - Arrays.clear(m_buf); - m_bufPos = 0; - - switch (m_state) - { - case DecInit: - case EncInit: - break; - case DecAad: - case DecData: - case DecFinal: - m_state = State.DecInit; - break; - case EncAad: - case EncData: - case EncFinal: - m_state = State.EncFinal; - return; - default: - throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); - } + bufferReset(); ascon_aeadinit(); super.reset(clearMac); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java index 17fb2a6a73..146b5cc1e0 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java @@ -1,6 +1,5 @@ package org.bouncycastle.crypto.engines; -import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.util.Pack; /** @@ -42,28 +41,30 @@ public AsconEngine(AsconParameters asconParameters) { case ascon80pq: KEY_SIZE = 20; - ASCON_AEAD_RATE = 8; + BlockSize = 8; ASCON_IV = 0xa0400c0600000000L; algorithmName = "Ascon-80pq AEAD"; break; case ascon128a: KEY_SIZE = 16; - ASCON_AEAD_RATE = 16; + BlockSize = 16; ASCON_IV = 0x80800c0800000000L; algorithmName = "Ascon-128a AEAD"; break; case ascon128: KEY_SIZE = 16; - ASCON_AEAD_RATE = 8; + BlockSize = 8; ASCON_IV = 0x80400c0600000000L; algorithmName = "Ascon-128 AEAD"; break; default: throw new IllegalArgumentException("invalid parameter setting for ASCON AEAD"); } - nr = (ASCON_AEAD_RATE == 8) ? 6 : 8; - m_bufferSizeDecrypt = ASCON_AEAD_RATE + MAC_SIZE; + nr = (BlockSize == 8) ? 6 : 8; + m_bufferSizeDecrypt = BlockSize + MAC_SIZE; m_buf = new byte[m_bufferSizeDecrypt]; + AADBufferSize = BlockSize; + m_aad = new byte[BlockSize]; dsep = 1L; } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java b/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java index aa11d266f3..c88a57d0ff 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java @@ -44,9 +44,9 @@ public String getName() public void performTest() throws Exception { + testVectorsEngine_asconaead128(); testVectorsDigest_AsconHash256(); testVectorsXof_AsconXof128(); - testVectorsEngine_asconaead128(); testBufferingEngine_asconaead128(); testBufferingEngine_ascon128(); @@ -572,7 +572,7 @@ private void implTestBufferingEngine(CreateEngine operator) byte[] output = new byte[ciphertextLength]; // Encryption - for (int split = 1; split < plaintextLength; ++split) + for (int split = 16; split < plaintextLength; ++split) { AEADCipher ascon = operator.createEngine(); initEngine(ascon, true); @@ -1111,7 +1111,7 @@ private void implTestVectorsEngine(AEADCipher ascon, String path, String filenam if (a < 0) { int count = Integer.parseInt(map.get("Count")); -// if (count != 34) +// if (count != 529) // { // continue; // } @@ -1156,7 +1156,7 @@ private void implTestVectorsEngine(AEADCipher ascon, String path, String filenam mismatch("Reccover Keystream " + map.get("Count"), (String)map.get("PT"), rv); } } - //System.out.println("pass "+ count); + System.out.println("pass "+ count); map.clear(); } else From ed2efcc7a6e998d7254b0ca0765f24b3bbb2ca55 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 14 Jan 2025 12:22:32 +1030 Subject: [PATCH 003/890] Separate AEADBufferBaseEngine.processAADByte, processAADBytes, and processBytes into two types. --- .../crypto/digests/XoodyakDigest.java | 3 - .../crypto/engines/AEADBufferBaseEngine.java | 428 +++++++++++++----- .../crypto/engines/ISAPEngine.java | 71 +-- .../crypto/engines/PhotonBeetleEngine.java | 1 + .../crypto/engines/SparkleEngine.java | 1 + .../crypto/engines/XoodyakEngine.java | 1 + .../bouncycastle/crypto/test/ISAPTest.java | 11 +- .../bouncycastle/crypto/test/SparkleTest.java | 8 +- 8 files changed, 347 insertions(+), 177 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java index d771a38738..f48652c149 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java @@ -27,9 +27,6 @@ public class XoodyakDigest private final int Rhash = 16; private final int PhaseDown = 1; private final int PhaseUp = 2; -// private final int NLANES = 12; -// private final int NROWS = 3; -// private final int NCOLUMS = 4; private final int MAXROUNDS = 12; private final int TAGLEN = 16; private final int[] RC = {0x00000058, 0x00000038, 0x000003C0, 0x000000D0, 0x00000120, 0x00000014, 0x00000060, diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index ed2e40ec19..e74ac1e4e2 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -8,6 +8,12 @@ abstract class AEADBufferBaseEngine extends AEADBaseEngine { + protected enum ProcessingBufferType + { + Buffered, + Immediate + } + protected enum State { Uninitialized, @@ -31,177 +37,369 @@ protected enum State protected int BlockSize; protected State m_state = State.Uninitialized; - @Override - public void processAADByte(byte input) + protected AADProcessingBuffer processor; + + protected AEADBufferBaseEngine(ProcessingBufferType type) { - checkAAD(); - if (m_aadPos == AADBufferSize) + switch (type) { - processBufferAAD(m_aad, 0); - m_aadPos = 0; + case Buffered: + processor = new BufferedAADProcessor(); + break; + case Immediate: + processor = new ImmediateAADProcessor(); + break; } - m_aad[m_aadPos++] = input; } - @Override - public void processAADBytes(byte[] input, int inOff, int len) + private interface AADProcessingBuffer { - if ((inOff + len) > input.length) - { - throw new DataLengthException("input buffer too short"); - } - // Don't enter AAD state until we actually get input - if (len <= 0) + void processAADByte(byte input); + + void processAADBytes(byte[] input, int inOff, int len); + + int processBytes(boolean forEncryption, byte[] input, int inOff, int len, byte[] output, int outOff); + } + + private class BufferedAADProcessor + implements AADProcessingBuffer + { + public void processAADByte(byte input) { - return; + if (m_aadPos == AADBufferSize) + { + processBufferAAD(m_aad, 0); + m_aadPos = 0; + } + m_aad[m_aadPos++] = input; } - checkAAD(); - if (m_aadPos > 0) + @Override + public void processAADBytes(byte[] input, int inOff, int len) { - int available = AADBufferSize - m_aadPos; - if (len <= available) + if (m_aadPos > 0) { - System.arraycopy(input, inOff, m_aad, m_aadPos, len); - m_aadPos += len; - return; - } + int available = AADBufferSize - m_aadPos; + if (len <= available) + { + System.arraycopy(input, inOff, m_aad, m_aadPos, len); + m_aadPos += len; + return; + } + + System.arraycopy(input, inOff, m_aad, m_aadPos, available); + inOff += available; + len -= available; - System.arraycopy(input, inOff, m_aad, m_aadPos, available); - inOff += available; - len -= available; + processBufferAAD(m_aad, 0); + m_aadPos = 0; + } + while (len > AADBufferSize) + { + processBufferAAD(input, inOff); + inOff += AADBufferSize; + len -= AADBufferSize; + } + System.arraycopy(input, inOff, m_aad, m_aadPos, len); + m_aadPos += len; - processBufferAAD(m_aad, 0); - m_aadPos = 0; - } - while (len > AADBufferSize) - { - processBufferAAD(input, inOff); - inOff += AADBufferSize; - len -= AADBufferSize; } - System.arraycopy(input, inOff, m_aad, m_aadPos, len); - m_aadPos += len; - } - @Override - public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - throws DataLengthException - { - if (inOff + len > input.length) + @Override + public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, byte[] output, int outOff) { - throw new DataLengthException("input buffer too short"); - } + int resultLength = 0; - boolean forEncryption = checkData(); + if (forEncryption) + { + if (m_bufPos > 0) + { + int available = BlockSize - m_bufPos; + if (len <= available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return 0; + } - int resultLength = 0; + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; - if (forEncryption) - { - if (m_bufPos > 0) + validateAndProcessBuffer(m_buf, 0, output, outOff); + resultLength = BlockSize; + //m_bufPos = 0; + } + + while (len > BlockSize) + { + validateAndProcessBuffer(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } + } + else { - int available = BlockSize - m_bufPos; + int available = BlockSize + MAC_SIZE - m_bufPos; if (len <= available) { System.arraycopy(input, inOff, m_buf, m_bufPos, len); m_bufPos += len; return 0; } + if (BlockSize >= MAC_SIZE) + { + if (m_bufPos > BlockSize) + { + validateAndProcessBuffer(m_buf, 0, output, outOff); + m_bufPos -= BlockSize; + System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); + resultLength = BlockSize; + + available += BlockSize; + if (len <= available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return resultLength; + } + } - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - - validateAndProcessBuffer(m_buf, 0, output, outOff); - resultLength = BlockSize; - //m_bufPos = 0; + available = BlockSize - m_bufPos; + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; + validateAndProcessBuffer(m_buf, 0, output, outOff + resultLength); + resultLength += BlockSize; + //m_bufPos = 0; + } + else + { + while (m_bufPos > BlockSize && len + m_bufPos > BlockSize + MAC_SIZE) + { + validateAndProcessBuffer(m_buf, resultLength, output, outOff + resultLength); + m_bufPos -= BlockSize; + resultLength += BlockSize; + } + if (m_bufPos != 0) + { + System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); + if (m_bufPos + len > BlockSize + MAC_SIZE) + { + available = Math.max(BlockSize - m_bufPos, 0); + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + validateAndProcessBuffer(m_buf, 0, output, outOff + resultLength); + resultLength += BlockSize; + len -= available; + } + else + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return resultLength; + } + } + } + while (len > m_buf.length) + { + validateAndProcessBuffer(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } } + System.arraycopy(input, inOff, m_buf, 0, len); + m_bufPos = len; + return resultLength; + } + } - while (len > BlockSize) + private class ImmediateAADProcessor + implements AADProcessingBuffer + { + public void processAADByte(byte input) + { + m_aad[m_aadPos++] = input; + if (m_aadPos == AADBufferSize) { - validateAndProcessBuffer(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; + processBufferAAD(m_aad, 0); + m_aadPos = 0; } } - else + + @Override + public void processAADBytes(byte[] input, int inOff, int len) { - int available = BlockSize + MAC_SIZE - m_bufPos; - if (len <= available) + if (m_aadPos > 0) { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; + int available = AADBufferSize - m_aadPos; + if (len < available) + { + System.arraycopy(input, inOff, m_aad, m_aadPos, len); + m_aadPos += len; + return; + } + + System.arraycopy(input, inOff, m_aad, m_aadPos, available); + inOff += available; + len -= available; + + processBufferAAD(m_aad, 0); + m_aadPos = 0; } - if (BlockSize >= MAC_SIZE) + while (len >= AADBufferSize) { - if (m_bufPos > BlockSize) - { - validateAndProcessBuffer(m_buf, 0, output, outOff); - m_bufPos -= BlockSize; - System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); - resultLength = BlockSize; + processBufferAAD(input, inOff); + inOff += AADBufferSize; + len -= AADBufferSize; + } + System.arraycopy(input, inOff, m_aad, m_aadPos, len); + m_aadPos += len; + } - available += BlockSize; - if (len <= available) + @Override + public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, byte[] output, int outOff) + { + int resultLength = 0; + + if (forEncryption) + { + if (m_bufPos > 0) + { + int available = BlockSize - m_bufPos; + if (len < available) { System.arraycopy(input, inOff, m_buf, m_bufPos, len); m_bufPos += len; - return resultLength; + return 0; } + + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; + + validateAndProcessBuffer(m_buf, 0, output, outOff); + resultLength = BlockSize; + //m_bufPos = 0; } - available = BlockSize - m_bufPos; - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - validateAndProcessBuffer(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - //m_bufPos = 0; + while (len >= BlockSize) + { + validateAndProcessBuffer(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } } else { - while (m_bufPos > BlockSize && len + m_bufPos > BlockSize + MAC_SIZE) + int available = BlockSize + MAC_SIZE - m_bufPos; + if (len < available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return 0; + } + if (BlockSize >= MAC_SIZE) { - validateAndProcessBuffer(m_buf, resultLength, output, outOff + resultLength); - m_bufPos -= BlockSize; + if (m_bufPos >= BlockSize) + { + validateAndProcessBuffer(m_buf, 0, output, outOff); + m_bufPos -= BlockSize; + System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); + resultLength = BlockSize; + + available += BlockSize; + if (len <= available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return resultLength; + } + } + + available = BlockSize - m_bufPos; + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; + validateAndProcessBuffer(m_buf, 0, output, outOff + resultLength); resultLength += BlockSize; + //m_bufPos = 0; } - if (m_bufPos != 0) + else { - System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); - if (m_bufPos + len > BlockSize + MAC_SIZE) + while (m_bufPos >= BlockSize && len + m_bufPos >= BlockSize + MAC_SIZE) { - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - validateAndProcessBuffer(m_buf, 0, output, outOff + resultLength); + validateAndProcessBuffer(m_buf, resultLength, output, outOff + resultLength); + m_bufPos -= BlockSize; resultLength += BlockSize; - len -= available; } - else + if (m_bufPos != 0) { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return resultLength; + System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); + if (m_bufPos + len >= BlockSize + MAC_SIZE) + { + available = Math.max(BlockSize - m_bufPos, 0); + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + validateAndProcessBuffer(m_buf, 0, output, outOff + resultLength); + resultLength += BlockSize; + len -= available; + } + else + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return resultLength; + } } } + while (len >= m_buf.length) + { + validateAndProcessBuffer(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } } - while (len > BlockSize + MAC_SIZE) - { - validateAndProcessBuffer(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; - } + System.arraycopy(input, inOff, m_buf, 0, len); + m_bufPos = len; + return resultLength; } + } - System.arraycopy(input, inOff, m_buf, 0, len); - m_bufPos = len; + @Override + public void processAADByte(byte input) + { + checkAAD(); + processor.processAADByte(input); + } - return resultLength; + @Override + public void processAADBytes(byte[] input, int inOff, int len) + { + ensureSufficientInputBuffer(input, inOff, len); + // Don't enter AAD state until we actually get input + if (len <= 0) + { + return; + } + + checkAAD(); + processor.processAADBytes(input, inOff, len); + } + + @Override + public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + throws DataLengthException + { + ensureSufficientInputBuffer(input, inOff, len); + + boolean forEncryption = checkData(); + + return processor.processBytes(forEncryption, input, inOff, len, output, outOff); } @Override @@ -393,6 +591,14 @@ protected void validateAndProcessBuffer(byte[] input, int inOff, byte[] output, processBuffer(input, inOff, output, outOff); } + protected void ensureSufficientInputBuffer(byte[] input, int inOff, int len) + { + if (inOff + len > input.length) + { + throw new DataLengthException("input buffer too short"); + } + } + protected abstract void processFinalBlock(byte[] output, int outOff); protected abstract void processBufferAAD(byte[] input, int inOff); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index 52d2f841a3..9a53975eaa 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -24,6 +24,7 @@ public enum IsapType public ISAPEngine(IsapType isapType) { + super(ProcessingBufferType.Immediate); KEY_SIZE = 16; IV_SIZE = 16; MAC_SIZE = 16; @@ -131,17 +132,9 @@ public void absorbMacBlock(byte[] input, int inOff) public void absorbFinalAADBlock() { - if (m_aadPos == AADBufferSize) + for (int i = 0; i < m_aadPos; ++i) { - absorbMacBlock(m_aad, 0); - m_aadPos = 0; - } - else - { - for (int i = 0; i < m_aadPos; ++i) - { - x0 ^= (m_aad[i] & 0xFFL) << ((7 - i) << 3); - } + x0 ^= (m_aad[i] & 0xFFL) << ((7 - i) << 3); } x0 ^= 0x80L << ((7 - m_aadPos) << 3); P12(); @@ -150,18 +143,12 @@ public void absorbFinalAADBlock() public void processMACFinal(byte[] input, int inOff, int len, byte[] tag) { - if (len == BlockSize) - { - absorbMacBlock(input, inOff); - len = 0; - } - else + + for (int i = 0; i < len; ++i) { - for (int i = 0; i < len; ++i) - { - x0 ^= (input[inOff++] & 0xFFL) << ((7 - i) << 3); - } + x0 ^= (input[inOff++] & 0xFFL) << ((7 - i) << 3); } + x0 ^= 0x80L << ((7 - len) << 3); P12(); // Derive K* @@ -206,19 +193,12 @@ public void processEncBlock(byte[] input, int inOff, byte[] output, int outOff) public void processEncFinalBlock(byte[] output, int outOff) { - if (m_bufPos == BlockSize) + /* Encrypt final m block */ + byte[] xo = Pack.longToLittleEndian(x0); + int mlen = m_bufPos; + while (mlen > 0) { - processEncBlock(m_buf, 0, output, outOff); - } - else - { - /* Encrypt final m block */ - byte[] xo = Pack.longToLittleEndian(x0); - int mlen = m_bufPos; - while (mlen > 0) - { - output[outOff + mlen - 1] = (byte)(xo[BlockSize - mlen] ^ m_buf[--mlen]); - } + output[outOff + mlen - 1] = (byte)(xo[BlockSize - mlen] ^ m_buf[--mlen]); } } @@ -407,17 +387,9 @@ public void absorbMacBlock(byte[] input, int inOff) public void absorbFinalAADBlock() { - if (m_aadPos == AADBufferSize) - { - absorbMacBlock(m_aad, 0); - m_aadPos = 0; - } - else + for (int i = 0; i < m_aadPos; i++) { - for (int i = 0; i < m_aadPos; i++) - { - SX[i >> 1] ^= (m_aad[i] & 0xFF) << ((i & 1) << 3); - } + SX[i >> 1] ^= (m_aad[i] & 0xFF) << ((i & 1) << 3); } SX[m_aadPos >> 1] ^= 0x80 << ((m_aadPos & 1) << 3); PermuteRoundsHX(SX, E, C); @@ -448,19 +420,12 @@ public void isap_rk(short[] iv16, byte[] y, int ylen, short[] out16, int outlen, public void processMACFinal(byte[] input, int inOff, int len, byte[] tag) { - if (len == BlockSize) + // Absorb C final block + for (int i = 0; i < len; i++) { - absorbMacBlock(input, inOff); - len = 0; - } - else - { - // Absorb C final block - for (int i = 0; i < len; i++) - { - SX[i >> 1] ^= (input[inOff++] & 0xFF) << ((i & 1) << 3); - } + SX[i >> 1] ^= (input[inOff++] & 0xFF) << ((i & 1) << 3); } + SX[len >> 1] ^= 0x80 << ((len & 1) << 3); PermuteRoundsHX(SX, E, C); // Derive K* diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 6a43f59890..75ad35aad8 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -55,6 +55,7 @@ public enum PhotonBeetleParameters public PhotonBeetleEngine(PhotonBeetleParameters pbp) { + super(ProcessingBufferType.Buffered); KEY_SIZE = 16; IV_SIZE = 16; MAC_SIZE = 16; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java index 3557aac603..b4bd266a22 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java @@ -43,6 +43,7 @@ public enum SparkleParameters public SparkleEngine(SparkleParameters sparkleParameters) { + super(ProcessingBufferType.Buffered); int SPARKLE_STATE; int SCHWAEMM_TAG_LEN; int SPARKLE_CAPACITY; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index 08dfa3f1e5..fb245b75e3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -34,6 +34,7 @@ enum MODE public XoodyakEngine() { + super(ProcessingBufferType.Buffered); algorithmName = "Xoodyak AEAD"; KEY_SIZE = 16; IV_SIZE = 16; diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java index 57eea8f2e4..2fb9c8e318 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java @@ -33,7 +33,6 @@ public String getName() public void performTest() throws Exception { - CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new ISAPEngine(IsapType.ISAP_A_128A)); testVectors("isapa128av20", IsapType.ISAP_A_128A); testVectors("isapa128v20", IsapType.ISAP_A_128); testVectors("isapk128av20", IsapType.ISAP_K_128A); @@ -95,7 +94,7 @@ public AEADCipher createInstance() CipherTest.checkAEADCipherOutputSize(this, 16, 16, 8, 16, new ISAPEngine(IsapType.ISAP_A_128)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new ISAPEngine(IsapType.ISAP_K_128A)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new ISAPEngine(IsapType.ISAP_K_128)); - + CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new ISAPEngine(IsapType.ISAP_A_128A)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new ISAPEngine(IsapType.ISAP_A_128)); } @@ -114,10 +113,10 @@ private void testVectors(String filename, IsapType isapType) int a = line.indexOf('='); if (a < 0) { -// if (!map.get("Count").equals("19")) -// { -// continue; -// } + if (!map.get("Count").equals("265")) + { + continue; + } byte[] key = Hex.decode(map.get("Key")); byte[] nonce = Hex.decode(map.get("Nonce")); byte[] ad = Hex.decode(map.get("AD")); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java index 6dda9f40eb..2812befabd 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java @@ -380,10 +380,10 @@ private void implTestVectorsEngine(SparkleEngine.SparkleParameters pbp, String f byte[] ad = Hex.decode(map.get("AD")); byte[] pt = Hex.decode(map.get("PT")); byte[] ct = Hex.decode(map.get("CT")); -// if (!map.get("Count").equals("17")) -// { -// continue; -// } + if (!map.get("Count").equals("17")) + { + continue; + } CipherParameters parameters = new ParametersWithIV(new KeyParameter(key), nonce); // Encrypt From 092ab30cdeaf34a37f6584ea812154d3a0b50696 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 14 Jan 2025 13:50:11 +1030 Subject: [PATCH 004/890] AsconBaseEngine is inherited from AEADBufferBaseEngine now. --- .../crypto/engines/AEADBufferBaseEngine.java | 75 +++++++++++++------ .../crypto/engines/AsconBaseEngine.java | 5 ++ .../crypto/engines/AsconEngine.java | 12 +-- 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 51eaf38dc7..89356702cd 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -59,6 +59,8 @@ private interface AADProcessingBuffer void processAADBytes(byte[] input, int inOff, int len); int processBytes(boolean forEncryption, byte[] input, int inOff, int len, byte[] output, int outOff); + + int getUpdateOutputSize(int len); } private class BufferedAADProcessor @@ -214,6 +216,32 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, m_bufPos = len; return resultLength; } + + @Override + public int getUpdateOutputSize(int len) + { + // The -1 is to account for the lazy processing of a full buffer + int total = Math.max(0, len) - 1; + + switch (m_state) + { + case DecInit: + case DecAad: + total = Math.max(0, total - MAC_SIZE); + break; + case DecData: + case DecFinal: + total = Math.max(0, total + m_bufPos - MAC_SIZE); + break; + case EncData: + case EncFinal: + total = Math.max(0, total + m_bufPos); + break; + default: + break; + } + return total - total % BlockSize; + } } private class ImmediateAADProcessor @@ -368,6 +396,31 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, m_bufPos = len; return resultLength; } + + public int getUpdateOutputSize(int len) + { + // The -1 is to account for the lazy processing of a full buffer + int total = Math.max(0, len); + + switch (m_state) + { + case DecInit: + case DecAad: + total = Math.max(0, total - MAC_SIZE); + break; + case DecData: + case DecFinal: + total = Math.max(0, total + m_bufPos - MAC_SIZE); + break; + case EncData: + case EncFinal: + total = Math.max(0, total + m_bufPos); + break; + default: + break; + } + return total - total % BlockSize; + } } @Override @@ -451,27 +504,7 @@ public int getBlockSize() public int getUpdateOutputSize(int len) { - // The -1 is to account for the lazy processing of a full buffer - int total = Math.max(0, len) - 1; - - switch (m_state) - { - case DecInit: - case DecAad: - total = Math.max(0, total - MAC_SIZE); - break; - case DecData: - case DecFinal: - total = Math.max(0, total + m_bufPos - MAC_SIZE); - break; - case EncData: - case EncFinal: - total = Math.max(0, total + m_bufPos); - break; - default: - break; - } - return total - total % BlockSize; + return processor.getUpdateOutputSize(len); } public int getOutputSize(int len) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index 4d1845164c..443b57fc0d 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -28,6 +28,11 @@ abstract class AsconBaseEngine protected abstract void setBytes(long n, byte[] bs, int off); + protected AsconBaseEngine() + { + super(ProcessingBufferType.Immediate); + } + private void round(long C) { long t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java index 146b5cc1e0..8417af8322 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java @@ -107,15 +107,15 @@ protected void ascon_aeadinit() protected void processFinalAadBlock() { - m_buf[m_bufPos] = (byte)0x80; - if (m_bufPos >= 8) // ASCON_AEAD_RATE == 16 is implied + m_aad[m_aadPos] = (byte)0x80; + if (m_aadPos >= 8) // ASCON_AEAD_RATE == 16 is implied { - x0 ^= Pack.bigEndianToLong(m_buf, 0); - x1 ^= Pack.bigEndianToLong(m_buf, 8) & (-1L << (56 - ((m_bufPos - 8) << 3))); + x0 ^= Pack.bigEndianToLong(m_aad, 0); + x1 ^= Pack.bigEndianToLong(m_aad, 8) & (-1L << (56 - ((m_aadPos - 8) << 3))); } else { - x0 ^= Pack.bigEndianToLong(m_buf, 0) & (-1L << (56 - (m_bufPos << 3))); + x0 ^= Pack.bigEndianToLong(m_aad, 0) & (-1L << (56 - (m_aadPos << 3))); } } @@ -183,7 +183,7 @@ protected void processFinalEncrypt(byte[] input, int inLen, byte[] output, int o finishData(State.EncFinal); } - private void finishData(State nextState) + protected void finishData(State nextState) { switch (asconParameters) { From 165cb233208358438fd9c3c4e651270fff18e54a Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 14 Jan 2025 15:55:01 +1030 Subject: [PATCH 005/890] Separate processBuffer into processBufferEncrypt and processBufferDecrypt --- .../crypto/engines/AEADBufferBaseEngine.java | 45 ++++----- .../crypto/engines/AsconBaseEngine.java | 91 +++---------------- .../crypto/engines/ISAPEngine.java | 22 ++--- .../crypto/engines/PhotonBeetleEngine.java | 28 ++++-- .../crypto/engines/SparkleEngine.java | 18 +--- .../crypto/engines/XoodyakEngine.java | 52 +++++++---- 6 files changed, 102 insertions(+), 154 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 89356702cd..a10a7c9cdb 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -114,6 +114,7 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, if (forEncryption) { + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - 1) * BlockSize / BlockSize); if (m_bufPos > 0) { int available = BlockSize - m_bufPos; @@ -123,19 +124,18 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, m_bufPos += len; return 0; } - System.arraycopy(input, inOff, m_buf, m_bufPos, available); inOff += available; len -= available; - validateAndProcessBuffer(m_buf, 0, output, outOff); + processBufferEncrypt(m_buf, 0, output, outOff); resultLength = BlockSize; //m_bufPos = 0; } while (len > BlockSize) { - validateAndProcessBuffer(input, inOff, output, outOff + resultLength); + processBufferEncrypt(input, inOff, output, outOff + resultLength); inOff += BlockSize; len -= BlockSize; resultLength += BlockSize; @@ -150,11 +150,12 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, m_bufPos += len; return 0; } + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE - 1) * BlockSize / BlockSize); if (BlockSize >= MAC_SIZE) { if (m_bufPos > BlockSize) { - validateAndProcessBuffer(m_buf, 0, output, outOff); + processBufferDecrypt(m_buf, 0, output, outOff); m_bufPos -= BlockSize; System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); resultLength = BlockSize; @@ -172,7 +173,7 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, System.arraycopy(input, inOff, m_buf, m_bufPos, available); inOff += available; len -= available; - validateAndProcessBuffer(m_buf, 0, output, outOff + resultLength); + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); resultLength += BlockSize; //m_bufPos = 0; } @@ -180,7 +181,7 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, { while (m_bufPos > BlockSize && len + m_bufPos > BlockSize + MAC_SIZE) { - validateAndProcessBuffer(m_buf, resultLength, output, outOff + resultLength); + processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); m_bufPos -= BlockSize; resultLength += BlockSize; } @@ -192,7 +193,7 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, available = Math.max(BlockSize - m_bufPos, 0); System.arraycopy(input, inOff, m_buf, m_bufPos, available); inOff += available; - validateAndProcessBuffer(m_buf, 0, output, outOff + resultLength); + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); resultLength += BlockSize; len -= available; } @@ -206,7 +207,7 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, } while (len > m_buf.length) { - validateAndProcessBuffer(input, inOff, output, outOff + resultLength); + processBufferDecrypt(input, inOff, output, outOff + resultLength); inOff += BlockSize; len -= BlockSize; resultLength += BlockSize; @@ -294,6 +295,7 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, if (forEncryption) { + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos) * BlockSize / BlockSize); if (m_bufPos > 0) { int available = BlockSize - m_bufPos; @@ -308,14 +310,14 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, inOff += available; len -= available; - validateAndProcessBuffer(m_buf, 0, output, outOff); + processBufferEncrypt(m_buf, 0, output, outOff); resultLength = BlockSize; //m_bufPos = 0; } while (len >= BlockSize) { - validateAndProcessBuffer(input, inOff, output, outOff + resultLength); + processBufferEncrypt(input, inOff, output, outOff + resultLength); inOff += BlockSize; len -= BlockSize; resultLength += BlockSize; @@ -323,6 +325,7 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, } else { + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE) * BlockSize / BlockSize); int available = BlockSize + MAC_SIZE - m_bufPos; if (len < available) { @@ -334,13 +337,13 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, { if (m_bufPos >= BlockSize) { - validateAndProcessBuffer(m_buf, 0, output, outOff); + processBufferDecrypt(m_buf, 0, output, outOff); m_bufPos -= BlockSize; System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); resultLength = BlockSize; available += BlockSize; - if (len <= available) + if (len < available) { System.arraycopy(input, inOff, m_buf, m_bufPos, len); m_bufPos += len; @@ -352,7 +355,7 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, System.arraycopy(input, inOff, m_buf, m_bufPos, available); inOff += available; len -= available; - validateAndProcessBuffer(m_buf, 0, output, outOff + resultLength); + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); resultLength += BlockSize; //m_bufPos = 0; } @@ -360,7 +363,7 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, { while (m_bufPos >= BlockSize && len + m_bufPos >= BlockSize + MAC_SIZE) { - validateAndProcessBuffer(m_buf, resultLength, output, outOff + resultLength); + processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); m_bufPos -= BlockSize; resultLength += BlockSize; } @@ -372,7 +375,7 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, available = Math.max(BlockSize - m_bufPos, 0); System.arraycopy(input, inOff, m_buf, m_bufPos, available); inOff += available; - validateAndProcessBuffer(m_buf, 0, output, outOff + resultLength); + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); resultLength += BlockSize; len -= available; } @@ -386,7 +389,7 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, } while (len >= m_buf.length) { - validateAndProcessBuffer(input, inOff, output, outOff + resultLength); + processBufferDecrypt(input, inOff, output, outOff + resultLength); inOff += BlockSize; len -= BlockSize; resultLength += BlockSize; @@ -399,7 +402,6 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, public int getUpdateOutputSize(int len) { - // The -1 is to account for the lazy processing of a full buffer int total = Math.max(0, len); switch (m_state) @@ -615,13 +617,12 @@ protected void bufferReset() } } - protected void validateAndProcessBuffer(byte[] input, int inOff, byte[] output, int outOff) + protected void ensureSufficientOutputBuffer(byte[] output, int outOff, int len) { - if (outOff > output.length - BlockSize) + if (len >= BlockSize && outOff + len > output.length) { throw new OutputLengthException("output buffer too short"); } - processBuffer(input, inOff, output, outOff); } protected void ensureSufficientInputBuffer(byte[] input, int inOff, int len) @@ -638,5 +639,7 @@ protected void ensureSufficientInputBuffer(byte[] input, int inOff, int len) protected abstract void processFinalAAD(); - protected abstract void processBuffer(byte[] input, int inOff, byte[] output, int outOff); + protected abstract void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff); + + protected abstract void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index 443b57fc0d..3b588b3565 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -1,8 +1,5 @@ package org.bouncycastle.crypto.engines; -import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.InvalidCipherTextException; -import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.util.Longs; abstract class AsconBaseEngine @@ -109,7 +106,17 @@ protected void processBufferAAD(byte[] buffer, int inOff) @Override protected void processFinalBlock(byte[] output, int outOff) { - + if (forEncryption) + { + processFinalEncrypt(m_buf, m_bufPos, output, outOff); + } + else + { + processFinalDecrypt(m_buf, m_bufPos, output, outOff); + } + mac = new byte[MAC_SIZE]; + setBytes(x3, mac, 0); + setBytes(x4, mac, 8); } @Override @@ -119,25 +126,8 @@ protected void processFinalAAD() p(nr); } - @Override - protected void processBuffer(byte[] input, int inOff, byte[] output, int outOff) - { - if (forEncryption) - { - processBufferEncrypt(input, inOff, output, outOff); - } - else - { - processBufferDecrypt(input, inOff, output, outOff); - } - } - protected void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { - if (outOff + BlockSize > output.length) - { - throw new OutputLengthException("output buffer too short"); - } long t0 = loadBytes(buffer, bufOff); setBytes(x0 ^ t0, output, outOff); x0 = t0; @@ -153,10 +143,6 @@ protected void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, in protected void processBufferEncrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { - if (outOff + BlockSize > output.length) - { - throw new OutputLengthException("output buffer too short"); - } x0 ^= loadBytes(buffer, bufOff); setBytes(x0, output, outOff); @@ -168,61 +154,6 @@ protected void processBufferEncrypt(byte[] buffer, int bufOff, byte[] output, in p(nr); } - public int doFinal(byte[] outBytes, int outOff) - throws IllegalStateException, InvalidCipherTextException, DataLengthException - { - boolean forEncryption = checkData(); - int resultLength; - if (forEncryption) - { - resultLength = m_bufPos + MAC_SIZE; - if (outOff + resultLength > outBytes.length) - { - throw new OutputLengthException("output buffer too short"); - } - if (m_bufPos == BlockSize) - { - processBufferEncrypt(m_buf, 0, outBytes, outOff); - m_bufPos -= BlockSize; - outOff += BlockSize; - } - processFinalEncrypt(m_buf, m_bufPos, outBytes, outOff); - mac = new byte[MAC_SIZE]; - setBytes(x3, mac, 0); - setBytes(x4, mac, 8); - System.arraycopy(mac, 0, outBytes, outOff + m_bufPos, MAC_SIZE); - reset(false); - } - else - { - if (m_bufPos < MAC_SIZE) - { - throw new InvalidCipherTextException("data too short"); - } - m_bufPos -= MAC_SIZE; - resultLength = m_bufPos; - if (outOff + resultLength > outBytes.length) - { - throw new OutputLengthException("output buffer too short"); - } - if (m_bufPos == BlockSize) - { - processBufferDecrypt(m_buf, 0, outBytes, outOff); - m_bufPos -= BlockSize; - outOff += BlockSize; - } - processFinalDecrypt(m_buf, m_bufPos, outBytes, outOff); - x3 ^= loadBytes(m_buf, resultLength); - x4 ^= loadBytes(m_buf, resultLength + 8); - if ((x3 | x4) != 0L) - { - throw new InvalidCipherTextException("mac check in " + getAlgorithmName() + " failed"); - } - reset(true); - } - return resultLength; - } - protected void reset(boolean clearMac) { bufferReset(); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index 9a53975eaa..4f063ae8c4 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -143,12 +143,10 @@ public void absorbFinalAADBlock() public void processMACFinal(byte[] input, int inOff, int len, byte[] tag) { - for (int i = 0; i < len; ++i) { x0 ^= (input[inOff++] & 0xFFL) << ((7 - i) << 3); } - x0 ^= 0x80L << ((7 - len) << 3); P12(); // Derive K* @@ -816,19 +814,21 @@ protected void processFinalAAD() } } - protected void processBuffer(byte[] input, int inOff, byte[] output, int outOff) + protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { processFinalAAD(); ISAPAEAD.processEncBlock(input, inOff, output, outOff); ISAPAEAD.swapInternalState(); - if (forEncryption) - { - ISAPAEAD.absorbMacBlock(output, outOff); - } - else - { - ISAPAEAD.absorbMacBlock(input, inOff); - } + ISAPAEAD.absorbMacBlock(output, outOff); + ISAPAEAD.swapInternalState(); + } + + protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) + { + processFinalAAD(); + ISAPAEAD.processEncBlock(input, inOff, output, outOff); + ISAPAEAD.swapInternalState(); + ISAPAEAD.absorbMacBlock(input, inOff); ISAPAEAD.swapInternalState(); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 75ad35aad8..e340a83d0a 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -102,10 +102,18 @@ protected void processBufferAAD(byte[] input, int inOff) XOR(input, inOff, BlockSize); } - protected void processBuffer(byte[] input, int inOff, byte[] output, int outOff) + protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { PHOTON_Permutation(); rhoohr(output, outOff, input, inOff, BlockSize); + XOR(input, inOff, BlockSize); + } + + protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) + { + PHOTON_Permutation(); + rhoohr(output, outOff, input, inOff, BlockSize); + XOR(output, outOff, BlockSize); } @Override @@ -147,7 +155,15 @@ protected void processFinalBlock(byte[] output, int outOff) { PHOTON_Permutation(); rhoohr(output, outOff, m_buf, 0, bufferLen); - if(bufferLen < BlockSize) + if (forEncryption) + { + XOR(m_buf, 0, bufferLen); + } + else + { + XOR(output, outOff, bufferLen); + } + if (bufferLen < BlockSize) { state[bufferLen] ^= 0x01; // ozs } @@ -307,14 +323,6 @@ private void rhoohr(byte[] ciphertext, int outOff, byte[] plaintext, int inOff, { ciphertext[i + outOff] = (byte)(OuterState_part1_ROTR1[i - RATE_INBYTES_HALF] ^ plaintext[i++ + inOff]); } - if (forEncryption) - { - XOR(plaintext, inOff, DBlen_inbytes); - } - else - { - XOR(ciphertext, outOff, DBlen_inbytes); - } } private void XOR(byte[] in_right, int rOff, int iolen_inbytes) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java index b4bd266a22..f41214f2af 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java @@ -213,9 +213,8 @@ protected void processBufferAAD(byte[] buffer, int bufOff) sparkle_opt(state, SPARKLE_STEPS_SLIM); } - private void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int outOff) + protected void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { -// assert bufOff <= buffer.length - RATE_BYTES; for (int i = 0; i < RATE_WORDS / 2; ++i) { @@ -239,20 +238,7 @@ private void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int encrypted = true; } - @Override - protected void processBuffer(byte[] input, int inOff, byte[] output, int outOff) - { - if (forEncryption) - { - processBufferEncrypt(input, inOff, output, outOff); - } - else - { - processBufferDecrypt(input, inOff, output, outOff); - } - } - - private void processBufferEncrypt(byte[] buffer, int bufOff, byte[] output, int outOff) + protected void processBufferEncrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { // assert bufOff <= buffer.length - RATE_BYTES; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index fb245b75e3..678b7a4216 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -78,42 +78,58 @@ protected void processFinalAAD() } } - protected void processBuffer(byte[] input, int inOff, byte[] output, int outOff) + protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { processFinalAAD(); encrypt(input, inOff, BlockSize, output, outOff); } + protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) + { + processFinalAAD(); + decrypt(input, inOff, BlockSize, output, outOff); + } + private void encrypt(byte[] input, int inOff, int len, byte[] output, int outOff) { - int IOLen = len; int splitLen; byte[] P = new byte[BlockSize]; int Cu = encrypted ? 0 : 0x80; - while (IOLen != 0 || !encrypted) + while (len != 0 || !encrypted) { - splitLen = Math.min(IOLen, BlockSize); /* use Rkout instead of Rsqueeze, this function is only called in keyed mode */ - if (forEncryption) - { - System.arraycopy(input, inOff, P, 0, splitLen); - } + splitLen = Math.min(len, BlockSize); /* use Rkout instead of Rsqueeze, this function is only called in keyed mode */ + System.arraycopy(input, inOff, P, 0, splitLen); Up(null, 0, Cu); /* Up without extract */ /* Extract from Up and Add */ for (int i = 0; i < splitLen; i++) { output[outOff + i] = (byte)(input[inOff++] ^ state[i]); } - if (forEncryption) - { - Down(P, 0, splitLen, 0x00); - } - else + Down(P, 0, splitLen, 0x00); + Cu = 0x00; + outOff += splitLen; + len -= splitLen; + encrypted = true; + } + } + + private void decrypt(byte[] input, int inOff, int len, byte[] output, int outOff) + { + int splitLen; + int Cu = encrypted ? 0 : 0x80; + while (len != 0 || !encrypted) + { + splitLen = Math.min(len, BlockSize); /* use Rkout instead of Rsqueeze, this function is only called in keyed mode */ + Up(null, 0, Cu); /* Up without extract */ + /* Extract from Up and Add */ + for (int i = 0; i < splitLen; i++) { - Down(output, outOff, splitLen, 0x00); + output[outOff + i] = (byte)(input[inOff++] ^ state[i]); } + Down(output, outOff, splitLen, 0x00); Cu = 0x00; outOff += splitLen; - IOLen -= splitLen; + len -= splitLen; encrypted = true; } } @@ -125,8 +141,12 @@ protected void processFinalBlock(byte[] output, int outOff) if (forEncryption) { Arrays.fill(m_buf, m_bufPos, BlockSize, (byte)0); + encrypt(m_buf, 0, m_bufPos, output, outOff); + } + else + { + decrypt(m_buf, 0, m_bufPos, output, outOff); } - encrypt(m_buf, 0, m_bufPos, output, outOff); mac = new byte[MAC_SIZE]; Up(mac, MAC_SIZE, 0x40); } From a18dfebbca410cabeb0eedf5de684b3b4e187d0e Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 15 Jan 2025 14:50:57 +1030 Subject: [PATCH 006/890] ElephantEngine is inherited from AEADBufferBaseEngine --- .../crypto/engines/AEADBufferBaseEngine.java | 91 +++-- .../crypto/engines/ElephantEngine.java | 361 ++++++++++-------- .../bouncycastle/crypto/test/CipherTest.java | 2 +- .../crypto/test/ElephantTest.java | 17 +- 4 files changed, 269 insertions(+), 202 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index a10a7c9cdb..fc31fb2cca 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -18,12 +18,12 @@ protected enum State { Uninitialized, EncInit, - EncAad, - EncData, + EncAad, // can process AAD + EncData, // cannot process AAD EncFinal, DecInit, - DecAad, - DecData, + DecAad, // can process AAD + DecData, // cannot process AAD DecFinal, } @@ -153,29 +153,31 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE - 1) * BlockSize / BlockSize); if (BlockSize >= MAC_SIZE) { - if (m_bufPos > BlockSize) + if (m_bufPos > 0) { - processBufferDecrypt(m_buf, 0, output, outOff); - m_bufPos -= BlockSize; - System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); - resultLength = BlockSize; - - available += BlockSize; - if (len <= available) + if (m_bufPos > BlockSize) { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return resultLength; + processBufferDecrypt(m_buf, 0, output, outOff); + m_bufPos -= BlockSize; + System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); + resultLength = BlockSize; + + available += BlockSize; + if (len <= available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return resultLength; + } } - } - available = BlockSize - m_bufPos; - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - //m_bufPos = 0; + available = BlockSize - m_bufPos; + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); + resultLength += BlockSize; + } } else { @@ -335,28 +337,35 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, } if (BlockSize >= MAC_SIZE) { - if (m_bufPos >= BlockSize) + if (m_bufPos > 0) { - processBufferDecrypt(m_buf, 0, output, outOff); - m_bufPos -= BlockSize; - System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); - resultLength = BlockSize; + if (m_bufPos >= BlockSize) + { + processBufferDecrypt(m_buf, 0, output, outOff); + m_bufPos -= BlockSize; + System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); + resultLength = BlockSize; + + available += BlockSize; + if (len < available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return resultLength; + } + } - available += BlockSize; - if (len < available) + available = Math.max(BlockSize - m_bufPos, 0); + if (available == -1) { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return resultLength; + System.out.println(); } + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); + resultLength += BlockSize; } - - available = BlockSize - m_bufPos; - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; //m_bufPos = 0; } else @@ -387,7 +396,7 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, } } } - while (len >= m_buf.length) + while (len >= BlockSize + MAC_SIZE) { processBufferDecrypt(input, inOff, output, outOff + resultLength); inOff += BlockSize; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index 86c1823459..ca64e2b0a7 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -4,8 +4,6 @@ import java.util.Arrays; import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.InvalidCipherTextException; -import org.bouncycastle.crypto.OutputLengthException; /** * Elephant AEAD v2, based on the current round 3 submission, https://www.esat.kuleuven.be/cosic/elephant/ @@ -13,7 +11,7 @@ * Specification: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/elephant-spec-final.pdf */ public class ElephantEngine - extends AEADBaseEngine + extends AEADBufferBaseEngine { public enum ElephantParameters { @@ -22,27 +20,13 @@ public enum ElephantParameters elephant200 } - private enum State - { - Uninitialized, - EncInit, - EncAad, // can process AAD - EncData, // cannot process AAD - EncFinal, - DecInit, - DecAad, // can process AAD - DecData, // cannot process AAD - DecFinal, - } private final ElephantParameters parameters; - private final int BLOCK_SIZE; private int nBits; private int nSBox; private final int nRounds; private byte lfsrIV; private byte[] npub; private byte[] expanded_key; - private boolean initialised; private int nb_its; private byte[] ad; private int adOff; @@ -53,10 +37,7 @@ private enum State private byte[] next_mask; private final byte[] buffer; private final byte[] previous_outputMessage; - private State m_state = State.Uninitialized; private final ByteArrayOutputStream aadData = new ByteArrayOutputStream(); - private int inputOff; - private byte[] inputMessage; private int messageLen; private final byte[] sBoxLayer = { @@ -87,12 +68,13 @@ private enum State public ElephantEngine(ElephantParameters parameters) { + super(ProcessingBufferType.Immediate); KEY_SIZE = 16; IV_SIZE = 12; switch (parameters) { case elephant160: - BLOCK_SIZE = 20; + BlockSize = 20; nBits = 160; nSBox = 20; nRounds = 80; @@ -101,7 +83,7 @@ public ElephantEngine(ElephantParameters parameters) algorithmName = "Elephant 160 AEAD"; break; case elephant176: - BLOCK_SIZE = 22; + BlockSize = 22; nBits = 176; nSBox = 22; nRounds = 90; @@ -110,7 +92,7 @@ public ElephantEngine(ElephantParameters parameters) MAC_SIZE = 8; break; case elephant200: - BLOCK_SIZE = 25; + BlockSize = 25; nRounds = 18; algorithmName = "Elephant 200 AEAD"; MAC_SIZE = 16; @@ -119,12 +101,13 @@ public ElephantEngine(ElephantParameters parameters) throw new IllegalArgumentException("Invalid parameter settings for Elephant"); } this.parameters = parameters; - tag_buffer = new byte[BLOCK_SIZE]; - previous_mask = new byte[BLOCK_SIZE]; - current_mask = new byte[BLOCK_SIZE]; - next_mask = new byte[BLOCK_SIZE]; - buffer = new byte[BLOCK_SIZE]; - previous_outputMessage = new byte[BLOCK_SIZE]; + tag_buffer = new byte[BlockSize]; + previous_mask = new byte[BlockSize]; + current_mask = new byte[BlockSize]; + next_mask = new byte[BlockSize]; + buffer = new byte[BlockSize]; + m_buf = new byte[BlockSize * 2 + MAC_SIZE]; + previous_outputMessage = new byte[BlockSize]; initialised = false; reset(false); } @@ -254,17 +237,17 @@ private void lfsr_step(byte[] output, byte[] input) switch (parameters) { case elephant160: - output[BLOCK_SIZE - 1] = (byte)((((input[0] & 0xFF) << 3) | ((input[0] & 0xFF) >>> 5)) ^ + output[BlockSize - 1] = (byte)((((input[0] & 0xFF) << 3) | ((input[0] & 0xFF) >>> 5)) ^ ((input[3] & 0xFF) << 7) ^ ((input[13] & 0xFF) >>> 7)); break; case elephant176: - output[BLOCK_SIZE - 1] = (byte)(rotl(input[0]) ^ ((input[3] & 0xFF) << 7) ^ ((input[19] & 0xFF) >>> 7)); + output[BlockSize - 1] = (byte)(rotl(input[0]) ^ ((input[3] & 0xFF) << 7) ^ ((input[19] & 0xFF) >>> 7)); break; case elephant200: - output[BLOCK_SIZE - 1] = (byte)(rotl(input[0]) ^ rotl(input[2]) ^ (input[13] << 1)); + output[BlockSize - 1] = (byte)(rotl(input[0]) ^ rotl(input[2]) ^ (input[13] << 1)); break; } - System.arraycopy(input, 1, output, 0, BLOCK_SIZE - 1); + System.arraycopy(input, 1, output, 0, BlockSize - 1); } private void xor_block(byte[] state, byte[] block, int bOff, int size) @@ -281,12 +264,11 @@ protected void init(byte[] k, byte[] iv) { npub = iv; // Storage for the expanded key L - expanded_key = new byte[BLOCK_SIZE]; + expanded_key = new byte[BlockSize]; System.arraycopy(k, 0, expanded_key, 0, KEY_SIZE); permutation(expanded_key); initialised = true; m_state = forEncryption ? State.EncInit : State.DecInit; - inputMessage = new byte[BLOCK_SIZE * 2 + (forEncryption ? 0 : MAC_SIZE)]; reset(false); } @@ -306,95 +288,147 @@ public void processAADBytes(byte[] input, int inOff, int len) aadData.write(input, inOff, len); } - @Override - public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - throws DataLengthException + protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { - if (inOff + len > input.length) + if (m_state == State.DecInit || m_state == State.EncInit) { - throw new DataLengthException("input buffer too short"); + processAADBytes(); } + byte[] outputMessage = new byte[BlockSize]; + // Compute mask for the next message + lfsr_step(next_mask, current_mask); - if (inputOff + len - (forEncryption ? 0 : MAC_SIZE) >= BLOCK_SIZE) + // Compute ciphertext block + System.arraycopy(npub, 0, buffer, 0, IV_SIZE); + Arrays.fill(buffer, IV_SIZE, BlockSize, (byte)0); + xor_block(buffer, current_mask, 0, BlockSize); + xor_block(buffer, next_mask, 0, BlockSize); + permutation(buffer); + xor_block(buffer, current_mask, 0, BlockSize); + xor_block(buffer, next_mask, 0, BlockSize); + + xor_block(buffer, input, inOff, BlockSize); + System.arraycopy(buffer, 0, output, outOff, BlockSize); + + System.arraycopy(buffer, 0, outputMessage, 0, BlockSize); + + + if (nb_its > 0) { - int mlen = inputOff + messageLen + len - (forEncryption ? 0 : MAC_SIZE); - int adlen = processAADBytes(); - int nblocks_c = 1 + mlen / BLOCK_SIZE; - int nblocks_m = ((mlen % BLOCK_SIZE) != 0 ? nblocks_c : nblocks_c - 1); - int nblocks_ad = 1 + (IV_SIZE + adlen) / BLOCK_SIZE; - int nb_it = Math.max(nblocks_c + 1, nblocks_ad - 1); - byte[] tempInput = new byte[Math.max(nblocks_c, 1) * BLOCK_SIZE]; - System.arraycopy(inputMessage, 0, tempInput, 0, inputOff); - System.arraycopy(input, inOff, tempInput, inputOff, Math.min(len, tempInput.length - inputOff)); - int rv = processBytes(tempInput, output, outOff, nb_it, nblocks_m, nblocks_c, mlen, nblocks_ad, false); - int copyLen = rv - inputOff; - if (copyLen >= 0) - { - inputOff = inputOff + len - rv; - System.arraycopy(input, inOff + copyLen, inputMessage, 0, inputOff); - } - else - { - System.arraycopy(inputMessage, inputOff + copyLen, inputMessage, 0, -copyLen); - System.arraycopy(input, inOff, inputMessage, -copyLen, len); - inputOff = len - copyLen; - } - messageLen += rv; - return rv; + // enough ciphertext + System.arraycopy(previous_outputMessage, 0, buffer, 0, BlockSize); + + xor_block(buffer, previous_mask, 0, BlockSize); + xor_block(buffer, next_mask, 0, BlockSize); + permutation(buffer); + xor_block(buffer, previous_mask, 0, BlockSize); + xor_block(buffer, next_mask, 0, BlockSize); + xor_block(tag_buffer, buffer, 0, BlockSize); } - else + // If there is any AD left, compute tag for AD block + if (m_state != AEADBufferBaseEngine.State.EncData) { - System.arraycopy(input, inOff, inputMessage, inputOff, len); - inputOff += len; - return 0; + processAADBytes(buffer); + xor_block(buffer, next_mask, 0, BlockSize); + permutation(buffer); + xor_block(buffer, next_mask, 0, BlockSize); + xor_block(tag_buffer, buffer, 0, BlockSize); } + // Cyclically shift the mask buffers + // Value of next_mask will be computed in the next iteration + byte[] temp = previous_mask; + previous_mask = current_mask; + current_mask = next_mask; + next_mask = temp; + System.arraycopy(outputMessage, 0, previous_outputMessage, 0, BlockSize); + nb_its++; + messageLen += BlockSize; } - @Override - public int doFinal(byte[] output, int outOff) - throws IllegalStateException, InvalidCipherTextException + protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - if (!initialised) + if (m_state == State.DecInit || m_state == State.EncInit) + { + int adlen = processAADBytes(); + } + byte[] outputMessage = new byte[BlockSize]; + // Compute mask for the next message + lfsr_step(next_mask, current_mask); + + // Compute ciphertext block + System.arraycopy(npub, 0, buffer, 0, IV_SIZE); + Arrays.fill(buffer, IV_SIZE, BlockSize, (byte)0); + xor_block(buffer, current_mask, 0, BlockSize); + xor_block(buffer, next_mask, 0, BlockSize); + permutation(buffer); + xor_block(buffer, current_mask, 0, BlockSize); + xor_block(buffer, next_mask, 0, BlockSize); + + xor_block(buffer, input, inOff, BlockSize); + System.arraycopy(buffer, 0, output, outOff, BlockSize); + + System.arraycopy(input, inOff, outputMessage, 0, BlockSize); + + if (nb_its > 0) { - throw new IllegalArgumentException(algorithmName + " needs call init function before doFinal"); + // enough ciphertext + System.arraycopy(previous_outputMessage, 0, buffer, 0, BlockSize); + + xor_block(buffer, previous_mask, 0, BlockSize); + xor_block(buffer, next_mask, 0, BlockSize); + permutation(buffer); + xor_block(buffer, previous_mask, 0, BlockSize); + xor_block(buffer, next_mask, 0, BlockSize); + xor_block(tag_buffer, buffer, 0, BlockSize); } - int len = inputOff; - if ((forEncryption && len + outOff + MAC_SIZE > output.length) || - (!forEncryption && len + outOff - MAC_SIZE > output.length)) + // If there is any AD left, compute tag for AD block + if (m_state != AEADBufferBaseEngine.State.DecData) { - throw new OutputLengthException("output buffer is too short"); + processAADBytes(buffer); + xor_block(buffer, next_mask, 0, BlockSize); + permutation(buffer); + xor_block(buffer, next_mask, 0, BlockSize); + xor_block(tag_buffer, buffer, 0, BlockSize); } - int mlen = len + messageLen - (forEncryption ? 0 : MAC_SIZE); + // Cyclically shift the mask buffers + // Value of next_mask will be computed in the next iteration + byte[] temp = previous_mask; + previous_mask = current_mask; + current_mask = next_mask; + next_mask = temp; + System.arraycopy(outputMessage, 0, previous_outputMessage, 0, BlockSize); + nb_its++; + messageLen += BlockSize; + } + + protected void processFinalBlock(byte[] output, int outOff) + { + int len = m_bufPos; + int mlen = len + messageLen; int rv = mlen - messageLen; - int adlen = processAADBytes(); - int nblocks_c = 1 + mlen / BLOCK_SIZE; - int nblocks_m = (mlen % BLOCK_SIZE) != 0 ? nblocks_c : nblocks_c - 1; - int nblocks_ad = 1 + (IV_SIZE + adlen) / BLOCK_SIZE; + processAADBytes(); + int nblocks_c = 1 + mlen / BlockSize; + int nblocks_m = (mlen % BlockSize) != 0 ? nblocks_c : nblocks_c - 1; + int nblocks_ad = 1 + (IV_SIZE + adlen) / BlockSize; int nb_it = Math.max(nblocks_c + 1, nblocks_ad - 1); - outOff += processBytes(inputMessage, output, outOff, nb_it, nblocks_m, nblocks_c, mlen, nblocks_ad, true); + outOff += processBytes(m_buf, output, outOff, nb_it, nblocks_m, nblocks_c, mlen, nblocks_ad, true); mac = new byte[MAC_SIZE]; - xor_block(tag_buffer, expanded_key, 0, BLOCK_SIZE); + xor_block(tag_buffer, expanded_key, 0, BlockSize); permutation(tag_buffer); - xor_block(tag_buffer, expanded_key, 0, BLOCK_SIZE); - if (forEncryption) - { - System.arraycopy(tag_buffer, 0, mac, 0, MAC_SIZE); - System.arraycopy(mac, 0, output, outOff, mac.length); - rv += MAC_SIZE; - } - else - { - inputOff -= MAC_SIZE; - for (int i = 0; i < MAC_SIZE; ++i) - { - if (tag_buffer[i] != inputMessage[inputOff + i]) - { - throw new IllegalArgumentException("Mac does not match"); - } - } - } - reset(false); - return rv; + xor_block(tag_buffer, expanded_key, 0, BlockSize); + System.arraycopy(tag_buffer, 0, mac, 0, MAC_SIZE); + } + + @Override + protected void processBufferAAD(byte[] input, int inOff) + { + + } + + @Override + protected void processFinalAAD() + { + } @Override @@ -411,18 +445,18 @@ public int getUpdateOutputSize(int len) case EncData: case EncInit: { - int total = inputOff + len; - return total - total % BLOCK_SIZE; + int total = m_bufPos + len; + return total - total % BlockSize; } case DecAad: case DecData: case DecInit: { - int total = Math.max(0, inputOff + len - MAC_SIZE); - return total - total % BLOCK_SIZE; + int total = Math.max(0, m_bufPos + len - MAC_SIZE); + return total - total % BlockSize; } } - return Math.max(0, len + inputOff - MAC_SIZE); + return Math.max(0, len + m_bufPos - MAC_SIZE); } @Override @@ -438,14 +472,20 @@ public int getOutputSize(int len) case EncAad: case EncData: case EncInit: - return len + inputOff + MAC_SIZE; + return len + m_bufPos + MAC_SIZE; } - return Math.max(0, len + inputOff - MAC_SIZE); + return Math.max(0, len + m_bufPos - MAC_SIZE); } private int processAADBytes() { - byte[] ad = aadData.toByteArray(); + if (adOff == -1) + { + ad = aadData.toByteArray(); + adOff = 0; + adlen = ad.length; + aadData.reset(); + } switch (m_state) { case EncInit: @@ -461,19 +501,14 @@ protected void reset(boolean clearMac) aadData.reset(); Arrays.fill(tag_buffer, (byte)0); Arrays.fill(previous_outputMessage, (byte)0); - inputOff = 0; + m_bufPos = 0; nb_its = 0; adOff = -1; messageLen = 0; super.reset(clearMac); } - public int getBlockSize() - { - return BLOCK_SIZE; - } - - private void checkAad() + protected void checkAAD() { switch (m_state) { @@ -488,9 +523,28 @@ private void checkAad() } } + protected boolean checkData() + { + switch (m_state) + { + case DecInit: + case DecAad: + case DecData: + return false; + case EncInit: + case EncAad: + case EncData: + return true; + case EncFinal: + throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption"); + default: + throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); + } + } + private void processAADBytes(byte[] output) { - checkAad(); + //checkAAD(); if (adOff == -1) { @@ -502,13 +556,13 @@ private void processAADBytes(byte[] output) switch (m_state) { case DecInit: - System.arraycopy(expanded_key, 0, current_mask, 0, BLOCK_SIZE); + System.arraycopy(expanded_key, 0, current_mask, 0, BlockSize); System.arraycopy(npub, 0, output, 0, IV_SIZE); len += IV_SIZE; m_state = State.DecAad; break; case EncInit: - System.arraycopy(expanded_key, 0, current_mask, 0, BLOCK_SIZE); + System.arraycopy(expanded_key, 0, current_mask, 0, BlockSize); System.arraycopy(npub, 0, output, 0, IV_SIZE); len += IV_SIZE; m_state = State.EncAad; @@ -518,19 +572,19 @@ private void processAADBytes(byte[] output) // If adlen is divisible by BLOCK_SIZE, add an additional padding block if (adOff == adlen) { - Arrays.fill(output, 0, BLOCK_SIZE, (byte)0); + Arrays.fill(output, 0, BlockSize, (byte)0); output[0] = 0x01; return; } break; - case DecData: - throw new IllegalArgumentException(algorithmName + " cannot process AAD when the length of the plaintext to be processed exceeds the a block size"); - case EncData: - throw new IllegalArgumentException(algorithmName + " cannot process AAD when the length of the ciphertext to be processed exceeds the a block size"); - case EncFinal: - throw new IllegalArgumentException(algorithmName + " cannot be reused for encryption"); +// case DecData: +// throw new IllegalArgumentException(algorithmName + " cannot process AAD when the length of the plaintext to be processed exceeds the a block size"); +// case EncData: +// throw new IllegalArgumentException(algorithmName + " cannot process AAD when the length of the ciphertext to be processed exceeds the a block size"); +// case EncFinal: +// throw new IllegalArgumentException(algorithmName + " cannot be reused for encryption"); } - int r_outlen = BLOCK_SIZE - len; + int r_outlen = BlockSize - len; int r_adlen = adlen - adOff; // Fill with associated data if available if (r_outlen <= r_adlen) @@ -563,12 +617,12 @@ private int processBytes(byte[] m, byte[] output, int outOff, int nb_it, int nbl int nblocks_ad, boolean isDofinal) { int rv = 0; - byte[] outputMessage = new byte[BLOCK_SIZE]; + byte[] outputMessage = new byte[BlockSize]; int i; for (i = nb_its; i < nb_it; ++i) { - int r_size = (i == nblocks_m - 1) ? mlen - i * BLOCK_SIZE : BLOCK_SIZE; - if (!isDofinal && (mlen <= i * BLOCK_SIZE || r_size % BLOCK_SIZE != 0)) + int r_size = (i == nblocks_m - 1) ? mlen - i * BlockSize : BlockSize; + if (!isDofinal && (mlen <= i * BlockSize || r_size % BlockSize != 0)) { break; } @@ -578,12 +632,12 @@ private int processBytes(byte[] m, byte[] output, int outOff, int nb_it, int nbl { // Compute ciphertext block System.arraycopy(npub, 0, buffer, 0, IV_SIZE); - Arrays.fill(buffer, IV_SIZE, BLOCK_SIZE, (byte)0); - xor_block(buffer, current_mask, 0, BLOCK_SIZE); - xor_block(buffer, next_mask, 0, BLOCK_SIZE); + Arrays.fill(buffer, IV_SIZE, BlockSize, (byte)0); + xor_block(buffer, current_mask, 0, BlockSize); + xor_block(buffer, next_mask, 0, BlockSize); permutation(buffer); - xor_block(buffer, current_mask, 0, BLOCK_SIZE); - xor_block(buffer, next_mask, 0, BLOCK_SIZE); + xor_block(buffer, current_mask, 0, BlockSize); + xor_block(buffer, next_mask, 0, BlockSize); xor_block(buffer, m, rv, r_size); System.arraycopy(buffer, 0, output, outOff, r_size); @@ -602,47 +656,48 @@ private int processBytes(byte[] m, byte[] output, int outOff, int nb_it, int nbl if (i > 0 && i <= nblocks_c) { //get_c_block: Compute tag for ciphertext block - int block_offset = (i - 1) * BLOCK_SIZE; + int block_offset = (i - 1) * BlockSize; // If clen is divisible by BLOCK_SIZE, add an additional padding block if (block_offset == mlen) { - Arrays.fill(buffer, 0, BLOCK_SIZE, (byte)0); + //TODO: the size of m_buf = BlockSize + MAC_SIZE + Arrays.fill(buffer, 0, BlockSize, (byte)0); buffer[0] = 0x01; } else { int r_clen = mlen - block_offset; // Fill with ciphertext if available - if (BLOCK_SIZE <= r_clen) + if (BlockSize <= r_clen) { // enough ciphertext - System.arraycopy(previous_outputMessage, 0, buffer, 0, BLOCK_SIZE); + System.arraycopy(previous_outputMessage, 0, buffer, 0, BlockSize); } else { // not enough ciphertext, need to pad if (r_clen > 0) // c might be nullptr { System.arraycopy(previous_outputMessage, 0, buffer, 0, r_clen); - Arrays.fill(buffer, r_clen, BLOCK_SIZE, (byte)0); + Arrays.fill(buffer, r_clen, BlockSize, (byte)0); buffer[r_clen] = 0x01; } } } - xor_block(buffer, previous_mask, 0, BLOCK_SIZE); - xor_block(buffer, next_mask, 0, BLOCK_SIZE); + xor_block(buffer, previous_mask, 0, BlockSize); + xor_block(buffer, next_mask, 0, BlockSize); permutation(buffer); - xor_block(buffer, previous_mask, 0, BLOCK_SIZE); - xor_block(buffer, next_mask, 0, BLOCK_SIZE); - xor_block(tag_buffer, buffer, 0, BLOCK_SIZE); + xor_block(buffer, previous_mask, 0, BlockSize); + xor_block(buffer, next_mask, 0, BlockSize); + xor_block(tag_buffer, buffer, 0, BlockSize); } // If there is any AD left, compute tag for AD block if (i + 1 < nblocks_ad) { processAADBytes(buffer); - xor_block(buffer, next_mask, 0, BLOCK_SIZE); + xor_block(buffer, next_mask, 0, BlockSize); permutation(buffer); - xor_block(buffer, next_mask, 0, BLOCK_SIZE); - xor_block(tag_buffer, buffer, 0, BLOCK_SIZE); + xor_block(buffer, next_mask, 0, BlockSize); + xor_block(tag_buffer, buffer, 0, BlockSize); } // Cyclically shift the mask buffers // Value of next_mask will be computed in the next iteration @@ -650,7 +705,7 @@ private int processBytes(byte[] m, byte[] output, int outOff, int nb_it, int nbl previous_mask = current_mask; current_mask = next_mask; next_mask = temp; - System.arraycopy(outputMessage, 0, previous_outputMessage, 0, BLOCK_SIZE); + System.arraycopy(outputMessage, 0, previous_outputMessage, 0, BlockSize); } nb_its = i; return rv; diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java index bfe80251cb..050c7546d3 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java @@ -354,7 +354,7 @@ public void operation() * @param PARTLEN Partial Data length. Must be greater than or equal to internal buffer length to exhibit problem. * @param AEADLEN AEAD length. * @param NONCELEN Nonce length. - * */ + */ static void checkAEADCipherMultipleBlocks(SimpleTest test, int DATALEN, int PARTLEN, int AEADLEN, int strength, int NONCELEN, final AEADCipher pCipher) throws InvalidCipherTextException { diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java index 590f017a77..6447e321a5 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java @@ -8,6 +8,7 @@ import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.ElephantEngine; import org.bouncycastle.crypto.modes.AEADCipher; import org.bouncycastle.crypto.params.KeyParameter; @@ -27,6 +28,10 @@ public String getName() public void performTest() throws Exception { + testVectors(ElephantEngine.ElephantParameters.elephant200, "v200"); + testVectors(ElephantEngine.ElephantParameters.elephant160, "v160"); + testVectors(ElephantEngine.ElephantParameters.elephant176, "v176"); + CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 41, 10, 128, 12, new ElephantEngine(ElephantEngine.ElephantParameters.elephant160)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 41, 10, 128, 12, new ElephantEngine(ElephantEngine.ElephantParameters.elephant176)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 41, 10, 128, 12, new ElephantEngine(ElephantEngine.ElephantParameters.elephant200)); @@ -61,9 +66,7 @@ public AEADCipher createInstance() return new ElephantEngine(ElephantEngine.ElephantParameters.elephant200); } }); - testVectors(ElephantEngine.ElephantParameters.elephant200, "v200"); - testVectors(ElephantEngine.ElephantParameters.elephant160, "v160"); - testVectors(ElephantEngine.ElephantParameters.elephant176, "v176"); + elephant = new ElephantEngine(ElephantEngine.ElephantParameters.elephant160); @@ -92,7 +95,7 @@ private void testVectors(ElephantEngine.ElephantParameters pbp, String filename) int a = line.indexOf('='); if (a < 0) { -// if (!map.get("Count").equals("34")) +// if (!map.get("Count").equals("689")) // { // continue; // } @@ -185,7 +188,7 @@ private void testExceptions(AEADCipher aeadBlockCipher, int keysize, int ivsize, aeadBlockCipher.doFinal(c1, m.length); fail(aeadBlockCipher.getAlgorithmName() + " need to be initialed before dofinal"); } - catch (IllegalArgumentException e) + catch (IllegalStateException e) { //expected } @@ -197,7 +200,7 @@ private void testExceptions(AEADCipher aeadBlockCipher, int keysize, int ivsize, // aeadBlockCipher.getOutputSize(0); // aeadBlockCipher.getUpdateOutputSize(0); } - catch (IllegalArgumentException e) + catch (IllegalStateException e) { //expected fail(aeadBlockCipher.getAlgorithmName() + " functions can be called before initialisation"); @@ -370,7 +373,7 @@ private void testExceptions(AEADCipher aeadBlockCipher, int keysize, int ivsize, aeadBlockCipher.doFinal(m4, offset); fail(aeadBlockCipher.getAlgorithmName() + ": The decryption should fail"); } - catch (IllegalArgumentException e) + catch (InvalidCipherTextException e) { //expected; } From 9d923bd97edb7095769cc26aee8a5916a5ffe0ce Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 15 Jan 2025 14:51:39 +1030 Subject: [PATCH 007/890] Remove System.out.println --- .../org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index fc31fb2cca..74856d8cf4 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -356,10 +356,6 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, } available = Math.max(BlockSize - m_bufPos, 0); - if (available == -1) - { - System.out.println(); - } System.arraycopy(input, inOff, m_buf, m_bufPos, available); inOff += available; len -= available; From 44a6a7f7453b506cb595a8e6ff557f481b7b4321 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 15 Jan 2025 15:30:05 +1030 Subject: [PATCH 008/890] Remove code duplicates in ElephantEngine --- .../crypto/engines/ElephantEngine.java | 176 +++++------------- 1 file changed, 49 insertions(+), 127 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index ca64e2b0a7..8be73891a4 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -3,8 +3,6 @@ import java.io.ByteArrayOutputStream; import java.util.Arrays; -import org.bouncycastle.crypto.DataLengthException; - /** * Elephant AEAD v2, based on the current round 3 submission, https://www.esat.kuleuven.be/cosic/elephant/ * Reference C implementation: https://github.com/TimBeyne/Elephant @@ -281,81 +279,54 @@ public void processAADByte(byte input) @Override public void processAADBytes(byte[] input, int inOff, int len) { - if (inOff + len > input.length) - { - throw new DataLengthException("input buffer too short"); - } + ensureSufficientInputBuffer(input, inOff, len); aadData.write(input, inOff, len); } protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) + { + processBuffer(input, inOff, output, outOff, State.EncData); + System.arraycopy(output, outOff, previous_outputMessage, 0, BlockSize); + } + + private void processBuffer(byte[] input, int inOff, byte[] output, int outOff, State encData) { if (m_state == State.DecInit || m_state == State.EncInit) { processAADBytes(); } - byte[] outputMessage = new byte[BlockSize]; // Compute mask for the next message lfsr_step(next_mask, current_mask); // Compute ciphertext block - System.arraycopy(npub, 0, buffer, 0, IV_SIZE); - Arrays.fill(buffer, IV_SIZE, BlockSize, (byte)0); - xor_block(buffer, current_mask, 0, BlockSize); - xor_block(buffer, next_mask, 0, BlockSize); - permutation(buffer); - xor_block(buffer, current_mask, 0, BlockSize); - xor_block(buffer, next_mask, 0, BlockSize); - - xor_block(buffer, input, inOff, BlockSize); - System.arraycopy(buffer, 0, output, outOff, BlockSize); - - System.arraycopy(buffer, 0, outputMessage, 0, BlockSize); - + computerCipherBlock(input, inOff, BlockSize, output, outOff); if (nb_its > 0) { // enough ciphertext System.arraycopy(previous_outputMessage, 0, buffer, 0, BlockSize); - - xor_block(buffer, previous_mask, 0, BlockSize); - xor_block(buffer, next_mask, 0, BlockSize); - permutation(buffer); - xor_block(buffer, previous_mask, 0, BlockSize); - xor_block(buffer, next_mask, 0, BlockSize); - xor_block(tag_buffer, buffer, 0, BlockSize); + absorbCiphertext(); } // If there is any AD left, compute tag for AD block - if (m_state != AEADBufferBaseEngine.State.EncData) + if (m_state != encData) { - processAADBytes(buffer); - xor_block(buffer, next_mask, 0, BlockSize); - permutation(buffer); - xor_block(buffer, next_mask, 0, BlockSize); - xor_block(tag_buffer, buffer, 0, BlockSize); + absorbAAD(); } // Cyclically shift the mask buffers // Value of next_mask will be computed in the next iteration - byte[] temp = previous_mask; - previous_mask = current_mask; - current_mask = next_mask; - next_mask = temp; - System.arraycopy(outputMessage, 0, previous_outputMessage, 0, BlockSize); + swapMasks(); nb_its++; messageLen += BlockSize; } protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - if (m_state == State.DecInit || m_state == State.EncInit) - { - int adlen = processAADBytes(); - } - byte[] outputMessage = new byte[BlockSize]; - // Compute mask for the next message - lfsr_step(next_mask, current_mask); + processBuffer(input, inOff, output, outOff, State.DecData); + System.arraycopy(input, inOff, previous_outputMessage, 0, BlockSize); + } - // Compute ciphertext block + private void computerCipherBlock(byte[] input, int inOff, int blockSize, byte[] output, int outOff) + { System.arraycopy(npub, 0, buffer, 0, IV_SIZE); Arrays.fill(buffer, IV_SIZE, BlockSize, (byte)0); xor_block(buffer, current_mask, 0, BlockSize); @@ -364,54 +335,47 @@ protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int xor_block(buffer, current_mask, 0, BlockSize); xor_block(buffer, next_mask, 0, BlockSize); - xor_block(buffer, input, inOff, BlockSize); - System.arraycopy(buffer, 0, output, outOff, BlockSize); - - System.arraycopy(input, inOff, outputMessage, 0, BlockSize); - - if (nb_its > 0) - { - // enough ciphertext - System.arraycopy(previous_outputMessage, 0, buffer, 0, BlockSize); + xor_block(buffer, input, inOff, blockSize); + System.arraycopy(buffer, 0, output, outOff, blockSize); + } - xor_block(buffer, previous_mask, 0, BlockSize); - xor_block(buffer, next_mask, 0, BlockSize); - permutation(buffer); - xor_block(buffer, previous_mask, 0, BlockSize); - xor_block(buffer, next_mask, 0, BlockSize); - xor_block(tag_buffer, buffer, 0, BlockSize); - } - // If there is any AD left, compute tag for AD block - if (m_state != AEADBufferBaseEngine.State.DecData) - { - processAADBytes(buffer); - xor_block(buffer, next_mask, 0, BlockSize); - permutation(buffer); - xor_block(buffer, next_mask, 0, BlockSize); - xor_block(tag_buffer, buffer, 0, BlockSize); - } - // Cyclically shift the mask buffers - // Value of next_mask will be computed in the next iteration + private void swapMasks() + { byte[] temp = previous_mask; previous_mask = current_mask; current_mask = next_mask; next_mask = temp; - System.arraycopy(outputMessage, 0, previous_outputMessage, 0, BlockSize); - nb_its++; - messageLen += BlockSize; + } + + private void absorbAAD() + { + processAADBytes(buffer); + xor_block(buffer, next_mask, 0, BlockSize); + permutation(buffer); + xor_block(buffer, next_mask, 0, BlockSize); + xor_block(tag_buffer, buffer, 0, BlockSize); + } + + private void absorbCiphertext() + { + xor_block(buffer, previous_mask, 0, BlockSize); + xor_block(buffer, next_mask, 0, BlockSize); + permutation(buffer); + xor_block(buffer, previous_mask, 0, BlockSize); + xor_block(buffer, next_mask, 0, BlockSize); + xor_block(tag_buffer, buffer, 0, BlockSize); } protected void processFinalBlock(byte[] output, int outOff) { int len = m_bufPos; int mlen = len + messageLen; - int rv = mlen - messageLen; processAADBytes(); int nblocks_c = 1 + mlen / BlockSize; int nblocks_m = (mlen % BlockSize) != 0 ? nblocks_c : nblocks_c - 1; int nblocks_ad = 1 + (IV_SIZE + adlen) / BlockSize; int nb_it = Math.max(nblocks_c + 1, nblocks_ad - 1); - outOff += processBytes(m_buf, output, outOff, nb_it, nblocks_m, nblocks_c, mlen, nblocks_ad, true); + processBytes(m_buf, output, outOff, nb_it, nblocks_m, nblocks_c, mlen, nblocks_ad); mac = new byte[MAC_SIZE]; xor_block(tag_buffer, expanded_key, 0, BlockSize); permutation(tag_buffer); @@ -477,7 +441,7 @@ public int getOutputSize(int len) return Math.max(0, len + m_bufPos - MAC_SIZE); } - private int processAADBytes() + private void processAADBytes() { if (adOff == -1) { @@ -493,7 +457,6 @@ private int processAADBytes() processAADBytes(tag_buffer); break; } - return ad.length; } protected void reset(boolean clearMac) @@ -544,14 +507,6 @@ protected boolean checkData() private void processAADBytes(byte[] output) { - //checkAAD(); - - if (adOff == -1) - { - adlen = aadData.size(); - ad = aadData.toByteArray(); - adOff = 0; - } int len = 0; switch (m_state) { @@ -577,12 +532,6 @@ private void processAADBytes(byte[] output) return; } break; -// case DecData: -// throw new IllegalArgumentException(algorithmName + " cannot process AAD when the length of the plaintext to be processed exceeds the a block size"); -// case EncData: -// throw new IllegalArgumentException(algorithmName + " cannot process AAD when the length of the ciphertext to be processed exceeds the a block size"); -// case EncFinal: -// throw new IllegalArgumentException(algorithmName + " cannot be reused for encryption"); } int r_outlen = BlockSize - len; int r_adlen = adlen - adOff; @@ -613,8 +562,8 @@ private void processAADBytes(byte[] output) } } - private int processBytes(byte[] m, byte[] output, int outOff, int nb_it, int nblocks_m, int nblocks_c, int mlen, - int nblocks_ad, boolean isDofinal) + private void processBytes(byte[] m, byte[] output, int outOff, int nb_it, int nblocks_m, int nblocks_c, int mlen, + int nblocks_ad) { int rv = 0; byte[] outputMessage = new byte[BlockSize]; @@ -622,25 +571,12 @@ private int processBytes(byte[] m, byte[] output, int outOff, int nb_it, int nbl for (i = nb_its; i < nb_it; ++i) { int r_size = (i == nblocks_m - 1) ? mlen - i * BlockSize : BlockSize; - if (!isDofinal && (mlen <= i * BlockSize || r_size % BlockSize != 0)) - { - break; - } // Compute mask for the next message lfsr_step(next_mask, current_mask); if (i < nblocks_m) { // Compute ciphertext block - System.arraycopy(npub, 0, buffer, 0, IV_SIZE); - Arrays.fill(buffer, IV_SIZE, BlockSize, (byte)0); - xor_block(buffer, current_mask, 0, BlockSize); - xor_block(buffer, next_mask, 0, BlockSize); - permutation(buffer); - xor_block(buffer, current_mask, 0, BlockSize); - xor_block(buffer, next_mask, 0, BlockSize); - - xor_block(buffer, m, rv, r_size); - System.arraycopy(buffer, 0, output, outOff, r_size); + computerCipherBlock(m, rv, r_size, output, outOff); if (forEncryption) { System.arraycopy(buffer, 0, outputMessage, 0, r_size); @@ -660,7 +596,6 @@ private int processBytes(byte[] m, byte[] output, int outOff, int nb_it, int nbl // If clen is divisible by BLOCK_SIZE, add an additional padding block if (block_offset == mlen) { - //TODO: the size of m_buf = BlockSize + MAC_SIZE Arrays.fill(buffer, 0, BlockSize, (byte)0); buffer[0] = 0x01; } @@ -683,31 +618,18 @@ private int processBytes(byte[] m, byte[] output, int outOff, int nb_it, int nbl } } - xor_block(buffer, previous_mask, 0, BlockSize); - xor_block(buffer, next_mask, 0, BlockSize); - permutation(buffer); - xor_block(buffer, previous_mask, 0, BlockSize); - xor_block(buffer, next_mask, 0, BlockSize); - xor_block(tag_buffer, buffer, 0, BlockSize); + absorbCiphertext(); } // If there is any AD left, compute tag for AD block if (i + 1 < nblocks_ad) { - processAADBytes(buffer); - xor_block(buffer, next_mask, 0, BlockSize); - permutation(buffer); - xor_block(buffer, next_mask, 0, BlockSize); - xor_block(tag_buffer, buffer, 0, BlockSize); + absorbAAD(); } // Cyclically shift the mask buffers // Value of next_mask will be computed in the next iteration - byte[] temp = previous_mask; - previous_mask = current_mask; - current_mask = next_mask; - next_mask = temp; + swapMasks(); System.arraycopy(outputMessage, 0, previous_outputMessage, 0, BlockSize); } nb_its = i; - return rv; } } From 7d88e94291e969b89ad016478a9bee076af1c12c Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 15 Jan 2025 16:37:05 +1030 Subject: [PATCH 009/890] Add Spongent, Dumbo, Jumbo and Delirium class in ElephantEngine. --- .../crypto/engines/ElephantEngine.java | 269 ++++++++++-------- 1 file changed, 155 insertions(+), 114 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index 8be73891a4..c7511e9fd6 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -18,11 +18,6 @@ public enum ElephantParameters elephant200 } - private final ElephantParameters parameters; - private int nBits; - private int nSBox; - private final int nRounds; - private byte lfsrIV; private byte[] npub; private byte[] expanded_key; private int nb_its; @@ -37,32 +32,7 @@ public enum ElephantParameters private final byte[] previous_outputMessage; private final ByteArrayOutputStream aadData = new ByteArrayOutputStream(); private int messageLen; - - private final byte[] sBoxLayer = { - (byte)0xee, (byte)0xed, (byte)0xeb, (byte)0xe0, (byte)0xe2, (byte)0xe1, (byte)0xe4, (byte)0xef, (byte)0xe7, (byte)0xea, (byte)0xe8, (byte)0xe5, (byte)0xe9, (byte)0xec, (byte)0xe3, (byte)0xe6, - (byte)0xde, (byte)0xdd, (byte)0xdb, (byte)0xd0, (byte)0xd2, (byte)0xd1, (byte)0xd4, (byte)0xdf, (byte)0xd7, (byte)0xda, (byte)0xd8, (byte)0xd5, (byte)0xd9, (byte)0xdc, (byte)0xd3, (byte)0xd6, - (byte)0xbe, (byte)0xbd, (byte)0xbb, (byte)0xb0, (byte)0xb2, (byte)0xb1, (byte)0xb4, (byte)0xbf, (byte)0xb7, (byte)0xba, (byte)0xb8, (byte)0xb5, (byte)0xb9, (byte)0xbc, (byte)0xb3, (byte)0xb6, - (byte)0x0e, (byte)0x0d, (byte)0x0b, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x04, (byte)0x0f, (byte)0x07, (byte)0x0a, (byte)0x08, (byte)0x05, (byte)0x09, (byte)0x0c, (byte)0x03, (byte)0x06, - (byte)0x2e, (byte)0x2d, (byte)0x2b, (byte)0x20, (byte)0x22, (byte)0x21, (byte)0x24, (byte)0x2f, (byte)0x27, (byte)0x2a, (byte)0x28, (byte)0x25, (byte)0x29, (byte)0x2c, (byte)0x23, (byte)0x26, - (byte)0x1e, (byte)0x1d, (byte)0x1b, (byte)0x10, (byte)0x12, (byte)0x11, (byte)0x14, (byte)0x1f, (byte)0x17, (byte)0x1a, (byte)0x18, (byte)0x15, (byte)0x19, (byte)0x1c, (byte)0x13, (byte)0x16, - (byte)0x4e, (byte)0x4d, (byte)0x4b, (byte)0x40, (byte)0x42, (byte)0x41, (byte)0x44, (byte)0x4f, (byte)0x47, (byte)0x4a, (byte)0x48, (byte)0x45, (byte)0x49, (byte)0x4c, (byte)0x43, (byte)0x46, - (byte)0xfe, (byte)0xfd, (byte)0xfb, (byte)0xf0, (byte)0xf2, (byte)0xf1, (byte)0xf4, (byte)0xff, (byte)0xf7, (byte)0xfa, (byte)0xf8, (byte)0xf5, (byte)0xf9, (byte)0xfc, (byte)0xf3, (byte)0xf6, - (byte)0x7e, (byte)0x7d, (byte)0x7b, (byte)0x70, (byte)0x72, (byte)0x71, (byte)0x74, (byte)0x7f, (byte)0x77, (byte)0x7a, (byte)0x78, (byte)0x75, (byte)0x79, (byte)0x7c, (byte)0x73, (byte)0x76, - (byte)0xae, (byte)0xad, (byte)0xab, (byte)0xa0, (byte)0xa2, (byte)0xa1, (byte)0xa4, (byte)0xaf, (byte)0xa7, (byte)0xaa, (byte)0xa8, (byte)0xa5, (byte)0xa9, (byte)0xac, (byte)0xa3, (byte)0xa6, - (byte)0x8e, (byte)0x8d, (byte)0x8b, (byte)0x80, (byte)0x82, (byte)0x81, (byte)0x84, (byte)0x8f, (byte)0x87, (byte)0x8a, (byte)0x88, (byte)0x85, (byte)0x89, (byte)0x8c, (byte)0x83, (byte)0x86, - (byte)0x5e, (byte)0x5d, (byte)0x5b, (byte)0x50, (byte)0x52, (byte)0x51, (byte)0x54, (byte)0x5f, (byte)0x57, (byte)0x5a, (byte)0x58, (byte)0x55, (byte)0x59, (byte)0x5c, (byte)0x53, (byte)0x56, - (byte)0x9e, (byte)0x9d, (byte)0x9b, (byte)0x90, (byte)0x92, (byte)0x91, (byte)0x94, (byte)0x9f, (byte)0x97, (byte)0x9a, (byte)0x98, (byte)0x95, (byte)0x99, (byte)0x9c, (byte)0x93, (byte)0x96, - (byte)0xce, (byte)0xcd, (byte)0xcb, (byte)0xc0, (byte)0xc2, (byte)0xc1, (byte)0xc4, (byte)0xcf, (byte)0xc7, (byte)0xca, (byte)0xc8, (byte)0xc5, (byte)0xc9, (byte)0xcc, (byte)0xc3, (byte)0xc6, - (byte)0x3e, (byte)0x3d, (byte)0x3b, (byte)0x30, (byte)0x32, (byte)0x31, (byte)0x34, (byte)0x3f, (byte)0x37, (byte)0x3a, (byte)0x38, (byte)0x35, (byte)0x39, (byte)0x3c, (byte)0x33, (byte)0x36, - (byte)0x6e, (byte)0x6d, (byte)0x6b, (byte)0x60, (byte)0x62, (byte)0x61, (byte)0x64, (byte)0x6f, (byte)0x67, (byte)0x6a, (byte)0x68, (byte)0x65, (byte)0x69, (byte)0x6c, (byte)0x63, (byte)0x66 - }; - - private final byte[] KeccakRoundConstants = { - (byte)0x01, (byte)0x82, (byte)0x8a, (byte)0x00, (byte)0x8b, (byte)0x01, (byte)0x81, (byte)0x09, (byte)0x8a, - (byte)0x88, (byte)0x09, (byte)0x0a, (byte)0x8b, (byte)0x8b, (byte)0x89, (byte)0x03, (byte)0x02, (byte)0x80 - }; - - private final int[] KeccakRhoOffsets = {0, 1, 6, 4, 3, 4, 4, 6, 7, 4, 3, 2, 3, 1, 7, 1, 5, 7, 5, 0, 2, 2, 5, 0, 6}; + private final Permutation instance; public ElephantEngine(ElephantParameters parameters) { @@ -73,32 +43,25 @@ public ElephantEngine(ElephantParameters parameters) { case elephant160: BlockSize = 20; - nBits = 160; - nSBox = 20; - nRounds = 80; - lfsrIV = 0x75; + instance = new Dumbo(); MAC_SIZE = 8; algorithmName = "Elephant 160 AEAD"; break; case elephant176: BlockSize = 22; - nBits = 176; - nSBox = 22; - nRounds = 90; - lfsrIV = 0x45; + instance = new Jumbo(); algorithmName = "Elephant 176 AEAD"; MAC_SIZE = 8; break; case elephant200: BlockSize = 25; - nRounds = 18; + instance = new Delirium(); algorithmName = "Elephant 200 AEAD"; MAC_SIZE = 16; break; default: throw new IllegalArgumentException("Invalid parameter settings for Elephant"); } - this.parameters = parameters; tag_buffer = new byte[BlockSize]; previous_mask = new byte[BlockSize]; current_mask = new byte[BlockSize]; @@ -110,12 +73,49 @@ public ElephantEngine(ElephantParameters parameters) reset(false); } - private void permutation(byte[] state) + private interface Permutation { - switch (parameters) + void permutation(byte[] state); + + void lfsr_step(); + } + + private abstract static class Spongent + implements Permutation + { + private final byte lfsrIV; + private final int nRounds; + private final int nBits; + private final int nSBox; + private final byte[] sBoxLayer = { + (byte)0xee, (byte)0xed, (byte)0xeb, (byte)0xe0, (byte)0xe2, (byte)0xe1, (byte)0xe4, (byte)0xef, (byte)0xe7, (byte)0xea, (byte)0xe8, (byte)0xe5, (byte)0xe9, (byte)0xec, (byte)0xe3, (byte)0xe6, + (byte)0xde, (byte)0xdd, (byte)0xdb, (byte)0xd0, (byte)0xd2, (byte)0xd1, (byte)0xd4, (byte)0xdf, (byte)0xd7, (byte)0xda, (byte)0xd8, (byte)0xd5, (byte)0xd9, (byte)0xdc, (byte)0xd3, (byte)0xd6, + (byte)0xbe, (byte)0xbd, (byte)0xbb, (byte)0xb0, (byte)0xb2, (byte)0xb1, (byte)0xb4, (byte)0xbf, (byte)0xb7, (byte)0xba, (byte)0xb8, (byte)0xb5, (byte)0xb9, (byte)0xbc, (byte)0xb3, (byte)0xb6, + (byte)0x0e, (byte)0x0d, (byte)0x0b, (byte)0x00, (byte)0x02, (byte)0x01, (byte)0x04, (byte)0x0f, (byte)0x07, (byte)0x0a, (byte)0x08, (byte)0x05, (byte)0x09, (byte)0x0c, (byte)0x03, (byte)0x06, + (byte)0x2e, (byte)0x2d, (byte)0x2b, (byte)0x20, (byte)0x22, (byte)0x21, (byte)0x24, (byte)0x2f, (byte)0x27, (byte)0x2a, (byte)0x28, (byte)0x25, (byte)0x29, (byte)0x2c, (byte)0x23, (byte)0x26, + (byte)0x1e, (byte)0x1d, (byte)0x1b, (byte)0x10, (byte)0x12, (byte)0x11, (byte)0x14, (byte)0x1f, (byte)0x17, (byte)0x1a, (byte)0x18, (byte)0x15, (byte)0x19, (byte)0x1c, (byte)0x13, (byte)0x16, + (byte)0x4e, (byte)0x4d, (byte)0x4b, (byte)0x40, (byte)0x42, (byte)0x41, (byte)0x44, (byte)0x4f, (byte)0x47, (byte)0x4a, (byte)0x48, (byte)0x45, (byte)0x49, (byte)0x4c, (byte)0x43, (byte)0x46, + (byte)0xfe, (byte)0xfd, (byte)0xfb, (byte)0xf0, (byte)0xf2, (byte)0xf1, (byte)0xf4, (byte)0xff, (byte)0xf7, (byte)0xfa, (byte)0xf8, (byte)0xf5, (byte)0xf9, (byte)0xfc, (byte)0xf3, (byte)0xf6, + (byte)0x7e, (byte)0x7d, (byte)0x7b, (byte)0x70, (byte)0x72, (byte)0x71, (byte)0x74, (byte)0x7f, (byte)0x77, (byte)0x7a, (byte)0x78, (byte)0x75, (byte)0x79, (byte)0x7c, (byte)0x73, (byte)0x76, + (byte)0xae, (byte)0xad, (byte)0xab, (byte)0xa0, (byte)0xa2, (byte)0xa1, (byte)0xa4, (byte)0xaf, (byte)0xa7, (byte)0xaa, (byte)0xa8, (byte)0xa5, (byte)0xa9, (byte)0xac, (byte)0xa3, (byte)0xa6, + (byte)0x8e, (byte)0x8d, (byte)0x8b, (byte)0x80, (byte)0x82, (byte)0x81, (byte)0x84, (byte)0x8f, (byte)0x87, (byte)0x8a, (byte)0x88, (byte)0x85, (byte)0x89, (byte)0x8c, (byte)0x83, (byte)0x86, + (byte)0x5e, (byte)0x5d, (byte)0x5b, (byte)0x50, (byte)0x52, (byte)0x51, (byte)0x54, (byte)0x5f, (byte)0x57, (byte)0x5a, (byte)0x58, (byte)0x55, (byte)0x59, (byte)0x5c, (byte)0x53, (byte)0x56, + (byte)0x9e, (byte)0x9d, (byte)0x9b, (byte)0x90, (byte)0x92, (byte)0x91, (byte)0x94, (byte)0x9f, (byte)0x97, (byte)0x9a, (byte)0x98, (byte)0x95, (byte)0x99, (byte)0x9c, (byte)0x93, (byte)0x96, + (byte)0xce, (byte)0xcd, (byte)0xcb, (byte)0xc0, (byte)0xc2, (byte)0xc1, (byte)0xc4, (byte)0xcf, (byte)0xc7, (byte)0xca, (byte)0xc8, (byte)0xc5, (byte)0xc9, (byte)0xcc, (byte)0xc3, (byte)0xc6, + (byte)0x3e, (byte)0x3d, (byte)0x3b, (byte)0x30, (byte)0x32, (byte)0x31, (byte)0x34, (byte)0x3f, (byte)0x37, (byte)0x3a, (byte)0x38, (byte)0x35, (byte)0x39, (byte)0x3c, (byte)0x33, (byte)0x36, + (byte)0x6e, (byte)0x6d, (byte)0x6b, (byte)0x60, (byte)0x62, (byte)0x61, (byte)0x64, (byte)0x6f, (byte)0x67, (byte)0x6a, (byte)0x68, (byte)0x65, (byte)0x69, (byte)0x6c, (byte)0x63, (byte)0x66 + }; + + public Spongent(int nBits, int nSBox, int nRounds, byte lfsrIV) + { + this.nRounds = nRounds; + this.nSBox = nSBox; + this.lfsrIV = lfsrIV; + this.nBits = nBits; + } + + public void permutation(byte[] state) { - case elephant160: - case elephant176: byte IV = lfsrIV; byte[] tmp = new byte[nSBox]; for (int i = 0; i < nRounds; i++) @@ -147,105 +147,146 @@ private void permutation(byte[] state) } System.arraycopy(tmp, 0, state, 0, nSBox); } - break; - case elephant200: - for (int i = 0; i < nRounds; i++) - { - KeccakP200Round(state, i); - } - break; } } - private byte rotl(byte b) + private class Dumbo + extends Spongent { - return (byte)(((b & 0xFF) << 1) | ((b & 0xFF) >>> 7)); - } - private byte ROL8(byte a, int offset) - { - return (byte)((offset != 0) ? (((a & 0xFF) << offset) ^ ((a & 0xFF) >>> (8 - offset))) : a); + public Dumbo() + { + super(160, 20, 80, (byte)0x75); + } + + @Override + public void lfsr_step() + { + next_mask[BlockSize - 1] = (byte)((((current_mask[0] & 0xFF) << 3) | ((current_mask[0] & 0xFF) >>> 5)) ^ + ((current_mask[3] & 0xFF) << 7) ^ ((current_mask[13] & 0xFF) >>> 7)); + } } - private int index(int x, int y) + private class Jumbo + extends Spongent { - return x + y * 5; + + public Jumbo() + { + super(176, 22, 90, (byte)0x45); + } + + @Override + public void lfsr_step() + { + next_mask[BlockSize - 1] = (byte)(rotl(current_mask[0]) ^ ((current_mask[3] & 0xFF) << 7) ^ ((current_mask[19] & 0xFF) >>> 7)); + } } - private void KeccakP200Round(byte[] state, int indexRound) + private class Delirium + implements Permutation { - int x, y; - byte[] tempA = new byte[25]; - //theta - for (x = 0; x < 5; x++) + private static final int nRounds = 18; + private final byte[] KeccakRoundConstants = { + (byte)0x01, (byte)0x82, (byte)0x8a, (byte)0x00, (byte)0x8b, (byte)0x01, (byte)0x81, (byte)0x09, (byte)0x8a, + (byte)0x88, (byte)0x09, (byte)0x0a, (byte)0x8b, (byte)0x8b, (byte)0x89, (byte)0x03, (byte)0x02, (byte)0x80 + }; + + private final int[] KeccakRhoOffsets = {0, 1, 6, 4, 3, 4, 4, 6, 7, 4, 3, 2, 3, 1, 7, 1, 5, 7, 5, 0, 2, 2, 5, 0, 6}; + + @Override + public void permutation(byte[] state) { - for (y = 0; y < 5; y++) + for (int i = 0; i < nRounds; i++) { - tempA[x] ^= state[index(x, y)]; + KeccakP200Round(state, i); } } - for (x = 0; x < 5; x++) + + @Override + public void lfsr_step() { - tempA[x + 5] = (byte)(ROL8(tempA[(x + 1) % 5], 1) ^ tempA[(x + 4) % 5]); + next_mask[BlockSize - 1] = (byte)(rotl(current_mask[0]) ^ rotl(current_mask[2]) ^ (current_mask[13] << 1)); } - for (x = 0; x < 5; x++) + + private void KeccakP200Round(byte[] state, int indexRound) { - for (y = 0; y < 5; y++) + int x, y; + byte[] tempA = new byte[25]; + //theta + for (x = 0; x < 5; x++) { - state[index(x, y)] ^= tempA[x + 5]; + for (y = 0; y < 5; y++) + { + tempA[x] ^= state[index(x, y)]; + } } - } - //rho - for (x = 0; x < 5; x++) - { - for (y = 0; y < 5; y++) + for (x = 0; x < 5; x++) { - tempA[index(x, y)] = ROL8(state[index(x, y)], KeccakRhoOffsets[index(x, y)]); + tempA[x + 5] = (byte)(ROL8(tempA[(x + 1) % 5], 1) ^ tempA[(x + 4) % 5]); } - } - //pi - for (x = 0; x < 5; x++) - { - for (y = 0; y < 5; y++) + for (x = 0; x < 5; x++) { - state[index(y, (2 * x + 3 * y) % 5)] = tempA[index(x, y)]; + for (y = 0; y < 5; y++) + { + state[index(x, y)] ^= tempA[x + 5]; + } } - } - //chi - for (y = 0; y < 5; y++) - { + //rho for (x = 0; x < 5; x++) { - tempA[x] = (byte)(state[index(x, y)] ^ ((~state[index((x + 1) % 5, y)]) & state[index((x + 2) % 5, y)])); + for (y = 0; y < 5; y++) + { + tempA[index(x, y)] = ROL8(state[index(x, y)], KeccakRhoOffsets[index(x, y)]); + } } + //pi for (x = 0; x < 5; x++) { - state[index(x, y)] = tempA[x]; + for (y = 0; y < 5; y++) + { + state[index(y, (2 * x + 3 * y) % 5)] = tempA[index(x, y)]; + } } + //chi + for (y = 0; y < 5; y++) + { + for (x = 0; x < 5; x++) + { + tempA[x] = (byte)(state[index(x, y)] ^ ((~state[index((x + 1) % 5, y)]) & state[index((x + 2) % 5, y)])); + } + for (x = 0; x < 5; x++) + { + state[index(x, y)] = tempA[x]; + } + } + //iota + state[0] ^= KeccakRoundConstants[indexRound];//index(0,0) + } + + private byte ROL8(byte a, int offset) + { + return (byte)((offset != 0) ? (((a & 0xFF) << offset) ^ ((a & 0xFF) >>> (8 - offset))) : a); + } + + private int index(int x, int y) + { + return x + y * 5; } - //iota - state[0] ^= KeccakRoundConstants[indexRound];//index(0,0) + } + + private byte rotl(byte b) + { + return (byte)(((b & 0xFF) << 1) | ((b & 0xFF) >>> 7)); } // State should be BLOCK_SIZE bytes long // Note: input may be equal to output - private void lfsr_step(byte[] output, byte[] input) + private void lfsr_step() { - switch (parameters) - { - case elephant160: - output[BlockSize - 1] = (byte)((((input[0] & 0xFF) << 3) | ((input[0] & 0xFF) >>> 5)) ^ - ((input[3] & 0xFF) << 7) ^ ((input[13] & 0xFF) >>> 7)); - break; - case elephant176: - output[BlockSize - 1] = (byte)(rotl(input[0]) ^ ((input[3] & 0xFF) << 7) ^ ((input[19] & 0xFF) >>> 7)); - break; - case elephant200: - output[BlockSize - 1] = (byte)(rotl(input[0]) ^ rotl(input[2]) ^ (input[13] << 1)); - break; - } - System.arraycopy(input, 1, output, 0, BlockSize - 1); + instance.lfsr_step(); + System.arraycopy(current_mask, 1, next_mask, 0, BlockSize - 1); } private void xor_block(byte[] state, byte[] block, int bOff, int size) @@ -264,7 +305,7 @@ protected void init(byte[] k, byte[] iv) // Storage for the expanded key L expanded_key = new byte[BlockSize]; System.arraycopy(k, 0, expanded_key, 0, KEY_SIZE); - permutation(expanded_key); + instance.permutation(expanded_key); initialised = true; m_state = forEncryption ? State.EncInit : State.DecInit; reset(false); @@ -296,7 +337,7 @@ private void processBuffer(byte[] input, int inOff, byte[] output, int outOff, S processAADBytes(); } // Compute mask for the next message - lfsr_step(next_mask, current_mask); + lfsr_step(); // Compute ciphertext block computerCipherBlock(input, inOff, BlockSize, output, outOff); @@ -331,7 +372,7 @@ private void computerCipherBlock(byte[] input, int inOff, int blockSize, byte[] Arrays.fill(buffer, IV_SIZE, BlockSize, (byte)0); xor_block(buffer, current_mask, 0, BlockSize); xor_block(buffer, next_mask, 0, BlockSize); - permutation(buffer); + instance.permutation(buffer); xor_block(buffer, current_mask, 0, BlockSize); xor_block(buffer, next_mask, 0, BlockSize); @@ -351,7 +392,7 @@ private void absorbAAD() { processAADBytes(buffer); xor_block(buffer, next_mask, 0, BlockSize); - permutation(buffer); + instance.permutation(buffer); xor_block(buffer, next_mask, 0, BlockSize); xor_block(tag_buffer, buffer, 0, BlockSize); } @@ -360,7 +401,7 @@ private void absorbCiphertext() { xor_block(buffer, previous_mask, 0, BlockSize); xor_block(buffer, next_mask, 0, BlockSize); - permutation(buffer); + instance.permutation(buffer); xor_block(buffer, previous_mask, 0, BlockSize); xor_block(buffer, next_mask, 0, BlockSize); xor_block(tag_buffer, buffer, 0, BlockSize); @@ -378,7 +419,7 @@ protected void processFinalBlock(byte[] output, int outOff) processBytes(m_buf, output, outOff, nb_it, nblocks_m, nblocks_c, mlen, nblocks_ad); mac = new byte[MAC_SIZE]; xor_block(tag_buffer, expanded_key, 0, BlockSize); - permutation(tag_buffer); + instance.permutation(tag_buffer); xor_block(tag_buffer, expanded_key, 0, BlockSize); System.arraycopy(tag_buffer, 0, mac, 0, MAC_SIZE); } @@ -572,7 +613,7 @@ private void processBytes(byte[] m, byte[] output, int outOff, int nb_it, int nb { int r_size = (i == nblocks_m - 1) ? mlen - i * BlockSize : BlockSize; // Compute mask for the next message - lfsr_step(next_mask, current_mask); + lfsr_step(); if (i < nblocks_m) { // Compute ciphertext block From 1fdff371fac2bda65f3a3638b5dfaa76e0ebc38d Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 15 Jan 2025 16:59:52 +1030 Subject: [PATCH 010/890] Remove initialised from AEADBufferBaseEngine --- .../crypto/engines/AEADBufferBaseEngine.java | 10 +++++++++- .../bouncycastle/crypto/engines/ElephantEngine.java | 2 -- .../org/bouncycastle/crypto/engines/ISAPEngine.java | 11 ++--------- .../crypto/engines/PhotonBeetleEngine.java | 7 +------ .../bouncycastle/crypto/engines/SparkleEngine.java | 1 - .../bouncycastle/crypto/engines/XoodyakEngine.java | 8 ++------ .../java/org/bouncycastle/crypto/test/ISAPTest.java | 2 ++ .../bouncycastle/crypto/test/PhotonBeetleTest.java | 2 +- .../org/bouncycastle/crypto/test/XoodyakTest.java | 2 +- 9 files changed, 18 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 74856d8cf4..d0ffe0a0a7 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -32,7 +32,6 @@ protected enum State protected int m_bufPos; protected int m_aadPos; protected boolean aadFinished; - protected boolean initialised = false; protected int AADBufferSize; protected int BlockSize; protected State m_state = State.Uninitialized; @@ -366,6 +365,7 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, } else { + //ISAP: ISAP_A_128A, ISAP_A_128 while (m_bufPos >= BlockSize && len + m_bufPos >= BlockSize + MAC_SIZE) { processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); @@ -638,6 +638,14 @@ protected void ensureSufficientInputBuffer(byte[] input, int inOff, int len) } } + protected void ensureInitialized() + { + if (m_state == State.Uninitialized) + { + throw new IllegalStateException("Need to call init function before operation"); + } + } + protected abstract void processFinalBlock(byte[] output, int outOff); protected abstract void processBufferAAD(byte[] input, int inOff); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index c7511e9fd6..3f4e0cd587 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -69,7 +69,6 @@ public ElephantEngine(ElephantParameters parameters) buffer = new byte[BlockSize]; m_buf = new byte[BlockSize * 2 + MAC_SIZE]; previous_outputMessage = new byte[BlockSize]; - initialised = false; reset(false); } @@ -306,7 +305,6 @@ protected void init(byte[] k, byte[] iv) expanded_key = new byte[BlockSize]; System.arraycopy(k, 0, expanded_key, 0, KEY_SIZE); instance.permutation(expanded_key); - initialised = true; m_state = forEncryption ? State.EncInit : State.DecInit; reset(false); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index 4f063ae8c4..ad575893cb 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -793,7 +793,6 @@ protected void init(byte[] key, byte[] iv) k = key; m_buf = new byte[BlockSize + (forEncryption ? 0 : MAC_SIZE)]; ISAPAEAD.init(); - initialised = true; m_state = forEncryption ? State.EncInit : State.DecInit; reset(); } @@ -852,15 +851,9 @@ protected void processFinalBlock(byte[] output, int outOff) protected void reset(boolean clearMac) { - if (!initialised) - { - throw new IllegalStateException("Need call init function before encryption/decryption"); - } - Arrays.fill(m_buf, (byte)0); - Arrays.fill(m_aad, (byte)0); + ensureInitialized(); + bufferReset(); ISAPAEAD.reset(); - m_bufPos = 0; - m_aadPos = 0; aadFinished = false; super.reset(clearMac); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index e340a83d0a..f2062756b9 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -76,7 +76,6 @@ public PhotonBeetleEngine(PhotonBeetleParameters pbp) int STATE_INBITS = RATE_INBITS + CAPACITY_INBITS; STATE_INBYTES = (STATE_INBITS + 7) >>> 3; LAST_THREE_BITS_OFFSET = (STATE_INBITS - ((STATE_INBYTES - 1) << 3) - 3); - initialised = false; algorithmName = "Photon-Beetle AEAD"; m_aad = new byte[AADBufferSize]; } @@ -90,7 +89,6 @@ protected void init(byte[] key, byte[] iv) state = new byte[STATE_INBYTES]; state_2d = new byte[D][D]; mac = new byte[MAC_SIZE]; - initialised = true; m_buf = new byte[BlockSize + (forEncryption ? 0 : MAC_SIZE)]; m_state = forEncryption ? State.EncInit : State.DecInit; reset(false); @@ -204,10 +202,7 @@ protected void processFinalAAD() protected void reset(boolean clearMac) { - if (!initialised) - { - throw new IllegalArgumentException("Need call init function before encryption/decryption"); - } + ensureInitialized(); bufferReset(); input_empty = true; aadLen = 0; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java index f41214f2af..7b26e1dba3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java @@ -125,7 +125,6 @@ protected void init(byte[] key, byte[] iv) { Pack.littleEndianToInt(key, 0, k); Pack.littleEndianToInt(iv, 0, npub); - initialised = true; m_state = forEncryption ? State.EncInit : State.DecInit; reset(); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index 678b7a4216..b17cc0513f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -53,7 +53,6 @@ public void init(byte[] key, byte[] iv) state = new byte[48]; mac = new byte[MAC_SIZE]; m_buf = new byte[BlockSize + (forEncryption ? 0 : MAC_SIZE)]; - initialised = true; m_state = forEncryption ? State.EncInit : State.DecInit; reset(); } @@ -153,10 +152,8 @@ protected void processFinalBlock(byte[] output, int outOff) protected void reset(boolean clearMac) { - if (!initialised) - { - throw new IllegalArgumentException("Need call init function before encryption/decryption"); - } + ensureInitialized(); + super.reset(clearMac); Arrays.fill(state, (byte)0); aadFinished = false; encrypted = false; @@ -175,7 +172,6 @@ protected void reset(boolean clearMac) System.arraycopy(iv, 0, KID, KLen, IDLen); KID[KLen + IDLen] = (byte)IDLen; AbsorbAny(KID, 0, KLen + IDLen + 1, 0x02); - super.reset(clearMac); } private void AbsorbAny(byte[] X, int Xoff, int XLen, int Cd) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java index 2fb9c8e318..a6e50db4cc 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java @@ -345,6 +345,7 @@ private void testExceptions(AEADCipher aeadBlockCipher, int keysize, int ivsize, fail(aeadBlockCipher.getAlgorithmName() + ": mac should not match"); } aeadBlockCipher.reset(); + aeadBlockCipher.init(true, params); aeadBlockCipher.processBytes(new byte[16], 0, 16, new byte[16], 0); // try // { @@ -386,6 +387,7 @@ private void testExceptions(AEADCipher aeadBlockCipher, int keysize, int ivsize, } try { + aeadBlockCipher.init(true, params); aeadBlockCipher.processBytes(new byte[blocksize + 1], 0, blocksize + 1, new byte[blocksize], blocksize >> 1); fail(aeadBlockCipher.getAlgorithmName() + ": output for processBytes is too short"); } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java b/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java index de75ee1330..d5b64c06eb 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java @@ -178,7 +178,7 @@ private void testExceptions(AEADCipher aeadBlockCipher, int keysize, int ivsize, aeadBlockCipher.reset(); fail(aeadBlockCipher.getAlgorithmName() + " need to be initialed before reset"); } - catch (IllegalArgumentException e) + catch (IllegalStateException e) { //expected } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java b/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java index 1d19639d99..f39ef1cdc8 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java @@ -187,7 +187,7 @@ private void testExceptions(AEADCipher aeadBlockCipher, int keysize, int ivsize, aeadBlockCipher.reset(); fail(aeadBlockCipher.getAlgorithmName() + " need to be initialed before reset"); } - catch (IllegalArgumentException e) + catch (IllegalStateException e) { //expected } From 7945d25fbacc33bd2469635cca2a8d9a5d3fd101 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 15 Jan 2025 17:28:41 +1030 Subject: [PATCH 011/890] Reduce m_buf size in ElephantEngine. Refactor on AEADBufferBaseEngine. TODO: refactor around AADProcessingBuffer. --- .../crypto/engines/AEADBufferBaseEngine.java | 200 ++++++++++++------ .../crypto/engines/ElephantEngine.java | 2 +- .../crypto/engines/ISAPEngine.java | 3 +- 3 files changed, 136 insertions(+), 69 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index d0ffe0a0a7..d8f8e45435 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -11,7 +11,8 @@ abstract class AEADBufferBaseEngine protected enum ProcessingBufferType { Buffered, - Immediate + Immediate, + ImmediateLargeMac, } protected enum State @@ -48,6 +49,8 @@ protected AEADBufferBaseEngine(ProcessingBufferType type) case Immediate: processor = new ImmediateAADProcessor(); break; + case ImmediateLargeMac: + processor = new ImmediateLargeMacAADProcessor(); } } @@ -246,7 +249,7 @@ public int getUpdateOutputSize(int len) } } - private class ImmediateAADProcessor + private abstract class ImmediateBaseAADProcessor implements AADProcessingBuffer { public void processAADByte(byte input) @@ -289,6 +292,34 @@ public void processAADBytes(byte[] input, int inOff, int len) m_aadPos += len; } + public int getUpdateOutputSize(int len) + { + int total = Math.max(0, len); + + switch (m_state) + { + case DecInit: + case DecAad: + total = Math.max(0, total - MAC_SIZE); + break; + case DecData: + case DecFinal: + total = Math.max(0, total + m_bufPos - MAC_SIZE); + break; + case EncData: + case EncFinal: + total = Math.max(0, total + m_bufPos); + break; + default: + break; + } + return total - total % BlockSize; + } + } + + private class ImmediateAADProcessor + extends ImmediateBaseAADProcessor + { @Override public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, byte[] output, int outOff) { @@ -334,64 +365,34 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, m_bufPos += len; return 0; } - if (BlockSize >= MAC_SIZE) - { - if (m_bufPos > 0) - { - if (m_bufPos >= BlockSize) - { - processBufferDecrypt(m_buf, 0, output, outOff); - m_bufPos -= BlockSize; - System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); - resultLength = BlockSize; - - available += BlockSize; - if (len < available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return resultLength; - } - } - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - } - //m_bufPos = 0; - } - else + if (m_bufPos > 0) { - //ISAP: ISAP_A_128A, ISAP_A_128 - while (m_bufPos >= BlockSize && len + m_bufPos >= BlockSize + MAC_SIZE) + if (m_bufPos >= BlockSize) { - processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); + processBufferDecrypt(m_buf, 0, output, outOff); m_bufPos -= BlockSize; - resultLength += BlockSize; - } - if (m_bufPos != 0) - { - System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); - if (m_bufPos + len >= BlockSize + MAC_SIZE) - { - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - len -= available; - } - else + System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); + resultLength = BlockSize; + + available += BlockSize; + if (len < available) { System.arraycopy(input, inOff, m_buf, m_bufPos, len); m_bufPos += len; return resultLength; } } + + available = Math.max(BlockSize - m_bufPos, 0); + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); + resultLength += BlockSize; } + //m_bufPos = 0; + while (len >= BlockSize + MAC_SIZE) { processBufferDecrypt(input, inOff, output, outOff + resultLength); @@ -404,29 +405,94 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, m_bufPos = len; return resultLength; } + } - public int getUpdateOutputSize(int len) + private class ImmediateLargeMacAADProcessor + extends ImmediateBaseAADProcessor + { + @Override + public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, byte[] output, int outOff) { - int total = Math.max(0, len); + int resultLength = 0; - switch (m_state) + if (forEncryption) { - case DecInit: - case DecAad: - total = Math.max(0, total - MAC_SIZE); - break; - case DecData: - case DecFinal: - total = Math.max(0, total + m_bufPos - MAC_SIZE); - break; - case EncData: - case EncFinal: - total = Math.max(0, total + m_bufPos); - break; - default: - break; + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos) * BlockSize / BlockSize); + if (m_bufPos > 0) + { + int available = BlockSize - m_bufPos; + if (len < available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return 0; + } + + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; + + processBufferEncrypt(m_buf, 0, output, outOff); + resultLength = BlockSize; + //m_bufPos = 0; + } + + while (len >= BlockSize) + { + processBufferEncrypt(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } } - return total - total % BlockSize; + else + { + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE) * BlockSize / BlockSize); + int available = BlockSize + MAC_SIZE - m_bufPos; + if (len < available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return 0; + } + //ISAP: ISAP_A_128A, ISAP_A_128 + while (m_bufPos >= BlockSize && len + m_bufPos >= BlockSize + MAC_SIZE) + { + processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); + m_bufPos -= BlockSize; + resultLength += BlockSize; + } + if (m_bufPos != 0) + { + System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); + if (m_bufPos + len >= BlockSize + MAC_SIZE) + { + available = Math.max(BlockSize - m_bufPos, 0); + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); + resultLength += BlockSize; + len -= available; + } + else + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return resultLength; + } + } + } + while (len >= BlockSize + MAC_SIZE) + { + processBufferDecrypt(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } + + System.arraycopy(input, inOff, m_buf, 0, len); + m_bufPos = len; + return resultLength; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index 3f4e0cd587..56965c3e83 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -67,7 +67,7 @@ public ElephantEngine(ElephantParameters parameters) current_mask = new byte[BlockSize]; next_mask = new byte[BlockSize]; buffer = new byte[BlockSize]; - m_buf = new byte[BlockSize * 2 + MAC_SIZE]; + m_buf = new byte[BlockSize + MAC_SIZE]; previous_outputMessage = new byte[BlockSize]; reset(false); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index ad575893cb..f001a7fd2d 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -24,7 +24,8 @@ public enum IsapType public ISAPEngine(IsapType isapType) { - super(ProcessingBufferType.Immediate); + super(isapType == IsapType.ISAP_K_128A || isapType == IsapType.ISAP_K_128 ? ProcessingBufferType.Immediate : + ProcessingBufferType.ImmediateLargeMac); KEY_SIZE = 16; IV_SIZE = 16; MAC_SIZE = 16; From d9bb9d93e7bfb4485e646084e52bc665cffa712f Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 16 Jan 2025 12:43:51 +1030 Subject: [PATCH 012/890] Fix the bug in Ascon ciphers. Refactor in AEADBufferBaseEngine --- .../crypto/engines/AEADBufferBaseEngine.java | 386 +++++++++--------- .../crypto/engines/AsconAEAD128.java | 1 + .../crypto/engines/AsconBaseEngine.java | 4 +- .../crypto/engines/AsconEngine.java | 3 + 4 files changed, 189 insertions(+), 205 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index d8f8e45435..01df6c65bb 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -60,7 +60,9 @@ private interface AADProcessingBuffer void processAADBytes(byte[] input, int inOff, int len); - int processBytes(boolean forEncryption, byte[] input, int inOff, int len, byte[] output, int outOff); + int processEncryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff); + + int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff); int getUpdateOutputSize(int len); } @@ -106,117 +108,119 @@ public void processAADBytes(byte[] input, int inOff, int len) } System.arraycopy(input, inOff, m_aad, m_aadPos, len); m_aadPos += len; + } + + @Override + public int processEncryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + int resultLength = 0; + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - 1) * BlockSize / BlockSize); + if (m_bufPos > 0) + { + int available = BlockSize - m_bufPos; + if (len <= available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return 0; + } + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; + processBufferEncrypt(m_buf, 0, output, outOff); + resultLength = BlockSize; + //m_bufPos = 0; + } + while (len > BlockSize) + { + processBufferEncrypt(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } + System.arraycopy(input, inOff, m_buf, 0, len); + m_bufPos = len; + return resultLength; } @Override - public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, byte[] output, int outOff) + public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { int resultLength = 0; - if (forEncryption) + int available = BlockSize + MAC_SIZE - m_bufPos; + if (len <= available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return 0; + } + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE - 1) * BlockSize / BlockSize); + if (BlockSize >= MAC_SIZE) { - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - 1) * BlockSize / BlockSize); if (m_bufPos > 0) { - int available = BlockSize - m_bufPos; - if (len <= available) + if (m_bufPos > BlockSize) { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; + processBufferDecrypt(m_buf, 0, output, outOff); + m_bufPos -= BlockSize; + System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); + resultLength = BlockSize; + + available += BlockSize; + if (len <= available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return resultLength; + } } + + available = BlockSize - m_bufPos; System.arraycopy(input, inOff, m_buf, m_bufPos, available); inOff += available; len -= available; - - processBufferEncrypt(m_buf, 0, output, outOff); - resultLength = BlockSize; - //m_bufPos = 0; - } - - while (len > BlockSize) - { - processBufferEncrypt(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); resultLength += BlockSize; } } else { - int available = BlockSize + MAC_SIZE - m_bufPos; - if (len <= available) + while (m_bufPos > BlockSize && len + m_bufPos > BlockSize + MAC_SIZE) { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; + processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); + m_bufPos -= BlockSize; + resultLength += BlockSize; } - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE - 1) * BlockSize / BlockSize); - if (BlockSize >= MAC_SIZE) + if (m_bufPos != 0) { - if (m_bufPos > 0) + System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); + if (m_bufPos + len > BlockSize + MAC_SIZE) { - if (m_bufPos > BlockSize) - { - processBufferDecrypt(m_buf, 0, output, outOff); - m_bufPos -= BlockSize; - System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); - resultLength = BlockSize; - - available += BlockSize; - if (len <= available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return resultLength; - } - } - - available = BlockSize - m_bufPos; + available = Math.max(BlockSize - m_bufPos, 0); System.arraycopy(input, inOff, m_buf, m_bufPos, available); inOff += available; - len -= available; processBufferDecrypt(m_buf, 0, output, outOff + resultLength); resultLength += BlockSize; + len -= available; } - } - else - { - while (m_bufPos > BlockSize && len + m_bufPos > BlockSize + MAC_SIZE) - { - processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); - m_bufPos -= BlockSize; - resultLength += BlockSize; - } - if (m_bufPos != 0) + else { - System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); - if (m_bufPos + len > BlockSize + MAC_SIZE) - { - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - len -= available; - } - else - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return resultLength; - } + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return resultLength; } } - while (len > m_buf.length) - { - processBufferDecrypt(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; - } } + while (len > m_buf.length) + { + processBufferDecrypt(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } + System.arraycopy(input, inOff, m_buf, 0, len); m_bufPos = len; return resultLength; @@ -315,50 +319,15 @@ public int getUpdateOutputSize(int len) } return total - total % BlockSize; } - } - private class ImmediateAADProcessor - extends ImmediateBaseAADProcessor - { @Override - public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, byte[] output, int outOff) + public int processEncryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { int resultLength = 0; - - if (forEncryption) + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos) * BlockSize / BlockSize); + if (m_bufPos > 0) { - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos) * BlockSize / BlockSize); - if (m_bufPos > 0) - { - int available = BlockSize - m_bufPos; - if (len < available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } - - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - - processBufferEncrypt(m_buf, 0, output, outOff); - resultLength = BlockSize; - //m_bufPos = 0; - } - - while (len >= BlockSize) - { - processBufferEncrypt(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; - } - } - else - { - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE) * BlockSize / BlockSize); - int available = BlockSize + MAC_SIZE - m_bufPos; + int available = BlockSize - m_bufPos; if (len < available) { System.arraycopy(input, inOff, m_buf, m_bufPos, len); @@ -366,122 +335,128 @@ public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, return 0; } - if (m_bufPos > 0) - { - if (m_bufPos >= BlockSize) - { - processBufferDecrypt(m_buf, 0, output, outOff); - m_bufPos -= BlockSize; - System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); - resultLength = BlockSize; - - available += BlockSize; - if (len < available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return resultLength; - } - } + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - } + processBufferEncrypt(m_buf, 0, output, outOff); + resultLength = BlockSize; //m_bufPos = 0; + } - while (len >= BlockSize + MAC_SIZE) - { - processBufferDecrypt(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; - } + while (len >= BlockSize) + { + processBufferEncrypt(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; } + System.arraycopy(input, inOff, m_buf, 0, len); m_bufPos = len; return resultLength; } } - private class ImmediateLargeMacAADProcessor + private class ImmediateAADProcessor extends ImmediateBaseAADProcessor { @Override - public int processBytes(boolean forEncryption, byte[] input, int inOff, int len, byte[] output, int outOff) + public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { int resultLength = 0; + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE) * BlockSize / BlockSize); + int available = BlockSize + MAC_SIZE - m_bufPos; + if (len < available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return 0; + } - if (forEncryption) + if (m_bufPos > 0) { - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos) * BlockSize / BlockSize); - if (m_bufPos > 0) + if (m_bufPos >= BlockSize) { - int available = BlockSize - m_bufPos; + processBufferDecrypt(m_buf, 0, output, outOff); + m_bufPos -= BlockSize; + System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); + resultLength = BlockSize; + + available += BlockSize; if (len < available) { System.arraycopy(input, inOff, m_buf, m_bufPos, len); m_bufPos += len; - return 0; + return resultLength; } + } - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; + available = Math.max(BlockSize - m_bufPos, 0); + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); + resultLength += BlockSize; + } + //m_bufPos = 0; - processBufferEncrypt(m_buf, 0, output, outOff); - resultLength = BlockSize; - //m_bufPos = 0; - } + while (len >= BlockSize + MAC_SIZE) + { + processBufferDecrypt(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } - while (len >= BlockSize) - { - processBufferEncrypt(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; - } + System.arraycopy(input, inOff, m_buf, 0, len); + m_bufPos = len; + return resultLength; + } + } + + private class ImmediateLargeMacAADProcessor + extends ImmediateBaseAADProcessor + { + @Override + public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + int resultLength = 0; + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE) * BlockSize / BlockSize); + int available = BlockSize + MAC_SIZE - m_bufPos; + if (len < available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return 0; } - else + //ISAP: ISAP_A_128A, ISAP_A_128 + while (m_bufPos >= BlockSize && len + m_bufPos >= BlockSize + MAC_SIZE) { - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE) * BlockSize / BlockSize); - int available = BlockSize + MAC_SIZE - m_bufPos; - if (len < available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } - //ISAP: ISAP_A_128A, ISAP_A_128 - while (m_bufPos >= BlockSize && len + m_bufPos >= BlockSize + MAC_SIZE) + processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); + m_bufPos -= BlockSize; + resultLength += BlockSize; + } + if (m_bufPos != 0) + { + System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); + if (m_bufPos + len >= BlockSize + MAC_SIZE) { - processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); - m_bufPos -= BlockSize; + available = Math.max(BlockSize - m_bufPos, 0); + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); resultLength += BlockSize; + len -= available; } - if (m_bufPos != 0) + else { - System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); - if (m_bufPos + len >= BlockSize + MAC_SIZE) - { - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - len -= available; - } - else - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return resultLength; - } + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return resultLength; } } + while (len >= BlockSize + MAC_SIZE) { processBufferDecrypt(input, inOff, output, outOff + resultLength); @@ -523,9 +498,14 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output, int out { ensureSufficientInputBuffer(input, inOff, len); - boolean forEncryption = checkData(); - - return processor.processBytes(forEncryption, input, inOff, len, output, outOff); + if (checkData()) + { + return processor.processEncryptBytes(input, inOff, len, output, outOff); + } + else + { + return processor.processDecryptBytes(input, inOff, len, output, outOff); + } } @Override diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java index 9a82079f96..0be8153be2 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java @@ -21,6 +21,7 @@ public class AsconAEAD128 { public AsconAEAD128() { + super(ProcessingBufferType.Immediate); KEY_SIZE = 16; IV_SIZE = 16; MAC_SIZE = 16; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index 3b588b3565..87bb522d8a 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -25,9 +25,9 @@ abstract class AsconBaseEngine protected abstract void setBytes(long n, byte[] bs, int off); - protected AsconBaseEngine() + protected AsconBaseEngine(ProcessingBufferType type) { - super(ProcessingBufferType.Immediate); + super(type); } private void round(long C) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java index 8417af8322..3d2b38e244 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java @@ -16,6 +16,7 @@ * ASCON C Reference Implementation (NIST Round 2) * . *

+ * * @deprecated Now superseded. Please refer to {@code AsconAEAD128Engine} for future implementations. */ @@ -34,6 +35,7 @@ public enum AsconParameters public AsconEngine(AsconParameters asconParameters) { + super(asconParameters == AsconParameters.ascon128a ? ProcessingBufferType.Immediate : ProcessingBufferType.ImmediateLargeMac); this.asconParameters = asconParameters; IV_SIZE = 16; MAC_SIZE = 16; @@ -84,6 +86,7 @@ protected void setBytes(long n, byte[] bs, int off) { Pack.longToBigEndian(n, bs, off); } + protected void ascon_aeadinit() { /* initialize */ From f33f3a82d687d9fbd110dbcbdcee12f90b7bef12 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 16 Jan 2025 13:30:07 +1030 Subject: [PATCH 013/890] Add BufferedLargeMac in ProcessingBufferType. --- .../crypto/engines/AEADBufferBaseEngine.java | 134 ++++++++++++++++-- .../crypto/engines/PhotonBeetleEngine.java | 2 +- 2 files changed, 127 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 01df6c65bb..e6d929bc11 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -11,6 +11,7 @@ abstract class AEADBufferBaseEngine protected enum ProcessingBufferType { Buffered, + BufferedLargeMac, Immediate, ImmediateLargeMac, } @@ -46,11 +47,15 @@ protected AEADBufferBaseEngine(ProcessingBufferType type) case Buffered: processor = new BufferedAADProcessor(); break; + case BufferedLargeMac: + processor = new BufferedLargeMacAADProcessor(); + break; case Immediate: processor = new ImmediateAADProcessor(); break; case ImmediateLargeMac: processor = new ImmediateLargeMacAADProcessor(); + break; } } @@ -67,7 +72,7 @@ private interface AADProcessingBuffer int getUpdateOutputSize(int len); } - private class BufferedAADProcessor + private abstract class BufferedBaseAADProcessor implements AADProcessingBuffer { public void processAADByte(byte input) @@ -98,7 +103,7 @@ public void processAADBytes(byte[] input, int inOff, int len) len -= available; processBufferAAD(m_aad, 0); - m_aadPos = 0; + } while (len > AADBufferSize) { @@ -106,8 +111,8 @@ public void processAADBytes(byte[] input, int inOff, int len) inOff += AADBufferSize; len -= AADBufferSize; } - System.arraycopy(input, inOff, m_aad, m_aadPos, len); - m_aadPos += len; + System.arraycopy(input, inOff, m_aad, 0, len); + m_aadPos = len; } @Override @@ -253,6 +258,120 @@ public int getUpdateOutputSize(int len) } } + private class BufferedAADProcessor + extends BufferedBaseAADProcessor + { + @Override + public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + int resultLength = 0; + + int available = BlockSize + MAC_SIZE - m_bufPos; + if (len <= available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return 0; + } + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE - 1) * BlockSize / BlockSize); + + if (m_bufPos > 0) + { + if (m_bufPos > BlockSize) + { + processBufferDecrypt(m_buf, 0, output, outOff); + m_bufPos -= BlockSize; + System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); + resultLength = BlockSize; + + available += BlockSize; + if (len <= available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return resultLength; + } + } + + available = BlockSize - m_bufPos; + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); + resultLength += BlockSize; + } + + while (len > m_buf.length) + { + processBufferDecrypt(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } + + System.arraycopy(input, inOff, m_buf, 0, len); + m_bufPos = len; + return resultLength; + } + } + + private class BufferedLargeMacAADProcessor + extends BufferedBaseAADProcessor + { + @Override + public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + int resultLength = 0; + + int available = BlockSize + MAC_SIZE - m_bufPos; + if (len <= available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return 0; + } + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE - 1) * BlockSize / BlockSize); + + while (m_bufPos > BlockSize && len + m_bufPos > BlockSize + MAC_SIZE) + { + processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); + m_bufPos -= BlockSize; + resultLength += BlockSize; + } + if (m_bufPos != 0) + { + System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); + if (m_bufPos + len > BlockSize + MAC_SIZE) + { + available = Math.max(BlockSize - m_bufPos, 0); + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); + resultLength += BlockSize; + len -= available; + } + else + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return resultLength; + } + } + + while (len > m_buf.length) + { + processBufferDecrypt(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } + + System.arraycopy(input, inOff, m_buf, 0, len); + m_bufPos = len; + return resultLength; + } + } + private abstract class ImmediateBaseAADProcessor implements AADProcessingBuffer { @@ -284,7 +403,6 @@ public void processAADBytes(byte[] input, int inOff, int len) len -= available; processBufferAAD(m_aad, 0); - m_aadPos = 0; } while (len >= AADBufferSize) { @@ -292,8 +410,8 @@ public void processAADBytes(byte[] input, int inOff, int len) inOff += AADBufferSize; len -= AADBufferSize; } - System.arraycopy(input, inOff, m_aad, m_aadPos, len); - m_aadPos += len; + System.arraycopy(input, inOff, m_aad, 0, len); + m_aadPos = len; } public int getUpdateOutputSize(int len) @@ -437,7 +555,7 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, m_bufPos -= BlockSize; resultLength += BlockSize; } - if (m_bufPos != 0) + if (m_bufPos > 0) { System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); if (m_bufPos + len >= BlockSize + MAC_SIZE) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index f2062756b9..081b1e0414 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -55,7 +55,7 @@ public enum PhotonBeetleParameters public PhotonBeetleEngine(PhotonBeetleParameters pbp) { - super(ProcessingBufferType.Buffered); + super(pbp == PhotonBeetleParameters.pb128 ? ProcessingBufferType.Buffered : ProcessingBufferType.BufferedLargeMac); KEY_SIZE = 16; IV_SIZE = 16; MAC_SIZE = 16; From a166d84c828aff411eccadbf51baa4cfaa72d418 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 16 Jan 2025 13:46:02 +1030 Subject: [PATCH 014/890] Add m_bufferSizeDecrypt to AEADBufferBaseEngine --- .../crypto/engines/AEADBufferBaseEngine.java | 117 +++--------------- .../crypto/engines/AsconBaseEngine.java | 1 - .../crypto/engines/ElephantEngine.java | 1 + .../crypto/engines/ISAPEngine.java | 1 + .../crypto/engines/PhotonBeetleEngine.java | 3 +- .../crypto/engines/SparkleEngine.java | 2 - .../crypto/engines/XoodyakEngine.java | 3 +- 7 files changed, 22 insertions(+), 106 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index e6d929bc11..d071213fe3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -37,7 +37,7 @@ protected enum State protected int AADBufferSize; protected int BlockSize; protected State m_state = State.Uninitialized; - + protected int m_bufferSizeDecrypt; protected AADProcessingBuffer processor; protected AEADBufferBaseEngine(ProcessingBufferType type) @@ -149,88 +149,6 @@ public int processEncryptBytes(byte[] input, int inOff, int len, byte[] output, return resultLength; } - @Override - public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - { - int resultLength = 0; - - int available = BlockSize + MAC_SIZE - m_bufPos; - if (len <= available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE - 1) * BlockSize / BlockSize); - if (BlockSize >= MAC_SIZE) - { - if (m_bufPos > 0) - { - if (m_bufPos > BlockSize) - { - processBufferDecrypt(m_buf, 0, output, outOff); - m_bufPos -= BlockSize; - System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); - resultLength = BlockSize; - - available += BlockSize; - if (len <= available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return resultLength; - } - } - - available = BlockSize - m_bufPos; - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - } - } - else - { - while (m_bufPos > BlockSize && len + m_bufPos > BlockSize + MAC_SIZE) - { - processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); - m_bufPos -= BlockSize; - resultLength += BlockSize; - } - if (m_bufPos != 0) - { - System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); - if (m_bufPos + len > BlockSize + MAC_SIZE) - { - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - len -= available; - } - else - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return resultLength; - } - } - } - while (len > m_buf.length) - { - processBufferDecrypt(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; - } - - System.arraycopy(input, inOff, m_buf, 0, len); - m_bufPos = len; - return resultLength; - } - @Override public int getUpdateOutputSize(int len) { @@ -266,7 +184,7 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, { int resultLength = 0; - int available = BlockSize + MAC_SIZE - m_bufPos; + int available = m_bufferSizeDecrypt - m_bufPos; if (len <= available) { System.arraycopy(input, inOff, m_buf, m_bufPos, len); @@ -301,7 +219,7 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, resultLength += BlockSize; } - while (len > m_buf.length) + while (len > m_bufferSizeDecrypt) { processBufferDecrypt(input, inOff, output, outOff + resultLength); inOff += BlockSize; @@ -323,7 +241,7 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, { int resultLength = 0; - int available = BlockSize + MAC_SIZE - m_bufPos; + int available = m_bufferSizeDecrypt - m_bufPos; if (len <= available) { System.arraycopy(input, inOff, m_buf, m_bufPos, len); @@ -332,7 +250,7 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, } ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE - 1) * BlockSize / BlockSize); - while (m_bufPos > BlockSize && len + m_bufPos > BlockSize + MAC_SIZE) + while (m_bufPos > BlockSize && len + m_bufPos > m_bufferSizeDecrypt) { processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); m_bufPos -= BlockSize; @@ -341,7 +259,7 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, if (m_bufPos != 0) { System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); - if (m_bufPos + len > BlockSize + MAC_SIZE) + if (m_bufPos + len > m_bufferSizeDecrypt) { available = Math.max(BlockSize - m_bufPos, 0); System.arraycopy(input, inOff, m_buf, m_bufPos, available); @@ -358,7 +276,7 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, } } - while (len > m_buf.length) + while (len > m_bufferSizeDecrypt) { processBufferDecrypt(input, inOff, output, outOff + resultLength); inOff += BlockSize; @@ -459,7 +377,6 @@ public int processEncryptBytes(byte[] input, int inOff, int len, byte[] output, processBufferEncrypt(m_buf, 0, output, outOff); resultLength = BlockSize; - //m_bufPos = 0; } while (len >= BlockSize) @@ -483,15 +400,14 @@ private class ImmediateAADProcessor public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { int resultLength = 0; - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE) * BlockSize / BlockSize); - int available = BlockSize + MAC_SIZE - m_bufPos; + int available = m_bufferSizeDecrypt - m_bufPos; if (len < available) { System.arraycopy(input, inOff, m_buf, m_bufPos, len); m_bufPos += len; return 0; } - + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE) * BlockSize / BlockSize); if (m_bufPos > 0) { if (m_bufPos >= BlockSize) @@ -517,9 +433,8 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, processBufferDecrypt(m_buf, 0, output, outOff + resultLength); resultLength += BlockSize; } - //m_bufPos = 0; - while (len >= BlockSize + MAC_SIZE) + while (len >= m_bufferSizeDecrypt) { processBufferDecrypt(input, inOff, output, outOff + resultLength); inOff += BlockSize; @@ -540,16 +455,16 @@ private class ImmediateLargeMacAADProcessor public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { int resultLength = 0; - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE) * BlockSize / BlockSize); - int available = BlockSize + MAC_SIZE - m_bufPos; + int available = m_bufferSizeDecrypt - m_bufPos; if (len < available) { System.arraycopy(input, inOff, m_buf, m_bufPos, len); m_bufPos += len; return 0; } - //ISAP: ISAP_A_128A, ISAP_A_128 - while (m_bufPos >= BlockSize && len + m_bufPos >= BlockSize + MAC_SIZE) + ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE) * BlockSize / BlockSize); + + while (m_bufPos >= BlockSize && len + m_bufPos >= m_bufferSizeDecrypt) { processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); m_bufPos -= BlockSize; @@ -558,7 +473,7 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, if (m_bufPos > 0) { System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); - if (m_bufPos + len >= BlockSize + MAC_SIZE) + if (m_bufPos + len >= m_bufferSizeDecrypt) { available = Math.max(BlockSize - m_bufPos, 0); System.arraycopy(input, inOff, m_buf, m_bufPos, available); @@ -575,7 +490,7 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, } } - while (len >= BlockSize + MAC_SIZE) + while (len >= m_bufferSizeDecrypt) { processBufferDecrypt(input, inOff, output, outOff + resultLength); inOff += BlockSize; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index 87bb522d8a..f0bf5218f8 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -16,7 +16,6 @@ abstract class AsconBaseEngine protected long x2; protected long x3; protected long x4; - protected int m_bufferSizeDecrypt; protected long dsep; //domain separation protected abstract long pad(int i); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index 56965c3e83..dde1d5149e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -62,6 +62,7 @@ public ElephantEngine(ElephantParameters parameters) default: throw new IllegalArgumentException("Invalid parameter settings for Elephant"); } + m_bufferSizeDecrypt = BlockSize + MAC_SIZE; tag_buffer = new byte[BlockSize]; previous_mask = new byte[BlockSize]; current_mask = new byte[BlockSize]; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index f001a7fd2d..3f55246fdd 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -48,6 +48,7 @@ public ISAPEngine(IsapType isapType) algorithmName = "ISAP-K-128 AEAD"; break; } + m_bufferSizeDecrypt = BlockSize + MAC_SIZE; AADBufferSize = BlockSize; m_aad = new byte[AADBufferSize]; } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 081b1e0414..0aed6fa2c8 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -77,6 +77,8 @@ public PhotonBeetleEngine(PhotonBeetleParameters pbp) STATE_INBYTES = (STATE_INBITS + 7) >>> 3; LAST_THREE_BITS_OFFSET = (STATE_INBITS - ((STATE_INBYTES - 1) << 3) - 3); algorithmName = "Photon-Beetle AEAD"; + m_bufferSizeDecrypt = BlockSize + MAC_SIZE; + m_buf = new byte[m_bufferSizeDecrypt]; m_aad = new byte[AADBufferSize]; } @@ -89,7 +91,6 @@ protected void init(byte[] key, byte[] iv) state = new byte[STATE_INBYTES]; state_2d = new byte[D][D]; mac = new byte[MAC_SIZE]; - m_buf = new byte[BlockSize + (forEncryption ? 0 : MAC_SIZE)]; m_state = forEncryption ? State.EncInit : State.DecInit; reset(false); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java index 7b26e1dba3..20f5f73166 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java @@ -27,8 +27,6 @@ public enum SparkleParameters private final int[] k; private final int[] npub; private boolean encrypted; - private final int m_bufferSizeDecrypt; - private final int SPARKLE_STEPS_SLIM; private final int SPARKLE_STEPS_BIG; private final int KEY_WORDS; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index b17cc0513f..259ea6952e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -41,6 +41,8 @@ public XoodyakEngine() MAC_SIZE = 16; BlockSize = 24; AADBufferSize = 44; + m_bufferSizeDecrypt = BlockSize + MAC_SIZE; + m_buf = new byte[m_bufferSizeDecrypt]; m_aad = new byte[AADBufferSize]; } @@ -52,7 +54,6 @@ public void init(byte[] key, byte[] iv) this.iv = iv; state = new byte[48]; mac = new byte[MAC_SIZE]; - m_buf = new byte[BlockSize + (forEncryption ? 0 : MAC_SIZE)]; m_state = forEncryption ? State.EncInit : State.DecInit; reset(); } From 1d5d3fb35fe4fb4444d0f12af683537b75ecd80e Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 16 Jan 2025 14:36:59 +1030 Subject: [PATCH 015/890] Remove aadFinished from AEADBufferBaseEngine --- .../bouncycastle/crypto/engines/AEADBufferBaseEngine.java | 3 +-- .../java/org/bouncycastle/crypto/engines/ISAPEngine.java | 1 + .../org/bouncycastle/crypto/engines/PhotonBeetleEngine.java | 1 + .../java/org/bouncycastle/crypto/engines/XoodyakEngine.java | 5 +---- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index d071213fe3..77f0a37882 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -33,7 +33,6 @@ protected enum State protected byte[] m_aad; protected int m_bufPos; protected int m_aadPos; - protected boolean aadFinished; protected int AADBufferSize; protected int BlockSize; protected State m_state = State.Uninitialized; @@ -689,7 +688,7 @@ protected void bufferReset() case DecAad: case DecData: case DecFinal: - m_state = State.DecInit; + m_state = State.DecFinal; break; case EncAad: case EncData: diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index 3f55246fdd..e0c86373c0 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -57,6 +57,7 @@ public ISAPEngine(IsapType isapType) private byte[] k; private byte[] npub; private int ISAP_rH; + private boolean aadFinished; private ISAP_AEAD ISAPAEAD; private interface ISAP_AEAD diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 0aed6fa2c8..7cf94b0c1d 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -30,6 +30,7 @@ public enum PhotonBeetleParameters private final int STATE_INBYTES; private final int LAST_THREE_BITS_OFFSET; private final int D = 8; + private boolean aadFinished; private final byte[][] RC = { {1, 3, 7, 14, 13, 11, 6, 12, 9, 2, 5, 10}, {0, 2, 6, 15, 12, 10, 7, 13, 8, 3, 4, 11}, diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index 259ea6952e..7c479f6882 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -25,6 +25,7 @@ public class XoodyakEngine 0x0000002C, 0x00000380, 0x000000F0, 0x000001A0, 0x00000012}; private boolean encrypted; private byte aadcd; + private boolean aadFinished; enum MODE { @@ -66,10 +67,6 @@ protected void processBufferAAD(byte[] input, int inOff) protected void processFinalAAD() { - if (mode != MODE.ModeKeyed) - { - throw new IllegalArgumentException("Xoodyak has not been initialised"); - } if (!aadFinished) { AbsorbAny(m_aad, 0, m_aadPos, aadcd); From 4086458a22dd66ee17fff28dd1ef297340929632 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 16 Jan 2025 14:41:49 +1030 Subject: [PATCH 016/890] Fix the issue in XoodyakEngine --- .../java/org/bouncycastle/crypto/engines/XoodyakEngine.java | 5 +---- .../test/java/org/bouncycastle/crypto/test/XoodyakTest.java | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index 7c479f6882..2b64c363b1 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -150,16 +150,13 @@ protected void processFinalBlock(byte[] output, int outOff) protected void reset(boolean clearMac) { + bufferReset(); ensureInitialized(); super.reset(clearMac); Arrays.fill(state, (byte)0); aadFinished = false; encrypted = false; phase = PhaseUp; - Arrays.fill(m_buf, (byte)0); - Arrays.fill(m_aad, (byte)0); - m_bufPos = 0; - m_aadPos = 0; aadcd = (byte)0x03; //Absorb key int KLen = K.length; diff --git a/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java b/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java index f39ef1cdc8..587c095c96 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java @@ -301,6 +301,7 @@ private void testExceptions(AEADCipher aeadBlockCipher, int keysize, int ivsize, } aeadBlockCipher.reset(); + aeadBlockCipher.init(true, params); try { aeadBlockCipher.processAADBytes(new byte[]{0}, 1, 1); From 79daa789d580875e724bed0d887b5663e7782068 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 16 Jan 2025 15:19:28 +1030 Subject: [PATCH 017/890] Refactor in AEADBufferBaseEngine --- .../crypto/engines/AEADBufferBaseEngine.java | 255 ++++++------------ 1 file changed, 89 insertions(+), 166 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 77f0a37882..aac902fed3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -62,13 +62,13 @@ private interface AADProcessingBuffer { void processAADByte(byte input); - void processAADBytes(byte[] input, int inOff, int len); - - int processEncryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff); - int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff); int getUpdateOutputSize(int len); + + boolean isLengthWithinAvailableSpace(int len, int available); + + boolean isLengthExceedingBlockSize(int len, int size); } private abstract class BufferedBaseAADProcessor @@ -84,94 +84,21 @@ public void processAADByte(byte input) m_aad[m_aadPos++] = input; } - @Override - public void processAADBytes(byte[] input, int inOff, int len) + public boolean isLengthWithinAvailableSpace(int len, int available) { - if (m_aadPos > 0) - { - int available = AADBufferSize - m_aadPos; - if (len <= available) - { - System.arraycopy(input, inOff, m_aad, m_aadPos, len); - m_aadPos += len; - return; - } - - System.arraycopy(input, inOff, m_aad, m_aadPos, available); - inOff += available; - len -= available; - - processBufferAAD(m_aad, 0); - - } - while (len > AADBufferSize) - { - processBufferAAD(input, inOff); - inOff += AADBufferSize; - len -= AADBufferSize; - } - System.arraycopy(input, inOff, m_aad, 0, len); - m_aadPos = len; + return len <= available; } - @Override - public int processEncryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + public boolean isLengthExceedingBlockSize(int len, int size) { - int resultLength = 0; - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - 1) * BlockSize / BlockSize); - if (m_bufPos > 0) - { - int available = BlockSize - m_bufPos; - if (len <= available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - - processBufferEncrypt(m_buf, 0, output, outOff); - resultLength = BlockSize; - //m_bufPos = 0; - } - while (len > BlockSize) - { - processBufferEncrypt(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; - } - System.arraycopy(input, inOff, m_buf, 0, len); - m_bufPos = len; - return resultLength; + return len > size; } @Override public int getUpdateOutputSize(int len) { // The -1 is to account for the lazy processing of a full buffer - int total = Math.max(0, len) - 1; - - switch (m_state) - { - case DecInit: - case DecAad: - total = Math.max(0, total - MAC_SIZE); - break; - case DecData: - case DecFinal: - total = Math.max(0, total + m_bufPos - MAC_SIZE); - break; - case EncData: - case EncFinal: - total = Math.max(0, total + m_bufPos); - break; - default: - break; - } - return total - total % BlockSize; + return Math.max(0, len) - 1; } } @@ -255,7 +182,7 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, m_bufPos -= BlockSize; resultLength += BlockSize; } - if (m_bufPos != 0) + if (m_bufPos > 0) { System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); if (m_bufPos + len > m_bufferSizeDecrypt) @@ -302,93 +229,19 @@ public void processAADByte(byte input) } } - @Override - public void processAADBytes(byte[] input, int inOff, int len) + public int getUpdateOutputSize(int len) { - if (m_aadPos > 0) - { - int available = AADBufferSize - m_aadPos; - if (len < available) - { - System.arraycopy(input, inOff, m_aad, m_aadPos, len); - m_aadPos += len; - return; - } - - System.arraycopy(input, inOff, m_aad, m_aadPos, available); - inOff += available; - len -= available; - - processBufferAAD(m_aad, 0); - } - while (len >= AADBufferSize) - { - processBufferAAD(input, inOff); - inOff += AADBufferSize; - len -= AADBufferSize; - } - System.arraycopy(input, inOff, m_aad, 0, len); - m_aadPos = len; + return Math.max(0, len); } - public int getUpdateOutputSize(int len) + public boolean isLengthWithinAvailableSpace(int len, int available) { - int total = Math.max(0, len); - - switch (m_state) - { - case DecInit: - case DecAad: - total = Math.max(0, total - MAC_SIZE); - break; - case DecData: - case DecFinal: - total = Math.max(0, total + m_bufPos - MAC_SIZE); - break; - case EncData: - case EncFinal: - total = Math.max(0, total + m_bufPos); - break; - default: - break; - } - return total - total % BlockSize; + return len < available; } - @Override - public int processEncryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + public boolean isLengthExceedingBlockSize(int len, int size) { - int resultLength = 0; - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos) * BlockSize / BlockSize); - if (m_bufPos > 0) - { - int available = BlockSize - m_bufPos; - if (len < available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } - - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - - processBufferEncrypt(m_buf, 0, output, outOff); - resultLength = BlockSize; - } - - while (len >= BlockSize) - { - processBufferEncrypt(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; - } - - System.arraycopy(input, inOff, m_buf, 0, len); - m_bufPos = len; - return resultLength; + return len >= size; } } @@ -521,7 +374,31 @@ public void processAADBytes(byte[] input, int inOff, int len) } checkAAD(); - processor.processAADBytes(input, inOff, len); + if (m_aadPos > 0) + { + int available = AADBufferSize - m_aadPos; + if (processor.isLengthWithinAvailableSpace(len, available)) + { + System.arraycopy(input, inOff, m_aad, m_aadPos, len); + m_aadPos += len; + return; + } + + System.arraycopy(input, inOff, m_aad, m_aadPos, available); + inOff += available; + len -= available; + + processBufferAAD(m_aad, 0); + + } + while (processor.isLengthExceedingBlockSize(len, AADBufferSize)) + { + processBufferAAD(input, inOff); + inOff += AADBufferSize; + len -= AADBufferSize; + } + System.arraycopy(input, inOff, m_aad, 0, len); + m_aadPos = len; } @Override @@ -532,7 +409,35 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output, int out if (checkData()) { - return processor.processEncryptBytes(input, inOff, len, output, outOff); + + int resultLength = 0; + ensureSufficientOutputBuffer(output, outOff, (processor.getUpdateOutputSize(len) + m_bufPos) * BlockSize / BlockSize); + if (m_bufPos > 0) + { + int available = BlockSize - m_bufPos; + if (processor.isLengthWithinAvailableSpace(len, available)) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return 0; + } + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; + + processBufferEncrypt(m_buf, 0, output, outOff); + resultLength = BlockSize; + } + while (processor.isLengthExceedingBlockSize(len, BlockSize)) + { + processBufferEncrypt(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } + System.arraycopy(input, inOff, m_buf, 0, len); + m_bufPos = len; + return resultLength; } else { @@ -589,7 +494,25 @@ public int getBlockSize() public int getUpdateOutputSize(int len) { - return processor.getUpdateOutputSize(len); + int total = processor.getUpdateOutputSize(len); + switch (m_state) + { + case DecInit: + case DecAad: + total = Math.max(0, total - MAC_SIZE); + break; + case DecData: + case DecFinal: + total = Math.max(0, total + m_bufPos - MAC_SIZE); + break; + case EncData: + case EncFinal: + total = Math.max(0, total + m_bufPos); + break; + default: + break; + } + return total - total % BlockSize; } public int getOutputSize(int len) From cb1b596613a03245bc0f666248f5169f7495ea15 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 16 Jan 2025 17:38:55 +1030 Subject: [PATCH 018/890] Refactor in AEADBufferBaseEngine --- .../crypto/engines/AEADBufferBaseEngine.java | 146 ++++++------------ 1 file changed, 44 insertions(+), 102 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index aac902fed3..9bd70b05f4 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -109,16 +109,7 @@ private class BufferedAADProcessor public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { int resultLength = 0; - int available = m_bufferSizeDecrypt - m_bufPos; - if (len <= available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE - 1) * BlockSize / BlockSize); - if (m_bufPos > 0) { if (m_bufPos > BlockSize) @@ -133,29 +124,16 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, { System.arraycopy(input, inOff, m_buf, m_bufPos, len); m_bufPos += len; - return resultLength; + return -1; } } available = BlockSize - m_bufPos; System.arraycopy(input, inOff, m_buf, m_bufPos, available); inOff += available; - len -= available; processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - } - - while (len > m_bufferSizeDecrypt) - { - processBufferDecrypt(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; } - - System.arraycopy(input, inOff, m_buf, 0, len); - m_bufPos = len; - return resultLength; + return inOff; } } @@ -167,15 +145,7 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, { int resultLength = 0; - int available = m_bufferSizeDecrypt - m_bufPos; - if (len <= available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE - 1) * BlockSize / BlockSize); - + int available; while (m_bufPos > BlockSize && len + m_bufPos > m_bufferSizeDecrypt) { processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); @@ -191,28 +161,15 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, System.arraycopy(input, inOff, m_buf, m_bufPos, available); inOff += available; processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - len -= available; } else { System.arraycopy(input, inOff, m_buf, m_bufPos, len); m_bufPos += len; - return resultLength; + return -1; } } - - while (len > m_bufferSizeDecrypt) - { - processBufferDecrypt(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; - } - - System.arraycopy(input, inOff, m_buf, 0, len); - m_bufPos = len; - return resultLength; + return inOff; } } @@ -253,13 +210,6 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, { int resultLength = 0; int available = m_bufferSizeDecrypt - m_bufPos; - if (len < available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE) * BlockSize / BlockSize); if (m_bufPos > 0) { if (m_bufPos >= BlockSize) @@ -274,29 +224,16 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, { System.arraycopy(input, inOff, m_buf, m_bufPos, len); m_bufPos += len; - return resultLength; + return -1; } } available = Math.max(BlockSize - m_bufPos, 0); System.arraycopy(input, inOff, m_buf, m_bufPos, available); inOff += available; - len -= available; processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - } - - while (len >= m_bufferSizeDecrypt) - { - processBufferDecrypt(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; } - - System.arraycopy(input, inOff, m_buf, 0, len); - m_bufPos = len; - return resultLength; + return inOff; } } @@ -307,14 +244,7 @@ private class ImmediateLargeMacAADProcessor public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { int resultLength = 0; - int available = m_bufferSizeDecrypt - m_bufPos; - if (len < available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } - ensureSufficientOutputBuffer(output, outOff, (len + m_bufPos - MAC_SIZE) * BlockSize / BlockSize); + int available; while (m_bufPos >= BlockSize && len + m_bufPos >= m_bufferSizeDecrypt) { @@ -331,28 +261,15 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, System.arraycopy(input, inOff, m_buf, m_bufPos, available); inOff += available; processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - len -= available; } else { System.arraycopy(input, inOff, m_buf, m_bufPos, len); m_bufPos += len; - return resultLength; + return -1; } } - - while (len >= m_bufferSizeDecrypt) - { - processBufferDecrypt(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; - } - - System.arraycopy(input, inOff, m_buf, 0, len); - m_bufPos = len; - return resultLength; + return inOff; } } @@ -406,15 +323,15 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output, int out throws DataLengthException { ensureSufficientInputBuffer(input, inOff, len); - + int available, resultLength; if (checkData()) { - - int resultLength = 0; - ensureSufficientOutputBuffer(output, outOff, (processor.getUpdateOutputSize(len) + m_bufPos) * BlockSize / BlockSize); + resultLength = 0; + available = processor.getUpdateOutputSize(len) + m_bufPos; + ensureSufficientOutputBuffer(output, outOff, available - available % BlockSize); if (m_bufPos > 0) { - int available = BlockSize - m_bufPos; + available = BlockSize - m_bufPos; if (processor.isLengthWithinAvailableSpace(len, available)) { System.arraycopy(input, inOff, m_buf, m_bufPos, len); @@ -435,14 +352,39 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output, int out len -= BlockSize; resultLength += BlockSize; } - System.arraycopy(input, inOff, m_buf, 0, len); - m_bufPos = len; - return resultLength; } else { - return processor.processDecryptBytes(input, inOff, len, output, outOff); + available = m_bufferSizeDecrypt - m_bufPos; + if (processor.isLengthWithinAvailableSpace(len, available)) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return 0; + } + resultLength = (processor.getUpdateOutputSize(len) + m_bufPos - MAC_SIZE); + resultLength -= resultLength % BlockSize; + ensureSufficientOutputBuffer(output, outOff, resultLength); + int originalInOff = inOff; + int originalm_bufPos = m_bufPos; + if ((inOff = processor.processDecryptBytes(input, inOff, len, output, outOff)) == -1) + { + return resultLength; + } + resultLength = inOff - originalInOff; + len -= resultLength; + resultLength += originalm_bufPos; + while (processor.isLengthExceedingBlockSize(len, m_bufferSizeDecrypt)) + { + processBufferDecrypt(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } } + System.arraycopy(input, inOff, m_buf, 0, len); + m_bufPos = len; + return resultLength; } @Override From 6c43cfc7a0f4f9c8e7d59eb30f15e0262539d8eb Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 16 Jan 2025 18:52:11 +1030 Subject: [PATCH 019/890] Refactor in AEADBufferBaseEngine.processDecryptBytes --- .../crypto/engines/AEADBufferBaseEngine.java | 172 +++++++----------- 1 file changed, 66 insertions(+), 106 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 9bd70b05f4..4b17187198 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -84,11 +84,13 @@ public void processAADByte(byte input) m_aad[m_aadPos++] = input; } + @Override public boolean isLengthWithinAvailableSpace(int len, int available) { return len <= available; } + @Override public boolean isLengthExceedingBlockSize(int len, int size) { return len > size; @@ -108,32 +110,7 @@ private class BufferedAADProcessor @Override public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { - int resultLength = 0; - int available = m_bufferSizeDecrypt - m_bufPos; - if (m_bufPos > 0) - { - if (m_bufPos > BlockSize) - { - processBufferDecrypt(m_buf, 0, output, outOff); - m_bufPos -= BlockSize; - System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); - resultLength = BlockSize; - - available += BlockSize; - if (len <= available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return -1; - } - } - - available = BlockSize - m_bufPos; - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - } - return inOff; + return processDecryptionWithSmallMacSize(input, inOff, len, output, outOff); } } @@ -143,33 +120,7 @@ private class BufferedLargeMacAADProcessor @Override public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { - int resultLength = 0; - - int available; - while (m_bufPos > BlockSize && len + m_bufPos > m_bufferSizeDecrypt) - { - processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); - m_bufPos -= BlockSize; - resultLength += BlockSize; - } - if (m_bufPos > 0) - { - System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); - if (m_bufPos + len > m_bufferSizeDecrypt) - { - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - } - else - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return -1; - } - } - return inOff; + return processDecryptionWithLargeMacSize(input, inOff, len, output, outOff); } } @@ -186,16 +137,19 @@ public void processAADByte(byte input) } } + @Override public int getUpdateOutputSize(int len) { return Math.max(0, len); } + @Override public boolean isLengthWithinAvailableSpace(int len, int available) { return len < available; } + @Override public boolean isLengthExceedingBlockSize(int len, int size) { return len >= size; @@ -208,32 +162,7 @@ private class ImmediateAADProcessor @Override public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { - int resultLength = 0; - int available = m_bufferSizeDecrypt - m_bufPos; - if (m_bufPos > 0) - { - if (m_bufPos >= BlockSize) - { - processBufferDecrypt(m_buf, 0, output, outOff); - m_bufPos -= BlockSize; - System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); - resultLength = BlockSize; - - available += BlockSize; - if (len < available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return -1; - } - } - - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - } - return inOff; + return processDecryptionWithSmallMacSize(input, inOff, len, output, outOff); } } @@ -243,33 +172,7 @@ private class ImmediateLargeMacAADProcessor @Override public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { - int resultLength = 0; - int available; - - while (m_bufPos >= BlockSize && len + m_bufPos >= m_bufferSizeDecrypt) - { - processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); - m_bufPos -= BlockSize; - resultLength += BlockSize; - } - if (m_bufPos > 0) - { - System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); - if (m_bufPos + len >= m_bufferSizeDecrypt) - { - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - } - else - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return -1; - } - } - return inOff; + return processDecryptionWithLargeMacSize(input, inOff, len, output, outOff); } } @@ -387,6 +290,63 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output, int out return resultLength; } + private int processDecryptionWithLargeMacSize(byte[] input, int inOff, int len, byte[] output, int outOff) + { + int resultLength = 0, available; + while (processor.isLengthExceedingBlockSize(m_bufPos, BlockSize) + && processor.isLengthExceedingBlockSize(len + m_bufPos, m_bufferSizeDecrypt)) + { + processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); + m_bufPos -= BlockSize; + resultLength += BlockSize; + } + if (m_bufPos > 0) + { + System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); + if (processor.isLengthExceedingBlockSize(m_bufPos + len, m_bufferSizeDecrypt)) + { + available = Math.max(BlockSize - m_bufPos, 0); + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); + } + else + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return -1; + } + } + return inOff; + } + + int processDecryptionWithSmallMacSize(byte[] input, int inOff, int len, byte[] output, int outOff) + { + int resultLength = 0, available = m_bufferSizeDecrypt - m_bufPos; + if (m_bufPos > 0) + { + if (processor.isLengthExceedingBlockSize(m_bufPos, BlockSize)) + { + processBufferDecrypt(m_buf, 0, output, outOff); + m_bufPos -= BlockSize; + System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); + resultLength = BlockSize; + available += BlockSize; + if (processor.isLengthWithinAvailableSpace(len, available)) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return -1; + } + } + available = Math.max(BlockSize - m_bufPos, 0); + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); + } + return inOff; + } + @Override public int doFinal(byte[] output, int outOff) throws IllegalStateException, InvalidCipherTextException From e3189d711d31b4c364d803cccb62dbb1356b2f97 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 17 Jan 2025 09:43:39 +1030 Subject: [PATCH 020/890] Merge GiftCofb branch into this branch --- .../crypto/engines/AEADBufferBaseEngine.java | 2 +- .../crypto/engines/GiftCofbEngine.java | 538 ++++++++++++++++++ .../bouncycastle/util/test/SimpleTest.java | 4 +- .../bouncycastle/crypto/test/CipherTest.java | 83 +++ .../crypto/test/GiftCofbTest.java | 387 +++++++++++++ 5 files changed, 1011 insertions(+), 3 deletions(-) create mode 100644 core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java create mode 100644 core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 4b17187198..7bef3ea765 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -320,7 +320,7 @@ private int processDecryptionWithLargeMacSize(byte[] input, int inOff, int len, return inOff; } - int processDecryptionWithSmallMacSize(byte[] input, int inOff, int len, byte[] output, int outOff) + private int processDecryptionWithSmallMacSize(byte[] input, int inOff, int len, byte[] output, int outOff) { int resultLength = 0, available = m_bufferSizeDecrypt - m_bufPos; if (m_bufPos > 0) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java new file mode 100644 index 0000000000..35765abd47 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java @@ -0,0 +1,538 @@ +package org.bouncycastle.crypto.engines; + +import java.io.ByteArrayOutputStream; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.OutputLengthException; +import org.bouncycastle.crypto.constraints.DefaultServiceProperties; +import org.bouncycastle.crypto.modes.AEADBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +/** + * GIFT-COFB v1.1, based on the current round 3 submission, https://www.isical.ac.in/~lightweight/COFB/ + * Reference C implementation: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-submissions/elephant.zip + * Specification: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/gift-cofb-spec-final.pdf + */ + +public class GiftCofbEngine + implements AEADBlockCipher +{ + private final int CRYPTO_ABYTES = 16; + private boolean forEncryption; + private boolean initialised = false; + private byte[] npub; + private byte[] k; + private byte[] Y; + private byte[] mac; + private byte[] input; + private byte[] offset; + private boolean encrypted; + private final ByteArrayOutputStream aadData = new ByteArrayOutputStream(); + private final ByteArrayOutputStream message = new ByteArrayOutputStream(); + /*Round constants*/ + private final byte[] GIFT_RC = { + (byte)0x01, (byte)0x03, (byte)0x07, (byte)0x0F, (byte)0x1F, (byte)0x3E, (byte)0x3D, (byte)0x3B, (byte)0x37, (byte)0x2F, + (byte)0x1E, (byte)0x3C, (byte)0x39, (byte)0x33, (byte)0x27, (byte)0x0E, (byte)0x1D, (byte)0x3A, (byte)0x35, (byte)0x2B, + (byte)0x16, (byte)0x2C, (byte)0x18, (byte)0x30, (byte)0x21, (byte)0x02, (byte)0x05, (byte)0x0B, (byte)0x17, (byte)0x2E, + (byte)0x1C, (byte)0x38, (byte)0x31, (byte)0x23, (byte)0x06, (byte)0x0D, (byte)0x1B, (byte)0x36, (byte)0x2D, (byte)0x1A + }; + + private int rowperm(int S, int B0_pos, int B1_pos, int B2_pos, int B3_pos) + { + int T = 0; + int b; + for (b = 0; b < 8; b++) + { + T |= ((S >>> (4 * b)) & 0x1) << (b + 8 * B0_pos); + T |= ((S >>> (4 * b + 1)) & 0x1) << (b + 8 * B1_pos); + T |= ((S >>> (4 * b + 2)) & 0x1) << (b + 8 * B2_pos); + T |= ((S >>> (4 * b + 3)) & 0x1) << (b + 8 * B3_pos); + } + return T; + } + + private void giftb128(byte[] P, byte[] K, byte[] C) + { + int round, T; + int[] S = new int[4]; + short[] W = new short[8]; + short T6, T7; + S[0] = ((P[0] & 0xFF) << 24) | ((P[1] & 0xFF) << 16) | ((P[2] & 0xFF) << 8) | (P[3] & 0xFF); + S[1] = ((P[4] & 0xFF) << 24) | ((P[5] & 0xFF) << 16) | ((P[6] & 0xFF) << 8) | (P[7] & 0xFF); + S[2] = ((P[8] & 0xFF) << 24) | ((P[9] & 0xFF) << 16) | ((P[10] & 0xFF) << 8) | (P[11] & 0xFF); + S[3] = ((P[12] & 0xFF) << 24) | ((P[13] & 0xFF) << 16) | ((P[14] & 0xFF) << 8) | (P[15] & 0xFF); + W[0] = (short)(((K[0] & 0xFF) << 8) | (K[1] & 0xFF)); + W[1] = (short)(((K[2] & 0xFF) << 8) | (K[3] & 0xFF)); + W[2] = (short)(((K[4] & 0xFF) << 8) | (K[5] & 0xFF)); + W[3] = (short)(((K[6] & 0xFF) << 8) | (K[7] & 0xFF)); + W[4] = (short)(((K[8] & 0xFF) << 8) | (K[9] & 0xFF)); + W[5] = (short)(((K[10] & 0xFF) << 8) | (K[11] & 0xFF)); + W[6] = (short)(((K[12] & 0xFF) << 8) | (K[13] & 0xFF)); + W[7] = (short)(((K[14] & 0xFF) << 8) | (K[15] & 0xFF)); + for (round = 0; round < 40; round++) + { + /*===SubCells===*/ + S[1] ^= S[0] & S[2]; + S[0] ^= S[1] & S[3]; + S[2] ^= S[0] | S[1]; + S[3] ^= S[2]; + S[1] ^= S[3]; + S[3] ^= 0xffffffff; + S[2] ^= S[0] & S[1]; + T = S[0]; + S[0] = S[3]; + S[3] = T; + /*===PermBits===*/ + S[0] = rowperm(S[0], 0, 3, 2, 1); + S[1] = rowperm(S[1], 1, 0, 3, 2); + S[2] = rowperm(S[2], 2, 1, 0, 3); + S[3] = rowperm(S[3], 3, 2, 1, 0); + /*===AddRoundKey===*/ + S[2] ^= ((W[2] & 0xFFFF) << 16) | (W[3] & 0xFFFF); + S[1] ^= ((W[6] & 0xFFFF) << 16) | (W[7] & 0xFFFF); + /*Add round constant*/ + S[3] ^= 0x80000000 ^ (GIFT_RC[round] & 0xFF); + /*===Key state update===*/ + T6 = (short)(((W[6] & 0xFFFF) >>> 2) | ((W[6] & 0xFFFF) << 14)); + T7 = (short)(((W[7] & 0xFFFF) >>> 12) | ((W[7] & 0xFFFF) << 4)); + W[7] = W[5]; + W[6] = W[4]; + W[5] = W[3]; + W[4] = W[2]; + W[3] = W[1]; + W[2] = W[0]; + W[1] = T7; + W[0] = T6; + } + C[0] = (byte)(S[0] >>> 24); + C[1] = (byte)(S[0] >>> 16); + C[2] = (byte)(S[0] >>> 8); + C[3] = (byte)(S[0]); + C[4] = (byte)(S[1] >>> 24); + C[5] = (byte)(S[1] >>> 16); + C[6] = (byte)(S[1] >>> 8); + C[7] = (byte)(S[1]); + C[8] = (byte)(S[2] >>> 24); + C[9] = (byte)(S[2] >>> 16); + C[10] = (byte)(S[2] >>> 8); + C[11] = (byte)(S[2]); + C[12] = (byte)(S[3] >>> 24); + C[13] = (byte)(S[3] >>> 16); + C[14] = (byte)(S[3] >>> 8); + C[15] = (byte)(S[3]); + } + + private void xor_block(byte[] d, int dOff, byte[] s1, byte[] s2, int s2Off, int no_of_bytes) + { + for (int i = 0; i < no_of_bytes; i++) + { + d[i + dOff] = (byte)(s1[i] ^ s2[i + s2Off]); + } + } + + private void xor_topbar_block(byte[] d, byte[] s1, byte[] s2) + { + for (int i = 0; i < 8; i++) + { + d[i] = (byte)(s1[i] ^ s2[i]); + } + System.arraycopy(s1, 8, d, 8, 8); + } + + private void double_half_block(byte[] d, byte[] s) + { + int i; + byte[] tmp = new byte[8]; + /*x^{64} + x^4 + x^3 + x + 1*/ + for (i = 0; i < 7; i++) + { + tmp[i] = (byte)(((s[i] & 0xFF) << 1) | ((s[i + 1] & 0xFF) >>> 7)); + } + tmp[7] = (byte)(((s[7] & 0xFF) << 1) ^ (((s[0] & 0xFF) >>> 7) * 27)); + System.arraycopy(tmp, 0, d, 0, 8); + } + + private void triple_half_block(byte[] d, byte[] s) + { + byte[] tmp = new byte[8]; + double_half_block(tmp, s); + for (int i = 0; i < 8; i++) + { + d[i] = (byte)(s[i] ^ tmp[i]); + } + } + + private void pho1(byte[] d, byte[] Y, byte[] M, int mOff, int no_of_bytes) + { + byte[] tmpM = new byte[16]; + //padding(tmpM, M, mOff, no_of_bytes); + byte[] tmp = new byte[16]; + if (no_of_bytes == 0) + { + tmpM[0] = (byte)0x80; + } + else if (no_of_bytes < 16) + { + System.arraycopy(M, mOff, tmpM, 0, no_of_bytes); + tmpM[no_of_bytes] = (byte)0x80; + } + else + { + System.arraycopy(M, mOff, tmpM, 0, no_of_bytes); + } + int i; + //G(Y, Y); + /*Y[1],Y[2] -> Y[2],Y[1]<<<1*/ + System.arraycopy(Y, 8, tmp, 0, 8); + for (i = 0; i < 7; i++) + { + tmp[i + 8] = (byte)((Y[i] & 0xFF) << 1 | (Y[i + 1] & 0xFF) >>> 7); + } + tmp[15] = (byte)((Y[7] & 0xFF) << 1 | (Y[0] & 0xFF) >>> 7); + System.arraycopy(tmp, 0, Y, 0, 16); + xor_block(d, 0, Y, tmpM, 0, 16); + } + + private void pho(byte[] Y, byte[] M, int mOff, byte[] X, byte[] C, int cOff, int no_of_bytes) + { + xor_block(C, cOff, Y, M, mOff, no_of_bytes); + pho1(X, Y, M, mOff, no_of_bytes); + } + + private void phoprime(byte[] Y, byte[] C, int cOff, byte[] X, byte[] M, int mOff, int no_of_bytes) + { + xor_block(M, mOff, Y, C, cOff, no_of_bytes); + pho1(X, Y, M, mOff, no_of_bytes); + } + + private void processAAD(boolean emptyM) + { + byte[] a = aadData.toByteArray(); + int alen = aadData.size(); + int aOff = 0; + boolean emptyA = (alen == 0); + /*Process AD*/ + /*non-empty A*/ + /*full blocks*/ + while (alen > 16) + { + /* X[i] = (A[i] + G(Y[i-1])) + offset */ + pho1(input, Y, a, aOff, 16); + /* offset = 2*offset */ + double_half_block(offset, offset); + xor_topbar_block(input, input, offset); + /* Y[i] = E(X[i]) */ + giftb128(input, k, Y); + aOff += 16; + alen -= 16; + } + /* last byte[] */ + /* full byte[]: offset = 3*offset */ + /* partial byte[]: offset = 3^2*offset */ + triple_half_block(offset, offset); + if (((alen & 15) != 0) || emptyA) + { + triple_half_block(offset, offset); + } + if (emptyM) + { + /* empty M: offset = 3^2*offset */ + triple_half_block(offset, offset); + triple_half_block(offset, offset); + } + /* X[i] = (pad(A[i]) + G(Y[i-1])) + offset */ + pho1(input, Y, a, aOff, alen); + xor_topbar_block(input, input, offset); + /* Y[a] = E(X[a]) */ + giftb128(input, k, Y); + } + + private int cofb_crypt(byte[] output, int outOff, byte[] k, byte[] intputM, int inOff, int inlen) + { + int rv = 0; + /* Process M */ + /* full byte[]s */ + while (inlen > 16) + { + double_half_block(offset, offset); + /* C[i] = Y[i+a-1] + M[i]*/ + /* X[i] = M[i] + G(Y[i+a-1]) + offset */ + if (forEncryption) + { + pho(Y, intputM, inOff, input, output, outOff, 16); + } + else + { + phoprime(Y, intputM, inOff, input, output, outOff, 16); + } + xor_topbar_block(input, input, offset); + /* Y[i] = E(X[i+a]) */ + giftb128(input, k, Y); + inOff += 16; + outOff += 16; + inlen -= 16; + rv += 16; + encrypted = true; + } + return rv; + } + + @Override + public BlockCipher getUnderlyingCipher() + { + return null; + } + + @Override + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + this.forEncryption = forEncryption; + if (!(params instanceof ParametersWithIV)) + { + throw new IllegalArgumentException("Gift-Cofb init parameters must include an IV"); + } + ParametersWithIV ivParams = (ParametersWithIV)params; + npub = ivParams.getIV(); + if (npub == null || npub.length != 16) + { + throw new IllegalArgumentException("Gift-Cofb requires exactly 16 bytes of IV"); + } + if (!(ivParams.getParameters() instanceof KeyParameter)) + { + throw new IllegalArgumentException("Gift-Cofb init parameters must include a key"); + } + KeyParameter key = (KeyParameter)ivParams.getParameters(); + k = key.getKey(); + if (k.length != 16) + { + throw new IllegalArgumentException("Gift-Cofb key must be 128 bits long"); + } + CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties( + this.getAlgorithmName(), 128, params, Utils.getPurpose(forEncryption))); + /*Mask-Gen*/ + Y = new byte[CRYPTO_ABYTES]; + input = new byte[16]; + offset = new byte[8]; + initialised = true; + reset(false); + } + + @Override + public String getAlgorithmName() + { + return "GIFT-COFB AEAD"; + } + + @Override + public void processAADByte(byte in) + { + if (encrypted) + { + throw new IllegalArgumentException("Gift-Cofb: AAD cannot be added after reading a full block(" + + CRYPTO_ABYTES + " bytes) of input for " + (forEncryption ? "encryption" : "decryption")); + } + aadData.write(in); + } + + @Override + public void processAADBytes(byte[] in, int inOff, int len) + { + if (encrypted) + { + throw new IllegalArgumentException("Gift-Cofb: AAD cannot be added after reading a full block(" + + CRYPTO_ABYTES + " bytes) of input for " + (forEncryption ? "encryption" : "decryption")); + } + if (inOff + len > in.length) + { + throw new DataLengthException("Gift-Cofb input buffer too short"); + } + aadData.write(in, inOff, len); + } + + @Override + public int processByte(byte in, byte[] out, int outOff) + throws DataLengthException + { + if (!initialised) + { + throw new IllegalArgumentException("Gift-Cofb needs call init function before processByte"); + } + message.write(in); + return 0; + } + + @Override + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) + throws DataLengthException + { + if (!initialised) + { + throw new IllegalArgumentException("Gift-Cofb needs call init function before processBytes"); + } + if (inOff + len > in.length) + { + throw new DataLengthException("Gift-Cofb input buffer too short"); + } + message.write(in, inOff, len); + int inlen = message.size() - (forEncryption ? 0 : 16); + int rv = inlen - (inlen & 15); + if (outOff + rv > out.length) + { + throw new OutputLengthException("output buffer is too short"); + } + rv = 0; + if (inlen > 16) + { + processAAD(false); + encrypted = true; + byte[] input = message.toByteArray(); + rv = cofb_crypt(out, outOff, k, input, 0, inlen); + if (rv < inlen) + { + message.reset(); + message.write(input, rv, inlen - rv + (forEncryption ? 0 : 16)); + } + } + return rv; + } + + @Override + public int doFinal(byte[] output, int outOff) + throws IllegalStateException, InvalidCipherTextException + { + if (!initialised) + { + throw new IllegalArgumentException("Gift-Cofb needs call init function before doFinal"); + } + int inlen = message.size() - (forEncryption ? 0 : CRYPTO_ABYTES); + if ((forEncryption && inlen + CRYPTO_ABYTES + outOff > output.length) || + (!forEncryption && inlen + outOff > output.length)) + { + throw new OutputLengthException("output buffer is too short"); + } + + if (!encrypted) + { + processAAD(inlen == 0); + } + int inOff = 0; + byte[] intputM = message.toByteArray(); + + if (encrypted || inlen != 0) + { + /* full block: offset = 3*offset */ + /* empty data / partial block: offset = 3^2*offset */ + triple_half_block(offset, offset); + if ((inlen & 15) != 0) + { + triple_half_block(offset, offset); + } + /* last block */ + /* C[m] = Y[m+a-1] + M[m]*/ + /* X[a+m] = M[m] + G(Y[m+a-1]) + offset */ + if (forEncryption) + { + pho(Y, intputM, inOff, input, output, outOff, inlen); + outOff += inlen; + } + else + { + phoprime(Y, intputM, inOff, input, output, outOff, inlen); + inOff += inlen; + } + xor_topbar_block(input, input, offset); + /* T = E(X[m+a]) */ + giftb128(input, k, Y); + } + if (forEncryption) + { + System.arraycopy(Y, 0, output, outOff, CRYPTO_ABYTES); + mac = new byte[CRYPTO_ABYTES]; + System.arraycopy(Y, 0, mac, 0, CRYPTO_ABYTES); + inlen += CRYPTO_ABYTES; + } + else + { + for (int i = 0; i < CRYPTO_ABYTES; ++i) + { + if (Y[i] != intputM[inOff + i]) + { + throw new InvalidCipherTextException("mac check in Gift-Cofb failed"); + } + } + } + reset(false); + return inlen; + } + + @Override + public byte[] getMac() + { + return mac; + } + + @Override + public int getUpdateOutputSize(int len) + { + if (!initialised) + { + throw new IllegalArgumentException("Gift-Cofb needs call init function before getUpdateOutputSize"); + } + int totalData = message.size() + len; + if (!forEncryption) + { + if (totalData < CRYPTO_ABYTES) + { + return 0; + } + totalData -= CRYPTO_ABYTES; + } + return totalData - totalData % CRYPTO_ABYTES; + } + + @Override + public int getOutputSize(int len) + { + if (!initialised) + { + throw new IllegalArgumentException("Gift-Cofb needs call init function before getOutputSize"); + } + int totalData = message.size() + len; + if (forEncryption) + { + return totalData + CRYPTO_ABYTES; + } + return Math.max(0, totalData - CRYPTO_ABYTES); + } + + @Override + public void reset() + { + reset(true); + } + + private void reset(boolean clearMac) + { + if (!initialised) + { + throw new IllegalArgumentException("Gift-Cofb needs call init function before reset"); + } + if (clearMac) + { + mac = null; + } + /*nonce is 128-bit*/ + System.arraycopy(npub, 0, input, 0, 16); + giftb128(input, k, Y); + System.arraycopy(Y, 0, offset, 0, 8); + aadData.reset(); + message.reset(); + encrypted = false; + } +} diff --git a/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java b/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java index 53fc71bf17..386fc32d6d 100644 --- a/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java +++ b/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java @@ -163,7 +163,7 @@ protected void fail( throw new TestFailedException(SimpleTestResult.failed(this, message, throwable)); } - protected void fail( + public void fail( String message, Object expected, Object found) @@ -178,7 +178,7 @@ protected boolean areEqual( return Arrays.areEqual(a, b); } - protected boolean areEqual(byte[] a, int aFromIndex, int aToIndex, byte[] b, int bFromIndex, int bToIndex) + public boolean areEqual(byte[] a, int aFromIndex, int aToIndex, byte[] b, int bFromIndex, int bToIndex) { return Arrays.areEqual(a, aFromIndex, aToIndex, b, bFromIndex, bToIndex); } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java index 050c7546d3..92f1d00840 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java @@ -1,10 +1,16 @@ package org.bouncycastle.crypto.test; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Random; import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.CipherKeyGenerator; +import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.DefaultBufferedBlockCipher; import org.bouncycastle.crypto.InvalidCipherTextException; @@ -13,7 +19,9 @@ import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; import org.bouncycastle.util.test.SimpleTestResult; import org.bouncycastle.util.test.TestFailedException; @@ -413,4 +421,79 @@ static void checkAEADCipherMultipleBlocks(SimpleTest test, int DATALEN, int PART /* Check that we have the same result */ test.isTrue("cipher text check", Arrays.areEqual(myData, myResult)); } + + static void implTestVectorsEngine(AEADCipher cipher, String path, String filename, SimpleTest test) + throws Exception + { + Random random = new Random(); + InputStream src = TestResourceFinder.findTestResource(path, filename); + BufferedReader bin = new BufferedReader(new InputStreamReader(src)); + String line; + HashMap map = new HashMap(); + while ((line = bin.readLine()) != null) + { + int a = line.indexOf('='); + if (a < 0) + { + int count = Integer.parseInt((String)map.get("Count")); +// if (count != 34) +// { +// continue; +// } + byte[] key = Hex.decode((String)map.get("Key")); + byte[] nonce = Hex.decode((String)map.get("Nonce")); + byte[] ad = Hex.decode((String)map.get("AD")); + byte[] pt = Hex.decode((String)map.get("PT")); + byte[] ct = Hex.decode((String)map.get("CT")); + + CipherParameters parameters = new ParametersWithIV(new KeyParameter(key), nonce); + + // Encrypt + { + cipher.init(true, parameters); + + byte[] rv = new byte[cipher.getOutputSize(pt.length)]; + random.nextBytes(rv); // should overwrite any existing data + + cipher.processAADBytes(ad, 0, ad.length); + int len = cipher.processBytes(pt, 0, pt.length, rv, 0); + len += cipher.doFinal(rv, len); + + if (!test.areEqual(rv, 0, len, ct, 0, ct.length)) + { + mismatch("Keystream " + map.get("Count"), (String)map.get("CT"), rv, test); + } + } + + // Decrypt + { + cipher.init(false, parameters); + + byte[] rv = new byte[cipher.getOutputSize(ct.length)]; + random.nextBytes(rv); // should overwrite any existing data + + cipher.processAADBytes(ad, 0, ad.length); + int len = cipher.processBytes(ct, 0, ct.length, rv, 0); + len += cipher.doFinal(rv, len); + + if (!test.areEqual(rv, 0, len, pt, 0, pt.length)) + { + mismatch("Reccover Keystream " + map.get("Count"), (String)map.get("PT"), rv, test); + } + } + //System.out.println("pass "+ count); + map.clear(); + } + else + { + map.put(line.substring(0, a).trim(), line.substring(a + 1).trim()); + } + } + } + + static void mismatch(String name, String expected, byte[] found, SimpleTest test) + throws Exception + { + test.fail("mismatch on " + name, expected, new String(Hex.encode(found))); + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java b/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java new file mode 100644 index 0000000000..3e08c4954a --- /dev/null +++ b/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java @@ -0,0 +1,387 @@ +package org.bouncycastle.crypto.test; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Random; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.OutputLengthException; +import org.bouncycastle.crypto.engines.GiftCofbEngine; +import org.bouncycastle.crypto.modes.AEADBlockCipher; +import org.bouncycastle.crypto.params.AEADParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +public class GiftCofbTest + extends SimpleTest +{ + public String getName() + { + return "GiftCofb"; + } + + public void performTest() + throws Exception + { + CipherTest.implTestVectorsEngine(new GiftCofbEngine(), "crypto/giftcofb", "giftcofb_LWC_AEAD_KAT_128_128.txt", this); + //testExceptions(new GiftCofbEngine(), 16, 16, 16); + } + + + private void testVectors() + throws Exception + { + GiftCofbEngine GiftCofb = new GiftCofbEngine(); + CipherParameters params; + InputStream src = GiftCofbTest.class.getResourceAsStream("/org/bouncycastle/crypto/test/giftcofb_LWC_AEAD_KAT_128_128.txt"); + BufferedReader bin = new BufferedReader(new InputStreamReader(src)); + String line; + byte[] rv; + HashMap map = new HashMap(); + while ((line = bin.readLine()) != null) + { + int a = line.indexOf('='); + if (a < 0) + { +// if (!map.get("Count").equals("529")) +// { +// continue; +// } + byte[] key = Hex.decode(map.get("Key")); + byte[] nonce = Hex.decode(map.get("Nonce")); + byte[] ad = Hex.decode(map.get("AD")); + byte[] pt = Hex.decode(map.get("PT")); + byte[] ct = Hex.decode(map.get("CT")); + params = new ParametersWithIV(new KeyParameter(key), nonce); + GiftCofb.init(true, params); + GiftCofb.processAADBytes(ad, 0, ad.length); + rv = new byte[GiftCofb.getOutputSize(pt.length)]; + int len = GiftCofb.processBytes(pt, 0, pt.length, rv, 0); + GiftCofb.doFinal(rv, len); + if (!areEqual(rv, ct)) + { + mismatch("Keystream " + map.get("Count"), (String)map.get("CT"), rv); + } +// else +// { +// System.out.println("Keystream " + map.get("Count") + " pass"); +// } + GiftCofb.reset(); + GiftCofb.init(false, params); + //Decrypt + GiftCofb.processAADBytes(ad, 0, ad.length); + rv = new byte[pt.length]; + len = GiftCofb.processBytes(ct, 0, ct.length, rv, 0); + GiftCofb.doFinal(rv, len); + byte[] pt_recovered = new byte[pt.length]; + System.arraycopy(rv, 0, pt_recovered, 0, pt.length); + if (!areEqual(pt, pt_recovered)) + { + mismatch("Reccover Keystream " + map.get("Count"), (String)map.get("PT"), pt_recovered); + } + //System.out.println("Keystream " + map.get("Count") + " pass"); + GiftCofb.reset(); + map.clear(); + + } + else + { + map.put(line.substring(0, a).trim(), line.substring(a + 1).trim()); + } + } + System.out.println("GiftCofb AEAD pass"); + } + + private void testExceptions(AEADBlockCipher aeadBlockCipher, int keysize, int ivsize, int blocksize) + throws Exception + { + CipherParameters params; + byte[] k = new byte[keysize]; + byte[] iv = new byte[ivsize]; + byte[] m = new byte[0]; + params = new ParametersWithIV(new KeyParameter(k), iv); + + byte[] c1 = new byte[32]; + try + { + aeadBlockCipher.processBytes(m, 0, m.length, c1, 0); + fail(aeadBlockCipher.getAlgorithmName() + " need to be initialed before processBytes"); + } + catch (IllegalArgumentException e) + { + //expected + } + + try + { + aeadBlockCipher.processByte((byte)0, c1, 0); + fail(aeadBlockCipher.getAlgorithmName() + " need to be initialed before processByte"); + } + catch (IllegalArgumentException e) + { + //expected + } + + try + { + aeadBlockCipher.reset(); + fail(aeadBlockCipher.getAlgorithmName() + " need to be initialed before reset"); + } + catch (IllegalArgumentException e) + { + //expected + } + + try + { + aeadBlockCipher.doFinal(c1, m.length); + fail(aeadBlockCipher.getAlgorithmName() + " need to be initialed before dofinal"); + } + catch (IllegalArgumentException e) + { + //expected + } + + try + { + aeadBlockCipher.getMac(); + aeadBlockCipher.getAlgorithmName(); +// aeadBlockCipher.getOutputSize(0); +// aeadBlockCipher.getUpdateOutputSize(0); + } + catch (IllegalArgumentException e) + { + //expected + fail(aeadBlockCipher.getAlgorithmName() + " functions can be called before initialisation"); + } + Random rand = new Random(); + int randomNum; + while ((randomNum = rand.nextInt(100)) == keysize) ; + byte[] k1 = new byte[randomNum]; + while ((randomNum = rand.nextInt(100)) == ivsize) ; + byte[] iv1 = new byte[randomNum]; + try + { + aeadBlockCipher.init(true, new ParametersWithIV(new KeyParameter(k1), iv)); + fail(aeadBlockCipher.getAlgorithmName() + " k size does not match"); + } + catch (IllegalArgumentException e) + { + //expected + } + try + { + aeadBlockCipher.init(true, new ParametersWithIV(new KeyParameter(k), iv1)); + fail(aeadBlockCipher.getAlgorithmName() + "iv size does not match"); + } + catch (IllegalArgumentException e) + { + //expected + } + + try + { + aeadBlockCipher.init(true, new AEADParameters(new KeyParameter(k), 0, iv)); + fail(aeadBlockCipher.getAlgorithmName() + " wrong type of CipherParameters"); + } + catch (IllegalArgumentException e) + { + //expected + } + + aeadBlockCipher.init(true, params); + c1 = new byte[aeadBlockCipher.getOutputSize(0)]; + try + { + aeadBlockCipher.doFinal(c1, m.length); + } + catch (Exception e) + { + fail(aeadBlockCipher.getAlgorithmName() + " allows no input for AAD and plaintext"); + } + byte[] mac2 = aeadBlockCipher.getMac(); + if (mac2 == null) + { + fail(aeadBlockCipher.getAlgorithmName() + ": mac should not be empty after dofinal"); + } + if (!areEqual(mac2, c1)) + { + fail(aeadBlockCipher.getAlgorithmName() + ": mac should be equal when calling dofinal and getMac"); + } + + aeadBlockCipher.reset(); + aeadBlockCipher.processAADByte((byte)0); + byte[] mac1 = new byte[aeadBlockCipher.getOutputSize(0)]; + aeadBlockCipher.doFinal(mac1, 0); + if (areEqual(mac1, mac2)) + { + fail(aeadBlockCipher.getAlgorithmName() + ": mac should not match"); + } + aeadBlockCipher.reset(); + aeadBlockCipher.processBytes(new byte[blocksize + 1], 0, blocksize + 1, new byte[blocksize + 1], 0); + try + { + aeadBlockCipher.processAADByte((byte)0); + fail(aeadBlockCipher.getAlgorithmName() + ": processAADByte(s) cannot be called after encryption/decryption"); + } + catch (IllegalArgumentException e) + { + //expected + } + try + { + aeadBlockCipher.processAADBytes(new byte[]{0}, 0, 1); + fail(aeadBlockCipher.getAlgorithmName() + ": processAADByte(s) cannot be called once only"); + } + catch (IllegalArgumentException e) + { + //expected + } + + aeadBlockCipher.reset(); + try + { + aeadBlockCipher.processAADBytes(new byte[]{0}, 1, 1); + fail(aeadBlockCipher.getAlgorithmName() + ": input for processAADBytes is too short"); + } + catch (DataLengthException e) + { + //expected + } + try + { + aeadBlockCipher.processBytes(new byte[]{0}, 1, 1, c1, 0); + fail(aeadBlockCipher.getAlgorithmName() + ": input for processBytes is too short"); + } + catch (DataLengthException e) + { + //expected + } + try + { + aeadBlockCipher.processBytes(new byte[blocksize + 1], 0, blocksize + 1, new byte[blocksize + 1], blocksize >> 1); + fail(aeadBlockCipher.getAlgorithmName() + ": output for processBytes is too short"); + } + catch (OutputLengthException e) + { + //expected + } + try + { + aeadBlockCipher.doFinal(new byte[2], 2); + fail(aeadBlockCipher.getAlgorithmName() + ": output for dofinal is too short"); + } + catch (DataLengthException e) + { + //expected + } + + mac1 = new byte[aeadBlockCipher.getOutputSize(0)]; + mac2 = new byte[aeadBlockCipher.getOutputSize(0)]; + aeadBlockCipher.reset(); + aeadBlockCipher.processAADBytes(new byte[]{0, 0}, 0, 2); + aeadBlockCipher.doFinal(mac1, 0); + aeadBlockCipher.reset(); + aeadBlockCipher.processAADByte((byte)0); + aeadBlockCipher.processAADByte((byte)0); + aeadBlockCipher.doFinal(mac2, 0); + if (!areEqual(mac1, mac2)) + { + fail(aeadBlockCipher.getAlgorithmName() + ": mac should match for the same AAD with different ways of inputing"); + } + + byte[] c2 = new byte[aeadBlockCipher.getOutputSize(10)]; + byte[] c3 = new byte[aeadBlockCipher.getOutputSize(10) + 2]; + + byte[] aad2 = {0, 1, 2, 3, 4}; + byte[] aad3 = {0, 0, 1, 2, 3, 4, 5}; + byte[] m2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + byte[] m3 = {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + byte[] m4 = new byte[m2.length]; + aeadBlockCipher.reset(); + aeadBlockCipher.processAADBytes(aad2, 0, aad2.length); + int offset = aeadBlockCipher.processBytes(m2, 0, m2.length, c2, 0); + aeadBlockCipher.doFinal(c2, offset); + aeadBlockCipher.reset(); + aeadBlockCipher.processAADBytes(aad3, 1, aad2.length); + offset = aeadBlockCipher.processBytes(m3, 1, m2.length, c3, 1); + aeadBlockCipher.doFinal(c3, offset + 1); + byte[] c3_partial = new byte[c2.length]; + System.arraycopy(c3, 1, c3_partial, 0, c2.length); + if (!areEqual(c2, c3_partial)) + { + fail(aeadBlockCipher.getAlgorithmName() + ": mac should match for the same AAD and message with different offset for both input and output"); + } + aeadBlockCipher.reset(); + aeadBlockCipher.init(false, params); + aeadBlockCipher.processAADBytes(aad2, 0, aad2.length); + offset = aeadBlockCipher.processBytes(c2, 0, c2.length, m4, 0); + aeadBlockCipher.doFinal(m4, offset); + if (!areEqual(m2, m4)) + { + fail(aeadBlockCipher.getAlgorithmName() + ": The encryption and decryption does not recover the plaintext"); + } + + c2[c2.length - 1] ^= 1; + aeadBlockCipher.reset(); + aeadBlockCipher.init(false, params); + aeadBlockCipher.processAADBytes(aad2, 0, aad2.length); + offset = aeadBlockCipher.processBytes(c2, 0, c2.length, m4, 0); + try + { + aeadBlockCipher.doFinal(m4, offset); + fail(aeadBlockCipher.getAlgorithmName() + ": The decryption should fail"); + } + catch (InvalidCipherTextException e) + { + //expected; + } + + byte[] m7 = new byte[blocksize * 2]; + for (int i = 0; i < m7.length; ++i) + { + m7[i] = (byte)rand.nextInt(); + } + aeadBlockCipher.init(true, params); + byte[] c7 = new byte[aeadBlockCipher.getOutputSize(m7.length)]; + byte[] c8 = new byte[c7.length]; + byte[] c9 = new byte[c7.length]; + + aeadBlockCipher.processAADBytes(aad2, 0, aad2.length); + offset = aeadBlockCipher.processBytes(m7, 0, m7.length, c7, 0); + aeadBlockCipher.doFinal(c7, offset); + aeadBlockCipher.reset(); + aeadBlockCipher.processAADBytes(aad2, 0, aad2.length); + offset = aeadBlockCipher.processBytes(m7, 0, blocksize, c8, 0); + offset += aeadBlockCipher.processBytes(m7, blocksize, m7.length - blocksize, c8, offset); + aeadBlockCipher.doFinal(c8, offset); + aeadBlockCipher.reset(); + int split = rand.nextInt(blocksize * 2); + aeadBlockCipher.processAADBytes(aad2, 0, aad2.length); + offset = aeadBlockCipher.processBytes(m7, 0, split, c9, 0); + offset += aeadBlockCipher.processBytes(m7, split, m7.length - split, c9, offset); + aeadBlockCipher.doFinal(c9, offset); + if (!areEqual(c7, c8) || !areEqual(c7, c9)) + { + fail(aeadBlockCipher.getAlgorithmName() + ": Splitting input of plaintext should output the same ciphertext"); + } + System.out.println(aeadBlockCipher.getAlgorithmName() + " test Exceptions pass"); + } + + + private void mismatch(String name, String expected, byte[] found) + { + fail("mismatch on " + name, expected, new String(Hex.encode(found))); + } + + public static void main(String[] args) + { + runTest(new GiftCofbTest()); + } +} From 0244e2d242ea08c4eaacad6782fe8337eac90ec9 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 17 Jan 2025 14:25:44 +1030 Subject: [PATCH 021/890] The work on GiftCofbEngine is done. --- .../crypto/engines/AEADBufferBaseEngine.java | 17 +- .../crypto/engines/AsconBaseEngine.java | 2 +- .../crypto/engines/ElephantEngine.java | 2 +- .../crypto/engines/GiftCofbEngine.java | 389 +++++------------- .../bouncycastle/util/test/SimpleTest.java | 2 +- .../bouncycastle/crypto/test/CipherTest.java | 385 ++++++++++++++++- .../crypto/test/GiftCofbTest.java | 378 ++--------------- .../bouncycastle/crypto/test/SparkleTest.java | 1 - 8 files changed, 544 insertions(+), 632 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 7bef3ea765..924d677c2c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -227,7 +227,7 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output, int out { ensureSufficientInputBuffer(input, inOff, len); int available, resultLength; - if (checkData()) + if (checkData(false)) { resultLength = 0; available = processor.getUpdateOutputSize(len) + m_bufPos; @@ -351,7 +351,7 @@ private int processDecryptionWithSmallMacSize(byte[] input, int inOff, int len, public int doFinal(byte[] output, int outOff) throws IllegalStateException, InvalidCipherTextException { - boolean forEncryption = checkData(); + boolean forEncryption = checkData(true); int resultLength; if (forEncryption) { @@ -401,8 +401,6 @@ public int getUpdateOutputSize(int len) { case DecInit: case DecAad: - total = Math.max(0, total - MAC_SIZE); - break; case DecData: case DecFinal: total = Math.max(0, total + m_bufPos - MAC_SIZE); @@ -425,7 +423,7 @@ public int getOutputSize(int len) { case DecInit: case DecAad: - return Math.max(0, total - MAC_SIZE); +// return Math.max(0, total + m_bufPos- MAC_SIZE); case DecData: case DecFinal: return Math.max(0, total + m_bufPos - MAC_SIZE); @@ -457,17 +455,17 @@ protected void checkAAD() } } - protected boolean checkData() + protected boolean checkData(boolean isDoFinal) { switch (m_state) { case DecInit: case DecAad: - finishAAD(State.DecData); + finishAAD(State.DecData, isDoFinal); return false; case EncInit: case EncAad: - finishAAD(State.EncData); + finishAAD(State.EncData, isDoFinal); return true; case DecData: return false; @@ -480,7 +478,8 @@ protected boolean checkData() } } - protected void finishAAD(State nextState) + //TODO: override this for aadFinished + protected void finishAAD(State nextState, boolean isDoFinal) { // State indicates whether we ever received AAD switch (m_state) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index f0bf5218f8..377d8c7fd0 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -67,7 +67,7 @@ protected void p(int nr) protected abstract void ascon_aeadinit(); - protected void finishAAD(State nextState) + protected void finishAAD(State nextState, boolean isDofinal) { // State indicates whether we ever received AAD switch (m_state) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index dde1d5149e..a2a57d8044 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -526,7 +526,7 @@ protected void checkAAD() } } - protected boolean checkData() + protected boolean checkData(boolean isDofinal) { switch (m_state) { diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java index 35765abd47..66dd3a7661 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java @@ -1,17 +1,6 @@ package org.bouncycastle.crypto.engines; -import java.io.ByteArrayOutputStream; - -import org.bouncycastle.crypto.BlockCipher; -import org.bouncycastle.crypto.CipherParameters; -import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.InvalidCipherTextException; -import org.bouncycastle.crypto.OutputLengthException; -import org.bouncycastle.crypto.constraints.DefaultServiceProperties; -import org.bouncycastle.crypto.modes.AEADBlockCipher; -import org.bouncycastle.crypto.params.KeyParameter; -import org.bouncycastle.crypto.params.ParametersWithIV; /** * GIFT-COFB v1.1, based on the current round 3 submission, https://www.isical.ac.in/~lightweight/COFB/ @@ -20,20 +9,15 @@ */ public class GiftCofbEngine - implements AEADBlockCipher + extends AEADBufferBaseEngine { - private final int CRYPTO_ABYTES = 16; - private boolean forEncryption; - private boolean initialised = false; private byte[] npub; private byte[] k; private byte[] Y; - private byte[] mac; private byte[] input; private byte[] offset; - private boolean encrypted; - private final ByteArrayOutputStream aadData = new ByteArrayOutputStream(); - private final ByteArrayOutputStream message = new ByteArrayOutputStream(); + private int aadLen; + private int messageLen; /*Round constants*/ private final byte[] GIFT_RC = { (byte)0x01, (byte)0x03, (byte)0x07, (byte)0x0F, (byte)0x1F, (byte)0x3E, (byte)0x3D, (byte)0x3B, (byte)0x37, (byte)0x2F, @@ -42,6 +26,38 @@ public class GiftCofbEngine (byte)0x1C, (byte)0x38, (byte)0x31, (byte)0x23, (byte)0x06, (byte)0x0D, (byte)0x1B, (byte)0x36, (byte)0x2D, (byte)0x1A }; + public GiftCofbEngine() + { + super(ProcessingBufferType.Buffered); + AADBufferSize = BlockSize = MAC_SIZE = IV_SIZE = KEY_SIZE = 16; + algorithmName = "GIFT-COFB AEAD"; + m_bufferSizeDecrypt = BlockSize + MAC_SIZE; + m_buf = new byte[m_bufferSizeDecrypt]; + m_aad = new byte[AADBufferSize]; + } + + @Override + public void processAADByte(byte input) + { + aadLen++; + super.processAADByte(input); + } + + @Override + public void processAADBytes(byte[] input, int inOff, int len) + { + aadLen += len; + super.processAADBytes(input, inOff, len); + } + + @Override + public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + throws DataLengthException + { + messageLen += len; + return super.processBytes(input, inOff, len, output, outOff); + } + private int rowperm(int S, int B0_pos, int B1_pos, int B2_pos, int B3_pos) { int T = 0; @@ -210,226 +226,90 @@ private void phoprime(byte[] Y, byte[] C, int cOff, byte[] X, byte[] M, int mOff pho1(X, Y, M, mOff, no_of_bytes); } - private void processAAD(boolean emptyM) + @Override + protected void processBufferAAD(byte[] in, int inOff) + { + + pho1(input, Y, in, inOff, 16); + /* offset = 2*offset */ + double_half_block(offset, offset); + xor_topbar_block(input, input, offset); + /* Y[i] = E(X[i]) */ + giftb128(input, k, Y); + } + + //@Override + protected void finishAAD(State nextState, boolean isDoFinal) { - byte[] a = aadData.toByteArray(); - int alen = aadData.size(); - int aOff = 0; - boolean emptyA = (alen == 0); - /*Process AD*/ - /*non-empty A*/ - /*full blocks*/ - while (alen > 16) + // State indicates whether we ever received AAD + switch (m_state) { - /* X[i] = (A[i] + G(Y[i-1])) + offset */ - pho1(input, Y, a, aOff, 16); - /* offset = 2*offset */ - double_half_block(offset, offset); - xor_topbar_block(input, input, offset); - /* Y[i] = E(X[i]) */ - giftb128(input, k, Y); - aOff += 16; - alen -= 16; + case DecInit: + case DecAad: + if (!isDoFinal && messageLen <= MAC_SIZE) + { + //m_state = State.DecData; + return; + } + case EncInit: + case EncAad: + processFinalAAD(); + break; } + + m_aadPos = 0; + m_state = nextState; + } + + @Override + protected void processFinalAAD() + { + int len = messageLen - (forEncryption ? 0 : MAC_SIZE); /* last byte[] */ /* full byte[]: offset = 3*offset */ /* partial byte[]: offset = 3^2*offset */ triple_half_block(offset, offset); - if (((alen & 15) != 0) || emptyA) + if (((aadLen & 15) != 0) || m_state == State.DecInit || m_state == State.EncInit) { triple_half_block(offset, offset); } - if (emptyM) + if (len == 0) { /* empty M: offset = 3^2*offset */ triple_half_block(offset, offset); triple_half_block(offset, offset); } /* X[i] = (pad(A[i]) + G(Y[i-1])) + offset */ - pho1(input, Y, a, aOff, alen); + pho1(input, Y, m_aad, 0, m_aadPos); xor_topbar_block(input, input, offset); /* Y[a] = E(X[a]) */ giftb128(input, k, Y); } - private int cofb_crypt(byte[] output, int outOff, byte[] k, byte[] intputM, int inOff, int inlen) - { - int rv = 0; - /* Process M */ - /* full byte[]s */ - while (inlen > 16) - { - double_half_block(offset, offset); - /* C[i] = Y[i+a-1] + M[i]*/ - /* X[i] = M[i] + G(Y[i+a-1]) + offset */ - if (forEncryption) - { - pho(Y, intputM, inOff, input, output, outOff, 16); - } - else - { - phoprime(Y, intputM, inOff, input, output, outOff, 16); - } - xor_topbar_block(input, input, offset); - /* Y[i] = E(X[i+a]) */ - giftb128(input, k, Y); - inOff += 16; - outOff += 16; - inlen -= 16; - rv += 16; - encrypted = true; - } - return rv; - } - @Override - public BlockCipher getUnderlyingCipher() + protected void init(byte[] key, byte[] iv) { - return null; - } - - @Override - public void init(boolean forEncryption, CipherParameters params) - throws IllegalArgumentException - { - this.forEncryption = forEncryption; - if (!(params instanceof ParametersWithIV)) - { - throw new IllegalArgumentException("Gift-Cofb init parameters must include an IV"); - } - ParametersWithIV ivParams = (ParametersWithIV)params; - npub = ivParams.getIV(); - if (npub == null || npub.length != 16) - { - throw new IllegalArgumentException("Gift-Cofb requires exactly 16 bytes of IV"); - } - if (!(ivParams.getParameters() instanceof KeyParameter)) - { - throw new IllegalArgumentException("Gift-Cofb init parameters must include a key"); - } - KeyParameter key = (KeyParameter)ivParams.getParameters(); - k = key.getKey(); - if (k.length != 16) - { - throw new IllegalArgumentException("Gift-Cofb key must be 128 bits long"); - } - CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties( - this.getAlgorithmName(), 128, params, Utils.getPurpose(forEncryption))); - /*Mask-Gen*/ - Y = new byte[CRYPTO_ABYTES]; + npub = iv; + k = key; + Y = new byte[BlockSize]; input = new byte[16]; offset = new byte[8]; - initialised = true; + m_state = forEncryption ? State.EncInit : State.DecInit; reset(false); } - @Override - public String getAlgorithmName() - { - return "GIFT-COFB AEAD"; - } - - @Override - public void processAADByte(byte in) - { - if (encrypted) - { - throw new IllegalArgumentException("Gift-Cofb: AAD cannot be added after reading a full block(" + - CRYPTO_ABYTES + " bytes) of input for " + (forEncryption ? "encryption" : "decryption")); - } - aadData.write(in); - } - - @Override - public void processAADBytes(byte[] in, int inOff, int len) - { - if (encrypted) - { - throw new IllegalArgumentException("Gift-Cofb: AAD cannot be added after reading a full block(" + - CRYPTO_ABYTES + " bytes) of input for " + (forEncryption ? "encryption" : "decryption")); - } - if (inOff + len > in.length) - { - throw new DataLengthException("Gift-Cofb input buffer too short"); - } - aadData.write(in, inOff, len); - } - - @Override - public int processByte(byte in, byte[] out, int outOff) - throws DataLengthException - { - if (!initialised) - { - throw new IllegalArgumentException("Gift-Cofb needs call init function before processByte"); - } - message.write(in); - return 0; - } - - @Override - public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) - throws DataLengthException - { - if (!initialised) - { - throw new IllegalArgumentException("Gift-Cofb needs call init function before processBytes"); - } - if (inOff + len > in.length) - { - throw new DataLengthException("Gift-Cofb input buffer too short"); - } - message.write(in, inOff, len); - int inlen = message.size() - (forEncryption ? 0 : 16); - int rv = inlen - (inlen & 15); - if (outOff + rv > out.length) - { - throw new OutputLengthException("output buffer is too short"); - } - rv = 0; - if (inlen > 16) - { - processAAD(false); - encrypted = true; - byte[] input = message.toByteArray(); - rv = cofb_crypt(out, outOff, k, input, 0, inlen); - if (rv < inlen) - { - message.reset(); - message.write(input, rv, inlen - rv + (forEncryption ? 0 : 16)); - } - } - return rv; - } @Override - public int doFinal(byte[] output, int outOff) - throws IllegalStateException, InvalidCipherTextException + protected void processFinalBlock(byte[] output, int outOff) { - if (!initialised) - { - throw new IllegalArgumentException("Gift-Cofb needs call init function before doFinal"); - } - int inlen = message.size() - (forEncryption ? 0 : CRYPTO_ABYTES); - if ((forEncryption && inlen + CRYPTO_ABYTES + outOff > output.length) || - (!forEncryption && inlen + outOff > output.length)) - { - throw new OutputLengthException("output buffer is too short"); - } - - if (!encrypted) - { - processAAD(inlen == 0); - } int inOff = 0; - byte[] intputM = message.toByteArray(); - - if (encrypted || inlen != 0) + int len = messageLen - (forEncryption ? 0 : MAC_SIZE); + if (len != 0) { /* full block: offset = 3*offset */ /* empty data / partial block: offset = 3^2*offset */ triple_half_block(offset, offset); - if ((inlen & 15) != 0) + if ((len & 15) != 0) { triple_half_block(offset, offset); } @@ -438,101 +318,58 @@ public int doFinal(byte[] output, int outOff) /* X[a+m] = M[m] + G(Y[m+a-1]) + offset */ if (forEncryption) { - pho(Y, intputM, inOff, input, output, outOff, inlen); - outOff += inlen; + pho(Y, m_buf, inOff, input, output, outOff, m_bufPos); } else { - phoprime(Y, intputM, inOff, input, output, outOff, inlen); - inOff += inlen; + phoprime(Y, m_buf, inOff, input, output, outOff, m_bufPos); } xor_topbar_block(input, input, offset); /* T = E(X[m+a]) */ giftb128(input, k, Y); } - if (forEncryption) - { - System.arraycopy(Y, 0, output, outOff, CRYPTO_ABYTES); - mac = new byte[CRYPTO_ABYTES]; - System.arraycopy(Y, 0, mac, 0, CRYPTO_ABYTES); - inlen += CRYPTO_ABYTES; - } - else - { - for (int i = 0; i < CRYPTO_ABYTES; ++i) - { - if (Y[i] != intputM[inOff + i]) - { - throw new InvalidCipherTextException("mac check in Gift-Cofb failed"); - } - } - } - reset(false); - return inlen; - } - - @Override - public byte[] getMac() - { - return mac; + mac = new byte[BlockSize]; + System.arraycopy(Y, 0, mac, 0, BlockSize); } - @Override - public int getUpdateOutputSize(int len) - { - if (!initialised) - { - throw new IllegalArgumentException("Gift-Cofb needs call init function before getUpdateOutputSize"); - } - int totalData = message.size() + len; - if (!forEncryption) - { - if (totalData < CRYPTO_ABYTES) - { - return 0; - } - totalData -= CRYPTO_ABYTES; - } - return totalData - totalData % CRYPTO_ABYTES; - } @Override - public int getOutputSize(int len) + protected void processBufferEncrypt(byte[] inputM, int inOff, byte[] output, int outOff) { - if (!initialised) - { - throw new IllegalArgumentException("Gift-Cofb needs call init function before getOutputSize"); - } - int totalData = message.size() + len; - if (forEncryption) - { - return totalData + CRYPTO_ABYTES; - } - return Math.max(0, totalData - CRYPTO_ABYTES); + /* Process M */ + /* full byte[]s */ + double_half_block(offset, offset); + /* C[i] = Y[i+a-1] + M[i]*/ + /* X[i] = M[i] + G(Y[i+a-1]) + offset */ + pho(Y, inputM, inOff, input, output, outOff, BlockSize); + xor_topbar_block(input, input, offset); + /* Y[i] = E(X[i+a]) */ + giftb128(input, k, Y); } @Override - public void reset() + protected void processBufferDecrypt(byte[] inputM, int inOff, byte[] output, int outOff) { - reset(true); + /* Process M */ + /* full byte[]s */ + double_half_block(offset, offset); + /* C[i] = Y[i+a-1] + M[i]*/ + /* X[i] = M[i] + G(Y[i+a-1]) + offset */ + phoprime(Y, inputM, inOff, input, output, outOff, BlockSize); + xor_topbar_block(input, input, offset); + /* Y[i] = E(X[i+a]) */ + giftb128(input, k, Y); } - private void reset(boolean clearMac) + protected void reset(boolean clearMac) { - if (!initialised) - { - throw new IllegalArgumentException("Gift-Cofb needs call init function before reset"); - } - if (clearMac) - { - mac = null; - } + bufferReset(); + super.reset(clearMac); /*nonce is 128-bit*/ - System.arraycopy(npub, 0, input, 0, 16); + System.arraycopy(npub, 0, input, 0, IV_SIZE); giftb128(input, k, Y); System.arraycopy(Y, 0, offset, 0, 8); - aadData.reset(); - message.reset(); - encrypted = false; + aadLen = 0; + messageLen = 0; } } diff --git a/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java b/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java index 386fc32d6d..2d29635e54 100644 --- a/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java +++ b/core/src/main/java/org/bouncycastle/util/test/SimpleTest.java @@ -16,7 +16,7 @@ private TestResult success() return SimpleTestResult.successful(this, "Okay"); } - protected void fail( + public void fail( String message) { throw new TestFailedException(SimpleTestResult.failed(this, message)); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java index 92f1d00840..41ff9680ed 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java @@ -15,6 +15,7 @@ import org.bouncycastle.crypto.DefaultBufferedBlockCipher; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.KeyGenerationParameters; +import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.modes.AEADCipher; import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; @@ -436,7 +437,7 @@ static void implTestVectorsEngine(AEADCipher cipher, String path, String filenam if (a < 0) { int count = Integer.parseInt((String)map.get("Count")); -// if (count != 34) +// if (count != 562) // { // continue; // } @@ -481,7 +482,7 @@ static void implTestVectorsEngine(AEADCipher cipher, String path, String filenam mismatch("Reccover Keystream " + map.get("Count"), (String)map.get("PT"), rv, test); } } - //System.out.println("pass "+ count); + System.out.println("pass "+ count); map.clear(); } else @@ -496,4 +497,384 @@ static void mismatch(String name, String expected, byte[] found, SimpleTest test { test.fail("mismatch on " + name, expected, new String(Hex.encode(found))); } + + static void implTestBufferingEngine(int keySize, int ivSize, final int macSize, SimpleTest test, Instance instance) + throws Exception + { + Random random = new Random(); + + int plaintextLength = 256; + byte[] plaintext = new byte[plaintextLength]; + random.nextBytes(plaintext); + + AEADCipher cipher0 = instance.createInstance(); + AEADParameters parameters = new AEADParameters(new KeyParameter(new byte[keySize]), macSize, new byte[ivSize], null); + cipher0.init(true, parameters); + + byte[] ciphertext = new byte[cipher0.getOutputSize(plaintextLength)]; + random.nextBytes(ciphertext); + + int ciphertextLength = cipher0.processBytes(plaintext, 0, plaintextLength, ciphertext, 0); + ciphertextLength += cipher0.doFinal(ciphertext, ciphertextLength); + + byte[] output = new byte[ciphertextLength]; + + // Encryption + for (int split = 1; split < plaintextLength; ++split) + { + AEADCipher cipher = instance.createInstance(); + cipher.init(true, parameters); + + random.nextBytes(output); + + int length = cipher.processBytes(plaintext, 0, split, output, 0); + + if (0 != cipher.getUpdateOutputSize(0)) + { + test.fail(""); + } + + length += cipher.processBytes(plaintext, split, plaintextLength - split, output, length); + length += cipher.doFinal(output, length); + + if (!Arrays.areEqual(ciphertext, 0, ciphertextLength, output, 0, length)) + { + test.fail("encryption failed with split: " + split); + } + } + + // Decryption + for (int split = 16; split < ciphertextLength; ++split) + { + AEADCipher cipher = instance.createInstance(); + cipher.init(false, parameters); + + random.nextBytes(output); + + int length = cipher.processBytes(ciphertext, 0, split, output, 0); + + if (0 != cipher.getUpdateOutputSize(0)) + { + test.fail(""); + } + + length += cipher.processBytes(ciphertext, split, ciphertextLength - split, output, length); + length += cipher.doFinal(output, length); + + if (!Arrays.areEqual(plaintext, 0, plaintextLength, output, 0, length)) + { + test.fail("decryption failed with split: " + split); + } + } + } + + static void implTestExceptionsEngine(int keysize, int ivsize, SimpleTest test, Instance instance) + throws Exception + { + AEADCipher cipher = instance.createInstance(); + + int offset; + byte[] k = new byte[keysize]; + byte[] iv = new byte[ivsize]; + byte[] m = new byte[0]; + CipherParameters params = new ParametersWithIV(new KeyParameter(k), iv); + try + { + cipher.processBytes(m, 0, m.length, null, 0); + test.fail(cipher.getAlgorithmName() + " need to be initialized before processBytes"); + } + catch (IllegalStateException e) + { + //expected + } + + try + { + cipher.processByte((byte)0, null, 0); + test.fail(cipher.getAlgorithmName() + " need to be initialized before processByte"); + } + catch (IllegalStateException e) + { + //expected + } + + try + { + cipher.reset(); + test.fail(cipher.getAlgorithmName() + " need to be initialized before reset"); + } + catch (IllegalStateException e) + { + //expected + } + + try + { + cipher.doFinal(null, m.length); + test.fail(cipher.getAlgorithmName() + " need to be initialized before dofinal"); + } + catch (IllegalStateException e) + { + //expected + } + + try + { + cipher.getMac(); + cipher.getOutputSize(0); + cipher.getUpdateOutputSize(0); + } + catch (IllegalStateException e) + { + //expected + test.fail(cipher.getAlgorithmName() + " functions can be called before initialization"); + } + + Random rand = new Random(); + int randomNum; + while ((randomNum = rand.nextInt(100)) == keysize) ; + byte[] k1 = new byte[randomNum]; + while ((randomNum = rand.nextInt(100)) == ivsize) ; + byte[] iv1 = new byte[randomNum]; + try + { + cipher.init(true, new ParametersWithIV(new KeyParameter(k1), iv)); + test.fail(cipher.getAlgorithmName() + " k size does not match"); + } + catch (IllegalArgumentException e) + { + //expected + } + try + { + cipher.init(true, new ParametersWithIV(new KeyParameter(k), iv1)); + test.fail(cipher.getAlgorithmName() + "iv size does not match"); + } + catch (IllegalArgumentException e) + { + //expected + } + + try + { + cipher.init(true, new AEADParameters(new KeyParameter(k), 0, iv)); + test.fail(cipher.getAlgorithmName() + " wrong type of CipherParameters"); + } + catch (IllegalArgumentException e) + { + //expected + } + + cipher.init(true, params); + byte[] c1 = new byte[cipher.getOutputSize(m.length)]; + try + { + cipher.doFinal(c1, m.length); + } + catch (Exception e) + { + test.fail(cipher.getAlgorithmName() + " allows no input for AAD and plaintext"); + } + byte[] mac2 = cipher.getMac(); + if (mac2 == null) + { + test.fail("mac should not be empty after dofinal"); + } + if (!Arrays.areEqual(mac2, c1)) + { + test.fail("mac should be equal when calling dofinal and getMac"); + } + cipher.init(true, params); + cipher.processAADByte((byte)0); + byte[] mac1 = new byte[cipher.getOutputSize(0)]; + cipher.doFinal(mac1, 0); + if (Arrays.areEqual(mac1, mac2)) + { + test.fail("mac should not match"); + } + cipher.init(true, params); + cipher.processByte((byte)0, new byte[1], 0); + try + { + cipher.processAADByte((byte)0); + test.fail("processAADByte(s) cannot be called after encryption/decryption"); + } + catch (IllegalStateException e) + { + //expected + } + try + { + cipher.processAADBytes(new byte[]{0}, 0, 1); + test.fail("processAADByte(s) cannot be called once only"); + } + catch (IllegalStateException e) + { + //expected + } + + cipher.reset(); + try + { + cipher.processAADBytes(new byte[]{0}, 1, 1); + test.fail("input for processAADBytes is too short"); + } + catch (DataLengthException e) + { + //expected + } + try + { + cipher.processBytes(new byte[]{0}, 1, 1, c1, 0); + test.fail("input for processBytes is too short"); + } + catch (DataLengthException e) + { + //expected + } + cipher.init(true, params); + try + { + int need = cipher.getUpdateOutputSize(64); + cipher.processBytes(new byte[64], 0, 64, new byte[need], 1); + test.fail("output for processBytes is too short"); + } + catch (OutputLengthException e) + { + //expected + } + try + { + cipher.doFinal(new byte[2], 2); + test.fail("output for dofinal is too short"); + } + catch (OutputLengthException e) + { + //expected + } + + implTestExceptionsGetUpdateOutputSize(cipher, false, params, 100, test); + implTestExceptionsGetUpdateOutputSize(cipher, true, params, 100, test); + + mac1 = new byte[cipher.getOutputSize(0)]; + mac2 = new byte[cipher.getOutputSize(0)]; + cipher.init(true, params); + cipher.processAADBytes(new byte[]{0, 0}, 0, 2); + cipher.doFinal(mac1, 0); + cipher.init(true, params); + cipher.processAADByte((byte)0); + cipher.processAADByte((byte)0); + cipher.doFinal(mac2, 0); + if (!Arrays.areEqual(mac1, mac2)) + { + test.fail("mac should match for the same AAD with different ways of inputing"); + } + + byte[] c2 = new byte[cipher.getOutputSize(10)]; + byte[] c3 = new byte[cipher.getOutputSize(10) + 2]; + + byte[] aad2 = {0, 1, 2, 3, 4}; + byte[] aad3 = {0, 0, 1, 2, 3, 4, 5}; + byte[] m2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + byte[] m3 = {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + byte[] m4 = new byte[m2.length]; + cipher.init(true, params); + cipher.processAADBytes(aad2, 0, aad2.length); + offset = cipher.processBytes(m2, 0, m2.length, c2, 0); + cipher.doFinal(c2, offset); + cipher.init(true, params); + cipher.processAADBytes(aad3, 1, aad2.length); + offset = cipher.processBytes(m3, 1, m2.length, c3, 1); + cipher.doFinal(c3, offset + 1); + byte[] c3_partial = new byte[c2.length]; + System.arraycopy(c3, 1, c3_partial, 0, c2.length); + if (!Arrays.areEqual(c2, c3_partial)) + { + test.fail("mac should match for the same AAD and message with different offset for both input and output"); + } + cipher.init(false, params); + cipher.processAADBytes(aad2, 0, aad2.length); + offset = cipher.processBytes(c2, 0, c2.length, m4, 0); + cipher.doFinal(m4, offset); + if (!Arrays.areEqual(m2, m4)) + { + test.fail("The encryption and decryption does not recover the plaintext"); + } + c2[c2.length - 1] ^= 1; + cipher.init(false, params); + cipher.processAADBytes(aad2, 0, aad2.length); + offset = cipher.processBytes(c2, 0, c2.length, m4, 0); + try + { + cipher.doFinal(m4, offset); + test.fail("The decryption should fail"); + } + catch (InvalidCipherTextException e) + { + //expected; + } + + byte[] m7 = new byte[32 + rand.nextInt(32)]; + rand.nextBytes(m7); + + cipher.init(true, params); + byte[] c7 = new byte[cipher.getOutputSize(m7.length)]; + byte[] c8 = new byte[c7.length]; + byte[] c9 = new byte[c7.length]; + cipher.processAADBytes(aad2, 0, aad2.length); + offset = cipher.processBytes(m7, 0, m7.length, c7, 0); + cipher.doFinal(c7, offset); + + cipher.init(true, params); + cipher.processAADBytes(aad2, 0, aad2.length); + offset = cipher.processBytes(m7, 0, m7.length / 2, c8, 0); + offset += cipher.processBytes(m7, m7.length / 2, m7.length - m7.length / 2, c8, offset); + cipher.doFinal(c8, offset); + + cipher.init(true, params); + int split = rand.nextInt(m7.length - 1) + 1; + cipher.processAADBytes(aad2, 0, aad2.length); + offset = cipher.processBytes(m7, 0, split, c9, 0); + offset += cipher.processBytes(m7, split, m7.length - split, c9, offset); + cipher.doFinal(c9, offset); + + if (!Arrays.areEqual(c7, c8) || !Arrays.areEqual(c7, c9)) + { + test.fail("Splitting input of plaintext should output the same ciphertext"); + } + } + + static void implTestExceptionsGetUpdateOutputSize(AEADCipher cipher, boolean forEncryption, + CipherParameters parameters, int maxInputSize, SimpleTest test) + { + cipher.init(forEncryption, parameters); + + int maxOutputSize = cipher.getUpdateOutputSize(maxInputSize); + + byte[] input = new byte[maxInputSize]; + byte[] output = new byte[maxOutputSize]; + + for (int inputSize = 0; inputSize <= maxInputSize; ++inputSize) + { + cipher.init(forEncryption, parameters); + + int outputSize = cipher.getUpdateOutputSize(inputSize); + if (outputSize > 0) + { + try + { + cipher.processBytes(input, 0, inputSize, output, maxOutputSize - outputSize + 1); + test.fail("output for processBytes is too short"); + } + catch (OutputLengthException e) + { + //expected + } + } + else + { + cipher.processBytes(input, 0, inputSize, null, 0); + } + } + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java b/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java index 3e08c4954a..26935bedb8 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java @@ -1,21 +1,10 @@ package org.bouncycastle.crypto.test; -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.HashMap; -import java.util.Random; - import org.bouncycastle.crypto.CipherParameters; -import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.InvalidCipherTextException; -import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.engines.GiftCofbEngine; -import org.bouncycastle.crypto.modes.AEADBlockCipher; -import org.bouncycastle.crypto.params.AEADParameters; +import org.bouncycastle.crypto.modes.AEADCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; -import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; public class GiftCofbTest @@ -29,355 +18,62 @@ public String getName() public void performTest() throws Exception { + CipherTest.checkAEADCipherOutputSize(this, 16, 16, 16, 16, new GiftCofbEngine()); CipherTest.implTestVectorsEngine(new GiftCofbEngine(), "crypto/giftcofb", "giftcofb_LWC_AEAD_KAT_128_128.txt", this); - //testExceptions(new GiftCofbEngine(), 16, 16, 16); - } - - - private void testVectors() - throws Exception - { - GiftCofbEngine GiftCofb = new GiftCofbEngine(); - CipherParameters params; - InputStream src = GiftCofbTest.class.getResourceAsStream("/org/bouncycastle/crypto/test/giftcofb_LWC_AEAD_KAT_128_128.txt"); - BufferedReader bin = new BufferedReader(new InputStreamReader(src)); - String line; - byte[] rv; - HashMap map = new HashMap(); - while ((line = bin.readLine()) != null) + CipherTest.implTestBufferingEngine(16, 16, 128, this, new CipherTest.Instance() { - int a = line.indexOf('='); - if (a < 0) + @Override + public AEADCipher createInstance() { -// if (!map.get("Count").equals("529")) -// { -// continue; -// } - byte[] key = Hex.decode(map.get("Key")); - byte[] nonce = Hex.decode(map.get("Nonce")); - byte[] ad = Hex.decode(map.get("AD")); - byte[] pt = Hex.decode(map.get("PT")); - byte[] ct = Hex.decode(map.get("CT")); - params = new ParametersWithIV(new KeyParameter(key), nonce); - GiftCofb.init(true, params); - GiftCofb.processAADBytes(ad, 0, ad.length); - rv = new byte[GiftCofb.getOutputSize(pt.length)]; - int len = GiftCofb.processBytes(pt, 0, pt.length, rv, 0); - GiftCofb.doFinal(rv, len); - if (!areEqual(rv, ct)) - { - mismatch("Keystream " + map.get("Count"), (String)map.get("CT"), rv); - } -// else -// { -// System.out.println("Keystream " + map.get("Count") + " pass"); -// } - GiftCofb.reset(); - GiftCofb.init(false, params); - //Decrypt - GiftCofb.processAADBytes(ad, 0, ad.length); - rv = new byte[pt.length]; - len = GiftCofb.processBytes(ct, 0, ct.length, rv, 0); - GiftCofb.doFinal(rv, len); - byte[] pt_recovered = new byte[pt.length]; - System.arraycopy(rv, 0, pt_recovered, 0, pt.length); - if (!areEqual(pt, pt_recovered)) - { - mismatch("Reccover Keystream " + map.get("Count"), (String)map.get("PT"), pt_recovered); - } - //System.out.println("Keystream " + map.get("Count") + " pass"); - GiftCofb.reset(); - map.clear(); - + return new GiftCofbEngine(); } - else + }); + CipherTest.implTestExceptionsEngine(16, 16, this, new CipherTest.Instance() + { + @Override + public AEADCipher createInstance() { - map.put(line.substring(0, a).trim(), line.substring(a + 1).trim()); + return new GiftCofbEngine(); } - } - System.out.println("GiftCofb AEAD pass"); - } - - private void testExceptions(AEADBlockCipher aeadBlockCipher, int keysize, int ivsize, int blocksize) - throws Exception - { - CipherParameters params; - byte[] k = new byte[keysize]; - byte[] iv = new byte[ivsize]; - byte[] m = new byte[0]; - params = new ParametersWithIV(new KeyParameter(k), iv); - - byte[] c1 = new byte[32]; - try - { - aeadBlockCipher.processBytes(m, 0, m.length, c1, 0); - fail(aeadBlockCipher.getAlgorithmName() + " need to be initialed before processBytes"); - } - catch (IllegalArgumentException e) - { - //expected - } - - try - { - aeadBlockCipher.processByte((byte)0, c1, 0); - fail(aeadBlockCipher.getAlgorithmName() + " need to be initialed before processByte"); - } - catch (IllegalArgumentException e) - { - //expected - } - - try - { - aeadBlockCipher.reset(); - fail(aeadBlockCipher.getAlgorithmName() + " need to be initialed before reset"); - } - catch (IllegalArgumentException e) - { - //expected - } - - try - { - aeadBlockCipher.doFinal(c1, m.length); - fail(aeadBlockCipher.getAlgorithmName() + " need to be initialed before dofinal"); - } - catch (IllegalArgumentException e) - { - //expected - } - - try - { - aeadBlockCipher.getMac(); - aeadBlockCipher.getAlgorithmName(); -// aeadBlockCipher.getOutputSize(0); -// aeadBlockCipher.getUpdateOutputSize(0); - } - catch (IllegalArgumentException e) - { - //expected - fail(aeadBlockCipher.getAlgorithmName() + " functions can be called before initialisation"); - } - Random rand = new Random(); - int randomNum; - while ((randomNum = rand.nextInt(100)) == keysize) ; - byte[] k1 = new byte[randomNum]; - while ((randomNum = rand.nextInt(100)) == ivsize) ; - byte[] iv1 = new byte[randomNum]; - try - { - aeadBlockCipher.init(true, new ParametersWithIV(new KeyParameter(k1), iv)); - fail(aeadBlockCipher.getAlgorithmName() + " k size does not match"); - } - catch (IllegalArgumentException e) - { - //expected - } - try - { - aeadBlockCipher.init(true, new ParametersWithIV(new KeyParameter(k), iv1)); - fail(aeadBlockCipher.getAlgorithmName() + "iv size does not match"); - } - catch (IllegalArgumentException e) - { - //expected - } - - try - { - aeadBlockCipher.init(true, new AEADParameters(new KeyParameter(k), 0, iv)); - fail(aeadBlockCipher.getAlgorithmName() + " wrong type of CipherParameters"); - } - catch (IllegalArgumentException e) - { - //expected - } + }); + implTestParametersEngine(new GiftCofbEngine(), 16, 16, 16); + CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new GiftCofbEngine()); + CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new GiftCofbEngine()); - aeadBlockCipher.init(true, params); - c1 = new byte[aeadBlockCipher.getOutputSize(0)]; - try + CipherTest.checkCipher(16, 16, 40, 128, new CipherTest.Instance() { - aeadBlockCipher.doFinal(c1, m.length); - } - catch (Exception e) - { - fail(aeadBlockCipher.getAlgorithmName() + " allows no input for AAD and plaintext"); - } - byte[] mac2 = aeadBlockCipher.getMac(); - if (mac2 == null) - { - fail(aeadBlockCipher.getAlgorithmName() + ": mac should not be empty after dofinal"); - } - if (!areEqual(mac2, c1)) - { - fail(aeadBlockCipher.getAlgorithmName() + ": mac should be equal when calling dofinal and getMac"); - } - - aeadBlockCipher.reset(); - aeadBlockCipher.processAADByte((byte)0); - byte[] mac1 = new byte[aeadBlockCipher.getOutputSize(0)]; - aeadBlockCipher.doFinal(mac1, 0); - if (areEqual(mac1, mac2)) - { - fail(aeadBlockCipher.getAlgorithmName() + ": mac should not match"); - } - aeadBlockCipher.reset(); - aeadBlockCipher.processBytes(new byte[blocksize + 1], 0, blocksize + 1, new byte[blocksize + 1], 0); - try - { - aeadBlockCipher.processAADByte((byte)0); - fail(aeadBlockCipher.getAlgorithmName() + ": processAADByte(s) cannot be called after encryption/decryption"); - } - catch (IllegalArgumentException e) - { - //expected - } - try - { - aeadBlockCipher.processAADBytes(new byte[]{0}, 0, 1); - fail(aeadBlockCipher.getAlgorithmName() + ": processAADByte(s) cannot be called once only"); - } - catch (IllegalArgumentException e) - { - //expected - } - - aeadBlockCipher.reset(); - try - { - aeadBlockCipher.processAADBytes(new byte[]{0}, 1, 1); - fail(aeadBlockCipher.getAlgorithmName() + ": input for processAADBytes is too short"); - } - catch (DataLengthException e) - { - //expected - } - try - { - aeadBlockCipher.processBytes(new byte[]{0}, 1, 1, c1, 0); - fail(aeadBlockCipher.getAlgorithmName() + ": input for processBytes is too short"); - } - catch (DataLengthException e) - { - //expected - } - try - { - aeadBlockCipher.processBytes(new byte[blocksize + 1], 0, blocksize + 1, new byte[blocksize + 1], blocksize >> 1); - fail(aeadBlockCipher.getAlgorithmName() + ": output for processBytes is too short"); - } - catch (OutputLengthException e) - { - //expected - } - try - { - aeadBlockCipher.doFinal(new byte[2], 2); - fail(aeadBlockCipher.getAlgorithmName() + ": output for dofinal is too short"); - } - catch (DataLengthException e) - { - //expected - } - - mac1 = new byte[aeadBlockCipher.getOutputSize(0)]; - mac2 = new byte[aeadBlockCipher.getOutputSize(0)]; - aeadBlockCipher.reset(); - aeadBlockCipher.processAADBytes(new byte[]{0, 0}, 0, 2); - aeadBlockCipher.doFinal(mac1, 0); - aeadBlockCipher.reset(); - aeadBlockCipher.processAADByte((byte)0); - aeadBlockCipher.processAADByte((byte)0); - aeadBlockCipher.doFinal(mac2, 0); - if (!areEqual(mac1, mac2)) - { - fail(aeadBlockCipher.getAlgorithmName() + ": mac should match for the same AAD with different ways of inputing"); - } - - byte[] c2 = new byte[aeadBlockCipher.getOutputSize(10)]; - byte[] c3 = new byte[aeadBlockCipher.getOutputSize(10) + 2]; + public AEADCipher createInstance() + { + return new GiftCofbEngine(); + } + }); + } - byte[] aad2 = {0, 1, 2, 3, 4}; - byte[] aad3 = {0, 0, 1, 2, 3, 4, 5}; - byte[] m2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; - byte[] m3 = {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - byte[] m4 = new byte[m2.length]; - aeadBlockCipher.reset(); - aeadBlockCipher.processAADBytes(aad2, 0, aad2.length); - int offset = aeadBlockCipher.processBytes(m2, 0, m2.length, c2, 0); - aeadBlockCipher.doFinal(c2, offset); - aeadBlockCipher.reset(); - aeadBlockCipher.processAADBytes(aad3, 1, aad2.length); - offset = aeadBlockCipher.processBytes(m3, 1, m2.length, c3, 1); - aeadBlockCipher.doFinal(c3, offset + 1); - byte[] c3_partial = new byte[c2.length]; - System.arraycopy(c3, 1, c3_partial, 0, c2.length); - if (!areEqual(c2, c3_partial)) + private void implTestParametersEngine(GiftCofbEngine cipher, int keySize, int ivSize, + int macSize) + { + if (cipher.getKeyBytesSize() != keySize) { - fail(aeadBlockCipher.getAlgorithmName() + ": mac should match for the same AAD and message with different offset for both input and output"); + fail("key bytes of " + cipher.getAlgorithmName() + " is not correct"); } - aeadBlockCipher.reset(); - aeadBlockCipher.init(false, params); - aeadBlockCipher.processAADBytes(aad2, 0, aad2.length); - offset = aeadBlockCipher.processBytes(c2, 0, c2.length, m4, 0); - aeadBlockCipher.doFinal(m4, offset); - if (!areEqual(m2, m4)) + if (cipher.getIVBytesSize() != ivSize) { - fail(aeadBlockCipher.getAlgorithmName() + ": The encryption and decryption does not recover the plaintext"); + fail("iv bytes of " + cipher.getAlgorithmName() + " is not correct"); } - c2[c2.length - 1] ^= 1; - aeadBlockCipher.reset(); - aeadBlockCipher.init(false, params); - aeadBlockCipher.processAADBytes(aad2, 0, aad2.length); - offset = aeadBlockCipher.processBytes(c2, 0, c2.length, m4, 0); - try - { - aeadBlockCipher.doFinal(m4, offset); - fail(aeadBlockCipher.getAlgorithmName() + ": The decryption should fail"); - } - catch (InvalidCipherTextException e) - { - //expected; - } + CipherParameters parameters = new ParametersWithIV(new KeyParameter(new byte[keySize]), new byte[ivSize]); - byte[] m7 = new byte[blocksize * 2]; - for (int i = 0; i < m7.length; ++i) + cipher.init(true, parameters); + if (cipher.getOutputSize(0) != macSize) { - m7[i] = (byte)rand.nextInt(); + fail("getOutputSize of " + cipher.getAlgorithmName() + " is incorrect for encryption"); } - aeadBlockCipher.init(true, params); - byte[] c7 = new byte[aeadBlockCipher.getOutputSize(m7.length)]; - byte[] c8 = new byte[c7.length]; - byte[] c9 = new byte[c7.length]; - aeadBlockCipher.processAADBytes(aad2, 0, aad2.length); - offset = aeadBlockCipher.processBytes(m7, 0, m7.length, c7, 0); - aeadBlockCipher.doFinal(c7, offset); - aeadBlockCipher.reset(); - aeadBlockCipher.processAADBytes(aad2, 0, aad2.length); - offset = aeadBlockCipher.processBytes(m7, 0, blocksize, c8, 0); - offset += aeadBlockCipher.processBytes(m7, blocksize, m7.length - blocksize, c8, offset); - aeadBlockCipher.doFinal(c8, offset); - aeadBlockCipher.reset(); - int split = rand.nextInt(blocksize * 2); - aeadBlockCipher.processAADBytes(aad2, 0, aad2.length); - offset = aeadBlockCipher.processBytes(m7, 0, split, c9, 0); - offset += aeadBlockCipher.processBytes(m7, split, m7.length - split, c9, offset); - aeadBlockCipher.doFinal(c9, offset); - if (!areEqual(c7, c8) || !areEqual(c7, c9)) + cipher.init(false, parameters); + if (cipher.getOutputSize(macSize) != 0) { - fail(aeadBlockCipher.getAlgorithmName() + ": Splitting input of plaintext should output the same ciphertext"); + fail("getOutputSize of " + cipher.getAlgorithmName() + " is incorrect for decryption"); } - System.out.println(aeadBlockCipher.getAlgorithmName() + " test Exceptions pass"); - } - - - private void mismatch(String name, String expected, byte[] found) - { - fail("mismatch on " + name, expected, new String(Hex.encode(found))); } public static void main(String[] args) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java index 2812befabd..6b6914b973 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java @@ -49,7 +49,6 @@ public void performTest() testExceptionsDigest_ESCH256(); testExceptionsDigest_ESCH384(); ; - testExceptionsEngine_SCHWAEMM128_128(); testExceptionsEngine_SCHWAEMM192_192(); testExceptionsEngine_SCHWAEMM256_128(); From 7ee7b38f76b8a2bc1f3294e3b39e738fb1a24103 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 17 Jan 2025 14:39:28 +1030 Subject: [PATCH 022/890] Merge Romulus branch to this branch --- .../crypto/digests/RomulusDigest.java | 286 +++++ .../crypto/engines/RomulusEngine.java | 1016 +++++++++++++++++ .../crypto/test/RegressionTest.java | 2 + .../bouncycastle/crypto/test/RomulusTest.java | 138 +++ 4 files changed, 1442 insertions(+) create mode 100644 core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java create mode 100644 core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java new file mode 100644 index 0000000000..54d8700dac --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java @@ -0,0 +1,286 @@ +package org.bouncycastle.crypto.digests; + +import java.io.ByteArrayOutputStream; + +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.util.Arrays; + +/** + * Romulus v1.3, based on the current round 3 submission, https://romulusae.github.io/romulus/ + * Reference C implementation: https://github.com/romulusae/romulus + * Specification: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/romulus-spec-final.pdf + */ + +public class RomulusDigest + implements Digest +{ + private final int CRYPTO_BYTES = 32; + + /* + * This file includes only the encryption function of SKINNY-128-384+ as required by Romulus-v1.3 + */ +// Packing of data is done as follows (state[i][j] stands for row i and column j): +// 0 1 2 3 +// 4 5 6 7 +// 8 9 10 11 +//12 13 14 15 + + // 8-bit Sbox + private final byte[] sbox_8 = + { + (byte)0x65, (byte)0x4c, (byte)0x6a, (byte)0x42, (byte)0x4b, (byte)0x63, (byte)0x43, (byte)0x6b, (byte)0x55, + (byte)0x75, (byte)0x5a, (byte)0x7a, (byte)0x53, (byte)0x73, (byte)0x5b, (byte)0x7b, (byte)0x35, (byte)0x8c, + (byte)0x3a, (byte)0x81, (byte)0x89, (byte)0x33, (byte)0x80, (byte)0x3b, (byte)0x95, (byte)0x25, (byte)0x98, + (byte)0x2a, (byte)0x90, (byte)0x23, (byte)0x99, (byte)0x2b, (byte)0xe5, (byte)0xcc, (byte)0xe8, (byte)0xc1, + (byte)0xc9, (byte)0xe0, (byte)0xc0, (byte)0xe9, (byte)0xd5, (byte)0xf5, (byte)0xd8, (byte)0xf8, (byte)0xd0, + (byte)0xf0, (byte)0xd9, (byte)0xf9, (byte)0xa5, (byte)0x1c, (byte)0xa8, (byte)0x12, (byte)0x1b, (byte)0xa0, + (byte)0x13, (byte)0xa9, (byte)0x05, (byte)0xb5, (byte)0x0a, (byte)0xb8, (byte)0x03, (byte)0xb0, (byte)0x0b, + (byte)0xb9, (byte)0x32, (byte)0x88, (byte)0x3c, (byte)0x85, (byte)0x8d, (byte)0x34, (byte)0x84, (byte)0x3d, + (byte)0x91, (byte)0x22, (byte)0x9c, (byte)0x2c, (byte)0x94, (byte)0x24, (byte)0x9d, (byte)0x2d, (byte)0x62, + (byte)0x4a, (byte)0x6c, (byte)0x45, (byte)0x4d, (byte)0x64, (byte)0x44, (byte)0x6d, (byte)0x52, (byte)0x72, + (byte)0x5c, (byte)0x7c, (byte)0x54, (byte)0x74, (byte)0x5d, (byte)0x7d, (byte)0xa1, (byte)0x1a, (byte)0xac, + (byte)0x15, (byte)0x1d, (byte)0xa4, (byte)0x14, (byte)0xad, (byte)0x02, (byte)0xb1, (byte)0x0c, (byte)0xbc, + (byte)0x04, (byte)0xb4, (byte)0x0d, (byte)0xbd, (byte)0xe1, (byte)0xc8, (byte)0xec, (byte)0xc5, (byte)0xcd, + (byte)0xe4, (byte)0xc4, (byte)0xed, (byte)0xd1, (byte)0xf1, (byte)0xdc, (byte)0xfc, (byte)0xd4, (byte)0xf4, + (byte)0xdd, (byte)0xfd, (byte)0x36, (byte)0x8e, (byte)0x38, (byte)0x82, (byte)0x8b, (byte)0x30, (byte)0x83, + (byte)0x39, (byte)0x96, (byte)0x26, (byte)0x9a, (byte)0x28, (byte)0x93, (byte)0x20, (byte)0x9b, (byte)0x29, + (byte)0x66, (byte)0x4e, (byte)0x68, (byte)0x41, (byte)0x49, (byte)0x60, (byte)0x40, (byte)0x69, (byte)0x56, + (byte)0x76, (byte)0x58, (byte)0x78, (byte)0x50, (byte)0x70, (byte)0x59, (byte)0x79, (byte)0xa6, (byte)0x1e, + (byte)0xaa, (byte)0x11, (byte)0x19, (byte)0xa3, (byte)0x10, (byte)0xab, (byte)0x06, (byte)0xb6, (byte)0x08, + (byte)0xba, (byte)0x00, (byte)0xb3, (byte)0x09, (byte)0xbb, (byte)0xe6, (byte)0xce, (byte)0xea, (byte)0xc2, + (byte)0xcb, (byte)0xe3, (byte)0xc3, (byte)0xeb, (byte)0xd6, (byte)0xf6, (byte)0xda, (byte)0xfa, (byte)0xd3, + (byte)0xf3, (byte)0xdb, (byte)0xfb, (byte)0x31, (byte)0x8a, (byte)0x3e, (byte)0x86, (byte)0x8f, (byte)0x37, + (byte)0x87, (byte)0x3f, (byte)0x92, (byte)0x21, (byte)0x9e, (byte)0x2e, (byte)0x97, (byte)0x27, (byte)0x9f, + (byte)0x2f, (byte)0x61, (byte)0x48, (byte)0x6e, (byte)0x46, (byte)0x4f, (byte)0x67, (byte)0x47, (byte)0x6f, + (byte)0x51, (byte)0x71, (byte)0x5e, (byte)0x7e, (byte)0x57, (byte)0x77, (byte)0x5f, (byte)0x7f, (byte)0xa2, + (byte)0x18, (byte)0xae, (byte)0x16, (byte)0x1f, (byte)0xa7, (byte)0x17, (byte)0xaf, (byte)0x01, (byte)0xb2, + (byte)0x0e, (byte)0xbe, (byte)0x07, (byte)0xb7, (byte)0x0f, (byte)0xbf, (byte)0xe2, (byte)0xca, (byte)0xee, + (byte)0xc6, (byte)0xcf, (byte)0xe7, (byte)0xc7, (byte)0xef, (byte)0xd2, (byte)0xf2, (byte)0xde, (byte)0xfe, + (byte)0xd7, (byte)0xf7, (byte)0xdf, (byte)0xff + }; + // Tweakey permutation + private final byte[] TWEAKEY_P = {9, 15, 8, 13, 10, 14, 12, 11, 0, 1, 2, 3, 4, 5, 6, 7}; + + // round constants + private final byte[] RC = { + (byte)0x01, (byte)0x03, (byte)0x07, (byte)0x0F, (byte)0x1F, (byte)0x3E, (byte)0x3D, (byte)0x3B, (byte)0x37, (byte)0x2F, + (byte)0x1E, (byte)0x3C, (byte)0x39, (byte)0x33, (byte)0x27, (byte)0x0E, (byte)0x1D, (byte)0x3A, (byte)0x35, (byte)0x2B, + (byte)0x16, (byte)0x2C, (byte)0x18, (byte)0x30, (byte)0x21, (byte)0x02, (byte)0x05, (byte)0x0B, (byte)0x17, (byte)0x2E, + (byte)0x1C, (byte)0x38, (byte)0x31, (byte)0x23, (byte)0x06, (byte)0x0D, (byte)0x1B, (byte)0x36, (byte)0x2D, (byte)0x1A}; + + void skinny_128_384_plus_enc(byte[] input, byte[] userkey) + { + byte[][] state = new byte[4][4]; + byte[][][] keyCells = new byte[3][4][4]; + int i, j, q, r; + byte pos, tmp; + byte[][][] keyCells_tmp = new byte[3][4][4]; + for (i = 0; i < 4; ++i) + { + q = i << 2; + System.arraycopy(input, q, state[i], 0, 4); + System.arraycopy(userkey, q, keyCells[0][i], 0, 4); + System.arraycopy(userkey, q + 16, keyCells[1][i], 0, 4); + System.arraycopy(userkey, q + 32, keyCells[2][i], 0, 4); + } + for (int round = 0; round < 40; round++) + { + //SubCell8; + for (i = 0; i < 4; i++) + { + for (j = 0; j < 4; j++) + { + state[i][j] = sbox_8[state[i][j] & 0xFF]; + } + } + //AddConstants + state[0][0] ^= (RC[round] & 0xf); + state[1][0] ^= ((RC[round] >>> 4) & 0x3); + state[2][0] ^= 0x2; + //AddKey + // apply the subtweakey to the internal state + for (i = 0; i <= 1; i++) + { + for (j = 0; j < 4; j++) + { + state[i][j] ^= keyCells[0][i][j] ^ keyCells[1][i][j] ^ keyCells[2][i][j]; + } + } + for (i = 0; i < 4; i++) + { + for (j = 0; j < 4; j++) + { + //application of the TWEAKEY permutation + pos = TWEAKEY_P[j + (i << 2)]; + q = pos >>> 2; + r = pos & 3; + keyCells_tmp[0][i][j] = keyCells[0][q][r]; + keyCells_tmp[1][i][j] = keyCells[1][q][r]; + keyCells_tmp[2][i][j] = keyCells[2][q][r]; + } + } + // update the subtweakey states with the LFSRs + for (i = 0; i <= 1; i++) + { + for (j = 0; j < 4; j++) + { + //application of LFSRs for TK updates + keyCells[0][i][j] = keyCells_tmp[0][i][j]; + tmp = keyCells_tmp[1][i][j]; + keyCells[1][i][j] = (byte)(((tmp << 1) & 0xFE) ^ ((tmp >>> 7) & 0x01) ^ ((tmp >>> 5) & 0x01)); + tmp = keyCells_tmp[2][i][j]; + keyCells[2][i][j] = (byte)(((tmp >>> 1) & 0x7F) ^ ((tmp << 7) & 0x80) ^ ((tmp << 1) & 0x80)); + } + } + for (; i < 4; ++i) + { + for (j = 0; j < 4; j++) + { + keyCells[0][i][j] = keyCells_tmp[0][i][j]; + keyCells[1][i][j] = keyCells_tmp[1][i][j]; + keyCells[2][i][j] = keyCells_tmp[2][i][j]; + } + } + //ShiftRows(state); + tmp = state[1][3]; + state[1][3] = state[1][2]; + state[1][2] = state[1][1]; + state[1][1] = state[1][0]; + state[1][0] = tmp; + tmp = state[2][0]; + state[2][0] = state[2][2]; + state[2][2] = tmp; + tmp = state[2][1]; + state[2][1] = state[2][3]; + state[2][3] = tmp; + tmp = state[3][0]; + state[3][0] = state[3][1]; + state[3][1] = state[3][2]; + state[3][2] = state[3][3]; + state[3][3] = tmp; + //MixColumn(state); + for (j = 0; j < 4; j++) + { + state[1][j] ^= state[2][j]; + state[2][j] ^= state[0][j]; + state[3][j] ^= state[2][j]; + tmp = state[3][j]; + state[3][j] = state[2][j]; + state[2][j] = state[1][j]; + state[1][j] = state[0][j]; + state[0][j] = tmp; + } + } //The last subtweakey should not be added + for (i = 0; i < 16; i++) + { + input[i] = (byte)(state[i >>> 2][i & 0x3] & 0xFF); + } + } + + + // The hirose double-block length (DBL) compression function. + void hirose_128_128_256(byte[] h, byte[] g, byte[] m, int mOff) + { + byte[] key = new byte[48]; + byte[] hh = new byte[16]; + int i; + // assign the key for the hirose compresison function + System.arraycopy(g, 0, key, 0, 16); + System.arraycopy(h, 0, g, 0, 16); + System.arraycopy(h, 0, hh, 0, 16); + g[0] ^= 0x01; + System.arraycopy(m, mOff, key, 16, 32); + skinny_128_384_plus_enc(h, key); + skinny_128_384_plus_enc(g, key); + for (i = 0; i < 16; i++) + { + h[i] ^= hh[i]; + g[i] ^= hh[i]; + } + g[0] ^= 0x01; + } + + // Padding function: pads the byte length of the message mod 32 to the last incomplete block. +// For complete blocks it returns the same block. For an empty block it returns a 0^2n string. +// The function is called for full block messages to add a 0^2n block. This and the modulus are +// the only differences compared to the use in Romulus-N + void ipad_256(byte[] m, int inOff, byte[] mp, int len8) + { + System.arraycopy(m, inOff, mp, 0, len8); + Arrays.fill(mp, len8, 31, (byte)0); + mp[31] = (byte)(len8 & 0x1f); + } + + private void crypto_hash(byte[] out, int outOff, byte[] input, int inlen) + { + byte[] h = new byte[16]; + byte[] g = new byte[16]; + int mlen; + byte[] p = new byte[32]; + mlen = inlen; + int inOff = 0; + while (mlen >= 32) + { // Normal loop + hirose_128_128_256(h, g, input, inOff); + inOff += 32; + mlen -= 32; + } + // Partial block (or in case there is no partial block we add a 0^2n block + ipad_256(input, inOff, p, mlen); + h[0] ^= 2; + hirose_128_128_256(h, g, p, 0); + // Assign the output tag + System.arraycopy(h, 0, out, outOff, 16); + System.arraycopy(g, 0, out, 16 + outOff, 16); + } + + private final ByteArrayOutputStream message = new ByteArrayOutputStream(); + + @Override + public String getAlgorithmName() + { + return "Romulus Hash"; + } + + @Override + public int getDigestSize() + { + return CRYPTO_BYTES; + } + + @Override + public void update(byte input) + { + message.write(input); + } + + @Override + public void update(byte[] input, int inOff, int len) + { + if (inOff + len > input.length) + { + throw new DataLengthException(" input buffer too short"); + } + message.write(input, inOff, len); + } + + @Override + public int doFinal(byte[] output, int outOff) + { + if (outOff + 32 > output.length) + { + throw new DataLengthException(" output buffer too short"); + } + byte[] input = message.toByteArray(); + int inlen = input.length; + crypto_hash(output, outOff, input, inlen); + return CRYPTO_BYTES; + } + + @Override + public void reset() + { + message.reset(); + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java new file mode 100644 index 0000000000..f5e47c3024 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -0,0 +1,1016 @@ +package org.bouncycastle.crypto.engines; + +import java.io.ByteArrayOutputStream; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.OutputLengthException; +import org.bouncycastle.crypto.constraints.DefaultServiceProperties; +import org.bouncycastle.crypto.modes.AEADBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; + +/** + * Romulus v1.3, based on the current round 3 submission, https://romulusae.github.io/romulus/ + * Reference C implementation: https://github.com/romulusae/romulus + * Specification: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/romulus-spec-final.pdf + */ + +public class RomulusEngine + implements AEADBlockCipher +{ + public enum RomulusParameters + { + RomulusM, + RomulusN, + RomulusT, + } + + private String algorithmName; + private boolean forEncryption; + private boolean initialised; + private final RomulusParameters romulusParameters; + private int offset; + private byte[] T; + private byte[] k; + private byte[] npub; + private final ByteArrayOutputStream aadData = new ByteArrayOutputStream(); + private final ByteArrayOutputStream message = new ByteArrayOutputStream(); + private final int CRYPTO_ABYTES = 16; + private final int AD_BLK_LEN_HALF = 16; + + // Packing of data is done as follows (state[i][j] stands for row i and column j): + // 0 1 2 3 + // 4 5 6 7 + // 8 9 10 11 + //12 13 14 15 + + // 8-bit Sbox + private final byte[] sbox_8 = + { + (byte)0x65, (byte)0x4c, (byte)0x6a, (byte)0x42, (byte)0x4b, (byte)0x63, (byte)0x43, (byte)0x6b, (byte)0x55, + (byte)0x75, (byte)0x5a, (byte)0x7a, (byte)0x53, (byte)0x73, (byte)0x5b, (byte)0x7b, (byte)0x35, (byte)0x8c, + (byte)0x3a, (byte)0x81, (byte)0x89, (byte)0x33, (byte)0x80, (byte)0x3b, (byte)0x95, (byte)0x25, (byte)0x98, + (byte)0x2a, (byte)0x90, (byte)0x23, (byte)0x99, (byte)0x2b, (byte)0xe5, (byte)0xcc, (byte)0xe8, (byte)0xc1, + (byte)0xc9, (byte)0xe0, (byte)0xc0, (byte)0xe9, (byte)0xd5, (byte)0xf5, (byte)0xd8, (byte)0xf8, (byte)0xd0, + (byte)0xf0, (byte)0xd9, (byte)0xf9, (byte)0xa5, (byte)0x1c, (byte)0xa8, (byte)0x12, (byte)0x1b, (byte)0xa0, + (byte)0x13, (byte)0xa9, (byte)0x05, (byte)0xb5, (byte)0x0a, (byte)0xb8, (byte)0x03, (byte)0xb0, (byte)0x0b, + (byte)0xb9, (byte)0x32, (byte)0x88, (byte)0x3c, (byte)0x85, (byte)0x8d, (byte)0x34, (byte)0x84, (byte)0x3d, + (byte)0x91, (byte)0x22, (byte)0x9c, (byte)0x2c, (byte)0x94, (byte)0x24, (byte)0x9d, (byte)0x2d, (byte)0x62, + (byte)0x4a, (byte)0x6c, (byte)0x45, (byte)0x4d, (byte)0x64, (byte)0x44, (byte)0x6d, (byte)0x52, (byte)0x72, + (byte)0x5c, (byte)0x7c, (byte)0x54, (byte)0x74, (byte)0x5d, (byte)0x7d, (byte)0xa1, (byte)0x1a, (byte)0xac, + (byte)0x15, (byte)0x1d, (byte)0xa4, (byte)0x14, (byte)0xad, (byte)0x02, (byte)0xb1, (byte)0x0c, (byte)0xbc, + (byte)0x04, (byte)0xb4, (byte)0x0d, (byte)0xbd, (byte)0xe1, (byte)0xc8, (byte)0xec, (byte)0xc5, (byte)0xcd, + (byte)0xe4, (byte)0xc4, (byte)0xed, (byte)0xd1, (byte)0xf1, (byte)0xdc, (byte)0xfc, (byte)0xd4, (byte)0xf4, + (byte)0xdd, (byte)0xfd, (byte)0x36, (byte)0x8e, (byte)0x38, (byte)0x82, (byte)0x8b, (byte)0x30, (byte)0x83, + (byte)0x39, (byte)0x96, (byte)0x26, (byte)0x9a, (byte)0x28, (byte)0x93, (byte)0x20, (byte)0x9b, (byte)0x29, + (byte)0x66, (byte)0x4e, (byte)0x68, (byte)0x41, (byte)0x49, (byte)0x60, (byte)0x40, (byte)0x69, (byte)0x56, + (byte)0x76, (byte)0x58, (byte)0x78, (byte)0x50, (byte)0x70, (byte)0x59, (byte)0x79, (byte)0xa6, (byte)0x1e, + (byte)0xaa, (byte)0x11, (byte)0x19, (byte)0xa3, (byte)0x10, (byte)0xab, (byte)0x06, (byte)0xb6, (byte)0x08, + (byte)0xba, (byte)0x00, (byte)0xb3, (byte)0x09, (byte)0xbb, (byte)0xe6, (byte)0xce, (byte)0xea, (byte)0xc2, + (byte)0xcb, (byte)0xe3, (byte)0xc3, (byte)0xeb, (byte)0xd6, (byte)0xf6, (byte)0xda, (byte)0xfa, (byte)0xd3, + (byte)0xf3, (byte)0xdb, (byte)0xfb, (byte)0x31, (byte)0x8a, (byte)0x3e, (byte)0x86, (byte)0x8f, (byte)0x37, + (byte)0x87, (byte)0x3f, (byte)0x92, (byte)0x21, (byte)0x9e, (byte)0x2e, (byte)0x97, (byte)0x27, (byte)0x9f, + (byte)0x2f, (byte)0x61, (byte)0x48, (byte)0x6e, (byte)0x46, (byte)0x4f, (byte)0x67, (byte)0x47, (byte)0x6f, + (byte)0x51, (byte)0x71, (byte)0x5e, (byte)0x7e, (byte)0x57, (byte)0x77, (byte)0x5f, (byte)0x7f, (byte)0xa2, + (byte)0x18, (byte)0xae, (byte)0x16, (byte)0x1f, (byte)0xa7, (byte)0x17, (byte)0xaf, (byte)0x01, (byte)0xb2, + (byte)0x0e, (byte)0xbe, (byte)0x07, (byte)0xb7, (byte)0x0f, (byte)0xbf, (byte)0xe2, (byte)0xca, (byte)0xee, + (byte)0xc6, (byte)0xcf, (byte)0xe7, (byte)0xc7, (byte)0xef, (byte)0xd2, (byte)0xf2, (byte)0xde, (byte)0xfe, + (byte)0xd7, (byte)0xf7, (byte)0xdf, (byte)0xff + }; + + // Tweakey permutation + private final byte[] TWEAKEY_P = {9, 15, 8, 13, 10, 14, 12, 11, 0, 1, 2, 3, 4, 5, 6, 7}; + + // round constants + private final byte[] RC = { + 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3E, 0x3D, 0x3B, 0x37, 0x2F, + 0x1E, 0x3C, 0x39, 0x33, 0x27, 0x0E, 0x1D, 0x3A, 0x35, 0x2B, + 0x16, 0x2C, 0x18, 0x30, 0x21, 0x02, 0x05, 0x0B, 0x17, 0x2E, + 0x1C, 0x38, 0x31, 0x23, 0x06, 0x0D, 0x1B, 0x36, 0x2D, 0x1A + }; + + public RomulusEngine(RomulusParameters romulusParameters) + { + this.romulusParameters = romulusParameters; + switch (romulusParameters) + { + case RomulusM: + algorithmName = "Romulus-M"; + break; + case RomulusN: + algorithmName = "Romulus-N"; + break; + case RomulusT: + algorithmName = "Romulus-T"; + break; + } + initialised = false; + } + + private void skinny_128_384_plus_enc(byte[] input, byte[] userkey) + { + byte[][] state = new byte[4][4]; + byte[][][] keyCells = new byte[3][4][4]; + int i, j, q, r; + byte pos, tmp; + byte[][][] keyCells_tmp = new byte[3][4][4]; + for (i = 0; i < 4; ++i) + { + q = i << 2; + System.arraycopy(input, q, state[i], 0, 4); + System.arraycopy(userkey, q, keyCells[0][i], 0, 4); + System.arraycopy(userkey, q + 16, keyCells[1][i], 0, 4); + System.arraycopy(userkey, q + 32, keyCells[2][i], 0, 4); + } + for (int round = 0; round < 40; round++) + { + //SubCell8; + for (i = 0; i < 4; i++) + { + for (j = 0; j < 4; j++) + { + state[i][j] = sbox_8[state[i][j] & 0xFF]; + } + } + //AddConstants + state[0][0] ^= (RC[round] & 0xf); + state[1][0] ^= ((RC[round] >>> 4) & 0x3); + state[2][0] ^= 0x2; + //AddKey + // apply the subtweakey to the internal state + for (i = 0; i <= 1; i++) + { + for (j = 0; j < 4; j++) + { + state[i][j] ^= keyCells[0][i][j] ^ keyCells[1][i][j] ^ keyCells[2][i][j]; + } + } + for (i = 0; i < 4; i++) + { + for (j = 0; j < 4; j++) + { + //application of the TWEAKEY permutation + pos = TWEAKEY_P[j + (i << 2)]; + q = pos >>> 2; + r = pos & 3; + keyCells_tmp[0][i][j] = keyCells[0][q][r]; + keyCells_tmp[1][i][j] = keyCells[1][q][r]; + keyCells_tmp[2][i][j] = keyCells[2][q][r]; + } + } + // update the subtweakey states with the LFSRs + for (i = 0; i <= 1; i++) + { + for (j = 0; j < 4; j++) + { + //application of LFSRs for TK updates + keyCells[0][i][j] = keyCells_tmp[0][i][j]; + tmp = keyCells_tmp[1][i][j]; + keyCells[1][i][j] = (byte)(((tmp << 1) & 0xFE) ^ ((tmp >>> 7) & 0x01) ^ ((tmp >>> 5) & 0x01)); + tmp = keyCells_tmp[2][i][j]; + keyCells[2][i][j] = (byte)(((tmp >>> 1) & 0x7F) ^ ((tmp << 7) & 0x80) ^ ((tmp << 1) & 0x80)); + } + } + for (; i < 4; ++i) + { + for (j = 0; j < 4; j++) + { + keyCells[0][i][j] = keyCells_tmp[0][i][j]; + keyCells[1][i][j] = keyCells_tmp[1][i][j]; + keyCells[2][i][j] = keyCells_tmp[2][i][j]; + } + } + //ShiftRows(state); + tmp = state[1][3]; + state[1][3] = state[1][2]; + state[1][2] = state[1][1]; + state[1][1] = state[1][0]; + state[1][0] = tmp; + tmp = state[2][0]; + state[2][0] = state[2][2]; + state[2][2] = tmp; + tmp = state[2][1]; + state[2][1] = state[2][3]; + state[2][3] = tmp; + tmp = state[3][0]; + state[3][0] = state[3][1]; + state[3][1] = state[3][2]; + state[3][2] = state[3][3]; + state[3][3] = tmp; + //MixColumn(state); + for (j = 0; j < 4; j++) + { + state[1][j] ^= state[2][j]; + state[2][j] ^= state[0][j]; + state[3][j] ^= state[2][j]; + tmp = state[3][j]; + state[3][j] = state[2][j]; + state[2][j] = state[1][j]; + state[1][j] = state[0][j]; + state[0][j] = tmp; + } + } //The last subtweakey should not be added + for (i = 0; i < 16; i++) + { + input[i] = (byte)(state[i >>> 2][i & 0x3] & 0xFF); + } + } + + + // Padding function: pads the byte length of the message mod 16 to the last incomplete block. +// For complete blocks it returns the same block. + void pad(byte[] m, int mOff, byte[] mp, int l, int len8) + { + mp[l - 1] = (byte)(len8 & 0x0f); + System.arraycopy(m, mOff, mp, 0, len8); + } + + // G(S): generates the key stream from the internal state by multiplying the state S by the constant matrix G + void g8A(byte[] s, byte[] c, int cOff) + { + int len = Math.min(c.length - cOff, 16); + for (int i = 0; i < len; i++) + { + c[i + cOff] = (byte)(((s[i] & 0xFF) >>> 1) ^ (s[i] & 0x80) ^ ((s[i] & 0x01) << 7)); + } + } + + // Rho(S,M): pads an M block and outputs S'= M xor S and C = M xor G(S) + // Inverse-Rho(S,M): pads a C block and outputs S'= C xor G(S) xor S and M = C xor G(S) + void rho(byte[] m, int mOff, byte[] c, int cOff, byte[] s, int len8) + { + byte[] mp = new byte[16]; + pad(m, mOff, mp, AD_BLK_LEN_HALF, len8); + g8A(s, c, cOff); + if (forEncryption) + { + for (int i = 0; i < AD_BLK_LEN_HALF; i++) + { + s[i] ^= mp[i]; + if (i < len8) + { + c[i + cOff] ^= mp[i]; + } + else + { + c[i + cOff] = 0; + } + } + } + else + { + for (int i = 0; i < AD_BLK_LEN_HALF; i++) + { + s[i] ^= mp[i]; + if (i < len8 && i + cOff < c.length) + { + s[i] ^= c[i + cOff]; + c[i + cOff] ^= mp[i]; + } + } + } + + } + + // Applies CNT'=2 * CNT (mod GF(2^56)), where GF(2^56) is defined using the irreducible polynomial +// x^56 + x^7 + x^4 + x^2 + 1 + void lfsr_gf56(byte[] CNT) + { + byte fb0 = (byte)((CNT[6] & 0xFF) >>> 7); + CNT[6] = (byte)(((CNT[6] & 0xFF) << 1) | ((CNT[5] & 0xFF) >>> 7)); + CNT[5] = (byte)(((CNT[5] & 0xFF) << 1) | ((CNT[4] & 0xFF) >>> 7)); + CNT[4] = (byte)((((CNT[4] & 0xFF) << 1) | ((CNT[3] & 0xFF) >>> 7))); + CNT[3] = (byte)(((CNT[3] & 0xFF) << 1) | ((CNT[2] & 0xFF) >>> 7)); + CNT[2] = (byte)(((CNT[2] & 0xFF) << 1) | ((CNT[1] & 0xFF) >>> 7)); + CNT[1] = (byte)(((CNT[1] & 0xFF) << 1) | ((CNT[0] & 0xFF) >>> 7)); + if (fb0 == 1) + { + CNT[0] = (byte)(((CNT[0] & 0xFF) << 1) ^ 0x95); + } + else + { + CNT[0] = (byte)(((CNT[0] & 0xFF) << 1)); + } + } + + // An interface between Romulus and the underlying TBC + void block_cipher(byte[] s, byte[] K, byte[] T, int tOff, byte[] CNT, byte D) + { + byte[] KT = new byte[48]; + // Combines the secret key, counter and domain bits to form the full 384-bit tweakey + System.arraycopy(CNT, 0, KT, 0, 7); + KT[7] = D; + Arrays.fill(KT, 8, 16, (byte)0x00); + System.arraycopy(T, tOff, KT, 16, 16); + System.arraycopy(K, 0, KT, 32, 16); + skinny_128_384_plus_enc(s, KT); + } + + // Calls the TBC using the nonce as part of the tweakey + void nonce_encryption(byte[] N, byte[] CNT, byte[] s, byte[] k, byte D) + { + byte[] T = new byte[16]; + System.arraycopy(N, 0, T, 0, 16); + block_cipher(s, k, T, 0, CNT, D); + } + + // Absorbs the AD blocks. + int ad_encryption(byte[] A, int AOff, byte[] s, byte[] k, int adlen, byte[] CNT, byte D) + { + byte[] T = new byte[16]; + byte[] mp = new byte[16]; + int n = 16; + int i, len8; + len8 = Math.min(adlen, n); + adlen -= len8; + // Rho(S,A) pads an A block and XORs it to the internal state. + pad(A, AOff, mp, n, len8); + for (i = 0; i < n; i++) + { + s[i] = (byte)(s[i] ^ mp[i]); + } + offset = AOff += len8; + lfsr_gf56(CNT); + if (adlen != 0) + { + len8 = Math.min(adlen, n); + adlen -= len8; + pad(A, AOff, T, n, len8); + offset = AOff + len8; + block_cipher(s, k, T, 0, CNT, D); + lfsr_gf56(CNT); + } + return adlen; + } + + private void reset_lfsr_gf56(byte[] CNT) + { + CNT[0] = 0x01; + CNT[1] = 0x00; + CNT[2] = 0x00; + CNT[3] = 0x00; + CNT[4] = 0x00; + CNT[5] = 0x00; + CNT[6] = 0x00; + } + + private void romulus_m_encrypt(byte[] c, byte[] m, int mlen, byte[] ad, int adlen, byte[] N, byte[] k) + { + byte[] s = new byte[16]; + byte[] CNT = new byte[7]; + T = new byte[16]; + int xlen, cOff = 0, mOff = 0, adOff = 0, mauth = 0; + byte w; + xlen = mlen; + reset_lfsr_gf56(CNT); + // Calculating the domain separation bits for the last block MAC TBC call depending on the length of M and AD + w = 48; + if ((adlen & 31) == 0 && adlen != 0) + { + w ^= 8; + } + else if ((adlen & 31) < AD_BLK_LEN_HALF) + { + w ^= 2; + } + else if ((adlen & 31) != AD_BLK_LEN_HALF) + { + w ^= 10; + } + if ((xlen & 31) == 0 && xlen != 0) + { + w ^= 4; + } + else if ((xlen & 31) < AD_BLK_LEN_HALF) + { + w ^= 1; + } + else if ((xlen & 31) != AD_BLK_LEN_HALF) + { + w ^= 5; + } + if (forEncryption) + { + if (adlen == 0) + { // AD is an empty string + lfsr_gf56(CNT); + } + else + { + while (adlen > 0) + { + offset = adOff; + adlen = ad_encryption(ad, adOff, s, k, adlen, CNT, (byte)40); + adOff = offset; + } + } + mOff = 0; + if ((w & 8) == 0) + { + byte[] Temp = new byte[16]; + int len8 = Math.min(xlen, AD_BLK_LEN_HALF); + xlen -= len8; + pad(m, mOff, Temp, AD_BLK_LEN_HALF, len8); + block_cipher(s, k, Temp, 0, CNT, (byte)44); + lfsr_gf56(CNT); + mOff += len8; + } + else if (mlen == 0) + { + lfsr_gf56(CNT); + } + while (xlen > 0) + { + offset = mOff; + xlen = ad_encryption(m, mOff, s, k, xlen, CNT, (byte)44); + mOff = offset; + } + nonce_encryption(N, CNT, s, k, w); + // Tag generation + g8A(s, T, 0); + mOff -= mlen; + } + else + { + System.arraycopy(m, mlen, T, 0, CRYPTO_ABYTES); + } + reset_lfsr_gf56(CNT); + System.arraycopy(T, 0, s, 0, AD_BLK_LEN_HALF); + if (mlen > 0) + { + nonce_encryption(N, CNT, s, k, (byte)36); + while (mlen > AD_BLK_LEN_HALF) + { + mlen = mlen - AD_BLK_LEN_HALF; + rho(m, mOff, c, cOff, s, AD_BLK_LEN_HALF); + cOff += AD_BLK_LEN_HALF; + mOff += AD_BLK_LEN_HALF; + lfsr_gf56(CNT); + nonce_encryption(N, CNT, s, k, (byte)36); + } + rho(m, mOff, c, cOff, s, mlen); + } + if (!forEncryption) + { + Arrays.fill(s, (byte)0); + reset_lfsr_gf56(CNT); + if (adlen == 0) + { // AD is an empty string + lfsr_gf56(CNT); + } + else + { + while (adlen > 0) + { + offset = adOff; + adlen = ad_encryption(ad, adOff, s, k, adlen, CNT, (byte)40); + adOff = offset; + } + } + if ((w & 8) == 0) + { + byte[] Temp = new byte[16]; + int len8 = Math.min(xlen, AD_BLK_LEN_HALF); + xlen -= len8; + pad(c, mauth, Temp, AD_BLK_LEN_HALF, len8); + block_cipher(s, k, Temp, 0, CNT, (byte)44); + lfsr_gf56(CNT); + mauth += len8; + mOff += len8; + } + else if (mlen == 0) + { + lfsr_gf56(CNT); + } + while (xlen > 0) + { + offset = mauth; + xlen = ad_encryption(c, mauth, s, k, xlen, CNT, (byte)44); + mauth = offset; + } + nonce_encryption(N, CNT, s, k, w); + // Tag generation + g8A(s, T, 0); + } + } + + private void romulus_n(byte[] c, byte[] I, int mlen, byte[] A, int adlen, byte[] N, byte[] k) + { + byte[] s = new byte[16]; + byte[] CNT = new byte[7]; + int mOff = 0, cOff = 0; + reset_lfsr_gf56(CNT); + if (adlen == 0) + { // AD is an empty string + lfsr_gf56(CNT); + nonce_encryption(N, CNT, s, k, (byte)0x1a); + } + else + { + while (adlen > 0) + { + if (adlen < AD_BLK_LEN_HALF) + { // The last block of AD is odd and incomplete + adlen = ad_encryption(A, 0, s, k, adlen, CNT, (byte)0x08); + nonce_encryption(N, CNT, s, k, (byte)0x1a); + } + else if (adlen == AD_BLK_LEN_HALF) + { // The last block of AD is odd and complete + adlen = ad_encryption(A, 0, s, k, adlen, CNT, (byte)0x08); + nonce_encryption(N, CNT, s, k, (byte)0x18); + } + else if (adlen < (AD_BLK_LEN_HALF + AD_BLK_LEN_HALF)) + { // The last block of AD is even and incomplete + adlen = ad_encryption(A, 0, s, k, adlen, CNT, (byte)0x08); + nonce_encryption(N, CNT, s, k, (byte)0x1a); + } + else if (adlen == (AD_BLK_LEN_HALF + AD_BLK_LEN_HALF)) + { // The last block of AD is even and complete + adlen = ad_encryption(A, 0, s, k, adlen, CNT, (byte)0x08); + nonce_encryption(N, CNT, s, k, (byte)0x18); + } + else + { // A normal full pair of blocks of AD + adlen = ad_encryption(A, 0, s, k, adlen, CNT, (byte)0x08); + } + } + } + reset_lfsr_gf56(CNT); + if (mlen == 0) + { // M is an empty string + lfsr_gf56(CNT); + nonce_encryption(N, CNT, s, k, (byte)0x15); + } + else + { + int tmp; + while (mlen > 0) + { + if (mlen < AD_BLK_LEN_HALF) + { // The last block of M is incomplete + tmp = mlen; + mlen = msg_encryption(I, mOff, c, cOff, N, CNT, s, k, (byte)0x15, mlen); + } + else if (mlen == AD_BLK_LEN_HALF) + { // The last block of M is complete + tmp = AD_BLK_LEN_HALF; + mlen = msg_encryption(I, mOff, c, cOff, N, CNT, s, k, (byte)0x14, mlen); + } + else + { // A normal full message block + tmp = AD_BLK_LEN_HALF; + mlen = msg_encryption(I, mOff, c, cOff, N, CNT, s, k, (byte)0x04, mlen); + } + mOff += tmp; + cOff += tmp; + } + } + // Tag generation + T = new byte[16]; + g8A(s, T, 0); + } + + // Absorbs and encrypts the message blocks. + int msg_encryption(byte[] m, int mOff, byte[] c, int cOff, byte[] N, byte[] CNT, byte[] s, byte[] k, byte D, int mlen) + { + int len8 = Math.min(mlen, AD_BLK_LEN_HALF); + mlen -= len8; + rho(m, mOff, c, cOff, s, len8); + lfsr_gf56(CNT); + nonce_encryption(N, CNT, s, k, D); + return mlen; + } + + private void romulus_t_encrypt(byte[] c, byte[] m, int mlen, byte[] A, int adlen, byte[] N, byte[] k) + throws InvalidCipherTextException + { + byte[] Z = new byte[16]; + byte[] CNT = new byte[7]; + byte[] CNT_Z = new byte[7]; + int mlen_int; + byte[] LR = new byte[32]; + int i; + int mOff = 0, cOff = 0; + reset_lfsr_gf56(CNT); + // Initialization function: KDF + byte[] S = new byte[16]; + mlen_int = mlen; + if (!forEncryption) + { + // T = hash(ipad*(A)||ipad*(C)||N||CNT) + T = new byte[16]; + crypto_hash_vector(LR, A, adlen, m, mOff, mlen_int, N, CNT); + // Generates the tag T from the final state S by applying the Tag Generation Function (TGF). + block_cipher(LR, k, LR, 16, CNT_Z, (byte)68); + System.arraycopy(LR, 0, T, 0, 16); + reset_lfsr_gf56(CNT); + tagVerification(m, mlen_int); + } + T = new byte[16]; + System.arraycopy(N, 0, Z, 0, 16); + block_cipher(Z, k, T, 0, CNT_Z, (byte)66); + while (mlen != 0) + { + int len8 = Math.min(mlen, 16); + mlen -= len8; + System.arraycopy(N, 0, S, 0, 16); + block_cipher(S, Z, T, 0, CNT, (byte)64); + for (i = 0; i < len8 && i + cOff < c.length; i++) + { + c[i + cOff] = (byte)((m[i + mOff]) ^ S[i]); + } + cOff += len8; + mOff += len8; + System.arraycopy(N, 0, S, 0, 16); + if (mlen != 0) + { + block_cipher(S, Z, T, 0, CNT, (byte)65); + System.arraycopy(S, 0, Z, 0, 16); + } + lfsr_gf56(CNT); + } + if (forEncryption) + { + // T = hash(A||N||M) + // We need to first pad A, N and C + cOff -= mlen_int; + crypto_hash_vector(LR, A, adlen, c, cOff, mlen_int, N, CNT); + // Generates the tag T from the final state S by applying the Tag Generation Function (TGF). + block_cipher(LR, k, LR, 16, CNT_Z, (byte)68); + System.arraycopy(LR, 0, T, 0, 16); + } + } + + // This function is required for Romulus-T. It assumes that the input comes in three parts that can +// be stored in different locations in the memory. It processes these inputs sequentially. +// The padding is ipad_256(ipad*_128(A)||ipad*_128(C)||N|| CNT ) +// A and C are of variable length, while N is of 16 bytes and CNT is of 7 bytes + private void crypto_hash_vector(byte[] out, byte[] A, int adlen, byte[] C, int cOff, int clen, byte[] N, byte[] CNT) + { + byte[] h = new byte[16]; + byte[] g = new byte[16]; + byte[] p = new byte[32]; + int aOff = 0; + int n = 16; + byte adempty = (byte)((adlen == 0) ? 1 : 0); + byte cempty = (byte)(clen == 0 ? 1 : 0); + reset_lfsr_gf56(CNT); + while (adlen >= 32) + { // AD Normal loop + hirose_128_128_256(h, g, A, aOff); + aOff += 32; + adlen -= 32; + } + // Partial block (or in case there is no partial block we add a 0^2n block + if (adlen >= 16) + { + ipad_128(A, aOff, p, 0, 32, adlen); + hirose_128_128_256(h, g, p, 0); + } + else if ((adlen >= 0) && (adempty == 0)) + { + ipad_128(A, aOff, p, 0, 16, adlen); + if (clen >= 16) + { + System.arraycopy(C, cOff, p, 16, 16); + hirose_128_128_256(h, g, p, 0); + lfsr_gf56(CNT); + clen -= 16; + cOff += 16; + } + else if (clen > 0) + { + ipad_128(C, cOff, p, 16, 16, clen); + hirose_128_128_256(h, g, p, 0); + clen = 0; + cempty = 1; + cOff += 16; + lfsr_gf56(CNT); + } + else + { + System.arraycopy(N, 0, p, 16, 16); + hirose_128_128_256(h, g, p, 0); + n = 0; + } + } + while (clen >= 32) + { // C Normal loop + hirose_128_128_256(h, g, C, cOff); + cOff += 32; + clen -= 32; + lfsr_gf56(CNT); + lfsr_gf56(CNT); + } + if (clen > 16) + { + ipad_128(C, cOff, p, 0, 32, clen); + hirose_128_128_256(h, g, p, 0); + lfsr_gf56(CNT); + lfsr_gf56(CNT); + } + else if (clen == 16) + { + ipad_128(C, cOff, p, 0, 32, clen); + hirose_128_128_256(h, g, p, 0); + lfsr_gf56(CNT); + } + else if ((clen >= 0) && (cempty == 0)) + { + ipad_128(C, cOff, p, 0, 16, clen); + if (clen > 0) + { + lfsr_gf56(CNT); + } + // Pad the nonce + System.arraycopy(N, 0, p, 16, 16); + hirose_128_128_256(h, g, p, 0); + n = 0; + } + if (n == 16) + { + // Pad the nonce and counter + System.arraycopy(N, 0, p, 0, 16); + System.arraycopy(CNT, 0, p, 16, 7); + ipad_256(p, p, 23); + } + else + { + ipad_256(CNT, p, 7); + } + h[0] ^= 2; + hirose_128_128_256(h, g, p, 0); + // Assign the output tag + System.arraycopy(h, 0, out, 0, 16); + System.arraycopy(g, 0, out, 16, 16); + } + + // Padding function: pads the byte length of the message mod 32 to the last incomplete block. +// For complete blocks it returns the same block. For an empty block it returns a 0^2n string. +// The function is called for full block messages to add a 0^2n block. This and the modulus are +// the only differences compared to the use in Romulus-N + void ipad_256(byte[] m, byte[] mp, int len8) + { + System.arraycopy(m, 0, mp, 0, len8); + Arrays.fill(mp, len8, 31, (byte)0); + mp[31] = (byte)(len8 & 0x1f); + } + + // Padding function: pads the byte length of the message mod 32 to the last incomplete block. +// For complete blocks it returns the same block. For an empty block it returns a 0^2n string. +// The function is called for full block messages to add a 0^2n block. This and the modulus are +// the only differences compared to the use in Romulus-N + void ipad_128(byte[] m, int mOff, byte[] mp, int mpOff, int l, int len8) + { + System.arraycopy(m, mOff, mp, mpOff, len8); + Arrays.fill(mp, len8 + mpOff, l - 1 + mpOff, (byte)0); + mp[mpOff + l - 1] = (byte)(len8 & 0xf); + } + + // The hirose double-block length (DBL) compression function. + void hirose_128_128_256(byte[] h, byte[] g, byte[] m, int mOff) + { + byte[] key = new byte[48]; + byte[] hh = new byte[16]; + int i; + // assign the key for the hirose compresison function + System.arraycopy(g, 0, key, 0, 16); + System.arraycopy(h, 0, g, 0, 16); + System.arraycopy(h, 0, hh, 0, 16); + g[0] ^= 0x01; + System.arraycopy(m, mOff, key, 16, 32); + skinny_128_384_plus_enc(h, key); + skinny_128_384_plus_enc(g, key); + for (i = 0; i < 16; i++) + { + h[i] ^= hh[i]; + g[i] ^= hh[i]; + } + g[0] ^= 0x01; + } + + @Override + public BlockCipher getUnderlyingCipher() + { + return null; + } + + @Override + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + this.forEncryption = forEncryption; + if (!(params instanceof ParametersWithIV)) + { + throw new IllegalArgumentException("Romulus init parameters must include an IV"); + } + + ParametersWithIV ivParams = (ParametersWithIV)params; + npub = ivParams.getIV(); + if (npub == null || npub.length != CRYPTO_ABYTES) + { + throw new IllegalArgumentException("Romulus requires exactly " + CRYPTO_ABYTES + " bytes of IV"); + } + + if (!(ivParams.getParameters() instanceof KeyParameter)) + { + throw new IllegalArgumentException("Romulus init parameters must include a key"); + } + + KeyParameter key = (KeyParameter)ivParams.getParameters(); + k = key.getKey(); + if (k.length != 16) + { + throw new IllegalArgumentException("Romulus key must be 16 bytes long"); + } + + CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties( + this.getAlgorithmName(), 128, params, Utils.getPurpose(forEncryption))); + initialised = true; + reset(false); + } + + @Override + public String getAlgorithmName() + { + return algorithmName; + } + + @Override + public void processAADByte(byte in) + { + aadData.write(in); + } + + @Override + public void processAADBytes(byte[] in, int inOff, int len) + { + if (inOff + len > in.length) + { + throw new DataLengthException(algorithmName + " input buffer too short"); + } + aadData.write(in, inOff, len); + } + + @Override + public int processByte(byte in, byte[] out, int outOff) + throws DataLengthException + { + message.write(in); + return 0; + } + + @Override + public int processBytes(byte[] input, int inOff, int len, byte[] out, int outOff) + throws DataLengthException + { + if (inOff + len > input.length) + { + throw new DataLengthException(algorithmName + " input buffer too short"); + } + message.write(input, inOff, len); + return 0; + } + + @Override + public int doFinal(byte[] out, int outOff) + throws IllegalStateException, InvalidCipherTextException + { + if (!initialised) + { + throw new IllegalArgumentException(algorithmName + " needs call init function before dofinal"); + } + int len = message.size(), inOff = 0; + if ((forEncryption && len + CRYPTO_ABYTES + outOff > out.length) || + (!forEncryption && len - CRYPTO_ABYTES + outOff > out.length)) + { + throw new OutputLengthException("output buffer is too short"); + } + byte[] ad = aadData.toByteArray(); + int adlen = ad.length; + byte[] input = message.toByteArray(); + + if ((ad.length & 7) != 0) + { + byte[] tmp = new byte[((ad.length >> 3) << 3) + 16]; + System.arraycopy(ad, 0, tmp, 0, adlen); + ad = tmp; + } + if ((len & 7) != 0) + { + byte[] tmp = new byte[((len >> 3) << 3) + 16]; + System.arraycopy(input, inOff, tmp, 0, len); + input = tmp; + } + byte[] out_tmp = new byte[((len >> 3) << 3) + 16]; + len -= (forEncryption ? 0 : 16); + switch (romulusParameters) + { + case RomulusM: + romulus_m_encrypt(out_tmp, input, len, ad, adlen, npub, k); + break; + case RomulusN: + romulus_n(out_tmp, input, len, ad, adlen, npub, k); + break; + case RomulusT: + romulus_t_encrypt(out_tmp, input, len, ad, adlen, npub, k); + break; + } + System.arraycopy(out_tmp, 0, out, outOff, len); + outOff += len; + if (forEncryption) + { + System.arraycopy(T, 0, out, outOff, CRYPTO_ABYTES); + len += CRYPTO_ABYTES; + } + else + { + if (romulusParameters != RomulusParameters.RomulusT) + { + tagVerification(input, len); + } + } + reset(false); + return len; + } + + private void tagVerification(byte[] input, int inOff) + throws InvalidCipherTextException + { + for (int i = 0; i < 16; ++i) + { + if (T[i] != input[inOff + i]) + { + throw new InvalidCipherTextException("mac check in " + algorithmName + " failed"); + } + } + } + + @Override + public byte[] getMac() + { + return T; + } + + @Override + public int getUpdateOutputSize(int len) + { + int totalData = message.size() + len; + if (!forEncryption) + { + if (totalData < 32) + { + return 0; + } + totalData -= CRYPTO_ABYTES; + } + return totalData - totalData % CRYPTO_ABYTES; + } + + @Override + public int getOutputSize(int len) + { + int totalData = message.size() + len; + if (forEncryption) + { + return totalData + CRYPTO_ABYTES; + } + return Math.max(0, totalData - CRYPTO_ABYTES); + } + + @Override + public void reset() + { + reset(true); + } + + private void reset(boolean clearMac) + { + if (clearMac) + { + T = null; + } + aadData.reset(); + message.reset(); + } + + public int getKeyBytesSize() + { + return 16; + } + + public int getIVBytesSize() + { + return 16; + } + + public int getBlockSize() + { + return 32; + } +} diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java index c945e3154a..e01ed64e00 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java @@ -195,6 +195,8 @@ public class RegressionTest new SparkleTest(), new ISAPTest(), new ConcatenationKDFTest(), + new GiftCofbTest(), + new RomulusTest(), }; public static void main(String[] args) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java new file mode 100644 index 0000000000..b66ca673bb --- /dev/null +++ b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java @@ -0,0 +1,138 @@ +package org.bouncycastle.crypto.test; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Random; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.digests.RomulusDigest; +import org.bouncycastle.crypto.engines.GiftCofbEngine; +import org.bouncycastle.crypto.engines.RomulusEngine; +import org.bouncycastle.crypto.modes.AEADBlockCipher; +import org.bouncycastle.crypto.params.AEADParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +public class RomulusTest + extends SimpleTest +{ + public String getName() + { + return "Romulus"; + } + + public void performTest() + throws Exception + { + CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT), "crypto/romulus", "t_LWC_AEAD_KAT_128_128.txt", this); + CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM), "crypto/romulus", "m_LWC_AEAD_KAT_128_128.txt", this); + CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN), "crypto/romulus", "n_LWC_AEAD_KAT_128_128.txt", this); +// RomulusEngine romulus = new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT); +// testExceptions(romulus, romulus.getKeyBytesSize(), romulus.getIVBytesSize(), romulus.getBlockSize()); +// romulus = new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM); +// testExceptions(romulus, romulus.getKeyBytesSize(), romulus.getIVBytesSize(), romulus.getBlockSize()); +// romulus = new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN); +// testExceptions(romulus, romulus.getKeyBytesSize(), romulus.getIVBytesSize(), romulus.getBlockSize()); +// testExceptions(new RomulusDigest(), 32); +// //testVectorsHash(); +// testVectors(RomulusEngine.RomulusParameters.RomulusT, "t"); +// testVectors(RomulusEngine.RomulusParameters.RomulusM, "m"); +// testVectors(RomulusEngine.RomulusParameters.RomulusN, "n"); + } + + + + private void testVectorsHash() + throws Exception + { + RomulusDigest Romulus = new RomulusDigest(); + InputStream src = RomulusTest.class.getResourceAsStream("/org/bouncycastle/crypto/test/romulus/LWC_HASH_KAT_256.txt"); + BufferedReader bin = new BufferedReader(new InputStreamReader(src)); + String line; + byte[] ptByte, adByte; + byte[] rv; + HashMap map = new HashMap(); + while ((line = bin.readLine()) != null) + { + int a = line.indexOf('='); + if (a < 0) + { +// if (!map.get("Count").equals("3")) +// { +// continue; +// } + Romulus.reset(); + ptByte = Hex.decode((String)map.get("Msg")); + Romulus.update(ptByte, 0, ptByte.length); + byte[] hash = new byte[Romulus.getDigestSize()]; + Romulus.doFinal(hash, 0); + if (!areEqual(hash, Hex.decode((String)map.get("MD")))) + { + mismatch("Keystream " + map.get("Count"), (String)map.get("MD"), hash); + } +// else +// { +// System.out.println("Keystream " + map.get("Count") + " pass"); +// } + map.clear(); + Romulus.reset(); + } + else + { + map.put(line.substring(0, a).trim(), line.substring(a + 1).trim()); + } + } + System.out.println("Romulus Hash pass"); + } + + + private void testExceptions(Digest digest, int digestsize) + { + if (digest.getDigestSize() != digestsize) + { + fail(digest.getAlgorithmName() + ": digest size is not correct"); + } + + try + { + digest.update(new byte[1], 1, 1); + fail(digest.getAlgorithmName() + ": input for update is too short"); + } + catch (DataLengthException e) + { + //expected + } + try + { + digest.doFinal(new byte[digest.getDigestSize() - 1], 2); + fail(digest.getAlgorithmName() + ": output for dofinal is too short"); + } + catch (DataLengthException e) + { + //expected + } + System.out.println(digest.getAlgorithmName() + " test Exceptions pass"); + } + + + private void mismatch(String name, String expected, byte[] found) + { + fail("mismatch on " + name, expected, new String(Hex.encode(found))); + } + + public static void main(String[] args) + { + runTest(new RomulusTest()); + } + +} + + + From a19343db20246d26924646befb23e7dc20347f10 Mon Sep 17 00:00:00 2001 From: royb Date: Fri, 17 Jan 2025 14:58:10 -0500 Subject: [PATCH 023/890] Lowered tls logs to match the changes --- .../bouncycastle/jsse/provider/ProvTlsClient.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsClient.java b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsClient.java index f26afc0652..11f89da0c0 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsClient.java +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsClient.java @@ -477,9 +477,9 @@ public void notifyConnectionClosed() { super.notifyConnectionClosed(); - if (LOG.isLoggable(Level.INFO)) + if (LOG.isLoggable(Level.FINE)) { - LOG.info(clientID + " disconnected from " + JsseUtils.getPeerReport(manager)); + LOG.fine(clientID + " disconnected from " + JsseUtils.getPeerReport(manager)); } } @@ -488,9 +488,9 @@ public void notifyHandshakeBeginning() throws IOException { super.notifyHandshakeBeginning(); - if (LOG.isLoggable(Level.INFO)) + if (LOG.isLoggable(Level.FINE)) { - LOG.info(clientID + " opening connection to " + JsseUtils.getPeerReport(manager)); + LOG.fine(clientID + " opening connection to " + JsseUtils.getPeerReport(manager)); } ContextData contextData = manager.getContextData(); @@ -509,9 +509,9 @@ public synchronized void notifyHandshakeComplete() throws IOException this.handshakeComplete = true; - if (LOG.isLoggable(Level.INFO)) + if (LOG.isLoggable(Level.FINE)) { - LOG.info(clientID + " established connection with " + JsseUtils.getPeerReport(manager)); + LOG.fine(clientID + " established connection with " + JsseUtils.getPeerReport(manager)); } TlsSession connectionTlsSession = context.getSession(); From e7616712342e28e88396feacb314d51ca6fd09eb Mon Sep 17 00:00:00 2001 From: David Hook Date: Sat, 18 Jan 2025 11:05:52 +1100 Subject: [PATCH 024/890] added null check for algorithms (relates to github #1895) --- .../src/main/java/org/bouncycastle/tsp/TimeStampRequest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkix/src/main/java/org/bouncycastle/tsp/TimeStampRequest.java b/pkix/src/main/java/org/bouncycastle/tsp/TimeStampRequest.java index 4152a02fb6..76531d7aca 100644 --- a/pkix/src/main/java/org/bouncycastle/tsp/TimeStampRequest.java +++ b/pkix/src/main/java/org/bouncycastle/tsp/TimeStampRequest.java @@ -152,6 +152,11 @@ public void validate( policies = convert(policies); extensions = convert(extensions); + if (algorithms == null) + { + throw new TSPValidationException("no algorithms associated with request", PKIFailureInfo.badAlg); + } + if (!algorithms.contains(this.getMessageImprintAlgOID())) { throw new TSPValidationException("request contains unknown algorithm", PKIFailureInfo.badAlg); From 75b4e8a0e7c8aa838feea0836fb70f37ed1235a3 Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 20 Jan 2025 14:10:33 +1030 Subject: [PATCH 025/890] Pass test vector of Romulus N --- .../crypto/engines/RomulusEngine.java | 715 +++++++++++++----- .../bouncycastle/crypto/test/RomulusTest.java | 5 +- 2 files changed, 529 insertions(+), 191 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index f5e47c3024..87680bac20 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -2,18 +2,11 @@ import java.io.ByteArrayOutputStream; -import org.bouncycastle.crypto.BlockCipher; -import org.bouncycastle.crypto.CipherParameters; -import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; -import org.bouncycastle.crypto.OutputLengthException; -import org.bouncycastle.crypto.constraints.DefaultServiceProperties; -import org.bouncycastle.crypto.modes.AEADBlockCipher; -import org.bouncycastle.crypto.params.KeyParameter; -import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.util.Arrays; + /** * Romulus v1.3, based on the current round 3 submission, https://romulusae.github.io/romulus/ * Reference C implementation: https://github.com/romulusae/romulus @@ -21,7 +14,7 @@ */ public class RomulusEngine - implements AEADBlockCipher + extends AEADBufferBaseEngine { public enum RomulusParameters { @@ -30,18 +23,17 @@ public enum RomulusParameters RomulusT, } - private String algorithmName; - private boolean forEncryption; private boolean initialised; private final RomulusParameters romulusParameters; private int offset; - private byte[] T; private byte[] k; private byte[] npub; private final ByteArrayOutputStream aadData = new ByteArrayOutputStream(); private final ByteArrayOutputStream message = new ByteArrayOutputStream(); - private final int CRYPTO_ABYTES = 16; private final int AD_BLK_LEN_HALF = 16; + private int messegeLen; + private int aadLen; + private Instance instance; // Packing of data is done as follows (state[i][j] stands for row i and column j): // 0 1 2 3 @@ -96,22 +88,396 @@ public enum RomulusParameters public RomulusEngine(RomulusParameters romulusParameters) { + super(romulusParameters == RomulusParameters.RomulusN ? ProcessingBufferType.Buffered : ProcessingBufferType.Immediate); + KEY_SIZE = IV_SIZE = MAC_SIZE = BlockSize = AADBufferSize = 16; + m_bufferSizeDecrypt = MAC_SIZE + BlockSize; + m_buf = new byte[m_bufferSizeDecrypt]; + m_aad = new byte[AADBufferSize]; this.romulusParameters = romulusParameters; switch (romulusParameters) { case RomulusM: algorithmName = "Romulus-M"; + instance = new RomulusM(); break; case RomulusN: algorithmName = "Romulus-N"; + instance = new RomulusN(); break; case RomulusT: algorithmName = "Romulus-T"; + instance = new RomulusT(); break; } initialised = false; + + } + + private interface Instance + { + void processFinalBlock(byte[] output, int outOff); + + void processBufferAAD(byte[] input, int inOff); + + void processFinalAAD(); + + void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff); + + void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff); + + void reset(); + } + + private class RomulusM + implements Instance + { + byte[] mac_s = new byte[16]; + byte[] mac_CNT = new byte[7]; + + byte[] s = new byte[16]; + byte[] CNT = new byte[7]; + boolean isfirstblock; + + + public RomulusM() + { + mac = new byte[16]; + reset_lfsr_gf56(mac_CNT); + isfirstblock = true; + } + + @Override + public void processFinalBlock(byte[] output, int outOff) + { + byte w = 48; + if ((aadLen & 31) == 0 && aadLen != 0) + { + w ^= 8; + } + else if ((aadLen & 31) < AD_BLK_LEN_HALF) + { + w ^= 2; + } + else if ((aadLen & 31) != AD_BLK_LEN_HALF) + { + w ^= 10; + } + if ((messegeLen & 31) == 0 && messegeLen != 0) + { + w ^= 4; + } + else if ((messegeLen & 31) < AD_BLK_LEN_HALF) + { + w ^= 1; + } + else if ((messegeLen & 31) != AD_BLK_LEN_HALF) + { + w ^= 5; + } + if (forEncryption) + { + if ((w & 8) == 0 && isfirstblock) + { + byte[] Temp = new byte[16]; + int len8 = Math.min(messegeLen, AD_BLK_LEN_HALF); + pad(m_buf, 0, Temp, AD_BLK_LEN_HALF, len8); + block_cipher(mac_s, k, Temp, 0, mac_CNT, (byte)44); + lfsr_gf56(mac_CNT); + } + else if (messegeLen == 0) + { + lfsr_gf56(mac_CNT); + } + } + nonce_encryption(npub, mac_CNT, mac_s, k, w); + // Tag generation + g8A(mac_s, mac, 0); + } + + @Override + public void processBufferAAD(byte[] input, int inOff) + { + byte[] T = new byte[16]; + byte[] mp = new byte[16]; + // Rho(S,A) pads an A block and XORs it to the internal state. + pad(input, inOff, mp, 16, 16); + for (int i = 0; i < 16; i++) + { + mac_s[i] = (byte)(mac_s[i] ^ input[inOff + i]); + } + lfsr_gf56(mac_CNT); + inOff += 16; + block_cipher(mac_s, k, input, inOff, CNT, (byte)40); + lfsr_gf56(mac_CNT); + } + + @Override + public void processFinalAAD() + { + if (aadLen == 0) + { + // AD is an empty string + lfsr_gf56(mac_CNT); + } + else if (m_aadPos != 0) + { + byte[] T = new byte[16]; + pad(m_aad, 0, T, 16, m_aadPos); + block_cipher(mac_s, k, T, 0, mac_CNT, (byte)40); + lfsr_gf56(mac_CNT); + if (m_aadPos > 16) + { + int len8 = Math.min(m_aadPos - 16, 16); + pad(m_aad, 16, T, 16, len8); + block_cipher(s, k, T, 0, CNT, (byte)40); + lfsr_gf56(CNT); + } + } + } + + @Override + public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) + { + if ((aadLen == 0 || ((aadLen & 31) > 0 && (aadLen & 31) < 15))) + { + block_cipher(mac_s, k, input, inOff, mac_CNT, (byte)44); + lfsr_gf56(mac_CNT); + for (int i = 0; i < AD_BLK_LEN_HALF; i++) + { + mac_s[i] = (byte)(mac_s[i] ^ input[inOff + 16 + i]); + } + lfsr_gf56(mac_CNT); + } + else + { + for (int i = 0; i < AD_BLK_LEN_HALF; i++) + { + mac_s[i] = (byte)(mac_s[i] ^ input[inOff + i]); + } + lfsr_gf56(mac_CNT); + block_cipher(mac_s, k, input, inOff + AD_BLK_LEN_HALF, mac_CNT, (byte)44); + lfsr_gf56(mac_CNT); + } + if (isfirstblock) + { + isfirstblock = false; + nonce_encryption(npub, CNT, s, k, (byte)36); + } + rho(input, inOff, output, outOff, s, AD_BLK_LEN_HALF); + lfsr_gf56(CNT); + } + + @Override + public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) + { + if (isfirstblock) + { + isfirstblock = false; + nonce_encryption(npub, CNT, s, k, (byte)36); + } + rho(input, inOff, output, outOff, s, AD_BLK_LEN_HALF); + lfsr_gf56(CNT); + if ((aadLen == 0 || ((aadLen & 31) > 0 && (aadLen & 31) < 15))) + { + block_cipher(mac_s, k, output, outOff, mac_CNT, (byte)44); + lfsr_gf56(mac_CNT); + for (int i = 0; i < 16; i++) + { + mac_s[i] = (byte)(mac_s[i] ^ output[outOff + AD_BLK_LEN_HALF + i]); + } + lfsr_gf56(mac_CNT); + } + else + { + for (int i = 0; i < 16; i++) + { + mac_s[i] = (byte)(mac_s[i] ^ output[outOff + i]); + } + lfsr_gf56(mac_CNT); + block_cipher(mac_s, k, output, outOff + AD_BLK_LEN_HALF, mac_CNT, (byte)44); + lfsr_gf56(mac_CNT); + } + } + + @Override + public void reset() + { + Arrays.clear(s); + Arrays.clear(CNT); + Arrays.clear(mac_s); + Arrays.clear(mac_CNT); + } } + private class RomulusN + implements Instance + { + private final byte[] s; + private final byte[] CNT; + boolean twist; + + public RomulusN() + { + s = new byte[AD_BLK_LEN_HALF]; + CNT = new byte[7]; + twist = true; + } + + @Override + public void processFinalBlock(byte[] output, int outOff) + { + messegeLen -= (forEncryption ? 0 : MAC_SIZE); + if (messegeLen == 0) + { + lfsr_gf56(CNT); + nonce_encryption(npub, CNT, s, k, (byte)0x15); + } + else if (m_bufPos != 0) + { + int len8 = Math.min(m_bufPos, AD_BLK_LEN_HALF); + rho(m_buf, 0, output, outOff, s, len8); + lfsr_gf56(CNT); + nonce_encryption(npub, CNT, s, k, m_bufPos == AD_BLK_LEN_HALF ? (byte)0x14 : (byte)0x15); + } + mac = new byte[MAC_SIZE]; + g8A(s, mac, 0); + } + + @Override + public void processBufferAAD(byte[] input, int inOff) + { + if (twist) + { + for (int i = 0; i < AD_BLK_LEN_HALF; i++) + { + s[i] = (byte)(s[i] ^ input[inOff + i]); + } + } + else + { + block_cipher(s, k, input, inOff, CNT, (byte)0x08); + } + lfsr_gf56(CNT); + twist = !twist; + } + + @Override + public void processFinalAAD() + { + if (m_aadPos != 0) + { + byte[] mp = new byte[AD_BLK_LEN_HALF]; + int len8 = Math.min(m_aadPos, AD_BLK_LEN_HALF); + pad(m_aad, 0, mp, AD_BLK_LEN_HALF, len8); + if (twist) + { + for (int i = 0; i < AD_BLK_LEN_HALF; i++) + { + s[i] = (byte)(s[i] ^ mp[i]); + } + } + else + { + block_cipher(s, k, mp, 0, CNT, (byte)0x08); + } + lfsr_gf56(CNT); + } + if (aadLen == 0) + { + + lfsr_gf56(CNT); + nonce_encryption(npub, CNT, s, k, (byte)0x1a); + } + else if ((m_aadPos & 15) != 0) + { + nonce_encryption(npub, CNT, s, k, (byte)0x1a); + } + else + { + nonce_encryption(npub, CNT, s, k, (byte)0x18); + } + reset_lfsr_gf56(CNT); + } + + @Override + public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) + { + g8A(s, output, outOff); + for (int i = 0; i < AD_BLK_LEN_HALF; i++) + { + s[i] ^= input[i + inOff]; + output[i + outOff] ^= input[i + inOff]; + } + lfsr_gf56(CNT); + nonce_encryption(npub, CNT, s, k, (byte)0x04); + } + + @Override + public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) + { + g8A(s, output, outOff); + for (int i = 0; i < AD_BLK_LEN_HALF; i++) + { + s[i] ^= input[i + inOff]; + s[i] ^= output[i + outOff]; + output[i + outOff] ^= input[i + inOff]; + } + lfsr_gf56(CNT); + nonce_encryption(npub, CNT, s, k, (byte)0x04); + } + + @Override + public void reset() + { + Arrays.clear(CNT); + Arrays.clear(s); + reset_lfsr_gf56(CNT); + twist = true; + } + } + + private class RomulusT + implements Instance + { + + @Override + public void processFinalBlock(byte[] output, int outOff) + { + + } + + @Override + public void processBufferAAD(byte[] input, int inOff) + { + + } + + @Override + public void processFinalAAD() + { + + } + + @Override + public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) + { + + } + + @Override + public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) + { + + } + + @Override + public void reset() + { + + } + } + + private void skinny_128_384_plus_enc(byte[] input, byte[] userkey) { byte[][] state = new byte[4][4]; @@ -363,7 +729,7 @@ private void romulus_m_encrypt(byte[] c, byte[] m, int mlen, byte[] ad, int adle { byte[] s = new byte[16]; byte[] CNT = new byte[7]; - T = new byte[16]; + mac = new byte[16]; int xlen, cOff = 0, mOff = 0, adOff = 0, mauth = 0; byte w; xlen = mlen; @@ -432,15 +798,15 @@ else if (mlen == 0) } nonce_encryption(N, CNT, s, k, w); // Tag generation - g8A(s, T, 0); + g8A(s, mac, 0); mOff -= mlen; } else { - System.arraycopy(m, mlen, T, 0, CRYPTO_ABYTES); + System.arraycopy(m, mlen, mac, 0, MAC_SIZE); } reset_lfsr_gf56(CNT); - System.arraycopy(T, 0, s, 0, AD_BLK_LEN_HALF); + System.arraycopy(mac, 0, s, 0, AD_BLK_LEN_HALF); if (mlen > 0) { nonce_encryption(N, CNT, s, k, (byte)36); @@ -495,7 +861,7 @@ else if (mlen == 0) } nonce_encryption(N, CNT, s, k, w); // Tag generation - g8A(s, T, 0); + g8A(s, mac, 0); } } @@ -571,8 +937,8 @@ else if (mlen == AD_BLK_LEN_HALF) } } // Tag generation - T = new byte[16]; - g8A(s, T, 0); + mac = new byte[16]; + g8A(s, mac, 0); } // Absorbs and encrypts the message blocks. @@ -603,23 +969,23 @@ private void romulus_t_encrypt(byte[] c, byte[] m, int mlen, byte[] A, int adlen if (!forEncryption) { // T = hash(ipad*(A)||ipad*(C)||N||CNT) - T = new byte[16]; + mac = new byte[16]; crypto_hash_vector(LR, A, adlen, m, mOff, mlen_int, N, CNT); // Generates the tag T from the final state S by applying the Tag Generation Function (TGF). block_cipher(LR, k, LR, 16, CNT_Z, (byte)68); - System.arraycopy(LR, 0, T, 0, 16); + System.arraycopy(LR, 0, mac, 0, 16); reset_lfsr_gf56(CNT); tagVerification(m, mlen_int); } - T = new byte[16]; + mac = new byte[16]; System.arraycopy(N, 0, Z, 0, 16); - block_cipher(Z, k, T, 0, CNT_Z, (byte)66); + block_cipher(Z, k, mac, 0, CNT_Z, (byte)66); while (mlen != 0) { int len8 = Math.min(mlen, 16); mlen -= len8; System.arraycopy(N, 0, S, 0, 16); - block_cipher(S, Z, T, 0, CNT, (byte)64); + block_cipher(S, Z, mac, 0, CNT, (byte)64); for (i = 0; i < len8 && i + cOff < c.length; i++) { c[i + cOff] = (byte)((m[i + mOff]) ^ S[i]); @@ -629,7 +995,7 @@ private void romulus_t_encrypt(byte[] c, byte[] m, int mlen, byte[] A, int adlen System.arraycopy(N, 0, S, 0, 16); if (mlen != 0) { - block_cipher(S, Z, T, 0, CNT, (byte)65); + block_cipher(S, Z, mac, 0, CNT, (byte)65); System.arraycopy(S, 0, Z, 0, 16); } lfsr_gf56(CNT); @@ -642,7 +1008,7 @@ private void romulus_t_encrypt(byte[] c, byte[] m, int mlen, byte[] A, int adlen crypto_hash_vector(LR, A, adlen, c, cOff, mlen_int, N, CNT); // Generates the tag T from the final state S by applying the Tag Generation Function (TGF). block_cipher(LR, k, LR, 16, CNT_Z, (byte)68); - System.arraycopy(LR, 0, T, 0, 16); + System.arraycopy(LR, 0, mac, 0, 16); } } @@ -795,222 +1161,193 @@ void hirose_128_128_256(byte[] h, byte[] g, byte[] m, int mOff) } @Override - public BlockCipher getUnderlyingCipher() - { - return null; - } - - @Override - public void init(boolean forEncryption, CipherParameters params) + public void init(byte[] key, byte[] iv) throws IllegalArgumentException { - this.forEncryption = forEncryption; - if (!(params instanceof ParametersWithIV)) - { - throw new IllegalArgumentException("Romulus init parameters must include an IV"); - } - - ParametersWithIV ivParams = (ParametersWithIV)params; - npub = ivParams.getIV(); - if (npub == null || npub.length != CRYPTO_ABYTES) - { - throw new IllegalArgumentException("Romulus requires exactly " + CRYPTO_ABYTES + " bytes of IV"); - } - - if (!(ivParams.getParameters() instanceof KeyParameter)) - { - throw new IllegalArgumentException("Romulus init parameters must include a key"); - } - - KeyParameter key = (KeyParameter)ivParams.getParameters(); - k = key.getKey(); - if (k.length != 16) - { - throw new IllegalArgumentException("Romulus key must be 16 bytes long"); - } - - CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties( - this.getAlgorithmName(), 128, params, Utils.getPurpose(forEncryption))); + npub = iv; + k = key; initialised = true; + m_state = forEncryption ? State.EncInit : State.DecInit; reset(false); } - @Override - public String getAlgorithmName() - { - return algorithmName; - } - @Override public void processAADByte(byte in) { - aadData.write(in); + aadLen++; + super.processAADByte(in); +// aadData.write(in); } + // @Override public void processAADBytes(byte[] in, int inOff, int len) { - if (inOff + len > in.length) - { - throw new DataLengthException(algorithmName + " input buffer too short"); - } - aadData.write(in, inOff, len); - } - - @Override - public int processByte(byte in, byte[] out, int outOff) - throws DataLengthException - { - message.write(in); - return 0; +// if (inOff + len > in.length) +// { +// throw new DataLengthException(algorithmName + " input buffer too short"); +// } + aadLen += len; + super.processAADBytes(in, inOff, len); + //aadData.write(in, inOff, len); } +// +// @Override +// public int processByte(byte in, byte[] out, int outOff) +// throws DataLengthException +// { +// message.write(in); +// return 0; +// } @Override public int processBytes(byte[] input, int inOff, int len, byte[] out, int outOff) throws DataLengthException { - if (inOff + len > input.length) - { - throw new DataLengthException(algorithmName + " input buffer too short"); - } - message.write(input, inOff, len); - return 0; + messegeLen += len; + return super.processBytes(input, inOff, len, out, outOff); +// if (inOff + len > input.length) +// { +// throw new DataLengthException(algorithmName + " input buffer too short"); +// } +// message.write(input, inOff, len); +// return 0; } - @Override - public int doFinal(byte[] out, int outOff) - throws IllegalStateException, InvalidCipherTextException - { - if (!initialised) - { - throw new IllegalArgumentException(algorithmName + " needs call init function before dofinal"); - } - int len = message.size(), inOff = 0; - if ((forEncryption && len + CRYPTO_ABYTES + outOff > out.length) || - (!forEncryption && len - CRYPTO_ABYTES + outOff > out.length)) - { - throw new OutputLengthException("output buffer is too short"); - } - byte[] ad = aadData.toByteArray(); - int adlen = ad.length; - byte[] input = message.toByteArray(); +// @Override +// public int doFinal(byte[] out, int outOff) +// throws IllegalStateException, InvalidCipherTextException +// { +// if (!initialised) +// { +// throw new IllegalArgumentException(algorithmName + " needs call init function before dofinal"); +// } +// int len = message.size(), inOff = 0; +// if ((forEncryption && len + KEY_SIZE + outOff > out.length) || +// (!forEncryption && len - KEY_SIZE + outOff > out.length)) +// { +// throw new OutputLengthException("output buffer is too short"); +// } +// byte[] ad = aadData.toByteArray(); +// int adlen = ad.length; +// byte[] input = message.toByteArray(); +// +// if ((ad.length & 7) != 0) +// { +// byte[] tmp = new byte[((ad.length >> 3) << 3) + 16]; +// System.arraycopy(ad, 0, tmp, 0, adlen); +// ad = tmp; +// } +// if ((len & 7) != 0) +// { +// byte[] tmp = new byte[((len >> 3) << 3) + 16]; +// System.arraycopy(input, inOff, tmp, 0, len); +// input = tmp; +// } +// byte[] out_tmp = new byte[((len >> 3) << 3) + 16]; +// len -= (forEncryption ? 0 : 16); +// switch (romulusParameters) +// { +// case RomulusM: +// romulus_m_encrypt(out_tmp, input, len, ad, adlen, npub, k); +// break; +// case RomulusN: +// romulus_n(out_tmp, input, len, ad, adlen, npub, k); +// break; +// case RomulusT: +// romulus_t_encrypt(out_tmp, input, len, ad, adlen, npub, k); +// break; +// } +// System.arraycopy(out_tmp, 0, out, outOff, len); +// outOff += len; +// if (forEncryption) +// { +// System.arraycopy(mac, 0, out, outOff, KEY_SIZE); +// len += KEY_SIZE; +// } +// else +// { +// if (romulusParameters != RomulusParameters.RomulusT) +// { +// tagVerification(input, len); +// } +// } +// reset(false); +// return len; +// } - if ((ad.length & 7) != 0) - { - byte[] tmp = new byte[((ad.length >> 3) << 3) + 16]; - System.arraycopy(ad, 0, tmp, 0, adlen); - ad = tmp; - } - if ((len & 7) != 0) + + protected void finishAAD(State nextState, boolean isDoFinal) + { + // State indicates whether we ever received AAD + switch (m_state) { - byte[] tmp = new byte[((len >> 3) << 3) + 16]; - System.arraycopy(input, inOff, tmp, 0, len); - input = tmp; - } - byte[] out_tmp = new byte[((len >> 3) << 3) + 16]; - len -= (forEncryption ? 0 : 16); - switch (romulusParameters) + case DecInit: + case DecAad: + case EncInit: + case EncAad: { - case RomulusM: - romulus_m_encrypt(out_tmp, input, len, ad, adlen, npub, k); - break; - case RomulusN: - romulus_n(out_tmp, input, len, ad, adlen, npub, k); - break; - case RomulusT: - romulus_t_encrypt(out_tmp, input, len, ad, adlen, npub, k); + processFinalAAD(); break; } - System.arraycopy(out_tmp, 0, out, outOff, len); - outOff += len; - if (forEncryption) - { - System.arraycopy(T, 0, out, outOff, CRYPTO_ABYTES); - len += CRYPTO_ABYTES; - } - else - { - if (romulusParameters != RomulusParameters.RomulusT) - { - tagVerification(input, len); - } + default: + break; } - reset(false); - return len; + + m_aadPos = 0; + m_state = nextState; } - private void tagVerification(byte[] input, int inOff) - throws InvalidCipherTextException + @Override + protected void processFinalBlock(byte[] output, int outOff) { - for (int i = 0; i < 16; ++i) - { - if (T[i] != input[inOff + i]) - { - throw new InvalidCipherTextException("mac check in " + algorithmName + " failed"); - } - } + instance.processFinalBlock(output, outOff); } @Override - public byte[] getMac() + protected void processBufferAAD(byte[] input, int inOff) { - return T; + instance.processBufferAAD(input, inOff); } @Override - public int getUpdateOutputSize(int len) + protected void processFinalAAD() { - int totalData = message.size() + len; - if (!forEncryption) - { - if (totalData < 32) - { - return 0; - } - totalData -= CRYPTO_ABYTES; - } - return totalData - totalData % CRYPTO_ABYTES; + instance.processFinalAAD(); } @Override - public int getOutputSize(int len) + protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { - int totalData = message.size() + len; - if (forEncryption) - { - return totalData + CRYPTO_ABYTES; - } - return Math.max(0, totalData - CRYPTO_ABYTES); + instance.processBufferEncrypt(input, inOff, output, outOff); } @Override - public void reset() + protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - reset(true); + instance.processBufferDecrypt(input, inOff, output, outOff); } - private void reset(boolean clearMac) + private void tagVerification(byte[] input, int inOff) + throws InvalidCipherTextException { - if (clearMac) + for (int i = 0; i < 16; ++i) { - T = null; + if (mac[i] != input[inOff + i]) + { + throw new InvalidCipherTextException("mac check in " + algorithmName + " failed"); + } } - aadData.reset(); - message.reset(); } - public int getKeyBytesSize() + protected void reset(boolean clearMac) { - return 16; - } - - public int getIVBytesSize() - { - return 16; - } - - public int getBlockSize() - { - return 32; + aadLen = 0; + messegeLen = 0; + instance.reset(); + bufferReset(); + aadData.reset(); + message.reset(); + super.reset(clearMac); } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java index b66ca673bb..294195d05e 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java @@ -31,9 +31,10 @@ public String getName() public void performTest() throws Exception { - CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT), "crypto/romulus", "t_LWC_AEAD_KAT_128_128.txt", this); - CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM), "crypto/romulus", "m_LWC_AEAD_KAT_128_128.txt", this); CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN), "crypto/romulus", "n_LWC_AEAD_KAT_128_128.txt", this); + //CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM), "crypto/romulus", "m_LWC_AEAD_KAT_128_128.txt", this); + //CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT), "crypto/romulus", "t_LWC_AEAD_KAT_128_128.txt", this); + // RomulusEngine romulus = new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT); // testExceptions(romulus, romulus.getKeyBytesSize(), romulus.getIVBytesSize(), romulus.getBlockSize()); // romulus = new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM); From c808b0ab718be2a00e25276e5e9d783aab7087b4 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 20 Jan 2025 22:32:21 +0700 Subject: [PATCH 026/890] TLS: Initial work on draft-tls-westerbaan-mldsa-00 --- .../jsse/provider/SignatureSchemeInfo.java | 5 ++ .../org/bouncycastle/tls/SignatureScheme.java | 29 +++++++++ .../bouncycastle/tls/crypto/impl/PQCUtil.java | 65 +++++++++++++++++++ .../bc/BcDefaultTlsCredentialedSigner.java | 28 +++++--- .../tls/crypto/impl/bc/BcTlsCrypto.java | 4 ++ .../tls/crypto/impl/bc/BcTlsMLDSASigner.java | 52 +++++++++++++++ .../impl/bc/BcTlsRawKeyCertificate.java | 52 +++++++++++++++ .../crypto/impl/jcajce/JcaTlsCertificate.java | 4 ++ .../tls/crypto/impl/jcajce/JcaTlsCrypto.java | 4 ++ .../crypto/impl/jcajce/JcaTlsCertificate.java | 4 ++ .../tls/crypto/impl/jcajce/JcaTlsCrypto.java | 4 ++ .../tls/crypto/test/TlsCryptoTest.java | 6 +- 12 files changed, 248 insertions(+), 9 deletions(-) create mode 100644 tls/src/main/java/org/bouncycastle/tls/crypto/impl/PQCUtil.java create mode 100644 tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLDSASigner.java diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/SignatureSchemeInfo.java b/tls/src/main/java/org/bouncycastle/jsse/provider/SignatureSchemeInfo.java index 08eea6bdde..e56f8bc677 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/provider/SignatureSchemeInfo.java +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/SignatureSchemeInfo.java @@ -71,6 +71,11 @@ private enum All sm2sig_sm3(SignatureScheme.sm2sig_sm3, "SM3withSM2", "EC"), + // TODO[tls] Need mechanism for restricting signature schemes to TLS 1.3+ before adding +// DRAFT_mldsa44(SignatureScheme.DRAFT_mldsa44, "ML-DSA-44", "ML-DSA-44"), +// DRAFT_mldsa65(SignatureScheme.DRAFT_mldsa65, "ML-DSA-65", "ML-DSA-65"), +// DRAFT_mldsa87(SignatureScheme.DRAFT_mldsa87, "ML-DSA-87", "ML-DSA-87"), + /* * Legacy/Historical: mostly not supported in 1.3, except ecdsa_sha1 and rsa_pkcs1_sha1 are * still permitted as a last resort for certs. diff --git a/tls/src/main/java/org/bouncycastle/tls/SignatureScheme.java b/tls/src/main/java/org/bouncycastle/tls/SignatureScheme.java index 2a980300fc..4d2479921b 100644 --- a/tls/src/main/java/org/bouncycastle/tls/SignatureScheme.java +++ b/tls/src/main/java/org/bouncycastle/tls/SignatureScheme.java @@ -45,6 +45,13 @@ public class SignatureScheme public static final int sm2sig_sm3 = 0x0708; + /* + * draft-tls-westerbaan-mldsa-00 + */ + public static final int DRAFT_mldsa44 = 0x0904; + public static final int DRAFT_mldsa65 = 0x0905; + public static final int DRAFT_mldsa87 = 0x0906; + /* * RFC 8446 reserved for private use (0xFE00..0xFFFF) */ @@ -70,6 +77,9 @@ public static int getCryptoHashAlgorithm(int signatureScheme) { case ed25519: case ed448: + case DRAFT_mldsa44: + case DRAFT_mldsa65: + case DRAFT_mldsa87: return -1; case ecdsa_brainpoolP256r1tls13_sha256: case rsa_pss_pss_sha256: @@ -146,6 +156,12 @@ public static String getName(int signatureScheme) return "ecdsa_brainpoolP512r1tls13_sha512"; case sm2sig_sm3: return "sm2sig_sm3"; + case DRAFT_mldsa44: + return "DRAFT_mldsa44"; + case DRAFT_mldsa65: + return "DRAFT_mldsa65"; + case DRAFT_mldsa87: + return "DRAFT_mldsa87"; default: return "UNKNOWN"; } @@ -238,6 +254,19 @@ public static boolean isECDSA(int signatureScheme) } } + public static boolean isMLDSA(int signatureScheme) + { + switch (signatureScheme) + { + case DRAFT_mldsa44: + case DRAFT_mldsa65: + case DRAFT_mldsa87: + return true; + default: + return false; + } + } + public static boolean isRSAPSS(int signatureScheme) { switch (signatureScheme) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/PQCUtil.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/PQCUtil.java new file mode 100644 index 0000000000..0a40e3dd05 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/PQCUtil.java @@ -0,0 +1,65 @@ +package org.bouncycastle.tls.crypto.impl; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.pqc.crypto.mldsa.MLDSAParameters; +import org.bouncycastle.tls.SignatureScheme; + +public class PQCUtil +{ + public static ASN1ObjectIdentifier getMLDSAObjectidentifier(int signatureScheme) + { + switch (signatureScheme) + { + case SignatureScheme.DRAFT_mldsa44: + return NISTObjectIdentifiers.id_ml_dsa_44; + case SignatureScheme.DRAFT_mldsa65: + return NISTObjectIdentifiers.id_ml_dsa_65; + case SignatureScheme.DRAFT_mldsa87: + return NISTObjectIdentifiers.id_ml_dsa_87; + default: + throw new IllegalArgumentException(); + } + } + + public static ASN1ObjectIdentifier getMLDSAObjectidentifier(MLDSAParameters parameters) + { + if (MLDSAParameters.ml_dsa_44 == parameters) + { + return NISTObjectIdentifiers.id_ml_dsa_44; + } + if (MLDSAParameters.ml_dsa_65 == parameters) + { + return NISTObjectIdentifiers.id_ml_dsa_65; + } + if (MLDSAParameters.ml_dsa_87 == parameters) + { + return NISTObjectIdentifiers.id_ml_dsa_87; + } + throw new IllegalArgumentException(); + } + + public static int getMLDSASignatureScheme(MLDSAParameters parameters) + { + if (MLDSAParameters.ml_dsa_44 == parameters) + { + return SignatureScheme.DRAFT_mldsa44; + } + if (MLDSAParameters.ml_dsa_65 == parameters) + { + return SignatureScheme.DRAFT_mldsa65; + } + if (MLDSAParameters.ml_dsa_87 == parameters) + { + return SignatureScheme.DRAFT_mldsa87; + } + throw new IllegalArgumentException(); + } + + public static boolean supportsMLDSA(AlgorithmIdentifier pubKeyAlgID, ASN1ObjectIdentifier algorithm) + { + return pubKeyAlgID.getAlgorithm().equals(algorithm) + && pubKeyAlgID.getParameters() == null; + } +} diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcDefaultTlsCredentialedSigner.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcDefaultTlsCredentialedSigner.java index 82b6dc79ac..b5f50f99db 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcDefaultTlsCredentialedSigner.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcDefaultTlsCredentialedSigner.java @@ -6,6 +6,7 @@ import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters; import org.bouncycastle.crypto.params.RSAKeyParameters; +import org.bouncycastle.pqc.crypto.mldsa.MLDSAPrivateKeyParameters; import org.bouncycastle.tls.Certificate; import org.bouncycastle.tls.DefaultTlsCredentialedSigner; import org.bouncycastle.tls.SignatureAndHashAlgorithm; @@ -22,7 +23,6 @@ public class BcDefaultTlsCredentialedSigner private static TlsSigner makeSigner(BcTlsCrypto crypto, AsymmetricKeyParameter privateKey, Certificate certificate, SignatureAndHashAlgorithm signatureAndHashAlgorithm) { - TlsSigner signer; if (privateKey instanceof RSAKeyParameters) { RSAKeyParameters privKeyRSA = (RSAKeyParameters)privateKey; @@ -36,11 +36,11 @@ private static TlsSigner makeSigner(BcTlsCrypto crypto, AsymmetricKeyParameter p } } - signer = new BcTlsRSASigner(crypto, privKeyRSA); + return new BcTlsRSASigner(crypto, privKeyRSA); } else if (privateKey instanceof DSAPrivateKeyParameters) { - signer = new BcTlsDSASigner(crypto, (DSAPrivateKeyParameters)privateKey); + return new BcTlsDSASigner(crypto, (DSAPrivateKeyParameters)privateKey); } else if (privateKey instanceof ECPrivateKeyParameters) { @@ -63,22 +63,34 @@ else if (privateKey instanceof ECPrivateKeyParameters) } } - signer = new BcTlsECDSASigner(crypto, privKeyEC); + return new BcTlsECDSASigner(crypto, privKeyEC); } else if (privateKey instanceof Ed25519PrivateKeyParameters) { - signer = new BcTlsEd25519Signer(crypto, (Ed25519PrivateKeyParameters)privateKey); + return new BcTlsEd25519Signer(crypto, (Ed25519PrivateKeyParameters)privateKey); } else if (privateKey instanceof Ed448PrivateKeyParameters) { - signer = new BcTlsEd448Signer(crypto, (Ed448PrivateKeyParameters)privateKey); + return new BcTlsEd448Signer(crypto, (Ed448PrivateKeyParameters)privateKey); + } + else if (privateKey instanceof MLDSAPrivateKeyParameters) + { + if (signatureAndHashAlgorithm != null) + { + TlsSigner signer = BcTlsMLDSASigner.create(crypto, (MLDSAPrivateKeyParameters)privateKey, + SignatureScheme.from(signatureAndHashAlgorithm)); + if (signer != null) + { + return signer; + } + } + + throw new IllegalArgumentException("ML-DSA private key of wrong type for signature algorithm"); } else { throw new IllegalArgumentException("'privateKey' type not supported: " + privateKey.getClass().getName()); } - - return signer; } public BcDefaultTlsCredentialedSigner(TlsCryptoParameters cryptoParams, BcTlsCrypto crypto, diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java index 9207c2f544..6265353d9e 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java @@ -455,6 +455,10 @@ public boolean hasSignatureScheme(int signatureScheme) switch (signatureScheme) { case SignatureScheme.sm2sig_sm3: + // TODO[tls] Test coverage before adding + case SignatureScheme.DRAFT_mldsa44: + case SignatureScheme.DRAFT_mldsa65: + case SignatureScheme.DRAFT_mldsa87: return false; default: { diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLDSASigner.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLDSASigner.java new file mode 100644 index 0000000000..69b6f2ea22 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLDSASigner.java @@ -0,0 +1,52 @@ +package org.bouncycastle.tls.crypto.impl.bc; + +import java.io.IOException; + +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.pqc.crypto.mldsa.MLDSAPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.mldsa.MLDSASigner; +import org.bouncycastle.tls.SignatureAndHashAlgorithm; +import org.bouncycastle.tls.SignatureScheme; +import org.bouncycastle.tls.crypto.TlsStreamSigner; +import org.bouncycastle.tls.crypto.impl.PQCUtil; + +public class BcTlsMLDSASigner + extends BcTlsSigner +{ + public static BcTlsMLDSASigner create(BcTlsCrypto crypto, MLDSAPrivateKeyParameters privateKey, int signatureScheme) + { + if (signatureScheme != PQCUtil.getMLDSASignatureScheme(privateKey.getParameters())) + { + return null; + } + + return new BcTlsMLDSASigner(crypto, privateKey, signatureScheme); + } + + private final int signatureScheme; + + private BcTlsMLDSASigner(BcTlsCrypto crypto, MLDSAPrivateKeyParameters privateKey, int signatureScheme) + { + super(crypto, privateKey); + + this.signatureScheme = signatureScheme; + } + + public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash) throws IOException + { + throw new UnsupportedOperationException(); + } + + public TlsStreamSigner getStreamSigner(SignatureAndHashAlgorithm algorithm) + { + if (algorithm == null || SignatureScheme.from(algorithm) != signatureScheme) + { + throw new IllegalStateException("Invalid algorithm: " + algorithm); + } + + MLDSASigner signer = new MLDSASigner(); + signer.init(true, new ParametersWithRandom(privateKey, crypto.getSecureRandom())); + + return new BcTlsStreamSigner(signer); + } +} diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsRawKeyCertificate.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsRawKeyCertificate.java index b975a720ba..7babf085c7 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsRawKeyCertificate.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsRawKeyCertificate.java @@ -26,6 +26,9 @@ import org.bouncycastle.crypto.signers.PSSSigner; import org.bouncycastle.crypto.signers.RSADigestSigner; import org.bouncycastle.crypto.util.PublicKeyFactory; +import org.bouncycastle.pqc.crypto.mldsa.MLDSAParameters; +import org.bouncycastle.pqc.crypto.mldsa.MLDSAPublicKeyParameters; +import org.bouncycastle.pqc.crypto.mldsa.MLDSASigner; import org.bouncycastle.tls.AlertDescription; import org.bouncycastle.tls.HashAlgorithm; import org.bouncycastle.tls.SignatureAlgorithm; @@ -39,6 +42,7 @@ import org.bouncycastle.tls.crypto.TlsEncryptor; import org.bouncycastle.tls.crypto.TlsVerifier; import org.bouncycastle.tls.crypto.impl.LegacyTls13Verifier; +import org.bouncycastle.tls.crypto.impl.PQCUtil; import org.bouncycastle.tls.crypto.impl.RSAUtil; /** @@ -247,6 +251,27 @@ public Tls13Verifier createVerifier(int signatureScheme) throws IOException // return new BcTls13Verifier(verifier); // } + case SignatureScheme.DRAFT_mldsa44: + case SignatureScheme.DRAFT_mldsa65: + case SignatureScheme.DRAFT_mldsa87: + { + ASN1ObjectIdentifier algorithm = PQCUtil.getMLDSAObjectidentifier(signatureScheme); + validateMLDSA(algorithm); + + MLDSAPublicKeyParameters publicKey = getPubKeyMLDSA(); + MLDSAParameters parameters = publicKey.getParameters(); + if (!PQCUtil.getMLDSAObjectidentifier(parameters).equals(algorithm)) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown, + "ML-DSA public key not for " + SignatureScheme.getText(signatureScheme)); + } + + MLDSASigner verifier = new MLDSASigner(); + verifier.init(false, publicKey); + + return new BcTls13Verifier(verifier); + } + default: throw new TlsFatalAlert(AlertDescription.internal_error); } @@ -387,6 +412,18 @@ public Ed448PublicKeyParameters getPubKeyEd448() throws IOException } } + public MLDSAPublicKeyParameters getPubKeyMLDSA() throws IOException + { + try + { + return (MLDSAPublicKeyParameters)getPublicKey(); + } + catch (ClassCastException e) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown, "Public key not ML-DSA", e); + } + } + public RSAKeyParameters getPubKeyRSA() throws IOException { try @@ -448,6 +485,12 @@ protected boolean supportsKeyUsage(int keyUsageBit) return true; } + protected boolean supportsMLDSA(ASN1ObjectIdentifier algorithm) + { + AlgorithmIdentifier pubKeyAlgID = keyInfo.getAlgorithm(); + return PQCUtil.supportsMLDSA(pubKeyAlgID, algorithm); + } + protected boolean supportsRSA_PKCS1() { AlgorithmIdentifier pubKeyAlgID = keyInfo.getAlgorithm(); @@ -539,6 +582,15 @@ public void validateKeyUsage(int keyUsageBit) } } + protected void validateMLDSA(ASN1ObjectIdentifier algorithm) + throws IOException + { + if (!supportsMLDSA(algorithm)) + { + throw new TlsFatalAlert(AlertDescription.certificate_unknown, "No support for ML-DSA signature scheme"); + } + } + protected void validateRSA_PKCS1() throws IOException { diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCertificate.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCertificate.java index e88b99302d..9f14399762 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCertificate.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCertificate.java @@ -274,6 +274,10 @@ public Tls13Verifier createVerifier(int signatureScheme) throws IOException // TODO[RFC 8998] // case SignatureScheme.sm2sig_sm3: + case SignatureScheme.DRAFT_mldsa44: + case SignatureScheme.DRAFT_mldsa65: + case SignatureScheme.DRAFT_mldsa87: + default: throw new TlsFatalAlert(AlertDescription.internal_error); } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index d89b0257ec..a42f4e201f 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -778,6 +778,10 @@ public boolean hasSignatureScheme(int signatureScheme) switch (signatureScheme) { case SignatureScheme.sm2sig_sm3: + // TODO[tls] Implement before adding + case SignatureScheme.DRAFT_mldsa44: + case SignatureScheme.DRAFT_mldsa65: + case SignatureScheme.DRAFT_mldsa87: return false; default: { diff --git a/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCertificate.java b/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCertificate.java index f0d9fed6c3..227a6d4c8b 100644 --- a/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCertificate.java +++ b/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCertificate.java @@ -274,6 +274,10 @@ public Tls13Verifier createVerifier(int signatureScheme) throws IOException // TODO[RFC 8998] // case SignatureScheme.sm2sig_sm3: + case SignatureScheme.DRAFT_mldsa44: + case SignatureScheme.DRAFT_mldsa65: + case SignatureScheme.DRAFT_mldsa87: + default: throw new TlsFatalAlert(AlertDescription.internal_error); } diff --git a/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index 5f5f6c7716..9c8639254d 100644 --- a/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -776,6 +776,10 @@ public boolean hasSignatureScheme(int signatureScheme) switch (signatureScheme) { case SignatureScheme.sm2sig_sm3: + // TODO[tls] Implement before adding + case SignatureScheme.DRAFT_mldsa44: + case SignatureScheme.DRAFT_mldsa65: + case SignatureScheme.DRAFT_mldsa87: return false; default: { diff --git a/tls/src/test/java/org/bouncycastle/tls/crypto/test/TlsCryptoTest.java b/tls/src/test/java/org/bouncycastle/tls/crypto/test/TlsCryptoTest.java index ed2689c571..58c66a6a4b 100644 --- a/tls/src/test/java/org/bouncycastle/tls/crypto/test/TlsCryptoTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/crypto/test/TlsCryptoTest.java @@ -195,6 +195,9 @@ protected TlsCredentialedSigner loadCredentialedSigner13(TlsCryptoParameters cry case SignatureScheme.ecdsa_secp384r1_sha384: case SignatureScheme.ecdsa_secp521r1_sha512: case SignatureScheme.sm2sig_sm3: + case SignatureScheme.DRAFT_mldsa44: + case SignatureScheme.DRAFT_mldsa65: + case SignatureScheme.DRAFT_mldsa87: default: return null; @@ -593,7 +596,8 @@ public void testSignatures13() throws Exception SignatureScheme.ecdsa_secp521r1_sha512, SignatureScheme.ed25519, SignatureScheme.ed448, SignatureScheme.rsa_pss_pss_sha256, SignatureScheme.rsa_pss_pss_sha384, SignatureScheme.rsa_pss_pss_sha512, SignatureScheme.rsa_pss_rsae_sha256, SignatureScheme.rsa_pss_rsae_sha384, - SignatureScheme.rsa_pss_rsae_sha512, SignatureScheme.sm2sig_sm3, + SignatureScheme.rsa_pss_rsae_sha512, SignatureScheme.sm2sig_sm3, SignatureScheme.DRAFT_mldsa44, + SignatureScheme.DRAFT_mldsa65, SignatureScheme.DRAFT_mldsa87, // These are only used for certs in 1.3 (cert verification is not done by TlsCrypto) // SignatureScheme.ecdsa_sha1, SignatureScheme.rsa_pkcs1_sha1, SignatureScheme.rsa_pkcs1_sha256, // SignatureScheme.rsa_pkcs1_sha384, SignatureScheme.rsa_pkcs1_sha512, From 12bf4c2f6d6a3f106d2c4e3df02070b4e2cdf130 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 20 Jan 2025 23:59:39 +0700 Subject: [PATCH 027/890] Add validatilon methods to Arrays --- .../java/org/bouncycastle/util/Arrays.java | 26 +++++++++++++++++++ .../org/bouncycastle/util/io/Streams.java | 13 +++------- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/util/Arrays.java b/core/src/main/java/org/bouncycastle/util/Arrays.java index 27371dd423..2067a9adb6 100644 --- a/core/src/main/java/org/bouncycastle/util/Arrays.java +++ b/core/src/main/java/org/bouncycastle/util/Arrays.java @@ -1240,4 +1240,30 @@ public static boolean isNullOrEmpty(Object[] array) { return null == array || array.length < 1; } + + public static void validateRange(byte[] buf, int from, int to) + { + if (buf == null) + { + throw new NullPointerException("'buf' cannot be null"); + } + if ((from | (buf.length - from) | (to - from) | (buf.length - to)) < 0) + { + throw new IndexOutOfBoundsException("buf.length: " + buf.length + ", from: " + from + ", to: " + to); + } + } + + public static void validateSegment(byte[] buf, int off, int len) + { + if (buf == null) + { + throw new NullPointerException("'buf' cannot be null"); + } + int available = buf.length - off; + int remaining = available - len; + if ((off | len | available | remaining) < 0) + { + throw new IndexOutOfBoundsException("buf.length: " + buf.length + ", off: " + off + ", len: " + len); + } + } } diff --git a/core/src/main/java/org/bouncycastle/util/io/Streams.java b/core/src/main/java/org/bouncycastle/util/io/Streams.java index c72a476fa1..21eb4a4ebe 100644 --- a/core/src/main/java/org/bouncycastle/util/io/Streams.java +++ b/core/src/main/java/org/bouncycastle/util/io/Streams.java @@ -5,6 +5,8 @@ import java.io.InputStream; import java.io.OutputStream; +import org.bouncycastle.util.Arrays; + /** * Utility methods to assist with stream processing. */ @@ -160,16 +162,7 @@ public static int readFully(InputStream inStr, byte[] buf, int off, int len) public static void validateBufferArguments(byte[] buf, int off, int len) { - if (buf == null) - { - throw new NullPointerException(); - } - int available = buf.length - off; - int remaining = available - len; - if ((off | len | available | remaining) < 0) - { - throw new IndexOutOfBoundsException(); - } + Arrays.validateSegment(buf, off, len); } public static void writeBufTo(ByteArrayOutputStream buf, OutputStream output) From 6788e5ea75fc6e56618facf5922d4369da7009bc Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 21 Jan 2025 12:26:26 +1030 Subject: [PATCH 028/890] Pass test vector of Romulus T --- .../crypto/engines/RomulusEngine.java | 719 ++++-------------- .../bouncycastle/crypto/test/RomulusTest.java | 3 +- 2 files changed, 168 insertions(+), 554 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index 87680bac20..96c88bec2d 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -1,7 +1,5 @@ package org.bouncycastle.crypto.engines; -import java.io.ByteArrayOutputStream; - import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.util.Arrays; @@ -23,17 +21,13 @@ public enum RomulusParameters RomulusT, } - private boolean initialised; - private final RomulusParameters romulusParameters; - private int offset; private byte[] k; private byte[] npub; - private final ByteArrayOutputStream aadData = new ByteArrayOutputStream(); - private final ByteArrayOutputStream message = new ByteArrayOutputStream(); private final int AD_BLK_LEN_HALF = 16; private int messegeLen; private int aadLen; private Instance instance; + private final byte[] CNT; // Packing of data is done as follows (state[i][j] stands for row i and column j): // 0 1 2 3 @@ -88,12 +82,9 @@ public enum RomulusParameters public RomulusEngine(RomulusParameters romulusParameters) { - super(romulusParameters == RomulusParameters.RomulusN ? ProcessingBufferType.Buffered : ProcessingBufferType.Immediate); + super(romulusParameters == RomulusParameters.RomulusT ? ProcessingBufferType.Immediate : ProcessingBufferType.Buffered); KEY_SIZE = IV_SIZE = MAC_SIZE = BlockSize = AADBufferSize = 16; - m_bufferSizeDecrypt = MAC_SIZE + BlockSize; - m_buf = new byte[m_bufferSizeDecrypt]; - m_aad = new byte[AADBufferSize]; - this.romulusParameters = romulusParameters; + CNT = new byte[7]; switch (romulusParameters) { case RomulusM: @@ -106,11 +97,13 @@ public RomulusEngine(RomulusParameters romulusParameters) break; case RomulusT: algorithmName = "Romulus-T"; + AADBufferSize = 32; instance = new RomulusT(); break; } - initialised = false; - + m_bufferSizeDecrypt = MAC_SIZE + BlockSize; + m_buf = new byte[m_bufferSizeDecrypt]; + m_aad = new byte[AADBufferSize]; } private interface Instance @@ -197,7 +190,6 @@ else if (messegeLen == 0) @Override public void processBufferAAD(byte[] input, int inOff) { - byte[] T = new byte[16]; byte[] mp = new byte[16]; // Rho(S,A) pads an A block and XORs it to the internal state. pad(input, inOff, mp, 16, 16); @@ -233,6 +225,7 @@ else if (m_aadPos != 0) lfsr_gf56(CNT); } } + m_aadPos = 0; } @Override @@ -313,13 +306,11 @@ private class RomulusN implements Instance { private final byte[] s; - private final byte[] CNT; boolean twist; public RomulusN() { s = new byte[AD_BLK_LEN_HALF]; - CNT = new byte[7]; twist = true; } @@ -439,41 +430,194 @@ public void reset() private class RomulusT implements Instance { + private final byte[] h = new byte[16]; + private final byte[] g = new byte[16]; + byte[] Z = new byte[16]; + byte[] CNT_Z = new byte[7]; + byte[] LR = new byte[32]; + byte[] T = new byte[16]; + // Initialization function: KDF + byte[] S = new byte[16]; @Override public void processFinalBlock(byte[] output, int outOff) { + int n = 16; + messegeLen -= (forEncryption ? 0 : MAC_SIZE); + if (m_bufPos != 0) + { + int len8 = Math.min(m_bufPos, 16); + System.arraycopy(npub, 0, S, 0, 16); + block_cipher(S, Z, T, 0, CNT, (byte)64); + for (int i = 0; i < len8; i++) + { + output[i + outOff] = (byte)((m_buf[i]) ^ S[i]); + } + System.arraycopy(npub, 0, S, 0, 16); + + lfsr_gf56(CNT); + + byte[] macIn; + int macInOff; + if (forEncryption) + { + macIn = output; + macInOff = outOff; + } + else + { + macIn = m_buf; + macInOff = 0; + } + System.arraycopy(macIn, macInOff, m_aad, m_aadPos, m_bufPos); + Arrays.fill(m_aad, m_aadPos + m_bufPos, AADBufferSize - 1, (byte)0); + m_aad[m_aadPos + BlockSize - 1] = (byte)(m_bufPos & 0xf); + if (m_aadPos == 0) + { + System.arraycopy(npub, 0, m_aad, BlockSize, BlockSize); + n = 0; + } + hirose_128_128_256(h, g, m_aad, 0); + lfsr_gf56(CNT_Z); + } + else if (m_aadPos != 0) + { + if (messegeLen > 0) + { + Arrays.fill(m_aad, BlockSize, AADBufferSize, (byte)0); + } + else if (aadLen != 0) + { + System.arraycopy(npub, 0, m_aad, m_aadPos, 16); + n = 0; + m_aadPos = 0; + } + hirose_128_128_256(h, g, m_aad, 0); + } + else if (messegeLen > 0) + { + Arrays.fill(m_aad, 0, BlockSize, (byte)0); + System.arraycopy(npub, 0, m_aad, BlockSize, BlockSize); + n = 0; + hirose_128_128_256(h, g, m_aad, 0); + } + if (n == 16) + { + // Pad the nonce and counter + System.arraycopy(npub, 0, m_aad, 0, 16); + System.arraycopy(CNT, 0, m_aad, 16, 7); + ipad_256(m_aad, m_aad, 23); + } + else + { + ipad_256(CNT_Z, m_aad, 7); + } + h[0] ^= 2; + hirose_128_128_256(h, g, m_aad, 0); + // Assign the output tag + System.arraycopy(h, 0, LR, 0, 16); + System.arraycopy(g, 0, LR, 16, 16); + Arrays.clear(CNT_Z); + block_cipher(LR, k, LR, 16, CNT_Z, (byte)68); + mac = new byte[MAC_SIZE]; + System.arraycopy(LR, 0, mac, 0, MAC_SIZE); } @Override public void processBufferAAD(byte[] input, int inOff) { - + hirose_128_128_256(h, g, input, inOff); } @Override public void processFinalAAD() { - + // Partial block (or in case there is no partial block we add a 0^2n block + Arrays.fill(m_aad, m_aadPos, AADBufferSize - 1, (byte)0); + if (m_aadPos >= 16) + { + m_aad[AADBufferSize - 1] = (byte)(m_aadPos & 0xf); + hirose_128_128_256(h, g, m_aad, 0); + m_aadPos = 0; + } + else if ((m_aadPos >= 0) && (aadLen != 0)) + { + m_aad[BlockSize - 1] = (byte)(m_aadPos & 0xf); + m_aadPos = BlockSize; + } } @Override public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { - + System.arraycopy(npub, 0, S, 0, 16); + block_cipher(S, Z, T, 0, CNT, (byte)64); + for (int i = 0; i < AD_BLK_LEN_HALF; i++) + { + output[i + outOff] = (byte)((input[i + inOff]) ^ S[i]); + } + System.arraycopy(npub, 0, S, 0, 16); + block_cipher(S, Z, T, 0, CNT, (byte)65); + System.arraycopy(S, 0, Z, 0, 16); + lfsr_gf56(CNT); + // ipad_256(ipad*_128(A)||ipad*_128(C)||N|| CNT + System.arraycopy(output, outOff, m_aad, m_aadPos, BlockSize); + if (m_aadPos == BlockSize) + { + hirose_128_128_256(h, g, m_aad, 0); + m_aadPos = 0; + lfsr_gf56(CNT_Z); + } + else + { + m_aadPos = BlockSize; + lfsr_gf56(CNT_Z); + } } @Override public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - + System.arraycopy(npub, 0, S, 0, 16); + block_cipher(S, Z, T, 0, CNT, (byte)64); + for (int i = 0; i < AD_BLK_LEN_HALF; i++) + { + output[i + outOff] = (byte)((input[i + inOff]) ^ S[i]); + } + System.arraycopy(npub, 0, S, 0, 16); + block_cipher(S, Z, T, 0, CNT, (byte)65); + System.arraycopy(S, 0, Z, 0, 16); + lfsr_gf56(CNT); + // ipad_256(ipad*_128(A)||ipad*_128(C)||N|| CNT + System.arraycopy(input, inOff, m_aad, m_aadPos, BlockSize); + if (m_aadPos == BlockSize) + { + hirose_128_128_256(h, g, m_aad, 0); + m_aadPos = 0; + lfsr_gf56(CNT_Z); + } + else + { + m_aadPos = BlockSize; + lfsr_gf56(CNT_Z); + } } @Override public void reset() { - + Arrays.clear(Z); + Arrays.clear(h); + Arrays.clear(g); + Arrays.clear(LR); + Arrays.clear(CNT_Z); + Arrays.clear(T); + Arrays.clear(S); + reset_lfsr_gf56(CNT); + System.arraycopy(npub, 0, Z, 0, 16); + block_cipher(Z, k, T, 0, CNT_Z, (byte)66); + reset_lfsr_gf56(CNT_Z); } } @@ -685,35 +829,6 @@ void nonce_encryption(byte[] N, byte[] CNT, byte[] s, byte[] k, byte D) block_cipher(s, k, T, 0, CNT, D); } - // Absorbs the AD blocks. - int ad_encryption(byte[] A, int AOff, byte[] s, byte[] k, int adlen, byte[] CNT, byte D) - { - byte[] T = new byte[16]; - byte[] mp = new byte[16]; - int n = 16; - int i, len8; - len8 = Math.min(adlen, n); - adlen -= len8; - // Rho(S,A) pads an A block and XORs it to the internal state. - pad(A, AOff, mp, n, len8); - for (i = 0; i < n; i++) - { - s[i] = (byte)(s[i] ^ mp[i]); - } - offset = AOff += len8; - lfsr_gf56(CNT); - if (adlen != 0) - { - len8 = Math.min(adlen, n); - adlen -= len8; - pad(A, AOff, T, n, len8); - offset = AOff + len8; - block_cipher(s, k, T, 0, CNT, D); - lfsr_gf56(CNT); - } - return adlen; - } - private void reset_lfsr_gf56(byte[] CNT) { CNT[0] = 0x01; @@ -725,396 +840,6 @@ private void reset_lfsr_gf56(byte[] CNT) CNT[6] = 0x00; } - private void romulus_m_encrypt(byte[] c, byte[] m, int mlen, byte[] ad, int adlen, byte[] N, byte[] k) - { - byte[] s = new byte[16]; - byte[] CNT = new byte[7]; - mac = new byte[16]; - int xlen, cOff = 0, mOff = 0, adOff = 0, mauth = 0; - byte w; - xlen = mlen; - reset_lfsr_gf56(CNT); - // Calculating the domain separation bits for the last block MAC TBC call depending on the length of M and AD - w = 48; - if ((adlen & 31) == 0 && adlen != 0) - { - w ^= 8; - } - else if ((adlen & 31) < AD_BLK_LEN_HALF) - { - w ^= 2; - } - else if ((adlen & 31) != AD_BLK_LEN_HALF) - { - w ^= 10; - } - if ((xlen & 31) == 0 && xlen != 0) - { - w ^= 4; - } - else if ((xlen & 31) < AD_BLK_LEN_HALF) - { - w ^= 1; - } - else if ((xlen & 31) != AD_BLK_LEN_HALF) - { - w ^= 5; - } - if (forEncryption) - { - if (adlen == 0) - { // AD is an empty string - lfsr_gf56(CNT); - } - else - { - while (adlen > 0) - { - offset = adOff; - adlen = ad_encryption(ad, adOff, s, k, adlen, CNT, (byte)40); - adOff = offset; - } - } - mOff = 0; - if ((w & 8) == 0) - { - byte[] Temp = new byte[16]; - int len8 = Math.min(xlen, AD_BLK_LEN_HALF); - xlen -= len8; - pad(m, mOff, Temp, AD_BLK_LEN_HALF, len8); - block_cipher(s, k, Temp, 0, CNT, (byte)44); - lfsr_gf56(CNT); - mOff += len8; - } - else if (mlen == 0) - { - lfsr_gf56(CNT); - } - while (xlen > 0) - { - offset = mOff; - xlen = ad_encryption(m, mOff, s, k, xlen, CNT, (byte)44); - mOff = offset; - } - nonce_encryption(N, CNT, s, k, w); - // Tag generation - g8A(s, mac, 0); - mOff -= mlen; - } - else - { - System.arraycopy(m, mlen, mac, 0, MAC_SIZE); - } - reset_lfsr_gf56(CNT); - System.arraycopy(mac, 0, s, 0, AD_BLK_LEN_HALF); - if (mlen > 0) - { - nonce_encryption(N, CNT, s, k, (byte)36); - while (mlen > AD_BLK_LEN_HALF) - { - mlen = mlen - AD_BLK_LEN_HALF; - rho(m, mOff, c, cOff, s, AD_BLK_LEN_HALF); - cOff += AD_BLK_LEN_HALF; - mOff += AD_BLK_LEN_HALF; - lfsr_gf56(CNT); - nonce_encryption(N, CNT, s, k, (byte)36); - } - rho(m, mOff, c, cOff, s, mlen); - } - if (!forEncryption) - { - Arrays.fill(s, (byte)0); - reset_lfsr_gf56(CNT); - if (adlen == 0) - { // AD is an empty string - lfsr_gf56(CNT); - } - else - { - while (adlen > 0) - { - offset = adOff; - adlen = ad_encryption(ad, adOff, s, k, adlen, CNT, (byte)40); - adOff = offset; - } - } - if ((w & 8) == 0) - { - byte[] Temp = new byte[16]; - int len8 = Math.min(xlen, AD_BLK_LEN_HALF); - xlen -= len8; - pad(c, mauth, Temp, AD_BLK_LEN_HALF, len8); - block_cipher(s, k, Temp, 0, CNT, (byte)44); - lfsr_gf56(CNT); - mauth += len8; - mOff += len8; - } - else if (mlen == 0) - { - lfsr_gf56(CNT); - } - while (xlen > 0) - { - offset = mauth; - xlen = ad_encryption(c, mauth, s, k, xlen, CNT, (byte)44); - mauth = offset; - } - nonce_encryption(N, CNT, s, k, w); - // Tag generation - g8A(s, mac, 0); - } - } - - private void romulus_n(byte[] c, byte[] I, int mlen, byte[] A, int adlen, byte[] N, byte[] k) - { - byte[] s = new byte[16]; - byte[] CNT = new byte[7]; - int mOff = 0, cOff = 0; - reset_lfsr_gf56(CNT); - if (adlen == 0) - { // AD is an empty string - lfsr_gf56(CNT); - nonce_encryption(N, CNT, s, k, (byte)0x1a); - } - else - { - while (adlen > 0) - { - if (adlen < AD_BLK_LEN_HALF) - { // The last block of AD is odd and incomplete - adlen = ad_encryption(A, 0, s, k, adlen, CNT, (byte)0x08); - nonce_encryption(N, CNT, s, k, (byte)0x1a); - } - else if (adlen == AD_BLK_LEN_HALF) - { // The last block of AD is odd and complete - adlen = ad_encryption(A, 0, s, k, adlen, CNT, (byte)0x08); - nonce_encryption(N, CNT, s, k, (byte)0x18); - } - else if (adlen < (AD_BLK_LEN_HALF + AD_BLK_LEN_HALF)) - { // The last block of AD is even and incomplete - adlen = ad_encryption(A, 0, s, k, adlen, CNT, (byte)0x08); - nonce_encryption(N, CNT, s, k, (byte)0x1a); - } - else if (adlen == (AD_BLK_LEN_HALF + AD_BLK_LEN_HALF)) - { // The last block of AD is even and complete - adlen = ad_encryption(A, 0, s, k, adlen, CNT, (byte)0x08); - nonce_encryption(N, CNT, s, k, (byte)0x18); - } - else - { // A normal full pair of blocks of AD - adlen = ad_encryption(A, 0, s, k, adlen, CNT, (byte)0x08); - } - } - } - reset_lfsr_gf56(CNT); - if (mlen == 0) - { // M is an empty string - lfsr_gf56(CNT); - nonce_encryption(N, CNT, s, k, (byte)0x15); - } - else - { - int tmp; - while (mlen > 0) - { - if (mlen < AD_BLK_LEN_HALF) - { // The last block of M is incomplete - tmp = mlen; - mlen = msg_encryption(I, mOff, c, cOff, N, CNT, s, k, (byte)0x15, mlen); - } - else if (mlen == AD_BLK_LEN_HALF) - { // The last block of M is complete - tmp = AD_BLK_LEN_HALF; - mlen = msg_encryption(I, mOff, c, cOff, N, CNT, s, k, (byte)0x14, mlen); - } - else - { // A normal full message block - tmp = AD_BLK_LEN_HALF; - mlen = msg_encryption(I, mOff, c, cOff, N, CNT, s, k, (byte)0x04, mlen); - } - mOff += tmp; - cOff += tmp; - } - } - // Tag generation - mac = new byte[16]; - g8A(s, mac, 0); - } - - // Absorbs and encrypts the message blocks. - int msg_encryption(byte[] m, int mOff, byte[] c, int cOff, byte[] N, byte[] CNT, byte[] s, byte[] k, byte D, int mlen) - { - int len8 = Math.min(mlen, AD_BLK_LEN_HALF); - mlen -= len8; - rho(m, mOff, c, cOff, s, len8); - lfsr_gf56(CNT); - nonce_encryption(N, CNT, s, k, D); - return mlen; - } - - private void romulus_t_encrypt(byte[] c, byte[] m, int mlen, byte[] A, int adlen, byte[] N, byte[] k) - throws InvalidCipherTextException - { - byte[] Z = new byte[16]; - byte[] CNT = new byte[7]; - byte[] CNT_Z = new byte[7]; - int mlen_int; - byte[] LR = new byte[32]; - int i; - int mOff = 0, cOff = 0; - reset_lfsr_gf56(CNT); - // Initialization function: KDF - byte[] S = new byte[16]; - mlen_int = mlen; - if (!forEncryption) - { - // T = hash(ipad*(A)||ipad*(C)||N||CNT) - mac = new byte[16]; - crypto_hash_vector(LR, A, adlen, m, mOff, mlen_int, N, CNT); - // Generates the tag T from the final state S by applying the Tag Generation Function (TGF). - block_cipher(LR, k, LR, 16, CNT_Z, (byte)68); - System.arraycopy(LR, 0, mac, 0, 16); - reset_lfsr_gf56(CNT); - tagVerification(m, mlen_int); - } - mac = new byte[16]; - System.arraycopy(N, 0, Z, 0, 16); - block_cipher(Z, k, mac, 0, CNT_Z, (byte)66); - while (mlen != 0) - { - int len8 = Math.min(mlen, 16); - mlen -= len8; - System.arraycopy(N, 0, S, 0, 16); - block_cipher(S, Z, mac, 0, CNT, (byte)64); - for (i = 0; i < len8 && i + cOff < c.length; i++) - { - c[i + cOff] = (byte)((m[i + mOff]) ^ S[i]); - } - cOff += len8; - mOff += len8; - System.arraycopy(N, 0, S, 0, 16); - if (mlen != 0) - { - block_cipher(S, Z, mac, 0, CNT, (byte)65); - System.arraycopy(S, 0, Z, 0, 16); - } - lfsr_gf56(CNT); - } - if (forEncryption) - { - // T = hash(A||N||M) - // We need to first pad A, N and C - cOff -= mlen_int; - crypto_hash_vector(LR, A, adlen, c, cOff, mlen_int, N, CNT); - // Generates the tag T from the final state S by applying the Tag Generation Function (TGF). - block_cipher(LR, k, LR, 16, CNT_Z, (byte)68); - System.arraycopy(LR, 0, mac, 0, 16); - } - } - - // This function is required for Romulus-T. It assumes that the input comes in three parts that can -// be stored in different locations in the memory. It processes these inputs sequentially. -// The padding is ipad_256(ipad*_128(A)||ipad*_128(C)||N|| CNT ) -// A and C are of variable length, while N is of 16 bytes and CNT is of 7 bytes - private void crypto_hash_vector(byte[] out, byte[] A, int adlen, byte[] C, int cOff, int clen, byte[] N, byte[] CNT) - { - byte[] h = new byte[16]; - byte[] g = new byte[16]; - byte[] p = new byte[32]; - int aOff = 0; - int n = 16; - byte adempty = (byte)((adlen == 0) ? 1 : 0); - byte cempty = (byte)(clen == 0 ? 1 : 0); - reset_lfsr_gf56(CNT); - while (adlen >= 32) - { // AD Normal loop - hirose_128_128_256(h, g, A, aOff); - aOff += 32; - adlen -= 32; - } - // Partial block (or in case there is no partial block we add a 0^2n block - if (adlen >= 16) - { - ipad_128(A, aOff, p, 0, 32, adlen); - hirose_128_128_256(h, g, p, 0); - } - else if ((adlen >= 0) && (adempty == 0)) - { - ipad_128(A, aOff, p, 0, 16, adlen); - if (clen >= 16) - { - System.arraycopy(C, cOff, p, 16, 16); - hirose_128_128_256(h, g, p, 0); - lfsr_gf56(CNT); - clen -= 16; - cOff += 16; - } - else if (clen > 0) - { - ipad_128(C, cOff, p, 16, 16, clen); - hirose_128_128_256(h, g, p, 0); - clen = 0; - cempty = 1; - cOff += 16; - lfsr_gf56(CNT); - } - else - { - System.arraycopy(N, 0, p, 16, 16); - hirose_128_128_256(h, g, p, 0); - n = 0; - } - } - while (clen >= 32) - { // C Normal loop - hirose_128_128_256(h, g, C, cOff); - cOff += 32; - clen -= 32; - lfsr_gf56(CNT); - lfsr_gf56(CNT); - } - if (clen > 16) - { - ipad_128(C, cOff, p, 0, 32, clen); - hirose_128_128_256(h, g, p, 0); - lfsr_gf56(CNT); - lfsr_gf56(CNT); - } - else if (clen == 16) - { - ipad_128(C, cOff, p, 0, 32, clen); - hirose_128_128_256(h, g, p, 0); - lfsr_gf56(CNT); - } - else if ((clen >= 0) && (cempty == 0)) - { - ipad_128(C, cOff, p, 0, 16, clen); - if (clen > 0) - { - lfsr_gf56(CNT); - } - // Pad the nonce - System.arraycopy(N, 0, p, 16, 16); - hirose_128_128_256(h, g, p, 0); - n = 0; - } - if (n == 16) - { - // Pad the nonce and counter - System.arraycopy(N, 0, p, 0, 16); - System.arraycopy(CNT, 0, p, 16, 7); - ipad_256(p, p, 23); - } - else - { - ipad_256(CNT, p, 7); - } - h[0] ^= 2; - hirose_128_128_256(h, g, p, 0); - // Assign the output tag - System.arraycopy(h, 0, out, 0, 16); - System.arraycopy(g, 0, out, 16, 16); - } // Padding function: pads the byte length of the message mod 32 to the last incomplete block. // For complete blocks it returns the same block. For an empty block it returns a 0^2n string. @@ -1127,17 +852,6 @@ void ipad_256(byte[] m, byte[] mp, int len8) mp[31] = (byte)(len8 & 0x1f); } - // Padding function: pads the byte length of the message mod 32 to the last incomplete block. -// For complete blocks it returns the same block. For an empty block it returns a 0^2n string. -// The function is called for full block messages to add a 0^2n block. This and the modulus are -// the only differences compared to the use in Romulus-N - void ipad_128(byte[] m, int mOff, byte[] mp, int mpOff, int l, int len8) - { - System.arraycopy(m, mOff, mp, mpOff, len8); - Arrays.fill(mp, len8 + mpOff, l - 1 + mpOff, (byte)0); - mp[mpOff + l - 1] = (byte)(len8 & 0xf); - } - // The hirose double-block length (DBL) compression function. void hirose_128_128_256(byte[] h, byte[] g, byte[] m, int mOff) { @@ -1166,7 +880,6 @@ public void init(byte[] key, byte[] iv) { npub = iv; k = key; - initialised = true; m_state = forEncryption ? State.EncInit : State.DecInit; reset(false); } @@ -1176,29 +889,14 @@ public void processAADByte(byte in) { aadLen++; super.processAADByte(in); -// aadData.write(in); } - // @Override public void processAADBytes(byte[] in, int inOff, int len) { -// if (inOff + len > in.length) -// { -// throw new DataLengthException(algorithmName + " input buffer too short"); -// } aadLen += len; super.processAADBytes(in, inOff, len); - //aadData.write(in, inOff, len); } -// -// @Override -// public int processByte(byte in, byte[] out, int outOff) -// throws DataLengthException -// { -// message.write(in); -// return 0; -// } @Override public int processBytes(byte[] input, int inOff, int len, byte[] out, int outOff) @@ -1206,77 +904,8 @@ public int processBytes(byte[] input, int inOff, int len, byte[] out, int outOff { messegeLen += len; return super.processBytes(input, inOff, len, out, outOff); -// if (inOff + len > input.length) -// { -// throw new DataLengthException(algorithmName + " input buffer too short"); -// } -// message.write(input, inOff, len); -// return 0; } -// @Override -// public int doFinal(byte[] out, int outOff) -// throws IllegalStateException, InvalidCipherTextException -// { -// if (!initialised) -// { -// throw new IllegalArgumentException(algorithmName + " needs call init function before dofinal"); -// } -// int len = message.size(), inOff = 0; -// if ((forEncryption && len + KEY_SIZE + outOff > out.length) || -// (!forEncryption && len - KEY_SIZE + outOff > out.length)) -// { -// throw new OutputLengthException("output buffer is too short"); -// } -// byte[] ad = aadData.toByteArray(); -// int adlen = ad.length; -// byte[] input = message.toByteArray(); -// -// if ((ad.length & 7) != 0) -// { -// byte[] tmp = new byte[((ad.length >> 3) << 3) + 16]; -// System.arraycopy(ad, 0, tmp, 0, adlen); -// ad = tmp; -// } -// if ((len & 7) != 0) -// { -// byte[] tmp = new byte[((len >> 3) << 3) + 16]; -// System.arraycopy(input, inOff, tmp, 0, len); -// input = tmp; -// } -// byte[] out_tmp = new byte[((len >> 3) << 3) + 16]; -// len -= (forEncryption ? 0 : 16); -// switch (romulusParameters) -// { -// case RomulusM: -// romulus_m_encrypt(out_tmp, input, len, ad, adlen, npub, k); -// break; -// case RomulusN: -// romulus_n(out_tmp, input, len, ad, adlen, npub, k); -// break; -// case RomulusT: -// romulus_t_encrypt(out_tmp, input, len, ad, adlen, npub, k); -// break; -// } -// System.arraycopy(out_tmp, 0, out, outOff, len); -// outOff += len; -// if (forEncryption) -// { -// System.arraycopy(mac, 0, out, outOff, KEY_SIZE); -// len += KEY_SIZE; -// } -// else -// { -// if (romulusParameters != RomulusParameters.RomulusT) -// { -// tagVerification(input, len); -// } -// } -// reset(false); -// return len; -// } - - protected void finishAAD(State nextState, boolean isDoFinal) { // State indicates whether we ever received AAD @@ -1293,8 +922,6 @@ protected void finishAAD(State nextState, boolean isDoFinal) default: break; } - - m_aadPos = 0; m_state = nextState; } @@ -1328,26 +955,12 @@ protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int instance.processBufferDecrypt(input, inOff, output, outOff); } - private void tagVerification(byte[] input, int inOff) - throws InvalidCipherTextException - { - for (int i = 0; i < 16; ++i) - { - if (mac[i] != input[inOff + i]) - { - throw new InvalidCipherTextException("mac check in " + algorithmName + " failed"); - } - } - } - protected void reset(boolean clearMac) { aadLen = 0; messegeLen = 0; instance.reset(); bufferReset(); - aadData.reset(); - message.reset(); super.reset(clearMac); } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java index 294195d05e..d305095035 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java @@ -31,9 +31,10 @@ public String getName() public void performTest() throws Exception { + CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT), "crypto/romulus", "t_LWC_AEAD_KAT_128_128.txt", this); CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN), "crypto/romulus", "n_LWC_AEAD_KAT_128_128.txt", this); //CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM), "crypto/romulus", "m_LWC_AEAD_KAT_128_128.txt", this); - //CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT), "crypto/romulus", "t_LWC_AEAD_KAT_128_128.txt", this); + // // RomulusEngine romulus = new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT); // testExceptions(romulus, romulus.getKeyBytesSize(), romulus.getIVBytesSize(), romulus.getBlockSize()); From 701a72406b24a9620efaf736739b0cc194412f86 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 21 Jan 2025 13:08:46 +1030 Subject: [PATCH 029/890] TODO: Romulus M --- .../crypto/engines/RomulusEngine.java | 112 +++++++++--------- .../bouncycastle/crypto/test/RomulusTest.java | 3 +- 2 files changed, 55 insertions(+), 60 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index 96c88bec2d..c34d4bddf3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -1,10 +1,8 @@ package org.bouncycastle.crypto.engines; import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.util.Arrays; - /** * Romulus v1.3, based on the current round 3 submission, https://romulusae.github.io/romulus/ * Reference C implementation: https://github.com/romulusae/romulus @@ -28,6 +26,7 @@ public enum RomulusParameters private int aadLen; private Instance instance; private final byte[] CNT; + private final byte[] s; // Packing of data is done as follows (state[i][j] stands for row i and column j): // 0 1 2 3 @@ -85,6 +84,7 @@ public RomulusEngine(RomulusParameters romulusParameters) super(romulusParameters == RomulusParameters.RomulusT ? ProcessingBufferType.Immediate : ProcessingBufferType.Buffered); KEY_SIZE = IV_SIZE = MAC_SIZE = BlockSize = AADBufferSize = 16; CNT = new byte[7]; + s = new byte[AD_BLK_LEN_HALF]; switch (romulusParameters) { case RomulusM: @@ -126,22 +126,19 @@ private class RomulusM { byte[] mac_s = new byte[16]; byte[] mac_CNT = new byte[7]; - - byte[] s = new byte[16]; - byte[] CNT = new byte[7]; boolean isfirstblock; - public RomulusM() { - mac = new byte[16]; - reset_lfsr_gf56(mac_CNT); + + isfirstblock = true; } @Override public void processFinalBlock(byte[] output, int outOff) { + messegeLen -= (forEncryption ? 0 : MAC_SIZE); byte w = 48; if ((aadLen & 31) == 0 && aadLen != 0) { @@ -167,23 +164,33 @@ else if ((messegeLen & 31) != AD_BLK_LEN_HALF) { w ^= 5; } - if (forEncryption) + if ((w & 8) == 0 && isfirstblock) { - if ((w & 8) == 0 && isfirstblock) + byte[] Temp = new byte[16]; + int len8 = Math.min(messegeLen, AD_BLK_LEN_HALF); + byte[] macIn; + int macInOff; + if (forEncryption) { - byte[] Temp = new byte[16]; - int len8 = Math.min(messegeLen, AD_BLK_LEN_HALF); - pad(m_buf, 0, Temp, AD_BLK_LEN_HALF, len8); - block_cipher(mac_s, k, Temp, 0, mac_CNT, (byte)44); - lfsr_gf56(mac_CNT); + macIn = output; + macInOff = outOff; } - else if (messegeLen == 0) + else { - lfsr_gf56(mac_CNT); + macIn = m_buf; + macInOff = 0; } + pad(macIn, macInOff, Temp, AD_BLK_LEN_HALF, len8); + block_cipher(mac_s, k, Temp, 0, mac_CNT, (byte)44); + lfsr_gf56(mac_CNT); + } + else if (messegeLen == 0) + { + lfsr_gf56(mac_CNT); } nonce_encryption(npub, mac_CNT, mac_s, k, w); // Tag generation + mac = new byte[16]; g8A(mac_s, mac, 0); } @@ -213,17 +220,18 @@ public void processFinalAAD() } else if (m_aadPos != 0) { - byte[] T = new byte[16]; - pad(m_aad, 0, T, 16, m_aadPos); - block_cipher(mac_s, k, T, 0, mac_CNT, (byte)40); - lfsr_gf56(mac_CNT); - if (m_aadPos > 16) + //TODO + //Arrays.fill(m_aad, ); + if (m_aadPos < AD_BLK_LEN_HALF) { - int len8 = Math.min(m_aadPos - 16, 16); - pad(m_aad, 16, T, 16, len8); - block_cipher(s, k, T, 0, CNT, (byte)40); - lfsr_gf56(CNT); + m_aad[AD_BLK_LEN_HALF - 1] = (byte)(m_aadPos & 0x0f); } +// for (int i = 0; i < 16; i++) +// { +// mac_s[i] = (byte)(mac_s[i] ^ input[inOff + i]); +// } + block_cipher(mac_s, k, m_aad, 0, mac_CNT, (byte)40); + lfsr_gf56(mac_CNT); } m_aadPos = 0; } @@ -295,25 +303,17 @@ public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int out @Override public void reset() { - Arrays.clear(s); - Arrays.clear(CNT); + reset_lfsr_gf56(CNT); + reset_lfsr_gf56(mac_CNT); Arrays.clear(mac_s); - Arrays.clear(mac_CNT); } } private class RomulusN implements Instance { - private final byte[] s; boolean twist; - public RomulusN() - { - s = new byte[AD_BLK_LEN_HALF]; - twist = true; - } - @Override public void processFinalBlock(byte[] output, int outOff) { @@ -420,8 +420,6 @@ public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int out @Override public void reset() { - Arrays.clear(CNT); - Arrays.clear(s); reset_lfsr_gf56(CNT); twist = true; } @@ -436,8 +434,6 @@ private class RomulusT byte[] CNT_Z = new byte[7]; byte[] LR = new byte[32]; byte[] T = new byte[16]; - // Initialization function: KDF - byte[] S = new byte[16]; @Override public void processFinalBlock(byte[] output, int outOff) @@ -447,13 +443,13 @@ public void processFinalBlock(byte[] output, int outOff) if (m_bufPos != 0) { int len8 = Math.min(m_bufPos, 16); - System.arraycopy(npub, 0, S, 0, 16); - block_cipher(S, Z, T, 0, CNT, (byte)64); + System.arraycopy(npub, 0, s, 0, 16); + block_cipher(s, Z, T, 0, CNT, (byte)64); for (int i = 0; i < len8; i++) { - output[i + outOff] = (byte)((m_buf[i]) ^ S[i]); + output[i + outOff] = (byte)((m_buf[i]) ^ s[i]); } - System.arraycopy(npub, 0, S, 0, 16); + System.arraycopy(npub, 0, s, 0, 16); lfsr_gf56(CNT); @@ -551,15 +547,15 @@ else if ((m_aadPos >= 0) && (aadLen != 0)) @Override public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { - System.arraycopy(npub, 0, S, 0, 16); - block_cipher(S, Z, T, 0, CNT, (byte)64); + System.arraycopy(npub, 0, s, 0, 16); + block_cipher(s, Z, T, 0, CNT, (byte)64); for (int i = 0; i < AD_BLK_LEN_HALF; i++) { - output[i + outOff] = (byte)((input[i + inOff]) ^ S[i]); + output[i + outOff] = (byte)((input[i + inOff]) ^ s[i]); } - System.arraycopy(npub, 0, S, 0, 16); - block_cipher(S, Z, T, 0, CNT, (byte)65); - System.arraycopy(S, 0, Z, 0, 16); + System.arraycopy(npub, 0, s, 0, 16); + block_cipher(s, Z, T, 0, CNT, (byte)65); + System.arraycopy(s, 0, Z, 0, 16); lfsr_gf56(CNT); // ipad_256(ipad*_128(A)||ipad*_128(C)||N|| CNT System.arraycopy(output, outOff, m_aad, m_aadPos, BlockSize); @@ -579,15 +575,15 @@ public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int out @Override public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - System.arraycopy(npub, 0, S, 0, 16); - block_cipher(S, Z, T, 0, CNT, (byte)64); + System.arraycopy(npub, 0, s, 0, 16); + block_cipher(s, Z, T, 0, CNT, (byte)64); for (int i = 0; i < AD_BLK_LEN_HALF; i++) { - output[i + outOff] = (byte)((input[i + inOff]) ^ S[i]); + output[i + outOff] = (byte)((input[i + inOff]) ^ s[i]); } - System.arraycopy(npub, 0, S, 0, 16); - block_cipher(S, Z, T, 0, CNT, (byte)65); - System.arraycopy(S, 0, Z, 0, 16); + System.arraycopy(npub, 0, s, 0, 16); + block_cipher(s, Z, T, 0, CNT, (byte)65); + System.arraycopy(s, 0, Z, 0, 16); lfsr_gf56(CNT); // ipad_256(ipad*_128(A)||ipad*_128(C)||N|| CNT System.arraycopy(input, inOff, m_aad, m_aadPos, BlockSize); @@ -613,8 +609,6 @@ public void reset() Arrays.clear(LR); Arrays.clear(CNT_Z); Arrays.clear(T); - Arrays.clear(S); - reset_lfsr_gf56(CNT); System.arraycopy(npub, 0, Z, 0, 16); block_cipher(Z, k, T, 0, CNT_Z, (byte)66); reset_lfsr_gf56(CNT_Z); @@ -959,6 +953,8 @@ protected void reset(boolean clearMac) { aadLen = 0; messegeLen = 0; + Arrays.clear(s); + Arrays.clear(CNT); instance.reset(); bufferReset(); super.reset(clearMac); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java index d305095035..cb18f84fe0 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java @@ -31,10 +31,9 @@ public String getName() public void performTest() throws Exception { + CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM), "crypto/romulus", "m_LWC_AEAD_KAT_128_128.txt", this); CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT), "crypto/romulus", "t_LWC_AEAD_KAT_128_128.txt", this); CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN), "crypto/romulus", "n_LWC_AEAD_KAT_128_128.txt", this); - //CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM), "crypto/romulus", "m_LWC_AEAD_KAT_128_128.txt", this); - // // RomulusEngine romulus = new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT); // testExceptions(romulus, romulus.getKeyBytesSize(), romulus.getIVBytesSize(), romulus.getBlockSize()); From 6e801d196b04d6d9f273a4a653eef792f7d550a5 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 21 Jan 2025 13:15:53 +1030 Subject: [PATCH 030/890] Fix the bug in ShamirSecretSplitterTest.testShamirSecretMultipleDivide that mul may be 0. --- .../threshold/test/ShamirSecretSplitterTest.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/crypto/threshold/test/ShamirSecretSplitterTest.java b/core/src/test/java/org/bouncycastle/crypto/threshold/test/ShamirSecretSplitterTest.java index cd81bda2a4..ae13341922 100644 --- a/core/src/test/java/org/bouncycastle/crypto/threshold/test/ShamirSecretSplitterTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/threshold/test/ShamirSecretSplitterTest.java @@ -19,7 +19,12 @@ public static void main(String[] args) throws IOException { ShamirSecretSplitterTest test = new ShamirSecretSplitterTest(); + for (int i = 0; i < 1000; ++i) + { + test.testShamirSecretMultipleDivide(); + } test.performTest(); + System.out.println("OK"); } public void performTest() @@ -77,7 +82,7 @@ public void testShamirSecretMultipleDivide() ShamirSplitSecret splitSecret1 = new ShamirSplitSecret(algorithm, mode, secretShares1); byte[] secret1 = splitSecret1.getSecret(); - int mul = random.nextInt(255); + int mul = random.nextInt(254) + 1; splitSecret.multiple(mul); secretShares = (ShamirSplitSecretShare[])splitSecret.getSecretShares(); ShamirSplitSecretShare[] secretShares4 = new ShamirSplitSecretShare[]{secretShares[1], secretShares[2], secretShares[5]}; @@ -89,7 +94,6 @@ public void testShamirSecretMultipleDivide() ShamirSplitSecretShare[] secretShares2 = new ShamirSplitSecretShare[]{secretShares[4], secretShares[7], secretShares[8]}; ShamirSplitSecret splitSecret2 = new ShamirSplitSecret(algorithm, mode, secretShares2); byte[] secret2 = splitSecret2.getSecret(); - assertTrue(Arrays.areEqual(secret1, secret2)); @@ -966,7 +970,7 @@ public ShamirSplitSecret newInstance(ShamirSplitSecretShare[] secretShares) @Override public ShamirSecretSplitter newInstance(int l, int m, int n, SecureRandom random) { - return new ShamirSecretSplitter(ShamirSecretSplitter.Algorithm.AES, ShamirSecretSplitter.Mode.Table, l, random); + return new ShamirSecretSplitter(ShamirSecretSplitter.Algorithm.AES, ShamirSecretSplitter.Mode.Table, l, random); } @Override @@ -996,7 +1000,7 @@ public ShamirSplitSecret newInstance(ShamirSplitSecretShare[] secretShares) @Override public ShamirSecretSplitter newInstance(int l, int m, int n, SecureRandom random) { - return new ShamirSecretSplitter(ShamirSecretSplitter.Algorithm.RSA, ShamirSecretSplitter.Mode.Table, l,random); + return new ShamirSecretSplitter(ShamirSecretSplitter.Algorithm.RSA, ShamirSecretSplitter.Mode.Table, l, random); } @Override From 131303e739585d512e3e60381a5e55c4a453f7ef Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 21 Jan 2025 15:01:45 +1030 Subject: [PATCH 031/890] Add tests for RomulusEngine, RomulusM will be done in the future. --- .../crypto/engines/RomulusEngine.java | 421 +++++++++--------- .../bouncycastle/crypto/test/RomulusTest.java | 84 +++- 2 files changed, 295 insertions(+), 210 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index c34d4bddf3..c6084c4cb5 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -3,6 +3,7 @@ import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.util.Arrays; + /** * Romulus v1.3, based on the current round 3 submission, https://romulusae.github.io/romulus/ * Reference C implementation: https://github.com/romulusae/romulus @@ -26,7 +27,6 @@ public enum RomulusParameters private int aadLen; private Instance instance; private final byte[] CNT; - private final byte[] s; // Packing of data is done as follows (state[i][j] stands for row i and column j): // 0 1 2 3 @@ -84,13 +84,12 @@ public RomulusEngine(RomulusParameters romulusParameters) super(romulusParameters == RomulusParameters.RomulusT ? ProcessingBufferType.Immediate : ProcessingBufferType.Buffered); KEY_SIZE = IV_SIZE = MAC_SIZE = BlockSize = AADBufferSize = 16; CNT = new byte[7]; - s = new byte[AD_BLK_LEN_HALF]; switch (romulusParameters) { - case RomulusM: - algorithmName = "Romulus-M"; - instance = new RomulusM(); - break; +// case RomulusM: +// algorithmName = "Romulus-M"; +// instance = new RomulusM(); +// break; case RomulusN: algorithmName = "Romulus-N"; instance = new RomulusN(); @@ -121,199 +120,199 @@ private interface Instance void reset(); } - private class RomulusM - implements Instance - { - byte[] mac_s = new byte[16]; - byte[] mac_CNT = new byte[7]; - boolean isfirstblock; - - public RomulusM() - { - - - isfirstblock = true; - } - - @Override - public void processFinalBlock(byte[] output, int outOff) - { - messegeLen -= (forEncryption ? 0 : MAC_SIZE); - byte w = 48; - if ((aadLen & 31) == 0 && aadLen != 0) - { - w ^= 8; - } - else if ((aadLen & 31) < AD_BLK_LEN_HALF) - { - w ^= 2; - } - else if ((aadLen & 31) != AD_BLK_LEN_HALF) - { - w ^= 10; - } - if ((messegeLen & 31) == 0 && messegeLen != 0) - { - w ^= 4; - } - else if ((messegeLen & 31) < AD_BLK_LEN_HALF) - { - w ^= 1; - } - else if ((messegeLen & 31) != AD_BLK_LEN_HALF) - { - w ^= 5; - } - if ((w & 8) == 0 && isfirstblock) - { - byte[] Temp = new byte[16]; - int len8 = Math.min(messegeLen, AD_BLK_LEN_HALF); - byte[] macIn; - int macInOff; - if (forEncryption) - { - macIn = output; - macInOff = outOff; - } - else - { - macIn = m_buf; - macInOff = 0; - } - pad(macIn, macInOff, Temp, AD_BLK_LEN_HALF, len8); - block_cipher(mac_s, k, Temp, 0, mac_CNT, (byte)44); - lfsr_gf56(mac_CNT); - } - else if (messegeLen == 0) - { - lfsr_gf56(mac_CNT); - } - nonce_encryption(npub, mac_CNT, mac_s, k, w); - // Tag generation - mac = new byte[16]; - g8A(mac_s, mac, 0); - } - - @Override - public void processBufferAAD(byte[] input, int inOff) - { - byte[] mp = new byte[16]; - // Rho(S,A) pads an A block and XORs it to the internal state. - pad(input, inOff, mp, 16, 16); - for (int i = 0; i < 16; i++) - { - mac_s[i] = (byte)(mac_s[i] ^ input[inOff + i]); - } - lfsr_gf56(mac_CNT); - inOff += 16; - block_cipher(mac_s, k, input, inOff, CNT, (byte)40); - lfsr_gf56(mac_CNT); - } - - @Override - public void processFinalAAD() - { - if (aadLen == 0) - { - // AD is an empty string - lfsr_gf56(mac_CNT); - } - else if (m_aadPos != 0) - { - //TODO - //Arrays.fill(m_aad, ); - if (m_aadPos < AD_BLK_LEN_HALF) - { - m_aad[AD_BLK_LEN_HALF - 1] = (byte)(m_aadPos & 0x0f); - } -// for (int i = 0; i < 16; i++) +// private class RomulusM +// implements Instance +// { +// byte[] mac_s = new byte[16]; +// byte[] mac_CNT = new byte[7]; +// +// byte[] s = new byte[16]; +// byte[] CNT = new byte[7]; +// boolean isfirstblock; +// +// +// public RomulusM() +// { +// mac = new byte[16]; +// reset_lfsr_gf56(mac_CNT); +// isfirstblock = true; +// } +// +// @Override +// public void processFinalBlock(byte[] output, int outOff) +// { +// byte w = 48; +// if ((aadLen & 31) == 0 && aadLen != 0) +// { +// w ^= 8; +// } +// else if ((aadLen & 31) < AD_BLK_LEN_HALF) +// { +// w ^= 2; +// } +// else if ((aadLen & 31) != AD_BLK_LEN_HALF) +// { +// w ^= 10; +// } +// if ((messegeLen & 31) == 0 && messegeLen != 0) +// { +// w ^= 4; +// } +// else if ((messegeLen & 31) < AD_BLK_LEN_HALF) +// { +// w ^= 1; +// } +// else if ((messegeLen & 31) != AD_BLK_LEN_HALF) +// { +// w ^= 5; +// } +// if (forEncryption) +// { +// if ((w & 8) == 0 && isfirstblock) +// { +// byte[] Temp = new byte[16]; +// int len8 = Math.min(messegeLen, AD_BLK_LEN_HALF); +// pad(m_buf, 0, Temp, AD_BLK_LEN_HALF, len8); +// block_cipher(mac_s, k, Temp, 0, mac_CNT, (byte)44); +// lfsr_gf56(mac_CNT); +// } +// else if (messegeLen == 0) +// { +// lfsr_gf56(mac_CNT); +// } +// } +// nonce_encryption(npub, mac_CNT, mac_s, k, w); +// // Tag generation +// g8A(mac_s, mac, 0); +// } +// +// @Override +// public void processBufferAAD(byte[] input, int inOff) +// { +// byte[] mp = new byte[16]; +// // Rho(S,A) pads an A block and XORs it to the internal state. +// pad(input, inOff, mp, 16, 16); +// for (int i = 0; i < 16; i++) +// { +// mac_s[i] = (byte)(mac_s[i] ^ input[inOff + i]); +// } +// lfsr_gf56(mac_CNT); +// inOff += 16; +// block_cipher(mac_s, k, input, inOff, CNT, (byte)40); +// lfsr_gf56(mac_CNT); +// } +// +// @Override +// public void processFinalAAD() +// { +// if (aadLen == 0) +// { +// // AD is an empty string +// lfsr_gf56(mac_CNT); +// } +// else if (m_aadPos != 0) +// { +// byte[] T = new byte[16]; +// pad(m_aad, 0, T, 16, m_aadPos); +// block_cipher(mac_s, k, T, 0, mac_CNT, (byte)40); +// lfsr_gf56(mac_CNT); +// if (m_aadPos > 16) +// { +// int len8 = Math.min(m_aadPos - 16, 16); +// pad(m_aad, 16, T, 16, len8); +// block_cipher(s, k, T, 0, CNT, (byte)40); +// lfsr_gf56(CNT); +// } +// } +// m_aadPos = 0; +// } +// +// @Override +// public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) +// { +// if ((aadLen == 0 || ((aadLen & 31) > 0 && (aadLen & 31) < 15))) +// { +// block_cipher(mac_s, k, input, inOff, mac_CNT, (byte)44); +// lfsr_gf56(mac_CNT); +// for (int i = 0; i < AD_BLK_LEN_HALF; i++) +// { +// mac_s[i] = (byte)(mac_s[i] ^ input[inOff + 16 + i]); +// } +// lfsr_gf56(mac_CNT); +// } +// else +// { +// for (int i = 0; i < AD_BLK_LEN_HALF; i++) // { // mac_s[i] = (byte)(mac_s[i] ^ input[inOff + i]); // } - block_cipher(mac_s, k, m_aad, 0, mac_CNT, (byte)40); - lfsr_gf56(mac_CNT); - } - m_aadPos = 0; - } - - @Override - public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) - { - if ((aadLen == 0 || ((aadLen & 31) > 0 && (aadLen & 31) < 15))) - { - block_cipher(mac_s, k, input, inOff, mac_CNT, (byte)44); - lfsr_gf56(mac_CNT); - for (int i = 0; i < AD_BLK_LEN_HALF; i++) - { - mac_s[i] = (byte)(mac_s[i] ^ input[inOff + 16 + i]); - } - lfsr_gf56(mac_CNT); - } - else - { - for (int i = 0; i < AD_BLK_LEN_HALF; i++) - { - mac_s[i] = (byte)(mac_s[i] ^ input[inOff + i]); - } - lfsr_gf56(mac_CNT); - block_cipher(mac_s, k, input, inOff + AD_BLK_LEN_HALF, mac_CNT, (byte)44); - lfsr_gf56(mac_CNT); - } - if (isfirstblock) - { - isfirstblock = false; - nonce_encryption(npub, CNT, s, k, (byte)36); - } - rho(input, inOff, output, outOff, s, AD_BLK_LEN_HALF); - lfsr_gf56(CNT); - } - - @Override - public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) - { - if (isfirstblock) - { - isfirstblock = false; - nonce_encryption(npub, CNT, s, k, (byte)36); - } - rho(input, inOff, output, outOff, s, AD_BLK_LEN_HALF); - lfsr_gf56(CNT); - if ((aadLen == 0 || ((aadLen & 31) > 0 && (aadLen & 31) < 15))) - { - block_cipher(mac_s, k, output, outOff, mac_CNT, (byte)44); - lfsr_gf56(mac_CNT); - for (int i = 0; i < 16; i++) - { - mac_s[i] = (byte)(mac_s[i] ^ output[outOff + AD_BLK_LEN_HALF + i]); - } - lfsr_gf56(mac_CNT); - } - else - { - for (int i = 0; i < 16; i++) - { - mac_s[i] = (byte)(mac_s[i] ^ output[outOff + i]); - } - lfsr_gf56(mac_CNT); - block_cipher(mac_s, k, output, outOff + AD_BLK_LEN_HALF, mac_CNT, (byte)44); - lfsr_gf56(mac_CNT); - } - } - - @Override - public void reset() - { - reset_lfsr_gf56(CNT); - reset_lfsr_gf56(mac_CNT); - Arrays.clear(mac_s); - } - } +// lfsr_gf56(mac_CNT); +// block_cipher(mac_s, k, input, inOff + AD_BLK_LEN_HALF, mac_CNT, (byte)44); +// lfsr_gf56(mac_CNT); +// } +// if (isfirstblock) +// { +// isfirstblock = false; +// nonce_encryption(npub, CNT, s, k, (byte)36); +// } +// rho(input, inOff, output, outOff, s, AD_BLK_LEN_HALF); +// lfsr_gf56(CNT); +// } +// +// @Override +// public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) +// { +// if (isfirstblock) +// { +// isfirstblock = false; +// nonce_encryption(npub, CNT, s, k, (byte)36); +// } +// rho(input, inOff, output, outOff, s, AD_BLK_LEN_HALF); +// lfsr_gf56(CNT); +// if ((aadLen == 0 || ((aadLen & 31) > 0 && (aadLen & 31) < 15))) +// { +// block_cipher(mac_s, k, output, outOff, mac_CNT, (byte)44); +// lfsr_gf56(mac_CNT); +// for (int i = 0; i < 16; i++) +// { +// mac_s[i] = (byte)(mac_s[i] ^ output[outOff + AD_BLK_LEN_HALF + i]); +// } +// lfsr_gf56(mac_CNT); +// } +// else +// { +// for (int i = 0; i < 16; i++) +// { +// mac_s[i] = (byte)(mac_s[i] ^ output[outOff + i]); +// } +// lfsr_gf56(mac_CNT); +// block_cipher(mac_s, k, output, outOff + AD_BLK_LEN_HALF, mac_CNT, (byte)44); +// lfsr_gf56(mac_CNT); +// } +// } +// +// @Override +// public void reset() +// { +// Arrays.clear(s); +// Arrays.clear(CNT); +// Arrays.clear(mac_s); +// Arrays.clear(mac_CNT); +// } +// } private class RomulusN implements Instance { + private final byte[] s; boolean twist; + public RomulusN() + { + s = new byte[AD_BLK_LEN_HALF]; + twist = true; + } + @Override public void processFinalBlock(byte[] output, int outOff) { @@ -420,6 +419,8 @@ public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int out @Override public void reset() { + Arrays.clear(CNT); + Arrays.clear(s); reset_lfsr_gf56(CNT); twist = true; } @@ -434,6 +435,8 @@ private class RomulusT byte[] CNT_Z = new byte[7]; byte[] LR = new byte[32]; byte[] T = new byte[16]; + // Initialization function: KDF + byte[] S = new byte[16]; @Override public void processFinalBlock(byte[] output, int outOff) @@ -443,13 +446,13 @@ public void processFinalBlock(byte[] output, int outOff) if (m_bufPos != 0) { int len8 = Math.min(m_bufPos, 16); - System.arraycopy(npub, 0, s, 0, 16); - block_cipher(s, Z, T, 0, CNT, (byte)64); + System.arraycopy(npub, 0, S, 0, 16); + block_cipher(S, Z, T, 0, CNT, (byte)64); for (int i = 0; i < len8; i++) { - output[i + outOff] = (byte)((m_buf[i]) ^ s[i]); + output[i + outOff] = (byte)((m_buf[i]) ^ S[i]); } - System.arraycopy(npub, 0, s, 0, 16); + System.arraycopy(npub, 0, S, 0, 16); lfsr_gf56(CNT); @@ -547,15 +550,15 @@ else if ((m_aadPos >= 0) && (aadLen != 0)) @Override public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { - System.arraycopy(npub, 0, s, 0, 16); - block_cipher(s, Z, T, 0, CNT, (byte)64); + System.arraycopy(npub, 0, S, 0, 16); + block_cipher(S, Z, T, 0, CNT, (byte)64); for (int i = 0; i < AD_BLK_LEN_HALF; i++) { - output[i + outOff] = (byte)((input[i + inOff]) ^ s[i]); + output[i + outOff] = (byte)((input[i + inOff]) ^ S[i]); } - System.arraycopy(npub, 0, s, 0, 16); - block_cipher(s, Z, T, 0, CNT, (byte)65); - System.arraycopy(s, 0, Z, 0, 16); + System.arraycopy(npub, 0, S, 0, 16); + block_cipher(S, Z, T, 0, CNT, (byte)65); + System.arraycopy(S, 0, Z, 0, 16); lfsr_gf56(CNT); // ipad_256(ipad*_128(A)||ipad*_128(C)||N|| CNT System.arraycopy(output, outOff, m_aad, m_aadPos, BlockSize); @@ -575,15 +578,15 @@ public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int out @Override public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - System.arraycopy(npub, 0, s, 0, 16); - block_cipher(s, Z, T, 0, CNT, (byte)64); + System.arraycopy(npub, 0, S, 0, 16); + block_cipher(S, Z, T, 0, CNT, (byte)64); for (int i = 0; i < AD_BLK_LEN_HALF; i++) { - output[i + outOff] = (byte)((input[i + inOff]) ^ s[i]); + output[i + outOff] = (byte)((input[i + inOff]) ^ S[i]); } - System.arraycopy(npub, 0, s, 0, 16); - block_cipher(s, Z, T, 0, CNT, (byte)65); - System.arraycopy(s, 0, Z, 0, 16); + System.arraycopy(npub, 0, S, 0, 16); + block_cipher(S, Z, T, 0, CNT, (byte)65); + System.arraycopy(S, 0, Z, 0, 16); lfsr_gf56(CNT); // ipad_256(ipad*_128(A)||ipad*_128(C)||N|| CNT System.arraycopy(input, inOff, m_aad, m_aadPos, BlockSize); @@ -609,6 +612,8 @@ public void reset() Arrays.clear(LR); Arrays.clear(CNT_Z); Arrays.clear(T); + Arrays.clear(S); + reset_lfsr_gf56(CNT); System.arraycopy(npub, 0, Z, 0, 16); block_cipher(Z, k, T, 0, CNT_Z, (byte)66); reset_lfsr_gf56(CNT_Z); @@ -953,10 +958,8 @@ protected void reset(boolean clearMac) { aadLen = 0; messegeLen = 0; - Arrays.clear(s); - Arrays.clear(CNT); - instance.reset(); bufferReset(); + instance.reset(); super.reset(clearMac); } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java index cb18f84fe0..2b987520de 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java @@ -14,6 +14,7 @@ import org.bouncycastle.crypto.engines.GiftCofbEngine; import org.bouncycastle.crypto.engines.RomulusEngine; import org.bouncycastle.crypto.modes.AEADBlockCipher; +import org.bouncycastle.crypto.modes.AEADCipher; import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; @@ -31,10 +32,65 @@ public String getName() public void performTest() throws Exception { - CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM), "crypto/romulus", "m_LWC_AEAD_KAT_128_128.txt", this); + //CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM), "crypto/romulus", "m_LWC_AEAD_KAT_128_128.txt", this); CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT), "crypto/romulus", "t_LWC_AEAD_KAT_128_128.txt", this); CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN), "crypto/romulus", "n_LWC_AEAD_KAT_128_128.txt", this); + CipherTest.implTestBufferingEngine(16, 16, 128, this, new CipherTest.Instance() + { + @Override + public AEADCipher createInstance() + { + return new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT); + } + }); + CipherTest.implTestBufferingEngine(16, 16, 128, this, new CipherTest.Instance() + { + @Override + public AEADCipher createInstance() + { + return new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN); + } + }); + CipherTest.implTestExceptionsEngine(16, 16, this, new CipherTest.Instance() + { + @Override + public AEADCipher createInstance() + { + return new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT); + } + }); + CipherTest.implTestExceptionsEngine(16, 16, this, new CipherTest.Instance() + { + @Override + public AEADCipher createInstance() + { + return new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN); + } + }); + implTestParametersEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT), 16, 16, 16); + implTestParametersEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN), 16, 16, 16); + CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT)); + CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN)); + CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT)); + CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN)); + + CipherTest.checkCipher(16, 16, 40, 128, new CipherTest.Instance() + { + public AEADCipher createInstance() + { + return new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT); + } + }); + + CipherTest.checkCipher(16, 16, 40, 128, new CipherTest.Instance() + { + public AEADCipher createInstance() + { + return new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN); + } + }); + // RomulusEngine romulus = new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT); // testExceptions(romulus, romulus.getKeyBytesSize(), romulus.getIVBytesSize(), romulus.getBlockSize()); // romulus = new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM); @@ -48,6 +104,32 @@ public void performTest() // testVectors(RomulusEngine.RomulusParameters.RomulusN, "n"); } + private void implTestParametersEngine(RomulusEngine cipher, int keySize, int ivSize, + int macSize) + { + if (cipher.getKeyBytesSize() != keySize) + { + fail("key bytes of " + cipher.getAlgorithmName() + " is not correct"); + } + if (cipher.getIVBytesSize() != ivSize) + { + fail("iv bytes of " + cipher.getAlgorithmName() + " is not correct"); + } + + CipherParameters parameters = new ParametersWithIV(new KeyParameter(new byte[keySize]), new byte[ivSize]); + + cipher.init(true, parameters); + if (cipher.getOutputSize(0) != macSize) + { + fail("getOutputSize of " + cipher.getAlgorithmName() + " is incorrect for encryption"); + } + + cipher.init(false, parameters); + if (cipher.getOutputSize(macSize) != 0) + { + fail("getOutputSize of " + cipher.getAlgorithmName() + " is incorrect for decryption"); + } + } private void testVectorsHash() From 24797f683f62c9d55ff91bcfc9c9a0ea5747b870 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 21 Jan 2025 22:32:03 +1100 Subject: [PATCH 032/890] Update of PQC key encodings for ML-KEM and ML-DSA (no encoding preservation) --- .../pqc/crypto/util/PrivateKeyFactory.java | 92 ++++++++++++++++--- .../crypto/util/PrivateKeyInfoFactory.java | 69 +++++++++----- .../pqc/crypto/util/PublicKeyFactory.java | 8 +- .../bouncycastle/pqc/crypto/util/Utils.java | 51 ++++++++-- .../asymmetric/mlkem/BCMLKEMPrivateKey.java | 2 +- .../pqc/jcajce/provider/test/MLDSATest.java | 11 ++- .../pqc/jcajce/provider/test/MLKEMTest.java | 1 + .../pqc/jcajce/provider/test/SLHDSATest.java | 6 +- 8 files changed, 181 insertions(+), 59 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java index 96a5dfcd7e..26ee5e5d88 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java @@ -11,6 +11,7 @@ import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.bc.BCObjectIdentifiers; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; @@ -228,10 +229,28 @@ else if (algOID.equals(NISTObjectIdentifiers.id_alg_ml_kem_512) || algOID.equals(NISTObjectIdentifiers.id_alg_ml_kem_768) || algOID.equals(NISTObjectIdentifiers.id_alg_ml_kem_1024)) { - ASN1OctetString mlkemKey = parseOctetString(keyInfo.getPrivateKey(), 64); + ASN1Primitive mlkemKey = parsePrimitiveString(keyInfo.getPrivateKey(), 64); MLKEMParameters mlkemParams = Utils.mlkemParamsLookup(algOID); - return new MLKEMPrivateKeyParameters(mlkemParams, mlkemKey.getOctets()); + if (mlkemKey instanceof ASN1Sequence) + { + ASN1Sequence keySeq = ASN1Sequence.getInstance(mlkemKey); + + if (keySeq.getObjectAt(0) instanceof ASN1OctetString) + { + return new MLKEMPrivateKeyParameters(mlkemParams, ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets()); + } + else + { + return new MLKEMPrivateKeyParameters(mlkemParams, ASN1OctetString.getInstance((ASN1TaggedObject)keySeq.getObjectAt(0), false).getOctets()); + } + } + else if (mlkemKey instanceof ASN1OctetString) + { + return new MLKEMPrivateKeyParameters(mlkemParams, ASN1OctetString.getInstance(mlkemKey).getOctets()); + } + + throw new IllegalArgumentException("unknown key format"); } else if (algOID.on(BCObjectIdentifiers.pqc_kem_ntrulprime)) { @@ -260,18 +279,33 @@ else if (algOID.on(BCObjectIdentifiers.pqc_kem_sntruprime)) } else if (Utils.mldsaParams.containsKey(algOID)) { - ASN1Encodable keyObj = parseOctetString(keyInfo.getPrivateKey(), 32); + ASN1Encodable keyObj = parsePrimitiveString(keyInfo.getPrivateKey(), 32); MLDSAParameters spParams = Utils.mldsaParamsLookup(algOID); - if (keyObj instanceof DEROctetString) + MLDSAPublicKeyParameters pubParams = null; + if (keyInfo.getPublicKeyData() != null) + { + pubParams = PublicKeyFactory.MLDSAConverter.getPublicKeyParams(spParams, keyInfo.getPublicKeyData()); + } + + if (keyObj instanceof ASN1OctetString) { byte[] data = ASN1OctetString.getInstance(keyObj).getOctets(); - if (keyInfo.getPublicKeyData() != null) + + return new MLDSAPrivateKeyParameters(spParams, data, pubParams); + } + else if (keyObj instanceof ASN1Sequence) + { + ASN1Sequence keySeq = ASN1Sequence.getInstance(keyObj); + + if (keySeq.getObjectAt(0) instanceof ASN1OctetString) + { + return new MLDSAPrivateKeyParameters(spParams, ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(), pubParams); + } + else { - MLDSAPublicKeyParameters pubParams = PublicKeyFactory.MLDSAConverter.getPublicKeyParams(spParams, keyInfo.getPublicKeyData()); - return new MLDSAPrivateKeyParameters(spParams, data, pubParams); + return new MLDSAPrivateKeyParameters(spParams, ASN1OctetString.getInstance((ASN1TaggedObject)keySeq.getObjectAt(0), false).getOctets(), pubParams); } - return new MLDSAPrivateKeyParameters(spParams, data); } else { @@ -464,15 +498,49 @@ private static ASN1OctetString parseOctetString(ASN1OctetString octStr, int expe // // possible internal OCTET STRING, possibly long form with or without the internal OCTET STRING - data = Utils.readOctetString(data); - if (data != null) + ASN1OctetString obj = Utils.parseOctetData(data); + + if (obj != null) + { + return ASN1OctetString.getInstance(obj); + } + + return octStr; + } + + /** + * So it seems for the new PQC algorithms, there's a couple of approaches to what goes in the OCTET STRING + * and in this case there may also be SEQUENCE. + */ + private static ASN1Primitive parsePrimitiveString(ASN1OctetString octStr, int expectedLength) + throws IOException + { + byte[] data = octStr.getOctets(); + // + // it's the right length for a RAW encoding, just return it. + // + if (data.length == expectedLength) + { + return octStr; + } + + // + // possible internal OCTET STRING, possibly long form with or without the internal OCTET STRING + // or possible SEQUENCE + ASN1Encodable obj = Utils.parseData(data); + + if (obj instanceof ASN1OctetString) { - return new DEROctetString(data); + return ASN1OctetString.getInstance(obj); + } + if (obj instanceof ASN1Sequence) + { + return ASN1Sequence.getInstance(obj); } return octStr; } - + private static short[] convert(byte[] octets) { short[] rv = new short[octets.length / 2]; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java index baf84f6262..b7f92bb668 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java @@ -3,9 +3,11 @@ import java.io.IOException; import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; @@ -32,7 +34,6 @@ import org.bouncycastle.pqc.crypto.lms.HSSPrivateKeyParameters; import org.bouncycastle.pqc.crypto.lms.LMSPrivateKeyParameters; import org.bouncycastle.pqc.crypto.mldsa.MLDSAPrivateKeyParameters; -import org.bouncycastle.pqc.crypto.mldsa.MLDSAPublicKeyParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMPrivateKeyParameters; import org.bouncycastle.pqc.crypto.newhope.NHPrivateKeyParameters; import org.bouncycastle.pqc.crypto.ntru.NTRUPrivateKeyParameters; @@ -246,16 +247,18 @@ else if (privateKey instanceof MLKEMPrivateKeyParameters) MLKEMPrivateKeyParameters params = (MLKEMPrivateKeyParameters)privateKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.mlkemOidLookup(params.getParameters())); - - byte[] seed = params.getSeed(); - if (seed == null) - { - return new PrivateKeyInfo(algorithmIdentifier, params.getEncoded(), attributes); - } - else - { - return new PrivateKeyInfo(algorithmIdentifier, seed, attributes); - } + + return new PrivateKeyInfo(algorithmIdentifier, getBasicPQCEncoding(params.getSeed(), params.getEncoded()), attributes); +// byte[] seed = params.getSeed(); +// +// if (seed == null) +// { +// return new PrivateKeyInfo(algorithmIdentifier, params.getEncoded(), attributes); +// } +// else +// { +// return new PrivateKeyInfo(algorithmIdentifier, seed, attributes); +// } } else if (privateKey instanceof NTRULPRimePrivateKeyParameters) { @@ -294,19 +297,20 @@ else if (privateKey instanceof MLDSAPrivateKeyParameters) AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.mldsaOidLookup(params.getParameters())); - byte[] seed = params.getSeed(); - if (seed == null) - { - MLDSAPublicKeyParameters pubParams = params.getPublicKeyParameters(); - - return new PrivateKeyInfo(algorithmIdentifier, params.getEncoded(), attributes, pubParams.getEncoded()); - } - else - { - MLDSAPublicKeyParameters pubParams = params.getPublicKeyParameters(); - - return new PrivateKeyInfo(algorithmIdentifier, params.getSeed(), attributes); - } + return new PrivateKeyInfo(algorithmIdentifier, getBasicPQCEncoding(params.getSeed(), params.getEncoded()), attributes); +// byte[] seed = params.getSeed(); +// if (seed == null) +// { +// MLDSAPublicKeyParameters pubParams = params.getPublicKeyParameters(); +// +// return new PrivateKeyInfo(algorithmIdentifier, params.getEncoded(), attributes, pubParams.getEncoded()); +// } +// else +// { +// MLDSAPublicKeyParameters pubParams = params.getPublicKeyParameters(); +// +// return new PrivateKeyInfo(algorithmIdentifier, seed, attributes, pubParams.getEncoded()); +// } } else if (privateKey instanceof DilithiumPrivateKeyParameters) { @@ -395,6 +399,23 @@ private static XMSSPrivateKey xmssCreateKeyStructure(XMSSPrivateKeyParameters ke } } + private static ASN1Sequence getBasicPQCEncoding(byte[] seed, byte[] expanded) + { + ASN1EncodableVector v = new ASN1EncodableVector(2); + + if (seed != null) + { + v.add(new DEROctetString(seed)); + } + + if (expanded != null) + { + v.add(new DERTaggedObject(false, 1, new DEROctetString(expanded))); + } + + return new DERSequence(v); + } + private static XMSSMTPrivateKey xmssmtCreateKeyStructure(XMSSMTPrivateKeyParameters keyParams) throws IOException { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java index 6f2fde3b0e..d7320c8090 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java @@ -438,11 +438,11 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje throws IOException { byte[] keyEnc = keyInfo.getPublicKeyData().getOctets(); - byte[] data = Utils.readOctetString(keyEnc); + ASN1OctetString data = (ASN1OctetString)Utils.parseData(keyEnc); if (data != null) { - return getLmsKeyParameters(data); + return getLmsKeyParameters(data.getOctets()); } return getLmsKeyParameters(keyEnc); @@ -567,11 +567,11 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje throws IOException { byte[] keyEnc = keyInfo.getPublicKeyData().getOctets(); - byte[] data = Utils.readOctetString(keyEnc); + ASN1OctetString data = Utils.parseOctetData(keyEnc); if (data != null) { - return getNtruPublicKeyParameters(keyInfo, data); + return getNtruPublicKeyParameters(keyInfo, data.getOctets()); } return getNtruPublicKeyParameters(keyInfo, keyEnc); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java index 2b4b420e13..ddc3e0867d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java @@ -1,11 +1,13 @@ package org.bouncycastle.pqc.crypto.util; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.BERTags; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.bc.BCObjectIdentifiers; @@ -38,7 +40,6 @@ import org.bouncycastle.pqc.crypto.xmss.XMSSKeyParameters; import org.bouncycastle.pqc.legacy.crypto.qtesla.QTESLASecurityCategory; import org.bouncycastle.util.Integers; -import org.bouncycastle.util.io.Streams; class Utils { @@ -783,18 +784,48 @@ static RainbowParameters rainbowParamsLookup(ASN1ObjectIdentifier oid) return (RainbowParameters)rainbowParams.get(oid); } - static byte[] readOctetString(byte[] data) - throws IOException + private static boolean isRaw(byte[] data) { - if (data[0] == BERTags.OCTET_STRING) + // check well-formed first + ByteArrayInputStream bIn = new ByteArrayInputStream(data); + + int tag = bIn.read(); + int len = readLen(bIn); + if (len != bIn.available()) + { + return true; + } + + return false; + } + + static ASN1OctetString parseOctetData(byte[] data) + { + // check well-formed first + if (!isRaw(data)) + { + if (data[0] == BERTags.OCTET_STRING) + { + return ASN1OctetString.getInstance(data); + } + } + + return null; + } + + static ASN1Primitive parseData(byte[] data) + { + // check well-formed first + if (!isRaw(data)) { - ByteArrayInputStream bIn = new ByteArrayInputStream(data); + if (data[0] == (BERTags.SEQUENCE | BERTags.CONSTRUCTED)) + { + return ASN1Sequence.getInstance(data); + } - int tag = bIn.read(); - int len = readLen(bIn); - if (len == bIn.available()) + if (data[0] == BERTags.OCTET_STRING) { - return Streams.readAll(bIn); + return ASN1OctetString.getInstance(data); } } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java index 012e08b7cf..44fcfcd22c 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java @@ -42,7 +42,7 @@ public BCMLKEMPrivateKey(PrivateKeyInfo keyInfo) private void init(PrivateKeyInfo keyInfo) throws IOException { - this.attributes = keyInfo.getAttributes();; + this.attributes = keyInfo.getAttributes(); this.params = (MLKEMPrivateKeyParameters)PrivateKeyFactory.createKey(keyInfo); this.algorithm = Strings.toUpperCase(MLKEMParameterSpec.fromName(params.getParameters().getName()).getName()); } diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java index 44939fadd2..9b93c58007 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java @@ -20,6 +20,7 @@ import junit.framework.TestCase; import org.bouncycastle.asn1.ASN1BitString; import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; @@ -83,7 +84,7 @@ public void testParametersAndParamSpecs() assertEquals(names[i], MLDSAParameterSpec.fromName(names[i]).getName()); } } - + public void testKeyFactory() throws Exception { @@ -344,7 +345,7 @@ public void testMLDSAKATSig() PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp.getPrivate().getEncoded()); ASN1OctetString seq = privInfo.getPrivateKey(); - assertTrue(Arrays.areEqual(seq.getOctets(), seed)); + assertTrue(Arrays.areEqual(ASN1OctetString.getInstance(ASN1Sequence.getInstance(seq.getOctets()).getObjectAt(0)).getOctets(), seed)); Signature sig = Signature.getInstance("ML-DSA", "BC"); @@ -408,7 +409,7 @@ public void testMLDSAKATSigWithContext() PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp.getPrivate().getEncoded()); ASN1OctetString seq = privInfo.getPrivateKey(); - assertTrue(Arrays.areEqual(seq.getOctets(), seed)); + assertTrue(Arrays.areEqual(ASN1OctetString.getInstance(ASN1Sequence.getInstance(seq.getOctets()).getObjectAt(0)).getOctets(), seed)); Signature sig = Signature.getInstance("ML-DSA", "BC"); @@ -492,7 +493,7 @@ public void testHashMLDSAKATSig() PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp.getPrivate().getEncoded()); ASN1OctetString seq = privInfo.getPrivateKey(); - assertTrue(Arrays.areEqual(seq.getOctets(), seed)); + assertTrue(Arrays.areEqual(ASN1OctetString.getInstance(ASN1Sequence.getInstance(seq.getOctets()).getObjectAt(0)).getOctets(), seed)); Signature sig = Signature.getInstance("HASH-ML-DSA", "BC"); @@ -587,7 +588,7 @@ public void testHashMLDSAKATSigWithContext() PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp.getPrivate().getEncoded()); ASN1OctetString seq = privInfo.getPrivateKey(); - assertTrue(Arrays.areEqual(seq.getOctets(), seed)); + assertTrue(Arrays.areEqual(ASN1OctetString.getInstance(ASN1Sequence.getInstance(seq.getOctets()).getObjectAt(0)).getOctets(), seed)); Signature sig = Signature.getInstance("HASH-ML-DSA", "BC"); diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java index 0ad4e0289f..e9c01e4119 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java @@ -123,6 +123,7 @@ public void testBasicKEMCamellia() KeyPairGenerator kpg = KeyPairGenerator.getInstance("ML-KEM", "BC"); kpg.initialize(MLKEMParameterSpec.ml_kem_512, new SecureRandom()); + kpg.generateKeyPair().getPrivate().getEncoded(); performKEMScipher(kpg.generateKeyPair(), "ML-KEM", new KTSParameterSpec.Builder("Camellia", 128).withNoKdf().build()); performKEMScipher(kpg.generateKeyPair(), "ML-KEM", new KTSParameterSpec.Builder("Camellia-KWP", 128).withNoKdf().build()); } diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/SLHDSATest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/SLHDSATest.java index 58b8d6cdea..7f87ae00e7 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/SLHDSATest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/SLHDSATest.java @@ -152,13 +152,13 @@ public void testKeyFactory() NISTObjectIdentifiers.id_hash_slh_dsa_shake_256s_with_shake256, }; - for (int i = 0; i != names.length; i++) + for (int i = 0; i != 1; i++) { KeyPairGenerator kpGen = KeyPairGenerator.getInstance(names[i]); KeyPair kp = kpGen.generateKeyPair(); - + System.err.println(names[i]); tryKeyFact(KeyFactory.getInstance(names[i], "BC"), kp, kp44, "2.16.840.1.101.3.4.3.17"); - tryKeyFact(KeyFactory.getInstance(oids[i].toString(), "BC"), kp, kp44, "2.16.840.1.101.3.4.3.17"); + // tryKeyFact(KeyFactory.getInstance(oids[i].toString(), "BC"), kp, kp44, "2.16.840.1.101.3.4.3.17"); } } From 8b813e11536eddea8a2efc370ee7db0ddadcd97c Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 21 Jan 2025 21:13:14 +0700 Subject: [PATCH 033/890] Refactoring in crypto.hpke --- .../org/bouncycastle/crypto/hpke/AEAD.java | 107 +++----- .../org/bouncycastle/crypto/hpke/DHKEM.java | 244 ++++++++---------- 2 files changed, 139 insertions(+), 212 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/hpke/AEAD.java b/core/src/main/java/org/bouncycastle/crypto/hpke/AEAD.java index e4e840bec1..a6d93cab1b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/hpke/AEAD.java +++ b/core/src/main/java/org/bouncycastle/crypto/hpke/AEAD.java @@ -9,11 +9,11 @@ import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Bytes; import org.bouncycastle.util.Pack; public class AEAD { - private final short aeadId; private final byte[] key; private final byte[] baseNonce; @@ -32,7 +32,7 @@ public AEAD(short aeadId, byte[] key, byte[] baseNonce) { case HPKE.aead_AES_GCM128: case HPKE.aead_AES_GCM256: - cipher = new GCMBlockCipher(new AESEngine()); + cipher = GCMBlockCipher.newInstance(AESEngine.newInstance()); break; case HPKE.aead_CHACHA20_POLY1305: cipher = new ChaCha20Poly1305(); @@ -42,106 +42,73 @@ public AEAD(short aeadId, byte[] key, byte[] baseNonce) } } + // used by Sender + public byte[] seal(byte[] aad, byte[] pt) + throws InvalidCipherTextException + { + return process(true, aad, pt, 0, pt.length); + } // used by Sender public byte[] seal(byte[] aad, byte[] pt, int ptOffset, int ptLength) throws InvalidCipherTextException { - if (ptOffset < 0 || ptOffset > pt.length) - { - throw new IndexOutOfBoundsException("Invalid offset"); - } - if (ptOffset + ptLength > pt.length) - { - throw new IndexOutOfBoundsException("Invalid length"); - } - - CipherParameters params; - switch (aeadId) - { - case HPKE.aead_AES_GCM128: - case HPKE.aead_AES_GCM256: - case HPKE.aead_CHACHA20_POLY1305: - params = new ParametersWithIV(new KeyParameter(key), ComputeNonce()); - break; - case HPKE.aead_EXPORT_ONLY: - default: - throw new IllegalStateException("Export only mode, cannot be used to seal/open"); - } - cipher.init(true, params); - cipher.processAADBytes(aad, 0, aad.length); - byte[] ct = new byte[cipher.getOutputSize(ptLength)]; - int len = cipher.processBytes(pt, ptOffset, ptLength, ct, 0); - cipher.doFinal(ct, len); + Arrays.validateSegment(pt, ptOffset, ptLength); - seq++; - return ct; + return process(true, aad, pt, ptOffset, ptLength); } - // used by Sender - public byte[] seal(byte[] aad, byte[] pt) + // used by Receiver + public byte[] open(byte[] aad, byte[] ct) throws InvalidCipherTextException { - return this.seal(aad, pt, 0, pt.length); + return process(false, aad, ct, 0, ct.length); } // used by Receiver public byte[] open(byte[] aad, byte[] ct, int ctOffset, int ctLength) throws InvalidCipherTextException { - if (ctOffset < 0 || ctOffset > ct.length) - { - throw new IndexOutOfBoundsException("Invalid offset"); - } - if (ctOffset + ctLength > ct.length) - { - throw new IndexOutOfBoundsException("Invalid length"); - } + Arrays.validateSegment(ct, ctOffset, ctLength); + + return process(false, aad, ct, ctOffset, ctLength); + } + + private byte[] computeNonce() + { + byte[] seq_bytes = Pack.longToBigEndian(seq++); + byte[] nonce = Arrays.clone(baseNonce); + Bytes.xorTo(8, seq_bytes, 0, nonce, nonce.length - 8); + return nonce; + } + private byte[] process(boolean forEncryption, byte[] aad, byte[] buf, int off, int len) + throws InvalidCipherTextException + { CipherParameters params; switch (aeadId) { case HPKE.aead_AES_GCM128: case HPKE.aead_AES_GCM256: case HPKE.aead_CHACHA20_POLY1305: - params = new ParametersWithIV(new KeyParameter(key), ComputeNonce()); + params = new ParametersWithIV(new KeyParameter(key), computeNonce()); break; case HPKE.aead_EXPORT_ONLY: default: throw new IllegalStateException("Export only mode, cannot be used to seal/open"); } - cipher.init(false, params); + cipher.init(forEncryption, params); cipher.processAADBytes(aad, 0, aad.length); - byte[] pt = new byte[cipher.getOutputSize(ctLength)]; - int len = cipher.processBytes(ct, ctOffset, ctLength, pt, 0); - len += cipher.doFinal(pt, len); - - seq++; - return pt; - } - - // used by Receiver - public byte[] open(byte[] aad, byte[] ct) - throws InvalidCipherTextException - { - return this.open(aad, ct, 0, ct.length); - } - - private byte[] ComputeNonce() - { - byte[] seq_bytes = Pack.longToBigEndian(seq); - int Nn = baseNonce.length; - byte[] nonce = Arrays.clone(baseNonce); - //xor - for (int i = 0; i < 8; i++) + byte[] output = new byte[cipher.getOutputSize(len)]; + int pos = cipher.processBytes(buf, off, len, output, 0); + pos += cipher.doFinal(output, pos); + if (pos != output.length) { - nonce[Nn - 8 + i] ^= seq_bytes[i]; + // Existing AEAD modes should return exact value for getOutputSize. + throw new IllegalStateException(); } - return nonce; + return output; } - - } - diff --git a/core/src/main/java/org/bouncycastle/crypto/hpke/DHKEM.java b/core/src/main/java/org/bouncycastle/crypto/hpke/DHKEM.java index 44a46d6a47..9c23ed9b2e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/hpke/DHKEM.java +++ b/core/src/main/java/org/bouncycastle/crypto/hpke/DHKEM.java @@ -6,8 +6,10 @@ import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; import org.bouncycastle.crypto.BasicAgreement; +import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement; import org.bouncycastle.crypto.agreement.XDHBasicAgreement; +import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.generators.X25519KeyPairGenerator; import org.bouncycastle.crypto.generators.X448KeyPairGenerator; @@ -22,18 +24,13 @@ import org.bouncycastle.crypto.params.X448KeyGenerationParameters; import org.bouncycastle.crypto.params.X448PrivateKeyParameters; import org.bouncycastle.crypto.params.X448PublicKeyParameters; -import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.math.ec.FixedPointCombMultiplier; import org.bouncycastle.math.ec.WNafUtil; -import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve; -import org.bouncycastle.math.ec.custom.sec.SecP384R1Curve; -import org.bouncycastle.math.ec.custom.sec.SecP521R1Curve; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.BigIntegers; import org.bouncycastle.util.Pack; import org.bouncycastle.util.Strings; -import org.bouncycastle.util.encoders.Hex; - class DHKEM extends KEM @@ -56,44 +53,25 @@ class DHKEM protected DHKEM(short kemid) { this.kemId = kemid; - ECCurve curve; + switch (kemid) { case HPKE.kem_P256_SHA256: this.hkdf = new HKDF(HPKE.kdf_HKDF_SHA256); - curve = new SecP256R1Curve(); - domainParams = new ECDomainParameters( - curve, - curve.createPoint( - new BigInteger(1, Hex.decode("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296")), - new BigInteger(1, Hex.decode("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5")) - ), - curve.getOrder(), - curve.getCofactor(), - Hex.decode("c49d360886e704936a6678e1139d26b7819f7e90") - ); + domainParams = getDomainParameters("P-256"); this.agreement = new ECDHCBasicAgreement(); bitmask = (byte)0xff; Nsk = 32; Nsecret = 32; Nenc = 65; + this.kpGen = new ECKeyPairGenerator(); - this.kpGen.init(new ECKeyGenerationParameters(domainParams, new SecureRandom())); + this.kpGen.init(new ECKeyGenerationParameters(domainParams, getSecureRandom())); break; case HPKE.kem_P384_SHA348: this.hkdf = new HKDF(HPKE.kdf_HKDF_SHA384); - curve = new SecP384R1Curve(); - domainParams = new ECDomainParameters( - curve, - curve.createPoint( - new BigInteger(1, Hex.decode("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7")), - new BigInteger(1, Hex.decode("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f")) - ), - curve.getOrder(), - curve.getCofactor(), - Hex.decode("a335926aa319a27a1d00896a6773a4827acdac73") - ); + domainParams = getDomainParameters("P-384"); this.agreement = new ECDHCBasicAgreement(); bitmask = (byte)0xff; Nsk = 48; @@ -101,23 +79,12 @@ protected DHKEM(short kemid) Nenc = 97; this.kpGen = new ECKeyPairGenerator(); - this.kpGen.init(new ECKeyGenerationParameters(domainParams, new SecureRandom())); + this.kpGen.init(new ECKeyGenerationParameters(domainParams, getSecureRandom())); break; case HPKE.kem_P521_SHA512: this.hkdf = new HKDF(HPKE.kdf_HKDF_SHA512); - - curve = new SecP521R1Curve(); - domainParams = new ECDomainParameters( - curve, - curve.createPoint( - new BigInteger("c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", 16), - new BigInteger("11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", 16) - ), - curve.getOrder(), - curve.getCofactor(), - Hex.decode("d09e8800291cb85396cc6717393284aaa0da64ba") - ); + domainParams = getDomainParameters("P-521"); this.agreement = new ECDHCBasicAgreement(); bitmask = 0x01; Nsk = 66; @@ -125,7 +92,7 @@ protected DHKEM(short kemid) Nenc = 133; this.kpGen = new ECKeyPairGenerator(); - this.kpGen.init(new ECKeyGenerationParameters(domainParams, new SecureRandom())); + this.kpGen.init(new ECKeyGenerationParameters(domainParams, getSecureRandom())); break; case HPKE.kem_X25519_SHA256: @@ -134,8 +101,9 @@ protected DHKEM(short kemid) Nsecret = 32; Nsk = 32; Nenc = 32; + this.kpGen = new X25519KeyPairGenerator(); - this.kpGen.init(new X25519KeyGenerationParameters(new SecureRandom())); + this.kpGen.init(new X25519KeyGenerationParameters(getSecureRandom())); break; case HPKE.kem_X448_SHA512: @@ -146,7 +114,7 @@ protected DHKEM(short kemid) Nenc = 56; this.kpGen = new X448KeyPairGenerator(); - this.kpGen.init(new X448KeyGenerationParameters(new SecureRandom())); + this.kpGen.init(new X448KeyGenerationParameters(getSecureRandom())); break; default: @@ -156,7 +124,6 @@ protected DHKEM(short kemid) public byte[] SerializePublicKey(AsymmetricKeyParameter key) { - switch (kemId) { case HPKE.kem_P256_SHA256: @@ -171,6 +138,7 @@ public byte[] SerializePublicKey(AsymmetricKeyParameter key) throw new IllegalStateException("invalid kem id"); } } + public byte[] SerializePrivateKey(AsymmetricKeyParameter key) { switch (kemId) @@ -178,7 +146,7 @@ public byte[] SerializePrivateKey(AsymmetricKeyParameter key) case HPKE.kem_P256_SHA256: case HPKE.kem_P384_SHA348: case HPKE.kem_P521_SHA512: - return formatBigIntegerBytes(((ECPrivateKeyParameters)key).getD().toByteArray(), Nsk); + return BigIntegers.asUnsignedByteArray(Nsk, ((ECPrivateKeyParameters)key).getD()); case HPKE.kem_X448_SHA512: return ((X448PrivateKeyParameters)key).getEncoded(); case HPKE.kem_X25519_SHA256: @@ -192,22 +160,32 @@ public AsymmetricKeyParameter DeserializePublicKey(byte[] encoded) { switch (kemId) { - case HPKE.kem_P256_SHA256: - case HPKE.kem_P384_SHA348: - case HPKE.kem_P521_SHA512: - ECPoint G = domainParams.getCurve().decodePoint(encoded); - return new ECPublicKeyParameters(G, domainParams); - case HPKE.kem_X448_SHA512: - return new X448PublicKeyParameters(encoded); - case HPKE.kem_X25519_SHA256: - return new X25519PublicKeyParameters(encoded); - default: - throw new IllegalStateException("invalid kem id"); + case HPKE.kem_P256_SHA256: + case HPKE.kem_P384_SHA348: + case HPKE.kem_P521_SHA512: + // TODO Does the encoding have to be uncompressed? (i.e. encoded.length MUST be Nenc?) + ECPoint G = domainParams.getCurve().decodePoint(encoded); + return new ECPublicKeyParameters(G, domainParams); + case HPKE.kem_X448_SHA512: + return new X448PublicKeyParameters(encoded); + case HPKE.kem_X25519_SHA256: + return new X25519PublicKeyParameters(encoded); + default: + throw new IllegalStateException("invalid kem id"); } } public AsymmetricCipherKeyPair DeserializePrivateKey(byte[] skEncoded, byte[] pkEncoded) { + if (skEncoded == null) + { + throw new NullPointerException("'skEncoded' cannot be null"); + } + if (skEncoded.length != Nsk) + { + throw new IllegalArgumentException("'skEncoded' has invalid length"); + } + AsymmetricKeyParameter pubParam = null; if (pkEncoded != null) @@ -217,34 +195,34 @@ public AsymmetricCipherKeyPair DeserializePrivateKey(byte[] skEncoded, byte[] pk switch (kemId) { - case HPKE.kem_P256_SHA256: - case HPKE.kem_P384_SHA348: - case HPKE.kem_P521_SHA512: - BigInteger d = new BigInteger(1, skEncoded); - ECPrivateKeyParameters ec = new ECPrivateKeyParameters(d, domainParams); + case HPKE.kem_P256_SHA256: + case HPKE.kem_P384_SHA348: + case HPKE.kem_P521_SHA512: + BigInteger d = new BigInteger(1, skEncoded); + ECPrivateKeyParameters ec = new ECPrivateKeyParameters(d, domainParams); - if (pubParam == null) - { - ECPoint Q = new FixedPointCombMultiplier().multiply(domainParams.getG(), ((ECPrivateKeyParameters)ec).getD()); - pubParam = new ECPublicKeyParameters(Q, domainParams); - } - return new AsymmetricCipherKeyPair(pubParam, ec); - case HPKE.kem_X448_SHA512: - X448PrivateKeyParameters x448 = new X448PrivateKeyParameters(skEncoded); - if (pubParam == null) - { - pubParam = x448.generatePublicKey(); - } - return new AsymmetricCipherKeyPair(pubParam, x448); - case HPKE.kem_X25519_SHA256: - X25519PrivateKeyParameters x25519 = new X25519PrivateKeyParameters(skEncoded); - if (pubParam == null) - { - pubParam = x25519.generatePublicKey(); - } - return new AsymmetricCipherKeyPair(pubParam, x25519); - default: - throw new IllegalStateException("invalid kem id"); + if (pubParam == null) + { + ECPoint Q = new FixedPointCombMultiplier().multiply(domainParams.getG(), ((ECPrivateKeyParameters)ec).getD()); + pubParam = new ECPublicKeyParameters(Q, domainParams); + } + return new AsymmetricCipherKeyPair(pubParam, ec); + case HPKE.kem_X448_SHA512: + X448PrivateKeyParameters x448 = new X448PrivateKeyParameters(skEncoded); + if (pubParam == null) + { + pubParam = x448.generatePublicKey(); + } + return new AsymmetricCipherKeyPair(pubParam, x448); + case HPKE.kem_X25519_SHA256: + X25519PrivateKeyParameters x25519 = new X25519PrivateKeyParameters(skEncoded); + if (pubParam == null) + { + pubParam = x25519.generatePublicKey(); + } + return new AsymmetricCipherKeyPair(pubParam, x25519); + default: + throw new IllegalStateException("invalid kem id"); } } @@ -253,7 +231,7 @@ int getEncryptionSize() return Nenc; } - private boolean ValidateSk(BigInteger d) + private boolean validateSk(BigInteger d) { BigInteger n = domainParams.getN(); int nBitLength = n.bitLength(); @@ -289,44 +267,41 @@ public AsymmetricCipherKeyPair DeriveKeyPair(byte[] ikm) case HPKE.kem_P256_SHA256: case HPKE.kem_P384_SHA348: case HPKE.kem_P521_SHA512: + { byte[] dkp_prk = hkdf.LabeledExtract(null, suiteID, "dkp_prk", ikm); - int counter = 0; byte[] counterArray = new byte[1]; - while (true) + for (int counter = 0; counter < 256; ++counter) { - if (counter > 255) - { - throw new IllegalStateException("DeriveKeyPairError"); - } counterArray[0] = (byte)counter; byte[] bytes = hkdf.LabeledExpand(dkp_prk, suiteID, "candidate", counterArray, Nsk); bytes[0] = (byte)(bytes[0] & bitmask); - // generating keypair BigInteger d = new BigInteger(1, bytes); - if (ValidateSk(d)) + if (validateSk(d)) { ECPoint Q = new FixedPointCombMultiplier().multiply(domainParams.getG(), d); ECPrivateKeyParameters sk = new ECPrivateKeyParameters(d, domainParams); ECPublicKeyParameters pk = new ECPublicKeyParameters(Q, domainParams); return new AsymmetricCipherKeyPair(pk, sk); } - - counter++; } + throw new IllegalStateException("DeriveKeyPairError"); + } case HPKE.kem_X448_SHA512: - dkp_prk = hkdf.LabeledExtract(null, suiteID, "dkp_prk", ikm); + { + byte[] dkp_prk = hkdf.LabeledExtract(null, suiteID, "dkp_prk", ikm); byte[] x448sk = hkdf.LabeledExpand(dkp_prk, suiteID, "sk", null, Nsk); X448PrivateKeyParameters x448params = new X448PrivateKeyParameters(x448sk); return new AsymmetricCipherKeyPair(x448params.generatePublicKey(), x448params); - + } case HPKE.kem_X25519_SHA256: - dkp_prk = hkdf.LabeledExtract(null, suiteID, "dkp_prk", ikm); + { + byte[] dkp_prk = hkdf.LabeledExtract(null, suiteID, "dkp_prk", ikm); byte[] skBytes = hkdf.LabeledExpand(dkp_prk, suiteID, "sk", null, Nsk); X25519PrivateKeyParameters sk = new X25519PrivateKeyParameters(skBytes); - return new AsymmetricCipherKeyPair(sk.generatePublicKey(), sk); + } default: throw new IllegalStateException("invalid kem id"); } @@ -341,11 +316,8 @@ protected byte[][] Encap(AsymmetricKeyParameter pkR, AsymmetricCipherKeyPair kpE { byte[][] output = new byte[2][]; - //DH - agreement.init(kpE.getPrivate()); - - byte[] temp = agreement.calculateAgreement(pkR).toByteArray(); - byte[] secret = formatBigIntegerBytes(temp, agreement.getFieldSize()); + // DH + byte[] secret = calculateAgreement(agreement, kpE.getPrivate(), pkR); byte[] enc = SerializePublicKey(kpE.getPublic()); byte[] pkRm = SerializePublicKey(pkR); @@ -362,17 +334,13 @@ protected byte[] Decap(byte[] enc, AsymmetricCipherKeyPair kpR) { AsymmetricKeyParameter pkE = DeserializePublicKey(enc); - //DH - agreement.init(kpR.getPrivate()); - - byte[] temp = agreement.calculateAgreement(pkE).toByteArray(); // add leading zeros - byte[] secret = formatBigIntegerBytes(temp, agreement.getFieldSize()); + // DH + byte[] secret = calculateAgreement(agreement, kpR.getPrivate(), pkE); byte[] pkRm = SerializePublicKey(kpR.getPublic()); byte[] KEMContext = Arrays.concatenate(enc, pkRm); - byte[] sharedSecret = ExtractAndExpand(secret, KEMContext); - return sharedSecret; + return ExtractAndExpand(secret, KEMContext); } protected byte[][] AuthEncap(AsymmetricKeyParameter pkR, AsymmetricCipherKeyPair kpS) @@ -381,16 +349,11 @@ protected byte[][] AuthEncap(AsymmetricKeyParameter pkR, AsymmetricCipherKeyPair AsymmetricCipherKeyPair kpE = kpGen.generateKeyPair(); // todo: can be replaced with deriveKeyPair(random) - // DH(skE, pkR) - agreement.init(kpE.getPrivate()); - byte[] temp = agreement.calculateAgreement(pkR).toByteArray(); - byte[] secret1 = formatBigIntegerBytes(temp, agreement.getFieldSize()); + byte[] secret1 = calculateAgreement(agreement, kpE.getPrivate(), pkR); // DH(skS, pkR) - agreement.init(kpS.getPrivate()); - temp = agreement.calculateAgreement(pkR).toByteArray(); - byte[] secret2 = formatBigIntegerBytes(temp, agreement.getFieldSize()); + byte[] secret2 = calculateAgreement(agreement, kpS.getPrivate(), pkR); byte[] secret = Arrays.concatenate(secret1, secret2); byte[] enc = SerializePublicKey(kpE.getPublic()); @@ -411,15 +374,10 @@ protected byte[] AuthDecap(byte[] enc, AsymmetricCipherKeyPair kpR, AsymmetricKe AsymmetricKeyParameter pkE = DeserializePublicKey(enc); // DH(skR, pkE) - agreement.init(kpR.getPrivate()); - - byte[] temp = agreement.calculateAgreement(pkE).toByteArray(); // add leading zeros - byte[] secret1 = formatBigIntegerBytes(temp, agreement.getFieldSize()); + byte[] secret1 = calculateAgreement(agreement, kpR.getPrivate(), pkE); // DH(skR, pkS) - agreement.init(kpR.getPrivate()); - temp = agreement.calculateAgreement(pkS).toByteArray(); - byte[] secret2 = formatBigIntegerBytes(temp, agreement.getFieldSize()); + byte[] secret2 = calculateAgreement(agreement, kpR.getPrivate(), pkS); byte[] secret = Arrays.concatenate(secret1, secret2); @@ -427,8 +385,7 @@ protected byte[] AuthDecap(byte[] enc, AsymmetricCipherKeyPair kpR, AsymmetricKe byte[] pkSm = SerializePublicKey(pkS); byte[] KEMContext = Arrays.concatenate(enc, pkRm, pkSm); - byte[] sharedSecret = ExtractAndExpand(secret, KEMContext); - return sharedSecret; + return ExtractAndExpand(secret, KEMContext); } private byte[] ExtractAndExpand(byte[] dh, byte[] kemContext) @@ -437,21 +394,24 @@ private byte[] ExtractAndExpand(byte[] dh, byte[] kemContext) byte[] eae_prk = hkdf.LabeledExtract(null, suiteID, "eae_prk", dh); - byte[] sharedSecret = hkdf.LabeledExpand(eae_prk, suiteID, "shared_secret", kemContext, Nsecret); - return sharedSecret; + return hkdf.LabeledExpand(eae_prk, suiteID, "shared_secret", kemContext, Nsecret); } - private byte[] formatBigIntegerBytes(byte[] bigIntBytes, int outputSize) + private static byte[] calculateAgreement(BasicAgreement agreement, AsymmetricKeyParameter privateKey, + AsymmetricKeyParameter publicKey) { - byte[] output = new byte[outputSize]; - if (bigIntBytes.length <= outputSize) - { - System.arraycopy(bigIntBytes, 0, output, outputSize - bigIntBytes.length, bigIntBytes.length); - } - else - { - System.arraycopy(bigIntBytes, bigIntBytes.length - outputSize, output, 0, outputSize); - } - return output; + agreement.init(privateKey); + BigInteger z = agreement.calculateAgreement(publicKey); + return BigIntegers.asUnsignedByteArray(agreement.getFieldSize(), z); + } + + private static ECDomainParameters getDomainParameters(String curveName) + { + return new ECDomainParameters(CustomNamedCurves.getByName(curveName)); + } + + private static SecureRandom getSecureRandom() + { + return CryptoServicesRegistrar.getSecureRandom(); } } From a0ce263a3a60f49eb9ed7fa84fd41a2a60b357d6 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 22 Jan 2025 17:22:18 +1030 Subject: [PATCH 034/890] TODO: 1. extract common part of ElephantEngine and Grain128AEADEngine to refactor AEADBufferBaseEngine. 2. finishAAD is set to abstract method to be overrided by child classes. 3. merge AEADBufferBaseEngine and AEADBaseEngine. 4. Derive AADOperator enum and DataOperator enum, and or some other ways to improve it. --- .../bouncycastle/crypto/engines/AEADBufferBaseEngine.java | 2 -- .../org/bouncycastle/crypto/engines/Grain128AEADEngine.java | 6 ++---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 924d677c2c..5ad3f15112 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -209,7 +209,6 @@ public void processAADBytes(byte[] input, int inOff, int len) len -= available; processBufferAAD(m_aad, 0); - } while (processor.isLengthExceedingBlockSize(len, AADBufferSize)) { @@ -423,7 +422,6 @@ public int getOutputSize(int len) { case DecInit: case DecAad: -// return Math.max(0, total + m_bufPos- MAC_SIZE); case DecData: case DecFinal: return Math.max(0, total + m_bufPos - MAC_SIZE); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java index f15056de89..c2c52f0abb 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java @@ -5,6 +5,7 @@ import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.OutputLengthException; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; /** @@ -344,10 +345,7 @@ private void doProcessAADBytes(byte[] input, int len) tmp >>>= 8; } } - for (int i = 0; i < len; ++i) - { - ader[1 + aderlen + i] = input[i]; - } + System.arraycopy(input, 0, ader, 1 + aderlen, len); for (int i = 0; i < ader.length; ++i) { From 7b8748920ab04f12f097b6ce0ba024115c74dc08 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 22 Jan 2025 12:15:01 +0100 Subject: [PATCH 035/890] Fix checkstyle complaining about non-static inner classes --- .../org/bouncycastle/openpgp/test/OperatorJcajceTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/OperatorJcajceTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/OperatorJcajceTest.java index 2fadb77a55..210df8bf84 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/OperatorJcajceTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/OperatorJcajceTest.java @@ -507,7 +507,7 @@ public void testJcaAEADSecretKeyEncryptorBuilder() } } - private class NullProvider + private static final class NullProvider extends Provider { NullProvider() @@ -516,7 +516,7 @@ private class NullProvider } } - private class NonDashProvider + private static final class NonDashProvider extends Provider { NonDashProvider() @@ -527,7 +527,7 @@ private class NonDashProvider } } - private class DashProvider + private static final class DashProvider extends Provider { DashProvider() From ee168797f09c0ebac3ca84fd16dfc3ad5dfddf34 Mon Sep 17 00:00:00 2001 From: royb Date: Wed, 22 Jan 2025 14:34:58 -0500 Subject: [PATCH 036/890] Fixed MLS test: converted encoded P521 private keys to match required length --- .../org/bouncycastle/mls/test/ClientVectorTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mls/src/test/java/org/bouncycastle/mls/test/ClientVectorTest.java b/mls/src/test/java/org/bouncycastle/mls/test/ClientVectorTest.java index 4b96ccc867..71c7146784 100644 --- a/mls/src/test/java/org/bouncycastle/mls/test/ClientVectorTest.java +++ b/mls/src/test/java/org/bouncycastle/mls/test/ClientVectorTest.java @@ -3,6 +3,7 @@ import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; +import java.math.BigInteger; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -11,6 +12,7 @@ import junit.framework.TestCase; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.mls.TreeKEM.TreeKEMPublicKey; import org.bouncycastle.mls.codec.MLSInputStream; import org.bouncycastle.mls.codec.MLSMessage; @@ -20,6 +22,7 @@ import org.bouncycastle.mls.protocol.Group; import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.BigIntegers; import org.bouncycastle.util.encoders.Hex; public class ClientVectorTest @@ -134,6 +137,14 @@ public Epoch(List proposals, byte[] commit, byte[] epoch_authenticator) } MlsCipherSuite suite = MlsCipherSuite.getSuite(cipherSuite); + + if(cipherSuite == MlsCipherSuite.MLS_256_DHKEMP521_AES256GCM_SHA512_P521) + { + //Converts encoded HPKE private key for P521 to comply with length constraints + encryption_priv = BigIntegers.asUnsignedByteArray(66, new BigInteger(1, encryption_priv)); + init_priv = BigIntegers.asUnsignedByteArray(66, new BigInteger(1, init_priv)); + } + AsymmetricCipherKeyPair leafKeyPair = suite.getHPKE().deserializePrivateKey(encryption_priv, null); Map externalPsks = new HashMap(); for (PreSharedKeyID ext : externalPSKs) From d26c8c3dd742812422850fb6868bea4cdbc96cb4 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 23 Jan 2025 16:36:05 +1030 Subject: [PATCH 037/890] Introduce AADOperator and related classes. --- .../crypto/engines/AEADBufferBaseEngine.java | 234 ++++++++++++++++-- .../crypto/engines/AsconBaseEngine.java | 15 +- .../crypto/engines/ElephantEngine.java | 36 +-- .../crypto/engines/GiftCofbEngine.java | 65 ++--- .../crypto/engines/ISAPEngine.java | 3 + .../crypto/engines/PhotonBeetleEngine.java | 67 +++-- .../crypto/engines/RomulusEngine.java | 37 +-- .../crypto/engines/SparkleEngine.java | 1 + .../crypto/engines/XoodyakEngine.java | 1 + .../bouncycastle/crypto/test/CipherTest.java | 9 +- .../bouncycastle/crypto/test/RomulusTest.java | 5 - 11 files changed, 300 insertions(+), 173 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 5ad3f15112..a617e1fa99 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -1,5 +1,7 @@ package org.bouncycastle.crypto.engines; +import java.io.ByteArrayOutputStream; + import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.OutputLengthException; @@ -16,6 +18,13 @@ protected enum ProcessingBufferType ImmediateLargeMac, } + protected enum AADOperatorType + { + Default, + Counter, + Stream + } + protected enum State { Uninitialized, @@ -38,6 +47,7 @@ protected enum State protected State m_state = State.Uninitialized; protected int m_bufferSizeDecrypt; protected AADProcessingBuffer processor; + protected AADOperator aadOperator; protected AEADBufferBaseEngine(ProcessingBufferType type) { @@ -58,7 +68,39 @@ protected AEADBufferBaseEngine(ProcessingBufferType type) } } - private interface AADProcessingBuffer + protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOperatorType) + { +// switch (type) +// { +// case Buffered: +// processor = new BufferedAADProcessor(); +// break; +// case BufferedLargeMac: +// processor = new BufferedLargeMacAADProcessor(); +// break; +// case Immediate: +// processor = new ImmediateAADProcessor(); +// break; +// case ImmediateLargeMac: +// processor = new ImmediateLargeMacAADProcessor(); +// break; +// } + + switch (aadOperatorType) + { + case Default: + aadOperator = new DefaultAADOperator(); + break; + case Counter: + aadOperator = new CounterAADOperator(); + break; + case Stream: + aadOperator = new StreamAADOperator(); + break; + } + } + + protected interface AADProcessingBuffer { void processAADByte(byte input); @@ -176,11 +218,173 @@ public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, } } + protected interface AADOperator + { + void processAADByte(byte input); + + void processAADBytes(byte[] input, int inOff, int len); + + void reset(); + + int getAadLen(); + } + + protected class DefaultAADOperator + implements AADOperator + { + @Override + public void processAADByte(byte input) + { + processor.processAADByte(input); + } + + @Override + public void processAADBytes(byte[] input, int inOff, int len) + { + if (m_aadPos > 0) + { + int available = AADBufferSize - m_aadPos; + if (processor.isLengthWithinAvailableSpace(len, available)) + { + System.arraycopy(input, inOff, m_aad, m_aadPos, len); + m_aadPos += len; + return; + } + + System.arraycopy(input, inOff, m_aad, m_aadPos, available); + inOff += available; + len -= available; + + processBufferAAD(m_aad, 0); + } + while (processor.isLengthExceedingBlockSize(len, AADBufferSize)) + { + processBufferAAD(input, inOff); + inOff += AADBufferSize; + len -= AADBufferSize; + } + System.arraycopy(input, inOff, m_aad, 0, len); + m_aadPos = len; + } + + public void reset() + { + + } + + @Override + public int getAadLen() + { + return m_aadPos; + } + } + + protected class CounterAADOperator + implements AADOperator + { + private int aadLen; + + @Override + public void processAADByte(byte input) + { + aadLen++; + processor.processAADByte(input); + } + + @Override + public void processAADBytes(byte[] input, int inOff, int len) + { + aadLen += len; + if (m_aadPos > 0) + { + int available = AADBufferSize - m_aadPos; + if (processor.isLengthWithinAvailableSpace(len, available)) + { + System.arraycopy(input, inOff, m_aad, m_aadPos, len); + m_aadPos += len; + return; + } + + System.arraycopy(input, inOff, m_aad, m_aadPos, available); + inOff += available; + len -= available; + + processBufferAAD(m_aad, 0); + } + while (processor.isLengthExceedingBlockSize(len, AADBufferSize)) + { + processBufferAAD(input, inOff); + inOff += AADBufferSize; + len -= AADBufferSize; + } + System.arraycopy(input, inOff, m_aad, 0, len); + m_aadPos = len; + } + + public int getAadLen() + { + return aadLen; + } + + public void reset() + { + aadLen = 0; + } + } + + protected class StreamAADOperator + implements AADOperator + { + private ErasableOutputStream stream = new ErasableOutputStream(); + + @Override + public void processAADByte(byte input) + { + stream.write(input); + } + + @Override + public void processAADBytes(byte[] input, int inOff, int len) + { + stream.write(input, inOff, len); + } + + public byte[] getBytes() + { + return stream.getBuf(); + } + + @Override + public void reset() + { + stream.reset(); + } + + @Override + public int getAadLen() + { + return stream.size(); + } + } + + protected static final class ErasableOutputStream + extends ByteArrayOutputStream + { + public ErasableOutputStream() + { + } + + public byte[] getBuf() + { + return buf; + } + } + @Override public void processAADByte(byte input) { checkAAD(); - processor.processAADByte(input); + aadOperator.processAADByte(input); } @Override @@ -194,30 +398,7 @@ public void processAADBytes(byte[] input, int inOff, int len) } checkAAD(); - if (m_aadPos > 0) - { - int available = AADBufferSize - m_aadPos; - if (processor.isLengthWithinAvailableSpace(len, available)) - { - System.arraycopy(input, inOff, m_aad, m_aadPos, len); - m_aadPos += len; - return; - } - - System.arraycopy(input, inOff, m_aad, m_aadPos, available); - inOff += available; - len -= available; - - processBufferAAD(m_aad, 0); - } - while (processor.isLengthExceedingBlockSize(len, AADBufferSize)) - { - processBufferAAD(input, inOff); - inOff += AADBufferSize; - len -= AADBufferSize; - } - System.arraycopy(input, inOff, m_aad, 0, len); - m_aadPos = len; + aadOperator.processAADBytes(input, inOff, len); } @Override @@ -520,6 +701,7 @@ protected void bufferReset() default: throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); } + aadOperator.reset(); } protected void ensureSufficientOutputBuffer(byte[] output, int outOff, int len) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index 377d8c7fd0..b8153486f7 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -27,6 +27,7 @@ abstract class AsconBaseEngine protected AsconBaseEngine(ProcessingBufferType type) { super(type); + setInnerMembers(type, AADOperatorType.Default); } private void round(long C) @@ -92,6 +93,7 @@ protected void finishAAD(State nextState, boolean isDofinal) protected abstract void processFinalEncrypt(byte[] input, int inLen, byte[] output, int outOff); + protected void processBufferAAD(byte[] buffer, int inOff) { x0 ^= loadBytes(buffer, inOff); @@ -102,6 +104,12 @@ protected void processBufferAAD(byte[] buffer, int inOff) p(nr); } + protected void processFinalAAD() + { + processFinalAadBlock(); + p(nr); + } + @Override protected void processFinalBlock(byte[] output, int outOff) { @@ -118,13 +126,6 @@ protected void processFinalBlock(byte[] output, int outOff) setBytes(x4, mac, 8); } - @Override - protected void processFinalAAD() - { - processFinalAadBlock(); - p(nr); - } - protected void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { long t0 = loadBytes(buffer, bufOff); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index a2a57d8044..e33c41e889 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -70,6 +70,7 @@ public ElephantEngine(ElephantParameters parameters) buffer = new byte[BlockSize]; m_buf = new byte[BlockSize + MAC_SIZE]; previous_outputMessage = new byte[BlockSize]; + setInnerMembers(ProcessingBufferType.Immediate, AADOperatorType.Stream); reset(false); } @@ -310,19 +311,6 @@ protected void init(byte[] k, byte[] iv) reset(false); } - @Override - public void processAADByte(byte input) - { - aadData.write(input); - } - - @Override - public void processAADBytes(byte[] input, int inOff, int len) - { - ensureSufficientInputBuffer(input, inOff, len); - aadData.write(input, inOff, len); - } - protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { processBuffer(input, inOff, output, outOff, State.EncData); @@ -333,7 +321,7 @@ private void processBuffer(byte[] input, int inOff, byte[] output, int outOff, S { if (m_state == State.DecInit || m_state == State.EncInit) { - processAADBytes(); + processFinalAAD(); } // Compute mask for the next message lfsr_step(); @@ -410,7 +398,7 @@ protected void processFinalBlock(byte[] output, int outOff) { int len = m_bufPos; int mlen = len + messageLen; - processAADBytes(); + processFinalAAD(); int nblocks_c = 1 + mlen / BlockSize; int nblocks_m = (mlen % BlockSize) != 0 ? nblocks_c : nblocks_c - 1; int nblocks_ad = 1 + (IV_SIZE + adlen) / BlockSize; @@ -426,13 +414,6 @@ protected void processFinalBlock(byte[] output, int outOff) @Override protected void processBufferAAD(byte[] input, int inOff) { - - } - - @Override - protected void processFinalAAD() - { - } @Override @@ -481,14 +462,16 @@ public int getOutputSize(int len) return Math.max(0, len + m_bufPos - MAC_SIZE); } - private void processAADBytes() + + @Override + protected void processFinalAAD() { if (adOff == -1) { - ad = aadData.toByteArray(); + ad = ((StreamAADOperator)aadOperator).getBytes(); adOff = 0; - adlen = ad.length; - aadData.reset(); + adlen = aadOperator.getAadLen(); + aadOperator.reset(); } switch (m_state) { @@ -501,7 +484,6 @@ private void processAADBytes() protected void reset(boolean clearMac) { - aadData.reset(); Arrays.fill(tag_buffer, (byte)0); Arrays.fill(previous_outputMessage, (byte)0); m_bufPos = 0; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java index 66dd3a7661..1fac1765d7 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java @@ -16,7 +16,6 @@ public class GiftCofbEngine private byte[] Y; private byte[] input; private byte[] offset; - private int aadLen; private int messageLen; /*Round constants*/ private final byte[] GIFT_RC = { @@ -34,20 +33,7 @@ public GiftCofbEngine() m_bufferSizeDecrypt = BlockSize + MAC_SIZE; m_buf = new byte[m_bufferSizeDecrypt]; m_aad = new byte[AADBufferSize]; - } - - @Override - public void processAADByte(byte input) - { - aadLen++; - super.processAADByte(input); - } - - @Override - public void processAADBytes(byte[] input, int inOff, int len) - { - aadLen += len; - super.processAADBytes(input, inOff, len); + setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Counter); } @Override @@ -229,7 +215,6 @@ private void phoprime(byte[] Y, byte[] C, int cOff, byte[] X, byte[] M, int mOff @Override protected void processBufferAAD(byte[] in, int inOff) { - pho1(input, Y, in, inOff, 16); /* offset = 2*offset */ double_half_block(offset, offset); @@ -238,29 +223,6 @@ protected void processBufferAAD(byte[] in, int inOff) giftb128(input, k, Y); } - //@Override - protected void finishAAD(State nextState, boolean isDoFinal) - { - // State indicates whether we ever received AAD - switch (m_state) - { - case DecInit: - case DecAad: - if (!isDoFinal && messageLen <= MAC_SIZE) - { - //m_state = State.DecData; - return; - } - case EncInit: - case EncAad: - processFinalAAD(); - break; - } - - m_aadPos = 0; - m_state = nextState; - } - @Override protected void processFinalAAD() { @@ -269,6 +231,7 @@ protected void processFinalAAD() /* full byte[]: offset = 3*offset */ /* partial byte[]: offset = 3^2*offset */ triple_half_block(offset, offset); + int aadLen = aadOperator.getAadLen(); if (((aadLen & 15) != 0) || m_state == State.DecInit || m_state == State.EncInit) { triple_half_block(offset, offset); @@ -286,6 +249,29 @@ protected void processFinalAAD() giftb128(input, k, Y); } + @Override + protected void finishAAD(State nextState, boolean isDoFinal) + { + // State indicates whether we ever received AAD + switch (m_state) + { + case DecInit: + case DecAad: + if (!isDoFinal && messageLen <= MAC_SIZE) + { + //m_state = State.DecData; + return; + } + case EncInit: + case EncAad: + processFinalAAD(); + break; + } + + m_aadPos = 0; + m_state = nextState; + } + @Override protected void init(byte[] key, byte[] iv) { @@ -369,7 +355,6 @@ protected void reset(boolean clearMac) System.arraycopy(npub, 0, input, 0, IV_SIZE); giftb128(input, k, Y); System.arraycopy(Y, 0, offset, 0, 8); - aadLen = 0; messageLen = 0; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index e0c86373c0..72d677e1e6 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -51,6 +51,8 @@ public ISAPEngine(IsapType isapType) m_bufferSizeDecrypt = BlockSize + MAC_SIZE; AADBufferSize = BlockSize; m_aad = new byte[AADBufferSize]; + setInnerMembers(isapType == IsapType.ISAP_K_128A || isapType == IsapType.ISAP_K_128 ? ProcessingBufferType.Immediate : + ProcessingBufferType.ImmediateLargeMac, AADOperatorType.Default); } final int ISAP_STATE_SZ = 40; @@ -800,6 +802,7 @@ protected void init(byte[] key, byte[] iv) reset(); } + protected void processBufferAAD(byte[] input, int inOff) { ISAPAEAD.absorbMacBlock(input, inOff); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 7cf94b0c1d..a084690137 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -24,7 +24,6 @@ public enum PhotonBeetleParameters private byte[] N; private byte[] state; private byte[][] state_2d; - private int aadLen; private int messageLen; private final int RATE_INBYTES_HALF; private final int STATE_INBYTES; @@ -81,6 +80,8 @@ public PhotonBeetleEngine(PhotonBeetleParameters pbp) m_bufferSizeDecrypt = BlockSize + MAC_SIZE; m_buf = new byte[m_bufferSizeDecrypt]; m_aad = new byte[AADBufferSize]; + setInnerMembers(pbp == PhotonBeetleParameters.pb128 ? ProcessingBufferType.Buffered : ProcessingBufferType.BufferedLargeMac, + AADOperatorType.Counter); } @Override @@ -96,12 +97,37 @@ protected void init(byte[] key, byte[] iv) reset(false); } + protected void processBufferAAD(byte[] input, int inOff) { PHOTON_Permutation(); XOR(input, inOff, BlockSize); } + public void processFinalAAD() + { + if (!aadFinished) + { + int aadLen = aadOperator.getAadLen(); + if (aadLen != 0) + { + if (m_aadPos != 0) + { + PHOTON_Permutation(); + XOR(m_aad, 0, m_aadPos); + if (m_aadPos < BlockSize) + { + state[m_aadPos] ^= 0x01; // ozs + } + } + state[STATE_INBYTES - 1] ^= select(messageLen - (forEncryption ? 0 : MAC_SIZE) > 0, + ((aadLen % BlockSize) == 0), (byte)3, (byte)4) << LAST_THREE_BITS_OFFSET; + } + m_aadPos = 0; + aadFinished = true; + } + } + protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { PHOTON_Permutation(); @@ -116,20 +142,6 @@ protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int XOR(output, outOff, BlockSize); } - @Override - public void processAADByte(byte input) - { - aadLen++; - super.processAADByte(input); - } - - @Override - public void processAADBytes(byte[] input, int inOff, int len) - { - aadLen += len; - super.processAADBytes(input, inOff, len); - } - @Override public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) throws DataLengthException @@ -143,6 +155,7 @@ protected void processFinalBlock(byte[] output, int outOff) { int len = messageLen - (forEncryption ? 0 : MAC_SIZE); int bufferLen = m_bufPos;// - (forEncryption ? 0 : MAC_SIZE); + int aadLen = aadOperator.getAadLen(); if (aadLen != 0 || len != 0) { input_empty = false; @@ -179,35 +192,11 @@ protected void processFinalBlock(byte[] output, int outOff) System.arraycopy(state, 0, mac, 0, MAC_SIZE); } - protected void processFinalAAD() - { - if (!aadFinished) - { - if (aadLen != 0) - { - if (m_aadPos != 0) - { - PHOTON_Permutation(); - XOR(m_aad, 0, m_aadPos); - if (m_aadPos < BlockSize) - { - state[m_aadPos] ^= 0x01; // ozs - } - } - state[STATE_INBYTES - 1] ^= select(messageLen - (forEncryption ? 0 : MAC_SIZE) > 0, - ((aadLen % BlockSize) == 0), (byte)3, (byte)4) << LAST_THREE_BITS_OFFSET; - } - m_aadPos = 0; - aadFinished = true; - } - } - protected void reset(boolean clearMac) { ensureInitialized(); bufferReset(); input_empty = true; - aadLen = 0; aadFinished = false; messageLen = 0; System.arraycopy(K, 0, state, 0, K.length); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index c6084c4cb5..8123718ec5 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -103,6 +103,7 @@ public RomulusEngine(RomulusParameters romulusParameters) m_bufferSizeDecrypt = MAC_SIZE + BlockSize; m_buf = new byte[m_bufferSizeDecrypt]; m_aad = new byte[AADBufferSize]; + setInnerMembers(romulusParameters == RomulusParameters.RomulusT ? ProcessingBufferType.Immediate : ProcessingBufferType.Buffered, AADOperatorType.Counter); } private interface Instance @@ -372,9 +373,8 @@ public void processFinalAAD() } lfsr_gf56(CNT); } - if (aadLen == 0) + if (aadOperator.getAadLen() == 0) { - lfsr_gf56(CNT); nonce_encryption(npub, CNT, s, k, (byte)0x1a); } @@ -485,7 +485,7 @@ else if (m_aadPos != 0) { Arrays.fill(m_aad, BlockSize, AADBufferSize, (byte)0); } - else if (aadLen != 0) + else if (aadOperator.getAadLen() != 0) { System.arraycopy(npub, 0, m_aad, m_aadPos, 16); n = 0; @@ -540,7 +540,7 @@ public void processFinalAAD() hirose_128_128_256(h, g, m_aad, 0); m_aadPos = 0; } - else if ((m_aadPos >= 0) && (aadLen != 0)) + else if ((m_aadPos >= 0) && (aadOperator.getAadLen() != 0)) { m_aad[BlockSize - 1] = (byte)(m_aadPos & 0xf); m_aadPos = BlockSize; @@ -883,20 +883,6 @@ public void init(byte[] key, byte[] iv) reset(false); } - @Override - public void processAADByte(byte in) - { - aadLen++; - super.processAADByte(in); - } - - @Override - public void processAADBytes(byte[] in, int inOff, int len) - { - aadLen += len; - super.processAADBytes(in, inOff, len); - } - @Override public int processBytes(byte[] input, int inOff, int len, byte[] out, int outOff) throws DataLengthException @@ -924,24 +910,22 @@ protected void finishAAD(State nextState, boolean isDoFinal) m_state = nextState; } - @Override - protected void processFinalBlock(byte[] output, int outOff) - { - instance.processFinalBlock(output, outOff); - } - - @Override protected void processBufferAAD(byte[] input, int inOff) { instance.processBufferAAD(input, inOff); } - @Override protected void processFinalAAD() { instance.processFinalAAD(); } + @Override + protected void processFinalBlock(byte[] output, int outOff) + { + instance.processFinalBlock(output, outOff); + } + @Override protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { @@ -956,7 +940,6 @@ protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int protected void reset(boolean clearMac) { - aadLen = 0; messegeLen = 0; bufferReset(); instance.reset(); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java index 20f5f73166..77077b1e3b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java @@ -113,6 +113,7 @@ public SparkleEngine(SparkleParameters sparkleParameters) m_bufferSizeDecrypt = IV_SIZE + MAC_SIZE; m_buf = new byte[m_bufferSizeDecrypt]; m_aad = new byte[BlockSize]; + setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Default); // Relied on by processBytes method for decryption // assert RATE_BYTES >= TAG_BYTES; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index 2b64c363b1..f2e1c74113 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -45,6 +45,7 @@ public XoodyakEngine() m_bufferSizeDecrypt = BlockSize + MAC_SIZE; m_buf = new byte[m_bufferSizeDecrypt]; m_aad = new byte[AADBufferSize]; + setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Default); } @Override diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java index 41ff9680ed..1a50267aeb 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java @@ -339,6 +339,11 @@ static void checkAEADParemeter(SimpleTest test, int keySize, int ivSize, final i byte[] ciphertext2 = new byte[cipher.getOutputSize(plaintext.length)]; len = cipher.processBytes(plaintext, 0, plaintext.length, ciphertext2, 0); len += cipher.doFinal(ciphertext2, len); + cipher.init(true, new ParametersWithIV(new KeyParameter(key), iv)); + byte[] ciphertext3 = new byte[cipher.getOutputSize(plaintext.length)]; + cipher.processAADBytes(aad, 0, aad.length); + len = cipher.processBytes(plaintext, 0, plaintext.length, ciphertext3, 0); + len += cipher.doFinal(ciphertext3, len); test.isTrue("cipher text check", Arrays.areEqual(ciphertext1, ciphertext2)); test.testException("Invalid value for MAC size: ", "IllegalArgumentException", new TestExceptionOperation() @@ -482,7 +487,7 @@ static void implTestVectorsEngine(AEADCipher cipher, String path, String filenam mismatch("Reccover Keystream " + map.get("Count"), (String)map.get("PT"), rv, test); } } - System.out.println("pass "+ count); + System.out.println("pass " + count); map.clear(); } else @@ -845,7 +850,7 @@ static void implTestExceptionsEngine(int keysize, int ivsize, SimpleTest test, I } static void implTestExceptionsGetUpdateOutputSize(AEADCipher cipher, boolean forEncryption, - CipherParameters parameters, int maxInputSize, SimpleTest test) + CipherParameters parameters, int maxInputSize, SimpleTest test) { cipher.init(forEncryption, parameters); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java index 2b987520de..6f92bbaf47 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java @@ -4,18 +4,13 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; -import java.util.Random; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.Digest; -import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.digests.RomulusDigest; -import org.bouncycastle.crypto.engines.GiftCofbEngine; import org.bouncycastle.crypto.engines.RomulusEngine; -import org.bouncycastle.crypto.modes.AEADBlockCipher; import org.bouncycastle.crypto.modes.AEADCipher; -import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.util.encoders.Hex; From 02deb69f6497c5ab0b21a3c6726e8e7ddfd9cf03 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 23 Jan 2025 17:08:36 +1030 Subject: [PATCH 038/890] Introduce DataOperator and related classes. --- .../crypto/engines/AEADBufferBaseEngine.java | 234 ++++++++++++++---- .../crypto/engines/AsconBaseEngine.java | 2 +- .../crypto/engines/ElephantEngine.java | 4 +- .../crypto/engines/GiftCofbEngine.java | 22 +- .../crypto/engines/ISAPEngine.java | 2 +- .../crypto/engines/PhotonBeetleEngine.java | 6 +- .../crypto/engines/RomulusEngine.java | 26 +- .../crypto/engines/SparkleEngine.java | 2 +- .../crypto/engines/XoodyakEngine.java | 2 +- 9 files changed, 201 insertions(+), 99 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index a617e1fa99..40f9ae8b3f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -25,6 +25,13 @@ protected enum AADOperatorType Stream } + protected enum DataOperatorType + { + Default, + Counter, + Stream + } + protected enum State { Uninitialized, @@ -48,6 +55,7 @@ protected enum State protected int m_bufferSizeDecrypt; protected AADProcessingBuffer processor; protected AADOperator aadOperator; + protected DataOperator dataOperator; protected AEADBufferBaseEngine(ProcessingBufferType type) { @@ -100,6 +108,51 @@ protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOpe } } + protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOperatorType, DataOperatorType dataOperatorType) + { +// switch (type) +// { +// case Buffered: +// processor = new BufferedAADProcessor(); +// break; +// case BufferedLargeMac: +// processor = new BufferedLargeMacAADProcessor(); +// break; +// case Immediate: +// processor = new ImmediateAADProcessor(); +// break; +// case ImmediateLargeMac: +// processor = new ImmediateLargeMacAADProcessor(); +// break; +// } + + switch (aadOperatorType) + { + case Default: + aadOperator = new DefaultAADOperator(); + break; + case Counter: + aadOperator = new CounterAADOperator(); + break; + case Stream: + aadOperator = new StreamAADOperator(); + break; + } + + switch (dataOperatorType) + { + case Default: + dataOperator = new DefaultDataOperator(); + break; + case Counter: + dataOperator = new CounterDataOperator(); + break; + case Stream: + dataOperator = new StreamDataOperator(); + break; + } + } + protected interface AADProcessingBuffer { void processAADByte(byte input); @@ -226,7 +279,7 @@ protected interface AADOperator void reset(); - int getAadLen(); + int getLen(); } protected class DefaultAADOperator @@ -241,39 +294,15 @@ public void processAADByte(byte input) @Override public void processAADBytes(byte[] input, int inOff, int len) { - if (m_aadPos > 0) - { - int available = AADBufferSize - m_aadPos; - if (processor.isLengthWithinAvailableSpace(len, available)) - { - System.arraycopy(input, inOff, m_aad, m_aadPos, len); - m_aadPos += len; - return; - } - - System.arraycopy(input, inOff, m_aad, m_aadPos, available); - inOff += available; - len -= available; - - processBufferAAD(m_aad, 0); - } - while (processor.isLengthExceedingBlockSize(len, AADBufferSize)) - { - processBufferAAD(input, inOff); - inOff += AADBufferSize; - len -= AADBufferSize; - } - System.arraycopy(input, inOff, m_aad, 0, len); - m_aadPos = len; + processAadBytes(input, inOff, len); } public void reset() { - } @Override - public int getAadLen() + public int getLen() { return m_aadPos; } @@ -295,33 +324,10 @@ public void processAADByte(byte input) public void processAADBytes(byte[] input, int inOff, int len) { aadLen += len; - if (m_aadPos > 0) - { - int available = AADBufferSize - m_aadPos; - if (processor.isLengthWithinAvailableSpace(len, available)) - { - System.arraycopy(input, inOff, m_aad, m_aadPos, len); - m_aadPos += len; - return; - } - - System.arraycopy(input, inOff, m_aad, m_aadPos, available); - inOff += available; - len -= available; - - processBufferAAD(m_aad, 0); - } - while (processor.isLengthExceedingBlockSize(len, AADBufferSize)) - { - processBufferAAD(input, inOff); - inOff += AADBufferSize; - len -= AADBufferSize; - } - System.arraycopy(input, inOff, m_aad, 0, len); - m_aadPos = len; + processAadBytes(input, inOff, len); } - public int getAadLen() + public int getLen() { return aadLen; } @@ -332,10 +338,10 @@ public void reset() } } - protected class StreamAADOperator + protected static class StreamAADOperator implements AADOperator { - private ErasableOutputStream stream = new ErasableOutputStream(); + private final ErasableOutputStream stream = new ErasableOutputStream(); @Override public void processAADByte(byte input) @@ -361,12 +367,96 @@ public void reset() } @Override - public int getAadLen() + public int getLen() { return stream.size(); } } + protected interface DataOperator + { + int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff); + + int getLen(); + + void reset(); + } + + protected class DefaultDataOperator + implements DataOperator + { + public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + return processEncDecBytes(input, inOff, len, output, outOff); + } + + @Override + public int getLen() + { + return m_bufPos; + } + + @Override + public void reset() + { + + } + } + + protected class CounterDataOperator + implements DataOperator + { + private int messegeLen; + + public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + messegeLen += len; + return processEncDecBytes(input, inOff, len, output, outOff); + } + + @Override + public int getLen() + { + return messegeLen; + } + + @Override + public void reset() + { + messegeLen = 0; + } + } + + protected class StreamDataOperator + implements DataOperator + { + private ErasableOutputStream stream = new ErasableOutputStream(); + + @Override + public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + stream.write(input, inOff, len); + return 0; + } + + public byte[] getBytes() + { + return stream.getBuf(); + } + + @Override + public int getLen() + { + return stream.size(); + } + + @Override + public void reset() + { + stream.reset(); + } + } + protected static final class ErasableOutputStream extends ByteArrayOutputStream { @@ -401,11 +491,44 @@ public void processAADBytes(byte[] input, int inOff, int len) aadOperator.processAADBytes(input, inOff, len); } + private void processAadBytes(byte[] input, int inOff, int len) + { + if (m_aadPos > 0) + { + int available = AADBufferSize - m_aadPos; + if (processor.isLengthWithinAvailableSpace(len, available)) + { + System.arraycopy(input, inOff, m_aad, m_aadPos, len); + m_aadPos += len; + return; + } + + System.arraycopy(input, inOff, m_aad, m_aadPos, available); + inOff += available; + len -= available; + + processBufferAAD(m_aad, 0); + } + while (processor.isLengthExceedingBlockSize(len, AADBufferSize)) + { + processBufferAAD(input, inOff); + inOff += AADBufferSize; + len -= AADBufferSize; + } + System.arraycopy(input, inOff, m_aad, 0, len); + m_aadPos = len; + } + @Override public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) throws DataLengthException { ensureSufficientInputBuffer(input, inOff, len); + return dataOperator.processBytes(input, inOff, len, output, outOff); + } + + protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { int available, resultLength; if (checkData(false)) { @@ -702,6 +825,7 @@ protected void bufferReset() throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); } aadOperator.reset(); + dataOperator.reset(); } protected void ensureSufficientOutputBuffer(byte[] output, int outOff, int len) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index b8153486f7..a3d70bbfdf 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -27,7 +27,7 @@ abstract class AsconBaseEngine protected AsconBaseEngine(ProcessingBufferType type) { super(type); - setInnerMembers(type, AADOperatorType.Default); + setInnerMembers(type, AADOperatorType.Default, DataOperatorType.Default); } private void round(long C) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index e33c41e889..3d0a696b06 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -70,7 +70,7 @@ public ElephantEngine(ElephantParameters parameters) buffer = new byte[BlockSize]; m_buf = new byte[BlockSize + MAC_SIZE]; previous_outputMessage = new byte[BlockSize]; - setInnerMembers(ProcessingBufferType.Immediate, AADOperatorType.Stream); + setInnerMembers(ProcessingBufferType.Immediate, AADOperatorType.Stream, DataOperatorType.Default); reset(false); } @@ -470,7 +470,7 @@ protected void processFinalAAD() { ad = ((StreamAADOperator)aadOperator).getBytes(); adOff = 0; - adlen = aadOperator.getAadLen(); + adlen = aadOperator.getLen(); aadOperator.reset(); } switch (m_state) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java index 1fac1765d7..bf34230fd1 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java @@ -1,7 +1,5 @@ package org.bouncycastle.crypto.engines; -import org.bouncycastle.crypto.DataLengthException; - /** * GIFT-COFB v1.1, based on the current round 3 submission, https://www.isical.ac.in/~lightweight/COFB/ * Reference C implementation: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-submissions/elephant.zip @@ -16,7 +14,6 @@ public class GiftCofbEngine private byte[] Y; private byte[] input; private byte[] offset; - private int messageLen; /*Round constants*/ private final byte[] GIFT_RC = { (byte)0x01, (byte)0x03, (byte)0x07, (byte)0x0F, (byte)0x1F, (byte)0x3E, (byte)0x3D, (byte)0x3B, (byte)0x37, (byte)0x2F, @@ -33,15 +30,7 @@ public GiftCofbEngine() m_bufferSizeDecrypt = BlockSize + MAC_SIZE; m_buf = new byte[m_bufferSizeDecrypt]; m_aad = new byte[AADBufferSize]; - setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Counter); - } - - @Override - public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - throws DataLengthException - { - messageLen += len; - return super.processBytes(input, inOff, len, output, outOff); + setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Counter, DataOperatorType.Counter); } private int rowperm(int S, int B0_pos, int B1_pos, int B2_pos, int B3_pos) @@ -226,12 +215,12 @@ protected void processBufferAAD(byte[] in, int inOff) @Override protected void processFinalAAD() { - int len = messageLen - (forEncryption ? 0 : MAC_SIZE); + int len = dataOperator.getLen() - (forEncryption ? 0 : MAC_SIZE); /* last byte[] */ /* full byte[]: offset = 3*offset */ /* partial byte[]: offset = 3^2*offset */ triple_half_block(offset, offset); - int aadLen = aadOperator.getAadLen(); + int aadLen = aadOperator.getLen(); if (((aadLen & 15) != 0) || m_state == State.DecInit || m_state == State.EncInit) { triple_half_block(offset, offset); @@ -257,7 +246,7 @@ protected void finishAAD(State nextState, boolean isDoFinal) { case DecInit: case DecAad: - if (!isDoFinal && messageLen <= MAC_SIZE) + if (!isDoFinal && dataOperator.getLen() <= MAC_SIZE) { //m_state = State.DecData; return; @@ -289,7 +278,7 @@ protected void init(byte[] key, byte[] iv) protected void processFinalBlock(byte[] output, int outOff) { int inOff = 0; - int len = messageLen - (forEncryption ? 0 : MAC_SIZE); + int len = dataOperator.getLen() - (forEncryption ? 0 : MAC_SIZE); if (len != 0) { /* full block: offset = 3*offset */ @@ -355,6 +344,5 @@ protected void reset(boolean clearMac) System.arraycopy(npub, 0, input, 0, IV_SIZE); giftb128(input, k, Y); System.arraycopy(Y, 0, offset, 0, 8); - messageLen = 0; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index 72d677e1e6..04f55084d9 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -52,7 +52,7 @@ public ISAPEngine(IsapType isapType) AADBufferSize = BlockSize; m_aad = new byte[AADBufferSize]; setInnerMembers(isapType == IsapType.ISAP_K_128A || isapType == IsapType.ISAP_K_128 ? ProcessingBufferType.Immediate : - ProcessingBufferType.ImmediateLargeMac, AADOperatorType.Default); + ProcessingBufferType.ImmediateLargeMac, AADOperatorType.Default, DataOperatorType.Default); } final int ISAP_STATE_SZ = 40; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index a084690137..658117e96e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -81,7 +81,7 @@ public PhotonBeetleEngine(PhotonBeetleParameters pbp) m_buf = new byte[m_bufferSizeDecrypt]; m_aad = new byte[AADBufferSize]; setInnerMembers(pbp == PhotonBeetleParameters.pb128 ? ProcessingBufferType.Buffered : ProcessingBufferType.BufferedLargeMac, - AADOperatorType.Counter); + AADOperatorType.Counter, DataOperatorType.Default); } @Override @@ -108,7 +108,7 @@ public void processFinalAAD() { if (!aadFinished) { - int aadLen = aadOperator.getAadLen(); + int aadLen = aadOperator.getLen(); if (aadLen != 0) { if (m_aadPos != 0) @@ -155,7 +155,7 @@ protected void processFinalBlock(byte[] output, int outOff) { int len = messageLen - (forEncryption ? 0 : MAC_SIZE); int bufferLen = m_bufPos;// - (forEncryption ? 0 : MAC_SIZE); - int aadLen = aadOperator.getAadLen(); + int aadLen = aadOperator.getLen(); if (aadLen != 0 || len != 0) { input_empty = false; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index 8123718ec5..ff292f8e63 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -1,6 +1,5 @@ package org.bouncycastle.crypto.engines; -import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.util.Arrays; @@ -23,8 +22,6 @@ public enum RomulusParameters private byte[] k; private byte[] npub; private final int AD_BLK_LEN_HALF = 16; - private int messegeLen; - private int aadLen; private Instance instance; private final byte[] CNT; @@ -103,7 +100,9 @@ public RomulusEngine(RomulusParameters romulusParameters) m_bufferSizeDecrypt = MAC_SIZE + BlockSize; m_buf = new byte[m_bufferSizeDecrypt]; m_aad = new byte[AADBufferSize]; - setInnerMembers(romulusParameters == RomulusParameters.RomulusT ? ProcessingBufferType.Immediate : ProcessingBufferType.Buffered, AADOperatorType.Counter); + setInnerMembers(romulusParameters == RomulusParameters.RomulusT ? ProcessingBufferType.Immediate : ProcessingBufferType.Buffered, + AADOperatorType.Counter, + romulusParameters == RomulusParameters.RomulusM ? DataOperatorType.Stream : DataOperatorType.Counter); } private interface Instance @@ -317,7 +316,7 @@ public RomulusN() @Override public void processFinalBlock(byte[] output, int outOff) { - messegeLen -= (forEncryption ? 0 : MAC_SIZE); + int messegeLen = dataOperator.getLen() - (forEncryption ? 0 : MAC_SIZE); if (messegeLen == 0) { lfsr_gf56(CNT); @@ -373,7 +372,7 @@ public void processFinalAAD() } lfsr_gf56(CNT); } - if (aadOperator.getAadLen() == 0) + if (aadOperator.getLen() == 0) { lfsr_gf56(CNT); nonce_encryption(npub, CNT, s, k, (byte)0x1a); @@ -442,7 +441,7 @@ private class RomulusT public void processFinalBlock(byte[] output, int outOff) { int n = 16; - messegeLen -= (forEncryption ? 0 : MAC_SIZE); + int messegeLen = dataOperator.getLen() - (forEncryption ? 0 : MAC_SIZE); if (m_bufPos != 0) { int len8 = Math.min(m_bufPos, 16); @@ -485,7 +484,7 @@ else if (m_aadPos != 0) { Arrays.fill(m_aad, BlockSize, AADBufferSize, (byte)0); } - else if (aadOperator.getAadLen() != 0) + else if (aadOperator.getLen() != 0) { System.arraycopy(npub, 0, m_aad, m_aadPos, 16); n = 0; @@ -540,7 +539,7 @@ public void processFinalAAD() hirose_128_128_256(h, g, m_aad, 0); m_aadPos = 0; } - else if ((m_aadPos >= 0) && (aadOperator.getAadLen() != 0)) + else if ((m_aadPos >= 0) && (aadOperator.getLen() != 0)) { m_aad[BlockSize - 1] = (byte)(m_aadPos & 0xf); m_aadPos = BlockSize; @@ -883,14 +882,6 @@ public void init(byte[] key, byte[] iv) reset(false); } - @Override - public int processBytes(byte[] input, int inOff, int len, byte[] out, int outOff) - throws DataLengthException - { - messegeLen += len; - return super.processBytes(input, inOff, len, out, outOff); - } - protected void finishAAD(State nextState, boolean isDoFinal) { // State indicates whether we ever received AAD @@ -940,7 +931,6 @@ protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int protected void reset(boolean clearMac) { - messegeLen = 0; bufferReset(); instance.reset(); super.reset(clearMac); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java index 77077b1e3b..26a65ab344 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java @@ -113,7 +113,7 @@ public SparkleEngine(SparkleParameters sparkleParameters) m_bufferSizeDecrypt = IV_SIZE + MAC_SIZE; m_buf = new byte[m_bufferSizeDecrypt]; m_aad = new byte[BlockSize]; - setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Default); + setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Default, DataOperatorType.Default); // Relied on by processBytes method for decryption // assert RATE_BYTES >= TAG_BYTES; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index f2e1c74113..3818a78b9f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -45,7 +45,7 @@ public XoodyakEngine() m_bufferSizeDecrypt = BlockSize + MAC_SIZE; m_buf = new byte[m_bufferSizeDecrypt]; m_aad = new byte[AADBufferSize]; - setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Default); + setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Default, DataOperatorType.Default); } @Override From e3979e5d2f0e7e957e98ebdb71909b0deb1c8bd6 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 23 Jan 2025 17:32:22 +1030 Subject: [PATCH 039/890] Refactor on setInnerMembers and constructor of AEADBufferBaseEngine --- .../crypto/engines/AEADBufferBaseEngine.java | 71 +++++-------------- .../crypto/engines/AsconAEAD128.java | 2 +- .../crypto/engines/AsconBaseEngine.java | 6 -- .../crypto/engines/AsconEngine.java | 2 +- .../crypto/engines/ElephantEngine.java | 16 +---- .../crypto/engines/GiftCofbEngine.java | 4 -- .../crypto/engines/ISAPEngine.java | 4 -- .../crypto/engines/PhotonBeetleEngine.java | 4 -- .../crypto/engines/RomulusEngine.java | 4 -- .../crypto/engines/SparkleEngine.java | 4 -- .../crypto/engines/XoodyakEngine.java | 4 -- .../crypto/test/ElephantTest.java | 39 ++++++---- 12 files changed, 46 insertions(+), 114 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 40f9ae8b3f..b6d392b072 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -57,7 +57,7 @@ protected enum State protected AADOperator aadOperator; protected DataOperator dataOperator; - protected AEADBufferBaseEngine(ProcessingBufferType type) + protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOperatorType, DataOperatorType dataOperatorType) { switch (type) { @@ -74,64 +74,17 @@ protected AEADBufferBaseEngine(ProcessingBufferType type) processor = new ImmediateLargeMacAADProcessor(); break; } - } - protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOperatorType) - { -// switch (type) -// { -// case Buffered: -// processor = new BufferedAADProcessor(); -// break; -// case BufferedLargeMac: -// processor = new BufferedLargeMacAADProcessor(); -// break; -// case Immediate: -// processor = new ImmediateAADProcessor(); -// break; -// case ImmediateLargeMac: -// processor = new ImmediateLargeMacAADProcessor(); -// break; -// } - - switch (aadOperatorType) - { - case Default: - aadOperator = new DefaultAADOperator(); - break; - case Counter: - aadOperator = new CounterAADOperator(); - break; - case Stream: - aadOperator = new StreamAADOperator(); - break; - } - } - - protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOperatorType, DataOperatorType dataOperatorType) - { -// switch (type) -// { -// case Buffered: -// processor = new BufferedAADProcessor(); -// break; -// case BufferedLargeMac: -// processor = new BufferedLargeMacAADProcessor(); -// break; -// case Immediate: -// processor = new ImmediateAADProcessor(); -// break; -// case ImmediateLargeMac: -// processor = new ImmediateLargeMacAADProcessor(); -// break; -// } + m_bufferSizeDecrypt = BlockSize + MAC_SIZE; switch (aadOperatorType) { case Default: + m_aad = new byte[AADBufferSize]; aadOperator = new DefaultAADOperator(); break; case Counter: + m_aad = new byte[AADBufferSize]; aadOperator = new CounterAADOperator(); break; case Stream: @@ -142,9 +95,11 @@ protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOpe switch (dataOperatorType) { case Default: + m_buf = new byte[m_bufferSizeDecrypt]; dataOperator = new DefaultDataOperator(); break; case Counter: + m_buf = new byte[m_bufferSizeDecrypt]; dataOperator = new CounterDataOperator(); break; case Stream: @@ -802,10 +757,16 @@ protected void finishAAD(State nextState, boolean isDoFinal) protected void bufferReset() { - Arrays.fill(m_buf, (byte)0); - Arrays.fill(m_aad, (byte)0); - m_bufPos = 0; - m_aadPos = 0; + if (m_buf != null) + { + Arrays.fill(m_buf, (byte)0); + m_bufPos = 0; + } + if (m_aad != null) + { + Arrays.fill(m_aad, (byte)0); + m_aadPos = 0; + } switch (m_state) { case DecInit: diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java index 0be8153be2..449805fde7 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java @@ -21,7 +21,6 @@ public class AsconAEAD128 { public AsconAEAD128() { - super(ProcessingBufferType.Immediate); KEY_SIZE = 16; IV_SIZE = 16; MAC_SIZE = 16; @@ -33,6 +32,7 @@ public AsconAEAD128() m_buf = new byte[m_bufferSizeDecrypt]; m_aad = new byte[BlockSize]; dsep = -9223372036854775808L; //0x80L << 56 + setInnerMembers(ProcessingBufferType.Immediate, AADOperatorType.Default, DataOperatorType.Default); } protected long pad(int i) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index a3d70bbfdf..a3ecd42dca 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -24,12 +24,6 @@ abstract class AsconBaseEngine protected abstract void setBytes(long n, byte[] bs, int off); - protected AsconBaseEngine(ProcessingBufferType type) - { - super(type); - setInnerMembers(type, AADOperatorType.Default, DataOperatorType.Default); - } - private void round(long C) { long t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java index 3d2b38e244..5ad2975040 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java @@ -35,7 +35,6 @@ public enum AsconParameters public AsconEngine(AsconParameters asconParameters) { - super(asconParameters == AsconParameters.ascon128a ? ProcessingBufferType.Immediate : ProcessingBufferType.ImmediateLargeMac); this.asconParameters = asconParameters; IV_SIZE = 16; MAC_SIZE = 16; @@ -68,6 +67,7 @@ public AsconEngine(AsconParameters asconParameters) AADBufferSize = BlockSize; m_aad = new byte[BlockSize]; dsep = 1L; + setInnerMembers(asconParameters == AsconParameters.ascon128a ? ProcessingBufferType.Immediate : ProcessingBufferType.ImmediateLargeMac, AADOperatorType.Default, DataOperatorType.Default); } protected long pad(int i) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index 3d0a696b06..9e0f578114 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -1,6 +1,5 @@ package org.bouncycastle.crypto.engines; -import java.io.ByteArrayOutputStream; import java.util.Arrays; /** @@ -30,13 +29,10 @@ public enum ElephantParameters private byte[] next_mask; private final byte[] buffer; private final byte[] previous_outputMessage; - private final ByteArrayOutputStream aadData = new ByteArrayOutputStream(); - private int messageLen; private final Permutation instance; public ElephantEngine(ElephantParameters parameters) { - super(ProcessingBufferType.Immediate); KEY_SIZE = 16; IV_SIZE = 12; switch (parameters) @@ -62,16 +58,13 @@ public ElephantEngine(ElephantParameters parameters) default: throw new IllegalArgumentException("Invalid parameter settings for Elephant"); } - m_bufferSizeDecrypt = BlockSize + MAC_SIZE; tag_buffer = new byte[BlockSize]; previous_mask = new byte[BlockSize]; current_mask = new byte[BlockSize]; next_mask = new byte[BlockSize]; buffer = new byte[BlockSize]; - m_buf = new byte[BlockSize + MAC_SIZE]; previous_outputMessage = new byte[BlockSize]; - setInnerMembers(ProcessingBufferType.Immediate, AADOperatorType.Stream, DataOperatorType.Default); - reset(false); + setInnerMembers(ProcessingBufferType.Immediate, AADOperatorType.Stream, DataOperatorType.Counter); } private interface Permutation @@ -344,7 +337,6 @@ private void processBuffer(byte[] input, int inOff, byte[] output, int outOff, S // Value of next_mask will be computed in the next iteration swapMasks(); nb_its++; - messageLen += BlockSize; } protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) @@ -396,8 +388,7 @@ private void absorbCiphertext() protected void processFinalBlock(byte[] output, int outOff) { - int len = m_bufPos; - int mlen = len + messageLen; + int mlen = dataOperator.getLen() - (forEncryption ? 0 : MAC_SIZE); processFinalAAD(); int nblocks_c = 1 + mlen / BlockSize; int nblocks_m = (mlen % BlockSize) != 0 ? nblocks_c : nblocks_c - 1; @@ -486,11 +477,10 @@ protected void reset(boolean clearMac) { Arrays.fill(tag_buffer, (byte)0); Arrays.fill(previous_outputMessage, (byte)0); - m_bufPos = 0; nb_its = 0; adOff = -1; - messageLen = 0; super.reset(clearMac); + bufferReset(); } protected void checkAAD() diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java index bf34230fd1..266aaee046 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java @@ -24,12 +24,8 @@ public class GiftCofbEngine public GiftCofbEngine() { - super(ProcessingBufferType.Buffered); AADBufferSize = BlockSize = MAC_SIZE = IV_SIZE = KEY_SIZE = 16; algorithmName = "GIFT-COFB AEAD"; - m_bufferSizeDecrypt = BlockSize + MAC_SIZE; - m_buf = new byte[m_bufferSizeDecrypt]; - m_aad = new byte[AADBufferSize]; setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Counter, DataOperatorType.Counter); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index 04f55084d9..78e9403820 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -24,8 +24,6 @@ public enum IsapType public ISAPEngine(IsapType isapType) { - super(isapType == IsapType.ISAP_K_128A || isapType == IsapType.ISAP_K_128 ? ProcessingBufferType.Immediate : - ProcessingBufferType.ImmediateLargeMac); KEY_SIZE = 16; IV_SIZE = 16; MAC_SIZE = 16; @@ -48,9 +46,7 @@ public ISAPEngine(IsapType isapType) algorithmName = "ISAP-K-128 AEAD"; break; } - m_bufferSizeDecrypt = BlockSize + MAC_SIZE; AADBufferSize = BlockSize; - m_aad = new byte[AADBufferSize]; setInnerMembers(isapType == IsapType.ISAP_K_128A || isapType == IsapType.ISAP_K_128 ? ProcessingBufferType.Immediate : ProcessingBufferType.ImmediateLargeMac, AADOperatorType.Default, DataOperatorType.Default); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 658117e96e..ea8d4b1576 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -55,7 +55,6 @@ public enum PhotonBeetleParameters public PhotonBeetleEngine(PhotonBeetleParameters pbp) { - super(pbp == PhotonBeetleParameters.pb128 ? ProcessingBufferType.Buffered : ProcessingBufferType.BufferedLargeMac); KEY_SIZE = 16; IV_SIZE = 16; MAC_SIZE = 16; @@ -77,9 +76,6 @@ public PhotonBeetleEngine(PhotonBeetleParameters pbp) STATE_INBYTES = (STATE_INBITS + 7) >>> 3; LAST_THREE_BITS_OFFSET = (STATE_INBITS - ((STATE_INBYTES - 1) << 3) - 3); algorithmName = "Photon-Beetle AEAD"; - m_bufferSizeDecrypt = BlockSize + MAC_SIZE; - m_buf = new byte[m_bufferSizeDecrypt]; - m_aad = new byte[AADBufferSize]; setInnerMembers(pbp == PhotonBeetleParameters.pb128 ? ProcessingBufferType.Buffered : ProcessingBufferType.BufferedLargeMac, AADOperatorType.Counter, DataOperatorType.Default); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index ff292f8e63..8e59dc160b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -78,7 +78,6 @@ public enum RomulusParameters public RomulusEngine(RomulusParameters romulusParameters) { - super(romulusParameters == RomulusParameters.RomulusT ? ProcessingBufferType.Immediate : ProcessingBufferType.Buffered); KEY_SIZE = IV_SIZE = MAC_SIZE = BlockSize = AADBufferSize = 16; CNT = new byte[7]; switch (romulusParameters) @@ -97,9 +96,6 @@ public RomulusEngine(RomulusParameters romulusParameters) instance = new RomulusT(); break; } - m_bufferSizeDecrypt = MAC_SIZE + BlockSize; - m_buf = new byte[m_bufferSizeDecrypt]; - m_aad = new byte[AADBufferSize]; setInnerMembers(romulusParameters == RomulusParameters.RomulusT ? ProcessingBufferType.Immediate : ProcessingBufferType.Buffered, AADOperatorType.Counter, romulusParameters == RomulusParameters.RomulusM ? DataOperatorType.Stream : DataOperatorType.Counter); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java index 26a65ab344..088dcf9150 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java @@ -41,7 +41,6 @@ public enum SparkleParameters public SparkleEngine(SparkleParameters sparkleParameters) { - super(ProcessingBufferType.Buffered); int SPARKLE_STATE; int SCHWAEMM_TAG_LEN; int SPARKLE_CAPACITY; @@ -110,9 +109,6 @@ public SparkleEngine(SparkleParameters sparkleParameters) k = new int[KEY_WORDS]; npub = new int[RATE_WORDS]; AADBufferSize = BlockSize = IV_SIZE; - m_bufferSizeDecrypt = IV_SIZE + MAC_SIZE; - m_buf = new byte[m_bufferSizeDecrypt]; - m_aad = new byte[BlockSize]; setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Default, DataOperatorType.Default); // Relied on by processBytes method for decryption diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index 3818a78b9f..bfb4630976 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -35,16 +35,12 @@ enum MODE public XoodyakEngine() { - super(ProcessingBufferType.Buffered); algorithmName = "Xoodyak AEAD"; KEY_SIZE = 16; IV_SIZE = 16; MAC_SIZE = 16; BlockSize = 24; AADBufferSize = 44; - m_bufferSizeDecrypt = BlockSize + MAC_SIZE; - m_buf = new byte[m_bufferSizeDecrypt]; - m_aad = new byte[AADBufferSize]; setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Default, DataOperatorType.Default); } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java index 6447e321a5..b4a556e279 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java @@ -10,6 +10,7 @@ import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.ElephantEngine; +import org.bouncycastle.crypto.engines.GiftCofbEngine; import org.bouncycastle.crypto.modes.AEADCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; @@ -44,7 +45,7 @@ public void performTest() // //testVectors(ElephantEngine.ElephantParameters.elephant160, "v160_2"); ElephantEngine elephant = new ElephantEngine(ElephantEngine.ElephantParameters.elephant200); testExceptions(elephant, elephant.getKeyBytesSize(), elephant.getIVBytesSize(), elephant.getBlockSize()); - testParameters(elephant, 16, 12, 16); + implTestParametersEngine(elephant, 16, 12, 16); CipherTest.checkCipher(10, 12, 40, 128, new CipherTest.Instance() { public AEADCipher createInstance() @@ -71,10 +72,10 @@ public AEADCipher createInstance() elephant = new ElephantEngine(ElephantEngine.ElephantParameters.elephant160); testExceptions(elephant, elephant.getKeyBytesSize(), elephant.getIVBytesSize(), elephant.getBlockSize()); - testParameters(elephant, 16, 12, 8); + implTestParametersEngine(elephant, 16, 12, 8); elephant = new ElephantEngine(ElephantEngine.ElephantParameters.elephant176); testExceptions(elephant, elephant.getKeyBytesSize(), elephant.getIVBytesSize(), elephant.getBlockSize()); - testParameters(elephant, 16, 12, 8); + implTestParametersEngine(elephant, 16, 12, 8); } @@ -309,6 +310,7 @@ private void testExceptions(AEADCipher aeadBlockCipher, int keysize, int ivsize, // } try { + aeadBlockCipher.init(true, params); aeadBlockCipher.doFinal(new byte[2], 2); fail(aeadBlockCipher.getAlgorithmName() + ": output for dofinal is too short"); } @@ -331,15 +333,14 @@ private void testExceptions(AEADCipher aeadBlockCipher, int keysize, int ivsize, fail(aeadBlockCipher.getAlgorithmName() + ": mac should match for the same AAD with different ways of inputing"); } - byte[] c2 = new byte[aeadBlockCipher.getOutputSize(10)]; - byte[] c3 = new byte[aeadBlockCipher.getOutputSize(10) + 2]; - byte[] aad2 = {0, 1, 2, 3, 4}; byte[] aad3 = {0, 0, 1, 2, 3, 4, 5}; byte[] m2 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; byte[] m3 = {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; byte[] m4 = new byte[m2.length]; aeadBlockCipher.init(true, params); + byte[] c2 = new byte[aeadBlockCipher.getOutputSize(10)]; + byte[] c3 = new byte[aeadBlockCipher.getOutputSize(10) + 2]; aeadBlockCipher.processAADBytes(aad2, 0, aad2.length); int offset = aeadBlockCipher.processBytes(m2, 0, m2.length, c2, 0); aeadBlockCipher.doFinal(c2, offset); @@ -414,21 +415,31 @@ private void testExceptions(AEADCipher aeadBlockCipher, int keysize, int ivsize, // System.out.println(aeadBlockCipher.getAlgorithmName() + " test Exceptions pass"); } - private void testParameters(ElephantEngine isap, int keySize, int ivSize, int macSize) + private void implTestParametersEngine(ElephantEngine cipher, int keySize, int ivSize, + int macSize) { - if (isap.getKeyBytesSize() != keySize) + if (cipher.getKeyBytesSize() != keySize) { - fail(isap.getAlgorithmName() + ": key bytes of " + isap.getAlgorithmName() + " is not correct"); + fail("key bytes of " + cipher.getAlgorithmName() + " is not correct"); } - if (isap.getIVBytesSize() != ivSize) + if (cipher.getIVBytesSize() != ivSize) { - fail(isap.getAlgorithmName() + ": iv bytes of " + isap.getAlgorithmName() + " is not correct"); + fail("iv bytes of " + cipher.getAlgorithmName() + " is not correct"); } - if (isap.getOutputSize(0) != macSize) + + CipherParameters parameters = new ParametersWithIV(new KeyParameter(new byte[keySize]), new byte[ivSize]); + + cipher.init(true, parameters); + if (cipher.getOutputSize(0) != macSize) + { + fail("getOutputSize of " + cipher.getAlgorithmName() + " is incorrect for encryption"); + } + + cipher.init(false, parameters); + if (cipher.getOutputSize(macSize) != 0) { - fail(isap.getAlgorithmName() + ": mac bytes of " + isap.getAlgorithmName() + " is not correct"); + fail("getOutputSize of " + cipher.getAlgorithmName() + " is incorrect for decryption"); } - // System.out.println(isap.getAlgorithmName() + " test Parameters pass"); } From 788a2124a7c6a51c4540b87a8ce66a1406cafdad Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 23 Jan 2025 17:37:15 +1030 Subject: [PATCH 040/890] Set DataOperatorType as Counter for PhotonBeetleEngine --- .../crypto/engines/AEADBufferBaseEngine.java | 4 ++-- .../crypto/engines/PhotonBeetleEngine.java | 18 +++--------------- .../crypto/engines/RomulusEngine.java | 1 - .../bouncycastle/crypto/test/ElephantTest.java | 1 - 4 files changed, 5 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index b6d392b072..ff5503953a 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -382,10 +382,10 @@ public void reset() } } - protected class StreamDataOperator + protected static class StreamDataOperator implements DataOperator { - private ErasableOutputStream stream = new ErasableOutputStream(); + private final ErasableOutputStream stream = new ErasableOutputStream(); @Override public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index ea8d4b1576..2d103b3121 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -1,7 +1,5 @@ package org.bouncycastle.crypto.engines; -import org.bouncycastle.crypto.DataLengthException; - /** * Photon-Beetle, * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/photon-beetle-spec-final.pdf @@ -24,7 +22,6 @@ public enum PhotonBeetleParameters private byte[] N; private byte[] state; private byte[][] state_2d; - private int messageLen; private final int RATE_INBYTES_HALF; private final int STATE_INBYTES; private final int LAST_THREE_BITS_OFFSET; @@ -77,7 +74,7 @@ public PhotonBeetleEngine(PhotonBeetleParameters pbp) LAST_THREE_BITS_OFFSET = (STATE_INBITS - ((STATE_INBYTES - 1) << 3) - 3); algorithmName = "Photon-Beetle AEAD"; setInnerMembers(pbp == PhotonBeetleParameters.pb128 ? ProcessingBufferType.Buffered : ProcessingBufferType.BufferedLargeMac, - AADOperatorType.Counter, DataOperatorType.Default); + AADOperatorType.Counter, DataOperatorType.Counter); } @Override @@ -116,7 +113,7 @@ public void processFinalAAD() state[m_aadPos] ^= 0x01; // ozs } } - state[STATE_INBYTES - 1] ^= select(messageLen - (forEncryption ? 0 : MAC_SIZE) > 0, + state[STATE_INBYTES - 1] ^= select(dataOperator.getLen() - (forEncryption ? 0 : MAC_SIZE) > 0, ((aadLen % BlockSize) == 0), (byte)3, (byte)4) << LAST_THREE_BITS_OFFSET; } m_aadPos = 0; @@ -138,18 +135,10 @@ protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int XOR(output, outOff, BlockSize); } - @Override - public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - throws DataLengthException - { - messageLen += len; - return super.processBytes(input, inOff, len, output, outOff); - } - @Override protected void processFinalBlock(byte[] output, int outOff) { - int len = messageLen - (forEncryption ? 0 : MAC_SIZE); + int len = dataOperator.getLen() - (forEncryption ? 0 : MAC_SIZE); int bufferLen = m_bufPos;// - (forEncryption ? 0 : MAC_SIZE); int aadLen = aadOperator.getLen(); if (aadLen != 0 || len != 0) @@ -194,7 +183,6 @@ protected void reset(boolean clearMac) bufferReset(); input_empty = true; aadFinished = false; - messageLen = 0; System.arraycopy(K, 0, state, 0, K.length); System.arraycopy(N, 0, state, K.length, N.length); super.reset(clearMac); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index 8e59dc160b..d2756a9e2f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -2,7 +2,6 @@ import org.bouncycastle.util.Arrays; - /** * Romulus v1.3, based on the current round 3 submission, https://romulusae.github.io/romulus/ * Reference C implementation: https://github.com/romulusae/romulus diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java index b4a556e279..82e332918c 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java @@ -10,7 +10,6 @@ import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.ElephantEngine; -import org.bouncycastle.crypto.engines.GiftCofbEngine; import org.bouncycastle.crypto.modes.AEADCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; From 6fd0964c72e0d8d620ec58f77dff37ac1d85fe89 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 23 Jan 2025 16:31:53 +0700 Subject: [PATCH 041/890] More work in crypto.hpke - Validate public key lengths and uncompressed format - Clamp XDH private key output - Switch to RawAgreement (add BasicRawAgreement adapter) - Avoid String.getBytes for label conversion --- .../crypto/agreement/BasicRawAgreement.java | 40 ++++++ .../org/bouncycastle/crypto/hpke/DHKEM.java | 126 ++++++++++++++---- .../org/bouncycastle/crypto/hpke/HKDF.java | 19 ++- .../bouncycastle/math/ec/rfc7748/X25519.java | 16 ++- .../bouncycastle/math/ec/rfc7748/X448.java | 14 +- .../crypto/test/HPKETestVectors.java | 37 ++++- 6 files changed, 211 insertions(+), 41 deletions(-) create mode 100644 core/src/main/java/org/bouncycastle/crypto/agreement/BasicRawAgreement.java diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/BasicRawAgreement.java b/core/src/main/java/org/bouncycastle/crypto/agreement/BasicRawAgreement.java new file mode 100644 index 0000000000..1309fbf696 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/agreement/BasicRawAgreement.java @@ -0,0 +1,40 @@ +package org.bouncycastle.crypto.agreement; + +import java.math.BigInteger; + +import org.bouncycastle.crypto.BasicAgreement; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.RawAgreement; +import org.bouncycastle.util.BigIntegers; + +public final class BasicRawAgreement + implements RawAgreement +{ + public final BasicAgreement basicAgreement; + + public BasicRawAgreement(BasicAgreement basicAgreement) + { + if (basicAgreement == null) + { + throw new NullPointerException("'basicAgreement' cannot be null"); + } + + this.basicAgreement = basicAgreement; + } + + public void init(CipherParameters parameters) + { + basicAgreement.init(parameters); + } + + public int getAgreementSize() + { + return basicAgreement.getFieldSize(); + } + + public void calculateAgreement(CipherParameters publicKey, byte[] buf, int off) + { + BigInteger z = basicAgreement.calculateAgreement(publicKey); + BigIntegers.asUnsignedByteArray(z, buf, off, getAgreementSize()); + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/hpke/DHKEM.java b/core/src/main/java/org/bouncycastle/crypto/hpke/DHKEM.java index 9c23ed9b2e..2eb175b6a3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/hpke/DHKEM.java +++ b/core/src/main/java/org/bouncycastle/crypto/hpke/DHKEM.java @@ -5,10 +5,12 @@ import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; -import org.bouncycastle.crypto.BasicAgreement; import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.RawAgreement; +import org.bouncycastle.crypto.agreement.BasicRawAgreement; import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement; -import org.bouncycastle.crypto.agreement.XDHBasicAgreement; +import org.bouncycastle.crypto.agreement.X25519Agreement; +import org.bouncycastle.crypto.agreement.X448Agreement; import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.generators.X25519KeyPairGenerator; @@ -27,6 +29,8 @@ import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.math.ec.FixedPointCombMultiplier; import org.bouncycastle.math.ec.WNafUtil; +import org.bouncycastle.math.ec.rfc7748.X25519; +import org.bouncycastle.math.ec.rfc7748.X448; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; import org.bouncycastle.util.Pack; @@ -37,7 +41,7 @@ class DHKEM { private AsymmetricCipherKeyPairGenerator kpGen; - private BasicAgreement agreement; + private RawAgreement rawAgreement; // kem ids private final short kemId; @@ -59,7 +63,7 @@ protected DHKEM(short kemid) case HPKE.kem_P256_SHA256: this.hkdf = new HKDF(HPKE.kdf_HKDF_SHA256); domainParams = getDomainParameters("P-256"); - this.agreement = new ECDHCBasicAgreement(); + rawAgreement = new BasicRawAgreement(new ECDHCBasicAgreement()); bitmask = (byte)0xff; Nsk = 32; Nsecret = 32; @@ -72,7 +76,7 @@ protected DHKEM(short kemid) case HPKE.kem_P384_SHA348: this.hkdf = new HKDF(HPKE.kdf_HKDF_SHA384); domainParams = getDomainParameters("P-384"); - this.agreement = new ECDHCBasicAgreement(); + rawAgreement = new BasicRawAgreement(new ECDHCBasicAgreement()); bitmask = (byte)0xff; Nsk = 48; Nsecret = 48; @@ -85,7 +89,7 @@ protected DHKEM(short kemid) case HPKE.kem_P521_SHA512: this.hkdf = new HKDF(HPKE.kdf_HKDF_SHA512); domainParams = getDomainParameters("P-521"); - this.agreement = new ECDHCBasicAgreement(); + rawAgreement = new BasicRawAgreement(new ECDHCBasicAgreement()); bitmask = 0x01; Nsk = 66; Nsecret = 64; @@ -97,7 +101,7 @@ protected DHKEM(short kemid) break; case HPKE.kem_X25519_SHA256: this.hkdf = new HKDF(HPKE.kdf_HKDF_SHA256); - this.agreement = new XDHBasicAgreement(); + rawAgreement = new X25519Agreement(); Nsecret = 32; Nsk = 32; Nenc = 32; @@ -108,7 +112,7 @@ protected DHKEM(short kemid) break; case HPKE.kem_X448_SHA512: this.hkdf = new HKDF(HPKE.kdf_HKDF_SHA512); - this.agreement = new XDHBasicAgreement(); + rawAgreement = new X448Agreement(); Nsecret = 64; Nsk = 56; Nenc = 56; @@ -129,6 +133,10 @@ public byte[] SerializePublicKey(AsymmetricKeyParameter key) case HPKE.kem_P256_SHA256: case HPKE.kem_P384_SHA348: case HPKE.kem_P521_SHA512: + /* + * RFC 9180 7.1.1. For P-256, P-384, and P-521, the SerializePublicKey() function of the KEM performs + * the uncompressed Elliptic-Curve-Point-to-Octet-String conversion according to [SECG]. + */ return ((ECPublicKeyParameters)key).getQ().getEncoded(false); case HPKE.kem_X448_SHA512: return ((X448PublicKeyParameters)key).getEncoded(); @@ -146,30 +154,74 @@ public byte[] SerializePrivateKey(AsymmetricKeyParameter key) case HPKE.kem_P256_SHA256: case HPKE.kem_P384_SHA348: case HPKE.kem_P521_SHA512: + { + /* + * RFC 9180 7.1.2. For P-256, P-384, and P-521, the SerializePrivateKey() function of the KEM + * performs the Field-Element-to-Octet-String conversion according to [SECG]. + */ return BigIntegers.asUnsignedByteArray(Nsk, ((ECPrivateKeyParameters)key).getD()); + } case HPKE.kem_X448_SHA512: - return ((X448PrivateKeyParameters)key).getEncoded(); + { + /* + * RFC 9180 7.1.2. For [..] X448 [..]. The SerializePrivateKey() function MUST clamp its output + * [..]. + * + * NOTE: Our X448 implementation clamps generated keys, but de-serialized keys are preserved as is + * (clamping applied only during usage). + */ + byte[] encoded = ((X448PrivateKeyParameters)key).getEncoded(); + X448.clampPrivateKey(encoded); + return encoded; + } case HPKE.kem_X25519_SHA256: - return ((X25519PrivateKeyParameters)key).getEncoded(); + { + /* + * RFC 9180 7.1.2. For X25519 [..]. The SerializePrivateKey() function MUST clamp its output [..]. + * + * NOTE: Our X25519 implementation clamps generated keys, but de-serialized keys are preserved as + * is (clamping applied only during usage). + */ + byte[] encoded = ((X25519PrivateKeyParameters)key).getEncoded(); + X25519.clampPrivateKey(encoded); + return encoded; + } default: throw new IllegalStateException("invalid kem id"); } } - public AsymmetricKeyParameter DeserializePublicKey(byte[] encoded) + public AsymmetricKeyParameter DeserializePublicKey(byte[] pkEncoded) { + if (pkEncoded == null) + { + throw new NullPointerException("'pkEncoded' cannot be null"); + } + if (pkEncoded.length != Nenc) + { + throw new IllegalArgumentException("'pkEncoded' has invalid length"); + } + switch (kemId) { case HPKE.kem_P256_SHA256: case HPKE.kem_P384_SHA348: case HPKE.kem_P521_SHA512: - // TODO Does the encoding have to be uncompressed? (i.e. encoded.length MUST be Nenc?) - ECPoint G = domainParams.getCurve().decodePoint(encoded); + /* + * RFC 9180 7.1.1. For P-256, P-384, and P-521 [..]. DeserializePublicKey() performs the + * uncompressed Octet-String-to-Elliptic-Curve-Point conversion. + */ + if (pkEncoded[0] != 0x04) // "0x04" is the marker for an uncompressed encoding + { + throw new IllegalArgumentException("'pkEncoded' has invalid format"); + } + + ECPoint G = domainParams.getCurve().decodePoint(pkEncoded); return new ECPublicKeyParameters(G, domainParams); case HPKE.kem_X448_SHA512: - return new X448PublicKeyParameters(encoded); + return new X448PublicKeyParameters(pkEncoded); case HPKE.kem_X25519_SHA256: - return new X25519PublicKeyParameters(encoded); + return new X25519PublicKeyParameters(pkEncoded); default: throw new IllegalStateException("invalid kem id"); } @@ -198,6 +250,10 @@ public AsymmetricCipherKeyPair DeserializePrivateKey(byte[] skEncoded, byte[] pk case HPKE.kem_P256_SHA256: case HPKE.kem_P384_SHA348: case HPKE.kem_P521_SHA512: + /* + * RFC 9180 7.1.2. For P-256, P-384, and P-521 [..]. DeserializePrivateKey() performs the Octet- + * String-to-Field-Element conversion according to [SECG]. + */ BigInteger d = new BigInteger(1, skEncoded); ECPrivateKeyParameters ec = new ECPrivateKeyParameters(d, domainParams); @@ -317,7 +373,7 @@ protected byte[][] Encap(AsymmetricKeyParameter pkR, AsymmetricCipherKeyPair kpE byte[][] output = new byte[2][]; // DH - byte[] secret = calculateAgreement(agreement, kpE.getPrivate(), pkR); + byte[] secret = calculateRawAgreement(rawAgreement, kpE.getPrivate(), pkR); byte[] enc = SerializePublicKey(kpE.getPublic()); byte[] pkRm = SerializePublicKey(pkR); @@ -335,7 +391,7 @@ protected byte[] Decap(byte[] enc, AsymmetricCipherKeyPair kpR) AsymmetricKeyParameter pkE = DeserializePublicKey(enc); // DH - byte[] secret = calculateAgreement(agreement, kpR.getPrivate(), pkE); + byte[] secret = calculateRawAgreement(rawAgreement, kpR.getPrivate(), pkE); byte[] pkRm = SerializePublicKey(kpR.getPublic()); byte[] KEMContext = Arrays.concatenate(enc, pkRm); @@ -350,12 +406,22 @@ protected byte[][] AuthEncap(AsymmetricKeyParameter pkR, AsymmetricCipherKeyPair AsymmetricCipherKeyPair kpE = kpGen.generateKeyPair(); // todo: can be replaced with deriveKeyPair(random) // DH(skE, pkR) - byte[] secret1 = calculateAgreement(agreement, kpE.getPrivate(), pkR); + rawAgreement.init(kpE.getPrivate()); + int agreementSize = rawAgreement.getAgreementSize(); + + byte[] secret = new byte[agreementSize * 2]; + + rawAgreement.calculateAgreement(pkR, secret, 0); // DH(skS, pkR) - byte[] secret2 = calculateAgreement(agreement, kpS.getPrivate(), pkR); + rawAgreement.init(kpS.getPrivate()); + if (agreementSize != rawAgreement.getAgreementSize()) + { + throw new IllegalStateException(); + } + + rawAgreement.calculateAgreement(pkR, secret, agreementSize); - byte[] secret = Arrays.concatenate(secret1, secret2); byte[] enc = SerializePublicKey(kpE.getPublic()); byte[] pkRm = SerializePublicKey(pkR); @@ -373,13 +439,16 @@ protected byte[] AuthDecap(byte[] enc, AsymmetricCipherKeyPair kpR, AsymmetricKe { AsymmetricKeyParameter pkE = DeserializePublicKey(enc); + rawAgreement.init(kpR.getPrivate()); + + int agreementSize = rawAgreement.getAgreementSize(); + byte[] secret = new byte[agreementSize * 2]; + // DH(skR, pkE) - byte[] secret1 = calculateAgreement(agreement, kpR.getPrivate(), pkE); + rawAgreement.calculateAgreement(pkE, secret, 0); // DH(skR, pkS) - byte[] secret2 = calculateAgreement(agreement, kpR.getPrivate(), pkS); - - byte[] secret = Arrays.concatenate(secret1, secret2); + rawAgreement.calculateAgreement(pkS, secret, agreementSize); byte[] pkRm = SerializePublicKey(kpR.getPublic()); byte[] pkSm = SerializePublicKey(pkS); @@ -397,12 +466,13 @@ private byte[] ExtractAndExpand(byte[] dh, byte[] kemContext) return hkdf.LabeledExpand(eae_prk, suiteID, "shared_secret", kemContext, Nsecret); } - private static byte[] calculateAgreement(BasicAgreement agreement, AsymmetricKeyParameter privateKey, + private static byte[] calculateRawAgreement(RawAgreement rawAgreement, AsymmetricKeyParameter privateKey, AsymmetricKeyParameter publicKey) { - agreement.init(privateKey); - BigInteger z = agreement.calculateAgreement(publicKey); - return BigIntegers.asUnsignedByteArray(agreement.getFieldSize(), z); + rawAgreement.init(privateKey); + byte[] z = new byte[rawAgreement.getAgreementSize()]; + rawAgreement.calculateAgreement(publicKey, z, 0); + return z; } private static ECDomainParameters getDomainParameters(String curveName) diff --git a/core/src/main/java/org/bouncycastle/crypto/hpke/HKDF.java b/core/src/main/java/org/bouncycastle/crypto/hpke/HKDF.java index a9e0ef8d37..f69cc8e398 100644 --- a/core/src/main/java/org/bouncycastle/crypto/hpke/HKDF.java +++ b/core/src/main/java/org/bouncycastle/crypto/hpke/HKDF.java @@ -8,10 +8,11 @@ import org.bouncycastle.crypto.params.HKDFParameters; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; +import org.bouncycastle.util.Strings; class HKDF { - private final static String versionLabel = "HPKE-v1"; + private final static byte[] VERSION_LABEL = getBytes("HPKE-v1"); private final HKDFBytesGenerator kdf; private final int hashLength; @@ -50,7 +51,7 @@ protected byte[] LabeledExtract(byte[] salt, byte[] suiteID, String label, byte[ salt = new byte[hashLength]; } - byte[] labeledIKM = Arrays.concatenate(versionLabel.getBytes(), suiteID, label.getBytes(), ikm); + byte[] labeledIKM = Arrays.concatenate(VERSION_LABEL, suiteID, getBytes(label), ikm); return kdf.extractPRK(salt, labeledIKM); } @@ -61,7 +62,9 @@ protected byte[] LabeledExpand(byte[] prk, byte[] suiteID, String label, byte[] { throw new IllegalArgumentException("Expand length cannot be larger than 2^16"); } - byte[] labeledInfo = Arrays.concatenate(Pack.shortToBigEndian((short)L), versionLabel.getBytes(), suiteID, label.getBytes()); + + byte[] labeledInfo = Arrays.concatenate(Pack.shortToBigEndian((short)L), VERSION_LABEL, suiteID, + getBytes(label)); kdf.init(HKDFParameters.skipExtractParameters(prk, Arrays.concatenate(labeledInfo, info))); @@ -97,4 +100,14 @@ protected byte[] Expand(byte[] prk, byte[] info, int L) return rv; } + + private static byte[] getBytes(String label) + { + /* + * RFC 9180 seems silent about this conversion, but all given labels are ASCII anyway. + * + * NOTE: String#getBytes not reliable because it depends on the platform's default charset. + */ + return Strings.toByteArray(label); + } } diff --git a/core/src/main/java/org/bouncycastle/math/ec/rfc7748/X25519.java b/core/src/main/java/org/bouncycastle/math/ec/rfc7748/X25519.java index 02fdefed0f..3a752d3324 100644 --- a/core/src/main/java/org/bouncycastle/math/ec/rfc7748/X25519.java +++ b/core/src/main/java/org/bouncycastle/math/ec/rfc7748/X25519.java @@ -30,6 +30,18 @@ public static boolean calculateAgreement(byte[] k, int kOff, byte[] u, int uOff, return !Arrays.areAllZeroes(r, rOff, POINT_SIZE); } + public static void clampPrivateKey(byte[] k) + { + if (k.length != SCALAR_SIZE) + { + throw new IllegalArgumentException("k"); + } + + k[0] &= 0xF8; + k[SCALAR_SIZE - 1] &= 0x7F; + k[SCALAR_SIZE - 1] |= 0x40; + } + private static int decode32(byte[] bs, int off) { int n = bs[off] & 0xFF; @@ -60,9 +72,7 @@ public static void generatePrivateKey(SecureRandom random, byte[] k) random.nextBytes(k); - k[0] &= 0xF8; - k[SCALAR_SIZE - 1] &= 0x7F; - k[SCALAR_SIZE - 1] |= 0x40; + clampPrivateKey(k); } public static void generatePublicKey(byte[] k, int kOff, byte[] r, int rOff) diff --git a/core/src/main/java/org/bouncycastle/math/ec/rfc7748/X448.java b/core/src/main/java/org/bouncycastle/math/ec/rfc7748/X448.java index 3954ba110c..ecfbc8c4f1 100644 --- a/core/src/main/java/org/bouncycastle/math/ec/rfc7748/X448.java +++ b/core/src/main/java/org/bouncycastle/math/ec/rfc7748/X448.java @@ -31,6 +31,17 @@ public static boolean calculateAgreement(byte[] k, int kOff, byte[] u, int uOff, return !Arrays.areAllZeroes(r, rOff, POINT_SIZE); } + public static void clampPrivateKey(byte[] k) + { + if (k.length != SCALAR_SIZE) + { + throw new IllegalArgumentException("k"); + } + + k[0] &= 0xFC; + k[SCALAR_SIZE - 1] |= 0x80; + } + private static int decode32(byte[] bs, int off) { int n = bs[ off] & 0xFF; @@ -60,8 +71,7 @@ public static void generatePrivateKey(SecureRandom random, byte[] k) random.nextBytes(k); - k[0] &= 0xFC; - k[SCALAR_SIZE - 1] |= 0x80; + clampPrivateKey(k); } public static void generatePublicKey(byte[] k, int kOff, byte[] r, int rOff) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/HPKETestVectors.java b/core/src/test/java/org/bouncycastle/crypto/test/HPKETestVectors.java index 7f3c12579b..830535a040 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/HPKETestVectors.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/HPKETestVectors.java @@ -8,7 +8,6 @@ import java.util.HashMap; import java.util.Iterator; -import junit.framework.TestCase; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.hpke.AEAD; @@ -16,10 +15,14 @@ import org.bouncycastle.crypto.hpke.HPKEContext; import org.bouncycastle.crypto.hpke.HPKEContextWithEncapsulation; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.math.ec.rfc7748.X25519; +import org.bouncycastle.math.ec.rfc7748.X448; import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; +import junit.framework.TestCase; + public class HPKETestVectors extends TestCase { @@ -259,26 +262,50 @@ public void testVectors() // generate a private key from skRm and pkRm AsymmetricCipherKeyPair kp = hpke.deserializePrivateKey(skRm, pkRm); + byte[] skRm_serialized = Arrays.clone(skRm); + byte[] skSm_serialized = Arrays.clone(skSm); + byte[] skEm_serialized = Arrays.clone(skEm); + + switch (kem_id) + { + case HPKE.kem_X25519_SHA256: + X25519.clampPrivateKey(skRm_serialized); + if (mode == 2 || mode == 3) + { + X25519.clampPrivateKey(skSm_serialized); + } + X25519.clampPrivateKey(skEm_serialized); + break; + case HPKE.kem_X448_SHA512: + X448.clampPrivateKey(skRm_serialized); + if (mode == 2 || mode == 3) + { + X448.clampPrivateKey(skSm_serialized); + } + X448.clampPrivateKey(skEm_serialized); + break; + } + // tesing serialize assertTrue("serialize public key failed", Arrays.areEqual(pkRm, hpke.serializePublicKey(kp.getPublic()))); - assertTrue("serialize private key failed", Arrays.areEqual(skRm, hpke.serializePrivateKey(kp.getPrivate()))); + assertTrue("serialize private key failed", Arrays.areEqual(skRm_serialized, hpke.serializePrivateKey(kp.getPrivate()))); // testing receiver derive key pair assertTrue("receiver derived public key pair incorrect", Arrays.areEqual(pkRm, hpke.serializePublicKey(derivedKeyPairR.getPublic()))); - assertTrue("receiver derived secret key pair incorrect", Arrays.areEqual(skRm, hpke.serializePrivateKey(derivedKeyPairR.getPrivate()))); + assertTrue("receiver derived secret key pair incorrect", Arrays.areEqual(skRm_serialized, hpke.serializePrivateKey(derivedKeyPairR.getPrivate()))); // testing sender's derived key pair if (mode == 2 || mode == 3) { AsymmetricCipherKeyPair derivedSenderKeyPair = hpke.deriveKeyPair(ikmS); assertTrue("sender derived public key pair incorrect", Arrays.areEqual(pkSm, hpke.serializePublicKey(derivedSenderKeyPair.getPublic()))); - assertTrue("sender derived private key pair incorrect", Arrays.areEqual(skSm, hpke.serializePrivateKey(derivedSenderKeyPair.getPrivate()))); + assertTrue("sender derived private key pair incorrect", Arrays.areEqual(skSm_serialized, hpke.serializePrivateKey(derivedSenderKeyPair.getPrivate()))); } // testing ephemeral derived key pair AsymmetricCipherKeyPair derivedEKeyPair = hpke.deriveKeyPair(ikmE); assertTrue("ephemeral derived public key pair incorrect", Arrays.areEqual(pkEm, hpke.serializePublicKey(derivedEKeyPair.getPublic()))); - assertTrue("ephemeral derived private key pair incorrect", Arrays.areEqual(skEm, hpke.serializePrivateKey(derivedEKeyPair.getPrivate()))); + assertTrue("ephemeral derived private key pair incorrect", Arrays.areEqual(skEm_serialized, hpke.serializePrivateKey(derivedEKeyPair.getPrivate()))); // create a context with setupRecv // use pkEm as encap, private key from above, info as info From 32608c8eefba009452085f526ad85e8b90a7e1da Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 23 Jan 2025 16:32:31 +0700 Subject: [PATCH 042/890] Add Extensions helpers --- .../asn1/x509/CertificateList.java | 5 +++ .../bouncycastle/asn1/x509/Extensions.java | 43 +++++++++++++++---- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java b/core/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java index c4c14d9947..dc848c6519 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java @@ -117,6 +117,11 @@ public Time getNextUpdate() return tbsCertList.getNextUpdate(); } + public Extensions getExtensions() + { + return tbsCertList.getExtensions(); + } + public ASN1Primitive toASN1Primitive() { ASN1EncodableVector v = new ASN1EncodableVector(3); diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/Extensions.java b/core/src/main/java/org/bouncycastle/asn1/x509/Extensions.java index c552d370b7..4434183613 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/Extensions.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/Extensions.java @@ -8,6 +8,7 @@ import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Object; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; @@ -40,6 +41,11 @@ public static ASN1Encodable getExtensionParsedValue(Extensions extensions, ASN1O return null == extensions ? null : extensions.getExtensionParsedValue(oid); } + public static ASN1OctetString getExtensionValue(Extensions extensions, ASN1ObjectIdentifier oid) + { + return null == extensions ? null : extensions.getExtensionValue(oid); + } + public static Extensions getInstance( ASN1TaggedObject obj, boolean explicit) @@ -141,8 +147,7 @@ public Enumeration oids() * * @return the extension if it's present, null otherwise. */ - public Extension getExtension( - ASN1ObjectIdentifier oid) + public Extension getExtension(ASN1ObjectIdentifier oid) { return (Extension)extensions.get(oid); } @@ -155,14 +160,19 @@ public Extension getExtension( */ public ASN1Encodable getExtensionParsedValue(ASN1ObjectIdentifier oid) { - Extension ext = this.getExtension(oid); - - if (ext != null) - { - return ext.getParsedValue(); - } + Extension ext = getExtension(oid); + return ext == null ? null : ext.getParsedValue(); + } - return null; + /** + * return the value of the extension represented by the object identifier passed in. + * + * @return the value of the extension if it's present, null otherwise. + */ + public ASN1OctetString getExtensionValue(ASN1ObjectIdentifier oid) + { + Extension ext = getExtension(oid); + return ext == null ? null : ext.getExtnValue(); } /** @@ -229,6 +239,21 @@ public ASN1ObjectIdentifier[] getCriticalExtensionOIDs() return getExtensionOIDs(true); } + public boolean hasAnyCriticalExtensions() + { + for (int i = 0; i != ordering.size(); i++) + { + Object oid = ordering.elementAt(i); + + if (((Extension)extensions.get(oid)).isCritical()) + { + return true; + } + } + + return false; + } + private ASN1ObjectIdentifier[] getExtensionOIDs(boolean isCritical) { Vector oidVec = new Vector(); From 075a7bb6e0bf156bbaaf82343bc06fdd57ac4e47 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 23 Jan 2025 16:47:27 +0700 Subject: [PATCH 043/890] Simplify adding oracle trusted key usage attribute --- .../keystore/pkcs12/PKCS12KeyStoreSpi.java | 33 +++++-------------- 1 file changed, 9 insertions(+), 24 deletions(-) diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java index 3a1a46d691..ebbd568a5c 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java @@ -1984,36 +1984,21 @@ private SafeBag createSafeBag(String certId, Certificate cert, boolean overwrite if (cert instanceof X509Certificate) { TBSCertificate tbsCert = TBSCertificate.getInstance(((X509Certificate)cert).getTBSCertificate()); - Extensions exts = tbsCert.getExtensions(); - if (exts != null) - { - Extension extUsage = exts.getExtension(Extension.extendedKeyUsage); - if (extUsage != null) - { - ASN1EncodableVector fSeq = new ASN1EncodableVector(); - // oracle trusted key usage OID. - fSeq.add(MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage); - fSeq.add(new DERSet(ExtendedKeyUsage.getInstance(extUsage.getParsedValue()).getUsages())); - fName.add(new DERSequence(fSeq)); - } - else - { - ASN1EncodableVector fSeq = new ASN1EncodableVector(); + ASN1OctetString eku = Extensions.getExtensionValue(tbsCert.getExtensions(), + Extension.extendedKeyUsage); - fSeq.add(MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage); - fSeq.add(new DERSet(KeyPurposeId.anyExtendedKeyUsage)); - fName.add(new DERSequence(fSeq)); - } + DERSet attrValue; + if (eku != null) + { + attrValue = new DERSet(ExtendedKeyUsage.getInstance(eku.getOctets()).getUsages()); } else { - ASN1EncodableVector fSeq = new ASN1EncodableVector(); - - fSeq.add(MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage); - fSeq.add(new DERSet(KeyPurposeId.anyExtendedKeyUsage)); - fName.add(new DERSequence(fSeq)); + attrValue = new DERSet(KeyPurposeId.anyExtendedKeyUsage); } + + fName.add(new DERSequence(MiscObjectIdentifiers.id_oracle_pkcs12_trusted_key_usage, attrValue)); } return new SafeBag(certBag, cBag.toASN1Primitive(), new DERSet(fName)); From 54661c8d19ec62f07610b089909d80466e54c0a0 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 23 Jan 2025 17:09:19 +0700 Subject: [PATCH 044/890] Refactoring in openpgp.operator.bc --- .../bc/BcPublicKeyDataDecryptorFactory.java | 13 ++++--------- .../bc/BcPublicKeyKeyEncryptionMethodGenerator.java | 8 +++----- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java index ffc262be30..a407e38710 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java @@ -1,7 +1,6 @@ package org.bouncycastle.openpgp.operator.bc; import java.io.IOException; -import java.math.BigInteger; import org.bouncycastle.asn1.cryptlib.CryptlibObjectIdentifiers; import org.bouncycastle.asn1.edec.EdECObjectIdentifiers; @@ -18,6 +17,7 @@ import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.RawAgreement; import org.bouncycastle.crypto.Wrapper; +import org.bouncycastle.crypto.agreement.BasicRawAgreement; import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; import org.bouncycastle.crypto.agreement.X25519Agreement; import org.bouncycastle.crypto.agreement.X448Agreement; @@ -37,7 +37,6 @@ import org.bouncycastle.openpgp.operator.PGPPad; import org.bouncycastle.openpgp.operator.RFC6637Utils; import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.BigIntegers; /** * A decryptor factory for handling public key decryption operations. @@ -210,15 +209,11 @@ else if (ecPubKey.getCurveOID().equals(EdECObjectIdentifiers.id_X448)) } else { - ECDomainParameters ecParameters = ((ECPrivateKeyParameters) privKey).getParameters(); - + ECDomainParameters ecParameters = ((ECPrivateKeyParameters)privKey).getParameters(); ECPublicKeyParameters ephPub = new ECPublicKeyParameters(ecParameters.getCurve().decodePoint(pEnc), - ecParameters); + ecParameters); - ECDHBasicAgreement agreement = new ECDHBasicAgreement(); - agreement.init(privKey); - BigInteger S = agreement.calculateAgreement(ephPub); - secret = BigIntegers.asUnsignedByteArray(agreement.getFieldSize(), S); + secret = BcUtil.getSecret(new BasicRawAgreement(new ECDHBasicAgreement()), privKey, ephPub); } hashAlgorithm = ecPubKey.getHashAlgorithm(); symmetricKeyAlgorithm = ecPubKey.getSymmetricKeyAlgorithm(); diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java index a59d12dc76..ea70b108f1 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyKeyEncryptionMethodGenerator.java @@ -17,6 +17,7 @@ import org.bouncycastle.crypto.KeyGenerationParameters; import org.bouncycastle.crypto.RawAgreement; import org.bouncycastle.crypto.Wrapper; +import org.bouncycastle.crypto.agreement.BasicRawAgreement; import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; import org.bouncycastle.crypto.agreement.X25519Agreement; import org.bouncycastle.crypto.agreement.X448Agreement; @@ -38,7 +39,6 @@ import org.bouncycastle.openpgp.operator.PublicKeyKeyEncryptionMethodGenerator; import org.bouncycastle.openpgp.operator.RFC6637Utils; import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.BigIntegers; /** * A method generator for supporting public key based encryption operations. @@ -122,10 +122,8 @@ else if (ecPubKey.getCurveOID().equals(EdECObjectIdentifiers.id_X448)) AsymmetricCipherKeyPair ephKp = getAsymmetricCipherKeyPair(new ECKeyPairGenerator(), new ECKeyGenerationParameters(((ECPublicKeyParameters)cryptoPublicKey).getParameters(), random)); - ECDHBasicAgreement agreement = new ECDHBasicAgreement(); - agreement.init(ephKp.getPrivate()); - BigInteger S = agreement.calculateAgreement(cryptoPublicKey); - byte[] secret = BigIntegers.asUnsignedByteArray(agreement.getFieldSize(), S); + byte[] secret = BcUtil.getSecret(new BasicRawAgreement(new ECDHBasicAgreement()), + ephKp.getPrivate(), cryptoPublicKey); byte[] ephPubEncoding = ((ECPublicKeyParameters)ephKp.getPublic()).getQ().getEncoded(false); From 785d57a3114d7ba90520468d0f64ebf9ed682d48 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 24 Jan 2025 13:21:39 +1030 Subject: [PATCH 045/890] Add Romulus M --- .../crypto/engines/AEADBufferBaseEngine.java | 36 +- .../crypto/engines/Grain128AEADEngine.java | 1 - .../crypto/engines/RomulusEngine.java | 411 ++++++++++-------- .../bouncycastle/crypto/test/CipherTest.java | 22 +- .../bouncycastle/crypto/test/RomulusTest.java | 30 +- 5 files changed, 305 insertions(+), 195 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index ff5503953a..5d10c1f737 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -29,7 +29,8 @@ protected enum DataOperatorType { Default, Counter, - Stream + Stream, + //StreamCipher } protected enum State @@ -103,8 +104,12 @@ protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOpe dataOperator = new CounterDataOperator(); break; case Stream: + m_buf = new byte[MAC_SIZE]; dataOperator = new StreamDataOperator(); break; +// case StreamCipher: +// dataOperator = new StreamCipherOperator(); +// break; } } @@ -382,7 +387,7 @@ public void reset() } } - protected static class StreamDataOperator + protected class StreamDataOperator implements DataOperator { private final ErasableOutputStream stream = new ErasableOutputStream(); @@ -390,7 +395,9 @@ protected static class StreamDataOperator @Override public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { + ensureInitialized(); stream.write(input, inOff, len); + m_bufPos = stream.size(); return 0; } @@ -412,6 +419,31 @@ public void reset() } } +// protected class StreamCipherOperator +// implements DataOperator +// { +// private int len; +// @Override +// public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) +// { +// this.len = len; +// processBufferEncrypt(input, inOff, output, outOff); +// return len; +// } +// +// @Override +// public int getLen() +// { +// return 0; +// } +// +// @Override +// public void reset() +// { +// +// } +// } + protected static final class ErasableOutputStream extends ByteArrayOutputStream { diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java index c2c52f0abb..de8766363f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java @@ -5,7 +5,6 @@ import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.OutputLengthException; -import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; /** diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index d2756a9e2f..663f168046 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -81,10 +81,10 @@ public RomulusEngine(RomulusParameters romulusParameters) CNT = new byte[7]; switch (romulusParameters) { -// case RomulusM: -// algorithmName = "Romulus-M"; -// instance = new RomulusM(); -// break; + case RomulusM: + algorithmName = "Romulus-M"; + instance = new RomulusM(); + break; case RomulusN: algorithmName = "Romulus-N"; instance = new RomulusN(); @@ -95,7 +95,7 @@ public RomulusEngine(RomulusParameters romulusParameters) instance = new RomulusT(); break; } - setInnerMembers(romulusParameters == RomulusParameters.RomulusT ? ProcessingBufferType.Immediate : ProcessingBufferType.Buffered, + setInnerMembers(romulusParameters == RomulusParameters.RomulusN ? ProcessingBufferType.Buffered : ProcessingBufferType.Immediate, AADOperatorType.Counter, romulusParameters == RomulusParameters.RomulusM ? DataOperatorType.Stream : DataOperatorType.Counter); } @@ -115,186 +115,227 @@ private interface Instance void reset(); } -// private class RomulusM -// implements Instance -// { -// byte[] mac_s = new byte[16]; -// byte[] mac_CNT = new byte[7]; -// -// byte[] s = new byte[16]; -// byte[] CNT = new byte[7]; -// boolean isfirstblock; -// -// -// public RomulusM() -// { -// mac = new byte[16]; -// reset_lfsr_gf56(mac_CNT); -// isfirstblock = true; -// } -// -// @Override -// public void processFinalBlock(byte[] output, int outOff) -// { -// byte w = 48; -// if ((aadLen & 31) == 0 && aadLen != 0) -// { -// w ^= 8; -// } -// else if ((aadLen & 31) < AD_BLK_LEN_HALF) -// { -// w ^= 2; -// } -// else if ((aadLen & 31) != AD_BLK_LEN_HALF) -// { -// w ^= 10; -// } -// if ((messegeLen & 31) == 0 && messegeLen != 0) -// { -// w ^= 4; -// } -// else if ((messegeLen & 31) < AD_BLK_LEN_HALF) -// { -// w ^= 1; -// } -// else if ((messegeLen & 31) != AD_BLK_LEN_HALF) -// { -// w ^= 5; -// } -// if (forEncryption) -// { -// if ((w & 8) == 0 && isfirstblock) -// { -// byte[] Temp = new byte[16]; -// int len8 = Math.min(messegeLen, AD_BLK_LEN_HALF); -// pad(m_buf, 0, Temp, AD_BLK_LEN_HALF, len8); -// block_cipher(mac_s, k, Temp, 0, mac_CNT, (byte)44); -// lfsr_gf56(mac_CNT); -// } -// else if (messegeLen == 0) -// { -// lfsr_gf56(mac_CNT); -// } -// } -// nonce_encryption(npub, mac_CNT, mac_s, k, w); -// // Tag generation -// g8A(mac_s, mac, 0); -// } -// -// @Override -// public void processBufferAAD(byte[] input, int inOff) -// { -// byte[] mp = new byte[16]; -// // Rho(S,A) pads an A block and XORs it to the internal state. -// pad(input, inOff, mp, 16, 16); -// for (int i = 0; i < 16; i++) -// { -// mac_s[i] = (byte)(mac_s[i] ^ input[inOff + i]); -// } -// lfsr_gf56(mac_CNT); -// inOff += 16; -// block_cipher(mac_s, k, input, inOff, CNT, (byte)40); -// lfsr_gf56(mac_CNT); -// } -// -// @Override -// public void processFinalAAD() -// { -// if (aadLen == 0) -// { -// // AD is an empty string -// lfsr_gf56(mac_CNT); -// } -// else if (m_aadPos != 0) -// { -// byte[] T = new byte[16]; -// pad(m_aad, 0, T, 16, m_aadPos); -// block_cipher(mac_s, k, T, 0, mac_CNT, (byte)40); -// lfsr_gf56(mac_CNT); -// if (m_aadPos > 16) -// { -// int len8 = Math.min(m_aadPos - 16, 16); -// pad(m_aad, 16, T, 16, len8); -// block_cipher(s, k, T, 0, CNT, (byte)40); -// lfsr_gf56(CNT); -// } -// } -// m_aadPos = 0; -// } -// -// @Override -// public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) -// { -// if ((aadLen == 0 || ((aadLen & 31) > 0 && (aadLen & 31) < 15))) -// { -// block_cipher(mac_s, k, input, inOff, mac_CNT, (byte)44); -// lfsr_gf56(mac_CNT); -// for (int i = 0; i < AD_BLK_LEN_HALF; i++) -// { -// mac_s[i] = (byte)(mac_s[i] ^ input[inOff + 16 + i]); -// } -// lfsr_gf56(mac_CNT); -// } -// else -// { -// for (int i = 0; i < AD_BLK_LEN_HALF; i++) -// { -// mac_s[i] = (byte)(mac_s[i] ^ input[inOff + i]); -// } -// lfsr_gf56(mac_CNT); -// block_cipher(mac_s, k, input, inOff + AD_BLK_LEN_HALF, mac_CNT, (byte)44); -// lfsr_gf56(mac_CNT); -// } -// if (isfirstblock) -// { -// isfirstblock = false; -// nonce_encryption(npub, CNT, s, k, (byte)36); -// } -// rho(input, inOff, output, outOff, s, AD_BLK_LEN_HALF); -// lfsr_gf56(CNT); -// } -// -// @Override -// public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) -// { -// if (isfirstblock) -// { -// isfirstblock = false; -// nonce_encryption(npub, CNT, s, k, (byte)36); -// } -// rho(input, inOff, output, outOff, s, AD_BLK_LEN_HALF); -// lfsr_gf56(CNT); -// if ((aadLen == 0 || ((aadLen & 31) > 0 && (aadLen & 31) < 15))) -// { -// block_cipher(mac_s, k, output, outOff, mac_CNT, (byte)44); -// lfsr_gf56(mac_CNT); -// for (int i = 0; i < 16; i++) -// { -// mac_s[i] = (byte)(mac_s[i] ^ output[outOff + AD_BLK_LEN_HALF + i]); -// } -// lfsr_gf56(mac_CNT); -// } -// else -// { -// for (int i = 0; i < 16; i++) -// { -// mac_s[i] = (byte)(mac_s[i] ^ output[outOff + i]); -// } -// lfsr_gf56(mac_CNT); -// block_cipher(mac_s, k, output, outOff + AD_BLK_LEN_HALF, mac_CNT, (byte)44); -// lfsr_gf56(mac_CNT); -// } -// } -// -// @Override -// public void reset() -// { -// Arrays.clear(s); -// Arrays.clear(CNT); -// Arrays.clear(mac_s); -// Arrays.clear(mac_CNT); -// } -// } + private class RomulusM + implements Instance + { + byte[] mac_s = new byte[16]; + byte[] mac_CNT = new byte[7]; + + byte[] s = new byte[16]; + byte[] CNT = new byte[7]; + int offset; + boolean twist = true; + + public RomulusM() + { + } + + @Override + public void processFinalBlock(byte[] output, int outOff) + { + byte w = 48; + int adlen = aadOperator.getLen(); + int mlen = dataOperator.getLen() - (forEncryption ? 0 : MAC_SIZE); + byte[] m = ((StreamDataOperator)dataOperator).getBytes(); + mac = new byte[MAC_SIZE]; + int xlen, mOff = 0, mauth = 0; + xlen = mlen; + if ((adlen & 31) == 0 && adlen != 0) + { + w ^= 8; + } + else if ((adlen & 31) < AD_BLK_LEN_HALF) + { + w ^= 2; + } + else if ((adlen & 31) != AD_BLK_LEN_HALF) + { + w ^= 10; + } + if ((mlen & 31) == 0 && mlen != 0) + { + w ^= 4; + } + else if ((mlen & 31) < AD_BLK_LEN_HALF) + { + w ^= 1; + } + else if ((mlen & 31) != AD_BLK_LEN_HALF) + { + w ^= 5; + } + if (forEncryption) + { + if ((w & 8) == 0) + { + byte[] Temp = new byte[16]; + int len8 = Math.min(xlen, AD_BLK_LEN_HALF); + xlen -= len8; + pad(m, mOff, Temp, AD_BLK_LEN_HALF, len8); + block_cipher(mac_s, k, Temp, 0, mac_CNT, (byte)44); + lfsr_gf56(mac_CNT); + mOff += len8; + } + else if (mlen == 0) + { + lfsr_gf56(mac_CNT); + } + while (xlen > 0) + { + offset = mOff; + xlen = ad_encryption(m, mOff, mac_s, k, xlen, mac_CNT, (byte)44); + mOff = offset; + } + nonce_encryption(npub, mac_CNT, mac_s, k, w); + // Tag generation + g8A(mac_s, mac, 0); + mOff -= mlen; + } + else + { + System.arraycopy(m, mlen, mac, 0, MAC_SIZE); + } + reset_lfsr_gf56(CNT); + System.arraycopy(mac, 0, s, 0, AD_BLK_LEN_HALF); + if (mlen > 0) + { + nonce_encryption(npub, CNT, s, k, (byte)36); + while (mlen > AD_BLK_LEN_HALF) + { + mlen = mlen - AD_BLK_LEN_HALF; + rho(m, mOff, output, outOff, s, AD_BLK_LEN_HALF); + outOff += AD_BLK_LEN_HALF; + mOff += AD_BLK_LEN_HALF; + lfsr_gf56(CNT); + nonce_encryption(npub, CNT, s, k, (byte)36); + } + rho(m, mOff, output, outOff, s, mlen); + } + if (!forEncryption) + { + if ((w & 8) == 0) + { + byte[] Temp = new byte[16]; + int len8 = Math.min(xlen, AD_BLK_LEN_HALF); + xlen -= len8; + pad(output, mauth, Temp, AD_BLK_LEN_HALF, len8); + block_cipher(mac_s, k, Temp, 0, mac_CNT, (byte)44); + lfsr_gf56(mac_CNT); + mauth += len8; + } + else if (mlen == 0) + { + lfsr_gf56(mac_CNT); + } + while (xlen > 0) + { + offset = mauth; + xlen = ad_encryption(output, mauth, mac_s, k, xlen, mac_CNT, (byte)44); + mauth = offset; + } + nonce_encryption(npub, mac_CNT, mac_s, k, w); + // Tag generation + g8A(mac_s, mac, 0); + System.arraycopy(m, dataOperator.getLen() - MAC_SIZE, m_buf, 0, MAC_SIZE); + m_bufPos = 0; + } + } + + int ad_encryption(byte[] A, int AOff, byte[] s, byte[] k, int adlen, byte[] CNT, byte D) + { + byte[] T = new byte[16]; + byte[] mp = new byte[16]; + int n = 16; + int i, len8; + len8 = Math.min(adlen, n); + adlen -= len8; + // Rho(S,A) pads an A block and XORs it to the internal state. + pad(A, AOff, mp, n, len8); + for (i = 0; i < n; i++) + { + s[i] = (byte)(s[i] ^ mp[i]); + } + offset = AOff += len8; + lfsr_gf56(CNT); + if (adlen != 0) + { + len8 = Math.min(adlen, n); + adlen -= len8; + pad(A, AOff, T, n, len8); + offset = AOff + len8; + block_cipher(s, k, T, 0, CNT, D); + lfsr_gf56(CNT); + } + return adlen; + } + + @Override + public void processBufferAAD(byte[] input, int inOff) + { + if (twist) + { + for (int i = 0; i < 16; i++) + { + mac_s[i] = (byte)(mac_s[i] ^ input[inOff + i]); + } + } + else + { + block_cipher(mac_s, k, input, inOff, mac_CNT, (byte)40); + } + twist = !twist; + lfsr_gf56(mac_CNT); + } + + @Override + public void processFinalAAD() + { + if (aadOperator.getLen() == 0) + { + // AD is an empty string + lfsr_gf56(mac_CNT); + } + else if (m_aadPos != 0) + { + Arrays.fill(m_aad, m_aadPos, BlockSize - 1, (byte)0); + m_aad[BlockSize - 1] = (byte)(m_aadPos & 0x0f); + if (twist) + { + for (int i = 0; i < BlockSize; i++) + { + mac_s[i] = (byte)(mac_s[i] ^ m_aad[i]); + } + } + else + { + block_cipher(mac_s, k, m_aad, 0, mac_CNT, (byte)40); + } + lfsr_gf56(mac_CNT); + } + m_aadPos = 0; + m_bufPos = dataOperator.getLen(); + } + + @Override + public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) + { + } + + @Override + public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) + { + } + + @Override + public void reset() + { + Arrays.clear(s); + Arrays.clear(mac_s); + reset_lfsr_gf56(mac_CNT); + reset_lfsr_gf56(CNT); + twist = true; + } + } private class RomulusN implements Instance diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java index 1a50267aeb..1d460fc514 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java @@ -487,7 +487,7 @@ static void implTestVectorsEngine(AEADCipher cipher, String path, String filenam mismatch("Reccover Keystream " + map.get("Count"), (String)map.get("PT"), rv, test); } } - System.out.println("pass " + count); + //System.out.println("pass " + count); map.clear(); } else @@ -536,7 +536,7 @@ static void implTestBufferingEngine(int keySize, int ivSize, final int macSize, if (0 != cipher.getUpdateOutputSize(0)) { - test.fail(""); + test.fail("fail in implTestBufferingEngine encryption"); } length += cipher.processBytes(plaintext, split, plaintextLength - split, output, length); @@ -560,7 +560,7 @@ static void implTestBufferingEngine(int keySize, int ivSize, final int macSize, if (0 != cipher.getUpdateOutputSize(0)) { - test.fail(""); + test.fail("fail in implTestBufferingEngine decryption"); } length += cipher.processBytes(ciphertext, split, ciphertextLength - split, output, length); @@ -702,7 +702,11 @@ static void implTestExceptionsEngine(int keysize, int ivsize, SimpleTest test, I try { cipher.processAADByte((byte)0); - test.fail("processAADByte(s) cannot be called after encryption/decryption"); + // Romuls-M stores message into Stream, so the procssAADbyte(s) is allowed + if (!cipher.getAlgorithmName().equals("Romulus-M")) + { + test.fail("processAADByte(s) cannot be called after encryption/decryption"); + } } catch (IllegalStateException e) { @@ -711,7 +715,10 @@ static void implTestExceptionsEngine(int keysize, int ivsize, SimpleTest test, I try { cipher.processAADBytes(new byte[]{0}, 0, 1); - test.fail("processAADByte(s) cannot be called once only"); + if (!cipher.getAlgorithmName().equals("Romulus-M")) + { + test.fail("processAADByte(s) cannot be called once only"); + } } catch (IllegalStateException e) { @@ -742,7 +749,10 @@ static void implTestExceptionsEngine(int keysize, int ivsize, SimpleTest test, I { int need = cipher.getUpdateOutputSize(64); cipher.processBytes(new byte[64], 0, 64, new byte[need], 1); - test.fail("output for processBytes is too short"); + if (!cipher.getAlgorithmName().equals("Romulus-M")) + { + test.fail("output for processBytes is too short"); + } } catch (OutputLengthException e) { diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java index 6f92bbaf47..6af86de0a8 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java @@ -27,10 +27,19 @@ public String getName() public void performTest() throws Exception { - //CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM), "crypto/romulus", "m_LWC_AEAD_KAT_128_128.txt", this); + CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM), "crypto/romulus", "m_LWC_AEAD_KAT_128_128.txt", this); CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT), "crypto/romulus", "t_LWC_AEAD_KAT_128_128.txt", this); CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN), "crypto/romulus", "n_LWC_AEAD_KAT_128_128.txt", this); + //TODO: StreamDataOperator does not suit for implTestBufferingEngine +// CipherTest.implTestBufferingEngine(16, 16, 128, this, new CipherTest.Instance() +// { +// @Override +// public AEADCipher createInstance() +// { +// return new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM); +// } +// }); CipherTest.implTestBufferingEngine(16, 16, 128, this, new CipherTest.Instance() { @Override @@ -47,6 +56,15 @@ public AEADCipher createInstance() return new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN); } }); + //TODO: StreamDataOperator does not suit for implTestExceptionsEngine +// CipherTest.implTestExceptionsEngine(16, 16, this, new CipherTest.Instance() +// { +// @Override +// public AEADCipher createInstance() +// { +// return new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM); +// } +// }); CipherTest.implTestExceptionsEngine(16, 16, this, new CipherTest.Instance() { @Override @@ -63,13 +81,23 @@ public AEADCipher createInstance() return new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN); } }); + implTestParametersEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM), 16, 16, 16); implTestParametersEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT), 16, 16, 16); implTestParametersEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN), 16, 16, 16); + CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM)); CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT)); CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN)); + CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN)); + CipherTest.checkCipher(16, 16, 40, 128, new CipherTest.Instance() + { + public AEADCipher createInstance() + { + return new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM); + } + }); CipherTest.checkCipher(16, 16, 40, 128, new CipherTest.Instance() { public AEADCipher createInstance() From 11a0bc7e1d99132cd49a2e8d2711a0f5c3abc816 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 24 Jan 2025 14:36:13 +1030 Subject: [PATCH 046/890] Update ISAPDigest --- .../crypto/digests/AsconBaseDigest.java | 23 +++-- .../crypto/digests/AsconCXof128.java | 3 +- .../crypto/digests/AsconHash256.java | 2 - .../crypto/digests/BufferBaseDigest.java | 90 ++++++++++++++++++ .../crypto/digests/ISAPDigest.java | 92 ++++++------------- .../bouncycastle/crypto/test/DigestTest.java | 88 ++++++++++++++++++ .../bouncycastle/crypto/test/ISAPTest.java | 74 +-------------- 7 files changed, 223 insertions(+), 149 deletions(-) create mode 100644 core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java index cfe6f466df..f71d58813a 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java @@ -14,13 +14,12 @@ abstract class AsconBaseDigest protected long x2; protected long x3; protected long x4; - protected final int CRYPTO_BYTES = 32; - protected final int ASCON_HASH_RATE = 8; + protected final int DigestSize = 32; + protected final int BlockSize = 8; protected int ASCON_PB_ROUNDS = 12; - protected final byte[] m_buf = new byte[ASCON_HASH_RATE]; + protected final byte[] m_buf = new byte[BlockSize]; protected int m_bufPos = 0; - private void round(long C) { long t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); @@ -70,20 +69,20 @@ protected void p(int nr) @Override public int getDigestSize() { - return CRYPTO_BYTES; + return DigestSize; } @Override public int getByteLength() { - return ASCON_HASH_RATE; + return BlockSize; } @Override public void update(byte in) { m_buf[m_bufPos] = in; - if (++m_bufPos == ASCON_HASH_RATE) + if (++m_bufPos == BlockSize) { x0 ^= loadBytes(m_buf, 0); p(ASCON_PB_ROUNDS); @@ -127,7 +126,7 @@ public void update(byte[] input, int inOff, int len) @Override public int doFinal(byte[] output, int outOff) { - return hash(output, outOff, CRYPTO_BYTES); + return hash(output, outOff, DigestSize); } protected void padAndAbsorb() @@ -140,12 +139,12 @@ protected void padAndAbsorb() protected void squeeze(byte[] output, int outOff, int len) { /* squeeze full output blocks */ - while (len > ASCON_HASH_RATE) + while (len > BlockSize) { setBytes(x0, output, outOff); p(ASCON_PB_ROUNDS); - outOff += ASCON_HASH_RATE; - len -= ASCON_HASH_RATE; + outOff += BlockSize; + len -= BlockSize; } /* squeeze final output block */ setBytes(x0, output, outOff, len); @@ -154,7 +153,7 @@ protected void squeeze(byte[] output, int outOff, int len) protected int hash(byte[] output, int outOff, int outLen) { - if (CRYPTO_BYTES + outOff > output.length) + if (DigestSize + outOff > output.length) { throw new OutputLengthException("output buffer is too short"); } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java index f54c622675..d9bca9abbd 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java @@ -112,7 +112,7 @@ public String getAlgorithmName() @Override public int doOutput(byte[] output, int outOff, int outLen) { - if (CRYPTO_BYTES + outOff > output.length) + if (DigestSize + outOff > output.length) { throw new OutputLengthException("output buffer is too short"); } @@ -122,7 +122,6 @@ public int doOutput(byte[] output, int outOff, int outLen) return outLen; } - @Override public int doFinal(byte[] output, int outOff, int outLen) { diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java index 842346e155..d459c37b6a 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java @@ -52,8 +52,6 @@ public String getAlgorithmName() return "Ascon-Hash256"; } - - @Override public void reset() { diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java new file mode 100644 index 0000000000..c8661598be --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java @@ -0,0 +1,90 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.ExtendedDigest; +import org.bouncycastle.crypto.OutputLengthException; + +public abstract class BufferBaseDigest + implements ExtendedDigest +{ + protected int DigestSize; + protected int BlockSize; + protected byte[] m_buf; + protected int m_bufPos; + protected String algorithmName; + + @Override + public String getAlgorithmName() + { + return algorithmName; + } + + @Override + public int getDigestSize() + { + return DigestSize; + } + + @Override + public int getByteLength() + { + return BlockSize; + } + + @Override + public void update(byte in) + { + m_buf[m_bufPos] = in; + if (++m_bufPos == BlockSize) + { + processBytes(m_buf, 0); + m_bufPos = 0; + } + } + + @Override + public void update(byte[] input, int inOff, int len) + { + if ((inOff + len) > input.length) + { + throw new DataLengthException("input buffer too short"); + } + int available = BlockSize - m_bufPos; + if (len < available) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return; + } + int inPos = 0; + if (m_bufPos > 0) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inPos += available; + processBytes(m_buf, 0); + } + int remaining; + while ((remaining = len - inPos) >= BlockSize) + { + processBytes(input, inOff + inPos); + inPos += 8; + } + System.arraycopy(input, inOff + inPos, m_buf, 0, remaining); + m_bufPos = remaining; + } + + @Override + public int doFinal(byte[] output, int outOff) + { + if (DigestSize + outOff > output.length) + { + throw new OutputLengthException("output buffer is too short"); + } + finish(output, outOff); + reset(); + return DigestSize; + } + + protected abstract void processBytes(byte[] input, int inOff); + protected abstract void finish(byte[] output, int outOff); +} diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java index 9120e7592d..a72a26a4ff 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java @@ -1,10 +1,6 @@ package org.bouncycastle.crypto.digests; -import java.io.ByteArrayOutputStream; - -import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.Digest; -import org.bouncycastle.crypto.OutputLengthException; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; /** @@ -16,11 +12,19 @@ */ public class ISAPDigest - implements Digest + extends BufferBaseDigest { private long x0, x1, x2, x3, x4; private long t0, t1, t2, t3, t4; - private ByteArrayOutputStream buffer = new ByteArrayOutputStream(); + + public ISAPDigest() + { + DigestSize = 32; + BlockSize = 8; + m_buf = new byte[BlockSize]; + algorithmName = "ISAP Hash"; + reset(); + } private void ROUND(long C) { @@ -64,64 +68,22 @@ protected long U64BIG(long x) } @Override - public String getAlgorithmName() - { - return "ISAP Hash"; - } - - @Override - public int getDigestSize() - { - return 32; - } - - @Override - public void update(byte input) - { - buffer.write(input); - } - - @Override - public void update(byte[] input, int inOff, int len) + protected void processBytes(byte[] input, int inOff) { - if ((inOff + len) > input.length) - { - throw new DataLengthException("input buffer too short"); - } - buffer.write(input, inOff, len); + /* absorb */ + x0 ^= Pack.bigEndianToLong(input, inOff); + P12(); } @Override - public int doFinal(byte[] out, int outOff) + protected void finish(byte[] output, int outOff) { - if (32 + outOff > out.length) - { - throw new OutputLengthException("output buffer is too short"); - } - t0 = t1 = t2 = t3 = t4 = 0; - /* init state */ - x0 = -1255492011513352131L; - x1 = -8380609354527731710L; - x2 = -5437372128236807582L; - x3 = 4834782570098516968L; - x4 = 3787428097924915520L; - /* absorb */ - byte[] input = buffer.toByteArray(); - int len = input.length; - long[] in64 = new long[len >> 3]; - Pack.littleEndianToLong(input, 0, in64, 0, in64.length); - int idx = 0; - while (len >= 8) - { - x0 ^= U64BIG(in64[idx++]); - P12(); - len -= 8; - } /* absorb final input block */ - x0 ^= 0x80L << ((7 - len) << 3); - while (len > 0) + int idx; + x0 ^= 0x80L << ((7 - m_bufPos) << 3); + while (m_bufPos > 0) { - x0 ^= (input[(idx << 3) + --len] & 0xFFL) << ((7 - len) << 3); + x0 ^= (m_buf[--m_bufPos] & 0xFFL) << ((7 - m_bufPos) << 3); } P12(); // squeeze @@ -133,14 +95,20 @@ public int doFinal(byte[] out, int outOff) } /* squeeze final output block */ out64[idx] = U64BIG(x0); - Pack.longToLittleEndian(out64, out, outOff); - buffer.reset(); - return 32; + Pack.longToLittleEndian(out64, output, outOff); } @Override public void reset() { - buffer.reset(); + t0 = t1 = t2 = t3 = t4 = 0; + /* init state */ + x0 = -1255492011513352131L; + x1 = -8380609354527731710L; + x2 = -5437372128236807582L; + x3 = 4834782570098516968L; + x4 = 3787428097924915520L; + Arrays.clear(m_buf); + m_bufPos = 0; } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java index 50fb248271..79b0e173be 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java @@ -1,9 +1,18 @@ package org.bouncycastle.crypto.test; +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Random; +import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.ExtendedDigest; +import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.digests.EncodableDigest; +import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Memoable; import org.bouncycastle.util.encoders.Hex; @@ -274,4 +283,83 @@ static void checkDigestReset(final SimpleTest test, final Digest pDigest) throw new TestFailedException(SimpleTestResult.failed(test,"Digest " + pDigest.getAlgorithmName() + " does not reset properly on doFinal()")); } } + + static void implTestExceptionsAndParametersDigest(final SimpleTest test, final Digest pDigest, final int digestsize) + { + if (pDigest.getDigestSize() != digestsize) + { + test.fail(pDigest.getAlgorithmName() + ": digest size is not correct"); + } + + try + { + pDigest.update(new byte[1], 1, 1); + test.fail(pDigest.getAlgorithmName() + ": input for update is too short"); + } + catch (DataLengthException e) + { + //expected + } + + try + { + pDigest.doFinal(new byte[pDigest.getDigestSize() - 1], 2); + test.fail(pDigest.getAlgorithmName() + ": output for dofinal is too short"); + } + catch (OutputLengthException e) + { + //expected + } + } + + static void implTestVectorsDigest(SimpleTest test, ExtendedDigest digest, String path, String filename) + throws Exception + { + Random random = new Random(); + InputStream src = TestResourceFinder.findTestResource(path, filename); + BufferedReader bin = new BufferedReader(new InputStreamReader(src)); + String line; + HashMap map = new HashMap(); + while ((line = bin.readLine()) != null) + { + int a = line.indexOf('='); + if (a < 0) + { + byte[] ptByte = Hex.decode((String)map.get("Msg")); + byte[] expected = Hex.decode((String)map.get("MD")); + + byte[] hash = new byte[digest.getDigestSize()]; + + digest.update(ptByte, 0, ptByte.length); + digest.doFinal(hash, 0); + if (!Arrays.areEqual(hash, expected)) + { + mismatch(test,"Keystream " + map.get("Count"), (String)map.get("MD"), hash); + } + + if (ptByte.length > 1) + { + int split = random.nextInt(ptByte.length - 1) + 1; + digest.update(ptByte, 0, split); + digest.update(ptByte, split, ptByte.length - split); + digest.doFinal(hash, 0); + if (!Arrays.areEqual(hash, expected)) + { + mismatch(test,"Keystream " + map.get("Count"), (String)map.get("MD"), hash); + } + } + + map.clear(); + } + else + { + map.put(line.substring(0, a).trim(), line.substring(a + 1).trim()); + } + } + } + + private static void mismatch(SimpleTest test, String name, String expected, byte[] found) + { + test.fail("mismatch on " + name, expected, new String(Hex.encode(found))); + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java index a6e50db4cc..32c0978b75 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java @@ -8,7 +8,6 @@ import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.digests.ISAPDigest; @@ -33,6 +32,8 @@ public String getName() public void performTest() throws Exception { + DigestTest.implTestVectorsDigest(this, new ISAPDigest(), "crypto/isap", "LWC_HASH_KAT_256.txt"); + DigestTest.checkDigestReset(this, new ISAPDigest()); testVectors("isapa128av20", IsapType.ISAP_A_128A); testVectors("isapa128v20", IsapType.ISAP_A_128); testVectors("isapk128av20", IsapType.ISAP_K_128A); @@ -49,9 +50,8 @@ public void performTest() ISAP = new ISAPEngine(IsapType.ISAP_A_128); testExceptions(ISAP, ISAP.getKeyBytesSize(), ISAP.getIVBytesSize(), ISAP.getBlockSize()); testParameters(ISAP, 16, 16, 16); - testExceptions(new ISAPDigest(), 32); + DigestTest.implTestExceptionsAndParametersDigest(this, new ISAPDigest(), 32); - testVectors(); CipherTest.checkCipher(32, 16, 100, 128, new CipherTest.Instance() { @Override @@ -162,74 +162,6 @@ private void testVectors(String filename, IsapType isapType) //System.out.print.println(filename + " pass"); } - private void testVectors() - throws Exception - { - ISAPDigest isap = new ISAPDigest(); - InputStream src = TestResourceFinder.findTestResource("crypto/isap", "LWC_HASH_KAT_256.txt"); - BufferedReader bin = new BufferedReader(new InputStreamReader(src)); - String line; - byte[] ptByte; - HashMap map = new HashMap(); - while ((line = bin.readLine()) != null) - { - int a = line.indexOf('='); - if (a < 0) - { -// if (!map.get("Count").equals("10")) -// { -// continue; -// } - ptByte = Hex.decode((String)map.get("Msg")); - isap.update(ptByte, 0, ptByte.length); - byte[] hash = new byte[32]; - isap.doFinal(hash, 0); - if (!areEqual(hash, Hex.decode((String)map.get("MD")))) - { - mismatch("Keystream " + map.get("Count"), (String)map.get("MD"), hash); - } -// else -// { -// System.out.println(map.get("Count") + " pass"); -// } - map.clear(); - isap.reset(); - } - else - { - map.put(line.substring(0, a).trim(), line.substring(a + 1).trim()); - } - } - //System.out.print.println("ISAP Hash pass"); - } - - private void testExceptions(Digest digest, int digestsize) - { - if (digest.getDigestSize() != digestsize) - { - fail(digest.getAlgorithmName() + ": digest size is not correct"); - } - - try - { - digest.update(new byte[1], 1, 1); - fail(digest.getAlgorithmName() + ": input for update is too short"); - } - catch (DataLengthException e) - { - //expected - } - try - { - digest.doFinal(new byte[digest.getDigestSize() - 1], 2); - fail(digest.getAlgorithmName() + ": output for dofinal is too short"); - } - catch (DataLengthException e) - { - //expected - } - //System.out.print.println(digest.getAlgorithmName() + " test Exceptions pass"); - } private void testExceptions(AEADCipher aeadBlockCipher, int keysize, int ivsize, int blocksize) throws Exception From f82e2421e286002101b5ac7fd80bd26474d99919 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 24 Jan 2025 15:28:07 +1030 Subject: [PATCH 047/890] Update PhotonBeetleDigest --- .../crypto/digests/BufferBaseDigest.java | 2 +- .../crypto/digests/PhotonBeetleDigest.java | 129 +++++++----------- .../bouncycastle/crypto/test/DigestTest.java | 47 ++++--- .../bouncycastle/crypto/test/ISAPTest.java | 2 +- .../crypto/test/PhotonBeetleTest.java | 38 +----- 5 files changed, 80 insertions(+), 138 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java index c8661598be..07e037ea0c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java @@ -67,7 +67,7 @@ public void update(byte[] input, int inOff, int len) while ((remaining = len - inPos) >= BlockSize) { processBytes(input, inOff + inPos); - inPos += 8; + inPos += BlockSize; } System.arraycopy(input, inOff + inPos, m_buf, 0, remaining); m_bufPos = remaining; diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java index cdf424f18b..f36a150257 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java @@ -1,10 +1,5 @@ package org.bouncycastle.crypto.digests; -import java.io.ByteArrayOutputStream; - -import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.Digest; -import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Bytes; @@ -16,25 +11,14 @@ *

*/ public class PhotonBeetleDigest - implements Digest + extends BufferBaseDigest { - private byte[] state; - private byte[][] state_2d; - private ByteArrayOutputStream buffer = new ByteArrayOutputStream(); - private final int INITIAL_RATE_INBYTES = 16; - private int RATE_INBYTES = 4; - private int SQUEEZE_RATE_INBYTES = 16; - private int STATE_INBYTES = 32; - private int TAG_INBYTES = 32; - private int LAST_THREE_BITS_OFFSET = 5; - private int ROUND = 12; - private int D = 8; - private int Dq = 3; - private int Dr = 7; - private int DSquare = 64; - private int S = 4; - private int S_1 = 3; - private byte[][] RC = {//[D][12] + private final byte[] state; + private final byte[][] state_2d; + private final int STATE_INBYTES = 32; + private final int D = 8; + private int blockCount; + private static final byte[][] RC = {//[D][12] {1, 3, 7, 14, 13, 11, 6, 12, 9, 2, 5, 10}, {0, 2, 6, 15, 12, 10, 7, 13, 8, 3, 4, 11}, {2, 0, 4, 13, 14, 8, 5, 15, 10, 1, 6, 9}, @@ -44,7 +28,7 @@ public class PhotonBeetleDigest {13, 15, 11, 2, 1, 7, 10, 0, 5, 14, 9, 6}, {9, 11, 15, 6, 5, 3, 14, 4, 1, 10, 13, 2} }; - private byte[][] MixColMatrix = { //[D][D] + private static final byte[][] MixColMatrix = { //[D][D] {2, 4, 2, 11, 2, 8, 5, 6}, {12, 9, 8, 13, 7, 7, 5, 2}, {4, 4, 13, 13, 9, 4, 13, 9}, @@ -55,106 +39,89 @@ public class PhotonBeetleDigest {15, 1, 13, 10, 5, 10, 2, 3} }; - private byte[] sbox = {12, 5, 6, 11, 9, 0, 10, 13, 3, 14, 15, 8, 4, 7, 1, 2}; + private static final byte[] sbox = {12, 5, 6, 11, 9, 0, 10, 13, 3, 14, 15, 8, 4, 7, 1, 2}; public PhotonBeetleDigest() { state = new byte[STATE_INBYTES]; state_2d = new byte[D][D]; + DigestSize = 32; + algorithmName = "Photon-Beetle Hash"; + BlockSize = 4; + blockCount = 0; + m_buf = new byte[BlockSize]; } @Override - public String getAlgorithmName() - { - return "Photon-Beetle Hash"; - } - - @Override - public int getDigestSize() - { - return TAG_INBYTES; - } - - @Override - public void update(byte input) + protected void processBytes(byte[] input, int inOff) { - buffer.write(input); - } - - @Override - public void update(byte[] input, int inOff, int len) - { - if ((inOff + len) > input.length) + if (blockCount < 4) + { + System.arraycopy(input, inOff, state, blockCount << 2, BlockSize); + } + else { - throw new DataLengthException("input buffer too short"); + PHOTON_Permutation(); + Bytes.xorTo(BlockSize, input, inOff, state, 0); } - buffer.write(input, inOff, len); + blockCount++; } @Override - public int doFinal(byte[] output, int outOff) + protected void finish(byte[] output, int outOff) { - if (32 + outOff > output.length) + int LAST_THREE_BITS_OFFSET = 5; + if (m_bufPos == 0 && blockCount == 0) { - throw new OutputLengthException("output buffer is too short"); + state[STATE_INBYTES - 1] ^= 1 << LAST_THREE_BITS_OFFSET; } - byte[] input = buffer.toByteArray(); - int inlen = input.length; - if (inlen == 0) + else if (blockCount < 4) { - state[STATE_INBYTES - 1] ^= 1 << LAST_THREE_BITS_OFFSET; + System.arraycopy(m_buf, 0, state, blockCount << 2, m_bufPos); + state[(blockCount << 2) + m_bufPos] ^= 0x01; // ozs + state[STATE_INBYTES - 1] ^= (byte)1 << LAST_THREE_BITS_OFFSET; } - else if (inlen <= INITIAL_RATE_INBYTES) + else if (blockCount == 4 && m_bufPos == 0) { - System.arraycopy(input, 0, state, 0, inlen); - if (inlen < INITIAL_RATE_INBYTES) - { - state[inlen] ^= 0x01; // ozs - } - state[STATE_INBYTES - 1] ^= (inlen < INITIAL_RATE_INBYTES ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET; + state[STATE_INBYTES - 1] ^= (byte)2 << LAST_THREE_BITS_OFFSET; } else { - System.arraycopy(input, 0, state, 0, INITIAL_RATE_INBYTES); - inlen -= INITIAL_RATE_INBYTES; - int Dlen_inblocks = (inlen + RATE_INBYTES - 1) / RATE_INBYTES; - int i, LastDBlocklen; - for (i = 0; i < Dlen_inblocks - 1; i++) - { - PHOTON_Permutation(); - Bytes.xorTo(RATE_INBYTES, input, INITIAL_RATE_INBYTES + i * RATE_INBYTES, state, 0); - } PHOTON_Permutation(); - LastDBlocklen = inlen - i * RATE_INBYTES; - Bytes.xorTo(LastDBlocklen, input, INITIAL_RATE_INBYTES + i * RATE_INBYTES, state, 0); - if (LastDBlocklen < RATE_INBYTES) + Bytes.xorTo(m_bufPos, m_buf, 0, state, 0); + if (m_bufPos < BlockSize) { - state[LastDBlocklen] ^= 0x01; // ozs + state[m_bufPos] ^= 0x01; // ozs } - state[STATE_INBYTES - 1] ^= (inlen % RATE_INBYTES == 0 ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET; + state[STATE_INBYTES - 1] ^= (m_bufPos == 0 ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET; } PHOTON_Permutation(); + int SQUEEZE_RATE_INBYTES = 16; System.arraycopy(state, 0, output, outOff, SQUEEZE_RATE_INBYTES); PHOTON_Permutation(); - System.arraycopy(state, 0, output, outOff + SQUEEZE_RATE_INBYTES, TAG_INBYTES - SQUEEZE_RATE_INBYTES); - reset(); - return TAG_INBYTES; + System.arraycopy(state, 0, output, outOff + SQUEEZE_RATE_INBYTES, DigestSize - SQUEEZE_RATE_INBYTES); } @Override public void reset() { - buffer.reset(); Arrays.fill(state, (byte)0); + blockCount = 0; + Arrays.clear(m_buf); + m_bufPos = 0; } void PHOTON_Permutation() { int i, j, k; + int DSquare = 64; + int dr = 7; + int dq = 3; for (i = 0; i < DSquare; i++) { - state_2d[i >>> Dq][i & Dr] = (byte)(((state[i >> 1] & 0xFF) >>> (4 * (i & 1))) & 0xf); + state_2d[i >>> dq][i & dr] = (byte)(((state[i >> 1] & 0xFF) >>> (4 * (i & 1))) & 0xf); } + int ROUND = 12; for (int round = 0; round < ROUND; round++) { //AddKey @@ -210,7 +177,7 @@ void PHOTON_Permutation() } for (i = 0; i < DSquare; i += 2) { - state[i >>> 1] = (byte)(((state_2d[i >>> Dq][i & Dr] & 0xf)) | ((state_2d[i >>> Dq][(i + 1) & Dr] & 0xf) << 4)); + state[i >>> 1] = (byte)(((state_2d[i >>> dq][i & dr] & 0xf)) | ((state_2d[i >>> dq][(i + 1) & dr] & 0xf) << 4)); } } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java index 79b0e173be..b49bbff4ca 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java @@ -36,16 +36,16 @@ public abstract class DigestTest this.input = input; this.results = results; } - + public String getName() { return digest.getAlgorithmName(); } - + public void performTest() { byte[] resBuf = new byte[digest.getDigestSize()]; - + for (int i = 0; i < input.length - 1; i++) { byte[] m = toByteArray(input[i]); @@ -57,7 +57,7 @@ public void performTest() byte[] lastV = toByteArray(input[input.length - 1]); byte[] lastDigest = Hex.decode(results[input.length - 1]); - + vectorTest(digest, input.length - 1, resBuf, lastV, Hex.decode(results[input.length - 1])); testClone(resBuf, lastV, lastDigest); @@ -107,13 +107,13 @@ private void testMemo(byte[] resBuf, byte[] input, byte[] expected) { Memoable m = (Memoable)digest; - digest.update(input, 0, input.length/2); + digest.update(input, 0, input.length / 2); // copy the Digest Memoable copy1 = m.copy(); Memoable copy2 = copy1.copy(); - digest.update(input, input.length/2, input.length - input.length/2); + digest.update(input, input.length / 2, input.length - input.length / 2); digest.doFinal(resBuf, 0); if (!areEqual(expected, resBuf)) @@ -123,7 +123,7 @@ private void testMemo(byte[] resBuf, byte[] input, byte[] expected) m.reset(copy1); - digest.update(input, input.length/2, input.length - input.length/2); + digest.update(input, input.length / 2, input.length - input.length / 2); digest.doFinal(resBuf, 0); if (!areEqual(expected, resBuf)) @@ -133,7 +133,7 @@ private void testMemo(byte[] resBuf, byte[] input, byte[] expected) Digest md = (Digest)copy2; - md.update(input, input.length/2, input.length - input.length/2); + md.update(input, input.length / 2, input.length - input.length / 2); md.doFinal(resBuf, 0); if (!areEqual(expected, resBuf)) @@ -149,7 +149,7 @@ private void testClone(byte[] resBuf, byte[] input, byte[] expected) // clone the Digest Digest d = cloneDigest(digest); - digest.update(input, input.length/2, input.length - input.length/2); + digest.update(input, input.length / 2, input.length - input.length / 2); digest.doFinal(resBuf, 0); if (!areEqual(expected, resBuf)) @@ -157,7 +157,7 @@ private void testClone(byte[] resBuf, byte[] input, byte[] expected) fail("failing clone vector test", results[results.length - 1], new String(Hex.encode(resBuf))); } - d.update(input, input.length/2, input.length - input.length/2); + d.update(input, input.length / 2, input.length - input.length / 2); d.doFinal(resBuf, 0); if (!areEqual(expected, resBuf)) @@ -169,15 +169,15 @@ private void testClone(byte[] resBuf, byte[] input, byte[] expected) protected byte[] toByteArray(String input) { byte[] bytes = new byte[input.length()]; - + for (int i = 0; i != bytes.length; i++) { bytes[i] = (byte)input.charAt(i); } - + return bytes; } - + private void vectorTest( Digest digest, int count, @@ -225,12 +225,12 @@ protected void millionATest( String expected) { byte[] resBuf = new byte[digest.getDigestSize()]; - + for (int i = 0; i < 1000000; i++) { digest.update((byte)'a'); } - + digest.doFinal(resBuf, 0); if (!areEqual(resBuf, Hex.decode(expected))) @@ -238,17 +238,17 @@ protected void millionATest( fail("Million a's failed", expected, new String(Hex.encode(resBuf))); } } - + protected void sixtyFourKTest( String expected) { byte[] resBuf = new byte[digest.getDigestSize()]; - + for (int i = 0; i < 65536; i++) { digest.update((byte)(i & 0xff)); } - + digest.doFinal(resBuf, 0); if (!areEqual(resBuf, Hex.decode(expected))) @@ -280,7 +280,7 @@ static void checkDigestReset(final SimpleTest test, final Digest pDigest) /* Check that we have the same result */ if (!java.util.Arrays.equals(myFirst, mySecond)) { - throw new TestFailedException(SimpleTestResult.failed(test,"Digest " + pDigest.getAlgorithmName() + " does not reset properly on doFinal()")); + throw new TestFailedException(SimpleTestResult.failed(test, "Digest " + pDigest.getAlgorithmName() + " does not reset properly on doFinal()")); } } @@ -325,6 +325,11 @@ static void implTestVectorsDigest(SimpleTest test, ExtendedDigest digest, String int a = line.indexOf('='); if (a < 0) { + int count = Integer.parseInt(map.get("Count")); + if (count != 6) + { + continue; + } byte[] ptByte = Hex.decode((String)map.get("Msg")); byte[] expected = Hex.decode((String)map.get("MD")); @@ -334,7 +339,7 @@ static void implTestVectorsDigest(SimpleTest test, ExtendedDigest digest, String digest.doFinal(hash, 0); if (!Arrays.areEqual(hash, expected)) { - mismatch(test,"Keystream " + map.get("Count"), (String)map.get("MD"), hash); + mismatch(test, "Keystream " + map.get("Count"), (String)map.get("MD"), hash); } if (ptByte.length > 1) @@ -345,7 +350,7 @@ static void implTestVectorsDigest(SimpleTest test, ExtendedDigest digest, String digest.doFinal(hash, 0); if (!Arrays.areEqual(hash, expected)) { - mismatch(test,"Keystream " + map.get("Count"), (String)map.get("MD"), hash); + mismatch(test, "Keystream " + map.get("Count"), (String)map.get("MD"), hash); } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java index 32c0978b75..ba91962ad4 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java @@ -34,6 +34,7 @@ public void performTest() { DigestTest.implTestVectorsDigest(this, new ISAPDigest(), "crypto/isap", "LWC_HASH_KAT_256.txt"); DigestTest.checkDigestReset(this, new ISAPDigest()); + DigestTest.implTestExceptionsAndParametersDigest(this, new ISAPDigest(), 32); testVectors("isapa128av20", IsapType.ISAP_A_128A); testVectors("isapa128v20", IsapType.ISAP_A_128); testVectors("isapk128av20", IsapType.ISAP_K_128A); @@ -50,7 +51,6 @@ public void performTest() ISAP = new ISAPEngine(IsapType.ISAP_A_128); testExceptions(ISAP, ISAP.getKeyBytesSize(), ISAP.getIVBytesSize(), ISAP.getBlockSize()); testParameters(ISAP, 16, 16, 16); - DigestTest.implTestExceptionsAndParametersDigest(this, new ISAPDigest(), 32); CipherTest.checkCipher(32, 16, 100, 128, new CipherTest.Instance() { diff --git a/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java b/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java index d5b64c06eb..1814847c0d 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java @@ -31,12 +31,15 @@ public String getName() public void performTest() throws Exception { + DigestTest.implTestVectorsDigest(this, new PhotonBeetleDigest(), "crypto/photonbeetle", "LWC_HASH_KAT_256.txt"); + DigestTest.checkDigestReset(this, new PhotonBeetleDigest()); + DigestTest.implTestExceptionsAndParametersDigest(this, new PhotonBeetleDigest(), 32); CipherTest.checkAEADCipherMultipleBlocks(this, 1024, 19, 100, 128, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb128)); CipherTest.checkAEADCipherMultipleBlocks(this, 1024, 19, 100, 128, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb32)); testVectors(PhotonBeetleEngine.PhotonBeetleParameters.pb32, "v32"); testVectors(PhotonBeetleEngine.PhotonBeetleParameters.pb128, "v128"); DigestTest.checkDigestReset(this, new PhotonBeetleDigest()); - testVectorsHash(); + PhotonBeetleEngine pb = new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb32); testExceptions(pb, pb.getKeyBytesSize(), pb.getIVBytesSize(), pb.getBlockSize()); testParameters(pb, 16, 16, 16); @@ -51,39 +54,6 @@ public void performTest() CipherTest.checkAEADCipherOutputSize(this, 16, 16, 4, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb32)); } - private void testVectorsHash() - throws Exception - { - PhotonBeetleDigest PhotonBeetle = new PhotonBeetleDigest(); - InputStream src = TestResourceFinder.findTestResource("crypto/photonbeetle", "LWC_HASH_KAT_256.txt"); - BufferedReader bin = new BufferedReader(new InputStreamReader(src)); - String line; - byte[] ptByte; - HashMap map = new HashMap(); - while ((line = bin.readLine()) != null) - { - int a = line.indexOf('='); - if (a < 0) - { - PhotonBeetle.reset(); - ptByte = Hex.decode((String)map.get("Msg")); - PhotonBeetle.update(ptByte, 0, ptByte.length); - byte[] hash = new byte[32]; - PhotonBeetle.doFinal(hash, 0); - if (!areEqual(hash, Hex.decode((String)map.get("MD")))) - { - mismatch("Keystream " + map.get("Count"), (String)map.get("MD"), hash); - } - map.clear(); - PhotonBeetle.reset(); - } - else - { - map.put(line.substring(0, a).trim(), line.substring(a + 1).trim()); - } - } - } - private void testVectors(PhotonBeetleEngine.PhotonBeetleParameters pbp, String filename) throws Exception { From 8a4cc61711c852d724b3465641e74772eef0f7d2 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 24 Jan 2025 15:45:11 +1030 Subject: [PATCH 048/890] Update XoodyakDigest --- .../crypto/digests/XoodyakDigest.java | 85 ++++++------------- .../bouncycastle/crypto/test/DigestTest.java | 10 +-- .../bouncycastle/crypto/test/XoodyakTest.java | 46 +--------- 3 files changed, 32 insertions(+), 109 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java index f48652c149..866675ca85 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java @@ -1,10 +1,5 @@ package org.bouncycastle.crypto.digests; -import java.io.ByteArrayOutputStream; - -import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.Digest; -import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Integers; import org.bouncycastle.util.Pack; @@ -17,21 +12,16 @@ */ public class XoodyakDigest - implements Digest + extends BufferBaseDigest { - private byte[] state; + private final byte[] state; private int phase; private MODE mode; - private int Rabsorb; private final int f_bPrime = 48; - private final int Rhash = 16; - private final int PhaseDown = 1; private final int PhaseUp = 2; - private final int MAXROUNDS = 12; - private final int TAGLEN = 16; + private int Cd; private final int[] RC = {0x00000058, 0x00000038, 0x000003C0, 0x000000D0, 0x00000120, 0x00000014, 0x00000060, 0x0000002C, 0x00000380, 0x000000F0, 0x000001A0, 0x00000012}; - private final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); enum MODE { @@ -41,69 +31,40 @@ enum MODE public XoodyakDigest() { + DigestSize = 32; state = new byte[48]; + BlockSize = 16; + m_buf = new byte[BlockSize]; + algorithmName = "Xoodyak Hash"; reset(); } @Override - public String getAlgorithmName() - { - return "Xoodyak Hash"; - } - - @Override - public int getDigestSize() - { - return 32; - } - - @Override - public void update(byte input) - { - buffer.write(input); - } - - @Override - public void update(byte[] input, int inOff, int len) + protected void processBytes(byte[] input, int inOff) { - if ((inOff + len) > input.length) + if (phase != PhaseUp) { - throw new DataLengthException("input buffer too short"); + Up(null, 0, 0, 0); } - buffer.write(input, inOff, len); - + Down(input, inOff, BlockSize, Cd); + Cd = 0; } @Override - public int doFinal(byte[] output, int outOff) + protected void finish(byte[] output, int outOff) { - if (32 + outOff > output.length) - { - throw new OutputLengthException("output buffer is too short"); - } - byte[] input = buffer.toByteArray(); - int inOff = 0; - int len = buffer.size(); - int Cd = 0x03; - int splitLen; - do + if (m_bufPos != 0) { if (phase != PhaseUp) { Up(null, 0, 0, 0); } - splitLen = Math.min(len, Rabsorb); - Down(input, inOff, splitLen, Cd); - Cd = 0; - inOff += splitLen; - len -= splitLen; + Down(m_buf, 0, m_bufPos, Cd); } - while (len != 0); + int TAGLEN = 16; Up(output, outOff, TAGLEN, 0x40); Down(null, 0, 0, 0); Up(output, outOff + TAGLEN, TAGLEN, 0); - reset(); - return 32; } @Override @@ -112,8 +73,9 @@ public void reset() Arrays.fill(state, (byte)0); phase = PhaseUp; mode = MODE.ModeHash; - Rabsorb = Rhash; - buffer.reset(); + Arrays.clear(m_buf); + m_bufPos = 0; + Cd = 0x03; } private void Up(byte[] Yi, int YiOff, int YiLen, int Cu) @@ -136,6 +98,7 @@ private void Up(byte[] Yi, int YiOff, int YiLen, int Cu) int a10 = Pack.littleEndianToInt(state, 40); int a11 = Pack.littleEndianToInt(state, 44); + int MAXROUNDS = 12; for (int i = 0; i < MAXROUNDS; ++i) { /* Theta: Column Parity Mixer */ @@ -164,7 +127,7 @@ private void Up(byte[] Yi, int YiOff, int YiLen, int Cu) a3 ^= e3; a7 ^= e3; a11 ^= e3; - + /* Rho-west: plane shift */ int b0 = a0; int b1 = a1; @@ -183,7 +146,7 @@ private void Up(byte[] Yi, int YiOff, int YiLen, int Cu) /* Iota: round ant */ b0 ^= RC[i]; - + /* Chi: non linear layer */ a0 = b0 ^ (~b4 & b8); a1 = b1 ^ (~b5 & b9); @@ -199,7 +162,7 @@ private void Up(byte[] Yi, int YiOff, int YiLen, int Cu) b9 ^= (~b1 & b5); b10 ^= (~b2 & b6); b11 ^= (~b3 & b7); - + /* Rho-east: plane shift */ a4 = Integers.rotateLeft(a4, 1); a5 = Integers.rotateLeft(a5, 1); @@ -240,6 +203,6 @@ void Down(byte[] Xi, int XiOff, int XiLen, int Cd) } state[XiLen] ^= 0x01; state[f_bPrime - 1] ^= (mode == MODE.ModeHash) ? (Cd & 0x01) : Cd; - phase = PhaseDown; + phase = 1; } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java index b49bbff4ca..9245a22636 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java @@ -325,11 +325,11 @@ static void implTestVectorsDigest(SimpleTest test, ExtendedDigest digest, String int a = line.indexOf('='); if (a < 0) { - int count = Integer.parseInt(map.get("Count")); - if (count != 6) - { - continue; - } + //int count = Integer.parseInt(map.get("Count")); +// if (count != 17) +// { +// continue; +// } byte[] ptByte = Hex.decode((String)map.get("Msg")); byte[] expected = Hex.decode((String)map.get("MD")); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java b/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java index 587c095c96..b32d581a74 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java @@ -33,6 +33,9 @@ public String getName() public void performTest() throws Exception { + DigestTest.implTestVectorsDigest(this, new XoodyakDigest(), "crypto/xoodyak", "LWC_HASH_KAT_256.txt"); + DigestTest.checkDigestReset(this, new XoodyakDigest()); + DigestTest.implTestExceptionsAndParametersDigest(this, new XoodyakDigest(), 32); CipherTest.checkAEADCipherMultipleBlocks(this, 1024, 18, 100, 128, 16, new XoodyakEngine()); testVectors(); CipherTest.checkCipher(32, 16, 100, 128, new CipherTest.Instance() @@ -43,8 +46,6 @@ public AEADCipher createInstance() return new XoodyakEngine(); } }); - DigestTest.checkDigestReset(this, new XoodyakDigest()); - testVectorsHash(); XoodyakEngine xoodyak = new XoodyakEngine(); testExceptions(xoodyak, xoodyak.getKeyBytesSize(), xoodyak.getIVBytesSize(), xoodyak.getBlockSize()); @@ -54,47 +55,6 @@ public AEADCipher createInstance() CipherTest.checkAEADParemeter(this, 16, 16, 16, 24, new XoodyakEngine()); } - private void testVectorsHash() - throws Exception - { - XoodyakDigest xoodyak = new XoodyakDigest(); - InputStream src = TestResourceFinder.findTestResource("crypto/xoodyak", "LWC_HASH_KAT_256.txt"); - BufferedReader bin = new BufferedReader(new InputStreamReader(src)); - String line; - byte[] ptByte; - HashMap map = new HashMap(); - while ((line = bin.readLine()) != null) - { - int a = line.indexOf('='); - if (a < 0) - { -// if (!map.get("Count").equals("18")) -// { -// continue; -// } - xoodyak.reset(); - ptByte = Hex.decode((String)map.get("Msg")); - xoodyak.update(ptByte, 0, ptByte.length); - byte[] hash = new byte[32]; - xoodyak.doFinal(hash, 0); - if (!areEqual(hash, Hex.decode((String)map.get("MD")))) - { - mismatch("Keystream " + map.get("Count"), (String)map.get("MD"), hash); - } -// else -// { -// System.out.println("Keystream " + map.get("Count") + " pass"); -// } - map.clear(); - } - else - { - map.put(line.substring(0, a).trim(), line.substring(a + 1).trim()); - } - } -// System.out.println("Xoodyak Hash pass"); - } - private void testVectors() throws Exception { From 34ccfb0fb5482c63072bc735ac5874d89ffea44a Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 24 Jan 2025 16:35:24 +1030 Subject: [PATCH 049/890] Update AsconBaseDigest --- .../crypto/digests/AsconBaseDigest.java | 79 ++++--------------- .../bouncycastle/crypto/test/AsconTest.java | 8 +- 2 files changed, 19 insertions(+), 68 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java index f71d58813a..9a0c48db12 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java @@ -1,24 +1,25 @@ package org.bouncycastle.crypto.digests; -import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.ExtendedDigest; import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Longs; abstract class AsconBaseDigest - implements ExtendedDigest + extends BufferBaseDigest { protected long x0; protected long x1; protected long x2; protected long x3; protected long x4; - protected final int DigestSize = 32; - protected final int BlockSize = 8; protected int ASCON_PB_ROUNDS = 12; - protected final byte[] m_buf = new byte[BlockSize]; - protected int m_bufPos = 0; + + protected AsconBaseDigest() + { + DigestSize = 32; + BlockSize = 8; + m_buf = new byte[BlockSize]; + } private void round(long C) { @@ -66,67 +67,17 @@ protected void p(int nr) protected abstract void setBytes(long w, byte[] bytes, int inOff, int n); - @Override - public int getDigestSize() - { - return DigestSize; - } - - @Override - public int getByteLength() - { - return BlockSize; - } - - @Override - public void update(byte in) + protected void processBytes(byte[] input, int inOff) { - m_buf[m_bufPos] = in; - if (++m_bufPos == BlockSize) - { - x0 ^= loadBytes(m_buf, 0); - p(ASCON_PB_ROUNDS); - m_bufPos = 0; - } + x0 ^= loadBytes(input, inOff); + p(ASCON_PB_ROUNDS); } - @Override - public void update(byte[] input, int inOff, int len) + protected void finish(byte[] output, int outOff) { - if ((inOff + len) > input.length) - { - throw new DataLengthException("input buffer too short"); - } - int available = 8 - m_bufPos; - if (len < available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return; - } - int inPos = 0; - if (m_bufPos > 0) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inPos += available; - x0 ^= loadBytes(m_buf, 0); - p(ASCON_PB_ROUNDS); - } - int remaining; - while ((remaining = len - inPos) >= 8) - { - x0 ^= loadBytes(input, inOff + inPos); - p(ASCON_PB_ROUNDS); - inPos += 8; - } - System.arraycopy(input, inOff + inPos, m_buf, 0, remaining); - m_bufPos = remaining; - } - - @Override - public int doFinal(byte[] output, int outOff) - { - return hash(output, outOff, DigestSize); + padAndAbsorb(); + /* squeeze full output blocks */ + squeeze(output, outOff, DigestSize); } protected void padAndAbsorb() diff --git a/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java b/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java index 5051aa4c98..50374ee564 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java @@ -91,10 +91,10 @@ public void performTest() testVectorsXof_AsconXof(); testVectorsXof_AsconXofA(); - CipherTest.checkAEADParemeter(this, 16,16, 16, 16, new AsconAEAD128()); - CipherTest.checkAEADParemeter(this, 16,16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon128)); - CipherTest.checkAEADParemeter(this, 16,16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon128a)); - CipherTest.checkAEADParemeter(this, 20,16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon80pq)); + CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new AsconAEAD128()); + CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon128)); + CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon128a)); + CipherTest.checkAEADParemeter(this, 20, 16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon80pq)); CipherTest.checkCipher(32, 16, 100, 128, new CipherTest.Instance() { From b949a8fb952a8adf9229fbe5a05ac9f6104db6df Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 24 Jan 2025 13:18:13 +0700 Subject: [PATCH 050/890] Refactoring in legacy McEliece --- .../crypto/mceliece/McElieceFujisakiCipher.java | 2 -- .../crypto/mceliece/McElieceKobaraImaiCipher.java | 12 +++++++----- .../crypto/test/McElieceKobaraImaiCipherTest.java | 8 +------- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/mceliece/McElieceFujisakiCipher.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/mceliece/McElieceFujisakiCipher.java index 8309055ad3..6221f76b88 100644 --- a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/mceliece/McElieceFujisakiCipher.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/mceliece/McElieceFujisakiCipher.java @@ -30,8 +30,6 @@ public class McElieceFujisakiCipher */ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2.1"; - private static final String DEFAULT_PRNG_NAME = "SHA1PRNG"; - private Digest messDigest; private SecureRandom sr; diff --git a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/mceliece/McElieceKobaraImaiCipher.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/mceliece/McElieceKobaraImaiCipher.java index 6555410479..ffb1c2dcb8 100644 --- a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/mceliece/McElieceKobaraImaiCipher.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/mceliece/McElieceKobaraImaiCipher.java @@ -13,6 +13,8 @@ import org.bouncycastle.pqc.legacy.math.linearalgebra.ByteUtils; import org.bouncycastle.pqc.legacy.math.linearalgebra.GF2Vector; import org.bouncycastle.pqc.legacy.math.linearalgebra.IntegerFunctions; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; /** * This class implements the Kobara/Imai conversion of the McEliecePKCS. This is @@ -23,20 +25,20 @@ public class McElieceKobaraImaiCipher implements MessageEncryptor { + public static byte[] getPublicConstant() + { + return Arrays.clone(PUBLIC_CONSTANT); + } /** * The OID of the algorithm. */ public static final String OID = "1.3.6.1.4.1.8301.3.1.3.4.2.3"; - private static final String DEFAULT_PRNG_NAME = "SHA1PRNG"; - /** * A predetermined public constant. */ - public static final byte[] PUBLIC_CONSTANT = "a predetermined public constant" - .getBytes(); - + private static final byte[] PUBLIC_CONSTANT = Strings.toByteArray("a predetermined public constant"); private Digest messDigest; diff --git a/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/McElieceKobaraImaiCipherTest.java b/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/McElieceKobaraImaiCipherTest.java index 35ffc0ecf2..3c013f6b76 100644 --- a/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/McElieceKobaraImaiCipherTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/McElieceKobaraImaiCipherTest.java @@ -129,13 +129,7 @@ public void performTest() // XXX write in McElieceFujisakiDigestCipher? - boolean verified = true; - for (int i = 0; i < hash.length; i++) - { - verified = verified && hash[i] == constructedmessage[i]; - } - - if (!verified) + if (!Arrays.areEqual(hash, constructedmessage)) { fail("en/decryption fails"); } From aa70711bea3941339fe4c4642b4eab530500d96e Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 24 Jan 2025 17:18:23 +1030 Subject: [PATCH 051/890] Update SparkleDigest --- .../crypto/digests/AsconBaseDigest.java | 3 +- .../crypto/digests/AsconXof128.java | 8 +- .../crypto/digests/BufferBaseDigest.java | 109 +++++++++++++++--- .../crypto/digests/ISAPDigest.java | 3 +- .../crypto/digests/PhotonBeetleDigest.java | 5 +- .../crypto/digests/SparkleDigest.java | 99 ++-------------- .../crypto/digests/XoodyakDigest.java | 3 +- .../bouncycastle/crypto/test/DigestTest.java | 10 +- 8 files changed, 114 insertions(+), 126 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java index 9a0c48db12..d22b7062f8 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java @@ -16,9 +16,8 @@ abstract class AsconBaseDigest protected AsconBaseDigest() { + super(ProcessingBufferType.Immediate, 8); DigestSize = 32; - BlockSize = 8; - m_buf = new byte[BlockSize]; } private void round(long C) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java index 31fe0f64a9..e2c6e526b7 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java @@ -18,7 +18,7 @@ public class AsconXof128 extends AsconBaseDigest implements Xof { - private boolean m_squeezing = false; + private boolean m_squeezing; public AsconXof128() { @@ -96,12 +96,6 @@ public int doFinal(byte[] output, int outOff, int outLen) return rlt; } - @Override - public int getByteLength() - { - return 8; - } - @Override public void reset() { diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java index 07e037ea0c..4dee19a9d6 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java @@ -7,11 +7,94 @@ public abstract class BufferBaseDigest implements ExtendedDigest { + protected enum ProcessingBufferType + { + Buffered, + Immediate, + } + protected int DigestSize; protected int BlockSize; protected byte[] m_buf; protected int m_bufPos; protected String algorithmName; + protected ProcessingBuffer processor; + + protected BufferBaseDigest(ProcessingBufferType type, int BlockSize) + { + this.BlockSize = BlockSize; + m_buf = new byte[BlockSize]; + switch (type) + { + case Buffered: + processor = new BufferedProcessor(); + break; + case Immediate: + processor = new ImmediateProcessor(); + break; + } + } + + protected interface ProcessingBuffer + { + void update(byte input); + + boolean isLengthWithinAvailableSpace(int len, int available); + + boolean isLengthExceedingBlockSize(int len, int size); + } + + private class BufferedProcessor + implements ProcessingBuffer + { + public void update(byte input) + { + if (m_bufPos == BlockSize) + { + processBytes(m_buf, 0); + m_bufPos = 0; + } + m_buf[m_bufPos++] = input; + } + + @Override + public boolean isLengthWithinAvailableSpace(int len, int available) + { + return len <= available; + } + + @Override + public boolean isLengthExceedingBlockSize(int len, int size) + { + return len > size; + } + } + + private class ImmediateProcessor + implements ProcessingBuffer + { + public void update(byte input) + { + m_buf[m_bufPos] = input; + if (++m_bufPos == BlockSize) + { + processBytes(m_buf, 0); + m_bufPos = 0; + } + } + + @Override + public boolean isLengthWithinAvailableSpace(int len, int available) + { + return len < available; + } + + @Override + public boolean isLengthExceedingBlockSize(int len, int size) + { + return len >= size; + } + } @Override public String getAlgorithmName() @@ -34,12 +117,7 @@ public int getByteLength() @Override public void update(byte in) { - m_buf[m_bufPos] = in; - if (++m_bufPos == BlockSize) - { - processBytes(m_buf, 0); - m_bufPos = 0; - } + processor.update(in); } @Override @@ -50,27 +128,27 @@ public void update(byte[] input, int inOff, int len) throw new DataLengthException("input buffer too short"); } int available = BlockSize - m_bufPos; - if (len < available) + if (processor.isLengthWithinAvailableSpace(len, available)) { System.arraycopy(input, inOff, m_buf, m_bufPos, len); m_bufPos += len; return; } - int inPos = 0; if (m_bufPos > 0) { System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inPos += available; + inOff += available; + len -= available; processBytes(m_buf, 0); } - int remaining; - while ((remaining = len - inPos) >= BlockSize) + while (processor.isLengthExceedingBlockSize(len, BlockSize)) { - processBytes(input, inOff + inPos); - inPos += BlockSize; + processBytes(input, inOff); + inOff += BlockSize; + len -= BlockSize; } - System.arraycopy(input, inOff + inPos, m_buf, 0, remaining); - m_bufPos = remaining; + System.arraycopy(input, inOff, m_buf, 0, len); + m_bufPos = len; } @Override @@ -86,5 +164,6 @@ public int doFinal(byte[] output, int outOff) } protected abstract void processBytes(byte[] input, int inOff); + protected abstract void finish(byte[] output, int outOff); } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java index a72a26a4ff..7b541dbc2f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java @@ -19,9 +19,8 @@ public class ISAPDigest public ISAPDigest() { + super(ProcessingBufferType.Immediate, 8); DigestSize = 32; - BlockSize = 8; - m_buf = new byte[BlockSize]; algorithmName = "ISAP Hash"; reset(); } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java index f36a150257..983fc9ac86 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java @@ -43,13 +43,12 @@ public class PhotonBeetleDigest public PhotonBeetleDigest() { + super(ProcessingBufferType.Buffered, 4); state = new byte[STATE_INBYTES]; state_2d = new byte[D][D]; DigestSize = 32; algorithmName = "Photon-Beetle Hash"; - BlockSize = 4; blockCount = 0; - m_buf = new byte[BlockSize]; } @Override @@ -93,7 +92,7 @@ else if (blockCount == 4 && m_bufPos == 0) { state[m_bufPos] ^= 0x01; // ozs } - state[STATE_INBYTES - 1] ^= (m_bufPos == 0 ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET; + state[STATE_INBYTES - 1] ^= (m_bufPos % BlockSize == 0 ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET; } PHOTON_Permutation(); int SQUEEZE_RATE_INBYTES = 16; diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java index 90c17f62ea..a7100f8628 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java @@ -1,8 +1,5 @@ package org.bouncycastle.crypto.digests; -import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.ExtendedDigest; -import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.engines.SparkleEngine; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Integers; @@ -14,7 +11,7 @@ * Specification: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/sparkle-spec-final.pdf */ public class SparkleDigest - implements ExtendedDigest + extends BufferBaseDigest { public static class Friend { @@ -27,34 +24,27 @@ public enum SparkleParameters ESCH256, ESCH384 } - - private static final int RATE_BYTES = 16; private static final int RATE_WORDS = 4; - - private String algorithmName; private final int[] state; - private final byte[] m_buf = new byte[RATE_BYTES]; - private final int DIGEST_BYTES; private final int SPARKLE_STEPS_SLIM; private final int SPARKLE_STEPS_BIG; private final int STATE_WORDS; - private int m_bufPos = 0; - public SparkleDigest(SparkleParameters sparkleParameters) { + super(ProcessingBufferType.Buffered, 16); switch (sparkleParameters) { case ESCH256: algorithmName = "ESCH-256"; - DIGEST_BYTES = 32; + DigestSize = 32; SPARKLE_STEPS_SLIM = 7; SPARKLE_STEPS_BIG = 11; STATE_WORDS = 12; break; case ESCH384: algorithmName = "ESCH-384"; - DIGEST_BYTES = 48; + DigestSize = 48; SPARKLE_STEPS_SLIM = 8; SPARKLE_STEPS_BIG = 12; STATE_WORDS = 16; @@ -62,94 +52,26 @@ public SparkleDigest(SparkleParameters sparkleParameters) default: throw new IllegalArgumentException("Invalid definition of SCHWAEMM instance"); } - state = new int[STATE_WORDS]; } @Override - public String getAlgorithmName() - { - return algorithmName; - } - - @Override - public int getDigestSize() - { - return DIGEST_BYTES; - } - - @Override - public int getByteLength() - { - return RATE_BYTES; - } - - @Override - public void update(byte input) - { - if (m_bufPos == RATE_BYTES) - { - processBlock(m_buf, 0, SPARKLE_STEPS_SLIM); - m_bufPos = 0; - } - - m_buf[m_bufPos++] = input; - } - - @Override - public void update(byte[] in, int inOff, int len) + protected void processBytes(byte[] input, int inOff) { - if (inOff > in.length - len) - { - throw new DataLengthException(algorithmName + " input buffer too short"); - } - - if (len < 1) - return; - - int available = RATE_BYTES - m_bufPos; - if (len <= available) - { - System.arraycopy(in, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return; - } - - int inPos = 0; - if (m_bufPos > 0) - { - System.arraycopy(in, inOff, m_buf, m_bufPos, available); - processBlock(m_buf, 0, SPARKLE_STEPS_SLIM); - inPos += available; - } - - int remaining; - while ((remaining = len - inPos) > RATE_BYTES) - { - processBlock(in, inOff + inPos, SPARKLE_STEPS_SLIM); - inPos += RATE_BYTES; - } - - System.arraycopy(in, inOff + inPos, m_buf, 0, remaining); - m_bufPos = remaining; + processBlock(input, inOff, SPARKLE_STEPS_SLIM); } @Override - public int doFinal(byte[] output, int outOff) + protected void finish(byte[] output, int outOff) { - if (outOff > output.length - DIGEST_BYTES) - { - throw new OutputLengthException(algorithmName + " input buffer too short"); - } - // addition of constant M1 or M2 to the state - if (m_bufPos < RATE_BYTES) + if (m_bufPos < BlockSize) { state[(STATE_WORDS >> 1) - 1] ^= 1 << 24; // padding m_buf[m_bufPos] = (byte)0x80; - while(++m_bufPos < RATE_BYTES) + while(++m_bufPos < BlockSize) { m_buf[m_bufPos] = 0x00; } @@ -175,9 +97,6 @@ public int doFinal(byte[] output, int outOff) SparkleEngine.sparkle_opt12(Friend.INSTANCE, state, SPARKLE_STEPS_SLIM); Pack.intToLittleEndian(state, 0, RATE_WORDS, output, outOff + 16); } - - reset(); - return DIGEST_BYTES; } @Override diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java index 866675ca85..1096c72687 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java @@ -31,10 +31,9 @@ enum MODE public XoodyakDigest() { + super(ProcessingBufferType.Immediate, 16); DigestSize = 32; state = new byte[48]; - BlockSize = 16; - m_buf = new byte[BlockSize]; algorithmName = "Xoodyak Hash"; reset(); } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java index 9245a22636..1f41c56f7f 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java @@ -325,11 +325,11 @@ static void implTestVectorsDigest(SimpleTest test, ExtendedDigest digest, String int a = line.indexOf('='); if (a < 0) { - //int count = Integer.parseInt(map.get("Count")); -// if (count != 17) -// { -// continue; -// } + int count = Integer.parseInt(map.get("Count")); + if (count != 21) + { + continue; + } byte[] ptByte = Hex.decode((String)map.get("Msg")); byte[] expected = Hex.decode((String)map.get("MD")); From 0cdc08a0f1550ff4558655dbda07f6e28a872649 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 24 Jan 2025 17:24:41 +1030 Subject: [PATCH 052/890] Update RomulusDigest --- .../crypto/digests/RomulusDigest.java | 83 +++++------------ .../bouncycastle/crypto/test/RomulusTest.java | 92 +------------------ 2 files changed, 27 insertions(+), 148 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java index 54d8700dac..2f1bdd84f5 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java @@ -1,9 +1,5 @@ package org.bouncycastle.crypto.digests; -import java.io.ByteArrayOutputStream; - -import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.Digest; import org.bouncycastle.util.Arrays; /** @@ -13,10 +9,10 @@ */ public class RomulusDigest - implements Digest + extends BufferBaseDigest { - private final int CRYPTO_BYTES = 32; - + byte[] h = new byte[16]; + byte[] g = new byte[16]; /* * This file includes only the encryption function of SKINNY-128-384+ as required by Romulus-v1.3 */ @@ -69,6 +65,12 @@ public class RomulusDigest (byte)0x16, (byte)0x2C, (byte)0x18, (byte)0x30, (byte)0x21, (byte)0x02, (byte)0x05, (byte)0x0B, (byte)0x17, (byte)0x2E, (byte)0x1C, (byte)0x38, (byte)0x31, (byte)0x23, (byte)0x06, (byte)0x0D, (byte)0x1B, (byte)0x36, (byte)0x2D, (byte)0x1A}; + public RomulusDigest() + { + super(ProcessingBufferType.Immediate, 32); + DigestSize = 32; + } + void skinny_128_384_plus_enc(byte[] input, byte[] userkey) { byte[][] state = new byte[4][4]; @@ -212,31 +214,6 @@ void ipad_256(byte[] m, int inOff, byte[] mp, int len8) mp[31] = (byte)(len8 & 0x1f); } - private void crypto_hash(byte[] out, int outOff, byte[] input, int inlen) - { - byte[] h = new byte[16]; - byte[] g = new byte[16]; - int mlen; - byte[] p = new byte[32]; - mlen = inlen; - int inOff = 0; - while (mlen >= 32) - { // Normal loop - hirose_128_128_256(h, g, input, inOff); - inOff += 32; - mlen -= 32; - } - // Partial block (or in case there is no partial block we add a 0^2n block - ipad_256(input, inOff, p, mlen); - h[0] ^= 2; - hirose_128_128_256(h, g, p, 0); - // Assign the output tag - System.arraycopy(h, 0, out, outOff, 16); - System.arraycopy(g, 0, out, 16 + outOff, 16); - } - - private final ByteArrayOutputStream message = new ByteArrayOutputStream(); - @Override public String getAlgorithmName() { @@ -244,43 +221,29 @@ public String getAlgorithmName() } @Override - public int getDigestSize() - { - return CRYPTO_BYTES; - } - - @Override - public void update(byte input) + protected void processBytes(byte[] input, int inOff) { - message.write(input); + hirose_128_128_256(h, g, input, inOff); } @Override - public void update(byte[] input, int inOff, int len) + protected void finish(byte[] output, int outOff) { - if (inOff + len > input.length) - { - throw new DataLengthException(" input buffer too short"); - } - message.write(input, inOff, len); - } - - @Override - public int doFinal(byte[] output, int outOff) - { - if (outOff + 32 > output.length) - { - throw new DataLengthException(" output buffer too short"); - } - byte[] input = message.toByteArray(); - int inlen = input.length; - crypto_hash(output, outOff, input, inlen); - return CRYPTO_BYTES; + byte[] p = new byte[32]; + ipad_256(m_buf, 0, p, m_bufPos); + h[0] ^= 2; + hirose_128_128_256(h, g, p, 0); + // Assign the output tag + System.arraycopy(h, 0, output, outOff, 16); + System.arraycopy(g, 0, output, 16 + outOff, 16); } @Override public void reset() { - message.reset(); + Arrays.clear(m_buf); + m_bufPos = 0; + Arrays.clear(h); + Arrays.clear(g); } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java index 6af86de0a8..cf20ff06ec 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java @@ -1,19 +1,11 @@ package org.bouncycastle.crypto.test; -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.HashMap; - import org.bouncycastle.crypto.CipherParameters; -import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.RomulusDigest; import org.bouncycastle.crypto.engines.RomulusEngine; import org.bouncycastle.crypto.modes.AEADCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; -import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; public class RomulusTest @@ -27,6 +19,10 @@ public String getName() public void performTest() throws Exception { + DigestTest.implTestVectorsDigest(this, new RomulusDigest(), "crypto/romulus", "LWC_HASH_KAT_256.txt"); + DigestTest.checkDigestReset(this, new RomulusDigest()); + DigestTest.implTestExceptionsAndParametersDigest(this, new RomulusDigest(), 32); + CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM), "crypto/romulus", "m_LWC_AEAD_KAT_128_128.txt", this); CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT), "crypto/romulus", "t_LWC_AEAD_KAT_128_128.txt", this); CipherTest.implTestVectorsEngine(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN), "crypto/romulus", "n_LWC_AEAD_KAT_128_128.txt", this); @@ -154,90 +150,10 @@ private void implTestParametersEngine(RomulusEngine cipher, int keySize, int ivS } } - - private void testVectorsHash() - throws Exception - { - RomulusDigest Romulus = new RomulusDigest(); - InputStream src = RomulusTest.class.getResourceAsStream("/org/bouncycastle/crypto/test/romulus/LWC_HASH_KAT_256.txt"); - BufferedReader bin = new BufferedReader(new InputStreamReader(src)); - String line; - byte[] ptByte, adByte; - byte[] rv; - HashMap map = new HashMap(); - while ((line = bin.readLine()) != null) - { - int a = line.indexOf('='); - if (a < 0) - { -// if (!map.get("Count").equals("3")) -// { -// continue; -// } - Romulus.reset(); - ptByte = Hex.decode((String)map.get("Msg")); - Romulus.update(ptByte, 0, ptByte.length); - byte[] hash = new byte[Romulus.getDigestSize()]; - Romulus.doFinal(hash, 0); - if (!areEqual(hash, Hex.decode((String)map.get("MD")))) - { - mismatch("Keystream " + map.get("Count"), (String)map.get("MD"), hash); - } -// else -// { -// System.out.println("Keystream " + map.get("Count") + " pass"); -// } - map.clear(); - Romulus.reset(); - } - else - { - map.put(line.substring(0, a).trim(), line.substring(a + 1).trim()); - } - } - System.out.println("Romulus Hash pass"); - } - - - private void testExceptions(Digest digest, int digestsize) - { - if (digest.getDigestSize() != digestsize) - { - fail(digest.getAlgorithmName() + ": digest size is not correct"); - } - - try - { - digest.update(new byte[1], 1, 1); - fail(digest.getAlgorithmName() + ": input for update is too short"); - } - catch (DataLengthException e) - { - //expected - } - try - { - digest.doFinal(new byte[digest.getDigestSize() - 1], 2); - fail(digest.getAlgorithmName() + ": output for dofinal is too short"); - } - catch (DataLengthException e) - { - //expected - } - System.out.println(digest.getAlgorithmName() + " test Exceptions pass"); - } - - - private void mismatch(String name, String expected, byte[] found) - { - fail("mismatch on " + name, expected, new String(Hex.encode(found))); - } - public static void main(String[] args) { runTest(new RomulusTest()); } - } From cc39f0bce071335bea7a787fe64f724604ba5102 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 24 Jan 2025 17:30:55 +1030 Subject: [PATCH 053/890] Refactor around constructor and reset of digests --- .../bouncycastle/crypto/digests/AsconBaseDigest.java | 6 ------ .../org/bouncycastle/crypto/digests/AsconCXof128.java | 7 +------ .../org/bouncycastle/crypto/digests/AsconDigest.java | 6 ------ .../org/bouncycastle/crypto/digests/AsconHash256.java | 7 +------ .../java/org/bouncycastle/crypto/digests/AsconXof.java | 6 ------ .../org/bouncycastle/crypto/digests/AsconXof128.java | 7 +------ .../bouncycastle/crypto/digests/BufferBaseDigest.java | 7 +++++++ .../org/bouncycastle/crypto/digests/ISAPDigest.java | 3 +-- .../crypto/digests/PhotonBeetleDigest.java | 3 +-- .../org/bouncycastle/crypto/digests/RomulusDigest.java | 10 ++-------- .../org/bouncycastle/crypto/digests/SparkleDigest.java | 3 +-- .../org/bouncycastle/crypto/digests/XoodyakDigest.java | 3 +-- 12 files changed, 16 insertions(+), 52 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java index d22b7062f8..a6f762fd49 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java @@ -112,10 +112,4 @@ protected int hash(byte[] output, int outOff, int outLen) squeeze(output, outOff, outLen); return outLen; } - - public void reset() - { - Arrays.clear(m_buf); - m_bufPos = 0; - } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java index d9bca9abbd..60b0e36f43 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java @@ -35,6 +35,7 @@ public AsconCXof128(byte[] s) public AsconCXof128(byte[] s, int off, int len) { + algorithmName = "Ascon-CXOF128"; if ((off + len) > s.length) { throw new DataLengthException("input buffer too short"); @@ -103,12 +104,6 @@ protected void padAndAbsorb() super.padAndAbsorb(); } - @Override - public String getAlgorithmName() - { - return "Ascon-CXOF128"; - } - @Override public int doOutput(byte[] output, int outOff, int outLen) { diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java index 1063df56c0..d837704e26 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java @@ -68,12 +68,6 @@ protected void setBytes(long w, byte[] bytes, int inOff, int n) Pack.longToBigEndian(w, bytes, inOff, n); } - @Override - public String getAlgorithmName() - { - return algorithmName; - } - @Override public void reset() { diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java index d459c37b6a..5324b98546 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java @@ -18,6 +18,7 @@ public class AsconHash256 { public AsconHash256() { + algorithmName = "Ascon-Hash256"; reset(); } @@ -46,12 +47,6 @@ protected void setBytes(long w, byte[] bytes, int inOff, int n) Pack.longToLittleEndian(w, bytes, inOff, n); } - @Override - public String getAlgorithmName() - { - return "Ascon-Hash256"; - } - @Override public void reset() { diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java index cb9241d6e9..0ae98d904e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java @@ -97,12 +97,6 @@ protected void setBytes(long w, byte[] bytes, int inOff, int n) Pack.longToBigEndian(w, bytes, inOff, n); } - @Override - public String getAlgorithmName() - { - return algorithmName; - } - @Override public int doOutput(byte[] output, int outOff, int outLen) { diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java index e2c6e526b7..e1dcc5e8e3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java @@ -22,6 +22,7 @@ public class AsconXof128 public AsconXof128() { + algorithmName = "Ascon-XOF-128"; reset(); } @@ -56,12 +57,6 @@ protected void padAndAbsorb() super.padAndAbsorb(); } - @Override - public String getAlgorithmName() - { - return "Ascon-XOF-128"; - } - @Override public void update(byte in) { diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java index 4dee19a9d6..ebec9dc9a1 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java @@ -3,6 +3,7 @@ import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.ExtendedDigest; import org.bouncycastle.crypto.OutputLengthException; +import org.bouncycastle.util.Arrays; public abstract class BufferBaseDigest implements ExtendedDigest @@ -163,6 +164,12 @@ public int doFinal(byte[] output, int outOff) return DigestSize; } + public void reset() + { + Arrays.clear(m_buf); + m_bufPos = 0; + } + protected abstract void processBytes(byte[] input, int inOff); protected abstract void finish(byte[] output, int outOff); diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java index 7b541dbc2f..6163422c97 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java @@ -100,6 +100,7 @@ protected void finish(byte[] output, int outOff) @Override public void reset() { + super.reset(); t0 = t1 = t2 = t3 = t4 = 0; /* init state */ x0 = -1255492011513352131L; @@ -107,7 +108,5 @@ public void reset() x2 = -5437372128236807582L; x3 = 4834782570098516968L; x4 = 3787428097924915520L; - Arrays.clear(m_buf); - m_bufPos = 0; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java index 983fc9ac86..ec8019dbb7 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java @@ -104,10 +104,9 @@ else if (blockCount == 4 && m_bufPos == 0) @Override public void reset() { + super.reset(); Arrays.fill(state, (byte)0); blockCount = 0; - Arrays.clear(m_buf); - m_bufPos = 0; } void PHOTON_Permutation() diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java index 2f1bdd84f5..24d098b60f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java @@ -69,6 +69,7 @@ public RomulusDigest() { super(ProcessingBufferType.Immediate, 32); DigestSize = 32; + algorithmName = "Romulus Hash"; } void skinny_128_384_plus_enc(byte[] input, byte[] userkey) @@ -214,12 +215,6 @@ void ipad_256(byte[] m, int inOff, byte[] mp, int len8) mp[31] = (byte)(len8 & 0x1f); } - @Override - public String getAlgorithmName() - { - return "Romulus Hash"; - } - @Override protected void processBytes(byte[] input, int inOff) { @@ -241,8 +236,7 @@ protected void finish(byte[] output, int outOff) @Override public void reset() { - Arrays.clear(m_buf); - m_bufPos = 0; + super.reset(); Arrays.clear(h); Arrays.clear(g); } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java index a7100f8628..8ae3d66048 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java @@ -102,9 +102,8 @@ protected void finish(byte[] output, int outOff) @Override public void reset() { + super.reset(); Arrays.fill(state, 0); - Arrays.fill(m_buf, (byte)0); - m_bufPos = 0; } private void processBlock(byte[] buf, int off, int steps) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java index 1096c72687..7f86dbcace 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java @@ -69,11 +69,10 @@ protected void finish(byte[] output, int outOff) @Override public void reset() { + super.reset(); Arrays.fill(state, (byte)0); phase = PhaseUp; mode = MODE.ModeHash; - Arrays.clear(m_buf); - m_bufPos = 0; Cd = 0x03; } From 85493d15fbc9e344daeb844a88d6979a84659fa5 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 24 Jan 2025 14:33:31 +0700 Subject: [PATCH 054/890] Update legacy (and old JDK) X509SignatureUtil --- .../asymmetric/x509/X509SignatureUtil.java | 1 - .../jce/provider/X509SignatureUtil.java | 44 ++++++------- .../asymmetric/x509/X509SignatureUtil.java | 65 ++++++++++--------- .../asymmetric/x509/X509SignatureUtil.java | 54 +++++++-------- .../jce/provider/X509SignatureUtil.java | 41 +++++++----- 5 files changed, 106 insertions(+), 99 deletions(-) diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java index b2dfac7c84..cae057337d 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java @@ -15,7 +15,6 @@ import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.RSASSAPSSparams; diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java index 01d592682f..15e8a3e3a8 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java @@ -10,9 +10,7 @@ import java.security.spec.PSSParameterSpec; import org.bouncycastle.asn1.ASN1Encodable; -import org.bouncycastle.asn1.ASN1Null; import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; @@ -25,17 +23,19 @@ class X509SignatureUtil { - private static final ASN1Null derNull = DERNull.INSTANCE; - - static void setSignatureParameters( - Signature signature, - ASN1Encodable params) + private static boolean isAbsentOrEmptyParameters(ASN1Encodable parameters) + { + return parameters == null || DERNull.INSTANCE.equals(parameters); + } + + static void setSignatureParameters(Signature signature, ASN1Encodable params) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { - if (params != null && !derNull.equals(params)) + if (!isAbsentOrEmptyParameters(params)) { - AlgorithmParameters sigParams = AlgorithmParameters.getInstance(signature.getAlgorithm(), signature.getProvider()); - + String sigAlgName = signature.getAlgorithm(); + AlgorithmParameters sigParams = AlgorithmParameters.getInstance(sigAlgName, signature.getProvider()); + try { sigParams.init(params.toASN1Primitive().getEncoded()); @@ -44,8 +44,8 @@ static void setSignatureParameters( { throw new SignatureException("IOException decoding parameters: " + e.getMessage()); } - - if (signature.getAlgorithm().endsWith("MGF1")) + + if (sigAlgName.endsWith("MGF1")) { try { @@ -58,21 +58,21 @@ static void setSignatureParameters( } } } - - static String getSignatureName( - AlgorithmIdentifier sigAlgId) + + static String getSignatureName(AlgorithmIdentifier sigAlgId) { + ASN1ObjectIdentifier sigAlgOid = sigAlgId.getAlgorithm(); ASN1Encodable params = sigAlgId.getParameters(); - - if (params != null && !derNull.equals(params)) + + if (!isAbsentOrEmptyParameters(params)) { - if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS)) + if (PKCSObjectIdentifiers.id_RSASSA_PSS.equals(sigAlgOid)) { RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params); - + return getDigestAlgName(rsaParams.getHashAlgorithm().getAlgorithm()) + "withRSAandMGF1"; } - if (sigAlgId.getAlgorithm().equals(X9ObjectIdentifiers.ecdsa_with_SHA2)) + if (X9ObjectIdentifiers.ecdsa_with_SHA2.equals(sigAlgOid)) { AlgorithmIdentifier ecDsaParams = AlgorithmIdentifier.getInstance(params); @@ -80,9 +80,9 @@ static String getSignatureName( } } - return sigAlgId.getAlgorithm().getId(); + return sigAlgOid.getId(); } - + /** * Return the digest algorithm using one of the standard JCA string * representations rather the the algorithm identifier (if possible). diff --git a/prov/src/main/jdk1.1/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java b/prov/src/main/jdk1.1/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java index 21391a5323..f964f7a0e8 100644 --- a/prov/src/main/jdk1.1/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java +++ b/prov/src/main/jdk1.1/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java @@ -17,7 +17,6 @@ import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Null; import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.internal.asn1.edec.EdECObjectIdentifiers; import org.bouncycastle.internal.asn1.oiw.OIWObjectIdentifiers; @@ -29,7 +28,6 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.Strings; - class X509SignatureUtil { private static final Map algNames = new HashMap(); @@ -42,29 +40,32 @@ class X509SignatureUtil algNames.put(X9ObjectIdentifiers.id_dsa_with_sha1, "SHA1withDSA"); } - private static final ASN1Null derNull = DERNull.INSTANCE; + private static boolean isAbsentOrEmptyParameters(ASN1Encodable parameters) + { + return parameters == null || DERNull.INSTANCE.equals(parameters); + } - static void setSignatureParameters( - Signature signature, - ASN1Encodable params) + static void setSignatureParameters(Signature signature, ASN1Encodable params) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { - if (params != null && !derNull.equals(params)) + if (!isAbsentOrEmptyParameters(params)) { + String sigAlgName = signature.getAlgorithm(); - AlgorithmParameters sigParams; + String sigParamsAlg; + if (sigAlgName.indexOf("MGF1") > 0) + { + sigParamsAlg = "PSS"; + } + else + { + sigParamsAlg = Strings.toUpperCase(sigAlgName); + } try { - if (signature.getAlgorithm().indexOf("MGF1") > 0) - { - sigParams = AlgorithmParameters.getInstance("PSS"); - } - else - { - sigParams = AlgorithmParameters.getInstance(Strings.toUpperCase(signature.getAlgorithm())); - } - + AlgorithmParameters sigParams = AlgorithmParameters.getInstance(sigParamsAlg); + sigParams.init(params.toASN1Primitive().getEncoded()); } catch (IOException e) @@ -73,38 +74,38 @@ static void setSignatureParameters( } } } - - static String getSignatureName( - AlgorithmIdentifier sigAlgId) + + static String getSignatureName(AlgorithmIdentifier sigAlgId) { + ASN1ObjectIdentifier sigAlgOid = sigAlgId.getAlgorithm(); ASN1Encodable params = sigAlgId.getParameters(); - - if (params != null && !derNull.equals(params)) + + if (!isAbsentOrEmptyParameters(params)) { - if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS)) + if (PKCSObjectIdentifiers.id_RSASSA_PSS.equals(sigAlgOid)) { RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params); - + return getDigestAlgName(rsaParams.getHashAlgorithm().getAlgorithm()) + "WITHRSAANDMGF1"; } - if (sigAlgId.getAlgorithm().equals(X9ObjectIdentifiers.ecdsa_with_SHA2)) + if (X9ObjectIdentifiers.ecdsa_with_SHA2.equals(sigAlgOid)) { - ASN1Sequence ecDsaParams = ASN1Sequence.getInstance(params); - - return getDigestAlgName((ASN1ObjectIdentifier)ecDsaParams.getObjectAt(0)) + "WITHECDSA"; + AlgorithmIdentifier ecDsaParams = AlgorithmIdentifier.getInstance(params); + + return getDigestAlgName(ecDsaParams.getAlgorithm()) + "WITHECDSA"; } } // deal with the "weird" ones. - String algName = (String)algNames.get(sigAlgId.getAlgorithm()); + String algName = (String)algNames.get(sigAlgOid); if (algName != null) { return algName; } - return findAlgName(sigAlgId.getAlgorithm()); + return findAlgName(sigAlgOid); } - + /** * Return the digest algorithm using one of the standard JCA string * representations rather the the algorithm identifier (if possible). @@ -155,7 +156,7 @@ private static String findAlgName(ASN1ObjectIdentifier algOid) private static String lookupAlg(Provider prov, ASN1ObjectIdentifier algOid) { - String algName = prov.getProperty("Alg.Alias.Signature." + algOid); + String algName = prov.getProperty("Alg.Alias.Signature." + algOid); if (algName != null) { diff --git a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java index 925ccc3206..4e603a3c88 100644 --- a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java +++ b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java @@ -17,7 +17,6 @@ import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Null; import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.internal.asn1.edec.EdECObjectIdentifiers; import org.bouncycastle.internal.asn1.oiw.OIWObjectIdentifiers; @@ -40,22 +39,23 @@ class X509SignatureUtil algNames.put(X9ObjectIdentifiers.id_dsa_with_sha1, "SHA1withDSA"); } - private static final ASN1Null derNull = DERNull.INSTANCE; + private static boolean isAbsentOrEmptyParameters(ASN1Encodable parameters) + { + return parameters == null || DERNull.INSTANCE.equals(parameters); + } - static void setSignatureParameters( - Signature signature, - ASN1Encodable params) + static void setSignatureParameters(Signature signature, ASN1Encodable params) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { - if (params != null && !derNull.equals(params)) + if (!isAbsentOrEmptyParameters(params)) { - - AlgorithmParameters sigParams; + String sigAlgName = signature.getAlgorithm(); try { - sigParams = AlgorithmParameters.getInstance(signature.getAlgorithm(), signature.getProvider().getName()); - + AlgorithmParameters sigParams = AlgorithmParameters.getInstance(sigAlgName, + signature.getProvider().getName()); + sigParams.init(params.toASN1Primitive().getEncoded()); } catch (NoSuchProviderException e) @@ -66,8 +66,8 @@ static void setSignatureParameters( { throw new SignatureException("IOException decoding parameters: " + e.getMessage()); } - - if (signature.getAlgorithm().endsWith("MGF1")) + + if (sigAlgName.endsWith("MGF1")) { try { @@ -80,38 +80,38 @@ static void setSignatureParameters( } } } - - static String getSignatureName( - AlgorithmIdentifier sigAlgId) + + static String getSignatureName(AlgorithmIdentifier sigAlgId) { + ASN1ObjectIdentifier sigAlgOid = sigAlgId.getAlgorithm(); ASN1Encodable params = sigAlgId.getParameters(); - - if (params != null && !derNull.equals(params)) + + if (!isAbsentOrEmptyParameters(params)) { - if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS)) + if (PKCSObjectIdentifiers.id_RSASSA_PSS.equals(sigAlgOid)) { RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params); - + return getDigestAlgName(rsaParams.getHashAlgorithm().getAlgorithm()) + "withRSAandMGF1"; } - if (sigAlgId.getAlgorithm().equals(X9ObjectIdentifiers.ecdsa_with_SHA2)) + if (X9ObjectIdentifiers.ecdsa_with_SHA2.equals(sigAlgOid)) { - ASN1Sequence ecDsaParams = ASN1Sequence.getInstance(params); - - return getDigestAlgName((ASN1ObjectIdentifier)ecDsaParams.getObjectAt(0)) + "withECDSA"; + AlgorithmIdentifier ecDsaParams = AlgorithmIdentifier.getInstance(params); + + return getDigestAlgName(ecDsaParams.getAlgorithm()) + "withECDSA"; } } // deal with the "weird" ones. - String algName = (String)algNames.get(sigAlgId.getAlgorithm()); + String algName = (String)algNames.get(sigAlgOid); if (algName != null) { return algName; } - return findAlgName(sigAlgId.getAlgorithm()); + return findAlgName(sigAlgOid); } - + /** * Return the digest algorithm using one of the standard JCA string * representations rather the the algorithm identifier (if possible). @@ -162,7 +162,7 @@ private static String findAlgName(ASN1ObjectIdentifier algOid) private static String lookupAlg(Provider prov, ASN1ObjectIdentifier algOid) { - String algName = prov.getProperty("Alg.Alias.Signature." + algOid); + String algName = prov.getProperty("Alg.Alias.Signature." + algOid); if (algName != null) { diff --git a/prov/src/main/jdk1.4/org/bouncycastle/jce/provider/X509SignatureUtil.java b/prov/src/main/jdk1.4/org/bouncycastle/jce/provider/X509SignatureUtil.java index 3338e07046..949d07a2dd 100644 --- a/prov/src/main/jdk1.4/org/bouncycastle/jce/provider/X509SignatureUtil.java +++ b/prov/src/main/jdk1.4/org/bouncycastle/jce/provider/X509SignatureUtil.java @@ -6,7 +6,6 @@ import java.security.SignatureException; import org.bouncycastle.asn1.ASN1Encodable; -import org.bouncycastle.asn1.ASN1Null; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; @@ -16,17 +15,19 @@ import org.bouncycastle.asn1.pkcs.RSASSAPSSparams; import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; class X509SignatureUtil { - private static final ASN1Null derNull = DERNull.INSTANCE; - - static void setSignatureParameters( - Signature signature, - ASN1Encodable params) + private static boolean isAbsentOrEmptyParameters(ASN1Encodable parameters) + { + return parameters == null || DERNull.INSTANCE.equals(parameters); + } + + static void setSignatureParameters(Signature signature, ASN1Encodable params) throws NoSuchAlgorithmException, SignatureException, InvalidKeyException { - if (params != null && !derNull.equals(params)) + if (!isAbsentOrEmptyParameters(params)) { /* AlgorithmParameters sigParams = AlgorithmParameters.getInstance(signature.getAlgorithm(), signature.getProvider()); @@ -51,25 +52,31 @@ static void setSignatureParameters( */ } } - - static String getSignatureName( - AlgorithmIdentifier sigAlgId) + + static String getSignatureName(AlgorithmIdentifier sigAlgId) { + ASN1ObjectIdentifier sigAlgOid = sigAlgId.getAlgorithm(); ASN1Encodable params = sigAlgId.getParameters(); - - if (params != null && !derNull.equals(params)) + + if (!isAbsentOrEmptyParameters(params)) { - if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS)) - { + if (PKCSObjectIdentifiers.id_RSASSA_PSS.equals(sigAlgOid)) + { RSASSAPSSparams rsaParams = RSASSAPSSparams.getInstance(params); - + return getDigestAlgName(rsaParams.getHashAlgorithm().getAlgorithm()) + "withRSAandMGF1"; } + if (X9ObjectIdentifiers.ecdsa_with_SHA2.equals(sigAlgOid)) + { + AlgorithmIdentifier ecDsaParams = AlgorithmIdentifier.getInstance(params); + + return getDigestAlgName(ecDsaParams.getAlgorithm()) + "withECDSA"; + } } - return sigAlgId.getAlgorithm().getId(); + return sigAlgOid.getId(); } - + /** * Return the digest algorithm using one of the standard JCA string * representations rather the the algorithm identifier (if possible). From eda7147af3da251106c3e6f23ac89cf3e0227245 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 24 Jan 2025 18:11:49 +1030 Subject: [PATCH 055/890] Introduce Friend between PhotonBeetleDigest and PhotonBeetleEngine --- .../crypto/digests/PhotonBeetleDigest.java | 108 ++---------------- .../crypto/engines/PhotonBeetleEngine.java | 34 ++++-- 2 files changed, 34 insertions(+), 108 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java index ec8019dbb7..0606dedc8a 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto.digests; +import org.bouncycastle.crypto.engines.PhotonBeetleEngine; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Bytes; @@ -13,33 +14,16 @@ public class PhotonBeetleDigest extends BufferBaseDigest { + public static class Friend + { + private static final Friend INSTANCE = new Friend(); + private Friend() {} + } private final byte[] state; private final byte[][] state_2d; private final int STATE_INBYTES = 32; - private final int D = 8; + private static final int D = 8; private int blockCount; - private static final byte[][] RC = {//[D][12] - {1, 3, 7, 14, 13, 11, 6, 12, 9, 2, 5, 10}, - {0, 2, 6, 15, 12, 10, 7, 13, 8, 3, 4, 11}, - {2, 0, 4, 13, 14, 8, 5, 15, 10, 1, 6, 9}, - {6, 4, 0, 9, 10, 12, 1, 11, 14, 5, 2, 13}, - {14, 12, 8, 1, 2, 4, 9, 3, 6, 13, 10, 5}, - {15, 13, 9, 0, 3, 5, 8, 2, 7, 12, 11, 4}, - {13, 15, 11, 2, 1, 7, 10, 0, 5, 14, 9, 6}, - {9, 11, 15, 6, 5, 3, 14, 4, 1, 10, 13, 2} - }; - private static final byte[][] MixColMatrix = { //[D][D] - {2, 4, 2, 11, 2, 8, 5, 6}, - {12, 9, 8, 13, 7, 7, 5, 2}, - {4, 4, 13, 13, 9, 4, 13, 9}, - {1, 6, 5, 1, 12, 13, 15, 14}, - {15, 12, 9, 13, 14, 5, 14, 13}, - {9, 14, 5, 15, 4, 12, 9, 6}, - {12, 2, 2, 10, 3, 1, 1, 14}, - {15, 1, 13, 10, 5, 10, 2, 3} - }; - - private static final byte[] sbox = {12, 5, 6, 11, 9, 0, 10, 13, 3, 14, 15, 8, 4, 7, 1, 2}; public PhotonBeetleDigest() { @@ -60,7 +44,7 @@ protected void processBytes(byte[] input, int inOff) } else { - PHOTON_Permutation(); + PhotonBeetleEngine.PhotonPermutation(Friend.INSTANCE, state_2d, state); Bytes.xorTo(BlockSize, input, inOff, state, 0); } blockCount++; @@ -86,7 +70,7 @@ else if (blockCount == 4 && m_bufPos == 0) } else { - PHOTON_Permutation(); + PhotonBeetleEngine.PhotonPermutation(Friend.INSTANCE, state_2d, state); Bytes.xorTo(m_bufPos, m_buf, 0, state, 0); if (m_bufPos < BlockSize) { @@ -94,10 +78,10 @@ else if (blockCount == 4 && m_bufPos == 0) } state[STATE_INBYTES - 1] ^= (m_bufPos % BlockSize == 0 ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET; } - PHOTON_Permutation(); + PhotonBeetleEngine.PhotonPermutation(Friend.INSTANCE, state_2d, state); int SQUEEZE_RATE_INBYTES = 16; System.arraycopy(state, 0, output, outOff, SQUEEZE_RATE_INBYTES); - PHOTON_Permutation(); + PhotonBeetleEngine.PhotonPermutation(Friend.INSTANCE, state_2d, state); System.arraycopy(state, 0, output, outOff + SQUEEZE_RATE_INBYTES, DigestSize - SQUEEZE_RATE_INBYTES); } @@ -108,74 +92,4 @@ public void reset() Arrays.fill(state, (byte)0); blockCount = 0; } - - void PHOTON_Permutation() - { - int i, j, k; - int DSquare = 64; - int dr = 7; - int dq = 3; - for (i = 0; i < DSquare; i++) - { - state_2d[i >>> dq][i & dr] = (byte)(((state[i >> 1] & 0xFF) >>> (4 * (i & 1))) & 0xf); - } - int ROUND = 12; - for (int round = 0; round < ROUND; round++) - { - //AddKey - for (i = 0; i < D; i++) - { - state_2d[i][0] ^= RC[i][round]; - } - //SubCell - for (i = 0; i < D; i++) - { - for (j = 0; j < D; j++) - { - state_2d[i][j] = sbox[state_2d[i][j]]; - } - } - //ShiftRow - for (i = 1; i < D; i++) - { - System.arraycopy(state_2d[i], 0, state, 0, D); - System.arraycopy(state, i, state_2d[i], 0, D - i); - System.arraycopy(state, 0, state_2d[i], D - i, i); - } - //MixColumn - for (j = 0; j < D; j++) - { - for (i = 0; i < D; i++) - { - int sum = 0; - - for (k = 0; k < D; k++) - { - int x = MixColMatrix[i][k], b = state_2d[k][j]; - - sum ^= x * (b & 1); - sum ^= x * (b & 2); - sum ^= x * (b & 4); - sum ^= x * (b & 8); - } - - int t0 = sum >>> 4; - sum = (sum & 15) ^ t0 ^ (t0 << 1); - - int t1 = sum >>> 4; - sum = (sum & 15) ^ t1 ^ (t1 << 1); - - state[i] = (byte)sum; - } - for (i = 0; i < D; i++) - { - state_2d[i][j] = state[i]; - } - } - } - for (i = 0; i < DSquare; i += 2) - { - state[i >>> 1] = (byte)(((state_2d[i >>> dq][i & dr] & 0xf)) | ((state_2d[i >>> dq][(i + 1) & dr] & 0xf) << 4)); - } - } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 2d103b3121..93947d0b7d 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -1,5 +1,7 @@ package org.bouncycastle.crypto.engines; +import org.bouncycastle.crypto.digests.PhotonBeetleDigest; + /** * Photon-Beetle, * https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/photon-beetle-spec-final.pdf @@ -25,9 +27,9 @@ public enum PhotonBeetleParameters private final int RATE_INBYTES_HALF; private final int STATE_INBYTES; private final int LAST_THREE_BITS_OFFSET; - private final int D = 8; + private static final int D = 8; private boolean aadFinished; - private final byte[][] RC = { + private static final byte[][] RC = { {1, 3, 7, 14, 13, 11, 6, 12, 9, 2, 5, 10}, {0, 2, 6, 15, 12, 10, 7, 13, 8, 3, 4, 11}, {2, 0, 4, 13, 14, 8, 5, 15, 10, 1, 6, 9}, @@ -37,7 +39,7 @@ public enum PhotonBeetleParameters {13, 15, 11, 2, 1, 7, 10, 0, 5, 14, 9, 6}, {9, 11, 15, 6, 5, 3, 14, 4, 1, 10, 13, 2} }; - private final byte[][] MixColMatrix = { + private static final byte[][] MixColMatrix = { {2, 4, 2, 11, 2, 8, 5, 6}, {12, 9, 8, 13, 7, 7, 5, 2}, {4, 4, 13, 13, 9, 4, 13, 9}, @@ -48,7 +50,7 @@ public enum PhotonBeetleParameters {15, 1, 13, 10, 5, 10, 2, 3} }; - private final byte[] sbox = {12, 5, 6, 11, 9, 0, 10, 13, 3, 14, 15, 8, 4, 7, 1, 2}; + private static final byte[] sbox = {12, 5, 6, 11, 9, 0, 10, 13, 3, 14, 15, 8, 4, 7, 1, 2}; public PhotonBeetleEngine(PhotonBeetleParameters pbp) { @@ -93,7 +95,7 @@ protected void init(byte[] key, byte[] iv) protected void processBufferAAD(byte[] input, int inOff) { - PHOTON_Permutation(); + PhotonPermutation(state_2d, state); XOR(input, inOff, BlockSize); } @@ -106,7 +108,7 @@ public void processFinalAAD() { if (m_aadPos != 0) { - PHOTON_Permutation(); + PhotonPermutation(state_2d, state); XOR(m_aad, 0, m_aadPos); if (m_aadPos < BlockSize) { @@ -123,14 +125,14 @@ public void processFinalAAD() protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { - PHOTON_Permutation(); + PhotonPermutation(state_2d, state); rhoohr(output, outOff, input, inOff, BlockSize); XOR(input, inOff, BlockSize); } protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - PHOTON_Permutation(); + PhotonPermutation(state_2d, state); rhoohr(output, outOff, input, inOff, BlockSize); XOR(output, outOff, BlockSize); } @@ -151,7 +153,7 @@ protected void processFinalBlock(byte[] output, int outOff) { if (bufferLen != 0) { - PHOTON_Permutation(); + PhotonPermutation(state_2d, state); rhoohr(output, outOff, m_buf, 0, bufferLen); if (forEncryption) { @@ -172,7 +174,7 @@ protected void processFinalBlock(byte[] output, int outOff) { state[STATE_INBYTES - 1] ^= 1 << LAST_THREE_BITS_OFFSET; } - PHOTON_Permutation(); + PhotonPermutation(state_2d, state); mac = new byte[MAC_SIZE]; System.arraycopy(state, 0, mac, 0, MAC_SIZE); } @@ -188,7 +190,7 @@ protected void reset(boolean clearMac) super.reset(clearMac); } - private void PHOTON_Permutation() + private static void PhotonPermutation(byte[][] state_2d, byte[] state) { int i, j, k; int dq = 3; @@ -302,4 +304,14 @@ private void XOR(byte[] in_right, int rOff, int iolen_inbytes) state[i] ^= in_right[rOff++]; } } + + public static void PhotonPermutation(PhotonBeetleDigest.Friend friend, byte[][] state_2d, byte[] state) + { + if (null == friend) + { + throw new NullPointerException("This method is only for use by PhotonBeetleDigest"); + } + + PhotonPermutation(state_2d, state); + } } From b77d97051737a0c6de1c4d31197fc5ce4d7c9a74 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 24 Jan 2025 22:49:43 +0700 Subject: [PATCH 056/890] Refactor extensions code in X.509 types --- .../asymmetric/x509/X509CRLEntryObject.java | 39 +---- .../provider/asymmetric/x509/X509CRLImpl.java | 82 ++++------ .../asymmetric/x509/X509CertificateImpl.java | 83 +++-------- .../asymmetric/x509/X509SignatureUtil.java | 27 ++++ .../jce/provider/X509CRLEntryObject.java | 39 +---- .../jce/provider/X509CRLObject.java | 82 ++++------ .../jce/provider/X509CertificateObject.java | 137 +++++++---------- .../jce/provider/X509SignatureUtil.java | 26 ++++ .../x509/X509V2AttributeCertificate.java | 6 +- .../asymmetric/x509/X509SignatureUtil.java | 27 ++++ .../jce/provider/X509CRLObject.java | 80 ++++------ .../jce/provider/X509CertificateObject.java | 139 +++++++---------- .../asymmetric/x509/X509CRLEntryObject.java | 35 +---- .../provider/asymmetric/x509/X509CRLImpl.java | 87 +++++------ .../asymmetric/x509/X509CRLObject.java | 2 +- .../asymmetric/x509/X509CertificateImpl.java | 87 ++++------- .../x509/X509CertificateObject.java | 5 +- .../asymmetric/x509/X509SignatureUtil.java | 26 ++++ .../jce/provider/X509CRLEntryObject.java | 38 ++--- .../jce/provider/X509CRLObject.java | 83 ++++------- .../jce/provider/X509CertificateObject.java | 140 +++++++----------- .../jce/provider/X509SignatureUtil.java | 28 +++- 22 files changed, 535 insertions(+), 763 deletions(-) diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java index e7e3a76a1c..e7c20daf1b 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java @@ -15,6 +15,7 @@ import org.bouncycastle.asn1.ASN1Enumerated; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.util.ASN1Dump; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.CRLReason; @@ -78,9 +79,9 @@ protected X509CRLEntryObject( */ public boolean hasUnsupportedCriticalExtension() { - Set extns = getCriticalExtensionOIDs(); + Extensions extensions = c.getExtensions(); - return extns != null && !extns.isEmpty(); + return extensions != null && extensions.hasAnyCriticalExtensions(); } private X500Name loadCertificateIssuer(boolean isIndirect, X500Name previousCertificateIssuer) @@ -90,15 +91,15 @@ private X500Name loadCertificateIssuer(boolean isIndirect, X500Name previousCert return null; } - Extension ext = getExtension(Extension.certificateIssuer); - if (ext == null) + ASN1OctetString extValue = Extensions.getExtensionValue(c.getExtensions(), Extension.certificateIssuer); + if (extValue == null) { return previousCertificateIssuer; } try { - GeneralName[] names = GeneralNames.getInstance(ext.getParsedValue()).getNames(); + GeneralName[] names = GeneralNames.getInstance(extValue.getOctets()).getNames(); for (int i = 0; i < names.length; i++) { if (names[i].getTagNo() == GeneralName.directoryName) @@ -166,35 +167,9 @@ public Set getNonCriticalExtensionOIDs() return getExtensionOIDs(false); } - private Extension getExtension(ASN1ObjectIdentifier oid) - { - Extensions exts = c.getExtensions(); - - if (exts != null) - { - return exts.getExtension(oid); - } - - return null; - } - public byte[] getExtensionValue(String oid) { - Extension ext = getExtension(new ASN1ObjectIdentifier(oid)); - - if (ext != null) - { - try - { - return ext.getExtnValue().getEncoded(); - } - catch (Exception e) - { - throw new IllegalStateException("Exception encoding: " + e.toString()); - } - } - - return null; + return X509SignatureUtil.getExtensionValue(c.getExtensions(), oid); } /** diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java index 20f6972188..e2de3fb4fc 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java @@ -54,7 +54,6 @@ import org.bouncycastle.jcajce.util.JcaJceHelper; import org.bouncycastle.jce.X509Principal; import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Exceptions; import org.bouncycastle.util.Strings; /** @@ -84,30 +83,41 @@ abstract class X509CRLImpl this.isIndirect = isIndirect; } - /** - * Will return true if any extensions are present and marked - * as critical as we currently dont handle any extensions! - */ public boolean hasUnsupportedCriticalExtension() { - Set extns = getCriticalExtensionOIDs(); - - if (extns == null) + if (getVersion() == 2) { - return false; - } + Extensions extensions = c.getExtensions(); + if (extensions != null) + { + Enumeration e = extensions.oids(); + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); - extns.remove(Extension.issuingDistributionPoint.getId()); - extns.remove(Extension.deltaCRLIndicator.getId()); + if (Extension.issuingDistributionPoint.equals(oid) || + Extension.deltaCRLIndicator.equals(oid)) + { + continue; + } + + Extension ext = extensions.getExtension(oid); + if (ext.isCritical()) + { + return true; + } + } + } + } - return !extns.isEmpty(); + return false; } private Set getExtensionOIDs(boolean critical) { if (this.getVersion() == 2) { - Extensions extensions = c.getTBSCertList().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -144,26 +154,7 @@ public Set getNonCriticalExtensionOIDs() public byte[] getExtensionValue(String oid) { - if (oid != null) - { - ASN1ObjectIdentifier asn1Oid = ASN1ObjectIdentifier.tryFromID(oid); - if (asn1Oid != null) - { - ASN1OctetString extValue = getExtensionValue(c, asn1Oid); - if (null != extValue) - { - try - { - return extValue.getEncoded(); - } - catch (Exception e) - { - throw Exceptions.illegalStateException("error parsing " + e.getMessage(), e); - } - } - } - } - return null; + return X509SignatureUtil.getExtensionValue(c.getExtensions(), oid); } public void verify(PublicKey key) @@ -548,7 +539,7 @@ public String toString() X509SignatureUtil.prettyPrintSignature(this.getSignature(), buf, nl); - Extensions extensions = c.getTBSCertList().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -708,25 +699,8 @@ public boolean isRevoked(Certificate cert) static byte[] getExtensionOctets(CertificateList c, ASN1ObjectIdentifier oid) { - ASN1OctetString extValue = getExtensionValue(c, oid); - if (null != extValue) - { - return extValue.getOctets(); - } - return null; - } + ASN1OctetString extValue = Extensions.getExtensionValue(c.getExtensions(), oid); - static ASN1OctetString getExtensionValue(CertificateList c, ASN1ObjectIdentifier oid) - { - Extensions exts = c.getTBSCertList().getExtensions(); - if (null != exts) - { - Extension ext = exts.getExtension(oid); - if (null != ext) - { - return ext.getExtnValue(); - } - } - return null; + return extValue == null ? null : extValue.getOctets(); } } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java index 2665531c27..71e4832042 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java @@ -271,7 +271,7 @@ public boolean[] getKeyUsage() return Arrays.clone(keyUsage); } - public List getExtendedKeyUsage() + public List getExtendedKeyUsage() throws CertificateParsingException { byte[] extOctets = getExtensionOctets(c, Extension.extendedKeyUsage); @@ -330,7 +330,7 @@ public Set getCriticalExtensionOIDs() if (this.getVersion() == 3) { Set set = new HashSet(); - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -356,26 +356,7 @@ public Set getCriticalExtensionOIDs() public byte[] getExtensionValue(String oid) { - if (oid != null) - { - ASN1ObjectIdentifier asn1Oid = ASN1ObjectIdentifier.tryFromID(oid); - if (asn1Oid != null) - { - ASN1OctetString extValue = getExtensionValue(c, asn1Oid); - if (null != extValue) - { - try - { - return extValue.getEncoded(); - } - catch (Exception e) - { - throw Exceptions.illegalStateException("error parsing " + e.getMessage(), e); - } - } - } - } - return null; + return X509SignatureUtil.getExtensionValue(c.getExtensions(), oid); } public Set getNonCriticalExtensionOIDs() @@ -383,7 +364,7 @@ public Set getNonCriticalExtensionOIDs() if (this.getVersion() == 3) { Set set = new HashSet(); - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -409,35 +390,32 @@ public Set getNonCriticalExtensionOIDs() public boolean hasUnsupportedCriticalExtension() { - if (this.getVersion() == 3) + if (getVersion() == 3) { - Extensions extensions = c.getTBSCertificate().getExtensions(); - + Extensions extensions = c.getExtensions(); if (extensions != null) { - Enumeration e = extensions.oids(); - + Enumeration e = extensions.oids(); while (e.hasMoreElements()) { ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); - if (oid.equals(Extension.keyUsage) - || oid.equals(Extension.certificatePolicies) - || oid.equals(Extension.policyMappings) - || oid.equals(Extension.inhibitAnyPolicy) - || oid.equals(Extension.cRLDistributionPoints) - || oid.equals(Extension.issuingDistributionPoint) - || oid.equals(Extension.deltaCRLIndicator) - || oid.equals(Extension.policyConstraints) - || oid.equals(Extension.basicConstraints) - || oid.equals(Extension.subjectAlternativeName) - || oid.equals(Extension.nameConstraints)) + if (Extension.keyUsage.equals(oid) || + Extension.certificatePolicies.equals(oid) || + Extension.policyMappings.equals(oid) || + Extension.inhibitAnyPolicy.equals(oid) || + Extension.cRLDistributionPoints.equals(oid) || + Extension.issuingDistributionPoint.equals(oid) || + Extension.deltaCRLIndicator.equals(oid) || + Extension.policyConstraints.equals(oid) || + Extension.basicConstraints.equals(oid) || + Extension.subjectAlternativeName.equals(oid) || + Extension.nameConstraints.equals(oid)) { continue; } - Extension ext = extensions.getExtension(oid); - + Extension ext = extensions.getExtension(oid); if (ext.isCritical()) { return true; @@ -477,7 +455,7 @@ public String toString() X509SignatureUtil.prettyPrintSignature(this.getSignature(), buf, nl); - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -852,25 +830,8 @@ private static Collection getAlternativeNames(org.bouncycastle.asn1.x509.Certifi static byte[] getExtensionOctets(org.bouncycastle.asn1.x509.Certificate c, ASN1ObjectIdentifier oid) { - ASN1OctetString extValue = getExtensionValue(c, oid); - if (null != extValue) - { - return extValue.getOctets(); - } - return null; - } + ASN1OctetString extValue = Extensions.getExtensionValue(c.getExtensions(), oid); - static ASN1OctetString getExtensionValue(org.bouncycastle.asn1.x509.Certificate c, ASN1ObjectIdentifier oid) - { - Extensions exts = c.getTBSCertificate().getExtensions(); - if (null != exts) - { - Extension ext = exts.getExtension(oid); - if (null != ext) - { - return ext.getExtnValue(); - } - } - return null; + return extValue == null ? null : extValue.getOctets(); } } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java index cae057337d..86641be60b 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java @@ -15,16 +15,19 @@ import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.RSASSAPSSparams; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.Extensions; import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.internal.asn1.edec.EdECObjectIdentifiers; import org.bouncycastle.internal.asn1.misc.MiscObjectIdentifiers; import org.bouncycastle.internal.asn1.oiw.OIWObjectIdentifiers; import org.bouncycastle.jcajce.util.MessageDigestUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Exceptions; import org.bouncycastle.util.Objects; import org.bouncycastle.util.Properties; import org.bouncycastle.util.encoders.Hex; @@ -59,6 +62,30 @@ static boolean areEquivalentAlgorithms(AlgorithmIdentifier id1, AlgorithmIdentif return Objects.areEqual(id1.getParameters(), id2.getParameters()); } + static byte[] getExtensionValue(Extensions extensions, String oid) + { + if (oid != null) + { + ASN1ObjectIdentifier asn1Oid = ASN1ObjectIdentifier.tryFromID(oid); + if (asn1Oid != null) + { + ASN1OctetString extValue = Extensions.getExtensionValue(extensions, asn1Oid); + if (null != extValue) + { + try + { + return extValue.getEncoded(); + } + catch (Exception e) + { + throw Exceptions.illegalStateException("error parsing " + e.getMessage(), e); + } + } + } + } + return null; + } + private static boolean isAbsentOrEmptyParameters(ASN1Encodable parameters) { return parameters == null || DERNull.INSTANCE.equals(parameters); diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java index bbfacab4cb..a253aeaedd 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java @@ -15,6 +15,7 @@ import org.bouncycastle.asn1.ASN1Enumerated; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.util.ASN1Dump; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.CRLReason; @@ -77,9 +78,9 @@ public X509CRLEntryObject( */ public boolean hasUnsupportedCriticalExtension() { - Set extns = getCriticalExtensionOIDs(); + Extensions extensions = c.getExtensions(); - return extns != null && !extns.isEmpty(); + return extensions != null && extensions.hasAnyCriticalExtensions(); } private X500Name loadCertificateIssuer(boolean isIndirect, X500Name previousCertificateIssuer) @@ -89,15 +90,15 @@ private X500Name loadCertificateIssuer(boolean isIndirect, X500Name previousCert return null; } - Extension ext = getExtension(Extension.certificateIssuer); - if (ext == null) + ASN1OctetString extValue = Extensions.getExtensionValue(c.getExtensions(), Extension.certificateIssuer); + if (extValue == null) { return previousCertificateIssuer; } try { - GeneralName[] names = GeneralNames.getInstance(ext.getParsedValue()).getNames(); + GeneralName[] names = GeneralNames.getInstance(extValue.getOctets()).getNames(); for (int i = 0; i < names.length; i++) { if (names[i].getTagNo() == GeneralName.directoryName) @@ -165,35 +166,9 @@ public Set getNonCriticalExtensionOIDs() return getExtensionOIDs(false); } - private Extension getExtension(ASN1ObjectIdentifier oid) - { - Extensions exts = c.getExtensions(); - - if (exts != null) - { - return exts.getExtension(oid); - } - - return null; - } - public byte[] getExtensionValue(String oid) { - Extension ext = getExtension(new ASN1ObjectIdentifier(oid)); - - if (ext != null) - { - try - { - return ext.getExtnValue().getEncoded(); - } - catch (Exception e) - { - throw new RuntimeException("error encoding " + e.toString()); - } - } - - return null; + return X509SignatureUtil.getExtensionValue(c.getExtensions(), oid); } /** diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java index 5b32a13225..44e102edde 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java @@ -41,7 +41,9 @@ import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.IssuingDistributionPoint; import org.bouncycastle.asn1.x509.TBSCertList; +import org.bouncycastle.asn1.x509.Time; import org.bouncycastle.jce.X509Principal; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; @@ -108,30 +110,41 @@ public X509CRLObject( } } - /** - * Will return true if any extensions are present and marked - * as critical as we currently dont handle any extensions! - */ public boolean hasUnsupportedCriticalExtension() { - Set extns = getCriticalExtensionOIDs(); - - if (extns == null) + if (getVersion() == 2) { - return false; - } + Extensions extensions = c.getExtensions(); + if (extensions != null) + { + Enumeration e = extensions.oids(); + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); - extns.remove(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT); - extns.remove(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR); + if (Extension.issuingDistributionPoint.equals(oid) || + Extension.deltaCRLIndicator.equals(oid)) + { + continue; + } - return !extns.isEmpty(); + Extension ext = extensions.getExtension(oid); + if (ext.isCritical()) + { + return true; + } + } + } + } + + return false; } private Set getExtensionOIDs(boolean critical) { if (this.getVersion() == 2) { - Extensions extensions = c.getTBSCertList().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -168,26 +181,7 @@ public Set getNonCriticalExtensionOIDs() public byte[] getExtensionValue(String oid) { - Extensions exts = c.getTBSCertList().getExtensions(); - - if (exts != null) - { - Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); - - if (ext != null) - { - try - { - return ext.getExtnValue().getEncoded(); - } - catch (Exception e) - { - throw new IllegalStateException("error parsing " + e.toString()); - } - } - } - - return null; + return X509SignatureUtil.getExtensionValue(c.getExtensions(), oid); } public byte[] getEncoded() @@ -304,14 +298,11 @@ public Date getThisUpdate() public Date getNextUpdate() { - if (c.getNextUpdate() != null) - { - return c.getNextUpdate().getDate(); - } + Time nextUpdate = c.getNextUpdate(); - return null; + return null == nextUpdate ? null : nextUpdate.getDate(); } - + private Set loadCRLEntries() { Set entrySet = new HashSet(); @@ -407,16 +398,7 @@ public String getSigAlgOID() public byte[] getSigAlgParams() { - if (sigAlgParams != null) - { - byte[] tmp = new byte[sigAlgParams.length]; - - System.arraycopy(sigAlgParams, 0, tmp, 0, tmp.length); - - return tmp; - } - - return null; + return Arrays.clone(sigAlgParams); } /** @@ -458,7 +440,7 @@ public String toString() } } - Extensions extensions = c.getTBSCertList().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java index 3954e217a6..90ca8d730a 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509CertificateObject.java @@ -38,6 +38,7 @@ import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1String; @@ -87,7 +88,7 @@ public X509CertificateObject( try { - byte[] bytes = this.getExtensionBytes("2.5.29.19"); + byte[] bytes = getExtensionOctets(c, Extension.basicConstraints); if (bytes != null) { @@ -101,7 +102,7 @@ public X509CertificateObject( try { - byte[] bytes = this.getExtensionBytes("2.5.29.15"); + byte[] bytes = getExtensionOctets(c, Extension.keyUsage); if (bytes != null) { ASN1BitString bits = ASN1BitString.getInstance(ASN1Primitive.fromByteArray(bytes)); @@ -333,32 +334,29 @@ public boolean[] getKeyUsage() public List getExtendedKeyUsage() throws CertificateParsingException { - byte[] bytes = this.getExtensionBytes("2.5.29.37"); + byte[] extOctets = getExtensionOctets(c, Extension.extendedKeyUsage); + if (null == extOctets) + { + return null; + } - if (bytes != null) + try { - try - { - ASN1InputStream dIn = new ASN1InputStream(bytes); - ASN1Sequence seq = (ASN1Sequence)dIn.readObject(); - List list = new ArrayList(); + ASN1Sequence seq = ASN1Sequence.getInstance(extOctets); - for (int i = 0; i != seq.size(); i++) - { - list.add(((ASN1ObjectIdentifier)seq.getObjectAt(i)).getId()); - } - - return Collections.unmodifiableList(list); - } - catch (Exception e) + List list = new ArrayList(); + for (int i = 0; i != seq.size(); i++) { - throw new CertificateParsingException("error processing extended key usage extension"); + list.add(((ASN1ObjectIdentifier)seq.getObjectAt(i)).getId()); } + return Collections.unmodifiableList(list); + } + catch (Exception e) + { + throw new CertificateParsingException("error processing extended key usage extension"); } - - return null; } - + public int getBasicConstraints() { if (basicConstraints == null || !basicConstraints.isCA()) @@ -378,13 +376,13 @@ public int getBasicConstraints() public Collection getSubjectAlternativeNames() throws CertificateParsingException { - return getAlternativeNames(getExtensionBytes(Extension.subjectAlternativeName.getId())); + return getAlternativeNames(c, Extension.subjectAlternativeName); } public Collection getIssuerAlternativeNames() throws CertificateParsingException { - return getAlternativeNames(getExtensionBytes(Extension.issuerAlternativeName.getId())); + return getAlternativeNames(c, Extension.issuerAlternativeName); } public Set getCriticalExtensionOIDs() @@ -392,7 +390,7 @@ public Set getCriticalExtensionOIDs() if (this.getVersion() == 3) { Set set = new HashSet(); - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -416,44 +414,9 @@ public Set getCriticalExtensionOIDs() return null; } - private byte[] getExtensionBytes(String oid) - { - Extensions exts = c.getTBSCertificate().getExtensions(); - - if (exts != null) - { - Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); - if (ext != null) - { - return ext.getExtnValue().getOctets(); - } - } - - return null; - } - public byte[] getExtensionValue(String oid) { - Extensions exts = c.getTBSCertificate().getExtensions(); - - if (exts != null) - { - Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); - - if (ext != null) - { - try - { - return ext.getExtnValue().getEncoded(); - } - catch (Exception e) - { - throw new IllegalStateException("error parsing " + e.toString()); - } - } - } - - return null; + return X509SignatureUtil.getExtensionValue(c.getExtensions(), oid); } public Set getNonCriticalExtensionOIDs() @@ -461,7 +424,7 @@ public Set getNonCriticalExtensionOIDs() if (this.getVersion() == 3) { Set set = new HashSet(); - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -487,36 +450,32 @@ public Set getNonCriticalExtensionOIDs() public boolean hasUnsupportedCriticalExtension() { - if (this.getVersion() == 3) + if (getVersion() == 3) { - Extensions extensions = c.getTBSCertificate().getExtensions(); - + Extensions extensions = c.getExtensions(); if (extensions != null) { - Enumeration e = extensions.oids(); - + Enumeration e = extensions.oids(); while (e.hasMoreElements()) { ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); - String oidId = oid.getId(); - - if (oidId.equals(RFC3280CertPathUtilities.KEY_USAGE) - || oidId.equals(RFC3280CertPathUtilities.CERTIFICATE_POLICIES) - || oidId.equals(RFC3280CertPathUtilities.POLICY_MAPPINGS) - || oidId.equals(RFC3280CertPathUtilities.INHIBIT_ANY_POLICY) - || oidId.equals(RFC3280CertPathUtilities.CRL_DISTRIBUTION_POINTS) - || oidId.equals(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT) - || oidId.equals(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR) - || oidId.equals(RFC3280CertPathUtilities.POLICY_CONSTRAINTS) - || oidId.equals(RFC3280CertPathUtilities.BASIC_CONSTRAINTS) - || oidId.equals(RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME) - || oidId.equals(RFC3280CertPathUtilities.NAME_CONSTRAINTS)) + + if (Extension.keyUsage.equals(oid) || + Extension.certificatePolicies.equals(oid) || + Extension.policyMappings.equals(oid) || + Extension.inhibitAnyPolicy.equals(oid) || + Extension.cRLDistributionPoints.equals(oid) || + Extension.issuingDistributionPoint.equals(oid) || + Extension.deltaCRLIndicator.equals(oid) || + Extension.policyConstraints.equals(oid) || + Extension.basicConstraints.equals(oid) || + Extension.subjectAlternativeName.equals(oid) || + Extension.nameConstraints.equals(oid)) { continue; } - Extension ext = extensions.getExtension(oid); - + Extension ext = extensions.getExtension(oid); if (ext.isCritical()) { return true; @@ -667,7 +626,7 @@ public String toString() } } - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -852,17 +811,18 @@ private boolean isAlgIdEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2) return id1.getParameters().equals(id2.getParameters()); } - private static Collection getAlternativeNames(byte[] extVal) + private static Collection getAlternativeNames(org.bouncycastle.asn1.x509.Certificate c, ASN1ObjectIdentifier oid) throws CertificateParsingException { - if (extVal == null) + byte[] extOctets = getExtensionOctets(c, oid); + if (extOctets == null) { return null; } try { Collection temp = new ArrayList(); - Enumeration it = ASN1Sequence.getInstance(extVal).getObjects(); + Enumeration it = ASN1Sequence.getInstance(extOctets).getObjects(); while (it.hasMoreElements()) { GeneralName genName = GeneralName.getInstance(it.nextElement()); @@ -916,4 +876,11 @@ private static Collection getAlternativeNames(byte[] extVal) throw new CertificateParsingException(e.getMessage()); } } + + private static byte[] getExtensionOctets(org.bouncycastle.asn1.x509.Certificate c, ASN1ObjectIdentifier oid) + { + ASN1OctetString extValue = Extensions.getExtensionValue(c.getExtensions(), oid); + + return extValue == null ? null : extValue.getOctets(); + } } diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java b/prov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java index 15e8a3e3a8..572eba6122 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/X509SignatureUtil.java @@ -11,6 +11,7 @@ import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; @@ -18,11 +19,36 @@ import org.bouncycastle.asn1.pkcs.RSASSAPSSparams; import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.Extensions; import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.internal.asn1.oiw.OIWObjectIdentifiers; class X509SignatureUtil { + static byte[] getExtensionValue(Extensions extensions, String oid) + { + if (oid != null) + { + ASN1ObjectIdentifier asn1Oid = ASN1ObjectIdentifier.tryFromID(oid); + if (asn1Oid != null) + { + ASN1OctetString extValue = Extensions.getExtensionValue(extensions, asn1Oid); + if (null != extValue) + { + try + { + return extValue.getEncoded(); + } + catch (Exception e) + { + throw new IllegalStateException("error parsing " + e.toString()); + } + } + } + } + return null; + } + private static boolean isAbsentOrEmptyParameters(ASN1Encodable parameters) { return parameters == null || DERNull.INSTANCE.equals(parameters); diff --git a/prov/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificate.java b/prov/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificate.java index 8eb5e983af..9ef019376b 100644 --- a/prov/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificate.java +++ b/prov/src/main/java/org/bouncycastle/x509/X509V2AttributeCertificate.java @@ -265,12 +265,12 @@ public Set getCriticalExtensionOIDs() { return getExtensionOIDs(true); } - + public boolean hasUnsupportedCriticalExtension() { - Set extensions = getCriticalExtensionOIDs(); + Extensions extensions = cert.getAcinfo().getExtensions(); - return extensions != null && !extensions.isEmpty(); + return extensions != null && extensions.hasAnyCriticalExtensions(); } public X509Attribute[] getAttributes() diff --git a/prov/src/main/jdk1.1/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java b/prov/src/main/jdk1.1/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java index f964f7a0e8..a54d4aa287 100644 --- a/prov/src/main/jdk1.1/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java +++ b/prov/src/main/jdk1.1/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java @@ -17,15 +17,18 @@ import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Null; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.internal.asn1.edec.EdECObjectIdentifiers; import org.bouncycastle.internal.asn1.oiw.OIWObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.RSASSAPSSparams; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.Extensions; import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.jcajce.util.MessageDigestUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Exceptions; import org.bouncycastle.util.Strings; class X509SignatureUtil @@ -40,6 +43,30 @@ class X509SignatureUtil algNames.put(X9ObjectIdentifiers.id_dsa_with_sha1, "SHA1withDSA"); } + static byte[] getExtensionValue(Extensions extensions, String oid) + { + if (oid != null) + { + ASN1ObjectIdentifier asn1Oid = ASN1ObjectIdentifier.tryFromID(oid); + if (asn1Oid != null) + { + ASN1OctetString extValue = Extensions.getExtensionValue(extensions, asn1Oid); + if (null != extValue) + { + try + { + return extValue.getEncoded(); + } + catch (Exception e) + { + throw Exceptions.illegalStateException("error parsing " + e.getMessage(), e); + } + } + } + } + return null; + } + private static boolean isAbsentOrEmptyParameters(ASN1Encodable parameters) { return parameters == null || DERNull.INSTANCE.equals(parameters); diff --git a/prov/src/main/jdk1.1/org/bouncycastle/jce/provider/X509CRLObject.java b/prov/src/main/jdk1.1/org/bouncycastle/jce/provider/X509CRLObject.java index d256e7d1a0..9e5e6a9872 100644 --- a/prov/src/main/jdk1.1/org/bouncycastle/jce/provider/X509CRLObject.java +++ b/prov/src/main/jdk1.1/org/bouncycastle/jce/provider/X509CRLObject.java @@ -37,7 +37,9 @@ import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.IssuingDistributionPoint; import org.bouncycastle.asn1.x509.TBSCertList; +import org.bouncycastle.asn1.x509.Time; import org.bouncycastle.jce.X509Principal; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.x509.extension.X509ExtensionUtil; @@ -102,30 +104,41 @@ public X509CRLObject( } } - /** - * Will return true if any extensions are present and marked - * as critical as we currently dont handle any extensions! - */ public boolean hasUnsupportedCriticalExtension() { - Set extns = getCriticalExtensionOIDs(); - - if (extns == null) + if (getVersion() == 2) { - return false; - } + Extensions extensions = c.getExtensions(); + if (extensions != null) + { + Enumeration e = extensions.oids(); + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); - extns.remove(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT); - extns.remove(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR); + if (Extension.issuingDistributionPoint.equals(oid) || + Extension.deltaCRLIndicator.equals(oid)) + { + continue; + } + + Extension ext = extensions.getExtension(oid); + if (ext.isCritical()) + { + return true; + } + } + } + } - return !extns.isEmpty(); + return false; } private Set getExtensionOIDs(boolean critical) { if (this.getVersion() == 2) { - Extensions extensions = c.getTBSCertList().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -162,26 +175,7 @@ public Set getNonCriticalExtensionOIDs() public byte[] getExtensionValue(String oid) { - Extensions exts = c.getTBSCertList().getExtensions(); - - if (exts != null) - { - Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); - - if (ext != null) - { - try - { - return ext.getExtnValue().getEncoded(); - } - catch (Exception e) - { - throw new IllegalStateException("error parsing " + e.toString()); - } - } - } - - return null; + return X509SignatureUtil.getExtensionValue(c.getExtensions(), oid); } public byte[] getEncoded() @@ -250,12 +244,9 @@ public Date getThisUpdate() public Date getNextUpdate() { - if (c.getNextUpdate() != null) - { - return c.getNextUpdate().getDate(); - } + Time nextUpdate = c.getNextUpdate(); - return null; + return null == nextUpdate ? null : nextUpdate.getDate(); } private Set loadCRLEntries() @@ -353,16 +344,7 @@ public String getSigAlgOID() public byte[] getSigAlgParams() { - if (sigAlgParams != null) - { - byte[] tmp = new byte[sigAlgParams.length]; - - System.arraycopy(sigAlgParams, 0, tmp, 0, tmp.length); - - return tmp; - } - - return null; + return Arrays.clone(sigAlgParams); } /** @@ -404,7 +386,7 @@ public String toString() } } - Extensions extensions = c.getTBSCertList().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { diff --git a/prov/src/main/jdk1.1/org/bouncycastle/jce/provider/X509CertificateObject.java b/prov/src/main/jdk1.1/org/bouncycastle/jce/provider/X509CertificateObject.java index 1e4c262dd3..0ff3c41c21 100644 --- a/prov/src/main/jdk1.1/org/bouncycastle/jce/provider/X509CertificateObject.java +++ b/prov/src/main/jdk1.1/org/bouncycastle/jce/provider/X509CertificateObject.java @@ -34,6 +34,7 @@ import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1OutputStream; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; @@ -83,7 +84,7 @@ public X509CertificateObject( try { - byte[] bytes = this.getExtensionBytes("2.5.29.19"); + byte[] bytes = getExtensionOctets(c, Extension.basicConstraints); if (bytes != null) { @@ -97,10 +98,10 @@ public X509CertificateObject( try { - byte[] bytes = this.getExtensionBytes("2.5.29.15"); + byte[] bytes = getExtensionOctets(c, Extension.keyUsage); if (bytes != null) { - ASN1BitString bits = ASN1BitString.getInstance(ASN1Primitive.fromByteArray(bytes)); + ASN1BitString bits = ASN1BitString.getInstance(ASN1Primitive.fromByteArray(bytes)); bytes = bits.getBytes(); int length = (bytes.length * 8) - bits.getPadBits(); @@ -312,32 +313,29 @@ public boolean[] getKeyUsage() public List getExtendedKeyUsage() throws CertificateParsingException { - byte[] bytes = this.getExtensionBytes("2.5.29.37"); + byte[] extOctets = getExtensionOctets(c, Extension.extendedKeyUsage); + if (null == extOctets) + { + return null; + } - if (bytes != null) + try { - try - { - ASN1InputStream dIn = new ASN1InputStream(bytes); - ASN1Sequence seq = (ASN1Sequence)dIn.readObject(); - List list = new ArrayList(); + ASN1Sequence seq = ASN1Sequence.getInstance(extOctets); - for (int i = 0; i != seq.size(); i++) - { - list.add(((ASN1ObjectIdentifier)seq.getObjectAt(i)).getId()); - } - - return Collections.unmodifiableList(list); - } - catch (Exception e) + List list = new ArrayList(); + for (int i = 0; i != seq.size(); i++) { - throw new CertificateParsingException("error processing extended key usage extension"); + list.add(((ASN1ObjectIdentifier)seq.getObjectAt(i)).getId()); } + return Collections.unmodifiableList(list); + } + catch (Exception e) + { + throw new CertificateParsingException("error processing extended key usage extension"); } - - return null; } - + public int getBasicConstraints() { if (basicConstraints != null) @@ -365,13 +363,13 @@ public int getBasicConstraints() public Collection getSubjectAlternativeNames() throws CertificateParsingException { - return getAlternativeNames(getExtensionBytes(Extension.subjectAlternativeName.getId())); + return getAlternativeNames(c, Extension.subjectAlternativeName); } public Collection getIssuerAlternativeNames() throws CertificateParsingException { - return getAlternativeNames(getExtensionBytes(Extension.issuerAlternativeName.getId())); + return getAlternativeNames(c, Extension.issuerAlternativeName); } public Set getCriticalExtensionOIDs() @@ -379,7 +377,7 @@ public Set getCriticalExtensionOIDs() if (this.getVersion() == 3) { Set set = new HashSet(); - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -403,44 +401,9 @@ public Set getCriticalExtensionOIDs() return null; } - private byte[] getExtensionBytes(String oid) - { - Extensions exts = c.getTBSCertificate().getExtensions(); - - if (exts != null) - { - Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); - if (ext != null) - { - return ext.getExtnValue().getOctets(); - } - } - - return null; - } - public byte[] getExtensionValue(String oid) { - Extensions exts = c.getTBSCertificate().getExtensions(); - - if (exts != null) - { - Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); - - if (ext != null) - { - try - { - return ext.getExtnValue().getEncoded(); - } - catch (Exception e) - { - throw new IllegalStateException("error parsing " + e.toString()); - } - } - } - - return null; + return X509SignatureUtil.getExtensionValue(c.getExtensions(), oid); } public Set getNonCriticalExtensionOIDs() @@ -448,7 +411,7 @@ public Set getNonCriticalExtensionOIDs() if (this.getVersion() == 3) { Set set = new HashSet(); - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -474,36 +437,32 @@ public Set getNonCriticalExtensionOIDs() public boolean hasUnsupportedCriticalExtension() { - if (this.getVersion() == 3) + if (getVersion() == 3) { - Extensions extensions = c.getTBSCertificate().getExtensions(); - + Extensions extensions = c.getExtensions(); if (extensions != null) { - Enumeration e = extensions.oids(); - + Enumeration e = extensions.oids(); while (e.hasMoreElements()) { ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); - String oidId = oid.getId(); - - if (oidId.equals(RFC3280CertPathUtilities.KEY_USAGE) - || oidId.equals(RFC3280CertPathUtilities.CERTIFICATE_POLICIES) - || oidId.equals(RFC3280CertPathUtilities.POLICY_MAPPINGS) - || oidId.equals(RFC3280CertPathUtilities.INHIBIT_ANY_POLICY) - || oidId.equals(RFC3280CertPathUtilities.CRL_DISTRIBUTION_POINTS) - || oidId.equals(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT) - || oidId.equals(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR) - || oidId.equals(RFC3280CertPathUtilities.POLICY_CONSTRAINTS) - || oidId.equals(RFC3280CertPathUtilities.BASIC_CONSTRAINTS) - || oidId.equals(RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME) - || oidId.equals(RFC3280CertPathUtilities.NAME_CONSTRAINTS)) + + if (Extension.keyUsage.equals(oid) || + Extension.certificatePolicies.equals(oid) || + Extension.policyMappings.equals(oid) || + Extension.inhibitAnyPolicy.equals(oid) || + Extension.cRLDistributionPoints.equals(oid) || + Extension.issuingDistributionPoint.equals(oid) || + Extension.deltaCRLIndicator.equals(oid) || + Extension.policyConstraints.equals(oid) || + Extension.basicConstraints.equals(oid) || + Extension.subjectAlternativeName.equals(oid) || + Extension.nameConstraints.equals(oid)) { continue; } - Extension ext = extensions.getExtension(oid); - + Extension ext = extensions.getExtension(oid); if (ext.isCritical()) { return true; @@ -644,7 +603,7 @@ public String toString() } } - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -799,17 +758,18 @@ private boolean isAlgIdEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2) return id1.getParameters().equals(id2.getParameters()); } - private static Collection getAlternativeNames(byte[] extVal) + private static Collection getAlternativeNames(org.bouncycastle.asn1.x509.Certificate c, ASN1ObjectIdentifier oid) throws CertificateParsingException { - if (extVal == null) + byte[] extOctets = getExtensionOctets(c, oid); + if (extOctets == null) { return null; } try { Collection temp = new ArrayList(); - Enumeration it = ASN1Sequence.getInstance(extVal).getObjects(); + Enumeration it = ASN1Sequence.getInstance(extOctets).getObjects(); while (it.hasMoreElements()) { GeneralName genName = GeneralName.getInstance(it.nextElement()); @@ -854,4 +814,11 @@ private static Collection getAlternativeNames(byte[] extVal) throw new CertificateParsingException(e.getMessage()); } } + + private static byte[] getExtensionOctets(org.bouncycastle.asn1.x509.Certificate c, ASN1ObjectIdentifier oid) + { + ASN1OctetString extValue = Extensions.getExtensionValue(c.getExtensions(), oid); + + return extValue == null ? null : extValue.getOctets(); + } } diff --git a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java index 14d816c8da..e12e92d440 100644 --- a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java +++ b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java @@ -10,9 +10,10 @@ import java.util.Set; import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1Enumerated; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.ASN1Enumerated; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.util.ASN1Dump; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.CRLReason; @@ -78,9 +79,9 @@ public X509CRLEntryObject( */ public boolean hasUnsupportedCriticalExtension() { - Set extns = getCriticalExtensionOIDs(); + Extensions extensions = c.getExtensions(); - return extns != null && !extns.isEmpty(); + return extensions != null && extensions.hasAnyCriticalExtensions(); } private X500Name loadCertificateIssuer(boolean isIndirect, X500Name previousCertificateIssuer) @@ -90,16 +91,15 @@ private X500Name loadCertificateIssuer(boolean isIndirect, X500Name previousCert return null; } - byte[] ext = getExtensionValue(X509Extension.certificateIssuer.getId()); - if (ext == null) + ASN1OctetString extValue = Extensions.getExtensionValue(c.getExtensions(), Extension.certificateIssuer); + if (extValue == null) { return previousCertificateIssuer; } try { - GeneralName[] names = GeneralNames.getInstance( - X509ExtensionUtil.fromExtensionValue(ext)).getNames(); + GeneralName[] names = GeneralNames.getInstance(extValue.getOctets()).getNames(); for (int i = 0; i < names.length; i++) { if (names[i].getTagNo() == GeneralName.directoryName) @@ -168,26 +168,7 @@ public Set getNonCriticalExtensionOIDs() public byte[] getExtensionValue(String oid) { - Extensions exts = c.getExtensions(); - - if (exts != null) - { - Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); - - if (ext != null) - { - try - { - return ext.getExtnValue().getEncoded(); - } - catch (Exception e) - { - throw new RuntimeException("error encoding " + e.toString()); - } - } - } - - return null; + return X509SignatureUtil.getExtensionValue(c.getExtensions(), oid); } /** diff --git a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java index a0055b04c8..e11c0e9949 100644 --- a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java +++ b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLImpl.java @@ -41,6 +41,7 @@ import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.IssuingDistributionPoint; import org.bouncycastle.asn1.x509.TBSCertList; +import org.bouncycastle.asn1.x509.Time; import org.bouncycastle.jcajce.interfaces.BCX509Certificate; import org.bouncycastle.jcajce.io.OutputStreamFactory; import org.bouncycastle.jcajce.util.JcaJceHelper; @@ -76,30 +77,41 @@ abstract class X509CRLImpl this.isIndirect = isIndirect; } - /** - * Will return true if any extensions are present and marked - * as critical as we currently dont handle any extensions! - */ public boolean hasUnsupportedCriticalExtension() { - Set extns = getCriticalExtensionOIDs(); - - if (extns == null) + if (getVersion() == 2) { - return false; - } + Extensions extensions = c.getExtensions(); + if (extensions != null) + { + Enumeration e = extensions.oids(); + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); + + if (Extension.issuingDistributionPoint.equals(oid) || + Extension.deltaCRLIndicator.equals(oid)) + { + continue; + } - extns.remove(Extension.issuingDistributionPoint.getId()); - extns.remove(Extension.deltaCRLIndicator.getId()); + Extension ext = extensions.getExtension(oid); + if (ext.isCritical()) + { + return true; + } + } + } + } - return !extns.isEmpty(); + return false; } private Set getExtensionOIDs(boolean critical) { if (this.getVersion() == 2) { - Extensions extensions = c.getTBSCertList().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -136,19 +148,7 @@ public Set getNonCriticalExtensionOIDs() public byte[] getExtensionValue(String oid) { - ASN1OctetString extValue = getExtensionValue(c, oid); - if (null != extValue) - { - try - { - return extValue.getEncoded(); - } - catch (Exception e) - { - throw new IllegalStateException("error parsing " + e.toString()); - } - } - return null; + return X509SignatureUtil.getExtensionValue(c.getExtensions(), oid); } public byte[] getEncoded() @@ -285,14 +285,11 @@ public Date getThisUpdate() public Date getNextUpdate() { - if (c.getNextUpdate() != null) - { - return c.getNextUpdate().getDate(); - } + Time nextUpdate = c.getNextUpdate(); - return null; + return null == nextUpdate ? null : nextUpdate.getDate(); } - + private Set loadCRLEntries() { Set entrySet = new HashSet(); @@ -430,7 +427,7 @@ public String toString() } } - Extensions extensions = c.getTBSCertList().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -595,28 +592,10 @@ public boolean isRevoked(Certificate cert) return false; } - protected static byte[] getExtensionOctets(CertificateList c, String oid) + static byte[] getExtensionOctets(CertificateList c, ASN1ObjectIdentifier oid) { - ASN1OctetString extValue = getExtensionValue(c, oid); - if (null != extValue) - { - return extValue.getOctets(); - } - return null; - } + ASN1OctetString extValue = Extensions.getExtensionValue(c.getExtensions(), oid); - protected static ASN1OctetString getExtensionValue(CertificateList c, String oid) - { - Extensions exts = c.getTBSCertList().getExtensions(); - if (null != exts) - { - Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); - if (null != ext) - { - return ext.getExtnValue(); - } - } - return null; + return extValue == null ? null : extValue.getOctets(); } } - diff --git a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java index dfb0b0dc8d..964a705db4 100644 --- a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java +++ b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java @@ -135,7 +135,7 @@ private static boolean isIndirectCRL(CertificateList c) throws CRLException { try { - byte[] extOctets = getExtensionOctets(c, Extension.issuingDistributionPoint.getId()); + byte[] extOctets = getExtensionOctets(c, Extension.issuingDistributionPoint); if (null == extOctets) { return false; diff --git a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java index 527be010bf..f9ed96804b 100644 --- a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java +++ b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateImpl.java @@ -244,7 +244,7 @@ public boolean[] getKeyUsage() public List getExtendedKeyUsage() throws CertificateParsingException { - byte[] extOctets = getExtensionOctets(c, "2.5.29.37"); + byte[] extOctets = getExtensionOctets(c, Extension.extendedKeyUsage); if (null == extOctets) { return null; @@ -294,13 +294,13 @@ public int getBasicConstraints() public Collection getSubjectAlternativeNames() throws CertificateParsingException { - return getAlternativeNames(c, Extension.subjectAlternativeName.getId()); + return getAlternativeNames(c, Extension.subjectAlternativeName); } public Collection getIssuerAlternativeNames() throws CertificateParsingException { - return getAlternativeNames(c, Extension.issuerAlternativeName.getId()); + return getAlternativeNames(c, Extension.issuerAlternativeName); } public Set getCriticalExtensionOIDs() @@ -308,7 +308,7 @@ public Set getCriticalExtensionOIDs() if (this.getVersion() == 3) { Set set = new HashSet(); - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -332,22 +332,9 @@ public Set getCriticalExtensionOIDs() return null; } - public byte[] getExtensionValue(String oid) + public byte[] getExtensionValue(String oid) { - ASN1OctetString extValue = getExtensionValue(c, oid); - if (null != extValue) - { - try - { - return extValue.getEncoded(); - } - catch (Exception e) - { - throw new IllegalStateException("error parsing " + e.toString()); - } - } - - return null; + return X509SignatureUtil.getExtensionValue(c.getExtensions(), oid); } public Set getNonCriticalExtensionOIDs() @@ -355,7 +342,7 @@ public Set getNonCriticalExtensionOIDs() if (this.getVersion() == 3) { Set set = new HashSet(); - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -381,35 +368,32 @@ public Set getNonCriticalExtensionOIDs() public boolean hasUnsupportedCriticalExtension() { - if (this.getVersion() == 3) + if (getVersion() == 3) { - Extensions extensions = c.getTBSCertificate().getExtensions(); - + Extensions extensions = c.getExtensions(); if (extensions != null) { - Enumeration e = extensions.oids(); - + Enumeration e = extensions.oids(); while (e.hasMoreElements()) { ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); - if (oid.equals(Extension.keyUsage) - || oid.equals(Extension.certificatePolicies) - || oid.equals(Extension.policyMappings) - || oid.equals(Extension.inhibitAnyPolicy) - || oid.equals(Extension.cRLDistributionPoints) - || oid.equals(Extension.issuingDistributionPoint) - || oid.equals(Extension.deltaCRLIndicator) - || oid.equals(Extension.policyConstraints) - || oid.equals(Extension.basicConstraints) - || oid.equals(Extension.subjectAlternativeName) - || oid.equals(Extension.nameConstraints)) + if (Extension.keyUsage.equals(oid) || + Extension.certificatePolicies.equals(oid) || + Extension.policyMappings.equals(oid) || + Extension.inhibitAnyPolicy.equals(oid) || + Extension.cRLDistributionPoints.equals(oid) || + Extension.issuingDistributionPoint.equals(oid) || + Extension.deltaCRLIndicator.equals(oid) || + Extension.policyConstraints.equals(oid) || + Extension.basicConstraints.equals(oid) || + Extension.subjectAlternativeName.equals(oid) || + Extension.nameConstraints.equals(oid)) { continue; } - Extension ext = extensions.getExtension(oid); - + Extension ext = extensions.getExtension(oid); if (ext.isCritical()) { return true; @@ -475,7 +459,7 @@ public String toString() } } - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -678,7 +662,7 @@ private boolean isAlgIdEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2) return id1.getParameters().equals(id2.getParameters()); } - private static Collection getAlternativeNames(org.bouncycastle.asn1.x509.Certificate c, String oid) + private static Collection getAlternativeNames(org.bouncycastle.asn1.x509.Certificate c, ASN1ObjectIdentifier oid) throws CertificateParsingException { byte[] extOctets = getExtensionOctets(c, oid); @@ -737,27 +721,10 @@ private static Collection getAlternativeNames(org.bouncycastle.asn1.x509.Certifi } } - protected static byte[] getExtensionOctets(org.bouncycastle.asn1.x509.Certificate c, String oid) + static byte[] getExtensionOctets(org.bouncycastle.asn1.x509.Certificate c, ASN1ObjectIdentifier oid) { - ASN1OctetString extValue = getExtensionValue(c, oid); - if (null != extValue) - { - return extValue.getOctets(); - } - return null; - } + ASN1OctetString extValue = Extensions.getExtensionValue(c.getExtensions(), oid); - protected static ASN1OctetString getExtensionValue(org.bouncycastle.asn1.x509.Certificate c, String oid) - { - Extensions exts = c.getTBSCertificate().getExtensions(); - if (null != exts) - { - Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); - if (null != ext) - { - return ext.getExtnValue(); - } - } - return null; + return extValue == null ? null : extValue.getOctets(); } } diff --git a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java index a08a326deb..613fa78553 100644 --- a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java +++ b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CertificateObject.java @@ -14,6 +14,7 @@ import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; import org.bouncycastle.jcajce.util.JcaJceHelper; import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier; @@ -232,7 +233,7 @@ private static BasicConstraints createBasicConstraints(org.bouncycastle.asn1.x50 { try { - byte[] extOctets = getExtensionOctets(c, "2.5.29.19"); + byte[] extOctets = getExtensionOctets(c, Extension.basicConstraints); if (null == extOctets) { return null; @@ -250,7 +251,7 @@ private static boolean[] createKeyUsage(org.bouncycastle.asn1.x509.Certificate c { try { - byte[] extOctets = getExtensionOctets(c, "2.5.29.15"); + byte[] extOctets = getExtensionOctets(c, Extension.keyUsage); if (null == extOctets) { return null; diff --git a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java index 4e603a3c88..1d594b178c 100644 --- a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java +++ b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/asymmetric/x509/X509SignatureUtil.java @@ -17,12 +17,14 @@ import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Null; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.internal.asn1.edec.EdECObjectIdentifiers; import org.bouncycastle.internal.asn1.oiw.OIWObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.RSASSAPSSparams; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.Extensions; import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.jcajce.util.MessageDigestUtils; import org.bouncycastle.jce.provider.BouncyCastleProvider; @@ -39,6 +41,30 @@ class X509SignatureUtil algNames.put(X9ObjectIdentifiers.id_dsa_with_sha1, "SHA1withDSA"); } + static byte[] getExtensionValue(Extensions extensions, String oid) + { + if (oid != null) + { + ASN1ObjectIdentifier asn1Oid = ASN1ObjectIdentifier.tryFromID(oid); + if (asn1Oid != null) + { + ASN1OctetString extValue = Extensions.getExtensionValue(extensions, asn1Oid); + if (null != extValue) + { + try + { + return extValue.getEncoded(); + } + catch (Exception e) + { + throw new IllegalStateException("error parsing " + e.toString()); + } + } + } + } + return null; + } + private static boolean isAbsentOrEmptyParameters(ASN1Encodable parameters) { return parameters == null || DERNull.INSTANCE.equals(parameters); diff --git a/prov/src/main/jdk1.3/org/bouncycastle/jce/provider/X509CRLEntryObject.java b/prov/src/main/jdk1.3/org/bouncycastle/jce/provider/X509CRLEntryObject.java index af7be81ce9..9b0a50b568 100644 --- a/prov/src/main/jdk1.3/org/bouncycastle/jce/provider/X509CRLEntryObject.java +++ b/prov/src/main/jdk1.3/org/bouncycastle/jce/provider/X509CRLEntryObject.java @@ -12,6 +12,7 @@ import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Enumerated; import org.bouncycastle.asn1.util.ASN1Dump; import org.bouncycastle.asn1.x500.X500Name; @@ -78,9 +79,9 @@ public X509CRLEntryObject( */ public boolean hasUnsupportedCriticalExtension() { - Set extns = getCriticalExtensionOIDs(); + Extensions extensions = c.getExtensions(); - return extns != null && !extns.isEmpty(); + return extensions != null && extensions.hasAnyCriticalExtensions(); } private X500Name loadCertificateIssuer(boolean isIndirect, X500Name previousCertificateIssuer) @@ -90,16 +91,15 @@ private X500Name loadCertificateIssuer(boolean isIndirect, X500Name previousCert return null; } - byte[] ext = getExtensionValue(X509Extension.certificateIssuer.getId()); - if (ext == null) + ASN1OctetString extValue = Extensions.getExtensionValue(c.getExtensions(), Extension.certificateIssuer); + if (extValue == null) { return previousCertificateIssuer; } try { - GeneralName[] names = GeneralNames.getInstance( - X509ExtensionUtil.fromExtensionValue(ext)).getNames(); + GeneralName[] names = GeneralNames.getInstance(extValue.getOctets()).getNames(); for (int i = 0; i < names.length; i++) { if (names[i].getTagNo() == GeneralName.directoryName) @@ -121,8 +121,8 @@ public X509Principal getCertificateIssuer() { return null; } - try - { + try + { return new X509Principal(certificateIssuer.getEncoded()); } catch (Exception e) @@ -130,6 +130,7 @@ public X509Principal getCertificateIssuer() throw new IllegalStateException(e.toString()); } } + private Set getExtensionOIDs(boolean critical) { Extensions extensions = c.getExtensions(); @@ -168,26 +169,7 @@ public Set getNonCriticalExtensionOIDs() public byte[] getExtensionValue(String oid) { - Extensions exts = c.getExtensions(); - - if (exts != null) - { - Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); - - if (ext != null) - { - try - { - return ext.getExtnValue().getEncoded(); - } - catch (Exception e) - { - throw new RuntimeException("error encoding " + e.toString()); - } - } - } - - return null; + return X509SignatureUtil.getExtensionValue(c.getExtensions(), oid); } /** diff --git a/prov/src/main/jdk1.3/org/bouncycastle/jce/provider/X509CRLObject.java b/prov/src/main/jdk1.3/org/bouncycastle/jce/provider/X509CRLObject.java index 43fcadc46c..3020ee486b 100644 --- a/prov/src/main/jdk1.3/org/bouncycastle/jce/provider/X509CRLObject.java +++ b/prov/src/main/jdk1.3/org/bouncycastle/jce/provider/X509CRLObject.java @@ -37,9 +37,10 @@ import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.IssuingDistributionPoint; import org.bouncycastle.asn1.x509.TBSCertList; +import org.bouncycastle.asn1.x509.Time; import org.bouncycastle.jce.X509Principal; -import org.bouncycastle.jce.provider.RFC3280CertPathUtilities; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.x509.extension.X509ExtensionUtil; @@ -104,30 +105,41 @@ public X509CRLObject( } } - /** - * Will return true if any extensions are present and marked - * as critical as we currently dont handle any extensions! - */ public boolean hasUnsupportedCriticalExtension() { - Set extns = getCriticalExtensionOIDs(); - - if (extns == null) + if (getVersion() == 2) { - return false; - } + Extensions extensions = c.getExtensions(); + if (extensions != null) + { + Enumeration e = extensions.oids(); + while (e.hasMoreElements()) + { + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); - extns.remove(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT); - extns.remove(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR); + if (Extension.issuingDistributionPoint.equals(oid) || + Extension.deltaCRLIndicator.equals(oid)) + { + continue; + } + + Extension ext = extensions.getExtension(oid); + if (ext.isCritical()) + { + return true; + } + } + } + } - return !extns.isEmpty(); + return false; } private Set getExtensionOIDs(boolean critical) { if (this.getVersion() == 2) { - Extensions extensions = c.getTBSCertList().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -164,26 +176,7 @@ public Set getNonCriticalExtensionOIDs() public byte[] getExtensionValue(String oid) { - Extensions exts = c.getTBSCertList().getExtensions(); - - if (exts != null) - { - Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); - - if (ext != null) - { - try - { - return ext.getExtnValue().getEncoded(); - } - catch (Exception e) - { - throw new IllegalStateException("error parsing " + e.toString()); - } - } - } - - return null; + return X509SignatureUtil.getExtensionValue(c.getExtensions(), oid); } public byte[] getEncoded() @@ -252,14 +245,11 @@ public Date getThisUpdate() public Date getNextUpdate() { - if (c.getNextUpdate() != null) - { - return c.getNextUpdate().getDate(); - } + Time nextUpdate = c.getNextUpdate(); - return null; + return null == nextUpdate ? null : nextUpdate.getDate(); } - + private Set loadCRLEntries() { Set entrySet = new HashSet(); @@ -355,16 +345,7 @@ public String getSigAlgOID() public byte[] getSigAlgParams() { - if (sigAlgParams != null) - { - byte[] tmp = new byte[sigAlgParams.length]; - - System.arraycopy(sigAlgParams, 0, tmp, 0, tmp.length); - - return tmp; - } - - return null; + return Arrays.clone(sigAlgParams); } /** @@ -406,7 +387,7 @@ public String toString() } } - Extensions extensions = c.getTBSCertList().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { diff --git a/prov/src/main/jdk1.3/org/bouncycastle/jce/provider/X509CertificateObject.java b/prov/src/main/jdk1.3/org/bouncycastle/jce/provider/X509CertificateObject.java index 21bae59d99..ee198b6196 100644 --- a/prov/src/main/jdk1.3/org/bouncycastle/jce/provider/X509CertificateObject.java +++ b/prov/src/main/jdk1.3/org/bouncycastle/jce/provider/X509CertificateObject.java @@ -34,6 +34,7 @@ import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1OutputStream; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; @@ -57,7 +58,6 @@ import org.bouncycastle.asn1.x509.KeyUsage; import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl; import org.bouncycastle.jce.X509Principal; -import org.bouncycastle.jce.provider.RFC3280CertPathUtilities; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier; import org.bouncycastle.util.Arrays; @@ -85,7 +85,7 @@ public X509CertificateObject( try { - byte[] bytes = this.getExtensionBytes("2.5.29.19"); + byte[] bytes = getExtensionOctets(c, Extension.basicConstraints); if (bytes != null) { @@ -99,10 +99,10 @@ public X509CertificateObject( try { - byte[] bytes = this.getExtensionBytes("2.5.29.15"); + byte[] bytes = getExtensionOctets(c, Extension.keyUsage); if (bytes != null) { - ASN1BitString bits = ASN1BitString.getInstance(ASN1Primitive.fromByteArray(bytes)); + ASN1BitString bits = ASN1BitString.getInstance(ASN1Primitive.fromByteArray(bytes)); bytes = bits.getBytes(); int length = (bytes.length * 8) - bits.getPadBits(); @@ -314,32 +314,29 @@ public boolean[] getKeyUsage() public List getExtendedKeyUsage() throws CertificateParsingException { - byte[] bytes = this.getExtensionBytes("2.5.29.37"); + byte[] extOctets = getExtensionOctets(c, Extension.extendedKeyUsage); + if (null == extOctets) + { + return null; + } - if (bytes != null) + try { - try - { - ASN1InputStream dIn = new ASN1InputStream(bytes); - ASN1Sequence seq = (ASN1Sequence)dIn.readObject(); - List list = new ArrayList(); + ASN1Sequence seq = ASN1Sequence.getInstance(extOctets); - for (int i = 0; i != seq.size(); i++) - { - list.add(((ASN1ObjectIdentifier)seq.getObjectAt(i)).getId()); - } - - return Collections.unmodifiableList(list); - } - catch (Exception e) + List list = new ArrayList(); + for (int i = 0; i != seq.size(); i++) { - throw new CertificateParsingException("error processing extended key usage extension"); + list.add(((ASN1ObjectIdentifier)seq.getObjectAt(i)).getId()); } + return Collections.unmodifiableList(list); + } + catch (Exception e) + { + throw new CertificateParsingException("error processing extended key usage extension"); } - - return null; } - + public int getBasicConstraints() { if (basicConstraints != null) @@ -367,13 +364,13 @@ public int getBasicConstraints() public Collection getSubjectAlternativeNames() throws CertificateParsingException { - return getAlternativeNames(getExtensionBytes(Extension.subjectAlternativeName.getId())); + return getAlternativeNames(c, Extension.subjectAlternativeName); } public Collection getIssuerAlternativeNames() throws CertificateParsingException { - return getAlternativeNames(getExtensionBytes(Extension.issuerAlternativeName.getId())); + return getAlternativeNames(c, Extension.issuerAlternativeName); } public Set getCriticalExtensionOIDs() @@ -381,7 +378,7 @@ public Set getCriticalExtensionOIDs() if (this.getVersion() == 3) { Set set = new HashSet(); - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -405,44 +402,9 @@ public Set getCriticalExtensionOIDs() return null; } - private byte[] getExtensionBytes(String oid) - { - Extensions exts = c.getTBSCertificate().getExtensions(); - - if (exts != null) - { - Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); - if (ext != null) - { - return ext.getExtnValue().getOctets(); - } - } - - return null; - } - public byte[] getExtensionValue(String oid) { - Extensions exts = c.getTBSCertificate().getExtensions(); - - if (exts != null) - { - Extension ext = exts.getExtension(new ASN1ObjectIdentifier(oid)); - - if (ext != null) - { - try - { - return ext.getExtnValue().getEncoded(); - } - catch (Exception e) - { - throw new IllegalStateException("error parsing " + e.toString()); - } - } - } - - return null; + return X509SignatureUtil.getExtensionValue(c.getExtensions(), oid); } public Set getNonCriticalExtensionOIDs() @@ -450,7 +412,7 @@ public Set getNonCriticalExtensionOIDs() if (this.getVersion() == 3) { Set set = new HashSet(); - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -476,36 +438,32 @@ public Set getNonCriticalExtensionOIDs() public boolean hasUnsupportedCriticalExtension() { - if (this.getVersion() == 3) + if (getVersion() == 3) { - Extensions extensions = c.getTBSCertificate().getExtensions(); - + Extensions extensions = c.getExtensions(); if (extensions != null) { - Enumeration e = extensions.oids(); - + Enumeration e = extensions.oids(); while (e.hasMoreElements()) { ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)e.nextElement(); - String oidId = oid.getId(); - - if (oidId.equals(RFC3280CertPathUtilities.KEY_USAGE) - || oidId.equals(RFC3280CertPathUtilities.CERTIFICATE_POLICIES) - || oidId.equals(RFC3280CertPathUtilities.POLICY_MAPPINGS) - || oidId.equals(RFC3280CertPathUtilities.INHIBIT_ANY_POLICY) - || oidId.equals(RFC3280CertPathUtilities.CRL_DISTRIBUTION_POINTS) - || oidId.equals(RFC3280CertPathUtilities.ISSUING_DISTRIBUTION_POINT) - || oidId.equals(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR) - || oidId.equals(RFC3280CertPathUtilities.POLICY_CONSTRAINTS) - || oidId.equals(RFC3280CertPathUtilities.BASIC_CONSTRAINTS) - || oidId.equals(RFC3280CertPathUtilities.SUBJECT_ALTERNATIVE_NAME) - || oidId.equals(RFC3280CertPathUtilities.NAME_CONSTRAINTS)) + + if (Extension.keyUsage.equals(oid) || + Extension.certificatePolicies.equals(oid) || + Extension.policyMappings.equals(oid) || + Extension.inhibitAnyPolicy.equals(oid) || + Extension.cRLDistributionPoints.equals(oid) || + Extension.issuingDistributionPoint.equals(oid) || + Extension.deltaCRLIndicator.equals(oid) || + Extension.policyConstraints.equals(oid) || + Extension.basicConstraints.equals(oid) || + Extension.subjectAlternativeName.equals(oid) || + Extension.nameConstraints.equals(oid)) { continue; } - Extension ext = extensions.getExtension(oid); - + Extension ext = extensions.getExtension(oid); if (ext.isCritical()) { return true; @@ -646,7 +604,7 @@ public String toString() } } - Extensions extensions = c.getTBSCertificate().getExtensions(); + Extensions extensions = c.getExtensions(); if (extensions != null) { @@ -801,17 +759,18 @@ private boolean isAlgIdEqual(AlgorithmIdentifier id1, AlgorithmIdentifier id2) return id1.getParameters().equals(id2.getParameters()); } - private static Collection getAlternativeNames(byte[] extVal) + private static Collection getAlternativeNames(org.bouncycastle.asn1.x509.Certificate c, ASN1ObjectIdentifier oid) throws CertificateParsingException { - if (extVal == null) + byte[] extOctets = getExtensionOctets(c, oid); + if (extOctets == null) { return null; } try { Collection temp = new ArrayList(); - Enumeration it = ASN1Sequence.getInstance(extVal).getObjects(); + Enumeration it = ASN1Sequence.getInstance(extOctets).getObjects(); while (it.hasMoreElements()) { GeneralName genName = GeneralName.getInstance(it.nextElement()); @@ -856,4 +815,11 @@ private static Collection getAlternativeNames(byte[] extVal) throw new CertificateParsingException(e.getMessage()); } } + + private static byte[] getExtensionOctets(org.bouncycastle.asn1.x509.Certificate c, ASN1ObjectIdentifier oid) + { + ASN1OctetString extValue = Extensions.getExtensionValue(c.getExtensions(), oid); + + return extValue == null ? null : extValue.getOctets(); + } } diff --git a/prov/src/main/jdk1.4/org/bouncycastle/jce/provider/X509SignatureUtil.java b/prov/src/main/jdk1.4/org/bouncycastle/jce/provider/X509SignatureUtil.java index 949d07a2dd..8e5c90a3b3 100644 --- a/prov/src/main/jdk1.4/org/bouncycastle/jce/provider/X509SignatureUtil.java +++ b/prov/src/main/jdk1.4/org/bouncycastle/jce/provider/X509SignatureUtil.java @@ -8,17 +8,43 @@ import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; -import org.bouncycastle.internal.asn1.oiw.OIWObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.RSASSAPSSparams; import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.Extensions; import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; +import org.bouncycastle.internal.asn1.oiw.OIWObjectIdentifiers; class X509SignatureUtil { + static byte[] getExtensionValue(Extensions extensions, String oid) + { + if (oid != null) + { + ASN1ObjectIdentifier asn1Oid = ASN1ObjectIdentifier.tryFromID(oid); + if (asn1Oid != null) + { + ASN1OctetString extValue = Extensions.getExtensionValue(extensions, asn1Oid); + if (null != extValue) + { + try + { + return extValue.getEncoded(); + } + catch (Exception e) + { + throw new IllegalStateException("error parsing " + e.toString()); + } + } + } + } + return null; + } + private static boolean isAbsentOrEmptyParameters(ASN1Encodable parameters) { return parameters == null || DERNull.INSTANCE.equals(parameters); From 8c39ea1b238e00b0f0e9dd63936548e76fe93056 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 28 Jan 2025 09:47:04 +1030 Subject: [PATCH 057/890] Refactor on PhotonBeetleEngine and PhotonBeetleDigest --- .../crypto/digests/PhotonBeetleDigest.java | 12 +++++++---- .../crypto/engines/PhotonBeetleEngine.java | 21 +++++++------------ .../java/org/bouncycastle/util/Bytes.java | 8 +++++++ 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java index 0606dedc8a..651fa2b475 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java @@ -17,11 +17,15 @@ public class PhotonBeetleDigest public static class Friend { private static final Friend INSTANCE = new Friend(); - private Friend() {} + + private Friend() + { + } } + private final byte[] state; private final byte[][] state_2d; - private final int STATE_INBYTES = 32; + private static final int STATE_INBYTES = 32; private static final int D = 8; private int blockCount; @@ -45,7 +49,7 @@ protected void processBytes(byte[] input, int inOff) else { PhotonBeetleEngine.PhotonPermutation(Friend.INSTANCE, state_2d, state); - Bytes.xorTo(BlockSize, input, inOff, state, 0); + Bytes.xorTo(BlockSize, input, inOff, state); } blockCount++; } @@ -71,7 +75,7 @@ else if (blockCount == 4 && m_bufPos == 0) else { PhotonBeetleEngine.PhotonPermutation(Friend.INSTANCE, state_2d, state); - Bytes.xorTo(m_bufPos, m_buf, 0, state, 0); + Bytes.xorTo(m_bufPos, m_buf, state); if (m_bufPos < BlockSize) { state[m_bufPos] ^= 0x01; // ozs diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 93947d0b7d..a1c01e66a6 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.engines; import org.bouncycastle.crypto.digests.PhotonBeetleDigest; +import org.bouncycastle.util.Bytes; /** * Photon-Beetle, @@ -96,7 +97,7 @@ protected void init(byte[] key, byte[] iv) protected void processBufferAAD(byte[] input, int inOff) { PhotonPermutation(state_2d, state); - XOR(input, inOff, BlockSize); + Bytes.xorTo(BlockSize, input, inOff, state); } public void processFinalAAD() @@ -109,7 +110,7 @@ public void processFinalAAD() if (m_aadPos != 0) { PhotonPermutation(state_2d, state); - XOR(m_aad, 0, m_aadPos); + Bytes.xorTo(m_aadPos, m_aad, state); if (m_aadPos < BlockSize) { state[m_aadPos] ^= 0x01; // ozs @@ -127,14 +128,14 @@ protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int { PhotonPermutation(state_2d, state); rhoohr(output, outOff, input, inOff, BlockSize); - XOR(input, inOff, BlockSize); + Bytes.xorTo(BlockSize, input, inOff, state); } protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { PhotonPermutation(state_2d, state); rhoohr(output, outOff, input, inOff, BlockSize); - XOR(output, outOff, BlockSize); + Bytes.xorTo(BlockSize, output, outOff, state); } @Override @@ -157,11 +158,11 @@ protected void processFinalBlock(byte[] output, int outOff) rhoohr(output, outOff, m_buf, 0, bufferLen); if (forEncryption) { - XOR(m_buf, 0, bufferLen); + Bytes.xorTo(bufferLen, m_buf, state); } else { - XOR(output, outOff, bufferLen); + Bytes.xorTo(bufferLen, output, outOff, state); } if (bufferLen < BlockSize) { @@ -297,14 +298,6 @@ private void rhoohr(byte[] ciphertext, int outOff, byte[] plaintext, int inOff, } } - private void XOR(byte[] in_right, int rOff, int iolen_inbytes) - { - for (int i = 0; i < iolen_inbytes; i++) - { - state[i] ^= in_right[rOff++]; - } - } - public static void PhotonPermutation(PhotonBeetleDigest.Friend friend, byte[][] state_2d, byte[] state) { if (null == friend) diff --git a/core/src/main/java/org/bouncycastle/util/Bytes.java b/core/src/main/java/org/bouncycastle/util/Bytes.java index 4db85758a0..c613181393 100644 --- a/core/src/main/java/org/bouncycastle/util/Bytes.java +++ b/core/src/main/java/org/bouncycastle/util/Bytes.java @@ -32,6 +32,14 @@ public static void xorTo(int len, byte[] x, byte[] z) } } + public static void xorTo(int len, byte[] x, int xOff, byte[] z) + { + for (int i = 0; i < len; ++i) + { + z[i] ^= x[xOff++]; + } + } + public static void xorTo(int len, byte[] x, int xOff, byte[] z, int zOff) { for (int i = 0; i < len; ++i) From 63916346a1fec2ea6a13598bac26cc25143714d8 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 28 Jan 2025 10:52:15 +1030 Subject: [PATCH 058/890] Refactor on Engines around xor --- .../crypto/engines/ElephantEngine.java | 40 ++++++++----------- .../crypto/engines/GiftCofbEngine.java | 27 ++++--------- .../crypto/engines/ISAPEngine.java | 24 ++--------- .../crypto/engines/RomulusEngine.java | 36 ++++------------- .../crypto/engines/SparkleEngine.java | 1 + .../crypto/engines/XoodyakEngine.java | 13 +++--- .../java/org/bouncycastle/util/Bytes.java | 16 ++++++++ .../main/java/org/bouncycastle/util/Pack.java | 17 ++++++++ 8 files changed, 76 insertions(+), 98 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index 9e0f578114..fcbeab04ad 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -2,6 +2,8 @@ import java.util.Arrays; +import org.bouncycastle.util.Bytes; + /** * Elephant AEAD v2, based on the current round 3 submission, https://www.esat.kuleuven.be/cosic/elephant/ * Reference C implementation: https://github.com/TimBeyne/Elephant @@ -283,14 +285,6 @@ private void lfsr_step() System.arraycopy(current_mask, 1, next_mask, 0, BlockSize - 1); } - private void xor_block(byte[] state, byte[] block, int bOff, int size) - { - for (int i = 0; i < size; ++i) - { - state[i] ^= block[i + bOff]; - } - } - @Override protected void init(byte[] k, byte[] iv) throws IllegalArgumentException @@ -349,13 +343,13 @@ private void computerCipherBlock(byte[] input, int inOff, int blockSize, byte[] { System.arraycopy(npub, 0, buffer, 0, IV_SIZE); Arrays.fill(buffer, IV_SIZE, BlockSize, (byte)0); - xor_block(buffer, current_mask, 0, BlockSize); - xor_block(buffer, next_mask, 0, BlockSize); + Bytes.xorTo(BlockSize, current_mask, buffer); + Bytes.xorTo(BlockSize, next_mask, buffer); instance.permutation(buffer); - xor_block(buffer, current_mask, 0, BlockSize); - xor_block(buffer, next_mask, 0, BlockSize); + Bytes.xorTo(BlockSize, current_mask, buffer); + Bytes.xorTo(BlockSize, next_mask, buffer); - xor_block(buffer, input, inOff, blockSize); + Bytes.xorTo(blockSize, input, inOff, buffer); System.arraycopy(buffer, 0, output, outOff, blockSize); } @@ -370,20 +364,20 @@ private void swapMasks() private void absorbAAD() { processAADBytes(buffer); - xor_block(buffer, next_mask, 0, BlockSize); + Bytes.xorTo(BlockSize, next_mask, buffer); instance.permutation(buffer); - xor_block(buffer, next_mask, 0, BlockSize); - xor_block(tag_buffer, buffer, 0, BlockSize); + Bytes.xorTo(BlockSize, next_mask, buffer); + Bytes.xorTo(BlockSize, buffer, tag_buffer); } private void absorbCiphertext() { - xor_block(buffer, previous_mask, 0, BlockSize); - xor_block(buffer, next_mask, 0, BlockSize); + Bytes.xorTo(BlockSize, previous_mask, buffer); + Bytes.xorTo(BlockSize, next_mask, buffer); instance.permutation(buffer); - xor_block(buffer, previous_mask, 0, BlockSize); - xor_block(buffer, next_mask, 0, BlockSize); - xor_block(tag_buffer, buffer, 0, BlockSize); + Bytes.xorTo(BlockSize, previous_mask, buffer); + Bytes.xorTo(BlockSize, next_mask, buffer); + Bytes.xorTo(BlockSize, buffer, tag_buffer); } protected void processFinalBlock(byte[] output, int outOff) @@ -396,9 +390,9 @@ protected void processFinalBlock(byte[] output, int outOff) int nb_it = Math.max(nblocks_c + 1, nblocks_ad - 1); processBytes(m_buf, output, outOff, nb_it, nblocks_m, nblocks_c, mlen, nblocks_ad); mac = new byte[MAC_SIZE]; - xor_block(tag_buffer, expanded_key, 0, BlockSize); + Bytes.xorTo(BlockSize, expanded_key, tag_buffer); instance.permutation(tag_buffer); - xor_block(tag_buffer, expanded_key, 0, BlockSize); + Bytes.xorTo(BlockSize, expanded_key, tag_buffer); System.arraycopy(tag_buffer, 0, mac, 0, MAC_SIZE); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java index 266aaee046..94c93f29e1 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java @@ -1,5 +1,7 @@ package org.bouncycastle.crypto.engines; +import org.bouncycastle.util.Bytes; + /** * GIFT-COFB v1.1, based on the current round 3 submission, https://www.isical.ac.in/~lightweight/COFB/ * Reference C implementation: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-submissions/elephant.zip @@ -114,20 +116,9 @@ private void giftb128(byte[] P, byte[] K, byte[] C) C[15] = (byte)(S[3]); } - private void xor_block(byte[] d, int dOff, byte[] s1, byte[] s2, int s2Off, int no_of_bytes) - { - for (int i = 0; i < no_of_bytes; i++) - { - d[i + dOff] = (byte)(s1[i] ^ s2[i + s2Off]); - } - } - private void xor_topbar_block(byte[] d, byte[] s1, byte[] s2) { - for (int i = 0; i < 8; i++) - { - d[i] = (byte)(s1[i] ^ s2[i]); - } + Bytes.xor(8, s1, s2, d); System.arraycopy(s1, 8, d, 8, 8); } @@ -148,10 +139,7 @@ private void triple_half_block(byte[] d, byte[] s) { byte[] tmp = new byte[8]; double_half_block(tmp, s); - for (int i = 0; i < 8; i++) - { - d[i] = (byte)(s[i] ^ tmp[i]); - } + Bytes.xor(8, s, tmp, d); } private void pho1(byte[] d, byte[] Y, byte[] M, int mOff, int no_of_bytes) @@ -182,18 +170,19 @@ else if (no_of_bytes < 16) } tmp[15] = (byte)((Y[7] & 0xFF) << 1 | (Y[0] & 0xFF) >>> 7); System.arraycopy(tmp, 0, Y, 0, 16); - xor_block(d, 0, Y, tmpM, 0, 16); + Bytes.xor(16, Y, tmpM, d); } private void pho(byte[] Y, byte[] M, int mOff, byte[] X, byte[] C, int cOff, int no_of_bytes) { - xor_block(C, cOff, Y, M, mOff, no_of_bytes); + Bytes.xor(no_of_bytes, Y, M, mOff, C, cOff); pho1(X, Y, M, mOff, no_of_bytes); } private void phoprime(byte[] Y, byte[] C, int cOff, byte[] X, byte[] M, int mOff, int no_of_bytes) { - xor_block(M, mOff, Y, C, cOff, no_of_bytes); + Bytes.xor(no_of_bytes, Y, C, cOff, M, mOff); + //xor_block(M, mOff, Y, C, cOff, no_of_bytes); pho1(X, Y, M, mOff, no_of_bytes); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index 78e9403820..df56146ac5 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -338,9 +338,9 @@ public ISAPAEAD_K() public void init() { k16 = new short[k.length >> 1]; - byteToShort(k, k16, k16.length); + Pack.littleEndianToShort(k, 0, k16, 0, k16.length); iv16 = new short[npub.length >> 1]; - byteToShort(npub, iv16, iv16.length); + Pack.littleEndianToShort(npub, 0, iv16, 0, iv16.length); //reset(); } @@ -428,11 +428,11 @@ public void processMACFinal(byte[] input, int inOff, int len, byte[] tag) SX[len >> 1] ^= 0x80 << ((len & 1) << 3); PermuteRoundsHX(SX, E, C); // Derive K* - shortToByte(SX, tag); + Pack.shortToLittleEndian(SX, 0, 8, tag, 0); isap_rk(ISAP_IV2_16, tag, KEY_SIZE, SX, KEY_SIZE, C); // Squeeze tag PermuteRoundsHX(SX, E, C); - shortToByte(SX, tag); + Pack.shortToLittleEndian(SX, 0, 8, tag, 0); } public void processEncBlock(byte[] input, int inOff, byte[] output, int outOff) @@ -462,22 +462,6 @@ private void byteToShortXor(byte[] input, int inOff, short[] output, int outLen) } } - private void byteToShort(byte[] input, short[] output, int outLen) - { - for (int i = 0; i < outLen; ++i) - { - output[i] = Pack.littleEndianToShort(input, (i << 1)); - } - } - - private void shortToByte(short[] input, byte[] output) - { - for (int i = 0; i < 8; ++i) - { - Pack.shortToLittleEndian(input[i], output, (i << 1)); - } - } - protected void rounds12X(short[] SX, short[] E, short[] C) { prepareThetaX(SX, C); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index 663f168046..3842ba1742 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.engines; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Bytes; /** * Romulus v1.3, based on the current round 3 submission, https://romulusae.github.io/romulus/ @@ -274,10 +275,7 @@ public void processBufferAAD(byte[] input, int inOff) { if (twist) { - for (int i = 0; i < 16; i++) - { - mac_s[i] = (byte)(mac_s[i] ^ input[inOff + i]); - } + Bytes.xorTo(MAC_SIZE, input, inOff, mac_s); } else { @@ -301,10 +299,7 @@ else if (m_aadPos != 0) m_aad[BlockSize - 1] = (byte)(m_aadPos & 0x0f); if (twist) { - for (int i = 0; i < BlockSize; i++) - { - mac_s[i] = (byte)(mac_s[i] ^ m_aad[i]); - } + Bytes.xorTo(BlockSize, m_aad, mac_s); } else { @@ -374,10 +369,7 @@ public void processBufferAAD(byte[] input, int inOff) { if (twist) { - for (int i = 0; i < AD_BLK_LEN_HALF; i++) - { - s[i] = (byte)(s[i] ^ input[inOff + i]); - } + Bytes.xorTo(AD_BLK_LEN_HALF, input, inOff, s); } else { @@ -397,10 +389,7 @@ public void processFinalAAD() pad(m_aad, 0, mp, AD_BLK_LEN_HALF, len8); if (twist) { - for (int i = 0; i < AD_BLK_LEN_HALF; i++) - { - s[i] = (byte)(s[i] ^ mp[i]); - } + Bytes.xorTo(AD_BLK_LEN_HALF, mp, s); } else { @@ -483,10 +472,7 @@ public void processFinalBlock(byte[] output, int outOff) int len8 = Math.min(m_bufPos, 16); System.arraycopy(npub, 0, S, 0, 16); block_cipher(S, Z, T, 0, CNT, (byte)64); - for (int i = 0; i < len8; i++) - { - output[i + outOff] = (byte)((m_buf[i]) ^ S[i]); - } + Bytes.xor(len8, m_buf, S, output, outOff); System.arraycopy(npub, 0, S, 0, 16); lfsr_gf56(CNT); @@ -587,10 +573,7 @@ public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int out { System.arraycopy(npub, 0, S, 0, 16); block_cipher(S, Z, T, 0, CNT, (byte)64); - for (int i = 0; i < AD_BLK_LEN_HALF; i++) - { - output[i + outOff] = (byte)((input[i + inOff]) ^ S[i]); - } + Bytes.xor(AD_BLK_LEN_HALF, S, input, inOff, output, outOff); System.arraycopy(npub, 0, S, 0, 16); block_cipher(S, Z, T, 0, CNT, (byte)65); System.arraycopy(S, 0, Z, 0, 16); @@ -615,10 +598,7 @@ public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int out { System.arraycopy(npub, 0, S, 0, 16); block_cipher(S, Z, T, 0, CNT, (byte)64); - for (int i = 0; i < AD_BLK_LEN_HALF; i++) - { - output[i + outOff] = (byte)((input[i + inOff]) ^ S[i]); - } + Bytes.xor(AD_BLK_LEN_HALF, S, input, inOff, output, outOff); System.arraycopy(npub, 0, S, 0, 16); block_cipher(S, Z, T, 0, CNT, (byte)65); System.arraycopy(S, 0, Z, 0, 16); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java index 088dcf9150..f7f8a2d48e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.engines; import org.bouncycastle.crypto.digests.SparkleDigest; +import org.bouncycastle.util.Bytes; import org.bouncycastle.util.Integers; import org.bouncycastle.util.Pack; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index bfb4630976..0c3644d736 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.engines; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Bytes; import org.bouncycastle.util.Integers; import org.bouncycastle.util.Pack; @@ -95,10 +96,8 @@ private void encrypt(byte[] input, int inOff, int len, byte[] output, int outOff System.arraycopy(input, inOff, P, 0, splitLen); Up(null, 0, Cu); /* Up without extract */ /* Extract from Up and Add */ - for (int i = 0; i < splitLen; i++) - { - output[outOff + i] = (byte)(input[inOff++] ^ state[i]); - } + Bytes.xor(splitLen, state, input, inOff, output, outOff); + inOff += splitLen; Down(P, 0, splitLen, 0x00); Cu = 0x00; outOff += splitLen; @@ -116,10 +115,8 @@ private void decrypt(byte[] input, int inOff, int len, byte[] output, int outOff splitLen = Math.min(len, BlockSize); /* use Rkout instead of Rsqueeze, this function is only called in keyed mode */ Up(null, 0, Cu); /* Up without extract */ /* Extract from Up and Add */ - for (int i = 0; i < splitLen; i++) - { - output[outOff + i] = (byte)(input[inOff++] ^ state[i]); - } + Bytes.xor(splitLen, state, input, inOff, output, outOff); + inOff += splitLen; Down(output, outOff, splitLen, 0x00); Cu = 0x00; outOff += splitLen; diff --git a/core/src/main/java/org/bouncycastle/util/Bytes.java b/core/src/main/java/org/bouncycastle/util/Bytes.java index c613181393..7173f0890f 100644 --- a/core/src/main/java/org/bouncycastle/util/Bytes.java +++ b/core/src/main/java/org/bouncycastle/util/Bytes.java @@ -24,6 +24,22 @@ public static void xor(int len, byte[] x, int xOff, byte[] y, int yOff, byte[] z } } + public static void xor(int len, byte[] x, byte[] y, byte[] z, int zOff) + { + for (int i = 0; i < len; ++i) + { + z[zOff++] = (byte)(x[i] ^ y[i]); + } + } + + public static void xor(int len, byte[] x, byte[] y, int yOff, byte[] z, int zOff) + { + for (int i = 0; i < len; ++i) + { + z[zOff++] = (byte)(x[i] ^ y[yOff++]); + } + } + public static void xorTo(int len, byte[] x, byte[] z) { for (int i = 0; i < len; ++i) diff --git a/core/src/main/java/org/bouncycastle/util/Pack.java b/core/src/main/java/org/bouncycastle/util/Pack.java index d3a360975f..1aeee73858 100644 --- a/core/src/main/java/org/bouncycastle/util/Pack.java +++ b/core/src/main/java/org/bouncycastle/util/Pack.java @@ -175,6 +175,15 @@ public static short littleEndianToShort(byte[] bs, int off) return (short)n; } + public static void littleEndianToShort(byte[] bs, int bOff, short[] ns, int nOff, int count) + { + for (int i = 0; i < count; ++i) + { + ns[nOff + i] = littleEndianToShort(bs, bOff); + bOff += 2; + } + } + public static int littleEndianToInt(byte[] bs, int off) { int n = bs[off] & 0xff; @@ -245,6 +254,14 @@ public static void shortToLittleEndian(short n, byte[] bs, int off) bs[++off] = (byte)(n >>> 8); } + public static void shortToLittleEndian(short[] ns, int nsOff, int nsLen, byte[] bs, int bsOff) + { + for (int i = 0; i < nsLen; ++i) + { + shortToLittleEndian(ns[nsOff + i], bs, bsOff); + bsOff += 2; + } + } public static byte[] shortToBigEndian(short n) { From 17176fcc6d8f6f0f45e3472f70e129cd93716ad3 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 28 Jan 2025 12:36:33 +1030 Subject: [PATCH 059/890] Extract AsconPermutation from Ascon, ISAP engines and digests. --- .../crypto/digests/AsconBaseDigest.java | 68 +++----- .../crypto/digests/AsconCXof128.java | 32 ++-- .../crypto/digests/AsconDigest.java | 22 ++- .../crypto/digests/AsconHash256.java | 10 +- .../bouncycastle/crypto/digests/AsconXof.java | 22 ++- .../crypto/digests/AsconXof128.java | 10 +- .../crypto/digests/ISAPDigest.java | 81 ++++------ .../crypto/engines/AsconAEAD128.java | 78 +++++---- .../crypto/engines/AsconBaseEngine.java | 81 +++------- .../crypto/engines/AsconEngine.java | 88 +++++------ .../engines/AsconPermutationFriend.java | 65 ++++++++ .../crypto/engines/ISAPEngine.java | 149 +++++++----------- 12 files changed, 321 insertions(+), 385 deletions(-) create mode 100644 core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java index a6f762fd49..545a18a59d 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java @@ -1,60 +1,28 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.OutputLengthException; -import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Longs; +import org.bouncycastle.crypto.engines.AsconPermutationFriend; abstract class AsconBaseDigest extends BufferBaseDigest { - protected long x0; - protected long x1; - protected long x2; - protected long x3; - protected long x4; + public static class Friend + { + private static final Friend INSTANCE = new Friend(); + private Friend() {} + } + + + AsconPermutationFriend.AsconPermutation p; protected int ASCON_PB_ROUNDS = 12; protected AsconBaseDigest() { super(ProcessingBufferType.Immediate, 8); + p = AsconPermutationFriend.getAsconPermutation(ISAPDigest.Friend.getFriend(Friend.INSTANCE)); DigestSize = 32; } - private void round(long C) - { - long t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); - long t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3)); - long t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4); - long t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4)); - long t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); - x0 = t0 ^ Longs.rotateRight(t0, 19) ^ Longs.rotateRight(t0, 28); - x1 = t1 ^ Longs.rotateRight(t1, 39) ^ Longs.rotateRight(t1, 61); - x2 = ~(t2 ^ Longs.rotateRight(t2, 1) ^ Longs.rotateRight(t2, 6)); - x3 = t3 ^ Longs.rotateRight(t3, 10) ^ Longs.rotateRight(t3, 17); - x4 = t4 ^ Longs.rotateRight(t4, 7) ^ Longs.rotateRight(t4, 41); - } - - protected void p(int nr) - { - if (nr == 12) - { - round(0xf0L); - round(0xe1L); - round(0xd2L); - round(0xc3L); - } - if (nr >= 8) - { - round(0xb4L); - round(0xa5L); - } - round(0x96L); - round(0x87L); - round(0x78L); - round(0x69L); - round(0x5aL); - round(0x4bL); - } protected abstract long pad(int i); @@ -68,8 +36,8 @@ protected void p(int nr) protected void processBytes(byte[] input, int inOff) { - x0 ^= loadBytes(input, inOff); - p(ASCON_PB_ROUNDS); + p.x0 ^= loadBytes(input, inOff); + p.p(ASCON_PB_ROUNDS); } protected void finish(byte[] output, int outOff) @@ -81,9 +49,9 @@ protected void finish(byte[] output, int outOff) protected void padAndAbsorb() { - x0 ^= loadBytes(m_buf, 0, m_bufPos); - x0 ^= pad(m_bufPos); - p(12); + p.x0 ^= loadBytes(m_buf, 0, m_bufPos); + p.x0 ^= pad(m_bufPos); + p.p(12); } protected void squeeze(byte[] output, int outOff, int len) @@ -91,13 +59,13 @@ protected void squeeze(byte[] output, int outOff, int len) /* squeeze full output blocks */ while (len > BlockSize) { - setBytes(x0, output, outOff); - p(ASCON_PB_ROUNDS); + setBytes(p.x0, output, outOff); + p.p(ASCON_PB_ROUNDS); outOff += BlockSize; len -= BlockSize; } /* squeeze final output block */ - setBytes(x0, output, outOff, len); + setBytes(p.x0, output, outOff, len); reset(); } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java index 60b0e36f43..283a6fc5ea 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java @@ -46,11 +46,11 @@ public AsconCXof128(byte[] s, int off, int len) } initState(s, off, len); // NOTE: Cache the initialized state - z0 = x0; - z1 = x1; - z2 = x2; - z3 = x3; - z4 = x4; + z0 = p.x0; + z1 = p.x1; + z2 = p.x2; + z3 = p.x3; + z4 = p.x4; } @Override @@ -131,23 +131,23 @@ public void reset() super.reset(); m_squeezing = false; /* initialize */ - x0 = z0; - x1 = z1; - x2 = z2; - x3 = z3; - x4 = z4; + p.x0 = z0; + p.x1 = z1; + p.x2 = z2; + p.x3 = z3; + p.x4 = z4; } private void initState(byte[] z, int zOff, int zLen) { - x0 = 7445901275803737603L; - x1 = 4886737088792722364L; - x2 = -1616759365661982283L; - x3 = 3076320316797452470L; - x4 = -8124743304765850554L; + p.x0 = 7445901275803737603L; + p.x1 = 4886737088792722364L; + p.x2 = -1616759365661982283L; + p.x3 = 3076320316797452470L; + p.x4 = -8124743304765850554L; long bitLength = ((long)zLen) << 3; Pack.longToLittleEndian(bitLength, m_buf, 0); - p(12); + p.p(12); update(z, zOff, zLen); padAndAbsorb(); m_squeezing = false; diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java index d837704e26..5cbf14a21f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java @@ -41,8 +41,6 @@ public AsconDigest(AsconParameters parameters) reset(); } - private final String algorithmName; - protected long pad(int i) { return 0x80L << (56 - (i << 3)); @@ -76,18 +74,18 @@ public void reset() switch (asconParameters) { case AsconHashA: - x0 = 92044056785660070L; - x1 = 8326807761760157607L; - x2 = 3371194088139667532L; - x3 = -2956994353054992515L; - x4 = -6828509670848688761L; + p.x0 = 92044056785660070L; + p.x1 = 8326807761760157607L; + p.x2 = 3371194088139667532L; + p.x3 = -2956994353054992515L; + p.x4 = -6828509670848688761L; break; case AsconHash: - x0 = -1255492011513352131L; - x1 = -8380609354527731710L; - x2 = -5437372128236807582L; - x3 = 4834782570098516968L; - x4 = 3787428097924915520L; + p.x0 = -1255492011513352131L; + p.x1 = -8380609354527731710L; + p.x2 = -5437372128236807582L; + p.x3 = 4834782570098516968L; + p.x4 = 3787428097924915520L; break; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java index 5324b98546..90c09ee44b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java @@ -52,10 +52,10 @@ public void reset() { super.reset(); /* initialize */ - x0 = -7269279749984954751L; - x1 = 5459383224871899602L; - x2 = -5880230600644446182L; - x3 = 4359436768738168243L; - x4 = 1899470422303676269L; + p.x0 = -7269279749984954751L; + p.x1 = 5459383224871899602L; + p.x2 = -5880230600644446182L; + p.x3 = 4359436768738168243L; + p.x4 = 1899470422303676269L; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java index 0ae98d904e..243ca86b52 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java @@ -42,8 +42,6 @@ public AsconXof(AsconXof.AsconParameters parameters) } reset(); } - - private final String algorithmName; private boolean m_squeezing = false; @Override @@ -126,18 +124,18 @@ public void reset() switch (asconParameters) { case AsconXof: - x0 = -5368810569253202922L; - x1 = 3121280575360345120L; - x2 = 7395939140700676632L; - x3 = 6533890155656471820L; - x4 = 5710016986865767350L; + p.x0 = -5368810569253202922L; + p.x1 = 3121280575360345120L; + p.x2 = 7395939140700676632L; + p.x3 = 6533890155656471820L; + p.x4 = 5710016986865767350L; break; case AsconXofA: - x0 = 4940560291654768690L; - x1 = -3635129828240960206L; - x2 = -597534922722107095L; - x3 = 2623493988082852443L; - x4 = -6283826724160825537L; + p.x0 = 4940560291654768690L; + p.x1 = -3635129828240960206L; + p.x2 = -597534922722107095L; + p.x3 = 2623493988082852443L; + p.x4 = -6283826724160825537L; break; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java index e1dcc5e8e3..adad4b158b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java @@ -97,11 +97,11 @@ public void reset() m_squeezing = false; super.reset(); /* initialize */ - x0 = -2701369817892108309L; - x1 = -3711838248891385495L; - x2 = -1778763697082575311L; - x3 = 1072114354614917324L; - x4 = -2282070310009238562L; + p.x0 = -2701369817892108309L; + p.x1 = -3711838248891385495L; + p.x2 = -1778763697082575311L; + p.x3 = 1072114354614917324L; + p.x4 = -2282070310009238562L; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java index 6163422c97..8fb0ca8ba5 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java @@ -1,6 +1,6 @@ package org.bouncycastle.crypto.digests; -import org.bouncycastle.util.Arrays; +import org.bouncycastle.crypto.engines.AsconPermutationFriend; import org.bouncycastle.util.Pack; /** @@ -14,47 +14,35 @@ public class ISAPDigest extends BufferBaseDigest { - private long x0, x1, x2, x3, x4; - private long t0, t1, t2, t3, t4; + public static class Friend + { + private static final Friend INSTANCE = new Friend(); + + private Friend() + { + } + + static Friend getFriend(AsconBaseDigest.Friend friend) + { + if (null == friend) + { + throw new NullPointerException("This method is only for use by AsconBaseDigest"); + } + return INSTANCE; + } + } + + private final AsconPermutationFriend.AsconPermutation p; public ISAPDigest() { super(ProcessingBufferType.Immediate, 8); + p = AsconPermutationFriend.getAsconPermutation(Friend.INSTANCE); DigestSize = 32; algorithmName = "ISAP Hash"; reset(); } - private void ROUND(long C) - { - t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); - t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3)); - t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4); - t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4)); - t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); - x0 = t0 ^ ROTR(t0, 19) ^ ROTR(t0, 28); - x1 = t1 ^ ROTR(t1, 39) ^ ROTR(t1, 61); - x2 = ~(t2 ^ ROTR(t2, 1) ^ ROTR(t2, 6)); - x3 = t3 ^ ROTR(t3, 10) ^ ROTR(t3, 17); - x4 = t4 ^ ROTR(t4, 7) ^ ROTR(t4, 41); - } - - private void P12() - { - ROUND(0xf0); - ROUND(0xe1); - ROUND(0xd2); - ROUND(0xc3); - ROUND(0xb4); - ROUND(0xa5); - ROUND(0x96); - ROUND(0x87); - ROUND(0x78); - ROUND(0x69); - ROUND(0x5a); - ROUND(0x4b); - } - private long ROTR(long x, long n) { return (x >>> n) | (x << (64 - n)); @@ -70,8 +58,8 @@ protected long U64BIG(long x) protected void processBytes(byte[] input, int inOff) { /* absorb */ - x0 ^= Pack.bigEndianToLong(input, inOff); - P12(); + p.x0 ^= Pack.bigEndianToLong(input, inOff); + p.p(12); } @Override @@ -79,21 +67,21 @@ protected void finish(byte[] output, int outOff) { /* absorb final input block */ int idx; - x0 ^= 0x80L << ((7 - m_bufPos) << 3); + p.x0 ^= 0x80L << ((7 - m_bufPos) << 3); while (m_bufPos > 0) { - x0 ^= (m_buf[--m_bufPos] & 0xFFL) << ((7 - m_bufPos) << 3); + p.x0 ^= (m_buf[--m_bufPos] & 0xFFL) << ((7 - m_bufPos) << 3); } - P12(); + p.p(12); // squeeze long[] out64 = new long[4]; for (idx = 0; idx < 3; ++idx) { - out64[idx] = U64BIG(x0); - P12(); + out64[idx] = U64BIG(p.x0); + p.p(12); } /* squeeze final output block */ - out64[idx] = U64BIG(x0); + out64[idx] = U64BIG(p.x0); Pack.longToLittleEndian(out64, output, outOff); } @@ -101,12 +89,11 @@ protected void finish(byte[] output, int outOff) public void reset() { super.reset(); - t0 = t1 = t2 = t3 = t4 = 0; /* init state */ - x0 = -1255492011513352131L; - x1 = -8380609354527731710L; - x2 = -5437372128236807582L; - x3 = 4834782570098516968L; - x4 = 3787428097924915520L; + p.x0 = -1255492011513352131L; + p.x1 = -8380609354527731710L; + p.x2 = -5437372128236807582L; + p.x3 = 4834782570098516968L; + p.x4 = 3787428097924915520L; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java index 449805fde7..e9fedb1195 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto.engines; +import org.bouncycastle.crypto.digests.ISAPDigest; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; @@ -55,37 +56,34 @@ protected void setBytes(long n, byte[] bs, int off) protected void ascon_aeadinit() { /* initialize */ - x0 = ASCON_IV; - x1 = K0; - x2 = K1; - x3 = N0; - x4 = N1; - p(12); - x3 ^= K0; - x4 ^= K1; + p.x0 = ASCON_IV; + p.x1 = K0; + p.x2 = K1; + p.x3 = N0; + p.x4 = N1; + p.p(12); + p.x3 ^= K0; + p.x4 ^= K1; } protected void processFinalAadBlock() { if (m_aadPos == BlockSize) { - x0 ^= loadBytes(m_aad, 0); - if (BlockSize == 16) - { - x1 ^= loadBytes(m_aad, 8 ); - } + p.x0 ^= loadBytes(m_aad, 0); + p.x1 ^= loadBytes(m_aad, 8); m_aadPos -= BlockSize; - p(nr); + p.p(nr); } Arrays.fill(m_aad, m_aadPos, AADBufferSize, (byte)0); if (m_aadPos >= 8) // ASCON_AEAD_RATE == 16 is implied { - x0 ^= Pack.littleEndianToLong(m_aad, 0); - x1 ^= Pack.littleEndianToLong(m_aad, 8) ^ pad(m_aadPos); + p.x0 ^= Pack.littleEndianToLong(m_aad, 0); + p.x1 ^= Pack.littleEndianToLong(m_aad, 8) ^ pad(m_aadPos); } else { - x0 ^= Pack.littleEndianToLong(m_aad, 0) ^ pad(m_aadPos); + p.x0 ^= Pack.littleEndianToLong(m_aad, 0) ^ pad(m_aadPos); } } @@ -96,23 +94,23 @@ protected void processFinalDecrypt(byte[] input, int inLen, byte[] output, int o long c0 = Pack.littleEndianToLong(input, 0); inLen -= 8; long c1 = Pack.littleEndianToLong(input, 8, inLen); - Pack.longToLittleEndian(x0 ^ c0, output, outOff); - Pack.longToLittleEndian(x1 ^ c1, output, outOff + 8, inLen); - x0 = c0; - x1 &= -(1L << (inLen << 3)); - x1 |= c1; - x1 ^= pad(inLen); + Pack.longToLittleEndian(p.x0 ^ c0, output, outOff); + Pack.longToLittleEndian(p.x1 ^ c1, output, outOff + 8, inLen); + p.x0 = c0; + p.x1 &= -(1L << (inLen << 3)); + p.x1 |= c1; + p.x1 ^= pad(inLen); } else { if (inLen != 0) { long c0 = Pack.littleEndianToLong(input, 0, inLen); - Pack.longToLittleEndian(x0 ^ c0, output, outOff, inLen); - x0 &= -(1L << (inLen << 3)); - x0 |= c0; + Pack.longToLittleEndian(p.x0 ^ c0, output, outOff, inLen); + p.x0 &= -(1L << (inLen << 3)); + p.x0 |= c0; } - x0 ^= pad(inLen); + p.x0 ^= pad(inLen); } finishData(State.DecFinal); } @@ -121,32 +119,32 @@ protected void processFinalEncrypt(byte[] input, int inLen, byte[] output, int o { if (inLen >= 8) // ASCON_AEAD_RATE == 16 is implied { - x0 ^= Pack.littleEndianToLong(input, 0); + p.x0 ^= Pack.littleEndianToLong(input, 0); inLen -= 8; - x1 ^= Pack.littleEndianToLong(input, 8, inLen); - Pack.longToLittleEndian(x0, output, outOff); - Pack.longToLittleEndian(x1, output, outOff + 8); - x1 ^= pad(inLen); + p.x1 ^= Pack.littleEndianToLong(input, 8, inLen); + Pack.longToLittleEndian(p.x0, output, outOff); + Pack.longToLittleEndian(p.x1, output, outOff + 8); + p.x1 ^= pad(inLen); } else { if (inLen != 0) { - x0 ^= Pack.littleEndianToLong(input, 0, inLen); - Pack.longToLittleEndian(x0, output, outOff, inLen); + p.x0 ^= Pack.littleEndianToLong(input, 0, inLen); + Pack.longToLittleEndian(p.x0, output, outOff, inLen); } - x0 ^= pad(inLen); + p.x0 ^= pad(inLen); } finishData(State.EncFinal); } private void finishData(State nextState) { - x2 ^= K0; - x3 ^= K1; - p(12); - x3 ^= K0; - x4 ^= K1; + p.x2 ^= K0; + p.x3 ^= K1; + p.p(12); + p.x3 ^= K0; + p.x4 ^= K1; m_state = nextState; } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index a3ecd42dca..5fc67d40b0 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -1,7 +1,5 @@ package org.bouncycastle.crypto.engines; -import org.bouncycastle.util.Longs; - abstract class AsconBaseEngine extends AEADBufferBaseEngine { @@ -11,11 +9,7 @@ abstract class AsconBaseEngine protected long N0; protected long N1; protected long ASCON_IV; - protected long x0; - protected long x1; - protected long x2; - protected long x3; - protected long x4; + AsconPermutationFriend.AsconPermutation p; protected long dsep; //domain separation protected abstract long pad(int i); @@ -24,42 +18,6 @@ abstract class AsconBaseEngine protected abstract void setBytes(long n, byte[] bs, int off); - private void round(long C) - { - long t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); - long t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3)); - long t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4); - long t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4)); - long t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); - x0 = t0 ^ Longs.rotateRight(t0, 19) ^ Longs.rotateRight(t0, 28); - x1 = t1 ^ Longs.rotateRight(t1, 39) ^ Longs.rotateRight(t1, 61); - x2 = ~(t2 ^ Longs.rotateRight(t2, 1) ^ Longs.rotateRight(t2, 6)); - x3 = t3 ^ Longs.rotateRight(t3, 10) ^ Longs.rotateRight(t3, 17); - x4 = t4 ^ Longs.rotateRight(t4, 7) ^ Longs.rotateRight(t4, 41); - } - - protected void p(int nr) - { - if (nr == 12) - { - round(0xf0L); - round(0xe1L); - round(0xd2L); - round(0xc3L); - } - if (nr >= 8) - { - round(0xb4L); - round(0xa5L); - } - round(0x96L); - round(0x87L); - round(0x78L); - round(0x69L); - round(0x5aL); - round(0x4bL); - } - protected abstract void ascon_aeadinit(); protected void finishAAD(State nextState, boolean isDofinal) @@ -70,13 +28,13 @@ protected void finishAAD(State nextState, boolean isDofinal) case DecAad: case EncAad: processFinalAadBlock(); - p(nr); + p.p(nr); break; default: break; } // domain separation - x4 ^= dsep; + p.x4 ^= dsep; m_aadPos = 0; m_state = nextState; } @@ -90,18 +48,18 @@ protected void finishAAD(State nextState, boolean isDofinal) protected void processBufferAAD(byte[] buffer, int inOff) { - x0 ^= loadBytes(buffer, inOff); + p.x0 ^= loadBytes(buffer, inOff); if (BlockSize == 16) { - x1 ^= loadBytes(buffer, 8 + inOff); + p.x1 ^= loadBytes(buffer, 8 + inOff); } - p(nr); + p.p(nr); } protected void processFinalAAD() { processFinalAadBlock(); - p(nr); + p.p(nr); } @Override @@ -116,40 +74,41 @@ protected void processFinalBlock(byte[] output, int outOff) processFinalDecrypt(m_buf, m_bufPos, output, outOff); } mac = new byte[MAC_SIZE]; - setBytes(x3, mac, 0); - setBytes(x4, mac, 8); + setBytes(p.x3, mac, 0); + setBytes(p.x4, mac, 8); } protected void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { long t0 = loadBytes(buffer, bufOff); - setBytes(x0 ^ t0, output, outOff); - x0 = t0; + setBytes(p.x0 ^ t0, output, outOff); + p.x0 = t0; if (BlockSize == 16) { long t1 = loadBytes(buffer, bufOff + 8); - setBytes(x1 ^ t1, output, outOff + 8); - x1 = t1; + setBytes(p.x1 ^ t1, output, outOff + 8); + p.x1 = t1; } - p(nr); + p.p(nr); } protected void processBufferEncrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { - x0 ^= loadBytes(buffer, bufOff); - setBytes(x0, output, outOff); + p.x0 ^= loadBytes(buffer, bufOff); + setBytes(p.x0, output, outOff); if (BlockSize == 16) { - x1 ^= loadBytes(buffer, bufOff + 8); - setBytes(x1, output, outOff + 8); + p.x1 ^= loadBytes(buffer, bufOff + 8); + setBytes(p.x1, output, outOff + 8); } - p(nr); + p.p(nr); } protected void reset(boolean clearMac) { + p = new AsconPermutationFriend.AsconPermutation(); bufferReset(); ascon_aeadinit(); super.reset(clearMac); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java index 5ad2975040..fce2a90d48 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java @@ -90,22 +90,22 @@ protected void setBytes(long n, byte[] bs, int off) protected void ascon_aeadinit() { /* initialize */ - x0 = ASCON_IV; + p.x0 = ASCON_IV; if (KEY_SIZE == 20) { - x0 ^= K0; + p.x0 ^= K0; } - x1 = K1; - x2 = K2; - x3 = N0; - x4 = N1; - p(12); + p.x1 = K1; + p.x2 = K2; + p.x3 = N0; + p.x4 = N1; + p.p(12); if (KEY_SIZE == 20) { - x2 ^= K0; + p.x2 ^= K0; } - x3 ^= K1; - x4 ^= K2; + p.x3 ^= K1; + p.x4 ^= K2; } protected void processFinalAadBlock() @@ -113,12 +113,12 @@ protected void processFinalAadBlock() m_aad[m_aadPos] = (byte)0x80; if (m_aadPos >= 8) // ASCON_AEAD_RATE == 16 is implied { - x0 ^= Pack.bigEndianToLong(m_aad, 0); - x1 ^= Pack.bigEndianToLong(m_aad, 8) & (-1L << (56 - ((m_aadPos - 8) << 3))); + p.x0 ^= Pack.bigEndianToLong(m_aad, 0); + p.x1 ^= Pack.bigEndianToLong(m_aad, 8) & (-1L << (56 - ((m_aadPos - 8) << 3))); } else { - x0 ^= Pack.bigEndianToLong(m_aad, 0) & (-1L << (56 - (m_aadPos << 3))); + p.x0 ^= Pack.bigEndianToLong(m_aad, 0) & (-1L << (56 - (m_aadPos << 3))); } } @@ -127,32 +127,32 @@ protected void processFinalDecrypt(byte[] input, int inLen, byte[] output, int o if (inLen >= 8) // ASCON_AEAD_RATE == 16 is implied { long c0 = Pack.bigEndianToLong(input, 0); - x0 ^= c0; - Pack.longToBigEndian(x0, output, outOff); - x0 = c0; + p.x0 ^= c0; + Pack.longToBigEndian(p.x0, output, outOff); + p.x0 = c0; outOff += 8; inLen -= 8; - x1 ^= pad(inLen); + p.x1 ^= pad(inLen); if (inLen != 0) { long c1 = Pack.littleEndianToLong_High(input, 8, inLen); - x1 ^= c1; - Pack.longToLittleEndian_High(x1, output, outOff, inLen); - x1 &= -1L >>> (inLen << 3); - x1 ^= c1; + p.x1 ^= c1; + Pack.longToLittleEndian_High(p.x1, output, outOff, inLen); + p.x1 &= -1L >>> (inLen << 3); + p.x1 ^= c1; } } else { - x0 ^= pad(inLen); + p.x0 ^= pad(inLen); if (inLen != 0) { long c0 = Pack.littleEndianToLong_High(input, 0, inLen); - x0 ^= c0; - Pack.longToLittleEndian_High(x0, output, outOff, inLen); - x0 &= -1L >>> (inLen << 3); - x0 ^= c0; + p.x0 ^= c0; + Pack.longToLittleEndian_High(p.x0, output, outOff, inLen); + p.x0 &= -1L >>> (inLen << 3); + p.x0 ^= c0; } } @@ -163,24 +163,24 @@ protected void processFinalEncrypt(byte[] input, int inLen, byte[] output, int o { if (inLen >= 8) // ASCON_AEAD_RATE == 16 is implied { - x0 ^= Pack.bigEndianToLong(input, 0); - Pack.longToBigEndian(x0, output, outOff); + p.x0 ^= Pack.bigEndianToLong(input, 0); + Pack.longToBigEndian(p.x0, output, outOff); outOff += 8; inLen -= 8; - x1 ^= pad(inLen); + p.x1 ^= pad(inLen); if (inLen != 0) { - x1 ^= Pack.littleEndianToLong_High(input, 8, inLen); - Pack.longToLittleEndian_High(x1, output, outOff, inLen); + p.x1 ^= Pack.littleEndianToLong_High(input, 8, inLen); + Pack.longToLittleEndian_High(p.x1, output, outOff, inLen); } } else { - x0 ^= pad(inLen); + p.x0 ^= pad(inLen); if (inLen != 0) { - x0 ^= Pack.littleEndianToLong_High(input, 0, inLen); - Pack.longToLittleEndian_High(x0, output, outOff, inLen); + p.x0 ^= Pack.littleEndianToLong_High(input, 0, inLen); + Pack.longToLittleEndian_High(p.x0, output, outOff, inLen); } } finishData(State.EncFinal); @@ -191,24 +191,24 @@ protected void finishData(State nextState) switch (asconParameters) { case ascon128: - x1 ^= K1; - x2 ^= K2; + p.x1 ^= K1; + p.x2 ^= K2; break; case ascon128a: - x2 ^= K1; - x3 ^= K2; + p.x2 ^= K1; + p.x3 ^= K2; break; case ascon80pq: - x1 ^= (K0 << 32 | K1 >> 32); - x2 ^= (K1 << 32 | K2 >> 32); - x3 ^= K2 << 32; + p.x1 ^= (K0 << 32 | K1 >> 32); + p.x2 ^= (K1 << 32 | K2 >> 32); + p.x3 ^= K2 << 32; break; default: throw new IllegalStateException(); } - p(12); - x3 ^= K1; - x4 ^= K2; + p.p(12); + p.x3 ^= K1; + p.x4 ^= K2; m_state = nextState; } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java new file mode 100644 index 0000000000..306a7751bb --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java @@ -0,0 +1,65 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.digests.ISAPDigest; +import org.bouncycastle.util.Longs; + +public class AsconPermutationFriend +{ + public static AsconPermutation getAsconPermutation(ISAPDigest.Friend friend) + { + if (null == friend) + { + throw new NullPointerException("This method is only for use by ISAPDigest or Ascon Digest"); + } + return new AsconPermutation(); + } + + public static class AsconPermutation + { + AsconPermutation() + { + } + + public long x0; + public long x1; + public long x2; + public long x3; + public long x4; + + public void round(long C) + { + long t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); + long t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3)); + long t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4); + long t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4)); + long t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); + x0 = t0 ^ Longs.rotateRight(t0, 19) ^ Longs.rotateRight(t0, 28); + x1 = t1 ^ Longs.rotateRight(t1, 39) ^ Longs.rotateRight(t1, 61); + x2 = ~(t2 ^ Longs.rotateRight(t2, 1) ^ Longs.rotateRight(t2, 6)); + x3 = t3 ^ Longs.rotateRight(t3, 10) ^ Longs.rotateRight(t3, 17); + x4 = t4 ^ Longs.rotateRight(t4, 7) ^ Longs.rotateRight(t4, 41); + } + + public void p(int nr) + { + if (nr == 12) + { + round(0xf0L); + round(0xe1L); + round(0xd2L); + round(0xc3L); + } + if (nr >= 8) + { + round(0xb4L); + round(0xa5L); + } + round(0x96L); + round(0x87L); + round(0x78L); + round(0x69L); + round(0x5aL); + round(0x4bL); + } + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index df56146ac5..4b85674cd1 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.engines; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Longs; import org.bouncycastle.util.Pack; /** @@ -85,12 +86,14 @@ private abstract class ISAPAEAD_A protected long ISAP_IV1_64; protected long ISAP_IV2_64; protected long ISAP_IV3_64; - protected long x0, x1, x2, x3, x4, t0, t1, t2, t3, t4, macx0, macx1, macx2, macx3, macx4; + AsconPermutationFriend.AsconPermutation p; + protected long t0, t1, t2, t3, t4, macx0, macx1, macx2, macx3, macx4; public ISAPAEAD_A() { ISAP_rH = 64; BlockSize = (ISAP_rH + 7) >> 3; + p = new AsconPermutationFriend.AsconPermutation(); } public void init() @@ -108,16 +111,16 @@ public void init() public void swapInternalState() { - t0 = x0; - t1 = x1; - t2 = x2; - t3 = x3; - t4 = x4; - x0 = macx0; - x1 = macx1; - x2 = macx2; - x3 = macx3; - x4 = macx4; + t0 = p.x0; + t1 = p.x1; + t2 = p.x2; + t3 = p.x3; + t4 = p.x4; + p.x0 = macx0; + p.x1 = macx1; + p.x2 = macx2; + p.x3 = macx3; + p.x4 = macx4; macx0 = t0; macx1 = t1; macx2 = t2; @@ -127,65 +130,65 @@ public void swapInternalState() public void absorbMacBlock(byte[] input, int inOff) { - x0 ^= Pack.bigEndianToLong(input, inOff); - P12(); + p.x0 ^= Pack.bigEndianToLong(input, inOff); + p.p(12); } public void absorbFinalAADBlock() { for (int i = 0; i < m_aadPos; ++i) { - x0 ^= (m_aad[i] & 0xFFL) << ((7 - i) << 3); + p.x0 ^= (m_aad[i] & 0xFFL) << ((7 - i) << 3); } - x0 ^= 0x80L << ((7 - m_aadPos) << 3); - P12(); - x4 ^= 1L; + p.x0 ^= 0x80L << ((7 - m_aadPos) << 3); + p.p(12); + p.x4 ^= 1L; } public void processMACFinal(byte[] input, int inOff, int len, byte[] tag) { for (int i = 0; i < len; ++i) { - x0 ^= (input[inOff++] & 0xFFL) << ((7 - i) << 3); + p.x0 ^= (input[inOff++] & 0xFFL) << ((7 - i) << 3); } - x0 ^= 0x80L << ((7 - len) << 3); - P12(); + p.x0 ^= 0x80L << ((7 - len) << 3); + p.p(12); // Derive K* - Pack.longToBigEndian(x0, tag, 0); - Pack.longToBigEndian(x1, tag, 8); - long tmp_x2 = x2, tmp_x3 = x3, tmp_x4 = x4; + Pack.longToBigEndian(p.x0, tag, 0); + Pack.longToBigEndian(p.x1, tag, 8); + long tmp_x2 = p.x2, tmp_x3 = p.x3, tmp_x4 = p.x4; isap_rk(ISAP_IV2_64, tag, KEY_SIZE); - x2 = tmp_x2; - x3 = tmp_x3; - x4 = tmp_x4; + p.x2 = tmp_x2; + p.x3 = tmp_x3; + p.x4 = tmp_x4; // Squeeze tag - P12(); - Pack.longToBigEndian(x0, tag, 0); - Pack.longToBigEndian(x1, tag, 8); + p.p(12); + Pack.longToBigEndian(p.x0, tag, 0); + Pack.longToBigEndian(p.x1, tag, 8); } public void isap_rk(long iv64, byte[] y, int ylen) { // Init state - x0 = k64[0]; - x1 = k64[1]; - x2 = iv64; - x3 = x4 = 0; - P12(); + p.x0 = k64[0]; + p.x1 = k64[1]; + p.x2 = iv64; + p.x3 = p.x4 = 0; + p.p(12); // Absorb Y for (int i = 0; i < (ylen << 3) - 1; i++) { - x0 ^= ((((y[i >>> 3] >>> (7 - (i & 7))) & 0x01) << 7) & 0xFFL) << 56; + p.x0 ^= ((((y[i >>> 3] >>> (7 - (i & 7))) & 0x01) << 7) & 0xFFL) << 56; PX2(); } - x0 ^= (((y[ylen - 1]) & 0x01L) << 7) << 56; - P12(); + p.x0 ^= (((y[ylen - 1]) & 0x01L) << 7) << 56; + p.p(12); } public void processEncBlock(byte[] input, int inOff, byte[] output, int outOff) { long m64 = Pack.littleEndianToLong(input, inOff); - long c64 = U64BIG(x0) ^ m64; + long c64 = U64BIG(p.x0) ^ m64; PX1(); Pack.longToLittleEndian(c64, output, outOff); } @@ -193,7 +196,7 @@ public void processEncBlock(byte[] input, int inOff, byte[] output, int outOff) public void processEncFinalBlock(byte[] output, int outOff) { /* Encrypt final m block */ - byte[] xo = Pack.longToLittleEndian(x0); + byte[] xo = Pack.longToLittleEndian(p.x0); int mlen = m_bufPos; while (mlen > 0) { @@ -205,67 +208,27 @@ public void reset() { // Init state isap_rk(ISAP_IV3_64, npub, IV_SIZE); - x3 = npub64[0]; - x4 = npub64[1]; + p.x3 = npub64[0]; + p.x4 = npub64[1]; PX1(); swapInternalState(); // Init State for mac - x0 = npub64[0]; - x1 = npub64[1]; - x2 = ISAP_IV1_64; - x3 = x4 = 0; - P12(); + p.x0 = npub64[0]; + p.x1 = npub64[1]; + p.x2 = ISAP_IV1_64; + p.x3 = p.x4 = 0; + p.p(12); } private int getLongSize(int x) { - return (x >>> 3) + ((x & 7) != 0 ? 1 : 0); - } - - private long ROTR(long x, long n) - { - return (x >>> n) | (x << (64 - n)); + return ((x + 7) >>> 3); } protected long U64BIG(long x) { - return ((ROTR(x, 8) & (0xFF000000FF000000L)) | (ROTR(x, 24) & (0x00FF000000FF0000L)) | - (ROTR(x, 40) & (0x0000FF000000FF00L)) | (ROTR(x, 56) & (0x000000FF000000FFL))); - } - - protected void ROUND(long C) - { - t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); - t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3)); - t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4); - t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4)); - t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); - x0 = t0 ^ ROTR(t0, 19) ^ ROTR(t0, 28); - x1 = t1 ^ ROTR(t1, 39) ^ ROTR(t1, 61); - x2 = ~(t2 ^ ROTR(t2, 1) ^ ROTR(t2, 6)); - x3 = t3 ^ ROTR(t3, 10) ^ ROTR(t3, 17); - x4 = t4 ^ ROTR(t4, 7) ^ ROTR(t4, 41); - } - - public void P12() - { - ROUND(0xf0); - ROUND(0xe1); - ROUND(0xd2); - ROUND(0xc3); - ROUND(0xb4); - ROUND(0xa5); - P6(); - } - - protected void P6() - { - ROUND(0x96); - ROUND(0x87); - ROUND(0x78); - ROUND(0x69); - ROUND(0x5a); - ROUND(0x4b); + return ((Longs.rotateRight(x, 8) & (0xFF000000FF000000L)) | (Longs.rotateRight(x, 24) & (0x00FF000000FF0000L)) | + (Longs.rotateRight(x, 40) & (0x0000FF000000FF00L)) | (Longs.rotateRight(x, 56) & (0x000000FF000000FFL))); } } @@ -281,12 +244,12 @@ public ISAPAEAD_A_128A() protected void PX1() { - P6(); + p.p(6); } protected void PX2() { - ROUND(0x4b); + p.round(0x4bL); } } @@ -302,12 +265,12 @@ public ISAPAEAD_A_128() protected void PX1() { - P12(); + p.p(12); } protected void PX2() { - P12(); + p.p(12); } } From 4bb29fd63757a41839dbfa3b3050f6d0898b5bbd Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 28 Jan 2025 13:54:06 +1030 Subject: [PATCH 060/890] move new byte array of mac to AsconBufferBaseEngine --- .../org/bouncycastle/crypto/digests/ISAPDigest.java | 10 +++------- .../crypto/engines/AEADBufferBaseEngine.java | 2 +- .../bouncycastle/crypto/engines/AsconAEAD128.java | 1 - .../bouncycastle/crypto/engines/AsconBaseEngine.java | 1 - .../bouncycastle/crypto/engines/ElephantEngine.java | 1 - .../bouncycastle/crypto/engines/GiftCofbEngine.java | 1 - .../org/bouncycastle/crypto/engines/ISAPEngine.java | 1 - .../crypto/engines/PhotonBeetleEngine.java | 1 - .../bouncycastle/crypto/engines/RomulusEngine.java | 12 ++++-------- .../bouncycastle/crypto/engines/SparkleEngine.java | 1 - .../bouncycastle/crypto/engines/XoodyakEngine.java | 1 - 11 files changed, 8 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java index 8fb0ca8ba5..c44d367c4c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.engines.AsconPermutationFriend; +import org.bouncycastle.util.Longs; import org.bouncycastle.util.Pack; /** @@ -43,15 +44,10 @@ public ISAPDigest() reset(); } - private long ROTR(long x, long n) - { - return (x >>> n) | (x << (64 - n)); - } - protected long U64BIG(long x) { - return ((ROTR(x, 8) & (0xFF000000FF000000L)) | (ROTR(x, 24) & (0x00FF000000FF0000L)) | - (ROTR(x, 40) & (0x0000FF000000FF00L)) | (ROTR(x, 56) & (0x000000FF000000FFL))); + return ((Longs.rotateRight(x, 8) & (0xFF000000FF000000L)) | (Longs.rotateRight(x, 24) & (0x00FF000000FF0000L)) | + (Longs.rotateRight(x, 40) & (0x0000FF000000FF00L)) | (Longs.rotateRight(x, 56) & (0x000000FF000000FFL))); } @Override diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 5d10c1f737..9137482922 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -663,6 +663,7 @@ public int doFinal(byte[] output, int outOff) { throw new OutputLengthException("output buffer too short"); } + mac = new byte[MAC_SIZE]; processFinalBlock(output, outOff); if (forEncryption) { @@ -767,7 +768,6 @@ protected boolean checkData(boolean isDoFinal) } } - //TODO: override this for aadFinished protected void finishAAD(State nextState, boolean isDoFinal) { // State indicates whether we ever received AAD diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java index e9fedb1195..331a7b0737 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java @@ -1,6 +1,5 @@ package org.bouncycastle.crypto.engines; -import org.bouncycastle.crypto.digests.ISAPDigest; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index 5fc67d40b0..8e89e84401 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -73,7 +73,6 @@ protected void processFinalBlock(byte[] output, int outOff) { processFinalDecrypt(m_buf, m_bufPos, output, outOff); } - mac = new byte[MAC_SIZE]; setBytes(p.x3, mac, 0); setBytes(p.x4, mac, 8); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index fcbeab04ad..8ba86fba2c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -389,7 +389,6 @@ protected void processFinalBlock(byte[] output, int outOff) int nblocks_ad = 1 + (IV_SIZE + adlen) / BlockSize; int nb_it = Math.max(nblocks_c + 1, nblocks_ad - 1); processBytes(m_buf, output, outOff, nb_it, nblocks_m, nblocks_c, mlen, nblocks_ad); - mac = new byte[MAC_SIZE]; Bytes.xorTo(BlockSize, expanded_key, tag_buffer); instance.permutation(tag_buffer); Bytes.xorTo(BlockSize, expanded_key, tag_buffer); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java index 94c93f29e1..368d43c0c0 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java @@ -288,7 +288,6 @@ protected void processFinalBlock(byte[] output, int outOff) /* T = E(X[m+a]) */ giftb128(input, k, Y); } - mac = new byte[BlockSize]; System.arraycopy(Y, 0, mac, 0, BlockSize); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index 4b85674cd1..b5ca8531a6 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -785,7 +785,6 @@ protected void processFinalBlock(byte[] output, int outOff) { processFinalAAD(); int len = m_bufPos; - mac = new byte[MAC_SIZE]; ISAPAEAD.processEncFinalBlock(output, outOff); ISAPAEAD.swapInternalState(); if (forEncryption) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index a1c01e66a6..756296ce92 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -176,7 +176,6 @@ protected void processFinalBlock(byte[] output, int outOff) state[STATE_INBYTES - 1] ^= 1 << LAST_THREE_BITS_OFFSET; } PhotonPermutation(state_2d, state); - mac = new byte[MAC_SIZE]; System.arraycopy(state, 0, mac, 0, MAC_SIZE); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index 3842ba1742..58131ec904 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -32,7 +32,7 @@ public enum RomulusParameters //12 13 14 15 // 8-bit Sbox - private final byte[] sbox_8 = + private static final byte[] sbox_8 = { (byte)0x65, (byte)0x4c, (byte)0x6a, (byte)0x42, (byte)0x4b, (byte)0x63, (byte)0x43, (byte)0x6b, (byte)0x55, (byte)0x75, (byte)0x5a, (byte)0x7a, (byte)0x53, (byte)0x73, (byte)0x5b, (byte)0x7b, (byte)0x35, (byte)0x8c, @@ -66,10 +66,10 @@ public enum RomulusParameters }; // Tweakey permutation - private final byte[] TWEAKEY_P = {9, 15, 8, 13, 10, 14, 12, 11, 0, 1, 2, 3, 4, 5, 6, 7}; + private static final byte[] TWEAKEY_P = {9, 15, 8, 13, 10, 14, 12, 11, 0, 1, 2, 3, 4, 5, 6, 7}; // round constants - private final byte[] RC = { + private static final byte[] RC = { 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3E, 0x3D, 0x3B, 0x37, 0x2F, 0x1E, 0x3C, 0x39, 0x33, 0x27, 0x0E, 0x1D, 0x3A, 0x35, 0x2B, 0x16, 0x2C, 0x18, 0x30, 0x21, 0x02, 0x05, 0x0B, 0x17, 0x2E, @@ -138,7 +138,6 @@ public void processFinalBlock(byte[] output, int outOff) int adlen = aadOperator.getLen(); int mlen = dataOperator.getLen() - (forEncryption ? 0 : MAC_SIZE); byte[] m = ((StreamDataOperator)dataOperator).getBytes(); - mac = new byte[MAC_SIZE]; int xlen, mOff = 0, mauth = 0; xlen = mlen; if ((adlen & 31) == 0 && adlen != 0) @@ -360,7 +359,6 @@ else if (m_bufPos != 0) lfsr_gf56(CNT); nonce_encryption(npub, CNT, s, k, m_bufPos == AD_BLK_LEN_HALF ? (byte)0x14 : (byte)0x15); } - mac = new byte[MAC_SIZE]; g8A(s, mac, 0); } @@ -540,7 +538,6 @@ else if (messegeLen > 0) System.arraycopy(g, 0, LR, 16, 16); Arrays.clear(CNT_Z); block_cipher(LR, k, LR, 16, CNT_Z, (byte)68); - mac = new byte[MAC_SIZE]; System.arraycopy(LR, 0, mac, 0, MAC_SIZE); } @@ -635,8 +632,7 @@ public void reset() } } - - private void skinny_128_384_plus_enc(byte[] input, byte[] userkey) + private static void skinny_128_384_plus_enc(byte[] input, byte[] userkey) { byte[][] state = new byte[4][4]; byte[][][] keyCells = new byte[3][4][4]; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java index f7f8a2d48e..8d8c754387 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java @@ -184,7 +184,6 @@ protected void processFinalBlock(byte[] output, int outOff) { state[RATE_WORDS + i] ^= k[i]; } - mac = new byte[MAC_SIZE]; Pack.intToLittleEndian(state, RATE_WORDS, TAG_WORDS, mac, 0); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index 0c3644d736..bdea0f27f4 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -138,7 +138,6 @@ protected void processFinalBlock(byte[] output, int outOff) { decrypt(m_buf, 0, m_bufPos, output, outOff); } - mac = new byte[MAC_SIZE]; Up(mac, MAC_SIZE, 0x40); } From 1dc34f14537449a58a8d4879d0a235c0532bfa52 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 28 Jan 2025 14:07:30 +1030 Subject: [PATCH 061/890] Refactor on RomulusDigest and RomulusEngine --- .../crypto/digests/RomulusDigest.java | 204 ++---------------- .../crypto/engines/RomulusEngine.java | 25 +-- 2 files changed, 27 insertions(+), 202 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java index 24d098b60f..5fb19b87cf 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto.digests; +import org.bouncycastle.crypto.engines.RomulusEngine; import org.bouncycastle.util.Arrays; /** @@ -11,6 +12,15 @@ public class RomulusDigest extends BufferBaseDigest { + public static class Friend + { + private static final Friend INSTANCE = new Friend(); + + private Friend() + { + } + } + byte[] h = new byte[16]; byte[] g = new byte[16]; /* @@ -22,49 +32,6 @@ public class RomulusDigest // 8 9 10 11 //12 13 14 15 - // 8-bit Sbox - private final byte[] sbox_8 = - { - (byte)0x65, (byte)0x4c, (byte)0x6a, (byte)0x42, (byte)0x4b, (byte)0x63, (byte)0x43, (byte)0x6b, (byte)0x55, - (byte)0x75, (byte)0x5a, (byte)0x7a, (byte)0x53, (byte)0x73, (byte)0x5b, (byte)0x7b, (byte)0x35, (byte)0x8c, - (byte)0x3a, (byte)0x81, (byte)0x89, (byte)0x33, (byte)0x80, (byte)0x3b, (byte)0x95, (byte)0x25, (byte)0x98, - (byte)0x2a, (byte)0x90, (byte)0x23, (byte)0x99, (byte)0x2b, (byte)0xe5, (byte)0xcc, (byte)0xe8, (byte)0xc1, - (byte)0xc9, (byte)0xe0, (byte)0xc0, (byte)0xe9, (byte)0xd5, (byte)0xf5, (byte)0xd8, (byte)0xf8, (byte)0xd0, - (byte)0xf0, (byte)0xd9, (byte)0xf9, (byte)0xa5, (byte)0x1c, (byte)0xa8, (byte)0x12, (byte)0x1b, (byte)0xa0, - (byte)0x13, (byte)0xa9, (byte)0x05, (byte)0xb5, (byte)0x0a, (byte)0xb8, (byte)0x03, (byte)0xb0, (byte)0x0b, - (byte)0xb9, (byte)0x32, (byte)0x88, (byte)0x3c, (byte)0x85, (byte)0x8d, (byte)0x34, (byte)0x84, (byte)0x3d, - (byte)0x91, (byte)0x22, (byte)0x9c, (byte)0x2c, (byte)0x94, (byte)0x24, (byte)0x9d, (byte)0x2d, (byte)0x62, - (byte)0x4a, (byte)0x6c, (byte)0x45, (byte)0x4d, (byte)0x64, (byte)0x44, (byte)0x6d, (byte)0x52, (byte)0x72, - (byte)0x5c, (byte)0x7c, (byte)0x54, (byte)0x74, (byte)0x5d, (byte)0x7d, (byte)0xa1, (byte)0x1a, (byte)0xac, - (byte)0x15, (byte)0x1d, (byte)0xa4, (byte)0x14, (byte)0xad, (byte)0x02, (byte)0xb1, (byte)0x0c, (byte)0xbc, - (byte)0x04, (byte)0xb4, (byte)0x0d, (byte)0xbd, (byte)0xe1, (byte)0xc8, (byte)0xec, (byte)0xc5, (byte)0xcd, - (byte)0xe4, (byte)0xc4, (byte)0xed, (byte)0xd1, (byte)0xf1, (byte)0xdc, (byte)0xfc, (byte)0xd4, (byte)0xf4, - (byte)0xdd, (byte)0xfd, (byte)0x36, (byte)0x8e, (byte)0x38, (byte)0x82, (byte)0x8b, (byte)0x30, (byte)0x83, - (byte)0x39, (byte)0x96, (byte)0x26, (byte)0x9a, (byte)0x28, (byte)0x93, (byte)0x20, (byte)0x9b, (byte)0x29, - (byte)0x66, (byte)0x4e, (byte)0x68, (byte)0x41, (byte)0x49, (byte)0x60, (byte)0x40, (byte)0x69, (byte)0x56, - (byte)0x76, (byte)0x58, (byte)0x78, (byte)0x50, (byte)0x70, (byte)0x59, (byte)0x79, (byte)0xa6, (byte)0x1e, - (byte)0xaa, (byte)0x11, (byte)0x19, (byte)0xa3, (byte)0x10, (byte)0xab, (byte)0x06, (byte)0xb6, (byte)0x08, - (byte)0xba, (byte)0x00, (byte)0xb3, (byte)0x09, (byte)0xbb, (byte)0xe6, (byte)0xce, (byte)0xea, (byte)0xc2, - (byte)0xcb, (byte)0xe3, (byte)0xc3, (byte)0xeb, (byte)0xd6, (byte)0xf6, (byte)0xda, (byte)0xfa, (byte)0xd3, - (byte)0xf3, (byte)0xdb, (byte)0xfb, (byte)0x31, (byte)0x8a, (byte)0x3e, (byte)0x86, (byte)0x8f, (byte)0x37, - (byte)0x87, (byte)0x3f, (byte)0x92, (byte)0x21, (byte)0x9e, (byte)0x2e, (byte)0x97, (byte)0x27, (byte)0x9f, - (byte)0x2f, (byte)0x61, (byte)0x48, (byte)0x6e, (byte)0x46, (byte)0x4f, (byte)0x67, (byte)0x47, (byte)0x6f, - (byte)0x51, (byte)0x71, (byte)0x5e, (byte)0x7e, (byte)0x57, (byte)0x77, (byte)0x5f, (byte)0x7f, (byte)0xa2, - (byte)0x18, (byte)0xae, (byte)0x16, (byte)0x1f, (byte)0xa7, (byte)0x17, (byte)0xaf, (byte)0x01, (byte)0xb2, - (byte)0x0e, (byte)0xbe, (byte)0x07, (byte)0xb7, (byte)0x0f, (byte)0xbf, (byte)0xe2, (byte)0xca, (byte)0xee, - (byte)0xc6, (byte)0xcf, (byte)0xe7, (byte)0xc7, (byte)0xef, (byte)0xd2, (byte)0xf2, (byte)0xde, (byte)0xfe, - (byte)0xd7, (byte)0xf7, (byte)0xdf, (byte)0xff - }; - // Tweakey permutation - private final byte[] TWEAKEY_P = {9, 15, 8, 13, 10, 14, 12, 11, 0, 1, 2, 3, 4, 5, 6, 7}; - - // round constants - private final byte[] RC = { - (byte)0x01, (byte)0x03, (byte)0x07, (byte)0x0F, (byte)0x1F, (byte)0x3E, (byte)0x3D, (byte)0x3B, (byte)0x37, (byte)0x2F, - (byte)0x1E, (byte)0x3C, (byte)0x39, (byte)0x33, (byte)0x27, (byte)0x0E, (byte)0x1D, (byte)0x3A, (byte)0x35, (byte)0x2B, - (byte)0x16, (byte)0x2C, (byte)0x18, (byte)0x30, (byte)0x21, (byte)0x02, (byte)0x05, (byte)0x0B, (byte)0x17, (byte)0x2E, - (byte)0x1C, (byte)0x38, (byte)0x31, (byte)0x23, (byte)0x06, (byte)0x0D, (byte)0x1B, (byte)0x36, (byte)0x2D, (byte)0x1A}; - public RomulusDigest() { super(ProcessingBufferType.Immediate, 32); @@ -72,162 +39,19 @@ public RomulusDigest() algorithmName = "Romulus Hash"; } - void skinny_128_384_plus_enc(byte[] input, byte[] userkey) - { - byte[][] state = new byte[4][4]; - byte[][][] keyCells = new byte[3][4][4]; - int i, j, q, r; - byte pos, tmp; - byte[][][] keyCells_tmp = new byte[3][4][4]; - for (i = 0; i < 4; ++i) - { - q = i << 2; - System.arraycopy(input, q, state[i], 0, 4); - System.arraycopy(userkey, q, keyCells[0][i], 0, 4); - System.arraycopy(userkey, q + 16, keyCells[1][i], 0, 4); - System.arraycopy(userkey, q + 32, keyCells[2][i], 0, 4); - } - for (int round = 0; round < 40; round++) - { - //SubCell8; - for (i = 0; i < 4; i++) - { - for (j = 0; j < 4; j++) - { - state[i][j] = sbox_8[state[i][j] & 0xFF]; - } - } - //AddConstants - state[0][0] ^= (RC[round] & 0xf); - state[1][0] ^= ((RC[round] >>> 4) & 0x3); - state[2][0] ^= 0x2; - //AddKey - // apply the subtweakey to the internal state - for (i = 0; i <= 1; i++) - { - for (j = 0; j < 4; j++) - { - state[i][j] ^= keyCells[0][i][j] ^ keyCells[1][i][j] ^ keyCells[2][i][j]; - } - } - for (i = 0; i < 4; i++) - { - for (j = 0; j < 4; j++) - { - //application of the TWEAKEY permutation - pos = TWEAKEY_P[j + (i << 2)]; - q = pos >>> 2; - r = pos & 3; - keyCells_tmp[0][i][j] = keyCells[0][q][r]; - keyCells_tmp[1][i][j] = keyCells[1][q][r]; - keyCells_tmp[2][i][j] = keyCells[2][q][r]; - } - } - // update the subtweakey states with the LFSRs - for (i = 0; i <= 1; i++) - { - for (j = 0; j < 4; j++) - { - //application of LFSRs for TK updates - keyCells[0][i][j] = keyCells_tmp[0][i][j]; - tmp = keyCells_tmp[1][i][j]; - keyCells[1][i][j] = (byte)(((tmp << 1) & 0xFE) ^ ((tmp >>> 7) & 0x01) ^ ((tmp >>> 5) & 0x01)); - tmp = keyCells_tmp[2][i][j]; - keyCells[2][i][j] = (byte)(((tmp >>> 1) & 0x7F) ^ ((tmp << 7) & 0x80) ^ ((tmp << 1) & 0x80)); - } - } - for (; i < 4; ++i) - { - for (j = 0; j < 4; j++) - { - keyCells[0][i][j] = keyCells_tmp[0][i][j]; - keyCells[1][i][j] = keyCells_tmp[1][i][j]; - keyCells[2][i][j] = keyCells_tmp[2][i][j]; - } - } - //ShiftRows(state); - tmp = state[1][3]; - state[1][3] = state[1][2]; - state[1][2] = state[1][1]; - state[1][1] = state[1][0]; - state[1][0] = tmp; - tmp = state[2][0]; - state[2][0] = state[2][2]; - state[2][2] = tmp; - tmp = state[2][1]; - state[2][1] = state[2][3]; - state[2][3] = tmp; - tmp = state[3][0]; - state[3][0] = state[3][1]; - state[3][1] = state[3][2]; - state[3][2] = state[3][3]; - state[3][3] = tmp; - //MixColumn(state); - for (j = 0; j < 4; j++) - { - state[1][j] ^= state[2][j]; - state[2][j] ^= state[0][j]; - state[3][j] ^= state[2][j]; - tmp = state[3][j]; - state[3][j] = state[2][j]; - state[2][j] = state[1][j]; - state[1][j] = state[0][j]; - state[0][j] = tmp; - } - } //The last subtweakey should not be added - for (i = 0; i < 16; i++) - { - input[i] = (byte)(state[i >>> 2][i & 0x3] & 0xFF); - } - } - - - // The hirose double-block length (DBL) compression function. - void hirose_128_128_256(byte[] h, byte[] g, byte[] m, int mOff) - { - byte[] key = new byte[48]; - byte[] hh = new byte[16]; - int i; - // assign the key for the hirose compresison function - System.arraycopy(g, 0, key, 0, 16); - System.arraycopy(h, 0, g, 0, 16); - System.arraycopy(h, 0, hh, 0, 16); - g[0] ^= 0x01; - System.arraycopy(m, mOff, key, 16, 32); - skinny_128_384_plus_enc(h, key); - skinny_128_384_plus_enc(g, key); - for (i = 0; i < 16; i++) - { - h[i] ^= hh[i]; - g[i] ^= hh[i]; - } - g[0] ^= 0x01; - } - - // Padding function: pads the byte length of the message mod 32 to the last incomplete block. -// For complete blocks it returns the same block. For an empty block it returns a 0^2n string. -// The function is called for full block messages to add a 0^2n block. This and the modulus are -// the only differences compared to the use in Romulus-N - void ipad_256(byte[] m, int inOff, byte[] mp, int len8) - { - System.arraycopy(m, inOff, mp, 0, len8); - Arrays.fill(mp, len8, 31, (byte)0); - mp[31] = (byte)(len8 & 0x1f); - } - @Override protected void processBytes(byte[] input, int inOff) { - hirose_128_128_256(h, g, input, inOff); + RomulusEngine.hirose_128_128_256(Friend.INSTANCE, h, g, input, inOff); } @Override protected void finish(byte[] output, int outOff) { - byte[] p = new byte[32]; - ipad_256(m_buf, 0, p, m_bufPos); + Arrays.fill(m_buf, m_bufPos, 31, (byte)0); + m_buf[31] = (byte)(m_bufPos & 0x1f); h[0] ^= 2; - hirose_128_128_256(h, g, p, 0); + RomulusEngine.hirose_128_128_256(Friend.INSTANCE, h, g, m_buf, 0); // Assign the output tag System.arraycopy(h, 0, output, outOff, 16); System.arraycopy(g, 0, output, 16 + outOff, 16); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index 58131ec904..68b973aa69 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto.engines; +import org.bouncycastle.crypto.digests.RomulusDigest; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Bytes; @@ -525,11 +526,14 @@ else if (messegeLen > 0) // Pad the nonce and counter System.arraycopy(npub, 0, m_aad, 0, 16); System.arraycopy(CNT, 0, m_aad, 16, 7); - ipad_256(m_aad, m_aad, 23); + Arrays.fill(m_aad, 23, 31, (byte)0); + m_aad[31] = (byte)(23 & 0x1f); } else { - ipad_256(CNT_Z, m_aad, 7); + System.arraycopy(CNT_Z, 0, m_aad, 0, 7); + Arrays.fill(m_aad, 7, 31, (byte)0); + m_aad[31] = (byte)(7 & 0x1f); } h[0] ^= 2; hirose_128_128_256(h, g, m_aad, 0); @@ -850,20 +854,17 @@ private void reset_lfsr_gf56(byte[] CNT) CNT[6] = 0x00; } - - // Padding function: pads the byte length of the message mod 32 to the last incomplete block. -// For complete blocks it returns the same block. For an empty block it returns a 0^2n string. -// The function is called for full block messages to add a 0^2n block. This and the modulus are -// the only differences compared to the use in Romulus-N - void ipad_256(byte[] m, byte[] mp, int len8) + public static void hirose_128_128_256(RomulusDigest.Friend friend, byte[] h, byte[] g, byte[] m, int mOff) { - System.arraycopy(m, 0, mp, 0, len8); - Arrays.fill(mp, len8, 31, (byte)0); - mp[31] = (byte)(len8 & 0x1f); + if (null == friend) + { + throw new NullPointerException("This method is only for use by RomulusDigest"); + } + hirose_128_128_256(h, g, m, mOff); } // The hirose double-block length (DBL) compression function. - void hirose_128_128_256(byte[] h, byte[] g, byte[] m, int mOff) + static void hirose_128_128_256(byte[] h, byte[] g, byte[] m, int mOff) { byte[] key = new byte[48]; byte[] hh = new byte[16]; From 1a0e7d07ba71f071d9b538db76681fa98f3ec71e Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 28 Jan 2025 15:35:59 +1030 Subject: [PATCH 062/890] Refactor on XoodyakEngine and XoodyakDigest, refactor on ISAPDigest --- .../crypto/digests/ISAPDigest.java | 7 +- .../crypto/digests/RomulusDigest.java | 4 +- .../crypto/digests/XoodyakDigest.java | 170 +++--------------- .../crypto/engines/ISAPEngine.java | 1 - .../crypto/engines/PhotonBeetleEngine.java | 1 - .../crypto/engines/XoodyakEngine.java | 67 ++++--- 6 files changed, 63 insertions(+), 187 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java index c44d367c4c..727ccd1027 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java @@ -62,22 +62,19 @@ protected void processBytes(byte[] input, int inOff) protected void finish(byte[] output, int outOff) { /* absorb final input block */ - int idx; p.x0 ^= 0x80L << ((7 - m_bufPos) << 3); while (m_bufPos > 0) { p.x0 ^= (m_buf[--m_bufPos] & 0xFFL) << ((7 - m_bufPos) << 3); } - p.p(12); // squeeze long[] out64 = new long[4]; - for (idx = 0; idx < 3; ++idx) + for (int i = 0; i < 4; ++i) { - out64[idx] = U64BIG(p.x0); p.p(12); + out64[i] = U64BIG(p.x0); } /* squeeze final output block */ - out64[idx] = U64BIG(p.x0); Pack.longToLittleEndian(out64, output, outOff); } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java index 5fb19b87cf..4d5a722198 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/RomulusDigest.java @@ -21,8 +21,8 @@ private Friend() } } - byte[] h = new byte[16]; - byte[] g = new byte[16]; + private final byte[] h = new byte[16]; + private final byte[] g = new byte[16]; /* * This file includes only the encryption function of SKINNY-128-384+ as required by Romulus-v1.3 */ diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java index 7f86dbcace..af8e8f9840 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java @@ -1,8 +1,7 @@ package org.bouncycastle.crypto.digests; +import org.bouncycastle.crypto.engines.XoodyakEngine; import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Integers; -import org.bouncycastle.util.Pack; /** * Xoodyak v1, https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/xoodyak-spec-final.pdf @@ -14,20 +13,21 @@ public class XoodyakDigest extends BufferBaseDigest { + public static class Friend + { + private static final Friend INSTANCE = new Friend(); + + private Friend() + { + } + } + private final byte[] state; private int phase; - private MODE mode; - private final int f_bPrime = 48; - private final int PhaseUp = 2; + private static final int mode = 1; // set as ModeHash + private static final int PhaseUp = 2; + private static final int TAGLEN = 16; private int Cd; - private final int[] RC = {0x00000058, 0x00000038, 0x000003C0, 0x000000D0, 0x00000120, 0x00000014, 0x00000060, - 0x0000002C, 0x00000380, 0x000000F0, 0x000001A0, 0x00000012}; - - enum MODE - { - ModeHash, - ModeKeyed - } public XoodyakDigest() { @@ -43,9 +43,9 @@ protected void processBytes(byte[] input, int inOff) { if (phase != PhaseUp) { - Up(null, 0, 0, 0); + phase = XoodyakEngine.up(Friend.INSTANCE, mode, state, null, 0, 0, 0); } - Down(input, inOff, BlockSize, Cd); + phase = XoodyakEngine.down(Friend.INSTANCE, mode, state, input, inOff, BlockSize, Cd); Cd = 0; } @@ -56,14 +56,13 @@ protected void finish(byte[] output, int outOff) { if (phase != PhaseUp) { - Up(null, 0, 0, 0); + phase = XoodyakEngine.up(Friend.INSTANCE, mode, state, null, 0, 0, 0); } - Down(m_buf, 0, m_bufPos, Cd); + phase = XoodyakEngine.down(Friend.INSTANCE, mode, state, m_buf, 0, m_bufPos, Cd); } - int TAGLEN = 16; - Up(output, outOff, TAGLEN, 0x40); - Down(null, 0, 0, 0); - Up(output, outOff + TAGLEN, TAGLEN, 0); + phase = XoodyakEngine.up(Friend.INSTANCE, mode, state, output, outOff, TAGLEN, 0x40); + phase = XoodyakEngine.down(Friend.INSTANCE, mode, state, null, 0, 0, 0); + phase = XoodyakEngine.up(Friend.INSTANCE, mode, state, output, outOff + TAGLEN, TAGLEN, 0); } @Override @@ -72,135 +71,6 @@ public void reset() super.reset(); Arrays.fill(state, (byte)0); phase = PhaseUp; - mode = MODE.ModeHash; Cd = 0x03; } - - private void Up(byte[] Yi, int YiOff, int YiLen, int Cu) - { - if (mode != MODE.ModeHash) - { - state[f_bPrime - 1] ^= Cu; - } - - int a0 = Pack.littleEndianToInt(state, 0); - int a1 = Pack.littleEndianToInt(state, 4); - int a2 = Pack.littleEndianToInt(state, 8); - int a3 = Pack.littleEndianToInt(state, 12); - int a4 = Pack.littleEndianToInt(state, 16); - int a5 = Pack.littleEndianToInt(state, 20); - int a6 = Pack.littleEndianToInt(state, 24); - int a7 = Pack.littleEndianToInt(state, 28); - int a8 = Pack.littleEndianToInt(state, 32); - int a9 = Pack.littleEndianToInt(state, 36); - int a10 = Pack.littleEndianToInt(state, 40); - int a11 = Pack.littleEndianToInt(state, 44); - - int MAXROUNDS = 12; - for (int i = 0; i < MAXROUNDS; ++i) - { - /* Theta: Column Parity Mixer */ - int p0 = a0 ^ a4 ^ a8; - int p1 = a1 ^ a5 ^ a9; - int p2 = a2 ^ a6 ^ a10; - int p3 = a3 ^ a7 ^ a11; - - int e0 = Integers.rotateLeft(p3, 5) ^ Integers.rotateLeft(p3, 14); - int e1 = Integers.rotateLeft(p0, 5) ^ Integers.rotateLeft(p0, 14); - int e2 = Integers.rotateLeft(p1, 5) ^ Integers.rotateLeft(p1, 14); - int e3 = Integers.rotateLeft(p2, 5) ^ Integers.rotateLeft(p2, 14); - - a0 ^= e0; - a4 ^= e0; - a8 ^= e0; - - a1 ^= e1; - a5 ^= e1; - a9 ^= e1; - - a2 ^= e2; - a6 ^= e2; - a10 ^= e2; - - a3 ^= e3; - a7 ^= e3; - a11 ^= e3; - - /* Rho-west: plane shift */ - int b0 = a0; - int b1 = a1; - int b2 = a2; - int b3 = a3; - - int b4 = a7; - int b5 = a4; - int b6 = a5; - int b7 = a6; - - int b8 = Integers.rotateLeft(a8, 11); - int b9 = Integers.rotateLeft(a9, 11); - int b10 = Integers.rotateLeft(a10, 11); - int b11 = Integers.rotateLeft(a11, 11); - - /* Iota: round ant */ - b0 ^= RC[i]; - - /* Chi: non linear layer */ - a0 = b0 ^ (~b4 & b8); - a1 = b1 ^ (~b5 & b9); - a2 = b2 ^ (~b6 & b10); - a3 = b3 ^ (~b7 & b11); - - a4 = b4 ^ (~b8 & b0); - a5 = b5 ^ (~b9 & b1); - a6 = b6 ^ (~b10 & b2); - a7 = b7 ^ (~b11 & b3); - - b8 ^= (~b0 & b4); - b9 ^= (~b1 & b5); - b10 ^= (~b2 & b6); - b11 ^= (~b3 & b7); - - /* Rho-east: plane shift */ - a4 = Integers.rotateLeft(a4, 1); - a5 = Integers.rotateLeft(a5, 1); - a6 = Integers.rotateLeft(a6, 1); - a7 = Integers.rotateLeft(a7, 1); - - a8 = Integers.rotateLeft(b10, 8); - a9 = Integers.rotateLeft(b11, 8); - a10 = Integers.rotateLeft(b8, 8); - a11 = Integers.rotateLeft(b9, 8); - } - - Pack.intToLittleEndian(a0, state, 0); - Pack.intToLittleEndian(a1, state, 4); - Pack.intToLittleEndian(a2, state, 8); - Pack.intToLittleEndian(a3, state, 12); - Pack.intToLittleEndian(a4, state, 16); - Pack.intToLittleEndian(a5, state, 20); - Pack.intToLittleEndian(a6, state, 24); - Pack.intToLittleEndian(a7, state, 28); - Pack.intToLittleEndian(a8, state, 32); - Pack.intToLittleEndian(a9, state, 36); - Pack.intToLittleEndian(a10, state, 40); - Pack.intToLittleEndian(a11, state, 44); - - phase = PhaseUp; - if (Yi != null) - { - System.arraycopy(state, 0, Yi, YiOff, YiLen); - } - } - - void Down(byte[] Xi, int XiOff, int XiLen, int Cd) - { - for (int i = 0; i < XiLen; i++) - { - state[i] ^= Xi[XiOff++]; - } - state[XiLen] ^= 0x01; - state[f_bPrime - 1] ^= (mode == MODE.ModeHash) ? (Cd & 0x01) : Cd; - phase = 1; - } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index b5ca8531a6..411dea851c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -739,7 +739,6 @@ protected void init(byte[] key, byte[] iv) { npub = iv; k = key; - m_buf = new byte[BlockSize + (forEncryption ? 0 : MAC_SIZE)]; ISAPAEAD.init(); m_state = forEncryption ? State.EncInit : State.DecInit; reset(); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 756296ce92..145240a460 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -88,7 +88,6 @@ protected void init(byte[] key, byte[] iv) N = iv; state = new byte[STATE_INBYTES]; state_2d = new byte[D][D]; - mac = new byte[MAC_SIZE]; m_state = forEncryption ? State.EncInit : State.DecInit; reset(false); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index bdea0f27f4..5dc2ec72fb 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto.engines; +import org.bouncycastle.crypto.digests.XoodyakDigest; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Bytes; import org.bouncycastle.util.Integers; @@ -17,22 +18,18 @@ public class XoodyakEngine { private byte[] state; private int phase; - private MODE mode; - private final int f_bPrime_1 = 47; + private int mode; + private static final int f_bPrime_1 = 47; private byte[] K; private byte[] iv; - private final int PhaseUp = 2; - private final int[] RC = {0x00000058, 0x00000038, 0x000003C0, 0x000000D0, 0x00000120, 0x00000014, 0x00000060, + private static final int PhaseUp = 2; + private static final int[] RC = {0x00000058, 0x00000038, 0x000003C0, 0x000000D0, 0x00000120, 0x00000014, 0x00000060, 0x0000002C, 0x00000380, 0x000000F0, 0x000001A0, 0x00000012}; private boolean encrypted; private byte aadcd; private boolean aadFinished; - - enum MODE - { - ModeHash, - ModeKeyed - } + private static final int ModeKeyed = 0; + private static final int ModeHash = 1; public XoodyakEngine() { @@ -52,7 +49,6 @@ public void init(byte[] key, byte[] iv) K = key; this.iv = iv; state = new byte[48]; - mac = new byte[MAC_SIZE]; m_state = forEncryption ? State.EncInit : State.DecInit; reset(); } @@ -94,11 +90,11 @@ private void encrypt(byte[] input, int inOff, int len, byte[] output, int outOff { splitLen = Math.min(len, BlockSize); /* use Rkout instead of Rsqueeze, this function is only called in keyed mode */ System.arraycopy(input, inOff, P, 0, splitLen); - Up(null, 0, Cu); /* Up without extract */ + phase = up(mode, state, null, 0, 0, Cu); /* Up without extract */ /* Extract from Up and Add */ Bytes.xor(splitLen, state, input, inOff, output, outOff); inOff += splitLen; - Down(P, 0, splitLen, 0x00); + phase = down(mode, state, P, 0, splitLen, 0x00); Cu = 0x00; outOff += splitLen; len -= splitLen; @@ -113,11 +109,11 @@ private void decrypt(byte[] input, int inOff, int len, byte[] output, int outOff while (len != 0 || !encrypted) { splitLen = Math.min(len, BlockSize); /* use Rkout instead of Rsqueeze, this function is only called in keyed mode */ - Up(null, 0, Cu); /* Up without extract */ + phase = up(mode, state, null, 0, 0, Cu); /* Up without extract */ /* Extract from Up and Add */ Bytes.xor(splitLen, state, input, inOff, output, outOff); inOff += splitLen; - Down(output, outOff, splitLen, 0x00); + phase = down(mode, state, output, outOff, splitLen, 0x00); Cu = 0x00; outOff += splitLen; len -= splitLen; @@ -138,7 +134,7 @@ protected void processFinalBlock(byte[] output, int outOff) { decrypt(m_buf, 0, m_bufPos, output, outOff); } - Up(mac, MAC_SIZE, 0x40); + phase = up(mode, state, mac, 0, MAC_SIZE, 0x40); } protected void reset(boolean clearMac) @@ -155,7 +151,7 @@ protected void reset(boolean clearMac) int KLen = K.length; int IDLen = iv.length; byte[] KID = new byte[AADBufferSize]; - mode = MODE.ModeKeyed; + mode = ModeKeyed; System.arraycopy(K, 0, KID, 0, KLen); System.arraycopy(iv, 0, KID, KLen, IDLen); KID[KLen + IDLen] = (byte)IDLen; @@ -169,10 +165,10 @@ private void AbsorbAny(byte[] X, int Xoff, int XLen, int Cd) { if (phase != PhaseUp) { - Up(null, 0, 0); + phase = up(mode, state, null, 0, 0, 0); } splitLen = Math.min(XLen, AADBufferSize); - Down(X, Xoff, splitLen, Cd); + phase = down(mode, state, X, Xoff, splitLen, Cd); Cd = 0; Xoff += splitLen; XLen -= splitLen; @@ -180,9 +176,18 @@ private void AbsorbAny(byte[] X, int Xoff, int XLen, int Cd) while (XLen != 0); } - private void Up(byte[] Yi, int YiLen, int Cu) + public static int up(XoodyakDigest.Friend friend, int mode, byte[] state, byte[] Yi, int YiOff, int YiLen, int Cu) + { + if (null == friend) + { + throw new NullPointerException("This method is only for use by XoodyakDigest"); + } + return up(mode, state, Yi, YiOff, YiLen, Cu); + } + + private static int up(int mode, byte[] state, byte[] Yi, int YiOff, int YiLen, int Cu) { - if (mode != MODE.ModeHash) + if (mode != ModeHash) { state[f_bPrime_1] ^= Cu; } @@ -289,21 +294,27 @@ private void Up(byte[] Yi, int YiLen, int Cu) Pack.intToLittleEndian(a10, state, 40); Pack.intToLittleEndian(a11, state, 44); - phase = PhaseUp; if (Yi != null) { - System.arraycopy(state, 0, Yi, 0, YiLen); + System.arraycopy(state, 0, Yi, YiOff, YiLen); } + return PhaseUp; } - void Down(byte[] Xi, int XiOff, int XiLen, int Cd) + public static int down(XoodyakDigest.Friend friend, int mode, byte[] state, byte[] Xi, int XiOff, int XiLen, int Cd) { - for (int i = 0; i < XiLen; i++) + if (null == friend) { - state[i] ^= Xi[XiOff++]; + throw new NullPointerException("This method is only for use by XoodyakDigest"); } + return down(mode, state, Xi, XiOff, XiLen, Cd); + } + + private static int down(int mode, byte[] state, byte[] Xi, int XiOff, int XiLen, int Cd) + { + Bytes.xorTo(XiLen, Xi, XiOff, state); state[XiLen] ^= 0x01; - state[f_bPrime_1] ^= (mode == MODE.ModeHash) ? (Cd & 0x01) : Cd; - phase = 1; + state[f_bPrime_1] ^= (mode == ModeHash) ? (Cd & 0x01) : Cd; + return 1; } } From a52c46ad9b850ed63e31d2798df71e06d2ab57ba Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 28 Jan 2025 15:59:57 +1030 Subject: [PATCH 063/890] Remove ISAPEngine.swapInternalState --- .../crypto/engines/ISAPEngine.java | 156 +++++++----------- 1 file changed, 56 insertions(+), 100 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index 411dea851c..ace367b58d 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.engines; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Bytes; import org.bouncycastle.util.Longs; import org.bouncycastle.util.Pack; @@ -69,8 +70,6 @@ private interface ISAP_AEAD void absorbFinalAADBlock(); - void swapInternalState(); - void processEncBlock(byte[] input, int inOff, byte[] output, int outOff); void processEncFinalBlock(byte[] output, int outOff); @@ -87,13 +86,14 @@ private abstract class ISAPAEAD_A protected long ISAP_IV2_64; protected long ISAP_IV3_64; AsconPermutationFriend.AsconPermutation p; - protected long t0, t1, t2, t3, t4, macx0, macx1, macx2, macx3, macx4; + AsconPermutationFriend.AsconPermutation mac; public ISAPAEAD_A() { ISAP_rH = 64; BlockSize = (ISAP_rH + 7) >> 3; p = new AsconPermutationFriend.AsconPermutation(); + mac = new AsconPermutationFriend.AsconPermutation(); } public void init() @@ -102,72 +102,52 @@ public void init() k64 = new long[getLongSize(k.length)]; Pack.bigEndianToLong(npub, 0, npub64); Pack.bigEndianToLong(k, 0, k64); - //reset(); } - protected abstract void PX1(); + protected abstract void PX1(AsconPermutationFriend.AsconPermutation p); - protected abstract void PX2(); - - public void swapInternalState() - { - t0 = p.x0; - t1 = p.x1; - t2 = p.x2; - t3 = p.x3; - t4 = p.x4; - p.x0 = macx0; - p.x1 = macx1; - p.x2 = macx2; - p.x3 = macx3; - p.x4 = macx4; - macx0 = t0; - macx1 = t1; - macx2 = t2; - macx3 = t3; - macx4 = t4; - } + protected abstract void PX2(AsconPermutationFriend.AsconPermutation p); public void absorbMacBlock(byte[] input, int inOff) { - p.x0 ^= Pack.bigEndianToLong(input, inOff); - p.p(12); + mac.x0 ^= Pack.bigEndianToLong(input, inOff); + mac.p(12); } public void absorbFinalAADBlock() { for (int i = 0; i < m_aadPos; ++i) { - p.x0 ^= (m_aad[i] & 0xFFL) << ((7 - i) << 3); + mac.x0 ^= (m_aad[i] & 0xFFL) << ((7 - i) << 3); } - p.x0 ^= 0x80L << ((7 - m_aadPos) << 3); - p.p(12); - p.x4 ^= 1L; + mac.x0 ^= 0x80L << ((7 - m_aadPos) << 3); + mac.p(12); + mac.x4 ^= 1L; } public void processMACFinal(byte[] input, int inOff, int len, byte[] tag) { for (int i = 0; i < len; ++i) { - p.x0 ^= (input[inOff++] & 0xFFL) << ((7 - i) << 3); + mac.x0 ^= (input[inOff++] & 0xFFL) << ((7 - i) << 3); } - p.x0 ^= 0x80L << ((7 - len) << 3); - p.p(12); + mac.x0 ^= 0x80L << ((7 - len) << 3); + mac.p(12); // Derive K* - Pack.longToBigEndian(p.x0, tag, 0); - Pack.longToBigEndian(p.x1, tag, 8); - long tmp_x2 = p.x2, tmp_x3 = p.x3, tmp_x4 = p.x4; - isap_rk(ISAP_IV2_64, tag, KEY_SIZE); - p.x2 = tmp_x2; - p.x3 = tmp_x3; - p.x4 = tmp_x4; + Pack.longToBigEndian(mac.x0, tag, 0); + Pack.longToBigEndian(mac.x1, tag, 8); + long tmp_x2 = mac.x2, tmp_x3 = mac.x3, tmp_x4 = mac.x4; + isap_rk(mac, ISAP_IV2_64, tag, KEY_SIZE); + mac.x2 = tmp_x2; + mac.x3 = tmp_x3; + mac.x4 = tmp_x4; // Squeeze tag - p.p(12); - Pack.longToBigEndian(p.x0, tag, 0); - Pack.longToBigEndian(p.x1, tag, 8); + mac.p(12); + Pack.longToBigEndian(mac.x0, tag, 0); + Pack.longToBigEndian(mac.x1, tag, 8); } - public void isap_rk(long iv64, byte[] y, int ylen) + private void isap_rk(AsconPermutationFriend.AsconPermutation p, long iv64, byte[] y, int ylen) { // Init state p.x0 = k64[0]; @@ -179,7 +159,7 @@ public void isap_rk(long iv64, byte[] y, int ylen) for (int i = 0; i < (ylen << 3) - 1; i++) { p.x0 ^= ((((y[i >>> 3] >>> (7 - (i & 7))) & 0x01) << 7) & 0xFFL) << 56; - PX2(); + PX2(p); } p.x0 ^= (((y[ylen - 1]) & 0x01L) << 7) << 56; p.p(12); @@ -189,7 +169,7 @@ public void processEncBlock(byte[] input, int inOff, byte[] output, int outOff) { long m64 = Pack.littleEndianToLong(input, inOff); long c64 = U64BIG(p.x0) ^ m64; - PX1(); + PX1(p); Pack.longToLittleEndian(c64, output, outOff); } @@ -198,26 +178,22 @@ public void processEncFinalBlock(byte[] output, int outOff) /* Encrypt final m block */ byte[] xo = Pack.longToLittleEndian(p.x0); int mlen = m_bufPos; - while (mlen > 0) - { - output[outOff + mlen - 1] = (byte)(xo[BlockSize - mlen] ^ m_buf[--mlen]); - } + Bytes.xor(mlen, xo, BlockSize - mlen, m_buf, 0, output, outOff); } public void reset() { // Init state - isap_rk(ISAP_IV3_64, npub, IV_SIZE); + isap_rk(p, ISAP_IV3_64, npub, IV_SIZE); p.x3 = npub64[0]; p.x4 = npub64[1]; - PX1(); - swapInternalState(); + PX1(p); // Init State for mac - p.x0 = npub64[0]; - p.x1 = npub64[1]; - p.x2 = ISAP_IV1_64; - p.x3 = p.x4 = 0; - p.p(12); + mac.x0 = npub64[0]; + mac.x1 = npub64[1]; + mac.x2 = ISAP_IV1_64; + mac.x3 = mac.x4 = 0; + mac.p(12); } private int getLongSize(int x) @@ -242,12 +218,12 @@ public ISAPAEAD_A_128A() ISAP_IV3_64 = 252271952373286412L; } - protected void PX1() + protected void PX1(AsconPermutationFriend.AsconPermutation p) { p.p(6); } - protected void PX2() + protected void PX2(AsconPermutationFriend.AsconPermutation p) { p.round(0x4bL); } @@ -263,12 +239,12 @@ public ISAPAEAD_A_128() ISAP_IV3_64 = 252271952374008844L; } - protected void PX1() + protected void PX1(AsconPermutationFriend.AsconPermutation p) { p.p(12); } - protected void PX2() + protected void PX2(AsconPermutationFriend.AsconPermutation p) { p.p(12); } @@ -315,24 +291,10 @@ public void reset() System.arraycopy(iv16, 0, SX, 17, 8); PermuteRoundsKX(SX, E, C); // Init state for mac - swapInternalState(); - Arrays.fill(SX, 12, 25, (short)0); - System.arraycopy(iv16, 0, SX, 0, 8); - System.arraycopy(ISAP_IV1_16, 0, SX, 8, 4); - PermuteRoundsHX(SX, E, C); - } - - public void swapInternalState() - { - short[] tmp = SX; - SX = macSX; - macSX = tmp; - tmp = E; - E = macE; - macE = tmp; - tmp = C; - C = macC; - macC = tmp; + Arrays.fill(macSX, 12, 25, (short)0); + System.arraycopy(iv16, 0, macSX, 0, 8); + System.arraycopy(ISAP_IV1_16, 0, macSX, 8, 4); + PermuteRoundsHX(macSX, macE, macC); } protected abstract void PermuteRoundsHX(short[] SX, short[] E, short[] C); @@ -343,21 +305,21 @@ public void swapInternalState() public void absorbMacBlock(byte[] input, int inOff) { - byteToShortXor(input, inOff, SX, BlockSize >> 1); - PermuteRoundsHX(SX, E, C); + byteToShortXor(input, inOff, macSX, BlockSize >> 1); + PermuteRoundsHX(macSX, macE, macC); } public void absorbFinalAADBlock() { for (int i = 0; i < m_aadPos; i++) { - SX[i >> 1] ^= (m_aad[i] & 0xFF) << ((i & 1) << 3); + macSX[i >> 1] ^= (m_aad[i] & 0xFF) << ((i & 1) << 3); } - SX[m_aadPos >> 1] ^= 0x80 << ((m_aadPos & 1) << 3); - PermuteRoundsHX(SX, E, C); + macSX[m_aadPos >> 1] ^= 0x80 << ((m_aadPos & 1) << 3); + PermuteRoundsHX(macSX, macE, macC); // Domain seperation - SX[24] ^= 0x0100; + macSX[24] ^= 0x0100; } public void isap_rk(short[] iv16, byte[] y, int ylen, short[] out16, int outlen, short[] C) @@ -385,17 +347,17 @@ public void processMACFinal(byte[] input, int inOff, int len, byte[] tag) // Absorb C final block for (int i = 0; i < len; i++) { - SX[i >> 1] ^= (input[inOff++] & 0xFF) << ((i & 1) << 3); + macSX[i >> 1] ^= (input[inOff++] & 0xFF) << ((i & 1) << 3); } - SX[len >> 1] ^= 0x80 << ((len & 1) << 3); - PermuteRoundsHX(SX, E, C); + macSX[len >> 1] ^= 0x80 << ((len & 1) << 3); + PermuteRoundsHX(macSX, macE, macC); // Derive K* - Pack.shortToLittleEndian(SX, 0, 8, tag, 0); - isap_rk(ISAP_IV2_16, tag, KEY_SIZE, SX, KEY_SIZE, C); + Pack.shortToLittleEndian(macSX, 0, 8, tag, 0); + isap_rk(ISAP_IV2_16, tag, KEY_SIZE, macSX, KEY_SIZE, macC); // Squeeze tag - PermuteRoundsHX(SX, E, C); - Pack.shortToLittleEndian(SX, 0, 8, tag, 0); + PermuteRoundsHX(macSX, macE, macC); + Pack.shortToLittleEndian(macSX, 0, 8, tag, 0); } public void processEncBlock(byte[] input, int inOff, byte[] output, int outOff) @@ -755,7 +717,6 @@ protected void processFinalAAD() if (!aadFinished) { ISAPAEAD.absorbFinalAADBlock(); - ISAPAEAD.swapInternalState(); m_aadPos = 0; aadFinished = true; } @@ -765,18 +726,14 @@ protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int { processFinalAAD(); ISAPAEAD.processEncBlock(input, inOff, output, outOff); - ISAPAEAD.swapInternalState(); ISAPAEAD.absorbMacBlock(output, outOff); - ISAPAEAD.swapInternalState(); } protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { processFinalAAD(); ISAPAEAD.processEncBlock(input, inOff, output, outOff); - ISAPAEAD.swapInternalState(); ISAPAEAD.absorbMacBlock(input, inOff); - ISAPAEAD.swapInternalState(); } @Override @@ -785,7 +742,6 @@ protected void processFinalBlock(byte[] output, int outOff) processFinalAAD(); int len = m_bufPos; ISAPAEAD.processEncFinalBlock(output, outOff); - ISAPAEAD.swapInternalState(); if (forEncryption) { ISAPAEAD.processMACFinal(output, outOff, len, mac); From c2b0ed7f7cc3f8e1a080e184a1ca2607a50a0abf Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 28 Jan 2025 16:31:57 +1030 Subject: [PATCH 064/890] Remove some unnecessary code --- .../bouncycastle/crypto/engines/AsconAEAD128.java | 13 +++---------- .../crypto/engines/AsconBaseEngine.java | 11 +---------- .../bouncycastle/crypto/engines/AsconEngine.java | 9 +++------ .../org/bouncycastle/crypto/engines/ISAPEngine.java | 11 ++++------- .../bouncycastle/crypto/engines/RomulusEngine.java | 11 +++++------ 5 files changed, 16 insertions(+), 39 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java index 331a7b0737..8c074c3714 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java @@ -21,16 +21,10 @@ public class AsconAEAD128 { public AsconAEAD128() { - KEY_SIZE = 16; - IV_SIZE = 16; - MAC_SIZE = 16; - AADBufferSize = BlockSize = 16; + KEY_SIZE = IV_SIZE = MAC_SIZE = AADBufferSize = BlockSize = 16; ASCON_IV = 0x00001000808c0001L; algorithmName = "Ascon-AEAD128"; nr = 8; - m_bufferSizeDecrypt = BlockSize + MAC_SIZE; - m_buf = new byte[m_bufferSizeDecrypt]; - m_aad = new byte[BlockSize]; dsep = -9223372036854775808L; //0x80L << 56 setInnerMembers(ProcessingBufferType.Immediate, AADOperatorType.Default, DataOperatorType.Default); } @@ -65,7 +59,7 @@ protected void ascon_aeadinit() p.x4 ^= K1; } - protected void processFinalAadBlock() + protected void processFinalAAD() { if (m_aadPos == BlockSize) { @@ -164,5 +158,4 @@ public String getAlgorithmVersion() { return "v1.3"; } -} - +} \ No newline at end of file diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index 8e89e84401..a7958276b2 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -27,7 +27,7 @@ protected void finishAAD(State nextState, boolean isDofinal) { case DecAad: case EncAad: - processFinalAadBlock(); + this.processFinalAAD(); p.p(nr); break; default: @@ -39,13 +39,10 @@ protected void finishAAD(State nextState, boolean isDofinal) m_state = nextState; } - protected abstract void processFinalAadBlock(); - protected abstract void processFinalDecrypt(byte[] input, int inLen, byte[] output, int outOff); protected abstract void processFinalEncrypt(byte[] input, int inLen, byte[] output, int outOff); - protected void processBufferAAD(byte[] buffer, int inOff) { p.x0 ^= loadBytes(buffer, inOff); @@ -56,12 +53,6 @@ protected void processBufferAAD(byte[] buffer, int inOff) p.p(nr); } - protected void processFinalAAD() - { - processFinalAadBlock(); - p.p(nr); - } - @Override protected void processFinalBlock(byte[] output, int outOff) { diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java index fce2a90d48..5b6b9db43c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java @@ -36,8 +36,7 @@ public enum AsconParameters public AsconEngine(AsconParameters asconParameters) { this.asconParameters = asconParameters; - IV_SIZE = 16; - MAC_SIZE = 16; + IV_SIZE = MAC_SIZE = 16; switch (asconParameters) { case ascon80pq: @@ -63,9 +62,7 @@ public AsconEngine(AsconParameters asconParameters) } nr = (BlockSize == 8) ? 6 : 8; m_bufferSizeDecrypt = BlockSize + MAC_SIZE; - m_buf = new byte[m_bufferSizeDecrypt]; AADBufferSize = BlockSize; - m_aad = new byte[BlockSize]; dsep = 1L; setInnerMembers(asconParameters == AsconParameters.ascon128a ? ProcessingBufferType.Immediate : ProcessingBufferType.ImmediateLargeMac, AADOperatorType.Default, DataOperatorType.Default); } @@ -108,7 +105,7 @@ protected void ascon_aeadinit() p.x4 ^= K2; } - protected void processFinalAadBlock() + protected void processFinalAAD() { m_aad[m_aadPos] = (byte)0x80; if (m_aadPos >= 8) // ASCON_AEAD_RATE == 16 is implied @@ -244,4 +241,4 @@ public String getAlgorithmVersion() { return "v1.2"; } -} +} \ No newline at end of file diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index ace367b58d..f7b0f5a9e6 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -26,9 +26,7 @@ public enum IsapType public ISAPEngine(IsapType isapType) { - KEY_SIZE = 16; - IV_SIZE = 16; - MAC_SIZE = 16; + KEY_SIZE = IV_SIZE = MAC_SIZE = 16; switch (isapType) { case ISAP_A_128A: @@ -53,7 +51,7 @@ public ISAPEngine(IsapType isapType) ProcessingBufferType.ImmediateLargeMac, AADOperatorType.Default, DataOperatorType.Default); } - final int ISAP_STATE_SZ = 40; + private static final int ISAP_STATE_SZ = 40; private byte[] k; private byte[] npub; private int ISAP_rH; @@ -740,15 +738,14 @@ protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int protected void processFinalBlock(byte[] output, int outOff) { processFinalAAD(); - int len = m_bufPos; ISAPAEAD.processEncFinalBlock(output, outOff); if (forEncryption) { - ISAPAEAD.processMACFinal(output, outOff, len, mac); + ISAPAEAD.processMACFinal(output, outOff, m_bufPos, mac); } else { - ISAPAEAD.processMACFinal(m_buf, 0, len, mac); + ISAPAEAD.processMACFinal(m_buf, 0, m_bufPos, mac); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index 68b973aa69..7c4d94a3a2 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -22,7 +22,7 @@ public enum RomulusParameters private byte[] k; private byte[] npub; - private final int AD_BLK_LEN_HALF = 16; + private static final int AD_BLK_LEN_HALF = 16; private Instance instance; private final byte[] CNT; @@ -120,11 +120,11 @@ private interface Instance private class RomulusM implements Instance { - byte[] mac_s = new byte[16]; - byte[] mac_CNT = new byte[7]; + private final byte[] mac_s = new byte[16]; + private final byte[] mac_CNT = new byte[7]; - byte[] s = new byte[16]; - byte[] CNT = new byte[7]; + private final byte[] s = new byte[16]; + private final byte[] CNT = new byte[7]; int offset; boolean twist = true; @@ -626,7 +626,6 @@ public void reset() Arrays.clear(h); Arrays.clear(g); Arrays.clear(LR); - Arrays.clear(CNT_Z); Arrays.clear(T); Arrays.clear(S); reset_lfsr_gf56(CNT); From 9dbad2d2137727314553bd12cb519d80c2b73b9b Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 28 Jan 2025 16:48:53 +1030 Subject: [PATCH 065/890] Add AsconPermutation.set --- .../crypto/digests/AsconBaseDigest.java | 8 +++++--- .../crypto/digests/AsconCXof128.java | 12 ++--------- .../crypto/digests/AsconDigest.java | 12 ++--------- .../crypto/digests/AsconHash256.java | 8 ++------ .../bouncycastle/crypto/digests/AsconXof.java | 20 ++++--------------- .../crypto/digests/AsconXof128.java | 6 +----- .../crypto/digests/ISAPDigest.java | 6 +----- .../crypto/engines/AsconAEAD128.java | 6 +----- .../crypto/engines/AsconBaseEngine.java | 3 +-- .../crypto/engines/AsconEngine.java | 6 +----- .../engines/AsconPermutationFriend.java | 9 +++++++++ .../crypto/engines/ISAPEngine.java | 10 ++-------- 12 files changed, 31 insertions(+), 75 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java index 545a18a59d..dec0240a21 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java @@ -9,7 +9,10 @@ abstract class AsconBaseDigest public static class Friend { private static final Friend INSTANCE = new Friend(); - private Friend() {} + + private Friend() + { + } } @@ -49,8 +52,7 @@ protected void finish(byte[] output, int outOff) protected void padAndAbsorb() { - p.x0 ^= loadBytes(m_buf, 0, m_bufPos); - p.x0 ^= pad(m_bufPos); + p.x0 ^= loadBytes(m_buf, 0, m_bufPos) ^ pad(m_bufPos); p.p(12); } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java index 283a6fc5ea..a3b544aff0 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java @@ -131,20 +131,12 @@ public void reset() super.reset(); m_squeezing = false; /* initialize */ - p.x0 = z0; - p.x1 = z1; - p.x2 = z2; - p.x3 = z3; - p.x4 = z4; + p.set(z0, z1, z2, z3, z4); } private void initState(byte[] z, int zOff, int zLen) { - p.x0 = 7445901275803737603L; - p.x1 = 4886737088792722364L; - p.x2 = -1616759365661982283L; - p.x3 = 3076320316797452470L; - p.x4 = -8124743304765850554L; + p.set(7445901275803737603L, 4886737088792722364L, -1616759365661982283L, 3076320316797452470L, -8124743304765850554L); long bitLength = ((long)zLen) << 3; Pack.longToLittleEndian(bitLength, m_buf, 0); p.p(12); diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java index 5cbf14a21f..43f4af185f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconDigest.java @@ -74,18 +74,10 @@ public void reset() switch (asconParameters) { case AsconHashA: - p.x0 = 92044056785660070L; - p.x1 = 8326807761760157607L; - p.x2 = 3371194088139667532L; - p.x3 = -2956994353054992515L; - p.x4 = -6828509670848688761L; + p.set(92044056785660070L, 8326807761760157607L, 3371194088139667532L, -2956994353054992515L, -6828509670848688761L); break; case AsconHash: - p.x0 = -1255492011513352131L; - p.x1 = -8380609354527731710L; - p.x2 = -5437372128236807582L; - p.x3 = 4834782570098516968L; - p.x4 = 3787428097924915520L; + p.set(-1255492011513352131L, -8380609354527731710L, -5437372128236807582L, 4834782570098516968L, 3787428097924915520L); break; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java index 90c09ee44b..4cb4fd648b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconHash256.java @@ -10,7 +10,7 @@ * NIST SP 800-232 (Initial Public Draft). * For reference source code and implementation details, please see: * Reference, highly optimized, masked C and - * ASM implementations of Ascon (NIST SP 800-232). + * ASM implementations of Ascon (NIST SP 800-232). *

*/ public class AsconHash256 @@ -52,10 +52,6 @@ public void reset() { super.reset(); /* initialize */ - p.x0 = -7269279749984954751L; - p.x1 = 5459383224871899602L; - p.x2 = -5880230600644446182L; - p.x3 = 4359436768738168243L; - p.x4 = 1899470422303676269L; + p.set(-7269279749984954751L, 5459383224871899602L, -5880230600644446182L, 4359436768738168243L, 1899470422303676269L); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java index 243ca86b52..46dabf8755 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java @@ -26,6 +26,7 @@ public enum AsconParameters public AsconXof(AsconXof.AsconParameters parameters) { + BlockSize = 8; this.asconParameters = parameters; switch (parameters) { @@ -42,6 +43,7 @@ public AsconXof(AsconXof.AsconParameters parameters) } reset(); } + private boolean m_squeezing = false; @Override @@ -109,12 +111,6 @@ public int doFinal(byte[] output, int outOff, int outLen) return rlt; } - @Override - public int getByteLength() - { - return 8; - } - @Override public void reset() { @@ -124,18 +120,10 @@ public void reset() switch (asconParameters) { case AsconXof: - p.x0 = -5368810569253202922L; - p.x1 = 3121280575360345120L; - p.x2 = 7395939140700676632L; - p.x3 = 6533890155656471820L; - p.x4 = 5710016986865767350L; + p.set(-5368810569253202922L, 3121280575360345120L, 7395939140700676632L, 6533890155656471820L, 5710016986865767350L); break; case AsconXofA: - p.x0 = 4940560291654768690L; - p.x1 = -3635129828240960206L; - p.x2 = -597534922722107095L; - p.x3 = 2623493988082852443L; - p.x4 = -6283826724160825537L; + p.set(4940560291654768690L, -3635129828240960206L, -597534922722107095L, 2623493988082852443L, -6283826724160825537L); break; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java index adad4b158b..ee02923425 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java @@ -97,11 +97,7 @@ public void reset() m_squeezing = false; super.reset(); /* initialize */ - p.x0 = -2701369817892108309L; - p.x1 = -3711838248891385495L; - p.x2 = -1778763697082575311L; - p.x3 = 1072114354614917324L; - p.x4 = -2282070310009238562L; + p.set(-2701369817892108309L, -3711838248891385495L, -1778763697082575311L, 1072114354614917324L, -2282070310009238562L); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java index 727ccd1027..c4ae35b67e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java @@ -83,10 +83,6 @@ public void reset() { super.reset(); /* init state */ - p.x0 = -1255492011513352131L; - p.x1 = -8380609354527731710L; - p.x2 = -5437372128236807582L; - p.x3 = 4834782570098516968L; - p.x4 = 3787428097924915520L; + p.set(-1255492011513352131L, -8380609354527731710L, -5437372128236807582L, 4834782570098516968L, 3787428097924915520L); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java index 8c074c3714..5be9764901 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java @@ -49,11 +49,7 @@ protected void setBytes(long n, byte[] bs, int off) protected void ascon_aeadinit() { /* initialize */ - p.x0 = ASCON_IV; - p.x1 = K0; - p.x2 = K1; - p.x3 = N0; - p.x4 = N1; + p.set(ASCON_IV, K0, K1, N0, N1); p.p(12); p.x3 ^= K0; p.x4 ^= K1; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index a7958276b2..4561bfcf68 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -9,7 +9,7 @@ abstract class AsconBaseEngine protected long N0; protected long N1; protected long ASCON_IV; - AsconPermutationFriend.AsconPermutation p; + AsconPermutationFriend.AsconPermutation p = new AsconPermutationFriend.AsconPermutation(); protected long dsep; //domain separation protected abstract long pad(int i); @@ -98,7 +98,6 @@ protected void processBufferEncrypt(byte[] buffer, int bufOff, byte[] output, in protected void reset(boolean clearMac) { - p = new AsconPermutationFriend.AsconPermutation(); bufferReset(); ascon_aeadinit(); super.reset(clearMac); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java index 5b6b9db43c..cf63c72f5c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java @@ -87,15 +87,11 @@ protected void setBytes(long n, byte[] bs, int off) protected void ascon_aeadinit() { /* initialize */ - p.x0 = ASCON_IV; + p.set(ASCON_IV, K1, K2, N0, N1); if (KEY_SIZE == 20) { p.x0 ^= K0; } - p.x1 = K1; - p.x2 = K2; - p.x3 = N0; - p.x4 = N1; p.p(12); if (KEY_SIZE == 20) { diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java index 306a7751bb..8edef36a22 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java @@ -61,5 +61,14 @@ public void p(int nr) round(0x5aL); round(0x4bL); } + + public void set(long x0, long x1, long x2, long x3, long x4) + { + this.x0 = x0; + this.x1 = x1; + this.x2 = x2; + this.x3 = x3; + this.x4 = x4; + } } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index f7b0f5a9e6..9f2c527820 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -148,10 +148,7 @@ public void processMACFinal(byte[] input, int inOff, int len, byte[] tag) private void isap_rk(AsconPermutationFriend.AsconPermutation p, long iv64, byte[] y, int ylen) { // Init state - p.x0 = k64[0]; - p.x1 = k64[1]; - p.x2 = iv64; - p.x3 = p.x4 = 0; + p.set(k64[0], k64[1], iv64, 0L, 0L); p.p(12); // Absorb Y for (int i = 0; i < (ylen << 3) - 1; i++) @@ -187,10 +184,7 @@ public void reset() p.x4 = npub64[1]; PX1(p); // Init State for mac - mac.x0 = npub64[0]; - mac.x1 = npub64[1]; - mac.x2 = ISAP_IV1_64; - mac.x3 = mac.x4 = 0; + mac.set(npub64[0], npub64[1], ISAP_IV1_64, 0L, 0L); mac.p(12); } From a0dd6f0e798e3efbdbe5303e424effc5204492f1 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 28 Jan 2025 17:52:11 +1030 Subject: [PATCH 066/890] Fix the bug in RomulusEngine.RomulusT.reset --- .../main/java/org/bouncycastle/crypto/engines/RomulusEngine.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index 7c4d94a3a2..f1a9e9e1fa 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -628,6 +628,7 @@ public void reset() Arrays.clear(LR); Arrays.clear(T); Arrays.clear(S); + Arrays.clear(CNT_Z); reset_lfsr_gf56(CNT); System.arraycopy(npub, 0, Z, 0, 16); block_cipher(Z, k, T, 0, CNT_Z, (byte)66); From 6869b091285838e370adeedf1ee0a00165a6f940 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 29 Jan 2025 09:59:10 +1030 Subject: [PATCH 067/890] Refactor ElephantEngine xorTo --- .../crypto/engines/ElephantEngine.java | 22 +++++++++++-------- .../crypto/engines/GiftCofbEngine.java | 7 +++--- .../crypto/engines/RomulusEngine.java | 4 +--- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index 8ba86fba2c..5c8c40bd37 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -343,11 +343,9 @@ private void computerCipherBlock(byte[] input, int inOff, int blockSize, byte[] { System.arraycopy(npub, 0, buffer, 0, IV_SIZE); Arrays.fill(buffer, IV_SIZE, BlockSize, (byte)0); - Bytes.xorTo(BlockSize, current_mask, buffer); - Bytes.xorTo(BlockSize, next_mask, buffer); + xorTo(BlockSize, current_mask, next_mask, buffer); instance.permutation(buffer); - Bytes.xorTo(BlockSize, current_mask, buffer); - Bytes.xorTo(BlockSize, next_mask, buffer); + xorTo(BlockSize, current_mask, next_mask, buffer); Bytes.xorTo(blockSize, input, inOff, buffer); System.arraycopy(buffer, 0, output, outOff, blockSize); @@ -372,11 +370,9 @@ private void absorbAAD() private void absorbCiphertext() { - Bytes.xorTo(BlockSize, previous_mask, buffer); - Bytes.xorTo(BlockSize, next_mask, buffer); + xorTo(BlockSize, previous_mask, next_mask, buffer); instance.permutation(buffer); - Bytes.xorTo(BlockSize, previous_mask, buffer); - Bytes.xorTo(BlockSize, next_mask, buffer); + xorTo(BlockSize, previous_mask, next_mask, buffer); Bytes.xorTo(BlockSize, buffer, tag_buffer); } @@ -601,7 +597,7 @@ private void processBytes(byte[] m, byte[] output, int outOff, int nb_it, int nb // If clen is divisible by BLOCK_SIZE, add an additional padding block if (block_offset == mlen) { - Arrays.fill(buffer, 0, BlockSize, (byte)0); + Arrays.fill(buffer, 1, BlockSize, (byte)0); buffer[0] = 0x01; } else @@ -637,4 +633,12 @@ private void processBytes(byte[] m, byte[] output, int outOff, int nb_it, int nb } nb_its = i; } + + public static void xorTo(int len, byte[] x, byte[] y, byte[] z) + { + for (int i = 0; i < len; ++i) + { + z[i] ^= x[i] ^ y[i]; + } + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java index 368d43c0c0..9f3ab1bc4a 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java @@ -17,7 +17,7 @@ public class GiftCofbEngine private byte[] input; private byte[] offset; /*Round constants*/ - private final byte[] GIFT_RC = { + private static final byte[] GIFT_RC = { (byte)0x01, (byte)0x03, (byte)0x07, (byte)0x0F, (byte)0x1F, (byte)0x3E, (byte)0x3D, (byte)0x3B, (byte)0x37, (byte)0x2F, (byte)0x1E, (byte)0x3C, (byte)0x39, (byte)0x33, (byte)0x27, (byte)0x0E, (byte)0x1D, (byte)0x3A, (byte)0x35, (byte)0x2B, (byte)0x16, (byte)0x2C, (byte)0x18, (byte)0x30, (byte)0x21, (byte)0x02, (byte)0x05, (byte)0x0B, (byte)0x17, (byte)0x2E, @@ -28,7 +28,7 @@ public GiftCofbEngine() { AADBufferSize = BlockSize = MAC_SIZE = IV_SIZE = KEY_SIZE = 16; algorithmName = "GIFT-COFB AEAD"; - setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Counter, DataOperatorType.Counter); + setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Default, DataOperatorType.Counter); } private int rowperm(int S, int B0_pos, int B1_pos, int B2_pos, int B3_pos) @@ -205,8 +205,7 @@ protected void processFinalAAD() /* full byte[]: offset = 3*offset */ /* partial byte[]: offset = 3^2*offset */ triple_half_block(offset, offset); - int aadLen = aadOperator.getLen(); - if (((aadLen & 15) != 0) || m_state == State.DecInit || m_state == State.EncInit) + if (((m_aadPos & 15) != 0) || m_state == State.DecInit || m_state == State.EncInit) { triple_half_block(offset, offset); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index f1a9e9e1fa..ebfae3b79e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -442,7 +442,6 @@ public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int out @Override public void reset() { - Arrays.clear(CNT); Arrays.clear(s); reset_lfsr_gf56(CNT); twist = true; @@ -622,7 +621,6 @@ public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int out @Override public void reset() { - Arrays.clear(Z); Arrays.clear(h); Arrays.clear(g); Arrays.clear(LR); @@ -630,7 +628,7 @@ public void reset() Arrays.clear(S); Arrays.clear(CNT_Z); reset_lfsr_gf56(CNT); - System.arraycopy(npub, 0, Z, 0, 16); + System.arraycopy(npub, 0, Z, 0, IV_SIZE); block_cipher(Z, k, T, 0, CNT_Z, (byte)66); reset_lfsr_gf56(CNT_Z); } From e7fef7de56e8e1bad6f54efe0d2903ca1764ae9d Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 29 Jan 2025 12:26:54 +1030 Subject: [PATCH 068/890] Remove aadFinished --- .../crypto/engines/GiftCofbEngine.java | 2 - .../crypto/engines/ISAPEngine.java | 42 +++++++++++----- .../crypto/engines/PhotonBeetleEngine.java | 48 ++++++++++++------- .../crypto/engines/XoodyakEngine.java | 33 +++++++++---- 4 files changed, 85 insertions(+), 40 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java index 9f3ab1bc4a..dda066d820 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java @@ -182,7 +182,6 @@ private void pho(byte[] Y, byte[] M, int mOff, byte[] X, byte[] C, int cOff, int private void phoprime(byte[] Y, byte[] C, int cOff, byte[] X, byte[] M, int mOff, int no_of_bytes) { Bytes.xor(no_of_bytes, Y, C, cOff, M, mOff); - //xor_block(M, mOff, Y, C, cOff, no_of_bytes); pho1(X, Y, M, mOff, no_of_bytes); } @@ -232,7 +231,6 @@ protected void finishAAD(State nextState, boolean isDoFinal) case DecAad: if (!isDoFinal && dataOperator.getLen() <= MAC_SIZE) { - //m_state = State.DecData; return; } case EncInit: diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index 9f2c527820..4ca415e365 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -27,36 +27,41 @@ public enum IsapType public ISAPEngine(IsapType isapType) { KEY_SIZE = IV_SIZE = MAC_SIZE = 16; + ProcessingBufferType bufferType; switch (isapType) { case ISAP_A_128A: ISAPAEAD = new ISAPAEAD_A_128A(); algorithmName = "ISAP-A-128A AEAD"; + bufferType = ProcessingBufferType.ImmediateLargeMac; break; case ISAP_K_128A: ISAPAEAD = new ISAPAEAD_K_128A(); algorithmName = "ISAP-K-128A AEAD"; + bufferType = ProcessingBufferType.Immediate; break; case ISAP_A_128: ISAPAEAD = new ISAPAEAD_A_128(); algorithmName = "ISAP-A-128 AEAD"; + bufferType = ProcessingBufferType.ImmediateLargeMac; break; case ISAP_K_128: ISAPAEAD = new ISAPAEAD_K_128(); algorithmName = "ISAP-K-128 AEAD"; + bufferType = ProcessingBufferType.Immediate; break; + default: + throw new IllegalArgumentException("Incorrect ISAP parameter"); } AADBufferSize = BlockSize; - setInnerMembers(isapType == IsapType.ISAP_K_128A || isapType == IsapType.ISAP_K_128 ? ProcessingBufferType.Immediate : - ProcessingBufferType.ImmediateLargeMac, AADOperatorType.Default, DataOperatorType.Default); + setInnerMembers(bufferType, AADOperatorType.Default, DataOperatorType.Counter); } private static final int ISAP_STATE_SZ = 40; private byte[] k; private byte[] npub; private int ISAP_rH; - private boolean aadFinished; - private ISAP_AEAD ISAPAEAD; + private final ISAP_AEAD ISAPAEAD; private interface ISAP_AEAD { @@ -706,24 +711,39 @@ protected void processBufferAAD(byte[] input, int inOff) protected void processFinalAAD() { - if (!aadFinished) + ISAPAEAD.absorbFinalAADBlock(); + } + + @Override + protected void finishAAD(State nextState, boolean isDoFinal) + { + // State indicates whether we ever received AAD + switch (m_state) { - ISAPAEAD.absorbFinalAADBlock(); - m_aadPos = 0; - aadFinished = true; + case DecInit: + case DecAad: + if (!isDoFinal && dataOperator.getLen() <= MAC_SIZE) + { + return; + } + case EncInit: + case EncAad: + processFinalAAD(); + break; } + + m_aadPos = 0; + m_state = nextState; } protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { - processFinalAAD(); ISAPAEAD.processEncBlock(input, inOff, output, outOff); ISAPAEAD.absorbMacBlock(output, outOff); } protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - processFinalAAD(); ISAPAEAD.processEncBlock(input, inOff, output, outOff); ISAPAEAD.absorbMacBlock(input, inOff); } @@ -731,7 +751,6 @@ protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int @Override protected void processFinalBlock(byte[] output, int outOff) { - processFinalAAD(); ISAPAEAD.processEncFinalBlock(output, outOff); if (forEncryption) { @@ -748,7 +767,6 @@ protected void reset(boolean clearMac) ensureInitialized(); bufferReset(); ISAPAEAD.reset(); - aadFinished = false; super.reset(clearMac); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 145240a460..10339a9f61 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -29,7 +29,6 @@ public enum PhotonBeetleParameters private final int STATE_INBYTES; private final int LAST_THREE_BITS_OFFSET; private static final int D = 8; - private boolean aadFinished; private static final byte[][] RC = { {1, 3, 7, 14, 13, 11, 6, 12, 9, 2, 5, 10}, {0, 2, 6, 15, 12, 10, 7, 13, 8, 3, 4, 11}, @@ -99,27 +98,45 @@ protected void processBufferAAD(byte[] input, int inOff) Bytes.xorTo(BlockSize, input, inOff, state); } + @Override + protected void finishAAD(State nextState, boolean isDoFinal) + { + // State indicates whether we ever received AAD + switch (m_state) + { + case DecInit: + case DecAad: + if (!isDoFinal && dataOperator.getLen() <= MAC_SIZE) + { + //m_state = State.DecData; + return; + } + case EncInit: + case EncAad: + processFinalAAD(); + break; + } + + m_aadPos = 0; + m_state = nextState; + } + public void processFinalAAD() { - if (!aadFinished) + int aadLen = aadOperator.getLen(); + if (aadLen != 0) { - int aadLen = aadOperator.getLen(); - if (aadLen != 0) + if (m_aadPos != 0) { - if (m_aadPos != 0) + PhotonPermutation(state_2d, state); + Bytes.xorTo(m_aadPos, m_aad, state); + if (m_aadPos < BlockSize) { - PhotonPermutation(state_2d, state); - Bytes.xorTo(m_aadPos, m_aad, state); - if (m_aadPos < BlockSize) - { - state[m_aadPos] ^= 0x01; // ozs - } + state[m_aadPos] ^= 0x01; // ozs } - state[STATE_INBYTES - 1] ^= select(dataOperator.getLen() - (forEncryption ? 0 : MAC_SIZE) > 0, - ((aadLen % BlockSize) == 0), (byte)3, (byte)4) << LAST_THREE_BITS_OFFSET; } - m_aadPos = 0; - aadFinished = true; + state[STATE_INBYTES - 1] ^= select(dataOperator.getLen() - (forEncryption ? 0 : MAC_SIZE) > 0, + ((aadLen % BlockSize) == 0), (byte)3, (byte)4) << LAST_THREE_BITS_OFFSET; } } @@ -183,7 +200,6 @@ protected void reset(boolean clearMac) ensureInitialized(); bufferReset(); input_empty = true; - aadFinished = false; System.arraycopy(K, 0, state, 0, K.length); System.arraycopy(N, 0, state, K.length, N.length); super.reset(clearMac); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index 5dc2ec72fb..b9548ad533 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -27,7 +27,6 @@ public class XoodyakEngine 0x0000002C, 0x00000380, 0x000000F0, 0x000001A0, 0x00000012}; private boolean encrypted; private byte aadcd; - private boolean aadFinished; private static final int ModeKeyed = 0; private static final int ModeHash = 1; @@ -39,7 +38,7 @@ public XoodyakEngine() MAC_SIZE = 16; BlockSize = 24; AADBufferSize = 44; - setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Default, DataOperatorType.Default); + setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Default, DataOperatorType.Counter); } @Override @@ -61,23 +60,39 @@ protected void processBufferAAD(byte[] input, int inOff) protected void processFinalAAD() { - if (!aadFinished) + AbsorbAny(m_aad, 0, m_aadPos, aadcd); + m_aadPos = 0; + } + + @Override + protected void finishAAD(State nextState, boolean isDoFinal) + { + // State indicates whether we ever received AAD + switch (m_state) { - AbsorbAny(m_aad, 0, m_aadPos, aadcd); - aadFinished = true; - m_aadPos = 0; + case DecInit: + case DecAad: + if (!isDoFinal && dataOperator.getLen() <= MAC_SIZE) + { + return; + } + case EncInit: + case EncAad: + processFinalAAD(); + break; } + + m_aadPos = 0; + m_state = nextState; } protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { - processFinalAAD(); encrypt(input, inOff, BlockSize, output, outOff); } protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - processFinalAAD(); decrypt(input, inOff, BlockSize, output, outOff); } @@ -124,7 +139,6 @@ private void decrypt(byte[] input, int inOff, int len, byte[] output, int outOff @Override protected void processFinalBlock(byte[] output, int outOff) { - processFinalAAD(); if (forEncryption) { Arrays.fill(m_buf, m_bufPos, BlockSize, (byte)0); @@ -143,7 +157,6 @@ protected void reset(boolean clearMac) ensureInitialized(); super.reset(clearMac); Arrays.fill(state, (byte)0); - aadFinished = false; encrypted = false; phase = PhaseUp; aadcd = (byte)0x03; From 9f3d11cdb961b000f55ee0f24c8bd33769e125a4 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 29 Jan 2025 13:30:14 +1030 Subject: [PATCH 069/890] refactor around digests --- .../crypto/digests/AsconBaseDigest.java | 22 ++++++++--- .../crypto/digests/AsconCXof128.java | 20 ++-------- .../bouncycastle/crypto/digests/AsconXof.java | 10 +---- .../crypto/digests/AsconXof128.java | 10 +---- .../crypto/digests/BufferBaseDigest.java | 26 +++++++++---- .../crypto/digests/ISAPDigest.java | 13 +------ .../crypto/digests/PhotonBeetleDigest.java | 19 +++++---- .../crypto/digests/SparkleDigest.java | 19 ++++----- .../crypto/digests/XoodyakDigest.java | 21 ++++++---- .../crypto/engines/ElephantEngine.java | 2 - .../crypto/engines/XoodyakEngine.java | 39 +++++++++---------- 11 files changed, 96 insertions(+), 105 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java index dec0240a21..fe53fb5edc 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java @@ -15,7 +15,6 @@ private Friend() } } - AsconPermutationFriend.AsconPermutation p; protected int ASCON_PB_ROUNDS = 12; @@ -73,13 +72,26 @@ protected void squeeze(byte[] output, int outOff, int len) protected int hash(byte[] output, int outOff, int outLen) { - if (DigestSize + outOff > output.length) - { - throw new OutputLengthException("output buffer is too short"); - } + ensureSufficientOutputBuffer(output, outOff, outLen); padAndAbsorb(); /* squeeze full output blocks */ squeeze(output, outOff, outLen); return outLen; } + + protected void ensureSufficientOutputBuffer(byte[] output, int outOff, int len) + { + if (outOff + len > output.length) + { + throw new OutputLengthException("output buffer is too short"); + } + } + + protected void ensureNoAbsorbWhileSqueezing(boolean m_squeezing) + { + if (m_squeezing) + { + throw new IllegalArgumentException("attempt to absorb while squeezing"); + } + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java index a3b544aff0..3df7d1a538 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java @@ -36,10 +36,7 @@ public AsconCXof128(byte[] s) public AsconCXof128(byte[] s, int off, int len) { algorithmName = "Ascon-CXOF128"; - if ((off + len) > s.length) - { - throw new DataLengthException("input buffer too short"); - } + ensureSufficientInputBuffer(s, off, len); if (len > 256) { throw new DataLengthException("customized string is too long"); @@ -56,20 +53,14 @@ public AsconCXof128(byte[] s, int off, int len) @Override public void update(byte in) { - if (m_squeezing) - { - throw new IllegalArgumentException("attempt to absorb while squeezing"); - } + ensureNoAbsorbWhileSqueezing(m_squeezing); super.update(in); } @Override public void update(byte[] input, int inOff, int len) { - if (m_squeezing) - { - throw new IllegalArgumentException("attempt to absorb while squeezing"); - } + ensureNoAbsorbWhileSqueezing(m_squeezing); super.update(input, inOff, len); } @@ -107,10 +98,7 @@ protected void padAndAbsorb() @Override public int doOutput(byte[] output, int outOff, int outLen) { - if (DigestSize + outOff > output.length) - { - throw new OutputLengthException("output buffer is too short"); - } + ensureSufficientOutputBuffer(output, outOff, outLen); padAndAbsorb(); /* squeeze full output blocks */ squeeze(output, outOff, outLen); diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java index 46dabf8755..12c19b72b0 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java @@ -49,20 +49,14 @@ public AsconXof(AsconXof.AsconParameters parameters) @Override public void update(byte in) { - if (m_squeezing) - { - throw new IllegalArgumentException("attempt to absorb while squeezing"); - } + ensureNoAbsorbWhileSqueezing(m_squeezing); super.update(in); } @Override public void update(byte[] input, int inOff, int len) { - if (m_squeezing) - { - throw new IllegalArgumentException("attempt to absorb while squeezing"); - } + ensureNoAbsorbWhileSqueezing(m_squeezing); super.update(input, inOff, len); } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java index ee02923425..1a57225094 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java @@ -60,20 +60,14 @@ protected void padAndAbsorb() @Override public void update(byte in) { - if (m_squeezing) - { - throw new IllegalArgumentException("attempt to absorb while squeezing"); - } + ensureNoAbsorbWhileSqueezing(m_squeezing); super.update(in); } @Override public void update(byte[] input, int inOff, int len) { - if (m_squeezing) - { - throw new IllegalArgumentException("attempt to absorb while squeezing"); - } + ensureNoAbsorbWhileSqueezing(m_squeezing); super.update(input, inOff, len); } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java index ebec9dc9a1..d038985c4f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java @@ -124,10 +124,7 @@ public void update(byte in) @Override public void update(byte[] input, int inOff, int len) { - if ((inOff + len) > input.length) - { - throw new DataLengthException("input buffer too short"); - } + ensureSufficientInputBuffer(input, inOff, len); int available = BlockSize - m_bufPos; if (processor.isLengthWithinAvailableSpace(len, available)) { @@ -155,10 +152,7 @@ public void update(byte[] input, int inOff, int len) @Override public int doFinal(byte[] output, int outOff) { - if (DigestSize + outOff > output.length) - { - throw new OutputLengthException("output buffer is too short"); - } + ensureSufficientOutputBuffer(output, outOff); finish(output, outOff); reset(); return DigestSize; @@ -170,6 +164,22 @@ public void reset() m_bufPos = 0; } + protected void ensureSufficientInputBuffer(byte[] input, int inOff, int len) + { + if (inOff + len > input.length) + { + throw new DataLengthException("input buffer too short"); + } + } + + protected void ensureSufficientOutputBuffer(byte[] output, int outOff) + { + if (DigestSize + outOff > output.length) + { + throw new OutputLengthException("output buffer is too short"); + } + } + protected abstract void processBytes(byte[] input, int inOff); protected abstract void finish(byte[] output, int outOff); diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java index c4ae35b67e..853db0b309 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/ISAPDigest.java @@ -1,7 +1,6 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.engines.AsconPermutationFriend; -import org.bouncycastle.util.Longs; import org.bouncycastle.util.Pack; /** @@ -44,12 +43,6 @@ public ISAPDigest() reset(); } - protected long U64BIG(long x) - { - return ((Longs.rotateRight(x, 8) & (0xFF000000FF000000L)) | (Longs.rotateRight(x, 24) & (0x00FF000000FF0000L)) | - (Longs.rotateRight(x, 40) & (0x0000FF000000FF00L)) | (Longs.rotateRight(x, 56) & (0x000000FF000000FFL))); - } - @Override protected void processBytes(byte[] input, int inOff) { @@ -68,14 +61,12 @@ protected void finish(byte[] output, int outOff) p.x0 ^= (m_buf[--m_bufPos] & 0xFFL) << ((7 - m_bufPos) << 3); } // squeeze - long[] out64 = new long[4]; for (int i = 0; i < 4; ++i) { p.p(12); - out64[i] = U64BIG(p.x0); + Pack.longToBigEndian(p.x0, output, outOff); + outOff += 8; } - /* squeeze final output block */ - Pack.longToLittleEndian(out64, output, outOff); } @Override diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java index 651fa2b475..ac66c9339c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java @@ -25,16 +25,16 @@ private Friend() private final byte[] state; private final byte[][] state_2d; - private static final int STATE_INBYTES = 32; + private static final int SQUEEZE_RATE_INBYTES = 16; private static final int D = 8; private int blockCount; public PhotonBeetleDigest() { super(ProcessingBufferType.Buffered, 4); - state = new byte[STATE_INBYTES]; - state_2d = new byte[D][D]; DigestSize = 32; + state = new byte[DigestSize]; + state_2d = new byte[D][D]; algorithmName = "Photon-Beetle Hash"; blockCount = 0; } @@ -60,17 +60,17 @@ protected void finish(byte[] output, int outOff) int LAST_THREE_BITS_OFFSET = 5; if (m_bufPos == 0 && blockCount == 0) { - state[STATE_INBYTES - 1] ^= 1 << LAST_THREE_BITS_OFFSET; + state[DigestSize - 1] ^= 1 << LAST_THREE_BITS_OFFSET; } else if (blockCount < 4) { System.arraycopy(m_buf, 0, state, blockCount << 2, m_bufPos); state[(blockCount << 2) + m_bufPos] ^= 0x01; // ozs - state[STATE_INBYTES - 1] ^= (byte)1 << LAST_THREE_BITS_OFFSET; + state[DigestSize - 1] ^= (byte)1 << LAST_THREE_BITS_OFFSET; } else if (blockCount == 4 && m_bufPos == 0) { - state[STATE_INBYTES - 1] ^= (byte)2 << LAST_THREE_BITS_OFFSET; + state[DigestSize - 1] ^= (byte)2 << LAST_THREE_BITS_OFFSET; } else { @@ -80,13 +80,12 @@ else if (blockCount == 4 && m_bufPos == 0) { state[m_bufPos] ^= 0x01; // ozs } - state[STATE_INBYTES - 1] ^= (m_bufPos % BlockSize == 0 ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET; + state[DigestSize - 1] ^= (m_bufPos % BlockSize == 0 ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET; } PhotonBeetleEngine.PhotonPermutation(Friend.INSTANCE, state_2d, state); - int SQUEEZE_RATE_INBYTES = 16; System.arraycopy(state, 0, output, outOff, SQUEEZE_RATE_INBYTES); PhotonBeetleEngine.PhotonPermutation(Friend.INSTANCE, state_2d, state); - System.arraycopy(state, 0, output, outOff + SQUEEZE_RATE_INBYTES, DigestSize - SQUEEZE_RATE_INBYTES); + System.arraycopy(state, 0, output, outOff + SQUEEZE_RATE_INBYTES, SQUEEZE_RATE_INBYTES); } @Override @@ -96,4 +95,4 @@ public void reset() Arrays.fill(state, (byte)0); blockCount = 0; } -} +} \ No newline at end of file diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java index 8ae3d66048..9b36a32cce 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java @@ -16,7 +16,10 @@ public class SparkleDigest public static class Friend { private static final Friend INSTANCE = new Friend(); - private Friend() {} + + private Friend() + { + } } public enum SparkleParameters @@ -24,6 +27,7 @@ public enum SparkleParameters ESCH256, ESCH384 } + private static final int RATE_WORDS = 4; private final int[] state; private final int SPARKLE_STEPS_SLIM; @@ -71,10 +75,7 @@ protected void finish(byte[] output, int outOff) // padding m_buf[m_bufPos] = (byte)0x80; - while(++m_bufPos < BlockSize) - { - m_buf[m_bufPos] = 0x00; - } + Arrays.fill(m_buf, m_bufPos, BlockSize, (byte)0); } else { @@ -108,9 +109,9 @@ public void reset() private void processBlock(byte[] buf, int off, int steps) { - int t0 = Pack.littleEndianToInt(buf, off ); - int t1 = Pack.littleEndianToInt(buf, off + 4); - int t2 = Pack.littleEndianToInt(buf, off + 8); + int t0 = Pack.littleEndianToInt(buf, off); + int t1 = Pack.littleEndianToInt(buf, off + 4); + int t2 = Pack.littleEndianToInt(buf, off + 8); int t3 = Pack.littleEndianToInt(buf, off + 12); // addition of a buffer block to the state @@ -138,4 +139,4 @@ private static int ELL(int x) { return Integers.rotateRight(x, 16) ^ (x & 0xFFFF); } -} +} \ No newline at end of file diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java index af8e8f9840..ee099c4b9b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/XoodyakDigest.java @@ -26,6 +26,7 @@ private Friend() private int phase; private static final int mode = 1; // set as ModeHash private static final int PhaseUp = 2; + private static final int PhaseDown = 1; private static final int TAGLEN = 16; private int Cd; @@ -43,9 +44,10 @@ protected void processBytes(byte[] input, int inOff) { if (phase != PhaseUp) { - phase = XoodyakEngine.up(Friend.INSTANCE, mode, state, null, 0, 0, 0); + XoodyakEngine.up(Friend.INSTANCE, mode, state, 0); } - phase = XoodyakEngine.down(Friend.INSTANCE, mode, state, input, inOff, BlockSize, Cd); + XoodyakEngine.down(Friend.INSTANCE, mode, state, input, inOff, BlockSize, Cd); + phase = PhaseDown; Cd = 0; } @@ -56,13 +58,16 @@ protected void finish(byte[] output, int outOff) { if (phase != PhaseUp) { - phase = XoodyakEngine.up(Friend.INSTANCE, mode, state, null, 0, 0, 0); + XoodyakEngine.up(Friend.INSTANCE, mode, state, 0); } - phase = XoodyakEngine.down(Friend.INSTANCE, mode, state, m_buf, 0, m_bufPos, Cd); + XoodyakEngine.down(Friend.INSTANCE, mode, state, m_buf, 0, m_bufPos, Cd); } - phase = XoodyakEngine.up(Friend.INSTANCE, mode, state, output, outOff, TAGLEN, 0x40); - phase = XoodyakEngine.down(Friend.INSTANCE, mode, state, null, 0, 0, 0); - phase = XoodyakEngine.up(Friend.INSTANCE, mode, state, output, outOff + TAGLEN, TAGLEN, 0); + XoodyakEngine.up(Friend.INSTANCE, mode, state, 0x40); + System.arraycopy(state, 0, output, outOff, TAGLEN); + XoodyakEngine.down(Friend.INSTANCE, mode, state, null, 0, 0, 0); + XoodyakEngine.up(Friend.INSTANCE, mode, state, 0); + System.arraycopy(state, 0, output, outOff + TAGLEN, TAGLEN); + phase = PhaseDown; } @Override @@ -73,4 +78,4 @@ public void reset() phase = PhaseUp; Cd = 0x03; } -} +} \ No newline at end of file diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index 5c8c40bd37..248b4bd7f9 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -149,7 +149,6 @@ public void permutation(byte[] state) private class Dumbo extends Spongent { - public Dumbo() { super(160, 20, 80, (byte)0x75); @@ -166,7 +165,6 @@ public void lfsr_step() private class Jumbo extends Spongent { - public Jumbo() { super(176, 22, 90, (byte)0x45); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index b9548ad533..1e4c5abedd 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -23,6 +23,7 @@ public class XoodyakEngine private byte[] K; private byte[] iv; private static final int PhaseUp = 2; + private static final int PhaseDown = 1; private static final int[] RC = {0x00000058, 0x00000038, 0x000003C0, 0x000000D0, 0x00000120, 0x00000014, 0x00000060, 0x0000002C, 0x00000380, 0x000000F0, 0x000001A0, 0x00000012}; private boolean encrypted; @@ -105,11 +106,12 @@ private void encrypt(byte[] input, int inOff, int len, byte[] output, int outOff { splitLen = Math.min(len, BlockSize); /* use Rkout instead of Rsqueeze, this function is only called in keyed mode */ System.arraycopy(input, inOff, P, 0, splitLen); - phase = up(mode, state, null, 0, 0, Cu); /* Up without extract */ + up(mode, state, Cu); /* Up without extract */ /* Extract from Up and Add */ Bytes.xor(splitLen, state, input, inOff, output, outOff); inOff += splitLen; - phase = down(mode, state, P, 0, splitLen, 0x00); + down(mode, state, P, 0, splitLen, 0x00); + phase = PhaseDown; Cu = 0x00; outOff += splitLen; len -= splitLen; @@ -124,11 +126,12 @@ private void decrypt(byte[] input, int inOff, int len, byte[] output, int outOff while (len != 0 || !encrypted) { splitLen = Math.min(len, BlockSize); /* use Rkout instead of Rsqueeze, this function is only called in keyed mode */ - phase = up(mode, state, null, 0, 0, Cu); /* Up without extract */ + up(mode, state, Cu); /* Up without extract */ /* Extract from Up and Add */ Bytes.xor(splitLen, state, input, inOff, output, outOff); inOff += splitLen; - phase = down(mode, state, output, outOff, splitLen, 0x00); + down(mode, state, output, outOff, splitLen, 0x00); + phase = PhaseDown; Cu = 0x00; outOff += splitLen; len -= splitLen; @@ -148,7 +151,9 @@ protected void processFinalBlock(byte[] output, int outOff) { decrypt(m_buf, 0, m_bufPos, output, outOff); } - phase = up(mode, state, mac, 0, MAC_SIZE, 0x40); + up(mode, state, 0x40); + System.arraycopy(state, 0, mac, 0, MAC_SIZE); + phase = PhaseUp; } protected void reset(boolean clearMac) @@ -178,10 +183,11 @@ private void AbsorbAny(byte[] X, int Xoff, int XLen, int Cd) { if (phase != PhaseUp) { - phase = up(mode, state, null, 0, 0, 0); + up(mode, state, 0); } splitLen = Math.min(XLen, AADBufferSize); - phase = down(mode, state, X, Xoff, splitLen, Cd); + down(mode, state, X, Xoff, splitLen, Cd); + phase = PhaseDown; Cd = 0; Xoff += splitLen; XLen -= splitLen; @@ -189,16 +195,16 @@ private void AbsorbAny(byte[] X, int Xoff, int XLen, int Cd) while (XLen != 0); } - public static int up(XoodyakDigest.Friend friend, int mode, byte[] state, byte[] Yi, int YiOff, int YiLen, int Cu) + public static void up(XoodyakDigest.Friend friend, int mode, byte[] state, int Cu) { if (null == friend) { throw new NullPointerException("This method is only for use by XoodyakDigest"); } - return up(mode, state, Yi, YiOff, YiLen, Cu); + up(mode, state, Cu); } - private static int up(int mode, byte[] state, byte[] Yi, int YiOff, int YiLen, int Cu) + private static void up(int mode, byte[] state, int Cu) { if (mode != ModeHash) { @@ -306,28 +312,21 @@ private static int up(int mode, byte[] state, byte[] Yi, int YiOff, int YiLen, i Pack.intToLittleEndian(a9, state, 36); Pack.intToLittleEndian(a10, state, 40); Pack.intToLittleEndian(a11, state, 44); - - if (Yi != null) - { - System.arraycopy(state, 0, Yi, YiOff, YiLen); - } - return PhaseUp; } - public static int down(XoodyakDigest.Friend friend, int mode, byte[] state, byte[] Xi, int XiOff, int XiLen, int Cd) + public static void down(XoodyakDigest.Friend friend, int mode, byte[] state, byte[] Xi, int XiOff, int XiLen, int Cd) { if (null == friend) { throw new NullPointerException("This method is only for use by XoodyakDigest"); } - return down(mode, state, Xi, XiOff, XiLen, Cd); + down(mode, state, Xi, XiOff, XiLen, Cd); } - private static int down(int mode, byte[] state, byte[] Xi, int XiOff, int XiLen, int Cd) + private static void down(int mode, byte[] state, byte[] Xi, int XiOff, int XiLen, int Cd) { Bytes.xorTo(XiLen, Xi, XiOff, state); state[XiLen] ^= 0x01; state[f_bPrime_1] ^= (mode == ModeHash) ? (Cd & 0x01) : Cd; - return 1; } } From 5b2e973a4228e57e3ef8e97249cb63dbf15d2e21 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 29 Jan 2025 14:10:09 +1030 Subject: [PATCH 070/890] refactor around engines --- .../crypto/digests/PhotonBeetleDigest.java | 8 +++---- .../crypto/engines/AEADBufferBaseEngine.java | 19 +-------------- .../crypto/engines/AsconEngine.java | 1 - .../crypto/engines/ElephantEngine.java | 24 ++++++++++++++++--- .../crypto/engines/ISAPEngine.java | 4 +--- .../crypto/engines/PhotonBeetleEngine.java | 22 ++++++++--------- .../crypto/engines/RomulusEngine.java | 9 +------ .../crypto/engines/SparkleEngine.java | 22 ++++++++++++++--- .../crypto/engines/XoodyakEngine.java | 8 +++---- 9 files changed, 61 insertions(+), 56 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java index ac66c9339c..4e4b5e7bca 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java @@ -48,7 +48,7 @@ protected void processBytes(byte[] input, int inOff) } else { - PhotonBeetleEngine.PhotonPermutation(Friend.INSTANCE, state_2d, state); + PhotonBeetleEngine.photonPermutation(Friend.INSTANCE, state_2d, state); Bytes.xorTo(BlockSize, input, inOff, state); } blockCount++; @@ -74,7 +74,7 @@ else if (blockCount == 4 && m_bufPos == 0) } else { - PhotonBeetleEngine.PhotonPermutation(Friend.INSTANCE, state_2d, state); + PhotonBeetleEngine.photonPermutation(Friend.INSTANCE, state_2d, state); Bytes.xorTo(m_bufPos, m_buf, state); if (m_bufPos < BlockSize) { @@ -82,9 +82,9 @@ else if (blockCount == 4 && m_bufPos == 0) } state[DigestSize - 1] ^= (m_bufPos % BlockSize == 0 ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET; } - PhotonBeetleEngine.PhotonPermutation(Friend.INSTANCE, state_2d, state); + PhotonBeetleEngine.photonPermutation(Friend.INSTANCE, state_2d, state); System.arraycopy(state, 0, output, outOff, SQUEEZE_RATE_INBYTES); - PhotonBeetleEngine.PhotonPermutation(Friend.INSTANCE, state_2d, state); + PhotonBeetleEngine.photonPermutation(Friend.INSTANCE, state_2d, state); System.arraycopy(state, 0, output, outOff + SQUEEZE_RATE_INBYTES, SQUEEZE_RATE_INBYTES); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 9137482922..6a567ef9f3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -768,24 +768,7 @@ protected boolean checkData(boolean isDoFinal) } } - protected void finishAAD(State nextState, boolean isDoFinal) - { - // State indicates whether we ever received AAD - switch (m_state) - { - case DecAad: - case EncAad: - { - processFinalAAD(); - break; - } - default: - break; - } - - m_aadPos = 0; - m_state = nextState; - } + protected abstract void finishAAD(State nextState, boolean isDoFinal); protected void bufferReset() { diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java index cf63c72f5c..bbb807e507 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java @@ -61,7 +61,6 @@ public AsconEngine(AsconParameters asconParameters) throw new IllegalArgumentException("invalid parameter setting for ASCON AEAD"); } nr = (BlockSize == 8) ? 6 : 8; - m_bufferSizeDecrypt = BlockSize + MAC_SIZE; AADBufferSize = BlockSize; dsep = 1L; setInnerMembers(asconParameters == AsconParameters.ascon128a ? ProcessingBufferType.Immediate : ProcessingBufferType.ImmediateLargeMac, AADOperatorType.Default, DataOperatorType.Default); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index 248b4bd7f9..11d9185e74 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -312,7 +312,7 @@ private void processBuffer(byte[] input, int inOff, byte[] output, int outOff, S lfsr_step(); // Compute ciphertext block - computerCipherBlock(input, inOff, BlockSize, output, outOff); + computeCipherBlock(input, inOff, BlockSize, output, outOff); if (nb_its > 0) { @@ -337,7 +337,7 @@ protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int System.arraycopy(input, inOff, previous_outputMessage, 0, BlockSize); } - private void computerCipherBlock(byte[] input, int inOff, int blockSize, byte[] output, int outOff) + private void computeCipherBlock(byte[] input, int inOff, int blockSize, byte[] output, int outOff) { System.arraycopy(npub, 0, buffer, 0, IV_SIZE); Arrays.fill(buffer, IV_SIZE, BlockSize, (byte)0); @@ -440,6 +440,24 @@ public int getOutputSize(int len) return Math.max(0, len + m_bufPos - MAC_SIZE); } + protected void finishAAD(State nextState, boolean isDoFinal) + { + // State indicates whether we ever received AAD + switch (m_state) + { + case DecAad: + case EncAad: + { + processFinalAAD(); + break; + } + default: + break; + } + + m_aadPos = 0; + m_state = nextState; + } @Override protected void processFinalAAD() @@ -575,7 +593,7 @@ private void processBytes(byte[] m, byte[] output, int outOff, int nb_it, int nb if (i < nblocks_m) { // Compute ciphertext block - computerCipherBlock(m, rv, r_size, output, outOff); + computeCipherBlock(m, rv, r_size, output, outOff); if (forEncryption) { System.arraycopy(buffer, 0, outputMessage, 0, r_size); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index 4ca415e365..5a2f435c8c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -277,7 +277,6 @@ public void init() Pack.littleEndianToShort(k, 0, k16, 0, k16.length); iv16 = new short[npub.length >> 1]; Pack.littleEndianToShort(npub, 0, iv16, 0, iv16.length); - //reset(); } public void reset() @@ -369,8 +368,7 @@ public void processEncBlock(byte[] input, int inOff, byte[] output, int outOff) public void processEncFinalBlock(byte[] output, int outOff) { // Squeeze full or partial lane and stop - int len = m_bufPos; - for (int i = 0; i < len; ++i) + for (int i = 0; i < m_bufPos; ++i) { output[outOff++] = (byte)((SX[i >> 1] >>> ((i & 1) << 3)) ^ m_buf[i]); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 10339a9f61..6252628e07 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -54,9 +54,7 @@ public enum PhotonBeetleParameters public PhotonBeetleEngine(PhotonBeetleParameters pbp) { - KEY_SIZE = 16; - IV_SIZE = 16; - MAC_SIZE = 16; + KEY_SIZE = IV_SIZE = MAC_SIZE = 16; int CAPACITY_INBITS = 0, RATE_INBITS = 0; switch (pbp) { @@ -94,7 +92,7 @@ protected void init(byte[] key, byte[] iv) protected void processBufferAAD(byte[] input, int inOff) { - PhotonPermutation(state_2d, state); + photonPermutation(state_2d, state); Bytes.xorTo(BlockSize, input, inOff, state); } @@ -128,7 +126,7 @@ public void processFinalAAD() { if (m_aadPos != 0) { - PhotonPermutation(state_2d, state); + photonPermutation(state_2d, state); Bytes.xorTo(m_aadPos, m_aad, state); if (m_aadPos < BlockSize) { @@ -142,14 +140,14 @@ public void processFinalAAD() protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { - PhotonPermutation(state_2d, state); + photonPermutation(state_2d, state); rhoohr(output, outOff, input, inOff, BlockSize); Bytes.xorTo(BlockSize, input, inOff, state); } protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - PhotonPermutation(state_2d, state); + photonPermutation(state_2d, state); rhoohr(output, outOff, input, inOff, BlockSize); Bytes.xorTo(BlockSize, output, outOff, state); } @@ -170,7 +168,7 @@ protected void processFinalBlock(byte[] output, int outOff) { if (bufferLen != 0) { - PhotonPermutation(state_2d, state); + photonPermutation(state_2d, state); rhoohr(output, outOff, m_buf, 0, bufferLen); if (forEncryption) { @@ -191,7 +189,7 @@ protected void processFinalBlock(byte[] output, int outOff) { state[STATE_INBYTES - 1] ^= 1 << LAST_THREE_BITS_OFFSET; } - PhotonPermutation(state_2d, state); + photonPermutation(state_2d, state); System.arraycopy(state, 0, mac, 0, MAC_SIZE); } @@ -205,7 +203,7 @@ protected void reset(boolean clearMac) super.reset(clearMac); } - private static void PhotonPermutation(byte[][] state_2d, byte[] state) + private static void photonPermutation(byte[][] state_2d, byte[] state) { int i, j, k; int dq = 3; @@ -312,13 +310,13 @@ private void rhoohr(byte[] ciphertext, int outOff, byte[] plaintext, int inOff, } } - public static void PhotonPermutation(PhotonBeetleDigest.Friend friend, byte[][] state_2d, byte[] state) + public static void photonPermutation(PhotonBeetleDigest.Friend friend, byte[][] state_2d, byte[] state) { if (null == friend) { throw new NullPointerException("This method is only for use by PhotonBeetleDigest"); } - PhotonPermutation(state_2d, state); + photonPermutation(state_2d, state); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index ebfae3b79e..ec730279b9 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -122,9 +122,7 @@ private class RomulusM { private final byte[] mac_s = new byte[16]; private final byte[] mac_CNT = new byte[7]; - private final byte[] s = new byte[16]; - private final byte[] CNT = new byte[7]; int offset; boolean twist = true; @@ -844,12 +842,7 @@ void nonce_encryption(byte[] N, byte[] CNT, byte[] s, byte[] k, byte D) private void reset_lfsr_gf56(byte[] CNT) { CNT[0] = 0x01; - CNT[1] = 0x00; - CNT[2] = 0x00; - CNT[3] = 0x00; - CNT[4] = 0x00; - CNT[5] = 0x00; - CNT[6] = 0x00; + Arrays.fill(CNT, 1, 7, (byte) 0); } public static void hirose_128_128_256(RomulusDigest.Friend friend, byte[] h, byte[] g, byte[] m, int mOff) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java index 8d8c754387..7ddbe068ec 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java @@ -126,6 +126,25 @@ protected void init(byte[] key, byte[] iv) reset(); } + protected void finishAAD(State nextState, boolean isDoFinal) + { + // State indicates whether we ever received AAD + switch (m_state) + { + case DecAad: + case EncAad: + { + processFinalAAD(); + break; + } + default: + break; + } + + m_aadPos = 0; + m_state = nextState; + } + @Override protected void processFinalBlock(byte[] output, int outOff) { @@ -209,7 +228,6 @@ protected void processBufferAAD(byte[] buffer, int bufOff) protected void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { - for (int i = 0; i < RATE_WORDS / 2; ++i) { int j = i + (RATE_WORDS / 2); @@ -234,8 +252,6 @@ protected void processBufferDecrypt(byte[] buffer, int bufOff, byte[] output, in protected void processBufferEncrypt(byte[] buffer, int bufOff, byte[] output, int outOff) { -// assert bufOff <= buffer.length - RATE_BYTES; - for (int i = 0; i < RATE_WORDS / 2; ++i) { int j = i + (RATE_WORDS / 2); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index 1e4c5abedd..c8446a78fd 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -179,12 +179,12 @@ protected void reset(boolean clearMac) private void AbsorbAny(byte[] X, int Xoff, int XLen, int Cd) { int splitLen; + if (phase != PhaseUp) + { + up(mode, state, 0); + } do { - if (phase != PhaseUp) - { - up(mode, state, 0); - } splitLen = Math.min(XLen, AADBufferSize); down(mode, state, X, Xoff, splitLen, Cd); phase = PhaseDown; From 38a07e4b133bec837a30c10baf6997f0164fc1d1 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 29 Jan 2025 14:16:00 +1030 Subject: [PATCH 071/890] Fix the bug in SparkleDigest --- .../java/org/bouncycastle/crypto/digests/SparkleDigest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java index 9b36a32cce..7cabb2df28 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/SparkleDigest.java @@ -74,7 +74,7 @@ protected void finish(byte[] output, int outOff) state[(STATE_WORDS >> 1) - 1] ^= 1 << 24; // padding - m_buf[m_bufPos] = (byte)0x80; + m_buf[m_bufPos++] = (byte)0x80; Arrays.fill(m_buf, m_bufPos, BlockSize, (byte)0); } else From f12f73b98e8fb4b0e7ec22b230f66251a5f3dc72 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 29 Jan 2025 14:43:18 +1030 Subject: [PATCH 072/890] Refactor in XoodyakEngine --- .../crypto/engines/XoodyakEngine.java | 76 +++++++------------ 1 file changed, 26 insertions(+), 50 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index c8446a78fd..772a375411 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -39,7 +39,7 @@ public XoodyakEngine() MAC_SIZE = 16; BlockSize = 24; AADBufferSize = 44; - setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Default, DataOperatorType.Counter); + setInnerMembers(ProcessingBufferType.Immediate, AADOperatorType.Default, DataOperatorType.Counter); } @Override @@ -62,7 +62,6 @@ protected void processBufferAAD(byte[] input, int inOff) protected void processFinalAAD() { AbsorbAny(m_aad, 0, m_aadPos, aadcd); - m_aadPos = 0; } @Override @@ -89,67 +88,44 @@ protected void finishAAD(State nextState, boolean isDoFinal) protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { - encrypt(input, inOff, BlockSize, output, outOff); + int Cu = encrypted ? 0 : 0x80; + up(mode, state, Cu); /* Up without extract */ + /* Extract from Up and Add */ + Bytes.xor(BlockSize, state, input, inOff, output, outOff); + down(mode, state, input, inOff, BlockSize, 0x00); + phase = PhaseDown; + encrypted = true; } protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - decrypt(input, inOff, BlockSize, output, outOff); - } - - private void encrypt(byte[] input, int inOff, int len, byte[] output, int outOff) - { - int splitLen; - byte[] P = new byte[BlockSize]; int Cu = encrypted ? 0 : 0x80; - while (len != 0 || !encrypted) - { - splitLen = Math.min(len, BlockSize); /* use Rkout instead of Rsqueeze, this function is only called in keyed mode */ - System.arraycopy(input, inOff, P, 0, splitLen); - up(mode, state, Cu); /* Up without extract */ - /* Extract from Up and Add */ - Bytes.xor(splitLen, state, input, inOff, output, outOff); - inOff += splitLen; - down(mode, state, P, 0, splitLen, 0x00); - phase = PhaseDown; - Cu = 0x00; - outOff += splitLen; - len -= splitLen; - encrypted = true; - } + up(mode, state, Cu); /* Up without extract */ + /* Extract from Up and Add */ + Bytes.xor(BlockSize, state, input, inOff, output, outOff); + down(mode, state, output, outOff, BlockSize, 0x00); + phase = PhaseDown; + encrypted = true; } - private void decrypt(byte[] input, int inOff, int len, byte[] output, int outOff) + @Override + protected void processFinalBlock(byte[] output, int outOff) { - int splitLen; int Cu = encrypted ? 0 : 0x80; - while (len != 0 || !encrypted) + if (m_bufPos != 0 || !encrypted) { - splitLen = Math.min(len, BlockSize); /* use Rkout instead of Rsqueeze, this function is only called in keyed mode */ up(mode, state, Cu); /* Up without extract */ /* Extract from Up and Add */ - Bytes.xor(splitLen, state, input, inOff, output, outOff); - inOff += splitLen; - down(mode, state, output, outOff, splitLen, 0x00); + Bytes.xor(m_bufPos, state, m_buf, 0, output, outOff); + if (forEncryption) + { + down(mode, state, m_buf, 0, m_bufPos, 0x00); + } + else + { + down(mode, state, output, outOff, m_bufPos, 0x00); + } phase = PhaseDown; - Cu = 0x00; - outOff += splitLen; - len -= splitLen; - encrypted = true; - } - } - - @Override - protected void processFinalBlock(byte[] output, int outOff) - { - if (forEncryption) - { - Arrays.fill(m_buf, m_bufPos, BlockSize, (byte)0); - encrypt(m_buf, 0, m_bufPos, output, outOff); - } - else - { - decrypt(m_buf, 0, m_bufPos, output, outOff); } up(mode, state, 0x40); System.arraycopy(state, 0, mac, 0, MAC_SIZE); From 0713ee79770b5fefbfe075284b036f300df84baf Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 29 Jan 2025 16:22:54 +1030 Subject: [PATCH 073/890] Refactor in Engines --- .../crypto/engines/GiftCofbEngine.java | 83 ++++++++----------- .../crypto/engines/RomulusEngine.java | 6 +- .../crypto/engines/SparkleEngine.java | 8 +- .../crypto/engines/XoodyakEngine.java | 9 +- 4 files changed, 41 insertions(+), 65 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java index dda066d820..6bb4e0aa0b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java @@ -116,36 +116,32 @@ private void giftb128(byte[] P, byte[] K, byte[] C) C[15] = (byte)(S[3]); } - private void xor_topbar_block(byte[] d, byte[] s1, byte[] s2) + private void double_half_block(byte[] s) { - Bytes.xor(8, s1, s2, d); - System.arraycopy(s1, 8, d, 8, 8); + int mask = ((s[0] & 0xFF) >>> 7) * 27; + /*x^{64} + x^4 + x^3 + x + 1*/ + for (int i = 0; i < 7; i++) + { + s[i] = (byte)(((s[i] & 0xFF) << 1) | ((s[i + 1] & 0xFF) >>> 7)); + } + s[7] = (byte)(((s[7] & 0xFF) << 1) ^ mask); } - private void double_half_block(byte[] d, byte[] s) + private void triple_half_block(byte[] s) { - int i; byte[] tmp = new byte[8]; /*x^{64} + x^4 + x^3 + x + 1*/ - for (i = 0; i < 7; i++) + for (int i = 0; i < 7; i++) { tmp[i] = (byte)(((s[i] & 0xFF) << 1) | ((s[i + 1] & 0xFF) >>> 7)); } tmp[7] = (byte)(((s[7] & 0xFF) << 1) ^ (((s[0] & 0xFF) >>> 7) * 27)); - System.arraycopy(tmp, 0, d, 0, 8); - } - - private void triple_half_block(byte[] d, byte[] s) - { - byte[] tmp = new byte[8]; - double_half_block(tmp, s); - Bytes.xor(8, s, tmp, d); + Bytes.xorTo(8, tmp, s); } private void pho1(byte[] d, byte[] Y, byte[] M, int mOff, int no_of_bytes) { byte[] tmpM = new byte[16]; - //padding(tmpM, M, mOff, no_of_bytes); byte[] tmp = new byte[16]; if (no_of_bytes == 0) { @@ -160,11 +156,10 @@ else if (no_of_bytes < 16) { System.arraycopy(M, mOff, tmpM, 0, no_of_bytes); } - int i; //G(Y, Y); /*Y[1],Y[2] -> Y[2],Y[1]<<<1*/ System.arraycopy(Y, 8, tmp, 0, 8); - for (i = 0; i < 7; i++) + for (int i = 0; i < 7; i++) { tmp[i + 8] = (byte)((Y[i] & 0xFF) << 1 | (Y[i + 1] & 0xFF) >>> 7); } @@ -173,25 +168,13 @@ else if (no_of_bytes < 16) Bytes.xor(16, Y, tmpM, d); } - private void pho(byte[] Y, byte[] M, int mOff, byte[] X, byte[] C, int cOff, int no_of_bytes) - { - Bytes.xor(no_of_bytes, Y, M, mOff, C, cOff); - pho1(X, Y, M, mOff, no_of_bytes); - } - - private void phoprime(byte[] Y, byte[] C, int cOff, byte[] X, byte[] M, int mOff, int no_of_bytes) - { - Bytes.xor(no_of_bytes, Y, C, cOff, M, mOff); - pho1(X, Y, M, mOff, no_of_bytes); - } - @Override protected void processBufferAAD(byte[] in, int inOff) { pho1(input, Y, in, inOff, 16); /* offset = 2*offset */ - double_half_block(offset, offset); - xor_topbar_block(input, input, offset); + double_half_block(offset); + Bytes.xorTo(8, offset, input); /* Y[i] = E(X[i]) */ giftb128(input, k, Y); } @@ -203,20 +186,20 @@ protected void processFinalAAD() /* last byte[] */ /* full byte[]: offset = 3*offset */ /* partial byte[]: offset = 3^2*offset */ - triple_half_block(offset, offset); + triple_half_block(offset); if (((m_aadPos & 15) != 0) || m_state == State.DecInit || m_state == State.EncInit) { - triple_half_block(offset, offset); + triple_half_block(offset); } if (len == 0) { /* empty M: offset = 3^2*offset */ - triple_half_block(offset, offset); - triple_half_block(offset, offset); + triple_half_block(offset); + triple_half_block(offset); } /* X[i] = (pad(A[i]) + G(Y[i-1])) + offset */ pho1(input, Y, m_aad, 0, m_aadPos); - xor_topbar_block(input, input, offset); + Bytes.xorTo(8, offset, input); /* Y[a] = E(X[a]) */ giftb128(input, k, Y); } @@ -255,50 +238,49 @@ protected void init(byte[] key, byte[] iv) reset(false); } - @Override protected void processFinalBlock(byte[] output, int outOff) { - int inOff = 0; int len = dataOperator.getLen() - (forEncryption ? 0 : MAC_SIZE); if (len != 0) { /* full block: offset = 3*offset */ /* empty data / partial block: offset = 3^2*offset */ - triple_half_block(offset, offset); + triple_half_block(offset); if ((len & 15) != 0) { - triple_half_block(offset, offset); + triple_half_block(offset); } /* last block */ /* C[m] = Y[m+a-1] + M[m]*/ /* X[a+m] = M[m] + G(Y[m+a-1]) + offset */ + Bytes.xor(m_bufPos, Y, m_buf, 0, output, outOff); if (forEncryption) { - pho(Y, m_buf, inOff, input, output, outOff, m_bufPos); + pho1(input, Y, m_buf, 0, m_bufPos); } else { - phoprime(Y, m_buf, inOff, input, output, outOff, m_bufPos); + pho1(input, Y, output, outOff, m_bufPos); } - xor_topbar_block(input, input, offset); + Bytes.xorTo(8, offset, input); /* T = E(X[m+a]) */ giftb128(input, k, Y); } System.arraycopy(Y, 0, mac, 0, BlockSize); } - @Override protected void processBufferEncrypt(byte[] inputM, int inOff, byte[] output, int outOff) { /* Process M */ /* full byte[]s */ - double_half_block(offset, offset); + double_half_block(offset); /* C[i] = Y[i+a-1] + M[i]*/ /* X[i] = M[i] + G(Y[i+a-1]) + offset */ - pho(Y, inputM, inOff, input, output, outOff, BlockSize); - xor_topbar_block(input, input, offset); + Bytes.xor(BlockSize, Y, inputM, inOff, output, outOff); + pho1(input, Y, inputM, inOff, BlockSize); + Bytes.xorTo(8, offset, input); /* Y[i] = E(X[i+a]) */ giftb128(input, k, Y); } @@ -308,11 +290,12 @@ protected void processBufferDecrypt(byte[] inputM, int inOff, byte[] output, int { /* Process M */ /* full byte[]s */ - double_half_block(offset, offset); + double_half_block(offset); /* C[i] = Y[i+a-1] + M[i]*/ /* X[i] = M[i] + G(Y[i+a-1]) + offset */ - phoprime(Y, inputM, inOff, input, output, outOff, BlockSize); - xor_topbar_block(input, input, offset); + Bytes.xor(BlockSize, Y, inputM, inOff, output, outOff); + pho1(input, Y, output, outOff, BlockSize); + Bytes.xorTo(8, offset, input); /* Y[i] = E(X[i+a]) */ giftb128(input, k, Y); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index ec730279b9..6c1965f2a4 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -123,8 +123,8 @@ private class RomulusM private final byte[] mac_s = new byte[16]; private final byte[] mac_CNT = new byte[7]; private final byte[] s = new byte[16]; - int offset; - boolean twist = true; + private int offset; + private boolean twist = true; public RomulusM() { @@ -339,7 +339,6 @@ private class RomulusN public RomulusN() { s = new byte[AD_BLK_LEN_HALF]; - twist = true; } @Override @@ -825,7 +824,6 @@ void block_cipher(byte[] s, byte[] K, byte[] T, int tOff, byte[] CNT, byte D) // Combines the secret key, counter and domain bits to form the full 384-bit tweakey System.arraycopy(CNT, 0, KT, 0, 7); KT[7] = D; - Arrays.fill(KT, 8, 16, (byte)0x00); System.arraycopy(T, tOff, KT, 16, 16); System.arraycopy(K, 0, KT, 32, 16); skinny_128_384_plus_enc(s, KT); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java index 7ddbe068ec..2f838e5d80 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.engines; import org.bouncycastle.crypto.digests.SparkleDigest; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Bytes; import org.bouncycastle.util.Integers; import org.bouncycastle.util.Pack; @@ -282,11 +283,8 @@ protected void processFinalAAD() state[STATE_WORDS - 1] ^= _A0; // padding - m_aad[m_aadPos] = (byte)0x80; - while (++m_aadPos < BlockSize) - { - m_aad[m_aadPos] = 0x00; - } + m_aad[m_aadPos++] = (byte)0x80; + Arrays.fill(m_aad, m_aadPos, BlockSize, (byte)0); } else { diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index 772a375411..167990b30b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -88,8 +88,7 @@ protected void finishAAD(State nextState, boolean isDoFinal) protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { - int Cu = encrypted ? 0 : 0x80; - up(mode, state, Cu); /* Up without extract */ + up(mode, state, encrypted ? 0 : 0x80); /* Up without extract */ /* Extract from Up and Add */ Bytes.xor(BlockSize, state, input, inOff, output, outOff); down(mode, state, input, inOff, BlockSize, 0x00); @@ -99,8 +98,7 @@ protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - int Cu = encrypted ? 0 : 0x80; - up(mode, state, Cu); /* Up without extract */ + up(mode, state, encrypted ? 0 : 0x80); /* Up without extract */ /* Extract from Up and Add */ Bytes.xor(BlockSize, state, input, inOff, output, outOff); down(mode, state, output, outOff, BlockSize, 0x00); @@ -111,10 +109,9 @@ protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int @Override protected void processFinalBlock(byte[] output, int outOff) { - int Cu = encrypted ? 0 : 0x80; if (m_bufPos != 0 || !encrypted) { - up(mode, state, Cu); /* Up without extract */ + up(mode, state, encrypted ? 0 : 0x80); /* Up without extract */ /* Extract from Up and Add */ Bytes.xor(m_bufPos, state, m_buf, 0, output, outOff); if (forEncryption) From 476db8592ae0e6464d7b4973dd16895393868f36 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 29 Jan 2025 17:21:22 +1030 Subject: [PATCH 074/890] Refactor in Engines --- .../crypto/digests/PhotonBeetleDigest.java | 10 ++--- .../crypto/engines/ElephantEngine.java | 3 +- .../crypto/engines/ISAPEngine.java | 15 ++------ .../crypto/engines/PhotonBeetleEngine.java | 37 +++++++------------ .../crypto/engines/RomulusEngine.java | 8 +--- 5 files changed, 24 insertions(+), 49 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java index 4e4b5e7bca..62e04624e5 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/PhotonBeetleDigest.java @@ -24,7 +24,6 @@ private Friend() } private final byte[] state; - private final byte[][] state_2d; private static final int SQUEEZE_RATE_INBYTES = 16; private static final int D = 8; private int blockCount; @@ -34,7 +33,6 @@ public PhotonBeetleDigest() super(ProcessingBufferType.Buffered, 4); DigestSize = 32; state = new byte[DigestSize]; - state_2d = new byte[D][D]; algorithmName = "Photon-Beetle Hash"; blockCount = 0; } @@ -48,7 +46,7 @@ protected void processBytes(byte[] input, int inOff) } else { - PhotonBeetleEngine.photonPermutation(Friend.INSTANCE, state_2d, state); + PhotonBeetleEngine.photonPermutation(Friend.INSTANCE, state); Bytes.xorTo(BlockSize, input, inOff, state); } blockCount++; @@ -74,7 +72,7 @@ else if (blockCount == 4 && m_bufPos == 0) } else { - PhotonBeetleEngine.photonPermutation(Friend.INSTANCE, state_2d, state); + PhotonBeetleEngine.photonPermutation(Friend.INSTANCE, state); Bytes.xorTo(m_bufPos, m_buf, state); if (m_bufPos < BlockSize) { @@ -82,9 +80,9 @@ else if (blockCount == 4 && m_bufPos == 0) } state[DigestSize - 1] ^= (m_bufPos % BlockSize == 0 ? (byte)1 : (byte)2) << LAST_THREE_BITS_OFFSET; } - PhotonBeetleEngine.photonPermutation(Friend.INSTANCE, state_2d, state); + PhotonBeetleEngine.photonPermutation(Friend.INSTANCE, state); System.arraycopy(state, 0, output, outOff, SQUEEZE_RATE_INBYTES); - PhotonBeetleEngine.photonPermutation(Friend.INSTANCE, state_2d, state); + PhotonBeetleEngine.photonPermutation(Friend.INSTANCE, state); System.arraycopy(state, 0, output, outOff + SQUEEZE_RATE_INBYTES, SQUEEZE_RATE_INBYTES); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index 11d9185e74..c6789acbad 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -260,7 +260,7 @@ private void KeccakP200Round(byte[] state, int indexRound) private byte ROL8(byte a, int offset) { - return (byte)((offset != 0) ? (((a & 0xFF) << offset) ^ ((a & 0xFF) >>> (8 - offset))) : a); + return (byte)(((a & 0xff) << offset) | ((a & 0xff) >> (8 - offset))); } private int index(int x, int y) @@ -274,7 +274,6 @@ private byte rotl(byte b) return (byte)(((b & 0xFF) << 1) | ((b & 0xFF) >>> 7)); } - // State should be BLOCK_SIZE bytes long // Note: input may be equal to output private void lfsr_step() diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index 5a2f435c8c..f9638200d5 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -167,18 +167,15 @@ private void isap_rk(AsconPermutationFriend.AsconPermutation p, long iv64, byte[ public void processEncBlock(byte[] input, int inOff, byte[] output, int outOff) { - long m64 = Pack.littleEndianToLong(input, inOff); - long c64 = U64BIG(p.x0) ^ m64; + Pack.longToBigEndian(Pack.bigEndianToLong(input, inOff) ^ p.x0, output, outOff); PX1(p); - Pack.longToLittleEndian(c64, output, outOff); } public void processEncFinalBlock(byte[] output, int outOff) { /* Encrypt final m block */ byte[] xo = Pack.longToLittleEndian(p.x0); - int mlen = m_bufPos; - Bytes.xor(mlen, xo, BlockSize - mlen, m_buf, 0, output, outOff); + Bytes.xor(m_bufPos, xo, BlockSize - m_bufPos, m_buf, 0, output, outOff); } public void reset() @@ -197,12 +194,6 @@ private int getLongSize(int x) { return ((x + 7) >>> 3); } - - protected long U64BIG(long x) - { - return ((Longs.rotateRight(x, 8) & (0xFF000000FF000000L)) | (Longs.rotateRight(x, 24) & (0x00FF000000FF0000L)) | - (Longs.rotateRight(x, 40) & (0x0000FF000000FF00L)) | (Longs.rotateRight(x, 56) & (0x000000FF000000FFL))); - } } private class ISAPAEAD_A_128A @@ -250,7 +241,7 @@ protected void PX2(AsconPermutationFriend.AsconPermutation p) private abstract class ISAPAEAD_K implements ISAP_AEAD { - final int ISAP_STATE_SZ_CRYPTO_NPUBBYTES = ISAP_STATE_SZ - IV_SIZE; + protected final int ISAP_STATE_SZ_CRYPTO_NPUBBYTES = ISAP_STATE_SZ - IV_SIZE; protected short[] ISAP_IV1_16; protected short[] ISAP_IV2_16; protected short[] ISAP_IV3_16; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 6252628e07..5a2afe6ae8 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -24,7 +24,6 @@ public enum PhotonBeetleParameters private byte[] K; private byte[] N; private byte[] state; - private byte[][] state_2d; private final int RATE_INBYTES_HALF; private final int STATE_INBYTES; private final int LAST_THREE_BITS_OFFSET; @@ -84,7 +83,6 @@ protected void init(byte[] key, byte[] iv) K = key; N = iv; state = new byte[STATE_INBYTES]; - state_2d = new byte[D][D]; m_state = forEncryption ? State.EncInit : State.DecInit; reset(false); } @@ -92,7 +90,7 @@ protected void init(byte[] key, byte[] iv) protected void processBufferAAD(byte[] input, int inOff) { - photonPermutation(state_2d, state); + photonPermutation(state); Bytes.xorTo(BlockSize, input, inOff, state); } @@ -126,7 +124,7 @@ public void processFinalAAD() { if (m_aadPos != 0) { - photonPermutation(state_2d, state); + photonPermutation(state); Bytes.xorTo(m_aadPos, m_aad, state); if (m_aadPos < BlockSize) { @@ -140,14 +138,12 @@ public void processFinalAAD() protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { - photonPermutation(state_2d, state); rhoohr(output, outOff, input, inOff, BlockSize); Bytes.xorTo(BlockSize, input, inOff, state); } protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - photonPermutation(state_2d, state); rhoohr(output, outOff, input, inOff, BlockSize); Bytes.xorTo(BlockSize, output, outOff, state); } @@ -168,7 +164,6 @@ protected void processFinalBlock(byte[] output, int outOff) { if (bufferLen != 0) { - photonPermutation(state_2d, state); rhoohr(output, outOff, m_buf, 0, bufferLen); if (forEncryption) { @@ -185,11 +180,11 @@ protected void processFinalBlock(byte[] output, int outOff) } state[STATE_INBYTES - 1] ^= c1 << LAST_THREE_BITS_OFFSET; } - if (input_empty) + else if (input_empty) { state[STATE_INBYTES - 1] ^= 1 << LAST_THREE_BITS_OFFSET; } - photonPermutation(state_2d, state); + photonPermutation(state); System.arraycopy(state, 0, mac, 0, MAC_SIZE); } @@ -203,12 +198,13 @@ protected void reset(boolean clearMac) super.reset(clearMac); } - private static void photonPermutation(byte[][] state_2d, byte[] state) + private static void photonPermutation(byte[] state) { int i, j, k; int dq = 3; int dr = 7; int DSquare = 64; + byte[][] state_2d = new byte[D][D]; for (i = 0; i < DSquare; i++) { state_2d[i >>> dq][i & dr] = (byte)(((state[i >> 1] & 0xFF) >>> (4 * (i & 1))) & 0xf); @@ -292,31 +288,26 @@ private byte select(boolean condition1, boolean condition2, byte option3, byte o private void rhoohr(byte[] ciphertext, int outOff, byte[] plaintext, int inOff, int DBlen_inbytes) { - byte[] OuterState_part1_ROTR1 = state_2d[0]; + photonPermutation(state); + byte[] OuterState_part1_ROTR1 = new byte[D]; int i, loop_end = Math.min(DBlen_inbytes, RATE_INBYTES_HALF); for (i = 0; i < RATE_INBYTES_HALF - 1; i++) { OuterState_part1_ROTR1[i] = (byte)(((state[i] & 0xFF) >>> 1) | ((state[(i + 1)] & 1) << 7)); } OuterState_part1_ROTR1[RATE_INBYTES_HALF - 1] = (byte)(((state[i] & 0xFF) >>> 1) | ((state[0] & 1) << 7)); - i = 0; - while (i < loop_end) - { - ciphertext[i + outOff] = (byte)(state[i + RATE_INBYTES_HALF] ^ plaintext[i++ + inOff]); - } - while (i < DBlen_inbytes) - { - ciphertext[i + outOff] = (byte)(OuterState_part1_ROTR1[i - RATE_INBYTES_HALF] ^ plaintext[i++ + inOff]); - } + Bytes.xor(loop_end, state, RATE_INBYTES_HALF, plaintext, inOff, ciphertext, outOff); + Bytes.xor(DBlen_inbytes - loop_end, OuterState_part1_ROTR1, loop_end - RATE_INBYTES_HALF, plaintext, + inOff + loop_end, ciphertext, outOff + loop_end); } - public static void photonPermutation(PhotonBeetleDigest.Friend friend, byte[][] state_2d, byte[] state) + public static void photonPermutation(PhotonBeetleDigest.Friend friend, byte[] state) { if (null == friend) { throw new NullPointerException("This method is only for use by PhotonBeetleDigest"); } - photonPermutation(state_2d, state); + photonPermutation(state); } -} +} \ No newline at end of file diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index 6c1965f2a4..e6b846430f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -245,15 +245,12 @@ int ad_encryption(byte[] A, int AOff, byte[] s, byte[] k, int adlen, byte[] CNT, byte[] T = new byte[16]; byte[] mp = new byte[16]; int n = 16; - int i, len8; + int len8; len8 = Math.min(adlen, n); adlen -= len8; // Rho(S,A) pads an A block and XORs it to the internal state. pad(A, AOff, mp, n, len8); - for (i = 0; i < n; i++) - { - s[i] = (byte)(s[i] ^ mp[i]); - } + Bytes.xorTo(n, mp, s); offset = AOff += len8; lfsr_gf56(CNT); if (adlen != 0) @@ -793,7 +790,6 @@ void rho(byte[] m, int mOff, byte[] c, int cOff, byte[] s, int len8) } } } - } // Applies CNT'=2 * CNT (mod GF(2^56)), where GF(2^56) is defined using the irreducible polynomial From 6cfa1c243c1526294d8a7902449c467eb1ecb490 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 29 Jan 2025 17:45:25 +1030 Subject: [PATCH 075/890] Refactor in Engines --- .../crypto/engines/ISAPEngine.java | 1 - .../crypto/engines/RomulusEngine.java | 84 ++++++++----------- .../crypto/engines/SparkleEngine.java | 1 - 3 files changed, 34 insertions(+), 52 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index f9638200d5..a324de8321 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -2,7 +2,6 @@ import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Bytes; -import org.bouncycastle.util.Longs; import org.bouncycastle.util.Pack; /** diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index e6b846430f..ff1fd4cc5c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -182,10 +182,10 @@ else if (mlen == 0) while (xlen > 0) { offset = mOff; - xlen = ad_encryption(m, mOff, mac_s, k, xlen, mac_CNT, (byte)44); + xlen = ad_encryption(m, mOff, mac_s, k, xlen, mac_CNT); mOff = offset; } - nonce_encryption(npub, mac_CNT, mac_s, k, w); + block_cipher(mac_s, k, npub, 0, mac_CNT, w); // Tag generation g8A(mac_s, mac, 0); mOff -= mlen; @@ -198,7 +198,7 @@ else if (mlen == 0) System.arraycopy(mac, 0, s, 0, AD_BLK_LEN_HALF); if (mlen > 0) { - nonce_encryption(npub, CNT, s, k, (byte)36); + block_cipher(s, k, npub, 0, CNT, (byte)36); while (mlen > AD_BLK_LEN_HALF) { mlen = mlen - AD_BLK_LEN_HALF; @@ -206,7 +206,7 @@ else if (mlen == 0) outOff += AD_BLK_LEN_HALF; mOff += AD_BLK_LEN_HALF; lfsr_gf56(CNT); - nonce_encryption(npub, CNT, s, k, (byte)36); + block_cipher(s, k, npub, 0, CNT, (byte)36); } rho(m, mOff, output, outOff, s, mlen); } @@ -229,10 +229,10 @@ else if (mlen == 0) while (xlen > 0) { offset = mauth; - xlen = ad_encryption(output, mauth, mac_s, k, xlen, mac_CNT, (byte)44); + xlen = ad_encryption(output, mauth, mac_s, k, xlen, mac_CNT); mauth = offset; } - nonce_encryption(npub, mac_CNT, mac_s, k, w); + block_cipher(mac_s, k, npub, 0, mac_CNT, w); // Tag generation g8A(mac_s, mac, 0); System.arraycopy(m, dataOperator.getLen() - MAC_SIZE, m_buf, 0, MAC_SIZE); @@ -240,7 +240,7 @@ else if (mlen == 0) } } - int ad_encryption(byte[] A, int AOff, byte[] s, byte[] k, int adlen, byte[] CNT, byte D) + int ad_encryption(byte[] A, int AOff, byte[] s, byte[] k, int adlen, byte[] CNT) { byte[] T = new byte[16]; byte[] mp = new byte[16]; @@ -259,7 +259,7 @@ int ad_encryption(byte[] A, int AOff, byte[] s, byte[] k, int adlen, byte[] CNT, adlen -= len8; pad(A, AOff, T, n, len8); offset = AOff + len8; - block_cipher(s, k, T, 0, CNT, D); + block_cipher(s, k, T, 0, CNT, (byte)44); lfsr_gf56(CNT); } return adlen; @@ -345,14 +345,14 @@ public void processFinalBlock(byte[] output, int outOff) if (messegeLen == 0) { lfsr_gf56(CNT); - nonce_encryption(npub, CNT, s, k, (byte)0x15); + block_cipher(s, k, npub, 0, CNT, (byte)0x15); } else if (m_bufPos != 0) { int len8 = Math.min(m_bufPos, AD_BLK_LEN_HALF); rho(m_buf, 0, output, outOff, s, len8); lfsr_gf56(CNT); - nonce_encryption(npub, CNT, s, k, m_bufPos == AD_BLK_LEN_HALF ? (byte)0x14 : (byte)0x15); + block_cipher(s, k, npub, 0, CNT, m_bufPos == AD_BLK_LEN_HALF ? (byte)0x14 : (byte)0x15); } g8A(s, mac, 0); } @@ -393,15 +393,15 @@ public void processFinalAAD() if (aadOperator.getLen() == 0) { lfsr_gf56(CNT); - nonce_encryption(npub, CNT, s, k, (byte)0x1a); + block_cipher(s, k, npub, 0, CNT, (byte)0x1a); } else if ((m_aadPos & 15) != 0) { - nonce_encryption(npub, CNT, s, k, (byte)0x1a); + block_cipher(s, k, npub, 0, CNT, (byte)0x1a); } else { - nonce_encryption(npub, CNT, s, k, (byte)0x18); + block_cipher(s, k, npub, 0, CNT, (byte)0x18); } reset_lfsr_gf56(CNT); } @@ -416,7 +416,7 @@ public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int out output[i + outOff] ^= input[i + inOff]; } lfsr_gf56(CNT); - nonce_encryption(npub, CNT, s, k, (byte)0x04); + block_cipher(s, k, npub, 0, CNT, (byte)0x04); } @Override @@ -425,12 +425,11 @@ public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int out g8A(s, output, outOff); for (int i = 0; i < AD_BLK_LEN_HALF; i++) { - s[i] ^= input[i + inOff]; - s[i] ^= output[i + outOff]; output[i + outOff] ^= input[i + inOff]; + s[i] ^= output[i + outOff]; } lfsr_gf56(CNT); - nonce_encryption(npub, CNT, s, k, (byte)0x04); + block_cipher(s, k, npub, 0, CNT, (byte)0x04); } @Override @@ -562,8 +561,7 @@ else if ((m_aadPos >= 0) && (aadOperator.getLen() != 0)) } } - @Override - public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) + private void processBuffer(byte[] input, int inOff, byte[] output, int outOff) { System.arraycopy(npub, 0, S, 0, 16); block_cipher(S, Z, T, 0, CNT, (byte)64); @@ -572,44 +570,38 @@ public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int out block_cipher(S, Z, T, 0, CNT, (byte)65); System.arraycopy(S, 0, Z, 0, 16); lfsr_gf56(CNT); - // ipad_256(ipad*_128(A)||ipad*_128(C)||N|| CNT - System.arraycopy(output, outOff, m_aad, m_aadPos, BlockSize); + } + + private void processAfterAbsorbCiphertext() + { if (m_aadPos == BlockSize) { hirose_128_128_256(h, g, m_aad, 0); m_aadPos = 0; - lfsr_gf56(CNT_Z); } else { m_aadPos = BlockSize; - lfsr_gf56(CNT_Z); } + lfsr_gf56(CNT_Z); + } + + @Override + public void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) + { + processBuffer(input, inOff, output, outOff); + // ipad_256(ipad*_128(A)||ipad*_128(C)||N|| CNT + System.arraycopy(output, outOff, m_aad, m_aadPos, BlockSize); + processAfterAbsorbCiphertext(); } @Override public void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - System.arraycopy(npub, 0, S, 0, 16); - block_cipher(S, Z, T, 0, CNT, (byte)64); - Bytes.xor(AD_BLK_LEN_HALF, S, input, inOff, output, outOff); - System.arraycopy(npub, 0, S, 0, 16); - block_cipher(S, Z, T, 0, CNT, (byte)65); - System.arraycopy(S, 0, Z, 0, 16); - lfsr_gf56(CNT); + processBuffer(input, inOff, output, outOff); // ipad_256(ipad*_128(A)||ipad*_128(C)||N|| CNT System.arraycopy(input, inOff, m_aad, m_aadPos, BlockSize); - if (m_aadPos == BlockSize) - { - hirose_128_128_256(h, g, m_aad, 0); - m_aadPos = 0; - lfsr_gf56(CNT_Z); - } - else - { - m_aadPos = BlockSize; - lfsr_gf56(CNT_Z); - } + processAfterAbsorbCiphertext(); } @Override @@ -825,18 +817,10 @@ void block_cipher(byte[] s, byte[] K, byte[] T, int tOff, byte[] CNT, byte D) skinny_128_384_plus_enc(s, KT); } - // Calls the TBC using the nonce as part of the tweakey - void nonce_encryption(byte[] N, byte[] CNT, byte[] s, byte[] k, byte D) - { - byte[] T = new byte[16]; - System.arraycopy(N, 0, T, 0, 16); - block_cipher(s, k, T, 0, CNT, D); - } - private void reset_lfsr_gf56(byte[] CNT) { CNT[0] = 0x01; - Arrays.fill(CNT, 1, 7, (byte) 0); + Arrays.fill(CNT, 1, 7, (byte)0); } public static void hirose_128_128_256(RomulusDigest.Friend friend, byte[] h, byte[] g, byte[] m, int mOff) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java index 2f838e5d80..889188d1c0 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java @@ -2,7 +2,6 @@ import org.bouncycastle.crypto.digests.SparkleDigest; import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Bytes; import org.bouncycastle.util.Integers; import org.bouncycastle.util.Pack; From aba4b59272d44e6d3ea4d1304a1bf4296164ff77 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 29 Jan 2025 18:05:44 +1030 Subject: [PATCH 076/890] Add some comments in AEADBufferBaseEngine --- .../crypto/engines/AEADBufferBaseEngine.java | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 6a567ef9f3..f39f238ab8 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -12,17 +12,17 @@ abstract class AEADBufferBaseEngine { protected enum ProcessingBufferType { - Buffered, - BufferedLargeMac, - Immediate, - ImmediateLargeMac, + Buffered, // Store a (aad) block size of input and process after the input size exceeds the buffer size + BufferedLargeMac, // handle the situation when mac size is larger than the block size, used for pb128 + Immediate, //process the input immediately when the input size is equal or greater than the block size + ImmediateLargeMac, // handle the situation when mac size is larger than the block size, used for ascon80pq, ascon128, ISAP_A_128(A) } protected enum AADOperatorType { Default, - Counter, - Stream + Counter,//add a counter to count the size of AAD + Stream //process AAD data during the process data, used for elephant } protected enum DataOperatorType @@ -30,7 +30,7 @@ protected enum DataOperatorType Default, Counter, Stream, - //StreamCipher + //StreamCipher //TODO: add for Grain 128 AEAD } protected enum State @@ -525,6 +525,7 @@ protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output if (m_bufPos > 0) { available = BlockSize - m_bufPos; + // The function is just an operator < or <= if (processor.isLengthWithinAvailableSpace(len, available)) { System.arraycopy(input, inOff, m_buf, m_bufPos, len); @@ -538,6 +539,7 @@ protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output processBufferEncrypt(m_buf, 0, output, outOff); resultLength = BlockSize; } + // The function is just an operator >= or > while (processor.isLengthExceedingBlockSize(len, BlockSize)) { processBufferEncrypt(input, inOff, output, outOff + resultLength); @@ -583,6 +585,8 @@ protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output private int processDecryptionWithLargeMacSize(byte[] input, int inOff, int len, byte[] output, int outOff) { int resultLength = 0, available; + // If the mac size is greater than the block size, process the data in m_buf in the loop until + // there is nearly mac_size data left while (processor.isLengthExceedingBlockSize(m_bufPos, BlockSize) && processor.isLengthExceedingBlockSize(len + m_bufPos, m_bufferSizeDecrypt)) { From 4ed7d134ee6d51534f8e98522c63e7a9db3d9285 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 29 Jan 2025 19:03:48 +0700 Subject: [PATCH 077/890] Refactoring in pqc.crypto.mldsa - reduced allocations --- .../pqc/crypto/mldsa/MLDSAEngine.java | 196 ++---------------- .../pqc/crypto/mldsa/Packing.java | 35 ++-- .../bouncycastle/pqc/crypto/mldsa/Poly.java | 181 ++++++++-------- .../pqc/crypto/mldsa/PolyVecK.java | 71 +++---- .../pqc/crypto/mldsa/PolyVecL.java | 57 ++--- .../pqc/crypto/mldsa/PolyVecMatrix.java | 36 ++-- 6 files changed, 183 insertions(+), 393 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAEngine.java index 7fa39f5da3..393146d26c 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAEngine.java @@ -269,7 +269,7 @@ byte[][] generateKeyPairInternal(byte[] seed) s1hat = new PolyVecL(this); - s1.copyPolyVecL(s1hat); + s1.copyTo(s1hat); s1hat.polyVecNtt(); // System.out.println(s1hat.toString("s1hat")); @@ -323,7 +323,7 @@ byte[] deriveT1(byte[] rho, byte[] key, byte[] tr, byte[] s1Enc, byte[] s2Enc, b s1hat = new PolyVecL(this); - s1.copyPolyVecL(s1hat); + s1.copyTo(s1hat); s1hat.polyVecNtt(); // System.out.println(s1hat.toString("s1hat")); @@ -382,7 +382,7 @@ void initVerify(byte[] rho, byte[] encT1, boolean isPreHash, byte[] ctx) } } - public byte[] signInternal(byte[] msg, int msglen, byte[] rho, byte[] key, byte[] t0Enc, byte[] s1Enc, byte[] s2Enc, byte[] rnd) + byte[] signInternal(byte[] msg, int msglen, byte[] rho, byte[] key, byte[] t0Enc, byte[] s1Enc, byte[] s2Enc, byte[] rnd) { SHAKEDigest shake256 = new SHAKEDigest(shake256Digest); @@ -397,7 +397,6 @@ byte[] generateSignature(SHAKEDigest shake256Digest, byte[] rho, byte[] key, byt shake256Digest.doFinal(mu, 0, CrhBytes); - int n; byte[] outSig = new byte[CryptoBytes]; byte[] rhoPrime = new byte[CrhBytes]; short nonce = 0; @@ -428,7 +427,7 @@ byte[] generateSignature(SHAKEDigest shake256Digest, byte[] rho, byte[] key, byt // Sample intermediate vector y.uniformGamma1(rhoPrime, nonce++); - y.copyPolyVecL(z); + y.copyTo(z); z.polyVecNtt(); // Matrix-vector multiplication @@ -440,13 +439,13 @@ byte[] generateSignature(SHAKEDigest shake256Digest, byte[] rho, byte[] key, byt w1.conditionalAddQ(); w1.decompose(w0); - System.arraycopy(w1.packW1(), 0, outSig, 0, DilithiumK * DilithiumPolyW1PackedBytes); + w1.packW1(this, outSig, 0); shake256Digest.update(mu, 0, CrhBytes); shake256Digest.update(outSig, 0, DilithiumK * DilithiumPolyW1PackedBytes); shake256Digest.doFinal(outSig, 0, DilithiumCTilde); - cp.challenge(Arrays.copyOfRange(outSig, 0, DilithiumCTilde)); // uses only the first DilithiumCTilde bytes of sig + cp.challenge(outSig, 0, DilithiumCTilde); cp.polyNtt(); // Compute z, reject if it reveals secret @@ -478,235 +477,84 @@ byte[] generateSignature(SHAKEDigest shake256Digest, byte[] rho, byte[] key, byt w0.addPolyVecK(h); w0.conditionalAddQ(); - n = h.makeHint(w0, w1); + int n = h.makeHint(w0, w1); if (n > DilithiumOmega) { continue; } - return Packing.packSignature(outSig, z, h, this); + Packing.packSignature(outSig, z, h, this); + return outSig; } + // TODO[pqc] Shouldn't this throw an exception here (or in caller)? return null; } - public boolean verifyInternal(byte[] sig, int siglen, SHAKEDigest shake256Digest, byte[] rho, byte[] encT1) + boolean verifyInternal(byte[] sig, int siglen, SHAKEDigest shake256Digest, byte[] rho, byte[] encT1) { if (siglen != CryptoBytes) { return false; } - // System.out.println("publickey = "); - // Helper.printByteArray(publicKey); - byte[] buf, - mu = new byte[CrhBytes], - c, - c2 = new byte[DilithiumCTilde]; - Poly cp = new Poly(this); - PolyVecMatrix aMatrix = new PolyVecMatrix(this); + PolyVecK h = new PolyVecK(this); PolyVecL z = new PolyVecL(this); - PolyVecK t1 = new PolyVecK(this), w1 = new PolyVecK(this), h = new PolyVecK(this); - - t1 = Packing.unpackPublicKey(t1, encT1, this); - - // System.out.println(t1.toString("t1")); - - // System.out.println("rho = "); - // Helper.printByteArray(rho); if (!Packing.unpackSignature(z, h, sig, this)) { return false; } - c = Arrays.copyOfRange(sig, 0, DilithiumCTilde); - - // System.out.println(z.toString("z")); - // System.out.println(h.toString("h")); if (z.checkNorm(getDilithiumGamma1() - getDilithiumBeta())) { return false; } - shake256Digest.doFinal(mu, 0); - - // System.out.println("mu after = "); - // Helper.printByteArray(mu); - - // Matrix-vector multiplication; compute Az - c2^dt1 - cp.challenge(Arrays.copyOfRange(c, 0, DilithiumCTilde)); // use only first DilithiumCTilde of c. - // System.out.println("cp = "); - // System.out.println(cp.toString()); - - aMatrix.expandMatrix(rho); - // System.out.println(aMatrix.toString("aMatrix = ")); - - - z.polyVecNtt(); - aMatrix.pointwiseMontgomery(w1, z); - - cp.polyNtt(); - // System.out.println("cp = "); - // System.out.println(cp.toString()); - - t1.shiftLeft(); - t1.polyVecNtt(); - t1.pointwisePolyMontgomery(cp, t1); - - // System.out.println(t1.toString("t1")); - - w1.subtract(t1); - w1.reduce(); - w1.invNttToMont(); - - // System.out.println(w1.toString("w1 before caddq")); - - // Reconstruct w1 - w1.conditionalAddQ(); - // System.out.println(w1.toString("w1 before hint")); - w1.useHint(w1, h); - // System.out.println(w1.toString("w1")); - - buf = w1.packW1(); - - // System.out.println("buf = "); - // Helper.printByteArray(buf); - - // System.out.println("mu = "); - // Helper.printByteArray(mu); - - SHAKEDigest shakeDigest256 = new SHAKEDigest(256); - shakeDigest256.update(mu, 0, CrhBytes); - shakeDigest256.update(buf, 0, DilithiumK * DilithiumPolyW1PackedBytes); - shakeDigest256.doFinal(c2, 0, DilithiumCTilde); - - // System.out.println("c = "); - // Helper.printByteArray(c); + byte[] buf = new byte[Math.max(CrhBytes + DilithiumK * DilithiumPolyW1PackedBytes, DilithiumCTilde)]; - // System.out.println("c2 = "); - // Helper.printByteArray(c2); + // Mu + shake256Digest.doFinal(buf, 0); - - return Arrays.constantTimeAreEqual(c, c2); - } - - public boolean verifyInternal(byte[] sig, int siglen, byte[] msg, int msglen, byte[] rho, byte[] encT1) - { - if (siglen != CryptoBytes) - { - return false; - } - - // System.out.println("publickey = "); - // Helper.printByteArray(publicKey); - byte[] buf, - mu = new byte[CrhBytes], - c, - c2 = new byte[DilithiumCTilde]; Poly cp = new Poly(this); PolyVecMatrix aMatrix = new PolyVecMatrix(this); - PolyVecL z = new PolyVecL(this); - PolyVecK t1 = new PolyVecK(this), w1 = new PolyVecK(this), h = new PolyVecK(this); + PolyVecK t1 = new PolyVecK(this), w1 = new PolyVecK(this); t1 = Packing.unpackPublicKey(t1, encT1, this); - // System.out.println(t1.toString("t1")); - - // System.out.println("rho = "); - // Helper.printByteArray(rho); - - if (!Packing.unpackSignature(z, h, sig, this)) - { - return false; - } - c = Arrays.copyOfRange(sig, 0, DilithiumCTilde); - - // System.out.println(z.toString("z")); - // System.out.println(h.toString("h")); - - if (z.checkNorm(getDilithiumGamma1() - getDilithiumBeta())) - { - return false; - } - - // Compute crh(crh(rho, t1), msg) -// shake256Digest.update(rho, 0, rho.length); -// shake256Digest.update(encT1, 0, encT1.length); -// shake256Digest.doFinal(mu, 0, TrBytes); - // System.out.println("mu before = "); - // Helper.printByteArray(mu); - - //shake256Digest.update(mu, 0, TrBytes); - shake256Digest.update(msg, 0, msglen); - shake256Digest.doFinal(mu, 0); - - // System.out.println("mu after = "); - // Helper.printByteArray(mu); - // Matrix-vector multiplication; compute Az - c2^dt1 - cp.challenge(Arrays.copyOfRange(c, 0, DilithiumCTilde)); // use only first DilithiumCTilde of c. - // System.out.println("cp = "); - // System.out.println(cp.toString()); + cp.challenge(sig, 0, DilithiumCTilde); aMatrix.expandMatrix(rho); - // System.out.println(aMatrix.toString("aMatrix = ")); - z.polyVecNtt(); aMatrix.pointwiseMontgomery(w1, z); cp.polyNtt(); - // System.out.println("cp = "); - // System.out.println(cp.toString()); t1.shiftLeft(); t1.polyVecNtt(); t1.pointwisePolyMontgomery(cp, t1); - // System.out.println(t1.toString("t1")); - w1.subtract(t1); w1.reduce(); w1.invNttToMont(); - // System.out.println(w1.toString("w1 before caddq")); - - // Reconstruct w1 w1.conditionalAddQ(); - // System.out.println(w1.toString("w1 before hint")); w1.useHint(w1, h); - // System.out.println(w1.toString("w1")); - - buf = w1.packW1(); - - // System.out.println("buf = "); - // Helper.printByteArray(buf); - // System.out.println("mu = "); - // Helper.printByteArray(mu); + w1.packW1(this, buf, CrhBytes); - SHAKEDigest shakeDigest256 = new SHAKEDigest(256); - shakeDigest256.update(mu, 0, CrhBytes); - shakeDigest256.update(buf, 0, DilithiumK * DilithiumPolyW1PackedBytes); - shakeDigest256.doFinal(c2, 0, DilithiumCTilde); + shake256Digest.update(buf, 0, CrhBytes + DilithiumK * DilithiumPolyW1PackedBytes); + shake256Digest.doFinal(buf, 0, DilithiumCTilde); - // System.out.println("c = "); - // Helper.printByteArray(c); - - // System.out.println("c2 = "); - // Helper.printByteArray(c2); - - - return Arrays.constantTimeAreEqual(c, c2); + return Arrays.constantTimeAreEqual(DilithiumCTilde, sig, 0, buf, 0); } - public byte[][] generateKeyPair() + byte[][] generateKeyPair() { byte[] seedBuf = new byte[SeedBytes]; random.nextBytes(seedBuf); return generateKeyPairInternal(seedBuf); - } - } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/Packing.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/Packing.java index 51ed681bd2..c577fea200 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/Packing.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/Packing.java @@ -4,7 +4,6 @@ class Packing { - static byte[] packPublicKey(PolyVecK t1, MLDSAEngine engine) { byte[] out = new byte[engine.getCryptoPublicKeyBytes() - MLDSAEngine.SeedBytes]; @@ -62,7 +61,6 @@ static byte[][] packSecretKey(byte[] rho, byte[] tr, byte[] key, PolyVecK t0, Po * @param engine * @return Byte matrix where byte[0] = rho, byte[1] = tr, byte[2] = key */ - static void unpackSecretKey(PolyVecK t0, PolyVecL s1, PolyVecK s2, byte[] t0Enc, byte[] s1Enc, byte[] s2Enc, MLDSAEngine engine) { for (int i = 0; i < engine.getDilithiumL(); ++i) @@ -81,40 +79,32 @@ static void unpackSecretKey(PolyVecK t0, PolyVecL s1, PolyVecK s2, byte[] t0Enc, } } - static byte[] packSignature(byte[] c, PolyVecL z, PolyVecK h, MLDSAEngine engine) + static void packSignature(byte[] sig, PolyVecL z, PolyVecK h, MLDSAEngine engine) { - int i, j, k, end = 0; - byte[] outBytes = new byte[engine.getCryptoBytes()]; - - System.arraycopy(c, 0, outBytes, 0, engine.getDilithiumCTilde()); - end += engine.getDilithiumCTilde(); - - for (i = 0; i < engine.getDilithiumL(); ++i) + int end = engine.getDilithiumCTilde(); + for (int i = 0; i < engine.getDilithiumL(); ++i) { - System.arraycopy(z.getVectorIndex(i).zPack(), 0, outBytes, end + i * engine.getDilithiumPolyZPackedBytes(), engine.getDilithiumPolyZPackedBytes()); + z.getVectorIndex(i).zPack(sig, end); + end += engine.getDilithiumPolyZPackedBytes(); } - end += engine.getDilithiumL() * engine.getDilithiumPolyZPackedBytes(); - for (i = 0; i < engine.getDilithiumOmega() + engine.getDilithiumK(); ++i) + for (int i = 0; i < engine.getDilithiumOmega() + engine.getDilithiumK(); ++i) { - outBytes[end + i] = 0; + sig[end + i] = 0; } - k = 0; - for (i = 0; i < engine.getDilithiumK(); ++i) + int k = 0; + for (int i = 0; i < engine.getDilithiumK(); ++i) { - for (j = 0; j < MLDSAEngine.DilithiumN; ++j) + for (int j = 0; j < MLDSAEngine.DilithiumN; ++j) { if (h.getVectorIndex(i).getCoeffIndex(j) != 0) { - outBytes[end + k++] = (byte)j; + sig[end + k++] = (byte)j; } } - outBytes[end + engine.getDilithiumOmega() + i] = (byte)k; + sig[end + engine.getDilithiumOmega() + i] = (byte)k; } - - return outBytes; - } static boolean unpackSignature(PolyVecL z, PolyVecK h, byte[] sig, MLDSAEngine engine) @@ -161,5 +151,4 @@ static boolean unpackSignature(PolyVecL z, PolyVecK h, byte[] sig, MLDSAEngine e } return true; } - } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/Poly.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/Poly.java index 2066d8607e..6804be37a6 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/Poly.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/Poly.java @@ -4,23 +4,27 @@ class Poly { + private final static int DilithiumN = MLDSAEngine.DilithiumN; + private final int polyUniformNBlocks; private int[] coeffs; private final MLDSAEngine engine; - private final int dilithiumN; private final Symmetric symmetric; - public Poly(MLDSAEngine engine) { - this.dilithiumN = MLDSAEngine.DilithiumN; - this.coeffs = new int[dilithiumN]; + this.coeffs = new int[DilithiumN]; this.engine = engine; this.symmetric = engine.GetSymmetric(); this.polyUniformNBlocks = (768 + symmetric.stream128BlockBytes - 1) / symmetric.stream128BlockBytes; } + void copyTo(Poly z) + { + System.arraycopy(coeffs, 0, z.coeffs, 0, DilithiumN); + } + public int getCoeffIndex(int i) { return this.coeffs[i]; @@ -51,11 +55,11 @@ public void uniformBlocks(byte[] seed, short nonce) symmetric.stream128squeezeBlocks(buf, 0, buflen); - ctr = rejectUniform(this, 0, dilithiumN, buf, buflen); + ctr = rejectUniform(this, 0, DilithiumN, buf, buflen); // ctr can be less than N - while (ctr < dilithiumN) + while (ctr < DilithiumN) { off = buflen % 3; for (i = 0; i < off; ++i) @@ -64,7 +68,7 @@ public void uniformBlocks(byte[] seed, short nonce) } symmetric.stream128squeezeBlocks(buf, off, symmetric.stream128BlockBytes); buflen = symmetric.stream128BlockBytes + off; - ctr += rejectUniform(this, ctr, dilithiumN - ctr, buf, buflen); + ctr += rejectUniform(this, ctr, DilithiumN - ctr, buf, buflen); } } @@ -117,12 +121,12 @@ else if (engine.getDilithiumEta() == 4) symmetric.stream256init(seed, nonce); symmetric.stream256squeezeBlocks(buf, 0, buflen); - ctr = rejectEta(this, 0, dilithiumN, buf, buflen, eta); + ctr = rejectEta(this, 0, DilithiumN, buf, buflen, eta); while (ctr < MLDSAEngine.DilithiumN) { symmetric.stream256squeezeBlocks(buf, 0, symmetric.stream256BlockBytes); - ctr += rejectEta(this, ctr, dilithiumN - ctr, buf, symmetric.stream256BlockBytes, eta); + ctr += rejectEta(this, ctr, DilithiumN - ctr, buf, symmetric.stream256BlockBytes, eta); } } @@ -179,7 +183,7 @@ public void polyNtt() public void pointwiseMontgomery(Poly v, Poly w) { int i; - for (i = 0; i < dilithiumN; ++i) + for (i = 0; i < DilithiumN; ++i) { this.setCoeffIndex(i, Reduce.montgomeryReduce((long)((long)v.getCoeffIndex(i) * (long)w.getCoeffIndex(i)))); } @@ -204,7 +208,7 @@ public void pointwiseAccountMontgomery(PolyVecL u, PolyVecL v) public void addPoly(Poly a) { int i; - for (i = 0; i < dilithiumN; i++) + for (i = 0; i < DilithiumN; i++) { this.setCoeffIndex(i, this.getCoeffIndex(i) + a.getCoeffIndex(i)); } @@ -213,7 +217,7 @@ public void addPoly(Poly a) public void reduce() { - for (int i = 0; i < dilithiumN; ++i) + for (int i = 0; i < DilithiumN; ++i) { this.setCoeffIndex(i, Reduce.reduce32(this.getCoeffIndex(i))); } @@ -226,7 +230,7 @@ public void invNttToMont() public void conditionalAddQ() { - for (int i = 0; i < dilithiumN; ++i) + for (int i = 0; i < DilithiumN; ++i) { this.setCoeffIndex(i, Reduce.conditionalAddQ(this.getCoeffIndex(i))); } @@ -234,7 +238,7 @@ public void conditionalAddQ() public void power2Round(Poly a) { - for (int i = 0; i < dilithiumN; ++i) + for (int i = 0; i < DilithiumN; ++i) { int[] p2r = Rounding.power2Round(this.getCoeffIndex(i)); this.setCoeffIndex(i, p2r[0]); @@ -246,7 +250,7 @@ public byte[] polyt1Pack() { byte[] out = new byte[MLDSAEngine.DilithiumPolyT1PackedBytes]; - for (int i = 0; i < dilithiumN / 4; ++i) + for (int i = 0; i < DilithiumN / 4; ++i) { out[5 * i + 0] = (byte)(this.coeffs[4 * i + 0] >> 0); out[5 * i + 1] = (byte)((this.coeffs[4 * i + 0] >> 8) | (this.coeffs[4 * i + 1] << 2)); @@ -261,7 +265,7 @@ public void polyt1Unpack(byte[] a) { int i; - for (i = 0; i < dilithiumN / 4; ++i) + for (i = 0; i < DilithiumN / 4; ++i) { this.setCoeffIndex(4 * i + 0, (((a[5 * i + 0] & 0xFF) >> 0) | ((int)(a[5 * i + 1] & 0xFF) << 8)) & 0x3FF); this.setCoeffIndex(4 * i + 1, (((a[5 * i + 1] & 0xFF) >> 2) | ((int)(a[5 * i + 2] & 0xFF) << 6)) & 0x3FF); @@ -277,7 +281,7 @@ public byte[] polyEtaPack(byte[] out, int outOff) if (engine.getDilithiumEta() == 2) { - for (i = 0; i < dilithiumN / 8; ++i) + for (i = 0; i < DilithiumN / 8; ++i) { t[0] = (byte)(engine.getDilithiumEta() - this.getCoeffIndex(8 * i + 0)); t[1] = (byte)(engine.getDilithiumEta() - this.getCoeffIndex(8 * i + 1)); @@ -295,7 +299,7 @@ public byte[] polyEtaPack(byte[] out, int outOff) } else if (engine.getDilithiumEta() == 4) { - for (i = 0; i < dilithiumN / 2; ++i) + for (i = 0; i < DilithiumN / 2; ++i) { t[0] = (byte)(engine.getDilithiumEta() - this.getCoeffIndex(2 * i + 0)); t[1] = (byte)(engine.getDilithiumEta() - this.getCoeffIndex(2 * i + 1)); @@ -315,7 +319,7 @@ public void polyEtaUnpack(byte[] a, int aOff) if (engine.getDilithiumEta() == 2) { - for (i = 0; i < dilithiumN / 8; ++i) + for (i = 0; i < DilithiumN / 8; ++i) { int base = aOff + 3 * i; this.setCoeffIndex(8 * i + 0, (((a[base + 0] & 0xFF) >> 0)) & 7); @@ -339,7 +343,7 @@ public void polyEtaUnpack(byte[] a, int aOff) } else if (engine.getDilithiumEta() == 4) { - for (i = 0; i < dilithiumN / 2; ++i) + for (i = 0; i < DilithiumN / 2; ++i) { this.setCoeffIndex(2 * i + 0, a[aOff + i] & 0x0F); this.setCoeffIndex(2 * i + 1, (a[aOff + i] & 0xFF) >> 4); @@ -354,7 +358,7 @@ public byte[] polyt0Pack(byte[] out, int outOff) int i; int[] t = new int[8]; - for (i = 0; i < dilithiumN / 8; ++i) + for (i = 0; i < DilithiumN / 8; ++i) { t[0] = (1 << (MLDSAEngine.DilithiumD - 1)) - this.getCoeffIndex(8 * i + 0); t[1] = (1 << (MLDSAEngine.DilithiumD - 1)) - this.getCoeffIndex(8 * i + 1); @@ -393,7 +397,7 @@ public byte[] polyt0Pack(byte[] out, int outOff) public void polyt0Unpack(byte[] a, int aOff) { int i; - for (i = 0; i < dilithiumN / 8; ++i) + for (i = 0; i < DilithiumN / 8; ++i) { int base = aOff + 13 * i; this.setCoeffIndex(8 * i + 0, @@ -472,10 +476,10 @@ public void uniformGamma1(byte[] seed, short nonce) private void unpackZ(byte[] a) { - int i; - if (engine.getDilithiumGamma1() == (1 << 17)) + int gamma1 = engine.getDilithiumGamma1(); + if (gamma1 == (1 << 17)) { - for (i = 0; i < dilithiumN / 4; ++i) + for (int i = 0; i < DilithiumN / 4; ++i) { this.setCoeffIndex(4 * i + 0, ( @@ -503,15 +507,15 @@ private void unpackZ(byte[] a) ) & 0x3FFFF); - this.setCoeffIndex(4 * i + 0, engine.getDilithiumGamma1() - this.getCoeffIndex(4 * i + 0)); - this.setCoeffIndex(4 * i + 1, engine.getDilithiumGamma1() - this.getCoeffIndex(4 * i + 1)); - this.setCoeffIndex(4 * i + 2, engine.getDilithiumGamma1() - this.getCoeffIndex(4 * i + 2)); - this.setCoeffIndex(4 * i + 3, engine.getDilithiumGamma1() - this.getCoeffIndex(4 * i + 3)); + this.setCoeffIndex(4 * i + 0, gamma1 - this.getCoeffIndex(4 * i + 0)); + this.setCoeffIndex(4 * i + 1, gamma1 - this.getCoeffIndex(4 * i + 1)); + this.setCoeffIndex(4 * i + 2, gamma1 - this.getCoeffIndex(4 * i + 2)); + this.setCoeffIndex(4 * i + 3, gamma1 - this.getCoeffIndex(4 * i + 3)); } } - else if (engine.getDilithiumGamma1() == (1 << 19)) + else if (gamma1 == (1 << 19)) { - for (i = 0; i < dilithiumN / 2; ++i) + for (int i = 0; i < DilithiumN / 2; ++i) { this.setCoeffIndex(2 * i + 0, ( @@ -526,8 +530,8 @@ else if (engine.getDilithiumGamma1() == (1 << 19)) ((a[5 * i + 4] & 0xFF) << 12) ) & 0xFFFFF); - this.setCoeffIndex(2 * i + 0, engine.getDilithiumGamma1() - this.getCoeffIndex(2 * i + 0)); - this.setCoeffIndex(2 * i + 1, engine.getDilithiumGamma1() - this.getCoeffIndex(2 * i + 1)); + this.setCoeffIndex(2 * i + 0, gamma1 - this.getCoeffIndex(2 * i + 0)); + this.setCoeffIndex(2 * i + 1, gamma1 - this.getCoeffIndex(2 * i + 1)); } } else @@ -538,49 +542,46 @@ else if (engine.getDilithiumGamma1() == (1 << 19)) public void decompose(Poly a) { - int i; - for (i = 0; i < dilithiumN; ++i) + int gamma2 = engine.getDilithiumGamma2(); + for (int i = 0; i < DilithiumN; ++i) { - int[] decomp = Rounding.decompose(this.getCoeffIndex(i), engine.getDilithiumGamma2()); + int[] decomp = Rounding.decompose(this.getCoeffIndex(i), gamma2); this.setCoeffIndex(i, decomp[1]); a.setCoeffIndex(i, decomp[0]); } } - public byte[] w1Pack() + void packW1(byte[] r, int rOff) { - int i; - - byte[] out = new byte[engine.getDilithiumPolyW1PackedBytes()]; +// byte[] out = new byte[engine.getDilithiumPolyW1PackedBytes()]; - if (engine.getDilithiumGamma2() == (MLDSAEngine.DilithiumQ - 1) / 88) + int gamma2 = engine.getDilithiumGamma2(); + if (gamma2 == (MLDSAEngine.DilithiumQ - 1) / 88) { - for (i = 0; i < dilithiumN / 4; ++i) + for (int i = 0; i < DilithiumN / 4; ++i) { - out[3 * i + 0] = (byte)(((byte)this.getCoeffIndex(4 * i + 0)) | (this.getCoeffIndex(4 * i + 1) << 6)); - out[3 * i + 1] = (byte)((byte)(this.getCoeffIndex(4 * i + 1) >> 2) | (this.getCoeffIndex(4 * i + 2) << 4)); - out[3 * i + 2] = (byte)((byte)(this.getCoeffIndex(4 * i + 2) >> 4) | (this.getCoeffIndex(4 * i + 3) << 2)); + r[rOff + 3 * i + 0] = (byte)(((byte)getCoeffIndex(4 * i + 0)) | (getCoeffIndex(4 * i + 1) << 6)); + r[rOff + 3 * i + 1] = (byte)((byte)(getCoeffIndex(4 * i + 1) >> 2) | (getCoeffIndex(4 * i + 2) << 4)); + r[rOff + 3 * i + 2] = (byte)((byte)(getCoeffIndex(4 * i + 2) >> 4) | (getCoeffIndex(4 * i + 3) << 2)); } } else if (engine.getDilithiumGamma2() == (MLDSAEngine.DilithiumQ - 1) / 32) { - for (i = 0; i < dilithiumN / 2; ++i) + for (int i = 0; i < DilithiumN / 2; ++i) { - out[i] = (byte)(this.getCoeffIndex(2 * i + 0) | (this.getCoeffIndex(2 * i + 1) << 4)); + r[rOff + i] = (byte)(getCoeffIndex(2 * i + 0) | (getCoeffIndex(2 * i + 1) << 4)); } } - - return out; } - public void challenge(byte[] seed) + public void challenge(byte[] seed, int seedOff, int seedLen) { int i, b = 0, pos; long signs; byte[] buf = new byte[symmetric.stream256BlockBytes]; SHAKEDigest shake256Digest = new SHAKEDigest(256); - shake256Digest.update(seed, 0, engine.getDilithiumCTilde()); + shake256Digest.update(seed, seedOff, seedLen); shake256Digest.doOutput(buf, 0, symmetric.stream256BlockBytes); signs = (long)0; @@ -591,11 +592,11 @@ public void challenge(byte[] seed) pos = 8; - for (i = 0; i < dilithiumN; ++i) + for (i = 0; i < DilithiumN; ++i) { this.setCoeffIndex(i, 0); } - for (i = dilithiumN - engine.getDilithiumTau(); i < dilithiumN; ++i) + for (i = DilithiumN - engine.getDilithiumTau(); i < DilithiumN; ++i) { do { @@ -623,7 +624,7 @@ public boolean checkNorm(int B) return true; } - for (i = 0; i < dilithiumN; ++i) + for (i = 0; i < DilithiumN; ++i) { t = this.getCoeffIndex(i) >> 31; t = this.getCoeffIndex(i) - (t & 2 * this.getCoeffIndex(i)); @@ -638,7 +639,7 @@ public boolean checkNorm(int B) public void subtract(Poly inpPoly) { - for (int i = 0; i < dilithiumN; ++i) + for (int i = 0; i < DilithiumN; ++i) { this.setCoeffIndex(i, this.getCoeffIndex(i) - inpPoly.getCoeffIndex(i)); } @@ -648,7 +649,7 @@ public int polyMakeHint(Poly a0, Poly a1) { int i, s = 0; - for (i = 0; i < dilithiumN; ++i) + for (i = 0; i < DilithiumN; ++i) { this.setCoeffIndex(i, Rounding.makeHint(a0.getCoeffIndex(i), a1.getCoeffIndex(i), engine)); s += this.getCoeffIndex(i); @@ -658,57 +659,53 @@ public int polyMakeHint(Poly a0, Poly a1) public void polyUseHint(Poly a, Poly h) { - for (int i = 0; i < dilithiumN; ++i) + for (int i = 0; i < DilithiumN; ++i) { this.setCoeffIndex(i, Rounding.useHint(a.getCoeffIndex(i), h.getCoeffIndex(i), engine.getDilithiumGamma2())); } } - public byte[] zPack() + public void zPack(byte[] z, int zOff) { - byte[] outBytes = new byte[engine.getDilithiumPolyZPackedBytes()]; - int i; - int[] t = new int[4]; - if (engine.getDilithiumGamma1() == (1 << 17)) + int gamma1 = engine.getDilithiumGamma1(); + if (gamma1 == (1 << 17)) { - for (i = 0; i < dilithiumN / 4; ++i) + for (int i = 0; i < DilithiumN / 4; ++i) { - t[0] = engine.getDilithiumGamma1() - this.getCoeffIndex(4 * i + 0); - t[1] = engine.getDilithiumGamma1() - this.getCoeffIndex(4 * i + 1); - t[2] = engine.getDilithiumGamma1() - this.getCoeffIndex(4 * i + 2); - t[3] = engine.getDilithiumGamma1() - this.getCoeffIndex(4 * i + 3); - - outBytes[9 * i + 0] = (byte)t[0]; - outBytes[9 * i + 1] = (byte)(t[0] >> 8); - outBytes[9 * i + 2] = (byte)((byte)(t[0] >> 16) | (t[1] << 2)); - outBytes[9 * i + 3] = (byte)(t[1] >> 6); - outBytes[9 * i + 4] = (byte)((byte)(t[1] >> 14) | (t[2] << 4)); - outBytes[9 * i + 5] = (byte)(t[2] >> 4); - outBytes[9 * i + 6] = (byte)((byte)(t[2] >> 12) | (t[3] << 6)); - outBytes[9 * i + 7] = (byte)(t[3] >> 2); - outBytes[9 * i + 8] = (byte)(t[3] >> 10); + int t0 = gamma1 - getCoeffIndex(4 * i + 0); + int t1 = gamma1 - getCoeffIndex(4 * i + 1); + int t2 = gamma1 - getCoeffIndex(4 * i + 2); + int t3 = gamma1 - getCoeffIndex(4 * i + 3); + + z[zOff + 9 * i + 0] = (byte)t0; + z[zOff + 9 * i + 1] = (byte)(t0 >> 8); + z[zOff + 9 * i + 2] = (byte)((byte)(t0 >> 16) | (t1 << 2)); + z[zOff + 9 * i + 3] = (byte)(t1 >> 6); + z[zOff + 9 * i + 4] = (byte)((byte)(t1 >> 14) | (t2 << 4)); + z[zOff + 9 * i + 5] = (byte)(t2 >> 4); + z[zOff + 9 * i + 6] = (byte)((byte)(t2 >> 12) | (t3 << 6)); + z[zOff + 9 * i + 7] = (byte)(t3 >> 2); + z[zOff + 9 * i + 8] = (byte)(t3 >> 10); } } - else if (engine.getDilithiumGamma1() == (1 << 19)) + else if (gamma1 == (1 << 19)) { - for (i = 0; i < dilithiumN / 2; ++i) + for (int i = 0; i < DilithiumN / 2; ++i) { - t[0] = engine.getDilithiumGamma1() - this.getCoeffIndex(2 * i + 0); - t[1] = engine.getDilithiumGamma1() - this.getCoeffIndex(2 * i + 1); - - outBytes[5 * i + 0] = (byte)t[0]; - outBytes[5 * i + 1] = (byte)(t[0] >> 8); - outBytes[5 * i + 2] = (byte)((byte)(t[0] >> 16) | (t[1] << 4)); - outBytes[5 * i + 3] = (byte)(t[1] >> 4); - outBytes[5 * i + 4] = (byte)(t[1] >> 12); - + int t0 = gamma1 - getCoeffIndex(2 * i + 0); + int t1 = gamma1 - getCoeffIndex(2 * i + 1); + + z[zOff + 5 * i + 0] = (byte)t0; + z[zOff + 5 * i + 1] = (byte)(t0 >> 8); + z[zOff + 5 * i + 2] = (byte)((byte)(t0 >> 16) | (t1 << 4)); + z[zOff + 5 * i + 3] = (byte)(t1 >> 4); + z[zOff + 5 * i + 4] = (byte)(t1 >> 12); } } else { throw new RuntimeException("Wrong Dilithium Gamma1!"); } - return outBytes; } void zUnpack(byte[] a) @@ -716,7 +713,7 @@ void zUnpack(byte[] a) int i; if (engine.getDilithiumGamma1() == (1 << 17)) { - for (i = 0; i < dilithiumN / 4; ++i) + for (i = 0; i < DilithiumN / 4; ++i) { this.setCoeffIndex(4 * i + 0, (((int)(a[9 * i + 0] & 0xFF) @@ -750,7 +747,7 @@ void zUnpack(byte[] a) } else if (engine.getDilithiumGamma1() == (1 << 19)) { - for (i = 0; i < dilithiumN / 2; ++i) + for (i = 0; i < DilithiumN / 2; ++i) { this.setCoeffIndex(2 * i + 0, (int)(((((int)(a[5 * i + 0] & 0xFF)) @@ -778,7 +775,7 @@ else if (engine.getDilithiumGamma1() == (1 << 19)) public void shiftLeft() { - for (int i = 0; i < dilithiumN; ++i) + for (int i = 0; i < DilithiumN; ++i) { this.setCoeffIndex(i, this.getCoeffIndex(i) << MLDSAEngine.DilithiumD); } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/PolyVecK.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/PolyVecK.java index ec8890056e..ba00307d12 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/PolyVecK.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/PolyVecK.java @@ -2,19 +2,11 @@ class PolyVecK { - Poly[] vec; - private MLDSAEngine engine; - private int mode; - private int polyVecBytes; - private int dilithiumK; - private int dilithiumL; + private final Poly[] vec; - public PolyVecK(MLDSAEngine engine) + PolyVecK(MLDSAEngine engine) { - this.engine = engine; - this.mode = engine.getDilithiumMode(); - this.dilithiumK = engine.getDilithiumK(); - this.dilithiumL = engine.getDilithiumL(); + int dilithiumK = engine.getDilithiumK(); this.vec = new Poly[dilithiumK]; for (int i = 0; i < dilithiumK; i++) @@ -23,36 +15,28 @@ public PolyVecK(MLDSAEngine engine) } } - public PolyVecK() - throws Exception - { - throw new Exception("Requires Parameter"); - } - - public Poly getVectorIndex(int i) + Poly getVectorIndex(int i) { return vec[i]; } - public void setVectorIndex(int i, Poly p) + void setVectorIndex(int i, Poly p) { this.vec[i] = p; } public void uniformEta(byte[] seed, short nonce) { - int i; short n = nonce; - for (i = 0; i < dilithiumK; ++i) + for (int i = 0; i < vec.length; ++i) { - getVectorIndex(i).uniformEta(seed, n++); + vec[i].uniformEta(seed, n++); } - } public void reduce() { - for (int i = 0; i < dilithiumK; ++i) + for (int i = 0; i < vec.length; ++i) { this.getVectorIndex(i).reduce(); } @@ -60,7 +44,7 @@ public void reduce() public void invNttToMont() { - for (int i = 0; i < dilithiumK; ++i) + for (int i = 0; i < vec.length; ++i) { this.getVectorIndex(i).invNttToMont(); } @@ -68,7 +52,7 @@ public void invNttToMont() public void addPolyVecK(PolyVecK b) { - for (int i = 0; i < dilithiumK; ++i) + for (int i = 0; i < vec.length; ++i) { this.getVectorIndex(i).addPoly(b.getVectorIndex(i)); } @@ -76,7 +60,7 @@ public void addPolyVecK(PolyVecK b) public void conditionalAddQ() { - for (int i = 0; i < dilithiumK; ++i) + for (int i = 0; i < vec.length; ++i) { this.getVectorIndex(i).conditionalAddQ(); } @@ -84,7 +68,7 @@ public void conditionalAddQ() public void power2Round(PolyVecK pvk) { - for (int i = 0; i < dilithiumK; ++i) + for (int i = 0; i < vec.length; ++i) { this.getVectorIndex(i).power2Round(pvk.getVectorIndex(i)); } @@ -93,7 +77,7 @@ public void power2Round(PolyVecK pvk) public void polyVecNtt() { int i; - for (i = 0; i < dilithiumK; ++i) + for (i = 0; i < vec.length; ++i) { this.vec[i].polyNtt(); } @@ -101,26 +85,23 @@ public void polyVecNtt() public void decompose(PolyVecK v) { - for (int i = 0; i < dilithiumK; ++i) + for (int i = 0; i < vec.length; ++i) { this.getVectorIndex(i).decompose(v.getVectorIndex(i)); } } - public byte[] packW1() + public void packW1(MLDSAEngine engine, byte[] r, int rOff) { - byte[] out = new byte[dilithiumK * engine.getDilithiumPolyW1PackedBytes()]; - int i; - for (i = 0; i < dilithiumK; ++i) + for (int i = 0; i < vec.length; ++i) { - System.arraycopy(this.getVectorIndex(i).w1Pack(), 0, out, i * engine.getDilithiumPolyW1PackedBytes(), engine.getDilithiumPolyW1PackedBytes()); + getVectorIndex(i).packW1(r, rOff + i * engine.getDilithiumPolyW1PackedBytes()); } - return out; } public void pointwisePolyMontgomery(Poly a, PolyVecK v) { - for (int i = 0; i < dilithiumK; ++i) + for (int i = 0; i < vec.length; ++i) { this.getVectorIndex(i).pointwiseMontgomery(a, v.getVectorIndex(i)); } @@ -128,7 +109,7 @@ public void pointwisePolyMontgomery(Poly a, PolyVecK v) public void subtract(PolyVecK inpVec) { - for (int i = 0; i < dilithiumK; ++i) + for (int i = 0; i < vec.length; ++i) { this.getVectorIndex(i).subtract(inpVec.getVectorIndex(i)); } @@ -136,7 +117,7 @@ public void subtract(PolyVecK inpVec) public boolean checkNorm(int bound) { - for (int i = 0; i < dilithiumK; ++i) + for (int i = 0; i < vec.length; ++i) { if (this.getVectorIndex(i).checkNorm(bound)) { @@ -149,8 +130,8 @@ public boolean checkNorm(int bound) public int makeHint(PolyVecK v0, PolyVecK v1) { - int i, s = 0; - for (i = 0; i < dilithiumK; ++i) + int s = 0; + for (int i = 0; i < vec.length; ++i) { s += this.getVectorIndex(i).polyMakeHint(v0.getVectorIndex(i), v1.getVectorIndex(i)); } @@ -160,7 +141,7 @@ public int makeHint(PolyVecK v0, PolyVecK v1) public void useHint(PolyVecK u, PolyVecK h) { - for (int i = 0; i < dilithiumK; ++i) + for (int i = 0; i < vec.length; ++i) { this.getVectorIndex(i).polyUseHint(u.getVectorIndex(i), h.getVectorIndex(i)); } @@ -168,7 +149,7 @@ public void useHint(PolyVecK u, PolyVecK h) public void shiftLeft() { - for (int i = 0; i < dilithiumK; ++i) + for (int i = 0; i < vec.length; ++i) { this.getVectorIndex(i).shiftLeft(); } @@ -178,10 +159,10 @@ public void shiftLeft() public String toString() { String out = "["; - for (int i = 0; i < dilithiumK; i++) + for (int i = 0; i < vec.length; i++) { out += i + " " + this.getVectorIndex(i).toString(); - if (i == dilithiumK - 1) + if (i == vec.length - 1) { continue; } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/PolyVecL.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/PolyVecL.java index 1bff4cfd37..045f67c99d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/PolyVecL.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/PolyVecL.java @@ -2,19 +2,11 @@ class PolyVecL { - Poly[] vec; - private MLDSAEngine engine; - private int mode; - private int polyVecBytes; - private int dilithiumL; - private int dilithiumK; - - public PolyVecL(MLDSAEngine engine) + private final Poly[] vec; + + PolyVecL(MLDSAEngine engine) { - this.engine = engine; - this.mode = engine.getDilithiumMode(); - this.dilithiumL = engine.getDilithiumL(); - this.dilithiumK = engine.getDilithiumK(); + int dilithiumL = engine.getDilithiumL(); this.vec = new Poly[dilithiumL]; for (int i = 0; i < dilithiumL; i++) @@ -34,12 +26,11 @@ public Poly getVectorIndex(int i) return vec[i]; } - public void expandMatrix(byte[] rho, int i) + void uniformBlocks(byte[] rho, int t) { - int j; - for (j = 0; j < dilithiumL; j++) + for (int i = 0; i < vec.length; ++i) { - vec[j].uniformBlocks(rho, (short)((i << 8) + j)); + vec[i].uniformBlocks(rho, (short)(t + i)); } } @@ -47,28 +38,24 @@ public void uniformEta(byte[] seed, short nonce) { int i; short n = nonce; - for (i = 0; i < dilithiumL; ++i) + for (i = 0; i < vec.length; ++i) { getVectorIndex(i).uniformEta(seed, n++); } } - public void copyPolyVecL(PolyVecL outPoly) + void copyTo(PolyVecL z) { - for (int i = 0; i < dilithiumL; i++) + for (int i = 0; i < vec.length; i++) { - for (int j = 0; j < MLDSAEngine.DilithiumN; j++) - { - outPoly.getVectorIndex(i).setCoeffIndex(j, this.getVectorIndex(i).getCoeffIndex(j)); - } + vec[i].copyTo(z.vec[i]); } } public void polyVecNtt() { - int i; - for (i = 0; i < dilithiumL; ++i) + for (int i = 0; i < vec.length; ++i) { this.vec[i].polyNtt(); } @@ -76,17 +63,15 @@ public void polyVecNtt() public void uniformGamma1(byte[] seed, short nonce) { - int i; - for (i = 0; i < dilithiumL; ++i) + for (int i = 0; i < vec.length; ++i) { - this.getVectorIndex(i).uniformGamma1(seed, (short)(dilithiumL * nonce + i)); + this.getVectorIndex(i).uniformGamma1(seed, (short)(vec.length * nonce + i)); } - } public void pointwisePolyMontgomery(Poly a, PolyVecL v) { - for (int i = 0; i < dilithiumL; ++i) + for (int i = 0; i < vec.length; ++i) { this.getVectorIndex(i).pointwiseMontgomery(a, v.getVectorIndex(i)); } @@ -94,7 +79,7 @@ public void pointwisePolyMontgomery(Poly a, PolyVecL v) public void invNttToMont() { - for (int i = 0; i < dilithiumL; ++i) + for (int i = 0; i < vec.length; ++i) { this.getVectorIndex(i).invNttToMont(); } @@ -102,7 +87,7 @@ public void invNttToMont() public void addPolyVecL(PolyVecL v) { - for (int i = 0; i < dilithiumL; ++i) + for (int i = 0; i < vec.length; ++i) { this.getVectorIndex(i).addPoly(v.getVectorIndex(i)); } @@ -110,7 +95,7 @@ public void addPolyVecL(PolyVecL v) public void reduce() { - for (int i = 0; i < dilithiumL; ++i) + for (int i = 0; i < vec.length; ++i) { this.getVectorIndex(i).reduce(); } @@ -118,7 +103,7 @@ public void reduce() public boolean checkNorm(int bound) { - for (int i = 0; i < dilithiumL; ++i) + for (int i = 0; i < vec.length; ++i) { if (this.getVectorIndex(i).checkNorm(bound)) { @@ -132,10 +117,10 @@ public boolean checkNorm(int bound) public String toString() { String out = "\n["; - for (int i = 0; i < dilithiumL; i++) + for (int i = 0; i < vec.length; i++) { out += "Inner Matrix " + i + " " + this.getVectorIndex(i).toString(); - if (i == dilithiumL - 1) + if (i == vec.length - 1) { continue; } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/PolyVecMatrix.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/PolyVecMatrix.java index d1d36a58de..562377fa97 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/PolyVecMatrix.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/PolyVecMatrix.java @@ -2,58 +2,48 @@ class PolyVecMatrix { - private final int dilithiumK; - private final int dilithiumL; - - private final PolyVecL[] mat; + private final PolyVecL[] matrix; /** * PolyVecL Matrix of size K * * @param engine source engine for the matrix to be used by. */ - public PolyVecMatrix(MLDSAEngine engine) + PolyVecMatrix(MLDSAEngine engine) { - this.dilithiumK = engine.getDilithiumK(); - this.dilithiumL = engine.getDilithiumL(); - this.mat = new PolyVecL[dilithiumK]; + int K = engine.getDilithiumK(); - for (int i = 0; i < dilithiumK; i++) + this.matrix = new PolyVecL[K]; + for (int i = 0; i < K; i++) { - mat[i] = new PolyVecL(engine); + matrix[i] = new PolyVecL(engine); } } public void pointwiseMontgomery(PolyVecK t, PolyVecL v) { - int i; - for (i = 0; i < dilithiumK; ++i) + for (int i = 0; i < matrix.length; ++i) { - t.getVectorIndex(i).pointwiseAccountMontgomery(mat[i], v); + t.getVectorIndex(i).pointwiseAccountMontgomery(matrix[i], v); } } public void expandMatrix(byte[] rho) { - int i, j; - for (i = 0; i < dilithiumK; ++i) + for (int i = 0; i < matrix.length; ++i) { - for (j = 0; j < dilithiumL; ++j) - { - this.mat[i].getVectorIndex(j).uniformBlocks(rho, (short)((i << 8) + j)); - } + matrix[i].uniformBlocks(rho, i << 8); } } private String addString() { String out = "["; - int i; - for (i = 0; i < dilithiumK; i++) + for (int i = 0; i < matrix.length; i++) { out += "Outer Matrix " + i + " ["; - out += this.mat[i].toString(); - if (i == dilithiumK - 1) + out += matrix[i].toString(); + if (i == matrix.length - 1) { out += "]\n"; continue; From bee6734a7fda6498d3cca0b495911db92538a5b3 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 29 Jan 2025 19:04:39 +0700 Subject: [PATCH 078/890] Refactoring in tls.crypto.impl --- .../bouncycastle/tls/crypto/impl/PQCUtil.java | 21 ++----------------- .../crypto/impl/bc/BcTlsEd25519Signer.java | 7 ------- .../tls/crypto/impl/bc/BcTlsEd448Signer.java | 7 ------- .../tls/crypto/impl/bc/BcTlsMLDSASigner.java | 7 ------- .../impl/bc/BcTlsRawKeyCertificate.java | 19 ++++++----------- .../tls/crypto/impl/bc/BcTlsSM2Signer.java | 7 ------- .../tls/crypto/impl/bc/BcTlsSigner.java | 7 +++++++ 7 files changed, 15 insertions(+), 60 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/PQCUtil.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/PQCUtil.java index 0a40e3dd05..0f8d51bd4a 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/PQCUtil.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/PQCUtil.java @@ -23,23 +23,6 @@ public static ASN1ObjectIdentifier getMLDSAObjectidentifier(int signatureScheme) } } - public static ASN1ObjectIdentifier getMLDSAObjectidentifier(MLDSAParameters parameters) - { - if (MLDSAParameters.ml_dsa_44 == parameters) - { - return NISTObjectIdentifiers.id_ml_dsa_44; - } - if (MLDSAParameters.ml_dsa_65 == parameters) - { - return NISTObjectIdentifiers.id_ml_dsa_65; - } - if (MLDSAParameters.ml_dsa_87 == parameters) - { - return NISTObjectIdentifiers.id_ml_dsa_87; - } - throw new IllegalArgumentException(); - } - public static int getMLDSASignatureScheme(MLDSAParameters parameters) { if (MLDSAParameters.ml_dsa_44 == parameters) @@ -57,9 +40,9 @@ public static int getMLDSASignatureScheme(MLDSAParameters parameters) throw new IllegalArgumentException(); } - public static boolean supportsMLDSA(AlgorithmIdentifier pubKeyAlgID, ASN1ObjectIdentifier algorithm) + public static boolean supportsMLDSA(AlgorithmIdentifier pubKeyAlgID, ASN1ObjectIdentifier mlDsaAlgOid) { - return pubKeyAlgID.getAlgorithm().equals(algorithm) + return pubKeyAlgID.getAlgorithm().equals(mlDsaAlgOid) && pubKeyAlgID.getParameters() == null; } } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEd25519Signer.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEd25519Signer.java index cdf6ec4fcc..de3df46357 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEd25519Signer.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEd25519Signer.java @@ -1,7 +1,5 @@ package org.bouncycastle.tls.crypto.impl.bc; -import java.io.IOException; - import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; import org.bouncycastle.crypto.signers.Ed25519Signer; import org.bouncycastle.tls.SignatureAndHashAlgorithm; @@ -16,11 +14,6 @@ public BcTlsEd25519Signer(BcTlsCrypto crypto, Ed25519PrivateKeyParameters privat super(crypto, privateKey); } - public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash) throws IOException - { - throw new UnsupportedOperationException(); - } - public TlsStreamSigner getStreamSigner(SignatureAndHashAlgorithm algorithm) { if (algorithm == null || SignatureScheme.from(algorithm) != SignatureScheme.ed25519) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEd448Signer.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEd448Signer.java index a6decb17cc..e7810861ce 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEd448Signer.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsEd448Signer.java @@ -1,7 +1,5 @@ package org.bouncycastle.tls.crypto.impl.bc; -import java.io.IOException; - import org.bouncycastle.crypto.params.Ed448PrivateKeyParameters; import org.bouncycastle.crypto.signers.Ed448Signer; import org.bouncycastle.tls.SignatureAndHashAlgorithm; @@ -17,11 +15,6 @@ public BcTlsEd448Signer(BcTlsCrypto crypto, Ed448PrivateKeyParameters privateKey super(crypto, privateKey); } - public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash) throws IOException - { - throw new UnsupportedOperationException(); - } - public TlsStreamSigner getStreamSigner(SignatureAndHashAlgorithm algorithm) { if (algorithm == null || SignatureScheme.from(algorithm) != SignatureScheme.ed448) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLDSASigner.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLDSASigner.java index 69b6f2ea22..09e3de2c1b 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLDSASigner.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLDSASigner.java @@ -1,7 +1,5 @@ package org.bouncycastle.tls.crypto.impl.bc; -import java.io.IOException; - import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.pqc.crypto.mldsa.MLDSAPrivateKeyParameters; import org.bouncycastle.pqc.crypto.mldsa.MLDSASigner; @@ -32,11 +30,6 @@ private BcTlsMLDSASigner(BcTlsCrypto crypto, MLDSAPrivateKeyParameters privateKe this.signatureScheme = signatureScheme; } - public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash) throws IOException - { - throw new UnsupportedOperationException(); - } - public TlsStreamSigner getStreamSigner(SignatureAndHashAlgorithm algorithm) { if (algorithm == null || SignatureScheme.from(algorithm) != signatureScheme) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsRawKeyCertificate.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsRawKeyCertificate.java index 7babf085c7..ed9342dded 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsRawKeyCertificate.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsRawKeyCertificate.java @@ -26,7 +26,6 @@ import org.bouncycastle.crypto.signers.PSSSigner; import org.bouncycastle.crypto.signers.RSADigestSigner; import org.bouncycastle.crypto.util.PublicKeyFactory; -import org.bouncycastle.pqc.crypto.mldsa.MLDSAParameters; import org.bouncycastle.pqc.crypto.mldsa.MLDSAPublicKeyParameters; import org.bouncycastle.pqc.crypto.mldsa.MLDSASigner; import org.bouncycastle.tls.AlertDescription; @@ -255,16 +254,10 @@ public Tls13Verifier createVerifier(int signatureScheme) throws IOException case SignatureScheme.DRAFT_mldsa65: case SignatureScheme.DRAFT_mldsa87: { - ASN1ObjectIdentifier algorithm = PQCUtil.getMLDSAObjectidentifier(signatureScheme); - validateMLDSA(algorithm); + ASN1ObjectIdentifier mlDsaAlgOid = PQCUtil.getMLDSAObjectidentifier(signatureScheme); + validateMLDSA(mlDsaAlgOid); MLDSAPublicKeyParameters publicKey = getPubKeyMLDSA(); - MLDSAParameters parameters = publicKey.getParameters(); - if (!PQCUtil.getMLDSAObjectidentifier(parameters).equals(algorithm)) - { - throw new TlsFatalAlert(AlertDescription.certificate_unknown, - "ML-DSA public key not for " + SignatureScheme.getText(signatureScheme)); - } MLDSASigner verifier = new MLDSASigner(); verifier.init(false, publicKey); @@ -485,10 +478,10 @@ protected boolean supportsKeyUsage(int keyUsageBit) return true; } - protected boolean supportsMLDSA(ASN1ObjectIdentifier algorithm) + protected boolean supportsMLDSA(ASN1ObjectIdentifier mlDsaAlgOid) { AlgorithmIdentifier pubKeyAlgID = keyInfo.getAlgorithm(); - return PQCUtil.supportsMLDSA(pubKeyAlgID, algorithm); + return PQCUtil.supportsMLDSA(pubKeyAlgID, mlDsaAlgOid); } protected boolean supportsRSA_PKCS1() @@ -582,10 +575,10 @@ public void validateKeyUsage(int keyUsageBit) } } - protected void validateMLDSA(ASN1ObjectIdentifier algorithm) + protected void validateMLDSA(ASN1ObjectIdentifier mlDsaAlgOid) throws IOException { - if (!supportsMLDSA(algorithm)) + if (!supportsMLDSA(mlDsaAlgOid)) { throw new TlsFatalAlert(AlertDescription.certificate_unknown, "No support for ML-DSA signature scheme"); } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsSM2Signer.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsSM2Signer.java index 6d964a4902..e7203411e9 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsSM2Signer.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsSM2Signer.java @@ -1,7 +1,5 @@ package org.bouncycastle.tls.crypto.impl.bc; -import java.io.IOException; - import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ParametersWithID; import org.bouncycastle.crypto.params.ParametersWithRandom; @@ -22,11 +20,6 @@ public BcTlsSM2Signer(BcTlsCrypto crypto, ECPrivateKeyParameters privateKey, byt this.identifier = Arrays.clone(identifier); } - public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash) throws IOException - { - throw new UnsupportedOperationException(); - } - public TlsStreamSigner getStreamSigner(SignatureAndHashAlgorithm algorithm) { if (algorithm == null diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsSigner.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsSigner.java index 8b76ab7730..1c20f74445 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsSigner.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsSigner.java @@ -1,5 +1,7 @@ package org.bouncycastle.tls.crypto.impl.bc; +import java.io.IOException; + import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.tls.SignatureAndHashAlgorithm; import org.bouncycastle.tls.crypto.TlsSigner; @@ -30,6 +32,11 @@ protected BcTlsSigner(BcTlsCrypto crypto, AsymmetricKeyParameter privateKey) this.privateKey = privateKey; } + public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] hash) throws IOException + { + throw new UnsupportedOperationException(); + } + public TlsStreamSigner getStreamSigner(SignatureAndHashAlgorithm algorithm) { return null; From b11ddb0b76732ae7a257ad6443c7cdd7cc26b5b6 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 30 Jan 2025 17:38:07 +1030 Subject: [PATCH 079/890] TODO: try to get the correct q --- .../crypto/kems/SAKKEKEMSGenerator.java | 150 ++++++++++++++++++ .../bouncycastle/crypto/kems/SAKKEUtils.java | 66 ++++++++ .../crypto/params/SAKKEPrivateKey.java | 37 +++++ .../crypto/params/SAKKEPublicKey.java | 52 ++++++ .../crypto/kems/test/SAKKEKEMSTest.java | 81 ++++++++++ 5 files changed, 386 insertions(+) create mode 100644 core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKey.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java create mode 100644 core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java new file mode 100644 index 0000000000..e44d681f12 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -0,0 +1,150 @@ +package org.bouncycastle.crypto.kems; + +import org.bouncycastle.crypto.EncapsulatedSecretGenerator; +import org.bouncycastle.crypto.SecretWithEncapsulation; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.params.SAKKEPublicKey; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.BigIntegers; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.security.SecureRandom; + +public class SAKKEKEMSGenerator + implements EncapsulatedSecretGenerator +{ + private final SAKKEPublicKey publicParams; + private final SecureRandom random; + + public SAKKEKEMSGenerator(SAKKEPublicKey params, SecureRandom random) + { + this.publicParams = params; + this.random = random; + } + + @Override + public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey) + { + // 1. Generate random SSV in range [0, 2^n - 1] + BigInteger ssv = new BigInteger(publicParams.getN(), random); + + // 2. Compute r = HashToIntegerRange(SSV || b, q) + BigInteger b = getRecipientId((SAKKEPublicKey)recipientKey); + BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), publicParams.getQ()); + + // 3. Compute R_(b,S) = [r]([b]P + Z_S) + ECPoint bP = publicParams.getP().multiply(b); // [b]P + ECPoint Z_S = publicParams.getZ(); // Z_S + ECPoint R_bS = bP.add(Z_S).multiply(r); // [r]([b]P + Z_S) + + // 4. Compute H = SSV XOR HashToIntegerRange( g^r, 2^n ) + BigInteger g_r = pairing(R_bS, publicParams.getP(), publicParams.getQ(), publicParams.getP().getCurve().getField().getCharacteristic()); + BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(publicParams.getN())); // 2^n + + BigInteger H = ssv.xor(mask); + + // 5. Encode encapsulated data (R_bS, H) + byte[] encapsulated = encodeData(R_bS, H); + + return new SecretWithEncapsulationImpl( + BigIntegers.asUnsignedByteArray(publicParams.getN() / 8, ssv), // Output SSV as key material + encapsulated + ); + } + + private BigInteger getRecipientId(SAKKEPublicKey pubKey) + { + byte[] hashedId = SAKKEUtils.hash(pubKey.getZ().getEncoded(false)); // Hash Z_S + return new BigInteger(1, hashedId).mod(pubKey.getQ().subtract(BigInteger.ONE)).add(BigIntegers.TWO); + } + + /** + * Computes the Tate-Lichtenbaum pairing ⟨P, Q⟩ as per RFC 6508. + *

+ * //* @param P First point (on E(F_p)). + * + * @param Q Second point (on E(F_p)). + * @return Result of the pairing in the field F_p^2. + */ + public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) + { + ECCurve curve = R.getCurve(); + ECFieldElement i = curve.fromBigInteger(BigInteger.ONE.negate()); // i = -1 in F_p^2 + + ECPoint C = R; + BigInteger c = p.add(BigInteger.ONE).divide(q); + ECFieldElement v = curve.fromBigInteger(BigInteger.ONE); // v = 1 in F_p + + String qBits = q.subtract(BigInteger.ONE).toString(2); // Binary representation of q-1 + + for (int j = 1; j < qBits.length(); j++) + { // Skip MSB + // l = (3 * (C_x^2 - 1)) / (2 * C_y) + ECFieldElement Cx = C.getAffineXCoord(); + ECFieldElement Cy = C.getAffineYCoord(); + ECFieldElement l = Cx.square().multiply(curve.fromBigInteger(ECFieldElement.THREE)).subtract(curve.fromBigInteger(BigInteger.ONE)) + .divide(Cy.multiply(curve.fromBigInteger(BigIntegers.TWO))); + + // v = v^2 * (l * (Q_x + C_x) + (i * Q_y - C_y)) + ECFieldElement Qx = Q.getAffineXCoord(); + ECFieldElement Qy = Q.getAffineYCoord(); + v = v.square().multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); + + // Double the point + C = C.twice(); + + // If the bit is 1, perform additional step + if (qBits.charAt(j) == '1') + { + // l = (C_y - R_y) / (C_x - R_x) + ECFieldElement Rx = R.getAffineXCoord(); + ECFieldElement Ry = R.getAffineYCoord(); + l = Cy.subtract(Ry).divide(Cx.subtract(Rx)); + + // v = v * (l * (Q_x + C_x) + (i * Q_y - C_y)) + v = v.multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); + + // C = C + R + C = C.add(R); + } + } + + // Compute v^c + v = curve.fromBigInteger(v.toBigInteger().pow(c.intValue())); + + // Convert to F_p representative + return computeFpRepresentative(v, curve); + } + + private static BigInteger computeFpRepresentative(ECFieldElement t, ECCurve curve) + { + // Characteristic of F_p + BigInteger p = ((ECCurve.Fp) curve).getQ(); + + // Assume t = a + i * b in F_p² → extract a, b + ECFieldElement a = t; // In F_p², a is the real part + ECFieldElement b = t.multiply(curve.fromBigInteger(BigInteger.ONE.negate())); // Imaginary part + + // Compute b/a mod p + return b.toBigInteger().multiply(a.toBigInteger().modInverse(p)).mod(p); + } + + public static byte[] encodeData(ECPoint R_bS, BigInteger H) { + // 1. Serialize EC Point (use compressed format for efficiency) + byte[] R_bS_bytes = R_bS.getEncoded(true); + + // 2. Serialize H (convert to a fixed-length byte array) + byte[] H_bytes = H.toByteArray(); + + // 3. Combine both into a single byte array + ByteBuffer buffer = ByteBuffer.allocate(R_bS_bytes.length + H_bytes.length); + buffer.put(R_bS_bytes); + buffer.put(H_bytes); + + return buffer.array(); + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java new file mode 100644 index 0000000000..bc3a9a90f2 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java @@ -0,0 +1,66 @@ +package org.bouncycastle.crypto.kems; + +import java.math.BigInteger; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.Strings; +import org.bouncycastle.util.encoders.Hex; + +public class SAKKEUtils +{ + public static BigInteger hashToIntegerRange(byte[] input, BigInteger q) + { + // RFC 6508 Section 5.1: Hashing to an Integer Range + SHA256Digest digest = new SHA256Digest(); + byte[] hash = new byte[digest.getDigestSize()]; + + // Step 1: Compute A = hashfn(s) + digest.update(input, 0, input.length); + digest.doFinal(hash, 0); + byte[] A = hash.clone(); + + // Step 2: Initialize h_0 to all-zero bytes of hashlen size + byte[] h = new byte[digest.getDigestSize()]; + + // Step 3: Compute l = Ceiling(lg(n)/hashlen) + int l = q.bitLength() >> 8; + + BigInteger v = BigInteger.ZERO; + + // Step 4: Compute h_i and v_i + for (int i = 1; i <= l; i++) + { + // h_i = hashfn(h_{i-1}) + digest.update(h, 0, h.length); + digest.doFinal(h, 0); + System.out.println("h_"+i+":" +new String(Hex.encode(h))); + // v_i = hashfn(h_i || A) + digest.update(h, 0, h.length); + digest.update(A, 0, A.length); + byte[] v_i = new byte[digest.getDigestSize()]; + digest.doFinal(v_i, 0); + System.out.println("v_"+i+":" +new String(Hex.encode(v_i))); + // Append v_i to v' + v = v.shiftLeft(v_i.length * 8).add(new BigInteger(1, v_i)); + } + System.out.println("v:" +new String(Hex.encode(v.toByteArray()))); + // Step 6: v = v' mod n + return v.mod(q); + } + + public static byte[] hash(byte[] data) + { + Digest digest = new SHA256Digest(); + byte[] rlt = new byte[digest.getDigestSize()]; + digest.update(data, 0, data.length); + digest.doFinal(rlt, 0); + return rlt; + } + + public static byte[] hash(ECPoint point) + { + return hash(point.getEncoded(false)); // Use uncompressed encoding + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKey.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKey.java new file mode 100644 index 0000000000..02e6d7c54b --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKey.java @@ -0,0 +1,37 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECPoint; + +public class SAKKEPrivateKey + extends AsymmetricKeyParameter +{ + private final BigInteger b; // User's identity + private final ECPoint K; // Private key K_a + private final SAKKEPublicKey publicParams; + + public SAKKEPrivateKey(BigInteger b, ECPoint K, SAKKEPublicKey publicParams) + { + super(true); + this.b = b; + this.K = K; + this.publicParams = publicParams; + } + + // Getters + public ECPoint getK() + { + return K; + } + + public BigInteger getB() + { + return b; + } + + public SAKKEPublicKey getPublicParams() + { + return publicParams; + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java new file mode 100644 index 0000000000..81829f9caf --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java @@ -0,0 +1,52 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; + +public class SAKKEPublicKey + extends AsymmetricKeyParameter +{ + private final ECCurve curve; + private final ECPoint P; // Base point + private final ECPoint Z; // KMS Public Key: Z = [z]P + private final BigInteger q; // Subgroup order + private final int n; // SSV bit length + + public SAKKEPublicKey(ECCurve curve, ECPoint P, ECPoint Z, BigInteger q, int n) + { + super(false); + this.curve = curve; + this.P = P; + this.Z = Z; + this.q = q; + this.n = n; + } + + // Getters + public ECCurve getCurve() + { + return curve; + } + + public ECPoint getP() + { + return P; + } + + public ECPoint getZ() + { + return Z; + } + + public BigInteger getQ() + { + return q; + } + + public int getN() + { + return n; + } +} diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java new file mode 100644 index 0000000000..61ba76350d --- /dev/null +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -0,0 +1,81 @@ +package org.bouncycastle.crypto.kems.test; + +import java.math.BigInteger; + + +import org.bouncycastle.crypto.kems.SAKKEKEMSGenerator; +import org.bouncycastle.crypto.kems.SAKKEUtils; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; +import org.junit.Assert; + +public class SAKKEKEMSTest + extends SimpleTest +{ + public static void main(String[] args) + throws Exception + { + SAKKEKEMSTest test = new SAKKEKEMSTest(); + test.performTest(); + // Expected Rb values +// BigInteger expectedRbx = new BigInteger("44E8AD44AB8592A6A5A3DDCA5CF896C718043606A01D650DEF37A01F37C228C332FC317354E2C274D4DAF8AD001054C7... +// BigInteger expectedRby = new BigInteger("557E134AD85BB1D4B9CE4F8BE4B08A12BABF55B1D6F1D7A638019EA28E15AB1C9F76375FDD1210D4F4351B9A009486B7... +// +// // Instantiate SAKKE KEM Generator +// SAKKEKEMSGenerator kem = new SAKKEKEMSGenerator(); +// EncapsulatedData encapsulatedData = kem.encapsulate(SSV); +// +// // Validate results +// boolean testPassed = expectedRbx.equals(encapsulatedData.getRbx()) && expectedRby.equals(encapsulatedData.getRby()); + + //System.out.println("SAKKE KEM Test " + (testPassed ? "PASSED" : "FAILED")); + } + + private static byte[] hexStringToByteArray(String s) + { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) + { + data[i / 2] = (byte)((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i + 1), 16)); + } + return data; + } + + @Override + public String getName() + { + return null; + } + + @Override + public void performTest() + throws Exception + { +// BigInteger z = new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F"); +// BigInteger Zx = new BigInteger("5958EF1B1679BF099B3A030DF255AA6A23C1D8F143D4D23F753E69BD27A832F38CB4AD53DDEF" +// + "4260B0FE8BB45C4C1FF510EFFE300367A37B61F701D914AEF09724825FA0707D61A6DFF4FBD7273566CDDE352A0B04B7C16A78309BE" +// + "640697DE747613A5FC195E8B9F328852A579DB8F99B1D0034479EA9C5595F47C4B2F54FF2"); +// BigInteger Zy = new BigInteger("1508D37514DCF7A8E143A6058C09A6BF2C9858CA37C258065AE6BF7532BC8B5B63383866E075" +// + "3C5AC0E72709F8445F2E6178E065857E0EDA10F68206B63505ED87E534FB2831FF957FB7DC619DAE61301EEACC2FDA3680EA499925" + +// "8A833CEA8FC67C6D19487FB449059F26CC8AAB655AB58B7CC796E24E9A394095754F5F8BAE"); +// + byte[] b = Hex.decode("323031312D30320074656C3A2B34343737303039303031323300"); + + byte[] SSV = Hex.decode("123456789ABCDEF0123456789ABCDEF0"); + byte[] expectedR = Hex.decode("13EE3E1B8DAC5DB168B1CEB32F0566A4C273693F78BAFFA2A2EE6A686E6BD90F8206CCAB84E7F" + + "42ED39BD4FB131012ECCA2ECD2119414560C17CAB46B956A80F58A3302EB3E2C9A228FBA7ED34D8ACA2392DA1FFB0B17B2320AE09AAEDF" + + "D0235F6FE0EB65337A63F9CC97728B8E5AD0460FADE144369AA5B2166213247712096"); + + BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(SSV, b), BigInteger.valueOf(1024)); + + System.out.println("r:" +new String(Hex.encode(r.toByteArray()))); + + System.out.println("r:" +new String(Hex.encode(expectedR))); + + Assert.assertTrue(Arrays.areEqual(r.toByteArray(), expectedR)); + } +} From d53a732c815d8db128194ff41d0b9e3701877a01 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 31 Jan 2025 16:59:02 +1030 Subject: [PATCH 080/890] hashToIntegerRange is correct. TODO: fix the curve definition and pairing function --- .../crypto/kems/SAKKEKEMSGenerator.java | 140 ++++++++++++++++-- .../bouncycastle/crypto/kems/SAKKEUtils.java | 9 +- .../crypto/params/SAKKEPublicKey.java | 6 +- .../crypto/kems/test/SAKKEKEMSTest.java | 105 +++++++++++-- 4 files changed, 228 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index e44d681f12..1024ed7428 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -7,8 +7,10 @@ import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; +import org.bouncycastle.util.encoders.Hex; import java.math.BigInteger; import java.nio.ByteBuffer; @@ -17,12 +19,74 @@ public class SAKKEKEMSGenerator implements EncapsulatedSecretGenerator { - private final SAKKEPublicKey publicParams; + + // private static final BigInteger p = new BigInteger(Hex.decode("997ABB1F 0A563FDA 65C61198 DAD0657A\n" + +// " 416C0CE1 9CB48261 BE9AE358 B3E01A2E\n" + +// " F40AAB27 E2FC0F1B 228730D5 31A59CB0\n" + +// " E791B39F F7C88A19 356D27F4 A666A6D0\n" + +// " E26C6487 326B4CD4 512AC5CD 65681CE1\n" + +// " B6AFF4A8 31852A82 A7CF3C52 1C3C09AA\n" + +// " 9F94D6AF 56971F1F FCE3E823 89857DB0\n" + +// " 80C5DF10 AC7ACE87 666D807A FEA85FEB")); + private static final BigInteger p = new BigInteger( + "997ABB1F0A563FDA65C61198DAD0657A416C0CE19CB48261BE9AE358B3E01A2E" + + "F40AAB27E2FC0F1B228730D531A59CB0E791B39FF7C88A19356D27F4A666A6D0" + + "E26C6487326B4CD4512AC5CD65681CE1B6AFF4A831852A82A7CF3C521C3C09AA" + + "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 + ); + // private static final BigInteger q = new BigInteger(Hex.decode("265EAEC7 C2958FF6 99718466 36B4195E\n" + +// " 905B0338 672D2098 6FA6B8D6 2CF8068B\n" + +// " BD02AAC9 F8BF03C6 C8A1CC35 4C69672C\n" + +// " 39E46CE7 FDF22286 4D5B49FD 2999A9B4\n" + +// " 389B1921 CC9AD335 144AB173 595A0738\n" + +// " 6DABFD2A 0C614AA0 A9F3CF14 870F026A\n" + +// " A7E535AB D5A5C7C7 FF38FA08 E2615F6C\n" + +// " 203177C4 2B1EB3A1 D99B601E BFAA17FB")); + private static final BigInteger q = new BigInteger( + "265EAEC7C2958FF69971846636B4195E905B0338672D20986FA6B8D62CF8068B" + + "BD02AAC9F8BF03C6C8A1CC354C69672C39E46CE7FDF222864D5B49FD2999A9B4" + + "389B1921CC9AD335144AB173595A07386DABFD2A0C614AA0A9F3CF14870F026A" + + "A7E535ABD5A5C7C7FF38FA08E2615F6C203177C42B1EB3A1D99B601EBFAA17FB", 16 + ); +// private static final BigInteger a = BigInteger.valueOf(-3).mod(p); // y² = x³ - 3x +// private static final BigInteger b = BigInteger.ZERO; +// private static final ECCurve.Fp curve = new ECCurve.Fp( +// p, // Prime p +// BigInteger.valueOf(-3).mod(p), // a = -3 +// BigInteger.ZERO, // b = 0 +// q, // Order of the subgroup (from RFC 6509) +// BigInteger.ONE // Cofactor = 1 +// ); + + // Base point P = (Px, Py) + private static final BigInteger Px = new BigInteger( + "53FC09EE332C29AD0A7990053ED9B52A2B1A2FD60AEC69C698B2F204B6FF7CBF" + + "B5EDB6C0F6CE2308AB10DB9030B09E1043D5F22CDB9DFA55718BD9E7406CE890" + + "9760AF765DD5BCCB337C86548B72F2E1A702C3397A60DE74A7C1514DBA66910D" + + "D5CFB4CC80728D87EE9163A5B63F73EC80EC46C4967E0979880DC8ABEAE63895", 16 + ); + + private static final BigInteger Py = new BigInteger( + "0A8249063F6009F1F9F1F0533634A135D3E82016029906963D778D821E141178" + + "F5EA69F4654EC2B9E7F7F5E5F0DE55F66B598CCF9A140B2E416CFF0CA9E032B9" + + "70DAE117AD547C6CCAD696B5B7652FE0AC6F1E80164AA989492D979FC5A4D5F2" + + "13515AD7E9CB99A980BDAD5AD5BB4636ADB9B5706A67DCDE75573FD71BEF16D7", 16 + ); + + + BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + + " C6A87BD1 FB94C41E 27FABE65 8E015A87\n" + + " 371E9474 4C96FEDA 449AE956 3F8BC446\n" + + " CBFDA85D 5D00EF57 7072DA8F 541721BE\n" + + " EE0FAED1 828EAB90 B99DFB01 38C78433\n" + + " 55DF0460 B4A9FD74 B4F1A32B CAFA1FFA\n" + + " D682C033 A7942BCC E3720F20 B9B7B040\n" + + " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); + private final int n = 128; private final SecureRandom random; - public SAKKEKEMSGenerator(SAKKEPublicKey params, SecureRandom random) + public SAKKEKEMSGenerator(SecureRandom random) { - this.publicParams = params; this.random = random; } @@ -30,20 +94,67 @@ public SAKKEKEMSGenerator(SAKKEPublicKey params, SecureRandom random) public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey) { // 1. Generate random SSV in range [0, 2^n - 1] - BigInteger ssv = new BigInteger(publicParams.getN(), random); + BigInteger ssv = new BigInteger("123456789ABCDEF0123456789ABCDEF0", 16);//new BigInteger(n, random); // 2. Compute r = HashToIntegerRange(SSV || b, q) - BigInteger b = getRecipientId((SAKKEPublicKey)recipientKey); - BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), publicParams.getQ()); - + BigInteger b = new BigInteger("323031312D30320074656C3A2B34343737303039303031323300", 16); //getRecipientId((SAKKEPublicKey)recipientKey); + BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); + System.out.println(new String(Hex.encode(r.toByteArray()))); + ECCurve.Fp curve = new ECCurve.Fp( + p, // Prime p + BigInteger.valueOf(-3).mod(p), // a = -3 + BigInteger.ZERO, // , + g, // Order of the subgroup (from RFC 6509) + BigInteger.ONE // Cofactor = 1 + ); + ECPoint P = curve.createPoint(Px, Py); + ECPoint G = curve.createPoint( + new BigInteger(Hex.decode("53FC09EE 332C29AD 0A799005 3ED9B52A\n" + + " 2B1A2FD6 0AEC69C6 98B2F204 B6FF7CBF\n" + + " B5EDB6C0 F6CE2308 AB10DB90 30B09E10\n" + + " 43D5F22C DB9DFA55 718BD9E7 406CE890\n" + + " 9760AF76 5DD5BCCB 337C8654 8B72F2E1\n" + + " A702C339 7A60DE74 A7C1514D BA66910D\n" + + " D5CFB4CC 80728D87 EE9163A5 B63F73EC\n" + + " 80EC46C4 967E0979 880DC8AB EAE63895")), // Px + new BigInteger(Hex.decode("0A824906 3F6009F1 F9F1F053 3634A135\n" + + " D3E82016 02990696 3D778D82 1E141178\n" + + " F5EA69F4 654EC2B9 E7F7F5E5 F0DE55F6\n" + + " 6B598CCF 9A140B2E 416CFF0C A9E032B9\n" + + " 70DAE117 AD547C6C CAD696B5 B7652FE0\n" + + " AC6F1E80 164AA989 492D979F C5A4D5F2\n" + + " 13515AD7 E9CB99A9 80BDAD5A D5BB4636\n" + + " ADB9B570 6A67DCDE 75573FD7 1BEF16D7")) // Py + ); + ECPoint Z = curve.createPoint( + new BigInteger("5958EF1B1679BF099B3A030DF255AA6A" + + "23C1D8F143D4D23F753E69BD27A832F3" + + "8CB4AD53DDEF4260B0FE8BB45C4C1FF5" + + "10EFFE300367A37B61F701D914AEF097" + + "24825FA0707D61A6DFF4FBD7273566CD" + + "DE352A0B04B7C16A78309BE640697DE7" + + "47613A5FC195E8B9F328852A579DB8F9" + + "9B1D0034479EA9C5595F47C4B2F54FF2", 16), // Px + new BigInteger("1508D37514DCF7A8E143A6058C09A6BF" + + "2C9858CA37C258065AE6BF7532BC8B5B" + + "63383866E0753C5AC0E72709F8445F2E" + + "6178E065857E0EDA10F68206B63505ED" + + "87E534FB2831FF957FB7DC619DAE6130" + + "1EEACC2FDA3680EA4999258A833CEA8F" + + "C67C6D19487FB449059F26CC8AAB655A" + + "B58B7CC796E24E9A394095754F5F8BAE", 16) // Py + ); // 3. Compute R_(b,S) = [r]([b]P + Z_S) - ECPoint bP = publicParams.getP().multiply(b); // [b]P - ECPoint Z_S = publicParams.getZ(); // Z_S + ECPoint bP = P.multiply(b).normalize(); + ECPoint Z_S = Z;// P.multiply(ssv).normalize();;//.multiply(new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F", 16)); // Z_S ECPoint R_bS = bP.add(Z_S).multiply(r); // [r]([b]P + Z_S) + System.out.println("R_Bs x:" + new String(Hex.encode(R_bS.getXCoord().toBigInteger().toByteArray()))); + System.out.println("R_Bs y:" + new String(Hex.encode(R_bS.getYCoord().toBigInteger().toByteArray()))); + // 4. Compute H = SSV XOR HashToIntegerRange( g^r, 2^n ) - BigInteger g_r = pairing(R_bS, publicParams.getP(), publicParams.getQ(), publicParams.getP().getCurve().getField().getCharacteristic()); - BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(publicParams.getN())); // 2^n + BigInteger g_r = pairing(R_bS, G, p, q); + BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(n)); // 2^n BigInteger H = ssv.xor(mask); @@ -51,7 +162,7 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip byte[] encapsulated = encodeData(R_bS, H); return new SecretWithEncapsulationImpl( - BigIntegers.asUnsignedByteArray(publicParams.getN() / 8, ssv), // Output SSV as key material + BigIntegers.asUnsignedByteArray(n / 8, ssv), // Output SSV as key material encapsulated ); } @@ -123,7 +234,7 @@ public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger private static BigInteger computeFpRepresentative(ECFieldElement t, ECCurve curve) { // Characteristic of F_p - BigInteger p = ((ECCurve.Fp) curve).getQ(); + BigInteger p = ((ECCurve.Fp)curve).getQ(); // Assume t = a + i * b in F_p² → extract a, b ECFieldElement a = t; // In F_p², a is the real part @@ -133,7 +244,8 @@ private static BigInteger computeFpRepresentative(ECFieldElement t, ECCurve curv return b.toBigInteger().multiply(a.toBigInteger().modInverse(p)).mod(p); } - public static byte[] encodeData(ECPoint R_bS, BigInteger H) { + public static byte[] encodeData(ECPoint R_bS, BigInteger H) + { // 1. Serialize EC Point (use compressed format for efficiency) byte[] R_bS_bytes = R_bS.getEncoded(true); diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java index bc3a9a90f2..05938e2ac5 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java @@ -5,7 +5,6 @@ import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.math.ec.ECPoint; -import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; public class SAKKEUtils @@ -30,22 +29,22 @@ public static BigInteger hashToIntegerRange(byte[] input, BigInteger q) BigInteger v = BigInteger.ZERO; // Step 4: Compute h_i and v_i - for (int i = 1; i <= l; i++) + for (int i = 0; i <= l; i++) { // h_i = hashfn(h_{i-1}) digest.update(h, 0, h.length); digest.doFinal(h, 0); - System.out.println("h_"+i+":" +new String(Hex.encode(h))); + //System.out.println("h_"+i+":" +new String(Hex.encode(h))); // v_i = hashfn(h_i || A) digest.update(h, 0, h.length); digest.update(A, 0, A.length); byte[] v_i = new byte[digest.getDigestSize()]; digest.doFinal(v_i, 0); - System.out.println("v_"+i+":" +new String(Hex.encode(v_i))); + //System.out.println("v_"+i+":" +new String(Hex.encode(v_i))); // Append v_i to v' v = v.shiftLeft(v_i.length * 8).add(new BigInteger(1, v_i)); } - System.out.println("v:" +new String(Hex.encode(v.toByteArray()))); + //System.out.println("v:" +new String(Hex.encode(v.toByteArray()))); // Step 6: v = v' mod n return v.mod(q); } diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java index 81829f9caf..c992dbb70b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java @@ -4,20 +4,20 @@ import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve; public class SAKKEPublicKey extends AsymmetricKeyParameter { - private final ECCurve curve; + private final ECCurve curve = new SecP256R1Curve(); private final ECPoint P; // Base point private final ECPoint Z; // KMS Public Key: Z = [z]P private final BigInteger q; // Subgroup order private final int n; // SSV bit length - public SAKKEPublicKey(ECCurve curve, ECPoint P, ECPoint Z, BigInteger q, int n) + public SAKKEPublicKey(ECPoint P, ECPoint Z, BigInteger q, int n) { super(false); - this.curve = curve; this.P = P; this.Z = Z; this.q = q; diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index 61ba76350d..4a40e99c5c 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -1,13 +1,17 @@ package org.bouncycastle.crypto.kems.test; import java.math.BigInteger; +import java.security.SecureRandom; import org.bouncycastle.crypto.kems.SAKKEKEMSGenerator; import org.bouncycastle.crypto.kems.SAKKEUtils; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.FixedSecureRandom; import org.bouncycastle.util.test.SimpleTest; import org.junit.Assert; @@ -55,13 +59,29 @@ public String getName() public void performTest() throws Exception { -// BigInteger z = new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F"); -// BigInteger Zx = new BigInteger("5958EF1B1679BF099B3A030DF255AA6A23C1D8F143D4D23F753E69BD27A832F38CB4AD53DDEF" -// + "4260B0FE8BB45C4C1FF510EFFE300367A37B61F701D914AEF09724825FA0707D61A6DFF4FBD7273566CDDE352A0B04B7C16A78309BE" -// + "640697DE747613A5FC195E8B9F328852A579DB8F99B1D0034479EA9C5595F47C4B2F54FF2"); -// BigInteger Zy = new BigInteger("1508D37514DCF7A8E143A6058C09A6BF2C9858CA37C258065AE6BF7532BC8B5B63383866E075" -// + "3C5AC0E72709F8445F2E6178E065857E0EDA10F68206B63505ED87E534FB2831FF957FB7DC619DAE61301EEACC2FDA3680EA499925" + -// "8A833CEA8FC67C6D19487FB449059F26CC8AAB655AB58B7CC796E24E9A394095754F5F8BAE"); + BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068" + + " C6A87BD1 FB94C41E 27FABE65 8E015A87" + + " 371E9474 4C96FEDA 449AE956 3F8BC446" + + " CBFDA85D 5D00EF57 7072DA8F 541721BE" + + " EE0FAED1 828EAB90 B99DFB01 38C78433" + + " 55DF0460 B4A9FD74 B4F1A32B CAFA1FFA" + + " D682C033 A7942BCC E3720F20 B9B7B040" + + " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); + BigInteger z = new BigInteger(Hex.decode("AFF429D35F84B110D094803B3595A6E2998BC99F")); + BigInteger Zx = new BigInteger(Hex.decode("5958EF1B1679BF099B3A030DF255AA6A23C1D8F143D4D23F753E69BD27A832F38CB4AD53DDEF" + + "4260B0FE8BB45C4C1FF510EFFE300367A37B61F701D914AEF09724825FA0707D61A6DFF4FBD7273566CDDE352A0B04B7C16A78309BE" + + "640697DE747613A5FC195E8B9F328852A579DB8F99B1D0034479EA9C5595F47C4B2F54FF2")); + BigInteger Zy = new BigInteger(Hex.decode("1508D37514DCF7A8E143A6058C09A6BF2C9858CA37C258065AE6BF7532BC8B5B63383866E075" + + "3C5AC0E72709F8445F2E6178E065857E0EDA10F68206B63505ED87E534FB2831FF957FB7DC619DAE61301EEACC2FDA3680EA499925" + + "8A833CEA8FC67C6D19487FB449059F26CC8AAB655AB58B7CC796E24E9A394095754F5F8BAE")); + BigInteger q = new BigInteger(Hex.decode("265EAEC7 C2958FF6 99718466 36B4195E" + + " 905B0338 672D2098 6FA6B8D6 2CF8068B" + + " BD02AAC9 F8BF03C6 C8A1CC35 4C69672C" + + " 39E46CE7 FDF22286 4D5B49FD 2999A9B4" + + " 389B1921 CC9AD335 144AB173 595A0738" + + " 6DABFD2A 0C614AA0 A9F3CF14 870F026A" + + " A7E535AB D5A5C7C7 FF38FA08 E2615F6C" + + " 203177C4 2B1EB3A1 D99B601E BFAA17FB")); // byte[] b = Hex.decode("323031312D30320074656C3A2B34343737303039303031323300"); @@ -70,12 +90,77 @@ public void performTest() + "42ED39BD4FB131012ECCA2ECD2119414560C17CAB46B956A80F58A3302EB3E2C9A228FBA7ED34D8ACA2392DA1FFB0B17B2320AE09AAEDF" + "D0235F6FE0EB65337A63F9CC97728B8E5AD0460FADE144369AA5B2166213247712096"); - BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(SSV, b), BigInteger.valueOf(1024)); + BigInteger kbx = new BigInteger("93AF67E5007BA6E6A80DA793DA300FA4" + + "B52D0A74E25E6E7B2B3D6EE9D18A9B5C" + + "5023597BD82D8062D34019563BA1D25C" + + "0DC56B7B979D74AA50F29FBF11CC2C93" + + "F5DFCA615E609279F6175CEADB00B58C" + + "6BEE1E7A2A47C4F0C456F05259A6FA94" + + "A634A40DAE1DF593D4FECF688D5FC678" + + "BE7EFC6DF3D6835325B83B2C6E69036B", 16); - System.out.println("r:" +new String(Hex.encode(r.toByteArray()))); + BigInteger kby = new BigInteger("155F0A27241094B04BFB0BDFAC6C670A" + + "65C325D39A069F03659D44CA27D3BE8D" + + "F311172B554160181CBE94A2A783320C" + + "ED590BC42644702CF371271E496BF20F" + + "588B78A1BC01ECBB6559934BDD2FB65D" + + "2884318A33D1A42ADF5E33CC5800280B" + + "28356497F87135BAB9612A1726042440" + + "9AC15FEE996B744C332151235DECB0F5", 16); + BigInteger w = new BigInteger(Hex.decode("7D2A8438 E6291C64 9B6579EB 3B79EAE9" + + "48B1DE9E 5F7D1F40 70A08F8D B6B3C515" + + "6F2201AF FBB5CB9D 82AA3EC0 D0398B89" + + "ABC78A13 A760C0BF 3F77E63D 0DF3F1A3" + + "41A41B88 11DF197F D6CD0F00 3125606F" + + "4F109F40 0F7292A1 0D255E3C 0EBCCB42" + + "53FB182C 68F09CF6 CD9C4A53 DA6C74AD" + + "007AF36B 8BCA979D 5895E282 F483FCD6")); + BigInteger Rbx = new BigInteger(Hex.decode("44E8AD44 AB8592A6 A5A3DDCA 5CF896C7" + + "18043606 A01D650D EF37A01F 37C228C3" + + "32FC3173 54E2C274 D4DAF8AD 001054C7" + + "6CE57971 C6F4486D 57230432 61C506EB" + + "F5BE438F 53DE04F0 67C776E0 DD3B71A6" + + "29013328 3725A532 F21AF145 126DC1D7" + + "77ECC27B E50835BD 28098B8A 73D9F801" + + "D893793A 41FF5C49 B87E79F2 BE4D56CE")); + BigInteger Rby = new BigInteger(Hex.decode("557E134A D85BB1D4 B9CE4F8B E4B08A12" + + "BABF55B1 D6F1D7A6 38019EA2 8E15AB1C" + + "9F76375F DD1210D4 F4351B9A 009486B7" + + "F3ED46C9 65DED2D8 0DADE4F3 8C6721D5" + + "2C3AD103 A10EBD29 59248B4E F006836B" + + "F097448E 6107C9ED EE9FB704 823DF199" + + "F832C905 AE45F8A2 47A072D8 EF729EAB" + + "C5E27574 B07739B3 4BE74A53 2F747B86")); + BigInteger p = new BigInteger( + "997ABB1F0A563FDA65C61198DAD0657A416C0CE19CB48261BE9AE358B3E01A2E" + + "F40AAB27E2FC0F1B228730D531A59CB0E791B39FF7C88A19356D27F4A666A6D0" + + "E26C6487326B4CD4512AC5CD65681CE1B6AFF4A831852A82A7CF3C521C3C09AA" + + "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 + ); - System.out.println("r:" +new String(Hex.encode(expectedR))); + ECCurve.Fp curve = new ECCurve.Fp( + p, // Prime p + BigInteger.valueOf(-3).mod(p), // a = -3 + BigInteger.ZERO, // , + q,// Order of the subgroup (from RFC 6509) + BigInteger.ONE // Cofactor = 1 + ); + + ECPoint K_bS = curve.createPoint(kbx, kby); + System.out.println("K_bS x:" + new String(Hex.encode(K_bS.getXCoord().toBigInteger().toByteArray()))); + System.out.println("K_bS y:" + new String(Hex.encode(K_bS.getYCoord().toBigInteger().toByteArray()))); + ECPoint R_bs = curve.createPoint(Rbx, Rby); + SAKKEKEMSGenerator.pairing(K_bS, R_bs, p, q); + + BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(SSV, b), q); + + System.out.println("r:" + new String(Hex.encode(r.toByteArray()))); + + System.out.println("r:" + new String(Hex.encode(expectedR))); Assert.assertTrue(Arrays.areEqual(r.toByteArray(), expectedR)); + SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); + generator.generateEncapsulated(null); + } } From 79e72d376ca4ba166b3fc093096fc4305d234cbd Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 31 Jan 2025 21:49:58 +1030 Subject: [PATCH 081/890] Fix step 3. --- .../org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java | 4 ++-- .../org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index 1024ed7428..e8f53b2cd7 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -144,10 +144,10 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip "C67C6D19487FB449059F26CC8AAB655A" + "B58B7CC796E24E9A394095754F5F8BAE", 16) // Py ); + BigInteger z = new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F", 16); // 3. Compute R_(b,S) = [r]([b]P + Z_S) ECPoint bP = P.multiply(b).normalize(); - ECPoint Z_S = Z;// P.multiply(ssv).normalize();;//.multiply(new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F", 16)); // Z_S - ECPoint R_bS = bP.add(Z_S).multiply(r); // [r]([b]P + Z_S) + ECPoint R_bS = bP.add(Z).multiply(r).normalize(); // [r]([b]P + Z_S) System.out.println("R_Bs x:" + new String(Hex.encode(R_bS.getXCoord().toBigInteger().toByteArray()))); System.out.println("R_Bs y:" + new String(Hex.encode(R_bS.getYCoord().toBigInteger().toByteArray()))); diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index 4a40e99c5c..cbe940a064 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -142,10 +142,11 @@ public void performTest() p, // Prime p BigInteger.valueOf(-3).mod(p), // a = -3 BigInteger.ZERO, // , - q,// Order of the subgroup (from RFC 6509) + g,// Order of the subgroup (from RFC 6509) BigInteger.ONE // Cofactor = 1 ); - + SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); + generator.generateEncapsulated(null); ECPoint K_bS = curve.createPoint(kbx, kby); System.out.println("K_bS x:" + new String(Hex.encode(K_bS.getXCoord().toBigInteger().toByteArray()))); System.out.println("K_bS y:" + new String(Hex.encode(K_bS.getYCoord().toBigInteger().toByteArray()))); @@ -159,8 +160,8 @@ public void performTest() System.out.println("r:" + new String(Hex.encode(expectedR))); Assert.assertTrue(Arrays.areEqual(r.toByteArray(), expectedR)); - SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); - generator.generateEncapsulated(null); +// SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); +// generator.generateEncapsulated(null); } } From 5174843c46264863a09bd6ea69be037b32995f87 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sat, 1 Feb 2025 09:20:44 +1100 Subject: [PATCH 082/890] added exception check on UTF8 parsing to prevent escaping exceptions. --- .../org/bouncycastle/bcpg/ArmoredInputStream.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java index 817829d128..190e3a82e4 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java @@ -268,7 +268,17 @@ private boolean parseHeaders() } if (c == '\r' || (last != '\r' && c == '\n')) { - String line = Strings.fromUTF8ByteArray(buf.toByteArray()); + String line; + + try + { + line = Strings.fromUTF8ByteArray(buf.toByteArray()); + } + catch (Exception e) + { + throw new ArmoredInputException(e.getMessage()); + } + if (line.trim().length() == 0) { break; From 8ee89a48ea2866cf90a0d1854369a70bbc8e0ce6 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sat, 1 Feb 2025 09:39:10 +1100 Subject: [PATCH 083/890] modified to use JCA. --- .../JcePBEKeyEncryptionMethodGenerator.java | 109 +++++++++++++----- 1 file changed, 82 insertions(+), 27 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java index 32ad340394..eb51f115e1 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java @@ -1,5 +1,6 @@ package org.bouncycastle.openpgp.operator.jcajce; +import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Provider; @@ -12,12 +13,11 @@ import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; +import org.bouncycastle.bcpg.AEADAlgorithmTags; import org.bouncycastle.bcpg.S2K; +import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; import org.bouncycastle.bcpg.SymmetricKeyUtils; -import org.bouncycastle.crypto.InvalidCipherTextException; -import org.bouncycastle.crypto.modes.AEADCipher; -import org.bouncycastle.crypto.params.AEADParameters; -import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.jcajce.spec.AEADParameterSpec; import org.bouncycastle.jcajce.util.DefaultJcaJceHelper; import org.bouncycastle.jcajce.util.NamedJcaJceHelper; import org.bouncycastle.jcajce.util.ProviderJcaJceHelper; @@ -25,7 +25,6 @@ import org.bouncycastle.openpgp.PGPUtil; import org.bouncycastle.openpgp.operator.PBEKeyEncryptionMethodGenerator; import org.bouncycastle.openpgp.operator.PGPDigestCalculator; -import org.bouncycastle.openpgp.operator.bc.BcAEADUtil; /** * JCE based generator for password based encryption (PBE) data protection methods. @@ -152,26 +151,82 @@ protected byte[] encryptSessionInfo(int encAlgorithm, byte[] key, byte[] session } protected byte[] generateV6KEK(int kekAlgorithm, byte[] ikm, byte[] info) - { - return JceAEADUtil.generateHKDFBytes(ikm, null, info, SymmetricKeyUtils.getKeyLengthInOctets(kekAlgorithm)); - } - - protected byte[] getEskAndTag(int kekAlgorithm, int aeadAlgorithm, byte[] sessionKey, byte[] key, byte[] iv, byte[] info) - throws PGPException - { - AEADCipher aeadCipher = BcAEADUtil.createAEADCipher(kekAlgorithm, aeadAlgorithm); - aeadCipher.init(true, new AEADParameters(new KeyParameter(key), 128, iv, info)); - int outLen = aeadCipher.getOutputSize(sessionKey.length); - byte[] eskAndTag = new byte[outLen]; - int len = aeadCipher.processBytes(sessionKey, 0, sessionKey.length, eskAndTag, 0); - try - { - len += aeadCipher.doFinal(eskAndTag, len); - } - catch (InvalidCipherTextException e) - { - throw new PGPException("cannot encrypt session info", e); - } - return eskAndTag; - } + { + return JceAEADUtil.generateHKDFBytes(ikm, null, info, SymmetricKeyUtils.getKeyLengthInOctets(kekAlgorithm)); + } + + protected byte[] getEskAndTag(int kekAlgorithm, int aeadAlgorithm, byte[] sessionKey, byte[] key, byte[] iv, byte[] info) + throws PGPException + { + String algorithm = getBaseAEADAlgorithm(kekAlgorithm); + + Cipher aeadCipher = createAEADCipher(algorithm, aeadAlgorithm); + + try + { + aeadCipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, algorithm), new AEADParameterSpec(iv, 128, info)); + int outLen = aeadCipher.getOutputSize(sessionKey.length); + byte[] eskAndTag = new byte[outLen]; + + int len = aeadCipher.update(sessionKey, 0, sessionKey.length, eskAndTag, 0); + + len += aeadCipher.doFinal(eskAndTag, len); + + if (len < eskAndTag.length) + { + byte[] rv = new byte[len]; + System.arraycopy(eskAndTag, 0, rv, 0, len); + return rv; + } + + return eskAndTag; + } + catch (GeneralSecurityException e) + { + throw new PGPException("cannot encrypt session info", e); + } + + } + + public static String getBaseAEADAlgorithm(int encAlgorithm) + throws PGPException + { + if (encAlgorithm == SymmetricKeyAlgorithmTags.AES_128 + || encAlgorithm == SymmetricKeyAlgorithmTags.AES_192 + || encAlgorithm == SymmetricKeyAlgorithmTags.AES_256) + { + return "AES"; + } + else if (encAlgorithm == SymmetricKeyAlgorithmTags.CAMELLIA_128 + || encAlgorithm == SymmetricKeyAlgorithmTags.CAMELLIA_192 + || encAlgorithm == SymmetricKeyAlgorithmTags.CAMELLIA_256) + { + return "Camellia"; + } + throw new PGPException("AEAD only supported for AES and Camellia based algorithms"); + } + + public static Cipher createAEADCipher(String algorithm, int aeadAlgorithm) + throws PGPException + { + // Block Cipher must work on 16 byte blocks + try + { + switch (aeadAlgorithm) + { + case AEADAlgorithmTags.EAX: + return Cipher.getInstance(algorithm + "/EAX/NoPadding", "BC"); + case AEADAlgorithmTags.OCB: + return Cipher.getInstance(algorithm + "/OCB/NoPadding", "BC"); + case AEADAlgorithmTags.GCM: + return Cipher.getInstance(algorithm + "/GCM/NoPadding", "BC"); + default: + throw new PGPException("unrecognised AEAD algorithm: " + aeadAlgorithm); + } + } + catch (GeneralSecurityException e) + { + throw new PGPException("unrecognised AEAD algorithm: " + e.getMessage(), e); + } + } } From 502fddd48f77f3e3b6b188103621dd00f063d7da Mon Sep 17 00:00:00 2001 From: David Hook Date: Sat, 1 Feb 2025 09:41:05 +1100 Subject: [PATCH 084/890] changed methods to private --- .../operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java index eb51f115e1..0c4f2b9388 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java @@ -188,7 +188,7 @@ protected byte[] getEskAndTag(int kekAlgorithm, int aeadAlgorithm, byte[] sessio } - public static String getBaseAEADAlgorithm(int encAlgorithm) + private static String getBaseAEADAlgorithm(int encAlgorithm) throws PGPException { if (encAlgorithm == SymmetricKeyAlgorithmTags.AES_128 @@ -206,7 +206,7 @@ else if (encAlgorithm == SymmetricKeyAlgorithmTags.CAMELLIA_128 throw new PGPException("AEAD only supported for AES and Camellia based algorithms"); } - public static Cipher createAEADCipher(String algorithm, int aeadAlgorithm) + private static Cipher createAEADCipher(String algorithm, int aeadAlgorithm) throws PGPException { // Block Cipher must work on 16 byte blocks From 2a4ec8a420ead915b7971bc283279b81693346f1 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sat, 1 Feb 2025 16:41:15 +1100 Subject: [PATCH 085/890] added use of local helper. --- .../JcePBEKeyEncryptionMethodGenerator.java | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java index 0c4f2b9388..c6fcaebe06 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java @@ -206,27 +206,20 @@ else if (encAlgorithm == SymmetricKeyAlgorithmTags.CAMELLIA_128 throw new PGPException("AEAD only supported for AES and Camellia based algorithms"); } - private static Cipher createAEADCipher(String algorithm, int aeadAlgorithm) + private Cipher createAEADCipher(String algorithm, int aeadAlgorithm) throws PGPException { // Block Cipher must work on 16 byte blocks - try - { - switch (aeadAlgorithm) - { - case AEADAlgorithmTags.EAX: - return Cipher.getInstance(algorithm + "/EAX/NoPadding", "BC"); - case AEADAlgorithmTags.OCB: - return Cipher.getInstance(algorithm + "/OCB/NoPadding", "BC"); - case AEADAlgorithmTags.GCM: - return Cipher.getInstance(algorithm + "/GCM/NoPadding", "BC"); - default: - throw new PGPException("unrecognised AEAD algorithm: " + aeadAlgorithm); - } - } - catch (GeneralSecurityException e) + switch (aeadAlgorithm) { - throw new PGPException("unrecognised AEAD algorithm: " + e.getMessage(), e); + case AEADAlgorithmTags.EAX: + return helper.createCipher(algorithm + "/EAX/NoPadding"); + case AEADAlgorithmTags.OCB: + return helper.createCipher(algorithm + "/OCB/NoPadding"); + case AEADAlgorithmTags.GCM: + return helper.createCipher(algorithm + "/GCM/NoPadding"); + default: + throw new PGPException("unrecognised AEAD algorithm: " + aeadAlgorithm); } } } From 0ea89a4388de4f18a2cd3a1801d5bdb2a954644d Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 3 Feb 2025 15:15:30 +1100 Subject: [PATCH 086/890] added encoding preservation for ML-DSA, ML-KEM, added property for triggering seed only PrivateKeyInfo generation, relates to github #1969. --- .../crypto/util/PrivateKeyInfoFactory.java | 46 +++--- .../asymmetric/mldsa/BCMLDSAPrivateKey.java | 1 + .../asymmetric/mlkem/BCMLKEMPrivateKey.java | 7 + .../pqc/jcajce/provider/test/MLDSATest.java | 148 +++++++++++++++++- .../pqc/jcajce/provider/test/MLKEMTest.java | 76 +++++++++ 5 files changed, 251 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java index b7f92bb668..941425df3f 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java @@ -53,6 +53,7 @@ import org.bouncycastle.pqc.legacy.crypto.mceliece.McElieceCCA2PrivateKeyParameters; import org.bouncycastle.pqc.legacy.crypto.qtesla.QTESLAPrivateKeyParameters; import org.bouncycastle.util.Pack; +import org.bouncycastle.util.Properties; /** * Factory to create ASN.1 private key info objects from lightweight private keys. @@ -247,18 +248,17 @@ else if (privateKey instanceof MLKEMPrivateKeyParameters) MLKEMPrivateKeyParameters params = (MLKEMPrivateKeyParameters)privateKey; AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.mlkemOidLookup(params.getParameters())); - - return new PrivateKeyInfo(algorithmIdentifier, getBasicPQCEncoding(params.getSeed(), params.getEncoded()), attributes); -// byte[] seed = params.getSeed(); -// -// if (seed == null) -// { -// return new PrivateKeyInfo(algorithmIdentifier, params.getEncoded(), attributes); -// } -// else -// { -// return new PrivateKeyInfo(algorithmIdentifier, seed, attributes); -// } + + byte[] seed = params.getSeed(); + if (Properties.isOverrideSet("org.bouncycastle.mlkem.seedOnly")) + { + if (seed == null) // very difficult to imagine, but... + { + throw new IOException("no seed available"); + } + return new PrivateKeyInfo(algorithmIdentifier, seed, attributes); + } + return new PrivateKeyInfo(algorithmIdentifier, getBasicPQCEncoding(seed, params.getEncoded()), attributes); } else if (privateKey instanceof NTRULPRimePrivateKeyParameters) { @@ -297,20 +297,16 @@ else if (privateKey instanceof MLDSAPrivateKeyParameters) AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.mldsaOidLookup(params.getParameters())); + byte[] seed = params.getSeed(); + if (Properties.isOverrideSet("org.bouncycastle.mldsa.seedOnly")) + { + if (seed == null) // very difficult to imagine, but... + { + throw new IOException("no seed available"); + } + return new PrivateKeyInfo(algorithmIdentifier, seed, attributes); + } return new PrivateKeyInfo(algorithmIdentifier, getBasicPQCEncoding(params.getSeed(), params.getEncoded()), attributes); -// byte[] seed = params.getSeed(); -// if (seed == null) -// { -// MLDSAPublicKeyParameters pubParams = params.getPublicKeyParameters(); -// -// return new PrivateKeyInfo(algorithmIdentifier, params.getEncoded(), attributes, pubParams.getEncoded()); -// } -// else -// { -// MLDSAPublicKeyParameters pubParams = params.getPublicKeyParameters(); -// -// return new PrivateKeyInfo(algorithmIdentifier, seed, attributes, pubParams.getEncoded()); -// } } else if (privateKey instanceof DilithiumPrivateKeyParameters) { diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.java index 4cc33e8b14..42ba9ccb23 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.java @@ -44,6 +44,7 @@ public BCMLDSAPrivateKey(PrivateKeyInfo keyInfo) private void init(PrivateKeyInfo keyInfo) throws IOException { + this.encoding = keyInfo.getEncoded(); init((MLDSAPrivateKeyParameters)PrivateKeyFactory.createKey(keyInfo), keyInfo.getAttributes()); } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java index 44fcfcd22c..eb3f360400 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java @@ -25,6 +25,7 @@ public class BCMLKEMPrivateKey private transient MLKEMPrivateKeyParameters params; private transient String algorithm; private transient ASN1Set attributes; + private transient byte[] priorEncoding; public BCMLKEMPrivateKey( MLKEMPrivateKeyParameters params) @@ -43,6 +44,7 @@ private void init(PrivateKeyInfo keyInfo) throws IOException { this.attributes = keyInfo.getAttributes(); + this.priorEncoding = keyInfo.getPrivateKey().getOctets(); this.params = (MLKEMPrivateKeyParameters)PrivateKeyFactory.createKey(keyInfo); this.algorithm = Strings.toUpperCase(MLKEMParameterSpec.fromName(params.getParameters().getName()).getName()); } @@ -89,6 +91,11 @@ public byte[] getEncoded() { PrivateKeyInfo pki = PrivateKeyInfoFactory.createPrivateKeyInfo(params, attributes); + if (priorEncoding != null) + { + pki = new PrivateKeyInfo(pki.getPrivateKeyAlgorithm(), priorEncoding, attributes); + } + return pki.getEncoded(); } catch (IOException e) diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java index 9b93c58007..9ca25d2c74 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java @@ -10,6 +10,7 @@ import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.PrivateKey; import java.security.SecureRandom; import java.security.Security; import java.security.Signature; @@ -21,6 +22,7 @@ import org.bouncycastle.asn1.ASN1BitString; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; @@ -33,7 +35,9 @@ import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; +import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.FixedSecureRandom; /** * MLDSA now in BC provider @@ -84,7 +88,7 @@ public void testParametersAndParamSpecs() assertEquals(names[i], MLDSAParameterSpec.fromName(names[i]).getName()); } } - + public void testKeyFactory() throws Exception { @@ -172,6 +176,73 @@ public void testPrivateKeyRecovery() assertEquals(((MLDSAPrivateKey)privKey).getPublicKey(), ((MLDSAPrivateKey)privKey2).getPublicKey()); } + public void testDefaultPrivateKeyEncoding() + throws Exception + { + KeyPairGenerator kpGen44 = KeyPairGenerator.getInstance("ML-DSA-44"); + + byte[] seed = Hex.decode("000102030405060708090a0b0c0d0e0f" + "100102030405060708090a0b0c0d0e0f"); + + kpGen44.initialize(MLDSAParameterSpec.ml_dsa_44, new FixedSecureRandom(seed)); + KeyPair kp44 = kpGen44.generateKeyPair(); + + PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp44.getPrivate().getEncoded()); + ASN1OctetString seq = ASN1OctetString.getInstance(ASN1Sequence.getInstance(privInfo.getPrivateKey().getOctets()).getObjectAt(0)); + + assertTrue(Arrays.areEqual(seq.getOctets(), seed)); + + ASN1OctetString privData = ASN1OctetString.getInstance((ASN1TaggedObject)ASN1Sequence.getInstance(privInfo.getPrivateKey().getOctets()).getObjectAt(1), false); + + assertTrue(Arrays.areEqual(privData.getOctets(), ((MLDSAPrivateKey)kp44.getPrivate()).getPrivateData())); + } + + + public void testSeedPrivateKeyEncoding() + throws Exception + { + KeyPairGenerator kpGen44 = KeyPairGenerator.getInstance("ML-DSA-44"); + + byte[] seed = Hex.decode("000102030405060708090a0b0c0d0e0f" + "100102030405060708090a0b0c0d0e0f"); + + kpGen44.initialize(MLDSAParameterSpec.ml_dsa_44, new FixedSecureRandom(seed)); + KeyPair kp44 = kpGen44.generateKeyPair(); + + Security.setProperty("org.bouncycastle.mldsa.seedOnly", "true"); + + PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp44.getPrivate().getEncoded()); + + Security.setProperty("org.bouncycastle.mldsa.seedOnly", "false"); + ASN1OctetString k = privInfo.getPrivateKey(); + + assertTrue(Arrays.areEqual(k.getOctets(), seed)); + } + + public void testPrivateKeyRecoding() + throws Exception + { + byte[] mldsa44_sequence = Base64.decode("MIIKPgIBADALBglghkgBZQMEAxEEggoqMIIKJgQgAAECAwQFBgcICQoLDA0ODxABAgMEBQYHCAkKCwwNDg+BggoAw/FofbPea45APAHeQqNeZL4KcvMHr8V/inNsUbmYlE0vTZbckQVhHq8sQi2F3yC7B1/aLXIta1rImyPmzsYITzPH4Iz+3/ysH0n3TDx9HFKMp5WwuOX7ZRnNqS/RaiaYltSR0LBwyi5uPgGqw1Cb/mAsJkoJuGx31QGfc1KTMSQYAjEiqU0QJjHAlAyBtiRSxkGZIFFYGAYIIEXSSC4YE2rAooADOQoIQmwbKARSQiWBRExbOIkEQEVJIEQcEUCTxhEjQCahpJFLwDEIhIFamETJqDEbEWrKyCwBKWnCQECcNgYDtAQMMWwMQTFjwCiisEUTQEGiFAoRQibaQmmaokgjFUYJREmMBHAClSABlyRQFAlQECCgRiSCJIEQEwoACSRElg0QEjBSFAgIOQLIuDCiRHHcFoIZp0zkCDCbBIkbFYnYFAQMQ1ELGSCYICDSBGhhBmAcoS0JQ0HBEGGUiIlTIkYaGGpJMJHLtCDAEDKBpgXUAgoSxQAjQm0EuUQjCBDcIkbKwkUUom0clomLKHETNpBioEBTpEDLEkocIzJiQI0JBAIaMQHaRCQbkW0KSHHgBGADoi0AFHHYFA3hgg0ayU1iloyIAEmQBJLLKC4JgQADhiGbmIiUggBcwCWDIiwByWCSEExaRC0ZkgALM0kTlZCJAnHUNEaYlJEEMU6JtAUAo0gRJ23kokkSxoREsjCSomBUyCFEAEpUJmkDkWVLloSSQkpjkm0AEQqZxFEIE4gjGCGTpkEhsEAUEyIZqEAjJg1BRlHBmCEJg4AZSGobNE5gMIWRRmTMtGBcgCVhpo2bwCDjFFLbAkJksHCZRAEKMVBjQi1jhkQQpg3EgmybBjDDqC3hRIBMoGHUpiHIMilQEgpQMA7QEClMBolUNihTAmDQAICTEm6IqEATCCEMOCoEEYwKN4maRDAjkSEJRmnIohALgAADBCoBhjFgJiIYkWmTQiHkKA4cImYLk3CSBIKUEA4gE2ibMkRjkkUKACyMRkYRoWQUl0wbmW2QBkkJR4AgR2iCJAYUMgpLKGwgKVHiBE4iBRALNYwbFwwUAGpQwAwDQSwSwQAQAi7REgrSIAlbkHEJyZAASWDMMm4YlSHiwFFIFkrbFI6IJJERJUhLlkXRyCAAk5GDoC2JyJGYQgkRRGkTJE2jRCzUGFEiF1DAghECNIJIgHAgQWlvzZh7vICP92WCMX3B8zbvb7tgGJ1FERU17RMAxgnav4gEW/oe9DFzrwvEKF4Ku25tc0FQgdmnvLQFYwpTQuDjTxsug4GZViD6JP0GvZpbvfRhSWIjeFVdQcspJ26wR3IaM4M8yRhim1xEfUf7HcopRHQ6bzJ0B7rhE60A1mouwYG8ekzSxQ4WPtqecHdT57vYaEjOqjM0g3vUKa8ybDzUUVJDXx/6pcww596LIBNHygVaBGdRyAhNYxzSg0Z9BuxN295I3WJLU13+yYsdgvMq/qoj0LVZPevCjMBLnGnBAGsXxpYmTZbiyE6v5vQO3xWB6So6vX+G82gl0T33gZEw0B0stRf76XAcHHNkQl5nz1ZdkCS2QGh8Or4nmnuatXYkfXSP8YWDFz7bgtISNta6w7iiluZOySkDb8unuWXa2lUiSbHOJJ1vJ3kxa/jOpQvKULjQ49VFJQpH+vVVZKRtXkvpkFnOmISVSx0rfus9NJCPpQm8S7DJH6pRA1ZgKYsLochGcs0pjWJGAZtUC9V0Bz7gz5XEWJzouDZZe7DfOagc/Ts7irQmpLMFuBeno9kzplKmYSDW1N88uO9CsCP4JOY/EKusbqzRu/032a0I9kozn8gyw3aAU6MArCVbfmr4/L+LV+MhLlnxe2RHy9iLw0SmVI2nJDNoLWichbLKJ7euagFMjqyw45WTmIYXKvX8rsl9TATOwcvL+0okzt8GuDyDmWej1UoGPPC74eyRfVVifPCPfb0M/o+QVwsNMggT11piJcEYmPS3hJWlTJ6t3WDIqwpvh9Bh2OBmNyJ0xMC3Zw3rNWVCLalzxf3RdOhINiSS91DtDP5FDqnZ8Br0HK5GCN00NPfWNt0FjNAWYuRoIBPqx471i+91mQAM7cRwP8izxj7D+wcGQMyQs/FA3ALXU62532rTt/eG90vPtTtj6Jvxcqx+l5oclJIZ/6qQjaqpzP6DIhlPa0e6FBiwRefdazXu2C5gPKtyY9YukXWftFnu3xDqRvxcYd/LBeK0utJYtERs4U2naS0W/8DMRrTTtiME4QZf8QSHnQoEjOS+ZNxb1yUUdmH0FKqeLgPNfXfVmD1B6ZTp/B02o3uRd7y9k4f5IKIPQedW4gdUIfycBP20lYmV5U1xcYVG6r/r1L1kfeCwlEsJ/3NsapVQx2JVuJ+EKBdqsx3zUHHSLnk3kRoZ4q0E5KRPccxgEGCpb2rK4W+hwvskcqfhKZQhYij4B10yzeno8FNIxT2vvXyALAxg2t4nSCVX7Wc1jv9ufEV3z1d5xzo5IDRHVkM1qT+V6zx+mkwXXksaVLiMNkeoy3/FF7uJxNcLajSwUwxht5LAzALa3LqlMyleIvK2fGWiqLaDkp1aYwvzc4i66sm42x2LHE6XwVVR/RnHj/1cg9rbcdVr1huGWA+TmLc0u01EYZrZnc0fGi9BnlYvm3JcEaNJ3f09cB/iN7pgMD/j8kzhykd0RsJgS5sU1ggAFkK4E9kt3WPrNqyIP1jdUZ30+Qnj6KL/bcTcvaxbC6Y1E0sIQgTSaQC9n3yhKawohtEjRDGnbqqltCw9qw6MRjTXWEPeju5YwRpN0DeVNB+3B8/sLeR7aR2tvbk7tyHvB9pW6WSInfmsGnTPqVChU7F0m/qYeP6PwjGK8TY69Pr0ziBh0JOLKYRVG2RwQnZGsGoLoXSHxZCV90Q5mIRagpGDBslvJTcxWtnUWT/ktFYkiFXHvW09hD/jqjzBRXSKOzfZnr5Xery2BIcmslYrSYbb38dY/3TmFL2k56R7nXS2uQEzhFJhT1OjOoOZ6hy6fGlNHhTXhiK0ZmF6AGb/Nw1nS0zW3HEa0wTJ5gnFUQrgkgon3NBtah5+dBsxNGn02O24vF4Lg8Va1Zj04igpuE/CctbUlJ5YKqqAj2SwL5hpBtNk8eV0srIFaFImaMcgzTJXt4tz5gOI3Ds3fkLjt3NBdLwMjw9VegdKHboCCYhVPzFY2lG++5LqJud37flp7Ikni17qpdUYO7khvsMLeUru6ouFqdV6fVdVywn2bW22Xs0DNNIfXGjnfhwhZ4AEvOqyFXmYHYqRYWOtBIxWu0aUtvtjcIi8gkDNsi0kY2Iuf6UFqwN7zqvuGYwsuC+n0UODwdi/48k1FWz/pOzqeaxo9O6AccKNEdDaXT4kwuJZsuvuKo0zv9IxDkVYoRB5Jx+vt58MtKGSxSpylWpJfw=="); + byte[] mldsa44_seed_only = Base64.decode("MDICAQAwCwYJYIZIAWUDBAMRBCAAAQIDBAUGBwgJCgsMDQ4PEAECAwQFBgcICQoLDA0ODw=="); + byte[] mldsa44_wrap_seed_only = Base64.decode("MDQCAQAwCwYJYIZIAWUDBAMRBCIEIAABAgMEBQYHCAkKCwwNDg8QAQIDBAUGBwgJCgsMDQ4P"); + byte[] mldsa44_expanded_only = Base64.decode("MIIKFAIBADALBglghkgBZQMEAxEEggoAw/FofbPea45APAHeQqNeZL4KcvMHr8V/inNsUbmYlE0vTZbckQVhHq8sQi2F3yC7B1/aLXIta1rImyPmzsYITzPH4Iz+3/ysH0n3TDx9HFKMp5WwuOX7ZRnNqS/RaiaYltSR0LBwyi5uPgGqw1Cb/mAsJkoJuGx31QGfc1KTMSQYAjEiqU0QJjHAlAyBtiRSxkGZIFFYGAYIIEXSSC4YE2rAooADOQoIQmwbKARSQiWBRExbOIkEQEVJIEQcEUCTxhEjQCahpJFLwDEIhIFamETJqDEbEWrKyCwBKWnCQECcNgYDtAQMMWwMQTFjwCiisEUTQEGiFAoRQibaQmmaokgjFUYJREmMBHAClSABlyRQFAlQECCgRiSCJIEQEwoACSRElg0QEjBSFAgIOQLIuDCiRHHcFoIZp0zkCDCbBIkbFYnYFAQMQ1ELGSCYICDSBGhhBmAcoS0JQ0HBEGGUiIlTIkYaGGpJMJHLtCDAEDKBpgXUAgoSxQAjQm0EuUQjCBDcIkbKwkUUom0clomLKHETNpBioEBTpEDLEkocIzJiQI0JBAIaMQHaRCQbkW0KSHHgBGADoi0AFHHYFA3hgg0ayU1iloyIAEmQBJLLKC4JgQADhiGbmIiUggBcwCWDIiwByWCSEExaRC0ZkgALM0kTlZCJAnHUNEaYlJEEMU6JtAUAo0gRJ23kokkSxoREsjCSomBUyCFEAEpUJmkDkWVLloSSQkpjkm0AEQqZxFEIE4gjGCGTpkEhsEAUEyIZqEAjJg1BRlHBmCEJg4AZSGobNE5gMIWRRmTMtGBcgCVhpo2bwCDjFFLbAkJksHCZRAEKMVBjQi1jhkQQpg3EgmybBjDDqC3hRIBMoGHUpiHIMilQEgpQMA7QEClMBolUNihTAmDQAICTEm6IqEATCCEMOCoEEYwKN4maRDAjkSEJRmnIohALgAADBCoBhjFgJiIYkWmTQiHkKA4cImYLk3CSBIKUEA4gE2ibMkRjkkUKACyMRkYRoWQUl0wbmW2QBkkJR4AgR2iCJAYUMgpLKGwgKVHiBE4iBRALNYwbFwwUAGpQwAwDQSwSwQAQAi7REgrSIAlbkHEJyZAASWDMMm4YlSHiwFFIFkrbFI6IJJERJUhLlkXRyCAAk5GDoC2JyJGYQgkRRGkTJE2jRCzUGFEiF1DAghECNIJIgHAgQWlvzZh7vICP92WCMX3B8zbvb7tgGJ1FERU17RMAxgnav4gEW/oe9DFzrwvEKF4Ku25tc0FQgdmnvLQFYwpTQuDjTxsug4GZViD6JP0GvZpbvfRhSWIjeFVdQcspJ26wR3IaM4M8yRhim1xEfUf7HcopRHQ6bzJ0B7rhE60A1mouwYG8ekzSxQ4WPtqecHdT57vYaEjOqjM0g3vUKa8ybDzUUVJDXx/6pcww596LIBNHygVaBGdRyAhNYxzSg0Z9BuxN295I3WJLU13+yYsdgvMq/qoj0LVZPevCjMBLnGnBAGsXxpYmTZbiyE6v5vQO3xWB6So6vX+G82gl0T33gZEw0B0stRf76XAcHHNkQl5nz1ZdkCS2QGh8Or4nmnuatXYkfXSP8YWDFz7bgtISNta6w7iiluZOySkDb8unuWXa2lUiSbHOJJ1vJ3kxa/jOpQvKULjQ49VFJQpH+vVVZKRtXkvpkFnOmISVSx0rfus9NJCPpQm8S7DJH6pRA1ZgKYsLochGcs0pjWJGAZtUC9V0Bz7gz5XEWJzouDZZe7DfOagc/Ts7irQmpLMFuBeno9kzplKmYSDW1N88uO9CsCP4JOY/EKusbqzRu/032a0I9kozn8gyw3aAU6MArCVbfmr4/L+LV+MhLlnxe2RHy9iLw0SmVI2nJDNoLWichbLKJ7euagFMjqyw45WTmIYXKvX8rsl9TATOwcvL+0okzt8GuDyDmWej1UoGPPC74eyRfVVifPCPfb0M/o+QVwsNMggT11piJcEYmPS3hJWlTJ6t3WDIqwpvh9Bh2OBmNyJ0xMC3Zw3rNWVCLalzxf3RdOhINiSS91DtDP5FDqnZ8Br0HK5GCN00NPfWNt0FjNAWYuRoIBPqx471i+91mQAM7cRwP8izxj7D+wcGQMyQs/FA3ALXU62532rTt/eG90vPtTtj6Jvxcqx+l5oclJIZ/6qQjaqpzP6DIhlPa0e6FBiwRefdazXu2C5gPKtyY9YukXWftFnu3xDqRvxcYd/LBeK0utJYtERs4U2naS0W/8DMRrTTtiME4QZf8QSHnQoEjOS+ZNxb1yUUdmH0FKqeLgPNfXfVmD1B6ZTp/B02o3uRd7y9k4f5IKIPQedW4gdUIfycBP20lYmV5U1xcYVG6r/r1L1kfeCwlEsJ/3NsapVQx2JVuJ+EKBdqsx3zUHHSLnk3kRoZ4q0E5KRPccxgEGCpb2rK4W+hwvskcqfhKZQhYij4B10yzeno8FNIxT2vvXyALAxg2t4nSCVX7Wc1jv9ufEV3z1d5xzo5IDRHVkM1qT+V6zx+mkwXXksaVLiMNkeoy3/FF7uJxNcLajSwUwxht5LAzALa3LqlMyleIvK2fGWiqLaDkp1aYwvzc4i66sm42x2LHE6XwVVR/RnHj/1cg9rbcdVr1huGWA+TmLc0u01EYZrZnc0fGi9BnlYvm3JcEaNJ3f09cB/iN7pgMD/j8kzhykd0RsJgS5sU1ggAFkK4E9kt3WPrNqyIP1jdUZ30+Qnj6KL/bcTcvaxbC6Y1E0sIQgTSaQC9n3yhKawohtEjRDGnbqqltCw9qw6MRjTXWEPeju5YwRpN0DeVNB+3B8/sLeR7aR2tvbk7tyHvB9pW6WSInfmsGnTPqVChU7F0m/qYeP6PwjGK8TY69Pr0ziBh0JOLKYRVG2RwQnZGsGoLoXSHxZCV90Q5mIRagpGDBslvJTcxWtnUWT/ktFYkiFXHvW09hD/jqjzBRXSKOzfZnr5Xery2BIcmslYrSYbb38dY/3TmFL2k56R7nXS2uQEzhFJhT1OjOoOZ6hy6fGlNHhTXhiK0ZmF6AGb/Nw1nS0zW3HEa0wTJ5gnFUQrgkgon3NBtah5+dBsxNGn02O24vF4Lg8Va1Zj04igpuE/CctbUlJ5YKqqAj2SwL5hpBtNk8eV0srIFaFImaMcgzTJXt4tz5gOI3Ds3fkLjt3NBdLwMjw9VegdKHboCCYhVPzFY2lG++5LqJud37flp7Ikni17qpdUYO7khvsMLeUru6ouFqdV6fVdVywn2bW22Xs0DNNIfXGjnfhwhZ4AEvOqyFXmYHYqRYWOtBIxWu0aUtvtjcIi8gkDNsi0kY2Iuf6UFqwN7zqvuGYwsuC+n0UODwdi/48k1FWz/pOzqeaxo9O6AccKNEdDaXT4kwuJZsuvuKo0zv9IxDkVYoRB5Jx+vt58MtKGSxSpylWpJfw=="); + byte[] mldsa44_wrap_expanded_only = Base64.decode("MIIKGAIBADALBglghkgBZQMEAxEEggoEBIIKAMPxaH2z3muOQDwB3kKjXmS+CnLzB6/Ff4pzbFG5mJRNL02W3JEFYR6vLEIthd8guwdf2i1yLWtayJsj5s7GCE8zx+CM/t/8rB9J90w8fRxSjKeVsLjl+2UZzakv0WommJbUkdCwcMoubj4BqsNQm/5gLCZKCbhsd9UBn3NSkzEkGAIxIqlNECYxwJQMgbYkUsZBmSBRWBgGCCBF0kguGBNqwKKAAzkKCEJsGygEUkIlgURMWziJBEBFSSBEHBFAk8YRI0AmoaSRS8AxCISBWphEyagxGxFqysgsASlpwkBAnDYGA7QEDDFsDEExY8AoorBFE0BBohQKEUIm2kJpmqJIIxVGCURJjARwApUgAZckUBQJUBAgoEYkgiSBEBMKAAkkRJYNEBIwUhQICDkCyLgwokRx3BaCGadM5AgwmwSJGxWJ2BQEDENRCxkgmCAg0gRoYQZgHKEtCUNBwRBhlIiJUyJGGhhqSTCRy7QgwBAygaYF1AIKEsUAI0JtBLlEIwgQ3CJGysJFFKJtHJaJiyhxEzaQYqBAU6RAyxJKHCMyYkCNCQQCGjEB2kQkG5FtCkhx4ARgA6ItABRx2BQN4YINGslNYpaMiABJkASSyyguCYEAA4Yhm5iIlIIAXMAlgyIsAclgkhBMWkQtGZIACzNJE5WQiQJx1DRGmJSRBDFOibQFAKNIESdt5KJJEsaERLIwkqJgVMghRABKVCZpA5FlS5aEkkJKY5JtABEKmcRRCBOIIxghk6ZBIbBAFBMiGahAIyYNQUZRwZghCYOAGUhqGzROYDCFkUZkzLRgXIAlYaaNm8Ag4xRS2wJCZLBwmUQBCjFQY0ItY4ZEEKYNxIJsmwYww6gt4USATKBh1KYhyDIpUBIKUDAO0BApTAaJVDYoUwJg0ACAkxJuiKhAEwghDDgqBBGMCjeJmkQwI5EhCUZpyKIQC4AAAwQqAYYxYCYiGJFpk0Ih5CgOHCJmC5NwkgSClBAOIBNomzJEY5JFCgAsjEZGEaFkFJdMG5ltkAZJCUeAIEdogiQGFDIKSyhsIClR4gROIgUQCzWMGxcMFABqUMAMA0EsEsEAEAIu0RIK0iAJW5BxCcmQAElgzDJuGJUh4sBRSBZK2xSOiCSRESVIS5ZF0cggAJORg6AticiRmEIJEURpEyRNo0Qs1BhRIhdQwIIRAjSCSIBwIEFpb82Ye7yAj/dlgjF9wfM272+7YBidRREVNe0TAMYJ2r+IBFv6HvQxc68LxCheCrtubXNBUIHZp7y0BWMKU0Lg408bLoOBmVYg+iT9Br2aW730YUliI3hVXUHLKSdusEdyGjODPMkYYptcRH1H+x3KKUR0Om8ydAe64ROtANZqLsGBvHpM0sUOFj7annB3U+e72GhIzqozNIN71CmvMmw81FFSQ18f+qXMMOfeiyATR8oFWgRnUcgITWMc0oNGfQbsTdveSN1iS1Nd/smLHYLzKv6qI9C1WT3rwozAS5xpwQBrF8aWJk2W4shOr+b0Dt8VgekqOr1/hvNoJdE994GRMNAdLLUX++lwHBxzZEJeZ89WXZAktkBofDq+J5p7mrV2JH10j/GFgxc+24LSEjbWusO4opbmTskpA2/Lp7ll2tpVIkmxziSdbyd5MWv4zqULylC40OPVRSUKR/r1VWSkbV5L6ZBZzpiElUsdK37rPTSQj6UJvEuwyR+qUQNWYCmLC6HIRnLNKY1iRgGbVAvVdAc+4M+VxFic6Lg2WXuw3zmoHP07O4q0JqSzBbgXp6PZM6ZSpmEg1tTfPLjvQrAj+CTmPxCrrG6s0bv9N9mtCPZKM5/IMsN2gFOjAKwlW35q+Py/i1fjIS5Z8XtkR8vYi8NEplSNpyQzaC1onIWyyie3rmoBTI6ssOOVk5iGFyr1/K7JfUwEzsHLy/tKJM7fBrg8g5lno9VKBjzwu+HskX1VYnzwj329DP6PkFcLDTIIE9daYiXBGJj0t4SVpUyerd1gyKsKb4fQYdjgZjcidMTAt2cN6zVlQi2pc8X90XToSDYkkvdQ7Qz+RQ6p2fAa9ByuRgjdNDT31jbdBYzQFmLkaCAT6seO9YvvdZkADO3EcD/Is8Y+w/sHBkDMkLPxQNwC11Otud9q07f3hvdLz7U7Y+ib8XKsfpeaHJSSGf+qkI2qqcz+gyIZT2tHuhQYsEXn3Ws17tguYDyrcmPWLpF1n7RZ7t8Q6kb8XGHfywXitLrSWLREbOFNp2ktFv/AzEa007YjBOEGX/EEh50KBIzkvmTcW9clFHZh9BSqni4DzX131Zg9QemU6fwdNqN7kXe8vZOH+SCiD0HnVuIHVCH8nAT9tJWJleVNcXGFRuq/69S9ZH3gsJRLCf9zbGqVUMdiVbifhCgXarMd81Bx0i55N5EaGeKtBOSkT3HMYBBgqW9qyuFvocL7JHKn4SmUIWIo+AddMs3p6PBTSMU9r718gCwMYNreJ0glV+1nNY7/bnxFd89Xecc6OSA0R1ZDNak/les8fppMF15LGlS4jDZHqMt/xRe7icTXC2o0sFMMYbeSwMwC2ty6pTMpXiLytnxloqi2g5KdWmML83OIuurJuNsdixxOl8FVUf0Zx4/9XIPa23HVa9YbhlgPk5i3NLtNRGGa2Z3NHxovQZ5WL5tyXBGjSd39PXAf4je6YDA/4/JM4cpHdEbCYEubFNYIABZCuBPZLd1j6zasiD9Y3VGd9PkJ4+ii/23E3L2sWwumNRNLCEIE0mkAvZ98oSmsKIbRI0Qxp26qpbQsPasOjEY011hD3o7uWMEaTdA3lTQftwfP7C3ke2kdrb25O7ch7wfaVulkiJ35rBp0z6lQoVOxdJv6mHj+j8IxivE2OvT69M4gYdCTiymEVRtkcEJ2RrBqC6F0h8WQlfdEOZiEWoKRgwbJbyU3MVrZ1Fk/5LRWJIhVx71tPYQ/46o8wUV0ijs32Z6+V3q8tgSHJrJWK0mG29/HWP905hS9pOeke510trkBM4RSYU9TozqDmeocunxpTR4U14YitGZhegBm/zcNZ0tM1txxGtMEyeYJxVEK4JIKJ9zQbWoefnQbMTRp9NjtuLxeC4PFWtWY9OIoKbhPwnLW1JSeWCqqgI9ksC+YaQbTZPHldLKyBWhSJmjHIM0yV7eLc+YDiNw7N35C47dzQXS8DI8PVXoHSh26AgmIVT8xWNpRvvuS6ibnd+35aeyJJ4te6qXVGDu5Ib7DC3lK7uqLhanVen1XVcsJ9m1ttl7NAzTSH1xo534cIWeABLzqshV5mB2KkWFjrQSMVrtGlLb7Y3CIvIJAzbItJGNiLn+lBasDe86r7hmMLLgvp9FDg8HYv+PJNRVs/6Ts6nmsaPTugHHCjRHQ2l0+JMLiWbLr7iqNM7/SMQ5FWKEQeScfr7efDLShksUqcpVqSX8="); + KeyFactory kFact = KeyFactory.getInstance("ML-DSA", "BC"); + + checkEncodeRecode(kFact, mldsa44_sequence); + checkEncodeRecode(kFact, mldsa44_seed_only); + checkEncodeRecode(kFact, mldsa44_wrap_seed_only); + checkEncodeRecode(kFact, mldsa44_expanded_only); + checkEncodeRecode(kFact, mldsa44_wrap_expanded_only); + } + + private void checkEncodeRecode(KeyFactory kFact, byte[] encoding) + throws Exception + { + PrivateKey key = kFact.generatePrivate(new PKCS8EncodedKeySpec(encoding)); + + assertTrue(Arrays.areEqual(encoding, key.getEncoded())); + } + + public void testPublicKeyRecovery() throws Exception { @@ -257,6 +328,72 @@ private void doTestRestrictedSignature(String sigName, MLDSAParameterSpec spec, } } + /* + public void testMLDSA() + throws Exception + { + + KeyPairGenerator kpGen44 = KeyPairGenerator.getInstance("ML-DSA-44"); + KeyPair kp44 = kpGen44.generateKeyPair(); + KeyPairGenerator kpGen65 = KeyPairGenerator.getInstance("ML-DSA-65"); + KeyPair kp65 = kpGen65.generateKeyPair(); + KeyPairGenerator kpGen87 = KeyPairGenerator.getInstance("ML-DSA-87"); + KeyPair kp87 = kpGen87.generateKeyPair(); + + outputKeyPair("ml-dsa-44", kp44); + outputKeyPair("ml-dsa-65", kp65); + outputKeyPair("ml-dsa-87", kp87); + } + + private void outputKeyPair(String algorithm, KeyPair kp) + throws Exception + { + KeyFactory kFact = KeyFactory.getInstance("ML-DSA", "BC"); + + System.setProperty("seed", "true"); + System.setProperty("expanded", "true"); + FileWriter fWrt = new FileWriter("/tmp/ml-dsa-pems/" + algorithm + "-priv.pem"); + + PemWriter pWrt = new PemWriter(fWrt); + + pWrt.writeObject(new PemObject("PRIVATE KEY", kp.getPrivate().getEncoded())); + + pWrt.close(); + + PrivateKey priv = kFact.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded())); + + System.setProperty("seed", "true"); + System.setProperty("expanded", "false"); + fWrt = new FileWriter("/tmp/ml-dsa-pems/" + algorithm + "-seed-only-priv.pem"); + + pWrt = new PemWriter(fWrt); + + pWrt.writeObject(new PemObject("PRIVATE KEY", priv.getEncoded())); + + pWrt.close(); + + priv = kFact.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded())); + + System.setProperty("seed", "false"); + System.setProperty("expanded", "true"); + fWrt = new FileWriter("/tmp/ml-dsa-pems/" + algorithm + "-expanded-only-priv.pem"); + + pWrt = new PemWriter(fWrt); + + pWrt.writeObject(new PemObject("PRIVATE KEY", priv.getEncoded())); + + pWrt.close(); + + fWrt = new FileWriter("/tmp/ml-dsa-pems/" + algorithm + "-pub.pem"); + + pWrt = new PemWriter(fWrt); + + pWrt.writeObject(new PemObject("PUBLIC KEY", kp.getPublic().getEncoded())); + + pWrt.close(); + } + */ + public void testRestrictedKeyPairGen() throws Exception { @@ -532,7 +669,7 @@ public void testHashMLDSAKATSig() assertTrue(sig.verify(genS)); AlgorithmParameters algP = sig.getParameters(); - + assertTrue(null == algP); // test using ml-dsa-44 for the key, should be the same. @@ -646,4 +783,11 @@ public void nextBytes(byte[] bytes) } } } + + public static void main(String[] args) + throws Exception + { + byte[] enc = Hex.decode("30820A2604201BA29BED84A27773E818F0B3FDABB60D37AFAE2CF8CB14844130C2F3A003B1BA81820A004CE51DC37BB794EB06DB74F14F050585CD70139D8ECDB6F0D2277144662158D969E49D22ED3BAB11A549FEA12174B4C6C9652D70229C78D7D9737443CF7E6387D01D7390342777C200309C05303F388A2AE1ED0BA183A1EE6C0808DEE411DF19CA18CAEC042F9E90DA15F6606CA35E57B917297E7ACD2C981265E5A40ABA67D88B36202219712009461A328D90C66100882DA4480C6482604A80456320269AC82C23845060A084A0A0208CA46820115044942C5C20404A044EC4162900C911549488E22602CC8889134906210068C38650138968529091C83688634406A3A404922045D99620DC066903150810B490CBC44C09878894346C20102422336C004981418044A4A08C44860502100040A091043042548221090564D03890194060A0C05004A205A3262DD9866D09A349C9C641CBB6699A10311A884D88244A6232701C9688400820514226DAA468C4046424260209010AC24668C0348E1C9421DC088489029058065183260223B38D82B60C000704A328660096858AA4099BC409934429A4424C40424D63B490101565C0C468C8222E02888083460000282542A02460320CD4262CE2146889C0851B376013468A5C1609603862A3364C60A231CB2491D2186E091149401291882404013485C4A64864304600272A0AA3502131700243291B096810274124030EA0B60094000202B40812980C0C1528CC8804D0069099368493382922B58D54C0410CC58C133081C9204022C86864960102870088306EA3464C08A090003362D8008A5A9881840072E3A805C0188011329044206C93125081205081422D028789CB2650499849610840DA10020A19219AB4880844201AB7400A47880432729404602020202412065A264C1C39911825104AC6094C20668022262427041216100282100C3202E33650E448440C246DCAA2641C126904856D8A46905A868C12422EC1A44820349109152103428C53842D24136DE34880202368C828400C81040B826D124469C9260C24B090824204D2B46483B441E4C40D011429D0248249062D99828D992842A3184622044244464D118371C0100448C0648A101199066243B26CD3304AC810010AB4245AA84118B460D3340CC4840DC84048D9164D53B4206218698484445336401BA2840195208846428B2221C4B84811358A1A17005490711C411103A18D104828C4A20853140609B5910C129183B82C5C1424A4446D09B8051A3766D4820961A6201015862224605C1870203709AF99F0C00D4D82188D2DE0792B0010C0AE9CC07F6C172F8974187DD7A3ED3C904068E1806D73B53910EC410ECC02B1980DF524F78A0F4CD155D6595E9C27FA19D96DE7376B4894083FA8BCC1752ACF88075ADA57A9D6606FE887EE0D8BE481A754DC061FA3F7F9CC21501F4C6BB325D1890A5DA77BCF5C97B0BA9E7D2BD055665C4E2E15F50606FA649689E046C22063EB272AA351BDE346AD2424D7054F4E23CAD899E25FB5A5E9D1653ECFD50772092D2D25A4EB62B42CDAA23DE42C7B50D23F8BB429D8E8479779FCD4173B69C3B343A08874FBEAAF1D727D929CA4F8011CBA9DB33C1DA0E4BCFA8D1397DB74A5ADCFF0A2BAB74139B51E3F6BDA077579073E6050352FE7F85FC028EAA91FD1DA18D35BC9EE6B8148BEA2B22F17BF24DFC387C2E1591B2DAE0DE0DF9B36313360E14F926A31E19B0473C45DDE050C2A1CB394C8BD4E9403B065414EF8C01F42AD4C4FA69BD831376327947EC0F9D2E129EB3034CA5DF04260AD1B15C66123BC220BAF53DE2CA65E7A0E7CDFFF4EEFB3FC386FBD04BC06BDE385C79ABC81CE8A447A64CC916AA7FCC9471CA6710252A2220BD27A6D3413BD4AD36240C69FD95C9BA3249E3D03745289CE7C8AAB2168974BABA116F02F9AD5EB4210193C37E28D7E6925D64EC3C551F0398C75A3A34A4F07E5BB84E412CAD58EB1FC1D059D747E3177BC51E68A833358BB0254266F88F2A4DE4D6FE4285F213ED479ADB6E2AA320343B80051CA6D4929478812405ADD1F8054B56BCBE02E43D66CFEA6FE976E9FDBEC9EEB2A86D0111165DDA1E622E040F05D171822A74F5458B5925FB0AFF0D623A4666452DC2B11F8D14C477F676BCD1BED62FD1427A25CB743EAE47FB6AF0C51B2DDA77B5EB0EF001839384DA8D7390DF8654BA0C8EB94A92D20BE50500450E7A3D3B374AD02943F2D22046611F8C23B68B7679E9D81AC4B3A01261D85CAAD696EF65E39D2D0D7308ACFA954A8D89A6BE9EE3FBC313BE89035E4431212A7B0DCBADFBAEC294F7CECBDD4D59709276D2DDF4428D9D019F2814B60B9A388C47DD7E26A00131DC853148A79F02D395C33205C1CFBB688C1FEE9A8453B2B371B431E7DA86DB98A090AF0A6CBF131811BE86B2BAEF3C2D61E9EE1D628BAC2D9A4CF8ECD32CC8B926D01DBDFA10327375B9461827B52DAA483F918E5F30894BEBF73332E2169472E72B61A16E2F9A5876D429E5B5791F61ECDF2F73F69DD7609AD36C1CC0DF6C8A5744055E863F63F069CE304D1BCA0680DC64AD9B00ABC39662C74C59AA677F59A6B101C393B7EB7B427125AA1785ED2E4ADD2FA074599A954E1D1B8BE2D18A65A86BB1D2274C7B11FB2228A5C632B705B428ADBFCAE399C7748F08557F3AAA459010F803049B08D9832706C2E52BF88836C6BB31BCCB93C16E413294EE9BDFF5023A5DA482783C4134A5FB7AB93AD58B85A6105640671DC3B86066BF690B3F3730AB2A9D25437690E31AC3F1CC17437F9E641E3C4CA28A8D7C1FD6BD27E534CD31F25DC9E6105C86C3DA36379C7D84C51F116B975C3A4D4D3641671581CD654B1794087649AB9479D3DD80C147D169E34E6FD9A7478FC318FCD92435C874ABF0F1939C6795ED10B5659A271DE900D08E580F74D670557888CD790495442D129B4A41577801F456A7286A1B860849A45E0CD1882390C012E644ECCB9393E9863B55B470647456916D3EBE20DC16A90C09F2DDB0525A151661D04FA916F7AC66E78AB76CE15FF9E6EC14520CAFF8CF8BF81DFBBAC96E2D85ACDEF6950FFBEA257D6817151B5D5EF32329C016A00F75AE006261A6CF5DB50CA909554E48790C11FC4A3B7A07C8EC15439E59754AF8F513D1B9526670A18FCD89CA476DF41FA8D59F56988BF38A85E2996F1CAFBCF8D039E1ED9FE5C258B825953B4669FBCB652DB604A90E02B803B72DC414EDAF62B31C640126A90CD946DBAAE786637695A0BCE6007E671844A90BC24DD8EAA36364D739A350F7D442C6C1B06CD5CFEB2F2C997BDB599DFD4933FBC8C3DD70DE8F20D52479149D477E765358F64A403E6B7D42B8B302A404026AEA4EBCD9755EE1727977C0228505587F0104F9D57B52D87B3A894992CE3D9C12E1ED625132584F521C5076504AC979053E1DE85E1A2D0F78D70B4A4F680EEAD0876710289448DB78DBF3BC1CEE48033C58BEF2CB9348871A5BB6132BED61E30AE4A4E23B2A2A49C15438CE23697ABEE47F1FC482BB02F26512796F5D8A3BC407DFB5745734FC96ADDD8204B8B6BA0B48C62089FC8F9951782969B661F0B66BDEA3AFE0E40AA9B5C2E4DA8B42D56907EDA4AE42CFA2E38EFC961C4EAB9C0824B36A503E85A41C5"); + + } } diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java index e9c01e4119..fdb2284fd7 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java @@ -6,6 +6,7 @@ import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.PrivateKey; import java.security.SecureRandom; import java.security.Security; import java.security.spec.InvalidKeySpecException; @@ -18,8 +19,13 @@ import javax.crypto.spec.SecretKeySpec; import junit.framework.TestCase; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.jcajce.SecretKeyWithEncapsulation; +import org.bouncycastle.jcajce.interfaces.MLKEMPrivateKey; import org.bouncycastle.jcajce.spec.KEMExtractSpec; import org.bouncycastle.jcajce.spec.KEMGenerateSpec; import org.bouncycastle.jcajce.spec.KTSParameterSpec; @@ -28,7 +34,9 @@ import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters; import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.FixedSecureRandom; /** * KEM tests for MLKEM with the BC provider. @@ -117,6 +125,74 @@ private void tryKeyFact(KeyFactory kFact, KeyPair kpValid, KeyPair kpInvalid, St } } + public void testDefaultPrivateKeyEncoding() + throws Exception + { + KeyPairGenerator kpGen512 = KeyPairGenerator.getInstance("ML-KEM-512", "BC"); + + byte[] seed = Hex.decode("000102030405060708090a0b0c0d0e0f" + + "100102030405060708090a0b0c0d0e0f" + + "200102030405060708090a0b0c0d0e0f" + + "300102030405060708090a0b0c0d0e0f"); + kpGen512.initialize(MLKEMParameterSpec.ml_kem_512, new FixedSecureRandom(seed)); + KeyPair kp512 = kpGen512.generateKeyPair(); + + PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp512.getPrivate().getEncoded()); + ASN1OctetString seq = ASN1OctetString.getInstance(ASN1Sequence.getInstance(privInfo.getPrivateKey().getOctets()).getObjectAt(0)); + + assertTrue(Arrays.areEqual(seq.getOctets(), seed)); + + ASN1OctetString privData = ASN1OctetString.getInstance((ASN1TaggedObject)ASN1Sequence.getInstance(privInfo.getPrivateKey().getOctets()).getObjectAt(1), false); + + assertTrue(Arrays.areEqual(privData.getOctets(), ((MLKEMPrivateKey)kp512.getPrivate()).getPrivateData())); + } + + public void testSeedPrivateKeyEncoding() + throws Exception + { + KeyPairGenerator kpGen512 = KeyPairGenerator.getInstance("ML-KEM-512", "BC"); + + byte[] seed = Hex.decode("000102030405060708090a0b0c0d0e0f" + + "100102030405060708090a0b0c0d0e0f" + + "200102030405060708090a0b0c0d0e0f" + + "300102030405060708090a0b0c0d0e0f"); + kpGen512.initialize(MLKEMParameterSpec.ml_kem_512, new FixedSecureRandom(seed)); + KeyPair kp512 = kpGen512.generateKeyPair(); + Security.setProperty("org.bouncycastle.mlkem.seedOnly", "true"); + + PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp512.getPrivate().getEncoded()); + + Security.setProperty("org.bouncycastle.mlkem.seedOnly", "false"); + ASN1OctetString k = privInfo.getPrivateKey(); + + assertTrue(Arrays.areEqual(k.getOctets(), seed)); + } + + public void testPrivateKeyRecoding() + throws Exception + { + byte[] mlkem512_sequence = Base64.decode("MIIGvgIBADALBglghkgBZQMEBAEEggaqMIIGpgRAAAECAwQFBgcICQoLDA0ODxABAgMEBQYHCAkKCwwNDg8gAQIDBAUGBwgJCgsMDQ4PMAECAwQFBgcICQoLDA0OD4GCBmBYIXof3JWb6cceWnPV84vw6JvPYY65dINCHBetKcOtprZ9gHQvFXXZOynsWCAHXGa8iD4YFcmglwnoA8O4MKoT9G1bl5JnAosj9yp65s1puVLtIJBI5GtmI25WSgaA+KRP2HNQOG8OF0Tu24P5wXVR+kQIZk4UMV9op4W58mVf+RwgDAWOBc4+omsGJR3cFSxvYbzWTHIkd8Q4Nq5Xennx0BZF5bhZ/DObwI/45cwCxSJlOUcLMnFtpcU/AIFqiRz/xcF3oVQ4EscCPFtVEq+5NBLwkQksVMe+pi16GKTrqER01QkF9KlEYVqVEWO9enxpwVeaW5VNCw7p6hMvmk7SdnV+F61/REqDN4jTsbTEiF6urGWfV3CPtW0zpaxbpbFlwUeQFAxYWJFSK3EnVAhupj/U+jq4ESn9hg48Sh7uozDaHG9p00R/sqJE+rpH6jEMzCe2F7H3g7vxk2gMeIzk91ThYqPwSSvJKjj25L539hSfHEZltJJj67NNhjsP7DvNxqmvSyCaZQge1gRTpbGYIwUDw2YAHJLDTE6EloMGyTkRYrVpmqQz56puFh9HAikW+spqkCLzqLqMBp1zCbwBJGObJmZF8JiR68bySKxVCDjhMVs/pFihegUfNcjcGC4kKZsYFLtgVJ/AdwiI7Ju4lZAurEhlmQ93OUaacyvbm6KZsHHLMgHhnDsexl8YQZDfF0CwJGEWyRDDRcMchVZn0hk3+mh+pSU2Gqp4YILpFpGSAMx2ssTxsF4HxZS3RLJcqAJl5ckGyEprd2Fl46rXJY23mEH+tkoOCzOo+bC/6GJnFRGDpVQk8SiJdoCxscFkpZE7LCbtZ4ryCF2zJrOEYVskx0qEA5JgoGBYgEJxQEdkwsSG2beA0sd3MbHgO0Lh0aYkfExGEWgflxf8BGU75Fwq141lNzJP6GVIiDJT/AGDHMGEjCxWaQxiK48aeaoQAXT4sjv1WqtMMkWZ0FgYmx1KE5XwTLgka6WahE3ejI62+w8+knxB+XTGAkufo2zzspwj8xRpe2KUyl5jDFLUDAWe8lkbMMqHUbFO0oPzuJPMSsiCJXQAWiOwRnn0CS+MaYXDWM++FIhewX3TQQxzw7ahYCsochjNdYvYCZ8OaaaMXFNbghznHJB6OSJuKiVseEBvRFhh6wjVmZSdcKPKIFLP8KmAjIUiOpVcA7EabLlcl0yhsMluGHTGdXe7K00Ze5GJiqOdpBPNUSls1RhdsB1WgW4d/Awp/Lp4cgFegskWel0s4RidiKt4Ji/WasZiNawf0xjhizXu+8hwVKJr9Fz5W0f0ImTZuqARWorZOxXoqBERomAMGaPQFGnyi6s79gAUyyXEtwtAOqsq0DgmEZwDOGxiOnNCUFeWNKWqYUpaaMIgFEWsVowb7G/vpgFqqH7RoRAo5VPYnA3IAMbdKieGhQOi+j4n1SVoaWPCZ5oBAKi5BkMJcQuCt4qkc4VowrtGlAc2O2W/4W5X6Ttae505Gz01173YrDXdJFK6WZC1qjVIcl/RVFy+5C7d4zMsuyOW4SUqoSm8Nlf8KDvpeASm1hoSqUxA1Jrp+HfRtz4aGjIVEbHfOTfO9sau2sRaI8hpwIx5AoyaJ43xqmOtqACJmwZ2kwKx/GrXEAmtocHhwwZKLHdk6nyuIq6aI7gRDAOG0ka7g1dssLjn0mSpoQ/UdMD+M2nCIhEGwcTd2U5oUTuImT7OesxfNwb7V8cBeENq3A8SLMAu6nKFPECImwJbPFy0oVvOi7NvBy5cAqoluV011GzH4RJq6Cg35AouYQk4mWmNYoUg5ETh1pZemgLPhY/DUAMJOINDdbNM9Cfjq2WAMMXBPHrR4mT4ICfjxD55gzdbpIMK2ospiBSMuUlxqR0oyYolV0gvUDrqC0feMD+gm6uM0Y1ocr5k0oOb03EqtZD0+aYECIew8qoiiwtHixnqgA0Lia1UcreVmZjP0sj96Mh8uB3trDUg0G6k48+kaatCoDgtwaB3SYwJVEsrESo8FG5MNosEAjwd0lTCyDAjcasfVmQCw0cGGa9R1I2OJymU6hYdw1iL1TgVmQiJ8GMdSAZQ1SGbDgyR/ch95i0mgu2Olg4iNDfIUfzPN0QoFjvggsdP4FJaJqQDIAECAwQFBgcICQoLDA0ODzABAgMEBQYHCAkKCwwNDg8="); + byte[] mlkem512_seed_only = Base64.decode("MFICAQAwCwYJYIZIAWUDBAQBBEAAAQIDBAUGBwgJCgsMDQ4PEAECAwQFBgcICQoLDA0ODyABAgMEBQYHCAkKCwwNDg8wAQIDBAUGBwgJCgsMDQ4P"); + byte[] mlkem512_wrap_seed_only = Base64.decode("MFQCAQAwCwYJYIZIAWUDBAQBBEIEQAABAgMEBQYHCAkKCwwNDg8QAQIDBAUGBwgJCgsMDQ4PIAECAwQFBgcICQoLDA0ODzABAgMEBQYHCAkKCwwNDg8="); + byte[] mlKem512_expanded_only = Base64.decode("MIIGdAIBADALBglghkgBZQMEBAEEggZgWCF6H9yVm+nHHlpz1fOL8Oibz2GOuXSDQhwXrSnDraa2fYB0LxV12Tsp7FggB1xmvIg+GBXJoJcJ6APDuDCqE/RtW5eSZwKLI/cqeubNablS7SCQSORrZiNuVkoGgPikT9hzUDhvDhdE7tuD+cF1UfpECGZOFDFfaKeFufJlX/kcIAwFjgXOPqJrBiUd3BUsb2G81kxyJHfEODauV3p58dAWReW4Wfwzm8CP+OXMAsUiZTlHCzJxbaXFPwCBaokc/8XBd6FUOBLHAjxbVRKvuTQS8JEJLFTHvqYtehik66hEdNUJBfSpRGFalRFjvXp8acFXmluVTQsO6eoTL5pO0nZ1fhetf0RKgzeI07G0xIherqxln1dwj7VtM6WsW6WxZcFHkBQMWFiRUitxJ1QIbqY/1Po6uBEp/YYOPEoe7qMw2hxvadNEf7KiRPq6R+oxDMwnthex94O78ZNoDHiM5PdU4WKj8EkrySo49uS+d/YUnxxGZbSSY+uzTYY7D+w7zcapr0sgmmUIHtYEU6WxmCMFA8NmABySw0xOhJaDBsk5EWK1aZqkM+eqbhYfRwIpFvrKapAi86i6jAadcwm8ASRjmyZmRfCYkevG8kisVQg44TFbP6RYoXoFHzXI3BguJCmbGBS7YFSfwHcIiOybuJWQLqxIZZkPdzlGmnMr25uimbBxyzIB4Zw7HsZfGEGQ3xdAsCRhFskQw0XDHIVWZ9IZN/pofqUlNhqqeGCC6RaRkgDMdrLE8bBeB8WUt0SyXKgCZeXJBshKa3dhZeOq1yWNt5hB/rZKDgszqPmwv+hiZxURg6VUJPEoiXaAsbHBZKWROywm7WeK8ghdsyazhGFbJMdKhAOSYKBgWIBCcUBHZMLEhtm3gNLHdzGx4DtC4dGmJHxMRhFoH5cX/ARlO+RcKteNZTcyT+hlSIgyU/wBgxzBhIwsVmkMYiuPGnmqEAF0+LI79VqrTDJFmdBYGJsdShOV8Ey4JGulmoRN3oyOtvsPPpJ8Qfl0xgJLn6Ns87KcI/MUaXtilMpeYwxS1AwFnvJZGzDKh1GxTtKD87iTzErIgiV0AFojsEZ59AkvjGmFw1jPvhSIXsF900EMc8O2oWArKHIYzXWL2AmfDmmmjFxTW4Ic5xyQejkibiolbHhAb0RYYesI1ZmUnXCjyiBSz/CpgIyFIjqVXAOxGmy5XJdMobDJbhh0xnV3uytNGXuRiYqjnaQTzVEpbNUYXbAdVoFuHfwMKfy6eHIBXoLJFnpdLOEYnYireCYv1mrGYjWsH9MY4Ys17vvIcFSia/Rc+VtH9CJk2bqgEVqK2TsV6KgREaJgDBmj0BRp8ourO/YAFMslxLcLQDqrKtA4JhGcAzhsYjpzQlBXljSlqmFKWmjCIBRFrFaMG+xv76YBaqh+0aEQKOVT2JwNyADG3SonhoUDovo+J9UlaGljwmeaAQCouQZDCXELgreKpHOFaMK7RpQHNjtlv+FuV+k7WnudORs9Nde92Kw13SRSulmQtao1SHJf0VRcvuQu3eMzLLsjluElKqEpvDZX/Cg76XgEptYaEqlMQNSa6fh30bc+GhoyFRGx3zk3zvbGrtrEWiPIacCMeQKMmieN8apjragAiZsGdpMCsfxq1xAJraHB4cMGSix3ZOp8riKumiO4EQwDhtJGu4NXbLC459JkqaEP1HTA/jNpwiIRBsHE3dlOaFE7iJk+znrMXzcG+1fHAXhDatwPEizALupyhTxAiJsCWzxctKFbzouzbwcuXAKqJbldNdRsx+ESaugoN+QKLmEJOJlpjWKFIORE4daWXpoCz4WPw1ADCTiDQ3WzTPQn46tlgDDFwTx60eJk+CAn48Q+eYM3W6SDCtqLKYgUjLlJcakdKMmKJVdIL1A66gtH3jA/oJurjNGNaHK+ZNKDm9NxKrWQ9PmmBAiHsPKqIosLR4sZ6oANC4mtVHK3lZmYz9LI/ejIfLgd7aw1INBupOPPpGmrQqA4LcGgd0mMCVRLKxEqPBRuTDaLBAI8HdJUwsgwI3GrH1ZkAsNHBhmvUdSNjicplOoWHcNYi9U4FZkIifBjHUgGUNUhmw4Mkf3IfeYtJoLtjpYOIjQ3yFH8zzdEKBY74ILHT+BSWiakAyABAgMEBQYHCAkKCwwNDg8wAQIDBAUGBwgJCgsMDQ4P"); + byte[] mlKem512_wrap_expanded_only = Base64.decode("MIIGeAIBADALBglghkgBZQMEBAEEggZkBIIGYFgheh/clZvpxx5ac9Xzi/Dom89hjrl0g0IcF60pw62mtn2AdC8Vddk7KexYIAdcZryIPhgVyaCXCegDw7gwqhP0bVuXkmcCiyP3KnrmzWm5Uu0gkEjka2YjblZKBoD4pE/Yc1A4bw4XRO7bg/nBdVH6RAhmThQxX2inhbnyZV/5HCAMBY4Fzj6iawYlHdwVLG9hvNZMciR3xDg2rld6efHQFkXluFn8M5vAj/jlzALFImU5RwsycW2lxT8AgWqJHP/FwXehVDgSxwI8W1USr7k0EvCRCSxUx76mLXoYpOuoRHTVCQX0qURhWpURY716fGnBV5pblU0LDunqEy+aTtJ2dX4XrX9ESoM3iNOxtMSIXq6sZZ9XcI+1bTOlrFulsWXBR5AUDFhYkVIrcSdUCG6mP9T6OrgRKf2GDjxKHu6jMNocb2nTRH+yokT6ukfqMQzMJ7YXsfeDu/GTaAx4jOT3VOFio/BJK8kqOPbkvnf2FJ8cRmW0kmPrs02GOw/sO83Gqa9LIJplCB7WBFOlsZgjBQPDZgAcksNMToSWgwbJORFitWmapDPnqm4WH0cCKRb6ymqQIvOouowGnXMJvAEkY5smZkXwmJHrxvJIrFUIOOExWz+kWKF6BR81yNwYLiQpmxgUu2BUn8B3CIjsm7iVkC6sSGWZD3c5RppzK9ubopmwccsyAeGcOx7GXxhBkN8XQLAkYRbJEMNFwxyFVmfSGTf6aH6lJTYaqnhggukWkZIAzHayxPGwXgfFlLdEslyoAmXlyQbISmt3YWXjqtcljbeYQf62Sg4LM6j5sL/oYmcVEYOlVCTxKIl2gLGxwWSlkTssJu1nivIIXbMms4RhWyTHSoQDkmCgYFiAQnFAR2TCxIbZt4DSx3cxseA7QuHRpiR8TEYRaB+XF/wEZTvkXCrXjWU3Mk/oZUiIMlP8AYMcwYSMLFZpDGIrjxp5qhABdPiyO/Vaq0wyRZnQWBibHUoTlfBMuCRrpZqETd6Mjrb7Dz6SfEH5dMYCS5+jbPOynCPzFGl7YpTKXmMMUtQMBZ7yWRswyodRsU7Sg/O4k8xKyIIldABaI7BGefQJL4xphcNYz74UiF7BfdNBDHPDtqFgKyhyGM11i9gJnw5ppoxcU1uCHOcckHo5Im4qJWx4QG9EWGHrCNWZlJ1wo8ogUs/wqYCMhSI6lVwDsRpsuVyXTKGwyW4YdMZ1d7srTRl7kYmKo52kE81RKWzVGF2wHVaBbh38DCn8unhyAV6CyRZ6XSzhGJ2Iq3gmL9ZqxmI1rB/TGOGLNe77yHBUomv0XPlbR/QiZNm6oBFaitk7FeioERGiYAwZo9AUafKLqzv2ABTLJcS3C0A6qyrQOCYRnAM4bGI6c0JQV5Y0paphSlpowiAURaxWjBvsb++mAWqoftGhECjlU9icDcgAxt0qJ4aFA6L6PifVJWhpY8JnmgEAqLkGQwlxC4K3iqRzhWjCu0aUBzY7Zb/hblfpO1p7nTkbPTXXvdisNd0kUrpZkLWqNUhyX9FUXL7kLt3jMyy7I5bhJSqhKbw2V/woO+l4BKbWGhKpTEDUmun4d9G3PhoaMhURsd85N872xq7axFojyGnAjHkCjJonjfGqY62oAImbBnaTArH8atcQCa2hweHDBkosd2TqfK4irpojuBEMA4bSRruDV2ywuOfSZKmhD9R0wP4zacIiEQbBxN3ZTmhRO4iZPs56zF83BvtXxwF4Q2rcDxIswC7qcoU8QIibAls8XLShW86Ls28HLlwCqiW5XTXUbMfhEmroKDfkCi5hCTiZaY1ihSDkROHWll6aAs+Fj8NQAwk4g0N1s0z0J+OrZYAwxcE8etHiZPggJ+PEPnmDN1ukgwraiymIFIy5SXGpHSjJiiVXSC9QOuoLR94wP6Cbq4zRjWhyvmTSg5vTcSq1kPT5pgQIh7DyqiKLC0eLGeqADQuJrVRyt5WZmM/SyP3oyHy4He2sNSDQbqTjz6Rpq0KgOC3BoHdJjAlUSysRKjwUbkw2iwQCPB3SVMLIMCNxqx9WZALDRwYZr1HUjY4nKZTqFh3DWIvVOBWZCInwYx1IBlDVIZsODJH9yH3mLSaC7Y6WDiI0N8hR/M83RCgWO+CCx0/gUlompAMgAQIDBAUGBwgJCgsMDQ4PMAECAwQFBgcICQoLDA0ODw=="); + KeyFactory kFact = KeyFactory.getInstance("ML-KEM", "BC"); + + checkEncodeRecode(kFact, mlkem512_sequence); + checkEncodeRecode(kFact, mlkem512_seed_only); + checkEncodeRecode(kFact, mlkem512_wrap_seed_only); + checkEncodeRecode(kFact, mlKem512_expanded_only); + checkEncodeRecode(kFact, mlKem512_wrap_expanded_only); + } + + private void checkEncodeRecode(KeyFactory kFact, byte[] encoding) + throws Exception + { + PrivateKey key = kFact.generatePrivate(new PKCS8EncodedKeySpec(encoding)); + + assertTrue(Arrays.areEqual(encoding, key.getEncoded())); + } + public void testBasicKEMCamellia() throws Exception { From f8afa1f968fc4a4faf96cb4e54527d756c24eea1 Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 3 Feb 2025 18:07:28 +1030 Subject: [PATCH 087/890] TODO fix the bugs in SAKKEKEMExtractor --- .../crypto/kems/SAKKEKEMExtractor.java | 128 +++++++ .../crypto/kems/SAKKEKEMSGenerator.java | 351 ++++++++++++++++-- .../bouncycastle/crypto/kems/SAKKEUtils.java | 32 ++ ...ey.java => SAKKEPrivateKeyParameters.java} | 19 +- .../crypto/params/SAKKEPublicKey.java | 52 --- .../params/SAKKEPublicKeyParameters.java | 100 +++++ .../crypto/kems/test/SAKKEKEMSTest.java | 13 +- 7 files changed, 597 insertions(+), 98 deletions(-) create mode 100644 core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java rename core/src/main/java/org/bouncycastle/crypto/params/{SAKKEPrivateKey.java => SAKKEPrivateKeyParameters.java} (63%) delete mode 100644 core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java new file mode 100644 index 0000000000..caff3dffa5 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java @@ -0,0 +1,128 @@ +package org.bouncycastle.crypto.kems; + +import java.math.BigInteger; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.EncapsulatedSecretExtractor; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.params.SAKKEPrivateKeyParameters; +import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.BigIntegers; + +import static org.bouncycastle.crypto.kems.SAKKEKEMSGenerator.pairing; + +public class SAKKEKEMExtractor implements EncapsulatedSecretExtractor +{ + private final ECCurve curve; + private final BigInteger p; + private final BigInteger q; + private final ECPoint P; + private final ECPoint Z_S; + private final ECPoint K_bS; // Receiver's RSK + private final int n; // Security parameter + private final SAKKEPrivateKeyParameters privateKey; + + public SAKKEKEMExtractor(SAKKEPrivateKeyParameters privateKey) { + this.privateKey = privateKey; + SAKKEPublicKeyParameters publicKey = privateKey.getPublicParams(); + this.curve = publicKey.getCurve(); + this.q = publicKey.getQ(); + this.P = publicKey.getP(); + this.p = publicKey.getp(); + this.Z_S = publicKey.getZ(); + this.K_bS = privateKey.getPrivatePoint(); + this.n = publicKey.getN(); + } + + @Override + public byte[] extractSecret(byte[] encapsulation) { + try { + // Step 1: Parse Encapsulated Data (R_bS, H) + ECPoint R_bS = parseECPoint(encapsulation); + BigInteger H = parseH(encapsulation); + + // Step 2: Compute w = using pairing + BigInteger w = computePairing(R_bS, K_bS); + + // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) + BigInteger ssv = computeSSV(H, w); + + // Step 4: Compute r = HashToIntegerRange(SSV || b) +// BigInteger r = computeR(ssv, privateKey.getPrivatePoint()); +// +// // Step 5: Validate R_bS +// if (!validateR_bS(r, privateKey.getPrivatePoint(), R_bS)) { +// throw new IllegalStateException("Validation of R_bS failed"); +// } + + return BigIntegers.asUnsignedByteArray(n/8, ssv); + } catch (Exception e) { + throw new IllegalStateException("SAKKE extraction failed: " + e.getMessage()); + } + } + + @Override + public int getEncapsulationLength() + { + return 0; + } + + private ECPoint parseECPoint(byte[] encapsulation) { + int coordLen = (p.bitLength() + 7) / 8; + byte[] xBytes = Arrays.copyOfRange(encapsulation, 0, coordLen); + byte[] yBytes = Arrays.copyOfRange(encapsulation, coordLen, 2*coordLen); + + BigInteger x = new BigInteger(1, xBytes); + BigInteger y = new BigInteger(1, yBytes); + + return curve.createPoint(x, y).normalize(); + } + + private BigInteger parseH(byte[] encapsulation) { + int coordLen = (p.bitLength() + 7) / 8; + byte[] hBytes = Arrays.copyOfRange(encapsulation, 2*coordLen, encapsulation.length); + return new BigInteger(1, hBytes); + } + + private BigInteger computePairing(ECPoint R, ECPoint K) { + // Use your existing pairing implementation + return pairing(R, K, p, q); + } + + private BigInteger computeSSV(BigInteger H, BigInteger w) { + BigInteger twoToN = BigInteger.ONE.shiftLeft(n); + BigInteger mask = SAKKEUtils.hashToIntegerRange(w.toByteArray(), twoToN); + return H.xor(mask); + } + + private BigInteger computeR(BigInteger ssv, byte[] userId) { + byte[] ssvBytes = BigIntegers.asUnsignedByteArray(ssv); + byte[] ssvConcatB = Arrays.concatenate(ssvBytes, userId); + return SAKKEUtils.hashToIntegerRange(ssvConcatB, q); + } + + private boolean validateR_bS(BigInteger r, byte[] b, ECPoint receivedR) { + try { + // Compute [b]P + ECPoint bP = P.multiply(new BigInteger(1, b)).normalize(); + + // Compute [b]P + Z_S + ECPoint bP_plus_Z = bP.add(Z_S).normalize(); + + // Compute [r]([b]P + Z_S) + ECPoint computedR = bP_plus_Z.multiply(r).normalize(); + + return pointsEqual(computedR, receivedR); + } catch (Exception e) { + return false; + } + } + + private boolean pointsEqual(ECPoint p1, ECPoint p2) { + return p1.normalize().getXCoord().equals(p2.normalize().getXCoord()) + && p1.normalize().getYCoord().equals(p2.normalize().getYCoord()); + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index e8f53b2cd7..df94f031ae 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -3,11 +3,10 @@ import org.bouncycastle.crypto.EncapsulatedSecretGenerator; import org.bouncycastle.crypto.SecretWithEncapsulation; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; -import org.bouncycastle.crypto.params.SAKKEPublicKey; +import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECPoint; -import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; import org.bouncycastle.util.encoders.Hex; @@ -153,8 +152,21 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip // 4. Compute H = SSV XOR HashToIntegerRange( g^r, 2^n ) - BigInteger g_r = pairing(R_bS, G, p, q); + BigInteger[] result = fp2Exponentiate(p, BigInteger.ONE, g, r); + BigInteger g_r = result[0].mod(p); + g_r = g_r.modInverse(p); + g_r = g_r.multiply(result[1]).mod(p); + System.out.println("g_r " + new String(Hex.encode(g_r.toByteArray()))); + byte[] expected_g_r = Hex.decode("7D2A8438 E6291C64 9B6579EB 3B79EAE9\n" + + " 48B1DE9E 5F7D1F40 70A08F8D B6B3C515\n" + + " 6F2201AF FBB5CB9D 82AA3EC0 D0398B89\n" + + " ABC78A13 A760C0BF 3F77E63D 0DF3F1A3\n" + + " 41A41B88 11DF197F D6CD0F00 3125606F\n" + + " 4F109F40 0F7292A1 0D255E3C 0EBCCB42\n" + + " 53FB182C 68F09CF6 CD9C4A53 DA6C74AD\n" + + " 007AF36B 8BCA979D 5895E282 F483FCD6"); BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(n)); // 2^n + System.out.println(new String(Hex.encode(mask.toByteArray()))); BigInteger H = ssv.xor(mask); @@ -162,17 +174,158 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip byte[] encapsulated = encodeData(R_bS, H); return new SecretWithEncapsulationImpl( - BigIntegers.asUnsignedByteArray(n / 8, ssv), // Output SSV as key material - encapsulated + encapsulated, + BigIntegers.asUnsignedByteArray(n / 8, ssv) // Output SSV as key material ); } - private BigInteger getRecipientId(SAKKEPublicKey pubKey) + + // Helper method for F_p² exponentiation + public static BigInteger[] fp2Exponentiate( + BigInteger p, + BigInteger x, + BigInteger y, + BigInteger exponent + ) + { + BigInteger[] result = new BigInteger[2]; + sakkePointExponent(p, result, x, y, exponent); + return result; + } + + public static boolean sakkePointExponent( + BigInteger p, + BigInteger[] result, + BigInteger pointX, + BigInteger pointY, + BigInteger n + ) + { + if (n.equals(BigInteger.ZERO)) + { + return false; + } + + // Initialize result with the original point + BigInteger currentX = pointX; + BigInteger currentY = pointY; + + int numBits = n.bitLength(); + + // Process bits from MSB-1 down to 0 + for (int i = numBits - 2; i >= 0; i--) + { + // Square the current point + //sakkePointSquare(p, new BigInteger[]{currentX, currentY}); + // Compute newX = (x + y)(x - y) mod p + BigInteger xPlusY = currentX.add(currentY).mod(p); + BigInteger xMinusY = currentX.subtract(currentY).mod(p); + BigInteger newX = xPlusY.multiply(xMinusY).mod(p); + + // Compute newY = 2xy mod p + BigInteger newY = currentX.multiply(currentY).multiply(BigInteger.valueOf(2)).mod(p); + currentX = newX; + currentY = newY; + // Multiply if bit is set + if (n.testBit(i)) + { + //sakkePointsMultiply(p, currentX, currentY, pointX, pointY); + BigInteger real = currentX.multiply(pointX) + .subtract(currentY.multiply(pointY)) + .mod(p); + + // Compute imaginary part = x1*y2 + x2*y1 mod p + BigInteger imag = currentX.multiply(pointY) + .add(pointX.multiply(currentY)) + .mod(p); + + currentX = real; + currentY = imag; + } + } + + result[0] = currentX; + result[1] = currentY; + return true; + } + + + private BigInteger getRecipientId(SAKKEPublicKeyParameters pubKey) { byte[] hashedId = SAKKEUtils.hash(pubKey.getZ().getEncoded(false)); // Hash Z_S return new BigInteger(1, hashedId).mod(pubKey.getQ().subtract(BigInteger.ONE)).add(BigIntegers.TWO); } + public static class FP2Element + { + private final BigInteger a; // Real part + private final BigInteger b; // Imaginary part + private final BigInteger p; // Prime modulus + + public FP2Element(BigInteger a, BigInteger b, BigInteger p) + { + this.a = a.mod(p); + this.b = b.mod(p); + this.p = p; + } + + public FP2Element add(FP2Element other) + { + return new FP2Element(a.add(other.a), b.add(other.b), p); + } + + public FP2Element subtract(FP2Element other) + { + return new FP2Element(a.subtract(other.a), b.subtract(other.b), p); + } + + public FP2Element multiply(FP2Element other) + { + BigInteger real = a.multiply(other.a).subtract(b.multiply(other.b)).mod(p); + BigInteger imag = a.multiply(other.b).add(b.multiply(other.a)).mod(p); + return new FP2Element(real, imag, p); + } + + public FP2Element inverse() + { + BigInteger denom = a.pow(2).add(b.pow(2)).mod(p); + BigInteger invDenom = denom.modInverse(p); + return new FP2Element(a.multiply(invDenom), b.negate().multiply(invDenom), p); + } + + public FP2Element square() + { + return this.multiply(this); + } + + public FP2Element pow(BigInteger exponent) + { + // Implement exponentiation using square-and-multiply + FP2Element result = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); + FP2Element base = this; + for (int i = exponent.bitLength() - 1; i >= 0; i--) + { + result = result.square(); + if (exponent.testBit(i)) + { + result = result.multiply(base); + } + } + return result; + } + + // Getters + public BigInteger getA() + { + return a; + } + + public BigInteger getB() + { + return b; + } + } + /** * Computes the Tate-Lichtenbaum pairing ⟨P, Q⟩ as per RFC 6508. *

@@ -184,53 +337,181 @@ private BigInteger getRecipientId(SAKKEPublicKey pubKey) public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) { ECCurve curve = R.getCurve(); - ECFieldElement i = curve.fromBigInteger(BigInteger.ONE.negate()); // i = -1 in F_p^2 + FP2Element v = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); // Initialize to 1+0i - ECPoint C = R; - BigInteger c = p.add(BigInteger.ONE).divide(q); - ECFieldElement v = curve.fromBigInteger(BigInteger.ONE); // v = 1 in F_p + // Use correct exponent from RFC 6508: (p² - 1)/q + BigInteger exponent = p.pow(2).subtract(BigInteger.ONE).divide(q); - String qBits = q.subtract(BigInteger.ONE).toString(2); // Binary representation of q-1 + String qBits = q.subtract(BigInteger.ONE).toString(2); + ECPoint C = R.normalize(); for (int j = 1; j < qBits.length(); j++) - { // Skip MSB - // l = (3 * (C_x^2 - 1)) / (2 * C_y) + { + C = C.normalize(); ECFieldElement Cx = C.getAffineXCoord(); ECFieldElement Cy = C.getAffineYCoord(); - ECFieldElement l = Cx.square().multiply(curve.fromBigInteger(ECFieldElement.THREE)).subtract(curve.fromBigInteger(BigInteger.ONE)) - .divide(Cy.multiply(curve.fromBigInteger(BigIntegers.TWO))); - // v = v^2 * (l * (Q_x + C_x) + (i * Q_y - C_y)) + // Line function for doubling + ECFieldElement lNum = Cx.square().multiply(curve.fromBigInteger(BigInteger.valueOf(3))).add(curve.getA()); + ECFieldElement lDen = Cy.multiply(curve.fromBigInteger(BigInteger.valueOf(2))); + BigInteger l = lNum.divide(lDen).toBigInteger(); + + // Evaluate line at Q using F_p² arithmetic ECFieldElement Qx = Q.getAffineXCoord(); ECFieldElement Qy = Q.getAffineYCoord(); - v = v.square().multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); + FP2Element lineVal = new FP2Element( + l.multiply(Qx.add(Cx).toBigInteger()), + l.multiply(Qy.toBigInteger()), + p + ); - // Double the point - C = C.twice(); + v = v.multiply(lineVal).pow(BigInteger.valueOf(2)); + C = C.twice().normalize(); - // If the bit is 1, perform additional step if (qBits.charAt(j) == '1') { - // l = (C_y - R_y) / (C_x - R_x) - ECFieldElement Rx = R.getAffineXCoord(); - ECFieldElement Ry = R.getAffineYCoord(); - l = Cy.subtract(Ry).divide(Cx.subtract(Rx)); - - // v = v * (l * (Q_x + C_x) + (i * Q_y - C_y)) - v = v.multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); - - // C = C + R - C = C.add(R); + ECPoint Rnorm = R.normalize(); + ECFieldElement Rx = Rnorm.getAffineXCoord(); + ECFieldElement Ry = Rnorm.getAffineYCoord(); + + // Line function for addition + ECFieldElement lAddNum = Cy.subtract(Ry); + ECFieldElement lAddDen = Cx.subtract(Rx); + BigInteger lAdd = lAddNum.divide(lAddDen).toBigInteger(); + + FP2Element lineAddVal = new FP2Element( + lAdd.multiply(Qx.add(Cx).toBigInteger()), + lAdd.multiply(Qy.toBigInteger()), + p + ); + + v = v.multiply(lineAddVal); + C = C.add(Rnorm).normalize(); } } - // Compute v^c - v = curve.fromBigInteger(v.toBigInteger().pow(c.intValue())); + // Final exponentiation + FP2Element t = v.pow(exponent); - // Convert to F_p representative - return computeFpRepresentative(v, curve); + // Convert to F_p representative: b/a mod p + BigInteger a = t.getA(); + BigInteger b = t.getB(); + return b.multiply(a.modInverse(p)).mod(p); } +// public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) +// { +// ECCurve curve = R.getCurve(); +// FP2Element i = new FP2Element(BigInteger.ZERO, BigInteger.ONE, p); // i = -1 in F_p^2 +// +// ECPoint C = R.normalize(); +// BigInteger c = p.add(BigInteger.ONE).divide(q); +// //ECFieldElement v = curve.fromBigInteger(BigInteger.ONE); // v = 1 in F_p +// FP2Element v = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); +// +// String qBits = q.subtract(BigInteger.ONE).toString(2); // Binary representation of q-1 +// +// for (int j = 1; j < qBits.length(); j++) +// { // Skip MSB +// // l = (3 * (C_x^2 - 1)) / (2 * C_y) +// C = C.normalize(); // Add this line to ensure normalization +// ECFieldElement Cx = C.getAffineXCoord(); +// ECFieldElement Cy = C.getAffineXCoord(); +// BigInteger CxVal = Cx.toBigInteger(); +// BigInteger CyVal = Cy.toBigInteger(); +//// ECFieldElement l = Cx.square().multiply(curve.fromBigInteger(ECFieldElement.THREE)).subtract(curve.fromBigInteger(BigInteger.ONE)) +//// .divide(Cy.multiply(curve.fromBigInteger(BigIntegers.TWO))); +// +// // l = 3*(Cx² - 1) / (2*Cy) in F_p +// ECFieldElement lNum = Cx.square().subtract(curve.fromBigInteger(BigInteger.ONE)).multiply(curve.fromBigInteger(BigInteger.valueOf(3))); +// ECFieldElement lDen = Cy.multiply(curve.fromBigInteger(BigInteger.valueOf(2))); +// ECFieldElement l = lNum.divide(lDen); +// BigInteger lVal = l.toBigInteger(); +// +// // Evaluate line at [i]Q: (Qx, i*Qy) +// ECFieldElement Qx = Q.getAffineXCoord(); +// ECFieldElement Qy = Q.getAffineYCoord(); +// BigInteger QxVal = Qx.toBigInteger(); +// BigInteger QyVal = Qy.toBigInteger(); +// +// // Convert l*(Qx + Cx) to F_p² +// FP2Element term1 = new FP2Element(lVal.multiply(QxVal.add(CxVal)).mod(p), BigInteger.ZERO, p); +// +// // (i*Qy - Cy) in F_p²: (-Cy, Qy) +// FP2Element term2 = new FP2Element(QyVal.negate().mod(p), QyVal, p); // Wait, original term is i*Qy - Cy: i*Qy is (0, Qy), subtract Cy (Cy,0) gives (-Cy, Qy) +// FP2Element lineVal = new FP2Element(lVal, BigInteger.ZERO, p) // l is in F_p +// .multiply(new FP2Element(QxVal.add(CxVal).mod(p), BigInteger.ZERO, p)) // (Qx + Cx) in F_p +// .add(term2); // i*Qy - Cy +// +// v = v.square().multiply(lineVal); +// +// C = C.twice().normalize();; +// +// // v = v^2 * (l * (Q_x + C_x) + (i * Q_y - C_y)) +//// ECFieldElement Qx = Q.getAffineXCoord(); +//// ECFieldElement Qy = Q.getAffineYCoord(); +//// v = v.square().multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); +// +// // Double the point +//// C = C.twice(); +// if (qBits.charAt(j) == '1') +// { +// // Compute line function for addition +// ECFieldElement Rx = R.getAffineXCoord(); +// ECFieldElement Ry = R.getAffineYCoord(); +// BigInteger RxVal = Rx.toBigInteger(); +// BigInteger RyVal = Ry.toBigInteger(); +// +// ECFieldElement lAddNum = Cy.subtract(Ry); +// ECFieldElement lAddDen = Cx.subtract(Rx); +// ECFieldElement lAdd = lAddNum.divide(lAddDen); +// BigInteger lAddVal = lAdd.toBigInteger(); +// +// // Evaluate line at [i]Q +// FP2Element lineAddTerm1 = new FP2Element(lAddVal.multiply(QxVal.add(CxVal)).mod(p), BigInteger.ZERO, p); +// FP2Element lineAddTerm2 = new FP2Element(QyVal.negate().mod(p), QyVal, p); // i*Qy - Cy (Cy is current C's y) +// FP2Element lineAddVal = lineAddTerm1.add(lineAddTerm2); +// +// v = v.multiply(lineAddVal); +// +// C = C.add(R); +// } +// +//// // If the bit is 1, perform additional step +//// if (qBits.charAt(j) == '1') +//// { +//// // l = (C_y - R_y) / (C_x - R_x) +//// ECFieldElement Rx = R.getAffineXCoord(); +//// ECFieldElement Ry = R.getAffineYCoord(); +//// l = Cy.subtract(Ry).divide(Cx.subtract(Rx)); +//// +//// // v = v * (l * (Q_x + C_x) + (i * Q_y - C_y)) +//// v = v.multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); +//// +//// // C = C + R +//// C = C.add(R); +//// } +// } +// +//// // Compute v^c +//// v = curve.fromBigInteger(v.toBigInteger().modPow(c, p)); +//// +//// // Convert to F_p representative +//// return computeFpRepresentative(v, curve); +// FP2Element t = v.pow(c); +// +// // Compute representative: b/a mod p +// BigInteger a = t.getA(); +// BigInteger bVal = t.getB(); +// if (a.equals(BigInteger.ZERO)) { +// throw new ArithmeticException("Division by zero in F_p representative"); +// } +// BigInteger aInv = a.modInverse(p); +// BigInteger representative = bVal.multiply(aInv).mod(p); +// +// return representative; +// } + private static BigInteger computeFpRepresentative(ECFieldElement t, ECCurve curve) { // Characteristic of F_p @@ -238,7 +519,7 @@ private static BigInteger computeFpRepresentative(ECFieldElement t, ECCurve curv // Assume t = a + i * b in F_p² → extract a, b ECFieldElement a = t; // In F_p², a is the real part - ECFieldElement b = t.multiply(curve.fromBigInteger(BigInteger.ONE.negate())); // Imaginary part + ECFieldElement b = t.multiply(curve.fromBigInteger(BigInteger.ONE.negate().add(p))); // Imaginary part // Compute b/a mod p return b.toBigInteger().multiply(a.toBigInteger().modInverse(p)).mod(p); diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java index 05938e2ac5..45fbbb38b5 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java @@ -9,6 +9,38 @@ public class SAKKEUtils { + public static ECPoint sakkePointExponent(ECPoint point, BigInteger n) { + if (n.equals(BigInteger.ZERO)) { + throw new IllegalArgumentException("Exponent cannot be zero."); + } + + ECPoint result = point; + int N = n.bitLength() - 1; + + for (; N != 0; --N) { + result = sakkePointSquare(result); + if (n.testBit(N - 1)) { + result = sakkePointsMultiply(result, point); + } + } + return result; + } + + public static ECPoint sakkePointSquare(ECPoint point) { + BigInteger x = point.getAffineXCoord().toBigInteger(); + BigInteger y = point.getAffineYCoord().toBigInteger(); + + BigInteger bx1 = x.add(y); + BigInteger bx2 = x.subtract(y); + BigInteger newX = bx1.multiply(bx2).mod(point.getCurve().getField().getCharacteristic()); + BigInteger newY = x.multiply(y).multiply(BigInteger.valueOf(2)).mod(point.getCurve().getField().getCharacteristic()); + + return point.getCurve().createPoint(newX, newY); + } + + public static ECPoint sakkePointsMultiply(ECPoint p1, ECPoint p2) { + return p1.add(p2).normalize(); + } public static BigInteger hashToIntegerRange(byte[] input, BigInteger q) { // RFC 6508 Section 5.1: Hashing to an Integer Range diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKey.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java similarity index 63% rename from core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKey.java rename to core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java index 02e6d7c54b..2c83f35ba3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKey.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java @@ -4,14 +4,14 @@ import org.bouncycastle.math.ec.ECPoint; -public class SAKKEPrivateKey +public class SAKKEPrivateKeyParameters extends AsymmetricKeyParameter { private final BigInteger b; // User's identity private final ECPoint K; // Private key K_a - private final SAKKEPublicKey publicParams; + private final SAKKEPublicKeyParameters publicParams; - public SAKKEPrivateKey(BigInteger b, ECPoint K, SAKKEPublicKey publicParams) + public SAKKEPrivateKeyParameters(BigInteger b, ECPoint K, SAKKEPublicKeyParameters publicParams) { super(true); this.b = b; @@ -19,19 +19,18 @@ public SAKKEPrivateKey(BigInteger b, ECPoint K, SAKKEPublicKey publicParams) this.publicParams = publicParams; } - // Getters - public ECPoint getK() - { - return K; - } - public BigInteger getB() { return b; } - public SAKKEPublicKey getPublicParams() + public SAKKEPublicKeyParameters getPublicParams() { return publicParams; } + + public ECPoint getPrivatePoint() + { + return K; + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java deleted file mode 100644 index c992dbb70b..0000000000 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.bouncycastle.crypto.params; - -import java.math.BigInteger; - -import org.bouncycastle.math.ec.ECCurve; -import org.bouncycastle.math.ec.ECPoint; -import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve; - -public class SAKKEPublicKey - extends AsymmetricKeyParameter -{ - private final ECCurve curve = new SecP256R1Curve(); - private final ECPoint P; // Base point - private final ECPoint Z; // KMS Public Key: Z = [z]P - private final BigInteger q; // Subgroup order - private final int n; // SSV bit length - - public SAKKEPublicKey(ECPoint P, ECPoint Z, BigInteger q, int n) - { - super(false); - this.P = P; - this.Z = Z; - this.q = q; - this.n = n; - } - - // Getters - public ECCurve getCurve() - { - return curve; - } - - public ECPoint getP() - { - return P; - } - - public ECPoint getZ() - { - return Z; - } - - public BigInteger getQ() - { - return q; - } - - public int getN() - { - return n; - } -} diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java new file mode 100644 index 0000000000..66ce4e81ef --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java @@ -0,0 +1,100 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SAKKEPublicKeyParameters + extends AsymmetricKeyParameter +{ + // Base point + private static final BigInteger p = new BigInteger( + "997ABB1F0A563FDA65C61198DAD0657A416C0CE19CB48261BE9AE358B3E01A2E" + + "F40AAB27E2FC0F1B228730D531A59CB0E791B39FF7C88A19356D27F4A666A6D0" + + "E26C6487326B4CD4512AC5CD65681CE1B6AFF4A831852A82A7CF3C521C3C09AA" + + "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 + ); + + private static final BigInteger Px = new BigInteger( + "53FC09EE332C29AD0A7990053ED9B52A2B1A2FD60AEC69C698B2F204B6FF7CBF" + + "B5EDB6C0F6CE2308AB10DB9030B09E1043D5F22CDB9DFA55718BD9E7406CE890" + + "9760AF765DD5BCCB337C86548B72F2E1A702C3397A60DE74A7C1514DBA66910D" + + "D5CFB4CC80728D87EE9163A5B63F73EC80EC46C4967E0979880DC8ABEAE63895", 16 + ); + + private static final BigInteger Py = new BigInteger( + "0A8249063F6009F1F9F1F0533634A135D3E82016029906963D778D821E141178" + + "F5EA69F4654EC2B9E7F7F5E5F0DE55F66B598CCF9A140B2E416CFF0CA9E032B9" + + "70DAE117AD547C6CCAD696B5B7652FE0AC6F1E80164AA989492D979FC5A4D5F2" + + "13515AD7E9CB99A980BDAD5AD5BB4636ADB9B5706A67DCDE75573FD71BEF16D7", 16 + ); + + + private static final BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + + " C6A87BD1 FB94C41E 27FABE65 8E015A87\n" + + " 371E9474 4C96FEDA 449AE956 3F8BC446\n" + + " CBFDA85D 5D00EF57 7072DA8F 541721BE\n" + + " EE0FAED1 828EAB90 B99DFB01 38C78433\n" + + " 55DF0460 B4A9FD74 B4F1A32B CAFA1FFA\n" + + " D682C033 A7942BCC E3720F20 B9B7B040\n" + + " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); + + private static final BigInteger q = new BigInteger( + "265EAEC7C2958FF69971846636B4195E905B0338672D20986FA6B8D62CF8068B" + + "BD02AAC9F8BF03C6C8A1CC354C69672C39E46CE7FDF222864D5B49FD2999A9B4" + + "389B1921CC9AD335144AB173595A07386DABFD2A0C614AA0A9F3CF14870F026A" + + "A7E535ABD5A5C7C7FF38FA08E2615F6C203177C42B1EB3A1D99B601EBFAA17FB", 16 + ); + + private static final ECCurve.Fp curve = new ECCurve.Fp( + p, // Prime p + BigInteger.valueOf(-3).mod(p), // a = -3 + BigInteger.ZERO, // , + g, // Order of the subgroup (from RFC 6509) + BigInteger.ONE // Cofactor = 1 + ); + + private static final ECPoint P = curve.createPoint(Px, Py); + private final ECPoint Z; // KMS Public Key: Z = [z]P + + private static final int n = 128; // SSV bit length + + public SAKKEPublicKeyParameters(ECPoint Z) + { + super(false); + this.Z = Z; + } + + // Getters + public ECCurve getCurve() + { + return curve; + } + + public ECPoint getP() + { + return P; + } + + public ECPoint getZ() + { + return Z; + } + + public BigInteger getp() + { + return p; + } + + public BigInteger getQ() + { + return q; + } + + public int getN() + { + return n; + } +} diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index cbe940a064..485965ad59 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -4,8 +4,12 @@ import java.security.SecureRandom; +import org.bouncycastle.crypto.SecretWithEncapsulation; +import org.bouncycastle.crypto.kems.SAKKEKEMExtractor; import org.bouncycastle.crypto.kems.SAKKEKEMSGenerator; import org.bouncycastle.crypto.kems.SAKKEUtils; +import org.bouncycastle.crypto.params.SAKKEPrivateKeyParameters; +import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; @@ -146,8 +150,15 @@ public void performTest() BigInteger.ONE // Cofactor = 1 ); SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); - generator.generateEncapsulated(null); + SecretWithEncapsulation rlt = generator.generateEncapsulated(null); + ECPoint K_bS = curve.createPoint(kbx, kby); + + + SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(new BigInteger(b), K_bS, new SAKKEPublicKeyParameters(null))); + byte[] test = extractor.extractSecret(rlt.getSecret()); + + System.out.println("K_bS x:" + new String(Hex.encode(K_bS.getXCoord().toBigInteger().toByteArray()))); System.out.println("K_bS y:" + new String(Hex.encode(K_bS.getYCoord().toBigInteger().toByteArray()))); ECPoint R_bs = curve.createPoint(Rbx, Rby); From 76d616dc60520168bb4203f7d5ec4a6430d7f1d1 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 4 Feb 2025 18:07:31 +1100 Subject: [PATCH 088/890] added private key with public key test. --- .../provider/asymmetric/mlkem/BCMLKEMPrivateKey.java | 7 +++---- .../bouncycastle/pqc/jcajce/provider/test/MLDSATest.java | 3 +++ .../bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java | 3 +++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java index eb3f360400..1a3f922cbf 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java @@ -44,7 +44,7 @@ private void init(PrivateKeyInfo keyInfo) throws IOException { this.attributes = keyInfo.getAttributes(); - this.priorEncoding = keyInfo.getPrivateKey().getOctets(); + this.priorEncoding = keyInfo.getEncoded(); this.params = (MLKEMPrivateKeyParameters)PrivateKeyFactory.createKey(keyInfo); this.algorithm = Strings.toUpperCase(MLKEMParameterSpec.fromName(params.getParameters().getName()).getName()); } @@ -89,12 +89,11 @@ public byte[] getEncoded() { try { - PrivateKeyInfo pki = PrivateKeyInfoFactory.createPrivateKeyInfo(params, attributes); - if (priorEncoding != null) { - pki = new PrivateKeyInfo(pki.getPrivateKeyAlgorithm(), priorEncoding, attributes); + return priorEncoding; } + PrivateKeyInfo pki = PrivateKeyInfoFactory.createPrivateKeyInfo(params, attributes); return pki.getEncoded(); } diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java index 9ca25d2c74..e0f4acde87 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java @@ -225,6 +225,8 @@ public void testPrivateKeyRecoding() byte[] mldsa44_wrap_seed_only = Base64.decode("MDQCAQAwCwYJYIZIAWUDBAMRBCIEIAABAgMEBQYHCAkKCwwNDg8QAQIDBAUGBwgJCgsMDQ4P"); byte[] mldsa44_expanded_only = Base64.decode("MIIKFAIBADALBglghkgBZQMEAxEEggoAw/FofbPea45APAHeQqNeZL4KcvMHr8V/inNsUbmYlE0vTZbckQVhHq8sQi2F3yC7B1/aLXIta1rImyPmzsYITzPH4Iz+3/ysH0n3TDx9HFKMp5WwuOX7ZRnNqS/RaiaYltSR0LBwyi5uPgGqw1Cb/mAsJkoJuGx31QGfc1KTMSQYAjEiqU0QJjHAlAyBtiRSxkGZIFFYGAYIIEXSSC4YE2rAooADOQoIQmwbKARSQiWBRExbOIkEQEVJIEQcEUCTxhEjQCahpJFLwDEIhIFamETJqDEbEWrKyCwBKWnCQECcNgYDtAQMMWwMQTFjwCiisEUTQEGiFAoRQibaQmmaokgjFUYJREmMBHAClSABlyRQFAlQECCgRiSCJIEQEwoACSRElg0QEjBSFAgIOQLIuDCiRHHcFoIZp0zkCDCbBIkbFYnYFAQMQ1ELGSCYICDSBGhhBmAcoS0JQ0HBEGGUiIlTIkYaGGpJMJHLtCDAEDKBpgXUAgoSxQAjQm0EuUQjCBDcIkbKwkUUom0clomLKHETNpBioEBTpEDLEkocIzJiQI0JBAIaMQHaRCQbkW0KSHHgBGADoi0AFHHYFA3hgg0ayU1iloyIAEmQBJLLKC4JgQADhiGbmIiUggBcwCWDIiwByWCSEExaRC0ZkgALM0kTlZCJAnHUNEaYlJEEMU6JtAUAo0gRJ23kokkSxoREsjCSomBUyCFEAEpUJmkDkWVLloSSQkpjkm0AEQqZxFEIE4gjGCGTpkEhsEAUEyIZqEAjJg1BRlHBmCEJg4AZSGobNE5gMIWRRmTMtGBcgCVhpo2bwCDjFFLbAkJksHCZRAEKMVBjQi1jhkQQpg3EgmybBjDDqC3hRIBMoGHUpiHIMilQEgpQMA7QEClMBolUNihTAmDQAICTEm6IqEATCCEMOCoEEYwKN4maRDAjkSEJRmnIohALgAADBCoBhjFgJiIYkWmTQiHkKA4cImYLk3CSBIKUEA4gE2ibMkRjkkUKACyMRkYRoWQUl0wbmW2QBkkJR4AgR2iCJAYUMgpLKGwgKVHiBE4iBRALNYwbFwwUAGpQwAwDQSwSwQAQAi7REgrSIAlbkHEJyZAASWDMMm4YlSHiwFFIFkrbFI6IJJERJUhLlkXRyCAAk5GDoC2JyJGYQgkRRGkTJE2jRCzUGFEiF1DAghECNIJIgHAgQWlvzZh7vICP92WCMX3B8zbvb7tgGJ1FERU17RMAxgnav4gEW/oe9DFzrwvEKF4Ku25tc0FQgdmnvLQFYwpTQuDjTxsug4GZViD6JP0GvZpbvfRhSWIjeFVdQcspJ26wR3IaM4M8yRhim1xEfUf7HcopRHQ6bzJ0B7rhE60A1mouwYG8ekzSxQ4WPtqecHdT57vYaEjOqjM0g3vUKa8ybDzUUVJDXx/6pcww596LIBNHygVaBGdRyAhNYxzSg0Z9BuxN295I3WJLU13+yYsdgvMq/qoj0LVZPevCjMBLnGnBAGsXxpYmTZbiyE6v5vQO3xWB6So6vX+G82gl0T33gZEw0B0stRf76XAcHHNkQl5nz1ZdkCS2QGh8Or4nmnuatXYkfXSP8YWDFz7bgtISNta6w7iiluZOySkDb8unuWXa2lUiSbHOJJ1vJ3kxa/jOpQvKULjQ49VFJQpH+vVVZKRtXkvpkFnOmISVSx0rfus9NJCPpQm8S7DJH6pRA1ZgKYsLochGcs0pjWJGAZtUC9V0Bz7gz5XEWJzouDZZe7DfOagc/Ts7irQmpLMFuBeno9kzplKmYSDW1N88uO9CsCP4JOY/EKusbqzRu/032a0I9kozn8gyw3aAU6MArCVbfmr4/L+LV+MhLlnxe2RHy9iLw0SmVI2nJDNoLWichbLKJ7euagFMjqyw45WTmIYXKvX8rsl9TATOwcvL+0okzt8GuDyDmWej1UoGPPC74eyRfVVifPCPfb0M/o+QVwsNMggT11piJcEYmPS3hJWlTJ6t3WDIqwpvh9Bh2OBmNyJ0xMC3Zw3rNWVCLalzxf3RdOhINiSS91DtDP5FDqnZ8Br0HK5GCN00NPfWNt0FjNAWYuRoIBPqx471i+91mQAM7cRwP8izxj7D+wcGQMyQs/FA3ALXU62532rTt/eG90vPtTtj6Jvxcqx+l5oclJIZ/6qQjaqpzP6DIhlPa0e6FBiwRefdazXu2C5gPKtyY9YukXWftFnu3xDqRvxcYd/LBeK0utJYtERs4U2naS0W/8DMRrTTtiME4QZf8QSHnQoEjOS+ZNxb1yUUdmH0FKqeLgPNfXfVmD1B6ZTp/B02o3uRd7y9k4f5IKIPQedW4gdUIfycBP20lYmV5U1xcYVG6r/r1L1kfeCwlEsJ/3NsapVQx2JVuJ+EKBdqsx3zUHHSLnk3kRoZ4q0E5KRPccxgEGCpb2rK4W+hwvskcqfhKZQhYij4B10yzeno8FNIxT2vvXyALAxg2t4nSCVX7Wc1jv9ufEV3z1d5xzo5IDRHVkM1qT+V6zx+mkwXXksaVLiMNkeoy3/FF7uJxNcLajSwUwxht5LAzALa3LqlMyleIvK2fGWiqLaDkp1aYwvzc4i66sm42x2LHE6XwVVR/RnHj/1cg9rbcdVr1huGWA+TmLc0u01EYZrZnc0fGi9BnlYvm3JcEaNJ3f09cB/iN7pgMD/j8kzhykd0RsJgS5sU1ggAFkK4E9kt3WPrNqyIP1jdUZ30+Qnj6KL/bcTcvaxbC6Y1E0sIQgTSaQC9n3yhKawohtEjRDGnbqqltCw9qw6MRjTXWEPeju5YwRpN0DeVNB+3B8/sLeR7aR2tvbk7tyHvB9pW6WSInfmsGnTPqVChU7F0m/qYeP6PwjGK8TY69Pr0ziBh0JOLKYRVG2RwQnZGsGoLoXSHxZCV90Q5mIRagpGDBslvJTcxWtnUWT/ktFYkiFXHvW09hD/jqjzBRXSKOzfZnr5Xery2BIcmslYrSYbb38dY/3TmFL2k56R7nXS2uQEzhFJhT1OjOoOZ6hy6fGlNHhTXhiK0ZmF6AGb/Nw1nS0zW3HEa0wTJ5gnFUQrgkgon3NBtah5+dBsxNGn02O24vF4Lg8Va1Zj04igpuE/CctbUlJ5YKqqAj2SwL5hpBtNk8eV0srIFaFImaMcgzTJXt4tz5gOI3Ds3fkLjt3NBdLwMjw9VegdKHboCCYhVPzFY2lG++5LqJud37flp7Ikni17qpdUYO7khvsMLeUru6ouFqdV6fVdVywn2bW22Xs0DNNIfXGjnfhwhZ4AEvOqyFXmYHYqRYWOtBIxWu0aUtvtjcIi8gkDNsi0kY2Iuf6UFqwN7zqvuGYwsuC+n0UODwdi/48k1FWz/pOzqeaxo9O6AccKNEdDaXT4kwuJZsuvuKo0zv9IxDkVYoRB5Jx+vt58MtKGSxSpylWpJfw=="); byte[] mldsa44_wrap_expanded_only = Base64.decode("MIIKGAIBADALBglghkgBZQMEAxEEggoEBIIKAMPxaH2z3muOQDwB3kKjXmS+CnLzB6/Ff4pzbFG5mJRNL02W3JEFYR6vLEIthd8guwdf2i1yLWtayJsj5s7GCE8zx+CM/t/8rB9J90w8fRxSjKeVsLjl+2UZzakv0WommJbUkdCwcMoubj4BqsNQm/5gLCZKCbhsd9UBn3NSkzEkGAIxIqlNECYxwJQMgbYkUsZBmSBRWBgGCCBF0kguGBNqwKKAAzkKCEJsGygEUkIlgURMWziJBEBFSSBEHBFAk8YRI0AmoaSRS8AxCISBWphEyagxGxFqysgsASlpwkBAnDYGA7QEDDFsDEExY8AoorBFE0BBohQKEUIm2kJpmqJIIxVGCURJjARwApUgAZckUBQJUBAgoEYkgiSBEBMKAAkkRJYNEBIwUhQICDkCyLgwokRx3BaCGadM5AgwmwSJGxWJ2BQEDENRCxkgmCAg0gRoYQZgHKEtCUNBwRBhlIiJUyJGGhhqSTCRy7QgwBAygaYF1AIKEsUAI0JtBLlEIwgQ3CJGysJFFKJtHJaJiyhxEzaQYqBAU6RAyxJKHCMyYkCNCQQCGjEB2kQkG5FtCkhx4ARgA6ItABRx2BQN4YINGslNYpaMiABJkASSyyguCYEAA4Yhm5iIlIIAXMAlgyIsAclgkhBMWkQtGZIACzNJE5WQiQJx1DRGmJSRBDFOibQFAKNIESdt5KJJEsaERLIwkqJgVMghRABKVCZpA5FlS5aEkkJKY5JtABEKmcRRCBOIIxghk6ZBIbBAFBMiGahAIyYNQUZRwZghCYOAGUhqGzROYDCFkUZkzLRgXIAlYaaNm8Ag4xRS2wJCZLBwmUQBCjFQY0ItY4ZEEKYNxIJsmwYww6gt4USATKBh1KYhyDIpUBIKUDAO0BApTAaJVDYoUwJg0ACAkxJuiKhAEwghDDgqBBGMCjeJmkQwI5EhCUZpyKIQC4AAAwQqAYYxYCYiGJFpk0Ih5CgOHCJmC5NwkgSClBAOIBNomzJEY5JFCgAsjEZGEaFkFJdMG5ltkAZJCUeAIEdogiQGFDIKSyhsIClR4gROIgUQCzWMGxcMFABqUMAMA0EsEsEAEAIu0RIK0iAJW5BxCcmQAElgzDJuGJUh4sBRSBZK2xSOiCSRESVIS5ZF0cggAJORg6AticiRmEIJEURpEyRNo0Qs1BhRIhdQwIIRAjSCSIBwIEFpb82Ye7yAj/dlgjF9wfM272+7YBidRREVNe0TAMYJ2r+IBFv6HvQxc68LxCheCrtubXNBUIHZp7y0BWMKU0Lg408bLoOBmVYg+iT9Br2aW730YUliI3hVXUHLKSdusEdyGjODPMkYYptcRH1H+x3KKUR0Om8ydAe64ROtANZqLsGBvHpM0sUOFj7annB3U+e72GhIzqozNIN71CmvMmw81FFSQ18f+qXMMOfeiyATR8oFWgRnUcgITWMc0oNGfQbsTdveSN1iS1Nd/smLHYLzKv6qI9C1WT3rwozAS5xpwQBrF8aWJk2W4shOr+b0Dt8VgekqOr1/hvNoJdE994GRMNAdLLUX++lwHBxzZEJeZ89WXZAktkBofDq+J5p7mrV2JH10j/GFgxc+24LSEjbWusO4opbmTskpA2/Lp7ll2tpVIkmxziSdbyd5MWv4zqULylC40OPVRSUKR/r1VWSkbV5L6ZBZzpiElUsdK37rPTSQj6UJvEuwyR+qUQNWYCmLC6HIRnLNKY1iRgGbVAvVdAc+4M+VxFic6Lg2WXuw3zmoHP07O4q0JqSzBbgXp6PZM6ZSpmEg1tTfPLjvQrAj+CTmPxCrrG6s0bv9N9mtCPZKM5/IMsN2gFOjAKwlW35q+Py/i1fjIS5Z8XtkR8vYi8NEplSNpyQzaC1onIWyyie3rmoBTI6ssOOVk5iGFyr1/K7JfUwEzsHLy/tKJM7fBrg8g5lno9VKBjzwu+HskX1VYnzwj329DP6PkFcLDTIIE9daYiXBGJj0t4SVpUyerd1gyKsKb4fQYdjgZjcidMTAt2cN6zVlQi2pc8X90XToSDYkkvdQ7Qz+RQ6p2fAa9ByuRgjdNDT31jbdBYzQFmLkaCAT6seO9YvvdZkADO3EcD/Is8Y+w/sHBkDMkLPxQNwC11Otud9q07f3hvdLz7U7Y+ib8XKsfpeaHJSSGf+qkI2qqcz+gyIZT2tHuhQYsEXn3Ws17tguYDyrcmPWLpF1n7RZ7t8Q6kb8XGHfywXitLrSWLREbOFNp2ktFv/AzEa007YjBOEGX/EEh50KBIzkvmTcW9clFHZh9BSqni4DzX131Zg9QemU6fwdNqN7kXe8vZOH+SCiD0HnVuIHVCH8nAT9tJWJleVNcXGFRuq/69S9ZH3gsJRLCf9zbGqVUMdiVbifhCgXarMd81Bx0i55N5EaGeKtBOSkT3HMYBBgqW9qyuFvocL7JHKn4SmUIWIo+AddMs3p6PBTSMU9r718gCwMYNreJ0glV+1nNY7/bnxFd89Xecc6OSA0R1ZDNak/les8fppMF15LGlS4jDZHqMt/xRe7icTXC2o0sFMMYbeSwMwC2ty6pTMpXiLytnxloqi2g5KdWmML83OIuurJuNsdixxOl8FVUf0Zx4/9XIPa23HVa9YbhlgPk5i3NLtNRGGa2Z3NHxovQZ5WL5tyXBGjSd39PXAf4je6YDA/4/JM4cpHdEbCYEubFNYIABZCuBPZLd1j6zasiD9Y3VGd9PkJ4+ii/23E3L2sWwumNRNLCEIE0mkAvZ98oSmsKIbRI0Qxp26qpbQsPasOjEY011hD3o7uWMEaTdA3lTQftwfP7C3ke2kdrb25O7ch7wfaVulkiJ35rBp0z6lQoVOxdJv6mHj+j8IxivE2OvT69M4gYdCTiymEVRtkcEJ2RrBqC6F0h8WQlfdEOZiEWoKRgwbJbyU3MVrZ1Fk/5LRWJIhVx71tPYQ/46o8wUV0ijs32Z6+V3q8tgSHJrJWK0mG29/HWP905hS9pOeke510trkBM4RSYU9TozqDmeocunxpTR4U14YitGZhegBm/zcNZ0tM1txxGtMEyeYJxVEK4JIKJ9zQbWoefnQbMTRp9NjtuLxeC4PFWtWY9OIoKbhPwnLW1JSeWCqqgI9ksC+YaQbTZPHldLKyBWhSJmjHIM0yV7eLc+YDiNw7N35C47dzQXS8DI8PVXoHSh26AgmIVT8xWNpRvvuS6ibnd+35aeyJJ4te6qXVGDu5Ib7DC3lK7uqLhanVen1XVcsJ9m1ttl7NAzTSH1xo534cIWeABLzqshV5mB2KkWFjrQSMVrtGlLb7Y3CIvIJAzbItJGNiLn+lBasDe86r7hmMLLgvp9FDg8HYv+PJNRVs/6Ts6nmsaPTugHHCjRHQ2l0+JMLiWbLr7iqNM7/SMQ5FWKEQeScfr7efDLShksUqcpVqSX8="); + byte[] mldsa44_seed_with_pub_key = Base64.decode("MIIFVwIBATALBglghkgBZQMEAxEEIAABAgMEBQYHCAkKCwwNDg8QAQIDBAUGBwgJCgsMDQ4PgYIFIQDD8Wh9s95rjkA8Ad5Co15kvgpy8wevxX+Kc2xRuZiUTcEHWDP0MI8enUbboCTOE020U+28L6HkQZZ4JzkhORY54IkHcSKSRcKkiNkLdbm9jWKOEiQAWKuYJyQ3dcbNWa4ogK+E4LsT2jHguWA/rmSJ42Azys2/d+kIMTmmKt3Y2PyiWU5zzj/2TNOQUHaHuJhAmXAT7iCrcEz+6vCsH/dKQrcVjMjIfbibRyRMLZIrLhqXqV4vboYE/yu9jzNRUGQH5rmenF+wDtnvu7YNO/2hWYRZJKrbTgy+nuBEBAkCKorHWVaGJvmXcEPSodB+5cRIQ5+uCCswH1lJT00+SzKsCacs68iBFcte5TkAOwTzq7slL1lAptILPkzwAhNhqKGCVz4B+qRiB6CK8917el2ioTKPxyHBtgHTKPIEoIdRAgi0+uaVBPAQ21VUlYDcILBd0mAH/7rxXsIdfq5PmklvIVD0VNxeDmwIbQH0J/LNr9kKCfmHSAXgZ3Vh/DQa2eVukKv7PZiBGSCyBHukEFK+791XNCydlSNsJkvz+xvp5cEkAseNxGJoUigCW4a5r2FV37JD9FQE6PDeCrKSL428p8vMl4Vpp6I+G90JuVv7+c0ShDh9n/PdnF/hC3v9uHso/COKsYvaKN4CIo9pwiyrm44XLuDSoX+F8DcmmaLYcEqW+o06/mVz5+dTSSrWZgiXqkIkPFI44AgwC/21SI9piGtdXouzcjxmIrgg1n1utPGESQXE2xxd3AkYJtVnPpdvMoEv/MSN2OMl5LGeO0DhPD4rdRYkT1HnT1/dbb5QoKpWHBiUgxTb2xJGzljMMaFooPi9mQiUas1fSjGk/7gNKgfi88nR8HiAPm5me3NGfxcS3plrI1UewwB9ubOOBfAQiRYQI2w5ax12ygyiPSS8ekYLv1HD2TfpfdVzwIQhWYnYWDls6WhW+sJ15Al5QJ3kGe2fIQflEH6196P5Xo/4Kmv1A02jzeN3KSPb/KutI8HM4TXWAgsQX+7XmH+zzS3lXqdjRhhQ0RQ9HK4HFZ2OFw9/2KhsbR0vMIcsc0VB0zsx096ZIgMynAbyQ5rI3mFeKM7n+CD4m5KXMYSupfesNv9e8Of/FROnwR9It9N2BzQVcLWwiMuwYiqgchfUfmZFWcD2if5e23xuSt/t8hYReU/I2SydkhzTUNfwbELIkjcsxwWVkpYFIf7rT2i5Atkl6auBag5Eg/fjEuiR7RsFS1C/AcHx5lxqqbckZ/eXinG7kPDw9ZSpjHki4/PeKTeviCs4aOGtLC9kexDAU++k2cFQe7fqbUHST+eENU2m9HQ1/WfUt1426wDUdYZTqInnvk/PjUbkvE0RNbFzS+0w9Qsnh28YfLXRt/j+IcZhG4buivL2G0N/UqHtwWpyh3Wigic5tqpMaTZ54RN1/jJWmsQS3rvd8rHaZPk2CbH0Iss1PrcROzksogqZBQn4Y1zwDsF6yqdscqaMJXmH5+7REdQACUTCzU2Qj4x59doVByXnKY+Of8fAB1XTjurYgq8DrbKnzfUThC3zEodT/HTcLdP7gEIhPizDQYxNA6LlGUxGzpy7+iMs3j38WiSbToASbB1dCsFtR7Jp0P8qc9LJmX36CW87JON1pEVaTCth/HQS8AXUsGU7HSorD/CkCwehxKlABcRbpPTIZC/dtR9LkrkI5T9AyTWMDS2pPzICo+PUhOOkU3pSKS6o20VnvZtj2uU8M31K6I16H5KbqXMr"); + KeyFactory kFact = KeyFactory.getInstance("ML-DSA", "BC"); checkEncodeRecode(kFact, mldsa44_sequence); @@ -232,6 +234,7 @@ public void testPrivateKeyRecoding() checkEncodeRecode(kFact, mldsa44_wrap_seed_only); checkEncodeRecode(kFact, mldsa44_expanded_only); checkEncodeRecode(kFact, mldsa44_wrap_expanded_only); + checkEncodeRecode(kFact, mldsa44_seed_with_pub_key); } private void checkEncodeRecode(KeyFactory kFact, byte[] encoding) diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java index fdb2284fd7..1db24e0a05 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java @@ -176,6 +176,8 @@ public void testPrivateKeyRecoding() byte[] mlkem512_wrap_seed_only = Base64.decode("MFQCAQAwCwYJYIZIAWUDBAQBBEIEQAABAgMEBQYHCAkKCwwNDg8QAQIDBAUGBwgJCgsMDQ4PIAECAwQFBgcICQoLDA0ODzABAgMEBQYHCAkKCwwNDg8="); byte[] mlKem512_expanded_only = Base64.decode("MIIGdAIBADALBglghkgBZQMEBAEEggZgWCF6H9yVm+nHHlpz1fOL8Oibz2GOuXSDQhwXrSnDraa2fYB0LxV12Tsp7FggB1xmvIg+GBXJoJcJ6APDuDCqE/RtW5eSZwKLI/cqeubNablS7SCQSORrZiNuVkoGgPikT9hzUDhvDhdE7tuD+cF1UfpECGZOFDFfaKeFufJlX/kcIAwFjgXOPqJrBiUd3BUsb2G81kxyJHfEODauV3p58dAWReW4Wfwzm8CP+OXMAsUiZTlHCzJxbaXFPwCBaokc/8XBd6FUOBLHAjxbVRKvuTQS8JEJLFTHvqYtehik66hEdNUJBfSpRGFalRFjvXp8acFXmluVTQsO6eoTL5pO0nZ1fhetf0RKgzeI07G0xIherqxln1dwj7VtM6WsW6WxZcFHkBQMWFiRUitxJ1QIbqY/1Po6uBEp/YYOPEoe7qMw2hxvadNEf7KiRPq6R+oxDMwnthex94O78ZNoDHiM5PdU4WKj8EkrySo49uS+d/YUnxxGZbSSY+uzTYY7D+w7zcapr0sgmmUIHtYEU6WxmCMFA8NmABySw0xOhJaDBsk5EWK1aZqkM+eqbhYfRwIpFvrKapAi86i6jAadcwm8ASRjmyZmRfCYkevG8kisVQg44TFbP6RYoXoFHzXI3BguJCmbGBS7YFSfwHcIiOybuJWQLqxIZZkPdzlGmnMr25uimbBxyzIB4Zw7HsZfGEGQ3xdAsCRhFskQw0XDHIVWZ9IZN/pofqUlNhqqeGCC6RaRkgDMdrLE8bBeB8WUt0SyXKgCZeXJBshKa3dhZeOq1yWNt5hB/rZKDgszqPmwv+hiZxURg6VUJPEoiXaAsbHBZKWROywm7WeK8ghdsyazhGFbJMdKhAOSYKBgWIBCcUBHZMLEhtm3gNLHdzGx4DtC4dGmJHxMRhFoH5cX/ARlO+RcKteNZTcyT+hlSIgyU/wBgxzBhIwsVmkMYiuPGnmqEAF0+LI79VqrTDJFmdBYGJsdShOV8Ey4JGulmoRN3oyOtvsPPpJ8Qfl0xgJLn6Ns87KcI/MUaXtilMpeYwxS1AwFnvJZGzDKh1GxTtKD87iTzErIgiV0AFojsEZ59AkvjGmFw1jPvhSIXsF900EMc8O2oWArKHIYzXWL2AmfDmmmjFxTW4Ic5xyQejkibiolbHhAb0RYYesI1ZmUnXCjyiBSz/CpgIyFIjqVXAOxGmy5XJdMobDJbhh0xnV3uytNGXuRiYqjnaQTzVEpbNUYXbAdVoFuHfwMKfy6eHIBXoLJFnpdLOEYnYireCYv1mrGYjWsH9MY4Ys17vvIcFSia/Rc+VtH9CJk2bqgEVqK2TsV6KgREaJgDBmj0BRp8ourO/YAFMslxLcLQDqrKtA4JhGcAzhsYjpzQlBXljSlqmFKWmjCIBRFrFaMG+xv76YBaqh+0aEQKOVT2JwNyADG3SonhoUDovo+J9UlaGljwmeaAQCouQZDCXELgreKpHOFaMK7RpQHNjtlv+FuV+k7WnudORs9Nde92Kw13SRSulmQtao1SHJf0VRcvuQu3eMzLLsjluElKqEpvDZX/Cg76XgEptYaEqlMQNSa6fh30bc+GhoyFRGx3zk3zvbGrtrEWiPIacCMeQKMmieN8apjragAiZsGdpMCsfxq1xAJraHB4cMGSix3ZOp8riKumiO4EQwDhtJGu4NXbLC459JkqaEP1HTA/jNpwiIRBsHE3dlOaFE7iJk+znrMXzcG+1fHAXhDatwPEizALupyhTxAiJsCWzxctKFbzouzbwcuXAKqJbldNdRsx+ESaugoN+QKLmEJOJlpjWKFIORE4daWXpoCz4WPw1ADCTiDQ3WzTPQn46tlgDDFwTx60eJk+CAn48Q+eYM3W6SDCtqLKYgUjLlJcakdKMmKJVdIL1A66gtH3jA/oJurjNGNaHK+ZNKDm9NxKrWQ9PmmBAiHsPKqIosLR4sZ6oANC4mtVHK3lZmYz9LI/ejIfLgd7aw1INBupOPPpGmrQqA4LcGgd0mMCVRLKxEqPBRuTDaLBAI8HdJUwsgwI3GrH1ZkAsNHBhmvUdSNjicplOoWHcNYi9U4FZkIifBjHUgGUNUhmw4Mkf3IfeYtJoLtjpYOIjQ3yFH8zzdEKBY74ILHT+BSWiakAyABAgMEBQYHCAkKCwwNDg8wAQIDBAUGBwgJCgsMDQ4P"); byte[] mlKem512_wrap_expanded_only = Base64.decode("MIIGeAIBADALBglghkgBZQMEBAEEggZkBIIGYFgheh/clZvpxx5ac9Xzi/Dom89hjrl0g0IcF60pw62mtn2AdC8Vddk7KexYIAdcZryIPhgVyaCXCegDw7gwqhP0bVuXkmcCiyP3KnrmzWm5Uu0gkEjka2YjblZKBoD4pE/Yc1A4bw4XRO7bg/nBdVH6RAhmThQxX2inhbnyZV/5HCAMBY4Fzj6iawYlHdwVLG9hvNZMciR3xDg2rld6efHQFkXluFn8M5vAj/jlzALFImU5RwsycW2lxT8AgWqJHP/FwXehVDgSxwI8W1USr7k0EvCRCSxUx76mLXoYpOuoRHTVCQX0qURhWpURY716fGnBV5pblU0LDunqEy+aTtJ2dX4XrX9ESoM3iNOxtMSIXq6sZZ9XcI+1bTOlrFulsWXBR5AUDFhYkVIrcSdUCG6mP9T6OrgRKf2GDjxKHu6jMNocb2nTRH+yokT6ukfqMQzMJ7YXsfeDu/GTaAx4jOT3VOFio/BJK8kqOPbkvnf2FJ8cRmW0kmPrs02GOw/sO83Gqa9LIJplCB7WBFOlsZgjBQPDZgAcksNMToSWgwbJORFitWmapDPnqm4WH0cCKRb6ymqQIvOouowGnXMJvAEkY5smZkXwmJHrxvJIrFUIOOExWz+kWKF6BR81yNwYLiQpmxgUu2BUn8B3CIjsm7iVkC6sSGWZD3c5RppzK9ubopmwccsyAeGcOx7GXxhBkN8XQLAkYRbJEMNFwxyFVmfSGTf6aH6lJTYaqnhggukWkZIAzHayxPGwXgfFlLdEslyoAmXlyQbISmt3YWXjqtcljbeYQf62Sg4LM6j5sL/oYmcVEYOlVCTxKIl2gLGxwWSlkTssJu1nivIIXbMms4RhWyTHSoQDkmCgYFiAQnFAR2TCxIbZt4DSx3cxseA7QuHRpiR8TEYRaB+XF/wEZTvkXCrXjWU3Mk/oZUiIMlP8AYMcwYSMLFZpDGIrjxp5qhABdPiyO/Vaq0wyRZnQWBibHUoTlfBMuCRrpZqETd6Mjrb7Dz6SfEH5dMYCS5+jbPOynCPzFGl7YpTKXmMMUtQMBZ7yWRswyodRsU7Sg/O4k8xKyIIldABaI7BGefQJL4xphcNYz74UiF7BfdNBDHPDtqFgKyhyGM11i9gJnw5ppoxcU1uCHOcckHo5Im4qJWx4QG9EWGHrCNWZlJ1wo8ogUs/wqYCMhSI6lVwDsRpsuVyXTKGwyW4YdMZ1d7srTRl7kYmKo52kE81RKWzVGF2wHVaBbh38DCn8unhyAV6CyRZ6XSzhGJ2Iq3gmL9ZqxmI1rB/TGOGLNe77yHBUomv0XPlbR/QiZNm6oBFaitk7FeioERGiYAwZo9AUafKLqzv2ABTLJcS3C0A6qyrQOCYRnAM4bGI6c0JQV5Y0paphSlpowiAURaxWjBvsb++mAWqoftGhECjlU9icDcgAxt0qJ4aFA6L6PifVJWhpY8JnmgEAqLkGQwlxC4K3iqRzhWjCu0aUBzY7Zb/hblfpO1p7nTkbPTXXvdisNd0kUrpZkLWqNUhyX9FUXL7kLt3jMyy7I5bhJSqhKbw2V/woO+l4BKbWGhKpTEDUmun4d9G3PhoaMhURsd85N872xq7axFojyGnAjHkCjJonjfGqY62oAImbBnaTArH8atcQCa2hweHDBkosd2TqfK4irpojuBEMA4bSRruDV2ywuOfSZKmhD9R0wP4zacIiEQbBxN3ZTmhRO4iZPs56zF83BvtXxwF4Q2rcDxIswC7qcoU8QIibAls8XLShW86Ls28HLlwCqiW5XTXUbMfhEmroKDfkCi5hCTiZaY1ihSDkROHWll6aAs+Fj8NQAwk4g0N1s0z0J+OrZYAwxcE8etHiZPggJ+PEPnmDN1ukgwraiymIFIy5SXGpHSjJiiVXSC9QOuoLR94wP6Cbq4zRjWhyvmTSg5vTcSq1kPT5pgQIh7DyqiKLC0eLGeqADQuJrVRyt5WZmM/SyP3oyHy4He2sNSDQbqTjz6Rpq0KgOC3BoHdJjAlUSysRKjwUbkw2iwQCPB3SVMLIMCNxqx9WZALDRwYZr1HUjY4nKZTqFh3DWIvVOBWZCInwYx1IBlDVIZsODJH9yH3mLSaC7Y6WDiI0N8hR/M83RCgWO+CCx0/gUlompAMgAQIDBAUGBwgJCgsMDQ4PMAECAwQFBgcICQoLDA0ODw=="); + byte[] mlkem512_seed_with_pub_key = Base64.decode("MIIDdwIBATALBglghkgBZQMEBAEEQAABAgMEBQYHCAkKCwwNDg8QAQIDBAUGBwgJCgsMDQ4PIAECAwQFBgcICQoLDA0ODzABAgMEBQYHCAkKCwwNDg+BggMhAPOynCPzFGl7YpTKXmMMUtQMBZ7yWRswyodRsU7Sg/O4k8xKyIIldABaI7BGefQJL4xphcNYz74UiF7BfdNBDHPDtqFgKyhyGM11i9gJnw5ppoxcU1uCHOcckHo5Im4qJWx4QG9EWGHrCNWZlJ1wo8ogUs/wqYCMhSI6lVwDsRpsuVyXTKGwyW4YdMZ1d7srTRl7kYmKo52kE81RKWzVGF2wHVaBbh38DCn8unhyAV6CyRZ6XSzhGJ2Iq3gmL9ZqxmI1rB/TGOGLNe77yHBUomv0XPlbR/QiZNm6oBFaitk7FeioERGiYAwZo9AUafKLqzv2ABTLJcS3C0A6qyrQOCYRnAM4bGI6c0JQV5Y0paphSlpowiAURaxWjBvsb++mAWqoftGhECjlU9icDcgAxt0qJ4aFA6L6PifVJWhpY8JnmgEAqLkGQwlxC4K3iqRzhWjCu0aUBzY7Zb/hblfpO1p7nTkbPTXXvdisNd0kUrpZkLWqNUhyX9FUXL7kLt3jMyy7I5bhJSqhKbw2V/woO+l4BKbWGhKpTEDUmun4d9G3PhoaMhURsd85N872xq7axFojyGnAjHkCjJonjfGqY62oAImbBnaTArH8atcQCa2hweHDBkosd2TqfK4irpojuBEMA4bSRruDV2ywuOfSZKmhD9R0wP4zacIiEQbBxN3ZTmhRO4iZPs56zF83BvtXxwF4Q2rcDxIswC7qcoU8QIibAls8XLShW86Ls28HLlwCqiW5XTXUbMfhEmroKDfkCi5hCTiZaY1ihSDkROHWll6aAs+Fj8NQAwk4g0N1s0z0J+OrZYAwxcE8etHiZPggJ+PEPnmDN1ukgwraiymIFIy5SXGpHSjJiiVXSC9QOuoLR94wP6Cbq4zRjWhyvmTSg5vTcSq1kPT5pgQIh7DyqiKLC0eLGeqADQuJrVRyt5WZmM/SyP3oyHy4He2sNSDQbqTjz6Rpq0KgOC3BoHdJjAlUSysRKjwUbkw2iwQCPB3SVMLIMCNxqx9WZALDRwYZr1HUjY4nKZTqFh3DWIvVOBWZCInwYx1IBlDVIZsODJH9"); + KeyFactory kFact = KeyFactory.getInstance("ML-KEM", "BC"); checkEncodeRecode(kFact, mlkem512_sequence); @@ -183,6 +185,7 @@ public void testPrivateKeyRecoding() checkEncodeRecode(kFact, mlkem512_wrap_seed_only); checkEncodeRecode(kFact, mlKem512_expanded_only); checkEncodeRecode(kFact, mlKem512_wrap_expanded_only); + checkEncodeRecode(kFact, mlkem512_seed_with_pub_key); } private void checkEncodeRecode(KeyFactory kFact, byte[] encoding) From bf356f5c3bf7b6369ed15a2232bc5f5f06b88b3a Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 4 Feb 2025 18:32:08 +1030 Subject: [PATCH 089/890] TODO pairing --- .../crypto/kems/SAKKEKEMExtractor.java | 300 +++++++++++-- .../crypto/kems/SAKKEKEMSGenerator.java | 396 +++++++++++------- 2 files changed, 490 insertions(+), 206 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java index caff3dffa5..0e23ab22cb 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java @@ -14,7 +14,8 @@ import static org.bouncycastle.crypto.kems.SAKKEKEMSGenerator.pairing; -public class SAKKEKEMExtractor implements EncapsulatedSecretExtractor +public class SAKKEKEMExtractor + implements EncapsulatedSecretExtractor { private final ECCurve curve; private final BigInteger p; @@ -25,7 +26,8 @@ public class SAKKEKEMExtractor implements EncapsulatedSecretExtractor private final int n; // Security parameter private final SAKKEPrivateKeyParameters privateKey; - public SAKKEKEMExtractor(SAKKEPrivateKeyParameters privateKey) { + public SAKKEKEMExtractor(SAKKEPrivateKeyParameters privateKey) + { this.privateKey = privateKey; SAKKEPublicKeyParameters publicKey = privateKey.getPublicParams(); this.curve = publicKey.getCurve(); @@ -38,14 +40,18 @@ public SAKKEKEMExtractor(SAKKEPrivateKeyParameters privateKey) { } @Override - public byte[] extractSecret(byte[] encapsulation) { - try { + public byte[] extractSecret(byte[] encapsulation) + { + try + { // Step 1: Parse Encapsulated Data (R_bS, H) - ECPoint R_bS = parseECPoint(encapsulation); - BigInteger H = parseH(encapsulation); + ECPoint R_bS = curve.decodePoint(Arrays.copyOfRange(encapsulation, 0, 257)); + BigInteger H = new BigInteger(Arrays.copyOfRange(encapsulation, 257, 274)); // Step 2: Compute w = using pairing - BigInteger w = computePairing(R_bS, K_bS); +// BigInteger w = computeTLPairing(new BigInteger[] {R_bS.getXCoord().toBigInteger(), R_bS.getYCoord().toBigInteger()}, +// new BigInteger[] {K_bS.getXCoord().toBigInteger(), K_bS.getYCoord().toBigInteger()}, this.p, this.q); + BigInteger w = computePairing(R_bS, K_bS, p, q); // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) BigInteger ssv = computeSSV(H, w); @@ -58,8 +64,10 @@ public byte[] extractSecret(byte[] encapsulation) { // throw new IllegalStateException("Validation of R_bS failed"); // } - return BigIntegers.asUnsignedByteArray(n/8, ssv); - } catch (Exception e) { + return BigIntegers.asUnsignedByteArray(n / 8, ssv); + } + catch (Exception e) + { throw new IllegalStateException("SAKKE extraction failed: " + e.getMessage()); } } @@ -70,59 +78,263 @@ public int getEncapsulationLength() return 0; } - private ECPoint parseECPoint(byte[] encapsulation) { - int coordLen = (p.bitLength() + 7) / 8; - byte[] xBytes = Arrays.copyOfRange(encapsulation, 0, coordLen); - byte[] yBytes = Arrays.copyOfRange(encapsulation, coordLen, 2*coordLen); + private BigInteger computePairing(ECPoint R, ECPoint K) + { + // Use your existing pairing implementation + return pairing(R, K, p, q); + } + + private BigInteger computeSSV(BigInteger H, BigInteger w) + { + BigInteger twoToN = BigInteger.ONE.shiftLeft(n); + BigInteger mask = SAKKEUtils.hashToIntegerRange(w.toByteArray(), twoToN); + return H.xor(mask); + } + + public static BigInteger computeTLPairing( + BigInteger[] R, // C = (Rx, Ry) + BigInteger[] Q, // Q = (Qx, Qy) + BigInteger p, + BigInteger q + ) + { + BigInteger qMinus1 = q.subtract(BigInteger.ONE); + int N = qMinus1.bitLength() - 1; + + // Initialize V = (1, 0) + BigInteger[] V = {BigInteger.ONE, BigInteger.ZERO}; + // Initialize C = R + BigInteger[] C = {R[0], R[1]}; + + for (; N > 0; N--) + { + // V = V^2 + pointSquare(V, p); + + // Compute line function T + BigInteger[] T = computeLineFunctionT(C, Q, p); + + // V = V * T + pointMultiply(V, T, p); + + // C = 2*C (point doubling) + pointDouble(C, p); - BigInteger x = new BigInteger(1, xBytes); - BigInteger y = new BigInteger(1, yBytes); + if (qMinus1.testBit(N - 1)) + { + // Compute addition line function + BigInteger[] TAdd = computeLineFunctionAdd(C, R, Q, p); - return curve.createPoint(x, y).normalize(); + // V = V * TAdd + pointMultiply(V, TAdd, p); + + // C = C + R (point addition) + pointAdd(C, R, p); + } + } + + // Final squaring + pointSquare(V, p); + pointSquare(V, p); + + // Compute w = (Vy * Vx^{-1}) mod p + BigInteger VxInv = V[0].modInverse(p); + return V[1].multiply(VxInv).mod(p); } - private BigInteger parseH(byte[] encapsulation) { - int coordLen = (p.bitLength() + 7) / 8; - byte[] hBytes = Arrays.copyOfRange(encapsulation, 2*coordLen, encapsulation.length); - return new BigInteger(1, hBytes); + private static void pointSquare(BigInteger[] point, BigInteger p) + { + BigInteger x = point[0]; + BigInteger y = point[1]; + + // x = (x + y)(x - y) mod p + BigInteger xPlusY = x.add(y).mod(p); + BigInteger xMinusY = x.subtract(y).mod(p); + BigInteger newX = xPlusY.multiply(xMinusY).mod(p); + + // y = 2xy mod p + BigInteger newY = x.multiply(y).multiply(BigInteger.valueOf(2)).mod(p); + + point[0] = newX; + point[1] = newY; } - private BigInteger computePairing(ECPoint R, ECPoint K) { - // Use your existing pairing implementation - return pairing(R, K, p, q); + private static void pointMultiply(BigInteger[] a, BigInteger[] b, BigInteger p) + { + // Complex multiplication (a + bi)*(c + di) = (ac - bd) + (ad + bc)i + BigInteger real = a[0].multiply(b[0]).subtract(a[1].multiply(b[1])).mod(p); + BigInteger imag = a[0].multiply(b[1]).add(a[1].multiply(b[0])).mod(p); + + a[0] = real; + a[1] = imag; } - private BigInteger computeSSV(BigInteger H, BigInteger w) { - BigInteger twoToN = BigInteger.ONE.shiftLeft(n); - BigInteger mask = SAKKEUtils.hashToIntegerRange(w.toByteArray(), twoToN); - return H.xor(mask); + private static void pointDouble(BigInteger[] point, BigInteger p) + { + // Elliptic curve point doubling formulas + BigInteger x = point[0]; + BigInteger y = point[1]; + + BigInteger slope = x.pow(2).multiply(BigInteger.valueOf(3)) + .mod(p) + .multiply(y.multiply(BigInteger.valueOf(2)).modInverse(p)) + .mod(p); + + BigInteger newX = slope.pow(2).subtract(x.multiply(BigInteger.valueOf(2))).mod(p); + BigInteger newY = slope.multiply(x.subtract(newX)).subtract(y).mod(p); + + point[0] = newX; + point[1] = newY; } - private BigInteger computeR(BigInteger ssv, byte[] userId) { - byte[] ssvBytes = BigIntegers.asUnsignedByteArray(ssv); - byte[] ssvConcatB = Arrays.concatenate(ssvBytes, userId); - return SAKKEUtils.hashToIntegerRange(ssvConcatB, q); + private static void pointAdd(BigInteger[] a, BigInteger[] b, BigInteger p) + { + // Elliptic curve point addition + BigInteger x1 = a[0], y1 = a[1]; + BigInteger x2 = b[0], y2 = b[1]; + + BigInteger slope = y2.subtract(y1) + .multiply(x2.subtract(x1).modInverse(p)) + .mod(p); + + BigInteger newX = slope.pow(2).subtract(x1).subtract(x2).mod(p); + BigInteger newY = slope.multiply(x1.subtract(newX)).subtract(y1).mod(p); + + a[0] = newX; + a[1] = newY; } - private boolean validateR_bS(BigInteger r, byte[] b, ECPoint receivedR) { - try { - // Compute [b]P - ECPoint bP = P.multiply(new BigInteger(1, b)).normalize(); + private static BigInteger[] computeLineFunctionT( + BigInteger[] C, + BigInteger[] Q, + BigInteger p + ) + { + // Line function evaluation for doubling + BigInteger Cx = C[0], Cy = C[1]; + BigInteger Qx = Q[0], Qy = Q[1]; - // Compute [b]P + Z_S - ECPoint bP_plus_Z = bP.add(Z_S).normalize(); + // l = (3Cx² + a)/(2Cy) but a=0 for many curves + BigInteger numerator = Cx.pow(2).multiply(BigInteger.valueOf(3)).mod(p); + BigInteger denominator = Cy.multiply(BigInteger.valueOf(2)).mod(p); + BigInteger l = numerator.multiply(denominator.modInverse(p)).mod(p); - // Compute [r]([b]P + Z_S) - ECPoint computedR = bP_plus_Z.multiply(r).normalize(); + // T = l*(Qx + Cx) - 2Qy + BigInteger tReal = l.multiply(Qx.add(Cx).mod(p)).mod(p); + BigInteger tImag = l.multiply(Qy).negate().mod(p); - return pointsEqual(computedR, receivedR); - } catch (Exception e) { - return false; - } + return new BigInteger[]{tReal, tImag}; } - private boolean pointsEqual(ECPoint p1, ECPoint p2) { + private static BigInteger[] computeLineFunctionAdd( + BigInteger[] C, + BigInteger[] R, + BigInteger[] Q, + BigInteger p + ) + { + // Line function evaluation for addition + BigInteger Cx = C[0], Cy = C[1]; + BigInteger Rx = R[0], Ry = R[1]; + BigInteger Qx = Q[0], Qy = Q[1]; + + // l = (Cy - Ry)/(Cx - Rx) + BigInteger numerator = Cy.subtract(Ry).mod(p); + BigInteger denominator = Cx.subtract(Rx).mod(p); + BigInteger l = numerator.multiply(denominator.modInverse(p)).mod(p); + + // T = l*(Qx + Cx) - Qy + BigInteger tReal = l.multiply(Qx.add(Cx).mod(p)).mod(p); + BigInteger tImag = l.multiply(Qy).negate().mod(p); + + return new BigInteger[]{tReal, tImag}; + } + + private boolean pointsEqual(ECPoint p1, ECPoint p2) + { return p1.normalize().getXCoord().equals(p2.normalize().getXCoord()) && p1.normalize().getYCoord().equals(p2.normalize().getYCoord()); } + + public static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) + { + BigInteger c = p.add(BigInteger.ONE).divide(q); // Compute c = (p+1)/q + BigInteger[] v = new BigInteger[]{BigInteger.ONE, BigInteger.ZERO}; // v = (1,0) in F_p^2 + ECPoint C = R; + + BigInteger qMinusOne = q.subtract(BigInteger.ONE); + int numBits = qMinusOne.bitLength(); + + // Miller loop + for (int i = numBits - 2; i >= 0; i--) + { + v = fp2SquareAndAccumulate(v, C, Q, p); + C = C.twice().normalize(); // C = [2]C + + if (qMinusOne.testBit(i)) + { + v = fp2MultiplyAndAccumulate(v, C, R, Q, p); + C = C.add(R).normalize(); + } + } + + // Final exponentiation: t = v^c + return fp2FinalExponentiation(v, p, c); + } + + private static BigInteger[] fp2SquareAndAccumulate(BigInteger[] v, ECPoint C, ECPoint Q, BigInteger p) + { + BigInteger Cx = C.getAffineXCoord().toBigInteger(); + BigInteger Cy = C.getAffineYCoord().toBigInteger(); + BigInteger Qx = Q.getAffineXCoord().toBigInteger(); + BigInteger Qy = Q.getAffineYCoord().toBigInteger(); + + // Compute l = (3 * (Cx^2 - 1)) / (2 * Cy) mod p + BigInteger l = Cx.multiply(Cx).mod(p).subtract(BigInteger.ONE).multiply(BigInteger.valueOf(3)).mod(p) + .multiply(Cy.multiply(BigInteger.valueOf(2)).modInverse(p)) + .mod(p); + + // Compute v = v^2 * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) + v = fp2Multiply(v[0], v[1], v[0], v[1], p); + return fp2Multiply(v[0], v[1], l.multiply(Qx.add(Cx)), (Qy.subtract(Cy)), p); + } + + private static BigInteger[] fp2MultiplyAndAccumulate(BigInteger[] v, ECPoint C, ECPoint R, ECPoint Q, BigInteger p) + { + BigInteger Cx = C.getAffineXCoord().toBigInteger(); + BigInteger Cy = C.getAffineYCoord().toBigInteger(); + BigInteger Rx = R.getAffineXCoord().toBigInteger(); + BigInteger Ry = R.getAffineYCoord().toBigInteger(); + BigInteger Qx = Q.getAffineXCoord().toBigInteger(); + BigInteger Qy = Q.getAffineYCoord().toBigInteger(); + + // Compute l = (Cy - Ry) / (Cx - Rx) mod p + BigInteger l = Cy.subtract(Ry) + .multiply(Cx.subtract(Rx).modInverse(p)) + .mod(p); + + // Compute v = v * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) + return fp2Multiply(v[0], v[1], l.multiply(Qx.add(Cx)), Qy.subtract(Cy), p); + } + + + private static BigInteger[] fp2Multiply(BigInteger x_real, BigInteger x_imag, BigInteger y_real, BigInteger y_imag, BigInteger p) + { + // Multiply v = (a + i*b) * scalar + return new BigInteger[]{ + x_real.multiply(y_real).subtract(x_imag.multiply(y_imag)).mod(p), + x_real.multiply(y_imag).add(x_imag.multiply(y_real)).mod(p) + }; + } + + private static BigInteger fp2FinalExponentiation(BigInteger[] v, BigInteger p, BigInteger c) + { + // Compute representative in F_p: return b/a (mod p) +// BigInteger v0 = v[0].modPow(c, p); +// BigInteger v1 = v[1].modPow(c, p); +// return v1.multiply(v0.modInverse(p)).mod(p); + v = fp2Multiply(v[0], v[1], v[0], v[1], p); + v = fp2Multiply(v[0], v[1], v[0], v[1], p); + return v[1].multiply(v[0].modInverse(p)).mod(p); + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index df94f031ae..59895f5738 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -171,7 +171,7 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip BigInteger H = ssv.xor(mask); // 5. Encode encapsulated data (R_bS, H) - byte[] encapsulated = encodeData(R_bS, H); + byte[] encapsulated = Arrays.concatenate(R_bS.getEncoded(false), H.toByteArray()); return new SecretWithEncapsulationImpl( encapsulated, @@ -337,207 +337,279 @@ public BigInteger getB() public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) { ECCurve curve = R.getCurve(); - FP2Element v = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); // Initialize to 1+0i + //FP2Element v = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); // Initialize to 1+0i - // Use correct exponent from RFC 6508: (p² - 1)/q - BigInteger exponent = p.pow(2).subtract(BigInteger.ONE).divide(q); + // Use correct exponent from RFC 6508: (p+1)/q + BigInteger exponent = p.add(BigInteger.ONE).divide(q); String qBits = q.subtract(BigInteger.ONE).toString(2); ECPoint C = R.normalize(); - - for (int j = 1; j < qBits.length(); j++) + BigInteger vx = BigInteger.ONE; + BigInteger vy = BigInteger.ZERO; + + // Evaluate line at Q using F_p² arithmetic + BigInteger Qx = Q.getAffineXCoord().toBigInteger(); + BigInteger Qy = Q.getAffineYCoord().toBigInteger(); + + ECPoint Rnorm = R.normalize(); + BigInteger Rx = Rnorm.getAffineXCoord().toBigInteger(); + BigInteger Ry = Rnorm.getAffineYCoord().toBigInteger(); + int n = q.subtract(BigInteger.ONE).bitLength() - 1; + for (int j = n; j > 0; j--) { + /* + * BigInteger xPlusY = currentX.add(currentY).mod(p); + BigInteger xMinusY = currentX.subtract(currentY).mod(p); + BigInteger newX = xPlusY.multiply(xMinusY).mod(p); + + // Compute newY = 2xy mod p + BigInteger newY = currentX.multiply(currentY).multiply(BigInteger.valueOf(2)).mod(p); + * */ + BigInteger xPlusY = vx.add(vy).mod(p); + BigInteger xMinusY = vx.subtract(vy).mod(p); + BigInteger newX = xPlusY.multiply(xMinusY).mod(p); + + // Compute newY = 2xy mod p + BigInteger newY = vx.multiply(vy).multiply(BigInteger.valueOf(2)).mod(p); + vx = newX; + vy = newY; + C = C.normalize(); - ECFieldElement Cx = C.getAffineXCoord(); - ECFieldElement Cy = C.getAffineYCoord(); + BigInteger Cx = C.getAffineXCoord().toBigInteger(); + BigInteger Cy = C.getAffineYCoord().toBigInteger(); + // Line function for doubling - ECFieldElement lNum = Cx.square().multiply(curve.fromBigInteger(BigInteger.valueOf(3))).add(curve.getA()); - ECFieldElement lDen = Cy.multiply(curve.fromBigInteger(BigInteger.valueOf(2))); - BigInteger l = lNum.divide(lDen).toBigInteger(); - - // Evaluate line at Q using F_p² arithmetic - ECFieldElement Qx = Q.getAffineXCoord(); - ECFieldElement Qy = Q.getAffineYCoord(); - FP2Element lineVal = new FP2Element( - l.multiply(Qx.add(Cx).toBigInteger()), - l.multiply(Qy.toBigInteger()), - p - ); - - v = v.multiply(lineVal).pow(BigInteger.valueOf(2)); + //3*(C_x^2 - 1) + BigInteger t_x1_bn = (Cx.multiply(Cx).mod(p).subtract(BigInteger.ONE)).multiply(BigInteger.valueOf(3)); + //Qx + Cx + BigInteger t_bn = Qx.add(Cx); + //3*(C_x^2 - 1)(Qx+Cx) + t_x1_bn = t_x1_bn.multiply(t_bn).mod(p); + //Cy^2*2 + t_bn = Cy.multiply(Cy).mod(p).multiply(BigInteger.valueOf(2)); + //3*(C_x^2 - 1)(Qx+Cx) - Cy^2*2 + t_x1_bn = t_x1_bn.subtract(t_bn).mod(p); + // Cy*2*Qy + BigInteger t_x2_bn = Cy.multiply(BigInteger.valueOf(2)).multiply(Qy).mod(p); + + /* + * BigInteger real = currentX.multiply(pointX) + .subtract(currentY.multiply(pointY)) + .mod(p); + + // Compute imaginary part = x1*y2 + x2*y1 mod p + BigInteger imag = currentX.multiply(pointY) + .add(pointX.multiply(currentY)) + .mod(p);*/ + BigInteger real = vx.multiply(t_x1_bn) + .subtract(vy.multiply(t_x2_bn)) + .mod(p); + + // Compute imaginary part = x1*y2 + x2*y1 mod p + BigInteger imag = vx.multiply(t_x2_bn) + .add(t_x1_bn.multiply(vy)) + .mod(p); + + vx = real; + vy = imag; + C = C.twice().normalize(); if (qBits.charAt(j) == '1') { - ECPoint Rnorm = R.normalize(); - ECFieldElement Rx = Rnorm.getAffineXCoord(); - ECFieldElement Ry = Rnorm.getAffineYCoord(); - - // Line function for addition - ECFieldElement lAddNum = Cy.subtract(Ry); - ECFieldElement lAddDen = Cx.subtract(Rx); - BigInteger lAdd = lAddNum.divide(lAddDen).toBigInteger(); - - FP2Element lineAddVal = new FP2Element( - lAdd.multiply(Qx.add(Cx).toBigInteger()), - lAdd.multiply(Qy.toBigInteger()), - p - ); - - v = v.multiply(lineAddVal); + t_x1_bn = Qx.add(Rx).multiply(Cy).mod(p); + BigInteger tmp_t_bn = Qx.add(Cx).multiply(Ry); + t_x1_bn = t_x1_bn.subtract(tmp_t_bn).mod(p); + t_x2_bn = Cx.subtract(Rx).multiply(Qy).mod(p); + real = vx.multiply(t_x1_bn) + .subtract(vy.multiply(t_x2_bn)) + .mod(p); + + // Compute imaginary part = x1*y2 + x2*y1 mod p + imag = vx.multiply(t_x2_bn) + .add(t_x1_bn.multiply(vy)) + .mod(p); + + vx = real; + vy = imag; C = C.add(Rnorm).normalize(); } } + BigInteger xPlusY = vx.add(vy).mod(p); + BigInteger xMinusY = vx.subtract(vy).mod(p); + BigInteger newX = xPlusY.multiply(xMinusY).mod(p); - // Final exponentiation - FP2Element t = v.pow(exponent); + // Compute newY = 2xy mod p + BigInteger newY = vx.multiply(vy).multiply(BigInteger.valueOf(2)).mod(p); + vx = newX; + vy = newY; - // Convert to F_p representative: b/a mod p - BigInteger a = t.getA(); - BigInteger b = t.getB(); - return b.multiply(a.modInverse(p)).mod(p); - } + xPlusY = vx.add(vy).mod(p); + xMinusY = vx.subtract(vy).mod(p); + newX = xPlusY.multiply(xMinusY).mod(p); + + // Compute newY = 2xy mod p + newY = vx.multiply(vy).multiply(BigInteger.valueOf(2)).mod(p); + + vx = newX; + vy = newY; -// public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) -// { + BigInteger w = vx.modInverse(p).multiply(vy).mod(p); + + return w; +// // Final exponentiation +// FP2Element t = v.pow(exponent); +// +// // Convert to F_p representative: b/a mod p +// BigInteger a = t.getA(); +// BigInteger b = t.getB(); +// return b.multiply(a.modInverse(p)).mod(p); + } +// public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) { // ECCurve curve = R.getCurve(); -// FP2Element i = new FP2Element(BigInteger.ZERO, BigInteger.ONE, p); // i = -1 in F_p^2 +// BigInteger qMinus1 = q.subtract(BigInteger.ONE); +// int N = qMinus1.bitLength() - 1; // +// // Initialize V = (1, 0) in Fp² +// BigInteger vx = BigInteger.ONE; +// BigInteger vy = BigInteger.ZERO; +// +// // Initialize C = R // ECPoint C = R.normalize(); -// BigInteger c = p.add(BigInteger.ONE).divide(q); -// //ECFieldElement v = curve.fromBigInteger(BigInteger.ONE); // v = 1 in F_p -// FP2Element v = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); +// BigInteger Cx = C.getAffineXCoord().toBigInteger(); +// BigInteger Cy = C.getAffineYCoord().toBigInteger(); // -// String qBits = q.subtract(BigInteger.ONE).toString(2); // Binary representation of q-1 +// // Precompute Q coordinates +// ECPoint Qnorm = Q.normalize(); +// BigInteger Qx = Qnorm.getAffineXCoord().toBigInteger(); +// BigInteger Qy = Qnorm.getAffineYCoord().toBigInteger(); // -// for (int j = 1; j < qBits.length(); j++) -// { // Skip MSB -// // l = (3 * (C_x^2 - 1)) / (2 * C_y) -// C = C.normalize(); // Add this line to ensure normalization -// ECFieldElement Cx = C.getAffineXCoord(); -// ECFieldElement Cy = C.getAffineXCoord(); -// BigInteger CxVal = Cx.toBigInteger(); -// BigInteger CyVal = Cy.toBigInteger(); -//// ECFieldElement l = Cx.square().multiply(curve.fromBigInteger(ECFieldElement.THREE)).subtract(curve.fromBigInteger(BigInteger.ONE)) -//// .divide(Cy.multiply(curve.fromBigInteger(BigIntegers.TWO))); +// // Precompute R coordinates for addition steps +// ECPoint Rnorm = R.normalize(); +// BigInteger Rx = Rnorm.getAffineXCoord().toBigInteger(); +// BigInteger Ry = Rnorm.getAffineYCoord().toBigInteger(); // -// // l = 3*(Cx² - 1) / (2*Cy) in F_p -// ECFieldElement lNum = Cx.square().subtract(curve.fromBigInteger(BigInteger.ONE)).multiply(curve.fromBigInteger(BigInteger.valueOf(3))); -// ECFieldElement lDen = Cy.multiply(curve.fromBigInteger(BigInteger.valueOf(2))); -// ECFieldElement l = lNum.divide(lDen); -// BigInteger lVal = l.toBigInteger(); +// for (; N > 0; N--) { +// // V = V² (complex squaring) +// BigInteger[] squared = pointSquare(vx, vy, p); +// vx = squared[0]; +// vy = squared[1]; // -// // Evaluate line at [i]Q: (Qx, i*Qy) -// ECFieldElement Qx = Q.getAffineXCoord(); -// ECFieldElement Qy = Q.getAffineYCoord(); -// BigInteger QxVal = Qx.toBigInteger(); -// BigInteger QyVal = Qy.toBigInteger(); +// // Calculate line function for doubling +// BigInteger[] T = computeLineFunction(Cx, Cy, Qx, Qy, p); // -// // Convert l*(Qx + Cx) to F_p² -// FP2Element term1 = new FP2Element(lVal.multiply(QxVal.add(CxVal)).mod(p), BigInteger.ZERO, p); +// // V = V * T (complex multiplication) +// BigInteger[] multiplied = pointMultiply(vx, vy, T[0], T[1], p); +// vx = multiplied[0]; +// vy = multiplied[1]; // -// // (i*Qy - Cy) in F_p²: (-Cy, Qy) -// FP2Element term2 = new FP2Element(QyVal.negate().mod(p), QyVal, p); // Wait, original term is i*Qy - Cy: i*Qy is (0, Qy), subtract Cy (Cy,0) gives (-Cy, Qy) -// FP2Element lineVal = new FP2Element(lVal, BigInteger.ZERO, p) // l is in F_p -// .multiply(new FP2Element(QxVal.add(CxVal).mod(p), BigInteger.ZERO, p)) // (Qx + Cx) in F_p -// .add(term2); // i*Qy - Cy +// // Double point C +// BigInteger[] doubled = pointDouble(Cx, Cy, p); +// Cx = doubled[0]; +// Cy = doubled[1]; // -// v = v.square().multiply(lineVal); +// if (qMinus1.testBit(N-1)) { +// // Calculate line function for addition +// BigInteger[] TAdd = computeLineFunctionAdd(Cx, Cy, Rx, Ry, Qx, Qy, p); // -// C = C.twice().normalize();; +// // V = V * TAdd +// multiplied = pointMultiply(vx, vy, TAdd[0], TAdd[1], p); +// vx = multiplied[0]; +// vy = multiplied[1]; // -// // v = v^2 * (l * (Q_x + C_x) + (i * Q_y - C_y)) -//// ECFieldElement Qx = Q.getAffineXCoord(); -//// ECFieldElement Qy = Q.getAffineYCoord(); -//// v = v.square().multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); +// // Add points C = C + R +// BigInteger[] added = pointAdd(Cx, Cy, Rx, Ry, p); +// Cx = added[0]; +// Cy = added[1]; +// } +// } // -// // Double the point -//// C = C.twice(); -// if (qBits.charAt(j) == '1') -// { -// // Compute line function for addition -// ECFieldElement Rx = R.getAffineXCoord(); -// ECFieldElement Ry = R.getAffineYCoord(); -// BigInteger RxVal = Rx.toBigInteger(); -// BigInteger RyVal = Ry.toBigInteger(); +// // Final squaring V = V² +// BigInteger[] squared = pointSquare(vx, vy, p); +// vx = squared[0]; +// vy = squared[1]; +// squared = pointSquare(vx, vy, p); +// vx = squared[0]; +// vy = squared[1]; // -// ECFieldElement lAddNum = Cy.subtract(Ry); -// ECFieldElement lAddDen = Cx.subtract(Rx); -// ECFieldElement lAdd = lAddNum.divide(lAddDen); -// BigInteger lAddVal = lAdd.toBigInteger(); +// // Compute w = (Vy * Vx⁻¹) mod p +// BigInteger vxInv = vx.modInverse(p); +// return vy.multiply(vxInv).mod(p); +// } // -// // Evaluate line at [i]Q -// FP2Element lineAddTerm1 = new FP2Element(lAddVal.multiply(QxVal.add(CxVal)).mod(p), BigInteger.ZERO, p); -// FP2Element lineAddTerm2 = new FP2Element(QyVal.negate().mod(p), QyVal, p); // i*Qy - Cy (Cy is current C's y) -// FP2Element lineAddVal = lineAddTerm1.add(lineAddTerm2); +// // Helper methods implementing exact C code operations +// private static BigInteger[] pointSquare(BigInteger x, BigInteger y, BigInteger p) { +// BigInteger xPlusY = x.add(y).mod(p); +// BigInteger xMinusY = x.subtract(y).mod(p); +// return new BigInteger[] { +// xPlusY.multiply(xMinusY).mod(p), +// x.multiply(y).multiply(BigInteger.valueOf(2)).mod(p) +// }; +// } // -// v = v.multiply(lineAddVal); +// private static BigInteger[] pointMultiply(BigInteger a, BigInteger b, BigInteger c, BigInteger d, BigInteger p) { +// return new BigInteger[] { +// a.multiply(d).add(b.multiply(c)).mod(p), +// a.multiply(c).subtract(b.multiply(d)).mod(p), // -// C = C.add(R); -// } +// }; +// } // -//// // If the bit is 1, perform additional step -//// if (qBits.charAt(j) == '1') -//// { -//// // l = (C_y - R_y) / (C_x - R_x) -//// ECFieldElement Rx = R.getAffineXCoord(); -//// ECFieldElement Ry = R.getAffineYCoord(); -//// l = Cy.subtract(Ry).divide(Cx.subtract(Rx)); -//// -//// // v = v * (l * (Q_x + C_x) + (i * Q_y - C_y)) -//// v = v.multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); -//// -//// // C = C + R -//// C = C.add(R); -//// } -// } +// private static BigInteger[] pointDouble(BigInteger x, BigInteger y, BigInteger p) { +// BigInteger slope = x.pow(2).multiply(BigInteger.valueOf(3)) +// .mod(p) +// .multiply(y.multiply(BigInteger.valueOf(2)).modInverse(p)) +// .mod(p); +// return new BigInteger[] { +// slope.pow(2).subtract(x.multiply(BigInteger.valueOf(2))).mod(p), +// slope.multiply(x.subtract(slope.pow(2).mod(p))) +// .subtract(y).mod(p) +// }; +// } // -//// // Compute v^c -//// v = curve.fromBigInteger(v.toBigInteger().modPow(c, p)); -//// -//// // Convert to F_p representative -//// return computeFpRepresentative(v, curve); -// FP2Element t = v.pow(c); +// private static BigInteger[] pointAdd(BigInteger x1, BigInteger y1, BigInteger x2, BigInteger y2, BigInteger p) { +// BigInteger slope = y2.subtract(y1) +// .multiply(x2.subtract(x1).modInverse(p)) +// .mod(p); +// return new BigInteger[] { +// slope.pow(2).subtract(x1).subtract(x2).mod(p), +// slope.multiply(x1.subtract(slope.pow(2).subtract(x1).subtract(x2).mod(p))) +// .subtract(y1).mod(p) +// }; +// } // -// // Compute representative: b/a mod p -// BigInteger a = t.getA(); -// BigInteger bVal = t.getB(); -// if (a.equals(BigInteger.ZERO)) { -// throw new ArithmeticException("Division by zero in F_p representative"); -// } -// BigInteger aInv = a.modInverse(p); -// BigInteger representative = bVal.multiply(aInv).mod(p); +// private static BigInteger[] computeLineFunction(BigInteger Cx, BigInteger Cy, +// BigInteger Qx, BigInteger Qy, +// BigInteger p) { +// // Calculate 3*(Cx² - 1) +// BigInteger t_x1 = Cx.pow(2).subtract(BigInteger.ONE).multiply(BigInteger.valueOf(3)).mod(p); +// // Calculate Qx + Cx +// BigInteger t = Qx.add(Cx).mod(p); +// // Multiply components +// t_x1 = t_x1.multiply(t).mod(p); +// // Subtract 2*Cy² +// t_x1 = t_x1.subtract(Cy.pow(2).multiply(BigInteger.valueOf(2))).mod(p); +// // Calculate 2*Cy*Qy +// BigInteger t_x2 = Cy.multiply(BigInteger.valueOf(2)).multiply(Qy).mod(p); +// return new BigInteger[] {t_x1, t_x2}; +// } +// +// private static BigInteger[] computeLineFunctionAdd(BigInteger Cx, BigInteger Cy, +// BigInteger Rx, BigInteger Ry, +// BigInteger Qx, BigInteger Qy, +// BigInteger p) { +// // Calculate (Cy - Ry) +// BigInteger numerator = Cy.subtract(Ry).mod(p); +// // Calculate (Cx - Rx)⁻¹ +// BigInteger denominator = Cx.subtract(Rx).modInverse(p); +// BigInteger slope = numerator.multiply(denominator).mod(p); // -// return representative; +// // Calculate line function components +// BigInteger t_x1 = slope.multiply(Qx.add(Cx).mod(p)).mod(p); +// BigInteger t_x2 = slope.multiply(Qy).negate().mod(p); +// return new BigInteger[] {t_x1, t_x2}; // } - private static BigInteger computeFpRepresentative(ECFieldElement t, ECCurve curve) - { - // Characteristic of F_p - BigInteger p = ((ECCurve.Fp)curve).getQ(); - - // Assume t = a + i * b in F_p² → extract a, b - ECFieldElement a = t; // In F_p², a is the real part - ECFieldElement b = t.multiply(curve.fromBigInteger(BigInteger.ONE.negate().add(p))); // Imaginary part - // Compute b/a mod p - return b.toBigInteger().multiply(a.toBigInteger().modInverse(p)).mod(p); - } - - public static byte[] encodeData(ECPoint R_bS, BigInteger H) - { - // 1. Serialize EC Point (use compressed format for efficiency) - byte[] R_bS_bytes = R_bS.getEncoded(true); - - // 2. Serialize H (convert to a fixed-length byte array) - byte[] H_bytes = H.toByteArray(); - - // 3. Combine both into a single byte array - ByteBuffer buffer = ByteBuffer.allocate(R_bS_bytes.length + H_bytes.length); - buffer.put(R_bS_bytes); - buffer.put(H_bytes); - - return buffer.array(); - } } From b75fe36531f7b833225b40ff58b65bcb1785e8ea Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 5 Feb 2025 14:23:30 +1030 Subject: [PATCH 090/890] Pass the test vector of SAKKE --- .../crypto/kems/SAKKEKEMExtractor.java | 216 +++--------------- .../crypto/kems/SAKKEKEMSGenerator.java | 16 +- .../crypto/kems/test/SAKKEKEMSTest.java | 3 +- 3 files changed, 42 insertions(+), 193 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java index 0e23ab22cb..551f83cd01 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.kems; import java.math.BigInteger; +import java.security.SecureRandom; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.EncapsulatedSecretExtractor; @@ -8,9 +9,11 @@ import org.bouncycastle.crypto.params.SAKKEPrivateKeyParameters; import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; +import org.bouncycastle.util.encoders.Hex; import static org.bouncycastle.crypto.kems.SAKKEKEMSGenerator.pairing; @@ -48,18 +51,28 @@ public byte[] extractSecret(byte[] encapsulation) ECPoint R_bS = curve.decodePoint(Arrays.copyOfRange(encapsulation, 0, 257)); BigInteger H = new BigInteger(Arrays.copyOfRange(encapsulation, 257, 274)); + //ECCurveWithTatePairing pairing = new ECCurveWithTatePairing(q, BigInteger.ONE, BigInteger.ZERO, p); + //BigInteger w = pairing.TatePairing(R_bS, K_bS).toBigInteger(); // Step 2: Compute w = using pairing // BigInteger w = computeTLPairing(new BigInteger[] {R_bS.getXCoord().toBigInteger(), R_bS.getYCoord().toBigInteger()}, // new BigInteger[] {K_bS.getXCoord().toBigInteger(), K_bS.getYCoord().toBigInteger()}, this.p, this.q); BigInteger w = computePairing(R_bS, K_bS, p, q); - + System.out.println(new String(Hex.encode(w.toByteArray()))); + //BigInteger w = tatePairing(R_bS.getXCoord().toBigInteger(), R_bS.getYCoord().toBigInteger(), K_bS.getXCoord().toBigInteger(), K_bS.getYCoord().toBigInteger(), q, p); // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) BigInteger ssv = computeSSV(H, w); // Step 4: Compute r = HashToIntegerRange(SSV || b) -// BigInteger r = computeR(ssv, privateKey.getPrivatePoint()); + BigInteger b = privateKey.getB(); + BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); // // // Step 5: Validate R_bS + ECPoint bP = P.multiply(b).normalize(); + ECPoint Test = bP.add(Z_S).multiply(r).normalize(); + if(!R_bS.equals(Test)) + { + throw new IllegalStateException("Validation of R_bS failed"); + } // if (!validateR_bS(r, privateKey.getPrivatePoint(), R_bS)) { // throw new IllegalStateException("Validation of R_bS failed"); // } @@ -78,11 +91,6 @@ public int getEncapsulationLength() return 0; } - private BigInteger computePairing(ECPoint R, ECPoint K) - { - // Use your existing pairing implementation - return pairing(R, K, p, q); - } private BigInteger computeSSV(BigInteger H, BigInteger w) { @@ -91,175 +99,11 @@ private BigInteger computeSSV(BigInteger H, BigInteger w) return H.xor(mask); } - public static BigInteger computeTLPairing( - BigInteger[] R, // C = (Rx, Ry) - BigInteger[] Q, // Q = (Qx, Qy) - BigInteger p, - BigInteger q - ) - { - BigInteger qMinus1 = q.subtract(BigInteger.ONE); - int N = qMinus1.bitLength() - 1; - - // Initialize V = (1, 0) - BigInteger[] V = {BigInteger.ONE, BigInteger.ZERO}; - // Initialize C = R - BigInteger[] C = {R[0], R[1]}; - - for (; N > 0; N--) - { - // V = V^2 - pointSquare(V, p); - - // Compute line function T - BigInteger[] T = computeLineFunctionT(C, Q, p); - - // V = V * T - pointMultiply(V, T, p); - - // C = 2*C (point doubling) - pointDouble(C, p); - - if (qMinus1.testBit(N - 1)) - { - // Compute addition line function - BigInteger[] TAdd = computeLineFunctionAdd(C, R, Q, p); - - // V = V * TAdd - pointMultiply(V, TAdd, p); - - // C = C + R (point addition) - pointAdd(C, R, p); - } - } - - // Final squaring - pointSquare(V, p); - pointSquare(V, p); - - // Compute w = (Vy * Vx^{-1}) mod p - BigInteger VxInv = V[0].modInverse(p); - return V[1].multiply(VxInv).mod(p); - } - - private static void pointSquare(BigInteger[] point, BigInteger p) - { - BigInteger x = point[0]; - BigInteger y = point[1]; - - // x = (x + y)(x - y) mod p - BigInteger xPlusY = x.add(y).mod(p); - BigInteger xMinusY = x.subtract(y).mod(p); - BigInteger newX = xPlusY.multiply(xMinusY).mod(p); - - // y = 2xy mod p - BigInteger newY = x.multiply(y).multiply(BigInteger.valueOf(2)).mod(p); - - point[0] = newX; - point[1] = newY; - } - - private static void pointMultiply(BigInteger[] a, BigInteger[] b, BigInteger p) - { - // Complex multiplication (a + bi)*(c + di) = (ac - bd) + (ad + bc)i - BigInteger real = a[0].multiply(b[0]).subtract(a[1].multiply(b[1])).mod(p); - BigInteger imag = a[0].multiply(b[1]).add(a[1].multiply(b[0])).mod(p); - - a[0] = real; - a[1] = imag; - } - - private static void pointDouble(BigInteger[] point, BigInteger p) - { - // Elliptic curve point doubling formulas - BigInteger x = point[0]; - BigInteger y = point[1]; - - BigInteger slope = x.pow(2).multiply(BigInteger.valueOf(3)) - .mod(p) - .multiply(y.multiply(BigInteger.valueOf(2)).modInverse(p)) - .mod(p); - - BigInteger newX = slope.pow(2).subtract(x.multiply(BigInteger.valueOf(2))).mod(p); - BigInteger newY = slope.multiply(x.subtract(newX)).subtract(y).mod(p); - - point[0] = newX; - point[1] = newY; - } - - private static void pointAdd(BigInteger[] a, BigInteger[] b, BigInteger p) - { - // Elliptic curve point addition - BigInteger x1 = a[0], y1 = a[1]; - BigInteger x2 = b[0], y2 = b[1]; - - BigInteger slope = y2.subtract(y1) - .multiply(x2.subtract(x1).modInverse(p)) - .mod(p); - - BigInteger newX = slope.pow(2).subtract(x1).subtract(x2).mod(p); - BigInteger newY = slope.multiply(x1.subtract(newX)).subtract(y1).mod(p); - - a[0] = newX; - a[1] = newY; - } - - private static BigInteger[] computeLineFunctionT( - BigInteger[] C, - BigInteger[] Q, - BigInteger p - ) - { - // Line function evaluation for doubling - BigInteger Cx = C[0], Cy = C[1]; - BigInteger Qx = Q[0], Qy = Q[1]; - - // l = (3Cx² + a)/(2Cy) but a=0 for many curves - BigInteger numerator = Cx.pow(2).multiply(BigInteger.valueOf(3)).mod(p); - BigInteger denominator = Cy.multiply(BigInteger.valueOf(2)).mod(p); - BigInteger l = numerator.multiply(denominator.modInverse(p)).mod(p); - - // T = l*(Qx + Cx) - 2Qy - BigInteger tReal = l.multiply(Qx.add(Cx).mod(p)).mod(p); - BigInteger tImag = l.multiply(Qy).negate().mod(p); - - return new BigInteger[]{tReal, tImag}; - } - - private static BigInteger[] computeLineFunctionAdd( - BigInteger[] C, - BigInteger[] R, - BigInteger[] Q, - BigInteger p - ) - { - // Line function evaluation for addition - BigInteger Cx = C[0], Cy = C[1]; - BigInteger Rx = R[0], Ry = R[1]; - BigInteger Qx = Q[0], Qy = Q[1]; - - // l = (Cy - Ry)/(Cx - Rx) - BigInteger numerator = Cy.subtract(Ry).mod(p); - BigInteger denominator = Cx.subtract(Rx).mod(p); - BigInteger l = numerator.multiply(denominator.modInverse(p)).mod(p); - - // T = l*(Qx + Cx) - Qy - BigInteger tReal = l.multiply(Qx.add(Cx).mod(p)).mod(p); - BigInteger tImag = l.multiply(Qy).negate().mod(p); - - return new BigInteger[]{tReal, tImag}; - } - - private boolean pointsEqual(ECPoint p1, ECPoint p2) - { - return p1.normalize().getXCoord().equals(p2.normalize().getXCoord()) - && p1.normalize().getYCoord().equals(p2.normalize().getYCoord()); - } - public static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) { BigInteger c = p.add(BigInteger.ONE).divide(q); // Compute c = (p+1)/q BigInteger[] v = new BigInteger[]{BigInteger.ONE, BigInteger.ZERO}; // v = (1,0) in F_p^2 + //BigInteger v = BigInteger.ONE; ECPoint C = R; BigInteger qMinusOne = q.subtract(BigInteger.ONE); @@ -269,6 +113,7 @@ public static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigI for (int i = numBits - 2; i >= 0; i--) { v = fp2SquareAndAccumulate(v, C, Q, p); + C = C.twice().normalize(); // C = [2]C if (qMinusOne.testBit(i)) @@ -290,13 +135,24 @@ private static BigInteger[] fp2SquareAndAccumulate(BigInteger[] v, ECPoint C, EC BigInteger Qy = Q.getAffineYCoord().toBigInteger(); // Compute l = (3 * (Cx^2 - 1)) / (2 * Cy) mod p - BigInteger l = Cx.multiply(Cx).mod(p).subtract(BigInteger.ONE).multiply(BigInteger.valueOf(3)).mod(p) - .multiply(Cy.multiply(BigInteger.valueOf(2)).modInverse(p)) - .mod(p); + BigInteger l = BigInteger.valueOf(3).multiply(Cx.multiply(Cx).subtract(BigInteger.ONE)) + .multiply(Cy.multiply(BigInteger.valueOf(2)).modInverse(p)).mod(p); // Compute v = v^2 * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) v = fp2Multiply(v[0], v[1], v[0], v[1], p); - return fp2Multiply(v[0], v[1], l.multiply(Qx.add(Cx)), (Qy.subtract(Cy)), p); +// v[0] = v[0].multiply(v[0]); +// v[1] = v[1].multiply(v[1]); + return accumulateLine(v[0], v[1], Cx, Cy, Qx, Qy, l, p); +// BigInteger t_x1_bn = Cx.multiply(Cx).subtract(BigInteger.ONE).multiply(BigInteger.valueOf(3)).multiply(Qx.add(Cx)).mod(p) +// .subtract(Cy.multiply(Cy).multiply(BigInteger.valueOf(2))).mod(p); +// BigInteger t_x2_bn = Cy.multiply(Qy).multiply(BigInteger.valueOf(2)).mod(p); +// v = fp2Multiply(v[0], v[1], v[0], v[1], p); +// return fp2Multiply(v[0], v[1], t_x1_bn, t_x2_bn, p); + } + + private static BigInteger[] accumulateLine(BigInteger v0, BigInteger v1, BigInteger Cx, BigInteger Cy, BigInteger Qx, BigInteger Qy, BigInteger l, BigInteger p) + { + return fp2Multiply(v0, v1, l.multiply(Qx.add(Cx)).subtract(Cy), Qy, p); } private static BigInteger[] fp2MultiplyAndAccumulate(BigInteger[] v, ECPoint C, ECPoint R, ECPoint Q, BigInteger p) @@ -314,11 +170,15 @@ private static BigInteger[] fp2MultiplyAndAccumulate(BigInteger[] v, ECPoint C, .mod(p); // Compute v = v * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) - return fp2Multiply(v[0], v[1], l.multiply(Qx.add(Cx)), Qy.subtract(Cy), p); + return accumulateLine(v[0], v[1], Cx, Cy, Qx, Qy, l, p); +// BigInteger t_x1_bn = Qx.add(Rx).multiply(Cy).subtract(Qx.add(Cx).multiply(Ry)).mod(p); +// BigInteger t_x2_bn = Cx.subtract(Rx).multiply(Qy).mod(p); +// return fp2Multiply(v[0], v[1], t_x1_bn, t_x2_bn, p); + } - private static BigInteger[] fp2Multiply(BigInteger x_real, BigInteger x_imag, BigInteger y_real, BigInteger y_imag, BigInteger p) + static BigInteger[] fp2Multiply(BigInteger x_real, BigInteger x_imag, BigInteger y_real, BigInteger y_imag, BigInteger p) { // Multiply v = (a + i*b) * scalar return new BigInteger[]{ diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index 59895f5738..1d5128e7d3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -156,20 +156,12 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip BigInteger g_r = result[0].mod(p); g_r = g_r.modInverse(p); g_r = g_r.multiply(result[1]).mod(p); - System.out.println("g_r " + new String(Hex.encode(g_r.toByteArray()))); - byte[] expected_g_r = Hex.decode("7D2A8438 E6291C64 9B6579EB 3B79EAE9\n" + - " 48B1DE9E 5F7D1F40 70A08F8D B6B3C515\n" + - " 6F2201AF FBB5CB9D 82AA3EC0 D0398B89\n" + - " ABC78A13 A760C0BF 3F77E63D 0DF3F1A3\n" + - " 41A41B88 11DF197F D6CD0F00 3125606F\n" + - " 4F109F40 0F7292A1 0D255E3C 0EBCCB42\n" + - " 53FB182C 68F09CF6 CD9C4A53 DA6C74AD\n" + - " 007AF36B 8BCA979D 5895E282 F483FCD6"); + BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(n)); // 2^n System.out.println(new String(Hex.encode(mask.toByteArray()))); BigInteger H = ssv.xor(mask); - + System.out.println(new String(Hex.encode(H.toByteArray()))); // 5. Encode encapsulated data (R_bS, H) byte[] encapsulated = Arrays.concatenate(R_bS.getEncoded(false), H.toByteArray()); @@ -201,10 +193,6 @@ public static boolean sakkePointExponent( BigInteger n ) { - if (n.equals(BigInteger.ZERO)) - { - return false; - } // Initialize result with the original point BigInteger currentX = pointX; diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index 485965ad59..d40a78abfb 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -155,7 +155,8 @@ public void performTest() ECPoint K_bS = curve.createPoint(kbx, kby); - SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(new BigInteger(b), K_bS, new SAKKEPublicKeyParameters(null))); + SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(new BigInteger(b), K_bS, + new SAKKEPublicKeyParameters(curve.createPoint(Zx, Zy)))); byte[] test = extractor.extractSecret(rlt.getSecret()); From 96ef694744d8dc576a8b287389d7208e6b01e48c Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 5 Feb 2025 16:25:43 +1030 Subject: [PATCH 091/890] TODO: SAKKEKEMSGenerator and parameter settings --- .../crypto/kems/SAKKEKEMExtractor.java | 133 ++--- .../crypto/kems/SAKKEKEMSGenerator.java | 478 +----------------- .../crypto/kems/test/SAKKEKEMSTest.java | 32 +- 3 files changed, 75 insertions(+), 568 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java index 551f83cd01..8551f74680 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java @@ -1,11 +1,8 @@ package org.bouncycastle.crypto.kems; import java.math.BigInteger; -import java.security.SecureRandom; -import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.EncapsulatedSecretExtractor; -import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.params.SAKKEPrivateKeyParameters; import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; @@ -15,7 +12,6 @@ import org.bouncycastle.util.BigIntegers; import org.bouncycastle.util.encoders.Hex; -import static org.bouncycastle.crypto.kems.SAKKEKEMSGenerator.pairing; public class SAKKEKEMExtractor implements EncapsulatedSecretExtractor @@ -51,31 +47,26 @@ public byte[] extractSecret(byte[] encapsulation) ECPoint R_bS = curve.decodePoint(Arrays.copyOfRange(encapsulation, 0, 257)); BigInteger H = new BigInteger(Arrays.copyOfRange(encapsulation, 257, 274)); - //ECCurveWithTatePairing pairing = new ECCurveWithTatePairing(q, BigInteger.ONE, BigInteger.ZERO, p); - //BigInteger w = pairing.TatePairing(R_bS, K_bS).toBigInteger(); // Step 2: Compute w = using pairing -// BigInteger w = computeTLPairing(new BigInteger[] {R_bS.getXCoord().toBigInteger(), R_bS.getYCoord().toBigInteger()}, -// new BigInteger[] {K_bS.getXCoord().toBigInteger(), K_bS.getYCoord().toBigInteger()}, this.p, this.q); BigInteger w = computePairing(R_bS, K_bS, p, q); System.out.println(new String(Hex.encode(w.toByteArray()))); //BigInteger w = tatePairing(R_bS.getXCoord().toBigInteger(), R_bS.getYCoord().toBigInteger(), K_bS.getXCoord().toBigInteger(), K_bS.getYCoord().toBigInteger(), q, p); // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) - BigInteger ssv = computeSSV(H, w); + BigInteger twoToN = BigInteger.ONE.shiftLeft(n); + BigInteger mask = SAKKEUtils.hashToIntegerRange(w.toByteArray(), twoToN); + BigInteger ssv = H.xor(mask); // Step 4: Compute r = HashToIntegerRange(SSV || b) BigInteger b = privateKey.getB(); BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); -// -// // Step 5: Validate R_bS + + // Step 5: Validate R_bS ECPoint bP = P.multiply(b).normalize(); ECPoint Test = bP.add(Z_S).multiply(r).normalize(); - if(!R_bS.equals(Test)) + if (!R_bS.equals(Test)) { throw new IllegalStateException("Validation of R_bS failed"); } -// if (!validateR_bS(r, privateKey.getPrivatePoint(), R_bS)) { -// throw new IllegalStateException("Validation of R_bS failed"); -// } return BigIntegers.asUnsignedByteArray(n / 8, ssv); } @@ -92,109 +83,75 @@ public int getEncapsulationLength() } - private BigInteger computeSSV(BigInteger H, BigInteger w) - { - BigInteger twoToN = BigInteger.ONE.shiftLeft(n); - BigInteger mask = SAKKEUtils.hashToIntegerRange(w.toByteArray(), twoToN); - return H.xor(mask); - } - public static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) { - BigInteger c = p.add(BigInteger.ONE).divide(q); // Compute c = (p+1)/q - BigInteger[] v = new BigInteger[]{BigInteger.ONE, BigInteger.ZERO}; // v = (1,0) in F_p^2 - //BigInteger v = BigInteger.ONE; + // v = (1,0) in F_p^2 + BigInteger[] v = new BigInteger[]{BigInteger.ONE, BigInteger.ZERO}; ECPoint C = R; BigInteger qMinusOne = q.subtract(BigInteger.ONE); int numBits = qMinusOne.bitLength(); + BigInteger Qx = Q.getAffineXCoord().toBigInteger(); + BigInteger Qy = Q.getAffineYCoord().toBigInteger(); + BigInteger Rx = R.getAffineXCoord().toBigInteger(); + BigInteger Ry = R.getAffineYCoord().toBigInteger(); + BigInteger l, Cx, Cy; + final BigInteger three = BigInteger.valueOf(3); + final BigInteger two = BigInteger.valueOf(2); // Miller loop for (int i = numBits - 2; i >= 0; i--) { - v = fp2SquareAndAccumulate(v, C, Q, p); + Cx = C.getAffineXCoord().toBigInteger(); + Cy = C.getAffineYCoord().toBigInteger(); + + // Compute l = (3 * (Cx^2 - 1)) / (2 * Cy) mod p + l = three.multiply(Cx.multiply(Cx).subtract(BigInteger.ONE)) + .multiply(Cy.multiply(two).modInverse(p)).mod(p); + + // Compute v = v^2 * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) + v = fp2PointSquare(v[0], v[1], p); + v = fp2Multiply(v[0], v[1], l.multiply(Qx.add(Cx)).subtract(Cy), Qy, p); C = C.twice().normalize(); // C = [2]C if (qMinusOne.testBit(i)) { - v = fp2MultiplyAndAccumulate(v, C, R, Q, p); + Cx = C.getAffineXCoord().toBigInteger(); + Cy = C.getAffineYCoord().toBigInteger(); + + // Compute l = (Cy - Ry) / (Cx - Rx) mod p + l = Cy.subtract(Ry).multiply(Cx.subtract(Rx).modInverse(p)).mod(p); + + // Compute v = v * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) + v = fp2Multiply(v[0], v[1], l.multiply(Qx.add(Cx)).subtract(Cy), Qy, p); + C = C.add(R).normalize(); } } // Final exponentiation: t = v^c - return fp2FinalExponentiation(v, p, c); - } - - private static BigInteger[] fp2SquareAndAccumulate(BigInteger[] v, ECPoint C, ECPoint Q, BigInteger p) - { - BigInteger Cx = C.getAffineXCoord().toBigInteger(); - BigInteger Cy = C.getAffineYCoord().toBigInteger(); - BigInteger Qx = Q.getAffineXCoord().toBigInteger(); - BigInteger Qy = Q.getAffineYCoord().toBigInteger(); - - // Compute l = (3 * (Cx^2 - 1)) / (2 * Cy) mod p - BigInteger l = BigInteger.valueOf(3).multiply(Cx.multiply(Cx).subtract(BigInteger.ONE)) - .multiply(Cy.multiply(BigInteger.valueOf(2)).modInverse(p)).mod(p); - - // Compute v = v^2 * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) - v = fp2Multiply(v[0], v[1], v[0], v[1], p); -// v[0] = v[0].multiply(v[0]); -// v[1] = v[1].multiply(v[1]); - return accumulateLine(v[0], v[1], Cx, Cy, Qx, Qy, l, p); -// BigInteger t_x1_bn = Cx.multiply(Cx).subtract(BigInteger.ONE).multiply(BigInteger.valueOf(3)).multiply(Qx.add(Cx)).mod(p) -// .subtract(Cy.multiply(Cy).multiply(BigInteger.valueOf(2))).mod(p); -// BigInteger t_x2_bn = Cy.multiply(Qy).multiply(BigInteger.valueOf(2)).mod(p); -// v = fp2Multiply(v[0], v[1], v[0], v[1], p); -// return fp2Multiply(v[0], v[1], t_x1_bn, t_x2_bn, p); - } - - private static BigInteger[] accumulateLine(BigInteger v0, BigInteger v1, BigInteger Cx, BigInteger Cy, BigInteger Qx, BigInteger Qy, BigInteger l, BigInteger p) - { - return fp2Multiply(v0, v1, l.multiply(Qx.add(Cx)).subtract(Cy), Qy, p); - } - - private static BigInteger[] fp2MultiplyAndAccumulate(BigInteger[] v, ECPoint C, ECPoint R, ECPoint Q, BigInteger p) - { - BigInteger Cx = C.getAffineXCoord().toBigInteger(); - BigInteger Cy = C.getAffineYCoord().toBigInteger(); - BigInteger Rx = R.getAffineXCoord().toBigInteger(); - BigInteger Ry = R.getAffineYCoord().toBigInteger(); - BigInteger Qx = Q.getAffineXCoord().toBigInteger(); - BigInteger Qy = Q.getAffineYCoord().toBigInteger(); - - // Compute l = (Cy - Ry) / (Cx - Rx) mod p - BigInteger l = Cy.subtract(Ry) - .multiply(Cx.subtract(Rx).modInverse(p)) - .mod(p); - - // Compute v = v * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) - return accumulateLine(v[0], v[1], Cx, Cy, Qx, Qy, l, p); -// BigInteger t_x1_bn = Qx.add(Rx).multiply(Cy).subtract(Qx.add(Cx).multiply(Ry)).mod(p); -// BigInteger t_x2_bn = Cx.subtract(Rx).multiply(Qy).mod(p); -// return fp2Multiply(v[0], v[1], t_x1_bn, t_x2_bn, p); - + v = fp2PointSquare(v[0], v[1], p); + v = fp2PointSquare(v[0], v[1], p); + return v[1].multiply(v[0].modInverse(p)).mod(p); } - static BigInteger[] fp2Multiply(BigInteger x_real, BigInteger x_imag, BigInteger y_real, BigInteger y_imag, BigInteger p) { - // Multiply v = (a + i*b) * scalar return new BigInteger[]{ x_real.multiply(y_real).subtract(x_imag.multiply(y_imag)).mod(p), x_real.multiply(y_imag).add(x_imag.multiply(y_real)).mod(p) }; } - private static BigInteger fp2FinalExponentiation(BigInteger[] v, BigInteger p, BigInteger c) + static BigInteger[] fp2PointSquare(BigInteger currentX, BigInteger currentY, BigInteger p) { - // Compute representative in F_p: return b/a (mod p) -// BigInteger v0 = v[0].modPow(c, p); -// BigInteger v1 = v[1].modPow(c, p); -// return v1.multiply(v0.modInverse(p)).mod(p); - v = fp2Multiply(v[0], v[1], v[0], v[1], p); - v = fp2Multiply(v[0], v[1], v[0], v[1], p); - return v[1].multiply(v[0].modInverse(p)).mod(p); + BigInteger xPlusY = currentX.add(currentY).mod(p); + BigInteger xMinusY = currentX.subtract(currentY).mod(p); + BigInteger newX = xPlusY.multiply(xMinusY).mod(p); + + // Compute newY = 2xy mod p + BigInteger newY = currentX.multiply(currentY).multiply(BigInteger.valueOf(2)).mod(p); + return new BigInteger[]{newX, newY}; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index 1d5128e7d3..d2f81af17b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -3,61 +3,33 @@ import org.bouncycastle.crypto.EncapsulatedSecretGenerator; import org.bouncycastle.crypto.SecretWithEncapsulation; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; -import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; -import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; import org.bouncycastle.util.encoders.Hex; import java.math.BigInteger; -import java.nio.ByteBuffer; import java.security.SecureRandom; public class SAKKEKEMSGenerator implements EncapsulatedSecretGenerator { - // private static final BigInteger p = new BigInteger(Hex.decode("997ABB1F 0A563FDA 65C61198 DAD0657A\n" + -// " 416C0CE1 9CB48261 BE9AE358 B3E01A2E\n" + -// " F40AAB27 E2FC0F1B 228730D5 31A59CB0\n" + -// " E791B39F F7C88A19 356D27F4 A666A6D0\n" + -// " E26C6487 326B4CD4 512AC5CD 65681CE1\n" + -// " B6AFF4A8 31852A82 A7CF3C52 1C3C09AA\n" + -// " 9F94D6AF 56971F1F FCE3E823 89857DB0\n" + -// " 80C5DF10 AC7ACE87 666D807A FEA85FEB")); private static final BigInteger p = new BigInteger( "997ABB1F0A563FDA65C61198DAD0657A416C0CE19CB48261BE9AE358B3E01A2E" + "F40AAB27E2FC0F1B228730D531A59CB0E791B39FF7C88A19356D27F4A666A6D0" + "E26C6487326B4CD4512AC5CD65681CE1B6AFF4A831852A82A7CF3C521C3C09AA" + "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 ); - // private static final BigInteger q = new BigInteger(Hex.decode("265EAEC7 C2958FF6 99718466 36B4195E\n" + -// " 905B0338 672D2098 6FA6B8D6 2CF8068B\n" + -// " BD02AAC9 F8BF03C6 C8A1CC35 4C69672C\n" + -// " 39E46CE7 FDF22286 4D5B49FD 2999A9B4\n" + -// " 389B1921 CC9AD335 144AB173 595A0738\n" + -// " 6DABFD2A 0C614AA0 A9F3CF14 870F026A\n" + -// " A7E535AB D5A5C7C7 FF38FA08 E2615F6C\n" + -// " 203177C4 2B1EB3A1 D99B601E BFAA17FB")); + private static final BigInteger q = new BigInteger( "265EAEC7C2958FF69971846636B4195E905B0338672D20986FA6B8D62CF8068B" + "BD02AAC9F8BF03C6C8A1CC354C69672C39E46CE7FDF222864D5B49FD2999A9B4" + "389B1921CC9AD335144AB173595A07386DABFD2A0C614AA0A9F3CF14870F026A" + "A7E535ABD5A5C7C7FF38FA08E2615F6C203177C42B1EB3A1D99B601EBFAA17FB", 16 ); -// private static final BigInteger a = BigInteger.valueOf(-3).mod(p); // y² = x³ - 3x -// private static final BigInteger b = BigInteger.ZERO; -// private static final ECCurve.Fp curve = new ECCurve.Fp( -// p, // Prime p -// BigInteger.valueOf(-3).mod(p), // a = -3 -// BigInteger.ZERO, // b = 0 -// q, // Order of the subgroup (from RFC 6509) -// BigInteger.ONE // Cofactor = 1 -// ); - // Base point P = (Px, Py) private static final BigInteger Px = new BigInteger( "53FC09EE332C29AD0A7990053ED9B52A2B1A2FD60AEC69C698B2F204B6FF7CBF" + "B5EDB6C0F6CE2308AB10DB9030B09E1043D5F22CDB9DFA55718BD9E7406CE890" + @@ -72,7 +44,6 @@ public class SAKKEKEMSGenerator "13515AD7E9CB99A980BDAD5AD5BB4636ADB9B5706A67DCDE75573FD71BEF16D7", 16 ); - BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + " C6A87BD1 FB94C41E 27FABE65 8E015A87\n" + " 371E9474 4C96FEDA 449AE956 3F8BC446\n" + @@ -81,7 +52,7 @@ public class SAKKEKEMSGenerator " 55DF0460 B4A9FD74 B4F1A32B CAFA1FFA\n" + " D682C033 A7942BCC E3720F20 B9B7B040\n" + " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); - private final int n = 128; + private static final int n = 128; private final SecureRandom random; public SAKKEKEMSGenerator(SecureRandom random) @@ -98,7 +69,7 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip // 2. Compute r = HashToIntegerRange(SSV || b, q) BigInteger b = new BigInteger("323031312D30320074656C3A2B34343737303039303031323300", 16); //getRecipientId((SAKKEPublicKey)recipientKey); BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); - System.out.println(new String(Hex.encode(r.toByteArray()))); + //System.out.println(new String(Hex.encode(r.toByteArray()))); ECCurve.Fp curve = new ECCurve.Fp( p, // Prime p BigInteger.valueOf(-3).mod(p), // a = -3 @@ -107,24 +78,7 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip BigInteger.ONE // Cofactor = 1 ); ECPoint P = curve.createPoint(Px, Py); - ECPoint G = curve.createPoint( - new BigInteger(Hex.decode("53FC09EE 332C29AD 0A799005 3ED9B52A\n" + - " 2B1A2FD6 0AEC69C6 98B2F204 B6FF7CBF\n" + - " B5EDB6C0 F6CE2308 AB10DB90 30B09E10\n" + - " 43D5F22C DB9DFA55 718BD9E7 406CE890\n" + - " 9760AF76 5DD5BCCB 337C8654 8B72F2E1\n" + - " A702C339 7A60DE74 A7C1514D BA66910D\n" + - " D5CFB4CC 80728D87 EE9163A5 B63F73EC\n" + - " 80EC46C4 967E0979 880DC8AB EAE63895")), // Px - new BigInteger(Hex.decode("0A824906 3F6009F1 F9F1F053 3634A135\n" + - " D3E82016 02990696 3D778D82 1E141178\n" + - " F5EA69F4 654EC2B9 E7F7F5E5 F0DE55F6\n" + - " 6B598CCF 9A140B2E 416CFF0C A9E032B9\n" + - " 70DAE117 AD547C6C CAD696B5 B7652FE0\n" + - " AC6F1E80 164AA989 492D979F C5A4D5F2\n" + - " 13515AD7 E9CB99A9 80BDAD5A D5BB4636\n" + - " ADB9B570 6A67DCDE 75573FD7 1BEF16D7")) // Py - ); + ECPoint Z = curve.createPoint( new BigInteger("5958EF1B1679BF099B3A030DF255AA6A" + "23C1D8F143D4D23F753E69BD27A832F3" + @@ -143,31 +97,29 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip "C67C6D19487FB449059F26CC8AAB655A" + "B58B7CC796E24E9A394095754F5F8BAE", 16) // Py ); - BigInteger z = new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F", 16); + // 3. Compute R_(b,S) = [r]([b]P + Z_S) ECPoint bP = P.multiply(b).normalize(); ECPoint R_bS = bP.add(Z).multiply(r).normalize(); // [r]([b]P + Z_S) - System.out.println("R_Bs x:" + new String(Hex.encode(R_bS.getXCoord().toBigInteger().toByteArray()))); - System.out.println("R_Bs y:" + new String(Hex.encode(R_bS.getYCoord().toBigInteger().toByteArray()))); +// System.out.println("R_Bs x:" + new String(Hex.encode(R_bS.getXCoord().toBigInteger().toByteArray()))); +// System.out.println("R_Bs y:" + new String(Hex.encode(R_bS.getYCoord().toBigInteger().toByteArray()))); // 4. Compute H = SSV XOR HashToIntegerRange( g^r, 2^n ) - BigInteger[] result = fp2Exponentiate(p, BigInteger.ONE, g, r); - BigInteger g_r = result[0].mod(p); - g_r = g_r.modInverse(p); - g_r = g_r.multiply(result[1]).mod(p); + BigInteger[] v = fp2Exponentiate(p, BigInteger.ONE, g, r, curve); + BigInteger g_r = v[1].multiply(v[0].modInverse(p)).mod(p); BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(n)); // 2^n - System.out.println(new String(Hex.encode(mask.toByteArray()))); + //System.out.println(new String(Hex.encode(mask.toByteArray()))); BigInteger H = ssv.xor(mask); - System.out.println(new String(Hex.encode(H.toByteArray()))); + //System.out.println(new String(Hex.encode(H.toByteArray()))); // 5. Encode encapsulated data (R_bS, H) byte[] encapsulated = Arrays.concatenate(R_bS.getEncoded(false), H.toByteArray()); return new SecretWithEncapsulationImpl( - encapsulated, - BigIntegers.asUnsignedByteArray(n / 8, ssv) // Output SSV as key material + BigIntegers.asUnsignedByteArray(n / 8, ssv), // Output SSV as key material + encapsulated ); } @@ -177,11 +129,12 @@ public static BigInteger[] fp2Exponentiate( BigInteger p, BigInteger x, BigInteger y, - BigInteger exponent + BigInteger exponent, + ECCurve.Fp curve ) { BigInteger[] result = new BigInteger[2]; - sakkePointExponent(p, result, x, y, exponent); + sakkePointExponent(p, result, x, y, exponent, curve); return result; } @@ -190,45 +143,32 @@ public static boolean sakkePointExponent( BigInteger[] result, BigInteger pointX, BigInteger pointY, - BigInteger n - ) + BigInteger n, + ECCurve.Fp curve) { // Initialize result with the original point BigInteger currentX = pointX; BigInteger currentY = pointY; + ECPoint current = curve.createPoint(currentX, currentY); int numBits = n.bitLength(); - + BigInteger[] rlt; // Process bits from MSB-1 down to 0 for (int i = numBits - 2; i >= 0; i--) { // Square the current point - //sakkePointSquare(p, new BigInteger[]{currentX, currentY}); - // Compute newX = (x + y)(x - y) mod p - BigInteger xPlusY = currentX.add(currentY).mod(p); - BigInteger xMinusY = currentX.subtract(currentY).mod(p); - BigInteger newX = xPlusY.multiply(xMinusY).mod(p); - - // Compute newY = 2xy mod p - BigInteger newY = currentX.multiply(currentY).multiply(BigInteger.valueOf(2)).mod(p); - currentX = newX; - currentY = newY; + rlt = SAKKEKEMExtractor.fp2PointSquare(currentX, currentY, p); + current = current.timesPow2(2); + currentX = rlt[0]; + currentY = rlt[1]; // Multiply if bit is set if (n.testBit(i)) { - //sakkePointsMultiply(p, currentX, currentY, pointX, pointY); - BigInteger real = currentX.multiply(pointX) - .subtract(currentY.multiply(pointY)) - .mod(p); + rlt = SAKKEKEMExtractor.fp2Multiply(currentX, currentY, pointX, pointY, p); - // Compute imaginary part = x1*y2 + x2*y1 mod p - BigInteger imag = currentX.multiply(pointY) - .add(pointX.multiply(currentY)) - .mod(p); - - currentX = real; - currentY = imag; + currentX = rlt[0]; + currentY = rlt[1]; } } @@ -236,368 +176,4 @@ public static boolean sakkePointExponent( result[1] = currentY; return true; } - - - private BigInteger getRecipientId(SAKKEPublicKeyParameters pubKey) - { - byte[] hashedId = SAKKEUtils.hash(pubKey.getZ().getEncoded(false)); // Hash Z_S - return new BigInteger(1, hashedId).mod(pubKey.getQ().subtract(BigInteger.ONE)).add(BigIntegers.TWO); - } - - public static class FP2Element - { - private final BigInteger a; // Real part - private final BigInteger b; // Imaginary part - private final BigInteger p; // Prime modulus - - public FP2Element(BigInteger a, BigInteger b, BigInteger p) - { - this.a = a.mod(p); - this.b = b.mod(p); - this.p = p; - } - - public FP2Element add(FP2Element other) - { - return new FP2Element(a.add(other.a), b.add(other.b), p); - } - - public FP2Element subtract(FP2Element other) - { - return new FP2Element(a.subtract(other.a), b.subtract(other.b), p); - } - - public FP2Element multiply(FP2Element other) - { - BigInteger real = a.multiply(other.a).subtract(b.multiply(other.b)).mod(p); - BigInteger imag = a.multiply(other.b).add(b.multiply(other.a)).mod(p); - return new FP2Element(real, imag, p); - } - - public FP2Element inverse() - { - BigInteger denom = a.pow(2).add(b.pow(2)).mod(p); - BigInteger invDenom = denom.modInverse(p); - return new FP2Element(a.multiply(invDenom), b.negate().multiply(invDenom), p); - } - - public FP2Element square() - { - return this.multiply(this); - } - - public FP2Element pow(BigInteger exponent) - { - // Implement exponentiation using square-and-multiply - FP2Element result = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); - FP2Element base = this; - for (int i = exponent.bitLength() - 1; i >= 0; i--) - { - result = result.square(); - if (exponent.testBit(i)) - { - result = result.multiply(base); - } - } - return result; - } - - // Getters - public BigInteger getA() - { - return a; - } - - public BigInteger getB() - { - return b; - } - } - - /** - * Computes the Tate-Lichtenbaum pairing ⟨P, Q⟩ as per RFC 6508. - *

- * //* @param P First point (on E(F_p)). - * - * @param Q Second point (on E(F_p)). - * @return Result of the pairing in the field F_p^2. - */ - public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) - { - ECCurve curve = R.getCurve(); - //FP2Element v = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); // Initialize to 1+0i - - // Use correct exponent from RFC 6508: (p+1)/q - BigInteger exponent = p.add(BigInteger.ONE).divide(q); - - String qBits = q.subtract(BigInteger.ONE).toString(2); - ECPoint C = R.normalize(); - BigInteger vx = BigInteger.ONE; - BigInteger vy = BigInteger.ZERO; - - // Evaluate line at Q using F_p² arithmetic - BigInteger Qx = Q.getAffineXCoord().toBigInteger(); - BigInteger Qy = Q.getAffineYCoord().toBigInteger(); - - ECPoint Rnorm = R.normalize(); - BigInteger Rx = Rnorm.getAffineXCoord().toBigInteger(); - BigInteger Ry = Rnorm.getAffineYCoord().toBigInteger(); - int n = q.subtract(BigInteger.ONE).bitLength() - 1; - for (int j = n; j > 0; j--) - { - /* - * BigInteger xPlusY = currentX.add(currentY).mod(p); - BigInteger xMinusY = currentX.subtract(currentY).mod(p); - BigInteger newX = xPlusY.multiply(xMinusY).mod(p); - - // Compute newY = 2xy mod p - BigInteger newY = currentX.multiply(currentY).multiply(BigInteger.valueOf(2)).mod(p); - * */ - BigInteger xPlusY = vx.add(vy).mod(p); - BigInteger xMinusY = vx.subtract(vy).mod(p); - BigInteger newX = xPlusY.multiply(xMinusY).mod(p); - - // Compute newY = 2xy mod p - BigInteger newY = vx.multiply(vy).multiply(BigInteger.valueOf(2)).mod(p); - vx = newX; - vy = newY; - - C = C.normalize(); - BigInteger Cx = C.getAffineXCoord().toBigInteger(); - BigInteger Cy = C.getAffineYCoord().toBigInteger(); - - - // Line function for doubling - //3*(C_x^2 - 1) - BigInteger t_x1_bn = (Cx.multiply(Cx).mod(p).subtract(BigInteger.ONE)).multiply(BigInteger.valueOf(3)); - //Qx + Cx - BigInteger t_bn = Qx.add(Cx); - //3*(C_x^2 - 1)(Qx+Cx) - t_x1_bn = t_x1_bn.multiply(t_bn).mod(p); - //Cy^2*2 - t_bn = Cy.multiply(Cy).mod(p).multiply(BigInteger.valueOf(2)); - //3*(C_x^2 - 1)(Qx+Cx) - Cy^2*2 - t_x1_bn = t_x1_bn.subtract(t_bn).mod(p); - // Cy*2*Qy - BigInteger t_x2_bn = Cy.multiply(BigInteger.valueOf(2)).multiply(Qy).mod(p); - - /* - * BigInteger real = currentX.multiply(pointX) - .subtract(currentY.multiply(pointY)) - .mod(p); - - // Compute imaginary part = x1*y2 + x2*y1 mod p - BigInteger imag = currentX.multiply(pointY) - .add(pointX.multiply(currentY)) - .mod(p);*/ - BigInteger real = vx.multiply(t_x1_bn) - .subtract(vy.multiply(t_x2_bn)) - .mod(p); - - // Compute imaginary part = x1*y2 + x2*y1 mod p - BigInteger imag = vx.multiply(t_x2_bn) - .add(t_x1_bn.multiply(vy)) - .mod(p); - - vx = real; - vy = imag; - - C = C.twice().normalize(); - - if (qBits.charAt(j) == '1') - { - t_x1_bn = Qx.add(Rx).multiply(Cy).mod(p); - BigInteger tmp_t_bn = Qx.add(Cx).multiply(Ry); - t_x1_bn = t_x1_bn.subtract(tmp_t_bn).mod(p); - t_x2_bn = Cx.subtract(Rx).multiply(Qy).mod(p); - real = vx.multiply(t_x1_bn) - .subtract(vy.multiply(t_x2_bn)) - .mod(p); - - // Compute imaginary part = x1*y2 + x2*y1 mod p - imag = vx.multiply(t_x2_bn) - .add(t_x1_bn.multiply(vy)) - .mod(p); - - vx = real; - vy = imag; - C = C.add(Rnorm).normalize(); - } - } - BigInteger xPlusY = vx.add(vy).mod(p); - BigInteger xMinusY = vx.subtract(vy).mod(p); - BigInteger newX = xPlusY.multiply(xMinusY).mod(p); - - // Compute newY = 2xy mod p - BigInteger newY = vx.multiply(vy).multiply(BigInteger.valueOf(2)).mod(p); - vx = newX; - vy = newY; - - xPlusY = vx.add(vy).mod(p); - xMinusY = vx.subtract(vy).mod(p); - newX = xPlusY.multiply(xMinusY).mod(p); - - // Compute newY = 2xy mod p - newY = vx.multiply(vy).multiply(BigInteger.valueOf(2)).mod(p); - - vx = newX; - vy = newY; - - BigInteger w = vx.modInverse(p).multiply(vy).mod(p); - - return w; -// // Final exponentiation -// FP2Element t = v.pow(exponent); -// -// // Convert to F_p representative: b/a mod p -// BigInteger a = t.getA(); -// BigInteger b = t.getB(); -// return b.multiply(a.modInverse(p)).mod(p); - } -// public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) { -// ECCurve curve = R.getCurve(); -// BigInteger qMinus1 = q.subtract(BigInteger.ONE); -// int N = qMinus1.bitLength() - 1; -// -// // Initialize V = (1, 0) in Fp² -// BigInteger vx = BigInteger.ONE; -// BigInteger vy = BigInteger.ZERO; -// -// // Initialize C = R -// ECPoint C = R.normalize(); -// BigInteger Cx = C.getAffineXCoord().toBigInteger(); -// BigInteger Cy = C.getAffineYCoord().toBigInteger(); -// -// // Precompute Q coordinates -// ECPoint Qnorm = Q.normalize(); -// BigInteger Qx = Qnorm.getAffineXCoord().toBigInteger(); -// BigInteger Qy = Qnorm.getAffineYCoord().toBigInteger(); -// -// // Precompute R coordinates for addition steps -// ECPoint Rnorm = R.normalize(); -// BigInteger Rx = Rnorm.getAffineXCoord().toBigInteger(); -// BigInteger Ry = Rnorm.getAffineYCoord().toBigInteger(); -// -// for (; N > 0; N--) { -// // V = V² (complex squaring) -// BigInteger[] squared = pointSquare(vx, vy, p); -// vx = squared[0]; -// vy = squared[1]; -// -// // Calculate line function for doubling -// BigInteger[] T = computeLineFunction(Cx, Cy, Qx, Qy, p); -// -// // V = V * T (complex multiplication) -// BigInteger[] multiplied = pointMultiply(vx, vy, T[0], T[1], p); -// vx = multiplied[0]; -// vy = multiplied[1]; -// -// // Double point C -// BigInteger[] doubled = pointDouble(Cx, Cy, p); -// Cx = doubled[0]; -// Cy = doubled[1]; -// -// if (qMinus1.testBit(N-1)) { -// // Calculate line function for addition -// BigInteger[] TAdd = computeLineFunctionAdd(Cx, Cy, Rx, Ry, Qx, Qy, p); -// -// // V = V * TAdd -// multiplied = pointMultiply(vx, vy, TAdd[0], TAdd[1], p); -// vx = multiplied[0]; -// vy = multiplied[1]; -// -// // Add points C = C + R -// BigInteger[] added = pointAdd(Cx, Cy, Rx, Ry, p); -// Cx = added[0]; -// Cy = added[1]; -// } -// } -// -// // Final squaring V = V² -// BigInteger[] squared = pointSquare(vx, vy, p); -// vx = squared[0]; -// vy = squared[1]; -// squared = pointSquare(vx, vy, p); -// vx = squared[0]; -// vy = squared[1]; -// -// // Compute w = (Vy * Vx⁻¹) mod p -// BigInteger vxInv = vx.modInverse(p); -// return vy.multiply(vxInv).mod(p); -// } -// -// // Helper methods implementing exact C code operations -// private static BigInteger[] pointSquare(BigInteger x, BigInteger y, BigInteger p) { -// BigInteger xPlusY = x.add(y).mod(p); -// BigInteger xMinusY = x.subtract(y).mod(p); -// return new BigInteger[] { -// xPlusY.multiply(xMinusY).mod(p), -// x.multiply(y).multiply(BigInteger.valueOf(2)).mod(p) -// }; -// } -// -// private static BigInteger[] pointMultiply(BigInteger a, BigInteger b, BigInteger c, BigInteger d, BigInteger p) { -// return new BigInteger[] { -// a.multiply(d).add(b.multiply(c)).mod(p), -// a.multiply(c).subtract(b.multiply(d)).mod(p), -// -// }; -// } -// -// private static BigInteger[] pointDouble(BigInteger x, BigInteger y, BigInteger p) { -// BigInteger slope = x.pow(2).multiply(BigInteger.valueOf(3)) -// .mod(p) -// .multiply(y.multiply(BigInteger.valueOf(2)).modInverse(p)) -// .mod(p); -// return new BigInteger[] { -// slope.pow(2).subtract(x.multiply(BigInteger.valueOf(2))).mod(p), -// slope.multiply(x.subtract(slope.pow(2).mod(p))) -// .subtract(y).mod(p) -// }; -// } -// -// private static BigInteger[] pointAdd(BigInteger x1, BigInteger y1, BigInteger x2, BigInteger y2, BigInteger p) { -// BigInteger slope = y2.subtract(y1) -// .multiply(x2.subtract(x1).modInverse(p)) -// .mod(p); -// return new BigInteger[] { -// slope.pow(2).subtract(x1).subtract(x2).mod(p), -// slope.multiply(x1.subtract(slope.pow(2).subtract(x1).subtract(x2).mod(p))) -// .subtract(y1).mod(p) -// }; -// } -// -// private static BigInteger[] computeLineFunction(BigInteger Cx, BigInteger Cy, -// BigInteger Qx, BigInteger Qy, -// BigInteger p) { -// // Calculate 3*(Cx² - 1) -// BigInteger t_x1 = Cx.pow(2).subtract(BigInteger.ONE).multiply(BigInteger.valueOf(3)).mod(p); -// // Calculate Qx + Cx -// BigInteger t = Qx.add(Cx).mod(p); -// // Multiply components -// t_x1 = t_x1.multiply(t).mod(p); -// // Subtract 2*Cy² -// t_x1 = t_x1.subtract(Cy.pow(2).multiply(BigInteger.valueOf(2))).mod(p); -// // Calculate 2*Cy*Qy -// BigInteger t_x2 = Cy.multiply(BigInteger.valueOf(2)).multiply(Qy).mod(p); -// return new BigInteger[] {t_x1, t_x2}; -// } -// -// private static BigInteger[] computeLineFunctionAdd(BigInteger Cx, BigInteger Cy, -// BigInteger Rx, BigInteger Ry, -// BigInteger Qx, BigInteger Qy, -// BigInteger p) { -// // Calculate (Cy - Ry) -// BigInteger numerator = Cy.subtract(Ry).mod(p); -// // Calculate (Cx - Rx)⁻¹ -// BigInteger denominator = Cx.subtract(Rx).modInverse(p); -// BigInteger slope = numerator.multiply(denominator).mod(p); -// -// // Calculate line function components -// BigInteger t_x1 = slope.multiply(Qx.add(Cx).mod(p)).mod(p); -// BigInteger t_x2 = slope.multiply(Qy).negate().mod(p); -// return new BigInteger[] {t_x1, t_x2}; -// } - - } diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index d40a78abfb..187adf0d09 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -41,17 +41,6 @@ public static void main(String[] args) //System.out.println("SAKKE KEM Test " + (testPassed ? "PASSED" : "FAILED")); } - private static byte[] hexStringToByteArray(String s) - { - int len = s.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) - { - data[i / 2] = (byte)((Character.digit(s.charAt(i), 16) << 4) - + Character.digit(s.charAt(i + 1), 16)); - } - return data; - } @Override public String getName() @@ -141,7 +130,6 @@ public void performTest() "E26C6487326B4CD4512AC5CD65681CE1B6AFF4A831852A82A7CF3C521C3C09AA" + "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 ); - ECCurve.Fp curve = new ECCurve.Fp( p, // Prime p BigInteger.valueOf(-3).mod(p), // a = -3 @@ -152,28 +140,14 @@ public void performTest() SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); SecretWithEncapsulation rlt = generator.generateEncapsulated(null); + ECPoint K_bS = curve.createPoint(kbx, kby); SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(new BigInteger(b), K_bS, new SAKKEPublicKeyParameters(curve.createPoint(Zx, Zy)))); - byte[] test = extractor.extractSecret(rlt.getSecret()); - - - System.out.println("K_bS x:" + new String(Hex.encode(K_bS.getXCoord().toBigInteger().toByteArray()))); - System.out.println("K_bS y:" + new String(Hex.encode(K_bS.getYCoord().toBigInteger().toByteArray()))); - ECPoint R_bs = curve.createPoint(Rbx, Rby); - SAKKEKEMSGenerator.pairing(K_bS, R_bs, p, q); - - BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(SSV, b), q); - - System.out.println("r:" + new String(Hex.encode(r.toByteArray()))); - - System.out.println("r:" + new String(Hex.encode(expectedR))); - - Assert.assertTrue(Arrays.areEqual(r.toByteArray(), expectedR)); -// SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); -// generator.generateEncapsulated(null); + byte[] test = extractor.extractSecret(rlt.getEncapsulation()); + Assert.assertTrue(Arrays.areEqual(test, SSV)); } } From d404477baa2ecefe4725cfafb235fd7dc5f5c74c Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 5 Feb 2025 18:26:05 +1030 Subject: [PATCH 092/890] Remove unused functions. Format the code --- .../crypto/kems/SAKKEKEMExtractor.java | 4 +- .../crypto/kems/SAKKEKEMSGenerator.java | 22 ++------ .../bouncycastle/crypto/kems/SAKKEUtils.java | 51 ------------------- .../crypto/kems/test/SAKKEKEMSTest.java | 25 +++------ .../crypto/test/RegressionTest.java | 2 + 5 files changed, 14 insertions(+), 90 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java index 8551f74680..469cd1aa92 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java @@ -6,11 +6,9 @@ import org.bouncycastle.crypto.params.SAKKEPrivateKeyParameters; import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; -import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; -import org.bouncycastle.util.encoders.Hex; public class SAKKEKEMExtractor @@ -49,7 +47,7 @@ public byte[] extractSecret(byte[] encapsulation) // Step 2: Compute w = using pairing BigInteger w = computePairing(R_bS, K_bS, p, q); - System.out.println(new String(Hex.encode(w.toByteArray()))); + //System.out.println(new String(Hex.encode(w.toByteArray()))); //BigInteger w = tatePairing(R_bS.getXCoord().toBigInteger(), R_bS.getYCoord().toBigInteger(), K_bS.getXCoord().toBigInteger(), K_bS.getYCoord().toBigInteger(), q, p); // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) BigInteger twoToN = BigInteger.ONE.shiftLeft(n); diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index d2f81af17b..0d53ce0281 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -64,7 +64,7 @@ public SAKKEKEMSGenerator(SecureRandom random) public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey) { // 1. Generate random SSV in range [0, 2^n - 1] - BigInteger ssv = new BigInteger("123456789ABCDEF0123456789ABCDEF0", 16);//new BigInteger(n, random); + BigInteger ssv = new BigInteger(n, random); // 2. Compute r = HashToIntegerRange(SSV || b, q) BigInteger b = new BigInteger("323031312D30320074656C3A2B34343737303039303031323300", 16); //getRecipientId((SAKKEPublicKey)recipientKey); @@ -127,25 +127,13 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip // Helper method for F_p² exponentiation public static BigInteger[] fp2Exponentiate( BigInteger p, - BigInteger x, - BigInteger y, - BigInteger exponent, - ECCurve.Fp curve - ) - { - BigInteger[] result = new BigInteger[2]; - sakkePointExponent(p, result, x, y, exponent, curve); - return result; - } - - public static boolean sakkePointExponent( - BigInteger p, - BigInteger[] result, BigInteger pointX, BigInteger pointY, BigInteger n, - ECCurve.Fp curve) + ECCurve.Fp curve + ) { + BigInteger[] result = new BigInteger[2]; // Initialize result with the original point BigInteger currentX = pointX; @@ -174,6 +162,6 @@ public static boolean sakkePointExponent( result[0] = currentX; result[1] = currentY; - return true; + return result; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java index 45fbbb38b5..891f2ae878 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java @@ -2,45 +2,11 @@ import java.math.BigInteger; -import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA256Digest; -import org.bouncycastle.math.ec.ECPoint; -import org.bouncycastle.util.encoders.Hex; public class SAKKEUtils { - public static ECPoint sakkePointExponent(ECPoint point, BigInteger n) { - if (n.equals(BigInteger.ZERO)) { - throw new IllegalArgumentException("Exponent cannot be zero."); - } - - ECPoint result = point; - int N = n.bitLength() - 1; - - for (; N != 0; --N) { - result = sakkePointSquare(result); - if (n.testBit(N - 1)) { - result = sakkePointsMultiply(result, point); - } - } - return result; - } - - public static ECPoint sakkePointSquare(ECPoint point) { - BigInteger x = point.getAffineXCoord().toBigInteger(); - BigInteger y = point.getAffineYCoord().toBigInteger(); - - BigInteger bx1 = x.add(y); - BigInteger bx2 = x.subtract(y); - BigInteger newX = bx1.multiply(bx2).mod(point.getCurve().getField().getCharacteristic()); - BigInteger newY = x.multiply(y).multiply(BigInteger.valueOf(2)).mod(point.getCurve().getField().getCharacteristic()); - - return point.getCurve().createPoint(newX, newY); - } - public static ECPoint sakkePointsMultiply(ECPoint p1, ECPoint p2) { - return p1.add(p2).normalize(); - } public static BigInteger hashToIntegerRange(byte[] input, BigInteger q) { // RFC 6508 Section 5.1: Hashing to an Integer Range @@ -66,32 +32,15 @@ public static BigInteger hashToIntegerRange(byte[] input, BigInteger q) // h_i = hashfn(h_{i-1}) digest.update(h, 0, h.length); digest.doFinal(h, 0); - //System.out.println("h_"+i+":" +new String(Hex.encode(h))); // v_i = hashfn(h_i || A) digest.update(h, 0, h.length); digest.update(A, 0, A.length); byte[] v_i = new byte[digest.getDigestSize()]; digest.doFinal(v_i, 0); - //System.out.println("v_"+i+":" +new String(Hex.encode(v_i))); // Append v_i to v' v = v.shiftLeft(v_i.length * 8).add(new BigInteger(1, v_i)); } - //System.out.println("v:" +new String(Hex.encode(v.toByteArray()))); // Step 6: v = v' mod n return v.mod(q); } - - public static byte[] hash(byte[] data) - { - Digest digest = new SHA256Digest(); - byte[] rlt = new byte[digest.getDigestSize()]; - digest.update(data, 0, data.length); - digest.doFinal(rlt, 0); - return rlt; - } - - public static byte[] hash(ECPoint point) - { - return hash(point.getEncoded(false)); // Use uncompressed encoding - } } diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index 187adf0d09..7a2b0d5b64 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -7,13 +7,11 @@ import org.bouncycastle.crypto.SecretWithEncapsulation; import org.bouncycastle.crypto.kems.SAKKEKEMExtractor; import org.bouncycastle.crypto.kems.SAKKEKEMSGenerator; -import org.bouncycastle.crypto.kems.SAKKEUtils; import org.bouncycastle.crypto.params.SAKKEPrivateKeyParameters; import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.FixedSecureRandom; import org.bouncycastle.util.test.SimpleTest; @@ -27,18 +25,6 @@ public static void main(String[] args) { SAKKEKEMSTest test = new SAKKEKEMSTest(); test.performTest(); - // Expected Rb values -// BigInteger expectedRbx = new BigInteger("44E8AD44AB8592A6A5A3DDCA5CF896C718043606A01D650DEF37A01F37C228C332FC317354E2C274D4DAF8AD001054C7... -// BigInteger expectedRby = new BigInteger("557E134AD85BB1D4B9CE4F8BE4B08A12BABF55B1D6F1D7A638019EA28E15AB1C9F76375FDD1210D4F4351B9A009486B7... -// -// // Instantiate SAKKE KEM Generator -// SAKKEKEMSGenerator kem = new SAKKEKEMSGenerator(); -// EncapsulatedData encapsulatedData = kem.encapsulate(SSV); -// -// // Validate results -// boolean testPassed = expectedRbx.equals(encapsulatedData.getRbx()) && expectedRby.equals(encapsulatedData.getRby()); - - //System.out.println("SAKKE KEM Test " + (testPassed ? "PASSED" : "FAILED")); } @@ -78,7 +64,7 @@ public void performTest() // byte[] b = Hex.decode("323031312D30320074656C3A2B34343737303039303031323300"); - byte[] SSV = Hex.decode("123456789ABCDEF0123456789ABCDEF0"); + byte[] ssv = Hex.decode("123456789ABCDEF0123456789ABCDEF0"); byte[] expectedR = Hex.decode("13EE3E1B8DAC5DB168B1CEB32F0566A4C273693F78BAFFA2A2EE6A686E6BD90F8206CCAB84E7F" + "42ED39BD4FB131012ECCA2ECD2119414560C17CAB46B956A80F58A3302EB3E2C9A228FBA7ED34D8ACA2392DA1FFB0B17B2320AE09AAEDF" + "D0235F6FE0EB65337A63F9CC97728B8E5AD0460FADE144369AA5B2166213247712096"); @@ -137,17 +123,18 @@ public void performTest() g,// Order of the subgroup (from RFC 6509) BigInteger.ONE // Cofactor = 1 ); - SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); - SecretWithEncapsulation rlt = generator.generateEncapsulated(null); + SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(ssv), + new FixedSecureRandom.Data(b)}); + SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(random); + SecretWithEncapsulation rlt = generator.generateEncapsulated(null); ECPoint K_bS = curve.createPoint(kbx, kby); - SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(new BigInteger(b), K_bS, new SAKKEPublicKeyParameters(curve.createPoint(Zx, Zy)))); byte[] test = extractor.extractSecret(rlt.getEncapsulation()); - Assert.assertTrue(Arrays.areEqual(test, SSV)); + Assert.assertTrue(Arrays.areEqual(test, ssv)); } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java index c945e3154a..1025007493 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto.test; +import org.bouncycastle.crypto.kems.test.SAKKEKEMSTest; import org.bouncycastle.util.test.SimpleTest; import org.bouncycastle.util.test.Test; @@ -195,6 +196,7 @@ public class RegressionTest new SparkleTest(), new ISAPTest(), new ConcatenationKDFTest(), + new SAKKEKEMSTest(), }; public static void main(String[] args) From 7e41e48171becbf876a10e5a9352a2f4e44b726f Mon Sep 17 00:00:00 2001 From: David Hook Date: Thu, 6 Feb 2025 13:55:53 +1100 Subject: [PATCH 093/890] minor formatting --- pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java index 190e3a82e4..bd9a5f705d 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java @@ -276,9 +276,8 @@ private boolean parseHeaders() } catch (Exception e) { - throw new ArmoredInputException(e.getMessage()); + throw new ArmoredInputException(e.getMessage()); } - if (line.trim().length() == 0) { break; From 0aa9d1c5e1455aac4e734734f7a643ee1d13691f Mon Sep 17 00:00:00 2001 From: David Hook Date: Thu, 6 Feb 2025 13:59:19 +1100 Subject: [PATCH 094/890] minor formatting --- .../operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java | 1 - 1 file changed, 1 deletion(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java index c6fcaebe06..bcb5a5df55 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBEKeyEncryptionMethodGenerator.java @@ -185,7 +185,6 @@ protected byte[] getEskAndTag(int kekAlgorithm, int aeadAlgorithm, byte[] sessio { throw new PGPException("cannot encrypt session info", e); } - } private static String getBaseAEADAlgorithm(int encAlgorithm) From c6e2abd2b7bd9829ab553e256ea41d2ab1829a36 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 6 Feb 2025 17:20:19 +1030 Subject: [PATCH 095/890] Set teh parameter settings of SAKKE --- .../crypto/kems/SAKKEKEMExtractor.java | 17 ++-- .../crypto/kems/SAKKEKEMSGenerator.java | 90 ++++--------------- .../params/SAKKEPrivateKeyParameters.java | 23 ++--- .../params/SAKKEPublicKeyParameters.java | 56 ++++++++---- .../crypto/kems/test/SAKKEKEMSTest.java | 26 +++++- 5 files changed, 97 insertions(+), 115 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java index 469cd1aa92..418ec767dd 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java @@ -19,21 +19,21 @@ public class SAKKEKEMExtractor private final BigInteger q; private final ECPoint P; private final ECPoint Z_S; - private final ECPoint K_bS; // Receiver's RSK + private final ECPoint K_bs; private final int n; // Security parameter - private final SAKKEPrivateKeyParameters privateKey; + private final BigInteger identifier; public SAKKEKEMExtractor(SAKKEPrivateKeyParameters privateKey) { - this.privateKey = privateKey; SAKKEPublicKeyParameters publicKey = privateKey.getPublicParams(); this.curve = publicKey.getCurve(); this.q = publicKey.getQ(); this.P = publicKey.getP(); - this.p = publicKey.getp(); + this.p = publicKey.getPrime(); this.Z_S = publicKey.getZ(); - this.K_bS = privateKey.getPrivatePoint(); + this.K_bs = privateKey.getRSK(); this.n = publicKey.getN(); + this.identifier = publicKey.getIdentifier(); } @Override @@ -46,16 +46,15 @@ public byte[] extractSecret(byte[] encapsulation) BigInteger H = new BigInteger(Arrays.copyOfRange(encapsulation, 257, 274)); // Step 2: Compute w = using pairing - BigInteger w = computePairing(R_bS, K_bS, p, q); - //System.out.println(new String(Hex.encode(w.toByteArray()))); - //BigInteger w = tatePairing(R_bS.getXCoord().toBigInteger(), R_bS.getYCoord().toBigInteger(), K_bS.getXCoord().toBigInteger(), K_bS.getYCoord().toBigInteger(), q, p); + BigInteger w = computePairing(R_bS, K_bs, p, q); + // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) BigInteger twoToN = BigInteger.ONE.shiftLeft(n); BigInteger mask = SAKKEUtils.hashToIntegerRange(w.toByteArray(), twoToN); BigInteger ssv = H.xor(mask); // Step 4: Compute r = HashToIntegerRange(SSV || b) - BigInteger b = privateKey.getB(); + BigInteger b = identifier; BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); // Step 5: Validate R_bS diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index 0d53ce0281..fe48251968 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -3,11 +3,11 @@ import org.bouncycastle.crypto.EncapsulatedSecretGenerator; import org.bouncycastle.crypto.SecretWithEncapsulation; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; -import org.bouncycastle.util.encoders.Hex; import java.math.BigInteger; import java.security.SecureRandom; @@ -15,44 +15,6 @@ public class SAKKEKEMSGenerator implements EncapsulatedSecretGenerator { - - private static final BigInteger p = new BigInteger( - "997ABB1F0A563FDA65C61198DAD0657A416C0CE19CB48261BE9AE358B3E01A2E" + - "F40AAB27E2FC0F1B228730D531A59CB0E791B39FF7C88A19356D27F4A666A6D0" + - "E26C6487326B4CD4512AC5CD65681CE1B6AFF4A831852A82A7CF3C521C3C09AA" + - "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 - ); - - private static final BigInteger q = new BigInteger( - "265EAEC7C2958FF69971846636B4195E905B0338672D20986FA6B8D62CF8068B" + - "BD02AAC9F8BF03C6C8A1CC354C69672C39E46CE7FDF222864D5B49FD2999A9B4" + - "389B1921CC9AD335144AB173595A07386DABFD2A0C614AA0A9F3CF14870F026A" + - "A7E535ABD5A5C7C7FF38FA08E2615F6C203177C42B1EB3A1D99B601EBFAA17FB", 16 - ); - - private static final BigInteger Px = new BigInteger( - "53FC09EE332C29AD0A7990053ED9B52A2B1A2FD60AEC69C698B2F204B6FF7CBF" + - "B5EDB6C0F6CE2308AB10DB9030B09E1043D5F22CDB9DFA55718BD9E7406CE890" + - "9760AF765DD5BCCB337C86548B72F2E1A702C3397A60DE74A7C1514DBA66910D" + - "D5CFB4CC80728D87EE9163A5B63F73EC80EC46C4967E0979880DC8ABEAE63895", 16 - ); - - private static final BigInteger Py = new BigInteger( - "0A8249063F6009F1F9F1F0533634A135D3E82016029906963D778D821E141178" + - "F5EA69F4654EC2B9E7F7F5E5F0DE55F66B598CCF9A140B2E416CFF0CA9E032B9" + - "70DAE117AD547C6CCAD696B5B7652FE0AC6F1E80164AA989492D979FC5A4D5F2" + - "13515AD7E9CB99A980BDAD5AD5BB4636ADB9B5706A67DCDE75573FD71BEF16D7", 16 - ); - - BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + - " C6A87BD1 FB94C41E 27FABE65 8E015A87\n" + - " 371E9474 4C96FEDA 449AE956 3F8BC446\n" + - " CBFDA85D 5D00EF57 7072DA8F 541721BE\n" + - " EE0FAED1 828EAB90 B99DFB01 38C78433\n" + - " 55DF0460 B4A9FD74 B4F1A32B CAFA1FFA\n" + - " D682C033 A7942BCC E3720F20 B9B7B040\n" + - " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); - private static final int n = 128; private final SecureRandom random; public SAKKEKEMSGenerator(SecureRandom random) @@ -63,54 +25,34 @@ public SAKKEKEMSGenerator(SecureRandom random) @Override public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey) { + SAKKEPublicKeyParameters keyParameters = (SAKKEPublicKeyParameters)recipientKey; + ECPoint Z = keyParameters.getZ(); + BigInteger b = keyParameters.getIdentifier(); + BigInteger p = keyParameters.getPrime(); + BigInteger q = keyParameters.getQ(); + BigInteger g = keyParameters.getG(); + int n = keyParameters.getN(); + ECCurve curve = keyParameters.getCurve(); + ECPoint P = keyParameters.getP(); + // 1. Generate random SSV in range [0, 2^n - 1] BigInteger ssv = new BigInteger(n, random); + // 2. Compute r = HashToIntegerRange(SSV || b, q) - BigInteger b = new BigInteger("323031312D30320074656C3A2B34343737303039303031323300", 16); //getRecipientId((SAKKEPublicKey)recipientKey); + BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); - //System.out.println(new String(Hex.encode(r.toByteArray()))); - ECCurve.Fp curve = new ECCurve.Fp( - p, // Prime p - BigInteger.valueOf(-3).mod(p), // a = -3 - BigInteger.ZERO, // , - g, // Order of the subgroup (from RFC 6509) - BigInteger.ONE // Cofactor = 1 - ); - ECPoint P = curve.createPoint(Px, Py); - - ECPoint Z = curve.createPoint( - new BigInteger("5958EF1B1679BF099B3A030DF255AA6A" + - "23C1D8F143D4D23F753E69BD27A832F3" + - "8CB4AD53DDEF4260B0FE8BB45C4C1FF5" + - "10EFFE300367A37B61F701D914AEF097" + - "24825FA0707D61A6DFF4FBD7273566CD" + - "DE352A0B04B7C16A78309BE640697DE7" + - "47613A5FC195E8B9F328852A579DB8F9" + - "9B1D0034479EA9C5595F47C4B2F54FF2", 16), // Px - new BigInteger("1508D37514DCF7A8E143A6058C09A6BF" + - "2C9858CA37C258065AE6BF7532BC8B5B" + - "63383866E0753C5AC0E72709F8445F2E" + - "6178E065857E0EDA10F68206B63505ED" + - "87E534FB2831FF957FB7DC619DAE6130" + - "1EEACC2FDA3680EA4999258A833CEA8F" + - "C67C6D19487FB449059F26CC8AAB655A" + - "B58B7CC796E24E9A394095754F5F8BAE", 16) // Py - ); + // 3. Compute R_(b,S) = [r]([b]P + Z_S) ECPoint bP = P.multiply(b).normalize(); ECPoint R_bS = bP.add(Z).multiply(r).normalize(); // [r]([b]P + Z_S) -// System.out.println("R_Bs x:" + new String(Hex.encode(R_bS.getXCoord().toBigInteger().toByteArray()))); -// System.out.println("R_Bs y:" + new String(Hex.encode(R_bS.getYCoord().toBigInteger().toByteArray()))); - // 4. Compute H = SSV XOR HashToIntegerRange( g^r, 2^n ) BigInteger[] v = fp2Exponentiate(p, BigInteger.ONE, g, r, curve); BigInteger g_r = v[1].multiply(v[0].modInverse(p)).mod(p); BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(n)); // 2^n - //System.out.println(new String(Hex.encode(mask.toByteArray()))); BigInteger H = ssv.xor(mask); //System.out.println(new String(Hex.encode(H.toByteArray()))); @@ -124,14 +66,12 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip } - // Helper method for F_p² exponentiation public static BigInteger[] fp2Exponentiate( BigInteger p, BigInteger pointX, BigInteger pointY, BigInteger n, - ECCurve.Fp curve - ) + ECCurve curve) { BigInteger[] result = new BigInteger[2]; diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java index 2c83f35ba3..c90a373cf0 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java @@ -7,30 +7,31 @@ public class SAKKEPrivateKeyParameters extends AsymmetricKeyParameter { - private final BigInteger b; // User's identity - private final ECPoint K; // Private key K_a private final SAKKEPublicKeyParameters publicParams; + private final BigInteger z; // KMS Public Key: Z = [z]P + private final ECPoint rsk; - public SAKKEPrivateKeyParameters(BigInteger b, ECPoint K, SAKKEPublicKeyParameters publicParams) + public SAKKEPrivateKeyParameters(BigInteger z, ECPoint rsk, SAKKEPublicKeyParameters publicParams) { super(true); - this.b = b; - this.K = K; + this.z = z; + this.rsk = rsk; this.publicParams = publicParams; } - public BigInteger getB() + public SAKKEPublicKeyParameters getPublicParams() { - return b; + return publicParams; } - public SAKKEPublicKeyParameters getPublicParams() + + public BigInteger getMasterSecret() { - return publicParams; + return z; } - public ECPoint getPrivatePoint() + public ECPoint getRSK() { - return K; + return rsk; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java index 66ce4e81ef..a062d766fc 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java @@ -2,6 +2,8 @@ import java.math.BigInteger; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.encoders.Hex; @@ -17,6 +19,13 @@ public class SAKKEPublicKeyParameters "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 ); + private static final BigInteger q = new BigInteger( + "265EAEC7C2958FF69971846636B4195E905B0338672D20986FA6B8D62CF8068B" + + "BD02AAC9F8BF03C6C8A1CC354C69672C39E46CE7FDF222864D5B49FD2999A9B4" + + "389B1921CC9AD335144AB173595A07386DABFD2A0C614AA0A9F3CF14870F026A" + + "A7E535ABD5A5C7C7FF38FA08E2615F6C203177C42B1EB3A1D99B601EBFAA17FB", 16 + ); + private static final BigInteger Px = new BigInteger( "53FC09EE332C29AD0A7990053ED9B52A2B1A2FD60AEC69C698B2F204B6FF7CBF" + "B5EDB6C0F6CE2308AB10DB9030B09E1043D5F22CDB9DFA55718BD9E7406CE890" + @@ -31,7 +40,8 @@ public class SAKKEPublicKeyParameters "13515AD7E9CB99A980BDAD5AD5BB4636ADB9B5706A67DCDE75573FD71BEF16D7", 16 ); - + // g = + // < , > is Tate-Lichtenbaum Pairing private static final BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + " C6A87BD1 FB94C41E 27FABE65 8E015A87\n" + " 371E9474 4C96FEDA 449AE956 3F8BC446\n" + @@ -41,13 +51,6 @@ public class SAKKEPublicKeyParameters " D682C033 A7942BCC E3720F20 B9B7B040\n" + " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); - private static final BigInteger q = new BigInteger( - "265EAEC7C2958FF69971846636B4195E905B0338672D20986FA6B8D62CF8068B" + - "BD02AAC9F8BF03C6C8A1CC354C69672C39E46CE7FDF222864D5B49FD2999A9B4" + - "389B1921CC9AD335144AB173595A07386DABFD2A0C614AA0A9F3CF14870F026A" + - "A7E535ABD5A5C7C7FF38FA08E2615F6C203177C42B1EB3A1D99B601EBFAA17FB", 16 - ); - private static final ECCurve.Fp curve = new ECCurve.Fp( p, // Prime p BigInteger.valueOf(-3).mod(p), // a = -3 @@ -56,18 +59,34 @@ public class SAKKEPublicKeyParameters BigInteger.ONE // Cofactor = 1 ); + private static final ECPoint P = curve.createPoint(Px, Py); - private final ECPoint Z; // KMS Public Key: Z = [z]P + private final ECPoint Z; + + private final BigInteger identifier; // User's identity private static final int n = 128; // SSV bit length - public SAKKEPublicKeyParameters(ECPoint Z) + private final Digest digest = new SHA256Digest(); + + public SAKKEPublicKeyParameters(BigInteger identifier, ECPoint Z) { super(false); + this.identifier = identifier; this.Z = Z; } // Getters + public BigInteger getIdentifier() + { + return identifier; + } + + public ECPoint getZ() + { + return Z; + } + public ECCurve getCurve() { return curve; @@ -78,12 +97,7 @@ public ECPoint getP() return P; } - public ECPoint getZ() - { - return Z; - } - - public BigInteger getp() + public BigInteger getPrime() { return p; } @@ -97,4 +111,14 @@ public int getN() { return n; } + + public Digest getDigest() + { + return digest; + } + + public BigInteger getG() + { + return g; + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index 7a2b0d5b64..8dd350c673 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -38,6 +38,20 @@ public String getName() public void performTest() throws Exception { + + final BigInteger Px = new BigInteger( + "53FC09EE332C29AD0A7990053ED9B52A2B1A2FD60AEC69C698B2F204B6FF7CBF" + + "B5EDB6C0F6CE2308AB10DB9030B09E1043D5F22CDB9DFA55718BD9E7406CE890" + + "9760AF765DD5BCCB337C86548B72F2E1A702C3397A60DE74A7C1514DBA66910D" + + "D5CFB4CC80728D87EE9163A5B63F73EC80EC46C4967E0979880DC8ABEAE63895", 16 + ); + + final BigInteger Py = new BigInteger( + "0A8249063F6009F1F9F1F0533634A135D3E82016029906963D778D821E141178" + + "F5EA69F4654EC2B9E7F7F5E5F0DE55F66B598CCF9A140B2E416CFF0CA9E032B9" + + "70DAE117AD547C6CCAD696B5B7652FE0AC6F1E80164AA989492D979FC5A4D5F2" + + "13515AD7E9CB99A980BDAD5AD5BB4636ADB9B5706A67DCDE75573FD71BEF16D7", 16 + ); BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068" + " C6A87BD1 FB94C41E 27FABE65 8E015A87" + " 371E9474 4C96FEDA 449AE956 3F8BC446" + @@ -127,14 +141,18 @@ public void performTest() SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(ssv), new FixedSecureRandom.Data(b)}); SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(random); - SecretWithEncapsulation rlt = generator.generateEncapsulated(null); + SecretWithEncapsulation rlt = generator.generateEncapsulated(new SAKKEPublicKeyParameters(new BigInteger(b), curve.createPoint(Zx, Zy))); + ECPoint P = curve.createPoint(Px, Py); + + BigInteger computed_g2 = SAKKEKEMExtractor.computePairing(P, P, p, q); + Assert.assertTrue(computed_g2.equals(g)); ECPoint K_bS = curve.createPoint(kbx, kby); - SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(new BigInteger(b), K_bS, - new SAKKEPublicKeyParameters(curve.createPoint(Zx, Zy)))); + + SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(z, K_bS, + new SAKKEPublicKeyParameters(new BigInteger(b), curve.createPoint(Zx, Zy)))); byte[] test = extractor.extractSecret(rlt.getEncapsulation()); Assert.assertTrue(Arrays.areEqual(test, ssv)); - } } From 4abc7afa3d0b0dbb6b26e1b7c876c5a385d13924 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 6 Feb 2025 16:23:06 +0700 Subject: [PATCH 096/890] Let EC J-PAKE work with custom curves --- .../crypto/agreement/ecjpake/ECJPAKECurve.java | 8 ++++---- .../crypto/agreement/ecjpake/ECJPAKECurves.java | 13 +++++++------ .../agreement/ecjpake/ECJPAKEParticipant.java | 6 +----- .../crypto/agreement/test/ECJPAKEUtilTest.java | 3 +-- 4 files changed, 13 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKECurve.java b/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKECurve.java index 90edecb756..a90ef1629d 100644 --- a/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKECurve.java +++ b/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKECurve.java @@ -18,7 +18,7 @@ */ public class ECJPAKECurve { - private final ECCurve.Fp curve; + private final ECCurve.AbstractFp curve; private final ECPoint g; /** @@ -116,7 +116,7 @@ public ECJPAKECurve(BigInteger q, BigInteger a, BigInteger b, BigInteger n, BigI * groups in {@link ECJPAKECurves}. * These pre-approved curves can avoid the expensive checks. */ - ECJPAKECurve(ECCurve.Fp curve, ECPoint g) + ECJPAKECurve(ECCurve.AbstractFp curve, ECPoint g) { ECJPAKEUtil.validateNotNull(curve, "curve"); ECJPAKEUtil.validateNotNull(g, "g"); @@ -127,7 +127,7 @@ public ECJPAKECurve(BigInteger q, BigInteger a, BigInteger b, BigInteger n, BigI this.g = g; } - public ECCurve.Fp getCurve() + public ECCurve.AbstractFp getCurve() { return curve; } @@ -159,7 +159,7 @@ public BigInteger getH() public BigInteger getQ() { - return curve.getQ(); + return curve.getField().getCharacteristic(); } private static BigInteger calculateDeterminant(BigInteger q, BigInteger a, BigInteger b) diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKECurves.java b/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKECurves.java index 3a4c04b724..ef72b3345f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKECurves.java +++ b/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKECurves.java @@ -1,7 +1,7 @@ package org.bouncycastle.crypto.agreement.ecjpake; -import org.bouncycastle.asn1.nist.NISTNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.math.ec.ECCurve; /** @@ -37,13 +37,14 @@ public class ECJPAKECurves static { - NIST_P256 = fromX9ECParameters(NISTNamedCurves.getByName("P-256")); - NIST_P384 = fromX9ECParameters(NISTNamedCurves.getByName("P-384")); - NIST_P521 = fromX9ECParameters(NISTNamedCurves.getByName("P-521")); + NIST_P256 = getCurve("P-256"); + NIST_P384 = getCurve("P-384"); + NIST_P521 = getCurve("P-521"); } - private static ECJPAKECurve fromX9ECParameters(X9ECParameters x9) + private static ECJPAKECurve getCurve(String curveName) { - return new ECJPAKECurve((ECCurve.Fp)x9.getCurve(), x9.getG()); + X9ECParameters x9 = CustomNamedCurves.getByName(curveName); + return new ECJPAKECurve((ECCurve.AbstractFp)x9.getCurve(), x9.getG()); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKEParticipant.java b/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKEParticipant.java index e2b323b9c6..66c5bcb986 100644 --- a/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKEParticipant.java +++ b/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKEParticipant.java @@ -107,9 +107,7 @@ public class ECJPAKEParticipant */ private String partnerParticipantId; - private ECCurve.Fp ecCurve; - private BigInteger ecca; - private BigInteger eccb; + private ECCurve.AbstractFp ecCurve; private BigInteger q; private BigInteger h; private BigInteger n; @@ -255,8 +253,6 @@ public ECJPAKEParticipant( this.password = Arrays.copyOf(password, password.length); this.ecCurve = curve.getCurve(); - this.ecca = curve.getA(); - this.eccb = curve.getB(); this.g = curve.getG(); this.h = curve.getH(); this.n = curve.getN(); diff --git a/core/src/test/java/org/bouncycastle/crypto/agreement/test/ECJPAKEUtilTest.java b/core/src/test/java/org/bouncycastle/crypto/agreement/test/ECJPAKEUtilTest.java index 86f7ab2571..59e16d2fd9 100644 --- a/core/src/test/java/org/bouncycastle/crypto/agreement/test/ECJPAKEUtilTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/agreement/test/ECJPAKEUtilTest.java @@ -18,7 +18,6 @@ public class ECJPAKEUtilTest extends TestCase { - private static final BigInteger TEN = BigInteger.valueOf(10); private static final BigInteger ONE = BigInteger.valueOf(1); public void testValidateParticipantIdsDiffer() @@ -217,7 +216,7 @@ public void testValidateZeroKnowledgeProof() } // (x,y) elements for Gx are not in Fq ie: not in [0,q-1] - ECCurve.Fp curve = (ECCurve.Fp)curve1.getCurve(); + ECCurve.AbstractFp curve = curve1.getCurve(); try { ECPoint invalidGx_1 = curve.createPoint(ONE.negate(), ONE); From 4189cec15b7df41b8c2a098a3edc0917da9c6c4a Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 6 Feb 2025 17:14:26 +0700 Subject: [PATCH 097/890] Tolerate custom EC curves --- .../bouncycastle/eac/jcajce/JcaPublicKeyConverter.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkix/src/main/jdk1.4/org/bouncycastle/eac/jcajce/JcaPublicKeyConverter.java b/pkix/src/main/jdk1.4/org/bouncycastle/eac/jcajce/JcaPublicKeyConverter.java index f412e8e009..eab5e73a0b 100644 --- a/pkix/src/main/jdk1.4/org/bouncycastle/eac/jcajce/JcaPublicKeyConverter.java +++ b/pkix/src/main/jdk1.4/org/bouncycastle/eac/jcajce/JcaPublicKeyConverter.java @@ -128,11 +128,13 @@ public PublicKeyDataObject getPublicKeyDataObject(ASN1ObjectIdentifier usage, Pu ECPublicKey pubKey = (ECPublicKey)publicKey; ECParameterSpec params = pubKey.getParameters(); + ECCurve.AbstractFp curve = (ECCurve.AbstractFp)params.getCurve(); + return new ECDSAPublicKey( usage, - ((ECCurve.Fp)params.getCurve()).getQ(), - ((ECFieldElement.Fp)params.getCurve().getA()).toBigInteger(), - ((ECFieldElement.Fp)params.getCurve().getB()).toBigInteger(), + curve.getField().getCharacteristic(), + curve.getA().toBigInteger(), + curve.getB().toBigInteger(), params.getG().getEncoded(false), params.getN(), pubKey.getQ().getEncoded(false), From 3559d88b4cf6b1b0b7da28e3dd62dab284a37336 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 6 Feb 2025 19:26:03 +0700 Subject: [PATCH 098/890] Use custom curves in DualEC, FWIW --- .../crypto/prng/drbg/DualECSP800DRBG.java | 41 ++++++++----------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java index 83fa545e58..2d3aaba7d9 100644 --- a/core/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java +++ b/core/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java @@ -2,8 +2,8 @@ import java.math.BigInteger; -import org.bouncycastle.asn1.nist.NISTNamedCurves; import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.prng.EntropySource; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECMultiplier; @@ -36,23 +36,18 @@ public class DualECSP800DRBG private static final BigInteger p521_Qx = new BigInteger("1b9fa3e518d683c6b65763694ac8efbaec6fab44f2276171a42726507dd08add4c3b3f4c1ebc5b1222ddba077f722943b24c3edfa0f85fe24d0c8c01591f0be6f63", 16); private static final BigInteger p521_Qy = new BigInteger("1f3bdba585295d9a1110d1df1f9430ef8442c5018976ff3437ef91b81dc0b8132c8d5c39c32d0e004a3092b7d327c0e7a4d26d2c7b69b58f9066652911e457779de", 16); - private static final DualECPoints[] nistPoints; - - static + private static final DualECPoints[] nistPoints = new DualECPoints[] { - nistPoints = new DualECPoints[3]; - - ECCurve.Fp curve = (ECCurve.Fp)NISTNamedCurves.getByNameLazy("P-256").getCurve(); - - nistPoints[0] = new DualECPoints(128, curve.createPoint(p256_Px, p256_Py), curve.createPoint(p256_Qx, p256_Qy), 1); - - curve = (ECCurve.Fp)NISTNamedCurves.getByNameLazy("P-384").getCurve(); - - nistPoints[1] = new DualECPoints(192, curve.createPoint(p384_Px, p384_Py), curve.createPoint(p384_Qx, p384_Qy), 1); + createDualECPoints("P-256", 128, p256_Px, p256_Py, p256_Qx, p256_Qy, 1), + createDualECPoints("P-384", 192, p384_Px, p384_Py, p384_Qx, p384_Qy, 1), + createDualECPoints("P-521", 256, p521_Px, p521_Py, p521_Qx, p521_Qy, 1), + }; - curve = (ECCurve.Fp)NISTNamedCurves.getByNameLazy("P-521").getCurve(); - - nistPoints[2] = new DualECPoints(256, curve.createPoint(p521_Px, p521_Py), curve.createPoint(p521_Qx, p521_Qy), 1); + private static DualECPoints createDualECPoints(String curveName, int securityStrength, BigInteger Px, + BigInteger Py, BigInteger Qx, BigInteger Qy, int cofactor) + { + ECCurve.AbstractFp c = (ECCurve.AbstractFp)CustomNamedCurves.getByNameLazy(curveName).getCurve(); + return new DualECPoints(securityStrength, c.createPoint(Px, Py), c.createPoint(Qx, Qy), cofactor); } @@ -67,7 +62,6 @@ public class DualECSP800DRBG private int _securityStrength; private int _seedlen; private int _outlen; - private ECCurve.Fp _curve; private ECPoint _P; private ECPoint _Q; private byte[] _s; @@ -210,11 +204,9 @@ public int generate(byte[] output, byte[] additionalInput, boolean predictionRes { s = getScalarMultipleXCoord(_P, s); - //System.err.println("S: " + new String(Hex.encode(_s))); - byte[] r = getScalarMultipleXCoord(_Q, s).toByteArray(); - if (r.length > _outlen) + if (r.length >= _outlen) { System.arraycopy(r, r.length - _outlen, output, outOffset, _outlen); } @@ -223,7 +215,6 @@ public int generate(byte[] output, byte[] additionalInput, boolean predictionRes System.arraycopy(r, 0, output, outOffset + (_outlen - r.length), r.length); } - //System.err.println("R: " + new String(Hex.encode(r))); outOffset += _outlen; _reseedCounter++; @@ -237,13 +228,17 @@ public int generate(byte[] output, byte[] additionalInput, boolean predictionRes int required = output.length - outOffset; - if (r.length > _outlen) + if (r.length >= _outlen) { System.arraycopy(r, r.length - _outlen, output, outOffset, required); } else { - System.arraycopy(r, 0, output, outOffset + (_outlen - r.length), required); + int outPos = _outlen - r.length; + if (outPos < required) + { + System.arraycopy(r, 0, output, outOffset + outPos, required - outPos); + } } _reseedCounter++; From 463c9c6b37e64521cc9aecf48f2928358b6b6c28 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 7 Feb 2025 14:10:33 +1030 Subject: [PATCH 099/890] Add javadoc and add random tests. --- .../crypto/kems/SAKKEKEMExtractor.java | 133 +++++++++++----- .../crypto/kems/SAKKEKEMSGenerator.java | 143 +++++++++++++----- .../bouncycastle/crypto/kems/SAKKEUtils.java | 46 ------ .../params/SAKKEPrivateKeyParameters.java | 64 ++++++-- .../params/SAKKEPublicKeyParameters.java | 102 +++++++++++-- .../crypto/kems/test/SAKKEKEMSTest.java | 89 ++++++----- 6 files changed, 398 insertions(+), 179 deletions(-) delete mode 100644 core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java index 418ec767dd..8e2307e153 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java @@ -2,6 +2,7 @@ import java.math.BigInteger; +import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.EncapsulatedSecretExtractor; import org.bouncycastle.crypto.params.SAKKEPrivateKeyParameters; import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; @@ -10,7 +11,22 @@ import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; - +/** + * Implements the receiver side of the SAKKE (Sakai-Kasahara Key Encryption) protocol + * as defined in RFC 6508. This class extracts the shared secret value (SSV) from + * encapsulated data using the receiver's private key. + *

+ * The extraction process follows these steps (RFC 6508, Section 6.2.2): + *

    + *
  1. Parse encapsulated data into R_(b,S) and H
  2. + *
  3. Compute pairing result w = <R_(b,S), K_(b,S)>
  4. + *
  5. Recover SSV via SSV = H XOR HashToIntegerRange(w, 2^n)
  6. + *
  7. Validate R_(b,S) by recomputing it with derived parameters
  8. + *
+ *

+ * + * @see Sakai-Kasahara Key Encryption (SAKKE) + */ public class SAKKEKEMExtractor implements EncapsulatedSecretExtractor { @@ -22,65 +38,88 @@ public class SAKKEKEMExtractor private final ECPoint K_bs; private final int n; // Security parameter private final BigInteger identifier; - + private final Digest digest; + + /** + * Initializes the extractor with cryptographic parameters from the receiver's private key. + * + * @param privateKey The receiver's private key containing public parameters + * (curve, prime, generator, etc.) and the Receiver Secret Key (RSK). + * Must not be {@code null}. + */ public SAKKEKEMExtractor(SAKKEPrivateKeyParameters privateKey) { SAKKEPublicKeyParameters publicKey = privateKey.getPublicParams(); this.curve = publicKey.getCurve(); this.q = publicKey.getQ(); - this.P = publicKey.getP(); + this.P = publicKey.getPoint(); this.p = publicKey.getPrime(); this.Z_S = publicKey.getZ(); - this.K_bs = privateKey.getRSK(); - this.n = publicKey.getN(); this.identifier = publicKey.getIdentifier(); + this.K_bs = P.multiply(this.identifier.add(privateKey.getMasterSecret()).modInverse(q)).normalize(); + this.n = publicKey.getN(); + + this.digest = publicKey.getDigest(); } + /** + * Extracts the shared secret value (SSV) from encapsulated data as per RFC 6508. + * + * @param encapsulation The encapsulated data containing: + *
    + *
  • R_(b,S): Elliptic curve point (uncompressed format, 257 bytes)
  • + *
  • H: Integer value (n/8 bytes)
  • + *
+ * @return The extracted SSV as a byte array. + * @throws IllegalStateException If: Validation of R_(b,S) fails + */ @Override public byte[] extractSecret(byte[] encapsulation) { - try - { - // Step 1: Parse Encapsulated Data (R_bS, H) - ECPoint R_bS = curve.decodePoint(Arrays.copyOfRange(encapsulation, 0, 257)); - BigInteger H = new BigInteger(Arrays.copyOfRange(encapsulation, 257, 274)); - - // Step 2: Compute w = using pairing - BigInteger w = computePairing(R_bS, K_bs, p, q); - - // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) - BigInteger twoToN = BigInteger.ONE.shiftLeft(n); - BigInteger mask = SAKKEUtils.hashToIntegerRange(w.toByteArray(), twoToN); - BigInteger ssv = H.xor(mask); - - // Step 4: Compute r = HashToIntegerRange(SSV || b) - BigInteger b = identifier; - BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); - - // Step 5: Validate R_bS - ECPoint bP = P.multiply(b).normalize(); - ECPoint Test = bP.add(Z_S).multiply(r).normalize(); - if (!R_bS.equals(Test)) - { - throw new IllegalStateException("Validation of R_bS failed"); - } - - return BigIntegers.asUnsignedByteArray(n / 8, ssv); - } - catch (Exception e) + // Step 1: Parse Encapsulated Data (R_bS, H) + ECPoint R_bS = curve.decodePoint(Arrays.copyOfRange(encapsulation, 0, 257)); + BigInteger H = BigIntegers.fromUnsignedByteArray(encapsulation, 257, 16); + + // Step 2: Compute w = using pairing + BigInteger w = computePairing(R_bS, K_bs, p, q); + + // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) + BigInteger twoToN = BigInteger.ONE.shiftLeft(n); + BigInteger mask = SAKKEKEMSGenerator.hashToIntegerRange(w.toByteArray(), twoToN, digest); + BigInteger ssv = H.xor(mask).mod(p); + + // Step 4: Compute r = HashToIntegerRange(SSV || b) + BigInteger b = identifier; + BigInteger r = SAKKEKEMSGenerator.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q, digest); + + // Step 5: Validate R_bS + ECPoint bP = P.multiply(b).normalize(); + ECPoint Test = bP.add(Z_S).multiply(r).normalize(); + if (!R_bS.equals(Test)) { - throw new IllegalStateException("SAKKE extraction failed: " + e.getMessage()); + throw new IllegalStateException("Validation of R_bS failed"); } + + return BigIntegers.asUnsignedByteArray(n / 8, ssv); } @Override public int getEncapsulationLength() { - return 0; + return 273; //257 (length of ECPoint) + 16 (length of Hash) } - - public static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) + /** + * Computes the Tate-Lichtenbaum pairing <R, Q> for SAKKE validation. + * Follows the pairing algorithm described in RFC 6508, Section 3.2. + * + * @param R First pairing input (elliptic curve point) + * @param Q Second pairing input (elliptic curve point) + * @param p Prime field characteristic + * @param q Subgroup order + * @return Pairing result in PF_p[q], represented as a field element + */ + static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) { // v = (1,0) in F_p^2 BigInteger[] v = new BigInteger[]{BigInteger.ONE, BigInteger.ZERO}; @@ -133,6 +172,16 @@ public static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigI return v[1].multiply(v[0].modInverse(p)).mod(p); } + /** + * Performs multiplication in F_p^2 field. + * + * @param x_real Real component of first operand + * @param x_imag Imaginary component of first operand + * @param y_real Real component of second operand + * @param y_imag Imaginary component of second operand + * @param p Prime field characteristic + * @return Result of multiplication in F_p^2 as [real, imaginary] array + */ static BigInteger[] fp2Multiply(BigInteger x_real, BigInteger x_imag, BigInteger y_real, BigInteger y_imag, BigInteger p) { return new BigInteger[]{ @@ -141,6 +190,14 @@ static BigInteger[] fp2Multiply(BigInteger x_real, BigInteger x_imag, BigInteger }; } + /** + * Computes squaring operation in F_p^2 field. + * + * @param currentX Real component of input + * @param currentY Imaginary component of input + * @param p Prime field characteristic + * @return Squared result in F_p^2 as [newX, newY] array + */ static BigInteger[] fp2PointSquare(BigInteger currentX, BigInteger currentY, BigInteger p) { BigInteger xPlusY = currentX.add(currentY).mod(p); diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index fe48251968..9bb956d4c1 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto.kems; +import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.EncapsulatedSecretGenerator; import org.bouncycastle.crypto.SecretWithEncapsulation; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; @@ -12,19 +13,59 @@ import java.math.BigInteger; import java.security.SecureRandom; +/** + * This class implements the SAKKE (Sakai-Kasahara Key Encryption) Key Encapsulation Mechanism + * as defined in RFC 6508. It generates an encapsulated shared secret value (SSV) using + * Identity-Based Encryption (IBE) for secure transmission from a Sender to a Receiver. + *

+ * The algorithm follows these steps (as per RFC 6508, Section 6.2.1): + *

    + *
  1. Generate a random SSV in the range [0, 2^n - 1].
  2. + *
  3. Compute r = HashToIntegerRange(SSV || b, q).
  4. + *
  5. Compute R_(b,S) = [r]([b]P + Z_S) on the elliptic curve.
  6. + *
  7. Compute H = SSV XOR HashToIntegerRange(g^r, 2^n).
  8. + *
  9. Encode the encapsulated data (R_(b,S), H).
  10. + *
+ *

+ * + * @see RFC 6508: Sakai-Kasahara Key Encryption (SAKKE) + */ public class SAKKEKEMSGenerator implements EncapsulatedSecretGenerator { private final SecureRandom random; + /** + * Constructs a SAKKEKEMSGenerator with the specified source of randomness. + * + * @param random a {@link SecureRandom} instance for generating cryptographically secure random values. + * Must not be {@code null}. + */ public SAKKEKEMSGenerator(SecureRandom random) { this.random = random; } + /** + * Generates an encapsulated shared secret value (SSV) using the recipient's public key parameters + * as specified in RFC 6508, Section 6.2.1. + *

+ * This method performs the following operations: + *

    + *
  • Derives cryptographic parameters from the recipient's public key.
  • + *
  • Generates a random SSV and computes the encapsulation components (R_(b,S), H).
  • + *
  • Encodes the encapsulated data as specified in RFC 6508, Section 4.
  • + *
+ *

+ * + * @param recipientKey the recipient's public key parameters. Must be an instance of + * {@link SAKKEPublicKeyParameters}. Must not be {@code null}. + * @return a {@link SecretWithEncapsulation} containing the SSV and the encapsulated data. + */ @Override public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey) { + // Extract public parameters from the recipient's key SAKKEPublicKeyParameters keyParameters = (SAKKEPublicKeyParameters)recipientKey; ECPoint Z = keyParameters.getZ(); BigInteger b = keyParameters.getIdentifier(); @@ -33,54 +74,31 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip BigInteger g = keyParameters.getG(); int n = keyParameters.getN(); ECCurve curve = keyParameters.getCurve(); - ECPoint P = keyParameters.getP(); + ECPoint P = keyParameters.getPoint(); + Digest digest = keyParameters.getDigest(); // 1. Generate random SSV in range [0, 2^n - 1] BigInteger ssv = new BigInteger(n, random); - // 2. Compute r = HashToIntegerRange(SSV || b, q) - - BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); + BigInteger r = hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q, digest); // 3. Compute R_(b,S) = [r]([b]P + Z_S) ECPoint bP = P.multiply(b).normalize(); - ECPoint R_bS = bP.add(Z).multiply(r).normalize(); // [r]([b]P + Z_S) + ECPoint R_bS = bP.add(Z).multiply(r).normalize(); // 4. Compute H = SSV XOR HashToIntegerRange( g^r, 2^n ) - BigInteger[] v = fp2Exponentiate(p, BigInteger.ONE, g, r, curve); - BigInteger g_r = v[1].multiply(v[0].modInverse(p)).mod(p); - - BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(n)); // 2^n - - BigInteger H = ssv.xor(mask); - //System.out.println(new String(Hex.encode(H.toByteArray()))); - // 5. Encode encapsulated data (R_bS, H) - byte[] encapsulated = Arrays.concatenate(R_bS.getEncoded(false), H.toByteArray()); - - return new SecretWithEncapsulationImpl( - BigIntegers.asUnsignedByteArray(n / 8, ssv), // Output SSV as key material - encapsulated - ); - } - - - public static BigInteger[] fp2Exponentiate( - BigInteger p, - BigInteger pointX, - BigInteger pointY, - BigInteger n, - ECCurve curve) - { - BigInteger[] result = new BigInteger[2]; + BigInteger pointX = BigInteger.ONE; + BigInteger pointY = g; + BigInteger[] v = new BigInteger[2]; // Initialize result with the original point - BigInteger currentX = pointX; - BigInteger currentY = pointY; + BigInteger currentX = BigInteger.ONE; + BigInteger currentY = g; ECPoint current = curve.createPoint(currentX, currentY); - int numBits = n.bitLength(); + int numBits = r.bitLength(); BigInteger[] rlt; // Process bits from MSB-1 down to 0 for (int i = numBits - 2; i >= 0; i--) @@ -91,7 +109,7 @@ public static BigInteger[] fp2Exponentiate( currentX = rlt[0]; currentY = rlt[1]; // Multiply if bit is set - if (n.testBit(i)) + if (r.testBit(i)) { rlt = SAKKEKEMExtractor.fp2Multiply(currentX, currentY, pointX, pointY, p); @@ -100,8 +118,59 @@ public static BigInteger[] fp2Exponentiate( } } - result[0] = currentX; - result[1] = currentY; - return result; + v[0] = currentX; + v[1] = currentY; + BigInteger g_r = v[1].multiply(v[0].modInverse(p)).mod(p); + + BigInteger mask = hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(n), digest); // 2^n + + BigInteger H = ssv.xor(mask); + // 5. Encode encapsulated data (R_bS, H) +// byte[] encapsulated = Arrays.concatenate(new byte[]{(byte)0x04}, +// BigIntegers.asUnsignedByteArray(n, R_bS.getXCoord().toBigInteger()), +// BigIntegers.asUnsignedByteArray(n, R_bS.getYCoord().toBigInteger()), +// BigIntegers.asUnsignedByteArray(16, H)); + byte[] encapsulated = Arrays.concatenate(R_bS.getEncoded(false), BigIntegers.asUnsignedByteArray(16, H)); + + return new SecretWithEncapsulationImpl( + BigIntegers.asUnsignedByteArray(n / 8, ssv), // Output SSV as key material + encapsulated + ); + } + + static BigInteger hashToIntegerRange(byte[] input, BigInteger q, Digest digest) + { + // RFC 6508 Section 5.1: Hashing to an Integer Range + byte[] hash = new byte[digest.getDigestSize()]; + + // Step 1: Compute A = hashfn(s) + digest.update(input, 0, input.length); + digest.doFinal(hash, 0); + byte[] A = hash.clone(); + + // Step 2: Initialize h_0 to all-zero bytes of hashlen size + byte[] h = new byte[digest.getDigestSize()]; + + // Step 3: Compute l = Ceiling(lg(n)/hashlen) + int l = q.bitLength() >> 8; + + BigInteger v = BigInteger.ZERO; + + // Step 4: Compute h_i and v_i + for (int i = 0; i <= l; i++) + { + // h_i = hashfn(h_{i-1}) + digest.update(h, 0, h.length); + digest.doFinal(h, 0); + // v_i = hashfn(h_i || A) + digest.update(h, 0, h.length); + digest.update(A, 0, A.length); + byte[] v_i = new byte[digest.getDigestSize()]; + digest.doFinal(v_i, 0); + // Append v_i to v' + v = v.shiftLeft(v_i.length * 8).add(new BigInteger(1, v_i)); + } + // Step 6: v = v' mod n + return v.mod(q); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java deleted file mode 100644 index 891f2ae878..0000000000 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.bouncycastle.crypto.kems; - -import java.math.BigInteger; - -import org.bouncycastle.crypto.digests.SHA256Digest; - -public class SAKKEUtils -{ - - public static BigInteger hashToIntegerRange(byte[] input, BigInteger q) - { - // RFC 6508 Section 5.1: Hashing to an Integer Range - SHA256Digest digest = new SHA256Digest(); - byte[] hash = new byte[digest.getDigestSize()]; - - // Step 1: Compute A = hashfn(s) - digest.update(input, 0, input.length); - digest.doFinal(hash, 0); - byte[] A = hash.clone(); - - // Step 2: Initialize h_0 to all-zero bytes of hashlen size - byte[] h = new byte[digest.getDigestSize()]; - - // Step 3: Compute l = Ceiling(lg(n)/hashlen) - int l = q.bitLength() >> 8; - - BigInteger v = BigInteger.ZERO; - - // Step 4: Compute h_i and v_i - for (int i = 0; i <= l; i++) - { - // h_i = hashfn(h_{i-1}) - digest.update(h, 0, h.length); - digest.doFinal(h, 0); - // v_i = hashfn(h_i || A) - digest.update(h, 0, h.length); - digest.update(A, 0, A.length); - byte[] v_i = new byte[digest.getDigestSize()]; - digest.doFinal(v_i, 0); - // Append v_i to v' - v = v.shiftLeft(v_i.length * 8).add(new BigInteger(1, v_i)); - } - // Step 6: v = v' mod n - return v.mod(q); - } -} diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java index c90a373cf0..d5a7539045 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java @@ -1,37 +1,81 @@ package org.bouncycastle.crypto.params; import java.math.BigInteger; +import java.security.SecureRandom; -import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.BigIntegers; +/** + * Represents a private key for the Sakai-Kasahara Key Encryption (SAKKE) scheme, as defined in RFC 6508. + * + *

SAKKE is an identity-based public key encryption scheme designed for one-pass key establishment. + * It is used in MIKEY-SAKKE for secure communication key distribution.

+ * + *

This class generates and manages a SAKKE private key, which consists of a randomly generated + * scalar {@code z}. The corresponding public key is computed as {@code Z = [z]P}, where {@code P} + * is a publicly known generator point on the elliptic curve.

+ * + *

The private key is used to derive the master secret in the key exchange process.

+ * + * @see RFC 6508: Sakai-Kasahara Key Encryption (SAKKE) + */ public class SAKKEPrivateKeyParameters extends AsymmetricKeyParameter { + private static final BigInteger qMinOne = SAKKEPublicKeyParameters.q.subtract(BigInteger.ONE); + /** The associated public key parameters. */ private final SAKKEPublicKeyParameters publicParams; + /** The private key scalar (master secret). */ private final BigInteger z; // KMS Public Key: Z = [z]P - private final ECPoint rsk; - public SAKKEPrivateKeyParameters(BigInteger z, ECPoint rsk, SAKKEPublicKeyParameters publicParams) + /** + * Constructs a SAKKE private key with a given private value and associated public parameters. + * + * @param z The private key scalar. + * @param publicParams The associated public key parameters. + */ + public SAKKEPrivateKeyParameters(BigInteger z, SAKKEPublicKeyParameters publicParams) { super(true); this.z = z; - this.rsk = rsk; this.publicParams = publicParams; } + /** + * Generates a random SAKKE private key and its corresponding public key. + * + *

The private key scalar {@code z} is chosen randomly in the range [2, q-1], + * where {@code q} is the order of the subgroup. The public key is computed as + * {@code Z = [z]P}, where {@code P} is the public generator.

+ * + * @param random A cryptographic random number generator. + */ + public SAKKEPrivateKeyParameters(SecureRandom random) + { + super(true); + this.z = BigIntegers.createRandomInRange(BigIntegers.TWO, qMinOne, random); + BigInteger identifier = BigIntegers.createRandomInRange(BigIntegers.TWO, qMinOne, random); + this.publicParams = new SAKKEPublicKeyParameters(identifier, + SAKKEPublicKeyParameters.P.multiply(z).normalize()); + } + + /** + * Retrieves the public key parameters associated with this private key. + * + * @return The corresponding SAKKE public key parameters. + */ public SAKKEPublicKeyParameters getPublicParams() { return publicParams; } - + /** + * Retrieves the private key scalar (master secret). + * + * @return The private key scalar {@code z}. + */ public BigInteger getMasterSecret() { return z; } - - public ECPoint getRSK() - { - return rsk; - } } diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java index a062d766fc..2380efe143 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java @@ -8,18 +8,50 @@ import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.encoders.Hex; +/** + * Represents the public parameters for the SAKKE (Sakai-Kasahara Key Encryption) scheme + * as defined in RFC 6508. This class encapsulates the cryptographic domain parameters + * and public key components required for SAKKE operations. + *

+ * Contains the following public parameters (RFC 6508, Section 2.3): + *

    + *
  • Prime modulus {@code p} defining the field F_p
  • + *
  • Subgroup order {@code q} (divides p+1)
  • + *
  • Base point {@code P} on the elliptic curve E(F_p)
  • + *
  • Pairing result {@code g = }
  • + *
  • KMS Public Key {@code Z_S = [z_S]P}
  • + *
  • Security parameter {@code n} (SSV bit length)
  • + *
  • User Identifier
  • + *
  • Elliptic curve parameters (a = -3, b = 0)
  • + *
+ *

+ *

+ * The predefined parameters in this implementation correspond to the 128-bit security + * level example from RFC 6509 Appendix A. + *

+ * + * @see RFC 6508: Sakai-Kasahara Key Encryption + * @see RFC 6509: MIKEY-SAKKE + */ public class SAKKEPublicKeyParameters extends AsymmetricKeyParameter { - // Base point - private static final BigInteger p = new BigInteger( + /** + * Prime modulus p defining the finite field F_p (RFC 6508, Section 2.1). + * Value from RFC 6509 Appendix A. + */ + static final BigInteger p = new BigInteger( "997ABB1F0A563FDA65C61198DAD0657A416C0CE19CB48261BE9AE358B3E01A2E" + "F40AAB27E2FC0F1B228730D531A59CB0E791B39FF7C88A19356D27F4A666A6D0" + "E26C6487326B4CD4512AC5CD65681CE1B6AFF4A831852A82A7CF3C521C3C09AA" + "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 ); - private static final BigInteger q = new BigInteger( + /** + * Subgroup order q (divides p+1) (RFC 6508, Section 2.1). + * Value from RFC 6509 Appendix A. + */ + static final BigInteger q = new BigInteger( "265EAEC7C2958FF69971846636B4195E905B0338672D20986FA6B8D62CF8068B" + "BD02AAC9F8BF03C6C8A1CC354C69672C39E46CE7FDF222864D5B49FD2999A9B4" + "389B1921CC9AD335144AB173595A07386DABFD2A0C614AA0A9F3CF14870F026A" + @@ -33,6 +65,7 @@ public class SAKKEPublicKeyParameters "D5CFB4CC80728D87EE9163A5B63F73EC80EC46C4967E0979880DC8ABEAE63895", 16 ); + private static final BigInteger Py = new BigInteger( "0A8249063F6009F1F9F1F0533634A135D3E82016029906963D778D821E141178" + "F5EA69F4654EC2B9E7F7F5E5F0DE55F66B598CCF9A140B2E416CFF0CA9E032B9" + @@ -40,8 +73,10 @@ public class SAKKEPublicKeyParameters "13515AD7E9CB99A980BDAD5AD5BB4636ADB9B5706A67DCDE75573FD71BEF16D7", 16 ); - // g = - // < , > is Tate-Lichtenbaum Pairing + /** + * Pairing result g = computed using the Tate-Lichtenbaum pairing + * (RFC 6508, Section 3.2). Value from RFC 6509 Appendix A. + */ private static final BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + " C6A87BD1 FB94C41E 27FABE65 8E015A87\n" + " 371E9474 4C96FEDA 449AE956 3F8BC446\n" + @@ -51,6 +86,10 @@ public class SAKKEPublicKeyParameters " D682C033 A7942BCC E3720F20 B9B7B040\n" + " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); + /** + * The elliptic curve E: y² = x³ - 3x over F_p (RFC 6508, Section 3.1). + * Uses parameters from RFC 6509 Appendix A. + */ private static final ECCurve.Fp curve = new ECCurve.Fp( p, // Prime p BigInteger.valueOf(-3).mod(p), // a = -3 @@ -59,16 +98,27 @@ public class SAKKEPublicKeyParameters BigInteger.ONE // Cofactor = 1 ); - - private static final ECPoint P = curve.createPoint(Px, Py); + /** + * Base point P on the elliptic curve E(F_p) (RFC 6508, Section 3.1). + * Coordinates from RFC 6509 Appendix A. + */ + static final ECPoint P = curve.createPoint(Px, Py); + /** KMS Public Key Z_S = [z_S]P (RFC 6508, Section 2.2) */ private final ECPoint Z; - + /** User's Identifier (RFC 6508, Section 2.2) */ private final BigInteger identifier; // User's identity - + /** Security parameter: SSV bit length (n = 128 bits) */ private static final int n = 128; // SSV bit length - + /** Hash function (SHA-256) used in SAKKE operations */ private final Digest digest = new SHA256Digest(); - + /** + * Constructs SAKKE public key parameters with the specified identifier and KMS Public Key. + * + * @param identifier The user's identifier as defined in RFC 6508, Section 2.2. + * Must be a valid integer in [2, q-1]. + * @param Z The KMS Public Key Z_S = [z_S]P (RFC 6508, Section 2.2). + * Must be a valid point on the curve E(F_p). + */ public SAKKEPublicKeyParameters(BigInteger identifier, ECPoint Z) { super(false); @@ -76,47 +126,73 @@ public SAKKEPublicKeyParameters(BigInteger identifier, ECPoint Z) this.Z = Z; } - // Getters + /** + * @return The user's identifier (RFC 6508, Section 2.2) + */ public BigInteger getIdentifier() { return identifier; } + /** + * @return The KMS Public Key Z_S = [z_S]P (RFC 6508, Section 2.2) + */ public ECPoint getZ() { return Z; } + /** + * @return The elliptic curve E(F_p) with parameters from RFC 6509 Appendix A + */ public ECCurve getCurve() { return curve; } - public ECPoint getP() + /** + * @return The base point P on E(F_p) (RFC 6508, Section 3.1) + */ + public ECPoint getPoint() { return P; } + /** + * @return Prime modulus p defining the field F_p (RFC 6508, Section 2.1) + */ public BigInteger getPrime() { return p; } + /** + * @return Subgroup order q (divides p+1) (RFC 6508, Section 2.1) + */ public BigInteger getQ() { return q; } + /** + * @return Security parameter n (SSV bit length = 128 bits) + */ public int getN() { return n; } + /** + * @return The hash function (SHA-256) used in SAKKE operations + */ public Digest getDigest() { return digest; } + /** + * @return The pairing result g = (RFC 6508, Section 3.2) + */ public BigInteger getG() { return g; diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index 8dd350c673..492c8d1e4c 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -38,7 +38,15 @@ public String getName() public void performTest() throws Exception { + testTestVector(); + for (int i = 0; i < 100; ++i) + { + testRandom(); + } + } + private void testTestVector() + { final BigInteger Px = new BigInteger( "53FC09EE332C29AD0A7990053ED9B52A2B1A2FD60AEC69C698B2F204B6FF7CBF" + "B5EDB6C0F6CE2308AB10DB9030B09E1043D5F22CDB9DFA55718BD9E7406CE890" + @@ -60,7 +68,7 @@ public void performTest() " 55DF0460 B4A9FD74 B4F1A32B CAFA1FFA" + " D682C033 A7942BCC E3720F20 B9B7B040" + " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); - BigInteger z = new BigInteger(Hex.decode("AFF429D35F84B110D094803B3595A6E2998BC99F")); + BigInteger z = new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F", 16); BigInteger Zx = new BigInteger(Hex.decode("5958EF1B1679BF099B3A030DF255AA6A23C1D8F143D4D23F753E69BD27A832F38CB4AD53DDEF" + "4260B0FE8BB45C4C1FF510EFFE300367A37B61F701D914AEF09724825FA0707D61A6DFF4FBD7273566CDDE352A0B04B7C16A78309BE" + "640697DE747613A5FC195E8B9F328852A579DB8F99B1D0034479EA9C5595F47C4B2F54FF2")); @@ -100,30 +108,30 @@ public void performTest() "2884318A33D1A42ADF5E33CC5800280B" + "28356497F87135BAB9612A1726042440" + "9AC15FEE996B744C332151235DECB0F5", 16); - BigInteger w = new BigInteger(Hex.decode("7D2A8438 E6291C64 9B6579EB 3B79EAE9" + - "48B1DE9E 5F7D1F40 70A08F8D B6B3C515" + - "6F2201AF FBB5CB9D 82AA3EC0 D0398B89" + - "ABC78A13 A760C0BF 3F77E63D 0DF3F1A3" + - "41A41B88 11DF197F D6CD0F00 3125606F" + - "4F109F40 0F7292A1 0D255E3C 0EBCCB42" + - "53FB182C 68F09CF6 CD9C4A53 DA6C74AD" + - "007AF36B 8BCA979D 5895E282 F483FCD6")); - BigInteger Rbx = new BigInteger(Hex.decode("44E8AD44 AB8592A6 A5A3DDCA 5CF896C7" + - "18043606 A01D650D EF37A01F 37C228C3" + - "32FC3173 54E2C274 D4DAF8AD 001054C7" + - "6CE57971 C6F4486D 57230432 61C506EB" + - "F5BE438F 53DE04F0 67C776E0 DD3B71A6" + - "29013328 3725A532 F21AF145 126DC1D7" + - "77ECC27B E50835BD 28098B8A 73D9F801" + - "D893793A 41FF5C49 B87E79F2 BE4D56CE")); - BigInteger Rby = new BigInteger(Hex.decode("557E134A D85BB1D4 B9CE4F8B E4B08A12" + - "BABF55B1 D6F1D7A6 38019EA2 8E15AB1C" + - "9F76375F DD1210D4 F4351B9A 009486B7" + - "F3ED46C9 65DED2D8 0DADE4F3 8C6721D5" + - "2C3AD103 A10EBD29 59248B4E F006836B" + - "F097448E 6107C9ED EE9FB704 823DF199" + - "F832C905 AE45F8A2 47A072D8 EF729EAB" + - "C5E27574 B07739B3 4BE74A53 2F747B86")); +// BigInteger w = new BigInteger(Hex.decode("7D2A8438 E6291C64 9B6579EB 3B79EAE9" + +// "48B1DE9E 5F7D1F40 70A08F8D B6B3C515" + +// "6F2201AF FBB5CB9D 82AA3EC0 D0398B89" + +// "ABC78A13 A760C0BF 3F77E63D 0DF3F1A3" + +// "41A41B88 11DF197F D6CD0F00 3125606F" + +// "4F109F40 0F7292A1 0D255E3C 0EBCCB42" + +// "53FB182C 68F09CF6 CD9C4A53 DA6C74AD" + +// "007AF36B 8BCA979D 5895E282 F483FCD6")); +// BigInteger Rbx = new BigInteger(Hex.decode("44E8AD44 AB8592A6 A5A3DDCA 5CF896C7" + +// "18043606 A01D650D EF37A01F 37C228C3" + +// "32FC3173 54E2C274 D4DAF8AD 001054C7" + +// "6CE57971 C6F4486D 57230432 61C506EB" + +// "F5BE438F 53DE04F0 67C776E0 DD3B71A6" + +// "29013328 3725A532 F21AF145 126DC1D7" + +// "77ECC27B E50835BD 28098B8A 73D9F801" + +// "D893793A 41FF5C49 B87E79F2 BE4D56CE")); +// BigInteger Rby = new BigInteger(Hex.decode("557E134A D85BB1D4 B9CE4F8B E4B08A12" + +// "BABF55B1 D6F1D7A6 38019EA2 8E15AB1C" + +// "9F76375F DD1210D4 F4351B9A 009486B7" + +// "F3ED46C9 65DED2D8 0DADE4F3 8C6721D5" + +// "2C3AD103 A10EBD29 59248B4E F006836B" + +// "F097448E 6107C9ED EE9FB704 823DF199" + +// "F832C905 AE45F8A2 47A072D8 EF729EAB" + +// "C5E27574 B07739B3 4BE74A53 2F747B86")); BigInteger p = new BigInteger( "997ABB1F0A563FDA65C61198DAD0657A416C0CE19CB48261BE9AE358B3E01A2E" + "F40AAB27E2FC0F1B228730D531A59CB0E791B39FF7C88A19356D27F4A666A6D0" + @@ -137,21 +145,32 @@ public void performTest() g,// Order of the subgroup (from RFC 6509) BigInteger.ONE // Cofactor = 1 ); + ECPoint P = curve.createPoint(Px, Py); - SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(ssv), - new FixedSecureRandom.Data(b)}); - SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(random); - SecretWithEncapsulation rlt = generator.generateEncapsulated(new SAKKEPublicKeyParameters(new BigInteger(b), curve.createPoint(Zx, Zy))); + ECPoint computed_Z = P.multiply(z).normalize(); + Assert.assertTrue(computed_Z.equals(curve.createPoint(Zx, Zy))); - ECPoint P = curve.createPoint(Px, Py); + SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(ssv)}); + SAKKEPublicKeyParameters b_publicKey = new SAKKEPublicKeyParameters(new BigInteger(b), curve.createPoint(Zx, Zy)); + SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(random); + SecretWithEncapsulation rlt = generator.generateEncapsulated(b_publicKey); - BigInteger computed_g2 = SAKKEKEMExtractor.computePairing(P, P, p, q); - Assert.assertTrue(computed_g2.equals(g)); - ECPoint K_bS = curve.createPoint(kbx, kby); + SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(z, b_publicKey)); + byte[] test = extractor.extractSecret(rlt.getEncapsulation()); + Assert.assertTrue(Arrays.areEqual(test, ssv)); + } - SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(z, K_bS, - new SAKKEPublicKeyParameters(new BigInteger(b), curve.createPoint(Zx, Zy)))); + private void testRandom() + { + SecureRandom random = new SecureRandom(); + byte[] ssv = new byte[16]; + random.nextBytes(ssv); + SAKKEPrivateKeyParameters b_priv = new SAKKEPrivateKeyParameters(random); + SAKKEPublicKeyParameters b_pub = b_priv.getPublicParams(); + SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(ssv)})); + SecretWithEncapsulation rlt = generator.generateEncapsulated(b_pub); + SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(b_priv); byte[] test = extractor.extractSecret(rlt.getEncapsulation()); Assert.assertTrue(Arrays.areEqual(test, ssv)); } From baffe645e685fa7ad813f559b3037cc4a2c98a18 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 7 Feb 2025 14:42:34 +1030 Subject: [PATCH 100/890] Add key pair check in SAKKEPrivateKeyParameters. --- .../crypto/params/SAKKEPrivateKeyParameters.java | 16 +++++++++++++--- .../crypto/kems/test/SAKKEKEMSTest.java | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java index d5a7539045..9e3be92c3f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java @@ -3,6 +3,7 @@ import java.math.BigInteger; import java.security.SecureRandom; +import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.BigIntegers; /** @@ -23,15 +24,19 @@ public class SAKKEPrivateKeyParameters extends AsymmetricKeyParameter { private static final BigInteger qMinOne = SAKKEPublicKeyParameters.q.subtract(BigInteger.ONE); - /** The associated public key parameters. */ + /** + * The associated public key parameters. + */ private final SAKKEPublicKeyParameters publicParams; - /** The private key scalar (master secret). */ + /** + * The private key scalar (master secret). + */ private final BigInteger z; // KMS Public Key: Z = [z]P /** * Constructs a SAKKE private key with a given private value and associated public parameters. * - * @param z The private key scalar. + * @param z The private key scalar. * @param publicParams The associated public key parameters. */ public SAKKEPrivateKeyParameters(BigInteger z, SAKKEPublicKeyParameters publicParams) @@ -39,6 +44,11 @@ public SAKKEPrivateKeyParameters(BigInteger z, SAKKEPublicKeyParameters publicPa super(true); this.z = z; this.publicParams = publicParams; + ECPoint computed_Z = publicParams.getPoint().multiply(z).normalize(); + if (!computed_Z.equals(publicParams.getZ())) + { + throw new IllegalStateException("public key and private key of SAKKE do not match"); + } } /** diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index 492c8d1e4c..0ef2797037 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -39,7 +39,7 @@ public void performTest() throws Exception { testTestVector(); - for (int i = 0; i < 100; ++i) + for (int i = 0; i < 1; ++i) { testRandom(); } From 09578870df67962ed0cdc7a3d16e5eef6e60d306 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 7 Feb 2025 17:03:32 +1030 Subject: [PATCH 101/890] Test the code of Jasypt --- .../symmetric/util/BaseBlockCipher.java | 46 +- .../jasypt/AlreadyInitializedException.java | 42 + .../jcajce/provider/test/jasypt/Base64.java | 585 ++++++++ .../provider/test/jasypt/ByteEncryptor.java | 53 + .../test/jasypt/CleanablePasswordBased.java | 48 + .../provider/test/jasypt/CommonUtils.java | 298 ++++ .../test/jasypt/DecoderException.java | 19 + .../test/jasypt/EncoderException.java | 22 + .../EncryptionInitializationException.java | 52 + ...cryptionOperationNotPossibleException.java | 56 + .../test/jasypt/FixedSaltGenerator.java | 43 + .../provider/test/jasypt/IvGenerator.java | 73 + .../provider/test/jasypt/NoIvGenerator.java | 72 + .../provider/test/jasypt/Normalizer.java | 230 ++++ .../PBEByteCleanablePasswordEncryptor.java | 44 + .../test/jasypt/PBEByteEncryptor.java | 42 + .../jasypt/PBECleanablePasswordConfig.java | 70 + .../provider/test/jasypt/PBEConfig.java | 208 +++ .../PBEStringCleanablePasswordEncryptor.java | 43 + .../test/jasypt/PBEStringEncryptor.java | 42 + .../provider/test/jasypt/PasswordBased.java | 44 + .../test/jasypt/RandomIvGenerator.java | 105 ++ .../test/jasypt/RandomSaltGenerator.java | 109 ++ .../provider/test/jasypt/SaltGenerator.java | 73 + .../test/jasypt/StandardPBEByteEncryptor.java | 1209 +++++++++++++++++ .../jasypt/StandardPBEStringEncryptor.java | 750 ++++++++++ .../provider/test/jasypt/StringEncryptor.java | 55 + .../provider/test/jasypt/StringPBEConfig.java | 71 + .../provider/test/jasypt/TestJasypt.java | 18 + 29 files changed, 4499 insertions(+), 23 deletions(-) create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/AlreadyInitializedException.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Base64.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/ByteEncryptor.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CleanablePasswordBased.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CommonUtils.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/DecoderException.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncoderException.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionInitializationException.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionOperationNotPossibleException.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/FixedSaltGenerator.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/IvGenerator.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/NoIvGenerator.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Normalizer.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteCleanablePasswordEncryptor.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteEncryptor.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBECleanablePasswordConfig.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEConfig.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringCleanablePasswordEncryptor.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringEncryptor.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PasswordBased.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomIvGenerator.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomSaltGenerator.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/SaltGenerator.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEByteEncryptor.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEStringEncryptor.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringEncryptor.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringPBEConfig.java create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/TestJasypt.java diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java index 207bd9c272..c8ef9f740f 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java @@ -634,11 +634,11 @@ else if (paddingName.equals("TBCPADDING")) protected void engineInit( int opmode, Key key, - final AlgorithmParameterSpec paramSpec, + AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - CipherParameters param; + CipherParameters param = null; this.pbeSpec = null; this.pbeAlgorithm = null; @@ -656,7 +656,7 @@ protected void engineInit( // // for RC5-64 we must have some default parameters // - if (paramSpec == null && (baseEngine != null && baseEngine.getAlgorithmName().startsWith("RC5-64"))) + if (params == null && (baseEngine != null && baseEngine.getAlgorithmName().startsWith("RC5-64"))) { throw new InvalidAlgorithmParameterException("RC5 requires an RC5ParametersSpec to be passed in."); } @@ -676,9 +676,9 @@ protected void engineInit( throw new InvalidKeyException("PKCS12 requires a SecretKey/PBEKey"); } - if (paramSpec instanceof PBEParameterSpec) + if (params instanceof PBEParameterSpec) { - pbeSpec = (PBEParameterSpec)paramSpec; + pbeSpec = (PBEParameterSpec)params; } if (k instanceof PBEKey && pbeSpec == null) @@ -727,9 +727,9 @@ else if (key instanceof PBKDF1Key) { PBKDF1Key k = (PBKDF1Key)key; - if (paramSpec instanceof PBEParameterSpec) + if (params instanceof PBEParameterSpec) { - pbeSpec = (PBEParameterSpec)paramSpec; + pbeSpec = (PBEParameterSpec)params; } if (k instanceof PBKDF1KeyWithParameters && pbeSpec == null) { @@ -746,9 +746,9 @@ else if (key instanceof PBKDF2Key) { PBKDF2Key k = (PBKDF2Key)key; - if (paramSpec instanceof PBEParameterSpec) + if (param instanceof PBEParameterSpec) { - pbeSpec = (PBEParameterSpec)paramSpec; + pbeSpec = (PBEParameterSpec)param; } if (k instanceof PBKDF2KeyWithParameters && pbeSpec == null) { @@ -776,12 +776,12 @@ else if (key instanceof BCPBEKey) if (k.getParam() != null) { - param = adjustParameters(paramSpec, k.getParam()); + param = adjustParameters(params, k.getParam()); } - else if (paramSpec instanceof PBEParameterSpec) + else if (params instanceof PBEParameterSpec) { - pbeSpec = (PBEParameterSpec)paramSpec; - param = PBE.Util.makePBEParameters(k, paramSpec, cipher.getUnderlyingCipher().getAlgorithmName()); + pbeSpec = (PBEParameterSpec)params; + param = PBE.Util.makePBEParameters(k, params, cipher.getUnderlyingCipher().getAlgorithmName()); } else { @@ -796,7 +796,7 @@ else if (paramSpec instanceof PBEParameterSpec) else if (key instanceof PBEKey) { PBEKey k = (PBEKey)key; - pbeSpec = (PBEParameterSpec)paramSpec; + pbeSpec = (PBEParameterSpec)params; if (k instanceof PKCS12KeyWithParameters && pbeSpec == null) { pbeSpec = new PBEParameterSpec(k.getSalt(), k.getIterationCount()); @@ -821,15 +821,15 @@ else if (!(key instanceof RepeatedSecretKeySpec)) param = null; } - AlgorithmParameterSpec params; - if (paramSpec instanceof PBEParameterSpec) - { - params = ((PBEParameterSpec)paramSpec).getParameterSpec(); - } - else - { - params = paramSpec; - } +// AlgorithmParameterSpec params; +// if (params instanceof PBEParameterSpec) +// { +// params = ((PBEParameterSpec)params).getParameterSpec(); +// } +// else +// { +// params = params; +// } if (params instanceof AEADParameterSpec) { diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/AlreadyInitializedException.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/AlreadyInitializedException.java new file mode 100644 index 0000000000..8e62105045 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/AlreadyInitializedException.java @@ -0,0 +1,42 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + + +/** + * Exception thrown when an attempt is made to change the configuration + * of an entity once it has been initialized. + * + * + * @since 1.0 + * + * @author Daniel Fernández + * + */ +public final class AlreadyInitializedException + extends RuntimeException { + + private static final long serialVersionUID = 4592515503937873874L; + + public AlreadyInitializedException() { + super("Encryption entity already initialized"); + } + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Base64.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Base64.java new file mode 100644 index 0000000000..9516551656 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Base64.java @@ -0,0 +1,585 @@ +package org.bouncycastle.jcajce.provider.test.jasypt; + +/* + * Copyright 2001-2004 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * IMPORTANT NOTE: This class has been included into Jasypt's source tree from + * Apache Commons-Codec version 1.3 [see http://commons.apache.org/codec], + * licensed under Apache License 2.0 [see http://www.apache.org/licenses/LICENSE-2.0]. + * No modifications have been made to the code of this class except the package name. + */ + + + +/** + * Provides Base64 encoding and decoding as defined by RFC 2045. + * + *

This class implements section 6.8. Base64 Content-Transfer-Encoding + * from RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: + * Format of Internet Message Bodies by Freed and Borenstein.

+ * + * @author Apache Software Foundation + * @see RFC 2045 + * @since 1.0-dev + */ +public class Base64 +{ + + /** + * Chunk size per RFC 2045 section 6.8. + * + *

The {@value} character limit does not count the trailing CRLF, but counts + * all other characters, including any equal signs.

+ * + * @see RFC 2045 section 6.8 + */ + static final int CHUNK_SIZE = 76; + + /** + * Chunk separator per RFC 2045 section 2.1. + * + * @see RFC 2045 section 2.1 + */ + static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes(); + + /** + * The base length. + */ + static final int BASELENGTH = 255; + + /** + * Lookup length. + */ + static final int LOOKUPLENGTH = 64; + + /** + * Used to calculate the number of bits in a byte. + */ + static final int EIGHTBIT = 8; + + /** + * Used when encoding something which has fewer than 24 bits. + */ + static final int SIXTEENBIT = 16; + + /** + * Used to determine how many bits data contains. + */ + static final int TWENTYFOURBITGROUP = 24; + + /** + * Used to get the number of Quadruples. + */ + static final int FOURBYTE = 4; + + /** + * Used to test the sign of a byte. + */ + static final int SIGN = -128; + + /** + * Byte used to pad output. + */ + static final byte PAD = (byte)'='; + + // Create arrays to hold the base64 characters and a + // lookup for base64 chars + private static byte[] base64Alphabet = new byte[BASELENGTH]; + private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH]; + + // Populating the lookup and character arrays + static + { + for (int i = 0; i < BASELENGTH; i++) + { + base64Alphabet[i] = (byte)-1; + } + for (int i = 'Z'; i >= 'A'; i--) + { + base64Alphabet[i] = (byte)(i - 'A'); + } + for (int i = 'z'; i >= 'a'; i--) + { + base64Alphabet[i] = (byte)(i - 'a' + 26); + } + for (int i = '9'; i >= '0'; i--) + { + base64Alphabet[i] = (byte)(i - '0' + 52); + } + + base64Alphabet['+'] = 62; + base64Alphabet['/'] = 63; + + for (int i = 0; i <= 25; i++) + { + lookUpBase64Alphabet[i] = (byte)('A' + i); + } + + for (int i = 26, j = 0; i <= 51; i++, j++) + { + lookUpBase64Alphabet[i] = (byte)('a' + j); + } + + for (int i = 52, j = 0; i <= 61; i++, j++) + { + lookUpBase64Alphabet[i] = (byte)('0' + j); + } + + lookUpBase64Alphabet[62] = (byte)'+'; + lookUpBase64Alphabet[63] = (byte)'/'; + } + + private static boolean isBase64(byte octect) + { + if (octect == PAD) + { + return true; + } + else if (base64Alphabet[octect] == -1) + { + return false; + } + else + { + return true; + } + } + + /** + * Tests a given byte array to see if it contains + * only valid characters within the Base64 alphabet. + * + * @param arrayOctect byte array to test + * @return true if all bytes are valid characters in the Base64 + * alphabet or if the byte array is empty; false, otherwise + */ + public static boolean isArrayByteBase64(byte[] arrayOctect) + { + + arrayOctect = discardWhitespace(arrayOctect); + + int length = arrayOctect.length; + if (length == 0) + { + // shouldn't a 0 length array be valid base64 data? + // return false; + return true; + } + for (int i = 0; i < length; i++) + { + if (!isBase64(arrayOctect[i])) + { + return false; + } + } + return true; + } + + /** + * Encodes binary data using the base64 algorithm but + * does not chunk the output. + * + * @param binaryData binary data to encode + * @return Base64 characters + */ + public static byte[] encodeBase64(byte[] binaryData) + { + return encodeBase64(binaryData, false); + } + + /** + * Encodes binary data using the base64 algorithm and chunks + * the encoded output into 76 character blocks + * + * @param binaryData binary data to encode + * @return Base64 characters chunked in 76 character blocks + */ + public static byte[] encodeBase64Chunked(byte[] binaryData) + { + return encodeBase64(binaryData, true); + } + + + /** + * Decodes an Object using the base64 algorithm. This method + * is provided in order to satisfy the requirements of the + * Decoder interface, and will throw a DecoderException if the + * supplied object is not of type byte[]. + * + * @param pObject Object to decode + * @return An object (of type byte[]) containing the + * binary data which corresponds to the byte[] supplied. + * @throws DecoderException if the parameter supplied is not + * of type byte[] + */ + public Object decode(Object pObject) + throws DecoderException + { + if (!(pObject instanceof byte[])) + { + throw new DecoderException("Parameter supplied to Base64 decode is not a byte[]"); + } + return decode((byte[])pObject); + } + + /** + * Decodes a byte[] containing containing + * characters in the Base64 alphabet. + * + * @param pArray A byte array containing Base64 character data + * @return a byte array containing binary data + */ + public byte[] decode(byte[] pArray) + { + return decodeBase64(pArray); + } + + /** + * Encodes binary data using the base64 algorithm, optionally + * chunking the output into 76 character blocks. + * + * @param binaryData Array containing binary data to encode. + * @param isChunked if isChunked is true this encoder will chunk + * the base64 output into 76 character blocks + * @return Base64-encoded data. + */ + public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) + { + int lengthDataBits = binaryData.length * EIGHTBIT; + int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; + int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; + byte encodedData[] = null; + int encodedDataLength = 0; + int nbrChunks = 0; + + if (fewerThan24bits != 0) + { + //data not divisible by 24 bit + encodedDataLength = (numberTriplets + 1) * 4; + } + else + { + // 16 or 8 bit + encodedDataLength = numberTriplets * 4; + } + + // If the output is to be "chunked" into 76 character sections, + // for compliance with RFC 2045 MIME, then it is important to + // allow for extra length to account for the separator(s) + if (isChunked) + { + + nbrChunks = + (CHUNK_SEPARATOR.length == 0 ? 0 : (int)Math.ceil((float)encodedDataLength / CHUNK_SIZE)); + encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length; + } + + encodedData = new byte[encodedDataLength]; + + byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; + + int encodedIndex = 0; + int dataIndex = 0; + int i = 0; + int nextSeparatorIndex = CHUNK_SIZE; + int chunksSoFar = 0; + + //log.debug("number of triplets = " + numberTriplets); + for (i = 0; i < numberTriplets; i++) + { + dataIndex = i * 3; + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + b3 = binaryData[dataIndex + 2]; + + //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3); + + l = (byte)(b2 & 0x0f); + k = (byte)(b1 & 0x03); + + byte val1 = + ((b1 & SIGN) == 0) ? (byte)(b1 >> 2) : (byte)((b1) >> 2 ^ 0xc0); + byte val2 = + ((b2 & SIGN) == 0) ? (byte)(b2 >> 4) : (byte)((b2) >> 4 ^ 0xf0); + byte val3 = + ((b3 & SIGN) == 0) ? (byte)(b3 >> 6) : (byte)((b3) >> 6 ^ 0xfc); + + encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; + //log.debug( "val2 = " + val2 ); + //log.debug( "k4 = " + (k<<4) ); + //log.debug( "vak = " + (val2 | (k<<4)) ); + encodedData[encodedIndex + 1] = + lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex + 2] = + lookUpBase64Alphabet[(l << 2) | val3]; + encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f]; + + encodedIndex += 4; + + // If we are chunking, let's put a chunk separator down. + if (isChunked) + { + // this assumes that CHUNK_SIZE % 4 == 0 + if (encodedIndex == nextSeparatorIndex) + { + System.arraycopy( + CHUNK_SEPARATOR, + 0, + encodedData, + encodedIndex, + CHUNK_SEPARATOR.length); + chunksSoFar++; + nextSeparatorIndex = + (CHUNK_SIZE * (chunksSoFar + 1)) + + (chunksSoFar * CHUNK_SEPARATOR.length); + encodedIndex += CHUNK_SEPARATOR.length; + } + } + } + + // form integral number of 6-bit groups + dataIndex = i * 3; + + if (fewerThan24bits == EIGHTBIT) + { + b1 = binaryData[dataIndex]; + k = (byte)(b1 & 0x03); + //log.debug("b1=" + b1); + //log.debug("b1<<2 = " + (b1>>2) ); + byte val1 = + ((b1 & SIGN) == 0) ? (byte)(b1 >> 2) : (byte)((b1) >> 2 ^ 0xc0); + encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4]; + encodedData[encodedIndex + 2] = PAD; + encodedData[encodedIndex + 3] = PAD; + } + else if (fewerThan24bits == SIXTEENBIT) + { + + b1 = binaryData[dataIndex]; + b2 = binaryData[dataIndex + 1]; + l = (byte)(b2 & 0x0f); + k = (byte)(b1 & 0x03); + + byte val1 = + ((b1 & SIGN) == 0) ? (byte)(b1 >> 2) : (byte)((b1) >> 2 ^ 0xc0); + byte val2 = + ((b2 & SIGN) == 0) ? (byte)(b2 >> 4) : (byte)((b2) >> 4 ^ 0xf0); + + encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; + encodedData[encodedIndex + 1] = + lookUpBase64Alphabet[val2 | (k << 4)]; + encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2]; + encodedData[encodedIndex + 3] = PAD; + } + + if (isChunked) + { + // we also add a separator to the end of the final chunk. + if (chunksSoFar < nbrChunks) + { + System.arraycopy( + CHUNK_SEPARATOR, + 0, + encodedData, + encodedDataLength - CHUNK_SEPARATOR.length, + CHUNK_SEPARATOR.length); + } + } + + return encodedData; + } + + /** + * Decodes Base64 data into octects + * + * @param base64Data Byte array containing Base64 data + * @return Array containing decoded data. + */ + public static byte[] decodeBase64(byte[] base64Data) + { + // RFC 2045 requires that we discard ALL non-Base64 characters + base64Data = discardNonBase64(base64Data); + + // handle the edge case, so we don't have to worry about it later + if (base64Data.length == 0) + { + return new byte[0]; + } + + int numberQuadruple = base64Data.length / FOURBYTE; + byte decodedData[] = null; + byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0; + + // Throw away anything not in base64Data + + int encodedIndex = 0; + int dataIndex = 0; + { + // this sizes the output array properly - rlw + int lastData = base64Data.length; + // ignore the '=' padding + while (base64Data[lastData - 1] == PAD) + { + if (--lastData == 0) + { + return new byte[0]; + } + } + decodedData = new byte[lastData - numberQuadruple]; + } + + for (int i = 0; i < numberQuadruple; i++) + { + dataIndex = i * 4; + marker0 = base64Data[dataIndex + 2]; + marker1 = base64Data[dataIndex + 3]; + + b1 = base64Alphabet[base64Data[dataIndex]]; + b2 = base64Alphabet[base64Data[dataIndex + 1]]; + + if (marker0 != PAD && marker1 != PAD) + { + //No PAD e.g 3cQl + b3 = base64Alphabet[marker0]; + b4 = base64Alphabet[marker1]; + + decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4); + decodedData[encodedIndex + 1] = + (byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + decodedData[encodedIndex + 2] = (byte)(b3 << 6 | b4); + } + else if (marker0 == PAD) + { + //Two PAD e.g. 3c[Pad][Pad] + decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4); + } + else if (marker1 == PAD) + { + //One PAD e.g. 3cQ[Pad] + b3 = base64Alphabet[marker0]; + + decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4); + decodedData[encodedIndex + 1] = + (byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); + } + encodedIndex += 3; + } + return decodedData; + } + + /** + * Discards any whitespace from a base-64 encoded block. + * + * @param data The base-64 encoded data to discard the whitespace + * from. + * @return The data, less whitespace (see RFC 2045). + */ + static byte[] discardWhitespace(byte[] data) + { + byte groomedData[] = new byte[data.length]; + int bytesCopied = 0; + + for (int i = 0; i < data.length; i++) + { + switch (data[i]) + { + case (byte)' ': + case (byte)'\n': + case (byte)'\r': + case (byte)'\t': + break; + default: + groomedData[bytesCopied++] = data[i]; + } + } + + byte packedData[] = new byte[bytesCopied]; + + System.arraycopy(groomedData, 0, packedData, 0, bytesCopied); + + return packedData; + } + + /** + * Discards any characters outside of the base64 alphabet, per + * the requirements on page 25 of RFC 2045 - "Any characters + * outside of the base64 alphabet are to be ignored in base64 + * encoded data." + * + * @param data The base-64 encoded data to groom + * @return The data, less non-base64 characters (see RFC 2045). + */ + static byte[] discardNonBase64(byte[] data) + { + byte groomedData[] = new byte[data.length]; + int bytesCopied = 0; + + for (int i = 0; i < data.length; i++) + { + if (isBase64(data[i])) + { + groomedData[bytesCopied++] = data[i]; + } + } + + byte packedData[] = new byte[bytesCopied]; + + System.arraycopy(groomedData, 0, packedData, 0, bytesCopied); + + return packedData; + } + + + // Implementation of the Encoder Interface + + /** + * Encodes an Object using the base64 algorithm. This method + * is provided in order to satisfy the requirements of the + * Encoder interface, and will throw an EncoderException if the + * supplied object is not of type byte[]. + * + * @param pObject Object to encode + * @return An object (of type byte[]) containing the + * base64 encoded data which corresponds to the byte[] supplied. + * @throws EncoderException if the parameter supplied is not + * of type byte[] + */ + public Object encode(Object pObject) + throws EncoderException + { + if (!(pObject instanceof byte[])) + { + throw new EncoderException( + "Parameter supplied to Base64 encode is not a byte[]"); + } + return encode((byte[])pObject); + } + + /** + * Encodes a byte[] containing binary data, into a byte[] containing + * characters in the Base64 alphabet. + * + * @param pArray a byte array containing binary data + * @return A byte array containing only Base64 character data + */ + public byte[] encode(byte[] pArray) + { + return encodeBase64(pArray, false); + } + +} + diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/ByteEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/ByteEncryptor.java new file mode 100644 index 0000000000..4ba03b35f6 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/ByteEncryptor.java @@ -0,0 +1,53 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + +/** + *

+ * Common interface for all Encryptors which receive a + * byte array message and return a byte array result. + *

+ * + * @since 1.0 + * + * @author Daniel Fernández + * + */ +public interface ByteEncryptor +{ + + + /** + * Encrypt the input message + * + * @param message the message to be encrypted + * @return the result of encryption + */ + public byte[] encrypt(byte[] message); + + /** + * Decrypt an encrypted message + * + * @param encryptedMessage the encrypted message to be decrypted + * @return the result of decryption + */ + public byte[] decrypt(byte[] encryptedMessage); + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CleanablePasswordBased.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CleanablePasswordBased.java new file mode 100644 index 0000000000..fb9782905d --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CleanablePasswordBased.java @@ -0,0 +1,48 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + +/** + *

+ * Common interface for all entities which can be set a password in char[] shape, + * which can be cleaned once the encryptor is initialized so that no immutable + * Strings containing the password are left in memory. + *

+ * + * @since 1.8 + * + * @author Daniel Fernández + * + */ +public interface CleanablePasswordBased + extends PasswordBased { + + /** + *

+ * Sets a password to be used by the encryptor, as a (cleanable) char[]. + *

+ * + * @since 1.8 + * + * @param password the password to be used. + */ + public void setPasswordCharArray(char[] password); + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CommonUtils.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CommonUtils.java new file mode 100644 index 0000000000..53a6bdb8d2 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CommonUtils.java @@ -0,0 +1,298 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + *

+ * Common utils regarding treatment of parameter values and encoding operations. + * This class is for internal use only. + *

+ * + * @since 1.3 + * + * @author Daniel Fernández + * + */ +public final class CommonUtils +{ + + public static final String STRING_OUTPUT_TYPE_BASE64 = "base64"; + public static final String STRING_OUTPUT_TYPE_HEXADECIMAL = "hexadecimal"; + + private static final List STRING_OUTPUT_TYPE_HEXADECIMAL_NAMES = + Arrays.asList( + new String[] { + "HEXADECIMAL", "HEXA", "0X", "HEX", "HEXADEC" + } + ); + + private static char[] hexDigits = + {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; + + + + + public static Boolean getStandardBooleanValue(final String valueStr) { + if (valueStr == null) { + return null; + } + final String upperValue = valueStr.toUpperCase(); + if ("TRUE".equals(upperValue) || "ON".equals(upperValue) || "YES".equals(upperValue)) { + return Boolean.TRUE; + } + if ("FALSE".equals(upperValue) || "OFF".equals(upperValue) || "NO".equals(upperValue)) { + return Boolean.FALSE; + } + return null; + } + + + public static String getStandardStringOutputType(final String valueStr) { + if (valueStr == null) { + return null; + } + if (STRING_OUTPUT_TYPE_HEXADECIMAL_NAMES.contains(valueStr.toUpperCase())) { + return STRING_OUTPUT_TYPE_HEXADECIMAL; + } + return STRING_OUTPUT_TYPE_BASE64; + } + + + public static String toHexadecimal(final byte[] message) { + if (message == null) { + return null; + } + final StringBuffer buffer = new StringBuffer(); + for (int i = 0; i < message.length; i++) { + int curByte = message[i] & 0xff; + buffer.append(hexDigits[(curByte >> 4)]); + buffer.append(hexDigits[curByte & 0xf]); + } + return buffer.toString(); + } + + + public static byte[] fromHexadecimal(final String message) { + if (message == null) { + return null; + } + if ((message.length() % 2) != 0) { + throw new EncryptionOperationNotPossibleException(); + } + try { + final byte[] result = new byte[message.length() / 2]; + for (int i = 0; i < message.length(); i = i + 2) { + final int first = Integer.parseInt("" + message.charAt(i), 16); + final int second = Integer.parseInt("" + message.charAt(i + 1), 16); + result[i/2] = (byte) (0x0 + ((first & 0xff) << 4) + (second & 0xff)); + } + return result; + } catch (Exception e) { + throw new EncryptionOperationNotPossibleException(); + } + } + + + public static boolean isEmpty(final String string) { + if (string == null || string.length() == 0) { + return true; + } + return false; + } + + + public static boolean isNotEmpty(final String string) { + if (string == null || string.length() == 0) { + return false; + } + return true; + } + + + public static void validateNotNull(final Object object, final String message) { + if (object == null) { + throw new IllegalArgumentException(message); + } + } + + + public static void validateNotEmpty(final String string, final String message) { + if (isEmpty(string)) { + throw new IllegalArgumentException(message); + } + } + + + public static void validateIsTrue(final boolean expression, final String message) { + if (expression == false) { + throw new IllegalArgumentException(message); + } + } + + + + + public static String[] split(final String string) { + // Whitespace will be used as separator + return split(string, null); + } + + + public static String[] split(final String string, final String separators) { + + if (string == null) { + return null; + } + + final int length = string.length(); + + if (length == 0) { + return new String[0]; + } + + final List results = new ArrayList(); + int i = 0; + int start = 0; + boolean tokenInProgress = false; + + if (separators == null) { + + while (i < length) { + if (Character.isWhitespace(string.charAt(i))) { + if (tokenInProgress) { + results.add(string.substring(start, i)); + tokenInProgress = false; + } + start = ++i; + continue; + } + tokenInProgress = true; + i++; + } + + } else if (separators.length() == 1) { + + final char separator = separators.charAt(0); + while (i < length) { + if (string.charAt(i) == separator) { + if (tokenInProgress) { + results.add(string.substring(start, i)); + tokenInProgress = false; + } + start = ++i; + continue; + } + tokenInProgress = true; + i++; + } + + } else { + + while (i < length) { + if (separators.indexOf(string.charAt(i)) >= 0) { + if (tokenInProgress) { + results.add(string.substring(start, i)); + tokenInProgress = false; + } + start = ++i; + continue; + } + tokenInProgress = true; + i++; + } + + } + + if (tokenInProgress) { + results.add(string.substring(start, i)); + } + + return (String[]) results.toArray(new String[results.size()]); + + } + + + + + public static String substringBefore(final String string, final String separator) { + + if (isEmpty(string) || separator == null) { + return string; + } + if (separator.length() == 0) { + return ""; + } + final int pos = string.indexOf(separator); + if (pos == -1) { + return string; + } + return string.substring(0, pos); + + } + + + + public static String substringAfter(final String string, final String separator) { + if (isEmpty(string)) { + return string; + } + if (separator == null) { + return ""; + } + final int pos = string.indexOf(separator); + if (pos == -1) { + return ""; + } + return string.substring(pos + separator.length()); + } + + + + public static int nextRandomInt() { + return (int)(Math.random() * Integer.MAX_VALUE); + } + + + + public static byte[] appendArrays(final byte[] firstArray, final byte[] secondArray) { + + validateNotNull(firstArray, "Appended array cannot be null"); + validateNotNull(secondArray, "Appended array cannot be null"); + + final byte[] result = new byte[firstArray.length + secondArray.length]; + + System.arraycopy(firstArray, 0, result, 0, firstArray.length); + System.arraycopy(secondArray, 0, result, firstArray.length, secondArray.length); + + return result; + + } + + + // This class should only be called statically + private CommonUtils() { + super(); + } + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/DecoderException.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/DecoderException.java new file mode 100644 index 0000000000..f22c38a973 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/DecoderException.java @@ -0,0 +1,19 @@ +package org.bouncycastle.jcajce.provider.test.jasypt; + +/** + * Thrown when a Decoder has encountered a failure condition during a decode. + * + * @author Apache Software Foundation + */ +public class DecoderException extends Exception { + + /** + * Creates a DecoderException + * + * @param pMessage A message with meaning to a human + */ + public DecoderException(String pMessage) { + super(pMessage); + } + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncoderException.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncoderException.java new file mode 100644 index 0000000000..a745caec9d --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncoderException.java @@ -0,0 +1,22 @@ +package org.bouncycastle.jcajce.provider.test.jasypt; + +/** + * Thrown when there is a failure condition during the encoding process. This + * exception is thrown when an Encoder encounters a encoding specific exception + * such as invalid data, inability to calculate a checksum, characters outside of the + * expected range. + * + * @author Apache Software Foundation + */ +public class EncoderException extends Exception { + + /** + * Creates a new instance of this exception with an useful message. + * + * @param pMessage a useful message relating to the encoder specific error. + */ + public EncoderException(String pMessage) { + super(pMessage); + } +} + diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionInitializationException.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionInitializationException.java new file mode 100644 index 0000000000..45b43af9af --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionInitializationException.java @@ -0,0 +1,52 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + +/** + * Exception thrown when an error is raised during initialization of + * an entity. + * + * @since 1.0 + * + * @author Daniel Fernández + * + */ +public final class EncryptionInitializationException + extends RuntimeException { + + private static final long serialVersionUID = 8929638240023639778L; + + public EncryptionInitializationException() { + super(); + } + + public EncryptionInitializationException(final Throwable t) { + super(t); + } + + public EncryptionInitializationException(final String msg, final Throwable t) { + super(msg, t); + } + + public EncryptionInitializationException(final String msg) { + super(msg); + } + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionOperationNotPossibleException.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionOperationNotPossibleException.java new file mode 100644 index 0000000000..1850a2d63c --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionOperationNotPossibleException.java @@ -0,0 +1,56 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + + +/** + *

+ * General exception thrown when any errors are raised during encryption, + * digesting, etc. + *

+ *

+ * It is intended to provide very little information (if any) of the error + * causes, so that encryption internals are not revealed through error + * messages. + *

+ * + * @since 1.0 + * + * @author Daniel Fernández + * + */ +public final class EncryptionOperationNotPossibleException + extends RuntimeException { + + private static final long serialVersionUID = 6304674109588715145L; + + public EncryptionOperationNotPossibleException() { + super(); + } + + public EncryptionOperationNotPossibleException(final Throwable t) { + super(t); + } + + public EncryptionOperationNotPossibleException(final String message) { + super(message); + } + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/FixedSaltGenerator.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/FixedSaltGenerator.java new file mode 100644 index 0000000000..44a460452a --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/FixedSaltGenerator.java @@ -0,0 +1,43 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + + +/** + *

+ * Marker interface for all implementations of {@link SaltGenerator} that + * will always return the same salt (for the same amount of bytes asked). + *

+ *

+ * Use of this interface in salt generators enables encryptors to perform + * some performance optimizations whenever they are used. + *

+ * + * @since 1.9.2 + * + * @author Daniel Fernández + * + */ +public interface FixedSaltGenerator + extends SaltGenerator { + + // Marker interface - no methods added + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/IvGenerator.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/IvGenerator.java new file mode 100644 index 0000000000..5e42da7a80 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/IvGenerator.java @@ -0,0 +1,73 @@ +/* + * ============================================================================= + * + * Copyright (c) 2019, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + +/** + *

+ * Common interface for all initialization vector (IV) generators which can be applied in + * encryption operations. + *

+ *

+ * Every implementation of this interface must be thread-safe. + *

+ * + * @since 1.9.3 + * + * @author Hoki Torres + * + */ +public interface IvGenerator +{ + + /** + *

+ * This method will be called for requesting the generation of a new + * IV of the specified length. + *

+ * + * @param lengthBytes the requested length for the IV. + * @return the generated IV. + */ + public byte[] generateIv(int lengthBytes); + + + /** + *

+ * Determines if the encrypted messages created with a + * specific IV generator will include (prepended) the unencrypted + * IV itself, so that it can be used for decryption + * operations. + *

+ *

+ * Generally, including the IV unencrypted in encryption results will + * be mandatory for randomly generated IV, or for those generated in a + * non-predictable manner. + * Otherwise, decryption operations will always fail. + * For fixed IV, inclusion will be optional (and in fact undesirable + * if we want to hide the IV value). + *

+ * + * @return whether the plain (unencrypted) IV has to be included in + * encryption results or not. + */ + public boolean includePlainIvInEncryptionResults(); + + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/NoIvGenerator.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/NoIvGenerator.java new file mode 100644 index 0000000000..8ecd9e9b3b --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/NoIvGenerator.java @@ -0,0 +1,72 @@ +/* + * ============================================================================= + * + * Copyright (c) 2019, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + + + +/** + *

+ * This implementation of {@link IvGenerator} always returns a + * initialization vector (IV) of length 0. + *

+ *

+ * This class is thread-safe. + *

+ * + * @since 1.9.3 + * + * @author Hoki Torres + * + */ +public class NoIvGenerator + implements IvGenerator { + + /** + * Creates a new instance of NoIvGenerator + * + */ + public NoIvGenerator() { + super(); + } + + + /** + * Return IV with 0 byte length. + * + * @param lengthBytes length in bytes. + * @return the generated IV. + */ + public byte[] generateIv(final int lengthBytes) { + return new byte[0]; + } + + + /** + * As this IV generator provides an empty vector, its inclusion + * unencrypted in encryption results is not necessary. + * + * @return false + */ + public boolean includePlainIvInEncryptionResults() { + return false; + } + + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Normalizer.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Normalizer.java new file mode 100644 index 0000000000..1a33db5769 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Normalizer.java @@ -0,0 +1,230 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + + +/** + *

+ * Utility for the normalization of Unicode Strings to NFC form. + *

+ *

+ * This class tries to use the java.text.Normalizer class in JDK 1.6 + * first and, if it the class is not found (Java version < 6), then it will use + * the ICU4J com.ibm.icu.text.Normalizer class (in this case, a + * ClassNotFoundException will be thrown if ICU4J is not present). + *

+ * + * @since 1.5 + * + * @author Daniel Fernández + * + */ +public final class Normalizer +{ + + private static final String ICU_NORMALIZER_CLASS_NAME = "com.ibm.icu.text.Normalizer"; + private static final String JDK_NORMALIZER_CLASS_NAME = "java.text.Normalizer"; + private static final String JDK_NORMALIZER_FORM_CLASS_NAME = "java.text.Normalizer$Form"; + + private static Boolean useIcuNormalizer = null; + + private static Method javaTextNormalizerMethod = null; + private static Object javaTextNormalizerFormNFCConstant = null; + + + /** + *

+ * Normalize Unicode-input message to NFC. + *

+ *

+ * This algorithm will first try to normalize the input's UNICODE using icu4j's + * com.ibm.icu.text.Normalizer and, if it is not present at the + * classpath, will try to use java.text.Normalizer. If this is not present + * either (this class appeared in JavaSE 6), it will raise an exception. + *

+ * + * @param message the message to be normalized + * @return the result of the normalization operation + */ + public static String normalizeToNfc(final String message) { + return new String(normalizeToNfc(message.toCharArray())); + } + + + /** + *

+ * Normalize Unicode-input message to NFC. + *

+ *

+ * This algorithm will first try to normalize the input's UNICODE using icu4j's + * com.ibm.icu.text.Normalizer and, if it is not present at the + * classpath, will try to use java.text.Normalizer. If this is not present + * either (this class appeared in JavaSE 6), it will raise an exception. + *

+ * + * @param message the message to be normalized + * @return the result of the normalization operation + */ + public static char[] normalizeToNfc(final char[] message) { + + if (useIcuNormalizer == null) { + // Still not initialized, will try to load the icu4j Normalizer. If + // icu4j is in the classpath, it will be used even if java version is >= 6. + try { + + initializeIcu4j(); + + } catch (final ClassNotFoundException e) { + + try { + + initializeJavaTextNormalizer(); + + } catch (final ClassNotFoundException e2) { + throw new EncryptionInitializationException( + "Cannot find a valid UNICODE normalizer: neither " + JDK_NORMALIZER_CLASS_NAME + " nor " + + ICU_NORMALIZER_CLASS_NAME + " have been found at the classpath. If you are using " + + "a version of the JDK older than JavaSE 6, you should include the icu4j library in " + + "your classpath."); + } catch (final NoSuchMethodException e2) { + throw new EncryptionInitializationException( + "Cannot find a valid UNICODE normalizer: " + JDK_NORMALIZER_CLASS_NAME + " has " + + "been found at the classpath, but has an incompatible signature for its 'normalize' " + + "method."); + } catch (final NoSuchFieldException e2) { + throw new EncryptionInitializationException( + "Cannot find a valid UNICODE normalizer: " + JDK_NORMALIZER_FORM_CLASS_NAME + " has " + + "been found at the classpath, but seems to have no 'NFC' value."); + } catch (final IllegalAccessException e2) { + throw new EncryptionInitializationException( + "Cannot find a valid UNICODE normalizer: " + JDK_NORMALIZER_FORM_CLASS_NAME + " has " + + "been found at the classpath, but seems to have no 'NFC' value."); + } + + } + } + + if (useIcuNormalizer.booleanValue()) { + return normalizeWithIcu4j(message); + } + + return normalizeWithJavaNormalizer(message); + + } + + + + static void initializeIcu4j() throws ClassNotFoundException { + Thread.currentThread().getContextClassLoader().loadClass(ICU_NORMALIZER_CLASS_NAME); + useIcuNormalizer = Boolean.TRUE; + } + + + + static void initializeJavaTextNormalizer() + throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException { + + final Class javaTextNormalizerClass = + Thread.currentThread().getContextClassLoader().loadClass(JDK_NORMALIZER_CLASS_NAME); + final Class javaTextNormalizerFormClass = + Thread.currentThread().getContextClassLoader().loadClass(JDK_NORMALIZER_FORM_CLASS_NAME); + javaTextNormalizerMethod = + javaTextNormalizerClass.getMethod( + "normalize", new Class[]{ CharSequence.class, javaTextNormalizerFormClass }); + final Field javaTextNormalizerFormNFCConstantField = javaTextNormalizerFormClass.getField("NFC"); + javaTextNormalizerFormNFCConstant = javaTextNormalizerFormNFCConstantField.get(null); + + useIcuNormalizer = Boolean.FALSE; + + } + + + + + static char[] normalizeWithJavaNormalizer(final char[] message) { + + if (javaTextNormalizerMethod == null || javaTextNormalizerFormNFCConstant == null) { + throw new EncryptionInitializationException( + "Cannot use: " + JDK_NORMALIZER_FORM_CLASS_NAME + ", as JDK-based normalization has " + + "not been initialized! (check previous execution errors)"); + } + + // Using java JDK's Normalizer, we cannot avoid creating Strings + // (it is the only possible interface to the Normalizer class). + // + // Note java.text.Normalizer is accessed via reflection in order to allow this + // class to be JDK 1.4-compilable (though ICU4j will be needed at runtime + // if Java 1.4 is used). + final String messageStr = new String(message); + final String result; + try { + result = (String) javaTextNormalizerMethod.invoke( + null, new Object[] { messageStr, javaTextNormalizerFormNFCConstant }); + } catch (final Exception e) { + throw new EncryptionInitializationException( + "Could not perform a valid UNICODE normalization", e); + } + return result.toCharArray(); + } + + + static char[] normalizeWithIcu4j(final char[] message) { + // initialize the result to twice the size of the message + // this should be more than enough in most cases + char[] normalizationResult = new char[message.length * 2]; + int normalizationResultSize = 0; + while(true) { + // Execute normalization. The result will be written into the normalizationResult + // char array, and the returned int will be the real size of the result. Normally, + // this will be smaller than the size of normalizationResult, but if it is bigger, + // we will have to create a new normalizationResult array and try again (icu4j will + // not raise an exception, only return a value bigger than the destination array size). + normalizationResultSize = 0; + throw new IllegalStateException("Not implemented"); + //normalize(message, normalizationResult, new NFCMode(), 0); +// if (normalizationResultSize <= normalizationResult.length) { +// // everything went OK and result fitted. Copy to a correctly-sized array +// // and clean normalizationResult +// final char[] result = new char[normalizationResultSize]; +// System.arraycopy(normalizationResult, 0, result, 0, normalizationResultSize); +// for (int i = 0; i < normalizationResult.length; i++) { +// normalizationResult[i] = (char)0; +// } +// return result; +// } +// // We need a bigger array. the old array must be cleaned also +// for (int i = 0; i < normalizationResult.length; i++) { +// normalizationResult[i] = (char)0; +// } +// normalizationResult = new char[normalizationResultSize]; + } + + } + + + + private Normalizer() { + super(); + } + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteCleanablePasswordEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteCleanablePasswordEncryptor.java new file mode 100644 index 0000000000..34c9ce83f9 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteCleanablePasswordEncryptor.java @@ -0,0 +1,44 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + + +/** + *

+ * Common interface for all Password Based Encryptors which receive a + * byte array message and return a byte array result, and provide means + * to set passwords as cleanable char[] objects (instead of + * immutable Strings). + *

+ *

+ * For a default implementation, see {@link StandardPBEByteEncryptor}. + *

+ * + * @since 1.8 + * + * @author Daniel Fernández + * + */ +public interface PBEByteCleanablePasswordEncryptor + extends PBEByteEncryptor, CleanablePasswordBased { + + // aggregator interface + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteEncryptor.java new file mode 100644 index 0000000000..883d4531a4 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteEncryptor.java @@ -0,0 +1,42 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + + +/** + *

+ * Common interface for all Password Based Encryptors which receive a + * byte array message and return a byte array result. + *

+ *

+ * For a default implementation, see {@link StandardPBEByteEncryptor}. + *

+ * + * @since 1.0 + * + * @author Daniel Fernández + * + */ +public interface PBEByteEncryptor + extends ByteEncryptor, PasswordBased { + + // aggregator interface + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBECleanablePasswordConfig.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBECleanablePasswordConfig.java new file mode 100644 index 0000000000..62cfed747b --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBECleanablePasswordConfig.java @@ -0,0 +1,70 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + + + +/** + *

+ * Common interface for all PBEConfig implementations that store passwords as char[] instead + * of String and also allow this passwords to be set as char[] instead of Strings. + *

+ * + * @since 1.8 + * + * @author Daniel Fernández + * + */ +public interface PBECleanablePasswordConfig +{ + + + /** + *

+ * Return the password set, as a char array. + *

+ *

+ * Important: the returned array MUST BE A COPY of the one + * stored in the configuration object. The caller of + * this method is therefore be responsible for cleaning this + * resulting char[]. + *

+ * + * @since 1.8 + * + */ + public char[] getPasswordCharArray(); + + /** + *

+ * Clean the password stored in this configuration object. + *

+ *

+ * A common implementation of this cleaning operation consists of + * iterating the array of chars and setting each of its positions to (char)0. + *

+ * + * @since 1.8 + * + */ + public void cleanPassword(); + + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEConfig.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEConfig.java new file mode 100644 index 0000000000..3203e66c4d --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEConfig.java @@ -0,0 +1,208 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + +import java.security.Provider; + + +/** + *

+ * Common interface for config classes applicable to + + *

+ *

+ * This interface lets the user create new PBEConfig + * classes which retrieve values for this parameters from different + * (and maybe more secure) sources (remote servers, LDAP, other databases...), + * and do this transparently for the encryptor object. + *

+ *

+ * The config objects passed to an encryptor will only be queried once + * for each configuration parameter, and this will happen + * during the initialization of the encryptor object. + *

+ *

+ * For a default implementation, see SimplePBEConfig. + *

+ * + * @since 1.0 + * + * @author Daniel Fernández + * + */ +public interface PBEConfig +{ + + + /** + *

+ * Returns the algorithm to be used for encryption, like + * PBEWithMD5AndDES. + *

+ * + *

+ * This algorithm has to be supported by the specified JCE provider + * (or the default one if no provider has been specified) and, if the + * provider supports it, you can also specify mode and + * padding for it, like ALGORITHM/MODE/PADDING. + *

+ * + * @return the name of the algorithm to be used. + */ + public String getAlgorithm(); + + + /** + *

+ * Returns the password to be used. + *

+ *

+ * There is no default value for password, so not setting + * this parameter either from a + * {@link PBEConfig} object or from + * a call to setPassword will result in an + * EncryptionInitializationException being thrown during initialization. + *

+ * + * @return the password to be used. + */ + public String getPassword(); + + + /** + *

+ * Returns the number of hashing iterations applied to obtain the + * encryption key. + *

+ *

+ * This mechanism is explained in + * PKCS #5: Password-Based Cryptography Standard. + *

+ * + * @return the number of iterations + */ + public Integer getKeyObtentionIterations(); + + + /** + *

+ * Returns a {@link SaltGenerator} implementation to be used by the + * encryptor. + *

+ *

+ * If this method returns null, the encryptor will ignore the config object + * when deciding the salt generator to be used. + *

+ * + * @return the salt generator, or null if this object will not want to set + * a specific SaltGenerator implementation. + */ + public SaltGenerator getSaltGenerator(); + + + /** + *

+ * Returns a {@link IvGenerator} implementation to be used by the + * encryptor. + *

+ *

+ * If this method returns null, the encryptor will ignore the config object + * when deciding the IV generator to be used. + *

+ * + * @return the IV generator, or null if this object will not want to set + * a specific IvGenerator implementation. + */ + public IvGenerator getIvGenerator(); + + + /** + *

+ * Returns the name of the java.security.Provider implementation + * to be used by the encryptor for obtaining the encryption algorithm. This + * provider must have been registered beforehand. + *

+ *

+ * If this method returns null, the encryptor will ignore this parameter + * when deciding the name of the security provider to be used. + *

+ *

+ * If this method does not return null, and neither does {@link #getProvider()}, + * providerName will be ignored, and the provider object returned + * by getProvider() will be used. + *

+ * + * @since 1.3 + * + * @return the name of the security provider to be used. + */ + public String getProviderName(); + + + /** + *

+ * Returns the java.security.Provider implementation object + * to be used by the encryptor for obtaining the encryption algorithm. + *

+ *

+ * If this method returns null, the encryptor will ignore this parameter + * when deciding the security provider object to be used. + *

+ *

+ * If this method does not return null, and neither does {@link #getProviderName()}, + * providerName will be ignored, and the provider object returned + * by getProvider() will be used. + *

+ *

+ * The provider returned by this method does not need to be + * registered beforehand, and its use will not result in its + * being registered. + *

+ * + * @since 1.3 + * + * @return the security provider object to be asked for the digest + * algorithm. + */ + public Provider getProvider(); + + + + + + + /** + *

+ * Get the size of the pool of encryptors to be created. + *

+ *

+ * This parameter will be ignored if used with a non-pooled encryptor. + *

+ * + * @since 1.7 + * + * @return the size of the pool to be used if this configuration is used with a + * pooled encryptor + */ + public Integer getPoolSize(); + + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringCleanablePasswordEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringCleanablePasswordEncryptor.java new file mode 100644 index 0000000000..4c4ae55d09 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringCleanablePasswordEncryptor.java @@ -0,0 +1,43 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + + +/** + *

+ * Common interface for all Password Based Encryptors which receive a + * String message and return a String result, and provide means + * to set passwords as cleanable char[] objects (instead of + * immutable Strings). + *

+ * For a default implementation, see {@link StandardPBEStringEncryptor}. + *

+ * + * @since 1.8 + * + * @author Daniel Fernández + * + */ +public interface PBEStringCleanablePasswordEncryptor + extends PBEStringEncryptor, CleanablePasswordBased { + + // aggregator interface + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringEncryptor.java new file mode 100644 index 0000000000..487c788e4e --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringEncryptor.java @@ -0,0 +1,42 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + + +/** + *

+ * Common interface for all Password Based Encryptors which receive a + * String message and return a String result. + *

+ *

+ * For a default implementation, see {@link StandardPBEStringEncryptor}. + *

+ * + * @since 1.0 + * + * @author Daniel Fernández + * + */ +public interface PBEStringEncryptor + extends StringEncryptor, PasswordBased { + + // aggregator interface + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PasswordBased.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PasswordBased.java new file mode 100644 index 0000000000..5b966914e4 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PasswordBased.java @@ -0,0 +1,44 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + +/** + *

+ * Common interface for all entities which can be set a password. + *

+ * + * @since 1.3 + * + * @author Daniel Fernández + * + */ +public interface PasswordBased +{ + + /** + *

+ * Sets a password to be used by the encryptor. + *

+ * + * @param password the password to be used. + */ + public void setPassword(String password); + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomIvGenerator.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomIvGenerator.java new file mode 100644 index 0000000000..70c58898e1 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomIvGenerator.java @@ -0,0 +1,105 @@ +/* + * ============================================================================= + * + * Copyright (c) 2019, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +/** + *

+ * This implementation of {@link IvGenerator} holds a secure random + * generator which can be used for generating random initialization vectors (IV) for encryption. + *

+ *

+ * The algorithm used for random number generation can be configured at + * instantiation time. If not, the default algorithm will be used. + *

+ *

+ * This class is thread-safe. + *

+ * + * @since 1.9.3 + * + * @author Hoki Torres + * + */ +public class RandomIvGenerator + implements IvGenerator { + + /** + * The default algorithm to be used for secure random number + * generation: set to SHA1PRNG. + */ + public static final String DEFAULT_SECURE_RANDOM_ALGORITHM = "SHA1PRNG"; + + private final SecureRandom random; + + + /** + * Creates a new instance of RandomIvGenerator using the + * default secure random number generation algorithm. + */ + public RandomIvGenerator() { + this(DEFAULT_SECURE_RANDOM_ALGORITHM); + } + + + /** + * Creates a new instance of RandomIvGenerator specifying a + * secure random number generation algorithm. + */ + public RandomIvGenerator(final String secureRandomAlgorithm) { + super(); + try { + this.random = SecureRandom.getInstance(secureRandomAlgorithm); + } catch (NoSuchAlgorithmException e) { + throw new EncryptionInitializationException(e); + } + } + + + /** + * Generate a random IV of the specified length in bytes. + * + * @param lengthBytes length in bytes. + * @return the generated IV. + */ + public byte[] generateIv(final int lengthBytes) { + final byte[] iv = new byte[lengthBytes]; + synchronized (this.random) { + this.random.nextBytes(iv); + } + return iv; + } + + + /** + * This IV generator needs the IV to be included unencrypted in + * encryption results, because of its being random. This method will always + * return true. + * + * @return true + */ + public boolean includePlainIvInEncryptionResults() { + return true; + } + + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomSaltGenerator.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomSaltGenerator.java new file mode 100644 index 0000000000..04453b679f --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomSaltGenerator.java @@ -0,0 +1,109 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +/** + *

+ * This implementation of {@link SaltGenerator} holds a secure random + * generator which can be used for generating random salts for encryption + * or digesting. + *

+ *

+ * The algorithm used for random number generation can be configured at + * instantiation time. If not, the default algorithm will be used. + *

+ *

+ * This class is thread-safe. + *

+ * + * @since 1.2 + * + * @author Daniel Fernández + * + */ +public class RandomSaltGenerator + implements SaltGenerator { + + /** + * The default algorithm to be used for secure random number + * generation: set to SHA1PRNG. + */ + public static final String DEFAULT_SECURE_RANDOM_ALGORITHM = "SHA1PRNG"; + + private final SecureRandom random; + + + /** + * Creates a new instance of RandomSaltGenerator using the + * default secure random number generation algorithm. + */ + public RandomSaltGenerator() { + this(DEFAULT_SECURE_RANDOM_ALGORITHM); + } + + + /** + * Creates a new instance of RandomSaltGenerator specifying a + * secure random number generation algorithm. + * + * @since 1.5 + * + */ + public RandomSaltGenerator(final String secureRandomAlgorithm) { + super(); + try { + this.random = SecureRandom.getInstance(secureRandomAlgorithm); + } catch (NoSuchAlgorithmException e) { + throw new EncryptionInitializationException(e); + } + } + + + /** + * Generate a random salt of the specified length in bytes. + * + * @param lengthBytes length in bytes. + * @return the generated salt. + */ + public byte[] generateSalt(final int lengthBytes) { + final byte[] salt = new byte[lengthBytes]; + synchronized (this.random) { + this.random.nextBytes(salt); + } + return salt; + } + + + /** + * This salt generator needs the salt to be included unencrypted in + * encryption results, because of its being random. This method will always + * return true. + * + * @return true + */ + public boolean includePlainSaltInEncryptionResults() { + return true; + } + + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/SaltGenerator.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/SaltGenerator.java new file mode 100644 index 0000000000..d8022df565 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/SaltGenerator.java @@ -0,0 +1,73 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + +/** + *

+ * Common interface for all salt generators which can be applied in digest + * or encryption operations. + *

+ *

+ * Every implementation of this interface must be thread-safe. + *

+ * + * @since 1.2 + * + * @author Daniel Fernández + * + */ +public interface SaltGenerator +{ + + /** + *

+ * This method will be called for requesting the generation of a new + * salt of the specified length. + *

+ * + * @param lengthBytes the requested length for the salt. + * @return the generated salt. + */ + public byte[] generateSalt(int lengthBytes); + + + /** + *

+ * Determines if the digests and encrypted messages created with a + * specific salt generator will include (prepended) the unencrypted + * salt itself, so that it can be used for matching and decryption + * operations. + *

+ *

+ * Generally, including the salt unencrypted in encryption results will + * be mandatory for randomly generated salts, or for those generated in a + * non-predictable manner. + * Otherwise, digest matching and decryption operations will always fail. + * For fixed salts, inclusion will be optional (and in fact undesirable + * if we want to hide the salt value). + *

+ * + * @return whether the plain (unencrypted) salt has to be included in + * encryption results or not. + */ + public boolean includePlainSaltInEncryptionResults(); + + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEByteEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEByteEncryptor.java new file mode 100644 index 0000000000..eae8b798ef --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEByteEncryptor.java @@ -0,0 +1,1209 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + +import java.lang.reflect.Constructor; +import java.security.InvalidKeyException; +import java.security.Provider; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + + +/** + *

+ * Standard implementation of the {@link PBEByteEncryptor} interface. + * This class lets the user specify the algorithm, provider and + * the initialization vector (IV) generator to be used for + * encryption, the password to use, + * the number of hashing iterations and the salt generator + * that will be applied for obtaining the encryption key. + *

+ *

+ * This class is thread-safe. + *

+ *

+ *
Configuration + *

+ *

+ * The algorithm, provider, IV generator, password, key-obtention iterations + * and salt generator can take values in any of these ways: + *

    + *
  • Using its default values (except for password).
  • + *
  • Setting a {@link PBEConfig} + * object which provides new + * configuration values.
  • + *
  • Calling the corresponding setAlgorithm(...), + * setProvider(...), setProviderName(...), + * setIvGenerator(...), + * setPassword(...), setKeyObtentionIterations(...) or + * setSaltGenerator(...) methods.
  • + *
+ * And the actual values to be used for initialization will be established + * by applying the following priorities: + *
    + *
  1. First, the default values are considered (except for password).
  2. + *
  3. Then, if a {@link PBEConfig} + * object has been set with + * setConfig(...), the non-null values returned by its + * getX() methods override the default values.
  4. + *
  5. Finally, if the corresponding setX(...) method has been called + * on the encryptor itself for any of the configuration parameters, the + * values set by these calls override all of the above.
  6. + *
+ *

+ * + *

+ *
Initialization + *

+ *

+ * Before it is ready to encrypt, an object of this class has to be + * initialized. Initialization happens: + *

    + *
  • When initialize() is called.
  • + *
  • When encrypt(...) or decrypt(...) are called for the + * first time, if initialize() has not been called before.
  • + *
+ * Once an encryptor has been initialized, trying to + * change its configuration will + * result in an AlreadyInitializedException being thrown. + *

+ * + *

+ *
Usage + *

+ *

+ * An encryptor may be used for: + *

    + *
  • Encrypting messages, by calling the encrypt(...) method.
  • + *
  • Decrypting messages, by calling the decrypt(...) method.
  • + *
+ * If a random salt generator is used, two encryption results for + * the same message will always be different + * (except in the case of random salt coincidence). This may enforce + * security by difficulting brute force attacks on sets of data at a time + * and forcing attackers to perform a brute force attack on each separate + * piece of encrypted data. + * The same applies when a random IV generator is used. + *

+ *

+ * To learn more about the mechanisms involved in encryption, read + * PKCS #5: Password-Based Cryptography Standard. + *

+ * + * @since 1.0 + * + * @author Daniel Fernández + * + */ +public final class StandardPBEByteEncryptor + implements PBEByteCleanablePasswordEncryptor { + + + /** + * The default algorithm to be used if none specified: PBEWithMD5AndDES. + */ + public static final String DEFAULT_ALGORITHM = "PBEWithMD5AndDES"; + + /** + * The default number of hashing iterations applied for obtaining the + * encryption key from the specified password, set to 1000. + */ + public static final int DEFAULT_KEY_OBTENTION_ITERATIONS = 1000; + + /** + * The default salt size, only used if the chosen encryption algorithm + * is not a block algorithm and thus block size cannot be used as salt size. + */ + public static final int DEFAULT_SALT_SIZE_BYTES = 8; + + /** + * The default IV size, only used if the chosen encryption algorithm + * is not a block algorithm and thus block size cannot be used as IV size. + */ + public static final int DEFAULT_IV_SIZE_BYTES = 16; + + + // Algorithm (and provider-related info) for Password Based Encoding. + private String algorithm = DEFAULT_ALGORITHM; + private String providerName = null; + private Provider provider = null; + + // Password to be applied. This will NOT have a default value. If none + // is set during configuration, an exception will be thrown. + private char[] password = null; + + // Number of hashing iterations to be applied for obtaining the encryption + // key from the specified password. + private int keyObtentionIterations = DEFAULT_KEY_OBTENTION_ITERATIONS; + + // SaltGenerator to be used. Initialization of a salt generator is costly, + // and so default value will be applied only in initialize(), if it finally + // becomes necessary. + private SaltGenerator saltGenerator = null; + + // Size in bytes of the salt to be used for obtaining the + // encryption key. This size will depend on the PBE algorithm being used, + // and it will be set to the size of the block for the specific + // chosen algorithm (if the algorithm is not a block algorithm, the + // default value will be used). + private int saltSizeBytes = DEFAULT_SALT_SIZE_BYTES; + + // IvGenerator to be used. Initialization of a IV generator is costly, + // and so default value will be applied only in initialize(), if it finally + // becomes necessary. + private IvGenerator ivGenerator = null; + + // Size in bytes of the IV. This size will depend on the PBE algorithm + // being used, and it will be set to the size of the block for the specific + // chosen algorithm (if the algorithm is not a block algorithm, the + // default value will be used). + private int ivSizeBytes = DEFAULT_IV_SIZE_BYTES; + + + // Config object set (optionally). + private PBEConfig config = null; + + /* + * Set of booleans which indicate whether the config or default values + * have to be overriden because of the setX methods having been + * called. + */ + private boolean algorithmSet = false; + private boolean passwordSet = false; + private boolean iterationsSet = false; + private boolean saltGeneratorSet = false; + private boolean ivGeneratorSet = false; + private boolean providerNameSet = false; + private boolean providerSet = false; + + + /* + * Flag which indicates whether the encryptor has been initialized or not. + * + * Once initialized, no further modifications to its configuration will + * be allowed. + */ + private boolean initialized = false; + + + // Encryption key generated. + private SecretKey key = null; + + // Ciphers to be used for encryption and decryption. + private Cipher encryptCipher = null; + private Cipher decryptCipher = null; + + + // Flag which indicates whether the salt generator being used is a + // FixedSaltGenerator implementation (in which case some optimizations can + // be applied). + private boolean optimizingDueFixedSalt = false; + private byte[] fixedSaltInUse = null; + + + + + /** + * Creates a new instance of StandardPBEByteEncryptor. + */ + public StandardPBEByteEncryptor() { + super(); + } + + + /** + *

+ * Sets a {@link PBEConfig} object + * for the encryptor. If this config + * object is set, it will be asked values for: + *

+ * + *
    + *
  • Algorithm
  • + *
  • Security Provider (or provider name)
  • + *
  • Password
  • + *
  • Hashing iterations for obtaining the encryption key
  • + *
  • Salt generator
  • + *
  • IV generator
  • + *
+ * + *

+ * The non-null values it returns will override the default ones, + * and will be overriden by any values specified with a setX + * method. + *

+ * + * @param config the PBEConfig object to be used as the + * source for configuration parameters. + */ + public synchronized void setConfig(PBEConfig config) { + CommonUtils.validateNotNull(config, "Config cannot be set null"); + if (isInitialized()) { + throw new AlreadyInitializedException(); + } + this.config = config; + } + + + /** + *

+ * Sets the algorithm to be used for encryption, like + * PBEWithMD5AndDES. + *

+ *

+ * This algorithm has to be supported by your JCE provider (if you specify + * one, or the default JVM provider if you don't) and, if it is supported, + * you can also specify mode and padding for + * it, like ALGORITHM/MODE/PADDING. + *

+ * + * @param algorithm the name of the algorithm to be used. + */ + public synchronized void setAlgorithm(String algorithm) { + CommonUtils.validateNotEmpty(algorithm, "Algorithm cannot be set empty"); + if (isInitialized()) { + throw new AlreadyInitializedException(); + } + this.algorithm = algorithm; + this.algorithmSet = true; + } + + + /** + *

+ * Sets the password to be used. + *

+ *

+ * There is no default value for password, so not setting + * this parameter either from a + * {@link PBEConfig} object or from + * a call to setPassword will result in an + * EncryptionInitializationException being thrown during initialization. + *

+ * + * @param password the password to be used. + */ + public synchronized void setPassword(String password) { + CommonUtils.validateNotEmpty(password, "Password cannot be set empty"); + if (isInitialized()) { + throw new AlreadyInitializedException(); + } + if (this.password != null) { + // We clean the old password, if there is one. + cleanPassword(this.password); + } + this.password = password.toCharArray(); + this.passwordSet = true; + } + + + /** + *

+ * Sets the password to be used, as a char[]. + *

+ *

+ * This allows the password to be specified as a cleanable + * char[] instead of a String, in extreme security conscious environments + * in which no copy of the password as an immutable String should + * be kept in memory. + *

+ *

+ * Important: the array specified as a parameter WILL BE COPIED + * in order to be stored as encryptor configuration. The caller of + * this method will therefore be responsible for its cleaning (jasypt + * will only clean the internally stored copy). + *

+ *

+ * There is no default value for password, so not setting + * this parameter either from a + * {@link PBEConfig} object or from + * a call to setPassword will result in an + * EncryptionInitializationException being thrown during initialization. + *

+ * + * @since 1.8 + * + * @param password the password to be used. + */ + public synchronized void setPasswordCharArray(char[] password) { + CommonUtils.validateNotNull(password, "Password cannot be set null"); + CommonUtils.validateIsTrue(password.length > 0, "Password cannot be set empty"); + if (isInitialized()) { + throw new AlreadyInitializedException(); + } + if (this.password != null) { + // We clean the old password, if there is one. + cleanPassword(this.password); + } + this.password = new char[password.length]; + System.arraycopy(password, 0, this.password, 0, password.length); + this.passwordSet = true; + } + + + /** + *

+ * Set the number of hashing iterations applied to obtain the + * encryption key. + *

+ *

+ * This mechanism is explained in + * PKCS #5: Password-Based Cryptography Standard. + *

+ * + * @param keyObtentionIterations the number of iterations + */ + public synchronized void setKeyObtentionIterations( + int keyObtentionIterations) { + CommonUtils.validateIsTrue(keyObtentionIterations > 0, + "Number of iterations for key obtention must be " + + "greater than zero"); + if (isInitialized()) { + throw new AlreadyInitializedException(); + } + this.keyObtentionIterations = keyObtentionIterations; + this.iterationsSet = true; + } + + + /** + *

+ * Sets the salt generator to be used. If no salt generator is specified, + * an instance of RandomSaltGenerator will be used. + *

+ * + * @param saltGenerator the salt generator to be used. + */ + public synchronized void setSaltGenerator(SaltGenerator saltGenerator) { + CommonUtils.validateNotNull(saltGenerator, "Salt generator cannot be set null"); + if (isInitialized()) { + throw new AlreadyInitializedException(); + } + this.saltGenerator = saltGenerator; + this.saltGeneratorSet = true; + } + + /** + *

+ * Sets the IV generator to be used. If no IV generator is specified, + * an instance of NoIvGenerator will be used. + *

+ * + * @param ivGenerator the IV generator to be used. + */ + public synchronized void setIvGenerator(IvGenerator ivGenerator) { ; + if (isInitialized()) { + throw new AlreadyInitializedException(); + } + this.ivGenerator = ivGenerator; + this.ivGeneratorSet = true; + } + + + /** + *

+ * Sets the name of the security provider to be asked for the + * encryption algorithm. This security provider has to be registered + * beforehand at the JVM security framework. + *

+ *

+ * The provider can also be set with the {@link #setProvider(Provider)} + * method, in which case it will not be necessary neither registering + * the provider beforehand, + * nor calling this {@link #setProviderName(String)} method to specify + * a provider name. + *

+ *

+ * Note that a call to {@link #setProvider(Provider)} overrides any value + * set by this method. + *

+ *

+ * If no provider name / provider is explicitly set, the default JVM + * provider will be used. + *

+ * + * @since 1.3 + * + * @param providerName the name of the security provider to be asked + * for the encryption algorithm. + */ + public synchronized void setProviderName(String providerName) { + CommonUtils.validateNotNull(providerName, "Provider name cannot be set null"); + if (isInitialized()) { + throw new AlreadyInitializedException(); + } + this.providerName = providerName; + this.providerNameSet = true; + } + + + /** + *

+ * Sets the security provider to be asked for the encryption algorithm. + * The provider does not have to be registered at the security + * infrastructure beforehand, and its being used here will not result in + * its being registered. + *

+ *

+ * If this method is called, calling {@link #setProviderName(String)} + * becomes unnecessary. + *

+ *

+ * If no provider name / provider is explicitly set, the default JVM + * provider will be used. + *

+ * + * @since 1.3 + * + * @param provider the provider to be asked for the chosen algorithm + */ + public synchronized void setProvider(Provider provider) { + CommonUtils.validateNotNull(provider, "Provider cannot be set null"); + if (isInitialized()) { + throw new AlreadyInitializedException(); + } + this.provider = provider; + this.providerSet = true; + } + + + + + + + + + + + /* + * Clone this encryptor 'size' times and initialize it. + * This encryptor will be at position 0 itself. + * Clones will NOT be initialized. + */ + synchronized StandardPBEByteEncryptor[] cloneAndInitializeEncryptor(final int size) { + + if (isInitialized()) { + throw new EncryptionInitializationException( + "Cannot clone encryptor if it has been already initialized"); + } + + // If there is a config object, this forces the password configured value + // (if any) into the this.password property. + resolveConfigurationPassword(); + + final char[] copiedPassword = new char[this.password.length]; + System.arraycopy(this.password, 0, copiedPassword, 0, this.password.length); + + // Initialize the encryptor - note that this will clean the + // password (that's why copied it before) + initialize(); + + final StandardPBEByteEncryptor[] clones = new StandardPBEByteEncryptor[size]; + + clones[0] = this; + + for (int i = 1; i < size; i++) { + + final StandardPBEByteEncryptor clone = new StandardPBEByteEncryptor(); + clone.setPasswordCharArray(copiedPassword); + if (CommonUtils.isNotEmpty(this.algorithm)) { + clone.setAlgorithm(this.algorithm); + } + clone.setKeyObtentionIterations(this.keyObtentionIterations); + if (this.provider != null) { + clone.setProvider(this.provider); + } + if (this.providerName != null) { + clone.setProviderName(this.providerName); + } + if (this.saltGenerator != null) { + clone.setSaltGenerator(this.saltGenerator); + } + if (this.ivGenerator != null) { + clone.setIvGenerator(this.ivGenerator); + } + + clones[i] = clone; + + } + + cleanPassword(copiedPassword); + + return clones; + + } + + + + + /** + *

+ * Returns true if the encryptor has already been initialized, false if + * not.
+ * Initialization happens: + *

+ *
    + *
  • When initialize is called.
  • + *
  • When encrypt or decrypt are called for the + * first time, if initialize has not been called before.
  • + *
+ *

+ * Once an encryptor has been initialized, trying to + * change its configuration will + * result in an AlreadyInitializedException being thrown. + *

+ * + * @return true if the encryptor has already been initialized, false if + * not. + */ + public boolean isInitialized() { + return this.initialized; + } + + + /** + *

+ * Initialize the encryptor. + *

+ *

+ * This operation will consist in determining the actual configuration + * values to be used, and then initializing the encryptor with them. + *
+ * These values are decided by applying the following priorities: + *

+ *
    + *
  1. First, the default values are considered (except for password). + *
  2. + *
  3. Then, if a + * {@link PBEConfig} + * object has been set with + * setConfig, the non-null values returned by its + * getX methods override the default values.
  4. + *
  5. Finally, if the corresponding setX method has been called + * on the encryptor itself for any of the configuration parameters, + * the values set by these calls override all of the above.
  6. + *
+ *

+ * Once an encryptor has been initialized, trying to + * change its configuration will + * result in an AlreadyInitializedException being thrown. + *

+ * + * @throws EncryptionInitializationException if initialization could not + * be correctly done (for example, no password has been set). + */ + public synchronized void initialize() { + + // Double-check to avoid synchronization issues + if (!this.initialized) { + + /* + * If a PBEConfig object has been set, we need to + * consider the values it returns (if, for each value, the + * corresponding "setX" method has not been called). + */ + if (this.config != null) { + + resolveConfigurationPassword(); + + final String configAlgorithm = this.config.getAlgorithm(); + if (configAlgorithm != null) { + CommonUtils.validateNotEmpty(configAlgorithm, + "Algorithm cannot be set empty"); + } + + final Integer configKeyObtentionIterations = + this.config.getKeyObtentionIterations(); + if (configKeyObtentionIterations != null) { + CommonUtils.validateIsTrue(configKeyObtentionIterations.intValue() > 0, + "Number of iterations for key obtention must be " + + "greater than zero"); + } + + final SaltGenerator configSaltGenerator = this.config.getSaltGenerator(); + + final IvGenerator configIvGenerator = this.config.getIvGenerator(); + + final String configProviderName = this.config.getProviderName(); + if (configProviderName != null) { + CommonUtils.validateNotEmpty(configProviderName, + "Provider name cannot be empty"); + } + + final Provider configProvider = this.config.getProvider(); + + this.algorithm = + ((this.algorithmSet) || (configAlgorithm == null))? + this.algorithm : configAlgorithm; + this.keyObtentionIterations = + ((this.iterationsSet) || + (configKeyObtentionIterations == null))? + this.keyObtentionIterations : + configKeyObtentionIterations.intValue(); + this.saltGenerator = + ((this.saltGeneratorSet) || (configSaltGenerator == null))? + this.saltGenerator : configSaltGenerator; + this.ivGenerator = + ((this.ivGeneratorSet) || (configIvGenerator == null))? + this.ivGenerator : configIvGenerator; + this.providerName = + ((this.providerNameSet) || (configProviderName == null))? + this.providerName : configProviderName; + this.provider = + ((this.providerSet) || (configProvider == null))? + this.provider : configProvider; + + } + + /* + * If the encryptor was not set a salt generator in any way, + * it is time to apply its default value. + */ + if (this.saltGenerator == null) { + this.saltGenerator = new RandomSaltGenerator(); + } + + /* + * Default value is a no-op IV generator to maintain backwards compatibility + */ + if (this.ivGenerator == null) { + this.ivGenerator = new NoIvGenerator(); + } + + try { + + // Password cannot be null. + if (this.password == null) { + throw new EncryptionInitializationException( + "Password not set for Password Based Encryptor"); + } + + // Normalize password to NFC form + final char[] normalizedPassword = Normalizer.normalizeToNfc(this.password); + + /* + * Encryption and decryption Ciphers are created the usual way. + */ + final PBEKeySpec pbeKeySpec = new PBEKeySpec(normalizedPassword); + + // We don't need the char[] passwords anymore -> clean! + cleanPassword(this.password); + cleanPassword(normalizedPassword); + + + if (this.provider != null) { + + final SecretKeyFactory factory = + SecretKeyFactory.getInstance( + this.algorithm, + this.provider); + + this.key = factory.generateSecret(pbeKeySpec); + + this.encryptCipher = + Cipher.getInstance(this.algorithm, this.provider); + this.decryptCipher = + Cipher.getInstance(this.algorithm, this.provider); + + } else if (this.providerName != null) { + + final SecretKeyFactory factory = + SecretKeyFactory.getInstance( + this.algorithm, + this.providerName); + + this.key = factory.generateSecret(pbeKeySpec); + + this.encryptCipher = + Cipher.getInstance(this.algorithm, this.providerName); + this.decryptCipher = + Cipher.getInstance(this.algorithm, this.providerName); + + } else { + + final SecretKeyFactory factory = + SecretKeyFactory.getInstance(this.algorithm); + + this.key = factory.generateSecret(pbeKeySpec); + + this.encryptCipher = Cipher.getInstance(this.algorithm); + this.decryptCipher = Cipher.getInstance(this.algorithm); + + } + + } catch (EncryptionInitializationException e) { + throw e; + } catch (Throwable t) { + throw new EncryptionInitializationException(t); + } + + + // The salt size and the IV size for the chosen algorithm are set to be equal + // to the algorithm's block size (if it is a block algorithm). + final int algorithmBlockSize = this.encryptCipher.getBlockSize(); + if (algorithmBlockSize > 0) { + this.saltSizeBytes = algorithmBlockSize; + this.ivSizeBytes = algorithmBlockSize; + } + + + this.optimizingDueFixedSalt = (this.saltGenerator instanceof FixedSaltGenerator) + && (this.ivGenerator instanceof NoIvGenerator); + + if (this.optimizingDueFixedSalt) { + + // Create salt + this.fixedSaltInUse = + this.saltGenerator.generateSalt(this.saltSizeBytes); + + /* + * Initialize the Cipher objects themselves. Due to the fact that + * we will be using a fixed salt, this can be done just once, which + * means a better performance at the encrypt/decrypt methods. + */ + + final PBEParameterSpec parameterSpec = + new PBEParameterSpec(this.fixedSaltInUse, this.keyObtentionIterations); + + try { + + this.encryptCipher.init( + Cipher.ENCRYPT_MODE, this.key, parameterSpec); + this.decryptCipher.init( + Cipher.DECRYPT_MODE, this.key, parameterSpec); + + } catch (final Exception e) { + // If encryption fails, it is more secure not to return any + // information about the cause in nested exceptions. Simply fail. + throw new EncryptionOperationNotPossibleException(); + } + + + } + + + this.initialized = true; + + } + + } + + + + private synchronized void resolveConfigurationPassword() { + + // Double-check to avoid synchronization issues + if (!this.initialized) { + + if (this.config != null && !this.passwordSet) { + + // Get the configured password. If the config object implements + // CleanablePassword, we get password directly as a char array + // in order to avoid unnecessary creation of immutable Strings + // containing such password. + char[] configPassword = null; + if (this.config instanceof PBECleanablePasswordConfig) { + configPassword = ((PBECleanablePasswordConfig)this.config).getPasswordCharArray(); + } else { + final String configPwd = this.config.getPassword(); + if (configPwd != null) { + configPassword = configPwd.toCharArray(); + } + } + + if (configPassword != null) { + CommonUtils.validateIsTrue(configPassword.length > 0, + "Password cannot be set empty"); + } + + if (configPassword != null) { + this.password = new char[configPassword.length]; + System.arraycopy(configPassword, 0, this.password, 0, configPassword.length); + this.passwordSet = true; + cleanPassword(configPassword); + } + + // Finally, clean the password at the configuration object + if (this.config instanceof PBECleanablePasswordConfig) { + ((PBECleanablePasswordConfig)this.config).cleanPassword(); + } + + + } + + } + + } + + + + private static void cleanPassword(final char[] password) { + if (password != null) { + synchronized (password) { + final int pwdLength = password.length; + for (int i = 0; i < pwdLength; i++) { + password[i] = (char)0; + } + } + } + } + + + + /** + *

+ * Encrypts a message using the specified configuration. + *

+ *

+ * The mechanisms applied to perform the encryption operation are described + * in PKCS #5: Password-Based Cryptography Standard. + *

+ *

+ * This encryptor uses a salt and IV for each encryption + * operation. Sizes of the salt and IV depends on the algorithm + * being used. The salt and the IV, if generated by a random generator, + * they are also appended unencrypted at the beginning + * of the results so that a decryption operation can be performed. + *

+ *

+ * If a random salt generator is used, two encryption results for + * the same message will always be different + * (except in the case of random salt coincidence). This may enforce + * security by difficulting brute force attacks on sets of data at a time + * and forcing attackers to perform a brute force attack on each separate + * piece of encrypted data. + * The same is applied if a random IV generator is used. + *

+ * + * @param message the byte array message to be encrypted + * @return the result of encryption + * @throws EncryptionOperationNotPossibleException if the encryption + * operation fails, ommitting any further information about the + * cause for security reasons. + * @throws EncryptionInitializationException if initialization could not + * be correctly done (for example, no password has been set). + */ + public byte[] encrypt(final byte[] message) + throws EncryptionOperationNotPossibleException { + + if (message == null) { + return null; + } + + // Check initialization + if (!isInitialized()) { + initialize(); + } + + try { + + final byte[] salt; + byte[] iv = null; + byte[] encryptedMessage; + if (this.optimizingDueFixedSalt) { + + salt = this.fixedSaltInUse; + + synchronized (this.encryptCipher) { + encryptedMessage = this.encryptCipher.doFinal(message); + } + + } else { + + // Create salt + salt = this.saltGenerator.generateSalt(this.saltSizeBytes); + + // Create IV + iv = this.ivGenerator.generateIv(this.ivSizeBytes); + + + /* + * Perform encryption using the Cipher + */ + final PBEParameterSpec parameterSpec = buildPBEParameterSpec(salt, iv); + + synchronized (this.encryptCipher) { + this.encryptCipher.init( + Cipher.ENCRYPT_MODE, this.key, parameterSpec); + encryptedMessage = this.encryptCipher.doFinal(message); + } + + } + + // We build an array containing both the unencrypted IV + // and the result of the encryption. This is done only + // if the IV generator we are using specifies to do so. + if (this.ivGenerator.includePlainIvInEncryptionResults()) { + + // Insert plain IV before the encryption result + encryptedMessage = CommonUtils.appendArrays(iv, encryptedMessage); + + } + + // Finally we build an array containing both the unencrypted salt + // and the result of the encryption. This is done only + // if the salt generator we are using specifies to do so. + if (this.saltGenerator.includePlainSaltInEncryptionResults()) { + + // Insert unhashed salt before the encryption result + encryptedMessage = CommonUtils.appendArrays(salt, encryptedMessage); + + } + + + return encryptedMessage; + + } catch (final InvalidKeyException e) { + // The problem could be not having the unlimited strength policies + // installed, so better give a usefull error message. + handleInvalidKeyException(e); + throw new EncryptionOperationNotPossibleException(); + } catch (final Exception e) { + // If encryption fails, it is more secure not to return any + // information about the cause in nested exceptions. Simply fail. + e.printStackTrace(); + throw new EncryptionOperationNotPossibleException(); + } + + } + + + /** + *

+ * Decrypts a message using the specified configuration. + *

+ *

+ * The mechanisms applied to perform the decryption operation are described + * in PKCS #5: Password-Based Cryptography Standard. + *

+ *

+ * If a random salt generator is used, this decryption operation will + * expect to find an unencrypted salt at the + * beginning of the encrypted input, so that the decryption operation can be + * correctly performed (there is no other way of knowing it). + *

+ *

+ * If a random IV generator is used, this decryption operation will + * expect to find an unencrypted IV at the + * beginning of the encrypted input, so that the decryption operation can be + * correctly performed (there is no other way of knowing it). + *

+ * + * @param encryptedMessage the byte array message to be decrypted + * @return the result of decryption + * @throws EncryptionOperationNotPossibleException if the decryption + * operation fails, ommitting any further information about the + * cause for security reasons. + * @throws EncryptionInitializationException if initialization could not + * be correctly done (for example, no password has been set). + */ + public byte[] decrypt(final byte[] encryptedMessage) + throws EncryptionOperationNotPossibleException { + + if (encryptedMessage == null) { + return null; + } + + // Check initialization + if (!isInitialized()) { + initialize(); + } + + + if (this.saltGenerator.includePlainSaltInEncryptionResults() + && this.ivGenerator.includePlainIvInEncryptionResults()) { + // Check that the received message is bigger than the salt + IV + if (encryptedMessage.length <= this.saltSizeBytes + this.ivSizeBytes) { + throw new EncryptionOperationNotPossibleException(); + } + } else if (this.saltGenerator.includePlainSaltInEncryptionResults()) { + // Check that the received message is bigger than the salt + if (encryptedMessage.length <= this.saltSizeBytes) { + throw new EncryptionOperationNotPossibleException(); + } + } else if (this.ivGenerator.includePlainIvInEncryptionResults()) { + // Check that the received message is bigger than the IV + if (encryptedMessage.length <= this.ivSizeBytes) { + throw new EncryptionOperationNotPossibleException(); + } + } + + + try { + + // If we are using a salt generator which specifies the salt + // to be included into the encrypted message itself, get it from + // there. If not, the salt is supposed to be fixed and thus the + // salt generator can be safely asked for it again. + byte[] salt = null; + byte[] encryptedMessageKernel = null; + if (this.saltGenerator.includePlainSaltInEncryptionResults()) { + + final int saltStart = 0; + final int saltSize = + (this.saltSizeBytes < encryptedMessage.length? this.saltSizeBytes : encryptedMessage.length); + final int encMesKernelStart = + (this.saltSizeBytes < encryptedMessage.length? this.saltSizeBytes : encryptedMessage.length); + final int encMesKernelSize = + (this.saltSizeBytes < encryptedMessage.length? (encryptedMessage.length - this.saltSizeBytes) : 0); + + salt = new byte[saltSize]; + encryptedMessageKernel = new byte[encMesKernelSize]; + + System.arraycopy(encryptedMessage, saltStart, salt, 0, saltSize); + System.arraycopy(encryptedMessage, encMesKernelStart, encryptedMessageKernel, 0, encMesKernelSize); + + } else if (!this.optimizingDueFixedSalt){ + + salt = this.saltGenerator.generateSalt(this.saltSizeBytes); + encryptedMessageKernel = encryptedMessage; + + } else { + // this.optimizingDueFixedSalt == true + + salt = this.fixedSaltInUse; + encryptedMessageKernel = encryptedMessage; + + } + + byte[] iv = null; + byte[] finalEncryptedMessageKernel = null; + if (this.ivGenerator.includePlainIvInEncryptionResults()) { + + final int ivStart = 0; + final int ivSize = + (this.ivSizeBytes < encryptedMessageKernel.length? this.ivSizeBytes : encryptedMessageKernel.length); + final int encMesKernelStart = + (this.ivSizeBytes < encryptedMessageKernel.length? this.ivSizeBytes : encryptedMessageKernel.length); + final int encMesKernelSize = + (this.ivSizeBytes < encryptedMessageKernel.length? (encryptedMessageKernel.length - this.ivSizeBytes) : 0); + + iv = new byte[ivSize]; + finalEncryptedMessageKernel = new byte[encMesKernelSize]; + + System.arraycopy(encryptedMessageKernel, ivStart, iv, 0, ivSize); + System.arraycopy(encryptedMessageKernel, encMesKernelStart, finalEncryptedMessageKernel, 0, encMesKernelSize); + + } else { + iv = ivGenerator.generateIv(ivSizeBytes); + finalEncryptedMessageKernel = encryptedMessageKernel; + + } + + + final byte[] decryptedMessage; + if (this.optimizingDueFixedSalt) { + + /* + * Fixed salt is being used, therefore no initialization supposedly needed + */ + synchronized (this.decryptCipher) { + decryptedMessage = + this.decryptCipher.doFinal(finalEncryptedMessageKernel); + } + + } else { + + /* + * Perform decryption using the Cipher + */ + final PBEParameterSpec parameterSpec = buildPBEParameterSpec(salt, iv); + + synchronized (this.decryptCipher) { + this.decryptCipher.init( + Cipher.DECRYPT_MODE, this.key, parameterSpec); + decryptedMessage = + this.decryptCipher.doFinal(finalEncryptedMessageKernel); + } + + } + + // Return the results + return decryptedMessage; + + } catch (final InvalidKeyException e) { + // The problem could be not having the unlimited strength policies + // installed, so better give a usefull error message. + handleInvalidKeyException(e); + throw new EncryptionOperationNotPossibleException(); + } catch (final Exception e) { + // If decryption fails, it is more secure not to return any + // information about the cause in nested exceptions. Simply fail. + throw new EncryptionOperationNotPossibleException(); + } + + } + + + private PBEParameterSpec buildPBEParameterSpec(final byte[] salt, final byte[] iv) { + + PBEParameterSpec parameterSpec; + + try { + Class[] parameters = {byte[].class, int.class, AlgorithmParameterSpec.class}; + Constructor java8Constructor = PBEParameterSpec.class.getConstructor(parameters); + + Object[] parameterValues = {salt, this.keyObtentionIterations, new IvParameterSpec(iv)}; + + parameterSpec = java8Constructor.newInstance(parameterValues); + + } catch (Exception e) { + parameterSpec = new PBEParameterSpec(salt, this.keyObtentionIterations); + } + + return parameterSpec; + + } + + /* + * Method used to provide an useful error message in the case that the + * user tried to use a strong PBE algorithm like TripleDES and he/she + * has not installed the Unlimited Strength Policy files (the default + * message for this is simply "invalid key size", which does not provide + * enough clues for the user to know what is really going on). + */ + private void handleInvalidKeyException(final InvalidKeyException e) { + + if ((e.getMessage() != null) && + ((e.getMessage().toUpperCase().indexOf("KEY SIZE") != -1))) { + + throw new EncryptionOperationNotPossibleException( + "Encryption raised an exception. A possible cause is " + + "you are using strong encryption algorithms and " + + "you have not installed the Java Cryptography " + + "Extension (JCE) Unlimited Strength Jurisdiction " + + "Policy Files in this Java Virtual Machine"); + + } + + } + +} + diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEStringEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEStringEncryptor.java new file mode 100644 index 0000000000..4ba8a4c3d4 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEStringEncryptor.java @@ -0,0 +1,750 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + +import java.security.Provider; + + + +/** + *

+ * Standard implementation of the {@link PBEStringEncryptor} interface. + * This class lets the user specify the algorithm (and provider) to be used for + * encryption, the password to use, + * the number of hashing iterations and the salt generator + * that will be applied for obtaining + * the encryption key. + *

+ *

+ * This class avoids byte-conversion problems related to the fact of + * different platforms having different default charsets, and returns + * encryption results in the form of BASE64-encoded or HEXADECIMAL + * ASCII Strings. + *

+ *

+ * This class is thread-safe. + *

+ *

+ *
Configuration + *

+ *

+ * The algorithm, provider, password, key-obtention iterations and salt generator can take + * values in any of these ways: + *

    + *
  • Using its default values (except for password).
  • + *
  • Setting a {@link PBEConfig} + * object which provides new + * configuration values.
  • + *
  • Calling the corresponding setX(...) methods.
  • + *
+ * And the actual values to be used for initialization will be established + * by applying the following priorities: + *
    + *
  1. First, the default values are considered (except for password).
  2. + *
  3. Then, if a {@link PBEConfig} + * object has been set with + * setConfig(...), the non-null values returned by its + * getX() methods override the default values.
  4. + *
  5. Finally, if the corresponding setX(...) method has been called + * on the encryptor itself for any of the configuration parameters, the + * values set by these calls override all of the above.
  6. + *
+ *

+ * + *

+ *
Initialization + *

+ *

+ * Before it is ready to encrypt, an object of this class has to be + * initialized. Initialization happens: + *

    + *
  • When initialize() is called.
  • + *
  • When encrypt(...) or decrypt(...) are called for the + * first time, if initialize() has not been called before.
  • + *
+ * Once an encryptor has been initialized, trying to + * change its configuration will + * result in an AlreadyInitializedException being thrown. + *

+ * + *

+ *
Usage + *

+ *

+ * An encryptor may be used for: + *

    + *
  • Encrypting messages, by calling the encrypt(...) method.
  • + *
  • Decrypting messages, by calling the decrypt(...) method.
  • + *
+ * If a random salt generator is used, two encryption results for + * the same message will always be different + * (except in the case of random salt coincidence). This may enforce + * security by difficulting brute force attacks on sets of data at a time + * and forcing attackers to perform a brute force attack on each separate + * piece of encrypted data. + *

+ *

+ * To learn more about the mechanisms involved in encryption, read + * PKCS #5: Password-Based Cryptography Standard. + *

+ * + * @since 1.0 + * + * @author Daniel Fernández + * + */ +public final class StandardPBEStringEncryptor + implements PBEStringCleanablePasswordEncryptor { + + /** + *

+ * Charset to be used to obtain "encryptable" byte arrays from input + * Strings. Set to UTF-8. + *

+ *

+ * This charset has to be fixed to some value so that we avoid problems + * with different platforms having different "default" charsets. + *

+ *

+ * It is set to UTF-8 because it covers the whole spectrum of + * characters representable in Java (which internally uses UTF-16), and + * avoids the size penalty of UTF-16 (which will always use two bytes for + * representing each character, even if it is an ASCII one). + *

+ *

+ * Setting it to UTF-8 does not mean that Strings that originally come, + * for example, from an ISO-8859-1 input, won't be correctly encoded, as we + * only need to use the same charset both when encoding and decoding. That + * way the same String will be reconstructed independently of the original + * encoding (for encrypting, we only need "a byte representation" of the + * string, not "a readable byte representation"). + *

+ */ + private static final String MESSAGE_CHARSET = "UTF-8"; + + /** + *

+ * Charset to be used for encoding the encryption results. + * Set to US-ASCII. + *

+ *

+ * The result of encrypting some bytes can be any other bytes, and so + * the result of encrypting, for example, some LATIN-1 valid String bytes, + * can be bytes that may not conform a "valid" LATIN-1 String. + *

+ *

+ * Because of this, encryption results are always encoded in BASE64 + * (default) or HEXADECIMAL after being created, and this ensures + * that the results will make perfectly representable, safe ASCII Strings. + * Because of this, the charset used to convert the encrypted bytes to the + * returned String is set to US-ASCII. + *

+ */ + private static final String ENCRYPTED_MESSAGE_CHARSET = "US-ASCII"; + + + /** + *

+ * Default type of String output. Set to BASE64. + *

+ */ + public static final String DEFAULT_STRING_OUTPUT_TYPE = + CommonUtils.STRING_OUTPUT_TYPE_BASE64; + + + // If the config object set is a StringPBEConfig, it must be referenced + private StringPBEConfig stringPBEConfig = null; + + // This variable holds the type of String output which will be done, + // and also a boolean variable for faster comparison + private String stringOutputType = DEFAULT_STRING_OUTPUT_TYPE; + private boolean stringOutputTypeBase64 = true; + + + /* + * Set of booleans which indicate whether the config or default values + * have to be overriden because of the setX methods having been + * called. + */ + private boolean stringOutputTypeSet = false; + + + // The StandardPBEByteEncryptor that will be internally used. + private final StandardPBEByteEncryptor byteEncryptor; + + // BASE64 encoder which will make sure the returned results are + // valid US-ASCII strings. + // The Base64 encoder is THREAD-SAFE + private final Base64 base64; + + + + /** + * Creates a new instance of StandardPBEStringEncryptor. + */ + public StandardPBEStringEncryptor() { + super(); + this.byteEncryptor = new StandardPBEByteEncryptor(); + this.base64 = new Base64(); + } + + + + /* + * Creates a new instance of StandardPBEStringEncryptor using + * the specified byte encryptor (constructor used for cloning) + */ + private StandardPBEStringEncryptor(final StandardPBEByteEncryptor standardPBEByteEncryptor) { + super(); + this.byteEncryptor = standardPBEByteEncryptor; + this.base64 = new Base64(); + } + + + + /** + *

+ * Sets a {@link PBEConfig} object + * for the encryptor. If this config + * object is set, it will be asked values for: + *

+ * + *
    + *
  • Algorithm
  • + *
  • Security Provider (or provider name)
  • + *
  • Password
  • + *
  • Hashing iterations for obtaining the encryption key
  • + *
  • Salt generator
  • + *
  • Output type (base64, hexadecimal) + * (only StringPBEConfig)
  • + *
+ * + *

+ * The non-null values it returns will override the default ones, + * and will be overriden by any values specified with a setX + * method. + *

+ * + * @param config the PBEConfig object to be used as the + * source for configuration parameters. + */ + public synchronized void setConfig(final PBEConfig config) { + this.byteEncryptor.setConfig(config); + if ((config != null) && (config instanceof StringPBEConfig)) { + this.stringPBEConfig = (StringPBEConfig) config; + } + } + + + /** + *

+ * Sets the algorithm to be used for encryption, like + * PBEWithMD5AndDES. + *

+ *

+ * This algorithm has to be supported by your JCE provider (if you specify + * one, or the default JVM provider if you don't) and, if it is supported, + * you can also specify mode and padding for + * it, like ALGORITHM/MODE/PADDING. + *

+ * + * @param algorithm the name of the algorithm to be used. + */ + public void setAlgorithm(final String algorithm) { + this.byteEncryptor.setAlgorithm(algorithm); + } + + + /** + *

+ * Sets the password to be used. + *

+ *

+ * There is no default value for password, so not setting + * this parameter either from a + * {@link PBEConfig} object or from + * a call to setPassword will result in an + * EncryptionInitializationException being thrown during initialization. + *

+ * + * @param password the password to be used. + */ + public void setPassword(final String password) { + this.byteEncryptor.setPassword(password); + } + + + /** + *

+ * Sets the password to be used, as a char[]. + *

+ *

+ * This allows the password to be specified as a cleanable + * char[] instead of a String, in extreme security conscious environments + * in which no copy of the password as an immutable String should + * be kept in memory. + *

+ *

+ * Important: the array specified as a parameter WILL BE COPIED + * in order to be stored as encryptor configuration. The caller of + * this method will therefore be responsible for its cleaning (jasypt + * will only clean the internally stored copy). + *

+ *

+ * There is no default value for password, so not setting + * this parameter either from a + * {@link PBEConfig} object or from + * a call to setPassword will result in an + * EncryptionInitializationException being thrown during initialization. + *

+ * + * @since 1.8 + * + * @param password the password to be used. + */ + public void setPasswordCharArray(char[] password) { + this.byteEncryptor.setPasswordCharArray(password); + } + + + /** + *

+ * Set the number of hashing iterations applied to obtain the + * encryption key. + *

+ *

+ * This mechanism is explained in + * PKCS #5: Password-Based Cryptography Standard. + *

+ * + * @param keyObtentionIterations the number of iterations + */ + public void setKeyObtentionIterations(final int keyObtentionIterations) { + this.byteEncryptor.setKeyObtentionIterations(keyObtentionIterations); + } + + + /** + *

+ * Sets the salt generator to be used. If no salt generator is specified, + * an instance of {@link org.jasypt.salt.RandomSaltGenerator} will be used. + *

+ * + * @param saltGenerator the salt generator to be used. + */ + public void setSaltGenerator(final SaltGenerator saltGenerator) { + this.byteEncryptor.setSaltGenerator(saltGenerator); + } + + /** + *

+ * Sets the IV generator to be used. If no IV generator is specified, + * an instance of {@link org.jasypt.iv.NoIvGenerator} will be used. + *

+ * + * @param ivGenerator the IV generator to be used. + */ + public void setIvGenerator(final IvGenerator ivGenerator) { + this.byteEncryptor.setIvGenerator(ivGenerator); + } + + + /** + *

+ * Sets the name of the security provider to be asked for the + * encryption algorithm. This security provider has to be registered + * beforehand at the JVM security framework. + *

+ *

+ * The provider can also be set with the {@link #setProvider(Provider)} + * method, in which case it will not be necessary neither registering + * the provider beforehand, + * nor calling this {@link #setProviderName(String)} method to specify + * a provider name. + *

+ *

+ * Note that a call to {@link #setProvider(Provider)} overrides any value + * set by this method. + *

+ *

+ * If no provider name / provider is explicitly set, the default JVM + * provider will be used. + *

+ * + * @since 1.3 + * + * @param providerName the name of the security provider to be asked + * for the encryption algorithm. + */ + public void setProviderName(final String providerName) { + this.byteEncryptor.setProviderName(providerName); + } + + + /** + *

+ * Sets the security provider to be asked for the encryption algorithm. + * The provider does not have to be registered at the security + * infrastructure beforehand, and its being used here will not result in + * its being registered. + *

+ *

+ * If this method is called, calling {@link #setProviderName(String)} + * becomes unnecessary. + *

+ *

+ * If no provider name / provider is explicitly set, the default JVM + * provider will be used. + *

+ * + * @since 1.3 + * + * @param provider the provider to be asked for the chosen algorithm + */ + public void setProvider(final Provider provider) { + this.byteEncryptor.setProvider(provider); + } + + + /** + *

+ * Sets the the form in which String output + * will be encoded. Available encoding types are: + *

+ *
    + *
  • base64 (default)
  • + *
  • hexadecimal
  • + *
+ *

+ * If not set, null will be returned. + *

+ + * @since 1.3 + * + * @param stringOutputType the string output type. + */ + public synchronized void setStringOutputType(final String stringOutputType) { + CommonUtils.validateNotEmpty(stringOutputType, + "String output type cannot be set empty"); + if (isInitialized()) { + throw new AlreadyInitializedException(); + } + this.stringOutputType = + CommonUtils. + getStandardStringOutputType(stringOutputType); + + this.stringOutputTypeSet = true; + } + + + + + + + + + + + /* + * Clone this encryptor 'size' times and initialize it. + * This encryptor will be at position 0 itself. + * Clones will NOT be initialized. + */ + synchronized StandardPBEStringEncryptor[] cloneAndInitializeEncryptor(final int size) { + + final StandardPBEByteEncryptor[] byteEncryptorClones = + this.byteEncryptor.cloneAndInitializeEncryptor(size); + + initializeSpecifics(); + + final StandardPBEStringEncryptor[] clones = new StandardPBEStringEncryptor[size]; + + clones[0] = this; + + for (int i = 1; i < size; i++) { + clones[i] = new StandardPBEStringEncryptor(byteEncryptorClones[i]); + if (CommonUtils.isNotEmpty(this.stringOutputType)) { + clones[i].setStringOutputType(this.stringOutputType); + } + } + + return clones; + + } + + + + + /** + *

+ * Returns true if the encryptor has already been initialized, false if + * not.
+ * Initialization happens: + *

+ *
    + *
  • When initialize is called.
  • + *
  • When encrypt or decrypt are called for the + * first time, if initialize has not been called before.
  • + *
+ *

+ * Once an encryptor has been initialized, trying to + * change its configuration will + * result in an AlreadyInitializedException being thrown. + *

+ * + * @return true if the encryptor has already been initialized, false if + * not. + */ + public boolean isInitialized() { + return this.byteEncryptor.isInitialized(); + } + + + /** + *

+ * Initialize the encryptor. + *

+ *

+ * This operation will consist in determining the actual configuration + * values to be used, and then initializing the encryptor with them. + *
+ * These values are decided by applying the following priorities: + *

+ *
    + *
  1. First, the default values are considered (except for password). + *
  2. + *
  3. Then, if a + * {@link PBEConfig} + * object has been set with + * setConfig, the non-null values returned by its + * getX methods override the default values.
  4. + *
  5. Finally, if the corresponding setX method has been called + * on the encryptor itself for any of the configuration parameters, + * the values set by these calls override all of the above.
  6. + *
+ *

+ * Once an encryptor has been initialized, trying to + * change its configuration will + * result in an AlreadyInitializedException being thrown. + *

+ * + * @throws EncryptionInitializationException if initialization could not + * be correctly done (for example, no password has been set). + */ + public synchronized void initialize() { + + // Double-check to avoid synchronization issues + if (!this.isInitialized()) { + initializeSpecifics(); + this.byteEncryptor.initialize(); + } + + } + + + + + private void initializeSpecifics() { + /* + * If a StringPBEConfig object has been set, we need to + * consider the values it returns (if, for each value, the + * corresponding "setX" method has not been called). + */ + if (this.stringPBEConfig != null) { + + final String configStringOutputType = + this.stringPBEConfig.getStringOutputType(); + + this.stringOutputType = + ((this.stringOutputTypeSet) || (configStringOutputType == null))? + this.stringOutputType : configStringOutputType; + + } + + this.stringOutputTypeBase64 = + (CommonUtils.STRING_OUTPUT_TYPE_BASE64. + equalsIgnoreCase(this.stringOutputType)); + + } + + + /** + *

+ * Encrypts a message using the specified configuration. + *

+ *

+ * The Strings returned by this method are BASE64-encoded (default) or + * HEXADECIMAL ASCII Strings. + *

+ *

+ * The mechanisms applied to perform the encryption operation are described + * in PKCS #5: Password-Based Cryptography Standard. + *

+ *

+ * This encryptor uses a salt for each encryption + * operation. The size of the salt depends on the algorithm + * being used. This salt is used + * for creating the encryption key and, if generated by a random generator, + * it is also appended unencrypted at the beginning + * of the results so that a decryption operation can be performed. + *

+ *

+ * If a random salt generator is used, two encryption results for + * the same message will always be different + * (except in the case of random salt coincidence). This may enforce + * security by difficulting brute force attacks on sets of data at a time + * and forcing attackers to perform a brute force attack on each separate + * piece of encrypted data. + *

+ * + * @param message the String message to be encrypted + * @return the result of encryption + * @throws EncryptionOperationNotPossibleException if the encryption + * operation fails, ommitting any further information about the + * cause for security reasons. + * @throws EncryptionInitializationException if initialization could not + * be correctly done (for example, no password has been set). + */ + public String encrypt(final String message) { + + if (message == null) { + return null; + } + + // Check initialization + if (!isInitialized()) { + initialize(); + } + + try { + + // The input String is converted into bytes using MESSAGE_CHARSET + // as a fixed charset to avoid problems with different platforms + // having different default charsets (see MESSAGE_CHARSET doc). + final byte[] messageBytes = message.getBytes(MESSAGE_CHARSET); + + // The StandardPBEByteEncryptor does its job. + byte[] encryptedMessage = this.byteEncryptor.encrypt(messageBytes); + + // We encode the result in BASE64 or HEXADECIMAL so that we obtain + // the safest result String possible. + String result = null; + if (this.stringOutputTypeBase64) { + encryptedMessage = this.base64.encode(encryptedMessage); + result = new String(encryptedMessage,ENCRYPTED_MESSAGE_CHARSET); + } else { + result = CommonUtils.toHexadecimal(encryptedMessage); + } + + return result; + + } catch (EncryptionInitializationException e) { + throw e; + } catch (EncryptionOperationNotPossibleException e) { + throw e; + } catch (Exception e) { + // If encryption fails, it is more secure not to return any + // information about the cause in nested exceptions. Simply fail. + throw new EncryptionOperationNotPossibleException(); + } + + } + + + /** + *

+ * Decrypts a message using the specified configuration. + *

+ *

+ * This method expects to receive a BASE64-encoded (default) + * or HEXADECIMAL ASCII String. + *

+ *

+ * The mechanisms applied to perform the decryption operation are described + * in PKCS #5: Password-Based Cryptography Standard. + *

+ *

+ * If a random salt generator is used, this decryption operation will + * expect to find an unencrypted salt at the + * beginning of the encrypted input, so that the decryption operation can be + * correctly performed (there is no other way of knowing it). + *

+ * + * @param encryptedMessage the String message to be decrypted + * @return the result of decryption + * @throws EncryptionOperationNotPossibleException if the decryption + * operation fails, ommitting any further information about the + * cause for security reasons. + * @throws EncryptionInitializationException if initialization could not + * be correctly done (for example, no password has been set). + */ + public String decrypt(final String encryptedMessage) { + + if (encryptedMessage == null) { + return null; + } + + // Check initialization + if (!isInitialized()) { + initialize(); + } + + try { + + byte[] encryptedMessageBytes = null; + + // Decode input to bytes depending on whether it is a + // BASE64-encoded or hexadecimal String + if (this.stringOutputTypeBase64) { + encryptedMessageBytes = + encryptedMessage.getBytes(ENCRYPTED_MESSAGE_CHARSET); + encryptedMessageBytes = + this.base64.decode(encryptedMessageBytes); + } else { + encryptedMessageBytes = + CommonUtils.fromHexadecimal(encryptedMessage); + } + + // Let the byte encyptor decrypt + final byte[] message = this.byteEncryptor.decrypt(encryptedMessageBytes); + + // Return the resulting decrypted String, using MESSAGE_CHARSET + // as charset to maintain between encryption and decyption + // processes. + return new String(message, MESSAGE_CHARSET); + + } catch (EncryptionInitializationException e) { + throw e; + } catch (EncryptionOperationNotPossibleException e) { + throw e; + } catch (Exception e) { + // If decryption fails, it is more secure not to return any + // information about the cause in nested exceptions. Simply fail. + throw new EncryptionOperationNotPossibleException(); + } + + } + + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringEncryptor.java new file mode 100644 index 0000000000..218be61a6f --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringEncryptor.java @@ -0,0 +1,55 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + + +/** + *

+ * Common interface for all Encryptors which receive a + * String message and return a String result. + *

+ * + * @since 1.0 + * + * @author Daniel Fernández + * + */ +public interface StringEncryptor +{ + + + /** + * Encrypt the input message + * + * @param message the message to be encrypted + * @return the result of encryption + */ + public String encrypt(String message); + + + /** + * Decrypt an encrypted message + * + * @param encryptedMessage the encrypted message to be decrypted + * @return the result of decryption + */ + public String decrypt(String encryptedMessage); + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringPBEConfig.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringPBEConfig.java new file mode 100644 index 0000000000..1d48951745 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringPBEConfig.java @@ -0,0 +1,71 @@ +/* + * ============================================================================= + * + * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * ============================================================================= + */ +package org.bouncycastle.jcajce.provider.test.jasypt; + + + +/** + *

+ * Common interface for config classes applicable to + * StandardPBEStringEncrypto} objects. + * This interface extends {@link PBEConfig} to add config parameters specific + * to String encryption. + *

+ *

+ * This interface lets the user create new PBEConfig + * classes which retrieve values for this parameters from different + * (and maybe more secure) sources (remote servers, LDAP, other databases...), + * and do this transparently for the encryptor object. + *

+ *

+ * The config objects passed to an encryptor will only be queried once + * for each configuration parameter, and this will happen + * during the initialization of the encryptor object. + *

+ *

+ * For a default implementation, see SimpleStringPBEConfig. + *

+ * + * @since 1.3 + * + * @author Daniel Fernández + * + */ +public interface StringPBEConfig + extends PBEConfig { + + + + /** + *

+ * This parameter lets the user specify the form in which String output + * will be encoded. Available encoding types are: + *

+ *
    + *
  • base64 (default)
  • + *
  • hexadecimal
  • + *
+ * + * @return The name of the encoding type for String output + */ + public String getStringOutputType(); + + +} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/TestJasypt.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/TestJasypt.java new file mode 100644 index 0000000000..222c67827c --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/TestJasypt.java @@ -0,0 +1,18 @@ +package org.bouncycastle.jcajce.provider.test.jasypt; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +public class TestJasypt +{ + public static void main(String[] args) + { + StandardPBEStringEncryptor stringEncryptor = new StandardPBEStringEncryptor(); + stringEncryptor.setAlgorithm("PBEWITHSHA256AND256BITAES-CBC-BC"); + stringEncryptor.setPassword("secretPassword"); + stringEncryptor.setIvGenerator(new RandomIvGenerator()); + stringEncryptor.setProvider(new BouncyCastleProvider()); + + String encryptedText = stringEncryptor.encrypt("plainText"); + + } +} From 7cf6f5b5019917d863d009fdc5c80e59a401eff3 Mon Sep 17 00:00:00 2001 From: gefeili Date: Sat, 8 Feb 2025 20:58:49 +1030 Subject: [PATCH 102/890] Test the code of Jasypt --- .../jcajce/provider/symmetric/util/BaseBlockCipher.java | 8 ++++++-- .../jcajce/provider/test/jasypt/TestJasypt.java | 9 +++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java index c8ef9f740f..9dc8c0e224 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java @@ -448,7 +448,7 @@ else if (modeName.startsWith("PGPCFB")) { throw new NoSuchAlgorithmException("no mode support for " + modeName); } - + ivLength = baseEngine.getBlockSize(); cipher = new BufferedGenericBlockCipher( new PGPCFBBlockCipher(baseEngine, inlineIV)); @@ -634,7 +634,7 @@ else if (paddingName.equals("TBCPADDING")) protected void engineInit( int opmode, Key key, - AlgorithmParameterSpec params, + AlgorithmParameterSpec params, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { @@ -825,6 +825,10 @@ else if (!(key instanceof RepeatedSecretKeySpec)) // if (params instanceof PBEParameterSpec) // { // params = ((PBEParameterSpec)params).getParameterSpec(); +// if (((IvParameterSpec)params).getIV().length == 0) +// { +// params = new IvParameterSpec(((ParametersWithIV)param).getIV()); +// } // } // else // { diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/TestJasypt.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/TestJasypt.java index 222c67827c..bc36a3fb93 100644 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/TestJasypt.java +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/TestJasypt.java @@ -14,5 +14,14 @@ public static void main(String[] args) String encryptedText = stringEncryptor.encrypt("plainText"); + StandardPBEStringEncryptor stringdecryptor = new StandardPBEStringEncryptor(); + stringdecryptor.setAlgorithm("PBEWITHSHA256AND256BITAES-CBC-BC"); + stringdecryptor.setPassword("secretPassword"); + stringdecryptor.setIvGenerator(new RandomIvGenerator()); + stringdecryptor.setProvider(new BouncyCastleProvider()); + + String decryptedText = stringdecryptor.decrypt(encryptedText); + System.out.println(decryptedText); + } } From 57bcec305c24b88b243bc9142a9bcbd5c93f574d Mon Sep 17 00:00:00 2001 From: gefeili Date: Sun, 9 Feb 2025 00:08:07 +1030 Subject: [PATCH 103/890] Refactor in permutation --- .../crypto/engines/AsconPermutationFriend.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java index 8edef36a22..95bce04e37 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java @@ -28,11 +28,19 @@ public static class AsconPermutation public void round(long C) { - long t0 = x0 ^ x1 ^ x2 ^ x3 ^ C ^ (x1 & (x0 ^ x2 ^ x4 ^ C)); - long t1 = x0 ^ x2 ^ x3 ^ x4 ^ C ^ ((x1 ^ x2 ^ C) & (x1 ^ x3)); - long t2 = x1 ^ x2 ^ x4 ^ C ^ (x3 & x4); - long t3 = x0 ^ x1 ^ x2 ^ C ^ ((~x0) & (x3 ^ x4)); - long t4 = x1 ^ x3 ^ x4 ^ ((x0 ^ x4) & x1); + x2 ^= C; + long x0x4 = x0 ^ x4; + //long x0x2c = x0 ^ x2; + long x1x2c = x1 ^ x2; + + //long t0 = x0 ^ x1x2c ^ x3 ^ (x1 & (x0x4 ^ x2)); + long t0 = x0 ^ x2 ^ x3 ^ (x1 & ~(x0x4 ^ x2)); + long t1 = x0x4 ^ x2 ^ x3 ^ (x1x2c & (x1 ^ x3)); + //long t1 = x0x4 ^ x2 ^ x3 ^ (x1 & ~(x2 | x3)); + long t2 = x1x2c ^ (x4 & (~x3));//x4 ^ (x3 & x4); + //long t3 = x0 ^ x1x2c ^ ((~x0) & (x3 ^ x4)); + long t3 = (x0 | (x3 ^ x4)) ^ x1x2c; + long t4 = x1 ^ x3 ^ x4 ^ (x0x4 & x1); x0 = t0 ^ Longs.rotateRight(t0, 19) ^ Longs.rotateRight(t0, 28); x1 = t1 ^ Longs.rotateRight(t1, 39) ^ Longs.rotateRight(t1, 61); x2 = ~(t2 ^ Longs.rotateRight(t2, 1) ^ Longs.rotateRight(t2, 6)); From 1d372a6c3733cd6ab8e5c212fd4b1c06d109844d Mon Sep 17 00:00:00 2001 From: gefeili Date: Sun, 9 Feb 2025 12:52:52 +1030 Subject: [PATCH 104/890] Refactor in permutation --- .../crypto/engines/AsconPermutationFriend.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java index 95bce04e37..e0dd899d42 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java @@ -33,14 +33,14 @@ public void round(long C) //long x0x2c = x0 ^ x2; long x1x2c = x1 ^ x2; - //long t0 = x0 ^ x1x2c ^ x3 ^ (x1 & (x0x4 ^ x2)); - long t0 = x0 ^ x2 ^ x3 ^ (x1 & ~(x0x4 ^ x2)); - long t1 = x0x4 ^ x2 ^ x3 ^ (x1x2c & (x1 ^ x3)); - //long t1 = x0x4 ^ x2 ^ x3 ^ (x1 & ~(x2 | x3)); + long t0 = x3 ^ (x1 | x2) ^ x0 ^ (x1 & x0x4); + //long t1 = x0x4 ^ x2 ^ x3 ^ (x1x2c & (x1 ^ x3)); + long t1 = x0x4 ^ (x1 | x2 | x3) ^ (x1 & x2 & x3); long t2 = x1x2c ^ (x4 & (~x3));//x4 ^ (x3 & x4); //long t3 = x0 ^ x1x2c ^ ((~x0) & (x3 ^ x4)); long t3 = (x0 | (x3 ^ x4)) ^ x1x2c; - long t4 = x1 ^ x3 ^ x4 ^ (x0x4 & x1); + //long t4 = x1 ^ x3 ^ x4 ^ (x0x4 & x1); + long t4 = x3 ^ (x1 | x4) ^ (x0 & x1); x0 = t0 ^ Longs.rotateRight(t0, 19) ^ Longs.rotateRight(t0, 28); x1 = t1 ^ Longs.rotateRight(t1, 39) ^ Longs.rotateRight(t1, 61); x2 = ~(t2 ^ Longs.rotateRight(t2, 1) ^ Longs.rotateRight(t2, 6)); From 4127c2f05ee5e2b7669926ddfc2661d0a457c905 Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 10 Feb 2025 08:09:54 +1030 Subject: [PATCH 105/890] Refactor in permutation --- .../bouncycastle/crypto/engines/AsconPermutationFriend.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java index e0dd899d42..3d7b8d037e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconPermutationFriend.java @@ -32,10 +32,10 @@ public void round(long C) long x0x4 = x0 ^ x4; //long x0x2c = x0 ^ x2; long x1x2c = x1 ^ x2; - - long t0 = x3 ^ (x1 | x2) ^ x0 ^ (x1 & x0x4); + long x1orx2c = x1 | x2; + long t0 = x3 ^ x1orx2c ^ x0 ^ (x1 & x0x4); //long t1 = x0x4 ^ x2 ^ x3 ^ (x1x2c & (x1 ^ x3)); - long t1 = x0x4 ^ (x1 | x2 | x3) ^ (x1 & x2 & x3); + long t1 = x0x4 ^ (x1orx2c | x3) ^ (x1 & x2 & x3); long t2 = x1x2c ^ (x4 & (~x3));//x4 ^ (x3 & x4); //long t3 = x0 ^ x1x2c ^ ((~x0) & (x3 ^ x4)); long t3 = (x0 | (x3 ^ x4)) ^ x1x2c; From 66f21aaa43a6bfb2ca20dea0dea983dccf38a03d Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 10 Feb 2025 12:05:50 +1030 Subject: [PATCH 106/890] Fix the issue and add test for #1985 --- .../symmetric/util/BaseBlockCipher.java | 52 +- .../jasypt/AlreadyInitializedException.java | 42 - .../jcajce/provider/test/jasypt/Base64.java | 585 -------- .../provider/test/jasypt/ByteEncryptor.java | 53 - .../test/jasypt/CleanablePasswordBased.java | 48 - .../provider/test/jasypt/CommonUtils.java | 298 ---- .../test/jasypt/DecoderException.java | 19 - .../test/jasypt/EncoderException.java | 22 - .../EncryptionInitializationException.java | 52 - ...cryptionOperationNotPossibleException.java | 56 - .../test/jasypt/FixedSaltGenerator.java | 43 - .../provider/test/jasypt/IvGenerator.java | 73 - .../provider/test/jasypt/NoIvGenerator.java | 72 - .../provider/test/jasypt/Normalizer.java | 230 ---- .../PBEByteCleanablePasswordEncryptor.java | 44 - .../test/jasypt/PBEByteEncryptor.java | 42 - .../jasypt/PBECleanablePasswordConfig.java | 70 - .../provider/test/jasypt/PBEConfig.java | 208 --- .../PBEStringCleanablePasswordEncryptor.java | 43 - .../test/jasypt/PBEStringEncryptor.java | 42 - .../provider/test/jasypt/PasswordBased.java | 44 - .../test/jasypt/RandomIvGenerator.java | 105 -- .../test/jasypt/RandomSaltGenerator.java | 109 -- .../provider/test/jasypt/SaltGenerator.java | 73 - .../test/jasypt/StandardPBEByteEncryptor.java | 1209 ----------------- .../jasypt/StandardPBEStringEncryptor.java | 750 ---------- .../provider/test/jasypt/StringEncryptor.java | 55 - .../provider/test/jasypt/StringPBEConfig.java | 71 - .../provider/test/jasypt/TestJasypt.java | 27 - .../jce/provider/test/PBETest.java | 39 +- 30 files changed, 63 insertions(+), 4513 deletions(-) delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/AlreadyInitializedException.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Base64.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/ByteEncryptor.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CleanablePasswordBased.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CommonUtils.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/DecoderException.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncoderException.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionInitializationException.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionOperationNotPossibleException.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/FixedSaltGenerator.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/IvGenerator.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/NoIvGenerator.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Normalizer.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteCleanablePasswordEncryptor.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteEncryptor.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBECleanablePasswordConfig.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEConfig.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringCleanablePasswordEncryptor.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringEncryptor.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PasswordBased.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomIvGenerator.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomSaltGenerator.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/SaltGenerator.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEByteEncryptor.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEStringEncryptor.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringEncryptor.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringPBEConfig.java delete mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/TestJasypt.java diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java index 9dc8c0e224..a026b2188f 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java @@ -634,11 +634,11 @@ else if (paddingName.equals("TBCPADDING")) protected void engineInit( int opmode, Key key, - AlgorithmParameterSpec params, + final AlgorithmParameterSpec paramSpec, SecureRandom random) throws InvalidKeyException, InvalidAlgorithmParameterException { - CipherParameters param = null; + CipherParameters param; this.pbeSpec = null; this.pbeAlgorithm = null; @@ -656,7 +656,7 @@ protected void engineInit( // // for RC5-64 we must have some default parameters // - if (params == null && (baseEngine != null && baseEngine.getAlgorithmName().startsWith("RC5-64"))) + if (paramSpec == null && (baseEngine != null && baseEngine.getAlgorithmName().startsWith("RC5-64"))) { throw new InvalidAlgorithmParameterException("RC5 requires an RC5ParametersSpec to be passed in."); } @@ -676,9 +676,9 @@ protected void engineInit( throw new InvalidKeyException("PKCS12 requires a SecretKey/PBEKey"); } - if (params instanceof PBEParameterSpec) + if (paramSpec instanceof PBEParameterSpec) { - pbeSpec = (PBEParameterSpec)params; + pbeSpec = (PBEParameterSpec)paramSpec; } if (k instanceof PBEKey && pbeSpec == null) @@ -727,9 +727,9 @@ else if (key instanceof PBKDF1Key) { PBKDF1Key k = (PBKDF1Key)key; - if (params instanceof PBEParameterSpec) + if (paramSpec instanceof PBEParameterSpec) { - pbeSpec = (PBEParameterSpec)params; + pbeSpec = (PBEParameterSpec)paramSpec; } if (k instanceof PBKDF1KeyWithParameters && pbeSpec == null) { @@ -746,9 +746,9 @@ else if (key instanceof PBKDF2Key) { PBKDF2Key k = (PBKDF2Key)key; - if (param instanceof PBEParameterSpec) + if (paramSpec instanceof PBEParameterSpec) { - pbeSpec = (PBEParameterSpec)param; + pbeSpec = (PBEParameterSpec)paramSpec; } if (k instanceof PBKDF2KeyWithParameters && pbeSpec == null) { @@ -776,12 +776,12 @@ else if (key instanceof BCPBEKey) if (k.getParam() != null) { - param = adjustParameters(params, k.getParam()); + param = adjustParameters(paramSpec, k.getParam()); } - else if (params instanceof PBEParameterSpec) + else if (paramSpec instanceof PBEParameterSpec) { - pbeSpec = (PBEParameterSpec)params; - param = PBE.Util.makePBEParameters(k, params, cipher.getUnderlyingCipher().getAlgorithmName()); + pbeSpec = (PBEParameterSpec)paramSpec; + param = PBE.Util.makePBEParameters(k, paramSpec, cipher.getUnderlyingCipher().getAlgorithmName()); } else { @@ -796,7 +796,7 @@ else if (params instanceof PBEParameterSpec) else if (key instanceof PBEKey) { PBEKey k = (PBEKey)key; - pbeSpec = (PBEParameterSpec)params; + pbeSpec = (PBEParameterSpec)paramSpec; if (k instanceof PKCS12KeyWithParameters && pbeSpec == null) { pbeSpec = new PBEParameterSpec(k.getSalt(), k.getIterationCount()); @@ -821,19 +821,17 @@ else if (!(key instanceof RepeatedSecretKeySpec)) param = null; } -// AlgorithmParameterSpec params; -// if (params instanceof PBEParameterSpec) -// { -// params = ((PBEParameterSpec)params).getParameterSpec(); -// if (((IvParameterSpec)params).getIV().length == 0) -// { -// params = new IvParameterSpec(((ParametersWithIV)param).getIV()); -// } -// } -// else -// { -// params = params; -// } + AlgorithmParameterSpec params = paramSpec; + if (paramSpec instanceof PBEParameterSpec) + { + params = ((PBEParameterSpec)paramSpec).getParameterSpec(); + // If params.getIv() returns an empty byte array, ivParam will be assigned an IV generated by PBE.Util.makePBEParameters + // according to RFC 7292. This behavior is intended for Jasypt users who choose to use NoIvGenerator. + if (params instanceof IvParameterSpec && ((IvParameterSpec)params).getIV().length == 0) + { + params = paramSpec; + } + } if (params instanceof AEADParameterSpec) { diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/AlreadyInitializedException.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/AlreadyInitializedException.java deleted file mode 100644 index 8e62105045..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/AlreadyInitializedException.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - - -/** - * Exception thrown when an attempt is made to change the configuration - * of an entity once it has been initialized. - * - * - * @since 1.0 - * - * @author Daniel Fernández - * - */ -public final class AlreadyInitializedException - extends RuntimeException { - - private static final long serialVersionUID = 4592515503937873874L; - - public AlreadyInitializedException() { - super("Encryption entity already initialized"); - } - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Base64.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Base64.java deleted file mode 100644 index 9516551656..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Base64.java +++ /dev/null @@ -1,585 +0,0 @@ -package org.bouncycastle.jcajce.provider.test.jasypt; - -/* - * Copyright 2001-2004 The Apache Software Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -/* - * IMPORTANT NOTE: This class has been included into Jasypt's source tree from - * Apache Commons-Codec version 1.3 [see http://commons.apache.org/codec], - * licensed under Apache License 2.0 [see http://www.apache.org/licenses/LICENSE-2.0]. - * No modifications have been made to the code of this class except the package name. - */ - - - -/** - * Provides Base64 encoding and decoding as defined by RFC 2045. - * - *

This class implements section 6.8. Base64 Content-Transfer-Encoding - * from RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: - * Format of Internet Message Bodies by Freed and Borenstein.

- * - * @author Apache Software Foundation - * @see RFC 2045 - * @since 1.0-dev - */ -public class Base64 -{ - - /** - * Chunk size per RFC 2045 section 6.8. - * - *

The {@value} character limit does not count the trailing CRLF, but counts - * all other characters, including any equal signs.

- * - * @see RFC 2045 section 6.8 - */ - static final int CHUNK_SIZE = 76; - - /** - * Chunk separator per RFC 2045 section 2.1. - * - * @see RFC 2045 section 2.1 - */ - static final byte[] CHUNK_SEPARATOR = "\r\n".getBytes(); - - /** - * The base length. - */ - static final int BASELENGTH = 255; - - /** - * Lookup length. - */ - static final int LOOKUPLENGTH = 64; - - /** - * Used to calculate the number of bits in a byte. - */ - static final int EIGHTBIT = 8; - - /** - * Used when encoding something which has fewer than 24 bits. - */ - static final int SIXTEENBIT = 16; - - /** - * Used to determine how many bits data contains. - */ - static final int TWENTYFOURBITGROUP = 24; - - /** - * Used to get the number of Quadruples. - */ - static final int FOURBYTE = 4; - - /** - * Used to test the sign of a byte. - */ - static final int SIGN = -128; - - /** - * Byte used to pad output. - */ - static final byte PAD = (byte)'='; - - // Create arrays to hold the base64 characters and a - // lookup for base64 chars - private static byte[] base64Alphabet = new byte[BASELENGTH]; - private static byte[] lookUpBase64Alphabet = new byte[LOOKUPLENGTH]; - - // Populating the lookup and character arrays - static - { - for (int i = 0; i < BASELENGTH; i++) - { - base64Alphabet[i] = (byte)-1; - } - for (int i = 'Z'; i >= 'A'; i--) - { - base64Alphabet[i] = (byte)(i - 'A'); - } - for (int i = 'z'; i >= 'a'; i--) - { - base64Alphabet[i] = (byte)(i - 'a' + 26); - } - for (int i = '9'; i >= '0'; i--) - { - base64Alphabet[i] = (byte)(i - '0' + 52); - } - - base64Alphabet['+'] = 62; - base64Alphabet['/'] = 63; - - for (int i = 0; i <= 25; i++) - { - lookUpBase64Alphabet[i] = (byte)('A' + i); - } - - for (int i = 26, j = 0; i <= 51; i++, j++) - { - lookUpBase64Alphabet[i] = (byte)('a' + j); - } - - for (int i = 52, j = 0; i <= 61; i++, j++) - { - lookUpBase64Alphabet[i] = (byte)('0' + j); - } - - lookUpBase64Alphabet[62] = (byte)'+'; - lookUpBase64Alphabet[63] = (byte)'/'; - } - - private static boolean isBase64(byte octect) - { - if (octect == PAD) - { - return true; - } - else if (base64Alphabet[octect] == -1) - { - return false; - } - else - { - return true; - } - } - - /** - * Tests a given byte array to see if it contains - * only valid characters within the Base64 alphabet. - * - * @param arrayOctect byte array to test - * @return true if all bytes are valid characters in the Base64 - * alphabet or if the byte array is empty; false, otherwise - */ - public static boolean isArrayByteBase64(byte[] arrayOctect) - { - - arrayOctect = discardWhitespace(arrayOctect); - - int length = arrayOctect.length; - if (length == 0) - { - // shouldn't a 0 length array be valid base64 data? - // return false; - return true; - } - for (int i = 0; i < length; i++) - { - if (!isBase64(arrayOctect[i])) - { - return false; - } - } - return true; - } - - /** - * Encodes binary data using the base64 algorithm but - * does not chunk the output. - * - * @param binaryData binary data to encode - * @return Base64 characters - */ - public static byte[] encodeBase64(byte[] binaryData) - { - return encodeBase64(binaryData, false); - } - - /** - * Encodes binary data using the base64 algorithm and chunks - * the encoded output into 76 character blocks - * - * @param binaryData binary data to encode - * @return Base64 characters chunked in 76 character blocks - */ - public static byte[] encodeBase64Chunked(byte[] binaryData) - { - return encodeBase64(binaryData, true); - } - - - /** - * Decodes an Object using the base64 algorithm. This method - * is provided in order to satisfy the requirements of the - * Decoder interface, and will throw a DecoderException if the - * supplied object is not of type byte[]. - * - * @param pObject Object to decode - * @return An object (of type byte[]) containing the - * binary data which corresponds to the byte[] supplied. - * @throws DecoderException if the parameter supplied is not - * of type byte[] - */ - public Object decode(Object pObject) - throws DecoderException - { - if (!(pObject instanceof byte[])) - { - throw new DecoderException("Parameter supplied to Base64 decode is not a byte[]"); - } - return decode((byte[])pObject); - } - - /** - * Decodes a byte[] containing containing - * characters in the Base64 alphabet. - * - * @param pArray A byte array containing Base64 character data - * @return a byte array containing binary data - */ - public byte[] decode(byte[] pArray) - { - return decodeBase64(pArray); - } - - /** - * Encodes binary data using the base64 algorithm, optionally - * chunking the output into 76 character blocks. - * - * @param binaryData Array containing binary data to encode. - * @param isChunked if isChunked is true this encoder will chunk - * the base64 output into 76 character blocks - * @return Base64-encoded data. - */ - public static byte[] encodeBase64(byte[] binaryData, boolean isChunked) - { - int lengthDataBits = binaryData.length * EIGHTBIT; - int fewerThan24bits = lengthDataBits % TWENTYFOURBITGROUP; - int numberTriplets = lengthDataBits / TWENTYFOURBITGROUP; - byte encodedData[] = null; - int encodedDataLength = 0; - int nbrChunks = 0; - - if (fewerThan24bits != 0) - { - //data not divisible by 24 bit - encodedDataLength = (numberTriplets + 1) * 4; - } - else - { - // 16 or 8 bit - encodedDataLength = numberTriplets * 4; - } - - // If the output is to be "chunked" into 76 character sections, - // for compliance with RFC 2045 MIME, then it is important to - // allow for extra length to account for the separator(s) - if (isChunked) - { - - nbrChunks = - (CHUNK_SEPARATOR.length == 0 ? 0 : (int)Math.ceil((float)encodedDataLength / CHUNK_SIZE)); - encodedDataLength += nbrChunks * CHUNK_SEPARATOR.length; - } - - encodedData = new byte[encodedDataLength]; - - byte k = 0, l = 0, b1 = 0, b2 = 0, b3 = 0; - - int encodedIndex = 0; - int dataIndex = 0; - int i = 0; - int nextSeparatorIndex = CHUNK_SIZE; - int chunksSoFar = 0; - - //log.debug("number of triplets = " + numberTriplets); - for (i = 0; i < numberTriplets; i++) - { - dataIndex = i * 3; - b1 = binaryData[dataIndex]; - b2 = binaryData[dataIndex + 1]; - b3 = binaryData[dataIndex + 2]; - - //log.debug("b1= " + b1 +", b2= " + b2 + ", b3= " + b3); - - l = (byte)(b2 & 0x0f); - k = (byte)(b1 & 0x03); - - byte val1 = - ((b1 & SIGN) == 0) ? (byte)(b1 >> 2) : (byte)((b1) >> 2 ^ 0xc0); - byte val2 = - ((b2 & SIGN) == 0) ? (byte)(b2 >> 4) : (byte)((b2) >> 4 ^ 0xf0); - byte val3 = - ((b3 & SIGN) == 0) ? (byte)(b3 >> 6) : (byte)((b3) >> 6 ^ 0xfc); - - encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; - //log.debug( "val2 = " + val2 ); - //log.debug( "k4 = " + (k<<4) ); - //log.debug( "vak = " + (val2 | (k<<4)) ); - encodedData[encodedIndex + 1] = - lookUpBase64Alphabet[val2 | (k << 4)]; - encodedData[encodedIndex + 2] = - lookUpBase64Alphabet[(l << 2) | val3]; - encodedData[encodedIndex + 3] = lookUpBase64Alphabet[b3 & 0x3f]; - - encodedIndex += 4; - - // If we are chunking, let's put a chunk separator down. - if (isChunked) - { - // this assumes that CHUNK_SIZE % 4 == 0 - if (encodedIndex == nextSeparatorIndex) - { - System.arraycopy( - CHUNK_SEPARATOR, - 0, - encodedData, - encodedIndex, - CHUNK_SEPARATOR.length); - chunksSoFar++; - nextSeparatorIndex = - (CHUNK_SIZE * (chunksSoFar + 1)) + - (chunksSoFar * CHUNK_SEPARATOR.length); - encodedIndex += CHUNK_SEPARATOR.length; - } - } - } - - // form integral number of 6-bit groups - dataIndex = i * 3; - - if (fewerThan24bits == EIGHTBIT) - { - b1 = binaryData[dataIndex]; - k = (byte)(b1 & 0x03); - //log.debug("b1=" + b1); - //log.debug("b1<<2 = " + (b1>>2) ); - byte val1 = - ((b1 & SIGN) == 0) ? (byte)(b1 >> 2) : (byte)((b1) >> 2 ^ 0xc0); - encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; - encodedData[encodedIndex + 1] = lookUpBase64Alphabet[k << 4]; - encodedData[encodedIndex + 2] = PAD; - encodedData[encodedIndex + 3] = PAD; - } - else if (fewerThan24bits == SIXTEENBIT) - { - - b1 = binaryData[dataIndex]; - b2 = binaryData[dataIndex + 1]; - l = (byte)(b2 & 0x0f); - k = (byte)(b1 & 0x03); - - byte val1 = - ((b1 & SIGN) == 0) ? (byte)(b1 >> 2) : (byte)((b1) >> 2 ^ 0xc0); - byte val2 = - ((b2 & SIGN) == 0) ? (byte)(b2 >> 4) : (byte)((b2) >> 4 ^ 0xf0); - - encodedData[encodedIndex] = lookUpBase64Alphabet[val1]; - encodedData[encodedIndex + 1] = - lookUpBase64Alphabet[val2 | (k << 4)]; - encodedData[encodedIndex + 2] = lookUpBase64Alphabet[l << 2]; - encodedData[encodedIndex + 3] = PAD; - } - - if (isChunked) - { - // we also add a separator to the end of the final chunk. - if (chunksSoFar < nbrChunks) - { - System.arraycopy( - CHUNK_SEPARATOR, - 0, - encodedData, - encodedDataLength - CHUNK_SEPARATOR.length, - CHUNK_SEPARATOR.length); - } - } - - return encodedData; - } - - /** - * Decodes Base64 data into octects - * - * @param base64Data Byte array containing Base64 data - * @return Array containing decoded data. - */ - public static byte[] decodeBase64(byte[] base64Data) - { - // RFC 2045 requires that we discard ALL non-Base64 characters - base64Data = discardNonBase64(base64Data); - - // handle the edge case, so we don't have to worry about it later - if (base64Data.length == 0) - { - return new byte[0]; - } - - int numberQuadruple = base64Data.length / FOURBYTE; - byte decodedData[] = null; - byte b1 = 0, b2 = 0, b3 = 0, b4 = 0, marker0 = 0, marker1 = 0; - - // Throw away anything not in base64Data - - int encodedIndex = 0; - int dataIndex = 0; - { - // this sizes the output array properly - rlw - int lastData = base64Data.length; - // ignore the '=' padding - while (base64Data[lastData - 1] == PAD) - { - if (--lastData == 0) - { - return new byte[0]; - } - } - decodedData = new byte[lastData - numberQuadruple]; - } - - for (int i = 0; i < numberQuadruple; i++) - { - dataIndex = i * 4; - marker0 = base64Data[dataIndex + 2]; - marker1 = base64Data[dataIndex + 3]; - - b1 = base64Alphabet[base64Data[dataIndex]]; - b2 = base64Alphabet[base64Data[dataIndex + 1]]; - - if (marker0 != PAD && marker1 != PAD) - { - //No PAD e.g 3cQl - b3 = base64Alphabet[marker0]; - b4 = base64Alphabet[marker1]; - - decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4); - decodedData[encodedIndex + 1] = - (byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); - decodedData[encodedIndex + 2] = (byte)(b3 << 6 | b4); - } - else if (marker0 == PAD) - { - //Two PAD e.g. 3c[Pad][Pad] - decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4); - } - else if (marker1 == PAD) - { - //One PAD e.g. 3cQ[Pad] - b3 = base64Alphabet[marker0]; - - decodedData[encodedIndex] = (byte)(b1 << 2 | b2 >> 4); - decodedData[encodedIndex + 1] = - (byte)(((b2 & 0xf) << 4) | ((b3 >> 2) & 0xf)); - } - encodedIndex += 3; - } - return decodedData; - } - - /** - * Discards any whitespace from a base-64 encoded block. - * - * @param data The base-64 encoded data to discard the whitespace - * from. - * @return The data, less whitespace (see RFC 2045). - */ - static byte[] discardWhitespace(byte[] data) - { - byte groomedData[] = new byte[data.length]; - int bytesCopied = 0; - - for (int i = 0; i < data.length; i++) - { - switch (data[i]) - { - case (byte)' ': - case (byte)'\n': - case (byte)'\r': - case (byte)'\t': - break; - default: - groomedData[bytesCopied++] = data[i]; - } - } - - byte packedData[] = new byte[bytesCopied]; - - System.arraycopy(groomedData, 0, packedData, 0, bytesCopied); - - return packedData; - } - - /** - * Discards any characters outside of the base64 alphabet, per - * the requirements on page 25 of RFC 2045 - "Any characters - * outside of the base64 alphabet are to be ignored in base64 - * encoded data." - * - * @param data The base-64 encoded data to groom - * @return The data, less non-base64 characters (see RFC 2045). - */ - static byte[] discardNonBase64(byte[] data) - { - byte groomedData[] = new byte[data.length]; - int bytesCopied = 0; - - for (int i = 0; i < data.length; i++) - { - if (isBase64(data[i])) - { - groomedData[bytesCopied++] = data[i]; - } - } - - byte packedData[] = new byte[bytesCopied]; - - System.arraycopy(groomedData, 0, packedData, 0, bytesCopied); - - return packedData; - } - - - // Implementation of the Encoder Interface - - /** - * Encodes an Object using the base64 algorithm. This method - * is provided in order to satisfy the requirements of the - * Encoder interface, and will throw an EncoderException if the - * supplied object is not of type byte[]. - * - * @param pObject Object to encode - * @return An object (of type byte[]) containing the - * base64 encoded data which corresponds to the byte[] supplied. - * @throws EncoderException if the parameter supplied is not - * of type byte[] - */ - public Object encode(Object pObject) - throws EncoderException - { - if (!(pObject instanceof byte[])) - { - throw new EncoderException( - "Parameter supplied to Base64 encode is not a byte[]"); - } - return encode((byte[])pObject); - } - - /** - * Encodes a byte[] containing binary data, into a byte[] containing - * characters in the Base64 alphabet. - * - * @param pArray a byte array containing binary data - * @return A byte array containing only Base64 character data - */ - public byte[] encode(byte[] pArray) - { - return encodeBase64(pArray, false); - } - -} - diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/ByteEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/ByteEncryptor.java deleted file mode 100644 index 4ba03b35f6..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/ByteEncryptor.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - -/** - *

- * Common interface for all Encryptors which receive a - * byte array message and return a byte array result. - *

- * - * @since 1.0 - * - * @author Daniel Fernández - * - */ -public interface ByteEncryptor -{ - - - /** - * Encrypt the input message - * - * @param message the message to be encrypted - * @return the result of encryption - */ - public byte[] encrypt(byte[] message); - - /** - * Decrypt an encrypted message - * - * @param encryptedMessage the encrypted message to be decrypted - * @return the result of decryption - */ - public byte[] decrypt(byte[] encryptedMessage); - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CleanablePasswordBased.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CleanablePasswordBased.java deleted file mode 100644 index fb9782905d..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CleanablePasswordBased.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - -/** - *

- * Common interface for all entities which can be set a password in char[] shape, - * which can be cleaned once the encryptor is initialized so that no immutable - * Strings containing the password are left in memory. - *

- * - * @since 1.8 - * - * @author Daniel Fernández - * - */ -public interface CleanablePasswordBased - extends PasswordBased { - - /** - *

- * Sets a password to be used by the encryptor, as a (cleanable) char[]. - *

- * - * @since 1.8 - * - * @param password the password to be used. - */ - public void setPasswordCharArray(char[] password); - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CommonUtils.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CommonUtils.java deleted file mode 100644 index 53a6bdb8d2..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/CommonUtils.java +++ /dev/null @@ -1,298 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -/** - *

- * Common utils regarding treatment of parameter values and encoding operations. - * This class is for internal use only. - *

- * - * @since 1.3 - * - * @author Daniel Fernández - * - */ -public final class CommonUtils -{ - - public static final String STRING_OUTPUT_TYPE_BASE64 = "base64"; - public static final String STRING_OUTPUT_TYPE_HEXADECIMAL = "hexadecimal"; - - private static final List STRING_OUTPUT_TYPE_HEXADECIMAL_NAMES = - Arrays.asList( - new String[] { - "HEXADECIMAL", "HEXA", "0X", "HEX", "HEXADEC" - } - ); - - private static char[] hexDigits = - {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; - - - - - public static Boolean getStandardBooleanValue(final String valueStr) { - if (valueStr == null) { - return null; - } - final String upperValue = valueStr.toUpperCase(); - if ("TRUE".equals(upperValue) || "ON".equals(upperValue) || "YES".equals(upperValue)) { - return Boolean.TRUE; - } - if ("FALSE".equals(upperValue) || "OFF".equals(upperValue) || "NO".equals(upperValue)) { - return Boolean.FALSE; - } - return null; - } - - - public static String getStandardStringOutputType(final String valueStr) { - if (valueStr == null) { - return null; - } - if (STRING_OUTPUT_TYPE_HEXADECIMAL_NAMES.contains(valueStr.toUpperCase())) { - return STRING_OUTPUT_TYPE_HEXADECIMAL; - } - return STRING_OUTPUT_TYPE_BASE64; - } - - - public static String toHexadecimal(final byte[] message) { - if (message == null) { - return null; - } - final StringBuffer buffer = new StringBuffer(); - for (int i = 0; i < message.length; i++) { - int curByte = message[i] & 0xff; - buffer.append(hexDigits[(curByte >> 4)]); - buffer.append(hexDigits[curByte & 0xf]); - } - return buffer.toString(); - } - - - public static byte[] fromHexadecimal(final String message) { - if (message == null) { - return null; - } - if ((message.length() % 2) != 0) { - throw new EncryptionOperationNotPossibleException(); - } - try { - final byte[] result = new byte[message.length() / 2]; - for (int i = 0; i < message.length(); i = i + 2) { - final int first = Integer.parseInt("" + message.charAt(i), 16); - final int second = Integer.parseInt("" + message.charAt(i + 1), 16); - result[i/2] = (byte) (0x0 + ((first & 0xff) << 4) + (second & 0xff)); - } - return result; - } catch (Exception e) { - throw new EncryptionOperationNotPossibleException(); - } - } - - - public static boolean isEmpty(final String string) { - if (string == null || string.length() == 0) { - return true; - } - return false; - } - - - public static boolean isNotEmpty(final String string) { - if (string == null || string.length() == 0) { - return false; - } - return true; - } - - - public static void validateNotNull(final Object object, final String message) { - if (object == null) { - throw new IllegalArgumentException(message); - } - } - - - public static void validateNotEmpty(final String string, final String message) { - if (isEmpty(string)) { - throw new IllegalArgumentException(message); - } - } - - - public static void validateIsTrue(final boolean expression, final String message) { - if (expression == false) { - throw new IllegalArgumentException(message); - } - } - - - - - public static String[] split(final String string) { - // Whitespace will be used as separator - return split(string, null); - } - - - public static String[] split(final String string, final String separators) { - - if (string == null) { - return null; - } - - final int length = string.length(); - - if (length == 0) { - return new String[0]; - } - - final List results = new ArrayList(); - int i = 0; - int start = 0; - boolean tokenInProgress = false; - - if (separators == null) { - - while (i < length) { - if (Character.isWhitespace(string.charAt(i))) { - if (tokenInProgress) { - results.add(string.substring(start, i)); - tokenInProgress = false; - } - start = ++i; - continue; - } - tokenInProgress = true; - i++; - } - - } else if (separators.length() == 1) { - - final char separator = separators.charAt(0); - while (i < length) { - if (string.charAt(i) == separator) { - if (tokenInProgress) { - results.add(string.substring(start, i)); - tokenInProgress = false; - } - start = ++i; - continue; - } - tokenInProgress = true; - i++; - } - - } else { - - while (i < length) { - if (separators.indexOf(string.charAt(i)) >= 0) { - if (tokenInProgress) { - results.add(string.substring(start, i)); - tokenInProgress = false; - } - start = ++i; - continue; - } - tokenInProgress = true; - i++; - } - - } - - if (tokenInProgress) { - results.add(string.substring(start, i)); - } - - return (String[]) results.toArray(new String[results.size()]); - - } - - - - - public static String substringBefore(final String string, final String separator) { - - if (isEmpty(string) || separator == null) { - return string; - } - if (separator.length() == 0) { - return ""; - } - final int pos = string.indexOf(separator); - if (pos == -1) { - return string; - } - return string.substring(0, pos); - - } - - - - public static String substringAfter(final String string, final String separator) { - if (isEmpty(string)) { - return string; - } - if (separator == null) { - return ""; - } - final int pos = string.indexOf(separator); - if (pos == -1) { - return ""; - } - return string.substring(pos + separator.length()); - } - - - - public static int nextRandomInt() { - return (int)(Math.random() * Integer.MAX_VALUE); - } - - - - public static byte[] appendArrays(final byte[] firstArray, final byte[] secondArray) { - - validateNotNull(firstArray, "Appended array cannot be null"); - validateNotNull(secondArray, "Appended array cannot be null"); - - final byte[] result = new byte[firstArray.length + secondArray.length]; - - System.arraycopy(firstArray, 0, result, 0, firstArray.length); - System.arraycopy(secondArray, 0, result, firstArray.length, secondArray.length); - - return result; - - } - - - // This class should only be called statically - private CommonUtils() { - super(); - } - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/DecoderException.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/DecoderException.java deleted file mode 100644 index f22c38a973..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/DecoderException.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.bouncycastle.jcajce.provider.test.jasypt; - -/** - * Thrown when a Decoder has encountered a failure condition during a decode. - * - * @author Apache Software Foundation - */ -public class DecoderException extends Exception { - - /** - * Creates a DecoderException - * - * @param pMessage A message with meaning to a human - */ - public DecoderException(String pMessage) { - super(pMessage); - } - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncoderException.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncoderException.java deleted file mode 100644 index a745caec9d..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncoderException.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.bouncycastle.jcajce.provider.test.jasypt; - -/** - * Thrown when there is a failure condition during the encoding process. This - * exception is thrown when an Encoder encounters a encoding specific exception - * such as invalid data, inability to calculate a checksum, characters outside of the - * expected range. - * - * @author Apache Software Foundation - */ -public class EncoderException extends Exception { - - /** - * Creates a new instance of this exception with an useful message. - * - * @param pMessage a useful message relating to the encoder specific error. - */ - public EncoderException(String pMessage) { - super(pMessage); - } -} - diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionInitializationException.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionInitializationException.java deleted file mode 100644 index 45b43af9af..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionInitializationException.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - -/** - * Exception thrown when an error is raised during initialization of - * an entity. - * - * @since 1.0 - * - * @author Daniel Fernández - * - */ -public final class EncryptionInitializationException - extends RuntimeException { - - private static final long serialVersionUID = 8929638240023639778L; - - public EncryptionInitializationException() { - super(); - } - - public EncryptionInitializationException(final Throwable t) { - super(t); - } - - public EncryptionInitializationException(final String msg, final Throwable t) { - super(msg, t); - } - - public EncryptionInitializationException(final String msg) { - super(msg); - } - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionOperationNotPossibleException.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionOperationNotPossibleException.java deleted file mode 100644 index 1850a2d63c..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/EncryptionOperationNotPossibleException.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - - -/** - *

- * General exception thrown when any errors are raised during encryption, - * digesting, etc. - *

- *

- * It is intended to provide very little information (if any) of the error - * causes, so that encryption internals are not revealed through error - * messages. - *

- * - * @since 1.0 - * - * @author Daniel Fernández - * - */ -public final class EncryptionOperationNotPossibleException - extends RuntimeException { - - private static final long serialVersionUID = 6304674109588715145L; - - public EncryptionOperationNotPossibleException() { - super(); - } - - public EncryptionOperationNotPossibleException(final Throwable t) { - super(t); - } - - public EncryptionOperationNotPossibleException(final String message) { - super(message); - } - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/FixedSaltGenerator.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/FixedSaltGenerator.java deleted file mode 100644 index 44a460452a..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/FixedSaltGenerator.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - - -/** - *

- * Marker interface for all implementations of {@link SaltGenerator} that - * will always return the same salt (for the same amount of bytes asked). - *

- *

- * Use of this interface in salt generators enables encryptors to perform - * some performance optimizations whenever they are used. - *

- * - * @since 1.9.2 - * - * @author Daniel Fernández - * - */ -public interface FixedSaltGenerator - extends SaltGenerator { - - // Marker interface - no methods added - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/IvGenerator.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/IvGenerator.java deleted file mode 100644 index 5e42da7a80..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/IvGenerator.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2019, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - -/** - *

- * Common interface for all initialization vector (IV) generators which can be applied in - * encryption operations. - *

- *

- * Every implementation of this interface must be thread-safe. - *

- * - * @since 1.9.3 - * - * @author Hoki Torres - * - */ -public interface IvGenerator -{ - - /** - *

- * This method will be called for requesting the generation of a new - * IV of the specified length. - *

- * - * @param lengthBytes the requested length for the IV. - * @return the generated IV. - */ - public byte[] generateIv(int lengthBytes); - - - /** - *

- * Determines if the encrypted messages created with a - * specific IV generator will include (prepended) the unencrypted - * IV itself, so that it can be used for decryption - * operations. - *

- *

- * Generally, including the IV unencrypted in encryption results will - * be mandatory for randomly generated IV, or for those generated in a - * non-predictable manner. - * Otherwise, decryption operations will always fail. - * For fixed IV, inclusion will be optional (and in fact undesirable - * if we want to hide the IV value). - *

- * - * @return whether the plain (unencrypted) IV has to be included in - * encryption results or not. - */ - public boolean includePlainIvInEncryptionResults(); - - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/NoIvGenerator.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/NoIvGenerator.java deleted file mode 100644 index 8ecd9e9b3b..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/NoIvGenerator.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2019, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - - - -/** - *

- * This implementation of {@link IvGenerator} always returns a - * initialization vector (IV) of length 0. - *

- *

- * This class is thread-safe. - *

- * - * @since 1.9.3 - * - * @author Hoki Torres - * - */ -public class NoIvGenerator - implements IvGenerator { - - /** - * Creates a new instance of NoIvGenerator - * - */ - public NoIvGenerator() { - super(); - } - - - /** - * Return IV with 0 byte length. - * - * @param lengthBytes length in bytes. - * @return the generated IV. - */ - public byte[] generateIv(final int lengthBytes) { - return new byte[0]; - } - - - /** - * As this IV generator provides an empty vector, its inclusion - * unencrypted in encryption results is not necessary. - * - * @return false - */ - public boolean includePlainIvInEncryptionResults() { - return false; - } - - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Normalizer.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Normalizer.java deleted file mode 100644 index 1a33db5769..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/Normalizer.java +++ /dev/null @@ -1,230 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; - - -/** - *

- * Utility for the normalization of Unicode Strings to NFC form. - *

- *

- * This class tries to use the java.text.Normalizer class in JDK 1.6 - * first and, if it the class is not found (Java version < 6), then it will use - * the ICU4J com.ibm.icu.text.Normalizer class (in this case, a - * ClassNotFoundException will be thrown if ICU4J is not present). - *

- * - * @since 1.5 - * - * @author Daniel Fernández - * - */ -public final class Normalizer -{ - - private static final String ICU_NORMALIZER_CLASS_NAME = "com.ibm.icu.text.Normalizer"; - private static final String JDK_NORMALIZER_CLASS_NAME = "java.text.Normalizer"; - private static final String JDK_NORMALIZER_FORM_CLASS_NAME = "java.text.Normalizer$Form"; - - private static Boolean useIcuNormalizer = null; - - private static Method javaTextNormalizerMethod = null; - private static Object javaTextNormalizerFormNFCConstant = null; - - - /** - *

- * Normalize Unicode-input message to NFC. - *

- *

- * This algorithm will first try to normalize the input's UNICODE using icu4j's - * com.ibm.icu.text.Normalizer and, if it is not present at the - * classpath, will try to use java.text.Normalizer. If this is not present - * either (this class appeared in JavaSE 6), it will raise an exception. - *

- * - * @param message the message to be normalized - * @return the result of the normalization operation - */ - public static String normalizeToNfc(final String message) { - return new String(normalizeToNfc(message.toCharArray())); - } - - - /** - *

- * Normalize Unicode-input message to NFC. - *

- *

- * This algorithm will first try to normalize the input's UNICODE using icu4j's - * com.ibm.icu.text.Normalizer and, if it is not present at the - * classpath, will try to use java.text.Normalizer. If this is not present - * either (this class appeared in JavaSE 6), it will raise an exception. - *

- * - * @param message the message to be normalized - * @return the result of the normalization operation - */ - public static char[] normalizeToNfc(final char[] message) { - - if (useIcuNormalizer == null) { - // Still not initialized, will try to load the icu4j Normalizer. If - // icu4j is in the classpath, it will be used even if java version is >= 6. - try { - - initializeIcu4j(); - - } catch (final ClassNotFoundException e) { - - try { - - initializeJavaTextNormalizer(); - - } catch (final ClassNotFoundException e2) { - throw new EncryptionInitializationException( - "Cannot find a valid UNICODE normalizer: neither " + JDK_NORMALIZER_CLASS_NAME + " nor " + - ICU_NORMALIZER_CLASS_NAME + " have been found at the classpath. If you are using " + - "a version of the JDK older than JavaSE 6, you should include the icu4j library in " + - "your classpath."); - } catch (final NoSuchMethodException e2) { - throw new EncryptionInitializationException( - "Cannot find a valid UNICODE normalizer: " + JDK_NORMALIZER_CLASS_NAME + " has " + - "been found at the classpath, but has an incompatible signature for its 'normalize' " + - "method."); - } catch (final NoSuchFieldException e2) { - throw new EncryptionInitializationException( - "Cannot find a valid UNICODE normalizer: " + JDK_NORMALIZER_FORM_CLASS_NAME + " has " + - "been found at the classpath, but seems to have no 'NFC' value."); - } catch (final IllegalAccessException e2) { - throw new EncryptionInitializationException( - "Cannot find a valid UNICODE normalizer: " + JDK_NORMALIZER_FORM_CLASS_NAME + " has " + - "been found at the classpath, but seems to have no 'NFC' value."); - } - - } - } - - if (useIcuNormalizer.booleanValue()) { - return normalizeWithIcu4j(message); - } - - return normalizeWithJavaNormalizer(message); - - } - - - - static void initializeIcu4j() throws ClassNotFoundException { - Thread.currentThread().getContextClassLoader().loadClass(ICU_NORMALIZER_CLASS_NAME); - useIcuNormalizer = Boolean.TRUE; - } - - - - static void initializeJavaTextNormalizer() - throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException { - - final Class javaTextNormalizerClass = - Thread.currentThread().getContextClassLoader().loadClass(JDK_NORMALIZER_CLASS_NAME); - final Class javaTextNormalizerFormClass = - Thread.currentThread().getContextClassLoader().loadClass(JDK_NORMALIZER_FORM_CLASS_NAME); - javaTextNormalizerMethod = - javaTextNormalizerClass.getMethod( - "normalize", new Class[]{ CharSequence.class, javaTextNormalizerFormClass }); - final Field javaTextNormalizerFormNFCConstantField = javaTextNormalizerFormClass.getField("NFC"); - javaTextNormalizerFormNFCConstant = javaTextNormalizerFormNFCConstantField.get(null); - - useIcuNormalizer = Boolean.FALSE; - - } - - - - - static char[] normalizeWithJavaNormalizer(final char[] message) { - - if (javaTextNormalizerMethod == null || javaTextNormalizerFormNFCConstant == null) { - throw new EncryptionInitializationException( - "Cannot use: " + JDK_NORMALIZER_FORM_CLASS_NAME + ", as JDK-based normalization has " + - "not been initialized! (check previous execution errors)"); - } - - // Using java JDK's Normalizer, we cannot avoid creating Strings - // (it is the only possible interface to the Normalizer class). - // - // Note java.text.Normalizer is accessed via reflection in order to allow this - // class to be JDK 1.4-compilable (though ICU4j will be needed at runtime - // if Java 1.4 is used). - final String messageStr = new String(message); - final String result; - try { - result = (String) javaTextNormalizerMethod.invoke( - null, new Object[] { messageStr, javaTextNormalizerFormNFCConstant }); - } catch (final Exception e) { - throw new EncryptionInitializationException( - "Could not perform a valid UNICODE normalization", e); - } - return result.toCharArray(); - } - - - static char[] normalizeWithIcu4j(final char[] message) { - // initialize the result to twice the size of the message - // this should be more than enough in most cases - char[] normalizationResult = new char[message.length * 2]; - int normalizationResultSize = 0; - while(true) { - // Execute normalization. The result will be written into the normalizationResult - // char array, and the returned int will be the real size of the result. Normally, - // this will be smaller than the size of normalizationResult, but if it is bigger, - // we will have to create a new normalizationResult array and try again (icu4j will - // not raise an exception, only return a value bigger than the destination array size). - normalizationResultSize = 0; - throw new IllegalStateException("Not implemented"); - //normalize(message, normalizationResult, new NFCMode(), 0); -// if (normalizationResultSize <= normalizationResult.length) { -// // everything went OK and result fitted. Copy to a correctly-sized array -// // and clean normalizationResult -// final char[] result = new char[normalizationResultSize]; -// System.arraycopy(normalizationResult, 0, result, 0, normalizationResultSize); -// for (int i = 0; i < normalizationResult.length; i++) { -// normalizationResult[i] = (char)0; -// } -// return result; -// } -// // We need a bigger array. the old array must be cleaned also -// for (int i = 0; i < normalizationResult.length; i++) { -// normalizationResult[i] = (char)0; -// } -// normalizationResult = new char[normalizationResultSize]; - } - - } - - - - private Normalizer() { - super(); - } - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteCleanablePasswordEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteCleanablePasswordEncryptor.java deleted file mode 100644 index 34c9ce83f9..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteCleanablePasswordEncryptor.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - - -/** - *

- * Common interface for all Password Based Encryptors which receive a - * byte array message and return a byte array result, and provide means - * to set passwords as cleanable char[] objects (instead of - * immutable Strings). - *

- *

- * For a default implementation, see {@link StandardPBEByteEncryptor}. - *

- * - * @since 1.8 - * - * @author Daniel Fernández - * - */ -public interface PBEByteCleanablePasswordEncryptor - extends PBEByteEncryptor, CleanablePasswordBased { - - // aggregator interface - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteEncryptor.java deleted file mode 100644 index 883d4531a4..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEByteEncryptor.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - - -/** - *

- * Common interface for all Password Based Encryptors which receive a - * byte array message and return a byte array result. - *

- *

- * For a default implementation, see {@link StandardPBEByteEncryptor}. - *

- * - * @since 1.0 - * - * @author Daniel Fernández - * - */ -public interface PBEByteEncryptor - extends ByteEncryptor, PasswordBased { - - // aggregator interface - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBECleanablePasswordConfig.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBECleanablePasswordConfig.java deleted file mode 100644 index 62cfed747b..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBECleanablePasswordConfig.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - - - -/** - *

- * Common interface for all PBEConfig implementations that store passwords as char[] instead - * of String and also allow this passwords to be set as char[] instead of Strings. - *

- * - * @since 1.8 - * - * @author Daniel Fernández - * - */ -public interface PBECleanablePasswordConfig -{ - - - /** - *

- * Return the password set, as a char array. - *

- *

- * Important: the returned array MUST BE A COPY of the one - * stored in the configuration object. The caller of - * this method is therefore be responsible for cleaning this - * resulting char[]. - *

- * - * @since 1.8 - * - */ - public char[] getPasswordCharArray(); - - /** - *

- * Clean the password stored in this configuration object. - *

- *

- * A common implementation of this cleaning operation consists of - * iterating the array of chars and setting each of its positions to (char)0. - *

- * - * @since 1.8 - * - */ - public void cleanPassword(); - - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEConfig.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEConfig.java deleted file mode 100644 index 3203e66c4d..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEConfig.java +++ /dev/null @@ -1,208 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - -import java.security.Provider; - - -/** - *

- * Common interface for config classes applicable to - - *

- *

- * This interface lets the user create new PBEConfig - * classes which retrieve values for this parameters from different - * (and maybe more secure) sources (remote servers, LDAP, other databases...), - * and do this transparently for the encryptor object. - *

- *

- * The config objects passed to an encryptor will only be queried once - * for each configuration parameter, and this will happen - * during the initialization of the encryptor object. - *

- *

- * For a default implementation, see SimplePBEConfig. - *

- * - * @since 1.0 - * - * @author Daniel Fernández - * - */ -public interface PBEConfig -{ - - - /** - *

- * Returns the algorithm to be used for encryption, like - * PBEWithMD5AndDES. - *

- * - *

- * This algorithm has to be supported by the specified JCE provider - * (or the default one if no provider has been specified) and, if the - * provider supports it, you can also specify mode and - * padding for it, like ALGORITHM/MODE/PADDING. - *

- * - * @return the name of the algorithm to be used. - */ - public String getAlgorithm(); - - - /** - *

- * Returns the password to be used. - *

- *

- * There is no default value for password, so not setting - * this parameter either from a - * {@link PBEConfig} object or from - * a call to setPassword will result in an - * EncryptionInitializationException being thrown during initialization. - *

- * - * @return the password to be used. - */ - public String getPassword(); - - - /** - *

- * Returns the number of hashing iterations applied to obtain the - * encryption key. - *

- *

- * This mechanism is explained in - * PKCS #5: Password-Based Cryptography Standard. - *

- * - * @return the number of iterations - */ - public Integer getKeyObtentionIterations(); - - - /** - *

- * Returns a {@link SaltGenerator} implementation to be used by the - * encryptor. - *

- *

- * If this method returns null, the encryptor will ignore the config object - * when deciding the salt generator to be used. - *

- * - * @return the salt generator, or null if this object will not want to set - * a specific SaltGenerator implementation. - */ - public SaltGenerator getSaltGenerator(); - - - /** - *

- * Returns a {@link IvGenerator} implementation to be used by the - * encryptor. - *

- *

- * If this method returns null, the encryptor will ignore the config object - * when deciding the IV generator to be used. - *

- * - * @return the IV generator, or null if this object will not want to set - * a specific IvGenerator implementation. - */ - public IvGenerator getIvGenerator(); - - - /** - *

- * Returns the name of the java.security.Provider implementation - * to be used by the encryptor for obtaining the encryption algorithm. This - * provider must have been registered beforehand. - *

- *

- * If this method returns null, the encryptor will ignore this parameter - * when deciding the name of the security provider to be used. - *

- *

- * If this method does not return null, and neither does {@link #getProvider()}, - * providerName will be ignored, and the provider object returned - * by getProvider() will be used. - *

- * - * @since 1.3 - * - * @return the name of the security provider to be used. - */ - public String getProviderName(); - - - /** - *

- * Returns the java.security.Provider implementation object - * to be used by the encryptor for obtaining the encryption algorithm. - *

- *

- * If this method returns null, the encryptor will ignore this parameter - * when deciding the security provider object to be used. - *

- *

- * If this method does not return null, and neither does {@link #getProviderName()}, - * providerName will be ignored, and the provider object returned - * by getProvider() will be used. - *

- *

- * The provider returned by this method does not need to be - * registered beforehand, and its use will not result in its - * being registered. - *

- * - * @since 1.3 - * - * @return the security provider object to be asked for the digest - * algorithm. - */ - public Provider getProvider(); - - - - - - - /** - *

- * Get the size of the pool of encryptors to be created. - *

- *

- * This parameter will be ignored if used with a non-pooled encryptor. - *

- * - * @since 1.7 - * - * @return the size of the pool to be used if this configuration is used with a - * pooled encryptor - */ - public Integer getPoolSize(); - - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringCleanablePasswordEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringCleanablePasswordEncryptor.java deleted file mode 100644 index 4c4ae55d09..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringCleanablePasswordEncryptor.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - - -/** - *

- * Common interface for all Password Based Encryptors which receive a - * String message and return a String result, and provide means - * to set passwords as cleanable char[] objects (instead of - * immutable Strings). - *

- * For a default implementation, see {@link StandardPBEStringEncryptor}. - *

- * - * @since 1.8 - * - * @author Daniel Fernández - * - */ -public interface PBEStringCleanablePasswordEncryptor - extends PBEStringEncryptor, CleanablePasswordBased { - - // aggregator interface - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringEncryptor.java deleted file mode 100644 index 487c788e4e..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PBEStringEncryptor.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - - -/** - *

- * Common interface for all Password Based Encryptors which receive a - * String message and return a String result. - *

- *

- * For a default implementation, see {@link StandardPBEStringEncryptor}. - *

- * - * @since 1.0 - * - * @author Daniel Fernández - * - */ -public interface PBEStringEncryptor - extends StringEncryptor, PasswordBased { - - // aggregator interface - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PasswordBased.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PasswordBased.java deleted file mode 100644 index 5b966914e4..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/PasswordBased.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - -/** - *

- * Common interface for all entities which can be set a password. - *

- * - * @since 1.3 - * - * @author Daniel Fernández - * - */ -public interface PasswordBased -{ - - /** - *

- * Sets a password to be used by the encryptor. - *

- * - * @param password the password to be used. - */ - public void setPassword(String password); - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomIvGenerator.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomIvGenerator.java deleted file mode 100644 index 70c58898e1..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomIvGenerator.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2019, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - -/** - *

- * This implementation of {@link IvGenerator} holds a secure random - * generator which can be used for generating random initialization vectors (IV) for encryption. - *

- *

- * The algorithm used for random number generation can be configured at - * instantiation time. If not, the default algorithm will be used. - *

- *

- * This class is thread-safe. - *

- * - * @since 1.9.3 - * - * @author Hoki Torres - * - */ -public class RandomIvGenerator - implements IvGenerator { - - /** - * The default algorithm to be used for secure random number - * generation: set to SHA1PRNG. - */ - public static final String DEFAULT_SECURE_RANDOM_ALGORITHM = "SHA1PRNG"; - - private final SecureRandom random; - - - /** - * Creates a new instance of RandomIvGenerator using the - * default secure random number generation algorithm. - */ - public RandomIvGenerator() { - this(DEFAULT_SECURE_RANDOM_ALGORITHM); - } - - - /** - * Creates a new instance of RandomIvGenerator specifying a - * secure random number generation algorithm. - */ - public RandomIvGenerator(final String secureRandomAlgorithm) { - super(); - try { - this.random = SecureRandom.getInstance(secureRandomAlgorithm); - } catch (NoSuchAlgorithmException e) { - throw new EncryptionInitializationException(e); - } - } - - - /** - * Generate a random IV of the specified length in bytes. - * - * @param lengthBytes length in bytes. - * @return the generated IV. - */ - public byte[] generateIv(final int lengthBytes) { - final byte[] iv = new byte[lengthBytes]; - synchronized (this.random) { - this.random.nextBytes(iv); - } - return iv; - } - - - /** - * This IV generator needs the IV to be included unencrypted in - * encryption results, because of its being random. This method will always - * return true. - * - * @return true - */ - public boolean includePlainIvInEncryptionResults() { - return true; - } - - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomSaltGenerator.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomSaltGenerator.java deleted file mode 100644 index 04453b679f..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/RandomSaltGenerator.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - -import java.security.NoSuchAlgorithmException; -import java.security.SecureRandom; - -/** - *

- * This implementation of {@link SaltGenerator} holds a secure random - * generator which can be used for generating random salts for encryption - * or digesting. - *

- *

- * The algorithm used for random number generation can be configured at - * instantiation time. If not, the default algorithm will be used. - *

- *

- * This class is thread-safe. - *

- * - * @since 1.2 - * - * @author Daniel Fernández - * - */ -public class RandomSaltGenerator - implements SaltGenerator { - - /** - * The default algorithm to be used for secure random number - * generation: set to SHA1PRNG. - */ - public static final String DEFAULT_SECURE_RANDOM_ALGORITHM = "SHA1PRNG"; - - private final SecureRandom random; - - - /** - * Creates a new instance of RandomSaltGenerator using the - * default secure random number generation algorithm. - */ - public RandomSaltGenerator() { - this(DEFAULT_SECURE_RANDOM_ALGORITHM); - } - - - /** - * Creates a new instance of RandomSaltGenerator specifying a - * secure random number generation algorithm. - * - * @since 1.5 - * - */ - public RandomSaltGenerator(final String secureRandomAlgorithm) { - super(); - try { - this.random = SecureRandom.getInstance(secureRandomAlgorithm); - } catch (NoSuchAlgorithmException e) { - throw new EncryptionInitializationException(e); - } - } - - - /** - * Generate a random salt of the specified length in bytes. - * - * @param lengthBytes length in bytes. - * @return the generated salt. - */ - public byte[] generateSalt(final int lengthBytes) { - final byte[] salt = new byte[lengthBytes]; - synchronized (this.random) { - this.random.nextBytes(salt); - } - return salt; - } - - - /** - * This salt generator needs the salt to be included unencrypted in - * encryption results, because of its being random. This method will always - * return true. - * - * @return true - */ - public boolean includePlainSaltInEncryptionResults() { - return true; - } - - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/SaltGenerator.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/SaltGenerator.java deleted file mode 100644 index d8022df565..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/SaltGenerator.java +++ /dev/null @@ -1,73 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - -/** - *

- * Common interface for all salt generators which can be applied in digest - * or encryption operations. - *

- *

- * Every implementation of this interface must be thread-safe. - *

- * - * @since 1.2 - * - * @author Daniel Fernández - * - */ -public interface SaltGenerator -{ - - /** - *

- * This method will be called for requesting the generation of a new - * salt of the specified length. - *

- * - * @param lengthBytes the requested length for the salt. - * @return the generated salt. - */ - public byte[] generateSalt(int lengthBytes); - - - /** - *

- * Determines if the digests and encrypted messages created with a - * specific salt generator will include (prepended) the unencrypted - * salt itself, so that it can be used for matching and decryption - * operations. - *

- *

- * Generally, including the salt unencrypted in encryption results will - * be mandatory for randomly generated salts, or for those generated in a - * non-predictable manner. - * Otherwise, digest matching and decryption operations will always fail. - * For fixed salts, inclusion will be optional (and in fact undesirable - * if we want to hide the salt value). - *

- * - * @return whether the plain (unencrypted) salt has to be included in - * encryption results or not. - */ - public boolean includePlainSaltInEncryptionResults(); - - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEByteEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEByteEncryptor.java deleted file mode 100644 index eae8b798ef..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEByteEncryptor.java +++ /dev/null @@ -1,1209 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - -import java.lang.reflect.Constructor; -import java.security.InvalidKeyException; -import java.security.Provider; -import java.security.spec.AlgorithmParameterSpec; - -import javax.crypto.Cipher; -import javax.crypto.SecretKey; -import javax.crypto.SecretKeyFactory; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.PBEKeySpec; -import javax.crypto.spec.PBEParameterSpec; - - -/** - *

- * Standard implementation of the {@link PBEByteEncryptor} interface. - * This class lets the user specify the algorithm, provider and - * the initialization vector (IV) generator to be used for - * encryption, the password to use, - * the number of hashing iterations and the salt generator - * that will be applied for obtaining the encryption key. - *

- *

- * This class is thread-safe. - *

- *

- *
Configuration - *

- *

- * The algorithm, provider, IV generator, password, key-obtention iterations - * and salt generator can take values in any of these ways: - *

    - *
  • Using its default values (except for password).
  • - *
  • Setting a {@link PBEConfig} - * object which provides new - * configuration values.
  • - *
  • Calling the corresponding setAlgorithm(...), - * setProvider(...), setProviderName(...), - * setIvGenerator(...), - * setPassword(...), setKeyObtentionIterations(...) or - * setSaltGenerator(...) methods.
  • - *
- * And the actual values to be used for initialization will be established - * by applying the following priorities: - *
    - *
  1. First, the default values are considered (except for password).
  2. - *
  3. Then, if a {@link PBEConfig} - * object has been set with - * setConfig(...), the non-null values returned by its - * getX() methods override the default values.
  4. - *
  5. Finally, if the corresponding setX(...) method has been called - * on the encryptor itself for any of the configuration parameters, the - * values set by these calls override all of the above.
  6. - *
- *

- * - *

- *
Initialization - *

- *

- * Before it is ready to encrypt, an object of this class has to be - * initialized. Initialization happens: - *

    - *
  • When initialize() is called.
  • - *
  • When encrypt(...) or decrypt(...) are called for the - * first time, if initialize() has not been called before.
  • - *
- * Once an encryptor has been initialized, trying to - * change its configuration will - * result in an AlreadyInitializedException being thrown. - *

- * - *

- *
Usage - *

- *

- * An encryptor may be used for: - *

    - *
  • Encrypting messages, by calling the encrypt(...) method.
  • - *
  • Decrypting messages, by calling the decrypt(...) method.
  • - *
- * If a random salt generator is used, two encryption results for - * the same message will always be different - * (except in the case of random salt coincidence). This may enforce - * security by difficulting brute force attacks on sets of data at a time - * and forcing attackers to perform a brute force attack on each separate - * piece of encrypted data. - * The same applies when a random IV generator is used. - *

- *

- * To learn more about the mechanisms involved in encryption, read - * PKCS #5: Password-Based Cryptography Standard. - *

- * - * @since 1.0 - * - * @author Daniel Fernández - * - */ -public final class StandardPBEByteEncryptor - implements PBEByteCleanablePasswordEncryptor { - - - /** - * The default algorithm to be used if none specified: PBEWithMD5AndDES. - */ - public static final String DEFAULT_ALGORITHM = "PBEWithMD5AndDES"; - - /** - * The default number of hashing iterations applied for obtaining the - * encryption key from the specified password, set to 1000. - */ - public static final int DEFAULT_KEY_OBTENTION_ITERATIONS = 1000; - - /** - * The default salt size, only used if the chosen encryption algorithm - * is not a block algorithm and thus block size cannot be used as salt size. - */ - public static final int DEFAULT_SALT_SIZE_BYTES = 8; - - /** - * The default IV size, only used if the chosen encryption algorithm - * is not a block algorithm and thus block size cannot be used as IV size. - */ - public static final int DEFAULT_IV_SIZE_BYTES = 16; - - - // Algorithm (and provider-related info) for Password Based Encoding. - private String algorithm = DEFAULT_ALGORITHM; - private String providerName = null; - private Provider provider = null; - - // Password to be applied. This will NOT have a default value. If none - // is set during configuration, an exception will be thrown. - private char[] password = null; - - // Number of hashing iterations to be applied for obtaining the encryption - // key from the specified password. - private int keyObtentionIterations = DEFAULT_KEY_OBTENTION_ITERATIONS; - - // SaltGenerator to be used. Initialization of a salt generator is costly, - // and so default value will be applied only in initialize(), if it finally - // becomes necessary. - private SaltGenerator saltGenerator = null; - - // Size in bytes of the salt to be used for obtaining the - // encryption key. This size will depend on the PBE algorithm being used, - // and it will be set to the size of the block for the specific - // chosen algorithm (if the algorithm is not a block algorithm, the - // default value will be used). - private int saltSizeBytes = DEFAULT_SALT_SIZE_BYTES; - - // IvGenerator to be used. Initialization of a IV generator is costly, - // and so default value will be applied only in initialize(), if it finally - // becomes necessary. - private IvGenerator ivGenerator = null; - - // Size in bytes of the IV. This size will depend on the PBE algorithm - // being used, and it will be set to the size of the block for the specific - // chosen algorithm (if the algorithm is not a block algorithm, the - // default value will be used). - private int ivSizeBytes = DEFAULT_IV_SIZE_BYTES; - - - // Config object set (optionally). - private PBEConfig config = null; - - /* - * Set of booleans which indicate whether the config or default values - * have to be overriden because of the setX methods having been - * called. - */ - private boolean algorithmSet = false; - private boolean passwordSet = false; - private boolean iterationsSet = false; - private boolean saltGeneratorSet = false; - private boolean ivGeneratorSet = false; - private boolean providerNameSet = false; - private boolean providerSet = false; - - - /* - * Flag which indicates whether the encryptor has been initialized or not. - * - * Once initialized, no further modifications to its configuration will - * be allowed. - */ - private boolean initialized = false; - - - // Encryption key generated. - private SecretKey key = null; - - // Ciphers to be used for encryption and decryption. - private Cipher encryptCipher = null; - private Cipher decryptCipher = null; - - - // Flag which indicates whether the salt generator being used is a - // FixedSaltGenerator implementation (in which case some optimizations can - // be applied). - private boolean optimizingDueFixedSalt = false; - private byte[] fixedSaltInUse = null; - - - - - /** - * Creates a new instance of StandardPBEByteEncryptor. - */ - public StandardPBEByteEncryptor() { - super(); - } - - - /** - *

- * Sets a {@link PBEConfig} object - * for the encryptor. If this config - * object is set, it will be asked values for: - *

- * - *
    - *
  • Algorithm
  • - *
  • Security Provider (or provider name)
  • - *
  • Password
  • - *
  • Hashing iterations for obtaining the encryption key
  • - *
  • Salt generator
  • - *
  • IV generator
  • - *
- * - *

- * The non-null values it returns will override the default ones, - * and will be overriden by any values specified with a setX - * method. - *

- * - * @param config the PBEConfig object to be used as the - * source for configuration parameters. - */ - public synchronized void setConfig(PBEConfig config) { - CommonUtils.validateNotNull(config, "Config cannot be set null"); - if (isInitialized()) { - throw new AlreadyInitializedException(); - } - this.config = config; - } - - - /** - *

- * Sets the algorithm to be used for encryption, like - * PBEWithMD5AndDES. - *

- *

- * This algorithm has to be supported by your JCE provider (if you specify - * one, or the default JVM provider if you don't) and, if it is supported, - * you can also specify mode and padding for - * it, like ALGORITHM/MODE/PADDING. - *

- * - * @param algorithm the name of the algorithm to be used. - */ - public synchronized void setAlgorithm(String algorithm) { - CommonUtils.validateNotEmpty(algorithm, "Algorithm cannot be set empty"); - if (isInitialized()) { - throw new AlreadyInitializedException(); - } - this.algorithm = algorithm; - this.algorithmSet = true; - } - - - /** - *

- * Sets the password to be used. - *

- *

- * There is no default value for password, so not setting - * this parameter either from a - * {@link PBEConfig} object or from - * a call to setPassword will result in an - * EncryptionInitializationException being thrown during initialization. - *

- * - * @param password the password to be used. - */ - public synchronized void setPassword(String password) { - CommonUtils.validateNotEmpty(password, "Password cannot be set empty"); - if (isInitialized()) { - throw new AlreadyInitializedException(); - } - if (this.password != null) { - // We clean the old password, if there is one. - cleanPassword(this.password); - } - this.password = password.toCharArray(); - this.passwordSet = true; - } - - - /** - *

- * Sets the password to be used, as a char[]. - *

- *

- * This allows the password to be specified as a cleanable - * char[] instead of a String, in extreme security conscious environments - * in which no copy of the password as an immutable String should - * be kept in memory. - *

- *

- * Important: the array specified as a parameter WILL BE COPIED - * in order to be stored as encryptor configuration. The caller of - * this method will therefore be responsible for its cleaning (jasypt - * will only clean the internally stored copy). - *

- *

- * There is no default value for password, so not setting - * this parameter either from a - * {@link PBEConfig} object or from - * a call to setPassword will result in an - * EncryptionInitializationException being thrown during initialization. - *

- * - * @since 1.8 - * - * @param password the password to be used. - */ - public synchronized void setPasswordCharArray(char[] password) { - CommonUtils.validateNotNull(password, "Password cannot be set null"); - CommonUtils.validateIsTrue(password.length > 0, "Password cannot be set empty"); - if (isInitialized()) { - throw new AlreadyInitializedException(); - } - if (this.password != null) { - // We clean the old password, if there is one. - cleanPassword(this.password); - } - this.password = new char[password.length]; - System.arraycopy(password, 0, this.password, 0, password.length); - this.passwordSet = true; - } - - - /** - *

- * Set the number of hashing iterations applied to obtain the - * encryption key. - *

- *

- * This mechanism is explained in - * PKCS #5: Password-Based Cryptography Standard. - *

- * - * @param keyObtentionIterations the number of iterations - */ - public synchronized void setKeyObtentionIterations( - int keyObtentionIterations) { - CommonUtils.validateIsTrue(keyObtentionIterations > 0, - "Number of iterations for key obtention must be " + - "greater than zero"); - if (isInitialized()) { - throw new AlreadyInitializedException(); - } - this.keyObtentionIterations = keyObtentionIterations; - this.iterationsSet = true; - } - - - /** - *

- * Sets the salt generator to be used. If no salt generator is specified, - * an instance of RandomSaltGenerator will be used. - *

- * - * @param saltGenerator the salt generator to be used. - */ - public synchronized void setSaltGenerator(SaltGenerator saltGenerator) { - CommonUtils.validateNotNull(saltGenerator, "Salt generator cannot be set null"); - if (isInitialized()) { - throw new AlreadyInitializedException(); - } - this.saltGenerator = saltGenerator; - this.saltGeneratorSet = true; - } - - /** - *

- * Sets the IV generator to be used. If no IV generator is specified, - * an instance of NoIvGenerator will be used. - *

- * - * @param ivGenerator the IV generator to be used. - */ - public synchronized void setIvGenerator(IvGenerator ivGenerator) { ; - if (isInitialized()) { - throw new AlreadyInitializedException(); - } - this.ivGenerator = ivGenerator; - this.ivGeneratorSet = true; - } - - - /** - *

- * Sets the name of the security provider to be asked for the - * encryption algorithm. This security provider has to be registered - * beforehand at the JVM security framework. - *

- *

- * The provider can also be set with the {@link #setProvider(Provider)} - * method, in which case it will not be necessary neither registering - * the provider beforehand, - * nor calling this {@link #setProviderName(String)} method to specify - * a provider name. - *

- *

- * Note that a call to {@link #setProvider(Provider)} overrides any value - * set by this method. - *

- *

- * If no provider name / provider is explicitly set, the default JVM - * provider will be used. - *

- * - * @since 1.3 - * - * @param providerName the name of the security provider to be asked - * for the encryption algorithm. - */ - public synchronized void setProviderName(String providerName) { - CommonUtils.validateNotNull(providerName, "Provider name cannot be set null"); - if (isInitialized()) { - throw new AlreadyInitializedException(); - } - this.providerName = providerName; - this.providerNameSet = true; - } - - - /** - *

- * Sets the security provider to be asked for the encryption algorithm. - * The provider does not have to be registered at the security - * infrastructure beforehand, and its being used here will not result in - * its being registered. - *

- *

- * If this method is called, calling {@link #setProviderName(String)} - * becomes unnecessary. - *

- *

- * If no provider name / provider is explicitly set, the default JVM - * provider will be used. - *

- * - * @since 1.3 - * - * @param provider the provider to be asked for the chosen algorithm - */ - public synchronized void setProvider(Provider provider) { - CommonUtils.validateNotNull(provider, "Provider cannot be set null"); - if (isInitialized()) { - throw new AlreadyInitializedException(); - } - this.provider = provider; - this.providerSet = true; - } - - - - - - - - - - - /* - * Clone this encryptor 'size' times and initialize it. - * This encryptor will be at position 0 itself. - * Clones will NOT be initialized. - */ - synchronized StandardPBEByteEncryptor[] cloneAndInitializeEncryptor(final int size) { - - if (isInitialized()) { - throw new EncryptionInitializationException( - "Cannot clone encryptor if it has been already initialized"); - } - - // If there is a config object, this forces the password configured value - // (if any) into the this.password property. - resolveConfigurationPassword(); - - final char[] copiedPassword = new char[this.password.length]; - System.arraycopy(this.password, 0, copiedPassword, 0, this.password.length); - - // Initialize the encryptor - note that this will clean the - // password (that's why copied it before) - initialize(); - - final StandardPBEByteEncryptor[] clones = new StandardPBEByteEncryptor[size]; - - clones[0] = this; - - for (int i = 1; i < size; i++) { - - final StandardPBEByteEncryptor clone = new StandardPBEByteEncryptor(); - clone.setPasswordCharArray(copiedPassword); - if (CommonUtils.isNotEmpty(this.algorithm)) { - clone.setAlgorithm(this.algorithm); - } - clone.setKeyObtentionIterations(this.keyObtentionIterations); - if (this.provider != null) { - clone.setProvider(this.provider); - } - if (this.providerName != null) { - clone.setProviderName(this.providerName); - } - if (this.saltGenerator != null) { - clone.setSaltGenerator(this.saltGenerator); - } - if (this.ivGenerator != null) { - clone.setIvGenerator(this.ivGenerator); - } - - clones[i] = clone; - - } - - cleanPassword(copiedPassword); - - return clones; - - } - - - - - /** - *

- * Returns true if the encryptor has already been initialized, false if - * not.
- * Initialization happens: - *

- *
    - *
  • When initialize is called.
  • - *
  • When encrypt or decrypt are called for the - * first time, if initialize has not been called before.
  • - *
- *

- * Once an encryptor has been initialized, trying to - * change its configuration will - * result in an AlreadyInitializedException being thrown. - *

- * - * @return true if the encryptor has already been initialized, false if - * not. - */ - public boolean isInitialized() { - return this.initialized; - } - - - /** - *

- * Initialize the encryptor. - *

- *

- * This operation will consist in determining the actual configuration - * values to be used, and then initializing the encryptor with them. - *
- * These values are decided by applying the following priorities: - *

- *
    - *
  1. First, the default values are considered (except for password). - *
  2. - *
  3. Then, if a - * {@link PBEConfig} - * object has been set with - * setConfig, the non-null values returned by its - * getX methods override the default values.
  4. - *
  5. Finally, if the corresponding setX method has been called - * on the encryptor itself for any of the configuration parameters, - * the values set by these calls override all of the above.
  6. - *
- *

- * Once an encryptor has been initialized, trying to - * change its configuration will - * result in an AlreadyInitializedException being thrown. - *

- * - * @throws EncryptionInitializationException if initialization could not - * be correctly done (for example, no password has been set). - */ - public synchronized void initialize() { - - // Double-check to avoid synchronization issues - if (!this.initialized) { - - /* - * If a PBEConfig object has been set, we need to - * consider the values it returns (if, for each value, the - * corresponding "setX" method has not been called). - */ - if (this.config != null) { - - resolveConfigurationPassword(); - - final String configAlgorithm = this.config.getAlgorithm(); - if (configAlgorithm != null) { - CommonUtils.validateNotEmpty(configAlgorithm, - "Algorithm cannot be set empty"); - } - - final Integer configKeyObtentionIterations = - this.config.getKeyObtentionIterations(); - if (configKeyObtentionIterations != null) { - CommonUtils.validateIsTrue(configKeyObtentionIterations.intValue() > 0, - "Number of iterations for key obtention must be " + - "greater than zero"); - } - - final SaltGenerator configSaltGenerator = this.config.getSaltGenerator(); - - final IvGenerator configIvGenerator = this.config.getIvGenerator(); - - final String configProviderName = this.config.getProviderName(); - if (configProviderName != null) { - CommonUtils.validateNotEmpty(configProviderName, - "Provider name cannot be empty"); - } - - final Provider configProvider = this.config.getProvider(); - - this.algorithm = - ((this.algorithmSet) || (configAlgorithm == null))? - this.algorithm : configAlgorithm; - this.keyObtentionIterations = - ((this.iterationsSet) || - (configKeyObtentionIterations == null))? - this.keyObtentionIterations : - configKeyObtentionIterations.intValue(); - this.saltGenerator = - ((this.saltGeneratorSet) || (configSaltGenerator == null))? - this.saltGenerator : configSaltGenerator; - this.ivGenerator = - ((this.ivGeneratorSet) || (configIvGenerator == null))? - this.ivGenerator : configIvGenerator; - this.providerName = - ((this.providerNameSet) || (configProviderName == null))? - this.providerName : configProviderName; - this.provider = - ((this.providerSet) || (configProvider == null))? - this.provider : configProvider; - - } - - /* - * If the encryptor was not set a salt generator in any way, - * it is time to apply its default value. - */ - if (this.saltGenerator == null) { - this.saltGenerator = new RandomSaltGenerator(); - } - - /* - * Default value is a no-op IV generator to maintain backwards compatibility - */ - if (this.ivGenerator == null) { - this.ivGenerator = new NoIvGenerator(); - } - - try { - - // Password cannot be null. - if (this.password == null) { - throw new EncryptionInitializationException( - "Password not set for Password Based Encryptor"); - } - - // Normalize password to NFC form - final char[] normalizedPassword = Normalizer.normalizeToNfc(this.password); - - /* - * Encryption and decryption Ciphers are created the usual way. - */ - final PBEKeySpec pbeKeySpec = new PBEKeySpec(normalizedPassword); - - // We don't need the char[] passwords anymore -> clean! - cleanPassword(this.password); - cleanPassword(normalizedPassword); - - - if (this.provider != null) { - - final SecretKeyFactory factory = - SecretKeyFactory.getInstance( - this.algorithm, - this.provider); - - this.key = factory.generateSecret(pbeKeySpec); - - this.encryptCipher = - Cipher.getInstance(this.algorithm, this.provider); - this.decryptCipher = - Cipher.getInstance(this.algorithm, this.provider); - - } else if (this.providerName != null) { - - final SecretKeyFactory factory = - SecretKeyFactory.getInstance( - this.algorithm, - this.providerName); - - this.key = factory.generateSecret(pbeKeySpec); - - this.encryptCipher = - Cipher.getInstance(this.algorithm, this.providerName); - this.decryptCipher = - Cipher.getInstance(this.algorithm, this.providerName); - - } else { - - final SecretKeyFactory factory = - SecretKeyFactory.getInstance(this.algorithm); - - this.key = factory.generateSecret(pbeKeySpec); - - this.encryptCipher = Cipher.getInstance(this.algorithm); - this.decryptCipher = Cipher.getInstance(this.algorithm); - - } - - } catch (EncryptionInitializationException e) { - throw e; - } catch (Throwable t) { - throw new EncryptionInitializationException(t); - } - - - // The salt size and the IV size for the chosen algorithm are set to be equal - // to the algorithm's block size (if it is a block algorithm). - final int algorithmBlockSize = this.encryptCipher.getBlockSize(); - if (algorithmBlockSize > 0) { - this.saltSizeBytes = algorithmBlockSize; - this.ivSizeBytes = algorithmBlockSize; - } - - - this.optimizingDueFixedSalt = (this.saltGenerator instanceof FixedSaltGenerator) - && (this.ivGenerator instanceof NoIvGenerator); - - if (this.optimizingDueFixedSalt) { - - // Create salt - this.fixedSaltInUse = - this.saltGenerator.generateSalt(this.saltSizeBytes); - - /* - * Initialize the Cipher objects themselves. Due to the fact that - * we will be using a fixed salt, this can be done just once, which - * means a better performance at the encrypt/decrypt methods. - */ - - final PBEParameterSpec parameterSpec = - new PBEParameterSpec(this.fixedSaltInUse, this.keyObtentionIterations); - - try { - - this.encryptCipher.init( - Cipher.ENCRYPT_MODE, this.key, parameterSpec); - this.decryptCipher.init( - Cipher.DECRYPT_MODE, this.key, parameterSpec); - - } catch (final Exception e) { - // If encryption fails, it is more secure not to return any - // information about the cause in nested exceptions. Simply fail. - throw new EncryptionOperationNotPossibleException(); - } - - - } - - - this.initialized = true; - - } - - } - - - - private synchronized void resolveConfigurationPassword() { - - // Double-check to avoid synchronization issues - if (!this.initialized) { - - if (this.config != null && !this.passwordSet) { - - // Get the configured password. If the config object implements - // CleanablePassword, we get password directly as a char array - // in order to avoid unnecessary creation of immutable Strings - // containing such password. - char[] configPassword = null; - if (this.config instanceof PBECleanablePasswordConfig) { - configPassword = ((PBECleanablePasswordConfig)this.config).getPasswordCharArray(); - } else { - final String configPwd = this.config.getPassword(); - if (configPwd != null) { - configPassword = configPwd.toCharArray(); - } - } - - if (configPassword != null) { - CommonUtils.validateIsTrue(configPassword.length > 0, - "Password cannot be set empty"); - } - - if (configPassword != null) { - this.password = new char[configPassword.length]; - System.arraycopy(configPassword, 0, this.password, 0, configPassword.length); - this.passwordSet = true; - cleanPassword(configPassword); - } - - // Finally, clean the password at the configuration object - if (this.config instanceof PBECleanablePasswordConfig) { - ((PBECleanablePasswordConfig)this.config).cleanPassword(); - } - - - } - - } - - } - - - - private static void cleanPassword(final char[] password) { - if (password != null) { - synchronized (password) { - final int pwdLength = password.length; - for (int i = 0; i < pwdLength; i++) { - password[i] = (char)0; - } - } - } - } - - - - /** - *

- * Encrypts a message using the specified configuration. - *

- *

- * The mechanisms applied to perform the encryption operation are described - * in PKCS #5: Password-Based Cryptography Standard. - *

- *

- * This encryptor uses a salt and IV for each encryption - * operation. Sizes of the salt and IV depends on the algorithm - * being used. The salt and the IV, if generated by a random generator, - * they are also appended unencrypted at the beginning - * of the results so that a decryption operation can be performed. - *

- *

- * If a random salt generator is used, two encryption results for - * the same message will always be different - * (except in the case of random salt coincidence). This may enforce - * security by difficulting brute force attacks on sets of data at a time - * and forcing attackers to perform a brute force attack on each separate - * piece of encrypted data. - * The same is applied if a random IV generator is used. - *

- * - * @param message the byte array message to be encrypted - * @return the result of encryption - * @throws EncryptionOperationNotPossibleException if the encryption - * operation fails, ommitting any further information about the - * cause for security reasons. - * @throws EncryptionInitializationException if initialization could not - * be correctly done (for example, no password has been set). - */ - public byte[] encrypt(final byte[] message) - throws EncryptionOperationNotPossibleException { - - if (message == null) { - return null; - } - - // Check initialization - if (!isInitialized()) { - initialize(); - } - - try { - - final byte[] salt; - byte[] iv = null; - byte[] encryptedMessage; - if (this.optimizingDueFixedSalt) { - - salt = this.fixedSaltInUse; - - synchronized (this.encryptCipher) { - encryptedMessage = this.encryptCipher.doFinal(message); - } - - } else { - - // Create salt - salt = this.saltGenerator.generateSalt(this.saltSizeBytes); - - // Create IV - iv = this.ivGenerator.generateIv(this.ivSizeBytes); - - - /* - * Perform encryption using the Cipher - */ - final PBEParameterSpec parameterSpec = buildPBEParameterSpec(salt, iv); - - synchronized (this.encryptCipher) { - this.encryptCipher.init( - Cipher.ENCRYPT_MODE, this.key, parameterSpec); - encryptedMessage = this.encryptCipher.doFinal(message); - } - - } - - // We build an array containing both the unencrypted IV - // and the result of the encryption. This is done only - // if the IV generator we are using specifies to do so. - if (this.ivGenerator.includePlainIvInEncryptionResults()) { - - // Insert plain IV before the encryption result - encryptedMessage = CommonUtils.appendArrays(iv, encryptedMessage); - - } - - // Finally we build an array containing both the unencrypted salt - // and the result of the encryption. This is done only - // if the salt generator we are using specifies to do so. - if (this.saltGenerator.includePlainSaltInEncryptionResults()) { - - // Insert unhashed salt before the encryption result - encryptedMessage = CommonUtils.appendArrays(salt, encryptedMessage); - - } - - - return encryptedMessage; - - } catch (final InvalidKeyException e) { - // The problem could be not having the unlimited strength policies - // installed, so better give a usefull error message. - handleInvalidKeyException(e); - throw new EncryptionOperationNotPossibleException(); - } catch (final Exception e) { - // If encryption fails, it is more secure not to return any - // information about the cause in nested exceptions. Simply fail. - e.printStackTrace(); - throw new EncryptionOperationNotPossibleException(); - } - - } - - - /** - *

- * Decrypts a message using the specified configuration. - *

- *

- * The mechanisms applied to perform the decryption operation are described - * in PKCS #5: Password-Based Cryptography Standard. - *

- *

- * If a random salt generator is used, this decryption operation will - * expect to find an unencrypted salt at the - * beginning of the encrypted input, so that the decryption operation can be - * correctly performed (there is no other way of knowing it). - *

- *

- * If a random IV generator is used, this decryption operation will - * expect to find an unencrypted IV at the - * beginning of the encrypted input, so that the decryption operation can be - * correctly performed (there is no other way of knowing it). - *

- * - * @param encryptedMessage the byte array message to be decrypted - * @return the result of decryption - * @throws EncryptionOperationNotPossibleException if the decryption - * operation fails, ommitting any further information about the - * cause for security reasons. - * @throws EncryptionInitializationException if initialization could not - * be correctly done (for example, no password has been set). - */ - public byte[] decrypt(final byte[] encryptedMessage) - throws EncryptionOperationNotPossibleException { - - if (encryptedMessage == null) { - return null; - } - - // Check initialization - if (!isInitialized()) { - initialize(); - } - - - if (this.saltGenerator.includePlainSaltInEncryptionResults() - && this.ivGenerator.includePlainIvInEncryptionResults()) { - // Check that the received message is bigger than the salt + IV - if (encryptedMessage.length <= this.saltSizeBytes + this.ivSizeBytes) { - throw new EncryptionOperationNotPossibleException(); - } - } else if (this.saltGenerator.includePlainSaltInEncryptionResults()) { - // Check that the received message is bigger than the salt - if (encryptedMessage.length <= this.saltSizeBytes) { - throw new EncryptionOperationNotPossibleException(); - } - } else if (this.ivGenerator.includePlainIvInEncryptionResults()) { - // Check that the received message is bigger than the IV - if (encryptedMessage.length <= this.ivSizeBytes) { - throw new EncryptionOperationNotPossibleException(); - } - } - - - try { - - // If we are using a salt generator which specifies the salt - // to be included into the encrypted message itself, get it from - // there. If not, the salt is supposed to be fixed and thus the - // salt generator can be safely asked for it again. - byte[] salt = null; - byte[] encryptedMessageKernel = null; - if (this.saltGenerator.includePlainSaltInEncryptionResults()) { - - final int saltStart = 0; - final int saltSize = - (this.saltSizeBytes < encryptedMessage.length? this.saltSizeBytes : encryptedMessage.length); - final int encMesKernelStart = - (this.saltSizeBytes < encryptedMessage.length? this.saltSizeBytes : encryptedMessage.length); - final int encMesKernelSize = - (this.saltSizeBytes < encryptedMessage.length? (encryptedMessage.length - this.saltSizeBytes) : 0); - - salt = new byte[saltSize]; - encryptedMessageKernel = new byte[encMesKernelSize]; - - System.arraycopy(encryptedMessage, saltStart, salt, 0, saltSize); - System.arraycopy(encryptedMessage, encMesKernelStart, encryptedMessageKernel, 0, encMesKernelSize); - - } else if (!this.optimizingDueFixedSalt){ - - salt = this.saltGenerator.generateSalt(this.saltSizeBytes); - encryptedMessageKernel = encryptedMessage; - - } else { - // this.optimizingDueFixedSalt == true - - salt = this.fixedSaltInUse; - encryptedMessageKernel = encryptedMessage; - - } - - byte[] iv = null; - byte[] finalEncryptedMessageKernel = null; - if (this.ivGenerator.includePlainIvInEncryptionResults()) { - - final int ivStart = 0; - final int ivSize = - (this.ivSizeBytes < encryptedMessageKernel.length? this.ivSizeBytes : encryptedMessageKernel.length); - final int encMesKernelStart = - (this.ivSizeBytes < encryptedMessageKernel.length? this.ivSizeBytes : encryptedMessageKernel.length); - final int encMesKernelSize = - (this.ivSizeBytes < encryptedMessageKernel.length? (encryptedMessageKernel.length - this.ivSizeBytes) : 0); - - iv = new byte[ivSize]; - finalEncryptedMessageKernel = new byte[encMesKernelSize]; - - System.arraycopy(encryptedMessageKernel, ivStart, iv, 0, ivSize); - System.arraycopy(encryptedMessageKernel, encMesKernelStart, finalEncryptedMessageKernel, 0, encMesKernelSize); - - } else { - iv = ivGenerator.generateIv(ivSizeBytes); - finalEncryptedMessageKernel = encryptedMessageKernel; - - } - - - final byte[] decryptedMessage; - if (this.optimizingDueFixedSalt) { - - /* - * Fixed salt is being used, therefore no initialization supposedly needed - */ - synchronized (this.decryptCipher) { - decryptedMessage = - this.decryptCipher.doFinal(finalEncryptedMessageKernel); - } - - } else { - - /* - * Perform decryption using the Cipher - */ - final PBEParameterSpec parameterSpec = buildPBEParameterSpec(salt, iv); - - synchronized (this.decryptCipher) { - this.decryptCipher.init( - Cipher.DECRYPT_MODE, this.key, parameterSpec); - decryptedMessage = - this.decryptCipher.doFinal(finalEncryptedMessageKernel); - } - - } - - // Return the results - return decryptedMessage; - - } catch (final InvalidKeyException e) { - // The problem could be not having the unlimited strength policies - // installed, so better give a usefull error message. - handleInvalidKeyException(e); - throw new EncryptionOperationNotPossibleException(); - } catch (final Exception e) { - // If decryption fails, it is more secure not to return any - // information about the cause in nested exceptions. Simply fail. - throw new EncryptionOperationNotPossibleException(); - } - - } - - - private PBEParameterSpec buildPBEParameterSpec(final byte[] salt, final byte[] iv) { - - PBEParameterSpec parameterSpec; - - try { - Class[] parameters = {byte[].class, int.class, AlgorithmParameterSpec.class}; - Constructor java8Constructor = PBEParameterSpec.class.getConstructor(parameters); - - Object[] parameterValues = {salt, this.keyObtentionIterations, new IvParameterSpec(iv)}; - - parameterSpec = java8Constructor.newInstance(parameterValues); - - } catch (Exception e) { - parameterSpec = new PBEParameterSpec(salt, this.keyObtentionIterations); - } - - return parameterSpec; - - } - - /* - * Method used to provide an useful error message in the case that the - * user tried to use a strong PBE algorithm like TripleDES and he/she - * has not installed the Unlimited Strength Policy files (the default - * message for this is simply "invalid key size", which does not provide - * enough clues for the user to know what is really going on). - */ - private void handleInvalidKeyException(final InvalidKeyException e) { - - if ((e.getMessage() != null) && - ((e.getMessage().toUpperCase().indexOf("KEY SIZE") != -1))) { - - throw new EncryptionOperationNotPossibleException( - "Encryption raised an exception. A possible cause is " + - "you are using strong encryption algorithms and " + - "you have not installed the Java Cryptography " + - "Extension (JCE) Unlimited Strength Jurisdiction " + - "Policy Files in this Java Virtual Machine"); - - } - - } - -} - diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEStringEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEStringEncryptor.java deleted file mode 100644 index 4ba8a4c3d4..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StandardPBEStringEncryptor.java +++ /dev/null @@ -1,750 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - -import java.security.Provider; - - - -/** - *

- * Standard implementation of the {@link PBEStringEncryptor} interface. - * This class lets the user specify the algorithm (and provider) to be used for - * encryption, the password to use, - * the number of hashing iterations and the salt generator - * that will be applied for obtaining - * the encryption key. - *

- *

- * This class avoids byte-conversion problems related to the fact of - * different platforms having different default charsets, and returns - * encryption results in the form of BASE64-encoded or HEXADECIMAL - * ASCII Strings. - *

- *

- * This class is thread-safe. - *

- *

- *
Configuration - *

- *

- * The algorithm, provider, password, key-obtention iterations and salt generator can take - * values in any of these ways: - *

    - *
  • Using its default values (except for password).
  • - *
  • Setting a {@link PBEConfig} - * object which provides new - * configuration values.
  • - *
  • Calling the corresponding setX(...) methods.
  • - *
- * And the actual values to be used for initialization will be established - * by applying the following priorities: - *
    - *
  1. First, the default values are considered (except for password).
  2. - *
  3. Then, if a {@link PBEConfig} - * object has been set with - * setConfig(...), the non-null values returned by its - * getX() methods override the default values.
  4. - *
  5. Finally, if the corresponding setX(...) method has been called - * on the encryptor itself for any of the configuration parameters, the - * values set by these calls override all of the above.
  6. - *
- *

- * - *

- *
Initialization - *

- *

- * Before it is ready to encrypt, an object of this class has to be - * initialized. Initialization happens: - *

    - *
  • When initialize() is called.
  • - *
  • When encrypt(...) or decrypt(...) are called for the - * first time, if initialize() has not been called before.
  • - *
- * Once an encryptor has been initialized, trying to - * change its configuration will - * result in an AlreadyInitializedException being thrown. - *

- * - *

- *
Usage - *

- *

- * An encryptor may be used for: - *

    - *
  • Encrypting messages, by calling the encrypt(...) method.
  • - *
  • Decrypting messages, by calling the decrypt(...) method.
  • - *
- * If a random salt generator is used, two encryption results for - * the same message will always be different - * (except in the case of random salt coincidence). This may enforce - * security by difficulting brute force attacks on sets of data at a time - * and forcing attackers to perform a brute force attack on each separate - * piece of encrypted data. - *

- *

- * To learn more about the mechanisms involved in encryption, read - * PKCS #5: Password-Based Cryptography Standard. - *

- * - * @since 1.0 - * - * @author Daniel Fernández - * - */ -public final class StandardPBEStringEncryptor - implements PBEStringCleanablePasswordEncryptor { - - /** - *

- * Charset to be used to obtain "encryptable" byte arrays from input - * Strings. Set to UTF-8. - *

- *

- * This charset has to be fixed to some value so that we avoid problems - * with different platforms having different "default" charsets. - *

- *

- * It is set to UTF-8 because it covers the whole spectrum of - * characters representable in Java (which internally uses UTF-16), and - * avoids the size penalty of UTF-16 (which will always use two bytes for - * representing each character, even if it is an ASCII one). - *

- *

- * Setting it to UTF-8 does not mean that Strings that originally come, - * for example, from an ISO-8859-1 input, won't be correctly encoded, as we - * only need to use the same charset both when encoding and decoding. That - * way the same String will be reconstructed independently of the original - * encoding (for encrypting, we only need "a byte representation" of the - * string, not "a readable byte representation"). - *

- */ - private static final String MESSAGE_CHARSET = "UTF-8"; - - /** - *

- * Charset to be used for encoding the encryption results. - * Set to US-ASCII. - *

- *

- * The result of encrypting some bytes can be any other bytes, and so - * the result of encrypting, for example, some LATIN-1 valid String bytes, - * can be bytes that may not conform a "valid" LATIN-1 String. - *

- *

- * Because of this, encryption results are always encoded in BASE64 - * (default) or HEXADECIMAL after being created, and this ensures - * that the results will make perfectly representable, safe ASCII Strings. - * Because of this, the charset used to convert the encrypted bytes to the - * returned String is set to US-ASCII. - *

- */ - private static final String ENCRYPTED_MESSAGE_CHARSET = "US-ASCII"; - - - /** - *

- * Default type of String output. Set to BASE64. - *

- */ - public static final String DEFAULT_STRING_OUTPUT_TYPE = - CommonUtils.STRING_OUTPUT_TYPE_BASE64; - - - // If the config object set is a StringPBEConfig, it must be referenced - private StringPBEConfig stringPBEConfig = null; - - // This variable holds the type of String output which will be done, - // and also a boolean variable for faster comparison - private String stringOutputType = DEFAULT_STRING_OUTPUT_TYPE; - private boolean stringOutputTypeBase64 = true; - - - /* - * Set of booleans which indicate whether the config or default values - * have to be overriden because of the setX methods having been - * called. - */ - private boolean stringOutputTypeSet = false; - - - // The StandardPBEByteEncryptor that will be internally used. - private final StandardPBEByteEncryptor byteEncryptor; - - // BASE64 encoder which will make sure the returned results are - // valid US-ASCII strings. - // The Base64 encoder is THREAD-SAFE - private final Base64 base64; - - - - /** - * Creates a new instance of StandardPBEStringEncryptor. - */ - public StandardPBEStringEncryptor() { - super(); - this.byteEncryptor = new StandardPBEByteEncryptor(); - this.base64 = new Base64(); - } - - - - /* - * Creates a new instance of StandardPBEStringEncryptor using - * the specified byte encryptor (constructor used for cloning) - */ - private StandardPBEStringEncryptor(final StandardPBEByteEncryptor standardPBEByteEncryptor) { - super(); - this.byteEncryptor = standardPBEByteEncryptor; - this.base64 = new Base64(); - } - - - - /** - *

- * Sets a {@link PBEConfig} object - * for the encryptor. If this config - * object is set, it will be asked values for: - *

- * - *
    - *
  • Algorithm
  • - *
  • Security Provider (or provider name)
  • - *
  • Password
  • - *
  • Hashing iterations for obtaining the encryption key
  • - *
  • Salt generator
  • - *
  • Output type (base64, hexadecimal) - * (only StringPBEConfig)
  • - *
- * - *

- * The non-null values it returns will override the default ones, - * and will be overriden by any values specified with a setX - * method. - *

- * - * @param config the PBEConfig object to be used as the - * source for configuration parameters. - */ - public synchronized void setConfig(final PBEConfig config) { - this.byteEncryptor.setConfig(config); - if ((config != null) && (config instanceof StringPBEConfig)) { - this.stringPBEConfig = (StringPBEConfig) config; - } - } - - - /** - *

- * Sets the algorithm to be used for encryption, like - * PBEWithMD5AndDES. - *

- *

- * This algorithm has to be supported by your JCE provider (if you specify - * one, or the default JVM provider if you don't) and, if it is supported, - * you can also specify mode and padding for - * it, like ALGORITHM/MODE/PADDING. - *

- * - * @param algorithm the name of the algorithm to be used. - */ - public void setAlgorithm(final String algorithm) { - this.byteEncryptor.setAlgorithm(algorithm); - } - - - /** - *

- * Sets the password to be used. - *

- *

- * There is no default value for password, so not setting - * this parameter either from a - * {@link PBEConfig} object or from - * a call to setPassword will result in an - * EncryptionInitializationException being thrown during initialization. - *

- * - * @param password the password to be used. - */ - public void setPassword(final String password) { - this.byteEncryptor.setPassword(password); - } - - - /** - *

- * Sets the password to be used, as a char[]. - *

- *

- * This allows the password to be specified as a cleanable - * char[] instead of a String, in extreme security conscious environments - * in which no copy of the password as an immutable String should - * be kept in memory. - *

- *

- * Important: the array specified as a parameter WILL BE COPIED - * in order to be stored as encryptor configuration. The caller of - * this method will therefore be responsible for its cleaning (jasypt - * will only clean the internally stored copy). - *

- *

- * There is no default value for password, so not setting - * this parameter either from a - * {@link PBEConfig} object or from - * a call to setPassword will result in an - * EncryptionInitializationException being thrown during initialization. - *

- * - * @since 1.8 - * - * @param password the password to be used. - */ - public void setPasswordCharArray(char[] password) { - this.byteEncryptor.setPasswordCharArray(password); - } - - - /** - *

- * Set the number of hashing iterations applied to obtain the - * encryption key. - *

- *

- * This mechanism is explained in - * PKCS #5: Password-Based Cryptography Standard. - *

- * - * @param keyObtentionIterations the number of iterations - */ - public void setKeyObtentionIterations(final int keyObtentionIterations) { - this.byteEncryptor.setKeyObtentionIterations(keyObtentionIterations); - } - - - /** - *

- * Sets the salt generator to be used. If no salt generator is specified, - * an instance of {@link org.jasypt.salt.RandomSaltGenerator} will be used. - *

- * - * @param saltGenerator the salt generator to be used. - */ - public void setSaltGenerator(final SaltGenerator saltGenerator) { - this.byteEncryptor.setSaltGenerator(saltGenerator); - } - - /** - *

- * Sets the IV generator to be used. If no IV generator is specified, - * an instance of {@link org.jasypt.iv.NoIvGenerator} will be used. - *

- * - * @param ivGenerator the IV generator to be used. - */ - public void setIvGenerator(final IvGenerator ivGenerator) { - this.byteEncryptor.setIvGenerator(ivGenerator); - } - - - /** - *

- * Sets the name of the security provider to be asked for the - * encryption algorithm. This security provider has to be registered - * beforehand at the JVM security framework. - *

- *

- * The provider can also be set with the {@link #setProvider(Provider)} - * method, in which case it will not be necessary neither registering - * the provider beforehand, - * nor calling this {@link #setProviderName(String)} method to specify - * a provider name. - *

- *

- * Note that a call to {@link #setProvider(Provider)} overrides any value - * set by this method. - *

- *

- * If no provider name / provider is explicitly set, the default JVM - * provider will be used. - *

- * - * @since 1.3 - * - * @param providerName the name of the security provider to be asked - * for the encryption algorithm. - */ - public void setProviderName(final String providerName) { - this.byteEncryptor.setProviderName(providerName); - } - - - /** - *

- * Sets the security provider to be asked for the encryption algorithm. - * The provider does not have to be registered at the security - * infrastructure beforehand, and its being used here will not result in - * its being registered. - *

- *

- * If this method is called, calling {@link #setProviderName(String)} - * becomes unnecessary. - *

- *

- * If no provider name / provider is explicitly set, the default JVM - * provider will be used. - *

- * - * @since 1.3 - * - * @param provider the provider to be asked for the chosen algorithm - */ - public void setProvider(final Provider provider) { - this.byteEncryptor.setProvider(provider); - } - - - /** - *

- * Sets the the form in which String output - * will be encoded. Available encoding types are: - *

- *
    - *
  • base64 (default)
  • - *
  • hexadecimal
  • - *
- *

- * If not set, null will be returned. - *

- - * @since 1.3 - * - * @param stringOutputType the string output type. - */ - public synchronized void setStringOutputType(final String stringOutputType) { - CommonUtils.validateNotEmpty(stringOutputType, - "String output type cannot be set empty"); - if (isInitialized()) { - throw new AlreadyInitializedException(); - } - this.stringOutputType = - CommonUtils. - getStandardStringOutputType(stringOutputType); - - this.stringOutputTypeSet = true; - } - - - - - - - - - - - /* - * Clone this encryptor 'size' times and initialize it. - * This encryptor will be at position 0 itself. - * Clones will NOT be initialized. - */ - synchronized StandardPBEStringEncryptor[] cloneAndInitializeEncryptor(final int size) { - - final StandardPBEByteEncryptor[] byteEncryptorClones = - this.byteEncryptor.cloneAndInitializeEncryptor(size); - - initializeSpecifics(); - - final StandardPBEStringEncryptor[] clones = new StandardPBEStringEncryptor[size]; - - clones[0] = this; - - for (int i = 1; i < size; i++) { - clones[i] = new StandardPBEStringEncryptor(byteEncryptorClones[i]); - if (CommonUtils.isNotEmpty(this.stringOutputType)) { - clones[i].setStringOutputType(this.stringOutputType); - } - } - - return clones; - - } - - - - - /** - *

- * Returns true if the encryptor has already been initialized, false if - * not.
- * Initialization happens: - *

- *
    - *
  • When initialize is called.
  • - *
  • When encrypt or decrypt are called for the - * first time, if initialize has not been called before.
  • - *
- *

- * Once an encryptor has been initialized, trying to - * change its configuration will - * result in an AlreadyInitializedException being thrown. - *

- * - * @return true if the encryptor has already been initialized, false if - * not. - */ - public boolean isInitialized() { - return this.byteEncryptor.isInitialized(); - } - - - /** - *

- * Initialize the encryptor. - *

- *

- * This operation will consist in determining the actual configuration - * values to be used, and then initializing the encryptor with them. - *
- * These values are decided by applying the following priorities: - *

- *
    - *
  1. First, the default values are considered (except for password). - *
  2. - *
  3. Then, if a - * {@link PBEConfig} - * object has been set with - * setConfig, the non-null values returned by its - * getX methods override the default values.
  4. - *
  5. Finally, if the corresponding setX method has been called - * on the encryptor itself for any of the configuration parameters, - * the values set by these calls override all of the above.
  6. - *
- *

- * Once an encryptor has been initialized, trying to - * change its configuration will - * result in an AlreadyInitializedException being thrown. - *

- * - * @throws EncryptionInitializationException if initialization could not - * be correctly done (for example, no password has been set). - */ - public synchronized void initialize() { - - // Double-check to avoid synchronization issues - if (!this.isInitialized()) { - initializeSpecifics(); - this.byteEncryptor.initialize(); - } - - } - - - - - private void initializeSpecifics() { - /* - * If a StringPBEConfig object has been set, we need to - * consider the values it returns (if, for each value, the - * corresponding "setX" method has not been called). - */ - if (this.stringPBEConfig != null) { - - final String configStringOutputType = - this.stringPBEConfig.getStringOutputType(); - - this.stringOutputType = - ((this.stringOutputTypeSet) || (configStringOutputType == null))? - this.stringOutputType : configStringOutputType; - - } - - this.stringOutputTypeBase64 = - (CommonUtils.STRING_OUTPUT_TYPE_BASE64. - equalsIgnoreCase(this.stringOutputType)); - - } - - - /** - *

- * Encrypts a message using the specified configuration. - *

- *

- * The Strings returned by this method are BASE64-encoded (default) or - * HEXADECIMAL ASCII Strings. - *

- *

- * The mechanisms applied to perform the encryption operation are described - * in PKCS #5: Password-Based Cryptography Standard. - *

- *

- * This encryptor uses a salt for each encryption - * operation. The size of the salt depends on the algorithm - * being used. This salt is used - * for creating the encryption key and, if generated by a random generator, - * it is also appended unencrypted at the beginning - * of the results so that a decryption operation can be performed. - *

- *

- * If a random salt generator is used, two encryption results for - * the same message will always be different - * (except in the case of random salt coincidence). This may enforce - * security by difficulting brute force attacks on sets of data at a time - * and forcing attackers to perform a brute force attack on each separate - * piece of encrypted data. - *

- * - * @param message the String message to be encrypted - * @return the result of encryption - * @throws EncryptionOperationNotPossibleException if the encryption - * operation fails, ommitting any further information about the - * cause for security reasons. - * @throws EncryptionInitializationException if initialization could not - * be correctly done (for example, no password has been set). - */ - public String encrypt(final String message) { - - if (message == null) { - return null; - } - - // Check initialization - if (!isInitialized()) { - initialize(); - } - - try { - - // The input String is converted into bytes using MESSAGE_CHARSET - // as a fixed charset to avoid problems with different platforms - // having different default charsets (see MESSAGE_CHARSET doc). - final byte[] messageBytes = message.getBytes(MESSAGE_CHARSET); - - // The StandardPBEByteEncryptor does its job. - byte[] encryptedMessage = this.byteEncryptor.encrypt(messageBytes); - - // We encode the result in BASE64 or HEXADECIMAL so that we obtain - // the safest result String possible. - String result = null; - if (this.stringOutputTypeBase64) { - encryptedMessage = this.base64.encode(encryptedMessage); - result = new String(encryptedMessage,ENCRYPTED_MESSAGE_CHARSET); - } else { - result = CommonUtils.toHexadecimal(encryptedMessage); - } - - return result; - - } catch (EncryptionInitializationException e) { - throw e; - } catch (EncryptionOperationNotPossibleException e) { - throw e; - } catch (Exception e) { - // If encryption fails, it is more secure not to return any - // information about the cause in nested exceptions. Simply fail. - throw new EncryptionOperationNotPossibleException(); - } - - } - - - /** - *

- * Decrypts a message using the specified configuration. - *

- *

- * This method expects to receive a BASE64-encoded (default) - * or HEXADECIMAL ASCII String. - *

- *

- * The mechanisms applied to perform the decryption operation are described - * in PKCS #5: Password-Based Cryptography Standard. - *

- *

- * If a random salt generator is used, this decryption operation will - * expect to find an unencrypted salt at the - * beginning of the encrypted input, so that the decryption operation can be - * correctly performed (there is no other way of knowing it). - *

- * - * @param encryptedMessage the String message to be decrypted - * @return the result of decryption - * @throws EncryptionOperationNotPossibleException if the decryption - * operation fails, ommitting any further information about the - * cause for security reasons. - * @throws EncryptionInitializationException if initialization could not - * be correctly done (for example, no password has been set). - */ - public String decrypt(final String encryptedMessage) { - - if (encryptedMessage == null) { - return null; - } - - // Check initialization - if (!isInitialized()) { - initialize(); - } - - try { - - byte[] encryptedMessageBytes = null; - - // Decode input to bytes depending on whether it is a - // BASE64-encoded or hexadecimal String - if (this.stringOutputTypeBase64) { - encryptedMessageBytes = - encryptedMessage.getBytes(ENCRYPTED_MESSAGE_CHARSET); - encryptedMessageBytes = - this.base64.decode(encryptedMessageBytes); - } else { - encryptedMessageBytes = - CommonUtils.fromHexadecimal(encryptedMessage); - } - - // Let the byte encyptor decrypt - final byte[] message = this.byteEncryptor.decrypt(encryptedMessageBytes); - - // Return the resulting decrypted String, using MESSAGE_CHARSET - // as charset to maintain between encryption and decyption - // processes. - return new String(message, MESSAGE_CHARSET); - - } catch (EncryptionInitializationException e) { - throw e; - } catch (EncryptionOperationNotPossibleException e) { - throw e; - } catch (Exception e) { - // If decryption fails, it is more secure not to return any - // information about the cause in nested exceptions. Simply fail. - throw new EncryptionOperationNotPossibleException(); - } - - } - - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringEncryptor.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringEncryptor.java deleted file mode 100644 index 218be61a6f..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringEncryptor.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - - -/** - *

- * Common interface for all Encryptors which receive a - * String message and return a String result. - *

- * - * @since 1.0 - * - * @author Daniel Fernández - * - */ -public interface StringEncryptor -{ - - - /** - * Encrypt the input message - * - * @param message the message to be encrypted - * @return the result of encryption - */ - public String encrypt(String message); - - - /** - * Decrypt an encrypted message - * - * @param encryptedMessage the encrypted message to be decrypted - * @return the result of decryption - */ - public String decrypt(String encryptedMessage); - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringPBEConfig.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringPBEConfig.java deleted file mode 100644 index 1d48951745..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/StringPBEConfig.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * ============================================================================= - * - * Copyright (c) 2007-2010, The JASYPT team (http://www.jasypt.org) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * ============================================================================= - */ -package org.bouncycastle.jcajce.provider.test.jasypt; - - - -/** - *

- * Common interface for config classes applicable to - * StandardPBEStringEncrypto} objects. - * This interface extends {@link PBEConfig} to add config parameters specific - * to String encryption. - *

- *

- * This interface lets the user create new PBEConfig - * classes which retrieve values for this parameters from different - * (and maybe more secure) sources (remote servers, LDAP, other databases...), - * and do this transparently for the encryptor object. - *

- *

- * The config objects passed to an encryptor will only be queried once - * for each configuration parameter, and this will happen - * during the initialization of the encryptor object. - *

- *

- * For a default implementation, see SimpleStringPBEConfig. - *

- * - * @since 1.3 - * - * @author Daniel Fernández - * - */ -public interface StringPBEConfig - extends PBEConfig { - - - - /** - *

- * This parameter lets the user specify the form in which String output - * will be encoded. Available encoding types are: - *

- *
    - *
  • base64 (default)
  • - *
  • hexadecimal
  • - *
- * - * @return The name of the encoding type for String output - */ - public String getStringOutputType(); - - -} diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/TestJasypt.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/TestJasypt.java deleted file mode 100644 index bc36a3fb93..0000000000 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/jasypt/TestJasypt.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.bouncycastle.jcajce.provider.test.jasypt; - -import org.bouncycastle.jce.provider.BouncyCastleProvider; - -public class TestJasypt -{ - public static void main(String[] args) - { - StandardPBEStringEncryptor stringEncryptor = new StandardPBEStringEncryptor(); - stringEncryptor.setAlgorithm("PBEWITHSHA256AND256BITAES-CBC-BC"); - stringEncryptor.setPassword("secretPassword"); - stringEncryptor.setIvGenerator(new RandomIvGenerator()); - stringEncryptor.setProvider(new BouncyCastleProvider()); - - String encryptedText = stringEncryptor.encrypt("plainText"); - - StandardPBEStringEncryptor stringdecryptor = new StandardPBEStringEncryptor(); - stringdecryptor.setAlgorithm("PBEWITHSHA256AND256BITAES-CBC-BC"); - stringdecryptor.setPassword("secretPassword"); - stringdecryptor.setIvGenerator(new RandomIvGenerator()); - stringdecryptor.setProvider(new BouncyCastleProvider()); - - String decryptedText = stringdecryptor.decrypt(encryptedText); - System.out.println(decryptedText); - - } -} diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/PBETest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/PBETest.java index a566bb64fc..c7ea5a707b 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/PBETest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/PBETest.java @@ -564,6 +564,43 @@ private void testExtendedPBEParameterSpec() isTrue(Arrays.areEqual(input, decryptedBytes)); } + + private void testNoIvPBEParameterSpec() + throws Exception + { + String cipherAlgo = "PBEWITHSHA256AND256BITAES-CBC-BC"; + + SecureRandom random = new FixedSecureRandom(Hex.decode( + "000102030405060708090a0b0c0d0e0f" + + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf")); + + char[] password = "abcdefghijklmnop".toCharArray(); + PBEKeySpec pbeKeySpec = new PBEKeySpec(password); + + SecretKeyFactory factory = SecretKeyFactory.getInstance( + "PBEWITHSHA256AND256BITAES-CBC-BC", + "BC"); + SecretKey key = factory.generateSecret(pbeKeySpec); + + byte[] salt = new byte[16]; + random.nextBytes(salt); + // simulate the situation for issue #1985 + byte[] iv = new byte[0]; + + PBEParameterSpec pbeParamSpec = new PBEParameterSpec(salt, 1000, new IvParameterSpec(iv)); + + Cipher encryptCipher = Cipher.getInstance(cipherAlgo, "BC"); + Cipher decryptCipher = Cipher.getInstance(cipherAlgo, "BC"); + + encryptCipher.init(Cipher.ENCRYPT_MODE, key, pbeParamSpec); + decryptCipher.init(Cipher.DECRYPT_MODE, key, pbeParamSpec); + + byte[] input = Strings.toByteArray("testing"); + byte[] encryptedBytes = encryptCipher.doFinal(input); + byte[] decryptedBytes = decryptCipher.doFinal(encryptedBytes); + + isTrue(Arrays.areEqual(input, decryptedBytes)); + } public void performTest() throws Exception @@ -710,7 +747,7 @@ public void performTest() } testExtendedPBEParameterSpec(); - + testNoIvPBEParameterSpec(); testPKCS12Interop(); testPBEHMac("PBEWithHMacSHA1", hMac1); From 9706d9a364e56ad5c784ef1cd59f0351f5390722 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 10 Feb 2025 16:41:36 +1100 Subject: [PATCH 107/890] dealt with expiring certificate in test --- .../cms/test/GOSTR3410_2012_256CmsSignVerifyDetached.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/GOSTR3410_2012_256CmsSignVerifyDetached.java b/pkix/src/test/java/org/bouncycastle/cms/test/GOSTR3410_2012_256CmsSignVerifyDetached.java index a82ea627cd..6e1bb4e57c 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/GOSTR3410_2012_256CmsSignVerifyDetached.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/GOSTR3410_2012_256CmsSignVerifyDetached.java @@ -9,6 +9,7 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.HashSet; import java.util.List; @@ -121,9 +122,13 @@ private static boolean verifyDetached(byte[] data, byte[] detachedCms, // Validate signer's certificate chain X509CertSelector constraints = new X509CertSelector(); - constraints.setCertificate(getX509Certificate(signerCert)); + X509Certificate x509Certificate = getX509Certificate(signerCert); + constraints.setCertificate(x509Certificate); + PKIXBuilderParameters params = new PKIXBuilderParameters(trustAnchors, constraints); + params.setDate(new Date(x509Certificate.getNotAfter().getTime() - 5000L)); + JcaCertStoreBuilder certStoreBuilder = new JcaCertStoreBuilder(); certStoreBuilder.addCertificate(signerCert); From 0ca189294f5948fda42a48fc6b4e7f45d44f4399 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 10 Feb 2025 16:45:01 +1100 Subject: [PATCH 108/890] dealt with expiring certificate in test --- .../cms/test/GOSTR3410_2012_256CmsSignVerifyDetached.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/GOSTR3410_2012_256CmsSignVerifyDetached.java b/pkix/src/test/java/org/bouncycastle/cms/test/GOSTR3410_2012_256CmsSignVerifyDetached.java index a82ea627cd..91918de68b 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/GOSTR3410_2012_256CmsSignVerifyDetached.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/GOSTR3410_2012_256CmsSignVerifyDetached.java @@ -9,6 +9,7 @@ import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.HashSet; import java.util.List; @@ -121,9 +122,13 @@ private static boolean verifyDetached(byte[] data, byte[] detachedCms, // Validate signer's certificate chain X509CertSelector constraints = new X509CertSelector(); - constraints.setCertificate(getX509Certificate(signerCert)); + X509Certificate x509Certificate = getX509Certificate(signerCert); + constraints.setCertificate(x509Certificate); + PKIXBuilderParameters params = new PKIXBuilderParameters(trustAnchors, constraints); + params.setDate(new Date(x509Certificate.getNotAfter().getTime() - 5000L)); + JcaCertStoreBuilder certStoreBuilder = new JcaCertStoreBuilder(); certStoreBuilder.addCertificate(signerCert); From de1894d39e9795ae7364dcf4a368fe1df1351587 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 11 Feb 2025 16:17:28 +1100 Subject: [PATCH 109/890] Java 4 update --- ant/jdk14.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/ant/jdk14.xml b/ant/jdk14.xml index 70f04c6c8d..5fe4ae4802 100644 --- a/ant/jdk14.xml +++ b/ant/jdk14.xml @@ -214,6 +214,7 @@ + From 0b6d399806308b6ce16065bda83f0349218efbbf Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 11 Feb 2025 16:19:35 +1100 Subject: [PATCH 110/890] fixed checkstyle issue, initial experiment with ML-DSA seed files --- .../asn1/bc/BCObjectIdentifiers.java | 14 +++++ .../bouncycastle/pqc/crypto/util/Utils.java | 3 + .../jcajce/interfaces/MLDSAPrivateKey.java | 8 +++ .../asymmetric/mldsa/BCMLDSAPrivateKey.java | 15 +++++ .../asymmetric/mldsa/MLDSAKeyFactorySpi.java | 57 ++++++++++++++++--- .../provider/util/BaseKeyFactorySpi.java | 11 ++++ .../pqc/jcajce/provider/util/KeyUtil.java | 43 ++++++++++++++ .../pqc/jcajce/provider/test/MLDSATest.java | 1 + 8 files changed, 143 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java index 6c5db36169..c5753d7dc7 100644 --- a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java @@ -429,4 +429,18 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier hqc128 = pqc_kem_hqc.branch("1"); ASN1ObjectIdentifier hqc192 = pqc_kem_hqc.branch("2"); ASN1ObjectIdentifier hqc256 = pqc_kem_hqc.branch("3"); + + /** + * ML-KEM/ML-DSA seed parameters algorithms - temporary + * + */ + //TODO: delete before release + ASN1ObjectIdentifier id_id_alg_seed = bc.branch("10"); + + ASN1ObjectIdentifier id_id_alg_ml_dsa_44_seed = id_id_alg_seed.branch("1"); + ASN1ObjectIdentifier id_id_alg_ml_dsa_65_seed = id_id_alg_seed.branch("2"); + ASN1ObjectIdentifier id_id_alg_ml_dsa_87_seed = id_id_alg_seed.branch("3"); + ASN1ObjectIdentifier id_id_alg_ml_kem_512_seed = id_id_alg_seed.branch("4"); + ASN1ObjectIdentifier id_id_alg_ml_kem_768_seed = id_id_alg_seed.branch("5"); + ASN1ObjectIdentifier id_id_alg_ml_kem_1024_seed = id_id_alg_seed.branch("6"); } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java index ddc3e0867d..72cbc901c5 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java @@ -279,6 +279,9 @@ class Utils mldsaParams.put(NISTObjectIdentifiers.id_ml_dsa_44, MLDSAParameters.ml_dsa_44); mldsaParams.put(NISTObjectIdentifiers.id_ml_dsa_65, MLDSAParameters.ml_dsa_65); mldsaParams.put(NISTObjectIdentifiers.id_ml_dsa_87, MLDSAParameters.ml_dsa_87); + mldsaParams.put(BCObjectIdentifiers.id_id_alg_ml_dsa_44_seed, MLDSAParameters.ml_dsa_44); + mldsaParams.put(BCObjectIdentifiers.id_id_alg_ml_dsa_65_seed, MLDSAParameters.ml_dsa_65); + mldsaParams.put(BCObjectIdentifiers.id_id_alg_ml_dsa_87_seed, MLDSAParameters.ml_dsa_87); mldsaParams.put(NISTObjectIdentifiers.id_hash_ml_dsa_44_with_sha512, MLDSAParameters.ml_dsa_44_with_sha512); mldsaParams.put(NISTObjectIdentifiers.id_hash_ml_dsa_65_with_sha512, MLDSAParameters.ml_dsa_65_with_sha512); mldsaParams.put(NISTObjectIdentifiers.id_hash_ml_dsa_87_with_sha512, MLDSAParameters.ml_dsa_87_with_sha512); diff --git a/prov/src/main/java/org/bouncycastle/jcajce/interfaces/MLDSAPrivateKey.java b/prov/src/main/java/org/bouncycastle/jcajce/interfaces/MLDSAPrivateKey.java index d7ee863f26..52c58aca25 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/interfaces/MLDSAPrivateKey.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/interfaces/MLDSAPrivateKey.java @@ -25,4 +25,12 @@ public interface MLDSAPrivateKey * @return the seed for the private key, null if not available. */ byte[] getSeed(); + + /** + * Return the encoding of the key or an encoding of its key generation parameters (the seed). + * + * @param asKeyGenParams return a key gen parameters structure. + * @return a PKCS#8 of the private key encoding, or a PKCS#8 of the seed. + */ + byte[] getEncoded(boolean asKeyGenParams); } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.java index 42ba9ccb23..e581149380 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.java @@ -91,6 +91,21 @@ public final String getAlgorithm() return algorithm; } + public byte[] getEncoded(boolean asKeyGenParams) + { + if (asKeyGenParams) + { + byte[] seed = params.getSeed(); + if (seed == null) + { + return null; + } + return KeyUtil.getEncodedPrivateKeyInfo(getParameterSpec(), seed, attributes); + } + + return getEncoded(); + } + public byte[] getEncoded() { if (encoding == null) diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/MLDSAKeyFactorySpi.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/MLDSAKeyFactorySpi.java index 3a2ba11de0..fba0040d6a 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/MLDSAKeyFactorySpi.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/MLDSAKeyFactorySpi.java @@ -13,6 +13,7 @@ import java.util.Set; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.bc.BCObjectIdentifiers; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; @@ -44,14 +45,22 @@ public class MLDSAKeyFactorySpi hashKeyOids.add(NISTObjectIdentifiers.id_hash_ml_dsa_87_with_sha512); } + private final boolean isHashOnly; + public MLDSAKeyFactorySpi(Set keyOids) { super(keyOids); + + this.isHashOnly = false; } - public MLDSAKeyFactorySpi(ASN1ObjectIdentifier keyOid) + public MLDSAKeyFactorySpi(ASN1ObjectIdentifier keyOid, ASN1ObjectIdentifier seedOid) { - super(keyOid); + super(setOf(keyOid, seedOid)); + + this.isHashOnly = (keyOid.equals(NISTObjectIdentifiers.id_hash_ml_dsa_44_with_sha512) + || keyOid.equals(NISTObjectIdentifiers.id_hash_ml_dsa_65_with_sha512) + || keyOid.equals(NISTObjectIdentifiers.id_hash_ml_dsa_87_with_sha512)); } public final KeySpec engineGetKeySpec(Key key, Class keySpec) @@ -165,7 +174,37 @@ public PublicKey engineGeneratePublic( public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) throws IOException { - return new BCMLDSAPrivateKey(keyInfo); + BCMLDSAPrivateKey key = new BCMLDSAPrivateKey(keyInfo); + + if (!isHashOnly || (key.getAlgorithm().indexOf("WITH") > 0)) + { + return key; + } + + // keyfactory for hash-only, convert key to hash-only. + MLDSAPrivateKeyParameters kParams = key.getKeyParams(); + MLDSAParameters mldsaParameters = null; + if (kParams.getParameters().equals(MLDSAParameters.ml_dsa_44)) + { + mldsaParameters = MLDSAParameters.ml_dsa_44_with_sha512; + } + else if (kParams.getParameters().equals(MLDSAParameters.ml_dsa_65)) + { + mldsaParameters = MLDSAParameters.ml_dsa_65_with_sha512; + } + else if (kParams.getParameters().equals(MLDSAParameters.ml_dsa_87)) + { + mldsaParameters = MLDSAParameters.ml_dsa_87_with_sha512; + } + else + { + throw new IllegalStateException("unknown ML-DSA parameters"); + } + + MLDSAPrivateKeyParameters hkParams = new MLDSAPrivateKeyParameters( + mldsaParameters, kParams.getRho(), kParams.getK(), kParams.getTr(), kParams.getS1(), kParams.getS2(), kParams.getT0(), kParams.getT1(), kParams.getSeed()); + + return new BCMLDSAPrivateKey(hkParams); } public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) @@ -188,7 +227,7 @@ public static class MLDSA44 { public MLDSA44() { - super(NISTObjectIdentifiers.id_ml_dsa_44); + super(NISTObjectIdentifiers.id_ml_dsa_44, BCObjectIdentifiers.id_id_alg_ml_dsa_44_seed); } } @@ -197,7 +236,7 @@ public static class MLDSA65 { public MLDSA65() { - super(NISTObjectIdentifiers.id_ml_dsa_65); + super(NISTObjectIdentifiers.id_ml_dsa_65, BCObjectIdentifiers.id_id_alg_ml_dsa_65_seed); } } @@ -206,7 +245,7 @@ public static class MLDSA87 { public MLDSA87() { - super(NISTObjectIdentifiers.id_ml_dsa_87); + super(NISTObjectIdentifiers.id_ml_dsa_87, BCObjectIdentifiers.id_id_alg_ml_dsa_87_seed); } } @@ -224,7 +263,7 @@ public static class HashMLDSA44 { public HashMLDSA44() { - super(NISTObjectIdentifiers.id_hash_ml_dsa_44_with_sha512); + super(NISTObjectIdentifiers.id_hash_ml_dsa_44_with_sha512, BCObjectIdentifiers.id_id_alg_ml_dsa_44_seed); } } @@ -233,7 +272,7 @@ public static class HashMLDSA65 { public HashMLDSA65() { - super(NISTObjectIdentifiers.id_hash_ml_dsa_65_with_sha512); + super(NISTObjectIdentifiers.id_hash_ml_dsa_65_with_sha512, BCObjectIdentifiers.id_id_alg_ml_dsa_65_seed); } } @@ -242,7 +281,7 @@ public static class HashMLDSA87 { public HashMLDSA87() { - super(NISTObjectIdentifiers.id_hash_ml_dsa_87_with_sha512); + super(NISTObjectIdentifiers.id_hash_ml_dsa_87_with_sha512, BCObjectIdentifiers.id_id_alg_ml_dsa_87_seed); } } } diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/BaseKeyFactorySpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/BaseKeyFactorySpi.java index 52433bc59b..7abce93b22 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/BaseKeyFactorySpi.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/BaseKeyFactorySpi.java @@ -7,6 +7,7 @@ import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; +import java.util.HashSet; import java.util.Set; import org.bouncycastle.asn1.ASN1ObjectIdentifier; @@ -33,6 +34,16 @@ protected BaseKeyFactorySpi(ASN1ObjectIdentifier keyOid) this.keyOids = null; } + protected static Set setOf(ASN1ObjectIdentifier oid1, ASN1ObjectIdentifier oid2) + { + Set hashSet = new HashSet(2); + + hashSet.add(oid1); + hashSet.add(oid2); + + return hashSet; + } + public PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException { diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/KeyUtil.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/KeyUtil.java index e0b2645418..e2dc120eec 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/KeyUtil.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/KeyUtil.java @@ -1,12 +1,16 @@ package org.bouncycastle.pqc.jcajce.provider.util; +import java.security.spec.AlgorithmParameterSpec; + import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.bc.BCObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.jcajce.spec.MLDSAParameterSpec; import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory; import org.bouncycastle.pqc.crypto.util.SubjectPublicKeyInfoFactory; @@ -91,6 +95,45 @@ public static byte[] getEncodedPrivateKeyInfo(PrivateKeyInfo info) } } + public static byte[] getEncodedPrivateKeyInfo(AlgorithmParameterSpec paramSpec, byte[] seed, ASN1Set attributes) + { + byte[] enc = null; + + try + { + if (paramSpec instanceof MLDSAParameterSpec) + { + String name = ((MLDSAParameterSpec)paramSpec).getName(); + if (name.equals(MLDSAParameterSpec.ml_dsa_44.getName()) || name.equals(MLDSAParameterSpec.ml_dsa_44_with_sha512.getName())) + { + enc = new PrivateKeyInfo(new AlgorithmIdentifier(BCObjectIdentifiers.id_id_alg_ml_dsa_44_seed), seed, attributes).getEncoded(); + } + else if (name.equals(MLDSAParameterSpec.ml_dsa_65.getName()) || name.equals(MLDSAParameterSpec.ml_dsa_65_with_sha512.getName())) + { + enc = new PrivateKeyInfo(new AlgorithmIdentifier(BCObjectIdentifiers.id_id_alg_ml_dsa_65_seed), seed, attributes).getEncoded(); + } + else if (name.equals(MLDSAParameterSpec.ml_dsa_87.getName()) || name.equals(MLDSAParameterSpec.ml_dsa_87_with_sha512.getName())) + { + enc = new PrivateKeyInfo(new AlgorithmIdentifier(BCObjectIdentifiers.id_id_alg_ml_dsa_87_seed), seed, attributes).getEncoded(); + } + else + { + throw new IllegalStateException("unknown ML-DSA algorithm"); + } + } + } + catch (IllegalStateException e) + { + throw e; + } + catch (Exception e) + { + return null; + } + + return enc; + } + public static byte[] getEncodedPrivateKeyInfo(AsymmetricKeyParameter privateKey, ASN1Set attributes) { if (!privateKey.isPrivate()) diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java index e0f4acde87..b285cf0591 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java @@ -124,6 +124,7 @@ private void tryKeyFact(KeyFactory kFact, KeyPair kpValid, KeyPair kpInvalid, St throws Exception { kFact.generatePrivate(new PKCS8EncodedKeySpec(kpValid.getPrivate().getEncoded())); + kFact.generatePrivate(new PKCS8EncodedKeySpec(((MLDSAPrivateKey)kpValid.getPrivate()).getEncoded(true))); kFact.generatePublic(new X509EncodedKeySpec(kpValid.getPublic().getEncoded())); try From 7c22ce9d0622c183fdeec9a753aaaaa85d483e1a Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 11 Feb 2025 16:19:51 +1100 Subject: [PATCH 111/890] fixed checkstyle issue, initial experiment with ML-DSA seed files --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index 9167ea9e13..aa7eea0bdb 100644 --- a/build.gradle +++ b/build.gradle @@ -253,7 +253,10 @@ subprojects { } nohttp { + source.exclude '**/*.asc' + source.exclude '**/*.pem' source.exclude '**/*.rsp' + source.exclude '**/*.jar' } jacocoTestReport { From e0cc328b166a7b4f7d56e4f5a5d4255b0008cbf8 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 13 Feb 2025 17:40:48 +1030 Subject: [PATCH 112/890] Add setProvider in JcaSimpleSignerInfoVerifierBuilder for PQCSignedDataTest.testLmsEncapsulated --- .../test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java index 4383e9813d..de19d395e0 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java @@ -337,7 +337,7 @@ public void testLmsEncapsulated() Iterator certIt = certCollection.iterator(); X509CertificateHolder cert = (X509CertificateHolder)certIt.next(); - assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert))); + assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert))); // // check content digest From de7af471b80152b362cf8f981c166935e6b57cec Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Thu, 13 Feb 2025 13:23:31 +0100 Subject: [PATCH 113/890] SecretKeyPacket: Properly pass newPacketFormat down to PublicKeyPacket --- pg/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java index d2f9f8873c..9b59fd4f5c 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java @@ -146,11 +146,11 @@ public class SecretKeyPacket if (this instanceof SecretSubkeyPacket) { - pubKeyPacket = new PublicSubkeyPacket(in); + pubKeyPacket = new PublicSubkeyPacket(in, newPacketFormat); } else { - pubKeyPacket = new PublicKeyPacket(in); + pubKeyPacket = new PublicKeyPacket(in, newPacketFormat); } int version = pubKeyPacket.getVersion(); @@ -342,7 +342,7 @@ public SecretKeyPacket( byte[] iv, byte[] secKeyData) { - super(keyTag); + super(keyTag, pubKeyPacket.hasNewPacketFormat()); this.pubKeyPacket = pubKeyPacket; this.encAlgorithm = encAlgorithm; From a3bde4e091f17edc40d2f4eb4a8d514826bef2f3 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 13 Feb 2025 20:44:37 +0700 Subject: [PATCH 114/890] PKIX: refactoring around CertPath validation - reduced allocations - improved policy node removal logic --- .../jcajce/PKIXCRLStoreSelector.java | 49 ++- .../provider/CertPathValidatorUtilities.java | 298 ++++++------- .../jce/provider/PKIXPolicyNode.java | 61 ++- .../provider/RFC3280CertPathUtilities.java | 390 +++++++----------- 4 files changed, 315 insertions(+), 483 deletions(-) diff --git a/prov/src/main/java/org/bouncycastle/jcajce/PKIXCRLStoreSelector.java b/prov/src/main/java/org/bouncycastle/jcajce/PKIXCRLStoreSelector.java index e7dbcce0f8..b82b7ca616 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/PKIXCRLStoreSelector.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/PKIXCRLStoreSelector.java @@ -185,45 +185,44 @@ public boolean match(CRL obj) } X509CRL crl = (X509CRL)obj; - ASN1Integer dci = null; - try + + // TODO[pkix] Do we always need to parse the Delta CRL Indicator extension? { - byte[] bytes = crl - .getExtensionValue(Extension.deltaCRLIndicator.getId()); - if (bytes != null) + ASN1Integer baseCRLNumber = null; + try { - dci = ASN1Integer.getInstance(ASN1OctetString.getInstance(bytes).getOctets()); + byte[] dci = crl.getExtensionValue(Extension.deltaCRLIndicator.getId()); + if (dci != null) + { + baseCRLNumber = ASN1Integer.getInstance(ASN1OctetString.getInstance(dci).getOctets()); + } } - } - catch (Exception e) - { - return false; - } - if (isDeltaCRLIndicatorEnabled()) - { - if (dci == null) + catch (Exception e) { return false; } - } - if (isCompleteCRLEnabled()) - { - if (dci != null) + + if (baseCRLNumber == null) { - return false; + if (isDeltaCRLIndicatorEnabled()) + { + return false; + } } - } - if (dci != null) - { - - if (maxBaseCRLNumber != null) + else { - if (dci.getPositiveValue().compareTo(maxBaseCRLNumber) == 1) + if (isCompleteCRLEnabled()) + { + return false; + } + + if (maxBaseCRLNumber != null && baseCRLNumber.getPositiveValue().compareTo(maxBaseCRLNumber) == 1) { return false; } } } + if (issuingDistributionPointEnabled) { byte[] idp = crl diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java b/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java index efb6ff6053..37f269b35f 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java @@ -413,34 +413,65 @@ protected static final Set getQualifierSet(ASN1Sequence qualifiers) return pq; } - protected static PKIXPolicyNode removePolicyNode( - PKIXPolicyNode validPolicyTree, - List[] policyNodes, - PKIXPolicyNode _node) + static PKIXPolicyNode removeChildlessPolicyNodes(PKIXPolicyNode validPolicyTree, List[] policyNodes, int depthLimit) { - PKIXPolicyNode _parent = (PKIXPolicyNode)_node.getParent(); - if (validPolicyTree == null) { return null; } - if (_parent == null) + int i = depthLimit; + while (--i >= 0) { - for (int j = 0; j < policyNodes.length; j++) + List nodes_i = policyNodes[i]; + + int j = nodes_i.size(); + while (--j >= 0) { - policyNodes[j] = new ArrayList(); + PKIXPolicyNode node_j = (PKIXPolicyNode)nodes_i.get(j); + + if (node_j.hasChildren()) + { + continue; + } + + nodes_i.remove(j); + + PKIXPolicyNode parent = (PKIXPolicyNode)node_j.getParent(); + if (parent == null) + { + return null; + } + + parent.removeChild(node_j); } + } + return validPolicyTree; + } + + static PKIXPolicyNode removePolicyNode(PKIXPolicyNode validPolicyTree, List[] policyNodes, PKIXPolicyNode node) + { + if (validPolicyTree == null) + { return null; } - else + + PKIXPolicyNode parent = (PKIXPolicyNode)node.getParent(); + if (parent == null) { - _parent.removeChild(_node); - removePolicyNodeRecurse(policyNodes, _node); + for (int j = 0; j < policyNodes.length; j++) + { + policyNodes[j].clear(); + } - return validPolicyTree; + return null; } + + parent.removeChild(node); + removePolicyNodeRecurse(policyNodes, node); + + return validPolicyTree; } private static void removePolicyNodeRecurse( @@ -496,160 +527,21 @@ protected static boolean processCertD1i( return false; } - protected static void processCertD1ii( - int index, - List[] policyNodes, - ASN1ObjectIdentifier _poid, - Set _pq) + static void processCertD1ii(int index, List[] policyNodes, ASN1ObjectIdentifier _poid, Set _pq) { - List policyNodeVec = policyNodes[index - 1]; - - for (int j = 0; j < policyNodeVec.size(); j++) + PKIXPolicyNode anyPolicyNode = findValidPolicy(policyNodes[index - 1].iterator(), ANY_POLICY); + if (anyPolicyNode != null) { - PKIXPolicyNode _node = (PKIXPolicyNode)policyNodeVec.get(j); - - if (ANY_POLICY.equals(_node.getValidPolicy())) - { - Set _childExpectedPolicies = new HashSet(); - _childExpectedPolicies.add(_poid.getId()); - - PKIXPolicyNode _child = new PKIXPolicyNode(new ArrayList(), - index, - _childExpectedPolicies, - _node, - _pq, - _poid.getId(), - false); - _node.addChild(_child); - policyNodes[index].add(_child); - return; - } - } - } - - protected static void prepareNextCertB1( - int i, - List[] policyNodes, - String id_p, - Map m_idp, - X509Certificate cert - ) - throws AnnotatedException, CertPathValidatorException - { - boolean idp_found = false; - Iterator nodes_i = policyNodes[i].iterator(); - while (nodes_i.hasNext()) - { - PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next(); - if (node.getValidPolicy().equals(id_p)) - { - idp_found = true; - node.expectedPolicies = (Set)m_idp.get(id_p); - break; - } - } - - if (!idp_found) - { - nodes_i = policyNodes[i].iterator(); - while (nodes_i.hasNext()) - { - PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next(); - if (ANY_POLICY.equals(node.getValidPolicy())) - { - Set pq = null; - ASN1Sequence policies = null; - try - { - policies = DERSequence.getInstance(getExtensionValue(cert, CERTIFICATE_POLICIES)); - } - catch (Exception e) - { - throw new AnnotatedException("Certificate policies cannot be decoded.", e); - } - Enumeration e = policies.getObjects(); - while (e.hasMoreElements()) - { - PolicyInformation pinfo = null; - - try - { - pinfo = PolicyInformation.getInstance(e.nextElement()); - } - catch (Exception ex) - { - throw new AnnotatedException("Policy information cannot be decoded.", ex); - } - if (ANY_POLICY.equals(pinfo.getPolicyIdentifier().getId())) - { - try - { - pq = getQualifierSet(pinfo.getPolicyQualifiers()); - } - catch (CertPathValidatorException ex) - { - throw new ExtCertPathValidatorException( - "Policy qualifier info set could not be built.", ex); - } - break; - } - } - boolean ci = false; - if (cert.getCriticalExtensionOIDs() != null) - { - ci = cert.getCriticalExtensionOIDs().contains(CERTIFICATE_POLICIES); - } + String policy = _poid.getId(); - PKIXPolicyNode p_node = (PKIXPolicyNode)node.getParent(); - if (ANY_POLICY.equals(p_node.getValidPolicy())) - { - PKIXPolicyNode c_node = new PKIXPolicyNode( - new ArrayList(), i, - (Set)m_idp.get(id_p), - p_node, pq, id_p, ci); - p_node.addChild(c_node); - policyNodes[i].add(c_node); - } - break; - } - } - } - } + Set _childExpectedPolicies = new HashSet(); + _childExpectedPolicies.add(policy); - protected static PKIXPolicyNode prepareNextCertB2( - int i, - List[] policyNodes, - String id_p, - PKIXPolicyNode validPolicyTree) - { - Iterator nodes_i = policyNodes[i].iterator(); - while (nodes_i.hasNext()) - { - PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next(); - if (node.getValidPolicy().equals(id_p)) - { - PKIXPolicyNode p_node = (PKIXPolicyNode)node.getParent(); - p_node.removeChild(node); - nodes_i.remove(); - for (int k = (i - 1); k >= 0; k--) - { - List nodes = policyNodes[k]; - for (int l = 0; l < nodes.size(); l++) - { - PKIXPolicyNode node2 = (PKIXPolicyNode)nodes.get(l); - if (!node2.hasChildren()) - { - validPolicyTree = removePolicyNode(validPolicyTree, policyNodes, node2); - if (validPolicyTree == null) - { - break; - } - } - } - } - } + PKIXPolicyNode _child = new PKIXPolicyNode(new ArrayList(), index, _childExpectedPolicies, anyPolicyNode, + _pq, policy, false); + anyPolicyNode.addChild(_child); + policyNodes[index].add(_child); } - return validPolicyTree; } protected static boolean isAnyPolicy( @@ -981,10 +873,7 @@ else if (!PrincipalUtils.getEncodedIssuerPrincipal(cert).equals(PrincipalUtils.g ASN1Enumerated reasonCode = null; if (crl_entry.hasExtensions()) { - if (crl_entry.hasUnsupportedCriticalExtension()) - { - throw new AnnotatedException("CRL entry has unsupported critical extensions."); - } + checkCRLEntryCriticalExtensions(crl_entry, "CRL entry has unsupported critical extensions."); try { @@ -1079,6 +968,9 @@ protected static Set getDeltaCRLs(Date validityDate, // 5.2.4 (c) selBuilder.setMaxBaseCRLNumber(completeCRLNumber); + // TODO[pkix] Would adding this to the selector be helpful? + //selBuilder.setDeltaCRLIndicatorEnabled(true); + PKIXCRLStoreSelector deltaSelect = selBuilder.build(); // find delta CRLs @@ -1132,7 +1024,7 @@ protected static Set getDeltaCRLs(Date validityDate, } } } - + Set result = new HashSet(); for (Iterator it = temp.iterator(); it.hasNext(); ) @@ -1150,14 +1042,7 @@ protected static Set getDeltaCRLs(Date validityDate, private static boolean isDeltaCRL(X509CRL crl) { - Set critical = crl.getCriticalExtensionOIDs(); - - if (critical == null) - { - return false; - } - - return critical.contains(RFC3280CertPathUtilities.DELTA_CRL_INDICATOR); + return hasCriticalExtension(crl, RFC3280CertPathUtilities.DELTA_CRL_INDICATOR); } /** @@ -1415,4 +1300,67 @@ static void checkCRLsNotEmpty(PKIXCertRevocationCheckerParameters params, Set cr } } } + + static void checkCRLCriticalExtensions(X509CRL crl, String exceptionMessage) + throws AnnotatedException + { + Set criticalExtensions = crl.getCriticalExtensionOIDs(); + if (criticalExtensions != null) + { + int count = criticalExtensions.size(); + if (count > 0) + { + if (criticalExtensions.contains(Extension.issuingDistributionPoint.getId())) + { + --count; + } + if (criticalExtensions.contains(Extension.deltaCRLIndicator.getId())) + { + --count; + } + + if (count > 0) + { + throw new AnnotatedException(exceptionMessage); + } + } + } + } + + static void checkCRLEntryCriticalExtensions(X509CRLEntry crlEntry, String exceptionMessage) + throws AnnotatedException + { + if (crlEntry.hasUnsupportedCriticalExtension()) + { + throw new AnnotatedException(exceptionMessage); + } + } + + static PKIXPolicyNode findValidPolicy(Iterator policyNodes, String policy) + { + while (policyNodes.hasNext()) + { + PKIXPolicyNode node = (PKIXPolicyNode)policyNodes.next(); + if (policy.equals(node.getValidPolicy())) + { + return node; + } + } + return null; + } + + static boolean hasCriticalExtension(X509Certificate cert, String extensionOID) + { + return hasCriticalExtension(cert.getCriticalExtensionOIDs(), extensionOID); + } + + static boolean hasCriticalExtension(X509CRL crl, String extensionOID) + { + return hasCriticalExtension(crl.getCriticalExtensionOIDs(), extensionOID); + } + + private static boolean hasCriticalExtension(Set criticalExtensionOIDs, String extensionOID) + { + return criticalExtensionOIDs != null && criticalExtensionOIDs.contains(extensionOID); + } } diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/PKIXPolicyNode.java b/prov/src/main/java/org/bouncycastle/jce/provider/PKIXPolicyNode.java index d89e920dff..d54aa2d25b 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/PKIXPolicyNode.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/PKIXPolicyNode.java @@ -53,37 +53,37 @@ public Iterator getChildren() { return children.iterator(); } - + public int getDepth() { return depth; } - + public Set getExpectedPolicies() { return expectedPolicies; } - + public PolicyNode getParent() { return parent; } - + public Set getPolicyQualifiers() { return policyQualifiers; } - + public String getValidPolicy() { return validPolicy; } - + public boolean hasChildren() { return !children.isEmpty(); } - + public boolean isCritical() { return critical; @@ -98,7 +98,12 @@ public void setCritical(boolean _critical) { critical = _critical; } - + + public void setExpectedPolicies(Set expectedPolicies) + { + this.expectedPolicies = expectedPolicies; + } + public void setParent(PKIXPolicyNode _parent) { parent = _parent; @@ -125,49 +130,39 @@ public String toString(String _indent) _buf.append("}\n"); return _buf.toString(); } - + + // TODO[api] Maybe remove this, the 'clone' loses its parent public Object clone() { return copy(); } - + public PKIXPolicyNode copy() { - Set _expectedPolicies = new HashSet(); + Set _expectedPolicies = new HashSet(); Iterator _iter = expectedPolicies.iterator(); while (_iter.hasNext()) { - _expectedPolicies.add(new String((String)_iter.next())); + _expectedPolicies.add(_iter.next()); } - - Set _policyQualifiers = new HashSet(); + + Set _policyQualifiers = new HashSet(); _iter = policyQualifiers.iterator(); while (_iter.hasNext()) { - _policyQualifiers.add(new String((String)_iter.next())); + _policyQualifiers.add(_iter.next()); } - - PKIXPolicyNode _node = new PKIXPolicyNode(new ArrayList(), - depth, - _expectedPolicies, - null, - _policyQualifiers, - new String(validPolicy), - critical); - + + PKIXPolicyNode copy = new PKIXPolicyNode(new ArrayList(), depth, _expectedPolicies, null, _policyQualifiers, + validPolicy, critical); + _iter = children.iterator(); while (_iter.hasNext()) { - PKIXPolicyNode _child = ((PKIXPolicyNode)_iter.next()).copy(); - _child.setParent(_node); - _node.addChild(_child); + PKIXPolicyNode child = (PKIXPolicyNode)_iter.next(); + copy.addChild(child.copy()); } - - return _node; - } - public void setExpectedPolicies(Set expectedPolicies) - { - this.expectedPolicies = expectedPolicies; + return copy; } } diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java b/prov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java index befb795385..09c146283c 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/RFC3280CertPathUtilities.java @@ -808,176 +808,148 @@ protected static PKIXPolicyNode prepareCertB( int i = n - index; // (b) // - ASN1Sequence pm = null; + ASN1Sequence mappings; try { - pm = ASN1Sequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert, - RFC3280CertPathUtilities.POLICY_MAPPINGS)); + mappings = ASN1Sequence.getInstance(CertPathValidatorUtilities.getExtensionValue(cert, POLICY_MAPPINGS)); } catch (AnnotatedException ex) { throw new ExtCertPathValidatorException("Policy mappings extension could not be decoded.", ex, certPath, index); } - PKIXPolicyNode _validPolicyTree = validPolicyTree; - if (pm != null) + + if (mappings != null) { - ASN1Sequence mappings = (ASN1Sequence)pm; - Map m_idp = new HashMap(); - Set s_idp = new HashSet(); + HashMap m_idp = new HashMap(); for (int j = 0; j < mappings.size(); j++) { ASN1Sequence mapping = (ASN1Sequence)mappings.getObjectAt(j); String id_p = ((ASN1ObjectIdentifier)mapping.getObjectAt(0)).getId(); String sd_p = ((ASN1ObjectIdentifier)mapping.getObjectAt(1)).getId(); - Set tmp; - if (!m_idp.containsKey(id_p)) + HashSet tmp = (HashSet)m_idp.get(id_p); + if (tmp == null) { tmp = new HashSet(); - tmp.add(sd_p); m_idp.put(id_p, tmp); - s_idp.add(id_p); - } - else - { - tmp = (Set)m_idp.get(id_p); - tmp.add(sd_p); } + + tmp.add(sd_p); } - Iterator it_idp = s_idp.iterator(); + Iterator it_idp = m_idp.entrySet().iterator(); while (it_idp.hasNext()) { - String id_p = (String)it_idp.next(); + Map.Entry e_idp = (Map.Entry)it_idp.next(); + + String id_p = (String)e_idp.getKey(); + HashSet expectedPolicies = (HashSet)e_idp.getValue(); // - // (1) + // (2) // - if (policyMapping > 0) + if (policyMapping <= 0) { - boolean idp_found = false; - Iterator nodes_i = policyNodes[i].iterator(); - while (nodes_i.hasNext()) + List nodes_i = policyNodes[i]; + + int j = nodes_i.size(); + while (--j >= 0) { - PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next(); - if (node.getValidPolicy().equals(id_p)) + PKIXPolicyNode node_j = (PKIXPolicyNode)nodes_i.get(j); + if (node_j.getValidPolicy().equals(id_p)) { - idp_found = true; - node.expectedPolicies = (Set)m_idp.get(id_p); - break; + PKIXPolicyNode p_node = (PKIXPolicyNode)node_j.getParent(); + p_node.removeChild(node_j); + nodes_i.remove(j); } } - if (!idp_found) - { - nodes_i = policyNodes[i].iterator(); - while (nodes_i.hasNext()) - { - PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next(); - if (RFC3280CertPathUtilities.ANY_POLICY.equals(node.getValidPolicy())) - { - Set pq = null; - ASN1Sequence policies = null; - try - { - policies = (ASN1Sequence)CertPathValidatorUtilities.getExtensionValue(cert, - RFC3280CertPathUtilities.CERTIFICATE_POLICIES); - } - catch (AnnotatedException e) - { - throw new ExtCertPathValidatorException( - "Certificate policies extension could not be decoded.", e, certPath, index); - } - Enumeration e = policies.getObjects(); - while (e.hasMoreElements()) - { - PolicyInformation pinfo = null; - try - { - pinfo = PolicyInformation.getInstance(e.nextElement()); - } - catch (Exception ex) - { - throw new CertPathValidatorException( - "Policy information could not be decoded.", ex, certPath, index); - } - if (RFC3280CertPathUtilities.ANY_POLICY.equals(pinfo.getPolicyIdentifier().getId())) - { - try - { - pq = CertPathValidatorUtilities - .getQualifierSet(pinfo.getPolicyQualifiers()); - } - catch (CertPathValidatorException ex) - { - - throw new ExtCertPathValidatorException( - "Policy qualifier info set could not be decoded.", ex, certPath, - index); - } - break; - } - } - boolean ci = false; - if (cert.getCriticalExtensionOIDs() != null) - { - ci = cert.getCriticalExtensionOIDs().contains( - RFC3280CertPathUtilities.CERTIFICATE_POLICIES); - } + validPolicyTree = CertPathValidatorUtilities.removeChildlessPolicyNodes(validPolicyTree, + policyNodes, i); - PKIXPolicyNode p_node = (PKIXPolicyNode)node.getParent(); - if (RFC3280CertPathUtilities.ANY_POLICY.equals(p_node.getValidPolicy())) - { - PKIXPolicyNode c_node = new PKIXPolicyNode(new ArrayList(), i, (Set)m_idp - .get(id_p), p_node, pq, id_p, ci); - p_node.addChild(c_node); - policyNodes[i].add(c_node); - } - break; - } - } - } + continue; + } - // - // (2) - // + // + // (1) + // +// assert policyMapping > 0; + + PKIXPolicyNode validPolicyNode = CertPathValidatorUtilities.findValidPolicy( + policyNodes[i].iterator(), id_p); + + if (validPolicyNode != null) + { + validPolicyNode.setExpectedPolicies(expectedPolicies); + continue; + } + + PKIXPolicyNode anyPolicyNode = CertPathValidatorUtilities.findValidPolicy( + policyNodes[i].iterator(), ANY_POLICY); + + if (anyPolicyNode == null) + { + continue; + } + + ASN1Sequence policies; + try + { + policies = ASN1Sequence.getInstance( + CertPathValidatorUtilities.getExtensionValue(cert, CERTIFICATE_POLICIES)); + } + catch (AnnotatedException e) + { + throw new ExtCertPathValidatorException( + "Certificate policies extension could not be decoded.", e, certPath, index); } - else if (policyMapping <= 0) + + Set pq = null; + + Enumeration e = policies.getObjects(); + while (e.hasMoreElements()) { - Iterator nodes_i = policyNodes[i].iterator(); - while (nodes_i.hasNext()) + PolicyInformation policyInformation; + try + { + policyInformation = PolicyInformation.getInstance(e.nextElement()); + } + catch (Exception ex) { - PKIXPolicyNode node = (PKIXPolicyNode)nodes_i.next(); - if (node.getValidPolicy().equals(id_p)) + throw new CertPathValidatorException("Policy information could not be decoded.", ex, certPath, + index); + } + + if (ANY_POLICY.equals(policyInformation.getPolicyIdentifier().getId())) + { + try { - PKIXPolicyNode p_node = (PKIXPolicyNode)node.getParent(); - p_node.removeChild(node); - nodes_i.remove(); - for (int k = (i - 1); k >= 0; k--) - { - List nodes = policyNodes[k]; - for (int l = 0; l < nodes.size(); l++) - { - PKIXPolicyNode node2 = (PKIXPolicyNode)nodes.get(l); - if (!node2.hasChildren()) - { - _validPolicyTree = CertPathValidatorUtilities.removePolicyNode( - _validPolicyTree, policyNodes, node2); - if (_validPolicyTree == null) - { - break; - } - } - } - } + pq = CertPathValidatorUtilities.getQualifierSet(policyInformation.getPolicyQualifiers()); } + catch (CertPathValidatorException ex) + { + throw new ExtCertPathValidatorException("Policy qualifier info set could not be decoded.", + ex, certPath, index); + } + break; } } + + boolean critical = CertPathValidatorUtilities.hasCriticalExtension(cert, CERTIFICATE_POLICIES); + + PKIXPolicyNode p_node = (PKIXPolicyNode)anyPolicyNode.getParent(); + if (ANY_POLICY.equals(p_node.getValidPolicy())) + { + PKIXPolicyNode c_node = new PKIXPolicyNode(new ArrayList(), i, expectedPolicies, p_node, pq, id_p, + critical); + p_node.addChild(c_node); + policyNodes[i].add(c_node); + } } } - return _validPolicyTree; + return validPolicyTree; } protected static void prepareNextCertA( @@ -1322,20 +1294,10 @@ else if (_tmp instanceof ASN1ObjectIdentifier) continue; } - boolean _found = false; - Iterator _childrenIter = _node.getChildren(); + PKIXPolicyNode validPolicyChild = CertPathValidatorUtilities.findValidPolicy( + _node.getChildren(), _policy); - while (_childrenIter.hasNext()) - { - PKIXPolicyNode _child = (PKIXPolicyNode)_childrenIter.next(); - - if (_policy.equals(_child.getValidPolicy())) - { - _found = true; - } - } - - if (!_found) + if (validPolicyChild == null) { Set _newChildExpectedPolicies = new HashSet(); _newChildExpectedPolicies.add(_policy); @@ -1352,46 +1314,24 @@ else if (_tmp instanceof ASN1ObjectIdentifier) } } - PKIXPolicyNode _validPolicyTree = validPolicyTree; // // (d) (3) // - for (int j = (i - 1); j >= 0; j--) - { - List nodes = policyNodes[j]; - - for (int k = 0; k < nodes.size(); k++) - { - PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k); - if (!node.hasChildren()) - { - _validPolicyTree = CertPathValidatorUtilities.removePolicyNode(_validPolicyTree, policyNodes, - node); - if (_validPolicyTree == null) - { - break; - } - } - } - } + validPolicyTree = CertPathValidatorUtilities.removeChildlessPolicyNodes(validPolicyTree, policyNodes, i); // // d (4) // - Set criticalExtensionOids = cert.getCriticalExtensionOIDs(); - - if (criticalExtensionOids != null) + if (CertPathValidatorUtilities.hasCriticalExtension(cert, CERTIFICATE_POLICIES)) { - boolean critical = criticalExtensionOids.contains(RFC3280CertPathUtilities.CERTIFICATE_POLICIES); - List nodes = policyNodes[i]; for (int j = 0; j < nodes.size(); j++) { PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(j); - node.setCritical(critical); + node.setCritical(true); } } - return _validPolicyTree; + return validPolicyTree; } return null; } @@ -1800,32 +1740,13 @@ private static void checkCRL( // update reasons mask reasonMask.addReasons(interimReasonsMask); - Set criticalExtensions = crl.getCriticalExtensionOIDs(); - if (criticalExtensions != null) - { - criticalExtensions = new HashSet(criticalExtensions); - criticalExtensions.remove(Extension.issuingDistributionPoint.getId()); - criticalExtensions.remove(Extension.deltaCRLIndicator.getId()); - - if (!criticalExtensions.isEmpty()) - { - throw new AnnotatedException("CRL contains unsupported critical extensions."); - } - } + CertPathValidatorUtilities.checkCRLCriticalExtensions(crl, + "CRL contains unsupported critical extensions."); if (deltaCRL != null) { - criticalExtensions = deltaCRL.getCriticalExtensionOIDs(); - if (criticalExtensions != null) - { - criticalExtensions = new HashSet(criticalExtensions); - criticalExtensions.remove(Extension.issuingDistributionPoint.getId()); - criticalExtensions.remove(Extension.deltaCRLIndicator.getId()); - if (!criticalExtensions.isEmpty()) - { - throw new AnnotatedException("Delta CRL contains unsupported critical extension."); - } - } + CertPathValidatorUtilities.checkCRLCriticalExtensions(deltaCRL, + "Delta CRL contains unsupported critical extensions."); } validCrlFound = true; @@ -2392,8 +2313,7 @@ protected static PKIXPolicyNode wrapupCertG( } intersection = null; } - else if (CertPathValidatorUtilities.isAnyPolicy(userInitialPolicySet)) // (g) - // (ii) + else if (CertPathValidatorUtilities.isAnyPolicy(userInitialPolicySet)) // (g) (ii) { if (paramsPKIX.isExplicitPolicyRequired()) { @@ -2402,60 +2322,45 @@ else if (CertPathValidatorUtilities.isAnyPolicy(userInitialPolicySet)) // (g) throw new ExtCertPathValidatorException("Explicit policy requested but none available.", null, certPath, index); } - else + + Set _validPolicyNodeSet = new HashSet(); + + for (int j = 0; j < policyNodes.length; j++) { - Set _validPolicyNodeSet = new HashSet(); + List _nodeDepth = policyNodes[j]; - for (int j = 0; j < policyNodes.length; j++) + for (int k = 0; k < _nodeDepth.size(); k++) { - List _nodeDepth = policyNodes[j]; + PKIXPolicyNode _node = (PKIXPolicyNode)_nodeDepth.get(k); - for (int k = 0; k < _nodeDepth.size(); k++) + if (ANY_POLICY.equals(_node.getValidPolicy())) { - PKIXPolicyNode _node = (PKIXPolicyNode)_nodeDepth.get(k); - - if (RFC3280CertPathUtilities.ANY_POLICY.equals(_node.getValidPolicy())) + Iterator _iter = _node.getChildren(); + while (_iter.hasNext()) { - Iterator _iter = _node.getChildren(); - while (_iter.hasNext()) - { - _validPolicyNodeSet.add(_iter.next()); - } + _validPolicyNodeSet.add(_iter.next()); } + + // TODO[pkix] break if there can only be one ANY_POLICY node at this depth? (use findValidPolicy) } } + } - Iterator _vpnsIter = _validPolicyNodeSet.iterator(); - while (_vpnsIter.hasNext()) - { - PKIXPolicyNode _node = (PKIXPolicyNode)_vpnsIter.next(); - String _validPolicy = _node.getValidPolicy(); + Iterator _vpnsIter = _validPolicyNodeSet.iterator(); + while (_vpnsIter.hasNext()) + { + PKIXPolicyNode _node = (PKIXPolicyNode)_vpnsIter.next(); + String _validPolicy = _node.getValidPolicy(); - if (!acceptablePolicies.contains(_validPolicy)) - { - // validPolicyTree = - // removePolicyNode(validPolicyTree, policyNodes, - // _node); - } - } - if (validPolicyTree != null) + if (!acceptablePolicies.contains(_validPolicy)) { - for (int j = (n - 1); j >= 0; j--) - { - List nodes = policyNodes[j]; - - for (int k = 0; k < nodes.size(); k++) - { - PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k); - if (!node.hasChildren()) - { - validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, - policyNodes, node); - } - } - } + // TODO? + // validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes, + // _node); } } + + validPolicyTree = CertPathValidatorUtilities.removeChildlessPolicyNodes(validPolicyTree, policyNodes, n); } intersection = validPolicyTree; @@ -2485,17 +2390,19 @@ else if (CertPathValidatorUtilities.isAnyPolicy(userInitialPolicySet)) // (g) { PKIXPolicyNode _node = (PKIXPolicyNode)_nodeDepth.get(k); - if (RFC3280CertPathUtilities.ANY_POLICY.equals(_node.getValidPolicy())) + if (ANY_POLICY.equals(_node.getValidPolicy())) { Iterator _iter = _node.getChildren(); while (_iter.hasNext()) { PKIXPolicyNode _c_node = (PKIXPolicyNode)_iter.next(); - if (!RFC3280CertPathUtilities.ANY_POLICY.equals(_c_node.getValidPolicy())) + if (!ANY_POLICY.equals(_c_node.getValidPolicy())) { _validPolicyNodeSet.add(_c_node); } } + + // TODO[pkix] break if there can only be one ANY_POLICY node at this depth? (use findValidPolicy) } } } @@ -2518,27 +2425,10 @@ else if (CertPathValidatorUtilities.isAnyPolicy(userInitialPolicySet)) // (g) // // (g) (iii) 4 // - if (validPolicyTree != null) - { - for (int j = (n - 1); j >= 0; j--) - { - List nodes = policyNodes[j]; - - for (int k = 0; k < nodes.size(); k++) - { - PKIXPolicyNode node = (PKIXPolicyNode)nodes.get(k); - if (!node.hasChildren()) - { - validPolicyTree = CertPathValidatorUtilities.removePolicyNode(validPolicyTree, policyNodes, - node); - } - } - } - } + validPolicyTree = CertPathValidatorUtilities.removeChildlessPolicyNodes(validPolicyTree, policyNodes, n); intersection = validPolicyTree; } return intersection; } - } From 9c17ca781a78c6f410f1074d4f4305c26acebbcc Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 18 Feb 2025 19:14:04 +1030 Subject: [PATCH 115/890] Add ECCSI key generator process --- .../generators/ECCSIKeyPairGenerator.java | 78 +++++++++++++ .../params/ECCSIKeyGenerationParameters.java | 60 ++++++++++ .../params/ECCSIPrivateKeyParameters.java | 14 +++ .../params/ECCSIPublicKeyParameters.java | 14 +++ .../crypto/signers/ECCSISigner.java | 108 ++++++++++++++++++ 5 files changed, 274 insertions(+) create mode 100644 core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java new file mode 100644 index 0000000000..fac222e103 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java @@ -0,0 +1,78 @@ +package org.bouncycastle.crypto.generators; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.bouncycastle.crypto.CryptoServicePurpose; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.KeyGenerationParameters; +import org.bouncycastle.crypto.constraints.DefaultServiceProperties; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.crypto.params.ECCSIKeyGenerationParameters; +import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; + +public class ECCSIKeyPairGenerator + implements AsymmetricCipherKeyPairGenerator +{ + // Initialize NIST P-256 curve + private static final X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); + private static final ECCurve curve = params.getCurve(); + + private static final BigInteger q = ((ECCurve.Fp)curve).getQ(); + + //BigInteger p = ((ECCurve.Fp)curve).getOrder(); + + // The subgroup order is available as: + //BigInteger n = params.getN(); + + // And the base point (generator) is: + private static final ECPoint G = params.getG(); + //int N = 32; // 256 bits + + private ECCSIKeyGenerationParameters parameters; + + @Override + public void init(KeyGenerationParameters parameters) + { + this.parameters = (ECCSIKeyGenerationParameters)parameters; + + CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("ECCSI", 256, null, CryptoServicePurpose.KEYGEN)); + } + + @Override + public AsymmetricCipherKeyPair generateKeyPair() + { + SecureRandom random = parameters.getRandom(); + byte[] id = parameters.getId(); + ECPoint kpak = parameters.getKPAK(); + // 1) Choose v, a random (ephemeral) non-zero element of F_q; + BigInteger v = new BigInteger(256, random).mod(q); + // 2) Compute PVT = [v]G + ECPoint pvt = G.multiply(v).normalize(); + + // 3) Compute a hash value HS = hash( G || KPAK || ID || PVT ), an N-octet integer; + Digest digest = new SHA256Digest(); + byte[] tmp = G.getEncoded(false); + digest.update(tmp, 0, tmp.length); + tmp = kpak.getEncoded(false); + digest.update(tmp, 0, tmp.length); + digest.update(id, 0, id.length); + tmp = pvt.getEncoded(false); + digest.update(tmp, 0, tmp.length); + tmp = new byte[digest.getDigestSize()]; + digest.doFinal(tmp, 0); + BigInteger HS = new BigInteger(1, tmp).mod(q); + + // 4) Compute SSK = ( KSAK + HS * v ) modulo q; + BigInteger ssk = parameters.computeSSK(HS.multiply(v)); + return new AsymmetricCipherKeyPair(new ECCSIPublicKeyParameters(pvt), new ECCSIPrivateKeyParameters(ssk)); + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java new file mode 100644 index 0000000000..f21a8df432 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java @@ -0,0 +1,60 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.KeyGenerationParameters; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.Arrays; + +public class ECCSIKeyGenerationParameters + extends KeyGenerationParameters +{ + private static final X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); + private static final ECCurve curve = params.getCurve(); + + private static final BigInteger q = ((ECCurve.Fp)curve).getQ(); + + //BigInteger p = ((ECCurve.Fp)curve).getOrder(); + + // The subgroup order is available as: + //BigInteger n = params.getN(); + + // And the base point (generator) is: + private static final ECPoint G = params.getG(); + private final byte[] id; + private final BigInteger ksak; + private final ECPoint kpak; + + /** + * initialise the generator with a source of randomness + * and a strength (in bits). + * + * @param random the random byte source. + */ + public ECCSIKeyGenerationParameters(SecureRandom random, byte[] id) + { + super(random, 256); + this.id = Arrays.clone(id); + this.ksak = new BigInteger(256, random).mod(q); + this.kpak = G.multiply(ksak).normalize(); + } + + public byte[] getId() + { + return id; + } + + public ECPoint getKPAK() + { + return kpak; + } + + public BigInteger computeSSK(BigInteger hs_v) + { + return ksak.add(hs_v).mod(q); + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java new file mode 100644 index 0000000000..e98a947257 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java @@ -0,0 +1,14 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; + +public class ECCSIPrivateKeyParameters + extends AsymmetricKeyParameter +{ + private final BigInteger ssk; + public ECCSIPrivateKeyParameters(BigInteger ssk) + { + super(true); + this.ssk = ssk; + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java new file mode 100644 index 0000000000..aa41c352f0 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java @@ -0,0 +1,14 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.math.ec.ECPoint; + +public class ECCSIPublicKeyParameters + extends AsymmetricKeyParameter +{ + private final ECPoint pvt; + public ECCSIPublicKeyParameters(ECPoint pvt) + { + super(false); + this.pvt = pvt; + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java new file mode 100644 index 0000000000..b5dd0c2d1b --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java @@ -0,0 +1,108 @@ +package org.bouncycastle.crypto.signers; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.Signer; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.BigIntegers; + +public class ECCSISigner + implements Signer +{ + private static final X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); + private static final ECCurve curve = params.getCurve(); + + private static final BigInteger q = ((ECCurve.Fp)curve).getQ(); + + //BigInteger p = ((ECCurve.Fp)curve).getOrder(); + + // The subgroup order is available as: + //BigInteger n = params.getN(); + + // And the base point (generator) is: + private static final ECPoint G = params.getG(); + private final Digest digest = new SHA256Digest(); + BigInteger j; + ECPoint J; + BigInteger r; + + public ECCSISigner() + { + + } + + @Override + public void init(boolean forSigning, CipherParameters param) + { + SecureRandom random = null; + if (param instanceof ParametersWithRandom) + { + random = ((ParametersWithRandom)param).getRandom(); + param = ((ParametersWithRandom)param).getParameters(); + } + + if (forSigning) + { + ECCSIPrivateKeyParameters parameters = (ECCSIPrivateKeyParameters)param; + + j = new BigInteger(256, random).mod(q); + J = G.multiply(j).normalize(); + r = J.getAffineXCoord().toBigInteger(); + byte[] rBytes = BigIntegers.asUnsignedByteArray(256, r); +// BigInteger kpak = parameters + byte[] tmp = G.getEncoded(false); + digest.update(tmp, 0, tmp.length); +// tmp = kpak.getEncoded(false); +// digest.update(tmp, 0, tmp.length); +// digest.update(id, 0, id.length); +// tmp = pvt.getEncoded(false); +// digest.update(tmp, 0, tmp.length); + tmp = new byte[digest.getDigestSize()]; + digest.doFinal(tmp, 0); + BigInteger HS = new BigInteger(1, tmp).mod(q); + } + + } + + @Override + public void update(byte b) + { + + } + + @Override + public void update(byte[] in, int off, int len) + { + + } + + @Override + public byte[] generateSignature() + throws CryptoException, DataLengthException + { + return new byte[0]; + } + + @Override + public boolean verifySignature(byte[] signature) + { + return false; + } + + @Override + public void reset() + { + + } +} From fc91cf491003660c2ebe5127ee53e31ce7cd2aa2 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 18 Feb 2025 20:21:47 +0700 Subject: [PATCH 116/890] Confirm delta CRLs before skipping CRLDP - also fix URI search --- .../provider/CertPathValidatorUtilities.java | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java b/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java index 37f269b35f..951e12be40 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java @@ -52,7 +52,6 @@ import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1String; import org.bouncycastle.asn1.DEROctetString; -import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.style.RFC4519Style; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; @@ -64,7 +63,6 @@ import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; -import org.bouncycastle.asn1.x509.PolicyInformation; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.internal.asn1.isismtt.ISISMTTObjectIdentifiers; import org.bouncycastle.jcajce.PKIXCRLStore; @@ -968,16 +966,16 @@ protected static Set getDeltaCRLs(Date validityDate, // 5.2.4 (c) selBuilder.setMaxBaseCRLNumber(completeCRLNumber); - // TODO[pkix] Would adding this to the selector be helpful? - //selBuilder.setDeltaCRLIndicatorEnabled(true); + // NOTE: Does not restrict to critical DCI extension, so we filter non-critical ones later + selBuilder.setDeltaCRLIndicatorEnabled(true); PKIXCRLStoreSelector deltaSelect = selBuilder.build(); // find delta CRLs - Set temp = PKIXCRLUtil.findCRLs(deltaSelect, validityDate, certStores, pkixCrlStores); + Set deltaCRLs = getDeltaCRLs(PKIXCRLUtil.findCRLs(deltaSelect, validityDate, certStores, pkixCrlStores)); // if the named CRL store is empty, and we're told to check with CRLDP - if (temp.isEmpty() && Properties.isOverrideSet("org.bouncycastle.x509.enableCRLDP")) + if (deltaCRLs.isEmpty() && Properties.isOverrideSet("org.bouncycastle.x509.enableCRLDP")) { CertificateFactory certFact; try @@ -1001,7 +999,7 @@ protected static Set getDeltaCRLs(Date validityDate, for (int j = 0; j < genNames.length; j++) { - GeneralName name = genNames[i]; + GeneralName name = genNames[j]; if (name.getTagNo() == GeneralName.uniformResourceIdentifier) { try @@ -1010,8 +1008,9 @@ protected static Set getDeltaCRLs(Date validityDate, new URI(((ASN1String)name.getName()).getString())); if (store != null) { - temp = PKIXCRLUtil.findCRLs(deltaSelect, validityDate, Collections.EMPTY_LIST, - Collections.singletonList(store)); + deltaCRLs = getDeltaCRLs( + PKIXCRLUtil.findCRLs(deltaSelect, validityDate, Collections.EMPTY_LIST, + Collections.singletonList(store))); } break; } @@ -1025,9 +1024,14 @@ protected static Set getDeltaCRLs(Date validityDate, } } + return deltaCRLs; + } + + private static Set getDeltaCRLs(Set crls) + { Set result = new HashSet(); - for (Iterator it = temp.iterator(); it.hasNext(); ) + for (Iterator it = crls.iterator(); it.hasNext(); ) { X509CRL crl = (X509CRL)it.next(); @@ -1042,7 +1046,7 @@ protected static Set getDeltaCRLs(Date validityDate, private static boolean isDeltaCRL(X509CRL crl) { - return hasCriticalExtension(crl, RFC3280CertPathUtilities.DELTA_CRL_INDICATOR); + return hasCriticalExtension(crl, Extension.deltaCRLIndicator.getId()); } /** From b477c178d47313ae31e10f0eb5a2098445ddf8af Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 19 Feb 2025 17:00:58 +1030 Subject: [PATCH 117/890] Pass the test vector of ECCSISigner --- .../generators/ECCSIKeyPairGenerator.java | 13 +- .../params/ECCSIKeyGenerationParameters.java | 19 +-- .../params/ECCSIPrivateKeyParameters.java | 15 +- .../params/ECCSIPublicKeyParameters.java | 6 + .../params/SAKKEPublicKeyParameters.java | 2 +- .../crypto/signers/ECCSISigner.java | 152 ++++++++++++++---- .../crypto/test/ECCSISignerTest.java | 72 +++++++++ .../crypto/test/RegressionTest.java | 1 - .../crypto/{kems => }/test/SAKKEKEMSTest.java | 4 +- 9 files changed, 223 insertions(+), 61 deletions(-) create mode 100644 core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java rename core/src/test/java/org/bouncycastle/crypto/{kems => }/test/SAKKEKEMSTest.java (99%) diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java index fac222e103..25cf523d64 100644 --- a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java @@ -16,7 +16,6 @@ import org.bouncycastle.crypto.params.ECCSIKeyGenerationParameters; import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; -import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; public class ECCSIKeyPairGenerator @@ -24,14 +23,7 @@ public class ECCSIKeyPairGenerator { // Initialize NIST P-256 curve private static final X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); - private static final ECCurve curve = params.getCurve(); - - private static final BigInteger q = ((ECCurve.Fp)curve).getQ(); - - //BigInteger p = ((ECCurve.Fp)curve).getOrder(); - - // The subgroup order is available as: - //BigInteger n = params.getN(); + private static final BigInteger q = params.getCurve().getOrder(); // And the base point (generator) is: private static final ECPoint G = params.getG(); @@ -73,6 +65,7 @@ public AsymmetricCipherKeyPair generateKeyPair() // 4) Compute SSK = ( KSAK + HS * v ) modulo q; BigInteger ssk = parameters.computeSSK(HS.multiply(v)); - return new AsymmetricCipherKeyPair(new ECCSIPublicKeyParameters(pvt), new ECCSIPrivateKeyParameters(ssk)); + ECCSIPublicKeyParameters pub = new ECCSIPublicKeyParameters(pvt); + return new AsymmetricCipherKeyPair(new ECCSIPublicKeyParameters(pvt), new ECCSIPrivateKeyParameters(ssk, pub)); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java index f21a8df432..0e773b17ec 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java @@ -6,25 +6,22 @@ import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.KeyGenerationParameters; import org.bouncycastle.crypto.ec.CustomNamedCurves; -import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; public class ECCSIKeyGenerationParameters extends KeyGenerationParameters { - private static final X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); - private static final ECCurve curve = params.getCurve(); + private static final BigInteger q; + private static final ECPoint G; - private static final BigInteger q = ((ECCurve.Fp)curve).getQ(); - - //BigInteger p = ((ECCurve.Fp)curve).getOrder(); - - // The subgroup order is available as: - //BigInteger n = params.getN(); + static + { + X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); + q = params.getCurve().getOrder(); + G = params.getG(); + } - // And the base point (generator) is: - private static final ECPoint G = params.getG(); private final byte[] id; private final BigInteger ksak; private final ECPoint kpak; diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java index e98a947257..17ee88614a 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java @@ -6,9 +6,22 @@ public class ECCSIPrivateKeyParameters extends AsymmetricKeyParameter { private final BigInteger ssk; - public ECCSIPrivateKeyParameters(BigInteger ssk) + private final ECCSIPublicKeyParameters pub; + + public ECCSIPrivateKeyParameters(BigInteger ssk, ECCSIPublicKeyParameters pub) { super(true); this.ssk = ssk; + this.pub = pub; + } + + public ECCSIPublicKeyParameters getPublicKeyParameters() + { + return pub; + } + + public BigInteger getSSK() + { + return ssk; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java index aa41c352f0..19bfc43b8f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java @@ -6,9 +6,15 @@ public class ECCSIPublicKeyParameters extends AsymmetricKeyParameter { private final ECPoint pvt; + public ECCSIPublicKeyParameters(ECPoint pvt) { super(false); this.pvt = pvt; } + + public final ECPoint getPVT() + { + return pvt; + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java index 2380efe143..5dc57b95d1 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java @@ -77,7 +77,7 @@ public class SAKKEPublicKeyParameters * Pairing result g = computed using the Tate-Lichtenbaum pairing * (RFC 6508, Section 3.2). Value from RFC 6509 Appendix A. */ - private static final BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + + private static final BigInteger g = new BigInteger(1, Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + " C6A87BD1 FB94C41E 27FABE65 8E015A87\n" + " 371E9474 4C96FEDA 449AE956 3F8BC446\n" + " CBFDA85D 5D00EF57 7072DA8F 541721BE\n" + diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java index b5dd0c2d1b..550f6e9996 100644 --- a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java +++ b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto.signers; +import java.io.ByteArrayOutputStream; import java.math.BigInteger; import java.security.SecureRandom; @@ -12,92 +13,173 @@ import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; import org.bouncycastle.crypto.params.ParametersWithRandom; -import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; public class ECCSISigner implements Signer { - private static final X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); - private static final ECCurve curve = params.getCurve(); + private static final BigInteger q; + private static final ECPoint G; - private static final BigInteger q = ((ECCurve.Fp)curve).getQ(); - - //BigInteger p = ((ECCurve.Fp)curve).getOrder(); - - // The subgroup order is available as: - //BigInteger n = params.getN(); + static + { + X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); + q = params.getCurve().getOrder(); + G = params.getG(); + } - // And the base point (generator) is: - private static final ECPoint G = params.getG(); private final Digest digest = new SHA256Digest(); - BigInteger j; - ECPoint J; - BigInteger r; - - public ECCSISigner() + private BigInteger j; + private BigInteger r; + private ECPoint Y; + private final ECPoint kpak; + private final byte[] id; + private CipherParameters param; + private ByteArrayOutputStream stream; + private boolean forSigning; + + public ECCSISigner(ECPoint kpak, byte[] id) { - + this.kpak = kpak; + this.id = id; } @Override public void init(boolean forSigning, CipherParameters param) { + this.forSigning = forSigning; + this.param = param; SecureRandom random = null; if (param instanceof ParametersWithRandom) { random = ((ParametersWithRandom)param).getRandom(); param = ((ParametersWithRandom)param).getParameters(); } - + ECPoint kpak_computed = null; + ECPoint pvt; if (forSigning) { ECCSIPrivateKeyParameters parameters = (ECCSIPrivateKeyParameters)param; j = new BigInteger(256, random).mod(q); - J = G.multiply(j).normalize(); + ECPoint J = G.multiply(j).normalize(); r = J.getAffineXCoord().toBigInteger(); - byte[] rBytes = BigIntegers.asUnsignedByteArray(256, r); -// BigInteger kpak = parameters - byte[] tmp = G.getEncoded(false); - digest.update(tmp, 0, tmp.length); -// tmp = kpak.getEncoded(false); -// digest.update(tmp, 0, tmp.length); -// digest.update(id, 0, id.length); -// tmp = pvt.getEncoded(false); -// digest.update(tmp, 0, tmp.length); - tmp = new byte[digest.getDigestSize()]; - digest.doFinal(tmp, 0); - BigInteger HS = new BigInteger(1, tmp).mod(q); + pvt = parameters.getPublicKeyParameters().getPVT(); + kpak_computed = G.multiply(parameters.getSSK()); + } + else + { + ECCSIPublicKeyParameters parameters = (ECCSIPublicKeyParameters)param; + pvt = parameters.getPVT(); + stream = new ByteArrayOutputStream(); } + // compute HS + byte[] tmp = G.getEncoded(false); + digest.update(tmp, 0, tmp.length); + tmp = kpak.getEncoded(false); + digest.update(tmp, 0, tmp.length); + digest.update(id, 0, id.length); + tmp = pvt.getEncoded(false); + digest.update(tmp, 0, tmp.length); + tmp = new byte[digest.getDigestSize()]; + digest.doFinal(tmp, 0); + BigInteger HS = new BigInteger(1, tmp).mod(q); + + //HE = hash( HS || r || M ); + digest.update(tmp, 0, tmp.length); + if (forSigning) + { + kpak_computed = kpak_computed.subtract(pvt.multiply(HS)).normalize(); + if (!kpak_computed.equals(kpak)) + { + throw new IllegalArgumentException("Invalid KPAK"); + } + byte[] rBytes = BigIntegers.asUnsignedByteArray(32, r); + digest.update(rBytes, 0, rBytes.length); + } + else + { + // Compute Y = HS*PVT + KPAK + Y = pvt.multiply(HS).add(kpak).normalize(); + } } @Override public void update(byte b) { - + if (forSigning) + { + digest.update(b); + } + else + { + stream.write(b); + } } @Override public void update(byte[] in, int off, int len) { - + if (forSigning) + { + digest.update(in, off, len); + } + else + { + stream.write(in, off, len); + } } @Override public byte[] generateSignature() throws CryptoException, DataLengthException { - return new byte[0]; + byte[] heBytes = new byte[digest.getDigestSize()]; + digest.doFinal(heBytes, 0); + + //Compute s' = ( (( HE + r * SSK )^-1) * j ) modulo q + ECCSIPrivateKeyParameters params = (ECCSIPrivateKeyParameters)(((ParametersWithRandom)param).getParameters()); + BigInteger ssk = params.getSSK(); + BigInteger denominator = new BigInteger(1, heBytes).add(r.multiply(ssk)).mod(q); + if (denominator.equals(BigInteger.ZERO)) + { + throw new IllegalArgumentException("Invalid j, retry"); + } + + BigInteger sPrime = denominator.modInverse(q).multiply(j).mod(q); + + return Arrays.concatenate(BigIntegers.asUnsignedByteArray(32, r), BigIntegers.asUnsignedByteArray(32, sPrime), + params.getPublicKeyParameters().getPVT().getEncoded(false)); } @Override public boolean verifySignature(byte[] signature) { - return false; + byte[] bytes = Arrays.copyOf(signature, 32); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signature, 32, 64)); + r = new BigInteger(1, bytes).mod(q); + digest.update(bytes, 0, 32); + bytes = stream.toByteArray(); + digest.update(bytes, 0, bytes.length); + bytes = new byte[digest.getDigestSize()]; + digest.doFinal(bytes, 0); + + BigInteger HE = new BigInteger(1, bytes).mod(q); + + // Compute J = s*(HE*G + r*Y) + ECPoint HE_G = G.multiply(HE).normalize(); + ECPoint rY = Y.multiply(r).normalize(); + ECPoint sum = HE_G.add(rY).normalize(); + ECPoint J = sum.multiply(s).normalize(); + + BigInteger rComputed = J.getAffineXCoord().toBigInteger(); + + return rComputed.mod(q).equals(r.mod(q)); } @Override diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java new file mode 100644 index 0000000000..ba959a929c --- /dev/null +++ b/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java @@ -0,0 +1,72 @@ +package org.bouncycastle.crypto.test; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.generators.ECCSIKeyPairGenerator; +import org.bouncycastle.crypto.params.ECCSIKeyGenerationParameters; +import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.crypto.signers.ECCSISigner; +import org.bouncycastle.util.BigIntegers; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.FixedSecureRandom; +import org.bouncycastle.util.test.SimpleTest; + +public class ECCSISignerTest + extends SimpleTest +{ + public static void main(String[] args) + throws Exception + { + ECCSISignerTest test = new ECCSISignerTest(); + test.performTest(); + } + + @Override + public String getName() + { + return "ECCSISigner Test"; + } + + @Override + public void performTest() + throws Exception + { + testTestVector(); + } + + private void testTestVector() + throws Exception + { + BigInteger ksak = BigInteger.valueOf(0x12345); + BigInteger v = BigInteger.valueOf(0x23456); + BigInteger j = BigInteger.valueOf(0x34567); + ECCSIKeyPairGenerator generator = new ECCSIKeyPairGenerator(); + SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(BigIntegers.asUnsignedByteArray(32, ksak)), + new FixedSecureRandom.Data(BigIntegers.asUnsignedByteArray(32, v)), + new FixedSecureRandom.Data(BigIntegers.asUnsignedByteArray(32, j))}); + ECCSIKeyGenerationParameters keyGenerationParameters = new ECCSIKeyGenerationParameters(random, "2011-02\0tel:+447700900123\0".getBytes()); + generator.init(keyGenerationParameters); + AsymmetricCipherKeyPair keyPair = generator.generateKeyPair(); + ECCSIPublicKeyParameters pub = (ECCSIPublicKeyParameters)keyPair.getPublic(); + ECCSIPrivateKeyParameters priv = (ECCSIPrivateKeyParameters)keyPair.getPrivate(); + System.out.println(new String(Hex.encode(pub.getPVT().getXCoord().toBigInteger().toByteArray()))); + System.out.println(new String(Hex.encode(pub.getPVT().getYCoord().toBigInteger().toByteArray()))); + System.out.println(new String(Hex.encode(priv.getSSK().toByteArray()))); + + byte[] M = "message\0".getBytes(); + + ECCSISigner signer = new ECCSISigner(keyGenerationParameters.getKPAK(), keyGenerationParameters.getId()); + signer.init(true, new ParametersWithRandom(priv, random)); + signer.update(M, 0, M.length); + byte[] sig = signer.generateSignature(); + System.out.println("sig: " + new String(Hex.encode(sig))); + + signer.init(false, pub); + signer.update(M, 0, M.length); + isTrue(signer.verifySignature(sig)); + } +} diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java index 1025007493..4349b06ec0 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java @@ -1,6 +1,5 @@ package org.bouncycastle.crypto.test; -import org.bouncycastle.crypto.kems.test.SAKKEKEMSTest; import org.bouncycastle.util.test.SimpleTest; import org.bouncycastle.util.test.Test; diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SAKKEKEMSTest.java similarity index 99% rename from core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java rename to core/src/test/java/org/bouncycastle/crypto/test/SAKKEKEMSTest.java index 0ef2797037..fa9d73f1e4 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SAKKEKEMSTest.java @@ -1,4 +1,4 @@ -package org.bouncycastle.crypto.kems.test; +package org.bouncycastle.crypto.test; import java.math.BigInteger; import java.security.SecureRandom; @@ -31,7 +31,7 @@ public static void main(String[] args) @Override public String getName() { - return null; + return "SAKKE-KEMS Test"; } @Override From 39656234e1fac01484f25b063c175c24b577ad63 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 20 Feb 2025 12:36:13 +1030 Subject: [PATCH 118/890] Add support for different curves --- .../generators/ECCSIKeyPairGenerator.java | 21 +-- .../params/ECCSIKeyGenerationParameters.java | 46 +++-- .../crypto/signers/ECCSISigner.java | 158 ++++++++++-------- .../crypto/test/ECCSISignerTest.java | 103 +++++++++++- 4 files changed, 223 insertions(+), 105 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java index 25cf523d64..bfe73a514f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java @@ -3,7 +3,6 @@ import java.math.BigInteger; import java.security.SecureRandom; -import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; import org.bouncycastle.crypto.CryptoServicePurpose; @@ -11,8 +10,6 @@ import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.KeyGenerationParameters; import org.bouncycastle.crypto.constraints.DefaultServiceProperties; -import org.bouncycastle.crypto.digests.SHA256Digest; -import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.params.ECCSIKeyGenerationParameters; import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; @@ -21,28 +18,27 @@ public class ECCSIKeyPairGenerator implements AsymmetricCipherKeyPairGenerator { - // Initialize NIST P-256 curve - private static final X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); - private static final BigInteger q = params.getCurve().getOrder(); - - // And the base point (generator) is: - private static final ECPoint G = params.getG(); - //int N = 32; // 256 bits - + private BigInteger q; + private ECPoint G; + private Digest digest; private ECCSIKeyGenerationParameters parameters; @Override public void init(KeyGenerationParameters parameters) { this.parameters = (ECCSIKeyGenerationParameters)parameters; + this.q = this.parameters.getQ(); + this.G = this.parameters.getG(); + this.digest = this.parameters.getDigest(); - CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("ECCSI", 256, null, CryptoServicePurpose.KEYGEN)); + CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("ECCSI", this.parameters.getN(), null, CryptoServicePurpose.KEYGEN)); } @Override public AsymmetricCipherKeyPair generateKeyPair() { SecureRandom random = parameters.getRandom(); + this.digest.reset(); byte[] id = parameters.getId(); ECPoint kpak = parameters.getKPAK(); // 1) Choose v, a random (ephemeral) non-zero element of F_q; @@ -51,7 +47,6 @@ public AsymmetricCipherKeyPair generateKeyPair() ECPoint pvt = G.multiply(v).normalize(); // 3) Compute a hash value HS = hash( G || KPAK || ID || PVT ), an N-octet integer; - Digest digest = new SHA256Digest(); byte[] tmp = G.getEncoded(false); digest.update(tmp, 0, tmp.length); tmp = kpak.getEncoded(false); diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java index 0e773b17ec..22df66c639 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java @@ -4,27 +4,21 @@ import java.security.SecureRandom; import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.KeyGenerationParameters; -import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; public class ECCSIKeyGenerationParameters extends KeyGenerationParameters { - private static final BigInteger q; - private static final ECPoint G; - - static - { - X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); - q = params.getCurve().getOrder(); - G = params.getG(); - } - + private final BigInteger q; + private final ECPoint G; + private final Digest digest; private final byte[] id; private final BigInteger ksak; private final ECPoint kpak; + private final int n; /** * initialise the generator with a source of randomness @@ -32,11 +26,15 @@ public class ECCSIKeyGenerationParameters * * @param random the random byte source. */ - public ECCSIKeyGenerationParameters(SecureRandom random, byte[] id) + public ECCSIKeyGenerationParameters(SecureRandom random, X9ECParameters params, Digest digest, byte[] id) { - super(random, 256); + super(random, params.getCurve().getA().bitLength()); + this.q = params.getCurve().getOrder(); + this.G = params.getG(); + this.digest = digest; this.id = Arrays.clone(id); - this.ksak = new BigInteger(256, random).mod(q); + this.n = params.getCurve().getA().bitLength(); + this.ksak = new BigInteger(n, random).mod(q); this.kpak = G.multiply(ksak).normalize(); } @@ -54,4 +52,24 @@ public BigInteger computeSSK(BigInteger hs_v) { return ksak.add(hs_v).mod(q); } + + public BigInteger getQ() + { + return q; + } + + public ECPoint getG() + { + return G; + } + + public Digest getDigest() + { + return digest; + } + + public int getN() + { + return n; + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java index 550f6e9996..c825971f04 100644 --- a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java +++ b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java @@ -10,8 +10,6 @@ import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Signer; -import org.bouncycastle.crypto.digests.SHA256Digest; -import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; import org.bouncycastle.crypto.params.ParametersWithRandom; @@ -19,20 +17,27 @@ import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; +/** + * Implementation of Elliptic Curve-based Certificateless Signatures for Identity-Based Encryption (ECCSI) + * as defined in RFC 6507. + *

+ * This class handles both signature generation and verification using the ECCSI scheme. It supports: + *

    + *
  • NIST P-256 (secp256r1) elliptic curve parameters
  • + *
  • SHA-256 hash function
  • + *
  • Certificateless signatures using KMS Public Authentication Key (KPAK)
  • + *
  • Identity-based signatures with Secret Signing Key (SSK)
  • + *
+ * + * @see RFC 6507: Elliptic Curve-Based Certificateless + * Signatures for Identity-Based Encryption (ECCSI) + */ public class ECCSISigner implements Signer { - private static final BigInteger q; - private static final ECPoint G; - - static - { - X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); - q = params.getCurve().getOrder(); - G = params.getG(); - } - - private final Digest digest = new SHA256Digest(); + private final BigInteger q; + private final ECPoint G; + private final Digest digest; private BigInteger j; private BigInteger r; private ECPoint Y; @@ -41,11 +46,18 @@ public class ECCSISigner private CipherParameters param; private ByteArrayOutputStream stream; private boolean forSigning; + private final int N; - public ECCSISigner(ECPoint kpak, byte[] id) + + public ECCSISigner(ECPoint kpak, X9ECParameters params, Digest digest, byte[] id) { this.kpak = kpak; this.id = id; + this.q = params.getCurve().getOrder(); + this.G = params.getG(); + this.digest = digest; + this.digest.reset(); + this.N = (params.getCurve().getOrder().bitLength() + 7) >> 3; } @Override @@ -53,60 +65,7 @@ public void init(boolean forSigning, CipherParameters param) { this.forSigning = forSigning; this.param = param; - SecureRandom random = null; - if (param instanceof ParametersWithRandom) - { - random = ((ParametersWithRandom)param).getRandom(); - param = ((ParametersWithRandom)param).getParameters(); - } - ECPoint kpak_computed = null; - ECPoint pvt; - if (forSigning) - { - ECCSIPrivateKeyParameters parameters = (ECCSIPrivateKeyParameters)param; - - j = new BigInteger(256, random).mod(q); - ECPoint J = G.multiply(j).normalize(); - r = J.getAffineXCoord().toBigInteger(); - pvt = parameters.getPublicKeyParameters().getPVT(); - kpak_computed = G.multiply(parameters.getSSK()); - } - else - { - ECCSIPublicKeyParameters parameters = (ECCSIPublicKeyParameters)param; - pvt = parameters.getPVT(); - stream = new ByteArrayOutputStream(); - } - - // compute HS - byte[] tmp = G.getEncoded(false); - digest.update(tmp, 0, tmp.length); - tmp = kpak.getEncoded(false); - digest.update(tmp, 0, tmp.length); - digest.update(id, 0, id.length); - tmp = pvt.getEncoded(false); - digest.update(tmp, 0, tmp.length); - tmp = new byte[digest.getDigestSize()]; - digest.doFinal(tmp, 0); - BigInteger HS = new BigInteger(1, tmp).mod(q); - - //HE = hash( HS || r || M ); - digest.update(tmp, 0, tmp.length); - if (forSigning) - { - kpak_computed = kpak_computed.subtract(pvt.multiply(HS)).normalize(); - if (!kpak_computed.equals(kpak)) - { - throw new IllegalArgumentException("Invalid KPAK"); - } - byte[] rBytes = BigIntegers.asUnsignedByteArray(32, r); - digest.update(rBytes, 0, rBytes.length); - } - else - { - // Compute Y = HS*PVT + KPAK - Y = pvt.multiply(HS).add(kpak).normalize(); - } + reset(); } @Override @@ -153,17 +112,17 @@ public byte[] generateSignature() BigInteger sPrime = denominator.modInverse(q).multiply(j).mod(q); - return Arrays.concatenate(BigIntegers.asUnsignedByteArray(32, r), BigIntegers.asUnsignedByteArray(32, sPrime), + return Arrays.concatenate(BigIntegers.asUnsignedByteArray(this.N, r), BigIntegers.asUnsignedByteArray(this.N, sPrime), params.getPublicKeyParameters().getPVT().getEncoded(false)); } @Override public boolean verifySignature(byte[] signature) { - byte[] bytes = Arrays.copyOf(signature, 32); - BigInteger s = new BigInteger(1, Arrays.copyOfRange(signature, 32, 64)); + byte[] bytes = Arrays.copyOf(signature, this.N); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signature, this.N, this.N << 1)); r = new BigInteger(1, bytes).mod(q); - digest.update(bytes, 0, 32); + digest.update(bytes, 0, this.N); bytes = stream.toByteArray(); digest.update(bytes, 0, bytes.length); bytes = new byte[digest.getDigestSize()]; @@ -185,6 +144,61 @@ public boolean verifySignature(byte[] signature) @Override public void reset() { + digest.reset(); + CipherParameters param = this.param; + SecureRandom random = null; + if (param instanceof ParametersWithRandom) + { + random = ((ParametersWithRandom)param).getRandom(); + param = ((ParametersWithRandom)param).getParameters(); + } + ECPoint kpak_computed = null; + ECPoint pvt; + if (forSigning) + { + ECCSIPrivateKeyParameters parameters = (ECCSIPrivateKeyParameters)param; + pvt = parameters.getPublicKeyParameters().getPVT(); + j = new BigInteger(q.bitLength(), random); + ECPoint J = G.multiply(j).normalize(); + r = J.getAffineXCoord().toBigInteger().mod(q); + kpak_computed = G.multiply(parameters.getSSK()); + } + else + { + ECCSIPublicKeyParameters parameters = (ECCSIPublicKeyParameters)param; + pvt = parameters.getPVT(); + stream = new ByteArrayOutputStream(); + } + + // compute HS + byte[] tmp = G.getEncoded(false); + digest.update(tmp, 0, tmp.length); + tmp = kpak.getEncoded(false); + digest.update(tmp, 0, tmp.length); + digest.update(id, 0, id.length); + tmp = pvt.getEncoded(false); + digest.update(tmp, 0, tmp.length); + tmp = new byte[digest.getDigestSize()]; + digest.doFinal(tmp, 0); + BigInteger HS = new BigInteger(1, tmp).mod(q); + + //HE = hash( HS || r || M ); + digest.update(tmp, 0, tmp.length); + if (forSigning) + { + kpak_computed = kpak_computed.subtract(pvt.multiply(HS)).normalize(); + if (!kpak_computed.equals(kpak)) + { + throw new IllegalArgumentException("Invalid KPAK"); + } + byte[] rBytes = BigIntegers.asUnsignedByteArray(this.N, r); + digest.update(rBytes, 0, rBytes.length); + } + else + { + // Compute Y = HS*PVT + KPAK + Y = pvt.multiply(HS).add(kpak).normalize(); + } } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java index ba959a929c..dc6018444f 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java @@ -3,13 +3,19 @@ import java.math.BigInteger; import java.security.SecureRandom; +import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.digests.SHA512Digest; +import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.generators.ECCSIKeyPairGenerator; import org.bouncycastle.crypto.params.ECCSIKeyGenerationParameters; import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.crypto.signers.ECCSISigner; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.FixedSecureRandom; @@ -18,6 +24,42 @@ public class ECCSISignerTest extends SimpleTest { + String[] curveNames = { + "curve25519", + "secp128r1", + "secp160k1", + "secp160r1", + "secp160r2", + "secp192k1", + "secp192r1", + "secp224k1", + "secp224r1", + "secp256k1", + "secp256r1", + "secp384r1", + "secp521r1", + "sect113r1", + "sect113r2", + "sect131r1", + "sect131r2", + "sect163k1", + "sect163r1", + "sect163r2", + "sect193r1", + "sect193r2", + "sect233k1", + "sect233r1", + "sect239k1", + "sect283k1", + "sect283r1", + "sect409k1", + "sect409r1", + "sect571k1", + "sect571r1", + "sm2p256v1" + }; + + public static void main(String[] args) throws Exception { @@ -36,6 +78,11 @@ public void performTest() throws Exception { testTestVector(); + for (int i = 0; i < curveNames.length; ++i) + { + //System.out.println(curveNames[i]); + testRandom(curveNames[i]); + } } private void testTestVector() @@ -48,25 +95,69 @@ private void testTestVector() SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(BigIntegers.asUnsignedByteArray(32, ksak)), new FixedSecureRandom.Data(BigIntegers.asUnsignedByteArray(32, v)), new FixedSecureRandom.Data(BigIntegers.asUnsignedByteArray(32, j))}); - ECCSIKeyGenerationParameters keyGenerationParameters = new ECCSIKeyGenerationParameters(random, "2011-02\0tel:+447700900123\0".getBytes()); + ECCSIKeyGenerationParameters keyGenerationParameters = new ECCSIKeyGenerationParameters(random, + CustomNamedCurves.getByName("secP256r1"), new SHA256Digest(), "2011-02\0tel:+447700900123\0".getBytes()); generator.init(keyGenerationParameters); AsymmetricCipherKeyPair keyPair = generator.generateKeyPair(); ECCSIPublicKeyParameters pub = (ECCSIPublicKeyParameters)keyPair.getPublic(); ECCSIPrivateKeyParameters priv = (ECCSIPrivateKeyParameters)keyPair.getPrivate(); - System.out.println(new String(Hex.encode(pub.getPVT().getXCoord().toBigInteger().toByteArray()))); - System.out.println(new String(Hex.encode(pub.getPVT().getYCoord().toBigInteger().toByteArray()))); - System.out.println(new String(Hex.encode(priv.getSSK().toByteArray()))); +// System.out.println(new String(Hex.encode(pub.getPVT().getXCoord().toBigInteger().toByteArray()))); +// System.out.println(new String(Hex.encode(pub.getPVT().getYCoord().toBigInteger().toByteArray()))); +// System.out.println(new String(Hex.encode(priv.getSSK().toByteArray()))); byte[] M = "message\0".getBytes(); - ECCSISigner signer = new ECCSISigner(keyGenerationParameters.getKPAK(), keyGenerationParameters.getId()); + ECCSISigner signer = new ECCSISigner(keyGenerationParameters.getKPAK(), CustomNamedCurves.getByName("secP256r1"), new SHA256Digest(), keyGenerationParameters.getId()); signer.init(true, new ParametersWithRandom(priv, random)); signer.update(M, 0, M.length); byte[] sig = signer.generateSignature(); - System.out.println("sig: " + new String(Hex.encode(sig))); + isTrue(Arrays.areEqual(sig, Hex.decode("269D4C8F DEB66A74 E4EF8C0D 5DCC597D\n" + + " DFE6029C 2AFFC493 6008CD2C C1045D81\n" + + " E09B528D 0EF8D6DF 1AA3ECBF 80110CFC\n" + + " EC9FC682 52CEBB67 9F413484 6940CCFD\n" + + " 04\n" + + "\n" + + " 758A1427 79BE89E8 29E71984 CB40EF75\n" + + " 8CC4AD77 5FC5B9A3 E1C8ED52 F6FA36D9\n" + + " A79D2476 92F4EDA3 A6BDAB77 D6AA6474\n" + + " A464AE49 34663C52 65BA7018 BA091F79"))); +// System.out.println("sig: " + new String(Hex.encode(sig))); signer.init(false, pub); signer.update(M, 0, M.length); isTrue(signer.verifySignature(sig)); } + + private void testRandom(String curveName) + throws Exception + { + SecureRandom random = new SecureRandom(); + ECCSIKeyPairGenerator generator = new ECCSIKeyPairGenerator(); + byte[] id = new byte[16]; + random.nextBytes(id); + Digest digest = new SHA512Digest(); + X9ECParameters params = CustomNamedCurves.getByName(curveName); + ECCSIKeyGenerationParameters keyGenerationParameters = new ECCSIKeyGenerationParameters(random, + params, digest, id); + generator.init(keyGenerationParameters); + AsymmetricCipherKeyPair keyPair = generator.generateKeyPair(); + ECCSIPublicKeyParameters pub = (ECCSIPublicKeyParameters)keyPair.getPublic(); + ECCSIPrivateKeyParameters priv = (ECCSIPrivateKeyParameters)keyPair.getPrivate(); + + byte[] M = "message\0".getBytes(); + + ECCSISigner signer = new ECCSISigner(keyGenerationParameters.getKPAK(), params, digest, keyGenerationParameters.getId()); + signer.init(true, new ParametersWithRandom(priv, random)); + signer.update(M, 0, M.length); + signer.reset(); + signer.update(M, 0, M.length); + byte[] sig = signer.generateSignature(); + signer = new ECCSISigner(keyGenerationParameters.getKPAK(), params, digest, keyGenerationParameters.getId()); + signer.init(false, pub); + signer.update(M, 0, M.length); + signer.reset(); + signer.update(M, 0, M.length); + isTrue(signer.verifySignature(sig)); + } + } From 6f8ca1a6d9a6c529fcb15801558e0c9445b00efc Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 20 Feb 2025 13:51:18 +1030 Subject: [PATCH 119/890] Add java doc and more tests with different digests --- .../params/ECCSIKeyGenerationParameters.java | 91 ++++++++++++++++++- .../params/ECCSIPrivateKeyParameters.java | 45 +++++++++ .../params/ECCSIPublicKeyParameters.java | 30 ++++++ .../crypto/signers/ECCSISigner.java | 49 ++++++++-- .../crypto/test/ECCSISignerTest.java | 26 +++++- 5 files changed, 224 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java index 22df66c639..3b62709c19 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java @@ -9,22 +9,68 @@ import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; +/** + * Parameters for ECCSI key generation. + * + *

+ * This class encapsulates the parameters required for ECCSI (Elliptic Curve + * Certificateless Signatures for Identity-based encryption) key generation. + * It holds the elliptic curve domain parameters and computes the key pair + * components used in ECCSI. + *

+ * + *

+ * The secret component {@code ksak} is generated randomly and reduced modulo + * {@code q}, while {@code kpak} is derived from {@code ksak} by multiplying the + * generator point. + *

+ */ public class ECCSIKeyGenerationParameters extends KeyGenerationParameters { + /** + * The order of the elliptic curve. + */ private final BigInteger q; + + /** + * The generator (base point) of the elliptic curve. + */ private final ECPoint G; + + /** + * The digest algorithm used in key generation. + */ private final Digest digest; + + /** + * The identifier (e.g. user identity) used in key generation. + */ private final byte[] id; + + /** + * The secret key component (ksak) used in ECCSI, generated randomly. + */ private final BigInteger ksak; + + /** + * The public key component (kpak), computed as G * ksak. + */ private final ECPoint kpak; + + /** + * The bit length used for key generation (typically the bit length of the curve's parameter A). + */ private final int n; /** - * initialise the generator with a source of randomness - * and a strength (in bits). + * Constructs an instance of {@code ECCSIKeyGenerationParameters} with the specified + * source of randomness, elliptic curve parameters, digest algorithm, and identifier. * - * @param random the random byte source. + * @param random the source of randomness. + * @param params the elliptic curve parameters (in X9.62 format) providing the curve, order, and generator. + * @param digest the digest algorithm to be used. + * @param id the identifier associated with the key generation (e.g. a user or device ID). */ public ECCSIKeyGenerationParameters(SecureRandom random, X9ECParameters params, Digest digest, byte[] id) { @@ -38,36 +84,73 @@ public ECCSIKeyGenerationParameters(SecureRandom random, X9ECParameters params, this.kpak = G.multiply(ksak).normalize(); } + /** + * Returns a copy of the identifier used in these parameters. + * + * @return a byte array containing the identifier. + */ public byte[] getId() { - return id; + return Arrays.clone(id); } + /** + * Returns the public key component (kpak) corresponding to the secret key. + * + * @return the public key point. + */ public ECPoint getKPAK() { return kpak; } + /** + * Computes the session secret key (SSK) by adding the provided value to the secret key component + * and reducing modulo the curve order. + * + * @param hs_v a BigInteger value (typically derived from a hash) to be added to the secret. + * @return the computed session secret key. + */ public BigInteger computeSSK(BigInteger hs_v) { return ksak.add(hs_v).mod(q); } + /** + * Returns the order of the elliptic curve. + * + * @return the curve order. + */ public BigInteger getQ() { return q; } + /** + * Returns the generator (base point) of the elliptic curve. + * + * @return the generator point. + */ public ECPoint getG() { return G; } + /** + * Returns the digest algorithm used for key generation. + * + * @return the digest. + */ public Digest getDigest() { return digest; } + /** + * Returns the bit length used in key generation. + * + * @return the bit length. + */ public int getN() { return n; diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java index 17ee88614a..bc37f5f839 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java @@ -2,12 +2,47 @@ import java.math.BigInteger; +/** + * Represents the private key parameters for the Elliptic Curve-based Certificateless + * Signature Infrastructure (ECCSI) scheme as defined in RFC 6507. + * + *

+ * This class encapsulates the secret signing key (SSK) used in ECCSI. The SSK is generated by + * the Key Management Service (KMS) and is a random integer modulo the order of the elliptic curve. + * It is paired with the corresponding public key parameters, represented by an instance of + * {@link ECCSIPublicKeyParameters}, to form the complete key material required for generating + * and verifying ECCSI signatures without the use of traditional certificates. + *

+ * + *

+ * Per RFC 6507 Section 5.1: + *

    + *
  • The SSK is generated as a random value in the appropriate range.
  • + *
  • It is used in conjunction with the public validation token (PVT) to perform signature + * operations.
  • + *
  • The combination of the SSK and the public key parameters enables certificateless + * signature generation and verification.
  • + *
+ *

+ * + * @see ECCSIPublicKeyParameters + * @see + * RFC 6507: Elliptic Curve-Based Certificateless Signatures for Identity-Based Encryption (ECCSI) + * + */ public class ECCSIPrivateKeyParameters extends AsymmetricKeyParameter { private final BigInteger ssk; private final ECCSIPublicKeyParameters pub; + /** + * Constructs {@code ECCSIPrivateKeyParameters} with the specified secret signing key + * and associated public key parameters. + * + * @param ssk the secret signing key (SSK) as a BigInteger. + * @param pub the corresponding public key parameters, which encapsulate the public validation token. + */ public ECCSIPrivateKeyParameters(BigInteger ssk, ECCSIPublicKeyParameters pub) { super(true); @@ -15,11 +50,21 @@ public ECCSIPrivateKeyParameters(BigInteger ssk, ECCSIPublicKeyParameters pub) this.pub = pub; } + /** + * Returns the public key parameters associated with this private key. + * + * @return the {@link ECCSIPublicKeyParameters} containing the public validation token (PVT). + */ public ECCSIPublicKeyParameters getPublicKeyParameters() { return pub; } + /** + * Returns the secret signing key (SSK) used in ECCSI. + * + * @return the SSK as a BigInteger. + */ public BigInteger getSSK() { return ssk; diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java index 19bfc43b8f..15269a0b46 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java @@ -2,17 +2,47 @@ import org.bouncycastle.math.ec.ECPoint; +/** + * Represents the public key parameters for the Elliptic Curve-based Certificateless + * Signature Infrastructure (ECCSI) scheme as defined in RFC 6507. + *

+ * This class encapsulates the Public Validation Token (PVT) required for verifying + * ECCSI signatures. The PVT is cryptographically bound to a user's identity and + * generated by the Key Management Service (KMS) as part of the key material. + * + *

Per RFC 6507 Section 5.1: + *

    + *
  • The PVT is derived from the user's identity and KMS secret material
  • + *
  • Used during signature verification to validate the signer's identity
  • + *
  • Does not require certificates for authentication
  • + *
+ * + * @see RFC 6507: Elliptic Curve-Based Certificateless + * * Signatures for Identity-Based Encryption (ECCSI) + */ + public class ECCSIPublicKeyParameters extends AsymmetricKeyParameter { private final ECPoint pvt; + /** + * Constructs {@code ECCSIPublicKeyParameters} with the provided Public Validation Token (PVT). + */ public ECCSIPublicKeyParameters(ECPoint pvt) { super(false); this.pvt = pvt; } + /** + * Returns the Public Validation Token (PVT) for signature verification. + *

+ * The PVT is used in conjunction with the KMS Public Authentication Key (KPAK) + * to verify signatures per RFC 6507 Section 5.2.2. + * + * @return The PVT as an elliptic curve point in uncompressed format + */ public final ECPoint getPVT() { return pvt; diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java index c825971f04..98df43fc99 100644 --- a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java +++ b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java @@ -20,14 +20,6 @@ /** * Implementation of Elliptic Curve-based Certificateless Signatures for Identity-Based Encryption (ECCSI) * as defined in RFC 6507. - *

- * This class handles both signature generation and verification using the ECCSI scheme. It supports: - *

    - *
  • NIST P-256 (secp256r1) elliptic curve parameters
  • - *
  • SHA-256 hash function
  • - *
  • Certificateless signatures using KMS Public Authentication Key (KPAK)
  • - *
  • Identity-based signatures with Secret Signing Key (SSK)
  • - *
* * @see RFC 6507: Elliptic Curve-Based Certificateless * Signatures for Identity-Based Encryption (ECCSI) @@ -48,7 +40,12 @@ public class ECCSISigner private boolean forSigning; private final int N; - + /** + * Constructs an ECCSI signer/verifier with KMS Public Authentication Key and user identity. + * + * @param kpak KMS Public Authentication Key (KPAK) from RFC 6507 Section 2 + * @param id User identity byte array formatted + */ public ECCSISigner(ECPoint kpak, X9ECParameters params, Digest digest, byte[] id) { this.kpak = kpak; @@ -60,6 +57,15 @@ public ECCSISigner(ECPoint kpak, X9ECParameters params, Digest digest, byte[] id this.N = (params.getCurve().getOrder().bitLength() + 7) >> 3; } + /** + * Initializes the signer for either signature generation or verification. + * + * @param forSigning true for signing, false for verification + * @param param Key parameters: + * - For signing: {@code ParametersWithRandom} containing {@code ECCSIPrivateKeyParameters} + * - For verification: {@code ECCSIPublicKeyParameters} + * @throws IllegalArgumentException if invalid parameters are provided + */ @Override public void init(boolean forSigning, CipherParameters param) { @@ -94,6 +100,17 @@ public void update(byte[] in, int off, int len) } } + /** + * Generates an ECCSI signature according to RFC 6507 Section 5.2.1. + * + * @return Signature structure containing: + * - r (N bytes) + * - s (N bytes) + * - PVT (Public Validation Token) + * @throws CryptoException if cryptographic operations fail + * @throws DataLengthException if input data is invalid + * @throws IllegalArgumentException if invalid SSK or j parameter is detected + */ @Override public byte[] generateSignature() throws CryptoException, DataLengthException @@ -116,6 +133,13 @@ public byte[] generateSignature() params.getPublicKeyParameters().getPVT().getEncoded(false)); } + /** + * Verifies an ECCSI signature according to RFC 6507 Section 5.2.2. + * + * @param signature Signature to verify (r || s || PVT) + * @return true if signature is valid, false otherwise + * @throws IllegalArgumentException if signature format is invalid + */ @Override public boolean verifySignature(byte[] signature) { @@ -141,6 +165,13 @@ public boolean verifySignature(byte[] signature) return rComputed.mod(q).equals(r.mod(q)); } + /** + * Resets the signer/verifier state and performs initial computations: + * - For signing: Validates KPAK consistency (RFC 6507 Section 5.1.2) + * - For verification: Computes Y = HS·PVT + KPAK + * + * Also computes HS = hash(G || KPAK || ID || PVT) as per RFC 6507 Section 5.1.1 + */ @Override public void reset() { diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java index dc6018444f..7559cf2a90 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java @@ -6,8 +6,13 @@ import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.AsconHash256; +import org.bouncycastle.crypto.digests.MD5Digest; +import org.bouncycastle.crypto.digests.SHA224Digest; import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.digests.SHA3Digest; import org.bouncycastle.crypto.digests.SHA512Digest; +import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.generators.ECCSIKeyPairGenerator; import org.bouncycastle.crypto.params.ECCSIKeyGenerationParameters; @@ -59,6 +64,18 @@ public class ECCSISignerTest "sm2p256v1" }; + Digest[] digests = new Digest[]{ + new SHA256Digest(), + new SHA3Digest(), + new SHA3Digest(512), + new SHA224Digest(), + new SHA512Digest(), + new AsconHash256(), + new SHAKEDigest(256), + new SHAKEDigest(128), + new MD5Digest() + }; + public static void main(String[] args) throws Exception @@ -80,8 +97,10 @@ public void performTest() testTestVector(); for (int i = 0; i < curveNames.length; ++i) { - //System.out.println(curveNames[i]); - testRandom(curveNames[i]); + for (int j = 0; j < digests.length; ++j) + { + testRandom(curveNames[i], digests[j]); + } } } @@ -128,14 +147,13 @@ private void testTestVector() isTrue(signer.verifySignature(sig)); } - private void testRandom(String curveName) + private void testRandom(String curveName, Digest digest) throws Exception { SecureRandom random = new SecureRandom(); ECCSIKeyPairGenerator generator = new ECCSIKeyPairGenerator(); byte[] id = new byte[16]; random.nextBytes(id); - Digest digest = new SHA512Digest(); X9ECParameters params = CustomNamedCurves.getByName(curveName); ECCSIKeyGenerationParameters keyGenerationParameters = new ECCSIKeyGenerationParameters(random, params, digest, id); From de0a5c9c8b01045fd96d2671f8034111df4d7ed2 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 20 Feb 2025 13:58:11 +1030 Subject: [PATCH 120/890] Add java doc --- .../crypto/generators/ECCSIKeyPairGenerator.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java index bfe73a514f..49b5c2f4a3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java @@ -15,6 +15,15 @@ import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; import org.bouncycastle.math.ec.ECPoint; +/** + * A key pair generator for the ECCSI scheme (Elliptic Curve-based Certificateless Signatures + * for Identity-based Encryption) as defined in RFC 6507. + * + * @see + * RFC 6507: Elliptic Curve-Based Certificateless Signatures for Identity-based Encryption (ECCSI) + * + */ + public class ECCSIKeyPairGenerator implements AsymmetricCipherKeyPairGenerator { @@ -23,6 +32,12 @@ public class ECCSIKeyPairGenerator private Digest digest; private ECCSIKeyGenerationParameters parameters; + /** + * Initializes the key pair generator with the specified parameters. + * + * @param parameters an instance of {@link ECCSIKeyGenerationParameters} which encapsulates the elliptic + * curve domain parameters, the digest algorithm, and an associated identifier. + */ @Override public void init(KeyGenerationParameters parameters) { From 1ce94798a109b7ee532952ec337124d64837e81d Mon Sep 17 00:00:00 2001 From: David Hook Date: Fri, 21 Feb 2025 15:34:36 +1100 Subject: [PATCH 121/890] added support for CHOICE private key encoding. --- .../jcajce/interfaces/MLDSAPrivateKey.java | 8 ++-- .../asymmetric/mldsa/BCMLDSAPrivateKey.java | 11 +++-- .../pqc/jcajce/provider/util/KeyUtil.java | 43 ------------------- .../pqc/jcajce/provider/test/MLDSATest.java | 29 +++++++++---- 4 files changed, 30 insertions(+), 61 deletions(-) diff --git a/prov/src/main/java/org/bouncycastle/jcajce/interfaces/MLDSAPrivateKey.java b/prov/src/main/java/org/bouncycastle/jcajce/interfaces/MLDSAPrivateKey.java index 52c58aca25..1f4224e461 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/interfaces/MLDSAPrivateKey.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/interfaces/MLDSAPrivateKey.java @@ -27,10 +27,10 @@ public interface MLDSAPrivateKey byte[] getSeed(); /** - * Return the encoding of the key or an encoding of its key generation parameters (the seed). + * Return a privateKey which will encode as seed-only or as an expanded-key. * - * @param asKeyGenParams return a key gen parameters structure. - * @return a PKCS#8 of the private key encoding, or a PKCS#8 of the seed. + * @param preferSeedOnly if true, return a privateKey which will encode to seed-only if possible. + * @return a new MLDSAPrivateKey which encodes to either seed-only or expanded-key. */ - byte[] getEncoded(boolean asKeyGenParams); + MLDSAPrivateKey getPrivateKey(boolean preferSeedOnly); } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.java index e581149380..04a61ceeb4 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/BCMLDSAPrivateKey.java @@ -91,19 +91,18 @@ public final String getAlgorithm() return algorithm; } - public byte[] getEncoded(boolean asKeyGenParams) + public MLDSAPrivateKey getPrivateKey(boolean preferSeedOnly) { - if (asKeyGenParams) + if (preferSeedOnly) { byte[] seed = params.getSeed(); - if (seed == null) + if (seed != null) { - return null; + return new BCMLDSAPrivateKey(this.params.getParametersWithFormat(MLDSAPrivateKeyParameters.SEED_ONLY)); } - return KeyUtil.getEncodedPrivateKeyInfo(getParameterSpec(), seed, attributes); } - return getEncoded(); + return new BCMLDSAPrivateKey(this.params.getParametersWithFormat(MLDSAPrivateKeyParameters.EXPANDED_KEY)); } public byte[] getEncoded() diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/KeyUtil.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/KeyUtil.java index e2dc120eec..e0b2645418 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/KeyUtil.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/KeyUtil.java @@ -1,16 +1,12 @@ package org.bouncycastle.pqc.jcajce.provider.util; -import java.security.spec.AlgorithmParameterSpec; - import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1Set; -import org.bouncycastle.asn1.bc.BCObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; -import org.bouncycastle.jcajce.spec.MLDSAParameterSpec; import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory; import org.bouncycastle.pqc.crypto.util.SubjectPublicKeyInfoFactory; @@ -95,45 +91,6 @@ public static byte[] getEncodedPrivateKeyInfo(PrivateKeyInfo info) } } - public static byte[] getEncodedPrivateKeyInfo(AlgorithmParameterSpec paramSpec, byte[] seed, ASN1Set attributes) - { - byte[] enc = null; - - try - { - if (paramSpec instanceof MLDSAParameterSpec) - { - String name = ((MLDSAParameterSpec)paramSpec).getName(); - if (name.equals(MLDSAParameterSpec.ml_dsa_44.getName()) || name.equals(MLDSAParameterSpec.ml_dsa_44_with_sha512.getName())) - { - enc = new PrivateKeyInfo(new AlgorithmIdentifier(BCObjectIdentifiers.id_id_alg_ml_dsa_44_seed), seed, attributes).getEncoded(); - } - else if (name.equals(MLDSAParameterSpec.ml_dsa_65.getName()) || name.equals(MLDSAParameterSpec.ml_dsa_65_with_sha512.getName())) - { - enc = new PrivateKeyInfo(new AlgorithmIdentifier(BCObjectIdentifiers.id_id_alg_ml_dsa_65_seed), seed, attributes).getEncoded(); - } - else if (name.equals(MLDSAParameterSpec.ml_dsa_87.getName()) || name.equals(MLDSAParameterSpec.ml_dsa_87_with_sha512.getName())) - { - enc = new PrivateKeyInfo(new AlgorithmIdentifier(BCObjectIdentifiers.id_id_alg_ml_dsa_87_seed), seed, attributes).getEncoded(); - } - else - { - throw new IllegalStateException("unknown ML-DSA algorithm"); - } - } - } - catch (IllegalStateException e) - { - throw e; - } - catch (Exception e) - { - return null; - } - - return enc; - } - public static byte[] getEncodedPrivateKeyInfo(AsymmetricKeyParameter privateKey, ASN1Set attributes) { if (!privateKey.isPrivate()) diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java index b285cf0591..6b19537129 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java @@ -124,7 +124,8 @@ private void tryKeyFact(KeyFactory kFact, KeyPair kpValid, KeyPair kpInvalid, St throws Exception { kFact.generatePrivate(new PKCS8EncodedKeySpec(kpValid.getPrivate().getEncoded())); - kFact.generatePrivate(new PKCS8EncodedKeySpec(((MLDSAPrivateKey)kpValid.getPrivate()).getEncoded(true))); + kFact.generatePrivate(new PKCS8EncodedKeySpec(((MLDSAPrivateKey)kpValid.getPrivate()).getPrivateKey(true).getEncoded())); + kFact.generatePrivate(new PKCS8EncodedKeySpec(((MLDSAPrivateKey)kpValid.getPrivate()).getPrivateKey(false).getEncoded())); kFact.generatePublic(new X509EncodedKeySpec(kpValid.getPublic().getEncoded())); try @@ -192,12 +193,11 @@ public void testDefaultPrivateKeyEncoding() assertTrue(Arrays.areEqual(seq.getOctets(), seed)); - ASN1OctetString privData = ASN1OctetString.getInstance((ASN1TaggedObject)ASN1Sequence.getInstance(privInfo.getPrivateKey().getOctets()).getObjectAt(1), false); + ASN1OctetString privData = ASN1OctetString.getInstance(ASN1Sequence.getInstance(privInfo.getPrivateKey().getOctets()).getObjectAt(1)); assertTrue(Arrays.areEqual(privData.getOctets(), ((MLDSAPrivateKey)kp44.getPrivate()).getPrivateData())); } - public void testSeedPrivateKeyEncoding() throws Exception { @@ -208,14 +208,27 @@ public void testSeedPrivateKeyEncoding() kpGen44.initialize(MLDSAParameterSpec.ml_dsa_44, new FixedSecureRandom(seed)); KeyPair kp44 = kpGen44.generateKeyPair(); - Security.setProperty("org.bouncycastle.mldsa.seedOnly", "true"); - - PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp44.getPrivate().getEncoded()); + PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(((MLDSAPrivateKey)kp44.getPrivate()).getPrivateKey(true).getEncoded()); - Security.setProperty("org.bouncycastle.mldsa.seedOnly", "false"); ASN1OctetString k = privInfo.getPrivateKey(); - assertTrue(Arrays.areEqual(k.getOctets(), seed)); + assertTrue(Arrays.areEqual(ASN1OctetString.getInstance((ASN1TaggedObject)privInfo.parsePrivateKey(), false).getOctets(), seed)); + } + + public void testExpandedKeyPrivateKeyEncoding() + throws Exception + { + KeyPairGenerator kpGen44 = KeyPairGenerator.getInstance("ML-DSA-44"); + + byte[] seed = Hex.decode("000102030405060708090a0b0c0d0e0f" + "100102030405060708090a0b0c0d0e0f"); + byte[] expandedKey = Base64.decode("w/FofbPea45APAHeQqNeZL4KcvMHr8V/inNsUbmYlE0vTZbckQVhHq8sQi2F3yC7B1/aLXIta1rImyPmzsYITzPH4Iz+3/ysH0n3TDx9HFKMp5WwuOX7ZRnNqS/RaiaYltSR0LBwyi5uPgGqw1Cb/mAsJkoJuGx31QGfc1KTMSQYAjEiqU0QJjHAlAyBtiRSxkGZIFFYGAYIIEXSSC4YE2rAooADOQoIQmwbKARSQiWBRExbOIkEQEVJIEQcEUCTxhEjQCahpJFLwDEIhIFamETJqDEbEWrKyCwBKWnCQECcNgYDtAQMMWwMQTFjwCiisEUTQEGiFAoRQibaQmmaokgjFUYJREmMBHAClSABlyRQFAlQECCgRiSCJIEQEwoACSRElg0QEjBSFAgIOQLIuDCiRHHcFoIZp0zkCDCbBIkbFYnYFAQMQ1ELGSCYICDSBGhhBmAcoS0JQ0HBEGGUiIlTIkYaGGpJMJHLtCDAEDKBpgXUAgoSxQAjQm0EuUQjCBDcIkbKwkUUom0clomLKHETNpBioEBTpEDLEkocIzJiQI0JBAIaMQHaRCQbkW0KSHHgBGADoi0AFHHYFA3hgg0ayU1iloyIAEmQBJLLKC4JgQADhiGbmIiUggBcwCWDIiwByWCSEExaRC0ZkgALM0kTlZCJAnHUNEaYlJEEMU6JtAUAo0gRJ23kokkSxoREsjCSomBUyCFEAEpUJmkDkWVLloSSQkpjkm0AEQqZxFEIE4gjGCGTpkEhsEAUEyIZqEAjJg1BRlHBmCEJg4AZSGobNE5gMIWRRmTMtGBcgCVhpo2bwCDjFFLbAkJksHCZRAEKMVBjQi1jhkQQpg3EgmybBjDDqC3hRIBMoGHUpiHIMilQEgpQMA7QEClMBolUNihTAmDQAICTEm6IqEATCCEMOCoEEYwKN4maRDAjkSEJRmnIohALgAADBCoBhjFgJiIYkWmTQiHkKA4cImYLk3CSBIKUEA4gE2ibMkRjkkUKACyMRkYRoWQUl0wbmW2QBkkJR4AgR2iCJAYUMgpLKGwgKVHiBE4iBRALNYwbFwwUAGpQwAwDQSwSwQAQAi7REgrSIAlbkHEJyZAASWDMMm4YlSHiwFFIFkrbFI6IJJERJUhLlkXRyCAAk5GDoC2JyJGYQgkRRGkTJE2jRCzUGFEiF1DAghECNIJIgHAgQWlvzZh7vICP92WCMX3B8zbvb7tgGJ1FERU17RMAxgnav4gEW/oe9DFzrwvEKF4Ku25tc0FQgdmnvLQFYwpTQuDjTxsug4GZViD6JP0GvZpbvfRhSWIjeFVdQcspJ26wR3IaM4M8yRhim1xEfUf7HcopRHQ6bzJ0B7rhE60A1mouwYG8ekzSxQ4WPtqecHdT57vYaEjOqjM0g3vUKa8ybDzUUVJDXx/6pcww596LIBNHygVaBGdRyAhNYxzSg0Z9BuxN295I3WJLU13+yYsdgvMq/qoj0LVZPevCjMBLnGnBAGsXxpYmTZbiyE6v5vQO3xWB6So6vX+G82gl0T33gZEw0B0stRf76XAcHHNkQl5nz1ZdkCS2QGh8Or4nmnuatXYkfXSP8YWDFz7bgtISNta6w7iiluZOySkDb8unuWXa2lUiSbHOJJ1vJ3kxa/jOpQvKULjQ49VFJQpH+vVVZKRtXkvpkFnOmISVSx0rfus9NJCPpQm8S7DJH6pRA1ZgKYsLochGcs0pjWJGAZtUC9V0Bz7gz5XEWJzouDZZe7DfOagc/Ts7irQmpLMFuBeno9kzplKmYSDW1N88uO9CsCP4JOY/EKusbqzRu/032a0I9kozn8gyw3aAU6MArCVbfmr4/L+LV+MhLlnxe2RHy9iLw0SmVI2nJDNoLWichbLKJ7euagFMjqyw45WTmIYXKvX8rsl9TATOwcvL+0okzt8GuDyDmWej1UoGPPC74eyRfVVifPCPfb0M/o+QVwsNMggT11piJcEYmPS3hJWlTJ6t3WDIqwpvh9Bh2OBmNyJ0xMC3Zw3rNWVCLalzxf3RdOhINiSS91DtDP5FDqnZ8Br0HK5GCN00NPfWNt0FjNAWYuRoIBPqx471i+91mQAM7cRwP8izxj7D+wcGQMyQs/FA3ALXU62532rTt/eG90vPtTtj6Jvxcqx+l5oclJIZ/6qQjaqpzP6DIhlPa0e6FBiwRefdazXu2C5gPKtyY9YukXWftFnu3xDqRvxcYd/LBeK0utJYtERs4U2naS0W/8DMRrTTtiME4QZf8QSHnQoEjOS+ZNxb1yUUdmH0FKqeLgPNfXfVmD1B6ZTp/B02o3uRd7y9k4f5IKIPQedW4gdUIfycBP20lYmV5U1xcYVG6r/r1L1kfeCwlEsJ/3NsapVQx2JVuJ+EKBdqsx3zUHHSLnk3kRoZ4q0E5KRPccxgEGCpb2rK4W+hwvskcqfhKZQhYij4B10yzeno8FNIxT2vvXyALAxg2t4nSCVX7Wc1jv9ufEV3z1d5xzo5IDRHVkM1qT+V6zx+mkwXXksaVLiMNkeoy3/FF7uJxNcLajSwUwxht5LAzALa3LqlMyleIvK2fGWiqLaDkp1aYwvzc4i66sm42x2LHE6XwVVR/RnHj/1cg9rbcdVr1huGWA+TmLc0u01EYZrZnc0fGi9BnlYvm3JcEaNJ3f09cB/iN7pgMD/j8kzhykd0RsJgS5sU1ggAFkK4E9kt3WPrNqyIP1jdUZ30+Qnj6KL/bcTcvaxbC6Y1E0sIQgTSaQC9n3yhKawohtEjRDGnbqqltCw9qw6MRjTXWEPeju5YwRpN0DeVNB+3B8/sLeR7aR2tvbk7tyHvB9pW6WSInfmsGnTPqVChU7F0m/qYeP6PwjGK8TY69Pr0ziBh0JOLKYRVG2RwQnZGsGoLoXSHxZCV90Q5mIRagpGDBslvJTcxWtnUWT/ktFYkiFXHvW09hD/jqjzBRXSKOzfZnr5Xery2BIcmslYrSYbb38dY/3TmFL2k56R7nXS2uQEzhFJhT1OjOoOZ6hy6fGlNHhTXhiK0ZmF6AGb/Nw1nS0zW3HEa0wTJ5gnFUQrgkgon3NBtah5+dBsxNGn02O24vF4Lg8Va1Zj04igpuE/CctbUlJ5YKqqAj2SwL5hpBtNk8eV0srIFaFImaMcgzTJXt4tz5gOI3Ds3fkLjt3NBdLwMjw9VegdKHboCCYhVPzFY2lG++5LqJud37flp7Ikni17qpdUYO7khvsMLeUru6ouFqdV6fVdVywn2bW22Xs0DNNIfXGjnfhwhZ4AEvOqyFXmYHYqRYWOtBIxWu0aUtvtjcIi8gkDNsi0kY2Iuf6UFqwN7zqvuGYwsuC+n0UODwdi/48k1FWz/pOzqeaxo9O6AccKNEdDaXT4kwuJZsuvuKo0zv9IxDkVYoRB5Jx+vt58MtKGSxSpylWpJfw=="); + + kpGen44.initialize(MLDSAParameterSpec.ml_dsa_44, new FixedSecureRandom(seed)); + KeyPair kp44 = kpGen44.generateKeyPair(); + + PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(((MLDSAPrivateKey)kp44.getPrivate()).getPrivateKey(false).getEncoded()); + + assertTrue(Arrays.areEqual(ASN1OctetString.getInstance(privInfo.parsePrivateKey()).getOctets(), expandedKey)); } public void testPrivateKeyRecoding() From 7822234fc5998b8d7dade567252e40e934d30def Mon Sep 17 00:00:00 2001 From: David Hook Date: Fri, 21 Feb 2025 16:11:09 +1100 Subject: [PATCH 122/890] updated parsing to handle CHOICE in ML-DSA. --- .../mldsa/MLDSAPrivateKeyParameters.java | 37 +++++++++++++++++++ .../crypto/util/PrivateKeyInfoFactory.java | 14 +++---- .../bouncycastle/pqc/crypto/util/Utils.java | 6 +++ 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAPrivateKeyParameters.java index e9e6562d6c..ead219ad99 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAPrivateKeyParameters.java @@ -5,6 +5,10 @@ public class MLDSAPrivateKeyParameters extends MLDSAKeyParameters { + public static final int BOTH = 0; + public static final int SEED_ONLY = 1; + public static final int EXPANDED_KEY = 2; + final byte[] rho; final byte[] k; final byte[] tr; @@ -15,6 +19,8 @@ public class MLDSAPrivateKeyParameters private final byte[] t1; private final byte[] seed; + private final int prefFormat; + public MLDSAPrivateKeyParameters(MLDSAParameters params, byte[] encoding) { this(params, encoding, null); @@ -36,6 +42,7 @@ public MLDSAPrivateKeyParameters(MLDSAParameters params, byte[] rho, byte[] K, b this.t0 = Arrays.clone(t0); this.t1 = Arrays.clone(t1); this.seed = Arrays.clone(seed); + this.prefFormat = (seed != null) ? BOTH : EXPANDED_KEY; } public MLDSAPrivateKeyParameters(MLDSAParameters params, byte[] encoding, MLDSAPublicKeyParameters pubKey) @@ -86,6 +93,36 @@ public MLDSAPrivateKeyParameters(MLDSAParameters params, byte[] encoding, MLDSAP this.seed = null; } + this.prefFormat = (seed != null) ? BOTH : EXPANDED_KEY; + } + + private MLDSAPrivateKeyParameters(MLDSAPrivateKeyParameters params, int preferredFormat) + { + super(true, params.getParameters()); + + this.rho = params.rho; + this.k = params.k; + this.tr = params.tr; + this.s1 = params.s1; + this.s2 = params.s2; + this.t0 = params.t0; + this.t1 = params.t1; + this.seed = params.seed; + this.prefFormat = preferredFormat; + } + + public MLDSAPrivateKeyParameters getParametersWithFormat(int format) + { + if (this.seed == null && format == SEED_ONLY) + { + throw new IllegalArgumentException("no seed available"); + } + return new MLDSAPrivateKeyParameters(this, format); + } + + public int getPreferredFormat() + { + return prefFormat; } public byte[] getEncoded() diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java index 941425df3f..69730fccc2 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java @@ -298,13 +298,13 @@ else if (privateKey instanceof MLDSAPrivateKeyParameters) AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.mldsaOidLookup(params.getParameters())); byte[] seed = params.getSeed(); - if (Properties.isOverrideSet("org.bouncycastle.mldsa.seedOnly")) + if (params.getPreferredFormat() == MLDSAPrivateKeyParameters.SEED_ONLY) { - if (seed == null) // very difficult to imagine, but... - { - throw new IOException("no seed available"); - } - return new PrivateKeyInfo(algorithmIdentifier, seed, attributes); + return new PrivateKeyInfo(algorithmIdentifier, new DERTaggedObject(false, 0, new DEROctetString(params.getSeed())), attributes); + } + else if (params.getPreferredFormat() == MLDSAPrivateKeyParameters.EXPANDED_KEY) + { + return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(params.getEncoded()), attributes); } return new PrivateKeyInfo(algorithmIdentifier, getBasicPQCEncoding(params.getSeed(), params.getEncoded()), attributes); } @@ -406,7 +406,7 @@ private static ASN1Sequence getBasicPQCEncoding(byte[] seed, byte[] expanded) if (expanded != null) { - v.add(new DERTaggedObject(false, 1, new DEROctetString(expanded))); + v.add(new DEROctetString(expanded)); } return new DERSequence(v); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java index 72cbc901c5..496211c67d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java @@ -8,6 +8,7 @@ import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.BERTags; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.bc.BCObjectIdentifiers; @@ -830,6 +831,11 @@ static ASN1Primitive parseData(byte[] data) { return ASN1OctetString.getInstance(data); } + + if ((data[0] & 0xff) == BERTags.TAGGED) + { + return ASN1OctetString.getInstance(ASN1TaggedObject.getInstance(data), false); + } } return null; From 2e96d8e48c3543957ccd7a899e4d5dc9f619fd83 Mon Sep 17 00:00:00 2001 From: David Hook Date: Fri, 21 Feb 2025 17:40:02 +1100 Subject: [PATCH 123/890] partial update to new key encoding --- .../bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java index 1db24e0a05..516763c292 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java @@ -21,7 +21,6 @@ import junit.framework.TestCase; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Sequence; -import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.jcajce.SecretKeyWithEncapsulation; @@ -138,13 +137,11 @@ public void testDefaultPrivateKeyEncoding() KeyPair kp512 = kpGen512.generateKeyPair(); PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp512.getPrivate().getEncoded()); - ASN1OctetString seq = ASN1OctetString.getInstance(ASN1Sequence.getInstance(privInfo.getPrivateKey().getOctets()).getObjectAt(0)); + ASN1Sequence seq = ASN1Sequence.getInstance(privInfo.getPrivateKey().getOctets()); - assertTrue(Arrays.areEqual(seq.getOctets(), seed)); + assertTrue(Arrays.areEqual(ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets(), seed)); - ASN1OctetString privData = ASN1OctetString.getInstance((ASN1TaggedObject)ASN1Sequence.getInstance(privInfo.getPrivateKey().getOctets()).getObjectAt(1), false); - - assertTrue(Arrays.areEqual(privData.getOctets(), ((MLKEMPrivateKey)kp512.getPrivate()).getPrivateData())); + assertTrue(Arrays.areEqual(ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets(), ((MLKEMPrivateKey)kp512.getPrivate()).getPrivateData())); } public void testSeedPrivateKeyEncoding() From ce4aeba238865fd685919b8fd47ff7a9ba87a9a5 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sat, 22 Feb 2025 10:34:10 +1100 Subject: [PATCH 124/890] added choice key encodings similar to ML-DSA --- .../mlkem/MLKEMPrivateKeyParameters.java | 38 ++++++++++++++++++- .../crypto/util/PrivateKeyInfoFactory.java | 17 ++++----- .../jcajce/interfaces/MLKEMPrivateKey.java | 8 ++++ .../asymmetric/mlkem/BCMLKEMPrivateKey.java | 16 ++++++++ .../pqc/jcajce/provider/test/MLKEMTest.java | 28 ++++++++++++-- 5 files changed, 92 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java index b59198a6f1..2eab3f5ba2 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java @@ -5,13 +5,19 @@ public class MLKEMPrivateKeyParameters extends MLKEMKeyParameters { + public static final int BOTH = 0; + public static final int SEED_ONLY = 1; + public static final int EXPANDED_KEY = 2; + final byte[] s; final byte[] hpk; final byte[] nonce; final byte[] t; final byte[] rho; final byte[] seed; - + + private final int prefFormat; + public MLKEMPrivateKeyParameters(MLKEMParameters params, byte[] s, byte[] hpk, byte[] nonce, byte[] t, byte[] rho) { this(params, s, hpk, nonce, t, rho, null); @@ -27,6 +33,7 @@ public MLKEMPrivateKeyParameters(MLKEMParameters params, byte[] s, byte[] hpk, b this.t = Arrays.clone(t); this.rho = Arrays.clone(rho); this.seed = Arrays.clone(seed); + this.prefFormat = BOTH; } public MLKEMPrivateKeyParameters(MLKEMParameters params, byte[] encoding) @@ -60,6 +67,35 @@ public MLKEMPrivateKeyParameters(MLKEMParameters params, byte[] encoding) this.nonce = Arrays.copyOfRange(encoding, index, index + MLKEMEngine.KyberSymBytes); this.seed = null; } + + this.prefFormat = BOTH; + } + + private MLKEMPrivateKeyParameters(MLKEMPrivateKeyParameters params, int preferredFormat) + { + super(true, params.getParameters()); + + this.s = params.s; + this.t = params.t; + this.rho = params.rho; + this.hpk = params.hpk; + this.nonce = params.nonce; + this.seed = params.seed; + this.prefFormat = preferredFormat; + } + + public MLKEMPrivateKeyParameters getParametersWithFormat(int format) + { + if (this.seed == null && format == SEED_ONLY) + { + throw new IllegalArgumentException("no seed available"); + } + return new MLKEMPrivateKeyParameters(this, format); + } + + public int getPreferredFormat() + { + return prefFormat; } public byte[] getEncoded() diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java index 69730fccc2..fbe8e45bae 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java @@ -53,7 +53,6 @@ import org.bouncycastle.pqc.legacy.crypto.mceliece.McElieceCCA2PrivateKeyParameters; import org.bouncycastle.pqc.legacy.crypto.qtesla.QTESLAPrivateKeyParameters; import org.bouncycastle.util.Pack; -import org.bouncycastle.util.Properties; /** * Factory to create ASN.1 private key info objects from lightweight private keys. @@ -249,16 +248,15 @@ else if (privateKey instanceof MLKEMPrivateKeyParameters) AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.mlkemOidLookup(params.getParameters())); - byte[] seed = params.getSeed(); - if (Properties.isOverrideSet("org.bouncycastle.mlkem.seedOnly")) + if (params.getPreferredFormat() == MLKEMPrivateKeyParameters.SEED_ONLY) { - if (seed == null) // very difficult to imagine, but... - { - throw new IOException("no seed available"); - } - return new PrivateKeyInfo(algorithmIdentifier, seed, attributes); + return new PrivateKeyInfo(algorithmIdentifier, new DERTaggedObject(false, 0, new DEROctetString(params.getSeed())), attributes); } - return new PrivateKeyInfo(algorithmIdentifier, getBasicPQCEncoding(seed, params.getEncoded()), attributes); + else if (params.getPreferredFormat() == MLKEMPrivateKeyParameters.EXPANDED_KEY) + { + return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(params.getEncoded()), attributes); + } + return new PrivateKeyInfo(algorithmIdentifier, getBasicPQCEncoding(params.getSeed(), params.getEncoded()), attributes); } else if (privateKey instanceof NTRULPRimePrivateKeyParameters) { @@ -297,7 +295,6 @@ else if (privateKey instanceof MLDSAPrivateKeyParameters) AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.mldsaOidLookup(params.getParameters())); - byte[] seed = params.getSeed(); if (params.getPreferredFormat() == MLDSAPrivateKeyParameters.SEED_ONLY) { return new PrivateKeyInfo(algorithmIdentifier, new DERTaggedObject(false, 0, new DEROctetString(params.getSeed())), attributes); diff --git a/prov/src/main/java/org/bouncycastle/jcajce/interfaces/MLKEMPrivateKey.java b/prov/src/main/java/org/bouncycastle/jcajce/interfaces/MLKEMPrivateKey.java index 7c8d7b6f34..38ecf2bf06 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/interfaces/MLKEMPrivateKey.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/interfaces/MLKEMPrivateKey.java @@ -25,4 +25,12 @@ public interface MLKEMPrivateKey * @return the seed for the private key, null if not available. */ byte[] getSeed(); + + /** + * Return a privateKey which will encode as seed-only or as an expanded-key. + * + * @param preferSeedOnly if true, return a privateKey which will encode to seed-only if possible. + * @return a new MLKEMPrivateKey which encodes to either seed-only or expanded-key. + */ + MLKEMPrivateKey getPrivateKey(boolean preferSeedOnly); } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java index 1a3f922cbf..3937b4a1e0 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/BCMLKEMPrivateKey.java @@ -9,6 +9,7 @@ import org.bouncycastle.jcajce.interfaces.MLKEMPrivateKey; import org.bouncycastle.jcajce.interfaces.MLKEMPublicKey; import org.bouncycastle.jcajce.spec.MLKEMParameterSpec; +import org.bouncycastle.pqc.crypto.mldsa.MLDSAPrivateKeyParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMPrivateKeyParameters; import org.bouncycastle.pqc.crypto.util.PrivateKeyFactory; import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory; @@ -120,6 +121,21 @@ public byte[] getSeed() return params.getSeed(); } + @Override + public MLKEMPrivateKey getPrivateKey(boolean preferSeedOnly) + { + if (preferSeedOnly) + { + byte[] seed = params.getSeed(); + if (seed != null) + { + return new BCMLKEMPrivateKey(this.params.getParametersWithFormat(MLDSAPrivateKeyParameters.SEED_ONLY)); + } + } + + return new BCMLKEMPrivateKey(this.params.getParametersWithFormat(MLDSAPrivateKeyParameters.EXPANDED_KEY)); + } + public MLKEMParameterSpec getParameterSpec() { return MLKEMParameterSpec.fromName(params.getParameters().getName()); diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java index 516763c292..ffd4b5f237 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java @@ -21,6 +21,7 @@ import junit.framework.TestCase; import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.jcajce.SecretKeyWithEncapsulation; @@ -155,16 +156,35 @@ public void testSeedPrivateKeyEncoding() + "300102030405060708090a0b0c0d0e0f"); kpGen512.initialize(MLKEMParameterSpec.ml_kem_512, new FixedSecureRandom(seed)); KeyPair kp512 = kpGen512.generateKeyPair(); - Security.setProperty("org.bouncycastle.mlkem.seedOnly", "true"); - PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp512.getPrivate().getEncoded()); + PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(((MLKEMPrivateKey)kp512.getPrivate()).getPrivateKey(true).getEncoded()); - Security.setProperty("org.bouncycastle.mlkem.seedOnly", "false"); - ASN1OctetString k = privInfo.getPrivateKey(); + ASN1OctetString k = ASN1OctetString.getInstance((ASN1TaggedObject)privInfo.parsePrivateKey(), false); assertTrue(Arrays.areEqual(k.getOctets(), seed)); } + public void testExpandedPrivateKeyEncoding() + throws Exception + { + KeyPairGenerator kpGen512 = KeyPairGenerator.getInstance("ML-KEM-512", "BC"); + + byte[] seed = Hex.decode("000102030405060708090a0b0c0d0e0f" + + "100102030405060708090a0b0c0d0e0f" + + "200102030405060708090a0b0c0d0e0f" + + "300102030405060708090a0b0c0d0e0f"); + byte[] expanded = Base64.decode("WCF6H9yVm+nHHlpz1fOL8Oibz2GOuXSDQhwXrSnDraa2fYB0LxV12Tsp7FggB1xmvIg+GBXJoJcJ6APDuDCqE/RtW5eSZwKLI/cqeubNablS7SCQSORrZiNuVkoGgPikT9hzUDhvDhdE7tuD+cF1UfpECGZOFDFfaKeFufJlX/kcIAwFjgXOPqJrBiUd3BUsb2G81kxyJHfEODauV3p58dAWReW4Wfwzm8CP+OXMAsUiZTlHCzJxbaXFPwCBaokc/8XBd6FUOBLHAjxbVRKvuTQS8JEJLFTHvqYtehik66hEdNUJBfSpRGFalRFjvXp8acFXmluVTQsO6eoTL5pO0nZ1fhetf0RKgzeI07G0xIherqxln1dwj7VtM6WsW6WxZcFHkBQMWFiRUitxJ1QIbqY/1Po6uBEp/YYOPEoe7qMw2hxvadNEf7KiRPq6R+oxDMwnthex94O78ZNoDHiM5PdU4WKj8EkrySo49uS+d/YUnxxGZbSSY+uzTYY7D+w7zcapr0sgmmUIHtYEU6WxmCMFA8NmABySw0xOhJaDBsk5EWK1aZqkM+eqbhYfRwIpFvrKapAi86i6jAadcwm8ASRjmyZmRfCYkevG8kisVQg44TFbP6RYoXoFHzXI3BguJCmbGBS7YFSfwHcIiOybuJWQLqxIZZkPdzlGmnMr25uimbBxyzIB4Zw7HsZfGEGQ3xdAsCRhFskQw0XDHIVWZ9IZN/pofqUlNhqqeGCC6RaRkgDMdrLE8bBeB8WUt0SyXKgCZeXJBshKa3dhZeOq1yWNt5hB/rZKDgszqPmwv+hiZxURg6VUJPEoiXaAsbHBZKWROywm7WeK8ghdsyazhGFbJMdKhAOSYKBgWIBCcUBHZMLEhtm3gNLHdzGx4DtC4dGmJHxMRhFoH5cX/ARlO+RcKteNZTcyT+hlSIgyU/wBgxzBhIwsVmkMYiuPGnmqEAF0+LI79VqrTDJFmdBYGJsdShOV8Ey4JGulmoRN3oyOtvsPPpJ8Qfl0xgJLn6Ns87KcI/MUaXtilMpeYwxS1AwFnvJZGzDKh1GxTtKD87iTzErIgiV0AFojsEZ59AkvjGmFw1jPvhSIXsF900EMc8O2oWArKHIYzXWL2AmfDmmmjFxTW4Ic5xyQejkibiolbHhAb0RYYesI1ZmUnXCjyiBSz/CpgIyFIjqVXAOxGmy5XJdMobDJbhh0xnV3uytNGXuRiYqjnaQTzVEpbNUYXbAdVoFuHfwMKfy6eHIBXoLJFnpdLOEYnYireCYv1mrGYjWsH9MY4Ys17vvIcFSia/Rc+VtH9CJk2bqgEVqK2TsV6KgREaJgDBmj0BRp8ourO/YAFMslxLcLQDqrKtA4JhGcAzhsYjpzQlBXljSlqmFKWmjCIBRFrFaMG+xv76YBaqh+0aEQKOVT2JwNyADG3SonhoUDovo+J9UlaGljwmeaAQCouQZDCXELgreKpHOFaMK7RpQHNjtlv+FuV+k7WnudORs9Nde92Kw13SRSulmQtao1SHJf0VRcvuQu3eMzLLsjluElKqEpvDZX/Cg76XgEptYaEqlMQNSa6fh30bc+GhoyFRGx3zk3zvbGrtrEWiPIacCMeQKMmieN8apjragAiZsGdpMCsfxq1xAJraHB4cMGSix3ZOp8riKumiO4EQwDhtJGu4NXbLC459JkqaEP1HTA/jNpwiIRBsHE3dlOaFE7iJk+znrMXzcG+1fHAXhDatwPEizALupyhTxAiJsCWzxctKFbzouzbwcuXAKqJbldNdRsx+ESaugoN+QKLmEJOJlpjWKFIORE4daWXpoCz4WPw1ADCTiDQ3WzTPQn46tlgDDFwTx60eJk+CAn48Q+eYM3W6SDCtqLKYgUjLlJcakdKMmKJVdIL1A66gtH3jA/oJurjNGNaHK+ZNKDm9NxKrWQ9PmmBAiHsPKqIosLR4sZ6oANC4mtVHK3lZmYz9LI/ejIfLgd7aw1INBupOPPpGmrQqA4LcGgd0mMCVRLKxEqPBRuTDaLBAI8HdJUwsgwI3GrH1ZkAsNHBhmvUdSNjicplOoWHcNYi9U4FZkIifBjHUgGUNUhmw4Mkf3IfeYtJoLtjpYOIjQ3yFH8zzdEKBY74ILHT+BSWiakAyABAgMEBQYHCAkKCwwNDg8wAQIDBAUGBwgJCgsMDQ4P"); + + kpGen512.initialize(MLKEMParameterSpec.ml_kem_512, new FixedSecureRandom(seed)); + KeyPair kp512 = kpGen512.generateKeyPair(); + + PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(((MLKEMPrivateKey)kp512.getPrivate()).getPrivateKey(false).getEncoded()); + + ASN1OctetString k = ASN1OctetString.getInstance(privInfo.parsePrivateKey()); + + assertTrue(Arrays.areEqual(k.getOctets(), expanded)); + } + public void testPrivateKeyRecoding() throws Exception { From d6af532dc1ec4d86f114e12fd68da6eb10133470 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 24 Feb 2025 16:07:35 +1100 Subject: [PATCH 125/890] moved pubKey test for ML-DSA to apply to both encodings. added pubKey check to ML-KEM where pubKey is provided. --- .../crypto/mldsa/MLDSAPrivateKeyParameters.java | 14 +++++++------- .../crypto/mlkem/MLKEMPrivateKeyParameters.java | 13 +++++++++++++ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAPrivateKeyParameters.java index ead219ad99..03f56b6abd 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAPrivateKeyParameters.java @@ -82,17 +82,17 @@ public MLDSAPrivateKeyParameters(MLDSAParameters params, byte[] encoding, MLDSAP this.t0 = Arrays.copyOfRange(encoding, index, index + delta); index += delta; this.t1 = eng.deriveT1(rho, k, tr, s1, s2, t0); + this.seed = null; + } - if (pubKey != null) + if (pubKey != null) + { + if (!Arrays.constantTimeAreEqual(this.t1, pubKey.getT1())) { - if (!Arrays.constantTimeAreEqual(this.t1, pubKey.getT1())) - { - throw new IllegalArgumentException("passed in public key does not match private values"); - } + throw new IllegalArgumentException("passed in public key does not match private values"); } - - this.seed = null; } + this.prefFormat = (seed != null) ? BOTH : EXPANDED_KEY; } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java index 2eab3f5ba2..a2c5e96c82 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java @@ -37,6 +37,11 @@ public MLKEMPrivateKeyParameters(MLKEMParameters params, byte[] s, byte[] hpk, b } public MLKEMPrivateKeyParameters(MLKEMParameters params, byte[] encoding) + { + this(params, encoding, null); + } + + public MLKEMPrivateKeyParameters(MLKEMParameters params, byte[] encoding, MLKEMPublicKeyParameters pubKey) { super(true, params); @@ -68,6 +73,14 @@ public MLKEMPrivateKeyParameters(MLKEMParameters params, byte[] encoding) this.seed = null; } + if (pubKey != null) + { + if (!Arrays.constantTimeAreEqual(this.t, pubKey.t) || !Arrays.constantTimeAreEqual(this.rho, pubKey.rho)) + { + throw new IllegalArgumentException("passed in public key does not match private values"); + } + } + this.prefFormat = BOTH; } From ca4632b2fb0af7c637feddb5f9ff0516d34970ee Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 24 Feb 2025 16:09:24 +1100 Subject: [PATCH 126/890] added both check for key/seed verification for ML-KEM and ML-DSA. --- .../pqc/crypto/util/PrivateKeyFactory.java | 122 +++++++++--------- .../pqc/crypto/util/PublicKeyFactory.java | 41 +++++- .../provider/util/BaseKeyFactorySpi.java | 4 + 3 files changed, 101 insertions(+), 66 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java index 26ee5e5d88..fd5ca0146a 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java @@ -11,7 +11,6 @@ import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; -import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.bc.BCObjectIdentifiers; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; @@ -49,6 +48,7 @@ import org.bouncycastle.pqc.crypto.mldsa.MLDSAPublicKeyParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.mlkem.MLKEMPublicKeyParameters; import org.bouncycastle.pqc.crypto.newhope.NHPrivateKeyParameters; import org.bouncycastle.pqc.crypto.ntru.NTRUParameters; import org.bouncycastle.pqc.crypto.ntru.NTRUPrivateKeyParameters; @@ -92,7 +92,7 @@ public class PrivateKeyFactory * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(byte[] privateKeyInfoData) - throws IOException + throws IOException { if (privateKeyInfoData == null) { @@ -114,7 +114,7 @@ public static AsymmetricKeyParameter createKey(byte[] privateKeyInfoData) * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(InputStream inStr) - throws IOException + throws IOException { return createKey(PrivateKeyInfo.getInstance(new ASN1InputStream(inStr).readObject())); } @@ -127,7 +127,7 @@ public static AsymmetricKeyParameter createKey(InputStream inStr) * @throws IOException on an error decoding the key */ public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo) - throws IOException + throws IOException { if (keyInfo == null) { @@ -146,7 +146,7 @@ public static AsymmetricKeyParameter createKey(PrivateKeyInfo keyInfo) else if (algOID.equals(PQCObjectIdentifiers.sphincs256)) { return new SPHINCSPrivateKeyParameters(ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets(), - Utils.sphincs256LookupTreeAlgName(SPHINCS256KeyParams.getInstance(algId.getParameters()))); + Utils.sphincs256LookupTreeAlgName(SPHINCS256KeyParams.getInstance(algId.getParameters()))); } else if (algOID.equals(PQCObjectIdentifiers.newHope)) { @@ -176,7 +176,7 @@ else if (algOID.on(BCObjectIdentifiers.sphincsPlus) || algOID.on(BCObjectIdentif SPHINCSPLUSPrivateKey spKey = SPHINCSPLUSPrivateKey.getInstance(obj); SPHINCSPLUSPublicKey publicKey = spKey.getPublicKey(); return new SPHINCSPlusPrivateKeyParameters(spParams, spKey.getSkseed(), spKey.getSkprf(), - publicKey.getPkseed(), publicKey.getPkroot()); + publicKey.getPkseed(), publicKey.getPkroot()); } else { @@ -226,24 +226,29 @@ else if (algOID.on(BCObjectIdentifiers.pqc_kem_ntru)) return new NTRUPrivateKeyParameters(spParams, keyEnc); } else if (algOID.equals(NISTObjectIdentifiers.id_alg_ml_kem_512) || - algOID.equals(NISTObjectIdentifiers.id_alg_ml_kem_768) || - algOID.equals(NISTObjectIdentifiers.id_alg_ml_kem_1024)) + algOID.equals(NISTObjectIdentifiers.id_alg_ml_kem_768) || + algOID.equals(NISTObjectIdentifiers.id_alg_ml_kem_1024)) { ASN1Primitive mlkemKey = parsePrimitiveString(keyInfo.getPrivateKey(), 64); MLKEMParameters mlkemParams = Utils.mlkemParamsLookup(algOID); + MLKEMPublicKeyParameters pubParams = null; + if (keyInfo.getPublicKeyData() != null) + { + pubParams = PublicKeyFactory.MLKEMConverter.getPublicKeyParams(mlkemParams, keyInfo.getPublicKeyData()); + } + if (mlkemKey instanceof ASN1Sequence) { ASN1Sequence keySeq = ASN1Sequence.getInstance(mlkemKey); - if (keySeq.getObjectAt(0) instanceof ASN1OctetString) - { - return new MLKEMPrivateKeyParameters(mlkemParams, ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets()); - } - else + MLKEMPrivateKeyParameters mlkemPriv = new MLKEMPrivateKeyParameters(mlkemParams, ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(), pubParams); + if (!Arrays.constantTimeAreEqual(mlkemPriv.getEncoded(), ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets())) { - return new MLKEMPrivateKeyParameters(mlkemParams, ASN1OctetString.getInstance((ASN1TaggedObject)keySeq.getObjectAt(0), false).getOctets()); + throw new IllegalStateException("seed/expanded-key mismatch"); } + + return mlkemPriv; } else if (mlkemKey instanceof ASN1OctetString) { @@ -259,10 +264,10 @@ else if (algOID.on(BCObjectIdentifiers.pqc_kem_ntrulprime)) NTRULPRimeParameters spParams = Utils.ntrulprimeParamsLookup(algOID); return new NTRULPRimePrivateKeyParameters(spParams, - ASN1OctetString.getInstance(keyEnc.getObjectAt(0)).getOctets(), - ASN1OctetString.getInstance(keyEnc.getObjectAt(1)).getOctets(), - ASN1OctetString.getInstance(keyEnc.getObjectAt(2)).getOctets(), - ASN1OctetString.getInstance(keyEnc.getObjectAt(3)).getOctets()); + ASN1OctetString.getInstance(keyEnc.getObjectAt(0)).getOctets(), + ASN1OctetString.getInstance(keyEnc.getObjectAt(1)).getOctets(), + ASN1OctetString.getInstance(keyEnc.getObjectAt(2)).getOctets(), + ASN1OctetString.getInstance(keyEnc.getObjectAt(3)).getOctets()); } else if (algOID.on(BCObjectIdentifiers.pqc_kem_sntruprime)) { @@ -271,11 +276,11 @@ else if (algOID.on(BCObjectIdentifiers.pqc_kem_sntruprime)) SNTRUPrimeParameters spParams = Utils.sntruprimeParamsLookup(algOID); return new SNTRUPrimePrivateKeyParameters(spParams, - ASN1OctetString.getInstance(keyEnc.getObjectAt(0)).getOctets(), - ASN1OctetString.getInstance(keyEnc.getObjectAt(1)).getOctets(), - ASN1OctetString.getInstance(keyEnc.getObjectAt(2)).getOctets(), - ASN1OctetString.getInstance(keyEnc.getObjectAt(3)).getOctets(), - ASN1OctetString.getInstance(keyEnc.getObjectAt(4)).getOctets()); + ASN1OctetString.getInstance(keyEnc.getObjectAt(0)).getOctets(), + ASN1OctetString.getInstance(keyEnc.getObjectAt(1)).getOctets(), + ASN1OctetString.getInstance(keyEnc.getObjectAt(2)).getOctets(), + ASN1OctetString.getInstance(keyEnc.getObjectAt(3)).getOctets(), + ASN1OctetString.getInstance(keyEnc.getObjectAt(4)).getOctets()); } else if (Utils.mldsaParams.containsKey(algOID)) { @@ -298,14 +303,13 @@ else if (keyObj instanceof ASN1Sequence) { ASN1Sequence keySeq = ASN1Sequence.getInstance(keyObj); - if (keySeq.getObjectAt(0) instanceof ASN1OctetString) - { - return new MLDSAPrivateKeyParameters(spParams, ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(), pubParams); - } - else + MLDSAPrivateKeyParameters mldsaPriv = new MLDSAPrivateKeyParameters(spParams, ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(), pubParams); + if (!Arrays.constantTimeAreEqual(mldsaPriv.getEncoded(), ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets())) { - return new MLDSAPrivateKeyParameters(spParams, ASN1OctetString.getInstance((ASN1TaggedObject)keySeq.getObjectAt(0), false).getOctets(), pubParams); + throw new IllegalStateException("seed/expanded-key mismatch"); } + + return mldsaPriv; } else { @@ -313,7 +317,7 @@ else if (keyObj instanceof ASN1Sequence) } } else if (algOID.equals(BCObjectIdentifiers.dilithium2) - || algOID.equals(BCObjectIdentifiers.dilithium3) || algOID.equals(BCObjectIdentifiers.dilithium5)) + || algOID.equals(BCObjectIdentifiers.dilithium3) || algOID.equals(BCObjectIdentifiers.dilithium5)) { ASN1Encodable keyObj = keyInfo.parsePrivateKey(); DilithiumParameters dilParams = Utils.dilithiumParamsLookup(algOID); @@ -333,24 +337,24 @@ else if (algOID.equals(BCObjectIdentifiers.dilithium2) DilithiumPublicKeyParameters pubParams = PublicKeyFactory.DilithiumConverter.getPublicKeyParams(dilParams, keyInfo.getPublicKeyData()); return new DilithiumPrivateKeyParameters(dilParams, - ASN1BitString.getInstance(keyEnc.getObjectAt(1)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(2)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(3)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(4)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(5)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(6)).getOctets(), - pubParams.getT1()); // encT1 + ASN1BitString.getInstance(keyEnc.getObjectAt(1)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(2)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(3)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(4)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(5)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(6)).getOctets(), + pubParams.getT1()); // encT1 } else { return new DilithiumPrivateKeyParameters(dilParams, - ASN1BitString.getInstance(keyEnc.getObjectAt(1)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(2)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(3)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(4)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(5)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(6)).getOctets(), - null); + ASN1BitString.getInstance(keyEnc.getObjectAt(1)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(2)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(3)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(4)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(5)).getOctets(), + ASN1BitString.getInstance(keyEnc.getObjectAt(6)).getOctets(), + null); } } else if (keyObj instanceof DEROctetString) @@ -409,12 +413,12 @@ else if (algOID.equals(PQCObjectIdentifiers.xmss)) try { XMSSPrivateKeyParameters.Builder keyBuilder = new XMSSPrivateKeyParameters - .Builder(new XMSSParameters(keyParams.getHeight(), Utils.getDigest(treeDigest))) - .withIndex(xmssPrivateKey.getIndex()) - .withSecretKeySeed(xmssPrivateKey.getSecretKeySeed()) - .withSecretKeyPRF(xmssPrivateKey.getSecretKeyPRF()) - .withPublicSeed(xmssPrivateKey.getPublicSeed()) - .withRoot(xmssPrivateKey.getRoot()); + .Builder(new XMSSParameters(keyParams.getHeight(), Utils.getDigest(treeDigest))) + .withIndex(xmssPrivateKey.getIndex()) + .withSecretKeySeed(xmssPrivateKey.getSecretKeySeed()) + .withSecretKeyPRF(xmssPrivateKey.getSecretKeyPRF()) + .withPublicSeed(xmssPrivateKey.getPublicSeed()) + .withRoot(xmssPrivateKey.getRoot()); if (xmssPrivateKey.getVersion() != 0) { @@ -423,7 +427,7 @@ else if (algOID.equals(PQCObjectIdentifiers.xmss)) if (xmssPrivateKey.getBdsState() != null) { - BDS bds = (BDS) XMSSUtil.deserialize(xmssPrivateKey.getBdsState(), BDS.class); + BDS bds = (BDS)XMSSUtil.deserialize(xmssPrivateKey.getBdsState(), BDS.class); keyBuilder.withBDSState(bds.withWOTSDigest(treeDigest)); } @@ -444,12 +448,12 @@ else if (algOID.equals(PQCObjectIdentifiers.xmss_mt)) XMSSMTPrivateKey xmssMtPrivateKey = XMSSMTPrivateKey.getInstance(keyInfo.parsePrivateKey()); XMSSMTPrivateKeyParameters.Builder keyBuilder = new XMSSMTPrivateKeyParameters - .Builder(new XMSSMTParameters(keyParams.getHeight(), keyParams.getLayers(), Utils.getDigest(treeDigest))) - .withIndex(xmssMtPrivateKey.getIndex()) - .withSecretKeySeed(xmssMtPrivateKey.getSecretKeySeed()) - .withSecretKeyPRF(xmssMtPrivateKey.getSecretKeyPRF()) - .withPublicSeed(xmssMtPrivateKey.getPublicSeed()) - .withRoot(xmssMtPrivateKey.getRoot()); + .Builder(new XMSSMTParameters(keyParams.getHeight(), keyParams.getLayers(), Utils.getDigest(treeDigest))) + .withIndex(xmssMtPrivateKey.getIndex()) + .withSecretKeySeed(xmssMtPrivateKey.getSecretKeySeed()) + .withSecretKeyPRF(xmssMtPrivateKey.getSecretKeyPRF()) + .withPublicSeed(xmssMtPrivateKey.getPublicSeed()) + .withRoot(xmssMtPrivateKey.getRoot()); if (xmssMtPrivateKey.getVersion() != 0) { @@ -458,7 +462,7 @@ else if (algOID.equals(PQCObjectIdentifiers.xmss_mt)) if (xmssMtPrivateKey.getBdsState() != null) { - BDSStateMap bdsState = (BDSStateMap) XMSSUtil.deserialize(xmssMtPrivateKey.getBdsState(), BDSStateMap.class); + BDSStateMap bdsState = (BDSStateMap)XMSSUtil.deserialize(xmssMtPrivateKey.getBdsState(), BDSStateMap.class); keyBuilder.withBDSState(bdsState.withWOTSDigest(treeDigest)); } @@ -528,7 +532,7 @@ private static ASN1Primitive parsePrimitiveString(ASN1OctetString octStr, int ex // possible internal OCTET STRING, possibly long form with or without the internal OCTET STRING // or possible SEQUENCE ASN1Encodable obj = Utils.parseData(data); - + if (obj instanceof ASN1OctetString) { return ASN1OctetString.getInstance(obj); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java index d7320c8090..38384b2f7b 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java @@ -186,12 +186,12 @@ public class PublicKeyFactory converters.put(BCObjectIdentifiers.ntruhrss1373, new NtruConverter()); converters.put(BCObjectIdentifiers.falcon_512, new FalconConverter()); converters.put(BCObjectIdentifiers.falcon_1024, new FalconConverter()); - converters.put(NISTObjectIdentifiers.id_alg_ml_kem_512, new KyberConverter()); - converters.put(NISTObjectIdentifiers.id_alg_ml_kem_768, new KyberConverter()); - converters.put(NISTObjectIdentifiers.id_alg_ml_kem_1024, new KyberConverter()); - converters.put(BCObjectIdentifiers.kyber512_aes, new KyberConverter()); - converters.put(BCObjectIdentifiers.kyber768_aes, new KyberConverter()); - converters.put(BCObjectIdentifiers.kyber1024_aes, new KyberConverter()); + converters.put(NISTObjectIdentifiers.id_alg_ml_kem_512, new MLKEMConverter()); + converters.put(NISTObjectIdentifiers.id_alg_ml_kem_768, new MLKEMConverter()); + converters.put(NISTObjectIdentifiers.id_alg_ml_kem_1024, new MLKEMConverter()); + converters.put(BCObjectIdentifiers.kyber512_aes, new MLKEMConverter()); + converters.put(BCObjectIdentifiers.kyber768_aes, new MLKEMConverter()); + converters.put(BCObjectIdentifiers.kyber1024_aes, new MLKEMConverter()); converters.put(BCObjectIdentifiers.ntrulpr653, new NTRULPrimeConverter()); converters.put(BCObjectIdentifiers.ntrulpr761, new NTRULPrimeConverter()); converters.put(BCObjectIdentifiers.ntrulpr857, new NTRULPrimeConverter()); @@ -602,7 +602,7 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } } - private static class KyberConverter + static class MLKEMConverter extends SubjectPublicKeyInfoConverter { AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) @@ -623,6 +623,33 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje return new MLKEMPublicKeyParameters(kyberParameters, keyInfo.getPublicKeyData().getOctets()); } } + + static MLKEMPublicKeyParameters getPublicKeyParams(MLKEMParameters dilithiumParams, ASN1BitString publicKeyData) + { + try + { + ASN1Primitive obj = ASN1Primitive.fromByteArray(publicKeyData.getOctets()); + if (obj instanceof ASN1Sequence) + { + ASN1Sequence keySeq = ASN1Sequence.getInstance(obj); + + return new MLKEMPublicKeyParameters(dilithiumParams, + ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(), + ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets()); + } + else + { + byte[] encKey = ASN1OctetString.getInstance(obj).getOctets(); + + return new MLKEMPublicKeyParameters(dilithiumParams, encKey); + } + } + catch (Exception e) + { + // we're a raw encoding + return new MLKEMPublicKeyParameters(dilithiumParams, publicKeyData.getOctets()); + } + } } private static class NTRULPrimeConverter diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/BaseKeyFactorySpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/BaseKeyFactorySpi.java index 7abce93b22..e4be781da1 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/BaseKeyFactorySpi.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/BaseKeyFactorySpi.java @@ -64,6 +64,10 @@ public PrivateKey engineGeneratePrivate(KeySpec keySpec) { throw e; } + catch (IllegalStateException e) + { + throw new InvalidKeySpecException(e.getMessage()); + } catch (Exception e) { throw new InvalidKeySpecException(e.toString()); From 3804f98294bc9a6cfec8fa975b865a8e4853c7a7 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 24 Feb 2025 17:13:47 +1100 Subject: [PATCH 127/890] corrected use of BOTH in MLKEMPrivateKeyParameters. Removed null checks on "both" construction for ML-KEM and ML-DSA --- .../pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java | 2 +- .../pqc/crypto/util/PrivateKeyInfoFactory.java | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java index a2c5e96c82..2c91a70f46 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java @@ -81,7 +81,7 @@ public MLKEMPrivateKeyParameters(MLKEMParameters params, byte[] encoding, MLKEMP } } - this.prefFormat = BOTH; + this.prefFormat = (seed == null) ? EXPANDED_KEY : BOTH; } private MLKEMPrivateKeyParameters(MLKEMPrivateKeyParameters params, int preferredFormat) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java index fbe8e45bae..cd41f2465e 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java @@ -395,16 +395,10 @@ private static XMSSPrivateKey xmssCreateKeyStructure(XMSSPrivateKeyParameters ke private static ASN1Sequence getBasicPQCEncoding(byte[] seed, byte[] expanded) { ASN1EncodableVector v = new ASN1EncodableVector(2); - - if (seed != null) - { - v.add(new DEROctetString(seed)); - } - if (expanded != null) - { - v.add(new DEROctetString(expanded)); - } + v.add(new DEROctetString(seed)); + + v.add(new DEROctetString(expanded)); return new DERSequence(v); } From 0b48ab0f1d37c6cf58aa056145b53adc93533fb1 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 24 Feb 2025 23:37:57 +1100 Subject: [PATCH 128/890] commented out old-format encodings (earlier beta) --- .../pqc/jcajce/provider/test/MLDSATest.java | 13 +++++++------ .../pqc/jcajce/provider/test/MLKEMTest.java | 17 +++++++++-------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java index 6b19537129..b037cbd4fc 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java @@ -234,21 +234,22 @@ public void testExpandedKeyPrivateKeyEncoding() public void testPrivateKeyRecoding() throws Exception { - byte[] mldsa44_sequence = Base64.decode("MIIKPgIBADALBglghkgBZQMEAxEEggoqMIIKJgQgAAECAwQFBgcICQoLDA0ODxABAgMEBQYHCAkKCwwNDg+BggoAw/FofbPea45APAHeQqNeZL4KcvMHr8V/inNsUbmYlE0vTZbckQVhHq8sQi2F3yC7B1/aLXIta1rImyPmzsYITzPH4Iz+3/ysH0n3TDx9HFKMp5WwuOX7ZRnNqS/RaiaYltSR0LBwyi5uPgGqw1Cb/mAsJkoJuGx31QGfc1KTMSQYAjEiqU0QJjHAlAyBtiRSxkGZIFFYGAYIIEXSSC4YE2rAooADOQoIQmwbKARSQiWBRExbOIkEQEVJIEQcEUCTxhEjQCahpJFLwDEIhIFamETJqDEbEWrKyCwBKWnCQECcNgYDtAQMMWwMQTFjwCiisEUTQEGiFAoRQibaQmmaokgjFUYJREmMBHAClSABlyRQFAlQECCgRiSCJIEQEwoACSRElg0QEjBSFAgIOQLIuDCiRHHcFoIZp0zkCDCbBIkbFYnYFAQMQ1ELGSCYICDSBGhhBmAcoS0JQ0HBEGGUiIlTIkYaGGpJMJHLtCDAEDKBpgXUAgoSxQAjQm0EuUQjCBDcIkbKwkUUom0clomLKHETNpBioEBTpEDLEkocIzJiQI0JBAIaMQHaRCQbkW0KSHHgBGADoi0AFHHYFA3hgg0ayU1iloyIAEmQBJLLKC4JgQADhiGbmIiUggBcwCWDIiwByWCSEExaRC0ZkgALM0kTlZCJAnHUNEaYlJEEMU6JtAUAo0gRJ23kokkSxoREsjCSomBUyCFEAEpUJmkDkWVLloSSQkpjkm0AEQqZxFEIE4gjGCGTpkEhsEAUEyIZqEAjJg1BRlHBmCEJg4AZSGobNE5gMIWRRmTMtGBcgCVhpo2bwCDjFFLbAkJksHCZRAEKMVBjQi1jhkQQpg3EgmybBjDDqC3hRIBMoGHUpiHIMilQEgpQMA7QEClMBolUNihTAmDQAICTEm6IqEATCCEMOCoEEYwKN4maRDAjkSEJRmnIohALgAADBCoBhjFgJiIYkWmTQiHkKA4cImYLk3CSBIKUEA4gE2ibMkRjkkUKACyMRkYRoWQUl0wbmW2QBkkJR4AgR2iCJAYUMgpLKGwgKVHiBE4iBRALNYwbFwwUAGpQwAwDQSwSwQAQAi7REgrSIAlbkHEJyZAASWDMMm4YlSHiwFFIFkrbFI6IJJERJUhLlkXRyCAAk5GDoC2JyJGYQgkRRGkTJE2jRCzUGFEiF1DAghECNIJIgHAgQWlvzZh7vICP92WCMX3B8zbvb7tgGJ1FERU17RMAxgnav4gEW/oe9DFzrwvEKF4Ku25tc0FQgdmnvLQFYwpTQuDjTxsug4GZViD6JP0GvZpbvfRhSWIjeFVdQcspJ26wR3IaM4M8yRhim1xEfUf7HcopRHQ6bzJ0B7rhE60A1mouwYG8ekzSxQ4WPtqecHdT57vYaEjOqjM0g3vUKa8ybDzUUVJDXx/6pcww596LIBNHygVaBGdRyAhNYxzSg0Z9BuxN295I3WJLU13+yYsdgvMq/qoj0LVZPevCjMBLnGnBAGsXxpYmTZbiyE6v5vQO3xWB6So6vX+G82gl0T33gZEw0B0stRf76XAcHHNkQl5nz1ZdkCS2QGh8Or4nmnuatXYkfXSP8YWDFz7bgtISNta6w7iiluZOySkDb8unuWXa2lUiSbHOJJ1vJ3kxa/jOpQvKULjQ49VFJQpH+vVVZKRtXkvpkFnOmISVSx0rfus9NJCPpQm8S7DJH6pRA1ZgKYsLochGcs0pjWJGAZtUC9V0Bz7gz5XEWJzouDZZe7DfOagc/Ts7irQmpLMFuBeno9kzplKmYSDW1N88uO9CsCP4JOY/EKusbqzRu/032a0I9kozn8gyw3aAU6MArCVbfmr4/L+LV+MhLlnxe2RHy9iLw0SmVI2nJDNoLWichbLKJ7euagFMjqyw45WTmIYXKvX8rsl9TATOwcvL+0okzt8GuDyDmWej1UoGPPC74eyRfVVifPCPfb0M/o+QVwsNMggT11piJcEYmPS3hJWlTJ6t3WDIqwpvh9Bh2OBmNyJ0xMC3Zw3rNWVCLalzxf3RdOhINiSS91DtDP5FDqnZ8Br0HK5GCN00NPfWNt0FjNAWYuRoIBPqx471i+91mQAM7cRwP8izxj7D+wcGQMyQs/FA3ALXU62532rTt/eG90vPtTtj6Jvxcqx+l5oclJIZ/6qQjaqpzP6DIhlPa0e6FBiwRefdazXu2C5gPKtyY9YukXWftFnu3xDqRvxcYd/LBeK0utJYtERs4U2naS0W/8DMRrTTtiME4QZf8QSHnQoEjOS+ZNxb1yUUdmH0FKqeLgPNfXfVmD1B6ZTp/B02o3uRd7y9k4f5IKIPQedW4gdUIfycBP20lYmV5U1xcYVG6r/r1L1kfeCwlEsJ/3NsapVQx2JVuJ+EKBdqsx3zUHHSLnk3kRoZ4q0E5KRPccxgEGCpb2rK4W+hwvskcqfhKZQhYij4B10yzeno8FNIxT2vvXyALAxg2t4nSCVX7Wc1jv9ufEV3z1d5xzo5IDRHVkM1qT+V6zx+mkwXXksaVLiMNkeoy3/FF7uJxNcLajSwUwxht5LAzALa3LqlMyleIvK2fGWiqLaDkp1aYwvzc4i66sm42x2LHE6XwVVR/RnHj/1cg9rbcdVr1huGWA+TmLc0u01EYZrZnc0fGi9BnlYvm3JcEaNJ3f09cB/iN7pgMD/j8kzhykd0RsJgS5sU1ggAFkK4E9kt3WPrNqyIP1jdUZ30+Qnj6KL/bcTcvaxbC6Y1E0sIQgTSaQC9n3yhKawohtEjRDGnbqqltCw9qw6MRjTXWEPeju5YwRpN0DeVNB+3B8/sLeR7aR2tvbk7tyHvB9pW6WSInfmsGnTPqVChU7F0m/qYeP6PwjGK8TY69Pr0ziBh0JOLKYRVG2RwQnZGsGoLoXSHxZCV90Q5mIRagpGDBslvJTcxWtnUWT/ktFYkiFXHvW09hD/jqjzBRXSKOzfZnr5Xery2BIcmslYrSYbb38dY/3TmFL2k56R7nXS2uQEzhFJhT1OjOoOZ6hy6fGlNHhTXhiK0ZmF6AGb/Nw1nS0zW3HEa0wTJ5gnFUQrgkgon3NBtah5+dBsxNGn02O24vF4Lg8Va1Zj04igpuE/CctbUlJ5YKqqAj2SwL5hpBtNk8eV0srIFaFImaMcgzTJXt4tz5gOI3Ds3fkLjt3NBdLwMjw9VegdKHboCCYhVPzFY2lG++5LqJud37flp7Ikni17qpdUYO7khvsMLeUru6ouFqdV6fVdVywn2bW22Xs0DNNIfXGjnfhwhZ4AEvOqyFXmYHYqRYWOtBIxWu0aUtvtjcIi8gkDNsi0kY2Iuf6UFqwN7zqvuGYwsuC+n0UODwdi/48k1FWz/pOzqeaxo9O6AccKNEdDaXT4kwuJZsuvuKo0zv9IxDkVYoRB5Jx+vt58MtKGSxSpylWpJfw=="); + // TODO: rebuild with newer encodings. +// byte[] mldsa44_sequence = Base64.decode("MIIKPgIBADALBglghkgBZQMEAxEEggoqMIIKJgQgAAECAwQFBgcICQoLDA0ODxABAgMEBQYHCAkKCwwNDg+BggoAw/FofbPea45APAHeQqNeZL4KcvMHr8V/inNsUbmYlE0vTZbckQVhHq8sQi2F3yC7B1/aLXIta1rImyPmzsYITzPH4Iz+3/ysH0n3TDx9HFKMp5WwuOX7ZRnNqS/RaiaYltSR0LBwyi5uPgGqw1Cb/mAsJkoJuGx31QGfc1KTMSQYAjEiqU0QJjHAlAyBtiRSxkGZIFFYGAYIIEXSSC4YE2rAooADOQoIQmwbKARSQiWBRExbOIkEQEVJIEQcEUCTxhEjQCahpJFLwDEIhIFamETJqDEbEWrKyCwBKWnCQECcNgYDtAQMMWwMQTFjwCiisEUTQEGiFAoRQibaQmmaokgjFUYJREmMBHAClSABlyRQFAlQECCgRiSCJIEQEwoACSRElg0QEjBSFAgIOQLIuDCiRHHcFoIZp0zkCDCbBIkbFYnYFAQMQ1ELGSCYICDSBGhhBmAcoS0JQ0HBEGGUiIlTIkYaGGpJMJHLtCDAEDKBpgXUAgoSxQAjQm0EuUQjCBDcIkbKwkUUom0clomLKHETNpBioEBTpEDLEkocIzJiQI0JBAIaMQHaRCQbkW0KSHHgBGADoi0AFHHYFA3hgg0ayU1iloyIAEmQBJLLKC4JgQADhiGbmIiUggBcwCWDIiwByWCSEExaRC0ZkgALM0kTlZCJAnHUNEaYlJEEMU6JtAUAo0gRJ23kokkSxoREsjCSomBUyCFEAEpUJmkDkWVLloSSQkpjkm0AEQqZxFEIE4gjGCGTpkEhsEAUEyIZqEAjJg1BRlHBmCEJg4AZSGobNE5gMIWRRmTMtGBcgCVhpo2bwCDjFFLbAkJksHCZRAEKMVBjQi1jhkQQpg3EgmybBjDDqC3hRIBMoGHUpiHIMilQEgpQMA7QEClMBolUNihTAmDQAICTEm6IqEATCCEMOCoEEYwKN4maRDAjkSEJRmnIohALgAADBCoBhjFgJiIYkWmTQiHkKA4cImYLk3CSBIKUEA4gE2ibMkRjkkUKACyMRkYRoWQUl0wbmW2QBkkJR4AgR2iCJAYUMgpLKGwgKVHiBE4iBRALNYwbFwwUAGpQwAwDQSwSwQAQAi7REgrSIAlbkHEJyZAASWDMMm4YlSHiwFFIFkrbFI6IJJERJUhLlkXRyCAAk5GDoC2JyJGYQgkRRGkTJE2jRCzUGFEiF1DAghECNIJIgHAgQWlvzZh7vICP92WCMX3B8zbvb7tgGJ1FERU17RMAxgnav4gEW/oe9DFzrwvEKF4Ku25tc0FQgdmnvLQFYwpTQuDjTxsug4GZViD6JP0GvZpbvfRhSWIjeFVdQcspJ26wR3IaM4M8yRhim1xEfUf7HcopRHQ6bzJ0B7rhE60A1mouwYG8ekzSxQ4WPtqecHdT57vYaEjOqjM0g3vUKa8ybDzUUVJDXx/6pcww596LIBNHygVaBGdRyAhNYxzSg0Z9BuxN295I3WJLU13+yYsdgvMq/qoj0LVZPevCjMBLnGnBAGsXxpYmTZbiyE6v5vQO3xWB6So6vX+G82gl0T33gZEw0B0stRf76XAcHHNkQl5nz1ZdkCS2QGh8Or4nmnuatXYkfXSP8YWDFz7bgtISNta6w7iiluZOySkDb8unuWXa2lUiSbHOJJ1vJ3kxa/jOpQvKULjQ49VFJQpH+vVVZKRtXkvpkFnOmISVSx0rfus9NJCPpQm8S7DJH6pRA1ZgKYsLochGcs0pjWJGAZtUC9V0Bz7gz5XEWJzouDZZe7DfOagc/Ts7irQmpLMFuBeno9kzplKmYSDW1N88uO9CsCP4JOY/EKusbqzRu/032a0I9kozn8gyw3aAU6MArCVbfmr4/L+LV+MhLlnxe2RHy9iLw0SmVI2nJDNoLWichbLKJ7euagFMjqyw45WTmIYXKvX8rsl9TATOwcvL+0okzt8GuDyDmWej1UoGPPC74eyRfVVifPCPfb0M/o+QVwsNMggT11piJcEYmPS3hJWlTJ6t3WDIqwpvh9Bh2OBmNyJ0xMC3Zw3rNWVCLalzxf3RdOhINiSS91DtDP5FDqnZ8Br0HK5GCN00NPfWNt0FjNAWYuRoIBPqx471i+91mQAM7cRwP8izxj7D+wcGQMyQs/FA3ALXU62532rTt/eG90vPtTtj6Jvxcqx+l5oclJIZ/6qQjaqpzP6DIhlPa0e6FBiwRefdazXu2C5gPKtyY9YukXWftFnu3xDqRvxcYd/LBeK0utJYtERs4U2naS0W/8DMRrTTtiME4QZf8QSHnQoEjOS+ZNxb1yUUdmH0FKqeLgPNfXfVmD1B6ZTp/B02o3uRd7y9k4f5IKIPQedW4gdUIfycBP20lYmV5U1xcYVG6r/r1L1kfeCwlEsJ/3NsapVQx2JVuJ+EKBdqsx3zUHHSLnk3kRoZ4q0E5KRPccxgEGCpb2rK4W+hwvskcqfhKZQhYij4B10yzeno8FNIxT2vvXyALAxg2t4nSCVX7Wc1jv9ufEV3z1d5xzo5IDRHVkM1qT+V6zx+mkwXXksaVLiMNkeoy3/FF7uJxNcLajSwUwxht5LAzALa3LqlMyleIvK2fGWiqLaDkp1aYwvzc4i66sm42x2LHE6XwVVR/RnHj/1cg9rbcdVr1huGWA+TmLc0u01EYZrZnc0fGi9BnlYvm3JcEaNJ3f09cB/iN7pgMD/j8kzhykd0RsJgS5sU1ggAFkK4E9kt3WPrNqyIP1jdUZ30+Qnj6KL/bcTcvaxbC6Y1E0sIQgTSaQC9n3yhKawohtEjRDGnbqqltCw9qw6MRjTXWEPeju5YwRpN0DeVNB+3B8/sLeR7aR2tvbk7tyHvB9pW6WSInfmsGnTPqVChU7F0m/qYeP6PwjGK8TY69Pr0ziBh0JOLKYRVG2RwQnZGsGoLoXSHxZCV90Q5mIRagpGDBslvJTcxWtnUWT/ktFYkiFXHvW09hD/jqjzBRXSKOzfZnr5Xery2BIcmslYrSYbb38dY/3TmFL2k56R7nXS2uQEzhFJhT1OjOoOZ6hy6fGlNHhTXhiK0ZmF6AGb/Nw1nS0zW3HEa0wTJ5gnFUQrgkgon3NBtah5+dBsxNGn02O24vF4Lg8Va1Zj04igpuE/CctbUlJ5YKqqAj2SwL5hpBtNk8eV0srIFaFImaMcgzTJXt4tz5gOI3Ds3fkLjt3NBdLwMjw9VegdKHboCCYhVPzFY2lG++5LqJud37flp7Ikni17qpdUYO7khvsMLeUru6ouFqdV6fVdVywn2bW22Xs0DNNIfXGjnfhwhZ4AEvOqyFXmYHYqRYWOtBIxWu0aUtvtjcIi8gkDNsi0kY2Iuf6UFqwN7zqvuGYwsuC+n0UODwdi/48k1FWz/pOzqeaxo9O6AccKNEdDaXT4kwuJZsuvuKo0zv9IxDkVYoRB5Jx+vt58MtKGSxSpylWpJfw=="); byte[] mldsa44_seed_only = Base64.decode("MDICAQAwCwYJYIZIAWUDBAMRBCAAAQIDBAUGBwgJCgsMDQ4PEAECAwQFBgcICQoLDA0ODw=="); byte[] mldsa44_wrap_seed_only = Base64.decode("MDQCAQAwCwYJYIZIAWUDBAMRBCIEIAABAgMEBQYHCAkKCwwNDg8QAQIDBAUGBwgJCgsMDQ4P"); byte[] mldsa44_expanded_only = Base64.decode("MIIKFAIBADALBglghkgBZQMEAxEEggoAw/FofbPea45APAHeQqNeZL4KcvMHr8V/inNsUbmYlE0vTZbckQVhHq8sQi2F3yC7B1/aLXIta1rImyPmzsYITzPH4Iz+3/ysH0n3TDx9HFKMp5WwuOX7ZRnNqS/RaiaYltSR0LBwyi5uPgGqw1Cb/mAsJkoJuGx31QGfc1KTMSQYAjEiqU0QJjHAlAyBtiRSxkGZIFFYGAYIIEXSSC4YE2rAooADOQoIQmwbKARSQiWBRExbOIkEQEVJIEQcEUCTxhEjQCahpJFLwDEIhIFamETJqDEbEWrKyCwBKWnCQECcNgYDtAQMMWwMQTFjwCiisEUTQEGiFAoRQibaQmmaokgjFUYJREmMBHAClSABlyRQFAlQECCgRiSCJIEQEwoACSRElg0QEjBSFAgIOQLIuDCiRHHcFoIZp0zkCDCbBIkbFYnYFAQMQ1ELGSCYICDSBGhhBmAcoS0JQ0HBEGGUiIlTIkYaGGpJMJHLtCDAEDKBpgXUAgoSxQAjQm0EuUQjCBDcIkbKwkUUom0clomLKHETNpBioEBTpEDLEkocIzJiQI0JBAIaMQHaRCQbkW0KSHHgBGADoi0AFHHYFA3hgg0ayU1iloyIAEmQBJLLKC4JgQADhiGbmIiUggBcwCWDIiwByWCSEExaRC0ZkgALM0kTlZCJAnHUNEaYlJEEMU6JtAUAo0gRJ23kokkSxoREsjCSomBUyCFEAEpUJmkDkWVLloSSQkpjkm0AEQqZxFEIE4gjGCGTpkEhsEAUEyIZqEAjJg1BRlHBmCEJg4AZSGobNE5gMIWRRmTMtGBcgCVhpo2bwCDjFFLbAkJksHCZRAEKMVBjQi1jhkQQpg3EgmybBjDDqC3hRIBMoGHUpiHIMilQEgpQMA7QEClMBolUNihTAmDQAICTEm6IqEATCCEMOCoEEYwKN4maRDAjkSEJRmnIohALgAADBCoBhjFgJiIYkWmTQiHkKA4cImYLk3CSBIKUEA4gE2ibMkRjkkUKACyMRkYRoWQUl0wbmW2QBkkJR4AgR2iCJAYUMgpLKGwgKVHiBE4iBRALNYwbFwwUAGpQwAwDQSwSwQAQAi7REgrSIAlbkHEJyZAASWDMMm4YlSHiwFFIFkrbFI6IJJERJUhLlkXRyCAAk5GDoC2JyJGYQgkRRGkTJE2jRCzUGFEiF1DAghECNIJIgHAgQWlvzZh7vICP92WCMX3B8zbvb7tgGJ1FERU17RMAxgnav4gEW/oe9DFzrwvEKF4Ku25tc0FQgdmnvLQFYwpTQuDjTxsug4GZViD6JP0GvZpbvfRhSWIjeFVdQcspJ26wR3IaM4M8yRhim1xEfUf7HcopRHQ6bzJ0B7rhE60A1mouwYG8ekzSxQ4WPtqecHdT57vYaEjOqjM0g3vUKa8ybDzUUVJDXx/6pcww596LIBNHygVaBGdRyAhNYxzSg0Z9BuxN295I3WJLU13+yYsdgvMq/qoj0LVZPevCjMBLnGnBAGsXxpYmTZbiyE6v5vQO3xWB6So6vX+G82gl0T33gZEw0B0stRf76XAcHHNkQl5nz1ZdkCS2QGh8Or4nmnuatXYkfXSP8YWDFz7bgtISNta6w7iiluZOySkDb8unuWXa2lUiSbHOJJ1vJ3kxa/jOpQvKULjQ49VFJQpH+vVVZKRtXkvpkFnOmISVSx0rfus9NJCPpQm8S7DJH6pRA1ZgKYsLochGcs0pjWJGAZtUC9V0Bz7gz5XEWJzouDZZe7DfOagc/Ts7irQmpLMFuBeno9kzplKmYSDW1N88uO9CsCP4JOY/EKusbqzRu/032a0I9kozn8gyw3aAU6MArCVbfmr4/L+LV+MhLlnxe2RHy9iLw0SmVI2nJDNoLWichbLKJ7euagFMjqyw45WTmIYXKvX8rsl9TATOwcvL+0okzt8GuDyDmWej1UoGPPC74eyRfVVifPCPfb0M/o+QVwsNMggT11piJcEYmPS3hJWlTJ6t3WDIqwpvh9Bh2OBmNyJ0xMC3Zw3rNWVCLalzxf3RdOhINiSS91DtDP5FDqnZ8Br0HK5GCN00NPfWNt0FjNAWYuRoIBPqx471i+91mQAM7cRwP8izxj7D+wcGQMyQs/FA3ALXU62532rTt/eG90vPtTtj6Jvxcqx+l5oclJIZ/6qQjaqpzP6DIhlPa0e6FBiwRefdazXu2C5gPKtyY9YukXWftFnu3xDqRvxcYd/LBeK0utJYtERs4U2naS0W/8DMRrTTtiME4QZf8QSHnQoEjOS+ZNxb1yUUdmH0FKqeLgPNfXfVmD1B6ZTp/B02o3uRd7y9k4f5IKIPQedW4gdUIfycBP20lYmV5U1xcYVG6r/r1L1kfeCwlEsJ/3NsapVQx2JVuJ+EKBdqsx3zUHHSLnk3kRoZ4q0E5KRPccxgEGCpb2rK4W+hwvskcqfhKZQhYij4B10yzeno8FNIxT2vvXyALAxg2t4nSCVX7Wc1jv9ufEV3z1d5xzo5IDRHVkM1qT+V6zx+mkwXXksaVLiMNkeoy3/FF7uJxNcLajSwUwxht5LAzALa3LqlMyleIvK2fGWiqLaDkp1aYwvzc4i66sm42x2LHE6XwVVR/RnHj/1cg9rbcdVr1huGWA+TmLc0u01EYZrZnc0fGi9BnlYvm3JcEaNJ3f09cB/iN7pgMD/j8kzhykd0RsJgS5sU1ggAFkK4E9kt3WPrNqyIP1jdUZ30+Qnj6KL/bcTcvaxbC6Y1E0sIQgTSaQC9n3yhKawohtEjRDGnbqqltCw9qw6MRjTXWEPeju5YwRpN0DeVNB+3B8/sLeR7aR2tvbk7tyHvB9pW6WSInfmsGnTPqVChU7F0m/qYeP6PwjGK8TY69Pr0ziBh0JOLKYRVG2RwQnZGsGoLoXSHxZCV90Q5mIRagpGDBslvJTcxWtnUWT/ktFYkiFXHvW09hD/jqjzBRXSKOzfZnr5Xery2BIcmslYrSYbb38dY/3TmFL2k56R7nXS2uQEzhFJhT1OjOoOZ6hy6fGlNHhTXhiK0ZmF6AGb/Nw1nS0zW3HEa0wTJ5gnFUQrgkgon3NBtah5+dBsxNGn02O24vF4Lg8Va1Zj04igpuE/CctbUlJ5YKqqAj2SwL5hpBtNk8eV0srIFaFImaMcgzTJXt4tz5gOI3Ds3fkLjt3NBdLwMjw9VegdKHboCCYhVPzFY2lG++5LqJud37flp7Ikni17qpdUYO7khvsMLeUru6ouFqdV6fVdVywn2bW22Xs0DNNIfXGjnfhwhZ4AEvOqyFXmYHYqRYWOtBIxWu0aUtvtjcIi8gkDNsi0kY2Iuf6UFqwN7zqvuGYwsuC+n0UODwdi/48k1FWz/pOzqeaxo9O6AccKNEdDaXT4kwuJZsuvuKo0zv9IxDkVYoRB5Jx+vt58MtKGSxSpylWpJfw=="); - byte[] mldsa44_wrap_expanded_only = Base64.decode("MIIKGAIBADALBglghkgBZQMEAxEEggoEBIIKAMPxaH2z3muOQDwB3kKjXmS+CnLzB6/Ff4pzbFG5mJRNL02W3JEFYR6vLEIthd8guwdf2i1yLWtayJsj5s7GCE8zx+CM/t/8rB9J90w8fRxSjKeVsLjl+2UZzakv0WommJbUkdCwcMoubj4BqsNQm/5gLCZKCbhsd9UBn3NSkzEkGAIxIqlNECYxwJQMgbYkUsZBmSBRWBgGCCBF0kguGBNqwKKAAzkKCEJsGygEUkIlgURMWziJBEBFSSBEHBFAk8YRI0AmoaSRS8AxCISBWphEyagxGxFqysgsASlpwkBAnDYGA7QEDDFsDEExY8AoorBFE0BBohQKEUIm2kJpmqJIIxVGCURJjARwApUgAZckUBQJUBAgoEYkgiSBEBMKAAkkRJYNEBIwUhQICDkCyLgwokRx3BaCGadM5AgwmwSJGxWJ2BQEDENRCxkgmCAg0gRoYQZgHKEtCUNBwRBhlIiJUyJGGhhqSTCRy7QgwBAygaYF1AIKEsUAI0JtBLlEIwgQ3CJGysJFFKJtHJaJiyhxEzaQYqBAU6RAyxJKHCMyYkCNCQQCGjEB2kQkG5FtCkhx4ARgA6ItABRx2BQN4YINGslNYpaMiABJkASSyyguCYEAA4Yhm5iIlIIAXMAlgyIsAclgkhBMWkQtGZIACzNJE5WQiQJx1DRGmJSRBDFOibQFAKNIESdt5KJJEsaERLIwkqJgVMghRABKVCZpA5FlS5aEkkJKY5JtABEKmcRRCBOIIxghk6ZBIbBAFBMiGahAIyYNQUZRwZghCYOAGUhqGzROYDCFkUZkzLRgXIAlYaaNm8Ag4xRS2wJCZLBwmUQBCjFQY0ItY4ZEEKYNxIJsmwYww6gt4USATKBh1KYhyDIpUBIKUDAO0BApTAaJVDYoUwJg0ACAkxJuiKhAEwghDDgqBBGMCjeJmkQwI5EhCUZpyKIQC4AAAwQqAYYxYCYiGJFpk0Ih5CgOHCJmC5NwkgSClBAOIBNomzJEY5JFCgAsjEZGEaFkFJdMG5ltkAZJCUeAIEdogiQGFDIKSyhsIClR4gROIgUQCzWMGxcMFABqUMAMA0EsEsEAEAIu0RIK0iAJW5BxCcmQAElgzDJuGJUh4sBRSBZK2xSOiCSRESVIS5ZF0cggAJORg6AticiRmEIJEURpEyRNo0Qs1BhRIhdQwIIRAjSCSIBwIEFpb82Ye7yAj/dlgjF9wfM272+7YBidRREVNe0TAMYJ2r+IBFv6HvQxc68LxCheCrtubXNBUIHZp7y0BWMKU0Lg408bLoOBmVYg+iT9Br2aW730YUliI3hVXUHLKSdusEdyGjODPMkYYptcRH1H+x3KKUR0Om8ydAe64ROtANZqLsGBvHpM0sUOFj7annB3U+e72GhIzqozNIN71CmvMmw81FFSQ18f+qXMMOfeiyATR8oFWgRnUcgITWMc0oNGfQbsTdveSN1iS1Nd/smLHYLzKv6qI9C1WT3rwozAS5xpwQBrF8aWJk2W4shOr+b0Dt8VgekqOr1/hvNoJdE994GRMNAdLLUX++lwHBxzZEJeZ89WXZAktkBofDq+J5p7mrV2JH10j/GFgxc+24LSEjbWusO4opbmTskpA2/Lp7ll2tpVIkmxziSdbyd5MWv4zqULylC40OPVRSUKR/r1VWSkbV5L6ZBZzpiElUsdK37rPTSQj6UJvEuwyR+qUQNWYCmLC6HIRnLNKY1iRgGbVAvVdAc+4M+VxFic6Lg2WXuw3zmoHP07O4q0JqSzBbgXp6PZM6ZSpmEg1tTfPLjvQrAj+CTmPxCrrG6s0bv9N9mtCPZKM5/IMsN2gFOjAKwlW35q+Py/i1fjIS5Z8XtkR8vYi8NEplSNpyQzaC1onIWyyie3rmoBTI6ssOOVk5iGFyr1/K7JfUwEzsHLy/tKJM7fBrg8g5lno9VKBjzwu+HskX1VYnzwj329DP6PkFcLDTIIE9daYiXBGJj0t4SVpUyerd1gyKsKb4fQYdjgZjcidMTAt2cN6zVlQi2pc8X90XToSDYkkvdQ7Qz+RQ6p2fAa9ByuRgjdNDT31jbdBYzQFmLkaCAT6seO9YvvdZkADO3EcD/Is8Y+w/sHBkDMkLPxQNwC11Otud9q07f3hvdLz7U7Y+ib8XKsfpeaHJSSGf+qkI2qqcz+gyIZT2tHuhQYsEXn3Ws17tguYDyrcmPWLpF1n7RZ7t8Q6kb8XGHfywXitLrSWLREbOFNp2ktFv/AzEa007YjBOEGX/EEh50KBIzkvmTcW9clFHZh9BSqni4DzX131Zg9QemU6fwdNqN7kXe8vZOH+SCiD0HnVuIHVCH8nAT9tJWJleVNcXGFRuq/69S9ZH3gsJRLCf9zbGqVUMdiVbifhCgXarMd81Bx0i55N5EaGeKtBOSkT3HMYBBgqW9qyuFvocL7JHKn4SmUIWIo+AddMs3p6PBTSMU9r718gCwMYNreJ0glV+1nNY7/bnxFd89Xecc6OSA0R1ZDNak/les8fppMF15LGlS4jDZHqMt/xRe7icTXC2o0sFMMYbeSwMwC2ty6pTMpXiLytnxloqi2g5KdWmML83OIuurJuNsdixxOl8FVUf0Zx4/9XIPa23HVa9YbhlgPk5i3NLtNRGGa2Z3NHxovQZ5WL5tyXBGjSd39PXAf4je6YDA/4/JM4cpHdEbCYEubFNYIABZCuBPZLd1j6zasiD9Y3VGd9PkJ4+ii/23E3L2sWwumNRNLCEIE0mkAvZ98oSmsKIbRI0Qxp26qpbQsPasOjEY011hD3o7uWMEaTdA3lTQftwfP7C3ke2kdrb25O7ch7wfaVulkiJ35rBp0z6lQoVOxdJv6mHj+j8IxivE2OvT69M4gYdCTiymEVRtkcEJ2RrBqC6F0h8WQlfdEOZiEWoKRgwbJbyU3MVrZ1Fk/5LRWJIhVx71tPYQ/46o8wUV0ijs32Z6+V3q8tgSHJrJWK0mG29/HWP905hS9pOeke510trkBM4RSYU9TozqDmeocunxpTR4U14YitGZhegBm/zcNZ0tM1txxGtMEyeYJxVEK4JIKJ9zQbWoefnQbMTRp9NjtuLxeC4PFWtWY9OIoKbhPwnLW1JSeWCqqgI9ksC+YaQbTZPHldLKyBWhSJmjHIM0yV7eLc+YDiNw7N35C47dzQXS8DI8PVXoHSh26AgmIVT8xWNpRvvuS6ibnd+35aeyJJ4te6qXVGDu5Ib7DC3lK7uqLhanVen1XVcsJ9m1ttl7NAzTSH1xo534cIWeABLzqshV5mB2KkWFjrQSMVrtGlLb7Y3CIvIJAzbItJGNiLn+lBasDe86r7hmMLLgvp9FDg8HYv+PJNRVs/6Ts6nmsaPTugHHCjRHQ2l0+JMLiWbLr7iqNM7/SMQ5FWKEQeScfr7efDLShksUqcpVqSX8="); - byte[] mldsa44_seed_with_pub_key = Base64.decode("MIIFVwIBATALBglghkgBZQMEAxEEIAABAgMEBQYHCAkKCwwNDg8QAQIDBAUGBwgJCgsMDQ4PgYIFIQDD8Wh9s95rjkA8Ad5Co15kvgpy8wevxX+Kc2xRuZiUTcEHWDP0MI8enUbboCTOE020U+28L6HkQZZ4JzkhORY54IkHcSKSRcKkiNkLdbm9jWKOEiQAWKuYJyQ3dcbNWa4ogK+E4LsT2jHguWA/rmSJ42Azys2/d+kIMTmmKt3Y2PyiWU5zzj/2TNOQUHaHuJhAmXAT7iCrcEz+6vCsH/dKQrcVjMjIfbibRyRMLZIrLhqXqV4vboYE/yu9jzNRUGQH5rmenF+wDtnvu7YNO/2hWYRZJKrbTgy+nuBEBAkCKorHWVaGJvmXcEPSodB+5cRIQ5+uCCswH1lJT00+SzKsCacs68iBFcte5TkAOwTzq7slL1lAptILPkzwAhNhqKGCVz4B+qRiB6CK8917el2ioTKPxyHBtgHTKPIEoIdRAgi0+uaVBPAQ21VUlYDcILBd0mAH/7rxXsIdfq5PmklvIVD0VNxeDmwIbQH0J/LNr9kKCfmHSAXgZ3Vh/DQa2eVukKv7PZiBGSCyBHukEFK+791XNCydlSNsJkvz+xvp5cEkAseNxGJoUigCW4a5r2FV37JD9FQE6PDeCrKSL428p8vMl4Vpp6I+G90JuVv7+c0ShDh9n/PdnF/hC3v9uHso/COKsYvaKN4CIo9pwiyrm44XLuDSoX+F8DcmmaLYcEqW+o06/mVz5+dTSSrWZgiXqkIkPFI44AgwC/21SI9piGtdXouzcjxmIrgg1n1utPGESQXE2xxd3AkYJtVnPpdvMoEv/MSN2OMl5LGeO0DhPD4rdRYkT1HnT1/dbb5QoKpWHBiUgxTb2xJGzljMMaFooPi9mQiUas1fSjGk/7gNKgfi88nR8HiAPm5me3NGfxcS3plrI1UewwB9ubOOBfAQiRYQI2w5ax12ygyiPSS8ekYLv1HD2TfpfdVzwIQhWYnYWDls6WhW+sJ15Al5QJ3kGe2fIQflEH6196P5Xo/4Kmv1A02jzeN3KSPb/KutI8HM4TXWAgsQX+7XmH+zzS3lXqdjRhhQ0RQ9HK4HFZ2OFw9/2KhsbR0vMIcsc0VB0zsx096ZIgMynAbyQ5rI3mFeKM7n+CD4m5KXMYSupfesNv9e8Of/FROnwR9It9N2BzQVcLWwiMuwYiqgchfUfmZFWcD2if5e23xuSt/t8hYReU/I2SydkhzTUNfwbELIkjcsxwWVkpYFIf7rT2i5Atkl6auBag5Eg/fjEuiR7RsFS1C/AcHx5lxqqbckZ/eXinG7kPDw9ZSpjHki4/PeKTeviCs4aOGtLC9kexDAU++k2cFQe7fqbUHST+eENU2m9HQ1/WfUt1426wDUdYZTqInnvk/PjUbkvE0RNbFzS+0w9Qsnh28YfLXRt/j+IcZhG4buivL2G0N/UqHtwWpyh3Wigic5tqpMaTZ54RN1/jJWmsQS3rvd8rHaZPk2CbH0Iss1PrcROzksogqZBQn4Y1zwDsF6yqdscqaMJXmH5+7REdQACUTCzU2Qj4x59doVByXnKY+Of8fAB1XTjurYgq8DrbKnzfUThC3zEodT/HTcLdP7gEIhPizDQYxNA6LlGUxGzpy7+iMs3j38WiSbToASbB1dCsFtR7Jp0P8qc9LJmX36CW87JON1pEVaTCth/HQS8AXUsGU7HSorD/CkCwehxKlABcRbpPTIZC/dtR9LkrkI5T9AyTWMDS2pPzICo+PUhOOkU3pSKS6o20VnvZtj2uU8M31K6I16H5KbqXMr"); +// byte[] mldsa44_wrap_expanded_only = Base64.decode("MIIKGAIBADALBglghkgBZQMEAxEEggoEBIIKAMPxaH2z3muOQDwB3kKjXmS+CnLzB6/Ff4pzbFG5mJRNL02W3JEFYR6vLEIthd8guwdf2i1yLWtayJsj5s7GCE8zx+CM/t/8rB9J90w8fRxSjKeVsLjl+2UZzakv0WommJbUkdCwcMoubj4BqsNQm/5gLCZKCbhsd9UBn3NSkzEkGAIxIqlNECYxwJQMgbYkUsZBmSBRWBgGCCBF0kguGBNqwKKAAzkKCEJsGygEUkIlgURMWziJBEBFSSBEHBFAk8YRI0AmoaSRS8AxCISBWphEyagxGxFqysgsASlpwkBAnDYGA7QEDDFsDEExY8AoorBFE0BBohQKEUIm2kJpmqJIIxVGCURJjARwApUgAZckUBQJUBAgoEYkgiSBEBMKAAkkRJYNEBIwUhQICDkCyLgwokRx3BaCGadM5AgwmwSJGxWJ2BQEDENRCxkgmCAg0gRoYQZgHKEtCUNBwRBhlIiJUyJGGhhqSTCRy7QgwBAygaYF1AIKEsUAI0JtBLlEIwgQ3CJGysJFFKJtHJaJiyhxEzaQYqBAU6RAyxJKHCMyYkCNCQQCGjEB2kQkG5FtCkhx4ARgA6ItABRx2BQN4YINGslNYpaMiABJkASSyyguCYEAA4Yhm5iIlIIAXMAlgyIsAclgkhBMWkQtGZIACzNJE5WQiQJx1DRGmJSRBDFOibQFAKNIESdt5KJJEsaERLIwkqJgVMghRABKVCZpA5FlS5aEkkJKY5JtABEKmcRRCBOIIxghk6ZBIbBAFBMiGahAIyYNQUZRwZghCYOAGUhqGzROYDCFkUZkzLRgXIAlYaaNm8Ag4xRS2wJCZLBwmUQBCjFQY0ItY4ZEEKYNxIJsmwYww6gt4USATKBh1KYhyDIpUBIKUDAO0BApTAaJVDYoUwJg0ACAkxJuiKhAEwghDDgqBBGMCjeJmkQwI5EhCUZpyKIQC4AAAwQqAYYxYCYiGJFpk0Ih5CgOHCJmC5NwkgSClBAOIBNomzJEY5JFCgAsjEZGEaFkFJdMG5ltkAZJCUeAIEdogiQGFDIKSyhsIClR4gROIgUQCzWMGxcMFABqUMAMA0EsEsEAEAIu0RIK0iAJW5BxCcmQAElgzDJuGJUh4sBRSBZK2xSOiCSRESVIS5ZF0cggAJORg6AticiRmEIJEURpEyRNo0Qs1BhRIhdQwIIRAjSCSIBwIEFpb82Ye7yAj/dlgjF9wfM272+7YBidRREVNe0TAMYJ2r+IBFv6HvQxc68LxCheCrtubXNBUIHZp7y0BWMKU0Lg408bLoOBmVYg+iT9Br2aW730YUliI3hVXUHLKSdusEdyGjODPMkYYptcRH1H+x3KKUR0Om8ydAe64ROtANZqLsGBvHpM0sUOFj7annB3U+e72GhIzqozNIN71CmvMmw81FFSQ18f+qXMMOfeiyATR8oFWgRnUcgITWMc0oNGfQbsTdveSN1iS1Nd/smLHYLzKv6qI9C1WT3rwozAS5xpwQBrF8aWJk2W4shOr+b0Dt8VgekqOr1/hvNoJdE994GRMNAdLLUX++lwHBxzZEJeZ89WXZAktkBofDq+J5p7mrV2JH10j/GFgxc+24LSEjbWusO4opbmTskpA2/Lp7ll2tpVIkmxziSdbyd5MWv4zqULylC40OPVRSUKR/r1VWSkbV5L6ZBZzpiElUsdK37rPTSQj6UJvEuwyR+qUQNWYCmLC6HIRnLNKY1iRgGbVAvVdAc+4M+VxFic6Lg2WXuw3zmoHP07O4q0JqSzBbgXp6PZM6ZSpmEg1tTfPLjvQrAj+CTmPxCrrG6s0bv9N9mtCPZKM5/IMsN2gFOjAKwlW35q+Py/i1fjIS5Z8XtkR8vYi8NEplSNpyQzaC1onIWyyie3rmoBTI6ssOOVk5iGFyr1/K7JfUwEzsHLy/tKJM7fBrg8g5lno9VKBjzwu+HskX1VYnzwj329DP6PkFcLDTIIE9daYiXBGJj0t4SVpUyerd1gyKsKb4fQYdjgZjcidMTAt2cN6zVlQi2pc8X90XToSDYkkvdQ7Qz+RQ6p2fAa9ByuRgjdNDT31jbdBYzQFmLkaCAT6seO9YvvdZkADO3EcD/Is8Y+w/sHBkDMkLPxQNwC11Otud9q07f3hvdLz7U7Y+ib8XKsfpeaHJSSGf+qkI2qqcz+gyIZT2tHuhQYsEXn3Ws17tguYDyrcmPWLpF1n7RZ7t8Q6kb8XGHfywXitLrSWLREbOFNp2ktFv/AzEa007YjBOEGX/EEh50KBIzkvmTcW9clFHZh9BSqni4DzX131Zg9QemU6fwdNqN7kXe8vZOH+SCiD0HnVuIHVCH8nAT9tJWJleVNcXGFRuq/69S9ZH3gsJRLCf9zbGqVUMdiVbifhCgXarMd81Bx0i55N5EaGeKtBOSkT3HMYBBgqW9qyuFvocL7JHKn4SmUIWIo+AddMs3p6PBTSMU9r718gCwMYNreJ0glV+1nNY7/bnxFd89Xecc6OSA0R1ZDNak/les8fppMF15LGlS4jDZHqMt/xRe7icTXC2o0sFMMYbeSwMwC2ty6pTMpXiLytnxloqi2g5KdWmML83OIuurJuNsdixxOl8FVUf0Zx4/9XIPa23HVa9YbhlgPk5i3NLtNRGGa2Z3NHxovQZ5WL5tyXBGjSd39PXAf4je6YDA/4/JM4cpHdEbCYEubFNYIABZCuBPZLd1j6zasiD9Y3VGd9PkJ4+ii/23E3L2sWwumNRNLCEIE0mkAvZ98oSmsKIbRI0Qxp26qpbQsPasOjEY011hD3o7uWMEaTdA3lTQftwfP7C3ke2kdrb25O7ch7wfaVulkiJ35rBp0z6lQoVOxdJv6mHj+j8IxivE2OvT69M4gYdCTiymEVRtkcEJ2RrBqC6F0h8WQlfdEOZiEWoKRgwbJbyU3MVrZ1Fk/5LRWJIhVx71tPYQ/46o8wUV0ijs32Z6+V3q8tgSHJrJWK0mG29/HWP905hS9pOeke510trkBM4RSYU9TozqDmeocunxpTR4U14YitGZhegBm/zcNZ0tM1txxGtMEyeYJxVEK4JIKJ9zQbWoefnQbMTRp9NjtuLxeC4PFWtWY9OIoKbhPwnLW1JSeWCqqgI9ksC+YaQbTZPHldLKyBWhSJmjHIM0yV7eLc+YDiNw7N35C47dzQXS8DI8PVXoHSh26AgmIVT8xWNpRvvuS6ibnd+35aeyJJ4te6qXVGDu5Ib7DC3lK7uqLhanVen1XVcsJ9m1ttl7NAzTSH1xo534cIWeABLzqshV5mB2KkWFjrQSMVrtGlLb7Y3CIvIJAzbItJGNiLn+lBasDe86r7hmMLLgvp9FDg8HYv+PJNRVs/6Ts6nmsaPTugHHCjRHQ2l0+JMLiWbLr7iqNM7/SMQ5FWKEQeScfr7efDLShksUqcpVqSX8="); +// byte[] mldsa44_seed_with_pub_key = Base64.decode("MIIFVwIBATALBglghkgBZQMEAxEEIAABAgMEBQYHCAkKCwwNDg8QAQIDBAUGBwgJCgsMDQ4PgYIFIQDD8Wh9s95rjkA8Ad5Co15kvgpy8wevxX+Kc2xRuZiUTcEHWDP0MI8enUbboCTOE020U+28L6HkQZZ4JzkhORY54IkHcSKSRcKkiNkLdbm9jWKOEiQAWKuYJyQ3dcbNWa4ogK+E4LsT2jHguWA/rmSJ42Azys2/d+kIMTmmKt3Y2PyiWU5zzj/2TNOQUHaHuJhAmXAT7iCrcEz+6vCsH/dKQrcVjMjIfbibRyRMLZIrLhqXqV4vboYE/yu9jzNRUGQH5rmenF+wDtnvu7YNO/2hWYRZJKrbTgy+nuBEBAkCKorHWVaGJvmXcEPSodB+5cRIQ5+uCCswH1lJT00+SzKsCacs68iBFcte5TkAOwTzq7slL1lAptILPkzwAhNhqKGCVz4B+qRiB6CK8917el2ioTKPxyHBtgHTKPIEoIdRAgi0+uaVBPAQ21VUlYDcILBd0mAH/7rxXsIdfq5PmklvIVD0VNxeDmwIbQH0J/LNr9kKCfmHSAXgZ3Vh/DQa2eVukKv7PZiBGSCyBHukEFK+791XNCydlSNsJkvz+xvp5cEkAseNxGJoUigCW4a5r2FV37JD9FQE6PDeCrKSL428p8vMl4Vpp6I+G90JuVv7+c0ShDh9n/PdnF/hC3v9uHso/COKsYvaKN4CIo9pwiyrm44XLuDSoX+F8DcmmaLYcEqW+o06/mVz5+dTSSrWZgiXqkIkPFI44AgwC/21SI9piGtdXouzcjxmIrgg1n1utPGESQXE2xxd3AkYJtVnPpdvMoEv/MSN2OMl5LGeO0DhPD4rdRYkT1HnT1/dbb5QoKpWHBiUgxTb2xJGzljMMaFooPi9mQiUas1fSjGk/7gNKgfi88nR8HiAPm5me3NGfxcS3plrI1UewwB9ubOOBfAQiRYQI2w5ax12ygyiPSS8ekYLv1HD2TfpfdVzwIQhWYnYWDls6WhW+sJ15Al5QJ3kGe2fIQflEH6196P5Xo/4Kmv1A02jzeN3KSPb/KutI8HM4TXWAgsQX+7XmH+zzS3lXqdjRhhQ0RQ9HK4HFZ2OFw9/2KhsbR0vMIcsc0VB0zsx096ZIgMynAbyQ5rI3mFeKM7n+CD4m5KXMYSupfesNv9e8Of/FROnwR9It9N2BzQVcLWwiMuwYiqgchfUfmZFWcD2if5e23xuSt/t8hYReU/I2SydkhzTUNfwbELIkjcsxwWVkpYFIf7rT2i5Atkl6auBag5Eg/fjEuiR7RsFS1C/AcHx5lxqqbckZ/eXinG7kPDw9ZSpjHki4/PeKTeviCs4aOGtLC9kexDAU++k2cFQe7fqbUHST+eENU2m9HQ1/WfUt1426wDUdYZTqInnvk/PjUbkvE0RNbFzS+0w9Qsnh28YfLXRt/j+IcZhG4buivL2G0N/UqHtwWpyh3Wigic5tqpMaTZ54RN1/jJWmsQS3rvd8rHaZPk2CbH0Iss1PrcROzksogqZBQn4Y1zwDsF6yqdscqaMJXmH5+7REdQACUTCzU2Qj4x59doVByXnKY+Of8fAB1XTjurYgq8DrbKnzfUThC3zEodT/HTcLdP7gEIhPizDQYxNA6LlGUxGzpy7+iMs3j38WiSbToASbB1dCsFtR7Jp0P8qc9LJmX36CW87JON1pEVaTCth/HQS8AXUsGU7HSorD/CkCwehxKlABcRbpPTIZC/dtR9LkrkI5T9AyTWMDS2pPzICo+PUhOOkU3pSKS6o20VnvZtj2uU8M31K6I16H5KbqXMr"); KeyFactory kFact = KeyFactory.getInstance("ML-DSA", "BC"); - checkEncodeRecode(kFact, mldsa44_sequence); +// checkEncodeRecode(kFact, mldsa44_sequence); checkEncodeRecode(kFact, mldsa44_seed_only); checkEncodeRecode(kFact, mldsa44_wrap_seed_only); checkEncodeRecode(kFact, mldsa44_expanded_only); - checkEncodeRecode(kFact, mldsa44_wrap_expanded_only); - checkEncodeRecode(kFact, mldsa44_seed_with_pub_key); +// checkEncodeRecode(kFact, mldsa44_wrap_expanded_only); +// checkEncodeRecode(kFact, mldsa44_seed_with_pub_key); } private void checkEncodeRecode(KeyFactory kFact, byte[] encoding) diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java index ffd4b5f237..4fe14f5536 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLKEMTest.java @@ -188,21 +188,22 @@ public void testExpandedPrivateKeyEncoding() public void testPrivateKeyRecoding() throws Exception { - byte[] mlkem512_sequence = Base64.decode("MIIGvgIBADALBglghkgBZQMEBAEEggaqMIIGpgRAAAECAwQFBgcICQoLDA0ODxABAgMEBQYHCAkKCwwNDg8gAQIDBAUGBwgJCgsMDQ4PMAECAwQFBgcICQoLDA0OD4GCBmBYIXof3JWb6cceWnPV84vw6JvPYY65dINCHBetKcOtprZ9gHQvFXXZOynsWCAHXGa8iD4YFcmglwnoA8O4MKoT9G1bl5JnAosj9yp65s1puVLtIJBI5GtmI25WSgaA+KRP2HNQOG8OF0Tu24P5wXVR+kQIZk4UMV9op4W58mVf+RwgDAWOBc4+omsGJR3cFSxvYbzWTHIkd8Q4Nq5Xennx0BZF5bhZ/DObwI/45cwCxSJlOUcLMnFtpcU/AIFqiRz/xcF3oVQ4EscCPFtVEq+5NBLwkQksVMe+pi16GKTrqER01QkF9KlEYVqVEWO9enxpwVeaW5VNCw7p6hMvmk7SdnV+F61/REqDN4jTsbTEiF6urGWfV3CPtW0zpaxbpbFlwUeQFAxYWJFSK3EnVAhupj/U+jq4ESn9hg48Sh7uozDaHG9p00R/sqJE+rpH6jEMzCe2F7H3g7vxk2gMeIzk91ThYqPwSSvJKjj25L539hSfHEZltJJj67NNhjsP7DvNxqmvSyCaZQge1gRTpbGYIwUDw2YAHJLDTE6EloMGyTkRYrVpmqQz56puFh9HAikW+spqkCLzqLqMBp1zCbwBJGObJmZF8JiR68bySKxVCDjhMVs/pFihegUfNcjcGC4kKZsYFLtgVJ/AdwiI7Ju4lZAurEhlmQ93OUaacyvbm6KZsHHLMgHhnDsexl8YQZDfF0CwJGEWyRDDRcMchVZn0hk3+mh+pSU2Gqp4YILpFpGSAMx2ssTxsF4HxZS3RLJcqAJl5ckGyEprd2Fl46rXJY23mEH+tkoOCzOo+bC/6GJnFRGDpVQk8SiJdoCxscFkpZE7LCbtZ4ryCF2zJrOEYVskx0qEA5JgoGBYgEJxQEdkwsSG2beA0sd3MbHgO0Lh0aYkfExGEWgflxf8BGU75Fwq141lNzJP6GVIiDJT/AGDHMGEjCxWaQxiK48aeaoQAXT4sjv1WqtMMkWZ0FgYmx1KE5XwTLgka6WahE3ejI62+w8+knxB+XTGAkufo2zzspwj8xRpe2KUyl5jDFLUDAWe8lkbMMqHUbFO0oPzuJPMSsiCJXQAWiOwRnn0CS+MaYXDWM++FIhewX3TQQxzw7ahYCsochjNdYvYCZ8OaaaMXFNbghznHJB6OSJuKiVseEBvRFhh6wjVmZSdcKPKIFLP8KmAjIUiOpVcA7EabLlcl0yhsMluGHTGdXe7K00Ze5GJiqOdpBPNUSls1RhdsB1WgW4d/Awp/Lp4cgFegskWel0s4RidiKt4Ji/WasZiNawf0xjhizXu+8hwVKJr9Fz5W0f0ImTZuqARWorZOxXoqBERomAMGaPQFGnyi6s79gAUyyXEtwtAOqsq0DgmEZwDOGxiOnNCUFeWNKWqYUpaaMIgFEWsVowb7G/vpgFqqH7RoRAo5VPYnA3IAMbdKieGhQOi+j4n1SVoaWPCZ5oBAKi5BkMJcQuCt4qkc4VowrtGlAc2O2W/4W5X6Ttae505Gz01173YrDXdJFK6WZC1qjVIcl/RVFy+5C7d4zMsuyOW4SUqoSm8Nlf8KDvpeASm1hoSqUxA1Jrp+HfRtz4aGjIVEbHfOTfO9sau2sRaI8hpwIx5AoyaJ43xqmOtqACJmwZ2kwKx/GrXEAmtocHhwwZKLHdk6nyuIq6aI7gRDAOG0ka7g1dssLjn0mSpoQ/UdMD+M2nCIhEGwcTd2U5oUTuImT7OesxfNwb7V8cBeENq3A8SLMAu6nKFPECImwJbPFy0oVvOi7NvBy5cAqoluV011GzH4RJq6Cg35AouYQk4mWmNYoUg5ETh1pZemgLPhY/DUAMJOINDdbNM9Cfjq2WAMMXBPHrR4mT4ICfjxD55gzdbpIMK2ospiBSMuUlxqR0oyYolV0gvUDrqC0feMD+gm6uM0Y1ocr5k0oOb03EqtZD0+aYECIew8qoiiwtHixnqgA0Lia1UcreVmZjP0sj96Mh8uB3trDUg0G6k48+kaatCoDgtwaB3SYwJVEsrESo8FG5MNosEAjwd0lTCyDAjcasfVmQCw0cGGa9R1I2OJymU6hYdw1iL1TgVmQiJ8GMdSAZQ1SGbDgyR/ch95i0mgu2Olg4iNDfIUfzPN0QoFjvggsdP4FJaJqQDIAECAwQFBgcICQoLDA0ODzABAgMEBQYHCAkKCwwNDg8="); + // TODO: rebuild with newer encodings. +// byte[] mlkem512_sequence = Base64.decode("MIIGvgIBADALBglghkgBZQMEBAEEggaqMIIGpgRAAAECAwQFBgcICQoLDA0ODxABAgMEBQYHCAkKCwwNDg8gAQIDBAUGBwgJCgsMDQ4PMAECAwQFBgcICQoLDA0OD4GCBmBYIXof3JWb6cceWnPV84vw6JvPYY65dINCHBetKcOtprZ9gHQvFXXZOynsWCAHXGa8iD4YFcmglwnoA8O4MKoT9G1bl5JnAosj9yp65s1puVLtIJBI5GtmI25WSgaA+KRP2HNQOG8OF0Tu24P5wXVR+kQIZk4UMV9op4W58mVf+RwgDAWOBc4+omsGJR3cFSxvYbzWTHIkd8Q4Nq5Xennx0BZF5bhZ/DObwI/45cwCxSJlOUcLMnFtpcU/AIFqiRz/xcF3oVQ4EscCPFtVEq+5NBLwkQksVMe+pi16GKTrqER01QkF9KlEYVqVEWO9enxpwVeaW5VNCw7p6hMvmk7SdnV+F61/REqDN4jTsbTEiF6urGWfV3CPtW0zpaxbpbFlwUeQFAxYWJFSK3EnVAhupj/U+jq4ESn9hg48Sh7uozDaHG9p00R/sqJE+rpH6jEMzCe2F7H3g7vxk2gMeIzk91ThYqPwSSvJKjj25L539hSfHEZltJJj67NNhjsP7DvNxqmvSyCaZQge1gRTpbGYIwUDw2YAHJLDTE6EloMGyTkRYrVpmqQz56puFh9HAikW+spqkCLzqLqMBp1zCbwBJGObJmZF8JiR68bySKxVCDjhMVs/pFihegUfNcjcGC4kKZsYFLtgVJ/AdwiI7Ju4lZAurEhlmQ93OUaacyvbm6KZsHHLMgHhnDsexl8YQZDfF0CwJGEWyRDDRcMchVZn0hk3+mh+pSU2Gqp4YILpFpGSAMx2ssTxsF4HxZS3RLJcqAJl5ckGyEprd2Fl46rXJY23mEH+tkoOCzOo+bC/6GJnFRGDpVQk8SiJdoCxscFkpZE7LCbtZ4ryCF2zJrOEYVskx0qEA5JgoGBYgEJxQEdkwsSG2beA0sd3MbHgO0Lh0aYkfExGEWgflxf8BGU75Fwq141lNzJP6GVIiDJT/AGDHMGEjCxWaQxiK48aeaoQAXT4sjv1WqtMMkWZ0FgYmx1KE5XwTLgka6WahE3ejI62+w8+knxB+XTGAkufo2zzspwj8xRpe2KUyl5jDFLUDAWe8lkbMMqHUbFO0oPzuJPMSsiCJXQAWiOwRnn0CS+MaYXDWM++FIhewX3TQQxzw7ahYCsochjNdYvYCZ8OaaaMXFNbghznHJB6OSJuKiVseEBvRFhh6wjVmZSdcKPKIFLP8KmAjIUiOpVcA7EabLlcl0yhsMluGHTGdXe7K00Ze5GJiqOdpBPNUSls1RhdsB1WgW4d/Awp/Lp4cgFegskWel0s4RidiKt4Ji/WasZiNawf0xjhizXu+8hwVKJr9Fz5W0f0ImTZuqARWorZOxXoqBERomAMGaPQFGnyi6s79gAUyyXEtwtAOqsq0DgmEZwDOGxiOnNCUFeWNKWqYUpaaMIgFEWsVowb7G/vpgFqqH7RoRAo5VPYnA3IAMbdKieGhQOi+j4n1SVoaWPCZ5oBAKi5BkMJcQuCt4qkc4VowrtGlAc2O2W/4W5X6Ttae505Gz01173YrDXdJFK6WZC1qjVIcl/RVFy+5C7d4zMsuyOW4SUqoSm8Nlf8KDvpeASm1hoSqUxA1Jrp+HfRtz4aGjIVEbHfOTfO9sau2sRaI8hpwIx5AoyaJ43xqmOtqACJmwZ2kwKx/GrXEAmtocHhwwZKLHdk6nyuIq6aI7gRDAOG0ka7g1dssLjn0mSpoQ/UdMD+M2nCIhEGwcTd2U5oUTuImT7OesxfNwb7V8cBeENq3A8SLMAu6nKFPECImwJbPFy0oVvOi7NvBy5cAqoluV011GzH4RJq6Cg35AouYQk4mWmNYoUg5ETh1pZemgLPhY/DUAMJOINDdbNM9Cfjq2WAMMXBPHrR4mT4ICfjxD55gzdbpIMK2ospiBSMuUlxqR0oyYolV0gvUDrqC0feMD+gm6uM0Y1ocr5k0oOb03EqtZD0+aYECIew8qoiiwtHixnqgA0Lia1UcreVmZjP0sj96Mh8uB3trDUg0G6k48+kaatCoDgtwaB3SYwJVEsrESo8FG5MNosEAjwd0lTCyDAjcasfVmQCw0cGGa9R1I2OJymU6hYdw1iL1TgVmQiJ8GMdSAZQ1SGbDgyR/ch95i0mgu2Olg4iNDfIUfzPN0QoFjvggsdP4FJaJqQDIAECAwQFBgcICQoLDA0ODzABAgMEBQYHCAkKCwwNDg8="); byte[] mlkem512_seed_only = Base64.decode("MFICAQAwCwYJYIZIAWUDBAQBBEAAAQIDBAUGBwgJCgsMDQ4PEAECAwQFBgcICQoLDA0ODyABAgMEBQYHCAkKCwwNDg8wAQIDBAUGBwgJCgsMDQ4P"); byte[] mlkem512_wrap_seed_only = Base64.decode("MFQCAQAwCwYJYIZIAWUDBAQBBEIEQAABAgMEBQYHCAkKCwwNDg8QAQIDBAUGBwgJCgsMDQ4PIAECAwQFBgcICQoLDA0ODzABAgMEBQYHCAkKCwwNDg8="); - byte[] mlKem512_expanded_only = Base64.decode("MIIGdAIBADALBglghkgBZQMEBAEEggZgWCF6H9yVm+nHHlpz1fOL8Oibz2GOuXSDQhwXrSnDraa2fYB0LxV12Tsp7FggB1xmvIg+GBXJoJcJ6APDuDCqE/RtW5eSZwKLI/cqeubNablS7SCQSORrZiNuVkoGgPikT9hzUDhvDhdE7tuD+cF1UfpECGZOFDFfaKeFufJlX/kcIAwFjgXOPqJrBiUd3BUsb2G81kxyJHfEODauV3p58dAWReW4Wfwzm8CP+OXMAsUiZTlHCzJxbaXFPwCBaokc/8XBd6FUOBLHAjxbVRKvuTQS8JEJLFTHvqYtehik66hEdNUJBfSpRGFalRFjvXp8acFXmluVTQsO6eoTL5pO0nZ1fhetf0RKgzeI07G0xIherqxln1dwj7VtM6WsW6WxZcFHkBQMWFiRUitxJ1QIbqY/1Po6uBEp/YYOPEoe7qMw2hxvadNEf7KiRPq6R+oxDMwnthex94O78ZNoDHiM5PdU4WKj8EkrySo49uS+d/YUnxxGZbSSY+uzTYY7D+w7zcapr0sgmmUIHtYEU6WxmCMFA8NmABySw0xOhJaDBsk5EWK1aZqkM+eqbhYfRwIpFvrKapAi86i6jAadcwm8ASRjmyZmRfCYkevG8kisVQg44TFbP6RYoXoFHzXI3BguJCmbGBS7YFSfwHcIiOybuJWQLqxIZZkPdzlGmnMr25uimbBxyzIB4Zw7HsZfGEGQ3xdAsCRhFskQw0XDHIVWZ9IZN/pofqUlNhqqeGCC6RaRkgDMdrLE8bBeB8WUt0SyXKgCZeXJBshKa3dhZeOq1yWNt5hB/rZKDgszqPmwv+hiZxURg6VUJPEoiXaAsbHBZKWROywm7WeK8ghdsyazhGFbJMdKhAOSYKBgWIBCcUBHZMLEhtm3gNLHdzGx4DtC4dGmJHxMRhFoH5cX/ARlO+RcKteNZTcyT+hlSIgyU/wBgxzBhIwsVmkMYiuPGnmqEAF0+LI79VqrTDJFmdBYGJsdShOV8Ey4JGulmoRN3oyOtvsPPpJ8Qfl0xgJLn6Ns87KcI/MUaXtilMpeYwxS1AwFnvJZGzDKh1GxTtKD87iTzErIgiV0AFojsEZ59AkvjGmFw1jPvhSIXsF900EMc8O2oWArKHIYzXWL2AmfDmmmjFxTW4Ic5xyQejkibiolbHhAb0RYYesI1ZmUnXCjyiBSz/CpgIyFIjqVXAOxGmy5XJdMobDJbhh0xnV3uytNGXuRiYqjnaQTzVEpbNUYXbAdVoFuHfwMKfy6eHIBXoLJFnpdLOEYnYireCYv1mrGYjWsH9MY4Ys17vvIcFSia/Rc+VtH9CJk2bqgEVqK2TsV6KgREaJgDBmj0BRp8ourO/YAFMslxLcLQDqrKtA4JhGcAzhsYjpzQlBXljSlqmFKWmjCIBRFrFaMG+xv76YBaqh+0aEQKOVT2JwNyADG3SonhoUDovo+J9UlaGljwmeaAQCouQZDCXELgreKpHOFaMK7RpQHNjtlv+FuV+k7WnudORs9Nde92Kw13SRSulmQtao1SHJf0VRcvuQu3eMzLLsjluElKqEpvDZX/Cg76XgEptYaEqlMQNSa6fh30bc+GhoyFRGx3zk3zvbGrtrEWiPIacCMeQKMmieN8apjragAiZsGdpMCsfxq1xAJraHB4cMGSix3ZOp8riKumiO4EQwDhtJGu4NXbLC459JkqaEP1HTA/jNpwiIRBsHE3dlOaFE7iJk+znrMXzcG+1fHAXhDatwPEizALupyhTxAiJsCWzxctKFbzouzbwcuXAKqJbldNdRsx+ESaugoN+QKLmEJOJlpjWKFIORE4daWXpoCz4WPw1ADCTiDQ3WzTPQn46tlgDDFwTx60eJk+CAn48Q+eYM3W6SDCtqLKYgUjLlJcakdKMmKJVdIL1A66gtH3jA/oJurjNGNaHK+ZNKDm9NxKrWQ9PmmBAiHsPKqIosLR4sZ6oANC4mtVHK3lZmYz9LI/ejIfLgd7aw1INBupOPPpGmrQqA4LcGgd0mMCVRLKxEqPBRuTDaLBAI8HdJUwsgwI3GrH1ZkAsNHBhmvUdSNjicplOoWHcNYi9U4FZkIifBjHUgGUNUhmw4Mkf3IfeYtJoLtjpYOIjQ3yFH8zzdEKBY74ILHT+BSWiakAyABAgMEBQYHCAkKCwwNDg8wAQIDBAUGBwgJCgsMDQ4P"); - byte[] mlKem512_wrap_expanded_only = Base64.decode("MIIGeAIBADALBglghkgBZQMEBAEEggZkBIIGYFgheh/clZvpxx5ac9Xzi/Dom89hjrl0g0IcF60pw62mtn2AdC8Vddk7KexYIAdcZryIPhgVyaCXCegDw7gwqhP0bVuXkmcCiyP3KnrmzWm5Uu0gkEjka2YjblZKBoD4pE/Yc1A4bw4XRO7bg/nBdVH6RAhmThQxX2inhbnyZV/5HCAMBY4Fzj6iawYlHdwVLG9hvNZMciR3xDg2rld6efHQFkXluFn8M5vAj/jlzALFImU5RwsycW2lxT8AgWqJHP/FwXehVDgSxwI8W1USr7k0EvCRCSxUx76mLXoYpOuoRHTVCQX0qURhWpURY716fGnBV5pblU0LDunqEy+aTtJ2dX4XrX9ESoM3iNOxtMSIXq6sZZ9XcI+1bTOlrFulsWXBR5AUDFhYkVIrcSdUCG6mP9T6OrgRKf2GDjxKHu6jMNocb2nTRH+yokT6ukfqMQzMJ7YXsfeDu/GTaAx4jOT3VOFio/BJK8kqOPbkvnf2FJ8cRmW0kmPrs02GOw/sO83Gqa9LIJplCB7WBFOlsZgjBQPDZgAcksNMToSWgwbJORFitWmapDPnqm4WH0cCKRb6ymqQIvOouowGnXMJvAEkY5smZkXwmJHrxvJIrFUIOOExWz+kWKF6BR81yNwYLiQpmxgUu2BUn8B3CIjsm7iVkC6sSGWZD3c5RppzK9ubopmwccsyAeGcOx7GXxhBkN8XQLAkYRbJEMNFwxyFVmfSGTf6aH6lJTYaqnhggukWkZIAzHayxPGwXgfFlLdEslyoAmXlyQbISmt3YWXjqtcljbeYQf62Sg4LM6j5sL/oYmcVEYOlVCTxKIl2gLGxwWSlkTssJu1nivIIXbMms4RhWyTHSoQDkmCgYFiAQnFAR2TCxIbZt4DSx3cxseA7QuHRpiR8TEYRaB+XF/wEZTvkXCrXjWU3Mk/oZUiIMlP8AYMcwYSMLFZpDGIrjxp5qhABdPiyO/Vaq0wyRZnQWBibHUoTlfBMuCRrpZqETd6Mjrb7Dz6SfEH5dMYCS5+jbPOynCPzFGl7YpTKXmMMUtQMBZ7yWRswyodRsU7Sg/O4k8xKyIIldABaI7BGefQJL4xphcNYz74UiF7BfdNBDHPDtqFgKyhyGM11i9gJnw5ppoxcU1uCHOcckHo5Im4qJWx4QG9EWGHrCNWZlJ1wo8ogUs/wqYCMhSI6lVwDsRpsuVyXTKGwyW4YdMZ1d7srTRl7kYmKo52kE81RKWzVGF2wHVaBbh38DCn8unhyAV6CyRZ6XSzhGJ2Iq3gmL9ZqxmI1rB/TGOGLNe77yHBUomv0XPlbR/QiZNm6oBFaitk7FeioERGiYAwZo9AUafKLqzv2ABTLJcS3C0A6qyrQOCYRnAM4bGI6c0JQV5Y0paphSlpowiAURaxWjBvsb++mAWqoftGhECjlU9icDcgAxt0qJ4aFA6L6PifVJWhpY8JnmgEAqLkGQwlxC4K3iqRzhWjCu0aUBzY7Zb/hblfpO1p7nTkbPTXXvdisNd0kUrpZkLWqNUhyX9FUXL7kLt3jMyy7I5bhJSqhKbw2V/woO+l4BKbWGhKpTEDUmun4d9G3PhoaMhURsd85N872xq7axFojyGnAjHkCjJonjfGqY62oAImbBnaTArH8atcQCa2hweHDBkosd2TqfK4irpojuBEMA4bSRruDV2ywuOfSZKmhD9R0wP4zacIiEQbBxN3ZTmhRO4iZPs56zF83BvtXxwF4Q2rcDxIswC7qcoU8QIibAls8XLShW86Ls28HLlwCqiW5XTXUbMfhEmroKDfkCi5hCTiZaY1ihSDkROHWll6aAs+Fj8NQAwk4g0N1s0z0J+OrZYAwxcE8etHiZPggJ+PEPnmDN1ukgwraiymIFIy5SXGpHSjJiiVXSC9QOuoLR94wP6Cbq4zRjWhyvmTSg5vTcSq1kPT5pgQIh7DyqiKLC0eLGeqADQuJrVRyt5WZmM/SyP3oyHy4He2sNSDQbqTjz6Rpq0KgOC3BoHdJjAlUSysRKjwUbkw2iwQCPB3SVMLIMCNxqx9WZALDRwYZr1HUjY4nKZTqFh3DWIvVOBWZCInwYx1IBlDVIZsODJH9yH3mLSaC7Y6WDiI0N8hR/M83RCgWO+CCx0/gUlompAMgAQIDBAUGBwgJCgsMDQ4PMAECAwQFBgcICQoLDA0ODw=="); - byte[] mlkem512_seed_with_pub_key = Base64.decode("MIIDdwIBATALBglghkgBZQMEBAEEQAABAgMEBQYHCAkKCwwNDg8QAQIDBAUGBwgJCgsMDQ4PIAECAwQFBgcICQoLDA0ODzABAgMEBQYHCAkKCwwNDg+BggMhAPOynCPzFGl7YpTKXmMMUtQMBZ7yWRswyodRsU7Sg/O4k8xKyIIldABaI7BGefQJL4xphcNYz74UiF7BfdNBDHPDtqFgKyhyGM11i9gJnw5ppoxcU1uCHOcckHo5Im4qJWx4QG9EWGHrCNWZlJ1wo8ogUs/wqYCMhSI6lVwDsRpsuVyXTKGwyW4YdMZ1d7srTRl7kYmKo52kE81RKWzVGF2wHVaBbh38DCn8unhyAV6CyRZ6XSzhGJ2Iq3gmL9ZqxmI1rB/TGOGLNe77yHBUomv0XPlbR/QiZNm6oBFaitk7FeioERGiYAwZo9AUafKLqzv2ABTLJcS3C0A6qyrQOCYRnAM4bGI6c0JQV5Y0paphSlpowiAURaxWjBvsb++mAWqoftGhECjlU9icDcgAxt0qJ4aFA6L6PifVJWhpY8JnmgEAqLkGQwlxC4K3iqRzhWjCu0aUBzY7Zb/hblfpO1p7nTkbPTXXvdisNd0kUrpZkLWqNUhyX9FUXL7kLt3jMyy7I5bhJSqhKbw2V/woO+l4BKbWGhKpTEDUmun4d9G3PhoaMhURsd85N872xq7axFojyGnAjHkCjJonjfGqY62oAImbBnaTArH8atcQCa2hweHDBkosd2TqfK4irpojuBEMA4bSRruDV2ywuOfSZKmhD9R0wP4zacIiEQbBxN3ZTmhRO4iZPs56zF83BvtXxwF4Q2rcDxIswC7qcoU8QIibAls8XLShW86Ls28HLlwCqiW5XTXUbMfhEmroKDfkCi5hCTiZaY1ihSDkROHWll6aAs+Fj8NQAwk4g0N1s0z0J+OrZYAwxcE8etHiZPggJ+PEPnmDN1ukgwraiymIFIy5SXGpHSjJiiVXSC9QOuoLR94wP6Cbq4zRjWhyvmTSg5vTcSq1kPT5pgQIh7DyqiKLC0eLGeqADQuJrVRyt5WZmM/SyP3oyHy4He2sNSDQbqTjz6Rpq0KgOC3BoHdJjAlUSysRKjwUbkw2iwQCPB3SVMLIMCNxqx9WZALDRwYZr1HUjY4nKZTqFh3DWIvVOBWZCInwYx1IBlDVIZsODJH9"); +// byte[] mlKem512_expanded_only = Base64.decode("MIIGdAIBADALBglghkgBZQMEBAEEggZgWCF6H9yVm+nHHlpz1fOL8Oibz2GOuXSDQhwXrSnDraa2fYB0LxV12Tsp7FggB1xmvIg+GBXJoJcJ6APDuDCqE/RtW5eSZwKLI/cqeubNablS7SCQSORrZiNuVkoGgPikT9hzUDhvDhdE7tuD+cF1UfpECGZOFDFfaKeFufJlX/kcIAwFjgXOPqJrBiUd3BUsb2G81kxyJHfEODauV3p58dAWReW4Wfwzm8CP+OXMAsUiZTlHCzJxbaXFPwCBaokc/8XBd6FUOBLHAjxbVRKvuTQS8JEJLFTHvqYtehik66hEdNUJBfSpRGFalRFjvXp8acFXmluVTQsO6eoTL5pO0nZ1fhetf0RKgzeI07G0xIherqxln1dwj7VtM6WsW6WxZcFHkBQMWFiRUitxJ1QIbqY/1Po6uBEp/YYOPEoe7qMw2hxvadNEf7KiRPq6R+oxDMwnthex94O78ZNoDHiM5PdU4WKj8EkrySo49uS+d/YUnxxGZbSSY+uzTYY7D+w7zcapr0sgmmUIHtYEU6WxmCMFA8NmABySw0xOhJaDBsk5EWK1aZqkM+eqbhYfRwIpFvrKapAi86i6jAadcwm8ASRjmyZmRfCYkevG8kisVQg44TFbP6RYoXoFHzXI3BguJCmbGBS7YFSfwHcIiOybuJWQLqxIZZkPdzlGmnMr25uimbBxyzIB4Zw7HsZfGEGQ3xdAsCRhFskQw0XDHIVWZ9IZN/pofqUlNhqqeGCC6RaRkgDMdrLE8bBeB8WUt0SyXKgCZeXJBshKa3dhZeOq1yWNt5hB/rZKDgszqPmwv+hiZxURg6VUJPEoiXaAsbHBZKWROywm7WeK8ghdsyazhGFbJMdKhAOSYKBgWIBCcUBHZMLEhtm3gNLHdzGx4DtC4dGmJHxMRhFoH5cX/ARlO+RcKteNZTcyT+hlSIgyU/wBgxzBhIwsVmkMYiuPGnmqEAF0+LI79VqrTDJFmdBYGJsdShOV8Ey4JGulmoRN3oyOtvsPPpJ8Qfl0xgJLn6Ns87KcI/MUaXtilMpeYwxS1AwFnvJZGzDKh1GxTtKD87iTzErIgiV0AFojsEZ59AkvjGmFw1jPvhSIXsF900EMc8O2oWArKHIYzXWL2AmfDmmmjFxTW4Ic5xyQejkibiolbHhAb0RYYesI1ZmUnXCjyiBSz/CpgIyFIjqVXAOxGmy5XJdMobDJbhh0xnV3uytNGXuRiYqjnaQTzVEpbNUYXbAdVoFuHfwMKfy6eHIBXoLJFnpdLOEYnYireCYv1mrGYjWsH9MY4Ys17vvIcFSia/Rc+VtH9CJk2bqgEVqK2TsV6KgREaJgDBmj0BRp8ourO/YAFMslxLcLQDqrKtA4JhGcAzhsYjpzQlBXljSlqmFKWmjCIBRFrFaMG+xv76YBaqh+0aEQKOVT2JwNyADG3SonhoUDovo+J9UlaGljwmeaAQCouQZDCXELgreKpHOFaMK7RpQHNjtlv+FuV+k7WnudORs9Nde92Kw13SRSulmQtao1SHJf0VRcvuQu3eMzLLsjluElKqEpvDZX/Cg76XgEptYaEqlMQNSa6fh30bc+GhoyFRGx3zk3zvbGrtrEWiPIacCMeQKMmieN8apjragAiZsGdpMCsfxq1xAJraHB4cMGSix3ZOp8riKumiO4EQwDhtJGu4NXbLC459JkqaEP1HTA/jNpwiIRBsHE3dlOaFE7iJk+znrMXzcG+1fHAXhDatwPEizALupyhTxAiJsCWzxctKFbzouzbwcuXAKqJbldNdRsx+ESaugoN+QKLmEJOJlpjWKFIORE4daWXpoCz4WPw1ADCTiDQ3WzTPQn46tlgDDFwTx60eJk+CAn48Q+eYM3W6SDCtqLKYgUjLlJcakdKMmKJVdIL1A66gtH3jA/oJurjNGNaHK+ZNKDm9NxKrWQ9PmmBAiHsPKqIosLR4sZ6oANC4mtVHK3lZmYz9LI/ejIfLgd7aw1INBupOPPpGmrQqA4LcGgd0mMCVRLKxEqPBRuTDaLBAI8HdJUwsgwI3GrH1ZkAsNHBhmvUdSNjicplOoWHcNYi9U4FZkIifBjHUgGUNUhmw4Mkf3IfeYtJoLtjpYOIjQ3yFH8zzdEKBY74ILHT+BSWiakAyABAgMEBQYHCAkKCwwNDg8wAQIDBAUGBwgJCgsMDQ4P"); +// byte[] mlKem512_wrap_expanded_only = Base64.decode("MIIGeAIBADALBglghkgBZQMEBAEEggZkBIIGYFgheh/clZvpxx5ac9Xzi/Dom89hjrl0g0IcF60pw62mtn2AdC8Vddk7KexYIAdcZryIPhgVyaCXCegDw7gwqhP0bVuXkmcCiyP3KnrmzWm5Uu0gkEjka2YjblZKBoD4pE/Yc1A4bw4XRO7bg/nBdVH6RAhmThQxX2inhbnyZV/5HCAMBY4Fzj6iawYlHdwVLG9hvNZMciR3xDg2rld6efHQFkXluFn8M5vAj/jlzALFImU5RwsycW2lxT8AgWqJHP/FwXehVDgSxwI8W1USr7k0EvCRCSxUx76mLXoYpOuoRHTVCQX0qURhWpURY716fGnBV5pblU0LDunqEy+aTtJ2dX4XrX9ESoM3iNOxtMSIXq6sZZ9XcI+1bTOlrFulsWXBR5AUDFhYkVIrcSdUCG6mP9T6OrgRKf2GDjxKHu6jMNocb2nTRH+yokT6ukfqMQzMJ7YXsfeDu/GTaAx4jOT3VOFio/BJK8kqOPbkvnf2FJ8cRmW0kmPrs02GOw/sO83Gqa9LIJplCB7WBFOlsZgjBQPDZgAcksNMToSWgwbJORFitWmapDPnqm4WH0cCKRb6ymqQIvOouowGnXMJvAEkY5smZkXwmJHrxvJIrFUIOOExWz+kWKF6BR81yNwYLiQpmxgUu2BUn8B3CIjsm7iVkC6sSGWZD3c5RppzK9ubopmwccsyAeGcOx7GXxhBkN8XQLAkYRbJEMNFwxyFVmfSGTf6aH6lJTYaqnhggukWkZIAzHayxPGwXgfFlLdEslyoAmXlyQbISmt3YWXjqtcljbeYQf62Sg4LM6j5sL/oYmcVEYOlVCTxKIl2gLGxwWSlkTssJu1nivIIXbMms4RhWyTHSoQDkmCgYFiAQnFAR2TCxIbZt4DSx3cxseA7QuHRpiR8TEYRaB+XF/wEZTvkXCrXjWU3Mk/oZUiIMlP8AYMcwYSMLFZpDGIrjxp5qhABdPiyO/Vaq0wyRZnQWBibHUoTlfBMuCRrpZqETd6Mjrb7Dz6SfEH5dMYCS5+jbPOynCPzFGl7YpTKXmMMUtQMBZ7yWRswyodRsU7Sg/O4k8xKyIIldABaI7BGefQJL4xphcNYz74UiF7BfdNBDHPDtqFgKyhyGM11i9gJnw5ppoxcU1uCHOcckHo5Im4qJWx4QG9EWGHrCNWZlJ1wo8ogUs/wqYCMhSI6lVwDsRpsuVyXTKGwyW4YdMZ1d7srTRl7kYmKo52kE81RKWzVGF2wHVaBbh38DCn8unhyAV6CyRZ6XSzhGJ2Iq3gmL9ZqxmI1rB/TGOGLNe77yHBUomv0XPlbR/QiZNm6oBFaitk7FeioERGiYAwZo9AUafKLqzv2ABTLJcS3C0A6qyrQOCYRnAM4bGI6c0JQV5Y0paphSlpowiAURaxWjBvsb++mAWqoftGhECjlU9icDcgAxt0qJ4aFA6L6PifVJWhpY8JnmgEAqLkGQwlxC4K3iqRzhWjCu0aUBzY7Zb/hblfpO1p7nTkbPTXXvdisNd0kUrpZkLWqNUhyX9FUXL7kLt3jMyy7I5bhJSqhKbw2V/woO+l4BKbWGhKpTEDUmun4d9G3PhoaMhURsd85N872xq7axFojyGnAjHkCjJonjfGqY62oAImbBnaTArH8atcQCa2hweHDBkosd2TqfK4irpojuBEMA4bSRruDV2ywuOfSZKmhD9R0wP4zacIiEQbBxN3ZTmhRO4iZPs56zF83BvtXxwF4Q2rcDxIswC7qcoU8QIibAls8XLShW86Ls28HLlwCqiW5XTXUbMfhEmroKDfkCi5hCTiZaY1ihSDkROHWll6aAs+Fj8NQAwk4g0N1s0z0J+OrZYAwxcE8etHiZPggJ+PEPnmDN1ukgwraiymIFIy5SXGpHSjJiiVXSC9QOuoLR94wP6Cbq4zRjWhyvmTSg5vTcSq1kPT5pgQIh7DyqiKLC0eLGeqADQuJrVRyt5WZmM/SyP3oyHy4He2sNSDQbqTjz6Rpq0KgOC3BoHdJjAlUSysRKjwUbkw2iwQCPB3SVMLIMCNxqx9WZALDRwYZr1HUjY4nKZTqFh3DWIvVOBWZCInwYx1IBlDVIZsODJH9yH3mLSaC7Y6WDiI0N8hR/M83RCgWO+CCx0/gUlompAMgAQIDBAUGBwgJCgsMDQ4PMAECAwQFBgcICQoLDA0ODw=="); +// byte[] mlkem512_seed_with_pub_key = Base64.decode("MIIDdwIBATALBglghkgBZQMEBAEEQAABAgMEBQYHCAkKCwwNDg8QAQIDBAUGBwgJCgsMDQ4PIAECAwQFBgcICQoLDA0ODzABAgMEBQYHCAkKCwwNDg+BggMhAPOynCPzFGl7YpTKXmMMUtQMBZ7yWRswyodRsU7Sg/O4k8xKyIIldABaI7BGefQJL4xphcNYz74UiF7BfdNBDHPDtqFgKyhyGM11i9gJnw5ppoxcU1uCHOcckHo5Im4qJWx4QG9EWGHrCNWZlJ1wo8ogUs/wqYCMhSI6lVwDsRpsuVyXTKGwyW4YdMZ1d7srTRl7kYmKo52kE81RKWzVGF2wHVaBbh38DCn8unhyAV6CyRZ6XSzhGJ2Iq3gmL9ZqxmI1rB/TGOGLNe77yHBUomv0XPlbR/QiZNm6oBFaitk7FeioERGiYAwZo9AUafKLqzv2ABTLJcS3C0A6qyrQOCYRnAM4bGI6c0JQV5Y0paphSlpowiAURaxWjBvsb++mAWqoftGhECjlU9icDcgAxt0qJ4aFA6L6PifVJWhpY8JnmgEAqLkGQwlxC4K3iqRzhWjCu0aUBzY7Zb/hblfpO1p7nTkbPTXXvdisNd0kUrpZkLWqNUhyX9FUXL7kLt3jMyy7I5bhJSqhKbw2V/woO+l4BKbWGhKpTEDUmun4d9G3PhoaMhURsd85N872xq7axFojyGnAjHkCjJonjfGqY62oAImbBnaTArH8atcQCa2hweHDBkosd2TqfK4irpojuBEMA4bSRruDV2ywuOfSZKmhD9R0wP4zacIiEQbBxN3ZTmhRO4iZPs56zF83BvtXxwF4Q2rcDxIswC7qcoU8QIibAls8XLShW86Ls28HLlwCqiW5XTXUbMfhEmroKDfkCi5hCTiZaY1ihSDkROHWll6aAs+Fj8NQAwk4g0N1s0z0J+OrZYAwxcE8etHiZPggJ+PEPnmDN1ukgwraiymIFIy5SXGpHSjJiiVXSC9QOuoLR94wP6Cbq4zRjWhyvmTSg5vTcSq1kPT5pgQIh7DyqiKLC0eLGeqADQuJrVRyt5WZmM/SyP3oyHy4He2sNSDQbqTjz6Rpq0KgOC3BoHdJjAlUSysRKjwUbkw2iwQCPB3SVMLIMCNxqx9WZALDRwYZr1HUjY4nKZTqFh3DWIvVOBWZCInwYx1IBlDVIZsODJH9"); KeyFactory kFact = KeyFactory.getInstance("ML-KEM", "BC"); - checkEncodeRecode(kFact, mlkem512_sequence); +// checkEncodeRecode(kFact, mlkem512_sequence); checkEncodeRecode(kFact, mlkem512_seed_only); checkEncodeRecode(kFact, mlkem512_wrap_seed_only); - checkEncodeRecode(kFact, mlKem512_expanded_only); - checkEncodeRecode(kFact, mlKem512_wrap_expanded_only); - checkEncodeRecode(kFact, mlkem512_seed_with_pub_key); +// checkEncodeRecode(kFact, mlKem512_expanded_only); +// checkEncodeRecode(kFact, mlKem512_wrap_expanded_only); +// checkEncodeRecode(kFact, mlkem512_seed_with_pub_key); } private void checkEncodeRecode(KeyFactory kFact, byte[] encoding) From 6ce85f4f1c8c76e5750fcf18340cb6334c213eca Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 25 Feb 2025 16:36:56 +1030 Subject: [PATCH 129/890] Add HOWTO.md --- HOWTO.md | 129 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 HOWTO.md diff --git a/HOWTO.md b/HOWTO.md new file mode 100644 index 0000000000..c728d84096 --- /dev/null +++ b/HOWTO.md @@ -0,0 +1,129 @@ +# Bouncy Castle Java API How To +## Using Bouncy Castle with GraalVM Native Image +### Problem: Provider Not Registered at Build Time with `UnsupportedFeatureError` Exception +#### Error message +```text +Trying to verify a provider that was not registered at build time: BC version... +``` +#### Cause: +Bouncy Castle security provider isn't properly registered during GraalVM native image build process. + +### Solution 1: Static Initializer Approach (No GraalVM SDK) +#### Step 1. Create Initializer Class +```java +package com.yourpackage.crypto; // ← Replace with your actual package + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import java.security.Security; + +public class BCInitializer { + static { + // Force provider registration during image build + Security.addProvider(new BouncyCastleProvider()); + } +} +``` + +#### Step 2. And then in the native-image build configuration +For Maven (`pom.xml`) +```xml + + org.graalvm.buildtools + native-maven-plugin + 0.9.28 + + + + --initialize-at-build-time=org.bouncycastle,com.yourpackage.crypto.BCInitializer + + --initialize-at-run-time=org.bouncycastle.jcajce.provider.drbg.DRBG$Default,org.bouncycastle.jcajce.provider.drbg.DRBG$NonceAndIV + + + +``` + +For Gradle (`build.gradle`), +```gradle + buildArgs.add('--initialize-at-build-time=com.yourpackage.crypto.BCInitializer') + buildArgs.add("--initialize-at-run-time=org.bouncycastle.jcajce.provider.drbg.DRBG\$Default,org.bouncycastle.jcajce.provider.drbg.DRBG\$NonceAndIV") +``` +# Key Configuration + +| Argument | Purpose | +| ------------------------------- |-----------------------------------------------------------------| +| `--initialize-at-build-time` | Forces inclusion of BC classes and triggers static initializer. | +| `--initialize-at-run-time` | Solves stateful SecureRandom initialization issues. | +|`--enable-all-security-services` | (optional) Enables JCE security infrastructure | + + +### Solution 2: GraalVM Feature Approach (With SDK) + +#### Step 1: Create a Native Image Feature +```java +package com.yourpackage.crypto; // ← Replace with your actual package + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.graalvm.nativeimage.hosted.Feature; + +import java.security.Security; + +/** + * A GraalVM Feature that registers the Bouncy Castle provider. + * This is required so that native image builds verify and include the provider. + */ +public class BouncyCastleFeature implements Feature { + + @Override + public void afterRegistration(AfterRegistrationAccess access) { + // Register the Bouncy Castle provider + Security.addProvider(new BouncyCastleProvider()); + } +} +``` + +#### Step 2: Configure Dependencies and Build +##### 2.1 add dependency +```xml + + org.graalvm.sdk + graal-sdk + 21.0.0 + provided + +``` +##### 2.2 add plugin +```xml + + org.graalvm.buildtools + native-maven-plugin + 0.9.28 + + + --features=com.yourpackage.crypto.BouncyCastleFeature + --initialize-at-build-time=org.bouncycastle + --initialize-at-run-time=org.bouncycastle.jcajce.provider.drbg.DRBG$Default,org.bouncycastle.jcajce.provider.drbg.DRBG$NonceAndIV + + + +``` +Key Configuration Explanations: +`--features=...` +- Registers custom feature class that adds BouncyCastle provider at build time +- Required for JCE security provider verification + +### Troubleshooting +#### Common Issues +##### Classpath Conflicts: + +```text +Error: Class-path entry contains class from image builder +``` +Fix: Add `-H:+AllowDeprecatedBuilderClassesOnImageClasspath` (temporary) or ensure graal-sdk has provided scope + +##### Missing Algorithms: +Example of the error message: +```text +No such algorithm: AES/CBC/PKCS5Padding +``` + +Fix: Verify `--initialize-at-build-time` includes `org.bouncycastle` \ No newline at end of file From 5af657e5ce9bdcaca0610a9fe5833f637402e1b1 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 25 Feb 2025 17:51:47 +1100 Subject: [PATCH 130/890] added support for id-alg-noSignature --- .../asn1/x509/X509ObjectIdentifiers.java | 5 ++ .../operator/NoSignatureContentSigner.java | 48 ++++++++++ .../org/bouncycastle/cert/test/CertTest.java | 57 ++++++++++++ .../jcajce/provider/asymmetric/NoSig.java | 90 +++++++++++++++++++ .../jce/provider/BouncyCastleProvider.java | 2 +- 5 files changed, 201 insertions(+), 1 deletion(-) create mode 100644 pkix/src/main/java/org/bouncycastle/operator/NoSignatureContentSigner.java create mode 100644 prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/NoSig.java diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java index a6bc6e531c..5f719a35fe 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java @@ -95,6 +95,11 @@ public interface X509ObjectIdentifiers */ static final ASN1ObjectIdentifier id_ecdsa_with_shake256 = pkix_algorithms.branch("33"); + /** + * id-alg-noSignature OBJECT IDENTIFIER ::= {id-pkix id-alg(6) 2} + */ + ASN1ObjectIdentifier id_alg_noSignature = pkix_algorithms.branch("2"); + /** 1.3.6.1.5.5.7.9 */ static final ASN1ObjectIdentifier id_pda = id_pkix.branch("9"); diff --git a/pkix/src/main/java/org/bouncycastle/operator/NoSignatureContentSigner.java b/pkix/src/main/java/org/bouncycastle/operator/NoSignatureContentSigner.java new file mode 100644 index 0000000000..95916074f5 --- /dev/null +++ b/pkix/src/main/java/org/bouncycastle/operator/NoSignatureContentSigner.java @@ -0,0 +1,48 @@ +package org.bouncycastle.operator; + +import java.io.IOException; +import java.io.OutputStream; + +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; + +/** + * ContentSigner for "Unsigned X.509 Certificates" + */ +public class NoSignatureContentSigner + implements ContentSigner +{ + @Override + public AlgorithmIdentifier getAlgorithmIdentifier() + { + return new AlgorithmIdentifier(X509ObjectIdentifiers.id_alg_noSignature, DERNull.INSTANCE); + } + + @Override + public OutputStream getOutputStream() + { + return new OutputStream() + { + @Override + public void write(byte[] buf, int off, int len) + throws IOException + { + // do nothing + } + + @Override + public void write(int i) + throws IOException + { + // do nothing + } + }; + } + + @Override + public byte[] getSignature() + { + return new byte[0]; + } +} diff --git a/pkix/src/test/java/org/bouncycastle/cert/test/CertTest.java b/pkix/src/test/java/org/bouncycastle/cert/test/CertTest.java index ef23bd1ce5..9c2993c7e3 100644 --- a/pkix/src/test/java/org/bouncycastle/cert/test/CertTest.java +++ b/pkix/src/test/java/org/bouncycastle/cert/test/CertTest.java @@ -77,6 +77,7 @@ import org.bouncycastle.asn1.x509.KeyPurposeId; import org.bouncycastle.asn1.x509.SubjectAltPublicKeyInfo; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; import org.bouncycastle.asn1.x9.ECNamedCurveTable; import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; @@ -118,6 +119,7 @@ import org.bouncycastle.operator.ContentVerifierProvider; import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; +import org.bouncycastle.operator.NoSignatureContentSigner; import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder; import org.bouncycastle.operator.bc.BcRSAContentVerifierProviderBuilder; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; @@ -3973,6 +3975,60 @@ public void checkCreationRSAPSS() isTrue(null == crt.getSubjectPublicKeyInfo().getAlgorithm().getParameters()); } + public void checkCreationNoSignature() + throws Exception + { + // + // set up the keys + // + KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSASSA-PSS", BC); + + KeyPair kp = kpg.generateKeyPair(); + + PrivateKey privKey = kp.getPrivate(); + PublicKey pubKey = kp.getPublic(); + + // + // distinguished name table. + // + X500NameBuilder builder = createStdBuilder(); + + // + // create the certificate - version 3 + // + ContentSigner sigGen = new NoSignatureContentSigner(); + X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey); + + X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen)); + + cert.checkValidity(new Date()); + + // + // check fails on verify + // + try + { + cert.verify(pubKey); + fail("no exception"); + } + catch (InvalidKeyException e) + { + isEquals(e.getMessage(), "attempt to pass public key to NoSig"); + } + + // convert and check components. + ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded()); + CertificateFactory fact = CertificateFactory.getInstance("X.509", BC); + + cert = (X509Certificate)fact.generateCertificate(bIn); + + org.bouncycastle.asn1.x509.Certificate crt = org.bouncycastle.asn1.x509.Certificate.getInstance(cert.getEncoded()); + + isTrue(new AlgorithmIdentifier(X509ObjectIdentifiers.id_alg_noSignature, DERNull.INSTANCE).equals(crt.getTBSCertificate().getSignature())); + isTrue(new AlgorithmIdentifier(X509ObjectIdentifiers.id_alg_noSignature, DERNull.INSTANCE).equals(crt.getSignatureAlgorithm())); + isTrue(0 == cert.getSignature().length); + } + /* * we generate a self signed certificate across the range of ECDSA algorithms */ @@ -5644,6 +5700,7 @@ public void performTest() checkCreationECDSA(); checkCreationRSA(); checkCreationRSAPSS(); + checkCreationNoSignature(); checkCreationFalcon(); checkCreationDilithium(); diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/NoSig.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/NoSig.java new file mode 100644 index 0000000000..abf599f26a --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/NoSig.java @@ -0,0 +1,90 @@ +package org.bouncycastle.jcajce.provider.asymmetric; + +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SignatureException; +import java.security.SignatureSpi; + +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; + +public class NoSig +{ + private static final String PREFIX = "org.bouncycastle.jcajce.provider.asymmetric.NoSig$"; + + public static class SigSpi + extends SignatureSpi + { + @Override + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException + { + throw new InvalidKeyException("attempt to pass public key to NoSig"); + } + + @Override + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException + { + throw new InvalidKeyException("attempt to pass private key to NoSig"); + } + + @Override + protected void engineUpdate(byte b) + throws SignatureException + { + + } + + @Override + protected void engineUpdate(byte[] bytes, int i, int i1) + throws SignatureException + { + + } + + @Override + protected byte[] engineSign() + throws SignatureException + { + return new byte[0]; + } + + @Override + protected boolean engineVerify(byte[] bytes) + throws SignatureException + { + return false; + } + + @Override + protected void engineSetParameter(String s, Object o) + throws InvalidParameterException + { + + } + + @Override + protected Object engineGetParameter(String s) + throws InvalidParameterException + { + return null; + } + } + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("Signature." + X509ObjectIdentifiers.id_alg_noSignature, PREFIX + "SigSpi"); + } + } +} diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java index c0ab81239a..f26c929495 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java @@ -123,7 +123,7 @@ public final class BouncyCastleProvider extends Provider // later ones configure it. private static final String[] ASYMMETRIC_GENERIC = { - "X509", "IES", "COMPOSITE", "EXTERNAL", "CompositeSignatures" + "X509", "IES", "COMPOSITE", "EXTERNAL", "CompositeSignatures", "NoSig" }; private static final String[] ASYMMETRIC_CIPHERS = From 35eabaa2782a984877bec742ec64d438d877d4cc Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 25 Feb 2025 14:25:22 +0700 Subject: [PATCH 131/890] Refactor X9ECParameters --- .../org/bouncycastle/asn1/x9/X9ECParameters.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java b/core/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java index 31f5d429eb..3dc42979b6 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java +++ b/core/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java @@ -12,6 +12,7 @@ import org.bouncycastle.math.ec.ECAlgorithms; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.field.FiniteField; import org.bouncycastle.math.field.PolynomialExtensionField; import org.bouncycastle.util.Arrays; @@ -112,14 +113,15 @@ public X9ECParameters( this.h = h; this.seed = Arrays.clone(seed); - if (ECAlgorithms.isFpCurve(curve)) + FiniteField field = curve.getField(); + if (ECAlgorithms.isFpField(field)) { - this.fieldID = new X9FieldID(curve.getField().getCharacteristic()); + this.fieldID = new X9FieldID(field.getCharacteristic()); } - else if (ECAlgorithms.isF2mCurve(curve)) + else if (ECAlgorithms.isF2mField(field)) { - PolynomialExtensionField field = (PolynomialExtensionField)curve.getField(); - int[] exponents = field.getMinimalPolynomial().getExponentsPresent(); + PolynomialExtensionField f2mField = (PolynomialExtensionField)field; + int[] exponents = f2mField.getMinimalPolynomial().getExponentsPresent(); if (exponents.length == 3) { this.fieldID = new X9FieldID(exponents[2], exponents[1]); From 761d9644f1e68c25176941fc9ba9e6bb4df66aff Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 25 Feb 2025 15:06:27 +0700 Subject: [PATCH 132/890] Add getQ to ECCurve.AbstractFp --- .../j2me/org/bouncycastle/math/ec/ECCurve.java | 17 ++++++++++------- .../crypto/agreement/ecjpake/ECJPAKECurve.java | 2 +- .../java/org/bouncycastle/math/ec/ECCurve.java | 17 ++++++++++------- .../org/bouncycastle/math/ec/ECCurve.java | 17 ++++++++++------- .../eac/jcajce/JcaPublicKeyConverter.java | 2 +- .../jce/provider/test/DetDSATest.java | 13 ++++++++----- 6 files changed, 40 insertions(+), 28 deletions(-) diff --git a/core/src/main/j2me/org/bouncycastle/math/ec/ECCurve.java b/core/src/main/j2me/org/bouncycastle/math/ec/ECCurve.java index 41e6cc796f..b580541a51 100644 --- a/core/src/main/j2me/org/bouncycastle/math/ec/ECCurve.java +++ b/core/src/main/j2me/org/bouncycastle/math/ec/ECCurve.java @@ -593,9 +593,14 @@ protected AbstractFp(BigInteger q) super(FiniteFields.getPrimeField(q)); } + public BigInteger getQ() + { + return getField().getCharacteristic(); + } + public boolean isValidFieldElement(BigInteger x) { - return x != null && x.signum() >= 0 && x.compareTo(this.getField().getCharacteristic()) < 0; + return x != null && x.signum() >= 0 && x.compareTo(this.getQ()) < 0; } public ECFieldElement randomFieldElement(SecureRandom r) @@ -604,7 +609,7 @@ public ECFieldElement randomFieldElement(SecureRandom r) * NOTE: BigInteger comparisons in the rejection sampling are not constant-time, so we * use the product of two independent elements to mitigate side-channels. */ - BigInteger p = this.getField().getCharacteristic(); + BigInteger p = this.getQ(); ECFieldElement fe1 = this.fromBigInteger(implRandomFieldElement(r, p)); ECFieldElement fe2 = this.fromBigInteger(implRandomFieldElement(r, p)); return fe1.multiply(fe2); @@ -616,7 +621,7 @@ public ECFieldElement randomFieldElementMult(SecureRandom r) * NOTE: BigInteger comparisons in the rejection sampling are not constant-time, so we * use the product of two independent elements to mitigate side-channels. */ - BigInteger p = this.getField().getCharacteristic(); + BigInteger p = this.getQ(); ECFieldElement fe1 = this.fromBigInteger(implRandomFieldElementMult(r, p)); ECFieldElement fe2 = this.fromBigInteger(implRandomFieldElementMult(r, p)); return fe1.multiply(fe2); @@ -699,12 +704,11 @@ public Fp(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger if (isInternal) { - this.q = q; knownQs.add(q); } else if (knownQs.contains(q) || validatedQs.contains(q)) { - this.q = q; + // No need to validate } else { @@ -724,10 +728,9 @@ else if (knownQs.contains(q) || validatedQs.contains(q)) } validatedQs.add(q); - - this.q = q; } + this.q = q; this.r = ECFieldElement.Fp.calculateResidue(q); this.infinity = new ECPoint.Fp(this, null, null); diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKECurve.java b/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKECurve.java index a90ef1629d..1d56989471 100644 --- a/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKECurve.java +++ b/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKECurve.java @@ -159,7 +159,7 @@ public BigInteger getH() public BigInteger getQ() { - return curve.getField().getCharacteristic(); + return curve.getQ(); } private static BigInteger calculateDeterminant(BigInteger q, BigInteger a, BigInteger b) diff --git a/core/src/main/java/org/bouncycastle/math/ec/ECCurve.java b/core/src/main/java/org/bouncycastle/math/ec/ECCurve.java index 4bef7efac6..de75cb24ff 100644 --- a/core/src/main/java/org/bouncycastle/math/ec/ECCurve.java +++ b/core/src/main/java/org/bouncycastle/math/ec/ECCurve.java @@ -596,9 +596,14 @@ protected AbstractFp(BigInteger q) super(FiniteFields.getPrimeField(q)); } + public BigInteger getQ() + { + return getField().getCharacteristic(); + } + public boolean isValidFieldElement(BigInteger x) { - return x != null && x.signum() >= 0 && x.compareTo(this.getField().getCharacteristic()) < 0; + return x != null && x.signum() >= 0 && x.compareTo(getQ()) < 0; } public ECFieldElement randomFieldElement(SecureRandom r) @@ -607,7 +612,7 @@ public ECFieldElement randomFieldElement(SecureRandom r) * NOTE: BigInteger comparisons in the rejection sampling are not constant-time, so we * use the product of two independent elements to mitigate side-channels. */ - BigInteger p = getField().getCharacteristic(); + BigInteger p = getQ(); ECFieldElement fe1 = fromBigInteger(implRandomFieldElement(r, p)); ECFieldElement fe2 = fromBigInteger(implRandomFieldElement(r, p)); return fe1.multiply(fe2); @@ -619,7 +624,7 @@ public ECFieldElement randomFieldElementMult(SecureRandom r) * NOTE: BigInteger comparisons in the rejection sampling are not constant-time, so we * use the product of two independent elements to mitigate side-channels. */ - BigInteger p = getField().getCharacteristic(); + BigInteger p = getQ(); ECFieldElement fe1 = fromBigInteger(implRandomFieldElementMult(r, p)); ECFieldElement fe2 = fromBigInteger(implRandomFieldElementMult(r, p)); return fe1.multiply(fe2); @@ -702,12 +707,11 @@ public Fp(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger if (isInternal) { - this.q = q; knownQs.add(q); } else if (knownQs.contains(q) || validatedQs.contains(q)) { - this.q = q; + // No need to validate } else { @@ -727,10 +731,9 @@ else if (knownQs.contains(q) || validatedQs.contains(q)) } validatedQs.add(q); - - this.q = q; } + this.q = q; this.r = ECFieldElement.Fp.calculateResidue(q); this.infinity = new ECPoint.Fp(this, null, null); diff --git a/core/src/main/jdk1.2/org/bouncycastle/math/ec/ECCurve.java b/core/src/main/jdk1.2/org/bouncycastle/math/ec/ECCurve.java index fe62e8f1fd..8539b62c24 100644 --- a/core/src/main/jdk1.2/org/bouncycastle/math/ec/ECCurve.java +++ b/core/src/main/jdk1.2/org/bouncycastle/math/ec/ECCurve.java @@ -596,9 +596,14 @@ protected AbstractFp(BigInteger q) super(FiniteFields.getPrimeField(q)); } + public BigInteger getQ() + { + return getField().getCharacteristic(); + } + public boolean isValidFieldElement(BigInteger x) { - return x != null && x.signum() >= 0 && x.compareTo(this.getField().getCharacteristic()) < 0; + return x != null && x.signum() >= 0 && x.compareTo(this.getQ()) < 0; } public ECFieldElement randomFieldElement(SecureRandom r) @@ -607,7 +612,7 @@ public ECFieldElement randomFieldElement(SecureRandom r) * NOTE: BigInteger comparisons in the rejection sampling are not constant-time, so we * use the product of two independent elements to mitigate side-channels. */ - BigInteger p = this.getField().getCharacteristic(); + BigInteger p = this.getQ(); ECFieldElement fe1 = this.fromBigInteger(implRandomFieldElement(r, p)); ECFieldElement fe2 = this.fromBigInteger(implRandomFieldElement(r, p)); return fe1.multiply(fe2); @@ -619,7 +624,7 @@ public ECFieldElement randomFieldElementMult(SecureRandom r) * NOTE: BigInteger comparisons in the rejection sampling are not constant-time, so we * use the product of two independent elements to mitigate side-channels. */ - BigInteger p = this.getField().getCharacteristic(); + BigInteger p = this.getQ(); ECFieldElement fe1 = this.fromBigInteger(implRandomFieldElementMult(r, p)); ECFieldElement fe2 = this.fromBigInteger(implRandomFieldElementMult(r, p)); return fe1.multiply(fe2); @@ -702,12 +707,11 @@ public Fp(BigInteger q, BigInteger a, BigInteger b, BigInteger order, BigInteger if (isInternal) { - this.q = q; knownQs.add(q); } else if (knownQs.contains(q) || validatedQs.contains(q)) { - this.q = q; + // No need to validate } else { @@ -727,10 +731,9 @@ else if (knownQs.contains(q) || validatedQs.contains(q)) } validatedQs.add(q); - - this.q = q; } + this.q = q; this.r = ECFieldElement.Fp.calculateResidue(q); this.infinity = new ECPoint.Fp(this, null, null); diff --git a/pkix/src/main/jdk1.4/org/bouncycastle/eac/jcajce/JcaPublicKeyConverter.java b/pkix/src/main/jdk1.4/org/bouncycastle/eac/jcajce/JcaPublicKeyConverter.java index eab5e73a0b..a156d107e3 100644 --- a/pkix/src/main/jdk1.4/org/bouncycastle/eac/jcajce/JcaPublicKeyConverter.java +++ b/pkix/src/main/jdk1.4/org/bouncycastle/eac/jcajce/JcaPublicKeyConverter.java @@ -132,7 +132,7 @@ public PublicKeyDataObject getPublicKeyDataObject(ASN1ObjectIdentifier usage, Pu return new ECDSAPublicKey( usage, - curve.getField().getCharacteristic(), + curve.getQ(), curve.getA().toBigInteger(), curve.getB().toBigInteger(), params.getG().getEncoded(false), diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/DetDSATest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/DetDSATest.java index 8e75b474c5..1c4a7cef9b 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/DetDSATest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/DetDSATest.java @@ -14,8 +14,8 @@ import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1Sequence; -import org.bouncycastle.asn1.nist.NISTNamedCurves; import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.util.encoders.Hex; @@ -78,13 +78,16 @@ private void doTestHMACDetDSATest(String algName, PrivateKey privKey, BigInteger private void testECHMacDeterministic() throws Exception { - X9ECParameters x9ECParameters = NISTNamedCurves.getByName("P-192"); - ECCurve curve = x9ECParameters.getCurve(); + X9ECParameters x9ECParameters = CustomNamedCurves.getByName("P-192"); + ECCurve.AbstractFp curve = (ECCurve.AbstractFp)x9ECParameters.getCurve(); + BigInteger q = curve.getQ(); + + org.bouncycastle.math.ec.ECPoint g = x9ECParameters.getG().normalize(); ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(new BigInteger("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), new ECParameterSpec( - new EllipticCurve(new ECFieldFp(((ECCurve.Fp)curve).getQ()), curve.getA().toBigInteger(), curve.getB().toBigInteger(), null), - new ECPoint(x9ECParameters.getG().getXCoord().toBigInteger(), x9ECParameters.getG().getYCoord().toBigInteger()), + new EllipticCurve(new ECFieldFp(q), curve.getA().toBigInteger(), curve.getB().toBigInteger(), null), + new ECPoint(g.getAffineXCoord().toBigInteger(), g.getAffineYCoord().toBigInteger()), x9ECParameters.getN(), x9ECParameters.getH().intValue()) ); From 7b5e242061a33896fdb957b798f3ecc18892ce62 Mon Sep 17 00:00:00 2001 From: mwcw Date: Wed, 26 Feb 2025 13:41:26 +1100 Subject: [PATCH 133/890] Added module export / require validation to check stage --- ci/check_java.sh | 3 +++ run_mtt.sh | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100755 run_mtt.sh diff --git a/ci/check_java.sh b/ci/check_java.sh index 0883c084de..6b355cd4ff 100644 --- a/ci/check_java.sh +++ b/ci/check_java.sh @@ -22,3 +22,6 @@ export PATH=$JAVA_HOME/bin:$PATH ./gradlew clean build -x test ./osgi_scan.sh + +# module tester +./run_mtt.sh \ No newline at end of file diff --git a/run_mtt.sh b/run_mtt.sh new file mode 100755 index 0000000000..ed60f4fd54 --- /dev/null +++ b/run_mtt.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +set -e + +export script_loc=$( cd -- "$( dirname -- "$0" )" &> /dev/null && pwd ) +version=$(fgrep version "$script_loc/gradle.properties" | sed -e "s/version=//") + +echo "" +echo "Module dependency testing" +echo "" + +# +# This is an internal tool used to verify that classes needed by one module +# are correctly exported by another module. +# + +levels=( "9" "11" "17" "21" ) + +for level in "${levels[@]}" +do +echo "---------------------------------------------------------------------" +echo "Start ${level}" + +( + echo "With Jakarta mail.." + modtest \ + -scan "${script_loc}/jmail/build/libs/bcjmail-jdk18on-${version}.jar" \ + -scan "${script_loc}/mls/build/libs/bcmls-jdk18on-${version}.jar" \ + -scan "${script_loc}/pg/build/libs/bcpg-jdk18on-${version}.jar" \ + -scan "${script_loc}/pkix/build/libs/bcpkix-jdk18on-${version}.jar" \ + -scan "${script_loc}/prov/build/libs/bcprov-jdk18on-${version}.jar" \ + -scan "${script_loc}/tls/build/libs/bctls-jdk18on-${version}.jar" \ + -scan "${script_loc}/util/build/libs/bcutil-jdk18on-${version}.jar" \ + -include "^org\.bouncycastle\..*" \ + -ignore "^java\..*" \ + -ignore "^javax\..*" \ + -ignore "^jakarta\..*" \ + -ignore "^io\.grpc\..*" \ + -ignore "^com\.google\..*" \ + -ignore "^com\.sun\..*" \ + -jvmlevel ${level} +) + +( # mail + echo "With Java mail.." + modtest \ + -scan "${script_loc}/mail/build/libs/bcmail-jdk18on-${version}.jar" \ + -scan "${script_loc}/mls/build/libs/bcmls-jdk18on-${version}.jar" \ + -scan "${script_loc}/pg/build/libs/bcpg-jdk18on-${version}.jar" \ + -scan "${script_loc}/pkix/build/libs/bcpkix-jdk18on-${version}.jar" \ + -scan "${script_loc}/prov/build/libs/bcprov-jdk18on-${version}.jar" \ + -scan "${script_loc}/tls/build/libs/bctls-jdk18on-${version}.jar" \ + -scan "${script_loc}/util/build/libs/bcutil-jdk18on-${version}.jar" \ + -include "^org\.bouncycastle\..*" \ + -ignore "^java\..*" \ + -ignore "^javax\..*" \ + -ignore "^jakarta\..*" \ + -ignore "^io\.grpc\..*" \ + -ignore "^com\.google\..*" \ + -ignore "^com\.sun\..*" \ + -jvmlevel ${level} +) + echo "End java ${level}" + echo "" +done \ No newline at end of file From cf561fbded87af46ba3bcfc7f691cb1769e9c5a9 Mon Sep 17 00:00:00 2001 From: mwcw Date: Wed, 26 Feb 2025 13:56:38 +1100 Subject: [PATCH 134/890] Removed unnecessary rebuild in check_java.sh --- ci/check_java.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ci/check_java.sh b/ci/check_java.sh index 6b355cd4ff..7300ac561f 100644 --- a/ci/check_java.sh +++ b/ci/check_java.sh @@ -15,11 +15,10 @@ export JAVA_HOME=`openjdk_21` export PATH=$JAVA_HOME/bin:$PATH # Checkstyle -./gradlew check -x test; +./gradlew clean build check -x test; # OSGI scanner only, no testing -./gradlew clean build -x test ./osgi_scan.sh From aac5f3ea588e8c24b289d602ede40bc92078da70 Mon Sep 17 00:00:00 2001 From: David Hook Date: Wed, 26 Feb 2025 15:33:42 +1100 Subject: [PATCH 135/890] added test for trustAnchor using id-alg-noSignature --- .../provider/test/CertPathBuilderTest.java | 57 +++++++++++++++++-- .../jce/provider/test/TestUtils.java | 33 +++++++++++ 2 files changed, 86 insertions(+), 4 deletions(-) diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/CertPathBuilderTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/CertPathBuilderTest.java index 07a1e6b992..8b133f984c 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/CertPathBuilderTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/CertPathBuilderTest.java @@ -117,6 +117,54 @@ private void v0Test() } } + private void noSigV0Test() + throws Exception + { + // create certificates and CRLs + KeyPair rootPair = TestUtils.generateRSAKeyPair(); + KeyPair interPair = TestUtils.generateRSAKeyPair(); + KeyPair endPair = TestUtils.generateRSAKeyPair(); + + X509Certificate rootCert = TestUtils.generateNoSigRootCert(rootPair); + X509Certificate interCert = TestUtils.generateIntermediateCert(interPair.getPublic(), rootPair.getPrivate(), rootCert); + X509Certificate endCert = TestUtils.generateEndEntityCert(endPair.getPublic(), interPair.getPrivate(), interCert); + + BigInteger revokedSerialNumber = BigInteger.valueOf(2); + X509CRL rootCRL = TestCertificateGen.createCRL(rootCert, rootPair.getPrivate(), revokedSerialNumber); + X509CRL interCRL = TestCertificateGen.createCRL(interCert, interPair.getPrivate(), revokedSerialNumber); + + // create CertStore to support path building + List list = new ArrayList(); + + list.add(rootCert); + list.add(interCert); + list.add(endCert); + list.add(rootCRL); + list.add(interCRL); + + CollectionCertStoreParameters params = new CollectionCertStoreParameters(list); + CertStore store = CertStore.getInstance("Collection", params); + + // build the path + CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC"); + X509CertSelector pathConstraints = new X509CertSelector(); + + pathConstraints.setSubject(endCert.getSubjectX500Principal().getEncoded()); + + PKIXBuilderParameters buildParams = new PKIXBuilderParameters(Collections.singleton(new TrustAnchor(rootCert, null)), pathConstraints); + + buildParams.addCertStore(store); + buildParams.setDate(new Date()); + + PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult)builder.build(buildParams); + CertPath path = result.getCertPath(); + + if (path.getCertificates().size() != 2) + { + fail("wrong number of certs in v0Test path"); + } + } + private void eeInSelectorTest() throws Exception { @@ -206,10 +254,11 @@ private void eeOnlyInSelectorTest() public void performTest() throws Exception { - baseTest(); - v0Test(); - eeInSelectorTest(); - eeOnlyInSelectorTest(); +// baseTest(); +// v0Test(); + noSigV0Test(); +// eeInSelectorTest(); +// eeOnlyInSelectorTest(); } public String getName() diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/TestUtils.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/TestUtils.java index 12415dcb2e..89a7d1e691 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/TestUtils.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/TestUtils.java @@ -53,6 +53,7 @@ import org.bouncycastle.asn1.x509.Time; import org.bouncycastle.asn1.x509.V1TBSCertificateGenerator; import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator; +import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA1Digest; @@ -117,6 +118,32 @@ public static X509Certificate createSelfSignedCert(X500Name dn, String sigName, return (X509Certificate)CertificateFactory.getInstance("X.509", "BC").generateCertificate(new ByteArrayInputStream(new DERSequence(v).getEncoded(ASN1Encoding.DER))); } + public static X509Certificate createNoSigCert(X500Name dn, KeyPair keyPair) + throws Exception + { + V1TBSCertificateGenerator certGen = new V1TBSCertificateGenerator(); + + long time = System.currentTimeMillis(); + + certGen.setSerialNumber(new ASN1Integer(serialNumber.getAndIncrement())); + certGen.setIssuer(dn); + certGen.setSubject(dn); + certGen.setStartDate(new Time(new Date(time - 5000))); + certGen.setEndDate(new Time(new Date(time + 30 * 60 * 1000))); + certGen.setSignature(new AlgorithmIdentifier(X509ObjectIdentifiers.id_alg_noSignature, DERNull.INSTANCE)); + certGen.setSubjectPublicKeyInfo(SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded())); + + TBSCertificate tbsCert = certGen.generateTBSCertificate(); + + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(tbsCert); + v.add(new AlgorithmIdentifier(X509ObjectIdentifiers.id_alg_noSignature, DERNull.INSTANCE)); + v.add(new DERBitString(new byte[0])); + + return (X509Certificate)CertificateFactory.getInstance("X.509", "BC").generateCertificate(new ByteArrayInputStream(new DERSequence(v).getEncoded(ASN1Encoding.DER))); + } + public static X509Certificate createCert(X500Name signerName, PrivateKey signerKey, String dn, String sigName, Extensions extensions, PublicKey pubKey) throws Exception { @@ -167,6 +194,12 @@ public static KeyPair generateRSAKeyPair() return kpGen.generateKeyPair(); } + public static X509Certificate generateNoSigRootCert(KeyPair pair) + throws Exception + { + return createNoSigCert(new X500Name("CN=Test CA Certificate"), pair); + } + public static X509Certificate generateRootCert(KeyPair pair) throws Exception { From fa78d66e1255dea33c830cb9270ac0f41ac21420 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 27 Feb 2025 17:21:18 +1030 Subject: [PATCH 136/890] Pass Key Pair Generation of Mayo --- .../pqc/crypto/mayo/GF16Utils.java | 185 +++++++++ .../pqc/crypto/mayo/MayoEngine.java | 190 +++++++++ .../mayo/MayoKeyGenerationParameters.java | 24 ++ .../pqc/crypto/mayo/MayoKeyPairGenerator.java | 177 +++++++++ .../pqc/crypto/mayo/MayoKeyParameters.java | 22 ++ .../pqc/crypto/mayo/MayoParameters.java | 364 ++++++++++++++++++ .../crypto/mayo/MayoPrivateKeyParameter.java | 41 ++ .../crypto/mayo/MayoPublicKeyParameter.java | 26 ++ .../bouncycastle/pqc/crypto/mayo/Utils.java | 169 ++++++++ .../util/SubjectPublicKeyInfoFactory.java | 5 +- .../pqc/crypto/test/MayoTest.java | 67 ++++ .../pqc/crypto/test/TestUtils.java | 93 +++++ 12 files changed, 1361 insertions(+), 2 deletions(-) create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoEngine.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyGenerationParameters.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyParameters.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameter.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPublicKeyParameter.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java create mode 100644 core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java new file mode 100644 index 0000000000..9d42c4a5ca --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java @@ -0,0 +1,185 @@ +package org.bouncycastle.pqc.crypto.mayo; + +public class GF16Utils +{ + + /** + * Multiplies a 64-bit limb by a GF(16) element (represented as an int, 0–255). + * This emulates gf16v_mul_u64 from C. + * + * @param a a 64-bit limb + * @param b an 8-bit GF(16) element (only the low 4 bits are used) + * @return the product as a 64-bit limb + */ + public static long gf16vMulU64(long a, int b) + { + long maskMsb = 0x8888888888888888L; + long a64 = a; + // In the original code there is a conditional XOR with unsigned_char_blocker; + // here we simply use b directly. + long b32 = b & 0x00000000FFFFFFFFL; + long r64 = a64 * (b32 & 1); + + long a_msb = a64 & maskMsb; + a64 ^= a_msb; + a64 = (a64 << 1) ^ ((a_msb >>> 3) * 3); + r64 ^= a64 * ((b32 >> 1) & 1); + + a_msb = a64 & maskMsb; + a64 ^= a_msb; + a64 = (a64 << 1) ^ ((a_msb >>> 3) * 3); + r64 ^= a64 * ((b32 >>> 2) & 1); + + a_msb = a64 & maskMsb; + a64 ^= a_msb; + a64 = (a64 << 1) ^ ((a_msb >>> 3) * 3); + r64 ^= a64 * ((b32 >> 3) & 1); + + return r64; + } + + /** + * Multiplies each limb of a GF(16) vector (subarray of 'in') by the GF(16) element 'a' + * and XORs the result into the corresponding subarray of acc. + *

+ * This version uses explicit array offsets. + * + * @param mVecLimbs the number of limbs in the vector + * @param in the input long array containing the vector; the vector starts at index inOffset + * @param inOffset the starting index in 'in' + * @param a the GF(16) element (0–255) to multiply by + * @param acc the accumulator long array; the target vector starts at index accOffset + * @param accOffset the starting index in 'acc' + */ + public static void mVecMulAdd(int mVecLimbs, long[] in, int inOffset, int a, long[] acc, int accOffset) + { + for (int i = 0; i < mVecLimbs; i++) + { + acc[accOffset + i] ^= gf16vMulU64(in[inOffset + i], a); + } + } + + /** + * Convenience overload of mVecMulAdd that assumes zero offsets. + * + * @param mVecLimbs the number of limbs + * @param in the input vector + * @param a the GF(16) element to multiply by + * @param acc the accumulator vector + */ + public static void mVecMulAdd(int mVecLimbs, long[] in, int a, long[] acc) + { + mVecMulAdd(mVecLimbs, in, 0, a, acc, 0); + } + + /** + * Performs the multiplication and accumulation of a block of an upper‐triangular matrix + * times a second matrix. + * + * @param mVecLimbs number of limbs per m-vector. + * @param bsMat the “basis” matrix (as a flat long[] array); each entry occupies mVecLimbs elements. + * @param mat the second matrix (as a flat byte[] array) stored row‐major, + * with dimensions (bsMatCols x matCols). + * @param acc the accumulator (as a flat long[] array) with dimensions (bsMatRows x matCols); + * each “entry” is an m‐vector (length mVecLimbs). + * @param bsMatRows number of rows in the bsMat (the “triangular” matrix’s row count). + * @param bsMatCols number of columns in bsMat. + * @param matCols number of columns in the matrix “mat.” + * @param triangular if 1, start column index for each row is (r * triangular); otherwise use 0. + */ + public static void mulAddMUpperTriangularMatXMat(int mVecLimbs, long[] bsMat, byte[] mat, long[] acc, + int bsMatRows, int bsMatCols, int matCols, int triangular) + { + int bsMatEntriesUsed = 0; + for (int r = 0; r < bsMatRows; r++) + { + // For each row r, the inner loop goes from column triangular*r to bsMatCols-1. + for (int c = triangular * r; c < bsMatCols; c++) + { + for (int k = 0; k < matCols; k++) + { + // Calculate the offsets: + // For bsMat: the m-vector starting at index bsMatEntriesUsed * mVecLimbs. + int bsMatOffset = bsMatEntriesUsed * mVecLimbs; + // For mat: element at row c, column k (row-major layout). + int a = mat[c * matCols + k] & 0xFF; + // For acc: add into the m-vector at row r, column k. + int accOffset = (r * matCols + k) * mVecLimbs; + GF16Utils.mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, a, acc, accOffset); + } + bsMatEntriesUsed++; + } + } + } + + /** + * Computes P1_times_O. + *

+ * In C: + * P1_times_O(p, P1, O, acc) calls: + * mul_add_m_upper_triangular_mat_x_mat(PARAM_m_vec_limbs(p), P1, O, acc, PARAM_v(p), PARAM_v(p), PARAM_o(p), 1); + * + * @param p the parameter object. + * @param P1 the P1 matrix as a long[] array. + * @param O the O matrix as a byte[] array. + * @param acc the output accumulator (long[] array). + */ + public static void P1TimesO(MayoParameters p, long[] P1, byte[] O, long[] acc) + { + int mVecLimbs = p.getMVecLimbs(); + int paramV = p.getV(); + int paramO = p.getO(); + // Here, bsMatRows and bsMatCols are both paramV, and matCols is paramO, triangular=1. + mulAddMUpperTriangularMatXMat(mVecLimbs, P1, O, acc, paramV, paramV, paramO, 1); + } + + /** + * Multiplies the transpose of a single matrix with m matrices and adds the result into acc. + * + * @param mVecLimbs number of limbs per m-vector. + * @param mat the matrix to be transposed (as a flat byte[] array), dimensions: (matRows x matCols). + * @param bsMat the m-matrix (as a flat long[] array), with each entry of length mVecLimbs. + * Its logical dimensions: (matRows x bsMatCols). + * @param acc the accumulator (as a flat long[] array) with dimensions (matCols x bsMatCols); + * each entry is an m-vector. + * @param matRows number of rows in the matrix “mat.” + * @param matCols number of columns in “mat.” + * @param bsMatCols number of columns in the bsMat matrix. + */ + public static void mulAddMatTransXMMat(int mVecLimbs, byte[] mat, long[] bsMat, long[] acc, + int matRows, int matCols, int bsMatCols) + { + // Loop over each column r of mat (which becomes row of mat^T) + for (int r = 0; r < matCols; r++) + { + for (int c = 0; c < matRows; c++) + { + for (int k = 0; k < bsMatCols; k++) + { + // For bsMat: the m-vector at index (c * bsMatCols + k) + int bsMatOffset = (c * bsMatCols + k) * mVecLimbs; + // For mat: element at row c, column r. + int a = mat[c * matCols + r] & 0xFF; + // For acc: add into the m-vector at index (r * bsMatCols + k) + int accOffset = (r * bsMatCols + k) * mVecLimbs; + GF16Utils.mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, a, acc, accOffset); + } + } + } + } + + + /** + * Adds (bitwise XOR) mVecLimbs elements from the source array (starting at srcOffset) + * into the destination array (starting at destOffset). + */ + public static void mVecAdd(int mVecLimbs, long[] src, int srcOffset, long[] dest, int destOffset) + { + for (int i = 0; i < mVecLimbs; i++) + { + dest[destOffset + i] ^= src[srcOffset + i]; + } + } + +} + diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoEngine.java new file mode 100644 index 0000000000..51cf98bc74 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoEngine.java @@ -0,0 +1,190 @@ +package org.bouncycastle.pqc.crypto.mayo; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.modes.CTRModeCipher; +import org.bouncycastle.crypto.modes.SICBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Pack; + +public class MayoEngine +{ + /** + * Expands P1 and P2 using AES_128_CTR as a PRF and then unpacks the resulting bytes + * into an array of 64-bit limbs. + * + * @param p Mayo parameters + * @param P The output long array which will hold the unpacked limbs. + * Its length should be at least ((P1_bytes + P2_bytes) / 8) limbs. + * @param seed_pk The seed (used as the key) for the PRF. + * @return The number of bytes produced, i.e., P1_bytes + P2_bytes. + */ + public static int expandP1P2(MayoParameters p, long[] P, byte[] seed_pk) + { + // Compute total number of bytes to generate: P1_bytes + P2_bytes. + int outLen = p.getP1Bytes() + p.getP2Bytes(); + // Temporary byte array to hold the PRF output. + byte[] temp = new byte[outLen]; + + // Call AES_128_CTR (our previously defined function using BouncyCastle) + // to fill temp with outLen pseudorandom bytes using seed_pk as key. + AES_128_CTR(temp, outLen, seed_pk, p.getPkSeedBytes()); + + // The number of vectors is the total limbs divided by mVecLimbs. + int numVectors = (p.getP1Limbs() + p.getP2Limbs()) / p.getMVecLimbs(); + + // Unpack the byte array 'temp' into the long array 'P' + // using our previously defined unpackMVecs method. + Utils.unpackMVecs(temp, P, numVectors, p.getM()); + + // Return the number of output bytes produced. + return outLen; + } + + /** + * AES_128_CTR generates outputByteLen bytes using AES-128 in CTR mode. + * The key (of length keyLen) is used to expand the AES key. + * A 16-byte IV (all zeros) is used. + * + * @param output the output buffer which will be filled with the keystream + * @param outputByteLen the number of bytes to produce + * @param key the AES key (should be 16 bytes for AES-128) + * @param keyLen the length of the key (unused here but kept for similarity) + * @return the number of output bytes produced (i.e. outputByteLen) + */ + public static int AES_128_CTR(byte[] output, int outputByteLen, byte[] key, int keyLen) + { + // Create a 16-byte IV (all zeros) + byte[] iv = new byte[16]; // automatically zero-initialized + + // Set up AES engine in CTR (SIC) mode. + BlockCipher aesEngine = AESEngine.newInstance(); + // SICBlockCipher implements CTR mode for AES. + CTRModeCipher ctrCipher = SICBlockCipher.newInstance(aesEngine); + // Wrap the key with the IV. + ParametersWithIV params = new ParametersWithIV(new KeyParameter(Arrays.copyOf(key, keyLen)), iv); + ctrCipher.init(true, params); + + // CTR mode is a stream cipher: encrypting zero bytes produces the keystream. + int blockSize = ctrCipher.getBlockSize(); // typically 16 bytes + byte[] zeroBlock = new byte[blockSize]; // block of zeros + byte[] blockOut = new byte[blockSize]; + + int offset = 0; + // Process full blocks + while (offset + blockSize <= outputByteLen) + { + ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); + System.arraycopy(blockOut, 0, output, offset, blockSize); + offset += blockSize; + } + // Process any remaining partial block. + if (offset < outputByteLen) + { + ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); + int remaining = outputByteLen - offset; + System.arraycopy(blockOut, 0, output, offset, remaining); + } + return outputByteLen; + } + + public static final int MAYO_OK = 0; + public static final int PK_SEED_BYTES_MAX = 16; // Adjust as needed + public static final int O_BYTES_MAX = 312; // Adjust as needed + + /** + * Expands the secret key. + * + * @param p the MayoParameters instance. + * @param csk the input secret key seed (byte array). + * @param sk the Sk object that holds the expanded secret key components. + * @return MAYO_OK on success. + */ +// public static int mayoExpandSk(MayoParameters p, byte[] csk, MayoPrivateKeyParameter sk) +// { +// int ret = MAYO_OK; +// int totalS = PK_SEED_BYTES_MAX + O_BYTES_MAX; +// byte[] S = new byte[totalS]; +// +// // sk.p is the long[] array, sk.O is the byte[] array. +// +// long[] P = new long[p.getPkSeedBytes() >> 3]; +// Pack.littleEndianToLong(sk.getP(), 0, P); +// byte[] O = sk.getO(); +// +// int param_o = p.getO(); +// int param_v = p.getV(); +// int param_O_bytes = p.getOBytes(); +// int param_pk_seed_bytes = p.getPkSeedBytes(); +// int param_sk_seed_bytes = p.getSkSeedBytes(); +// +// // In C, seed_sk = csk and seed_pk = S (the beginning of S) +// byte[] seed_sk = csk; +// byte[] seed_pk = S; // first param_pk_seed_bytes of S +// +// // Generate S = seed_pk || (additional bytes), using SHAKE256. +// // Output length is param_pk_seed_bytes + param_O_bytes. +// Utils.shake256(S, param_pk_seed_bytes + param_O_bytes, seed_sk, param_sk_seed_bytes); +// +// // Decode the portion of S after the first param_pk_seed_bytes into O. +// // (In C, this is: decode(S + param_pk_seed_bytes, O, param_v * param_o)) +// Utils.decode(S, param_pk_seed_bytes, O, param_v * param_o); +// +// // Expand P1 and P2 into the long array P using seed_pk. +// MayoEngine.expandP1P2(p, P, seed_pk); +// +// // Let P2 start at offset = PARAM_P1_limbs(p) +// int p1Limbs = p.getP1Limbs(); +// int offsetP2 = p1Limbs; +// +// // Compute L_i = (P1 + P1^t)*O + P2. +// // Here, we assume that P1P1tTimesO writes into the portion of P starting at offsetP2. +// P1P1tTimesO(p, P, O, P, offsetP2); +// +// // Securely clear sensitive temporary data. +// java.util.Arrays.fill(S, (byte)0); +// return ret; +// } + + /** + * Multiplies and accumulates the product (P1 + P1^t)*O into the accumulator. + * This version writes into the 'acc' array starting at the specified offset. + * + * @param p the MayoParameters. + * @param P1 the P1 vector as a long[] array. + * @param O the O array (each byte represents a GF(16) element). + * @param acc the accumulator array where results are XORed in. + * @param accOffset the starting index in acc. + */ + public static void P1P1tTimesO(MayoParameters p, long[] P1, byte[] O, long[] acc, int accOffset) + { + int paramO = p.getO(); + int paramV = p.getV(); + int mVecLimbs = p.getMVecLimbs(); + int bsMatEntriesUsed = 0; + for (int r = 0; r < paramV; r++) + { + for (int c = r; c < paramV; c++) + { + if (c == r) + { + bsMatEntriesUsed++; + continue; + } + for (int k = 0; k < paramO; k++) + { + // Multiply the m-vector at P1 for the current matrix entry, + // and accumulate into acc for row r. + GF16Utils.mVecMulAdd(mVecLimbs, P1, bsMatEntriesUsed * mVecLimbs, + O[c * paramO + k] & 0xFF, acc, accOffset + (r * paramO + k) * mVecLimbs); + // Similarly, accumulate into acc for row c. + GF16Utils.mVecMulAdd(mVecLimbs, P1, bsMatEntriesUsed * mVecLimbs, + O[r * paramO + k] & 0xFF, acc, accOffset + (c * paramO + k) * mVecLimbs); + } + bsMatEntriesUsed++; + } + } + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyGenerationParameters.java new file mode 100644 index 0000000000..0ca1b7f1b4 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyGenerationParameters.java @@ -0,0 +1,24 @@ +package org.bouncycastle.pqc.crypto.mayo; + +import java.security.SecureRandom; + +import org.bouncycastle.crypto.KeyGenerationParameters; + +public class MayoKeyGenerationParameters + extends KeyGenerationParameters +{ + private final MayoParameters params; + + public MayoKeyGenerationParameters( + SecureRandom random, + MayoParameters mayoParameters) + { + super(random, 256); + this.params = mayoParameters; + } + + public MayoParameters getParameters() + { + return params; + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java new file mode 100644 index 0000000000..96e4e9fb01 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java @@ -0,0 +1,177 @@ +package org.bouncycastle.pqc.crypto.mayo; + +import java.security.SecureRandom; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.bouncycastle.crypto.KeyGenerationParameters; +import org.bouncycastle.util.Pack; + +public class MayoKeyPairGenerator + implements AsymmetricCipherKeyPairGenerator +{ + private MayoParameters p; + private SecureRandom random; + + + public void init(KeyGenerationParameters param) + { + this.p = ((MayoKeyGenerationParameters)param).getParameters(); + this.random = param.getRandom(); + } + + + @Override + public AsymmetricCipherKeyPair generateKeyPair() + { + int ret = MayoEngine.MAYO_OK; + byte[] cpk = new byte[p.getCpkBytes()]; + // seed_sk points to csk. + byte[] seed_sk = new byte[p.getCskBytes()]; + + // Allocate S = new byte[PK_SEED_BYTES_MAX + O_BYTES_MAX] + byte[] S = new byte[p.getPkSeedBytes() + p.getOBytes()]; + + // Allocate P as a long array of size (P1_LIMBS_MAX + P2_LIMBS_MAX) + long[] P = new long[p.getP1Limbs() + p.getP2Limbs()]; + + // Allocate P3 as a long array of size (O_MAX * O_MAX * M_VEC_LIMBS_MAX), zero-initialized. + long[] P3 = new long[p.getO() * p.getO() * p.getMVecLimbs()]; + + // seed_pk will be a reference into S. + byte[] seed_pk; + + // Allocate O as a byte array of size (V_MAX * O_MAX). + // Here we assume V_MAX is given by p.getV() (or replace with a constant if needed). + byte[] O = new byte[p.getV() * p.getO()]; + + // Retrieve parameters from p. + int m_vec_limbs = p.getMVecLimbs(); + int param_m = p.getM(); + int param_v = p.getV(); + int param_o = p.getO(); + int param_O_bytes = p.getOBytes(); + int param_P1_limbs = p.getP1Limbs(); + int param_P3_limbs = p.getP3Limbs(); + int param_pk_seed_bytes = p.getPkSeedBytes(); + int param_sk_seed_bytes = p.getSkSeedBytes(); + + // In the C code, P1 is P and P2 is P offset by param_P1_limbs. + // In Java, we will have functions (like expandP1P2) work on the full array P. + + // Generate secret key seed (seed_sk) using a secure random generator. + random.nextBytes(seed_sk); + + // S ← shake256(seed_sk, pk_seed_bytes + O_bytes) + Utils.shake256(S, param_pk_seed_bytes + param_O_bytes, seed_sk, param_sk_seed_bytes); + + // seed_pk is the beginning of S. + seed_pk = S; + + // o ← Decode_o(S[ param_pk_seed_bytes : param_pk_seed_bytes + O_bytes ]) + // Decode nibbles from S starting at offset param_pk_seed_bytes into O, + // with expected output length = param_v * param_o. + Utils.decode(S, param_pk_seed_bytes, O, param_v * param_o); + + // Expand P1 and P2 into the array P using seed_pk. + MayoEngine.expandP1P2(p, P, seed_pk); + + // For compute_P3, we need to separate P1 and P2. + // Here, we treat P1 as the first param_P1_limbs elements of P, + // and P2 as the remaining elements. + long[] P1 = P; + long[] P2 = new long[P.length - param_P1_limbs]; + System.arraycopy(P, param_P1_limbs, P2, 0, P2.length); + + // Compute P3, which (in the process) modifies P2. + computeP3(p, P1, P2, O, P3); + + // Store seed_pk into the public key cpk. + System.arraycopy(seed_pk, 0, cpk, 0, param_pk_seed_bytes); + + // Allocate an array for the "upper" part of P3. + long[] P3_upper = new long[p.getP3Limbs()]; + + // Compute Upper(P3) and store the result in P3_upper. + mUpper(p, P3, P3_upper, param_o); + + // Pack the m-vectors in P3_upper into cpk (after the seed_pk). + // The number of m-vectors to pack is (param_P3_limbs / m_vec_limbs), + // and param_m is used as the m value. + Utils.packMVecs(P3_upper, cpk, param_pk_seed_bytes, param_P3_limbs / m_vec_limbs, param_m); + // Securely clear sensitive data. +// secureClear(O); +// secureClear(P2); +// secureClear(P3); + + return new AsymmetricCipherKeyPair(new MayoPublicKeyParameter(p, cpk), new MayoPrivateKeyParameter(p, seed_sk)); + } + + /** + * Computes P3 from P1, P2, and O. + *

+ * In C, compute_P3 does: + * 1. Compute P1*O + P2, storing result in P2. + * 2. Compute P3 = O^T * (P1*O + P2). + * + * @param p the parameter object. + * @param P1 the P1 matrix as a long[] array. + * @param P2 the P2 matrix as a long[] array; on output, P1*O is added to it. + * @param O the O matrix as a byte[] array. + * @param P3 the output matrix (as a long[] array) which will receive O^T*(P1*O + P2). + */ + public static void computeP3(MayoParameters p, long[] P1, long[] P2, byte[] O, long[] P3) + { + int mVecLimbs = p.getMVecLimbs(); + int paramV = p.getV(); + int paramO = p.getO(); + + // Compute P1 * O + P2 and store the result in P2. + GF16Utils.P1TimesO(p, P1, O, P2); + + // Compute P3 = O^T * (P1*O + P2). + // Here, treat P2 as the bsMat for the multiplication. + // Dimensions: mat = O (size: paramV x paramO), bsMat = P2 (size: paramV x paramO), + // and acc (P3) will have dimensions: (paramO x paramO), each entry being an m-vector. + GF16Utils.mulAddMatTransXMMat(mVecLimbs, O, P2, P3, paramV, paramO, paramO); + } + + /** + * Reproduces the behavior of the C function m_upper. + *

+ * For each pair (r, c) with 0 <= r <= c < size, it copies the m-vector at + * position (r, c) from 'in' to the next position in 'out' and, if r != c, + * it adds (XORs) the m-vector at position (c, r) into that same output vector. + * + * @param p the parameter object (used to get mVecLimbs) + * @param in the input long array (each vector is mVecLimbs in length) + * @param out the output long array (must be large enough to store all output vectors) + * @param size the size parameter defining the matrix dimensions. + */ + public static void mUpper(MayoParameters p, long[] in, long[] out, int size) + { + int mVecLimbs = p.getMVecLimbs(); + int mVecsStored = 0; + for (int r = 0; r < size; r++) + { + for (int c = r; c < size; c++) + { + // Compute the starting index for the (r, c) vector in the input array. + int srcOffset = mVecLimbs * (r * size + c); + // Compute the output offset for the current stored vector. + int destOffset = mVecLimbs * mVecsStored; + + // Copy the vector at (r, c) into the output. + System.arraycopy(in, srcOffset, out, destOffset, mVecLimbs); + + // If off-diagonal, add (XOR) the vector at (c, r) into the same output vector. + if (r != c) + { + int srcOffset2 = mVecLimbs * (c * size + r); + GF16Utils.mVecAdd(mVecLimbs, in, srcOffset2, out, destOffset); + } + mVecsStored++; + } + } + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyParameters.java new file mode 100644 index 0000000000..4b932949dc --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyParameters.java @@ -0,0 +1,22 @@ +package org.bouncycastle.pqc.crypto.mayo; + +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; + +public class MayoKeyParameters + extends AsymmetricKeyParameter +{ + private final MayoParameters params; + + public MayoKeyParameters( + boolean isPrivate, + MayoParameters params) + { + super(isPrivate); + this.params = params; + } + + public MayoParameters getParameters() + { + return params; + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java new file mode 100644 index 0000000000..e6e285167f --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java @@ -0,0 +1,364 @@ +package org.bouncycastle.pqc.crypto.mayo; + +public class MayoParameters +{ + public static final MayoParameters MAYO1 = new MayoParameters( + "MAYO_1", // name + 86, // n + 78, // m + 5, // m_vec_limbs + 8, // o + 86 - 8, // v = n - o = 78 + 10 * 8 + 1, // A_cols = k * o + 1 = 10 * 8 + 1 = 81 + 10, // k + 16, // q + 39, // m_bytes + 312, // O_bytes + 39, // v_bytes + 40, // r_bytes + 120159, // P1_bytes + 24336, // P2_bytes + 1404, // P3_bytes + 24, // csk_bytes + 1420, // cpk_bytes + 454, // sig_bytes + new int[]{8, 1, 1, 0}, // F_TAIL_78 + new byte[]{8, 1, 1, 0}, // f_tail_arr + 24, // salt_bytes + 32, // digest_bytes + 16, // pk_seed_bytes + 24 // sk_seed_bytes + ); + + public static final MayoParameters MAYO2 = new MayoParameters( + "MAYO_2", // name + 81, // n + 64, // m + 4, // m_vec_limbs + 17, // o + 81 - 17, // v = 64 + 4 * 17 + 1, // A_cols = 4 * 17 + 1 = 69 + 4, // k + 16, // q + 32, // m_bytes + 544, // O_bytes + 32, // v_bytes + 34, // r_bytes + 66560, // P1_bytes + 34816, // P2_bytes + 4896, // P3_bytes + 24, // csk_bytes + 4912, // cpk_bytes + 186, // sig_bytes + new int[]{8, 0, 2, 8}, //F_TAIL_64 + new byte[]{8, 0, 2, 8}, // f_tail_arr + 24, // salt_bytes + 32, // digest_bytes + 16, // pk_seed_bytes + 24 // sk_seed_bytes + ); + + public static final MayoParameters MAYO3 = new MayoParameters( + "MAYO_3", // name + 118, // n + 108, // m + 7, // m_vec_limbs + 10, // o + 118 - 10, // v = 108 + 11 * 10 + 1, // A_cols = 11 * 10 + 1 = 111 + 11, // k + 16, // q + 54, // m_bytes + 540, // O_bytes + 54, // v_bytes + 55, // r_bytes + 317844, // P1_bytes + 58320, // P2_bytes + 2970, // P3_bytes + 32, // csk_bytes + 2986, // cpk_bytes + 681, // sig_bytes + new int[]{8, 0, 1, 7}, //F_TAIL_108 + new byte[]{8, 0, 1, 7}, // f_tail_arr + 32, // salt_bytes + 48, // digest_bytes + 16, // pk_seed_bytes + 32 // sk_seed_bytes + ); + + public static final MayoParameters MAYO5 = new MayoParameters( + "MAYO_5", // name + 154, // n + 142, // m + 9, // m_vec_limbs + 12, // o + 154 - 12, // v = 142 + 12 * 12 + 1, // A_cols = 12 * 12 + 1 = 145 + 12, // k + 16, // q + 71, // m_bytes + 852, // O_bytes + 71, // v_bytes + 72, // r_bytes + 720863, // P1_bytes + 120984, // P2_bytes + 5538, // P3_bytes + 40, // csk_bytes + 5554, // cpk_bytes + 964, // sig_bytes + new int[]{4, 0, 8, 1}, //F_TAIL_142 + new byte[]{4, 0, 8, 1}, // f_tail_arr + 40, // salt_bytes + 64, // digest_bytes + 16, // pk_seed_bytes + 40 // sk_seed_bytes + ); + + private final String name; + private final int n; + private final int m; + private final int mVecLimbs; + private final int o; + private final int v; + private final int ACols; + private final int k; + private final int q; + private final int mBytes; + private final int OBytes; + private final int vBytes; + private final int rBytes; + private final int P1Bytes; + private final int P2Bytes; + private final int P3Bytes; + private final int cskBytes; + private final int cpkBytes; + private final int sigBytes; + private final int[] fTail; + private final byte[] fTailArr; + private final int saltBytes; + private final int digestBytes; + private final int pkSeedBytes; + private final int skSeedBytes; + + private MayoParameters(String name, int n, int m, int mVecLimbs, int o, int v, int ACols, int k, int q, + int mBytes, int OBytes, int vBytes, int rBytes, int P1Bytes, int P2Bytes, int P3Bytes, + int cskBytes, int cpkBytes, int sigBytes, int[] fTail, byte[] fTailArr, + int saltBytes, int digestBytes, int pkSeedBytes, int skSeedBytes) + { + this.name = name; + this.n = n; + this.m = m; + this.mVecLimbs = mVecLimbs; + this.o = o; + this.v = v; + this.ACols = ACols; + this.k = k; + this.q = q; + this.mBytes = mBytes; + this.OBytes = OBytes; + this.vBytes = vBytes; + this.rBytes = rBytes; + this.P1Bytes = P1Bytes; + this.P2Bytes = P2Bytes; + this.P3Bytes = P3Bytes; + this.cskBytes = cskBytes; + this.cpkBytes = cpkBytes; + this.sigBytes = sigBytes; + this.fTail = fTail; + this.fTailArr = fTailArr; + this.saltBytes = saltBytes; + this.digestBytes = digestBytes; + this.pkSeedBytes = pkSeedBytes; + this.skSeedBytes = skSeedBytes; + } + + public String getName() + { + return name; + } + + public int getN() + { + return n; + } + + public int getM() + { + return m; + } + + public int getMVecLimbs() + { + return mVecLimbs; + } + + public int getO() + { + return o; + } + + public int getV() + { + return v; + } + + public int getACols() + { + return ACols; + } + + public int getK() + { + return k; + } + + public int getQ() + { + return q; + } + + public int getMBytes() + { + return mBytes; + } + + public int getOBytes() + { + return OBytes; + } + + public int getVBytes() + { + return vBytes; + } + + public int getRBytes() + { + return rBytes; + } + + public int getP1Bytes() + { + return P1Bytes; + } + + public int getP2Bytes() + { + return P2Bytes; + } + + public int getP3Bytes() + { + return P3Bytes; + } + + public int getCskBytes() + { + return cskBytes; + } + + public int getCpkBytes() + { + return cpkBytes; + } + + public int getSigBytes() + { + return sigBytes; + } + + public int[] getFTail() + { + return fTail; + } + + public byte[] getFTailArr() + { + return fTailArr; + } + + public int getSaltBytes() + { + return saltBytes; + } + + public int getDigestBytes() + { + return digestBytes; + } + + public int getPkSeedBytes() + { + return pkSeedBytes; + } + + public int getSkSeedBytes() + { + return skSeedBytes; + } + + /** + * Computes: (v * (v + 1) / 2) * mVecLimbs + */ + public int getP1Limbs() + { + return ((v * (v + 1)) / 2) * mVecLimbs; + } + + /** + * Computes: v * o * mVecLimbs + */ + public int getP2Limbs() + { + return v * o * mVecLimbs; + } + + /** + * Computes: (o * (o + 1) / 2) * mVecLimbs + */ + public int getP3Limbs() + { + return ((o * (o + 1)) / 2) * mVecLimbs; + } + + /** + * Computes: P1_limbs + P2_limbs + P3_limbs + */ + public int getEPKLimbs() + { + return getP1Limbs() + getP2Limbs() + getP3Limbs(); + } + + @Override + public String toString() + { + return "MayoParameters{" + + "name='" + name + '\'' + + ", n=" + n + + ", m=" + m + + ", mVecLimbs=" + mVecLimbs + + ", o=" + o + + ", v=" + v + + ", ACols=" + ACols + + ", k=" + k + + ", q=" + q + + ", mBytes=" + mBytes + + ", OBytes=" + OBytes + + ", vBytes=" + vBytes + + ", rBytes=" + rBytes + + ", P1Bytes=" + P1Bytes + + ", P2Bytes=" + P2Bytes + + ", P3Bytes=" + P3Bytes + + ", cskBytes=" + cskBytes + + ", cpkBytes=" + cpkBytes + + ", sigBytes=" + sigBytes + + ", fTail='{" + fTail[0] + "," + fTail[1] + "," + fTail[2] + "," + fTail[3] + "}'" + + ", fTailArr='{" + fTailArr[0] + "," + fTailArr[1] + "," + fTailArr[2] + "," + fTailArr[3] + "}'" + + ", saltBytes=" + saltBytes + + ", digestBytes=" + digestBytes + + ", pkSeedBytes=" + pkSeedBytes + + ", skSeedBytes=" + skSeedBytes + + '}'; + } +} + diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameter.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameter.java new file mode 100644 index 0000000000..7954094a38 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameter.java @@ -0,0 +1,41 @@ +package org.bouncycastle.pqc.crypto.mayo; + +import org.bouncycastle.util.Arrays; + +public class MayoPrivateKeyParameter + extends MayoKeyParameters +{ + // Represents the field: uint64_t p[P1_LIMBS_MAX + P2_LIMBS_MAX]; +// private final byte[] p; + // Represents the field: uint8_t O[V_MAX * O_MAX]; +// private final byte[] O; + private final byte[] seed_sk; + + public MayoPrivateKeyParameter(MayoParameters params, byte[] seed_sk) + { + super(true, params); + this.seed_sk = seed_sk; +// this.p = p; +// this.O = O; + } + +// public byte[] getP() +// { +// return p; +// } +// +// public byte[] getO() +// { +// return O; +// } + + public byte[] getEncoded() + { + return Arrays.clone(seed_sk); + } + + public byte[] getSeedSk() + { + return Arrays.clone(seed_sk); + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPublicKeyParameter.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPublicKeyParameter.java new file mode 100644 index 0000000000..68613b35bd --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPublicKeyParameter.java @@ -0,0 +1,26 @@ +package org.bouncycastle.pqc.crypto.mayo; + +import org.bouncycastle.util.Arrays; + +public class MayoPublicKeyParameter + extends MayoKeyParameters +{ + // Represents the field: uint64_t p[P1_LIMBS_MAX + P2_LIMBS_MAX + P3_LIMBS_MAX]; + private final byte[] p; + + public MayoPublicKeyParameter(MayoParameters params, byte[] p) + { + super(false, params); + this.p = p; + } + + public byte[] getP() + { + return p; + } + + public byte[] getEncoded() + { + return Arrays.clone(p); + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java new file mode 100644 index 0000000000..d3de862432 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java @@ -0,0 +1,169 @@ +package org.bouncycastle.pqc.crypto.mayo; + +import org.bouncycastle.crypto.digests.SHAKEDigest; +import org.bouncycastle.util.Pack; + +public class Utils +{ + /** + * Decodes an encoded byte array. + * Each byte in the input contains two nibbles (4-bit values); the lower nibble is stored first, + * followed by the upper nibble. + * + * @param m the input byte array (each byte holds two 4-bit values) + * @param mdec the output array that will hold the decoded nibbles (one per byte) + * @param mdecLen the total number of nibbles to decode + */ + public static void decode(byte[] m, byte[] mdec, int mdecLen) + { + int i; + int decIndex = 0; + // Process pairs of nibbles from each byte + for (i = 0; i < mdecLen / 2; i++) + { + // Extract the lower nibble + mdec[decIndex++] = (byte)((m[i] & 0xFF) & 0x0F); + // Extract the upper nibble (shift right 4 bits) + mdec[decIndex++] = (byte)(((m[i] & 0xFF) >> 4) & 0x0F); + } + // If there is an extra nibble (odd number of nibbles), decode only the lower nibble + if (mdecLen % 2 == 1) + { + mdec[decIndex] = (byte)((m[i] & 0xFF) & 0x0F); + } + } + + /** + * Decodes a nibble-packed byte array into an output array. + * + * @param input the input byte array. + * @param inputOffset the offset in input from which to start decoding. + * @param output the output byte array to hold the decoded nibbles. + * @param mdecLen the total number of nibbles to decode. + */ + public static void decode(byte[] input, int inputOffset, byte[] output, int mdecLen) + { + int decIndex = 0; + int blocks = mdecLen / 2; + for (int i = 0; i < blocks; i++) + { + output[decIndex++] = (byte)(input[inputOffset + i] & 0x0F); + output[decIndex++] = (byte)((input[inputOffset + i] >> 4) & 0x0F); + } + if (mdecLen % 2 == 1) + { + output[decIndex] = (byte)(input[inputOffset + blocks] & 0x0F); + } + } + + /** + * Encodes an array of 4-bit values into a byte array. + * Two 4-bit values are packed into one byte, with the first nibble stored in the lower 4 bits + * and the second nibble stored in the upper 4 bits. + * + * @param m the input array of 4-bit values (stored as bytes, only lower 4 bits used) + * @param menc the output byte array that will hold the encoded bytes + * @param mlen the number of nibbles in the input array + */ + public static void encode(byte[] m, byte[] menc, int mlen) + { + int i; + int srcIndex = 0; + // Process pairs of 4-bit values + for (i = 0; i < mlen / 2; i++) + { + int lowerNibble = m[srcIndex] & 0x0F; + int upperNibble = (m[srcIndex + 1] & 0x0F) << 4; + menc[i] = (byte)(lowerNibble | upperNibble); + srcIndex += 2; + } + // If there is an extra nibble (odd number of nibbles), store it directly in lower 4 bits. + if (mlen % 2 == 1) + { + menc[i] = (byte)(m[srcIndex] & 0x0F); + } + } + + /** + * Unpacks m-vectors from a packed byte array into an array of 64-bit limbs. + * + * @param in the input byte array containing packed data + * @param out the output long array where unpacked limbs are stored + * @param vecs the number of vectors + * @param m the m parameter (used to compute m_vec_limbs and copy lengths) + */ + public static void unpackMVecs(byte[] in, long[] out, int vecs, int m) + { + int mVecLimbs = (m + 15) / 16; + int bytesToCopy = m / 2; // Number of bytes to copy per vector + + // Process vectors in reverse order + for (int i = vecs - 1; i >= 0; i--) + { + // Temporary buffer to hold mVecLimbs longs (each long is 8 bytes) + byte[] tmp = new byte[mVecLimbs * 8]; + // Copy m/2 bytes from the input into tmp. The rest remains zero. + System.arraycopy(in, i * bytesToCopy, tmp, 0, bytesToCopy); + + // Convert each 8-byte block in tmp into a long using Pack + for (int j = 0; j < mVecLimbs; j++) + { + out[i * mVecLimbs + j] = Pack.littleEndianToLong(tmp, j * 8); + } + } + } + + /** + * Packs m-vectors from an array of 64-bit limbs into a packed byte array. + * + * @param in the input long array containing the m-vectors + * @param out the output byte array that will contain the packed data + * @param vecs the number of vectors + * @param m the m parameter (used to compute m_vec_limbs and copy lengths) + */ + public static void packMVecs(long[] in, byte[] out, int outOff, int vecs, int m) + { + int mVecLimbs = (m + 15) / 16; + int bytesToCopy = m / 2; // Number of bytes per vector to write + + // Process each vector in order + for (int i = 0; i < vecs; i++) + { + // Temporary buffer to hold the bytes for this vector + byte[] tmp = new byte[mVecLimbs * 8]; + + // Convert each long into 8 bytes using Pack + for (int j = 0; j < mVecLimbs; j++) + { + Pack.longToLittleEndian(in[i * mVecLimbs + j], tmp, j * 8); + } + + // Copy the first m/2 bytes from tmp to the output array + System.arraycopy(tmp, 0, out, i * bytesToCopy + outOff, bytesToCopy); + } + } + + /** + * Computes the SHAKE256 XOF on the given input. + * + * @param output the output buffer that will be filled with the result. + * @param outlen the number of bytes to produce. + * @param input the input byte array. + * @param inlen the number of input bytes. + * @return the number of output bytes produced (equals outlen). + */ + public static int shake256(byte[] output, int outlen, byte[] input, int inlen) + { + // Create a new SHAKE256 digest instance. + SHAKEDigest shake = new SHAKEDigest(256); + + // Absorb the input. + shake.update(input, 0, inlen); + + // Squeeze out outlen bytes into the output array. + shake.doFinal(output, 0, outlen); + + return outlen; + } + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java index f46a283c5d..ff782c74af 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java @@ -25,6 +25,7 @@ import org.bouncycastle.pqc.crypto.lms.Composer; import org.bouncycastle.pqc.crypto.lms.HSSPublicKeyParameters; import org.bouncycastle.pqc.crypto.lms.LMSPublicKeyParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoPublicKeyParameter; import org.bouncycastle.pqc.crypto.mldsa.MLDSAPublicKeyParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMPublicKeyParameters; import org.bouncycastle.pqc.crypto.newhope.NHPublicKeyParameters; @@ -196,7 +197,7 @@ else if (publicKey instanceof SABERPublicKeyParameters) byte[] encoding = params.getEncoded(); AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.saberOidLookup(params.getParameters())); - + return new SubjectPublicKeyInfo(algorithmIdentifier, new DERSequence(new DEROctetString(encoding))); } else if (publicKey instanceof PicnicPublicKeyParameters) @@ -274,7 +275,7 @@ else if (publicKey instanceof MLDSAPublicKeyParameters) } else if (publicKey instanceof BIKEPublicKeyParameters) { - BIKEPublicKeyParameters params = (BIKEPublicKeyParameters) publicKey; + BIKEPublicKeyParameters params = (BIKEPublicKeyParameters)publicKey; byte[] encoding = params.getEncoded(); diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java new file mode 100644 index 0000000000..8f720f844e --- /dev/null +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java @@ -0,0 +1,67 @@ +package org.bouncycastle.pqc.crypto.test; + +import java.io.IOException; + +import junit.framework.TestCase; +import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.pqc.crypto.mayo.MayoKeyGenerationParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoKeyPairGenerator; +import org.bouncycastle.pqc.crypto.mayo.MayoParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoPrivateKeyParameter; +import org.bouncycastle.pqc.crypto.mayo.MayoPublicKeyParameter; + +public class MayoTest + extends TestCase +{ + public static void main(String[] args) + throws Exception + { + MayoTest test = new MayoTest(); + test.testKeyGen(); + } + + private static final MayoParameters[] PARAMETER_SETS = new MayoParameters[] + { + MayoParameters.MAYO1, + MayoParameters.MAYO2, + MayoParameters.MAYO3, + MayoParameters.MAYO5 + }; + + public void testKeyGen() + throws IOException + { + String[] files = new String[]{ + "PQCsignKAT_24_MAYO_1.rsp", + "PQCsignKAT_24_MAYO_2.rsp", + "PQCsignKAT_32_MAYO_3.rsp", + "PQCsignKAT_40_MAYO_5.rsp", + }; + TestUtils.testKeyGen(false, "pqc/crypto/mayo", files, new TestUtils.KeyGenerationOperation() + { + @Override + public AsymmetricCipherKeyPairGenerator getAsymmetricCipherKeyPairGenerator(int fileIndex, byte[] seed) + { + NISTSecureRandom random = new NISTSecureRandom(seed, null); + MayoParameters parameters = PARAMETER_SETS[fileIndex]; + + MayoKeyPairGenerator kpGen = new MayoKeyPairGenerator(); + kpGen.init(new MayoKeyGenerationParameters(random, parameters)); + return kpGen; + } + + @Override + public byte[] getPublicKeyEncoded(AsymmetricKeyParameter pubParams) + { + return ((MayoPublicKeyParameter)pubParams).getEncoded(); + } + + @Override + public byte[] getPrivateKeyEncoded(AsymmetricKeyParameter privParams) + { + return ((MayoPrivateKeyParameter)privParams).getEncoded(); + } + }); + } +} diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java index 743f936caf..966b7bf455 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java @@ -1,9 +1,102 @@ package org.bouncycastle.pqc.crypto.test; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.security.SecureRandom; +import java.util.HashMap; + +import junit.framework.Assert; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.pqc.crypto.util.PrivateKeyFactory; +import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory; +import org.bouncycastle.pqc.crypto.util.PublicKeyFactory; +import org.bouncycastle.pqc.crypto.util.SubjectPublicKeyInfoFactory; +import org.bouncycastle.test.TestResourceFinder; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.FixedSecureRandom; + class TestUtils { static boolean parseBoolean(String value) { return "true".equalsIgnoreCase(value); } + + public interface KeyGenerationOperation + { + AsymmetricCipherKeyPairGenerator getAsymmetricCipherKeyPairGenerator(int fileIndex, byte[] seed); + + byte[] getPublicKeyEncoded(AsymmetricKeyParameter pubParams); + + byte[] getPrivateKeyEncoded(AsymmetricKeyParameter privParams); + } + + public static void testKeyGen(boolean enableFactory, String homeDir, String[] files, KeyGenerationOperation operation) + throws IOException + { + for (int fileIndex = 0; fileIndex != files.length; fileIndex++) + { + String name = files[fileIndex]; + InputStream src = TestResourceFinder.findTestResource(homeDir, name); + BufferedReader bin = new BufferedReader(new InputStreamReader(src)); + + String line; + HashMap buf = new HashMap(); + while ((line = bin.readLine()) != null) + { + line = line.trim(); + + if (line.startsWith("#")) + { + continue; + } + if (line.length() == 0) + { + if (buf.size() > 0) + { + byte[] seed = Hex.decode((String)buf.get("seed")); + byte[] pk = Hex.decode((String)buf.get("pk")); + byte[] sk = Hex.decode((String)buf.get("sk")); + + AsymmetricCipherKeyPairGenerator kpGen = operation.getAsymmetricCipherKeyPairGenerator(fileIndex, seed); + + // + // Generate keys and test. + // + AsymmetricCipherKeyPair kp = kpGen.generateKeyPair(); + AsymmetricKeyParameter pubParams, privParams; + if (enableFactory) + { + pubParams = PublicKeyFactory.createKey( + SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(kp.getPublic())); + privParams = PrivateKeyFactory.createKey( + PrivateKeyInfoFactory.createPrivateKeyInfo(kp.getPrivate())); + } + else + { + pubParams = kp.getPublic(); + privParams = kp.getPrivate(); + } + + Assert.assertTrue(name + ": public key", Arrays.areEqual(pk, operation.getPublicKeyEncoded(pubParams))); + Assert.assertTrue(name + ": secret key", Arrays.areEqual(sk, operation.getPrivateKeyEncoded(privParams))); + } + buf.clear(); + continue; + } + + int a = line.indexOf("="); + if (a > -1) + { + buf.put(line.substring(0, a).trim(), line.substring(a + 1).trim()); + } + } + } + } } From bab5138c12c95c1f6881637e058037b0903d7f39 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 28 Feb 2025 11:41:18 +0700 Subject: [PATCH 137/890] Migrate tls test data to bc-test-data --- .../bouncycastle/test/TestResourceFinder.java | 39 ++++ .../bouncycastle/tls/test/TlsTestUtils.java | 3 +- .../org/bouncycastle/tls/test/README.txt | 181 ------------------ .../org/bouncycastle/tls/test/ca.tmpl | 4 - .../bouncycastle/tls/test/client_agree.tmpl | 4 - .../org/bouncycastle/tls/test/client_enc.tmpl | 4 - .../bouncycastle/tls/test/client_sign.tmpl | 4 - .../bouncycastle/tls/test/server_agree.tmpl | 4 - .../org/bouncycastle/tls/test/server_enc.tmpl | 4 - .../bouncycastle/tls/test/server_sign.tmpl | 4 - .../org/bouncycastle/tls/test/x509-ca-dsa.pem | 26 --- .../bouncycastle/tls/test/x509-ca-ecdsa.pem | 11 -- .../bouncycastle/tls/test/x509-ca-ed25519.pem | 9 - .../bouncycastle/tls/test/x509-ca-ed448.pem | 11 -- .../bouncycastle/tls/test/x509-ca-key-dsa.pem | 15 -- .../tls/test/x509-ca-key-ecdsa.pem | 6 - .../tls/test/x509-ca-key-ed25519.pem | 25 --- .../tls/test/x509-ca-key-ed448.pem | 28 --- .../bouncycastle/tls/test/x509-ca-key-rsa.pem | 28 --- .../tls/test/x509-ca-key-rsa_pss_256.pem | 138 ------------- .../tls/test/x509-ca-key-rsa_pss_384.pem | 138 ------------- .../tls/test/x509-ca-key-rsa_pss_512.pem | 138 ------------- .../org/bouncycastle/tls/test/x509-ca-rsa.pem | 19 -- .../tls/test/x509-ca-rsa_pss_256.pem | 22 --- .../tls/test/x509-ca-rsa_pss_384.pem | 22 --- .../tls/test/x509-ca-rsa_pss_512.pem | 22 --- .../bouncycastle/tls/test/x509-client-dsa.pem | 27 --- .../tls/test/x509-client-ecdh.pem | 12 -- .../tls/test/x509-client-ecdsa.pem | 12 -- .../tls/test/x509-client-ed25519.pem | 11 -- .../tls/test/x509-client-ed448.pem | 12 -- .../tls/test/x509-client-key-dsa.pem | 15 -- .../tls/test/x509-client-key-ecdh.pem | 6 - .../tls/test/x509-client-key-ecdsa.pem | 6 - .../tls/test/x509-client-key-ed25519.pem | 25 --- .../tls/test/x509-client-key-ed448.pem | 28 --- .../tls/test/x509-client-key-rsa.pem | 28 --- .../tls/test/x509-client-key-rsa_pss_256.pem | 138 ------------- .../tls/test/x509-client-key-rsa_pss_384.pem | 138 ------------- .../tls/test/x509-client-key-rsa_pss_512.pem | 138 ------------- .../bouncycastle/tls/test/x509-client-rsa.pem | 20 -- .../tls/test/x509-client-rsa_pss_256.pem | 23 --- .../tls/test/x509-client-rsa_pss_384.pem | 23 --- .../tls/test/x509-client-rsa_pss_512.pem | 23 --- .../bouncycastle/tls/test/x509-server-dsa.pem | 27 --- .../tls/test/x509-server-ecdh.pem | 12 -- .../tls/test/x509-server-ecdsa.pem | 12 -- .../tls/test/x509-server-ed25519.pem | 11 -- .../tls/test/x509-server-ed448.pem | 12 -- .../tls/test/x509-server-key-dsa.pem | 15 -- .../tls/test/x509-server-key-ecdh.pem | 6 - .../tls/test/x509-server-key-ecdsa.pem | 6 - .../tls/test/x509-server-key-ed25519.pem | 25 --- .../tls/test/x509-server-key-ed448.pem | 28 --- .../tls/test/x509-server-key-rsa-enc.pem | 28 --- .../tls/test/x509-server-key-rsa-sign.pem | 28 --- .../tls/test/x509-server-key-rsa_pss_256.pem | 138 ------------- .../tls/test/x509-server-key-rsa_pss_384.pem | 138 ------------- .../tls/test/x509-server-key-rsa_pss_512.pem | 138 ------------- .../tls/test/x509-server-rsa-enc.pem | 20 -- .../tls/test/x509-server-rsa-sign.pem | 20 -- .../tls/test/x509-server-rsa_pss_256.pem | 23 --- .../tls/test/x509-server-rsa_pss_384.pem | 23 --- .../tls/test/x509-server-rsa_pss_512.pem | 23 --- 64 files changed, 41 insertions(+), 2286 deletions(-) create mode 100644 tls/src/test/java/org/bouncycastle/test/TestResourceFinder.java delete mode 100755 tls/src/test/resources/org/bouncycastle/tls/test/README.txt delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/ca.tmpl delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/client_agree.tmpl delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/client_enc.tmpl delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/client_sign.tmpl delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/server_agree.tmpl delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/server_enc.tmpl delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/server_sign.tmpl delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-dsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-ecdsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-ed25519.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-ed448.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-dsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-ecdsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-ed25519.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-ed448.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa_pss_256.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa_pss_384.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa_pss_512.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa_pss_256.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa_pss_384.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa_pss_512.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-dsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ecdh.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ecdsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ed25519.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ed448.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-dsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ecdh.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ecdsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ed25519.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ed448.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa_pss_256.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa_pss_384.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa_pss_512.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa_pss_256.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa_pss_384.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa_pss_512.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-dsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ecdh.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ecdsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ed25519.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ed448.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-dsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ecdh.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ecdsa.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ed25519.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ed448.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa-enc.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa-sign.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa_pss_256.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa_pss_384.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa_pss_512.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa-enc.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa-sign.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa_pss_256.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa_pss_384.pem delete mode 100644 tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa_pss_512.pem diff --git a/tls/src/test/java/org/bouncycastle/test/TestResourceFinder.java b/tls/src/test/java/org/bouncycastle/test/TestResourceFinder.java new file mode 100644 index 0000000000..14214bafae --- /dev/null +++ b/tls/src/test/java/org/bouncycastle/test/TestResourceFinder.java @@ -0,0 +1,39 @@ +package org.bouncycastle.test; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; + +public class TestResourceFinder +{ + private static final String dataDirName = "bc-test-data"; + + /** + * We search starting at the working directory looking for the bc-test-data directory. + * + * @throws FileNotFoundException + */ + public static InputStream findTestResource(String homeDir, String fileName) + throws FileNotFoundException + { + String wrkDirName = System.getProperty("user.dir"); + String separator = System.getProperty("file.separator"); + File wrkDir = new File(wrkDirName); + File dataDir = new File(wrkDir, dataDirName); + while (!dataDir.exists() && wrkDirName.length() > 1) + { + wrkDirName = wrkDirName.substring(0, wrkDirName.lastIndexOf(separator)); + wrkDir = new File(wrkDirName); + dataDir = new File(wrkDir, dataDirName); + } + + if (!dataDir.exists()) + { + String ln = System.getProperty("line.separator"); + throw new FileNotFoundException("Test data directory " + dataDirName + " not found." + ln + "Test data available from: https://github.com/bcgit/bc-test-data.git"); + } + + return new FileInputStream(new File(dataDir, homeDir + separator + fileName)); + } +} diff --git a/tls/src/test/java/org/bouncycastle/tls/test/TlsTestUtils.java b/tls/src/test/java/org/bouncycastle/tls/test/TlsTestUtils.java index 918983da2a..84ab7539ee 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/TlsTestUtils.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/TlsTestUtils.java @@ -29,6 +29,7 @@ import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters; import org.bouncycastle.crypto.util.PrivateKeyFactory; +import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.tls.AlertDescription; import org.bouncycastle.tls.Certificate; import org.bouncycastle.tls.CertificateEntry; @@ -493,7 +494,7 @@ else if (EdECObjectIdentifiers.id_Ed448.equals(oid)) static PemObject loadPemResource(String resource) throws IOException { - InputStream s = TlsTestUtils.class.getResourceAsStream(resource); + InputStream s = TestResourceFinder.findTestResource("tls/credentials", resource); PemReader p = new PemReader(new InputStreamReader(s)); PemObject o = p.readPemObject(); p.close(); diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/README.txt b/tls/src/test/resources/org/bouncycastle/tls/test/README.txt deleted file mode 100755 index 538325645f..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/README.txt +++ /dev/null @@ -1,181 +0,0 @@ -# The key and certificate .pem files here were generated using GnuTLS certtool and the accompanying -# template files. (Note that the ed25519 files needed GnuTLS 3.6+, 3.6.12+ for ed448) - -# CA (signing) credentials: - - certtool --generate-privkey --outfile x509-ca-key-dsa.pem \ - --pkcs8 --password '' --dsa --bits 2048 - certtool --generate-self-signed --template ca.tmpl --outfile x509-ca-dsa.pem \ - --load-privkey x509-ca-key-dsa.pem --hash sha256 - - certtool --generate-privkey --outfile x509-ca-key-ecdsa.pem \ - --pkcs8 --password '' --ecdsa --curve secp256r1 - certtool --generate-self-signed --template ca.tmpl --outfile x509-ca-ecdsa.pem \ - --load-privkey x509-ca-key-ecdsa.pem --hash sha256 - - certtool --generate-privkey --outfile x509-ca-key-ed25519.pem \ - --pkcs8 --password '' --key-type=ed25519 - certtool --generate-self-signed --template ca.tmpl --outfile x509-ca-ed25519.pem \ - --load-privkey x509-ca-key-ed25519.pem - - certtool --generate-privkey --outfile x509-ca-key-ed448.pem \ - --pkcs8 --password '' --key-type=ed448 - certtool --generate-self-signed --template ca.tmpl --outfile x509-ca-ed448.pem \ - --load-privkey x509-ca-key-ed448.pem - - certtool --generate-privkey --outfile x509-ca-key-rsa.pem \ - --pkcs8 --password '' --rsa --bits 2048 - certtool --generate-self-signed --template ca.tmpl --outfile x509-ca-rsa.pem \ - --load-privkey x509-ca-key-rsa.pem --hash sha256 - - certtool --generate-privkey --outfile x509-ca-key-rsa_pss_256.pem \ - --pkcs8 --password '' --key-type='rsa-pss' --bits=2048 --hash=sha256 --salt-size=32 - certtool --generate-self-signed --template ca.tmpl --outfile x509-ca-rsa_pss_256.pem \ - --load-privkey x509-ca-key-rsa_pss_256.pem - - certtool --generate-privkey --outfile x509-ca-key-rsa_pss_384.pem \ - --pkcs8 --password '' --key-type='rsa-pss' --bits=2048 --hash=sha384 --salt-size=48 - certtool --generate-self-signed --template ca.tmpl --outfile x509-ca-rsa_pss_384.pem \ - --load-privkey x509-ca-key-rsa_pss_384.pem - - certtool --generate-privkey --outfile x509-ca-key-rsa_pss_512.pem \ - --pkcs8 --password '' --key-type='rsa-pss' --bits=2048 --hash=sha512 --salt-size=64 - certtool --generate-self-signed --template ca.tmpl --outfile x509-ca-rsa_pss_512.pem \ - --load-privkey x509-ca-key-rsa_pss_512.pem - -# Client agreement credentials: - - certtool --generate-privkey --outfile x509-client-key-ecdh.pem \ - --pkcs8 --password '' --ecc --curve secp256r1 - certtool --generate-certificate --template client_agree.tmpl --outfile x509-client-ecdh.pem \ - --load-privkey x509-client-key-ecdh.pem --hash sha256 \ - --load-ca-privkey x509-ca-key-ecdsa.pem --load-ca-certificate x509-ca-ecdsa.pem - -# Client signing credentials: - - certtool --generate-privkey --outfile x509-client-key-dsa.pem \ - --pkcs8 --password '' --dsa --bits 2048 - certtool --generate-certificate --template client_sign.tmpl --outfile x509-client-dsa.pem \ - --load-privkey x509-client-key-dsa.pem --hash sha256 \ - --load-ca-privkey x509-ca-key-dsa.pem --load-ca-certificate x509-ca-dsa.pem - - certtool --generate-privkey --outfile x509-client-key-ecdsa.pem \ - --pkcs8 --password '' --ecdsa --curve secp256r1 - certtool --generate-certificate --template client_sign.tmpl --outfile x509-client-ecdsa.pem \ - --load-privkey x509-client-key-ecdsa.pem --hash sha256 \ - --load-ca-privkey x509-ca-key-ecdsa.pem --load-ca-certificate x509-ca-ecdsa.pem - - certtool --generate-privkey --outfile x509-client-key-ed25519.pem \ - --pkcs8 --password '' --key-type=ed25519 - certtool --generate-certificate --template client_sign.tmpl --outfile x509-client-ed25519.pem \ - --load-privkey x509-client-key-ed25519.pem \ - --load-ca-privkey x509-ca-key-ed25519.pem --load-ca-certificate x509-ca-ed25519.pem - - certtool --generate-privkey --outfile x509-client-key-ed448.pem \ - --pkcs8 --password '' --key-type=ed448 - certtool --generate-certificate --template client_sign.tmpl --outfile x509-client-ed448.pem \ - --load-privkey x509-client-key-ed448.pem \ - --load-ca-privkey x509-ca-key-ed448.pem --load-ca-certificate x509-ca-ed448.pem - - certtool --generate-privkey --outfile x509-client-key-rsa.pem \ - --pkcs8 --password '' --rsa --bits 2048 - certtool --generate-certificate --template client_sign.tmpl --outfile x509-client-rsa.pem \ - --load-privkey x509-client-key-rsa.pem --hash sha256 \ - --load-ca-privkey x509-ca-key-rsa.pem --load-ca-certificate x509-ca-rsa.pem - - certtool --generate-privkey --outfile x509-client-key-rsa_pss_256.pem \ - --pkcs8 --password '' --key-type='rsa-pss' --bits=2048 --hash=sha256 --salt-size=32 - certtool --generate-certificate --template client_sign.tmpl \ - --outfile x509-client-rsa_pss_256.pem \ - --load-privkey x509-client-key-rsa_pss_256.pem \ - --load-ca-privkey x509-ca-key-rsa_pss_256.pem \ - --load-ca-certificate x509-ca-rsa_pss_256.pem - - certtool --generate-privkey --outfile x509-client-key-rsa_pss_384.pem \ - --pkcs8 --password '' --key-type='rsa-pss' --bits=2048 --hash=sha384 --salt-size=48 - certtool --generate-certificate --template client_sign.tmpl \ - --outfile x509-client-rsa_pss_384.pem \ - --load-privkey x509-client-key-rsa_pss_384.pem \ - --load-ca-privkey x509-ca-key-rsa_pss_384.pem \ - --load-ca-certificate x509-ca-rsa_pss_384.pem - - certtool --generate-privkey --outfile x509-client-key-rsa_pss_512.pem \ - --pkcs8 --password '' --key-type='rsa-pss' --bits=2048 --hash=sha512 --salt-size=64 - certtool --generate-certificate --template client_sign.tmpl \ - --outfile x509-client-rsa_pss_512.pem \ - --load-privkey x509-client-key-rsa_pss_512.pem \ - --load-ca-privkey x509-ca-key-rsa_pss_512.pem \ - --load-ca-certificate x509-ca-rsa_pss_512.pem - -# Server agreement credentials: - - certtool --generate-privkey --outfile x509-server-key-ecdh.pem \ - --pkcs8 --password '' --ecc --curve secp256r1 - certtool --generate-certificate --template server_agree.tmpl --outfile x509-server-ecdh.pem \ - --load-privkey x509-server-key-ecdh.pem --hash sha256 \ - --load-ca-privkey x509-ca-key-ecdsa.pem --load-ca-certificate x509-ca-ecdsa.pem - -# Server encryption credentials: - - certtool --generate-privkey --outfile x509-server-key-rsa-enc.pem \ - --pkcs8 --password '' --rsa --bits 2048 - certtool --generate-certificate --outfile x509-server-rsa-enc.pem \ - --load-privkey x509-server-key-rsa-enc.pem --template server_enc.tmpl \ - --load-ca-privkey x509-ca-key-rsa.pem --load-ca-certificate x509-ca-rsa.pem \ - --hash sha256 - -# Server signing credentials: - - certtool --generate-privkey --outfile x509-server-key-dsa.pem \ - --pkcs8 --password '' --dsa --bits 2048 - certtool --generate-certificate --template server_sign.tmpl --outfile x509-server-dsa.pem \ - --load-privkey x509-server-key-dsa.pem --hash sha256 \ - --load-ca-privkey x509-ca-key-dsa.pem --load-ca-certificate x509-ca-dsa.pem - - certtool --generate-privkey --outfile x509-server-key-ecdsa.pem \ - --pkcs8 --password '' --ecdsa --curve secp256r1 - certtool --generate-certificate --template server_sign.tmpl --outfile x509-server-ecdsa.pem \ - --load-privkey x509-server-key-ecdsa.pem --hash sha256 \ - --load-ca-privkey x509-ca-key-ecdsa.pem --load-ca-certificate x509-ca-ecdsa.pem - - certtool --generate-privkey --outfile x509-server-key-ed25519.pem \ - --pkcs8 --password '' --key-type=ed25519 - certtool --generate-certificate --template server_sign.tmpl --outfile x509-server-ed25519.pem \ - --load-privkey x509-server-key-ed25519.pem \ - --load-ca-privkey x509-ca-key-ed25519.pem --load-ca-certificate x509-ca-ed25519.pem - - certtool --generate-privkey --outfile x509-server-key-ed448.pem \ - --pkcs8 --password '' --key-type=ed448 - certtool --generate-certificate --template server_sign.tmpl --outfile x509-server-ed448.pem \ - --load-privkey x509-server-key-ed448.pem \ - --load-ca-privkey x509-ca-key-ed448.pem --load-ca-certificate x509-ca-ed448.pem - - certtool --generate-privkey --outfile x509-server-key-rsa-sign.pem \ - --pkcs8 --password '' --rsa --bits 2048 - certtool --generate-certificate --template server_sign.tmpl --outfile x509-server-rsa-sign.pem \ - --load-privkey x509-server-key-rsa-sign.pem --hash sha256 \ - --load-ca-privkey x509-ca-key-rsa.pem --load-ca-certificate x509-ca-rsa.pem - - certtool --generate-privkey --outfile x509-server-key-rsa_pss_256.pem \ - --pkcs8 --password '' --key-type='rsa-pss' --bits=2048 --hash=sha256 --salt-size=32 - certtool --generate-certificate --template server_sign.tmpl \ - --outfile x509-server-rsa_pss_256.pem \ - --load-privkey x509-server-key-rsa_pss_256.pem \ - --load-ca-privkey x509-ca-key-rsa_pss_256.pem \ - --load-ca-certificate x509-ca-rsa_pss_256.pem - - certtool --generate-privkey --outfile x509-server-key-rsa_pss_384.pem \ - --pkcs8 --password '' --key-type='rsa-pss' --bits=2048 --hash=sha384 --salt-size=48 - certtool --generate-certificate --template server_sign.tmpl \ - --outfile x509-server-rsa_pss_384.pem \ - --load-privkey x509-server-key-rsa_pss_384.pem \ - --load-ca-privkey x509-ca-key-rsa_pss_384.pem \ - --load-ca-certificate x509-ca-rsa_pss_384.pem - - certtool --generate-privkey --outfile x509-server-key-rsa_pss_512.pem \ - --pkcs8 --password '' --key-type='rsa-pss' --bits=2048 --hash=sha512 --salt-size=64 - certtool --generate-certificate --template server_sign.tmpl \ - --outfile x509-server-rsa_pss_512.pem \ - --load-privkey x509-server-key-rsa_pss_512.pem \ - --load-ca-privkey x509-ca-key-rsa_pss_512.pem \ - --load-ca-certificate x509-ca-rsa_pss_512.pem diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/ca.tmpl b/tls/src/test/resources/org/bouncycastle/tls/test/ca.tmpl deleted file mode 100644 index 72e41e69ee..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/ca.tmpl +++ /dev/null @@ -1,4 +0,0 @@ -cn = BouncyCastle TLS Test CA -ca -cert_signing_key -expiration_days = 7301 diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/client_agree.tmpl b/tls/src/test/resources/org/bouncycastle/tls/test/client_agree.tmpl deleted file mode 100644 index 1718041888..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/client_agree.tmpl +++ /dev/null @@ -1,4 +0,0 @@ -cn = BouncyCastle Test Client -tls_www_client -key_agreement -expiration_days = 7300 diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/client_enc.tmpl b/tls/src/test/resources/org/bouncycastle/tls/test/client_enc.tmpl deleted file mode 100644 index 1a6f2efd8d..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/client_enc.tmpl +++ /dev/null @@ -1,4 +0,0 @@ -cn = BouncyCastle Test Client -tls_www_client -encryption_key -expiration_days = 7300 diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/client_sign.tmpl b/tls/src/test/resources/org/bouncycastle/tls/test/client_sign.tmpl deleted file mode 100644 index 5a320b1113..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/client_sign.tmpl +++ /dev/null @@ -1,4 +0,0 @@ -cn = BouncyCastle Test Client -tls_www_client -signing_key -expiration_days = 7300 diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/server_agree.tmpl b/tls/src/test/resources/org/bouncycastle/tls/test/server_agree.tmpl deleted file mode 100644 index 44f12f6509..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/server_agree.tmpl +++ /dev/null @@ -1,4 +0,0 @@ -cn = BouncyCastle Test Server -tls_www_server -key_agreement -expiration_days = 7300 diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/server_enc.tmpl b/tls/src/test/resources/org/bouncycastle/tls/test/server_enc.tmpl deleted file mode 100644 index 4bfc58e073..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/server_enc.tmpl +++ /dev/null @@ -1,4 +0,0 @@ -cn = BouncyCastle Test Server -tls_www_server -encryption_key -expiration_days = 7300 diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/server_sign.tmpl b/tls/src/test/resources/org/bouncycastle/tls/test/server_sign.tmpl deleted file mode 100644 index 1d12addabc..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/server_sign.tmpl +++ /dev/null @@ -1,4 +0,0 @@ -cn = BouncyCastle Test Server -tls_www_server -signing_key -expiration_days = 7300 diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-dsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-dsa.pem deleted file mode 100644 index d9cb013a2a..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-dsa.pem +++ /dev/null @@ -1,26 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEdDCCBBqgAwIBAgIMWYGU7zZX3YAzRZ8VMAsGCWCGSAFlAwQDAjAjMSEwHwYD -VQQDExhCb3VuY3lDYXN0bGUgVExTIFRlc3QgQ0EwHhcNMTcwODAyMDkwMTM1WhcN -MzcwNzI5MDkwMTM1WjAjMSEwHwYDVQQDExhCb3VuY3lDYXN0bGUgVExTIFRlc3Qg -Q0EwggNHMIICOQYHKoZIzjgEATCCAiwCggEBAO5dNk+YfyPOeIK54F9Wg6Zl5+cu -80tSk1fZBSQE1nFBblnGifl39LTmwkHL7XNZmUUFtrScY7Qlk0/cRsEqsl647fAy -XNhqpZAOMPJs4NBx6nzQvKlprxU9JYO5kvTIoJNBR4GCUDxX/DWLffFqvwbBmrZ3 -BgNAHhAHj/wDm0sfxKDXHgfUwmVJgBOne7xwh86SJJojw+MN33ILuSfNFePv2AeQ -kw+bY93tPpouyfskyBLdWs7DeGXmjftFSmpS68j36RAL0+4QZVwDyX/aIwK1bxAH -XLgRnono7NfGxzqlPiQXfb5ewUmZPyjJ3P06WMU3kgj6ZMoyfmJOHCSwjAECIQCg -NiET6OM1ljsc4tvOXtDSyXXqQ4YtRT5o41Gal8kv+QKCAQAbvM2Az1+PzOhrySA5 -PATwWWDMIXlxFx3U1gXIczu4lGGRaoVzfmSGsDZ6A/0j+/ZtmpjGH3B+jb0Rab0n -yZBDUzK+39w+YI7s+K6yGkKlSuyhpB/UokDP0h5Pf6MEVAU2bIcuuWTLMJmleWLw -zHKJTjUDnrC6txeVAbsW+O4l04jMHLTZMXg2OTk9urUtJzeJNkEEMNA8sv7h+yrA -oX2FqvhfDg/oLdfwXQULkKJc/ec6IQaXrtHm+oYwDQxsr9ap4FlET1nz91MMAXon -hB2Yfl7dRKJtKMUKGWGbCliTnJAPUHI1URltnEg387G/YbnnEBUnZlNBJeDrpZFg -+v2ZA4IBBgACggEBAJQi1dv4plwRKrP2fU/76FLcx++60cA4oajNm0f4Vyyaz2xF -7hPiFNj2H0nLNJAtPtdn/wC7KHNWmDx0OiUFIseAqPPoQvfaSrFun1F2iUJhlrKU -FOJ3RC2URoD+M7IbR1SXAyuWOVenhoYyky1mausApUhUjoLO3mMOyBDdXMGWMq7p -SHfyziAnySzwZ2bwOrVz4XL41w4cPbjdEzd4MLIfpPaqqW43y4MNlgLLspm9vBO5 -6Fbq9c0bDUa18jkSi7DU7uH43S9tq3HrPQh127XZ5SstNPU2uV0o+ZXAS8V9sBOM -LGZMjkiQ8bozYtUVAlhLFBnzMB/BOCWxe2yJGSyjQzBBMA8GA1UdEwEB/wQFMAMB -Af8wDwYDVR0PAQH/BAUDAwcEADAdBgNVHQ4EFgQUAOVmarDrz1YtyjvnX4C9cDcX -lagwCwYJYIZIAWUDBAMCA0cAMEQCIFEz1ogza6zKwImQS9tKVlPNjU4QSDEfPjZP -aSwIVMCyAiB+TcGoJvBEAdkzIeQQDdQLldrR+tO/WbayMeqPZHG6ww== ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-ecdsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-ecdsa.pem deleted file mode 100644 index 019ada2707..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-ecdsa.pem +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBgzCCASmgAwIBAgIMWYGU7zek17Bcz64zMAoGCCqGSM49BAMCMCMxITAfBgNV -BAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAeFw0xNzA4MDIwOTAxMzVaFw0z -NzA3MjkwOTAxMzVaMCMxITAfBgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBD -QTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDOjvFecZj9RsxsIZSygfmv/GX9M -oJkQeMk+sI6QRVv7YbzL7QUxKa4gRb2x2e3iKPi+Mi2x2wGAPJajJO6nj8ujQzBB -MA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcEADAdBgNVHQ4EFgQU0ma/ -FGcW5gGlL//B26Xmj0JISecwCgYIKoZIzj0EAwIDSAAwRQIhAJPFYIENHNONgDNy -1565P/2N0TMAkcENL2zyCEDnYVG4AiBiA3BuThR1Rgnvn1qRGaivzoIiMvDVRQb7 -p76OkZ8Igw== ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-ed25519.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-ed25519.pem deleted file mode 100644 index c117a2649f..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-ed25519.pem +++ /dev/null @@ -1,9 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBSjCB/aADAgECAhQ4EsBmtAbEpUqx/8m96XRPinrybTAFBgMrZXAwIzEhMB8G -A1UEAxMYQm91bmN5Q2FzdGxlIFRMUyBUZXN0IENBMB4XDTE4MDgyMzA5Mjk0N1oX -DTM4MDgxOTA5Mjk0N1owIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxlIFRMUyBUZXN0 -IENBMCowBQYDK2VwAyEA8ZleePPCqzYeCARxwHP10sYfQhcCQ4YXjyOxrb53I0qj -QzBBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0PAQH/BAUDAwcEADAdBgNVHQ4EFgQU -6nm+PvoHITBuh26gdYBbNqOczWYwBQYDK2VwA0EAoUgBYkz0bcLAC+kTmbwE05ga -u2SmQtPaiXGykqY+3RhoZVthxQyEzWT0N5KJ322l6mjs0CZRat0ai4hR1Yj7BA== ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-ed448.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-ed448.pem deleted file mode 100644 index e72fcc6d98..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-ed448.pem +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBljCCARagAwIBAgIUWxqMqdy/tO71K3Iz4GYJiWrc42wwBQYDK2VxMCMxITAf -BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAeFw0yMDAyMTMwNzMzMDJa -Fw00MDAyMDkwNzMzMDJaMCMxITAfBgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVz -dCBDQTBDMAUGAytlcQM6AKSetNrq+LCrx62FFnDlmUG/gZUa6LkaIHnfVM3w7/Wl -c1RONMpgGRUeNMqM8YBlgDzUrHjVkHfOAKNDMEEwDwYDVR0TAQH/BAUwAwEB/zAP -BgNVHQ8BAf8EBQMDBwQAMB0GA1UdDgQWBBS+BSv++BEWlTJ53q6rwnDLVnR5JTAF -BgMrZXEDcwCFAtJNOZDtSYHm/Mf/L2PEbXNDDNrieJGWbixz/QXYoXjNBFB0D521 -IGH0o6Gdh0ZaQvdktpXc9wDs9xAbzh/w6+hLROTj8UvxyvPuZbGgVYH/tvE++NH/ -H0EQMi0FnLI/iBGv/bEPm8VJ1e24qEiEAAA= ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-dsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-dsa.pem deleted file mode 100644 index 4c378da7a6..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-dsa.pem +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIICZQIBADCCAjkGByqGSM44BAEwggIsAoIBAQDuXTZPmH8jzniCueBfVoOmZefn -LvNLUpNX2QUkBNZxQW5Zxon5d/S05sJBy+1zWZlFBba0nGO0JZNP3EbBKrJeuO3w -MlzYaqWQDjDybODQcep80Lypaa8VPSWDuZL0yKCTQUeBglA8V/w1i33xar8GwZq2 -dwYDQB4QB4/8A5tLH8Sg1x4H1MJlSYATp3u8cIfOkiSaI8PjDd9yC7knzRXj79gH -kJMPm2Pd7T6aLsn7JMgS3VrOw3hl5o37RUpqUuvI9+kQC9PuEGVcA8l/2iMCtW8Q -B1y4EZ6J6OzXxsc6pT4kF32+XsFJmT8oydz9OljFN5II+mTKMn5iThwksIwBAiEA -oDYhE+jjNZY7HOLbzl7Q0sl16kOGLUU+aONRmpfJL/kCggEAG7zNgM9fj8zoa8kg -OTwE8FlgzCF5cRcd1NYFyHM7uJRhkWqFc35khrA2egP9I/v2bZqYxh9wfo29EWm9 -J8mQQ1Myvt/cPmCO7PiushpCpUrsoaQf1KJAz9IeT3+jBFQFNmyHLrlkyzCZpXli -8MxyiU41A56wurcXlQG7FvjuJdOIzBy02TF4Njk5Pbq1LSc3iTZBBDDQPLL+4fsq -wKF9har4Xw4P6C3X8F0FC5CiXP3nOiEGl67R5vqGMA0MbK/WqeBZRE9Z8/dTDAF6 -J4QdmH5e3USibSjFChlhmwpYk5yQD1ByNVEZbZxIN/Oxv2G55xAVJ2ZTQSXg66WR -YPr9mQQjAiEAnZYHGZPTcKWTzPTfMa5UQkZgqKU1jqyOOWWtHc50fPE= ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-ecdsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-ecdsa.pem deleted file mode 100644 index 42132d48f5..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-ecdsa.pem +++ /dev/null @@ -1,6 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgXP0gFZhM/8/F0imI -9EPD/er+bEFS8m2nY9JzJlricXigCgYIKoZIzj0DAQehRANCAAQzo7xXnGY/UbMb -CGUsoH5r/xl/TKCZEHjJPrCOkEVb+2G8y+0FMSmuIEW9sdnt4ij4vjItsdsBgDyW -oyTup4/L ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-ed25519.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-ed25519.pem deleted file mode 100644 index 027bb835ea..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-ed25519.pem +++ /dev/null @@ -1,25 +0,0 @@ -Public Key Info: - Public Key Algorithm: EdDSA (Ed25519) - Key Security Level: High (256 bits) - -curve: Ed25519 -private key: - a9:19:76:8a:f7:cf:95:c7:e3:94:3c:ce:3c:71:59:44 - 89:08:34:e5:49:0b:0a:44:56:d6:1d:05:f4:af:5d:85 - - -x: - f1:99:5e:78:f3:c2:ab:36:1e:08:04:71:c0:73:f5:d2 - c6:1f:42:17:02:43:86:17:8f:23:b1:ad:be:77:23:4a - - - -Public Key PIN: - pin-sha256:XFgN/8/bw68mn6K+U4zDmMa+HSGjXI0bejzTOxFQf0U= -Public Key ID: - sha256:5c580dffcfdbc3af269fa2be538cc398c6be1d21a35c8d1b7a3cd33b11507f45 - sha1:ea79be3efa0721306e876ea075805b36a39ccd66 - ------BEGIN PRIVATE KEY----- -MC4CAQAwBQYDK2VwBCIEIKkZdor3z5XH45Q8zjxxWUSJCDTlSQsKRFbWHQX0r12F ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-ed448.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-ed448.pem deleted file mode 100644 index 6974289736..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-ed448.pem +++ /dev/null @@ -1,28 +0,0 @@ -Public Key Info: - Public Key Algorithm: EdDSA (Ed448) - Key Security Level: Ultra (456 bits) - -curve: Ed448 -private key: - d8:36:a1:a6:12:8b:25:7a:86:eb:8a:b2:93:4a:71:df - 44:b7:9e:be:59:09:61:58:43:d2:3f:80:04:59:7b:37 - 89:cc:d9:6b:84:26:ae:b9:75:3c:b7:6d:eb:4b:c9:d8 - 36:94:5e:63:44:7b:7f:4b:e0: - -x: - a4:9e:b4:da:ea:f8:b0:ab:c7:ad:85:16:70:e5:99:41 - bf:81:95:1a:e8:b9:1a:20:79:df:54:cd:f0:ef:f5:a5 - 73:54:4e:34:ca:60:19:15:1e:34:ca:8c:f1:80:65:80 - 3c:d4:ac:78:d5:90:77:ce:00: - - -Public Key PIN: - pin-sha256:fnp0p/r6DXkqUgGSXDJih9BAOy/+ImGTT5dCbfH6H4w= -Public Key ID: - sha256:7e7a74a7fafa0d792a5201925c326287d0403b2ffe2261934f97426df1fa1f8c - sha1:be052bfef81116953279deaeabc270cb56747925 - ------BEGIN PRIVATE KEY----- -MEcCAQAwBQYDK2VxBDsEOdg2oaYSiyV6huuKspNKcd9Et56+WQlhWEPSP4AEWXs3 -iczZa4Qmrrl1PLdt60vJ2DaUXmNEe39L4A== ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa.pem deleted file mode 100644 index 9b51726a1a..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDjLlJpiydqUOH2 -IxH7iI9dUzJxvHaknGqXP8lPcxwg5+Q0CRjmv1+Y2KCV+JNj215OW23JNnGguCe/ -xlkboBnOCnDlSLQJp8W1cyarabHUa1VL086nL2kmTRxIElj+91ppQAl+sSB0cgba -/j8KYoO93mZDlXBjrG7FpYn31UNH9H8/wBrGIeSFgTZSHwTlsDhAQyWr5bGK1RgM -NTLl8ZJ8m7I1xUashTTxOkoXd1t3rXqPhTr0LMqKZl+hXD/hDmhUMUkO8kfsTbK3 -gssYuNIT37MDuk4oTfYS3VbdwvjhwycNsEdsTPksLFvB7uIxIttbmpEJfOOs/l3Y -FaPg9793AgMBAAECggEBANR2XtacMFmKmTijZc7y0Pk7tKKP2flq23jmS7QE+FqB -5HcRxvsOIS6F8fEvz1AFObZYZV1XkH75mxsMOgvO+DMsqpaUHuQkxo9CyPhoWcpK -MzQ+Ozc57MHIPdndZuPUmvZx0C9vIeYlOeoW+wgQSBsK4mL0YG6nNdWcUmK4TTr9 -V62ZvqDYoZjtTsAoLkUVJAgByN3XNzc70RQKN5OyCC8iib0JpLEeLdOkGgxWE7/n -KNLZ/9tbK9O4Mn3BWwZsTt8Hve0m/G9CbjlriEz9PYRBEg+JP6FmQIB5l0mjyRMQ -F5uRqiZt6r1uUGwUifNDOqNyJzSolj32u+OG3m8/6qkCgYEA+Tof5Wtuq9Oc5zCF -oVzm3cT1DZy3kJdGZWlMekLC9NMXhnA2huuNkln8Ph2qRx5pxmomRvX8ThaHNUdg -IRjzUomTQ0IWPkn+7TT6CmeSIEOoT4Tyw/xVvBHKm/yHKxHsCkcSIzt1cXS1SGU7 -Dfsy2C+FBjkcW6UBHkNKUMxce/0CgYEA6VrShFfora0fUOibaas/YG7XnqQzFyvw -mcBpqAbUXTneFdyssZDyfmQpE44KCywfPczz+34zIRkjnBX6mLyWx5rINE0KpOBK -uwS2ob1g6J1x6I6btBzIhhgR+Zj9zxhD5f+jYmmBCU3yTvWMXL4c5YsaWvlG8doq -bNo40cQykYMCgYEAmK8pV03n8VClMWWimGbn8Tl2v64hL23d7McD2WsJMSAZq30X -irTIeL60MAHQjd1uA+aIKLUOq3BVySg/FkfI2en61BuqsOJ4US5BeRpWhXmtpXnX -mIYAqEVmEQY2cQZ7yxgbXoZQvv83CHEsKraYQaVrI5Ldcq+17apf3vw0NKkCgYBH -u6WPDT73dIp14qszlnLLAAfEOpGCA/3YJa/U+RR6+/jrG4TyqK4CcGO4ISexO4T3 -CHPP0YGCISozJwZ7wS1QeqIkgbJN8KzIRLCnk4GgwBVt+bifa2Gw5uFPqtoKuVjV -8PmWnPwPkih0YUMel0pmvZYCdTJ70ibMg2CICxnIZQKBgQC7aMgcL5ORB/8fgOdy -g0at1fmz5kKKbv0Z9cmk7trKW67NDM5kwVyFdUuc5Vp7alkRyW4SZPtwpOG+d5Xt -M0DM77Qvom+sIYq5C5eSHPU6py2Ipn5HZzkHRnAM8qw3OrL6+iFklQUKEBXeqVEq -6MfsH8P1/RIRfvcOMhPrNEzpTg== ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa_pss_256.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa_pss_256.pem deleted file mode 100644 index 9243a1e32a..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa_pss_256.pem +++ /dev/null @@ -1,138 +0,0 @@ -Public Key Info: - Public Key Algorithm: RSA-PSS - Hash Algorithm: SHA256 - Salt Length: 32 - Key Security Level: Medium (2048 bits) - -modulus: - 00:b7:b4:9a:4d:2d:3f:08:b8:1e:55:d6:b0:b2:b7:d0 - 33:cc:bb:de:56:24:04:1d:73:ac:6a:32:db:af:26:4f - ef:b1:d6:10:90:79:dc:89:ac:60:e3:94:54:19:04:54 - 17:ea:18:0e:d9:46:be:60:9f:e6:33:ff:6e:60:6a:35 - c6:2d:dd:06:82:d5:80:c9:88:07:2f:27:4b:04:13:ba - 4b:69:92:ce:bc:00:be:42:2c:e2:35:8d:60:11:66:3c - 24:f8:3a:13:ad:49:e7:12:ca:c6:2a:52:84:ed:3d:7d - 1e:06:de:a4:64:d6:17:14:b1:19:63:a0:e1:7c:49:d3 - 41:73:29:f8:b5:75:e0:93:fa:e3:df:3b:82:77:5e:83 - cb:da:52:26:eb:c3:20:13:11:6d:e9:aa:a5:d7:b6:c3 - a1:a5:04:fa:6e:ee:4f:39:52:4d:18:f9:ab:87:4a:4e - d9:34:a7:c4:df:f5:1b:b6:57:71:7f:c3:c8:4f:2c:00 - c0:6e:e8:74:35:e2:71:1f:ef:c6:c1:62:7a:65:e7:c9 - c7:42:14:5f:16:c1:ab:4d:32:e5:e2:7c:38:a2:21:02 - 69:a3:81:f4:c8:78:48:58:0a:5d:99:85:f5:e6:2a:37 - be:23:e5:ff:15:ba:39:42:ac:9d:d0:11:7e:e4:2b:55 - db: - -public exponent: - 01:00:01: - -private exponent: - 00:9a:2e:3e:02:e0:22:b3:52:b4:43:1e:f9:16:46:27 - bc:11:ec:eb:62:28:c0:3b:67:c6:21:2b:a6:2d:8e:5e - 30:b2:75:13:59:ee:ad:25:ef:43:32:3e:5f:86:cf:97 - 34:ab:08:9e:0d:c5:ce:2a:92:89:46:c2:ef:04:84:9f - b5:40:f0:ec:72:0a:77:18:ad:ce:39:c9:24:b0:bb:4d - f3:d5:1b:9d:df:34:50:7a:81:e9:29:41:0a:8c:0f:de - 12:b9:33:25:28:9f:8a:0c:bf:9b:2a:12:2f:f6:5d:51 - 11:4e:7a:b6:46:db:58:6b:c9:67:a1:b2:79:0d:33:78 - d5:5d:69:38:91:d9:f0:37:28:da:67:56:96:dd:77:d4 - f0:cd:33:9a:a3:7d:cc:6a:5c:44:c1:bd:e3:d3:34:71 - 70:b1:ed:67:56:cc:02:28:3b:e2:2b:6d:d0:eb:69:9b - e5:d9:27:e8:fc:2a:03:14:e1:f7:76:3f:b7:a7:87:1c - 78:84:9e:17:60:71:84:b7:67:83:c5:41:27:40:18:96 - 3c:c5:13:9f:ff:fd:3f:ac:30:cd:e5:f7:b5:cb:ac:16 - 39:85:05:c1:59:46:ce:df:e8:67:9a:a2:56:88:73:a8 - 34:23:b2:54:2d:18:2c:c9:16:0c:d0:f6:46:55:9f:2a - 01: - -prime1: - 00:e1:11:b3:58:64:b9:77:3c:b9:f2:95:df:bb:0b:66 - f6:81:31:03:22:cb:eb:5a:a9:38:63:31:ff:f2:75:46 - 9a:dd:be:a7:d0:d4:c8:30:18:bf:6f:bf:c0:01:ba:27 - 0e:34:b2:7f:75:f0:aa:05:69:71:68:03:41:9b:47:5b - 6a:7b:89:ef:f4:e0:84:c0:01:67:5c:46:c6:29:40:3d - 55:15:48:d6:19:e9:22:d2:fd:88:1d:f6:cd:7e:9c:04 - c6:b0:d7:3d:1d:ea:42:44:8c:10:a8:6c:8a:d2:70:6d - b6:fb:9b:1f:ce:d9:ec:3e:1a:3f:ee:02:17:e3:37:74 - 9b: - -prime2: - 00:d0:f3:a9:3a:80:a3:ac:1f:e9:d8:29:f4:2f:1a:7c - d6:61:77:c8:82:cb:fa:64:a0:ce:ef:ec:5c:e9:97:a0 - 1d:28:a6:49:5d:34:59:92:96:c1:b2:c2:14:f5:02:a2 - 2e:b9:ab:df:a7:79:e6:b3:fa:5c:3e:bd:50:b2:e4:c1 - ee:d3:b5:5d:38:a4:f6:d0:81:8a:e1:5a:45:da:19:06 - a3:fa:6a:84:8f:04:f4:56:2a:00:13:2b:76:8d:c1:c8 - d4:eb:42:c0:02:d5:c5:b4:83:7f:36:32:27:93:fc:49 - 11:f4:a3:4d:7d:bd:03:61:e7:40:28:15:10:ad:8f:97 - c1: - -coefficient: - 00:d6:d9:e8:05:2e:4f:3c:27:59:e4:3a:5a:1a:15:59 - 0c:b7:fd:fa:23:ad:c1:64:07:e4:22:ae:35:9b:ac:e8 - 95:34:46:be:38:ba:99:07:4b:52:0f:f2:f9:cd:32:e7 - 99:2d:66:15:f1:f3:51:b5:4e:4a:d5:49:8f:a3:97:43 - ad:60:31:c7:c7:a7:4a:e0:2f:07:fe:40:24:22:5a:4e - 76:ee:ef:df:95:85:e4:5a:81:7a:ab:61:e9:da:51:7e - 2f:dd:d7:83:46:b2:76:13:ef:18:3a:64:45:31:e1:4f - af:6e:f0:6e:34:d2:cf:59:e3:ee:88:f2:22:1e:c8:06 - f6: - -exp1: - 00:96:25:00:c7:cf:2a:0a:e9:70:02:ed:08:bb:f6:f7 - 51:2b:0e:4f:51:3f:48:5a:ca:d8:db:13:d7:f3:1f:59 - 62:a6:db:31:88:96:ea:95:6b:6d:0a:57:98:f7:8d:ff - cf:f2:47:c1:d0:24:24:c8:47:77:68:34:03:e8:5a:ca - 19:57:20:c5:fb:4e:6c:40:ca:ae:f1:58:25:8a:0f:58 - db:11:bf:ed:54:8b:ba:b7:96:7a:df:c2:6d:84:31:00 - de:ab:ca:6a:f3:31:fb:d3:4e:bd:2e:1e:7a:dd:b8:32 - f9:07:10:8d:3f:a9:11:78:bc:7a:39:85:1b:fa:70:5c - 51: - -exp2: - 42:c5:ca:cb:8e:36:3f:98:07:33:73:dc:bb:7c:bc:6e - 09:c1:ac:8a:d7:c2:51:8b:ed:f5:4f:d4:35:35:a6:0e - 0b:62:70:49:5f:a4:4c:2a:ef:05:3f:ee:50:89:a1:e8 - 4a:9f:39:1e:9c:de:f3:9e:cb:01:a5:9f:f7:3b:11:1a - 4f:ff:42:26:0a:d9:70:b2:24:fe:74:c9:a3:b3:a1:a2 - 9f:30:90:e1:df:54:71:80:84:7b:9b:c5:0b:f1:e4:4a - de:4f:7b:6a:ac:83:bc:76:d5:1d:2d:93:e6:3f:95:de - 2e:0e:4d:82:23:f7:c3:be:91:8a:fd:88:51:de:74:41 - - - -Public Key PIN: - pin-sha256:O4J2O7fbEdphitrCWZG7Jyi8t5S4daRsB8YQZknFYWk= -Public Key ID: - sha256:3b82763bb7db11da618adac25991bb2728bcb794b875a46c07c6106649c56169 - sha1:211a88b6de03c00180aadb708899a68f17ceaa6c - ------BEGIN PRIVATE KEY----- -MIIE7wIBADA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEaMBgGCSqGSIb3 -DQEBCDALBglghkgBZQMEAgGiAwIBIASCBKkwggSlAgEAAoIBAQC3tJpNLT8IuB5V -1rCyt9AzzLveViQEHXOsajLbryZP77HWEJB53ImsYOOUVBkEVBfqGA7ZRr5gn+Yz -/25gajXGLd0GgtWAyYgHLydLBBO6S2mSzrwAvkIs4jWNYBFmPCT4OhOtSecSysYq -UoTtPX0eBt6kZNYXFLEZY6DhfEnTQXMp+LV14JP64987gndeg8vaUibrwyATEW3p -qqXXtsOhpQT6bu5POVJNGPmrh0pO2TSnxN/1G7ZXcX/DyE8sAMBu6HQ14nEf78bB -Ynpl58nHQhRfFsGrTTLl4nw4oiECaaOB9Mh4SFgKXZmF9eYqN74j5f8VujlCrJ3Q -EX7kK1XbAgMBAAECggEBAJouPgLgIrNStEMe+RZGJ7wR7OtiKMA7Z8YhK6Ytjl4w -snUTWe6tJe9DMj5fhs+XNKsIng3FziqSiUbC7wSEn7VA8OxyCncYrc45ySSwu03z -1Rud3zRQeoHpKUEKjA/eErkzJSifigy/myoSL/ZdURFOerZG21hryWehsnkNM3jV -XWk4kdnwNyjaZ1aW3XfU8M0zmqN9zGpcRMG949M0cXCx7WdWzAIoO+IrbdDraZvl -2Sfo/CoDFOH3dj+3p4cceISeF2BxhLdng8VBJ0AYljzFE5///T+sMM3l97XLrBY5 -hQXBWUbO3+hnmqJWiHOoNCOyVC0YLMkWDND2RlWfKgECgYEA4RGzWGS5dzy58pXf -uwtm9oExAyLL61qpOGMx//J1RprdvqfQ1MgwGL9vv8ABuicONLJ/dfCqBWlxaANB -m0dbanuJ7/TghMABZ1xGxilAPVUVSNYZ6SLS/Ygd9s1+nATGsNc9HepCRIwQqGyK -0nBttvubH87Z7D4aP+4CF+M3dJsCgYEA0POpOoCjrB/p2Cn0Lxp81mF3yILL+mSg -zu/sXOmXoB0opkldNFmSlsGywhT1AqIuuavfp3nms/pcPr1QsuTB7tO1XTik9tCB -iuFaRdoZBqP6aoSPBPRWKgATK3aNwcjU60LAAtXFtIN/NjInk/xJEfSjTX29A2Hn -QCgVEK2Pl8ECgYEAliUAx88qCulwAu0Iu/b3USsOT1E/SFrK2NsT1/MfWWKm2zGI -luqVa20KV5j3jf/P8kfB0CQkyEd3aDQD6FrKGVcgxftObEDKrvFYJYoPWNsRv+1U -i7q3lnrfwm2EMQDeq8pq8zH70069Lh563bgy+QcQjT+pEXi8ejmFG/pwXFECgYBC -xcrLjjY/mAczc9y7fLxuCcGsitfCUYvt9U/UNTWmDgticElfpEwq7wU/7lCJoehK -nzkenN7znssBpZ/3OxEaT/9CJgrZcLIk/nTJo7Ohop8wkOHfVHGAhHubxQvx5Ere -T3tqrIO8dtUdLZPmP5XeLg5NgiP3w76Riv2IUd50QQKBgQDW2egFLk88J1nkOloa -FVkMt/36I63BZAfkIq41m6zolTRGvji6mQdLUg/y+c0y55ktZhXx81G1TkrVSY+j -l0OtYDHHx6dK4C8H/kAkIlpOdu7v35WF5FqBeqth6dpRfi/d14NGsnYT7xg6ZEUx -4U+vbvBuNNLPWePuiPIiHsgG9g== ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa_pss_384.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa_pss_384.pem deleted file mode 100644 index b83a1059cf..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa_pss_384.pem +++ /dev/null @@ -1,138 +0,0 @@ -Public Key Info: - Public Key Algorithm: RSA-PSS - Hash Algorithm: SHA384 - Salt Length: 48 - Key Security Level: Medium (2048 bits) - -modulus: - 00:ee:6d:c0:c9:44:dd:94:f8:40:af:9a:f4:43:3e:78 - 45:61:5f:6a:15:23:42:f4:f8:a0:c2:eb:6a:0a:ae:0c - 43:a7:dc:e9:ac:ce:42:c4:c8:f5:4c:de:6c:52:4b:e5 - d3:2b:42:b8:f9:e5:5c:5f:5c:53:2f:0f:f7:f5:3d:04 - ed:2b:50:c9:ab:6b:74:2d:4b:42:71:57:7a:04:83:38 - 97:fa:5b:22:4a:14:d2:0d:dd:5c:66:c9:25:7d:e3:ff - 05:84:02:ea:3c:82:97:45:0f:7c:b3:71:8e:7f:31:8d - b5:eb:5e:b6:0d:91:9b:5d:51:bc:f7:e2:81:a2:5f:46 - 35:cf:9e:64:c4:3c:65:63:f6:ff:15:b0:e7:11:49:fa - c1:cc:d9:54:9b:f8:13:3d:95:ce:f4:7f:58:66:23:cd - bc:11:af:e1:3f:af:d8:5e:16:85:cd:7d:b7:7f:59:fa - ff:29:e6:ef:4f:6e:6b:ef:b0:91:ef:81:63:6f:b2:0c - 2b:47:a6:21:f7:1f:4b:fb:1d:e7:6f:f8:6f:0e:6a:8f - 8a:54:5f:4b:a2:6d:36:20:bb:ba:11:87:06:f3:8d:95 - 6a:10:b7:27:8e:1d:02:b4:ce:1e:c2:09:73:c4:b6:5d - c7:5f:e2:26:bd:4f:cd:7a:b0:c5:c0:d6:82:1f:d4:2e - 59: - -public exponent: - 01:00:01: - -private exponent: - 6a:cf:72:10:f8:2f:c7:9f:9a:e2:d0:28:e2:c2:e6:80 - 36:49:d7:2d:16:f9:d4:e2:58:aa:59:69:cc:d5:01:9b - 81:64:9e:ae:12:4c:a8:f9:59:a2:90:f5:b7:bc:56:7d - ce:20:7a:db:40:1b:ac:80:a0:a7:31:a1:24:14:ac:d3 - 4e:97:47:70:ea:97:45:ff:34:09:b0:65:72:06:12:e1 - 4a:7f:6f:11:fe:d7:c6:ec:46:8b:a9:4a:89:66:0d:05 - bc:88:cd:c4:43:c0:5e:68:bc:b5:6a:86:aa:86:59:74 - 88:b7:8a:18:f4:04:c4:be:6c:48:24:09:6c:e2:ff:81 - 18:5f:a8:85:db:79:b0:14:66:1c:fb:f7:8f:a8:29:79 - 83:79:aa:65:45:05:5a:0f:03:30:e5:75:43:23:23:e1 - eb:09:5d:e6:be:0a:10:bd:34:4a:02:d5:60:f6:51:90 - 30:b5:2d:95:37:0e:aa:fb:8e:f3:4f:b4:2f:54:a4:c1 - 8c:a8:5a:46:0c:85:f0:d5:94:d0:ee:71:a2:c6:d7:b0 - de:81:04:66:43:4c:10:ba:ba:ec:91:57:93:bd:74:4f - d4:ea:96:3b:53:a1:0c:0f:be:7d:70:9c:c2:ae:b2:13 - 4b:2b:c3:c9:8a:56:12:a0:ab:55:e4:4e:90:df:0c:01 - - -prime1: - 00:f8:f5:75:8a:5e:fb:0f:2a:09:42:dc:eb:1e:0d:c9 - 97:6f:30:c9:55:28:af:15:4e:4a:db:9e:cd:17:6a:7f - d1:be:1e:53:2b:79:d9:83:de:96:c9:9b:1f:7e:93:ce - 02:f3:fb:9d:56:a2:19:cf:f2:d0:f2:3b:80:5f:b5:7b - cb:df:d6:26:fb:e8:ba:2b:db:e5:c1:47:98:1d:2e:57 - 5d:26:19:72:2f:48:77:00:1d:8d:72:f6:48:68:97:62 - 0f:5d:30:e0:95:a4:00:34:e4:77:b9:47:81:30:63:48 - 29:64:a0:2f:4f:c0:42:4b:bb:a2:60:7b:ba:e4:79:6b - 81: - -prime2: - 00:f5:2c:0d:7a:8c:17:59:ac:95:20:ca:29:3e:45:23 - 23:6f:25:fe:a3:56:df:23:74:47:cd:38:47:a3:4d:06 - 0e:66:7a:9f:74:cc:86:3e:86:e3:c8:4b:45:09:93:2b - 81:d3:fc:56:a2:29:b3:52:4d:f1:45:69:e2:d3:a3:b1 - 4e:1a:25:bd:1a:6c:2b:57:8a:93:11:b9:e9:6d:11:43 - 2c:aa:22:e9:20:10:bf:e4:fe:a6:c5:30:ec:42:5a:5c - d9:3d:1d:e9:18:7e:10:5b:9d:59:cd:7f:7e:51:5c:c8 - 65:63:8b:4a:54:e8:c1:fa:5c:e9:36:23:20:20:87:0e - d9: - -coefficient: - 00:9f:a9:37:76:c0:24:33:8e:18:92:c5:4c:1e:05:fd - 82:39:d9:40:4d:15:95:7e:92:59:44:1a:99:81:4b:0f - 16:e0:16:99:23:45:91:04:76:f0:98:d7:f5:92:e4:af - 39:3b:6f:49:0b:8f:22:02:aa:3a:da:72:04:9c:a4:cb - 11:1a:44:f4:47:19:ed:a0:9c:94:58:06:cd:ca:b1:ff - 48:7f:bf:2e:23:66:63:0e:cd:0b:a5:43:ee:23:75:32 - d5:93:08:23:51:e3:9e:d2:82:be:9b:c4:6d:53:a9:50 - 66:74:af:11:dd:f2:d5:a6:9f:6e:ca:11:ac:6d:a6:ca - 0b: - -exp1: - 00:ba:47:87:9c:72:77:2e:20:88:ef:73:b7:a5:34:31 - cb:d2:91:d1:83:9b:be:6d:95:b8:63:5e:0e:1d:60:3d - a5:a5:b8:b1:08:8d:d2:d8:5d:db:bb:9c:0b:53:bd:aa - 5f:01:4a:1a:af:30:f9:59:64:59:3d:76:92:16:8b:07 - c7:43:83:cc:85:9e:dc:76:66:c2:21:fd:bc:ee:d0:b6 - e3:e6:d7:11:5e:19:bd:98:e3:83:ec:2a:25:81:c5:0b - c5:6d:38:5e:42:f9:84:82:0f:15:1a:18:4b:ac:f6:0c - 8f:94:50:5b:36:34:28:26:dc:8d:a1:dd:d2:b8:93:b5 - 81: - -exp2: - 5c:07:25:28:12:dd:d0:f3:4f:26:f7:bb:73:7c:50:2c - 44:d4:66:38:b9:ab:18:8b:d5:47:db:10:48:e3:e8:9a - 0f:2d:88:1d:37:88:4c:80:25:90:51:70:a0:9f:75:7d - 4e:2d:31:f7:bc:df:6a:cd:86:fb:1f:3b:dd:65:5c:70 - 8c:b0:0d:c3:95:46:cf:9d:5c:87:12:d9:e3:ee:ce:e0 - 3d:1c:cd:95:13:b4:74:28:82:41:12:94:1c:73:fe:d6 - 2c:72:c5:c4:43:cd:b0:15:e8:57:92:bb:bf:9e:ac:3a - 22:9b:6e:53:60:eb:2f:27:21:03:09:3c:4d:f9:64:41 - - - -Public Key PIN: - pin-sha256:wv5nADHzPA1LzmxBzNO9BTLNYGb9g0W8L55XearJ5C8= -Public Key ID: - sha256:c2fe670031f33c0d4bce6c41ccd3bd0532cd6066fd8345bc2f9e5779aac9e42f - sha1:a9f05d6f61a0f001e874b25a7ed411431c2a89e7 - ------BEGIN PRIVATE KEY----- -MIIE7gIBADA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAqEaMBgGCSqGSIb3 -DQEBCDALBglghkgBZQMEAgKiAwIBMASCBKgwggSkAgEAAoIBAQDubcDJRN2U+ECv -mvRDPnhFYV9qFSNC9PigwutqCq4MQ6fc6azOQsTI9UzebFJL5dMrQrj55VxfXFMv -D/f1PQTtK1DJq2t0LUtCcVd6BIM4l/pbIkoU0g3dXGbJJX3j/wWEAuo8gpdFD3yz -cY5/MY216162DZGbXVG89+KBol9GNc+eZMQ8ZWP2/xWw5xFJ+sHM2VSb+BM9lc70 -f1hmI828Ea/hP6/YXhaFzX23f1n6/ynm709ua++wke+BY2+yDCtHpiH3H0v7Hedv -+G8Oao+KVF9Lom02ILu6EYcG842VahC3J44dArTOHsIJc8S2Xcdf4ia9T816sMXA -1oIf1C5ZAgMBAAECggEAas9yEPgvx5+a4tAo4sLmgDZJ1y0W+dTiWKpZaczVAZuB -ZJ6uEkyo+VmikPW3vFZ9ziB620AbrICgpzGhJBSs006XR3Dql0X/NAmwZXIGEuFK -f28R/tfG7EaLqUqJZg0FvIjNxEPAXmi8tWqGqoZZdIi3ihj0BMS+bEgkCWzi/4EY -X6iF23mwFGYc+/ePqCl5g3mqZUUFWg8DMOV1QyMj4esJXea+ChC9NEoC1WD2UZAw -tS2VNw6q+47zT7QvVKTBjKhaRgyF8NWU0O5xosbXsN6BBGZDTBC6uuyRV5O9dE/U -6pY7U6EMD759cJzCrrITSyvDyYpWEqCrVeROkN8MAQKBgQD49XWKXvsPKglC3Ose -DcmXbzDJVSivFU5K257NF2p/0b4eUyt52YPelsmbH36TzgLz+51WohnP8tDyO4Bf -tXvL39Ym++i6K9vlwUeYHS5XXSYZci9IdwAdjXL2SGiXYg9dMOCVpAA05He5R4Ew -Y0gpZKAvT8BCS7uiYHu65HlrgQKBgQD1LA16jBdZrJUgyik+RSMjbyX+o1bfI3RH -zThHo00GDmZ6n3TMhj6G48hLRQmTK4HT/FaiKbNSTfFFaeLTo7FOGiW9GmwrV4qT -EbnpbRFDLKoi6SAQv+T+psUw7EJaXNk9HekYfhBbnVnNf35RXMhlY4tKVOjB+lzp -NiMgIIcO2QKBgQC6R4eccncuIIjvc7elNDHL0pHRg5u+bZW4Y14OHWA9paW4sQiN -0thd27ucC1O9ql8BShqvMPlZZFk9dpIWiwfHQ4PMhZ7cdmbCIf287tC24+bXEV4Z -vZjjg+wqJYHFC8VtOF5C+YSCDxUaGEus9gyPlFBbNjQoJtyNod3SuJO1gQKBgFwH -JSgS3dDzTyb3u3N8UCxE1GY4uasYi9VH2xBI4+iaDy2IHTeITIAlkFFwoJ91fU4t -Mfe832rNhvsfO91lXHCMsA3DlUbPnVyHEtnj7s7gPRzNlRO0dCiCQRKUHHP+1ixy -xcRDzbAV6FeSu7+erDoim25TYOsvJyEDCTxN+WRBAoGBAJ+pN3bAJDOOGJLFTB4F -/YI52UBNFZV+kllEGpmBSw8W4BaZI0WRBHbwmNf1kuSvOTtvSQuPIgKqOtpyBJyk -yxEaRPRHGe2gnJRYBs3Ksf9If78uI2ZjDs0LpUPuI3Uy1ZMII1HjntKCvpvEbVOp -UGZ0rxHd8tWmn27KEaxtpsoL ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa_pss_512.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa_pss_512.pem deleted file mode 100644 index 1bd1bcfc95..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-key-rsa_pss_512.pem +++ /dev/null @@ -1,138 +0,0 @@ -Public Key Info: - Public Key Algorithm: RSA-PSS - Hash Algorithm: SHA512 - Salt Length: 64 - Key Security Level: Medium (2048 bits) - -modulus: - 00:ea:1f:fc:30:5f:ff:19:db:9a:30:46:1b:fa:34:de - 6b:56:44:ff:a1:06:d1:5c:6a:a7:b9:f8:7d:ac:bc:0f - 2b:9c:d8:9b:90:2d:31:a0:41:c7:74:73:01:ba:ea:e4 - c6:9e:bf:9b:41:62:07:54:b4:53:cb:a2:d7:8b:df:3a - a4:6d:b6:95:71:32:21:ee:3c:9a:9d:9b:9b:4d:d5:ef - d3:70:84:ba:c2:0e:a7:2f:07:aa:29:69:20:46:f8:da - 87:49:e0:cf:00:d3:c0:26:a0:71:35:00:06:45:56:56 - 8f:93:60:75:42:f5:78:61:ee:ad:fb:d2:38:de:79:2d - 9b:5b:07:e6:2b:be:b5:a8:a7:95:df:9f:2a:1a:9f:62 - 55:26:35:53:88:33:8c:1c:f9:93:e6:ea:45:39:3c:09 - 7a:e1:4a:41:de:a6:38:fd:56:e4:d5:0d:ee:ff:28:81 - dc:b0:ca:a9:84:42:5f:7a:68:72:32:1e:05:4f:07:36 - 58:71:76:a4:c8:b3:d2:18:3e:c8:3d:7b:2e:18:44:8c - dc:df:b4:53:54:72:ee:78:11:68:94:92:f6:de:41:5b - 1c:e8:dc:1b:41:fe:8f:b4:7d:8d:59:56:ed:07:96:20 - bd:0b:18:cb:3f:40:d3:02:62:7a:50:2e:52:66:68:ab - a1: - -public exponent: - 01:00:01: - -private exponent: - 33:87:46:a1:fe:fe:ce:5a:1e:dd:71:10:c7:48:cb:8b - 24:39:9b:69:7d:6e:a6:c0:72:99:e3:af:05:4d:7e:a9 - 42:a4:09:d8:f9:99:6a:84:0f:b9:f9:75:f0:05:b2:c4 - 64:3c:17:97:94:53:b8:b8:d7:98:82:06:9e:aa:4a:e5 - d5:9f:d1:d4:50:0c:57:ba:ce:ec:d1:4a:a5:1e:e8:e1 - c8:69:ee:10:b7:d8:e3:e8:f3:f2:99:48:99:56:3c:02 - 7a:a8:17:e7:3e:b3:93:cc:cc:1d:b6:1b:ab:37:0d:66 - 1c:31:a6:9d:4e:19:68:b4:77:66:6d:26:47:10:b4:90 - 88:f9:af:65:d3:16:07:f2:6b:59:6e:ba:03:20:ba:a5 - 80:87:75:ae:92:42:bd:64:e1:5a:48:7b:73:33:c3:70 - 74:cc:14:05:c0:d2:42:01:7f:82:4e:e0:8b:f9:2b:12 - 4d:71:4f:55:86:fd:28:0a:8f:a6:ad:66:32:4b:5e:f3 - 22:58:b1:09:51:1f:77:b5:d8:76:e9:80:ea:25:24:09 - 2d:70:ef:5d:89:20:7d:49:4b:52:c1:26:36:c4:c3:95 - 38:80:ef:6d:15:43:5e:dc:bd:5e:06:56:42:6a:59:00 - 13:e3:99:3f:b1:c0:a6:e3:d5:fc:72:38:d1:ea:0d:61 - - -prime1: - 00:f1:54:7d:6d:25:84:c2:37:16:bc:18:04:8b:a2:5e - 09:bd:cc:98:4c:44:ef:8a:9f:b4:6a:72:ac:cf:c8:2f - b4:6b:a6:fd:e9:df:6e:98:42:6c:b4:95:b5:72:21:e0 - 21:e9:71:99:c6:8a:66:f5:06:96:35:9f:15:87:46:7a - 66:cd:3d:ba:1e:96:28:35:ab:fd:07:fd:ab:11:31:50 - 93:a6:6c:67:5e:dd:b6:a9:87:f0:9c:c6:46:cb:7d:83 - 5e:58:c6:16:81:03:64:f5:c6:01:a6:e2:54:7c:be:93 - 65:02:66:89:86:e8:c8:56:bd:cb:d0:bc:7e:df:cb:9c - f7: - -prime2: - 00:f8:5b:5f:19:24:20:60:41:4c:86:07:70:c7:2a:7d - dd:98:bc:f5:e8:ba:c3:7b:5b:2c:50:b3:fa:be:17:04 - 27:6e:75:3f:61:4f:3d:11:aa:f2:73:6a:f8:2f:da:59 - 65:b8:60:13:74:0e:d1:7e:01:07:d6:7e:fd:fb:f9:ca - 4f:9c:e9:c5:49:62:a7:99:36:ed:0f:1e:86:cb:e6:d3 - 87:d0:2c:93:3e:d6:b5:59:a6:b4:f3:2d:38:85:dc:ca - 5c:cd:e1:e9:3e:69:c4:04:25:5e:12:16:86:7f:30:3b - 84:63:80:78:77:d4:45:dd:f7:f0:cc:d2:b5:7e:53:ce - 27: - -coefficient: - 54:6b:6a:51:c7:7e:e2:d9:0b:b8:7e:ea:c8:1b:1b:a8 - 97:06:67:87:da:6a:f7:b2:15:7c:ae:91:4a:46:4a:bd - 79:3b:3a:d7:80:c6:95:2d:e7:92:97:76:cf:24:04:09 - e3:5c:11:68:77:0a:67:21:5a:82:57:80:d1:66:42:d5 - a7:03:1a:20:56:90:c2:e6:97:31:21:3e:a0:4f:41:43 - f8:07:41:92:48:39:ac:1c:8d:c1:a6:0d:be:5d:45:96 - 65:4b:b7:31:0c:c8:2d:f0:72:ca:ba:0e:f0:5a:7c:1a - 7e:23:20:98:1e:c0:55:72:f2:18:b3:22:de:ec:47:21 - - -exp1: - 00:90:e0:d8:2b:9e:4a:85:0d:ed:68:1e:43:1c:50:ed - 83:8b:9e:38:10:11:92:7c:f6:43:a9:64:0e:ba:ee:c3 - 34:dd:2b:f3:63:63:ef:51:19:0f:89:9a:16:c3:dd:f2 - 60:69:74:f9:8c:67:aa:47:8f:1c:be:34:33:08:73:17 - 28:80:2e:7e:7d:be:47:85:71:2b:06:91:13:11:cf:39 - 40:6a:b8:c9:95:fa:24:9e:c2:2d:80:f0:c7:af:82:3a - 4b:79:9f:f2:02:a1:b7:0a:95:44:88:9b:77:7d:2c:2b - f0:87:f0:66:bf:c7:1f:fe:73:12:d8:cd:50:9d:a9:ef - 21: - -exp2: - 2f:9e:a5:6f:56:a3:f6:90:ce:b1:6c:3f:cd:90:72:2d - c9:19:82:35:2b:8a:4b:de:c1:72:7f:ef:f5:fe:c7:c7 - 1f:c0:cf:74:43:13:3c:8e:00:8a:ec:d9:c5:a3:22:3d - 04:cb:37:2f:ab:9f:b3:7f:53:17:67:a6:1f:68:57:c8 - 48:17:f2:c2:0d:6e:81:4c:2c:cc:17:58:55:44:5f:0e - cd:75:9e:8e:0f:f1:19:cd:83:28:95:65:1f:15:a4:9f - 82:c2:6c:4c:91:4f:0a:54:77:e3:13:fa:99:ec:8f:9c - e4:cf:3f:4a:0a:a3:92:d9:f5:8b:f0:62:e8:63:fd:45 - - - -Public Key PIN: - pin-sha256:rS98yeSr3aoarf12o4jbIXeADc53+k2lu/+TB71kG0w= -Public Key ID: - sha256:ad2f7cc9e4abddaa1aadfd76a388db2177800dce77fa4da5bbff9307bd641b4c - sha1:a558e1b80dd69fbc34e5e83dd8fa0ca583b1c045 - ------BEGIN PRIVATE KEY----- -MIIE7QIBADA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCA6EaMBgGCSqGSIb3 -DQEBCDALBglghkgBZQMEAgOiAwIBQASCBKcwggSjAgEAAoIBAQDqH/wwX/8Z25ow -Rhv6NN5rVkT/oQbRXGqnufh9rLwPK5zYm5AtMaBBx3RzAbrq5Maev5tBYgdUtFPL -oteL3zqkbbaVcTIh7jyanZubTdXv03CEusIOpy8HqilpIEb42odJ4M8A08AmoHE1 -AAZFVlaPk2B1QvV4Ye6t+9I43nktm1sH5iu+tainld+fKhqfYlUmNVOIM4wc+ZPm -6kU5PAl64UpB3qY4/Vbk1Q3u/yiB3LDKqYRCX3pocjIeBU8HNlhxdqTIs9IYPsg9 -ey4YRIzc37RTVHLueBFolJL23kFbHOjcG0H+j7R9jVlW7QeWIL0LGMs/QNMCYnpQ -LlJmaKuhAgMBAAECggEAM4dGof7+zloe3XEQx0jLiyQ5m2l9bqbAcpnjrwVNfqlC -pAnY+ZlqhA+5+XXwBbLEZDwXl5RTuLjXmIIGnqpK5dWf0dRQDFe6zuzRSqUe6OHI -ae4Qt9jj6PPymUiZVjwCeqgX5z6zk8zMHbYbqzcNZhwxpp1OGWi0d2ZtJkcQtJCI -+a9l0xYH8mtZbroDILqlgId1rpJCvWThWkh7czPDcHTMFAXA0kIBf4JO4Iv5KxJN -cU9Vhv0oCo+mrWYyS17zIlixCVEfd7XYdumA6iUkCS1w712JIH1JS1LBJjbEw5U4 -gO9tFUNe3L1eBlZCalkAE+OZP7HApuPV/HI40eoNYQKBgQDxVH1tJYTCNxa8GASL -ol4JvcyYTETvip+0anKsz8gvtGum/enfbphCbLSVtXIh4CHpcZnGimb1BpY1nxWH -RnpmzT26HpYoNav9B/2rETFQk6ZsZ17dtqmH8JzGRst9g15YxhaBA2T1xgGm4lR8 -vpNlAmaJhujIVr3L0Lx+38uc9wKBgQD4W18ZJCBgQUyGB3DHKn3dmLz16LrDe1ss -ULP6vhcEJ251P2FPPRGq8nNq+C/aWWW4YBN0DtF+AQfWfv37+cpPnOnFSWKnmTbt -Dx6Gy+bTh9Askz7WtVmmtPMtOIXcylzN4ek+acQEJV4SFoZ/MDuEY4B4d9RF3ffw -zNK1flPOJwKBgQCQ4NgrnkqFDe1oHkMcUO2Di544EBGSfPZDqWQOuu7DNN0r82Nj -71EZD4maFsPd8mBpdPmMZ6pHjxy+NDMIcxcogC5+fb5HhXErBpETEc85QGq4yZX6 -JJ7CLYDwx6+COkt5n/ICobcKlUSIm3d9LCvwh/Bmv8cf/nMS2M1QnanvIQKBgC+e -pW9Wo/aQzrFsP82Qci3JGYI1K4pL3sFyf+/1/sfHH8DPdEMTPI4AiuzZxaMiPQTL -Ny+rn7N/Uxdnph9oV8hIF/LCDW6BTCzMF1hVRF8OzXWejg/xGc2DKJVlHxWkn4LC -bEyRTwpUd+MT+pnsj5zkzz9KCqOS2fWL8GLoY/1FAoGAVGtqUcd+4tkLuH7qyBsb -qJcGZ4faaveyFXyukUpGSr15OzrXgMaVLeeSl3bPJAQJ41wRaHcKZyFagleA0WZC -1acDGiBWkMLmlzEhPqBPQUP4B0GSSDmsHI3Bpg2+XUWWZUu3MQzILfByyroO8Fp8 -Gn4jIJgewFVy8hizIt7sRyE= ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa.pem deleted file mode 100644 index ffda4f2e40..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa.pem +++ /dev/null @@ -1,19 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDDzCCAfegAwIBAgIMWYGU7zoymvg+Z63iMA0GCSqGSIb3DQEBCwUAMCMxITAf -BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAeFw0xNzA4MDIwOTAxMzVa -Fw0zNzA3MjkwOTAxMzVaMCMxITAfBgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVz -dCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOMuUmmLJ2pQ4fYj -EfuIj11TMnG8dqScapc/yU9zHCDn5DQJGOa/X5jYoJX4k2PbXk5bbck2caC4J7/G -WRugGc4KcOVItAmnxbVzJqtpsdRrVUvTzqcvaSZNHEgSWP73WmlACX6xIHRyBtr+ -Pwpig73eZkOVcGOsbsWliffVQ0f0fz/AGsYh5IWBNlIfBOWwOEBDJavlsYrVGAw1 -MuXxknybsjXFRqyFNPE6Shd3W3eteo+FOvQsyopmX6FcP+EOaFQxSQ7yR+xNsreC -yxi40hPfswO6TihN9hLdVt3C+OHDJw2wR2xM+SwsW8Hu4jEi21uakQl846z+XdgV -o+D3v3cCAwEAAaNDMEEwDwYDVR0TAQH/BAUwAwEB/zAPBgNVHQ8BAf8EBQMDBwQA -MB0GA1UdDgQWBBQrQlWadXqqzBV+2iw7BZXrvFHaQjANBgkqhkiG9w0BAQsFAAOC -AQEArUVXn2oI5hgSkDwDrhQFijHBT3d37SX0eji//lkLPTHSEXHv+6kAoxVKmSnG -hAAxuLKxtAjNlXi4FAxSpQPX17/199JHUd/Ue63Tetc8DfaFc6MaFNxWkNrY6LUX -bEhbI/vB1kBu7sc8SW7N694WSpe/OmD/lB6GYW6ZV68hrTB0gfmfB6SfcGbQ69ss -YUsNU7Yo1GZnJTz0FZzybjx/T85NnVvpfVqjjaGRFeSva9GAU+5uO5DdbSpbkCcw -6QFFfvcJ7VD6qFqtLG1TfcdOuCaUB8cmDhDtTB/Ax96wGdJvp6ca3YaboZag9HOZ -4csuzHIJQyd5HT6xLbUBeskgWw== ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa_pss_256.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa_pss_256.pem deleted file mode 100644 index 0e5966a77c..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa_pss_256.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDpzCCAl+gAwIBAgIUIyISxx5PRQREJEmN08CNp74twmcwPQYJKoZIhvcNAQEK -MDCgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMC -ASAwIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxlIFRMUyBUZXN0IENBMB4XDTE4MTAy -NDA2MDY0MloXDTM4MTAyMDA2MDY0MlowIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxl -IFRMUyBUZXN0IENBMIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEa -MBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgGiAwIBIAOCAQ8AMIIBCgKCAQEAt7Sa -TS0/CLgeVdawsrfQM8y73lYkBB1zrGoy268mT++x1hCQedyJrGDjlFQZBFQX6hgO -2Ua+YJ/mM/9uYGo1xi3dBoLVgMmIBy8nSwQTuktpks68AL5CLOI1jWARZjwk+DoT -rUnnEsrGKlKE7T19HgbepGTWFxSxGWOg4XxJ00FzKfi1deCT+uPfO4J3XoPL2lIm -68MgExFt6aql17bDoaUE+m7uTzlSTRj5q4dKTtk0p8Tf9Ru2V3F/w8hPLADAbuh0 -NeJxH+/GwWJ6ZefJx0IUXxbBq00y5eJ8OKIhAmmjgfTIeEhYCl2ZhfXmKje+I+X/ -Fbo5Qqyd0BF+5CtV2wIDAQABo0MwQTAPBgNVHRMBAf8EBTADAQH/MA8GA1UdDwEB -/wQFAwMHBAAwHQYDVR0OBBYEFCEaiLbeA8ABgKrbcIiZpo8XzqpsMD0GCSqGSIb3 -DQEBCjAwoA0wCwYJYIZIAWUDBAIBoRowGAYJKoZIhvcNAQEIMAsGCWCGSAFlAwQC -AaIDAgEgA4IBAQCpl2XsRpIAR5cD0YRWOg2hNg1+oMNAj1wtuBOiaD84doN07sFl -ta3lnweoA1rZZNLtAOUu5Fs7FwetzhuA5+iHVRAU8qqoSojP5fZgTriOFK/7rEUu -ZTxL1IzXEuiQO2iCkTXslg5HP5QAsYV4iqGe6erSAgzD5tP2mpgQJudUiuvCdWGz -lgroWjDAklQHIXITCrLgnMieYIbILZXXMZLfIrMfZOPCd7IDPTjb8/k1j3N4Wuld -qZWSPeNhuUUTKniXy4GVBE1obMDyBRarR4jkeeKdsuYbdf0oSTMK/rcuZMrZfjAR -uH3sqZ1Ovw4rbYzjk/ET4QlolFnFexgZ5QcH ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa_pss_384.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa_pss_384.pem deleted file mode 100644 index 5e8bb89007..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa_pss_384.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDpzCCAl+gAwIBAgIUWplyXkxgG/630tCmc0xgpFKshNYwPQYJKoZIhvcNAQEK -MDCgDTALBglghkgBZQMEAgKhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAICogMC -ATAwIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxlIFRMUyBUZXN0IENBMB4XDTE4MTAy -NDA2MDY0MloXDTM4MTAyMDA2MDY0MlowIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxl -IFRMUyBUZXN0IENBMIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAqEa -MBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgKiAwIBMAOCAQ8AMIIBCgKCAQEA7m3A -yUTdlPhAr5r0Qz54RWFfahUjQvT4oMLragquDEOn3OmszkLEyPVM3mxSS+XTK0K4 -+eVcX1xTLw/39T0E7StQyatrdC1LQnFXegSDOJf6WyJKFNIN3VxmySV94/8FhALq -PIKXRQ98s3GOfzGNtetetg2Rm11RvPfigaJfRjXPnmTEPGVj9v8VsOcRSfrBzNlU -m/gTPZXO9H9YZiPNvBGv4T+v2F4Whc19t39Z+v8p5u9PbmvvsJHvgWNvsgwrR6Yh -9x9L+x3nb/hvDmqPilRfS6JtNiC7uhGHBvONlWoQtyeOHQK0zh7CCXPEtl3HX+Im -vU/NerDFwNaCH9QuWQIDAQABo0MwQTAPBgNVHRMBAf8EBTADAQH/MA8GA1UdDwEB -/wQFAwMHBAAwHQYDVR0OBBYEFKnwXW9hoPAB6HSyWn7UEUMcKonnMD0GCSqGSIb3 -DQEBCjAwoA0wCwYJYIZIAWUDBAICoRowGAYJKoZIhvcNAQEIMAsGCWCGSAFlAwQC -AqIDAgEwA4IBAQAwyVTh4HCTN1FnLLY3iUSEWHNqYNCdAmP/qMYDgwZAdS3hRHEt -9mE0a0/52bYEY9QMol4eGIGKjiXuxZpJKjK2WBgnUvyV7fEDcKy8UqB+ULFGNRxR -9tR3MRMSFrQl+W6q2ABPwmqvAtJHSiIRA30ZuwxSyX4ulzxThRYnkfp2eSKKHp2x -J6xUEnN5vnoXvpa1sFccKorPITF0r8OWzIi+GGzHz4Wq0Fqov4TOhnU/u6Xqw0xX -CXshQ0pN6+XWT0Ea0OuZk1/cKy2nyk+oHSmo/HhKJ8lGc3P2dLgCV9jjbM2WYCHs -O2rKlDWMxkd1Zx/I0ZzUI3Km+Qb8a+88KOtg ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa_pss_512.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa_pss_512.pem deleted file mode 100644 index 690d25654a..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-ca-rsa_pss_512.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDpzCCAl+gAwIBAgIUJ6gfVYLgx1UAxuxNNmuo6mUQP/gwPQYJKoZIhvcNAQEK -MDCgDTALBglghkgBZQMEAgOhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIDogMC -AUAwIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxlIFRMUyBUZXN0IENBMB4XDTE4MTAy -NDA2MDY0M1oXDTM4MTAyMDA2MDY0M1owIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxl -IFRMUyBUZXN0IENBMIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCA6Ea -MBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgOiAwIBQAOCAQ8AMIIBCgKCAQEA6h/8 -MF//GduaMEYb+jTea1ZE/6EG0Vxqp7n4fay8Dyuc2JuQLTGgQcd0cwG66uTGnr+b -QWIHVLRTy6LXi986pG22lXEyIe48mp2bm03V79NwhLrCDqcvB6opaSBG+NqHSeDP -ANPAJqBxNQAGRVZWj5NgdUL1eGHurfvSON55LZtbB+YrvrWop5Xfnyoan2JVJjVT -iDOMHPmT5upFOTwJeuFKQd6mOP1W5NUN7v8ogdywyqmEQl96aHIyHgVPBzZYcXak -yLPSGD7IPXsuGESM3N+0U1Ry7ngRaJSS9t5BWxzo3BtB/o+0fY1ZVu0HliC9CxjL -P0DTAmJ6UC5SZmiroQIDAQABo0MwQTAPBgNVHRMBAf8EBTADAQH/MA8GA1UdDwEB -/wQFAwMHBAAwHQYDVR0OBBYEFKVY4bgN1p+8NOXoPdj6DKWDscBFMD0GCSqGSIb3 -DQEBCjAwoA0wCwYJYIZIAWUDBAIDoRowGAYJKoZIhvcNAQEIMAsGCWCGSAFlAwQC -A6IDAgFAA4IBAQAIi+3zyTk8NR5w5a109GHz4wpACxafirJkr6wkAIzzU95OKLiw -fogX/ECW9UaIfJMHrnEADhKcMSRd7HcNpDg/E8GKr4IhtbkFhNjAjE10Ham6fLzw -SgvN34QpDT6iVP83Cx+SzlSDQLjq4es8diZXP39C0T2a9iskDXMF+ZcaZMwd1TLe -MI7HMIiLCoyVP7LOI39e6E0ho0W0FRrMtZuXIIvZjwkb+SGhAmlJiATmpLmtzuoK -oI/UE1gNglJFjA6HWHStHd7hEJrtGHNa061G130kOFGIJhGf8fHiGaWvWYfZ5/jc -egUo5t7rA6wg6KPwA2JjH2jxUp3NUzTRzV72 ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-dsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-dsa.pem deleted file mode 100644 index 782fe4ef98..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-dsa.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEpzCCBE2gAwIBAgIMWYGU8Awsp0jsH7C+MAsGCWCGSAFlAwQDAjAjMSEwHwYD -VQQDExhCb3VuY3lDYXN0bGUgVExTIFRlc3QgQ0EwHhcNMTcwODAyMDkwMTM2WhcN -MzcwNzI4MDkwMTM2WjAjMSEwHwYDVQQDExhCb3VuY3lDYXN0bGUgVGVzdCBDbGll -bnQwggNHMIICOQYHKoZIzjgEATCCAiwCggEBANIzNyBmt0q+w79byPcXWN5ETG80 -4OJFYhOAdUl5gv90ACofvnojmLaOu2AnbwEUVMlV5xBdeuanPl0ESmH+yFoDwwwA -3uNhPutZ91Yml5PPpks48Fj8IV8Mnnp+0LGC1Ug9IgeXkPSrGm1aIJjMNvK3xc7q -MGI8aeL//pFSoH4vNvRqi+QzZBW/FaXeWdRvkT0c1YACvdl0rnF3SPbi7bn45ceG -GEenq6dFjbZZG1e+bZyzd8/UGO01BLZxftG0IiWWqKi1BlP1Lgc8LyUIrza+Ie1a -vLk0jbjuhhQsYebpvnO84uhDOdavpxnHCqcEm8sElRctIFYY1xB3PD3bG8ECIQCI -pXijcj1zNsncR7roSHNeer5B3c1N92F2hTLedYbzwQKCAQB7PCrFF3u7oJq/64EL -8EMlnv81vgWnet/Ux5zzY2OEdtDOOR4YnrWXcs/rsjio/blHTf/CeMX3pTVV8GE3 -f0C4+Hx6mWb+okL7UgEPmen8dGHD//yn3e6wYBG7aqd8DtBkicmgd9eKlY+xgbww -rDN8dz9RbJ9oAOXbzASw1GvLbZ6UWsMTZ7WWoAbLOf+0gfiA+eRjC9x0HbaN0jLy -ztQJhEvWqGSynNfkB2xopRtoyYCvtEVDtQEC9H6PtIG9oKyiwLAtVOmbj6pf1/V3 -ZJJEPiFfKD8CHcS8Z87JXMbjTolIf1A+/Bjn+FKOS2zdIQ+rAOFdptUyU68CWkAV -6trbA4IBBgACggEBAKprryuOBKnrY00s9y+HM6UMvS/exCjzmQrlQ9rQMuwVG+lz -GKKWFu7shIQqneaNNUuNAXRK0NABkPjIBLfvdDmkXweU4/oi2mZQaIUPEWceHrmL -FliI3+VtqC3rPvXzVGvbBdUvqSCJrYD84EK6Ahs3LGIB0OC1Wldi6x8FtKewbn7B -23twnAsvNx4QzFt7W6j05EorWCvs+cwbTttptRiruNcPYLQweMIrcNvzApq+sgoW -traZdTS7rBklrgfi7/4JJNN+jV21XqDQYGugN8PnbORyuETGHEf1wk4HFL7eweHO -4KzTpaNeqsJfilOe9PV1SgDhv9oyKj6957xK1WSjdjB0MAwGA1UdEwEB/wQCMAAw -EwYDVR0lBAwwCgYIKwYBBQUHAwIwDwYDVR0PAQH/BAUDAweAADAdBgNVHQ4EFgQU -iU8ApXTLLe07mPDvg7O9CsNwS0EwHwYDVR0jBBgwFoAUAOVmarDrz1YtyjvnX4C9 -cDcXlagwCwYJYIZIAWUDBAMCA0cAMEQCIGlXUYEccybo2azMk3f6Zl6bpxERr/FU -+/2k+mbBeo7TAiA9/aIWf0bdED8AH0KNKFfcpKMsvnJHTCOPr7fBPU84lQ== ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ecdh.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ecdh.pem deleted file mode 100644 index a3b1b346b9..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ecdh.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBtTCCAVygAwIBAgIMWYGU7ztLkMAhmmRDMAoGCCqGSM49BAMCMCMxITAfBgNV -BAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAeFw0xNzA4MDIwOTAxMzVaFw0z -NzA3MjgwOTAxMzVaMCMxITAfBgNVBAMTGEJvdW5jeUNhc3RsZSBUZXN0IENsaWVu -dDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLRekMsBjYf7GXhy7ZqnX9lAvXyL -sC9VfMUIcfGNluSN2C3O9fjqN5FMary/eU49ij6L2mRUjZz6kQK0ZM68pL6jdjB0 -MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDwYDVR0PAQH/BAUD -AweIADAdBgNVHQ4EFgQU7ZRZoI8czdH0szeb+t3EwaXNUG8wHwYDVR0jBBgwFoAU -0ma/FGcW5gGlL//B26Xmj0JISecwCgYIKoZIzj0EAwIDRwAwRAIgVij5xSPQrUgo -VsbOgVdLGLJeiHo065dtdt87PSCJtusCID+c1QShYOgBkI/Bv3gotVhTP4mtjhp2 -2v442jTclSfT ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ecdsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ecdsa.pem deleted file mode 100644 index 261db1ce00..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ecdsa.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBtTCCAVygAwIBAgIMWYGU8A09KQDW+svJMAoGCCqGSM49BAMCMCMxITAfBgNV -BAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAeFw0xNzA4MDIwOTAxMzZaFw0z -NzA3MjgwOTAxMzZaMCMxITAfBgNVBAMTGEJvdW5jeUNhc3RsZSBUZXN0IENsaWVu -dDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLenUtWqfkGp0iFncfdsvBsfaXHv -Ne5gV7U/zUO0OQ71V1c8WpOx9f0rhpOSCN9GCqQNL3yd+nWf+pu40JMMrZKjdjB0 -MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDwYDVR0PAQH/BAUD -AweAADAdBgNVHQ4EFgQUtK5VEuBZJUDbU8gN9dleZeXDf2swHwYDVR0jBBgwFoAU -0ma/FGcW5gGlL//B26Xmj0JISecwCgYIKoZIzj0EAwIDRwAwRAIgNnj/nlAbCd0R -fCG6n5s5Sdsh4dR7KRhncCj8wjGYZMYCICUXU05FIr3bM9bELX2We3rv1ookK9rC -7ZVjCgt7YbHi ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ed25519.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ed25519.pem deleted file mode 100644 index e419a7b32a..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ed25519.pem +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBfjCCATCgAwIBAgIUO1XUdvEupfCQwrrPHXE28BoO+TswBQYDK2VwMCMxITAf -BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAeFw0xODA4MjMwOTI5NDda -Fw0zODA4MTgwOTI5NDdaMCMxITAfBgNVBAMTGEJvdW5jeUNhc3RsZSBUZXN0IENs -aWVudDAqMAUGAytlcAMhAJe53GEkXEVP7L7FbVZ1BCGvvIZac+6k41CncucJ3BJ2 -o3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMCMA8GA1UdDwEB -/wQFAwMHgAAwHQYDVR0OBBYEFNo2OYJ4FLTBdu5lozbqL2aAXsqzMB8GA1UdIwQY -MBaAFOp5vj76ByEwboduoHWAWzajnM1mMAUGAytlcANBAJrgAof3yetck8DDLNTG -dwLQdCNjEg5/xgvKVcTPuUC4rirgCoYtPY36lfPhd0v5Ev8v1Jjrxshyi/yVCsLb -mQ8= ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ed448.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ed448.pem deleted file mode 100644 index e7442fce53..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-ed448.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIByTCCAUmgAwIBAgIUbl3fESW+2UIW9SzaxnhuhiGygLIwBQYDK2VxMCMxITAf -BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAeFw0yMDAyMTMwNzMzMDJa -Fw00MDAyMDgwNzMzMDJaMCMxITAfBgNVBAMTGEJvdW5jeUNhc3RsZSBUZXN0IENs -aWVudDBDMAUGAytlcQM6AFbp8qV7nVhmDWZUsXTfqU8aOaP4XSmGUnKqdlQPR5O4 -3lMlUukbrclV5v5+b4Z8Qy4Abe+PZU+AgKN2MHQwDAYDVR0TAQH/BAIwADATBgNV -HSUEDDAKBggrBgEFBQcDAjAPBgNVHQ8BAf8EBQMDB4AAMB0GA1UdDgQWBBSlCh5L -f6BeaHQxiEe5B9ZjbbCKODAfBgNVHSMEGDAWgBS+BSv++BEWlTJ53q6rwnDLVnR5 -JTAFBgMrZXEDcwAC3Tugpa+pS0ExsLdh/6GsVAVvhYICLc+tWGExM+f3gzFU7yvO -uM7xwRR78ItbQyRkfLeL+6GYGgDzhjwQ6XzilwGibLtGuooLAMo3uOgW8N4mQslf -GqEW2V3VocPjmNYanAMJnTd68t5Wc0ow153cNgA= ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-dsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-dsa.pem deleted file mode 100644 index fd1a45b906..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-dsa.pem +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIICZAIBADCCAjkGByqGSM44BAEwggIsAoIBAQDSMzcgZrdKvsO/W8j3F1jeRExv -NODiRWITgHVJeYL/dAAqH756I5i2jrtgJ28BFFTJVecQXXrmpz5dBEph/shaA8MM -AN7jYT7rWfdWJpeTz6ZLOPBY/CFfDJ56ftCxgtVIPSIHl5D0qxptWiCYzDbyt8XO -6jBiPGni//6RUqB+Lzb0aovkM2QVvxWl3lnUb5E9HNWAAr3ZdK5xd0j24u25+OXH -hhhHp6unRY22WRtXvm2cs3fP1BjtNQS2cX7RtCIllqiotQZT9S4HPC8lCK82viHt -Wry5NI247oYULGHm6b5zvOLoQznWr6cZxwqnBJvLBJUXLSBWGNcQdzw92xvBAiEA -iKV4o3I9czbJ3Ee66EhzXnq+Qd3NTfdhdoUy3nWG88ECggEAezwqxRd7u6Cav+uB -C/BDJZ7/Nb4Fp3rf1Mec82NjhHbQzjkeGJ61l3LP67I4qP25R03/wnjF96U1VfBh -N39AuPh8eplm/qJC+1IBD5np/HRhw//8p93usGARu2qnfA7QZInJoHfXipWPsYG8 -MKwzfHc/UWyfaADl28wEsNRry22elFrDE2e1lqAGyzn/tIH4gPnkYwvcdB22jdIy -8s7UCYRL1qhkspzX5AdsaKUbaMmAr7RFQ7UBAvR+j7SBvaCsosCwLVTpm4+qX9f1 -d2SSRD4hXyg/Ah3EvGfOyVzG406JSH9QPvwY5/hSjkts3SEPqwDhXabVMlOvAlpA -Fera2wQiAiAFTXpu+duj6u8P3wMsd7fOOStBEV6hPUh+RLdOjGslXw== ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ecdh.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ecdh.pem deleted file mode 100644 index 2a28a9f102..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ecdh.pem +++ /dev/null @@ -1,6 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgAuAT+3hSra1qS+XA -iAUT+W6MSZ2rnoeQGjFe6FX7z8qgCgYIKoZIzj0DAQehRANCAAS0XpDLAY2H+xl4 -cu2ap1/ZQL18i7AvVXzFCHHxjZbkjdgtzvX46jeRTGq8v3lOPYo+i9pkVI2c+pEC -tGTOvKS+ ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ecdsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ecdsa.pem deleted file mode 100644 index d9aceb301e..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ecdsa.pem +++ /dev/null @@ -1,6 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGUAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHoweAIBAQQhAJxpy2FD0ePz3c9j -6fVPTSpHAmdiRIqnskVP0PjiBAEvoAoGCCqGSM49AwEHoUQDQgAEt6dS1ap+QanS -IWdx92y8Gx9pce817mBXtT/NQ7Q5DvVXVzxak7H1/SuGk5II30YKpA0vfJ36dZ/6 -m7jQkwytkg== ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ed25519.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ed25519.pem deleted file mode 100644 index a08f3c44a8..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ed25519.pem +++ /dev/null @@ -1,25 +0,0 @@ -Public Key Info: - Public Key Algorithm: EdDSA (Ed25519) - Key Security Level: High (256 bits) - -curve: Ed25519 -private key: - b6:9d:94:c8:9d:c9:7a:5d:b3:58:92:af:67:8e:c2:bd - 63:c1:81:55:3d:b2:5c:e0:c8:91:f1:40:34:5a:6d:b9 - - -x: - 97:b9:dc:61:24:5c:45:4f:ec:be:c5:6d:56:75:04:21 - af:bc:86:5a:73:ee:a4:e3:50:a7:72:e7:09:dc:12:76 - - - -Public Key PIN: - pin-sha256:aWaJiTGI21SUAEt2VgTiRGAnczFMD8axA/4hrKMpfno= -Public Key ID: - sha256:696689893188db5494004b765604e244602773314c0fc6b103fe21aca3297e7a - sha1:da3639827814b4c176ee65a336ea2f66805ecab3 - ------BEGIN PRIVATE KEY----- -MC4CAQAwBQYDK2VwBCIEILadlMidyXpds1iSr2eOwr1jwYFVPbJc4MiR8UA0Wm25 ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ed448.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ed448.pem deleted file mode 100644 index be3c8638b2..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-ed448.pem +++ /dev/null @@ -1,28 +0,0 @@ -Public Key Info: - Public Key Algorithm: EdDSA (Ed448) - Key Security Level: Ultra (456 bits) - -curve: Ed448 -private key: - b1:9c:73:a3:54:a0:33:05:b1:b0:ad:6f:1f:c0:ad:14 - db:d4:55:bb:eb:fa:d4:33:8c:e5:bc:68:03:d1:ca:25 - 19:75:0f:94:4f:e3:fd:3a:62:b5:77:23:d7:b0:d6:75 - 92:9c:98:86:2a:08:c5:b2:6e: - -x: - 56:e9:f2:a5:7b:9d:58:66:0d:66:54:b1:74:df:a9:4f - 1a:39:a3:f8:5d:29:86:52:72:aa:76:54:0f:47:93:b8 - de:53:25:52:e9:1b:ad:c9:55:e6:fe:7e:6f:86:7c:43 - 2e:00:6d:ef:8f:65:4f:80:80: - - -Public Key PIN: - pin-sha256:4r2UflhSms9yYj4SMc7tE9lkFxXR/ziPw1bhZGfI/qw= -Public Key ID: - sha256:e2bd947e58529acf72623e1231ceed13d9641715d1ff388fc356e16467c8feac - sha1:a50a1e4b7fa05e6874318847b907d6636db08a38 - ------BEGIN PRIVATE KEY----- -MEcCAQAwBQYDK2VxBDsEObGcc6NUoDMFsbCtbx/ArRTb1FW76/rUM4zlvGgD0col -GXUPlE/j/TpitXcj17DWdZKcmIYqCMWybg== ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa.pem deleted file mode 100644 index d2d19bf0eb..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDP8Yo5l5ez1W2c -EgROodwNqRLL+ALvOorSEuKm+xiTbzmdqcMdtvwDhq/yb7GBhpzrFcUOt5hPcQc9 -pYz7Khg4UHiw+wqbV2QHMGGupNs+kvdUndBgjToD7hYlyhV7fUovNrjQeNq+5Bxt -OHsZmO7ilHTKdSMVG2eO5XgZk+1uSOuZStmnMZM0Sum3TxK5zRs2beLf5OhNDqMH -+hJVH9FL4bF9LaxQfTvMrwnIqgEKBlMO29cs1gPW1otQEFD4MefHhjK0YEF3xZ7r -hnnx6lPv5wFQLeaRx1NBqgdPY3L9ZSUkKQzLQgWubbKK1b8vDookicdebSMjTrVX -HPcv6B93AgMBAAECggEAQZZe0cGFwNwdoW9xWlflL43Xduw4CLq/VHlOcfqbCs23 -L4p/F11C6d3Omzotk7wgvGl2aSjxaoUtEn2oFQR29TQ0jSXxd4O98iKJfOtUl80F -I/RO6FVDKkArTioKUpP3FSM+ccrcu/75FF4PPcil+GN43u7JbPvi0wh/tBmbdwAJ -aK2wskjaJAbQz8lnIEVZkz3uYLc32BD2FoCC+HyIPAYFbryus1m+TsXWVk3rDKst -85x4jadS7g3a1OvsDXqj8lHlom9dlxcMqTtSaJr1XJgMwztnc0FVuMOqs0MZpa2U -Mu/0EJQYLW98a1bDkXbLtbXjx2gWpO64gZqQn6aYAQKBgQDfgA2xVv7nmVuOkojJ -rJTS4muPG1BhXY7/MD13eopmjPk8rpVZt97RHfMHp3CCzuQVy9g398kBvCBtO9Ba -3/ab9ZrNMN3Rwm7vwUa4TBOvN17bRSeIPUenJJ42SDM2fmN4m8c91iiSyb9AVPmk -zSksQYtZr/8m9mQ0VOguzOxcAQKBgQDuLmDFSYDl4DO4cOZANpDYk1MkCtrW37xM -IUS6vnPFkRg3t57Og9tddj81VoMNeakGRvWmcDA1yuVdo52pqjlSXzQuSYAh7ZTC -KMzesLt1uilXQW2MNNvmZMjltgkHMiuY1cIvQLFC0o4AI5DLR5+5/w/lm/Qe9pfU -++B3XVVbdwKBgF5Yfu86ixYW/bg8kTOY/6XZ4I+jdxXy2ZdNtNTHzL6nidqc0/zw -ikV6QAoeG3eMgGnXB+nwVlC+Km4SDs0dt8t0LSmrFCgkzJG24/SOYMzZMdib46k2 -PRYIdiTx63R4e+MA12V6DtyP/4TXmh6AYH4HGRz+F1ZKMliI8w42gRwBAoGAN9sG -dJ21LbNzTZikVoC1XSTHhZdKFMPpO409ufF54uYQ4Ngd1N5VLkjRr+d22k0il0dC -ymJa2/KV8WyyR5yUzr1m1kgEVXCKxzGcQcj+XTBoC39belrXCuOtvTkASwC3+qJ+ -ZGhuaXZJOL0ecp18Vrj6+GSnTi+UEa1zyWpI3ycCgYAxJgHa3CGaGqbdzgkRTnWd -FMptvqaNd4POyy/TJ1Jf0ydZwgRpRMZTv8hJKZHh4lUkSOF6bE0qDhrxzUjpI9GT -SGYYX27pfVThoM421S7bvgVmneeLSz1S50Hg0jawHA2ixjne/7kQcMjQzYjjMfDy -FgVOrBIQnpUddGEpu1z5pw== ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa_pss_256.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa_pss_256.pem deleted file mode 100644 index 86f66f7d25..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa_pss_256.pem +++ /dev/null @@ -1,138 +0,0 @@ -Public Key Info: - Public Key Algorithm: RSA-PSS - Hash Algorithm: SHA256 - Salt Length: 32 - Key Security Level: Medium (2048 bits) - -modulus: - 00:d7:7a:3a:01:8b:5e:59:0f:10:7d:95:5c:ce:c6:ea - c8:29:2e:75:6e:fe:6c:f9:83:d5:3e:72:6c:fc:56:95 - 43:d4:62:34:df:05:7c:b3:b3:a6:4a:37:ca:b6:e0:23 - 44:19:fc:2c:ea:cf:fc:f9:26:1a:ac:5a:8d:83:f8:4f - d9:0b:ae:fc:e9:9d:75:37:2d:a4:93:02:5d:69:10:df - b7:7a:4c:b6:05:64:73:46:45:b2:fe:a4:b3:33:9e:68 - 6b:14:35:8e:ee:df:bb:e0:77:23:76:b8:6e:fd:fc:f2 - dd:02:59:5e:dc:a1:b3:5d:93:c6:2c:e2:4a:85:21:7f - ca:59:36:e8:77:dd:fe:cd:fe:f6:b0:96:fe:14:6a:5c - 68:4c:cf:bc:fd:fe:ec:e3:2f:51:10:cf:46:db:1d:3a - 3d:cd:87:ec:67:4a:7d:20:5e:79:6c:ea:d3:24:d3:82 - 05:bc:d9:79:e5:cd:df:6a:bb:62:9b:20:b3:35:34:61 - ae:69:a9:ab:b9:b6:27:ac:7b:33:f5:be:df:9a:b4:12 - 68:06:55:79:b3:ba:52:f0:91:90:69:b1:f1:5a:11:e3 - 8b:de:dd:f1:99:ca:5a:ad:87:ba:65:52:27:a6:07:12 - 60:10:24:93:4b:df:12:db:49:0f:fc:21:ff:9f:e6:92 - 75: - -public exponent: - 01:00:01: - -private exponent: - 3f:9d:f9:84:3d:36:84:ca:ba:ce:a9:0b:76:8d:2a:02 - 20:8e:73:e3:6a:40:98:46:40:ee:27:f0:5f:6b:dc:b3 - e2:ff:7f:a6:9c:c3:1d:77:1b:d0:6b:ba:70:d5:a9:f3 - d0:4c:30:a2:be:f7:6c:43:c0:ba:44:1d:e5:e9:a9:01 - 66:be:aa:32:fa:e7:01:7c:7b:4b:5f:f6:5e:2f:ba:2d - 3c:71:6b:88:1a:09:22:a5:2f:5c:99:19:c3:52:b0:77 - 74:c6:ff:45:2d:4f:15:cd:76:ed:f5:33:e7:cf:07:91 - 12:c1:7a:0d:5f:bc:4a:13:77:fe:06:6d:83:f2:c8:fc - 55:3b:f2:3f:9d:48:4b:b5:1b:22:ad:a5:7f:c6:29:b0 - 05:b8:e2:82:5c:b9:66:48:8e:05:d1:5a:ea:3d:63:c7 - 94:81:0e:c3:0a:33:d2:a8:a5:2f:a2:83:26:59:19:1b - 89:bc:8d:02:e1:c7:0c:53:64:6b:a2:d1:8b:ef:70:d6 - d5:94:bb:df:5b:49:4b:a5:cf:d4:df:0c:0c:ac:ed:8a - 39:61:b8:15:cb:64:b6:02:6f:21:e2:46:d6:c9:d1:d8 - 62:ca:1b:f6:3f:3b:74:c0:5a:ca:cb:a5:c5:aa:80:89 - e2:66:a8:7a:57:61:5e:5d:e3:0c:b2:4f:48:01:18:21 - - -prime1: - 00:e8:cf:45:ff:39:35:03:62:2f:2a:63:b6:14:95:22 - e7:30:6f:a1:d3:65:f1:8c:0e:13:a3:67:a2:1a:86:24 - 70:1c:97:64:15:9b:12:1b:59:66:de:05:42:6e:fe:ee - 77:a0:bd:1e:8c:5b:de:45:c7:4b:05:ff:ce:39:45:ee - 96:15:4b:96:df:b3:20:62:0c:90:bd:f8:1d:0a:ac:83 - 47:34:cb:63:0e:cf:1a:b9:20:62:59:1c:7b:a2:52:b0 - 4d:d7:c3:d5:90:32:9a:a5:3f:26:d6:66:f9:45:16:7d - a4:4f:2e:4d:87:4c:d3:8b:b4:00:5e:d8:5b:29:cc:a3 - 3d: - -prime2: - 00:ec:f0:f9:c9:43:a9:c5:fd:56:bc:57:d2:6d:18:9d - 90:bb:17:74:19:80:1f:e2:21:b9:b6:13:6b:b5:02:62 - b3:51:97:4d:29:dc:85:ee:bf:15:98:f8:21:78:00:0f - 3d:78:94:23:27:2f:52:a8:35:82:02:73:44:21:07:c6 - ab:61:fc:d1:1f:67:8f:43:5a:33:fd:ee:fe:d8:5c:a1 - 1e:b0:7d:6f:0b:6f:d6:47:25:1d:71:5f:b8:77:2f:8f - 4c:6a:0f:8e:03:ce:f3:cd:dc:02:fc:21:c6:ce:10:07 - 0d:ee:1f:d1:82:94:21:41:10:9a:76:62:80:cc:f1:3f - 99: - -coefficient: - 4b:89:17:37:c2:77:4d:99:ec:95:4d:d7:7f:c7:0b:8a - fc:67:ee:59:0d:66:7d:5c:33:36:6d:90:00:6e:3b:d4 - 79:9e:94:05:61:e6:e5:8d:f9:70:3d:7b:4d:be:7d:7f - 0f:ef:e8:e3:93:1e:42:da:d1:9f:12:6b:51:d9:7c:ef - 5b:c6:f1:e9:ab:9f:87:6f:d6:eb:29:4d:51:2b:f9:0d - b7:e5:96:fb:c9:4a:21:a4:b0:4a:af:b3:2b:3c:41:45 - 19:d5:3d:cb:fe:15:4a:f7:a4:52:e6:d6:0c:c9:cc:5b - 62:fd:b0:1d:ed:98:13:0f:9a:27:92:5e:a8:6b:bc:b8 - - -exp1: - 00:8a:f6:c6:32:5d:24:5e:bb:a9:a9:a4:d1:17:a2:19 - ae:64:04:0e:55:50:21:89:57:11:b3:d4:f5:36:dd:e1 - 3c:26:64:db:71:e6:19:3d:c7:f4:96:0c:0f:a6:8f:77 - 2a:63:00:e0:0e:29:fc:18:2c:a8:84:91:37:b8:8a:1c - aa:eb:55:2e:5e:a2:de:6e:88:4f:91:85:5b:58:76:b6 - f9:b6:f2:bc:53:27:9e:2c:e8:be:ab:b0:4b:c0:0d:99 - 7d:2d:90:90:96:bd:0e:00:1b:1d:04:97:7c:ad:17:8a - b1:9c:2d:e8:4b:1d:b9:9c:47:3a:7d:62:a9:af:de:9d - 85: - -exp2: - 6c:52:0a:4f:b9:b0:3e:c4:7f:c7:a0:fa:a1:47:74:99 - 3a:ff:10:e3:ab:90:67:e7:f5:27:c9:1f:1f:64:54:cd - 17:ca:ec:ca:eb:77:0b:5b:ae:3a:fd:8d:07:78:37:7f - 69:c5:87:80:9d:80:d3:47:8b:05:25:bf:0a:be:ac:53 - a3:7b:59:fb:5a:73:c3:5d:d4:91:0d:96:d2:41:1e:a3 - 92:19:f6:0f:2b:74:b1:97:c5:2b:14:90:97:64:55:c5 - a0:63:36:10:85:a7:2e:00:9c:18:ba:34:51:f6:3f:d3 - 5d:7e:8c:60:7e:e9:e8:fd:f7:2f:91:fe:c2:32:b4:59 - - - -Public Key PIN: - pin-sha256:V1OZkBgmgMWlbg+RZ0Dx3sw/b4WHbrN4NHttN6QbsGM= -Public Key ID: - sha256:57539990182680c5a56e0f916740f1decc3f6f85876eb378347b6d37a41bb063 - sha1:028b616b894849cbca9103fcf919fdda20ca101e - ------BEGIN PRIVATE KEY----- -MIIE7QIBADA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEaMBgGCSqGSIb3 -DQEBCDALBglghkgBZQMEAgGiAwIBIASCBKcwggSjAgEAAoIBAQDXejoBi15ZDxB9 -lVzOxurIKS51bv5s+YPVPnJs/FaVQ9RiNN8FfLOzpko3yrbgI0QZ/Czqz/z5Jhqs -Wo2D+E/ZC6786Z11Ny2kkwJdaRDft3pMtgVkc0ZFsv6kszOeaGsUNY7u37vgdyN2 -uG79/PLdAlle3KGzXZPGLOJKhSF/ylk26Hfd/s3+9rCW/hRqXGhMz7z9/uzjL1EQ -z0bbHTo9zYfsZ0p9IF55bOrTJNOCBbzZeeXN32q7YpsgszU0Ya5pqau5tiesezP1 -vt+atBJoBlV5s7pS8JGQabHxWhHji97d8ZnKWq2HumVSJ6YHEmAQJJNL3xLbSQ/8 -If+f5pJ1AgMBAAECggEAP535hD02hMq6zqkLdo0qAiCOc+NqQJhGQO4n8F9r3LPi -/3+mnMMddxvQa7pw1anz0Ewwor73bEPAukQd5empAWa+qjL65wF8e0tf9l4vui08 -cWuIGgkipS9cmRnDUrB3dMb/RS1PFc127fUz588HkRLBeg1fvEoTd/4GbYPyyPxV -O/I/nUhLtRsiraV/ximwBbjigly5ZkiOBdFa6j1jx5SBDsMKM9KopS+igyZZGRuJ -vI0C4ccMU2RrotGL73DW1ZS731tJS6XP1N8MDKztijlhuBXLZLYCbyHiRtbJ0dhi -yhv2Pzt0wFrKy6XFqoCJ4maoeldhXl3jDLJPSAEYIQKBgQDoz0X/OTUDYi8qY7YU -lSLnMG+h02XxjA4To2eiGoYkcByXZBWbEhtZZt4FQm7+7negvR6MW95Fx0sF/845 -Re6WFUuW37MgYgyQvfgdCqyDRzTLYw7PGrkgYlkce6JSsE3Xw9WQMpqlPybWZvlF -Fn2kTy5Nh0zTi7QAXthbKcyjPQKBgQDs8PnJQ6nF/Va8V9JtGJ2Quxd0GYAf4iG5 -thNrtQJis1GXTSnche6/FZj4IXgADz14lCMnL1KoNYICc0QhB8arYfzRH2ePQ1oz -/e7+2FyhHrB9bwtv1kclHXFfuHcvj0xqD44DzvPN3AL8IcbOEAcN7h/RgpQhQRCa -dmKAzPE/mQKBgQCK9sYyXSReu6mppNEXohmuZAQOVVAhiVcRs9T1Nt3hPCZk23Hm -GT3H9JYMD6aPdypjAOAOKfwYLKiEkTe4ihyq61UuXqLebohPkYVbWHa2+bbyvFMn -nizovquwS8ANmX0tkJCWvQ4AGx0El3ytF4qxnC3oSx25nEc6fWKpr96dhQKBgGxS -Ck+5sD7Ef8eg+qFHdJk6/xDjq5Bn5/UnyR8fZFTNF8rsyut3C1uuOv2NB3g3f2nF -h4CdgNNHiwUlvwq+rFOje1n7WnPDXdSRDZbSQR6jkhn2Dyt0sZfFKxSQl2RVxaBj -NhCFpy4AnBi6NFH2P9Ndfoxgfuno/fcvkf7CMrRZAoGAS4kXN8J3TZnslU3Xf8cL -ivxn7lkNZn1cMzZtkABuO9R5npQFYebljflwPXtNvn1/D+/o45MeQtrRnxJrUdl8 -71vG8emrn4dv1uspTVEr+Q235Zb7yUohpLBKr7MrPEFFGdU9y/4VSvekUubWDMnM -W2L9sB3tmBMPmieSXqhrvLg= ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa_pss_384.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa_pss_384.pem deleted file mode 100644 index 5cdedc5ad2..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa_pss_384.pem +++ /dev/null @@ -1,138 +0,0 @@ -Public Key Info: - Public Key Algorithm: RSA-PSS - Hash Algorithm: SHA384 - Salt Length: 48 - Key Security Level: Medium (2048 bits) - -modulus: - 00:dd:07:da:a8:65:55:09:ee:be:0b:c6:9d:95:05:28 - f6:d6:0b:d6:dd:31:7f:e9:b4:3c:53:0b:2d:ef:f1:bf - 92:13:14:9f:09:11:08:a1:d4:49:ff:8a:51:5e:45:62 - e0:63:7e:9d:18:68:bd:93:fe:42:a5:46:de:5b:06:d6 - 90:28:98:10:fd:ef:5d:0b:f9:a4:7c:f9:ce:68:2a:6c - 5d:78:5a:11:a7:d3:77:3a:49:c9:01:73:80:40:50:56 - df:1e:e6:21:d7:7b:9b:14:0b:00:fc:09:f7:97:9a:39 - 50:6d:ea:e9:c1:b1:47:ca:1a:c5:49:79:8e:42:2a:74 - e9:51:81:14:07:f2:51:59:72:b1:de:5a:09:72:de:77 - 90:62:e0:f7:96:64:07:7f:6d:83:c0:ed:82:6f:61:20 - 65:2a:09:ef:bd:8a:66:74:4c:2a:60:63:b9:91:6f:f7 - 55:aa:bd:d4:a0:a1:d5:a2:2f:20:8f:f8:19:45:60:93 - ba:19:2d:b4:eb:cf:8c:ea:1d:3b:2e:1f:99:82:66:79 - cb:c8:1b:1c:b2:a9:1d:bd:67:a0:39:e4:a2:59:14:67 - 69:0e:62:45:53:e2:8b:63:9f:38:cf:c0:dd:d1:c6:76 - b8:a2:87:78:08:31:38:28:e9:cc:25:98:69:fa:20:c8 - f1: - -public exponent: - 01:00:01: - -private exponent: - 54:12:88:b9:44:d1:f3:d5:3b:b4:7e:f7:b1:97:24:dd - be:cd:02:0d:60:a6:a6:de:47:93:ce:cc:ca:57:c9:e6 - 66:1b:91:e2:80:f8:27:95:f8:0f:9b:2d:18:0e:8c:6d - 8d:6a:bb:96:6d:40:ae:ea:27:af:76:25:5d:ba:5c:22 - b9:4e:1e:28:78:c3:8b:aa:89:46:80:3e:62:a0:c0:57 - 4d:4f:f5:27:40:e8:38:e3:97:f1:55:5d:93:18:fb:f2 - 22:6e:a6:b0:af:f3:6c:cc:42:b3:9b:96:f1:b3:57:d9 - 9f:f5:9a:b4:72:1a:3c:65:b2:65:20:37:5c:33:8a:03 - ff:ee:a7:73:42:38:40:0f:3e:af:73:c0:38:b0:21:c0 - 24:30:04:85:d5:01:0e:4e:4b:98:db:19:fb:88:39:de - c1:b0:ca:94:1a:f6:be:8d:c3:bb:b7:10:34:6f:53:c3 - 77:b8:ed:f2:b1:66:8f:f6:6e:a8:b4:d2:70:51:8c:b2 - 27:59:5e:01:9e:7e:b2:4d:a4:2a:7a:09:2f:cb:e7:f7 - d8:dd:a1:f7:97:61:cb:17:2c:5f:02:19:84:1c:54:c8 - 31:e3:50:b0:26:26:4f:7a:d9:c0:fe:4e:7b:b6:7d:b5 - bb:86:d9:67:10:47:7d:62:7b:e4:b4:9a:5e:c9:aa:01 - - -prime1: - 00:dd:5c:98:01:6c:e3:b1:f0:37:8b:d1:37:78:77:9b - 1a:f2:26:c2:b5:8a:58:9b:f0:f2:bb:cc:66:23:ea:8a - 9c:50:62:e6:d6:ea:11:ba:f3:ee:84:fd:9d:45:3e:ca - 55:65:11:46:46:1e:03:58:23:54:44:03:d9:85:50:43 - ac:97:27:bf:e1:5e:a9:17:a9:43:cf:e4:6e:d3:09:0c - 6c:11:74:8c:7d:dd:ed:be:96:bb:5b:d3:b9:c1:83:b3 - a3:70:52:d4:74:1c:d5:78:31:73:2b:1b:c0:dc:28:f8 - 51:55:4a:cf:16:b3:03:b1:58:d2:b7:df:bc:cb:6f:5f - bd: - -prime2: - 00:ff:9e:00:1d:18:dd:09:08:e6:20:10:b5:ea:c9:d5 - b8:17:2c:ce:c7:9c:d1:08:22:95:e0:41:1f:59:3e:2b - 91:07:80:21:7f:61:73:ae:74:24:c1:7d:a6:33:e0:e8 - 20:07:ed:e4:fd:d9:55:65:ae:7b:76:a3:c9:10:35:26 - 47:78:0b:d1:45:a1:31:dc:d7:a3:52:17:24:ff:55:4b - d0:c0:9e:12:73:f5:51:d1:89:ab:75:6a:0b:08:b7:8d - 9a:d3:d6:3b:c3:ee:e3:0c:47:8e:7a:01:4c:57:d2:cc - 7d:b7:bf:3d:02:cf:8e:0a:b0:43:4b:b4:15:d0:aa:17 - 45: - -coefficient: - 00:d2:c8:fc:3a:8c:28:d3:15:6f:0b:7b:51:7f:a9:8b - 3c:f5:ed:f5:6b:d1:d7:e4:e9:c8:46:16:80:1c:f8:78 - fc:10:bf:55:13:67:5a:a2:e6:2b:51:86:ca:d1:53:20 - 1e:e4:f8:82:ca:cd:4a:56:ba:bc:7a:dc:16:ba:16:43 - ca:66:21:f2:73:1b:ca:de:60:95:d1:b8:7b:ad:e1:1d - d5:48:2f:87:83:40:00:a4:ea:ac:2e:4b:a6:c8:b1:4a - 90:16:aa:e5:9c:91:a9:ee:57:ec:5d:13:b6:6d:bc:a6 - ef:b9:1b:b5:7c:44:21:24:06:c7:08:97:16:57:22:5f - d3: - -exp1: - 35:1f:b3:9b:23:f6:c1:0d:55:47:48:be:77:3a:bd:0e - 8a:6e:a2:eb:ce:77:d5:74:cd:cc:24:11:9f:2c:fa:76 - e9:13:d3:32:60:9a:40:b3:a9:da:60:c3:0d:8b:34:23 - aa:4d:aa:ff:c8:d4:24:a2:d5:e6:3c:c6:47:28:2c:15 - 8f:71:0a:ab:9b:7c:19:21:96:14:9e:4d:ba:77:c1:73 - 6d:fc:fa:7a:7a:78:43:f5:08:a1:d0:fe:13:62:f8:09 - 91:3b:4f:a1:4e:0a:2c:fe:31:15:77:63:a1:72:73:a5 - 91:42:92:d0:6f:c5:c3:19:fd:f8:02:c9:dc:48:ae:41 - - -exp2: - 00:b4:33:35:8f:4d:ac:dd:26:a9:dc:a7:0b:28:06:bb - a4:b8:a9:bc:e8:59:b3:be:d1:6a:e9:19:df:b8:b1:2c - 53:64:7f:3e:9e:27:1c:3f:3a:df:82:8c:4a:b3:bd:f4 - c6:47:f0:bc:82:fc:48:c8:92:f5:b4:d0:87:f8:e6:0f - 23:49:0c:c3:ae:1b:92:24:46:dc:7b:0d:97:e6:6c:c2 - 32:da:e7:54:c8:ec:83:8e:7d:48:23:50:eb:90:6c:9d - e6:2d:3a:95:0d:6e:86:1f:6c:fe:93:22:01:28:d4:91 - 96:7b:07:d5:41:fb:01:fe:a4:fd:fc:0b:6b:69:9b:cf - 25: - - -Public Key PIN: - pin-sha256:KV163ICyMOUr6z/cxe1yQ1x2GIwCrmZAG9iOyW244lg= -Public Key ID: - sha256:295d7adc80b230e52beb3fdcc5ed72435c76188c02ae66401bd88ec96db8e258 - sha1:0c3cb2bad4f2115a013b50d9cb9f7ddf183e6012 - ------BEGIN PRIVATE KEY----- -MIIE7gIBADA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAqEaMBgGCSqGSIb3 -DQEBCDALBglghkgBZQMEAgKiAwIBMASCBKgwggSkAgEAAoIBAQDdB9qoZVUJ7r4L -xp2VBSj21gvW3TF/6bQ8Uwst7/G/khMUnwkRCKHUSf+KUV5FYuBjfp0YaL2T/kKl -Rt5bBtaQKJgQ/e9dC/mkfPnOaCpsXXhaEafTdzpJyQFzgEBQVt8e5iHXe5sUCwD8 -CfeXmjlQberpwbFHyhrFSXmOQip06VGBFAfyUVlysd5aCXLed5Bi4PeWZAd/bYPA -7YJvYSBlKgnvvYpmdEwqYGO5kW/3Vaq91KCh1aIvII/4GUVgk7oZLbTrz4zqHTsu -H5mCZnnLyBscsqkdvWegOeSiWRRnaQ5iRVPii2OfOM/A3dHGdriih3gIMTgo6cwl -mGn6IMjxAgMBAAECggEAVBKIuUTR89U7tH73sZck3b7NAg1gpqbeR5POzMpXyeZm -G5HigPgnlfgPmy0YDoxtjWq7lm1Aruonr3YlXbpcIrlOHih4w4uqiUaAPmKgwFdN -T/UnQOg445fxVV2TGPvyIm6msK/zbMxCs5uW8bNX2Z/1mrRyGjxlsmUgN1wzigP/ -7qdzQjhADz6vc8A4sCHAJDAEhdUBDk5LmNsZ+4g53sGwypQa9r6Nw7u3EDRvU8N3 -uO3ysWaP9m6otNJwUYyyJ1leAZ5+sk2kKnoJL8vn99jdofeXYcsXLF8CGYQcVMgx -41CwJiZPetnA/k57tn21u4bZZxBHfWJ75LSaXsmqAQKBgQDdXJgBbOOx8DeL0Td4 -d5sa8ibCtYpYm/Dyu8xmI+qKnFBi5tbqEbrz7oT9nUU+ylVlEUZGHgNYI1REA9mF -UEOslye/4V6pF6lDz+Ru0wkMbBF0jH3d7b6Wu1vTucGDs6NwUtR0HNV4MXMrG8Dc -KPhRVUrPFrMDsVjSt9+8y29fvQKBgQD/ngAdGN0JCOYgELXqydW4FyzOx5zRCCKV -4EEfWT4rkQeAIX9hc650JMF9pjPg6CAH7eT92VVlrnt2o8kQNSZHeAvRRaEx3Nej -Uhck/1VL0MCeEnP1UdGJq3VqCwi3jZrT1jvD7uMMR456AUxX0sx9t789As+OCrBD -S7QV0KoXRQKBgDUfs5sj9sENVUdIvnc6vQ6KbqLrznfVdM3MJBGfLPp26RPTMmCa -QLOp2mDDDYs0I6pNqv/I1CSi1eY8xkcoLBWPcQqrm3wZIZYUnk26d8Fzbfz6enp4 -Q/UIodD+E2L4CZE7T6FOCiz+MRV3Y6Fyc6WRQpLQb8XDGf34AsncSK5BAoGBALQz -NY9NrN0mqdynCygGu6S4qbzoWbO+0WrpGd+4sSxTZH8+niccPzrfgoxKs730xkfw -vIL8SMiS9bTQh/jmDyNJDMOuG5IkRtx7DZfmbMIy2udUyOyDjn1II1DrkGyd5i06 -lQ1uhh9s/pMiASjUkZZ7B9VB+wH+pP38C2tpm88lAoGBANLI/DqMKNMVbwt7UX+p -izz17fVr0dfk6chGFoAc+Hj8EL9VE2daouYrUYbK0VMgHuT4gsrNSla6vHrcFroW -Q8pmIfJzG8reYJXRuHut4R3VSC+Hg0AApOqsLkumyLFKkBaq5ZyRqe5X7F0Ttm28 -pu+5G7V8RCEkBscIlxZXIl/T ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa_pss_512.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa_pss_512.pem deleted file mode 100644 index 82ab5d95bb..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-key-rsa_pss_512.pem +++ /dev/null @@ -1,138 +0,0 @@ -Public Key Info: - Public Key Algorithm: RSA-PSS - Hash Algorithm: SHA512 - Salt Length: 64 - Key Security Level: Medium (2048 bits) - -modulus: - 00:d4:ed:b5:a7:85:5a:ba:9c:02:b8:fc:02:46:1e:e3 - 6c:54:34:72:3c:fc:56:eb:0b:ad:9d:95:83:a3:09:15 - 9d:f1:e7:1a:93:21:3e:b5:b3:64:7f:98:83:97:a2:b7 - ca:02:c4:48:a2:55:3f:29:b8:b1:87:61:6d:72:5f:7a - 9f:20:69:f4:82:2c:7e:48:0d:d6:57:40:92:2c:13:9f - e5:44:75:d4:ad:41:f6:c9:f4:74:c4:31:14:dd:44:8c - ec:25:5b:4f:07:51:bc:4e:21:bd:47:c9:e8:56:b2:1e - f1:5d:e0:56:36:c8:18:18:86:71:41:bb:c2:3c:06:ca - e4:71:f2:cd:ec:9f:09:a0:05:6e:b4:d9:32:65:53:9b - 5a:7a:b9:a2:30:7a:cd:06:a6:47:3f:f4:05:9e:87:b7 - 2f:b2:e0:fc:63:c5:ec:2c:19:ed:32:ad:ee:71:9c:37 - d1:34:f3:aa:1a:ba:ed:cf:40:28:82:43:11:54:83:74 - 42:db:70:f7:58:12:c5:11:af:1a:05:26:24:ef:81:8a - 26:ee:2c:f3:8a:c4:2c:0c:47:cc:76:2b:7a:ce:e0:bb - 80:d2:d0:5e:c8:8f:1d:03:c5:4c:47:1b:7b:90:c4:d3 - 0b:9f:8b:6e:29:bd:ab:25:cd:aa:1e:ad:72:19:02:c0 - 11: - -public exponent: - 01:00:01: - -private exponent: - 01:23:1b:db:3f:2d:12:de:0e:6d:aa:7a:e0:a0:fd:99 - f0:81:2f:33:00:2d:fe:a7:5b:50:02:22:67:d6:7e:95 - 0f:5b:aa:9a:aa:8c:c9:2f:a2:13:c4:5e:bf:8a:90:ec - b5:43:13:18:3a:d8:51:82:b8:ff:fe:17:35:8b:28:fe - 7c:8f:d4:4c:75:ac:5e:fa:23:f0:e7:59:60:7e:e2:55 - b9:1d:df:fa:e4:e5:4a:82:d1:b4:d2:86:48:00:3b:b8 - 6f:22:a3:b3:68:4e:57:24:7a:fc:4d:29:be:7c:c9:09 - 84:f4:d3:c1:0b:24:85:cd:02:01:d5:dc:dd:b1:33:98 - 2e:3c:e5:7c:69:e2:e7:e4:02:83:b5:e8:d0:05:c5:cd - 5b:8e:72:f7:ee:b2:d4:11:15:85:b3:b3:4f:ac:cf:77 - 81:73:68:a9:70:fc:b3:94:24:f9:77:f5:38:4f:af:ab - cb:4e:7f:c2:79:76:87:f9:0f:a3:3b:5c:95:61:64:11 - 3a:40:98:28:51:86:48:11:41:30:e2:1a:94:94:06:d0 - 0a:15:de:19:13:9f:f3:06:b8:03:68:8f:87:b2:3a:4f - f3:75:bc:f7:5a:e4:1e:b4:49:29:09:da:57:e4:43:ea - 96:bd:74:e3:f3:38:5a:bd:b3:da:cc:09:99:f6:09:19 - - -prime1: - 00:f0:d9:0a:ee:11:50:da:c9:01:1f:5c:3d:c8:82:3b - 1c:0c:27:17:80:69:fa:d1:9d:ca:d2:7f:12:90:e9:a8 - e5:36:f0:b7:8f:8e:90:f0:0a:78:53:0f:93:51:f0:f4 - 72:a3:1a:de:ea:0f:5e:8f:84:c3:57:39:38:93:73:cf - 94:6e:1a:56:ef:36:d9:22:39:75:76:5b:4e:f6:54:e7 - 02:32:9f:54:5f:1b:02:37:50:f0:49:1b:e0:e6:d1:bd - b8:07:9b:7f:15:fd:ea:53:08:59:e8:17:66:d5:10:e4 - a2:f1:3e:c3:c8:7e:68:a2:a1:5f:04:f1:7b:a6:c5:3c - 73: - -prime2: - 00:e2:53:03:dd:5d:f1:fc:27:a6:d7:01:b1:5e:f3:26 - 5d:9e:fc:f2:45:85:4b:10:86:97:a5:9d:2b:19:3b:35 - 8e:91:36:67:50:d0:da:16:de:c7:13:99:76:b8:9b:2f - 44:fc:6b:c3:29:ec:a7:11:38:05:de:3d:2e:85:1a:49 - 88:28:b6:e0:1b:8f:6a:0a:21:56:ec:ee:56:34:b5:20 - e6:a2:c0:b0:08:c1:48:13:60:e3:65:b1:4a:b9:6c:9b - a4:63:92:2f:5a:e6:6d:80:2a:c2:97:53:87:05:dd:08 - 1d:98:52:c9:88:a9:d3:5d:18:d7:2b:5e:0c:63:e0:94 - 6b: - -coefficient: - 00:8e:60:92:ac:a2:02:d9:67:c7:c5:70:13:d2:b6:2f - 5d:b2:a1:68:0e:46:db:33:f6:8a:a4:bd:06:07:db:5b - 39:62:14:e6:81:95:32:ca:89:b7:dd:87:60:bd:98:33 - 5d:a8:21:25:d9:64:fb:5f:52:a5:28:e8:05:e2:7b:5d - 43:ae:fd:d2:98:fe:20:9e:4e:65:96:11:c4:ae:91:a7 - 36:cb:3d:8d:fe:29:7c:91:bf:8b:45:17:f6:7c:29:41 - 1a:4b:87:85:c2:95:96:d5:8d:c7:5b:7a:29:6e:c3:6a - c6:6f:38:55:f7:9f:aa:27:c4:a7:18:8c:42:f1:b2:94 - 9d: - -exp1: - 7c:76:ed:9b:11:ff:c2:d0:d5:6f:ab:6f:92:4b:1a:d8 - e7:be:db:fa:54:ca:75:c1:21:ab:9e:57:ad:e3:d2:90 - 81:cf:ec:4c:97:d4:76:f8:32:2e:5a:82:3b:7a:56:19 - 58:08:ee:e1:ee:87:63:8b:ac:97:4a:ce:de:04:9f:65 - 89:70:bb:34:6c:17:d2:03:f7:9b:ee:9b:e3:d9:04:78 - b2:48:7c:85:99:a3:8f:8a:98:62:6f:b1:ce:16:de:00 - 58:8e:17:22:fa:51:3a:0f:ba:c6:a2:31:56:32:a0:b5 - 44:0e:b7:86:c9:2c:b1:be:cb:27:f6:d3:7b:df:b9:d9 - - -exp2: - 32:88:88:9f:5f:bf:8d:26:a9:58:ee:76:d5:15:83:66 - 79:fe:4e:75:f9:5a:16:59:86:f8:a2:8c:21:f9:17:6f - 3a:bb:23:fc:66:75:9b:8f:a8:71:96:dd:6c:40:b2:20 - 3c:20:2f:96:67:d1:b1:c5:89:81:e2:b5:45:60:e6:34 - 31:ab:0b:84:fb:d3:98:69:73:48:39:bb:23:cf:a1:85 - fd:a6:fa:67:2a:08:d6:d2:d6:53:39:6f:ce:d1:12:3b - 75:44:09:b0:c9:2c:7f:e6:8c:46:4f:8f:21:5f:05:d5 - dd:d1:f6:4f:be:63:84:30:ec:b2:31:30:a1:08:5e:fb - - - -Public Key PIN: - pin-sha256:xZynWKbgc/clJ1vmaUuUrDqVNLkKBsbEVke+Uj4T30M= -Public Key ID: - sha256:c59ca758a6e073f725275be6694b94ac3a9534b90a06c6c45647be523e13df43 - sha1:c19f5569864001b25aad7ff9de9d200574d2b257 - ------BEGIN PRIVATE KEY----- -MIIE7QIBADA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCA6EaMBgGCSqGSIb3 -DQEBCDALBglghkgBZQMEAgOiAwIBQASCBKcwggSjAgEAAoIBAQDU7bWnhVq6nAK4 -/AJGHuNsVDRyPPxW6wutnZWDowkVnfHnGpMhPrWzZH+Yg5eit8oCxEiiVT8puLGH -YW1yX3qfIGn0gix+SA3WV0CSLBOf5UR11K1B9sn0dMQxFN1EjOwlW08HUbxOIb1H -yehWsh7xXeBWNsgYGIZxQbvCPAbK5HHyzeyfCaAFbrTZMmVTm1p6uaIwes0Gpkc/ -9AWeh7cvsuD8Y8XsLBntMq3ucZw30TTzqhq67c9AKIJDEVSDdELbcPdYEsURrxoF -JiTvgYom7izzisQsDEfMdit6zuC7gNLQXsiPHQPFTEcbe5DE0wufi24pvaslzaoe -rXIZAsARAgMBAAECggEAASMb2z8tEt4Obap64KD9mfCBLzMALf6nW1ACImfWfpUP -W6qaqozJL6ITxF6/ipDstUMTGDrYUYK4//4XNYso/nyP1Ex1rF76I/DnWWB+4lW5 -Hd/65OVKgtG00oZIADu4byKjs2hOVyR6/E0pvnzJCYT008ELJIXNAgHV3N2xM5gu -POV8aeLn5AKDtejQBcXNW45y9+6y1BEVhbOzT6zPd4FzaKlw/LOUJPl39ThPr6vL -Tn/CeXaH+Q+jO1yVYWQROkCYKFGGSBFBMOIalJQG0AoV3hkTn/MGuANoj4eyOk/z -dbz3WuQetEkpCdpX5EPqlr104/M4Wr2z2swJmfYJGQKBgQDw2QruEVDayQEfXD3I -gjscDCcXgGn60Z3K0n8SkOmo5Tbwt4+OkPAKeFMPk1Hw9HKjGt7qD16PhMNXOTiT -c8+UbhpW7zbZIjl1dltO9lTnAjKfVF8bAjdQ8Ekb4ObRvbgHm38V/epTCFnoF2bV -EOSi8T7DyH5ooqFfBPF7psU8cwKBgQDiUwPdXfH8J6bXAbFe8yZdnvzyRYVLEIaX -pZ0rGTs1jpE2Z1DQ2hbexxOZdribL0T8a8Mp7KcROAXePS6FGkmIKLbgG49qCiFW -7O5WNLUg5qLAsAjBSBNg42WxSrlsm6Rjki9a5m2AKsKXU4cF3QgdmFLJiKnTXRjX -K14MY+CUawKBgHx27ZsR/8LQ1W+rb5JLGtjnvtv6VMp1wSGrnlet49KQgc/sTJfU -dvgyLlqCO3pWGVgI7uHuh2OLrJdKzt4En2WJcLs0bBfSA/eb7pvj2QR4skh8hZmj -j4qYYm+xzhbeAFiOFyL6UToPusaiMVYyoLVEDreGySyxvssn9tN737nZAoGAMoiI -n1+/jSapWO521RWDZnn+TnX5WhZZhviijCH5F286uyP8ZnWbj6hxlt1sQLIgPCAv -lmfRscWJgeK1RWDmNDGrC4T705hpc0g5uyPPoYX9pvpnKgjW0tZTOW/O0RI7dUQJ -sMksf+aMRk+PIV8F1d3R9k++Y4Qw7LIxMKEIXvsCgYEAjmCSrKIC2WfHxXAT0rYv -XbKhaA5G2zP2iqS9BgfbWzliFOaBlTLKibfdh2C9mDNdqCEl2WT7X1KlKOgF4ntd -Q6790pj+IJ5OZZYRxK6RpzbLPY3+KXyRv4tFF/Z8KUEaS4eFwpWW1Y3HW3opbsNq -xm84VfefqifEpxiMQvGylJ0= ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa.pem deleted file mode 100644 index d49498959c..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDQjCCAiqgAwIBAgIMWYGU8BDBJ3AgFnsvMA0GCSqGSIb3DQEBCwUAMCMxITAf -BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAeFw0xNzA4MDIwOTAxMzZa -Fw0zNzA3MjgwOTAxMzZaMCMxITAfBgNVBAMTGEJvdW5jeUNhc3RsZSBUZXN0IENs -aWVudDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAM/xijmXl7PVbZwS -BE6h3A2pEsv4Au86itIS4qb7GJNvOZ2pwx22/AOGr/JvsYGGnOsVxQ63mE9xBz2l -jPsqGDhQeLD7CptXZAcwYa6k2z6S91Sd0GCNOgPuFiXKFXt9Si82uNB42r7kHG04 -exmY7uKUdMp1IxUbZ47leBmT7W5I65lK2acxkzRK6bdPErnNGzZt4t/k6E0Oowf6 -ElUf0UvhsX0trFB9O8yvCciqAQoGUw7b1yzWA9bWi1AQUPgx58eGMrRgQXfFnuuG -efHqU+/nAVAt5pHHU0GqB09jcv1lJSQpDMtCBa5tsorVvy8OiiSJx15tIyNOtVcc -9y/oH3cCAwEAAaN2MHQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcD -AjAPBgNVHQ8BAf8EBQMDB4AAMB0GA1UdDgQWBBSoiZsEL0tgug7IyLHTI1fA+tNH -OjAfBgNVHSMEGDAWgBQrQlWadXqqzBV+2iw7BZXrvFHaQjANBgkqhkiG9w0BAQsF -AAOCAQEAx3E9wM3ASFrxZ8Zw/036WHeJUjgOL+W9TM4Y4GZpQDkyMqKxs3pmyqcU -xuXWV+SmTCkOAvrH/kWhmgDxSs3eGrGBsRPEZcUolcedDWqPJpy0qZH6mv3Ge0+v -V+YOcd0qSVHaLpR1LQHrDnatASaVVRF4Ohk2IRSvjzYYJ158D3+erB79Txt4kqK2 -oDMp59uI2K4VwNiXeuWQgolaoKTEtPSrjuuKzetrwLN8ajii5rkiOKyuKlkZ2SyE -BYp/ye1hzmqXRHNrObbfMmggwimrShYWUSwHp0/gKMEhen+yqd9C2KEVj7dqm+oL -Jo+TGr6L77LBgZ5r7vfzj4tzL877mg== ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa_pss_256.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa_pss_256.pem deleted file mode 100644 index 77ec9737dd..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa_pss_256.pem +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIID2jCCApKgAwIBAgIUej61CoI5BANd5v/ceHNxIOtR8d0wPQYJKoZIhvcNAQEK -MDCgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMC -ASAwIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxlIFRMUyBUZXN0IENBMB4XDTE4MTAy -NDA2MDY0MloXDTM4MTAxOTA2MDY0MlowIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxl -IFRlc3QgQ2xpZW50MIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEa -MBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgGiAwIBIAOCAQ8AMIIBCgKCAQEA13o6 -AYteWQ8QfZVczsbqyCkudW7+bPmD1T5ybPxWlUPUYjTfBXyzs6ZKN8q24CNEGfws -6s/8+SYarFqNg/hP2Quu/OmddTctpJMCXWkQ37d6TLYFZHNGRbL+pLMznmhrFDWO -7t+74Hcjdrhu/fzy3QJZXtyhs12TxiziSoUhf8pZNuh33f7N/vawlv4UalxoTM+8 -/f7s4y9REM9G2x06Pc2H7GdKfSBeeWzq0yTTggW82Xnlzd9qu2KbILM1NGGuaamr -ubYnrHsz9b7fmrQSaAZVebO6UvCRkGmx8VoR44ve3fGZylqth7plUiemBxJgECST -S98S20kP/CH/n+aSdQIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG -CCsGAQUFBwMCMA8GA1UdDwEB/wQFAwMHgAAwHQYDVR0OBBYEFAKLYWuJSEnLypED -/PkZ/dogyhAeMB8GA1UdIwQYMBaAFCEaiLbeA8ABgKrbcIiZpo8XzqpsMD0GCSqG -SIb3DQEBCjAwoA0wCwYJYIZIAWUDBAIBoRowGAYJKoZIhvcNAQEIMAsGCWCGSAFl -AwQCAaIDAgEgA4IBAQCFjsx2JQ+AtT2AD7harhhhuCH8ah9hTLz1n+ynkmq1lmYP -JCudOJfgWjMFlSv1ptzkvOst+Ig2BCCJrwX1akMX1cMsR/eoDUxxkqREU/irEYPj -ePvVk0+bd6C4+nCvs/J0bv5P/16z2NLmAq8BUidi4ULPyzVn/+nAAUpAVlYi/mHP -RQCy8UMj2ENACc4LIP1tek9cp+t5JMjsv2ogy+SPCXtq7VTMAFrckrALv9b1QLE6 -J06bp7+Q7GIkn0gTm+Z5AaddHH1ZDjqpYFcngx7wVuQCbZwTqnVJx5nQrKz+HQYt -87KWIhR2nbtygp2oZESXQg9huH395f/h+foYArPG ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa_pss_384.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa_pss_384.pem deleted file mode 100644 index dda273d859..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa_pss_384.pem +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIID2jCCApKgAwIBAgIUbdDLDIxL8QBkB4RFPZ3TaeInTUcwPQYJKoZIhvcNAQEK -MDCgDTALBglghkgBZQMEAgKhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAICogMC -ATAwIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxlIFRMUyBUZXN0IENBMB4XDTE4MTAy -NDA2MDY0MloXDTM4MTAxOTA2MDY0MlowIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxl -IFRlc3QgQ2xpZW50MIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAqEa -MBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgKiAwIBMAOCAQ8AMIIBCgKCAQEA3Qfa -qGVVCe6+C8adlQUo9tYL1t0xf+m0PFMLLe/xv5ITFJ8JEQih1En/ilFeRWLgY36d -GGi9k/5CpUbeWwbWkCiYEP3vXQv5pHz5zmgqbF14WhGn03c6SckBc4BAUFbfHuYh -13ubFAsA/An3l5o5UG3q6cGxR8oaxUl5jkIqdOlRgRQH8lFZcrHeWgly3neQYuD3 -lmQHf22DwO2Cb2EgZSoJ772KZnRMKmBjuZFv91WqvdSgodWiLyCP+BlFYJO6GS20 -68+M6h07Lh+ZgmZ5y8gbHLKpHb1noDnkolkUZ2kOYkVT4otjnzjPwN3Rxna4ood4 -CDE4KOnMJZhp+iDI8QIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG -CCsGAQUFBwMCMA8GA1UdDwEB/wQFAwMHgAAwHQYDVR0OBBYEFAw8srrU8hFaATtQ -2cuffd8YPmASMB8GA1UdIwQYMBaAFKnwXW9hoPAB6HSyWn7UEUMcKonnMD0GCSqG -SIb3DQEBCjAwoA0wCwYJYIZIAWUDBAICoRowGAYJKoZIhvcNAQEIMAsGCWCGSAFl -AwQCAqIDAgEwA4IBAQAHS+4AoR5A0sNaCrhuB8rfDfI+kcO0SqwT98d5QTJnsiqu -/2OHecJbN7nkYKh9K+8ccWVCWncWV1dFP1fVnCibFpAF+750wLhSpb/RVfm8Gd8F -CLuZNC6i9w7ssQzus7SpBx2viY615zxRJ6kdhGhPSxc98tPAHkdkdWGWq0R8Q3u9 -hP/mj/oAxskhvF/Lofwk5uyYSNIcZ9w3YPmb70OUWDH1yreF7s0J3hOPZZiC7ZA4 -Nao6UPpwKk1IdZix63xHFwCN21AtQIHoL/aRcVSlHlol5VOgaWbetb1BTBqEoKux -s4hJIMFIslA++GDX/qj7JrYr+FJTOqFF2B+Rm7wO ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa_pss_512.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa_pss_512.pem deleted file mode 100644 index 2bc13ae96f..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-client-rsa_pss_512.pem +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIID2jCCApKgAwIBAgIUWlbiagMGpRhGjEwawdwRz964ObEwPQYJKoZIhvcNAQEK -MDCgDTALBglghkgBZQMEAgOhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIDogMC -AUAwIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxlIFRMUyBUZXN0IENBMB4XDTE4MTAy -NDA2MDY0M1oXDTM4MTAxOTA2MDY0M1owIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxl -IFRlc3QgQ2xpZW50MIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCA6Ea -MBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgOiAwIBQAOCAQ8AMIIBCgKCAQEA1O21 -p4VaupwCuPwCRh7jbFQ0cjz8VusLrZ2Vg6MJFZ3x5xqTIT61s2R/mIOXorfKAsRI -olU/Kbixh2Ftcl96nyBp9IIsfkgN1ldAkiwTn+VEddStQfbJ9HTEMRTdRIzsJVtP -B1G8TiG9R8noVrIe8V3gVjbIGBiGcUG7wjwGyuRx8s3snwmgBW602TJlU5taermi -MHrNBqZHP/QFnoe3L7Lg/GPF7CwZ7TKt7nGcN9E086oauu3PQCiCQxFUg3RC23D3 -WBLFEa8aBSYk74GKJu4s84rELAxHzHYres7gu4DS0F7Ijx0DxUxHG3uQxNMLn4tu -Kb2rJc2qHq1yGQLAEQIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG -CCsGAQUFBwMCMA8GA1UdDwEB/wQFAwMHgAAwHQYDVR0OBBYEFMGfVWmGQAGyWq1/ -+d6dIAV00rJXMB8GA1UdIwQYMBaAFKVY4bgN1p+8NOXoPdj6DKWDscBFMD0GCSqG -SIb3DQEBCjAwoA0wCwYJYIZIAWUDBAIDoRowGAYJKoZIhvcNAQEIMAsGCWCGSAFl -AwQCA6IDAgFAA4IBAQBm9QAlG/xd8O6/TgJwnxmjKcSNhWgFHz4yY2uSMRP51VR/ -SzoXIY3drTWz3fuXSxbjAcVSlfmSX3KMVm1M6IKOw37PwUD8C5oVpLEY/NpwTjfb -WS5RR0XpAi2JJhPa9vJEO8PMdDV5C07u3fs5QvysNPZqJJvn+i2rU44JTeeJzBf8 -hesUVx4m+16C4cdU80prTnq9QYnZi08vIJ0iW0czxrsRikcqAnb+FEsrnbl1t3Wy -PcdYUq7SkQ3B3pnreOMRnRGGP0dq4YOPto6nKD2h70TQ2rH2JhSlDy7aPNx9jUKO -dwz0hfct7G7Wyoz2U+rI7ulHt3Mck4DyOam+TMEJ ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-dsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-dsa.pem deleted file mode 100644 index 029d88ac24..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-dsa.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEpjCCBEygAwIBAgIMWYGU8C09TIACbzDkMAsGCWCGSAFlAwQDAjAjMSEwHwYD -VQQDExhCb3VuY3lDYXN0bGUgVExTIFRlc3QgQ0EwHhcNMTcwODAyMDkwMTM2WhcN -MzcwNzI4MDkwMTM2WjAjMSEwHwYDVQQDExhCb3VuY3lDYXN0bGUgVGVzdCBTZXJ2 -ZXIwggNGMIICOQYHKoZIzjgEATCCAiwCggEBAKIJ4msqhhMj/QtISZhmqwZzwvxo -9G4/z6zraDROzwt4KfCM+4Iplzb54xi4iifAKFjzegL066KAYuZEu3c5oU+1t2WT -Uz4WaD/F8jpA/p8/iMp7d/Aj+qwHoIewZmtFN7OyTvIgPFUtzKShwsA/FGxQzDbw -XOtsQ77WFdLa7B+1ZjFC776KbzNzZG0iClfZL0NweKYAGjdkJ6GKDJsKwkzWJ/wV -2fKew8GzvUKiUVamMN6QXVx8x7odGETBu5FB0bP4wCKqnQKLarfHRspynA8rCNHF -mW5CMGDQm+zaehpcgIOnsJE8xb9C7bzzFmkWxEy5DJmYPtnKEBDWz6XjdmUCIQCn -T2W/xqEt+tix4k4f/a/0D3i7iawNewwccUb4C5RcYQKCAQAs4tv1yBjpGvsspRpL -rxdAcIXhCeBQCzoYMI4CFH3lSjotUT5T30h2fA3qi3bm0GhnuAw7a1s3H749MAKm -P9RgskHkeyFoWWKJ5rVqz+KOBXQkhRDkGND5hYdBgMq5YnnDQ7O0D0621AovPOex -UQFVl2/4bJ/FRCPZ3NL790XwZNL0v/XFTDU1eL5CcSOwsENTfCpPa+6nnO4K/Gtr -QV4mCpY8dwtQB9lTB6BuhuMQ5Bmu+JVaVaGDIP8pd1ZXxPg9WQcZY+sFRz5zkiBm -Fs/+rbTnUC6Lx5T3tMBGhWC7+VmJNeT3eVzvMU7tgGGIElsQC1/5xU6pzRAL4/It -ijN3A4IBBQACggEAYi/1HWMttEK5mU2B6IpU6pgUmkUleSVrQo3tdMViiSlGPtB6 -c+yoZc3GxruCUHIYbN+rFNIsdoJQ2k9Ah4VkX6mBytopd5eulwG63DDrXCRgxqMm -BHFNvcZ/OG1Lj8Upnf9x9GbJwzoV+4J/tF/al9XlUk9gJf2zfr7oq6NAqvHXsaHj -4oC3FQV0enQdfnC325wVie+BrV5hE/YUgcYF5m2R3a0o4UokGqyJzxDjxLlrtMHe -6Rn7JngDfq7oFNXN1BMu9RD0Ll2KwZFURzZMAzj5DNc8eAScTC7omVrk6/lsQ1TW -tnlOm7QXoBcRINwrA9hoTAgPh7iyqr8497M2yqN2MHQwDAYDVR0TAQH/BAIwADAT -BgNVHSUEDDAKBggrBgEFBQcDATAPBgNVHQ8BAf8EBQMDB4AAMB0GA1UdDgQWBBQL -IkWyCawkb1V1BkwW61qMAd3TnTAfBgNVHSMEGDAWgBQA5WZqsOvPVi3KO+dfgL1w -NxeVqDALBglghkgBZQMEAwIDRwAwRAIgUHB/GqyakK8awZlbR6MsLGDz0UiCvfCR -eK2qrGSqsfICICKrf9+KyfiDh5HoB4pPZDOAAd5yQIEclw/aOD1yq0wT ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ecdh.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ecdh.pem deleted file mode 100644 index 7c8f044352..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ecdh.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBtjCCAVygAwIBAgIMWYGU8BHdfIBy0aZkMAoGCCqGSM49BAMCMCMxITAfBgNV -BAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAeFw0xNzA4MDIwOTAxMzZaFw0z -NzA3MjgwOTAxMzZaMCMxITAfBgNVBAMTGEJvdW5jeUNhc3RsZSBUZXN0IFNlcnZl -cjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLWCgejCbKGDGpoTzBtYGeQt3OSZ -B3aPwiAaQog3VfsSsERXWAiUdogMibtR4lLl6s9QbWDorQWXhejIUi7woY2jdjB0 -MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0PAQH/BAUD -AweIADAdBgNVHQ4EFgQU0L5LXF6oiQE1eNyzRztmBOoHiR0wHwYDVR0jBBgwFoAU -0ma/FGcW5gGlL//B26Xmj0JISecwCgYIKoZIzj0EAwIDSAAwRQIhAM6F/DDKK7H9 -HbkqCcUsxTCTakQ1wcAkkRDMCI/lKLxCAiAUjBMndyVRzNlY6h4+2NP0A3RT2Q87 -2mnmiPSuW9Q4iw== ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ecdsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ecdsa.pem deleted file mode 100644 index ffaa81e9f6..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ecdsa.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBtjCCAVygAwIBAgIMWYGU8C5XSABmYVGJMAoGCCqGSM49BAMCMCMxITAfBgNV -BAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAeFw0xNzA4MDIwOTAxMzZaFw0z -NzA3MjgwOTAxMzZaMCMxITAfBgNVBAMTGEJvdW5jeUNhc3RsZSBUZXN0IFNlcnZl -cjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABKzro1Qhiu2IrXTK1X9xZ20T0eL0 -vxwmwTGcr7EvI9kRnhXDsV5Har9CmzInfIcOhL9VlS4K2WUhxNMuLYr5EIKjdjB0 -MAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0PAQH/BAUD -AweAADAdBgNVHQ4EFgQUNjGYYKninT7GGg2A3pGsO2xPByIwHwYDVR0jBBgwFoAU -0ma/FGcW5gGlL//B26Xmj0JISecwCgYIKoZIzj0EAwIDSAAwRQIgWYw3r0KqlS60 -h4RON2Ycq7oH2qxf+b9mWaehP8sebHoCIQDJyu+qUegmitkVqbenVV2ypPL7x2nP -qvghAKPod/72bg== ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ed25519.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ed25519.pem deleted file mode 100644 index df3a379c4b..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ed25519.pem +++ /dev/null @@ -1,11 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIBfjCCATCgAwIBAgIUEOZLZhsVbmqCJUugm0oRFrWPO8swBQYDK2VwMCMxITAf -BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAeFw0xODA4MjMwOTI5NDda -Fw0zODA4MTgwOTI5NDdaMCMxITAfBgNVBAMTGEJvdW5jeUNhc3RsZSBUZXN0IFNl -cnZlcjAqMAUGAytlcAMhAM2PquzCzWQdU5dEG7JMCCfvaaNyfRlcIhsCzF8RLWiw -o3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdDwEB -/wQFAwMHgAAwHQYDVR0OBBYEFFsSy04J6VkN2IiqBJKGV26iMRYyMB8GA1UdIwQY -MBaAFOp5vj76ByEwboduoHWAWzajnM1mMAUGAytlcANBAOhCpwpdClN4+DFhtwLf -br8//ymNpCOf8W4vE4pZACueKIooh2S4WnbkFwBloOntxX+4TXnD06SBlpcmoQg2 -NwY= ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ed448.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ed448.pem deleted file mode 100644 index 3ba371f662..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-ed448.pem +++ /dev/null @@ -1,12 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIByTCCAUmgAwIBAgIUdWna0n25+zzo6pW93hOpSKLfU0swBQYDK2VxMCMxITAf -BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAeFw0yMDAyMTMwNzMzMDJa -Fw00MDAyMDgwNzMzMDJaMCMxITAfBgNVBAMTGEJvdW5jeUNhc3RsZSBUZXN0IFNl -cnZlcjBDMAUGAytlcQM6AETFuJ7D3g4EgZnNVbPW3j81X6VpJane2pY04n8bzYcs -kAvfb5lGogDBoczMsB9opnPMdSwZQs6CAKN2MHQwDAYDVR0TAQH/BAIwADATBgNV -HSUEDDAKBggrBgEFBQcDATAPBgNVHQ8BAf8EBQMDB4AAMB0GA1UdDgQWBBTHz9U/ -S0PulhuJNX8vOdBOjnLCqjAfBgNVHSMEGDAWgBS+BSv++BEWlTJ53q6rwnDLVnR5 -JTAFBgMrZXEDcwAp/84ZggkOUHT0qCL/8a3Qmj6HyXeb8NNVyPNSbBgW6HVQsn9c -wquAbara1d9VFJ+ucpSdQfYPs4DVfxADVcVfaunEGujBoL9U3q/9XIJ5IT70HL1O -+PUERelTyKywKQI8Y2YYKUH6UYsesLhcAQ8DBwA= ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-dsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-dsa.pem deleted file mode 100644 index 73247089ce..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-dsa.pem +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIICZAIBADCCAjkGByqGSM44BAEwggIsAoIBAQCiCeJrKoYTI/0LSEmYZqsGc8L8 -aPRuP8+s62g0Ts8LeCnwjPuCKZc2+eMYuIonwChY83oC9OuigGLmRLt3OaFPtbdl -k1M+Fmg/xfI6QP6fP4jKe3fwI/qsB6CHsGZrRTezsk7yIDxVLcykocLAPxRsUMw2 -8FzrbEO+1hXS2uwftWYxQu++im8zc2RtIgpX2S9DcHimABo3ZCehigybCsJM1if8 -FdnynsPBs71ColFWpjDekF1cfMe6HRhEwbuRQdGz+MAiqp0Ci2q3x0bKcpwPKwjR -xZluQjBg0Jvs2noaXICDp7CRPMW/Qu288xZpFsRMuQyZmD7ZyhAQ1s+l43ZlAiEA -p09lv8ahLfrYseJOH/2v9A94u4msDXsMHHFG+AuUXGECggEALOLb9cgY6Rr7LKUa -S68XQHCF4QngUAs6GDCOAhR95Uo6LVE+U99IdnwN6ot25tBoZ7gMO2tbNx++PTAC -pj/UYLJB5HshaFliiea1as/ijgV0JIUQ5BjQ+YWHQYDKuWJ5w0OztA9OttQKLzzn -sVEBVZdv+GyfxUQj2dzS+/dF8GTS9L/1xUw1NXi+QnEjsLBDU3wqT2vup5zuCvxr -a0FeJgqWPHcLUAfZUwegbobjEOQZrviVWlWhgyD/KXdWV8T4PVkHGWPrBUc+c5Ig -ZhbP/q2051Aui8eU97TARoVgu/lZiTXk93lc7zFO7YBhiBJbEAtf+cVOqc0QC+Py -LYozdwQiAiAxcsCPJCsRaG0j1eDW0xomfipUvAM3Ws9MZ38R8fxQ/A== ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ecdh.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ecdh.pem deleted file mode 100644 index 669fc5cd0d..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ecdh.pem +++ /dev/null @@ -1,6 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGUAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHoweAIBAQQhAJsXqInsRDYS26zX -d4tHYu+WKbw2gyrnMvDOamsRyZg5oAoGCCqGSM49AwEHoUQDQgAEtYKB6MJsoYMa -mhPMG1gZ5C3c5JkHdo/CIBpCiDdV+xKwRFdYCJR2iAyJu1HiUuXqz1BtYOitBZeF -6MhSLvChjQ== ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ecdsa.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ecdsa.pem deleted file mode 100644 index ca898da2fe..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ecdsa.pem +++ /dev/null @@ -1,6 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgHJsLqw/ZeIlLMmMZ -u+LqjbxD7OY2VhD055Icbpp5HBmgCgYIKoZIzj0DAQehRANCAASs66NUIYrtiK10 -ytV/cWdtE9Hi9L8cJsExnK+xLyPZEZ4Vw7FeR2q/QpsyJ3yHDoS/VZUuCtllIcTT -Li2K+RCC ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ed25519.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ed25519.pem deleted file mode 100644 index e1784c5efd..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ed25519.pem +++ /dev/null @@ -1,25 +0,0 @@ -Public Key Info: - Public Key Algorithm: EdDSA (Ed25519) - Key Security Level: High (256 bits) - -curve: Ed25519 -private key: - f8:32:a1:54:10:e9:b2:b2:31:10:9d:05:c0:3b:58:c5 - 1d:1b:7a:67:d1:53:5e:6b:58:fe:85:1f:4a:4e:71:13 - - -x: - cd:8f:aa:ec:c2:cd:64:1d:53:97:44:1b:b2:4c:08:27 - ef:69:a3:72:7d:19:5c:22:1b:02:cc:5f:11:2d:68:b0 - - - -Public Key PIN: - pin-sha256:tt5u5+ynOHjlD3uadUUmN2V5yZLekOkkwyyk8sZH7cI= -Public Key ID: - sha256:b6de6ee7eca73878e50f7b9a754526376579c992de90e924c32ca4f2c647edc2 - sha1:5b12cb4e09e9590dd888aa049286576ea2311632 - ------BEGIN PRIVATE KEY----- -MC4CAQAwBQYDK2VwBCIEIPgyoVQQ6bKyMRCdBcA7WMUdG3pn0VNea1j+hR9KTnET ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ed448.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ed448.pem deleted file mode 100644 index 382cdd6189..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-ed448.pem +++ /dev/null @@ -1,28 +0,0 @@ -Public Key Info: - Public Key Algorithm: EdDSA (Ed448) - Key Security Level: Ultra (456 bits) - -curve: Ed448 -private key: - af:0f:44:88:c8:11:f7:e4:87:19:b2:e5:f2:17:a4:e5 - 0a:69:78:ed:1c:34:f1:dd:8a:b1:c6:92:92:64:26:94 - 8f:46:45:af:b6:58:d5:b1:20:ce:5e:d6:b7:8e:48:e9 - 0e:14:a7:94:56:d5:14:c8:9d: - -x: - 44:c5:b8:9e:c3:de:0e:04:81:99:cd:55:b3:d6:de:3f - 35:5f:a5:69:25:a9:de:da:96:34:e2:7f:1b:cd:87:2c - 90:0b:df:6f:99:46:a2:00:c1:a1:cc:cc:b0:1f:68:a6 - 73:cc:75:2c:19:42:ce:82:00: - - -Public Key PIN: - pin-sha256:un7RlIHaUdVuKc7nlN4TlmbH42dUsl+RH5jeVUKSyiA= -Public Key ID: - sha256:ba7ed19481da51d56e29cee794de139666c7e36754b25f911f98de554292ca20 - sha1:c7cfd53f4b43ee961b89357f2f39d04e8e72c2aa - ------BEGIN PRIVATE KEY----- -MEcCAQAwBQYDK2VxBDsEOa8PRIjIEffkhxmy5fIXpOUKaXjtHDTx3YqxxpKSZCaU -j0ZFr7ZY1bEgzl7Wt45I6Q4Up5RW1RTInQ== ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa-enc.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa-enc.pem deleted file mode 100644 index d1e835252a..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa-enc.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3mOPbXv2BWujn -fNGxYw/JNbyW7qQTaKnZogE/aIh5m86hinj7agtV2UDsBXvaQSTrp8vXwKNr9IYX -VLEvT5lM8mj+GDlJ1jhKbo1ODbZPY7b1q5e1RtMUhNX3GgZDevrLL0Tt/g912KD4 -U7vTsS2TQ35hXOQ6bknN9VWNhat0lQ/L60q2QofPdk9PydFZV9CxkoGvwV1BqiTp -JiEloFAf3h07Gim/e3K6OlU2ci+Lb6/uvSDPiOIVYPBURmVjs49sLha2jDj51II4 -9KpPbJwNUT902VrvoRwhrWP8FxYO0QxG9J07IdsNLQLzVWBxz03qK3v+U/akO1QQ -uyrWmw69AgMBAAECggEBAJd3t2RgddymV8fDHmyuQXPKtY//ybWJf7dhBI5/ezh4 -5nw3daBV2Iw29GzECW4CmKcig/W3pBuIXKga4yMZFGx7AUvASGM2LLKbilB+142f -wm3j5wFTMQmYnb2C5u/9IbNHiCKgm7ipxAObcTYw0lzQLg+9Rz09h/43DSH4yX8f -ov1ANiERjkjTMYfaoMbdVrik3MrzBx3lNa0wShS3WebdLIBGv1pk6U2b0bCVJUPP -OJ9n9bs4+kzJBofDHad6N1UszQoHB+1xHthKC5YHyEbnkWlJoyiNWELcADCKYxWY -lsbH1b6zUXlQ+/IYe7uD9Y9nOa/6kXwmyPGuFVBXGgECgYEAwMvMRVkFArycrgd1 -mmYyewuGKQyOonj6qlKDjpuZ829CPwZ7xEpSBNQcKn1vHW52IxJizVh5fHWG/8ga -p2uVGdNUbtjlazrZXDm7xBEAYke+vxgrLHzwCvk3MxFF639nmN9khX+cceLkAaZm -AEye8thr6IN7TVMt43E4G7Bd5OECgYEA88kWgFRvY7rB3oRnMEuLKaTIAdJM81Tt -lQ3PQNTAdt9HenJE5E5y884xQsYCfmvkTGrd7bWHBzRgA0slJQExAaLBNrqm2evw -AshQRVY2mR4TUuKadrWVDV6IF9IDeGZpCEmz1A0RY5M77l2jJ+pAd8Pd8wM07QLV -KqjpDRLTCV0CgYA8xkGPPr+QnEo7pchRspOJLBnPiNDRsJc756Tm6HAAR/s3COEt -AEyYjxCN6FqFiZOd/Ka+mnw5WocCzF5yljw7Ft4PzzmKstNf+icRaFaZpIohjQnX -DU9R9juLUo+a69+JVipG1vJHCEHdr0mKIJ0ealChzAirWGQnxUHtoIwIoQKBgGH1 -JWN/ihrKymf9T/FqCYs8OVnyBRWpxKWmHOdyFbwuT+x1yhTrKOmqqsSoCAyAkgXa -0z5XOOC+PO5V3aEW73g2y+iP68eZNKIJl6ek0t+H5D/j6ilVIYVzvL/Flbtle0Ln -Sqkkbx5R5T0MxyicyjbVr3OckEHEZ59yq+Ki88XJAoGAIrVxwcg+b7FKvBhjrx68 -kBK0rFwTdcqq+82G5R5DGfK5nK9RON01aOM3ddZNy+q/xe5O0kzFPkEQhSsGXTJ2 -+O0cBtTSrQ8hMZKHH6Ol4y6rWjDObmhNZR5ms76l5Fi3EzUKcLbTcl1NXlihanmU -rmQcSs0PKSGpZdraqdH+YrU= ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa-sign.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa-sign.pem deleted file mode 100644 index 975638e550..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa-sign.pem +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC8Xwb2eeVqVUHg -obVcnfjv7C7nhlZAZWFEbrJJXCwqnbYTOUEywVTDkNr7cVp2Q0h1iLz1Dwpz8UfC -LD77DPAVKkI8o9nfQnL7jTYMCjCM1ANdG0Cz0OumnNU+zFP9gOEfscSTeWTqeAfl -MBMDQHgFX0PV2V/h5JlQIyjyYkc5o3c6ALJUEdBecy9dYI5kvEk8GbiftjKVIdPV -RnRyfiwdQjVX5xMcD6kd8HdU0D1C12Tdw8fn9Ftu4JgOBkYrTLS5bQJY72cxrevs -YW8iiSYzJ0wVpcxRYj/4IKXD7PqtoVvYt7zwhOXjhnZGw51yYt2ogcWKUynkueMB -nPzpE7+VAgMBAAECggEBAJDChltL+d3pfyLdor52OCRI4RLTzdzXDBTG7QQrbVWi -tZW4Xj5fDIDuBRtOVTKlKj4Iww2gbWwEdBzoW84adzYMr7JiSMCmFC70qiA+hGj1 -VVBr7SFC4JW92LLV24XpURhGSMb8d20oqQicFUBeft3CBCOHVYQHZTqMip8an5nO -jWPEcxgp2hU5R1BzliMtpwtP3yLN8UD281CEdKy/nG5AfyI882mLbpiFtr0gymhw -CEqJd62vmK1mvSYDYA2QHy3ki9CxCNog0raQy/sOX4JtgCDhmy06FOOqzyo3LFcN -I7Ng2D3R+d7tCEJ8A3C7Z/HbET0MsZDolVljBoNEMlECgYEA+Kznpbm0Vd0Oew9S -5HRzXeZbCnc1fjTOhrtL8o+gyZsXiJQfDWUJ+Mlac2GFDK2FhQofnpfRHlyJRr0b -PSMzPMEB2nj/7Im0LpOUBt2TD69zK8INHIyFmu76ARgqE/rwyuxv+fyqDgUmQShP -8XC2joqejZchvvtX9REj6lQ650sCgYEAwetol/72hrrLNycaVntq7GMmLZCtqIc9 -YcLwQQVjFPkf2eiAHrsdZSnOUYXUJfvsRY4OZ7IkOQp0isU8FqED9CCoLwZ7B67c -DPhUYN6JgIqsH3CuxE9q/5sYb+d8HM9450pk8JrKHJDbBE3nwftLcay5iudkArjw -2hCi4CqzSJ8CgYAdqjKwGGkk3Qv/LiLLUgD5MKOnqfTdq1r/w5QZyXx60F+MUW8q -3+TCovKBVR7UFlcZOc3v01iE8LEHmUOIlYxlMPkRoOGWzA6Mh9pev0vt0RZCIBIE -V9cQVnXIb6OFYqga7P2mqrd2mLKpjy+KM9HzSyIC7gZ+i+lAON059PZZ5QKBgBbA -UMgscK4D8l2pJ8zns/bB9zO3WriADXKP1XI7eJF4XQVK4uU4HM3Gpt8nrWk7clAC -x6vg2aEbmerCEzewcm9M+Y5y2zJekJCw/e1TjpxXKLSTmt2LV8lfX/GZHhWfPdcd -AlS8RGQvlpKdtUgr/ID8u9QRK8mp+xAKjaFxQRGPAoGBALIBTZMU/kmvnO0PrzVL -eR3yojWI/+743kMFxPYfD2sPo/3Dd9qg8J7d1B6wu36vSP2rWVWVGShY6qxiDINi -RnLHzQILuztRbWnxRijP19KtmQctfw7/QIyUMkJ/Ur1bU2JpCjnRdoDVudk8nLvp -dAVR+0swGkDk8ffY+jwZ4gMM ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa_pss_256.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa_pss_256.pem deleted file mode 100644 index 40f795a162..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa_pss_256.pem +++ /dev/null @@ -1,138 +0,0 @@ -Public Key Info: - Public Key Algorithm: RSA-PSS - Hash Algorithm: SHA256 - Salt Length: 32 - Key Security Level: Medium (2048 bits) - -modulus: - 00:a1:d4:bd:d5:9f:ca:7e:e5:d4:7f:49:52:78:33:d6 - b0:2f:37:a4:a2:f8:4b:32:b5:ef:d5:29:9c:7d:2e:ac - 69:b0:b1:1a:09:7d:f1:92:3a:ae:c4:d1:29:63:1b:5d - 60:d3:4d:06:3f:bc:7f:46:c7:22:c2:98:e0:2c:32:fb - 9a:94:f1:ea:b9:3b:37:4b:44:8b:bf:60:28:13:6b:e9 - 31:a6:0f:e2:3d:82:60:9d:28:8b:d4:63:7f:e1:06:cf - d6:81:a1:24:ef:b4:a5:3a:05:95:16:e5:c6:9d:e1:6e - 31:56:92:13:16:c4:2b:53:20:73:57:96:9d:7a:4b:be - e8:f0:50:1a:55:18:1e:d9:69:f2:f6:b5:e5:3a:98:b6 - 77:7b:ff:30:74:9f:6d:81:55:50:b5:3e:81:8d:54:f6 - cc:17:3d:1b:10:bf:69:77:09:d1:d8:be:79:11:ac:3b - be:19:69:5c:b1:50:4d:ba:04:09:98:74:7a:e3:96:48 - 4d:fc:67:7a:34:73:76:48:5e:41:05:61:5a:68:59:39 - b3:86:aa:78:28:f4:a6:4a:db:6f:85:47:75:75:a2:24 - e9:21:3f:18:c9:46:87:91:80:1a:f5:fa:ed:2b:ab:03 - e8:ed:c3:dd:67:ad:46:eb:9b:7e:44:d2:5c:76:4b:3d - 5f: - -public exponent: - 01:00:01: - -private exponent: - 69:92:00:19:18:f9:9f:98:cc:ec:10:68:05:54:43:ec - 81:90:fa:0c:fa:8f:0b:d0:d6:59:27:a1:17:a4:d8:02 - c6:aa:72:02:d9:2f:3b:26:9f:16:74:20:5c:af:e0:55 - a6:e2:6b:7e:2e:b8:94:f2:99:81:7a:fb:5a:ba:13:9a - bf:29:a5:e7:1a:73:32:dd:cf:90:93:e8:f0:ea:87:a0 - c4:e5:3d:c0:c4:89:c4:5c:4c:03:cc:b9:02:92:50:09 - 6e:5d:32:5c:51:6b:2c:13:b2:33:d2:c7:a3:fd:08:c6 - 94:e4:0c:21:e0:ed:26:78:57:e6:3e:b2:12:b2:d1:21 - d8:93:90:f5:8f:2f:c8:97:6b:f0:e6:b0:2a:df:02:18 - 7e:ce:98:8b:63:0c:15:7c:21:39:f9:6c:e2:61:93:fc - 49:36:cd:9d:29:d8:a4:ed:65:12:6d:11:72:f8:13:47 - 6d:8e:20:d7:9f:01:29:3b:8f:dc:d5:b8:f5:58:6f:c1 - 5c:8b:36:40:c5:80:9c:1e:4b:9f:03:55:b5:ff:1c:46 - 1f:e3:b0:12:0c:44:f0:91:07:41:20:08:6a:99:5c:f2 - 11:50:30:4b:4a:84:8e:03:87:89:4e:60:5f:69:01:94 - 5f:82:41:1c:dc:7d:34:f9:02:02:ee:e0:e7:59:63:c9 - - -prime1: - 00:c0:9b:4b:2f:d6:57:df:59:31:87:2a:4c:42:fa:4c - 0c:f2:4d:17:07:90:9b:9c:db:8e:b4:aa:68:96:d1:16 - 01:27:92:e9:8a:26:d1:73:fd:68:21:c7:19:7c:46:f0 - 33:de:21:46:9c:0d:eb:84:8c:b9:6f:cb:47:d0:c5:b8 - 95:1a:e3:18:03:99:81:39:54:2f:c3:a1:14:74:c7:5f - 82:2c:e8:b9:a9:7f:4c:ff:ac:a7:4f:7f:39:20:ee:3d - b1:0f:83:33:fa:76:57:68:4d:8b:99:24:69:d2:08:1b - 1c:36:e7:c9:be:ea:db:1d:38:61:a4:4c:7a:44:e1:82 - 53: - -prime2: - 00:d7:18:59:39:b2:de:4d:0b:58:69:8c:33:af:51:ee - c1:e2:3b:64:b6:36:dd:31:c5:9d:33:39:e2:88:c4:35 - b0:93:8a:6a:b2:c2:8b:ca:c0:0b:21:94:69:90:ae:19 - ab:7b:b8:48:eb:f3:27:3b:96:5c:17:1e:71:89:e7:c5 - 14:d8:d7:de:2b:89:1e:58:f4:4f:1a:95:a7:34:65:48 - 6a:94:f2:bb:33:3c:90:d6:99:4d:36:48:8f:0b:30:d9 - 5f:59:26:60:f0:97:8e:3e:d0:31:99:6f:93:c9:c4:ea - 25:08:f9:48:2f:2a:77:57:93:03:d6:6a:22:fe:16:cf - 45: - -coefficient: - 5b:5d:58:5d:f8:be:1f:31:c4:e9:23:1e:34:41:60:1b - 2d:57:2b:d7:3f:39:74:5f:fa:d6:71:4e:46:02:2d:1a - cc:51:d5:96:7b:d7:0c:f1:8a:a9:31:e7:61:bd:0c:31 - 31:e3:5c:27:32:0b:bd:4a:67:ad:c0:31:db:91:a4:96 - b0:a4:9e:81:0e:75:2e:5f:0c:c5:9b:8e:4d:6c:b4:7e - 2c:44:53:2d:b7:d7:82:20:ba:59:38:df:ec:99:8d:63 - 5f:e9:24:d1:8e:6e:e0:5b:fa:f2:12:16:75:ad:f3:a7 - 2d:fd:8f:55:5f:09:a3:42:4b:44:d2:c8:c8:41:7c:c8 - - -exp1: - 7f:cc:4a:e6:31:e5:da:67:d7:4a:25:51:b6:bb:57:8c - db:95:35:2b:aa:d2:e6:10:74:af:01:c7:26:13:13:f3 - ae:2b:77:d4:58:0f:70:53:fb:2d:36:6b:7d:9f:a0:2f - fa:3a:c0:1c:39:cc:45:06:0e:e0:d3:d4:11:fd:af:8d - 17:eb:08:fb:12:76:c0:f0:50:45:10:f3:7e:cc:ef:5d - 73:a8:f3:d0:38:8c:81:b5:30:ca:b9:d2:d1:3b:e3:29 - 41:ee:bf:a5:77:b2:65:9d:d6:7b:c5:c2:85:3f:25:a5 - e1:f4:88:53:aa:87:ba:ea:b7:37:0a:1b:b2:ea:a2:cb - - -exp2: - 63:04:e8:7e:71:63:79:20:51:f1:35:03:ce:1f:ef:c3 - fd:bb:cd:df:3c:5e:93:bd:1f:63:27:b0:ab:b9:77:e5 - f3:e5:f2:bc:9c:66:f2:4d:7a:52:59:1a:47:ea:7e:12 - bd:7f:d6:c2:18:4b:e5:58:90:c8:6b:d1:64:e4:f7:8b - 63:4f:ed:0d:29:b0:78:ce:ef:63:93:a5:47:af:a0:a8 - c0:2d:06:14:ce:3a:f7:2f:d7:a5:b7:bd:72:2f:68:c2 - 46:2e:2e:ce:53:56:be:7f:e5:75:77:32:17:de:b8:d3 - 97:cf:fa:75:0c:1d:a8:89:1b:69:27:af:38:3d:93:e9 - - - -Public Key PIN: - pin-sha256:KBIg1VxV9p1XXyGsX+MwqaE2DenjfwcmJzw2z8jOeT4= -Public Key ID: - sha256:281220d55c55f69d575f21ac5fe330a9a1360de9e37f0726273c36cfc8ce793e - sha1:06c5f6d2f4f448fa67ba12fe955efbe15febd164 - ------BEGIN PRIVATE KEY----- -MIIE7AIBADA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEaMBgGCSqGSIb3 -DQEBCDALBglghkgBZQMEAgGiAwIBIASCBKYwggSiAgEAAoIBAQCh1L3Vn8p+5dR/ -SVJ4M9awLzekovhLMrXv1SmcfS6sabCxGgl98ZI6rsTRKWMbXWDTTQY/vH9GxyLC -mOAsMvualPHquTs3S0SLv2AoE2vpMaYP4j2CYJ0oi9Rjf+EGz9aBoSTvtKU6BZUW -5cad4W4xVpITFsQrUyBzV5adeku+6PBQGlUYHtlp8va15TqYtnd7/zB0n22BVVC1 -PoGNVPbMFz0bEL9pdwnR2L55Eaw7vhlpXLFQTboECZh0euOWSE38Z3o0c3ZIXkEF -YVpoWTmzhqp4KPSmSttvhUd1daIk6SE/GMlGh5GAGvX67SurA+jtw91nrUbrm35E -0lx2Sz1fAgMBAAECggEAaZIAGRj5n5jM7BBoBVRD7IGQ+gz6jwvQ1lknoRek2ALG -qnIC2S87Jp8WdCBcr+BVpuJrfi64lPKZgXr7WroTmr8ppecaczLdz5CT6PDqh6DE -5T3AxInEXEwDzLkCklAJbl0yXFFrLBOyM9LHo/0IxpTkDCHg7SZ4V+Y+shKy0SHY -k5D1jy/Il2vw5rAq3wIYfs6Yi2MMFXwhOfls4mGT/Ek2zZ0p2KTtZRJtEXL4E0dt -jiDXnwEpO4/c1bj1WG/BXIs2QMWAnB5LnwNVtf8cRh/jsBIMRPCRB0EgCGqZXPIR -UDBLSoSOA4eJTmBfaQGUX4JBHNx9NPkCAu7g51ljyQKBgQDAm0sv1lffWTGHKkxC -+kwM8k0XB5CbnNuOtKpoltEWASeS6Yom0XP9aCHHGXxG8DPeIUacDeuEjLlvy0fQ -xbiVGuMYA5mBOVQvw6EUdMdfgizoual/TP+sp09/OSDuPbEPgzP6dldoTYuZJGnS -CBscNufJvurbHThhpEx6ROGCUwKBgQDXGFk5st5NC1hpjDOvUe7B4jtktjbdMcWd -MzniiMQ1sJOKarLCi8rACyGUaZCuGat7uEjr8yc7llwXHnGJ58UU2NfeK4keWPRP -GpWnNGVIapTyuzM8kNaZTTZIjwsw2V9ZJmDwl44+0DGZb5PJxOolCPlILyp3V5MD -1moi/hbPRQKBgH/MSuYx5dpn10olUba7V4zblTUrqtLmEHSvAccmExPzrit31FgP -cFP7LTZrfZ+gL/o6wBw5zEUGDuDT1BH9r40X6wj7EnbA8FBFEPN+zO9dc6jz0DiM -gbUwyrnS0TvjKUHuv6V3smWd1nvFwoU/JaXh9IhTqoe66rc3Chuy6qLLAoGAYwTo -fnFjeSBR8TUDzh/vw/27zd88XpO9H2MnsKu5d+Xz5fK8nGbyTXpSWRpH6n4SvX/W -whhL5ViQyGvRZOT3i2NP7Q0psHjO72OTpUevoKjALQYUzjr3L9elt71yL2jCRi4u -zlNWvn/ldXcyF96405fP+nUMHaiJG2knrzg9k+kCgYBbXVhd+L4fMcTpIx40QWAb -LVcr1z85dF/61nFORgItGsxR1ZZ71wzxiqkx52G9DDEx41wnMgu9SmetwDHbkaSW -sKSegQ51Ll8MxZuOTWy0fixEUy2314Igulk43+yZjWNf6STRjm7gW/ryEhZ1rfOn -Lf2PVV8Jo0JLRNLIyEF8yA== ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa_pss_384.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa_pss_384.pem deleted file mode 100644 index 49f5ca8c5d..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa_pss_384.pem +++ /dev/null @@ -1,138 +0,0 @@ -Public Key Info: - Public Key Algorithm: RSA-PSS - Hash Algorithm: SHA384 - Salt Length: 48 - Key Security Level: Medium (2048 bits) - -modulus: - 00:e7:f5:99:8b:69:95:62:3a:54:80:b9:4c:21:a3:dc - 50:b9:ac:8d:50:a4:98:ea:da:87:55:82:2c:c6:68:e4 - 36:6c:7a:b8:1e:21:db:e6:fa:1f:c2:54:3a:c0:8d:2d - 94:ce:15:66:76:82:d3:27:39:ff:11:f8:19:99:95:6d - 63:7e:35:3d:17:b6:2d:59:3f:c2:b4:b3:73:75:b8:b2 - e7:9d:4d:7d:0d:98:e3:bb:da:e2:44:69:bd:15:52:0c - 45:eb:24:70:5d:47:55:79:67:56:1e:4f:2f:d5:e2:8e - c2:96:db:5f:2b:6e:c5:cf:4f:20:61:6f:22:50:05:8c - e5:ef:5d:e2:bb:e9:af:79:9d:89:ec:19:b4:ed:7d:e7 - f7:f7:20:b4:b0:7c:57:a2:c4:66:67:bb:e2:29:e3:9c - 07:9c:b0:df:30:36:40:b2:45:12:ed:53:5d:75:4d:a6 - 04:e4:2f:db:92:96:94:be:cb:e8:ac:ee:8d:28:5d:95 - a6:9f:9c:28:d3:c2:87:5e:7b:72:de:f1:ff:16:f8:49 - e4:9d:de:e7:7a:20:23:69:a4:9f:68:b2:db:b0:fc:fb - c2:77:0d:41:0a:ff:66:02:ea:9e:6b:c3:09:dd:7c:bc - 1f:47:66:66:8b:a3:72:e9:94:50:62:97:50:5a:5e:2e - d3: - -public exponent: - 01:00:01: - -private exponent: - 69:3a:96:ec:92:fa:8c:f4:4f:4f:92:40:42:66:96:d5 - 1c:56:76:49:66:52:65:00:bc:32:83:7a:92:8c:15:33 - c7:64:a8:d0:2a:a6:1b:13:cf:82:96:39:8d:0e:be:e5 - e9:d3:f5:86:bf:f4:d0:af:d3:d2:30:0e:55:09:5f:f5 - a9:d4:b7:21:61:a9:12:fb:04:f6:7b:0e:5f:12:6a:3e - fe:b2:9f:8f:a2:93:75:ae:67:c5:87:7e:9b:04:7c:c2 - df:58:c9:8c:d7:86:a4:2b:c7:fa:ba:0b:c6:69:20:40 - 90:b5:76:68:3a:b9:8c:41:a6:3b:ed:71:d0:81:a4:17 - f2:a1:1d:b8:b4:6b:01:6d:a2:e7:9a:6e:9f:b5:a1:14 - 61:7c:66:50:dc:e8:27:67:55:36:50:cc:19:d4:c7:71 - d5:8f:a7:5f:96:f1:74:90:a1:38:1c:8d:b6:37:04:23 - 81:70:24:29:62:b6:e4:85:8d:46:e9:4a:a0:26:12:0f - 40:69:42:25:eb:18:0a:97:93:dc:50:12:85:ff:74:6d - 71:31:d8:45:f8:94:74:ff:43:55:f6:fc:a3:ce:1e:cb - b9:d7:b8:2b:e5:c6:ab:d3:ab:77:60:9c:6b:4c:8e:c0 - 67:a2:37:41:a0:b8:ad:4a:bd:20:1c:29:c8:49:cc:69 - - -prime1: - 00:ed:49:8c:54:96:6b:fe:77:60:f1:93:dd:3d:bc:46 - b2:ec:9e:35:20:cc:8f:63:55:66:90:a4:1e:e3:50:b1 - 51:a3:a7:8b:b1:81:cd:93:cf:0d:4a:ac:c0:a1:81:49 - cb:71:0e:6b:4f:16:75:04:ae:89:53:c1:1d:ac:44:bc - ae:9d:85:85:e9:8c:aa:8e:b9:a8:3e:3d:86:28:b5:c3 - da:35:98:67:70:5a:8b:1f:c2:18:ed:b0:6a:0c:74:b9 - 33:6b:08:e5:93:87:39:b0:44:79:5c:eb:4c:f0:f1:db - c1:41:76:b0:12:46:38:4f:bd:68:db:70:53:13:e8:5f - 95: - -prime2: - 00:fa:40:7d:45:ec:7b:68:68:31:02:9a:ef:b7:a4:35 - a5:7d:d0:be:75:82:39:44:5f:31:98:4d:ff:3b:ec:76 - ce:c3:32:f9:d4:ce:bc:be:4c:3a:72:2f:1d:f6:2c:85 - 0d:15:50:2e:14:19:bb:cc:b5:ad:6c:bc:59:3f:a0:ba - 8b:82:e3:9d:36:93:40:b8:ec:d4:eb:15:59:da:ca:a7 - 10:1e:8e:de:22:c0:96:a5:cb:d3:37:37:4a:4b:58:aa - 13:76:84:58:21:0b:be:8a:b7:c9:04:fd:d9:99:0e:0c - 8a:28:52:50:23:9e:df:80:54:db:16:46:34:18:3b:da - c7: - -coefficient: - 7e:b9:c8:22:2e:b4:07:cd:a1:11:43:4d:48:79:e6:86 - a2:6d:3e:41:85:1e:01:3e:05:77:3d:88:2e:8c:a1:43 - b1:5c:03:3c:d9:37:d8:48:06:fa:bf:de:3e:ad:33:63 - b4:03:f7:84:02:26:22:95:66:03:1d:91:73:20:42:97 - 0e:5d:dd:37:1e:f3:60:80:1b:e4:19:0c:cb:75:bf:30 - fd:38:73:67:9c:c2:68:4c:ff:70:cb:78:6c:b7:5a:1c - a3:a2:cb:a1:f1:f4:17:06:9b:53:96:c3:19:0f:36:98 - 1e:11:f9:ba:a6:cb:5d:d5:82:ae:43:4f:cd:9e:e3:66 - - -exp1: - 2e:c1:d9:67:29:a4:ea:25:b7:f2:a2:82:6c:11:d7:94 - 96:4f:ae:84:62:0a:b7:36:32:d9:b9:9d:64:89:98:07 - 50:4a:49:9a:96:cb:5d:9e:e5:2d:9b:d0:f1:82:3a:7a - 5e:32:cb:2e:70:6c:6a:99:c1:f1:c1:12:09:ca:19:ac - 06:da:32:c3:0c:b6:e7:1c:ea:6c:29:4f:70:62:30:cf - a4:d3:fd:3e:04:79:79:ae:93:9e:f2:ae:52:fa:05:2c - 7e:a0:e8:2c:23:ef:58:2e:86:03:ab:52:24:00:64:9f - 36:39:1f:04:da:d5:69:d1:17:02:76:a5:c8:3c:77:e9 - - -exp2: - 00:84:d4:6a:2a:0d:45:cb:bb:52:18:51:e8:df:8e:d7 - b2:c9:bf:5c:f8:be:70:6b:2c:24:04:f5:91:7e:5b:1b - 0c:d0:6b:64:54:62:8f:a8:6a:89:b3:45:f3:1f:51:ae - 25:ad:a4:6b:70:db:df:e4:de:a1:f8:cf:58:87:ff:66 - 44:da:ea:b9:ed:d7:e7:48:c0:dc:9b:13:30:28:83:dc - 7d:1f:db:31:69:3c:d4:39:98:a0:b9:f4:2d:09:25:3c - d1:2b:dd:3f:71:fa:eb:de:71:82:cf:95:76:44:59:42 - aa:aa:90:56:5d:31:dc:ec:1f:1e:53:0a:5c:68:68:8c - cd: - - -Public Key PIN: - pin-sha256:ehI/YLeqtW2fBULTjESCxxpNnhvQYw9LOVxfrWzyV/g= -Public Key ID: - sha256:7a123f60b7aab56d9f0542d38c4482c71a4d9e1bd0630f4b395c5fad6cf257f8 - sha1:7ec2c93bfa8195429459015e334ef8d5735430b0 - ------BEGIN PRIVATE KEY----- -MIIE7QIBADA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAqEaMBgGCSqGSIb3 -DQEBCDALBglghkgBZQMEAgKiAwIBMASCBKcwggSjAgEAAoIBAQDn9ZmLaZViOlSA -uUwho9xQuayNUKSY6tqHVYIsxmjkNmx6uB4h2+b6H8JUOsCNLZTOFWZ2gtMnOf8R -+BmZlW1jfjU9F7YtWT/CtLNzdbiy551NfQ2Y47va4kRpvRVSDEXrJHBdR1V5Z1Ye -Ty/V4o7ClttfK27Fz08gYW8iUAWM5e9d4rvpr3mdiewZtO195/f3ILSwfFeixGZn -u+Ip45wHnLDfMDZAskUS7VNddU2mBOQv25KWlL7L6KzujShdlaafnCjTwodee3Le -8f8W+Enknd7neiAjaaSfaLLbsPz7wncNQQr/ZgLqnmvDCd18vB9HZmaLo3LplFBi -l1BaXi7TAgMBAAECggEAaTqW7JL6jPRPT5JAQmaW1RxWdklmUmUAvDKDepKMFTPH -ZKjQKqYbE8+CljmNDr7l6dP1hr/00K/T0jAOVQlf9anUtyFhqRL7BPZ7Dl8Saj7+ -sp+PopN1rmfFh36bBHzC31jJjNeGpCvH+roLxmkgQJC1dmg6uYxBpjvtcdCBpBfy -oR24tGsBbaLnmm6ftaEUYXxmUNzoJ2dVNlDMGdTHcdWPp1+W8XSQoTgcjbY3BCOB -cCQpYrbkhY1G6UqgJhIPQGlCJesYCpeT3FAShf90bXEx2EX4lHT/Q1X2/KPOHsu5 -17gr5car06t3YJxrTI7AZ6I3QaC4rUq9IBwpyEnMaQKBgQDtSYxUlmv+d2Dxk909 -vEay7J41IMyPY1VmkKQe41CxUaOni7GBzZPPDUqswKGBSctxDmtPFnUErolTwR2s -RLyunYWF6YyqjrmoPj2GKLXD2jWYZ3Baix/CGO2wagx0uTNrCOWThzmwRHlc60zw -8dvBQXawEkY4T71o23BTE+hflQKBgQD6QH1F7HtoaDECmu+3pDWlfdC+dYI5RF8x -mE3/O+x2zsMy+dTOvL5MOnIvHfYshQ0VUC4UGbvMta1svFk/oLqLguOdNpNAuOzU -6xVZ2sqnEB6O3iLAlqXL0zc3SktYqhN2hFghC76Kt8kE/dmZDgyKKFJQI57fgFTb -FkY0GDvaxwKBgC7B2WcppOolt/KigmwR15SWT66EYgq3NjLZuZ1kiZgHUEpJmpbL -XZ7lLZvQ8YI6el4yyy5wbGqZwfHBEgnKGawG2jLDDLbnHOpsKU9wYjDPpNP9PgR5 -ea6TnvKuUvoFLH6g6Cwj71guhgOrUiQAZJ82OR8E2tVp0RcCdqXIPHfpAoGBAITU -aioNRcu7UhhR6N+O17LJv1z4vnBrLCQE9ZF+WxsM0GtkVGKPqGqJs0XzH1GuJa2k -a3Db3+TeofjPWIf/ZkTa6rnt1+dIwNybEzAog9x9H9sxaTzUOZigufQtCSU80Svd -P3H6695xgs+VdkRZQqqqkFZdMdzsHx5TClxoaIzNAoGAfrnIIi60B82hEUNNSHnm -hqJtPkGFHgE+BXc9iC6MoUOxXAM82TfYSAb6v94+rTNjtAP3hAImIpVmAx2RcyBC -lw5d3Tce82CAG+QZDMt1vzD9OHNnnMJoTP9wy3hst1oco6LLofH0FwabU5bDGQ82 -mB4R+bqmy13Vgq5DT82e42Y= ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa_pss_512.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa_pss_512.pem deleted file mode 100644 index 923f6f92ea..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-key-rsa_pss_512.pem +++ /dev/null @@ -1,138 +0,0 @@ -Public Key Info: - Public Key Algorithm: RSA-PSS - Hash Algorithm: SHA512 - Salt Length: 64 - Key Security Level: Medium (2048 bits) - -modulus: - 00:c2:ce:73:69:95:63:26:85:bd:a0:23:25:5c:94:41 - 04:11:84:78:6c:c9:a3:47:13:47:3b:c4:fe:f3:27:6c - eb:d7:41:a9:4c:e3:15:40:b8:bd:99:02:df:93:a9:bf - 07:de:a2:f1:d1:1e:49:39:2b:64:c7:5e:bb:c7:dd:30 - 07:3c:10:2c:c9:bd:d9:8f:1e:04:60:c1:92:72:44:e6 - 3c:6e:6d:7f:b7:6f:fa:ab:2f:e3:69:7b:0d:1c:31:d5 - 5e:dd:ab:99:0d:4e:80:69:53:3d:d3:63:04:1f:d6:83 - fa:d7:04:c9:3f:75:9c:95:bd:45:34:39:1d:a0:1d:d7 - 06:8e:60:17:a3:94:8f:e9:30:1e:d2:ee:05:42:a4:08 - 86:b7:93:c2:5c:c2:5e:bc:c0:26:5e:98:56:c3:76:87 - e9:9b:1a:3b:f6:bf:c1:6a:4f:f8:46:ba:0b:a8:3a:5e - bb:1d:af:e3:f3:9b:f1:b6:18:70:6d:af:30:62:5f:07 - a9:ff:7b:a2:dd:5f:7e:ff:33:19:80:a2:d7:f9:9e:c5 - a5:22:e7:79:3c:b7:ee:4a:33:c7:c4:72:e6:69:fd:ec - 43:8b:85:86:07:95:15:b9:fe:ed:1c:12:38:ca:ed:cc - 71:ef:9b:69:11:16:e5:1e:78:e0:b3:4d:4c:b4:79:ea - 9f: - -public exponent: - 01:00:01: - -private exponent: - 00:84:ed:bb:73:60:ac:b7:ac:ab:28:8a:d3:03:c9:66 - 54:10:60:04:8c:b7:4a:e3:45:14:66:84:96:33:f5:c3 - 2d:6b:45:32:f1:74:43:1c:56:f3:89:65:9c:8a:76:5a - 14:54:a7:7b:ba:e6:9f:b0:93:1b:c1:af:b3:13:3e:ab - 77:44:55:05:3a:e4:81:80:57:4b:45:7a:d1:23:88:40 - 53:1c:47:3b:cf:40:6a:1c:46:21:37:e8:ef:99:3d:a8 - 0b:83:d7:84:28:c0:58:7f:86:7d:b9:b0:e7:2f:92:81 - 9c:b8:fc:5b:17:22:7a:26:f3:70:35:a2:83:c4:ae:97 - fa:7e:c6:3f:d2:39:9b:fe:f1:e9:c6:d1:68:3e:ac:26 - b4:69:27:c6:1f:50:fc:ab:32:bb:3c:90:13:7e:5c:c0 - 52:0c:34:5d:f7:bd:dd:84:ca:7c:c7:fe:91:8d:60:fe - d7:a7:e3:95:46:b2:ce:a1:4b:af:ba:81:e5:52:7c:68 - 65:5b:9c:84:a5:b6:44:0c:28:b7:c4:19:aa:f5:f7:06 - 35:ac:92:fe:1b:12:f9:17:8f:28:b7:d0:66:3b:a8:5e - 91:6e:c1:06:65:69:97:4e:75:26:59:12:76:3a:3d:9e - ee:21:b4:df:1e:e5:c1:73:5f:cd:e7:4a:2b:66:d8:cc - 81: - -prime1: - 00:c8:9b:6c:7f:a0:08:ce:09:9b:2a:ea:f3:2c:62:d1 - ec:1e:61:7f:da:d1:3a:38:a8:31:4c:57:fa:b9:1c:d8 - 27:fc:ff:d7:79:82:f1:3b:3a:b6:93:f3:61:c8:17:e2 - 73:c8:bc:66:ff:98:9d:5e:31:4f:6b:d5:98:d3:1a:eb - cc:30:ab:f6:ed:1b:62:a4:24:6c:cd:eb:20:9e:d8:52 - 8e:49:b9:47:11:97:2d:0c:89:6c:01:0a:f2:0e:6f:cf - 57:57:7c:57:ce:06:4b:a2:d1:e4:97:91:b2:3b:ef:2a - 38:d1:64:ea:6e:b0:57:c0:93:ed:d6:27:ba:dd:9e:53 - 0b: - -prime2: - 00:f8:98:fc:b3:55:a0:27:40:a8:e2:62:3d:80:4b:13 - 10:1c:a7:22:af:3d:47:57:c4:34:8c:76:4e:95:d7:ff - e8:03:bb:cf:ac:9d:52:3a:c2:d0:91:5f:1c:1d:36:4a - 7e:9d:6d:81:4a:6e:00:f8:96:85:a1:ab:3f:54:d2:03 - ad:0e:d2:c8:c6:fc:b4:62:7e:ab:57:aa:b7:2c:6b:10 - 01:66:5d:ab:d0:5a:9e:02:5b:ad:e1:ab:be:6e:b4:b4 - d1:61:d1:5f:19:22:5c:f5:4e:9e:bd:25:ab:94:a6:be - 8c:a5:7a:2d:2f:f9:5f:55:d3:b8:d8:6d:e9:7c:b5:03 - 3d: - -coefficient: - 7a:dc:e4:d8:ed:ce:71:72:63:b3:a8:4d:c0:1d:fa:a2 - 8a:c4:9f:77:1e:5a:e1:17:d3:1a:f8:20:32:54:30:a7 - 0d:69:40:92:d7:d6:43:bf:b5:83:7e:d5:19:44:bd:3c - 8d:ff:31:ad:8b:bd:6d:ab:a7:34:d7:e3:75:57:02:85 - 8a:c0:78:2d:10:0f:6f:28:da:f7:22:69:40:f4:04:9f - a5:f9:e2:a9:0d:88:06:b4:f3:3c:5e:c6:8c:96:69:7e - f6:09:fa:9c:c6:87:de:a2:a5:9b:4d:22:2a:0b:27:7c - 25:31:26:60:b6:6d:0f:97:6f:48:f2:bf:88:dc:f3:83 - - -exp1: - 1a:20:e4:48:db:37:4a:5e:c5:ef:19:1b:03:34:fb:d2 - 9d:42:65:bc:c2:73:aa:dd:7d:4e:4c:47:43:c5:16:02 - 5f:59:93:5f:28:46:f3:47:fa:6f:da:cb:69:9c:72:ca - 51:e2:f8:27:62:61:5c:db:5f:54:d4:45:4b:79:be:2c - a2:4a:43:a7:2e:61:f2:af:2b:dc:c6:3b:41:75:3b:8b - 7c:de:bc:fa:f5:8d:d0:8c:35:9d:0d:27:e9:e9:76:40 - 12:0d:08:02:b5:9f:34:5d:d2:40:4b:a1:c3:5c:ab:4b - 2b:3a:d1:ae:09:19:e4:e3:5f:9e:fd:1d:c1:af:d5:71 - - -exp2: - 00:f0:6c:55:08:c3:a8:ee:0d:74:c7:ec:a6:fa:2a:a1 - 37:15:de:f6:86:70:47:4d:34:6e:75:e1:fd:42:a1:f1 - d6:db:b5:89:b5:b1:38:d3:a7:91:ba:e6:36:f4:71:8b - 3e:44:d6:a1:11:f0:ad:73:bd:6f:63:d9:90:98:61:bc - 38:64:7b:aa:bd:f7:ac:25:0d:c8:7c:32:98:90:96:c2 - 95:f8:00:63:a8:4f:db:3d:00:99:7c:05:73:58:f1:df - 66:18:aa:3a:c4:be:1d:15:09:82:2f:ff:fc:9e:f9:5c - 93:fd:7d:d9:b1:ea:05:2f:a6:61:c0:bf:1b:ef:05:c9 - 29: - - -Public Key PIN: - pin-sha256:ucGhof6fwEGIty3XlESZXFYnapJIa30Xc+yIYUtVbKY= -Public Key ID: - sha256:b9c1a1a1fe9fc04188b72dd79444995c56276a92486b7d1773ec88614b556ca6 - sha1:f0f7effab3260ffefd0c2c57c196d20d0fd4bd3b - ------BEGIN PRIVATE KEY----- -MIIE7gIBADA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCA6EaMBgGCSqGSIb3 -DQEBCDALBglghkgBZQMEAgOiAwIBQASCBKgwggSkAgEAAoIBAQDCznNplWMmhb2g -IyVclEEEEYR4bMmjRxNHO8T+8yds69dBqUzjFUC4vZkC35OpvwfeovHRHkk5K2TH -XrvH3TAHPBAsyb3Zjx4EYMGSckTmPG5tf7dv+qsv42l7DRwx1V7dq5kNToBpUz3T -YwQf1oP61wTJP3Wclb1FNDkdoB3XBo5gF6OUj+kwHtLuBUKkCIa3k8Jcwl68wCZe -mFbDdofpmxo79r/Bak/4RroLqDpeux2v4/Ob8bYYcG2vMGJfB6n/e6LdX37/MxmA -otf5nsWlIud5PLfuSjPHxHLmaf3sQ4uFhgeVFbn+7RwSOMrtzHHvm2kRFuUeeOCz -TUy0eeqfAgMBAAECggEBAITtu3NgrLesqyiK0wPJZlQQYASMt0rjRRRmhJYz9cMt -a0Uy8XRDHFbziWWcinZaFFSne7rmn7CTG8GvsxM+q3dEVQU65IGAV0tFetEjiEBT -HEc7z0BqHEYhN+jvmT2oC4PXhCjAWH+Gfbmw5y+SgZy4/FsXInom83A1ooPErpf6 -fsY/0jmb/vHpxtFoPqwmtGknxh9Q/KsyuzyQE35cwFIMNF33vd2EynzH/pGNYP7X -p+OVRrLOoUuvuoHlUnxoZVuchKW2RAwot8QZqvX3BjWskv4bEvkXjyi30GY7qF6R -bsEGZWmXTnUmWRJ2Oj2e7iG03x7lwXNfzedKK2bYzIECgYEAyJtsf6AIzgmbKurz -LGLR7B5hf9rROjioMUxX+rkc2Cf8/9d5gvE7OraT82HIF+JzyLxm/5idXjFPa9WY -0xrrzDCr9u0bYqQkbM3rIJ7YUo5JuUcRly0MiWwBCvIOb89XV3xXzgZLotHkl5Gy -O+8qONFk6m6wV8CT7dYnut2eUwsCgYEA+Jj8s1WgJ0Co4mI9gEsTEBynIq89R1fE -NIx2TpXX/+gDu8+snVI6wtCRXxwdNkp+nW2BSm4A+JaFoas/VNIDrQ7SyMb8tGJ+ -q1eqtyxrEAFmXavQWp4CW63hq75utLTRYdFfGSJc9U6evSWrlKa+jKV6LS/5X1XT -uNht6Xy1Az0CgYAaIORI2zdKXsXvGRsDNPvSnUJlvMJzqt19TkxHQ8UWAl9Zk18o -RvNH+m/ay2mccspR4vgnYmFc219U1EVLeb4sokpDpy5h8q8r3MY7QXU7i3zevPr1 -jdCMNZ0NJ+npdkASDQgCtZ80XdJAS6HDXKtLKzrRrgkZ5ONfnv0dwa/VcQKBgQDw -bFUIw6juDXTH7Kb6KqE3Fd72hnBHTTRudeH9QqHx1tu1ibWxONOnkbrmNvRxiz5E -1qER8K1zvW9j2ZCYYbw4ZHuqvfesJQ3IfDKYkJbClfgAY6hP2z0AmXwFc1jx32YY -qjrEvh0VCYIv//ye+VyT/X3ZseoFL6ZhwL8b7wXJKQKBgHrc5NjtznFyY7OoTcAd -+qKKxJ93HlrhF9Ma+CAyVDCnDWlAktfWQ7+1g37VGUS9PI3/Ma2LvW2rpzTX43VX -AoWKwHgtEA9vKNr3ImlA9ASfpfniqQ2IBrTzPF7GjJZpfvYJ+pzGh96ipZtNIioL -J3wlMSZgtm0Pl29I8r+I3POD ------END PRIVATE KEY----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa-enc.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa-enc.pem deleted file mode 100644 index bd4bee0a74..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa-enc.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDQjCCAiqgAwIBAgIMWYGU8Ba6TGDaJ+vVMA0GCSqGSIb3DQEBCwUAMCMxITAf -BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAeFw0xNzA4MDIwOTAxMzZa -Fw0zNzA3MjgwOTAxMzZaMCMxITAfBgNVBAMTGEJvdW5jeUNhc3RsZSBUZXN0IFNl -cnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALeY49te/YFa6Od8 -0bFjD8k1vJbupBNoqdmiAT9oiHmbzqGKePtqC1XZQOwFe9pBJOuny9fAo2v0hhdU -sS9PmUzyaP4YOUnWOEpujU4Ntk9jtvWrl7VG0xSE1fcaBkN6+ssvRO3+D3XYoPhT -u9OxLZNDfmFc5DpuSc31VY2Fq3SVD8vrSrZCh892T0/J0VlX0LGSga/BXUGqJOkm -ISWgUB/eHTsaKb97cro6VTZyL4tvr+69IM+I4hVg8FRGZWOzj2wuFraMOPnUgjj0 -qk9snA1RP3TZWu+hHCGtY/wXFg7RDEb0nTsh2w0tAvNVYHHPTeore/5T9qQ7VBC7 -KtabDr0CAwEAAaN2MHQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcD -ATAPBgNVHQ8BAf8EBQMDByAAMB0GA1UdDgQWBBQwgymoaNHkuh20njTXz48/R+kl -yDAfBgNVHSMEGDAWgBQrQlWadXqqzBV+2iw7BZXrvFHaQjANBgkqhkiG9w0BAQsF -AAOCAQEA3kf9yAMYuPlBW2/y7UiKZGu6IVuxpmy+IFKeNIH6MNN9AsEHM1Yx4F1O -c4UGHxEPoKj5k1cEjiNH4hcaAR/Gukq7efHtf98WGlEp8E8ctjkK6Eu3+hOLHQ01 -YjV76BOkWzOI6DwFYNa71Jae44A8QUFhdq3c874KwwX2VkKcfenb7SzfWn/93DpR -L899PIzXWggADuRGb2S6L35DNk6sHQR8CwkT/HxZr/xqeAVP0qaXtkSPoPntVw6Z -MIUFcmC7XbDuyo1Or3iCMgdsuqP88KWDqP7vF2Bu8deES4wQeIGBSl4e6kweesbY -ASRKL9Qf3tPtKVGp0zvJbfSsOc+wig== ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa-sign.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa-sign.pem deleted file mode 100644 index 09818af868..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa-sign.pem +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDQjCCAiqgAwIBAgIMWYGU8DJRSWC13gO8MA0GCSqGSIb3DQEBCwUAMCMxITAf -BgNVBAMTGEJvdW5jeUNhc3RsZSBUTFMgVGVzdCBDQTAeFw0xNzA4MDIwOTAxMzZa -Fw0zNzA3MjgwOTAxMzZaMCMxITAfBgNVBAMTGEJvdW5jeUNhc3RsZSBUZXN0IFNl -cnZlcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALxfBvZ55WpVQeCh -tVyd+O/sLueGVkBlYURusklcLCqdthM5QTLBVMOQ2vtxWnZDSHWIvPUPCnPxR8Is -PvsM8BUqQjyj2d9CcvuNNgwKMIzUA10bQLPQ66ac1T7MU/2A4R+xxJN5ZOp4B+Uw -EwNAeAVfQ9XZX+HkmVAjKPJiRzmjdzoAslQR0F5zL11gjmS8STwZuJ+2MpUh09VG -dHJ+LB1CNVfnExwPqR3wd1TQPULXZN3Dx+f0W27gmA4GRitMtLltAljvZzGt6+xh -byKJJjMnTBWlzFFiP/ggpcPs+q2hW9i3vPCE5eOGdkbDnXJi3aiBxYpTKeS54wGc -/OkTv5UCAwEAAaN2MHQwDAYDVR0TAQH/BAIwADATBgNVHSUEDDAKBggrBgEFBQcD -ATAPBgNVHQ8BAf8EBQMDB4AAMB0GA1UdDgQWBBTXwO7kKzgzAOC9j607abavBMKy -aTAfBgNVHSMEGDAWgBQrQlWadXqqzBV+2iw7BZXrvFHaQjANBgkqhkiG9w0BAQsF -AAOCAQEAAYRsE0f2BkOlNWj1SNiwoWkw4Ic6WW1X43yh3L17c5hRLOGPupDngJBA -EYTmsEfq26XX4mGlKXKgKZW2ijcrIQSkHEjkXHYdU+OI+4xufeNtGMYLijcwVKXM -mmSe8ERWODdCfzDJSRdLx/NgLeWphMQLaym1TkK29lASyqIbCNATXGnMw3bcMLvU -cXnjHtNU1tlYtVNCStvm2buy+vdVEGIsusqk+aetniEttBlQcPwuRMvbYmIDuRJP -Bxsst/t32W+s8Eb2dqbSKm6TucOG5o7oXKH6l15d+ARRJJ3C3nODBvsYgSqg1Vtm -7H0K9I4dLZoMUb4tWjSA9ckNU0nuKQ== ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa_pss_256.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa_pss_256.pem deleted file mode 100644 index 0ebfa2e204..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa_pss_256.pem +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIID2jCCApKgAwIBAgIUODLkLENJ2mZJLwlk5OXY6ebyHp8wPQYJKoZIhvcNAQEK -MDCgDTALBglghkgBZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMC -ASAwIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxlIFRMUyBUZXN0IENBMB4XDTE4MTAy -NDA2MDY0MloXDTM4MTAxOTA2MDY0MlowIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxl -IFRlc3QgU2VydmVyMIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAaEa -MBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgGiAwIBIAOCAQ8AMIIBCgKCAQEAodS9 -1Z/KfuXUf0lSeDPWsC83pKL4SzK179UpnH0urGmwsRoJffGSOq7E0SljG11g000G -P7x/RsciwpjgLDL7mpTx6rk7N0tEi79gKBNr6TGmD+I9gmCdKIvUY3/hBs/WgaEk -77SlOgWVFuXGneFuMVaSExbEK1Mgc1eWnXpLvujwUBpVGB7ZafL2teU6mLZ3e/8w -dJ9tgVVQtT6BjVT2zBc9GxC/aXcJ0di+eRGsO74ZaVyxUE26BAmYdHrjlkhN/Gd6 -NHN2SF5BBWFaaFk5s4aqeCj0pkrbb4VHdXWiJOkhPxjJRoeRgBr1+u0rqwPo7cPd -Z61G65t+RNJcdks9XwIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG -CCsGAQUFBwMBMA8GA1UdDwEB/wQFAwMHgAAwHQYDVR0OBBYEFAbF9tL09Ej6Z7oS -/pVe++Ff69FkMB8GA1UdIwQYMBaAFCEaiLbeA8ABgKrbcIiZpo8XzqpsMD0GCSqG -SIb3DQEBCjAwoA0wCwYJYIZIAWUDBAIBoRowGAYJKoZIhvcNAQEIMAsGCWCGSAFl -AwQCAaIDAgEgA4IBAQBQJnvKt+zUFenmqBo8zGAUQdNYcIp5JhmTP2JbgWlNjTkj -Tsc3i/rWKA0pxydWgQPL6VKBPiqFIQcuPEw6D9zQxRQpnOveRDTkDzcLbt/c5asO -6TpHYqlSujeW7TEH0WLBg0uAHuRUclKYB1/2gSU0MUVtG/sZkh213vEx56F56iqx -sXFDnt9pyu0tLE0nWWtY3dxGAYJpL1HGZ2ey//Tf0+lsS0t8iH0vAtUmssKJpA79 -76biXCHAVcL07O5jMwrF4v7ki/G7RQRQ+qrL03xBifAzKuczu4Ls++Wg7V4MUERj -mpw4pbRBam9Sz3uwc1qA+Ul0TCHzMDAFWyMod0ND ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa_pss_384.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa_pss_384.pem deleted file mode 100644 index 3506f48b27..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa_pss_384.pem +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIID2jCCApKgAwIBAgIUBSXp/i1JJIcj2ytjBNKJ0tbxKjYwPQYJKoZIhvcNAQEK -MDCgDTALBglghkgBZQMEAgKhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAICogMC -ATAwIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxlIFRMUyBUZXN0IENBMB4XDTE4MTAy -NDA3NTY0OVoXDTM4MTAxOTA3NTY0OVowIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxl -IFRlc3QgU2VydmVyMIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCAqEa -MBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgKiAwIBMAOCAQ8AMIIBCgKCAQEA5/WZ -i2mVYjpUgLlMIaPcULmsjVCkmOrah1WCLMZo5DZsergeIdvm+h/CVDrAjS2UzhVm -doLTJzn/EfgZmZVtY341PRe2LVk/wrSzc3W4suedTX0NmOO72uJEab0VUgxF6yRw -XUdVeWdWHk8v1eKOwpbbXytuxc9PIGFvIlAFjOXvXeK76a95nYnsGbTtfef39yC0 -sHxXosRmZ7viKeOcB5yw3zA2QLJFEu1TXXVNpgTkL9uSlpS+y+is7o0oXZWmn5wo -08KHXnty3vH/FvhJ5J3e53ogI2mkn2iy27D8+8J3DUEK/2YC6p5rwwndfLwfR2Zm -i6Ny6ZRQYpdQWl4u0wIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG -CCsGAQUFBwMBMA8GA1UdDwEB/wQFAwMHgAAwHQYDVR0OBBYEFH7CyTv6gZVClFkB -XjNO+NVzVDCwMB8GA1UdIwQYMBaAFKnwXW9hoPAB6HSyWn7UEUMcKonnMD0GCSqG -SIb3DQEBCjAwoA0wCwYJYIZIAWUDBAICoRowGAYJKoZIhvcNAQEIMAsGCWCGSAFl -AwQCAqIDAgEwA4IBAQAt1xrrBujilh2NVclsaRoqD+QJ7QhDdFVbS2zBJTVz7nds -TMu9iU/dXAp2zdsmpXi3mKStgYWS1yjSdyKyBy4v7EwGx5qHrcnMzyfX2BelGDk7 -OYCXGaFweAqRUh8SchYA1+Dlwg7ub+SpGkDnA76LgBcHFoC7AMozLOeY6GNT4qdq -64+jFvCFrDVC4xMvMB3+vwdlDIIyr9uY3z9Axw35IRPyVHJnyiLu/OIwD7Vw9A4n -11WrUXZ2fvqJUcSvwqdHRAy1kj9LsagfEXatJ79ZC2En2XhEs+PHTgtqw5UdPk16 -dVIlgPTQ/Te+ttubJPsKOpgKj8YOiNtnEZdzpJUP ------END CERTIFICATE----- diff --git a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa_pss_512.pem b/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa_pss_512.pem deleted file mode 100644 index 51e323b43c..0000000000 --- a/tls/src/test/resources/org/bouncycastle/tls/test/x509-server-rsa_pss_512.pem +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIID2jCCApKgAwIBAgIUV12PGXPx0Jj7USRKsKNrWFqJRHEwPQYJKoZIhvcNAQEK -MDCgDTALBglghkgBZQMEAgOhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIDogMC -AUAwIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxlIFRMUyBUZXN0IENBMB4XDTE4MTAy -NDA2MDY0M1oXDTM4MTAxOTA2MDY0M1owIzEhMB8GA1UEAxMYQm91bmN5Q2FzdGxl -IFRlc3QgU2VydmVyMIIBUjA9BgkqhkiG9w0BAQowMKANMAsGCWCGSAFlAwQCA6Ea -MBgGCSqGSIb3DQEBCDALBglghkgBZQMEAgOiAwIBQAOCAQ8AMIIBCgKCAQEAws5z -aZVjJoW9oCMlXJRBBBGEeGzJo0cTRzvE/vMnbOvXQalM4xVAuL2ZAt+Tqb8H3qLx -0R5JOStkx167x90wBzwQLMm92Y8eBGDBknJE5jxubX+3b/qrL+Npew0cMdVe3auZ -DU6AaVM902MEH9aD+tcEyT91nJW9RTQ5HaAd1waOYBejlI/pMB7S7gVCpAiGt5PC -XMJevMAmXphWw3aH6ZsaO/a/wWpP+Ea6C6g6Xrsdr+Pzm/G2GHBtrzBiXwep/3ui -3V9+/zMZgKLX+Z7FpSLneTy37kozx8Ry5mn97EOLhYYHlRW5/u0cEjjK7cxx75tp -ERblHnjgs01MtHnqnwIDAQABo3YwdDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoG -CCsGAQUFBwMBMA8GA1UdDwEB/wQFAwMHgAAwHQYDVR0OBBYEFPD37/qzJg/+/Qws -V8GW0g0P1L07MB8GA1UdIwQYMBaAFKVY4bgN1p+8NOXoPdj6DKWDscBFMD0GCSqG -SIb3DQEBCjAwoA0wCwYJYIZIAWUDBAIDoRowGAYJKoZIhvcNAQEIMAsGCWCGSAFl -AwQCA6IDAgFAA4IBAQCVRYStZNCUKNzu+kBiHBVJn5Dbu97s3jHBw0ACphUp+M75 -O1ohoT+ny3wgIJx4sN1Fc80cT24o1V48nI4nJTlgAhYyjYjaMilHpwLP4oLclQX8 -OUoyoTPIIRfP4UNRmxAtH+2eEGieO1QDsYyqsGKR9DeWme4t4dc/NTuZ8/E3UW9C -m0VO0ev3m8ZGfWANRP0yyjnvke4I5awFur9ncGn3vwZYJLDlV9dwi3B68VUzt4Um -jz4yhgCU+kOqID/HXgWUCosreQGN+KqungUlENOVvBV4sTfQpnaAJaKpT4zkEYMP -sRkBrV12dCYh4NIpqDsnjSpWEuDlpT4F3hJx2BqU ------END CERTIFICATE----- From e356f4ab9d151b2276f989f32aad2c2ca26187dd Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 3 Mar 2025 11:02:36 +1030 Subject: [PATCH 138/890] Pass all test vectors of Mayo --- .../pqc/crypto/mayo/GF16Utils.java | 281 ++++- .../pqc/crypto/mayo/MayoEngine.java | 89 +- .../pqc/crypto/mayo/MayoSigner.java | 1085 +++++++++++++++++ .../bouncycastle/pqc/crypto/mayo/Utils.java | 39 + .../pqc/crypto/test/AllTests.java | 1 + .../pqc/crypto/test/MayoTest.java | 53 +- .../pqc/crypto/test/TestUtils.java | 67 +- 7 files changed, 1545 insertions(+), 70 deletions(-) create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java index 9d42c4a5ca..cdcc073d8e 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java @@ -1,5 +1,7 @@ package org.bouncycastle.pqc.crypto.mayo; +import org.bouncycastle.util.Pack; + public class GF16Utils { @@ -105,7 +107,7 @@ public static void mulAddMUpperTriangularMatXMat(int mVecLimbs, long[] bsMat, by int a = mat[c * matCols + k] & 0xFF; // For acc: add into the m-vector at row r, column k. int accOffset = (r * matCols + k) * mVecLimbs; - GF16Utils.mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, a, acc, accOffset); + mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, a, acc, accOffset); } bsMatEntriesUsed++; } @@ -162,7 +164,7 @@ public static void mulAddMatTransXMMat(int mVecLimbs, byte[] mat, long[] bsMat, int a = mat[c * matCols + r] & 0xFF; // For acc: add into the m-vector at index (r * bsMatCols + k) int accOffset = (r * bsMatCols + k) * mVecLimbs; - GF16Utils.mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, a, acc, accOffset); + mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, a, acc, accOffset); } } } @@ -181,5 +183,280 @@ public static void mVecAdd(int mVecLimbs, long[] src, int srcOffset, long[] dest } } + /** + * Multiplies a matrix (given as a byte array) with a bit‐sliced matrix (given as a long array) + * and accumulates the result into the acc array. + * + *

+ * The operation iterates over the rows and columns of the matrix. For each element in the matrix, + * it multiplies a corresponding vector (from bsMat) by the scalar value (from mat) and adds the + * result to the accumulator vector in acc. + *

+ * + * @param mVecLimbs the number of limbs (elements) in each vector + * @param mat the matrix as a byte array with dimensions [matRows x matCols] + * @param bsMat the bit‐sliced matrix as a long array + * @param acc the accumulator array (long[]) where results are accumulated + * @param matRows the number of rows in the matrix + * @param matCols the number of columns in the matrix + * @param bsMatCols the number of columns in the bit‐sliced matrix (per block) + */ + public static void mulAddMatXMMat(int mVecLimbs, byte[] mat, long[] bsMat, long[] acc, + int matRows, int matCols, int bsMatCols) + { + for (int r = 0; r < matRows; r++) + { + for (int c = 0; c < matCols; c++) + { + // Retrieve the scalar from the matrix for row r and column c. + byte matVal = mat[r * matCols + c]; + for (int k = 0; k < bsMatCols; k++) + { + // Compute the starting index for the vector in bsMat. + int bsMatOffset = mVecLimbs * (c * bsMatCols + k); + // Compute the starting index for the accumulator vector in acc. + int accOffset = mVecLimbs * (r * bsMatCols + k); + // Multiply the vector by the scalar and add the result to the accumulator. + mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, matVal, acc, accOffset); + } + } + } + } + + public static void mulAddMatXMMat(int mVecLimbs, byte[] mat, long[] bsMat, int bsMatOff, long[] acc, + int matRows, int matCols, int bsMatCols) + { + for (int r = 0; r < matRows; r++) + { + for (int c = 0; c < matCols; c++) + { + // Retrieve the scalar from the matrix for row r and column c. + byte matVal = mat[r * matCols + c]; + for (int k = 0; k < bsMatCols; k++) + { + // Compute the starting index for the vector in bsMat. + int bsMatOffset = mVecLimbs * (c * bsMatCols + k) + bsMatOff; + // Compute the starting index for the accumulator vector in acc. + int accOffset = mVecLimbs * (r * bsMatCols + k); + // Multiply the vector by the scalar and add the result to the accumulator. + mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, matVal, acc, accOffset); + } + } + } + } + + /** + * Multiplies m (possibly upper triangular) matrices with the transpose of a single matrix + * and adds the result to the accumulator. + * + *

+ * For each row {@code r} in the bit‑sliced matrix and for each column {@code c} (starting from + * {@code triangular * r}) in the bit‑sliced matrix, this method iterates over all rows {@code k} + * of the single matrix, and for each element, it multiplies the vector (from {@code bsMat}) + * by the scalar (from {@code mat}) and adds the result to the corresponding vector in {@code acc}. + *

+ * + * @param mVecLimbs the number of limbs (elements) in each vector. + * @param bsMat the bit‑sliced matrix stored as a long array. + * @param mat the matrix stored as a byte array. + * @param acc the accumulator array where the results are added. + * @param bsMatRows the number of rows in the bit‑sliced matrix. + * @param bsMatCols the number of columns in the bit‑sliced matrix. + * @param matRows the number of rows in the matrix. + * @param triangular if non‑zero, indicates that the matrix is upper triangular (i.e. the loop for {@code c} + * starts at {@code triangular * r}). + */ + public static void mulAddMUpperTriangularMatXMatTrans(int mVecLimbs, long[] bsMat, byte[] mat, long[] acc, + int bsMatRows, int bsMatCols, int matRows, int triangular) + { + int bsMatEntriesUsed = 0; + for (int r = 0; r < bsMatRows; r++) + { + // For upper triangular, start c at triangular * r; otherwise, triangular is zero. + for (int c = triangular * r; c < bsMatCols; c++) + { + for (int k = 0; k < matRows; k++) + { + int bsMatOffset = mVecLimbs * bsMatEntriesUsed; + int accOffset = mVecLimbs * (r * matRows + k); + // Get the matrix element at row k and column c + byte matVal = mat[k * bsMatCols + c]; + mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, matVal, acc, accOffset); + } + bsMatEntriesUsed++; + } + } + } + + /** + * Multiplies a vector (from bsMat) by an unsigned scalar (from mat) and adds the result + * to the corresponding vector in acc. + * + *

+ * This method corresponds to the C function m_vec_mul_add. + * It processes {@code mVecLimbs} elements starting from the given offsets in the source and accumulator arrays. + *

+ * + * @param mVecLimbs the number of limbs (elements) in the vector + * @param bsMat the source array (bit-sliced matrix) of long values + * @param bsMatOffset the starting index in bsMat for the vector + * @param scalar the scalar value (from mat), as a byte + * @param acc the accumulator array where the result is added + * @param accOffset the starting index in the accumulator array for the current vector + */ + public static void mVecMulAdd(int mVecLimbs, long[] bsMat, int bsMatOffset, byte scalar, long[] acc, int accOffset) + { + for (int i = 0; i < mVecLimbs; i++) + { + acc[accOffset + i] ^= gf16vMulU64(bsMat[bsMatOffset + i], scalar); + } + } + + /** + * GF(16) multiplication mod x^4 + x + 1. + *

+ * This method multiplies two elements in GF(16) (represented as integers 0–15) + * using carryless multiplication followed by reduction modulo x^4 + x + 1. + * + * @param a an element in GF(16) (only the lower 4 bits are used) + * @param b an element in GF(16) (only the lower 4 bits are used) + * @return the product a * b in GF(16) + */ + public static int mulF(int a, int b) + { + // In C there is a conditional XOR with unsigned_char_blocker to work around + // compiler-specific behavior. In Java we can omit it (or define it as needed). + // a ^= unsignedCharBlocker; // Omitted in Java + + // Perform carryless multiplication: + // Multiply b by each bit of a and XOR the results. + int p = ((a & 1) * b) ^ + ((a & 2) * b) ^ + ((a & 4) * b) ^ + ((a & 8) * b); + + // Reduce modulo f(X) = x^4 + x + 1. + // Extract the upper nibble (bits 4 to 7). + int topP = p & 0xF0; + // The reduction: XOR p with (topP shifted right by 4 and by 3) and mask to 4 bits. + int out = (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0F; + return out; + } + + /** + * Performs a GF(16) carryless multiplication of a nibble (lower 4 bits of a) + * with a 64-bit word b, then reduces modulo the polynomial x⁴ + x + 1 on each byte. + * + * @param a a GF(16) element (only the low 4 bits are used) + * @param b a 64-bit word representing 16 GF(16) elements (packed 4 bits per element) + * @return the reduced 64-bit word after multiplication + */ + public static long mulFx8(byte a, long b) + { + // Convert 'a' to an unsigned int so that bit operations work as expected. + int aa = a & 0xFF; + // Carryless multiplication: for each bit in 'aa' (considering only the lower 4 bits), + // if that bit is set, multiply 'b' (by 1, 2, 4, or 8) and XOR the result. + long p = ((aa & 1) * b) + ^ ((aa & 2) * b) + ^ ((aa & 4) * b) + ^ ((aa & 8) * b); + + // Reduction mod (x^4 + x + 1): process each byte in parallel. + long topP = p & 0xf0f0f0f0f0f0f0f0L; + long out = (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0f0f0f0f0f0f0f0fL; + return out; + } + + public static void matMul(byte[] a, byte[] b, byte[] c, + int colrowAB, int rowA, int colB) + { + int cIndex = 0; + for (int i = 0; i < rowA; i++) + { + int aRowStart = i * colrowAB; + for (int j = 0; j < colB; j++) + { + c[cIndex++] = lincomb(a, aRowStart, b, j, colrowAB, colB); + } + } + } + + public static void matMul(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int cOff, + int colrowAB, int rowA, int colB) + { + int cIndex = 0; + for (int i = 0; i < rowA; i++) + { + int aRowStart = i * colrowAB; + for (int j = 0; j < colB; j++) + { + c[cOff + cIndex++] = lincomb(a, aOff + aRowStart, b, bOff + j, colrowAB, colB); + } + } + } + + + private static byte lincomb(byte[] a, int aStart, byte[] b, int bStart, + int colrowAB, int colB) + { + byte result = 0; + for (int k = 0; k < colrowAB; k++) + { + result ^= mulF(a[aStart + k], b[bStart + k * colB]); + } + return result; + } + + public static void matAdd(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int cOff, int m, int n) + { + for (int i = 0; i < m; i++) + { + for (int j = 0; j < n; j++) + { + int idx = i * n + j; + c[idx + cOff] = (byte)(a[idx + aOff] ^ b[idx + bOff]); + } + } + } + + // Define the blocker constant as needed (set to 0 if not used). + private static final byte UNSIGNED_CHAR_BLOCKER = 0; + + /** + * Returns 0x00 if a equals b, otherwise returns 0xFF. + * This operation is performed in constant time. + * + * @param a an 8-bit value + * @param b an 8-bit value + * @return 0x00 if a == b, 0xFF if a != b + */ + public static byte ctCompare8(byte a, byte b) + { + // Compute the difference between a and b using XOR. + // Masking with 0xFF ensures we work with values in 0..255. + int diff = (a ^ b) & 0xFF; + // Negate the difference. + int negDiff = -diff; + // Right shift by 31 bits (since 8*sizeof(uint32_t)-1 equals 31 for 32-bit integers). + // If diff is 0, then -diff is 0, and shifting yields 0. + // If diff is nonzero, -diff is negative, so the arithmetic shift yields -1 (0xFFFFFFFF), + // which when cast to a byte becomes 0xFF. + int result = negDiff >> 31; + // XOR with UNSIGNED_CHAR_BLOCKER (assumed 0 here) and cast to byte. + return (byte)(result ^ UNSIGNED_CHAR_BLOCKER); + } + + public static void efUnpackMVector(int legs, long[] packedRow, int packedRowOff, byte[] out) + { + int outIndex = 0; + byte[] bytes = new byte[out.length >> 1]; + Pack.longToLittleEndian(packedRow, packedRowOff, out.length >> 4, bytes, 0); + for (int i = 0; i < legs * 16; i += 2) + { + out[outIndex++] = (byte)(bytes[i / 2] & 0x0F); // Lower nibble + out[outIndex++] = (byte)((bytes[i / 2] >> 4) & 0x0F); // Upper nibble + } + } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoEngine.java index 51cf98bc74..f93e2848da 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoEngine.java @@ -91,62 +91,55 @@ public static int AES_128_CTR(byte[] output, int outputByteLen, byte[] key, int } public static final int MAYO_OK = 0; - public static final int PK_SEED_BYTES_MAX = 16; // Adjust as needed - public static final int O_BYTES_MAX = 312; // Adjust as needed /** * Expands the secret key. * * @param p the MayoParameters instance. * @param csk the input secret key seed (byte array). - * @param sk the Sk object that holds the expanded secret key components. * @return MAYO_OK on success. */ -// public static int mayoExpandSk(MayoParameters p, byte[] csk, MayoPrivateKeyParameter sk) -// { -// int ret = MAYO_OK; -// int totalS = PK_SEED_BYTES_MAX + O_BYTES_MAX; -// byte[] S = new byte[totalS]; -// -// // sk.p is the long[] array, sk.O is the byte[] array. -// -// long[] P = new long[p.getPkSeedBytes() >> 3]; -// Pack.littleEndianToLong(sk.getP(), 0, P); -// byte[] O = sk.getO(); -// -// int param_o = p.getO(); -// int param_v = p.getV(); -// int param_O_bytes = p.getOBytes(); -// int param_pk_seed_bytes = p.getPkSeedBytes(); -// int param_sk_seed_bytes = p.getSkSeedBytes(); -// -// // In C, seed_sk = csk and seed_pk = S (the beginning of S) -// byte[] seed_sk = csk; -// byte[] seed_pk = S; // first param_pk_seed_bytes of S -// -// // Generate S = seed_pk || (additional bytes), using SHAKE256. -// // Output length is param_pk_seed_bytes + param_O_bytes. -// Utils.shake256(S, param_pk_seed_bytes + param_O_bytes, seed_sk, param_sk_seed_bytes); -// -// // Decode the portion of S after the first param_pk_seed_bytes into O. -// // (In C, this is: decode(S + param_pk_seed_bytes, O, param_v * param_o)) -// Utils.decode(S, param_pk_seed_bytes, O, param_v * param_o); -// -// // Expand P1 and P2 into the long array P using seed_pk. -// MayoEngine.expandP1P2(p, P, seed_pk); -// -// // Let P2 start at offset = PARAM_P1_limbs(p) -// int p1Limbs = p.getP1Limbs(); -// int offsetP2 = p1Limbs; -// -// // Compute L_i = (P1 + P1^t)*O + P2. -// // Here, we assume that P1P1tTimesO writes into the portion of P starting at offsetP2. -// P1P1tTimesO(p, P, O, P, offsetP2); -// -// // Securely clear sensitive temporary data. -// java.util.Arrays.fill(S, (byte)0); -// return ret; -// } + public static int mayoExpandSk(MayoParameters p, byte[] csk, long[] P, byte[] O) + { + int ret = MAYO_OK; + int totalS = p.getPkSeedBytes() + p.getOBytes(); + byte[] S = new byte[totalS]; + + // sk.p is the long[] array, sk.O is the byte[] array. + + int param_o = p.getO(); + int param_v = p.getV(); + int param_O_bytes = p.getOBytes(); + int param_pk_seed_bytes = p.getPkSeedBytes(); + int param_sk_seed_bytes = p.getSkSeedBytes(); + + // In C, seed_sk = csk and seed_pk = S (the beginning of S) + byte[] seed_sk = csk; + byte[] seed_pk = S; // first param_pk_seed_bytes of S + + // Generate S = seed_pk || (additional bytes), using SHAKE256. + // Output length is param_pk_seed_bytes + param_O_bytes. + Utils.shake256(S, param_pk_seed_bytes + param_O_bytes, seed_sk, param_sk_seed_bytes); + + // Decode the portion of S after the first param_pk_seed_bytes into O. + // (In C, this is: decode(S + param_pk_seed_bytes, O, param_v * param_o)) + Utils.decode(S, param_pk_seed_bytes, O, param_v * param_o); + + // Expand P1 and P2 into the long array P using seed_pk. + MayoEngine.expandP1P2(p, P, seed_pk); + + // Let P2 start at offset = PARAM_P1_limbs(p) + int p1Limbs = p.getP1Limbs(); + int offsetP2 = p1Limbs; + + // Compute L_i = (P1 + P1^t)*O + P2. + // Here, we assume that P1P1tTimesO writes into the portion of P starting at offsetP2. + P1P1tTimesO(p, P, O, P, offsetP2); + + // Securely clear sensitive temporary data. + java.util.Arrays.fill(S, (byte)0); + return ret; + } /** * Multiplies and accumulates the product (P1 + P1^t)*O into the accumulator. diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java new file mode 100644 index 0000000000..0b2c9196f2 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -0,0 +1,1085 @@ +package org.bouncycastle.pqc.crypto.mayo; + +import java.security.SecureRandom; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.digests.SHAKEDigest; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.pqc.crypto.MessageSigner; + +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Pack; + +public class MayoSigner + implements MessageSigner +{ + private SecureRandom random; + MayoParameters params; + MayoEngine engine; + private MayoPublicKeyParameter pubKey; + private MayoPrivateKeyParameter privKey; + + @Override + public void init(boolean forSigning, CipherParameters param) + { + + if (forSigning) + { + pubKey = null; + + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom withRandom = (ParametersWithRandom)param; + privKey = (MayoPrivateKeyParameter)withRandom.getParameters(); + random = withRandom.getRandom(); + } + else + { + privKey = (MayoPrivateKeyParameter)param; + random = null; + } + params = privKey.getParameters(); + } + else + { + pubKey = (MayoPublicKeyParameter)param; + params = pubKey.getParameters(); + privKey = null; + random = null; + } + } + + @Override + public byte[] generateSignature(byte[] message) + { + byte[] tenc = new byte[params.getMBytes()]; + byte[] t = new byte[params.getM()]; + byte[] y = new byte[params.getM()]; + byte[] salt = new byte[params.getSaltBytes()]; + byte[] V = new byte[params.getK() * params.getVBytes() + params.getRBytes()]; + byte[] Vdec = new byte[params.getV() * params.getK()]; + byte[] A = new byte[((params.getM() + 7) / 8 * 8) * (params.getK() * params.getO() + 1)]; + byte[] x = new byte[params.getK() * params.getN()]; + byte[] r = new byte[params.getK() * params.getO() + 1]; + byte[] s = new byte[params.getK() * params.getN()]; + byte[] tmp = new byte[params.getDigestBytes() + params.getSaltBytes() + + params.getSkSeedBytes() + 1]; + byte[] sig = new byte[params.getSigBytes()]; + + try + { + long[] P = new long[params.getP1Limbs() + params.getP2Limbs()]; + byte[] O = new byte[params.getV() * params.getO()]; + // Expand secret key + MayoEngine.mayoExpandSk(params, privKey.getSeedSk(), P, O); + + // Hash message + SHAKEDigest shake = new SHAKEDigest(256); + shake.update(message, 0, message.length); + shake.doFinal(tmp, 0, params.getDigestBytes()); + + // Generate random salt + random.nextBytes(salt); + + System.arraycopy(salt, 0, tmp, params.getDigestBytes(), salt.length); + + // Hash to salt + System.arraycopy(privKey.getSeedSk(), 0, tmp, params.getDigestBytes() + params.getSaltBytes(), + params.getSkSeedBytes()); + + shake.update(tmp, 0, params.getDigestBytes() + params.getSaltBytes() + + params.getSkSeedBytes()); + shake.doFinal(salt, 0, params.getSaltBytes()); + + // Hash to t + System.arraycopy(salt, 0, tmp, params.getDigestBytes(), params.getSaltBytes()); + shake.update(tmp, 0, params.getDigestBytes() + params.getSaltBytes()); + shake.doFinal(tenc, 0, params.getMBytes()); + Utils.decode(tenc, t, params.getM()); + + for (int ctr = 0; ctr <= 255; ctr++) + { + tmp[tmp.length - 1] = (byte)ctr; + + // Generate V + shake.update(tmp, 0, tmp.length); + shake.doFinal(V, 0, V.length); + + // Decode vectors + for (int i = 0; i < params.getK(); i++) + { + Utils.decode(V, i * params.getVBytes(), Vdec, i * params.getV(), params.getV()); + } + + // Compute matrices + long[] Mtmp = new long[params.getK() * params.getO() * params.getMVecLimbs()]; + long[] vPv = new long[params.getK() * params.getK() * params.getMVecLimbs()]; + computeMandVPV(params, Vdec, P, params.getP1Limbs(), P, Mtmp, vPv); + + computeRHS(params, vPv, t, y); + computeA(params, Mtmp, A); + + // Clear trailing bytes + for (int i = 0; i < params.getM(); ++i) + { + A[(i + 1) * (params.getK() * params.getO() + 1) - 1] = 0; + } + + Utils.decode(V, params.getK() * params.getVBytes(), r, 0, + params.getK() * params.getO()); + + if (sampleSolution(params, A, y, r, x) != 0) + { + break; + } + else + { + Arrays.fill(Mtmp, 0L); + Arrays.fill(vPv, 0L); + } + } + + // Compute final signature components + byte[] Ox = new byte[params.getV()]; + for (int i = 0; i < params.getK(); i++) + { + byte[] vi = Arrays.copyOfRange(Vdec, i * params.getV(), + (i + 1) * params.getV()); + GF16Utils.matMul(O, 0, x, i * params.getO(), Ox, 0, + params.getO(), params.getN() - params.getO(), 1); + GF16Utils.matAdd(vi, 0, Ox, 0, s, i * params.getN(), params.getV(), 1); + System.arraycopy(x, i * params.getO(), s, + i * params.getN() + params.getN() - params.getO(), params.getO()); + } + + // Encode and add salt + Utils.encode(s, sig, params.getN() * params.getK()); + System.arraycopy(salt, 0, sig, sig.length - params.getSaltBytes(), + params.getSaltBytes()); + + return Arrays.concatenate(sig, message); + } + finally + { + // Secure cleanup + Arrays.fill(tenc, (byte)0); + Arrays.fill(t, (byte)0); + Arrays.fill(y, (byte)0); + Arrays.fill(salt, (byte)0); + Arrays.fill(V, (byte)0); + Arrays.fill(Vdec, (byte)0); + Arrays.fill(A, (byte)0); + Arrays.fill(x, (byte)0); + Arrays.fill(r, (byte)0); + Arrays.fill(s, (byte)0); + Arrays.fill(tmp, (byte)0); + } + } + + + @Override + public boolean verifySignature(byte[] message, byte[] signature) + { + final int paramM = params.getM(); + final int paramN = params.getN(); + final int paramK = params.getK(); + final int paramMBytes = params.getMBytes(); + final int paramSigBytes = params.getSigBytes(); + final int paramDigestBytes = params.getDigestBytes(); + final int paramSaltBytes = params.getSaltBytes(); + + byte[] tEnc = new byte[params.getMBytes()]; + byte[] t = new byte[params.getM()]; + byte[] y = new byte[2 * params.getM()]; + byte[] s = new byte[params.getK() * params.getN()]; + long[] pk = new long[params.getP1Limbs() + params.getP2Limbs() + params.getP3Limbs()]; + byte[] tmp = new byte[params.getDigestBytes() + params.getSaltBytes()]; + byte[] cpk = pubKey.getEncoded(); + + // Expand public key + // mayo_expand_pk + MayoEngine.expandP1P2(params, pk, cpk); + Utils.unpackMVecs(cpk, params.getPkSeedBytes(), pk, params.getP1Limbs() + params.getP2Limbs(), params.getP3Limbs() / params.getMVecLimbs(), params.getM()); + + // Split pk into P1, P2, P3 + int p1Limbs = params.getP1Limbs(); + int p2Limbs = params.getP2Limbs(); + int p3Limbs = params.getP3Limbs(); + + long[] P1 = new long[p1Limbs]; + long[] P2 = new long[p2Limbs]; + long[] P3 = new long[p3Limbs]; + System.arraycopy(pk, 0, P1, 0, p1Limbs); + System.arraycopy(pk, p1Limbs, P2, 0, p2Limbs); + System.arraycopy(pk, p1Limbs + p2Limbs, P3, 0, p3Limbs); + + // Hash message + Utils.shake256(tmp, paramDigestBytes, message, message.length); + + // Compute t + System.arraycopy(signature, paramSigBytes - paramSaltBytes, tmp, paramDigestBytes, paramSaltBytes); + Utils.shake256(tEnc, paramMBytes, tmp, paramDigestBytes + paramSaltBytes); + Utils.decode(tEnc, t, paramM); + + // Decode signature + Utils.decode(signature, s, paramK * paramN); + + // Evaluate public map + evalPublicMap(params, s, P1, P2, P3, y); + + // Compare results + return Arrays.constantTimeAreEqual(paramM, y, 0, t, 0); + } + + + /** + * Computes the product of the matrix P1 (bit-sliced) with the transpose of matrix V and adds the result to acc. + * + * @param p the parameters object + * @param P1 the bit-sliced matrix P1 as a long array + * @param V the matrix V as a byte array + * @param acc the accumulator array where the result is added + */ + public static void P1TimesVt(MayoParameters p, long[] P1, byte[] V, long[] acc) + { + int mVecLimbs = p.getMVecLimbs(); + int paramV = p.getV(); + int paramK = p.getK(); + // triangular parameter is set to 1 + GF16Utils.mulAddMUpperTriangularMatXMatTrans(mVecLimbs, P1, V, acc, paramV, paramV, paramK, 1); + } + + /** + * Computes the matrices M and VP1V from the given input matrices. + * + * @param p the parameters object + * @param Vdec the decoded V matrix as a byte array + * @param L the matrix L as a long array + * @param P1 the bit-sliced matrix P1 as a long array + * @param VL the output accumulator for VL + * @param VP1V the output accumulator for VP1V + */ + public static void computeMandVPV(MayoParameters p, byte[] Vdec, long[] L, int Loff, long[] P1, long[] VL, long[] VP1V) + { + int paramK = p.getK(); + int paramV = p.getV(); + int paramO = p.getO(); + int mVecLimbs = p.getMVecLimbs(); + + // Compute VL: VL = Vdec * L + GF16Utils.mulAddMatXMMat(mVecLimbs, Vdec, L, Loff, VL, paramK, paramV, paramO); + + // Compute VP1V: + // Allocate temporary array for Pv. Its length is V_MAX * K_MAX * M_VEC_LIMBS_MAX. + int size = p.getV() * p.getK() * p.getMVecLimbs(); + long[] Pv = new long[size]; // automatically initialized to zero in Java + + // Compute Pv = P1 * V^T (using upper triangular multiplication) + P1TimesVt(p, P1, Vdec, Pv); + + // Compute VP1V = Vdec * Pv + GF16Utils.mulAddMatXMMat(mVecLimbs, Vdec, Pv, VP1V, paramK, paramV, paramK); + } + + public static void computeRHS(MayoParameters p, long[] vPv, byte[] t, byte[] y) + { + final int m = p.getM(); + final int mVecLimbs = p.getMVecLimbs(); + final int k = p.getK(); + final int[] fTail = p.getFTail(); + + final int topPos = ((m - 1) % 16) * 4; + + // Zero out tails of m_vecs if necessary + if (m % 16 != 0) + { + long mask = 1L; + mask <<= ((m % 16) * 4); + mask -= 1; + final int kSquared = k * k; + + for (int i = 0; i < kSquared; i++) + { + int index = i * mVecLimbs + mVecLimbs - 1; + vPv[index] &= mask; + } + } + + long[] temp = new long[mVecLimbs]; + byte[] tempBytes = new byte[mVecLimbs << 3]; + + for (int i = k - 1; i >= 0; i--) + { + for (int j = i; j < k; j++) + { + // Multiply by X (shift up 4 bits) + int top = (int)((temp[mVecLimbs - 1] >>> topPos) & 0xF); + temp[mVecLimbs - 1] <<= 4; + + for (int limb = mVecLimbs - 2; limb >= 0; limb--) + { + temp[limb + 1] ^= temp[limb] >>> 60; + temp[limb] <<= 4; + } + Pack.longToLittleEndian(temp, tempBytes, 0); + + // Reduce mod f(X) + for (int jj = 0; jj < 4; jj++) + { + int ft = fTail[jj]; + if (ft == 0) + { + continue; + } + + long product = GF16Utils.mulF(top, ft); + if (jj % 2 == 0) + { + tempBytes[jj / 2] ^= (byte)(product & 0xF); + } + else + { + tempBytes[jj / 2] ^= (byte)((product & 0xF) << 4); + } + } + Pack.littleEndianToLong(tempBytes, 0, temp); + + // Extract from vPv and add + int matrixIndex = i * k + j; + int symmetricIndex = j * k + i; + boolean isDiagonal = (i == j); + + for (int limb = 0; limb < mVecLimbs; limb++) + { + long value = vPv[matrixIndex * mVecLimbs + limb]; + if (!isDiagonal) + { + value ^= vPv[symmetricIndex * mVecLimbs + limb]; + } + temp[limb] ^= value; + } + } + } + Pack.longToLittleEndian(temp, tempBytes, 0); + // Compute y + for (int i = 0; i < m; i += 2) + { + int bytePos = i / 2; + y[i] = (byte)(t[i] ^ (tempBytes[bytePos] & 0xF)); + y[i + 1] = (byte)(t[i + 1] ^ ((tempBytes[bytePos] >>> 4) & 0xF)); + } + } + + + private static final int F_TAIL_LEN = 4; + private static final long EVEN_NIBBLES = 0x0F0F0F0F0F0F0F0FL; + private static final long EVEN_BYTES = 0x00FF00FF00FF00FFL; + private static final long EVEN_2BYTES = 0x0000FFFF0000FFFFL; + private static final long EVEN_HALF = 0x00000000FFFFFFFFL; + private static final long LOW_BIT_IN_NIBBLE = 0x1111111111111111L; + + public static void computeA(MayoParameters p, long[] VtL, byte[] AOut) + { + final int k = p.getK(); + final int o = p.getO(); + final int m = p.getM(); + final int mVecLimbs = p.getMVecLimbs(); + final int ACols = p.getACols(); + final byte[] fTailArr = p.getFTailArr(); + + int bitsToShift = 0; + int wordsToShift = 0; + final int MAYO_M_OVER_8 = (m + 7) / 8; + final int AWidth = ((o * k + 15) / 16) * 16; + long[] A = new long[AWidth * MAYO_M_OVER_8 * 16]; + + // Zero out tails of m_vecs if necessary + if (m % 16 != 0) + { + long mask = 1L << ((m % 16) * 4); + mask -= 1; + for (int i = 0; i < o * k; i++) + { + int idx = i * mVecLimbs + mVecLimbs - 1; + VtL[idx] &= mask; + } + } + + for (int i = 0; i < k; i++) + { + for (int j = k - 1; j >= i; j--) + { + // Process Mj + int mjOffset = j * mVecLimbs * o; + for (int c = 0; c < o; c++) + { + for (int limb = 0; limb < mVecLimbs; limb++) + { + int idx = mjOffset + limb + c * mVecLimbs; + long value = VtL[idx]; + + int aIndex = o * i + c + (limb + wordsToShift) * AWidth; + A[aIndex] ^= value << bitsToShift; + + if (bitsToShift > 0) + { + A[aIndex + AWidth] ^= value >>> (64 - bitsToShift); + } + } + } + + if (i != j) + { + // Process Mi + int miOffset = i * mVecLimbs * o; + for (int c = 0; c < o; c++) + { + for (int limb = 0; limb < mVecLimbs; limb++) + { + int idx = miOffset + limb + c * mVecLimbs; + long value = VtL[idx]; + + int aIndex = o * j + c + (limb + wordsToShift) * AWidth; + A[aIndex] ^= value << bitsToShift; + + if (bitsToShift > 0) + { + A[aIndex + AWidth] ^= value >>> (64 - bitsToShift); + } + } + } + } + + bitsToShift += 4; + if (bitsToShift == 64) + { + wordsToShift++; + bitsToShift = 0; + } + } + } + + // Transpose blocks + for (int c = 0; c < AWidth * ((m + (k + 1) * k / 2 + 15) / 16); c += 16) + { + transpose16x16Nibbles(A, c); + } + + // Generate tab array + byte[] tab = new byte[F_TAIL_LEN * 4]; + for (int i = 0; i < F_TAIL_LEN; i++) + { + byte ft = fTailArr[i]; + tab[4 * i] = (byte)GF16Utils.mulF(ft, 1); + tab[4 * i + 1] = (byte)GF16Utils.mulF(ft, 2); + tab[4 * i + 2] = (byte)GF16Utils.mulF(ft, 4); + tab[4 * i + 3] = (byte)GF16Utils.mulF(ft, 8); + } + + // Final processing + for (int c = 0; c < AWidth; c += 16) + { + for (int r = m; r < m + (k + 1) * k / 2; r++) + { + int pos = (r / 16) * AWidth + c + (r % 16); + long t0 = A[pos] & LOW_BIT_IN_NIBBLE; + long t1 = (A[pos] >>> 1) & LOW_BIT_IN_NIBBLE; + long t2 = (A[pos] >>> 2) & LOW_BIT_IN_NIBBLE; + long t3 = (A[pos] >>> 3) & LOW_BIT_IN_NIBBLE; + + for (int t = 0; t < F_TAIL_LEN; t++) + { + int targetRow = r + t - m; + int targetPos = (targetRow / 16) * AWidth + c + (targetRow % 16); + long xorValue = (t0 * tab[4 * t]) ^ (t1 * tab[4 * t + 1]) + ^ (t2 * tab[4 * t + 2]) ^ (t3 * tab[4 * t + 3]); + A[targetPos] ^= xorValue; + } + } + } + + byte[] Abytes = Pack.longToLittleEndian(A); + // Decode to output + for (int r = 0; r < m; r += 16) + { + for (int c = 0; c < ACols - 1; c += 16) + { + for (int i = 0; i + r < m; i++) + { + Utils.decode(Abytes, (r * AWidth / 16 + c + i) * 8, + AOut, (r + i) * ACols + c, + Math.min(16, ACols - 1 - c)); + } + } + } + } + + private static void transpose16x16Nibbles(long[] M, int offset) + { + for (int i = 0; i < 16; i += 2) + { + int idx1 = offset + i; + int idx2 = offset + i + 1; + long t = ((M[idx1] >>> 4) ^ M[idx2]) & EVEN_NIBBLES; + M[idx1] ^= t << 4; + M[idx2] ^= t; + } + + for (int i = 0; i < 16; i += 4) + { + int base = offset + i; + long t0 = ((M[base] >>> 8) ^ M[base + 2]) & EVEN_BYTES; + long t1 = ((M[base + 1] >>> 8) ^ M[base + 3]) & EVEN_BYTES; + M[base] ^= t0 << 8; + M[base + 1] ^= t1 << 8; + M[base + 2] ^= t0; + M[base + 3] ^= t1; + } + + for (int i = 0; i < 4; i++) + { + int base = offset + i; + long t0 = ((M[base] >>> 16) ^ M[base + 4]) & EVEN_2BYTES; + long t1 = ((M[base + 8] >>> 16) ^ M[base + 12]) & EVEN_2BYTES; + M[base] ^= t0 << 16; + M[base + 8] ^= t1 << 16; + M[base + 4] ^= t0; + M[base + 12] ^= t1; + } + + for (int i = 0; i < 8; i++) + { + int base = offset + i; + long t = ((M[base] >>> 32) ^ M[base + 8]) & EVEN_HALF; + M[base] ^= t << 32; + M[base + 8] ^= t; + } + } + + public int sampleSolution(MayoParameters params, byte[] A, byte[] y, + byte[] r, byte[] x) + { + final int k = params.getK(); + final int o = params.getO(); + final int m = params.getM(); + final int aCols = params.getACols(); + + // Initialize x with r values + System.arraycopy(r, 0, x, 0, k * o); + + // Compute Ar matrix product + byte[] Ar = new byte[m]; +// Arrays.fill(Ar, (byte)0); + + // Clear last column of A + for (int i = 0; i < m; i++) + { + A[k * o + i * (k * o + 1)] = 0; + } + GF16Utils.matMul(A, r, Ar, k * o + 1, m, 1); + + // Update last column of A with y - Ar + for (int i = 0; i < m; i++) + { + A[k * o + i * (k * o + 1)] = (byte)(y[i] ^ Ar[i]); + } + + // Perform row echelon form transformation + ef(A, m, aCols); + + // Check matrix rank + boolean fullRank = false; + for (int i = 0; i < aCols - 1; i++) + { + fullRank |= (A[(m - 1) * aCols + i] != 0); + } + if (!fullRank) + { + return 0; + } + + // Constant-time back substitution + for (int row = m - 1; row >= 0; row--) + { + byte finished = 0; + int colUpperBound = Math.min(row + (32 / (m - row)), k * o); + + for (int col = row; col <= colUpperBound; col++) + { + byte correctCol = GF16Utils.ctCompare8(A[row * aCols + col], (byte)0); + byte mask = (byte)(correctCol & ~finished); + + // Update x[col] using constant-time mask + byte u = (byte)(mask & A[row * aCols + aCols - 1]); + //System.out.println("x[col]: " + x[col] + ", u: " + u); + x[col] ^= u; + + + // Update matrix entries + for (int i = 0; i < row; i += 8) + { + long tmp = 0; + // Pack 8 GF(16) elements into long + for (int j = 0; j < 8; j++) + { + tmp ^= (long)(A[(i + j) * aCols + col] & 0xFF) << (j * 8); + } + + // GF(16) multiplication + tmp = GF16Utils.mulFx8(u, tmp); + + // Unpack and update + for (int j = 0; j < 8; j++) + { + A[(i + j) * aCols + aCols - 1] ^= (byte)((tmp >> (j * 8)) & 0x0F); + } + } + finished |= correctCol; + } + } + return 1; + } + + // Adjust these as needed. In our translation we compute rowLen from ncols. + // These blockers are used in the C code for constant-time masking. + private static final long UINT64_BLOCKER = 0L; + private static final int UNSIGNED_CHAR_BLOCKER = 0; // Not used in our Java version. + + /** + * Converts a matrix A (given as a flat array of GF(16) elements, one per byte) + * into row echelon form (with ones on the first nonzero entries) in constant time. + * + * @param A the input matrix, stored rowwise; each element is in [0,15] + * @param nrows the number of rows + * @param ncols the number of columns (GF(16) elements per row) + */ + public void ef(byte[] A, int nrows, int ncols) + { + // Each 64-bit long can hold 16 nibbles (16 GF(16) elements). + int rowLen = (ncols + 15) / 16; + + // Allocate temporary arrays. + long[] pivotRow = new long[rowLen]; + long[] pivotRow2 = new long[rowLen]; + // The packed matrix: one contiguous array storing nrows rows, each rowLen longs long. + long[] packedA = new long[nrows * rowLen]; + + // Pack the matrix rows. + for (int i = 0; i < nrows; i++) + { + long[] packedRow = packRow(A, i, ncols); + System.arraycopy(packedRow, 0, packedA, i * rowLen, rowLen); + } + + int pivotRowIndex = 0; + // Loop over each pivot column (each column corresponds to one GF(16) element) + for (int pivotCol = 0; pivotCol < ncols; pivotCol++) + { + int lowerBound = Math.max(0, pivotCol + nrows - ncols); + int upperBound = Math.min(nrows - 1, pivotCol); + + // Zero out pivot row buffers. + for (int i = 0; i < rowLen; i++) + { + pivotRow[i] = 0; + pivotRow2[i] = 0; + } + + // Try to select a pivot row in constant time. + int pivot = 0; + long pivotIsZero = -1L; // all bits set (0xFFFFFFFFFFFFFFFF) + int searchUpper = Math.min(nrows - 1, upperBound + 32); + for (int row = lowerBound; row <= searchUpper; row++) + { + long isPivotRow = ~ctCompare64(row, pivotRowIndex); + long belowPivotRow = ct64IsGreaterThan(row, pivotRowIndex); + for (int j = 0; j < rowLen; j++) + { + // The expression below accumulates (in constant time) the candidate pivot row. + pivotRow[j] ^= (isPivotRow | (belowPivotRow & pivotIsZero)) + & packedA[row * rowLen + j]; + } + // Extract candidate pivot element from the packed row. + pivot = mExtractElement(pivotRow, pivotCol); + pivotIsZero = ~ctCompare64(pivot, 0); + } + + // Multiply the pivot row by the inverse of the pivot element. + int inv = inverseF(pivot); + vecMulAddU64(rowLen, pivotRow, (byte)inv, pivotRow2); + + // Conditionally write the pivot row back into the correct row (if pivot is nonzero). + for (int row = lowerBound; row <= upperBound; row++) + { + long doCopy = ~ctCompare64(row, pivotRowIndex) & ~pivotIsZero; + long doNotCopy = ~doCopy; + for (int col = 0; col < rowLen; col++) + { + // Since the masks are disjoint, addition is equivalent to OR. + packedA[row * rowLen + col] = + (doNotCopy & packedA[row * rowLen + col]) | + (doCopy & pivotRow2[col]); + } + } + + // Eliminate entries below the pivot. + for (int row = lowerBound; row < nrows; row++) + { + int belowPivot = (row > pivotRowIndex) ? 1 : 0; + int eltToElim = mExtractElementFromPacked(packedA, row, rowLen, pivotCol); + vecMulAddU64(rowLen, pivotRow2, (byte)(belowPivot * eltToElim), packedA, row * rowLen); + } + + // If pivot is nonzero, increment pivotRowIndex. + if (pivot != 0) + { + pivotRowIndex++; + } + } + + byte[] temp = new byte[params.getO() * params.getK() + 1 + 15]; + // At this point, packedA holds the row-echelon form of the original matrix. + // (Depending on your application you might want to unpack it back to A.) + for (int i = 0; i < nrows; i++) + { + GF16Utils.efUnpackMVector(rowLen, packedA, i * rowLen, temp); + for (int j = 0; j < ncols; j++) + { + A[i * ncols + j] = temp[j]; + } + } + } + + /** + * Packs one row of GF(16) elements (stored in A) into a long[]. + * Each long holds 16 GF(16) elements (4 bits each). + * + * @param A the flat input matrix (row-major) + * @param row the row index to pack + * @param ncols the number of columns (GF(16) elements) in a row + * @return an array of longs representing the packed row. + */ + private static long[] packRow(byte[] A, int row, int ncols) + { + int rowLen = (ncols + 15) / 16; // number of longs needed for this row + long[] packed = new long[rowLen]; + // Process each 64-bit word (each holds 16 nibbles). + for (int word = 0; word < rowLen; word++) + { + long wordVal = 0; + for (int nibble = 0; nibble < 16; nibble++) + { + int col = word * 16 + nibble; + if (col < ncols) + { + int element = A[row * ncols + col] & 0xF; + wordVal |= ((long)element) << (4 * nibble); + } + } + packed[word] = wordVal; + } + return packed; + } + + /** + * Constant-time comparison: returns 0 if a==b, else returns all 1s (0xFFFFFFFFFFFFFFFF). + */ + private static long ctCompare64(int a, int b) + { + // Compute (-(a XOR b)) >> 63 then XOR with UINT64_BLOCKER. + long diff = -(long)(a ^ b); + long shift = diff >> 63; // arithmetic shift; results in 0 or -1. + return shift ^ UINT64_BLOCKER; + } + + /** + * Returns 0xFFFFFFFFFFFFFFFF if a > b, 0 otherwise. + */ + private static long ct64IsGreaterThan(int a, int b) + { + long diff = (long)b - (long)a; + long shift = diff >> 63; + return shift ^ UINT64_BLOCKER; + } + + /** + * Extracts the GF(16) element at a given column index from a packed row. + * + * @param in the packed row (array of longs) + * @param index the column index (0-indexed) + * @return the GF(16) element (an int in 0..15) + */ + private static int mExtractElement(long[] in, int index) + { + int leg = index / 16; + int offset = index % 16; + return (int)((in[leg] >>> (4 * offset)) & 0xF); + } + + /** + * Extracts an element from the packed matrix for a given row and column. + * + * @param packedA the packed matrix stored in row-major order + * @param row the row index + * @param rowLen the number of longs per row + * @param index the column index + * @return the GF(16) element at that position. + */ + private static int mExtractElementFromPacked(long[] packedA, int row, int rowLen, int index) + { + int leg = index / 16; + int offset = index % 16; + return (int)((packedA[row * rowLen + leg] >>> (4 * offset)) & 0xF); + } + + /** + * Computes the multiplicative inverse in GF(16) for a GF(16) element. + */ + private static int inverseF(int a) + { + // In GF(16), the inverse can be computed via exponentiation. + int a2 = mulF(a, a); + int a4 = mulF(a2, a2); + int a8 = mulF(a4, a4); + int a6 = mulF(a2, a4); + int a14 = mulF(a8, a6); + return a14; + } + + /** + * GF(16) multiplication mod (x^4 + x + 1). + *

+ * Multiplies two GF(16) elements (only the lower 4 bits are used). + */ + public static int mulF(int a, int b) + { + // Carryless multiply: multiply b by each bit of a and XOR. + int p = ((a & 1) * b) ^ + ((a & 2) * b) ^ + ((a & 4) * b) ^ + ((a & 8) * b); + // Reduce modulo f(X) = x^4 + x + 1. + int topP = p & 0xF0; + int out = (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0F; + return out; + } + + /** + * Multiplies each word of the input vector (in) by a GF(16) scalar (a), + * then XORs the result into the accumulator vector (acc). + *

+ * This version updates the acc array starting at index 0. + * + * @param legs the number of 64-bit words in the vector. + * @param in the input vector. + * @param a the GF(16) scalar (as a byte; only low 4 bits used). + * @param acc the accumulator vector which is updated. + */ + private static void vecMulAddU64(int legs, long[] in, byte a, long[] acc) + { + int tab = mulTable(a & 0xFF); + long lsbAsk = 0x1111111111111111L; + for (int i = 0; i < legs; i++) + { + long val = ((in[i] & lsbAsk) * (tab & 0xFF)) + ^ (((in[i] >>> 1) & lsbAsk) * ((tab >>> 8) & 0xF)) + ^ (((in[i] >>> 2) & lsbAsk) * ((tab >>> 16) & 0xF)) + ^ (((in[i] >>> 3) & lsbAsk) * ((tab >>> 24) & 0xF)); + acc[i] ^= val; + } + } + + /** + * Overloaded version of vecMulAddU64 that writes to acc starting at accOffset. + * + * @param legs the number of 64-bit words. + * @param in the input vector. + * @param a the GF(16) scalar. + * @param acc the accumulator vector. + * @param accOffset the starting index in acc. + */ + private static void vecMulAddU64(int legs, long[] in, byte a, long[] acc, int accOffset) + { + int tab = mulTable(a & 0xFF); + long lsbAsk = 0x1111111111111111L; + for (int i = 0; i < legs; i++) + { + long val = ((in[i] & lsbAsk) * (tab & 0xFF)) + ^ (((in[i] >>> 1) & lsbAsk) * ((tab >>> 8) & 0xF)) + ^ (((in[i] >>> 2) & lsbAsk) * ((tab >>> 16) & 0xF)) + ^ (((in[i] >>> 3) & lsbAsk) * ((tab >>> 24) & 0xF)); + acc[accOffset + i] ^= val; + } + } + + /** + * Computes a multiplication table for nibble-packed vectors. + *

+ * Implements arithmetic for GF(16) elements modulo (x^4 + x + 1). + * + * @param b a GF(16) element (only lower 4 bits are used) + * @return a 32-bit integer representing the multiplication table. + */ + private static int mulTable(int b) + { + int x = b * 0x08040201; + int highNibbleMask = 0xf0f0f0f0; + int highHalf = x & highNibbleMask; + return x ^ (highHalf >>> 4) ^ (highHalf >>> 3); + } + + private static void evalPublicMap(MayoParameters p, byte[] s, long[] P1, long[] P2, long[] P3, byte[] eval) + { + int mVecLimbs = (p.getM() + 15) / 16; + long[] SPS = new long[p.getK() * p.getK() * mVecLimbs]; + mCalculatePsSps(p, P1, P2, P3, s, SPS); + byte[] zero = new byte[p.getM()]; + computeRHS(p, SPS, zero, eval); + } + + private static void mCalculatePsSps(MayoParameters p, long[] P1, long[] P2, long[] P3, byte[] s, long[] SPS) + { + int m = p.getM(); + int v = p.getV(); + int o = p.getO(); + int k = p.getK(); + int mVecLimbs = (m + 15) / 16; + + long[] PS = new long[p.getN() * p.getK() * mVecLimbs]; + mayoGenericMCalculatePS(p, P1, P2, P3, s, m, v, o, k, PS); + mayoGenericMCalculateSPS(PS, s, m, k, p.getN(), SPS); + } + + private static void mayoGenericMCalculatePS(MayoParameters p, long[] P1, long[] P2, long[] P3, byte[] S, + int m, int v, int o, int k, long[] PS) + { + int n = o + v; + int mVecLimbs = (m + 15) / 16; + long[] accumulator = new long[16 * ((p.getM() + 15) / 16 * p.getK() * p.getN() * mVecLimbs)]; + + int p1Used = 0; + for (int row = 0; row < v; row++) + { + for (int j = row; j < v; j++) + { + for (int col = 0; col < k; col++) + { + GF16Utils.mVecAdd(mVecLimbs, P1, p1Used * mVecLimbs, + accumulator, ((row * k + col) * 16 + (S[col * n + j] & 0xFF)) * mVecLimbs); + } + p1Used++; + } + + for (int j = 0; j < o; j++) + { + for (int col = 0; col < k; col++) + { + GF16Utils.mVecAdd(mVecLimbs, P2, (row * o + j) * mVecLimbs, + accumulator, ((row * k + col) * 16 + (S[(col * n) + j + v] & 0xFF)) * mVecLimbs); + } + } + } + + int p3Used = 0; + for (int row = v; row < n; row++) + { + for (int j = row; j < n; j++) + { + for (int col = 0; col < k; col++) + { + GF16Utils.mVecAdd(mVecLimbs, P3, p3Used * mVecLimbs, + accumulator, ((row * k + col) * 16 + (S[col * n + j] & 0xFF)) * mVecLimbs); + } + p3Used++; + } + } + + for (int i = 0; i < n * k; i++) + { + mVecMultiplyBins(mVecLimbs, accumulator, i * 16 * mVecLimbs, PS, i * mVecLimbs); + } + } + + private static void mayoGenericMCalculateSPS(long[] PS, byte[] S, int m, int k, int n, long[] SPS) + { + final int mVecLimbs = (m + 15) / 16; + final int accumulatorSize = 16 * mVecLimbs * k * k; + final long[] accumulator = new long[accumulatorSize]; + + // Accumulation phase + for (int row = 0; row < k; row++) + { + for (int j = 0; j < n; j++) + { + final int sVal = S[row * n + j] & 0xFF; // Unsigned byte value + for (int col = 0; col < k; col++) + { + final int psOffset = (j * k + col) * mVecLimbs; + final int accOffset = ((row * k + col) * 16 + sVal) * mVecLimbs; + mVecAdd(mVecLimbs, PS, psOffset, accumulator, accOffset); + } + } + } + + // Processing phase + for (int i = 0; i < k * k; i++) + { + mVecMultiplyBins(mVecLimbs, accumulator, i * 16 * mVecLimbs, SPS, i * mVecLimbs); + } + } + + // Helper method for vector addition (XOR) + private static void mVecAdd(int limbs, long[] src, int srcOffset, long[] dest, int destOffset) + { + for (int i = 0; i < limbs; i++) + { + dest[destOffset + i] ^= src[srcOffset + i]; + } + } + + // Main bin processing method + private static void mVecMultiplyBins(int mVecLimbs, long[] bins, int binOffset, long[] ps, int psOff) + { + // Series of modular operations as per original C code + mVecMulAddXInv(mVecLimbs, bins, binOffset + 5 * mVecLimbs, bins, binOffset + 10 * mVecLimbs); + mVecMulAddX(mVecLimbs, bins, binOffset + 11 * mVecLimbs, bins, binOffset + 12 * mVecLimbs); + mVecMulAddXInv(mVecLimbs, bins, binOffset + 10 * mVecLimbs, bins, binOffset + 7 * mVecLimbs); + mVecMulAddX(mVecLimbs, bins, binOffset + 12 * mVecLimbs, bins, binOffset + 6 * mVecLimbs); + mVecMulAddXInv(mVecLimbs, bins, binOffset + 7 * mVecLimbs, bins, binOffset + 14 * mVecLimbs); + mVecMulAddX(mVecLimbs, bins, binOffset + 6 * mVecLimbs, bins, binOffset + 3 * mVecLimbs); + mVecMulAddXInv(mVecLimbs, bins, binOffset + 14 * mVecLimbs, bins, binOffset + 15 * mVecLimbs); + mVecMulAddX(mVecLimbs, bins, binOffset + 3 * mVecLimbs, bins, binOffset + 8 * mVecLimbs); + mVecMulAddXInv(mVecLimbs, bins, binOffset + 15 * mVecLimbs, bins, binOffset + 13 * mVecLimbs); + mVecMulAddX(mVecLimbs, bins, binOffset + 8 * mVecLimbs, bins, binOffset + 4 * mVecLimbs); + mVecMulAddXInv(mVecLimbs, bins, binOffset + 13 * mVecLimbs, bins, binOffset + 9 * mVecLimbs); + mVecMulAddX(mVecLimbs, bins, binOffset + 4 * mVecLimbs, bins, binOffset + 2 * mVecLimbs); + mVecMulAddXInv(mVecLimbs, bins, binOffset + 9 * mVecLimbs, bins, binOffset + 1 * mVecLimbs); + mVecMulAddX(mVecLimbs, bins, binOffset + 2 * mVecLimbs, bins, binOffset + 1 * mVecLimbs); + System.arraycopy(bins, mVecLimbs + binOffset, ps, psOff, mVecLimbs); + } + + // Modular arithmetic operations + private static void mVecMulAddXInv(int limbs, long[] in, int inOffset, + long[] acc, int accOffset) + { + final long maskLsb = 0x1111111111111111L; + for (int i = 0; i < limbs; i++) + { + final long t = in[inOffset + i] & maskLsb; + acc[accOffset + i] ^= ((in[inOffset + i] ^ t) >>> 1) ^ (t * 9); + } + } + + private static void mVecMulAddX(int limbs, long[] in, int inOffset, + long[] acc, int accOffset) + { + final long maskMsb = 0x8888888888888888L; + for (int i = 0; i < limbs; i++) + { + final long t = in[inOffset + i] & maskMsb; + acc[accOffset + i] ^= ((in[inOffset + i] ^ t) << 1) ^ ((t >>> 3) * 3); + } + } + + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java index d3de862432..320204911a 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java @@ -33,6 +33,24 @@ public static void decode(byte[] m, byte[] mdec, int mdecLen) } } + public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mdecLen) + { + int i; + // Process pairs of nibbles from each byte + for (i = 0; i < mdecLen / 2; i++) + { + // Extract the lower nibble + mdec[decIndex++] = (byte)((m[i + mOff] & 0xFF) & 0x0F); + // Extract the upper nibble (shift right 4 bits) + mdec[decIndex++] = (byte)(((m[i + mOff] & 0xFF) >> 4) & 0x0F); + } + // If there is an extra nibble (odd number of nibbles), decode only the lower nibble + if (mdecLen % 2 == 1) + { + mdec[decIndex] = (byte)((m[i + mOff] & 0xFF) & 0x0F); + } + } + /** * Decodes a nibble-packed byte array into an output array. * @@ -113,6 +131,27 @@ public static void unpackMVecs(byte[] in, long[] out, int vecs, int m) } } + public static void unpackMVecs(byte[] in, int inOff, long[] out, int outOff, int vecs, int m) + { + int mVecLimbs = (m + 15) / 16; + int bytesToCopy = m / 2; // Number of bytes to copy per vector + + // Process vectors in reverse order + for (int i = vecs - 1; i >= 0; i--) + { + // Temporary buffer to hold mVecLimbs longs (each long is 8 bytes) + byte[] tmp = new byte[mVecLimbs * 8]; + // Copy m/2 bytes from the input into tmp. The rest remains zero. + System.arraycopy(in, inOff + i * bytesToCopy, tmp, 0, bytesToCopy); + + // Convert each 8-byte block in tmp into a long using Pack + for (int j = 0; j < mVecLimbs; j++) + { + out[outOff + i * mVecLimbs + j] = Pack.littleEndianToLong(tmp, j * 8); + } + } + } + /** * Packs m-vectors from an array of 64-bit limbs into a packed byte array. * diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/AllTests.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/AllTests.java index 08a3b0e5b4..fbf60a3de7 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/AllTests.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/AllTests.java @@ -51,6 +51,7 @@ public static Test suite() suite.addTestSuite(XWingTest.class); suite.addTestSuite(AllTests.SimpleTestTest.class); suite.addTestSuite(SLHDSATest.class); + suite.addTestSuite(MayoTest.class); return new BCTestSetup(suite); } diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java index 8f720f844e..ceb91d4601 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java @@ -1,15 +1,22 @@ package org.bouncycastle.pqc.crypto.test; import java.io.IOException; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Map; import junit.framework.TestCase; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Signer; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.pqc.crypto.MessageSigner; import org.bouncycastle.pqc.crypto.mayo.MayoKeyGenerationParameters; import org.bouncycastle.pqc.crypto.mayo.MayoKeyPairGenerator; import org.bouncycastle.pqc.crypto.mayo.MayoParameters; import org.bouncycastle.pqc.crypto.mayo.MayoPrivateKeyParameter; import org.bouncycastle.pqc.crypto.mayo.MayoPublicKeyParameter; +import org.bouncycastle.pqc.crypto.mayo.MayoSigner; public class MayoTest extends TestCase @@ -18,7 +25,8 @@ public static void main(String[] args) throws Exception { MayoTest test = new MayoTest(); - test.testKeyGen(); + test.testTestVectors(); + //test.testKeyGen(); } private static final MayoParameters[] PARAMETER_SETS = new MayoParameters[] @@ -29,21 +37,28 @@ public static void main(String[] args) MayoParameters.MAYO5 }; - public void testKeyGen() - throws IOException + private static final String[] files = new String[]{ + "PQCsignKAT_24_MAYO_1.rsp", + "PQCsignKAT_24_MAYO_2.rsp", + "PQCsignKAT_32_MAYO_3.rsp", + "PQCsignKAT_40_MAYO_5.rsp", + }; + + + public void testTestVectors() + throws Exception { - String[] files = new String[]{ - "PQCsignKAT_24_MAYO_1.rsp", - "PQCsignKAT_24_MAYO_2.rsp", - "PQCsignKAT_32_MAYO_3.rsp", - "PQCsignKAT_40_MAYO_5.rsp", - }; - TestUtils.testKeyGen(false, "pqc/crypto/mayo", files, new TestUtils.KeyGenerationOperation() + TestUtils.testTestVector(false, false, "pqc/crypto/mayo", files, new TestUtils.KeyGenerationOperation() { @Override - public AsymmetricCipherKeyPairGenerator getAsymmetricCipherKeyPairGenerator(int fileIndex, byte[] seed) + public SecureRandom getSecureRanom(byte[] seed) + { + return new NISTSecureRandom(seed, null); + } + + @Override + public AsymmetricCipherKeyPairGenerator getAsymmetricCipherKeyPairGenerator(int fileIndex, SecureRandom random) { - NISTSecureRandom random = new NISTSecureRandom(seed, null); MayoParameters parameters = PARAMETER_SETS[fileIndex]; MayoKeyPairGenerator kpGen = new MayoKeyPairGenerator(); @@ -58,10 +73,22 @@ public byte[] getPublicKeyEncoded(AsymmetricKeyParameter pubParams) } @Override - public byte[] getPrivateKeyEncoded(AsymmetricKeyParameter privParams) + public byte[] getPrivateKeyEncoded(CipherParameters privParams) { return ((MayoPrivateKeyParameter)privParams).getEncoded(); } + + @Override + public Signer getSigner() + { + return null; + } + + @Override + public MessageSigner getMessageSigner() + { + return new MayoSigner(); + } }); } } diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java index 966b7bf455..aeb9d09fbf 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java @@ -6,12 +6,17 @@ import java.io.InputStreamReader; import java.security.SecureRandom; import java.util.HashMap; +import java.util.Map; import junit.framework.Assert; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Signer; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.pqc.crypto.MessageSigner; import org.bouncycastle.pqc.crypto.util.PrivateKeyFactory; import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory; import org.bouncycastle.pqc.crypto.util.PublicKeyFactory; @@ -19,7 +24,6 @@ import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; -import org.bouncycastle.util.test.FixedSecureRandom; class TestUtils { @@ -30,15 +34,21 @@ static boolean parseBoolean(String value) public interface KeyGenerationOperation { - AsymmetricCipherKeyPairGenerator getAsymmetricCipherKeyPairGenerator(int fileIndex, byte[] seed); + SecureRandom getSecureRanom(byte[] seed); + + AsymmetricCipherKeyPairGenerator getAsymmetricCipherKeyPairGenerator(int fileIndex, SecureRandom random); byte[] getPublicKeyEncoded(AsymmetricKeyParameter pubParams); - byte[] getPrivateKeyEncoded(AsymmetricKeyParameter privParams); + byte[] getPrivateKeyEncoded(CipherParameters privParams); + + Signer getSigner(); + + MessageSigner getMessageSigner(); } - public static void testKeyGen(boolean enableFactory, String homeDir, String[] files, KeyGenerationOperation operation) - throws IOException + public static void testTestVector(boolean enableFactory, boolean isSigner, String homeDir, String[] files, KeyGenerationOperation operation) + throws Exception { for (int fileIndex = 0; fileIndex != files.length; fileIndex++) { @@ -60,17 +70,27 @@ public static void testKeyGen(boolean enableFactory, String homeDir, String[] fi { if (buf.size() > 0) { + int count = Integer.parseInt(buf.get("count")); + if (count == 99) + { + System.out.println("break"); + } byte[] seed = Hex.decode((String)buf.get("seed")); byte[] pk = Hex.decode((String)buf.get("pk")); byte[] sk = Hex.decode((String)buf.get("sk")); + byte[] message = Hex.decode((String)buf.get("msg")); + byte[] signature = Hex.decode((String)buf.get("sm")); - AsymmetricCipherKeyPairGenerator kpGen = operation.getAsymmetricCipherKeyPairGenerator(fileIndex, seed); + SecureRandom random = operation.getSecureRanom(seed); + + AsymmetricCipherKeyPairGenerator kpGen = operation.getAsymmetricCipherKeyPairGenerator(fileIndex, random); // // Generate keys and test. // AsymmetricCipherKeyPair kp = kpGen.generateKeyPair(); - AsymmetricKeyParameter pubParams, privParams; + AsymmetricKeyParameter pubParams; + CipherParameters privParams; if (enableFactory) { pubParams = PublicKeyFactory.createKey( @@ -86,6 +106,39 @@ public static void testKeyGen(boolean enableFactory, String homeDir, String[] fi Assert.assertTrue(name + ": public key", Arrays.areEqual(pk, operation.getPublicKeyEncoded(pubParams))); Assert.assertTrue(name + ": secret key", Arrays.areEqual(sk, operation.getPrivateKeyEncoded(privParams))); + + byte[] sigGenerated; + privParams = new ParametersWithRandom(privParams, random); + if (isSigner) + { + Signer signer = operation.getSigner(); + signer.init(true, privParams); + signer.update(message, 0, message.length); + sigGenerated = signer.generateSignature(); + } + else + { + MessageSigner signer = operation.getMessageSigner(); + signer.init(true, privParams); + sigGenerated = signer.generateSignature(message); + } + + Assert.assertTrue(Arrays.areEqual(sigGenerated, signature)); + + if (isSigner) + { + Signer signer = operation.getSigner(); + signer.init(false, pubParams); + signer.update(message, 0, message.length); + Assert.assertTrue(signer.verifySignature(sigGenerated)); + } + else + { + MessageSigner signer = operation.getMessageSigner(); + signer.init(false, pubParams); + Assert.assertTrue(signer.verifySignature(message, sigGenerated)); + } + System.out.println("Count " + count + " pass"); } buf.clear(); continue; From 01dbe523f48a4af9703ca6c4ad01513f5b630a17 Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 3 Mar 2025 11:51:02 +1030 Subject: [PATCH 139/890] Refactor for Mayo --- .../pqc/crypto/mayo/GF16Utils.java | 95 +++-------- .../pqc/crypto/mayo/MayoKeyPairGenerator.java | 147 ++++++------------ .../pqc/crypto/mayo/MayoParameters.java | 37 ----- .../pqc/crypto/mayo/MayoSigner.java | 15 +- .../java/org/bouncycastle/util/Arrays.java | 8 + .../java/org/bouncycastle/util/Longs.java | 8 + .../pqc/crypto/test/TestUtils.java | 12 +- 7 files changed, 94 insertions(+), 228 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java index cdcc073d8e..d7f08cf296 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java @@ -16,26 +16,25 @@ public class GF16Utils public static long gf16vMulU64(long a, int b) { long maskMsb = 0x8888888888888888L; - long a64 = a; // In the original code there is a conditional XOR with unsigned_char_blocker; // here we simply use b directly. long b32 = b & 0x00000000FFFFFFFFL; - long r64 = a64 * (b32 & 1); + long r64 = a * (b32 & 1); - long a_msb = a64 & maskMsb; - a64 ^= a_msb; - a64 = (a64 << 1) ^ ((a_msb >>> 3) * 3); - r64 ^= a64 * ((b32 >> 1) & 1); + long a_msb = a & maskMsb; + a ^= a_msb; + a = (a << 1) ^ ((a_msb >>> 3) * 3); + r64 ^= a * ((b32 >> 1) & 1); - a_msb = a64 & maskMsb; - a64 ^= a_msb; - a64 = (a64 << 1) ^ ((a_msb >>> 3) * 3); - r64 ^= a64 * ((b32 >>> 2) & 1); + a_msb = a & maskMsb; + a ^= a_msb; + a = (a << 1) ^ ((a_msb >>> 3) * 3); + r64 ^= a * ((b32 >>> 2) & 1); - a_msb = a64 & maskMsb; - a64 ^= a_msb; - a64 = (a64 << 1) ^ ((a_msb >>> 3) * 3); - r64 ^= a64 * ((b32 >> 3) & 1); + a_msb = a & maskMsb; + a ^= a_msb; + a = (a << 1) ^ ((a_msb >>> 3) * 3); + r64 ^= a * ((b32 >> 3) & 1); return r64; } @@ -61,18 +60,6 @@ public static void mVecMulAdd(int mVecLimbs, long[] in, int inOffset, int a, lon } } - /** - * Convenience overload of mVecMulAdd that assumes zero offsets. - * - * @param mVecLimbs the number of limbs - * @param in the input vector - * @param a the GF(16) element to multiply by - * @param acc the accumulator vector - */ - public static void mVecMulAdd(int mVecLimbs, long[] in, int a, long[] acc) - { - mVecMulAdd(mVecLimbs, in, 0, a, acc, 0); - } /** * Performs the multiplication and accumulation of a block of an upper‐triangular matrix @@ -156,33 +143,18 @@ public static void mulAddMatTransXMMat(int mVecLimbs, byte[] mat, long[] bsMat, { for (int c = 0; c < matRows; c++) { + byte matVal = mat[c * matCols + r]; for (int k = 0; k < bsMatCols; k++) { - // For bsMat: the m-vector at index (c * bsMatCols + k) int bsMatOffset = (c * bsMatCols + k) * mVecLimbs; - // For mat: element at row c, column r. - int a = mat[c * matCols + r] & 0xFF; // For acc: add into the m-vector at index (r * bsMatCols + k) int accOffset = (r * bsMatCols + k) * mVecLimbs; - mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, a, acc, accOffset); + mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, matVal, acc, accOffset); } } } } - - /** - * Adds (bitwise XOR) mVecLimbs elements from the source array (starting at srcOffset) - * into the destination array (starting at destOffset). - */ - public static void mVecAdd(int mVecLimbs, long[] src, int srcOffset, long[] dest, int destOffset) - { - for (int i = 0; i < mVecLimbs; i++) - { - dest[destOffset + i] ^= src[srcOffset + i]; - } - } - /** * Multiplies a matrix (given as a byte array) with a bit‐sliced matrix (given as a long array) * and accumulates the result into the acc array. @@ -288,30 +260,6 @@ public static void mulAddMUpperTriangularMatXMatTrans(int mVecLimbs, long[] bsMa } } - /** - * Multiplies a vector (from bsMat) by an unsigned scalar (from mat) and adds the result - * to the corresponding vector in acc. - * - *

- * This method corresponds to the C function m_vec_mul_add. - * It processes {@code mVecLimbs} elements starting from the given offsets in the source and accumulator arrays. - *

- * - * @param mVecLimbs the number of limbs (elements) in the vector - * @param bsMat the source array (bit-sliced matrix) of long values - * @param bsMatOffset the starting index in bsMat for the vector - * @param scalar the scalar value (from mat), as a byte - * @param acc the accumulator array where the result is added - * @param accOffset the starting index in the accumulator array for the current vector - */ - public static void mVecMulAdd(int mVecLimbs, long[] bsMat, int bsMatOffset, byte scalar, long[] acc, int accOffset) - { - for (int i = 0; i < mVecLimbs; i++) - { - acc[accOffset + i] ^= gf16vMulU64(bsMat[bsMatOffset + i], scalar); - } - } - /** * GF(16) multiplication mod x^4 + x + 1. *

@@ -339,8 +287,7 @@ public static int mulF(int a, int b) // Extract the upper nibble (bits 4 to 7). int topP = p & 0xF0; // The reduction: XOR p with (topP shifted right by 4 and by 3) and mask to 4 bits. - int out = (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0F; - return out; + return (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0F; } /** @@ -364,8 +311,7 @@ public static long mulFx8(byte a, long b) // Reduction mod (x^4 + x + 1): process each byte in parallel. long topP = p & 0xf0f0f0f0f0f0f0f0L; - long out = (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0f0f0f0f0f0f0f0fL; - return out; + return (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0f0f0f0f0f0f0f0fL; } public static void matMul(byte[] a, byte[] b, byte[] c, @@ -420,9 +366,6 @@ public static void matAdd(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int } } - // Define the blocker constant as needed (set to 0 if not used). - private static final byte UNSIGNED_CHAR_BLOCKER = 0; - /** * Returns 0x00 if a equals b, otherwise returns 0xFF. * This operation is performed in constant time. @@ -442,9 +385,7 @@ public static byte ctCompare8(byte a, byte b) // If diff is 0, then -diff is 0, and shifting yields 0. // If diff is nonzero, -diff is negative, so the arithmetic shift yields -1 (0xFFFFFFFF), // which when cast to a byte becomes 0xFF. - int result = negDiff >> 31; - // XOR with UNSIGNED_CHAR_BLOCKER (assumed 0 here) and cast to byte. - return (byte)(result ^ UNSIGNED_CHAR_BLOCKER); + return (byte) (negDiff >> 31); } public static void efUnpackMVector(int legs, long[] packedRow, int packedRowOff, byte[] out) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java index 96e4e9fb01..80dab0898b 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java @@ -5,7 +5,8 @@ import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; import org.bouncycastle.crypto.KeyGenerationParameters; -import org.bouncycastle.util.Pack; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Longs; public class MayoKeyPairGenerator implements AsymmetricCipherKeyPairGenerator @@ -13,65 +14,53 @@ public class MayoKeyPairGenerator private MayoParameters p; private SecureRandom random; - public void init(KeyGenerationParameters param) { this.p = ((MayoKeyGenerationParameters)param).getParameters(); this.random = param.getRandom(); } - @Override public AsymmetricCipherKeyPair generateKeyPair() { - int ret = MayoEngine.MAYO_OK; + // Retrieve parameters from p. + int mVecLimbs = p.getMVecLimbs(); + int m = p.getM(); + int v = p.getV(); + int o = p.getO(); + int oBytes = p.getOBytes(); + int p1Limbs = p.getP1Limbs(); + int p3Limbs = p.getP3Limbs(); + int pkSeedBytes = p.getPkSeedBytes(); + int skSeedBytes = p.getSkSeedBytes(); + byte[] cpk = new byte[p.getCpkBytes()]; // seed_sk points to csk. byte[] seed_sk = new byte[p.getCskBytes()]; // Allocate S = new byte[PK_SEED_BYTES_MAX + O_BYTES_MAX] - byte[] S = new byte[p.getPkSeedBytes() + p.getOBytes()]; + byte[] seed_pk = new byte[pkSeedBytes + oBytes]; // Allocate P as a long array of size (P1_LIMBS_MAX + P2_LIMBS_MAX) - long[] P = new long[p.getP1Limbs() + p.getP2Limbs()]; + long[] P = new long[p1Limbs + p.getP2Limbs()]; // Allocate P3 as a long array of size (O_MAX * O_MAX * M_VEC_LIMBS_MAX), zero-initialized. - long[] P3 = new long[p.getO() * p.getO() * p.getMVecLimbs()]; - - // seed_pk will be a reference into S. - byte[] seed_pk; + long[] P3 = new long[o * o * mVecLimbs]; // Allocate O as a byte array of size (V_MAX * O_MAX). // Here we assume V_MAX is given by p.getV() (or replace with a constant if needed). - byte[] O = new byte[p.getV() * p.getO()]; - - // Retrieve parameters from p. - int m_vec_limbs = p.getMVecLimbs(); - int param_m = p.getM(); - int param_v = p.getV(); - int param_o = p.getO(); - int param_O_bytes = p.getOBytes(); - int param_P1_limbs = p.getP1Limbs(); - int param_P3_limbs = p.getP3Limbs(); - int param_pk_seed_bytes = p.getPkSeedBytes(); - int param_sk_seed_bytes = p.getSkSeedBytes(); - - // In the C code, P1 is P and P2 is P offset by param_P1_limbs. - // In Java, we will have functions (like expandP1P2) work on the full array P. + byte[] O = new byte[v * o]; // Generate secret key seed (seed_sk) using a secure random generator. random.nextBytes(seed_sk); // S ← shake256(seed_sk, pk_seed_bytes + O_bytes) - Utils.shake256(S, param_pk_seed_bytes + param_O_bytes, seed_sk, param_sk_seed_bytes); - - // seed_pk is the beginning of S. - seed_pk = S; + Utils.shake256(seed_pk, pkSeedBytes + oBytes, seed_sk, skSeedBytes); // o ← Decode_o(S[ param_pk_seed_bytes : param_pk_seed_bytes + O_bytes ]) // Decode nibbles from S starting at offset param_pk_seed_bytes into O, // with expected output length = param_v * param_o. - Utils.decode(S, param_pk_seed_bytes, O, param_v * param_o); + Utils.decode(seed_pk, pkSeedBytes, O, v * o); // Expand P1 and P2 into the array P using seed_pk. MayoEngine.expandP1P2(p, P, seed_pk); @@ -79,99 +68,57 @@ public AsymmetricCipherKeyPair generateKeyPair() // For compute_P3, we need to separate P1 and P2. // Here, we treat P1 as the first param_P1_limbs elements of P, // and P2 as the remaining elements. - long[] P1 = P; - long[] P2 = new long[P.length - param_P1_limbs]; - System.arraycopy(P, param_P1_limbs, P2, 0, P2.length); - - // Compute P3, which (in the process) modifies P2. - computeP3(p, P1, P2, O, P3); - - // Store seed_pk into the public key cpk. - System.arraycopy(seed_pk, 0, cpk, 0, param_pk_seed_bytes); - - // Allocate an array for the "upper" part of P3. - long[] P3_upper = new long[p.getP3Limbs()]; - - // Compute Upper(P3) and store the result in P3_upper. - mUpper(p, P3, P3_upper, param_o); - - // Pack the m-vectors in P3_upper into cpk (after the seed_pk). - // The number of m-vectors to pack is (param_P3_limbs / m_vec_limbs), - // and param_m is used as the m value. - Utils.packMVecs(P3_upper, cpk, param_pk_seed_bytes, param_P3_limbs / m_vec_limbs, param_m); - // Securely clear sensitive data. -// secureClear(O); -// secureClear(P2); -// secureClear(P3); - - return new AsymmetricCipherKeyPair(new MayoPublicKeyParameter(p, cpk), new MayoPrivateKeyParameter(p, seed_sk)); - } - - /** - * Computes P3 from P1, P2, and O. - *

- * In C, compute_P3 does: - * 1. Compute P1*O + P2, storing result in P2. - * 2. Compute P3 = O^T * (P1*O + P2). - * - * @param p the parameter object. - * @param P1 the P1 matrix as a long[] array. - * @param P2 the P2 matrix as a long[] array; on output, P1*O is added to it. - * @param O the O matrix as a byte[] array. - * @param P3 the output matrix (as a long[] array) which will receive O^T*(P1*O + P2). - */ - public static void computeP3(MayoParameters p, long[] P1, long[] P2, byte[] O, long[] P3) - { - int mVecLimbs = p.getMVecLimbs(); - int paramV = p.getV(); - int paramO = p.getO(); + long[] P2 = new long[P.length - p1Limbs]; + System.arraycopy(P, p1Limbs, P2, 0, P2.length); // Compute P1 * O + P2 and store the result in P2. - GF16Utils.P1TimesO(p, P1, O, P2); + GF16Utils.P1TimesO(p, P, O, P2); // Compute P3 = O^T * (P1*O + P2). // Here, treat P2 as the bsMat for the multiplication. // Dimensions: mat = O (size: paramV x paramO), bsMat = P2 (size: paramV x paramO), // and acc (P3) will have dimensions: (paramO x paramO), each entry being an m-vector. - GF16Utils.mulAddMatTransXMMat(mVecLimbs, O, P2, P3, paramV, paramO, paramO); - } + GF16Utils.mulAddMatTransXMMat(mVecLimbs, O, P2, P3, v, o, o); - /** - * Reproduces the behavior of the C function m_upper. - *

- * For each pair (r, c) with 0 <= r <= c < size, it copies the m-vector at - * position (r, c) from 'in' to the next position in 'out' and, if r != c, - * it adds (XORs) the m-vector at position (c, r) into that same output vector. - * - * @param p the parameter object (used to get mVecLimbs) - * @param in the input long array (each vector is mVecLimbs in length) - * @param out the output long array (must be large enough to store all output vectors) - * @param size the size parameter defining the matrix dimensions. - */ - public static void mUpper(MayoParameters p, long[] in, long[] out, int size) - { - int mVecLimbs = p.getMVecLimbs(); + // Store seed_pk into the public key cpk. + System.arraycopy(seed_pk, 0, cpk, 0, pkSeedBytes); + + // Allocate an array for the "upper" part of P3. + long[] P3_upper = new long[p3Limbs]; + + // Compute Upper(P3) and store the result in P3_upper. int mVecsStored = 0; - for (int r = 0; r < size; r++) + for (int r = 0; r < o; r++) { - for (int c = r; c < size; c++) + for (int c = r; c < o; c++) { // Compute the starting index for the (r, c) vector in the input array. - int srcOffset = mVecLimbs * (r * size + c); + int srcOffset = mVecLimbs * (r * o + c); // Compute the output offset for the current stored vector. int destOffset = mVecLimbs * mVecsStored; // Copy the vector at (r, c) into the output. - System.arraycopy(in, srcOffset, out, destOffset, mVecLimbs); + System.arraycopy(P3, srcOffset, P3_upper, destOffset, mVecLimbs); // If off-diagonal, add (XOR) the vector at (c, r) into the same output vector. if (r != c) { - int srcOffset2 = mVecLimbs * (c * size + r); - GF16Utils.mVecAdd(mVecLimbs, in, srcOffset2, out, destOffset); + int srcOffset2 = mVecLimbs * (c * o + r); + Longs.xorTo(mVecLimbs, P3, srcOffset2, P3_upper, destOffset); } mVecsStored++; } } + + // Pack the m-vectors in P3_upper into cpk (after the seed_pk). + // The number of m-vectors to pack is (param_P3_limbs / m_vec_limbs), + // and param_m is used as the m value. + Utils.packMVecs(P3_upper, cpk, pkSeedBytes, p3Limbs / mVecLimbs, m); + // Securely clear sensitive data. + Arrays.clear(O); + Arrays.clear(P2); + Arrays.clear(P3); + + return new AsymmetricCipherKeyPair(new MayoPublicKeyParameter(p, cpk), new MayoPrivateKeyParameter(p, seed_sk)); } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java index e6e285167f..23aeac4dfd 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java @@ -247,11 +247,6 @@ public int getP2Bytes() return P2Bytes; } - public int getP3Bytes() - { - return P3Bytes; - } - public int getCskBytes() { return cskBytes; @@ -328,37 +323,5 @@ public int getEPKLimbs() { return getP1Limbs() + getP2Limbs() + getP3Limbs(); } - - @Override - public String toString() - { - return "MayoParameters{" + - "name='" + name + '\'' + - ", n=" + n + - ", m=" + m + - ", mVecLimbs=" + mVecLimbs + - ", o=" + o + - ", v=" + v + - ", ACols=" + ACols + - ", k=" + k + - ", q=" + q + - ", mBytes=" + mBytes + - ", OBytes=" + OBytes + - ", vBytes=" + vBytes + - ", rBytes=" + rBytes + - ", P1Bytes=" + P1Bytes + - ", P2Bytes=" + P2Bytes + - ", P3Bytes=" + P3Bytes + - ", cskBytes=" + cskBytes + - ", cpkBytes=" + cpkBytes + - ", sigBytes=" + sigBytes + - ", fTail='{" + fTail[0] + "," + fTail[1] + "," + fTail[2] + "," + fTail[3] + "}'" + - ", fTailArr='{" + fTailArr[0] + "," + fTailArr[1] + "," + fTailArr[2] + "," + fTailArr[3] + "}'" + - ", saltBytes=" + saltBytes + - ", digestBytes=" + digestBytes + - ", pkSeedBytes=" + pkSeedBytes + - ", skSeedBytes=" + skSeedBytes + - '}'; - } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index 0b2c9196f2..f9a727888a 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -8,6 +8,7 @@ import org.bouncycastle.pqc.crypto.MessageSigner; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Longs; import org.bouncycastle.util.Pack; public class MayoSigner @@ -15,7 +16,6 @@ public class MayoSigner { private SecureRandom random; MayoParameters params; - MayoEngine engine; private MayoPublicKeyParameter pubKey; private MayoPrivateKeyParameter privKey; @@ -62,14 +62,13 @@ public byte[] generateSignature(byte[] message) byte[] x = new byte[params.getK() * params.getN()]; byte[] r = new byte[params.getK() * params.getO() + 1]; byte[] s = new byte[params.getK() * params.getN()]; - byte[] tmp = new byte[params.getDigestBytes() + params.getSaltBytes() + - params.getSkSeedBytes() + 1]; + byte[] tmp = new byte[params.getDigestBytes() + params.getSaltBytes() + params.getSkSeedBytes() + 1]; byte[] sig = new byte[params.getSigBytes()]; + long[] P = new long[params.getP1Limbs() + params.getP2Limbs()]; + byte[] O = new byte[params.getV() * params.getO()]; try { - long[] P = new long[params.getP1Limbs() + params.getP2Limbs()]; - byte[] O = new byte[params.getV() * params.getO()]; // Expand secret key MayoEngine.mayoExpandSk(params, privKey.getSeedSk(), P, O); @@ -964,7 +963,7 @@ private static void mayoGenericMCalculatePS(MayoParameters p, long[] P1, long[] { for (int col = 0; col < k; col++) { - GF16Utils.mVecAdd(mVecLimbs, P1, p1Used * mVecLimbs, + Longs.xorTo(mVecLimbs, P1, p1Used * mVecLimbs, accumulator, ((row * k + col) * 16 + (S[col * n + j] & 0xFF)) * mVecLimbs); } p1Used++; @@ -974,7 +973,7 @@ private static void mayoGenericMCalculatePS(MayoParameters p, long[] P1, long[] { for (int col = 0; col < k; col++) { - GF16Utils.mVecAdd(mVecLimbs, P2, (row * o + j) * mVecLimbs, + Longs.xorTo(mVecLimbs, P2, (row * o + j) * mVecLimbs, accumulator, ((row * k + col) * 16 + (S[(col * n) + j + v] & 0xFF)) * mVecLimbs); } } @@ -987,7 +986,7 @@ private static void mayoGenericMCalculatePS(MayoParameters p, long[] P1, long[] { for (int col = 0; col < k; col++) { - GF16Utils.mVecAdd(mVecLimbs, P3, p3Used * mVecLimbs, + Longs.xorTo(mVecLimbs, P3, p3Used * mVecLimbs, accumulator, ((row * k + col) * 16 + (S[col * n + j] & 0xFF)) * mVecLimbs); } p3Used++; diff --git a/core/src/main/java/org/bouncycastle/util/Arrays.java b/core/src/main/java/org/bouncycastle/util/Arrays.java index 2067a9adb6..1fbd229188 100644 --- a/core/src/main/java/org/bouncycastle/util/Arrays.java +++ b/core/src/main/java/org/bouncycastle/util/Arrays.java @@ -1209,6 +1209,14 @@ public static void clear(int[] data) } } + public static void clear(long[] data) + { + if (null != data) + { + java.util.Arrays.fill(data, 0); + } + } + public static boolean isNullOrContainsNull(Object[] array) { if (null == array) diff --git a/core/src/main/java/org/bouncycastle/util/Longs.java b/core/src/main/java/org/bouncycastle/util/Longs.java index 443e310f4f..eaaada9b0e 100644 --- a/core/src/main/java/org/bouncycastle/util/Longs.java +++ b/core/src/main/java/org/bouncycastle/util/Longs.java @@ -52,4 +52,12 @@ public static Long valueOf(long value) { return Long.valueOf(value); } + + public static void xorTo(int len, long[] x, int xOff, long[] z, int zOff) + { + for (int i = 0; i < len; ++i) + { + z[zOff + i] ^= x[xOff + i]; + } + } } diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java index aeb9d09fbf..986de28ed2 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java @@ -70,11 +70,11 @@ public static void testTestVector(boolean enableFactory, boolean isSigner, Strin { if (buf.size() > 0) { - int count = Integer.parseInt(buf.get("count")); - if (count == 99) - { - System.out.println("break"); - } +// int count = Integer.parseInt(buf.get("count")); +// if (count == 99) +// { +// System.out.println("break"); +// } byte[] seed = Hex.decode((String)buf.get("seed")); byte[] pk = Hex.decode((String)buf.get("pk")); byte[] sk = Hex.decode((String)buf.get("sk")); @@ -138,7 +138,7 @@ public static void testTestVector(boolean enableFactory, boolean isSigner, Strin signer.init(false, pubParams); Assert.assertTrue(signer.verifySignature(message, sigGenerated)); } - System.out.println("Count " + count + " pass"); + //System.out.println("Count " + count + " pass"); } buf.clear(); continue; From d1b12046e6a3e7bec2ac32d6ba25254bfd017cb9 Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 3 Mar 2025 14:20:44 +1030 Subject: [PATCH 140/890] Refactor for Mayo --- .../pqc/crypto/mayo/GF16Utils.java | 48 +-- .../pqc/crypto/mayo/MayoKeyPairGenerator.java | 4 +- .../pqc/crypto/mayo/MayoParameters.java | 20 +- .../pqc/crypto/mayo/MayoSigner.java | 406 ++++++------------ .../pqc/crypto/test/MayoTest.java | 3 + 5 files changed, 153 insertions(+), 328 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java index d7f08cf296..f17d0608da 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java @@ -101,27 +101,6 @@ public static void mulAddMUpperTriangularMatXMat(int mVecLimbs, long[] bsMat, by } } - /** - * Computes P1_times_O. - *

- * In C: - * P1_times_O(p, P1, O, acc) calls: - * mul_add_m_upper_triangular_mat_x_mat(PARAM_m_vec_limbs(p), P1, O, acc, PARAM_v(p), PARAM_v(p), PARAM_o(p), 1); - * - * @param p the parameter object. - * @param P1 the P1 matrix as a long[] array. - * @param O the O matrix as a byte[] array. - * @param acc the output accumulator (long[] array). - */ - public static void P1TimesO(MayoParameters p, long[] P1, byte[] O, long[] acc) - { - int mVecLimbs = p.getMVecLimbs(); - int paramV = p.getV(); - int paramO = p.getO(); - // Here, bsMatRows and bsMatCols are both paramV, and matCols is paramO, triangular=1. - mulAddMUpperTriangularMatXMat(mVecLimbs, P1, O, acc, paramV, paramV, paramO, 1); - } - /** * Multiplies the transpose of a single matrix with m matrices and adds the result into acc. * @@ -304,10 +283,7 @@ public static long mulFx8(byte a, long b) int aa = a & 0xFF; // Carryless multiplication: for each bit in 'aa' (considering only the lower 4 bits), // if that bit is set, multiply 'b' (by 1, 2, 4, or 8) and XOR the result. - long p = ((aa & 1) * b) - ^ ((aa & 2) * b) - ^ ((aa & 4) * b) - ^ ((aa & 8) * b); + long p = ((aa & 1) * b) ^ ((aa & 2) * b) ^ ((aa & 4) * b) ^ ((aa & 8) * b); // Reduction mod (x^4 + x + 1): process each byte in parallel. long topP = p & 0xf0f0f0f0f0f0f0f0L; @@ -366,28 +342,6 @@ public static void matAdd(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int } } - /** - * Returns 0x00 if a equals b, otherwise returns 0xFF. - * This operation is performed in constant time. - * - * @param a an 8-bit value - * @param b an 8-bit value - * @return 0x00 if a == b, 0xFF if a != b - */ - public static byte ctCompare8(byte a, byte b) - { - // Compute the difference between a and b using XOR. - // Masking with 0xFF ensures we work with values in 0..255. - int diff = (a ^ b) & 0xFF; - // Negate the difference. - int negDiff = -diff; - // Right shift by 31 bits (since 8*sizeof(uint32_t)-1 equals 31 for 32-bit integers). - // If diff is 0, then -diff is 0, and shifting yields 0. - // If diff is nonzero, -diff is negative, so the arithmetic shift yields -1 (0xFFFFFFFF), - // which when cast to a byte becomes 0xFF. - return (byte) (negDiff >> 31); - } - public static void efUnpackMVector(int legs, long[] packedRow, int packedRowOff, byte[] out) { int outIndex = 0; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java index 80dab0898b..dac5454c11 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java @@ -72,7 +72,9 @@ public AsymmetricCipherKeyPair generateKeyPair() System.arraycopy(P, p1Limbs, P2, 0, P2.length); // Compute P1 * O + P2 and store the result in P2. - GF16Utils.P1TimesO(p, P, O, P2); +// GF16Utils.P1TimesO(p, P, O, P2); + // Here, bsMatRows and bsMatCols are both paramV, and matCols is paramO, triangular=1. + GF16Utils.mulAddMUpperTriangularMatXMat(mVecLimbs, P, O, P2, v, v, o, 1); // Compute P3 = O^T * (P1*O + P2). // Here, treat P2 as the bsMat for the multiplication. diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java index 23aeac4dfd..f1a1def99d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java @@ -18,7 +18,7 @@ public class MayoParameters 40, // r_bytes 120159, // P1_bytes 24336, // P2_bytes - 1404, // P3_bytes + // P3_bytes 24, // csk_bytes 1420, // cpk_bytes 454, // sig_bytes @@ -46,7 +46,7 @@ public class MayoParameters 34, // r_bytes 66560, // P1_bytes 34816, // P2_bytes - 4896, // P3_bytes + // P3_bytes 24, // csk_bytes 4912, // cpk_bytes 186, // sig_bytes @@ -74,7 +74,7 @@ public class MayoParameters 55, // r_bytes 317844, // P1_bytes 58320, // P2_bytes - 2970, // P3_bytes + // P3_bytes 32, // csk_bytes 2986, // cpk_bytes 681, // sig_bytes @@ -102,7 +102,7 @@ public class MayoParameters 72, // r_bytes 720863, // P1_bytes 120984, // P2_bytes - 5538, // P3_bytes + // P3_bytes 40, // csk_bytes 5554, // cpk_bytes 964, // sig_bytes @@ -129,7 +129,6 @@ public class MayoParameters private final int rBytes; private final int P1Bytes; private final int P2Bytes; - private final int P3Bytes; private final int cskBytes; private final int cpkBytes; private final int sigBytes; @@ -141,7 +140,7 @@ public class MayoParameters private final int skSeedBytes; private MayoParameters(String name, int n, int m, int mVecLimbs, int o, int v, int ACols, int k, int q, - int mBytes, int OBytes, int vBytes, int rBytes, int P1Bytes, int P2Bytes, int P3Bytes, + int mBytes, int OBytes, int vBytes, int rBytes, int P1Bytes, int P2Bytes, int cskBytes, int cpkBytes, int sigBytes, int[] fTail, byte[] fTailArr, int saltBytes, int digestBytes, int pkSeedBytes, int skSeedBytes) { @@ -160,7 +159,6 @@ private MayoParameters(String name, int n, int m, int mVecLimbs, int o, int v, i this.rBytes = rBytes; this.P1Bytes = P1Bytes; this.P2Bytes = P2Bytes; - this.P3Bytes = P3Bytes; this.cskBytes = cskBytes; this.cpkBytes = cpkBytes; this.sigBytes = sigBytes; @@ -315,13 +313,5 @@ public int getP3Limbs() { return ((o * (o + 1)) / 2) * mVecLimbs; } - - /** - * Computes: P1_limbs + P2_limbs + P3_limbs - */ - public int getEPKLimbs() - { - return getP1Limbs() + getP2Limbs() + getP3Limbs(); - } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index f9a727888a..a68d01d15c 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -52,20 +52,27 @@ public void init(boolean forSigning, CipherParameters param) @Override public byte[] generateSignature(byte[] message) { + int k = params.getK(); + int v = params.getV(); + int o = params.getO(); + int saltBytes = params.getSaltBytes(); + int mVecLimbs = params.getMVecLimbs(); byte[] tenc = new byte[params.getMBytes()]; byte[] t = new byte[params.getM()]; byte[] y = new byte[params.getM()]; - byte[] salt = new byte[params.getSaltBytes()]; - byte[] V = new byte[params.getK() * params.getVBytes() + params.getRBytes()]; - byte[] Vdec = new byte[params.getV() * params.getK()]; - byte[] A = new byte[((params.getM() + 7) / 8 * 8) * (params.getK() * params.getO() + 1)]; - byte[] x = new byte[params.getK() * params.getN()]; - byte[] r = new byte[params.getK() * params.getO() + 1]; - byte[] s = new byte[params.getK() * params.getN()]; - byte[] tmp = new byte[params.getDigestBytes() + params.getSaltBytes() + params.getSkSeedBytes() + 1]; + byte[] salt = new byte[saltBytes]; + byte[] V = new byte[k * params.getVBytes() + params.getRBytes()]; + byte[] Vdec = new byte[v * k]; + byte[] A = new byte[((params.getM() + 7) / 8 * 8) * (k * o + 1)]; + byte[] x = new byte[k * params.getN()]; + byte[] r = new byte[k * o + 1]; + byte[] s = new byte[k * params.getN()]; + byte[] tmp = new byte[params.getDigestBytes() + saltBytes + params.getSkSeedBytes() + 1]; byte[] sig = new byte[params.getSigBytes()]; long[] P = new long[params.getP1Limbs() + params.getP2Limbs()]; - byte[] O = new byte[params.getV() * params.getO()]; + byte[] O = new byte[v * o]; + long[] Mtmp = new long[k * o * params.getMVecLimbs()]; + long[] vPv = new long[k * k * params.getMVecLimbs()]; try { @@ -83,16 +90,16 @@ public byte[] generateSignature(byte[] message) System.arraycopy(salt, 0, tmp, params.getDigestBytes(), salt.length); // Hash to salt - System.arraycopy(privKey.getSeedSk(), 0, tmp, params.getDigestBytes() + params.getSaltBytes(), + System.arraycopy(privKey.getSeedSk(), 0, tmp, params.getDigestBytes() + saltBytes, params.getSkSeedBytes()); - shake.update(tmp, 0, params.getDigestBytes() + params.getSaltBytes() + + shake.update(tmp, 0, params.getDigestBytes() + saltBytes + params.getSkSeedBytes()); - shake.doFinal(salt, 0, params.getSaltBytes()); + shake.doFinal(salt, 0, saltBytes); // Hash to t - System.arraycopy(salt, 0, tmp, params.getDigestBytes(), params.getSaltBytes()); - shake.update(tmp, 0, params.getDigestBytes() + params.getSaltBytes()); + System.arraycopy(salt, 0, tmp, params.getDigestBytes(), saltBytes); + shake.update(tmp, 0, params.getDigestBytes() + saltBytes); shake.doFinal(tenc, 0, params.getMBytes()); Utils.decode(tenc, t, params.getM()); @@ -105,27 +112,36 @@ public byte[] generateSignature(byte[] message) shake.doFinal(V, 0, V.length); // Decode vectors - for (int i = 0; i < params.getK(); i++) + for (int i = 0; i < k; i++) { - Utils.decode(V, i * params.getVBytes(), Vdec, i * params.getV(), params.getV()); + Utils.decode(V, i * params.getVBytes(), Vdec, i * v, v); } - // Compute matrices - long[] Mtmp = new long[params.getK() * params.getO() * params.getMVecLimbs()]; - long[] vPv = new long[params.getK() * params.getK() * params.getMVecLimbs()]; - computeMandVPV(params, Vdec, P, params.getP1Limbs(), P, Mtmp, vPv); + //computeMandVPV(params, Vdec, P, params.getP1Limbs(), P, Mtmp, vPv); + // Compute VL: VL = Vdec * L + GF16Utils.mulAddMatXMMat(mVecLimbs, Vdec, P, params.getP1Limbs(), Mtmp, k, v, o); + + // Compute VP1V: + // Allocate temporary array for Pv. Its length is V_MAX * K_MAX * M_VEC_LIMBS_MAX. + int size = v * k * mVecLimbs; + long[] Pv = new long[size]; // automatically initialized to zero in Java + + // Compute Pv = P1 * V^T (using upper triangular multiplication) + GF16Utils.mulAddMUpperTriangularMatXMatTrans(mVecLimbs, P, Vdec, Pv, v, v, k, 1); + // Compute VP1V = Vdec * Pv + GF16Utils.mulAddMatXMMat(mVecLimbs, Vdec, Pv, vPv, k, v, k); - computeRHS(params, vPv, t, y); + computeRHS(vPv, t, y); computeA(params, Mtmp, A); // Clear trailing bytes for (int i = 0; i < params.getM(); ++i) { - A[(i + 1) * (params.getK() * params.getO() + 1) - 1] = 0; + A[(i + 1) * (k * o + 1) - 1] = 0; } - Utils.decode(V, params.getK() * params.getVBytes(), r, 0, - params.getK() * params.getO()); + Utils.decode(V, k * params.getVBytes(), r, 0, + k * o); if (sampleSolution(params, A, y, r, x) != 0) { @@ -139,22 +155,20 @@ public byte[] generateSignature(byte[] message) } // Compute final signature components - byte[] Ox = new byte[params.getV()]; - for (int i = 0; i < params.getK(); i++) + byte[] Ox = new byte[v]; + for (int i = 0; i < k; i++) { - byte[] vi = Arrays.copyOfRange(Vdec, i * params.getV(), - (i + 1) * params.getV()); - GF16Utils.matMul(O, 0, x, i * params.getO(), Ox, 0, - params.getO(), params.getN() - params.getO(), 1); - GF16Utils.matAdd(vi, 0, Ox, 0, s, i * params.getN(), params.getV(), 1); - System.arraycopy(x, i * params.getO(), s, - i * params.getN() + params.getN() - params.getO(), params.getO()); + byte[] vi = Arrays.copyOfRange(Vdec, i * v, (i + 1) * v); + GF16Utils.matMul(O, 0, x, i * o, Ox, 0, o, params.getN() - o, 1); + GF16Utils.matAdd(vi, 0, Ox, 0, s, i * params.getN(), v, 1); + System.arraycopy(x, i * o, s, + i * params.getN() + params.getN() - o, o); } // Encode and add salt - Utils.encode(s, sig, params.getN() * params.getK()); - System.arraycopy(salt, 0, sig, sig.length - params.getSaltBytes(), - params.getSaltBytes()); + Utils.encode(s, sig, params.getN() * k); + System.arraycopy(salt, 0, sig, sig.length - saltBytes, + saltBytes); return Arrays.concatenate(sig, message); } @@ -175,13 +189,15 @@ public byte[] generateSignature(byte[] message) } } - @Override public boolean verifySignature(byte[] message, byte[] signature) { - final int paramM = params.getM(); - final int paramN = params.getN(); - final int paramK = params.getK(); + final int m = params.getM(); + final int n = params.getN(); + final int k = params.getK(); + int p1Limbs = params.getP1Limbs(); + int p2Limbs = params.getP2Limbs(); + int p3Limbs = params.getP3Limbs(); final int paramMBytes = params.getMBytes(); final int paramSigBytes = params.getSigBytes(); final int paramDigestBytes = params.getDigestBytes(); @@ -191,20 +207,16 @@ public boolean verifySignature(byte[] message, byte[] signature) byte[] t = new byte[params.getM()]; byte[] y = new byte[2 * params.getM()]; byte[] s = new byte[params.getK() * params.getN()]; - long[] pk = new long[params.getP1Limbs() + params.getP2Limbs() + params.getP3Limbs()]; + long[] pk = new long[p1Limbs + params.getP2Limbs() + params.getP3Limbs()]; byte[] tmp = new byte[params.getDigestBytes() + params.getSaltBytes()]; byte[] cpk = pubKey.getEncoded(); // Expand public key // mayo_expand_pk MayoEngine.expandP1P2(params, pk, cpk); - Utils.unpackMVecs(cpk, params.getPkSeedBytes(), pk, params.getP1Limbs() + params.getP2Limbs(), params.getP3Limbs() / params.getMVecLimbs(), params.getM()); + Utils.unpackMVecs(cpk, params.getPkSeedBytes(), pk, p1Limbs + params.getP2Limbs(), params.getP3Limbs() / params.getMVecLimbs(), params.getM()); // Split pk into P1, P2, P3 - int p1Limbs = params.getP1Limbs(); - int p2Limbs = params.getP2Limbs(); - int p3Limbs = params.getP3Limbs(); - long[] P1 = new long[p1Limbs]; long[] P2 = new long[p2Limbs]; long[] P3 = new long[p3Limbs]; @@ -218,74 +230,31 @@ public boolean verifySignature(byte[] message, byte[] signature) // Compute t System.arraycopy(signature, paramSigBytes - paramSaltBytes, tmp, paramDigestBytes, paramSaltBytes); Utils.shake256(tEnc, paramMBytes, tmp, paramDigestBytes + paramSaltBytes); - Utils.decode(tEnc, t, paramM); + Utils.decode(tEnc, t, m); // Decode signature - Utils.decode(signature, s, paramK * paramN); + Utils.decode(signature, s, k * n); // Evaluate public map - evalPublicMap(params, s, P1, P2, P3, y); +// evalPublicMap(params, s, P1, P2, P3, y); + int mVecLimbs = (params.getM() + 15) / 16; + long[] SPS = new long[k * k * mVecLimbs]; + long[] PS = new long[n * k * mVecLimbs]; + mayoGenericMCalculatePS(params, P1, P2, P3, s, m, params.getV(), params.getO(), k, PS); + mayoGenericMCalculateSPS(PS, s, m, k, n, SPS); + byte[] zero = new byte[m]; + computeRHS(SPS, zero, y); // Compare results - return Arrays.constantTimeAreEqual(paramM, y, 0, t, 0); + return Arrays.constantTimeAreEqual(m, y, 0, t, 0); } - - /** - * Computes the product of the matrix P1 (bit-sliced) with the transpose of matrix V and adds the result to acc. - * - * @param p the parameters object - * @param P1 the bit-sliced matrix P1 as a long array - * @param V the matrix V as a byte array - * @param acc the accumulator array where the result is added - */ - public static void P1TimesVt(MayoParameters p, long[] P1, byte[] V, long[] acc) + public void computeRHS(long[] vPv, byte[] t, byte[] y) { - int mVecLimbs = p.getMVecLimbs(); - int paramV = p.getV(); - int paramK = p.getK(); - // triangular parameter is set to 1 - GF16Utils.mulAddMUpperTriangularMatXMatTrans(mVecLimbs, P1, V, acc, paramV, paramV, paramK, 1); - } - - /** - * Computes the matrices M and VP1V from the given input matrices. - * - * @param p the parameters object - * @param Vdec the decoded V matrix as a byte array - * @param L the matrix L as a long array - * @param P1 the bit-sliced matrix P1 as a long array - * @param VL the output accumulator for VL - * @param VP1V the output accumulator for VP1V - */ - public static void computeMandVPV(MayoParameters p, byte[] Vdec, long[] L, int Loff, long[] P1, long[] VL, long[] VP1V) - { - int paramK = p.getK(); - int paramV = p.getV(); - int paramO = p.getO(); - int mVecLimbs = p.getMVecLimbs(); - - // Compute VL: VL = Vdec * L - GF16Utils.mulAddMatXMMat(mVecLimbs, Vdec, L, Loff, VL, paramK, paramV, paramO); - - // Compute VP1V: - // Allocate temporary array for Pv. Its length is V_MAX * K_MAX * M_VEC_LIMBS_MAX. - int size = p.getV() * p.getK() * p.getMVecLimbs(); - long[] Pv = new long[size]; // automatically initialized to zero in Java - - // Compute Pv = P1 * V^T (using upper triangular multiplication) - P1TimesVt(p, P1, Vdec, Pv); - - // Compute VP1V = Vdec * Pv - GF16Utils.mulAddMatXMMat(mVecLimbs, Vdec, Pv, VP1V, paramK, paramV, paramK); - } - - public static void computeRHS(MayoParameters p, long[] vPv, byte[] t, byte[] y) - { - final int m = p.getM(); - final int mVecLimbs = p.getMVecLimbs(); - final int k = p.getK(); - final int[] fTail = p.getFTail(); + final int m = params.getM(); + final int mVecLimbs = params.getMVecLimbs(); + final int k = params.getK(); + final int[] fTail = params.getFTail(); final int topPos = ((m - 1) % 16) * 4; @@ -363,28 +332,25 @@ public static void computeRHS(MayoParameters p, long[] vPv, byte[] t, byte[] y) // Compute y for (int i = 0; i < m; i += 2) { - int bytePos = i / 2; + int bytePos = i >> 1; y[i] = (byte)(t[i] ^ (tempBytes[bytePos] & 0xF)); y[i + 1] = (byte)(t[i + 1] ^ ((tempBytes[bytePos] >>> 4) & 0xF)); } } - private static final int F_TAIL_LEN = 4; - private static final long EVEN_NIBBLES = 0x0F0F0F0F0F0F0F0FL; private static final long EVEN_BYTES = 0x00FF00FF00FF00FFL; private static final long EVEN_2BYTES = 0x0000FFFF0000FFFFL; - private static final long EVEN_HALF = 0x00000000FFFFFFFFL; private static final long LOW_BIT_IN_NIBBLE = 0x1111111111111111L; - public static void computeA(MayoParameters p, long[] VtL, byte[] AOut) + public static void computeA(MayoParameters params, long[] Mtmp, byte[] AOut) { - final int k = p.getK(); - final int o = p.getO(); - final int m = p.getM(); - final int mVecLimbs = p.getMVecLimbs(); - final int ACols = p.getACols(); - final byte[] fTailArr = p.getFTailArr(); + final int k = params.getK(); + final int o = params.getO(); + final int m = params.getM(); + final int mVecLimbs = params.getMVecLimbs(); + final int ACols = params.getACols(); + final byte[] fTailArr = params.getFTailArr(); int bitsToShift = 0; int wordsToShift = 0; @@ -400,7 +366,7 @@ public static void computeA(MayoParameters p, long[] VtL, byte[] AOut) for (int i = 0; i < o * k; i++) { int idx = i * mVecLimbs + mVecLimbs - 1; - VtL[idx] &= mask; + Mtmp[idx] &= mask; } } @@ -415,7 +381,7 @@ public static void computeA(MayoParameters p, long[] VtL, byte[] AOut) for (int limb = 0; limb < mVecLimbs; limb++) { int idx = mjOffset + limb + c * mVecLimbs; - long value = VtL[idx]; + long value = Mtmp[idx]; int aIndex = o * i + c + (limb + wordsToShift) * AWidth; A[aIndex] ^= value << bitsToShift; @@ -436,7 +402,7 @@ public static void computeA(MayoParameters p, long[] VtL, byte[] AOut) for (int limb = 0; limb < mVecLimbs; limb++) { int idx = miOffset + limb + c * mVecLimbs; - long value = VtL[idx]; + long value = Mtmp[idx]; int aIndex = o * j + c + (limb + wordsToShift) * AWidth; A[aIndex] ^= value << bitsToShift; @@ -519,7 +485,7 @@ private static void transpose16x16Nibbles(long[] M, int offset) { int idx1 = offset + i; int idx2 = offset + i + 1; - long t = ((M[idx1] >>> 4) ^ M[idx2]) & EVEN_NIBBLES; + long t = ((M[idx1] >>> 4) ^ M[idx2]) & 0x0F0F0F0F0F0F0F0FL; M[idx1] ^= t << 4; M[idx2] ^= t; } @@ -549,7 +515,7 @@ private static void transpose16x16Nibbles(long[] M, int offset) for (int i = 0; i < 8; i++) { int base = offset + i; - long t = ((M[base] >>> 32) ^ M[base + 8]) & EVEN_HALF; + long t = ((M[base] >>> 32) ^ M[base + 8]) & 0x00000000FFFFFFFFL; M[base] ^= t << 32; M[base + 8] ^= t; } @@ -568,7 +534,6 @@ public int sampleSolution(MayoParameters params, byte[] A, byte[] y, // Compute Ar matrix product byte[] Ar = new byte[m]; -// Arrays.fill(Ar, (byte)0); // Clear last column of A for (int i = 0; i < m; i++) @@ -605,11 +570,10 @@ public int sampleSolution(MayoParameters params, byte[] A, byte[] y, for (int col = row; col <= colUpperBound; col++) { - byte correctCol = GF16Utils.ctCompare8(A[row * aCols + col], (byte)0); - byte mask = (byte)(correctCol & ~finished); + byte correctCol = (byte)((-(A[row * aCols + col] & 0xFF)) >> 31); // Update x[col] using constant-time mask - byte u = (byte)(mask & A[row * aCols + aCols - 1]); + byte u = (byte)(correctCol & ~finished & A[row * aCols + aCols - 1]); //System.out.println("x[col]: " + x[col] + ", u: " + u); x[col] ^= u; @@ -639,11 +603,6 @@ public int sampleSolution(MayoParameters params, byte[] A, byte[] y, return 1; } - // Adjust these as needed. In our translation we compute rowLen from ncols. - // These blockers are used in the C code for constant-time masking. - private static final long UINT64_BLOCKER = 0L; - private static final int UNSIGNED_CHAR_BLOCKER = 0; // Not used in our Java version. - /** * Converts a matrix A (given as a flat array of GF(16) elements, one per byte) * into row echelon form (with ones on the first nonzero entries) in constant time. @@ -666,8 +625,21 @@ public void ef(byte[] A, int nrows, int ncols) // Pack the matrix rows. for (int i = 0; i < nrows; i++) { - long[] packedRow = packRow(A, i, ncols); - System.arraycopy(packedRow, 0, packedA, i * rowLen, rowLen); + //packRow(A, i, ncols); + // Process each 64-bit word (each holds 16 nibbles). + for (int word = 0; word < rowLen; word++) + { + long wordVal = 0; + for (int nibble = 0; nibble < 16; nibble++) + { + int col = (word << 4) + nibble; + if (col < ncols) + { + wordVal |= ((long)A[i * ncols + col] & 0xF) << (nibble << 2); + } + } + packedA[word + i * rowLen] = wordVal; + } } int pivotRowIndex = 0; @@ -691,7 +663,8 @@ public void ef(byte[] A, int nrows, int ncols) for (int row = lowerBound; row <= searchUpper; row++) { long isPivotRow = ~ctCompare64(row, pivotRowIndex); - long belowPivotRow = ct64IsGreaterThan(row, pivotRowIndex); + //ct64IsGreaterThan(a, b): Returns 0xFFFFFFFFFFFFFFFF if a > b, 0 otherwise. + long belowPivotRow = ((long)pivotRowIndex - (long)row) >> 63; for (int j = 0; j < rowLen; j++) { // The expression below accumulates (in constant time) the candidate pivot row. @@ -699,7 +672,7 @@ public void ef(byte[] A, int nrows, int ncols) & packedA[row * rowLen + j]; } // Extract candidate pivot element from the packed row. - pivot = mExtractElement(pivotRow, pivotCol); + pivot = (int)((pivotRow[pivotCol >>> 4] >>> ((pivotCol & 15) << 2)) & 0xF); pivotIsZero = ~ctCompare64(pivot, 0); } @@ -715,9 +688,8 @@ public void ef(byte[] A, int nrows, int ncols) for (int col = 0; col < rowLen; col++) { // Since the masks are disjoint, addition is equivalent to OR. - packedA[row * rowLen + col] = - (doNotCopy & packedA[row * rowLen + col]) | - (doCopy & pivotRow2[col]); + packedA[row * rowLen + col] = (doNotCopy & packedA[row * rowLen + col]) | + (doCopy & pivotRow2[col]); } } @@ -742,77 +714,20 @@ public void ef(byte[] A, int nrows, int ncols) for (int i = 0; i < nrows; i++) { GF16Utils.efUnpackMVector(rowLen, packedA, i * rowLen, temp); - for (int j = 0; j < ncols; j++) + if (ncols >= 0) { - A[i * ncols + j] = temp[j]; + System.arraycopy(temp, 0, A, i * ncols, ncols); } } } - /** - * Packs one row of GF(16) elements (stored in A) into a long[]. - * Each long holds 16 GF(16) elements (4 bits each). - * - * @param A the flat input matrix (row-major) - * @param row the row index to pack - * @param ncols the number of columns (GF(16) elements) in a row - * @return an array of longs representing the packed row. - */ - private static long[] packRow(byte[] A, int row, int ncols) - { - int rowLen = (ncols + 15) / 16; // number of longs needed for this row - long[] packed = new long[rowLen]; - // Process each 64-bit word (each holds 16 nibbles). - for (int word = 0; word < rowLen; word++) - { - long wordVal = 0; - for (int nibble = 0; nibble < 16; nibble++) - { - int col = word * 16 + nibble; - if (col < ncols) - { - int element = A[row * ncols + col] & 0xF; - wordVal |= ((long)element) << (4 * nibble); - } - } - packed[word] = wordVal; - } - return packed; - } - /** * Constant-time comparison: returns 0 if a==b, else returns all 1s (0xFFFFFFFFFFFFFFFF). */ private static long ctCompare64(int a, int b) { // Compute (-(a XOR b)) >> 63 then XOR with UINT64_BLOCKER. - long diff = -(long)(a ^ b); - long shift = diff >> 63; // arithmetic shift; results in 0 or -1. - return shift ^ UINT64_BLOCKER; - } - - /** - * Returns 0xFFFFFFFFFFFFFFFF if a > b, 0 otherwise. - */ - private static long ct64IsGreaterThan(int a, int b) - { - long diff = (long)b - (long)a; - long shift = diff >> 63; - return shift ^ UINT64_BLOCKER; - } - - /** - * Extracts the GF(16) element at a given column index from a packed row. - * - * @param in the packed row (array of longs) - * @param index the column index (0-indexed) - * @return the GF(16) element (an int in 0..15) - */ - private static int mExtractElement(long[] in, int index) - { - int leg = index / 16; - int offset = index % 16; - return (int)((in[leg] >>> (4 * offset)) & 0xF); + return (-(long)(a ^ b)) >> 63; } /** @@ -826,9 +741,7 @@ private static int mExtractElement(long[] in, int index) */ private static int mExtractElementFromPacked(long[] packedA, int row, int rowLen, int index) { - int leg = index / 16; - int offset = index % 16; - return (int)((packedA[row * rowLen + leg] >>> (4 * offset)) & 0xF); + return (int)((packedA[row * rowLen + (index >>> 4)] >>> ((index & 15) << 2)) & 0xF); } /** @@ -841,8 +754,7 @@ private static int inverseF(int a) int a4 = mulF(a2, a2); int a8 = mulF(a4, a4); int a6 = mulF(a2, a4); - int a14 = mulF(a8, a6); - return a14; + return mulF(a8, a6); } /** @@ -859,8 +771,7 @@ public static int mulF(int a, int b) ((a & 8) * b); // Reduce modulo f(X) = x^4 + x + 1. int topP = p & 0xF0; - int out = (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0F; - return out; + return (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0F; } /** @@ -927,82 +838,57 @@ private static int mulTable(int b) return x ^ (highHalf >>> 4) ^ (highHalf >>> 3); } - private static void evalPublicMap(MayoParameters p, byte[] s, long[] P1, long[] P2, long[] P3, byte[] eval) - { - int mVecLimbs = (p.getM() + 15) / 16; - long[] SPS = new long[p.getK() * p.getK() * mVecLimbs]; - mCalculatePsSps(p, P1, P2, P3, s, SPS); - byte[] zero = new byte[p.getM()]; - computeRHS(p, SPS, zero, eval); - } - - private static void mCalculatePsSps(MayoParameters p, long[] P1, long[] P2, long[] P3, byte[] s, long[] SPS) - { - int m = p.getM(); - int v = p.getV(); - int o = p.getO(); - int k = p.getK(); - int mVecLimbs = (m + 15) / 16; - - long[] PS = new long[p.getN() * p.getK() * mVecLimbs]; - mayoGenericMCalculatePS(p, P1, P2, P3, s, m, v, o, k, PS); - mayoGenericMCalculateSPS(PS, s, m, k, p.getN(), SPS); - } - private static void mayoGenericMCalculatePS(MayoParameters p, long[] P1, long[] P2, long[] P3, byte[] S, int m, int v, int o, int k, long[] PS) { int n = o + v; int mVecLimbs = (m + 15) / 16; long[] accumulator = new long[16 * ((p.getM() + 15) / 16 * p.getK() * p.getN() * mVecLimbs)]; - - int p1Used = 0; - for (int row = 0; row < v; row++) + int o_mVecLimbs = o * mVecLimbs; + int pUsed = 0; + for (int row = 0, krow = 0, orow_mVecLimbs = 0; row < v; row++, krow += k, orow_mVecLimbs += o_mVecLimbs) { for (int j = row; j < v; j++) { - for (int col = 0; col < k; col++) + for (int col = 0, ncol = 0; col < k; col++, ncol += n) { - Longs.xorTo(mVecLimbs, P1, p1Used * mVecLimbs, - accumulator, ((row * k + col) * 16 + (S[col * n + j] & 0xFF)) * mVecLimbs); + Longs.xorTo(mVecLimbs, P1, pUsed, accumulator, (((krow + col) << 4) + (S[ncol + j] & 0xFF)) * mVecLimbs); } - p1Used++; + pUsed += mVecLimbs; } - for (int j = 0; j < o; j++) + for (int j = 0, orow_j_mVecLimbs = orow_mVecLimbs; j < o; j++, orow_j_mVecLimbs += mVecLimbs) { - for (int col = 0; col < k; col++) + for (int col = 0, ncol = 0; col < k; col++, ncol += n) { - Longs.xorTo(mVecLimbs, P2, (row * o + j) * mVecLimbs, - accumulator, ((row * k + col) * 16 + (S[(col * n) + j + v] & 0xFF)) * mVecLimbs); + Longs.xorTo(mVecLimbs, P2, orow_j_mVecLimbs, accumulator, (((krow + col) << 4) + (S[ncol + j + v] & 0xFF)) * mVecLimbs); } } } - int p3Used = 0; - for (int row = v; row < n; row++) + pUsed = 0; + for (int row = v, krow = v * k; row < n; row++, krow += k) { for (int j = row; j < n; j++) { - for (int col = 0; col < k; col++) + for (int col = 0, ncol = 0; col < k; col++, ncol += n) { - Longs.xorTo(mVecLimbs, P3, p3Used * mVecLimbs, - accumulator, ((row * k + col) * 16 + (S[col * n + j] & 0xFF)) * mVecLimbs); + Longs.xorTo(mVecLimbs, P3, pUsed, accumulator, (((krow + col) << 4) + (S[ncol + j] & 0xFF)) * mVecLimbs); } - p3Used++; + pUsed += mVecLimbs; } } - for (int i = 0; i < n * k; i++) + for (int i = 0, imVecLimbs = 0; i < n * k; i++, imVecLimbs += mVecLimbs) { - mVecMultiplyBins(mVecLimbs, accumulator, i * 16 * mVecLimbs, PS, i * mVecLimbs); + mVecMultiplyBins(mVecLimbs, accumulator, imVecLimbs << 4, PS, imVecLimbs); } } private static void mayoGenericMCalculateSPS(long[] PS, byte[] S, int m, int k, int n, long[] SPS) { final int mVecLimbs = (m + 15) / 16; - final int accumulatorSize = 16 * mVecLimbs * k * k; + final int accumulatorSize = (mVecLimbs * k * k) << 4; final long[] accumulator = new long[accumulatorSize]; // Accumulation phase @@ -1015,7 +901,7 @@ private static void mayoGenericMCalculateSPS(long[] PS, byte[] S, int m, int k, { final int psOffset = (j * k + col) * mVecLimbs; final int accOffset = ((row * k + col) * 16 + sVal) * mVecLimbs; - mVecAdd(mVecLimbs, PS, psOffset, accumulator, accOffset); + Longs.xorTo(mVecLimbs, PS, psOffset, accumulator, accOffset); } } } @@ -1027,16 +913,6 @@ private static void mayoGenericMCalculateSPS(long[] PS, byte[] S, int m, int k, } } - // Helper method for vector addition (XOR) - private static void mVecAdd(int limbs, long[] src, int srcOffset, long[] dest, int destOffset) - { - for (int i = 0; i < limbs; i++) - { - dest[destOffset + i] ^= src[srcOffset + i]; - } - } - - // Main bin processing method private static void mVecMultiplyBins(int mVecLimbs, long[] bins, int binOffset, long[] ps, int psOff) { // Series of modular operations as per original C code @@ -1052,8 +928,8 @@ private static void mVecMultiplyBins(int mVecLimbs, long[] bins, int binOffset, mVecMulAddX(mVecLimbs, bins, binOffset + 8 * mVecLimbs, bins, binOffset + 4 * mVecLimbs); mVecMulAddXInv(mVecLimbs, bins, binOffset + 13 * mVecLimbs, bins, binOffset + 9 * mVecLimbs); mVecMulAddX(mVecLimbs, bins, binOffset + 4 * mVecLimbs, bins, binOffset + 2 * mVecLimbs); - mVecMulAddXInv(mVecLimbs, bins, binOffset + 9 * mVecLimbs, bins, binOffset + 1 * mVecLimbs); - mVecMulAddX(mVecLimbs, bins, binOffset + 2 * mVecLimbs, bins, binOffset + 1 * mVecLimbs); + mVecMulAddXInv(mVecLimbs, bins, binOffset + 9 * mVecLimbs, bins, binOffset + mVecLimbs); + mVecMulAddX(mVecLimbs, bins, binOffset + 2 * mVecLimbs, bins, binOffset + mVecLimbs); System.arraycopy(bins, mVecLimbs + binOffset, ps, psOff, mVecLimbs); } @@ -1064,8 +940,9 @@ private static void mVecMulAddXInv(int limbs, long[] in, int inOffset, final long maskLsb = 0x1111111111111111L; for (int i = 0; i < limbs; i++) { - final long t = in[inOffset + i] & maskLsb; - acc[accOffset + i] ^= ((in[inOffset + i] ^ t) >>> 1) ^ (t * 9); + long input = in[inOffset + i]; + long t = input & maskLsb; + acc[accOffset + i] ^= ((input ^ t) >>> 1) ^ (t * 9); } } @@ -1075,10 +952,9 @@ private static void mVecMulAddX(int limbs, long[] in, int inOffset, final long maskMsb = 0x8888888888888888L; for (int i = 0; i < limbs; i++) { - final long t = in[inOffset + i] & maskMsb; - acc[accOffset + i] ^= ((in[inOffset + i] ^ t) << 1) ^ ((t >>> 3) * 3); + long input = in[inOffset + i]; + long t = input & maskMsb; + acc[accOffset + i] ^= ((input ^ t) << 1) ^ ((t >>> 3) * 3); } } - - } diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java index ceb91d4601..e479221220 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java @@ -48,6 +48,7 @@ public static void main(String[] args) public void testTestVectors() throws Exception { + long start = System.currentTimeMillis(); TestUtils.testTestVector(false, false, "pqc/crypto/mayo", files, new TestUtils.KeyGenerationOperation() { @Override @@ -90,5 +91,7 @@ public MessageSigner getMessageSigner() return new MayoSigner(); } }); + long end = System.currentTimeMillis(); + System.out.println("time cost: " + (end - start) +"\n"); } } From 7a48919f76b90f952aee41bb2597e9bb87e03d40 Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 3 Mar 2025 18:01:29 +1030 Subject: [PATCH 141/890] Refactor for Mayo --- .../pqc/crypto/mayo/GF16Utils.java | 140 +++++++----------- .../pqc/crypto/mayo/MayoKeyPairGenerator.java | 34 ++--- .../pqc/crypto/mayo/MayoSigner.java | 108 +++++++------- 3 files changed, 115 insertions(+), 167 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java index f17d0608da..7882cc472a 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java @@ -1,44 +1,8 @@ package org.bouncycastle.pqc.crypto.mayo; -import org.bouncycastle.util.Pack; - public class GF16Utils { - /** - * Multiplies a 64-bit limb by a GF(16) element (represented as an int, 0–255). - * This emulates gf16v_mul_u64 from C. - * - * @param a a 64-bit limb - * @param b an 8-bit GF(16) element (only the low 4 bits are used) - * @return the product as a 64-bit limb - */ - public static long gf16vMulU64(long a, int b) - { - long maskMsb = 0x8888888888888888L; - // In the original code there is a conditional XOR with unsigned_char_blocker; - // here we simply use b directly. - long b32 = b & 0x00000000FFFFFFFFL; - long r64 = a * (b32 & 1); - - long a_msb = a & maskMsb; - a ^= a_msb; - a = (a << 1) ^ ((a_msb >>> 3) * 3); - r64 ^= a * ((b32 >> 1) & 1); - - a_msb = a & maskMsb; - a ^= a_msb; - a = (a << 1) ^ ((a_msb >>> 3) * 3); - r64 ^= a * ((b32 >>> 2) & 1); - - a_msb = a & maskMsb; - a ^= a_msb; - a = (a << 1) ^ ((a_msb >>> 3) * 3); - r64 ^= a * ((b32 >> 3) & 1); - - return r64; - } - /** * Multiplies each limb of a GF(16) vector (subarray of 'in') by the GF(16) element 'a' * and XORs the result into the corresponding subarray of acc. @@ -48,15 +12,40 @@ public static long gf16vMulU64(long a, int b) * @param mVecLimbs the number of limbs in the vector * @param in the input long array containing the vector; the vector starts at index inOffset * @param inOffset the starting index in 'in' - * @param a the GF(16) element (0–255) to multiply by + * @param b the GF(16) element (0–255) to multiply by * @param acc the accumulator long array; the target vector starts at index accOffset * @param accOffset the starting index in 'acc' */ - public static void mVecMulAdd(int mVecLimbs, long[] in, int inOffset, int a, long[] acc, int accOffset) + public static void mVecMulAdd(int mVecLimbs, long[] in, int inOffset, int b, long[] acc, int accOffset) { + long maskMsb = 0x8888888888888888L; + long a, r64, a_msb; + long b32 = b & 0x00000000FFFFFFFFL; + long b32and1 = b32 & 1; + long b32_1_1 = ((b32 >>> 1) & 1); + long b32_2_1 = ((b32 >>> 2) & 1); + long b32_3_1 = ((b32 >>> 3) & 1); for (int i = 0; i < mVecLimbs; i++) { - acc[accOffset + i] ^= gf16vMulU64(in[inOffset + i], a); + // In the original code there is a conditional XOR with unsigned_char_blocker; + // here we simply use b directly. + a = in[inOffset + i]; + r64 = a * b32and1; + + a_msb = a & maskMsb; + a ^= a_msb; + a = (a << 1) ^ ((a_msb >>> 3) * 3); + r64 ^= a * b32_1_1; + + a_msb = a & maskMsb; + a ^= a_msb; + a = (a << 1) ^ ((a_msb >>> 3) * 3); + r64 ^= a * b32_2_1; + + a_msb = a & maskMsb; + a ^= a_msb; + a = (a << 1) ^ ((a_msb >>> 3) * 3); + acc[accOffset + i] ^= r64 ^ (a * b32_3_1); } } @@ -65,38 +54,32 @@ public static void mVecMulAdd(int mVecLimbs, long[] in, int inOffset, int a, lon * Performs the multiplication and accumulation of a block of an upper‐triangular matrix * times a second matrix. * - * @param mVecLimbs number of limbs per m-vector. - * @param bsMat the “basis” matrix (as a flat long[] array); each entry occupies mVecLimbs elements. - * @param mat the second matrix (as a flat byte[] array) stored row‐major, - * with dimensions (bsMatCols x matCols). - * @param acc the accumulator (as a flat long[] array) with dimensions (bsMatRows x matCols); - * each “entry” is an m‐vector (length mVecLimbs). - * @param bsMatRows number of rows in the bsMat (the “triangular” matrix’s row count). - * @param bsMatCols number of columns in bsMat. - * @param matCols number of columns in the matrix “mat.” - * @param triangular if 1, start column index for each row is (r * triangular); otherwise use 0. + * @param mVecLimbs number of limbs per m-vector. + * @param bsMat the “basis” matrix (as a flat long[] array); each entry occupies mVecLimbs elements. + * @param mat the second matrix (as a flat byte[] array) stored row‐major, + * with dimensions (bsMatCols x matCols). + * @param acc the accumulator (as a flat long[] array) with dimensions (bsMatRows x matCols); + * each “entry” is an m‐vector (length mVecLimbs). + * @param bsMatRows number of rows in the bsMat (the “triangular” matrix’s row count). + * @param bsMatCols number of columns in bsMat. + * @param matCols number of columns in the matrix “mat.” */ - public static void mulAddMUpperTriangularMatXMat(int mVecLimbs, long[] bsMat, byte[] mat, long[] acc, - int bsMatRows, int bsMatCols, int matCols, int triangular) + public static void mulAddMUpperTriangularMatXMat(int mVecLimbs, long[] bsMat, byte[] mat, long[] acc, int accOff, + int bsMatRows, int bsMatCols, int matCols) { int bsMatEntriesUsed = 0; - for (int r = 0; r < bsMatRows; r++) + int matColsmVecLimbs = matCols * mVecLimbs; + for (int r = 0, rmatCols = 0, rmatColsmVecLimbs = 0; r < bsMatRows; r++, rmatCols += matCols, rmatColsmVecLimbs += matColsmVecLimbs) { // For each row r, the inner loop goes from column triangular*r to bsMatCols-1. - for (int c = triangular * r; c < bsMatCols; c++) + for (int c = r, cmatCols = rmatCols; c < bsMatCols; c++, cmatCols += matCols) { - for (int k = 0; k < matCols; k++) + for (int k = 0, kmVecLimbs = 0; k < matCols; k++, kmVecLimbs += mVecLimbs) { - // Calculate the offsets: - // For bsMat: the m-vector starting at index bsMatEntriesUsed * mVecLimbs. - int bsMatOffset = bsMatEntriesUsed * mVecLimbs; - // For mat: element at row c, column k (row-major layout). - int a = mat[c * matCols + k] & 0xFF; // For acc: add into the m-vector at row r, column k. - int accOffset = (r * matCols + k) * mVecLimbs; - mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, a, acc, accOffset); + mVecMulAdd(mVecLimbs, bsMat, bsMatEntriesUsed, mat[cmatCols + k] & 0xFF, acc, accOff + rmatColsmVecLimbs + kmVecLimbs); } - bsMatEntriesUsed++; + bsMatEntriesUsed += mVecLimbs; } } } @@ -114,18 +97,18 @@ public static void mulAddMUpperTriangularMatXMat(int mVecLimbs, long[] bsMat, by * @param matCols number of columns in “mat.” * @param bsMatCols number of columns in the bsMat matrix. */ - public static void mulAddMatTransXMMat(int mVecLimbs, byte[] mat, long[] bsMat, long[] acc, + public static void mulAddMatTransXMMat(int mVecLimbs, byte[] mat, long[] bsMat, int bsMatOff, long[] acc, int matRows, int matCols, int bsMatCols) { // Loop over each column r of mat (which becomes row of mat^T) for (int r = 0; r < matCols; r++) { - for (int c = 0; c < matRows; c++) + for (int c = 0, cmatCols = 0; c < matRows; c++, cmatCols += matCols) { - byte matVal = mat[c * matCols + r]; + byte matVal = mat[cmatCols + r]; for (int k = 0; k < bsMatCols; k++) { - int bsMatOffset = (c * bsMatCols + k) * mVecLimbs; + int bsMatOffset = bsMatOff + (c * bsMatCols + k) * mVecLimbs; // For acc: add into the m-vector at index (r * bsMatCols + k) int accOffset = (r * bsMatCols + k) * mVecLimbs; mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, matVal, acc, accOffset); @@ -257,10 +240,7 @@ public static int mulF(int a, int b) // Perform carryless multiplication: // Multiply b by each bit of a and XOR the results. - int p = ((a & 1) * b) ^ - ((a & 2) * b) ^ - ((a & 4) * b) ^ - ((a & 8) * b); + int p = ((a & 1) * b) ^ ((a & 2) * b) ^ ((a & 4) * b) ^ ((a & 8) * b); // Reduce modulo f(X) = x^4 + x + 1. // Extract the upper nibble (bits 4 to 7). @@ -308,9 +288,8 @@ public static void matMul(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int int colrowAB, int rowA, int colB) { int cIndex = 0; - for (int i = 0; i < rowA; i++) + for (int i = 0, aRowStart = 0; i < rowA; i++, aRowStart += colrowAB) { - int aRowStart = i * colrowAB; for (int j = 0; j < colB; j++) { c[cOff + cIndex++] = lincomb(a, aOff + aRowStart, b, bOff + j, colrowAB, colB); @@ -318,7 +297,6 @@ public static void matMul(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int } } - private static byte lincomb(byte[] a, int aStart, byte[] b, int bStart, int colrowAB, int colB) { @@ -332,26 +310,14 @@ private static byte lincomb(byte[] a, int aStart, byte[] b, int bStart, public static void matAdd(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int cOff, int m, int n) { - for (int i = 0; i < m; i++) + for (int i = 0, in = 0; i < m; i++, in += n) { for (int j = 0; j < n; j++) { - int idx = i * n + j; + int idx = in + j; c[idx + cOff] = (byte)(a[idx + aOff] ^ b[idx + bOff]); } } } - - public static void efUnpackMVector(int legs, long[] packedRow, int packedRowOff, byte[] out) - { - int outIndex = 0; - byte[] bytes = new byte[out.length >> 1]; - Pack.longToLittleEndian(packedRow, packedRowOff, out.length >> 4, bytes, 0); - for (int i = 0; i < legs * 16; i += 2) - { - out[outIndex++] = (byte)(bytes[i / 2] & 0x0F); // Lower nibble - out[outIndex++] = (byte)((bytes[i / 2] >> 4) & 0x0F); // Upper nibble - } - } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java index dac5454c11..42a349d5a8 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java @@ -47,8 +47,6 @@ public AsymmetricCipherKeyPair generateKeyPair() // Allocate P3 as a long array of size (O_MAX * O_MAX * M_VEC_LIMBS_MAX), zero-initialized. long[] P3 = new long[o * o * mVecLimbs]; - // Allocate O as a byte array of size (V_MAX * O_MAX). - // Here we assume V_MAX is given by p.getV() (or replace with a constant if needed). byte[] O = new byte[v * o]; // Generate secret key seed (seed_sk) using a secure random generator. @@ -60,27 +58,21 @@ public AsymmetricCipherKeyPair generateKeyPair() // o ← Decode_o(S[ param_pk_seed_bytes : param_pk_seed_bytes + O_bytes ]) // Decode nibbles from S starting at offset param_pk_seed_bytes into O, // with expected output length = param_v * param_o. - Utils.decode(seed_pk, pkSeedBytes, O, v * o); + Utils.decode(seed_pk, pkSeedBytes, O, O.length); // Expand P1 and P2 into the array P using seed_pk. MayoEngine.expandP1P2(p, P, seed_pk); - // For compute_P3, we need to separate P1 and P2. - // Here, we treat P1 as the first param_P1_limbs elements of P, - // and P2 as the remaining elements. - long[] P2 = new long[P.length - p1Limbs]; - System.arraycopy(P, p1Limbs, P2, 0, P2.length); - // Compute P1 * O + P2 and store the result in P2. -// GF16Utils.P1TimesO(p, P, O, P2); + // GF16Utils.P1TimesO(p, P, O, P2); // Here, bsMatRows and bsMatCols are both paramV, and matCols is paramO, triangular=1. - GF16Utils.mulAddMUpperTriangularMatXMat(mVecLimbs, P, O, P2, v, v, o, 1); + GF16Utils.mulAddMUpperTriangularMatXMat(mVecLimbs, P, O, P, p1Limbs, v, v, o); // Compute P3 = O^T * (P1*O + P2). // Here, treat P2 as the bsMat for the multiplication. // Dimensions: mat = O (size: paramV x paramO), bsMat = P2 (size: paramV x paramO), // and acc (P3) will have dimensions: (paramO x paramO), each entry being an m-vector. - GF16Utils.mulAddMatTransXMMat(mVecLimbs, O, P2, P3, v, o, o); + GF16Utils.mulAddMatTransXMMat(mVecLimbs, O, P, p1Limbs, P3, v, o, o); // Store seed_pk into the public key cpk. System.arraycopy(seed_pk, 0, cpk, 0, pkSeedBytes); @@ -90,25 +82,20 @@ public AsymmetricCipherKeyPair generateKeyPair() // Compute Upper(P3) and store the result in P3_upper. int mVecsStored = 0; - for (int r = 0; r < o; r++) + int omVecLimbs = o * mVecLimbs; + for (int r = 0, rmVecLimbs = 0, romVecLimbs = 0; r < o; r++, romVecLimbs += omVecLimbs, rmVecLimbs += mVecLimbs) { - for (int c = r; c < o; c++) + for (int c = r, cmVecLimbs = rmVecLimbs, comVecLimbs = romVecLimbs; c < o; c++, cmVecLimbs += mVecLimbs, comVecLimbs += omVecLimbs) { - // Compute the starting index for the (r, c) vector in the input array. - int srcOffset = mVecLimbs * (r * o + c); - // Compute the output offset for the current stored vector. - int destOffset = mVecLimbs * mVecsStored; - // Copy the vector at (r, c) into the output. - System.arraycopy(P3, srcOffset, P3_upper, destOffset, mVecLimbs); + System.arraycopy(P3, romVecLimbs + cmVecLimbs, P3_upper, mVecsStored, mVecLimbs); // If off-diagonal, add (XOR) the vector at (c, r) into the same output vector. if (r != c) { - int srcOffset2 = mVecLimbs * (c * o + r); - Longs.xorTo(mVecLimbs, P3, srcOffset2, P3_upper, destOffset); + Longs.xorTo(mVecLimbs, P3, comVecLimbs + rmVecLimbs, P3_upper, mVecsStored); } - mVecsStored++; + mVecsStored += mVecLimbs; } } @@ -118,7 +105,6 @@ public AsymmetricCipherKeyPair generateKeyPair() Utils.packMVecs(P3_upper, cpk, pkSeedBytes, p3Limbs / mVecLimbs, m); // Securely clear sensitive data. Arrays.clear(O); - Arrays.clear(P2); Arrays.clear(P3); return new AsymmetricCipherKeyPair(new MayoPublicKeyParameter(p, cpk), new MayoPrivateKeyParameter(p, seed_sk)); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index a68d01d15c..f6d2b77c3d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -132,7 +132,7 @@ public byte[] generateSignature(byte[] message) GF16Utils.mulAddMatXMMat(mVecLimbs, Vdec, Pv, vPv, k, v, k); computeRHS(vPv, t, y); - computeA(params, Mtmp, A); + computeA(Mtmp, A); // Clear trailing bytes for (int i = 0; i < params.getM(); ++i) @@ -143,7 +143,7 @@ public byte[] generateSignature(byte[] message) Utils.decode(V, k * params.getVBytes(), r, 0, k * o); - if (sampleSolution(params, A, y, r, x) != 0) + if (sampleSolution(params, A, y, r, x)) { break; } @@ -216,13 +216,6 @@ public boolean verifySignature(byte[] message, byte[] signature) MayoEngine.expandP1P2(params, pk, cpk); Utils.unpackMVecs(cpk, params.getPkSeedBytes(), pk, p1Limbs + params.getP2Limbs(), params.getP3Limbs() / params.getMVecLimbs(), params.getM()); - // Split pk into P1, P2, P3 - long[] P1 = new long[p1Limbs]; - long[] P2 = new long[p2Limbs]; - long[] P3 = new long[p3Limbs]; - System.arraycopy(pk, 0, P1, 0, p1Limbs); - System.arraycopy(pk, p1Limbs, P2, 0, p2Limbs); - System.arraycopy(pk, p1Limbs + p2Limbs, P3, 0, p3Limbs); // Hash message Utils.shake256(tmp, paramDigestBytes, message, message.length); @@ -240,7 +233,7 @@ public boolean verifySignature(byte[] message, byte[] signature) int mVecLimbs = (params.getM() + 15) / 16; long[] SPS = new long[k * k * mVecLimbs]; long[] PS = new long[n * k * mVecLimbs]; - mayoGenericMCalculatePS(params, P1, P2, P3, s, m, params.getV(), params.getO(), k, PS); + mayoGenericMCalculatePS(params, pk, p1Limbs, p1Limbs + p2Limbs, s, m, params.getV(), params.getO(), k, PS); mayoGenericMCalculateSPS(PS, s, m, k, n, SPS); byte[] zero = new byte[m]; computeRHS(SPS, zero, y); @@ -343,7 +336,7 @@ public void computeRHS(long[] vPv, byte[] t, byte[] y) private static final long EVEN_2BYTES = 0x0000FFFF0000FFFFL; private static final long LOW_BIT_IN_NIBBLE = 0x1111111111111111L; - public static void computeA(MayoParameters params, long[] Mtmp, byte[] AOut) + public void computeA(long[] Mtmp, byte[] AOut) { final int k = params.getK(); final int o = params.getO(); @@ -431,7 +424,7 @@ public static void computeA(MayoParameters params, long[] Mtmp, byte[] AOut) } // Generate tab array - byte[] tab = new byte[F_TAIL_LEN * 4]; + byte[] tab = new byte[F_TAIL_LEN << 2]; for (int i = 0; i < F_TAIL_LEN; i++) { byte ft = fTailArr[i]; @@ -521,8 +514,8 @@ private static void transpose16x16Nibbles(long[] M, int offset) } } - public int sampleSolution(MayoParameters params, byte[] A, byte[] y, - byte[] r, byte[] x) + public boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, + byte[] r, byte[] x) { final int k = params.getK(); final int o = params.getO(); @@ -559,7 +552,7 @@ public int sampleSolution(MayoParameters params, byte[] A, byte[] y, } if (!fullRank) { - return 0; + return false; } // Constant-time back substitution @@ -600,7 +593,7 @@ public int sampleSolution(MayoParameters params, byte[] A, byte[] y, finished |= correctCol; } } - return 1; + return true; } /** @@ -621,9 +614,12 @@ public void ef(byte[] A, int nrows, int ncols) long[] pivotRow2 = new long[rowLen]; // The packed matrix: one contiguous array storing nrows rows, each rowLen longs long. long[] packedA = new long[nrows * rowLen]; + int len = params.getO() * params.getK() + 16; + byte[] bytes = new byte[len >> 1]; + int len_4 = len >> 4; // Pack the matrix rows. - for (int i = 0; i < nrows; i++) + for (int i = 0, incols = 0; i < nrows; i++, incols += ncols) { //packRow(A, i, ncols); // Process each 64-bit word (each holds 16 nibbles). @@ -635,7 +631,7 @@ public void ef(byte[] A, int nrows, int ncols) int col = (word << 4) + nibble; if (col < ncols) { - wordVal |= ((long)A[i * ncols + col] & 0xF) << (nibble << 2); + wordVal |= ((long)A[incols + col] & 0xF) << (nibble << 2); } } packedA[word + i * rowLen] = wordVal; @@ -681,14 +677,14 @@ public void ef(byte[] A, int nrows, int ncols) vecMulAddU64(rowLen, pivotRow, (byte)inv, pivotRow2); // Conditionally write the pivot row back into the correct row (if pivot is nonzero). - for (int row = lowerBound; row <= upperBound; row++) + for (int row = lowerBound, rowRowLen = lowerBound * rowLen; row <= upperBound; row++, rowRowLen += rowLen) { long doCopy = ~ctCompare64(row, pivotRowIndex) & ~pivotIsZero; long doNotCopy = ~doCopy; - for (int col = 0; col < rowLen; col++) + for (int col = 0, rowRowLen_col = rowRowLen; col < rowLen; col++, rowRowLen_col++) { // Since the masks are disjoint, addition is equivalent to OR. - packedA[row * rowLen + col] = (doNotCopy & packedA[row * rowLen + col]) | + packedA[rowRowLen_col] = (doNotCopy & packedA[rowRowLen_col]) | (doCopy & pivotRow2[col]); } } @@ -708,16 +704,19 @@ public void ef(byte[] A, int nrows, int ncols) } } - byte[] temp = new byte[params.getO() * params.getK() + 1 + 15]; + int outIndex = 0; // At this point, packedA holds the row-echelon form of the original matrix. // (Depending on your application you might want to unpack it back to A.) - for (int i = 0; i < nrows; i++) + for (int i = 0, irowLen = 0; i < nrows; i++, irowLen += rowLen) { - GF16Utils.efUnpackMVector(rowLen, packedA, i * rowLen, temp); - if (ncols >= 0) + Pack.longToLittleEndian(packedA, irowLen, len_4, bytes, 0); + int j = 0; + for (; j < ncols >> 1; j++) { - System.arraycopy(temp, 0, A, i * ncols, ncols); + A[outIndex++] = (byte)(bytes[j] & 0x0F); // Lower nibble + A[outIndex++] = (byte)((bytes[j] >> 4) & 0x0F); // Upper nibble } + A[outIndex++] = (byte)(bytes[j] & 0x0F); } } @@ -838,7 +837,7 @@ private static int mulTable(int b) return x ^ (highHalf >>> 4) ^ (highHalf >>> 3); } - private static void mayoGenericMCalculatePS(MayoParameters p, long[] P1, long[] P2, long[] P3, byte[] S, + private static void mayoGenericMCalculatePS(MayoParameters p, long[] P1, int p2, int p3, byte[] S, int m, int v, int o, int k, long[] PS) { int n = o + v; @@ -861,7 +860,7 @@ private static void mayoGenericMCalculatePS(MayoParameters p, long[] P1, long[] { for (int col = 0, ncol = 0; col < k; col++, ncol += n) { - Longs.xorTo(mVecLimbs, P2, orow_j_mVecLimbs, accumulator, (((krow + col) << 4) + (S[ncol + j + v] & 0xFF)) * mVecLimbs); + Longs.xorTo(mVecLimbs, P1, p2 + orow_j_mVecLimbs, accumulator, (((krow + col) << 4) + (S[ncol + j + v] & 0xFF)) * mVecLimbs); } } } @@ -873,16 +872,13 @@ private static void mayoGenericMCalculatePS(MayoParameters p, long[] P1, long[] { for (int col = 0, ncol = 0; col < k; col++, ncol += n) { - Longs.xorTo(mVecLimbs, P3, pUsed, accumulator, (((krow + col) << 4) + (S[ncol + j] & 0xFF)) * mVecLimbs); + Longs.xorTo(mVecLimbs, P1, p3 + pUsed, accumulator, (((krow + col) << 4) + (S[ncol + j] & 0xFF)) * mVecLimbs); } pUsed += mVecLimbs; } } - for (int i = 0, imVecLimbs = 0; i < n * k; i++, imVecLimbs += mVecLimbs) - { - mVecMultiplyBins(mVecLimbs, accumulator, imVecLimbs << 4, PS, imVecLimbs); - } + mVecMultiplyBins(mVecLimbs, n * k, accumulator, PS); } private static void mayoGenericMCalculateSPS(long[] PS, byte[] S, int m, int k, int n, long[] SPS) @@ -892,11 +888,11 @@ private static void mayoGenericMCalculateSPS(long[] PS, byte[] S, int m, int k, final long[] accumulator = new long[accumulatorSize]; // Accumulation phase - for (int row = 0; row < k; row++) + for (int row = 0, nrow = 0; row < k; row++, nrow += n) { for (int j = 0; j < n; j++) { - final int sVal = S[row * n + j] & 0xFF; // Unsigned byte value + final int sVal = S[nrow + j] & 0xFF; // Unsigned byte value for (int col = 0; col < k; col++) { final int psOffset = (j * k + col) * mVecLimbs; @@ -907,30 +903,30 @@ private static void mayoGenericMCalculateSPS(long[] PS, byte[] S, int m, int k, } // Processing phase - for (int i = 0; i < k * k; i++) - { - mVecMultiplyBins(mVecLimbs, accumulator, i * 16 * mVecLimbs, SPS, i * mVecLimbs); - } + mVecMultiplyBins(mVecLimbs, k * k, accumulator, SPS); } - private static void mVecMultiplyBins(int mVecLimbs, long[] bins, int binOffset, long[] ps, int psOff) + private static void mVecMultiplyBins(int mVecLimbs, int len, long[] bins, long[] ps) { - // Series of modular operations as per original C code - mVecMulAddXInv(mVecLimbs, bins, binOffset + 5 * mVecLimbs, bins, binOffset + 10 * mVecLimbs); - mVecMulAddX(mVecLimbs, bins, binOffset + 11 * mVecLimbs, bins, binOffset + 12 * mVecLimbs); - mVecMulAddXInv(mVecLimbs, bins, binOffset + 10 * mVecLimbs, bins, binOffset + 7 * mVecLimbs); - mVecMulAddX(mVecLimbs, bins, binOffset + 12 * mVecLimbs, bins, binOffset + 6 * mVecLimbs); - mVecMulAddXInv(mVecLimbs, bins, binOffset + 7 * mVecLimbs, bins, binOffset + 14 * mVecLimbs); - mVecMulAddX(mVecLimbs, bins, binOffset + 6 * mVecLimbs, bins, binOffset + 3 * mVecLimbs); - mVecMulAddXInv(mVecLimbs, bins, binOffset + 14 * mVecLimbs, bins, binOffset + 15 * mVecLimbs); - mVecMulAddX(mVecLimbs, bins, binOffset + 3 * mVecLimbs, bins, binOffset + 8 * mVecLimbs); - mVecMulAddXInv(mVecLimbs, bins, binOffset + 15 * mVecLimbs, bins, binOffset + 13 * mVecLimbs); - mVecMulAddX(mVecLimbs, bins, binOffset + 8 * mVecLimbs, bins, binOffset + 4 * mVecLimbs); - mVecMulAddXInv(mVecLimbs, bins, binOffset + 13 * mVecLimbs, bins, binOffset + 9 * mVecLimbs); - mVecMulAddX(mVecLimbs, bins, binOffset + 4 * mVecLimbs, bins, binOffset + 2 * mVecLimbs); - mVecMulAddXInv(mVecLimbs, bins, binOffset + 9 * mVecLimbs, bins, binOffset + mVecLimbs); - mVecMulAddX(mVecLimbs, bins, binOffset + 2 * mVecLimbs, bins, binOffset + mVecLimbs); - System.arraycopy(bins, mVecLimbs + binOffset, ps, psOff, mVecLimbs); + for (int i = 0, imVecLimbs4 = 0; i < len; i++, imVecLimbs4 += (mVecLimbs << 4)) + { + // Series of modular operations as per original C code + mVecMulAddXInv(mVecLimbs, bins, imVecLimbs4 + 5 * mVecLimbs, bins, imVecLimbs4 + 10 * mVecLimbs); + mVecMulAddX(mVecLimbs, bins, imVecLimbs4 + 11 * mVecLimbs, bins, imVecLimbs4 + 12 * mVecLimbs); + mVecMulAddXInv(mVecLimbs, bins, imVecLimbs4 + 10 * mVecLimbs, bins, imVecLimbs4 + 7 * mVecLimbs); + mVecMulAddX(mVecLimbs, bins, imVecLimbs4 + 12 * mVecLimbs, bins, imVecLimbs4 + 6 * mVecLimbs); + mVecMulAddXInv(mVecLimbs, bins, imVecLimbs4 + 7 * mVecLimbs, bins, imVecLimbs4 + 14 * mVecLimbs); + mVecMulAddX(mVecLimbs, bins, imVecLimbs4 + 6 * mVecLimbs, bins, imVecLimbs4 + 3 * mVecLimbs); + mVecMulAddXInv(mVecLimbs, bins, imVecLimbs4 + 14 * mVecLimbs, bins, imVecLimbs4 + 15 * mVecLimbs); + mVecMulAddX(mVecLimbs, bins, imVecLimbs4 + 3 * mVecLimbs, bins, imVecLimbs4 + 8 * mVecLimbs); + mVecMulAddXInv(mVecLimbs, bins, imVecLimbs4 + 15 * mVecLimbs, bins, imVecLimbs4 + 13 * mVecLimbs); + mVecMulAddX(mVecLimbs, bins, imVecLimbs4 + 8 * mVecLimbs, bins, imVecLimbs4 + 4 * mVecLimbs); + mVecMulAddXInv(mVecLimbs, bins, imVecLimbs4 + 13 * mVecLimbs, bins, imVecLimbs4 + 9 * mVecLimbs); + mVecMulAddX(mVecLimbs, bins, imVecLimbs4 + 4 * mVecLimbs, bins, imVecLimbs4 + 2 * mVecLimbs); + mVecMulAddXInv(mVecLimbs, bins, imVecLimbs4 + 9 * mVecLimbs, bins, imVecLimbs4 + mVecLimbs); + mVecMulAddX(mVecLimbs, bins, imVecLimbs4 + 2 * mVecLimbs, bins, imVecLimbs4 + mVecLimbs); + System.arraycopy(bins, mVecLimbs + imVecLimbs4, ps, imVecLimbs4 >> 4, mVecLimbs); + } } // Modular arithmetic operations From 0a7476d4ab93df24fcbde791c8623e394a50335b Mon Sep 17 00:00:00 2001 From: Hawk Itzme Date: Mon, 3 Mar 2025 17:35:15 +0530 Subject: [PATCH 142/890] Suppress Lint warning for TrustAllX509TrustManager in JcaJceUtils --- pkix/src/main/java/org/bouncycastle/est/jcajce/JcaJceUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/pkix/src/main/java/org/bouncycastle/est/jcajce/JcaJceUtils.java b/pkix/src/main/java/org/bouncycastle/est/jcajce/JcaJceUtils.java index d581c55aea..3f6024e21b 100644 --- a/pkix/src/main/java/org/bouncycastle/est/jcajce/JcaJceUtils.java +++ b/pkix/src/main/java/org/bouncycastle/est/jcajce/JcaJceUtils.java @@ -35,6 +35,7 @@ public class JcaJceUtils { + @SuppressWarnings("TrustAllX509TrustManager") public static X509TrustManager getTrustAllTrustManager() { From bec5d19acd2b806065944a7c57997385f78285a6 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 4 Mar 2025 07:30:58 +1030 Subject: [PATCH 143/890] Refactor for Mayo --- .../pqc/crypto/mayo/GF16Utils.java | 64 ++-- .../pqc/crypto/mayo/MayoEngine.java | 183 --------- .../pqc/crypto/mayo/MayoKeyPairGenerator.java | 7 +- .../pqc/crypto/mayo/MayoSigner.java | 355 +++++++++--------- .../bouncycastle/pqc/crypto/mayo/Utils.java | 90 +++-- 5 files changed, 287 insertions(+), 412 deletions(-) delete mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoEngine.java diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java index 7882cc472a..6595d412eb 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java @@ -2,6 +2,10 @@ public class GF16Utils { + static final long NIBBLE_MASK_MSB = 0x7777777777777777L; + static final long MASK_MSB = 0x8888888888888888L; + static final long MASK_LSB = 0x1111111111111111L; + static final long NIBBLE_MASK_LSB = ~MASK_LSB; /** * Multiplies each limb of a GF(16) vector (subarray of 'in') by the GF(16) element 'a' @@ -18,8 +22,7 @@ public class GF16Utils */ public static void mVecMulAdd(int mVecLimbs, long[] in, int inOffset, int b, long[] acc, int accOffset) { - long maskMsb = 0x8888888888888888L; - long a, r64, a_msb; + long a, r64, a_msb, a_msb3; long b32 = b & 0x00000000FFFFFFFFL; long b32and1 = b32 & 1; long b32_1_1 = ((b32 >>> 1) & 1); @@ -29,23 +32,26 @@ public static void mVecMulAdd(int mVecLimbs, long[] in, int inOffset, int b, lon { // In the original code there is a conditional XOR with unsigned_char_blocker; // here we simply use b directly. - a = in[inOffset + i]; - r64 = a * b32and1; + a = in[inOffset++]; + r64 = a & -b32and1; - a_msb = a & maskMsb; - a ^= a_msb; - a = (a << 1) ^ ((a_msb >>> 3) * 3); - r64 ^= a * b32_1_1; + a_msb = a & MASK_MSB; + a &= NIBBLE_MASK_MSB; + a_msb3 = a_msb >>> 3; + a = (a << 1) ^ (a_msb3 + (a_msb3 << 1)); + r64 ^= a & -b32_1_1; - a_msb = a & maskMsb; - a ^= a_msb; - a = (a << 1) ^ ((a_msb >>> 3) * 3); - r64 ^= a * b32_2_1; + a_msb = a & MASK_MSB; + a &= NIBBLE_MASK_MSB; + a_msb3 = a_msb >>> 3; + a = (a << 1) ^ (a_msb3 + (a_msb3 << 1)); + r64 ^= a & -b32_2_1; - a_msb = a & maskMsb; - a ^= a_msb; - a = (a << 1) ^ ((a_msb >>> 3) * 3); - acc[accOffset + i] ^= r64 ^ (a * b32_3_1); + a_msb = a & MASK_MSB; + a &= NIBBLE_MASK_MSB; + a_msb3 = a_msb >>> 3; + a = (a << 1) ^ (a_msb3 + (a_msb3 << 1)); + acc[accOffset++] ^= r64 ^ (a & -b32_3_1); } } @@ -190,24 +196,22 @@ public static void mulAddMatXMMat(int mVecLimbs, byte[] mat, long[] bsMat, int b * by the scalar (from {@code mat}) and adds the result to the corresponding vector in {@code acc}. *

* - * @param mVecLimbs the number of limbs (elements) in each vector. - * @param bsMat the bit‑sliced matrix stored as a long array. - * @param mat the matrix stored as a byte array. - * @param acc the accumulator array where the results are added. - * @param bsMatRows the number of rows in the bit‑sliced matrix. - * @param bsMatCols the number of columns in the bit‑sliced matrix. - * @param matRows the number of rows in the matrix. - * @param triangular if non‑zero, indicates that the matrix is upper triangular (i.e. the loop for {@code c} - * starts at {@code triangular * r}). + * @param mVecLimbs the number of limbs (elements) in each vector. + * @param bsMat the bit‑sliced matrix stored as a long array. + * @param mat the matrix stored as a byte array. + * @param acc the accumulator array where the results are added. + * @param bsMatRows the number of rows in the bit‑sliced matrix. + * @param bsMatCols the number of columns in the bit‑sliced matrix. + * @param matRows the number of rows in the matrix. */ public static void mulAddMUpperTriangularMatXMatTrans(int mVecLimbs, long[] bsMat, byte[] mat, long[] acc, - int bsMatRows, int bsMatCols, int matRows, int triangular) + int bsMatRows, int bsMatCols, int matRows) { int bsMatEntriesUsed = 0; for (int r = 0; r < bsMatRows; r++) { // For upper triangular, start c at triangular * r; otherwise, triangular is zero. - for (int c = triangular * r; c < bsMatCols; c++) + for (int c = r; c < bsMatCols; c++) { for (int k = 0; k < matRows; k++) { @@ -270,8 +274,7 @@ public static long mulFx8(byte a, long b) return (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0f0f0f0f0f0f0f0fL; } - public static void matMul(byte[] a, byte[] b, byte[] c, - int colrowAB, int rowA, int colB) + public static void matMul(byte[] a, byte[] b, byte[] c, int colrowAB, int rowA, int colB) { int cIndex = 0; for (int i = 0; i < rowA; i++) @@ -287,12 +290,11 @@ public static void matMul(byte[] a, byte[] b, byte[] c, public static void matMul(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int cOff, int colrowAB, int rowA, int colB) { - int cIndex = 0; for (int i = 0, aRowStart = 0; i < rowA; i++, aRowStart += colrowAB) { for (int j = 0; j < colB; j++) { - c[cOff + cIndex++] = lincomb(a, aOff + aRowStart, b, bOff + j, colrowAB, colB); + c[cOff++] = lincomb(a, aOff + aRowStart, b, bOff + j, colrowAB, colB); } } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoEngine.java deleted file mode 100644 index f93e2848da..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoEngine.java +++ /dev/null @@ -1,183 +0,0 @@ -package org.bouncycastle.pqc.crypto.mayo; - -import org.bouncycastle.crypto.BlockCipher; -import org.bouncycastle.crypto.engines.AESEngine; -import org.bouncycastle.crypto.modes.CTRModeCipher; -import org.bouncycastle.crypto.modes.SICBlockCipher; -import org.bouncycastle.crypto.params.KeyParameter; -import org.bouncycastle.crypto.params.ParametersWithIV; -import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Pack; - -public class MayoEngine -{ - /** - * Expands P1 and P2 using AES_128_CTR as a PRF and then unpacks the resulting bytes - * into an array of 64-bit limbs. - * - * @param p Mayo parameters - * @param P The output long array which will hold the unpacked limbs. - * Its length should be at least ((P1_bytes + P2_bytes) / 8) limbs. - * @param seed_pk The seed (used as the key) for the PRF. - * @return The number of bytes produced, i.e., P1_bytes + P2_bytes. - */ - public static int expandP1P2(MayoParameters p, long[] P, byte[] seed_pk) - { - // Compute total number of bytes to generate: P1_bytes + P2_bytes. - int outLen = p.getP1Bytes() + p.getP2Bytes(); - // Temporary byte array to hold the PRF output. - byte[] temp = new byte[outLen]; - - // Call AES_128_CTR (our previously defined function using BouncyCastle) - // to fill temp with outLen pseudorandom bytes using seed_pk as key. - AES_128_CTR(temp, outLen, seed_pk, p.getPkSeedBytes()); - - // The number of vectors is the total limbs divided by mVecLimbs. - int numVectors = (p.getP1Limbs() + p.getP2Limbs()) / p.getMVecLimbs(); - - // Unpack the byte array 'temp' into the long array 'P' - // using our previously defined unpackMVecs method. - Utils.unpackMVecs(temp, P, numVectors, p.getM()); - - // Return the number of output bytes produced. - return outLen; - } - - /** - * AES_128_CTR generates outputByteLen bytes using AES-128 in CTR mode. - * The key (of length keyLen) is used to expand the AES key. - * A 16-byte IV (all zeros) is used. - * - * @param output the output buffer which will be filled with the keystream - * @param outputByteLen the number of bytes to produce - * @param key the AES key (should be 16 bytes for AES-128) - * @param keyLen the length of the key (unused here but kept for similarity) - * @return the number of output bytes produced (i.e. outputByteLen) - */ - public static int AES_128_CTR(byte[] output, int outputByteLen, byte[] key, int keyLen) - { - // Create a 16-byte IV (all zeros) - byte[] iv = new byte[16]; // automatically zero-initialized - - // Set up AES engine in CTR (SIC) mode. - BlockCipher aesEngine = AESEngine.newInstance(); - // SICBlockCipher implements CTR mode for AES. - CTRModeCipher ctrCipher = SICBlockCipher.newInstance(aesEngine); - // Wrap the key with the IV. - ParametersWithIV params = new ParametersWithIV(new KeyParameter(Arrays.copyOf(key, keyLen)), iv); - ctrCipher.init(true, params); - - // CTR mode is a stream cipher: encrypting zero bytes produces the keystream. - int blockSize = ctrCipher.getBlockSize(); // typically 16 bytes - byte[] zeroBlock = new byte[blockSize]; // block of zeros - byte[] blockOut = new byte[blockSize]; - - int offset = 0; - // Process full blocks - while (offset + blockSize <= outputByteLen) - { - ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); - System.arraycopy(blockOut, 0, output, offset, blockSize); - offset += blockSize; - } - // Process any remaining partial block. - if (offset < outputByteLen) - { - ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); - int remaining = outputByteLen - offset; - System.arraycopy(blockOut, 0, output, offset, remaining); - } - return outputByteLen; - } - - public static final int MAYO_OK = 0; - - /** - * Expands the secret key. - * - * @param p the MayoParameters instance. - * @param csk the input secret key seed (byte array). - * @return MAYO_OK on success. - */ - public static int mayoExpandSk(MayoParameters p, byte[] csk, long[] P, byte[] O) - { - int ret = MAYO_OK; - int totalS = p.getPkSeedBytes() + p.getOBytes(); - byte[] S = new byte[totalS]; - - // sk.p is the long[] array, sk.O is the byte[] array. - - int param_o = p.getO(); - int param_v = p.getV(); - int param_O_bytes = p.getOBytes(); - int param_pk_seed_bytes = p.getPkSeedBytes(); - int param_sk_seed_bytes = p.getSkSeedBytes(); - - // In C, seed_sk = csk and seed_pk = S (the beginning of S) - byte[] seed_sk = csk; - byte[] seed_pk = S; // first param_pk_seed_bytes of S - - // Generate S = seed_pk || (additional bytes), using SHAKE256. - // Output length is param_pk_seed_bytes + param_O_bytes. - Utils.shake256(S, param_pk_seed_bytes + param_O_bytes, seed_sk, param_sk_seed_bytes); - - // Decode the portion of S after the first param_pk_seed_bytes into O. - // (In C, this is: decode(S + param_pk_seed_bytes, O, param_v * param_o)) - Utils.decode(S, param_pk_seed_bytes, O, param_v * param_o); - - // Expand P1 and P2 into the long array P using seed_pk. - MayoEngine.expandP1P2(p, P, seed_pk); - - // Let P2 start at offset = PARAM_P1_limbs(p) - int p1Limbs = p.getP1Limbs(); - int offsetP2 = p1Limbs; - - // Compute L_i = (P1 + P1^t)*O + P2. - // Here, we assume that P1P1tTimesO writes into the portion of P starting at offsetP2. - P1P1tTimesO(p, P, O, P, offsetP2); - - // Securely clear sensitive temporary data. - java.util.Arrays.fill(S, (byte)0); - return ret; - } - - /** - * Multiplies and accumulates the product (P1 + P1^t)*O into the accumulator. - * This version writes into the 'acc' array starting at the specified offset. - * - * @param p the MayoParameters. - * @param P1 the P1 vector as a long[] array. - * @param O the O array (each byte represents a GF(16) element). - * @param acc the accumulator array where results are XORed in. - * @param accOffset the starting index in acc. - */ - public static void P1P1tTimesO(MayoParameters p, long[] P1, byte[] O, long[] acc, int accOffset) - { - int paramO = p.getO(); - int paramV = p.getV(); - int mVecLimbs = p.getMVecLimbs(); - int bsMatEntriesUsed = 0; - for (int r = 0; r < paramV; r++) - { - for (int c = r; c < paramV; c++) - { - if (c == r) - { - bsMatEntriesUsed++; - continue; - } - for (int k = 0; k < paramO; k++) - { - // Multiply the m-vector at P1 for the current matrix entry, - // and accumulate into acc for row r. - GF16Utils.mVecMulAdd(mVecLimbs, P1, bsMatEntriesUsed * mVecLimbs, - O[c * paramO + k] & 0xFF, acc, accOffset + (r * paramO + k) * mVecLimbs); - // Similarly, accumulate into acc for row c. - GF16Utils.mVecMulAdd(mVecLimbs, P1, bsMatEntriesUsed * mVecLimbs, - O[r * paramO + k] & 0xFF, acc, accOffset + (c * paramO + k) * mVecLimbs); - } - bsMatEntriesUsed++; - } - } - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java index 42a349d5a8..1a5f9ff037 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java @@ -5,6 +5,7 @@ import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; import org.bouncycastle.crypto.KeyGenerationParameters; +import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Longs; @@ -53,7 +54,9 @@ public AsymmetricCipherKeyPair generateKeyPair() random.nextBytes(seed_sk); // S ← shake256(seed_sk, pk_seed_bytes + O_bytes) - Utils.shake256(seed_pk, pkSeedBytes + oBytes, seed_sk, skSeedBytes); + SHAKEDigest shake = new SHAKEDigest(256); + shake.update(seed_sk, 0, skSeedBytes); + shake.doFinal(seed_pk, 0, pkSeedBytes + oBytes); // o ← Decode_o(S[ param_pk_seed_bytes : param_pk_seed_bytes + O_bytes ]) // Decode nibbles from S starting at offset param_pk_seed_bytes into O, @@ -61,7 +64,7 @@ public AsymmetricCipherKeyPair generateKeyPair() Utils.decode(seed_pk, pkSeedBytes, O, O.length); // Expand P1 and P2 into the array P using seed_pk. - MayoEngine.expandP1P2(p, P, seed_pk); + Utils.expandP1P2(p, P, seed_pk); // Compute P1 * O + P2 and store the result in P2. // GF16Utils.P1TimesO(p, P, O, P2); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index f6d2b77c3d..df6922625d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -55,51 +55,98 @@ public byte[] generateSignature(byte[] message) int k = params.getK(); int v = params.getV(); int o = params.getO(); + int n = params.getN(); + int vbytes = params.getVBytes(); + int oBytes = params.getOBytes(); int saltBytes = params.getSaltBytes(); int mVecLimbs = params.getMVecLimbs(); + int p1Limbs = params.getP1Limbs(); + int pk_seed_bytes = params.getPkSeedBytes(); + int digestBytes = params.getDigestBytes(); + int skSeedBytes = params.getSkSeedBytes(); byte[] tenc = new byte[params.getMBytes()]; byte[] t = new byte[params.getM()]; byte[] y = new byte[params.getM()]; byte[] salt = new byte[saltBytes]; - byte[] V = new byte[k * params.getVBytes() + params.getRBytes()]; + byte[] V = new byte[k * vbytes + params.getRBytes()]; byte[] Vdec = new byte[v * k]; byte[] A = new byte[((params.getM() + 7) / 8 * 8) * (k * o + 1)]; - byte[] x = new byte[k * params.getN()]; + byte[] x = new byte[k * n]; byte[] r = new byte[k * o + 1]; - byte[] s = new byte[k * params.getN()]; - byte[] tmp = new byte[params.getDigestBytes() + saltBytes + params.getSkSeedBytes() + 1]; + byte[] s = new byte[k * n]; + byte[] tmp = new byte[digestBytes + saltBytes + skSeedBytes + 1]; byte[] sig = new byte[params.getSigBytes()]; - long[] P = new long[params.getP1Limbs() + params.getP2Limbs()]; + long[] P = new long[p1Limbs + params.getP2Limbs()]; byte[] O = new byte[v * o]; - long[] Mtmp = new long[k * o * params.getMVecLimbs()]; - long[] vPv = new long[k * k * params.getMVecLimbs()]; - + long[] Mtmp = new long[k * o * mVecLimbs]; + long[] vPv = new long[k * k * mVecLimbs]; + SHAKEDigest shake = new SHAKEDigest(256); try { + byte[] seed_sk = privKey.getSeedSk(); // Expand secret key - MayoEngine.mayoExpandSk(params, privKey.getSeedSk(), P, O); + //MayoEngine.mayoExpandSk(params, seed_sk, P, O); + int totalS = pk_seed_bytes + oBytes; + byte[] seed_pk = new byte[totalS]; + + // Generate S = seed_pk || (additional bytes), using SHAKE256. + // Output length is param_pk_seed_bytes + param_O_bytes. + shake.update(seed_sk, 0, seed_sk.length); + shake.doFinal(seed_pk, 0, pk_seed_bytes + oBytes); + + // Decode the portion of S after the first param_pk_seed_bytes into O. + // (In C, this is: decode(S + param_pk_seed_bytes, O, param_v * param_o)) + Utils.decode(seed_pk, pk_seed_bytes, O, v * o); + + // Expand P1 and P2 into the long array P using seed_pk. + Utils.expandP1P2(params, P, seed_pk); + + // Compute L_i = (P1 + P1^t)*O + P2. + // Here, we assume that P1P1tTimesO writes into the portion of P starting at offsetP2. + //MayoEngine.P1P1tTimesO(params, P, O, P, p1Limbs); + int bsMatEntriesUsed = 0; + int omVecLimbs = o * mVecLimbs; + for (int i = 0, io = 0, iomVecLimbs = 0; i < v; i++, io += o, iomVecLimbs += omVecLimbs) + { + for (int c = i, co = io, comVecLimbs = iomVecLimbs; c < v; c++, co += o, comVecLimbs += omVecLimbs) + { + if (c == i) + { + bsMatEntriesUsed += mVecLimbs; + continue; + } + for (int j = 0, jmVecLimbs = p1Limbs; j < o; j++, jmVecLimbs += mVecLimbs) + { + // Multiply the m-vector at P1 for the current matrix entry, + // and accumulate into acc for row r. + GF16Utils.mVecMulAdd(mVecLimbs, P, bsMatEntriesUsed, O[co + j] & 0xFF, P, iomVecLimbs + jmVecLimbs); + // Similarly, accumulate into acc for row c. + GF16Utils.mVecMulAdd(mVecLimbs, P, bsMatEntriesUsed, O[io + j] & 0xFF, P, comVecLimbs + jmVecLimbs); + } + bsMatEntriesUsed += mVecLimbs; + } + } + // Securely clear sensitive temporary data. + Arrays.fill(seed_pk, (byte)0); // Hash message - SHAKEDigest shake = new SHAKEDigest(256); shake.update(message, 0, message.length); - shake.doFinal(tmp, 0, params.getDigestBytes()); + shake.doFinal(tmp, 0, digestBytes); // Generate random salt random.nextBytes(salt); - System.arraycopy(salt, 0, tmp, params.getDigestBytes(), salt.length); + System.arraycopy(salt, 0, tmp, digestBytes, salt.length); // Hash to salt - System.arraycopy(privKey.getSeedSk(), 0, tmp, params.getDigestBytes() + saltBytes, - params.getSkSeedBytes()); + System.arraycopy(seed_sk, 0, tmp, digestBytes + saltBytes, skSeedBytes); - shake.update(tmp, 0, params.getDigestBytes() + saltBytes + - params.getSkSeedBytes()); + shake.update(tmp, 0, digestBytes + saltBytes + skSeedBytes); shake.doFinal(salt, 0, saltBytes); // Hash to t - System.arraycopy(salt, 0, tmp, params.getDigestBytes(), saltBytes); - shake.update(tmp, 0, params.getDigestBytes() + saltBytes); + System.arraycopy(salt, 0, tmp, digestBytes, saltBytes); + shake.update(tmp, 0, digestBytes + saltBytes); shake.doFinal(tenc, 0, params.getMBytes()); Utils.decode(tenc, t, params.getM()); @@ -114,12 +161,12 @@ public byte[] generateSignature(byte[] message) // Decode vectors for (int i = 0; i < k; i++) { - Utils.decode(V, i * params.getVBytes(), Vdec, i * v, v); + Utils.decode(V, i * vbytes, Vdec, i * v, v); } //computeMandVPV(params, Vdec, P, params.getP1Limbs(), P, Mtmp, vPv); // Compute VL: VL = Vdec * L - GF16Utils.mulAddMatXMMat(mVecLimbs, Vdec, P, params.getP1Limbs(), Mtmp, k, v, o); + GF16Utils.mulAddMatXMMat(mVecLimbs, Vdec, P, p1Limbs, Mtmp, k, v, o); // Compute VP1V: // Allocate temporary array for Pv. Its length is V_MAX * K_MAX * M_VEC_LIMBS_MAX. @@ -127,7 +174,7 @@ public byte[] generateSignature(byte[] message) long[] Pv = new long[size]; // automatically initialized to zero in Java // Compute Pv = P1 * V^T (using upper triangular multiplication) - GF16Utils.mulAddMUpperTriangularMatXMatTrans(mVecLimbs, P, Vdec, Pv, v, v, k, 1); + GF16Utils.mulAddMUpperTriangularMatXMatTrans(mVecLimbs, P, Vdec, Pv, v, v, k); // Compute VP1V = Vdec * Pv GF16Utils.mulAddMatXMMat(mVecLimbs, Vdec, Pv, vPv, k, v, k); @@ -135,13 +182,12 @@ public byte[] generateSignature(byte[] message) computeA(Mtmp, A); // Clear trailing bytes - for (int i = 0; i < params.getM(); ++i) - { - A[(i + 1) * (k * o + 1) - 1] = 0; - } +// for (int i = 0; i < params.getM(); ++i) +// { +// A[(i + 1) * (k * o + 1) - 1] = 0; +// } - Utils.decode(V, k * params.getVBytes(), r, 0, - k * o); + Utils.decode(V, k * vbytes, r, 0, k * o); if (sampleSolution(params, A, y, r, x)) { @@ -158,17 +204,14 @@ public byte[] generateSignature(byte[] message) byte[] Ox = new byte[v]; for (int i = 0; i < k; i++) { - byte[] vi = Arrays.copyOfRange(Vdec, i * v, (i + 1) * v); - GF16Utils.matMul(O, 0, x, i * o, Ox, 0, o, params.getN() - o, 1); - GF16Utils.matAdd(vi, 0, Ox, 0, s, i * params.getN(), v, 1); - System.arraycopy(x, i * o, s, - i * params.getN() + params.getN() - o, o); + GF16Utils.matMul(O, 0, x, i * o, Ox, 0, o, n - o, 1); + GF16Utils.matAdd(Vdec, i * v, Ox, 0, s, i * n, v, 1); + System.arraycopy(x, i * o, s, i * n + n - o, o); } // Encode and add salt - Utils.encode(s, sig, params.getN() * k); - System.arraycopy(salt, 0, sig, sig.length - saltBytes, - saltBytes); + Utils.encode(s, sig, n * k); + System.arraycopy(salt, 0, sig, sig.length - saltBytes, saltBytes); return Arrays.concatenate(sig, message); } @@ -195,46 +238,48 @@ public boolean verifySignature(byte[] message, byte[] signature) final int m = params.getM(); final int n = params.getN(); final int k = params.getK(); + int kn = k * n; int p1Limbs = params.getP1Limbs(); int p2Limbs = params.getP2Limbs(); int p3Limbs = params.getP3Limbs(); - final int paramMBytes = params.getMBytes(); - final int paramSigBytes = params.getSigBytes(); - final int paramDigestBytes = params.getDigestBytes(); - final int paramSaltBytes = params.getSaltBytes(); - - byte[] tEnc = new byte[params.getMBytes()]; - byte[] t = new byte[params.getM()]; - byte[] y = new byte[2 * params.getM()]; - byte[] s = new byte[params.getK() * params.getN()]; - long[] pk = new long[p1Limbs + params.getP2Limbs() + params.getP3Limbs()]; - byte[] tmp = new byte[params.getDigestBytes() + params.getSaltBytes()]; + final int mBytes = params.getMBytes(); + final int sigBytes = params.getSigBytes(); + final int digestBytes = params.getDigestBytes(); + final int saltBytes = params.getSaltBytes(); + int mVecLimbs = params.getMVecLimbs(); + byte[] tEnc = new byte[mBytes]; + byte[] t = new byte[m]; + byte[] y = new byte[m << 1]; + byte[] s = new byte[kn]; + long[] pk = new long[p1Limbs + p2Limbs + p3Limbs]; + byte[] tmp = new byte[digestBytes + saltBytes]; byte[] cpk = pubKey.getEncoded(); // Expand public key // mayo_expand_pk - MayoEngine.expandP1P2(params, pk, cpk); - Utils.unpackMVecs(cpk, params.getPkSeedBytes(), pk, p1Limbs + params.getP2Limbs(), params.getP3Limbs() / params.getMVecLimbs(), params.getM()); - + Utils.expandP1P2(params, pk, cpk); + Utils.unpackMVecs(cpk, params.getPkSeedBytes(), pk, p1Limbs + p2Limbs, p3Limbs / mVecLimbs, m); // Hash message - Utils.shake256(tmp, paramDigestBytes, message, message.length); + SHAKEDigest shake = new SHAKEDigest(256); + shake.update(message, 0, message.length); + shake.doFinal(tmp, 0, digestBytes); // Compute t - System.arraycopy(signature, paramSigBytes - paramSaltBytes, tmp, paramDigestBytes, paramSaltBytes); - Utils.shake256(tEnc, paramMBytes, tmp, paramDigestBytes + paramSaltBytes); + shake.update(tmp, 0, digestBytes); + shake.update(signature, sigBytes - saltBytes, saltBytes); + shake.doFinal(tEnc, 0, mBytes); Utils.decode(tEnc, t, m); // Decode signature - Utils.decode(signature, s, k * n); + Utils.decode(signature, s, kn); // Evaluate public map -// evalPublicMap(params, s, P1, P2, P3, y); - int mVecLimbs = (params.getM() + 15) / 16; + //evalPublicMap(params, s, P1, P2, P3, y); long[] SPS = new long[k * k * mVecLimbs]; - long[] PS = new long[n * k * mVecLimbs]; - mayoGenericMCalculatePS(params, pk, p1Limbs, p1Limbs + p2Limbs, s, m, params.getV(), params.getO(), k, PS); - mayoGenericMCalculateSPS(PS, s, m, k, n, SPS); + long[] PS = new long[kn * mVecLimbs]; + mayoGenericMCalculatePS(params, pk, p1Limbs, p1Limbs + p2Limbs, s, params.getV(), params.getO(), k, PS); + mayoGenericMCalculateSPS(PS, s, mVecLimbs, k, n, SPS); byte[] zero = new byte[m]; computeRHS(SPS, zero, y); @@ -249,13 +294,12 @@ public void computeRHS(long[] vPv, byte[] t, byte[] y) final int k = params.getK(); final int[] fTail = params.getFTail(); - final int topPos = ((m - 1) % 16) * 4; + final int topPos = ((m - 1) & 15) * 4; // Zero out tails of m_vecs if necessary - if (m % 16 != 0) + if ((m & 15) != 0) { - long mask = 1L; - mask <<= ((m % 16) * 4); + long mask = 1L << ((m & 15) << 2); mask -= 1; final int kSquared = k * k; @@ -294,13 +338,13 @@ public void computeRHS(long[] vPv, byte[] t, byte[] y) } long product = GF16Utils.mulF(top, ft); - if (jj % 2 == 0) + if ((jj & 1) == 0) { - tempBytes[jj / 2] ^= (byte)(product & 0xF); + tempBytes[jj >> 1] ^= (byte)(product & 0xF); } else { - tempBytes[jj / 2] ^= (byte)((product & 0xF) << 4); + tempBytes[jj >> 1] ^= (byte)((product & 0xF) << 4); } } Pack.littleEndianToLong(tempBytes, 0, temp); @@ -334,7 +378,6 @@ public void computeRHS(long[] vPv, byte[] t, byte[] y) private static final int F_TAIL_LEN = 4; private static final long EVEN_BYTES = 0x00FF00FF00FF00FFL; private static final long EVEN_2BYTES = 0x0000FFFF0000FFFFL; - private static final long LOW_BIT_IN_NIBBLE = 0x1111111111111111L; public void computeA(long[] Mtmp, byte[] AOut) { @@ -347,18 +390,18 @@ public void computeA(long[] Mtmp, byte[] AOut) int bitsToShift = 0; int wordsToShift = 0; - final int MAYO_M_OVER_8 = (m + 7) / 8; - final int AWidth = ((o * k + 15) / 16) * 16; - long[] A = new long[AWidth * MAYO_M_OVER_8 * 16]; + final int MAYO_M_OVER_8 = (m + 7) >>> 3; + int ok = o * k; + final int AWidth = ((ok + 15) >> 4) << 4; + long[] A = new long[(AWidth * MAYO_M_OVER_8) << 4]; // Zero out tails of m_vecs if necessary - if (m % 16 != 0) + if ((m & 15) != 0) { - long mask = 1L << ((m % 16) * 4); + long mask = 1L << ((m & 15) << 2); mask -= 1; - for (int i = 0; i < o * k; i++) + for (int i = 0, idx = mVecLimbs - 1; i < ok; i++, idx += mVecLimbs) { - int idx = i * mVecLimbs + mVecLimbs - 1; Mtmp[idx] &= mask; } } @@ -425,13 +468,13 @@ public void computeA(long[] Mtmp, byte[] AOut) // Generate tab array byte[] tab = new byte[F_TAIL_LEN << 2]; - for (int i = 0; i < F_TAIL_LEN; i++) + for (int i = 0, idx = 0; i < F_TAIL_LEN; i++) { byte ft = fTailArr[i]; - tab[4 * i] = (byte)GF16Utils.mulF(ft, 1); - tab[4 * i + 1] = (byte)GF16Utils.mulF(ft, 2); - tab[4 * i + 2] = (byte)GF16Utils.mulF(ft, 4); - tab[4 * i + 3] = (byte)GF16Utils.mulF(ft, 8); + tab[idx++] = (byte)GF16Utils.mulF(ft, 1); + tab[idx++] = (byte)GF16Utils.mulF(ft, 2); + tab[idx++] = (byte)GF16Utils.mulF(ft, 4); + tab[idx++] = (byte)GF16Utils.mulF(ft, 8); } // Final processing @@ -439,19 +482,18 @@ public void computeA(long[] Mtmp, byte[] AOut) { for (int r = m; r < m + (k + 1) * k / 2; r++) { - int pos = (r / 16) * AWidth + c + (r % 16); - long t0 = A[pos] & LOW_BIT_IN_NIBBLE; - long t1 = (A[pos] >>> 1) & LOW_BIT_IN_NIBBLE; - long t2 = (A[pos] >>> 2) & LOW_BIT_IN_NIBBLE; - long t3 = (A[pos] >>> 3) & LOW_BIT_IN_NIBBLE; + int pos = (r >>> 4) * AWidth + c + (r & 15); + long t0 = A[pos] & GF16Utils.MASK_LSB; + long t1 = (A[pos] >>> 1) & GF16Utils.MASK_LSB; + long t2 = (A[pos] >>> 2) & GF16Utils.MASK_LSB; + long t3 = (A[pos] >>> 3) & GF16Utils.MASK_LSB; - for (int t = 0; t < F_TAIL_LEN; t++) + for (int t = 0, t4 = 0; t < F_TAIL_LEN; t++, t4 += 4) { int targetRow = r + t - m; - int targetPos = (targetRow / 16) * AWidth + c + (targetRow % 16); - long xorValue = (t0 * tab[4 * t]) ^ (t1 * tab[4 * t + 1]) - ^ (t2 * tab[4 * t + 2]) ^ (t3 * tab[4 * t + 3]); - A[targetPos] ^= xorValue; + int targetPos = (targetRow >> 4) * AWidth + c + (targetRow & 15); + A[targetPos] ^= (t0 * tab[t4]) ^ (t1 * tab[t4 + 1]) + ^ (t2 * tab[t4 + 2]) ^ (t3 * tab[t4 + 3]); } } } @@ -464,9 +506,8 @@ public void computeA(long[] Mtmp, byte[] AOut) { for (int i = 0; i + r < m; i++) { - Utils.decode(Abytes, (r * AWidth / 16 + c + i) * 8, - AOut, (r + i) * ACols + c, - Math.min(16, ACols - 1 - c)); + Utils.decode(Abytes, (((r * AWidth) >> 4) + c + i) << 3, + AOut, (r + i) * ACols + c, Math.min(16, ACols - 1 - c)); } } } @@ -477,21 +518,20 @@ private static void transpose16x16Nibbles(long[] M, int offset) for (int i = 0; i < 16; i += 2) { int idx1 = offset + i; - int idx2 = offset + i + 1; + int idx2 = idx1 + 1; long t = ((M[idx1] >>> 4) ^ M[idx2]) & 0x0F0F0F0F0F0F0F0FL; M[idx1] ^= t << 4; M[idx2] ^= t; } - for (int i = 0; i < 16; i += 4) + for (int i = 0, base = offset; i < 16; i += 4) { - int base = offset + i; long t0 = ((M[base] >>> 8) ^ M[base + 2]) & EVEN_BYTES; long t1 = ((M[base + 1] >>> 8) ^ M[base + 3]) & EVEN_BYTES; - M[base] ^= t0 << 8; - M[base + 1] ^= t1 << 8; - M[base + 2] ^= t0; - M[base + 3] ^= t1; + M[base++] ^= t0 << 8; + M[base++] ^= t1 << 8; + M[base++] ^= t0; + M[base++] ^= t1; } for (int i = 0; i < 4; i++) @@ -529,10 +569,10 @@ public boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, byte[] Ar = new byte[m]; // Clear last column of A - for (int i = 0; i < m; i++) - { - A[k * o + i * (k * o + 1)] = 0; - } +// for (int i = 0; i < m; i++) +// { +// A[k * o + i * (k * o + 1)] = 0; +// } GF16Utils.matMul(A, r, Ar, k * o + 1, m, 1); // Update last column of A with y - Ar @@ -572,22 +612,23 @@ public boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, // Update matrix entries - for (int i = 0; i < row; i += 8) + for (int i = 0, iaCols_col = col, iaCols_aCols1 = aCols - 1; i < row; i += 8, + iaCols_col += aCols << 3, iaCols_aCols1 += aCols << 3) { long tmp = 0; // Pack 8 GF(16) elements into long - for (int j = 0; j < 8; j++) + for (int j = 0, jaCols = 0; j < 8; j++, jaCols += aCols) { - tmp ^= (long)(A[(i + j) * aCols + col] & 0xFF) << (j * 8); + tmp ^= (long)(A[iaCols_col + jaCols] & 0xFF) << (j << 3); } // GF(16) multiplication tmp = GF16Utils.mulFx8(u, tmp); // Unpack and update - for (int j = 0; j < 8; j++) + for (int j = 0, jaCols = 0; j < 8; j++, jaCols += aCols) { - A[(i + j) * aCols + aCols - 1] ^= (byte)((tmp >> (j * 8)) & 0x0F); + A[iaCols_aCols1 + jaCols] ^= (byte)((tmp >> (j << 3)) & 0x0F); } } finished |= correctCol; @@ -656,7 +697,7 @@ public void ef(byte[] A, int nrows, int ncols) int pivot = 0; long pivotIsZero = -1L; // all bits set (0xFFFFFFFFFFFFFFFF) int searchUpper = Math.min(nrows - 1, upperBound + 32); - for (int row = lowerBound; row <= searchUpper; row++) + for (int row = lowerBound, rowRowLen = lowerBound * rowLen; row <= searchUpper; row++, rowRowLen += rowLen) { long isPivotRow = ~ctCompare64(row, pivotRowIndex); //ct64IsGreaterThan(a, b): Returns 0xFFFFFFFFFFFFFFFF if a > b, 0 otherwise. @@ -664,8 +705,7 @@ public void ef(byte[] A, int nrows, int ncols) for (int j = 0; j < rowLen; j++) { // The expression below accumulates (in constant time) the candidate pivot row. - pivotRow[j] ^= (isPivotRow | (belowPivotRow & pivotIsZero)) - & packedA[row * rowLen + j]; + pivotRow[j] ^= (isPivotRow | (belowPivotRow & pivotIsZero)) & packedA[rowRowLen + j]; } // Extract candidate pivot element from the packed row. pivot = (int)((pivotRow[pivotCol >>> 4] >>> ((pivotCol & 15) << 2)) & 0xF); @@ -684,17 +724,17 @@ public void ef(byte[] A, int nrows, int ncols) for (int col = 0, rowRowLen_col = rowRowLen; col < rowLen; col++, rowRowLen_col++) { // Since the masks are disjoint, addition is equivalent to OR. - packedA[rowRowLen_col] = (doNotCopy & packedA[rowRowLen_col]) | - (doCopy & pivotRow2[col]); + packedA[rowRowLen_col] = (doNotCopy & packedA[rowRowLen_col]) | (doCopy & pivotRow2[col]); } } // Eliminate entries below the pivot. - for (int row = lowerBound; row < nrows; row++) + for (int row = lowerBound, rowRowLen = lowerBound * rowLen; row < nrows; row++, rowRowLen += rowLen) { - int belowPivot = (row > pivotRowIndex) ? 1 : 0; - int eltToElim = mExtractElementFromPacked(packedA, row, rowLen, pivotCol); - vecMulAddU64(rowLen, pivotRow2, (byte)(belowPivot * eltToElim), packedA, row * rowLen); + int belowPivot = (row > pivotRowIndex) ? -1 : 0; + //int eltToElim = mExtractElementFromPacked(packedA, row, rowLen, pivotCol); + int eltToElim = (int)((packedA[rowRowLen + (pivotCol >>> 4)] >>> ((pivotCol & 15) << 2)) & 0xF); + vecMulAddU64(rowLen, pivotRow2, (byte)(belowPivot & eltToElim), packedA, rowRowLen); } // If pivot is nonzero, increment pivotRowIndex. @@ -729,20 +769,6 @@ private static long ctCompare64(int a, int b) return (-(long)(a ^ b)) >> 63; } - /** - * Extracts an element from the packed matrix for a given row and column. - * - * @param packedA the packed matrix stored in row-major order - * @param row the row index - * @param rowLen the number of longs per row - * @param index the column index - * @return the GF(16) element at that position. - */ - private static int mExtractElementFromPacked(long[] packedA, int row, int rowLen, int index) - { - return (int)((packedA[row * rowLen + (index >>> 4)] >>> ((index & 15) << 2)) & 0xF); - } - /** * Computes the multiplicative inverse in GF(16) for a GF(16) element. */ @@ -764,10 +790,7 @@ private static int inverseF(int a) public static int mulF(int a, int b) { // Carryless multiply: multiply b by each bit of a and XOR. - int p = ((a & 1) * b) ^ - ((a & 2) * b) ^ - ((a & 4) * b) ^ - ((a & 8) * b); + int p = (-(a & 1) & b) ^ (-((a >> 1) & 1) & (b << 1)) ^ (-((a >> 2) & 1) & (b << 2)) ^ (-((a >> 3) & 1) & (b << 3)); // Reduce modulo f(X) = x^4 + x + 1. int topP = p & 0xF0; return (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0F; @@ -787,13 +810,12 @@ public static int mulF(int a, int b) private static void vecMulAddU64(int legs, long[] in, byte a, long[] acc) { int tab = mulTable(a & 0xFF); - long lsbAsk = 0x1111111111111111L; for (int i = 0; i < legs; i++) { - long val = ((in[i] & lsbAsk) * (tab & 0xFF)) - ^ (((in[i] >>> 1) & lsbAsk) * ((tab >>> 8) & 0xF)) - ^ (((in[i] >>> 2) & lsbAsk) * ((tab >>> 16) & 0xF)) - ^ (((in[i] >>> 3) & lsbAsk) * ((tab >>> 24) & 0xF)); + long val = ((in[i] & GF16Utils.MASK_LSB) * (tab & 0xFF)) + ^ (((in[i] >>> 1) & GF16Utils.MASK_LSB) * ((tab >>> 8) & 0xF)) + ^ (((in[i] >>> 2) & GF16Utils.MASK_LSB) * ((tab >>> 16) & 0xF)) + ^ (((in[i] >>> 3) & GF16Utils.MASK_LSB) * ((tab >>> 24) & 0xF)); acc[i] ^= val; } } @@ -810,13 +832,12 @@ private static void vecMulAddU64(int legs, long[] in, byte a, long[] acc) private static void vecMulAddU64(int legs, long[] in, byte a, long[] acc, int accOffset) { int tab = mulTable(a & 0xFF); - long lsbAsk = 0x1111111111111111L; for (int i = 0; i < legs; i++) { - long val = ((in[i] & lsbAsk) * (tab & 0xFF)) - ^ (((in[i] >>> 1) & lsbAsk) * ((tab >>> 8) & 0xF)) - ^ (((in[i] >>> 2) & lsbAsk) * ((tab >>> 16) & 0xF)) - ^ (((in[i] >>> 3) & lsbAsk) * ((tab >>> 24) & 0xF)); + long val = ((in[i] & GF16Utils.MASK_LSB) * (tab & 0xFF)) + ^ (((in[i] >>> 1) & GF16Utils.MASK_LSB) * ((tab >>> 8) & 0xF)) + ^ (((in[i] >>> 2) & GF16Utils.MASK_LSB) * ((tab >>> 16) & 0xF)) + ^ (((in[i] >>> 3) & GF16Utils.MASK_LSB) * ((tab >>> 24) & 0xF)); acc[accOffset + i] ^= val; } } @@ -832,17 +853,16 @@ private static void vecMulAddU64(int legs, long[] in, byte a, long[] acc, int ac private static int mulTable(int b) { int x = b * 0x08040201; - int highNibbleMask = 0xf0f0f0f0; - int highHalf = x & highNibbleMask; + int highHalf = x & 0xf0f0f0f0; return x ^ (highHalf >>> 4) ^ (highHalf >>> 3); } private static void mayoGenericMCalculatePS(MayoParameters p, long[] P1, int p2, int p3, byte[] S, - int m, int v, int o, int k, long[] PS) + int v, int o, int k, long[] PS) { int n = o + v; - int mVecLimbs = (m + 15) / 16; - long[] accumulator = new long[16 * ((p.getM() + 15) / 16 * p.getK() * p.getN() * mVecLimbs)]; + int mVecLimbs = p.getMVecLimbs(); + long[] accumulator = new long[(mVecLimbs * p.getK() * p.getN() * mVecLimbs) << 4]; int o_mVecLimbs = o * mVecLimbs; int pUsed = 0; for (int row = 0, krow = 0, orow_mVecLimbs = 0; row < v; row++, krow += k, orow_mVecLimbs += o_mVecLimbs) @@ -881,29 +901,28 @@ private static void mayoGenericMCalculatePS(MayoParameters p, long[] P1, int p2, mVecMultiplyBins(mVecLimbs, n * k, accumulator, PS); } - private static void mayoGenericMCalculateSPS(long[] PS, byte[] S, int m, int k, int n, long[] SPS) + private static void mayoGenericMCalculateSPS(long[] PS, byte[] S, int mVecLimbs, int k, int n, long[] SPS) { - final int mVecLimbs = (m + 15) / 16; - final int accumulatorSize = (mVecLimbs * k * k) << 4; + int kk = k * k; + final int accumulatorSize = (mVecLimbs * kk) << 4; final long[] accumulator = new long[accumulatorSize]; + int kmVecLimbs = k * mVecLimbs; // Accumulation phase - for (int row = 0, nrow = 0; row < k; row++, nrow += n) + for (int row = 0, nrow = 0, krowmVecLimbs16 = 0; row < k; row++, nrow += n, krowmVecLimbs16 += kmVecLimbs << 4) { - for (int j = 0; j < n; j++) + for (int j = 0, jkmVecLimbs = 0; j < n; j++, jkmVecLimbs += kmVecLimbs) { - final int sVal = S[nrow + j] & 0xFF; // Unsigned byte value - for (int col = 0; col < k; col++) + final int sValmVecLimbs = (S[nrow + j] & 0xFF) * mVecLimbs + krowmVecLimbs16; // Unsigned byte value + for (int col = 0, colmVecLimbs = 0; col < k; col++, colmVecLimbs += mVecLimbs) { - final int psOffset = (j * k + col) * mVecLimbs; - final int accOffset = ((row * k + col) * 16 + sVal) * mVecLimbs; - Longs.xorTo(mVecLimbs, PS, psOffset, accumulator, accOffset); + Longs.xorTo(mVecLimbs, PS, jkmVecLimbs + colmVecLimbs, accumulator, sValmVecLimbs + (colmVecLimbs << 4)); } } } // Processing phase - mVecMultiplyBins(mVecLimbs, k * k, accumulator, SPS); + mVecMultiplyBins(mVecLimbs, kk, accumulator, SPS); } private static void mVecMultiplyBins(int mVecLimbs, int len, long[] bins, long[] ps) @@ -930,27 +949,23 @@ private static void mVecMultiplyBins(int mVecLimbs, int len, long[] bins, long[] } // Modular arithmetic operations - private static void mVecMulAddXInv(int limbs, long[] in, int inOffset, - long[] acc, int accOffset) + private static void mVecMulAddXInv(int limbs, long[] in, int inOffset, long[] acc, int accOffset) { - final long maskLsb = 0x1111111111111111L; for (int i = 0; i < limbs; i++) { long input = in[inOffset + i]; - long t = input & maskLsb; - acc[accOffset + i] ^= ((input ^ t) >>> 1) ^ (t * 9); + long t = input & GF16Utils.MASK_LSB; + acc[accOffset + i] ^= ((input & GF16Utils.NIBBLE_MASK_LSB) >>> 1) ^ ((t << 3) + t); } } - private static void mVecMulAddX(int limbs, long[] in, int inOffset, - long[] acc, int accOffset) + private static void mVecMulAddX(int limbs, long[] in, int inOffset, long[] acc, int accOffset) { - final long maskMsb = 0x8888888888888888L; for (int i = 0; i < limbs; i++) { long input = in[inOffset + i]; - long t = input & maskMsb; - acc[accOffset + i] ^= ((input ^ t) << 1) ^ ((t >>> 3) * 3); + long t = (input & GF16Utils.MASK_MSB) >>> 3; + acc[accOffset + i] ^= ((input & GF16Utils.NIBBLE_MASK_MSB) << 1) ^ ((t << 1) + t); } } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java index 320204911a..e48d019cda 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java @@ -1,6 +1,12 @@ package org.bouncycastle.pqc.crypto.mayo; -import org.bouncycastle.crypto.digests.SHAKEDigest; +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.modes.CTRModeCipher; +import org.bouncycastle.crypto.modes.SICBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; public class Utils @@ -40,14 +46,14 @@ public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mde for (i = 0; i < mdecLen / 2; i++) { // Extract the lower nibble - mdec[decIndex++] = (byte)((m[i + mOff] & 0xFF) & 0x0F); + mdec[decIndex++] = (byte)(m[i + mOff] & 0x0F); // Extract the upper nibble (shift right 4 bits) - mdec[decIndex++] = (byte)(((m[i + mOff] & 0xFF) >> 4) & 0x0F); + mdec[decIndex++] = (byte)((m[i + mOff] >> 4) & 0x0F); } // If there is an extra nibble (odd number of nibbles), decode only the lower nibble if (mdecLen % 2 == 1) { - mdec[decIndex] = (byte)((m[i + mOff] & 0xFF) & 0x0F); + mdec[decIndex] = (byte)(m[i + mOff] & 0x0F); } } @@ -114,19 +120,19 @@ public static void unpackMVecs(byte[] in, long[] out, int vecs, int m) { int mVecLimbs = (m + 15) / 16; int bytesToCopy = m / 2; // Number of bytes to copy per vector + // Temporary buffer to hold mVecLimbs longs (each long is 8 bytes) + byte[] tmp = new byte[mVecLimbs << 3]; // Process vectors in reverse order for (int i = vecs - 1; i >= 0; i--) { - // Temporary buffer to hold mVecLimbs longs (each long is 8 bytes) - byte[] tmp = new byte[mVecLimbs * 8]; // Copy m/2 bytes from the input into tmp. The rest remains zero. System.arraycopy(in, i * bytesToCopy, tmp, 0, bytesToCopy); // Convert each 8-byte block in tmp into a long using Pack for (int j = 0; j < mVecLimbs; j++) { - out[i * mVecLimbs + j] = Pack.littleEndianToLong(tmp, j * 8); + out[i * mVecLimbs + j] = Pack.littleEndianToLong(tmp, j << 3); } } } @@ -135,12 +141,11 @@ public static void unpackMVecs(byte[] in, int inOff, long[] out, int outOff, int { int mVecLimbs = (m + 15) / 16; int bytesToCopy = m / 2; // Number of bytes to copy per vector - + // Temporary buffer to hold mVecLimbs longs (each long is 8 bytes) + byte[] tmp = new byte[mVecLimbs << 3]; // Process vectors in reverse order for (int i = vecs - 1; i >= 0; i--) { - // Temporary buffer to hold mVecLimbs longs (each long is 8 bytes) - byte[] tmp = new byte[mVecLimbs * 8]; // Copy m/2 bytes from the input into tmp. The rest remains zero. System.arraycopy(in, inOff + i * bytesToCopy, tmp, 0, bytesToCopy); @@ -183,26 +188,59 @@ public static void packMVecs(long[] in, byte[] out, int outOff, int vecs, int m) } /** - * Computes the SHAKE256 XOF on the given input. + * Expands P1 and P2 using AES_128_CTR as a PRF and then unpacks the resulting bytes + * into an array of 64-bit limbs. * - * @param output the output buffer that will be filled with the result. - * @param outlen the number of bytes to produce. - * @param input the input byte array. - * @param inlen the number of input bytes. - * @return the number of output bytes produced (equals outlen). + * @param p Mayo parameters + * @param P The output long array which will hold the unpacked limbs. + * Its length should be at least ((P1_bytes + P2_bytes) / 8) limbs. + * @param seed_pk The seed (used as the key) for the PRF. */ - public static int shake256(byte[] output, int outlen, byte[] input, int inlen) + public static void expandP1P2(MayoParameters p, long[] P, byte[] seed_pk) { - // Create a new SHAKE256 digest instance. - SHAKEDigest shake = new SHAKEDigest(256); - - // Absorb the input. - shake.update(input, 0, inlen); + // Compute total number of bytes to generate: P1_bytes + P2_bytes. + int outLen = p.getP1Bytes() + p.getP2Bytes(); + // Temporary byte array to hold the PRF output. + byte[] temp = new byte[outLen]; + + //AES_128_CTR(temp, outLen, seed_pk, p.getPkSeedBytes()); + // Create a 16-byte IV (all zeros) + byte[] iv = new byte[16]; // automatically zero-initialized + + // Set up AES engine in CTR (SIC) mode. + BlockCipher aesEngine = AESEngine.newInstance(); + // SICBlockCipher implements CTR mode for AES. + CTRModeCipher ctrCipher = SICBlockCipher.newInstance(aesEngine); + // Wrap the key with the IV. + ParametersWithIV params = new ParametersWithIV(new KeyParameter(Arrays.copyOf(seed_pk, p.getPkSeedBytes())), iv); + ctrCipher.init(true, params); + + // CTR mode is a stream cipher: encrypting zero bytes produces the keystream. + int blockSize = ctrCipher.getBlockSize(); // typically 16 bytes + byte[] zeroBlock = new byte[blockSize]; // block of zeros + byte[] blockOut = new byte[blockSize]; + + int offset = 0; + // Process full blocks + while (offset + blockSize <= outLen) + { + ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); + System.arraycopy(blockOut, 0, temp, offset, blockSize); + offset += blockSize; + } + // Process any remaining partial block. + if (offset < outLen) + { + ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); + int remaining = outLen - offset; + System.arraycopy(blockOut, 0, temp, offset, remaining); + } - // Squeeze out outlen bytes into the output array. - shake.doFinal(output, 0, outlen); + // The number of vectors is the total limbs divided by mVecLimbs. + int numVectors = (p.getP1Limbs() + p.getP2Limbs()) / p.getMVecLimbs(); - return outlen; + // Unpack the byte array 'temp' into the long array 'P' + // using our previously defined unpackMVecs method. + unpackMVecs(temp, P, numVectors, p.getM()); } - } From 13d5a190ba33843e340de59d0b93ed42a04b9eda Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 4 Mar 2025 13:10:46 +1030 Subject: [PATCH 144/890] Add Mayo to PQC Provider --- .../asn1/bc/BCObjectIdentifiers.java | 16 +- .../pqc/crypto/mayo/MayoKeyPairGenerator.java | 2 +- .../pqc/crypto/mayo/MayoParameters.java | 8 +- .../crypto/mayo/MayoPrivateKeyParameter.java | 41 --- .../crypto/mayo/MayoPrivateKeyParameters.java | 25 ++ ...eter.java => MayoPublicKeyParameters.java} | 5 +- .../pqc/crypto/mayo/MayoSigner.java | 10 +- .../pqc/crypto/util/PrivateKeyFactory.java | 10 +- .../crypto/util/PrivateKeyInfoFactory.java | 8 + .../pqc/crypto/util/PublicKeyFactory.java | 21 ++ .../util/SubjectPublicKeyInfoFactory.java | 9 +- .../bouncycastle/pqc/crypto/util/Utils.java | 134 +++++---- .../pqc/crypto/test/MayoTest.java | 19 +- .../jce/provider/BouncyCastleProvider.java | 6 + .../pqc/jcajce/interfaces/MayoKey.java | 16 + .../provider/BouncyCastlePQCProvider.java | 3 +- .../pqc/jcajce/provider/Mayo.java | 44 +++ .../provider/cmce/BCCMCEPrivateKey.java | 2 +- .../jcajce/provider/cmce/BCCMCEPublicKey.java | 2 +- .../provider/falcon/FalconKeyFactorySpi.java | 1 - .../provider/mayo/BCMayoPrivateKey.java | 131 +++++++++ .../jcajce/provider/mayo/BCMayoPublicKey.java | 127 ++++++++ .../provider/mayo/MayoKeyFactorySpi.java | 130 +++++++++ .../mayo/MayoKeyPairGeneratorSpi.java | 150 ++++++++++ .../jcajce/provider/mayo/SignatureSpi.java | 218 ++++++++++++++ .../pqc/jcajce/spec/MayoParameterSpec.java | 48 +++ .../test/MayoKeyPairGeneratorTest.java | 63 ++++ .../pqc/jcajce/provider/test/MayoTest.java | 276 ++++++++++++++++++ 28 files changed, 1395 insertions(+), 130 deletions(-) delete mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameter.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameters.java rename core/src/main/java/org/bouncycastle/pqc/crypto/mayo/{MayoPublicKeyParameter.java => MayoPublicKeyParameters.java} (64%) create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/MayoKey.java create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Mayo.java create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/BCMayoPrivateKey.java create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/BCMayoPublicKey.java create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/MayoKeyFactorySpi.java create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/MayoKeyPairGeneratorSpi.java create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/SignatureSpi.java create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/spec/MayoParameterSpec.java create mode 100644 prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MayoKeyPairGeneratorTest.java create mode 100644 prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MayoTest.java diff --git a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java index c5753d7dc7..b0e8f7726f 100644 --- a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java @@ -248,7 +248,7 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier falcon = bc_sig.branch("7"); ASN1ObjectIdentifier falcon_512 = new ASN1ObjectIdentifier("1.3.9999.3.6"); // falcon.branch("1"); - ASN1ObjectIdentifier falcon_1024 = new ASN1ObjectIdentifier("1.3.9999.3.9"); // falcon.branch("2"); + ASN1ObjectIdentifier falcon_1024 = new ASN1ObjectIdentifier("1.3.9999.3.9"); // falcon.branch("2"); /* * Dilithium @@ -403,7 +403,7 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier ntrulpr953 = pqc_kem_ntrulprime.branch("4"); ASN1ObjectIdentifier ntrulpr1013 = pqc_kem_ntrulprime.branch("5"); ASN1ObjectIdentifier ntrulpr1277 = pqc_kem_ntrulprime.branch("6"); - + ASN1ObjectIdentifier pqc_kem_sntruprime = pqc_kem_ntruprime.branch("2"); ASN1ObjectIdentifier sntrup653 = pqc_kem_sntruprime.branch("1"); ASN1ObjectIdentifier sntrup761 = pqc_kem_sntruprime.branch("2"); @@ -411,7 +411,7 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier sntrup953 = pqc_kem_sntruprime.branch("4"); ASN1ObjectIdentifier sntrup1013 = pqc_kem_sntruprime.branch("5"); ASN1ObjectIdentifier sntrup1277 = pqc_kem_sntruprime.branch("6"); - + /** * BIKE **/ @@ -432,7 +432,6 @@ public interface BCObjectIdentifiers /** * ML-KEM/ML-DSA seed parameters algorithms - temporary - * */ //TODO: delete before release ASN1ObjectIdentifier id_id_alg_seed = bc.branch("10"); @@ -443,4 +442,13 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier id_id_alg_ml_kem_512_seed = id_id_alg_seed.branch("4"); ASN1ObjectIdentifier id_id_alg_ml_kem_768_seed = id_id_alg_seed.branch("5"); ASN1ObjectIdentifier id_id_alg_ml_kem_1024_seed = id_id_alg_seed.branch("6"); + + /** + * Mayo + */ + ASN1ObjectIdentifier mayo = bc_sig.branch("10"); + ASN1ObjectIdentifier mayo1 = mayo.branch("1"); + ASN1ObjectIdentifier mayo2 = mayo.branch("2"); + ASN1ObjectIdentifier mayo3 = mayo.branch("3"); + ASN1ObjectIdentifier mayo5 = mayo.branch("4"); } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java index 1a5f9ff037..ffad286ab3 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java @@ -110,6 +110,6 @@ public AsymmetricCipherKeyPair generateKeyPair() Arrays.clear(O); Arrays.clear(P3); - return new AsymmetricCipherKeyPair(new MayoPublicKeyParameter(p, cpk), new MayoPrivateKeyParameter(p, seed_sk)); + return new AsymmetricCipherKeyPair(new MayoPublicKeyParameters(p, cpk), new MayoPrivateKeyParameters(p, seed_sk)); } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java index f1a1def99d..86e99704a7 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java @@ -2,7 +2,7 @@ public class MayoParameters { - public static final MayoParameters MAYO1 = new MayoParameters( + public static final MayoParameters mayo1 = new MayoParameters( "MAYO_1", // name 86, // n 78, // m @@ -30,7 +30,7 @@ public class MayoParameters 24 // sk_seed_bytes ); - public static final MayoParameters MAYO2 = new MayoParameters( + public static final MayoParameters mayo2 = new MayoParameters( "MAYO_2", // name 81, // n 64, // m @@ -58,7 +58,7 @@ public class MayoParameters 24 // sk_seed_bytes ); - public static final MayoParameters MAYO3 = new MayoParameters( + public static final MayoParameters mayo3 = new MayoParameters( "MAYO_3", // name 118, // n 108, // m @@ -86,7 +86,7 @@ public class MayoParameters 32 // sk_seed_bytes ); - public static final MayoParameters MAYO5 = new MayoParameters( + public static final MayoParameters mayo5 = new MayoParameters( "MAYO_5", // name 154, // n 142, // m diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameter.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameter.java deleted file mode 100644 index 7954094a38..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameter.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.bouncycastle.pqc.crypto.mayo; - -import org.bouncycastle.util.Arrays; - -public class MayoPrivateKeyParameter - extends MayoKeyParameters -{ - // Represents the field: uint64_t p[P1_LIMBS_MAX + P2_LIMBS_MAX]; -// private final byte[] p; - // Represents the field: uint8_t O[V_MAX * O_MAX]; -// private final byte[] O; - private final byte[] seed_sk; - - public MayoPrivateKeyParameter(MayoParameters params, byte[] seed_sk) - { - super(true, params); - this.seed_sk = seed_sk; -// this.p = p; -// this.O = O; - } - -// public byte[] getP() -// { -// return p; -// } -// -// public byte[] getO() -// { -// return O; -// } - - public byte[] getEncoded() - { - return Arrays.clone(seed_sk); - } - - public byte[] getSeedSk() - { - return Arrays.clone(seed_sk); - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameters.java new file mode 100644 index 0000000000..f325f96dc4 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameters.java @@ -0,0 +1,25 @@ +package org.bouncycastle.pqc.crypto.mayo; + +import org.bouncycastle.util.Arrays; + +public class MayoPrivateKeyParameters + extends MayoKeyParameters +{ + private final byte[] seed_sk; + + public MayoPrivateKeyParameters(MayoParameters params, byte[] seed_sk) + { + super(true, params); + this.seed_sk = seed_sk; + } + + public byte[] getEncoded() + { + return Arrays.clone(seed_sk); + } + + public byte[] getSeedSk() + { + return Arrays.clone(seed_sk); + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPublicKeyParameter.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPublicKeyParameters.java similarity index 64% rename from core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPublicKeyParameter.java rename to core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPublicKeyParameters.java index 68613b35bd..086660edfd 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPublicKeyParameter.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPublicKeyParameters.java @@ -2,13 +2,12 @@ import org.bouncycastle.util.Arrays; -public class MayoPublicKeyParameter +public class MayoPublicKeyParameters extends MayoKeyParameters { - // Represents the field: uint64_t p[P1_LIMBS_MAX + P2_LIMBS_MAX + P3_LIMBS_MAX]; private final byte[] p; - public MayoPublicKeyParameter(MayoParameters params, byte[] p) + public MayoPublicKeyParameters(MayoParameters params, byte[] p) { super(false, params); this.p = p; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index df6922625d..9d8656b26f 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -16,8 +16,8 @@ public class MayoSigner { private SecureRandom random; MayoParameters params; - private MayoPublicKeyParameter pubKey; - private MayoPrivateKeyParameter privKey; + private MayoPublicKeyParameters pubKey; + private MayoPrivateKeyParameters privKey; @Override public void init(boolean forSigning, CipherParameters param) @@ -30,19 +30,19 @@ public void init(boolean forSigning, CipherParameters param) if (param instanceof ParametersWithRandom) { ParametersWithRandom withRandom = (ParametersWithRandom)param; - privKey = (MayoPrivateKeyParameter)withRandom.getParameters(); + privKey = (MayoPrivateKeyParameters)withRandom.getParameters(); random = withRandom.getRandom(); } else { - privKey = (MayoPrivateKeyParameter)param; + privKey = (MayoPrivateKeyParameters)param; random = null; } params = privKey.getParameters(); } else { - pubKey = (MayoPublicKeyParameter)param; + pubKey = (MayoPublicKeyParameters)param; params = pubKey.getParameters(); privKey = null; random = null; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java index fd5ca0146a..e582bfd41e 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java @@ -43,6 +43,8 @@ import org.bouncycastle.pqc.crypto.hqc.HQCParameters; import org.bouncycastle.pqc.crypto.hqc.HQCPrivateKeyParameters; import org.bouncycastle.pqc.crypto.lms.HSSPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoPrivateKeyParameters; import org.bouncycastle.pqc.crypto.mldsa.MLDSAParameters; import org.bouncycastle.pqc.crypto.mldsa.MLDSAPrivateKeyParameters; import org.bouncycastle.pqc.crypto.mldsa.MLDSAPublicKeyParameters; @@ -183,7 +185,7 @@ else if (algOID.on(BCObjectIdentifiers.sphincsPlus) || algOID.on(BCObjectIdentif return new SPHINCSPlusPrivateKeyParameters(spParams, ASN1OctetString.getInstance(obj).getOctets()); } } - else if (Utils.shldsaParams.containsKey(algOID)) + else if (Utils.slhdsaParams.containsKey(algOID)) { SLHDSAParameters spParams = Utils.slhdsaParamsLookup(algOID); ASN1OctetString slhdsaKey = parseOctetString(keyInfo.getPrivateKey(), spParams.getN() * 4); @@ -479,6 +481,12 @@ else if (algOID.equals(PQCObjectIdentifiers.mcElieceCca2)) return new McElieceCCA2PrivateKeyParameters(mKey.getN(), mKey.getK(), mKey.getField(), mKey.getGoppaPoly(), mKey.getP(), Utils.getDigestName(mKey.getDigest().getAlgorithm())); } + else if (algOID.on(BCObjectIdentifiers.mayo)) + { + byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets(); + MayoParameters mayoParams = Utils.mayoParamsLookup(algOID); + return new MayoPrivateKeyParameters(mayoParams, keyEnc); + } else { throw new RuntimeException("algorithm identifier in private key not recognised"); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java index cd41f2465e..9b3aaff843 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java @@ -33,6 +33,7 @@ import org.bouncycastle.pqc.crypto.lms.Composer; import org.bouncycastle.pqc.crypto.lms.HSSPrivateKeyParameters; import org.bouncycastle.pqc.crypto.lms.LMSPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoPrivateKeyParameters; import org.bouncycastle.pqc.crypto.mldsa.MLDSAPrivateKeyParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMPrivateKeyParameters; import org.bouncycastle.pqc.crypto.newhope.NHPrivateKeyParameters; @@ -336,6 +337,13 @@ else if (privateKey instanceof RainbowPrivateKeyParameters) byte[] encoding = params.getEncoded(); return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(encoding), attributes); } + else if (privateKey instanceof MayoPrivateKeyParameters) + { + MayoPrivateKeyParameters params = (MayoPrivateKeyParameters)privateKey; + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.mayoOidLookup(params.getParameters())); + byte[] encoding = params.getEncoded(); + return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(encoding), attributes); + } else { throw new IOException("key parameters not recognized"); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java index 38384b2f7b..fb8ebab79a 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java @@ -40,6 +40,8 @@ import org.bouncycastle.pqc.crypto.hqc.HQCPublicKeyParameters; import org.bouncycastle.pqc.crypto.lms.HSSPublicKeyParameters; import org.bouncycastle.pqc.crypto.lms.LMSKeyParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoPublicKeyParameters; import org.bouncycastle.pqc.crypto.mldsa.MLDSAParameters; import org.bouncycastle.pqc.crypto.mldsa.MLDSAPublicKeyParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters; @@ -253,6 +255,11 @@ public class PublicKeyFactory converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_192f_with_shake256, new SLHDSAConverter()); converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_256s_with_shake256, new SLHDSAConverter()); converters.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_256f_with_shake256, new SLHDSAConverter()); + + converters.put(BCObjectIdentifiers.mayo1, new MayoConverter()); + converters.put(BCObjectIdentifiers.mayo2, new MayoConverter()); + converters.put(BCObjectIdentifiers.mayo3, new MayoConverter()); + converters.put(BCObjectIdentifiers.mayo5, new MayoConverter()); } /** @@ -847,4 +854,18 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje return new RainbowPublicKeyParameters(rainbowParams, keyEnc); } } + + private static class MayoConverter + extends SubjectPublicKeyInfoConverter + { + AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + throws IOException + { + byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets(); + + MayoParameters mayoParams = Utils.mayoParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); + + return new MayoPublicKeyParameters(mayoParams, keyEnc); + } + } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java index ff782c74af..7edfd3599e 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java @@ -25,7 +25,7 @@ import org.bouncycastle.pqc.crypto.lms.Composer; import org.bouncycastle.pqc.crypto.lms.HSSPublicKeyParameters; import org.bouncycastle.pqc.crypto.lms.LMSPublicKeyParameters; -import org.bouncycastle.pqc.crypto.mayo.MayoPublicKeyParameter; +import org.bouncycastle.pqc.crypto.mayo.MayoPublicKeyParameters; import org.bouncycastle.pqc.crypto.mldsa.MLDSAPublicKeyParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMPublicKeyParameters; import org.bouncycastle.pqc.crypto.newhope.NHPublicKeyParameters; @@ -303,6 +303,13 @@ else if (publicKey instanceof RainbowPublicKeyParameters) return new SubjectPublicKeyInfo(algorithmIdentifier, new DEROctetString(encoding)); } + else if (publicKey instanceof MayoPublicKeyParameters) + { + MayoPublicKeyParameters params = (MayoPublicKeyParameters)publicKey; + byte[] encoding = params.getEncoded(); + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.mayoOidLookup(params.getParameters())); + return new SubjectPublicKeyInfo(algorithmIdentifier, new DEROctetString(encoding)); + } else { throw new IOException("key parameters not recognized"); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java index 496211c67d..750504067c 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java @@ -27,6 +27,7 @@ import org.bouncycastle.pqc.crypto.falcon.FalconParameters; import org.bouncycastle.pqc.crypto.frodo.FrodoParameters; import org.bouncycastle.pqc.crypto.hqc.HQCParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoParameters; import org.bouncycastle.pqc.crypto.mldsa.MLDSAParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters; import org.bouncycastle.pqc.crypto.ntru.NTRUParameters; @@ -105,8 +106,11 @@ class Utils static final Map mldsaOids = new HashMap(); static final Map mldsaParams = new HashMap(); - static final Map shldsaOids = new HashMap(); - static final Map shldsaParams = new HashMap(); + static final Map slhdsaOids = new HashMap(); + static final Map slhdsaParams = new HashMap(); + + static final Map mayoOids = new HashMap(); + static final Map mayoParams = new HashMap(); static { @@ -325,57 +329,57 @@ class Utils rainbowOids.put(RainbowParameters.rainbowVcircumzenithal, BCObjectIdentifiers.rainbow_V_circumzenithal); rainbowOids.put(RainbowParameters.rainbowVcompressed, BCObjectIdentifiers.rainbow_V_compressed); - shldsaOids.put(SLHDSAParameters.sha2_128s, NISTObjectIdentifiers.id_slh_dsa_sha2_128s); - shldsaOids.put(SLHDSAParameters.sha2_128f, NISTObjectIdentifiers.id_slh_dsa_sha2_128f); - shldsaOids.put(SLHDSAParameters.sha2_192s, NISTObjectIdentifiers.id_slh_dsa_sha2_192s); - shldsaOids.put(SLHDSAParameters.sha2_192f, NISTObjectIdentifiers.id_slh_dsa_sha2_192f); - shldsaOids.put(SLHDSAParameters.sha2_256s, NISTObjectIdentifiers.id_slh_dsa_sha2_256s); - shldsaOids.put(SLHDSAParameters.sha2_256f, NISTObjectIdentifiers.id_slh_dsa_sha2_256f); - shldsaOids.put(SLHDSAParameters.shake_128s, NISTObjectIdentifiers.id_slh_dsa_shake_128s); - shldsaOids.put(SLHDSAParameters.shake_128f, NISTObjectIdentifiers.id_slh_dsa_shake_128f); - shldsaOids.put(SLHDSAParameters.shake_192s, NISTObjectIdentifiers.id_slh_dsa_shake_192s); - shldsaOids.put(SLHDSAParameters.shake_192f, NISTObjectIdentifiers.id_slh_dsa_shake_192f); - shldsaOids.put(SLHDSAParameters.shake_256s, NISTObjectIdentifiers.id_slh_dsa_shake_256s); - shldsaOids.put(SLHDSAParameters.shake_256f, NISTObjectIdentifiers.id_slh_dsa_shake_256f); - - shldsaOids.put(SLHDSAParameters.sha2_128s_with_sha256, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128s_with_sha256); - shldsaOids.put(SLHDSAParameters.sha2_128f_with_sha256, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128f_with_sha256); - shldsaOids.put(SLHDSAParameters.sha2_192s_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192s_with_sha512); - shldsaOids.put(SLHDSAParameters.sha2_192f_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192f_with_sha512); - shldsaOids.put(SLHDSAParameters.sha2_256s_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256s_with_sha512); - shldsaOids.put(SLHDSAParameters.sha2_256f_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256f_with_sha512); - shldsaOids.put(SLHDSAParameters.shake_128s_with_shake128, NISTObjectIdentifiers.id_hash_slh_dsa_shake_128s_with_shake128); - shldsaOids.put(SLHDSAParameters.shake_128f_with_shake128, NISTObjectIdentifiers.id_hash_slh_dsa_shake_128f_with_shake128); - shldsaOids.put(SLHDSAParameters.shake_192s_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_192s_with_shake256); - shldsaOids.put(SLHDSAParameters.shake_192f_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_192f_with_shake256); - shldsaOids.put(SLHDSAParameters.shake_256s_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_256s_with_shake256); - shldsaOids.put(SLHDSAParameters.shake_256f_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_256f_with_shake256); - - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_128s, SLHDSAParameters.sha2_128s); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_128f, SLHDSAParameters.sha2_128f); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_192s, SLHDSAParameters.sha2_192s); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_192f, SLHDSAParameters.sha2_192f); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_256s, SLHDSAParameters.sha2_256s); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_256f, SLHDSAParameters.sha2_256f); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_128s, SLHDSAParameters.shake_128s); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_128f, SLHDSAParameters.shake_128f); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_192s, SLHDSAParameters.shake_192s); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_192f, SLHDSAParameters.shake_192f); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_256s, SLHDSAParameters.shake_256s); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_256f, SLHDSAParameters.shake_256f); - - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128s_with_sha256, SLHDSAParameters.sha2_128s_with_sha256); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128f_with_sha256, SLHDSAParameters.sha2_128f_with_sha256); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192s_with_sha512, SLHDSAParameters.sha2_192s_with_sha512); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192f_with_sha512, SLHDSAParameters.sha2_192f_with_sha512); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256s_with_sha512, SLHDSAParameters.sha2_256s_with_sha512); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256f_with_sha512, SLHDSAParameters.sha2_256f_with_sha512); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_128s_with_shake128, SLHDSAParameters.shake_128s_with_shake128); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_128f_with_shake128, SLHDSAParameters.shake_128f_with_shake128); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_192s_with_shake256, SLHDSAParameters.shake_192s_with_shake256); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_192f_with_shake256, SLHDSAParameters.shake_192f_with_shake256); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_256s_with_shake256, SLHDSAParameters.shake_256s_with_shake256); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_256f_with_shake256, SLHDSAParameters.shake_256f_with_shake256); + slhdsaOids.put(SLHDSAParameters.sha2_128s, NISTObjectIdentifiers.id_slh_dsa_sha2_128s); + slhdsaOids.put(SLHDSAParameters.sha2_128f, NISTObjectIdentifiers.id_slh_dsa_sha2_128f); + slhdsaOids.put(SLHDSAParameters.sha2_192s, NISTObjectIdentifiers.id_slh_dsa_sha2_192s); + slhdsaOids.put(SLHDSAParameters.sha2_192f, NISTObjectIdentifiers.id_slh_dsa_sha2_192f); + slhdsaOids.put(SLHDSAParameters.sha2_256s, NISTObjectIdentifiers.id_slh_dsa_sha2_256s); + slhdsaOids.put(SLHDSAParameters.sha2_256f, NISTObjectIdentifiers.id_slh_dsa_sha2_256f); + slhdsaOids.put(SLHDSAParameters.shake_128s, NISTObjectIdentifiers.id_slh_dsa_shake_128s); + slhdsaOids.put(SLHDSAParameters.shake_128f, NISTObjectIdentifiers.id_slh_dsa_shake_128f); + slhdsaOids.put(SLHDSAParameters.shake_192s, NISTObjectIdentifiers.id_slh_dsa_shake_192s); + slhdsaOids.put(SLHDSAParameters.shake_192f, NISTObjectIdentifiers.id_slh_dsa_shake_192f); + slhdsaOids.put(SLHDSAParameters.shake_256s, NISTObjectIdentifiers.id_slh_dsa_shake_256s); + slhdsaOids.put(SLHDSAParameters.shake_256f, NISTObjectIdentifiers.id_slh_dsa_shake_256f); + + slhdsaOids.put(SLHDSAParameters.sha2_128s_with_sha256, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128s_with_sha256); + slhdsaOids.put(SLHDSAParameters.sha2_128f_with_sha256, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128f_with_sha256); + slhdsaOids.put(SLHDSAParameters.sha2_192s_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192s_with_sha512); + slhdsaOids.put(SLHDSAParameters.sha2_192f_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192f_with_sha512); + slhdsaOids.put(SLHDSAParameters.sha2_256s_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256s_with_sha512); + slhdsaOids.put(SLHDSAParameters.sha2_256f_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256f_with_sha512); + slhdsaOids.put(SLHDSAParameters.shake_128s_with_shake128, NISTObjectIdentifiers.id_hash_slh_dsa_shake_128s_with_shake128); + slhdsaOids.put(SLHDSAParameters.shake_128f_with_shake128, NISTObjectIdentifiers.id_hash_slh_dsa_shake_128f_with_shake128); + slhdsaOids.put(SLHDSAParameters.shake_192s_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_192s_with_shake256); + slhdsaOids.put(SLHDSAParameters.shake_192f_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_192f_with_shake256); + slhdsaOids.put(SLHDSAParameters.shake_256s_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_256s_with_shake256); + slhdsaOids.put(SLHDSAParameters.shake_256f_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_256f_with_shake256); + + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_128s, SLHDSAParameters.sha2_128s); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_128f, SLHDSAParameters.sha2_128f); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_192s, SLHDSAParameters.sha2_192s); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_192f, SLHDSAParameters.sha2_192f); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_256s, SLHDSAParameters.sha2_256s); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_256f, SLHDSAParameters.sha2_256f); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_128s, SLHDSAParameters.shake_128s); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_128f, SLHDSAParameters.shake_128f); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_192s, SLHDSAParameters.shake_192s); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_192f, SLHDSAParameters.shake_192f); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_256s, SLHDSAParameters.shake_256s); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_256f, SLHDSAParameters.shake_256f); + + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128s_with_sha256, SLHDSAParameters.sha2_128s_with_sha256); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128f_with_sha256, SLHDSAParameters.sha2_128f_with_sha256); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192s_with_sha512, SLHDSAParameters.sha2_192s_with_sha512); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192f_with_sha512, SLHDSAParameters.sha2_192f_with_sha512); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256s_with_sha512, SLHDSAParameters.sha2_256s_with_sha512); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256f_with_sha512, SLHDSAParameters.sha2_256f_with_sha512); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_128s_with_shake128, SLHDSAParameters.shake_128s_with_shake128); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_128f_with_shake128, SLHDSAParameters.shake_128f_with_shake128); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_192s_with_shake256, SLHDSAParameters.shake_192s_with_shake256); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_192f_with_shake256, SLHDSAParameters.shake_192f_with_shake256); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_256s_with_shake256, SLHDSAParameters.shake_256s_with_shake256); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_256f_with_shake256, SLHDSAParameters.shake_256f_with_shake256); sphincsPlusOids.put(SLHDSAParameters.sha2_128s, BCObjectIdentifiers.sphincsPlus_sha2_128s); sphincsPlusOids.put(SLHDSAParameters.sha2_128f, BCObjectIdentifiers.sphincsPlus_sha2_128f); @@ -476,16 +480,26 @@ class Utils sphincsPlusParams.put(BCObjectIdentifiers.sphincsPlus_shake_256f_r3_simple, SPHINCSPlusParameters.shake_256f); sphincsPlusParams.put(BCObjectIdentifiers.sphincsPlus_haraka_256s_r3_simple, SPHINCSPlusParameters.haraka_256s_simple); sphincsPlusParams.put(BCObjectIdentifiers.sphincsPlus_haraka_256f_r3_simple, SPHINCSPlusParameters.haraka_256f_simple); + + mayoOids.put(MayoParameters.mayo1, BCObjectIdentifiers.mayo1); + mayoOids.put(MayoParameters.mayo2, BCObjectIdentifiers.mayo2); + mayoOids.put(MayoParameters.mayo3, BCObjectIdentifiers.mayo3); + mayoOids.put(MayoParameters.mayo5, BCObjectIdentifiers.mayo5); + + mayoParams.put(BCObjectIdentifiers.mayo1, MayoParameters.mayo1); + mayoParams.put(BCObjectIdentifiers.mayo2, MayoParameters.mayo2); + mayoParams.put(BCObjectIdentifiers.mayo3, MayoParameters.mayo3); + mayoParams.put(BCObjectIdentifiers.mayo5, MayoParameters.mayo5); } static ASN1ObjectIdentifier slhdsaOidLookup(SLHDSAParameters params) { - return (ASN1ObjectIdentifier)shldsaOids.get(params); + return (ASN1ObjectIdentifier)slhdsaOids.get(params); } static SLHDSAParameters slhdsaParamsLookup(ASN1ObjectIdentifier oid) { - return (SLHDSAParameters)shldsaParams.get(oid); + return (SLHDSAParameters)slhdsaParams.get(oid); } static int qTeslaLookupSecurityCategory(AlgorithmIdentifier algorithm) @@ -788,6 +802,16 @@ static RainbowParameters rainbowParamsLookup(ASN1ObjectIdentifier oid) return (RainbowParameters)rainbowParams.get(oid); } + static ASN1ObjectIdentifier mayoOidLookup(MayoParameters params) + { + return (ASN1ObjectIdentifier)mayoOids.get(params); + } + + static MayoParameters mayoParamsLookup(ASN1ObjectIdentifier oid) + { + return (MayoParameters)mayoParams.get(oid); + } + private static boolean isRaw(byte[] data) { // check well-formed first diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java index e479221220..d68d04930f 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java @@ -1,9 +1,6 @@ package org.bouncycastle.pqc.crypto.test; -import java.io.IOException; import java.security.SecureRandom; -import java.util.HashMap; -import java.util.Map; import junit.framework.TestCase; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; @@ -14,8 +11,8 @@ import org.bouncycastle.pqc.crypto.mayo.MayoKeyGenerationParameters; import org.bouncycastle.pqc.crypto.mayo.MayoKeyPairGenerator; import org.bouncycastle.pqc.crypto.mayo.MayoParameters; -import org.bouncycastle.pqc.crypto.mayo.MayoPrivateKeyParameter; -import org.bouncycastle.pqc.crypto.mayo.MayoPublicKeyParameter; +import org.bouncycastle.pqc.crypto.mayo.MayoPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoPublicKeyParameters; import org.bouncycastle.pqc.crypto.mayo.MayoSigner; public class MayoTest @@ -31,10 +28,10 @@ public static void main(String[] args) private static final MayoParameters[] PARAMETER_SETS = new MayoParameters[] { - MayoParameters.MAYO1, - MayoParameters.MAYO2, - MayoParameters.MAYO3, - MayoParameters.MAYO5 + MayoParameters.mayo1, + MayoParameters.mayo2, + MayoParameters.mayo3, + MayoParameters.mayo5 }; private static final String[] files = new String[]{ @@ -70,13 +67,13 @@ public AsymmetricCipherKeyPairGenerator getAsymmetricCipherKeyPairGenerator(int @Override public byte[] getPublicKeyEncoded(AsymmetricKeyParameter pubParams) { - return ((MayoPublicKeyParameter)pubParams).getEncoded(); + return ((MayoPublicKeyParameters)pubParams).getEncoded(); } @Override public byte[] getPrivateKeyEncoded(CipherParameters privParams) { - return ((MayoPrivateKeyParameter)privParams).getEncoded(); + return ((MayoPrivateKeyParameters)privParams).getEncoded(); } @Override diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java index f26c929495..6af5a7297a 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java @@ -38,6 +38,7 @@ import org.bouncycastle.pqc.jcajce.provider.hqc.HQCKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.kyber.KyberKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.lms.LMSKeyFactorySpi; +import org.bouncycastle.pqc.jcajce.provider.mayo.MayoKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.newhope.NHKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.ntru.NTRUKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.picnic.PicnicKeyFactorySpi; @@ -437,6 +438,11 @@ private void loadPQCKeys() addKeyInfoConverter(BCObjectIdentifiers.ntruhps2048677, new NTRUKeyFactorySpi()); addKeyInfoConverter(BCObjectIdentifiers.ntruhps4096821, new NTRUKeyFactorySpi()); addKeyInfoConverter(BCObjectIdentifiers.ntruhrss701, new NTRUKeyFactorySpi()); + + addKeyInfoConverter(BCObjectIdentifiers.mayo1, new MayoKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.mayo2, new MayoKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.mayo3, new MayoKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.mayo5, new MayoKeyFactorySpi()); } public void setParameter(String parameterName, Object parameter) diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/MayoKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/MayoKey.java new file mode 100644 index 0000000000..efe8e2fb9d --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/MayoKey.java @@ -0,0 +1,16 @@ +package org.bouncycastle.pqc.jcajce.interfaces; + +import java.security.Key; + +import org.bouncycastle.pqc.jcajce.spec.MayoParameterSpec; + +public interface MayoKey + extends Key +{ + /** + * Return the parameters for this key. + * + * @return a MayoParameterSpec + */ + MayoParameterSpec getParameterSpec(); +} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java index b1ef6ccbed..b616736f5b 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java @@ -40,7 +40,8 @@ public class BouncyCastlePQCProvider //"Rainbow", "McEliece", "SPHINCS", "LMS", "NH", "XMSS", "SPHINCSPlus", "CMCE", "Frodo", "SABER", "Picnic", "NTRU", "Falcon", "Kyber", - "Dilithium", "NTRUPrime", "BIKE", "HQC", "Rainbow" + "Dilithium", "NTRUPrime", "BIKE", "HQC", "Rainbow", + "Mayo" }; /** diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Mayo.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Mayo.java new file mode 100644 index 0000000000..ae8669d748 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Mayo.java @@ -0,0 +1,44 @@ +package org.bouncycastle.pqc.jcajce.provider; + +import org.bouncycastle.asn1.bc.BCObjectIdentifiers; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; +import org.bouncycastle.pqc.jcajce.provider.mayo.MayoKeyFactorySpi; + +public class Mayo +{ + private static final String PREFIX = "org.bouncycastle.pqc.jcajce.provider.mayo."; + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("KeyFactory.Mayo", PREFIX + "MayoKeyFactorySpi"); + + addKeyFactoryAlgorithm(provider, "MAYO_1", PREFIX + "MayoKeyFactorySpi$Mayo1", BCObjectIdentifiers.mayo1, new MayoKeyFactorySpi.Mayo1()); + addKeyFactoryAlgorithm(provider, "MAYO_2", PREFIX + "MayoKeyFactorySpi$Mayo2", BCObjectIdentifiers.mayo2, new MayoKeyFactorySpi.Mayo2()); + addKeyFactoryAlgorithm(provider, "MAYO_3", PREFIX + "MayoKeyFactorySpi$Mayo3", BCObjectIdentifiers.mayo3, new MayoKeyFactorySpi.Mayo3()); + addKeyFactoryAlgorithm(provider, "MAYO_5", PREFIX + "MayoKeyFactorySpi$Mayo5", BCObjectIdentifiers.mayo5, new MayoKeyFactorySpi.Mayo5()); + + provider.addAlgorithm("KeyPairGenerator.Mayo", PREFIX + "MayoKeyPairGeneratorSpi"); + + addKeyPairGeneratorAlgorithm(provider, "MAYO_1", PREFIX + "MayoKeyPairGeneratorSpi$Mayo1", BCObjectIdentifiers.mayo1); + addKeyPairGeneratorAlgorithm(provider, "MAYO_2", PREFIX + "MayoKeyPairGeneratorSpi$Mayo2", BCObjectIdentifiers.mayo2); + addKeyPairGeneratorAlgorithm(provider, "MAYO_3", PREFIX + "MayoKeyPairGeneratorSpi$Mayo3", BCObjectIdentifiers.mayo3); + addKeyPairGeneratorAlgorithm(provider, "MAYO_5", PREFIX + "MayoKeyPairGeneratorSpi$Mayo5", BCObjectIdentifiers.mayo5); + + addSignatureAlgorithm(provider, "Mayo", PREFIX + "SignatureSpi$Base", BCObjectIdentifiers.mayo); + + addSignatureAlgorithm(provider, "MAYO_1", PREFIX + "SignatureSpi$Mayo1", BCObjectIdentifiers.mayo1); + addSignatureAlgorithm(provider, "MAYO_2", PREFIX + "SignatureSpi$Mayo2", BCObjectIdentifiers.mayo2); + addSignatureAlgorithm(provider, "MAYO_3", PREFIX + "SignatureSpi$Mayo3", BCObjectIdentifiers.mayo3); + addSignatureAlgorithm(provider, "MAYO_5", PREFIX + "SignatureSpi$Mayo5", BCObjectIdentifiers.mayo5); + } + } +} + diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/cmce/BCCMCEPrivateKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/cmce/BCCMCEPrivateKey.java index e810a83003..a5868d94ce 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/cmce/BCCMCEPrivateKey.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/cmce/BCCMCEPrivateKey.java @@ -43,7 +43,7 @@ private void init(PrivateKeyInfo keyInfo) } /** - * Compare this SPHINCS-256 private key with another object. + * Compare this CMCE private key with another object. * * @param o the other object * @return the result of the comparison diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/cmce/BCCMCEPublicKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/cmce/BCCMCEPublicKey.java index 386b92a975..0d17b3793d 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/cmce/BCCMCEPublicKey.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/cmce/BCCMCEPublicKey.java @@ -40,7 +40,7 @@ private void init(SubjectPublicKeyInfo keyInfo) } /** - * Compare this SPHINCS-256 public key with another object. + * Compare this CMCE public key with another object. * * @param o the other object * @return the result of the comparison diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/falcon/FalconKeyFactorySpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/falcon/FalconKeyFactorySpi.java index d4f36890a5..58adc178c5 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/falcon/FalconKeyFactorySpi.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/falcon/FalconKeyFactorySpi.java @@ -17,7 +17,6 @@ import org.bouncycastle.asn1.bc.BCObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; import org.bouncycastle.pqc.jcajce.provider.util.BaseKeyFactorySpi; public class FalconKeyFactorySpi diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/BCMayoPrivateKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/BCMayoPrivateKey.java new file mode 100644 index 0000000000..043fee5635 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/BCMayoPrivateKey.java @@ -0,0 +1,131 @@ +package org.bouncycastle.pqc.jcajce.provider.mayo; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.security.PrivateKey; + +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.pqc.crypto.mayo.MayoPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.util.PrivateKeyFactory; +import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory; +import org.bouncycastle.pqc.jcajce.interfaces.MayoKey; +import org.bouncycastle.pqc.jcajce.spec.MayoParameterSpec; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; + +public class BCMayoPrivateKey + implements PrivateKey, MayoKey +{ + private static final long serialVersionUID = 1L; + + private transient MayoPrivateKeyParameters params; + private transient ASN1Set attributes; + + public BCMayoPrivateKey( + MayoPrivateKeyParameters params) + { + this.params = params; + } + + public BCMayoPrivateKey(PrivateKeyInfo keyInfo) + throws IOException + { + init(keyInfo); + } + + private void init(PrivateKeyInfo keyInfo) + throws IOException + { + this.attributes = keyInfo.getAttributes(); + this.params = (MayoPrivateKeyParameters) PrivateKeyFactory.createKey(keyInfo); + } + + /** + * Compare this private key with another object. + * + * @param o the other object + * @return the result of the comparison + */ + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (o instanceof BCMayoPrivateKey) + { + BCMayoPrivateKey otherKey = (BCMayoPrivateKey)o; + + return Arrays.areEqual(params.getEncoded(), otherKey.params.getEncoded()); + } + + return false; + } + + public int hashCode() + { + return Arrays.hashCode(params.getEncoded()); + } + + /** + * @return name of the algorithm - "Mayo[1|2|3|5]" + */ + public final String getAlgorithm() + { + return Strings.toUpperCase(params.getParameters().getName()); + } + + public byte[] getEncoded() + { + + try + { + PrivateKeyInfo pki = PrivateKeyInfoFactory.createPrivateKeyInfo(params, attributes); + + return pki.getEncoded(); + } + catch (IOException e) + { + return null; + } + } + + public MayoParameterSpec getParameterSpec() + { + return MayoParameterSpec.fromName(params.getParameters().getName()); + } + + public String getFormat() + { + return "PKCS#8"; + } + + MayoPrivateKeyParameters getKeyParams() + { + return params; + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + init(PrivateKeyInfo.getInstance(enc)); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } +} + diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/BCMayoPublicKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/BCMayoPublicKey.java new file mode 100644 index 0000000000..30d861d3b2 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/BCMayoPublicKey.java @@ -0,0 +1,127 @@ +package org.bouncycastle.pqc.jcajce.provider.mayo; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.security.PublicKey; + +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.pqc.crypto.mayo.MayoPublicKeyParameters; +import org.bouncycastle.pqc.crypto.util.PublicKeyFactory; +import org.bouncycastle.pqc.crypto.util.SubjectPublicKeyInfoFactory; +import org.bouncycastle.pqc.jcajce.interfaces.MayoKey; +import org.bouncycastle.pqc.jcajce.spec.MayoParameterSpec; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; + +public class BCMayoPublicKey + implements PublicKey, MayoKey +{ + private static final long serialVersionUID = 1L; + + private transient MayoPublicKeyParameters params; + + public BCMayoPublicKey( + MayoPublicKeyParameters params) + { + this.params = params; + } + + public BCMayoPublicKey(SubjectPublicKeyInfo keyInfo) + throws IOException + { + init(keyInfo); + } + + private void init(SubjectPublicKeyInfo keyInfo) + throws IOException + { + this.params = (MayoPublicKeyParameters) PublicKeyFactory.createKey(keyInfo); + } + + /** + * Compare this BIKE public key with another object. + * + * @param o the other object + * @return the result of the comparison + */ + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (o instanceof BCMayoPublicKey) + { + BCMayoPublicKey otherKey = (BCMayoPublicKey)o; + + return Arrays.areEqual(params.getEncoded(), otherKey.params.getEncoded()); + } + + return false; + } + + public int hashCode() + { + return Arrays.hashCode(params.getEncoded()); + } + + /** + * @return name of the algorithm - "BIKE" + */ + public final String getAlgorithm() + { + return Strings.toUpperCase(params.getParameters().getName()); + } + + public byte[] getEncoded() + { + try + { + SubjectPublicKeyInfo pki = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(params); + + return pki.getEncoded(); + } + catch (IOException e) + { + return null; + } + } + + public String getFormat() + { + return "X.509"; + } + + public MayoParameterSpec getParameterSpec() + { + return MayoParameterSpec.fromName(params.getParameters().getName()); + } + + MayoPublicKeyParameters getKeyParams() + { + return params; + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + init(SubjectPublicKeyInfo.getInstance(enc)); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } +} + diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/MayoKeyFactorySpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/MayoKeyFactorySpi.java new file mode 100644 index 0000000000..f27d25e86d --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/MayoKeyFactorySpi.java @@ -0,0 +1,130 @@ +package org.bouncycastle.pqc.jcajce.provider.mayo; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashSet; +import java.util.Set; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.bc.BCObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.pqc.jcajce.provider.util.BaseKeyFactorySpi; + +public class MayoKeyFactorySpi + extends BaseKeyFactorySpi +{ + private static final Set keyOids = new HashSet(); + + static + { + keyOids.add(BCObjectIdentifiers.mayo1); + keyOids.add(BCObjectIdentifiers.mayo2); + keyOids.add(BCObjectIdentifiers.mayo3); + keyOids.add(BCObjectIdentifiers.mayo5); + } + + public MayoKeyFactorySpi() + { + super(keyOids); + } + + public MayoKeyFactorySpi(ASN1ObjectIdentifier keyOid) + { + super(keyOid); + } + + public final KeySpec engineGetKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException + { + if (key instanceof BCMayoPrivateKey) + { + if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) + { + return new PKCS8EncodedKeySpec(key.getEncoded()); + } + } + else if (key instanceof BCMayoPublicKey) + { + if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) + { + return new X509EncodedKeySpec(key.getEncoded()); + } + } + else + { + throw new InvalidKeySpecException("Unsupported key type: " + + key.getClass() + "."); + } + + throw new InvalidKeySpecException("Unknown key specification: " + + keySpec + "."); + } + + public final Key engineTranslateKey(Key key) + throws InvalidKeyException + { + if (key instanceof BCMayoPrivateKey || key instanceof BCMayoPublicKey) + { + return key; + } + + throw new InvalidKeyException("Unsupported key type"); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + return new BCMayoPrivateKey(keyInfo); + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + return new BCMayoPublicKey(keyInfo); + } + + public static class Mayo1 + extends MayoKeyFactorySpi + { + public Mayo1() + { + super(BCObjectIdentifiers.mayo1); + } + } + + public static class Mayo2 + extends MayoKeyFactorySpi + { + public Mayo2() + { + super(BCObjectIdentifiers.mayo2); + } + } + + public static class Mayo3 + extends MayoKeyFactorySpi + { + public Mayo3() + { + super(BCObjectIdentifiers.mayo3); + } + } + + public static class Mayo5 + extends MayoKeyFactorySpi + { + public Mayo5() + { + super(BCObjectIdentifiers.mayo5); + } + } +} + diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/MayoKeyPairGeneratorSpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/MayoKeyPairGeneratorSpi.java new file mode 100644 index 0000000000..9f0eded803 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/MayoKeyPairGeneratorSpi.java @@ -0,0 +1,150 @@ +package org.bouncycastle.pqc.jcajce.provider.mayo; + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.util.HashMap; +import java.util.Map; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.pqc.crypto.falcon.FalconParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoKeyGenerationParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoKeyPairGenerator; +import org.bouncycastle.pqc.crypto.mayo.MayoParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoPublicKeyParameters; +import org.bouncycastle.pqc.jcajce.provider.falcon.FalconKeyPairGeneratorSpi; +import org.bouncycastle.pqc.jcajce.provider.util.SpecUtil; +import org.bouncycastle.pqc.jcajce.spec.MayoParameterSpec; +import org.bouncycastle.util.Strings; + +public class MayoKeyPairGeneratorSpi + extends java.security.KeyPairGenerator +{ + private static Map parameters = new HashMap(); + + static + { + parameters.put("MAYO_1", MayoParameters.mayo1); + parameters.put("MAYO_2", MayoParameters.mayo2); + parameters.put("MAYO_3", MayoParameters.mayo3); + parameters.put("MAYO_5", MayoParameters.mayo5); + parameters.put(MayoParameterSpec.mayo1.getName(), MayoParameters.mayo1); + parameters.put(MayoParameterSpec.mayo2.getName(), MayoParameters.mayo2); + parameters.put(MayoParameterSpec.mayo3.getName(), MayoParameters.mayo3); + parameters.put(MayoParameterSpec.mayo5.getName(), MayoParameters.mayo5); + } + + MayoKeyGenerationParameters param; + private MayoParameters mayoParameters; + MayoKeyPairGenerator engine = new MayoKeyPairGenerator(); + + SecureRandom random = CryptoServicesRegistrar.getSecureRandom(); + boolean initialised = false; + + public MayoKeyPairGeneratorSpi() + { + super("Mayo"); + } + + protected MayoKeyPairGeneratorSpi(MayoParameters mayoParameters) + { + super(mayoParameters.getName()); + this.mayoParameters = mayoParameters; + } + + public void initialize( + int strength, + SecureRandom random) + { + throw new IllegalArgumentException("use AlgorithmParameterSpec"); + } + + public void initialize( + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + String name = getNameFromParams(params); + + if (name != null) + { + param = new MayoKeyGenerationParameters(random, (MayoParameters)parameters.get(name)); + + engine.init(param); + initialised = true; + } + else + { + throw new InvalidAlgorithmParameterException("invalid ParameterSpec: " + params); + } + } + + private static String getNameFromParams(AlgorithmParameterSpec paramSpec) + { + if (paramSpec instanceof MayoParameterSpec) + { + MayoParameterSpec MayoParams = (MayoParameterSpec)paramSpec; + return MayoParams.getName(); + } + else + { + return Strings.toLowerCase(SpecUtil.getNameFrom(paramSpec)); + } + } + + public KeyPair generateKeyPair() + { + if (!initialised) + { + param = new MayoKeyGenerationParameters(random, MayoParameters.mayo1); + + engine.init(param); + initialised = true; + } + + AsymmetricCipherKeyPair pair = engine.generateKeyPair(); + MayoPublicKeyParameters pub = (MayoPublicKeyParameters)pair.getPublic(); + MayoPrivateKeyParameters priv = (MayoPrivateKeyParameters)pair.getPrivate(); + + return new KeyPair(new BCMayoPublicKey(pub), new BCMayoPrivateKey(priv)); + } + + public static class Mayo1 + extends MayoKeyPairGeneratorSpi + { + public Mayo1() + { + super(MayoParameters.mayo1); + } + } + + public static class Mayo2 + extends MayoKeyPairGeneratorSpi + { + public Mayo2() + { + super(MayoParameters.mayo2); + } + } + + public static class Mayo3 + extends MayoKeyPairGeneratorSpi + { + public Mayo3() + { + super(MayoParameters.mayo3); + } + } + + public static class Mayo5 + extends MayoKeyPairGeneratorSpi + { + public Mayo5() + { + super(MayoParameters.mayo5); + } + } +} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/SignatureSpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/SignatureSpi.java new file mode 100644 index 0000000000..dd791cba9a --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/SignatureSpi.java @@ -0,0 +1,218 @@ +package org.bouncycastle.pqc.jcajce.provider.mayo; + +import java.io.ByteArrayOutputStream; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.SignatureException; +import java.security.spec.AlgorithmParameterSpec; + +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.pqc.crypto.mayo.MayoParameters; +import org.bouncycastle.pqc.crypto.mayo.MayoSigner; +import org.bouncycastle.util.Strings; + +public class SignatureSpi + extends java.security.Signature +{ + private final ByteArrayOutputStream bOut; + private final MayoSigner signer; + private SecureRandom random; + private final MayoParameters parameters; + + protected SignatureSpi(MayoSigner signer) + { + super("Mayo"); + + this.bOut = new ByteArrayOutputStream(); + this.signer = signer; + this.parameters = null; + } + + protected SignatureSpi(MayoSigner signer, MayoParameters parameters) + { + super(Strings.toUpperCase(parameters.getName())); + this.parameters = parameters; + + this.bOut = new ByteArrayOutputStream(); + this.signer = signer; + } + + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException + { + if (!(publicKey instanceof BCMayoPublicKey)) + { + try + { + publicKey = new BCMayoPublicKey(SubjectPublicKeyInfo.getInstance(publicKey.getEncoded())); + } + catch (Exception e) + { + throw new InvalidKeyException("unknown public key passed to Mayo: " + e.getMessage(), e); + } + } + + BCMayoPublicKey key = (BCMayoPublicKey)publicKey; + + if (parameters != null) + { + String canonicalAlg = Strings.toUpperCase(parameters.getName()); + if (!canonicalAlg.equals(key.getAlgorithm())) + { + throw new InvalidKeyException("signature configured for " + canonicalAlg); + } + } + + signer.init(false, key.getKeyParams()); + } + + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException + { + this.random = random; + engineInitSign(privateKey); + } + + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException + { + if (privateKey instanceof BCMayoPrivateKey) + { + BCMayoPrivateKey key = (BCMayoPrivateKey)privateKey; + CipherParameters param = key.getKeyParams(); + + if (parameters != null) + { + String canonicalAlg = Strings.toUpperCase(parameters.getName()); + if (!canonicalAlg.equals(key.getAlgorithm())) + { + throw new InvalidKeyException("signature configured for " + canonicalAlg); + } + } + + if (random != null) + { + signer.init(true, new ParametersWithRandom(param, random)); + } + else + { + signer.init(true, param); + } + } + else + { + throw new InvalidKeyException("unknown private key passed to Mayo"); + } + } + + protected void engineUpdate(byte b) + throws SignatureException + { + bOut.write(b); + } + + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException + { + bOut.write(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + try + { + byte[] message = bOut.toByteArray(); + + bOut.reset(); + + return signer.generateSignature(message); + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify(byte[] sigBytes) + throws SignatureException + { + byte[] message = bOut.toByteArray(); + + bOut.reset(); + + return signer.verifySignature(message, sigBytes); + } + + protected void engineSetParameter(AlgorithmParameterSpec params) + { + // TODO + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec) + */ + protected void engineSetParameter(String param, Object value) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated + */ + protected Object engineGetParameter(String param) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + public static class Base + extends org.bouncycastle.pqc.jcajce.provider.mayo.SignatureSpi + { + public Base() + { + super(new MayoSigner()); + } + } + + public static class Mayo1 + extends org.bouncycastle.pqc.jcajce.provider.mayo.SignatureSpi + { + public Mayo1() + { + super(new MayoSigner(), MayoParameters.mayo1); + } + } + + public static class Mayo2 + extends org.bouncycastle.pqc.jcajce.provider.mayo.SignatureSpi + { + public Mayo2() + { + super(new MayoSigner(), MayoParameters.mayo2); + } + } + + public static class Mayo3 + extends org.bouncycastle.pqc.jcajce.provider.mayo.SignatureSpi + { + public Mayo3() + { + super(new MayoSigner(), MayoParameters.mayo3); + } + } + + public static class Mayo5 + extends org.bouncycastle.pqc.jcajce.provider.mayo.SignatureSpi + { + public Mayo5() + { + super(new MayoSigner(), MayoParameters.mayo5); + } + } +} + diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/spec/MayoParameterSpec.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/spec/MayoParameterSpec.java new file mode 100644 index 0000000000..9f3ee3e061 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/spec/MayoParameterSpec.java @@ -0,0 +1,48 @@ +package org.bouncycastle.pqc.jcajce.spec; + +import java.security.spec.AlgorithmParameterSpec; +import java.util.HashMap; +import java.util.Map; + +import org.bouncycastle.pqc.crypto.mayo.MayoParameters; +import org.bouncycastle.util.Strings; + +public class MayoParameterSpec + implements AlgorithmParameterSpec +{ + public static final MayoParameterSpec mayo1 = new MayoParameterSpec(MayoParameters.mayo1); + public static final MayoParameterSpec mayo2 = new MayoParameterSpec(MayoParameters.mayo2); + public static final MayoParameterSpec mayo3 = new MayoParameterSpec(MayoParameters.mayo3); + public static final MayoParameterSpec mayo5 = new MayoParameterSpec(MayoParameters.mayo5); + + private static Map parameters = new HashMap(); + + static + { +// parameters.put("mayo1", mayo1); +// parameters.put("mayo2", mayo2); +// parameters.put("mayo3", mayo3); +// parameters.put("mayo5", mayo5); + parameters.put("MAYO_1", mayo1); + parameters.put("MAYO_2", mayo2); + parameters.put("MAYO_3", mayo3); + parameters.put("MAYO_5", mayo5); + } + + private final String name; + + private MayoParameterSpec(MayoParameters parameters) + { + this.name = parameters.getName(); + } + + public String getName() + { + return name; + } + + public static MayoParameterSpec fromName(String name) + { + return (MayoParameterSpec)parameters.get(Strings.toLowerCase(name)); + } +} diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MayoKeyPairGeneratorTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MayoKeyPairGeneratorTest.java new file mode 100644 index 0000000000..454722219c --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MayoKeyPairGeneratorTest.java @@ -0,0 +1,63 @@ +package org.bouncycastle.pqc.jcajce.provider.test; + +import java.security.KeyFactory; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.pqc.jcajce.spec.MayoParameterSpec; + +public class MayoKeyPairGeneratorTest + extends KeyPairGeneratorTest +{ + public static void main(String[] args) + throws Exception + { + MayoKeyPairGeneratorTest test = new MayoKeyPairGeneratorTest(); + test.setUp(); + test.testKeyFactory(); + test.testKeyPairEncoding(); + } + + protected void setUp() + { + super.setUp(); + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) + { + Security.addProvider(new BouncyCastleProvider()); + } + } + + public void testKeyFactory() + throws Exception + { + kf = KeyFactory.getInstance("Mayo", "BCPQC"); + KeyFactory kf1 = KeyFactory.getInstance("MAYO_1", "BCPQC"); + KeyFactory kf2 = KeyFactory.getInstance("MAYO_2", "BCPQC"); + KeyFactory kf3 = KeyFactory.getInstance("MAYO_3", "BCPQC"); + KeyFactory kf5 = KeyFactory.getInstance("MAYO_5", "BCPQC"); + } + + public void testKeyPairEncoding() + throws Exception + { + MayoParameterSpec[] specs = + new MayoParameterSpec[] + { + MayoParameterSpec.mayo1, + MayoParameterSpec.mayo2, + MayoParameterSpec.mayo3, + MayoParameterSpec.mayo5 + }; + kf = KeyFactory.getInstance("Mayo", "BCPQC"); + + kpg = KeyPairGenerator.getInstance("Mayo", "BCPQC"); + + for (int i = 0; i != specs.length; i++) + { + kpg.initialize(specs[i], new SecureRandom()); + performKeyPairEncodingTest(specs[i].getName(), kpg.generateKeyPair()); + } + } +} diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MayoTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MayoTest.java new file mode 100644 index 0000000000..58d433f0c3 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MayoTest.java @@ -0,0 +1,276 @@ +package org.bouncycastle.pqc.jcajce.provider.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; +import java.security.Signature; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import junit.framework.TestCase; +import org.bouncycastle.pqc.jcajce.interfaces.MayoKey; +import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider; +import org.bouncycastle.pqc.jcajce.spec.MayoParameterSpec; +import org.bouncycastle.util.Strings; + +public class MayoTest + extends TestCase +{ + public static void main(String[] args) + throws Exception + { + MayoTest test = new MayoTest(); + test.setUp(); + test.testMayo3(); + test.testMayo5(); + test.testMayoRandomSig(); + test.testPrivateKeyRecovery(); + test.testPublicKeyRecovery(); + test.testRestrictedKeyPairGen(); + } + + byte[] msg = Strings.toByteArray("Hello World!"); + + public void setUp() + { + if (Security.getProvider(BouncyCastlePQCProvider.PROVIDER_NAME) == null) + { + Security.addProvider(new BouncyCastlePQCProvider()); + } + } + + public void testPrivateKeyRecovery() + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("Mayo", "BCPQC"); + + kpg.initialize(MayoParameterSpec.mayo1, new RiggedRandom()); + + KeyPair kp = kpg.generateKeyPair(); + + KeyFactory kFact = KeyFactory.getInstance("Mayo", "BCPQC"); + + MayoKey privKey = (MayoKey)kFact.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded())); + + assertEquals(kp.getPrivate(), privKey); + assertEquals(kp.getPrivate().getAlgorithm(), privKey.getAlgorithm()); + assertEquals(kp.getPrivate().hashCode(), privKey.hashCode()); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ObjectOutputStream oOut = new ObjectOutputStream(bOut); + + oOut.writeObject(privKey); + + oOut.close(); + + ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray())); + + MayoKey privKey2 = (MayoKey)oIn.readObject(); + + assertEquals(privKey, privKey2); + assertEquals(privKey.getAlgorithm(), privKey2.getAlgorithm()); + assertEquals(privKey.hashCode(), privKey2.hashCode()); + } + + public void testPublicKeyRecovery() + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("Mayo", "BCPQC"); + + kpg.initialize(MayoParameterSpec.mayo2, new MayoTest.RiggedRandom()); + + KeyPair kp = kpg.generateKeyPair(); + + KeyFactory kFact = KeyFactory.getInstance("Mayo_2", "BCPQC"); + + MayoKey pubKey = (MayoKey)kFact.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded())); + + assertEquals(kp.getPublic(), pubKey); + assertEquals(kp.getPublic().getAlgorithm(), pubKey.getAlgorithm()); + assertEquals(kp.getPublic().hashCode(), pubKey.hashCode()); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ObjectOutputStream oOut = new ObjectOutputStream(bOut); + + oOut.writeObject(pubKey); + + oOut.close(); + + ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray())); + + MayoKey pubKey2 = (MayoKey)oIn.readObject(); + + assertEquals(pubKey, pubKey2); + assertEquals(pubKey.getAlgorithm(), pubKey2.getAlgorithm()); + assertEquals(pubKey.hashCode(), pubKey2.hashCode()); + } + + public void testMayo5() + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("Mayo", "BCPQC"); + + kpg.initialize(MayoParameterSpec.mayo5, new SecureRandom()); + + KeyPair kp = kpg.generateKeyPair(); + + Signature sig = Signature.getInstance("MAYO_5", "BCPQC"); + + sig.initSign(kp.getPrivate(), new SecureRandom()); + + sig.update(msg, 0, msg.length); + + byte[] s = sig.sign(); + + sig = Signature.getInstance("MAYO_5", "BCPQC"); + + assertEquals("MAYO_5", Strings.toUpperCase(sig.getAlgorithm())); + + sig.initVerify(kp.getPublic()); + + sig.update(msg, 0, msg.length); + + assertTrue(sig.verify(s)); + + kpg = KeyPairGenerator.getInstance("Mayo", "BCPQC"); + + kpg.initialize(MayoParameterSpec.mayo1, new SecureRandom()); + + kp = kpg.generateKeyPair(); + + try + { + sig.initVerify(kp.getPublic()); + fail("no exception"); + } + catch (InvalidKeyException e) + { + assertEquals("signature configured for MAYO_5", e.getMessage()); + } + } + + public void testMayo3() + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("Mayo", "BCPQC"); + + kpg.initialize(MayoParameterSpec.mayo3, new SecureRandom()); + + KeyPair kp = kpg.generateKeyPair(); + + Signature sig = Signature.getInstance("MAYO_3", "BCPQC"); + + sig.initSign(kp.getPrivate(), new SecureRandom()); + + sig.update(msg, 0, msg.length); + + byte[] s = sig.sign(); + + sig = Signature.getInstance("MAYO_3", "BCPQC"); + + assertEquals("MAYO_3", Strings.toUpperCase(sig.getAlgorithm())); + + sig.initVerify(kp.getPublic()); + + sig.update(msg, 0, msg.length); + + assertTrue(sig.verify(s)); + + kpg = KeyPairGenerator.getInstance("Mayo", "BCPQC"); + + kpg.initialize(MayoParameterSpec.mayo5, new SecureRandom()); + + kp = kpg.generateKeyPair(); + + try + { + sig.initVerify(kp.getPublic()); + fail("no exception"); + } + catch (InvalidKeyException e) + { + assertEquals("signature configured for MAYO_3", e.getMessage()); + } + } + + public void testRestrictedKeyPairGen() + throws Exception + { + doTestRestrictedKeyPairGen(MayoParameterSpec.mayo1); + doTestRestrictedKeyPairGen(MayoParameterSpec.mayo2); + doTestRestrictedKeyPairGen(MayoParameterSpec.mayo3); + doTestRestrictedKeyPairGen(MayoParameterSpec.mayo5); + } + + private void doTestRestrictedKeyPairGen(MayoParameterSpec spec) + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance(spec.getName(), "BCPQC"); + + kpg.initialize(spec, new SecureRandom()); + + KeyPair kp = kpg.generateKeyPair(); + + assertEquals(spec.getName(), kp.getPublic().getAlgorithm()); + assertEquals(spec.getName(), kp.getPrivate().getAlgorithm()); + + //kpg = KeyPairGenerator.getInstance(spec.getName(), "BCPQC"); + +// try +// { +// kpg.initialize(altSpec, new SecureRandom()); +// fail("no exception"); +// } +// catch (InvalidAlgorithmParameterException e) +// { +// assertEquals("key pair generator locked to " + spec.getName(), e.getMessage()); +// } + } + + public void testMayoRandomSig() + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("Mayo", "BCPQC"); + + kpg.initialize(MayoParameterSpec.mayo2, new SecureRandom()); + + KeyPair kp = kpg.generateKeyPair(); + + Signature sig = Signature.getInstance("Mayo", "BCPQC"); + + sig.initSign(kp.getPrivate(), new SecureRandom()); + + sig.update(msg, 0, msg.length); + + byte[] s = sig.sign(); + + sig = Signature.getInstance("Mayo", "BCPQC"); + + sig.initVerify(kp.getPublic()); + + sig.update(msg, 0, msg.length); + + assertTrue(sig.verify(s)); + } + + private static class RiggedRandom + extends SecureRandom + { + public void nextBytes(byte[] bytes) + { + for (int i = 0; i != bytes.length; i++) + { + bytes[i] = (byte)(i & 0xff); + } + } + } + +} + From 76a597b2c768c6d6ba125fdc1fa41ad9e0762c82 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 4 Mar 2025 13:11:32 +1030 Subject: [PATCH 145/890] Add Mayo to PQC Provider --- .../org/bouncycastle/pqc/jcajce/provider/test/AllTests.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java index 693dae7730..a01a130495 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java @@ -77,6 +77,8 @@ public static Test suite() suite.addTestSuite(HQCTest.class); suite.addTestSuite(RainbowKeyPairGeneratorTest.class); suite.addTestSuite(RainbowTest.class); + suite.addTestSuite(MayoKeyPairGeneratorTest.class); + suite.addTestSuite(MayoTest.class); return new BCTestSetup(suite); } From 025f99d1ad942e895b98b5589073fafb1771b27d Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 4 Mar 2025 14:40:35 +1030 Subject: [PATCH 146/890] Refactor of Mayo --- .../pqc/crypto/mayo/GF16Utils.java | 105 +++++------- .../pqc/crypto/mayo/MayoKeyPairGenerator.java | 2 +- .../pqc/crypto/mayo/MayoSigner.java | 149 +++++++++--------- .../java/org/bouncycastle/util/Bytes.java | 8 + 4 files changed, 119 insertions(+), 145 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java index 6595d412eb..cc2db8ba1d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java @@ -1,6 +1,6 @@ package org.bouncycastle.pqc.crypto.mayo; -public class GF16Utils +class GF16Utils { static final long NIBBLE_MASK_MSB = 0x7777777777777777L; static final long MASK_MSB = 0x8888888888888888L; @@ -20,7 +20,7 @@ public class GF16Utils * @param acc the accumulator long array; the target vector starts at index accOffset * @param accOffset the starting index in 'acc' */ - public static void mVecMulAdd(int mVecLimbs, long[] in, int inOffset, int b, long[] acc, int accOffset) + static void mVecMulAdd(int mVecLimbs, long[] in, int inOffset, int b, long[] acc, int accOffset) { long a, r64, a_msb, a_msb3; long b32 = b & 0x00000000FFFFFFFFL; @@ -67,18 +67,17 @@ public static void mVecMulAdd(int mVecLimbs, long[] in, int inOffset, int b, lon * @param acc the accumulator (as a flat long[] array) with dimensions (bsMatRows x matCols); * each “entry” is an m‐vector (length mVecLimbs). * @param bsMatRows number of rows in the bsMat (the “triangular” matrix’s row count). - * @param bsMatCols number of columns in bsMat. * @param matCols number of columns in the matrix “mat.” */ - public static void mulAddMUpperTriangularMatXMat(int mVecLimbs, long[] bsMat, byte[] mat, long[] acc, int accOff, - int bsMatRows, int bsMatCols, int matCols) + static void mulAddMUpperTriangularMatXMat(int mVecLimbs, long[] bsMat, byte[] mat, long[] acc, int accOff, + int bsMatRows, int matCols) { int bsMatEntriesUsed = 0; int matColsmVecLimbs = matCols * mVecLimbs; for (int r = 0, rmatCols = 0, rmatColsmVecLimbs = 0; r < bsMatRows; r++, rmatCols += matCols, rmatColsmVecLimbs += matColsmVecLimbs) { // For each row r, the inner loop goes from column triangular*r to bsMatCols-1. - for (int c = r, cmatCols = rmatCols; c < bsMatCols; c++, cmatCols += matCols) + for (int c = r, cmatCols = rmatCols; c < bsMatRows; c++, cmatCols += matCols) { for (int k = 0, kmVecLimbs = 0; k < matCols; k++, kmVecLimbs += mVecLimbs) { @@ -103,8 +102,8 @@ public static void mulAddMUpperTriangularMatXMat(int mVecLimbs, long[] bsMat, by * @param matCols number of columns in “mat.” * @param bsMatCols number of columns in the bsMat matrix. */ - public static void mulAddMatTransXMMat(int mVecLimbs, byte[] mat, long[] bsMat, int bsMatOff, long[] acc, - int matRows, int matCols, int bsMatCols) + static void mulAddMatTransXMMat(int mVecLimbs, byte[] mat, long[] bsMat, int bsMatOff, long[] acc, + int matRows, int matCols, int bsMatCols) { // Loop over each column r of mat (which becomes row of mat^T) for (int r = 0; r < matCols; r++) @@ -141,8 +140,8 @@ public static void mulAddMatTransXMMat(int mVecLimbs, byte[] mat, long[] bsMat, * @param matCols the number of columns in the matrix * @param bsMatCols the number of columns in the bit‐sliced matrix (per block) */ - public static void mulAddMatXMMat(int mVecLimbs, byte[] mat, long[] bsMat, long[] acc, - int matRows, int matCols, int bsMatCols) + static void mulAddMatXMMat(int mVecLimbs, byte[] mat, long[] bsMat, long[] acc, + int matRows, int matCols, int bsMatCols) { for (int r = 0; r < matRows; r++) { @@ -163,8 +162,8 @@ public static void mulAddMatXMMat(int mVecLimbs, byte[] mat, long[] bsMat, long[ } } - public static void mulAddMatXMMat(int mVecLimbs, byte[] mat, long[] bsMat, int bsMatOff, long[] acc, - int matRows, int matCols, int bsMatCols) + static void mulAddMatXMMat(int mVecLimbs, byte[] mat, long[] bsMat, int bsMatOff, long[] acc, + int matRows, int matCols, int bsMatCols) { for (int r = 0; r < matRows; r++) { @@ -204,8 +203,8 @@ public static void mulAddMatXMMat(int mVecLimbs, byte[] mat, long[] bsMat, int b * @param bsMatCols the number of columns in the bit‑sliced matrix. * @param matRows the number of rows in the matrix. */ - public static void mulAddMUpperTriangularMatXMatTrans(int mVecLimbs, long[] bsMat, byte[] mat, long[] acc, - int bsMatRows, int bsMatCols, int matRows) + static void mulAddMUpperTriangularMatXMatTrans(int mVecLimbs, long[] bsMat, byte[] mat, long[] acc, + int bsMatRows, int bsMatCols, int matRows) { int bsMatEntriesUsed = 0; for (int r = 0; r < bsMatRows; r++) @@ -236,23 +235,28 @@ public static void mulAddMUpperTriangularMatXMatTrans(int mVecLimbs, long[] bsMa * @param b an element in GF(16) (only the lower 4 bits are used) * @return the product a * b in GF(16) */ - public static int mulF(int a, int b) + static int mulF(int a, int b) { - // In C there is a conditional XOR with unsigned_char_blocker to work around - // compiler-specific behavior. In Java we can omit it (or define it as needed). - // a ^= unsignedCharBlocker; // Omitted in Java - - // Perform carryless multiplication: - // Multiply b by each bit of a and XOR the results. - int p = ((a & 1) * b) ^ ((a & 2) * b) ^ ((a & 4) * b) ^ ((a & 8) * b); - + // Carryless multiply: multiply b by each bit of a and XOR. + int p = (-(a & 1) & b) ^ (-((a >> 1) & 1) & (b << 1)) ^ (-((a >> 2) & 1) & (b << 2)) ^ (-((a >> 3) & 1) & (b << 3)); // Reduce modulo f(X) = x^4 + x + 1. - // Extract the upper nibble (bits 4 to 7). int topP = p & 0xF0; - // The reduction: XOR p with (topP shifted right by 4 and by 3) and mask to 4 bits. return (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0F; } + /** + * Computes the multiplicative inverse in GF(16) for a GF(16) element. + */ + static byte inverseF(int a) + { + // In GF(16), the inverse can be computed via exponentiation. + int a2 = mulF(a, a); + int a4 = mulF(a2, a2); + int a8 = mulF(a4, a4); + int a6 = mulF(a2, a4); + return (byte) mulF(a8, a6); + } + /** * Performs a GF(16) carryless multiplication of a nibble (lower 4 bits of a) * with a 64-bit word b, then reduces modulo the polynomial x⁴ + x + 1 on each byte. @@ -261,64 +265,29 @@ public static int mulF(int a, int b) * @param b a 64-bit word representing 16 GF(16) elements (packed 4 bits per element) * @return the reduced 64-bit word after multiplication */ - public static long mulFx8(byte a, long b) + static long mulFx8(byte a, long b) { // Convert 'a' to an unsigned int so that bit operations work as expected. int aa = a & 0xFF; // Carryless multiplication: for each bit in 'aa' (considering only the lower 4 bits), // if that bit is set, multiply 'b' (by 1, 2, 4, or 8) and XOR the result. - long p = ((aa & 1) * b) ^ ((aa & 2) * b) ^ ((aa & 4) * b) ^ ((aa & 8) * b); + long p = (-(aa & 1) & b) ^ (-((aa >> 1) & 1) & (b << 1)) ^ (-((aa >> 2) & 1) & (b << 2)) ^ (-((aa >> 3) & 1) & (b << 3)); // Reduction mod (x^4 + x + 1): process each byte in parallel. long topP = p & 0xf0f0f0f0f0f0f0f0L; return (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0f0f0f0f0f0f0f0fL; } - public static void matMul(byte[] a, byte[] b, byte[] c, int colrowAB, int rowA, int colB) - { - int cIndex = 0; - for (int i = 0; i < rowA; i++) - { - int aRowStart = i * colrowAB; - for (int j = 0; j < colB; j++) - { - c[cIndex++] = lincomb(a, aRowStart, b, j, colrowAB, colB); - } - } - } - - public static void matMul(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int cOff, - int colrowAB, int rowA, int colB) - { - for (int i = 0, aRowStart = 0; i < rowA; i++, aRowStart += colrowAB) - { - for (int j = 0; j < colB; j++) - { - c[cOff++] = lincomb(a, aOff + aRowStart, b, bOff + j, colrowAB, colB); - } - } - } - - private static byte lincomb(byte[] a, int aStart, byte[] b, int bStart, - int colrowAB, int colB) - { - byte result = 0; - for (int k = 0; k < colrowAB; k++) - { - result ^= mulF(a[aStart + k], b[bStart + k * colB]); - } - return result; - } - - public static void matAdd(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int cOff, int m, int n) + static void matMul(byte[] a, byte[] b, int bOff, byte[] c, int colrowAB, int rowA) { - for (int i = 0, in = 0; i < m; i++, in += n) + for (int i = 0, aRowStart = 0, cOff = 0; i < rowA; i++, aRowStart += colrowAB) { - for (int j = 0; j < n; j++) + byte result = 0; + for (int k = 0; k < colrowAB; k++) { - int idx = in + j; - c[idx + cOff] = (byte)(a[idx + aOff] ^ b[idx + bOff]); + result ^= mulF(a[aRowStart + k], b[bOff + k]); } + c[cOff++] = result; } } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java index ffad286ab3..af6e96100c 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java @@ -69,7 +69,7 @@ public AsymmetricCipherKeyPair generateKeyPair() // Compute P1 * O + P2 and store the result in P2. // GF16Utils.P1TimesO(p, P, O, P2); // Here, bsMatRows and bsMatCols are both paramV, and matCols is paramO, triangular=1. - GF16Utils.mulAddMUpperTriangularMatXMat(mVecLimbs, P, O, P, p1Limbs, v, v, o); + GF16Utils.mulAddMUpperTriangularMatXMat(mVecLimbs, P, O, P, p1Limbs, v, o); // Compute P3 = O^T * (P1*O + P2). // Here, treat P2 as the bsMat for the multiplication. diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index 9d8656b26f..7a5cbcb109 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -8,6 +8,7 @@ import org.bouncycastle.pqc.crypto.MessageSigner; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Bytes; import org.bouncycastle.util.Longs; import org.bouncycastle.util.Pack; @@ -204,8 +205,8 @@ public byte[] generateSignature(byte[] message) byte[] Ox = new byte[v]; for (int i = 0; i < k; i++) { - GF16Utils.matMul(O, 0, x, i * o, Ox, 0, o, n - o, 1); - GF16Utils.matAdd(Vdec, i * v, Ox, 0, s, i * n, v, 1); + GF16Utils.matMul(O, x, i * o, Ox, o, n - o); + Bytes.xor(v, Vdec, i * v, Ox, s, i * n); System.arraycopy(x, i * o, s, i * n + n - o, o); } @@ -287,7 +288,7 @@ public boolean verifySignature(byte[] message, byte[] signature) return Arrays.constantTimeAreEqual(m, y, 0, t, 0); } - public void computeRHS(long[] vPv, byte[] t, byte[] y) + void computeRHS(long[] vPv, byte[] t, byte[] y) { final int m = params.getM(); final int mVecLimbs = params.getMVecLimbs(); @@ -350,16 +351,16 @@ public void computeRHS(long[] vPv, byte[] t, byte[] y) Pack.littleEndianToLong(tempBytes, 0, temp); // Extract from vPv and add - int matrixIndex = i * k + j; - int symmetricIndex = j * k + i; + int matrixIndex = (i * k + j) * mVecLimbs; + int symmetricIndex = (j * k + i) * mVecLimbs; boolean isDiagonal = (i == j); for (int limb = 0; limb < mVecLimbs; limb++) { - long value = vPv[matrixIndex * mVecLimbs + limb]; + long value = vPv[matrixIndex + limb]; if (!isDiagonal) { - value ^= vPv[symmetricIndex * mVecLimbs + limb]; + value ^= vPv[symmetricIndex + limb]; } temp[limb] ^= value; } @@ -379,7 +380,7 @@ public void computeRHS(long[] vPv, byte[] t, byte[] y) private static final long EVEN_BYTES = 0x00FF00FF00FF00FFL; private static final long EVEN_2BYTES = 0x0000FFFF0000FFFFL; - public void computeA(long[] Mtmp, byte[] AOut) + void computeA(long[] Mtmp, byte[] AOut) { final int k = params.getK(); final int o = params.getO(); @@ -554,8 +555,8 @@ private static void transpose16x16Nibbles(long[] M, int offset) } } - public boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, - byte[] r, byte[] x) + boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, + byte[] r, byte[] x) { final int k = params.getK(); final int o = params.getO(); @@ -573,7 +574,7 @@ public boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, // { // A[k * o + i * (k * o + 1)] = 0; // } - GF16Utils.matMul(A, r, Ar, k * o + 1, m, 1); + GF16Utils.matMul(A, r, 0, Ar, k * o + 1, m); // Update last column of A with y - Ar for (int i = 0; i < m; i++) @@ -645,7 +646,7 @@ public boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, * @param nrows the number of rows * @param ncols the number of columns (GF(16) elements per row) */ - public void ef(byte[] A, int nrows, int ncols) + void ef(byte[] A, int nrows, int ncols) { // Each 64-bit long can hold 16 nibbles (16 GF(16) elements). int rowLen = (ncols + 15) / 16; @@ -713,8 +714,7 @@ public void ef(byte[] A, int nrows, int ncols) } // Multiply the pivot row by the inverse of the pivot element. - int inv = inverseF(pivot); - vecMulAddU64(rowLen, pivotRow, (byte)inv, pivotRow2); + vecMulAddU64(rowLen, pivotRow, GF16Utils.inverseF(pivot), pivotRow2); // Conditionally write the pivot row back into the correct row (if pivot is nonzero). for (int row = lowerBound, rowRowLen = lowerBound * rowLen; row <= upperBound; row++, rowRowLen += rowLen) @@ -769,33 +769,6 @@ private static long ctCompare64(int a, int b) return (-(long)(a ^ b)) >> 63; } - /** - * Computes the multiplicative inverse in GF(16) for a GF(16) element. - */ - private static int inverseF(int a) - { - // In GF(16), the inverse can be computed via exponentiation. - int a2 = mulF(a, a); - int a4 = mulF(a2, a2); - int a8 = mulF(a4, a4); - int a6 = mulF(a2, a4); - return mulF(a8, a6); - } - - /** - * GF(16) multiplication mod (x^4 + x + 1). - *

- * Multiplies two GF(16) elements (only the lower 4 bits are used). - */ - public static int mulF(int a, int b) - { - // Carryless multiply: multiply b by each bit of a and XOR. - int p = (-(a & 1) & b) ^ (-((a >> 1) & 1) & (b << 1)) ^ (-((a >> 2) & 1) & (b << 2)) ^ (-((a >> 3) & 1) & (b << 3)); - // Reduce modulo f(X) = x^4 + x + 1. - int topP = p & 0xF0; - return (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0F; - } - /** * Multiplies each word of the input vector (in) by a GF(16) scalar (a), * then XORs the result into the accumulator vector (acc). @@ -927,45 +900,69 @@ private static void mayoGenericMCalculateSPS(long[] PS, byte[] S, int mVecLimbs, private static void mVecMultiplyBins(int mVecLimbs, int len, long[] bins, long[] ps) { + long a, b, t; + int mVecLimbs2 = mVecLimbs + mVecLimbs, + mVecLimbs3 = mVecLimbs2 + mVecLimbs, + mVecLimbs4 = mVecLimbs3 + mVecLimbs, + mVecLimbs5 = mVecLimbs4 + mVecLimbs, + mVecLimbs6 = mVecLimbs5 + mVecLimbs, + mVecLimbs7 = mVecLimbs6 + mVecLimbs, + mVecLimbs8 = mVecLimbs7 + mVecLimbs, + mVecLimbs9 = mVecLimbs8 + mVecLimbs, + mVecLimbs10 = mVecLimbs9 + mVecLimbs, + mVecLimbs11 = mVecLimbs10 + mVecLimbs, + mVecLimbs12 = mVecLimbs11 + mVecLimbs, + mVecLimbs13 = mVecLimbs12 + mVecLimbs, + mVecLimbs14 = mVecLimbs13 + mVecLimbs, + mVecLimbs15 = mVecLimbs14 + mVecLimbs; for (int i = 0, imVecLimbs4 = 0; i < len; i++, imVecLimbs4 += (mVecLimbs << 4)) { - // Series of modular operations as per original C code - mVecMulAddXInv(mVecLimbs, bins, imVecLimbs4 + 5 * mVecLimbs, bins, imVecLimbs4 + 10 * mVecLimbs); - mVecMulAddX(mVecLimbs, bins, imVecLimbs4 + 11 * mVecLimbs, bins, imVecLimbs4 + 12 * mVecLimbs); - mVecMulAddXInv(mVecLimbs, bins, imVecLimbs4 + 10 * mVecLimbs, bins, imVecLimbs4 + 7 * mVecLimbs); - mVecMulAddX(mVecLimbs, bins, imVecLimbs4 + 12 * mVecLimbs, bins, imVecLimbs4 + 6 * mVecLimbs); - mVecMulAddXInv(mVecLimbs, bins, imVecLimbs4 + 7 * mVecLimbs, bins, imVecLimbs4 + 14 * mVecLimbs); - mVecMulAddX(mVecLimbs, bins, imVecLimbs4 + 6 * mVecLimbs, bins, imVecLimbs4 + 3 * mVecLimbs); - mVecMulAddXInv(mVecLimbs, bins, imVecLimbs4 + 14 * mVecLimbs, bins, imVecLimbs4 + 15 * mVecLimbs); - mVecMulAddX(mVecLimbs, bins, imVecLimbs4 + 3 * mVecLimbs, bins, imVecLimbs4 + 8 * mVecLimbs); - mVecMulAddXInv(mVecLimbs, bins, imVecLimbs4 + 15 * mVecLimbs, bins, imVecLimbs4 + 13 * mVecLimbs); - mVecMulAddX(mVecLimbs, bins, imVecLimbs4 + 8 * mVecLimbs, bins, imVecLimbs4 + 4 * mVecLimbs); - mVecMulAddXInv(mVecLimbs, bins, imVecLimbs4 + 13 * mVecLimbs, bins, imVecLimbs4 + 9 * mVecLimbs); - mVecMulAddX(mVecLimbs, bins, imVecLimbs4 + 4 * mVecLimbs, bins, imVecLimbs4 + 2 * mVecLimbs); - mVecMulAddXInv(mVecLimbs, bins, imVecLimbs4 + 9 * mVecLimbs, bins, imVecLimbs4 + mVecLimbs); - mVecMulAddX(mVecLimbs, bins, imVecLimbs4 + 2 * mVecLimbs, bins, imVecLimbs4 + mVecLimbs); - System.arraycopy(bins, mVecLimbs + imVecLimbs4, ps, imVecLimbs4 >> 4, mVecLimbs); - } - } + for (int j = 0, off = imVecLimbs4; j < mVecLimbs; j++, off++) + { + b = bins[off + mVecLimbs5]; + t = b & GF16Utils.MASK_LSB; + b = bins[off + mVecLimbs10] ^ ((b & GF16Utils.NIBBLE_MASK_LSB) >>> 1) ^ ((t << 3) + t); - // Modular arithmetic operations - private static void mVecMulAddXInv(int limbs, long[] in, int inOffset, long[] acc, int accOffset) - { - for (int i = 0; i < limbs; i++) - { - long input = in[inOffset + i]; - long t = input & GF16Utils.MASK_LSB; - acc[accOffset + i] ^= ((input & GF16Utils.NIBBLE_MASK_LSB) >>> 1) ^ ((t << 3) + t); - } - } + a = bins[off + mVecLimbs11]; + t = (a & GF16Utils.MASK_MSB) >>> 3; + a = bins[off + mVecLimbs12] ^ ((a & GF16Utils.NIBBLE_MASK_MSB) << 1) ^ ((t << 1) + t); - private static void mVecMulAddX(int limbs, long[] in, int inOffset, long[] acc, int accOffset) - { - for (int i = 0; i < limbs; i++) - { - long input = in[inOffset + i]; - long t = (input & GF16Utils.MASK_MSB) >>> 3; - acc[accOffset + i] ^= ((input & GF16Utils.NIBBLE_MASK_MSB) << 1) ^ ((t << 1) + t); + t = b & GF16Utils.MASK_LSB; + b = bins[off + mVecLimbs7] ^ ((b & GF16Utils.NIBBLE_MASK_LSB) >>> 1) ^ ((t << 3) + t); + + t = (a & GF16Utils.MASK_MSB) >>> 3; + a = bins[off + mVecLimbs6] ^ ((a & GF16Utils.NIBBLE_MASK_MSB) << 1) ^ ((t << 1) + t); + + t = b & GF16Utils.MASK_LSB; + b = bins[off + mVecLimbs14] ^ ((b & GF16Utils.NIBBLE_MASK_LSB) >>> 1) ^ ((t << 3) + t); + + t = (a & GF16Utils.MASK_MSB) >>> 3; + a = bins[off + mVecLimbs3] ^ ((a & GF16Utils.NIBBLE_MASK_MSB) << 1) ^ ((t << 1) + t); + + t = b & GF16Utils.MASK_LSB; + b = bins[off + mVecLimbs15] ^ ((b & GF16Utils.NIBBLE_MASK_LSB) >>> 1) ^ ((t << 3) + t); + + t = (a & GF16Utils.MASK_MSB) >>> 3; + a = bins[off + mVecLimbs8] ^ ((a & GF16Utils.NIBBLE_MASK_MSB) << 1) ^ ((t << 1) + t); + + t = b & GF16Utils.MASK_LSB; + b = bins[off + mVecLimbs13] ^ ((b & GF16Utils.NIBBLE_MASK_LSB) >>> 1) ^ ((t << 3) + t); + + t = (a & GF16Utils.MASK_MSB) >>> 3; + a = bins[off + mVecLimbs4] ^ ((a & GF16Utils.NIBBLE_MASK_MSB) << 1) ^ ((t << 1) + t); + + t = b & GF16Utils.MASK_LSB; + b = bins[off + mVecLimbs9] ^ ((b & GF16Utils.NIBBLE_MASK_LSB) >>> 1) ^ ((t << 3) + t); + + t = (a & GF16Utils.MASK_MSB) >>> 3; + a = bins[off + mVecLimbs2] ^ ((a & GF16Utils.NIBBLE_MASK_MSB) << 1) ^ ((t << 1) + t); + + t = b & GF16Utils.MASK_LSB; + b = bins[off + mVecLimbs] ^ ((b & GF16Utils.NIBBLE_MASK_LSB) >>> 1) ^ ((t << 3) + t); + + t = (a & GF16Utils.MASK_MSB) >>> 3; + ps[(imVecLimbs4 >> 4) + j] = b ^ ((a & GF16Utils.NIBBLE_MASK_MSB) << 1) ^ ((t << 1) + t); + } } } } diff --git a/core/src/main/java/org/bouncycastle/util/Bytes.java b/core/src/main/java/org/bouncycastle/util/Bytes.java index 4db85758a0..928cacd714 100644 --- a/core/src/main/java/org/bouncycastle/util/Bytes.java +++ b/core/src/main/java/org/bouncycastle/util/Bytes.java @@ -16,6 +16,14 @@ public static void xor(int len, byte[] x, byte[] y, byte[] z) } } + public static void xor(int len, byte[] x, int xOff, byte[] y, byte[] z, int zOff) + { + for (int i = 0; i < len; ++i) + { + z[zOff + i] = (byte)(x[xOff + i] ^ y[i]); + } + } + public static void xor(int len, byte[] x, int xOff, byte[] y, int yOff, byte[] z, int zOff) { for (int i = 0; i < len; ++i) From 50f0914f65cf9f6567e1602c709078d992bffd5a Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 4 Mar 2025 15:53:34 +1030 Subject: [PATCH 147/890] Refactor of Mayo --- .../pqc/crypto/mayo/GF16Utils.java | 77 ++++++---------- .../pqc/crypto/mayo/MayoKeyPairGenerator.java | 2 +- .../pqc/crypto/mayo/MayoSigner.java | 90 +++++++++---------- .../bouncycastle/pqc/crypto/mayo/Utils.java | 25 +++--- 4 files changed, 87 insertions(+), 107 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java index cc2db8ba1d..d367c94109 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java @@ -100,23 +100,19 @@ static void mulAddMUpperTriangularMatXMat(int mVecLimbs, long[] bsMat, byte[] ma * each entry is an m-vector. * @param matRows number of rows in the matrix “mat.” * @param matCols number of columns in “mat.” - * @param bsMatCols number of columns in the bsMat matrix. */ static void mulAddMatTransXMMat(int mVecLimbs, byte[] mat, long[] bsMat, int bsMatOff, long[] acc, - int matRows, int matCols, int bsMatCols) + int matRows, int matCols) { - // Loop over each column r of mat (which becomes row of mat^T) - for (int r = 0; r < matCols; r++) + int multiply = matCols * mVecLimbs; + for (int r = 0, rmultiply = 0; r < matCols; r++, rmultiply += multiply) { - for (int c = 0, cmatCols = 0; c < matRows; c++, cmatCols += matCols) + for (int c = 0, cmatCols = 0, cmultiply = 0; c < matRows; c++, cmatCols += matCols, cmultiply += multiply) { byte matVal = mat[cmatCols + r]; - for (int k = 0; k < bsMatCols; k++) + for (int k = 0, kmVecLimbs = 0; k < matCols; k++, kmVecLimbs += mVecLimbs) { - int bsMatOffset = bsMatOff + (c * bsMatCols + k) * mVecLimbs; - // For acc: add into the m-vector at index (r * bsMatCols + k) - int accOffset = (r * bsMatCols + k) * mVecLimbs; - mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, matVal, acc, accOffset); + mVecMulAdd(mVecLimbs, bsMat, bsMatOff + cmultiply + kmVecLimbs, matVal, acc, rmultiply + kmVecLimbs); } } } @@ -138,25 +134,19 @@ static void mulAddMatTransXMMat(int mVecLimbs, byte[] mat, long[] bsMat, int bsM * @param acc the accumulator array (long[]) where results are accumulated * @param matRows the number of rows in the matrix * @param matCols the number of columns in the matrix - * @param bsMatCols the number of columns in the bit‐sliced matrix (per block) */ - static void mulAddMatXMMat(int mVecLimbs, byte[] mat, long[] bsMat, long[] acc, - int matRows, int matCols, int bsMatCols) + static void mulAddMatXMMat(int mVecLimbs, byte[] mat, long[] bsMat, long[] acc, int matRows, int matCols) { - for (int r = 0; r < matRows; r++) + int multiply = mVecLimbs * matRows; + for (int r = 0, rmatCols = 0, rmultiply = 0; r < matRows; r++, rmatCols += matCols, rmultiply += multiply) { - for (int c = 0; c < matCols; c++) + for (int c = 0, cmultiply = 0; c < matCols; c++, cmultiply += multiply) { // Retrieve the scalar from the matrix for row r and column c. - byte matVal = mat[r * matCols + c]; - for (int k = 0; k < bsMatCols; k++) + byte matVal = mat[rmatCols + c]; + for (int k = 0, kmVecLimbs = 0; k < matRows; k++, kmVecLimbs += mVecLimbs) { - // Compute the starting index for the vector in bsMat. - int bsMatOffset = mVecLimbs * (c * bsMatCols + k); - // Compute the starting index for the accumulator vector in acc. - int accOffset = mVecLimbs * (r * bsMatCols + k); - // Multiply the vector by the scalar and add the result to the accumulator. - mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, matVal, acc, accOffset); + mVecMulAdd(mVecLimbs, bsMat, cmultiply + kmVecLimbs, matVal, acc, rmultiply + kmVecLimbs); } } } @@ -165,20 +155,16 @@ static void mulAddMatXMMat(int mVecLimbs, byte[] mat, long[] bsMat, long[] acc, static void mulAddMatXMMat(int mVecLimbs, byte[] mat, long[] bsMat, int bsMatOff, long[] acc, int matRows, int matCols, int bsMatCols) { - for (int r = 0; r < matRows; r++) + int multiply = mVecLimbs * bsMatCols; + for (int r = 0, rmultiply = 0, rmatCols = 0; r < matRows; r++, rmultiply += multiply, rmatCols += matCols) { - for (int c = 0; c < matCols; c++) + for (int c = 0, cmultiply = 0; c < matCols; c++, cmultiply += multiply) { // Retrieve the scalar from the matrix for row r and column c. - byte matVal = mat[r * matCols + c]; - for (int k = 0; k < bsMatCols; k++) + byte matVal = mat[rmatCols + c]; + for (int k = 0, kmVecLimbs = 0; k < bsMatCols; k++, kmVecLimbs += mVecLimbs) { - // Compute the starting index for the vector in bsMat. - int bsMatOffset = mVecLimbs * (c * bsMatCols + k) + bsMatOff; - // Compute the starting index for the accumulator vector in acc. - int accOffset = mVecLimbs * (r * bsMatCols + k); - // Multiply the vector by the scalar and add the result to the accumulator. - mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, matVal, acc, accOffset); + mVecMulAdd(mVecLimbs, bsMat, cmultiply + kmVecLimbs + bsMatOff, matVal, acc, rmultiply + kmVecLimbs); } } } @@ -200,27 +186,22 @@ static void mulAddMatXMMat(int mVecLimbs, byte[] mat, long[] bsMat, int bsMatOff * @param mat the matrix stored as a byte array. * @param acc the accumulator array where the results are added. * @param bsMatRows the number of rows in the bit‑sliced matrix. - * @param bsMatCols the number of columns in the bit‑sliced matrix. * @param matRows the number of rows in the matrix. */ - static void mulAddMUpperTriangularMatXMatTrans(int mVecLimbs, long[] bsMat, byte[] mat, long[] acc, - int bsMatRows, int bsMatCols, int matRows) + static void mulAddMUpperTriangularMatXMatTrans(int mVecLimbs, long[] bsMat, byte[] mat, long[] acc, int bsMatRows, int matRows) { int bsMatEntriesUsed = 0; - for (int r = 0; r < bsMatRows; r++) + int multiply = mVecLimbs * matRows; + for (int r = 0, rmultiply = 0; r < bsMatRows; r++, rmultiply += multiply) { // For upper triangular, start c at triangular * r; otherwise, triangular is zero. - for (int c = r; c < bsMatCols; c++) + for (int c = r; c < bsMatRows; c++) { - for (int k = 0; k < matRows; k++) + for (int k = 0, kbsMatRows = 0, kmVecLimbs = 0; k < matRows; k++, kbsMatRows += bsMatRows, kmVecLimbs += mVecLimbs) { - int bsMatOffset = mVecLimbs * bsMatEntriesUsed; - int accOffset = mVecLimbs * (r * matRows + k); - // Get the matrix element at row k and column c - byte matVal = mat[k * bsMatCols + c]; - mVecMulAdd(mVecLimbs, bsMat, bsMatOffset, matVal, acc, accOffset); + mVecMulAdd(mVecLimbs, bsMat, bsMatEntriesUsed, mat[kbsMatRows + c], acc, rmultiply + kmVecLimbs); } - bsMatEntriesUsed++; + bsMatEntriesUsed += mVecLimbs; } } } @@ -254,7 +235,7 @@ static byte inverseF(int a) int a4 = mulF(a2, a2); int a8 = mulF(a4, a4); int a6 = mulF(a2, a4); - return (byte) mulF(a8, a6); + return (byte)mulF(a8, a6); } /** @@ -280,12 +261,12 @@ static long mulFx8(byte a, long b) static void matMul(byte[] a, byte[] b, int bOff, byte[] c, int colrowAB, int rowA) { - for (int i = 0, aRowStart = 0, cOff = 0; i < rowA; i++, aRowStart += colrowAB) + for (int i = 0, aRowStart = 0, cOff = 0; i < rowA; i++) { byte result = 0; for (int k = 0; k < colrowAB; k++) { - result ^= mulF(a[aRowStart + k], b[bOff + k]); + result ^= mulF(a[aRowStart++], b[bOff + k]); } c[cOff++] = result; } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java index af6e96100c..56fdd1fa35 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java @@ -75,7 +75,7 @@ public AsymmetricCipherKeyPair generateKeyPair() // Here, treat P2 as the bsMat for the multiplication. // Dimensions: mat = O (size: paramV x paramO), bsMat = P2 (size: paramV x paramO), // and acc (P3) will have dimensions: (paramO x paramO), each entry being an m-vector. - GF16Utils.mulAddMatTransXMMat(mVecLimbs, O, P, p1Limbs, P3, v, o, o); + GF16Utils.mulAddMatTransXMMat(mVecLimbs, O, P, p1Limbs, P3, v, o); // Store seed_pk into the public key cpk. System.arraycopy(seed_pk, 0, cpk, 0, pkSeedBytes); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index 7a5cbcb109..2e6fd8c96d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -71,15 +71,16 @@ public byte[] generateSignature(byte[] message) byte[] salt = new byte[saltBytes]; byte[] V = new byte[k * vbytes + params.getRBytes()]; byte[] Vdec = new byte[v * k]; - byte[] A = new byte[((params.getM() + 7) / 8 * 8) * (k * o + 1)]; + int ok = k * o; + byte[] A = new byte[((params.getM() + 7) / 8 * 8) * (ok + 1)]; byte[] x = new byte[k * n]; - byte[] r = new byte[k * o + 1]; + byte[] r = new byte[ok + 1]; byte[] s = new byte[k * n]; byte[] tmp = new byte[digestBytes + saltBytes + skSeedBytes + 1]; byte[] sig = new byte[params.getSigBytes()]; long[] P = new long[p1Limbs + params.getP2Limbs()]; byte[] O = new byte[v * o]; - long[] Mtmp = new long[k * o * mVecLimbs]; + long[] Mtmp = new long[ok * mVecLimbs]; long[] vPv = new long[k * k * mVecLimbs]; SHAKEDigest shake = new SHAKEDigest(256); try @@ -150,7 +151,8 @@ public byte[] generateSignature(byte[] message) shake.update(tmp, 0, digestBytes + saltBytes); shake.doFinal(tenc, 0, params.getMBytes()); Utils.decode(tenc, t, params.getM()); - + int size = v * k * mVecLimbs; + long[] Pv = new long[size]; for (int ctr = 0; ctr <= 255; ctr++) { tmp[tmp.length - 1] = (byte)ctr; @@ -171,13 +173,10 @@ public byte[] generateSignature(byte[] message) // Compute VP1V: // Allocate temporary array for Pv. Its length is V_MAX * K_MAX * M_VEC_LIMBS_MAX. - int size = v * k * mVecLimbs; - long[] Pv = new long[size]; // automatically initialized to zero in Java - // Compute Pv = P1 * V^T (using upper triangular multiplication) - GF16Utils.mulAddMUpperTriangularMatXMatTrans(mVecLimbs, P, Vdec, Pv, v, v, k); + GF16Utils.mulAddMUpperTriangularMatXMatTrans(mVecLimbs, P, Vdec, Pv, v, k); // Compute VP1V = Vdec * Pv - GF16Utils.mulAddMatXMMat(mVecLimbs, Vdec, Pv, vPv, k, v, k); + GF16Utils.mulAddMatXMMat(mVecLimbs, Vdec, Pv, vPv, k, v); computeRHS(vPv, t, y); computeA(Mtmp, A); @@ -185,10 +184,10 @@ public byte[] generateSignature(byte[] message) // Clear trailing bytes // for (int i = 0; i < params.getM(); ++i) // { -// A[(i + 1) * (k * o + 1) - 1] = 0; +// A[(i + 1) * (ok + 1) - 1] = 0; // } - Utils.decode(V, k * vbytes, r, 0, k * o); + Utils.decode(V, k * vbytes, r, 0, ok); if (sampleSolution(params, A, y, r, x)) { @@ -304,19 +303,21 @@ void computeRHS(long[] vPv, byte[] t, byte[] y) mask -= 1; final int kSquared = k * k; - for (int i = 0; i < kSquared; i++) + for (int i = 0, index = mVecLimbs - 1; i < kSquared; i++, index += mVecLimbs) { - int index = i * mVecLimbs + mVecLimbs - 1; vPv[index] &= mask; } } long[] temp = new long[mVecLimbs]; byte[] tempBytes = new byte[mVecLimbs << 3]; + int kmVecLimbs = k * mVecLimbs; - for (int i = k - 1; i >= 0; i--) + for (int i = k - 1, imVecLimbs = i * mVecLimbs, ikmVecLimbs = imVecLimbs * k; i >= 0; i--, + imVecLimbs -= mVecLimbs, ikmVecLimbs -= kmVecLimbs) { - for (int j = i; j < k; j++) + for (int j = i, jmVecLimbs = imVecLimbs, jkmVecLimbs = ikmVecLimbs; j < k; j++, + jmVecLimbs += mVecLimbs, jkmVecLimbs += kmVecLimbs) { // Multiply by X (shift up 4 bits) int top = (int)((temp[mVecLimbs - 1] >>> topPos) & 0xF); @@ -351,8 +352,8 @@ void computeRHS(long[] vPv, byte[] t, byte[] y) Pack.littleEndianToLong(tempBytes, 0, temp); // Extract from vPv and add - int matrixIndex = (i * k + j) * mVecLimbs; - int symmetricIndex = (j * k + i) * mVecLimbs; + int matrixIndex = ikmVecLimbs + jmVecLimbs; + int symmetricIndex = jkmVecLimbs + imVecLimbs; boolean isDiagonal = (i == j); for (int limb = 0; limb < mVecLimbs; limb++) @@ -393,6 +394,7 @@ void computeA(long[] Mtmp, byte[] AOut) int wordsToShift = 0; final int MAYO_M_OVER_8 = (m + 7) >>> 3; int ok = o * k; + int omVecLimbs = o * mVecLimbs; final int AWidth = ((ok + 15) >> 4) << 4; long[] A = new long[(AWidth * MAYO_M_OVER_8) << 4]; @@ -407,20 +409,18 @@ void computeA(long[] Mtmp, byte[] AOut) } } - for (int i = 0; i < k; i++) + for (int i = 0, io = 0; i < k; i++, io += o) { - for (int j = k - 1; j >= i; j--) + for (int j = k - 1, jomVecLimbs = j * omVecLimbs, jo = j * o; j >= i; j--, jomVecLimbs -= omVecLimbs, jo -= o) { // Process Mj - int mjOffset = j * mVecLimbs * o; - for (int c = 0; c < o; c++) + for (int c = 0, cmVecLimbs = 0; c < o; c++, cmVecLimbs += mVecLimbs) { - for (int limb = 0; limb < mVecLimbs; limb++) + for (int limb = 0, limbAWidhth = 0; limb < mVecLimbs; limb++, limbAWidhth += AWidth) { - int idx = mjOffset + limb + c * mVecLimbs; - long value = Mtmp[idx]; + long value = Mtmp[jomVecLimbs + limb + cmVecLimbs]; - int aIndex = o * i + c + (limb + wordsToShift) * AWidth; + int aIndex = io + c + wordsToShift + limbAWidhth; A[aIndex] ^= value << bitsToShift; if (bitsToShift > 0) @@ -434,14 +434,13 @@ void computeA(long[] Mtmp, byte[] AOut) { // Process Mi int miOffset = i * mVecLimbs * o; - for (int c = 0; c < o; c++) + for (int c = 0, cmVecLimbs = 0; c < o; c++, cmVecLimbs += mVecLimbs) { - for (int limb = 0; limb < mVecLimbs; limb++) + for (int limb = 0, limbAWidhth = 0; limb < mVecLimbs; limb++, limbAWidhth += AWidth) { - int idx = miOffset + limb + c * mVecLimbs; - long value = Mtmp[idx]; + long value = Mtmp[miOffset + limb + cmVecLimbs]; - int aIndex = o * j + c + (limb + wordsToShift) * AWidth; + int aIndex = jo + c + wordsToShift + limbAWidhth; A[aIndex] ^= value << bitsToShift; if (bitsToShift > 0) @@ -455,14 +454,14 @@ void computeA(long[] Mtmp, byte[] AOut) bitsToShift += 4; if (bitsToShift == 64) { - wordsToShift++; + wordsToShift += AWidth; bitsToShift = 0; } } } // Transpose blocks - for (int c = 0; c < AWidth * ((m + (k + 1) * k / 2 + 15) / 16); c += 16) + for (int c = 0; c < AWidth * ((m + (k + 1) * k / 2 + 15) >>> 4); c += 16) { transpose16x16Nibbles(A, c); } @@ -481,7 +480,7 @@ void computeA(long[] Mtmp, byte[] AOut) // Final processing for (int c = 0; c < AWidth; c += 16) { - for (int r = m; r < m + (k + 1) * k / 2; r++) + for (int r = m; r < m + (((k + 1) * k) >>> 1); r++) { int pos = (r >>> 4) * AWidth + c + (r & 15); long t0 = A[pos] & GF16Utils.MASK_LSB; @@ -562,9 +561,9 @@ boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, final int o = params.getO(); final int m = params.getM(); final int aCols = params.getACols(); - + int ok = k * o; // Initialize x with r values - System.arraycopy(r, 0, x, 0, k * o); + System.arraycopy(r, 0, x, 0, ok); // Compute Ar matrix product byte[] Ar = new byte[m]; @@ -572,14 +571,14 @@ boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, // Clear last column of A // for (int i = 0; i < m; i++) // { -// A[k * o + i * (k * o + 1)] = 0; +// A[ok + i * (ok + 1)] = 0; // } - GF16Utils.matMul(A, r, 0, Ar, k * o + 1, m); + GF16Utils.matMul(A, r, 0, Ar, ok + 1, m); // Update last column of A with y - Ar for (int i = 0; i < m; i++) { - A[k * o + i * (k * o + 1)] = (byte)(y[i] ^ Ar[i]); + A[ok + i * (ok + 1)] = (byte)(y[i] ^ Ar[i]); } // Perform row echelon form transformation @@ -597,18 +596,17 @@ boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, } // Constant-time back substitution - for (int row = m - 1; row >= 0; row--) + for (int row = m - 1, rowAcols = row * aCols; row >= 0; row--, rowAcols -= aCols) { byte finished = 0; - int colUpperBound = Math.min(row + (32 / (m - row)), k * o); + int colUpperBound = Math.min(row + (32 / (m - row)), ok); for (int col = row; col <= colUpperBound; col++) { - byte correctCol = (byte)((-(A[row * aCols + col] & 0xFF)) >> 31); + byte correctCol = (byte)((-(A[rowAcols + col] & 0xFF)) >> 31); // Update x[col] using constant-time mask - byte u = (byte)(correctCol & ~finished & A[row * aCols + aCols - 1]); - //System.out.println("x[col]: " + x[col] + ", u: " + u); + byte u = (byte)(correctCol & ~finished & A[rowAcols + aCols - 1]); x[col] ^= u; @@ -661,7 +659,7 @@ void ef(byte[] A, int nrows, int ncols) int len_4 = len >> 4; // Pack the matrix rows. - for (int i = 0, incols = 0; i < nrows; i++, incols += ncols) + for (int i = 0, incols = 0, irowLen = 0; i < nrows; i++, incols += ncols, irowLen += rowLen) { //packRow(A, i, ncols); // Process each 64-bit word (each holds 16 nibbles). @@ -676,7 +674,7 @@ void ef(byte[] A, int nrows, int ncols) wordVal |= ((long)A[incols + col] & 0xF) << (nibble << 2); } } - packedA[word + i * rowLen] = wordVal; + packedA[word + irowLen] = wordVal; } } @@ -710,7 +708,7 @@ void ef(byte[] A, int nrows, int ncols) } // Extract candidate pivot element from the packed row. pivot = (int)((pivotRow[pivotCol >>> 4] >>> ((pivotCol & 15) << 2)) & 0xF); - pivotIsZero = ~ctCompare64(pivot, 0); + pivotIsZero = ~((-(long)pivot) >> 63); } // Multiply the pivot row by the inverse of the pivot element. diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java index e48d019cda..8d9b1ef47b 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java @@ -24,13 +24,14 @@ public static void decode(byte[] m, byte[] mdec, int mdecLen) { int i; int decIndex = 0; + int blocks = mdecLen >> 1; // Process pairs of nibbles from each byte - for (i = 0; i < mdecLen / 2; i++) + for (i = 0; i < blocks; i++) { // Extract the lower nibble - mdec[decIndex++] = (byte)((m[i] & 0xFF) & 0x0F); + mdec[decIndex++] = (byte)(m[i] & 0x0F); // Extract the upper nibble (shift right 4 bits) - mdec[decIndex++] = (byte)(((m[i] & 0xFF) >> 4) & 0x0F); + mdec[decIndex++] = (byte)((m[i] >> 4) & 0x0F); } // If there is an extra nibble (odd number of nibbles), decode only the lower nibble if (mdecLen % 2 == 1) @@ -41,19 +42,19 @@ public static void decode(byte[] m, byte[] mdec, int mdecLen) public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mdecLen) { - int i; // Process pairs of nibbles from each byte - for (i = 0; i < mdecLen / 2; i++) + int blocks = mdecLen >> 1; + for (int i = 0; i < blocks; i++) { // Extract the lower nibble - mdec[decIndex++] = (byte)(m[i + mOff] & 0x0F); + mdec[decIndex++] = (byte)(m[mOff] & 0x0F); // Extract the upper nibble (shift right 4 bits) - mdec[decIndex++] = (byte)((m[i + mOff] >> 4) & 0x0F); + mdec[decIndex++] = (byte)((m[mOff++] >> 4) & 0x0F); } // If there is an extra nibble (odd number of nibbles), decode only the lower nibble if (mdecLen % 2 == 1) { - mdec[decIndex] = (byte)(m[i + mOff] & 0x0F); + mdec[decIndex] = (byte)(m[mOff] & 0x0F); } } @@ -68,15 +69,15 @@ public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mde public static void decode(byte[] input, int inputOffset, byte[] output, int mdecLen) { int decIndex = 0; - int blocks = mdecLen / 2; + int blocks = mdecLen >> 1; for (int i = 0; i < blocks; i++) { - output[decIndex++] = (byte)(input[inputOffset + i] & 0x0F); - output[decIndex++] = (byte)((input[inputOffset + i] >> 4) & 0x0F); + output[decIndex++] = (byte)(input[inputOffset] & 0x0F); + output[decIndex++] = (byte)((input[inputOffset++] >> 4) & 0x0F); } if (mdecLen % 2 == 1) { - output[decIndex] = (byte)(input[inputOffset + blocks] & 0x0F); + output[decIndex] = (byte)(input[inputOffset] & 0x0F); } } From fa20a568aeb0318b180a24c0ad41b9b442bce908 Mon Sep 17 00:00:00 2001 From: Jill Kleiber Date: Thu, 16 Jan 2025 11:58:44 +0100 Subject: [PATCH 148/890] added support for rfc9579 (PBMAC1 with PBKDF2) to PKCS12KeyStoreSpi --- .../keystore/pkcs12/PKCS12KeyStoreSpi.java | 56 +++ .../jce/provider/test/PKCS12StoreTest.java | 402 ++++++++++++++++++ 2 files changed, 458 insertions(+) diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java index ebbd568a5c..219efb6d8d 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java @@ -76,6 +76,7 @@ import org.bouncycastle.asn1.pkcs.KeyDerivationFunc; import org.bouncycastle.asn1.pkcs.MacData; import org.bouncycastle.asn1.pkcs.PBES2Parameters; +import org.bouncycastle.asn1.pkcs.PBMAC1Params; import org.bouncycastle.asn1.pkcs.PBKDF2Params; import org.bouncycastle.asn1.pkcs.PKCS12PBEParams; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; @@ -93,8 +94,14 @@ import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x509.TBSCertificate; import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; +import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.PBEParametersGenerator; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.digests.SHA512Digest; +import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; +import org.bouncycastle.crypto.macs.HMac; import org.bouncycastle.crypto.util.DigestFactory; import org.bouncycastle.internal.asn1.cms.GCMParameters; import org.bouncycastle.internal.asn1.misc.MiscObjectIdentifiers; @@ -2041,6 +2048,39 @@ private byte[] calculatePbeMac( byte[] data) throws Exception { + if (PKCSObjectIdentifiers.id_PBMAC1.equals(oid)) + { + PBMAC1Params pbmac1Params = PBMAC1Params.getInstance(macAlgorithm.getParameters()); + if (pbmac1Params == null) + { + throw new IOException("If the DigestAlgorithmIdentifier is id-PBMAC1, then the parameters field must contain valid PBMAC1-params parameters."); + } + if (PKCSObjectIdentifiers.id_PBKDF2.equals(pbmac1Params.getKeyDerivationFunc().getAlgorithm())) + { + PBKDF2Params pbkdf2Params = PBKDF2Params.getInstance(pbmac1Params.getKeyDerivationFunc().getParameters()); + if (pbkdf2Params.getKeyLength() == null) + { + throw new IOException("Key length must be present when using PBMAC1."); + } + final HMac hMac = new HMac(getPrf(pbmac1Params.getMessageAuthScheme().getAlgorithm())); + + PBEParametersGenerator generator = new PKCS5S2ParametersGenerator(getPrf(pbkdf2Params.getPrf().getAlgorithm())); + + generator.init( + Strings.toUTF8ByteArray(password), + pbkdf2Params.getSalt(), + BigIntegers.intValueExact(pbkdf2Params.getIterationCount())); + + CipherParameters key = generator.generateDerivedParameters(BigIntegers.intValueExact(pbkdf2Params.getKeyLength()) * 8); + + hMac.init(key); + hMac.update(data, 0, data.length); + byte[] res = new byte[hMac.getMacSize()]; + hMac.doFinal(res, 0); + return res; + } + } + PBEParameterSpec defParams = new PBEParameterSpec(salt, itCount); Mac mac = helper.createMac(oid.getId()); @@ -2050,6 +2090,22 @@ private byte[] calculatePbeMac( return mac.doFinal(); } + private static Digest getPrf(ASN1ObjectIdentifier prfId) + { + if (PKCSObjectIdentifiers.id_hmacWithSHA256.equals(prfId)) + { + return new SHA256Digest(); + } + else if (PKCSObjectIdentifiers.id_hmacWithSHA512.equals(prfId)) + { + return new SHA512Digest(); + } + else + { + throw new IllegalArgumentException("unknown prf id " + prfId); + } + } + public static class BCPKCS12KeyStore extends AdaptingKeyStoreSpi { diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java index 0ebbcdb104..9011fee6f7 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java @@ -943,6 +943,361 @@ public class PKCS12StoreTest private static byte[] rawKeyBagStore = Base64.decode("MIIFlgIBAzCCBY8GCSqGSIb3DQEHAaCCBYAEggV8MIIFeDCCAv4GCSqGSIb3DQEHAaCCAu8EggLrMIIC5zCCAuMGCyqGSIb3DQEMCgEBoIICejCCAnYCAQAwDQYJKoZIhvcNAQEBBQAEggJgMIICXAIBAAKBgQCF4Tw78b8eDuwY+FomQazkPFuAxDbWTs//AozC4MvzBatdJeDu+s9WyK3PdU+gI7wFish0r2FP8M5dj/rA0ieCJ9UDTGWVKm06DB0y7zmAO3SS/3TXGQRekMmOXBtVlZa4AYVy8Tr+Ls69gfo3sgqJU8uH0ebWuoQTKJz/mpst0wIDAQABAoGBAIJbpu/jWylkdEV4BSd9CWCO2LYP2CliQirXC8JxaoTuf0ZKrLNlqd+htYPsgSS3xstKsBbV8hYJrpbxq8J2npok973j0bm9sW9RL8XmAYJbaat27IzQQkGj2j4CNWPJzQC3NsDWQJPMJMFHvT1ZIj5ASwvOHwKpM6haLPxX24o5AkEA/zBVPpO6Ic9Yfd8Fk+BN/DykpPbLMUNZFl/I2MavoXTh5Ng7J4/S5ABxkvvQdqKf1Nhal5CznakU4BjFUGr+dwJBAIZOLwlfToFgekV4SmcPnq4aNGdetDfEettRGJLrKf+qrZrTzW3Rj6N2cjxKHsE5/xOpyjOtgVv3cTQm0x//VoUCQAdQBUFTzmOlo22H9Ir2RIXT3wvzHoN84JKpkAHWP7YquUZrg9ZwYqSx9o81tBWSN25L/NyXAu6jp7t8OjtBtaUCQCILB1k0001wCw4444MkLnCrK8VX+A56uzmEYNo8ybSIquCn91Zy3BnvGB24G/uWm9V8IEjhHf0Vx5gUj0d5DZECQGRs4BMYE+y2Tpn7/zbjhZh/iAdttDq5/b2BBMbSiosSKRIGkOyHTu0SJKoxoDnHA5ryLK8NoSwoGjID5qESjA8xVjAjBgkqhkiG9w0BCRUxFgQU3U3Taaj7rCAV2GyyVEnAUZvc4JkwLwYJKoZIhvcNAQkUMSIeIABPAE4AVgBJAEYAXwBUAGUAcwB0AF8AQQBsAGkAYQBzMIICcgYJKoZIhvcNAQcBoIICYwSCAl8wggJbMIICVwYLKoZIhvcNAQwKAQOgggHuMIIB6gYKKoZIhvcNAQkWAaCCAdoEggHWMIIB0jCCATugAwIBAgIICNurBKCCK6gwDQYJKoZIhvcNAQEFBQAwIDERMA8GA1UEAwwIT05WSUYgVFQxCzAJBgNVBAYTAlVTMCAXDTcwMDEwMTAwMDAwMFoYDzk5OTkxMjMxMjM1OTU5WjAgMREwDwYDVQQDDAhPTlZJRiBUVDELMAkGA1UEBhMCVVMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAIXhPDvxvx4O7Bj4WiZBrOQ8W4DENtZOz/8CjMLgy/MFq10l4O76z1bIrc91T6AjvAWKyHSvYU/wzl2P+sDSJ4In1QNMZZUqbToMHTLvOYA7dJL/dNcZBF6QyY5cG1WVlrgBhXLxOv4uzr2B+jeyColTy4fR5ta6hBMonP+amy3TAgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEARmnQ9q/lUycK5P4shGFlwK0Uy3dHZs4VxOBSinejSTTy1FL4+SRzwA+YDMmfRrI0WHY/upUCYyugDj5kDg5K6/mSiIWGr0PDjl+8qw352fpUQgY4vnpGBaJoLQf/KRFilVhZJz0QDq5iHo16UkibDDHYQqdt6la5SHKx4U6AJwYxVjAjBgkqhkiG9w0BCRUxFgQU3U3Taaj7rCAV2GyyVEnAUZvc4JkwLwYJKoZIhvcNAQkUMSIeIABPAE4AVgBJAEYAXwBUAGUAcwB0AF8AQQBsAGkAYQBz"); + // Valid PKCS #12 File with SHA-256 HMAC and PRF + private static final byte[] pkcs12WithPBMac1PBKdf2_a1 = Base64.decode( + "MIIKigIBAzCCCgUGCSqGSIb3DQEHAaCCCfYEggnyMIIJ7jCCBGIGCSqGSIb3DQEH\n" + + "BqCCBFMwggRPAgEAMIIESAYJKoZIhvcNAQcBMFcGCSqGSIb3DQEFDTBKMCkGCSqG\n" + + "SIb3DQEFDDAcBAg9pxXxY2yscwICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQME\n" + + "ASoEEK7yYaFQDi1pYwWzm9F/fs+AggPgFIT2XapyaFgDppdvLkdvaF3HXw+zjzKb\n" + + "7xFC76DtVPhVTWVHD+kIss+jsj+XyvMwY0aCuAhAG/Dig+vzWomnsqB5ssw5/kTb\n" + + "+TMQ5PXLkNeoBmB6ArKeGc/QmCBQvQG/a6b+nXSWmxNpP+71772dmWmB8gcSJ0kF\n" + + "Fj75NrIbmNiDMCb71Q8gOzBMFf6BpXf/3xWAJtxyic+tSNETfOJa8zTZb0+lV0w9\n" + + "5eUmDrPUpuxEVbb0KJtIc63gRkcfrPtDd6Ii4Zzbzj2Evr4/S4hnrQBsiryVzJWy\n" + + "IEjaD0y6+DmG0JwMgRuGi1wBoGowi37GMrDCOyOZWC4n5wHLtYyhR6JaElxbrhxP\n" + + "H46z2USLKmZoF+YgEQgYcSBXMgP0t36+XQocFWYi2N5niy02TnctwF430FYsQlhJ\n" + + "Suma4I33E808dJuMv8T/soF66HsD4Zj46hOf4nWmas7IaoSAbGKXgIa7KhGRJvij\n" + + "xM3WOX0aqNi/8bhnxSA7fCmIy/7opyx5UYJFWGBSmHP1pBHBVmx7Ad8SAsB9MSsh\n" + + "nbGjGiUk4h0QcOi29/M9WwFlo4urePyI8PK2qtVAmpD3rTLlsmgzguZ69L0Q/CFU\n" + + "fbtqsMF0bgEuh8cfivd1DYFABEt1gypuwCUtCqQ7AXK2nQqOjsQCxVz9i9K8NDeD\n" + + "aau98VAl0To2sk3/VR/QUq0PRwU1jPN5BzUevhE7SOy/ImuJKwpGqqFljYdrQmj5\n" + + "jDe+LmYH9QGVRlfN8zuU+48FY8CAoeBeHn5AAPml0PYPVUnt3/jQN1+v+CahNVI+\n" + + "La8q1Nen+j1R44aa2I3y/pUgtzXRwK+tPrxTQbG030EU51LYJn8amPWmn3w75ZIA\n" + + "MJrXWeKj44de7u4zdUsEBVC2uM44rIHM8MFjyYAwYsey0rcp0emsaxzar+7ZA67r\n" + + "lDoXvvS3NqsnTXHcn3T9tkPRoee6L7Dh3x4Od96lcRwgdYT5BwyH7e34ld4VTUmJ\n" + + "bDEq7Ijvn4JKrwQJh1RCC+Z/ObfkC42xAm7G010u3g08xB0Qujpdg4a7VcuWrywF\n" + + "c7hLNquuaF4qoDaVwYXHH3iuX6YlJ/3siTKbYCVXPEZOAMBP9lF/OU76UMJBQNfU\n" + + "0xjDx+3AhUVgnGuCsmYlK6ETDp8qOZKGyV0KrNSGtqLx3uMhd7PETeW+ML3tDQ/0\n" + + "X9fMkcZHi4C2fXnoHV/qa2dGhBj4jjQ0Xh1poU6mxGn2Mebe2hDsBZkkBpnn7pK4\n" + + "wP/VqXdQTwqEuvzGHLVFsCuADe40ZFBmtBrf70wG7ZkO8SUZ8Zz1IX3+S024g7yj\n" + + "QRev/6x6TtkwggWEBgkqhkiG9w0BBwGgggV1BIIFcTCCBW0wggVpBgsqhkiG9w0B\n" + + "DAoBAqCCBTEwggUtMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhTxzw+\n" + + "VptrYAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEK9nSqc1I2t4tMVG\n" + + "bWHpdtQEggTQzCwI7j34gCTvfj6nuOSndAjShGv7mN2j7WMV0pslTpq2b9Bn3vn1\n" + + "Y0JMvL4E7sLrUzNU02pdOcfCnEpMFccNv2sQrLp1mOCKxu8OjSqHZLoKVL0ROVsZ\n" + + "8dMECLLigDlPKRiSyLErl14tErX4/zbkUaWMROO28kFbTbubQ8YoHlRUwsKW1xLg\n" + + "vfi0gRkG/zHXRfQHjX/8NStv7hXlehn7/Gy2EKPsRFhadm/iUHAfmCMkMgHTU248\n" + + "JER9+nsXltd59H+IeDpj/kbxZ+YvHow9XUZKu828d3MQnUpLZ1BfJGhMBPVwbVUD\n" + + "A40CiQBVdCoGtPJyalL28xoS3H0ILFCnwQOr6u0HwleNJPGHq78HUyH6Hwxnh0b0\n" + + "5o163r6wTFZn5cMOxpbs/Ttd+3TrxmrYpd2XnuRme3cnaYJ0ILvpc/8eLLR7SKjD\n" + + "T4JhZ0h/CfcV2WWvhpQugkY0pWrZ+EIMneB1dZB96mJVLxOi148OeSgi0PsxZMNi\n" + + "YM33rTpwQT5WqOsEyDwUQpne5b8Kkt/s7EN0LJNnPyJJRL1LcqOdr6j+6YqRtPa7\n" + + "a9oWJqMcuTP+bqzGRJh+3HDlFBw2Yzp9iadv4KmB2MzhStLUoi2MSjvnnkkd5Led\n" + + "sshAd6WbKfF7kLAHQHT4Ai6dMEO4EKkEVF9JBtxCR4JEn6C98Lpg+Lk+rfY7gHOf\n" + + "ZxtgGURwgXRY3aLUrdT55ZKgk3ExVKPzi5EhdpAau7JKhpOwyKozAp/OKWMNrz6h\n" + + "obu2Mbn1B+IA60psYHHxynBgsJHv7WQmbYh8HyGfHgVvaA8pZCYqxxjpLjSJrR8B\n" + + "Bu9H9xkTh7KlhxgreXYv19uAYbUd95kcox9izad6VPnovgFSb+Omdy6PJACPj6hF\n" + + "W6PJbucP0YPpO0VtWtQdZZ3df1P0hZ7qvKwOPFA+gKZSckgqASfygiP9V3Zc8jIi\n" + + "wjNzoDM2QT+UUJKiiGYXJUEOO9hxzFHlGj759DcNRhpgl5AgR57ofISD9yBuCAJY\n" + + "PQ/aZHPFuRTrcVG3RaIbCAS73nEznKyFaLOXfzyfyaSmyhsH253tnyL1MejC+2bR\n" + + "Eko/yldgFUxvU5JI+Q3KJ6Awj+PnduHXx71E4UwSuu2xXYMpxnQwI6rroQpZBX82\n" + + "HhqgcLV83P8lpzQwPdHjH5zkoxmWdC0+jU/tcQfNXYpJdyoaX7tDmVclLhwl9ps/\n" + + "O841pIsNLJWXwvxG6B+3LN/kw4QjwN194PopiOD7+oDm5mhttO78CrBrRxHMD/0Q\n" + + "qniZjKzSZepxlZq+J792u8vtMnuzzChxu0Bf3PhIXcJNcVhwUtr0yKe/N+NvC0tm\n" + + "p8wyik/BlndxN9eKbdTOi2wIi64h2QG8nOk66wQ/PSIJYwZl6eDNEQSzH/1mGCfU\n" + + "QnUT17UC/p+Qgenf6Auap2GWlvsJrB7u/pytz65rtjt/ouo6Ih6EwWqwVVpGXZD0\n" + + "7gVWH0Ke/Vr6aPGNvkLcmftPuDZsn9jiig3guhdeyRVf10Ox369kKWcG75q77hxE\n" + + "IzSzDyUlBNbnom9SIjut3r+qVYmWONatC6q/4D0I42Lnjd3dEyZx7jmH3g/S2ASM\n" + + "FzWr9pvXc61dsYOkdZ4PYa9XPUZxXFagZsoS3F1sU799+IJVU0tC0MExJTAjBgkq\n" + + "hkiG9w0BCRUxFgQUwWO5DorvVWYF3BWUmAw0rUEajScwfDBtMEkGCSqGSIb3DQEF\n" + + "DjA8MCwGCSqGSIb3DQEFDDAfBAhvRzw4sC4xcwICCAACASAwDAYIKoZIhvcNAgkF\n" + + "ADAMBggqhkiG9w0CCQUABCB6pW2FOdcCNj87zS64NUXG36K5aXDnFHctIk5Bf4kG\n" + + "3QQITk9UIFVTRUQCAQE=\n"); + + // Valid PKCS #12 File with SHA-256 HMAC and SHA-512 PRF + private static final byte[] pkcs12WithPBMac1PBKdf2_a2 = Base64.decode("MIIKigIBAzCCCgUGCSqGSIb3DQEHAaCCCfYEggnyMIIJ7jCCBGIGCSqGSIb3DQEH\n" + + "BqCCBFMwggRPAgEAMIIESAYJKoZIhvcNAQcBMFcGCSqGSIb3DQEFDTBKMCkGCSqG\n" + + "SIb3DQEFDDAcBAi4j6UBBY2iOgICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQME\n" + + "ASoEEFpHSS5zrk/9pkDo1JRbtE6AggPgtbMLGoFd5KLpVXMdcxLrT129L7/vCr0B\n" + + "0I2tnhPPA7aFtRjjuGbwooCMQwxw9qzuCX1eH4xK2LUw6Gbd2H47WimSOWJMaiUb\n" + + "wy4alIWELYufe74kXPmKPCyH92lN1hqu8s0EGhIl7nBhWbFzow1+qpIc9/lpujJo\n" + + "wodSY+pNBD8oBeoU1m6DgOjgc62apL7m0nwavDUqEt7HAqtTBxKxu/3lpb1q8nbl\n" + + "XLTqROax5feXErf+GQAqs24hUJIPg3O1eCMDVzH0h5pgZyRN9ZSIP0HC1i+d1lnb\n" + + "JwHyrAhZv8GMdAVKaXHETbq8zTpxT3UE/LmH1gyZGOG2B21D2dvNDKa712sHOS/t\n" + + "3XkFngHDLx+a9pVftt6p7Nh6jqI581tb7fyc7HBV9VUc/+xGgPgHZouaZw+I3PUz\n" + + "fjHboyLQer22ndBz+l1/S2GhhZ4xLXg4l0ozkgn7DX92S/UlbmcZam1apjGwkGY/\n" + + "7ktA8BarNW211mJF+Z+hci+BeDiM7eyEguLCYRdH+/UBiUuYjG1hi5Ki3+42pRZD\n" + + "FZkTHGOrcG6qE2KJDsENj+RkGiylG98v7flm4iWFVAB78AlAogT38Bod40evR7Ok\n" + + "c48sOIW05eCH/GLSO0MHKcttYUQNMqIDiG1TLzP1czFghhG97AxiTzYkKLx2cYfs\n" + + "pgg5PE9drq1fNzBZMUmC2bSwRhGRb5PDu6meD8uqvjxoIIZQAEV53xmD63umlUH1\n" + + "jhVXfcWSmhU/+vV/IWStZgQbwhF7DmH2q6S8itCkz7J7Byp5xcDiUOZ5Gpf9RJnk\n" + + "DTZoOYM5iA8kte6KCwA+jnmCgstI5EbRbnsNcjNvAT3q/X776VdmnehW0VeL+6k4\n" + + "z+GvQkr+D2sxPpldIb5hrb+1rcp9nOQgtpBnbXaT16Lc1HdTNe5kx4ScujXOWwfd\n" + + "Iy6bR6H0QFq2SLKAAC0qw4E8h1j3WPxll9e0FXNtoRKdsRuX3jzyqDBrQ6oGskkL\n" + + "wnyMtVjSX+3c9xbFc4vyJPFMPwb3Ng3syjUDrOpU5RxaMEAWt4josadWKEeyIC2F\n" + + "wrS1dzFn/5wv1g7E7xWq+nLq4zdppsyYOljzNUbhOEtJ2lhme3NJ45fxnxXmrPku\n" + + "gBda1lLf29inVuzuTjwtLjQwGk+usHJm9R/K0hTaSNRgepXnjY0cIgS+0gEY1/BW\n" + + "k3+Y4GE2JXds2cQToe5rCSYH3QG0QTyUAGvwX6hAlhrRRgUG3vxtYSixQ3UUuwzs\n" + + "eQW2SUFLl1611lJ7cQwFSPyr0sL0p81vdxWiigwjkfPtgljZ2QpmzR5rX2xiqItH\n" + + "Dy4E+iVigIYwggWEBgkqhkiG9w0BBwGgggV1BIIFcTCCBW0wggVpBgsqhkiG9w0B\n" + + "DAoBAqCCBTEwggUtMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhDiwsh\n" + + "4wt3aAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEELNFnEpJT65wsXwd\n" + + "fZ1g56cEggTQRo04bP/fWfPPZrTEczq1qO1HHV86j76Sgxau2WQ9OQAG998HFtNq\n" + + "NxO8R66en6QFhqpWCI73tSJD+oA29qOsT+Xt2bR2z5+K7D4QoiXuLa3gXv62VkjB\n" + + "0DLCHAS7Mu+hkp5OKCpXCS7fo0OnAiQjM4EluAsiwwLrHu7z1E16UwpmlgKQnaC1\n" + + "S44fV9znS9TxofRTnuCq1lupdn2qQjSydOU6inQeKLBflKRiLrJHOobaFmjWwp1U\n" + + "OQAMuZrALhHyIbOFXMPYk3mmU/1UPuRGcbcV5v2Ut2UME+WYExXSCOYR3/R4UfVk\n" + + "IfEzeRPFs2slJMIDS2fmMyFkEEElBckhKO9IzhQV3koeKUBdM066ufyax/uIyXPm\n" + + "MiB9fAqbQQ4jkQTT80bKkBAP1Bvyg2L8BssstR5iCoZgWnfA9Uz4RI5GbRqbCz7H\n" + + "iSkuOIowEqOox3IWbXty5VdWBXNjZBHpbE0CyMLSH/4QdGVw8R0DiCAC0mmaMaZq\n" + + "32yrBR32E472N+2KaicvX31MwB/LkZN46c34TGanL5LJZx0DR6ITjdNgP8TlSSrp\n" + + "7y2mqi7VbKp/C/28Cj5r+m++Gk6EOUpLHsZ2d2hthrr7xqoPzUAEkkyYWedHJaoQ\n" + + "TkoIisZb0MGlXb9thjQ8Ee429ekfjv7CQfSDS6KTE/+mhuJ33mPz1ZcIacHjdHhE\n" + + "6rbrKhjSrLbgmrGa8i7ezd89T4EONu0wkG9KW0wM2cn5Gb12PF6rxjTfzypG7a50\n" + + "yc1IJ2Wrm0B7gGuYpVoCeIohr7IlxPYdeQGRO/SlzTd0xYaJVm9FzJaMNK0ZqnZo\n" + + "QMEPaeq8PC3kMjpa8eAiHXk9K3DWdOWYviGVCPVYIZK6Cpwe+EwfXs+2hZgZlYzc\n" + + "vpUWg60md1PD4UsyLQagaj37ubR6K4C4mzlhFx5NovV/C/KD+LgekMbjCtwEQeWy\n" + + "agev2l9KUEz73/BT4TgQFM5K2qZpVamwmsOmldPpekGPiUCu5YxYg/y4jUKvAqj1\n" + + "S9t4wUAScCJx8OvXUfgpmS2+mhFPBiFps0M4O3nWG91Q6mKMqbNHPUcFDn9P7cUh\n" + + "s1xu3NRLyJ+QIfVfba3YBTV8A6WBYEmL9lxf1uL1WS2Bx6+Crh0keyNUPo9cRjpx\n" + + "1oj/xkInoc2HQODEkvuK9DD7VrLr7sDhfmJvr1mUfJMQ5/THk7Z+E+NAuMdMtkM2\n" + + "yKXxghZAbBrQkU3mIW150i7PsjlUw0o0/LJvQwJIsh6yeJDHY8mby9mIdeP3LQAF\n" + + "clYKzNwmgwbdtmVAXmQxLuhmEpXfstIzkBrNJzChzb2onNSfa+r5L6XEHNHl7wCw\n" + + "TuuV/JWldNuYXLfVfuv3msfSjSWkv6aRtRWIvmOv0Qba2o05LlwFMd1PzKM5uN4D\n" + + "DYtsS9A6yQOXEsvUkWcLOJnCs8SkJRdXhJTxdmzeBqM1JttKwLbgGMbpjbxlg3ns\n" + + "N+Z+sEFox+2ZWOglgnBHj0mCZOiAC8wqUu+sxsLT4WndaPWKVqoRQChvDaZaNOaN\n" + + "qHciF9HPUcfZow+fH8TnSHneiQcDe6XcMhSaQ2MtpY8/jrgNKguZt22yH9gw/VpT\n" + + "3/QOB7FBgKFIEbvUaf3nVjFIlryIheg+LeiBd2isoMNNXaBwcg2YXukxJTAjBgkq\n" + + "hkiG9w0BCRUxFgQUwWO5DorvVWYF3BWUmAw0rUEajScwfDBtMEkGCSqGSIb3DQEF\n" + + "DjA8MCwGCSqGSIb3DQEFDDAfBAgUr2yP+/DBrgICCAACASAwDAYIKoZIhvcNAgsF\n" + + "ADAMBggqhkiG9w0CCQUABCA5zFL93jw8ItGlcbHKhqkNwbgpp6layuOuxSju4/Vd\n" + + "6QQITk9UIFVTRUQCAQE="); + + // Valid PKCS #12 File with SHA-512 HMAC and PRF + private static final byte[] pkcs12WithPBMac1PBKdf2_a3 = Base64.decode("MIIKrAIBAzCCCgUGCSqGSIb3DQEHAaCCCfYEggnyMIIJ7jCCBGIGCSqGSIb3DQEH\n" + + "BqCCBFMwggRPAgEAMIIESAYJKoZIhvcNAQcBMFcGCSqGSIb3DQEFDTBKMCkGCSqG\n" + + "SIb3DQEFDDAcBAisrqL8obSBaQICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQME\n" + + "ASoEECjXYYca0pwsgn1Imb9WqFGAggPgT7RcF5YzEJANZU9G3tSdpCHnyWatTlhm\n" + + "iCEcBGgwI5gz0+GoX+JCojgYY4g+KxeqznyCu+6GeD00T4Em7SWme9nzAfBFzng0\n" + + "3lYCSnahSEKfgHerbzAtq9kgXkclPVk0Liy92/buf0Mqotjjs/5o78AqP86Pwbj8\n" + + "xYNuXOU1ivO0JiW2c2HefKYvUvMYlOh99LCoZPLHPkaaZ4scAwDjFeTICU8oowVk\n" + + "LKvslrg1pHbfmXHMFJ4yqub37hRtj2CoJNy4+UA2hBYlBi9WnuAJIsjv0qS3kpLe\n" + + "4+J2DGe31GNG8pD01XD0l69OlailK1ykh4ap2u0KeD2z357+trCFbpWMMXQcSUCO\n" + + "OcVjxYqgv/l1++9huOHoPSt224x4wZfJ7cO2zbAAx/K2CPhdvi4CBaDHADsRq/c8\n" + + "SAi+LX5SCocGT51zL5KQD6pnr2ExaVum+U8a3nMPPMv9R2MfFUksYNGgFvS+lcZf\n" + + "R3qk/G9iXtSgray0mwRA8pWzoXl43vc9HJuuCU+ryOc/h36NChhQ9ltivUNaiUc2\n" + + "b9AAQSrZD8Z7KtxjbH3noS+gjDtimDB0Uh199zaCwQ95y463zdYsNCESm1OT979o\n" + + "Y+81BWFMFM/Hog5s7Ynhoi2E9+ZlyLK2UeKwvWjGzvcdPvxHR+5l/h6PyWROlpaZ\n" + + "zmzZBm+NKmbXtMD2AEa5+Q32ZqJQhijXZyIji3NS65y81j/a1ZrvU0lOVKA+MSPN\n" + + "KU27/eKZuF1LEL6qaazTUmpznLLdaVQy5aZ1qz5dyCziKcuHIclhh+RCblHU6XdE\n" + + "6pUTZSRQQiGUIkPUTnU9SFlZc7VwvxgeynLyXPCSzOKNWYGajy1LxDvv28uhMgNd\n" + + "WF51bNkl1QYl0fNunGO7YFt4wk+g7CQ/Yu2w4P7S3ZLMw0g4eYclcvyIMt4vxXfp\n" + + "VTKIPyzMqLr+0dp1eCPm8fIdaBZUhMUC/OVqLwgnPNY9cXCrn2R1cGKo5LtvtjbH\n" + + "2skz/D5DIOErfZSBJ8LE3De4j8MAjOeC8ia8LaM4PNfW/noQP1LBsZtTDTqEy01N\n" + + "Z5uliIocyQzlyWChErJv/Wxh+zBpbk1iXc2Owmh2GKjx0VSe7XbiqdoKkONUNUIE\n" + + "siseASiU/oXdJYUnBYVEUDJ1HPz7qnKiFhSgxNJZnoPfzbbx1hEzV+wxQqNnWIqQ\n" + + "U0s7Jt22wDBzPBHGao2tnGRLuBZWVePJGbsxThGKwrf3vYsNJTxme5KJiaxcPMwE\n" + + "r+ln2AqVOzzXHXgIxv/dvK0Qa7pH3AvGzcFjQChTRipgqiRrLor0//8580h+Ly2l\n" + + "IFo7bCuztmcwggWEBgkqhkiG9w0BBwGgggV1BIIFcTCCBW0wggVpBgsqhkiG9w0B\n" + + "DAoBAqCCBTEwggUtMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAi1c7S5\n" + + "IEG77wICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEN6rzRtIdYxqOnY+\n" + + "aDS3AFYEggTQNdwUoZDXCryOFBUI/z71vfoyAxlnwJLRHNXQUlI7w0KkH22aNnSm\n" + + "xiaXHoCP1HgcmsYORS7p/ITi/9atCHqnGR4zHmePNhoMpNHFehdjlUUWgt004vUJ\n" + + "5ZwTdXweM+K4We6CfWA/tyvsyGNAsuunel+8243Zsv0mGLKpjA+ZyALt51s0knmX\n" + + "OD2DW49FckImUVnNC5LmvEIAmVC/ZNycryZQI+2EBkJKe+BC3834GexJnSwtUBg3\n" + + "Xg33ZV7X66kw8tK1Ws5zND5GQAJyIu47mnjZkIWQBY+XbWowrBZ8uXIQuxMZC0p8\n" + + "u62oIAtZaVQoVTR1LyR/7PISFW6ApwtbTn6uQxsb16qF8lEM0S1+x0AfJY6Zm11t\n" + + "yCqbb2tYZF+X34MoUkR/IYC/KCq/KJdpnd8Yqgfrwjg8dR2WGIxbp2GBHq6BK/DI\n" + + "ehOLMcLcsOuP0DEXppfcelMOGNIs+4h4KsjWiHVDMPsqLdozBdm6FLGcno3lY5FO\n" + + "+avVrlElAOB+9evgaBbD2lSrEMoOjAoD090tgXXwYBEnWnIpdk+56cf5IpshrLBA\n" + + "/+H13LBLes+X1o5dd0Mu+3abp5RtAv7zLPRRtXkDYJPzgNcTvJ2Wxw2C+zrAclzZ\n" + + "7IRdcLESUa4CsN01aEvQgOtkCNVjSCtkJGP0FstsWM4hP7lfSB7P2tDL+ugy6GvB\n" + + "X1sz9fMC7QMAFL98nDm/yqcnejG1BcQXZho8n0svSfbcVByGlPZGMuI9t25+0B2M\n" + + "TAx0f6zoD8+fFmhcVgS6MQPybGKFawckYl0zulsePqs+G4voIW17owGKsRiv06Jm\n" + + "ZSwd3KoGmjM49ADzuG9yrQ5PSa0nhVk1tybNape4HNYHrAmmN0ILlN+E0Bs/Edz4\n" + + "ntYZuoc/Z35tCgm79dV4/Vl6HUZ1JrLsLrEWCByVytwVFyf3/MwTWdf+Ac+XzBuC\n" + + "yEMqPlvnPWswdnaid35pxios79fPl1Hr0/Q6+DoA5GyYq8SFdP7EYLrGMGa5GJ+x\n" + + "5nS7z6U4UmZ2sXuKYHnuhB0zi6Y04a+fhT71x02eTeC7aPlEB319UqysujJVJnso\n" + + "bkcwOu/Jj0Is9YeFd693dB44xeZuYyvlwoD19lqcim0TSa2Tw7D1W/yu47dKrVP2\n" + + "VKxRqomuAQOpoZiuSfq1/7ysrV8U4hIlIU2vnrSVJ8EtPQKsoBW5l70dQGwXyxBk\n" + + "BUTHqfJ4LG/kPGRMOtUzgqFw2DjJtbym1q1MZgp2ycMon4vp7DeQLGs2XfEANB+Y\n" + + "nRwtjpevqAnIuK6K3Y02LY4FXTNQpC37Xb04bmdIQAcE0MaoP4/hY87aS82PQ68g\n" + + "3bI79uKo4we2g+WaEJlEzQ7147ZzV2wbDq89W69x1MWTfaDwlEtd4UaacYchAv7B\n" + + "TVaaVFiRAUywWaHGePpZG2WV1feH/zd+temxWR9qMFgBZySg1jipBPVciwl0LqlW\n" + + "s/raIBYmLmAaMMgM3759UkNVznDoFHrY4z2EADXp0RHHVzJS1x+yYvp/9I+AcW55\n" + + "oN0UP/3uQ6eyz/ix22sovQwhMJ8rmgR6CfyRPKmXu1RPK3puNv7mbFTfTXpYN2vX\n" + + "vhEZReXY8hJF/9o4G3UrJ1F0MgUHMCG86cw1z0bhPSaXVoufOnx/fRoxJTAjBgkq\n" + + "hkiG9w0BCRUxFgQUwWO5DorvVWYF3BWUmAw0rUEajScwgZ0wgY0wSQYJKoZIhvcN\n" + + "AQUOMDwwLAYJKoZIhvcNAQUMMB8ECFDaXOUaOcUPAgIIAAIBQDAMBggqhkiG9w0C\n" + + "CwUAMAwGCCqGSIb3DQILBQAEQHIAM8C9OAsHUCj9CmOJioqf7YwD4O/b3UiZ3Wqo\n" + + "F6OmQIRDc68SdkZJ6024l4nWlnhTE7a4lb2Tru4k3NOTa1oECE5PVCBVU0VEAgEB"); + + // Invalid PKCS #12 File with Incorrect Iteration Count + private static final byte[] pkcs12WithPBMac1PBKdf2_a4 = Base64.decode("MIIKiwIBAzCCCgUGCSqGSIb3DQEHAaCCCfYEggnyMIIJ7jCCBGIGCSqGSIb3DQEH\n" + + "BqCCBFMwggRPAgEAMIIESAYJKoZIhvcNAQcBMFcGCSqGSIb3DQEFDTBKMCkGCSqG\n" + + "SIb3DQEFDDAcBAg9pxXxY2yscwICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQME\n" + + "ASoEEK7yYaFQDi1pYwWzm9F/fs+AggPgFIT2XapyaFgDppdvLkdvaF3HXw+zjzKb\n" + + "7xFC76DtVPhVTWVHD+kIss+jsj+XyvMwY0aCuAhAG/Dig+vzWomnsqB5ssw5/kTb\n" + + "+TMQ5PXLkNeoBmB6ArKeGc/QmCBQvQG/a6b+nXSWmxNpP+71772dmWmB8gcSJ0kF\n" + + "Fj75NrIbmNiDMCb71Q8gOzBMFf6BpXf/3xWAJtxyic+tSNETfOJa8zTZb0+lV0w9\n" + + "5eUmDrPUpuxEVbb0KJtIc63gRkcfrPtDd6Ii4Zzbzj2Evr4/S4hnrQBsiryVzJWy\n" + + "IEjaD0y6+DmG0JwMgRuGi1wBoGowi37GMrDCOyOZWC4n5wHLtYyhR6JaElxbrhxP\n" + + "H46z2USLKmZoF+YgEQgYcSBXMgP0t36+XQocFWYi2N5niy02TnctwF430FYsQlhJ\n" + + "Suma4I33E808dJuMv8T/soF66HsD4Zj46hOf4nWmas7IaoSAbGKXgIa7KhGRJvij\n" + + "xM3WOX0aqNi/8bhnxSA7fCmIy/7opyx5UYJFWGBSmHP1pBHBVmx7Ad8SAsB9MSsh\n" + + "nbGjGiUk4h0QcOi29/M9WwFlo4urePyI8PK2qtVAmpD3rTLlsmgzguZ69L0Q/CFU\n" + + "fbtqsMF0bgEuh8cfivd1DYFABEt1gypuwCUtCqQ7AXK2nQqOjsQCxVz9i9K8NDeD\n" + + "aau98VAl0To2sk3/VR/QUq0PRwU1jPN5BzUevhE7SOy/ImuJKwpGqqFljYdrQmj5\n" + + "jDe+LmYH9QGVRlfN8zuU+48FY8CAoeBeHn5AAPml0PYPVUnt3/jQN1+v+CahNVI+\n" + + "La8q1Nen+j1R44aa2I3y/pUgtzXRwK+tPrxTQbG030EU51LYJn8amPWmn3w75ZIA\n" + + "MJrXWeKj44de7u4zdUsEBVC2uM44rIHM8MFjyYAwYsey0rcp0emsaxzar+7ZA67r\n" + + "lDoXvvS3NqsnTXHcn3T9tkPRoee6L7Dh3x4Od96lcRwgdYT5BwyH7e34ld4VTUmJ\n" + + "bDEq7Ijvn4JKrwQJh1RCC+Z/ObfkC42xAm7G010u3g08xB0Qujpdg4a7VcuWrywF\n" + + "c7hLNquuaF4qoDaVwYXHH3iuX6YlJ/3siTKbYCVXPEZOAMBP9lF/OU76UMJBQNfU\n" + + "0xjDx+3AhUVgnGuCsmYlK6ETDp8qOZKGyV0KrNSGtqLx3uMhd7PETeW+ML3tDQ/0\n" + + "X9fMkcZHi4C2fXnoHV/qa2dGhBj4jjQ0Xh1poU6mxGn2Mebe2hDsBZkkBpnn7pK4\n" + + "wP/VqXdQTwqEuvzGHLVFsCuADe40ZFBmtBrf70wG7ZkO8SUZ8Zz1IX3+S024g7yj\n" + + "QRev/6x6TtkwggWEBgkqhkiG9w0BBwGgggV1BIIFcTCCBW0wggVpBgsqhkiG9w0B\n" + + "DAoBAqCCBTEwggUtMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhTxzw+\n" + + "VptrYAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEK9nSqc1I2t4tMVG\n" + + "bWHpdtQEggTQzCwI7j34gCTvfj6nuOSndAjShGv7mN2j7WMV0pslTpq2b9Bn3vn1\n" + + "Y0JMvL4E7sLrUzNU02pdOcfCnEpMFccNv2sQrLp1mOCKxu8OjSqHZLoKVL0ROVsZ\n" + + "8dMECLLigDlPKRiSyLErl14tErX4/zbkUaWMROO28kFbTbubQ8YoHlRUwsKW1xLg\n" + + "vfi0gRkG/zHXRfQHjX/8NStv7hXlehn7/Gy2EKPsRFhadm/iUHAfmCMkMgHTU248\n" + + "JER9+nsXltd59H+IeDpj/kbxZ+YvHow9XUZKu828d3MQnUpLZ1BfJGhMBPVwbVUD\n" + + "A40CiQBVdCoGtPJyalL28xoS3H0ILFCnwQOr6u0HwleNJPGHq78HUyH6Hwxnh0b0\n" + + "5o163r6wTFZn5cMOxpbs/Ttd+3TrxmrYpd2XnuRme3cnaYJ0ILvpc/8eLLR7SKjD\n" + + "T4JhZ0h/CfcV2WWvhpQugkY0pWrZ+EIMneB1dZB96mJVLxOi148OeSgi0PsxZMNi\n" + + "YM33rTpwQT5WqOsEyDwUQpne5b8Kkt/s7EN0LJNnPyJJRL1LcqOdr6j+6YqRtPa7\n" + + "a9oWJqMcuTP+bqzGRJh+3HDlFBw2Yzp9iadv4KmB2MzhStLUoi2MSjvnnkkd5Led\n" + + "sshAd6WbKfF7kLAHQHT4Ai6dMEO4EKkEVF9JBtxCR4JEn6C98Lpg+Lk+rfY7gHOf\n" + + "ZxtgGURwgXRY3aLUrdT55ZKgk3ExVKPzi5EhdpAau7JKhpOwyKozAp/OKWMNrz6h\n" + + "obu2Mbn1B+IA60psYHHxynBgsJHv7WQmbYh8HyGfHgVvaA8pZCYqxxjpLjSJrR8B\n" + + "Bu9H9xkTh7KlhxgreXYv19uAYbUd95kcox9izad6VPnovgFSb+Omdy6PJACPj6hF\n" + + "W6PJbucP0YPpO0VtWtQdZZ3df1P0hZ7qvKwOPFA+gKZSckgqASfygiP9V3Zc8jIi\n" + + "wjNzoDM2QT+UUJKiiGYXJUEOO9hxzFHlGj759DcNRhpgl5AgR57ofISD9yBuCAJY\n" + + "PQ/aZHPFuRTrcVG3RaIbCAS73nEznKyFaLOXfzyfyaSmyhsH253tnyL1MejC+2bR\n" + + "Eko/yldgFUxvU5JI+Q3KJ6Awj+PnduHXx71E4UwSuu2xXYMpxnQwI6rroQpZBX82\n" + + "HhqgcLV83P8lpzQwPdHjH5zkoxmWdC0+jU/tcQfNXYpJdyoaX7tDmVclLhwl9ps/\n" + + "O841pIsNLJWXwvxG6B+3LN/kw4QjwN194PopiOD7+oDm5mhttO78CrBrRxHMD/0Q\n" + + "qniZjKzSZepxlZq+J792u8vtMnuzzChxu0Bf3PhIXcJNcVhwUtr0yKe/N+NvC0tm\n" + + "p8wyik/BlndxN9eKbdTOi2wIi64h2QG8nOk66wQ/PSIJYwZl6eDNEQSzH/1mGCfU\n" + + "QnUT17UC/p+Qgenf6Auap2GWlvsJrB7u/pytz65rtjt/ouo6Ih6EwWqwVVpGXZD0\n" + + "7gVWH0Ke/Vr6aPGNvkLcmftPuDZsn9jiig3guhdeyRVf10Ox369kKWcG75q77hxE\n" + + "IzSzDyUlBNbnom9SIjut3r+qVYmWONatC6q/4D0I42Lnjd3dEyZx7jmH3g/S2ASM\n" + + "FzWr9pvXc61dsYOkdZ4PYa9XPUZxXFagZsoS3F1sU799+IJVU0tC0MExJTAjBgkq\n" + + "hkiG9w0BCRUxFgQUwWO5DorvVWYF3BWUmAw0rUEajScwfTBtMEkGCSqGSIb3DQEF\n" + + "DjA8MCwGCSqGSIb3DQEFDDAfBAhvRzw4sC4xcwICCAECASAwDAYIKoZIhvcNAgkF\n" + + "ADAMBggqhkiG9w0CCQUABCB6pW2FOdcCNj87zS64NUXG36K5aXDnFHctIk5Bf4kG\n" + + "3QQITk9UIFVTRUQCAggA"); + + // Invalid PKCS #12 File with Incorrect Salt + private static final byte[] pkcs12WithPBMac1PBKdf2_a5 = Base64.decode("MIIKigIBAzCCCgUGCSqGSIb3DQEHAaCCCfYEggnyMIIJ7jCCBGIGCSqGSIb3DQEH\n" + + "BqCCBFMwggRPAgEAMIIESAYJKoZIhvcNAQcBMFcGCSqGSIb3DQEFDTBKMCkGCSqG\n" + + "SIb3DQEFDDAcBAg9pxXxY2yscwICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQME\n" + + "ASoEEK7yYaFQDi1pYwWzm9F/fs+AggPgFIT2XapyaFgDppdvLkdvaF3HXw+zjzKb\n" + + "7xFC76DtVPhVTWVHD+kIss+jsj+XyvMwY0aCuAhAG/Dig+vzWomnsqB5ssw5/kTb\n" + + "+TMQ5PXLkNeoBmB6ArKeGc/QmCBQvQG/a6b+nXSWmxNpP+71772dmWmB8gcSJ0kF\n" + + "Fj75NrIbmNiDMCb71Q8gOzBMFf6BpXf/3xWAJtxyic+tSNETfOJa8zTZb0+lV0w9\n" + + "5eUmDrPUpuxEVbb0KJtIc63gRkcfrPtDd6Ii4Zzbzj2Evr4/S4hnrQBsiryVzJWy\n" + + "IEjaD0y6+DmG0JwMgRuGi1wBoGowi37GMrDCOyOZWC4n5wHLtYyhR6JaElxbrhxP\n" + + "H46z2USLKmZoF+YgEQgYcSBXMgP0t36+XQocFWYi2N5niy02TnctwF430FYsQlhJ\n" + + "Suma4I33E808dJuMv8T/soF66HsD4Zj46hOf4nWmas7IaoSAbGKXgIa7KhGRJvij\n" + + "xM3WOX0aqNi/8bhnxSA7fCmIy/7opyx5UYJFWGBSmHP1pBHBVmx7Ad8SAsB9MSsh\n" + + "nbGjGiUk4h0QcOi29/M9WwFlo4urePyI8PK2qtVAmpD3rTLlsmgzguZ69L0Q/CFU\n" + + "fbtqsMF0bgEuh8cfivd1DYFABEt1gypuwCUtCqQ7AXK2nQqOjsQCxVz9i9K8NDeD\n" + + "aau98VAl0To2sk3/VR/QUq0PRwU1jPN5BzUevhE7SOy/ImuJKwpGqqFljYdrQmj5\n" + + "jDe+LmYH9QGVRlfN8zuU+48FY8CAoeBeHn5AAPml0PYPVUnt3/jQN1+v+CahNVI+\n" + + "La8q1Nen+j1R44aa2I3y/pUgtzXRwK+tPrxTQbG030EU51LYJn8amPWmn3w75ZIA\n" + + "MJrXWeKj44de7u4zdUsEBVC2uM44rIHM8MFjyYAwYsey0rcp0emsaxzar+7ZA67r\n" + + "lDoXvvS3NqsnTXHcn3T9tkPRoee6L7Dh3x4Od96lcRwgdYT5BwyH7e34ld4VTUmJ\n" + + "bDEq7Ijvn4JKrwQJh1RCC+Z/ObfkC42xAm7G010u3g08xB0Qujpdg4a7VcuWrywF\n" + + "c7hLNquuaF4qoDaVwYXHH3iuX6YlJ/3siTKbYCVXPEZOAMBP9lF/OU76UMJBQNfU\n" + + "0xjDx+3AhUVgnGuCsmYlK6ETDp8qOZKGyV0KrNSGtqLx3uMhd7PETeW+ML3tDQ/0\n" + + "X9fMkcZHi4C2fXnoHV/qa2dGhBj4jjQ0Xh1poU6mxGn2Mebe2hDsBZkkBpnn7pK4\n" + + "wP/VqXdQTwqEuvzGHLVFsCuADe40ZFBmtBrf70wG7ZkO8SUZ8Zz1IX3+S024g7yj\n" + + "QRev/6x6TtkwggWEBgkqhkiG9w0BBwGgggV1BIIFcTCCBW0wggVpBgsqhkiG9w0B\n" + + "DAoBAqCCBTEwggUtMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhTxzw+\n" + + "VptrYAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEK9nSqc1I2t4tMVG\n" + + "bWHpdtQEggTQzCwI7j34gCTvfj6nuOSndAjShGv7mN2j7WMV0pslTpq2b9Bn3vn1\n" + + "Y0JMvL4E7sLrUzNU02pdOcfCnEpMFccNv2sQrLp1mOCKxu8OjSqHZLoKVL0ROVsZ\n" + + "8dMECLLigDlPKRiSyLErl14tErX4/zbkUaWMROO28kFbTbubQ8YoHlRUwsKW1xLg\n" + + "vfi0gRkG/zHXRfQHjX/8NStv7hXlehn7/Gy2EKPsRFhadm/iUHAfmCMkMgHTU248\n" + + "JER9+nsXltd59H+IeDpj/kbxZ+YvHow9XUZKu828d3MQnUpLZ1BfJGhMBPVwbVUD\n" + + "A40CiQBVdCoGtPJyalL28xoS3H0ILFCnwQOr6u0HwleNJPGHq78HUyH6Hwxnh0b0\n" + + "5o163r6wTFZn5cMOxpbs/Ttd+3TrxmrYpd2XnuRme3cnaYJ0ILvpc/8eLLR7SKjD\n" + + "T4JhZ0h/CfcV2WWvhpQugkY0pWrZ+EIMneB1dZB96mJVLxOi148OeSgi0PsxZMNi\n" + + "YM33rTpwQT5WqOsEyDwUQpne5b8Kkt/s7EN0LJNnPyJJRL1LcqOdr6j+6YqRtPa7\n" + + "a9oWJqMcuTP+bqzGRJh+3HDlFBw2Yzp9iadv4KmB2MzhStLUoi2MSjvnnkkd5Led\n" + + "sshAd6WbKfF7kLAHQHT4Ai6dMEO4EKkEVF9JBtxCR4JEn6C98Lpg+Lk+rfY7gHOf\n" + + "ZxtgGURwgXRY3aLUrdT55ZKgk3ExVKPzi5EhdpAau7JKhpOwyKozAp/OKWMNrz6h\n" + + "obu2Mbn1B+IA60psYHHxynBgsJHv7WQmbYh8HyGfHgVvaA8pZCYqxxjpLjSJrR8B\n" + + "Bu9H9xkTh7KlhxgreXYv19uAYbUd95kcox9izad6VPnovgFSb+Omdy6PJACPj6hF\n" + + "W6PJbucP0YPpO0VtWtQdZZ3df1P0hZ7qvKwOPFA+gKZSckgqASfygiP9V3Zc8jIi\n" + + "wjNzoDM2QT+UUJKiiGYXJUEOO9hxzFHlGj759DcNRhpgl5AgR57ofISD9yBuCAJY\n" + + "PQ/aZHPFuRTrcVG3RaIbCAS73nEznKyFaLOXfzyfyaSmyhsH253tnyL1MejC+2bR\n" + + "Eko/yldgFUxvU5JI+Q3KJ6Awj+PnduHXx71E4UwSuu2xXYMpxnQwI6rroQpZBX82\n" + + "HhqgcLV83P8lpzQwPdHjH5zkoxmWdC0+jU/tcQfNXYpJdyoaX7tDmVclLhwl9ps/\n" + + "O841pIsNLJWXwvxG6B+3LN/kw4QjwN194PopiOD7+oDm5mhttO78CrBrRxHMD/0Q\n" + + "qniZjKzSZepxlZq+J792u8vtMnuzzChxu0Bf3PhIXcJNcVhwUtr0yKe/N+NvC0tm\n" + + "p8wyik/BlndxN9eKbdTOi2wIi64h2QG8nOk66wQ/PSIJYwZl6eDNEQSzH/1mGCfU\n" + + "QnUT17UC/p+Qgenf6Auap2GWlvsJrB7u/pytz65rtjt/ouo6Ih6EwWqwVVpGXZD0\n" + + "7gVWH0Ke/Vr6aPGNvkLcmftPuDZsn9jiig3guhdeyRVf10Ox369kKWcG75q77hxE\n" + + "IzSzDyUlBNbnom9SIjut3r+qVYmWONatC6q/4D0I42Lnjd3dEyZx7jmH3g/S2ASM\n" + + "FzWr9pvXc61dsYOkdZ4PYa9XPUZxXFagZsoS3F1sU799+IJVU0tC0MExJTAjBgkq\n" + + "hkiG9w0BCRUxFgQUwWO5DorvVWYF3BWUmAw0rUEajScwfDBtMEkGCSqGSIb3DQEF\n" + + "DjA8MCwGCSqGSIb3DQEFDDAfBAhOT1QgVVNFRAICCAACASAwDAYIKoZIhvcNAgkF\n" + + "ADAMBggqhkiG9w0CCQUABCB6pW2FOdcCNj87zS64NUXG36K5aXDnFHctIk5Bf4kG\n" + + "3QQIb0c8OLAuMXMCAQE="); + + // Invalid PKCS #12 File with Missing Key Length + private static final byte[] pkcs12WithPBMac1PBKdf2_a6 = Base64.decode("MIIKiAIBAzCCCgUGCSqGSIb3DQEHAaCCCfYEggnyMIIJ7jCCBGIGCSqGSIb3DQEH\n" + + "BqCCBFMwggRPAgEAMIIESAYJKoZIhvcNAQcBMFcGCSqGSIb3DQEFDTBKMCkGCSqG\n" + + "SIb3DQEFDDAcBAg9pxXxY2yscwICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQME\n" + + "ASoEEK7yYaFQDi1pYwWzm9F/fs+AggPgFIT2XapyaFgDppdvLkdvaF3HXw+zjzKb\n" + + "7xFC76DtVPhVTWVHD+kIss+jsj+XyvMwY0aCuAhAG/Dig+vzWomnsqB5ssw5/kTb\n" + + "+TMQ5PXLkNeoBmB6ArKeGc/QmCBQvQG/a6b+nXSWmxNpP+71772dmWmB8gcSJ0kF\n" + + "Fj75NrIbmNiDMCb71Q8gOzBMFf6BpXf/3xWAJtxyic+tSNETfOJa8zTZb0+lV0w9\n" + + "5eUmDrPUpuxEVbb0KJtIc63gRkcfrPtDd6Ii4Zzbzj2Evr4/S4hnrQBsiryVzJWy\n" + + "IEjaD0y6+DmG0JwMgRuGi1wBoGowi37GMrDCOyOZWC4n5wHLtYyhR6JaElxbrhxP\n" + + "H46z2USLKmZoF+YgEQgYcSBXMgP0t36+XQocFWYi2N5niy02TnctwF430FYsQlhJ\n" + + "Suma4I33E808dJuMv8T/soF66HsD4Zj46hOf4nWmas7IaoSAbGKXgIa7KhGRJvij\n" + + "xM3WOX0aqNi/8bhnxSA7fCmIy/7opyx5UYJFWGBSmHP1pBHBVmx7Ad8SAsB9MSsh\n" + + "nbGjGiUk4h0QcOi29/M9WwFlo4urePyI8PK2qtVAmpD3rTLlsmgzguZ69L0Q/CFU\n" + + "fbtqsMF0bgEuh8cfivd1DYFABEt1gypuwCUtCqQ7AXK2nQqOjsQCxVz9i9K8NDeD\n" + + "aau98VAl0To2sk3/VR/QUq0PRwU1jPN5BzUevhE7SOy/ImuJKwpGqqFljYdrQmj5\n" + + "jDe+LmYH9QGVRlfN8zuU+48FY8CAoeBeHn5AAPml0PYPVUnt3/jQN1+v+CahNVI+\n" + + "La8q1Nen+j1R44aa2I3y/pUgtzXRwK+tPrxTQbG030EU51LYJn8amPWmn3w75ZIA\n" + + "MJrXWeKj44de7u4zdUsEBVC2uM44rIHM8MFjyYAwYsey0rcp0emsaxzar+7ZA67r\n" + + "lDoXvvS3NqsnTXHcn3T9tkPRoee6L7Dh3x4Od96lcRwgdYT5BwyH7e34ld4VTUmJ\n" + + "bDEq7Ijvn4JKrwQJh1RCC+Z/ObfkC42xAm7G010u3g08xB0Qujpdg4a7VcuWrywF\n" + + "c7hLNquuaF4qoDaVwYXHH3iuX6YlJ/3siTKbYCVXPEZOAMBP9lF/OU76UMJBQNfU\n" + + "0xjDx+3AhUVgnGuCsmYlK6ETDp8qOZKGyV0KrNSGtqLx3uMhd7PETeW+ML3tDQ/0\n" + + "X9fMkcZHi4C2fXnoHV/qa2dGhBj4jjQ0Xh1poU6mxGn2Mebe2hDsBZkkBpnn7pK4\n" + + "wP/VqXdQTwqEuvzGHLVFsCuADe40ZFBmtBrf70wG7ZkO8SUZ8Zz1IX3+S024g7yj\n" + + "QRev/6x6TtkwggWEBgkqhkiG9w0BBwGgggV1BIIFcTCCBW0wggVpBgsqhkiG9w0B\n" + + "DAoBAqCCBTEwggUtMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAhTxzw+\n" + + "VptrYAICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEK9nSqc1I2t4tMVG\n" + + "bWHpdtQEggTQzCwI7j34gCTvfj6nuOSndAjShGv7mN2j7WMV0pslTpq2b9Bn3vn1\n" + + "Y0JMvL4E7sLrUzNU02pdOcfCnEpMFccNv2sQrLp1mOCKxu8OjSqHZLoKVL0ROVsZ\n" + + "8dMECLLigDlPKRiSyLErl14tErX4/zbkUaWMROO28kFbTbubQ8YoHlRUwsKW1xLg\n" + + "vfi0gRkG/zHXRfQHjX/8NStv7hXlehn7/Gy2EKPsRFhadm/iUHAfmCMkMgHTU248\n" + + "JER9+nsXltd59H+IeDpj/kbxZ+YvHow9XUZKu828d3MQnUpLZ1BfJGhMBPVwbVUD\n" + + "A40CiQBVdCoGtPJyalL28xoS3H0ILFCnwQOr6u0HwleNJPGHq78HUyH6Hwxnh0b0\n" + + "5o163r6wTFZn5cMOxpbs/Ttd+3TrxmrYpd2XnuRme3cnaYJ0ILvpc/8eLLR7SKjD\n" + + "T4JhZ0h/CfcV2WWvhpQugkY0pWrZ+EIMneB1dZB96mJVLxOi148OeSgi0PsxZMNi\n" + + "YM33rTpwQT5WqOsEyDwUQpne5b8Kkt/s7EN0LJNnPyJJRL1LcqOdr6j+6YqRtPa7\n" + + "a9oWJqMcuTP+bqzGRJh+3HDlFBw2Yzp9iadv4KmB2MzhStLUoi2MSjvnnkkd5Led\n" + + "sshAd6WbKfF7kLAHQHT4Ai6dMEO4EKkEVF9JBtxCR4JEn6C98Lpg+Lk+rfY7gHOf\n" + + "ZxtgGURwgXRY3aLUrdT55ZKgk3ExVKPzi5EhdpAau7JKhpOwyKozAp/OKWMNrz6h\n" + + "obu2Mbn1B+IA60psYHHxynBgsJHv7WQmbYh8HyGfHgVvaA8pZCYqxxjpLjSJrR8B\n" + + "Bu9H9xkTh7KlhxgreXYv19uAYbUd95kcox9izad6VPnovgFSb+Omdy6PJACPj6hF\n" + + "W6PJbucP0YPpO0VtWtQdZZ3df1P0hZ7qvKwOPFA+gKZSckgqASfygiP9V3Zc8jIi\n" + + "wjNzoDM2QT+UUJKiiGYXJUEOO9hxzFHlGj759DcNRhpgl5AgR57ofISD9yBuCAJY\n" + + "PQ/aZHPFuRTrcVG3RaIbCAS73nEznKyFaLOXfzyfyaSmyhsH253tnyL1MejC+2bR\n" + + "Eko/yldgFUxvU5JI+Q3KJ6Awj+PnduHXx71E4UwSuu2xXYMpxnQwI6rroQpZBX82\n" + + "HhqgcLV83P8lpzQwPdHjH5zkoxmWdC0+jU/tcQfNXYpJdyoaX7tDmVclLhwl9ps/\n" + + "O841pIsNLJWXwvxG6B+3LN/kw4QjwN194PopiOD7+oDm5mhttO78CrBrRxHMD/0Q\n" + + "qniZjKzSZepxlZq+J792u8vtMnuzzChxu0Bf3PhIXcJNcVhwUtr0yKe/N+NvC0tm\n" + + "p8wyik/BlndxN9eKbdTOi2wIi64h2QG8nOk66wQ/PSIJYwZl6eDNEQSzH/1mGCfU\n" + + "QnUT17UC/p+Qgenf6Auap2GWlvsJrB7u/pytz65rtjt/ouo6Ih6EwWqwVVpGXZD0\n" + + "7gVWH0Ke/Vr6aPGNvkLcmftPuDZsn9jiig3guhdeyRVf10Ox369kKWcG75q77hxE\n" + + "IzSzDyUlBNbnom9SIjut3r+qVYmWONatC6q/4D0I42Lnjd3dEyZx7jmH3g/S2ASM\n" + + "FzWr9pvXc61dsYOkdZ4PYa9XPUZxXFagZsoS3F1sU799+IJVU0tC0MExJTAjBgkq\n" + + "hkiG9w0BCRUxFgQUwWO5DorvVWYF3BWUmAw0rUEajScwejBqMEYGCSqGSIb3DQEF\n" + + "DjA5MCkGCSqGSIb3DQEFDDAcBAhvRzw4sC4xcwICCAAwDAYIKoZIhvcNAgkFADAM\n" + + "BggqhkiG9w0CCQUABCB6pW2FOdcCNj87zS64NUXG36K5aXDnFHctIk5Bf4kG3QQI\n" + + "b0c8OLAuMXMCAggA"); + /** * we generate a self signed certificate for the sake of testing - RSA */ @@ -2145,6 +2500,52 @@ private void testIterationCount() System.clearProperty("org.bouncycastle.pkcs12.max_it_count"); } + private void testPBMac1PBKdf2() + throws Exception + { + KeyStore store = KeyStore.getInstance("PKCS12", BC); + final char[] password = "1234".toCharArray(); + ByteArrayInputStream stream; + // valid test vectors + for (byte[] test_vector : new byte[][]{pkcs12WithPBMac1PBKdf2_a1, pkcs12WithPBMac1PBKdf2_a2, pkcs12WithPBMac1PBKdf2_a3}) + { + stream = new ByteArrayInputStream(test_vector); + store.load(stream, password); + + try + { + store.load(stream, "not right".toCharArray()); + fail("no exception"); + } + catch (IOException ignored) {} + } + // invalid test vectors + for (byte[] test_vector : new byte[][]{pkcs12WithPBMac1PBKdf2_a4, pkcs12WithPBMac1PBKdf2_a5}) + { + stream = new ByteArrayInputStream(test_vector); + try + { + store.load(stream, password); + fail("no exception"); + } + catch (IOException e) + { + isTrue(e.getMessage().contains("PKCS12 key store mac invalid - wrong password or corrupted file.")); + } + } + // invalid test vector that throws exception + stream = new ByteArrayInputStream(pkcs12WithPBMac1PBKdf2_a6); + try + { + store.load(stream, password); + fail("no exception"); + } + catch (IOException e) + { + isTrue(e.getMessage().contains("Key length must be present when using PBMAC1.")); + } + } + private void testBCFKSLoad() throws Exception { @@ -2327,6 +2728,7 @@ public void performTest() { testPKCS12StoreFriendlyName(); testIterationCount(); + testPBMac1PBKdf2(); testPKCS12Store(); testGOSTStore(); testChainCycle(); From d1183b3e8e70f461749fac0df2dc66daa1fb61fd Mon Sep 17 00:00:00 2001 From: Jill Kleiber Date: Tue, 21 Jan 2025 13:20:14 +0100 Subject: [PATCH 149/890] added save tests to testPBMac1PBKdf2 test --- .../jce/provider/test/PKCS12StoreTest.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java index 9011fee6f7..3bfc031ef6 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java @@ -2509,6 +2509,9 @@ private void testPBMac1PBKdf2() // valid test vectors for (byte[] test_vector : new byte[][]{pkcs12WithPBMac1PBKdf2_a1, pkcs12WithPBMac1PBKdf2_a2, pkcs12WithPBMac1PBKdf2_a3}) { + // + // load test + // stream = new ByteArrayInputStream(test_vector); store.load(stream, password); @@ -2518,6 +2521,24 @@ private void testPBMac1PBKdf2() fail("no exception"); } catch (IOException ignored) {} + + // + // save test + // + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + store.store(bOut, passwd); + stream = new ByteArrayInputStream(bOut.toByteArray()); + store.load(stream, passwd); + + // + // save test using LoadStoreParameter + // + bOut = new ByteArrayOutputStream(); + PKCS12StoreParameter storeParam = new PKCS12StoreParameter(bOut, passwd, true); + store.store(storeParam); + byte[] data = bOut.toByteArray(); + stream = new ByteArrayInputStream(data); + store.load(stream, passwd); } // invalid test vectors for (byte[] test_vector : new byte[][]{pkcs12WithPBMac1PBKdf2_a4, pkcs12WithPBMac1PBKdf2_a5}) From 9ef9e788329bf2fe1f1cfa4ba861c8b4277e2791 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 4 Mar 2025 16:55:37 +1030 Subject: [PATCH 150/890] Refactor of Mayo --- .../pqc/crypto/mayo/GF16Utils.java | 4 +- .../pqc/crypto/mayo/MayoSigner.java | 71 +++++++-------- .../bouncycastle/pqc/crypto/mayo/Utils.java | 86 ++++++------------- .../java/org/bouncycastle/util/Bytes.java | 2 +- 4 files changed, 60 insertions(+), 103 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java index d367c94109..c12f35b289 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java @@ -82,7 +82,7 @@ static void mulAddMUpperTriangularMatXMat(int mVecLimbs, long[] bsMat, byte[] ma for (int k = 0, kmVecLimbs = 0; k < matCols; k++, kmVecLimbs += mVecLimbs) { // For acc: add into the m-vector at row r, column k. - mVecMulAdd(mVecLimbs, bsMat, bsMatEntriesUsed, mat[cmatCols + k] & 0xFF, acc, accOff + rmatColsmVecLimbs + kmVecLimbs); + mVecMulAdd(mVecLimbs, bsMat, bsMatEntriesUsed, mat[cmatCols + k], acc, accOff + rmatColsmVecLimbs + kmVecLimbs); } bsMatEntriesUsed += mVecLimbs; } @@ -256,7 +256,7 @@ static long mulFx8(byte a, long b) // Reduction mod (x^4 + x + 1): process each byte in parallel. long topP = p & 0xf0f0f0f0f0f0f0f0L; - return (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0f0f0f0f0f0f0f0fL; + return (p ^ (topP >>> 4) ^ (topP >>> 3)) & 0x0f0f0f0f0f0f0f0fL; } static void matMul(byte[] a, byte[] b, int bOff, byte[] c, int colrowAB, int rowA) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index 2e6fd8c96d..c5242b00bd 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -16,7 +16,7 @@ public class MayoSigner implements MessageSigner { private SecureRandom random; - MayoParameters params; + private MayoParameters params; private MayoPublicKeyParameters pubKey; private MayoPrivateKeyParameters privKey; @@ -57,6 +57,7 @@ public byte[] generateSignature(byte[] message) int v = params.getV(); int o = params.getO(); int n = params.getN(); + int m = params.getM(); int vbytes = params.getVBytes(); int oBytes = params.getOBytes(); int saltBytes = params.getSaltBytes(); @@ -66,16 +67,17 @@ public byte[] generateSignature(byte[] message) int digestBytes = params.getDigestBytes(); int skSeedBytes = params.getSkSeedBytes(); byte[] tenc = new byte[params.getMBytes()]; - byte[] t = new byte[params.getM()]; - byte[] y = new byte[params.getM()]; + byte[] t = new byte[m]; + byte[] y = new byte[m]; byte[] salt = new byte[saltBytes]; byte[] V = new byte[k * vbytes + params.getRBytes()]; byte[] Vdec = new byte[v * k]; int ok = k * o; - byte[] A = new byte[((params.getM() + 7) / 8 * 8) * (ok + 1)]; - byte[] x = new byte[k * n]; + int nk = k * n; + byte[] A = new byte[((m + 7) / 8 * 8) * (ok + 1)]; + byte[] x = new byte[nk]; byte[] r = new byte[ok + 1]; - byte[] s = new byte[k * n]; + byte[] s = new byte[nk]; byte[] tmp = new byte[digestBytes + saltBytes + skSeedBytes + 1]; byte[] sig = new byte[params.getSigBytes()]; long[] P = new long[p1Limbs + params.getP2Limbs()]; @@ -121,9 +123,9 @@ public byte[] generateSignature(byte[] message) { // Multiply the m-vector at P1 for the current matrix entry, // and accumulate into acc for row r. - GF16Utils.mVecMulAdd(mVecLimbs, P, bsMatEntriesUsed, O[co + j] & 0xFF, P, iomVecLimbs + jmVecLimbs); + GF16Utils.mVecMulAdd(mVecLimbs, P, bsMatEntriesUsed, O[co + j], P, iomVecLimbs + jmVecLimbs); // Similarly, accumulate into acc for row c. - GF16Utils.mVecMulAdd(mVecLimbs, P, bsMatEntriesUsed, O[io + j] & 0xFF, P, comVecLimbs + jmVecLimbs); + GF16Utils.mVecMulAdd(mVecLimbs, P, bsMatEntriesUsed, O[io + j], P, comVecLimbs + jmVecLimbs); } bsMatEntriesUsed += mVecLimbs; } @@ -150,9 +152,10 @@ public byte[] generateSignature(byte[] message) System.arraycopy(salt, 0, tmp, digestBytes, saltBytes); shake.update(tmp, 0, digestBytes + saltBytes); shake.doFinal(tenc, 0, params.getMBytes()); - Utils.decode(tenc, t, params.getM()); + Utils.decode(tenc, t, m); int size = v * k * mVecLimbs; long[] Pv = new long[size]; + byte[] Ox = new byte[v]; for (int ctr = 0; ctr <= 255; ctr++) { tmp[tmp.length - 1] = (byte)ctr; @@ -182,12 +185,12 @@ public byte[] generateSignature(byte[] message) computeA(Mtmp, A); // Clear trailing bytes -// for (int i = 0; i < params.getM(); ++i) +// for (int i = 0; i < m; ++i) // { // A[(i + 1) * (ok + 1) - 1] = 0; // } - Utils.decode(V, k * vbytes, r, 0, ok); + Utils.decode(V, k * vbytes, r, ok); if (sampleSolution(params, A, y, r, x)) { @@ -201,16 +204,16 @@ public byte[] generateSignature(byte[] message) } // Compute final signature components - byte[] Ox = new byte[v]; - for (int i = 0; i < k; i++) + + for (int i = 0, io = 0, in = 0, iv = 0; i < k; i++, io += o, in+= n, iv += v) { - GF16Utils.matMul(O, x, i * o, Ox, o, n - o); - Bytes.xor(v, Vdec, i * v, Ox, s, i * n); - System.arraycopy(x, i * o, s, i * n + n - o, o); + GF16Utils.matMul(O, x, io, Ox, o, v); + Bytes.xor(v, Vdec, iv, Ox, s, in); + System.arraycopy(x, io, s, in + v, o); } // Encode and add salt - Utils.encode(s, sig, n * k); + Utils.encode(s, sig, nk); System.arraycopy(salt, 0, sig, sig.length - saltBytes, saltBytes); return Arrays.concatenate(sig, message); @@ -294,13 +297,12 @@ void computeRHS(long[] vPv, byte[] t, byte[] y) final int k = params.getK(); final int[] fTail = params.getFTail(); - final int topPos = ((m - 1) & 15) * 4; + final int topPos = ((m - 1) & 15) << 2; // Zero out tails of m_vecs if necessary if ((m & 15) != 0) { - long mask = 1L << ((m & 15) << 2); - mask -= 1; + long mask = (1L << ((m & 15) << 2)) - 1; final int kSquared = k * k; for (int i = 0, index = mVecLimbs - 1; i < kSquared; i++, index += mVecLimbs) @@ -409,7 +411,7 @@ void computeA(long[] Mtmp, byte[] AOut) } } - for (int i = 0, io = 0; i < k; i++, io += o) + for (int i = 0, io = 0, iomVecLimbs = 0; i < k; i++, io += o, iomVecLimbs += omVecLimbs) { for (int j = k - 1, jomVecLimbs = j * omVecLimbs, jo = j * o; j >= i; j--, jomVecLimbs -= omVecLimbs, jo -= o) { @@ -433,13 +435,11 @@ void computeA(long[] Mtmp, byte[] AOut) if (i != j) { // Process Mi - int miOffset = i * mVecLimbs * o; for (int c = 0, cmVecLimbs = 0; c < o; c++, cmVecLimbs += mVecLimbs) { for (int limb = 0, limbAWidhth = 0; limb < mVecLimbs; limb++, limbAWidhth += AWidth) { - long value = Mtmp[miOffset + limb + cmVecLimbs]; - + long value = Mtmp[iomVecLimbs + limb + cmVecLimbs]; int aIndex = jo + c + wordsToShift + limbAWidhth; A[aIndex] ^= value << bitsToShift; @@ -461,7 +461,7 @@ void computeA(long[] Mtmp, byte[] AOut) } // Transpose blocks - for (int c = 0; c < AWidth * ((m + (k + 1) * k / 2 + 15) >>> 4); c += 16) + for (int c = 0; c < AWidth * ((m + (((k + 1) * k) >> 1) + 15) >>> 4); c += 16) { transpose16x16Nibbles(A, c); } @@ -554,8 +554,7 @@ private static void transpose16x16Nibbles(long[] M, int offset) } } - boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, - byte[] r, byte[] x) + boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, byte[] r, byte[] x) { final int k = params.getK(); final int o = params.getO(); @@ -576,9 +575,9 @@ boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, GF16Utils.matMul(A, r, 0, Ar, ok + 1, m); // Update last column of A with y - Ar - for (int i = 0; i < m; i++) + for (int i = 0, idx = ok; i < m; i++, idx += ok + 1) { - A[ok + i * (ok + 1)] = (byte)(y[i] ^ Ar[i]); + A[idx] = (byte)(y[i] ^ Ar[i]); } // Perform row echelon form transformation @@ -586,9 +585,9 @@ boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, // Check matrix rank boolean fullRank = false; - for (int i = 0; i < aCols - 1; i++) + for (int i = 0, idx = (m - 1) * aCols; i < aCols - 1; i++, idx++) { - fullRank |= (A[(m - 1) * aCols + i] != 0); + fullRank |= (A[idx] != 0); } if (!fullRank) { @@ -609,7 +608,6 @@ boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, byte u = (byte)(correctCol & ~finished & A[rowAcols + aCols - 1]); x[col] ^= u; - // Update matrix entries for (int i = 0, iaCols_col = col, iaCols_aCols1 = aCols - 1; i < row; i += 8, iaCols_col += aCols << 3, iaCols_aCols1 += aCols << 3) @@ -647,7 +645,7 @@ boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, void ef(byte[] A, int nrows, int ncols) { // Each 64-bit long can hold 16 nibbles (16 GF(16) elements). - int rowLen = (ncols + 15) / 16; + int rowLen = (ncols + 15) >> 4; // Allocate temporary arrays. long[] pivotRow = new long[rowLen]; @@ -686,11 +684,8 @@ void ef(byte[] A, int nrows, int ncols) int upperBound = Math.min(nrows - 1, pivotCol); // Zero out pivot row buffers. - for (int i = 0; i < rowLen; i++) - { - pivotRow[i] = 0; - pivotRow2[i] = 0; - } + Arrays.clear(pivotRow); + Arrays.clear(pivotRow2); // Try to select a pivot row in constant time. int pivot = 0; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java index 8d9b1ef47b..16b4922278 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java @@ -22,9 +22,7 @@ public class Utils */ public static void decode(byte[] m, byte[] mdec, int mdecLen) { - int i; - int decIndex = 0; - int blocks = mdecLen >> 1; + int i, decIndex = 0, blocks = mdecLen >> 1; // Process pairs of nibbles from each byte for (i = 0; i < blocks; i++) { @@ -34,7 +32,7 @@ public static void decode(byte[] m, byte[] mdec, int mdecLen) mdec[decIndex++] = (byte)((m[i] >> 4) & 0x0F); } // If there is an extra nibble (odd number of nibbles), decode only the lower nibble - if (mdecLen % 2 == 1) + if ((mdecLen & 1) == 1) { mdec[decIndex] = (byte)((m[i] & 0xFF) & 0x0F); } @@ -52,7 +50,7 @@ public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mde mdec[decIndex++] = (byte)((m[mOff++] >> 4) & 0x0F); } // If there is an extra nibble (odd number of nibbles), decode only the lower nibble - if (mdecLen % 2 == 1) + if ((mdecLen & 1) == 1) { mdec[decIndex] = (byte)(m[mOff] & 0x0F); } @@ -68,14 +66,13 @@ public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mde */ public static void decode(byte[] input, int inputOffset, byte[] output, int mdecLen) { - int decIndex = 0; - int blocks = mdecLen >> 1; + int decIndex = 0, blocks = mdecLen >> 1; for (int i = 0; i < blocks; i++) { output[decIndex++] = (byte)(input[inputOffset] & 0x0F); output[decIndex++] = (byte)((input[inputOffset++] >> 4) & 0x0F); } - if (mdecLen % 2 == 1) + if ((mdecLen & 1) == 1) { output[decIndex] = (byte)(input[inputOffset] & 0x0F); } @@ -92,8 +89,7 @@ public static void decode(byte[] input, int inputOffset, byte[] output, int mdec */ public static void encode(byte[] m, byte[] menc, int mlen) { - int i; - int srcIndex = 0; + int i, srcIndex = 0; // Process pairs of 4-bit values for (i = 0; i < mlen / 2; i++) { @@ -103,58 +99,28 @@ public static void encode(byte[] m, byte[] menc, int mlen) srcIndex += 2; } // If there is an extra nibble (odd number of nibbles), store it directly in lower 4 bits. - if (mlen % 2 == 1) + if ((mlen & 1) == 1) { menc[i] = (byte)(m[srcIndex] & 0x0F); } } - /** - * Unpacks m-vectors from a packed byte array into an array of 64-bit limbs. - * - * @param in the input byte array containing packed data - * @param out the output long array where unpacked limbs are stored - * @param vecs the number of vectors - * @param m the m parameter (used to compute m_vec_limbs and copy lengths) - */ - public static void unpackMVecs(byte[] in, long[] out, int vecs, int m) - { - int mVecLimbs = (m + 15) / 16; - int bytesToCopy = m / 2; // Number of bytes to copy per vector - // Temporary buffer to hold mVecLimbs longs (each long is 8 bytes) - byte[] tmp = new byte[mVecLimbs << 3]; - - // Process vectors in reverse order - for (int i = vecs - 1; i >= 0; i--) - { - // Copy m/2 bytes from the input into tmp. The rest remains zero. - System.arraycopy(in, i * bytesToCopy, tmp, 0, bytesToCopy); - - // Convert each 8-byte block in tmp into a long using Pack - for (int j = 0; j < mVecLimbs; j++) - { - out[i * mVecLimbs + j] = Pack.littleEndianToLong(tmp, j << 3); - } - } - } - public static void unpackMVecs(byte[] in, int inOff, long[] out, int outOff, int vecs, int m) { - int mVecLimbs = (m + 15) / 16; - int bytesToCopy = m / 2; // Number of bytes to copy per vector + int mVecLimbs = (m + 15) >> 4; + int bytesToCopy = m >> 1; // Number of bytes to copy per vector // Temporary buffer to hold mVecLimbs longs (each long is 8 bytes) - byte[] tmp = new byte[mVecLimbs << 3]; + int lastblockLen = 8 - (mVecLimbs << 3) + bytesToCopy; + int i, j; // Process vectors in reverse order - for (int i = vecs - 1; i >= 0; i--) + for (i = vecs - 1, outOff += i * mVecLimbs, inOff += i * bytesToCopy; i >= 0; i--, outOff -= mVecLimbs, inOff -= bytesToCopy) { - // Copy m/2 bytes from the input into tmp. The rest remains zero. - System.arraycopy(in, inOff + i * bytesToCopy, tmp, 0, bytesToCopy); - // Convert each 8-byte block in tmp into a long using Pack - for (int j = 0; j < mVecLimbs; j++) + for (j = 0; j < mVecLimbs - 1; j++) { - out[outOff + i * mVecLimbs + j] = Pack.littleEndianToLong(tmp, j * 8); + out[outOff + j] = Pack.littleEndianToLong(in, inOff + (j << 3)); } + out[outOff + j] = Pack.littleEndianToLong(in, inOff + (j << 3), lastblockLen); } } @@ -168,23 +134,19 @@ public static void unpackMVecs(byte[] in, int inOff, long[] out, int outOff, int */ public static void packMVecs(long[] in, byte[] out, int outOff, int vecs, int m) { - int mVecLimbs = (m + 15) / 16; - int bytesToCopy = m / 2; // Number of bytes per vector to write - + int mVecLimbs = (m + 15) >> 4; + int bytesToCopy = m >> 1; // Number of bytes per vector to write + int lastBlockLen = 8 - (mVecLimbs << 3) + bytesToCopy; + int j; // Process each vector in order - for (int i = 0; i < vecs; i++) + for (int i = 0, inOff = 0; i < vecs; i++, outOff += bytesToCopy, inOff += mVecLimbs) { - // Temporary buffer to hold the bytes for this vector - byte[] tmp = new byte[mVecLimbs * 8]; - // Convert each long into 8 bytes using Pack - for (int j = 0; j < mVecLimbs; j++) + for (j = 0; j < mVecLimbs - 1; j++) { - Pack.longToLittleEndian(in[i * mVecLimbs + j], tmp, j * 8); + Pack.longToLittleEndian(in[inOff + j], out, outOff + (j << 3)); } - - // Copy the first m/2 bytes from tmp to the output array - System.arraycopy(tmp, 0, out, i * bytesToCopy + outOff, bytesToCopy); + Pack.longToLittleEndian(in[inOff + j], out, outOff + (j << 3), lastBlockLen); } } @@ -242,6 +204,6 @@ public static void expandP1P2(MayoParameters p, long[] P, byte[] seed_pk) // Unpack the byte array 'temp' into the long array 'P' // using our previously defined unpackMVecs method. - unpackMVecs(temp, P, numVectors, p.getM()); + unpackMVecs(temp, 0, P, 0, numVectors, p.getM()); } } diff --git a/core/src/main/java/org/bouncycastle/util/Bytes.java b/core/src/main/java/org/bouncycastle/util/Bytes.java index 928cacd714..70dc29d13a 100644 --- a/core/src/main/java/org/bouncycastle/util/Bytes.java +++ b/core/src/main/java/org/bouncycastle/util/Bytes.java @@ -20,7 +20,7 @@ public static void xor(int len, byte[] x, int xOff, byte[] y, byte[] z, int zOff { for (int i = 0; i < len; ++i) { - z[zOff + i] = (byte)(x[xOff + i] ^ y[i]); + z[zOff++] = (byte)(x[xOff++] ^ y[i]); } } From 8b7bf520191713efc0dfb72e01d440462b71e480 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 4 Mar 2025 13:31:35 +0700 Subject: [PATCH 151/890] Use Arrays.areEqual --- .../bouncycastle/crypto/test/PKCS12Test.java | 27 +++---------------- 1 file changed, 4 insertions(+), 23 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/PKCS12Test.java b/core/src/test/java/org/bouncycastle/crypto/test/PKCS12Test.java index a5402aeae0..e158379013 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/PKCS12Test.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/PKCS12Test.java @@ -6,6 +6,7 @@ import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTestResult; import org.bouncycastle.util.test.Test; @@ -22,26 +23,6 @@ public class PKCS12Test char[] password1 = { 's', 'm', 'e', 'g' }; char[] password2 = { 'q', 'u', 'e', 'e', 'g' }; - private boolean isEqual( - byte[] a, - byte[] b) - { - if (a.length != b.length) - { - return false; - } - - for (int i = 0; i != a.length; i++) - { - if (a[i] != b[i]) - { - return false; - } - } - - return true; - } - private TestResult run1( int id, char[] password, @@ -59,7 +40,7 @@ private TestResult run1( CipherParameters key = generator.generateDerivedParameters(24 * 8); - if (isEqual(result, ((KeyParameter)key).getKey())) + if (Arrays.areEqual(result, ((KeyParameter)key).getKey())) { return new SimpleTestResult(true, "PKCS12Test: Okay"); } @@ -87,7 +68,7 @@ private TestResult run2( ParametersWithIV params = (ParametersWithIV)generator.generateDerivedParameters(64, 64); - if (isEqual(result, params.getIV())) + if (Arrays.areEqual(result, params.getIV())) { return new SimpleTestResult(true, "PKCS12Test: Okay"); } @@ -115,7 +96,7 @@ private TestResult run3( CipherParameters key = generator.generateDerivedMacParameters(160); - if (isEqual(result, ((KeyParameter)key).getKey())) + if (Arrays.areEqual(result, ((KeyParameter)key).getKey())) { return new SimpleTestResult(true, "PKCS12Test: Okay"); } From e78fa256be85f5f255215ab6132c914b29fdcea3 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 4 Mar 2025 17:33:25 +1030 Subject: [PATCH 152/890] Refactor of MayoParameters --- .../pqc/crypto/mayo/MayoParameters.java | 31 +++++++------------ 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java index 86e99704a7..ebaa54cbe0 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java @@ -11,7 +11,7 @@ public class MayoParameters 86 - 8, // v = n - o = 78 10 * 8 + 1, // A_cols = k * o + 1 = 10 * 8 + 1 = 81 10, // k - 16, // q + // q 39, // m_bytes 312, // O_bytes 39, // v_bytes @@ -26,7 +26,7 @@ public class MayoParameters new byte[]{8, 1, 1, 0}, // f_tail_arr 24, // salt_bytes 32, // digest_bytes - 16, // pk_seed_bytes + // pk_seed_bytes 24 // sk_seed_bytes ); @@ -39,7 +39,7 @@ public class MayoParameters 81 - 17, // v = 64 4 * 17 + 1, // A_cols = 4 * 17 + 1 = 69 4, // k - 16, // q + // q 32, // m_bytes 544, // O_bytes 32, // v_bytes @@ -54,7 +54,7 @@ public class MayoParameters new byte[]{8, 0, 2, 8}, // f_tail_arr 24, // salt_bytes 32, // digest_bytes - 16, // pk_seed_bytes + // pk_seed_bytes 24 // sk_seed_bytes ); @@ -67,7 +67,7 @@ public class MayoParameters 118 - 10, // v = 108 11 * 10 + 1, // A_cols = 11 * 10 + 1 = 111 11, // k - 16, // q + // q 54, // m_bytes 540, // O_bytes 54, // v_bytes @@ -82,7 +82,7 @@ public class MayoParameters new byte[]{8, 0, 1, 7}, // f_tail_arr 32, // salt_bytes 48, // digest_bytes - 16, // pk_seed_bytes + // pk_seed_bytes 32 // sk_seed_bytes ); @@ -95,7 +95,7 @@ public class MayoParameters 154 - 12, // v = 142 12 * 12 + 1, // A_cols = 12 * 12 + 1 = 145 12, // k - 16, // q + // q 71, // m_bytes 852, // O_bytes 71, // v_bytes @@ -110,7 +110,7 @@ public class MayoParameters new byte[]{4, 0, 8, 1}, // f_tail_arr 40, // salt_bytes 64, // digest_bytes - 16, // pk_seed_bytes + // pk_seed_bytes 40 // sk_seed_bytes ); @@ -122,7 +122,7 @@ public class MayoParameters private final int v; private final int ACols; private final int k; - private final int q; + //private final int q; q = 16 private final int mBytes; private final int OBytes; private final int vBytes; @@ -136,13 +136,13 @@ public class MayoParameters private final byte[] fTailArr; private final int saltBytes; private final int digestBytes; - private final int pkSeedBytes; + private static final int pkSeedBytes = 16; private final int skSeedBytes; - private MayoParameters(String name, int n, int m, int mVecLimbs, int o, int v, int ACols, int k, int q, + private MayoParameters(String name, int n, int m, int mVecLimbs, int o, int v, int ACols, int k, int mBytes, int OBytes, int vBytes, int rBytes, int P1Bytes, int P2Bytes, int cskBytes, int cpkBytes, int sigBytes, int[] fTail, byte[] fTailArr, - int saltBytes, int digestBytes, int pkSeedBytes, int skSeedBytes) + int saltBytes, int digestBytes, int skSeedBytes) { this.name = name; this.n = n; @@ -152,7 +152,6 @@ private MayoParameters(String name, int n, int m, int mVecLimbs, int o, int v, i this.v = v; this.ACols = ACols; this.k = k; - this.q = q; this.mBytes = mBytes; this.OBytes = OBytes; this.vBytes = vBytes; @@ -166,7 +165,6 @@ private MayoParameters(String name, int n, int m, int mVecLimbs, int o, int v, i this.fTailArr = fTailArr; this.saltBytes = saltBytes; this.digestBytes = digestBytes; - this.pkSeedBytes = pkSeedBytes; this.skSeedBytes = skSeedBytes; } @@ -210,11 +208,6 @@ public int getK() return k; } - public int getQ() - { - return q; - } - public int getMBytes() { return mBytes; From d54df598d84ea1c70220d0da7be58ad6edf4baf1 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 5 Mar 2025 12:17:17 +1030 Subject: [PATCH 153/890] Add java doc for the two main classes of Mayo --- .../pqc/crypto/mayo/MayoKeyPairGenerator.java | 33 ++++++++++ .../pqc/crypto/mayo/MayoSigner.java | 61 ++++++++++++++++++- 2 files changed, 91 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java index 56fdd1fa35..d123c7d8f8 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java @@ -9,6 +9,22 @@ import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Longs; +/** + * Implementation of the MAYO asymmetric key pair generator following the MAYO signature scheme specifications. + *

+ * This generator produces {@link MayoPublicKeyParameters} and {@link MayoPrivateKeyParameters} based on the + * MAYO algorithm parameters. The implementation follows the specification defined in the official MAYO + * documentation and reference implementation. + *

+ * + *

References:

+ * + * + */ public class MayoKeyPairGenerator implements AsymmetricCipherKeyPairGenerator { @@ -21,6 +37,23 @@ public void init(KeyGenerationParameters param) this.random = param.getRandom(); } + /** + * Generates a new asymmetric key pair following the MAYO algorithm specifications. + *

+ * The key generation process follows these steps: + *

+ *
    + *
  1. Initializes parameter dimensions from {@link MayoParameters}
  2. + *
  3. Generates secret key seed using a secure random generator
  4. + *
  5. Derives public key seed using SHAKE-256
  6. + *
  7. Expands matrix parameters P1 and P2
  8. + *
  9. Performs GF(16) matrix operations for key material generation
  10. + *
  11. Assembles and packages the public key components
  12. + *
  13. Securely clears temporary buffers containing sensitive data
  14. + *
+ * + * @return A valid MAYO key pair containing public and private key parameters + */ @Override public AsymmetricCipherKeyPair generateKeyPair() { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index c5242b00bd..560068bb34 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -3,6 +3,7 @@ import java.security.SecureRandom; import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.pqc.crypto.MessageSigner; @@ -12,6 +13,21 @@ import org.bouncycastle.util.Longs; import org.bouncycastle.util.Pack; +/** + * Implementation of the MAYO digital signature scheme as specified in the MAYO documentation. + * This class provides functionality for both signature generation and verification. + * + *

MAYO is a candidate in the NIST Post-Quantum Cryptography: Additional Digital Signature Schemes project, + * currently in Round 2 of evaluations. For more details about the NIST standardization process, see: + * NIST PQC Additional Digital Signatures.

+ * + *

References:

+ * + */ public class MayoSigner implements MessageSigner { @@ -20,6 +36,17 @@ public class MayoSigner private MayoPublicKeyParameters pubKey; private MayoPrivateKeyParameters privKey; + /** + * Initializes the signer for either signature generation or verification. + * + * @param forSigning {@code true} for signing mode, {@code false} for verification + * @param param CipherParameters containing: + *
    + *
  • {@link ParametersWithRandom} with {@link MayoPrivateKeyParameters} (for signing)
  • + *
  • {@link MayoPublicKeyParameters} (for verification)
  • + *
+ * @throws IllegalArgumentException if invalid parameters are provided + */ @Override public void init(boolean forSigning, CipherParameters param) { @@ -37,7 +64,7 @@ public void init(boolean forSigning, CipherParameters param) else { privKey = (MayoPrivateKeyParameters)param; - random = null; + random = CryptoServicesRegistrar.getSecureRandom(); } params = privKey.getParameters(); } @@ -50,6 +77,14 @@ public void init(boolean forSigning, CipherParameters param) } } + /** + * Generates a MAYO signature for the given message using the initialized private key. + * Follows the signature generation process outlined in the MAYO specification document. + * + * @param message The message to be signed + * @return The signature bytes concatenated with the original message + * @see MAYO Spec Algorithm 8 and 10 + */ @Override public byte[] generateSignature(byte[] message) { @@ -192,7 +227,7 @@ public byte[] generateSignature(byte[] message) Utils.decode(V, k * vbytes, r, ok); - if (sampleSolution(params, A, y, r, x)) + if (sampleSolution(A, y, r, x)) { break; } @@ -235,6 +270,15 @@ public byte[] generateSignature(byte[] message) } } + /** + * Verifies a MAYO signature against the initialized public key and message. + * Implements the verification process specified in the MAYO documentation. + * + * @param message The original message + * @param signature The signature to verify + * @return {@code true} if the signature is valid, {@code false} otherwise + * @see MAYO Spec Algorithm 9 and 11 + */ @Override public boolean verifySignature(byte[] message, byte[] signature) { @@ -554,7 +598,17 @@ private static void transpose16x16Nibbles(long[] M, int offset) } } - boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, byte[] r, byte[] x) + /** + * Samples a solution for the MAYO signature equation using the provided parameters. + * + * @param A Coefficient matrix + * @param y Target vector + * @param r Randomness vector + * @param x Output solution vector + * @return {@code true} if a valid solution was found, {@code false} otherwise + * @see MAYO Spec Algorithm 2 + */ + boolean sampleSolution(byte[] A, byte[] y, byte[] r, byte[] x) { final int k = params.getK(); final int o = params.getO(); @@ -641,6 +695,7 @@ boolean sampleSolution(MayoParameters params, byte[] A, byte[] y, byte[] r, byte * @param A the input matrix, stored rowwise; each element is in [0,15] * @param nrows the number of rows * @param ncols the number of columns (GF(16) elements per row) + * @see MAYO Spec Algorithm 1 */ void ef(byte[] A, int nrows, int ncols) { From 6c5020715ee785a0223a6923993ca6f6304b3902 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 5 Mar 2025 12:02:49 +0100 Subject: [PATCH 154/890] PGPSignatureSubpacketGenerator: setXYZ() now removes duplicates. Prior to this chance, calling e.g. setKeyFlags() on a subpacket generator that already contained key flags would introduce a duplicate key flag subpacket. I fixed this behavior by changing all setXYZ() methods to first remove any packet occurrences of the same type. NOTE: This breaks the current behavior of setRevocable() and setExportable(), as these method previously did a check to see if another packet of the same type was present and threw an IllegalStateException. Now, no such exception is thrown and instead the later method invocation takes precedence. NOTE: If you rely on duplicate packets (e.g. in your tests), you can still introduce duplicates by adding the duplicate packet via addCustomSubpacket(YourPacket). --- .../PGPSignatureSubpacketGenerator.java | 44 ++++++++++++++---- .../openpgp/test/PGPGeneralTest.java | 45 +++++++++---------- 2 files changed, 55 insertions(+), 34 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureSubpacketGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureSubpacketGenerator.java index 4330035bc1..2d008bd7d3 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureSubpacketGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureSubpacketGenerator.java @@ -69,10 +69,7 @@ public PGPSignatureSubpacketGenerator(PGPSignatureSubpacketVector sigSubV) */ public void setRevocable(boolean isCritical, boolean isRevocable) { - if (contains(SignatureSubpacketTags.REVOCABLE)) - { - throw new IllegalStateException("Revocable exists in the Signature Subpacket Generator"); - } + removePacketsOfType(SignatureSubpacketTags.REVOCABLE); packets.add(new Revocable(isCritical, isRevocable)); } @@ -97,10 +94,7 @@ public void setExportable(boolean isExportable) */ public void setExportable(boolean isCritical, boolean isExportable) { - if (contains(SignatureSubpacketTags.EXPORTABLE)) - { - throw new IllegalStateException("Exportable Certification exists in the Signature Subpacket Generator"); - } + removePacketsOfType(SignatureSubpacketTags.EXPORTABLE); packets.add(new Exportable(isCritical, isExportable)); } @@ -108,10 +102,11 @@ public void setExportable(boolean isCritical, boolean isExportable) * Specify the set of features of the key. * * @param isCritical true if should be treated as critical, false otherwise. - * @param feature features + * @param feature features bitmap */ public void setFeature(boolean isCritical, byte feature) { + removePacketsOfType(SignatureSubpacketTags.FEATURES); packets.add(new Features(isCritical, feature)); } @@ -126,6 +121,7 @@ public void setFeature(boolean isCritical, byte feature) */ public void setTrust(boolean isCritical, int depth, int trustAmount) { + removePacketsOfType(SignatureSubpacketTags.TRUST_SIG); packets.add(new TrustSignature(isCritical, depth, trustAmount)); } @@ -150,6 +146,7 @@ public void setKeyExpirationTime(long seconds) */ public void setKeyExpirationTime(boolean isCritical, long seconds) { + removePacketsOfType(SignatureSubpacketTags.KEY_EXPIRE_TIME); packets.add(new KeyExpirationTime(isCritical, seconds)); } @@ -175,6 +172,7 @@ public void setSignatureExpirationTime(long seconds) */ public void setSignatureExpirationTime(boolean isCritical, long seconds) { + removePacketsOfType(SignatureSubpacketTags.EXPIRE_TIME); packets.add(new SignatureExpirationTime(isCritical, seconds)); } @@ -200,6 +198,7 @@ public void setSignatureCreationTime(Date date) */ public void setSignatureCreationTime(boolean isCritical, Date date) { + removePacketsOfType(SignatureSubpacketTags.CREATION_TIME); packets.add(new SignatureCreationTime(isCritical, date)); } @@ -212,6 +211,7 @@ public void setSignatureCreationTime(boolean isCritical, Date date) */ public void setPreferredHashAlgorithms(boolean isCritical, int[] algorithms) { + removePacketsOfType(SignatureSubpacketTags.PREFERRED_HASH_ALGS); packets.add(new PreferredAlgorithms(SignatureSubpacketTags.PREFERRED_HASH_ALGS, isCritical, algorithms)); } @@ -225,6 +225,7 @@ public void setPreferredHashAlgorithms(boolean isCritical, int[] algorithms) */ public void setPreferredSymmetricAlgorithms(boolean isCritical, int[] algorithms) { + removePacketsOfType(SignatureSubpacketTags.PREFERRED_SYM_ALGS); packets.add(new PreferredAlgorithms(SignatureSubpacketTags.PREFERRED_SYM_ALGS, isCritical, algorithms)); } @@ -238,6 +239,7 @@ public void setPreferredSymmetricAlgorithms(boolean isCritical, int[] algorithms */ public void setPreferredCompressionAlgorithms(boolean isCritical, int[] algorithms) { + removePacketsOfType(SignatureSubpacketTags.PREFERRED_COMP_ALGS); packets.add(new PreferredAlgorithms(SignatureSubpacketTags.PREFERRED_COMP_ALGS, isCritical, algorithms)); } @@ -254,6 +256,7 @@ public void setPreferredCompressionAlgorithms(boolean isCritical, int[] algorith @Deprecated public void setPreferredAEADAlgorithms(boolean isCritical, int[] algorithms) { + removePacketsOfType(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS); packets.add(new PreferredAlgorithms(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS, isCritical, algorithms)); } @@ -268,6 +271,7 @@ public void setPreferredAEADAlgorithms(boolean isCritical, int[] algorithms) */ public void setPreferredAEADCiphersuites(boolean isCritical, PreferredAEADCiphersuites.Combination[] algorithms) { + removePacketsOfType(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS); packets.add(new PreferredAEADCiphersuites(isCritical, algorithms)); } @@ -280,6 +284,7 @@ public void setPreferredAEADCiphersuites(boolean isCritical, PreferredAEADCipher */ public void setPreferredAEADCiphersuites(PreferredAEADCiphersuites.Builder builder) { + removePacketsOfType(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS); packets.add(builder.build()); } @@ -300,6 +305,7 @@ public void setPreferredAEADCiphersuites(PreferredAEADCiphersuites.Builder build @Deprecated public void setPreferredLibrePgpEncryptionModes(boolean isCritical, int[] algorithms) { + removePacketsOfType(SignatureSubpacketTags.LIBREPGP_PREFERRED_ENCRYPTION_MODES); packets.add(new LibrePGPPreferredEncryptionModes(isCritical, algorithms)); } @@ -309,8 +315,22 @@ public void setPreferredLibrePgpEncryptionModes(boolean isCritical, int[] algori * * @param isCritical true if the subpacket should be treated as critical * @param uri key server URI + * @deprecated use {@link #addPreferredKeyServer(boolean, String)} instead. */ + @Deprecated public void setPreferredKeyServer(boolean isCritical, String uri) + { + addPreferredKeyServer(isCritical, uri); + } + + /** + * Specify a preferred key server for the signed user-id / key. + * Note, that the key server might also be a http/ftp etc. URI pointing to the key itself. + * + * @param isCritical true if the subpacket should be treated as critical + * @param uri key server URI + */ + public void addPreferredKeyServer(boolean isCritical, String uri) { packets.add(new PreferredKeyServer(isCritical, uri)); } @@ -341,6 +361,7 @@ public void setKeyFlags(int flags) */ public void setKeyFlags(boolean isCritical, int flags) { + removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS); packets.add(new KeyFlags(isCritical, flags)); } @@ -443,6 +464,7 @@ public void addEmbeddedSignature(boolean isCritical, PGPSignature pgpSignature) public void setPrimaryUserID(boolean isCritical, boolean isPrimaryUserID) { + removePacketsOfType(SignatureSubpacketTags.PRIMARY_USER_ID); packets.add(new PrimaryUserID(isCritical, isPrimaryUserID)); } @@ -485,6 +507,7 @@ public void addNotationData(boolean isCritical, boolean isHumanReadable, String */ public void setRevocationReason(boolean isCritical, byte reason, String description) { + removePacketsOfType(SignatureSubpacketTags.REVOCATION_REASON); packets.add(new RevocationReason(isCritical, reason, description)); } @@ -523,6 +546,7 @@ public void addRevocationKey(boolean isCritical, int keyAlgorithm, byte[] finger */ public void setIssuerKeyID(boolean isCritical, long keyID) { + removePacketsOfType(SignatureSubpacketTags.ISSUER_KEY_ID); packets.add(new IssuerKeyID(isCritical, keyID)); } @@ -536,6 +560,7 @@ public void setIssuerKeyID(boolean isCritical, long keyID) */ public void setSignatureTarget(boolean isCritical, int publicKeyAlgorithm, int hashAlgorithm, byte[] hashData) { + removePacketsOfType(SignatureSubpacketTags.SIGNATURE_TARGET); packets.add(new SignatureTarget(isCritical, publicKeyAlgorithm, hashAlgorithm, hashData)); } @@ -558,6 +583,7 @@ public void setIssuerFingerprint(boolean isCritical, PGPSecretKey secretKey) */ public void setIssuerFingerprint(boolean isCritical, PGPPublicKey publicKey) { + removePacketsOfType(SignatureSubpacketTags.ISSUER_FINGERPRINT); packets.add(new IssuerFingerprint(isCritical, publicKey.getVersion(), publicKey.getFingerprint())); } diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPGeneralTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPGeneralTest.java index bc03498760..afd421d860 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPGeneralTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPGeneralTest.java @@ -30,6 +30,7 @@ import org.bouncycastle.bcpg.SignatureSubpacketTags; import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; import org.bouncycastle.bcpg.attr.ImageAttribute; +import org.bouncycastle.bcpg.sig.Exportable; import org.bouncycastle.bcpg.sig.Features; import org.bouncycastle.bcpg.sig.IntendedRecipientFingerprint; import org.bouncycastle.bcpg.sig.KeyFlags; @@ -37,6 +38,7 @@ import org.bouncycastle.bcpg.sig.PolicyURI; import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites; import org.bouncycastle.bcpg.sig.RegularExpression; +import org.bouncycastle.bcpg.sig.Revocable; import org.bouncycastle.bcpg.sig.RevocationKey; import org.bouncycastle.bcpg.sig.RevocationKeyTags; import org.bouncycastle.bcpg.sig.RevocationReason; @@ -2169,7 +2171,7 @@ public void testPGPSignatureSubpacketVector() isTrue("Trust should be null", trustSignature != null); isTrue("Trust level depth should be " + depth, trustSignature.getDepth() == depth); isTrue("Trust amount should be " + trustAmount, trustSignature.getTrustAmount() == trustAmount); - isTrue("Exporable should be false", !hashedPcks.isExportable()); + isTrue("Exportable should be false", !hashedPcks.isExportable()); isTrue(hashedPcks.getIssuerFingerprint().getKeyVersion() == publicKey.getVersion()); isTrue("isPrimaryUserID should be true", hashedPcks.isPrimaryUserID()); @@ -2195,7 +2197,7 @@ public void testPGPSignatureSubpacketVector() isTrue("Trust should be null", trustSignature != null); isTrue("Trust level depth should be " + depth, trustSignature.getDepth() == depth); isTrue("Trust amount should be " + trustAmount, trustSignature.getTrustAmount() == trustAmount); - isTrue("Exporable should be false", !hashedPcks.isExportable()); + isTrue("Exportable should be false", !hashedPcks.isExportable()); isTrue(hashedPcks.getIssuerFingerprint().getKeyVersion() == publicKey.getVersion()); isTrue("isPrimaryUserID should be true", hashedPcks.isPrimaryUserID()); @@ -2207,27 +2209,20 @@ public void testPGPSignatureSubpacketVector() hashedGen = new PGPSignatureSubpacketGenerator(); hashedGen.setExportable(false, true); - try - { - hashedGen.setExportable(false, false); - fail("Duplicated settings for Exportable"); - } - catch (IllegalStateException e) - { - isTrue("Exportable Certification exists in the Signature Subpacket Generator", - messageIs(e.getMessage(), "Exportable Certification exists in the Signature Subpacket Generator")); - } + hashedGen.setExportable(false, false); + isEquals("Calling setExportable multiple times MUST NOT introduce duplicates", + 1, hashedGen.getSubpackets(SignatureSubpacketTags.EXPORTABLE).length); + Exportable exportable = (Exportable) hashedGen.getSubpackets(SignatureSubpacketTags.EXPORTABLE)[0]; + isTrue("Last invocation of setExportable MUST take precedence.", + !exportable.isExportable()); + hashedGen.setRevocable(false, true); - try - { - hashedGen.setRevocable(false, false); - fail("Duplicated settings for Revocable"); - } - catch (IllegalStateException e) - { - isTrue("Revocable exists in the Signature Subpacket Generator", - messageIs(e.getMessage(), "Revocable exists in the Signature Subpacket Generator")); - } + hashedGen.setRevocable(false, false); + isEquals("Calling setRevocable multiple times MUST NOT introduce duplicates.", + 1, hashedGen.getSubpackets(SignatureSubpacketTags.REVOCABLE).length); + Revocable revocable = (Revocable) hashedGen.getSubpackets(SignatureSubpacketTags.REVOCABLE)[0]; + isTrue("Last invocation of setRevocable MUST take precedence.", + !revocable.isRevocable()); try { @@ -2273,13 +2268,13 @@ public void testPGPSignatureSubpacketVector() hashedPcks = sig.getHashedSubPackets(); isTrue("URL should be " + url, hashedPcks.getPolicyURI().getURI().equals(url)); isTrue(areEqual(hashedPcks.getPolicyURI().getRawURI(), Strings.toUTF8ByteArray(url))); - isTrue("Exporable should be true", hashedPcks.isExportable()); - isTrue("Test Singner User ID", hashedPcks.getSignerUserID().equals("")); + isTrue("Exportable should be false", !hashedPcks.isExportable()); + isTrue("Test Signer User ID", hashedPcks.getSignerUserID().equals("")); isTrue("Test for empty description", hashedPcks.getRevocationReason().getRevocationDescription().equals("")); Features features = hashedPcks.getFeatures(); isTrue(features.supportsSEIPDv2()); isTrue(features.getFeatures() == Features.FEATURE_SEIPD_V2); - isTrue(hashedPcks.getRevocable().isRevocable()); + isTrue("Revocable should be false", !hashedPcks.getRevocable().isRevocable()); } public void testECNistCurves() From 08c9a09a0d456b75ca2d42bcf90d802420d0bdbe Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 7 Mar 2025 12:28:20 +1030 Subject: [PATCH 155/890] TODO: gen_a_FqS_ct function in line 192 of SnovaKeyPairGenerator --- .../pqc/crypto/snova/GF16Matrix.java | 105 +++++++ .../pqc/crypto/snova/GF16Utils.java | 149 +++++++++ .../pqc/crypto/snova/MapGroup1.java | 29 ++ .../pqc/crypto/snova/MapGroup2.java | 20 ++ .../pqc/crypto/snova/PublicKey.java | 14 + .../pqc/crypto/snova/PublicKeyExpanded.java | 19 ++ .../crypto/snova/PublicKeyExpandedPack.java | 19 ++ .../bouncycastle/pqc/crypto/snova/SKGF16.java | 36 +++ .../pqc/crypto/snova/SnovaEngine.java | 69 +++++ .../pqc/crypto/snova/SnovaKeyElements.java | 17 ++ .../snova/SnovaKeyGenerationParameters.java | 22 ++ .../crypto/snova/SnovaKeyPairGenerator.java | 283 ++++++++++++++++++ .../pqc/crypto/snova/SnovaParameters.java | 167 +++++++++++ .../snova/SnovaPrivateKeyParameters.java | 26 ++ .../snova/SnovaPublicKeyParameters.java | 26 ++ .../pqc/crypto/test/SnovaTest.java | 88 ++++++ .../pqc/crypto/test/TestUtils.java | 146 +++++++++ 17 files changed, 1235 insertions(+) create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Matrix.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpanded.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpandedPack.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/SKGF16.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyGenerationParameters.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPrivateKeyParameters.java create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPublicKeyParameters.java create mode 100644 core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Matrix.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Matrix.java new file mode 100644 index 0000000000..c929e33f5e --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Matrix.java @@ -0,0 +1,105 @@ +package org.bouncycastle.pqc.crypto.snova; + +class GF16Matrix +{ + private final byte[][] data; + private final int rank; + + public GF16Matrix(int rank) + { + this.rank = rank; + this.data = new byte[rank][rank]; + } + + public void set(int x, int y, byte value) + { + data[x][y] = (byte)(value & 0xF); + } + + public byte get(int x, int y) + { + return data[x][y]; + } + + public void add(GF16Matrix other) + { +// for (int i = 0; i < size; i++) +// { +// for (int j = 0; j < size; j++) +// { +// data[i][j] = add(data[i][j], other.data[i][j]); +// } +// } + } + + public void mul(GF16Matrix a, GF16Matrix b) + { + byte[][] temp = new byte[rank][rank]; + for (int i = 0; i < rank; i++) + { + for (int j = 0; j < rank; j++) + { + byte sum = 0; +// for (int k = 0; k < size; k++) +// { +// sum = add(sum, mul(a.data[i][k], b.data[k][j])); +// } + temp[i][j] = sum; + } + } + System.arraycopy(temp, 0, data, 0, temp.length); + } + + public void scale(byte scalar) + { +// for (int i = 0; i < size; i++) +// { +// for (int j = 0; j < size; j++) +// { +// data[i][j] = mul(data[i][j], scalar); +// } +// } + } + + public void transpose() + { + byte[][] temp = new byte[rank][rank]; + for (int i = 0; i < rank; i++) + { + for (int j = 0; j < rank; j++) + { + temp[j][i] = data[i][j]; + } + } + System.arraycopy(temp, 0, data, 0, temp.length); + } + + public void makeInvertible() + { + // Implementation of be_invertible_by_add_aS + GF16Matrix temp = new GF16Matrix(rank); + if (determinant() == 0) + { + for (byte a = 1; a < 16; a++) + { + temp.scale(a); + add(temp); + if (determinant() != 0) + { + return; + } + } + } + } + + private byte determinant() + { + // Simplified determinant calculation for small matrices +// if (rank == 2) +// { +// return add(mul(data[0][0], data[1][1]), mul(data[0][1], data[1][0])); +// } + // Add implementations for larger matrices as needed + throw new UnsupportedOperationException("Determinant for size " + rank + " not implemented"); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java new file mode 100644 index 0000000000..40549bbe5d --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java @@ -0,0 +1,149 @@ +package org.bouncycastle.pqc.crypto.snova; + + +public class GF16Utils +{ + private static final byte[] F_STAR = {1, 2, 4, 8, 3, 6, 12, 11, 5, 10, 7, 14, 15, 13, 9}; + private static final byte[] MT4B = new byte[256]; + private static final byte[] INV4B = new byte[16]; + + static byte mt(int p, int q) + { + return MT4B[((p) << 4) ^ (q)]; + } + + static + { + + // Initialize multiplication table + for (int i = 0; i < 15; i++) + { + for (int j = 0; j < 15; j++) + { + MT4B[(F_STAR[i]<<4) ^ F_STAR[j]] = F_STAR[(i + j) % 15]; + } + } + + int g = F_STAR[1], g_inv = F_STAR[14], gn = 1, gn_inv = 1; + // Initialize inversion table + INV4B[0] = 0; + INV4B[1] = 1; + for (int i = 0; i < 14; i++) + { + gn = mt(gn, g); + gn_inv = mt(gn_inv, g_inv); + INV4B[gn] = (byte)gn_inv; + } + } + + public static void gf16mMul(byte[] a, byte[] b, byte[] c, int rank) + { + + for (int i = 0; i < rank; i++) + { + for (int j = 0; j < rank; j++) + { + int cIndex = i * rank + j; + c[cIndex] = mt(getGf16m(a, i, 0, rank), getGf16m(b, 0, j, rank)); + for (int k = 1; k < rank; ++k) + { + c[cIndex] ^= mt(getGf16m(a, i, k, rank), getGf16m(b, k, j, rank)); + } + } + } + } + + static byte getGf16m(byte[] gf16m, int x, int y, int rank) + { + return gf16m[x * rank + y]; + } + + /** + * Conversion 4 bit -> 32 bit representation + */ + public static int gf16FromNibble(int idx) + { + int middle = idx | (idx << 4); + return ((middle & 0x41) | ((middle << 2) & 0x208)); + } + + public static void gf16mAdd(byte[] a, byte[] b, byte[] c, int rank) + { + + for (int i = 0; i < rank; ++i) + { + for (int j = 0; j < rank; ++j) + { + int index = i * rank + j; + // GF16 addition is XOR operation (equivalent to GF(2^4) addition) + // Mask with 0x0F to ensure we only keep 4-bit values + c[index] = (byte)((a[index] ^ b[index]) & 0x0F); + } + } + } + + public static byte mul(byte a, byte b) + { + return MT4B[(a & 0xF) << 4 | (b & 0xF)]; + } + + public static byte add(byte a, byte b) + { + return (byte)((a ^ b) & 0xF); + } + + public static byte inv(byte a) + { + return INV4B[a & 0xF]; + } + + public static void convertBytesToGF16s(byte[] input, byte[] output, int gf16Count) + { + int pairs = gf16Count / 2; + for (int i = 0; i < pairs; i++) + { + output[i * 2] = (byte)(input[i] & 0x0F); + output[i * 2 + 1] = (byte)((input[i] >> 4) & 0x0F); + } + if (gf16Count % 2 == 1) + { + output[gf16Count - 1] = (byte)(input[pairs] & 0x0F); + } + } + + public static void convertGF16sToBytes(byte[] output, byte[] gf16s, int gf16Count) + { + int pairs = gf16Count / 2; + for (int i = 0; i < pairs; i++) + { + output[i] = (byte)((gf16s[i * 2 + 1] << 4) | gf16s[i * 2]); + } + if (gf16Count % 2 == 1) + { + output[pairs] = gf16s[gf16Count - 1]; + } + } + + static GF16Matrix[][][] create3DArray(int d1, int d2, int d3, int rank) { + GF16Matrix[][][] arr = new GF16Matrix[d1][d2][d3]; + for (int i = 0; i < d1; i++) { + for (int j = 0; j < d2; j++) { + for (int k = 0; k < d3; k++) { + arr[i][j][k] = new GF16Matrix(rank); + } + } + } + return arr; + } + + static GF16Matrix[][] create2DArray(int d1, int d2, int rank) { + GF16Matrix[][] arr = new GF16Matrix[d1][d2]; + for (int i = 0; i < d1; i++) { + for (int j = 0; j < d2; j++) { + arr[i][j] = new GF16Matrix(rank); + } + } + return arr; + } + +} \ No newline at end of file diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java new file mode 100644 index 0000000000..57a8a51c83 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -0,0 +1,29 @@ +package org.bouncycastle.pqc.crypto.snova; + +class MapGroup1 +{ + public final GF16Matrix[][][] P11; // [m][v][v] + public final GF16Matrix[][][] P12; // [m][v][o] + public final GF16Matrix[][][] P21; // [m][o][v] + public final GF16Matrix[][] Aalpha; // [m][alpha] + public final GF16Matrix[][] Balpha; // [m][alpha] + public final GF16Matrix[][] Qalpha1;// [m][alpha] + public final GF16Matrix[][] Qalpha2;// [m][alpha] + + public MapGroup1(SnovaParameters params) + { + int m = params.getM(); + int v = params.getV(); + int o = params.getO(); + int alpha = params.getAlpha(); + int rank = params.getL(); + + P11 = GF16Utils.create3DArray(m, v, v, rank); + P12 = GF16Utils.create3DArray(m, v, o, rank); + P21 = GF16Utils.create3DArray(m, o, v, rank); + Aalpha = GF16Utils.create2DArray(m, alpha, rank); + Balpha = GF16Utils.create2DArray(m, alpha, rank); + Qalpha1 = GF16Utils.create2DArray(m, alpha, rank); + Qalpha2 = GF16Utils.create2DArray(m, alpha, rank); + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java new file mode 100644 index 0000000000..a78141e370 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java @@ -0,0 +1,20 @@ +package org.bouncycastle.pqc.crypto.snova; + +public class MapGroup2 +{ + public final GF16Matrix[][][] F11; // [m][v][v] + public final GF16Matrix[][][] F12; // [m][v][o] + public final GF16Matrix[][][] F21; // [m][o][v] + + public MapGroup2(SnovaParameters params) + { + int m = params.getM(); + int v = params.getV(); + int o = params.getO(); + int rank = params.getL(); + + F11 = GF16Utils.create3DArray(m, v, v, rank); + F12 = GF16Utils.create3DArray(m, v, o, rank); + F21 = GF16Utils.create3DArray(m, o, v, rank); + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java new file mode 100644 index 0000000000..a83b48f391 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java @@ -0,0 +1,14 @@ +package org.bouncycastle.pqc.crypto.snova; + +class PublicKey +{ + public final byte[] publicKeySeed; + public final byte[] P22; + + public PublicKey(SnovaParameters params) + { + publicKeySeed = new byte[SnovaKeyPairGenerator.publicSeedLength]; + P22 = new byte[(params.getM() * params.getO() * params.getO() * params.getL() * params.getL()) >> 1]; + } +} + diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpanded.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpanded.java new file mode 100644 index 0000000000..90ab90ecca --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpanded.java @@ -0,0 +1,19 @@ +package org.bouncycastle.pqc.crypto.snova; + +class PublicKeyExpanded +{ + public final byte[] publicKeySeed; + public final GF16Matrix[][][] P22; // [m][o][o] + public final MapGroup1 map1; + + public PublicKeyExpanded(SnovaParameters params) + { + int m = params.getM(); + int o = params.getO(); + int rank = params.getL(); + + publicKeySeed = new byte[SnovaKeyPairGenerator.publicSeedLength]; + P22 = GF16Utils.create3DArray(m, o, o, rank); + map1 = new MapGroup1(params); + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpandedPack.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpandedPack.java new file mode 100644 index 0000000000..fc73b52dec --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpandedPack.java @@ -0,0 +1,19 @@ +package org.bouncycastle.pqc.crypto.snova; + +class PublicKeyExpandedPack +{ + public final byte[] publicKeySeed; + public final byte[] packedData; + + public PublicKeyExpandedPack(SnovaParameters params) + { + publicKeySeed = new byte[SnovaKeyPairGenerator.publicSeedLength]; + int m = params.getM(); + int o = params.getO(); + int v = params.getV(); + int alpha = params.getAlpha(); + packedData = new byte[(((m * o * o) << 4) + // P22_t + (m * v * v * 16 + m * v * o * 16 + m * o * v * 16 + m * alpha * 16 * 4) // map_group1 + + 1) >> 1]; + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SKGF16.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SKGF16.java new file mode 100644 index 0000000000..fe25ffefb6 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SKGF16.java @@ -0,0 +1,36 @@ +package org.bouncycastle.pqc.crypto.snova; + +class SKGF16 +{ + public final GF16Matrix[][] Aalpha; // [m][alpha] + public final GF16Matrix[][] Balpha; // [m][alpha] + public final GF16Matrix[][] Qalpha1; // [m][alpha] + public final GF16Matrix[][] Qalpha2; // [m][alpha] + public final GF16Matrix[][] T12; // [v][o] + public final GF16Matrix[][][] F11; // [m][v][v] + public final GF16Matrix[][][] F12; // [m][v][o] + public final GF16Matrix[][][] F21; // [m][o][v] + public final byte[] publicKeySeed; + public final byte[] privateKeySeed; + + public SKGF16(SnovaParameters params) + { + int m = params.getM(); + int v = params.getV(); + int o = params.getO(); + int alpha = params.getAlpha(); + int rank = params.getL(); + + Aalpha = GF16Utils.create2DArray(m, alpha, rank); + Balpha = GF16Utils.create2DArray(m, alpha, rank); + Qalpha1 = GF16Utils.create2DArray(m, alpha, rank); + Qalpha2 = GF16Utils.create2DArray(m, alpha, rank); + T12 = GF16Utils.create2DArray(v, o, rank); + F11 = GF16Utils.create3DArray(m, v, v, rank); + F12 = GF16Utils.create3DArray(m, v, o, rank); + F21 = GF16Utils.create3DArray(m, o, v, rank); + + publicKeySeed = new byte[SnovaKeyPairGenerator.publicSeedLength]; + privateKeySeed = new byte[SnovaKeyPairGenerator.privateSeedLength]; + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java new file mode 100644 index 0000000000..60bfe3f4c7 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -0,0 +1,69 @@ +package org.bouncycastle.pqc.crypto.snova; + +public class SnovaEngine +{ + private final SnovaParameters params; + private final int l; + private final int lsq; + final byte[][] S; + final int[][] xS; + + public SnovaEngine(SnovaParameters params) + { + this.params = params; + this.l = params.getL(); + this.lsq = l * l; + S = new byte[l][lsq]; + xS = new int[l][lsq]; + be_aI(S[0], (byte)1); + beTheS(S[1]); + for (int index = 2; index < l; ++index) + { + GF16Utils.gf16mMul(S[index - 1], S[1], S[index], l); + } + + for (int index = 0; index < l; ++index) + { + for (int ij = 0; ij < lsq; ++ij) + { + xS[index][ij] = GF16Utils.gf16FromNibble(S[index][ij]); + } + } + } + + public void be_aI(byte[] target, byte a) + { + // Mask 'a' to ensure it's a valid 4-bit GF16 element + a = (byte)(a & 0x0F); + + for (int i = 0; i < l; ++i) + { + for (int j = 0; j < l; ++j) + { + int index = i * l + j; + target[index] = (i == j) ? a : (byte)0; + } + } + } + + private void beTheS(byte[] target) + { + // Set all elements to 8 - (i + j) in GF16 (4-bit values) + for (int i = 0; i < l; ++i) + { + for (int j = 0; j < l; ++j) + { + int value = 8 - (i + j); + target[i * l + j] = (byte)(value & 0x0F); // Mask to 4 bits + } + } + + // Special case for rank 5 + if (l == 5) + { + target[4 * 5 + 4] = (byte)(9 & 0x0F); // Set (4,4) to 9 + } + } + + +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java new file mode 100644 index 0000000000..e9f44db7d9 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java @@ -0,0 +1,17 @@ +package org.bouncycastle.pqc.crypto.snova; + +class SnovaKeyElements +{ + public final MapGroup1 map1; + public final GF16Matrix[][] T12; // [v][o] + public final MapGroup2 map2; + public final PublicKey publicKey; + + public SnovaKeyElements(SnovaParameters params) + { + map1 = new MapGroup1(params); + T12 = GF16Utils.create2DArray(params.getV(), params.getO(), params.getL()); + map2 = new MapGroup2(params); + publicKey = new PublicKey(params); + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyGenerationParameters.java new file mode 100644 index 0000000000..ef25e9605d --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyGenerationParameters.java @@ -0,0 +1,22 @@ +package org.bouncycastle.pqc.crypto.snova; + +import java.security.SecureRandom; + +import org.bouncycastle.crypto.KeyGenerationParameters; + +public class SnovaKeyGenerationParameters + extends KeyGenerationParameters +{ + private final SnovaParameters params; + + public SnovaKeyGenerationParameters(SecureRandom random, SnovaParameters params) + { + super(random, -1); // Security parameter not used directly + this.params = params; + } + + public SnovaParameters getParameters() + { + return params; + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java new file mode 100644 index 0000000000..23bc7c90db --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -0,0 +1,283 @@ +package org.bouncycastle.pqc.crypto.snova; + +import java.io.ByteArrayOutputStream; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.bouncycastle.crypto.KeyGenerationParameters; +import org.bouncycastle.crypto.digests.SHAKEDigest; +import org.bouncycastle.util.Arrays; + +public class SnovaKeyPairGenerator + implements AsymmetricCipherKeyPairGenerator +{ + private SnovaEngine engine; + private static final int seedLength = 48; + static final int publicSeedLength = 16; + static final int privateSeedLength = 32; + private SnovaParameters params; + private SecureRandom random; + private boolean initialized; + + @Override + public void init(KeyGenerationParameters param) + { + SnovaKeyGenerationParameters snovaParams = (SnovaKeyGenerationParameters)param; + this.params = snovaParams.getParameters(); + this.random = snovaParams.getRandom(); + this.initialized = true; + this.engine = new SnovaEngine(params); + } + + @Override + public AsymmetricCipherKeyPair generateKeyPair() + { + if (!initialized) + { + throw new IllegalStateException("SNOVA key pair generator not initialized"); + } + + // Generate seed pair according to SNOVA specifications + byte[] seedPair = new byte[seedLength]; + random.nextBytes(seedPair); + + byte[] pk = new byte[publicSeedLength]; + byte[] sk = new byte[privateSeedLength]; + + byte[] ptPublicKeySeed = Arrays.copyOfRange(seedPair, 0, publicSeedLength); + byte[] ptPrivateKeySeed = Arrays.copyOfRange(seedPair, publicSeedLength, seedPair.length); + + if (params.isSkIsSeed()) + { + generateKeysSSK(pk, sk, ptPublicKeySeed, ptPrivateKeySeed); + } + else + { + generateKeysESK(pk, sk, ptPublicKeySeed, ptPrivateKeySeed); + } + + return new AsymmetricCipherKeyPair( + new SnovaPublicKeyParameters(pk), + new SnovaPrivateKeyParameters(sk) + ); + } + + private void generateKeysSSK(byte[] pk, byte[] sk, byte[] ptPublicKeySeed, byte[] ptPrivateKeySeed) + { + // Implementation based on C's generate_keys_ssk + System.arraycopy(ptPublicKeySeed, 0, sk, 0, ptPublicKeySeed.length); + System.arraycopy(ptPrivateKeySeed, 0, sk, ptPublicKeySeed.length, ptPrivateKeySeed.length); + + // Actual key generation would go here using BC's SHAKE/AES implementations + // This would include the matrix operations from the C code + generatePublicKey(pk, ptPublicKeySeed, ptPrivateKeySeed); + } + + private void generateKeysESK(byte[] pk, byte[] esk, byte[] ptPublicKeySeed, byte[] ptPrivateKeySeed) + { + // Implementation based on C's generate_keys_esk + // Actual expanded key generation would go here + generatePublicKey(pk, ptPublicKeySeed, ptPrivateKeySeed); + packPrivateKey(esk, ptPublicKeySeed, ptPrivateKeySeed); + } + + private void packPrivateKey(byte[] esk, byte[] ptPublicKeySeed, byte[] ptPrivateKeySeed) + { + SnovaKeyElements keyElements = new SnovaKeyElements(params); + generateKeysCore(keyElements, ptPublicKeySeed, ptPrivateKeySeed); + + // Serialize all components + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + + // Serialize map components +// serializeMatrixGroup(bos, keyElements.map1.Aalpha); +// serializeMatrixGroup(bos, keyElements.map1.Balpha); +// serializeMatrixGroup(bos, keyElements.map1.Qalpha1); +// serializeMatrixGroup(bos, keyElements.map1.Qalpha2); + + // Serialize T12 + for (GF16Matrix[] row : keyElements.T12) + { + for (GF16Matrix matrix : row) + { + serializeMatrix(bos, matrix); + } + } + + // Add public and private seeds + bos.write(ptPublicKeySeed, 0, ptPublicKeySeed.length); + bos.write(ptPrivateKeySeed, 0, ptPrivateKeySeed.length); + + System.arraycopy(bos.toByteArray(), 0, esk, 0, esk.length); + } + + private void serializeMatrixGroup(ByteArrayOutputStream bos, GF16Matrix[][][] group) + { + for (GF16Matrix[][] dim1 : group) + { + for (GF16Matrix[] dim2 : dim1) + { + for (GF16Matrix matrix : dim2) + { + serializeMatrix(bos, matrix); + } + } + } + } + + private void serializeMatrix(ByteArrayOutputStream bos, GF16Matrix matrix) + { +// byte[] temp = new byte[(matrix.size * matrix.size + 1) / 2]; +// byte[] gf16s = new byte[matrix.size * matrix.size]; +// +// int idx = 0; +// for (int i = 0; i < matrix.size; i++) { +// for (int j = 0; j < matrix.size; j++) { +// gf16s[idx++] = matrix.get(i, j); +// } +// } +// +// GF16Utils.convertGF16sToBytes(temp, gf16s, gf16s.length); +// bos.write(temp, 0, temp.length); + } + + private void generatePublicKey(byte[] pk, byte[] ptPublicKeySeed, byte[] ptPrivateKeySeed) + { + + // Generate key elements + SnovaKeyElements keyElements = new SnovaKeyElements(params); + generateKeysCore(keyElements, ptPublicKeySeed, ptPrivateKeySeed); + + // Pack public key components + //packPublicKey(pk, keyElements); + } + + private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[] skSeed) + { + // Generate T12 matrix + genSeedsAndT12(keyElements.T12, skSeed); + + // Generate map components +// genABQP(keyElements.map1, pkSeed); +// +// // Generate F matrices +// genF(keyElements.map2, keyElements.map1, keyElements.T12); + + // Generate P22 matrix +// genP22(keyElements.pk.P22, keyElements.T12, keyElements.map1.P21, keyElements.map2.F12); + } + + private void genSeedsAndT12(GF16Matrix[][] T12, byte[] skSeed) + { + int bytesPrngPrivate = (params.getV() * params.getO() * params.getL() + 1) >>> 1; + int gf16sPrngPrivate = params.getV() * params.getO() * params.getL(); + byte[] prngOutput = new byte[bytesPrngPrivate]; + + // Generate PRNG output using SHAKE-256 + SHAKEDigest shake = new SHAKEDigest(256); + shake.update(skSeed, 0, skSeed.length); + shake.doFinal(prngOutput, 0, prngOutput.length); + + // Convert bytes to GF16 array + byte[] gf16PrngOutput = new byte[gf16sPrngPrivate]; + GF16Utils.convertBytesToGF16s(prngOutput, gf16PrngOutput, gf16sPrngPrivate); + + // Generate T12 matrices + int ptr = 0; + for (int j = 0; j < params.getV(); j++) + { + for (int k = 0; k < params.getO(); k++) + { + //gen_a_FqS_ct + GF16Matrix matrix = new GF16Matrix(params.getL()); + for (int i = 0; i < params.getL(); i++) + { + for (int m = 0; m < params.getL(); m++) + { + matrix.set(i, m, gf16PrngOutput[ptr++]); + } + } + matrix.makeInvertible(); + T12[j][k] = matrix; + } + } + } +// +// private void genABQP(MapGroup1 map1, byte[] pkSeed) +// { +// byte[] prngOutput = new byte[params.getBytesPrngPublic()]; +// +// if (params.isPkExpandShake()) { +// // SHAKE-based expansion +// SHAKEDigest shake = new SHAKEDigest(256); +// shake.update(pkSeed, 0, pkSeed.length); +// shake.doFinal(prngOutput, 0, prngOutput.length); +// } else { +// // AES-CTR-based expansion +// AESEngine aes = new AESEngine(); +// aes.init(true, new KeyParameter(pkSeed)); +// for (int i = 0; i < prngOutput.length; i += 16) { +// byte[] block = new byte[16]; +// aes.processBlock(block, 0, block, 0); +// System.arraycopy(block, 0, prngOutput, i, Math.min(16, prngOutput.length - i)); +// } +// } +// +// // Convert bytes to GF16 structures +// GF16Utils.convertBytesToGF16s(prngOutput, map1); +// +// // Post-processing for invertible matrices +// for (GF16Matrix matrix : map1.Aalpha) { +// GF16Utils.makeInvertible(matrix); +// } +// for (GF16Matrix matrix : map1.Balpha) { +// GF16Utils.makeInvertible(matrix); +// } +// } + +// private void genF(MapGroup2 map2, MapGroup1 map1, GF16Matrix[][] T12) +// { +// // Matrix operations from C code's gen_F_ref +// // Clone initial matrices +// System.arraycopy(map1.P11, 0, map2.F11, 0, map1.P11.length); +// System.arraycopy(map1.P12, 0, map2.F12, 0, map1.P12.length); +// System.arraycopy(map1.P21, 0, map2.F21, 0, map1.P21.length); +// +// // Perform matrix multiplications and additions +// GF16Matrix temp = new GF16Matrix(params.getL()); +// for (int i = 0; i < params.getM(); i++) { +// for (int j = 0; j < params.getV(); j++) { +// for (int k = 0; k < params.getO(); k++) { +// for (int idx = 0; idx < params.getV(); idx++) { +// GF16Matrix.mul(map1.P11[i][j][idx], T12[idx][k], temp); +// GF16Matrix.add(map2.F12[i][j][k], temp, map2.F12[i][j][k]); +// } +// } +// } +// } +// } + + private void genP22(byte[] outP22, GF16Matrix[][] T12, GF16Matrix[][][] P21, GF16Matrix[][][] F12) + { +// GF16Matrix[][][] P22 = new GF16Matrix[params.getM()][params.getO()][params.getO()]; +// GF16Matrix temp1 = new GF16Matrix(params.getL()); +// GF16Matrix temp2 = new GF16Matrix(params.getL()); +// +// for (int i = 0; i < params.getM(); i++) { +// for (int j = 0; j < params.getO(); j++) { +// for (int k = 0; k < params.getO(); k++) { +// for (int idx = 0; idx < params.getV(); idx++) { +// GF16Matrix.mul(T12[idx][j], F12[i][idx][k], temp1); +// GF16Matrix.mul(P21[i][j][idx], T12[idx][k], temp2); +// GF16Matrix.add(temp1, temp2, temp1); +// GF16Matrix.add(P22[i][j][k], temp1, P22[i][j][k]); +// } +// } +// } +// } +// +// // Convert GF16 matrices to bytes +// GF16Utils.convertGF16sToBytes(P22, outP22); + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java new file mode 100644 index 0000000000..90436bab3c --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java @@ -0,0 +1,167 @@ +package org.bouncycastle.pqc.crypto.snova; + +public class SnovaParameters +{ + public static final SnovaParameters SNOVA_24_5_16_4_SSK = + new SnovaParameters("SNOVA_24_5_16_4_SSK", 24, 5, 4, true, false); + public static final SnovaParameters SNOVA_24_5_16_4_ESK = + new SnovaParameters("SNOVA_24_5_16_4_ESK", 24, 5, 4, false, false); + public static final SnovaParameters SNOVA_24_5_16_4_SHAKE_SSK = + new SnovaParameters("SNOVA_24_5_16_4_SHAKE_SSK", 24, 5, 4, true, true); + public static final SnovaParameters SNOVA_24_5_16_4_SHAKE_ESK = + new SnovaParameters("SNOVA_24_5_16_4_SHAKE_ESK", 24, 5, 4, false, true); + + public static final SnovaParameters SNOVA_24_5_16_5_SSK = + new SnovaParameters("SNOVA_24_5_16_5_SSK", 24, 5, 5, true, false); + public static final SnovaParameters SNOVA_24_5_16_5_ESK = + new SnovaParameters("SNOVA_24_5_16_5_ESK", 24, 5, 5, false, false); + public static final SnovaParameters SNOVA_24_5_16_5_SHAKE_SSK = + new SnovaParameters("SNOVA_24_5_16_5_SHAKE_SSK", 24, 5, 5, true, true); + public static final SnovaParameters SNOVA_24_5_16_5_SHAKE_ESK = + new SnovaParameters("SNOVA_24_5_16_5_SHAKE_ESK", 24, 5, 5, false, true); + + public static final SnovaParameters SNOVA_25_8_16_3_SSK = + new SnovaParameters("SNOVA_25_8_16_3_SSK", 25, 8, 3, true, false); + public static final SnovaParameters SNOVA_25_8_16_3_ESK = + new SnovaParameters("SNOVA_25_8_16_3_ESK", 25, 8, 3, false, false); + public static final SnovaParameters SNOVA_25_8_16_3_SHAKE_SSK = + new SnovaParameters("SNOVA_25_8_16_3_SHAKE_SSK", 25, 8, 3, true, true); + public static final SnovaParameters SNOVA_25_8_16_3_SHAKE_ESK = + new SnovaParameters("SNOVA_25_8_16_3_SHAKE_ESK", 25, 8, 3, false, true); + + public static final SnovaParameters SNOVA_29_6_16_5_SSK = + new SnovaParameters("SNOVA_29_6_16_5_SSK", 29, 6, 5, true, false); + public static final SnovaParameters SNOVA_29_6_16_5_ESK = + new SnovaParameters("SNOVA_29_6_16_5_ESK", 29, 6, 5, false, false); + public static final SnovaParameters SNOVA_29_6_16_5_SHAKE_SSK = + new SnovaParameters("SNOVA_29_6_16_5_SHAKE_SSK", 29, 6, 5, true, true); + public static final SnovaParameters SNOVA_29_6_16_5_SHAKE_ESK = + new SnovaParameters("SNOVA_29_6_16_5_SHAKE_ESK", 29, 6, 5, false, true); + + public static final SnovaParameters SNOVA_37_8_16_4_SSK = + new SnovaParameters("SNOVA_37_8_16_4_SSK", 37, 8, 4, true, false); + public static final SnovaParameters SNOVA_37_8_16_4_ESK = + new SnovaParameters("SNOVA_37_8_16_4_ESK", 37, 8, 4, false, false); + public static final SnovaParameters SNOVA_37_8_16_4_SHAKE_SSK = + new SnovaParameters("SNOVA_37_8_16_4_SHAKE_SSK", 37, 8, 4, true, true); + public static final SnovaParameters SNOVA_37_8_16_4_SHAKE_ESK = + new SnovaParameters("SNOVA_37_8_16_4_SHAKE_ESK", 37, 8, 4, false, true); + + // SNOVA_37_17_16_2 variants + public static final SnovaParameters SNOVA_37_17_16_2_SSK = + new SnovaParameters("SNOVA_37_17_16_2_SSK", 37, 17, 2, true, false); + public static final SnovaParameters SNOVA_37_17_16_2_ESK = + new SnovaParameters("SNOVA_37_17_16_2_ESK", 37, 17, 2, false, false); + public static final SnovaParameters SNOVA_37_17_16_2_SHAKE_SSK = + new SnovaParameters("SNOVA_37_17_16_2_SHAKE_SSK", 37, 17, 2, true, true); + public static final SnovaParameters SNOVA_37_17_16_2_SHAKE_ESK = + new SnovaParameters("SNOVA_37_17_16_2_SHAKE_ESK", 37, 17, 2, false, true); + + // SNOVA_49_11_16_3 variants + public static final SnovaParameters SNOVA_49_11_16_3_SSK = + new SnovaParameters("SNOVA_49_11_16_3_SSK", 49, 11, 3, true, false); + public static final SnovaParameters SNOVA_49_11_16_3_ESK = + new SnovaParameters("SNOVA_49_11_16_3_ESK", 49, 11, 3, false, false); + public static final SnovaParameters SNOVA_49_11_16_3_SHAKE_SSK = + new SnovaParameters("SNOVA_49_11_16_3_SHAKE_SSK", 49, 11, 3, true, true); + public static final SnovaParameters SNOVA_49_11_16_3_SHAKE_ESK = + new SnovaParameters("SNOVA_49_11_16_3_SHAKE_ESK", 49, 11, 3, false, true); + + // SNOVA_56_25_16_2 variants + public static final SnovaParameters SNOVA_56_25_16_2_SSK = + new SnovaParameters("SNOVA_56_25_16_2_SSK", 56, 25, 2, true, false); + public static final SnovaParameters SNOVA_56_25_16_2_ESK = + new SnovaParameters("SNOVA_56_25_16_2_ESK", 56, 25, 2, false, false); + public static final SnovaParameters SNOVA_56_25_16_2_SHAKE_SSK = + new SnovaParameters("SNOVA_56_25_16_2_SHAKE_SSK", 56, 25, 2, true, true); + public static final SnovaParameters SNOVA_56_25_16_2_SHAKE_ESK = + new SnovaParameters("SNOVA_56_25_16_2_SHAKE_ESK", 56, 25, 2, false, true); + + // SNOVA_60_10_16_4 variants + public static final SnovaParameters SNOVA_60_10_16_4_SSK = + new SnovaParameters("SNOVA_60_10_16_4_SSK", 60, 10, 4, true, false); + public static final SnovaParameters SNOVA_60_10_16_4_ESK = + new SnovaParameters("SNOVA_60_10_16_4_ESK", 60, 10, 4, false, false); + public static final SnovaParameters SNOVA_60_10_16_4_SHAKE_SSK = + new SnovaParameters("SNOVA_60_10_16_4_SHAKE_SSK", 60, 10, 4, true, true); + public static final SnovaParameters SNOVA_60_10_16_4_SHAKE_ESK = + new SnovaParameters("SNOVA_60_10_16_4_SHAKE_ESK", 60, 10, 4, false, true); + + // SNOVA_66_15_16_4 variants + public static final SnovaParameters SNOVA_66_15_16_4_SSK = + new SnovaParameters("SNOVA_66_15_16_4_SSK", 66, 15, 4, true, false); + public static final SnovaParameters SNOVA_66_15_16_4_ESK = + new SnovaParameters("SNOVA_66_15_16_4_ESK", 66, 15, 4, false, false); + public static final SnovaParameters SNOVA_66_15_16_4_SHAKE_SSK = + new SnovaParameters("SNOVA_66_15_16_4_SHAKE_SSK", 66, 15, 4, true, true); + public static final SnovaParameters SNOVA_66_15_16_4_SHAKE_ESK = + new SnovaParameters("SNOVA_66_15_16_4_SHAKE_ESK", 66, 15, 4, false, true); + + // SNOVA_75_33_16_2 variants + public static final SnovaParameters SNOVA_75_33_16_2_SSK = + new SnovaParameters("SNOVA_75_33_16_2_SSK", 75, 33, 2, true, false); + public static final SnovaParameters SNOVA_75_33_16_2_ESK = + new SnovaParameters("SNOVA_75_33_16_2_ESK", 75, 33, 2, false, false); + public static final SnovaParameters SNOVA_75_33_16_2_SHAKE_SSK = + new SnovaParameters("SNOVA_75_33_16_2_SHAKE_SSK", 75, 33, 2, true, true); + public static final SnovaParameters SNOVA_75_33_16_2_SHAKE_ESK = + new SnovaParameters("SNOVA_75_33_16_2_SHAKE_ESK", 75, 33, 2, false, true); + + private final String name; + private final int v; + private final int o; + private final int l; + private final boolean skIsSeed; + private final boolean pkExpandShake; + + public SnovaParameters(String name, int v, int o, int l, boolean skIsSeed, boolean pkExpandShake) + { + this.name = name; + this.v = v; + this.o = o; + this.l = l; + this.skIsSeed = skIsSeed; + this.pkExpandShake = pkExpandShake; + } + + // Getter methods + public String getName() + { + return name; + } + + public int getV() + { + return v; + } + + public int getO() + { + return o; + } + + public int getL() + { + return l; + } + + public boolean isSkIsSeed() + { + return skIsSeed; + } + + public boolean isPkExpandShake() + { + return pkExpandShake; + } + + public int getM() + { + return o; + } + + public int getAlpha() + { + return l * l + l; + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPrivateKeyParameters.java new file mode 100644 index 0000000000..bcd35f51cf --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPrivateKeyParameters.java @@ -0,0 +1,26 @@ +package org.bouncycastle.pqc.crypto.snova; + +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.util.Arrays; + +public class SnovaPrivateKeyParameters + extends AsymmetricKeyParameter +{ + private final byte[] privateKey; + + public SnovaPrivateKeyParameters(byte[] privateKey) + { + super(true); + this.privateKey = Arrays.clone(privateKey); + } + + public byte[] getPrivateKey() + { + return Arrays.clone(privateKey); + } + + public byte[] getEncoded() + { + return Arrays.clone(privateKey); + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPublicKeyParameters.java new file mode 100644 index 0000000000..99f9c9a8d8 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPublicKeyParameters.java @@ -0,0 +1,26 @@ +package org.bouncycastle.pqc.crypto.snova; + +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.util.Arrays; + +public class SnovaPublicKeyParameters + extends AsymmetricKeyParameter +{ + private final byte[] publicKey; + + public SnovaPublicKeyParameters(byte[] publicKey) + { + super(false); + this.publicKey = Arrays.clone(publicKey); + } + + public byte[] getPublicKey() + { + return Arrays.clone(publicKey); + } + + public byte[] getEncoded() + { + return Arrays.clone(publicKey); + } +} diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java new file mode 100644 index 0000000000..b02b1ab703 --- /dev/null +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java @@ -0,0 +1,88 @@ +package org.bouncycastle.pqc.crypto.test; + +import java.security.SecureRandom; + +import junit.framework.TestCase; +import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Signer; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.pqc.crypto.MessageSigner; +import org.bouncycastle.pqc.crypto.snova.SnovaKeyGenerationParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaKeyPairGenerator; +import org.bouncycastle.pqc.crypto.snova.SnovaParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaPublicKeyParameters; + + +public class SnovaTest + extends TestCase +{ + public static void main(String[] args) + throws Exception + { + SnovaTest test = new SnovaTest(); + test.testTestVectors(); + } + + private static final SnovaParameters[] PARAMETER_SETS = new SnovaParameters[] + { + SnovaParameters.SNOVA_24_5_16_4_ESK, + }; + + private static final String[] files = new String[]{ + "PQCsignKAT_SNOVA_24_5_4_ESK.rsp", + }; + + + public void testTestVectors() + throws Exception + { + long start = System.currentTimeMillis(); + TestUtils.testTestVector(false, false, "pqc/crypto/snova", files, new TestUtils.KeyGenerationOperation() + { + @Override + public SecureRandom getSecureRanom(byte[] seed) + { + return new NISTSecureRandom(seed, null); + } + + @Override + public AsymmetricCipherKeyPairGenerator getAsymmetricCipherKeyPairGenerator(int fileIndex, SecureRandom random) + { + SnovaParameters parameters = PARAMETER_SETS[fileIndex]; + + SnovaKeyPairGenerator kpGen = new SnovaKeyPairGenerator(); + kpGen.init(new SnovaKeyGenerationParameters(random, parameters)); + return kpGen; + } + + @Override + public byte[] getPublicKeyEncoded(AsymmetricKeyParameter pubParams) + { + return ((SnovaPublicKeyParameters)pubParams).getEncoded(); + } + + @Override + public byte[] getPrivateKeyEncoded(CipherParameters privParams) + { + return ((SnovaPrivateKeyParameters)privParams).getEncoded(); + } + + @Override + public Signer getSigner() + { + return null; + } + + @Override + public MessageSigner getMessageSigner() + { + return null;//new SnovaSigner(); + } + }); + long end = System.currentTimeMillis(); + System.out.println("time cost: " + (end - start) + "\n"); + } +} + diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java index 743f936caf..986de28ed2 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java @@ -1,9 +1,155 @@ package org.bouncycastle.pqc.crypto.test; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.security.SecureRandom; +import java.util.HashMap; +import java.util.Map; + +import junit.framework.Assert; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Signer; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.pqc.crypto.MessageSigner; +import org.bouncycastle.pqc.crypto.util.PrivateKeyFactory; +import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory; +import org.bouncycastle.pqc.crypto.util.PublicKeyFactory; +import org.bouncycastle.pqc.crypto.util.SubjectPublicKeyInfoFactory; +import org.bouncycastle.test.TestResourceFinder; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; + class TestUtils { static boolean parseBoolean(String value) { return "true".equalsIgnoreCase(value); } + + public interface KeyGenerationOperation + { + SecureRandom getSecureRanom(byte[] seed); + + AsymmetricCipherKeyPairGenerator getAsymmetricCipherKeyPairGenerator(int fileIndex, SecureRandom random); + + byte[] getPublicKeyEncoded(AsymmetricKeyParameter pubParams); + + byte[] getPrivateKeyEncoded(CipherParameters privParams); + + Signer getSigner(); + + MessageSigner getMessageSigner(); + } + + public static void testTestVector(boolean enableFactory, boolean isSigner, String homeDir, String[] files, KeyGenerationOperation operation) + throws Exception + { + for (int fileIndex = 0; fileIndex != files.length; fileIndex++) + { + String name = files[fileIndex]; + InputStream src = TestResourceFinder.findTestResource(homeDir, name); + BufferedReader bin = new BufferedReader(new InputStreamReader(src)); + + String line; + HashMap buf = new HashMap(); + while ((line = bin.readLine()) != null) + { + line = line.trim(); + + if (line.startsWith("#")) + { + continue; + } + if (line.length() == 0) + { + if (buf.size() > 0) + { +// int count = Integer.parseInt(buf.get("count")); +// if (count == 99) +// { +// System.out.println("break"); +// } + byte[] seed = Hex.decode((String)buf.get("seed")); + byte[] pk = Hex.decode((String)buf.get("pk")); + byte[] sk = Hex.decode((String)buf.get("sk")); + byte[] message = Hex.decode((String)buf.get("msg")); + byte[] signature = Hex.decode((String)buf.get("sm")); + + SecureRandom random = operation.getSecureRanom(seed); + + AsymmetricCipherKeyPairGenerator kpGen = operation.getAsymmetricCipherKeyPairGenerator(fileIndex, random); + + // + // Generate keys and test. + // + AsymmetricCipherKeyPair kp = kpGen.generateKeyPair(); + AsymmetricKeyParameter pubParams; + CipherParameters privParams; + if (enableFactory) + { + pubParams = PublicKeyFactory.createKey( + SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(kp.getPublic())); + privParams = PrivateKeyFactory.createKey( + PrivateKeyInfoFactory.createPrivateKeyInfo(kp.getPrivate())); + } + else + { + pubParams = kp.getPublic(); + privParams = kp.getPrivate(); + } + + Assert.assertTrue(name + ": public key", Arrays.areEqual(pk, operation.getPublicKeyEncoded(pubParams))); + Assert.assertTrue(name + ": secret key", Arrays.areEqual(sk, operation.getPrivateKeyEncoded(privParams))); + + byte[] sigGenerated; + privParams = new ParametersWithRandom(privParams, random); + if (isSigner) + { + Signer signer = operation.getSigner(); + signer.init(true, privParams); + signer.update(message, 0, message.length); + sigGenerated = signer.generateSignature(); + } + else + { + MessageSigner signer = operation.getMessageSigner(); + signer.init(true, privParams); + sigGenerated = signer.generateSignature(message); + } + + Assert.assertTrue(Arrays.areEqual(sigGenerated, signature)); + + if (isSigner) + { + Signer signer = operation.getSigner(); + signer.init(false, pubParams); + signer.update(message, 0, message.length); + Assert.assertTrue(signer.verifySignature(sigGenerated)); + } + else + { + MessageSigner signer = operation.getMessageSigner(); + signer.init(false, pubParams); + Assert.assertTrue(signer.verifySignature(message, sigGenerated)); + } + //System.out.println("Count " + count + " pass"); + } + buf.clear(); + continue; + } + + int a = line.indexOf("="); + if (a > -1) + { + buf.put(line.substring(0, a).trim(), line.substring(a + 1).trim()); + } + } + } + } } From cf05a00a259408921295135ed6b141738401e5bf Mon Sep 17 00:00:00 2001 From: David Hook Date: Fri, 7 Mar 2025 13:10:36 +1100 Subject: [PATCH 156/890] added PKCS#10 PQC tests. --- .../org/bouncycastle/cert/test/AllTests.java | 1 + .../bouncycastle/cert/test/PQCPKCS10Test.java | 147 ++++++++++++++++++ 2 files changed, 148 insertions(+) create mode 100644 pkix/src/test/java/org/bouncycastle/cert/test/PQCPKCS10Test.java diff --git a/pkix/src/test/java/org/bouncycastle/cert/test/AllTests.java b/pkix/src/test/java/org/bouncycastle/cert/test/AllTests.java index 4034d9d018..39b7bdaeb5 100644 --- a/pkix/src/test/java/org/bouncycastle/cert/test/AllTests.java +++ b/pkix/src/test/java/org/bouncycastle/cert/test/AllTests.java @@ -61,6 +61,7 @@ public static Test suite() suite.addTestSuite(BcAttrCertTest.class); suite.addTestSuite(BcCertTest.class); suite.addTestSuite(BcPKCS10Test.class); + suite.addTestSuite(PQCPKCS10Test.class); suite.addTest(ConverterTest.suite()); return new BCTestSetup(suite); diff --git a/pkix/src/test/java/org/bouncycastle/cert/test/PQCPKCS10Test.java b/pkix/src/test/java/org/bouncycastle/cert/test/PQCPKCS10Test.java new file mode 100644 index 0000000000..c81c84123b --- /dev/null +++ b/pkix/src/test/java/org/bouncycastle/cert/test/PQCPKCS10Test.java @@ -0,0 +1,147 @@ +package org.bouncycastle.cert.test; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Security; +import java.security.cert.X509Certificate; +import java.util.Date; + +import javax.security.auth.x500.X500Principal; + +import junit.framework.TestCase; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x500.X500NameBuilder; +import org.bouncycastle.asn1.x500.style.BCStyle; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jcajce.spec.MLDSAParameterSpec; +import org.bouncycastle.jcajce.spec.MLKEMParameterSpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder; +import org.bouncycastle.pkcs.PKCS10CertificationRequest; +import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; +import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequest; +import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; +import org.bouncycastle.util.Arrays; + +/** + **/ +public class PQCPKCS10Test + extends TestCase +{ + private static final String BC = BouncyCastleProvider.PROVIDER_NAME; + + public String getName() + { + return "PKCS10CertRequest"; + } + + public void setUp() + { + if (Security.getProvider("BC") == null) + { + Security.addProvider(new BouncyCastleProvider()); + } + } + + public void testMLDsa() + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("ML-DSA", "BC"); + + kpg.initialize(MLDSAParameterSpec.ml_dsa_65); + + KeyPair kp = kpg.genKeyPair(); + + X500Name subject = getSubjectName(); + + PKCS10CertificationRequestBuilder requestBuilder = new JcaPKCS10CertificationRequestBuilder(subject, kp.getPublic()); + + PKCS10CertificationRequest req1 = requestBuilder.build(new JcaContentSignerBuilder("ML-DSA").setProvider(BC).build(kp.getPrivate())); + + JcaPKCS10CertificationRequest req2 = new JcaPKCS10CertificationRequest(req1.getEncoded()).setProvider(BC); + + if (!req2.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider(BC).build(kp.getPublic()))) + { + fail("ML-DSA: Failed verify check."); + } + + if (!Arrays.areEqual(req2.getPublicKey().getEncoded(), req1.getSubjectPublicKeyInfo().getEncoded())) + { + fail("ML-DSA: Failed public key check."); + } + } + + /** + * ML-KEM basesd PKCS#10 request using ML-DSA signing key. + */ + public void testMLKem() + throws Exception + { + KeyPairGenerator signKpg = KeyPairGenerator.getInstance("ML-DSA", "BC"); + + signKpg.initialize(MLDSAParameterSpec.ml_dsa_65); + + KeyPair signKp = signKpg.genKeyPair(); + X509Certificate signCert = getMLDSACertificate(signKp); + + KeyPairGenerator kemKpg = KeyPairGenerator.getInstance("ML-KEM", "BC"); + + kemKpg.initialize(MLKEMParameterSpec.ml_kem_768); + + KeyPair kemKp = kemKpg.genKeyPair(); + + X500Principal subject = signCert.getSubjectX500Principal(); + + PKCS10CertificationRequestBuilder requestBuilder = new JcaPKCS10CertificationRequestBuilder(subject, kemKp.getPublic()); + + PKCS10CertificationRequest req1 = requestBuilder.build(new JcaContentSignerBuilder("ML-DSA").setProvider(BC).build(signKp.getPrivate())); + + JcaPKCS10CertificationRequest req2 = new JcaPKCS10CertificationRequest(req1.getEncoded()).setProvider(BC); + + if (!req2.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider(BC).build(signCert.getPublicKey()))) + { + fail("ML-KEM: Failed verify check."); + } + + if (!Arrays.areEqual(req2.getPublicKey().getEncoded(), req1.getSubjectPublicKeyInfo().getEncoded())) + { + fail("ML-KEM: Failed public key check."); + } + } + + private X500Name getSubjectName() + { + X500NameBuilder x500NameBld = new X500NameBuilder(BCStyle.INSTANCE); + + x500NameBld.addRDN(BCStyle.C, "AU"); + x500NameBld.addRDN(BCStyle.O, "The Legion of the Bouncy Castle"); + x500NameBld.addRDN(BCStyle.L, "Melbourne"); + x500NameBld.addRDN(BCStyle.ST, "Victoria"); + x500NameBld.addRDN(BCStyle.EmailAddress, "feedback-crypto@bouncycastle.org"); + + X500Name subject = x500NameBld.build(); + return subject; + } + + private X509Certificate getMLDSACertificate(KeyPair kp) + throws Exception + { + X500Name issuer = getSubjectName(); // self signed + X509v3CertificateBuilder v3certBldr = new JcaX509v3CertificateBuilder(issuer, + BigInteger.valueOf(System.currentTimeMillis()), + new Date(System.currentTimeMillis() - 5000L), + new Date(System.currentTimeMillis() + 15000L), + issuer, kp.getPublic()).addExtension(Extension.basicConstraints, true, new BasicConstraints(false)); + + ContentSigner signer = new JcaContentSignerBuilder("ML-DSA").setProvider(BC).build(kp.getPrivate()); + + return new JcaX509CertificateConverter().setProvider(BC).getCertificate(v3certBldr.build(signer)); + } +} From 0db98d9cc2a24b46d6fc03fbbbe5a3fae065a050 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 7 Mar 2025 13:00:41 +1030 Subject: [PATCH 157/890] Fix the bug of ParallelHash that block size is set to 0. --- .../bouncycastle/crypto/digests/ParallelHash.java | 14 ++++++++++---- .../crypto/test/ParallelHashTest.java | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/ParallelHash.java b/core/src/main/java/org/bouncycastle/crypto/digests/ParallelHash.java index 5ac7f120d0..7b05eb52ca 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/ParallelHash.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/ParallelHash.java @@ -38,8 +38,8 @@ public class ParallelHash * Base constructor. * * @param bitLength security strength (bits) of the underlying SHAKE function, 128 or 256. - * @param S the customization string - available for local use. - * @param B the blocksize (in bytes) for hashing. + * @param S the customization string - available for local use. + * @param B the blocksize (in bytes) for hashing. */ public ParallelHash(int bitLength, byte[] S, int B) { @@ -50,12 +50,18 @@ public ParallelHash(int bitLength, byte[] S, int B, int outputSize) { this(bitLength, S, B, outputSize, CryptoServicePurpose.ANY); } + public ParallelHash(int bitLength, byte[] S, int B, int outputSize, CryptoServicePurpose purpose) { + if (B <= 0) + { + throw new IllegalArgumentException("block size should be greater than 0"); + } this.cshake = new CSHAKEDigest(bitLength, N_PARALLEL_HASH, S); this.compressor = new CSHAKEDigest(bitLength, new byte[0], new byte[0]); this.bitLength = bitLength; this.B = B; + this.outputLength = (outputSize + 7) / 8; this.buffer = new byte[B]; this.compressorBuffer = new byte[bitLength * 2 / 8]; @@ -112,7 +118,7 @@ public void update(byte in) public void update(byte[] in, int inOff, int len) throws DataLengthException, IllegalStateException { - len = Math.max(0, len); + len = Math.max(0, len); // // fill the current word @@ -198,7 +204,7 @@ public int doFinal(byte[] out, int outOff, int outLen) { wrapUp(outputLength); } - + int rv = cshake.doFinal(out, outOff, outLen); reset(); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ParallelHashTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ParallelHashTest.java index 05080caf2d..5d825cf705 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ParallelHashTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ParallelHashTest.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto.test; + import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.ParallelHash; import org.bouncycastle.util.Arrays; @@ -23,6 +24,7 @@ public String getName() public void performTest() throws Exception { + testException(); ParallelHash pHash = new ParallelHash(128, new byte[0], 8); byte[] data = Hex.decode("00 01 02 03 04 05 06 07 10 11 12 13 14 15 16 17 20 21 22 23 24 25 26 27"); @@ -164,6 +166,19 @@ private void testClone() } } + private void testException() + { + testException("block size should be greater than 0", "IllegalArgumentException", new TestExceptionOperation() + { + @Override + public void operation() + throws Exception + { + Digest digest = new ParallelHash(128, null, 0); + } + }); + } + public static void main( String[] args) { From ad11e9b7b185536519bc10d8871c9864bbdfc184 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 7 Mar 2025 16:30:38 +1030 Subject: [PATCH 158/890] Refactor of decode (which can be re-used in Snova). --- .../pqc/crypto/mayo/MayoKeyPairGenerator.java | 2 +- .../pqc/crypto/mayo/MayoSigner.java | 18 ++++++------- .../bouncycastle/pqc/crypto/mayo/Utils.java | 26 ++----------------- 3 files changed, 12 insertions(+), 34 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java index d123c7d8f8..95797cbf62 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java @@ -94,7 +94,7 @@ public AsymmetricCipherKeyPair generateKeyPair() // o ← Decode_o(S[ param_pk_seed_bytes : param_pk_seed_bytes + O_bytes ]) // Decode nibbles from S starting at offset param_pk_seed_bytes into O, // with expected output length = param_v * param_o. - Utils.decode(seed_pk, pkSeedBytes, O, O.length); + Utils.decode(seed_pk, pkSeedBytes, O, 0, O.length); // Expand P1 and P2 into the array P using seed_pk. Utils.expandP1P2(p, P, seed_pk); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index 560068bb34..65d148b896 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -135,7 +135,7 @@ public byte[] generateSignature(byte[] message) // Decode the portion of S after the first param_pk_seed_bytes into O. // (In C, this is: decode(S + param_pk_seed_bytes, O, param_v * param_o)) - Utils.decode(seed_pk, pk_seed_bytes, O, v * o); + Utils.decode(seed_pk, pk_seed_bytes, O, 0, v * o); // Expand P1 and P2 into the long array P using seed_pk. Utils.expandP1P2(params, P, seed_pk); @@ -225,7 +225,7 @@ public byte[] generateSignature(byte[] message) // A[(i + 1) * (ok + 1) - 1] = 0; // } - Utils.decode(V, k * vbytes, r, ok); + Utils.decode(V, k * vbytes, r, 0, ok); if (sampleSolution(A, y, r, x)) { @@ -240,7 +240,7 @@ public byte[] generateSignature(byte[] message) // Compute final signature components - for (int i = 0, io = 0, in = 0, iv = 0; i < k; i++, io += o, in+= n, iv += v) + for (int i = 0, io = 0, in = 0, iv = 0; i < k; i++, io += o, in += n, iv += v) { GF16Utils.matMul(O, x, io, Ox, o, v); Bytes.xor(v, Vdec, iv, Ox, s, in); @@ -274,8 +274,8 @@ public byte[] generateSignature(byte[] message) * Verifies a MAYO signature against the initialized public key and message. * Implements the verification process specified in the MAYO documentation. * - * @param message The original message - * @param signature The signature to verify + * @param message The original message + * @param signature The signature to verify * @return {@code true} if the signature is valid, {@code false} otherwise * @see MAYO Spec Algorithm 9 and 11 */ @@ -601,10 +601,10 @@ private static void transpose16x16Nibbles(long[] M, int offset) /** * Samples a solution for the MAYO signature equation using the provided parameters. * - * @param A Coefficient matrix - * @param y Target vector - * @param r Randomness vector - * @param x Output solution vector + * @param A Coefficient matrix + * @param y Target vector + * @param r Randomness vector + * @param x Output solution vector * @return {@code true} if a valid solution was found, {@code false} otherwise * @see MAYO Spec Algorithm 2 */ diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java index 16b4922278..f1205c3843 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java @@ -9,7 +9,7 @@ import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; -public class Utils +class Utils { /** * Decodes an encoded byte array. @@ -34,7 +34,7 @@ public static void decode(byte[] m, byte[] mdec, int mdecLen) // If there is an extra nibble (odd number of nibbles), decode only the lower nibble if ((mdecLen & 1) == 1) { - mdec[decIndex] = (byte)((m[i] & 0xFF) & 0x0F); + mdec[decIndex] = (byte)(m[i] & 0x0F); } } @@ -56,28 +56,6 @@ public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mde } } - /** - * Decodes a nibble-packed byte array into an output array. - * - * @param input the input byte array. - * @param inputOffset the offset in input from which to start decoding. - * @param output the output byte array to hold the decoded nibbles. - * @param mdecLen the total number of nibbles to decode. - */ - public static void decode(byte[] input, int inputOffset, byte[] output, int mdecLen) - { - int decIndex = 0, blocks = mdecLen >> 1; - for (int i = 0; i < blocks; i++) - { - output[decIndex++] = (byte)(input[inputOffset] & 0x0F); - output[decIndex++] = (byte)((input[inputOffset++] >> 4) & 0x0F); - } - if ((mdecLen & 1) == 1) - { - output[decIndex] = (byte)(input[inputOffset] & 0x0F); - } - } - /** * Encodes an array of 4-bit values into a byte array. * Two 4-bit values are packed into one byte, with the first nibble stored in the lower 4 bits From 46e112bf79bdefdca02bf4e33a54a4cad54b4dcf Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 7 Mar 2025 17:08:55 +1030 Subject: [PATCH 159/890] TODO: genABQP --- .../pqc/crypto/snova/GF16Utils.java | 133 +++++++++++++++--- .../pqc/crypto/snova/MapGroup1.java | 64 +++++++-- .../pqc/crypto/snova/SnovaEngine.java | 41 ++++++ .../pqc/crypto/snova/SnovaKeyElements.java | 4 +- .../crypto/snova/SnovaKeyPairGenerator.java | 102 ++++++++------ 5 files changed, 259 insertions(+), 85 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java index 40549bbe5d..9deda36297 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java @@ -20,7 +20,7 @@ static byte mt(int p, int q) { for (int j = 0; j < 15; j++) { - MT4B[(F_STAR[i]<<4) ^ F_STAR[j]] = F_STAR[(i + j) % 15]; + MT4B[(F_STAR[i] << 4) ^ F_STAR[j]] = F_STAR[(i + j) % 15]; } } @@ -36,6 +36,73 @@ static byte mt(int p, int q) } } + /** + * Convert one byte of data to GF16 representation (using only half of the + * byte). Example: -> + * + * @param m the input byte array (each byte holds two 4-bit values) + * @param mdec the output array that will hold the decoded nibbles (one per byte) + * @param mdecLen the total number of nibbles to decode + */ + public static void decode(byte[] m, byte[] mdec, int mdecLen) + { + int i, decIndex = 0, blocks = mdecLen >> 1; + // Process pairs of nibbles from each byte + for (i = 0; i < blocks; i++) + { + // Extract the lower nibble + mdec[decIndex++] = (byte)(m[i] & 0x0F); + // Extract the upper nibble (shift right 4 bits) + mdec[decIndex++] = (byte)((m[i] >> 4) & 0x0F); + } + // If there is an extra nibble (odd number of nibbles), decode only the lower nibble + if ((mdecLen & 1) == 1) + { + mdec[decIndex] = (byte)((m[i] & 0xFF) & 0x0F); + } + } + + public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mdecLen) + { + // Process pairs of nibbles from each byte + int blocks = mdecLen >> 1; + for (int i = 0; i < blocks; i++) + { + // Extract the lower nibble + mdec[decIndex++] = (byte)(m[mOff] & 0x0F); + // Extract the upper nibble (shift right 4 bits) + mdec[decIndex++] = (byte)((m[mOff++] >> 4) & 0x0F); + } + // If there is an extra nibble (odd number of nibbles), decode only the lower nibble + if ((mdecLen & 1) == 1) + { + mdec[decIndex] = (byte)(m[mOff] & 0x0F); + } + } + + /** + * Decodes a nibble-packed byte array into an output array. + * + * @param input the input byte array. + * @param inputOffset the offset in input from which to start decoding. + * @param output the output byte array to hold the decoded nibbles. + * @param mdecLen the total number of nibbles to decode. + */ + public static void decode(byte[] input, int inputOffset, byte[] output, int mdecLen) + { + int decIndex = 0, blocks = mdecLen >> 1; + for (int i = 0; i < blocks; i++) + { + output[decIndex++] = (byte)(input[inputOffset] & 0x0F); + output[decIndex++] = (byte)((input[inputOffset++] >> 4) & 0x0F); + } + if ((mdecLen & 1) == 1) + { + output[decIndex] = (byte)(input[inputOffset] & 0x0F); + } + } + public static void gf16mMul(byte[] a, byte[] b, byte[] c, int rank) { @@ -97,20 +164,6 @@ public static byte inv(byte a) return INV4B[a & 0xF]; } - public static void convertBytesToGF16s(byte[] input, byte[] output, int gf16Count) - { - int pairs = gf16Count / 2; - for (int i = 0; i < pairs; i++) - { - output[i * 2] = (byte)(input[i] & 0x0F); - output[i * 2 + 1] = (byte)((input[i] >> 4) & 0x0F); - } - if (gf16Count % 2 == 1) - { - output[gf16Count - 1] = (byte)(input[pairs] & 0x0F); - } - } - public static void convertGF16sToBytes(byte[] output, byte[] gf16s, int gf16Count) { int pairs = gf16Count / 2; @@ -124,11 +177,15 @@ public static void convertGF16sToBytes(byte[] output, byte[] gf16s, int gf16Coun } } - static GF16Matrix[][][] create3DArray(int d1, int d2, int d3, int rank) { + static GF16Matrix[][][] create3DArray(int d1, int d2, int d3, int rank) + { GF16Matrix[][][] arr = new GF16Matrix[d1][d2][d3]; - for (int i = 0; i < d1; i++) { - for (int j = 0; j < d2; j++) { - for (int k = 0; k < d3; k++) { + for (int i = 0; i < d1; i++) + { + for (int j = 0; j < d2; j++) + { + for (int k = 0; k < d3; k++) + { arr[i][j][k] = new GF16Matrix(rank); } } @@ -136,14 +193,46 @@ static GF16Matrix[][][] create3DArray(int d1, int d2, int d3, int rank) { return arr; } - static GF16Matrix[][] create2DArray(int d1, int d2, int rank) { + static GF16Matrix[][] create2DArray(int d1, int d2, int rank) + { GF16Matrix[][] arr = new GF16Matrix[d1][d2]; - for (int i = 0; i < d1; i++) { - for (int j = 0; j < d2; j++) { + for (int i = 0; i < d1; i++) + { + for (int j = 0; j < d2; j++) + { arr[i][j] = new GF16Matrix(rank); } } return arr; } + private static final int GF16_MASK = 0x249; // Mask for GF(2^4) reduction + + // Constant-time GF16 != 0 check + static int ctGF16IsNotZero(byte val) + { + int v = val & 0xFF; + return (v | (v >>> 1) | (v >>> 2) | (v >>> 3)) & 1; + } + + // GF16 reduction modulo x^4 + x + 1 + private static int gf16Reduce(int idx) + { + int res = idx & 0x49249249; + int upper = idx >>> 12; + res ^= upper ^ (upper << 3); + upper = res >>> 12; + res ^= upper ^ (upper << 3); + upper = res >>> 12; + res ^= upper ^ (upper << 3); + return res & GF16_MASK; + } + + // Convert 32-bit reduced value to 4-bit nibble + static byte gf16ToNibble(int val) + { + int res = gf16Reduce(val); + res |= res >>> 4; + return (byte)((res & 0x5) | ((res >>> 2) & 0xA)); + } } \ No newline at end of file diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java index 57a8a51c83..0f0caea2a4 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -2,13 +2,13 @@ class MapGroup1 { - public final GF16Matrix[][][] P11; // [m][v][v] - public final GF16Matrix[][][] P12; // [m][v][o] - public final GF16Matrix[][][] P21; // [m][o][v] - public final GF16Matrix[][] Aalpha; // [m][alpha] - public final GF16Matrix[][] Balpha; // [m][alpha] - public final GF16Matrix[][] Qalpha1;// [m][alpha] - public final GF16Matrix[][] Qalpha2;// [m][alpha] + public final byte[][][][] p11; // [m][v][v] + public final byte[][][][] p12; // [m][v][o] + public final byte[][][][] p21; // [m][o][v] + public final byte[][][] aAlpha; // [m][alpha] + public final byte[][][] bAlpha; // [m][alpha] + public final byte[][][] qAlpha1;// [m][alpha] + public final byte[][][] qAlpha2;// [m][alpha] public MapGroup1(SnovaParameters params) { @@ -16,14 +16,48 @@ public MapGroup1(SnovaParameters params) int v = params.getV(); int o = params.getO(); int alpha = params.getAlpha(); - int rank = params.getL(); - P11 = GF16Utils.create3DArray(m, v, v, rank); - P12 = GF16Utils.create3DArray(m, v, o, rank); - P21 = GF16Utils.create3DArray(m, o, v, rank); - Aalpha = GF16Utils.create2DArray(m, alpha, rank); - Balpha = GF16Utils.create2DArray(m, alpha, rank); - Qalpha1 = GF16Utils.create2DArray(m, alpha, rank); - Qalpha2 = GF16Utils.create2DArray(m, alpha, rank); + p11 = new byte[m][v][v][16]; + p12 = new byte[m][v][o][16]; + p21 = new byte[m][o][v][16]; + aAlpha = new byte[m][alpha][16]; + bAlpha = new byte[m][alpha][16]; + qAlpha1 = new byte[m][alpha][16]; + qAlpha2 = new byte[m][alpha][16]; } + + public int decode(byte[] input) + { + int inOff = decodeP(input, 0, p11); + inOff = decodeP(input, inOff, p12); + inOff = decodeP(input, inOff, p21); + inOff = decodeAlpha(input, inOff, aAlpha); + inOff = decodeAlpha(input, inOff, bAlpha); + inOff = decodeAlpha(input, inOff, qAlpha1); + inOff = decodeAlpha(input, inOff, qAlpha2); + return inOff; + } + + private int decodeP(byte[] input, int inOff, byte[][][][] p) + { + for (int i = 0; i < p.length; ++i) + { + inOff = decodeAlpha(input, inOff, p[i]); + } + return inOff; + } + + private int decodeAlpha(byte[] input, int inOff, byte[][][] alpha) + { + for (int i = 0; i < alpha.length; ++i) + { + for (int j = 0; j < alpha[i].length; ++j) + { + GF16Utils.decode(input, inOff, alpha[i][j], 0, alpha[i][j].length); + inOff += (alpha[i][j].length + 1) >> 1; + } + } + return inOff; + } + } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 60bfe3f4c7..30c5087c7b 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -1,5 +1,7 @@ package org.bouncycastle.pqc.crypto.snova; +import org.bouncycastle.util.Arrays; + public class SnovaEngine { private final SnovaParameters params; @@ -65,5 +67,44 @@ private void beTheS(byte[] target) } } + // Constant-time GF16 matrix generation + public void genAFqSCT(byte[] c, int cOff, byte[] ptMatrix) + { + int lsq = l * l; + int[] xTemp = new int[lsq]; + + // Initialize diagonal with c[0] + int cX = GF16Utils.gf16FromNibble(c[cOff]); + for (int ij = 0; ij < l; ij++) + { + xTemp[ij * l + ij] = cX; + } + // Process middle coefficients + for (int i1 = 1; i1 < l - 1; i1++) + { + cX = GF16Utils.gf16FromNibble(c[cOff + i1]); + for (int ij = 0; ij < lsq; ij++) + { + xTemp[ij] ^= cX * xS[i1][ij]; + } + } + + // Handle last coefficient with constant-time selection + int zero = GF16Utils.ctGF16IsNotZero(c[cOff + l - 1]); + int val = zero * c[cOff +l - 1] + (1 - zero) * (15 + GF16Utils.ctGF16IsNotZero(c[cOff]) - c[cOff]); + cX = GF16Utils.gf16FromNibble((byte)val); + + for (int ij = 0; ij < lsq; ij++) + { + xTemp[ij] ^= cX * xS[l - 1][ij]; + } + + // Convert to nibbles and clear temp + for (int ij = 0; ij < lsq; ij++) + { + ptMatrix[ij] = GF16Utils.gf16ToNibble(xTemp[ij]); + } + Arrays.fill(xTemp, 0); // Secure clear + } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java index e9f44db7d9..1c1d7ea3ea 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java @@ -3,14 +3,14 @@ class SnovaKeyElements { public final MapGroup1 map1; - public final GF16Matrix[][] T12; // [v][o] + public final byte[][][] T12; // [v][o] public final MapGroup2 map2; public final PublicKey publicKey; public SnovaKeyElements(SnovaParameters params) { map1 = new MapGroup1(params); - T12 = GF16Utils.create2DArray(params.getV(), params.getO(), params.getL()); + T12 = new byte[params.getV()][params.getO()][16]; map2 = new MapGroup2(params); publicKey = new PublicKey(params); } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index 23bc7c90db..81d814ad0f 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -7,6 +7,8 @@ import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; import org.bouncycastle.crypto.KeyGenerationParameters; import org.bouncycastle.crypto.digests.SHAKEDigest; +import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.util.Arrays; public class SnovaKeyPairGenerator @@ -97,13 +99,13 @@ private void packPrivateKey(byte[] esk, byte[] ptPublicKeySeed, byte[] ptPrivate // serializeMatrixGroup(bos, keyElements.map1.Qalpha2); // Serialize T12 - for (GF16Matrix[] row : keyElements.T12) - { - for (GF16Matrix matrix : row) - { - serializeMatrix(bos, matrix); - } - } +// for (GF16Matrix[] row : keyElements.T12) +// { +// for (GF16Matrix matrix : row) +// { +// serializeMatrix(bos, matrix); +// } +// } // Add public and private seeds bos.write(ptPublicKeySeed, 0, ptPublicKeySeed.length); @@ -168,7 +170,7 @@ private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[ // genP22(keyElements.pk.P22, keyElements.T12, keyElements.map1.P21, keyElements.map2.F12); } - private void genSeedsAndT12(GF16Matrix[][] T12, byte[] skSeed) + private void genSeedsAndT12(byte[][][] T12, byte[] skSeed) { int bytesPrngPrivate = (params.getV() * params.getO() * params.getL() + 1) >>> 1; int gf16sPrngPrivate = params.getV() * params.getO() * params.getL(); @@ -181,60 +183,68 @@ private void genSeedsAndT12(GF16Matrix[][] T12, byte[] skSeed) // Convert bytes to GF16 array byte[] gf16PrngOutput = new byte[gf16sPrngPrivate]; - GF16Utils.convertBytesToGF16s(prngOutput, gf16PrngOutput, gf16sPrngPrivate); + GF16Utils.decode(prngOutput, gf16PrngOutput, gf16sPrngPrivate); // Generate T12 matrices - int ptr = 0; + int ptArray = 0; + int l = params.getL(); for (int j = 0; j < params.getV(); j++) { for (int k = 0; k < params.getO(); k++) { //gen_a_FqS_ct - GF16Matrix matrix = new GF16Matrix(params.getL()); - for (int i = 0; i < params.getL(); i++) - { - for (int m = 0; m < params.getL(); m++) - { - matrix.set(i, m, gf16PrngOutput[ptr++]); - } - } - matrix.makeInvertible(); - T12[j][k] = matrix; + engine.genAFqSCT(gf16PrngOutput, ptArray, T12[j][k]); + ptArray += l; } } } -// -// private void genABQP(MapGroup1 map1, byte[] pkSeed) -// { -// byte[] prngOutput = new byte[params.getBytesPrngPublic()]; -// -// if (params.isPkExpandShake()) { -// // SHAKE-based expansion -// SHAKEDigest shake = new SHAKEDigest(256); -// shake.update(pkSeed, 0, pkSeed.length); -// shake.doFinal(prngOutput, 0, prngOutput.length); -// } else { -// // AES-CTR-based expansion -// AESEngine aes = new AESEngine(); -// aes.init(true, new KeyParameter(pkSeed)); -// for (int i = 0; i < prngOutput.length; i += 16) { -// byte[] block = new byte[16]; -// aes.processBlock(block, 0, block, 0); -// System.arraycopy(block, 0, prngOutput, i, Math.min(16, prngOutput.length - i)); -// } -// } -// -// // Convert bytes to GF16 structures -// GF16Utils.convertBytesToGF16s(prngOutput, map1); + + private void genABQP(MapGroup1 map1, byte[] pkSeed) + { + int l = params.getL(); + int lsq = l * l; + int m = params.getM(); + int alpha = params.getAlpha(); + int v = params.getV(); + int o = params.getO(); + int n = v + o; + + int gf16sPrngPublic = lsq * (2 * m * alpha + m * (n * n - m * m)) + l * 2 * m * alpha; + byte[] prngOutput = new byte[gf16sPrngPublic]; + + if (params.isPkExpandShake()) + { + // SHAKE-based expansion + SHAKEDigest shake = new SHAKEDigest(256); + shake.update(pkSeed, 0, pkSeed.length); + shake.doFinal(prngOutput, 0, prngOutput.length); + } + else + { + // AES-CTR-based expansion + AESEngine aes = new AESEngine(); + aes.init(true, new KeyParameter(pkSeed)); + for (int i = 0; i < prngOutput.length; i += 16) + { + byte[] block = new byte[16]; + aes.processBlock(block, 0, block, 0); + System.arraycopy(block, 0, prngOutput, i, Math.min(16, prngOutput.length - i)); + } + } + + // Convert bytes to GF16 structures +// int inOff = map1.decode(prngOutput); // // // Post-processing for invertible matrices -// for (GF16Matrix matrix : map1.Aalpha) { +// for (GF16Matrix matrix : map1.Aalpha) +// { // GF16Utils.makeInvertible(matrix); // } -// for (GF16Matrix matrix : map1.Balpha) { +// for (GF16Matrix matrix : map1.Balpha) +// { // GF16Utils.makeInvertible(matrix); // } -// } + } // private void genF(MapGroup2 map2, MapGroup1 map1, GF16Matrix[][] T12) // { From d42abdebd824426561a9ac46d9cae960600e763b Mon Sep 17 00:00:00 2001 From: David Hook Date: Fri, 7 Mar 2025 20:05:49 +1100 Subject: [PATCH 160/890] updated for tls change --- ant/jdk18+.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/ant/jdk18+.xml b/ant/jdk18+.xml index 4a37034a93..f099672565 100644 --- a/ant/jdk18+.xml +++ b/ant/jdk18+.xml @@ -43,7 +43,6 @@ - From 3a2c2d782ecf701fad1529728d154935fe8cc8c5 Mon Sep 17 00:00:00 2001 From: David Hook Date: Fri, 7 Mar 2025 20:07:25 +1100 Subject: [PATCH 161/890] updated for tls change --- ant/jdk15+.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/ant/jdk15+.xml b/ant/jdk15+.xml index 80412884fc..35a23c11d4 100644 --- a/ant/jdk15+.xml +++ b/ant/jdk15+.xml @@ -43,7 +43,6 @@ - From 81b4930b06448c76bd4131498c21494bb3d47ca8 Mon Sep 17 00:00:00 2001 From: David Hook Date: Fri, 7 Mar 2025 20:09:18 +1100 Subject: [PATCH 162/890] updated for tls change --- ant/jdk14.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/ant/jdk14.xml b/ant/jdk14.xml index 5fe4ae4802..d6769e5cbd 100644 --- a/ant/jdk14.xml +++ b/ant/jdk14.xml @@ -196,8 +196,6 @@ - - From 9d39e6f82f6ed44998fecce5db26073f78d3e679 Mon Sep 17 00:00:00 2001 From: gefeili Date: Sun, 9 Mar 2025 18:30:35 +1030 Subject: [PATCH 163/890] Array clone of Mayo Key parameters. Remove Mayo from BouncyCastleProvider --- .../pqc/crypto/mayo/MayoPrivateKeyParameters.java | 2 +- .../pqc/crypto/mayo/MayoPublicKeyParameters.java | 4 ++-- .../org/bouncycastle/jce/provider/BouncyCastleProvider.java | 6 ------ 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameters.java index f325f96dc4..1dcc6324dd 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPrivateKeyParameters.java @@ -10,7 +10,7 @@ public class MayoPrivateKeyParameters public MayoPrivateKeyParameters(MayoParameters params, byte[] seed_sk) { super(true, params); - this.seed_sk = seed_sk; + this.seed_sk = Arrays.clone(seed_sk); } public byte[] getEncoded() diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPublicKeyParameters.java index 086660edfd..f7df56fb69 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPublicKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoPublicKeyParameters.java @@ -10,12 +10,12 @@ public class MayoPublicKeyParameters public MayoPublicKeyParameters(MayoParameters params, byte[] p) { super(false, params); - this.p = p; + this.p = Arrays.clone(p); } public byte[] getP() { - return p; + return Arrays.clone(p); } public byte[] getEncoded() diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java index 6af5a7297a..f26c929495 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java @@ -38,7 +38,6 @@ import org.bouncycastle.pqc.jcajce.provider.hqc.HQCKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.kyber.KyberKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.lms.LMSKeyFactorySpi; -import org.bouncycastle.pqc.jcajce.provider.mayo.MayoKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.newhope.NHKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.ntru.NTRUKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.picnic.PicnicKeyFactorySpi; @@ -438,11 +437,6 @@ private void loadPQCKeys() addKeyInfoConverter(BCObjectIdentifiers.ntruhps2048677, new NTRUKeyFactorySpi()); addKeyInfoConverter(BCObjectIdentifiers.ntruhps4096821, new NTRUKeyFactorySpi()); addKeyInfoConverter(BCObjectIdentifiers.ntruhrss701, new NTRUKeyFactorySpi()); - - addKeyInfoConverter(BCObjectIdentifiers.mayo1, new MayoKeyFactorySpi()); - addKeyInfoConverter(BCObjectIdentifiers.mayo2, new MayoKeyFactorySpi()); - addKeyInfoConverter(BCObjectIdentifiers.mayo3, new MayoKeyFactorySpi()); - addKeyInfoConverter(BCObjectIdentifiers.mayo5, new MayoKeyFactorySpi()); } public void setParameter(String parameterName, Object parameter) From 0e88a50bcd72dece84f98ee6d1754aef88ad4071 Mon Sep 17 00:00:00 2001 From: gefeili Date: Sun, 9 Mar 2025 18:38:46 +1030 Subject: [PATCH 164/890] Add Mayo to BouncyCastleProvider --- .../jce/provider/BouncyCastleProvider.java | 80 ++++++++++--------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java index f26c929495..f59524555f 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java @@ -38,6 +38,7 @@ import org.bouncycastle.pqc.jcajce.provider.hqc.HQCKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.kyber.KyberKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.lms.LMSKeyFactorySpi; +import org.bouncycastle.pqc.jcajce.provider.mayo.MayoKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.newhope.NHKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.ntru.NTRUKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.picnic.PicnicKeyFactorySpi; @@ -92,29 +93,29 @@ public final class BouncyCastleProvider extends Provider private static final String SYMMETRIC_PACKAGE = "org.bouncycastle.jcajce.provider.symmetric."; private static final String[] SYMMETRIC_GENERIC = - { - "PBEPBKDF1", "PBEPBKDF2", "PBEPKCS12", "TLSKDF", "SCRYPT" - }; + { + "PBEPBKDF1", "PBEPBKDF2", "PBEPKCS12", "TLSKDF", "SCRYPT" + }; private static final String[] SYMMETRIC_MACS = - { - "SipHash", "SipHash128", "Poly1305" - }; + { + "SipHash", "SipHash128", "Poly1305" + }; private static final CryptoServiceProperties[] SYMMETRIC_CIPHERS = - { - // TODO: these numbers need a bit more work, we cap at 256 bits. - service("AES", 256), service("ARC4", 20), service("ARIA", 256), service("Blowfish", 128), service("Camellia", 256), - service("CAST5", 128), service("CAST6", 256), service("ChaCha", 128), service("DES", 56), service("DESede", 112), - service("GOST28147", 128), service("Grainv1", 128), service("Grain128", 128), service("HC128", 128), service("HC256", 256), - service("IDEA", 128), service("Noekeon", 128), service("RC2", 128), service("RC5", 128), service("RC6", 256), - service("Rijndael", 256), service("Salsa20", 128), service("SEED", 128), service("Serpent", 256), service("Shacal2", 128), - service("Skipjack", 80), service("SM4", 128), service("TEA", 128), service("Twofish", 256), service("Threefish", 128), - service("VMPC", 128), service("VMPCKSA3", 128), service("XTEA", 128), service("XSalsa20", 128), service("OpenSSLPBKDF", 128), - service("DSTU7624", 256), service("GOST3412_2015", 256), service("Zuc", 128) - }; - - /* + { + // TODO: these numbers need a bit more work, we cap at 256 bits. + service("AES", 256), service("ARC4", 20), service("ARIA", 256), service("Blowfish", 128), service("Camellia", 256), + service("CAST5", 128), service("CAST6", 256), service("ChaCha", 128), service("DES", 56), service("DESede", 112), + service("GOST28147", 128), service("Grainv1", 128), service("Grain128", 128), service("HC128", 128), service("HC256", 256), + service("IDEA", 128), service("Noekeon", 128), service("RC2", 128), service("RC5", 128), service("RC6", 256), + service("Rijndael", 256), service("Salsa20", 128), service("SEED", 128), service("Serpent", 256), service("Shacal2", 128), + service("Skipjack", 80), service("SM4", 128), service("TEA", 128), service("Twofish", 256), service("Threefish", 128), + service("VMPC", 128), service("VMPCKSA3", 128), service("XTEA", 128), service("XSalsa20", 128), service("OpenSSLPBKDF", 128), + service("DSTU7624", 256), service("GOST3412_2015", 256), service("Zuc", 128) + }; + + /* * Configurable asymmetric ciphers */ private static final String ASYMMETRIC_PACKAGE = "org.bouncycastle.jcajce.provider.asymmetric."; @@ -122,43 +123,43 @@ public final class BouncyCastleProvider extends Provider // this one is required for GNU class path - it needs to be loaded first as the // later ones configure it. private static final String[] ASYMMETRIC_GENERIC = - { - "X509", "IES", "COMPOSITE", "EXTERNAL", "CompositeSignatures", "NoSig" - }; + { + "X509", "IES", "COMPOSITE", "EXTERNAL", "CompositeSignatures", "NoSig" + }; private static final String[] ASYMMETRIC_CIPHERS = - { - "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145", "GM", "EdEC", "LMS", "SPHINCSPlus", "Dilithium", "Falcon", "NTRU", "CONTEXT", "SLHDSA", "MLDSA", "MLKEM" - }; + { + "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145", "GM", "EdEC", "LMS", "SPHINCSPlus", "Dilithium", "Falcon", "NTRU", "CONTEXT", "SLHDSA", "MLDSA", "MLKEM" + }; /* * Configurable digests */ private static final String DIGEST_PACKAGE = "org.bouncycastle.jcajce.provider.digest."; private static final String[] DIGESTS = - { - "GOST3411", "Keccak", "MD2", "MD4", "MD5", "SHA1", "RIPEMD128", "RIPEMD160", "RIPEMD256", "RIPEMD320", "SHA224", - "SHA256", "SHA384", "SHA512", "SHA3", "Skein", "SM3", "Tiger", "Whirlpool", "Blake2b", "Blake2s", "DSTU7564", - "Haraka", "Blake3" - }; + { + "GOST3411", "Keccak", "MD2", "MD4", "MD5", "SHA1", "RIPEMD128", "RIPEMD160", "RIPEMD256", "RIPEMD320", "SHA224", + "SHA256", "SHA384", "SHA512", "SHA3", "Skein", "SM3", "Tiger", "Whirlpool", "Blake2b", "Blake2s", "DSTU7564", + "Haraka", "Blake3" + }; /* * Configurable keystores */ private static final String KEYSTORE_PACKAGE = "org.bouncycastle.jcajce.provider.keystore."; private static final String[] KEYSTORES = - { - "BC", "BCFKS", "PKCS12" - }; + { + "BC", "BCFKS", "PKCS12" + }; /* * Configurable secure random */ private static final String SECURE_RANDOM_PACKAGE = "org.bouncycastle.jcajce.provider.drbg."; private static final String[] SECURE_RANDOMS = - { - "DRBG" - }; + { + "DRBG" + }; private Map serviceMap = new ConcurrentHashMap(); @@ -437,6 +438,11 @@ private void loadPQCKeys() addKeyInfoConverter(BCObjectIdentifiers.ntruhps2048677, new NTRUKeyFactorySpi()); addKeyInfoConverter(BCObjectIdentifiers.ntruhps4096821, new NTRUKeyFactorySpi()); addKeyInfoConverter(BCObjectIdentifiers.ntruhrss701, new NTRUKeyFactorySpi()); + + addKeyInfoConverter(BCObjectIdentifiers.mayo1, new MayoKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.mayo2, new MayoKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.mayo3, new MayoKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.mayo5, new MayoKeyFactorySpi()); } public void setParameter(String parameterName, Object parameter) @@ -480,7 +486,7 @@ public void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className addAttributes(type + "." + oid, attributes); addAttributes(type + ".OID." + oid, attributes); } - + public void addKeyInfoConverter(ASN1ObjectIdentifier oid, AsymmetricKeyInfoConverter keyInfoConverter) { synchronized (keyInfoConverters) From f4a2618924873095915b8292f50276630ceeec79 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 30 Jan 2025 17:38:07 +1030 Subject: [PATCH 165/890] TODO: try to get the correct q --- .../crypto/kems/SAKKEKEMSGenerator.java | 150 ++++++++++++++++++ .../bouncycastle/crypto/kems/SAKKEUtils.java | 66 ++++++++ .../crypto/params/SAKKEPrivateKey.java | 37 +++++ .../crypto/params/SAKKEPublicKey.java | 52 ++++++ .../crypto/kems/test/SAKKEKEMSTest.java | 81 ++++++++++ 5 files changed, 386 insertions(+) create mode 100644 core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKey.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java create mode 100644 core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java new file mode 100644 index 0000000000..e44d681f12 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -0,0 +1,150 @@ +package org.bouncycastle.crypto.kems; + +import org.bouncycastle.crypto.EncapsulatedSecretGenerator; +import org.bouncycastle.crypto.SecretWithEncapsulation; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.params.SAKKEPublicKey; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.BigIntegers; + +import java.math.BigInteger; +import java.nio.ByteBuffer; +import java.security.SecureRandom; + +public class SAKKEKEMSGenerator + implements EncapsulatedSecretGenerator +{ + private final SAKKEPublicKey publicParams; + private final SecureRandom random; + + public SAKKEKEMSGenerator(SAKKEPublicKey params, SecureRandom random) + { + this.publicParams = params; + this.random = random; + } + + @Override + public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey) + { + // 1. Generate random SSV in range [0, 2^n - 1] + BigInteger ssv = new BigInteger(publicParams.getN(), random); + + // 2. Compute r = HashToIntegerRange(SSV || b, q) + BigInteger b = getRecipientId((SAKKEPublicKey)recipientKey); + BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), publicParams.getQ()); + + // 3. Compute R_(b,S) = [r]([b]P + Z_S) + ECPoint bP = publicParams.getP().multiply(b); // [b]P + ECPoint Z_S = publicParams.getZ(); // Z_S + ECPoint R_bS = bP.add(Z_S).multiply(r); // [r]([b]P + Z_S) + + // 4. Compute H = SSV XOR HashToIntegerRange( g^r, 2^n ) + BigInteger g_r = pairing(R_bS, publicParams.getP(), publicParams.getQ(), publicParams.getP().getCurve().getField().getCharacteristic()); + BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(publicParams.getN())); // 2^n + + BigInteger H = ssv.xor(mask); + + // 5. Encode encapsulated data (R_bS, H) + byte[] encapsulated = encodeData(R_bS, H); + + return new SecretWithEncapsulationImpl( + BigIntegers.asUnsignedByteArray(publicParams.getN() / 8, ssv), // Output SSV as key material + encapsulated + ); + } + + private BigInteger getRecipientId(SAKKEPublicKey pubKey) + { + byte[] hashedId = SAKKEUtils.hash(pubKey.getZ().getEncoded(false)); // Hash Z_S + return new BigInteger(1, hashedId).mod(pubKey.getQ().subtract(BigInteger.ONE)).add(BigIntegers.TWO); + } + + /** + * Computes the Tate-Lichtenbaum pairing ⟨P, Q⟩ as per RFC 6508. + *

+ * //* @param P First point (on E(F_p)). + * + * @param Q Second point (on E(F_p)). + * @return Result of the pairing in the field F_p^2. + */ + public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) + { + ECCurve curve = R.getCurve(); + ECFieldElement i = curve.fromBigInteger(BigInteger.ONE.negate()); // i = -1 in F_p^2 + + ECPoint C = R; + BigInteger c = p.add(BigInteger.ONE).divide(q); + ECFieldElement v = curve.fromBigInteger(BigInteger.ONE); // v = 1 in F_p + + String qBits = q.subtract(BigInteger.ONE).toString(2); // Binary representation of q-1 + + for (int j = 1; j < qBits.length(); j++) + { // Skip MSB + // l = (3 * (C_x^2 - 1)) / (2 * C_y) + ECFieldElement Cx = C.getAffineXCoord(); + ECFieldElement Cy = C.getAffineYCoord(); + ECFieldElement l = Cx.square().multiply(curve.fromBigInteger(ECFieldElement.THREE)).subtract(curve.fromBigInteger(BigInteger.ONE)) + .divide(Cy.multiply(curve.fromBigInteger(BigIntegers.TWO))); + + // v = v^2 * (l * (Q_x + C_x) + (i * Q_y - C_y)) + ECFieldElement Qx = Q.getAffineXCoord(); + ECFieldElement Qy = Q.getAffineYCoord(); + v = v.square().multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); + + // Double the point + C = C.twice(); + + // If the bit is 1, perform additional step + if (qBits.charAt(j) == '1') + { + // l = (C_y - R_y) / (C_x - R_x) + ECFieldElement Rx = R.getAffineXCoord(); + ECFieldElement Ry = R.getAffineYCoord(); + l = Cy.subtract(Ry).divide(Cx.subtract(Rx)); + + // v = v * (l * (Q_x + C_x) + (i * Q_y - C_y)) + v = v.multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); + + // C = C + R + C = C.add(R); + } + } + + // Compute v^c + v = curve.fromBigInteger(v.toBigInteger().pow(c.intValue())); + + // Convert to F_p representative + return computeFpRepresentative(v, curve); + } + + private static BigInteger computeFpRepresentative(ECFieldElement t, ECCurve curve) + { + // Characteristic of F_p + BigInteger p = ((ECCurve.Fp) curve).getQ(); + + // Assume t = a + i * b in F_p² → extract a, b + ECFieldElement a = t; // In F_p², a is the real part + ECFieldElement b = t.multiply(curve.fromBigInteger(BigInteger.ONE.negate())); // Imaginary part + + // Compute b/a mod p + return b.toBigInteger().multiply(a.toBigInteger().modInverse(p)).mod(p); + } + + public static byte[] encodeData(ECPoint R_bS, BigInteger H) { + // 1. Serialize EC Point (use compressed format for efficiency) + byte[] R_bS_bytes = R_bS.getEncoded(true); + + // 2. Serialize H (convert to a fixed-length byte array) + byte[] H_bytes = H.toByteArray(); + + // 3. Combine both into a single byte array + ByteBuffer buffer = ByteBuffer.allocate(R_bS_bytes.length + H_bytes.length); + buffer.put(R_bS_bytes); + buffer.put(H_bytes); + + return buffer.array(); + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java new file mode 100644 index 0000000000..bc3a9a90f2 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java @@ -0,0 +1,66 @@ +package org.bouncycastle.crypto.kems; + +import java.math.BigInteger; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.Strings; +import org.bouncycastle.util.encoders.Hex; + +public class SAKKEUtils +{ + public static BigInteger hashToIntegerRange(byte[] input, BigInteger q) + { + // RFC 6508 Section 5.1: Hashing to an Integer Range + SHA256Digest digest = new SHA256Digest(); + byte[] hash = new byte[digest.getDigestSize()]; + + // Step 1: Compute A = hashfn(s) + digest.update(input, 0, input.length); + digest.doFinal(hash, 0); + byte[] A = hash.clone(); + + // Step 2: Initialize h_0 to all-zero bytes of hashlen size + byte[] h = new byte[digest.getDigestSize()]; + + // Step 3: Compute l = Ceiling(lg(n)/hashlen) + int l = q.bitLength() >> 8; + + BigInteger v = BigInteger.ZERO; + + // Step 4: Compute h_i and v_i + for (int i = 1; i <= l; i++) + { + // h_i = hashfn(h_{i-1}) + digest.update(h, 0, h.length); + digest.doFinal(h, 0); + System.out.println("h_"+i+":" +new String(Hex.encode(h))); + // v_i = hashfn(h_i || A) + digest.update(h, 0, h.length); + digest.update(A, 0, A.length); + byte[] v_i = new byte[digest.getDigestSize()]; + digest.doFinal(v_i, 0); + System.out.println("v_"+i+":" +new String(Hex.encode(v_i))); + // Append v_i to v' + v = v.shiftLeft(v_i.length * 8).add(new BigInteger(1, v_i)); + } + System.out.println("v:" +new String(Hex.encode(v.toByteArray()))); + // Step 6: v = v' mod n + return v.mod(q); + } + + public static byte[] hash(byte[] data) + { + Digest digest = new SHA256Digest(); + byte[] rlt = new byte[digest.getDigestSize()]; + digest.update(data, 0, data.length); + digest.doFinal(rlt, 0); + return rlt; + } + + public static byte[] hash(ECPoint point) + { + return hash(point.getEncoded(false)); // Use uncompressed encoding + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKey.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKey.java new file mode 100644 index 0000000000..02e6d7c54b --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKey.java @@ -0,0 +1,37 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECPoint; + +public class SAKKEPrivateKey + extends AsymmetricKeyParameter +{ + private final BigInteger b; // User's identity + private final ECPoint K; // Private key K_a + private final SAKKEPublicKey publicParams; + + public SAKKEPrivateKey(BigInteger b, ECPoint K, SAKKEPublicKey publicParams) + { + super(true); + this.b = b; + this.K = K; + this.publicParams = publicParams; + } + + // Getters + public ECPoint getK() + { + return K; + } + + public BigInteger getB() + { + return b; + } + + public SAKKEPublicKey getPublicParams() + { + return publicParams; + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java new file mode 100644 index 0000000000..81829f9caf --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java @@ -0,0 +1,52 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; + +public class SAKKEPublicKey + extends AsymmetricKeyParameter +{ + private final ECCurve curve; + private final ECPoint P; // Base point + private final ECPoint Z; // KMS Public Key: Z = [z]P + private final BigInteger q; // Subgroup order + private final int n; // SSV bit length + + public SAKKEPublicKey(ECCurve curve, ECPoint P, ECPoint Z, BigInteger q, int n) + { + super(false); + this.curve = curve; + this.P = P; + this.Z = Z; + this.q = q; + this.n = n; + } + + // Getters + public ECCurve getCurve() + { + return curve; + } + + public ECPoint getP() + { + return P; + } + + public ECPoint getZ() + { + return Z; + } + + public BigInteger getQ() + { + return q; + } + + public int getN() + { + return n; + } +} diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java new file mode 100644 index 0000000000..61ba76350d --- /dev/null +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -0,0 +1,81 @@ +package org.bouncycastle.crypto.kems.test; + +import java.math.BigInteger; + + +import org.bouncycastle.crypto.kems.SAKKEKEMSGenerator; +import org.bouncycastle.crypto.kems.SAKKEUtils; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; +import org.junit.Assert; + +public class SAKKEKEMSTest + extends SimpleTest +{ + public static void main(String[] args) + throws Exception + { + SAKKEKEMSTest test = new SAKKEKEMSTest(); + test.performTest(); + // Expected Rb values +// BigInteger expectedRbx = new BigInteger("44E8AD44AB8592A6A5A3DDCA5CF896C718043606A01D650DEF37A01F37C228C332FC317354E2C274D4DAF8AD001054C7... +// BigInteger expectedRby = new BigInteger("557E134AD85BB1D4B9CE4F8BE4B08A12BABF55B1D6F1D7A638019EA28E15AB1C9F76375FDD1210D4F4351B9A009486B7... +// +// // Instantiate SAKKE KEM Generator +// SAKKEKEMSGenerator kem = new SAKKEKEMSGenerator(); +// EncapsulatedData encapsulatedData = kem.encapsulate(SSV); +// +// // Validate results +// boolean testPassed = expectedRbx.equals(encapsulatedData.getRbx()) && expectedRby.equals(encapsulatedData.getRby()); + + //System.out.println("SAKKE KEM Test " + (testPassed ? "PASSED" : "FAILED")); + } + + private static byte[] hexStringToByteArray(String s) + { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) + { + data[i / 2] = (byte)((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i + 1), 16)); + } + return data; + } + + @Override + public String getName() + { + return null; + } + + @Override + public void performTest() + throws Exception + { +// BigInteger z = new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F"); +// BigInteger Zx = new BigInteger("5958EF1B1679BF099B3A030DF255AA6A23C1D8F143D4D23F753E69BD27A832F38CB4AD53DDEF" +// + "4260B0FE8BB45C4C1FF510EFFE300367A37B61F701D914AEF09724825FA0707D61A6DFF4FBD7273566CDDE352A0B04B7C16A78309BE" +// + "640697DE747613A5FC195E8B9F328852A579DB8F99B1D0034479EA9C5595F47C4B2F54FF2"); +// BigInteger Zy = new BigInteger("1508D37514DCF7A8E143A6058C09A6BF2C9858CA37C258065AE6BF7532BC8B5B63383866E075" +// + "3C5AC0E72709F8445F2E6178E065857E0EDA10F68206B63505ED87E534FB2831FF957FB7DC619DAE61301EEACC2FDA3680EA499925" + +// "8A833CEA8FC67C6D19487FB449059F26CC8AAB655AB58B7CC796E24E9A394095754F5F8BAE"); +// + byte[] b = Hex.decode("323031312D30320074656C3A2B34343737303039303031323300"); + + byte[] SSV = Hex.decode("123456789ABCDEF0123456789ABCDEF0"); + byte[] expectedR = Hex.decode("13EE3E1B8DAC5DB168B1CEB32F0566A4C273693F78BAFFA2A2EE6A686E6BD90F8206CCAB84E7F" + + "42ED39BD4FB131012ECCA2ECD2119414560C17CAB46B956A80F58A3302EB3E2C9A228FBA7ED34D8ACA2392DA1FFB0B17B2320AE09AAEDF" + + "D0235F6FE0EB65337A63F9CC97728B8E5AD0460FADE144369AA5B2166213247712096"); + + BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(SSV, b), BigInteger.valueOf(1024)); + + System.out.println("r:" +new String(Hex.encode(r.toByteArray()))); + + System.out.println("r:" +new String(Hex.encode(expectedR))); + + Assert.assertTrue(Arrays.areEqual(r.toByteArray(), expectedR)); + } +} From b69dd7362348e9b285d7d3be34ee48f31b9c47a9 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 31 Jan 2025 16:59:02 +1030 Subject: [PATCH 166/890] hashToIntegerRange is correct. TODO: fix the curve definition and pairing function --- .../crypto/kems/SAKKEKEMSGenerator.java | 140 ++++++++++++++++-- .../bouncycastle/crypto/kems/SAKKEUtils.java | 9 +- .../crypto/params/SAKKEPublicKey.java | 6 +- .../crypto/kems/test/SAKKEKEMSTest.java | 105 +++++++++++-- 4 files changed, 228 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index e44d681f12..1024ed7428 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -7,8 +7,10 @@ import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; +import org.bouncycastle.util.encoders.Hex; import java.math.BigInteger; import java.nio.ByteBuffer; @@ -17,12 +19,74 @@ public class SAKKEKEMSGenerator implements EncapsulatedSecretGenerator { - private final SAKKEPublicKey publicParams; + + // private static final BigInteger p = new BigInteger(Hex.decode("997ABB1F 0A563FDA 65C61198 DAD0657A\n" + +// " 416C0CE1 9CB48261 BE9AE358 B3E01A2E\n" + +// " F40AAB27 E2FC0F1B 228730D5 31A59CB0\n" + +// " E791B39F F7C88A19 356D27F4 A666A6D0\n" + +// " E26C6487 326B4CD4 512AC5CD 65681CE1\n" + +// " B6AFF4A8 31852A82 A7CF3C52 1C3C09AA\n" + +// " 9F94D6AF 56971F1F FCE3E823 89857DB0\n" + +// " 80C5DF10 AC7ACE87 666D807A FEA85FEB")); + private static final BigInteger p = new BigInteger( + "997ABB1F0A563FDA65C61198DAD0657A416C0CE19CB48261BE9AE358B3E01A2E" + + "F40AAB27E2FC0F1B228730D531A59CB0E791B39FF7C88A19356D27F4A666A6D0" + + "E26C6487326B4CD4512AC5CD65681CE1B6AFF4A831852A82A7CF3C521C3C09AA" + + "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 + ); + // private static final BigInteger q = new BigInteger(Hex.decode("265EAEC7 C2958FF6 99718466 36B4195E\n" + +// " 905B0338 672D2098 6FA6B8D6 2CF8068B\n" + +// " BD02AAC9 F8BF03C6 C8A1CC35 4C69672C\n" + +// " 39E46CE7 FDF22286 4D5B49FD 2999A9B4\n" + +// " 389B1921 CC9AD335 144AB173 595A0738\n" + +// " 6DABFD2A 0C614AA0 A9F3CF14 870F026A\n" + +// " A7E535AB D5A5C7C7 FF38FA08 E2615F6C\n" + +// " 203177C4 2B1EB3A1 D99B601E BFAA17FB")); + private static final BigInteger q = new BigInteger( + "265EAEC7C2958FF69971846636B4195E905B0338672D20986FA6B8D62CF8068B" + + "BD02AAC9F8BF03C6C8A1CC354C69672C39E46CE7FDF222864D5B49FD2999A9B4" + + "389B1921CC9AD335144AB173595A07386DABFD2A0C614AA0A9F3CF14870F026A" + + "A7E535ABD5A5C7C7FF38FA08E2615F6C203177C42B1EB3A1D99B601EBFAA17FB", 16 + ); +// private static final BigInteger a = BigInteger.valueOf(-3).mod(p); // y² = x³ - 3x +// private static final BigInteger b = BigInteger.ZERO; +// private static final ECCurve.Fp curve = new ECCurve.Fp( +// p, // Prime p +// BigInteger.valueOf(-3).mod(p), // a = -3 +// BigInteger.ZERO, // b = 0 +// q, // Order of the subgroup (from RFC 6509) +// BigInteger.ONE // Cofactor = 1 +// ); + + // Base point P = (Px, Py) + private static final BigInteger Px = new BigInteger( + "53FC09EE332C29AD0A7990053ED9B52A2B1A2FD60AEC69C698B2F204B6FF7CBF" + + "B5EDB6C0F6CE2308AB10DB9030B09E1043D5F22CDB9DFA55718BD9E7406CE890" + + "9760AF765DD5BCCB337C86548B72F2E1A702C3397A60DE74A7C1514DBA66910D" + + "D5CFB4CC80728D87EE9163A5B63F73EC80EC46C4967E0979880DC8ABEAE63895", 16 + ); + + private static final BigInteger Py = new BigInteger( + "0A8249063F6009F1F9F1F0533634A135D3E82016029906963D778D821E141178" + + "F5EA69F4654EC2B9E7F7F5E5F0DE55F66B598CCF9A140B2E416CFF0CA9E032B9" + + "70DAE117AD547C6CCAD696B5B7652FE0AC6F1E80164AA989492D979FC5A4D5F2" + + "13515AD7E9CB99A980BDAD5AD5BB4636ADB9B5706A67DCDE75573FD71BEF16D7", 16 + ); + + + BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + + " C6A87BD1 FB94C41E 27FABE65 8E015A87\n" + + " 371E9474 4C96FEDA 449AE956 3F8BC446\n" + + " CBFDA85D 5D00EF57 7072DA8F 541721BE\n" + + " EE0FAED1 828EAB90 B99DFB01 38C78433\n" + + " 55DF0460 B4A9FD74 B4F1A32B CAFA1FFA\n" + + " D682C033 A7942BCC E3720F20 B9B7B040\n" + + " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); + private final int n = 128; private final SecureRandom random; - public SAKKEKEMSGenerator(SAKKEPublicKey params, SecureRandom random) + public SAKKEKEMSGenerator(SecureRandom random) { - this.publicParams = params; this.random = random; } @@ -30,20 +94,67 @@ public SAKKEKEMSGenerator(SAKKEPublicKey params, SecureRandom random) public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey) { // 1. Generate random SSV in range [0, 2^n - 1] - BigInteger ssv = new BigInteger(publicParams.getN(), random); + BigInteger ssv = new BigInteger("123456789ABCDEF0123456789ABCDEF0", 16);//new BigInteger(n, random); // 2. Compute r = HashToIntegerRange(SSV || b, q) - BigInteger b = getRecipientId((SAKKEPublicKey)recipientKey); - BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), publicParams.getQ()); - + BigInteger b = new BigInteger("323031312D30320074656C3A2B34343737303039303031323300", 16); //getRecipientId((SAKKEPublicKey)recipientKey); + BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); + System.out.println(new String(Hex.encode(r.toByteArray()))); + ECCurve.Fp curve = new ECCurve.Fp( + p, // Prime p + BigInteger.valueOf(-3).mod(p), // a = -3 + BigInteger.ZERO, // , + g, // Order of the subgroup (from RFC 6509) + BigInteger.ONE // Cofactor = 1 + ); + ECPoint P = curve.createPoint(Px, Py); + ECPoint G = curve.createPoint( + new BigInteger(Hex.decode("53FC09EE 332C29AD 0A799005 3ED9B52A\n" + + " 2B1A2FD6 0AEC69C6 98B2F204 B6FF7CBF\n" + + " B5EDB6C0 F6CE2308 AB10DB90 30B09E10\n" + + " 43D5F22C DB9DFA55 718BD9E7 406CE890\n" + + " 9760AF76 5DD5BCCB 337C8654 8B72F2E1\n" + + " A702C339 7A60DE74 A7C1514D BA66910D\n" + + " D5CFB4CC 80728D87 EE9163A5 B63F73EC\n" + + " 80EC46C4 967E0979 880DC8AB EAE63895")), // Px + new BigInteger(Hex.decode("0A824906 3F6009F1 F9F1F053 3634A135\n" + + " D3E82016 02990696 3D778D82 1E141178\n" + + " F5EA69F4 654EC2B9 E7F7F5E5 F0DE55F6\n" + + " 6B598CCF 9A140B2E 416CFF0C A9E032B9\n" + + " 70DAE117 AD547C6C CAD696B5 B7652FE0\n" + + " AC6F1E80 164AA989 492D979F C5A4D5F2\n" + + " 13515AD7 E9CB99A9 80BDAD5A D5BB4636\n" + + " ADB9B570 6A67DCDE 75573FD7 1BEF16D7")) // Py + ); + ECPoint Z = curve.createPoint( + new BigInteger("5958EF1B1679BF099B3A030DF255AA6A" + + "23C1D8F143D4D23F753E69BD27A832F3" + + "8CB4AD53DDEF4260B0FE8BB45C4C1FF5" + + "10EFFE300367A37B61F701D914AEF097" + + "24825FA0707D61A6DFF4FBD7273566CD" + + "DE352A0B04B7C16A78309BE640697DE7" + + "47613A5FC195E8B9F328852A579DB8F9" + + "9B1D0034479EA9C5595F47C4B2F54FF2", 16), // Px + new BigInteger("1508D37514DCF7A8E143A6058C09A6BF" + + "2C9858CA37C258065AE6BF7532BC8B5B" + + "63383866E0753C5AC0E72709F8445F2E" + + "6178E065857E0EDA10F68206B63505ED" + + "87E534FB2831FF957FB7DC619DAE6130" + + "1EEACC2FDA3680EA4999258A833CEA8F" + + "C67C6D19487FB449059F26CC8AAB655A" + + "B58B7CC796E24E9A394095754F5F8BAE", 16) // Py + ); // 3. Compute R_(b,S) = [r]([b]P + Z_S) - ECPoint bP = publicParams.getP().multiply(b); // [b]P - ECPoint Z_S = publicParams.getZ(); // Z_S + ECPoint bP = P.multiply(b).normalize(); + ECPoint Z_S = Z;// P.multiply(ssv).normalize();;//.multiply(new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F", 16)); // Z_S ECPoint R_bS = bP.add(Z_S).multiply(r); // [r]([b]P + Z_S) + System.out.println("R_Bs x:" + new String(Hex.encode(R_bS.getXCoord().toBigInteger().toByteArray()))); + System.out.println("R_Bs y:" + new String(Hex.encode(R_bS.getYCoord().toBigInteger().toByteArray()))); + // 4. Compute H = SSV XOR HashToIntegerRange( g^r, 2^n ) - BigInteger g_r = pairing(R_bS, publicParams.getP(), publicParams.getQ(), publicParams.getP().getCurve().getField().getCharacteristic()); - BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(publicParams.getN())); // 2^n + BigInteger g_r = pairing(R_bS, G, p, q); + BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(n)); // 2^n BigInteger H = ssv.xor(mask); @@ -51,7 +162,7 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip byte[] encapsulated = encodeData(R_bS, H); return new SecretWithEncapsulationImpl( - BigIntegers.asUnsignedByteArray(publicParams.getN() / 8, ssv), // Output SSV as key material + BigIntegers.asUnsignedByteArray(n / 8, ssv), // Output SSV as key material encapsulated ); } @@ -123,7 +234,7 @@ public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger private static BigInteger computeFpRepresentative(ECFieldElement t, ECCurve curve) { // Characteristic of F_p - BigInteger p = ((ECCurve.Fp) curve).getQ(); + BigInteger p = ((ECCurve.Fp)curve).getQ(); // Assume t = a + i * b in F_p² → extract a, b ECFieldElement a = t; // In F_p², a is the real part @@ -133,7 +244,8 @@ private static BigInteger computeFpRepresentative(ECFieldElement t, ECCurve curv return b.toBigInteger().multiply(a.toBigInteger().modInverse(p)).mod(p); } - public static byte[] encodeData(ECPoint R_bS, BigInteger H) { + public static byte[] encodeData(ECPoint R_bS, BigInteger H) + { // 1. Serialize EC Point (use compressed format for efficiency) byte[] R_bS_bytes = R_bS.getEncoded(true); diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java index bc3a9a90f2..05938e2ac5 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java @@ -5,7 +5,6 @@ import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.math.ec.ECPoint; -import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; public class SAKKEUtils @@ -30,22 +29,22 @@ public static BigInteger hashToIntegerRange(byte[] input, BigInteger q) BigInteger v = BigInteger.ZERO; // Step 4: Compute h_i and v_i - for (int i = 1; i <= l; i++) + for (int i = 0; i <= l; i++) { // h_i = hashfn(h_{i-1}) digest.update(h, 0, h.length); digest.doFinal(h, 0); - System.out.println("h_"+i+":" +new String(Hex.encode(h))); + //System.out.println("h_"+i+":" +new String(Hex.encode(h))); // v_i = hashfn(h_i || A) digest.update(h, 0, h.length); digest.update(A, 0, A.length); byte[] v_i = new byte[digest.getDigestSize()]; digest.doFinal(v_i, 0); - System.out.println("v_"+i+":" +new String(Hex.encode(v_i))); + //System.out.println("v_"+i+":" +new String(Hex.encode(v_i))); // Append v_i to v' v = v.shiftLeft(v_i.length * 8).add(new BigInteger(1, v_i)); } - System.out.println("v:" +new String(Hex.encode(v.toByteArray()))); + //System.out.println("v:" +new String(Hex.encode(v.toByteArray()))); // Step 6: v = v' mod n return v.mod(q); } diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java index 81829f9caf..c992dbb70b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java @@ -4,20 +4,20 @@ import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve; public class SAKKEPublicKey extends AsymmetricKeyParameter { - private final ECCurve curve; + private final ECCurve curve = new SecP256R1Curve(); private final ECPoint P; // Base point private final ECPoint Z; // KMS Public Key: Z = [z]P private final BigInteger q; // Subgroup order private final int n; // SSV bit length - public SAKKEPublicKey(ECCurve curve, ECPoint P, ECPoint Z, BigInteger q, int n) + public SAKKEPublicKey(ECPoint P, ECPoint Z, BigInteger q, int n) { super(false); - this.curve = curve; this.P = P; this.Z = Z; this.q = q; diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index 61ba76350d..4a40e99c5c 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -1,13 +1,17 @@ package org.bouncycastle.crypto.kems.test; import java.math.BigInteger; +import java.security.SecureRandom; import org.bouncycastle.crypto.kems.SAKKEKEMSGenerator; import org.bouncycastle.crypto.kems.SAKKEUtils; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.FixedSecureRandom; import org.bouncycastle.util.test.SimpleTest; import org.junit.Assert; @@ -55,13 +59,29 @@ public String getName() public void performTest() throws Exception { -// BigInteger z = new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F"); -// BigInteger Zx = new BigInteger("5958EF1B1679BF099B3A030DF255AA6A23C1D8F143D4D23F753E69BD27A832F38CB4AD53DDEF" -// + "4260B0FE8BB45C4C1FF510EFFE300367A37B61F701D914AEF09724825FA0707D61A6DFF4FBD7273566CDDE352A0B04B7C16A78309BE" -// + "640697DE747613A5FC195E8B9F328852A579DB8F99B1D0034479EA9C5595F47C4B2F54FF2"); -// BigInteger Zy = new BigInteger("1508D37514DCF7A8E143A6058C09A6BF2C9858CA37C258065AE6BF7532BC8B5B63383866E075" -// + "3C5AC0E72709F8445F2E6178E065857E0EDA10F68206B63505ED87E534FB2831FF957FB7DC619DAE61301EEACC2FDA3680EA499925" + -// "8A833CEA8FC67C6D19487FB449059F26CC8AAB655AB58B7CC796E24E9A394095754F5F8BAE"); + BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068" + + " C6A87BD1 FB94C41E 27FABE65 8E015A87" + + " 371E9474 4C96FEDA 449AE956 3F8BC446" + + " CBFDA85D 5D00EF57 7072DA8F 541721BE" + + " EE0FAED1 828EAB90 B99DFB01 38C78433" + + " 55DF0460 B4A9FD74 B4F1A32B CAFA1FFA" + + " D682C033 A7942BCC E3720F20 B9B7B040" + + " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); + BigInteger z = new BigInteger(Hex.decode("AFF429D35F84B110D094803B3595A6E2998BC99F")); + BigInteger Zx = new BigInteger(Hex.decode("5958EF1B1679BF099B3A030DF255AA6A23C1D8F143D4D23F753E69BD27A832F38CB4AD53DDEF" + + "4260B0FE8BB45C4C1FF510EFFE300367A37B61F701D914AEF09724825FA0707D61A6DFF4FBD7273566CDDE352A0B04B7C16A78309BE" + + "640697DE747613A5FC195E8B9F328852A579DB8F99B1D0034479EA9C5595F47C4B2F54FF2")); + BigInteger Zy = new BigInteger(Hex.decode("1508D37514DCF7A8E143A6058C09A6BF2C9858CA37C258065AE6BF7532BC8B5B63383866E075" + + "3C5AC0E72709F8445F2E6178E065857E0EDA10F68206B63505ED87E534FB2831FF957FB7DC619DAE61301EEACC2FDA3680EA499925" + + "8A833CEA8FC67C6D19487FB449059F26CC8AAB655AB58B7CC796E24E9A394095754F5F8BAE")); + BigInteger q = new BigInteger(Hex.decode("265EAEC7 C2958FF6 99718466 36B4195E" + + " 905B0338 672D2098 6FA6B8D6 2CF8068B" + + " BD02AAC9 F8BF03C6 C8A1CC35 4C69672C" + + " 39E46CE7 FDF22286 4D5B49FD 2999A9B4" + + " 389B1921 CC9AD335 144AB173 595A0738" + + " 6DABFD2A 0C614AA0 A9F3CF14 870F026A" + + " A7E535AB D5A5C7C7 FF38FA08 E2615F6C" + + " 203177C4 2B1EB3A1 D99B601E BFAA17FB")); // byte[] b = Hex.decode("323031312D30320074656C3A2B34343737303039303031323300"); @@ -70,12 +90,77 @@ public void performTest() + "42ED39BD4FB131012ECCA2ECD2119414560C17CAB46B956A80F58A3302EB3E2C9A228FBA7ED34D8ACA2392DA1FFB0B17B2320AE09AAEDF" + "D0235F6FE0EB65337A63F9CC97728B8E5AD0460FADE144369AA5B2166213247712096"); - BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(SSV, b), BigInteger.valueOf(1024)); + BigInteger kbx = new BigInteger("93AF67E5007BA6E6A80DA793DA300FA4" + + "B52D0A74E25E6E7B2B3D6EE9D18A9B5C" + + "5023597BD82D8062D34019563BA1D25C" + + "0DC56B7B979D74AA50F29FBF11CC2C93" + + "F5DFCA615E609279F6175CEADB00B58C" + + "6BEE1E7A2A47C4F0C456F05259A6FA94" + + "A634A40DAE1DF593D4FECF688D5FC678" + + "BE7EFC6DF3D6835325B83B2C6E69036B", 16); - System.out.println("r:" +new String(Hex.encode(r.toByteArray()))); + BigInteger kby = new BigInteger("155F0A27241094B04BFB0BDFAC6C670A" + + "65C325D39A069F03659D44CA27D3BE8D" + + "F311172B554160181CBE94A2A783320C" + + "ED590BC42644702CF371271E496BF20F" + + "588B78A1BC01ECBB6559934BDD2FB65D" + + "2884318A33D1A42ADF5E33CC5800280B" + + "28356497F87135BAB9612A1726042440" + + "9AC15FEE996B744C332151235DECB0F5", 16); + BigInteger w = new BigInteger(Hex.decode("7D2A8438 E6291C64 9B6579EB 3B79EAE9" + + "48B1DE9E 5F7D1F40 70A08F8D B6B3C515" + + "6F2201AF FBB5CB9D 82AA3EC0 D0398B89" + + "ABC78A13 A760C0BF 3F77E63D 0DF3F1A3" + + "41A41B88 11DF197F D6CD0F00 3125606F" + + "4F109F40 0F7292A1 0D255E3C 0EBCCB42" + + "53FB182C 68F09CF6 CD9C4A53 DA6C74AD" + + "007AF36B 8BCA979D 5895E282 F483FCD6")); + BigInteger Rbx = new BigInteger(Hex.decode("44E8AD44 AB8592A6 A5A3DDCA 5CF896C7" + + "18043606 A01D650D EF37A01F 37C228C3" + + "32FC3173 54E2C274 D4DAF8AD 001054C7" + + "6CE57971 C6F4486D 57230432 61C506EB" + + "F5BE438F 53DE04F0 67C776E0 DD3B71A6" + + "29013328 3725A532 F21AF145 126DC1D7" + + "77ECC27B E50835BD 28098B8A 73D9F801" + + "D893793A 41FF5C49 B87E79F2 BE4D56CE")); + BigInteger Rby = new BigInteger(Hex.decode("557E134A D85BB1D4 B9CE4F8B E4B08A12" + + "BABF55B1 D6F1D7A6 38019EA2 8E15AB1C" + + "9F76375F DD1210D4 F4351B9A 009486B7" + + "F3ED46C9 65DED2D8 0DADE4F3 8C6721D5" + + "2C3AD103 A10EBD29 59248B4E F006836B" + + "F097448E 6107C9ED EE9FB704 823DF199" + + "F832C905 AE45F8A2 47A072D8 EF729EAB" + + "C5E27574 B07739B3 4BE74A53 2F747B86")); + BigInteger p = new BigInteger( + "997ABB1F0A563FDA65C61198DAD0657A416C0CE19CB48261BE9AE358B3E01A2E" + + "F40AAB27E2FC0F1B228730D531A59CB0E791B39FF7C88A19356D27F4A666A6D0" + + "E26C6487326B4CD4512AC5CD65681CE1B6AFF4A831852A82A7CF3C521C3C09AA" + + "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 + ); - System.out.println("r:" +new String(Hex.encode(expectedR))); + ECCurve.Fp curve = new ECCurve.Fp( + p, // Prime p + BigInteger.valueOf(-3).mod(p), // a = -3 + BigInteger.ZERO, // , + q,// Order of the subgroup (from RFC 6509) + BigInteger.ONE // Cofactor = 1 + ); + + ECPoint K_bS = curve.createPoint(kbx, kby); + System.out.println("K_bS x:" + new String(Hex.encode(K_bS.getXCoord().toBigInteger().toByteArray()))); + System.out.println("K_bS y:" + new String(Hex.encode(K_bS.getYCoord().toBigInteger().toByteArray()))); + ECPoint R_bs = curve.createPoint(Rbx, Rby); + SAKKEKEMSGenerator.pairing(K_bS, R_bs, p, q); + + BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(SSV, b), q); + + System.out.println("r:" + new String(Hex.encode(r.toByteArray()))); + + System.out.println("r:" + new String(Hex.encode(expectedR))); Assert.assertTrue(Arrays.areEqual(r.toByteArray(), expectedR)); + SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); + generator.generateEncapsulated(null); + } } From d53e3ab82d908e46acb4d1c7a7972ece07ae0d37 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 31 Jan 2025 21:49:58 +1030 Subject: [PATCH 167/890] Fix step 3. --- .../org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java | 4 ++-- .../org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index 1024ed7428..e8f53b2cd7 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -144,10 +144,10 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip "C67C6D19487FB449059F26CC8AAB655A" + "B58B7CC796E24E9A394095754F5F8BAE", 16) // Py ); + BigInteger z = new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F", 16); // 3. Compute R_(b,S) = [r]([b]P + Z_S) ECPoint bP = P.multiply(b).normalize(); - ECPoint Z_S = Z;// P.multiply(ssv).normalize();;//.multiply(new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F", 16)); // Z_S - ECPoint R_bS = bP.add(Z_S).multiply(r); // [r]([b]P + Z_S) + ECPoint R_bS = bP.add(Z).multiply(r).normalize(); // [r]([b]P + Z_S) System.out.println("R_Bs x:" + new String(Hex.encode(R_bS.getXCoord().toBigInteger().toByteArray()))); System.out.println("R_Bs y:" + new String(Hex.encode(R_bS.getYCoord().toBigInteger().toByteArray()))); diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index 4a40e99c5c..cbe940a064 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -142,10 +142,11 @@ public void performTest() p, // Prime p BigInteger.valueOf(-3).mod(p), // a = -3 BigInteger.ZERO, // , - q,// Order of the subgroup (from RFC 6509) + g,// Order of the subgroup (from RFC 6509) BigInteger.ONE // Cofactor = 1 ); - + SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); + generator.generateEncapsulated(null); ECPoint K_bS = curve.createPoint(kbx, kby); System.out.println("K_bS x:" + new String(Hex.encode(K_bS.getXCoord().toBigInteger().toByteArray()))); System.out.println("K_bS y:" + new String(Hex.encode(K_bS.getYCoord().toBigInteger().toByteArray()))); @@ -159,8 +160,8 @@ public void performTest() System.out.println("r:" + new String(Hex.encode(expectedR))); Assert.assertTrue(Arrays.areEqual(r.toByteArray(), expectedR)); - SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); - generator.generateEncapsulated(null); +// SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); +// generator.generateEncapsulated(null); } } From 2434c1c56c1df052bfbe877470b9467325dced78 Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 3 Feb 2025 18:07:28 +1030 Subject: [PATCH 168/890] TODO fix the bugs in SAKKEKEMExtractor --- .../crypto/kems/SAKKEKEMExtractor.java | 128 +++++++ .../crypto/kems/SAKKEKEMSGenerator.java | 351 ++++++++++++++++-- .../bouncycastle/crypto/kems/SAKKEUtils.java | 32 ++ ...ey.java => SAKKEPrivateKeyParameters.java} | 19 +- .../crypto/params/SAKKEPublicKey.java | 52 --- .../params/SAKKEPublicKeyParameters.java | 100 +++++ .../crypto/kems/test/SAKKEKEMSTest.java | 13 +- 7 files changed, 597 insertions(+), 98 deletions(-) create mode 100644 core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java rename core/src/main/java/org/bouncycastle/crypto/params/{SAKKEPrivateKey.java => SAKKEPrivateKeyParameters.java} (63%) delete mode 100644 core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java new file mode 100644 index 0000000000..caff3dffa5 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java @@ -0,0 +1,128 @@ +package org.bouncycastle.crypto.kems; + +import java.math.BigInteger; + +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.EncapsulatedSecretExtractor; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.params.SAKKEPrivateKeyParameters; +import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.BigIntegers; + +import static org.bouncycastle.crypto.kems.SAKKEKEMSGenerator.pairing; + +public class SAKKEKEMExtractor implements EncapsulatedSecretExtractor +{ + private final ECCurve curve; + private final BigInteger p; + private final BigInteger q; + private final ECPoint P; + private final ECPoint Z_S; + private final ECPoint K_bS; // Receiver's RSK + private final int n; // Security parameter + private final SAKKEPrivateKeyParameters privateKey; + + public SAKKEKEMExtractor(SAKKEPrivateKeyParameters privateKey) { + this.privateKey = privateKey; + SAKKEPublicKeyParameters publicKey = privateKey.getPublicParams(); + this.curve = publicKey.getCurve(); + this.q = publicKey.getQ(); + this.P = publicKey.getP(); + this.p = publicKey.getp(); + this.Z_S = publicKey.getZ(); + this.K_bS = privateKey.getPrivatePoint(); + this.n = publicKey.getN(); + } + + @Override + public byte[] extractSecret(byte[] encapsulation) { + try { + // Step 1: Parse Encapsulated Data (R_bS, H) + ECPoint R_bS = parseECPoint(encapsulation); + BigInteger H = parseH(encapsulation); + + // Step 2: Compute w = using pairing + BigInteger w = computePairing(R_bS, K_bS); + + // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) + BigInteger ssv = computeSSV(H, w); + + // Step 4: Compute r = HashToIntegerRange(SSV || b) +// BigInteger r = computeR(ssv, privateKey.getPrivatePoint()); +// +// // Step 5: Validate R_bS +// if (!validateR_bS(r, privateKey.getPrivatePoint(), R_bS)) { +// throw new IllegalStateException("Validation of R_bS failed"); +// } + + return BigIntegers.asUnsignedByteArray(n/8, ssv); + } catch (Exception e) { + throw new IllegalStateException("SAKKE extraction failed: " + e.getMessage()); + } + } + + @Override + public int getEncapsulationLength() + { + return 0; + } + + private ECPoint parseECPoint(byte[] encapsulation) { + int coordLen = (p.bitLength() + 7) / 8; + byte[] xBytes = Arrays.copyOfRange(encapsulation, 0, coordLen); + byte[] yBytes = Arrays.copyOfRange(encapsulation, coordLen, 2*coordLen); + + BigInteger x = new BigInteger(1, xBytes); + BigInteger y = new BigInteger(1, yBytes); + + return curve.createPoint(x, y).normalize(); + } + + private BigInteger parseH(byte[] encapsulation) { + int coordLen = (p.bitLength() + 7) / 8; + byte[] hBytes = Arrays.copyOfRange(encapsulation, 2*coordLen, encapsulation.length); + return new BigInteger(1, hBytes); + } + + private BigInteger computePairing(ECPoint R, ECPoint K) { + // Use your existing pairing implementation + return pairing(R, K, p, q); + } + + private BigInteger computeSSV(BigInteger H, BigInteger w) { + BigInteger twoToN = BigInteger.ONE.shiftLeft(n); + BigInteger mask = SAKKEUtils.hashToIntegerRange(w.toByteArray(), twoToN); + return H.xor(mask); + } + + private BigInteger computeR(BigInteger ssv, byte[] userId) { + byte[] ssvBytes = BigIntegers.asUnsignedByteArray(ssv); + byte[] ssvConcatB = Arrays.concatenate(ssvBytes, userId); + return SAKKEUtils.hashToIntegerRange(ssvConcatB, q); + } + + private boolean validateR_bS(BigInteger r, byte[] b, ECPoint receivedR) { + try { + // Compute [b]P + ECPoint bP = P.multiply(new BigInteger(1, b)).normalize(); + + // Compute [b]P + Z_S + ECPoint bP_plus_Z = bP.add(Z_S).normalize(); + + // Compute [r]([b]P + Z_S) + ECPoint computedR = bP_plus_Z.multiply(r).normalize(); + + return pointsEqual(computedR, receivedR); + } catch (Exception e) { + return false; + } + } + + private boolean pointsEqual(ECPoint p1, ECPoint p2) { + return p1.normalize().getXCoord().equals(p2.normalize().getXCoord()) + && p1.normalize().getYCoord().equals(p2.normalize().getYCoord()); + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index e8f53b2cd7..df94f031ae 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -3,11 +3,10 @@ import org.bouncycastle.crypto.EncapsulatedSecretGenerator; import org.bouncycastle.crypto.SecretWithEncapsulation; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; -import org.bouncycastle.crypto.params.SAKKEPublicKey; +import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECPoint; -import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; import org.bouncycastle.util.encoders.Hex; @@ -153,8 +152,21 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip // 4. Compute H = SSV XOR HashToIntegerRange( g^r, 2^n ) - BigInteger g_r = pairing(R_bS, G, p, q); + BigInteger[] result = fp2Exponentiate(p, BigInteger.ONE, g, r); + BigInteger g_r = result[0].mod(p); + g_r = g_r.modInverse(p); + g_r = g_r.multiply(result[1]).mod(p); + System.out.println("g_r " + new String(Hex.encode(g_r.toByteArray()))); + byte[] expected_g_r = Hex.decode("7D2A8438 E6291C64 9B6579EB 3B79EAE9\n" + + " 48B1DE9E 5F7D1F40 70A08F8D B6B3C515\n" + + " 6F2201AF FBB5CB9D 82AA3EC0 D0398B89\n" + + " ABC78A13 A760C0BF 3F77E63D 0DF3F1A3\n" + + " 41A41B88 11DF197F D6CD0F00 3125606F\n" + + " 4F109F40 0F7292A1 0D255E3C 0EBCCB42\n" + + " 53FB182C 68F09CF6 CD9C4A53 DA6C74AD\n" + + " 007AF36B 8BCA979D 5895E282 F483FCD6"); BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(n)); // 2^n + System.out.println(new String(Hex.encode(mask.toByteArray()))); BigInteger H = ssv.xor(mask); @@ -162,17 +174,158 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip byte[] encapsulated = encodeData(R_bS, H); return new SecretWithEncapsulationImpl( - BigIntegers.asUnsignedByteArray(n / 8, ssv), // Output SSV as key material - encapsulated + encapsulated, + BigIntegers.asUnsignedByteArray(n / 8, ssv) // Output SSV as key material ); } - private BigInteger getRecipientId(SAKKEPublicKey pubKey) + + // Helper method for F_p² exponentiation + public static BigInteger[] fp2Exponentiate( + BigInteger p, + BigInteger x, + BigInteger y, + BigInteger exponent + ) + { + BigInteger[] result = new BigInteger[2]; + sakkePointExponent(p, result, x, y, exponent); + return result; + } + + public static boolean sakkePointExponent( + BigInteger p, + BigInteger[] result, + BigInteger pointX, + BigInteger pointY, + BigInteger n + ) + { + if (n.equals(BigInteger.ZERO)) + { + return false; + } + + // Initialize result with the original point + BigInteger currentX = pointX; + BigInteger currentY = pointY; + + int numBits = n.bitLength(); + + // Process bits from MSB-1 down to 0 + for (int i = numBits - 2; i >= 0; i--) + { + // Square the current point + //sakkePointSquare(p, new BigInteger[]{currentX, currentY}); + // Compute newX = (x + y)(x - y) mod p + BigInteger xPlusY = currentX.add(currentY).mod(p); + BigInteger xMinusY = currentX.subtract(currentY).mod(p); + BigInteger newX = xPlusY.multiply(xMinusY).mod(p); + + // Compute newY = 2xy mod p + BigInteger newY = currentX.multiply(currentY).multiply(BigInteger.valueOf(2)).mod(p); + currentX = newX; + currentY = newY; + // Multiply if bit is set + if (n.testBit(i)) + { + //sakkePointsMultiply(p, currentX, currentY, pointX, pointY); + BigInteger real = currentX.multiply(pointX) + .subtract(currentY.multiply(pointY)) + .mod(p); + + // Compute imaginary part = x1*y2 + x2*y1 mod p + BigInteger imag = currentX.multiply(pointY) + .add(pointX.multiply(currentY)) + .mod(p); + + currentX = real; + currentY = imag; + } + } + + result[0] = currentX; + result[1] = currentY; + return true; + } + + + private BigInteger getRecipientId(SAKKEPublicKeyParameters pubKey) { byte[] hashedId = SAKKEUtils.hash(pubKey.getZ().getEncoded(false)); // Hash Z_S return new BigInteger(1, hashedId).mod(pubKey.getQ().subtract(BigInteger.ONE)).add(BigIntegers.TWO); } + public static class FP2Element + { + private final BigInteger a; // Real part + private final BigInteger b; // Imaginary part + private final BigInteger p; // Prime modulus + + public FP2Element(BigInteger a, BigInteger b, BigInteger p) + { + this.a = a.mod(p); + this.b = b.mod(p); + this.p = p; + } + + public FP2Element add(FP2Element other) + { + return new FP2Element(a.add(other.a), b.add(other.b), p); + } + + public FP2Element subtract(FP2Element other) + { + return new FP2Element(a.subtract(other.a), b.subtract(other.b), p); + } + + public FP2Element multiply(FP2Element other) + { + BigInteger real = a.multiply(other.a).subtract(b.multiply(other.b)).mod(p); + BigInteger imag = a.multiply(other.b).add(b.multiply(other.a)).mod(p); + return new FP2Element(real, imag, p); + } + + public FP2Element inverse() + { + BigInteger denom = a.pow(2).add(b.pow(2)).mod(p); + BigInteger invDenom = denom.modInverse(p); + return new FP2Element(a.multiply(invDenom), b.negate().multiply(invDenom), p); + } + + public FP2Element square() + { + return this.multiply(this); + } + + public FP2Element pow(BigInteger exponent) + { + // Implement exponentiation using square-and-multiply + FP2Element result = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); + FP2Element base = this; + for (int i = exponent.bitLength() - 1; i >= 0; i--) + { + result = result.square(); + if (exponent.testBit(i)) + { + result = result.multiply(base); + } + } + return result; + } + + // Getters + public BigInteger getA() + { + return a; + } + + public BigInteger getB() + { + return b; + } + } + /** * Computes the Tate-Lichtenbaum pairing ⟨P, Q⟩ as per RFC 6508. *

@@ -184,53 +337,181 @@ private BigInteger getRecipientId(SAKKEPublicKey pubKey) public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) { ECCurve curve = R.getCurve(); - ECFieldElement i = curve.fromBigInteger(BigInteger.ONE.negate()); // i = -1 in F_p^2 + FP2Element v = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); // Initialize to 1+0i - ECPoint C = R; - BigInteger c = p.add(BigInteger.ONE).divide(q); - ECFieldElement v = curve.fromBigInteger(BigInteger.ONE); // v = 1 in F_p + // Use correct exponent from RFC 6508: (p² - 1)/q + BigInteger exponent = p.pow(2).subtract(BigInteger.ONE).divide(q); - String qBits = q.subtract(BigInteger.ONE).toString(2); // Binary representation of q-1 + String qBits = q.subtract(BigInteger.ONE).toString(2); + ECPoint C = R.normalize(); for (int j = 1; j < qBits.length(); j++) - { // Skip MSB - // l = (3 * (C_x^2 - 1)) / (2 * C_y) + { + C = C.normalize(); ECFieldElement Cx = C.getAffineXCoord(); ECFieldElement Cy = C.getAffineYCoord(); - ECFieldElement l = Cx.square().multiply(curve.fromBigInteger(ECFieldElement.THREE)).subtract(curve.fromBigInteger(BigInteger.ONE)) - .divide(Cy.multiply(curve.fromBigInteger(BigIntegers.TWO))); - // v = v^2 * (l * (Q_x + C_x) + (i * Q_y - C_y)) + // Line function for doubling + ECFieldElement lNum = Cx.square().multiply(curve.fromBigInteger(BigInteger.valueOf(3))).add(curve.getA()); + ECFieldElement lDen = Cy.multiply(curve.fromBigInteger(BigInteger.valueOf(2))); + BigInteger l = lNum.divide(lDen).toBigInteger(); + + // Evaluate line at Q using F_p² arithmetic ECFieldElement Qx = Q.getAffineXCoord(); ECFieldElement Qy = Q.getAffineYCoord(); - v = v.square().multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); + FP2Element lineVal = new FP2Element( + l.multiply(Qx.add(Cx).toBigInteger()), + l.multiply(Qy.toBigInteger()), + p + ); - // Double the point - C = C.twice(); + v = v.multiply(lineVal).pow(BigInteger.valueOf(2)); + C = C.twice().normalize(); - // If the bit is 1, perform additional step if (qBits.charAt(j) == '1') { - // l = (C_y - R_y) / (C_x - R_x) - ECFieldElement Rx = R.getAffineXCoord(); - ECFieldElement Ry = R.getAffineYCoord(); - l = Cy.subtract(Ry).divide(Cx.subtract(Rx)); - - // v = v * (l * (Q_x + C_x) + (i * Q_y - C_y)) - v = v.multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); - - // C = C + R - C = C.add(R); + ECPoint Rnorm = R.normalize(); + ECFieldElement Rx = Rnorm.getAffineXCoord(); + ECFieldElement Ry = Rnorm.getAffineYCoord(); + + // Line function for addition + ECFieldElement lAddNum = Cy.subtract(Ry); + ECFieldElement lAddDen = Cx.subtract(Rx); + BigInteger lAdd = lAddNum.divide(lAddDen).toBigInteger(); + + FP2Element lineAddVal = new FP2Element( + lAdd.multiply(Qx.add(Cx).toBigInteger()), + lAdd.multiply(Qy.toBigInteger()), + p + ); + + v = v.multiply(lineAddVal); + C = C.add(Rnorm).normalize(); } } - // Compute v^c - v = curve.fromBigInteger(v.toBigInteger().pow(c.intValue())); + // Final exponentiation + FP2Element t = v.pow(exponent); - // Convert to F_p representative - return computeFpRepresentative(v, curve); + // Convert to F_p representative: b/a mod p + BigInteger a = t.getA(); + BigInteger b = t.getB(); + return b.multiply(a.modInverse(p)).mod(p); } +// public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) +// { +// ECCurve curve = R.getCurve(); +// FP2Element i = new FP2Element(BigInteger.ZERO, BigInteger.ONE, p); // i = -1 in F_p^2 +// +// ECPoint C = R.normalize(); +// BigInteger c = p.add(BigInteger.ONE).divide(q); +// //ECFieldElement v = curve.fromBigInteger(BigInteger.ONE); // v = 1 in F_p +// FP2Element v = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); +// +// String qBits = q.subtract(BigInteger.ONE).toString(2); // Binary representation of q-1 +// +// for (int j = 1; j < qBits.length(); j++) +// { // Skip MSB +// // l = (3 * (C_x^2 - 1)) / (2 * C_y) +// C = C.normalize(); // Add this line to ensure normalization +// ECFieldElement Cx = C.getAffineXCoord(); +// ECFieldElement Cy = C.getAffineXCoord(); +// BigInteger CxVal = Cx.toBigInteger(); +// BigInteger CyVal = Cy.toBigInteger(); +//// ECFieldElement l = Cx.square().multiply(curve.fromBigInteger(ECFieldElement.THREE)).subtract(curve.fromBigInteger(BigInteger.ONE)) +//// .divide(Cy.multiply(curve.fromBigInteger(BigIntegers.TWO))); +// +// // l = 3*(Cx² - 1) / (2*Cy) in F_p +// ECFieldElement lNum = Cx.square().subtract(curve.fromBigInteger(BigInteger.ONE)).multiply(curve.fromBigInteger(BigInteger.valueOf(3))); +// ECFieldElement lDen = Cy.multiply(curve.fromBigInteger(BigInteger.valueOf(2))); +// ECFieldElement l = lNum.divide(lDen); +// BigInteger lVal = l.toBigInteger(); +// +// // Evaluate line at [i]Q: (Qx, i*Qy) +// ECFieldElement Qx = Q.getAffineXCoord(); +// ECFieldElement Qy = Q.getAffineYCoord(); +// BigInteger QxVal = Qx.toBigInteger(); +// BigInteger QyVal = Qy.toBigInteger(); +// +// // Convert l*(Qx + Cx) to F_p² +// FP2Element term1 = new FP2Element(lVal.multiply(QxVal.add(CxVal)).mod(p), BigInteger.ZERO, p); +// +// // (i*Qy - Cy) in F_p²: (-Cy, Qy) +// FP2Element term2 = new FP2Element(QyVal.negate().mod(p), QyVal, p); // Wait, original term is i*Qy - Cy: i*Qy is (0, Qy), subtract Cy (Cy,0) gives (-Cy, Qy) +// FP2Element lineVal = new FP2Element(lVal, BigInteger.ZERO, p) // l is in F_p +// .multiply(new FP2Element(QxVal.add(CxVal).mod(p), BigInteger.ZERO, p)) // (Qx + Cx) in F_p +// .add(term2); // i*Qy - Cy +// +// v = v.square().multiply(lineVal); +// +// C = C.twice().normalize();; +// +// // v = v^2 * (l * (Q_x + C_x) + (i * Q_y - C_y)) +//// ECFieldElement Qx = Q.getAffineXCoord(); +//// ECFieldElement Qy = Q.getAffineYCoord(); +//// v = v.square().multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); +// +// // Double the point +//// C = C.twice(); +// if (qBits.charAt(j) == '1') +// { +// // Compute line function for addition +// ECFieldElement Rx = R.getAffineXCoord(); +// ECFieldElement Ry = R.getAffineYCoord(); +// BigInteger RxVal = Rx.toBigInteger(); +// BigInteger RyVal = Ry.toBigInteger(); +// +// ECFieldElement lAddNum = Cy.subtract(Ry); +// ECFieldElement lAddDen = Cx.subtract(Rx); +// ECFieldElement lAdd = lAddNum.divide(lAddDen); +// BigInteger lAddVal = lAdd.toBigInteger(); +// +// // Evaluate line at [i]Q +// FP2Element lineAddTerm1 = new FP2Element(lAddVal.multiply(QxVal.add(CxVal)).mod(p), BigInteger.ZERO, p); +// FP2Element lineAddTerm2 = new FP2Element(QyVal.negate().mod(p), QyVal, p); // i*Qy - Cy (Cy is current C's y) +// FP2Element lineAddVal = lineAddTerm1.add(lineAddTerm2); +// +// v = v.multiply(lineAddVal); +// +// C = C.add(R); +// } +// +//// // If the bit is 1, perform additional step +//// if (qBits.charAt(j) == '1') +//// { +//// // l = (C_y - R_y) / (C_x - R_x) +//// ECFieldElement Rx = R.getAffineXCoord(); +//// ECFieldElement Ry = R.getAffineYCoord(); +//// l = Cy.subtract(Ry).divide(Cx.subtract(Rx)); +//// +//// // v = v * (l * (Q_x + C_x) + (i * Q_y - C_y)) +//// v = v.multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); +//// +//// // C = C + R +//// C = C.add(R); +//// } +// } +// +//// // Compute v^c +//// v = curve.fromBigInteger(v.toBigInteger().modPow(c, p)); +//// +//// // Convert to F_p representative +//// return computeFpRepresentative(v, curve); +// FP2Element t = v.pow(c); +// +// // Compute representative: b/a mod p +// BigInteger a = t.getA(); +// BigInteger bVal = t.getB(); +// if (a.equals(BigInteger.ZERO)) { +// throw new ArithmeticException("Division by zero in F_p representative"); +// } +// BigInteger aInv = a.modInverse(p); +// BigInteger representative = bVal.multiply(aInv).mod(p); +// +// return representative; +// } + private static BigInteger computeFpRepresentative(ECFieldElement t, ECCurve curve) { // Characteristic of F_p @@ -238,7 +519,7 @@ private static BigInteger computeFpRepresentative(ECFieldElement t, ECCurve curv // Assume t = a + i * b in F_p² → extract a, b ECFieldElement a = t; // In F_p², a is the real part - ECFieldElement b = t.multiply(curve.fromBigInteger(BigInteger.ONE.negate())); // Imaginary part + ECFieldElement b = t.multiply(curve.fromBigInteger(BigInteger.ONE.negate().add(p))); // Imaginary part // Compute b/a mod p return b.toBigInteger().multiply(a.toBigInteger().modInverse(p)).mod(p); diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java index 05938e2ac5..45fbbb38b5 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java @@ -9,6 +9,38 @@ public class SAKKEUtils { + public static ECPoint sakkePointExponent(ECPoint point, BigInteger n) { + if (n.equals(BigInteger.ZERO)) { + throw new IllegalArgumentException("Exponent cannot be zero."); + } + + ECPoint result = point; + int N = n.bitLength() - 1; + + for (; N != 0; --N) { + result = sakkePointSquare(result); + if (n.testBit(N - 1)) { + result = sakkePointsMultiply(result, point); + } + } + return result; + } + + public static ECPoint sakkePointSquare(ECPoint point) { + BigInteger x = point.getAffineXCoord().toBigInteger(); + BigInteger y = point.getAffineYCoord().toBigInteger(); + + BigInteger bx1 = x.add(y); + BigInteger bx2 = x.subtract(y); + BigInteger newX = bx1.multiply(bx2).mod(point.getCurve().getField().getCharacteristic()); + BigInteger newY = x.multiply(y).multiply(BigInteger.valueOf(2)).mod(point.getCurve().getField().getCharacteristic()); + + return point.getCurve().createPoint(newX, newY); + } + + public static ECPoint sakkePointsMultiply(ECPoint p1, ECPoint p2) { + return p1.add(p2).normalize(); + } public static BigInteger hashToIntegerRange(byte[] input, BigInteger q) { // RFC 6508 Section 5.1: Hashing to an Integer Range diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKey.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java similarity index 63% rename from core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKey.java rename to core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java index 02e6d7c54b..2c83f35ba3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKey.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java @@ -4,14 +4,14 @@ import org.bouncycastle.math.ec.ECPoint; -public class SAKKEPrivateKey +public class SAKKEPrivateKeyParameters extends AsymmetricKeyParameter { private final BigInteger b; // User's identity private final ECPoint K; // Private key K_a - private final SAKKEPublicKey publicParams; + private final SAKKEPublicKeyParameters publicParams; - public SAKKEPrivateKey(BigInteger b, ECPoint K, SAKKEPublicKey publicParams) + public SAKKEPrivateKeyParameters(BigInteger b, ECPoint K, SAKKEPublicKeyParameters publicParams) { super(true); this.b = b; @@ -19,19 +19,18 @@ public SAKKEPrivateKey(BigInteger b, ECPoint K, SAKKEPublicKey publicParams) this.publicParams = publicParams; } - // Getters - public ECPoint getK() - { - return K; - } - public BigInteger getB() { return b; } - public SAKKEPublicKey getPublicParams() + public SAKKEPublicKeyParameters getPublicParams() { return publicParams; } + + public ECPoint getPrivatePoint() + { + return K; + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java deleted file mode 100644 index c992dbb70b..0000000000 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKey.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.bouncycastle.crypto.params; - -import java.math.BigInteger; - -import org.bouncycastle.math.ec.ECCurve; -import org.bouncycastle.math.ec.ECPoint; -import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve; - -public class SAKKEPublicKey - extends AsymmetricKeyParameter -{ - private final ECCurve curve = new SecP256R1Curve(); - private final ECPoint P; // Base point - private final ECPoint Z; // KMS Public Key: Z = [z]P - private final BigInteger q; // Subgroup order - private final int n; // SSV bit length - - public SAKKEPublicKey(ECPoint P, ECPoint Z, BigInteger q, int n) - { - super(false); - this.P = P; - this.Z = Z; - this.q = q; - this.n = n; - } - - // Getters - public ECCurve getCurve() - { - return curve; - } - - public ECPoint getP() - { - return P; - } - - public ECPoint getZ() - { - return Z; - } - - public BigInteger getQ() - { - return q; - } - - public int getN() - { - return n; - } -} diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java new file mode 100644 index 0000000000..66ce4e81ef --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java @@ -0,0 +1,100 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; + +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +public class SAKKEPublicKeyParameters + extends AsymmetricKeyParameter +{ + // Base point + private static final BigInteger p = new BigInteger( + "997ABB1F0A563FDA65C61198DAD0657A416C0CE19CB48261BE9AE358B3E01A2E" + + "F40AAB27E2FC0F1B228730D531A59CB0E791B39FF7C88A19356D27F4A666A6D0" + + "E26C6487326B4CD4512AC5CD65681CE1B6AFF4A831852A82A7CF3C521C3C09AA" + + "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 + ); + + private static final BigInteger Px = new BigInteger( + "53FC09EE332C29AD0A7990053ED9B52A2B1A2FD60AEC69C698B2F204B6FF7CBF" + + "B5EDB6C0F6CE2308AB10DB9030B09E1043D5F22CDB9DFA55718BD9E7406CE890" + + "9760AF765DD5BCCB337C86548B72F2E1A702C3397A60DE74A7C1514DBA66910D" + + "D5CFB4CC80728D87EE9163A5B63F73EC80EC46C4967E0979880DC8ABEAE63895", 16 + ); + + private static final BigInteger Py = new BigInteger( + "0A8249063F6009F1F9F1F0533634A135D3E82016029906963D778D821E141178" + + "F5EA69F4654EC2B9E7F7F5E5F0DE55F66B598CCF9A140B2E416CFF0CA9E032B9" + + "70DAE117AD547C6CCAD696B5B7652FE0AC6F1E80164AA989492D979FC5A4D5F2" + + "13515AD7E9CB99A980BDAD5AD5BB4636ADB9B5706A67DCDE75573FD71BEF16D7", 16 + ); + + + private static final BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + + " C6A87BD1 FB94C41E 27FABE65 8E015A87\n" + + " 371E9474 4C96FEDA 449AE956 3F8BC446\n" + + " CBFDA85D 5D00EF57 7072DA8F 541721BE\n" + + " EE0FAED1 828EAB90 B99DFB01 38C78433\n" + + " 55DF0460 B4A9FD74 B4F1A32B CAFA1FFA\n" + + " D682C033 A7942BCC E3720F20 B9B7B040\n" + + " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); + + private static final BigInteger q = new BigInteger( + "265EAEC7C2958FF69971846636B4195E905B0338672D20986FA6B8D62CF8068B" + + "BD02AAC9F8BF03C6C8A1CC354C69672C39E46CE7FDF222864D5B49FD2999A9B4" + + "389B1921CC9AD335144AB173595A07386DABFD2A0C614AA0A9F3CF14870F026A" + + "A7E535ABD5A5C7C7FF38FA08E2615F6C203177C42B1EB3A1D99B601EBFAA17FB", 16 + ); + + private static final ECCurve.Fp curve = new ECCurve.Fp( + p, // Prime p + BigInteger.valueOf(-3).mod(p), // a = -3 + BigInteger.ZERO, // , + g, // Order of the subgroup (from RFC 6509) + BigInteger.ONE // Cofactor = 1 + ); + + private static final ECPoint P = curve.createPoint(Px, Py); + private final ECPoint Z; // KMS Public Key: Z = [z]P + + private static final int n = 128; // SSV bit length + + public SAKKEPublicKeyParameters(ECPoint Z) + { + super(false); + this.Z = Z; + } + + // Getters + public ECCurve getCurve() + { + return curve; + } + + public ECPoint getP() + { + return P; + } + + public ECPoint getZ() + { + return Z; + } + + public BigInteger getp() + { + return p; + } + + public BigInteger getQ() + { + return q; + } + + public int getN() + { + return n; + } +} diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index cbe940a064..485965ad59 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -4,8 +4,12 @@ import java.security.SecureRandom; +import org.bouncycastle.crypto.SecretWithEncapsulation; +import org.bouncycastle.crypto.kems.SAKKEKEMExtractor; import org.bouncycastle.crypto.kems.SAKKEKEMSGenerator; import org.bouncycastle.crypto.kems.SAKKEUtils; +import org.bouncycastle.crypto.params.SAKKEPrivateKeyParameters; +import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; @@ -146,8 +150,15 @@ public void performTest() BigInteger.ONE // Cofactor = 1 ); SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); - generator.generateEncapsulated(null); + SecretWithEncapsulation rlt = generator.generateEncapsulated(null); + ECPoint K_bS = curve.createPoint(kbx, kby); + + + SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(new BigInteger(b), K_bS, new SAKKEPublicKeyParameters(null))); + byte[] test = extractor.extractSecret(rlt.getSecret()); + + System.out.println("K_bS x:" + new String(Hex.encode(K_bS.getXCoord().toBigInteger().toByteArray()))); System.out.println("K_bS y:" + new String(Hex.encode(K_bS.getYCoord().toBigInteger().toByteArray()))); ECPoint R_bs = curve.createPoint(Rbx, Rby); From a9c71796ff4c2580da47250f9e288367c1bdac91 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 4 Feb 2025 18:32:08 +1030 Subject: [PATCH 169/890] TODO pairing --- .../crypto/kems/SAKKEKEMExtractor.java | 300 +++++++++++-- .../crypto/kems/SAKKEKEMSGenerator.java | 396 +++++++++++------- 2 files changed, 490 insertions(+), 206 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java index caff3dffa5..0e23ab22cb 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java @@ -14,7 +14,8 @@ import static org.bouncycastle.crypto.kems.SAKKEKEMSGenerator.pairing; -public class SAKKEKEMExtractor implements EncapsulatedSecretExtractor +public class SAKKEKEMExtractor + implements EncapsulatedSecretExtractor { private final ECCurve curve; private final BigInteger p; @@ -25,7 +26,8 @@ public class SAKKEKEMExtractor implements EncapsulatedSecretExtractor private final int n; // Security parameter private final SAKKEPrivateKeyParameters privateKey; - public SAKKEKEMExtractor(SAKKEPrivateKeyParameters privateKey) { + public SAKKEKEMExtractor(SAKKEPrivateKeyParameters privateKey) + { this.privateKey = privateKey; SAKKEPublicKeyParameters publicKey = privateKey.getPublicParams(); this.curve = publicKey.getCurve(); @@ -38,14 +40,18 @@ public SAKKEKEMExtractor(SAKKEPrivateKeyParameters privateKey) { } @Override - public byte[] extractSecret(byte[] encapsulation) { - try { + public byte[] extractSecret(byte[] encapsulation) + { + try + { // Step 1: Parse Encapsulated Data (R_bS, H) - ECPoint R_bS = parseECPoint(encapsulation); - BigInteger H = parseH(encapsulation); + ECPoint R_bS = curve.decodePoint(Arrays.copyOfRange(encapsulation, 0, 257)); + BigInteger H = new BigInteger(Arrays.copyOfRange(encapsulation, 257, 274)); // Step 2: Compute w = using pairing - BigInteger w = computePairing(R_bS, K_bS); +// BigInteger w = computeTLPairing(new BigInteger[] {R_bS.getXCoord().toBigInteger(), R_bS.getYCoord().toBigInteger()}, +// new BigInteger[] {K_bS.getXCoord().toBigInteger(), K_bS.getYCoord().toBigInteger()}, this.p, this.q); + BigInteger w = computePairing(R_bS, K_bS, p, q); // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) BigInteger ssv = computeSSV(H, w); @@ -58,8 +64,10 @@ public byte[] extractSecret(byte[] encapsulation) { // throw new IllegalStateException("Validation of R_bS failed"); // } - return BigIntegers.asUnsignedByteArray(n/8, ssv); - } catch (Exception e) { + return BigIntegers.asUnsignedByteArray(n / 8, ssv); + } + catch (Exception e) + { throw new IllegalStateException("SAKKE extraction failed: " + e.getMessage()); } } @@ -70,59 +78,263 @@ public int getEncapsulationLength() return 0; } - private ECPoint parseECPoint(byte[] encapsulation) { - int coordLen = (p.bitLength() + 7) / 8; - byte[] xBytes = Arrays.copyOfRange(encapsulation, 0, coordLen); - byte[] yBytes = Arrays.copyOfRange(encapsulation, coordLen, 2*coordLen); + private BigInteger computePairing(ECPoint R, ECPoint K) + { + // Use your existing pairing implementation + return pairing(R, K, p, q); + } + + private BigInteger computeSSV(BigInteger H, BigInteger w) + { + BigInteger twoToN = BigInteger.ONE.shiftLeft(n); + BigInteger mask = SAKKEUtils.hashToIntegerRange(w.toByteArray(), twoToN); + return H.xor(mask); + } + + public static BigInteger computeTLPairing( + BigInteger[] R, // C = (Rx, Ry) + BigInteger[] Q, // Q = (Qx, Qy) + BigInteger p, + BigInteger q + ) + { + BigInteger qMinus1 = q.subtract(BigInteger.ONE); + int N = qMinus1.bitLength() - 1; + + // Initialize V = (1, 0) + BigInteger[] V = {BigInteger.ONE, BigInteger.ZERO}; + // Initialize C = R + BigInteger[] C = {R[0], R[1]}; + + for (; N > 0; N--) + { + // V = V^2 + pointSquare(V, p); + + // Compute line function T + BigInteger[] T = computeLineFunctionT(C, Q, p); + + // V = V * T + pointMultiply(V, T, p); + + // C = 2*C (point doubling) + pointDouble(C, p); - BigInteger x = new BigInteger(1, xBytes); - BigInteger y = new BigInteger(1, yBytes); + if (qMinus1.testBit(N - 1)) + { + // Compute addition line function + BigInteger[] TAdd = computeLineFunctionAdd(C, R, Q, p); - return curve.createPoint(x, y).normalize(); + // V = V * TAdd + pointMultiply(V, TAdd, p); + + // C = C + R (point addition) + pointAdd(C, R, p); + } + } + + // Final squaring + pointSquare(V, p); + pointSquare(V, p); + + // Compute w = (Vy * Vx^{-1}) mod p + BigInteger VxInv = V[0].modInverse(p); + return V[1].multiply(VxInv).mod(p); } - private BigInteger parseH(byte[] encapsulation) { - int coordLen = (p.bitLength() + 7) / 8; - byte[] hBytes = Arrays.copyOfRange(encapsulation, 2*coordLen, encapsulation.length); - return new BigInteger(1, hBytes); + private static void pointSquare(BigInteger[] point, BigInteger p) + { + BigInteger x = point[0]; + BigInteger y = point[1]; + + // x = (x + y)(x - y) mod p + BigInteger xPlusY = x.add(y).mod(p); + BigInteger xMinusY = x.subtract(y).mod(p); + BigInteger newX = xPlusY.multiply(xMinusY).mod(p); + + // y = 2xy mod p + BigInteger newY = x.multiply(y).multiply(BigInteger.valueOf(2)).mod(p); + + point[0] = newX; + point[1] = newY; } - private BigInteger computePairing(ECPoint R, ECPoint K) { - // Use your existing pairing implementation - return pairing(R, K, p, q); + private static void pointMultiply(BigInteger[] a, BigInteger[] b, BigInteger p) + { + // Complex multiplication (a + bi)*(c + di) = (ac - bd) + (ad + bc)i + BigInteger real = a[0].multiply(b[0]).subtract(a[1].multiply(b[1])).mod(p); + BigInteger imag = a[0].multiply(b[1]).add(a[1].multiply(b[0])).mod(p); + + a[0] = real; + a[1] = imag; } - private BigInteger computeSSV(BigInteger H, BigInteger w) { - BigInteger twoToN = BigInteger.ONE.shiftLeft(n); - BigInteger mask = SAKKEUtils.hashToIntegerRange(w.toByteArray(), twoToN); - return H.xor(mask); + private static void pointDouble(BigInteger[] point, BigInteger p) + { + // Elliptic curve point doubling formulas + BigInteger x = point[0]; + BigInteger y = point[1]; + + BigInteger slope = x.pow(2).multiply(BigInteger.valueOf(3)) + .mod(p) + .multiply(y.multiply(BigInteger.valueOf(2)).modInverse(p)) + .mod(p); + + BigInteger newX = slope.pow(2).subtract(x.multiply(BigInteger.valueOf(2))).mod(p); + BigInteger newY = slope.multiply(x.subtract(newX)).subtract(y).mod(p); + + point[0] = newX; + point[1] = newY; } - private BigInteger computeR(BigInteger ssv, byte[] userId) { - byte[] ssvBytes = BigIntegers.asUnsignedByteArray(ssv); - byte[] ssvConcatB = Arrays.concatenate(ssvBytes, userId); - return SAKKEUtils.hashToIntegerRange(ssvConcatB, q); + private static void pointAdd(BigInteger[] a, BigInteger[] b, BigInteger p) + { + // Elliptic curve point addition + BigInteger x1 = a[0], y1 = a[1]; + BigInteger x2 = b[0], y2 = b[1]; + + BigInteger slope = y2.subtract(y1) + .multiply(x2.subtract(x1).modInverse(p)) + .mod(p); + + BigInteger newX = slope.pow(2).subtract(x1).subtract(x2).mod(p); + BigInteger newY = slope.multiply(x1.subtract(newX)).subtract(y1).mod(p); + + a[0] = newX; + a[1] = newY; } - private boolean validateR_bS(BigInteger r, byte[] b, ECPoint receivedR) { - try { - // Compute [b]P - ECPoint bP = P.multiply(new BigInteger(1, b)).normalize(); + private static BigInteger[] computeLineFunctionT( + BigInteger[] C, + BigInteger[] Q, + BigInteger p + ) + { + // Line function evaluation for doubling + BigInteger Cx = C[0], Cy = C[1]; + BigInteger Qx = Q[0], Qy = Q[1]; - // Compute [b]P + Z_S - ECPoint bP_plus_Z = bP.add(Z_S).normalize(); + // l = (3Cx² + a)/(2Cy) but a=0 for many curves + BigInteger numerator = Cx.pow(2).multiply(BigInteger.valueOf(3)).mod(p); + BigInteger denominator = Cy.multiply(BigInteger.valueOf(2)).mod(p); + BigInteger l = numerator.multiply(denominator.modInverse(p)).mod(p); - // Compute [r]([b]P + Z_S) - ECPoint computedR = bP_plus_Z.multiply(r).normalize(); + // T = l*(Qx + Cx) - 2Qy + BigInteger tReal = l.multiply(Qx.add(Cx).mod(p)).mod(p); + BigInteger tImag = l.multiply(Qy).negate().mod(p); - return pointsEqual(computedR, receivedR); - } catch (Exception e) { - return false; - } + return new BigInteger[]{tReal, tImag}; } - private boolean pointsEqual(ECPoint p1, ECPoint p2) { + private static BigInteger[] computeLineFunctionAdd( + BigInteger[] C, + BigInteger[] R, + BigInteger[] Q, + BigInteger p + ) + { + // Line function evaluation for addition + BigInteger Cx = C[0], Cy = C[1]; + BigInteger Rx = R[0], Ry = R[1]; + BigInteger Qx = Q[0], Qy = Q[1]; + + // l = (Cy - Ry)/(Cx - Rx) + BigInteger numerator = Cy.subtract(Ry).mod(p); + BigInteger denominator = Cx.subtract(Rx).mod(p); + BigInteger l = numerator.multiply(denominator.modInverse(p)).mod(p); + + // T = l*(Qx + Cx) - Qy + BigInteger tReal = l.multiply(Qx.add(Cx).mod(p)).mod(p); + BigInteger tImag = l.multiply(Qy).negate().mod(p); + + return new BigInteger[]{tReal, tImag}; + } + + private boolean pointsEqual(ECPoint p1, ECPoint p2) + { return p1.normalize().getXCoord().equals(p2.normalize().getXCoord()) && p1.normalize().getYCoord().equals(p2.normalize().getYCoord()); } + + public static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) + { + BigInteger c = p.add(BigInteger.ONE).divide(q); // Compute c = (p+1)/q + BigInteger[] v = new BigInteger[]{BigInteger.ONE, BigInteger.ZERO}; // v = (1,0) in F_p^2 + ECPoint C = R; + + BigInteger qMinusOne = q.subtract(BigInteger.ONE); + int numBits = qMinusOne.bitLength(); + + // Miller loop + for (int i = numBits - 2; i >= 0; i--) + { + v = fp2SquareAndAccumulate(v, C, Q, p); + C = C.twice().normalize(); // C = [2]C + + if (qMinusOne.testBit(i)) + { + v = fp2MultiplyAndAccumulate(v, C, R, Q, p); + C = C.add(R).normalize(); + } + } + + // Final exponentiation: t = v^c + return fp2FinalExponentiation(v, p, c); + } + + private static BigInteger[] fp2SquareAndAccumulate(BigInteger[] v, ECPoint C, ECPoint Q, BigInteger p) + { + BigInteger Cx = C.getAffineXCoord().toBigInteger(); + BigInteger Cy = C.getAffineYCoord().toBigInteger(); + BigInteger Qx = Q.getAffineXCoord().toBigInteger(); + BigInteger Qy = Q.getAffineYCoord().toBigInteger(); + + // Compute l = (3 * (Cx^2 - 1)) / (2 * Cy) mod p + BigInteger l = Cx.multiply(Cx).mod(p).subtract(BigInteger.ONE).multiply(BigInteger.valueOf(3)).mod(p) + .multiply(Cy.multiply(BigInteger.valueOf(2)).modInverse(p)) + .mod(p); + + // Compute v = v^2 * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) + v = fp2Multiply(v[0], v[1], v[0], v[1], p); + return fp2Multiply(v[0], v[1], l.multiply(Qx.add(Cx)), (Qy.subtract(Cy)), p); + } + + private static BigInteger[] fp2MultiplyAndAccumulate(BigInteger[] v, ECPoint C, ECPoint R, ECPoint Q, BigInteger p) + { + BigInteger Cx = C.getAffineXCoord().toBigInteger(); + BigInteger Cy = C.getAffineYCoord().toBigInteger(); + BigInteger Rx = R.getAffineXCoord().toBigInteger(); + BigInteger Ry = R.getAffineYCoord().toBigInteger(); + BigInteger Qx = Q.getAffineXCoord().toBigInteger(); + BigInteger Qy = Q.getAffineYCoord().toBigInteger(); + + // Compute l = (Cy - Ry) / (Cx - Rx) mod p + BigInteger l = Cy.subtract(Ry) + .multiply(Cx.subtract(Rx).modInverse(p)) + .mod(p); + + // Compute v = v * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) + return fp2Multiply(v[0], v[1], l.multiply(Qx.add(Cx)), Qy.subtract(Cy), p); + } + + + private static BigInteger[] fp2Multiply(BigInteger x_real, BigInteger x_imag, BigInteger y_real, BigInteger y_imag, BigInteger p) + { + // Multiply v = (a + i*b) * scalar + return new BigInteger[]{ + x_real.multiply(y_real).subtract(x_imag.multiply(y_imag)).mod(p), + x_real.multiply(y_imag).add(x_imag.multiply(y_real)).mod(p) + }; + } + + private static BigInteger fp2FinalExponentiation(BigInteger[] v, BigInteger p, BigInteger c) + { + // Compute representative in F_p: return b/a (mod p) +// BigInteger v0 = v[0].modPow(c, p); +// BigInteger v1 = v[1].modPow(c, p); +// return v1.multiply(v0.modInverse(p)).mod(p); + v = fp2Multiply(v[0], v[1], v[0], v[1], p); + v = fp2Multiply(v[0], v[1], v[0], v[1], p); + return v[1].multiply(v[0].modInverse(p)).mod(p); + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index df94f031ae..59895f5738 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -171,7 +171,7 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip BigInteger H = ssv.xor(mask); // 5. Encode encapsulated data (R_bS, H) - byte[] encapsulated = encodeData(R_bS, H); + byte[] encapsulated = Arrays.concatenate(R_bS.getEncoded(false), H.toByteArray()); return new SecretWithEncapsulationImpl( encapsulated, @@ -337,207 +337,279 @@ public BigInteger getB() public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) { ECCurve curve = R.getCurve(); - FP2Element v = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); // Initialize to 1+0i + //FP2Element v = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); // Initialize to 1+0i - // Use correct exponent from RFC 6508: (p² - 1)/q - BigInteger exponent = p.pow(2).subtract(BigInteger.ONE).divide(q); + // Use correct exponent from RFC 6508: (p+1)/q + BigInteger exponent = p.add(BigInteger.ONE).divide(q); String qBits = q.subtract(BigInteger.ONE).toString(2); ECPoint C = R.normalize(); - - for (int j = 1; j < qBits.length(); j++) + BigInteger vx = BigInteger.ONE; + BigInteger vy = BigInteger.ZERO; + + // Evaluate line at Q using F_p² arithmetic + BigInteger Qx = Q.getAffineXCoord().toBigInteger(); + BigInteger Qy = Q.getAffineYCoord().toBigInteger(); + + ECPoint Rnorm = R.normalize(); + BigInteger Rx = Rnorm.getAffineXCoord().toBigInteger(); + BigInteger Ry = Rnorm.getAffineYCoord().toBigInteger(); + int n = q.subtract(BigInteger.ONE).bitLength() - 1; + for (int j = n; j > 0; j--) { + /* + * BigInteger xPlusY = currentX.add(currentY).mod(p); + BigInteger xMinusY = currentX.subtract(currentY).mod(p); + BigInteger newX = xPlusY.multiply(xMinusY).mod(p); + + // Compute newY = 2xy mod p + BigInteger newY = currentX.multiply(currentY).multiply(BigInteger.valueOf(2)).mod(p); + * */ + BigInteger xPlusY = vx.add(vy).mod(p); + BigInteger xMinusY = vx.subtract(vy).mod(p); + BigInteger newX = xPlusY.multiply(xMinusY).mod(p); + + // Compute newY = 2xy mod p + BigInteger newY = vx.multiply(vy).multiply(BigInteger.valueOf(2)).mod(p); + vx = newX; + vy = newY; + C = C.normalize(); - ECFieldElement Cx = C.getAffineXCoord(); - ECFieldElement Cy = C.getAffineYCoord(); + BigInteger Cx = C.getAffineXCoord().toBigInteger(); + BigInteger Cy = C.getAffineYCoord().toBigInteger(); + // Line function for doubling - ECFieldElement lNum = Cx.square().multiply(curve.fromBigInteger(BigInteger.valueOf(3))).add(curve.getA()); - ECFieldElement lDen = Cy.multiply(curve.fromBigInteger(BigInteger.valueOf(2))); - BigInteger l = lNum.divide(lDen).toBigInteger(); - - // Evaluate line at Q using F_p² arithmetic - ECFieldElement Qx = Q.getAffineXCoord(); - ECFieldElement Qy = Q.getAffineYCoord(); - FP2Element lineVal = new FP2Element( - l.multiply(Qx.add(Cx).toBigInteger()), - l.multiply(Qy.toBigInteger()), - p - ); - - v = v.multiply(lineVal).pow(BigInteger.valueOf(2)); + //3*(C_x^2 - 1) + BigInteger t_x1_bn = (Cx.multiply(Cx).mod(p).subtract(BigInteger.ONE)).multiply(BigInteger.valueOf(3)); + //Qx + Cx + BigInteger t_bn = Qx.add(Cx); + //3*(C_x^2 - 1)(Qx+Cx) + t_x1_bn = t_x1_bn.multiply(t_bn).mod(p); + //Cy^2*2 + t_bn = Cy.multiply(Cy).mod(p).multiply(BigInteger.valueOf(2)); + //3*(C_x^2 - 1)(Qx+Cx) - Cy^2*2 + t_x1_bn = t_x1_bn.subtract(t_bn).mod(p); + // Cy*2*Qy + BigInteger t_x2_bn = Cy.multiply(BigInteger.valueOf(2)).multiply(Qy).mod(p); + + /* + * BigInteger real = currentX.multiply(pointX) + .subtract(currentY.multiply(pointY)) + .mod(p); + + // Compute imaginary part = x1*y2 + x2*y1 mod p + BigInteger imag = currentX.multiply(pointY) + .add(pointX.multiply(currentY)) + .mod(p);*/ + BigInteger real = vx.multiply(t_x1_bn) + .subtract(vy.multiply(t_x2_bn)) + .mod(p); + + // Compute imaginary part = x1*y2 + x2*y1 mod p + BigInteger imag = vx.multiply(t_x2_bn) + .add(t_x1_bn.multiply(vy)) + .mod(p); + + vx = real; + vy = imag; + C = C.twice().normalize(); if (qBits.charAt(j) == '1') { - ECPoint Rnorm = R.normalize(); - ECFieldElement Rx = Rnorm.getAffineXCoord(); - ECFieldElement Ry = Rnorm.getAffineYCoord(); - - // Line function for addition - ECFieldElement lAddNum = Cy.subtract(Ry); - ECFieldElement lAddDen = Cx.subtract(Rx); - BigInteger lAdd = lAddNum.divide(lAddDen).toBigInteger(); - - FP2Element lineAddVal = new FP2Element( - lAdd.multiply(Qx.add(Cx).toBigInteger()), - lAdd.multiply(Qy.toBigInteger()), - p - ); - - v = v.multiply(lineAddVal); + t_x1_bn = Qx.add(Rx).multiply(Cy).mod(p); + BigInteger tmp_t_bn = Qx.add(Cx).multiply(Ry); + t_x1_bn = t_x1_bn.subtract(tmp_t_bn).mod(p); + t_x2_bn = Cx.subtract(Rx).multiply(Qy).mod(p); + real = vx.multiply(t_x1_bn) + .subtract(vy.multiply(t_x2_bn)) + .mod(p); + + // Compute imaginary part = x1*y2 + x2*y1 mod p + imag = vx.multiply(t_x2_bn) + .add(t_x1_bn.multiply(vy)) + .mod(p); + + vx = real; + vy = imag; C = C.add(Rnorm).normalize(); } } + BigInteger xPlusY = vx.add(vy).mod(p); + BigInteger xMinusY = vx.subtract(vy).mod(p); + BigInteger newX = xPlusY.multiply(xMinusY).mod(p); - // Final exponentiation - FP2Element t = v.pow(exponent); + // Compute newY = 2xy mod p + BigInteger newY = vx.multiply(vy).multiply(BigInteger.valueOf(2)).mod(p); + vx = newX; + vy = newY; - // Convert to F_p representative: b/a mod p - BigInteger a = t.getA(); - BigInteger b = t.getB(); - return b.multiply(a.modInverse(p)).mod(p); - } + xPlusY = vx.add(vy).mod(p); + xMinusY = vx.subtract(vy).mod(p); + newX = xPlusY.multiply(xMinusY).mod(p); + + // Compute newY = 2xy mod p + newY = vx.multiply(vy).multiply(BigInteger.valueOf(2)).mod(p); + + vx = newX; + vy = newY; -// public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) -// { + BigInteger w = vx.modInverse(p).multiply(vy).mod(p); + + return w; +// // Final exponentiation +// FP2Element t = v.pow(exponent); +// +// // Convert to F_p representative: b/a mod p +// BigInteger a = t.getA(); +// BigInteger b = t.getB(); +// return b.multiply(a.modInverse(p)).mod(p); + } +// public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) { // ECCurve curve = R.getCurve(); -// FP2Element i = new FP2Element(BigInteger.ZERO, BigInteger.ONE, p); // i = -1 in F_p^2 +// BigInteger qMinus1 = q.subtract(BigInteger.ONE); +// int N = qMinus1.bitLength() - 1; // +// // Initialize V = (1, 0) in Fp² +// BigInteger vx = BigInteger.ONE; +// BigInteger vy = BigInteger.ZERO; +// +// // Initialize C = R // ECPoint C = R.normalize(); -// BigInteger c = p.add(BigInteger.ONE).divide(q); -// //ECFieldElement v = curve.fromBigInteger(BigInteger.ONE); // v = 1 in F_p -// FP2Element v = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); +// BigInteger Cx = C.getAffineXCoord().toBigInteger(); +// BigInteger Cy = C.getAffineYCoord().toBigInteger(); // -// String qBits = q.subtract(BigInteger.ONE).toString(2); // Binary representation of q-1 +// // Precompute Q coordinates +// ECPoint Qnorm = Q.normalize(); +// BigInteger Qx = Qnorm.getAffineXCoord().toBigInteger(); +// BigInteger Qy = Qnorm.getAffineYCoord().toBigInteger(); // -// for (int j = 1; j < qBits.length(); j++) -// { // Skip MSB -// // l = (3 * (C_x^2 - 1)) / (2 * C_y) -// C = C.normalize(); // Add this line to ensure normalization -// ECFieldElement Cx = C.getAffineXCoord(); -// ECFieldElement Cy = C.getAffineXCoord(); -// BigInteger CxVal = Cx.toBigInteger(); -// BigInteger CyVal = Cy.toBigInteger(); -//// ECFieldElement l = Cx.square().multiply(curve.fromBigInteger(ECFieldElement.THREE)).subtract(curve.fromBigInteger(BigInteger.ONE)) -//// .divide(Cy.multiply(curve.fromBigInteger(BigIntegers.TWO))); +// // Precompute R coordinates for addition steps +// ECPoint Rnorm = R.normalize(); +// BigInteger Rx = Rnorm.getAffineXCoord().toBigInteger(); +// BigInteger Ry = Rnorm.getAffineYCoord().toBigInteger(); // -// // l = 3*(Cx² - 1) / (2*Cy) in F_p -// ECFieldElement lNum = Cx.square().subtract(curve.fromBigInteger(BigInteger.ONE)).multiply(curve.fromBigInteger(BigInteger.valueOf(3))); -// ECFieldElement lDen = Cy.multiply(curve.fromBigInteger(BigInteger.valueOf(2))); -// ECFieldElement l = lNum.divide(lDen); -// BigInteger lVal = l.toBigInteger(); +// for (; N > 0; N--) { +// // V = V² (complex squaring) +// BigInteger[] squared = pointSquare(vx, vy, p); +// vx = squared[0]; +// vy = squared[1]; // -// // Evaluate line at [i]Q: (Qx, i*Qy) -// ECFieldElement Qx = Q.getAffineXCoord(); -// ECFieldElement Qy = Q.getAffineYCoord(); -// BigInteger QxVal = Qx.toBigInteger(); -// BigInteger QyVal = Qy.toBigInteger(); +// // Calculate line function for doubling +// BigInteger[] T = computeLineFunction(Cx, Cy, Qx, Qy, p); // -// // Convert l*(Qx + Cx) to F_p² -// FP2Element term1 = new FP2Element(lVal.multiply(QxVal.add(CxVal)).mod(p), BigInteger.ZERO, p); +// // V = V * T (complex multiplication) +// BigInteger[] multiplied = pointMultiply(vx, vy, T[0], T[1], p); +// vx = multiplied[0]; +// vy = multiplied[1]; // -// // (i*Qy - Cy) in F_p²: (-Cy, Qy) -// FP2Element term2 = new FP2Element(QyVal.negate().mod(p), QyVal, p); // Wait, original term is i*Qy - Cy: i*Qy is (0, Qy), subtract Cy (Cy,0) gives (-Cy, Qy) -// FP2Element lineVal = new FP2Element(lVal, BigInteger.ZERO, p) // l is in F_p -// .multiply(new FP2Element(QxVal.add(CxVal).mod(p), BigInteger.ZERO, p)) // (Qx + Cx) in F_p -// .add(term2); // i*Qy - Cy +// // Double point C +// BigInteger[] doubled = pointDouble(Cx, Cy, p); +// Cx = doubled[0]; +// Cy = doubled[1]; // -// v = v.square().multiply(lineVal); +// if (qMinus1.testBit(N-1)) { +// // Calculate line function for addition +// BigInteger[] TAdd = computeLineFunctionAdd(Cx, Cy, Rx, Ry, Qx, Qy, p); // -// C = C.twice().normalize();; +// // V = V * TAdd +// multiplied = pointMultiply(vx, vy, TAdd[0], TAdd[1], p); +// vx = multiplied[0]; +// vy = multiplied[1]; // -// // v = v^2 * (l * (Q_x + C_x) + (i * Q_y - C_y)) -//// ECFieldElement Qx = Q.getAffineXCoord(); -//// ECFieldElement Qy = Q.getAffineYCoord(); -//// v = v.square().multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); +// // Add points C = C + R +// BigInteger[] added = pointAdd(Cx, Cy, Rx, Ry, p); +// Cx = added[0]; +// Cy = added[1]; +// } +// } // -// // Double the point -//// C = C.twice(); -// if (qBits.charAt(j) == '1') -// { -// // Compute line function for addition -// ECFieldElement Rx = R.getAffineXCoord(); -// ECFieldElement Ry = R.getAffineYCoord(); -// BigInteger RxVal = Rx.toBigInteger(); -// BigInteger RyVal = Ry.toBigInteger(); +// // Final squaring V = V² +// BigInteger[] squared = pointSquare(vx, vy, p); +// vx = squared[0]; +// vy = squared[1]; +// squared = pointSquare(vx, vy, p); +// vx = squared[0]; +// vy = squared[1]; // -// ECFieldElement lAddNum = Cy.subtract(Ry); -// ECFieldElement lAddDen = Cx.subtract(Rx); -// ECFieldElement lAdd = lAddNum.divide(lAddDen); -// BigInteger lAddVal = lAdd.toBigInteger(); +// // Compute w = (Vy * Vx⁻¹) mod p +// BigInteger vxInv = vx.modInverse(p); +// return vy.multiply(vxInv).mod(p); +// } // -// // Evaluate line at [i]Q -// FP2Element lineAddTerm1 = new FP2Element(lAddVal.multiply(QxVal.add(CxVal)).mod(p), BigInteger.ZERO, p); -// FP2Element lineAddTerm2 = new FP2Element(QyVal.negate().mod(p), QyVal, p); // i*Qy - Cy (Cy is current C's y) -// FP2Element lineAddVal = lineAddTerm1.add(lineAddTerm2); +// // Helper methods implementing exact C code operations +// private static BigInteger[] pointSquare(BigInteger x, BigInteger y, BigInteger p) { +// BigInteger xPlusY = x.add(y).mod(p); +// BigInteger xMinusY = x.subtract(y).mod(p); +// return new BigInteger[] { +// xPlusY.multiply(xMinusY).mod(p), +// x.multiply(y).multiply(BigInteger.valueOf(2)).mod(p) +// }; +// } // -// v = v.multiply(lineAddVal); +// private static BigInteger[] pointMultiply(BigInteger a, BigInteger b, BigInteger c, BigInteger d, BigInteger p) { +// return new BigInteger[] { +// a.multiply(d).add(b.multiply(c)).mod(p), +// a.multiply(c).subtract(b.multiply(d)).mod(p), // -// C = C.add(R); -// } +// }; +// } // -//// // If the bit is 1, perform additional step -//// if (qBits.charAt(j) == '1') -//// { -//// // l = (C_y - R_y) / (C_x - R_x) -//// ECFieldElement Rx = R.getAffineXCoord(); -//// ECFieldElement Ry = R.getAffineYCoord(); -//// l = Cy.subtract(Ry).divide(Cx.subtract(Rx)); -//// -//// // v = v * (l * (Q_x + C_x) + (i * Q_y - C_y)) -//// v = v.multiply(l.multiply(Qx.add(Cx)).add(i.multiply(Qy).subtract(Cy))); -//// -//// // C = C + R -//// C = C.add(R); -//// } -// } +// private static BigInteger[] pointDouble(BigInteger x, BigInteger y, BigInteger p) { +// BigInteger slope = x.pow(2).multiply(BigInteger.valueOf(3)) +// .mod(p) +// .multiply(y.multiply(BigInteger.valueOf(2)).modInverse(p)) +// .mod(p); +// return new BigInteger[] { +// slope.pow(2).subtract(x.multiply(BigInteger.valueOf(2))).mod(p), +// slope.multiply(x.subtract(slope.pow(2).mod(p))) +// .subtract(y).mod(p) +// }; +// } // -//// // Compute v^c -//// v = curve.fromBigInteger(v.toBigInteger().modPow(c, p)); -//// -//// // Convert to F_p representative -//// return computeFpRepresentative(v, curve); -// FP2Element t = v.pow(c); +// private static BigInteger[] pointAdd(BigInteger x1, BigInteger y1, BigInteger x2, BigInteger y2, BigInteger p) { +// BigInteger slope = y2.subtract(y1) +// .multiply(x2.subtract(x1).modInverse(p)) +// .mod(p); +// return new BigInteger[] { +// slope.pow(2).subtract(x1).subtract(x2).mod(p), +// slope.multiply(x1.subtract(slope.pow(2).subtract(x1).subtract(x2).mod(p))) +// .subtract(y1).mod(p) +// }; +// } // -// // Compute representative: b/a mod p -// BigInteger a = t.getA(); -// BigInteger bVal = t.getB(); -// if (a.equals(BigInteger.ZERO)) { -// throw new ArithmeticException("Division by zero in F_p representative"); -// } -// BigInteger aInv = a.modInverse(p); -// BigInteger representative = bVal.multiply(aInv).mod(p); +// private static BigInteger[] computeLineFunction(BigInteger Cx, BigInteger Cy, +// BigInteger Qx, BigInteger Qy, +// BigInteger p) { +// // Calculate 3*(Cx² - 1) +// BigInteger t_x1 = Cx.pow(2).subtract(BigInteger.ONE).multiply(BigInteger.valueOf(3)).mod(p); +// // Calculate Qx + Cx +// BigInteger t = Qx.add(Cx).mod(p); +// // Multiply components +// t_x1 = t_x1.multiply(t).mod(p); +// // Subtract 2*Cy² +// t_x1 = t_x1.subtract(Cy.pow(2).multiply(BigInteger.valueOf(2))).mod(p); +// // Calculate 2*Cy*Qy +// BigInteger t_x2 = Cy.multiply(BigInteger.valueOf(2)).multiply(Qy).mod(p); +// return new BigInteger[] {t_x1, t_x2}; +// } +// +// private static BigInteger[] computeLineFunctionAdd(BigInteger Cx, BigInteger Cy, +// BigInteger Rx, BigInteger Ry, +// BigInteger Qx, BigInteger Qy, +// BigInteger p) { +// // Calculate (Cy - Ry) +// BigInteger numerator = Cy.subtract(Ry).mod(p); +// // Calculate (Cx - Rx)⁻¹ +// BigInteger denominator = Cx.subtract(Rx).modInverse(p); +// BigInteger slope = numerator.multiply(denominator).mod(p); // -// return representative; +// // Calculate line function components +// BigInteger t_x1 = slope.multiply(Qx.add(Cx).mod(p)).mod(p); +// BigInteger t_x2 = slope.multiply(Qy).negate().mod(p); +// return new BigInteger[] {t_x1, t_x2}; // } - private static BigInteger computeFpRepresentative(ECFieldElement t, ECCurve curve) - { - // Characteristic of F_p - BigInteger p = ((ECCurve.Fp)curve).getQ(); - - // Assume t = a + i * b in F_p² → extract a, b - ECFieldElement a = t; // In F_p², a is the real part - ECFieldElement b = t.multiply(curve.fromBigInteger(BigInteger.ONE.negate().add(p))); // Imaginary part - // Compute b/a mod p - return b.toBigInteger().multiply(a.toBigInteger().modInverse(p)).mod(p); - } - - public static byte[] encodeData(ECPoint R_bS, BigInteger H) - { - // 1. Serialize EC Point (use compressed format for efficiency) - byte[] R_bS_bytes = R_bS.getEncoded(true); - - // 2. Serialize H (convert to a fixed-length byte array) - byte[] H_bytes = H.toByteArray(); - - // 3. Combine both into a single byte array - ByteBuffer buffer = ByteBuffer.allocate(R_bS_bytes.length + H_bytes.length); - buffer.put(R_bS_bytes); - buffer.put(H_bytes); - - return buffer.array(); - } } From cc77eaf2b97e786569223c337712f60e5fd31f30 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 5 Feb 2025 14:23:30 +1030 Subject: [PATCH 170/890] Pass the test vector of SAKKE --- .../crypto/kems/SAKKEKEMExtractor.java | 216 +++--------------- .../crypto/kems/SAKKEKEMSGenerator.java | 16 +- .../crypto/kems/test/SAKKEKEMSTest.java | 3 +- 3 files changed, 42 insertions(+), 193 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java index 0e23ab22cb..551f83cd01 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.kems; import java.math.BigInteger; +import java.security.SecureRandom; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.EncapsulatedSecretExtractor; @@ -8,9 +9,11 @@ import org.bouncycastle.crypto.params.SAKKEPrivateKeyParameters; import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; +import org.bouncycastle.util.encoders.Hex; import static org.bouncycastle.crypto.kems.SAKKEKEMSGenerator.pairing; @@ -48,18 +51,28 @@ public byte[] extractSecret(byte[] encapsulation) ECPoint R_bS = curve.decodePoint(Arrays.copyOfRange(encapsulation, 0, 257)); BigInteger H = new BigInteger(Arrays.copyOfRange(encapsulation, 257, 274)); + //ECCurveWithTatePairing pairing = new ECCurveWithTatePairing(q, BigInteger.ONE, BigInteger.ZERO, p); + //BigInteger w = pairing.TatePairing(R_bS, K_bS).toBigInteger(); // Step 2: Compute w = using pairing // BigInteger w = computeTLPairing(new BigInteger[] {R_bS.getXCoord().toBigInteger(), R_bS.getYCoord().toBigInteger()}, // new BigInteger[] {K_bS.getXCoord().toBigInteger(), K_bS.getYCoord().toBigInteger()}, this.p, this.q); BigInteger w = computePairing(R_bS, K_bS, p, q); - + System.out.println(new String(Hex.encode(w.toByteArray()))); + //BigInteger w = tatePairing(R_bS.getXCoord().toBigInteger(), R_bS.getYCoord().toBigInteger(), K_bS.getXCoord().toBigInteger(), K_bS.getYCoord().toBigInteger(), q, p); // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) BigInteger ssv = computeSSV(H, w); // Step 4: Compute r = HashToIntegerRange(SSV || b) -// BigInteger r = computeR(ssv, privateKey.getPrivatePoint()); + BigInteger b = privateKey.getB(); + BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); // // // Step 5: Validate R_bS + ECPoint bP = P.multiply(b).normalize(); + ECPoint Test = bP.add(Z_S).multiply(r).normalize(); + if(!R_bS.equals(Test)) + { + throw new IllegalStateException("Validation of R_bS failed"); + } // if (!validateR_bS(r, privateKey.getPrivatePoint(), R_bS)) { // throw new IllegalStateException("Validation of R_bS failed"); // } @@ -78,11 +91,6 @@ public int getEncapsulationLength() return 0; } - private BigInteger computePairing(ECPoint R, ECPoint K) - { - // Use your existing pairing implementation - return pairing(R, K, p, q); - } private BigInteger computeSSV(BigInteger H, BigInteger w) { @@ -91,175 +99,11 @@ private BigInteger computeSSV(BigInteger H, BigInteger w) return H.xor(mask); } - public static BigInteger computeTLPairing( - BigInteger[] R, // C = (Rx, Ry) - BigInteger[] Q, // Q = (Qx, Qy) - BigInteger p, - BigInteger q - ) - { - BigInteger qMinus1 = q.subtract(BigInteger.ONE); - int N = qMinus1.bitLength() - 1; - - // Initialize V = (1, 0) - BigInteger[] V = {BigInteger.ONE, BigInteger.ZERO}; - // Initialize C = R - BigInteger[] C = {R[0], R[1]}; - - for (; N > 0; N--) - { - // V = V^2 - pointSquare(V, p); - - // Compute line function T - BigInteger[] T = computeLineFunctionT(C, Q, p); - - // V = V * T - pointMultiply(V, T, p); - - // C = 2*C (point doubling) - pointDouble(C, p); - - if (qMinus1.testBit(N - 1)) - { - // Compute addition line function - BigInteger[] TAdd = computeLineFunctionAdd(C, R, Q, p); - - // V = V * TAdd - pointMultiply(V, TAdd, p); - - // C = C + R (point addition) - pointAdd(C, R, p); - } - } - - // Final squaring - pointSquare(V, p); - pointSquare(V, p); - - // Compute w = (Vy * Vx^{-1}) mod p - BigInteger VxInv = V[0].modInverse(p); - return V[1].multiply(VxInv).mod(p); - } - - private static void pointSquare(BigInteger[] point, BigInteger p) - { - BigInteger x = point[0]; - BigInteger y = point[1]; - - // x = (x + y)(x - y) mod p - BigInteger xPlusY = x.add(y).mod(p); - BigInteger xMinusY = x.subtract(y).mod(p); - BigInteger newX = xPlusY.multiply(xMinusY).mod(p); - - // y = 2xy mod p - BigInteger newY = x.multiply(y).multiply(BigInteger.valueOf(2)).mod(p); - - point[0] = newX; - point[1] = newY; - } - - private static void pointMultiply(BigInteger[] a, BigInteger[] b, BigInteger p) - { - // Complex multiplication (a + bi)*(c + di) = (ac - bd) + (ad + bc)i - BigInteger real = a[0].multiply(b[0]).subtract(a[1].multiply(b[1])).mod(p); - BigInteger imag = a[0].multiply(b[1]).add(a[1].multiply(b[0])).mod(p); - - a[0] = real; - a[1] = imag; - } - - private static void pointDouble(BigInteger[] point, BigInteger p) - { - // Elliptic curve point doubling formulas - BigInteger x = point[0]; - BigInteger y = point[1]; - - BigInteger slope = x.pow(2).multiply(BigInteger.valueOf(3)) - .mod(p) - .multiply(y.multiply(BigInteger.valueOf(2)).modInverse(p)) - .mod(p); - - BigInteger newX = slope.pow(2).subtract(x.multiply(BigInteger.valueOf(2))).mod(p); - BigInteger newY = slope.multiply(x.subtract(newX)).subtract(y).mod(p); - - point[0] = newX; - point[1] = newY; - } - - private static void pointAdd(BigInteger[] a, BigInteger[] b, BigInteger p) - { - // Elliptic curve point addition - BigInteger x1 = a[0], y1 = a[1]; - BigInteger x2 = b[0], y2 = b[1]; - - BigInteger slope = y2.subtract(y1) - .multiply(x2.subtract(x1).modInverse(p)) - .mod(p); - - BigInteger newX = slope.pow(2).subtract(x1).subtract(x2).mod(p); - BigInteger newY = slope.multiply(x1.subtract(newX)).subtract(y1).mod(p); - - a[0] = newX; - a[1] = newY; - } - - private static BigInteger[] computeLineFunctionT( - BigInteger[] C, - BigInteger[] Q, - BigInteger p - ) - { - // Line function evaluation for doubling - BigInteger Cx = C[0], Cy = C[1]; - BigInteger Qx = Q[0], Qy = Q[1]; - - // l = (3Cx² + a)/(2Cy) but a=0 for many curves - BigInteger numerator = Cx.pow(2).multiply(BigInteger.valueOf(3)).mod(p); - BigInteger denominator = Cy.multiply(BigInteger.valueOf(2)).mod(p); - BigInteger l = numerator.multiply(denominator.modInverse(p)).mod(p); - - // T = l*(Qx + Cx) - 2Qy - BigInteger tReal = l.multiply(Qx.add(Cx).mod(p)).mod(p); - BigInteger tImag = l.multiply(Qy).negate().mod(p); - - return new BigInteger[]{tReal, tImag}; - } - - private static BigInteger[] computeLineFunctionAdd( - BigInteger[] C, - BigInteger[] R, - BigInteger[] Q, - BigInteger p - ) - { - // Line function evaluation for addition - BigInteger Cx = C[0], Cy = C[1]; - BigInteger Rx = R[0], Ry = R[1]; - BigInteger Qx = Q[0], Qy = Q[1]; - - // l = (Cy - Ry)/(Cx - Rx) - BigInteger numerator = Cy.subtract(Ry).mod(p); - BigInteger denominator = Cx.subtract(Rx).mod(p); - BigInteger l = numerator.multiply(denominator.modInverse(p)).mod(p); - - // T = l*(Qx + Cx) - Qy - BigInteger tReal = l.multiply(Qx.add(Cx).mod(p)).mod(p); - BigInteger tImag = l.multiply(Qy).negate().mod(p); - - return new BigInteger[]{tReal, tImag}; - } - - private boolean pointsEqual(ECPoint p1, ECPoint p2) - { - return p1.normalize().getXCoord().equals(p2.normalize().getXCoord()) - && p1.normalize().getYCoord().equals(p2.normalize().getYCoord()); - } - public static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) { BigInteger c = p.add(BigInteger.ONE).divide(q); // Compute c = (p+1)/q BigInteger[] v = new BigInteger[]{BigInteger.ONE, BigInteger.ZERO}; // v = (1,0) in F_p^2 + //BigInteger v = BigInteger.ONE; ECPoint C = R; BigInteger qMinusOne = q.subtract(BigInteger.ONE); @@ -269,6 +113,7 @@ public static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigI for (int i = numBits - 2; i >= 0; i--) { v = fp2SquareAndAccumulate(v, C, Q, p); + C = C.twice().normalize(); // C = [2]C if (qMinusOne.testBit(i)) @@ -290,13 +135,24 @@ private static BigInteger[] fp2SquareAndAccumulate(BigInteger[] v, ECPoint C, EC BigInteger Qy = Q.getAffineYCoord().toBigInteger(); // Compute l = (3 * (Cx^2 - 1)) / (2 * Cy) mod p - BigInteger l = Cx.multiply(Cx).mod(p).subtract(BigInteger.ONE).multiply(BigInteger.valueOf(3)).mod(p) - .multiply(Cy.multiply(BigInteger.valueOf(2)).modInverse(p)) - .mod(p); + BigInteger l = BigInteger.valueOf(3).multiply(Cx.multiply(Cx).subtract(BigInteger.ONE)) + .multiply(Cy.multiply(BigInteger.valueOf(2)).modInverse(p)).mod(p); // Compute v = v^2 * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) v = fp2Multiply(v[0], v[1], v[0], v[1], p); - return fp2Multiply(v[0], v[1], l.multiply(Qx.add(Cx)), (Qy.subtract(Cy)), p); +// v[0] = v[0].multiply(v[0]); +// v[1] = v[1].multiply(v[1]); + return accumulateLine(v[0], v[1], Cx, Cy, Qx, Qy, l, p); +// BigInteger t_x1_bn = Cx.multiply(Cx).subtract(BigInteger.ONE).multiply(BigInteger.valueOf(3)).multiply(Qx.add(Cx)).mod(p) +// .subtract(Cy.multiply(Cy).multiply(BigInteger.valueOf(2))).mod(p); +// BigInteger t_x2_bn = Cy.multiply(Qy).multiply(BigInteger.valueOf(2)).mod(p); +// v = fp2Multiply(v[0], v[1], v[0], v[1], p); +// return fp2Multiply(v[0], v[1], t_x1_bn, t_x2_bn, p); + } + + private static BigInteger[] accumulateLine(BigInteger v0, BigInteger v1, BigInteger Cx, BigInteger Cy, BigInteger Qx, BigInteger Qy, BigInteger l, BigInteger p) + { + return fp2Multiply(v0, v1, l.multiply(Qx.add(Cx)).subtract(Cy), Qy, p); } private static BigInteger[] fp2MultiplyAndAccumulate(BigInteger[] v, ECPoint C, ECPoint R, ECPoint Q, BigInteger p) @@ -314,11 +170,15 @@ private static BigInteger[] fp2MultiplyAndAccumulate(BigInteger[] v, ECPoint C, .mod(p); // Compute v = v * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) - return fp2Multiply(v[0], v[1], l.multiply(Qx.add(Cx)), Qy.subtract(Cy), p); + return accumulateLine(v[0], v[1], Cx, Cy, Qx, Qy, l, p); +// BigInteger t_x1_bn = Qx.add(Rx).multiply(Cy).subtract(Qx.add(Cx).multiply(Ry)).mod(p); +// BigInteger t_x2_bn = Cx.subtract(Rx).multiply(Qy).mod(p); +// return fp2Multiply(v[0], v[1], t_x1_bn, t_x2_bn, p); + } - private static BigInteger[] fp2Multiply(BigInteger x_real, BigInteger x_imag, BigInteger y_real, BigInteger y_imag, BigInteger p) + static BigInteger[] fp2Multiply(BigInteger x_real, BigInteger x_imag, BigInteger y_real, BigInteger y_imag, BigInteger p) { // Multiply v = (a + i*b) * scalar return new BigInteger[]{ diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index 59895f5738..1d5128e7d3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -156,20 +156,12 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip BigInteger g_r = result[0].mod(p); g_r = g_r.modInverse(p); g_r = g_r.multiply(result[1]).mod(p); - System.out.println("g_r " + new String(Hex.encode(g_r.toByteArray()))); - byte[] expected_g_r = Hex.decode("7D2A8438 E6291C64 9B6579EB 3B79EAE9\n" + - " 48B1DE9E 5F7D1F40 70A08F8D B6B3C515\n" + - " 6F2201AF FBB5CB9D 82AA3EC0 D0398B89\n" + - " ABC78A13 A760C0BF 3F77E63D 0DF3F1A3\n" + - " 41A41B88 11DF197F D6CD0F00 3125606F\n" + - " 4F109F40 0F7292A1 0D255E3C 0EBCCB42\n" + - " 53FB182C 68F09CF6 CD9C4A53 DA6C74AD\n" + - " 007AF36B 8BCA979D 5895E282 F483FCD6"); + BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(n)); // 2^n System.out.println(new String(Hex.encode(mask.toByteArray()))); BigInteger H = ssv.xor(mask); - + System.out.println(new String(Hex.encode(H.toByteArray()))); // 5. Encode encapsulated data (R_bS, H) byte[] encapsulated = Arrays.concatenate(R_bS.getEncoded(false), H.toByteArray()); @@ -201,10 +193,6 @@ public static boolean sakkePointExponent( BigInteger n ) { - if (n.equals(BigInteger.ZERO)) - { - return false; - } // Initialize result with the original point BigInteger currentX = pointX; diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index 485965ad59..d40a78abfb 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -155,7 +155,8 @@ public void performTest() ECPoint K_bS = curve.createPoint(kbx, kby); - SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(new BigInteger(b), K_bS, new SAKKEPublicKeyParameters(null))); + SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(new BigInteger(b), K_bS, + new SAKKEPublicKeyParameters(curve.createPoint(Zx, Zy)))); byte[] test = extractor.extractSecret(rlt.getSecret()); From 50c84da8e52143b318d67e5ba26d23bd5f31eb22 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 5 Feb 2025 16:25:43 +1030 Subject: [PATCH 171/890] TODO: SAKKEKEMSGenerator and parameter settings --- .../crypto/kems/SAKKEKEMExtractor.java | 133 ++--- .../crypto/kems/SAKKEKEMSGenerator.java | 478 +----------------- .../crypto/kems/test/SAKKEKEMSTest.java | 32 +- 3 files changed, 75 insertions(+), 568 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java index 551f83cd01..8551f74680 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java @@ -1,11 +1,8 @@ package org.bouncycastle.crypto.kems; import java.math.BigInteger; -import java.security.SecureRandom; -import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.EncapsulatedSecretExtractor; -import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.params.SAKKEPrivateKeyParameters; import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; @@ -15,7 +12,6 @@ import org.bouncycastle.util.BigIntegers; import org.bouncycastle.util.encoders.Hex; -import static org.bouncycastle.crypto.kems.SAKKEKEMSGenerator.pairing; public class SAKKEKEMExtractor implements EncapsulatedSecretExtractor @@ -51,31 +47,26 @@ public byte[] extractSecret(byte[] encapsulation) ECPoint R_bS = curve.decodePoint(Arrays.copyOfRange(encapsulation, 0, 257)); BigInteger H = new BigInteger(Arrays.copyOfRange(encapsulation, 257, 274)); - //ECCurveWithTatePairing pairing = new ECCurveWithTatePairing(q, BigInteger.ONE, BigInteger.ZERO, p); - //BigInteger w = pairing.TatePairing(R_bS, K_bS).toBigInteger(); // Step 2: Compute w = using pairing -// BigInteger w = computeTLPairing(new BigInteger[] {R_bS.getXCoord().toBigInteger(), R_bS.getYCoord().toBigInteger()}, -// new BigInteger[] {K_bS.getXCoord().toBigInteger(), K_bS.getYCoord().toBigInteger()}, this.p, this.q); BigInteger w = computePairing(R_bS, K_bS, p, q); System.out.println(new String(Hex.encode(w.toByteArray()))); //BigInteger w = tatePairing(R_bS.getXCoord().toBigInteger(), R_bS.getYCoord().toBigInteger(), K_bS.getXCoord().toBigInteger(), K_bS.getYCoord().toBigInteger(), q, p); // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) - BigInteger ssv = computeSSV(H, w); + BigInteger twoToN = BigInteger.ONE.shiftLeft(n); + BigInteger mask = SAKKEUtils.hashToIntegerRange(w.toByteArray(), twoToN); + BigInteger ssv = H.xor(mask); // Step 4: Compute r = HashToIntegerRange(SSV || b) BigInteger b = privateKey.getB(); BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); -// -// // Step 5: Validate R_bS + + // Step 5: Validate R_bS ECPoint bP = P.multiply(b).normalize(); ECPoint Test = bP.add(Z_S).multiply(r).normalize(); - if(!R_bS.equals(Test)) + if (!R_bS.equals(Test)) { throw new IllegalStateException("Validation of R_bS failed"); } -// if (!validateR_bS(r, privateKey.getPrivatePoint(), R_bS)) { -// throw new IllegalStateException("Validation of R_bS failed"); -// } return BigIntegers.asUnsignedByteArray(n / 8, ssv); } @@ -92,109 +83,75 @@ public int getEncapsulationLength() } - private BigInteger computeSSV(BigInteger H, BigInteger w) - { - BigInteger twoToN = BigInteger.ONE.shiftLeft(n); - BigInteger mask = SAKKEUtils.hashToIntegerRange(w.toByteArray(), twoToN); - return H.xor(mask); - } - public static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) { - BigInteger c = p.add(BigInteger.ONE).divide(q); // Compute c = (p+1)/q - BigInteger[] v = new BigInteger[]{BigInteger.ONE, BigInteger.ZERO}; // v = (1,0) in F_p^2 - //BigInteger v = BigInteger.ONE; + // v = (1,0) in F_p^2 + BigInteger[] v = new BigInteger[]{BigInteger.ONE, BigInteger.ZERO}; ECPoint C = R; BigInteger qMinusOne = q.subtract(BigInteger.ONE); int numBits = qMinusOne.bitLength(); + BigInteger Qx = Q.getAffineXCoord().toBigInteger(); + BigInteger Qy = Q.getAffineYCoord().toBigInteger(); + BigInteger Rx = R.getAffineXCoord().toBigInteger(); + BigInteger Ry = R.getAffineYCoord().toBigInteger(); + BigInteger l, Cx, Cy; + final BigInteger three = BigInteger.valueOf(3); + final BigInteger two = BigInteger.valueOf(2); // Miller loop for (int i = numBits - 2; i >= 0; i--) { - v = fp2SquareAndAccumulate(v, C, Q, p); + Cx = C.getAffineXCoord().toBigInteger(); + Cy = C.getAffineYCoord().toBigInteger(); + + // Compute l = (3 * (Cx^2 - 1)) / (2 * Cy) mod p + l = three.multiply(Cx.multiply(Cx).subtract(BigInteger.ONE)) + .multiply(Cy.multiply(two).modInverse(p)).mod(p); + + // Compute v = v^2 * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) + v = fp2PointSquare(v[0], v[1], p); + v = fp2Multiply(v[0], v[1], l.multiply(Qx.add(Cx)).subtract(Cy), Qy, p); C = C.twice().normalize(); // C = [2]C if (qMinusOne.testBit(i)) { - v = fp2MultiplyAndAccumulate(v, C, R, Q, p); + Cx = C.getAffineXCoord().toBigInteger(); + Cy = C.getAffineYCoord().toBigInteger(); + + // Compute l = (Cy - Ry) / (Cx - Rx) mod p + l = Cy.subtract(Ry).multiply(Cx.subtract(Rx).modInverse(p)).mod(p); + + // Compute v = v * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) + v = fp2Multiply(v[0], v[1], l.multiply(Qx.add(Cx)).subtract(Cy), Qy, p); + C = C.add(R).normalize(); } } // Final exponentiation: t = v^c - return fp2FinalExponentiation(v, p, c); - } - - private static BigInteger[] fp2SquareAndAccumulate(BigInteger[] v, ECPoint C, ECPoint Q, BigInteger p) - { - BigInteger Cx = C.getAffineXCoord().toBigInteger(); - BigInteger Cy = C.getAffineYCoord().toBigInteger(); - BigInteger Qx = Q.getAffineXCoord().toBigInteger(); - BigInteger Qy = Q.getAffineYCoord().toBigInteger(); - - // Compute l = (3 * (Cx^2 - 1)) / (2 * Cy) mod p - BigInteger l = BigInteger.valueOf(3).multiply(Cx.multiply(Cx).subtract(BigInteger.ONE)) - .multiply(Cy.multiply(BigInteger.valueOf(2)).modInverse(p)).mod(p); - - // Compute v = v^2 * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) - v = fp2Multiply(v[0], v[1], v[0], v[1], p); -// v[0] = v[0].multiply(v[0]); -// v[1] = v[1].multiply(v[1]); - return accumulateLine(v[0], v[1], Cx, Cy, Qx, Qy, l, p); -// BigInteger t_x1_bn = Cx.multiply(Cx).subtract(BigInteger.ONE).multiply(BigInteger.valueOf(3)).multiply(Qx.add(Cx)).mod(p) -// .subtract(Cy.multiply(Cy).multiply(BigInteger.valueOf(2))).mod(p); -// BigInteger t_x2_bn = Cy.multiply(Qy).multiply(BigInteger.valueOf(2)).mod(p); -// v = fp2Multiply(v[0], v[1], v[0], v[1], p); -// return fp2Multiply(v[0], v[1], t_x1_bn, t_x2_bn, p); - } - - private static BigInteger[] accumulateLine(BigInteger v0, BigInteger v1, BigInteger Cx, BigInteger Cy, BigInteger Qx, BigInteger Qy, BigInteger l, BigInteger p) - { - return fp2Multiply(v0, v1, l.multiply(Qx.add(Cx)).subtract(Cy), Qy, p); - } - - private static BigInteger[] fp2MultiplyAndAccumulate(BigInteger[] v, ECPoint C, ECPoint R, ECPoint Q, BigInteger p) - { - BigInteger Cx = C.getAffineXCoord().toBigInteger(); - BigInteger Cy = C.getAffineYCoord().toBigInteger(); - BigInteger Rx = R.getAffineXCoord().toBigInteger(); - BigInteger Ry = R.getAffineYCoord().toBigInteger(); - BigInteger Qx = Q.getAffineXCoord().toBigInteger(); - BigInteger Qy = Q.getAffineYCoord().toBigInteger(); - - // Compute l = (Cy - Ry) / (Cx - Rx) mod p - BigInteger l = Cy.subtract(Ry) - .multiply(Cx.subtract(Rx).modInverse(p)) - .mod(p); - - // Compute v = v * ( l*( Q_x + C_x ) + ( i*Q_y - C_y ) ) - return accumulateLine(v[0], v[1], Cx, Cy, Qx, Qy, l, p); -// BigInteger t_x1_bn = Qx.add(Rx).multiply(Cy).subtract(Qx.add(Cx).multiply(Ry)).mod(p); -// BigInteger t_x2_bn = Cx.subtract(Rx).multiply(Qy).mod(p); -// return fp2Multiply(v[0], v[1], t_x1_bn, t_x2_bn, p); - + v = fp2PointSquare(v[0], v[1], p); + v = fp2PointSquare(v[0], v[1], p); + return v[1].multiply(v[0].modInverse(p)).mod(p); } - static BigInteger[] fp2Multiply(BigInteger x_real, BigInteger x_imag, BigInteger y_real, BigInteger y_imag, BigInteger p) { - // Multiply v = (a + i*b) * scalar return new BigInteger[]{ x_real.multiply(y_real).subtract(x_imag.multiply(y_imag)).mod(p), x_real.multiply(y_imag).add(x_imag.multiply(y_real)).mod(p) }; } - private static BigInteger fp2FinalExponentiation(BigInteger[] v, BigInteger p, BigInteger c) + static BigInteger[] fp2PointSquare(BigInteger currentX, BigInteger currentY, BigInteger p) { - // Compute representative in F_p: return b/a (mod p) -// BigInteger v0 = v[0].modPow(c, p); -// BigInteger v1 = v[1].modPow(c, p); -// return v1.multiply(v0.modInverse(p)).mod(p); - v = fp2Multiply(v[0], v[1], v[0], v[1], p); - v = fp2Multiply(v[0], v[1], v[0], v[1], p); - return v[1].multiply(v[0].modInverse(p)).mod(p); + BigInteger xPlusY = currentX.add(currentY).mod(p); + BigInteger xMinusY = currentX.subtract(currentY).mod(p); + BigInteger newX = xPlusY.multiply(xMinusY).mod(p); + + // Compute newY = 2xy mod p + BigInteger newY = currentX.multiply(currentY).multiply(BigInteger.valueOf(2)).mod(p); + return new BigInteger[]{newX, newY}; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index 1d5128e7d3..d2f81af17b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -3,61 +3,33 @@ import org.bouncycastle.crypto.EncapsulatedSecretGenerator; import org.bouncycastle.crypto.SecretWithEncapsulation; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; -import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; -import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; import org.bouncycastle.util.encoders.Hex; import java.math.BigInteger; -import java.nio.ByteBuffer; import java.security.SecureRandom; public class SAKKEKEMSGenerator implements EncapsulatedSecretGenerator { - // private static final BigInteger p = new BigInteger(Hex.decode("997ABB1F 0A563FDA 65C61198 DAD0657A\n" + -// " 416C0CE1 9CB48261 BE9AE358 B3E01A2E\n" + -// " F40AAB27 E2FC0F1B 228730D5 31A59CB0\n" + -// " E791B39F F7C88A19 356D27F4 A666A6D0\n" + -// " E26C6487 326B4CD4 512AC5CD 65681CE1\n" + -// " B6AFF4A8 31852A82 A7CF3C52 1C3C09AA\n" + -// " 9F94D6AF 56971F1F FCE3E823 89857DB0\n" + -// " 80C5DF10 AC7ACE87 666D807A FEA85FEB")); private static final BigInteger p = new BigInteger( "997ABB1F0A563FDA65C61198DAD0657A416C0CE19CB48261BE9AE358B3E01A2E" + "F40AAB27E2FC0F1B228730D531A59CB0E791B39FF7C88A19356D27F4A666A6D0" + "E26C6487326B4CD4512AC5CD65681CE1B6AFF4A831852A82A7CF3C521C3C09AA" + "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 ); - // private static final BigInteger q = new BigInteger(Hex.decode("265EAEC7 C2958FF6 99718466 36B4195E\n" + -// " 905B0338 672D2098 6FA6B8D6 2CF8068B\n" + -// " BD02AAC9 F8BF03C6 C8A1CC35 4C69672C\n" + -// " 39E46CE7 FDF22286 4D5B49FD 2999A9B4\n" + -// " 389B1921 CC9AD335 144AB173 595A0738\n" + -// " 6DABFD2A 0C614AA0 A9F3CF14 870F026A\n" + -// " A7E535AB D5A5C7C7 FF38FA08 E2615F6C\n" + -// " 203177C4 2B1EB3A1 D99B601E BFAA17FB")); + private static final BigInteger q = new BigInteger( "265EAEC7C2958FF69971846636B4195E905B0338672D20986FA6B8D62CF8068B" + "BD02AAC9F8BF03C6C8A1CC354C69672C39E46CE7FDF222864D5B49FD2999A9B4" + "389B1921CC9AD335144AB173595A07386DABFD2A0C614AA0A9F3CF14870F026A" + "A7E535ABD5A5C7C7FF38FA08E2615F6C203177C42B1EB3A1D99B601EBFAA17FB", 16 ); -// private static final BigInteger a = BigInteger.valueOf(-3).mod(p); // y² = x³ - 3x -// private static final BigInteger b = BigInteger.ZERO; -// private static final ECCurve.Fp curve = new ECCurve.Fp( -// p, // Prime p -// BigInteger.valueOf(-3).mod(p), // a = -3 -// BigInteger.ZERO, // b = 0 -// q, // Order of the subgroup (from RFC 6509) -// BigInteger.ONE // Cofactor = 1 -// ); - // Base point P = (Px, Py) private static final BigInteger Px = new BigInteger( "53FC09EE332C29AD0A7990053ED9B52A2B1A2FD60AEC69C698B2F204B6FF7CBF" + "B5EDB6C0F6CE2308AB10DB9030B09E1043D5F22CDB9DFA55718BD9E7406CE890" + @@ -72,7 +44,6 @@ public class SAKKEKEMSGenerator "13515AD7E9CB99A980BDAD5AD5BB4636ADB9B5706A67DCDE75573FD71BEF16D7", 16 ); - BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + " C6A87BD1 FB94C41E 27FABE65 8E015A87\n" + " 371E9474 4C96FEDA 449AE956 3F8BC446\n" + @@ -81,7 +52,7 @@ public class SAKKEKEMSGenerator " 55DF0460 B4A9FD74 B4F1A32B CAFA1FFA\n" + " D682C033 A7942BCC E3720F20 B9B7B040\n" + " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); - private final int n = 128; + private static final int n = 128; private final SecureRandom random; public SAKKEKEMSGenerator(SecureRandom random) @@ -98,7 +69,7 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip // 2. Compute r = HashToIntegerRange(SSV || b, q) BigInteger b = new BigInteger("323031312D30320074656C3A2B34343737303039303031323300", 16); //getRecipientId((SAKKEPublicKey)recipientKey); BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); - System.out.println(new String(Hex.encode(r.toByteArray()))); + //System.out.println(new String(Hex.encode(r.toByteArray()))); ECCurve.Fp curve = new ECCurve.Fp( p, // Prime p BigInteger.valueOf(-3).mod(p), // a = -3 @@ -107,24 +78,7 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip BigInteger.ONE // Cofactor = 1 ); ECPoint P = curve.createPoint(Px, Py); - ECPoint G = curve.createPoint( - new BigInteger(Hex.decode("53FC09EE 332C29AD 0A799005 3ED9B52A\n" + - " 2B1A2FD6 0AEC69C6 98B2F204 B6FF7CBF\n" + - " B5EDB6C0 F6CE2308 AB10DB90 30B09E10\n" + - " 43D5F22C DB9DFA55 718BD9E7 406CE890\n" + - " 9760AF76 5DD5BCCB 337C8654 8B72F2E1\n" + - " A702C339 7A60DE74 A7C1514D BA66910D\n" + - " D5CFB4CC 80728D87 EE9163A5 B63F73EC\n" + - " 80EC46C4 967E0979 880DC8AB EAE63895")), // Px - new BigInteger(Hex.decode("0A824906 3F6009F1 F9F1F053 3634A135\n" + - " D3E82016 02990696 3D778D82 1E141178\n" + - " F5EA69F4 654EC2B9 E7F7F5E5 F0DE55F6\n" + - " 6B598CCF 9A140B2E 416CFF0C A9E032B9\n" + - " 70DAE117 AD547C6C CAD696B5 B7652FE0\n" + - " AC6F1E80 164AA989 492D979F C5A4D5F2\n" + - " 13515AD7 E9CB99A9 80BDAD5A D5BB4636\n" + - " ADB9B570 6A67DCDE 75573FD7 1BEF16D7")) // Py - ); + ECPoint Z = curve.createPoint( new BigInteger("5958EF1B1679BF099B3A030DF255AA6A" + "23C1D8F143D4D23F753E69BD27A832F3" + @@ -143,31 +97,29 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip "C67C6D19487FB449059F26CC8AAB655A" + "B58B7CC796E24E9A394095754F5F8BAE", 16) // Py ); - BigInteger z = new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F", 16); + // 3. Compute R_(b,S) = [r]([b]P + Z_S) ECPoint bP = P.multiply(b).normalize(); ECPoint R_bS = bP.add(Z).multiply(r).normalize(); // [r]([b]P + Z_S) - System.out.println("R_Bs x:" + new String(Hex.encode(R_bS.getXCoord().toBigInteger().toByteArray()))); - System.out.println("R_Bs y:" + new String(Hex.encode(R_bS.getYCoord().toBigInteger().toByteArray()))); +// System.out.println("R_Bs x:" + new String(Hex.encode(R_bS.getXCoord().toBigInteger().toByteArray()))); +// System.out.println("R_Bs y:" + new String(Hex.encode(R_bS.getYCoord().toBigInteger().toByteArray()))); // 4. Compute H = SSV XOR HashToIntegerRange( g^r, 2^n ) - BigInteger[] result = fp2Exponentiate(p, BigInteger.ONE, g, r); - BigInteger g_r = result[0].mod(p); - g_r = g_r.modInverse(p); - g_r = g_r.multiply(result[1]).mod(p); + BigInteger[] v = fp2Exponentiate(p, BigInteger.ONE, g, r, curve); + BigInteger g_r = v[1].multiply(v[0].modInverse(p)).mod(p); BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(n)); // 2^n - System.out.println(new String(Hex.encode(mask.toByteArray()))); + //System.out.println(new String(Hex.encode(mask.toByteArray()))); BigInteger H = ssv.xor(mask); - System.out.println(new String(Hex.encode(H.toByteArray()))); + //System.out.println(new String(Hex.encode(H.toByteArray()))); // 5. Encode encapsulated data (R_bS, H) byte[] encapsulated = Arrays.concatenate(R_bS.getEncoded(false), H.toByteArray()); return new SecretWithEncapsulationImpl( - encapsulated, - BigIntegers.asUnsignedByteArray(n / 8, ssv) // Output SSV as key material + BigIntegers.asUnsignedByteArray(n / 8, ssv), // Output SSV as key material + encapsulated ); } @@ -177,11 +129,12 @@ public static BigInteger[] fp2Exponentiate( BigInteger p, BigInteger x, BigInteger y, - BigInteger exponent + BigInteger exponent, + ECCurve.Fp curve ) { BigInteger[] result = new BigInteger[2]; - sakkePointExponent(p, result, x, y, exponent); + sakkePointExponent(p, result, x, y, exponent, curve); return result; } @@ -190,45 +143,32 @@ public static boolean sakkePointExponent( BigInteger[] result, BigInteger pointX, BigInteger pointY, - BigInteger n - ) + BigInteger n, + ECCurve.Fp curve) { // Initialize result with the original point BigInteger currentX = pointX; BigInteger currentY = pointY; + ECPoint current = curve.createPoint(currentX, currentY); int numBits = n.bitLength(); - + BigInteger[] rlt; // Process bits from MSB-1 down to 0 for (int i = numBits - 2; i >= 0; i--) { // Square the current point - //sakkePointSquare(p, new BigInteger[]{currentX, currentY}); - // Compute newX = (x + y)(x - y) mod p - BigInteger xPlusY = currentX.add(currentY).mod(p); - BigInteger xMinusY = currentX.subtract(currentY).mod(p); - BigInteger newX = xPlusY.multiply(xMinusY).mod(p); - - // Compute newY = 2xy mod p - BigInteger newY = currentX.multiply(currentY).multiply(BigInteger.valueOf(2)).mod(p); - currentX = newX; - currentY = newY; + rlt = SAKKEKEMExtractor.fp2PointSquare(currentX, currentY, p); + current = current.timesPow2(2); + currentX = rlt[0]; + currentY = rlt[1]; // Multiply if bit is set if (n.testBit(i)) { - //sakkePointsMultiply(p, currentX, currentY, pointX, pointY); - BigInteger real = currentX.multiply(pointX) - .subtract(currentY.multiply(pointY)) - .mod(p); + rlt = SAKKEKEMExtractor.fp2Multiply(currentX, currentY, pointX, pointY, p); - // Compute imaginary part = x1*y2 + x2*y1 mod p - BigInteger imag = currentX.multiply(pointY) - .add(pointX.multiply(currentY)) - .mod(p); - - currentX = real; - currentY = imag; + currentX = rlt[0]; + currentY = rlt[1]; } } @@ -236,368 +176,4 @@ public static boolean sakkePointExponent( result[1] = currentY; return true; } - - - private BigInteger getRecipientId(SAKKEPublicKeyParameters pubKey) - { - byte[] hashedId = SAKKEUtils.hash(pubKey.getZ().getEncoded(false)); // Hash Z_S - return new BigInteger(1, hashedId).mod(pubKey.getQ().subtract(BigInteger.ONE)).add(BigIntegers.TWO); - } - - public static class FP2Element - { - private final BigInteger a; // Real part - private final BigInteger b; // Imaginary part - private final BigInteger p; // Prime modulus - - public FP2Element(BigInteger a, BigInteger b, BigInteger p) - { - this.a = a.mod(p); - this.b = b.mod(p); - this.p = p; - } - - public FP2Element add(FP2Element other) - { - return new FP2Element(a.add(other.a), b.add(other.b), p); - } - - public FP2Element subtract(FP2Element other) - { - return new FP2Element(a.subtract(other.a), b.subtract(other.b), p); - } - - public FP2Element multiply(FP2Element other) - { - BigInteger real = a.multiply(other.a).subtract(b.multiply(other.b)).mod(p); - BigInteger imag = a.multiply(other.b).add(b.multiply(other.a)).mod(p); - return new FP2Element(real, imag, p); - } - - public FP2Element inverse() - { - BigInteger denom = a.pow(2).add(b.pow(2)).mod(p); - BigInteger invDenom = denom.modInverse(p); - return new FP2Element(a.multiply(invDenom), b.negate().multiply(invDenom), p); - } - - public FP2Element square() - { - return this.multiply(this); - } - - public FP2Element pow(BigInteger exponent) - { - // Implement exponentiation using square-and-multiply - FP2Element result = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); - FP2Element base = this; - for (int i = exponent.bitLength() - 1; i >= 0; i--) - { - result = result.square(); - if (exponent.testBit(i)) - { - result = result.multiply(base); - } - } - return result; - } - - // Getters - public BigInteger getA() - { - return a; - } - - public BigInteger getB() - { - return b; - } - } - - /** - * Computes the Tate-Lichtenbaum pairing ⟨P, Q⟩ as per RFC 6508. - *

- * //* @param P First point (on E(F_p)). - * - * @param Q Second point (on E(F_p)). - * @return Result of the pairing in the field F_p^2. - */ - public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) - { - ECCurve curve = R.getCurve(); - //FP2Element v = new FP2Element(BigInteger.ONE, BigInteger.ZERO, p); // Initialize to 1+0i - - // Use correct exponent from RFC 6508: (p+1)/q - BigInteger exponent = p.add(BigInteger.ONE).divide(q); - - String qBits = q.subtract(BigInteger.ONE).toString(2); - ECPoint C = R.normalize(); - BigInteger vx = BigInteger.ONE; - BigInteger vy = BigInteger.ZERO; - - // Evaluate line at Q using F_p² arithmetic - BigInteger Qx = Q.getAffineXCoord().toBigInteger(); - BigInteger Qy = Q.getAffineYCoord().toBigInteger(); - - ECPoint Rnorm = R.normalize(); - BigInteger Rx = Rnorm.getAffineXCoord().toBigInteger(); - BigInteger Ry = Rnorm.getAffineYCoord().toBigInteger(); - int n = q.subtract(BigInteger.ONE).bitLength() - 1; - for (int j = n; j > 0; j--) - { - /* - * BigInteger xPlusY = currentX.add(currentY).mod(p); - BigInteger xMinusY = currentX.subtract(currentY).mod(p); - BigInteger newX = xPlusY.multiply(xMinusY).mod(p); - - // Compute newY = 2xy mod p - BigInteger newY = currentX.multiply(currentY).multiply(BigInteger.valueOf(2)).mod(p); - * */ - BigInteger xPlusY = vx.add(vy).mod(p); - BigInteger xMinusY = vx.subtract(vy).mod(p); - BigInteger newX = xPlusY.multiply(xMinusY).mod(p); - - // Compute newY = 2xy mod p - BigInteger newY = vx.multiply(vy).multiply(BigInteger.valueOf(2)).mod(p); - vx = newX; - vy = newY; - - C = C.normalize(); - BigInteger Cx = C.getAffineXCoord().toBigInteger(); - BigInteger Cy = C.getAffineYCoord().toBigInteger(); - - - // Line function for doubling - //3*(C_x^2 - 1) - BigInteger t_x1_bn = (Cx.multiply(Cx).mod(p).subtract(BigInteger.ONE)).multiply(BigInteger.valueOf(3)); - //Qx + Cx - BigInteger t_bn = Qx.add(Cx); - //3*(C_x^2 - 1)(Qx+Cx) - t_x1_bn = t_x1_bn.multiply(t_bn).mod(p); - //Cy^2*2 - t_bn = Cy.multiply(Cy).mod(p).multiply(BigInteger.valueOf(2)); - //3*(C_x^2 - 1)(Qx+Cx) - Cy^2*2 - t_x1_bn = t_x1_bn.subtract(t_bn).mod(p); - // Cy*2*Qy - BigInteger t_x2_bn = Cy.multiply(BigInteger.valueOf(2)).multiply(Qy).mod(p); - - /* - * BigInteger real = currentX.multiply(pointX) - .subtract(currentY.multiply(pointY)) - .mod(p); - - // Compute imaginary part = x1*y2 + x2*y1 mod p - BigInteger imag = currentX.multiply(pointY) - .add(pointX.multiply(currentY)) - .mod(p);*/ - BigInteger real = vx.multiply(t_x1_bn) - .subtract(vy.multiply(t_x2_bn)) - .mod(p); - - // Compute imaginary part = x1*y2 + x2*y1 mod p - BigInteger imag = vx.multiply(t_x2_bn) - .add(t_x1_bn.multiply(vy)) - .mod(p); - - vx = real; - vy = imag; - - C = C.twice().normalize(); - - if (qBits.charAt(j) == '1') - { - t_x1_bn = Qx.add(Rx).multiply(Cy).mod(p); - BigInteger tmp_t_bn = Qx.add(Cx).multiply(Ry); - t_x1_bn = t_x1_bn.subtract(tmp_t_bn).mod(p); - t_x2_bn = Cx.subtract(Rx).multiply(Qy).mod(p); - real = vx.multiply(t_x1_bn) - .subtract(vy.multiply(t_x2_bn)) - .mod(p); - - // Compute imaginary part = x1*y2 + x2*y1 mod p - imag = vx.multiply(t_x2_bn) - .add(t_x1_bn.multiply(vy)) - .mod(p); - - vx = real; - vy = imag; - C = C.add(Rnorm).normalize(); - } - } - BigInteger xPlusY = vx.add(vy).mod(p); - BigInteger xMinusY = vx.subtract(vy).mod(p); - BigInteger newX = xPlusY.multiply(xMinusY).mod(p); - - // Compute newY = 2xy mod p - BigInteger newY = vx.multiply(vy).multiply(BigInteger.valueOf(2)).mod(p); - vx = newX; - vy = newY; - - xPlusY = vx.add(vy).mod(p); - xMinusY = vx.subtract(vy).mod(p); - newX = xPlusY.multiply(xMinusY).mod(p); - - // Compute newY = 2xy mod p - newY = vx.multiply(vy).multiply(BigInteger.valueOf(2)).mod(p); - - vx = newX; - vy = newY; - - BigInteger w = vx.modInverse(p).multiply(vy).mod(p); - - return w; -// // Final exponentiation -// FP2Element t = v.pow(exponent); -// -// // Convert to F_p representative: b/a mod p -// BigInteger a = t.getA(); -// BigInteger b = t.getB(); -// return b.multiply(a.modInverse(p)).mod(p); - } -// public static BigInteger pairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) { -// ECCurve curve = R.getCurve(); -// BigInteger qMinus1 = q.subtract(BigInteger.ONE); -// int N = qMinus1.bitLength() - 1; -// -// // Initialize V = (1, 0) in Fp² -// BigInteger vx = BigInteger.ONE; -// BigInteger vy = BigInteger.ZERO; -// -// // Initialize C = R -// ECPoint C = R.normalize(); -// BigInteger Cx = C.getAffineXCoord().toBigInteger(); -// BigInteger Cy = C.getAffineYCoord().toBigInteger(); -// -// // Precompute Q coordinates -// ECPoint Qnorm = Q.normalize(); -// BigInteger Qx = Qnorm.getAffineXCoord().toBigInteger(); -// BigInteger Qy = Qnorm.getAffineYCoord().toBigInteger(); -// -// // Precompute R coordinates for addition steps -// ECPoint Rnorm = R.normalize(); -// BigInteger Rx = Rnorm.getAffineXCoord().toBigInteger(); -// BigInteger Ry = Rnorm.getAffineYCoord().toBigInteger(); -// -// for (; N > 0; N--) { -// // V = V² (complex squaring) -// BigInteger[] squared = pointSquare(vx, vy, p); -// vx = squared[0]; -// vy = squared[1]; -// -// // Calculate line function for doubling -// BigInteger[] T = computeLineFunction(Cx, Cy, Qx, Qy, p); -// -// // V = V * T (complex multiplication) -// BigInteger[] multiplied = pointMultiply(vx, vy, T[0], T[1], p); -// vx = multiplied[0]; -// vy = multiplied[1]; -// -// // Double point C -// BigInteger[] doubled = pointDouble(Cx, Cy, p); -// Cx = doubled[0]; -// Cy = doubled[1]; -// -// if (qMinus1.testBit(N-1)) { -// // Calculate line function for addition -// BigInteger[] TAdd = computeLineFunctionAdd(Cx, Cy, Rx, Ry, Qx, Qy, p); -// -// // V = V * TAdd -// multiplied = pointMultiply(vx, vy, TAdd[0], TAdd[1], p); -// vx = multiplied[0]; -// vy = multiplied[1]; -// -// // Add points C = C + R -// BigInteger[] added = pointAdd(Cx, Cy, Rx, Ry, p); -// Cx = added[0]; -// Cy = added[1]; -// } -// } -// -// // Final squaring V = V² -// BigInteger[] squared = pointSquare(vx, vy, p); -// vx = squared[0]; -// vy = squared[1]; -// squared = pointSquare(vx, vy, p); -// vx = squared[0]; -// vy = squared[1]; -// -// // Compute w = (Vy * Vx⁻¹) mod p -// BigInteger vxInv = vx.modInverse(p); -// return vy.multiply(vxInv).mod(p); -// } -// -// // Helper methods implementing exact C code operations -// private static BigInteger[] pointSquare(BigInteger x, BigInteger y, BigInteger p) { -// BigInteger xPlusY = x.add(y).mod(p); -// BigInteger xMinusY = x.subtract(y).mod(p); -// return new BigInteger[] { -// xPlusY.multiply(xMinusY).mod(p), -// x.multiply(y).multiply(BigInteger.valueOf(2)).mod(p) -// }; -// } -// -// private static BigInteger[] pointMultiply(BigInteger a, BigInteger b, BigInteger c, BigInteger d, BigInteger p) { -// return new BigInteger[] { -// a.multiply(d).add(b.multiply(c)).mod(p), -// a.multiply(c).subtract(b.multiply(d)).mod(p), -// -// }; -// } -// -// private static BigInteger[] pointDouble(BigInteger x, BigInteger y, BigInteger p) { -// BigInteger slope = x.pow(2).multiply(BigInteger.valueOf(3)) -// .mod(p) -// .multiply(y.multiply(BigInteger.valueOf(2)).modInverse(p)) -// .mod(p); -// return new BigInteger[] { -// slope.pow(2).subtract(x.multiply(BigInteger.valueOf(2))).mod(p), -// slope.multiply(x.subtract(slope.pow(2).mod(p))) -// .subtract(y).mod(p) -// }; -// } -// -// private static BigInteger[] pointAdd(BigInteger x1, BigInteger y1, BigInteger x2, BigInteger y2, BigInteger p) { -// BigInteger slope = y2.subtract(y1) -// .multiply(x2.subtract(x1).modInverse(p)) -// .mod(p); -// return new BigInteger[] { -// slope.pow(2).subtract(x1).subtract(x2).mod(p), -// slope.multiply(x1.subtract(slope.pow(2).subtract(x1).subtract(x2).mod(p))) -// .subtract(y1).mod(p) -// }; -// } -// -// private static BigInteger[] computeLineFunction(BigInteger Cx, BigInteger Cy, -// BigInteger Qx, BigInteger Qy, -// BigInteger p) { -// // Calculate 3*(Cx² - 1) -// BigInteger t_x1 = Cx.pow(2).subtract(BigInteger.ONE).multiply(BigInteger.valueOf(3)).mod(p); -// // Calculate Qx + Cx -// BigInteger t = Qx.add(Cx).mod(p); -// // Multiply components -// t_x1 = t_x1.multiply(t).mod(p); -// // Subtract 2*Cy² -// t_x1 = t_x1.subtract(Cy.pow(2).multiply(BigInteger.valueOf(2))).mod(p); -// // Calculate 2*Cy*Qy -// BigInteger t_x2 = Cy.multiply(BigInteger.valueOf(2)).multiply(Qy).mod(p); -// return new BigInteger[] {t_x1, t_x2}; -// } -// -// private static BigInteger[] computeLineFunctionAdd(BigInteger Cx, BigInteger Cy, -// BigInteger Rx, BigInteger Ry, -// BigInteger Qx, BigInteger Qy, -// BigInteger p) { -// // Calculate (Cy - Ry) -// BigInteger numerator = Cy.subtract(Ry).mod(p); -// // Calculate (Cx - Rx)⁻¹ -// BigInteger denominator = Cx.subtract(Rx).modInverse(p); -// BigInteger slope = numerator.multiply(denominator).mod(p); -// -// // Calculate line function components -// BigInteger t_x1 = slope.multiply(Qx.add(Cx).mod(p)).mod(p); -// BigInteger t_x2 = slope.multiply(Qy).negate().mod(p); -// return new BigInteger[] {t_x1, t_x2}; -// } - - } diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index d40a78abfb..187adf0d09 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -41,17 +41,6 @@ public static void main(String[] args) //System.out.println("SAKKE KEM Test " + (testPassed ? "PASSED" : "FAILED")); } - private static byte[] hexStringToByteArray(String s) - { - int len = s.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) - { - data[i / 2] = (byte)((Character.digit(s.charAt(i), 16) << 4) - + Character.digit(s.charAt(i + 1), 16)); - } - return data; - } @Override public String getName() @@ -141,7 +130,6 @@ public void performTest() "E26C6487326B4CD4512AC5CD65681CE1B6AFF4A831852A82A7CF3C521C3C09AA" + "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 ); - ECCurve.Fp curve = new ECCurve.Fp( p, // Prime p BigInteger.valueOf(-3).mod(p), // a = -3 @@ -152,28 +140,14 @@ public void performTest() SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); SecretWithEncapsulation rlt = generator.generateEncapsulated(null); + ECPoint K_bS = curve.createPoint(kbx, kby); SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(new BigInteger(b), K_bS, new SAKKEPublicKeyParameters(curve.createPoint(Zx, Zy)))); - byte[] test = extractor.extractSecret(rlt.getSecret()); - - - System.out.println("K_bS x:" + new String(Hex.encode(K_bS.getXCoord().toBigInteger().toByteArray()))); - System.out.println("K_bS y:" + new String(Hex.encode(K_bS.getYCoord().toBigInteger().toByteArray()))); - ECPoint R_bs = curve.createPoint(Rbx, Rby); - SAKKEKEMSGenerator.pairing(K_bS, R_bs, p, q); - - BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(SSV, b), q); - - System.out.println("r:" + new String(Hex.encode(r.toByteArray()))); - - System.out.println("r:" + new String(Hex.encode(expectedR))); - - Assert.assertTrue(Arrays.areEqual(r.toByteArray(), expectedR)); -// SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); -// generator.generateEncapsulated(null); + byte[] test = extractor.extractSecret(rlt.getEncapsulation()); + Assert.assertTrue(Arrays.areEqual(test, SSV)); } } From 1fdc823db8eb1feaa4914d12f5ff162334193f72 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 5 Feb 2025 18:26:05 +1030 Subject: [PATCH 172/890] Remove unused functions. Format the code --- .../crypto/kems/SAKKEKEMExtractor.java | 4 +- .../crypto/kems/SAKKEKEMSGenerator.java | 22 ++------ .../bouncycastle/crypto/kems/SAKKEUtils.java | 51 ------------------- .../crypto/kems/test/SAKKEKEMSTest.java | 25 +++------ .../crypto/test/RegressionTest.java | 2 + 5 files changed, 14 insertions(+), 90 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java index 8551f74680..469cd1aa92 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java @@ -6,11 +6,9 @@ import org.bouncycastle.crypto.params.SAKKEPrivateKeyParameters; import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; -import org.bouncycastle.math.ec.ECFieldElement; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; -import org.bouncycastle.util.encoders.Hex; public class SAKKEKEMExtractor @@ -49,7 +47,7 @@ public byte[] extractSecret(byte[] encapsulation) // Step 2: Compute w = using pairing BigInteger w = computePairing(R_bS, K_bS, p, q); - System.out.println(new String(Hex.encode(w.toByteArray()))); + //System.out.println(new String(Hex.encode(w.toByteArray()))); //BigInteger w = tatePairing(R_bS.getXCoord().toBigInteger(), R_bS.getYCoord().toBigInteger(), K_bS.getXCoord().toBigInteger(), K_bS.getYCoord().toBigInteger(), q, p); // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) BigInteger twoToN = BigInteger.ONE.shiftLeft(n); diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index d2f81af17b..0d53ce0281 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -64,7 +64,7 @@ public SAKKEKEMSGenerator(SecureRandom random) public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey) { // 1. Generate random SSV in range [0, 2^n - 1] - BigInteger ssv = new BigInteger("123456789ABCDEF0123456789ABCDEF0", 16);//new BigInteger(n, random); + BigInteger ssv = new BigInteger(n, random); // 2. Compute r = HashToIntegerRange(SSV || b, q) BigInteger b = new BigInteger("323031312D30320074656C3A2B34343737303039303031323300", 16); //getRecipientId((SAKKEPublicKey)recipientKey); @@ -127,25 +127,13 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip // Helper method for F_p² exponentiation public static BigInteger[] fp2Exponentiate( BigInteger p, - BigInteger x, - BigInteger y, - BigInteger exponent, - ECCurve.Fp curve - ) - { - BigInteger[] result = new BigInteger[2]; - sakkePointExponent(p, result, x, y, exponent, curve); - return result; - } - - public static boolean sakkePointExponent( - BigInteger p, - BigInteger[] result, BigInteger pointX, BigInteger pointY, BigInteger n, - ECCurve.Fp curve) + ECCurve.Fp curve + ) { + BigInteger[] result = new BigInteger[2]; // Initialize result with the original point BigInteger currentX = pointX; @@ -174,6 +162,6 @@ public static boolean sakkePointExponent( result[0] = currentX; result[1] = currentY; - return true; + return result; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java index 45fbbb38b5..891f2ae878 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java @@ -2,45 +2,11 @@ import java.math.BigInteger; -import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA256Digest; -import org.bouncycastle.math.ec.ECPoint; -import org.bouncycastle.util.encoders.Hex; public class SAKKEUtils { - public static ECPoint sakkePointExponent(ECPoint point, BigInteger n) { - if (n.equals(BigInteger.ZERO)) { - throw new IllegalArgumentException("Exponent cannot be zero."); - } - - ECPoint result = point; - int N = n.bitLength() - 1; - - for (; N != 0; --N) { - result = sakkePointSquare(result); - if (n.testBit(N - 1)) { - result = sakkePointsMultiply(result, point); - } - } - return result; - } - - public static ECPoint sakkePointSquare(ECPoint point) { - BigInteger x = point.getAffineXCoord().toBigInteger(); - BigInteger y = point.getAffineYCoord().toBigInteger(); - - BigInteger bx1 = x.add(y); - BigInteger bx2 = x.subtract(y); - BigInteger newX = bx1.multiply(bx2).mod(point.getCurve().getField().getCharacteristic()); - BigInteger newY = x.multiply(y).multiply(BigInteger.valueOf(2)).mod(point.getCurve().getField().getCharacteristic()); - - return point.getCurve().createPoint(newX, newY); - } - public static ECPoint sakkePointsMultiply(ECPoint p1, ECPoint p2) { - return p1.add(p2).normalize(); - } public static BigInteger hashToIntegerRange(byte[] input, BigInteger q) { // RFC 6508 Section 5.1: Hashing to an Integer Range @@ -66,32 +32,15 @@ public static BigInteger hashToIntegerRange(byte[] input, BigInteger q) // h_i = hashfn(h_{i-1}) digest.update(h, 0, h.length); digest.doFinal(h, 0); - //System.out.println("h_"+i+":" +new String(Hex.encode(h))); // v_i = hashfn(h_i || A) digest.update(h, 0, h.length); digest.update(A, 0, A.length); byte[] v_i = new byte[digest.getDigestSize()]; digest.doFinal(v_i, 0); - //System.out.println("v_"+i+":" +new String(Hex.encode(v_i))); // Append v_i to v' v = v.shiftLeft(v_i.length * 8).add(new BigInteger(1, v_i)); } - //System.out.println("v:" +new String(Hex.encode(v.toByteArray()))); // Step 6: v = v' mod n return v.mod(q); } - - public static byte[] hash(byte[] data) - { - Digest digest = new SHA256Digest(); - byte[] rlt = new byte[digest.getDigestSize()]; - digest.update(data, 0, data.length); - digest.doFinal(rlt, 0); - return rlt; - } - - public static byte[] hash(ECPoint point) - { - return hash(point.getEncoded(false)); // Use uncompressed encoding - } } diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index 187adf0d09..7a2b0d5b64 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -7,13 +7,11 @@ import org.bouncycastle.crypto.SecretWithEncapsulation; import org.bouncycastle.crypto.kems.SAKKEKEMExtractor; import org.bouncycastle.crypto.kems.SAKKEKEMSGenerator; -import org.bouncycastle.crypto.kems.SAKKEUtils; import org.bouncycastle.crypto.params.SAKKEPrivateKeyParameters; import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.FixedSecureRandom; import org.bouncycastle.util.test.SimpleTest; @@ -27,18 +25,6 @@ public static void main(String[] args) { SAKKEKEMSTest test = new SAKKEKEMSTest(); test.performTest(); - // Expected Rb values -// BigInteger expectedRbx = new BigInteger("44E8AD44AB8592A6A5A3DDCA5CF896C718043606A01D650DEF37A01F37C228C332FC317354E2C274D4DAF8AD001054C7... -// BigInteger expectedRby = new BigInteger("557E134AD85BB1D4B9CE4F8BE4B08A12BABF55B1D6F1D7A638019EA28E15AB1C9F76375FDD1210D4F4351B9A009486B7... -// -// // Instantiate SAKKE KEM Generator -// SAKKEKEMSGenerator kem = new SAKKEKEMSGenerator(); -// EncapsulatedData encapsulatedData = kem.encapsulate(SSV); -// -// // Validate results -// boolean testPassed = expectedRbx.equals(encapsulatedData.getRbx()) && expectedRby.equals(encapsulatedData.getRby()); - - //System.out.println("SAKKE KEM Test " + (testPassed ? "PASSED" : "FAILED")); } @@ -78,7 +64,7 @@ public void performTest() // byte[] b = Hex.decode("323031312D30320074656C3A2B34343737303039303031323300"); - byte[] SSV = Hex.decode("123456789ABCDEF0123456789ABCDEF0"); + byte[] ssv = Hex.decode("123456789ABCDEF0123456789ABCDEF0"); byte[] expectedR = Hex.decode("13EE3E1B8DAC5DB168B1CEB32F0566A4C273693F78BAFFA2A2EE6A686E6BD90F8206CCAB84E7F" + "42ED39BD4FB131012ECCA2ECD2119414560C17CAB46B956A80F58A3302EB3E2C9A228FBA7ED34D8ACA2392DA1FFB0B17B2320AE09AAEDF" + "D0235F6FE0EB65337A63F9CC97728B8E5AD0460FADE144369AA5B2166213247712096"); @@ -137,17 +123,18 @@ public void performTest() g,// Order of the subgroup (from RFC 6509) BigInteger.ONE // Cofactor = 1 ); - SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new SecureRandom()); - SecretWithEncapsulation rlt = generator.generateEncapsulated(null); + SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(ssv), + new FixedSecureRandom.Data(b)}); + SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(random); + SecretWithEncapsulation rlt = generator.generateEncapsulated(null); ECPoint K_bS = curve.createPoint(kbx, kby); - SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(new BigInteger(b), K_bS, new SAKKEPublicKeyParameters(curve.createPoint(Zx, Zy)))); byte[] test = extractor.extractSecret(rlt.getEncapsulation()); - Assert.assertTrue(Arrays.areEqual(test, SSV)); + Assert.assertTrue(Arrays.areEqual(test, ssv)); } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java index c945e3154a..1025007493 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto.test; +import org.bouncycastle.crypto.kems.test.SAKKEKEMSTest; import org.bouncycastle.util.test.SimpleTest; import org.bouncycastle.util.test.Test; @@ -195,6 +196,7 @@ public class RegressionTest new SparkleTest(), new ISAPTest(), new ConcatenationKDFTest(), + new SAKKEKEMSTest(), }; public static void main(String[] args) From 26136939d1269327228ee92dfcf7f35d62c75b3c Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 6 Feb 2025 17:20:19 +1030 Subject: [PATCH 173/890] Set teh parameter settings of SAKKE --- .../crypto/kems/SAKKEKEMExtractor.java | 17 ++-- .../crypto/kems/SAKKEKEMSGenerator.java | 90 ++++--------------- .../params/SAKKEPrivateKeyParameters.java | 23 ++--- .../params/SAKKEPublicKeyParameters.java | 56 ++++++++---- .../crypto/kems/test/SAKKEKEMSTest.java | 26 +++++- 5 files changed, 97 insertions(+), 115 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java index 469cd1aa92..418ec767dd 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java @@ -19,21 +19,21 @@ public class SAKKEKEMExtractor private final BigInteger q; private final ECPoint P; private final ECPoint Z_S; - private final ECPoint K_bS; // Receiver's RSK + private final ECPoint K_bs; private final int n; // Security parameter - private final SAKKEPrivateKeyParameters privateKey; + private final BigInteger identifier; public SAKKEKEMExtractor(SAKKEPrivateKeyParameters privateKey) { - this.privateKey = privateKey; SAKKEPublicKeyParameters publicKey = privateKey.getPublicParams(); this.curve = publicKey.getCurve(); this.q = publicKey.getQ(); this.P = publicKey.getP(); - this.p = publicKey.getp(); + this.p = publicKey.getPrime(); this.Z_S = publicKey.getZ(); - this.K_bS = privateKey.getPrivatePoint(); + this.K_bs = privateKey.getRSK(); this.n = publicKey.getN(); + this.identifier = publicKey.getIdentifier(); } @Override @@ -46,16 +46,15 @@ public byte[] extractSecret(byte[] encapsulation) BigInteger H = new BigInteger(Arrays.copyOfRange(encapsulation, 257, 274)); // Step 2: Compute w = using pairing - BigInteger w = computePairing(R_bS, K_bS, p, q); - //System.out.println(new String(Hex.encode(w.toByteArray()))); - //BigInteger w = tatePairing(R_bS.getXCoord().toBigInteger(), R_bS.getYCoord().toBigInteger(), K_bS.getXCoord().toBigInteger(), K_bS.getYCoord().toBigInteger(), q, p); + BigInteger w = computePairing(R_bS, K_bs, p, q); + // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) BigInteger twoToN = BigInteger.ONE.shiftLeft(n); BigInteger mask = SAKKEUtils.hashToIntegerRange(w.toByteArray(), twoToN); BigInteger ssv = H.xor(mask); // Step 4: Compute r = HashToIntegerRange(SSV || b) - BigInteger b = privateKey.getB(); + BigInteger b = identifier; BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); // Step 5: Validate R_bS diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index 0d53ce0281..fe48251968 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -3,11 +3,11 @@ import org.bouncycastle.crypto.EncapsulatedSecretGenerator; import org.bouncycastle.crypto.SecretWithEncapsulation; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; -import org.bouncycastle.util.encoders.Hex; import java.math.BigInteger; import java.security.SecureRandom; @@ -15,44 +15,6 @@ public class SAKKEKEMSGenerator implements EncapsulatedSecretGenerator { - - private static final BigInteger p = new BigInteger( - "997ABB1F0A563FDA65C61198DAD0657A416C0CE19CB48261BE9AE358B3E01A2E" + - "F40AAB27E2FC0F1B228730D531A59CB0E791B39FF7C88A19356D27F4A666A6D0" + - "E26C6487326B4CD4512AC5CD65681CE1B6AFF4A831852A82A7CF3C521C3C09AA" + - "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 - ); - - private static final BigInteger q = new BigInteger( - "265EAEC7C2958FF69971846636B4195E905B0338672D20986FA6B8D62CF8068B" + - "BD02AAC9F8BF03C6C8A1CC354C69672C39E46CE7FDF222864D5B49FD2999A9B4" + - "389B1921CC9AD335144AB173595A07386DABFD2A0C614AA0A9F3CF14870F026A" + - "A7E535ABD5A5C7C7FF38FA08E2615F6C203177C42B1EB3A1D99B601EBFAA17FB", 16 - ); - - private static final BigInteger Px = new BigInteger( - "53FC09EE332C29AD0A7990053ED9B52A2B1A2FD60AEC69C698B2F204B6FF7CBF" + - "B5EDB6C0F6CE2308AB10DB9030B09E1043D5F22CDB9DFA55718BD9E7406CE890" + - "9760AF765DD5BCCB337C86548B72F2E1A702C3397A60DE74A7C1514DBA66910D" + - "D5CFB4CC80728D87EE9163A5B63F73EC80EC46C4967E0979880DC8ABEAE63895", 16 - ); - - private static final BigInteger Py = new BigInteger( - "0A8249063F6009F1F9F1F0533634A135D3E82016029906963D778D821E141178" + - "F5EA69F4654EC2B9E7F7F5E5F0DE55F66B598CCF9A140B2E416CFF0CA9E032B9" + - "70DAE117AD547C6CCAD696B5B7652FE0AC6F1E80164AA989492D979FC5A4D5F2" + - "13515AD7E9CB99A980BDAD5AD5BB4636ADB9B5706A67DCDE75573FD71BEF16D7", 16 - ); - - BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + - " C6A87BD1 FB94C41E 27FABE65 8E015A87\n" + - " 371E9474 4C96FEDA 449AE956 3F8BC446\n" + - " CBFDA85D 5D00EF57 7072DA8F 541721BE\n" + - " EE0FAED1 828EAB90 B99DFB01 38C78433\n" + - " 55DF0460 B4A9FD74 B4F1A32B CAFA1FFA\n" + - " D682C033 A7942BCC E3720F20 B9B7B040\n" + - " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); - private static final int n = 128; private final SecureRandom random; public SAKKEKEMSGenerator(SecureRandom random) @@ -63,54 +25,34 @@ public SAKKEKEMSGenerator(SecureRandom random) @Override public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey) { + SAKKEPublicKeyParameters keyParameters = (SAKKEPublicKeyParameters)recipientKey; + ECPoint Z = keyParameters.getZ(); + BigInteger b = keyParameters.getIdentifier(); + BigInteger p = keyParameters.getPrime(); + BigInteger q = keyParameters.getQ(); + BigInteger g = keyParameters.getG(); + int n = keyParameters.getN(); + ECCurve curve = keyParameters.getCurve(); + ECPoint P = keyParameters.getP(); + // 1. Generate random SSV in range [0, 2^n - 1] BigInteger ssv = new BigInteger(n, random); + // 2. Compute r = HashToIntegerRange(SSV || b, q) - BigInteger b = new BigInteger("323031312D30320074656C3A2B34343737303039303031323300", 16); //getRecipientId((SAKKEPublicKey)recipientKey); + BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); - //System.out.println(new String(Hex.encode(r.toByteArray()))); - ECCurve.Fp curve = new ECCurve.Fp( - p, // Prime p - BigInteger.valueOf(-3).mod(p), // a = -3 - BigInteger.ZERO, // , - g, // Order of the subgroup (from RFC 6509) - BigInteger.ONE // Cofactor = 1 - ); - ECPoint P = curve.createPoint(Px, Py); - - ECPoint Z = curve.createPoint( - new BigInteger("5958EF1B1679BF099B3A030DF255AA6A" + - "23C1D8F143D4D23F753E69BD27A832F3" + - "8CB4AD53DDEF4260B0FE8BB45C4C1FF5" + - "10EFFE300367A37B61F701D914AEF097" + - "24825FA0707D61A6DFF4FBD7273566CD" + - "DE352A0B04B7C16A78309BE640697DE7" + - "47613A5FC195E8B9F328852A579DB8F9" + - "9B1D0034479EA9C5595F47C4B2F54FF2", 16), // Px - new BigInteger("1508D37514DCF7A8E143A6058C09A6BF" + - "2C9858CA37C258065AE6BF7532BC8B5B" + - "63383866E0753C5AC0E72709F8445F2E" + - "6178E065857E0EDA10F68206B63505ED" + - "87E534FB2831FF957FB7DC619DAE6130" + - "1EEACC2FDA3680EA4999258A833CEA8F" + - "C67C6D19487FB449059F26CC8AAB655A" + - "B58B7CC796E24E9A394095754F5F8BAE", 16) // Py - ); + // 3. Compute R_(b,S) = [r]([b]P + Z_S) ECPoint bP = P.multiply(b).normalize(); ECPoint R_bS = bP.add(Z).multiply(r).normalize(); // [r]([b]P + Z_S) -// System.out.println("R_Bs x:" + new String(Hex.encode(R_bS.getXCoord().toBigInteger().toByteArray()))); -// System.out.println("R_Bs y:" + new String(Hex.encode(R_bS.getYCoord().toBigInteger().toByteArray()))); - // 4. Compute H = SSV XOR HashToIntegerRange( g^r, 2^n ) BigInteger[] v = fp2Exponentiate(p, BigInteger.ONE, g, r, curve); BigInteger g_r = v[1].multiply(v[0].modInverse(p)).mod(p); BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(n)); // 2^n - //System.out.println(new String(Hex.encode(mask.toByteArray()))); BigInteger H = ssv.xor(mask); //System.out.println(new String(Hex.encode(H.toByteArray()))); @@ -124,14 +66,12 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip } - // Helper method for F_p² exponentiation public static BigInteger[] fp2Exponentiate( BigInteger p, BigInteger pointX, BigInteger pointY, BigInteger n, - ECCurve.Fp curve - ) + ECCurve curve) { BigInteger[] result = new BigInteger[2]; diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java index 2c83f35ba3..c90a373cf0 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java @@ -7,30 +7,31 @@ public class SAKKEPrivateKeyParameters extends AsymmetricKeyParameter { - private final BigInteger b; // User's identity - private final ECPoint K; // Private key K_a private final SAKKEPublicKeyParameters publicParams; + private final BigInteger z; // KMS Public Key: Z = [z]P + private final ECPoint rsk; - public SAKKEPrivateKeyParameters(BigInteger b, ECPoint K, SAKKEPublicKeyParameters publicParams) + public SAKKEPrivateKeyParameters(BigInteger z, ECPoint rsk, SAKKEPublicKeyParameters publicParams) { super(true); - this.b = b; - this.K = K; + this.z = z; + this.rsk = rsk; this.publicParams = publicParams; } - public BigInteger getB() + public SAKKEPublicKeyParameters getPublicParams() { - return b; + return publicParams; } - public SAKKEPublicKeyParameters getPublicParams() + + public BigInteger getMasterSecret() { - return publicParams; + return z; } - public ECPoint getPrivatePoint() + public ECPoint getRSK() { - return K; + return rsk; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java index 66ce4e81ef..a062d766fc 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java @@ -2,6 +2,8 @@ import java.math.BigInteger; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.encoders.Hex; @@ -17,6 +19,13 @@ public class SAKKEPublicKeyParameters "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 ); + private static final BigInteger q = new BigInteger( + "265EAEC7C2958FF69971846636B4195E905B0338672D20986FA6B8D62CF8068B" + + "BD02AAC9F8BF03C6C8A1CC354C69672C39E46CE7FDF222864D5B49FD2999A9B4" + + "389B1921CC9AD335144AB173595A07386DABFD2A0C614AA0A9F3CF14870F026A" + + "A7E535ABD5A5C7C7FF38FA08E2615F6C203177C42B1EB3A1D99B601EBFAA17FB", 16 + ); + private static final BigInteger Px = new BigInteger( "53FC09EE332C29AD0A7990053ED9B52A2B1A2FD60AEC69C698B2F204B6FF7CBF" + "B5EDB6C0F6CE2308AB10DB9030B09E1043D5F22CDB9DFA55718BD9E7406CE890" + @@ -31,7 +40,8 @@ public class SAKKEPublicKeyParameters "13515AD7E9CB99A980BDAD5AD5BB4636ADB9B5706A67DCDE75573FD71BEF16D7", 16 ); - + // g = + // < , > is Tate-Lichtenbaum Pairing private static final BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + " C6A87BD1 FB94C41E 27FABE65 8E015A87\n" + " 371E9474 4C96FEDA 449AE956 3F8BC446\n" + @@ -41,13 +51,6 @@ public class SAKKEPublicKeyParameters " D682C033 A7942BCC E3720F20 B9B7B040\n" + " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); - private static final BigInteger q = new BigInteger( - "265EAEC7C2958FF69971846636B4195E905B0338672D20986FA6B8D62CF8068B" + - "BD02AAC9F8BF03C6C8A1CC354C69672C39E46CE7FDF222864D5B49FD2999A9B4" + - "389B1921CC9AD335144AB173595A07386DABFD2A0C614AA0A9F3CF14870F026A" + - "A7E535ABD5A5C7C7FF38FA08E2615F6C203177C42B1EB3A1D99B601EBFAA17FB", 16 - ); - private static final ECCurve.Fp curve = new ECCurve.Fp( p, // Prime p BigInteger.valueOf(-3).mod(p), // a = -3 @@ -56,18 +59,34 @@ public class SAKKEPublicKeyParameters BigInteger.ONE // Cofactor = 1 ); + private static final ECPoint P = curve.createPoint(Px, Py); - private final ECPoint Z; // KMS Public Key: Z = [z]P + private final ECPoint Z; + + private final BigInteger identifier; // User's identity private static final int n = 128; // SSV bit length - public SAKKEPublicKeyParameters(ECPoint Z) + private final Digest digest = new SHA256Digest(); + + public SAKKEPublicKeyParameters(BigInteger identifier, ECPoint Z) { super(false); + this.identifier = identifier; this.Z = Z; } // Getters + public BigInteger getIdentifier() + { + return identifier; + } + + public ECPoint getZ() + { + return Z; + } + public ECCurve getCurve() { return curve; @@ -78,12 +97,7 @@ public ECPoint getP() return P; } - public ECPoint getZ() - { - return Z; - } - - public BigInteger getp() + public BigInteger getPrime() { return p; } @@ -97,4 +111,14 @@ public int getN() { return n; } + + public Digest getDigest() + { + return digest; + } + + public BigInteger getG() + { + return g; + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index 7a2b0d5b64..8dd350c673 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -38,6 +38,20 @@ public String getName() public void performTest() throws Exception { + + final BigInteger Px = new BigInteger( + "53FC09EE332C29AD0A7990053ED9B52A2B1A2FD60AEC69C698B2F204B6FF7CBF" + + "B5EDB6C0F6CE2308AB10DB9030B09E1043D5F22CDB9DFA55718BD9E7406CE890" + + "9760AF765DD5BCCB337C86548B72F2E1A702C3397A60DE74A7C1514DBA66910D" + + "D5CFB4CC80728D87EE9163A5B63F73EC80EC46C4967E0979880DC8ABEAE63895", 16 + ); + + final BigInteger Py = new BigInteger( + "0A8249063F6009F1F9F1F0533634A135D3E82016029906963D778D821E141178" + + "F5EA69F4654EC2B9E7F7F5E5F0DE55F66B598CCF9A140B2E416CFF0CA9E032B9" + + "70DAE117AD547C6CCAD696B5B7652FE0AC6F1E80164AA989492D979FC5A4D5F2" + + "13515AD7E9CB99A980BDAD5AD5BB4636ADB9B5706A67DCDE75573FD71BEF16D7", 16 + ); BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068" + " C6A87BD1 FB94C41E 27FABE65 8E015A87" + " 371E9474 4C96FEDA 449AE956 3F8BC446" + @@ -127,14 +141,18 @@ public void performTest() SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(ssv), new FixedSecureRandom.Data(b)}); SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(random); - SecretWithEncapsulation rlt = generator.generateEncapsulated(null); + SecretWithEncapsulation rlt = generator.generateEncapsulated(new SAKKEPublicKeyParameters(new BigInteger(b), curve.createPoint(Zx, Zy))); + ECPoint P = curve.createPoint(Px, Py); + + BigInteger computed_g2 = SAKKEKEMExtractor.computePairing(P, P, p, q); + Assert.assertTrue(computed_g2.equals(g)); ECPoint K_bS = curve.createPoint(kbx, kby); - SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(new BigInteger(b), K_bS, - new SAKKEPublicKeyParameters(curve.createPoint(Zx, Zy)))); + + SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(z, K_bS, + new SAKKEPublicKeyParameters(new BigInteger(b), curve.createPoint(Zx, Zy)))); byte[] test = extractor.extractSecret(rlt.getEncapsulation()); Assert.assertTrue(Arrays.areEqual(test, ssv)); - } } From 68c38165d2b0ced84a80fe4f87bbf28942847fcc Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 7 Feb 2025 14:10:33 +1030 Subject: [PATCH 174/890] Add javadoc and add random tests. --- .../crypto/kems/SAKKEKEMExtractor.java | 133 +++++++++++----- .../crypto/kems/SAKKEKEMSGenerator.java | 143 +++++++++++++----- .../bouncycastle/crypto/kems/SAKKEUtils.java | 46 ------ .../params/SAKKEPrivateKeyParameters.java | 64 ++++++-- .../params/SAKKEPublicKeyParameters.java | 102 +++++++++++-- .../crypto/kems/test/SAKKEKEMSTest.java | 89 ++++++----- 6 files changed, 398 insertions(+), 179 deletions(-) delete mode 100644 core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java index 418ec767dd..8e2307e153 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMExtractor.java @@ -2,6 +2,7 @@ import java.math.BigInteger; +import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.EncapsulatedSecretExtractor; import org.bouncycastle.crypto.params.SAKKEPrivateKeyParameters; import org.bouncycastle.crypto.params.SAKKEPublicKeyParameters; @@ -10,7 +11,22 @@ import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; - +/** + * Implements the receiver side of the SAKKE (Sakai-Kasahara Key Encryption) protocol + * as defined in RFC 6508. This class extracts the shared secret value (SSV) from + * encapsulated data using the receiver's private key. + *

+ * The extraction process follows these steps (RFC 6508, Section 6.2.2): + *

    + *
  1. Parse encapsulated data into R_(b,S) and H
  2. + *
  3. Compute pairing result w = <R_(b,S), K_(b,S)>
  4. + *
  5. Recover SSV via SSV = H XOR HashToIntegerRange(w, 2^n)
  6. + *
  7. Validate R_(b,S) by recomputing it with derived parameters
  8. + *
+ *

+ * + * @see Sakai-Kasahara Key Encryption (SAKKE) + */ public class SAKKEKEMExtractor implements EncapsulatedSecretExtractor { @@ -22,65 +38,88 @@ public class SAKKEKEMExtractor private final ECPoint K_bs; private final int n; // Security parameter private final BigInteger identifier; - + private final Digest digest; + + /** + * Initializes the extractor with cryptographic parameters from the receiver's private key. + * + * @param privateKey The receiver's private key containing public parameters + * (curve, prime, generator, etc.) and the Receiver Secret Key (RSK). + * Must not be {@code null}. + */ public SAKKEKEMExtractor(SAKKEPrivateKeyParameters privateKey) { SAKKEPublicKeyParameters publicKey = privateKey.getPublicParams(); this.curve = publicKey.getCurve(); this.q = publicKey.getQ(); - this.P = publicKey.getP(); + this.P = publicKey.getPoint(); this.p = publicKey.getPrime(); this.Z_S = publicKey.getZ(); - this.K_bs = privateKey.getRSK(); - this.n = publicKey.getN(); this.identifier = publicKey.getIdentifier(); + this.K_bs = P.multiply(this.identifier.add(privateKey.getMasterSecret()).modInverse(q)).normalize(); + this.n = publicKey.getN(); + + this.digest = publicKey.getDigest(); } + /** + * Extracts the shared secret value (SSV) from encapsulated data as per RFC 6508. + * + * @param encapsulation The encapsulated data containing: + *
    + *
  • R_(b,S): Elliptic curve point (uncompressed format, 257 bytes)
  • + *
  • H: Integer value (n/8 bytes)
  • + *
+ * @return The extracted SSV as a byte array. + * @throws IllegalStateException If: Validation of R_(b,S) fails + */ @Override public byte[] extractSecret(byte[] encapsulation) { - try - { - // Step 1: Parse Encapsulated Data (R_bS, H) - ECPoint R_bS = curve.decodePoint(Arrays.copyOfRange(encapsulation, 0, 257)); - BigInteger H = new BigInteger(Arrays.copyOfRange(encapsulation, 257, 274)); - - // Step 2: Compute w = using pairing - BigInteger w = computePairing(R_bS, K_bs, p, q); - - // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) - BigInteger twoToN = BigInteger.ONE.shiftLeft(n); - BigInteger mask = SAKKEUtils.hashToIntegerRange(w.toByteArray(), twoToN); - BigInteger ssv = H.xor(mask); - - // Step 4: Compute r = HashToIntegerRange(SSV || b) - BigInteger b = identifier; - BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); - - // Step 5: Validate R_bS - ECPoint bP = P.multiply(b).normalize(); - ECPoint Test = bP.add(Z_S).multiply(r).normalize(); - if (!R_bS.equals(Test)) - { - throw new IllegalStateException("Validation of R_bS failed"); - } - - return BigIntegers.asUnsignedByteArray(n / 8, ssv); - } - catch (Exception e) + // Step 1: Parse Encapsulated Data (R_bS, H) + ECPoint R_bS = curve.decodePoint(Arrays.copyOfRange(encapsulation, 0, 257)); + BigInteger H = BigIntegers.fromUnsignedByteArray(encapsulation, 257, 16); + + // Step 2: Compute w = using pairing + BigInteger w = computePairing(R_bS, K_bs, p, q); + + // Step 3: Compute SSV = H XOR HashToIntegerRange(w, 2^n) + BigInteger twoToN = BigInteger.ONE.shiftLeft(n); + BigInteger mask = SAKKEKEMSGenerator.hashToIntegerRange(w.toByteArray(), twoToN, digest); + BigInteger ssv = H.xor(mask).mod(p); + + // Step 4: Compute r = HashToIntegerRange(SSV || b) + BigInteger b = identifier; + BigInteger r = SAKKEKEMSGenerator.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q, digest); + + // Step 5: Validate R_bS + ECPoint bP = P.multiply(b).normalize(); + ECPoint Test = bP.add(Z_S).multiply(r).normalize(); + if (!R_bS.equals(Test)) { - throw new IllegalStateException("SAKKE extraction failed: " + e.getMessage()); + throw new IllegalStateException("Validation of R_bS failed"); } + + return BigIntegers.asUnsignedByteArray(n / 8, ssv); } @Override public int getEncapsulationLength() { - return 0; + return 273; //257 (length of ECPoint) + 16 (length of Hash) } - - public static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) + /** + * Computes the Tate-Lichtenbaum pairing <R, Q> for SAKKE validation. + * Follows the pairing algorithm described in RFC 6508, Section 3.2. + * + * @param R First pairing input (elliptic curve point) + * @param Q Second pairing input (elliptic curve point) + * @param p Prime field characteristic + * @param q Subgroup order + * @return Pairing result in PF_p[q], represented as a field element + */ + static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigInteger q) { // v = (1,0) in F_p^2 BigInteger[] v = new BigInteger[]{BigInteger.ONE, BigInteger.ZERO}; @@ -133,6 +172,16 @@ public static BigInteger computePairing(ECPoint R, ECPoint Q, BigInteger p, BigI return v[1].multiply(v[0].modInverse(p)).mod(p); } + /** + * Performs multiplication in F_p^2 field. + * + * @param x_real Real component of first operand + * @param x_imag Imaginary component of first operand + * @param y_real Real component of second operand + * @param y_imag Imaginary component of second operand + * @param p Prime field characteristic + * @return Result of multiplication in F_p^2 as [real, imaginary] array + */ static BigInteger[] fp2Multiply(BigInteger x_real, BigInteger x_imag, BigInteger y_real, BigInteger y_imag, BigInteger p) { return new BigInteger[]{ @@ -141,6 +190,14 @@ static BigInteger[] fp2Multiply(BigInteger x_real, BigInteger x_imag, BigInteger }; } + /** + * Computes squaring operation in F_p^2 field. + * + * @param currentX Real component of input + * @param currentY Imaginary component of input + * @param p Prime field characteristic + * @return Squared result in F_p^2 as [newX, newY] array + */ static BigInteger[] fp2PointSquare(BigInteger currentX, BigInteger currentY, BigInteger p) { BigInteger xPlusY = currentX.add(currentY).mod(p); diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index fe48251968..9bb956d4c1 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto.kems; +import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.EncapsulatedSecretGenerator; import org.bouncycastle.crypto.SecretWithEncapsulation; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; @@ -12,19 +13,59 @@ import java.math.BigInteger; import java.security.SecureRandom; +/** + * This class implements the SAKKE (Sakai-Kasahara Key Encryption) Key Encapsulation Mechanism + * as defined in RFC 6508. It generates an encapsulated shared secret value (SSV) using + * Identity-Based Encryption (IBE) for secure transmission from a Sender to a Receiver. + *

+ * The algorithm follows these steps (as per RFC 6508, Section 6.2.1): + *

    + *
  1. Generate a random SSV in the range [0, 2^n - 1].
  2. + *
  3. Compute r = HashToIntegerRange(SSV || b, q).
  4. + *
  5. Compute R_(b,S) = [r]([b]P + Z_S) on the elliptic curve.
  6. + *
  7. Compute H = SSV XOR HashToIntegerRange(g^r, 2^n).
  8. + *
  9. Encode the encapsulated data (R_(b,S), H).
  10. + *
+ *

+ * + * @see RFC 6508: Sakai-Kasahara Key Encryption (SAKKE) + */ public class SAKKEKEMSGenerator implements EncapsulatedSecretGenerator { private final SecureRandom random; + /** + * Constructs a SAKKEKEMSGenerator with the specified source of randomness. + * + * @param random a {@link SecureRandom} instance for generating cryptographically secure random values. + * Must not be {@code null}. + */ public SAKKEKEMSGenerator(SecureRandom random) { this.random = random; } + /** + * Generates an encapsulated shared secret value (SSV) using the recipient's public key parameters + * as specified in RFC 6508, Section 6.2.1. + *

+ * This method performs the following operations: + *

    + *
  • Derives cryptographic parameters from the recipient's public key.
  • + *
  • Generates a random SSV and computes the encapsulation components (R_(b,S), H).
  • + *
  • Encodes the encapsulated data as specified in RFC 6508, Section 4.
  • + *
+ *

+ * + * @param recipientKey the recipient's public key parameters. Must be an instance of + * {@link SAKKEPublicKeyParameters}. Must not be {@code null}. + * @return a {@link SecretWithEncapsulation} containing the SSV and the encapsulated data. + */ @Override public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey) { + // Extract public parameters from the recipient's key SAKKEPublicKeyParameters keyParameters = (SAKKEPublicKeyParameters)recipientKey; ECPoint Z = keyParameters.getZ(); BigInteger b = keyParameters.getIdentifier(); @@ -33,54 +74,31 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip BigInteger g = keyParameters.getG(); int n = keyParameters.getN(); ECCurve curve = keyParameters.getCurve(); - ECPoint P = keyParameters.getP(); + ECPoint P = keyParameters.getPoint(); + Digest digest = keyParameters.getDigest(); // 1. Generate random SSV in range [0, 2^n - 1] BigInteger ssv = new BigInteger(n, random); - // 2. Compute r = HashToIntegerRange(SSV || b, q) - - BigInteger r = SAKKEUtils.hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q); + BigInteger r = hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q, digest); // 3. Compute R_(b,S) = [r]([b]P + Z_S) ECPoint bP = P.multiply(b).normalize(); - ECPoint R_bS = bP.add(Z).multiply(r).normalize(); // [r]([b]P + Z_S) + ECPoint R_bS = bP.add(Z).multiply(r).normalize(); // 4. Compute H = SSV XOR HashToIntegerRange( g^r, 2^n ) - BigInteger[] v = fp2Exponentiate(p, BigInteger.ONE, g, r, curve); - BigInteger g_r = v[1].multiply(v[0].modInverse(p)).mod(p); - - BigInteger mask = SAKKEUtils.hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(n)); // 2^n - - BigInteger H = ssv.xor(mask); - //System.out.println(new String(Hex.encode(H.toByteArray()))); - // 5. Encode encapsulated data (R_bS, H) - byte[] encapsulated = Arrays.concatenate(R_bS.getEncoded(false), H.toByteArray()); - - return new SecretWithEncapsulationImpl( - BigIntegers.asUnsignedByteArray(n / 8, ssv), // Output SSV as key material - encapsulated - ); - } - - - public static BigInteger[] fp2Exponentiate( - BigInteger p, - BigInteger pointX, - BigInteger pointY, - BigInteger n, - ECCurve curve) - { - BigInteger[] result = new BigInteger[2]; + BigInteger pointX = BigInteger.ONE; + BigInteger pointY = g; + BigInteger[] v = new BigInteger[2]; // Initialize result with the original point - BigInteger currentX = pointX; - BigInteger currentY = pointY; + BigInteger currentX = BigInteger.ONE; + BigInteger currentY = g; ECPoint current = curve.createPoint(currentX, currentY); - int numBits = n.bitLength(); + int numBits = r.bitLength(); BigInteger[] rlt; // Process bits from MSB-1 down to 0 for (int i = numBits - 2; i >= 0; i--) @@ -91,7 +109,7 @@ public static BigInteger[] fp2Exponentiate( currentX = rlt[0]; currentY = rlt[1]; // Multiply if bit is set - if (n.testBit(i)) + if (r.testBit(i)) { rlt = SAKKEKEMExtractor.fp2Multiply(currentX, currentY, pointX, pointY, p); @@ -100,8 +118,59 @@ public static BigInteger[] fp2Exponentiate( } } - result[0] = currentX; - result[1] = currentY; - return result; + v[0] = currentX; + v[1] = currentY; + BigInteger g_r = v[1].multiply(v[0].modInverse(p)).mod(p); + + BigInteger mask = hashToIntegerRange(g_r.toByteArray(), BigInteger.ONE.shiftLeft(n), digest); // 2^n + + BigInteger H = ssv.xor(mask); + // 5. Encode encapsulated data (R_bS, H) +// byte[] encapsulated = Arrays.concatenate(new byte[]{(byte)0x04}, +// BigIntegers.asUnsignedByteArray(n, R_bS.getXCoord().toBigInteger()), +// BigIntegers.asUnsignedByteArray(n, R_bS.getYCoord().toBigInteger()), +// BigIntegers.asUnsignedByteArray(16, H)); + byte[] encapsulated = Arrays.concatenate(R_bS.getEncoded(false), BigIntegers.asUnsignedByteArray(16, H)); + + return new SecretWithEncapsulationImpl( + BigIntegers.asUnsignedByteArray(n / 8, ssv), // Output SSV as key material + encapsulated + ); + } + + static BigInteger hashToIntegerRange(byte[] input, BigInteger q, Digest digest) + { + // RFC 6508 Section 5.1: Hashing to an Integer Range + byte[] hash = new byte[digest.getDigestSize()]; + + // Step 1: Compute A = hashfn(s) + digest.update(input, 0, input.length); + digest.doFinal(hash, 0); + byte[] A = hash.clone(); + + // Step 2: Initialize h_0 to all-zero bytes of hashlen size + byte[] h = new byte[digest.getDigestSize()]; + + // Step 3: Compute l = Ceiling(lg(n)/hashlen) + int l = q.bitLength() >> 8; + + BigInteger v = BigInteger.ZERO; + + // Step 4: Compute h_i and v_i + for (int i = 0; i <= l; i++) + { + // h_i = hashfn(h_{i-1}) + digest.update(h, 0, h.length); + digest.doFinal(h, 0); + // v_i = hashfn(h_i || A) + digest.update(h, 0, h.length); + digest.update(A, 0, A.length); + byte[] v_i = new byte[digest.getDigestSize()]; + digest.doFinal(v_i, 0); + // Append v_i to v' + v = v.shiftLeft(v_i.length * 8).add(new BigInteger(1, v_i)); + } + // Step 6: v = v' mod n + return v.mod(q); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java deleted file mode 100644 index 891f2ae878..0000000000 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEUtils.java +++ /dev/null @@ -1,46 +0,0 @@ -package org.bouncycastle.crypto.kems; - -import java.math.BigInteger; - -import org.bouncycastle.crypto.digests.SHA256Digest; - -public class SAKKEUtils -{ - - public static BigInteger hashToIntegerRange(byte[] input, BigInteger q) - { - // RFC 6508 Section 5.1: Hashing to an Integer Range - SHA256Digest digest = new SHA256Digest(); - byte[] hash = new byte[digest.getDigestSize()]; - - // Step 1: Compute A = hashfn(s) - digest.update(input, 0, input.length); - digest.doFinal(hash, 0); - byte[] A = hash.clone(); - - // Step 2: Initialize h_0 to all-zero bytes of hashlen size - byte[] h = new byte[digest.getDigestSize()]; - - // Step 3: Compute l = Ceiling(lg(n)/hashlen) - int l = q.bitLength() >> 8; - - BigInteger v = BigInteger.ZERO; - - // Step 4: Compute h_i and v_i - for (int i = 0; i <= l; i++) - { - // h_i = hashfn(h_{i-1}) - digest.update(h, 0, h.length); - digest.doFinal(h, 0); - // v_i = hashfn(h_i || A) - digest.update(h, 0, h.length); - digest.update(A, 0, A.length); - byte[] v_i = new byte[digest.getDigestSize()]; - digest.doFinal(v_i, 0); - // Append v_i to v' - v = v.shiftLeft(v_i.length * 8).add(new BigInteger(1, v_i)); - } - // Step 6: v = v' mod n - return v.mod(q); - } -} diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java index c90a373cf0..d5a7539045 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java @@ -1,37 +1,81 @@ package org.bouncycastle.crypto.params; import java.math.BigInteger; +import java.security.SecureRandom; -import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.BigIntegers; +/** + * Represents a private key for the Sakai-Kasahara Key Encryption (SAKKE) scheme, as defined in RFC 6508. + * + *

SAKKE is an identity-based public key encryption scheme designed for one-pass key establishment. + * It is used in MIKEY-SAKKE for secure communication key distribution.

+ * + *

This class generates and manages a SAKKE private key, which consists of a randomly generated + * scalar {@code z}. The corresponding public key is computed as {@code Z = [z]P}, where {@code P} + * is a publicly known generator point on the elliptic curve.

+ * + *

The private key is used to derive the master secret in the key exchange process.

+ * + * @see RFC 6508: Sakai-Kasahara Key Encryption (SAKKE) + */ public class SAKKEPrivateKeyParameters extends AsymmetricKeyParameter { + private static final BigInteger qMinOne = SAKKEPublicKeyParameters.q.subtract(BigInteger.ONE); + /** The associated public key parameters. */ private final SAKKEPublicKeyParameters publicParams; + /** The private key scalar (master secret). */ private final BigInteger z; // KMS Public Key: Z = [z]P - private final ECPoint rsk; - public SAKKEPrivateKeyParameters(BigInteger z, ECPoint rsk, SAKKEPublicKeyParameters publicParams) + /** + * Constructs a SAKKE private key with a given private value and associated public parameters. + * + * @param z The private key scalar. + * @param publicParams The associated public key parameters. + */ + public SAKKEPrivateKeyParameters(BigInteger z, SAKKEPublicKeyParameters publicParams) { super(true); this.z = z; - this.rsk = rsk; this.publicParams = publicParams; } + /** + * Generates a random SAKKE private key and its corresponding public key. + * + *

The private key scalar {@code z} is chosen randomly in the range [2, q-1], + * where {@code q} is the order of the subgroup. The public key is computed as + * {@code Z = [z]P}, where {@code P} is the public generator.

+ * + * @param random A cryptographic random number generator. + */ + public SAKKEPrivateKeyParameters(SecureRandom random) + { + super(true); + this.z = BigIntegers.createRandomInRange(BigIntegers.TWO, qMinOne, random); + BigInteger identifier = BigIntegers.createRandomInRange(BigIntegers.TWO, qMinOne, random); + this.publicParams = new SAKKEPublicKeyParameters(identifier, + SAKKEPublicKeyParameters.P.multiply(z).normalize()); + } + + /** + * Retrieves the public key parameters associated with this private key. + * + * @return The corresponding SAKKE public key parameters. + */ public SAKKEPublicKeyParameters getPublicParams() { return publicParams; } - + /** + * Retrieves the private key scalar (master secret). + * + * @return The private key scalar {@code z}. + */ public BigInteger getMasterSecret() { return z; } - - public ECPoint getRSK() - { - return rsk; - } } diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java index a062d766fc..2380efe143 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java @@ -8,18 +8,50 @@ import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.encoders.Hex; +/** + * Represents the public parameters for the SAKKE (Sakai-Kasahara Key Encryption) scheme + * as defined in RFC 6508. This class encapsulates the cryptographic domain parameters + * and public key components required for SAKKE operations. + *

+ * Contains the following public parameters (RFC 6508, Section 2.3): + *

    + *
  • Prime modulus {@code p} defining the field F_p
  • + *
  • Subgroup order {@code q} (divides p+1)
  • + *
  • Base point {@code P} on the elliptic curve E(F_p)
  • + *
  • Pairing result {@code g = }
  • + *
  • KMS Public Key {@code Z_S = [z_S]P}
  • + *
  • Security parameter {@code n} (SSV bit length)
  • + *
  • User Identifier
  • + *
  • Elliptic curve parameters (a = -3, b = 0)
  • + *
+ *

+ *

+ * The predefined parameters in this implementation correspond to the 128-bit security + * level example from RFC 6509 Appendix A. + *

+ * + * @see RFC 6508: Sakai-Kasahara Key Encryption + * @see RFC 6509: MIKEY-SAKKE + */ public class SAKKEPublicKeyParameters extends AsymmetricKeyParameter { - // Base point - private static final BigInteger p = new BigInteger( + /** + * Prime modulus p defining the finite field F_p (RFC 6508, Section 2.1). + * Value from RFC 6509 Appendix A. + */ + static final BigInteger p = new BigInteger( "997ABB1F0A563FDA65C61198DAD0657A416C0CE19CB48261BE9AE358B3E01A2E" + "F40AAB27E2FC0F1B228730D531A59CB0E791B39FF7C88A19356D27F4A666A6D0" + "E26C6487326B4CD4512AC5CD65681CE1B6AFF4A831852A82A7CF3C521C3C09AA" + "9F94D6AF56971F1FFCE3E82389857DB080C5DF10AC7ACE87666D807AFEA85FEB", 16 ); - private static final BigInteger q = new BigInteger( + /** + * Subgroup order q (divides p+1) (RFC 6508, Section 2.1). + * Value from RFC 6509 Appendix A. + */ + static final BigInteger q = new BigInteger( "265EAEC7C2958FF69971846636B4195E905B0338672D20986FA6B8D62CF8068B" + "BD02AAC9F8BF03C6C8A1CC354C69672C39E46CE7FDF222864D5B49FD2999A9B4" + "389B1921CC9AD335144AB173595A07386DABFD2A0C614AA0A9F3CF14870F026A" + @@ -33,6 +65,7 @@ public class SAKKEPublicKeyParameters "D5CFB4CC80728D87EE9163A5B63F73EC80EC46C4967E0979880DC8ABEAE63895", 16 ); + private static final BigInteger Py = new BigInteger( "0A8249063F6009F1F9F1F0533634A135D3E82016029906963D778D821E141178" + "F5EA69F4654EC2B9E7F7F5E5F0DE55F66B598CCF9A140B2E416CFF0CA9E032B9" + @@ -40,8 +73,10 @@ public class SAKKEPublicKeyParameters "13515AD7E9CB99A980BDAD5AD5BB4636ADB9B5706A67DCDE75573FD71BEF16D7", 16 ); - // g = - // < , > is Tate-Lichtenbaum Pairing + /** + * Pairing result g = computed using the Tate-Lichtenbaum pairing + * (RFC 6508, Section 3.2). Value from RFC 6509 Appendix A. + */ private static final BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + " C6A87BD1 FB94C41E 27FABE65 8E015A87\n" + " 371E9474 4C96FEDA 449AE956 3F8BC446\n" + @@ -51,6 +86,10 @@ public class SAKKEPublicKeyParameters " D682C033 A7942BCC E3720F20 B9B7B040\n" + " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); + /** + * The elliptic curve E: y² = x³ - 3x over F_p (RFC 6508, Section 3.1). + * Uses parameters from RFC 6509 Appendix A. + */ private static final ECCurve.Fp curve = new ECCurve.Fp( p, // Prime p BigInteger.valueOf(-3).mod(p), // a = -3 @@ -59,16 +98,27 @@ public class SAKKEPublicKeyParameters BigInteger.ONE // Cofactor = 1 ); - - private static final ECPoint P = curve.createPoint(Px, Py); + /** + * Base point P on the elliptic curve E(F_p) (RFC 6508, Section 3.1). + * Coordinates from RFC 6509 Appendix A. + */ + static final ECPoint P = curve.createPoint(Px, Py); + /** KMS Public Key Z_S = [z_S]P (RFC 6508, Section 2.2) */ private final ECPoint Z; - + /** User's Identifier (RFC 6508, Section 2.2) */ private final BigInteger identifier; // User's identity - + /** Security parameter: SSV bit length (n = 128 bits) */ private static final int n = 128; // SSV bit length - + /** Hash function (SHA-256) used in SAKKE operations */ private final Digest digest = new SHA256Digest(); - + /** + * Constructs SAKKE public key parameters with the specified identifier and KMS Public Key. + * + * @param identifier The user's identifier as defined in RFC 6508, Section 2.2. + * Must be a valid integer in [2, q-1]. + * @param Z The KMS Public Key Z_S = [z_S]P (RFC 6508, Section 2.2). + * Must be a valid point on the curve E(F_p). + */ public SAKKEPublicKeyParameters(BigInteger identifier, ECPoint Z) { super(false); @@ -76,47 +126,73 @@ public SAKKEPublicKeyParameters(BigInteger identifier, ECPoint Z) this.Z = Z; } - // Getters + /** + * @return The user's identifier (RFC 6508, Section 2.2) + */ public BigInteger getIdentifier() { return identifier; } + /** + * @return The KMS Public Key Z_S = [z_S]P (RFC 6508, Section 2.2) + */ public ECPoint getZ() { return Z; } + /** + * @return The elliptic curve E(F_p) with parameters from RFC 6509 Appendix A + */ public ECCurve getCurve() { return curve; } - public ECPoint getP() + /** + * @return The base point P on E(F_p) (RFC 6508, Section 3.1) + */ + public ECPoint getPoint() { return P; } + /** + * @return Prime modulus p defining the field F_p (RFC 6508, Section 2.1) + */ public BigInteger getPrime() { return p; } + /** + * @return Subgroup order q (divides p+1) (RFC 6508, Section 2.1) + */ public BigInteger getQ() { return q; } + /** + * @return Security parameter n (SSV bit length = 128 bits) + */ public int getN() { return n; } + /** + * @return The hash function (SHA-256) used in SAKKE operations + */ public Digest getDigest() { return digest; } + /** + * @return The pairing result g = (RFC 6508, Section 3.2) + */ public BigInteger getG() { return g; diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index 8dd350c673..492c8d1e4c 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -38,7 +38,15 @@ public String getName() public void performTest() throws Exception { + testTestVector(); + for (int i = 0; i < 100; ++i) + { + testRandom(); + } + } + private void testTestVector() + { final BigInteger Px = new BigInteger( "53FC09EE332C29AD0A7990053ED9B52A2B1A2FD60AEC69C698B2F204B6FF7CBF" + "B5EDB6C0F6CE2308AB10DB9030B09E1043D5F22CDB9DFA55718BD9E7406CE890" + @@ -60,7 +68,7 @@ public void performTest() " 55DF0460 B4A9FD74 B4F1A32B CAFA1FFA" + " D682C033 A7942BCC E3720F20 B9B7B040" + " 3C8CAE87 B7A0042A CDE0FAB3 6461EA46")); - BigInteger z = new BigInteger(Hex.decode("AFF429D35F84B110D094803B3595A6E2998BC99F")); + BigInteger z = new BigInteger("AFF429D35F84B110D094803B3595A6E2998BC99F", 16); BigInteger Zx = new BigInteger(Hex.decode("5958EF1B1679BF099B3A030DF255AA6A23C1D8F143D4D23F753E69BD27A832F38CB4AD53DDEF" + "4260B0FE8BB45C4C1FF510EFFE300367A37B61F701D914AEF09724825FA0707D61A6DFF4FBD7273566CDDE352A0B04B7C16A78309BE" + "640697DE747613A5FC195E8B9F328852A579DB8F99B1D0034479EA9C5595F47C4B2F54FF2")); @@ -100,30 +108,30 @@ public void performTest() "2884318A33D1A42ADF5E33CC5800280B" + "28356497F87135BAB9612A1726042440" + "9AC15FEE996B744C332151235DECB0F5", 16); - BigInteger w = new BigInteger(Hex.decode("7D2A8438 E6291C64 9B6579EB 3B79EAE9" + - "48B1DE9E 5F7D1F40 70A08F8D B6B3C515" + - "6F2201AF FBB5CB9D 82AA3EC0 D0398B89" + - "ABC78A13 A760C0BF 3F77E63D 0DF3F1A3" + - "41A41B88 11DF197F D6CD0F00 3125606F" + - "4F109F40 0F7292A1 0D255E3C 0EBCCB42" + - "53FB182C 68F09CF6 CD9C4A53 DA6C74AD" + - "007AF36B 8BCA979D 5895E282 F483FCD6")); - BigInteger Rbx = new BigInteger(Hex.decode("44E8AD44 AB8592A6 A5A3DDCA 5CF896C7" + - "18043606 A01D650D EF37A01F 37C228C3" + - "32FC3173 54E2C274 D4DAF8AD 001054C7" + - "6CE57971 C6F4486D 57230432 61C506EB" + - "F5BE438F 53DE04F0 67C776E0 DD3B71A6" + - "29013328 3725A532 F21AF145 126DC1D7" + - "77ECC27B E50835BD 28098B8A 73D9F801" + - "D893793A 41FF5C49 B87E79F2 BE4D56CE")); - BigInteger Rby = new BigInteger(Hex.decode("557E134A D85BB1D4 B9CE4F8B E4B08A12" + - "BABF55B1 D6F1D7A6 38019EA2 8E15AB1C" + - "9F76375F DD1210D4 F4351B9A 009486B7" + - "F3ED46C9 65DED2D8 0DADE4F3 8C6721D5" + - "2C3AD103 A10EBD29 59248B4E F006836B" + - "F097448E 6107C9ED EE9FB704 823DF199" + - "F832C905 AE45F8A2 47A072D8 EF729EAB" + - "C5E27574 B07739B3 4BE74A53 2F747B86")); +// BigInteger w = new BigInteger(Hex.decode("7D2A8438 E6291C64 9B6579EB 3B79EAE9" + +// "48B1DE9E 5F7D1F40 70A08F8D B6B3C515" + +// "6F2201AF FBB5CB9D 82AA3EC0 D0398B89" + +// "ABC78A13 A760C0BF 3F77E63D 0DF3F1A3" + +// "41A41B88 11DF197F D6CD0F00 3125606F" + +// "4F109F40 0F7292A1 0D255E3C 0EBCCB42" + +// "53FB182C 68F09CF6 CD9C4A53 DA6C74AD" + +// "007AF36B 8BCA979D 5895E282 F483FCD6")); +// BigInteger Rbx = new BigInteger(Hex.decode("44E8AD44 AB8592A6 A5A3DDCA 5CF896C7" + +// "18043606 A01D650D EF37A01F 37C228C3" + +// "32FC3173 54E2C274 D4DAF8AD 001054C7" + +// "6CE57971 C6F4486D 57230432 61C506EB" + +// "F5BE438F 53DE04F0 67C776E0 DD3B71A6" + +// "29013328 3725A532 F21AF145 126DC1D7" + +// "77ECC27B E50835BD 28098B8A 73D9F801" + +// "D893793A 41FF5C49 B87E79F2 BE4D56CE")); +// BigInteger Rby = new BigInteger(Hex.decode("557E134A D85BB1D4 B9CE4F8B E4B08A12" + +// "BABF55B1 D6F1D7A6 38019EA2 8E15AB1C" + +// "9F76375F DD1210D4 F4351B9A 009486B7" + +// "F3ED46C9 65DED2D8 0DADE4F3 8C6721D5" + +// "2C3AD103 A10EBD29 59248B4E F006836B" + +// "F097448E 6107C9ED EE9FB704 823DF199" + +// "F832C905 AE45F8A2 47A072D8 EF729EAB" + +// "C5E27574 B07739B3 4BE74A53 2F747B86")); BigInteger p = new BigInteger( "997ABB1F0A563FDA65C61198DAD0657A416C0CE19CB48261BE9AE358B3E01A2E" + "F40AAB27E2FC0F1B228730D531A59CB0E791B39FF7C88A19356D27F4A666A6D0" + @@ -137,21 +145,32 @@ public void performTest() g,// Order of the subgroup (from RFC 6509) BigInteger.ONE // Cofactor = 1 ); + ECPoint P = curve.createPoint(Px, Py); - SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(ssv), - new FixedSecureRandom.Data(b)}); - SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(random); - SecretWithEncapsulation rlt = generator.generateEncapsulated(new SAKKEPublicKeyParameters(new BigInteger(b), curve.createPoint(Zx, Zy))); + ECPoint computed_Z = P.multiply(z).normalize(); + Assert.assertTrue(computed_Z.equals(curve.createPoint(Zx, Zy))); - ECPoint P = curve.createPoint(Px, Py); + SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(ssv)}); + SAKKEPublicKeyParameters b_publicKey = new SAKKEPublicKeyParameters(new BigInteger(b), curve.createPoint(Zx, Zy)); + SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(random); + SecretWithEncapsulation rlt = generator.generateEncapsulated(b_publicKey); - BigInteger computed_g2 = SAKKEKEMExtractor.computePairing(P, P, p, q); - Assert.assertTrue(computed_g2.equals(g)); - ECPoint K_bS = curve.createPoint(kbx, kby); + SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(z, b_publicKey)); + byte[] test = extractor.extractSecret(rlt.getEncapsulation()); + Assert.assertTrue(Arrays.areEqual(test, ssv)); + } - SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(z, K_bS, - new SAKKEPublicKeyParameters(new BigInteger(b), curve.createPoint(Zx, Zy)))); + private void testRandom() + { + SecureRandom random = new SecureRandom(); + byte[] ssv = new byte[16]; + random.nextBytes(ssv); + SAKKEPrivateKeyParameters b_priv = new SAKKEPrivateKeyParameters(random); + SAKKEPublicKeyParameters b_pub = b_priv.getPublicParams(); + SAKKEKEMSGenerator generator = new SAKKEKEMSGenerator(new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(ssv)})); + SecretWithEncapsulation rlt = generator.generateEncapsulated(b_pub); + SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(b_priv); byte[] test = extractor.extractSecret(rlt.getEncapsulation()); Assert.assertTrue(Arrays.areEqual(test, ssv)); } From 3b6cab061dd1ff1ea8c698835c31283b7696f4b0 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 7 Feb 2025 14:42:34 +1030 Subject: [PATCH 175/890] Add key pair check in SAKKEPrivateKeyParameters. --- .../crypto/params/SAKKEPrivateKeyParameters.java | 16 +++++++++++++--- .../crypto/kems/test/SAKKEKEMSTest.java | 2 +- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java index d5a7539045..9e3be92c3f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPrivateKeyParameters.java @@ -3,6 +3,7 @@ import java.math.BigInteger; import java.security.SecureRandom; +import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.BigIntegers; /** @@ -23,15 +24,19 @@ public class SAKKEPrivateKeyParameters extends AsymmetricKeyParameter { private static final BigInteger qMinOne = SAKKEPublicKeyParameters.q.subtract(BigInteger.ONE); - /** The associated public key parameters. */ + /** + * The associated public key parameters. + */ private final SAKKEPublicKeyParameters publicParams; - /** The private key scalar (master secret). */ + /** + * The private key scalar (master secret). + */ private final BigInteger z; // KMS Public Key: Z = [z]P /** * Constructs a SAKKE private key with a given private value and associated public parameters. * - * @param z The private key scalar. + * @param z The private key scalar. * @param publicParams The associated public key parameters. */ public SAKKEPrivateKeyParameters(BigInteger z, SAKKEPublicKeyParameters publicParams) @@ -39,6 +44,11 @@ public SAKKEPrivateKeyParameters(BigInteger z, SAKKEPublicKeyParameters publicPa super(true); this.z = z; this.publicParams = publicParams; + ECPoint computed_Z = publicParams.getPoint().multiply(z).normalize(); + if (!computed_Z.equals(publicParams.getZ())) + { + throw new IllegalStateException("public key and private key of SAKKE do not match"); + } } /** diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java index 492c8d1e4c..0ef2797037 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java @@ -39,7 +39,7 @@ public void performTest() throws Exception { testTestVector(); - for (int i = 0; i < 100; ++i) + for (int i = 0; i < 1; ++i) { testRandom(); } From b05a7b508d22919948ba60f0463b8a6729de62ba Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 18 Feb 2025 19:14:04 +1030 Subject: [PATCH 176/890] Add ECCSI key generator process --- .../generators/ECCSIKeyPairGenerator.java | 78 +++++++++++++ .../params/ECCSIKeyGenerationParameters.java | 60 ++++++++++ .../params/ECCSIPrivateKeyParameters.java | 14 +++ .../params/ECCSIPublicKeyParameters.java | 14 +++ .../crypto/signers/ECCSISigner.java | 108 ++++++++++++++++++ 5 files changed, 274 insertions(+) create mode 100644 core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java create mode 100644 core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java new file mode 100644 index 0000000000..fac222e103 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java @@ -0,0 +1,78 @@ +package org.bouncycastle.crypto.generators; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.bouncycastle.crypto.CryptoServicePurpose; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.KeyGenerationParameters; +import org.bouncycastle.crypto.constraints.DefaultServiceProperties; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.crypto.params.ECCSIKeyGenerationParameters; +import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; + +public class ECCSIKeyPairGenerator + implements AsymmetricCipherKeyPairGenerator +{ + // Initialize NIST P-256 curve + private static final X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); + private static final ECCurve curve = params.getCurve(); + + private static final BigInteger q = ((ECCurve.Fp)curve).getQ(); + + //BigInteger p = ((ECCurve.Fp)curve).getOrder(); + + // The subgroup order is available as: + //BigInteger n = params.getN(); + + // And the base point (generator) is: + private static final ECPoint G = params.getG(); + //int N = 32; // 256 bits + + private ECCSIKeyGenerationParameters parameters; + + @Override + public void init(KeyGenerationParameters parameters) + { + this.parameters = (ECCSIKeyGenerationParameters)parameters; + + CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("ECCSI", 256, null, CryptoServicePurpose.KEYGEN)); + } + + @Override + public AsymmetricCipherKeyPair generateKeyPair() + { + SecureRandom random = parameters.getRandom(); + byte[] id = parameters.getId(); + ECPoint kpak = parameters.getKPAK(); + // 1) Choose v, a random (ephemeral) non-zero element of F_q; + BigInteger v = new BigInteger(256, random).mod(q); + // 2) Compute PVT = [v]G + ECPoint pvt = G.multiply(v).normalize(); + + // 3) Compute a hash value HS = hash( G || KPAK || ID || PVT ), an N-octet integer; + Digest digest = new SHA256Digest(); + byte[] tmp = G.getEncoded(false); + digest.update(tmp, 0, tmp.length); + tmp = kpak.getEncoded(false); + digest.update(tmp, 0, tmp.length); + digest.update(id, 0, id.length); + tmp = pvt.getEncoded(false); + digest.update(tmp, 0, tmp.length); + tmp = new byte[digest.getDigestSize()]; + digest.doFinal(tmp, 0); + BigInteger HS = new BigInteger(1, tmp).mod(q); + + // 4) Compute SSK = ( KSAK + HS * v ) modulo q; + BigInteger ssk = parameters.computeSSK(HS.multiply(v)); + return new AsymmetricCipherKeyPair(new ECCSIPublicKeyParameters(pvt), new ECCSIPrivateKeyParameters(ssk)); + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java new file mode 100644 index 0000000000..f21a8df432 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java @@ -0,0 +1,60 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.KeyGenerationParameters; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.Arrays; + +public class ECCSIKeyGenerationParameters + extends KeyGenerationParameters +{ + private static final X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); + private static final ECCurve curve = params.getCurve(); + + private static final BigInteger q = ((ECCurve.Fp)curve).getQ(); + + //BigInteger p = ((ECCurve.Fp)curve).getOrder(); + + // The subgroup order is available as: + //BigInteger n = params.getN(); + + // And the base point (generator) is: + private static final ECPoint G = params.getG(); + private final byte[] id; + private final BigInteger ksak; + private final ECPoint kpak; + + /** + * initialise the generator with a source of randomness + * and a strength (in bits). + * + * @param random the random byte source. + */ + public ECCSIKeyGenerationParameters(SecureRandom random, byte[] id) + { + super(random, 256); + this.id = Arrays.clone(id); + this.ksak = new BigInteger(256, random).mod(q); + this.kpak = G.multiply(ksak).normalize(); + } + + public byte[] getId() + { + return id; + } + + public ECPoint getKPAK() + { + return kpak; + } + + public BigInteger computeSSK(BigInteger hs_v) + { + return ksak.add(hs_v).mod(q); + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java new file mode 100644 index 0000000000..e98a947257 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java @@ -0,0 +1,14 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; + +public class ECCSIPrivateKeyParameters + extends AsymmetricKeyParameter +{ + private final BigInteger ssk; + public ECCSIPrivateKeyParameters(BigInteger ssk) + { + super(true); + this.ssk = ssk; + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java new file mode 100644 index 0000000000..aa41c352f0 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java @@ -0,0 +1,14 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.math.ec.ECPoint; + +public class ECCSIPublicKeyParameters + extends AsymmetricKeyParameter +{ + private final ECPoint pvt; + public ECCSIPublicKeyParameters(ECPoint pvt) + { + super(false); + this.pvt = pvt; + } +} diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java new file mode 100644 index 0000000000..b5dd0c2d1b --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java @@ -0,0 +1,108 @@ +package org.bouncycastle.crypto.signers; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.Signer; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.BigIntegers; + +public class ECCSISigner + implements Signer +{ + private static final X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); + private static final ECCurve curve = params.getCurve(); + + private static final BigInteger q = ((ECCurve.Fp)curve).getQ(); + + //BigInteger p = ((ECCurve.Fp)curve).getOrder(); + + // The subgroup order is available as: + //BigInteger n = params.getN(); + + // And the base point (generator) is: + private static final ECPoint G = params.getG(); + private final Digest digest = new SHA256Digest(); + BigInteger j; + ECPoint J; + BigInteger r; + + public ECCSISigner() + { + + } + + @Override + public void init(boolean forSigning, CipherParameters param) + { + SecureRandom random = null; + if (param instanceof ParametersWithRandom) + { + random = ((ParametersWithRandom)param).getRandom(); + param = ((ParametersWithRandom)param).getParameters(); + } + + if (forSigning) + { + ECCSIPrivateKeyParameters parameters = (ECCSIPrivateKeyParameters)param; + + j = new BigInteger(256, random).mod(q); + J = G.multiply(j).normalize(); + r = J.getAffineXCoord().toBigInteger(); + byte[] rBytes = BigIntegers.asUnsignedByteArray(256, r); +// BigInteger kpak = parameters + byte[] tmp = G.getEncoded(false); + digest.update(tmp, 0, tmp.length); +// tmp = kpak.getEncoded(false); +// digest.update(tmp, 0, tmp.length); +// digest.update(id, 0, id.length); +// tmp = pvt.getEncoded(false); +// digest.update(tmp, 0, tmp.length); + tmp = new byte[digest.getDigestSize()]; + digest.doFinal(tmp, 0); + BigInteger HS = new BigInteger(1, tmp).mod(q); + } + + } + + @Override + public void update(byte b) + { + + } + + @Override + public void update(byte[] in, int off, int len) + { + + } + + @Override + public byte[] generateSignature() + throws CryptoException, DataLengthException + { + return new byte[0]; + } + + @Override + public boolean verifySignature(byte[] signature) + { + return false; + } + + @Override + public void reset() + { + + } +} From 9cb941fbff8c2c3e99a296f0c469d1b3289678ae Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 19 Feb 2025 17:00:58 +1030 Subject: [PATCH 177/890] Pass the test vector of ECCSISigner --- .../generators/ECCSIKeyPairGenerator.java | 13 +- .../params/ECCSIKeyGenerationParameters.java | 19 +-- .../params/ECCSIPrivateKeyParameters.java | 15 +- .../params/ECCSIPublicKeyParameters.java | 6 + .../params/SAKKEPublicKeyParameters.java | 2 +- .../crypto/signers/ECCSISigner.java | 152 ++++++++++++++---- .../crypto/test/ECCSISignerTest.java | 72 +++++++++ .../crypto/test/RegressionTest.java | 1 - .../crypto/{kems => }/test/SAKKEKEMSTest.java | 4 +- 9 files changed, 223 insertions(+), 61 deletions(-) create mode 100644 core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java rename core/src/test/java/org/bouncycastle/crypto/{kems => }/test/SAKKEKEMSTest.java (99%) diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java index fac222e103..25cf523d64 100644 --- a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java @@ -16,7 +16,6 @@ import org.bouncycastle.crypto.params.ECCSIKeyGenerationParameters; import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; -import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; public class ECCSIKeyPairGenerator @@ -24,14 +23,7 @@ public class ECCSIKeyPairGenerator { // Initialize NIST P-256 curve private static final X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); - private static final ECCurve curve = params.getCurve(); - - private static final BigInteger q = ((ECCurve.Fp)curve).getQ(); - - //BigInteger p = ((ECCurve.Fp)curve).getOrder(); - - // The subgroup order is available as: - //BigInteger n = params.getN(); + private static final BigInteger q = params.getCurve().getOrder(); // And the base point (generator) is: private static final ECPoint G = params.getG(); @@ -73,6 +65,7 @@ public AsymmetricCipherKeyPair generateKeyPair() // 4) Compute SSK = ( KSAK + HS * v ) modulo q; BigInteger ssk = parameters.computeSSK(HS.multiply(v)); - return new AsymmetricCipherKeyPair(new ECCSIPublicKeyParameters(pvt), new ECCSIPrivateKeyParameters(ssk)); + ECCSIPublicKeyParameters pub = new ECCSIPublicKeyParameters(pvt); + return new AsymmetricCipherKeyPair(new ECCSIPublicKeyParameters(pvt), new ECCSIPrivateKeyParameters(ssk, pub)); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java index f21a8df432..0e773b17ec 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java @@ -6,25 +6,22 @@ import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.KeyGenerationParameters; import org.bouncycastle.crypto.ec.CustomNamedCurves; -import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; public class ECCSIKeyGenerationParameters extends KeyGenerationParameters { - private static final X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); - private static final ECCurve curve = params.getCurve(); + private static final BigInteger q; + private static final ECPoint G; - private static final BigInteger q = ((ECCurve.Fp)curve).getQ(); - - //BigInteger p = ((ECCurve.Fp)curve).getOrder(); - - // The subgroup order is available as: - //BigInteger n = params.getN(); + static + { + X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); + q = params.getCurve().getOrder(); + G = params.getG(); + } - // And the base point (generator) is: - private static final ECPoint G = params.getG(); private final byte[] id; private final BigInteger ksak; private final ECPoint kpak; diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java index e98a947257..17ee88614a 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java @@ -6,9 +6,22 @@ public class ECCSIPrivateKeyParameters extends AsymmetricKeyParameter { private final BigInteger ssk; - public ECCSIPrivateKeyParameters(BigInteger ssk) + private final ECCSIPublicKeyParameters pub; + + public ECCSIPrivateKeyParameters(BigInteger ssk, ECCSIPublicKeyParameters pub) { super(true); this.ssk = ssk; + this.pub = pub; + } + + public ECCSIPublicKeyParameters getPublicKeyParameters() + { + return pub; + } + + public BigInteger getSSK() + { + return ssk; } } diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java index aa41c352f0..19bfc43b8f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java @@ -6,9 +6,15 @@ public class ECCSIPublicKeyParameters extends AsymmetricKeyParameter { private final ECPoint pvt; + public ECCSIPublicKeyParameters(ECPoint pvt) { super(false); this.pvt = pvt; } + + public final ECPoint getPVT() + { + return pvt; + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java index 2380efe143..5dc57b95d1 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/SAKKEPublicKeyParameters.java @@ -77,7 +77,7 @@ public class SAKKEPublicKeyParameters * Pairing result g = computed using the Tate-Lichtenbaum pairing * (RFC 6508, Section 3.2). Value from RFC 6509 Appendix A. */ - private static final BigInteger g = new BigInteger(Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + + private static final BigInteger g = new BigInteger(1, Hex.decode("66FC2A43 2B6EA392 148F1586 7D623068\n" + " C6A87BD1 FB94C41E 27FABE65 8E015A87\n" + " 371E9474 4C96FEDA 449AE956 3F8BC446\n" + " CBFDA85D 5D00EF57 7072DA8F 541721BE\n" + diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java index b5dd0c2d1b..550f6e9996 100644 --- a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java +++ b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto.signers; +import java.io.ByteArrayOutputStream; import java.math.BigInteger; import java.security.SecureRandom; @@ -12,92 +13,173 @@ import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; import org.bouncycastle.crypto.params.ParametersWithRandom; -import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; public class ECCSISigner implements Signer { - private static final X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); - private static final ECCurve curve = params.getCurve(); + private static final BigInteger q; + private static final ECPoint G; - private static final BigInteger q = ((ECCurve.Fp)curve).getQ(); - - //BigInteger p = ((ECCurve.Fp)curve).getOrder(); - - // The subgroup order is available as: - //BigInteger n = params.getN(); + static + { + X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); + q = params.getCurve().getOrder(); + G = params.getG(); + } - // And the base point (generator) is: - private static final ECPoint G = params.getG(); private final Digest digest = new SHA256Digest(); - BigInteger j; - ECPoint J; - BigInteger r; - - public ECCSISigner() + private BigInteger j; + private BigInteger r; + private ECPoint Y; + private final ECPoint kpak; + private final byte[] id; + private CipherParameters param; + private ByteArrayOutputStream stream; + private boolean forSigning; + + public ECCSISigner(ECPoint kpak, byte[] id) { - + this.kpak = kpak; + this.id = id; } @Override public void init(boolean forSigning, CipherParameters param) { + this.forSigning = forSigning; + this.param = param; SecureRandom random = null; if (param instanceof ParametersWithRandom) { random = ((ParametersWithRandom)param).getRandom(); param = ((ParametersWithRandom)param).getParameters(); } - + ECPoint kpak_computed = null; + ECPoint pvt; if (forSigning) { ECCSIPrivateKeyParameters parameters = (ECCSIPrivateKeyParameters)param; j = new BigInteger(256, random).mod(q); - J = G.multiply(j).normalize(); + ECPoint J = G.multiply(j).normalize(); r = J.getAffineXCoord().toBigInteger(); - byte[] rBytes = BigIntegers.asUnsignedByteArray(256, r); -// BigInteger kpak = parameters - byte[] tmp = G.getEncoded(false); - digest.update(tmp, 0, tmp.length); -// tmp = kpak.getEncoded(false); -// digest.update(tmp, 0, tmp.length); -// digest.update(id, 0, id.length); -// tmp = pvt.getEncoded(false); -// digest.update(tmp, 0, tmp.length); - tmp = new byte[digest.getDigestSize()]; - digest.doFinal(tmp, 0); - BigInteger HS = new BigInteger(1, tmp).mod(q); + pvt = parameters.getPublicKeyParameters().getPVT(); + kpak_computed = G.multiply(parameters.getSSK()); + } + else + { + ECCSIPublicKeyParameters parameters = (ECCSIPublicKeyParameters)param; + pvt = parameters.getPVT(); + stream = new ByteArrayOutputStream(); } + // compute HS + byte[] tmp = G.getEncoded(false); + digest.update(tmp, 0, tmp.length); + tmp = kpak.getEncoded(false); + digest.update(tmp, 0, tmp.length); + digest.update(id, 0, id.length); + tmp = pvt.getEncoded(false); + digest.update(tmp, 0, tmp.length); + tmp = new byte[digest.getDigestSize()]; + digest.doFinal(tmp, 0); + BigInteger HS = new BigInteger(1, tmp).mod(q); + + //HE = hash( HS || r || M ); + digest.update(tmp, 0, tmp.length); + if (forSigning) + { + kpak_computed = kpak_computed.subtract(pvt.multiply(HS)).normalize(); + if (!kpak_computed.equals(kpak)) + { + throw new IllegalArgumentException("Invalid KPAK"); + } + byte[] rBytes = BigIntegers.asUnsignedByteArray(32, r); + digest.update(rBytes, 0, rBytes.length); + } + else + { + // Compute Y = HS*PVT + KPAK + Y = pvt.multiply(HS).add(kpak).normalize(); + } } @Override public void update(byte b) { - + if (forSigning) + { + digest.update(b); + } + else + { + stream.write(b); + } } @Override public void update(byte[] in, int off, int len) { - + if (forSigning) + { + digest.update(in, off, len); + } + else + { + stream.write(in, off, len); + } } @Override public byte[] generateSignature() throws CryptoException, DataLengthException { - return new byte[0]; + byte[] heBytes = new byte[digest.getDigestSize()]; + digest.doFinal(heBytes, 0); + + //Compute s' = ( (( HE + r * SSK )^-1) * j ) modulo q + ECCSIPrivateKeyParameters params = (ECCSIPrivateKeyParameters)(((ParametersWithRandom)param).getParameters()); + BigInteger ssk = params.getSSK(); + BigInteger denominator = new BigInteger(1, heBytes).add(r.multiply(ssk)).mod(q); + if (denominator.equals(BigInteger.ZERO)) + { + throw new IllegalArgumentException("Invalid j, retry"); + } + + BigInteger sPrime = denominator.modInverse(q).multiply(j).mod(q); + + return Arrays.concatenate(BigIntegers.asUnsignedByteArray(32, r), BigIntegers.asUnsignedByteArray(32, sPrime), + params.getPublicKeyParameters().getPVT().getEncoded(false)); } @Override public boolean verifySignature(byte[] signature) { - return false; + byte[] bytes = Arrays.copyOf(signature, 32); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signature, 32, 64)); + r = new BigInteger(1, bytes).mod(q); + digest.update(bytes, 0, 32); + bytes = stream.toByteArray(); + digest.update(bytes, 0, bytes.length); + bytes = new byte[digest.getDigestSize()]; + digest.doFinal(bytes, 0); + + BigInteger HE = new BigInteger(1, bytes).mod(q); + + // Compute J = s*(HE*G + r*Y) + ECPoint HE_G = G.multiply(HE).normalize(); + ECPoint rY = Y.multiply(r).normalize(); + ECPoint sum = HE_G.add(rY).normalize(); + ECPoint J = sum.multiply(s).normalize(); + + BigInteger rComputed = J.getAffineXCoord().toBigInteger(); + + return rComputed.mod(q).equals(r.mod(q)); } @Override diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java new file mode 100644 index 0000000000..ba959a929c --- /dev/null +++ b/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java @@ -0,0 +1,72 @@ +package org.bouncycastle.crypto.test; + +import java.math.BigInteger; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.generators.ECCSIKeyPairGenerator; +import org.bouncycastle.crypto.params.ECCSIKeyGenerationParameters; +import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.crypto.signers.ECCSISigner; +import org.bouncycastle.util.BigIntegers; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.FixedSecureRandom; +import org.bouncycastle.util.test.SimpleTest; + +public class ECCSISignerTest + extends SimpleTest +{ + public static void main(String[] args) + throws Exception + { + ECCSISignerTest test = new ECCSISignerTest(); + test.performTest(); + } + + @Override + public String getName() + { + return "ECCSISigner Test"; + } + + @Override + public void performTest() + throws Exception + { + testTestVector(); + } + + private void testTestVector() + throws Exception + { + BigInteger ksak = BigInteger.valueOf(0x12345); + BigInteger v = BigInteger.valueOf(0x23456); + BigInteger j = BigInteger.valueOf(0x34567); + ECCSIKeyPairGenerator generator = new ECCSIKeyPairGenerator(); + SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(BigIntegers.asUnsignedByteArray(32, ksak)), + new FixedSecureRandom.Data(BigIntegers.asUnsignedByteArray(32, v)), + new FixedSecureRandom.Data(BigIntegers.asUnsignedByteArray(32, j))}); + ECCSIKeyGenerationParameters keyGenerationParameters = new ECCSIKeyGenerationParameters(random, "2011-02\0tel:+447700900123\0".getBytes()); + generator.init(keyGenerationParameters); + AsymmetricCipherKeyPair keyPair = generator.generateKeyPair(); + ECCSIPublicKeyParameters pub = (ECCSIPublicKeyParameters)keyPair.getPublic(); + ECCSIPrivateKeyParameters priv = (ECCSIPrivateKeyParameters)keyPair.getPrivate(); + System.out.println(new String(Hex.encode(pub.getPVT().getXCoord().toBigInteger().toByteArray()))); + System.out.println(new String(Hex.encode(pub.getPVT().getYCoord().toBigInteger().toByteArray()))); + System.out.println(new String(Hex.encode(priv.getSSK().toByteArray()))); + + byte[] M = "message\0".getBytes(); + + ECCSISigner signer = new ECCSISigner(keyGenerationParameters.getKPAK(), keyGenerationParameters.getId()); + signer.init(true, new ParametersWithRandom(priv, random)); + signer.update(M, 0, M.length); + byte[] sig = signer.generateSignature(); + System.out.println("sig: " + new String(Hex.encode(sig))); + + signer.init(false, pub); + signer.update(M, 0, M.length); + isTrue(signer.verifySignature(sig)); + } +} diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java index 1025007493..4349b06ec0 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RegressionTest.java @@ -1,6 +1,5 @@ package org.bouncycastle.crypto.test; -import org.bouncycastle.crypto.kems.test.SAKKEKEMSTest; import org.bouncycastle.util.test.SimpleTest; import org.bouncycastle.util.test.Test; diff --git a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SAKKEKEMSTest.java similarity index 99% rename from core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java rename to core/src/test/java/org/bouncycastle/crypto/test/SAKKEKEMSTest.java index 0ef2797037..fa9d73f1e4 100644 --- a/core/src/test/java/org/bouncycastle/crypto/kems/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SAKKEKEMSTest.java @@ -1,4 +1,4 @@ -package org.bouncycastle.crypto.kems.test; +package org.bouncycastle.crypto.test; import java.math.BigInteger; import java.security.SecureRandom; @@ -31,7 +31,7 @@ public static void main(String[] args) @Override public String getName() { - return null; + return "SAKKE-KEMS Test"; } @Override From 3d3ca2d6109e84d6d9c36da0faafbf113e45a359 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 20 Feb 2025 12:36:13 +1030 Subject: [PATCH 178/890] Add support for different curves --- .../generators/ECCSIKeyPairGenerator.java | 21 +-- .../params/ECCSIKeyGenerationParameters.java | 46 +++-- .../crypto/signers/ECCSISigner.java | 158 ++++++++++-------- .../crypto/test/ECCSISignerTest.java | 103 +++++++++++- 4 files changed, 223 insertions(+), 105 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java index 25cf523d64..bfe73a514f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java @@ -3,7 +3,6 @@ import java.math.BigInteger; import java.security.SecureRandom; -import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; import org.bouncycastle.crypto.CryptoServicePurpose; @@ -11,8 +10,6 @@ import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.KeyGenerationParameters; import org.bouncycastle.crypto.constraints.DefaultServiceProperties; -import org.bouncycastle.crypto.digests.SHA256Digest; -import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.params.ECCSIKeyGenerationParameters; import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; @@ -21,28 +18,27 @@ public class ECCSIKeyPairGenerator implements AsymmetricCipherKeyPairGenerator { - // Initialize NIST P-256 curve - private static final X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); - private static final BigInteger q = params.getCurve().getOrder(); - - // And the base point (generator) is: - private static final ECPoint G = params.getG(); - //int N = 32; // 256 bits - + private BigInteger q; + private ECPoint G; + private Digest digest; private ECCSIKeyGenerationParameters parameters; @Override public void init(KeyGenerationParameters parameters) { this.parameters = (ECCSIKeyGenerationParameters)parameters; + this.q = this.parameters.getQ(); + this.G = this.parameters.getG(); + this.digest = this.parameters.getDigest(); - CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("ECCSI", 256, null, CryptoServicePurpose.KEYGEN)); + CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties("ECCSI", this.parameters.getN(), null, CryptoServicePurpose.KEYGEN)); } @Override public AsymmetricCipherKeyPair generateKeyPair() { SecureRandom random = parameters.getRandom(); + this.digest.reset(); byte[] id = parameters.getId(); ECPoint kpak = parameters.getKPAK(); // 1) Choose v, a random (ephemeral) non-zero element of F_q; @@ -51,7 +47,6 @@ public AsymmetricCipherKeyPair generateKeyPair() ECPoint pvt = G.multiply(v).normalize(); // 3) Compute a hash value HS = hash( G || KPAK || ID || PVT ), an N-octet integer; - Digest digest = new SHA256Digest(); byte[] tmp = G.getEncoded(false); digest.update(tmp, 0, tmp.length); tmp = kpak.getEncoded(false); diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java index 0e773b17ec..22df66c639 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java @@ -4,27 +4,21 @@ import java.security.SecureRandom; import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.KeyGenerationParameters; -import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; public class ECCSIKeyGenerationParameters extends KeyGenerationParameters { - private static final BigInteger q; - private static final ECPoint G; - - static - { - X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); - q = params.getCurve().getOrder(); - G = params.getG(); - } - + private final BigInteger q; + private final ECPoint G; + private final Digest digest; private final byte[] id; private final BigInteger ksak; private final ECPoint kpak; + private final int n; /** * initialise the generator with a source of randomness @@ -32,11 +26,15 @@ public class ECCSIKeyGenerationParameters * * @param random the random byte source. */ - public ECCSIKeyGenerationParameters(SecureRandom random, byte[] id) + public ECCSIKeyGenerationParameters(SecureRandom random, X9ECParameters params, Digest digest, byte[] id) { - super(random, 256); + super(random, params.getCurve().getA().bitLength()); + this.q = params.getCurve().getOrder(); + this.G = params.getG(); + this.digest = digest; this.id = Arrays.clone(id); - this.ksak = new BigInteger(256, random).mod(q); + this.n = params.getCurve().getA().bitLength(); + this.ksak = new BigInteger(n, random).mod(q); this.kpak = G.multiply(ksak).normalize(); } @@ -54,4 +52,24 @@ public BigInteger computeSSK(BigInteger hs_v) { return ksak.add(hs_v).mod(q); } + + public BigInteger getQ() + { + return q; + } + + public ECPoint getG() + { + return G; + } + + public Digest getDigest() + { + return digest; + } + + public int getN() + { + return n; + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java index 550f6e9996..c825971f04 100644 --- a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java +++ b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java @@ -10,8 +10,6 @@ import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Signer; -import org.bouncycastle.crypto.digests.SHA256Digest; -import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; import org.bouncycastle.crypto.params.ParametersWithRandom; @@ -19,20 +17,27 @@ import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; +/** + * Implementation of Elliptic Curve-based Certificateless Signatures for Identity-Based Encryption (ECCSI) + * as defined in RFC 6507. + *

+ * This class handles both signature generation and verification using the ECCSI scheme. It supports: + *

    + *
  • NIST P-256 (secp256r1) elliptic curve parameters
  • + *
  • SHA-256 hash function
  • + *
  • Certificateless signatures using KMS Public Authentication Key (KPAK)
  • + *
  • Identity-based signatures with Secret Signing Key (SSK)
  • + *
+ * + * @see RFC 6507: Elliptic Curve-Based Certificateless + * Signatures for Identity-Based Encryption (ECCSI) + */ public class ECCSISigner implements Signer { - private static final BigInteger q; - private static final ECPoint G; - - static - { - X9ECParameters params = CustomNamedCurves.getByName("secP256r1"); - q = params.getCurve().getOrder(); - G = params.getG(); - } - - private final Digest digest = new SHA256Digest(); + private final BigInteger q; + private final ECPoint G; + private final Digest digest; private BigInteger j; private BigInteger r; private ECPoint Y; @@ -41,11 +46,18 @@ public class ECCSISigner private CipherParameters param; private ByteArrayOutputStream stream; private boolean forSigning; + private final int N; - public ECCSISigner(ECPoint kpak, byte[] id) + + public ECCSISigner(ECPoint kpak, X9ECParameters params, Digest digest, byte[] id) { this.kpak = kpak; this.id = id; + this.q = params.getCurve().getOrder(); + this.G = params.getG(); + this.digest = digest; + this.digest.reset(); + this.N = (params.getCurve().getOrder().bitLength() + 7) >> 3; } @Override @@ -53,60 +65,7 @@ public void init(boolean forSigning, CipherParameters param) { this.forSigning = forSigning; this.param = param; - SecureRandom random = null; - if (param instanceof ParametersWithRandom) - { - random = ((ParametersWithRandom)param).getRandom(); - param = ((ParametersWithRandom)param).getParameters(); - } - ECPoint kpak_computed = null; - ECPoint pvt; - if (forSigning) - { - ECCSIPrivateKeyParameters parameters = (ECCSIPrivateKeyParameters)param; - - j = new BigInteger(256, random).mod(q); - ECPoint J = G.multiply(j).normalize(); - r = J.getAffineXCoord().toBigInteger(); - pvt = parameters.getPublicKeyParameters().getPVT(); - kpak_computed = G.multiply(parameters.getSSK()); - } - else - { - ECCSIPublicKeyParameters parameters = (ECCSIPublicKeyParameters)param; - pvt = parameters.getPVT(); - stream = new ByteArrayOutputStream(); - } - - // compute HS - byte[] tmp = G.getEncoded(false); - digest.update(tmp, 0, tmp.length); - tmp = kpak.getEncoded(false); - digest.update(tmp, 0, tmp.length); - digest.update(id, 0, id.length); - tmp = pvt.getEncoded(false); - digest.update(tmp, 0, tmp.length); - tmp = new byte[digest.getDigestSize()]; - digest.doFinal(tmp, 0); - BigInteger HS = new BigInteger(1, tmp).mod(q); - - //HE = hash( HS || r || M ); - digest.update(tmp, 0, tmp.length); - if (forSigning) - { - kpak_computed = kpak_computed.subtract(pvt.multiply(HS)).normalize(); - if (!kpak_computed.equals(kpak)) - { - throw new IllegalArgumentException("Invalid KPAK"); - } - byte[] rBytes = BigIntegers.asUnsignedByteArray(32, r); - digest.update(rBytes, 0, rBytes.length); - } - else - { - // Compute Y = HS*PVT + KPAK - Y = pvt.multiply(HS).add(kpak).normalize(); - } + reset(); } @Override @@ -153,17 +112,17 @@ public byte[] generateSignature() BigInteger sPrime = denominator.modInverse(q).multiply(j).mod(q); - return Arrays.concatenate(BigIntegers.asUnsignedByteArray(32, r), BigIntegers.asUnsignedByteArray(32, sPrime), + return Arrays.concatenate(BigIntegers.asUnsignedByteArray(this.N, r), BigIntegers.asUnsignedByteArray(this.N, sPrime), params.getPublicKeyParameters().getPVT().getEncoded(false)); } @Override public boolean verifySignature(byte[] signature) { - byte[] bytes = Arrays.copyOf(signature, 32); - BigInteger s = new BigInteger(1, Arrays.copyOfRange(signature, 32, 64)); + byte[] bytes = Arrays.copyOf(signature, this.N); + BigInteger s = new BigInteger(1, Arrays.copyOfRange(signature, this.N, this.N << 1)); r = new BigInteger(1, bytes).mod(q); - digest.update(bytes, 0, 32); + digest.update(bytes, 0, this.N); bytes = stream.toByteArray(); digest.update(bytes, 0, bytes.length); bytes = new byte[digest.getDigestSize()]; @@ -185,6 +144,61 @@ public boolean verifySignature(byte[] signature) @Override public void reset() { + digest.reset(); + CipherParameters param = this.param; + SecureRandom random = null; + if (param instanceof ParametersWithRandom) + { + random = ((ParametersWithRandom)param).getRandom(); + param = ((ParametersWithRandom)param).getParameters(); + } + ECPoint kpak_computed = null; + ECPoint pvt; + if (forSigning) + { + ECCSIPrivateKeyParameters parameters = (ECCSIPrivateKeyParameters)param; + pvt = parameters.getPublicKeyParameters().getPVT(); + j = new BigInteger(q.bitLength(), random); + ECPoint J = G.multiply(j).normalize(); + r = J.getAffineXCoord().toBigInteger().mod(q); + kpak_computed = G.multiply(parameters.getSSK()); + } + else + { + ECCSIPublicKeyParameters parameters = (ECCSIPublicKeyParameters)param; + pvt = parameters.getPVT(); + stream = new ByteArrayOutputStream(); + } + + // compute HS + byte[] tmp = G.getEncoded(false); + digest.update(tmp, 0, tmp.length); + tmp = kpak.getEncoded(false); + digest.update(tmp, 0, tmp.length); + digest.update(id, 0, id.length); + tmp = pvt.getEncoded(false); + digest.update(tmp, 0, tmp.length); + tmp = new byte[digest.getDigestSize()]; + digest.doFinal(tmp, 0); + BigInteger HS = new BigInteger(1, tmp).mod(q); + + //HE = hash( HS || r || M ); + digest.update(tmp, 0, tmp.length); + if (forSigning) + { + kpak_computed = kpak_computed.subtract(pvt.multiply(HS)).normalize(); + if (!kpak_computed.equals(kpak)) + { + throw new IllegalArgumentException("Invalid KPAK"); + } + byte[] rBytes = BigIntegers.asUnsignedByteArray(this.N, r); + digest.update(rBytes, 0, rBytes.length); + } + else + { + // Compute Y = HS*PVT + KPAK + Y = pvt.multiply(HS).add(kpak).normalize(); + } } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java index ba959a929c..dc6018444f 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java @@ -3,13 +3,19 @@ import java.math.BigInteger; import java.security.SecureRandom; +import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.digests.SHA512Digest; +import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.generators.ECCSIKeyPairGenerator; import org.bouncycastle.crypto.params.ECCSIKeyGenerationParameters; import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.crypto.signers.ECCSISigner; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.FixedSecureRandom; @@ -18,6 +24,42 @@ public class ECCSISignerTest extends SimpleTest { + String[] curveNames = { + "curve25519", + "secp128r1", + "secp160k1", + "secp160r1", + "secp160r2", + "secp192k1", + "secp192r1", + "secp224k1", + "secp224r1", + "secp256k1", + "secp256r1", + "secp384r1", + "secp521r1", + "sect113r1", + "sect113r2", + "sect131r1", + "sect131r2", + "sect163k1", + "sect163r1", + "sect163r2", + "sect193r1", + "sect193r2", + "sect233k1", + "sect233r1", + "sect239k1", + "sect283k1", + "sect283r1", + "sect409k1", + "sect409r1", + "sect571k1", + "sect571r1", + "sm2p256v1" + }; + + public static void main(String[] args) throws Exception { @@ -36,6 +78,11 @@ public void performTest() throws Exception { testTestVector(); + for (int i = 0; i < curveNames.length; ++i) + { + //System.out.println(curveNames[i]); + testRandom(curveNames[i]); + } } private void testTestVector() @@ -48,25 +95,69 @@ private void testTestVector() SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(BigIntegers.asUnsignedByteArray(32, ksak)), new FixedSecureRandom.Data(BigIntegers.asUnsignedByteArray(32, v)), new FixedSecureRandom.Data(BigIntegers.asUnsignedByteArray(32, j))}); - ECCSIKeyGenerationParameters keyGenerationParameters = new ECCSIKeyGenerationParameters(random, "2011-02\0tel:+447700900123\0".getBytes()); + ECCSIKeyGenerationParameters keyGenerationParameters = new ECCSIKeyGenerationParameters(random, + CustomNamedCurves.getByName("secP256r1"), new SHA256Digest(), "2011-02\0tel:+447700900123\0".getBytes()); generator.init(keyGenerationParameters); AsymmetricCipherKeyPair keyPair = generator.generateKeyPair(); ECCSIPublicKeyParameters pub = (ECCSIPublicKeyParameters)keyPair.getPublic(); ECCSIPrivateKeyParameters priv = (ECCSIPrivateKeyParameters)keyPair.getPrivate(); - System.out.println(new String(Hex.encode(pub.getPVT().getXCoord().toBigInteger().toByteArray()))); - System.out.println(new String(Hex.encode(pub.getPVT().getYCoord().toBigInteger().toByteArray()))); - System.out.println(new String(Hex.encode(priv.getSSK().toByteArray()))); +// System.out.println(new String(Hex.encode(pub.getPVT().getXCoord().toBigInteger().toByteArray()))); +// System.out.println(new String(Hex.encode(pub.getPVT().getYCoord().toBigInteger().toByteArray()))); +// System.out.println(new String(Hex.encode(priv.getSSK().toByteArray()))); byte[] M = "message\0".getBytes(); - ECCSISigner signer = new ECCSISigner(keyGenerationParameters.getKPAK(), keyGenerationParameters.getId()); + ECCSISigner signer = new ECCSISigner(keyGenerationParameters.getKPAK(), CustomNamedCurves.getByName("secP256r1"), new SHA256Digest(), keyGenerationParameters.getId()); signer.init(true, new ParametersWithRandom(priv, random)); signer.update(M, 0, M.length); byte[] sig = signer.generateSignature(); - System.out.println("sig: " + new String(Hex.encode(sig))); + isTrue(Arrays.areEqual(sig, Hex.decode("269D4C8F DEB66A74 E4EF8C0D 5DCC597D\n" + + " DFE6029C 2AFFC493 6008CD2C C1045D81\n" + + " E09B528D 0EF8D6DF 1AA3ECBF 80110CFC\n" + + " EC9FC682 52CEBB67 9F413484 6940CCFD\n" + + " 04\n" + + "\n" + + " 758A1427 79BE89E8 29E71984 CB40EF75\n" + + " 8CC4AD77 5FC5B9A3 E1C8ED52 F6FA36D9\n" + + " A79D2476 92F4EDA3 A6BDAB77 D6AA6474\n" + + " A464AE49 34663C52 65BA7018 BA091F79"))); +// System.out.println("sig: " + new String(Hex.encode(sig))); signer.init(false, pub); signer.update(M, 0, M.length); isTrue(signer.verifySignature(sig)); } + + private void testRandom(String curveName) + throws Exception + { + SecureRandom random = new SecureRandom(); + ECCSIKeyPairGenerator generator = new ECCSIKeyPairGenerator(); + byte[] id = new byte[16]; + random.nextBytes(id); + Digest digest = new SHA512Digest(); + X9ECParameters params = CustomNamedCurves.getByName(curveName); + ECCSIKeyGenerationParameters keyGenerationParameters = new ECCSIKeyGenerationParameters(random, + params, digest, id); + generator.init(keyGenerationParameters); + AsymmetricCipherKeyPair keyPair = generator.generateKeyPair(); + ECCSIPublicKeyParameters pub = (ECCSIPublicKeyParameters)keyPair.getPublic(); + ECCSIPrivateKeyParameters priv = (ECCSIPrivateKeyParameters)keyPair.getPrivate(); + + byte[] M = "message\0".getBytes(); + + ECCSISigner signer = new ECCSISigner(keyGenerationParameters.getKPAK(), params, digest, keyGenerationParameters.getId()); + signer.init(true, new ParametersWithRandom(priv, random)); + signer.update(M, 0, M.length); + signer.reset(); + signer.update(M, 0, M.length); + byte[] sig = signer.generateSignature(); + signer = new ECCSISigner(keyGenerationParameters.getKPAK(), params, digest, keyGenerationParameters.getId()); + signer.init(false, pub); + signer.update(M, 0, M.length); + signer.reset(); + signer.update(M, 0, M.length); + isTrue(signer.verifySignature(sig)); + } + } From 3e093511f9e9aebaee56652fcd7bde51529918c4 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 20 Feb 2025 13:51:18 +1030 Subject: [PATCH 179/890] Add java doc and more tests with different digests --- .../params/ECCSIKeyGenerationParameters.java | 91 ++++++++++++++++++- .../params/ECCSIPrivateKeyParameters.java | 45 +++++++++ .../params/ECCSIPublicKeyParameters.java | 30 ++++++ .../crypto/signers/ECCSISigner.java | 49 ++++++++-- .../crypto/test/ECCSISignerTest.java | 26 +++++- 5 files changed, 224 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java index 22df66c639..3b62709c19 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java @@ -9,22 +9,68 @@ import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; +/** + * Parameters for ECCSI key generation. + * + *

+ * This class encapsulates the parameters required for ECCSI (Elliptic Curve + * Certificateless Signatures for Identity-based encryption) key generation. + * It holds the elliptic curve domain parameters and computes the key pair + * components used in ECCSI. + *

+ * + *

+ * The secret component {@code ksak} is generated randomly and reduced modulo + * {@code q}, while {@code kpak} is derived from {@code ksak} by multiplying the + * generator point. + *

+ */ public class ECCSIKeyGenerationParameters extends KeyGenerationParameters { + /** + * The order of the elliptic curve. + */ private final BigInteger q; + + /** + * The generator (base point) of the elliptic curve. + */ private final ECPoint G; + + /** + * The digest algorithm used in key generation. + */ private final Digest digest; + + /** + * The identifier (e.g. user identity) used in key generation. + */ private final byte[] id; + + /** + * The secret key component (ksak) used in ECCSI, generated randomly. + */ private final BigInteger ksak; + + /** + * The public key component (kpak), computed as G * ksak. + */ private final ECPoint kpak; + + /** + * The bit length used for key generation (typically the bit length of the curve's parameter A). + */ private final int n; /** - * initialise the generator with a source of randomness - * and a strength (in bits). + * Constructs an instance of {@code ECCSIKeyGenerationParameters} with the specified + * source of randomness, elliptic curve parameters, digest algorithm, and identifier. * - * @param random the random byte source. + * @param random the source of randomness. + * @param params the elliptic curve parameters (in X9.62 format) providing the curve, order, and generator. + * @param digest the digest algorithm to be used. + * @param id the identifier associated with the key generation (e.g. a user or device ID). */ public ECCSIKeyGenerationParameters(SecureRandom random, X9ECParameters params, Digest digest, byte[] id) { @@ -38,36 +84,73 @@ public ECCSIKeyGenerationParameters(SecureRandom random, X9ECParameters params, this.kpak = G.multiply(ksak).normalize(); } + /** + * Returns a copy of the identifier used in these parameters. + * + * @return a byte array containing the identifier. + */ public byte[] getId() { - return id; + return Arrays.clone(id); } + /** + * Returns the public key component (kpak) corresponding to the secret key. + * + * @return the public key point. + */ public ECPoint getKPAK() { return kpak; } + /** + * Computes the session secret key (SSK) by adding the provided value to the secret key component + * and reducing modulo the curve order. + * + * @param hs_v a BigInteger value (typically derived from a hash) to be added to the secret. + * @return the computed session secret key. + */ public BigInteger computeSSK(BigInteger hs_v) { return ksak.add(hs_v).mod(q); } + /** + * Returns the order of the elliptic curve. + * + * @return the curve order. + */ public BigInteger getQ() { return q; } + /** + * Returns the generator (base point) of the elliptic curve. + * + * @return the generator point. + */ public ECPoint getG() { return G; } + /** + * Returns the digest algorithm used for key generation. + * + * @return the digest. + */ public Digest getDigest() { return digest; } + /** + * Returns the bit length used in key generation. + * + * @return the bit length. + */ public int getN() { return n; diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java index 17ee88614a..bc37f5f839 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPrivateKeyParameters.java @@ -2,12 +2,47 @@ import java.math.BigInteger; +/** + * Represents the private key parameters for the Elliptic Curve-based Certificateless + * Signature Infrastructure (ECCSI) scheme as defined in RFC 6507. + * + *

+ * This class encapsulates the secret signing key (SSK) used in ECCSI. The SSK is generated by + * the Key Management Service (KMS) and is a random integer modulo the order of the elliptic curve. + * It is paired with the corresponding public key parameters, represented by an instance of + * {@link ECCSIPublicKeyParameters}, to form the complete key material required for generating + * and verifying ECCSI signatures without the use of traditional certificates. + *

+ * + *

+ * Per RFC 6507 Section 5.1: + *

    + *
  • The SSK is generated as a random value in the appropriate range.
  • + *
  • It is used in conjunction with the public validation token (PVT) to perform signature + * operations.
  • + *
  • The combination of the SSK and the public key parameters enables certificateless + * signature generation and verification.
  • + *
+ *

+ * + * @see ECCSIPublicKeyParameters + * @see + * RFC 6507: Elliptic Curve-Based Certificateless Signatures for Identity-Based Encryption (ECCSI) + * + */ public class ECCSIPrivateKeyParameters extends AsymmetricKeyParameter { private final BigInteger ssk; private final ECCSIPublicKeyParameters pub; + /** + * Constructs {@code ECCSIPrivateKeyParameters} with the specified secret signing key + * and associated public key parameters. + * + * @param ssk the secret signing key (SSK) as a BigInteger. + * @param pub the corresponding public key parameters, which encapsulate the public validation token. + */ public ECCSIPrivateKeyParameters(BigInteger ssk, ECCSIPublicKeyParameters pub) { super(true); @@ -15,11 +50,21 @@ public ECCSIPrivateKeyParameters(BigInteger ssk, ECCSIPublicKeyParameters pub) this.pub = pub; } + /** + * Returns the public key parameters associated with this private key. + * + * @return the {@link ECCSIPublicKeyParameters} containing the public validation token (PVT). + */ public ECCSIPublicKeyParameters getPublicKeyParameters() { return pub; } + /** + * Returns the secret signing key (SSK) used in ECCSI. + * + * @return the SSK as a BigInteger. + */ public BigInteger getSSK() { return ssk; diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java index 19bfc43b8f..15269a0b46 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIPublicKeyParameters.java @@ -2,17 +2,47 @@ import org.bouncycastle.math.ec.ECPoint; +/** + * Represents the public key parameters for the Elliptic Curve-based Certificateless + * Signature Infrastructure (ECCSI) scheme as defined in RFC 6507. + *

+ * This class encapsulates the Public Validation Token (PVT) required for verifying + * ECCSI signatures. The PVT is cryptographically bound to a user's identity and + * generated by the Key Management Service (KMS) as part of the key material. + * + *

Per RFC 6507 Section 5.1: + *

    + *
  • The PVT is derived from the user's identity and KMS secret material
  • + *
  • Used during signature verification to validate the signer's identity
  • + *
  • Does not require certificates for authentication
  • + *
+ * + * @see RFC 6507: Elliptic Curve-Based Certificateless + * * Signatures for Identity-Based Encryption (ECCSI) + */ + public class ECCSIPublicKeyParameters extends AsymmetricKeyParameter { private final ECPoint pvt; + /** + * Constructs {@code ECCSIPublicKeyParameters} with the provided Public Validation Token (PVT). + */ public ECCSIPublicKeyParameters(ECPoint pvt) { super(false); this.pvt = pvt; } + /** + * Returns the Public Validation Token (PVT) for signature verification. + *

+ * The PVT is used in conjunction with the KMS Public Authentication Key (KPAK) + * to verify signatures per RFC 6507 Section 5.2.2. + * + * @return The PVT as an elliptic curve point in uncompressed format + */ public final ECPoint getPVT() { return pvt; diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java index c825971f04..98df43fc99 100644 --- a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java +++ b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java @@ -20,14 +20,6 @@ /** * Implementation of Elliptic Curve-based Certificateless Signatures for Identity-Based Encryption (ECCSI) * as defined in RFC 6507. - *

- * This class handles both signature generation and verification using the ECCSI scheme. It supports: - *

    - *
  • NIST P-256 (secp256r1) elliptic curve parameters
  • - *
  • SHA-256 hash function
  • - *
  • Certificateless signatures using KMS Public Authentication Key (KPAK)
  • - *
  • Identity-based signatures with Secret Signing Key (SSK)
  • - *
* * @see RFC 6507: Elliptic Curve-Based Certificateless * Signatures for Identity-Based Encryption (ECCSI) @@ -48,7 +40,12 @@ public class ECCSISigner private boolean forSigning; private final int N; - + /** + * Constructs an ECCSI signer/verifier with KMS Public Authentication Key and user identity. + * + * @param kpak KMS Public Authentication Key (KPAK) from RFC 6507 Section 2 + * @param id User identity byte array formatted + */ public ECCSISigner(ECPoint kpak, X9ECParameters params, Digest digest, byte[] id) { this.kpak = kpak; @@ -60,6 +57,15 @@ public ECCSISigner(ECPoint kpak, X9ECParameters params, Digest digest, byte[] id this.N = (params.getCurve().getOrder().bitLength() + 7) >> 3; } + /** + * Initializes the signer for either signature generation or verification. + * + * @param forSigning true for signing, false for verification + * @param param Key parameters: + * - For signing: {@code ParametersWithRandom} containing {@code ECCSIPrivateKeyParameters} + * - For verification: {@code ECCSIPublicKeyParameters} + * @throws IllegalArgumentException if invalid parameters are provided + */ @Override public void init(boolean forSigning, CipherParameters param) { @@ -94,6 +100,17 @@ public void update(byte[] in, int off, int len) } } + /** + * Generates an ECCSI signature according to RFC 6507 Section 5.2.1. + * + * @return Signature structure containing: + * - r (N bytes) + * - s (N bytes) + * - PVT (Public Validation Token) + * @throws CryptoException if cryptographic operations fail + * @throws DataLengthException if input data is invalid + * @throws IllegalArgumentException if invalid SSK or j parameter is detected + */ @Override public byte[] generateSignature() throws CryptoException, DataLengthException @@ -116,6 +133,13 @@ public byte[] generateSignature() params.getPublicKeyParameters().getPVT().getEncoded(false)); } + /** + * Verifies an ECCSI signature according to RFC 6507 Section 5.2.2. + * + * @param signature Signature to verify (r || s || PVT) + * @return true if signature is valid, false otherwise + * @throws IllegalArgumentException if signature format is invalid + */ @Override public boolean verifySignature(byte[] signature) { @@ -141,6 +165,13 @@ public boolean verifySignature(byte[] signature) return rComputed.mod(q).equals(r.mod(q)); } + /** + * Resets the signer/verifier state and performs initial computations: + * - For signing: Validates KPAK consistency (RFC 6507 Section 5.1.2) + * - For verification: Computes Y = HS·PVT + KPAK + * + * Also computes HS = hash(G || KPAK || ID || PVT) as per RFC 6507 Section 5.1.1 + */ @Override public void reset() { diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java index dc6018444f..7559cf2a90 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ECCSISignerTest.java @@ -6,8 +6,13 @@ import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.AsconHash256; +import org.bouncycastle.crypto.digests.MD5Digest; +import org.bouncycastle.crypto.digests.SHA224Digest; import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.digests.SHA3Digest; import org.bouncycastle.crypto.digests.SHA512Digest; +import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.generators.ECCSIKeyPairGenerator; import org.bouncycastle.crypto.params.ECCSIKeyGenerationParameters; @@ -59,6 +64,18 @@ public class ECCSISignerTest "sm2p256v1" }; + Digest[] digests = new Digest[]{ + new SHA256Digest(), + new SHA3Digest(), + new SHA3Digest(512), + new SHA224Digest(), + new SHA512Digest(), + new AsconHash256(), + new SHAKEDigest(256), + new SHAKEDigest(128), + new MD5Digest() + }; + public static void main(String[] args) throws Exception @@ -80,8 +97,10 @@ public void performTest() testTestVector(); for (int i = 0; i < curveNames.length; ++i) { - //System.out.println(curveNames[i]); - testRandom(curveNames[i]); + for (int j = 0; j < digests.length; ++j) + { + testRandom(curveNames[i], digests[j]); + } } } @@ -128,14 +147,13 @@ private void testTestVector() isTrue(signer.verifySignature(sig)); } - private void testRandom(String curveName) + private void testRandom(String curveName, Digest digest) throws Exception { SecureRandom random = new SecureRandom(); ECCSIKeyPairGenerator generator = new ECCSIKeyPairGenerator(); byte[] id = new byte[16]; random.nextBytes(id); - Digest digest = new SHA512Digest(); X9ECParameters params = CustomNamedCurves.getByName(curveName); ECCSIKeyGenerationParameters keyGenerationParameters = new ECCSIKeyGenerationParameters(random, params, digest, id); From cd39549e5e06e087fd2059f2c537d73355486534 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 20 Feb 2025 13:58:11 +1030 Subject: [PATCH 180/890] Add java doc --- .../crypto/generators/ECCSIKeyPairGenerator.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java index bfe73a514f..49b5c2f4a3 100644 --- a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java @@ -15,6 +15,15 @@ import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; import org.bouncycastle.math.ec.ECPoint; +/** + * A key pair generator for the ECCSI scheme (Elliptic Curve-based Certificateless Signatures + * for Identity-based Encryption) as defined in RFC 6507. + * + * @see + * RFC 6507: Elliptic Curve-Based Certificateless Signatures for Identity-based Encryption (ECCSI) + * + */ + public class ECCSIKeyPairGenerator implements AsymmetricCipherKeyPairGenerator { @@ -23,6 +32,12 @@ public class ECCSIKeyPairGenerator private Digest digest; private ECCSIKeyGenerationParameters parameters; + /** + * Initializes the key pair generator with the specified parameters. + * + * @param parameters an instance of {@link ECCSIKeyGenerationParameters} which encapsulates the elliptic + * curve domain parameters, the digest algorithm, and an associated identifier. + */ @Override public void init(KeyGenerationParameters parameters) { From 4bfc1d72d1e482ca623418d2976f21dfe6472f3a Mon Sep 17 00:00:00 2001 From: gefeili Date: Sun, 9 Mar 2025 18:57:37 +1030 Subject: [PATCH 181/890] change new BigInteger(n, random) to BigIntegers.createRandomBigInteger --- .../bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java | 3 ++- .../java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java | 2 +- .../crypto/params/ECCSIKeyGenerationParameters.java | 3 ++- .../main/java/org/bouncycastle/crypto/signers/ECCSISigner.java | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java index 49b5c2f4a3..321c020abd 100644 --- a/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/generators/ECCSIKeyPairGenerator.java @@ -14,6 +14,7 @@ import org.bouncycastle.crypto.params.ECCSIPrivateKeyParameters; import org.bouncycastle.crypto.params.ECCSIPublicKeyParameters; import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.BigIntegers; /** * A key pair generator for the ECCSI scheme (Elliptic Curve-based Certificateless Signatures @@ -57,7 +58,7 @@ public AsymmetricCipherKeyPair generateKeyPair() byte[] id = parameters.getId(); ECPoint kpak = parameters.getKPAK(); // 1) Choose v, a random (ephemeral) non-zero element of F_q; - BigInteger v = new BigInteger(256, random).mod(q); + BigInteger v = BigIntegers.createRandomBigInteger(256, random).mod(q); // 2) Compute PVT = [v]G ECPoint pvt = G.multiply(v).normalize(); diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index 9bb956d4c1..eb14f47b04 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -78,7 +78,7 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip Digest digest = keyParameters.getDigest(); // 1. Generate random SSV in range [0, 2^n - 1] - BigInteger ssv = new BigInteger(n, random); + BigInteger ssv = BigIntegers.createRandomBigInteger(n, random); // 2. Compute r = HashToIntegerRange(SSV || b, q) BigInteger r = hashToIntegerRange(Arrays.concatenate(ssv.toByteArray(), b.toByteArray()), q, digest); diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java index 3b62709c19..4df70d28bd 100644 --- a/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java +++ b/core/src/main/java/org/bouncycastle/crypto/params/ECCSIKeyGenerationParameters.java @@ -8,6 +8,7 @@ import org.bouncycastle.crypto.KeyGenerationParameters; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.BigIntegers; /** * Parameters for ECCSI key generation. @@ -80,7 +81,7 @@ public ECCSIKeyGenerationParameters(SecureRandom random, X9ECParameters params, this.digest = digest; this.id = Arrays.clone(id); this.n = params.getCurve().getA().bitLength(); - this.ksak = new BigInteger(n, random).mod(q); + this.ksak = BigIntegers.createRandomBigInteger(n, random).mod(q); this.kpak = G.multiply(ksak).normalize(); } diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java index 98df43fc99..bec1461a8a 100644 --- a/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java +++ b/core/src/main/java/org/bouncycastle/crypto/signers/ECCSISigner.java @@ -189,7 +189,7 @@ public void reset() { ECCSIPrivateKeyParameters parameters = (ECCSIPrivateKeyParameters)param; pvt = parameters.getPublicKeyParameters().getPVT(); - j = new BigInteger(q.bitLength(), random); + j = BigIntegers.createRandomBigInteger(q.bitLength(), random); ECPoint J = G.multiply(j).normalize(); r = J.getAffineXCoord().toBigInteger().mod(q); From f088322676edc0af646490b7b578957b9508b0d1 Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 10 Mar 2025 10:08:59 +1030 Subject: [PATCH 182/890] TODO: AES CTR issue --- .../pqc/crypto/snova/MapGroup1.java | 34 ++++++++------- .../crypto/snova/SnovaKeyPairGenerator.java | 43 ++++++++++++++++--- 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java index 0f0caea2a4..cab068f328 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -26,38 +26,42 @@ public MapGroup1(SnovaParameters params) qAlpha2 = new byte[m][alpha][16]; } - public int decode(byte[] input) + public int decode(byte[] input, int len) { - int inOff = decodeP(input, 0, p11); - inOff = decodeP(input, inOff, p12); - inOff = decodeP(input, inOff, p21); - inOff = decodeAlpha(input, inOff, aAlpha); - inOff = decodeAlpha(input, inOff, bAlpha); - inOff = decodeAlpha(input, inOff, qAlpha1); - inOff = decodeAlpha(input, inOff, qAlpha2); + int inOff = decodeP(input, 0, p11, len); + inOff += decodeP(input, inOff, p12, len - inOff); + inOff += decodeP(input, inOff, p21, len - inOff); + inOff += decodeAlpha(input, inOff, aAlpha, len - inOff); + inOff += decodeAlpha(input, inOff, bAlpha, len - inOff); + inOff += decodeAlpha(input, inOff, qAlpha1, len - inOff); + inOff += decodeAlpha(input, inOff, qAlpha2, len - inOff); return inOff; } - private int decodeP(byte[] input, int inOff, byte[][][][] p) + private int decodeP(byte[] input, int inOff, byte[][][][] p, int len) { + int rlt = 0; for (int i = 0; i < p.length; ++i) { - inOff = decodeAlpha(input, inOff, p[i]); + rlt += decodeAlpha(input, inOff + rlt, p[i], len); } - return inOff; + return rlt; } - private int decodeAlpha(byte[] input, int inOff, byte[][][] alpha) + private int decodeAlpha(byte[] input, int inOff, byte[][][] alpha, int len) { + int rlt = 0; for (int i = 0; i < alpha.length; ++i) { for (int j = 0; j < alpha[i].length; ++j) { - GF16Utils.decode(input, inOff, alpha[i][j], 0, alpha[i][j].length); - inOff += (alpha[i][j].length + 1) >> 1; + int tmp = Math.min(alpha[i][j].length, len << 1); + GF16Utils.decode(input, inOff + rlt, alpha[i][j], 0, tmp); + rlt += (tmp + 1) >> 1; + len -= (tmp + 1) >> 1; } } - return inOff; + return rlt; } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index 81d814ad0f..a2c2a51a62 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -5,10 +5,14 @@ import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.KeyGenerationParameters; import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.modes.CTRModeCipher; +import org.bouncycastle.crypto.modes.SICBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.util.Arrays; public class SnovaKeyPairGenerator @@ -161,7 +165,7 @@ private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[ genSeedsAndT12(keyElements.T12, skSeed); // Generate map components -// genABQP(keyElements.map1, pkSeed); + genABQP(keyElements.map1, pkSeed); // // // Generate F matrices // genF(keyElements.map2, keyElements.map1, keyElements.T12); @@ -210,7 +214,8 @@ private void genABQP(MapGroup1 map1, byte[] pkSeed) int n = v + o; int gf16sPrngPublic = lsq * (2 * m * alpha + m * (n * n - m * m)) + l * 2 * m * alpha; - byte[] prngOutput = new byte[gf16sPrngPublic]; + byte[] qTemp = new byte[(m * alpha * 16 + m * alpha * 16) / l]; + byte[] prngOutput = new byte[(gf16sPrngPublic + 1) >> 1]; if (params.isPkExpandShake()) { @@ -221,19 +226,45 @@ private void genABQP(MapGroup1 map1, byte[] pkSeed) } else { + // Create a 16-byte IV (all zeros) + byte[] iv = new byte[16]; // automatically zero-initialized // AES-CTR-based expansion - AESEngine aes = new AESEngine(); - aes.init(true, new KeyParameter(pkSeed)); + // Set up AES engine in CTR (SIC) mode. + BlockCipher aesEngine = AESEngine.newInstance(); + // SICBlockCipher implements CTR mode for AES. + CTRModeCipher ctrCipher = SICBlockCipher.newInstance(aesEngine); + ParametersWithIV params = new ParametersWithIV(new KeyParameter(pkSeed), iv); + ctrCipher.init(true, params); + int blockSize = ctrCipher.getBlockSize(); // typically 16 bytes + byte[] zeroBlock = new byte[blockSize]; // block of zeros + byte[] blockOut = new byte[blockSize]; + + int offset = 0; + // Process full blocks + while (offset + blockSize <= prngOutput.length) + { + ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); + System.arraycopy(blockOut, 0, prngOutput, offset, blockSize); + offset += blockSize; + } + // Process any remaining partial block. + if (offset < prngOutput.length) + { + ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); + int remaining = prngOutput.length - offset; + System.arraycopy(blockOut, 0, prngOutput, offset, remaining); + } + for (int i = 0; i < prngOutput.length; i += 16) { byte[] block = new byte[16]; - aes.processBlock(block, 0, block, 0); + ctrCipher.processBlock(block, 0, block, 0); System.arraycopy(block, 0, prngOutput, i, Math.min(16, prngOutput.length - i)); } } // Convert bytes to GF16 structures -// int inOff = map1.decode(prngOutput); + int inOff = map1.decode(prngOutput, (gf16sPrngPublic - qTemp.length) >> 1); // // // Post-processing for invertible matrices // for (GF16Matrix matrix : map1.Aalpha) From 7dadecd49cf1fe82a3ae1263e760e6e94dba196f Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 10 Mar 2025 16:03:52 +1100 Subject: [PATCH 183/890] removed use of JUnit from simple test. --- .../java/org/bouncycastle/crypto/test/SAKKEKEMSTest.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SAKKEKEMSTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SAKKEKEMSTest.java index fa9d73f1e4..d21555274d 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/SAKKEKEMSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SAKKEKEMSTest.java @@ -3,7 +3,6 @@ import java.math.BigInteger; import java.security.SecureRandom; - import org.bouncycastle.crypto.SecretWithEncapsulation; import org.bouncycastle.crypto.kems.SAKKEKEMExtractor; import org.bouncycastle.crypto.kems.SAKKEKEMSGenerator; @@ -15,7 +14,6 @@ import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.FixedSecureRandom; import org.bouncycastle.util.test.SimpleTest; -import org.junit.Assert; public class SAKKEKEMSTest extends SimpleTest @@ -148,7 +146,7 @@ private void testTestVector() ECPoint P = curve.createPoint(Px, Py); ECPoint computed_Z = P.multiply(z).normalize(); - Assert.assertTrue(computed_Z.equals(curve.createPoint(Zx, Zy))); + isTrue(computed_Z.equals(curve.createPoint(Zx, Zy))); SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(ssv)}); SAKKEPublicKeyParameters b_publicKey = new SAKKEPublicKeyParameters(new BigInteger(b), curve.createPoint(Zx, Zy)); @@ -158,7 +156,7 @@ private void testTestVector() SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(new SAKKEPrivateKeyParameters(z, b_publicKey)); byte[] test = extractor.extractSecret(rlt.getEncapsulation()); - Assert.assertTrue(Arrays.areEqual(test, ssv)); + isTrue(Arrays.areEqual(test, ssv)); } private void testRandom() @@ -172,6 +170,6 @@ private void testRandom() SecretWithEncapsulation rlt = generator.generateEncapsulated(b_pub); SAKKEKEMExtractor extractor = new SAKKEKEMExtractor(b_priv); byte[] test = extractor.extractSecret(rlt.getEncapsulation()); - Assert.assertTrue(Arrays.areEqual(test, ssv)); + isTrue(Arrays.areEqual(test, ssv)); } } From a5c27a408d0bffe81cd39640b710a0cc17a8c5e6 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 10 Mar 2025 12:59:35 +0700 Subject: [PATCH 184/890] Cleanup array comparisons in tests - see https://github.com/bcgit/bc-java/pull/2012 --- .../jce/provider/test/DESedeTest.java | 59 ++++--------------- .../jce/provider/test/FIPSDESTest.java | 28 ++------- .../jce/provider/test/PSSTest.java | 29 ++------- .../jce/provider/test/RSATest.java | 34 +++-------- .../jce/provider/test/PSSTest.java | 26 +------- .../jce/provider/test/DHTest.java | 24 +------- .../jce/provider/test/PSSTest.java | 30 ++-------- 7 files changed, 36 insertions(+), 194 deletions(-) diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/DESedeTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/DESedeTest.java index 684fb17335..416e0cb55b 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/DESedeTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/DESedeTest.java @@ -19,6 +19,7 @@ import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -79,50 +80,11 @@ public String getName() return "DESEDE"; } - private boolean equalArray( - byte[] a, - byte[] b) + private static boolean equalPrefix(byte[] a, byte[] b, int length) { - if (a.length != b.length) - { - return false; - } - - for (int i = 0; i != a.length; i++) - { - if (a[i] != b[i]) - { - return false; - } - } - - return true; - } - - private boolean equalArray( - byte[] a, - byte[] b, - int length) - { - if (a.length < length) - { - return false; - } - - if (b.length < length) - { - return false; - } - - for (int i = 0; i != length; i++) - { - if (a[i] != b[i]) - { - return false; - } - } - - return true; + return a.length >= length + && b.length >= length + && Arrays.areEqual(a, 0, length, b, 0, length); } private void wrapTest( @@ -142,7 +104,7 @@ private void wrapTest( try { byte[] cText = wrapper.wrap(new SecretKeySpec(in, alg)); - if (!equalArray(cText, out)) + if (!Arrays.areEqual(cText, out)) { fail("failed wrap test " + id + " expected " + new String(Hex.encode(out)) + " got " + new String(Hex.encode(cText))); } @@ -157,7 +119,7 @@ private void wrapTest( try { Key pText = wrapper.unwrap(out, alg, Cipher.SECRET_KEY); - if (!equalArray(pText.getEncoded(), in)) + if (!Arrays.areEqual(pText.getEncoded(), in)) { fail("failed unwrap test " + id + " expected " + new String(Hex.encode(in)) + " got " + new String(Hex.encode(pText.getEncoded()))); } @@ -242,7 +204,7 @@ public void test( bytes = bOut.toByteArray(); - if (!equalArray(bytes, output)) + if (!Arrays.areEqual(bytes, output)) { fail(alg + " failed encryption - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(bytes))); } @@ -265,13 +227,14 @@ public void test( bytes[i] = (byte)dIn.read(); } dIn.readFully(bytes, input.length / 2, bytes.length - input.length / 2); + dIn.close(); } catch (Exception e) { fail(alg + " failed encryption - " + e.toString()); } - if (!equalArray(bytes, input)) + if (!Arrays.areEqual(bytes, input)) { fail(alg + " failed decryption - expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(bytes))); } @@ -284,7 +247,7 @@ public void test( SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(alg, "BC"); DESedeKeySpec keySpec = (DESedeKeySpec)keyFactory.getKeySpec((SecretKey)key, DESedeKeySpec.class); - if (!equalArray(key.getEncoded(), keySpec.getKey(), 16)) + if (!equalPrefix(key.getEncoded(), keySpec.getKey(), 16)) { fail(alg + " KeySpec does not match key."); } diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/FIPSDESTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/FIPSDESTest.java index 80024d458a..f9af706d6c 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/FIPSDESTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/FIPSDESTest.java @@ -16,6 +16,7 @@ import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTestResult; import org.bouncycastle.util.test.Test; @@ -54,26 +55,6 @@ public String getName() return "FIPSDESTest"; } - private boolean equalArray( - byte[] a, - byte[] b) - { - if (a.length != b.length) - { - return false; - } - - for (int i = 0; i != a.length; i++) - { - if (a[i] != b[i]) - { - return false; - } - } - - return true; - } - public TestResult test( String algorithm, byte[] input, @@ -89,8 +70,6 @@ public TestResult test( try { - String baseAlgorithm; - key = new SecretKeySpec(Hex.decode("0123456789abcdef"), "DES"); in = Cipher.getInstance(algorithm, "BC"); @@ -151,7 +130,7 @@ public TestResult test( bytes = bOut.toByteArray(); - if (!equalArray(bytes, output)) + if (!Arrays.areEqual(bytes, output)) { return new SimpleTestResult(false, getName() + ": " + algorithm + " failed encryption - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(bytes))); } @@ -174,13 +153,14 @@ public TestResult test( bytes[i] = (byte)dIn.read(); } dIn.readFully(bytes, input.length / 2, bytes.length - input.length / 2); + dIn.close(); } catch (Exception e) { return new SimpleTestResult(false, getName() + ": " + algorithm + " failed encryption - " + e.toString()); } - if (!equalArray(bytes, input)) + if (!Arrays.areEqual(bytes, input)) { return new SimpleTestResult(false, getName() + ": " + algorithm + " failed decryption - expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(bytes))); } diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/PSSTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/PSSTest.java index 75931bf195..462c06d5d7 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/PSSTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/PSSTest.java @@ -47,27 +47,6 @@ public void nextBytes( } } - private boolean arrayEquals( - byte[] a, - byte[] b) - { - if (a.length != b.length) - { - return false; - } - - for (int i = 0; i != a.length; i++) - { - if (a[i] != b[i]) - { - return false; - } - } - - return true; - } - - private RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec( new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137",16), new BigInteger("010001",16)); @@ -109,7 +88,7 @@ public void performTest() throws Exception s.update(msg1a); byte[] sig = s.sign(); - if (!arrayEquals(sig1a, sig)) + if (!Arrays.areEqual(sig1a, sig)) { fail("PSS Sign test expected " + new String(Hex.encode(sig1a)) + " got " + new String(Hex.encode(sig))); } @@ -135,7 +114,7 @@ public void performTest() throws Exception } AlgorithmParameters pss = s.getParameters(); - if (!arrayEquals(pss.getEncoded(), new byte[] { 0x30, 0x00 })) + if (!Arrays.areEqual(pss.getEncoded(), new byte[]{ 0x30, 0x00 })) { fail("failed default encoding test."); } @@ -148,7 +127,7 @@ public void performTest() throws Exception pss = s.getParameters(); - if (!arrayEquals(sig1b, sig)) + if (!Arrays.areEqual(sig1b, sig)) { fail("PSS Sign test expected " + new String(Hex.encode(sig1b)) + " got " + new String(Hex.encode(sig))); } @@ -251,7 +230,7 @@ public void performTest() throws Exception pss = s.getParameters(); - if (!arrayEquals(sig1c, sig)) + if (!Arrays.areEqual(sig1c, sig)) { fail("PSS Sign test expected " + new String(Hex.encode(sig1c)) + " got " + new String(Hex.encode(sig))); } diff --git a/prov/src/test/jdk1.1/org/bouncycastle/jce/provider/test/RSATest.java b/prov/src/test/jdk1.1/org/bouncycastle/jce/provider/test/RSATest.java index e756f401a5..16e36be2d9 100644 --- a/prov/src/test/jdk1.1/org/bouncycastle/jce/provider/test/RSATest.java +++ b/prov/src/test/jdk1.1/org/bouncycastle/jce/provider/test/RSATest.java @@ -12,6 +12,7 @@ import javax.crypto.Cipher; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTestResult; import org.bouncycastle.util.test.Test; @@ -50,27 +51,6 @@ public void nextBytes( } } - private boolean arrayEquals( - byte[] a, - byte[] b) - { - if (a.length != b.length) - { - return false; - } - - for (int i = 0; i != a.length; i++) - { - if (a[i] != b[i]) - { - return false; - } - } - - return true; - } - - private RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec( new BigInteger("b4a7e46170574f16a97082b22be58b6a2a629798419be12872a4bdba626cfae9900f76abfb12139dce5de56564fab2b6543165a040c606887420e33d91ed7ed7", 16), new BigInteger("11", 16)); @@ -115,7 +95,7 @@ public TestResult perform() byte[] out = c.doFinal(input); - if (!arrayEquals(out, output[0])) + if (!Arrays.areEqual(out, output[0])) { return new SimpleTestResult(false, "NoPadding test failed on encrypt expected " + new String(Hex.encode(output[0])) + " got " + new String(Hex.encode(out))); } @@ -124,7 +104,7 @@ public TestResult perform() out = c.doFinal(out); - if (!arrayEquals(out, input)) + if (!Arrays.areEqual(out, input)) { return new SimpleTestResult(false, "NoPadding test failed on decrypt expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(out))); } @@ -138,7 +118,7 @@ public TestResult perform() out = c.doFinal(input); - if (!arrayEquals(out, output[1])) + if (!Arrays.areEqual(out, output[1])) { return new SimpleTestResult(false, "PKCS1 test failed on encrypt expected " + new String(Hex.encode(output[1])) + " got " + new String(Hex.encode(out))); } @@ -147,7 +127,7 @@ public TestResult perform() out = c.doFinal(out); - if (!arrayEquals(out, input)) + if (!Arrays.areEqual(out, input)) { return new SimpleTestResult(false, "PKCS1 test failed on decrypt expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(out))); } @@ -161,7 +141,7 @@ public TestResult perform() out = c.doFinal(input); - if (!arrayEquals(out, output[2])) + if (!Arrays.areEqual(out, output[2])) { return new SimpleTestResult(false, "OAEP test failed on encrypt expected " + new String(Hex.encode(output[2])) + " got " + new String(Hex.encode(out))); } @@ -170,7 +150,7 @@ public TestResult perform() out = c.doFinal(out); - if (!arrayEquals(out, input)) + if (!Arrays.areEqual(out, input)) { return new SimpleTestResult(false, "OAEP test failed on decrypt expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(out))); } diff --git a/prov/src/test/jdk1.3/org/bouncycastle/jce/provider/test/PSSTest.java b/prov/src/test/jdk1.3/org/bouncycastle/jce/provider/test/PSSTest.java index bd1dc32f35..f0d567501a 100644 --- a/prov/src/test/jdk1.3/org/bouncycastle/jce/provider/test/PSSTest.java +++ b/prov/src/test/jdk1.3/org/bouncycastle/jce/provider/test/PSSTest.java @@ -11,6 +11,7 @@ import java.security.spec.RSAPublicKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTestResult; import org.bouncycastle.util.test.Test; @@ -38,27 +39,6 @@ public void nextBytes( } } - private boolean arrayEquals( - byte[] a, - byte[] b) - { - if (a.length != b.length) - { - return false; - } - - for (int i = 0; i != a.length; i++) - { - if (a[i] != b[i]) - { - return false; - } - } - - return true; - } - - private RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec( new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137",16), new BigInteger("010001",16)); @@ -98,7 +78,7 @@ public TestResult perform() s.update(msg1a); byte[] sig = s.sign(); - if (!arrayEquals(sig1a, sig)) + if (!Arrays.areEqual(sig1a, sig)) { return new SimpleTestResult(false, "PSS Sign test expected " + new String(Hex.encode(sig1a)) + " got " + new String(Hex.encode(sig))); } @@ -118,7 +98,7 @@ public TestResult perform() s.update(msg1a); sig = s.sign(); - if (!arrayEquals(sig1b, sig)) + if (!Arrays.areEqual(sig1b, sig)) { return new SimpleTestResult(false, "PSS Sign test expected " + new String(Hex.encode(sig1b)) + " got " + new String(Hex.encode(sig))); } diff --git a/prov/src/test/jdk1.4/org/bouncycastle/jce/provider/test/DHTest.java b/prov/src/test/jdk1.4/org/bouncycastle/jce/provider/test/DHTest.java index 19c30d6cef..b394a1572a 100644 --- a/prov/src/test/jdk1.4/org/bouncycastle/jce/provider/test/DHTest.java +++ b/prov/src/test/jdk1.4/org/bouncycastle/jce/provider/test/DHTest.java @@ -26,6 +26,7 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.jce.spec.ECParameterSpec; import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTestResult; import org.bouncycastle.util.test.Test; @@ -252,7 +253,7 @@ private TestResult testRandom( // a and a2 should be equivalent! byte[] encodeParams_2 = a2.getEncoded(); - if (!arrayEquals(encodeParams, encodeParams_2)) + if (!Arrays.areEqual(encodeParams, encodeParams_2)) { return new SimpleTestResult(false, this.getName() + ": encode/decode parameters failed"); } @@ -474,27 +475,6 @@ private TestResult testExceptions() return new SimpleTestResult(true, this.getName() + ": Okay"); } - - private boolean arrayEquals( - byte[] a, - byte[] b) - { - if (a.length != b.length) - { - return false; - } - - for (int i = 0; i != a.length; i++) - { - if (a[i] != b[i]) - { - return false; - } - } - - return true; - } - public TestResult perform() { diff --git a/prov/src/test/jdk1.4/org/bouncycastle/jce/provider/test/PSSTest.java b/prov/src/test/jdk1.4/org/bouncycastle/jce/provider/test/PSSTest.java index 3cba08eb79..672a6b9760 100644 --- a/prov/src/test/jdk1.4/org/bouncycastle/jce/provider/test/PSSTest.java +++ b/prov/src/test/jdk1.4/org/bouncycastle/jce/provider/test/PSSTest.java @@ -15,6 +15,7 @@ import java.security.spec.RSAPublicKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTestResult; import org.bouncycastle.util.test.Test; @@ -41,27 +42,6 @@ public void nextBytes( } } - private boolean arrayEquals( - byte[] a, - byte[] b) - { - if (a.length != b.length) - { - return false; - } - - for (int i = 0; i != a.length; i++) - { - if (a[i] != b[i]) - { - return false; - } - } - - return true; - } - - private RSAPublicKeySpec pubKeySpec = new RSAPublicKeySpec( new BigInteger("a56e4a0e701017589a5187dc7ea841d156f2ec0e36ad52a44dfeb1e61f7ad991d8c51056ffedb162b4c0f283a12a88a394dff526ab7291cbb307ceabfce0b1dfd5cd9508096d5b2b8b6df5d671ef6377c0921cb23c270a70e2598e6ff89d19f105acc2d3f0cb35f29280e1386b6f64c4ef22e1e1f20d0ce8cffb2249bd9a2137",16), new BigInteger("010001",16)); @@ -103,7 +83,7 @@ public TestResult perform() s.update(msg1a); byte[] sig = s.sign(); - if (!arrayEquals(sig1a, sig)) + if (!Arrays.areEqual(sig1a, sig)) { return new SimpleTestResult(false, "PSS Sign test expected " + new String(Hex.encode(sig1a)) + " got " + new String(Hex.encode(sig))); } @@ -129,7 +109,7 @@ public TestResult perform() } AlgorithmParameters pss = s.getParameters(); - if (!arrayEquals(pss.getEncoded(), new byte[] { 0x30, 0x00 })) + if (!Arrays.areEqual(pss.getEncoded(), new byte[] { 0x30, 0x00 })) { return new SimpleTestResult(false, "failed default encoding test."); } @@ -142,7 +122,7 @@ public TestResult perform() pss = s.getParameters(); - if (!arrayEquals(sig1b, sig)) + if (!Arrays.areEqual(sig1b, sig)) { return new SimpleTestResult(false, "PSS Sign test expected " + new String(Hex.encode(sig1b)) + " got " + new String(Hex.encode(sig))); } @@ -171,7 +151,7 @@ public TestResult perform() pss = s.getParameters(); - if (!arrayEquals(sig1c, sig)) + if (!Arrays.areEqual(sig1c, sig)) { return new SimpleTestResult(false, "PSS Sign test expected " + new String(Hex.encode(sig1c)) + " got " + new String(Hex.encode(sig))); } From bffd6a7e90aedd40653146557d0b527eef2ac9d7 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 10 Mar 2025 13:27:03 +0700 Subject: [PATCH 185/890] TLS: Update ML-KEM codepoints - draft-connolly-tls-mlkem-key-agreement-05 --- tls/src/main/java/org/bouncycastle/tls/NamedGroup.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java b/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java index b8fc46d1c5..a69ecd1bc9 100644 --- a/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java +++ b/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java @@ -110,11 +110,11 @@ public class NamedGroup public static final int OQS_mlkem1024 = 0x0249; /* - * draft-connolly-tls-mlkem-key-agreement-03 + * draft-connolly-tls-mlkem-key-agreement-05 */ - public static final int MLKEM512 = 0x0512; - public static final int MLKEM768 = 0x0768; - public static final int MLKEM1024 = 0x1024; + public static final int MLKEM512 = 0x0200; + public static final int MLKEM768 = 0x0201; + public static final int MLKEM1024 = 0x0202; /* Names of the actual underlying elliptic curves (not necessarily matching the NamedGroup names). */ private static final String[] CURVE_NAMES = new String[]{ "sect163k1", "sect163r1", "sect163r2", "sect193r1", From 0f08ecdd242eb13c63a52521ca9fb4957fbae3b4 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 10 Mar 2025 21:26:27 +1100 Subject: [PATCH 186/890] added PKCS12 example using PBMac1 additional PQC tests added support for AES_WRAP_PAD to CMS. --- .../asn1/x509/PrivateKeyStatement.java | 91 +++++++++++++++++ .../asn1/x509/X509AttributeIdentifiers.java | 28 +++--- .../crmf/CertificateRepMessageBuilder.java | 1 + .../org/bouncycastle/cms/CMSAlgorithm.java | 3 + .../org/bouncycastle/cms/jcajce/CMSUtils.java | 9 +- .../bc/BcPKCS12PBMac1CalculatorBuilder.java | 10 +- .../bouncycastle/pkcs/test/PQCPKCS10Test.java | 97 +++++++++++++++++++ .../bouncycastle/pkcs/test/PfxPduTest.java | 67 +++++++++++++ .../pqc/jcajce/provider/util/WrapUtil.java | 4 +- .../jce/provider/test/PQCDHTest.java | 89 +++++++++++++++++ 10 files changed, 378 insertions(+), 21 deletions(-) create mode 100644 core/src/main/java/org/bouncycastle/asn1/x509/PrivateKeyStatement.java create mode 100644 pkix/src/test/java/org/bouncycastle/pkcs/test/PQCPKCS10Test.java create mode 100644 prov/src/test/java/org/bouncycastle/jce/provider/test/PQCDHTest.java diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/PrivateKeyStatement.java b/core/src/main/java/org/bouncycastle/asn1/x509/PrivateKeyStatement.java new file mode 100644 index 0000000000..f03c66886b --- /dev/null +++ b/core/src/main/java/org/bouncycastle/asn1/x509/PrivateKeyStatement.java @@ -0,0 +1,91 @@ +package org.bouncycastle.asn1.x509; + +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Object; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.pkcs.IssuerAndSerialNumber; + +/** + *
+ * PrivateKeyStatement ::= SEQUENCE {
+ *       signer  IssuerAndSerialNumber,
+ *       cert    Certificate OPTIONAL }
+ * 
+ */ +public class PrivateKeyStatement + extends ASN1Object +{ + private final IssuerAndSerialNumber signer; + private final Certificate cert; + + public static PrivateKeyStatement getInstance(Object obj) + { + if (obj instanceof PrivateKeyStatement) + { + return (PrivateKeyStatement)obj; + } + + if (obj != null) + { + return new PrivateKeyStatement(ASN1Sequence.getInstance(obj)); + } + + return null; + } + + private PrivateKeyStatement(ASN1Sequence seq) + { + if (seq.size() == 1) + { + this.signer = IssuerAndSerialNumber.getInstance(seq.getObjectAt(0)); + this.cert = null; + } + else if (seq.size() == 2) + { + this.signer = IssuerAndSerialNumber.getInstance(seq.getObjectAt(0)); + this.cert = Certificate.getInstance(seq.getObjectAt(1)); + } + else + { + throw new IllegalArgumentException("unknown sequence in PrivateKeyStatement"); + } + } + + public PrivateKeyStatement(IssuerAndSerialNumber signer) + { + this.signer = signer; + this.cert = null; + } + + public PrivateKeyStatement(Certificate cert) + { + this.signer = new IssuerAndSerialNumber(cert.getIssuer(), cert.getSerialNumber().getValue()); + this.cert = cert; + } + + public IssuerAndSerialNumber getSigner() + { + return signer; + } + + public Certificate getCert() + { + return cert; + } + + public ASN1Primitive toASN1Primitive() + { + ASN1EncodableVector v = new ASN1EncodableVector(2); + + v.add(signer); + + if (cert != null) + { + v.add(cert); + } + + return new DERSequence(v); + } +} diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/X509AttributeIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/x509/X509AttributeIdentifiers.java index 0ed12f7eb6..a64f721dc8 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/X509AttributeIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/X509AttributeIdentifiers.java @@ -7,23 +7,25 @@ public interface X509AttributeIdentifiers /** * @deprecated use id_at_role */ - static final ASN1ObjectIdentifier RoleSyntax = new ASN1ObjectIdentifier("2.5.4.72"); + ASN1ObjectIdentifier RoleSyntax = new ASN1ObjectIdentifier("2.5.4.72"); - static final ASN1ObjectIdentifier id_pe_ac_auditIdentity = X509ObjectIdentifiers.id_pe.branch("4"); - static final ASN1ObjectIdentifier id_pe_aaControls = X509ObjectIdentifiers.id_pe.branch("6"); - static final ASN1ObjectIdentifier id_pe_ac_proxying = X509ObjectIdentifiers.id_pe.branch("10"); + ASN1ObjectIdentifier id_pe_ac_auditIdentity = X509ObjectIdentifiers.id_pe.branch("4"); + ASN1ObjectIdentifier id_pe_aaControls = X509ObjectIdentifiers.id_pe.branch("6"); + ASN1ObjectIdentifier id_pe_ac_proxying = X509ObjectIdentifiers.id_pe.branch("10"); - static final ASN1ObjectIdentifier id_ce_targetInformation= X509ObjectIdentifiers.id_ce.branch("55"); + ASN1ObjectIdentifier id_ce_targetInformation = X509ObjectIdentifiers.id_ce.branch("55"); - static final ASN1ObjectIdentifier id_aca = X509ObjectIdentifiers.id_pkix.branch("10"); + ASN1ObjectIdentifier id_aca = X509ObjectIdentifiers.id_pkix.branch("10"); - static final ASN1ObjectIdentifier id_aca_authenticationInfo = id_aca.branch("1"); - static final ASN1ObjectIdentifier id_aca_accessIdentity = id_aca.branch("2"); - static final ASN1ObjectIdentifier id_aca_chargingIdentity = id_aca.branch("3"); - static final ASN1ObjectIdentifier id_aca_group = id_aca.branch("4"); + ASN1ObjectIdentifier id_aca_authenticationInfo = id_aca.branch("1"); + ASN1ObjectIdentifier id_aca_accessIdentity = id_aca.branch("2"); + ASN1ObjectIdentifier id_aca_chargingIdentity = id_aca.branch("3"); + ASN1ObjectIdentifier id_aca_group = id_aca.branch("4"); // { id-aca 5 } is reserved - static final ASN1ObjectIdentifier id_aca_encAttrs = id_aca.branch("6"); + ASN1ObjectIdentifier id_aca_encAttrs = id_aca.branch("6"); - static final ASN1ObjectIdentifier id_at_role = new ASN1ObjectIdentifier("2.5.4.72"); - static final ASN1ObjectIdentifier id_at_clearance = new ASN1ObjectIdentifier("2.5.1.5.55"); + ASN1ObjectIdentifier id_at_role = new ASN1ObjectIdentifier("2.5.4.72"); + ASN1ObjectIdentifier id_at_clearance = new ASN1ObjectIdentifier("2.5.1.5.55"); + + ASN1ObjectIdentifier id_at_privateKeyStatement = new ASN1ObjectIdentifier("1.3.6.1.4.1.22112.2.1"); } diff --git a/pkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRepMessageBuilder.java b/pkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRepMessageBuilder.java index e6eabf140e..0b913218e5 100644 --- a/pkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRepMessageBuilder.java +++ b/pkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRepMessageBuilder.java @@ -30,6 +30,7 @@ public CertificateRepMessageBuilder(X509CertificateHolder... caCerts) this.caCerts[i] = new CMPCertificate(caCerts[i].toASN1Structure()); } } + public CertificateRepMessageBuilder addCertificateResponse(CertificateResponse response) { responses.add(response.toASN1Structure()); diff --git a/pkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java b/pkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java index 103659c424..0418b5ac6f 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java +++ b/pkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java @@ -46,6 +46,9 @@ public class CMSAlgorithm public static final ASN1ObjectIdentifier AES128_WRAP = NISTObjectIdentifiers.id_aes128_wrap.intern(); public static final ASN1ObjectIdentifier AES192_WRAP = NISTObjectIdentifiers.id_aes192_wrap.intern(); public static final ASN1ObjectIdentifier AES256_WRAP = NISTObjectIdentifiers.id_aes256_wrap.intern(); + public static final ASN1ObjectIdentifier AES128_WRAP_PAD = NISTObjectIdentifiers.id_aes128_wrap_pad.intern(); + public static final ASN1ObjectIdentifier AES192_WRAP_PAD = NISTObjectIdentifiers.id_aes192_wrap_pad.intern(); + public static final ASN1ObjectIdentifier AES256_WRAP_PAD = NISTObjectIdentifiers.id_aes256_wrap_pad.intern(); public static final ASN1ObjectIdentifier CAMELLIA128_WRAP = NTTObjectIdentifiers.id_camellia128_wrap.intern(); public static final ASN1ObjectIdentifier CAMELLIA192_WRAP = NTTObjectIdentifiers.id_camellia192_wrap.intern(); public static final ASN1ObjectIdentifier CAMELLIA256_WRAP = NTTObjectIdentifiers.id_camellia256_wrap.intern(); diff --git a/pkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java b/pkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java index 9d9584f0bd..4d79bec90b 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java +++ b/pkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java @@ -52,6 +52,9 @@ class CMSUtils wrapAlgNames.put(CMSAlgorithm.AES128_WRAP, "AESWRAP"); wrapAlgNames.put(CMSAlgorithm.AES192_WRAP, "AESWRAP"); wrapAlgNames.put(CMSAlgorithm.AES256_WRAP, "AESWRAP"); + wrapAlgNames.put(CMSAlgorithm.AES128_WRAP_PAD, "AES-KWP"); + wrapAlgNames.put(CMSAlgorithm.AES192_WRAP_PAD, "AES-KWP"); + wrapAlgNames.put(CMSAlgorithm.AES256_WRAP_PAD, "AES-KWP"); } static @@ -264,15 +267,15 @@ static Cipher createAsymmetricWrapper(JcaJceHelper helper, ASN1ObjectIdentifier public static int getKekSize(ASN1ObjectIdentifier symWrapAlg) { // TODO: add table - if (symWrapAlg.equals(CMSAlgorithm.AES256_WRAP)) + if (symWrapAlg.equals(CMSAlgorithm.AES256_WRAP) || symWrapAlg.equals(CMSAlgorithm.AES256_WRAP_PAD)) { return 32; } - else if (symWrapAlg.equals(CMSAlgorithm.AES128_WRAP)) + else if (symWrapAlg.equals(CMSAlgorithm.AES128_WRAP) || symWrapAlg.equals(CMSAlgorithm.AES128_WRAP_PAD)) { return 16; } - else if (symWrapAlg.equals(CMSAlgorithm.AES192_WRAP)) + else if (symWrapAlg.equals(CMSAlgorithm.AES192_WRAP) || symWrapAlg.equals(CMSAlgorithm.AES192_WRAP_PAD)) { return 24; } diff --git a/pkix/src/main/java/org/bouncycastle/pkcs/bc/BcPKCS12PBMac1CalculatorBuilder.java b/pkix/src/main/java/org/bouncycastle/pkcs/bc/BcPKCS12PBMac1CalculatorBuilder.java index d1deb1445b..bc3480cf46 100644 --- a/pkix/src/main/java/org/bouncycastle/pkcs/bc/BcPKCS12PBMac1CalculatorBuilder.java +++ b/pkix/src/main/java/org/bouncycastle/pkcs/bc/BcPKCS12PBMac1CalculatorBuilder.java @@ -1,5 +1,7 @@ package org.bouncycastle.pkcs.bc; +import java.io.IOException; + import org.bouncycastle.asn1.pkcs.PBKDF2Params; import org.bouncycastle.asn1.pkcs.PBMAC1Params; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; @@ -8,8 +10,6 @@ import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.pkcs.PKCS12MacCalculatorBuilder; -import java.io.IOException; - public class BcPKCS12PBMac1CalculatorBuilder implements PKCS12MacCalculatorBuilder { @@ -27,7 +27,11 @@ public BcPKCS12PBMac1CalculatorBuilder(PBMAC1Params pbeMacParams) throws IOExcep throw new IOException("Key length must be present when using PBMAC1."); } } - // TODO handle other cases + else + { + // TODO: add scrypt support. + throw new IllegalArgumentException("unrecognised PBKDF"); + } } @Override diff --git a/pkix/src/test/java/org/bouncycastle/pkcs/test/PQCPKCS10Test.java b/pkix/src/test/java/org/bouncycastle/pkcs/test/PQCPKCS10Test.java new file mode 100644 index 0000000000..fd4de9cbf5 --- /dev/null +++ b/pkix/src/test/java/org/bouncycastle/pkcs/test/PQCPKCS10Test.java @@ -0,0 +1,97 @@ +package org.bouncycastle.pkcs.test; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Security; +import java.util.Date; + +import junit.framework.TestCase; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.BasicConstraints; +import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.PrivateKeyStatement; +import org.bouncycastle.asn1.x509.X509AttributeIdentifiers; +import org.bouncycastle.cert.CertException; +import org.bouncycastle.cert.CertIOException; +import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder; +import org.bouncycastle.jcajce.spec.MLDSAParameterSpec; +import org.bouncycastle.jcajce.spec.MLKEMParameterSpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.ContentVerifierProvider; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; +import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder; +import org.bouncycastle.pkcs.PKCS10CertificationRequest; +import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder; +import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder; + +public class PQCPKCS10Test + extends TestCase +{ + public void setUp() + { + Security.addProvider(new BouncyCastleProvider()); + } + + public void testKEMPKCS10() + throws Exception + { + KeyPairGenerator dilKpGen = KeyPairGenerator.getInstance("ML-DSA", "BC"); + + dilKpGen.initialize(MLDSAParameterSpec.ml_dsa_65); + + KeyPair dilKp = dilKpGen.generateKeyPair(); + + X509CertificateHolder sigCert = makeV3Certificate("CN=ML-KEM Client", dilKp); + + KeyPairGenerator kemKpGen = KeyPairGenerator.getInstance("ML-KEM", "BC"); + + kemKpGen.initialize(MLKEMParameterSpec.ml_kem_768); + + KeyPair kemKp = kemKpGen.generateKeyPair(); + + PKCS10CertificationRequestBuilder pkcs10Builder = new JcaPKCS10CertificationRequestBuilder( + new X500Name("CN=ML-KEM Client"), kemKp.getPublic()); + + pkcs10Builder.addAttribute(X509AttributeIdentifiers.id_at_privateKeyStatement, + new PrivateKeyStatement(sigCert.toASN1Structure())); + + PKCS10CertificationRequest request = pkcs10Builder.build( + new JcaContentSignerBuilder("ML-DSA").setProvider("BC").build(dilKp.getPrivate())); + + assertTrue(request.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider("BC").build(sigCert.getSubjectPublicKeyInfo()))); + } + + private static X509CertificateHolder makeV3Certificate(String _subDN, KeyPair issKP) + throws OperatorCreationException, CertException, CertIOException + { + PrivateKey issPriv = issKP.getPrivate(); + PublicKey issPub = issKP.getPublic(); + + X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder( + new X500Name(_subDN), + BigInteger.valueOf(System.currentTimeMillis()), + new Date(System.currentTimeMillis() - 5000L), + new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)), + new X500Name(_subDN), + issKP.getPublic()); + + certGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(0)); + + ContentSigner signer = new JcaContentSignerBuilder("ML-DSA").build(issPriv); + + X509CertificateHolder certHolder = certGen.build(signer); + + ContentVerifierProvider verifier = new JcaContentVerifierProviderBuilder().build(issPub); + + assertTrue(certHolder.isSignatureValid(verifier)); + + return certHolder; + } +} diff --git a/pkix/src/test/java/org/bouncycastle/pkcs/test/PfxPduTest.java b/pkix/src/test/java/org/bouncycastle/pkcs/test/PfxPduTest.java index 7486ef6df6..3336fccb2e 100644 --- a/pkix/src/test/java/org/bouncycastle/pkcs/test/PfxPduTest.java +++ b/pkix/src/test/java/org/bouncycastle/pkcs/test/PfxPduTest.java @@ -35,11 +35,14 @@ import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; import org.bouncycastle.asn1.pkcs.Attribute; import org.bouncycastle.asn1.pkcs.ContentInfo; +import org.bouncycastle.asn1.pkcs.PBKDF2Params; +import org.bouncycastle.asn1.pkcs.PBMAC1Params; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.X500NameBuilder; import org.bouncycastle.asn1.x500.style.BCStyle; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; @@ -71,6 +74,7 @@ import org.bouncycastle.pkcs.bc.BcPKCS12MacCalculatorBuilderProvider; import org.bouncycastle.pkcs.bc.BcPKCS12PBEInputDecryptorProviderBuilder; import org.bouncycastle.pkcs.bc.BcPKCS12PBEOutputEncryptorBuilder; +import org.bouncycastle.pkcs.bc.BcPKCS12PBMac1CalculatorBuilder; import org.bouncycastle.pkcs.bc.BcPKCS12PBMac1CalculatorBuilderProvider; import org.bouncycastle.pkcs.jcajce.JcaPKCS12SafeBagBuilder; import org.bouncycastle.pkcs.jcajce.JcaPKCS8EncryptedPrivateKeyInfoBuilder; @@ -79,6 +83,7 @@ import org.bouncycastle.pkcs.jcajce.JcePKCSPBEInputDecryptorProviderBuilder; import org.bouncycastle.pkcs.jcajce.JcePKCSPBEOutputEncryptorBuilder; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Base64; import org.junit.function.ThrowingRunnable; @@ -1136,6 +1141,25 @@ public void testPfxPduMac() assertFalse(pfx.isMacValid(new BcPKCS12MacCalculatorBuilderProvider(BcDefaultDigestProvider.INSTANCE), "not right".toCharArray())); } + public void testPfxPduMac1() + throws Exception + { + // + // set up the keys + // + KeyFactory fact = KeyFactory.getInstance("RSA", BC); + PrivateKey privKey = fact.generatePrivate(privKeySpec); + PublicKey pubKey = fact.generatePublic(pubKeySpec); + + X509Certificate[] chain = createCertChain(fact, pubKey); + + PKCS12PfxPdu pfx = createPfxMac1(privKey, pubKey, chain); + + assertTrue(pfx.hasMac()); + assertTrue(pfx.isMacValid(new BcPKCS12PBMac1CalculatorBuilderProvider(), passwd)); + assertFalse(pfx.isMacValid(new BcPKCS12PBMac1CalculatorBuilderProvider(), "not right".toCharArray())); + } + public void testPfxPduPBMac1PBKdf2() throws Exception { @@ -1750,4 +1774,47 @@ private PKCS12PfxPdu createPfx(PrivateKey privKey, PublicKey pubKey, X509Certifi return pfxPduBuilder.build(new BcPKCS12MacCalculatorBuilder(), passwd); } + + private PKCS12PfxPdu createPfxMac1(PrivateKey privKey, PublicKey pubKey, X509Certificate[] chain) + throws NoSuchAlgorithmException, IOException, PKCSException + { + JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils(); + + PKCS12SafeBagBuilder taCertBagBuilder = new JcaPKCS12SafeBagBuilder(chain[2]); + + taCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString("Bouncy Primary Certificate")); + + PKCS12SafeBagBuilder caCertBagBuilder = new JcaPKCS12SafeBagBuilder(chain[1]); + + caCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString("Bouncy Intermediate Certificate")); + + PKCS12SafeBagBuilder eeCertBagBuilder = new JcaPKCS12SafeBagBuilder(chain[0]); + + eeCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString("Eric's Key")); + eeCertBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId, extUtils.createSubjectKeyIdentifier(pubKey)); + + PKCS12SafeBagBuilder keyBagBuilder = new JcaPKCS12SafeBagBuilder(privKey, new BcPKCS12PBEOutputEncryptorBuilder(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC, new CBCBlockCipher(new DESedeEngine())).build(passwd)); + + keyBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_friendlyName, new DERBMPString("Eric's Key")); + keyBagBuilder.addBagAttribute(PKCSObjectIdentifiers.pkcs_9_at_localKeyId, extUtils.createSubjectKeyIdentifier(pubKey)); + + // + // construct the actual key store + // + PKCS12PfxPduBuilder pfxPduBuilder = new PKCS12PfxPduBuilder(); + + PKCS12SafeBag[] certs = new PKCS12SafeBag[3]; + + certs[0] = eeCertBagBuilder.build(); + certs[1] = caCertBagBuilder.build(); + certs[2] = taCertBagBuilder.build(); + + pfxPduBuilder.addEncryptedData(new BcPKCS12PBEOutputEncryptorBuilder(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC, new CBCBlockCipher(new RC2Engine())).build(passwd), certs); + + pfxPduBuilder.addData(keyBagBuilder.build()); + + return pfxPduBuilder.build(new BcPKCS12PBMac1CalculatorBuilder(new PBMAC1Params( + new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(Strings.toByteArray("saltsalt"), 1024, 256, new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA256))), + new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512))), passwd); + } } diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/WrapUtil.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/WrapUtil.java index 7d1f11900b..41ff97e787 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/WrapUtil.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/WrapUtil.java @@ -55,10 +55,10 @@ public static Wrapper getKeyUnwrapper(KTSParameterSpec ktsParameterSpec, byte[] public static Wrapper getWrapper(String keyAlgorithmName) { Wrapper kWrap; - + if (keyAlgorithmName.equalsIgnoreCase("AESWRAP") || keyAlgorithmName.equalsIgnoreCase("AES")) { - kWrap = new RFC3394WrapEngine(new AESEngine()); + kWrap = new RFC3394WrapEngine(AESEngine.newInstance()); } else if (keyAlgorithmName.equalsIgnoreCase("ARIA")) { diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/PQCDHTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/PQCDHTest.java new file mode 100644 index 0000000000..7c9f299cad --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/PQCDHTest.java @@ -0,0 +1,89 @@ +package org.bouncycastle.jce.provider.test; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.Security; +import java.security.spec.ECGenParameterSpec; + +import javax.crypto.KeyAgreement; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; + +import org.bouncycastle.jcajce.SecretKeyWithEncapsulation; +import org.bouncycastle.jcajce.spec.HybridValueParameterSpec; +import org.bouncycastle.jcajce.spec.KEMExtractSpec; +import org.bouncycastle.jcajce.spec.KEMGenerateSpec; +import org.bouncycastle.jcajce.spec.MLKEMParameterSpec; +import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.SimpleTest; + +public class PQCDHTest + extends SimpleTest +{ + public String getName() + { + return "PQCDHTest"; + } + + private void testMLKemECDH() + throws Exception + { + + KeyPairGenerator kemKeyGen = KeyPairGenerator.getInstance("ML-KEM", "BC"); + + kemKeyGen.initialize(MLKEMParameterSpec.ml_kem_768); + + KeyPair kemKp = kemKeyGen.generateKeyPair(); + + KeyPairGenerator ecKeyGen = KeyPairGenerator.getInstance("EC", "BC"); + + ecKeyGen.initialize(new ECGenParameterSpec("P-256")); + + KeyPair ecKp = ecKeyGen.generateKeyPair(); + + byte[] ukm = Hex.decode("030f136fa7fef90d185655ed1c6d46bacdb820"); + + KeyGenerator keyGen = KeyGenerator.getInstance("ML-KEM", "BC"); + + keyGen.init(new KEMGenerateSpec.Builder(kemKp.getPublic(), "DEF", 256).withNoKdf().build()); + + SecretKeyWithEncapsulation secEnc1 = (SecretKeyWithEncapsulation)keyGen.generateKey(); + + KeyAgreement agreement = KeyAgreement.getInstance("ECCDHwithSHA256CKDF", "BC"); + + agreement.init(ecKp.getPrivate(), new HybridValueParameterSpec(secEnc1.getEncoded(), new UserKeyingMaterialSpec(ukm))); + + agreement.doPhase(ecKp.getPublic(), true); + + SecretKey k1 = agreement.generateSecret("AES[256]"); + + keyGen.init(new KEMExtractSpec.Builder(kemKp.getPrivate(), secEnc1.getEncapsulation(), "DEF", 256).withNoKdf().build()); + + SecretKeyWithEncapsulation secEnc2 = (SecretKeyWithEncapsulation)keyGen.generateKey(); + + agreement.init(ecKp.getPrivate(), new HybridValueParameterSpec(secEnc2.getEncoded(), new UserKeyingMaterialSpec(ukm))); + + agreement.doPhase(ecKp.getPublic(), true); + + SecretKey k2 = agreement.generateSecret("AES[256]"); + + isTrue(Arrays.areEqual(k1.getEncoded(), k2.getEncoded())); + } + + @Override + public void performTest() + throws Exception + { + testMLKemECDH(); + } + + public static void main(String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new PQCDHTest()); + } +} From b5126f406dc0ff6bc71a4811553160e97876237d Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 10 Mar 2025 19:30:51 +0700 Subject: [PATCH 187/890] Add TlsProtocolKemTest to AllTests --- tls/src/test/java/org/bouncycastle/tls/test/AllTests.java | 1 + 1 file changed, 1 insertion(+) diff --git a/tls/src/test/java/org/bouncycastle/tls/test/AllTests.java b/tls/src/test/java/org/bouncycastle/tls/test/AllTests.java index 64b985fe45..b2ecab6091 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/AllTests.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/AllTests.java @@ -31,6 +31,7 @@ public static Test suite() suite.addTestSuite(OCSPTest.class); suite.addTestSuite(PRFTest.class); suite.addTestSuite(Tls13PSKProtocolTest.class); + suite.addTestSuite(TlsProtocolKemTest.class); suite.addTestSuite(TlsProtocolNonBlockingTest.class); suite.addTestSuite(TlsProtocolTest.class); suite.addTestSuite(TlsPSKProtocolTest.class); From d709e2f3c954f97ad42d99f17087369d86ee0ae0 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 10 Mar 2025 19:31:39 +0700 Subject: [PATCH 188/890] Factor out testDHExplicit --- .../bouncycastle/tls/crypto/test/TlsCryptoTest.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/tls/src/test/java/org/bouncycastle/tls/crypto/test/TlsCryptoTest.java b/tls/src/test/java/org/bouncycastle/tls/crypto/test/TlsCryptoTest.java index 58c66a6a4b..a5ffbc4c30 100644 --- a/tls/src/test/java/org/bouncycastle/tls/crypto/test/TlsCryptoTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/crypto/test/TlsCryptoTest.java @@ -221,6 +221,14 @@ public void testDHDomain() throws Exception implTestDHDomain(new TlsDHConfig(namedGroup, false)); implTestDHDomain(new TlsDHConfig(namedGroup, true)); } + } + + public void testDHExplicit() throws Exception + { + if (!crypto.hasDHAgreement()) + { + return; + } new DefaultTlsDHGroupVerifier() {{ @@ -236,9 +244,10 @@ public void testDHDomain() throws Exception assertSame(dhGroup, TlsDHUtils.getStandardGroupForDHParameters(p, g)); int namedGroup = TlsDHUtils.getNamedGroupForDHParameters(p, g); + + // Named groups tested elsewhere if (NamedGroup.refersToASpecificFiniteField(namedGroup)) { - // Already tested the named groups continue; } From 3512785c31e9d38e859846e18c07acf9e7277c8b Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 10 Mar 2025 19:41:15 +0700 Subject: [PATCH 189/890] Change ports for EdDSACredentialsTest --- .../jsse/provider/test/EdDSACredentialsTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tls/src/test/java/org/bouncycastle/jsse/provider/test/EdDSACredentialsTest.java b/tls/src/test/java/org/bouncycastle/jsse/provider/test/EdDSACredentialsTest.java index a5bb4a4281..8e4c265257 100644 --- a/tls/src/test/java/org/bouncycastle/jsse/provider/test/EdDSACredentialsTest.java +++ b/tls/src/test/java/org/bouncycastle/jsse/provider/test/EdDSACredentialsTest.java @@ -28,10 +28,10 @@ protected void setUp() } private static final String HOST = "localhost"; - private static final int PORT_NO_12_ED25519 = 9020; - private static final int PORT_NO_12_ED448 = 9021; - private static final int PORT_NO_13_ED25519 = 9022; - private static final int PORT_NO_13_ED448 = 9023; + private static final int PORT_NO_12_ED25519 = 9050; + private static final int PORT_NO_12_ED448 = 9051; + private static final int PORT_NO_13_ED25519 = 9052; + private static final int PORT_NO_13_ED448 = 9053; static class EdDSAClient implements TestProtocolUtil.BlockingCallable From 223afeab117be228b12cfd36eb0f11f3b8c97702 Mon Sep 17 00:00:00 2001 From: mwcw Date: Tue, 11 Mar 2025 10:43:31 +1100 Subject: [PATCH 190/890] Fixed, relates to GitHub #2107 --- .../oer/its/template/ieee1609dot2/IEEE1609dot2.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/util/src/main/java/org/bouncycastle/oer/its/template/ieee1609dot2/IEEE1609dot2.java b/util/src/main/java/org/bouncycastle/oer/its/template/ieee1609dot2/IEEE1609dot2.java index fc36d74429..37bf6add62 100644 --- a/util/src/main/java/org/bouncycastle/oer/its/template/ieee1609dot2/IEEE1609dot2.java +++ b/util/src/main/java/org/bouncycastle/oer/its/template/ieee1609dot2/IEEE1609dot2.java @@ -259,7 +259,8 @@ public Element result(SwitchIndexer indexer) * EndEntityType ::= BIT STRING {app (0), enrol (1) } (SIZE (8)) */ public static final OERDefinition.Builder EndEntityType = - OERDefinition.bitString(8).defaultValue(new DERBitString(new byte[]{0}, 0)) + OERDefinition.bitString(8) + .defaultValue(new DERBitString(org.bouncycastle.oer.its.ieee1609dot2.EndEntityType.app)) .typeName("EndEntityType"); /** From 9228be2d49dbddb62282b39bec2d45a9745825f7 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 11 Mar 2025 11:06:48 +1100 Subject: [PATCH 191/890] cleaned up TODOs. --- .../java/org/bouncycastle/cms/KEMRecipientInformation.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkix/src/main/java/org/bouncycastle/cms/KEMRecipientInformation.java b/pkix/src/main/java/org/bouncycastle/cms/KEMRecipientInformation.java index e61ba4cbab..bf9984386c 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/KEMRecipientInformation.java +++ b/pkix/src/main/java/org/bouncycastle/cms/KEMRecipientInformation.java @@ -27,13 +27,13 @@ public class KEMRecipientInformation { ASN1OctetString octs = ASN1OctetString.getInstance(r.getId()); - rid = new KEMRecipientId(octs.getOctets()); // TODO: should be KEM + rid = new KEMRecipientId(octs.getOctets()); } else { IssuerAndSerialNumber iAnds = IssuerAndSerialNumber.getInstance(r.getId()); - rid = new KEMRecipientId(iAnds.getName(), iAnds.getSerialNumber().getValue()); // TODO: + rid = new KEMRecipientId(iAnds.getName(), iAnds.getSerialNumber().getValue()); } } From b7e747aa1ef5a06f4ce33945b820595426385be6 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 11 Mar 2025 12:13:00 +1030 Subject: [PATCH 192/890] Remove duplicated parameters in MayoParameters --- .../pqc/crypto/mayo/MayoParameters.java | 28 ++----------------- .../pqc/crypto/mayo/MayoSigner.java | 13 +++------ 2 files changed, 7 insertions(+), 34 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java index ebaa54cbe0..28c01ad0c4 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoParameters.java @@ -18,15 +18,12 @@ public class MayoParameters 40, // r_bytes 120159, // P1_bytes 24336, // P2_bytes - // P3_bytes 24, // csk_bytes 1420, // cpk_bytes 454, // sig_bytes new int[]{8, 1, 1, 0}, // F_TAIL_78 - new byte[]{8, 1, 1, 0}, // f_tail_arr 24, // salt_bytes 32, // digest_bytes - // pk_seed_bytes 24 // sk_seed_bytes ); @@ -39,22 +36,18 @@ public class MayoParameters 81 - 17, // v = 64 4 * 17 + 1, // A_cols = 4 * 17 + 1 = 69 4, // k - // q 32, // m_bytes 544, // O_bytes 32, // v_bytes 34, // r_bytes 66560, // P1_bytes 34816, // P2_bytes - // P3_bytes 24, // csk_bytes 4912, // cpk_bytes 186, // sig_bytes new int[]{8, 0, 2, 8}, //F_TAIL_64 - new byte[]{8, 0, 2, 8}, // f_tail_arr 24, // salt_bytes 32, // digest_bytes - // pk_seed_bytes 24 // sk_seed_bytes ); @@ -67,22 +60,18 @@ public class MayoParameters 118 - 10, // v = 108 11 * 10 + 1, // A_cols = 11 * 10 + 1 = 111 11, // k - // q 54, // m_bytes 540, // O_bytes 54, // v_bytes 55, // r_bytes 317844, // P1_bytes 58320, // P2_bytes - // P3_bytes 32, // csk_bytes 2986, // cpk_bytes 681, // sig_bytes new int[]{8, 0, 1, 7}, //F_TAIL_108 - new byte[]{8, 0, 1, 7}, // f_tail_arr 32, // salt_bytes 48, // digest_bytes - // pk_seed_bytes 32 // sk_seed_bytes ); @@ -95,22 +84,18 @@ public class MayoParameters 154 - 12, // v = 142 12 * 12 + 1, // A_cols = 12 * 12 + 1 = 145 12, // k - // q 71, // m_bytes 852, // O_bytes 71, // v_bytes 72, // r_bytes 720863, // P1_bytes 120984, // P2_bytes - // P3_bytes 40, // csk_bytes 5554, // cpk_bytes 964, // sig_bytes new int[]{4, 0, 8, 1}, //F_TAIL_142 - new byte[]{4, 0, 8, 1}, // f_tail_arr 40, // salt_bytes 64, // digest_bytes - // pk_seed_bytes 40 // sk_seed_bytes ); @@ -133,7 +118,6 @@ public class MayoParameters private final int cpkBytes; private final int sigBytes; private final int[] fTail; - private final byte[] fTailArr; private final int saltBytes; private final int digestBytes; private static final int pkSeedBytes = 16; @@ -141,7 +125,7 @@ public class MayoParameters private MayoParameters(String name, int n, int m, int mVecLimbs, int o, int v, int ACols, int k, int mBytes, int OBytes, int vBytes, int rBytes, int P1Bytes, int P2Bytes, - int cskBytes, int cpkBytes, int sigBytes, int[] fTail, byte[] fTailArr, + int cskBytes, int cpkBytes, int sigBytes, int[] fTail, int saltBytes, int digestBytes, int skSeedBytes) { this.name = name; @@ -162,7 +146,6 @@ private MayoParameters(String name, int n, int m, int mVecLimbs, int o, int v, i this.cpkBytes = cpkBytes; this.sigBytes = sigBytes; this.fTail = fTail; - this.fTailArr = fTailArr; this.saltBytes = saltBytes; this.digestBytes = digestBytes; this.skSeedBytes = skSeedBytes; @@ -258,11 +241,6 @@ public int[] getFTail() return fTail; } - public byte[] getFTailArr() - { - return fTailArr; - } - public int getSaltBytes() { return saltBytes; @@ -288,7 +266,7 @@ public int getSkSeedBytes() */ public int getP1Limbs() { - return ((v * (v + 1)) / 2) * mVecLimbs; + return ((v * (v + 1)) >> 1) * mVecLimbs; } /** @@ -304,7 +282,7 @@ public int getP2Limbs() */ public int getP3Limbs() { - return ((o * (o + 1)) / 2) * mVecLimbs; + return ((o * (o + 1)) >> 1) * mVecLimbs; } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index 65d148b896..254099dd90 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -434,7 +434,7 @@ void computeA(long[] Mtmp, byte[] AOut) final int m = params.getM(); final int mVecLimbs = params.getMVecLimbs(); final int ACols = params.getACols(); - final byte[] fTailArr = params.getFTailArr(); + final int[] fTailArr = params.getFTail(); int bitsToShift = 0; int wordsToShift = 0; @@ -514,7 +514,7 @@ void computeA(long[] Mtmp, byte[] AOut) byte[] tab = new byte[F_TAIL_LEN << 2]; for (int i = 0, idx = 0; i < F_TAIL_LEN; i++) { - byte ft = fTailArr[i]; + int ft = fTailArr[i]; tab[idx++] = (byte)GF16Utils.mulF(ft, 1); tab[idx++] = (byte)GF16Utils.mulF(ft, 2); tab[idx++] = (byte)GF16Utils.mulF(ft, 4); @@ -798,13 +798,8 @@ void ef(byte[] A, int nrows, int ncols) for (int i = 0, irowLen = 0; i < nrows; i++, irowLen += rowLen) { Pack.longToLittleEndian(packedA, irowLen, len_4, bytes, 0); - int j = 0; - for (; j < ncols >> 1; j++) - { - A[outIndex++] = (byte)(bytes[j] & 0x0F); // Lower nibble - A[outIndex++] = (byte)((bytes[j] >> 4) & 0x0F); // Upper nibble - } - A[outIndex++] = (byte)(bytes[j] & 0x0F); + Utils.decode(bytes, 0, A, outIndex, ncols); + outIndex += ncols; } } From 9839405918b5af09a95b94eea16bd86826224ef5 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 12 Mar 2025 12:18:19 +1030 Subject: [PATCH 193/890] Minor refactor on MayoSigner. --- .../java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index 254099dd90..21f8b6d75b 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -131,11 +131,11 @@ public byte[] generateSignature(byte[] message) // Generate S = seed_pk || (additional bytes), using SHAKE256. // Output length is param_pk_seed_bytes + param_O_bytes. shake.update(seed_sk, 0, seed_sk.length); - shake.doFinal(seed_pk, 0, pk_seed_bytes + oBytes); + shake.doFinal(seed_pk, 0, totalS); // Decode the portion of S after the first param_pk_seed_bytes into O. // (In C, this is: decode(S + param_pk_seed_bytes, O, param_v * param_o)) - Utils.decode(seed_pk, pk_seed_bytes, O, 0, v * o); + Utils.decode(seed_pk, pk_seed_bytes, O, 0, O.length); // Expand P1 and P2 into the long array P using seed_pk. Utils.expandP1P2(params, P, seed_pk); From 47d3dba986f9075cecc1b1d83ddf9a0be44653fb Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 12 Mar 2025 17:19:10 +1030 Subject: [PATCH 194/890] TODO: gen_F --- .../pqc/crypto/snova/GF16Utils.java | 13 - .../pqc/crypto/snova/MapGroup1.java | 16 +- .../pqc/crypto/snova/MapGroup2.java | 15 +- .../pqc/crypto/snova/SnovaEngine.java | 228 +++++++++++++++++- .../crypto/snova/SnovaKeyPairGenerator.java | 51 ++-- 5 files changed, 276 insertions(+), 47 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java index 9deda36297..75414b2152 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java @@ -164,19 +164,6 @@ public static byte inv(byte a) return INV4B[a & 0xF]; } - public static void convertGF16sToBytes(byte[] output, byte[] gf16s, int gf16Count) - { - int pairs = gf16Count / 2; - for (int i = 0; i < pairs; i++) - { - output[i] = (byte)((gf16s[i * 2 + 1] << 4) | gf16s[i * 2]); - } - if (gf16Count % 2 == 1) - { - output[pairs] = gf16s[gf16Count - 1]; - } - } - static GF16Matrix[][][] create3DArray(int d1, int d2, int d3, int rank) { GF16Matrix[][][] arr = new GF16Matrix[d1][d2][d3]; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java index cab068f328..552cdfeed7 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -16,14 +16,14 @@ public MapGroup1(SnovaParameters params) int v = params.getV(); int o = params.getO(); int alpha = params.getAlpha(); - - p11 = new byte[m][v][v][16]; - p12 = new byte[m][v][o][16]; - p21 = new byte[m][o][v][16]; - aAlpha = new byte[m][alpha][16]; - bAlpha = new byte[m][alpha][16]; - qAlpha1 = new byte[m][alpha][16]; - qAlpha2 = new byte[m][alpha][16]; + int lsq = params.getL() * params.getL(); + p11 = new byte[m][v][v][lsq]; + p12 = new byte[m][v][o][lsq]; + p21 = new byte[m][o][v][lsq]; + aAlpha = new byte[m][alpha][lsq]; + bAlpha = new byte[m][alpha][lsq]; + qAlpha1 = new byte[m][alpha][lsq]; + qAlpha2 = new byte[m][alpha][lsq]; } public int decode(byte[] input, int len) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java index a78141e370..a4c9cb834e 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java @@ -2,19 +2,18 @@ public class MapGroup2 { - public final GF16Matrix[][][] F11; // [m][v][v] - public final GF16Matrix[][][] F12; // [m][v][o] - public final GF16Matrix[][][] F21; // [m][o][v] + public final byte[][][][] f11; // [m][v][v] + public final byte[][][][] f12; // [m][v][o] + public final byte[][][][] f21; // [m][o][v] public MapGroup2(SnovaParameters params) { int m = params.getM(); int v = params.getV(); int o = params.getO(); - int rank = params.getL(); - - F11 = GF16Utils.create3DArray(m, v, v, rank); - F12 = GF16Utils.create3DArray(m, v, o, rank); - F21 = GF16Utils.create3DArray(m, o, v, rank); + int lsq = params.getL() * params.getL(); + f11 = new byte[m][v][v][lsq]; + f12 = new byte[m][v][o][lsq]; + f21 = new byte[m][o][v][lsq]; } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 30c5087c7b..c4330b0f78 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -28,11 +28,21 @@ public SnovaEngine(SnovaParameters params) { for (int ij = 0; ij < lsq; ++ij) { - xS[index][ij] = GF16Utils.gf16FromNibble(S[index][ij]); + xS[index][ij] = GF16Utils.gf16FromNibble(S[index][ij]); } } } + public byte getGF16m(byte[] gf16m, int x, int y) + { + return gf16m[x * l + y]; + } + + public void setGF16m(byte[] gf16m, int x, int y, byte value) + { + gf16m[x * l + y] = value; + } + public void be_aI(byte[] target, byte a) { // Mask 'a' to ensure it's a valid 4-bit GF16 element @@ -92,7 +102,7 @@ public void genAFqSCT(byte[] c, int cOff, byte[] ptMatrix) // Handle last coefficient with constant-time selection int zero = GF16Utils.ctGF16IsNotZero(c[cOff + l - 1]); - int val = zero * c[cOff +l - 1] + (1 - zero) * (15 + GF16Utils.ctGF16IsNotZero(c[cOff]) - c[cOff]); + int val = zero * c[cOff + l - 1] + (1 - zero) * (15 + GF16Utils.ctGF16IsNotZero(c[cOff]) - c[cOff]); cX = GF16Utils.gf16FromNibble((byte)val); for (int ij = 0; ij < lsq; ij++) @@ -107,4 +117,218 @@ public void genAFqSCT(byte[] c, int cOff, byte[] ptMatrix) } Arrays.fill(xTemp, 0); // Secure clear } + + public void makeInvertibleByAddingAS(byte[] source) + { + if (gf16Determinant(source) != 0) + { + return; + } + + + byte[] temp = new byte[l * l]; + + for (int a = 1; a < 16; a++) + { + generateASMatrix(temp, (byte)a); + addMatrices(temp, source, source); + + if (gf16Determinant(source) != 0) + { + return; + } + } + throw new IllegalStateException("Failed to make matrix invertible"); + } + + private byte gf16Determinant(byte[] matrix) + { + switch (l) + { + case 2: + return determinant2x2(matrix); + case 3: + return determinant3x3(matrix); + case 4: + return determinant4x4(matrix); + case 5: + return determinant5x5(matrix); + default: + throw new IllegalStateException(); + } + } + + private byte determinant2x2(byte[] m) + { + return gf16Add( + gf16Mul(getGF16m(m, 0, 0), getGF16m(m, 1, 1)), + gf16Mul(getGF16m(m, 0, 1), getGF16m(m, 1, 0))); + } + + private byte determinant3x3(byte[] m) + { + return gf16Add( + gf16Add( + gf16Mul(getGF16m(m, 0, 0), gf16Add( + gf16Mul(getGF16m(m, 1, 1), getGF16m(m, 2, 2)), + gf16Mul(getGF16m(m, 1, 2), getGF16m(m, 2, 1)) + )), + gf16Mul(getGF16m(m, 0, 1), gf16Add( + gf16Mul(getGF16m(m, 1, 0), getGF16m(m, 2, 2)), + gf16Mul(getGF16m(m, 1, 2), getGF16m(m, 2, 0)) + )) + ), + gf16Mul(getGF16m(m, 0, 2), gf16Add( + gf16Mul(getGF16m(m, 1, 0), getGF16m(m, 2, 1)), + gf16Mul(getGF16m(m, 1, 1), getGF16m(m, 2, 0)) + )) + ); + } + + private byte determinant4x4(byte[] m) + { + byte d0 = gf16Mul(getGF16m(m, 0, 0), gf16Add( + gf16Add( + pod(m, 1, 1, 2, 2, 3, 3, 2, 3, 3, 2), + pod(m, 1, 2, 2, 1, 3, 3, 2, 3, 3, 1) + ), + pod(m, 1, 3, 2, 1, 3, 2, 2, 2, 3, 1) + )); + + byte d1 = gf16Mul(getGF16m(m, 0, 1), gf16Add( + gf16Add( + pod(m, 1, 0, 2, 2, 3, 3, 2, 3, 3, 2), + pod(m, 1, 2, 2, 0, 3, 3, 2, 3, 3, 0) + ), + pod(m, 1, 3, 2, 0, 3, 2, 2, 2, 3, 0) + )); + + byte d2 = gf16Mul(getGF16m(m, 0, 2), gf16Add( + gf16Add( + pod(m, 1, 0, 2, 1, 3, 3, 2, 3, 3, 1), + pod(m, 1, 1, 2, 0, 3, 3, 2, 3, 3, 0) + ), + pod(m, 1, 3, 2, 0, 3, 1, 2, 1, 3, 0) + )); + + byte d3 = gf16Mul(getGF16m(m, 0, 3), gf16Add( + gf16Add( + pod(m, 1, 0, 2, 1, 3, 2, 2, 2, 3, 1), + pod(m, 1, 1, 2, 0, 3, 2, 2, 2, 3, 0) + ), + pod(m, 1, 2, 2, 0, 3, 1, 2, 1, 3, 0) + )); + + return (byte)(d0 ^ d1 ^ d2 ^ d3); + } + + private byte determinant5x5(byte[] m) + { + return 0; + //TODO: +// byte result; +// +// result = gf16Mul(det3x3(m, 0, 1, 2, 0, 1, 2), +// gf16Add(gf16Mul(m[3][3], m[4][4]), gf16Mul(m[3][4], m[4][3]))); + // ... similar calculations for other components ... + //result ^= gf16Mul(det3x3(m, 0, 1, 2, 0, 1, 2), + // gf16Add(gf16Mul(m[3][3], m[4][4]), gf16Mul(m[3][4], m[4][3]))); + //return result; + } + + private byte det3x3(byte[] m, int row1, int row2, int row3, int col1, int col2, int col3) + { + //TODO: +// byte[][] sub = new byte[3][3]; +// for (int i = 0; i < 3; i++) +// { +// sub[0][i] = m[row1][col1 + i]; +// sub[1][i] = m[row2][col1 + i]; +// sub[2][i] = m[row3][col1 + i]; +// } +// return determinant3x3(sub); + return 0; + } + + private void generateASMatrix(byte[] target, byte a) + { + for (int i = 0; i < l; i++) + { + for (int j = 0; j < l; j++) + { + byte coefficient = (byte)(8 - (i + j)); + if (l == 5 && i == 4 && j == 4) + { + coefficient = 9; + } + setGF16m(target, i, j, gf16Mul(coefficient, a)); + } + } + } + + // POD -> entry[a][b] * (entry[c][d] * entry[e][f] + entry[g][h] * entry[i][j]) + private byte pod(byte[] m, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) + { + return gf16Add( + gf16Mul(getGF16m(m, a, b), gf16Mul(getGF16m(m, c, d), getGF16m(m, e, f))), + gf16Mul(getGF16m(m, g, h), getGF16m(m, i, j))); + } + + private void addMatrices(byte[] a, byte[] b, byte[] c) + { + for (int i = 0; i < l; i++) + { + for (int j = 0; j < l; j++) + { + setGF16m(c, i, j, gf16Add(getGF16m(a, i, j), getGF16m(b, i, j))); + } + } + } + + // GF(16) arithmetic + private static byte gf16Add(byte a, byte b) + { + return (byte)(a ^ b); + } + + // GF(16) multiplication using lookup table + private static byte gf16Mul(byte a, byte b) + { + return GF16Utils.mul(a, b); + } + + public void genAFqS(byte[] c, int cOff, byte[] ptMatrix) + { + byte[] temp = new byte[l * l]; + + // Initialize with be_aI + be_aI(ptMatrix, c[cOff]); + + // Process middle terms + for (int i = 1; i < l - 1; ++i) + { + gf16mScale(S[i], c[cOff + i], temp); + addMatrices(ptMatrix, temp, ptMatrix); + } + + // Handle last term with special case + byte lastScalar = (c[cOff + l - 1] != 0) ? c[cOff + l - 1] : + gf16Add((byte)16, gf16Add(c[cOff], (byte)(c[cOff] == 0 ? 1 : 0))); + gf16mScale(S[l - 1], lastScalar, temp); + addMatrices(ptMatrix, temp, ptMatrix); + + // Clear temporary matrix + //clearMatrix(temp); + } + + private void gf16mScale(byte[] a, byte k, byte[] result) + { + for (int i = 0; i < l; ++i) + { + for (int j = 0; j < l; ++j) + { + setGF16m(result, i, j, gf16Mul(getGF16m(a, i, j), k)); + } + } + } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index a2c2a51a62..e22bfc4683 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -254,29 +254,48 @@ private void genABQP(MapGroup1 map1, byte[] pkSeed) int remaining = prngOutput.length - offset; System.arraycopy(blockOut, 0, prngOutput, offset, remaining); } - - for (int i = 0; i < prngOutput.length; i += 16) - { - byte[] block = new byte[16]; - ctrCipher.processBlock(block, 0, block, 0); - System.arraycopy(block, 0, prngOutput, i, Math.min(16, prngOutput.length - i)); - } } // Convert bytes to GF16 structures int inOff = map1.decode(prngOutput, (gf16sPrngPublic - qTemp.length) >> 1); + GF16Utils.decode(prngOutput, inOff, qTemp, 0, qTemp.length); // -// // Post-processing for invertible matrices -// for (GF16Matrix matrix : map1.Aalpha) -// { -// GF16Utils.makeInvertible(matrix); -// } -// for (GF16Matrix matrix : map1.Balpha) -// { -// GF16Utils.makeInvertible(matrix); -// } + // Post-processing for invertible matrices + for (int pi = 0; pi < m; ++pi) + { + for (int a = 0; a < alpha; ++a) + { + engine.makeInvertibleByAddingAS(map1.aAlpha[pi][a]); + } + } + for (int pi = 0; pi < m; ++pi) + { + for (int a = 0; a < alpha; ++a) + { + engine.makeInvertibleByAddingAS(map1.bAlpha[pi][a]); + } + } + + int ptArray = 0; + for (int pi = 0; pi < m; ++pi) + { + for (int a = 0; a < alpha; ++a) + { + engine.genAFqS(qTemp, ptArray, map1.qAlpha1[pi][a]); + ptArray += l; + } + } + for (int pi = 0; pi < m; ++pi) + { + for (int a = 0; a < alpha; ++a) + { + engine.genAFqS(qTemp, ptArray, map1.qAlpha2[pi][a]); + ptArray += l; + } + } } + // private void genF(MapGroup2 map2, MapGroup1 map1, GF16Matrix[][] T12) // { // // Matrix operations from C code's gen_F_ref From e7f8a727c37ad504015db8afb2221a766562743e Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 12 Mar 2025 13:57:57 +0700 Subject: [PATCH 195/890] Improve encapsulatedTest --- .../cms/test/NewSignedDataTest.java | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java index 948d194eb5..9135cb316f 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java @@ -705,7 +705,7 @@ public class NewSignedDataTest noParams.add(EdECObjectIdentifiers.id_Ed25519); noParams.add(EdECObjectIdentifiers.id_Ed448); } - + public NewSignedDataTest(String name) { super(name); @@ -2492,47 +2492,49 @@ private void encapsulatedTest( X509Certificate signatureCert, String signatureAlgorithm, ASN1ObjectIdentifier sigAlgOid, - AlgorithmIdentifier digAlgId) + AlgorithmIdentifier expectedDigAlgId) throws Exception { - List certList = new ArrayList(); - List crlList = new ArrayList(); - CMSTypedData msg = new CMSProcessableByteArray("Hello World!".getBytes()); - + CMSTypedData msg = new CMSProcessableByteArray("Hello World!".getBytes()); + + List certList = new ArrayList(); + List crlList = new ArrayList(); + certList.add(signatureCert); certList.add(_origCert); crlList.add(_signCrl); - Store certs = new JcaCertStore(certList); - Store crlStore = new JcaCRLStore(crlList); + Store certStore = new JcaCertStore(certList); + Store crlStore = new JcaCRLStore(crlList); CMSSignedDataGenerator gen = new CMSSignedDataGenerator(); ContentSigner contentSigner = new JcaContentSignerBuilder(signatureAlgorithm).setProvider(BC).build(signaturePair.getPrivate()); - gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC).build()).build(contentSigner, signatureCert)); + DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().setProvider(BC).build(); - gen.addCertificates(certs); - - CMSSignedData s = gen.generate(msg, true); + gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(digCalcProv).build(contentSigner, signatureCert)); - ByteArrayInputStream bIn = new ByteArrayInputStream(s.getEncoded()); - ASN1InputStream aIn = new ASN1InputStream(bIn); + gen.addCertificates(certStore); + gen.addCRLs(crlStore); - s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject())); + CMSSignedData s = gen.generate(msg, true); + + s = new CMSSignedData(ContentInfo.getInstance(s.getEncoded())); Set digestAlgorithms = new HashSet(s.getDigestAlgorithmIDs()); assertTrue(digestAlgorithms.size() > 0); - if (digAlgId != null) + if (expectedDigAlgId != null) { - assertTrue(digestAlgorithms.contains(digAlgId)); + assertTrue(digestAlgorithms.contains(expectedDigAlgId)); } - certs = s.getCertificates(); - + certStore = s.getCertificates(); + crlStore = s.getCRLs(); + SignerInformationStore signers = s.getSignerInfos(); Collection c = signers.getSigners(); Iterator it = c.iterator(); @@ -2540,7 +2542,7 @@ private void encapsulatedTest( while (it.hasNext()) { SignerInformation signer = (SignerInformation)it.next(); - Collection certCollection = certs.getMatches(signer.getSID()); + Collection certCollection = certStore.getMatches(signer.getSID()); Iterator certIt = certCollection.iterator(); X509CertificateHolder cert = (X509CertificateHolder)certIt.next(); @@ -2592,18 +2594,17 @@ private void encapsulatedTest( gen = new CMSSignedDataGenerator(); gen.addSigners(s.getSignerInfos()); - + gen.addCertificates(s.getCertificates()); - + gen.addCRLs(s.getCRLs()); + s = gen.generate(msg, true); - - bIn = new ByteArrayInputStream(s.getEncoded()); - aIn = new ASN1InputStream(bIn); - - s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject())); - - certs = s.getCertificates(); - + + s = new CMSSignedData(ContentInfo.getInstance(s.getEncoded())); + + certStore = s.getCertificates(); + crlStore = s.getCRLs(); + signers = s.getSignerInfos(); c = signers.getSigners(); it = c.iterator(); @@ -2611,7 +2612,7 @@ private void encapsulatedTest( while (it.hasNext()) { SignerInformation signer = (SignerInformation)it.next(); - Collection certCollection = certs.getMatches(signer.getSID()); + Collection certCollection = certStore.getMatches(signer.getSID()); Iterator certIt = certCollection.iterator(); X509CertificateHolder cert = (X509CertificateHolder)certIt.next(); From ed2d4390eb3cec699f14a4095a6619a2e23a9115 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 12 Mar 2025 14:01:13 +0700 Subject: [PATCH 196/890] Add note that only SHA1withRSA issuer is actually used --- .../java/org/bouncycastle/cms/test/CMSTestUtil.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java b/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java index 41b04336fa..571d2b318a 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java @@ -504,6 +504,10 @@ public static X509Certificate makeOaepCertificate(KeyPair subKP, String _subDN, private static JcaContentSignerBuilder makeContentSignerBuilder(PublicKey issPub) { + /* + * NOTE: Current ALL test certificates are issued under a SHA1withRSA root, so this list is mostly + * redundant (and also incomplete in that it doesn't handle EdDSA or ML-DSA issuers). + */ JcaContentSignerBuilder contentSignerBuilder; if (issPub instanceof RSAPublicKey) { @@ -521,10 +525,14 @@ else if (issPub.getAlgorithm().equals("ECGOST3410")) { contentSignerBuilder = new JcaContentSignerBuilder("GOST3411withECGOST3410"); } - else + else if (issPub.getAlgorithm().equals("GOST3410")) { contentSignerBuilder = new JcaContentSignerBuilder("GOST3411WithGOST3410"); } + else + { + throw new UnsupportedOperationException("Algorithm handlers incomplete"); + } contentSignerBuilder.setProvider(BouncyCastleProvider.PROVIDER_NAME); From 9afb944716cd470e06bbe52ff245fb5229bf3c7e Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 12 Mar 2025 15:12:23 +0700 Subject: [PATCH 197/890] CMS: Expand ML-KEM tests - adjust KDF, wrap, encryption algorithms --- .../bouncycastle/cms/test/CMSTestUtil.java | 22 +++- .../cms/test/NewEnvelopedDataTest.java | 121 ++++++++++++++++-- 2 files changed, 129 insertions(+), 14 deletions(-) diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java b/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java index 571d2b318a..01db2c4298 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java @@ -61,7 +61,9 @@ public class CMSTestUtil public static KeyPairGenerator ecDsaKpg; public static KeyPairGenerator ed25519Kpg; public static KeyPairGenerator ed448Kpg; - public static KeyPairGenerator mlKemKpg; + public static KeyPairGenerator mlKem512Kpg; + public static KeyPairGenerator mlKem768Kpg; + public static KeyPairGenerator mlKem1024Kpg; public static KeyPairGenerator ntruKpg; public static KeyGenerator aes192kg; public static KeyGenerator desede128kg; @@ -168,7 +170,9 @@ public class CMSTestUtil ed448Kpg = KeyPairGenerator.getInstance("Ed448", "BC"); ntruKpg = KeyPairGenerator.getInstance(BCObjectIdentifiers.ntruhps2048509.getId(), "BC"); - mlKemKpg = KeyPairGenerator.getInstance("ML-KEM-768", "BC"); + mlKem512Kpg = KeyPairGenerator.getInstance("ML-KEM-512", "BC"); + mlKem768Kpg = KeyPairGenerator.getInstance("ML-KEM-768", "BC"); + mlKem1024Kpg = KeyPairGenerator.getInstance("ML-KEM-1024", "BC"); aes192kg = KeyGenerator.getInstance("AES", "BC"); aes192kg.init(192, rand); @@ -281,9 +285,19 @@ public static KeyPair makeNtruKeyPair() return ntruKpg.generateKeyPair(); } - public static KeyPair makeMLKemKeyPair() + public static KeyPair makeMLKem512KeyPair() { - return mlKemKpg.generateKeyPair(); + return mlKem512Kpg.generateKeyPair(); + } + + public static KeyPair makeMLKem768KeyPair() + { + return mlKem768Kpg.generateKeyPair(); + } + + public static KeyPair makeMLKem1024KeyPair() + { + return mlKem1024Kpg.generateKeyPair(); } public static SecretKey makeDesede128Key() diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java index b973fe8f3f..d8e8845400 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java @@ -143,8 +143,12 @@ public class NewEnvelopedDataTest private static X509Certificate _reciKemsCert; private static KeyPair _reciNtruKP; private static X509Certificate _reciNtruCert; - private static KeyPair _reciMLKemKP; - private static X509Certificate _reciMLKemCert; + private static KeyPair _reciMLKem512KP; + private static X509Certificate _reciMLKem512Cert; + private static KeyPair _reciMLKem768KP; + private static X509Certificate _reciMLKem768Cert; + private static KeyPair _reciMLKem1024KP; + private static X509Certificate _reciMLKem1024Cert; private static KeyPair _origDhKP; private static KeyPair _reciDhKP; @@ -609,8 +613,14 @@ private static void init() _reciNtruKP = CMSTestUtil.makeNtruKeyPair(); _reciNtruCert = CMSTestUtil.makeCertificate(_reciNtruKP, _reciDN, _signKP, _signDN); - _reciMLKemKP = CMSTestUtil.makeMLKemKeyPair(); - _reciMLKemCert = CMSTestUtil.makeCertificate(_reciMLKemKP, _reciDN, _signKP, _signDN); + _reciMLKem512KP = CMSTestUtil.makeMLKem512KeyPair(); + _reciMLKem512Cert = CMSTestUtil.makeCertificate(_reciMLKem512KP, _reciDN, _signKP, _signDN); + + _reciMLKem768KP = CMSTestUtil.makeMLKem768KeyPair(); + _reciMLKem768Cert = CMSTestUtil.makeCertificate(_reciMLKem768KP, _reciDN, _signKP, _signDN); + + _reciMLKem1024KP = CMSTestUtil.makeMLKem1024KeyPair(); + _reciMLKem1024Cert = CMSTestUtil.makeCertificate(_reciMLKem1024KP, _reciDN, _signKP, _signDN); } } @@ -716,7 +726,7 @@ public void testContentType() } } - public void testMLKem() + public void testMLKem512() throws Exception { byte[] data = "WallaWallaWashington".getBytes(); @@ -725,8 +735,8 @@ public void testMLKem() CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator(); // note: use cert req ID as key ID, don't want to use issuer/serial in this case! - edGen.addRecipientInfoGenerator(new JceKEMRecipientInfoGenerator(_reciMLKemCert, CMSAlgorithm.AES256_WRAP).setKDF( - new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256))); + edGen.addRecipientInfoGenerator(new JceKEMRecipientInfoGenerator(_reciMLKem512Cert, CMSAlgorithm.AES128_WRAP) + .setKDF(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_alg_hkdf_with_sha256))); CMSEnvelopedData ed = edGen.generate( new CMSProcessableByteArray(data), @@ -743,17 +753,108 @@ public void testMLKem() Iterator it = c.iterator(); int expectedLength = new DefaultKemEncapsulationLengthProvider().getEncapsulationLength( - SubjectPublicKeyInfo.getInstance(_reciMLKemKP.getPublic().getEncoded()).getAlgorithm()); + SubjectPublicKeyInfo.getInstance(_reciMLKem512KP.getPublic().getEncoded()).getAlgorithm()); + + while (it.hasNext()) + { + KEMRecipientInformation recipient = (KEMRecipientInformation)it.next(); + + assertEquals(expectedLength, recipient.getEncapsulation().length); + + assertEquals(NISTObjectIdentifiers.id_alg_ml_kem_512.getId(), recipient.getKeyEncryptionAlgOID()); + + CMSTypedStream contentStream = recipient.getContentStream( + new JceKEMEnvelopedRecipient(_reciMLKem512KP.getPrivate()).setProvider(BC)); + + assertEquals(PKCSObjectIdentifiers.data, contentStream.getContentType()); + assertEquals(true, Arrays.equals(data, Streams.readAll(contentStream.getContentStream()))); + } + } + + public void testMLKem768() + throws Exception + { + byte[] data = "WallaWallaWashington".getBytes(); + + // Send response with encrypted certificate + CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator(); + + // note: use cert req ID as key ID, don't want to use issuer/serial in this case! + edGen.addRecipientInfoGenerator(new JceKEMRecipientInfoGenerator(_reciMLKem768Cert, CMSAlgorithm.AES256_WRAP) + .setKDF(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_alg_hkdf_with_sha256))); + + CMSEnvelopedData ed = edGen.generate( + new CMSProcessableByteArray(data), + new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES256_CBC).setProvider("BC").build()); + + RecipientInformationStore recipients = ed.getRecipientInfos(); + + assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.AES256_CBC); + + Collection c = recipients.getRecipients(); + + assertEquals(1, c.size()); + + Iterator it = c.iterator(); + + int expectedLength = new DefaultKemEncapsulationLengthProvider().getEncapsulationLength( + SubjectPublicKeyInfo.getInstance(_reciMLKem768KP.getPublic().getEncoded()).getAlgorithm()); while (it.hasNext()) { KEMRecipientInformation recipient = (KEMRecipientInformation)it.next(); assertEquals(expectedLength, recipient.getEncapsulation().length); - + assertEquals(NISTObjectIdentifiers.id_alg_ml_kem_768.getId(), recipient.getKeyEncryptionAlgOID()); - CMSTypedStream contentStream = recipient.getContentStream(new JceKEMEnvelopedRecipient(_reciMLKemKP.getPrivate()).setProvider(BC)); + CMSTypedStream contentStream = recipient.getContentStream( + new JceKEMEnvelopedRecipient(_reciMLKem768KP.getPrivate()).setProvider(BC)); + + assertEquals(PKCSObjectIdentifiers.data, contentStream.getContentType()); + assertEquals(true, Arrays.equals(data, Streams.readAll(contentStream.getContentStream()))); + } + } + + public void testMLKem1024() + throws Exception + { + byte[] data = "WallaWallaWashington".getBytes(); + + // Send response with encrypted certificate + CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator(); + + // note: use cert req ID as key ID, don't want to use issuer/serial in this case! + edGen.addRecipientInfoGenerator(new JceKEMRecipientInfoGenerator(_reciMLKem1024Cert, CMSAlgorithm.AES256_WRAP) + .setKDF(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_alg_hkdf_with_sha256))); + + CMSEnvelopedData ed = edGen.generate( + new CMSProcessableByteArray(data), + new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES256_CBC).setProvider("BC").build()); + + RecipientInformationStore recipients = ed.getRecipientInfos(); + + assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.AES256_CBC); + + Collection c = recipients.getRecipients(); + + assertEquals(1, c.size()); + + Iterator it = c.iterator(); + + int expectedLength = new DefaultKemEncapsulationLengthProvider().getEncapsulationLength( + SubjectPublicKeyInfo.getInstance(_reciMLKem1024KP.getPublic().getEncoded()).getAlgorithm()); + + while (it.hasNext()) + { + KEMRecipientInformation recipient = (KEMRecipientInformation)it.next(); + + assertEquals(expectedLength, recipient.getEncapsulation().length); + + assertEquals(NISTObjectIdentifiers.id_alg_ml_kem_1024.getId(), recipient.getKeyEncryptionAlgOID()); + + CMSTypedStream contentStream = recipient.getContentStream( + new JceKEMEnvelopedRecipient(_reciMLKem1024KP.getPrivate()).setProvider(BC)); assertEquals(PKCSObjectIdentifiers.data, contentStream.getContentType()); assertEquals(true, Arrays.equals(data, Streams.readAll(contentStream.getContentStream()))); From bb265da7ba02b9192f9c1178ed9e781c173e1d9b Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 12 Mar 2025 20:26:06 +0700 Subject: [PATCH 198/890] CMS: Prepare ML-DSA tests (not working yet) --- .../bouncycastle/cms/test/CMSTestUtil.java | 23 +++++ .../cms/test/NewSignedDataTest.java | 93 ++++++++++++++++++- 2 files changed, 114 insertions(+), 2 deletions(-) diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java b/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java index 01db2c4298..dfc7134ef5 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java @@ -61,6 +61,9 @@ public class CMSTestUtil public static KeyPairGenerator ecDsaKpg; public static KeyPairGenerator ed25519Kpg; public static KeyPairGenerator ed448Kpg; + public static KeyPairGenerator mlDsa44Kpg; + public static KeyPairGenerator mlDsa65Kpg; + public static KeyPairGenerator mlDsa87Kpg; public static KeyPairGenerator mlKem512Kpg; public static KeyPairGenerator mlKem768Kpg; public static KeyPairGenerator mlKem1024Kpg; @@ -170,6 +173,11 @@ public class CMSTestUtil ed448Kpg = KeyPairGenerator.getInstance("Ed448", "BC"); ntruKpg = KeyPairGenerator.getInstance(BCObjectIdentifiers.ntruhps2048509.getId(), "BC"); + + mlDsa44Kpg = KeyPairGenerator.getInstance("ML-DSA-44", "BC"); + mlDsa65Kpg = KeyPairGenerator.getInstance("ML-DSA-65", "BC"); + mlDsa87Kpg = KeyPairGenerator.getInstance("ML-DSA-87", "BC"); + mlKem512Kpg = KeyPairGenerator.getInstance("ML-KEM-512", "BC"); mlKem768Kpg = KeyPairGenerator.getInstance("ML-KEM-768", "BC"); mlKem1024Kpg = KeyPairGenerator.getInstance("ML-KEM-1024", "BC"); @@ -300,6 +308,21 @@ public static KeyPair makeMLKem1024KeyPair() return mlKem1024Kpg.generateKeyPair(); } + public static KeyPair makeMLDsa44KeyPair() + { + return mlDsa44Kpg.generateKeyPair(); + } + + public static KeyPair makeMLDsa65KeyPair() + { + return mlDsa65Kpg.generateKeyPair(); + } + + public static KeyPair makeMLDsa87KeyPair() + { + return mlDsa87Kpg.generateKeyPair(); + } + public static SecretKey makeDesede128Key() { return desede128kg.generateKey(); diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java index 9135cb316f..4ae2bfa147 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java @@ -144,6 +144,13 @@ public class NewSignedDataTest private static KeyPair _signEd448KP; private static X509Certificate _signEd448Cert; + private static KeyPair _signMLDsa44KP; + private static X509Certificate _signMLDsa44Cert; + private static KeyPair _signMLDsa65KP; + private static X509Certificate _signMLDsa65Cert; + private static KeyPair _signMLDsa87KP; + private static X509Certificate _signMLDsa87Cert; + private static String _reciDN; private static KeyPair _reciKP; private static X509Certificate _reciCert; @@ -704,6 +711,9 @@ public class NewSignedDataTest noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_512); noParams.add(EdECObjectIdentifiers.id_Ed25519); noParams.add(EdECObjectIdentifiers.id_Ed448); + noParams.add(NISTObjectIdentifiers.id_ml_dsa_44); + noParams.add(NISTObjectIdentifiers.id_ml_dsa_65); + noParams.add(NISTObjectIdentifiers.id_ml_dsa_87); } public NewSignedDataTest(String name) @@ -776,6 +786,15 @@ private static void init() _signEd448KP = CMSTestUtil.makeEd448KeyPair(); _signEd448Cert = CMSTestUtil.makeCertificate(_signEd448KP, _signDN, _origKP, _origDN); + _signMLDsa44KP = CMSTestUtil.makeMLDsa44KeyPair(); + _signMLDsa44Cert = CMSTestUtil.makeCertificate(_signMLDsa44KP, _signDN, _origKP, _origDN); + + _signMLDsa65KP = CMSTestUtil.makeMLDsa65KeyPair(); + _signMLDsa65Cert = CMSTestUtil.makeCertificate(_signMLDsa65KP, _signDN, _origKP, _origDN); + + _signMLDsa87KP = CMSTestUtil.makeMLDsa87KeyPair(); + _signMLDsa87Cert = CMSTestUtil.makeCertificate(_signMLDsa87KP, _signDN, _origKP, _origDN); + _reciDN = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU"; _reciKP = CMSTestUtil.makeKeyPair(); _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN); @@ -1789,13 +1808,32 @@ public void testSHA512_256ithRSADigest() public void testEd25519() throws Exception { - encapsulatedTest(_signEd25519KP, _signEd25519Cert, "Ed25519", EdECObjectIdentifiers.id_Ed25519, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512)); + /* + * RFC 8419 3.1. When signing with Ed25519, the digestAlgorithm MUST be id-sha512, and the algorithm + * parameters field MUST be absent. + * + * We confirm here that our implementation defaults to SHA-512 for the digest algorithm. + */ + AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512); + + encapsulatedTest(_signEd25519KP, _signEd25519Cert, "Ed25519", EdECObjectIdentifiers.id_Ed25519, + expectedDigAlgId); } public void testEd448() throws Exception { - encapsulatedTest(_signEd448KP, _signEd448Cert, "Ed448", EdECObjectIdentifiers.id_Ed448, new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256_len, new ASN1Integer(512))); + /* + * RFC 8419 3.1. When signing with Ed448, the digestAlgorithm MUST be id-shake256-len, the algorithm + * parameters field MUST be present, and the parameter MUST contain 512, encoded as a positive integer + * value. + * + * We confirm here that our implementation defaults to id-shake256-len/512 for the digest algorithm. + */ + AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256_len, + new ASN1Integer(512)); + + encapsulatedTest(_signEd448KP, _signEd448Cert, "Ed448", EdECObjectIdentifiers.id_Ed448, expectedDigAlgId); } public void testDetachedEd25519() @@ -2270,6 +2308,57 @@ public SignerInformationVerifier get(SignerId signerId) assertTrue(digAlgs.contains(new AlgorithmIdentifier(TeleTrusTObjectIdentifiers.ripemd160, DERNull.INSTANCE))); } +// public void testMLDsa44() +// throws Exception +// { +// /* +// * draft-ietf-lamps-cms-ml-dsa-02 3.3. SHA-512 [FIPS180] MUST be supported for use with the variants +// * of ML-DSA in this document; however, other hash functions MAY also be supported. When SHA-512 is +// * used, the id-sha512 [RFC5754] digest algorithm identifier is used and the parameters field MUST be +// * omitted. +// * +// * We confirm here that our implementation defaults to SHA-512 for the digest algorithm. +// */ +// AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512); +// +// encapsulatedTest(_signMLDsa44KP, _signMLDsa44Cert, "ML-DSA-44", NISTObjectIdentifiers.id_ml_dsa_44, +// expectedDigAlgId); +// } +// +// public void testMLDsa65() +// throws Exception +// { +// /* +// * draft-ietf-lamps-cms-ml-dsa-02 3.3. SHA-512 [FIPS180] MUST be supported for use with the variants +// * of ML-DSA in this document; however, other hash functions MAY also be supported. When SHA-512 is +// * used, the id-sha512 [RFC5754] digest algorithm identifier is used and the parameters field MUST be +// * omitted. +// * +// * We confirm here that our implementation defaults to SHA-512 for the digest algorithm. +// */ +// AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512); +// +// encapsulatedTest(_signMLDsa65KP, _signMLDsa65Cert, "ML-DSA-65", NISTObjectIdentifiers.id_ml_dsa_65, +// expectedDigAlgId); +// } +// +// public void testMLDsa87() +// throws Exception +// { +// /* +// * draft-ietf-lamps-cms-ml-dsa-02 3.3. SHA-512 [FIPS180] MUST be supported for use with the variants +// * of ML-DSA in this document; however, other hash functions MAY also be supported. When SHA-512 is +// * used, the id-sha512 [RFC5754] digest algorithm identifier is used and the parameters field MUST be +// * omitted. +// * +// * We confirm here that our implementation defaults to SHA-512 for the digest algorithm. +// */ +// AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512); +// +// encapsulatedTest(_signMLDsa87KP, _signMLDsa87Cert, "ML-DSA-87", NISTObjectIdentifiers.id_ml_dsa_87, +// expectedDigAlgId); +// } + private void rsaPSSTest(String signatureAlgorithmName) throws Exception { From 0f613faaad1ee64639709a38fa6fa91b33d6b5ab Mon Sep 17 00:00:00 2001 From: Tony Washer Date: Mon, 20 Jan 2025 14:08:39 +0000 Subject: [PATCH 199/890] Enable AsconXof to perform multiple outputs --- .../crypto/digests/AsconBaseDigest.java | 6 +- .../crypto/digests/AsconCXof128.java | 85 ++----------------- .../crypto/digests/AsconXof128.java | 70 +++++++++++++-- 3 files changed, 73 insertions(+), 88 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java index cfe6f466df..50037c731c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java @@ -127,7 +127,9 @@ public void update(byte[] input, int inOff, int len) @Override public int doFinal(byte[] output, int outOff) { - return hash(output, outOff, CRYPTO_BYTES); + int rv = hash(output, outOff, CRYPTO_BYTES); + reset(); + return rv; } protected void padAndAbsorb() @@ -149,7 +151,7 @@ protected void squeeze(byte[] output, int outOff, int len) } /* squeeze final output block */ setBytes(x0, output, outOff, len); - reset(); + p(ASCON_PB_ROUNDS); } protected int hash(byte[] output, int outOff, int outLen) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java index f54c622675..21296bd9fe 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java @@ -1,8 +1,6 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.OutputLengthException; -import org.bouncycastle.crypto.Xof; import org.bouncycastle.util.Pack; /** @@ -17,10 +15,8 @@ *

*/ public class AsconCXof128 - extends AsconBaseDigest - implements Xof + extends AsconXof128 { - private boolean m_squeezing = false; private final long z0, z1, z2, z3, z4; public AsconCXof128() @@ -35,6 +31,7 @@ public AsconCXof128(byte[] s) public AsconCXof128(byte[] s, int off, int len) { + super(false); if ((off + len) > s.length) { throw new DataLengthException("input buffer too short"); @@ -52,90 +49,18 @@ public AsconCXof128(byte[] s, int off, int len) z4 = x4; } - @Override - public void update(byte in) - { - if (m_squeezing) - { - throw new IllegalArgumentException("attempt to absorb while squeezing"); - } - super.update(in); - } - - @Override - public void update(byte[] input, int inOff, int len) - { - if (m_squeezing) - { - throw new IllegalArgumentException("attempt to absorb while squeezing"); - } - super.update(input, inOff, len); - } - - protected long pad(int i) - { - return 0x01L << (i << 3); - } - - protected long loadBytes(final byte[] bytes, int inOff) - { - return Pack.littleEndianToLong(bytes, inOff); - } - - protected long loadBytes(final byte[] bytes, int inOff, int n) - { - return Pack.littleEndianToLong(bytes, inOff, n); - } - - protected void setBytes(long w, byte[] bytes, int inOff) - { - Pack.longToLittleEndian(w, bytes, inOff); - } - - protected void setBytes(long w, byte[] bytes, int inOff, int n) - { - Pack.longToLittleEndian(w, bytes, inOff, n); - } - - protected void padAndAbsorb() - { - m_squeezing = true; - super.padAndAbsorb(); - } - @Override public String getAlgorithmName() { return "Ascon-CXOF128"; } - @Override - public int doOutput(byte[] output, int outOff, int outLen) - { - if (CRYPTO_BYTES + outOff > output.length) - { - throw new OutputLengthException("output buffer is too short"); - } - padAndAbsorb(); - /* squeeze full output blocks */ - squeeze(output, outOff, outLen); - return outLen; - } - - - @Override - public int doFinal(byte[] output, int outOff, int outLen) - { - int rlt = doOutput(output, outOff, outLen); - reset(); - return rlt; - } - @Override public void reset() { - super.reset(); + baseReset(); m_squeezing = false; + bytesInBuffer = 0; /* initialize */ x0 = z0; x1 = z1; @@ -157,6 +82,6 @@ private void initState(byte[] z, int zOff, int zLen) update(z, zOff, zLen); padAndAbsorb(); m_squeezing = false; + bytesInBuffer = 0; } } - diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java index 31fe0f64a9..0b8f368d9a 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto.digests; +import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.Xof; import org.bouncycastle.util.Pack; @@ -18,11 +19,22 @@ public class AsconXof128 extends AsconBaseDigest implements Xof { - private boolean m_squeezing = false; + protected boolean m_squeezing = false; + + private final byte[] buffer = new byte[ASCON_HASH_RATE]; + protected int bytesInBuffer; public AsconXof128() { - reset(); + this(true); + } + + protected AsconXof128(final boolean doReset) + { + if (doReset) + { + reset(); + } } protected long pad(int i) @@ -52,8 +64,11 @@ protected void setBytes(long w, byte[] bytes, int inOff, int n) protected void padAndAbsorb() { - m_squeezing = true; - super.padAndAbsorb(); + if (!m_squeezing) + { + m_squeezing = true; + super.padAndAbsorb(); + } } @Override @@ -85,7 +100,45 @@ public void update(byte[] input, int inOff, int len) @Override public int doOutput(byte[] output, int outOff, int outLen) { - return hash(output, outOff, outLen); + if (outLen + outOff > output.length) + { + throw new OutputLengthException("output buffer is too short"); + } + + /* Use buffered output first */ + int bytesOutput = 0; + if (bytesInBuffer != 0) + { + int startPos = ASCON_HASH_RATE - bytesInBuffer; + int bytesToOutput = Math.min(outLen, bytesInBuffer); + System.arraycopy(buffer, startPos, output, outOff, bytesToOutput); + bytesInBuffer -= bytesToOutput; + bytesOutput += bytesToOutput; + } + + /* If we still need to output data */ + if (outLen - bytesOutput >= ASCON_HASH_RATE) + { + /* Output full blocks */ + int bytesToOutput = ASCON_HASH_RATE * ((outLen - bytesOutput) / ASCON_HASH_RATE); + bytesOutput += hash(output, outOff + bytesOutput, bytesToOutput); + } + + /* If we need to output a partial buffer */ + if (bytesOutput < outLen) + { + /* Access the next buffer's worth of data */ + hash(buffer, 0, ASCON_HASH_RATE); + + /* Copy required length of data */ + int bytesToOutput = outLen - bytesOutput; + System.arraycopy(buffer, 0, output, outOff + bytesOutput, bytesToOutput); + bytesInBuffer = buffer.length - bytesToOutput; + bytesOutput += bytesToOutput; + } + + /* return the length of data output */ + return bytesOutput; } @Override @@ -106,6 +159,7 @@ public int getByteLength() public void reset() { m_squeezing = false; + bytesInBuffer = 0; super.reset(); /* initialize */ x0 = -2701369817892108309L; @@ -114,5 +168,9 @@ public void reset() x3 = 1072114354614917324L; x4 = -2282070310009238562L; } -} + protected void baseReset() + { + super.reset(); + } +} From eb646dffc56d154a4bf94cb1414518e982daa549 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 13 Mar 2025 00:38:56 +0700 Subject: [PATCH 200/890] Refactor checkForVersion3 method --- .../org/bouncycastle/cms/CMSSignedDataStreamGenerator.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java b/pkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java index 3b45edb06b..519b11941c 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java +++ b/pkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java @@ -372,13 +372,13 @@ else if (tagged.getTagNo() == 3) return new ASN1Integer(1); } - private boolean checkForVersion3(List signerInfos, List signerInfoGens) + private static boolean checkForVersion3(List signerInfos, List signerInfoGens) { for (Iterator it = signerInfos.iterator(); it.hasNext();) { - SignerInfo s = SignerInfo.getInstance(((SignerInformation)it.next()).toASN1Structure()); + SignerInfo s = ((SignerInformation)it.next()).toASN1Structure(); - if (s.getVersion().intValueExact() == 3) + if (s.getVersion().hasValue(3)) { return true; } From ac3224a6b1481c551b12de6eaa4278ae440bbb7e Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 13 Mar 2025 09:02:28 +1030 Subject: [PATCH 201/890] TODO: genP22 --- .../pqc/crypto/snova/SnovaEngine.java | 102 ++++++++++++++++++ .../crypto/snova/SnovaKeyPairGenerator.java | 6 +- 2 files changed, 105 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index c4330b0f78..9e1c42ef83 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -331,4 +331,106 @@ private void gf16mScale(byte[] a, byte k, byte[] result) } } } + + public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) { + int m = params.getM(); + int v = params.getV(); + int o = params.getO(); + int l = params.getL(); + int lsq = l * l; + + // Copy initial matrices + copy4DMatrix(map1.p11, map2.f11, m, v, v, lsq); + copy4DMatrix(map1.p12, map2.f12, m, v, o, lsq); + copy4DMatrix(map1.p21, map2.f21, m, o, v, lsq); + + byte[] temp = new byte[lsq]; + + // First matrix operation sequence + for (int i = 0; i < m; i++) { + for (int j = 0; j < v; j++) { + for (int k = 0; k < o; k++) { + for (int index = 0; index < v; index++) { + GF16Utils.gf16mMul(temp, map1.p11[i][j][index], T12[index][k], l); + GF16Utils.gf16mAdd(map2.f12[i][j][k], map2.f12[i][j][k], temp, l); + } + } + } + } + + // Second matrix operation sequence + for (int i = 0; i < m; i++) { + for (int j = 0; j < o; j++) { + for (int k = 0; k < v; k++) { + for (int index = 0; index < v; index++) { + GF16Utils.gf16mMul(temp, T12[index][j], map1.p11[i][index][k], l); + GF16Utils.gf16mAdd(map2.f21[i][j][k], map2.f21[i][j][k], temp, l); + } + } + } + } + + // Secure clear temporary buffer + Arrays.fill(temp, (byte) 0); + } + + private static void copy4DMatrix(byte[][][][] src, byte[][][][] dest, + int dim1, int dim2, int dim3, int lsq) { + for (int i = 0; i < dim1; i++) { + for (int j = 0; j < dim2; j++) { + for (int k = 0; k < dim3; k++) { + System.arraycopy( + src[i][j][k], 0, + dest[i][j][k], 0, + lsq + ); + } + } + } + } + + public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] F12, SnovaParameters params) { + int m = params.getM(); + int o = params.getO(); + int v = params.getV(); + int l = params.getL(); + int lsq = l * l; + + // Initialize P22 with zeros + byte[][][][] P22 = new byte[m][o][o][lsq]; + + // Temporary buffers + byte[] temp1 = new byte[lsq]; + byte[] temp2 = new byte[lsq]; + + try { + for (int i = 0; i < m; i++) { + for (int j = 0; j < o; j++) { + for (int k = 0; k < o; k++) { + for (int index = 0; index < v; index++) { + // temp1 = T12[index][j] * F12[i][index][k] + GF16Utils.gf16mMul(temp1, T12[index][j], F12[i][index][k], l); + + // temp2 = P21[i][j][index] * T12[index][k] + GF16Utils.gf16mMul(temp2, P21[i][j][index], T12[index][k], l); + + // temp1 += temp2 + GF16Utils.gf16mAdd(temp1, temp1, temp2, l); + + // P22[i][j][k] += temp1 + GF16Utils.gf16mAdd(P22[i][j][k], P22[i][j][k], temp1, l); + } + } + } + } + + // Convert GF16 elements to packed bytes + //TODO + //GF16Utils.decode(P22, outP22, m * o * o *lsq); + } finally { + // Secure clear temporary buffers + Arrays.fill(temp1, (byte) 0); + Arrays.fill(temp2, (byte) 0); + } + } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index e22bfc4683..8450c2b41d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -166,9 +166,9 @@ private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[ // Generate map components genABQP(keyElements.map1, pkSeed); -// -// // Generate F matrices -// genF(keyElements.map2, keyElements.map1, keyElements.T12); + + // Generate F matrices + engine.genF(keyElements.map2, keyElements.map1, keyElements.T12); // Generate P22 matrix // genP22(keyElements.pk.P22, keyElements.T12, keyElements.map1.P21, keyElements.map2.F12); From 0869f53e7369d0f2869a20116c84c39f51f617f2 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 13 Mar 2025 09:02:41 +1030 Subject: [PATCH 202/890] TODO: genP22 --- .../pqc/crypto/snova/SnovaEngine.java | 67 ++++++++++++------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 9e1c42ef83..e68138385e 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -332,7 +332,8 @@ private void gf16mScale(byte[] a, byte k, byte[] result) } } - public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) { + public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) + { int m = params.getM(); int v = params.getV(); int o = params.getO(); @@ -347,10 +348,14 @@ public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) { byte[] temp = new byte[lsq]; // First matrix operation sequence - for (int i = 0; i < m; i++) { - for (int j = 0; j < v; j++) { - for (int k = 0; k < o; k++) { - for (int index = 0; index < v; index++) { + for (int i = 0; i < m; i++) + { + for (int j = 0; j < v; j++) + { + for (int k = 0; k < o; k++) + { + for (int index = 0; index < v; index++) + { GF16Utils.gf16mMul(temp, map1.p11[i][j][index], T12[index][k], l); GF16Utils.gf16mAdd(map2.f12[i][j][k], map2.f12[i][j][k], temp, l); } @@ -359,10 +364,14 @@ public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) { } // Second matrix operation sequence - for (int i = 0; i < m; i++) { - for (int j = 0; j < o; j++) { - for (int k = 0; k < v; k++) { - for (int index = 0; index < v; index++) { + for (int i = 0; i < m; i++) + { + for (int j = 0; j < o; j++) + { + for (int k = 0; k < v; k++) + { + for (int index = 0; index < v; index++) + { GF16Utils.gf16mMul(temp, T12[index][j], map1.p11[i][index][k], l); GF16Utils.gf16mAdd(map2.f21[i][j][k], map2.f21[i][j][k], temp, l); } @@ -371,14 +380,18 @@ public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) { } // Secure clear temporary buffer - Arrays.fill(temp, (byte) 0); + Arrays.fill(temp, (byte)0); } private static void copy4DMatrix(byte[][][][] src, byte[][][][] dest, - int dim1, int dim2, int dim3, int lsq) { - for (int i = 0; i < dim1; i++) { - for (int j = 0; j < dim2; j++) { - for (int k = 0; k < dim3; k++) { + int dim1, int dim2, int dim3, int lsq) + { + for (int i = 0; i < dim1; i++) + { + for (int j = 0; j < dim2; j++) + { + for (int k = 0; k < dim3; k++) + { System.arraycopy( src[i][j][k], 0, dest[i][j][k], 0, @@ -389,7 +402,8 @@ private static void copy4DMatrix(byte[][][][] src, byte[][][][] dest, } } - public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] F12, SnovaParameters params) { + public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] F12, SnovaParameters params) + { int m = params.getM(); int o = params.getO(); int v = params.getV(); @@ -403,11 +417,16 @@ public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] byte[] temp1 = new byte[lsq]; byte[] temp2 = new byte[lsq]; - try { - for (int i = 0; i < m; i++) { - for (int j = 0; j < o; j++) { - for (int k = 0; k < o; k++) { - for (int index = 0; index < v; index++) { + try + { + for (int i = 0; i < m; i++) + { + for (int j = 0; j < o; j++) + { + for (int k = 0; k < o; k++) + { + for (int index = 0; index < v; index++) + { // temp1 = T12[index][j] * F12[i][index][k] GF16Utils.gf16mMul(temp1, T12[index][j], F12[i][index][k], l); @@ -427,10 +446,12 @@ public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] // Convert GF16 elements to packed bytes //TODO //GF16Utils.decode(P22, outP22, m * o * o *lsq); - } finally { + } + finally + { // Secure clear temporary buffers - Arrays.fill(temp1, (byte) 0); - Arrays.fill(temp2, (byte) 0); + Arrays.fill(temp1, (byte)0); + Arrays.fill(temp2, (byte)0); } } } From 79e98f3aeea54235d034d9c5a3635844123dbd51 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 13 Mar 2025 12:55:19 +1030 Subject: [PATCH 203/890] Remove BufferedLargeMac and ImmediateLargeMac --- .../crypto/engines/AEADBufferBaseEngine.java | 131 +++++++----------- .../crypto/engines/AsconEngine.java | 2 +- .../crypto/engines/ISAPEngine.java | 7 +- .../crypto/engines/PhotonBeetleEngine.java | 3 +- 4 files changed, 51 insertions(+), 92 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index f39f238ab8..7e7664d5e7 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -13,9 +13,7 @@ abstract class AEADBufferBaseEngine protected enum ProcessingBufferType { Buffered, // Store a (aad) block size of input and process after the input size exceeds the buffer size - BufferedLargeMac, // handle the situation when mac size is larger than the block size, used for pb128 Immediate, //process the input immediately when the input size is equal or greater than the block size - ImmediateLargeMac, // handle the situation when mac size is larger than the block size, used for ascon80pq, ascon128, ISAP_A_128(A) } protected enum AADOperatorType @@ -65,15 +63,9 @@ protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOpe case Buffered: processor = new BufferedAADProcessor(); break; - case BufferedLargeMac: - processor = new BufferedLargeMacAADProcessor(); - break; case Immediate: processor = new ImmediateAADProcessor(); break; - case ImmediateLargeMac: - processor = new ImmediateLargeMacAADProcessor(); - break; } m_bufferSizeDecrypt = BlockSize + MAC_SIZE; @@ -126,7 +118,7 @@ protected interface AADProcessingBuffer boolean isLengthExceedingBlockSize(int len, int size); } - private abstract class BufferedBaseAADProcessor + private class BufferedAADProcessor implements AADProcessingBuffer { public void processAADByte(byte input) @@ -157,29 +149,15 @@ public int getUpdateOutputSize(int len) // The -1 is to account for the lazy processing of a full buffer return Math.max(0, len) - 1; } - } - - private class BufferedAADProcessor - extends BufferedBaseAADProcessor - { - @Override - public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - { - return processDecryptionWithSmallMacSize(input, inOff, len, output, outOff); - } - } - private class BufferedLargeMacAADProcessor - extends BufferedBaseAADProcessor - { @Override public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { - return processDecryptionWithLargeMacSize(input, inOff, len, output, outOff); + return processDecryption(input, inOff, len, output, outOff); } } - private abstract class ImmediateBaseAADProcessor + private class ImmediateAADProcessor implements AADProcessingBuffer { public void processAADByte(byte input) @@ -209,25 +187,11 @@ public boolean isLengthExceedingBlockSize(int len, int size) { return len >= size; } - } - private class ImmediateAADProcessor - extends ImmediateBaseAADProcessor - { - @Override - public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - { - return processDecryptionWithSmallMacSize(input, inOff, len, output, outOff); - } - } - - private class ImmediateLargeMacAADProcessor - extends ImmediateBaseAADProcessor - { @Override public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { - return processDecryptionWithLargeMacSize(input, inOff, len, output, outOff); + return processDecryption(input, inOff, len, output, outOff); } } @@ -582,61 +546,62 @@ protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output return resultLength; } - private int processDecryptionWithLargeMacSize(byte[] input, int inOff, int len, byte[] output, int outOff) + private int processDecryption(byte[] input, int inOff, int len, byte[] output, int outOff) { - int resultLength = 0, available; - // If the mac size is greater than the block size, process the data in m_buf in the loop until - // there is nearly mac_size data left - while (processor.isLengthExceedingBlockSize(m_bufPos, BlockSize) - && processor.isLengthExceedingBlockSize(len + m_bufPos, m_bufferSizeDecrypt)) - { - processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); - m_bufPos -= BlockSize; - resultLength += BlockSize; - } - if (m_bufPos > 0) + int resultLength = 0, available = m_bufferSizeDecrypt - m_bufPos; + if (MAC_SIZE > BlockSize) { - System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); - if (processor.isLengthExceedingBlockSize(m_bufPos + len, m_bufferSizeDecrypt)) + // situation: pb128, ascon80pq, ascon128, ISAP_A_128(A) + // If the mac size is greater than the block size, process the data in m_buf in the loop until + // there is nearly mac_size data left + while (processor.isLengthExceedingBlockSize(m_bufPos, BlockSize) + && processor.isLengthExceedingBlockSize(len + m_bufPos, m_bufferSizeDecrypt)) { - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); + processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); + m_bufPos -= BlockSize; + resultLength += BlockSize; } - else + if (m_bufPos > 0) { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return -1; + System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); + if (processor.isLengthExceedingBlockSize(m_bufPos + len, m_bufferSizeDecrypt)) + { + available = Math.max(BlockSize - m_bufPos, 0); + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); + } + else + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return -1; + } } } - return inOff; - } - - private int processDecryptionWithSmallMacSize(byte[] input, int inOff, int len, byte[] output, int outOff) - { - int resultLength = 0, available = m_bufferSizeDecrypt - m_bufPos; - if (m_bufPos > 0) + else { - if (processor.isLengthExceedingBlockSize(m_bufPos, BlockSize)) + if (m_bufPos > 0) { - processBufferDecrypt(m_buf, 0, output, outOff); - m_bufPos -= BlockSize; - System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); - resultLength = BlockSize; - available += BlockSize; - if (processor.isLengthWithinAvailableSpace(len, available)) + if (processor.isLengthExceedingBlockSize(m_bufPos, BlockSize)) { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return -1; + processBufferDecrypt(m_buf, 0, output, outOff); + m_bufPos -= BlockSize; + System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); + resultLength = BlockSize; + available += BlockSize; + if (processor.isLengthWithinAvailableSpace(len, available)) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return -1; + } } + available = Math.max(BlockSize - m_bufPos, 0); + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); } - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); } return inOff; } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java index bbb807e507..2c9c9f3791 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java @@ -63,7 +63,7 @@ public AsconEngine(AsconParameters asconParameters) nr = (BlockSize == 8) ? 6 : 8; AADBufferSize = BlockSize; dsep = 1L; - setInnerMembers(asconParameters == AsconParameters.ascon128a ? ProcessingBufferType.Immediate : ProcessingBufferType.ImmediateLargeMac, AADOperatorType.Default, DataOperatorType.Default); + setInnerMembers(ProcessingBufferType.Immediate, AADOperatorType.Default, DataOperatorType.Default); } protected long pad(int i) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index a324de8321..e170f0ff96 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -26,34 +26,29 @@ public enum IsapType public ISAPEngine(IsapType isapType) { KEY_SIZE = IV_SIZE = MAC_SIZE = 16; - ProcessingBufferType bufferType; switch (isapType) { case ISAP_A_128A: ISAPAEAD = new ISAPAEAD_A_128A(); algorithmName = "ISAP-A-128A AEAD"; - bufferType = ProcessingBufferType.ImmediateLargeMac; break; case ISAP_K_128A: ISAPAEAD = new ISAPAEAD_K_128A(); algorithmName = "ISAP-K-128A AEAD"; - bufferType = ProcessingBufferType.Immediate; break; case ISAP_A_128: ISAPAEAD = new ISAPAEAD_A_128(); algorithmName = "ISAP-A-128 AEAD"; - bufferType = ProcessingBufferType.ImmediateLargeMac; break; case ISAP_K_128: ISAPAEAD = new ISAPAEAD_K_128(); algorithmName = "ISAP-K-128 AEAD"; - bufferType = ProcessingBufferType.Immediate; break; default: throw new IllegalArgumentException("Incorrect ISAP parameter"); } AADBufferSize = BlockSize; - setInnerMembers(bufferType, AADOperatorType.Default, DataOperatorType.Counter); + setInnerMembers(ProcessingBufferType.Immediate, AADOperatorType.Default, DataOperatorType.Counter); } private static final int ISAP_STATE_SZ = 40; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 5a2afe6ae8..b9b5657add 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -72,8 +72,7 @@ public PhotonBeetleEngine(PhotonBeetleParameters pbp) STATE_INBYTES = (STATE_INBITS + 7) >>> 3; LAST_THREE_BITS_OFFSET = (STATE_INBITS - ((STATE_INBYTES - 1) << 3) - 3); algorithmName = "Photon-Beetle AEAD"; - setInnerMembers(pbp == PhotonBeetleParameters.pb128 ? ProcessingBufferType.Buffered : ProcessingBufferType.BufferedLargeMac, - AADOperatorType.Counter, DataOperatorType.Counter); + setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Counter, DataOperatorType.Counter); } @Override From 7da1e85163ea19939400cf744b2d681e38d36c3a Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 13 Mar 2025 14:45:48 +1030 Subject: [PATCH 204/890] refactor of processDecryption --- .../crypto/engines/AEADBufferBaseEngine.java | 79 ++++++------------- 1 file changed, 24 insertions(+), 55 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 7e7664d5e7..06ba3df709 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -323,7 +323,6 @@ public int getLen() @Override public void reset() { - } } @@ -548,60 +547,30 @@ protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output private int processDecryption(byte[] input, int inOff, int len, byte[] output, int outOff) { - int resultLength = 0, available = m_bufferSizeDecrypt - m_bufPos; - if (MAC_SIZE > BlockSize) + int resultLength = 0, available; + + // loop will run more than once for the following situation: pb128, ascon80pq, ascon128, ISAP_A_128(A) + while (processor.isLengthExceedingBlockSize(m_bufPos, BlockSize) + && processor.isLengthExceedingBlockSize(len + m_bufPos, m_bufferSizeDecrypt)) { - // situation: pb128, ascon80pq, ascon128, ISAP_A_128(A) - // If the mac size is greater than the block size, process the data in m_buf in the loop until - // there is nearly mac_size data left - while (processor.isLengthExceedingBlockSize(m_bufPos, BlockSize) - && processor.isLengthExceedingBlockSize(len + m_bufPos, m_bufferSizeDecrypt)) - { - processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); - m_bufPos -= BlockSize; - resultLength += BlockSize; - } - if (m_bufPos > 0) - { - System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); - if (processor.isLengthExceedingBlockSize(m_bufPos + len, m_bufferSizeDecrypt)) - { - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - } - else - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return -1; - } - } + processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); + m_bufPos -= BlockSize; + resultLength += BlockSize; } - else + + if (m_bufPos > 0) { - if (m_bufPos > 0) + System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); + if (processor.isLengthWithinAvailableSpace(m_bufPos + len, m_bufferSizeDecrypt)) { - if (processor.isLengthExceedingBlockSize(m_bufPos, BlockSize)) - { - processBufferDecrypt(m_buf, 0, output, outOff); - m_bufPos -= BlockSize; - System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); - resultLength = BlockSize; - available += BlockSize; - if (processor.isLengthWithinAvailableSpace(len, available)) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return -1; - } - } - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return -1; } + available = Math.max(BlockSize - m_bufPos, 0); + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); } return inOff; } @@ -649,7 +618,7 @@ public int doFinal(byte[] output, int outOff) return resultLength; } - public int getBlockSize() + public final int getBlockSize() { return BlockSize; } @@ -739,7 +708,7 @@ protected boolean checkData(boolean isDoFinal) protected abstract void finishAAD(State nextState, boolean isDoFinal); - protected void bufferReset() + protected final void bufferReset() { if (m_buf != null) { @@ -773,7 +742,7 @@ protected void bufferReset() dataOperator.reset(); } - protected void ensureSufficientOutputBuffer(byte[] output, int outOff, int len) + protected final void ensureSufficientOutputBuffer(byte[] output, int outOff, int len) { if (len >= BlockSize && outOff + len > output.length) { @@ -781,7 +750,7 @@ protected void ensureSufficientOutputBuffer(byte[] output, int outOff, int len) } } - protected void ensureSufficientInputBuffer(byte[] input, int inOff, int len) + protected final void ensureSufficientInputBuffer(byte[] input, int inOff, int len) { if (inOff + len > input.length) { @@ -789,7 +758,7 @@ protected void ensureSufficientInputBuffer(byte[] input, int inOff, int len) } } - protected void ensureInitialized() + protected final void ensureInitialized() { if (m_state == State.Uninitialized) { From 063bb449beb0f076deb73ce4f5f09bd7bf4603e1 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 13 Mar 2025 14:47:49 +1030 Subject: [PATCH 205/890] refactor of processDecryption, remove AADProcessingBuffer.processDecryptBytes --- .../crypto/engines/AEADBufferBaseEngine.java | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 06ba3df709..6e0aa86aab 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -109,8 +109,6 @@ protected interface AADProcessingBuffer { void processAADByte(byte input); - int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff); - int getUpdateOutputSize(int len); boolean isLengthWithinAvailableSpace(int len, int available); @@ -149,12 +147,6 @@ public int getUpdateOutputSize(int len) // The -1 is to account for the lazy processing of a full buffer return Math.max(0, len) - 1; } - - @Override - public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - { - return processDecryption(input, inOff, len, output, outOff); - } } private class ImmediateAADProcessor @@ -187,12 +179,6 @@ public boolean isLengthExceedingBlockSize(int len, int size) { return len >= size; } - - @Override - public int processDecryptBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - { - return processDecryption(input, inOff, len, output, outOff); - } } protected interface AADOperator @@ -525,7 +511,7 @@ protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output ensureSufficientOutputBuffer(output, outOff, resultLength); int originalInOff = inOff; int originalm_bufPos = m_bufPos; - if ((inOff = processor.processDecryptBytes(input, inOff, len, output, outOff)) == -1) + if ((inOff = processDecryption(input, inOff, len, output, outOff)) == -1) { return resultLength; } From c6ed370d4065d9f0dae13fcf0837da3e154605f3 Mon Sep 17 00:00:00 2001 From: David Hook Date: Thu, 13 Mar 2025 16:58:24 +1100 Subject: [PATCH 206/890] corrected PKIX oid. --- .../internal/asn1/iana/IANAObjectIdentifiers.java | 4 ++-- .../org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java | 5 ++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/internal/asn1/iana/IANAObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/internal/asn1/iana/IANAObjectIdentifiers.java index 97a97d0680..2b586baf63 100644 --- a/core/src/main/java/org/bouncycastle/internal/asn1/iana/IANAObjectIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/internal/asn1/iana/IANAObjectIdentifiers.java @@ -37,8 +37,8 @@ public interface IANAObjectIdentifiers /** IANA security nametypes; 1.3.6.1.5.6 */ static final ASN1ObjectIdentifier security_nametypes = security.branch("6"); - /** PKIX base OID: 1.3.6.1.5.6.6 */ - static final ASN1ObjectIdentifier pkix = security_mechanisms.branch("6"); + /** PKIX base OID: 1.3.6.1.5.5.7 */ + static final ASN1ObjectIdentifier pkix = security_mechanisms.branch("7"); /** IPSEC base OID: 1.3.6.1.5.5.8 */ diff --git a/util/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java b/util/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java index 5bfdbab891..3be353ca22 100644 --- a/util/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java +++ b/util/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java @@ -37,9 +37,8 @@ public interface IANAObjectIdentifiers /** IANA security nametypes; 1.3.6.1.5.6 */ static final ASN1ObjectIdentifier security_nametypes = security.branch("6"); - /** PKIX base OID: 1.3.6.1.5.6.6 */ - static final ASN1ObjectIdentifier pkix = security_mechanisms.branch("6"); - + /** PKIX base OID: 1.3.6.1.5.5.7 */ + static final ASN1ObjectIdentifier pkix = security_mechanisms.branch("7"); /** IPSEC base OID: 1.3.6.1.5.5.8 */ static final ASN1ObjectIdentifier ipsec = security_mechanisms.branch("8"); From bee11a91ad41cfeb3c26cd60d629c05d52fca89b Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 13 Mar 2025 16:32:50 +1030 Subject: [PATCH 207/890] refactor of processEncDecBytes --- .../crypto/engines/AEADBufferBaseEngine.java | 98 +++++++------------ 1 file changed, 35 insertions(+), 63 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 6e0aa86aab..8d8c30e502 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -465,26 +465,26 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output, int out protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { + boolean forEncryption = checkData(false); int available, resultLength; - if (checkData(false)) + available = (forEncryption ? BlockSize : m_bufferSizeDecrypt) - m_bufPos; + // The function is just an operator < or <= + if (processor.isLengthWithinAvailableSpace(len, available)) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return 0; + } + resultLength = processor.getUpdateOutputSize(len) + m_bufPos - (forEncryption ? 0 : MAC_SIZE); + ensureSufficientOutputBuffer(output, outOff, resultLength - resultLength % BlockSize); + resultLength = 0; + if (forEncryption) { - resultLength = 0; - available = processor.getUpdateOutputSize(len) + m_bufPos; - ensureSufficientOutputBuffer(output, outOff, available - available % BlockSize); if (m_bufPos > 0) { - available = BlockSize - m_bufPos; - // The function is just an operator < or <= - if (processor.isLengthWithinAvailableSpace(len, available)) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } System.arraycopy(input, inOff, m_buf, m_bufPos, available); inOff += available; len -= available; - processBufferEncrypt(m_buf, 0, output, outOff); resultLength = BlockSize; } @@ -499,25 +499,30 @@ protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output } else { - available = m_bufferSizeDecrypt - m_bufPos; - if (processor.isLengthWithinAvailableSpace(len, available)) + // loop will run more than once for the following situation: pb128, ascon80pq, ascon128, ISAP_A_128(A) + while (processor.isLengthExceedingBlockSize(m_bufPos, BlockSize) + && processor.isLengthExceedingBlockSize(len + m_bufPos, m_bufferSizeDecrypt)) { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; + processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); + m_bufPos -= BlockSize; + resultLength += BlockSize; } - resultLength = (processor.getUpdateOutputSize(len) + m_bufPos - MAC_SIZE); - resultLength -= resultLength % BlockSize; - ensureSufficientOutputBuffer(output, outOff, resultLength); - int originalInOff = inOff; - int originalm_bufPos = m_bufPos; - if ((inOff = processDecryption(input, inOff, len, output, outOff)) == -1) + if (m_bufPos > 0) { - return resultLength; + System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); + if (processor.isLengthWithinAvailableSpace(m_bufPos + len, m_bufferSizeDecrypt)) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return resultLength; + } + available = Math.max(BlockSize - m_bufPos, 0); + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); + resultLength += BlockSize; } - resultLength = inOff - originalInOff; - len -= resultLength; - resultLength += originalm_bufPos; while (processor.isLengthExceedingBlockSize(len, m_bufferSizeDecrypt)) { processBufferDecrypt(input, inOff, output, outOff + resultLength); @@ -531,36 +536,6 @@ protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output return resultLength; } - private int processDecryption(byte[] input, int inOff, int len, byte[] output, int outOff) - { - int resultLength = 0, available; - - // loop will run more than once for the following situation: pb128, ascon80pq, ascon128, ISAP_A_128(A) - while (processor.isLengthExceedingBlockSize(m_bufPos, BlockSize) - && processor.isLengthExceedingBlockSize(len + m_bufPos, m_bufferSizeDecrypt)) - { - processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); - m_bufPos -= BlockSize; - resultLength += BlockSize; - } - - if (m_bufPos > 0) - { - System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); - if (processor.isLengthWithinAvailableSpace(m_bufPos + len, m_bufferSizeDecrypt)) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return -1; - } - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - } - return inOff; - } - @Override public int doFinal(byte[] output, int outOff) throws IllegalStateException, InvalidCipherTextException @@ -583,10 +558,7 @@ public int doFinal(byte[] output, int outOff) resultLength = m_bufPos; } - if (outOff > output.length - resultLength) - { - throw new OutputLengthException("output buffer too short"); - } + ensureSufficientOutputBuffer(output, outOff, resultLength); mac = new byte[MAC_SIZE]; processFinalBlock(output, outOff); if (forEncryption) @@ -730,7 +702,7 @@ protected final void bufferReset() protected final void ensureSufficientOutputBuffer(byte[] output, int outOff, int len) { - if (len >= BlockSize && outOff + len > output.length) + if (outOff + len > output.length) { throw new OutputLengthException("output buffer too short"); } From d73b9c7142332b54a6a9212759965c8bb4653584 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 13 Mar 2025 16:36:13 +1030 Subject: [PATCH 208/890] Remove the debug code in SparkleTest --- .../java/org/bouncycastle/crypto/test/SparkleTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java index 6b6914b973..f9fd990e8c 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java @@ -379,10 +379,10 @@ private void implTestVectorsEngine(SparkleEngine.SparkleParameters pbp, String f byte[] ad = Hex.decode(map.get("AD")); byte[] pt = Hex.decode(map.get("PT")); byte[] ct = Hex.decode(map.get("CT")); - if (!map.get("Count").equals("17")) - { - continue; - } +// if (!map.get("Count").equals("17")) +// { +// continue; +// } CipherParameters parameters = new ParametersWithIV(new KeyParameter(key), nonce); // Encrypt From 2c4453a7f3ff30995c327c65a453b08bea4e5fe9 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 14 Mar 2025 07:12:27 +1030 Subject: [PATCH 209/890] Fix the issue for #2025 --- .../java/org/bouncycastle/crypto/engines/RomulusEngine.java | 2 +- .../java/org/bouncycastle/crypto/engines/XoodyakEngine.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index ff1fd4cc5c..d2196675ff 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -855,7 +855,7 @@ static void hirose_128_128_256(byte[] h, byte[] g, byte[] m, int mOff) } @Override - public void init(byte[] key, byte[] iv) + protected void init(byte[] key, byte[] iv) throws IllegalArgumentException { npub = iv; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index 167990b30b..a03755bbbc 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -43,7 +43,7 @@ public XoodyakEngine() } @Override - public void init(byte[] key, byte[] iv) + protected void init(byte[] key, byte[] iv) throws IllegalArgumentException { K = key; From f8c09c9786f752a5c7ac911a1025e2ce09ee8523 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 14 Mar 2025 08:32:16 +1030 Subject: [PATCH 210/890] Fix the issue for #2025 --- .../crypto/engines/AEADBufferBaseEngine.java | 65 +++++++++++-------- .../crypto/engines/PhotonBeetleEngine.java | 2 +- 2 files changed, 38 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 8d8c30e502..983fea482e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -28,7 +28,7 @@ protected enum DataOperatorType Default, Counter, Stream, - //StreamCipher //TODO: add for Grain 128 AEAD + StreamCipher } protected enum State @@ -99,9 +99,10 @@ protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOpe m_buf = new byte[MAC_SIZE]; dataOperator = new StreamDataOperator(); break; -// case StreamCipher: -// dataOperator = new StreamCipherOperator(); -// break; + case StreamCipher: + m_buf = new byte[m_bufferSizeDecrypt]; + dataOperator = new StreamCipherOperator(); + break; } } @@ -368,30 +369,38 @@ public void reset() } } -// protected class StreamCipherOperator -// implements DataOperator -// { -// private int len; -// @Override -// public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) -// { -// this.len = len; -// processBufferEncrypt(input, inOff, output, outOff); -// return len; -// } -// -// @Override -// public int getLen() -// { -// return 0; -// } -// -// @Override -// public void reset() -// { -// -// } -// } + protected class StreamCipherOperator + implements DataOperator + { + private int len; + + @Override + public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + this.len = len; + if (forEncryption) + { + processBufferEncrypt(input, inOff, output, outOff); + } + else + { + processBufferDecrypt(input, inOff, output, outOff); + } + return len; + } + + @Override + public int getLen() + { + return 0; + } + + @Override + public void reset() + { + + } + } protected static final class ErasableOutputStream extends ByteArrayOutputStream diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index b9b5657add..b90ace4c42 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -116,7 +116,7 @@ protected void finishAAD(State nextState, boolean isDoFinal) m_state = nextState; } - public void processFinalAAD() + protected void processFinalAAD() { int aadLen = aadOperator.getLen(); if (aadLen != 0) From dd3826a1e90aabaa2166c18032b957381f6baf3e Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 14 Mar 2025 08:33:41 +1030 Subject: [PATCH 211/890] Fix the issue for #2025 --- .../org/bouncycastle/crypto/engines/PhotonBeetleEngine.java | 2 +- .../java/org/bouncycastle/crypto/engines/RomulusEngine.java | 2 +- .../java/org/bouncycastle/crypto/engines/XoodyakEngine.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index b9b5657add..b90ace4c42 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -116,7 +116,7 @@ protected void finishAAD(State nextState, boolean isDoFinal) m_state = nextState; } - public void processFinalAAD() + protected void processFinalAAD() { int aadLen = aadOperator.getLen(); if (aadLen != 0) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index ff1fd4cc5c..d2196675ff 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -855,7 +855,7 @@ static void hirose_128_128_256(byte[] h, byte[] g, byte[] m, int mOff) } @Override - public void init(byte[] key, byte[] iv) + protected void init(byte[] key, byte[] iv) throws IllegalArgumentException { npub = iv; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index 167990b30b..a03755bbbc 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -43,7 +43,7 @@ public XoodyakEngine() } @Override - public void init(byte[] key, byte[] iv) + protected void init(byte[] key, byte[] iv) throws IllegalArgumentException { K = key; From 20e323f5d1ec824c0466ad8fb774152fedd84fbe Mon Sep 17 00:00:00 2001 From: David Hook Date: Fri, 14 Mar 2025 14:57:52 +1100 Subject: [PATCH 212/890] updated HQC to always generate 256 bit secrets --- .../bouncycastle/pqc/crypto/hqc/HQCKEMExtractor.java | 2 +- .../bouncycastle/pqc/crypto/hqc/HQCKEMGenerator.java | 2 +- .../org/bouncycastle/pqc/crypto/hqc/HQCParameters.java | 2 +- .../pqc/jcajce/provider/hqc/HQCCipherSpi.java | 6 +++--- .../pqc/jcajce/provider/util/WrapUtil.java | 10 ++++++++++ .../bouncycastle/pqc/jcajce/provider/test/HQCTest.java | 2 +- 6 files changed, 17 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCKEMExtractor.java b/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCKEMExtractor.java index b565ea41c6..bc3cb1483b 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCKEMExtractor.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCKEMExtractor.java @@ -30,7 +30,7 @@ public byte[] extractSecret(byte[] encapsulation) engine.decaps(session_key, encapsulation, sk); - return Arrays.copyOfRange(session_key, 0, key.getParameters().getK()); + return Arrays.copyOfRange(session_key, 0, 32); } public int getEncapsulationLength() diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCKEMGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCKEMGenerator.java index 85cc4750d0..47caa9476e 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCKEMGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCKEMGenerator.java @@ -36,6 +36,6 @@ public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recip byte[] cipherText = Arrays.concatenate(u, v, salt); - return new SecretWithEncapsulationImpl(Arrays.copyOfRange(K, 0, key.getParameters().getK()), cipherText); + return new SecretWithEncapsulationImpl(Arrays.copyOfRange(K, 0, 32), cipherText); } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCParameters.java index 1090848fa8..86b965ec98 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/HQCParameters.java @@ -118,7 +118,7 @@ HQCEngine getEngine() public int getSessionKeySize() { - return k * 8; + return 32 * 8; } public String getName() diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCCipherSpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCCipherSpi.java index 2281b93db1..92a6c432dc 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCCipherSpi.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCCipherSpi.java @@ -251,7 +251,7 @@ protected byte[] engineWrap( Wrapper kWrap = WrapUtil.getWrapper(kemParameterSpec.getKeyAlgorithmName()); - KeyParameter keyParameter = new KeyParameter(secEnc.getSecret()); + KeyParameter keyParameter = new KeyParameter(WrapUtil.trimSecret(kemParameterSpec.getKeyAlgorithmName(), secEnc.getSecret())); kWrap.init(true, keyParameter); @@ -268,7 +268,7 @@ protected byte[] engineWrap( return rv; } catch (IllegalArgumentException e) - { + { e.printStackTrace(); throw new IllegalBlockSizeException("unable to generate KTS secret: " + e.getMessage()); } catch (DestroyFailedException e) @@ -296,7 +296,7 @@ protected Key engineUnwrap( Wrapper kWrap = WrapUtil.getWrapper(kemParameterSpec.getKeyAlgorithmName()); - KeyParameter keyParameter = new KeyParameter(secret); + KeyParameter keyParameter = new KeyParameter(WrapUtil.trimSecret(kemParameterSpec.getKeyAlgorithmName(), secret)); Arrays.clear(secret); diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/WrapUtil.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/WrapUtil.java index 41ff97e787..5a99f1cd7d 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/WrapUtil.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/WrapUtil.java @@ -91,6 +91,16 @@ else if (keyAlgorithmName.equalsIgnoreCase("ARIA-KWP")) return kWrap; } + public static byte[] trimSecret(String algName, byte[] secret) + { + if (algName.equals("SEED")) + { + return Arrays.copyOfRange(secret, 0, 16); + } + + return secret; + } + private static byte[] makeKeyBytes(KTSParameterSpec ktsSpec, byte[] secret) throws InvalidKeyException { diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java index 107512a2be..a818b59897 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java @@ -123,7 +123,7 @@ public void testGenerateAES() SecretKeyWithEncapsulation secEnc1 = (SecretKeyWithEncapsulation)keyGen.generateKey(); assertEquals("AES", secEnc1.getAlgorithm()); - assertEquals(16, secEnc1.getEncoded().length); + assertEquals(32, secEnc1.getEncoded().length); keyGen.init(new KEMExtractSpec(kp.getPrivate(), secEnc1.getEncapsulation(), "AES")); From 81e40ca158070ebcaa4e27102b4069470a2e152f Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 14 Mar 2025 15:30:57 +1030 Subject: [PATCH 213/890] Fix the issue related to Grain128AEADEngine --- .../crypto/engines/AEADBufferBaseEngine.java | 34 ++- .../crypto/engines/Grain128AEADEngine.java | 289 ++++++++---------- .../bouncycastle/crypto/test/CipherTest.java | 2 +- .../crypto/test/Grain128AEADTest.java | 126 ++++---- 4 files changed, 224 insertions(+), 227 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java index 983fea482e..87fee36a32 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java @@ -100,6 +100,7 @@ protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOpe dataOperator = new StreamDataOperator(); break; case StreamCipher: + BlockSize = 0; m_buf = new byte[m_bufferSizeDecrypt]; dataOperator = new StreamCipherOperator(); break; @@ -377,28 +378,51 @@ protected class StreamCipherOperator @Override public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { - this.len = len; + boolean forEncryption = checkData(false); if (forEncryption) { + this.len = len; processBufferEncrypt(input, inOff, output, outOff); + return len; } else { - processBufferDecrypt(input, inOff, output, outOff); + // keep last mac size bytes + int available = Math.max(m_bufPos + len - MAC_SIZE, 0); + int rlt = 0; + if (m_bufPos > 0) + { + this.len = Math.min(available, m_bufPos); + rlt = this.len; + processBufferDecrypt(m_buf, 0, output, outOff); + available -= rlt; + m_bufPos -= rlt; + System.arraycopy(m_buf, rlt, m_buf, 0, m_bufPos); + } + if (available > 0) + { + this.len = available; + processBufferDecrypt(input, inOff, output, outOff); + rlt += available; + len -= available; + inOff += available; + } + + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return rlt; } - return len; } @Override public int getLen() { - return 0; + return len; } @Override public void reset() { - } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java index de8766363f..2bffd9fcec 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java @@ -1,17 +1,12 @@ package org.bouncycastle.crypto.engines; -import java.io.ByteArrayOutputStream; - -import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.InvalidCipherTextException; -import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.util.Pack; /** * Grain-128 AEAD, based on the current round 3 submission, https://grain-128aead.github.io/ */ public class Grain128AEADEngine - extends AEADBaseEngine + extends AEADBufferBaseEngine { /** * Constants @@ -29,16 +24,13 @@ public class Grain128AEADEngine private int[] authAcc; private int[] authSr; - private boolean initialised = false; - private boolean aadFinished = false; - private final ErasableOutputStream aadData = new ErasableOutputStream(); - public Grain128AEADEngine() { - algorithmName = "Grain-128AEAD"; + algorithmName = "Grain-128 AEAD"; KEY_SIZE = 16; IV_SIZE = 12; MAC_SIZE = 8; + setInnerMembers(ProcessingBufferType.Immediate, AADOperatorType.Stream, DataOperatorType.StreamCipher); } /** @@ -49,11 +41,6 @@ public Grain128AEADEngine() protected void init(byte[] key, byte[] iv) throws IllegalArgumentException { - /* - * Grain encryption and decryption is completely symmetrical, so the - * 'forEncryption' is irrelevant. - */ - /* * Initialize variables. */ @@ -63,37 +50,15 @@ protected void init(byte[] key, byte[] iv) nfsr = new int[STATE_SIZE]; authAcc = new int[2]; authSr = new int[2]; - + m_state = forEncryption ? State.EncInit : State.DecInit; System.arraycopy(iv, 0, workingIV, 0, IV_SIZE); - + workingIV[12] = (byte)0xFF; + workingIV[13] = (byte)0xFF; + workingIV[14] = (byte)0xFF; + workingIV[15] = (byte)0x7F; reset(); } - /** - * 320 clocks initialization phase. - */ - private void initGrain() - { - for (int i = 0; i < 320; ++i) - { - int output = getOutput(); - nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0] ^ output) & 1); - lfsr = shift(lfsr, (getOutputLFSR() ^ output) & 1); - } - for (int quotient = 0; quotient < 8; ++quotient) - { - for (int remainder = 0; remainder < 8; ++remainder) - { - int output = getOutput(); - nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0] ^ output ^ ((workingKey[quotient]) >> remainder)) & 1); - lfsr = shift(lfsr, (getOutputLFSR() ^ output ^ ((workingKey[quotient + 8]) >> remainder)) & 1); - } - } - initGrain(authAcc); - initGrain(authSr); - initialised = true; - } - private void initGrain(int[] auth) { for (int quotient = 0; quotient < 2; ++quotient) @@ -211,84 +176,31 @@ private int[] shift(int[] array, int val) return array; } - /** - * Set keys, reset cipher. - * - * @param keyBytes The key. - * @param ivBytes The IV. - */ - private void setKey(byte[] keyBytes, byte[] ivBytes) + protected void reset(boolean clearMac) { - ivBytes[12] = (byte)0xFF; - ivBytes[13] = (byte)0xFF; - ivBytes[14] = (byte)0xFF; - ivBytes[15] = (byte)0x7F; - workingKey = keyBytes; - workingIV = ivBytes; + this.aadOperator.reset(); - /* - * Load NFSR and LFSR - */ Pack.littleEndianToInt(workingKey, 0, nfsr); Pack.littleEndianToInt(workingIV, 0, lfsr); - } - - public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - throws DataLengthException - { - if (!initialised) - { - throw new IllegalStateException(getAlgorithmName() + " not initialised"); - } - - if (!aadFinished) - { - doProcessAADBytes(aadData.getBuf(), aadData.size()); - aadFinished = true; - } - - if ((inOff + len) > input.length) - { - throw new DataLengthException("input buffer too short"); - } - - if ((outOff + len) > output.length) + // 320 clocks initialization phase. + for (int i = 0; i < 320; ++i) { - throw new OutputLengthException("output buffer too short"); + int output = getOutput(); + nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0] ^ output) & 1); + lfsr = shift(lfsr, (getOutputLFSR() ^ output) & 1); } - getKeyStream(input, inOff, len, output, outOff); - return len; - } - - protected void reset(boolean clearMac) - { - this.aadData.reset(); - this.aadFinished = false; - - setKey(workingKey, workingIV); - initGrain(); - super.reset(clearMac); - } - - private void getKeyStream(byte[] input, int inOff, int len, byte[] ciphertext, int outOff) - { - for (int i = 0; i < len; ++i) + for (int quotient = 0; quotient < 8; ++quotient) { - byte cc = 0, input_i = input[inOff + i]; - for (int j = 0; j < 8; ++j) + for (int remainder = 0; remainder < 8; ++remainder) { int output = getOutput(); - nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0]) & 1); - lfsr = shift(lfsr, (getOutputLFSR()) & 1); - - int input_i_j = (input_i >> j) & 1; - cc |= (input_i_j ^ output) << j; - - updateInternalState(input_i_j); + nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0] ^ output ^ ((workingKey[quotient]) >> remainder)) & 1); + lfsr = shift(lfsr, (getOutputLFSR() ^ output ^ ((workingKey[quotient + 8]) >> remainder)) & 1); } - ciphertext[outOff + i] = cc; } - + initGrain(authAcc); + initGrain(authSr); + super.reset(clearMac); } private void updateInternalState(int input_i_j) @@ -302,24 +214,6 @@ private void updateInternalState(int input_i_j) lfsr = shift(lfsr, (getOutputLFSR()) & 1); } - public void processAADByte(byte in) - { - if (aadFinished) - { - throw new IllegalStateException("associated data must be added before plaintext/ciphertext"); - } - aadData.write(in); - } - - public void processAADBytes(byte[] input, int inOff, int len) - { - if (aadFinished) - { - throw new IllegalStateException("associated data must be added before plaintext/ciphertext"); - } - aadData.write(input, inOff, len); - } - private void doProcessAADBytes(byte[] input, int len) { byte[] ader; @@ -361,47 +255,123 @@ private void doProcessAADBytes(byte[] input, int len) } } - private void accumulate() - { - authAcc[0] ^= authSr[0]; - authAcc[1] ^= authSr[1]; - } - private void authShift(int val) { authSr[0] = (authSr[0] >>> 1) | (authSr[1] << 31); authSr[1] = (authSr[1] >>> 1) | (val << 31); } - public int doFinal(byte[] out, int outOff) - throws IllegalStateException, InvalidCipherTextException + public int getUpdateOutputSize(int len) { - if (!aadFinished) + int total = processor.getUpdateOutputSize(len); + switch (m_state) { - doProcessAADBytes(aadData.getBuf(), aadData.size()); - aadFinished = true; + case DecInit: + case DecAad: + case DecData: + case DecFinal: + total = Math.max(0, total + m_bufPos - MAC_SIZE); + break; + case EncData: + case EncFinal: + total = Math.max(0, total + m_bufPos); + break; + default: + break; + } + return total; + } +// +// public int getOutputSize(int len) +// { +// //the last 8 bytes are from AD +// return len + MAC_SIZE; +// } + + @Override + protected void finishAAD(State nextState, boolean isDoFinal) + { + // State indicates whether we ever received AAD + switch (m_state) + { + case DecInit: + case DecAad: + case EncInit: + case EncAad: + { + processFinalAAD(); + break; + } + default: + break; } - accumulate(); + m_aadPos = 0; + m_state = nextState; + } - this.mac = Pack.intToLittleEndian(authAcc); + @Override + protected void processFinalBlock(byte[] output, int outOff) + { + authAcc[0] ^= authSr[0]; + authAcc[1] ^= authSr[1]; + Pack.intToLittleEndian(authAcc, mac, 0); + } - System.arraycopy(mac, 0, out, outOff, mac.length); + @Override + protected void processBufferAAD(byte[] input, int inOff) + { - reset(false); + } - return mac.length; + @Override + protected void processFinalAAD() + { + doProcessAADBytes(((StreamAADOperator)aadOperator).getBytes(), aadOperator.getLen()); } - public int getUpdateOutputSize(int len) + @Override + protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) { - return len; + int len = dataOperator.getLen(); + for (int i = 0; i < len; ++i) + { + byte cc = 0, input_i = input[inOff + i]; + for (int j = 0; j < 8; ++j) + { + int rlt = getOutput(); + nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0]) & 1); + lfsr = shift(lfsr, (getOutputLFSR()) & 1); + + int input_i_j = (input_i >> j) & 1; + cc |= (input_i_j ^ rlt) << j; + + updateInternalState(input_i_j); + } + output[outOff + i] = cc; + } } - public int getOutputSize(int len) + @Override + protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff) { - //the last 8 bytes are from AD - return len + 8; + int len = dataOperator.getLen(); + for (int i = 0; i < len; ++i) + { + byte cc = 0, input_i = input[inOff + i]; + for (int j = 0; j < 8; ++j) + { + int rlt = getOutput(); + nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0]) & 1); + lfsr = shift(lfsr, (getOutputLFSR()) & 1); + + int input_i_j = (input_i >> j) & 1; + cc |= (input_i_j ^ rlt) << j; + + updateInternalState((cc >> j) & 1); + } + output[outOff + i] = cc; + } } private static int len_length(int v) @@ -418,27 +388,6 @@ private static int len_length(int v) { return 3; } - return 4; } - - private static final class ErasableOutputStream - extends ByteArrayOutputStream - { - public ErasableOutputStream() - { - } - - public byte[] getBuf() - { - return buf; - } - -// public void erase() -// { -// Arrays.fill(this.buf, (byte)0); -// // this for JVM compatibility -// this.reset(); -// } - } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java index 1d460fc514..26bbd069d7 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java @@ -442,7 +442,7 @@ static void implTestVectorsEngine(AEADCipher cipher, String path, String filenam if (a < 0) { int count = Integer.parseInt((String)map.get("Count")); -// if (count != 562) +// if (count != 67) // { // continue; // } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java index 1d9395faaf..579b7522b5 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java @@ -3,17 +3,22 @@ import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; +import java.security.SecureRandom; import java.util.HashMap; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.Grain128AEADEngine; +import org.bouncycastle.crypto.engines.XoodyakEngine; +import org.bouncycastle.crypto.modes.AEADCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; +import org.bouncycastle.util.test.SimpleTestResult; +import org.bouncycastle.util.test.TestFailedException; public class Grain128AEADTest extends SimpleTest @@ -26,50 +31,25 @@ public String getName() public void performTest() throws Exception { + + checkAEADCipherOutputSize(this, 16, 12, 8, new Grain128AEADEngine()); + CipherTest.checkCipher(32, 12, 100, 128, new CipherTest.Instance() + { + @Override + public AEADCipher createInstance() + { + return new Grain128AEADEngine(); + } + }); + CipherTest.checkAEADCipherMultipleBlocks(this, 1024, 7, 100, 128, 12, new Grain128AEADEngine()); + + CipherTest.implTestVectorsEngine(new Grain128AEADEngine(), "crypto", "LWC_AEAD_KAT_128_96.txt", this); CipherTest.checkAEADParemeter(this, 16, 12, 8, 16, new Grain128AEADEngine()); - testVectors(); testSplitUpdate(); testExceptions(); testLongAEAD(); } - private void testVectors() - throws Exception - { - Grain128AEADEngine grain = new Grain128AEADEngine(); - CipherParameters params; - InputStream src = TestResourceFinder.findTestResource("crypto", "LWC_AEAD_KAT_128_96.txt"); - BufferedReader bin = new BufferedReader(new InputStreamReader(src)); - String line; - byte[] ptByte, adByte; - byte[] rv; - HashMap map = new HashMap(); - while ((line = bin.readLine()) != null) - { - int a = line.indexOf('='); - if (a < 0) - { - params = new ParametersWithIV(new KeyParameter(Hex.decode((String)map.get("Key"))), Hex.decode((String)map.get("Nonce"))); - grain.init(true, params); - adByte = Hex.decode((String)map.get("AD")); - grain.processAADBytes(adByte, 0, adByte.length); - ptByte = Hex.decode((String)map.get("PT")); - rv = new byte[ptByte.length]; - grain.processBytes(ptByte, 0, ptByte.length, rv, 0); - byte[] mac = new byte[8]; - grain.doFinal(mac, 0); - if (!areEqual(Arrays.concatenate(rv, mac), Hex.decode((String)map.get("CT")))) - { - mismatch("Keystream " + map.get("Count"), (String)map.get("CT"), rv); - } - map.clear(); - } - else - { - map.put(line.substring(0, a).trim(), line.substring(a + 1).trim()); - } - } - } private void testSplitUpdate() throws InvalidCipherTextException @@ -96,7 +76,7 @@ private void testSplitUpdate() grain.doFinal(rv, len); isTrue(Arrays.areEqual(rv, CT)); - + grain.init(true, params); grain.processBytes(PT, 0, 10, rv, 0); try { @@ -105,7 +85,7 @@ private void testSplitUpdate() } catch (IllegalStateException e) { - isEquals("associated data must be added before plaintext/ciphertext", e.getMessage()); + isEquals("Grain-128 AEAD needs to be initialized", e.getMessage()); } try @@ -115,7 +95,7 @@ private void testSplitUpdate() } catch (IllegalStateException e) { - isEquals("associated data must be added before plaintext/ciphertext", e.getMessage()); + isEquals("Grain-128 AEAD needs to be initialized", e.getMessage()); } } @@ -128,11 +108,11 @@ private void testLongAEAD() byte[] AD = Hex.decode( // 186 bytes "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F606162636465666768696A6B6C6D6E6F707172737475767778797A7B7C7D7E7F808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9FA0A1A2A3A4A5A6A7A8A9AAABACADAEAFB0B1B2B3B4B5B6B7B8B9"); byte[] CT = Hex.decode("731DAA8B1D15317A1CCB4E3DD320095FB27E5BB2A10F2C669F870538637D4F162298C70430A2B560"); - + Grain128AEADEngine grain = new Grain128AEADEngine(); ParametersWithIV params = new ParametersWithIV(new KeyParameter(Key), Nonce); grain.init(true, params); - + grain.processAADBytes(AD, 0, AD.length); byte[] rv = new byte[CT.length]; @@ -141,7 +121,7 @@ private void testLongAEAD() len += grain.processBytes(PT, 11, PT.length - 11, rv, len); grain.doFinal(rv, len); - + isTrue(Arrays.areEqual(rv, CT)); grain.processBytes(PT, 0, 10, rv, 0); @@ -152,7 +132,7 @@ private void testLongAEAD() } catch (IllegalStateException e) { - isEquals("associated data must be added before plaintext/ciphertext", e.getMessage()); + isEquals("Grain-128 AEAD needs to be initialized", e.getMessage()); } try @@ -162,7 +142,7 @@ private void testLongAEAD() } catch (IllegalStateException e) { - isEquals("associated data must be added before plaintext/ciphertext", e.getMessage()); + isEquals("Grain-128 AEAD needs to be initialized", e.getMessage()); } } @@ -178,7 +158,7 @@ private void testExceptions() } catch (IllegalArgumentException e) { - isEquals("invalid parameters passed to Grain-128AEAD", e.getMessage()); + isEquals("invalid parameters passed to Grain-128 AEAD", e.getMessage()); } try @@ -190,7 +170,7 @@ private void testExceptions() } catch (IllegalArgumentException e) { - isEquals("Grain-128AEAD requires exactly 12 bytes of IV", e.getMessage()); + isEquals("Grain-128 AEAD requires exactly 12 bytes of IV", e.getMessage()); } try @@ -202,13 +182,57 @@ private void testExceptions() } catch (IllegalArgumentException e) { - isEquals("Grain-128AEAD key must be 16 bytes long", e.getMessage()); + isEquals("Grain-128 AEAD key must be 16 bytes long", e.getMessage()); } } - private void mismatch(String name, String expected, byte[] found) + static void checkAEADCipherOutputSize(SimpleTest parent, int keySize, int ivSize, int tagSize, AEADCipher cipher) + throws InvalidCipherTextException + { + final SecureRandom random = new SecureRandom(); + int tmpLength = random.nextInt(tagSize - 1) + 1; + final byte[] plaintext = new byte[tmpLength]; + byte[] key = new byte[keySize]; + byte[] iv = new byte[ivSize]; + random.nextBytes(key); + random.nextBytes(iv); + random.nextBytes(plaintext); + cipher.init(true, new ParametersWithIV(new KeyParameter(key), iv)); + byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)]; + //before the encrypt + isEqualTo(parent, plaintext.length + tagSize, ciphertext.length); + isEqualTo(parent, plaintext.length, cipher.getUpdateOutputSize(plaintext.length)); + //during the encrypt process of the first block + int len = cipher.processBytes(plaintext, 0, tmpLength, ciphertext, 0); + isEqualTo(parent, plaintext.length + tagSize, len + cipher.getOutputSize(plaintext.length - tmpLength)); + isEqualTo(parent, plaintext.length, len + cipher.getUpdateOutputSize(plaintext.length - tmpLength)); + //process doFinal + len += cipher.doFinal(ciphertext, len); + isEqualTo(parent, len, ciphertext.length); + + cipher.init(false, new ParametersWithIV(new KeyParameter(key), iv)); + //before the encrypt + isEqualTo(parent, plaintext.length, cipher.getOutputSize(ciphertext.length)); + isEqualTo(parent, plaintext.length, cipher.getUpdateOutputSize(ciphertext.length)); + //during the encrypt process of the first block + len = cipher.processBytes(ciphertext, 0, tmpLength, plaintext, 0); + isEqualTo(parent, plaintext.length, len + cipher.getOutputSize(ciphertext.length - tmpLength)); + isEqualTo(parent, plaintext.length, len + cipher.getUpdateOutputSize(ciphertext.length - tmpLength)); + //process doFinal + len = cipher.processBytes(ciphertext, tmpLength, tagSize, plaintext, 0); + len += cipher.doFinal(plaintext, len); + isEqualTo(parent, len, plaintext.length); + } + + static void isEqualTo( + SimpleTest parent, + int a, + int b) { - fail("mismatch on " + name, expected, new String(Hex.encode(found))); + if (a != b) + { + throw new TestFailedException(SimpleTestResult.failed(parent, "no message")); + } } public static void main(String[] args) From b56ace48bdd2492f21677f728f03315e0f2873f1 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 14 Mar 2025 12:15:01 +0700 Subject: [PATCH 214/890] Remove debug print --- .../org/bouncycastle/pqc/jcajce/provider/hqc/HQCCipherSpi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCCipherSpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCCipherSpi.java index 92a6c432dc..8e51066d8b 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCCipherSpi.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCCipherSpi.java @@ -268,7 +268,7 @@ protected byte[] engineWrap( return rv; } catch (IllegalArgumentException e) - { e.printStackTrace(); + { throw new IllegalBlockSizeException("unable to generate KTS secret: " + e.getMessage()); } catch (DestroyFailedException e) From f29c16ba00c4cf8b4cc318a694beb89c05207f16 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 14 Mar 2025 16:13:44 +1030 Subject: [PATCH 215/890] Refactor of Grain128AEADEngine --- .../crypto/engines/Grain128AEADEngine.java | 130 ++++++++---------- .../crypto/test/Grain128AEADTest.java | 11 +- 2 files changed, 60 insertions(+), 81 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java index 2bffd9fcec..d53b61caff 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java @@ -65,9 +65,7 @@ private void initGrain(int[] auth) { for (int remainder = 0; remainder < 32; ++remainder) { - int output = getOutput(); - nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0]) & 1); - lfsr = shift(lfsr, (getOutputLFSR()) & 1); + int output = getByteKeyStream(); auth[quotient] |= output << remainder; } } @@ -176,6 +174,12 @@ private int[] shift(int[] array, int val) return array; } + private void shift() + { + nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0]) & 1); + lfsr = shift(lfsr, (getOutputLFSR()) & 1); + } + protected void reset(boolean clearMac) { this.aadOperator.reset(); @@ -208,59 +212,12 @@ private void updateInternalState(int input_i_j) int mask = -input_i_j; authAcc[0] ^= authSr[0] & mask; authAcc[1] ^= authSr[1] & mask; - - authShift(getOutput()); - nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0]) & 1); - lfsr = shift(lfsr, (getOutputLFSR()) & 1); - } - - private void doProcessAADBytes(byte[] input, int len) - { - byte[] ader; - int aderlen; - //encodeDer - if (len < 128) - { - ader = new byte[1 + len]; - ader[0] = (byte)len; - aderlen = 0; - } - else - { - // aderlen is the highest bit position divided by 8 - aderlen = len_length(len); - ader = new byte[1 + aderlen + len]; - ader[0] = (byte)(0x80 | aderlen); - int tmp = len; - for (int i = 0; i < aderlen; ++i) - { - ader[1 + i] = (byte)tmp; - tmp >>>= 8; - } - } - System.arraycopy(input, 0, ader, 1 + aderlen, len); - - for (int i = 0; i < ader.length; ++i) - { - byte ader_i = ader[i]; - for (int j = 0; j < 8; ++j) - { - nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0]) & 1); - lfsr = shift(lfsr, (getOutputLFSR()) & 1); - - int ader_i_j = (ader_i >> j) & 1; - - updateInternalState(ader_i_j); - } - } - } - - private void authShift(int val) - { + int val = getByteKeyStream(); authSr[0] = (authSr[0] >>> 1) | (authSr[1] << 31); authSr[1] = (authSr[1] >>> 1) | (val << 31); } + public int getUpdateOutputSize(int len) { int total = processor.getUpdateOutputSize(len); @@ -281,12 +238,6 @@ public int getUpdateOutputSize(int len) } return total; } -// -// public int getOutputSize(int len) -// { -// //the last 8 bytes are from AD -// return len + MAC_SIZE; -// } @Override protected void finishAAD(State nextState, boolean isDoFinal) @@ -327,7 +278,53 @@ protected void processBufferAAD(byte[] input, int inOff) @Override protected void processFinalAAD() { - doProcessAADBytes(((StreamAADOperator)aadOperator).getBytes(), aadOperator.getLen()); + int len = aadOperator.getLen(); + byte[] input = ((StreamAADOperator)aadOperator).getBytes(); + byte[] ader; + + //encodeDer + if (len < 128) + { + ader = new byte[1]; + ader[0] = (byte)len; + } + else + { + // aderlen is the highest bit position divided by 8 + int aderlen = len_length(len); + ader = new byte[1 + aderlen]; + ader[0] = (byte)(0x80 | aderlen); + int tmp = len; + for (int i = 0; i < aderlen; ++i) + { + ader[1 + i] = (byte)tmp; + tmp >>>= 8; + } + } + + absorbAadData(ader, ader.length); + absorbAadData(input, len); + } + + private void absorbAadData(byte[] ader, int len) + { + for (int i = 0; i < len; ++i) + { + byte ader_i = ader[i]; + for (int j = 0; j < 8; ++j) + { + shift(); + int ader_i_j = (ader_i >> j) & 1; + updateInternalState(ader_i_j); + } + } + } + + private int getByteKeyStream() + { + int rlt = getOutput(); + shift(); + return rlt; } @Override @@ -339,13 +336,8 @@ protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int byte cc = 0, input_i = input[inOff + i]; for (int j = 0; j < 8; ++j) { - int rlt = getOutput(); - nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0]) & 1); - lfsr = shift(lfsr, (getOutputLFSR()) & 1); - int input_i_j = (input_i >> j) & 1; - cc |= (input_i_j ^ rlt) << j; - + cc |= (input_i_j ^ getByteKeyStream()) << j; updateInternalState(input_i_j); } output[outOff + i] = cc; @@ -361,13 +353,7 @@ protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int byte cc = 0, input_i = input[inOff + i]; for (int j = 0; j < 8; ++j) { - int rlt = getOutput(); - nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0]) & 1); - lfsr = shift(lfsr, (getOutputLFSR()) & 1); - - int input_i_j = (input_i >> j) & 1; - cc |= (input_i_j ^ rlt) << j; - + cc |= (((input_i >> j) & 1) ^ getByteKeyStream()) << j; updateInternalState((cc >> j) & 1); } output[outOff + i] = cc; diff --git a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java index 579b7522b5..33124af334 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java @@ -1,19 +1,12 @@ package org.bouncycastle.crypto.test; -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; import java.security.SecureRandom; -import java.util.HashMap; -import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.Grain128AEADEngine; -import org.bouncycastle.crypto.engines.XoodyakEngine; import org.bouncycastle.crypto.modes.AEADCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; -import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -31,7 +24,7 @@ public String getName() public void performTest() throws Exception { - + CipherTest.implTestVectorsEngine(new Grain128AEADEngine(), "crypto", "LWC_AEAD_KAT_128_96.txt", this); checkAEADCipherOutputSize(this, 16, 12, 8, new Grain128AEADEngine()); CipherTest.checkCipher(32, 12, 100, 128, new CipherTest.Instance() { @@ -43,7 +36,7 @@ public AEADCipher createInstance() }); CipherTest.checkAEADCipherMultipleBlocks(this, 1024, 7, 100, 128, 12, new Grain128AEADEngine()); - CipherTest.implTestVectorsEngine(new Grain128AEADEngine(), "crypto", "LWC_AEAD_KAT_128_96.txt", this); + CipherTest.checkAEADParemeter(this, 16, 12, 8, 16, new Grain128AEADEngine()); testSplitUpdate(); testExceptions(); From 4d6a1825cbe606c05b8bbe199f561fc6288e83c8 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 14 Mar 2025 16:39:58 +1030 Subject: [PATCH 216/890] Merge AEADBaseEngine and AEADBufferBaseEngine into one class --- .../crypto/engines/AEADBaseEngine.java | 769 +++++++++++++++++- .../crypto/engines/AEADBufferBaseEngine.java | 769 ------------------ .../crypto/engines/AsconAEAD128.java | 4 - .../crypto/engines/AsconBaseEngine.java | 5 +- .../crypto/engines/AsconEngine.java | 4 - .../crypto/engines/ElephantEngine.java | 7 +- .../crypto/engines/GiftCofbEngine.java | 5 +- .../crypto/engines/Grain128AEADEngine.java | 5 +- .../crypto/engines/ISAPEngine.java | 8 +- .../crypto/engines/PhotonBeetleEngine.java | 10 +- .../crypto/engines/RomulusEngine.java | 7 +- .../crypto/engines/SparkleEngine.java | 9 +- .../crypto/engines/XoodyakEngine.java | 12 +- .../crypto/test/Grain128AEADTest.java | 10 +- 14 files changed, 795 insertions(+), 829 deletions(-) delete mode 100644 core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java index 25cff6b482..1598e97fbb 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java @@ -1,17 +1,56 @@ package org.bouncycastle.crypto.engines; +import java.io.ByteArrayOutputStream; + import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.constraints.DefaultServiceProperties; import org.bouncycastle.crypto.modes.AEADCipher; import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; abstract class AEADBaseEngine implements AEADCipher { + protected enum ProcessingBufferType + { + Buffered, // Store a (aad) block size of input and process after the input size exceeds the buffer size + Immediate, //process the input immediately when the input size is equal or greater than the block size + } + + protected enum AADOperatorType + { + Default, + Counter,//add a counter to count the size of AAD + Stream //process AAD data during the process data, used for elephant + } + + protected enum DataOperatorType + { + Default, + Counter, + Stream, + StreamCipher + } + + protected enum State + { + Uninitialized, + EncInit, + EncAad, // can process AAD + EncData, // cannot process AAD + EncFinal, + DecInit, + DecAad, // can process AAD + DecData, // cannot process AAD + DecFinal, + } + protected boolean forEncryption; protected String algorithmName; protected int KEY_SIZE; @@ -19,6 +58,17 @@ abstract class AEADBaseEngine protected int MAC_SIZE; protected byte[] initialAssociatedText; protected byte[] mac; + protected byte[] m_buf; + protected byte[] m_aad; + protected int m_bufPos; + protected int m_aadPos; + protected int AADBufferSize; + protected int BlockSize; + protected State m_state = State.Uninitialized; + protected int m_bufferSizeDecrypt; + protected AADProcessingBuffer processor; + protected AADOperator aadOperator; + protected DataOperator dataOperator; @Override public String getAlgorithmName() @@ -102,20 +152,737 @@ else if (params instanceof ParametersWithIV) CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties( this.getAlgorithmName(), 128, params, Utils.getPurpose(forEncryption))); + m_state = forEncryption ? State.EncInit : State.DecInit; init(k, npub); + reset(true); if (initialAssociatedText != null) { processAADBytes(initialAssociatedText, 0, initialAssociatedText.length); } } - protected abstract void init(byte[] key, byte[] iv); + protected void reset(boolean clearMac) { + ensureInitialized(); if (clearMac) { mac = null; } + if (m_buf != null) + { + Arrays.fill(m_buf, (byte)0); + m_bufPos = 0; + } + if (m_aad != null) + { + Arrays.fill(m_aad, (byte)0); + m_aadPos = 0; + } + switch (m_state) + { + case DecInit: + case EncInit: + break; + case DecAad: + case DecData: + case DecFinal: + m_state = State.DecFinal; + break; + case EncAad: + case EncData: + case EncFinal: + m_state = State.EncFinal; + return; + default: + throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); + } + aadOperator.reset(); + dataOperator.reset(); + } + + protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOperatorType, DataOperatorType dataOperatorType) + { + switch (type) + { + case Buffered: + processor = new BufferedAADProcessor(); + break; + case Immediate: + processor = new ImmediateAADProcessor(); + break; + } + + m_bufferSizeDecrypt = BlockSize + MAC_SIZE; + + switch (aadOperatorType) + { + case Default: + m_aad = new byte[AADBufferSize]; + aadOperator = new DefaultAADOperator(); + break; + case Counter: + m_aad = new byte[AADBufferSize]; + aadOperator = new CounterAADOperator(); + break; + case Stream: + aadOperator = new StreamAADOperator(); + break; + } + + switch (dataOperatorType) + { + case Default: + m_buf = new byte[m_bufferSizeDecrypt]; + dataOperator = new DefaultDataOperator(); + break; + case Counter: + m_buf = new byte[m_bufferSizeDecrypt]; + dataOperator = new CounterDataOperator(); + break; + case Stream: + m_buf = new byte[MAC_SIZE]; + dataOperator = new StreamDataOperator(); + break; + case StreamCipher: + BlockSize = 0; + m_buf = new byte[m_bufferSizeDecrypt]; + dataOperator = new StreamCipherOperator(); + break; + } + } + + protected interface AADProcessingBuffer + { + void processAADByte(byte input); + + int getUpdateOutputSize(int len); + + boolean isLengthWithinAvailableSpace(int len, int available); + + boolean isLengthExceedingBlockSize(int len, int size); + } + + private class BufferedAADProcessor + implements AADProcessingBuffer + { + public void processAADByte(byte input) + { + if (m_aadPos == AADBufferSize) + { + processBufferAAD(m_aad, 0); + m_aadPos = 0; + } + m_aad[m_aadPos++] = input; + } + + @Override + public boolean isLengthWithinAvailableSpace(int len, int available) + { + return len <= available; + } + + @Override + public boolean isLengthExceedingBlockSize(int len, int size) + { + return len > size; + } + + @Override + public int getUpdateOutputSize(int len) + { + // The -1 is to account for the lazy processing of a full buffer + return Math.max(0, len) - 1; + } + } + + private class ImmediateAADProcessor + implements AADProcessingBuffer + { + public void processAADByte(byte input) + { + m_aad[m_aadPos++] = input; + if (m_aadPos == AADBufferSize) + { + processBufferAAD(m_aad, 0); + m_aadPos = 0; + } + } + + @Override + public int getUpdateOutputSize(int len) + { + return Math.max(0, len); + } + + @Override + public boolean isLengthWithinAvailableSpace(int len, int available) + { + return len < available; + } + + @Override + public boolean isLengthExceedingBlockSize(int len, int size) + { + return len >= size; + } + } + + protected interface AADOperator + { + void processAADByte(byte input); + + void processAADBytes(byte[] input, int inOff, int len); + + void reset(); + + int getLen(); + } + + protected class DefaultAADOperator + implements AADOperator + { + @Override + public void processAADByte(byte input) + { + processor.processAADByte(input); + } + + @Override + public void processAADBytes(byte[] input, int inOff, int len) + { + processAadBytes(input, inOff, len); + } + + public void reset() + { + } + + @Override + public int getLen() + { + return m_aadPos; + } + } + + protected class CounterAADOperator + implements AADOperator + { + private int aadLen; + + @Override + public void processAADByte(byte input) + { + aadLen++; + processor.processAADByte(input); + } + + @Override + public void processAADBytes(byte[] input, int inOff, int len) + { + aadLen += len; + processAadBytes(input, inOff, len); + } + + public int getLen() + { + return aadLen; + } + + public void reset() + { + aadLen = 0; + } + } + + protected static class StreamAADOperator + implements AADOperator + { + private final ErasableOutputStream stream = new ErasableOutputStream(); + + @Override + public void processAADByte(byte input) + { + stream.write(input); + } + + @Override + public void processAADBytes(byte[] input, int inOff, int len) + { + stream.write(input, inOff, len); + } + + public byte[] getBytes() + { + return stream.getBuf(); + } + + @Override + public void reset() + { + stream.reset(); + } + + @Override + public int getLen() + { + return stream.size(); + } + } + + protected interface DataOperator + { + int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff); + + int getLen(); + + void reset(); + } + + protected class DefaultDataOperator + implements DataOperator + { + public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + return processEncDecBytes(input, inOff, len, output, outOff); + } + + @Override + public int getLen() + { + return m_bufPos; + } + + @Override + public void reset() + { + } + } + + protected class CounterDataOperator + implements DataOperator + { + private int messegeLen; + + public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + messegeLen += len; + return processEncDecBytes(input, inOff, len, output, outOff); + } + + @Override + public int getLen() + { + return messegeLen; + } + + @Override + public void reset() + { + messegeLen = 0; + } + } + + protected class StreamDataOperator + implements DataOperator + { + private final ErasableOutputStream stream = new ErasableOutputStream(); + + @Override + public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + ensureInitialized(); + stream.write(input, inOff, len); + m_bufPos = stream.size(); + return 0; + } + + public byte[] getBytes() + { + return stream.getBuf(); + } + + @Override + public int getLen() + { + return stream.size(); + } + + @Override + public void reset() + { + stream.reset(); + } + } + + protected class StreamCipherOperator + implements DataOperator + { + private int len; + + @Override + public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + boolean forEncryption = checkData(false); + if (forEncryption) + { + this.len = len; + processBufferEncrypt(input, inOff, output, outOff); + return len; + } + else + { + // keep last mac size bytes + int available = Math.max(m_bufPos + len - MAC_SIZE, 0); + int rlt = 0; + if (m_bufPos > 0) + { + this.len = Math.min(available, m_bufPos); + rlt = this.len; + processBufferDecrypt(m_buf, 0, output, outOff); + available -= rlt; + m_bufPos -= rlt; + System.arraycopy(m_buf, rlt, m_buf, 0, m_bufPos); + } + if (available > 0) + { + this.len = available; + processBufferDecrypt(input, inOff, output, outOff); + rlt += available; + len -= available; + inOff += available; + } + + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return rlt; + } + } + + @Override + public int getLen() + { + return len; + } + + @Override + public void reset() + { + } + } + + protected static final class ErasableOutputStream + extends ByteArrayOutputStream + { + public ErasableOutputStream() + { + } + + public byte[] getBuf() + { + return buf; + } + } + + @Override + public void processAADByte(byte input) + { + checkAAD(); + aadOperator.processAADByte(input); + } + + @Override + public void processAADBytes(byte[] input, int inOff, int len) + { + ensureSufficientInputBuffer(input, inOff, len); + // Don't enter AAD state until we actually get input + if (len <= 0) + { + return; + } + + checkAAD(); + aadOperator.processAADBytes(input, inOff, len); + } + + private void processAadBytes(byte[] input, int inOff, int len) + { + if (m_aadPos > 0) + { + int available = AADBufferSize - m_aadPos; + if (processor.isLengthWithinAvailableSpace(len, available)) + { + System.arraycopy(input, inOff, m_aad, m_aadPos, len); + m_aadPos += len; + return; + } + + System.arraycopy(input, inOff, m_aad, m_aadPos, available); + inOff += available; + len -= available; + + processBufferAAD(m_aad, 0); + } + while (processor.isLengthExceedingBlockSize(len, AADBufferSize)) + { + processBufferAAD(input, inOff); + inOff += AADBufferSize; + len -= AADBufferSize; + } + System.arraycopy(input, inOff, m_aad, 0, len); + m_aadPos = len; + } + + @Override + public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + throws DataLengthException + { + ensureSufficientInputBuffer(input, inOff, len); + return dataOperator.processBytes(input, inOff, len, output, outOff); + } + + protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + boolean forEncryption = checkData(false); + int available, resultLength; + available = (forEncryption ? BlockSize : m_bufferSizeDecrypt) - m_bufPos; + // The function is just an operator < or <= + if (processor.isLengthWithinAvailableSpace(len, available)) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return 0; + } + resultLength = processor.getUpdateOutputSize(len) + m_bufPos - (forEncryption ? 0 : MAC_SIZE); + ensureSufficientOutputBuffer(output, outOff, resultLength - resultLength % BlockSize); + resultLength = 0; + if (forEncryption) + { + if (m_bufPos > 0) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; + processBufferEncrypt(m_buf, 0, output, outOff); + resultLength = BlockSize; + } + // The function is just an operator >= or > + while (processor.isLengthExceedingBlockSize(len, BlockSize)) + { + processBufferEncrypt(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } + } + else + { + // loop will run more than once for the following situation: pb128, ascon80pq, ascon128, ISAP_A_128(A) + while (processor.isLengthExceedingBlockSize(m_bufPos, BlockSize) + && processor.isLengthExceedingBlockSize(len + m_bufPos, m_bufferSizeDecrypt)) + { + processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); + m_bufPos -= BlockSize; + resultLength += BlockSize; + } + if (m_bufPos > 0) + { + System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); + if (processor.isLengthWithinAvailableSpace(m_bufPos + len, m_bufferSizeDecrypt)) + { + System.arraycopy(input, inOff, m_buf, m_bufPos, len); + m_bufPos += len; + return resultLength; + } + available = Math.max(BlockSize - m_bufPos, 0); + System.arraycopy(input, inOff, m_buf, m_bufPos, available); + inOff += available; + len -= available; + processBufferDecrypt(m_buf, 0, output, outOff + resultLength); + resultLength += BlockSize; + } + while (processor.isLengthExceedingBlockSize(len, m_bufferSizeDecrypt)) + { + processBufferDecrypt(input, inOff, output, outOff + resultLength); + inOff += BlockSize; + len -= BlockSize; + resultLength += BlockSize; + } + } + System.arraycopy(input, inOff, m_buf, 0, len); + m_bufPos = len; + return resultLength; + } + + @Override + public int doFinal(byte[] output, int outOff) + throws IllegalStateException, InvalidCipherTextException + { + boolean forEncryption = checkData(true); + int resultLength; + if (forEncryption) + { + resultLength = m_bufPos + MAC_SIZE; + } + else + { + if (m_bufPos < MAC_SIZE) + { + throw new InvalidCipherTextException("data too short"); + } + + m_bufPos -= MAC_SIZE; + + resultLength = m_bufPos; + } + + ensureSufficientOutputBuffer(output, outOff, resultLength); + mac = new byte[MAC_SIZE]; + processFinalBlock(output, outOff); + if (forEncryption) + { + System.arraycopy(mac, 0, output, outOff + resultLength - MAC_SIZE, MAC_SIZE); + } + else + { + if (!Arrays.constantTimeAreEqual(MAC_SIZE, mac, 0, m_buf, m_bufPos)) + { + throw new InvalidCipherTextException(algorithmName + " mac does not match"); + } + } + reset(!forEncryption); + return resultLength; + } + + public final int getBlockSize() + { + return BlockSize; + } + + public int getUpdateOutputSize(int len) + { + int total = processor.getUpdateOutputSize(len); + switch (m_state) + { + case DecInit: + case DecAad: + case DecData: + case DecFinal: + total = Math.max(0, total + m_bufPos - MAC_SIZE); + break; + case EncData: + case EncFinal: + total = Math.max(0, total + m_bufPos); + break; + default: + break; + } + return total - total % BlockSize; + } + + public int getOutputSize(int len) + { + int total = Math.max(0, len); + + switch (m_state) + { + case DecInit: + case DecAad: + case DecData: + case DecFinal: + return Math.max(0, total + m_bufPos - MAC_SIZE); + case EncData: + case EncFinal: + return total + m_bufPos + MAC_SIZE; + default: + return total + MAC_SIZE; + } + } + + protected void checkAAD() + { + switch (m_state) + { + case DecInit: + m_state = State.DecAad; + break; + case EncInit: + m_state = State.EncAad; + break; + case DecAad: + case EncAad: + break; + case EncFinal: + throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption"); + default: + throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); + } + } + + protected boolean checkData(boolean isDoFinal) + { + switch (m_state) + { + case DecInit: + case DecAad: + finishAAD(State.DecData, isDoFinal); + return false; + case EncInit: + case EncAad: + finishAAD(State.EncData, isDoFinal); + return true; + case DecData: + return false; + case EncData: + return true; + case EncFinal: + throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption"); + default: + throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); + } + } + + protected abstract void finishAAD(State nextState, boolean isDoFinal); + + protected final void bufferReset() + { + } + + protected final void ensureSufficientOutputBuffer(byte[] output, int outOff, int len) + { + if (outOff + len > output.length) + { + throw new OutputLengthException("output buffer too short"); + } + } + + protected final void ensureSufficientInputBuffer(byte[] input, int inOff, int len) + { + if (inOff + len > input.length) + { + throw new DataLengthException("input buffer too short"); + } + } + + protected final void ensureInitialized() + { + if (m_state == State.Uninitialized) + { + throw new IllegalStateException("Need to call init function before operation"); + } + } + + protected abstract void init(byte[] key, byte[] iv); + + protected abstract void processFinalBlock(byte[] output, int outOff); + + protected abstract void processBufferAAD(byte[] input, int inOff); + + protected abstract void processFinalAAD(); + + protected abstract void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff); + + protected abstract void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff); } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java deleted file mode 100644 index 87fee36a32..0000000000 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ /dev/null @@ -1,769 +0,0 @@ -package org.bouncycastle.crypto.engines; - -import java.io.ByteArrayOutputStream; - -import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.InvalidCipherTextException; -import org.bouncycastle.crypto.OutputLengthException; -import org.bouncycastle.util.Arrays; - -abstract class AEADBufferBaseEngine - extends AEADBaseEngine -{ - protected enum ProcessingBufferType - { - Buffered, // Store a (aad) block size of input and process after the input size exceeds the buffer size - Immediate, //process the input immediately when the input size is equal or greater than the block size - } - - protected enum AADOperatorType - { - Default, - Counter,//add a counter to count the size of AAD - Stream //process AAD data during the process data, used for elephant - } - - protected enum DataOperatorType - { - Default, - Counter, - Stream, - StreamCipher - } - - protected enum State - { - Uninitialized, - EncInit, - EncAad, // can process AAD - EncData, // cannot process AAD - EncFinal, - DecInit, - DecAad, // can process AAD - DecData, // cannot process AAD - DecFinal, - } - - protected byte[] m_buf; - protected byte[] m_aad; - protected int m_bufPos; - protected int m_aadPos; - protected int AADBufferSize; - protected int BlockSize; - protected State m_state = State.Uninitialized; - protected int m_bufferSizeDecrypt; - protected AADProcessingBuffer processor; - protected AADOperator aadOperator; - protected DataOperator dataOperator; - - protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOperatorType, DataOperatorType dataOperatorType) - { - switch (type) - { - case Buffered: - processor = new BufferedAADProcessor(); - break; - case Immediate: - processor = new ImmediateAADProcessor(); - break; - } - - m_bufferSizeDecrypt = BlockSize + MAC_SIZE; - - switch (aadOperatorType) - { - case Default: - m_aad = new byte[AADBufferSize]; - aadOperator = new DefaultAADOperator(); - break; - case Counter: - m_aad = new byte[AADBufferSize]; - aadOperator = new CounterAADOperator(); - break; - case Stream: - aadOperator = new StreamAADOperator(); - break; - } - - switch (dataOperatorType) - { - case Default: - m_buf = new byte[m_bufferSizeDecrypt]; - dataOperator = new DefaultDataOperator(); - break; - case Counter: - m_buf = new byte[m_bufferSizeDecrypt]; - dataOperator = new CounterDataOperator(); - break; - case Stream: - m_buf = new byte[MAC_SIZE]; - dataOperator = new StreamDataOperator(); - break; - case StreamCipher: - BlockSize = 0; - m_buf = new byte[m_bufferSizeDecrypt]; - dataOperator = new StreamCipherOperator(); - break; - } - } - - protected interface AADProcessingBuffer - { - void processAADByte(byte input); - - int getUpdateOutputSize(int len); - - boolean isLengthWithinAvailableSpace(int len, int available); - - boolean isLengthExceedingBlockSize(int len, int size); - } - - private class BufferedAADProcessor - implements AADProcessingBuffer - { - public void processAADByte(byte input) - { - if (m_aadPos == AADBufferSize) - { - processBufferAAD(m_aad, 0); - m_aadPos = 0; - } - m_aad[m_aadPos++] = input; - } - - @Override - public boolean isLengthWithinAvailableSpace(int len, int available) - { - return len <= available; - } - - @Override - public boolean isLengthExceedingBlockSize(int len, int size) - { - return len > size; - } - - @Override - public int getUpdateOutputSize(int len) - { - // The -1 is to account for the lazy processing of a full buffer - return Math.max(0, len) - 1; - } - } - - private class ImmediateAADProcessor - implements AADProcessingBuffer - { - public void processAADByte(byte input) - { - m_aad[m_aadPos++] = input; - if (m_aadPos == AADBufferSize) - { - processBufferAAD(m_aad, 0); - m_aadPos = 0; - } - } - - @Override - public int getUpdateOutputSize(int len) - { - return Math.max(0, len); - } - - @Override - public boolean isLengthWithinAvailableSpace(int len, int available) - { - return len < available; - } - - @Override - public boolean isLengthExceedingBlockSize(int len, int size) - { - return len >= size; - } - } - - protected interface AADOperator - { - void processAADByte(byte input); - - void processAADBytes(byte[] input, int inOff, int len); - - void reset(); - - int getLen(); - } - - protected class DefaultAADOperator - implements AADOperator - { - @Override - public void processAADByte(byte input) - { - processor.processAADByte(input); - } - - @Override - public void processAADBytes(byte[] input, int inOff, int len) - { - processAadBytes(input, inOff, len); - } - - public void reset() - { - } - - @Override - public int getLen() - { - return m_aadPos; - } - } - - protected class CounterAADOperator - implements AADOperator - { - private int aadLen; - - @Override - public void processAADByte(byte input) - { - aadLen++; - processor.processAADByte(input); - } - - @Override - public void processAADBytes(byte[] input, int inOff, int len) - { - aadLen += len; - processAadBytes(input, inOff, len); - } - - public int getLen() - { - return aadLen; - } - - public void reset() - { - aadLen = 0; - } - } - - protected static class StreamAADOperator - implements AADOperator - { - private final ErasableOutputStream stream = new ErasableOutputStream(); - - @Override - public void processAADByte(byte input) - { - stream.write(input); - } - - @Override - public void processAADBytes(byte[] input, int inOff, int len) - { - stream.write(input, inOff, len); - } - - public byte[] getBytes() - { - return stream.getBuf(); - } - - @Override - public void reset() - { - stream.reset(); - } - - @Override - public int getLen() - { - return stream.size(); - } - } - - protected interface DataOperator - { - int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff); - - int getLen(); - - void reset(); - } - - protected class DefaultDataOperator - implements DataOperator - { - public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - { - return processEncDecBytes(input, inOff, len, output, outOff); - } - - @Override - public int getLen() - { - return m_bufPos; - } - - @Override - public void reset() - { - } - } - - protected class CounterDataOperator - implements DataOperator - { - private int messegeLen; - - public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - { - messegeLen += len; - return processEncDecBytes(input, inOff, len, output, outOff); - } - - @Override - public int getLen() - { - return messegeLen; - } - - @Override - public void reset() - { - messegeLen = 0; - } - } - - protected class StreamDataOperator - implements DataOperator - { - private final ErasableOutputStream stream = new ErasableOutputStream(); - - @Override - public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - { - ensureInitialized(); - stream.write(input, inOff, len); - m_bufPos = stream.size(); - return 0; - } - - public byte[] getBytes() - { - return stream.getBuf(); - } - - @Override - public int getLen() - { - return stream.size(); - } - - @Override - public void reset() - { - stream.reset(); - } - } - - protected class StreamCipherOperator - implements DataOperator - { - private int len; - - @Override - public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - { - boolean forEncryption = checkData(false); - if (forEncryption) - { - this.len = len; - processBufferEncrypt(input, inOff, output, outOff); - return len; - } - else - { - // keep last mac size bytes - int available = Math.max(m_bufPos + len - MAC_SIZE, 0); - int rlt = 0; - if (m_bufPos > 0) - { - this.len = Math.min(available, m_bufPos); - rlt = this.len; - processBufferDecrypt(m_buf, 0, output, outOff); - available -= rlt; - m_bufPos -= rlt; - System.arraycopy(m_buf, rlt, m_buf, 0, m_bufPos); - } - if (available > 0) - { - this.len = available; - processBufferDecrypt(input, inOff, output, outOff); - rlt += available; - len -= available; - inOff += available; - } - - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return rlt; - } - } - - @Override - public int getLen() - { - return len; - } - - @Override - public void reset() - { - } - } - - protected static final class ErasableOutputStream - extends ByteArrayOutputStream - { - public ErasableOutputStream() - { - } - - public byte[] getBuf() - { - return buf; - } - } - - @Override - public void processAADByte(byte input) - { - checkAAD(); - aadOperator.processAADByte(input); - } - - @Override - public void processAADBytes(byte[] input, int inOff, int len) - { - ensureSufficientInputBuffer(input, inOff, len); - // Don't enter AAD state until we actually get input - if (len <= 0) - { - return; - } - - checkAAD(); - aadOperator.processAADBytes(input, inOff, len); - } - - private void processAadBytes(byte[] input, int inOff, int len) - { - if (m_aadPos > 0) - { - int available = AADBufferSize - m_aadPos; - if (processor.isLengthWithinAvailableSpace(len, available)) - { - System.arraycopy(input, inOff, m_aad, m_aadPos, len); - m_aadPos += len; - return; - } - - System.arraycopy(input, inOff, m_aad, m_aadPos, available); - inOff += available; - len -= available; - - processBufferAAD(m_aad, 0); - } - while (processor.isLengthExceedingBlockSize(len, AADBufferSize)) - { - processBufferAAD(input, inOff); - inOff += AADBufferSize; - len -= AADBufferSize; - } - System.arraycopy(input, inOff, m_aad, 0, len); - m_aadPos = len; - } - - @Override - public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - throws DataLengthException - { - ensureSufficientInputBuffer(input, inOff, len); - return dataOperator.processBytes(input, inOff, len, output, outOff); - } - - protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - { - boolean forEncryption = checkData(false); - int available, resultLength; - available = (forEncryption ? BlockSize : m_bufferSizeDecrypt) - m_bufPos; - // The function is just an operator < or <= - if (processor.isLengthWithinAvailableSpace(len, available)) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } - resultLength = processor.getUpdateOutputSize(len) + m_bufPos - (forEncryption ? 0 : MAC_SIZE); - ensureSufficientOutputBuffer(output, outOff, resultLength - resultLength % BlockSize); - resultLength = 0; - if (forEncryption) - { - if (m_bufPos > 0) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - processBufferEncrypt(m_buf, 0, output, outOff); - resultLength = BlockSize; - } - // The function is just an operator >= or > - while (processor.isLengthExceedingBlockSize(len, BlockSize)) - { - processBufferEncrypt(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; - } - } - else - { - // loop will run more than once for the following situation: pb128, ascon80pq, ascon128, ISAP_A_128(A) - while (processor.isLengthExceedingBlockSize(m_bufPos, BlockSize) - && processor.isLengthExceedingBlockSize(len + m_bufPos, m_bufferSizeDecrypt)) - { - processBufferDecrypt(m_buf, resultLength, output, outOff + resultLength); - m_bufPos -= BlockSize; - resultLength += BlockSize; - } - if (m_bufPos > 0) - { - System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); - if (processor.isLengthWithinAvailableSpace(m_bufPos + len, m_bufferSizeDecrypt)) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return resultLength; - } - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - processBufferDecrypt(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - } - while (processor.isLengthExceedingBlockSize(len, m_bufferSizeDecrypt)) - { - processBufferDecrypt(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; - } - } - System.arraycopy(input, inOff, m_buf, 0, len); - m_bufPos = len; - return resultLength; - } - - @Override - public int doFinal(byte[] output, int outOff) - throws IllegalStateException, InvalidCipherTextException - { - boolean forEncryption = checkData(true); - int resultLength; - if (forEncryption) - { - resultLength = m_bufPos + MAC_SIZE; - } - else - { - if (m_bufPos < MAC_SIZE) - { - throw new InvalidCipherTextException("data too short"); - } - - m_bufPos -= MAC_SIZE; - - resultLength = m_bufPos; - } - - ensureSufficientOutputBuffer(output, outOff, resultLength); - mac = new byte[MAC_SIZE]; - processFinalBlock(output, outOff); - if (forEncryption) - { - System.arraycopy(mac, 0, output, outOff + resultLength - MAC_SIZE, MAC_SIZE); - } - else - { - if (!Arrays.constantTimeAreEqual(MAC_SIZE, mac, 0, m_buf, m_bufPos)) - { - throw new InvalidCipherTextException(algorithmName + " mac does not match"); - } - } - reset(!forEncryption); - return resultLength; - } - - public final int getBlockSize() - { - return BlockSize; - } - - public int getUpdateOutputSize(int len) - { - int total = processor.getUpdateOutputSize(len); - switch (m_state) - { - case DecInit: - case DecAad: - case DecData: - case DecFinal: - total = Math.max(0, total + m_bufPos - MAC_SIZE); - break; - case EncData: - case EncFinal: - total = Math.max(0, total + m_bufPos); - break; - default: - break; - } - return total - total % BlockSize; - } - - public int getOutputSize(int len) - { - int total = Math.max(0, len); - - switch (m_state) - { - case DecInit: - case DecAad: - case DecData: - case DecFinal: - return Math.max(0, total + m_bufPos - MAC_SIZE); - case EncData: - case EncFinal: - return total + m_bufPos + MAC_SIZE; - default: - return total + MAC_SIZE; - } - } - - protected void checkAAD() - { - switch (m_state) - { - case DecInit: - m_state = State.DecAad; - break; - case EncInit: - m_state = State.EncAad; - break; - case DecAad: - case EncAad: - break; - case EncFinal: - throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption"); - default: - throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); - } - } - - protected boolean checkData(boolean isDoFinal) - { - switch (m_state) - { - case DecInit: - case DecAad: - finishAAD(State.DecData, isDoFinal); - return false; - case EncInit: - case EncAad: - finishAAD(State.EncData, isDoFinal); - return true; - case DecData: - return false; - case EncData: - return true; - case EncFinal: - throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption"); - default: - throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); - } - } - - protected abstract void finishAAD(State nextState, boolean isDoFinal); - - protected final void bufferReset() - { - if (m_buf != null) - { - Arrays.fill(m_buf, (byte)0); - m_bufPos = 0; - } - if (m_aad != null) - { - Arrays.fill(m_aad, (byte)0); - m_aadPos = 0; - } - switch (m_state) - { - case DecInit: - case EncInit: - break; - case DecAad: - case DecData: - case DecFinal: - m_state = State.DecFinal; - break; - case EncAad: - case EncData: - case EncFinal: - m_state = State.EncFinal; - return; - default: - throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); - } - aadOperator.reset(); - dataOperator.reset(); - } - - protected final void ensureSufficientOutputBuffer(byte[] output, int outOff, int len) - { - if (outOff + len > output.length) - { - throw new OutputLengthException("output buffer too short"); - } - } - - protected final void ensureSufficientInputBuffer(byte[] input, int inOff, int len) - { - if (inOff + len > input.length) - { - throw new DataLengthException("input buffer too short"); - } - } - - protected final void ensureInitialized() - { - if (m_state == State.Uninitialized) - { - throw new IllegalStateException("Need to call init function before operation"); - } - } - - protected abstract void processFinalBlock(byte[] output, int outOff); - - protected abstract void processBufferAAD(byte[] input, int inOff); - - protected abstract void processFinalAAD(); - - protected abstract void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff); - - protected abstract void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff); -} diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java index 5be9764901..856eadc359 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconAEAD128.java @@ -144,10 +144,6 @@ protected void init(byte[] key, byte[] iv) K1 = Pack.littleEndianToLong(key, 8); N0 = Pack.littleEndianToLong(iv, 0); N1 = Pack.littleEndianToLong(iv, 8); - - m_state = forEncryption ? State.EncInit : State.DecInit; - - reset(true); } public String getAlgorithmVersion() diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index 4561bfcf68..b72a3efd3e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -1,7 +1,7 @@ package org.bouncycastle.crypto.engines; abstract class AsconBaseEngine - extends AEADBufferBaseEngine + extends AEADBaseEngine { protected int nr; protected long K0; @@ -98,9 +98,8 @@ protected void processBufferEncrypt(byte[] buffer, int bufOff, byte[] output, in protected void reset(boolean clearMac) { - bufferReset(); - ascon_aeadinit(); super.reset(clearMac); + ascon_aeadinit(); } public abstract String getAlgorithmVersion(); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java index 2c9c9f3791..f9f8a3995a 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java @@ -226,10 +226,6 @@ else if (KEY_SIZE == 20) { throw new IllegalStateException(); } - - m_state = forEncryption ? State.EncInit : State.DecInit; - - reset(true); } public String getAlgorithmVersion() diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index c6789acbad..a546d6246b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -10,7 +10,7 @@ * Specification: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/elephant-spec-final.pdf */ public class ElephantEngine - extends AEADBufferBaseEngine + extends AEADBaseEngine { public enum ElephantParameters { @@ -291,8 +291,6 @@ protected void init(byte[] k, byte[] iv) expanded_key = new byte[BlockSize]; System.arraycopy(k, 0, expanded_key, 0, KEY_SIZE); instance.permutation(expanded_key); - m_state = forEncryption ? State.EncInit : State.DecInit; - reset(false); } protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) @@ -479,12 +477,11 @@ protected void processFinalAAD() protected void reset(boolean clearMac) { + super.reset(clearMac); Arrays.fill(tag_buffer, (byte)0); Arrays.fill(previous_outputMessage, (byte)0); nb_its = 0; adOff = -1; - super.reset(clearMac); - bufferReset(); } protected void checkAAD() diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java index 6bb4e0aa0b..36f9eb4560 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java @@ -9,7 +9,7 @@ */ public class GiftCofbEngine - extends AEADBufferBaseEngine + extends AEADBaseEngine { private byte[] npub; private byte[] k; @@ -234,8 +234,6 @@ protected void init(byte[] key, byte[] iv) Y = new byte[BlockSize]; input = new byte[16]; offset = new byte[8]; - m_state = forEncryption ? State.EncInit : State.DecInit; - reset(false); } @Override @@ -302,7 +300,6 @@ protected void processBufferDecrypt(byte[] inputM, int inOff, byte[] output, int protected void reset(boolean clearMac) { - bufferReset(); super.reset(clearMac); /*nonce is 128-bit*/ System.arraycopy(npub, 0, input, 0, IV_SIZE); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java index d53b61caff..02b6bed9aa 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java @@ -6,7 +6,7 @@ * Grain-128 AEAD, based on the current round 3 submission, https://grain-128aead.github.io/ */ public class Grain128AEADEngine - extends AEADBufferBaseEngine + extends AEADBaseEngine { /** * Constants @@ -50,13 +50,12 @@ protected void init(byte[] key, byte[] iv) nfsr = new int[STATE_SIZE]; authAcc = new int[2]; authSr = new int[2]; - m_state = forEncryption ? State.EncInit : State.DecInit; + System.arraycopy(iv, 0, workingIV, 0, IV_SIZE); workingIV[12] = (byte)0xFF; workingIV[13] = (byte)0xFF; workingIV[14] = (byte)0xFF; workingIV[15] = (byte)0x7F; - reset(); } private void initGrain(int[] auth) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index e170f0ff96..3ec53ff003 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -12,7 +12,7 @@ *

*/ public class ISAPEngine - extends AEADBufferBaseEngine + extends AEADBaseEngine { public enum IsapType @@ -682,8 +682,6 @@ protected void init(byte[] key, byte[] iv) npub = iv; k = key; ISAPAEAD.init(); - m_state = forEncryption ? State.EncInit : State.DecInit; - reset(); } @@ -747,9 +745,7 @@ protected void processFinalBlock(byte[] output, int outOff) protected void reset(boolean clearMac) { - ensureInitialized(); - bufferReset(); - ISAPAEAD.reset(); super.reset(clearMac); + ISAPAEAD.reset(); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index b90ace4c42..9ac4b0869b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -12,7 +12,7 @@ */ public class PhotonBeetleEngine - extends AEADBufferBaseEngine + extends AEADBaseEngine { public enum PhotonBeetleParameters { @@ -72,6 +72,7 @@ public PhotonBeetleEngine(PhotonBeetleParameters pbp) STATE_INBYTES = (STATE_INBITS + 7) >>> 3; LAST_THREE_BITS_OFFSET = (STATE_INBITS - ((STATE_INBYTES - 1) << 3) - 3); algorithmName = "Photon-Beetle AEAD"; + state = new byte[STATE_INBYTES]; setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Counter, DataOperatorType.Counter); } @@ -81,9 +82,6 @@ protected void init(byte[] key, byte[] iv) { K = key; N = iv; - state = new byte[STATE_INBYTES]; - m_state = forEncryption ? State.EncInit : State.DecInit; - reset(false); } @@ -189,12 +187,10 @@ else if (input_empty) protected void reset(boolean clearMac) { - ensureInitialized(); - bufferReset(); + super.reset(clearMac); input_empty = true; System.arraycopy(K, 0, state, 0, K.length); System.arraycopy(N, 0, state, K.length, N.length); - super.reset(clearMac); } private static void photonPermutation(byte[] state) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index d2196675ff..3ada509a3d 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -11,7 +11,7 @@ */ public class RomulusEngine - extends AEADBufferBaseEngine + extends AEADBaseEngine { public enum RomulusParameters { @@ -860,8 +860,6 @@ protected void init(byte[] key, byte[] iv) { npub = iv; k = key; - m_state = forEncryption ? State.EncInit : State.DecInit; - reset(false); } protected void finishAAD(State nextState, boolean isDoFinal) @@ -913,8 +911,7 @@ protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int protected void reset(boolean clearMac) { - bufferReset(); - instance.reset(); super.reset(clearMac); + instance.reset(); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java index 889188d1c0..3084588da0 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java @@ -11,7 +11,7 @@ * Specification: https://csrc.nist.gov/CSRC/media/Projects/lightweight-cryptography/documents/finalist-round/updated-spec-doc/sparkle-spec-final.pdf */ public class SparkleEngine - extends AEADBufferBaseEngine + extends AEADBaseEngine { public enum SparkleParameters { @@ -111,9 +111,6 @@ public SparkleEngine(SparkleParameters sparkleParameters) npub = new int[RATE_WORDS]; AADBufferSize = BlockSize = IV_SIZE; setInnerMembers(ProcessingBufferType.Buffered, AADOperatorType.Default, DataOperatorType.Default); - - // Relied on by processBytes method for decryption -// assert RATE_BYTES >= TAG_BYTES; } protected void init(byte[] key, byte[] iv) @@ -121,9 +118,6 @@ protected void init(byte[] key, byte[] iv) { Pack.littleEndianToInt(key, 0, k); Pack.littleEndianToInt(iv, 0, npub); - m_state = forEncryption ? State.EncInit : State.DecInit; - - reset(); } protected void finishAAD(State nextState, boolean isDoFinal) @@ -310,7 +304,6 @@ protected void processFinalAAD() protected void reset(boolean clearMac) { - bufferReset(); encrypted = false; // The Initialize function loads nonce and key into the state and executes the // SPARKLE permutation with the big number of steps. diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index a03755bbbc..024a7754af 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -14,7 +14,7 @@ */ public class XoodyakEngine - extends AEADBufferBaseEngine + extends AEADBaseEngine { private byte[] state; private int phase; @@ -34,11 +34,10 @@ public class XoodyakEngine public XoodyakEngine() { algorithmName = "Xoodyak AEAD"; - KEY_SIZE = 16; - IV_SIZE = 16; - MAC_SIZE = 16; + KEY_SIZE = IV_SIZE = MAC_SIZE = 16; BlockSize = 24; AADBufferSize = 44; + state = new byte[48]; setInnerMembers(ProcessingBufferType.Immediate, AADOperatorType.Default, DataOperatorType.Counter); } @@ -48,9 +47,6 @@ protected void init(byte[] key, byte[] iv) { K = key; this.iv = iv; - state = new byte[48]; - m_state = forEncryption ? State.EncInit : State.DecInit; - reset(); } protected void processBufferAAD(byte[] input, int inOff) @@ -131,8 +127,6 @@ protected void processFinalBlock(byte[] output, int outOff) protected void reset(boolean clearMac) { - bufferReset(); - ensureInitialized(); super.reset(clearMac); Arrays.fill(state, (byte)0); encrypted = false; diff --git a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java index 33124af334..9a68806b78 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java @@ -116,7 +116,7 @@ private void testLongAEAD() grain.doFinal(rv, len); isTrue(Arrays.areEqual(rv, CT)); - + grain.init(true, params); grain.processBytes(PT, 0, 10, rv, 0); try { @@ -230,7 +230,15 @@ static void isEqualTo( public static void main(String[] args) { +// runTest(new AsconTest()); +// runTest(new ElephantTest()); +// runTest(new GiftCofbTest()); runTest(new Grain128AEADTest()); +// runTest(new ISAPTest()); +// runTest(new PhotonBeetleTest()); +// runTest(new RomulusTest()); +// runTest(new SparkleTest()); +// runTest(new XoodyakTest()); } } From c1447279c4c0d87c81002b4880eefe533decb041 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 14 Mar 2025 17:01:33 +1030 Subject: [PATCH 217/890] Minor changes around aead classes --- .../crypto/engines/AEADBaseEngine.java | 45 +++++++++---------- .../crypto/engines/AsconEngine.java | 1 - .../crypto/engines/Grain128AEADEngine.java | 23 +--------- .../crypto/engines/ISAPEngine.java | 1 - .../crypto/engines/PhotonBeetleEngine.java | 1 - .../crypto/engines/XoodyakEngine.java | 2 +- 6 files changed, 25 insertions(+), 48 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java index 1598e97fbb..b1818ae9d8 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java @@ -161,8 +161,6 @@ else if (params instanceof ParametersWithIV) } } - - protected void reset(boolean clearMac) { ensureInitialized(); @@ -572,19 +570,6 @@ public void reset() } } - protected static final class ErasableOutputStream - extends ByteArrayOutputStream - { - public ErasableOutputStream() - { - } - - public byte[] getBuf() - { - return buf; - } - } - @Override public void processAADByte(byte input) { @@ -761,6 +746,12 @@ public final int getBlockSize() } public int getUpdateOutputSize(int len) + { + int total = getTotalBytesForUpdate(len); + return total - total % BlockSize; + } + + protected int getTotalBytesForUpdate(int len) { int total = processor.getUpdateOutputSize(len); switch (m_state) @@ -778,7 +769,7 @@ public int getUpdateOutputSize(int len) default: break; } - return total - total % BlockSize; + return total; } public int getOutputSize(int len) @@ -843,13 +834,6 @@ protected boolean checkData(boolean isDoFinal) } } - protected abstract void finishAAD(State nextState, boolean isDoFinal); - - protected final void bufferReset() - { - - } - protected final void ensureSufficientOutputBuffer(byte[] output, int outOff, int len) { if (outOff + len > output.length) @@ -876,6 +860,8 @@ protected final void ensureInitialized() protected abstract void init(byte[] key, byte[] iv); + protected abstract void finishAAD(State nextState, boolean isDoFinal); + protected abstract void processFinalBlock(byte[] output, int outOff); protected abstract void processBufferAAD(byte[] input, int inOff); @@ -885,4 +871,17 @@ protected final void ensureInitialized() protected abstract void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff); protected abstract void processBufferDecrypt(byte[] input, int inOff, byte[] output, int outOff); + + protected static final class ErasableOutputStream + extends ByteArrayOutputStream + { + public ErasableOutputStream() + { + } + + public byte[] getBuf() + { + return buf; + } + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java index f9f8a3995a..9e9fab811d 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconEngine.java @@ -208,7 +208,6 @@ protected void finishData(State nextState) protected void init(byte[] key, byte[] iv) throws IllegalArgumentException { - N0 = Pack.bigEndianToLong(iv, 0); N1 = Pack.bigEndianToLong(iv, 8); if (KEY_SIZE == 16) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java index 02b6bed9aa..4039dc3ede 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java @@ -181,8 +181,7 @@ private void shift() protected void reset(boolean clearMac) { - this.aadOperator.reset(); - + super.reset(clearMac); Pack.littleEndianToInt(workingKey, 0, nfsr); Pack.littleEndianToInt(workingIV, 0, lfsr); // 320 clocks initialization phase. @@ -203,7 +202,6 @@ protected void reset(boolean clearMac) } initGrain(authAcc); initGrain(authSr); - super.reset(clearMac); } private void updateInternalState(int input_i_j) @@ -216,26 +214,9 @@ private void updateInternalState(int input_i_j) authSr[1] = (authSr[1] >>> 1) | (val << 31); } - public int getUpdateOutputSize(int len) { - int total = processor.getUpdateOutputSize(len); - switch (m_state) - { - case DecInit: - case DecAad: - case DecData: - case DecFinal: - total = Math.max(0, total + m_bufPos - MAC_SIZE); - break; - case EncData: - case EncFinal: - total = Math.max(0, total + m_bufPos); - break; - default: - break; - } - return total; + return getTotalBytesForUpdate(len); } @Override diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index 3ec53ff003..2bddc150da 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -684,7 +684,6 @@ protected void init(byte[] key, byte[] iv) ISAPAEAD.init(); } - protected void processBufferAAD(byte[] input, int inOff) { ISAPAEAD.absorbMacBlock(input, inOff); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 9ac4b0869b..014f4acb94 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -84,7 +84,6 @@ protected void init(byte[] key, byte[] iv) N = iv; } - protected void processBufferAAD(byte[] input, int inOff) { photonPermutation(state); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index 024a7754af..36c89d6d0b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -16,7 +16,7 @@ public class XoodyakEngine extends AEADBaseEngine { - private byte[] state; + private final byte[] state; private int phase; private int mode; private static final int f_bPrime_1 = 47; From 24cf9b794e6dbc0cd38ea48f0ce8944b9083ebdf Mon Sep 17 00:00:00 2001 From: gefeili Date: Sat, 15 Mar 2025 14:19:55 +1030 Subject: [PATCH 218/890] finishAAD. Set BufferBaseDigest not public --- .../crypto/digests/BufferBaseDigest.java | 2 +- .../crypto/engines/AEADBaseEngine.java | 73 +++++++++++++++++-- .../crypto/engines/ElephantEngine.java | 16 +--- .../crypto/engines/GiftCofbEngine.java | 18 +---- .../crypto/engines/Grain128AEADEngine.java | 73 +++++++------------ .../crypto/engines/ISAPEngine.java | 18 +---- .../crypto/engines/PhotonBeetleEngine.java | 19 +---- .../crypto/engines/RomulusEngine.java | 15 +--- .../crypto/engines/SparkleEngine.java | 16 +--- .../crypto/engines/XoodyakEngine.java | 18 +---- 10 files changed, 102 insertions(+), 166 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java index d038985c4f..55c27d83ce 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java @@ -5,7 +5,7 @@ import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.util.Arrays; -public abstract class BufferBaseDigest +abstract class BufferBaseDigest implements ExtendedDigest { protected enum ProcessingBufferType diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java index b1818ae9d8..b79cf198fb 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java @@ -96,12 +96,6 @@ public void reset() reset(true); } - public int processByte(byte in, byte[] out, int outOff) - throws DataLengthException - { - return processBytes(new byte[]{in}, 0, 1, out, outOff); - } - public void init(boolean forEncryption, CipherParameters params) { this.forEncryption = forEncryption; @@ -225,6 +219,7 @@ protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOpe aadOperator = new CounterAADOperator(); break; case Stream: + AADBufferSize = 0; aadOperator = new StreamAADOperator(); break; } @@ -619,6 +614,12 @@ private void processAadBytes(byte[] input, int inOff, int len) m_aadPos = len; } + public int processByte(byte in, byte[] out, int outOff) + throws DataLengthException + { + return processBytes(new byte[]{in}, 0, 1, out, outOff); + } + @Override public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) throws DataLengthException @@ -858,10 +859,68 @@ protected final void ensureInitialized() } } - protected abstract void init(byte[] key, byte[] iv); + protected void finishAAD1(State nextState) + { + switch (m_state) + { + case DecInit: + case DecAad: + case EncInit: + case EncAad: + { + processFinalAAD(); + break; + } + default: + break; + } + m_state = nextState; + } + + protected void finishAAD2(State nextState) + { + // State indicates whether we ever received AAD + switch (m_state) + { + case DecAad: + case EncAad: + { + processFinalAAD(); + break; + } + default: + break; + } + + m_aadPos = 0; + m_state = nextState; + } + + protected void finishAAD3(State nextState, boolean isDoFinal) + { + // State indicates whether we ever received AAD + switch (m_state) + { + case DecInit: + case DecAad: + if (!isDoFinal && dataOperator.getLen() <= MAC_SIZE) + { + return; + } + case EncInit: + case EncAad: + processFinalAAD(); + break; + } + + m_aadPos = 0; + m_state = nextState; + } protected abstract void finishAAD(State nextState, boolean isDoFinal); + protected abstract void init(byte[] key, byte[] iv); + protected abstract void processFinalBlock(byte[] output, int outOff); protected abstract void processBufferAAD(byte[] input, int inOff); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index a546d6246b..c85aaa9ac5 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -439,21 +439,7 @@ public int getOutputSize(int len) protected void finishAAD(State nextState, boolean isDoFinal) { - // State indicates whether we ever received AAD - switch (m_state) - { - case DecAad: - case EncAad: - { - processFinalAAD(); - break; - } - default: - break; - } - - m_aadPos = 0; - m_state = nextState; + finishAAD2(nextState); } @Override diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java index 36f9eb4560..a5ac0ae3db 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/GiftCofbEngine.java @@ -207,23 +207,7 @@ protected void processFinalAAD() @Override protected void finishAAD(State nextState, boolean isDoFinal) { - // State indicates whether we ever received AAD - switch (m_state) - { - case DecInit: - case DecAad: - if (!isDoFinal && dataOperator.getLen() <= MAC_SIZE) - { - return; - } - case EncInit: - case EncAad: - processFinalAAD(); - break; - } - - m_aadPos = 0; - m_state = nextState; + finishAAD3(nextState, isDoFinal); } @Override diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java index 4039dc3ede..04bf4e5491 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java @@ -1,5 +1,7 @@ package org.bouncycastle.crypto.engines; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; /** @@ -19,10 +21,10 @@ public class Grain128AEADEngine */ private byte[] workingKey; private byte[] workingIV; - private int[] lfsr; - private int[] nfsr; - private int[] authAcc; - private int[] authSr; + private final int[] lfsr; + private final int[] nfsr; + private final int[] authAcc; + private final int[] authSr; public Grain128AEADEngine() { @@ -30,13 +32,15 @@ public Grain128AEADEngine() KEY_SIZE = 16; IV_SIZE = 12; MAC_SIZE = 8; + lfsr = new int[STATE_SIZE]; + nfsr = new int[STATE_SIZE]; + authAcc = new int[2]; + authSr = new int[2]; setInnerMembers(ProcessingBufferType.Immediate, AADOperatorType.Stream, DataOperatorType.StreamCipher); } /** * Initialize a Grain-128AEAD cipher. - * - * @throws IllegalArgumentException If the params argument is inappropriate. */ protected void init(byte[] key, byte[] iv) throws IllegalArgumentException @@ -46,11 +50,6 @@ protected void init(byte[] key, byte[] iv) */ workingIV = new byte[16]; workingKey = key; - lfsr = new int[STATE_SIZE]; - nfsr = new int[STATE_SIZE]; - authAcc = new int[2]; - authSr = new int[2]; - System.arraycopy(iv, 0, workingIV, 0, IV_SIZE); workingIV[12] = (byte)0xFF; workingIV[13] = (byte)0xFF; @@ -158,25 +157,23 @@ private int getOutput() } /** - * Shift array 1 bit and add val to index.length - 1. + * Shift array 1 bit and add val to index - 1. * * @param array The array to shift. * @param val The value to shift in. - * @return The shifted array with val added to index.length - 1. */ - private int[] shift(int[] array, int val) + private void shift(int[] array, int val) { array[0] = (array[0] >>> 1) | (array[1] << 31); array[1] = (array[1] >>> 1) | (array[2] << 31); array[2] = (array[2] >>> 1) | (array[3] << 31); array[3] = (array[3] >>> 1) | (val << 31); - return array; } private void shift() { - nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0]) & 1); - lfsr = shift(lfsr, (getOutputLFSR()) & 1); + shift(nfsr, (getOutputNFSR() ^ lfsr[0]) & 1); + shift(lfsr, (getOutputLFSR()) & 1); } protected void reset(boolean clearMac) @@ -184,34 +181,37 @@ protected void reset(boolean clearMac) super.reset(clearMac); Pack.littleEndianToInt(workingKey, 0, nfsr); Pack.littleEndianToInt(workingIV, 0, lfsr); + Arrays.clear(authAcc); + Arrays.clear(authSr); + int output; // 320 clocks initialization phase. for (int i = 0; i < 320; ++i) { - int output = getOutput(); - nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0] ^ output) & 1); - lfsr = shift(lfsr, (getOutputLFSR() ^ output) & 1); + output = getOutput(); + shift(nfsr, (getOutputNFSR() ^ lfsr[0] ^ output) & 1); + shift(lfsr, (getOutputLFSR() ^ output) & 1); } for (int quotient = 0; quotient < 8; ++quotient) { for (int remainder = 0; remainder < 8; ++remainder) { - int output = getOutput(); - nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0] ^ output ^ ((workingKey[quotient]) >> remainder)) & 1); - lfsr = shift(lfsr, (getOutputLFSR() ^ output ^ ((workingKey[quotient + 8]) >> remainder)) & 1); + output = getOutput(); + shift(nfsr, (getOutputNFSR() ^ lfsr[0] ^ output ^ ((workingKey[quotient]) >> remainder)) & 1); + shift(lfsr, (getOutputLFSR() ^ output ^ ((workingKey[quotient + 8]) >> remainder)) & 1); } } initGrain(authAcc); initGrain(authSr); } - private void updateInternalState(int input_i_j) + private void updateInternalState(int mask) { - int mask = -input_i_j; + mask = -mask; authAcc[0] ^= authSr[0] & mask; authAcc[1] ^= authSr[1] & mask; - int val = getByteKeyStream(); + mask = getByteKeyStream(); authSr[0] = (authSr[0] >>> 1) | (authSr[1] << 31); - authSr[1] = (authSr[1] >>> 1) | (val << 31); + authSr[1] = (authSr[1] >>> 1) | (mask << 31); } public int getUpdateOutputSize(int len) @@ -222,23 +222,7 @@ public int getUpdateOutputSize(int len) @Override protected void finishAAD(State nextState, boolean isDoFinal) { - // State indicates whether we ever received AAD - switch (m_state) - { - case DecInit: - case DecAad: - case EncInit: - case EncAad: - { - processFinalAAD(); - break; - } - default: - break; - } - - m_aadPos = 0; - m_state = nextState; + finishAAD1(nextState); } @Override @@ -252,7 +236,6 @@ protected void processFinalBlock(byte[] output, int outOff) @Override protected void processBufferAAD(byte[] input, int inOff) { - } @Override diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java index 2bddc150da..5a404540d9 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ISAPEngine.java @@ -697,23 +697,7 @@ protected void processFinalAAD() @Override protected void finishAAD(State nextState, boolean isDoFinal) { - // State indicates whether we ever received AAD - switch (m_state) - { - case DecInit: - case DecAad: - if (!isDoFinal && dataOperator.getLen() <= MAC_SIZE) - { - return; - } - case EncInit: - case EncAad: - processFinalAAD(); - break; - } - - m_aadPos = 0; - m_state = nextState; + finishAAD3(nextState, isDoFinal); } protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java index 014f4acb94..29a299da8a 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/PhotonBeetleEngine.java @@ -93,24 +93,7 @@ protected void processBufferAAD(byte[] input, int inOff) @Override protected void finishAAD(State nextState, boolean isDoFinal) { - // State indicates whether we ever received AAD - switch (m_state) - { - case DecInit: - case DecAad: - if (!isDoFinal && dataOperator.getLen() <= MAC_SIZE) - { - //m_state = State.DecData; - return; - } - case EncInit: - case EncAad: - processFinalAAD(); - break; - } - - m_aadPos = 0; - m_state = nextState; + finishAAD3(nextState, isDoFinal); } protected void processFinalAAD() diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index 3ada509a3d..f0b2ba1484 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -865,20 +865,7 @@ protected void init(byte[] key, byte[] iv) protected void finishAAD(State nextState, boolean isDoFinal) { // State indicates whether we ever received AAD - switch (m_state) - { - case DecInit: - case DecAad: - case EncInit: - case EncAad: - { - processFinalAAD(); - break; - } - default: - break; - } - m_state = nextState; + finishAAD1(nextState); } protected void processBufferAAD(byte[] input, int inOff) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java index 3084588da0..6e71b16b7b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/SparkleEngine.java @@ -122,21 +122,7 @@ protected void init(byte[] key, byte[] iv) protected void finishAAD(State nextState, boolean isDoFinal) { - // State indicates whether we ever received AAD - switch (m_state) - { - case DecAad: - case EncAad: - { - processFinalAAD(); - break; - } - default: - break; - } - - m_aadPos = 0; - m_state = nextState; + finishAAD2(nextState); } @Override diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java index 36c89d6d0b..b22e39eeea 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/XoodyakEngine.java @@ -63,23 +63,7 @@ protected void processFinalAAD() @Override protected void finishAAD(State nextState, boolean isDoFinal) { - // State indicates whether we ever received AAD - switch (m_state) - { - case DecInit: - case DecAad: - if (!isDoFinal && dataOperator.getLen() <= MAC_SIZE) - { - return; - } - case EncInit: - case EncAad: - processFinalAAD(); - break; - } - - m_aadPos = 0; - m_state = nextState; + finishAAD3(nextState, isDoFinal); } protected void processBufferEncrypt(byte[] input, int inOff, byte[] output, int outOff) From a1e0799f317a0e634c625cc1621c7c54100e336b Mon Sep 17 00:00:00 2001 From: gefeili Date: Sun, 16 Mar 2025 09:38:03 +1030 Subject: [PATCH 219/890] Refactor on Grain128AEADEngine --- .../crypto/engines/AEADBaseEngine.java | 3 +++ .../crypto/test/Grain128AEADTest.java | 16 ++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java index b79cf198fb..c251408f48 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java @@ -859,6 +859,7 @@ protected final void ensureInitialized() } } + // Used for Grain128 AEAD and Romulus Engine protected void finishAAD1(State nextState) { switch (m_state) @@ -877,6 +878,7 @@ protected void finishAAD1(State nextState) m_state = nextState; } + // Use for Elephant and Sparkle protected void finishAAD2(State nextState) { // State indicates whether we ever received AAD @@ -896,6 +898,7 @@ protected void finishAAD2(State nextState) m_state = nextState; } + // Used for Gift-Cofb, ISAP, PhotonBeetle and Xoodyak protected void finishAAD3(State nextState, boolean isDoFinal) { // State indicates whether we ever received AAD diff --git a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java index 9a68806b78..ed44d534ce 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java @@ -230,15 +230,15 @@ static void isEqualTo( public static void main(String[] args) { -// runTest(new AsconTest()); -// runTest(new ElephantTest()); -// runTest(new GiftCofbTest()); + runTest(new AsconTest()); + runTest(new ElephantTest()); + runTest(new GiftCofbTest()); runTest(new Grain128AEADTest()); -// runTest(new ISAPTest()); -// runTest(new PhotonBeetleTest()); -// runTest(new RomulusTest()); -// runTest(new SparkleTest()); -// runTest(new XoodyakTest()); + runTest(new ISAPTest()); + runTest(new PhotonBeetleTest()); + runTest(new RomulusTest()); + runTest(new SparkleTest()); + runTest(new XoodyakTest()); } } From e150b26c86a9920fd44e147b836072f6af0abfd7 Mon Sep 17 00:00:00 2001 From: gefeili Date: Sun, 16 Mar 2025 09:38:16 +1030 Subject: [PATCH 220/890] Refactor on Grain128AEADEngine --- .../crypto/engines/Grain128AEADEngine.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java index 04bf4e5491..ff32602bfa 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java @@ -1,6 +1,5 @@ package org.bouncycastle.crypto.engines; -import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; @@ -63,8 +62,7 @@ private void initGrain(int[] auth) { for (int remainder = 0; remainder < 32; ++remainder) { - int output = getByteKeyStream(); - auth[quotient] |= output << remainder; + auth[quotient] |= getByteKeyStream() << remainder; } } } @@ -258,9 +256,9 @@ protected void processFinalAAD() ader = new byte[1 + aderlen]; ader[0] = (byte)(0x80 | aderlen); int tmp = len; - for (int i = 0; i < aderlen; ++i) + for (int i = 1; i < ader.length; ++i) { - ader[1 + i] = (byte)tmp; + ader[i] = (byte)tmp; tmp >>>= 8; } } @@ -277,8 +275,7 @@ private void absorbAadData(byte[] ader, int len) for (int j = 0; j < 8; ++j) { shift(); - int ader_i_j = (ader_i >> j) & 1; - updateInternalState(ader_i_j); + updateInternalState((ader_i >> j) & 1); } } } From 14d6f2463cffa13192a810bd50c5501bd28c1835 Mon Sep 17 00:00:00 2001 From: gefeili Date: Sun, 16 Mar 2025 09:39:00 +1030 Subject: [PATCH 221/890] Remove Grain128AEADTest.main --- .../crypto/test/Grain128AEADTest.java | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java index ed44d534ce..7f1f291db9 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java @@ -228,17 +228,17 @@ static void isEqualTo( } } - public static void main(String[] args) - { - runTest(new AsconTest()); - runTest(new ElephantTest()); - runTest(new GiftCofbTest()); - runTest(new Grain128AEADTest()); - runTest(new ISAPTest()); - runTest(new PhotonBeetleTest()); - runTest(new RomulusTest()); - runTest(new SparkleTest()); - runTest(new XoodyakTest()); - } +// public static void main(String[] args) +// { +// runTest(new AsconTest()); +// runTest(new ElephantTest()); +// runTest(new GiftCofbTest()); +// runTest(new Grain128AEADTest()); +// runTest(new ISAPTest()); +// runTest(new PhotonBeetleTest()); +// runTest(new RomulusTest()); +// runTest(new SparkleTest()); +// runTest(new XoodyakTest()); +// } } From b53457d967a452b42e17f19afda0fdbc8c98ee8c Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 16 Mar 2025 15:15:16 +1100 Subject: [PATCH 222/890] removed extra encoding of public key --- .../org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java index 9b3aaff843..eb43ac859c 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java @@ -152,7 +152,7 @@ else if (privateKey instanceof SLHDSAPrivateKeyParameters) AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.slhdsaOidLookup(params.getParameters())); - return new PrivateKeyInfo(algorithmIdentifier, params.getEncoded(), attributes, params.getPublicKey()); + return new PrivateKeyInfo(algorithmIdentifier, params.getEncoded(), attributes); } else if (privateKey instanceof PicnicPrivateKeyParameters) { From 4361cd76b1599e53fa885d24102c8aabba0d33d4 Mon Sep 17 00:00:00 2001 From: gefeili Date: Sun, 16 Mar 2025 17:30:12 +1030 Subject: [PATCH 223/890] Refactor ProcessByte --- .../crypto/engines/AEADBaseEngine.java | 117 ++++++++++++++++-- .../bouncycastle/crypto/test/CipherTest.java | 18 ++- .../crypto/test/Grain128AEADTest.java | 2 +- 3 files changed, 123 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java index c251408f48..09b8382124 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java @@ -91,11 +91,7 @@ public byte[] getMac() return mac; } - public void reset() - { - reset(true); - } - + @Override public void init(boolean forEncryption, CipherParameters params) { this.forEncryption = forEncryption; @@ -155,6 +151,12 @@ else if (params instanceof ParametersWithIV) } } + @Override + public void reset() + { + reset(true); + } + protected void reset(boolean clearMac) { ensureInitialized(); @@ -246,10 +248,12 @@ protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOpe } } - protected interface AADProcessingBuffer + private interface AADProcessingBuffer { void processAADByte(byte input); + int processByte(byte input, byte[] output, int outOff); + int getUpdateOutputSize(int len); boolean isLengthWithinAvailableSpace(int len, int available); @@ -270,6 +274,15 @@ public void processAADByte(byte input) m_aad[m_aadPos++] = input; } + @Override + public int processByte(byte input, byte[] output, int outOff) + { + checkData(false); + int rlt = processEncDecByte(output, outOff); + m_buf[m_bufPos++] = input; + return rlt; + } + @Override public boolean isLengthWithinAvailableSpace(int len, int available) { @@ -303,6 +316,14 @@ public void processAADByte(byte input) } } + @Override + public int processByte(byte input, byte[] output, int outOff) + { + checkData(false); + m_buf[m_bufPos++] = input; + return processEncDecByte(output, outOff); + } + @Override public int getUpdateOutputSize(int len) { @@ -333,7 +354,7 @@ protected interface AADOperator int getLen(); } - protected class DefaultAADOperator + private class DefaultAADOperator implements AADOperator { @Override @@ -359,7 +380,7 @@ public int getLen() } } - protected class CounterAADOperator + private class CounterAADOperator implements AADOperator { private int aadLen; @@ -426,6 +447,8 @@ public int getLen() protected interface DataOperator { + int processByte(byte input, byte[] output, int outOff); + int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff); int getLen(); @@ -433,9 +456,14 @@ protected interface DataOperator void reset(); } - protected class DefaultDataOperator + private class DefaultDataOperator implements DataOperator { + public int processByte(byte input, byte[] output, int outOff) + { + return processor.processByte(input, output, outOff); + } + public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { return processEncDecBytes(input, inOff, len, output, outOff); @@ -453,11 +481,17 @@ public void reset() } } - protected class CounterDataOperator + private class CounterDataOperator implements DataOperator { private int messegeLen; + public int processByte(byte input, byte[] output, int outOff) + { + messegeLen++; + return processor.processByte(input, output, outOff); + } + public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { messegeLen += len; @@ -482,6 +516,14 @@ protected class StreamDataOperator { private final ErasableOutputStream stream = new ErasableOutputStream(); + public int processByte(byte input, byte[] output, int outOff) + { + ensureInitialized(); + stream.write(input); + m_bufPos = stream.size(); + return 0; + } + @Override public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { @@ -509,11 +551,39 @@ public void reset() } } - protected class StreamCipherOperator + private class StreamCipherOperator implements DataOperator { + //TODO: shift index instead of arraycopy private int len; + public int processByte(byte input, byte[] output, int outOff) + { + boolean forEncryption = checkData(false); + if (forEncryption) + { + this.len = 1; + processBufferEncrypt(new byte[]{input}, 0, output, outOff); + return 1; + } + else + { + if (m_bufPos == MAC_SIZE) + { + this.len = 1; + processBufferDecrypt(m_buf, 0, output, outOff); + System.arraycopy(m_buf, 1, m_buf, 0, m_bufPos - 1); + m_buf[m_bufPos - 1] = input; + return 1; + } + else + { + m_buf[m_bufPos++] = input; + return 0; + } + } + } + @Override public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { @@ -614,10 +684,33 @@ private void processAadBytes(byte[] input, int inOff, int len) m_aadPos = len; } + @Override public int processByte(byte in, byte[] out, int outOff) throws DataLengthException { - return processBytes(new byte[]{in}, 0, 1, out, outOff); + return dataOperator.processByte(in, out, outOff); + } + + protected int processEncDecByte(byte[] output, int outOff) + { + int rlt = 0; + int available = (forEncryption ? BlockSize : m_bufferSizeDecrypt) - m_bufPos; + if (available == 0) + { + ensureSufficientOutputBuffer(output, outOff, BlockSize); + if (forEncryption) + { + processBufferEncrypt(m_buf, 0, output, outOff); + } + else + { + processBufferDecrypt(m_buf, 0, output, outOff); + System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos - BlockSize); + } + m_bufPos -= BlockSize; + rlt = BlockSize; + } + return rlt; } @Override diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java index 26bbd069d7..e88201d44e 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java @@ -331,7 +331,11 @@ static void checkAEADParemeter(SimpleTest test, int keySize, int ivSize, final i { cipher.processAADByte(aad[i]); } - int len = cipher.processBytes(plaintext, 0, plaintext.length, ciphertext1, 0); + int len = 0; + for (int i = 0; i < plaintext.length; ++i) + { + len += cipher.processByte(plaintext[i], ciphertext1, len); + } len += cipher.doFinal(ciphertext1, len); int aadSplit = random.nextInt(aad.length) + 1; cipher.init(true, new AEADParameters(new KeyParameter(key), macSize * 8, iv, Arrays.copyOf(aad, aadSplit))); @@ -345,6 +349,18 @@ static void checkAEADParemeter(SimpleTest test, int keySize, int ivSize, final i len = cipher.processBytes(plaintext, 0, plaintext.length, ciphertext3, 0); len += cipher.doFinal(ciphertext3, len); test.isTrue("cipher text check", Arrays.areEqual(ciphertext1, ciphertext2)); + cipher.init(false, new ParametersWithIV(new KeyParameter(key), iv)); + for (int i = 0; i < aad.length; ++i) + { + cipher.processAADByte(aad[i]); + } + len = 0; + byte[] plaintext1 = new byte[plaintext.length]; + for (int i = 0; i < ciphertext1.length; ++i) + { + len += cipher.processByte(ciphertext1[i], plaintext1, len); + } + len += cipher.doFinal(plaintext1, len); test.testException("Invalid value for MAC size: ", "IllegalArgumentException", new TestExceptionOperation() { diff --git a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java index 7f1f291db9..bc753137bc 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java @@ -37,7 +37,7 @@ public AEADCipher createInstance() CipherTest.checkAEADCipherMultipleBlocks(this, 1024, 7, 100, 128, 12, new Grain128AEADEngine()); - CipherTest.checkAEADParemeter(this, 16, 12, 8, 16, new Grain128AEADEngine()); + CipherTest.checkAEADParemeter(this, 16, 12, 8, 20, new Grain128AEADEngine()); testSplitUpdate(); testExceptions(); testLongAEAD(); From b8e6d33cb941bbc35db1ac59749625bfdd3622fc Mon Sep 17 00:00:00 2001 From: gefeili Date: Sun, 16 Mar 2025 19:18:20 +1030 Subject: [PATCH 224/890] refactor for ElephantEngine.rol8 and rotl --- .../org/bouncycastle/crypto/engines/ElephantEngine.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index c85aaa9ac5..aa2488bdd9 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -258,9 +258,11 @@ private void KeccakP200Round(byte[] state, int indexRound) state[0] ^= KeccakRoundConstants[indexRound];//index(0,0) } + //TODO: search for " >>> (8 - " merge with with CamelliaLightEngine.lRot8, + // code in OCBBlockCipher.init, DualECSP800DRBG.pad8, private byte ROL8(byte a, int offset) { - return (byte)(((a & 0xff) << offset) | ((a & 0xff) >> (8 - offset))); + return (byte)((a << offset) | ((a & 0xff) >>> (8 - offset))); } private int index(int x, int y) @@ -271,7 +273,7 @@ private int index(int x, int y) private byte rotl(byte b) { - return (byte)(((b & 0xFF) << 1) | ((b & 0xFF) >>> 7)); + return (byte)((b << 1) | ((b & 0xFF) >>> 7)); } // State should be BLOCK_SIZE bytes long From 7f0c8f6d04167951147c550957576c98ae9080df Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 17 Mar 2025 17:24:21 +1030 Subject: [PATCH 225/890] TODO: fix the bugs for packing sk of Snova. --- .../pqc/crypto/snova/GF16Utils.java | 40 +++++ .../pqc/crypto/snova/MapGroup1.java | 42 ++++- .../bouncycastle/pqc/crypto/snova/SKGF16.java | 36 ---- .../pqc/crypto/snova/SnovaEngine.java | 21 ++- .../pqc/crypto/snova/SnovaKeyElements.java | 45 ++++- .../crypto/snova/SnovaKeyPairGenerator.java | 157 ++---------------- .../pqc/crypto/snova/SnovaParameters.java | 15 +- 7 files changed, 163 insertions(+), 193 deletions(-) delete mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/SKGF16.java diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java index 75414b2152..0469a25989 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java @@ -81,6 +81,46 @@ public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mde } } + /** + * Convert two GF16 values to one byte. + * + * @param m the input array of 4-bit values (stored as bytes, only lower 4 bits used) + * @param menc the output byte array that will hold the encoded bytes + * @param mlen the number of nibbles in the input array + */ + public static void encode(byte[] m, byte[] menc, int outOff, int mlen) + { + int i, srcIndex = 0; + // Process pairs of 4-bit values + for (i = 0; i < mlen / 2; i++) + { + int lowerNibble = m[srcIndex] & 0x0F; + int upperNibble = (m[srcIndex + 1] & 0x0F) << 4; + menc[outOff++] = (byte)(lowerNibble | upperNibble); + srcIndex += 2; + } + // If there is an extra nibble (odd number of nibbles), store it directly in lower 4 bits. + if ((mlen & 1) == 1) + { + menc[outOff] = (byte)(m[srcIndex] & 0x0F); + } + } + + public static void encodeMergeInHalf(byte[] m, int mlen, byte[] menc) + { + int i, half = (mlen + 1) >>> 1; + // Process pairs of 4-bit values + for (i = 0; i < mlen / 2; i++, half++) + { + menc[i] = (byte)(m[i] | (m[half] << 4)); + } + // If there is an extra nibble (odd number of nibbles), store it directly in lower 4 bits. + if ((mlen & 1) == 1) + { + menc[i] = (byte)m[i]; + } + } + /** * Decodes a nibble-packed byte array into an output array. * diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java index 552cdfeed7..19b567b650 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -38,7 +38,19 @@ public int decode(byte[] input, int len) return inOff; } - private int decodeP(byte[] input, int inOff, byte[][][][] p, int len) +// public int encode(byte[] output, int len) +// { +// int outOff = encodeP(p11, output, 0, len); +// outOff += encodeP(p12, output, outOff, len - outOff); +// outOff += encodeP(p21, output, outOff, len - outOff); +// outOff += encodeAlpha(aAlpha, output, outOff, len - outOff); +// outOff += encodeAlpha(bAlpha, output, outOff, len - outOff); +// outOff += encodeAlpha(qAlpha1, output, outOff, len - outOff); +// outOff += encodeAlpha(qAlpha2, output, outOff, len - outOff); +// return outOff; +// } + + static int decodeP(byte[] input, int inOff, byte[][][][] p, int len) { int rlt = 0; for (int i = 0; i < p.length; ++i) @@ -48,7 +60,7 @@ private int decodeP(byte[] input, int inOff, byte[][][][] p, int len) return rlt; } - private int decodeAlpha(byte[] input, int inOff, byte[][][] alpha, int len) + private static int decodeAlpha(byte[] input, int inOff, byte[][][] alpha, int len) { int rlt = 0; for (int i = 0; i < alpha.length; ++i) @@ -64,4 +76,30 @@ private int decodeAlpha(byte[] input, int inOff, byte[][][] alpha, int len) return rlt; } + static int encodeP(byte[][][][] p, byte[] output, int outOff, int len) + { + int rlt = 0; + for (int i = 0; i < p.length; ++i) + { + rlt += encodeAlpha(p[i], output, outOff + rlt, len); + } + return rlt; + } + + static int encodeAlpha(byte[][][] alpha, byte[] output, int outOff, int len) + { + int rlt = 0; + for (int i = 0; i < alpha.length; ++i) + { + for (int j = 0; j < alpha[i].length; ++j) + { + int tmp = Math.min(alpha[i][j].length, len << 1); + GF16Utils.encode(alpha[i][j], output, outOff + rlt, tmp); + rlt += (tmp + 1) >> 1; + len -= (tmp + 1) >> 1; + } + } + return rlt; + } + } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SKGF16.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SKGF16.java deleted file mode 100644 index fe25ffefb6..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SKGF16.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.bouncycastle.pqc.crypto.snova; - -class SKGF16 -{ - public final GF16Matrix[][] Aalpha; // [m][alpha] - public final GF16Matrix[][] Balpha; // [m][alpha] - public final GF16Matrix[][] Qalpha1; // [m][alpha] - public final GF16Matrix[][] Qalpha2; // [m][alpha] - public final GF16Matrix[][] T12; // [v][o] - public final GF16Matrix[][][] F11; // [m][v][v] - public final GF16Matrix[][][] F12; // [m][v][o] - public final GF16Matrix[][][] F21; // [m][o][v] - public final byte[] publicKeySeed; - public final byte[] privateKeySeed; - - public SKGF16(SnovaParameters params) - { - int m = params.getM(); - int v = params.getV(); - int o = params.getO(); - int alpha = params.getAlpha(); - int rank = params.getL(); - - Aalpha = GF16Utils.create2DArray(m, alpha, rank); - Balpha = GF16Utils.create2DArray(m, alpha, rank); - Qalpha1 = GF16Utils.create2DArray(m, alpha, rank); - Qalpha2 = GF16Utils.create2DArray(m, alpha, rank); - T12 = GF16Utils.create2DArray(v, o, rank); - F11 = GF16Utils.create3DArray(m, v, v, rank); - F12 = GF16Utils.create3DArray(m, v, o, rank); - F21 = GF16Utils.create3DArray(m, o, v, rank); - - publicKeySeed = new byte[SnovaKeyPairGenerator.publicSeedLength]; - privateKeySeed = new byte[SnovaKeyPairGenerator.privateSeedLength]; - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index e68138385e..f68d399630 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -356,8 +356,8 @@ public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) { for (int index = 0; index < v; index++) { - GF16Utils.gf16mMul(temp, map1.p11[i][j][index], T12[index][k], l); - GF16Utils.gf16mAdd(map2.f12[i][j][k], map2.f12[i][j][k], temp, l); + GF16Utils.gf16mMul(map1.p11[i][j][index], T12[index][k], temp, l); + GF16Utils.gf16mAdd(map2.f12[i][j][k], temp, map2.f12[i][j][k], l); } } } @@ -372,8 +372,8 @@ public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) { for (int index = 0; index < v; index++) { - GF16Utils.gf16mMul(temp, T12[index][j], map1.p11[i][index][k], l); - GF16Utils.gf16mAdd(map2.f21[i][j][k], map2.f21[i][j][k], temp, l); + GF16Utils.gf16mMul(T12[index][j], map1.p11[i][index][k], temp, l); + GF16Utils.gf16mAdd(map2.f21[i][j][k], temp, map2.f21[i][j][k], l); } } } @@ -402,7 +402,7 @@ private static void copy4DMatrix(byte[][][][] src, byte[][][][] dest, } } - public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] F12, SnovaParameters params) + public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] F12) { int m = params.getM(); int o = params.getO(); @@ -428,24 +428,23 @@ public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] for (int index = 0; index < v; index++) { // temp1 = T12[index][j] * F12[i][index][k] - GF16Utils.gf16mMul(temp1, T12[index][j], F12[i][index][k], l); + GF16Utils.gf16mMul(T12[index][j], F12[i][index][k], temp1, l); // temp2 = P21[i][j][index] * T12[index][k] - GF16Utils.gf16mMul(temp2, P21[i][j][index], T12[index][k], l); + GF16Utils.gf16mMul(P21[i][j][index], T12[index][k], temp2, l); // temp1 += temp2 - GF16Utils.gf16mAdd(temp1, temp1, temp2, l); + GF16Utils.gf16mAdd(temp1, temp2, temp1, l); // P22[i][j][k] += temp1 - GF16Utils.gf16mAdd(P22[i][j][k], P22[i][j][k], temp1, l); + GF16Utils.gf16mAdd(P22[i][j][k], temp1, P22[i][j][k], l); } } } } // Convert GF16 elements to packed bytes - //TODO - //GF16Utils.decode(P22, outP22, m * o * o *lsq); + MapGroup1.encodeP(P22, outP22, 0, m * o * o *lsq); } finally { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java index 1c1d7ea3ea..026677ab92 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java @@ -6,12 +6,55 @@ class SnovaKeyElements public final byte[][][] T12; // [v][o] public final MapGroup2 map2; public final PublicKey publicKey; + private int length; public SnovaKeyElements(SnovaParameters params) { + int o = params.getO(); + int l = params.getL(); + int v = params.getV(); + int lsq = l * l; map1 = new MapGroup1(params); - T12 = new byte[params.getV()][params.getO()][16]; + T12 = new byte[v][o][lsq]; map2 = new MapGroup2(params); publicKey = new PublicKey(params); + length = o * params.getAlpha() * lsq * 4 + v * o * lsq + (o * v * v + o * v * o + o * o * v) * lsq; + } + + public void encodeMergerInHalf(byte[] output) + { + byte[] input = new byte[length]; + int inOff = 0; + inOff = copy3d(map1.aAlpha, input, inOff); + inOff = copy3d(map1.bAlpha, input, inOff); + inOff = copy3d(map1.qAlpha1, input, inOff); + inOff = copy3d(map1.qAlpha2, input, inOff); + inOff = copy3d(T12, input, inOff); + inOff = copy4d(map2.f11, input, inOff); + inOff = copy4d(map2.f12, input, inOff); + inOff = copy4d(map2.f21, input, inOff); + GF16Utils.encodeMergeInHalf(input, length, output); + } + + public int copy3d(byte[][][] alpha, byte[] output, int outOff) + { + for (int i = 0; i < alpha.length; ++i) + { + for (int j = 0; j < alpha[i].length; ++j) + { + System.arraycopy(alpha[i][j], 0, output, outOff, alpha[i][j].length); + outOff += alpha[i][j].length; + } + } + return outOff; + } + + public int copy4d(byte[][][][] alpha, byte[] output, int outOff) + { + for (int i = 0; i < alpha.length; ++i) + { + outOff = copy3d(alpha[i], output, outOff); + } + return outOff; } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index 8450c2b41d..b84a9f2f9a 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -1,6 +1,5 @@ package org.bouncycastle.pqc.crypto.snova; -import java.io.ByteArrayOutputStream; import java.security.SecureRandom; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; @@ -48,19 +47,29 @@ public AsymmetricCipherKeyPair generateKeyPair() byte[] seedPair = new byte[seedLength]; random.nextBytes(seedPair); - byte[] pk = new byte[publicSeedLength]; - byte[] sk = new byte[privateSeedLength]; + byte[] pk = new byte[params.getPublicKeyLength()]; + byte[] sk = new byte[params.getPrivateKeyLength()]; byte[] ptPublicKeySeed = Arrays.copyOfRange(seedPair, 0, publicSeedLength); byte[] ptPrivateKeySeed = Arrays.copyOfRange(seedPair, publicSeedLength, seedPair.length); +// System.arraycopy(ptPublicKeySeed, 0, sk, 0, ptPublicKeySeed.length); +// System.arraycopy(ptPrivateKeySeed, 0, sk, ptPublicKeySeed.length, ptPrivateKeySeed.length); + SnovaKeyElements keyElements = new SnovaKeyElements(params); + generateKeysCore(keyElements, ptPublicKeySeed, ptPrivateKeySeed); + + // Pack public key components + System.arraycopy(ptPublicKeySeed, 0, pk, 0, ptPublicKeySeed.length); + System.arraycopy(keyElements.publicKey.P22, 0, pk, ptPublicKeySeed.length, keyElements.publicKey.P22.length); + if (params.isSkIsSeed()) { - generateKeysSSK(pk, sk, ptPublicKeySeed, ptPrivateKeySeed); + sk = seedPair; } else { - generateKeysESK(pk, sk, ptPublicKeySeed, ptPrivateKeySeed); + keyElements.encodeMergerInHalf(sk); + System.arraycopy(seedPair, 0, sk, sk.length - seedLength, seedLength); } return new AsymmetricCipherKeyPair( @@ -69,96 +78,6 @@ public AsymmetricCipherKeyPair generateKeyPair() ); } - private void generateKeysSSK(byte[] pk, byte[] sk, byte[] ptPublicKeySeed, byte[] ptPrivateKeySeed) - { - // Implementation based on C's generate_keys_ssk - System.arraycopy(ptPublicKeySeed, 0, sk, 0, ptPublicKeySeed.length); - System.arraycopy(ptPrivateKeySeed, 0, sk, ptPublicKeySeed.length, ptPrivateKeySeed.length); - - // Actual key generation would go here using BC's SHAKE/AES implementations - // This would include the matrix operations from the C code - generatePublicKey(pk, ptPublicKeySeed, ptPrivateKeySeed); - } - - private void generateKeysESK(byte[] pk, byte[] esk, byte[] ptPublicKeySeed, byte[] ptPrivateKeySeed) - { - // Implementation based on C's generate_keys_esk - // Actual expanded key generation would go here - generatePublicKey(pk, ptPublicKeySeed, ptPrivateKeySeed); - packPrivateKey(esk, ptPublicKeySeed, ptPrivateKeySeed); - } - - private void packPrivateKey(byte[] esk, byte[] ptPublicKeySeed, byte[] ptPrivateKeySeed) - { - SnovaKeyElements keyElements = new SnovaKeyElements(params); - generateKeysCore(keyElements, ptPublicKeySeed, ptPrivateKeySeed); - - // Serialize all components - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - - // Serialize map components -// serializeMatrixGroup(bos, keyElements.map1.Aalpha); -// serializeMatrixGroup(bos, keyElements.map1.Balpha); -// serializeMatrixGroup(bos, keyElements.map1.Qalpha1); -// serializeMatrixGroup(bos, keyElements.map1.Qalpha2); - - // Serialize T12 -// for (GF16Matrix[] row : keyElements.T12) -// { -// for (GF16Matrix matrix : row) -// { -// serializeMatrix(bos, matrix); -// } -// } - - // Add public and private seeds - bos.write(ptPublicKeySeed, 0, ptPublicKeySeed.length); - bos.write(ptPrivateKeySeed, 0, ptPrivateKeySeed.length); - - System.arraycopy(bos.toByteArray(), 0, esk, 0, esk.length); - } - - private void serializeMatrixGroup(ByteArrayOutputStream bos, GF16Matrix[][][] group) - { - for (GF16Matrix[][] dim1 : group) - { - for (GF16Matrix[] dim2 : dim1) - { - for (GF16Matrix matrix : dim2) - { - serializeMatrix(bos, matrix); - } - } - } - } - - private void serializeMatrix(ByteArrayOutputStream bos, GF16Matrix matrix) - { -// byte[] temp = new byte[(matrix.size * matrix.size + 1) / 2]; -// byte[] gf16s = new byte[matrix.size * matrix.size]; -// -// int idx = 0; -// for (int i = 0; i < matrix.size; i++) { -// for (int j = 0; j < matrix.size; j++) { -// gf16s[idx++] = matrix.get(i, j); -// } -// } -// -// GF16Utils.convertGF16sToBytes(temp, gf16s, gf16s.length); -// bos.write(temp, 0, temp.length); - } - - private void generatePublicKey(byte[] pk, byte[] ptPublicKeySeed, byte[] ptPrivateKeySeed) - { - - // Generate key elements - SnovaKeyElements keyElements = new SnovaKeyElements(params); - generateKeysCore(keyElements, ptPublicKeySeed, ptPrivateKeySeed); - - // Pack public key components - //packPublicKey(pk, keyElements); - } - private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[] skSeed) { // Generate T12 matrix @@ -171,7 +90,7 @@ private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[ engine.genF(keyElements.map2, keyElements.map1, keyElements.T12); // Generate P22 matrix -// genP22(keyElements.pk.P22, keyElements.T12, keyElements.map1.P21, keyElements.map2.F12); + engine.genP22(keyElements.publicKey.P22, keyElements.T12, keyElements.map1.p21, keyElements.map2.f12); } private void genSeedsAndT12(byte[][][] T12, byte[] skSeed) @@ -294,50 +213,4 @@ private void genABQP(MapGroup1 map1, byte[] pkSeed) } } } - - -// private void genF(MapGroup2 map2, MapGroup1 map1, GF16Matrix[][] T12) -// { -// // Matrix operations from C code's gen_F_ref -// // Clone initial matrices -// System.arraycopy(map1.P11, 0, map2.F11, 0, map1.P11.length); -// System.arraycopy(map1.P12, 0, map2.F12, 0, map1.P12.length); -// System.arraycopy(map1.P21, 0, map2.F21, 0, map1.P21.length); -// -// // Perform matrix multiplications and additions -// GF16Matrix temp = new GF16Matrix(params.getL()); -// for (int i = 0; i < params.getM(); i++) { -// for (int j = 0; j < params.getV(); j++) { -// for (int k = 0; k < params.getO(); k++) { -// for (int idx = 0; idx < params.getV(); idx++) { -// GF16Matrix.mul(map1.P11[i][j][idx], T12[idx][k], temp); -// GF16Matrix.add(map2.F12[i][j][k], temp, map2.F12[i][j][k]); -// } -// } -// } -// } -// } - - private void genP22(byte[] outP22, GF16Matrix[][] T12, GF16Matrix[][][] P21, GF16Matrix[][][] F12) - { -// GF16Matrix[][][] P22 = new GF16Matrix[params.getM()][params.getO()][params.getO()]; -// GF16Matrix temp1 = new GF16Matrix(params.getL()); -// GF16Matrix temp2 = new GF16Matrix(params.getL()); -// -// for (int i = 0; i < params.getM(); i++) { -// for (int j = 0; j < params.getO(); j++) { -// for (int k = 0; k < params.getO(); k++) { -// for (int idx = 0; idx < params.getV(); idx++) { -// GF16Matrix.mul(T12[idx][j], F12[i][idx][k], temp1); -// GF16Matrix.mul(P21[i][j][idx], T12[idx][k], temp2); -// GF16Matrix.add(temp1, temp2, temp1); -// GF16Matrix.add(P22[i][j][k], temp1, P22[i][j][k]); -// } -// } -// } -// } -// -// // Convert GF16 matrices to bytes -// GF16Utils.convertGF16sToBytes(P22, outP22); - } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java index 90436bab3c..b6b0cc0027 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java @@ -111,6 +111,7 @@ public class SnovaParameters private final int v; private final int o; private final int l; + private final int alpha; private final boolean skIsSeed; private final boolean pkExpandShake; @@ -120,6 +121,7 @@ public SnovaParameters(String name, int v, int o, int l, boolean skIsSeed, boole this.v = v; this.o = o; this.l = l; + this.alpha = l * l + l; this.skIsSeed = skIsSeed; this.pkExpandShake = pkExpandShake; } @@ -162,6 +164,17 @@ public int getM() public int getAlpha() { - return l * l + l; + return alpha; + } + + public int getPublicKeyLength() + { + return SnovaKeyPairGenerator.publicSeedLength + ((o * o * o * l * l + 1) >>> 1); + } + + public int getPrivateKeyLength() + { + return ((l * l * (4 * o * alpha + o * (v * v + v * o + o * v) + v * o) + 1) >> 1) + + SnovaKeyPairGenerator.privateSeedLength + SnovaKeyPairGenerator.publicSeedLength; } } From e424fa5dad0269bbe4fd2ef93fa2f6d3492813e3 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 18 Mar 2025 13:31:29 +1100 Subject: [PATCH 226/890] corrected table inclusion - relates to github #2027 --- .../jcajce/provider/util/SecretKeyUtil.java | 7 ++++-- .../jcajce/provider/test/AllTests.java | 1 + .../jcajce/provider/test/RandomTest.java | 5 ++-- .../provider/test/SecretKeyUtilTest.java | 24 +++++++++++++++++++ 4 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 prov/src/test/java/org/bouncycastle/jcajce/provider/test/SecretKeyUtilTest.java diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/util/SecretKeyUtil.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/util/SecretKeyUtil.java index 686d6b8461..42c601a4bb 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/util/SecretKeyUtil.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/util/SecretKeyUtil.java @@ -9,13 +9,16 @@ import org.bouncycastle.internal.asn1.ntt.NTTObjectIdentifiers; import org.bouncycastle.util.Integers; +/** + * @deprecated class appears to be no longer in use, maybe getting imported by others though. + */ public class SecretKeyUtil { - private static Map keySizes = new HashMap(); + private static Map keySizes = new HashMap(); static { - keySizes.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integers.valueOf(192)); + keySizes.put(PKCSObjectIdentifiers.des_EDE3_CBC, Integers.valueOf(192)); keySizes.put(NISTObjectIdentifiers.id_aes128_CBC, Integers.valueOf(128)); keySizes.put(NISTObjectIdentifiers.id_aes192_CBC, Integers.valueOf(192)); diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/AllTests.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/AllTests.java index 295182830e..b14545d2f2 100644 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/AllTests.java +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/AllTests.java @@ -33,6 +33,7 @@ public static Test suite() suite.addTestSuite(CompositeSignaturesTest.class); suite.addTestSuite(BouncyCastleProviderTest.class); suite.addTestSuite(PQCSignatureTest.class); + suite.addTestSuite(SecretKeyUtilTest.class); return new BCTestSetup(suite); } diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/RandomTest.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/RandomTest.java index 5804c43f6d..0b4f11fec4 100644 --- a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/RandomTest.java +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/RandomTest.java @@ -2,7 +2,6 @@ import java.security.SecureRandom; -import junit.framework.Assert; import junit.framework.TestCase; import org.bouncycastle.jce.provider.BouncyCastleProvider; @@ -18,7 +17,7 @@ public void testCheckRandom() random.nextBytes(rng); - Assert.assertTrue(checkNonConstant(rng)); + assertTrue(checkNonConstant(rng)); } public void testCheckNonceIVRandom() @@ -30,7 +29,7 @@ public void testCheckNonceIVRandom() random.nextBytes(rng); - Assert.assertTrue(checkNonConstant(rng)); + assertTrue(checkNonConstant(rng)); } private boolean checkNonConstant(byte[] data) diff --git a/prov/src/test/java/org/bouncycastle/jcajce/provider/test/SecretKeyUtilTest.java b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/SecretKeyUtilTest.java new file mode 100644 index 0000000000..a545cdea09 --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/jcajce/provider/test/SecretKeyUtilTest.java @@ -0,0 +1,24 @@ +package org.bouncycastle.jcajce.provider.test; + +import junit.framework.TestCase; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.internal.asn1.ntt.NTTObjectIdentifiers; +import org.bouncycastle.jcajce.provider.util.SecretKeyUtil; + +public class SecretKeyUtilTest + extends TestCase +{ + public void testgetKeySize() + { + assertEquals(192, SecretKeyUtil.getKeySize(PKCSObjectIdentifiers.des_EDE3_CBC)); + + assertEquals(128, SecretKeyUtil.getKeySize(NISTObjectIdentifiers.id_aes128_CBC)); + assertEquals(192, SecretKeyUtil.getKeySize(NISTObjectIdentifiers.id_aes192_CBC)); + assertEquals(256, SecretKeyUtil.getKeySize(NISTObjectIdentifiers.id_aes256_CBC)); + + assertEquals(128, SecretKeyUtil.getKeySize(NTTObjectIdentifiers.id_camellia128_cbc)); + assertEquals(192, SecretKeyUtil.getKeySize(NTTObjectIdentifiers.id_camellia192_cbc)); + assertEquals(256, SecretKeyUtil.getKeySize(NTTObjectIdentifiers.id_camellia256_cbc)); + } +} From 05f25fb52c848c72ddd40c753ae4bbd8291d6010 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 18 Mar 2025 13:34:57 +1100 Subject: [PATCH 227/890] update --- CONTRIBUTORS.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CONTRIBUTORS.html b/CONTRIBUTORS.html index 77652932e5..5224952758 100644 --- a/CONTRIBUTORS.html +++ b/CONTRIBUTORS.html @@ -557,6 +557,8 @@
  • moonfruit <https://github.com/moonfruit> - Patch to allow for extensions of GMSignatureSpi.
  • Marcono1234 <https://github.com/Marcono1234> - Updates to OpenBSDBCrypt JavaDoc.
  • DawidM <https://github.com/dawmit> - Implementation of EC J-PAKE.
  • +
  • Syed Quasim <https://github.com/HawkItzme> - lint checker fix for EST getTrustAllTrustManager().
  • +
  • winfriedgerlach <https://github.com/winfriedgerlach> - patch to SecretKeyUtil class.
  • From 7acda243c5e7eff137695eb608012d7ccd7c06d7 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 18 Mar 2025 17:17:21 +1030 Subject: [PATCH 228/890] TODO: Fix the bug in generating SNOVA_24_5_5_ESK public key --- .../pqc/crypto/snova/MapGroup1.java | 83 ++++++++++------ .../pqc/crypto/snova/PublicKey.java | 2 +- .../pqc/crypto/snova/SnovaEngine.java | 97 +++++++++++++------ .../crypto/snova/SnovaKeyPairGenerator.java | 57 +++++++++-- .../pqc/crypto/test/SnovaTest.java | 90 ++++++++++++++++- 5 files changed, 256 insertions(+), 73 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java index 19b567b650..18fcdcbab2 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -26,62 +26,87 @@ public MapGroup1(SnovaParameters params) qAlpha2 = new byte[m][alpha][lsq]; } - public int decode(byte[] input, int len) - { - int inOff = decodeP(input, 0, p11, len); - inOff += decodeP(input, inOff, p12, len - inOff); - inOff += decodeP(input, inOff, p21, len - inOff); - inOff += decodeAlpha(input, inOff, aAlpha, len - inOff); - inOff += decodeAlpha(input, inOff, bAlpha, len - inOff); - inOff += decodeAlpha(input, inOff, qAlpha1, len - inOff); - inOff += decodeAlpha(input, inOff, qAlpha2, len - inOff); - return inOff; - } - -// public int encode(byte[] output, int len) +// public int decode(byte[] input, int len) // { -// int outOff = encodeP(p11, output, 0, len); -// outOff += encodeP(p12, output, outOff, len - outOff); -// outOff += encodeP(p21, output, outOff, len - outOff); -// outOff += encodeAlpha(aAlpha, output, outOff, len - outOff); -// outOff += encodeAlpha(bAlpha, output, outOff, len - outOff); -// outOff += encodeAlpha(qAlpha1, output, outOff, len - outOff); -// outOff += encodeAlpha(qAlpha2, output, outOff, len - outOff); -// return outOff; +// int inOff = decodeP(input, 0, p11, len); +// inOff += decodeP(input, inOff, p12, len - inOff); +// inOff += decodeP(input, inOff, p21, len - inOff); +// inOff += decodeAlpha(input, inOff, aAlpha, len - inOff); +// inOff += decodeAlpha(input, inOff, bAlpha, len - inOff); +// inOff += decodeAlpha(input, inOff, qAlpha1, len - inOff); +// inOff += decodeAlpha(input, inOff, qAlpha2, len - inOff); +// return inOff; // } - static int decodeP(byte[] input, int inOff, byte[][][][] p, int len) + public void fill(byte[] input) + { + int inOff = fillP(input, 0, p11, input.length); + inOff += fillP(input, inOff, p12, input.length - inOff); + inOff += fillP(input, inOff, p21, input.length - inOff); + inOff += fillAlpha(input, inOff, aAlpha, input.length - inOff); + inOff += fillAlpha(input, inOff, bAlpha, input.length - inOff); + inOff += fillAlpha(input, inOff, qAlpha1, input.length - inOff); + fillAlpha(input, inOff, qAlpha2, input.length - inOff); + } + + static int fillP(byte[] input, int inOff, byte[][][][] p, int len) { int rlt = 0; for (int i = 0; i < p.length; ++i) { - rlt += decodeAlpha(input, inOff + rlt, p[i], len); + rlt += fillAlpha(input, inOff + rlt, p[i], len - rlt); } return rlt; } - private static int decodeAlpha(byte[] input, int inOff, byte[][][] alpha, int len) + private static int fillAlpha(byte[] input, int inOff, byte[][][] alpha, int len) { int rlt = 0; for (int i = 0; i < alpha.length; ++i) { for (int j = 0; j < alpha[i].length; ++j) { - int tmp = Math.min(alpha[i][j].length, len << 1); - GF16Utils.decode(input, inOff + rlt, alpha[i][j], 0, tmp); - rlt += (tmp + 1) >> 1; - len -= (tmp + 1) >> 1; + int tmp = Math.min(alpha[i][j].length, len - rlt); + System.arraycopy(input, inOff + rlt, alpha[i][j], 0, tmp); + rlt += tmp; } } return rlt; } + +// static int decodeP(byte[] input, int inOff, byte[][][][] p, int len) +// { +// int rlt = 0; +// for (int i = 0; i < p.length; ++i) +// { +// rlt += decodeAlpha(input, inOff + rlt, p[i], len); +// } +// return rlt; +// } + +// private static int decodeAlpha(byte[] input, int inOff, byte[][][] alpha, int len) +// { +// int rlt = 0; +// for (int i = 0; i < alpha.length; ++i) +// { +// for (int j = 0; j < alpha[i].length; ++j) +// { +// int tmp = Math.min(alpha[i][j].length, len << 1); +// GF16Utils.decode(input, inOff + rlt, alpha[i][j], 0, tmp); +// rlt += (tmp + 1) >> 1; +// len -= (tmp + 1) >> 1; +// } +// } +// return rlt; +// } + static int encodeP(byte[][][][] p, byte[] output, int outOff, int len) { int rlt = 0; for (int i = 0; i < p.length; ++i) { - rlt += encodeAlpha(p[i], output, outOff + rlt, len); + rlt += encodeAlpha(p[i], output, outOff + rlt, len - rlt); } return rlt; } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java index a83b48f391..3bc2bcca0e 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java @@ -8,7 +8,7 @@ class PublicKey public PublicKey(SnovaParameters params) { publicKeySeed = new byte[SnovaKeyPairGenerator.publicSeedLength]; - P22 = new byte[(params.getM() * params.getO() * params.getO() * params.getL() * params.getL()) >> 1]; + P22 = new byte[(params.getM() * params.getO() * params.getO() * params.getL() * params.getL() + 1) >> 1]; } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index f68d399630..b598cfd4ae 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -185,6 +185,26 @@ private byte determinant3x3(byte[] m) ); } + private byte determinant3x3(byte[] m, int i0, int i1, int i2, int j0, int j1, int j2) + { + return gf16Add( + gf16Add( + gf16Mul(getGF16m(m, j0, i0), gf16Add( + gf16Mul(getGF16m(m, j1, i1), getGF16m(m, j2, i2)), + gf16Mul(getGF16m(m, j1, i2), getGF16m(m, j2, i1)) + )), + gf16Mul(getGF16m(m, j0, i1), gf16Add( + gf16Mul(getGF16m(m, j1, i0), getGF16m(m, j2, i2)), + gf16Mul(getGF16m(m, j1, i2), getGF16m(m, j2, i0)) + )) + ), + gf16Mul(getGF16m(m, j0, i2), gf16Add( + gf16Mul(getGF16m(m, j1, i0), getGF16m(m, j2, i1)), + gf16Mul(getGF16m(m, j1, i1), getGF16m(m, j2, i0)) + )) + ); + } + private byte determinant4x4(byte[] m) { byte d0 = gf16Mul(getGF16m(m, 0, 0), gf16Add( @@ -224,30 +244,48 @@ private byte determinant4x4(byte[] m) private byte determinant5x5(byte[] m) { - return 0; - //TODO: -// byte result; -// -// result = gf16Mul(det3x3(m, 0, 1, 2, 0, 1, 2), -// gf16Add(gf16Mul(m[3][3], m[4][4]), gf16Mul(m[3][4], m[4][3]))); - // ... similar calculations for other components ... - //result ^= gf16Mul(det3x3(m, 0, 1, 2, 0, 1, 2), - // gf16Add(gf16Mul(m[3][3], m[4][4]), gf16Mul(m[3][4], m[4][3]))); - //return result; - } - - private byte det3x3(byte[] m, int row1, int row2, int row3, int col1, int col2, int col3) - { - //TODO: -// byte[][] sub = new byte[3][3]; -// for (int i = 0; i < 3; i++) -// { -// sub[0][i] = m[row1][col1 + i]; -// sub[1][i] = m[row2][col1 + i]; -// sub[2][i] = m[row3][col1 + i]; -// } -// return determinant3x3(sub); - return 0; + byte result = gf16Mul(determinant3x3(m, 0, 1, 2, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3,3), getGF16m(m, 4,4)), gf16Mul(getGF16m(m, 3,4),getGF16m(m, 4,3)))); + result ^= gf16Mul(determinant3x3(m, 0, 1, 3, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3,2), getGF16m(m, 4,4)), gf16Mul(getGF16m(m, 3,4),getGF16m(m, 4,2)))); + result ^= gf16Mul(determinant3x3(m, 0, 1, 4, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3,2), getGF16m(m, 4,3)), gf16Mul(getGF16m(m, 3,3),getGF16m(m, 4,2)))); + result ^= gf16Mul(determinant3x3(m, 0, 2, 3, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3,1), getGF16m(m, 4,4)), gf16Mul(getGF16m(m, 3,4),getGF16m(m, 4,1)))); + result ^= gf16Mul(determinant3x3(m, 0, 2, 4, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3,1), getGF16m(m, 4,3)), gf16Mul(getGF16m(m, 3,3),getGF16m(m, 4,1)))); + result ^= gf16Mul(determinant3x3(m, 0, 3, 4, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3,1), getGF16m(m, 4,2)), gf16Mul(getGF16m(m, 3,2),getGF16m(m, 4,1)))); + result ^= gf16Mul(determinant3x3(m, 1, 2, 3, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3,0), getGF16m(m, 4,4)), gf16Mul(getGF16m(m, 3,4),getGF16m(m, 4,0)))); + result ^= gf16Mul(determinant3x3(m, 1, 2, 4, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3,0), getGF16m(m, 4,3)), gf16Mul(getGF16m(m, 3,3),getGF16m(m, 4,0)))); + result ^= gf16Mul(determinant3x3(m, 1, 3, 4, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3,0), getGF16m(m, 4,2)), gf16Mul(getGF16m(m, 3,2),getGF16m(m, 4,0)))); + result ^= gf16Mul(determinant3x3(m, 2, 3, 4, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3,0), getGF16m(m, 4,1)), gf16Mul(getGF16m(m, 3,1),getGF16m(m, 4,0)))); +// return result; + byte a012 = determinant3x3(m, 0, 1, 2, 0, 1, 2); + byte b012 = gf16Add(gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 3))); + byte a013 = determinant3x3(m, 0, 1, 3, 0, 1, 2); + byte b013 = gf16Add(gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 2))); + byte a014 = determinant3x3(m, 0, 1, 4, 0, 1, 2); + byte b014 = gf16Add(gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 3)), gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 2))); + byte a023 = determinant3x3(m, 0, 2, 3, 0, 1, 2); + byte b023 = gf16Add(gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 1))); + byte a024 = determinant3x3(m, 0, 2, 4, 0, 1, 2); + byte b024 = gf16Add(gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 3)), gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 1))); + byte a034 = determinant3x3(m, 0, 3, 4, 0, 1, 2); + byte b034 = gf16Add(gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 2)), gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 1))); + byte a123 = determinant3x3(m, 1, 2, 3, 0, 1, 2); + byte b123 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 0))); + byte a124 = determinant3x3(m, 1, 2, 4, 0, 1, 2); + byte b124 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 3)), gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 0))); + byte a134 = determinant3x3(m, 1, 3, 4, 0, 1, 2); + byte b134 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 2)), gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 0))); + byte a234 = determinant3x3(m, 2, 3, 4, 0, 1, 2); + byte b234 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 1)), gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 0))); + return result; } private void generateASMatrix(byte[] target, byte a) @@ -269,9 +307,7 @@ private void generateASMatrix(byte[] target, byte a) // POD -> entry[a][b] * (entry[c][d] * entry[e][f] + entry[g][h] * entry[i][j]) private byte pod(byte[] m, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) { - return gf16Add( - gf16Mul(getGF16m(m, a, b), gf16Mul(getGF16m(m, c, d), getGF16m(m, e, f))), - gf16Mul(getGF16m(m, g, h), getGF16m(m, i, j))); + return gf16Mul(getGF16m(m, a, b), (byte)(gf16Mul(getGF16m(m, c, d), getGF16m(m, e, f)) ^ gf16Mul(getGF16m(m, g, h), getGF16m(m, i, j)))); } private void addMatrices(byte[] a, byte[] b, byte[] c) @@ -312,8 +348,7 @@ public void genAFqS(byte[] c, int cOff, byte[] ptMatrix) } // Handle last term with special case - byte lastScalar = (c[cOff + l - 1] != 0) ? c[cOff + l - 1] : - gf16Add((byte)16, gf16Add(c[cOff], (byte)(c[cOff] == 0 ? 1 : 0))); + byte lastScalar = (byte)((c[cOff + l - 1] != 0) ? c[cOff + l - 1] : 16 - (c[cOff] + (c[cOff] == 0 ? 1 : 0))); gf16mScale(S[l - 1], lastScalar, temp); addMatrices(ptMatrix, temp, ptMatrix); @@ -437,14 +472,14 @@ public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] GF16Utils.gf16mAdd(temp1, temp2, temp1, l); // P22[i][j][k] += temp1 - GF16Utils.gf16mAdd(P22[i][j][k], temp1, P22[i][j][k], l); + GF16Utils.gf16mAdd(P22[i][j][k], temp1, P22[i][j][k], l); } } } } // Convert GF16 elements to packed bytes - MapGroup1.encodeP(P22, outP22, 0, m * o * o *lsq); + MapGroup1.encodeP(P22, outP22, 0, outP22.length); } finally { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index b84a9f2f9a..45bfade6a9 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -53,8 +53,6 @@ public AsymmetricCipherKeyPair generateKeyPair() byte[] ptPublicKeySeed = Arrays.copyOfRange(seedPair, 0, publicSeedLength); byte[] ptPrivateKeySeed = Arrays.copyOfRange(seedPair, publicSeedLength, seedPair.length); -// System.arraycopy(ptPublicKeySeed, 0, sk, 0, ptPublicKeySeed.length); -// System.arraycopy(ptPrivateKeySeed, 0, sk, ptPublicKeySeed.length, ptPrivateKeySeed.length); SnovaKeyElements keyElements = new SnovaKeyElements(params); generateKeysCore(keyElements, ptPublicKeySeed, ptPrivateKeySeed); @@ -133,15 +131,16 @@ private void genABQP(MapGroup1 map1, byte[] pkSeed) int n = v + o; int gf16sPrngPublic = lsq * (2 * m * alpha + m * (n * n - m * m)) + l * 2 * m * alpha; - byte[] qTemp = new byte[(m * alpha * 16 + m * alpha * 16) / l]; + byte[] qTemp = new byte[(m * alpha * lsq + m * alpha * lsq) / l]; byte[] prngOutput = new byte[(gf16sPrngPublic + 1) >> 1]; if (params.isPkExpandShake()) { + snovaShake(pkSeed, prngOutput.length, prngOutput); // SHAKE-based expansion - SHAKEDigest shake = new SHAKEDigest(256); - shake.update(pkSeed, 0, pkSeed.length); - shake.doFinal(prngOutput, 0, prngOutput.length); +// SHAKEDigest shake = new SHAKEDigest(128); +// shake.update(pkSeed, 0, pkSeed.length); +// shake.doFinal(prngOutput, 0, prngOutput.length); } else { @@ -174,11 +173,11 @@ private void genABQP(MapGroup1 map1, byte[] pkSeed) System.arraycopy(blockOut, 0, prngOutput, offset, remaining); } } + byte[] temp = new byte[gf16sPrngPublic - qTemp.length]; + GF16Utils.decode(prngOutput, temp, temp.length); + map1.fill(temp); + GF16Utils.decode(prngOutput, temp.length >> 1, qTemp, 0, qTemp.length); - // Convert bytes to GF16 structures - int inOff = map1.decode(prngOutput, (gf16sPrngPublic - qTemp.length) >> 1); - GF16Utils.decode(prngOutput, inOff, qTemp, 0, qTemp.length); -// // Post-processing for invertible matrices for (int pi = 0; pi < m; ++pi) { @@ -213,4 +212,42 @@ private void genABQP(MapGroup1 map1, byte[] pkSeed) } } } + + public static void snovaShake(byte[] ptSeed, int outputBytes, byte[] out) + { + final int SHAKE128_RATE = 168; // 1344-bit rate = 168 bytes + long blockCounter = 0; + int offset = 0; + int remaining = outputBytes; + + while (remaining > 0) + { + SHAKEDigest shake = new SHAKEDigest(128); + + // Process seed + counter + shake.update(ptSeed, 0, ptSeed.length); + updateWithCounter(shake, blockCounter); + + // Calculate bytes to generate in this iteration + int bytesToGenerate = Math.min(remaining, SHAKE128_RATE); + + // Generate output (XOF mode) + shake.doFinal(out, offset, bytesToGenerate); + + offset += bytesToGenerate; + remaining -= bytesToGenerate; + blockCounter++; + } + } + + private static void updateWithCounter(SHAKEDigest shake, long counter) + { + byte[] counterBytes = new byte[8]; + // Little-endian conversion + for (int i = 0; i < 8; i++) + { + counterBytes[i] = (byte)(counter >> (i * 8)); + } + shake.update(counterBytes, 0, 8); + } } diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java index b02b1ab703..9ae35a34c4 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java @@ -27,11 +27,97 @@ public static void main(String[] args) private static final SnovaParameters[] PARAMETER_SETS = new SnovaParameters[] { - SnovaParameters.SNOVA_24_5_16_4_ESK, +// SnovaParameters.SNOVA_24_5_16_4_ESK, +// SnovaParameters.SNOVA_24_5_16_4_SHAKE_ESK, +// SnovaParameters.SNOVA_24_5_16_4_SHAKE_SSK, +// SnovaParameters.SNOVA_24_5_16_4_SSK, + SnovaParameters.SNOVA_24_5_16_5_ESK, + SnovaParameters.SNOVA_24_5_16_5_SHAKE_ESK, + SnovaParameters.SNOVA_24_5_16_5_SHAKE_SSK, + SnovaParameters.SNOVA_24_5_16_5_SSK, + SnovaParameters.SNOVA_25_8_16_3_ESK, + SnovaParameters.SNOVA_25_8_16_3_SHAKE_ESK, + SnovaParameters.SNOVA_25_8_16_3_SHAKE_SSK, + SnovaParameters.SNOVA_25_8_16_3_SSK, + SnovaParameters.SNOVA_29_6_16_5_ESK, + SnovaParameters.SNOVA_29_6_16_5_SHAKE_ESK, + SnovaParameters.SNOVA_29_6_16_5_SHAKE_SSK, + SnovaParameters.SNOVA_29_6_16_5_SSK, + SnovaParameters.SNOVA_37_8_16_4_ESK, + SnovaParameters.SNOVA_37_8_16_4_SHAKE_ESK, + SnovaParameters.SNOVA_37_8_16_4_SHAKE_SSK, + SnovaParameters.SNOVA_37_8_16_4_SSK, + SnovaParameters.SNOVA_37_17_16_2_ESK, + SnovaParameters.SNOVA_37_17_16_2_SHAKE_ESK, + SnovaParameters.SNOVA_37_17_16_2_SHAKE_SSK, + SnovaParameters.SNOVA_37_17_16_2_SSK, + SnovaParameters.SNOVA_49_11_16_3_ESK, + SnovaParameters.SNOVA_49_11_16_3_SHAKE_ESK, + SnovaParameters.SNOVA_49_11_16_3_SHAKE_SSK, + SnovaParameters.SNOVA_49_11_16_3_SSK, + SnovaParameters.SNOVA_56_25_16_2_ESK, + SnovaParameters.SNOVA_56_25_16_2_SHAKE_ESK, + SnovaParameters.SNOVA_56_25_16_2_SHAKE_SSK, + SnovaParameters.SNOVA_56_25_16_2_SSK, + SnovaParameters.SNOVA_60_10_16_4_ESK, + SnovaParameters.SNOVA_60_10_16_4_SHAKE_ESK, + SnovaParameters.SNOVA_60_10_16_4_SHAKE_SSK, + SnovaParameters.SNOVA_60_10_16_4_SSK, + SnovaParameters.SNOVA_66_15_16_4_ESK, + SnovaParameters.SNOVA_66_15_16_4_SHAKE_ESK, + SnovaParameters.SNOVA_66_15_16_4_SHAKE_SSK, + SnovaParameters.SNOVA_66_15_16_4_SSK, + SnovaParameters.SNOVA_75_33_16_2_ESK, + SnovaParameters.SNOVA_75_33_16_2_SHAKE_ESK, + SnovaParameters.SNOVA_75_33_16_2_SHAKE_SSK, + SnovaParameters.SNOVA_75_33_16_2_SSK, }; private static final String[] files = new String[]{ - "PQCsignKAT_SNOVA_24_5_4_ESK.rsp", +// "PQCsignKAT_SNOVA_24_5_4_ESK.rsp", +// "PQCsignKAT_SNOVA_24_5_4_SHAKE_ESK.rsp", +// "PQCsignKAT_SNOVA_24_5_4_SHAKE_SSK.rsp", +// "PQCsignKAT_SNOVA_24_5_4_SSK.rsp", + "PQCsignKAT_SNOVA_24_5_5_ESK.rsp", + "PQCsignKAT_SNOVA_24_5_5_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_24_5_5_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_24_5_5_SSK.rsp", + "PQCsignKAT_SNOVA_25_8_3_ESK.rsp", + "PQCsignKAT_SNOVA_25_8_3_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_25_8_3_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_25_8_3_SSK.rsp", + "PQCsignKAT_SNOVA_29_6_5_ESK.rsp", + "PQCsignKAT_SNOVA_29_6_5_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_29_6_5_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_29_6_5_SSK.rsp", + "PQCsignKAT_SNOVA_37_8_4_ESK.rsp", + "PQCsignKAT_SNOVA_37_8_4_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_37_8_4_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_37_8_4_SSK.rsp", + "PQCsignKAT_SNOVA_37_17_2_ESK.rsp", + "PQCsignKAT_SNOVA_37_17_2_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_37_17_2_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_37_17_2_SSK.rsp", + "PQCsignKAT_SNOVA_49_11_3_ESK.rsp", + "PQCsignKAT_SNOVA_49_11_3_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_49_11_3_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_49_11_3_SSK.rsp", + "PQCsignKAT_SNOVA_56_25_2_ESK.rsp", + "PQCsignKAT_SNOVA_56_25_2_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_56_25_2_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_56_25_2_SSK.rsp", + "PQCsignKAT_SNOVA_60_10_4_ESK.rsp", + "PQCsignKAT_SNOVA_60_10_4_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_60_10_4_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_60_10_4_SSK.rsp", + "PQCsignKAT_SNOVA_66_15_3_ESK.rsp", + "PQCsignKAT_SNOVA_66_15_3_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_66_15_3_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_66_15_3_SSK.rsp", + "PQCsignKAT_SNOVA_75_33_2_ESK.rsp", + "PQCsignKAT_SNOVA_75_33_2_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_75_33_2_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_75_33_2_SSK.rsp", }; From 2670274c8b70c1f399600f2f0039e53610965038 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 18 Mar 2025 14:17:21 +0700 Subject: [PATCH 229/890] Misc. pkix refactoring --- .../bouncycastle/cert/X509ExtensionUtils.java | 13 ++---- .../org/bouncycastle/cert/test/CertTest.java | 2 +- .../cms/test/NewSignedDataTest.java | 22 +++++----- .../AuthorityKeyIdentifierStructure.java | 43 +++++++------------ 4 files changed, 31 insertions(+), 49 deletions(-) diff --git a/pkix/src/main/java/org/bouncycastle/cert/X509ExtensionUtils.java b/pkix/src/main/java/org/bouncycastle/cert/X509ExtensionUtils.java index fa8cb1edb9..f67498104c 100644 --- a/pkix/src/main/java/org/bouncycastle/cert/X509ExtensionUtils.java +++ b/pkix/src/main/java/org/bouncycastle/cert/X509ExtensionUtils.java @@ -113,23 +113,16 @@ public SubjectKeyIdentifier createTruncatedSubjectKeyIdentifier(SubjectPublicKey private byte[] getSubjectKeyIdentifier(X509CertificateHolder certHolder) { - if (certHolder.getVersionNumber() != 3) - { - return calculateIdentifier(certHolder.getSubjectPublicKeyInfo()); - } - else + if (certHolder.getVersionNumber() == 3) { Extension ext = certHolder.getExtension(Extension.subjectKeyIdentifier); - if (ext != null) { return ASN1OctetString.getInstance(ext.getParsedValue()).getOctets(); } - else - { - return calculateIdentifier(certHolder.getSubjectPublicKeyInfo()); - } } + + return calculateIdentifier(certHolder.getSubjectPublicKeyInfo()); } private byte[] calculateIdentifier(SubjectPublicKeyInfo publicKeyInfo) diff --git a/pkix/src/test/java/org/bouncycastle/cert/test/CertTest.java b/pkix/src/test/java/org/bouncycastle/cert/test/CertTest.java index 9c2993c7e3..cf4ebacefc 100644 --- a/pkix/src/test/java/org/bouncycastle/cert/test/CertTest.java +++ b/pkix/src/test/java/org/bouncycastle/cert/test/CertTest.java @@ -2864,7 +2864,7 @@ public void checkCRLCreation5() { ASN1Enumerated reasonCode = (ASN1Enumerated)fromExtensionValue(ext); - if (reasonCode.intValueExact() != CRLReason.privilegeWithdrawn) + if (!reasonCode.hasValue(CRLReason.privilegeWithdrawn)) { fail("CRL entry reasonCode wrong"); } diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java index 4ae2bfa147..72fab92b54 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java @@ -1976,6 +1976,17 @@ public void testECDSASHA512Encapsulated() encapsulatedTest(_signEcDsaKP, _signEcDsaCert, "SHA512withECDSA"); } + public void testECDSASHA512EncapsulatedWithKeyFactoryAsEC() + throws Exception + { + X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(_signEcDsaKP.getPublic().getEncoded()); + PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(_signEcDsaKP.getPrivate().getEncoded()); + KeyFactory keyFact = KeyFactory.getInstance("EC", BC); + KeyPair kp = new KeyPair(keyFact.generatePublic(pubSpec), keyFact.generatePrivate(privSpec)); + + encapsulatedTest(kp, _signEcDsaCert, "SHA512withECDSA"); + } + public void testECDSASHA3_224Encapsulated() throws Exception { @@ -2048,17 +2059,6 @@ public void testPLAIN_ECDSASHA3_512Encapsulated() encapsulatedTest(_signEcDsaKP, _signEcDsaCert, "SHA3-512withPLAIN-ECDSA"); } - public void testECDSASHA512EncapsulatedWithKeyFactoryAsEC() - throws Exception - { - X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(_signEcDsaKP.getPublic().getEncoded()); - PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(_signEcDsaKP.getPrivate().getEncoded()); - KeyFactory keyFact = KeyFactory.getInstance("EC", BC); - KeyPair kp = new KeyPair(keyFact.generatePublic(pubSpec), keyFact.generatePrivate(privSpec)); - - encapsulatedTest(kp, _signEcDsaCert, "SHA512withECDSA"); - } - public void testDSAEncapsulated() throws Exception { diff --git a/prov/src/main/java/org/bouncycastle/x509/extension/AuthorityKeyIdentifierStructure.java b/prov/src/main/java/org/bouncycastle/x509/extension/AuthorityKeyIdentifierStructure.java index bcd5993264..b629286e46 100644 --- a/prov/src/main/java/org/bouncycastle/x509/extension/AuthorityKeyIdentifierStructure.java +++ b/prov/src/main/java/org/bouncycastle/x509/extension/AuthorityKeyIdentifierStructure.java @@ -1,6 +1,7 @@ package org.bouncycastle.x509.extension; import java.io.IOException; +import java.math.BigInteger; import java.security.InvalidKeyException; import java.security.PublicKey; import java.security.cert.CertificateParsingException; @@ -59,48 +60,36 @@ public AuthorityKeyIdentifierStructure( super((ASN1Sequence)extension.getParsedValue()); } - private static ASN1Sequence fromCertificate( - X509Certificate certificate) + private static ASN1Sequence fromCertificate(X509Certificate certificate) throws CertificateParsingException { try { - if (certificate.getVersion() != 3) - { - GeneralName genName = new GeneralName(PrincipalUtil.getIssuerX509Principal(certificate)); - SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(certificate.getPublicKey().getEncoded()); - - return (ASN1Sequence)new AuthorityKeyIdentifier( - info, new GeneralNames(genName), certificate.getSerialNumber()).toASN1Primitive(); - } - else + GeneralName genName = new GeneralName(PrincipalUtil.getIssuerX509Principal(certificate)); + GeneralNames genNames = new GeneralNames(genName); + BigInteger serialNumber = certificate.getSerialNumber(); + + if (certificate.getVersion() == 3) { - GeneralName genName = new GeneralName(PrincipalUtil.getIssuerX509Principal(certificate)); - - byte[] ext = certificate.getExtensionValue(Extension.subjectKeyIdentifier.getId()); - + byte[] ext = certificate.getExtensionValue(Extension.subjectKeyIdentifier.getId()); if (ext != null) { - ASN1OctetString str = (ASN1OctetString)X509ExtensionUtil.fromExtensionValue(ext); - - return (ASN1Sequence)new AuthorityKeyIdentifier( - str.getOctets(), new GeneralNames(genName), certificate.getSerialNumber()).toASN1Primitive(); - } - else - { - SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(certificate.getPublicKey().getEncoded()); - - return (ASN1Sequence)new AuthorityKeyIdentifier( - info, new GeneralNames(genName), certificate.getSerialNumber()).toASN1Primitive(); + ASN1OctetString str = (ASN1OctetString)X509ExtensionUtil.fromExtensionValue(ext); + return (ASN1Sequence)new AuthorityKeyIdentifier(str.getOctets(), genNames, serialNumber) + .toASN1Primitive(); } } + + SubjectPublicKeyInfo info = SubjectPublicKeyInfo.getInstance(certificate.getPublicKey().getEncoded()); + + return (ASN1Sequence)new AuthorityKeyIdentifier(info, genNames, serialNumber).toASN1Primitive(); } catch (Exception e) { throw new CertificateParsingException("Exception extracting certificate details: " + e.toString()); } } - + private static ASN1Sequence fromKey( PublicKey pubKey) throws InvalidKeyException From da4636f85c970a3ece6c75304058c00ba3acd20e Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 18 Mar 2025 14:27:08 +0700 Subject: [PATCH 230/890] Refactoring in jce.provider.test: - add missing tests to RegressionTest and sort the list - cleanup unused test utils --- .../jce/provider/test/RegressionTest.java | 135 +++++++++--------- .../jce/provider/test/TestCertificateGen.java | 120 +++------------- .../jce/provider/test/TestUtils.java | 8 +- .../provider/test/X509LDAPCertStoreTest.java | 42 +++--- 4 files changed, 115 insertions(+), 190 deletions(-) diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java index 226b948364..9aab17bfc6 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/RegressionTest.java @@ -8,92 +8,99 @@ public class RegressionTest { - public static Test[] tests = { - new FIPSDESTest(), - new DESedeTest(), - new AESTest(), + public static Test[] tests = { new AEADTest(), - new CamelliaTest(), - new SEEDTest(), new AESSICTest(), - new GOST28147Test(), - new PBETest(), + new AESTest(), + new AlgorithmParametersTest(), + new ARIATest(), + new BCFKSStoreTest(), new BlockCipherTest(), - new MacTest(), - new HMacTest(), - new SealedTest(), - new RSATest(), - new DHTest(), + new CamelliaTest(), + new CertLocaleTest(), + new CertPathBuilderTest(), + new CertPathTest(), + new CertPathValidatorTest(), + new CertStoreTest(), + new CertTest(), + new CertUniqueIDTest(), + new ChaCha20Poly1305Test(), + new CipherStreamTest(), + new CipherStreamTest2(), + new CMacTest(), + new CRL5Test(), + new DESedeTest(), + new DetDSATest(), new DHIESTest(), + new DHTest(), + new DigestTest(), + new DoFinalTest(), + new DRBGTest(), new DSATest(), - new ImplicitlyCaTest(), - new ECNRTest(), + new DSTU4145Test(), + new DSTU7624Test(), + new ECDSA5Test(), + new ECEncodingTest(), new ECIESTest(), new ECIESVectorTest(), - new ECDSA5Test(), - new GOST3410Test(), + new ECNRTest(), + new EdECTest(), new ElGamalTest(), - new IESTest(), - new SigTest(), - new CertTest(), - new PKCS10CertRequestTest(), new EncryptedPrivateKeyInfoTest(), + new FIPSDESTest(), + new GMacTest(), + new GOST28147Test(), + new GOST3410KeyPairTest(), + new GOST3410Test(), + new GOST3412Test(), + new HMacTest(), + new IESTest(), + new ImplicitlyCaTest(), + new KeccakTest(), new KeyStoreTest(), - new PKCS12StoreTest(), - new DigestTest(), - new PSSTest(), - new WrapTest(), - new DoFinalTest(), - new CipherStreamTest(), - new CipherStreamTest2(), + new MacTest(), + new MQVTest(), + new MultiCertStoreTest(), new NamedCurveTest(), - new PKIXTest(), new NetscapeCertRequestTest(), - new X509StreamParserTest(), - new X509CertificatePairTest(), - new CertPathTest(), - new CertStoreTest(), - new CertPathValidatorTest(), - new CertPathBuilderTest(), - new ECEncodingTest(), - new AlgorithmParametersTest(), new NISTCertPathTest(), - new PKIXPolicyMappingTest(), - new SlotTwoTest(), - new PKIXNameConstraintsTest(), - new MultiCertStoreTest(), new NoekeonTest(), - new SerialisationTest(), - new SigNameTest(), - new MQVTest(), - new CMacTest(), - new GMacTest(), new OCBTest(), - new DSTU4145Test(), - new CRL5Test(), + new OpenSSHSpecTests(), + new PBETest(), + new PKCS10CertRequestTest(), + new PKCS12StorePBETest(), + new PKCS12StoreTest(), + new PKIXNameConstraintsTest(), + new PKIXPolicyMappingTest(), + new PKIXTest(), new Poly1305Test(), + new PQCDHTest(), + new PSSTest(), + new RSATest(), + new SealedTest(), + new SEEDTest(), + new SerialisationTest(), + new Shacal2Test(), + new SigNameTest(), + new SignatureTest(), + new SigTest(), + new SipHash128Test(), new SipHashTest(), - new KeccakTest(), new SkeinTest(), - new Shacal2Test(), - new DetDSATest(), - new ThreefishTest(), + new SlotTwoTest(), + new SM2CipherTest(), new SM2SignatureTest(), new SM4Test(), + new ThreefishTest(), new TLSKDFTest(), - new BCFKSStoreTest(), - new DSTU7624Test(), - new GOST3412Test(), - new GOST3410KeyPairTest(), - new EdECTest(), - new OpenSSHSpecTests(), - new SM2CipherTest(), - new ZucTest(), - new ChaCha20Poly1305Test(), - new SipHash128Test(), - new XOFTest(), + new WrapTest(), + new X509CertificatePairTest(), + new X509LDAPCertStoreTest(), + new X509StreamParserTest(), new XIESTest(), - new CertLocaleTest() + new XOFTest(), + new ZucTest(), }; public static void main(String[] args) diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/TestCertificateGen.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/TestCertificateGen.java index 1758e27387..e95599ca3f 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/TestCertificateGen.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/TestCertificateGen.java @@ -1,15 +1,10 @@ package org.bouncycastle.jce.provider.test; import java.io.ByteArrayInputStream; -import java.io.IOException; import java.math.BigInteger; import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; -import java.security.SecureRandom; import java.security.Signature; import java.security.cert.CertificateFactory; import java.security.cert.X509CRL; @@ -29,17 +24,13 @@ import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; -import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.CRLNumber; import org.bouncycastle.asn1.x509.CRLReason; -import org.bouncycastle.asn1.x509.Certificate; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.Extensions; import org.bouncycastle.asn1.x509.ExtensionsGenerator; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; -import org.bouncycastle.asn1.x509.KeyUsage; -import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.asn1.x509.TBSCertList; import org.bouncycastle.asn1.x509.TBSCertificate; @@ -190,83 +181,6 @@ public static X509Certificate createCertWithIDs(X500Name signerName, String sigN return (X509Certificate)CertificateFactory.getInstance("X.509", "BC").generateCertificate(new ByteArrayInputStream(new DERSequence(v).getEncoded(ASN1Encoding.DER))); } - /** - * Create a random 1024 bit RSA key pair - */ - public static KeyPair generateRSAKeyPair() - throws Exception - { - KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC"); - - kpGen.initialize(1024, new SecureRandom()); - - return kpGen.generateKeyPair(); - } - - public static X509Certificate generateRootCert(KeyPair pair) - throws Exception - { - return createSelfSignedCert("CN=Test CA Certificate", "SHA256withRSA", pair); - } - - public static X509Certificate generateRootCert(KeyPair pair, X500Name dn) - throws Exception - { - return createSelfSignedCert(dn, "SHA256withRSA", pair); - } - - public static X509Certificate generateIntermediateCert(PublicKey intKey, PrivateKey caKey, X509Certificate caCert) - throws Exception - { - return generateIntermediateCert( - intKey, new X500Name("CN=Test Intermediate Certificate"), caKey, caCert); - } - - public static X509Certificate generateIntermediateCert(PublicKey intKey, X500Name subject, PrivateKey caKey, X509Certificate caCert) - throws Exception - { - Certificate caCertLw = Certificate.getInstance(caCert.getEncoded()); - - ExtensionsGenerator extGen = new ExtensionsGenerator(); - - extGen.addExtension(Extension.authorityKeyIdentifier, false, new AuthorityKeyIdentifier(getDigest(caCertLw.getSubjectPublicKeyInfo()), - new GeneralNames(new GeneralName(caCertLw.getIssuer())), - caCertLw.getSerialNumber().getValue())); - extGen.addExtension(Extension.subjectKeyIdentifier, false, new SubjectKeyIdentifier(getDigest(SubjectPublicKeyInfo.getInstance(intKey.getEncoded())))); - extGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(0)); - extGen.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); - - return createCert( - caCertLw.getSubject(), - caKey, subject, "SHA256withRSA", extGen.generate(), intKey); - } - - public static X509Certificate generateEndEntityCert(PublicKey intKey, PrivateKey caKey, X509Certificate caCert) - throws Exception - { - return generateEndEntityCert( - intKey, new X500Name("CN=Test End Certificate"), caKey, caCert); - } - - public static X509Certificate generateEndEntityCert(PublicKey entityKey, X500Name subject, PrivateKey caKey, X509Certificate caCert) - throws Exception - { - Certificate caCertLw = Certificate.getInstance(caCert.getEncoded()); - - ExtensionsGenerator extGen = new ExtensionsGenerator(); - - extGen.addExtension(Extension.authorityKeyIdentifier, false, new AuthorityKeyIdentifier(getDigest(caCertLw.getSubjectPublicKeyInfo()), - new GeneralNames(new GeneralName(caCertLw.getIssuer())), - caCertLw.getSerialNumber().getValue())); - extGen.addExtension(Extension.subjectKeyIdentifier, false, new SubjectKeyIdentifier(getDigest(entityKey.getEncoded()))); - extGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(0)); - extGen.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); - - return createCert( - caCertLw.getSubject(), - caKey, subject, "SHA256withRSA", extGen.generate(), entityKey); - } - public static X509CRL createCRL( X509Certificate caCert, PrivateKey caKey, @@ -309,23 +223,23 @@ public static X509CRL createCRL( return (X509CRL)CertificateFactory.getInstance("X.509", "BC").generateCRL(new ByteArrayInputStream(new DERSequence(v).getEncoded(ASN1Encoding.DER))); } - private static byte[] getDigest(SubjectPublicKeyInfo spki) - throws IOException - { - return getDigest(spki.getPublicKeyData().getBytes()); - } - - private static byte[] getDigest(byte[] bytes) - { - try - { - return MessageDigest.getInstance("SHA1").digest(bytes); - } - catch (NoSuchAlgorithmException e) - { - return null; - } - } +// private static byte[] getDigest(SubjectPublicKeyInfo spki) +// throws IOException +// { +// return getDigest(spki.getPublicKeyData().getBytes()); +// } + +// private static byte[] getDigest(byte[] bytes) +// { +// try +// { +// return MessageDigest.getInstance("SHA1").digest(bytes); +// } +// catch (NoSuchAlgorithmException e) +// { +// return null; +// } +// } private static DERBitString booleanToBitString(boolean[] id) { diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/TestUtils.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/TestUtils.java index 89a7d1e691..b274b3d1ac 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/TestUtils.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/TestUtils.java @@ -255,8 +255,8 @@ public static X509Certificate generateEndEntityCert(PublicKey entityKey, X500Nam new GeneralNames(new GeneralName(caCertLw.getIssuer())), caCertLw.getSerialNumber().getValue())); extGen.addExtension(Extension.subjectKeyIdentifier, false, new SubjectKeyIdentifier(getDigest(entityKey.getEncoded()))); - extGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(0)); - extGen.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); + extGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(false)); + extGen.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature)); return createCert( caCertLw.getSubject(), @@ -280,8 +280,8 @@ public static X509Certificate generateEndEntityCert(PublicKey entityKey, X500Nam new GeneralNames(new GeneralName(caCertLw.getIssuer())), caCertLw.getSerialNumber().getValue())); extGen.addExtension(Extension.subjectKeyIdentifier, false, new SubjectKeyIdentifier(getDigest(entityKey.getEncoded()))); - extGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(0)); - extGen.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.keyCertSign | KeyUsage.cRLSign)); + extGen.addExtension(Extension.basicConstraints, true, new BasicConstraints(false)); + extGen.addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature)); if (keyPurpose2 == null) { extGen.addExtension(Extension.extendedKeyUsage, true, new ExtendedKeyUsage(keyPurpose1)); diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/X509LDAPCertStoreTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/X509LDAPCertStoreTest.java index 98419dab77..03c31c2ed2 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/X509LDAPCertStoreTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/X509LDAPCertStoreTest.java @@ -9,12 +9,12 @@ import com.unboundid.ldap.sdk.LDAPResult; import com.unboundid.ldap.sdk.ResultCode; import com.unboundid.ldif.LDIFException; -import junit.framework.TestCase; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.jce.X509LDAPCertStoreParameters; import org.bouncycastle.jce.exception.ExtCertPathBuilderException; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.test.TestResourceFinder; +import org.bouncycastle.util.test.SimpleTest; import javax.net.ServerSocketFactory; import javax.net.SocketFactory; @@ -39,18 +39,10 @@ import java.util.List; public class X509LDAPCertStoreTest - extends TestCase + extends SimpleTest { - public void setUp() - { - if (Security.getProvider("BC") == null) - { - Security.addProvider(new BouncyCastleProvider()); - } - } - - public void testLdapFilter() - throws Exception + public void performTest() + throws Exception { BcFilterCheck filterCheck = new BcFilterCheck(); @@ -96,7 +88,7 @@ public void testLdapFilter() //shut down ldap server ds.shutDown(true); - assertTrue(filterCheck.isUsed()); + isTrue(filterCheck.isUsed()); } private static InMemoryDirectoryServer mockLdapServer(BcFilterCheck filterCheck) @@ -116,7 +108,7 @@ private static InMemoryDirectoryServer mockLdapServer(BcFilterCheck filterCheck) return new InMemoryDirectoryServer(serverConfig); } - public static void readEntriesFromFile(InMemoryDirectoryServer ds) throws IOException, LDAPException, LDIFException + private void readEntriesFromFile(InMemoryDirectoryServer ds) throws IOException, LDAPException, LDIFException { InputStream src = TestResourceFinder.findTestResource("ldap/", "X509LDAPCertTest.ldif"); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); @@ -168,11 +160,12 @@ public static void readEntriesFromFile(InMemoryDirectoryServer ds) throws IOExce // addEntry(ds, "dn: cn=chars[*()\\\0],dc=people,dc=test", "objectClass: Person", "objectClass: organizationalPerson", "sn: chars", "cn: chars[*()\\\0]"); // } - public static void addEntry(InMemoryDirectoryServer ds, String... args) + private void addEntry(InMemoryDirectoryServer ds, String... args) throws LDIFException, LDAPException { LDAPResult result = ds.add(args); - assertEquals(0, result.getResultCode().intValue()); + + isEquals(0, result.getResultCode().intValue()); } static void verifyCert(X509Certificate cert) @@ -199,7 +192,6 @@ static void verifyCert(X509Certificate cert) { CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC"); PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult)builder.build(pkixParams); - } catch (ExtCertPathBuilderException exception) { @@ -210,7 +202,7 @@ static void verifyCert(X509Certificate cert) /* check we get a suitably escaped subject. */ - static class BcFilterCheck + class BcFilterCheck extends InMemoryOperationInterceptor { private volatile boolean used = false; @@ -219,7 +211,7 @@ public void processSearchResult(InMemoryInterceptedSearchResult result) { String filter = result.getRequest().getFilter().toString(); - assertEquals("(&(cn=*chars[\\2a\\28\\29\\00]*)(userCertificate=*))", filter); + isEquals("(&(cn=*chars[\\2a\\28\\29\\00]*)(userCertificate=*))", filter); used = true; @@ -231,4 +223,16 @@ boolean isUsed() return used; } } + + public String getName() + { + return "X509LDAPCertStore"; + } + + public static void main(String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new X509LDAPCertStoreTest()); + } } From 72513f28ed7d9872a5b5304a99532fb1c7714c8c Mon Sep 17 00:00:00 2001 From: Tony Washer Date: Tue, 18 Mar 2025 10:52:17 +0000 Subject: [PATCH 231/890] Optimise extra ascon rounds between outputs --- .../java/org/bouncycastle/crypto/digests/AsconBaseDigest.java | 3 +-- .../java/org/bouncycastle/crypto/digests/AsconXof128.java | 4 ++++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java index 50037c731c..7f60eed063 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java @@ -151,8 +151,7 @@ protected void squeeze(byte[] output, int outOff, int len) } /* squeeze final output block */ setBytes(x0, output, outOff, len); - p(ASCON_PB_ROUNDS); - } + } protected int hash(byte[] output, int outOff, int outLen) { diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java index 0b8f368d9a..cefbc03871 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java @@ -69,6 +69,10 @@ protected void padAndAbsorb() m_squeezing = true; super.padAndAbsorb(); } + else + { + p(ASCON_PB_ROUNDS); + } } @Override From 75c2f1ae1e93add20a43f7240a997fbdc84aa37f Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 18 Mar 2025 20:56:36 +0700 Subject: [PATCH 232/890] Refactor ASN1Dump --- .../org/bouncycastle/asn1/ASN1BitString.java | 10 ++ .../org/bouncycastle/asn1/util/ASN1Dump.java | 153 ++++++++---------- 2 files changed, 75 insertions(+), 88 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1BitString.java b/core/src/main/java/org/bouncycastle/asn1/ASN1BitString.java index aff52097a1..b5ada96794 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1BitString.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1BitString.java @@ -311,6 +311,16 @@ public byte[] getBytes() return rv; } + public int getBytesLength() + { + return contents.length - 1; + } + + public boolean isOctetAligned() + { + return getPadBits() == 0; + } + public int getPadBits() { return contents[0] & 0xFF; diff --git a/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java b/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java index 4ff5e2dcd9..bc2400b298 100644 --- a/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java +++ b/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java @@ -52,22 +52,18 @@ public class ASN1Dump * * @param obj the ASN1Primitive to be dumped out. */ - static void _dumpAsString( - String indent, - boolean verbose, - ASN1Primitive obj, - StringBuffer buf) + static void _dumpAsString(String indent, boolean verbose, ASN1Primitive obj, StringBuffer buf) { String nl = Strings.lineSeparator(); + buf.append(indent); + if (obj instanceof ASN1Null) { - buf.append(indent); buf.append("NULL"); buf.append(nl); } else if (obj instanceof ASN1Sequence) { - buf.append(indent); if (obj instanceof BERSequence) { buf.append("BER Sequence"); @@ -92,7 +88,6 @@ else if (obj instanceof DERSequence) } else if (obj instanceof ASN1Set) { - buf.append(indent); if (obj instanceof BERSet) { buf.append("BER Set"); @@ -117,7 +112,6 @@ else if (obj instanceof DERSet) } else if (obj instanceof ASN1TaggedObject) { - buf.append(indent); if (obj instanceof BERTaggedObject) { buf.append("BER Tagged "); @@ -137,7 +131,7 @@ else if (obj instanceof DERTaggedObject) if (!o.isExplicit()) { - buf.append(" IMPLICIT "); + buf.append(" IMPLICIT"); } buf.append(nl); @@ -146,130 +140,124 @@ else if (obj instanceof DERTaggedObject) _dumpAsString(baseIndent, verbose, o.getBaseObject().toASN1Primitive(), buf); } + else if (obj instanceof ASN1ObjectIdentifier) + { + buf.append("ObjectIdentifier(" + ((ASN1ObjectIdentifier)obj).getId() + ")" + nl); + } + else if (obj instanceof ASN1RelativeOID) + { + buf.append("RelativeOID(" + ((ASN1RelativeOID)obj).getId() + ")" + nl); + } + else if (obj instanceof ASN1Boolean) + { + buf.append("Boolean(" + ((ASN1Boolean)obj).isTrue() + ")" + nl); + } + else if (obj instanceof ASN1Integer) + { + buf.append("Integer(" + ((ASN1Integer)obj).getValue() + ")" + nl); + } else if (obj instanceof ASN1OctetString) { ASN1OctetString oct = (ASN1OctetString)obj; if (obj instanceof BEROctetString) { - buf.append(indent + "BER Constructed Octet String" + "[" + oct.getOctets().length + "] "); + buf.append("BER Constructed Octet String["); } else { - buf.append(indent + "DER Octet String" + "[" + oct.getOctets().length + "] "); + buf.append("DER Octet String["); } + + buf.append(oct.getOctetsLength() + "]" + nl); + if (verbose) { - buf.append(dumpBinaryDataAsString(indent, oct.getOctets())); - } - else - { - buf.append(nl); + dumpBinaryDataAsString(buf, indent, oct.getOctets()); } } - else if (obj instanceof ASN1ObjectIdentifier) - { - buf.append(indent + "ObjectIdentifier(" + ((ASN1ObjectIdentifier)obj).getId() + ")" + nl); - } - else if (obj instanceof ASN1RelativeOID) - { - buf.append(indent + "RelativeOID(" + ((ASN1RelativeOID)obj).getId() + ")" + nl); - } - else if (obj instanceof ASN1Boolean) - { - buf.append(indent + "Boolean(" + ((ASN1Boolean)obj).isTrue() + ")" + nl); - } - else if (obj instanceof ASN1Integer) - { - buf.append(indent + "Integer(" + ((ASN1Integer)obj).getValue() + ")" + nl); - } else if (obj instanceof ASN1BitString) { ASN1BitString bitString = (ASN1BitString)obj; - byte[] bytes = bitString.getBytes(); - int padBits = bitString.getPadBits(); - if (bitString instanceof DERBitString) { - buf.append(indent + "DER Bit String" + "[" + bytes.length + ", " + padBits + "] "); + buf.append("DER Bit String["); } else if (bitString instanceof DLBitString) { - buf.append(indent + "DL Bit String" + "[" + bytes.length + ", " + padBits + "] "); + buf.append("DL Bit String["); } else { - buf.append(indent + "BER Bit String" + "[" + bytes.length + ", " + padBits + "] "); + buf.append("BER Bit String["); } + buf.append(bitString.getBytesLength() + ", " + bitString.getPadBits() + "]" + nl); + if (verbose) { - buf.append(dumpBinaryDataAsString(indent, bytes)); - } - else - { - buf.append(nl); + dumpBinaryDataAsString(buf, indent, bitString.getBytes()); } } else if (obj instanceof ASN1IA5String) { - buf.append(indent + "IA5String(" + ((ASN1IA5String)obj).getString() + ") " + nl); + buf.append("IA5String(" + ((ASN1IA5String)obj).getString() + ") " + nl); } else if (obj instanceof ASN1UTF8String) { - buf.append(indent + "UTF8String(" + ((ASN1UTF8String)obj).getString() + ") " + nl); + buf.append("UTF8String(" + ((ASN1UTF8String)obj).getString() + ") " + nl); } else if (obj instanceof ASN1NumericString) { - buf.append(indent + "NumericString(" + ((ASN1NumericString)obj).getString() + ") " + nl); + buf.append("NumericString(" + ((ASN1NumericString)obj).getString() + ") " + nl); } else if (obj instanceof ASN1PrintableString) { - buf.append(indent + "PrintableString(" + ((ASN1PrintableString)obj).getString() + ") " + nl); + buf.append("PrintableString(" + ((ASN1PrintableString)obj).getString() + ") " + nl); } else if (obj instanceof ASN1VisibleString) { - buf.append(indent + "VisibleString(" + ((ASN1VisibleString)obj).getString() + ") " + nl); + buf.append("VisibleString(" + ((ASN1VisibleString)obj).getString() + ") " + nl); } else if (obj instanceof ASN1BMPString) { - buf.append(indent + "BMPString(" + ((ASN1BMPString)obj).getString() + ") " + nl); + buf.append("BMPString(" + ((ASN1BMPString)obj).getString() + ") " + nl); } else if (obj instanceof ASN1T61String) { - buf.append(indent + "T61String(" + ((ASN1T61String)obj).getString() + ") " + nl); + buf.append("T61String(" + ((ASN1T61String)obj).getString() + ") " + nl); } else if (obj instanceof ASN1GraphicString) { - buf.append(indent + "GraphicString(" + ((ASN1GraphicString)obj).getString() + ") " + nl); + buf.append("GraphicString(" + ((ASN1GraphicString)obj).getString() + ") " + nl); } else if (obj instanceof ASN1VideotexString) { - buf.append(indent + "VideotexString(" + ((ASN1VideotexString)obj).getString() + ") " + nl); + buf.append("VideotexString(" + ((ASN1VideotexString)obj).getString() + ") " + nl); } else if (obj instanceof ASN1UTCTime) { - buf.append(indent + "UTCTime(" + ((ASN1UTCTime)obj).getTime() + ") " + nl); + buf.append("UTCTime(" + ((ASN1UTCTime)obj).getTime() + ") " + nl); } else if (obj instanceof ASN1GeneralizedTime) { - buf.append(indent + "GeneralizedTime(" + ((ASN1GeneralizedTime)obj).getTime() + ") " + nl); + buf.append("GeneralizedTime(" + ((ASN1GeneralizedTime)obj).getTime() + ") " + nl); } else if (obj instanceof ASN1Enumerated) { ASN1Enumerated en = (ASN1Enumerated) obj; - buf.append(indent + "DER Enumerated(" + en.getValue() + ")" + nl); + buf.append("DER Enumerated(" + en.getValue() + ")" + nl); } else if (obj instanceof ASN1ObjectDescriptor) { ASN1ObjectDescriptor od = (ASN1ObjectDescriptor)obj; - buf.append(indent + "ObjectDescriptor(" + od.getBaseGraphicString().getString() + ") " + nl); + buf.append("ObjectDescriptor(" + od.getBaseGraphicString().getString() + ") " + nl); } else if (obj instanceof ASN1External) { ASN1External ext = (ASN1External) obj; - buf.append(indent + "External " + nl); + buf.append("External " + nl); String tab = indent + TAB; if (ext.getDirectReference() != null) { @@ -288,7 +276,7 @@ else if (obj instanceof ASN1External) } else { - buf.append(indent + obj.toString() + nl); + buf.append(obj.toString() + nl); } } @@ -334,45 +322,36 @@ else if (obj instanceof ASN1Encodable) return buf.toString(); } - private static String dumpBinaryDataAsString(String indent, byte[] bytes) + private static void dumpBinaryDataAsString(StringBuffer buf, String indent, byte[] bytes) { + if (bytes.length < 1) + { + return; + } + String nl = Strings.lineSeparator(); - StringBuffer buf = new StringBuffer(); indent += TAB; - - buf.append(nl); + for (int i = 0; i < bytes.length; i += SAMPLE_SIZE) { - if (bytes.length - i > SAMPLE_SIZE) - { - buf.append(indent); - buf.append(Strings.fromByteArray(Hex.encode(bytes, i, SAMPLE_SIZE))); - buf.append(TAB); - buf.append(calculateAscString(bytes, i, SAMPLE_SIZE)); - buf.append(nl); - } - else + int remaining = bytes.length - i; + int chunk = Math.min(remaining, SAMPLE_SIZE); + + buf.append(indent); + buf.append(Hex.toHexString(bytes, i, chunk)); + for (int j = chunk; j < SAMPLE_SIZE; ++j) { - buf.append(indent); - buf.append(Strings.fromByteArray(Hex.encode(bytes, i, bytes.length - i))); - for (int j = bytes.length - i; j != SAMPLE_SIZE; j++) - { - buf.append(" "); - } - buf.append(TAB); - buf.append(calculateAscString(bytes, i, bytes.length - i)); - buf.append(nl); + buf.append(" "); } + buf.append(TAB); + appendAscString(buf, bytes, i, chunk); + buf.append(nl); } - - return buf.toString(); } - private static String calculateAscString(byte[] bytes, int off, int len) + private static void appendAscString(StringBuffer buf, byte[] bytes, int off, int len) { - StringBuffer buf = new StringBuffer(); - for (int i = off; i != off + len; i++) { if (bytes[i] >= ' ' && bytes[i] <= '~') @@ -380,7 +359,5 @@ private static String calculateAscString(byte[] bytes, int off, int len) buf.append((char)bytes[i]); } } - - return buf.toString(); } } From eb6bf9c7785c57de3706c4caa123472928d0954a Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 19 Mar 2025 09:40:39 +1030 Subject: [PATCH 233/890] Initial push of the branch for #2029 --- core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java b/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java index bc2400b298..9bfcc224bf 100644 --- a/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java +++ b/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java @@ -339,6 +339,7 @@ private static void dumpBinaryDataAsString(StringBuffer buf, String indent, byte int chunk = Math.min(remaining, SAMPLE_SIZE); buf.append(indent); + // -DM Hex.toHexString buf.append(Hex.toHexString(bytes, i, chunk)); for (int j = chunk; j < SAMPLE_SIZE; ++j) { From fa0d3ae87405f5c477030c5deadbd82401547805 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 19 Mar 2025 10:12:53 +1030 Subject: [PATCH 234/890] Add //-DM Hex.toHexString to ASN1Dump --- core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java b/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java index bc2400b298..9bfcc224bf 100644 --- a/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java +++ b/core/src/main/java/org/bouncycastle/asn1/util/ASN1Dump.java @@ -339,6 +339,7 @@ private static void dumpBinaryDataAsString(StringBuffer buf, String indent, byte int chunk = Math.min(remaining, SAMPLE_SIZE); buf.append(indent); + // -DM Hex.toHexString buf.append(Hex.toHexString(bytes, i, chunk)); for (int j = chunk; j < SAMPLE_SIZE; ++j) { From e0ba6c2d4869c3f444c5269e370b6f22d267792c Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 19 Mar 2025 10:43:50 +1030 Subject: [PATCH 235/890] Fix the bugs that ASCON Xof does not support multi-part outputs based on the code from #2030 . --- .../crypto/digests/AsconBaseDigest.java | 9 -- .../crypto/digests/AsconCXof128.java | 46 +------- .../bouncycastle/crypto/digests/AsconXof.java | 41 +------ .../crypto/digests/AsconXof128.java | 40 +------ .../crypto/digests/AsconXofBase.java | 106 ++++++++++++++++++ .../bouncycastle/crypto/test/AsconTest.java | 6 + .../bouncycastle/crypto/test/DigestTest.java | 40 +++++++ 7 files changed, 156 insertions(+), 132 deletions(-) create mode 100644 core/src/main/java/org/bouncycastle/crypto/digests/AsconXofBase.java diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java index e1e452e9e8..872655f1cd 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconBaseDigest.java @@ -25,7 +25,6 @@ protected AsconBaseDigest() DigestSize = 32; } - protected abstract long pad(int i); protected abstract long loadBytes(final byte[] bytes, int inOff); @@ -85,12 +84,4 @@ protected void ensureSufficientOutputBuffer(byte[] output, int outOff, int len) throw new OutputLengthException("output buffer is too short"); } } - - protected void ensureNoAbsorbWhileSqueezing(boolean m_squeezing) - { - if (m_squeezing) - { - throw new IllegalArgumentException("attempt to absorb while squeezing"); - } - } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java index 19071923fa..70bc4ff572 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java @@ -1,7 +1,6 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.Xof; import org.bouncycastle.util.Pack; /** @@ -16,10 +15,8 @@ *

    */ public class AsconCXof128 - extends AsconBaseDigest - implements Xof + extends AsconXofBase { - private boolean m_squeezing = false; private final long z0, z1, z2, z3, z4; public AsconCXof128() @@ -49,20 +46,6 @@ public AsconCXof128(byte[] s, int off, int len) z4 = p.x4; } - @Override - public void update(byte in) - { - ensureNoAbsorbWhileSqueezing(m_squeezing); - super.update(in); - } - - @Override - public void update(byte[] input, int inOff, int len) - { - ensureNoAbsorbWhileSqueezing(m_squeezing); - super.update(input, inOff, len); - } - protected long pad(int i) { return 0x01L << (i << 3); @@ -88,35 +71,10 @@ protected void setBytes(long w, byte[] bytes, int inOff, int n) Pack.longToLittleEndian(w, bytes, inOff, n); } - protected void padAndAbsorb() - { - m_squeezing = true; - super.padAndAbsorb(); - } - - @Override - public int doOutput(byte[] output, int outOff, int outLen) - { - ensureSufficientOutputBuffer(output, outOff, outLen); - padAndAbsorb(); - /* squeeze full output blocks */ - squeeze(output, outOff, outLen); - return outLen; - } - - @Override - public int doFinal(byte[] output, int outOff, int outLen) - { - int rlt = doOutput(output, outOff, outLen); - reset(); - return rlt; - } - @Override public void reset() { super.reset(); - m_squeezing = false; /* initialize */ p.set(z0, z1, z2, z3, z4); } @@ -129,7 +87,7 @@ private void initState(byte[] z, int zOff, int zLen) p.p(12); update(z, zOff, zLen); padAndAbsorb(); - m_squeezing = false; + super.reset(); } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java index 12c19b72b0..db57218f9e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof.java @@ -1,6 +1,5 @@ package org.bouncycastle.crypto.digests; -import org.bouncycastle.crypto.Xof; import org.bouncycastle.util.Pack; /** @@ -13,8 +12,7 @@ * @deprecated Now superseded - please use AsconXof128 */ public class AsconXof - extends AsconBaseDigest - implements Xof + extends AsconXofBase { public enum AsconParameters { @@ -44,28 +42,6 @@ public AsconXof(AsconXof.AsconParameters parameters) reset(); } - private boolean m_squeezing = false; - - @Override - public void update(byte in) - { - ensureNoAbsorbWhileSqueezing(m_squeezing); - super.update(in); - } - - @Override - public void update(byte[] input, int inOff, int len) - { - ensureNoAbsorbWhileSqueezing(m_squeezing); - super.update(input, inOff, len); - } - - protected void padAndAbsorb() - { - m_squeezing = true; - super.padAndAbsorb(); - } - protected long pad(int i) { return 0x80L << (56 - (i << 3)); @@ -91,25 +67,10 @@ protected void setBytes(long w, byte[] bytes, int inOff, int n) Pack.longToBigEndian(w, bytes, inOff, n); } - @Override - public int doOutput(byte[] output, int outOff, int outLen) - { - return hash(output, outOff, outLen); - } - - @Override - public int doFinal(byte[] output, int outOff, int outLen) - { - int rlt = doOutput(output, outOff, outLen); - reset(); - return rlt; - } - @Override public void reset() { super.reset(); - m_squeezing = false; /* initialize */ switch (asconParameters) { diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java index 1a57225094..05dad34226 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXof128.java @@ -1,6 +1,5 @@ package org.bouncycastle.crypto.digests; -import org.bouncycastle.crypto.Xof; import org.bouncycastle.util.Pack; /** @@ -15,10 +14,8 @@ *

    */ public class AsconXof128 - extends AsconBaseDigest - implements Xof + extends AsconXofBase { - private boolean m_squeezing; public AsconXof128() { @@ -51,44 +48,9 @@ protected void setBytes(long w, byte[] bytes, int inOff, int n) Pack.longToLittleEndian(w, bytes, inOff, n); } - protected void padAndAbsorb() - { - m_squeezing = true; - super.padAndAbsorb(); - } - - @Override - public void update(byte in) - { - ensureNoAbsorbWhileSqueezing(m_squeezing); - super.update(in); - } - - @Override - public void update(byte[] input, int inOff, int len) - { - ensureNoAbsorbWhileSqueezing(m_squeezing); - super.update(input, inOff, len); - } - - @Override - public int doOutput(byte[] output, int outOff, int outLen) - { - return hash(output, outOff, outLen); - } - - @Override - public int doFinal(byte[] output, int outOff, int outLen) - { - int rlt = doOutput(output, outOff, outLen); - reset(); - return rlt; - } - @Override public void reset() { - m_squeezing = false; super.reset(); /* initialize */ p.set(-2701369817892108309L, -3711838248891385495L, -1778763697082575311L, 1072114354614917324L, -2282070310009238562L); diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXofBase.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXofBase.java new file mode 100644 index 0000000000..f27de16070 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXofBase.java @@ -0,0 +1,106 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.Xof; + +abstract class AsconXofBase + extends AsconBaseDigest + implements Xof +{ + private boolean m_squeezing; + private final byte[] buffer = new byte[BlockSize]; + private int bytesInBuffer; + + @Override + public void update(byte in) + { + ensureNoAbsorbWhileSqueezing(m_squeezing); + super.update(in); + } + + @Override + public void update(byte[] input, int inOff, int len) + { + ensureNoAbsorbWhileSqueezing(m_squeezing); + super.update(input, inOff, len); + } + + @Override + public int doOutput(byte[] output, int outOff, int outLen) + { + ensureSufficientOutputBuffer(output, outOff, outLen); + + /* Use buffered output first */ + int bytesOutput = 0; + if (bytesInBuffer != 0) + { + int startPos = BlockSize - bytesInBuffer; + int bytesToOutput = Math.min(outLen, bytesInBuffer); + System.arraycopy(buffer, startPos, output, outOff, bytesToOutput); + bytesInBuffer -= bytesToOutput; + bytesOutput += bytesToOutput; + } + + int available = outLen - bytesOutput; + /* If we still need to output data */ + if (available >= BlockSize) + { + /* Output full blocks */ + int bytesToOutput = available - available % BlockSize; + bytesOutput += hash(output, outOff + bytesOutput, bytesToOutput); + } + + /* If we need to output a partial buffer */ + if (bytesOutput < outLen) + { + /* Access the next buffer's worth of data */ + hash(buffer, 0, BlockSize); + + /* Copy required length of data */ + int bytesToOutput = outLen - bytesOutput; + System.arraycopy(buffer, 0, output, outOff + bytesOutput, bytesToOutput); + bytesInBuffer = buffer.length - bytesToOutput; + bytesOutput += bytesToOutput; + } + + /* return the length of data output */ + return bytesOutput; + } + + @Override + public int doFinal(byte[] output, int outOff, int outLen) + { + int rlt = doOutput(output, outOff, outLen); + reset(); + return rlt; + } + + @Override + public void reset() + { + m_squeezing = false; + bytesInBuffer = 0; + super.reset(); + } + + @Override + protected void padAndAbsorb() + { + if (!m_squeezing) + { + m_squeezing = true; + super.padAndAbsorb(); + } + else + { + p.p(ASCON_PB_ROUNDS); + } + } + + private void ensureNoAbsorbWhileSqueezing(boolean m_squeezing) + { + if (m_squeezing) + { + throw new IllegalArgumentException("attempt to absorb while squeezing"); + } + } +} diff --git a/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java b/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java index 50374ee564..3e4b12bded 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java @@ -3,6 +3,7 @@ import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; +import java.security.SecureRandom; import java.util.HashMap; import java.util.Random; @@ -44,6 +45,11 @@ public String getName() public void performTest() throws Exception { + DigestTest.checkXof(new AsconXof128(), 1429, 317, new SecureRandom(), this); + DigestTest.checkXof(new AsconCXof128(), 1429, 317, new SecureRandom(), this); + DigestTest.checkXof(new AsconXof(AsconXof.AsconParameters.AsconXof), 1429, 317, new SecureRandom(), this); + DigestTest.checkXof(new AsconXof(AsconXof.AsconParameters.AsconXofA), 1429, 317, new SecureRandom(), this); + testVectorsEngine_asconaead128(); testVectorsDigest_AsconHash256(); testVectorsXof_AsconXof128(); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java index 1f41c56f7f..d06daacc24 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java @@ -11,6 +11,7 @@ import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.ExtendedDigest; import org.bouncycastle.crypto.OutputLengthException; +import org.bouncycastle.crypto.Xof; import org.bouncycastle.crypto.digests.EncodableDigest; import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.util.Arrays; @@ -367,4 +368,43 @@ private static void mismatch(SimpleTest test, String name, String expected, byte { test.fail("mismatch on " + name, expected, new String(Hex.encode(found))); } + + /** + * Check xof. + * + * @param pXof the xof + * @param DATALEN DataLength + * @param PARTIALLEN Partial length + */ + public static void checkXof(final Xof pXof, int DATALEN, int PARTIALLEN, SecureRandom random, SimpleTest test) + { + /* Create the data */ + final byte[] myData = new byte[DATALEN]; + random.nextBytes(myData); + + /* Update the Xof with the data */ + pXof.update(myData, 0, DATALEN); + + /* Extract Xof as single block */ + final byte[] myFull = new byte[DATALEN]; + pXof.doFinal(myFull, 0, DATALEN); + + /* Update the Xof with the data */ + pXof.update(myData, 0, DATALEN); + final byte[] myPart = new byte[DATALEN]; + + /* Create the xof as partial blocks */ + for (int myPos = 0; myPos < DATALEN; myPos += PARTIALLEN) + { + final int myLen = Math.min(PARTIALLEN, DATALEN - myPos); + pXof.doOutput(myPart, myPos, myLen); + } + pXof.doFinal(myPart, 0, 0); + + /* Check that they are identical */ + if (!Arrays.areEqual(myPart, myFull)) + { + test.fail(pXof.getAlgorithmName() + ": Mismatch on partial vs full xof"); + } + } } From 45c8e3f2200efda41f08cff6db433bb95e2041d0 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 19 Mar 2025 12:34:52 +1030 Subject: [PATCH 236/890] Add a test case for HQCTest --- .../pqc/jcajce/provider/test/HQCTest.java | 68 +++++++++++++++++-- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java index a818b59897..0ca80d282d 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java @@ -16,10 +16,14 @@ import org.bouncycastle.jcajce.spec.KEMExtractSpec; import org.bouncycastle.jcajce.spec.KEMGenerateSpec; import org.bouncycastle.jcajce.spec.KEMParameterSpec; +import org.bouncycastle.pqc.crypto.hqc.HQCKeyGenerationParameters; +import org.bouncycastle.pqc.crypto.hqc.HQCKeyPairGenerator; +import org.bouncycastle.pqc.crypto.hqc.HQCParameters; import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider; import org.bouncycastle.pqc.jcajce.spec.HQCParameterSpec; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.FixedSecureRandom; /** * KEM tests for HQC with the BCPQC provider. @@ -36,7 +40,7 @@ public void setUp() } public void testBasicKEMAES() - throws Exception + throws Exception { KeyPairGenerator kpg = KeyPairGenerator.getInstance("HQC", "BCPQC"); kpg.initialize(HQCParameterSpec.hqc128, new SecureRandom()); @@ -51,7 +55,7 @@ public void testBasicKEMAES() } public void testBasicKEMCamellia() - throws Exception + throws Exception { KeyPairGenerator kpg = KeyPairGenerator.getInstance("HQC", "BCPQC"); kpg.initialize(HQCParameterSpec.hqc128, new SecureRandom()); @@ -61,7 +65,7 @@ public void testBasicKEMCamellia() } public void testBasicKEMSEED() - throws Exception + throws Exception { KeyPairGenerator kpg = KeyPairGenerator.getInstance("HQC", "BCPQC"); kpg.initialize(HQCParameterSpec.hqc128, new SecureRandom()); @@ -70,7 +74,7 @@ public void testBasicKEMSEED() } public void testBasicKEMARIA() - throws Exception + throws Exception { KeyPairGenerator kpg = KeyPairGenerator.getInstance("HQC", "BCPQC"); kpg.initialize(HQCParameterSpec.hqc128, new SecureRandom()); @@ -80,7 +84,7 @@ public void testBasicKEMARIA() } private void performKEMScipher(KeyPair kp, String algorithm, KEMParameterSpec ktsParameterSpec) - throws Exception + throws Exception { Cipher w1 = Cipher.getInstance(algorithm, "BCPQC"); @@ -109,7 +113,7 @@ private void performKEMScipher(KeyPair kp, String algorithm, KEMParameterSpec kt } public void testGenerateAES() - throws Exception + throws Exception { KeyPairGenerator kpg = KeyPairGenerator.getInstance("HQC", "BCPQC"); kpg.initialize(HQCParameterSpec.hqc128, new SecureRandom()); @@ -135,7 +139,7 @@ public void testGenerateAES() } public void testGenerateAES256() - throws Exception + throws Exception { KeyPairGenerator kpg = KeyPairGenerator.getInstance("HQC", "BCPQC"); kpg.initialize(HQCParameterSpec.hqc256, new SecureRandom()); @@ -159,4 +163,54 @@ public void testGenerateAES256() assertTrue(Arrays.areEqual(secEnc1.getEncoded(), secEnc2.getEncoded())); } + + public static void main(String[] args) + throws Exception + { + HQCTest test = new HQCTest(); + + test.setUp(); + + byte[] seed = Hex.decode("416a32ada1c7a569c34d5334273a781c340aac25eb7614271aa6930d0358fb30fd87e111336a29e165dc60d9643a3e9b"); + byte[] kemSeed = Hex.decode("13f36c0636ff93af6d702f7774097c185bf67cddc9b09f9b584d736c4faf40e073b0499efa0c926e9a44fec1e45ee4cf"); + //HQCKeyPairGenerator kpg = new HQCKeyPairGenerator(); + //kpg.init(new HQCKeyGenerationParameters(); + KeyPairGenerator kpg = KeyPairGenerator.getInstance("HQC", "BCPQC"); + SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(seed)}); + SecureRandom kemRandom = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(kemSeed)}); + kpg.initialize(HQCParameterSpec.hqc128, random); + KeyPair kp = kpg.generateKeyPair(); + String algorithm = "HQC"; + KEMParameterSpec ktsParameterSpec = new KEMParameterSpec("ARIA-KWP"); + Cipher w1 = Cipher.getInstance(algorithm, "BCPQC"); + + byte[] keyBytes; + if (algorithm.endsWith("KWP")) + { + keyBytes = Hex.decode("000102030405060708090a0b0c0d0e0faa"); + } + else + { + keyBytes = Hex.decode("000102030405060708090a0b0c0d0e0f"); + } + SecretKey key = new SecretKeySpec(keyBytes, "AES"); + + w1.init(Cipher.WRAP_MODE, kp.getPublic(), ktsParameterSpec, kemRandom); + + byte[] data = w1.wrap(key); + + Cipher w2 = Cipher.getInstance(algorithm, "BCPQC"); + + w2.init(Cipher.UNWRAP_MODE, kp.getPrivate(), ktsParameterSpec); + + Key k = w2.unwrap(data, "AES", Cipher.SECRET_KEY); + + assertTrue(Arrays.areEqual(keyBytes, k.getEncoded())); +// for (int i = 0; i < 10000; ++i) +// { +// test.testBasicKEMARIA(); +// } + System.out.println("OK"); + } + } From 7b5f81f87f13e9dfac4f92259bdac87ed9244c96 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 19 Mar 2025 13:32:45 +1030 Subject: [PATCH 237/890] TODO finish copyTo in MapGroup1 and replace MapGroup1.encodeP in line 482 of SnoveEngine. --- .../java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java | 2 +- .../java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java | 2 ++ .../org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java | 2 +- .../bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java | 4 ---- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java index 0469a25989..539bd649fa 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java @@ -117,7 +117,7 @@ public static void encodeMergeInHalf(byte[] m, int mlen, byte[] menc) // If there is an extra nibble (odd number of nibbles), store it directly in lower 4 bits. if ((mlen & 1) == 1) { - menc[i] = (byte)m[i]; + menc[i] = m[i]; } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java index 18fcdcbab2..963867b182 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -74,6 +74,8 @@ private static int fillAlpha(byte[] input, int inOff, byte[][][] alpha, int len) return rlt; } + //private static void copyTo(byte[][][] al) + // static int decodeP(byte[] input, int inOff, byte[][][][] p, int len) // { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java index 026677ab92..18c3004338 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java @@ -6,7 +6,7 @@ class SnovaKeyElements public final byte[][][] T12; // [v][o] public final MapGroup2 map2; public final PublicKey publicKey; - private int length; + private final int length; public SnovaKeyElements(SnovaParameters params) { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index 45bfade6a9..f7125c8e03 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -137,10 +137,6 @@ private void genABQP(MapGroup1 map1, byte[] pkSeed) if (params.isPkExpandShake()) { snovaShake(pkSeed, prngOutput.length, prngOutput); - // SHAKE-based expansion -// SHAKEDigest shake = new SHAKEDigest(128); -// shake.update(pkSeed, 0, pkSeed.length); -// shake.doFinal(prngOutput, 0, prngOutput.length); } else { From 16ba11ac7207622a03f2c1186704b4c332c4f472 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 19 Mar 2025 15:32:28 +1030 Subject: [PATCH 238/890] Fix the bug in ReedSolomon --- .../bouncycastle/pqc/crypto/hqc/ReedSolomon.java | 3 +-- .../pqc/jcajce/provider/test/HQCTest.java | 13 ++----------- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/ReedSolomon.java b/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/ReedSolomon.java index 8da0b7035d..18457241d4 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/ReedSolomon.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/hqc/ReedSolomon.java @@ -191,8 +191,7 @@ private static void computeZx(int[] output, int[] sigma, int deg, int[] syndrome for (int i = 2; i <= delta; i++) { int mask = i - deg < 1 ? 0xffff : 0; - output[i] = mask & sigma[i - 1]; - + output[i] ^= (mask) & syndromes[i - 1]; for (int j = 1; j < i; j++) { output[i] ^= (mask) & GFCalculator.mult(sigma[j], syndromes[i - j - 1]); diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java index 0ca80d282d..c2471029e1 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java @@ -164,14 +164,10 @@ public void testGenerateAES256() assertTrue(Arrays.areEqual(secEnc1.getEncoded(), secEnc2.getEncoded())); } - public static void main(String[] args) + public void testReedSolomon() throws Exception { - HQCTest test = new HQCTest(); - - test.setUp(); - - byte[] seed = Hex.decode("416a32ada1c7a569c34d5334273a781c340aac25eb7614271aa6930d0358fb30fd87e111336a29e165dc60d9643a3e9b"); + byte[] seed = Hex.decode("416a32ada1c7a569c34d5334273a781c340aac25eb7614271aa6930d0358fb30fd87e111336a29e165dc60d9643a3e9b");//b byte[] kemSeed = Hex.decode("13f36c0636ff93af6d702f7774097c185bf67cddc9b09f9b584d736c4faf40e073b0499efa0c926e9a44fec1e45ee4cf"); //HQCKeyPairGenerator kpg = new HQCKeyPairGenerator(); //kpg.init(new HQCKeyGenerationParameters(); @@ -206,11 +202,6 @@ public static void main(String[] args) Key k = w2.unwrap(data, "AES", Cipher.SECRET_KEY); assertTrue(Arrays.areEqual(keyBytes, k.getEncoded())); -// for (int i = 0; i < 10000; ++i) -// { -// test.testBasicKEMARIA(); -// } - System.out.println("OK"); } } From 1afbabdda5451c38a6b34d6a7e9a6bad22ed2bde Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 19 Mar 2025 17:01:46 +1030 Subject: [PATCH 239/890] TODO: fix the bug for l = 3 --- .../bouncycastle/pqc/crypto/snova/MapGroup1.java | 16 +++++++++++++++- .../pqc/crypto/snova/SnovaEngine.java | 4 +++- .../bouncycastle/pqc/crypto/test/SnovaTest.java | 16 ++++++++-------- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java index 963867b182..c066be416f 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -74,7 +74,21 @@ private static int fillAlpha(byte[] input, int inOff, byte[][][] alpha, int len) return rlt; } - //private static void copyTo(byte[][][] al) + static void copyTo(byte[][][][] alpha, byte[] output) + { + int outOff = 0; + for (int i = 0; i < alpha.length; ++i) + { + for (int j = 0; j < alpha[i].length; ++j) + { + for (int k = 0; k < alpha[i][j].length; ++k) + { + System.arraycopy(alpha[i][j][k], 0, output, outOff, alpha[i][j][k].length); + outOff += alpha[i][j][k].length; + } + } + } + } // static int decodeP(byte[] input, int inOff, byte[][][][] p, int len) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index b598cfd4ae..80eb8bf2e5 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -479,7 +479,9 @@ public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] } // Convert GF16 elements to packed bytes - MapGroup1.encodeP(P22, outP22, 0, outP22.length); + byte[] tmp = new byte[outP22.length << 1]; + MapGroup1.copyTo(P22, tmp); + GF16Utils.encode(tmp, outP22, 0, tmp.length); } finally { diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java index 9ae35a34c4..3b98bcaa50 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java @@ -31,10 +31,10 @@ public static void main(String[] args) // SnovaParameters.SNOVA_24_5_16_4_SHAKE_ESK, // SnovaParameters.SNOVA_24_5_16_4_SHAKE_SSK, // SnovaParameters.SNOVA_24_5_16_4_SSK, - SnovaParameters.SNOVA_24_5_16_5_ESK, - SnovaParameters.SNOVA_24_5_16_5_SHAKE_ESK, - SnovaParameters.SNOVA_24_5_16_5_SHAKE_SSK, - SnovaParameters.SNOVA_24_5_16_5_SSK, +// SnovaParameters.SNOVA_24_5_16_5_ESK, +// SnovaParameters.SNOVA_24_5_16_5_SHAKE_ESK, +// SnovaParameters.SNOVA_24_5_16_5_SHAKE_SSK, +// SnovaParameters.SNOVA_24_5_16_5_SSK, SnovaParameters.SNOVA_25_8_16_3_ESK, SnovaParameters.SNOVA_25_8_16_3_SHAKE_ESK, SnovaParameters.SNOVA_25_8_16_3_SHAKE_SSK, @@ -78,10 +78,10 @@ public static void main(String[] args) // "PQCsignKAT_SNOVA_24_5_4_SHAKE_ESK.rsp", // "PQCsignKAT_SNOVA_24_5_4_SHAKE_SSK.rsp", // "PQCsignKAT_SNOVA_24_5_4_SSK.rsp", - "PQCsignKAT_SNOVA_24_5_5_ESK.rsp", - "PQCsignKAT_SNOVA_24_5_5_SHAKE_ESK.rsp", - "PQCsignKAT_SNOVA_24_5_5_SHAKE_SSK.rsp", - "PQCsignKAT_SNOVA_24_5_5_SSK.rsp", +// "PQCsignKAT_SNOVA_24_5_5_ESK.rsp", +// "PQCsignKAT_SNOVA_24_5_5_SHAKE_ESK.rsp", +// "PQCsignKAT_SNOVA_24_5_5_SHAKE_SSK.rsp", +// "PQCsignKAT_SNOVA_24_5_5_SSK.rsp", "PQCsignKAT_SNOVA_25_8_3_ESK.rsp", "PQCsignKAT_SNOVA_25_8_3_SHAKE_ESK.rsp", "PQCsignKAT_SNOVA_25_8_3_SHAKE_SSK.rsp", From aca4bd329865530f048715d6115f075e2df450d2 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 20 Mar 2025 09:48:01 +1030 Subject: [PATCH 240/890] Fix the AsconCXof128 based on #2032 code --- .../crypto/digests/AsconCXof128.java | 3 +- .../bouncycastle/crypto/test/AsconTest.java | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java index 70bc4ff572..78a42a9a65 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java @@ -82,8 +82,7 @@ public void reset() private void initState(byte[] z, int zOff, int zLen) { p.set(7445901275803737603L, 4886737088792722364L, -1616759365661982283L, 3076320316797452470L, -8124743304765850554L); - long bitLength = ((long)zLen) << 3; - Pack.longToLittleEndian(bitLength, m_buf, 0); + p.x0 ^= ((long)zLen) << 3; p.p(12); update(z, zOff, zLen); padAndAbsorb(); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java b/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java index 3e4b12bded..27f8e35fb1 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java @@ -45,6 +45,7 @@ public String getName() public void performTest() throws Exception { + testVectorsAsconCXof128_512(); DigestTest.checkXof(new AsconXof128(), 1429, 317, new SecureRandom(), this); DigestTest.checkXof(new AsconCXof128(), 1429, 317, new SecureRandom(), this); DigestTest.checkXof(new AsconXof(AsconXof.AsconParameters.AsconXof), 1429, 317, new SecureRandom(), this); @@ -520,6 +521,12 @@ public void testVectorsXof_AsconXof128() implTestVectorsXof(new AsconXof128(), "crypto/ascon/asconxof128", "LWC_HASH_KAT_256.txt"); } + public void testVectorsAsconCXof128_512() + throws Exception + { + implTestVectorsAsconCXof128(512 / 8, "crypto/ascon/asconcxof128", "LWC_CXOF_KAT_128_512.txt"); + } + public void testVectorsXof_AsconXof() throws Exception { @@ -1172,6 +1179,54 @@ private void implTestVectorsEngine(AEADCipher ascon, String path, String filenam } } + private void implTestVectorsAsconCXof128(int hash_length, String path, String filename) + throws Exception + { + Random random = new Random(); + + InputStream src = TestResourceFinder.findTestResource(path, filename); + BufferedReader bin = new BufferedReader(new InputStreamReader(src)); + String line; + HashMap map = new HashMap(); + while ((line = bin.readLine()) != null) + { + int a = line.indexOf('='); + if (a < 0) + { + byte[] zByte = Hex.decode((String)map.get("Z")); + byte[] ptByte = Hex.decode((String)map.get("Msg")); + byte[] expected = Hex.decode((String)map.get("MD")); + + byte[] hash = new byte[hash_length]; + + AsconCXof128 ascon = new AsconCXof128(zByte); + ascon.update(ptByte, 0, ptByte.length); + ascon.doFinal(hash, 0, hash_length); + if (!areEqual(hash, expected)) + { + mismatch("Keystream " + map.get("Count"), (String)map.get("MD"), hash); + } + + if (ptByte.length > 1) + { + int split = random.nextInt(ptByte.length - 1) + 1; + ascon = new AsconCXof128(zByte); + ascon.update(ptByte, 0, split); + ascon.update(ptByte, split, ptByte.length - split); + ascon.doFinal(hash, 0, hash_length); + if (!areEqual(hash, expected)) + { + mismatch("Keystream " + map.get("Count"), (String)map.get("MD"), hash); + } + } + } + else + { + map.put(line.substring(0, a).trim(), line.substring(a + 1).trim()); + } + } + } + private void implTestVectorsXof(Xof ascon, String path, String filename) throws Exception { From 8fe3792570e2400b112bd3ed4e368e6ea669f2dd Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 20 Mar 2025 17:26:52 +1030 Subject: [PATCH 241/890] TODO: fix the line 216 of SnovaKeyPairGenerator --- .../pqc/crypto/snova/MapGroup1.java | 50 ++--- .../pqc/crypto/snova/SnovaEngine.java | 204 ++++++++---------- .../pqc/crypto/snova/SnovaKeyElements.java | 41 +++- .../crypto/snova/SnovaKeyPairGenerator.java | 62 +++--- 4 files changed, 192 insertions(+), 165 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java index c066be416f..fa0ab86d92 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -59,7 +59,7 @@ static int fillP(byte[] input, int inOff, byte[][][][] p, int len) return rlt; } - private static int fillAlpha(byte[] input, int inOff, byte[][][] alpha, int len) + static int fillAlpha(byte[] input, int inOff, byte[][][] alpha, int len) { int rlt = 0; for (int i = 0; i < alpha.length; ++i) @@ -117,30 +117,30 @@ static void copyTo(byte[][][][] alpha, byte[] output) // return rlt; // } - static int encodeP(byte[][][][] p, byte[] output, int outOff, int len) - { - int rlt = 0; - for (int i = 0; i < p.length; ++i) - { - rlt += encodeAlpha(p[i], output, outOff + rlt, len - rlt); - } - return rlt; - } +// static int encodeP(byte[][][][] p, byte[] output, int outOff, int len) +// { +// int rlt = 0; +// for (int i = 0; i < p.length; ++i) +// { +// rlt += encodeAlpha(p[i], output, outOff + rlt, len - rlt); +// } +// return rlt; +// } - static int encodeAlpha(byte[][][] alpha, byte[] output, int outOff, int len) - { - int rlt = 0; - for (int i = 0; i < alpha.length; ++i) - { - for (int j = 0; j < alpha[i].length; ++j) - { - int tmp = Math.min(alpha[i][j].length, len << 1); - GF16Utils.encode(alpha[i][j], output, outOff + rlt, tmp); - rlt += (tmp + 1) >> 1; - len -= (tmp + 1) >> 1; - } - } - return rlt; - } +// static int encodeAlpha(byte[][][] alpha, byte[] output, int outOff, int len) +// { +// int rlt = 0; +// for (int i = 0; i < alpha.length; ++i) +// { +// for (int j = 0; j < alpha[i].length; ++j) +// { +// int tmp = Math.min(alpha[i][j].length, len << 1); +// GF16Utils.encode(alpha[i][j], output, outOff + rlt, tmp); +// rlt += (tmp + 1) >> 1; +// len -= (tmp + 1) >> 1; +// } +// } +// return rlt; +// } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 80eb8bf2e5..104c0a895e 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -17,7 +17,7 @@ public SnovaEngine(SnovaParameters params) this.lsq = l * l; S = new byte[l][lsq]; xS = new int[l][lsq]; - be_aI(S[0], (byte)1); + be_aI(S[0], 0, (byte)1); beTheS(S[1]); for (int index = 2; index < l; ++index) { @@ -43,7 +43,7 @@ public void setGF16m(byte[] gf16m, int x, int y, byte value) gf16m[x * l + y] = value; } - public void be_aI(byte[] target, byte a) + public void be_aI(byte[] target, int off, byte a) { // Mask 'a' to ensure it's a valid 4-bit GF16 element a = (byte)(a & 0x0F); @@ -52,7 +52,7 @@ public void be_aI(byte[] target, byte a) { for (int j = 0; j < l; ++j) { - int index = i * l + j; + int index = i * l + j + off; target[index] = (i == j) ? a : (byte)0; } } @@ -118,9 +118,9 @@ public void genAFqSCT(byte[] c, int cOff, byte[] ptMatrix) Arrays.fill(xTemp, 0); // Secure clear } - public void makeInvertibleByAddingAS(byte[] source) + public void makeInvertibleByAddingAS(byte[] source, int off) { - if (gf16Determinant(source) != 0) + if (gf16Determinant(source, off) != 0) { return; } @@ -131,160 +131,140 @@ public void makeInvertibleByAddingAS(byte[] source) for (int a = 1; a < 16; a++) { generateASMatrix(temp, (byte)a); - addMatrices(temp, source, source); + addMatrices(temp, 0, source, off, source, off); - if (gf16Determinant(source) != 0) + if (gf16Determinant(source, off) != 0) { return; } } - throw new IllegalStateException("Failed to make matrix invertible"); + //throw new IllegalStateException("Failed to make matrix invertible"); } - private byte gf16Determinant(byte[] matrix) + private byte gf16Determinant(byte[] matrix, int off) { switch (l) { case 2: - return determinant2x2(matrix); + return determinant2x2(matrix, off); case 3: - return determinant3x3(matrix); + return determinant3x3(matrix, off, 0, 1, 2, 0, 1, 2); case 4: - return determinant4x4(matrix); + return determinant4x4(matrix, off); case 5: - return determinant5x5(matrix); + return determinant5x5(matrix, off); default: throw new IllegalStateException(); } } - private byte determinant2x2(byte[] m) + private byte determinant2x2(byte[] m, int off) { return gf16Add( - gf16Mul(getGF16m(m, 0, 0), getGF16m(m, 1, 1)), - gf16Mul(getGF16m(m, 0, 1), getGF16m(m, 1, 0))); + gf16Mul(getGF16m(m, 0, off), getGF16m(m, 1, off + 1)), + gf16Mul(getGF16m(m, 0, off + 1), getGF16m(m, 1, off))); } - private byte determinant3x3(byte[] m) + private byte determinant3x3(byte[] m, int off, int i0, int i1, int i2, int j0, int j1, int j2) { return gf16Add( gf16Add( - gf16Mul(getGF16m(m, 0, 0), gf16Add( - gf16Mul(getGF16m(m, 1, 1), getGF16m(m, 2, 2)), - gf16Mul(getGF16m(m, 1, 2), getGF16m(m, 2, 1)) + gf16Mul(getGF16m(m, j0, off + i0), gf16Add( + gf16Mul(getGF16m(m, j1, off + i1), getGF16m(m, j2, off + i2)), + gf16Mul(getGF16m(m, j1, off + i2), getGF16m(m, j2, off + i1)) )), - gf16Mul(getGF16m(m, 0, 1), gf16Add( - gf16Mul(getGF16m(m, 1, 0), getGF16m(m, 2, 2)), - gf16Mul(getGF16m(m, 1, 2), getGF16m(m, 2, 0)) + gf16Mul(getGF16m(m, j0, off + i1), gf16Add( + gf16Mul(getGF16m(m, j1, off + i0), getGF16m(m, j2, off + i2)), + gf16Mul(getGF16m(m, j1, off + i2), getGF16m(m, j2, off + i0)) )) ), - gf16Mul(getGF16m(m, 0, 2), gf16Add( - gf16Mul(getGF16m(m, 1, 0), getGF16m(m, 2, 1)), - gf16Mul(getGF16m(m, 1, 1), getGF16m(m, 2, 0)) + gf16Mul(getGF16m(m, j0, off + i2), gf16Add( + gf16Mul(getGF16m(m, j1, off + i0), getGF16m(m, j2, off + i1)), + gf16Mul(getGF16m(m, j1, off + i1), getGF16m(m, j2, off + i0)) )) ); } - private byte determinant3x3(byte[] m, int i0, int i1, int i2, int j0, int j1, int j2) + private byte determinant4x4(byte[] m, int off) { - return gf16Add( - gf16Add( - gf16Mul(getGF16m(m, j0, i0), gf16Add( - gf16Mul(getGF16m(m, j1, i1), getGF16m(m, j2, i2)), - gf16Mul(getGF16m(m, j1, i2), getGF16m(m, j2, i1)) - )), - gf16Mul(getGF16m(m, j0, i1), gf16Add( - gf16Mul(getGF16m(m, j1, i0), getGF16m(m, j2, i2)), - gf16Mul(getGF16m(m, j1, i2), getGF16m(m, j2, i0)) - )) - ), - gf16Mul(getGF16m(m, j0, i2), gf16Add( - gf16Mul(getGF16m(m, j1, i0), getGF16m(m, j2, i1)), - gf16Mul(getGF16m(m, j1, i1), getGF16m(m, j2, i0)) - )) - ); - } - - private byte determinant4x4(byte[] m) - { - byte d0 = gf16Mul(getGF16m(m, 0, 0), gf16Add( + byte d0 = gf16Mul(getGF16m(m, 0, off), gf16Add( gf16Add( - pod(m, 1, 1, 2, 2, 3, 3, 2, 3, 3, 2), - pod(m, 1, 2, 2, 1, 3, 3, 2, 3, 3, 1) + pod(m, off, 1, 1, 2, 2, 3, 3, 2, 3, 3, 2), + pod(m, off, 1, 2, 2, 1, 3, 3, 2, 3, 3, 1) ), - pod(m, 1, 3, 2, 1, 3, 2, 2, 2, 3, 1) + pod(m, off, 1, 3, 2, 1, 3, 2, 2, 2, 3, 1) )); - byte d1 = gf16Mul(getGF16m(m, 0, 1), gf16Add( + byte d1 = gf16Mul(getGF16m(m, 0, off + 1), gf16Add( gf16Add( - pod(m, 1, 0, 2, 2, 3, 3, 2, 3, 3, 2), - pod(m, 1, 2, 2, 0, 3, 3, 2, 3, 3, 0) + pod(m, off, 1, 0, 2, 2, 3, 3, 2, 3, 3, 2), + pod(m, off, 1, 2, 2, 0, 3, 3, 2, 3, 3, 0) ), - pod(m, 1, 3, 2, 0, 3, 2, 2, 2, 3, 0) + pod(m, off, 1, 3, 2, 0, 3, 2, 2, 2, 3, 0) )); - byte d2 = gf16Mul(getGF16m(m, 0, 2), gf16Add( + byte d2 = gf16Mul(getGF16m(m, 0, off + 2), gf16Add( gf16Add( - pod(m, 1, 0, 2, 1, 3, 3, 2, 3, 3, 1), - pod(m, 1, 1, 2, 0, 3, 3, 2, 3, 3, 0) + pod(m, off, 1, 0, 2, 1, 3, 3, 2, 3, 3, 1), + pod(m, off, 1, 1, 2, 0, 3, 3, 2, 3, 3, 0) ), - pod(m, 1, 3, 2, 0, 3, 1, 2, 1, 3, 0) + pod(m, off, 1, 3, 2, 0, 3, 1, 2, 1, 3, 0) )); - byte d3 = gf16Mul(getGF16m(m, 0, 3), gf16Add( + byte d3 = gf16Mul(getGF16m(m, 0, off + 3), gf16Add( gf16Add( - pod(m, 1, 0, 2, 1, 3, 2, 2, 2, 3, 1), - pod(m, 1, 1, 2, 0, 3, 2, 2, 2, 3, 0) + pod(m, off, 1, 0, 2, 1, 3, 2, 2, 2, 3, 1), + pod(m, off, 1, 1, 2, 0, 3, 2, 2, 2, 3, 0) ), - pod(m, 1, 2, 2, 0, 3, 1, 2, 1, 3, 0) + pod(m, off, 1, 2, 2, 0, 3, 1, 2, 1, 3, 0) )); return (byte)(d0 ^ d1 ^ d2 ^ d3); } - private byte determinant5x5(byte[] m) + private byte determinant5x5(byte[] m, int off) { - byte result = gf16Mul(determinant3x3(m, 0, 1, 2, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3,3), getGF16m(m, 4,4)), gf16Mul(getGF16m(m, 3,4),getGF16m(m, 4,3)))); - result ^= gf16Mul(determinant3x3(m, 0, 1, 3, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3,2), getGF16m(m, 4,4)), gf16Mul(getGF16m(m, 3,4),getGF16m(m, 4,2)))); - result ^= gf16Mul(determinant3x3(m, 0, 1, 4, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3,2), getGF16m(m, 4,3)), gf16Mul(getGF16m(m, 3,3),getGF16m(m, 4,2)))); - result ^= gf16Mul(determinant3x3(m, 0, 2, 3, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3,1), getGF16m(m, 4,4)), gf16Mul(getGF16m(m, 3,4),getGF16m(m, 4,1)))); - result ^= gf16Mul(determinant3x3(m, 0, 2, 4, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3,1), getGF16m(m, 4,3)), gf16Mul(getGF16m(m, 3,3),getGF16m(m, 4,1)))); - result ^= gf16Mul(determinant3x3(m, 0, 3, 4, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3,1), getGF16m(m, 4,2)), gf16Mul(getGF16m(m, 3,2),getGF16m(m, 4,1)))); - result ^= gf16Mul(determinant3x3(m, 1, 2, 3, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3,0), getGF16m(m, 4,4)), gf16Mul(getGF16m(m, 3,4),getGF16m(m, 4,0)))); - result ^= gf16Mul(determinant3x3(m, 1, 2, 4, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3,0), getGF16m(m, 4,3)), gf16Mul(getGF16m(m, 3,3),getGF16m(m, 4,0)))); - result ^= gf16Mul(determinant3x3(m, 1, 3, 4, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3,0), getGF16m(m, 4,2)), gf16Mul(getGF16m(m, 3,2),getGF16m(m, 4,0)))); - result ^= gf16Mul(determinant3x3(m, 2, 3, 4, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3,0), getGF16m(m, 4,1)), gf16Mul(getGF16m(m, 3,1),getGF16m(m, 4,0)))); + byte result = gf16Mul(determinant3x3(m, off, 0, 1, 2, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off + 4)), gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 3)))); + result ^= gf16Mul(determinant3x3(m, off, 0, 1, 3, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off + 4)), gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 2)))); + result ^= gf16Mul(determinant3x3(m, off, 0, 1, 4, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off + 3)), gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off + 2)))); + result ^= gf16Mul(determinant3x3(m, off, 0, 2, 3, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3,off + 1), getGF16m(m, 4,off + 4)), gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4,off + 1)))); + result ^= gf16Mul(determinant3x3(m, off, 0, 2, 4, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 3)), gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4,off + 1)))); + result ^= gf16Mul(determinant3x3(m, off, 0, 3, 4, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 2)), gf16Mul(getGF16m(m, 3,off + 2), getGF16m(m, 4, off + 1)))); + result ^= gf16Mul(determinant3x3(m, off, 1, 2, 3, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3, off + 0), getGF16m(m, 4, off + 4)), gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 0)))); + result ^= gf16Mul(determinant3x3(m, off, 1, 2, 4, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3, off + 0), getGF16m(m, 4, off + 3)), gf16Mul(getGF16m(m, 3,off + 3), getGF16m(m, 4, off + 0)))); + result ^= gf16Mul(determinant3x3(m, off, 1, 3, 4, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3, off + 0), getGF16m(m, 4, off + 2)), gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4,off + 0)))); + result ^= gf16Mul(determinant3x3(m, off, 2, 3, 4, 0, 1, 2), + gf16Add(gf16Mul(getGF16m(m, 3, off + 0), getGF16m(m, 4, off + 1)), gf16Mul(getGF16m(m, 3,off + 1), getGF16m(m, 4,off + 0)))); // return result; - byte a012 = determinant3x3(m, 0, 1, 2, 0, 1, 2); - byte b012 = gf16Add(gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 3))); - byte a013 = determinant3x3(m, 0, 1, 3, 0, 1, 2); - byte b013 = gf16Add(gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 2))); - byte a014 = determinant3x3(m, 0, 1, 4, 0, 1, 2); - byte b014 = gf16Add(gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 3)), gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 2))); - byte a023 = determinant3x3(m, 0, 2, 3, 0, 1, 2); - byte b023 = gf16Add(gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 1))); - byte a024 = determinant3x3(m, 0, 2, 4, 0, 1, 2); - byte b024 = gf16Add(gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 3)), gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 1))); - byte a034 = determinant3x3(m, 0, 3, 4, 0, 1, 2); - byte b034 = gf16Add(gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 2)), gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 1))); - byte a123 = determinant3x3(m, 1, 2, 3, 0, 1, 2); - byte b123 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 0))); - byte a124 = determinant3x3(m, 1, 2, 4, 0, 1, 2); - byte b124 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 3)), gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 0))); - byte a134 = determinant3x3(m, 1, 3, 4, 0, 1, 2); - byte b134 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 2)), gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 0))); - byte a234 = determinant3x3(m, 2, 3, 4, 0, 1, 2); - byte b234 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 1)), gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 0))); +// byte a012 = determinant3x3(m, 0, 1, 2, 0, 1, 2); +// byte b012 = gf16Add(gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 3))); +// byte a013 = determinant3x3(m, 0, 1, 3, 0, 1, 2); +// byte b013 = gf16Add(gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 2))); +// byte a014 = determinant3x3(m, 0, 1, 4, 0, 1, 2); +// byte b014 = gf16Add(gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 3)), gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 2))); +// byte a023 = determinant3x3(m, 0, 2, 3, 0, 1, 2); +// byte b023 = gf16Add(gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 1))); +// byte a024 = determinant3x3(m, 0, 2, 4, 0, 1, 2); +// byte b024 = gf16Add(gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 3)), gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 1))); +// byte a034 = determinant3x3(m, 0, 3, 4, 0, 1, 2); +// byte b034 = gf16Add(gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 2)), gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 1))); +// byte a123 = determinant3x3(m, 1, 2, 3, 0, 1, 2); +// byte b123 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 0))); +// byte a124 = determinant3x3(m, 1, 2, 4, 0, 1, 2); +// byte b124 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 3)), gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 0))); +// byte a134 = determinant3x3(m, 1, 3, 4, 0, 1, 2); +// byte b134 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 2)), gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 0))); +// byte a234 = determinant3x3(m, 2, 3, 4, 0, 1, 2); +// byte b234 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 1)), gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 0))); return result; } @@ -305,18 +285,18 @@ private void generateASMatrix(byte[] target, byte a) } // POD -> entry[a][b] * (entry[c][d] * entry[e][f] + entry[g][h] * entry[i][j]) - private byte pod(byte[] m, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) + private byte pod(byte[] m, int off, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) { - return gf16Mul(getGF16m(m, a, b), (byte)(gf16Mul(getGF16m(m, c, d), getGF16m(m, e, f)) ^ gf16Mul(getGF16m(m, g, h), getGF16m(m, i, j)))); + return gf16Mul(getGF16m(m, a, off + b), (byte)(gf16Mul(getGF16m(m, c, off + d), getGF16m(m, e, off + f)) ^ gf16Mul(getGF16m(m, g, off + h), getGF16m(m, i, off + j)))); } - private void addMatrices(byte[] a, byte[] b, byte[] c) + private void addMatrices(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int cOff) { for (int i = 0; i < l; i++) { for (int j = 0; j < l; j++) { - setGF16m(c, i, j, gf16Add(getGF16m(a, i, j), getGF16m(b, i, j))); + setGF16m(c, i, cOff + j, gf16Add(getGF16m(a, i, aOff + j), getGF16m(b, i, bOff + j))); } } } @@ -333,24 +313,24 @@ private static byte gf16Mul(byte a, byte b) return GF16Utils.mul(a, b); } - public void genAFqS(byte[] c, int cOff, byte[] ptMatrix) + public void genAFqS(byte[] c, int cOff, byte[] ptMatrix, int off) { byte[] temp = new byte[l * l]; // Initialize with be_aI - be_aI(ptMatrix, c[cOff]); + be_aI(ptMatrix, off, c[cOff]); // Process middle terms for (int i = 1; i < l - 1; ++i) { gf16mScale(S[i], c[cOff + i], temp); - addMatrices(ptMatrix, temp, ptMatrix); + addMatrices(ptMatrix, off, temp, 0, ptMatrix, off); } // Handle last term with special case byte lastScalar = (byte)((c[cOff + l - 1] != 0) ? c[cOff + l - 1] : 16 - (c[cOff] + (c[cOff] == 0 ? 1 : 0))); gf16mScale(S[l - 1], lastScalar, temp); - addMatrices(ptMatrix, temp, ptMatrix); + addMatrices(ptMatrix, off, temp, 0, ptMatrix, off); // Clear temporary matrix //clearMatrix(temp); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java index 18c3004338..69fd8abba7 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java @@ -1,5 +1,7 @@ package org.bouncycastle.pqc.crypto.snova; +import org.bouncycastle.crypto.digests.SHAKEDigest; + class SnovaKeyElements { public final MapGroup1 map1; @@ -7,18 +9,55 @@ class SnovaKeyElements public final MapGroup2 map2; public final PublicKey publicKey; private final int length; + byte[] fixedAbq; - public SnovaKeyElements(SnovaParameters params) + public SnovaKeyElements(SnovaParameters params, SnovaEngine engine) { int o = params.getO(); int l = params.getL(); int v = params.getV(); + int alpha = params.getAlpha(); int lsq = l * l; map1 = new MapGroup1(params); T12 = new byte[v][o][lsq]; map2 = new MapGroup2(params); publicKey = new PublicKey(params); length = o * params.getAlpha() * lsq * 4 + v * o * lsq + (o * v * v + o * v * o + o * o * v) * lsq; + if (l < 4) + { + fixedAbq = new byte[4 * o * alpha * lsq]; + //genABQ(byte[] abqSeed) + byte[] rngOut = new byte[o * alpha * (lsq + l)]; + byte[] q12 = new byte[2 * o * alpha * l]; + byte[] seed = "SNOVA_ABQ".getBytes(); + SHAKEDigest shake = new SHAKEDigest(256); + shake.update(seed, 0, seed.length); + shake.doFinal(rngOut, 0, rngOut.length); + GF16Utils.decode(rngOut, fixedAbq, 2 * o * alpha * lsq); + GF16Utils.decode(rngOut, alpha * lsq, q12, 2 * o * alpha * l); + // Post-processing for invertible matrices + for (int pi = 0; pi < o; ++pi) + { + for (int a = 0; a < alpha; ++a) + { + engine.makeInvertibleByAddingAS(fixedAbq, (pi * alpha + a) * lsq); + } + for (int a = 0; a < alpha; ++a) + { + engine.makeInvertibleByAddingAS(fixedAbq, ((o + pi) * alpha + a) * lsq); + } + + for (int a = 0; a < alpha; ++a) + { + engine.genAFqS(q12, (pi * alpha + a) * l, fixedAbq, ((2 * o + pi) * alpha + a) * lsq); + } + + for (int a = 0; a < alpha; ++a) + { + engine.genAFqS(q12, ((o + pi) * alpha + a) * l, fixedAbq, ((3 * o + pi) * alpha + a) * lsq); + } + } + } } public void encodeMergerInHalf(byte[] output) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index f7125c8e03..f2647d7c1a 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -53,7 +53,7 @@ public AsymmetricCipherKeyPair generateKeyPair() byte[] ptPublicKeySeed = Arrays.copyOfRange(seedPair, 0, publicSeedLength); byte[] ptPrivateKeySeed = Arrays.copyOfRange(seedPair, publicSeedLength, seedPair.length); - SnovaKeyElements keyElements = new SnovaKeyElements(params); + SnovaKeyElements keyElements = new SnovaKeyElements(params, engine); generateKeysCore(keyElements, ptPublicKeySeed, ptPrivateKeySeed); // Pack public key components @@ -82,7 +82,7 @@ private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[ genSeedsAndT12(keyElements.T12, skSeed); // Generate map components - genABQP(keyElements.map1, pkSeed); + genABQP(keyElements.map1, pkSeed, keyElements.fixedAbq); // Generate F matrices engine.genF(keyElements.map2, keyElements.map1, keyElements.T12); @@ -120,7 +120,7 @@ private void genSeedsAndT12(byte[][][] T12, byte[] skSeed) } } - private void genABQP(MapGroup1 map1, byte[] pkSeed) + private void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) { int l = params.getL(); int lsq = l * l; @@ -172,41 +172,49 @@ private void genABQP(MapGroup1 map1, byte[] pkSeed) byte[] temp = new byte[gf16sPrngPublic - qTemp.length]; GF16Utils.decode(prngOutput, temp, temp.length); map1.fill(temp); - GF16Utils.decode(prngOutput, temp.length >> 1, qTemp, 0, qTemp.length); - - // Post-processing for invertible matrices - for (int pi = 0; pi < m; ++pi) + if (l >= 4) { - for (int a = 0; a < alpha; ++a) + GF16Utils.decode(prngOutput, temp.length >> 1, qTemp, 0, qTemp.length); + + // Post-processing for invertible matrices + for (int pi = 0; pi < m; ++pi) { - engine.makeInvertibleByAddingAS(map1.aAlpha[pi][a]); + for (int a = 0; a < alpha; ++a) + { + engine.makeInvertibleByAddingAS(map1.aAlpha[pi][a], 0); + } } - } - for (int pi = 0; pi < m; ++pi) - { - for (int a = 0; a < alpha; ++a) + for (int pi = 0; pi < m; ++pi) { - engine.makeInvertibleByAddingAS(map1.bAlpha[pi][a]); + for (int a = 0; a < alpha; ++a) + { + engine.makeInvertibleByAddingAS(map1.bAlpha[pi][a], 0); + } } - } - int ptArray = 0; - for (int pi = 0; pi < m; ++pi) - { - for (int a = 0; a < alpha; ++a) + int ptArray = 0; + for (int pi = 0; pi < m; ++pi) { - engine.genAFqS(qTemp, ptArray, map1.qAlpha1[pi][a]); - ptArray += l; + for (int a = 0; a < alpha; ++a) + { + engine.genAFqS(qTemp, ptArray, map1.qAlpha1[pi][a], 0); + ptArray += l; + } } - } - for (int pi = 0; pi < m; ++pi) - { - for (int a = 0; a < alpha; ++a) + for (int pi = 0; pi < m; ++pi) { - engine.genAFqS(qTemp, ptArray, map1.qAlpha2[pi][a]); - ptArray += l; + for (int a = 0; a < alpha; ++a) + { + engine.genAFqS(qTemp, ptArray, map1.qAlpha2[pi][a], 0); + ptArray += l; + } } } + else + { + //TODO: fixedAbq fill more than aAlpha. bAlpha should be filled as well + MapGroup1.fillAlpha(fixedAbq, 0, map1.aAlpha, m * o * alpha * lsq); + } } public static void snovaShake(byte[] ptSeed, int outputBytes, byte[] out) From 1752dbf265c16e75420dc13b2fc4bf93e7d2a030 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 20 Mar 2025 18:21:57 +0700 Subject: [PATCH 242/890] CMS: Basic tests for ML-DSA SignedData examples --- .../bouncycastle/cms/SignerInformation.java | 2 + .../org/bouncycastle/cert/test/AllTests.java | 16 ++- .../cert/test/MLDSACredentialsTest.java | 41 ++++++++ .../cert/test/SampleCredentials.java | 98 +++++++++++++++++++ .../cms/test/NewSignedDataTest.java | 64 ++++++++++++ 5 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 pkix/src/test/java/org/bouncycastle/cert/test/MLDSACredentialsTest.java create mode 100644 pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java diff --git a/pkix/src/main/java/org/bouncycastle/cms/SignerInformation.java b/pkix/src/main/java/org/bouncycastle/cms/SignerInformation.java index b2451b5262..3fdf3ff3bf 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/SignerInformation.java +++ b/pkix/src/main/java/org/bouncycastle/cms/SignerInformation.java @@ -341,6 +341,8 @@ private boolean doVerify( SignerInformationVerifier verifier) throws CMSException { + // TODO[cms] For pure signature algorithms, restrict digest algorithm to permitted set + String encName = CMSSignedHelper.INSTANCE.getEncryptionAlgName(this.getEncryptionAlgOID()); AlgorithmIdentifier realDigestAlgorithm = signedAttributeSet != null ? info.getDigestAlgorithm() : translateBrokenRSAPkcs7(encryptionAlgorithm, info.getDigestAlgorithm()); diff --git a/pkix/src/test/java/org/bouncycastle/cert/test/AllTests.java b/pkix/src/test/java/org/bouncycastle/cert/test/AllTests.java index 39b7bdaeb5..f39effdbb5 100644 --- a/pkix/src/test/java/org/bouncycastle/cert/test/AllTests.java +++ b/pkix/src/test/java/org/bouncycastle/cert/test/AllTests.java @@ -23,8 +23,20 @@ public void setUp() public void testSimpleTests() { - org.bouncycastle.util.test.Test[] tests = new org.bouncycastle.util.test.Test[] { new CertTest(), new DANETest(), new PKCS10Test(), new AttrCertSelectorTest(), new AttrCertTest(), new X509ExtensionUtilsTest(), - new CertPathLoopTest(), new GOST3410_2012CMSTest(), new ExternalKeyTest() }; + org.bouncycastle.util.test.Test[] tests = new org.bouncycastle.util.test.Test[] + { + new AttrCertSelectorTest(), + new AttrCertTest(), + new CertPathLoopTest(), + new CertTest(), + new DANETest(), + new ExternalKeyTest(), + new GOST3410_2012CMSTest(), + new GOSTR3410_2012_256GenerateCertificate(), + new MLDSACredentialsTest(), + new PKCS10Test(), + new X509ExtensionUtilsTest(), + }; for (int i = 0; i != tests.length; i++) { diff --git a/pkix/src/test/java/org/bouncycastle/cert/test/MLDSACredentialsTest.java b/pkix/src/test/java/org/bouncycastle/cert/test/MLDSACredentialsTest.java new file mode 100644 index 0000000000..09a3f70f38 --- /dev/null +++ b/pkix/src/test/java/org/bouncycastle/cert/test/MLDSACredentialsTest.java @@ -0,0 +1,41 @@ +package org.bouncycastle.cert.test; + +import java.security.GeneralSecurityException; +import java.security.PublicKey; +import java.security.Security; +import java.security.cert.X509Certificate; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.test.SimpleTest; + +public class MLDSACredentialsTest + extends SimpleTest +{ + public String getName() + { + return "MLDSACredentials"; + } + + public void performTest() + throws Exception + { + checkSampleCredentials(SampleCredentials.ML_DSA_44); + checkSampleCredentials(SampleCredentials.ML_DSA_65); + checkSampleCredentials(SampleCredentials.ML_DSA_87); + } + + private static void checkSampleCredentials(SampleCredentials creds) + throws GeneralSecurityException + { + X509Certificate cert = creds.getCertificate(); + PublicKey pubKey = cert.getPublicKey(); + cert.verify(pubKey, BouncyCastleProvider.PROVIDER_NAME); + } + + public static void main(String[] args) + { + Security.addProvider(new BouncyCastleProvider()); + + runTest(new MLDSACredentialsTest()); + } +} diff --git a/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java b/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java new file mode 100644 index 0000000000..3df74bbcbf --- /dev/null +++ b/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java @@ -0,0 +1,98 @@ +package org.bouncycastle.cert.test; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Security; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.test.TestResourceFinder; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; +import org.junit.Assert; + +public class SampleCredentials +{ + public static final SampleCredentials ML_DSA_44 = load("ML-DSA-44", "pkix/cert/mldsa", "ML-DSA-44.pem"); + public static final SampleCredentials ML_DSA_65 = load("ML-DSA-65", "pkix/cert/mldsa", "ML-DSA-65.pem"); + public static final SampleCredentials ML_DSA_87 = load("ML-DSA-87", "pkix/cert/mldsa", "ML-DSA-87.pem"); + + private static PemObject expectPemObject(PemReader pemReader, String type) + throws IOException + { + PemObject result = pemReader.readPemObject(); + if (!type.equals(result.getType())) + { + throw new IllegalStateException(); + } + return result; + } + + private static SampleCredentials load(String algorithm, String path, String name) + { + try + { + if (Security.getProvider("BC") == null) + { + Security.addProvider(new BouncyCastleProvider()); + } + + InputStream input = new BufferedInputStream(TestResourceFinder.findTestResource(path, name)); + Reader reader = new InputStreamReader(input); + + PemReader pemReader = new PemReader(reader); + PemObject pemPub = expectPemObject(pemReader, "PRIVATE KEY"); + PemObject pemPriv = expectPemObject(pemReader, "PUBLIC KEY"); + PemObject pemCert = expectPemObject(pemReader, "CERTIFICATE"); + pemReader.close(); + + KeyFactory kf = KeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME); + CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); + + PrivateKey privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(pemPub.getContent())); + PublicKey publicKey = kf.generatePublic(new X509EncodedKeySpec(pemPriv.getContent())); + KeyPair keyPair = new KeyPair(publicKey, privateKey); + + X509Certificate certificate = (X509Certificate)cf.generateCertificate( + new ByteArrayInputStream(pemCert.getContent())); + + Assert.assertEquals(publicKey, certificate.getPublicKey()); + + return new SampleCredentials(keyPair, certificate); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + private final KeyPair keyPair; + private final X509Certificate certificate; + + private SampleCredentials(KeyPair keyPair, X509Certificate certificate) + { + this.keyPair = keyPair; + this.certificate = certificate; + } + + public X509Certificate getCertificate() + { + return certificate; + } + + public KeyPair getKeyPair() + { + return keyPair; + } +} diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java index 72fab92b54..6e45de7d2c 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java @@ -1,8 +1,12 @@ package org.bouncycastle.cms.test; +import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStream; +import java.io.Reader; import java.security.KeyFactory; import java.security.KeyPair; import java.security.MessageDigest; @@ -68,6 +72,7 @@ import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter; import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; import org.bouncycastle.cert.ocsp.OCSPResp; +import org.bouncycastle.cert.test.SampleCredentials; import org.bouncycastle.cms.CMSAbsentContent; import org.bouncycastle.cms.CMSAlgorithm; import org.bouncycastle.cms.CMSAttributeTableGenerationException; @@ -106,10 +111,13 @@ import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.util.CollectionStore; import org.bouncycastle.util.Store; import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.io.Streams; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; public class NewSignedDataTest extends TestCase @@ -689,6 +697,29 @@ public class NewSignedDataTest "CiwhMCLDeeEBOdxWZHVbIiFnnRTQqyIDGAOSSIUmjE/pMPKpPvumkCGq2r9GxPV9\n" + "YlpnThaYbDCnWg8tbWYAAAAAAAA="); + private static byte[] signedData_mldsa44 = loadPemContents("pkix/cms/mldsa", "SignedData_ML-DSA-44.pem"); + private static byte[] signedData_mldsa65 = loadPemContents("pkix/cms/mldsa", "SignedData_ML-DSA-65.pem"); + private static byte[] signedData_mldsa87 = loadPemContents("pkix/cms/mldsa", "SignedData_ML-DSA-87.pem"); + + private static byte[] loadPemContents(String path, String name) + { + try + { + InputStream input = new BufferedInputStream(TestResourceFinder.findTestResource(path, name)); + Reader reader = new InputStreamReader(input); + + PemReader pemReader = new PemReader(reader); + PemObject pemObject = pemReader.readPemObject(); + pemReader.close(); + + return pemObject.getContent(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + static { noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA1); @@ -3447,6 +3478,24 @@ public void testForMultipleCounterSignatures() } } + public void testVerifySignedDataMLDsa44() + throws Exception + { + implTestVerifySignedData(signedData_mldsa44, SampleCredentials.ML_DSA_44); + } + + public void testVerifySignedDataMLDsa65() + throws Exception + { + implTestVerifySignedData(signedData_mldsa65, SampleCredentials.ML_DSA_65); + } + + public void testVerifySignedDataMLDsa87() + throws Exception + { + implTestVerifySignedData(signedData_mldsa87, SampleCredentials.ML_DSA_87); + } + private void verifySignatures(CMSSignedDataParser sp) throws Exception { @@ -3468,6 +3517,21 @@ private void verifySignatures(CMSSignedDataParser sp) } } + private static void implTestVerifySignedData(byte[] signedData, SampleCredentials credentials) + throws Exception + { + CMSSignedData sd = new CMSSignedData(signedData); + + assertTrue(sd.verifySignatures(new SignerInformationVerifierProvider() + { + public SignerInformationVerifier get(SignerId signerId) + throws OperatorCreationException + { + return new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC).build(credentials.getCertificate()); + } + })); + } + private static class TestCMSSignatureAlgorithmNameGenerator extends DefaultCMSSignatureAlgorithmNameGenerator { From 214991ddd57e6db5e625d545eca1e246e3b67061 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 21 Mar 2025 09:39:43 +1030 Subject: [PATCH 243/890] Pass key generation of Snova. --- .../crypto/snova/SnovaKeyPairGenerator.java | 4 ++- .../pqc/crypto/test/SnovaTest.java | 32 +++++++++---------- 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index f2647d7c1a..13f0f779c2 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -212,8 +212,10 @@ private void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) } else { - //TODO: fixedAbq fill more than aAlpha. bAlpha should be filled as well MapGroup1.fillAlpha(fixedAbq, 0, map1.aAlpha, m * o * alpha * lsq); + MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq, map1.bAlpha, (m - 1) * o * alpha * lsq); + MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq * 2, map1.qAlpha1, (m - 2) * o * alpha * lsq); + MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq * 3, map1.qAlpha2, (m - 3) * o * alpha * lsq); } } diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java index 3b98bcaa50..995ce8e0e7 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java @@ -27,14 +27,14 @@ public static void main(String[] args) private static final SnovaParameters[] PARAMETER_SETS = new SnovaParameters[] { -// SnovaParameters.SNOVA_24_5_16_4_ESK, -// SnovaParameters.SNOVA_24_5_16_4_SHAKE_ESK, -// SnovaParameters.SNOVA_24_5_16_4_SHAKE_SSK, -// SnovaParameters.SNOVA_24_5_16_4_SSK, -// SnovaParameters.SNOVA_24_5_16_5_ESK, -// SnovaParameters.SNOVA_24_5_16_5_SHAKE_ESK, -// SnovaParameters.SNOVA_24_5_16_5_SHAKE_SSK, -// SnovaParameters.SNOVA_24_5_16_5_SSK, + SnovaParameters.SNOVA_24_5_16_4_ESK, + SnovaParameters.SNOVA_24_5_16_4_SHAKE_ESK, + SnovaParameters.SNOVA_24_5_16_4_SHAKE_SSK, + SnovaParameters.SNOVA_24_5_16_4_SSK, + SnovaParameters.SNOVA_24_5_16_5_ESK, + SnovaParameters.SNOVA_24_5_16_5_SHAKE_ESK, + SnovaParameters.SNOVA_24_5_16_5_SHAKE_SSK, + SnovaParameters.SNOVA_24_5_16_5_SSK, SnovaParameters.SNOVA_25_8_16_3_ESK, SnovaParameters.SNOVA_25_8_16_3_SHAKE_ESK, SnovaParameters.SNOVA_25_8_16_3_SHAKE_SSK, @@ -74,14 +74,14 @@ public static void main(String[] args) }; private static final String[] files = new String[]{ -// "PQCsignKAT_SNOVA_24_5_4_ESK.rsp", -// "PQCsignKAT_SNOVA_24_5_4_SHAKE_ESK.rsp", -// "PQCsignKAT_SNOVA_24_5_4_SHAKE_SSK.rsp", -// "PQCsignKAT_SNOVA_24_5_4_SSK.rsp", -// "PQCsignKAT_SNOVA_24_5_5_ESK.rsp", -// "PQCsignKAT_SNOVA_24_5_5_SHAKE_ESK.rsp", -// "PQCsignKAT_SNOVA_24_5_5_SHAKE_SSK.rsp", -// "PQCsignKAT_SNOVA_24_5_5_SSK.rsp", + "PQCsignKAT_SNOVA_24_5_4_ESK.rsp", + "PQCsignKAT_SNOVA_24_5_4_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_24_5_4_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_24_5_4_SSK.rsp", + "PQCsignKAT_SNOVA_24_5_5_ESK.rsp", + "PQCsignKAT_SNOVA_24_5_5_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_24_5_5_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_24_5_5_SSK.rsp", "PQCsignKAT_SNOVA_25_8_3_ESK.rsp", "PQCsignKAT_SNOVA_25_8_3_SHAKE_ESK.rsp", "PQCsignKAT_SNOVA_25_8_3_SHAKE_SSK.rsp", From 8da0e883bccecaf2b5e6d23e7420da8dfb3f3e83 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 21 Mar 2025 09:46:23 +1030 Subject: [PATCH 244/890] Correct the SnovaParmeters for v=16, o=15, l=3. --- .../pqc/crypto/snova/SnovaParameters.java | 16 +- .../pqc/crypto/test/SnovaTest.java | 152 +++++++++--------- 2 files changed, 84 insertions(+), 84 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java index b6b0cc0027..83a7666a4e 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java @@ -88,14 +88,14 @@ public class SnovaParameters new SnovaParameters("SNOVA_60_10_16_4_SHAKE_ESK", 60, 10, 4, false, true); // SNOVA_66_15_16_4 variants - public static final SnovaParameters SNOVA_66_15_16_4_SSK = - new SnovaParameters("SNOVA_66_15_16_4_SSK", 66, 15, 4, true, false); - public static final SnovaParameters SNOVA_66_15_16_4_ESK = - new SnovaParameters("SNOVA_66_15_16_4_ESK", 66, 15, 4, false, false); - public static final SnovaParameters SNOVA_66_15_16_4_SHAKE_SSK = - new SnovaParameters("SNOVA_66_15_16_4_SHAKE_SSK", 66, 15, 4, true, true); - public static final SnovaParameters SNOVA_66_15_16_4_SHAKE_ESK = - new SnovaParameters("SNOVA_66_15_16_4_SHAKE_ESK", 66, 15, 4, false, true); + public static final SnovaParameters SNOVA_66_15_16_3_SSK = + new SnovaParameters("SNOVA_66_15_16_3_SSK", 66, 15, 3, true, false); + public static final SnovaParameters SNOVA_66_15_16_3_ESK = + new SnovaParameters("SNOVA_66_15_16_3_ESK", 66, 15, 3, false, false); + public static final SnovaParameters SNOVA_66_15_16_3_SHAKE_SSK = + new SnovaParameters("SNOVA_66_15_16_3_SHAKE_SSK", 66, 15, 3, true, true); + public static final SnovaParameters SNOVA_66_15_16_3_SHAKE_ESK = + new SnovaParameters("SNOVA_66_15_16_3_SHAKE_ESK", 66, 15, 3, false, true); // SNOVA_75_33_16_2 variants public static final SnovaParameters SNOVA_75_33_16_2_SSK = diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java index 995ce8e0e7..c55cd69e8a 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java @@ -27,46 +27,46 @@ public static void main(String[] args) private static final SnovaParameters[] PARAMETER_SETS = new SnovaParameters[] { - SnovaParameters.SNOVA_24_5_16_4_ESK, - SnovaParameters.SNOVA_24_5_16_4_SHAKE_ESK, - SnovaParameters.SNOVA_24_5_16_4_SHAKE_SSK, - SnovaParameters.SNOVA_24_5_16_4_SSK, - SnovaParameters.SNOVA_24_5_16_5_ESK, - SnovaParameters.SNOVA_24_5_16_5_SHAKE_ESK, - SnovaParameters.SNOVA_24_5_16_5_SHAKE_SSK, - SnovaParameters.SNOVA_24_5_16_5_SSK, - SnovaParameters.SNOVA_25_8_16_3_ESK, - SnovaParameters.SNOVA_25_8_16_3_SHAKE_ESK, - SnovaParameters.SNOVA_25_8_16_3_SHAKE_SSK, - SnovaParameters.SNOVA_25_8_16_3_SSK, - SnovaParameters.SNOVA_29_6_16_5_ESK, - SnovaParameters.SNOVA_29_6_16_5_SHAKE_ESK, - SnovaParameters.SNOVA_29_6_16_5_SHAKE_SSK, - SnovaParameters.SNOVA_29_6_16_5_SSK, - SnovaParameters.SNOVA_37_8_16_4_ESK, - SnovaParameters.SNOVA_37_8_16_4_SHAKE_ESK, - SnovaParameters.SNOVA_37_8_16_4_SHAKE_SSK, - SnovaParameters.SNOVA_37_8_16_4_SSK, - SnovaParameters.SNOVA_37_17_16_2_ESK, - SnovaParameters.SNOVA_37_17_16_2_SHAKE_ESK, - SnovaParameters.SNOVA_37_17_16_2_SHAKE_SSK, - SnovaParameters.SNOVA_37_17_16_2_SSK, - SnovaParameters.SNOVA_49_11_16_3_ESK, - SnovaParameters.SNOVA_49_11_16_3_SHAKE_ESK, - SnovaParameters.SNOVA_49_11_16_3_SHAKE_SSK, - SnovaParameters.SNOVA_49_11_16_3_SSK, - SnovaParameters.SNOVA_56_25_16_2_ESK, - SnovaParameters.SNOVA_56_25_16_2_SHAKE_ESK, - SnovaParameters.SNOVA_56_25_16_2_SHAKE_SSK, - SnovaParameters.SNOVA_56_25_16_2_SSK, - SnovaParameters.SNOVA_60_10_16_4_ESK, - SnovaParameters.SNOVA_60_10_16_4_SHAKE_ESK, - SnovaParameters.SNOVA_60_10_16_4_SHAKE_SSK, - SnovaParameters.SNOVA_60_10_16_4_SSK, - SnovaParameters.SNOVA_66_15_16_4_ESK, - SnovaParameters.SNOVA_66_15_16_4_SHAKE_ESK, - SnovaParameters.SNOVA_66_15_16_4_SHAKE_SSK, - SnovaParameters.SNOVA_66_15_16_4_SSK, +// SnovaParameters.SNOVA_24_5_16_4_ESK, +// SnovaParameters.SNOVA_24_5_16_4_SHAKE_ESK, +// SnovaParameters.SNOVA_24_5_16_4_SHAKE_SSK, +// SnovaParameters.SNOVA_24_5_16_4_SSK, +// SnovaParameters.SNOVA_24_5_16_5_ESK, +// SnovaParameters.SNOVA_24_5_16_5_SHAKE_ESK, +// SnovaParameters.SNOVA_24_5_16_5_SHAKE_SSK, +// SnovaParameters.SNOVA_24_5_16_5_SSK, +// SnovaParameters.SNOVA_25_8_16_3_ESK, +// SnovaParameters.SNOVA_25_8_16_3_SHAKE_ESK, +// SnovaParameters.SNOVA_25_8_16_3_SHAKE_SSK, +// SnovaParameters.SNOVA_25_8_16_3_SSK, +// SnovaParameters.SNOVA_29_6_16_5_ESK, +// SnovaParameters.SNOVA_29_6_16_5_SHAKE_ESK, +// SnovaParameters.SNOVA_29_6_16_5_SHAKE_SSK, +// SnovaParameters.SNOVA_29_6_16_5_SSK, +// SnovaParameters.SNOVA_37_8_16_4_ESK, +// SnovaParameters.SNOVA_37_8_16_4_SHAKE_ESK, +// SnovaParameters.SNOVA_37_8_16_4_SHAKE_SSK, +// SnovaParameters.SNOVA_37_8_16_4_SSK, +// SnovaParameters.SNOVA_37_17_16_2_ESK, +// SnovaParameters.SNOVA_37_17_16_2_SHAKE_ESK, +// SnovaParameters.SNOVA_37_17_16_2_SHAKE_SSK, +// SnovaParameters.SNOVA_37_17_16_2_SSK, +// SnovaParameters.SNOVA_49_11_16_3_ESK, +// SnovaParameters.SNOVA_49_11_16_3_SHAKE_ESK, +// SnovaParameters.SNOVA_49_11_16_3_SHAKE_SSK, +// SnovaParameters.SNOVA_49_11_16_3_SSK, +// SnovaParameters.SNOVA_56_25_16_2_ESK, +// SnovaParameters.SNOVA_56_25_16_2_SHAKE_ESK, +// SnovaParameters.SNOVA_56_25_16_2_SHAKE_SSK, +// SnovaParameters.SNOVA_56_25_16_2_SSK, +// SnovaParameters.SNOVA_60_10_16_4_ESK, +// SnovaParameters.SNOVA_60_10_16_4_SHAKE_ESK, +// SnovaParameters.SNOVA_60_10_16_4_SHAKE_SSK, +// SnovaParameters.SNOVA_60_10_16_4_SSK, + SnovaParameters.SNOVA_66_15_16_3_ESK, + SnovaParameters.SNOVA_66_15_16_3_SHAKE_ESK, + SnovaParameters.SNOVA_66_15_16_3_SHAKE_SSK, + SnovaParameters.SNOVA_66_15_16_3_SSK, SnovaParameters.SNOVA_75_33_16_2_ESK, SnovaParameters.SNOVA_75_33_16_2_SHAKE_ESK, SnovaParameters.SNOVA_75_33_16_2_SHAKE_SSK, @@ -74,42 +74,42 @@ public static void main(String[] args) }; private static final String[] files = new String[]{ - "PQCsignKAT_SNOVA_24_5_4_ESK.rsp", - "PQCsignKAT_SNOVA_24_5_4_SHAKE_ESK.rsp", - "PQCsignKAT_SNOVA_24_5_4_SHAKE_SSK.rsp", - "PQCsignKAT_SNOVA_24_5_4_SSK.rsp", - "PQCsignKAT_SNOVA_24_5_5_ESK.rsp", - "PQCsignKAT_SNOVA_24_5_5_SHAKE_ESK.rsp", - "PQCsignKAT_SNOVA_24_5_5_SHAKE_SSK.rsp", - "PQCsignKAT_SNOVA_24_5_5_SSK.rsp", - "PQCsignKAT_SNOVA_25_8_3_ESK.rsp", - "PQCsignKAT_SNOVA_25_8_3_SHAKE_ESK.rsp", - "PQCsignKAT_SNOVA_25_8_3_SHAKE_SSK.rsp", - "PQCsignKAT_SNOVA_25_8_3_SSK.rsp", - "PQCsignKAT_SNOVA_29_6_5_ESK.rsp", - "PQCsignKAT_SNOVA_29_6_5_SHAKE_ESK.rsp", - "PQCsignKAT_SNOVA_29_6_5_SHAKE_SSK.rsp", - "PQCsignKAT_SNOVA_29_6_5_SSK.rsp", - "PQCsignKAT_SNOVA_37_8_4_ESK.rsp", - "PQCsignKAT_SNOVA_37_8_4_SHAKE_ESK.rsp", - "PQCsignKAT_SNOVA_37_8_4_SHAKE_SSK.rsp", - "PQCsignKAT_SNOVA_37_8_4_SSK.rsp", - "PQCsignKAT_SNOVA_37_17_2_ESK.rsp", - "PQCsignKAT_SNOVA_37_17_2_SHAKE_ESK.rsp", - "PQCsignKAT_SNOVA_37_17_2_SHAKE_SSK.rsp", - "PQCsignKAT_SNOVA_37_17_2_SSK.rsp", - "PQCsignKAT_SNOVA_49_11_3_ESK.rsp", - "PQCsignKAT_SNOVA_49_11_3_SHAKE_ESK.rsp", - "PQCsignKAT_SNOVA_49_11_3_SHAKE_SSK.rsp", - "PQCsignKAT_SNOVA_49_11_3_SSK.rsp", - "PQCsignKAT_SNOVA_56_25_2_ESK.rsp", - "PQCsignKAT_SNOVA_56_25_2_SHAKE_ESK.rsp", - "PQCsignKAT_SNOVA_56_25_2_SHAKE_SSK.rsp", - "PQCsignKAT_SNOVA_56_25_2_SSK.rsp", - "PQCsignKAT_SNOVA_60_10_4_ESK.rsp", - "PQCsignKAT_SNOVA_60_10_4_SHAKE_ESK.rsp", - "PQCsignKAT_SNOVA_60_10_4_SHAKE_SSK.rsp", - "PQCsignKAT_SNOVA_60_10_4_SSK.rsp", +// "PQCsignKAT_SNOVA_24_5_4_ESK.rsp", +// "PQCsignKAT_SNOVA_24_5_4_SHAKE_ESK.rsp", +// "PQCsignKAT_SNOVA_24_5_4_SHAKE_SSK.rsp", +// "PQCsignKAT_SNOVA_24_5_4_SSK.rsp", +// "PQCsignKAT_SNOVA_24_5_5_ESK.rsp", +// "PQCsignKAT_SNOVA_24_5_5_SHAKE_ESK.rsp", +// "PQCsignKAT_SNOVA_24_5_5_SHAKE_SSK.rsp", +// "PQCsignKAT_SNOVA_24_5_5_SSK.rsp", +// "PQCsignKAT_SNOVA_25_8_3_ESK.rsp", +// "PQCsignKAT_SNOVA_25_8_3_SHAKE_ESK.rsp", +// "PQCsignKAT_SNOVA_25_8_3_SHAKE_SSK.rsp", +// "PQCsignKAT_SNOVA_25_8_3_SSK.rsp", +// "PQCsignKAT_SNOVA_29_6_5_ESK.rsp", +// "PQCsignKAT_SNOVA_29_6_5_SHAKE_ESK.rsp", +// "PQCsignKAT_SNOVA_29_6_5_SHAKE_SSK.rsp", +// "PQCsignKAT_SNOVA_29_6_5_SSK.rsp", +// "PQCsignKAT_SNOVA_37_8_4_ESK.rsp", +// "PQCsignKAT_SNOVA_37_8_4_SHAKE_ESK.rsp", +// "PQCsignKAT_SNOVA_37_8_4_SHAKE_SSK.rsp", +// "PQCsignKAT_SNOVA_37_8_4_SSK.rsp", +// "PQCsignKAT_SNOVA_37_17_2_ESK.rsp", +// "PQCsignKAT_SNOVA_37_17_2_SHAKE_ESK.rsp", +// "PQCsignKAT_SNOVA_37_17_2_SHAKE_SSK.rsp", +// "PQCsignKAT_SNOVA_37_17_2_SSK.rsp", +// "PQCsignKAT_SNOVA_49_11_3_ESK.rsp", +// "PQCsignKAT_SNOVA_49_11_3_SHAKE_ESK.rsp", +// "PQCsignKAT_SNOVA_49_11_3_SHAKE_SSK.rsp", +// "PQCsignKAT_SNOVA_49_11_3_SSK.rsp", +// "PQCsignKAT_SNOVA_56_25_2_ESK.rsp", +// "PQCsignKAT_SNOVA_56_25_2_SHAKE_ESK.rsp", +// "PQCsignKAT_SNOVA_56_25_2_SHAKE_SSK.rsp", +// "PQCsignKAT_SNOVA_56_25_2_SSK.rsp", +// "PQCsignKAT_SNOVA_60_10_4_ESK.rsp", +// "PQCsignKAT_SNOVA_60_10_4_SHAKE_ESK.rsp", +// "PQCsignKAT_SNOVA_60_10_4_SHAKE_SSK.rsp", +// "PQCsignKAT_SNOVA_60_10_4_SSK.rsp", "PQCsignKAT_SNOVA_66_15_3_ESK.rsp", "PQCsignKAT_SNOVA_66_15_3_SHAKE_ESK.rsp", "PQCsignKAT_SNOVA_66_15_3_SHAKE_SSK.rsp", From 1503a6ce95e0be40fa21697ff91b269aa66550b0 Mon Sep 17 00:00:00 2001 From: David Hook Date: Fri, 21 Mar 2025 13:59:39 +1100 Subject: [PATCH 245/890] added support for external-mu --- .../pqc/crypto/mldsa/HashMLDSASigner.java | 3 +- .../pqc/crypto/mldsa/MLDSAEngine.java | 43 +++++-- .../pqc/crypto/mldsa/MLDSASigner.java | 51 +++++++- .../jcajce/MLDSAProxyPrivateKey.java | 73 ++++++++++++ .../jcajce/provider/asymmetric/MLDSA.java | 6 + .../asymmetric/mldsa/SignatureSpi.java | 110 ++++++++++++++++-- .../pqc/jcajce/provider/test/MLDSATest.java | 74 ++++++++++++ 7 files changed, 344 insertions(+), 16 deletions(-) create mode 100644 prov/src/main/java/org/bouncycastle/jcajce/MLDSAProxyPrivateKey.java diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java index 02736081fe..71ebaa56fc 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java @@ -119,8 +119,9 @@ public byte[] generateSignature() throws CryptoException, DataLengthException { random.nextBytes(rnd); } + byte[] mu = engine.generateMu(msgDigest); - return engine.generateSignature(msgDigest, privKey.rho, privKey.k, privKey.t0, privKey.s1, privKey.s2, rnd); + return engine.generateSignature(mu, msgDigest, privKey.rho, privKey.k, privKey.t0, privKey.s1, privKey.s2, rnd); } public boolean verifySignature(byte[] signature) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAEngine.java index 393146d26c..540736af1d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAEngine.java @@ -388,15 +388,20 @@ byte[] signInternal(byte[] msg, int msglen, byte[] rho, byte[] key, byte[] t0Enc shake256.update(msg, 0, msglen); - return generateSignature(shake256, rho, key, t0Enc, s1Enc, s2Enc, rnd); + return generateSignature(generateMu(shake256), shake256, rho, key, t0Enc, s1Enc, s2Enc, rnd); } - byte[] generateSignature(SHAKEDigest shake256Digest, byte[] rho, byte[] key, byte[] t0Enc, byte[] s1Enc, byte[] s2Enc, byte[] rnd) + byte[] generateMu(SHAKEDigest shake256Digest) { byte[] mu = new byte[CrhBytes]; shake256Digest.doFinal(mu, 0, CrhBytes); + return mu; + } + + byte[] generateSignature(byte[] mu, SHAKEDigest shake256Digest, byte[] rho, byte[] key, byte[] t0Enc, byte[] s1Enc, byte[] s2Enc, byte[] rnd) + { byte[] outSig = new byte[CryptoBytes]; byte[] rhoPrime = new byte[CrhBytes]; short nonce = 0; @@ -491,7 +496,36 @@ byte[] generateSignature(SHAKEDigest shake256Digest, byte[] rho, byte[] key, byt return null; } + boolean verifyInternalMu(byte[] providedMu) + { + byte[] mu = new byte[CrhBytes]; + + shake256Digest.doFinal(mu, 0); + + return Arrays.constantTimeAreEqual(mu, providedMu); + } + + boolean verifyInternalMuSignature(byte[] mu, byte[] sig, int siglen, SHAKEDigest shake256Digest, byte[] rho, byte[] encT1) + { + byte[] buf = new byte[Math.max(CrhBytes + DilithiumK * DilithiumPolyW1PackedBytes, DilithiumCTilde)]; + + // Mu + System.arraycopy(mu, 0, buf, 0, mu.length); + + return doVerifyInternal(buf, sig, siglen, shake256Digest, rho, encT1); + } + boolean verifyInternal(byte[] sig, int siglen, SHAKEDigest shake256Digest, byte[] rho, byte[] encT1) + { + byte[] buf = new byte[Math.max(CrhBytes + DilithiumK * DilithiumPolyW1PackedBytes, DilithiumCTilde)]; + + // Mu + shake256Digest.doFinal(buf, 0); + + return doVerifyInternal(buf, sig, siglen, shake256Digest, rho, encT1); + } + + private boolean doVerifyInternal(byte[] buf, byte[] sig, int siglen, SHAKEDigest shake256Digest, byte[] rho, byte[] encT1) { if (siglen != CryptoBytes) { @@ -511,11 +545,6 @@ boolean verifyInternal(byte[] sig, int siglen, SHAKEDigest shake256Digest, byte[ return false; } - byte[] buf = new byte[Math.max(CrhBytes + DilithiumK * DilithiumPolyW1PackedBytes, DilithiumCTilde)]; - - // Mu - shake256Digest.doFinal(buf, 0); - Poly cp = new Poly(this); PolyVecMatrix aMatrix = new PolyVecMatrix(this); PolyVecK t1 = new PolyVecK(this), w1 = new PolyVecK(this); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSASigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSASigner.java index 4ceac5c22d..8f31d04cd7 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSASigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSASigner.java @@ -93,6 +93,34 @@ public void update(byte[] in, int off, int len) msgDigest.update(in, off, len); } + public byte[] generateMu() + throws CryptoException, DataLengthException + { + byte[] mu = engine.generateMu(msgDigest); + + reset(); + + return mu; + } + + public byte[] generateMuSignature(byte[] mu) + throws CryptoException, DataLengthException + { + byte[] rnd = new byte[MLDSAEngine.RndBytes]; + if (random != null) + { + random.nextBytes(rnd); + } + + msgDigest.reset(); + + byte[] sig = engine.generateSignature(mu, msgDigest, privKey.rho, privKey.k, privKey.t0, privKey.s1, privKey.s2, rnd); + + reset(); + + return sig; + } + public byte[] generateSignature() throws CryptoException, DataLengthException { @@ -102,13 +130,23 @@ public byte[] generateSignature() random.nextBytes(rnd); } - byte[] sig = engine.generateSignature(msgDigest, privKey.rho, privKey.k, privKey.t0, privKey.s1, privKey.s2, rnd); + byte[] mu = engine.generateMu(msgDigest); + byte[] sig = engine.generateSignature(mu, msgDigest, privKey.rho, privKey.k, privKey.t0, privKey.s1, privKey.s2, rnd); reset(); return sig; } + public boolean verifyMu(byte[] mu) + { + boolean isTrue = engine.verifyInternalMu(mu); + + reset(); + + return isTrue; + } + public boolean verifySignature(byte[] signature) { boolean isTrue = engine.verifyInternal(signature, signature.length, msgDigest, pubKey.rho, pubKey.t1); @@ -118,6 +156,17 @@ public boolean verifySignature(byte[] signature) return isTrue; } + public boolean verifyMuSignature(byte[] mu, byte[] signature) + { + msgDigest.reset(); + + boolean isTrue = engine.verifyInternalMuSignature(mu, signature, signature.length, msgDigest, pubKey.rho, pubKey.t1); + + reset(); + + return isTrue; + } + public void reset() { msgDigest = engine.getShake256Digest(); diff --git a/prov/src/main/java/org/bouncycastle/jcajce/MLDSAProxyPrivateKey.java b/prov/src/main/java/org/bouncycastle/jcajce/MLDSAProxyPrivateKey.java new file mode 100644 index 0000000000..9074b56723 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/jcajce/MLDSAProxyPrivateKey.java @@ -0,0 +1,73 @@ +package org.bouncycastle.jcajce; + +import java.security.PublicKey; + +import org.bouncycastle.jcajce.interfaces.MLDSAPrivateKey; +import org.bouncycastle.jcajce.interfaces.MLDSAPublicKey; +import org.bouncycastle.jcajce.spec.MLDSAParameterSpec; + +/** + * An ML-DSA private key wrapper which acts as a proxy to allow an ML-DSA public key + * to be passed in for external-mu calculation. + */ +public class MLDSAProxyPrivateKey + implements MLDSAPrivateKey +{ + private final MLDSAPublicKey publicKey; + + public MLDSAProxyPrivateKey(PublicKey publicKey) + { + if (!(publicKey instanceof MLDSAPublicKey)) + { + throw new IllegalArgumentException("public key must be an ML-DSA public key"); + } + this.publicKey = (MLDSAPublicKey)publicKey; + } + + public MLDSAPublicKey getPublicKey() + { + return publicKey; + } + + @Override + public String getAlgorithm() + { + return publicKey.getAlgorithm(); + } + + @Override + public String getFormat() + { + return null; + } + + @Override + public byte[] getEncoded() + { + return new byte[0]; + } + + @Override + public MLDSAParameterSpec getParameterSpec() + { + return publicKey.getParameterSpec(); + } + + @Override + public byte[] getPrivateData() + { + return new byte[0]; + } + + @Override + public byte[] getSeed() + { + return new byte[0]; + } + + @Override + public MLDSAPrivateKey getPrivateKey(boolean preferSeedOnly) + { + return null; + } +} diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/MLDSA.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/MLDSA.java index 4f9424b029..79540890da 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/MLDSA.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/MLDSA.java @@ -49,6 +49,12 @@ public void configure(ConfigurableProvider provider) addSignatureAlgorithm(provider, "ML-DSA-87", PREFIX + "SignatureSpi$MLDSA87", NISTObjectIdentifiers.id_ml_dsa_87); provider.addAlgorithm("Alg.Alias.Signature.MLDSA", "ML-DSA"); + addSignatureAlgorithm(provider, "ML-DSA-CALCULATE-MU", PREFIX + "SignatureSpi$MLDSACalcMu", (ASN1ObjectIdentifier)null); + provider.addAlgorithm("Alg.Alias.Signature.MLDSA-CALCULATE-MU", "ML-DSA-CALCULATE-MU"); + + addSignatureAlgorithm(provider, "ML-DSA-EXTERNAL-MU", PREFIX + "SignatureSpi$MLDSAExtMu", (ASN1ObjectIdentifier)null); + provider.addAlgorithm("Alg.Alias.Signature.MLDSA-EXTERNAL-MU", "ML-DSA-EXTERNAL-MU"); + addSignatureAlgorithm(provider, "HASH-ML-DSA", PREFIX + "HashSignatureSpi$MLDSA", (ASN1ObjectIdentifier)null); addSignatureAlgorithm(provider, "ML-DSA-44-WITH-SHA512", PREFIX + "HashSignatureSpi$MLDSA44", NISTObjectIdentifiers.id_hash_ml_dsa_44_with_sha512); addSignatureAlgorithm(provider, "ML-DSA-65-WITH-SHA512", PREFIX + "HashSignatureSpi$MLDSA65", NISTObjectIdentifiers.id_hash_ml_dsa_65_with_sha512); diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/SignatureSpi.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/SignatureSpi.java index 3ddc0cfdba..69ef8254b0 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/SignatureSpi.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/SignatureSpi.java @@ -1,5 +1,6 @@ package org.bouncycastle.jcajce.provider.asymmetric.mldsa; +import java.io.ByteArrayOutputStream; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; @@ -8,6 +9,7 @@ import java.security.SignatureException; import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.jcajce.MLDSAProxyPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.util.BaseDeterministicOrRandomSignature; import org.bouncycastle.jcajce.spec.MLDSAParameterSpec; import org.bouncycastle.pqc.crypto.mldsa.MLDSAParameters; @@ -16,8 +18,8 @@ public class SignatureSpi extends BaseDeterministicOrRandomSignature { - private MLDSASigner signer; - private MLDSAParameters parameters; + protected MLDSASigner signer; + protected MLDSAParameters parameters; protected SignatureSpi(MLDSASigner signer) { @@ -78,6 +80,22 @@ protected void signInit(PrivateKey privateKey, SecureRandom random) } } } + else if (privateKey instanceof MLDSAProxyPrivateKey) + { + MLDSAProxyPrivateKey pKey = (MLDSAProxyPrivateKey)privateKey; + BCMLDSAPublicKey key = (BCMLDSAPublicKey)pKey.getPublicKey(); + + this.keyParams = key.getKeyParams(); + + if (parameters != null) + { + String canonicalAlg = MLDSAParameterSpec.fromName(parameters.getName()).getName(); + if (!canonicalAlg.equals(key.getAlgorithm())) + { + throw new InvalidKeyException("signature configured for " + canonicalAlg); + } + } + } else { throw new InvalidKeyException("unknown private key passed to ML-DSA"); @@ -121,7 +139,7 @@ protected void reInitialize(boolean forSigning, CipherParameters params) } public static class MLDSA - extends SignatureSpi + extends SignatureSpi { public MLDSA() { @@ -130,7 +148,7 @@ public MLDSA() } public static class MLDSA44 - extends SignatureSpi + extends SignatureSpi { public MLDSA44() { @@ -139,7 +157,7 @@ public MLDSA44() } public static class MLDSA65 - extends SignatureSpi + extends SignatureSpi { public MLDSA65() { @@ -148,12 +166,90 @@ public MLDSA65() } public static class MLDSA87 - extends SignatureSpi + extends SignatureSpi { public MLDSA87() - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { super(new MLDSASigner(), MLDSAParameters.ml_dsa_87); } } + + public static class MLDSAExtMu + extends SignatureSpi + { + private ByteArrayOutputStream bOut = new ByteArrayOutputStream(64); + + public MLDSAExtMu() + { + super(new MLDSASigner()); + } + + protected void updateEngine(byte b) + throws SignatureException + { + bOut.write(b); + } + + protected void updateEngine(byte[] b, int off, int len) + throws SignatureException + { + bOut.write(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + try + { + byte[] mu = bOut.toByteArray(); + + bOut.reset(); + + return signer.generateMuSignature(mu); + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify(byte[] sigBytes) + throws SignatureException + { + byte[] mu = bOut.toByteArray(); + + bOut.reset(); + + return signer.verifyMuSignature(mu, sigBytes); + } + } + + public static class MLDSACalcMu + extends SignatureSpi + { + public MLDSACalcMu() + { + super(new MLDSASigner()); + } + + protected byte[] engineSign() + throws SignatureException + { + try + { + return signer.generateMu(); + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify(byte[] sigBytes) + throws SignatureException + { + return signer.verifyMu(sigBytes); + } + } } diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java index b037cbd4fc..9eec4bac29 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java @@ -25,7 +25,9 @@ import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.jcajce.MLDSAProxyPrivateKey; import org.bouncycastle.jcajce.interfaces.MLDSAKey; import org.bouncycastle.jcajce.interfaces.MLDSAPrivateKey; import org.bouncycastle.jcajce.spec.ContextParameterSpec; @@ -475,6 +477,78 @@ public void testMLDSARandomSig() assertTrue(sig.verify(s)); } + public void testMLDSARandomMsgAndMuSig() + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("ML-DSA", "BC"); + + kpg.initialize(MLDSAParameterSpec.ml_dsa_44, new SecureRandom()); + + final KeyPair kp = kpg.generateKeyPair(); + + Signature sig = Signature.getInstance("ML-DSA-CALCULATE-MU", "BC"); + + sig.initSign(kp.getPrivate(), new SecureRandom()); + + sig.update(msg, 0, msg.length); + + byte[] mu = sig.sign(); + + sig = Signature.getInstance("ML-DSA-EXTERNAL-MU", "BC"); + + sig.initSign(kp.getPrivate(), new SecureRandom()); + + sig.update(mu, 0, mu.length); + + byte[] s = sig.sign(); + + sig = Signature.getInstance("ML-DSA", "BC"); + + sig.initVerify(kp.getPublic()); + + sig.update(msg, 0, msg.length); + + assertTrue(sig.verify(s)); + + sig = Signature.getInstance("ML-DSA-EXTERNAL-MU", "BC"); + + sig.initVerify(kp.getPublic()); + + sig.update(mu, 0, mu.length); + + assertTrue(sig.verify(s)); + + sig = Signature.getInstance("ML-DSA-CALCULATE-MU", "BC"); + + sig.initSign(new MLDSAProxyPrivateKey(kp.getPublic())); + + sig.update(msg, 0, msg.length); + + byte[] mu2 = sig.sign(); + + assertTrue(Arrays.areEqual(mu, mu2)); + } + + public void testMLDSAMuKatTest() + throws Exception + { + byte[] pk = Hex.decode("3BB5567D7049939D8A5911BBE1FEC62843DAD05447BFC37CEF917C431DD20B8EE32A985C4F681DB63186FCB455A9163559301728E6F03F9C915FBF48571CF7DC76B1D1373B1C57B1C479DCAF136EE893D04B8E8C3A86D28483E84CB6814A17DDE3FE4152B69ADFBFD40B1AF8D4CD66101070B9979E6EEEFD4ED21111D129FE959E39E8C00311EA624A54F3340FC6BE06A0A826A3E74DD4161FC9EEF6097562316E62BBAF4D482732BABBB1C5132B86C8D7D733DC9595DDB93EF6BF5D79A860885D3F3BE31908787587A99D84017D00E814E4F697F2688ADF3967AB05F61C0D0F02BD5B8C4EA51010103265F49F90F2D1D5DE55305B59CB3658146F1696F12EE25875136A234E35F10B7BE95FCCDAD326D9CEDCF64F7DA0E9F0697A9A04E4C7237CDFBAAFC6974DA04D158EA2CDC7A3F87CE0E5BE9DCD54ED84ABAFC6C9889C3427A904C2DFF70581E1BF0F544BC1954AAC852DFAB032D140AC94CACE5889DFD43BF9DB490CDF3342C15E4F88EC5F16B789828367A488CFAA6FED1E94322759EAAA211041444D10ACF8E1DF1EF840B68412603DA6BBC4FE69C8D2627C032A266475B1314119D843F54689300EAC933060D0A7106EDF58CC6B1F313B8FDE024C0179E7B67B08CC030B7B3BC610E9C2A60BFDEE54A1D5E0F53E8533456AEB4D0978DA9C7E904AC31B676222C9369D3FC74EDC8642C4F3C19E31B14F1E5EB3FCA413478E5B03DA9FB50755EECD2F0F513502232367A12A02A0B1B86C9FA8FD2990B8A7296818F9DA6E70228AC8BBC55A9374E1719BA180A29147CC1E379CF10FAFDF6FB0FFBB35CD033FBB6660042F9A61AEFBFBF6ECDBB146A3233644D5596AF68CE3C89BB77E75D60D2D044400374179483B3DC65FC029246A9C950D5AA4825C359B45EFB7B24E36C0A0B2592BD50F992484D170F4C3D77F8F7755D86FF1DE21332A7D1F70D069DAD0CFF4B4B3BBBC6A535C48E0262333AA87C22DD70D07610EA105D506BFCA77283B64C262F2A38FAAE68E96B9491A229B0A2FC6BF967F2C2DDDA1A8804F5B6E37C2AEFB768B33DC1D05B0E3C86BD4F799346101B2E44BCA78AF5CB50C8F57B104F054054CF2975DEFB661A8BE18BAA9296D3372B1460338F58E6B9CDCCF25CE371B3BF39E90E9B35037BE4F24BEA1ACF75A643ABD19E7D312DF45F27E04CD3D94E1569029F1EBB806E2617252263BD10EE898B90526675A42B90FB64F4C5B0F18B3446049B2F85BD1DD526E7AC5C41E3DF0F0920EAFAF804B808FF0E5B2E064BB39909AD39FB3BED06D1A97288A1D7F7B8EBBCD257869B736B87E18990E0E7DB4E2EB70FDE0B9B235DB741161C68F71E2C1AE9525AC68569D3AAD61584EE29EE5581C0F67439B9F1DE7EC65073929D08119A45B6C10028CC34132E57F8B969D4195095E891276822C83FA6AD3743B1F3597AE188A0324AF4C198995246338533123092A80F4B40D557B7AEF5CAA6C9F65B03E86A9D580370659F2BD76D1A79468BBE725E07CD67E0094CEB4D8CAC55C76B6AAE7C9DA8B7CD851679105B9934D8F8C5CE2F69124AC41EC8E8F4470AD4CCAA55C7F3533558D366D5A78410B4232927CAACA135166CD0792D9CDD595711633DADC81685939D1107AC085919534747095FD24026031480CC5E16BBA2A7083B0E94D3C67EF3901B30E1BF8645A96EC8DE67999E985EECD714603371C854F90F7545A45C455154E596BB9406769935022133B9195106AD5BB1BEDADF9BC05518E480832F6C961FF35F8B1C6D030CAE8CC9D47C8B573B6F8CD06510D9D9C33505EB9080089F41434E5695759B4641B2F06F33B4C8EAEA86116B7A45422FBB1E1FC0AF4C75CCFB94DEA99"); + byte[] mu = Hex.decode("ACFA27834894431BAA18EB0353DA5383BCFD8585E60F1A4382566E0D85E0519F67084AC615088A85074D901D8DBD36AE487B23281E1172F6C03C8CD31A4B683B"); + byte[] sig = Hex.decode("146E97D0704552E762FBF3B0387C255B381D84A1B98EACAF572E71EB0317133758BBF3631CA6C3412238327AEA511432CCF868841BC7F71E484DDB8158E20687AFA3381DC14B96E045036AF004955CF7C9BC9DBCDD3EE558C73E9E16AAD91603E0D839294684B358559F0B2278B5ABB6224395E02849CD7E4DC52805058809674D9CC79744A25436AFBBEB77784F6F85ABF315E963794270C763128F5EDB8E390E0CD2328B868FF6AEE3BC1DE73DC66DBD9D967E9BD1E0E96008C8678C5C28ED73349C297E86DEFC00653E97D873DD6443E4164A0D5231E8C9EFB4EA2F068FECA57BDAEA4A7989C96ED307A578013F705073E875B045CDDF8D131DC6A72DD4EB63495C0DDF53706EB43E44B7B4FAD7C835CB6E9C0D771894A11289A43D3454E4FC8301C9CEC180EAD4B763D332E4760CFE2DDCA5893D0190E6BB7E36E9B596AE714B5B30C65BA0B0675595BDED190BCEA2450063BA157E4CE7DAC45F66FC270BB0608C82196F5EAC4B53C5E2F8C59C3D18222428F935EB3F4E54C1FE7DE4BFDE305C2AFC36C91894387DDDC957C4D2737E9FCDE5C7CAE453A4C45D4FE10A811C78179D6DDB4E33F5E374245EBC3D3DEA4351C8F55E10CD0F79B70010E897BD3F376F7693937F2DF58A7BAB9A0AF5595E3383C426DEC394EBE410C026A2F3E6B2E8200BDAAF15F33DFA4ADEBD59F386AE6C8F097E1183CA9577FFC008C710705DC73A87B6EDBACC23CC8C4FE652916CF8DDFFBACB92EF6BAA668E5772A0A06A2F4DE1F307ABDF6A028BE7F82ECECDA72A411CFBF377B1B3B2AB731563611F8BC933478913A8C1FFF1BB7C76F7B6E55125A41A4FC12C306349546AEA527FA24C0815AAAF8AA3E5367654A6B2BBC886804F25A57E64EAFC0E1D3F805979500A7EF16AE8F0A6E13A3DB3B72A9A91B9D11D9C1A19A0300663C7E931A4AD5EB503EE6F4F7264B66C43C98AB26A5DC42BEFEBC5CAEDC08F7B328C5556B904AA173867737AB3EAEA09722680DF5CB5EF3A1C0D549731EF800D0C72AE8D90BD6C21BB4F4F0E2F4147181C4054F29EAB4A0AFCA49E7406C1A711F38659A638646E5CB3C142747EF843F6517A905F70BD93818816641A45C894E2D44E6CAB7DA0524C9998AF62EF0047CE1A7C5EDD490938402FB1B7015273FBDEB85E536946F0AB7051062536ACD21BAD2311E4A45AF17714426645293EF0D6266DB59692DD99286ABA0C9C77581ECE32CC4C7FBFABA55D719E35E68FC0A6C7031F999BB08153C2501FD61BEA99198DEF0C514A6A1A8FF6E5D9411BC0B7BD2B66E364AF51D007F49DC9E2A756396B1842BF6FCDDF0A2F9E8AF485AB8AF4653FB1F9570E99633DD4914624177CF3D2027053B0BA0D60FBCFEF92EAD69494C56D2717C427666AA313884FAC83BC42DAF224826E127F4B33A6340DEB52942E2792D90023FEBFC935E5B18C6EC8B2A6861C3F2C929A3FFA38C2DD23A97EBAA2CC694199BE95E93BDA5C4CBD13712D03A0C3CDC1F0A1C9B64A88F0E752A167F8F093077CA73B538A8738EA7F2B878A29FAA7D42266FCE8CF16A9BB413FD50FCF6057F77E16C916704AE7622AF72D666AD06C2596E9CE3A297786C134A430441958484EB058C8DB19D1A6E788336D67D52E877CEB4C9F204E4D995FE2685AAB6EEFCFA6D0FD1D61B0C8F532953B32642A34657BFBAD90B8F0C85A821054770C6A2E27D8CE315618DB82EA24A1D486C12934EBC9133F38B72481748B8C9D432A3D1E1AF85DEFB2BEE0C155006BE17E879811E2FE08A337D54A03E5884384000DE817A3D9BBE94941A52A58ECE851F605B9F69E1C3112CFF92AB332D6E694A65EFD58ED61F57F84947A75A5C9776A9A7FD187C5015425ED708DC242CDAA30BEE759BBD9CBB046DF3F4D54F481208216AF5CA7892150DD9829A3AB540BBFCFC2E303E9205ABB4D65D7287B5BBCC85F8987AD7B182058E36DA5C37FE5658138ED49D65B11B67360A1FEE9E44D2D2C3164BEF63B426DB495540B383AAE5F54283F91B1E543F7F965289CB9F64928E6AC208D9791DE234ABA43FC2B44D89317277136F791C782A773340998C526015C0415211676F53BDCE809974D6450E39CA2C814F644D75CB4F349D28EDF08B1CE58616D16816B4C021C5F16CCD5FB539BEFA6C9F03704FFE1010589350C20AE319545B0E39AE7CAA7FCED9A3D2151ACF6CBAB5FBB0D0E83313A45E6209046BF1A89D5F808969CC62167B014E969EAD6B2088B7738FAB3D277D1CDD6F4C6816B847AD1D30083F77564CABEB55CB4A8BB8C6BD47A59B6772DCCC8431A01C04184965B8F25BF8333281AC7686852FAFD77B374E76D8839CFE1F594E212531325F0CD6F8C2853194B4D668F843776F0E563E00CBBE6F5D6EBE4EBC11A5F70D3872F669939BD4A21A26FCD836DB79EC271CB463A521264DD62CD0A664B1EF3D7B4C84793F2B9C36369BEB651858CA9D5DCA23D6F8137C6C1F96FEE19DD3A4CD08A4FD4CD579F0F994E302A62F37121189B2A61E8591A98E0396766047B77E8D4533E62891B77AD0F7607DC0AF1D7D327E8E7B45991A508B5B22FD3398BEA7F80966EBEF5A1ACF9F5FCCF9C0C6E61A05DBB583EB048CAA41C28E87530820D21E90BFD2CFF29C8FEC2F7F1FC90605F2C84553A709CB9F53C1BDF12DC20524E76870ACB7624AD238B5BB8EEC8D5D37F02F12D1DF2384E7EC70CFDCC25831305682080326E702795ABFC6DF60BE923AB245887A35EB5EC00B7A109F86A8F7DCF2E59DBD8C4C9D645588F4D25C768F499C3FA65C98FEB48B07A4B01C7AF8AF28ADBC5C487A5C1FB059EF91B3A77C8321126978B5EAD648BB96C6210C6EF505B6E40098E415E0324D1CCD0A5B0560455BA894AD84CFA8E9B37706EFC23B00513BA3D6DF77D38E203AAD4E718286BE73A729C7CB2CB2EEC65726EA48E657DA31A548972B21642C01512421A21E2A4151A995CDC2DA9A7602E747CB62C893D555BA4455ACD880282459257622B370F3AEE7C99C2D8CD31050E6F1050340EB287BE92133C2479F07780ED355CC124FBDD2C7ADC2A129DA3A99C964F1BB32B5609C36ABB69AFABC0F1445362DFBE2A381C3D88973EDFF9D98ECD0E5EDA94D394B784ABC97470283FBB87403A5DCD4BCF55F24F4368BDE39E63E3C906FD2EA6C4103EF571FCD269700472C761B2E2C52F9F10C195C7947AE378B40724499C8EB3AE5ED4B3B06DCE02F29424662BF209AA14D39C1EEC5E3E5DE7B7CADFD1D8ED9D0AA552FF54353F0E9BD1CD02965A47D83F1359BE6FE0C0144BC0EA8BD2C37CAB4DFBEEB31833F4146316023C4D556C7C8093C3CAD4E7040B122B375963767893A9B4DEE5EDEEFAFC0409121A326F7072A2ACD5DCEEF1F2F4FD051E26373B3F464B97999A9B9FA4ABC9E2FDFE00000000000000000000000000000C1E2F42"); + + KeyFactory keyFactory = KeyFactory.getInstance("ML-DSA-44", "BC"); + + SubjectPublicKeyInfo mlDsaInfo = new SubjectPublicKeyInfo(new AlgorithmIdentifier(NISTObjectIdentifiers.id_ml_dsa_44), pk); + + Signature sigImpl = Signature.getInstance("ML-DSA-EXTERNAL-MU", "BC"); + + sigImpl.initVerify(keyFactory.generatePublic(new X509EncodedKeySpec(mlDsaInfo.getEncoded()))); + + sigImpl.update(mu, 0, mu.length); + + assertTrue(sigImpl.verify(sig)); + } + public void testMLDSAKATSig() throws Exception { From 9ec242f417b02dd39e8914d196740f4150a650c0 Mon Sep 17 00:00:00 2001 From: David Hook Date: Fri, 21 Mar 2025 15:15:43 +1100 Subject: [PATCH 246/890] generalized proxy private key public key usage. added bounds checking on mu. --- .../asymmetric/mldsa/SignatureSpi.java | 30 +++++++++++-- .../pqc/jcajce/provider/test/MLDSATest.java | 45 +++++++++++++++++++ 2 files changed, 71 insertions(+), 4 deletions(-) diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/SignatureSpi.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/SignatureSpi.java index 69ef8254b0..9d7433c117 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/SignatureSpi.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/SignatureSpi.java @@ -1,6 +1,7 @@ package org.bouncycastle.jcajce.provider.asymmetric.mldsa; import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; @@ -9,11 +10,14 @@ import java.security.SignatureException; import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.jcajce.MLDSAProxyPrivateKey; +import org.bouncycastle.jcajce.interfaces.MLDSAPublicKey; import org.bouncycastle.jcajce.provider.asymmetric.util.BaseDeterministicOrRandomSignature; import org.bouncycastle.jcajce.spec.MLDSAParameterSpec; import org.bouncycastle.pqc.crypto.mldsa.MLDSAParameters; import org.bouncycastle.pqc.crypto.mldsa.MLDSASigner; +import org.bouncycastle.pqc.crypto.util.PublicKeyFactory; public class SignatureSpi extends BaseDeterministicOrRandomSignature @@ -80,12 +84,19 @@ protected void signInit(PrivateKey privateKey, SecureRandom random) } } } - else if (privateKey instanceof MLDSAProxyPrivateKey) + else if (privateKey instanceof MLDSAProxyPrivateKey && this instanceof MLDSACalcMu) { MLDSAProxyPrivateKey pKey = (MLDSAProxyPrivateKey)privateKey; - BCMLDSAPublicKey key = (BCMLDSAPublicKey)pKey.getPublicKey(); + MLDSAPublicKey key = pKey.getPublicKey(); - this.keyParams = key.getKeyParams(); + try + { + this.keyParams = PublicKeyFactory.createKey(key.getEncoded()); + } + catch (IOException e) + { + throw new InvalidKeyException(e.getMessage()); + } if (parameters != null) { @@ -208,6 +219,10 @@ protected byte[] engineSign() return signer.generateMuSignature(mu); } + catch (DataLengthException e) + { + throw new SignatureException(e.getMessage()); + } catch (Exception e) { throw new SignatureException(e.toString()); @@ -221,7 +236,14 @@ protected boolean engineVerify(byte[] sigBytes) bOut.reset(); - return signer.verifyMuSignature(mu, sigBytes); + try + { + return signer.verifyMuSignature(mu, sigBytes); + } + catch (DataLengthException e) + { + throw new SignatureException(e.getMessage()); + } } } diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java index 9eec4bac29..e1f5d1e222 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java @@ -14,6 +14,7 @@ import java.security.SecureRandom; import java.security.Security; import java.security.Signature; +import java.security.SignatureException; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; @@ -549,6 +550,50 @@ public void testMLDSAMuKatTest() assertTrue(sigImpl.verify(sig)); } + public void testMLDSAMuExceptionTest() + throws Exception + { + // mu shortened by 1 byte + byte[] mu = Hex.decode("FA27834894431BAA18EB0353DA5383BCFD8585E60F1A4382566E0D85E0519F67084AC615088A85074D901D8DBD36AE487B23281E1172F6C03C8CD31A4B683B"); + byte[] sig = Hex.decode("146E97D0704552E762FBF3B0387C255B381D84A1B98EACAF572E71EB0317133758BBF3631CA6C3412238327AEA511432CCF868841BC7F71E484DDB8158E20687AFA3381DC14B96E045036AF004955CF7C9BC9DBCDD3EE558C73E9E16AAD91603E0D839294684B358559F0B2278B5ABB6224395E02849CD7E4DC52805058809674D9CC79744A25436AFBBEB77784F6F85ABF315E963794270C763128F5EDB8E390E0CD2328B868FF6AEE3BC1DE73DC66DBD9D967E9BD1E0E96008C8678C5C28ED73349C297E86DEFC00653E97D873DD6443E4164A0D5231E8C9EFB4EA2F068FECA57BDAEA4A7989C96ED307A578013F705073E875B045CDDF8D131DC6A72DD4EB63495C0DDF53706EB43E44B7B4FAD7C835CB6E9C0D771894A11289A43D3454E4FC8301C9CEC180EAD4B763D332E4760CFE2DDCA5893D0190E6BB7E36E9B596AE714B5B30C65BA0B0675595BDED190BCEA2450063BA157E4CE7DAC45F66FC270BB0608C82196F5EAC4B53C5E2F8C59C3D18222428F935EB3F4E54C1FE7DE4BFDE305C2AFC36C91894387DDDC957C4D2737E9FCDE5C7CAE453A4C45D4FE10A811C78179D6DDB4E33F5E374245EBC3D3DEA4351C8F55E10CD0F79B70010E897BD3F376F7693937F2DF58A7BAB9A0AF5595E3383C426DEC394EBE410C026A2F3E6B2E8200BDAAF15F33DFA4ADEBD59F386AE6C8F097E1183CA9577FFC008C710705DC73A87B6EDBACC23CC8C4FE652916CF8DDFFBACB92EF6BAA668E5772A0A06A2F4DE1F307ABDF6A028BE7F82ECECDA72A411CFBF377B1B3B2AB731563611F8BC933478913A8C1FFF1BB7C76F7B6E55125A41A4FC12C306349546AEA527FA24C0815AAAF8AA3E5367654A6B2BBC886804F25A57E64EAFC0E1D3F805979500A7EF16AE8F0A6E13A3DB3B72A9A91B9D11D9C1A19A0300663C7E931A4AD5EB503EE6F4F7264B66C43C98AB26A5DC42BEFEBC5CAEDC08F7B328C5556B904AA173867737AB3EAEA09722680DF5CB5EF3A1C0D549731EF800D0C72AE8D90BD6C21BB4F4F0E2F4147181C4054F29EAB4A0AFCA49E7406C1A711F38659A638646E5CB3C142747EF843F6517A905F70BD93818816641A45C894E2D44E6CAB7DA0524C9998AF62EF0047CE1A7C5EDD490938402FB1B7015273FBDEB85E536946F0AB7051062536ACD21BAD2311E4A45AF17714426645293EF0D6266DB59692DD99286ABA0C9C77581ECE32CC4C7FBFABA55D719E35E68FC0A6C7031F999BB08153C2501FD61BEA99198DEF0C514A6A1A8FF6E5D9411BC0B7BD2B66E364AF51D007F49DC9E2A756396B1842BF6FCDDF0A2F9E8AF485AB8AF4653FB1F9570E99633DD4914624177CF3D2027053B0BA0D60FBCFEF92EAD69494C56D2717C427666AA313884FAC83BC42DAF224826E127F4B33A6340DEB52942E2792D90023FEBFC935E5B18C6EC8B2A6861C3F2C929A3FFA38C2DD23A97EBAA2CC694199BE95E93BDA5C4CBD13712D03A0C3CDC1F0A1C9B64A88F0E752A167F8F093077CA73B538A8738EA7F2B878A29FAA7D42266FCE8CF16A9BB413FD50FCF6057F77E16C916704AE7622AF72D666AD06C2596E9CE3A297786C134A430441958484EB058C8DB19D1A6E788336D67D52E877CEB4C9F204E4D995FE2685AAB6EEFCFA6D0FD1D61B0C8F532953B32642A34657BFBAD90B8F0C85A821054770C6A2E27D8CE315618DB82EA24A1D486C12934EBC9133F38B72481748B8C9D432A3D1E1AF85DEFB2BEE0C155006BE17E879811E2FE08A337D54A03E5884384000DE817A3D9BBE94941A52A58ECE851F605B9F69E1C3112CFF92AB332D6E694A65EFD58ED61F57F84947A75A5C9776A9A7FD187C5015425ED708DC242CDAA30BEE759BBD9CBB046DF3F4D54F481208216AF5CA7892150DD9829A3AB540BBFCFC2E303E9205ABB4D65D7287B5BBCC85F8987AD7B182058E36DA5C37FE5658138ED49D65B11B67360A1FEE9E44D2D2C3164BEF63B426DB495540B383AAE5F54283F91B1E543F7F965289CB9F64928E6AC208D9791DE234ABA43FC2B44D89317277136F791C782A773340998C526015C0415211676F53BDCE809974D6450E39CA2C814F644D75CB4F349D28EDF08B1CE58616D16816B4C021C5F16CCD5FB539BEFA6C9F03704FFE1010589350C20AE319545B0E39AE7CAA7FCED9A3D2151ACF6CBAB5FBB0D0E83313A45E6209046BF1A89D5F808969CC62167B014E969EAD6B2088B7738FAB3D277D1CDD6F4C6816B847AD1D30083F77564CABEB55CB4A8BB8C6BD47A59B6772DCCC8431A01C04184965B8F25BF8333281AC7686852FAFD77B374E76D8839CFE1F594E212531325F0CD6F8C2853194B4D668F843776F0E563E00CBBE6F5D6EBE4EBC11A5F70D3872F669939BD4A21A26FCD836DB79EC271CB463A521264DD62CD0A664B1EF3D7B4C84793F2B9C36369BEB651858CA9D5DCA23D6F8137C6C1F96FEE19DD3A4CD08A4FD4CD579F0F994E302A62F37121189B2A61E8591A98E0396766047B77E8D4533E62891B77AD0F7607DC0AF1D7D327E8E7B45991A508B5B22FD3398BEA7F80966EBEF5A1ACF9F5FCCF9C0C6E61A05DBB583EB048CAA41C28E87530820D21E90BFD2CFF29C8FEC2F7F1FC90605F2C84553A709CB9F53C1BDF12DC20524E76870ACB7624AD238B5BB8EEC8D5D37F02F12D1DF2384E7EC70CFDCC25831305682080326E702795ABFC6DF60BE923AB245887A35EB5EC00B7A109F86A8F7DCF2E59DBD8C4C9D645588F4D25C768F499C3FA65C98FEB48B07A4B01C7AF8AF28ADBC5C487A5C1FB059EF91B3A77C8321126978B5EAD648BB96C6210C6EF505B6E40098E415E0324D1CCD0A5B0560455BA894AD84CFA8E9B37706EFC23B00513BA3D6DF77D38E203AAD4E718286BE73A729C7CB2CB2EEC65726EA48E657DA31A548972B21642C01512421A21E2A4151A995CDC2DA9A7602E747CB62C893D555BA4455ACD880282459257622B370F3AEE7C99C2D8CD31050E6F1050340EB287BE92133C2479F07780ED355CC124FBDD2C7ADC2A129DA3A99C964F1BB32B5609C36ABB69AFABC0F1445362DFBE2A381C3D88973EDFF9D98ECD0E5EDA94D394B784ABC97470283FBB87403A5DCD4BCF55F24F4368BDE39E63E3C906FD2EA6C4103EF571FCD269700472C761B2E2C52F9F10C195C7947AE378B40724499C8EB3AE5ED4B3B06DCE02F29424662BF209AA14D39C1EEC5E3E5DE7B7CADFD1D8ED9D0AA552FF54353F0E9BD1CD02965A47D83F1359BE6FE0C0144BC0EA8BD2C37CAB4DFBEEB31833F4146316023C4D556C7C8093C3CAD4E7040B122B375963767893A9B4DEE5EDEEFAFC0409121A326F7072A2ACD5DCEEF1F2F4FD051E26373B3F464B97999A9B9FA4ABC9E2FDFE00000000000000000000000000000C1E2F42"); + + KeyPairGenerator kpg = KeyPairGenerator.getInstance("ML-DSA", "BC"); + + kpg.initialize(MLDSAParameterSpec.ml_dsa_44, new SecureRandom()); + + final KeyPair kp = kpg.generateKeyPair(); + + Signature sigImpl = Signature.getInstance("ML-DSA-EXTERNAL-MU", "BC"); + + sigImpl.initVerify(kp.getPublic()); + + sigImpl.update(mu, 0, mu.length); + try + { + sigImpl.verify(sig); + fail("no exception"); + } + catch (SignatureException e) + { + assertEquals("mu value must be 64 bytes", e.getMessage()); + } + + sigImpl.initSign(kp.getPrivate()); + + sigImpl.update(mu, 0, mu.length); + + try + { + sigImpl.sign(); + fail("no exception"); + } + catch (Exception e) + { + assertEquals("mu value must be 64 bytes", e.getMessage()); + } + + } + public void testMLDSAKATSig() throws Exception { From c30fd6592c028f720282cf15dcf412419b71d1ec Mon Sep 17 00:00:00 2001 From: David Hook Date: Fri, 21 Mar 2025 15:17:32 +1100 Subject: [PATCH 247/890] missing file - mu bounds checking --- .../bouncycastle/pqc/crypto/mldsa/MLDSASigner.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSASigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSASigner.java index 8f31d04cd7..35a4d0f7de 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSASigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSASigner.java @@ -106,6 +106,10 @@ public byte[] generateMu() public byte[] generateMuSignature(byte[] mu) throws CryptoException, DataLengthException { + if (mu.length != MLDSAEngine.CrhBytes) + { + throw new DataLengthException("mu value must be " + MLDSAEngine.CrhBytes + " bytes"); + } byte[] rnd = new byte[MLDSAEngine.RndBytes]; if (random != null) { @@ -140,6 +144,11 @@ public byte[] generateSignature() public boolean verifyMu(byte[] mu) { + if (mu.length != MLDSAEngine.CrhBytes) + { + throw new DataLengthException("mu value must be " + MLDSAEngine.CrhBytes + " bytes"); + } + boolean isTrue = engine.verifyInternalMu(mu); reset(); @@ -158,6 +167,11 @@ public boolean verifySignature(byte[] signature) public boolean verifyMuSignature(byte[] mu, byte[] signature) { + if (mu.length != MLDSAEngine.CrhBytes) + { + throw new DataLengthException("mu value must be " + MLDSAEngine.CrhBytes + " bytes"); + } + msgDigest.reset(); boolean isTrue = engine.verifyInternalMuSignature(mu, signature, signature.length, msgDigest, pubKey.rho, pubKey.t1); From 6cbd53029116140d17fb1147d0f413a7dfd86a13 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 21 Mar 2025 18:08:01 +1030 Subject: [PATCH 248/890] TODO: SnovaSigner --- .../pqc/crypto/mayo/MayoSigner.java | 1 - .../crypto/snova/SnovaKeyPairGenerator.java | 4 +- .../pqc/crypto/snova/SnovaParameters.java | 10 + .../snova/SnovaPrivateKeyParameters.java | 9 +- .../snova/SnovaPublicKeyParameters.java | 9 +- .../pqc/crypto/snova/SnovaSigner.java | 298 ++++++++++++++++++ .../pqc/crypto/test/SnovaTest.java | 3 +- 7 files changed, 328 insertions(+), 6 deletions(-) create mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index 21f8b6d75b..8b5e9e6b44 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -50,7 +50,6 @@ public class MayoSigner @Override public void init(boolean forSigning, CipherParameters param) { - if (forSigning) { pubKey = null; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index 13f0f779c2..4dc6f8d28c 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -71,8 +71,8 @@ public AsymmetricCipherKeyPair generateKeyPair() } return new AsymmetricCipherKeyPair( - new SnovaPublicKeyParameters(pk), - new SnovaPrivateKeyParameters(sk) + new SnovaPublicKeyParameters(params, pk), + new SnovaPrivateKeyParameters(params, sk) ); } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java index 83a7666a4e..171f71342d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java @@ -177,4 +177,14 @@ public int getPrivateKeyLength() return ((l * l * (4 * o * alpha + o * (v * v + v * o + o * v) + v * o) + 1) >> 1) + SnovaKeyPairGenerator.privateSeedLength + SnovaKeyPairGenerator.publicSeedLength; } + + public int getN() + { + return v + o; + } + + public int getLsq() + { + return l * l; + } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPrivateKeyParameters.java index bcd35f51cf..a8643bcc98 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPrivateKeyParameters.java @@ -7,11 +7,13 @@ public class SnovaPrivateKeyParameters extends AsymmetricKeyParameter { private final byte[] privateKey; + private final SnovaParameters parameters; - public SnovaPrivateKeyParameters(byte[] privateKey) + public SnovaPrivateKeyParameters(SnovaParameters parameters, byte[] privateKey) { super(true); this.privateKey = Arrays.clone(privateKey); + this.parameters = parameters; } public byte[] getPrivateKey() @@ -23,4 +25,9 @@ public byte[] getEncoded() { return Arrays.clone(privateKey); } + + public SnovaParameters getParameters() + { + return parameters; + } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPublicKeyParameters.java index 99f9c9a8d8..40001e0269 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPublicKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaPublicKeyParameters.java @@ -7,11 +7,13 @@ public class SnovaPublicKeyParameters extends AsymmetricKeyParameter { private final byte[] publicKey; + private final SnovaParameters parameters; - public SnovaPublicKeyParameters(byte[] publicKey) + public SnovaPublicKeyParameters(SnovaParameters parameters, byte[] publicKey) { super(false); this.publicKey = Arrays.clone(publicKey); + this.parameters = parameters; } public byte[] getPublicKey() @@ -23,4 +25,9 @@ public byte[] getEncoded() { return Arrays.clone(publicKey); } + + public SnovaParameters getParameters() + { + return parameters; + } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java new file mode 100644 index 0000000000..d9fa84ae2c --- /dev/null +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -0,0 +1,298 @@ +package org.bouncycastle.pqc.crypto.snova; + +import java.security.SecureRandom; +import java.util.Arrays; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.Signer; +import org.bouncycastle.crypto.digests.SHAKEDigest; +import org.bouncycastle.crypto.params.ParametersWithRandom; + +public class SnovaSigner + implements Signer +{ + private SnovaParameters params; + private SnovaEngine engine; + private SecureRandom random; + private final SHAKEDigest digest = new SHAKEDigest(256); + + private SnovaPublicKeyParameters pubKey; + private SnovaPrivateKeyParameters privKey; + + @Override + public void init(boolean forSigning, CipherParameters param) + { + if (forSigning) + { + pubKey = null; + + if (param instanceof ParametersWithRandom) + { + ParametersWithRandom withRandom = (ParametersWithRandom)param; + privKey = (SnovaPrivateKeyParameters)withRandom.getParameters(); + random = withRandom.getRandom(); + } + else + { + privKey = (SnovaPrivateKeyParameters)param; + random = CryptoServicesRegistrar.getSecureRandom(); + } + params = privKey.getParameters(); + } + else + { + pubKey = (SnovaPublicKeyParameters)param; + params = pubKey.getParameters(); + privKey = null; + random = null; + } + } + + @Override + public void update(byte b) + { + digest.update(b); + } + + @Override + public void update(byte[] in, int off, int len) + { + digest.update(in, off, len); + } + + @Override + public byte[] generateSignature() + throws CryptoException, DataLengthException + { + return new byte[0]; + } + + @Override + public boolean verifySignature(byte[] signature) + { + return false; + } + + @Override + public void reset() + { + + } + + public static void createSignedHash( + byte[] digest, int bytesDigest, + byte[] ptPublicKeySeed, int seedLengthPublic, + byte[] arraySalt, int bytesSalt, + byte[] signedHashOut, int bytesHash + ) + { + // Initialize SHAKE256 XOF + SHAKEDigest shake = new SHAKEDigest(256); + + // 1. Absorb public key seed + shake.update(ptPublicKeySeed, 0, seedLengthPublic); + + // 2. Absorb message digest + shake.update(digest, 0, bytesDigest); + + // 3. Absorb salt + shake.update(arraySalt, 0, bytesSalt); + + // 4. Finalize absorption and squeeze output + shake.doFinal(signedHashOut, 0, bytesHash); + } + + public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, + byte[][][][] Aalpha, byte[][][][] Balpha, + byte[][][][] Qalpha1, byte[][][][] Qalpha2, + byte[][][][] T12, byte[][][][] F11, + byte[][][][] F12, byte[][][][] F21, + byte[] ptPublicKeySeed, byte[] ptPrivateKeySeed) + { + // Initialize constants from parameters + final int m = params.getM(); + final int lsq = params.getLsq(); + final int alpha = params.getAlpha(); + final int v = params.getV(); + final int o = params.getO(); + final int n = params.getN(); + final int bytesHash = (o * lsq + 1) >>> 1; + final int bytesSalt = 16; + + // Initialize matrices and arrays + byte[][] Gauss = new byte[m * lsq][m * lsq + 1]; + byte[][] Temp = new byte[lsq][lsq]; + byte[] solution = new byte[m * lsq]; + + byte[][][] Left = new byte[m][alpha][v]; + byte[][][] Right = new byte[m][alpha][v]; + byte[][] XInGF16Matrix = new byte[n][lsq]; + byte[][] FvvGF16Matrix = new byte[m][lsq]; + byte[] hashInGF16 = new byte[m * lsq]; + byte[][] signatureGF16Matrix = new byte[n][lsq]; + + byte[] signedHash = new byte[bytesHash]; + byte[] vinegarBytes = new byte[(v * lsq + 1) / 2]; + + // Temporary matrices + byte[] gf16mTemp0 = new byte[lsq]; + byte[] gf16mTemp1 = new byte[lsq]; + byte[] gf16mSecretTemp0 = new byte[lsq]; + + int flagRedo; + byte numSign = 0; + + // Step 1: Create signed hash + createSignedHash(digest, digest.length, ptPublicKeySeed, ptPublicKeySeed.length, + arraySalt, arraySalt.length, signedHash, bytesHash); + GF16Utils.decode(signedHash, 0, hashInGF16, 0, hashInGF16.length); + + do + { + // Initialize Gauss matrix + Arrays.stream(Gauss).forEach(row -> Arrays.fill(row, (byte)0)); + numSign++; + flagRedo = 0; + + // Fill last column of Gauss matrix + for (int i = 0; i < m * lsq; i++) + { + Gauss[i][m * lsq] = hashInGF16[i]; + } + + // Generate vinegar values + SHAKEDigest shake = new SHAKEDigest(256); + shake.update(ptPrivateKeySeed, 0, ptPrivateKeySeed.length); + shake.update(digest, 0, digest.length); + shake.update(arraySalt, 0, arraySalt.length); + shake.update(new byte[]{numSign}, 0, 1); + shake.doFinal(vinegarBytes, 0, vinegarBytes.length); + //GF16Utils.decode(vinegarBytes, 0, XInGF16Matrix, 0, v * lsq); + + // Evaluate vinegar part of central map + for (int mi = 0; mi < m; mi++) + { + for (int a = 0; a < alpha; a++) + { + for (int idx = 0; idx < v; idx++) + { + transposeGF16Matrix(XInGF16Matrix[idx], gf16mTemp0); +// multiplyGF16Matrices(gf16mTemp0, Qalpha1[mi][a], gf16mTemp1); +// multiplyGF16Matrices(Aalpha[mi][a], gf16mTemp1, Left[mi][a][idx]); +// +// multiplyGF16Matrices(Qalpha2[mi][a], XInGF16Matrix[idx], gf16mTemp1); +// multiplyGF16Matrices(gf16mTemp1, Balpha[mi][a], Right[mi][a][idx]); + } + } + } + + // Matrix operations for Fvv + Arrays.stream(FvvGF16Matrix).forEach(row -> Arrays.fill(row, (byte)0)); + for (int mi = 0; mi < m; mi++) + { + for (int a = 0; a < alpha; a++) + { + int miPrime = iPrime(mi, a); + for (int j = 0; j < v; j++) + { + for (int k = 0; k < v; k++) + { +// multiplyGF16Matrices(Left[mi][a][j], F11[miPrime][j][k], gf16mTemp0); +// multiplyGF16Matrices(gf16mTemp0, Right[mi][a][k], gf16mTemp1); + //GF16Utils.addGF16Matrices(FvvGF16Matrix[mi], gf16mTemp1, FvvGF16Matrix[mi]); + } + } + } + } + + // Gaussian elimination setup + for (int i = 0; i < m; i++) + { + for (int j = 0; j < params.getL(); j++) + { + for (int k = 0; k < params.getL(); k++) + { + int idx1 = i * lsq + j * params.getL() + k; + Gauss[idx1][m * lsq] = GF16Utils.add( + Gauss[idx1][m * lsq], + engine.getGF16m(FvvGF16Matrix[i], j, k) + ); + } + } + } + + // Gaussian elimination implementation + flagRedo = performGaussianElimination(Gauss, solution, m * lsq); + + } + while (flagRedo != 0); + + // Build final signature + buildSignature(XInGF16Matrix, signatureGF16Matrix, T12, v, o, lsq); + convertGF16MatrixToBytes(ptSignature, signatureGF16Matrix, n * lsq); + //System.arraycopy(arraySalt, 0, ptSignature, params.getBytesSignature(), bytesSalt); + + // Clear sensitive data + Arrays.fill(gf16mSecretTemp0, (byte)0); + } + + private void transposeGF16Matrix(byte[] src, byte[] dest) + { + for (int i = 0; i < params.getL(); i++) + { + for (int j = 0; j < params.getL(); j++) + { + engine.setGF16m(dest, i, j, engine.getGF16m(src, j, i)); + } + } + } + + private void multiplyGF16Matrices(byte[] a, byte[] b, byte[] result) + { + Arrays.fill(result, (byte)0); + for (int i = 0; i < params.getL(); i++) + { + for (int j = 0; j < params.getL(); j++) + { + byte sum = 0; + for (int k = 0; k < params.getL(); k++) + { + sum = GF16Utils.add(sum, GF16Utils.mul( + engine.getGF16m(a, i, k), + engine.getGF16m(b, k, j) + )); + } + engine.setGF16m(result, i, j, sum); + } + } + } + + private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size) + { + // Implementation of Gaussian elimination with GF16 arithmetic + // ... (similar structure to C code's elimination steps) + return 0; // Return 0 if successful, 1 if needs redo + } + + private void buildSignature(byte[][] XIn, byte[][] signature, + byte[][][][] T12, int v, int o, int lsq) + { + // Implementation of signature construction + // ... (similar to C code's final matrix operations) + } + + private void convertGF16MatrixToBytes(byte[] output, byte[][] matrix, int totalElements) + { + // Conversion implementation using GF16Utils.encode + } + + private int iPrime(int mi, int alpha) + { + // Implement index calculation based on SNOVA specification + return (mi + alpha) % params.getM(); + } + +} diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java index c55cd69e8a..baf0059b41 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java @@ -13,6 +13,7 @@ import org.bouncycastle.pqc.crypto.snova.SnovaParameters; import org.bouncycastle.pqc.crypto.snova.SnovaPrivateKeyParameters; import org.bouncycastle.pqc.crypto.snova.SnovaPublicKeyParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaSigner; public class SnovaTest @@ -158,7 +159,7 @@ public byte[] getPrivateKeyEncoded(CipherParameters privParams) @Override public Signer getSigner() { - return null; + return new SnovaSigner(); } @Override From 508dbf94ac729461cdaae8fee56e6b9933cff33c Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 14 Feb 2025 16:55:39 +1030 Subject: [PATCH 249/890] Add BcHssLmsContentSignerBuilder --- .../asn1/pkcs/PKCSObjectIdentifiers.java | 1 + .../bc/BcHssLmsContentSignerBuilder.java | 81 +++++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java index ab1d5d25ba..9343de210d 100644 --- a/core/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java @@ -278,6 +278,7 @@ public interface PKCSObjectIdentifiers * id-alg-hss-lms-hashsig OBJECT IDENTIFIER ::= { iso(1) * member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs9(9) * smime(16) alg(3) 17 } + * 1.2.840.113549.1.9.16.3.17 */ public static final ASN1ObjectIdentifier id_alg_hss_lms_hashsig = smime_alg.branch("17"); diff --git a/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java b/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java new file mode 100644 index 0000000000..816abe0b7a --- /dev/null +++ b/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java @@ -0,0 +1,81 @@ +package org.bouncycastle.operator.bc; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.Signer; +import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.pqc.crypto.MessageSigner; +import org.bouncycastle.pqc.crypto.lms.HSSSigner; + +public class BcHssLmsContentSignerBuilder + extends BcContentSignerBuilder +{ + public BcHssLmsContentSignerBuilder(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId) + { + super(sigAlgId, digAlgId); + } + + protected Signer createSigner(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId) + throws OperatorCreationException + { + Digest dig = digestProvider.get(digAlgId); + + return new HssSigner(dig); + } + + private static class HssSigner + implements Signer + { + private final MessageSigner hss = new HSSSigner(); + private final Digest digest; + + public HssSigner(Digest digest) + { + this.digest = digest; + } + + @Override + public void init(boolean forSigning, CipherParameters param) + { + hss.init(forSigning, param); + } + + @Override + public void update(byte b) + { + digest.update(b); + } + + @Override + public void update(byte[] in, int off, int len) + { + digest.update(in, off, len); + } + + @Override + public byte[] generateSignature() + throws CryptoException, DataLengthException + { + byte[] hash = new byte[digest.getDigestSize()]; + digest.doFinal(hash, 0); + return hss.generateSignature(hash); + } + + @Override + public boolean verifySignature(byte[] signature) + { + byte[] hash = new byte[digest.getDigestSize()]; + digest.doFinal(hash, 0); + return hss.verifySignature(hash, signature); + } + + @Override + public void reset() + { + digest.reset(); + } + } +} From 15c5a960c505abc21d24074e374689ec6861b331 Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 17 Feb 2025 14:18:21 +1030 Subject: [PATCH 250/890] Add some ObjectIdentifier. Pass the test for HSS signer. --- .../crypto/util/PublicKeyFactory.java | 3 +- .../util/SubjectPublicKeyInfoFactory.java | 12 ++ .../asn1/kisa/KISAObjectIdentifiers.java | 3 + .../bc/BcHssLmsContentSignerBuilder.java | 53 ++++++--- ...cHssLmsContentVerifierProviderBuilder.java | 30 +++++ .../cms/test/PQCSignedDataTest.java | 109 +++++++++++++++++- .../asn1/kisa/KISAObjectIdentifiers.java | 3 + .../asn1/mod/ModObjectIdentifiers.java | 22 ++++ 8 files changed, 213 insertions(+), 22 deletions(-) create mode 100644 pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentVerifierProviderBuilder.java create mode 100644 util/src/main/java/org/bouncycastle/asn1/mod/ModObjectIdentifiers.java diff --git a/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java b/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java index 0a5a1189a4..bc95a90f77 100644 --- a/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java @@ -165,7 +165,8 @@ public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Obj SubjectPublicKeyInfoConverter converter = (SubjectPublicKeyInfoConverter)converters.get(algID.getAlgorithm()); if (null == converter) { - throw new IOException("algorithm identifier in public key not recognised: " + algID.getAlgorithm()); + return org.bouncycastle.pqc.crypto.util.PublicKeyFactory.createKey(keyInfo, defaultParams); + //throw new IOException("algorithm identifier in public key not recognised: " + algID.getAlgorithm()); } return converter.getPublicKeyParameters(keyInfo, defaultParams); diff --git a/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java index 3ade492281..1797e24995 100644 --- a/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java @@ -35,6 +35,9 @@ import org.bouncycastle.crypto.params.X448PublicKeyParameters; import org.bouncycastle.internal.asn1.edec.EdECObjectIdentifiers; import org.bouncycastle.internal.asn1.rosstandart.RosstandartObjectIdentifiers; +import org.bouncycastle.pqc.crypto.lms.Composer; +import org.bouncycastle.pqc.crypto.lms.HSSPublicKeyParameters; +import org.bouncycastle.util.Arrays; /** * Factory to create ASN.1 subject public key info objects from lightweight public keys. @@ -192,6 +195,15 @@ else if (publicKey instanceof Ed25519PublicKeyParameters) return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), key.getEncoded()); } + else if (publicKey instanceof HSSPublicKeyParameters) + { + HSSPublicKeyParameters params = (HSSPublicKeyParameters)publicKey; + final byte tag_OctetString = (byte) 0x04; + byte[] encoding = Composer.compose().u32str(params.getL()).bytes(params.getLMSPublicKey()).build(); + + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig); + return new SubjectPublicKeyInfo(algorithmIdentifier, Arrays.concatenate(new byte[]{tag_OctetString, (byte)encoding.length}, encoding)); + } else { throw new IOException("key parameters not recognized"); diff --git a/core/src/main/java/org/bouncycastle/internal/asn1/kisa/KISAObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/internal/asn1/kisa/KISAObjectIdentifiers.java index 89ac25559c..19df01ad3a 100644 --- a/core/src/main/java/org/bouncycastle/internal/asn1/kisa/KISAObjectIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/internal/asn1/kisa/KISAObjectIdentifiers.java @@ -28,4 +28,7 @@ public interface KISAObjectIdentifiers /** RFC 4010: SeedEncryptionAlgorithmInCMS; OID 1.2.840.113549.1.9.16.0.24 */ static final ASN1ObjectIdentifier id_mod_cms_seed = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.24"); + + /** RFC 9708 MTS-HashSig-2013; OID 1.2.840.113549.1.9.16.0.64 */ + static final ASN1ObjectIdentifier id_mod_mts_hashsig_2013 = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.64"); } diff --git a/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java b/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java index 816abe0b7a..84af4b8ff2 100644 --- a/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java +++ b/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java @@ -1,81 +1,96 @@ package org.bouncycastle.operator.bc; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Signer; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.pqc.crypto.MessageSigner; +import org.bouncycastle.pqc.crypto.lms.HSSPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.lms.HSSPublicKeyParameters; import org.bouncycastle.pqc.crypto.lms.HSSSigner; +import org.bouncycastle.util.Arrays; public class BcHssLmsContentSignerBuilder extends BcContentSignerBuilder { - public BcHssLmsContentSignerBuilder(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId) + private static final AlgorithmIdentifier sigAlgId = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig); + + public BcHssLmsContentSignerBuilder() { - super(sigAlgId, digAlgId); + super(sigAlgId, null); } protected Signer createSigner(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId) throws OperatorCreationException { - Digest dig = digestProvider.get(digAlgId); - - return new HssSigner(dig); + return new HssSigner(); } - private static class HssSigner + static class HssSigner implements Signer { private final MessageSigner hss = new HSSSigner(); - private final Digest digest; + private final ByteArrayOutputStream stream = new ByteArrayOutputStream(); + private HSSPublicKeyParameters publicKeyParameters; + static final byte tag_OctetString = 0x04; - public HssSigner(Digest digest) + public HssSigner() { - this.digest = digest; } @Override public void init(boolean forSigning, CipherParameters param) { hss.init(forSigning, param); + if (forSigning) + { + publicKeyParameters = ((HSSPrivateKeyParameters)param).getPublicKey(); + } + else + { + publicKeyParameters = (HSSPublicKeyParameters)param; + } } @Override public void update(byte b) { - digest.update(b); + stream.write(b); } @Override public void update(byte[] in, int off, int len) { - digest.update(in, off, len); + stream.write(in, off, len); } @Override public byte[] generateSignature() throws CryptoException, DataLengthException { - byte[] hash = new byte[digest.getDigestSize()]; - digest.doFinal(hash, 0); - return hss.generateSignature(hash); + byte[] msg = stream.toByteArray(); + stream.reset(); + return hss.generateSignature(msg); } @Override public boolean verifySignature(byte[] signature) { - byte[] hash = new byte[digest.getDigestSize()]; - digest.doFinal(hash, 0); - return hss.verifySignature(hash, signature); + byte[] msg = stream.toByteArray(); + stream.reset(); + return hss.verifySignature(msg, signature); } @Override public void reset() { - digest.reset(); + stream.reset(); } } } diff --git a/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentVerifierProviderBuilder.java b/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentVerifierProviderBuilder.java new file mode 100644 index 0000000000..29ad2491b5 --- /dev/null +++ b/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentVerifierProviderBuilder.java @@ -0,0 +1,30 @@ +package org.bouncycastle.operator.bc; + +import java.io.IOException; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.Signer; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.util.PublicKeyFactory; +import org.bouncycastle.operator.OperatorCreationException; + +public class BcHssLmsContentVerifierProviderBuilder + extends BcContentVerifierProviderBuilder +{ + public BcHssLmsContentVerifierProviderBuilder() + { + } + + protected Signer createSigner(AlgorithmIdentifier sigAlgId) + throws OperatorCreationException + { + return new BcHssLmsContentSignerBuilder.HssSigner(); + } + + protected AsymmetricKeyParameter extractKeyParameters(SubjectPublicKeyInfo publicKeyInfo) + throws IOException + { + return PublicKeyFactory.createKey(publicKeyInfo); + } +} diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java index de19d395e0..a63d283702 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java @@ -2,13 +2,17 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.math.BigInteger; import java.security.KeyPair; import java.security.MessageDigest; +import java.security.SecureRandom; import java.security.Security; import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collection; +import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -27,8 +31,14 @@ import org.bouncycastle.asn1.cms.SignerInfo; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.x500.X500NameBuilder; +import org.bouncycastle.asn1.x500.style.RFC4519Style; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.cert.X509CertificateHolder; +import org.bouncycastle.cert.X509v1CertificateBuilder; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.bc.BcX509v1CertificateBuilder; +import org.bouncycastle.cert.bc.BcX509v3CertificateBuilder; import org.bouncycastle.cert.jcajce.JcaCertStore; import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.CMSProcessableByteArray; @@ -40,11 +50,31 @@ import org.bouncycastle.cms.SignerInformationStore; import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder; import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; +import org.bouncycastle.crypto.generators.DSAKeyPairGenerator; +import org.bouncycastle.crypto.generators.DSAParametersGenerator; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.params.DSAKeyGenerationParameters; +import org.bouncycastle.crypto.params.DSAParameters; +import org.bouncycastle.crypto.util.PublicKeyFactory; import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.DigestCalculatorProvider; import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.operator.bc.BcDSAContentSignerBuilder; +import org.bouncycastle.operator.bc.BcDSAContentVerifierProviderBuilder; +import org.bouncycastle.operator.bc.BcHssLmsContentSignerBuilder; +import org.bouncycastle.operator.bc.BcHssLmsContentVerifierProviderBuilder; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; +import org.bouncycastle.pqc.crypto.lms.HSSKeyGenerationParameters; +import org.bouncycastle.pqc.crypto.lms.HSSKeyPairGenerator; +import org.bouncycastle.pqc.crypto.lms.LMOtsParameters; +import org.bouncycastle.pqc.crypto.lms.LMSKeyGenerationParameters; +import org.bouncycastle.pqc.crypto.lms.LMSKeyPairGenerator; +import org.bouncycastle.pqc.crypto.lms.LMSParameters; +import org.bouncycastle.pqc.crypto.lms.LMSigParameters; import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider; import org.bouncycastle.util.Store; @@ -104,7 +134,7 @@ public static void main(String args[]) throws Exception { init(); - + //checkCreationHssLms(); junit.textui.TestRunner.run(PQCSignedDataTest.class); } @@ -321,7 +351,7 @@ public void testLmsEncapsulated() assertTrue(digAlgIds.contains(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256))); assertTrue(digAlgIds.size() == 1); - + certs = s.getCertificates(); SignerInformationStore signers = s.getSignerInfos(); @@ -352,6 +382,81 @@ public void testLmsEncapsulated() } } + public void testCheckCreationHssLms() + throws Exception + { + // + // set up the keys + // + AsymmetricKeyParameter privKey; + AsymmetricKeyParameter pubKey; + + AsymmetricCipherKeyPairGenerator kpg = new HSSKeyPairGenerator(); + + kpg.init(new HSSKeyGenerationParameters( + new LMSParameters[]{new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4)}, new SecureRandom())); + + AsymmetricCipherKeyPair pair = kpg.generateKeyPair(); + + privKey = (AsymmetricKeyParameter)pair.getPrivate(); + pubKey = (AsymmetricKeyParameter)pair.getPublic(); + + // + // distinguished name table. + // + X500NameBuilder builder = new X500NameBuilder(RFC4519Style.INSTANCE); + + builder.addRDN(RFC4519Style.c, "AU"); + builder.addRDN(RFC4519Style.o, "The Legion of the Bouncy Castle"); + builder.addRDN(RFC4519Style.l, "Melbourne"); + builder.addRDN(RFC4519Style.st, "Victoria"); + builder.addRDN(PKCSObjectIdentifiers.pkcs_9_at_emailAddress, "feedback-crypto@bouncycastle.org"); + + // + // extensions + // + + // + // create the certificate - version 3 + // + + + ContentSigner sigGen = new BcHssLmsContentSignerBuilder().build(privKey); + X509v3CertificateBuilder certGen = new BcX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey); + + + X509CertificateHolder cert = certGen.build(sigGen); + + assertTrue(cert.isValidOn(new Date())); + + assertTrue(cert.isSignatureValid(new BcHssLmsContentVerifierProviderBuilder().build(pubKey))); + + + // + // create the certificate - version 1 + // + + sigGen = new BcHssLmsContentSignerBuilder().build(privKey); + X509v1CertificateBuilder certGen1 = new BcX509v1CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey); + + cert = certGen1.build(sigGen); + + assertTrue(cert.isValidOn(new Date())); + + assertTrue(cert.isSignatureValid(new BcHssLmsContentVerifierProviderBuilder().build(pubKey))); + + AsymmetricKeyParameter certPubKey = PublicKeyFactory.createKey(cert.getSubjectPublicKeyInfo()); + + assertTrue(cert.isSignatureValid(new BcHssLmsContentVerifierProviderBuilder().build(certPubKey))); + + ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded()); + CertificateFactory fact = CertificateFactory.getInstance("X.509"); + + X509Certificate x509cert = (X509Certificate)fact.generateCertificate(bIn); + + //System.out.println(cert); + } + public void testTryLmsSettings() throws Exception { diff --git a/util/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java b/util/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java index a4611333f0..459aaebde1 100644 --- a/util/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java +++ b/util/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java @@ -28,4 +28,7 @@ public interface KISAObjectIdentifiers /** RFC 4010: SeedEncryptionAlgorithmInCMS; OID 1.2.840.113549.1.9.16.0.24 */ static final ASN1ObjectIdentifier id_mod_cms_seed = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.24"); + + /** RFC 9708 MTS-HashSig-2013; OID 1.2.840.113549.1.9.16.0.64 */ + static final ASN1ObjectIdentifier id_mod_mts_hashsig_2013 = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.64"); } diff --git a/util/src/main/java/org/bouncycastle/asn1/mod/ModObjectIdentifiers.java b/util/src/main/java/org/bouncycastle/asn1/mod/ModObjectIdentifiers.java new file mode 100644 index 0000000000..0b58e1528f --- /dev/null +++ b/util/src/main/java/org/bouncycastle/asn1/mod/ModObjectIdentifiers.java @@ -0,0 +1,22 @@ +package org.bouncycastle.asn1.mod; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; + +public interface ModObjectIdentifiers +{ + //TODO: add more from RFC 6268, RFC 5911 + + // id_mod OBJECT IDENTIFIER ::= { iso(1) identified_organization(3) + // dod(6) internet(1) security(5) mechanisms(5) pkix(7) mod(0) } + ASN1ObjectIdentifier id_mod = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.0"); + + /** + * PUBLIC-KEY, SIGNATURE-ALGORITHM, SMIME-CAPS + * FROM AlgorithmInformation-2009 -- RFC 5911 [CMSASN1] + * { iso(1) identified-organization(3) dod(6) internet(1) + * security(5) mechanisms(5) pkix(7) id-mod(0) + * id-mod-algorithmInformation-02(58) } ; + * 1.3.6.1.5.5.7.0.58 + */ + ASN1ObjectIdentifier id_mod_algorithmInformation_02 = id_mod.branch("58"); +} From 31b07025defec5d5b41236ce7344f970f7b0062e Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 17 Feb 2025 14:36:24 +1030 Subject: [PATCH 251/890] Pass the test for LMS signer. --- .../util/SubjectPublicKeyInfoFactory.java | 11 ++- .../bc/BcHssLmsContentSignerBuilder.java | 25 +++--- .../cms/test/PQCSignedDataTest.java | 76 ++++++++++++++++++- 3 files changed, 98 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java index 1797e24995..a7208d0b19 100644 --- a/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java @@ -37,6 +37,7 @@ import org.bouncycastle.internal.asn1.rosstandart.RosstandartObjectIdentifiers; import org.bouncycastle.pqc.crypto.lms.Composer; import org.bouncycastle.pqc.crypto.lms.HSSPublicKeyParameters; +import org.bouncycastle.pqc.crypto.lms.LMSPublicKeyParameters; import org.bouncycastle.util.Arrays; /** @@ -44,6 +45,7 @@ */ public class SubjectPublicKeyInfoFactory { + private static final byte tag_OctetString = (byte)0x04; private static Set cryptoProOids = new HashSet(5); static @@ -198,9 +200,14 @@ else if (publicKey instanceof Ed25519PublicKeyParameters) else if (publicKey instanceof HSSPublicKeyParameters) { HSSPublicKeyParameters params = (HSSPublicKeyParameters)publicKey; - final byte tag_OctetString = (byte) 0x04; byte[] encoding = Composer.compose().u32str(params.getL()).bytes(params.getLMSPublicKey()).build(); - + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig); + return new SubjectPublicKeyInfo(algorithmIdentifier, Arrays.concatenate(new byte[]{tag_OctetString, (byte)encoding.length}, encoding)); + } + else if (publicKey instanceof LMSPublicKeyParameters) + { + LMSPublicKeyParameters params = (LMSPublicKeyParameters)publicKey; + byte[] encoding = Composer.compose().u32str(1).bytes(params).build(); AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_alg_hss_lms_hashsig); return new SubjectPublicKeyInfo(algorithmIdentifier, Arrays.concatenate(new byte[]{tag_OctetString, (byte)encoding.length}, encoding)); } diff --git a/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java b/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java index 84af4b8ff2..02e7d97c7f 100644 --- a/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java +++ b/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java @@ -1,7 +1,6 @@ package org.bouncycastle.operator.bc; import java.io.ByteArrayOutputStream; -import java.io.IOException; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; @@ -14,7 +13,9 @@ import org.bouncycastle.pqc.crypto.lms.HSSPrivateKeyParameters; import org.bouncycastle.pqc.crypto.lms.HSSPublicKeyParameters; import org.bouncycastle.pqc.crypto.lms.HSSSigner; -import org.bouncycastle.util.Arrays; +import org.bouncycastle.pqc.crypto.lms.LMSPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.lms.LMSPublicKeyParameters; +import org.bouncycastle.pqc.crypto.lms.LMSSigner; public class BcHssLmsContentSignerBuilder extends BcContentSignerBuilder @@ -35,10 +36,8 @@ protected Signer createSigner(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier static class HssSigner implements Signer { - private final MessageSigner hss = new HSSSigner(); + private MessageSigner signer; private final ByteArrayOutputStream stream = new ByteArrayOutputStream(); - private HSSPublicKeyParameters publicKeyParameters; - static final byte tag_OctetString = 0x04; public HssSigner() { @@ -47,15 +46,19 @@ public HssSigner() @Override public void init(boolean forSigning, CipherParameters param) { - hss.init(forSigning, param); - if (forSigning) + if (param instanceof HSSPublicKeyParameters || param instanceof HSSPrivateKeyParameters) { - publicKeyParameters = ((HSSPrivateKeyParameters)param).getPublicKey(); + signer = new HSSSigner(); + } + else if (param instanceof LMSPublicKeyParameters || param instanceof LMSPrivateKeyParameters) + { + signer = new LMSSigner(); } else { - publicKeyParameters = (HSSPublicKeyParameters)param; + throw new IllegalArgumentException("Incorrect Key Parameters"); } + signer.init(forSigning, param); } @Override @@ -76,7 +79,7 @@ public byte[] generateSignature() { byte[] msg = stream.toByteArray(); stream.reset(); - return hss.generateSignature(msg); + return signer.generateSignature(msg); } @Override @@ -84,7 +87,7 @@ public boolean verifySignature(byte[] signature) { byte[] msg = stream.toByteArray(); stream.reset(); - return hss.verifySignature(msg, signature); + return signer.verifySignature(msg, signature); } @Override diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java index a63d283702..959711ee90 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java @@ -70,6 +70,7 @@ import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder; import org.bouncycastle.pqc.crypto.lms.HSSKeyGenerationParameters; import org.bouncycastle.pqc.crypto.lms.HSSKeyPairGenerator; +import org.bouncycastle.pqc.crypto.lms.HSSPublicKeyParameters; import org.bouncycastle.pqc.crypto.lms.LMOtsParameters; import org.bouncycastle.pqc.crypto.lms.LMSKeyGenerationParameters; import org.bouncycastle.pqc.crypto.lms.LMSKeyPairGenerator; @@ -382,7 +383,7 @@ public void testLmsEncapsulated() } } - public void testCheckCreationHssLms() + public void testCheckCreationHss() throws Exception { // @@ -457,6 +458,79 @@ public void testCheckCreationHssLms() //System.out.println(cert); } + public void testCheckCreationLms() + throws Exception + { + // + // set up the keys + // + AsymmetricKeyParameter privKey; + AsymmetricKeyParameter pubKey; + + AsymmetricCipherKeyPairGenerator kpg = new LMSKeyPairGenerator(); + + kpg.init(new LMSKeyGenerationParameters( + new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4), new SecureRandom())); + + AsymmetricCipherKeyPair pair = kpg.generateKeyPair(); + + privKey = (AsymmetricKeyParameter)pair.getPrivate(); + pubKey = (AsymmetricKeyParameter)pair.getPublic(); + + // + // distinguished name table. + // + X500NameBuilder builder = new X500NameBuilder(RFC4519Style.INSTANCE); + + builder.addRDN(RFC4519Style.c, "AU"); + builder.addRDN(RFC4519Style.o, "The Legion of the Bouncy Castle"); + builder.addRDN(RFC4519Style.l, "Melbourne"); + builder.addRDN(RFC4519Style.st, "Victoria"); + builder.addRDN(PKCSObjectIdentifiers.pkcs_9_at_emailAddress, "feedback-crypto@bouncycastle.org"); + + // + // extensions + // + + // + // create the certificate - version 3 + // + ContentSigner sigGen = new BcHssLmsContentSignerBuilder().build(privKey); + X509v3CertificateBuilder certGen = new BcX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey); + + + X509CertificateHolder cert = certGen.build(sigGen); + + assertTrue(cert.isValidOn(new Date())); + + assertTrue(cert.isSignatureValid(new BcHssLmsContentVerifierProviderBuilder().build(pubKey))); + + + // + // create the certificate - version 1 + // + + sigGen = new BcHssLmsContentSignerBuilder().build(privKey); + X509v1CertificateBuilder certGen1 = new BcX509v1CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey); + + cert = certGen1.build(sigGen); + + assertTrue(cert.isValidOn(new Date())); + + assertTrue(cert.isSignatureValid(new BcHssLmsContentVerifierProviderBuilder().build(pubKey))); + + AsymmetricKeyParameter certPubKey = ((HSSPublicKeyParameters)PublicKeyFactory.createKey(cert.getSubjectPublicKeyInfo())).getLMSPublicKey(); + + assertTrue(cert.isSignatureValid(new BcHssLmsContentVerifierProviderBuilder().build(certPubKey))); + + ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded()); + CertificateFactory fact = CertificateFactory.getInstance("X.509"); + + X509Certificate x509cert = (X509Certificate)fact.generateCertificate(bIn); + + //System.out.println(new String(cert.getEncoded())); + } + public void testTryLmsSettings() throws Exception { From 1f1e6d8df1dc9dead2ca8893666d0d512764fac2 Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 17 Feb 2025 15:24:47 +1030 Subject: [PATCH 252/890] Change LMSParameters[] in testCheckCreationHss --- .../java/org/bouncycastle/cms/test/PQCSignedDataTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java index 959711ee90..2217c5b806 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java @@ -395,7 +395,8 @@ public void testCheckCreationHss() AsymmetricCipherKeyPairGenerator kpg = new HSSKeyPairGenerator(); kpg.init(new HSSKeyGenerationParameters( - new LMSParameters[]{new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4)}, new SecureRandom())); + new LMSParameters[]{new LMSParameters(LMSigParameters.lms_sha256_n32_h5, LMOtsParameters.sha256_n32_w4), + new LMSParameters(LMSigParameters.lms_sha256_n24_h5, LMOtsParameters.sha256_n24_w4)}, new SecureRandom())); AsymmetricCipherKeyPair pair = kpg.generateKeyPair(); @@ -420,8 +421,6 @@ public void testCheckCreationHss() // // create the certificate - version 3 // - - ContentSigner sigGen = new BcHssLmsContentSignerBuilder().build(privKey); X509v3CertificateBuilder certGen = new BcX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey); @@ -436,7 +435,6 @@ public void testCheckCreationHss() // // create the certificate - version 1 // - sigGen = new BcHssLmsContentSignerBuilder().build(privKey); X509v1CertificateBuilder certGen1 = new BcX509v1CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey); From 9e25ed938d363cd75a61053620208117fed2841f Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 18 Feb 2025 10:58:32 +1030 Subject: [PATCH 253/890] Add javadoc for BcHssLmsContentSignerBuilder and BcHssLmsContentVerifierProviderBuilder. --- .../operator/bc/BcHssLmsContentSignerBuilder.java | 7 +++++++ .../bc/BcHssLmsContentVerifierProviderBuilder.java | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java b/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java index 02e7d97c7f..ac31c39883 100644 --- a/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java +++ b/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentSignerBuilder.java @@ -17,6 +17,12 @@ import org.bouncycastle.pqc.crypto.lms.LMSPublicKeyParameters; import org.bouncycastle.pqc.crypto.lms.LMSSigner; +/** + * Builder for creating content signers that use the HSS/LMS Hash-Based Signature Algorithm. + * + * Reference: Use of the HSS/LMS Hash-Based Signature Algorithm in the Cryptographic Message Syntax (CMS) + * RFC 9708. + */ public class BcHssLmsContentSignerBuilder extends BcContentSignerBuilder { @@ -58,6 +64,7 @@ else if (param instanceof LMSPublicKeyParameters || param instanceof LMSPrivateK { throw new IllegalArgumentException("Incorrect Key Parameters"); } + signer.init(forSigning, param); } diff --git a/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentVerifierProviderBuilder.java b/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentVerifierProviderBuilder.java index 29ad2491b5..de09b1aec0 100644 --- a/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentVerifierProviderBuilder.java +++ b/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentVerifierProviderBuilder.java @@ -9,6 +9,13 @@ import org.bouncycastle.crypto.util.PublicKeyFactory; import org.bouncycastle.operator.OperatorCreationException; +/** + * Builder for creating content verifier providers that support the HSS/LMS Hash-Based Signature Algorithm. + * + * Reference: Use of the HSS/LMS Hash-Based Signature Algorithm in the Cryptographic Message Syntax (CMS) + * RFC 9708. + *

    + */ public class BcHssLmsContentVerifierProviderBuilder extends BcContentVerifierProviderBuilder { From 316c2a4af71fa41a7091b3f1a856b156ec6c2b9a Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 24 Mar 2025 15:06:34 +1100 Subject: [PATCH 254/890] removed dependency on PQC PublicKeyFactory. --- .../bouncycastle/crypto/util/PublicKeyFactory.java | 3 +-- .../bc/BcHssLmsContentVerifierProviderBuilder.java | 2 +- .../org/bouncycastle/cms/test/PQCSignedDataTest.java | 11 ++--------- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java b/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java index bc95a90f77..0a5a1189a4 100644 --- a/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java @@ -165,8 +165,7 @@ public static AsymmetricKeyParameter createKey(SubjectPublicKeyInfo keyInfo, Obj SubjectPublicKeyInfoConverter converter = (SubjectPublicKeyInfoConverter)converters.get(algID.getAlgorithm()); if (null == converter) { - return org.bouncycastle.pqc.crypto.util.PublicKeyFactory.createKey(keyInfo, defaultParams); - //throw new IOException("algorithm identifier in public key not recognised: " + algID.getAlgorithm()); + throw new IOException("algorithm identifier in public key not recognised: " + algID.getAlgorithm()); } return converter.getPublicKeyParameters(keyInfo, defaultParams); diff --git a/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentVerifierProviderBuilder.java b/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentVerifierProviderBuilder.java index de09b1aec0..e552ffa6d8 100644 --- a/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentVerifierProviderBuilder.java +++ b/pkix/src/main/java/org/bouncycastle/operator/bc/BcHssLmsContentVerifierProviderBuilder.java @@ -6,8 +6,8 @@ import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.crypto.Signer; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; -import org.bouncycastle.crypto.util.PublicKeyFactory; import org.bouncycastle.operator.OperatorCreationException; +import org.bouncycastle.pqc.crypto.util.PublicKeyFactory; /** * Builder for creating content verifier providers that support the HSS/LMS Hash-Based Signature Algorithm. diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java index 2217c5b806..414b5b4c9f 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/PQCSignedDataTest.java @@ -52,18 +52,11 @@ import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; -import org.bouncycastle.crypto.generators.DSAKeyPairGenerator; -import org.bouncycastle.crypto.generators.DSAParametersGenerator; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; -import org.bouncycastle.crypto.params.DSAKeyGenerationParameters; -import org.bouncycastle.crypto.params.DSAParameters; -import org.bouncycastle.crypto.util.PublicKeyFactory; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.ContentSigner; import org.bouncycastle.operator.DigestCalculatorProvider; import org.bouncycastle.operator.OperatorCreationException; -import org.bouncycastle.operator.bc.BcDSAContentSignerBuilder; -import org.bouncycastle.operator.bc.BcDSAContentVerifierProviderBuilder; import org.bouncycastle.operator.bc.BcHssLmsContentSignerBuilder; import org.bouncycastle.operator.bc.BcHssLmsContentVerifierProviderBuilder; import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; @@ -444,7 +437,7 @@ public void testCheckCreationHss() assertTrue(cert.isSignatureValid(new BcHssLmsContentVerifierProviderBuilder().build(pubKey))); - AsymmetricKeyParameter certPubKey = PublicKeyFactory.createKey(cert.getSubjectPublicKeyInfo()); + AsymmetricKeyParameter certPubKey = org.bouncycastle.pqc.crypto.util.PublicKeyFactory.createKey(cert.getSubjectPublicKeyInfo()); assertTrue(cert.isSignatureValid(new BcHssLmsContentVerifierProviderBuilder().build(certPubKey))); @@ -517,7 +510,7 @@ public void testCheckCreationLms() assertTrue(cert.isSignatureValid(new BcHssLmsContentVerifierProviderBuilder().build(pubKey))); - AsymmetricKeyParameter certPubKey = ((HSSPublicKeyParameters)PublicKeyFactory.createKey(cert.getSubjectPublicKeyInfo())).getLMSPublicKey(); + AsymmetricKeyParameter certPubKey = ((HSSPublicKeyParameters)org.bouncycastle.pqc.crypto.util.PublicKeyFactory.createKey(cert.getSubjectPublicKeyInfo())).getLMSPublicKey(); assertTrue(cert.isSignatureValid(new BcHssLmsContentVerifierProviderBuilder().build(certPubKey))); From cff8aa0f89d31d6e9f16c111500322ae46f8de9d Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 24 Mar 2025 17:17:50 +1030 Subject: [PATCH 255/890] TODO: signDigestCore --- .../pqc/crypto/snova/GF16Matrix.java | 194 ++++++++-------- .../pqc/crypto/snova/GF16Utils.java | 69 +++--- .../pqc/crypto/snova/PublicKeyExpanded.java | 38 ++-- .../pqc/crypto/snova/SnovaKeyElements.java | 42 ++++ .../pqc/crypto/snova/SnovaSigner.java | 213 +++++++++++++++--- .../pqc/crypto/test/TestUtils.java | 51 +++-- 6 files changed, 410 insertions(+), 197 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Matrix.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Matrix.java index c929e33f5e..a08aa7a122 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Matrix.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Matrix.java @@ -1,105 +1,105 @@ -package org.bouncycastle.pqc.crypto.snova; - -class GF16Matrix -{ - private final byte[][] data; - private final int rank; - - public GF16Matrix(int rank) - { - this.rank = rank; - this.data = new byte[rank][rank]; - } - - public void set(int x, int y, byte value) - { - data[x][y] = (byte)(value & 0xF); - } - - public byte get(int x, int y) - { - return data[x][y]; - } - - public void add(GF16Matrix other) - { -// for (int i = 0; i < size; i++) +//package org.bouncycastle.pqc.crypto.snova; +// +//class GF16Matrix +//{ +// private final byte[][] data; +// private final int rank; +// +// public GF16Matrix(int rank) +// { +// this.rank = rank; +// this.data = new byte[rank][rank]; +// } +// +// public void set(int x, int y, byte value) +// { +// data[x][y] = (byte)(value & 0xF); +// } +// +// public byte get(int x, int y) +// { +// return data[x][y]; +// } +// +//// public void add(GF16Matrix other) +//// { +////// for (int i = 0; i < size; i++) +////// { +////// for (int j = 0; j < size; j++) +////// { +////// data[i][j] = add(data[i][j], other.data[i][j]); +////// } +////// } +//// } +// +// public void mul(GF16Matrix a, GF16Matrix b) +// { +// byte[][] temp = new byte[rank][rank]; +// for (int i = 0; i < rank; i++) // { -// for (int j = 0; j < size; j++) +// for (int j = 0; j < rank; j++) // { -// data[i][j] = add(data[i][j], other.data[i][j]); +// byte sum = 0; +//// for (int k = 0; k < size; k++) +//// { +//// sum = add(sum, mul(a.data[i][k], b.data[k][j])); +//// } +// temp[i][j] = sum; // } // } - } - - public void mul(GF16Matrix a, GF16Matrix b) - { - byte[][] temp = new byte[rank][rank]; - for (int i = 0; i < rank; i++) - { - for (int j = 0; j < rank; j++) - { - byte sum = 0; -// for (int k = 0; k < size; k++) -// { -// sum = add(sum, mul(a.data[i][k], b.data[k][j])); -// } - temp[i][j] = sum; - } - } - System.arraycopy(temp, 0, data, 0, temp.length); - } - - public void scale(byte scalar) - { -// for (int i = 0; i < size; i++) +// System.arraycopy(temp, 0, data, 0, temp.length); +// } +// +// public void scale(byte scalar) +// { +//// for (int i = 0; i < size; i++) +//// { +//// for (int j = 0; j < size; j++) +//// { +//// data[i][j] = mul(data[i][j], scalar); +//// } +//// } +// } +// +// public void transpose() +// { +// byte[][] temp = new byte[rank][rank]; +// for (int i = 0; i < rank; i++) // { -// for (int j = 0; j < size; j++) +// for (int j = 0; j < rank; j++) // { -// data[i][j] = mul(data[i][j], scalar); +// temp[j][i] = data[i][j]; // } // } - } - - public void transpose() - { - byte[][] temp = new byte[rank][rank]; - for (int i = 0; i < rank; i++) - { - for (int j = 0; j < rank; j++) - { - temp[j][i] = data[i][j]; - } - } - System.arraycopy(temp, 0, data, 0, temp.length); - } - - public void makeInvertible() - { - // Implementation of be_invertible_by_add_aS - GF16Matrix temp = new GF16Matrix(rank); - if (determinant() == 0) - { - for (byte a = 1; a < 16; a++) - { - temp.scale(a); - add(temp); - if (determinant() != 0) - { - return; - } - } - } - } - - private byte determinant() - { - // Simplified determinant calculation for small matrices -// if (rank == 2) -// { -// return add(mul(data[0][0], data[1][1]), mul(data[0][1], data[1][0])); -// } - // Add implementations for larger matrices as needed - throw new UnsupportedOperationException("Determinant for size " + rank + " not implemented"); - } -} \ No newline at end of file +// System.arraycopy(temp, 0, data, 0, temp.length); +// } +// +//// public void makeInvertible() +//// { +//// // Implementation of be_invertible_by_add_aS +//// GF16Matrix temp = new GF16Matrix(rank); +//// if (determinant() == 0) +//// { +//// for (byte a = 1; a < 16; a++) +//// { +//// temp.scale(a); +//// add(temp); +//// if (determinant() != 0) +//// { +//// return; +//// } +//// } +//// } +//// } +// +//// private byte determinant() +//// { +//// // Simplified determinant calculation for small matrices +////// if (rank == 2) +////// { +////// return add(mul(data[0][0], data[1][1]), mul(data[0][1], data[1][0])); +////// } +//// // Add implementations for larger matrices as needed +//// throw new UnsupportedOperationException("Determinant for size " + rank + " not implemented"); +//// } +//} \ No newline at end of file diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java index 539bd649fa..65c9025155 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java @@ -143,6 +143,21 @@ public static void decode(byte[] input, int inputOffset, byte[] output, int mdec } } + public static void decodeMergeInHalf(byte[] byteArray, byte[] gf16Array, int nGf16) + { + int i, half = (nGf16 + 1) >>> 1; + // Process pairs of 4-bit values + for (i = 0; i < half; i++) + { + gf16Array[i] = (byte)(byteArray[i] & 0x0F); + } + // If there is an extra nibble (odd number of nibbles), store it directly in lower 4 bits. + for (i = 0; i < nGf16 >>> 1; i++) + { + gf16Array[i + half] = (byte)((byteArray[i] >>> 4) & 0x0F); + } + } + public static void gf16mMul(byte[] a, byte[] b, byte[] c, int rank) { @@ -204,34 +219,34 @@ public static byte inv(byte a) return INV4B[a & 0xF]; } - static GF16Matrix[][][] create3DArray(int d1, int d2, int d3, int rank) - { - GF16Matrix[][][] arr = new GF16Matrix[d1][d2][d3]; - for (int i = 0; i < d1; i++) - { - for (int j = 0; j < d2; j++) - { - for (int k = 0; k < d3; k++) - { - arr[i][j][k] = new GF16Matrix(rank); - } - } - } - return arr; - } +// static GF16Matrix[][][] create3DArray(int d1, int d2, int d3, int rank) +// { +// GF16Matrix[][][] arr = new GF16Matrix[d1][d2][d3]; +// for (int i = 0; i < d1; i++) +// { +// for (int j = 0; j < d2; j++) +// { +// for (int k = 0; k < d3; k++) +// { +// arr[i][j][k] = new GF16Matrix(rank); +// } +// } +// } +// return arr; +// } - static GF16Matrix[][] create2DArray(int d1, int d2, int rank) - { - GF16Matrix[][] arr = new GF16Matrix[d1][d2]; - for (int i = 0; i < d1; i++) - { - for (int j = 0; j < d2; j++) - { - arr[i][j] = new GF16Matrix(rank); - } - } - return arr; - } +// static GF16Matrix[][] create2DArray(int d1, int d2, int rank) +// { +// GF16Matrix[][] arr = new GF16Matrix[d1][d2]; +// for (int i = 0; i < d1; i++) +// { +// for (int j = 0; j < d2; j++) +// { +// arr[i][j] = new GF16Matrix(rank); +// } +// } +// return arr; +// } private static final int GF16_MASK = 0x249; // Mask for GF(2^4) reduction diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpanded.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpanded.java index 90ab90ecca..950f103f40 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpanded.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpanded.java @@ -1,19 +1,19 @@ -package org.bouncycastle.pqc.crypto.snova; - -class PublicKeyExpanded -{ - public final byte[] publicKeySeed; - public final GF16Matrix[][][] P22; // [m][o][o] - public final MapGroup1 map1; - - public PublicKeyExpanded(SnovaParameters params) - { - int m = params.getM(); - int o = params.getO(); - int rank = params.getL(); - - publicKeySeed = new byte[SnovaKeyPairGenerator.publicSeedLength]; - P22 = GF16Utils.create3DArray(m, o, o, rank); - map1 = new MapGroup1(params); - } -} +//package org.bouncycastle.pqc.crypto.snova; +// +//class PublicKeyExpanded +//{ +// public final byte[] publicKeySeed; +// public final GF16Matrix[][][] P22; // [m][o][o] +// public final MapGroup1 map1; +// +// public PublicKeyExpanded(SnovaParameters params) +// { +// int m = params.getM(); +// int o = params.getO(); +// int rank = params.getL(); +// +// publicKeySeed = new byte[SnovaKeyPairGenerator.publicSeedLength]; +// P22 = GF16Utils.create3DArray(m, o, o, rank); +// map1 = new MapGroup1(params); +// } +//} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java index 69fd8abba7..5d8a69e3c9 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java @@ -8,6 +8,7 @@ class SnovaKeyElements public final byte[][][] T12; // [v][o] public final MapGroup2 map2; public final PublicKey publicKey; + public byte[] ptPrivateKeySeed; private final int length; byte[] fixedAbq; @@ -75,6 +76,25 @@ public void encodeMergerInHalf(byte[] output) GF16Utils.encodeMergeInHalf(input, length, output); } + public void skUnpack(byte[] input) + { + byte[] tmp = new byte[input.length << 1]; + GF16Utils.decodeMergeInHalf(input, tmp, tmp.length); + int inOff = 0; + inOff = copy3d(tmp, inOff, map1.aAlpha); + inOff = copy3d(tmp, inOff, map1.bAlpha); + inOff = copy3d(tmp, inOff, map1.qAlpha1); + inOff = copy3d(tmp, inOff, map1.qAlpha2); + inOff = copy3d(tmp, inOff, T12); + inOff = copy4d(tmp, inOff, map2.f11); + inOff = copy4d(tmp, inOff, map2.f12); + inOff = copy4d(tmp, inOff, map2.f21); + System.arraycopy(tmp, inOff, publicKey.publicKeySeed, 0, publicKey.publicKeySeed.length); + inOff += publicKey.publicKeySeed.length; + ptPrivateKeySeed = new byte[SnovaKeyPairGenerator.privateSeedLength]; + System.arraycopy(tmp, inOff, ptPrivateKeySeed, 0, ptPrivateKeySeed.length); + } + public int copy3d(byte[][][] alpha, byte[] output, int outOff) { for (int i = 0; i < alpha.length; ++i) @@ -96,4 +116,26 @@ public int copy4d(byte[][][][] alpha, byte[] output, int outOff) } return outOff; } + + public int copy3d(byte[] input, int inOff, byte[][][] alpha) + { + for (int i = 0; i < alpha.length; ++i) + { + for (int j = 0; j < alpha[i].length; ++j) + { + System.arraycopy(input, inOff, alpha[i][j], 0, alpha[i][j].length); + inOff += alpha[i][j].length; + } + } + return inOff; + } + + public int copy4d(byte[] input, int inOff, byte[][][][] alpha) + { + for (int i = 0; i < alpha.length; ++i) + { + inOff = copy3d(alpha[i], input, inOff); + } + return inOff; + } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index d9fa84ae2c..fb69b15ba9 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -49,6 +49,7 @@ public void init(boolean forSigning, CipherParameters param) privKey = null; random = null; } + engine = new SnovaEngine(params); } @Override @@ -67,6 +68,19 @@ public void update(byte[] in, int off, int len) public byte[] generateSignature() throws CryptoException, DataLengthException { + byte[] hash = new byte[digest.getDigestSize()]; + digest.doFinal(hash, 0); + byte[] salt = new byte[16]; + random.nextBytes(salt); + if (params.isSkIsSeed()) + { + + } + else + { + SnovaKeyElements esk = new SnovaKeyElements(params, engine); + esk.skUnpack(privKey.getPrivateKey()); + } return new byte[0]; } @@ -114,6 +128,7 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, { // Initialize constants from parameters final int m = params.getM(); + final int l = params.getL(); final int lsq = params.getLsq(); final int alpha = params.getAlpha(); final int v = params.getV(); @@ -127,9 +142,9 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, byte[][] Temp = new byte[lsq][lsq]; byte[] solution = new byte[m * lsq]; - byte[][][] Left = new byte[m][alpha][v]; - byte[][][] Right = new byte[m][alpha][v]; - byte[][] XInGF16Matrix = new byte[n][lsq]; + byte[][][][][] Left = new byte[m][alpha][v][l][l]; + byte[][][][][] Right = new byte[m][alpha][v][l][l]; + byte[][][] XInGF16Matrix = new byte[n][l][l]; byte[][] FvvGF16Matrix = new byte[m][lsq]; byte[] hashInGF16 = new byte[m * lsq]; byte[][] signatureGF16Matrix = new byte[n][lsq]; @@ -138,8 +153,8 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, byte[] vinegarBytes = new byte[(v * lsq + 1) / 2]; // Temporary matrices - byte[] gf16mTemp0 = new byte[lsq]; - byte[] gf16mTemp1 = new byte[lsq]; + byte[][] gf16mTemp0 = new byte[l][l]; + byte[][] gf16mTemp1 = new byte[l][l]; byte[] gf16mSecretTemp0 = new byte[lsq]; int flagRedo; @@ -155,7 +170,7 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, // Initialize Gauss matrix Arrays.stream(Gauss).forEach(row -> Arrays.fill(row, (byte)0)); numSign++; - flagRedo = 0; + //flagRedo = 0; // Fill last column of Gauss matrix for (int i = 0; i < m * lsq; i++) @@ -168,9 +183,11 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, shake.update(ptPrivateKeySeed, 0, ptPrivateKeySeed.length); shake.update(digest, 0, digest.length); shake.update(arraySalt, 0, arraySalt.length); - shake.update(new byte[]{numSign}, 0, 1); + shake.update(numSign); shake.doFinal(vinegarBytes, 0, vinegarBytes.length); - //GF16Utils.decode(vinegarBytes, 0, XInGF16Matrix, 0, v * lsq); + byte[] tmp = new byte[vinegarBytes.length << 1]; + GF16Utils.decode(vinegarBytes, tmp, tmp.length); + MapGroup1.fillAlpha(tmp, 0, XInGF16Matrix, tmp.length); // Evaluate vinegar part of central map for (int mi = 0; mi < m; mi++) @@ -180,11 +197,11 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, for (int idx = 0; idx < v; idx++) { transposeGF16Matrix(XInGF16Matrix[idx], gf16mTemp0); -// multiplyGF16Matrices(gf16mTemp0, Qalpha1[mi][a], gf16mTemp1); -// multiplyGF16Matrices(Aalpha[mi][a], gf16mTemp1, Left[mi][a][idx]); -// -// multiplyGF16Matrices(Qalpha2[mi][a], XInGF16Matrix[idx], gf16mTemp1); -// multiplyGF16Matrices(gf16mTemp1, Balpha[mi][a], Right[mi][a][idx]); + multiplyGF16Matrices(gf16mTemp0, Qalpha1[mi][a], gf16mTemp1); + multiplyGF16Matrices(Aalpha[mi][a], gf16mTemp1, Left[mi][a][idx]); + + multiplyGF16Matrices(Qalpha2[mi][a], XInGF16Matrix[idx], gf16mTemp1); + multiplyGF16Matrices(gf16mTemp1, Balpha[mi][a], Right[mi][a][idx]); } } } @@ -200,9 +217,9 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, { for (int k = 0; k < v; k++) { -// multiplyGF16Matrices(Left[mi][a][j], F11[miPrime][j][k], gf16mTemp0); -// multiplyGF16Matrices(gf16mTemp0, Right[mi][a][k], gf16mTemp1); - //GF16Utils.addGF16Matrices(FvvGF16Matrix[mi], gf16mTemp1, FvvGF16Matrix[mi]); + multiplyGF16Matrices(Left[mi][a][j], F11[miPrime][j][k], gf16mTemp0); + multiplyGF16Matrices(gf16mTemp0, Right[mi][a][k], gf16mTemp1); + addGF16Matrices(FvvGF16Matrix[mi], gf16mTemp1, FvvGF16Matrix[mi]); } } } @@ -231,62 +248,186 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, while (flagRedo != 0); // Build final signature - buildSignature(XInGF16Matrix, signatureGF16Matrix, T12, v, o, lsq); - convertGF16MatrixToBytes(ptSignature, signatureGF16Matrix, n * lsq); - //System.arraycopy(arraySalt, 0, ptSignature, params.getBytesSignature(), bytesSalt); + //buildSignature(XInGF16Matrix, signatureGF16Matrix, T12, v, o, lsq); + //void buildSignature(byte[][][] XIn, byte[][] signature, + // byte[][][][] T12, int v, int o, int lsq) + // Copy vinegar variables + for (int idx = 0; idx < v; idx++) + { + System.arraycopy(XInGF16Matrix[idx], 0, signatureGF16Matrix[idx], 0, lsq); + } + + // Process oil variables with T12 matrix + for (int idx = 0; idx < v; idx++) + { + for (int i = 0; i < o; i++) + { + multiplyGF16Matrices(T12[idx][i], XInGF16Matrix[v + i], gf16mTemp0); + addGF16Matrices(signatureGF16Matrix[idx], gf16mTemp0, signatureGF16Matrix[idx]); + } + } + + // Copy remaining oil variables + for (int idx = 0; idx < o; idx++) + { + System.arraycopy(XInGF16Matrix[v + idx], 0, signatureGF16Matrix[v + idx], 0, lsq); + } + + // Convert to packed bytes + int bytePos = 0; + for (int matIdx = 0; matIdx < signatureGF16Matrix.length; matIdx++) + { + for (int i = 0; i < l; i++) + { + for (int j = 0; j < l; j++) + { + int nibblePos = bytePos % 2; + if (nibblePos == 0) + { + ptSignature[bytePos / 2] = (byte)(engine.getGF16m(signatureGF16Matrix[matIdx], i, j) << 4); + } + else + { + ptSignature[bytePos / 2] |= engine.getGF16m(signatureGF16Matrix[matIdx], i, j) & 0x0F; + } + bytePos++; + } + } + } + byte[] tmp = new byte[n * lsq]; + for (int i = 0; i < signatureGF16Matrix.length; ++i) + { + System.arraycopy(signatureGF16Matrix[i], 0, tmp, 0, signatureGF16Matrix[i].length); + } + GF16Utils.encode(tmp, ptSignature, 0, tmp.length); + System.arraycopy(arraySalt, 0, ptSignature, 0, bytesSalt); // Clear sensitive data Arrays.fill(gf16mSecretTemp0, (byte)0); } - private void transposeGF16Matrix(byte[] src, byte[] dest) + private void transposeGF16Matrix(byte[][] src, byte[][] dest) { for (int i = 0; i < params.getL(); i++) { for (int j = 0; j < params.getL(); j++) { - engine.setGF16m(dest, i, j, engine.getGF16m(src, j, i)); + dest[i][j] = src[j][i]; } } } - private void multiplyGF16Matrices(byte[] a, byte[] b, byte[] result) + private void multiplyGF16Matrices(byte[][] a, byte[][] b, byte[][] result) { - Arrays.fill(result, (byte)0); for (int i = 0; i < params.getL(); i++) { + Arrays.fill(result[i], (byte)0); for (int j = 0; j < params.getL(); j++) { byte sum = 0; for (int k = 0; k < params.getL(); k++) { sum = GF16Utils.add(sum, GF16Utils.mul( - engine.getGF16m(a, i, k), - engine.getGF16m(b, k, j) + a[i][k], + b[k][j] )); } - engine.setGF16m(result, i, j, sum); + result[i][j] = sum; } } } - private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size) + private void multiplyGF16Matrices(byte[][] a, byte[] b, byte[][] result) { - // Implementation of Gaussian elimination with GF16 arithmetic - // ... (similar structure to C code's elimination steps) - return 0; // Return 0 if successful, 1 if needs redo + for (int i = 0; i < params.getL(); i++) + { + Arrays.fill(result[i], (byte)0); + for (int j = 0; j < params.getL(); j++) + { + byte sum = 0; + for (int k = 0; k < params.getL(); k++) + { + sum = GF16Utils.add(sum, GF16Utils.mul( + a[i][k], + engine.getGF16m(b, k, j) + )); + } + result[i][j] = sum; + } + } } - private void buildSignature(byte[][] XIn, byte[][] signature, - byte[][][][] T12, int v, int o, int lsq) + private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size) { - // Implementation of signature construction - // ... (similar to C code's final matrix operations) + final int cols = size + 1; + byte tGF16; + + for (int i = 0; i < size; i++) + { + // Find pivot + int pivot = i; + while (pivot < size && Gauss[pivot][i] == 0) + { + pivot++; + } + + // Check for singularity + if (pivot >= size) + { + return 1; // Flag for redo + } + + // Swap rows if needed + if (pivot != i) + { + byte[] tempRow = Gauss[i]; + Gauss[i] = Gauss[pivot]; + Gauss[pivot] = tempRow; + } + + // Normalize pivot row + byte invPivot = GF16Utils.inv(Gauss[i][i]); + for (int j = i; j < cols; j++) + { + Gauss[i][j] = GF16Utils.mul(Gauss[i][j], invPivot); + } + + // Eliminate below + for (int j = i + 1; j < size; j++) + { + byte factor = Gauss[j][i]; + if (factor != 0) + { + for (int k = i; k < cols; k++) + { + Gauss[j][k] = GF16Utils.add(Gauss[j][k], GF16Utils.mul(Gauss[i][k], factor)); + } + } + } + } + + // Back substitution + for (int i = size - 1; i >= 0; i--) + { + solution[i] = Gauss[i][size]; + for (int j = i + 1; j < size; j++) + { + solution[i] = GF16Utils.add(solution[i], GF16Utils.mul(Gauss[i][j], solution[j])); + } + } + + return 0; } - private void convertGF16MatrixToBytes(byte[] output, byte[][] matrix, int totalElements) + private void addGF16Matrices(byte[] a, byte[][] b, byte[] result) { - // Conversion implementation using GF16Utils.encode + for (int i = 0; i < b.length; i++) + { + for (int j = 0; j < b[i].length; ++j) + { + engine.setGF16m(result, i, j, GF16Utils.add(engine.getGF16m(a, i, j), b[i][j])); + } + } } private int iPrime(int mi, int alpha) diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java index 986de28ed2..3fbacf3049 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java @@ -1,12 +1,10 @@ package org.bouncycastle.pqc.crypto.test; import java.io.BufferedReader; -import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.security.SecureRandom; import java.util.HashMap; -import java.util.Map; import junit.framework.Assert; @@ -70,7 +68,7 @@ public static void testTestVector(boolean enableFactory, boolean isSigner, Strin { if (buf.size() > 0) { -// int count = Integer.parseInt(buf.get("count")); + int count = Integer.parseInt(buf.get("count")); // if (count == 99) // { // System.out.println("break"); @@ -103,7 +101,24 @@ public static void testTestVector(boolean enableFactory, boolean isSigner, Strin pubParams = kp.getPublic(); privParams = kp.getPrivate(); } - +// byte[] pk2 = operation.getPublicKeyEncoded(pubParams); +// for (int i = 0; i < pk2.length; ++i) +// { +// if (pk[i] != pk2[i]) +// { +// System.out.println(i + " " + pk[i] + " " + pk2[i]); +// } +// } +// +// byte[] sk2 = operation.getPrivateKeyEncoded(privParams); +// System.out.println(new String(Hex.encode(sk2))); +// for (int i = 0; i < sk2.length; ++i) +// { +// if (sk[i] != sk2[i]) +// { +// System.out.println(i + " " + sk[i] + " " + sk2[i]); +// } +// } Assert.assertTrue(name + ": public key", Arrays.areEqual(pk, operation.getPublicKeyEncoded(pubParams))); Assert.assertTrue(name + ": secret key", Arrays.areEqual(sk, operation.getPrivateKeyEncoded(privParams))); @@ -125,20 +140,20 @@ public static void testTestVector(boolean enableFactory, boolean isSigner, Strin Assert.assertTrue(Arrays.areEqual(sigGenerated, signature)); - if (isSigner) - { - Signer signer = operation.getSigner(); - signer.init(false, pubParams); - signer.update(message, 0, message.length); - Assert.assertTrue(signer.verifySignature(sigGenerated)); - } - else - { - MessageSigner signer = operation.getMessageSigner(); - signer.init(false, pubParams); - Assert.assertTrue(signer.verifySignature(message, sigGenerated)); - } - //System.out.println("Count " + count + " pass"); +// if (isSigner) +// { +// Signer signer = operation.getSigner(); +// signer.init(false, pubParams); +// signer.update(message, 0, message.length); +// Assert.assertTrue(signer.verifySignature(sigGenerated)); +// } +// else +// { +// MessageSigner signer = operation.getMessageSigner(); +// signer.init(false, pubParams); +// Assert.assertTrue(signer.verifySignature(message, sigGenerated)); +// } +// System.out.println("Count " + count + " pass"); } buf.clear(); continue; From e2df273fb3e7c801ac7bff870532cb0248ff74a8 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 24 Mar 2025 15:23:23 +0700 Subject: [PATCH 256/890] Grain128AEADEngine: fix DER length encoding for AAD lengths > 255 --- .../crypto/engines/Grain128AEADEngine.java | 57 ++++++++----------- .../crypto/test/Grain128AEADTest.java | 17 ++---- 2 files changed, 27 insertions(+), 47 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java index ff32602bfa..d81fd59045 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java @@ -239,43 +239,49 @@ protected void processBufferAAD(byte[] input, int inOff) @Override protected void processFinalAAD() { + // Encode(ad length) denotes the message length encoded in the DER format. + int len = aadOperator.getLen(); byte[] input = ((StreamAADOperator)aadOperator).getBytes(); - byte[] ader; - //encodeDer + // Need up to 5 bytes for the DER length as an 'int' + byte[] ader = new byte[5]; + + int pos; if (len < 128) { - ader = new byte[1]; - ader[0] = (byte)len; + pos = ader.length - 1; + ader[pos] = (byte)len; } else { - // aderlen is the highest bit position divided by 8 - int aderlen = len_length(len); - ader = new byte[1 + aderlen]; - ader[0] = (byte)(0x80 | aderlen); - int tmp = len; - for (int i = 1; i < ader.length; ++i) + pos = ader.length; + + int dl = len; + do { - ader[i] = (byte)tmp; - tmp >>>= 8; + ader[--pos] = (byte)dl; + dl >>>= 8; } + while (dl != 0); + + int count = ader.length - pos; + ader[--pos] = (byte)(0x80 | count); } - absorbAadData(ader, ader.length); - absorbAadData(input, len); + absorbAadData(ader, pos, ader.length - pos); + absorbAadData(input, 0, len); } - private void absorbAadData(byte[] ader, int len) + private void absorbAadData(byte[] buf, int off, int len) { for (int i = 0; i < len; ++i) { - byte ader_i = ader[i]; + byte b = buf[off + i]; for (int j = 0; j < 8; ++j) { shift(); - updateInternalState((ader_i >> j) & 1); + updateInternalState((b >> j) & 1); } } } @@ -319,21 +325,4 @@ protected void processBufferDecrypt(byte[] input, int inOff, byte[] output, int output[outOff + i] = cc; } } - - private static int len_length(int v) - { - if ((v & 0xff) == v) - { - return 1; - } - if ((v & 0xffff) == v) - { - return 2; - } - if ((v & 0xffffff) == v) - { - return 3; - } - return 4; - } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java index bc753137bc..89da222118 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java @@ -228,17 +228,8 @@ static void isEqualTo( } } -// public static void main(String[] args) -// { -// runTest(new AsconTest()); -// runTest(new ElephantTest()); -// runTest(new GiftCofbTest()); -// runTest(new Grain128AEADTest()); -// runTest(new ISAPTest()); -// runTest(new PhotonBeetleTest()); -// runTest(new RomulusTest()); -// runTest(new SparkleTest()); -// runTest(new XoodyakTest()); -// } + public static void main(String[] args) + { + runTest(new Grain128AEADTest()); + } } - From d02fceb9e3e7cbcfbf0f6f26171e6c6ed90a76f1 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 24 Mar 2025 22:50:12 +0700 Subject: [PATCH 257/890] Ascon: more thorough multi-output XOF tests --- .../bouncycastle/crypto/test/AsconTest.java | 62 +++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java b/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java index 27f8e35fb1..d92c10575d 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java @@ -70,10 +70,15 @@ public void performTest() testExceptionsEngine_ascon80pq(); testExceptionsXof_AsconXof128(); - testExceptionsXof_AsconCxof128(); + testExceptionsXof_AsconCXof128(); testExceptionsXof_AsconXof(); testExceptionsXof_AsconXofA(); + testOutputXof_AsconXof128(); + testOutputXof_AsconCXof128(); + testOutputXof_AsconXof(); + testOutputXof_AsconXofA(); + testParametersDigest_AsconHash256(); testParametersDigest_AsconHash(); testParametersDigest_AsconHashA(); @@ -84,7 +89,7 @@ public void performTest() testParametersEngine_ascon80pq(); testParametersXof_AsconXof128(); - testParametersXof_AsconCxof128(); + testParametersXof_AsconCXof128(); testParametersXof_AsconXof(); testParametersXof_AsconXofA(); @@ -331,7 +336,7 @@ public ExtendedDigest createDigest() }); } - public void testExceptionsXof_AsconCxof128() + public void testExceptionsXof_AsconCXof128() throws Exception { implTestExceptionsXof(new CreateDigest() @@ -344,6 +349,26 @@ public ExtendedDigest createDigest() }); } + public void testOutputXof_AsconXof() + { + implTestOutputXof(new AsconXof(AsconXof.AsconParameters.AsconXof)); + } + + public void testOutputXof_AsconXofA() + { + implTestOutputXof(new AsconXof(AsconXof.AsconParameters.AsconXofA)); + } + + public void testOutputXof_AsconXof128() + { + implTestOutputXof(new AsconXof128()); + } + + public void testOutputXof_AsconCXof128() + { + implTestOutputXof(new AsconCXof128()); + } + public void testParametersDigest_AsconHash() throws Exception { @@ -460,7 +485,7 @@ public ExtendedDigest createDigest() }, 32); } - public void testParametersXof_AsconCxof128() + public void testParametersXof_AsconCXof128() throws Exception { implTestParametersDigest(new CreateDigest() @@ -1005,6 +1030,35 @@ private void implTestExceptionsXof(CreateDigest operator) } } + private void implTestOutputXof(Xof ascon) + { + Random random = new Random(); + + byte[] expected = new byte[64]; + ascon.doFinal(expected, 0, expected.length); + + byte[] output = new byte[64]; + for (int i = 0; i < 64; ++i) + { + random.nextBytes(output); + + int pos = 0; + while (pos <= output.length - 16) + { + int len = random.nextInt(17); + ascon.doOutput(output, pos, len); + pos += len; + } + + ascon.doFinal(output, pos, output.length - pos); + + if (!areEqual(expected, output)) + { + fail(""); + } + } + } + private void implTestParametersDigest(CreateDigest operator, int digestSize) { ExtendedDigest ascon = operator.createDigest(); From 78cb999ae03146f46107c6b8934c0d78a2c53940 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 24 Mar 2025 22:50:35 +0700 Subject: [PATCH 258/890] Fix exception type --- .../main/java/org/bouncycastle/crypto/digests/AsconXofBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXofBase.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXofBase.java index f27de16070..2e28b2b01e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconXofBase.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconXofBase.java @@ -100,7 +100,7 @@ private void ensureNoAbsorbWhileSqueezing(boolean m_squeezing) { if (m_squeezing) { - throw new IllegalArgumentException("attempt to absorb while squeezing"); + throw new IllegalStateException("attempt to absorb while squeezing"); } } } From c6b4ab0a1f56a8b02728c75d618df0170cb731a3 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 24 Mar 2025 22:54:05 +0700 Subject: [PATCH 259/890] Optimize AsconCXof128 init for simple case --- .../crypto/digests/AsconCXof128.java | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java index 78a42a9a65..2a21511369 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/AsconCXof128.java @@ -81,12 +81,26 @@ public void reset() private void initState(byte[] z, int zOff, int zLen) { - p.set(7445901275803737603L, 4886737088792722364L, -1616759365661982283L, 3076320316797452470L, -8124743304765850554L); - p.x0 ^= ((long)zLen) << 3; - p.p(12); - update(z, zOff, zLen); - padAndAbsorb(); +// p.set(0x0000080000cc0004L, 0L, 0L, 0L, 0L); +// p.p(12); + + if (zLen == 0) + { +// p.p(12); +// padAndAbsorb(); + + p.set(0x500cccc894e3c9e8L, 0x5bed06f28f71248dL, 0x3b03a0f930afd512L, 0x112ef093aa5c698bL, 0x00c8356340a347f0L); + } + else + { + p.set(0x675527c2a0e8de03L, 0x43d12d7dc0377bbcL, 0xe9901dec426e81b5L, 0x2ab14907720780b6L, 0x8f3f1d02d432bc46L); + + p.x0 ^= ((long)zLen) << 3; + p.p(12); + update(z, zOff, zLen); + padAndAbsorb(); + } + super.reset(); } } - From 3b57d98ab6a613f9c8cddd83280d0a0857c24f11 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 25 Mar 2025 00:39:19 +0700 Subject: [PATCH 260/890] Fix naming --- .../org/bouncycastle/cert/test/SampleCredentials.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java b/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java index 3df74bbcbf..b3f86c2b34 100644 --- a/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java +++ b/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java @@ -52,16 +52,16 @@ private static SampleCredentials load(String algorithm, String path, String name Reader reader = new InputStreamReader(input); PemReader pemReader = new PemReader(reader); - PemObject pemPub = expectPemObject(pemReader, "PRIVATE KEY"); - PemObject pemPriv = expectPemObject(pemReader, "PUBLIC KEY"); + PemObject pemPriv = expectPemObject(pemReader, "PRIVATE KEY"); + PemObject pemPub = expectPemObject(pemReader, "PUBLIC KEY"); PemObject pemCert = expectPemObject(pemReader, "CERTIFICATE"); pemReader.close(); KeyFactory kf = KeyFactory.getInstance(algorithm, BouncyCastleProvider.PROVIDER_NAME); CertificateFactory cf = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); - PrivateKey privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(pemPub.getContent())); - PublicKey publicKey = kf.generatePublic(new X509EncodedKeySpec(pemPriv.getContent())); + PrivateKey privateKey = kf.generatePrivate(new PKCS8EncodedKeySpec(pemPriv.getContent())); + PublicKey publicKey = kf.generatePublic(new X509EncodedKeySpec(pemPub .getContent())); KeyPair keyPair = new KeyPair(publicKey, privateKey); X509Certificate certificate = (X509Certificate)cf.generateCertificate( From 8b4326f24738ad6f6ab360089436a8a93c6a5424 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 25 Mar 2025 00:57:03 +0700 Subject: [PATCH 261/890] Include external signer verification test --- .../cms/test/NewSignedDataTest.java | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java index 6e45de7d2c..9127390840 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java @@ -3522,14 +3522,33 @@ private static void implTestVerifySignedData(byte[] signedData, SampleCredential { CMSSignedData sd = new CMSSignedData(signedData); - assertTrue(sd.verifySignatures(new SignerInformationVerifierProvider() + // Verify using the certificate from the supplied credentials + SignerInformationVerifierProvider verifierProvider = new SignerInformationVerifierProvider() { public SignerInformationVerifier get(SignerId signerId) throws OperatorCreationException { return new JcaSimpleSignerInfoVerifierBuilder().setProvider(BC).build(credentials.getCertificate()); } - })); + }; + + // External signer verification + { + SignerInformationStore signers = sd.getSignerInfos(); + + Iterator it = signers.getSigners().iterator(); + while (it.hasNext()) + { + SignerInformation signer = (SignerInformation)it.next(); + + SignerInformationVerifier verifier = verifierProvider.get(signer.getSID()); + + assertTrue(signer.verify(verifier)); + } + } + + // Built-in signer verification + assertTrue(sd.verifySignatures(verifierProvider)); } private static class TestCMSSignatureAlgorithmNameGenerator From 54b3283d8b2b1beb8f70818dd74c9677da07eca9 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 25 Mar 2025 17:17:32 +1030 Subject: [PATCH 262/890] TODO: Evaluate vinegar part of central map --- .../pqc/crypto/snova/SnovaKeyElements.java | 16 +- .../pqc/crypto/snova/SnovaParameters.java | 5 + .../pqc/crypto/snova/SnovaSigner.java | 66 ++++---- .../pqc/crypto/test/SnovaTest.java | 148 +++++++++--------- 4 files changed, 126 insertions(+), 109 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java index 5d8a69e3c9..10d6b9537a 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java @@ -78,7 +78,7 @@ public void encodeMergerInHalf(byte[] output) public void skUnpack(byte[] input) { - byte[] tmp = new byte[input.length << 1]; + byte[] tmp = new byte[(input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength)<< 1]; GF16Utils.decodeMergeInHalf(input, tmp, tmp.length); int inOff = 0; inOff = copy3d(tmp, inOff, map1.aAlpha); @@ -89,10 +89,9 @@ public void skUnpack(byte[] input) inOff = copy4d(tmp, inOff, map2.f11); inOff = copy4d(tmp, inOff, map2.f12); inOff = copy4d(tmp, inOff, map2.f21); - System.arraycopy(tmp, inOff, publicKey.publicKeySeed, 0, publicKey.publicKeySeed.length); - inOff += publicKey.publicKeySeed.length; + System.arraycopy(input, input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength, publicKey.publicKeySeed, 0, publicKey.publicKeySeed.length); ptPrivateKeySeed = new byte[SnovaKeyPairGenerator.privateSeedLength]; - System.arraycopy(tmp, inOff, ptPrivateKeySeed, 0, ptPrivateKeySeed.length); + System.arraycopy(input, input.length - SnovaKeyPairGenerator.privateSeedLength, ptPrivateKeySeed, 0, ptPrivateKeySeed.length); } public int copy3d(byte[][][] alpha, byte[] output, int outOff) @@ -134,7 +133,14 @@ public int copy4d(byte[] input, int inOff, byte[][][][] alpha) { for (int i = 0; i < alpha.length; ++i) { - inOff = copy3d(alpha[i], input, inOff); + for (int j = 0; j < alpha[i].length; ++j) + { + for (int k = 0; k < alpha[i][j].length; ++k) + { + System.arraycopy(input, inOff, alpha[i][j][k], 0, alpha[i][j][k].length); + inOff += alpha[i][j][k].length; + } + } } return inOff; } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java index 171f71342d..468be6070a 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java @@ -187,4 +187,9 @@ public int getLsq() { return l * l; } + + public int getSaltLength() + { + return 16; + } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index fb69b15ba9..f03f4e810d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -4,15 +4,13 @@ import java.util.Arrays; import org.bouncycastle.crypto.CipherParameters; -import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.CryptoServicesRegistrar; -import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.Signer; import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.pqc.crypto.MessageSigner; public class SnovaSigner - implements Signer + implements MessageSigner { private SnovaParameters params; private SnovaEngine engine; @@ -53,25 +51,14 @@ public void init(boolean forSigning, CipherParameters param) } @Override - public void update(byte b) - { - digest.update(b); - } - - @Override - public void update(byte[] in, int off, int len) - { - digest.update(in, off, len); - } - - @Override - public byte[] generateSignature() - throws CryptoException, DataLengthException + public byte[] generateSignature(byte[] message) { byte[] hash = new byte[digest.getDigestSize()]; + digest.update(message, 0, message.length); digest.doFinal(hash, 0); - byte[] salt = new byte[16]; + byte[] salt = new byte[params.getSaltLength()]; random.nextBytes(salt); + byte[] signature = new byte[((params.getN() * params.getLsq() + 1) >>> 1) + params.getSaltLength()]; if (params.isSkIsSeed()) { @@ -80,22 +67,18 @@ public byte[] generateSignature() { SnovaKeyElements esk = new SnovaKeyElements(params, engine); esk.skUnpack(privKey.getPrivateKey()); + signDigestCore(signature, hash, salt, esk.map1.aAlpha, esk.map1.bAlpha, esk.map1.qAlpha1, esk.map1.qAlpha2, + esk.T12, esk.map2.f11, esk.map2.f12, esk.map2.f21, esk.publicKey.publicKeySeed, esk.ptPrivateKeySeed); } return new byte[0]; } @Override - public boolean verifySignature(byte[] signature) + public boolean verifySignature(byte[] message, byte[] signature) { return false; } - @Override - public void reset() - { - - } - public static void createSignedHash( byte[] digest, int bytesDigest, byte[] ptPublicKeySeed, int seedLengthPublic, @@ -120,9 +103,9 @@ public static void createSignedHash( } public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, - byte[][][][] Aalpha, byte[][][][] Balpha, - byte[][][][] Qalpha1, byte[][][][] Qalpha2, - byte[][][][] T12, byte[][][][] F11, + byte[][][] Aalpha, byte[][][] Balpha, + byte[][][] Qalpha1, byte[][][] Qalpha2, + byte[][][] T12, byte[][][][] F11, byte[][][][] F12, byte[][][][] F21, byte[] ptPublicKeySeed, byte[] ptPrivateKeySeed) { @@ -168,7 +151,10 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, do { // Initialize Gauss matrix - Arrays.stream(Gauss).forEach(row -> Arrays.fill(row, (byte)0)); + for (int i = 0; i < Gauss.length; ++i) + { + Arrays.fill(Gauss[i], (byte)0); + } numSign++; //flagRedo = 0; @@ -357,6 +343,26 @@ private void multiplyGF16Matrices(byte[][] a, byte[] b, byte[][] result) } } + private void multiplyGF16Matrices(byte[] a, byte[][] b, byte[][] result) + { + for (int i = 0; i < params.getL(); i++) + { + Arrays.fill(result[i], (byte)0); + for (int j = 0; j < params.getL(); j++) + { + byte sum = 0; + for (int k = 0; k < params.getL(); k++) + { + sum = GF16Utils.add(sum, GF16Utils.mul( + engine.getGF16m(a, i, k), + b[k][j] + )); + } + result[i][j] = sum; + } + } + } + private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size) { final int cols = size + 1; diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java index baf0059b41..f4f834b8a5 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java @@ -28,42 +28,42 @@ public static void main(String[] args) private static final SnovaParameters[] PARAMETER_SETS = new SnovaParameters[] { -// SnovaParameters.SNOVA_24_5_16_4_ESK, -// SnovaParameters.SNOVA_24_5_16_4_SHAKE_ESK, -// SnovaParameters.SNOVA_24_5_16_4_SHAKE_SSK, -// SnovaParameters.SNOVA_24_5_16_4_SSK, -// SnovaParameters.SNOVA_24_5_16_5_ESK, -// SnovaParameters.SNOVA_24_5_16_5_SHAKE_ESK, -// SnovaParameters.SNOVA_24_5_16_5_SHAKE_SSK, -// SnovaParameters.SNOVA_24_5_16_5_SSK, -// SnovaParameters.SNOVA_25_8_16_3_ESK, -// SnovaParameters.SNOVA_25_8_16_3_SHAKE_ESK, -// SnovaParameters.SNOVA_25_8_16_3_SHAKE_SSK, -// SnovaParameters.SNOVA_25_8_16_3_SSK, -// SnovaParameters.SNOVA_29_6_16_5_ESK, -// SnovaParameters.SNOVA_29_6_16_5_SHAKE_ESK, -// SnovaParameters.SNOVA_29_6_16_5_SHAKE_SSK, -// SnovaParameters.SNOVA_29_6_16_5_SSK, -// SnovaParameters.SNOVA_37_8_16_4_ESK, -// SnovaParameters.SNOVA_37_8_16_4_SHAKE_ESK, -// SnovaParameters.SNOVA_37_8_16_4_SHAKE_SSK, -// SnovaParameters.SNOVA_37_8_16_4_SSK, -// SnovaParameters.SNOVA_37_17_16_2_ESK, -// SnovaParameters.SNOVA_37_17_16_2_SHAKE_ESK, -// SnovaParameters.SNOVA_37_17_16_2_SHAKE_SSK, -// SnovaParameters.SNOVA_37_17_16_2_SSK, -// SnovaParameters.SNOVA_49_11_16_3_ESK, -// SnovaParameters.SNOVA_49_11_16_3_SHAKE_ESK, -// SnovaParameters.SNOVA_49_11_16_3_SHAKE_SSK, -// SnovaParameters.SNOVA_49_11_16_3_SSK, -// SnovaParameters.SNOVA_56_25_16_2_ESK, -// SnovaParameters.SNOVA_56_25_16_2_SHAKE_ESK, -// SnovaParameters.SNOVA_56_25_16_2_SHAKE_SSK, -// SnovaParameters.SNOVA_56_25_16_2_SSK, -// SnovaParameters.SNOVA_60_10_16_4_ESK, -// SnovaParameters.SNOVA_60_10_16_4_SHAKE_ESK, -// SnovaParameters.SNOVA_60_10_16_4_SHAKE_SSK, -// SnovaParameters.SNOVA_60_10_16_4_SSK, + SnovaParameters.SNOVA_24_5_16_4_ESK, + SnovaParameters.SNOVA_24_5_16_4_SHAKE_ESK, + SnovaParameters.SNOVA_24_5_16_4_SHAKE_SSK, + SnovaParameters.SNOVA_24_5_16_4_SSK, + SnovaParameters.SNOVA_24_5_16_5_ESK, + SnovaParameters.SNOVA_24_5_16_5_SHAKE_ESK, + SnovaParameters.SNOVA_24_5_16_5_SHAKE_SSK, + SnovaParameters.SNOVA_24_5_16_5_SSK, + SnovaParameters.SNOVA_25_8_16_3_ESK, + SnovaParameters.SNOVA_25_8_16_3_SHAKE_ESK, + SnovaParameters.SNOVA_25_8_16_3_SHAKE_SSK, + SnovaParameters.SNOVA_25_8_16_3_SSK, + SnovaParameters.SNOVA_29_6_16_5_ESK, + SnovaParameters.SNOVA_29_6_16_5_SHAKE_ESK, + SnovaParameters.SNOVA_29_6_16_5_SHAKE_SSK, + SnovaParameters.SNOVA_29_6_16_5_SSK, + SnovaParameters.SNOVA_37_8_16_4_ESK, + SnovaParameters.SNOVA_37_8_16_4_SHAKE_ESK, + SnovaParameters.SNOVA_37_8_16_4_SHAKE_SSK, + SnovaParameters.SNOVA_37_8_16_4_SSK, + SnovaParameters.SNOVA_37_17_16_2_ESK, + SnovaParameters.SNOVA_37_17_16_2_SHAKE_ESK, + SnovaParameters.SNOVA_37_17_16_2_SHAKE_SSK, + SnovaParameters.SNOVA_37_17_16_2_SSK, + SnovaParameters.SNOVA_49_11_16_3_ESK, + SnovaParameters.SNOVA_49_11_16_3_SHAKE_ESK, + SnovaParameters.SNOVA_49_11_16_3_SHAKE_SSK, + SnovaParameters.SNOVA_49_11_16_3_SSK, + SnovaParameters.SNOVA_56_25_16_2_ESK, + SnovaParameters.SNOVA_56_25_16_2_SHAKE_ESK, + SnovaParameters.SNOVA_56_25_16_2_SHAKE_SSK, + SnovaParameters.SNOVA_56_25_16_2_SSK, + SnovaParameters.SNOVA_60_10_16_4_ESK, + SnovaParameters.SNOVA_60_10_16_4_SHAKE_ESK, + SnovaParameters.SNOVA_60_10_16_4_SHAKE_SSK, + SnovaParameters.SNOVA_60_10_16_4_SSK, SnovaParameters.SNOVA_66_15_16_3_ESK, SnovaParameters.SNOVA_66_15_16_3_SHAKE_ESK, SnovaParameters.SNOVA_66_15_16_3_SHAKE_SSK, @@ -75,42 +75,42 @@ public static void main(String[] args) }; private static final String[] files = new String[]{ -// "PQCsignKAT_SNOVA_24_5_4_ESK.rsp", -// "PQCsignKAT_SNOVA_24_5_4_SHAKE_ESK.rsp", -// "PQCsignKAT_SNOVA_24_5_4_SHAKE_SSK.rsp", -// "PQCsignKAT_SNOVA_24_5_4_SSK.rsp", -// "PQCsignKAT_SNOVA_24_5_5_ESK.rsp", -// "PQCsignKAT_SNOVA_24_5_5_SHAKE_ESK.rsp", -// "PQCsignKAT_SNOVA_24_5_5_SHAKE_SSK.rsp", -// "PQCsignKAT_SNOVA_24_5_5_SSK.rsp", -// "PQCsignKAT_SNOVA_25_8_3_ESK.rsp", -// "PQCsignKAT_SNOVA_25_8_3_SHAKE_ESK.rsp", -// "PQCsignKAT_SNOVA_25_8_3_SHAKE_SSK.rsp", -// "PQCsignKAT_SNOVA_25_8_3_SSK.rsp", -// "PQCsignKAT_SNOVA_29_6_5_ESK.rsp", -// "PQCsignKAT_SNOVA_29_6_5_SHAKE_ESK.rsp", -// "PQCsignKAT_SNOVA_29_6_5_SHAKE_SSK.rsp", -// "PQCsignKAT_SNOVA_29_6_5_SSK.rsp", -// "PQCsignKAT_SNOVA_37_8_4_ESK.rsp", -// "PQCsignKAT_SNOVA_37_8_4_SHAKE_ESK.rsp", -// "PQCsignKAT_SNOVA_37_8_4_SHAKE_SSK.rsp", -// "PQCsignKAT_SNOVA_37_8_4_SSK.rsp", -// "PQCsignKAT_SNOVA_37_17_2_ESK.rsp", -// "PQCsignKAT_SNOVA_37_17_2_SHAKE_ESK.rsp", -// "PQCsignKAT_SNOVA_37_17_2_SHAKE_SSK.rsp", -// "PQCsignKAT_SNOVA_37_17_2_SSK.rsp", -// "PQCsignKAT_SNOVA_49_11_3_ESK.rsp", -// "PQCsignKAT_SNOVA_49_11_3_SHAKE_ESK.rsp", -// "PQCsignKAT_SNOVA_49_11_3_SHAKE_SSK.rsp", -// "PQCsignKAT_SNOVA_49_11_3_SSK.rsp", -// "PQCsignKAT_SNOVA_56_25_2_ESK.rsp", -// "PQCsignKAT_SNOVA_56_25_2_SHAKE_ESK.rsp", -// "PQCsignKAT_SNOVA_56_25_2_SHAKE_SSK.rsp", -// "PQCsignKAT_SNOVA_56_25_2_SSK.rsp", -// "PQCsignKAT_SNOVA_60_10_4_ESK.rsp", -// "PQCsignKAT_SNOVA_60_10_4_SHAKE_ESK.rsp", -// "PQCsignKAT_SNOVA_60_10_4_SHAKE_SSK.rsp", -// "PQCsignKAT_SNOVA_60_10_4_SSK.rsp", + "PQCsignKAT_SNOVA_24_5_4_ESK.rsp", + "PQCsignKAT_SNOVA_24_5_4_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_24_5_4_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_24_5_4_SSK.rsp", + "PQCsignKAT_SNOVA_24_5_5_ESK.rsp", + "PQCsignKAT_SNOVA_24_5_5_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_24_5_5_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_24_5_5_SSK.rsp", + "PQCsignKAT_SNOVA_25_8_3_ESK.rsp", + "PQCsignKAT_SNOVA_25_8_3_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_25_8_3_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_25_8_3_SSK.rsp", + "PQCsignKAT_SNOVA_29_6_5_ESK.rsp", + "PQCsignKAT_SNOVA_29_6_5_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_29_6_5_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_29_6_5_SSK.rsp", + "PQCsignKAT_SNOVA_37_8_4_ESK.rsp", + "PQCsignKAT_SNOVA_37_8_4_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_37_8_4_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_37_8_4_SSK.rsp", + "PQCsignKAT_SNOVA_37_17_2_ESK.rsp", + "PQCsignKAT_SNOVA_37_17_2_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_37_17_2_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_37_17_2_SSK.rsp", + "PQCsignKAT_SNOVA_49_11_3_ESK.rsp", + "PQCsignKAT_SNOVA_49_11_3_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_49_11_3_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_49_11_3_SSK.rsp", + "PQCsignKAT_SNOVA_56_25_2_ESK.rsp", + "PQCsignKAT_SNOVA_56_25_2_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_56_25_2_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_56_25_2_SSK.rsp", + "PQCsignKAT_SNOVA_60_10_4_ESK.rsp", + "PQCsignKAT_SNOVA_60_10_4_SHAKE_ESK.rsp", + "PQCsignKAT_SNOVA_60_10_4_SHAKE_SSK.rsp", + "PQCsignKAT_SNOVA_60_10_4_SSK.rsp", "PQCsignKAT_SNOVA_66_15_3_ESK.rsp", "PQCsignKAT_SNOVA_66_15_3_SHAKE_ESK.rsp", "PQCsignKAT_SNOVA_66_15_3_SHAKE_SSK.rsp", @@ -159,13 +159,13 @@ public byte[] getPrivateKeyEncoded(CipherParameters privParams) @Override public Signer getSigner() { - return new SnovaSigner(); + return null; } @Override public MessageSigner getMessageSigner() { - return null;//new SnovaSigner(); + return new SnovaSigner(); } }); long end = System.currentTimeMillis(); From 32a9232f3de93606f603f4e3f6d684be4070c907 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 26 Mar 2025 16:54:52 +1030 Subject: [PATCH 263/890] Pass the test vectors for signing of Snova. --- .../pqc/crypto/snova/PublicKey.java | 2 +- .../pqc/crypto/snova/SnovaEngine.java | 173 +++++++++++++++++ .../crypto/snova/SnovaKeyPairGenerator.java | 177 +---------------- .../pqc/crypto/snova/SnovaSigner.java | 181 +++++++++++++----- 4 files changed, 313 insertions(+), 220 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java index 3bc2bcca0e..74e2896dbb 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java @@ -2,7 +2,7 @@ class PublicKey { - public final byte[] publicKeySeed; + public byte[] publicKeySeed; public final byte[] P22; public PublicKey(SnovaParameters params) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 104c0a895e..64ca2d51ce 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -1,5 +1,12 @@ package org.bouncycastle.pqc.crypto.snova; +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.digests.SHAKEDigest; +import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.modes.CTRModeCipher; +import org.bouncycastle.crypto.modes.SICBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.util.Arrays; public class SnovaEngine @@ -470,4 +477,170 @@ public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] Arrays.fill(temp2, (byte)0); } } + + void genSeedsAndT12(byte[][][] T12, byte[] skSeed) + { + int bytesPrngPrivate = (params.getV() * params.getO() * params.getL() + 1) >>> 1; + int gf16sPrngPrivate = params.getV() * params.getO() * params.getL(); + byte[] prngOutput = new byte[bytesPrngPrivate]; + + // Generate PRNG output using SHAKE-256 + SHAKEDigest shake = new SHAKEDigest(256); + shake.update(skSeed, 0, skSeed.length); + shake.doFinal(prngOutput, 0, prngOutput.length); + + // Convert bytes to GF16 array + byte[] gf16PrngOutput = new byte[gf16sPrngPrivate]; + GF16Utils.decode(prngOutput, gf16PrngOutput, gf16sPrngPrivate); + + // Generate T12 matrices + int ptArray = 0; + int l = params.getL(); + for (int j = 0; j < params.getV(); j++) + { + for (int k = 0; k < params.getO(); k++) + { + //gen_a_FqS_ct + genAFqSCT(gf16PrngOutput, ptArray, T12[j][k]); + ptArray += l; + } + } + } + + void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) + { + int l = params.getL(); + int lsq = l * l; + int m = params.getM(); + int alpha = params.getAlpha(); + int v = params.getV(); + int o = params.getO(); + int n = v + o; + + int gf16sPrngPublic = lsq * (2 * m * alpha + m * (n * n - m * m)) + l * 2 * m * alpha; + byte[] qTemp = new byte[(m * alpha * lsq + m * alpha * lsq) / l]; + byte[] prngOutput = new byte[(gf16sPrngPublic + 1) >> 1]; + + if (params.isPkExpandShake()) + { + snovaShake(pkSeed, prngOutput.length, prngOutput); + } + else + { + // Create a 16-byte IV (all zeros) + byte[] iv = new byte[16]; // automatically zero-initialized + // AES-CTR-based expansion + // Set up AES engine in CTR (SIC) mode. + BlockCipher aesEngine = AESEngine.newInstance(); + // SICBlockCipher implements CTR mode for AES. + CTRModeCipher ctrCipher = SICBlockCipher.newInstance(aesEngine); + ParametersWithIV params = new ParametersWithIV(new KeyParameter(pkSeed), iv); + ctrCipher.init(true, params); + int blockSize = ctrCipher.getBlockSize(); // typically 16 bytes + byte[] zeroBlock = new byte[blockSize]; // block of zeros + byte[] blockOut = new byte[blockSize]; + + int offset = 0; + // Process full blocks + while (offset + blockSize <= prngOutput.length) + { + ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); + System.arraycopy(blockOut, 0, prngOutput, offset, blockSize); + offset += blockSize; + } + // Process any remaining partial block. + if (offset < prngOutput.length) + { + ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); + int remaining = prngOutput.length - offset; + System.arraycopy(blockOut, 0, prngOutput, offset, remaining); + } + } + byte[] temp = new byte[gf16sPrngPublic - qTemp.length]; + GF16Utils.decode(prngOutput, temp, temp.length); + map1.fill(temp); + if (l >= 4) + { + GF16Utils.decode(prngOutput, temp.length >> 1, qTemp, 0, qTemp.length); + + // Post-processing for invertible matrices + for (int pi = 0; pi < m; ++pi) + { + for (int a = 0; a < alpha; ++a) + { + makeInvertibleByAddingAS(map1.aAlpha[pi][a], 0); + } + } + for (int pi = 0; pi < m; ++pi) + { + for (int a = 0; a < alpha; ++a) + { + makeInvertibleByAddingAS(map1.bAlpha[pi][a], 0); + } + } + + int ptArray = 0; + for (int pi = 0; pi < m; ++pi) + { + for (int a = 0; a < alpha; ++a) + { + genAFqS(qTemp, ptArray, map1.qAlpha1[pi][a], 0); + ptArray += l; + } + } + for (int pi = 0; pi < m; ++pi) + { + for (int a = 0; a < alpha; ++a) + { + genAFqS(qTemp, ptArray, map1.qAlpha2[pi][a], 0); + ptArray += l; + } + } + } + else + { + MapGroup1.fillAlpha(fixedAbq, 0, map1.aAlpha, m * o * alpha * lsq); + MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq, map1.bAlpha, (m - 1) * o * alpha * lsq); + MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq * 2, map1.qAlpha1, (m - 2) * o * alpha * lsq); + MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq * 3, map1.qAlpha2, (m - 3) * o * alpha * lsq); + } + } + + public static void snovaShake(byte[] ptSeed, int outputBytes, byte[] out) + { + final int SHAKE128_RATE = 168; // 1344-bit rate = 168 bytes + long blockCounter = 0; + int offset = 0; + int remaining = outputBytes; + + while (remaining > 0) + { + SHAKEDigest shake = new SHAKEDigest(128); + + // Process seed + counter + shake.update(ptSeed, 0, ptSeed.length); + updateWithCounter(shake, blockCounter); + + // Calculate bytes to generate in this iteration + int bytesToGenerate = Math.min(remaining, SHAKE128_RATE); + + // Generate output (XOF mode) + shake.doFinal(out, offset, bytesToGenerate); + + offset += bytesToGenerate; + remaining -= bytesToGenerate; + blockCounter++; + } + } + + private static void updateWithCounter(SHAKEDigest shake, long counter) + { + byte[] counterBytes = new byte[8]; + // Little-endian conversion + for (int i = 0; i < 8; i++) + { + counterBytes[i] = (byte)(counter >> (i * 8)); + } + shake.update(counterBytes, 0, 8); + } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index 4dc6f8d28c..b7244dced8 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -4,14 +4,7 @@ import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; -import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.KeyGenerationParameters; -import org.bouncycastle.crypto.digests.SHAKEDigest; -import org.bouncycastle.crypto.engines.AESEngine; -import org.bouncycastle.crypto.modes.CTRModeCipher; -import org.bouncycastle.crypto.modes.SICBlockCipher; -import org.bouncycastle.crypto.params.KeyParameter; -import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.util.Arrays; public class SnovaKeyPairGenerator @@ -79,10 +72,10 @@ public AsymmetricCipherKeyPair generateKeyPair() private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[] skSeed) { // Generate T12 matrix - genSeedsAndT12(keyElements.T12, skSeed); + engine.genSeedsAndT12(keyElements.T12, skSeed); // Generate map components - genABQP(keyElements.map1, pkSeed, keyElements.fixedAbq); + engine.genABQP(keyElements.map1, pkSeed, keyElements.fixedAbq); // Generate F matrices engine.genF(keyElements.map2, keyElements.map1, keyElements.T12); @@ -90,170 +83,4 @@ private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[ // Generate P22 matrix engine.genP22(keyElements.publicKey.P22, keyElements.T12, keyElements.map1.p21, keyElements.map2.f12); } - - private void genSeedsAndT12(byte[][][] T12, byte[] skSeed) - { - int bytesPrngPrivate = (params.getV() * params.getO() * params.getL() + 1) >>> 1; - int gf16sPrngPrivate = params.getV() * params.getO() * params.getL(); - byte[] prngOutput = new byte[bytesPrngPrivate]; - - // Generate PRNG output using SHAKE-256 - SHAKEDigest shake = new SHAKEDigest(256); - shake.update(skSeed, 0, skSeed.length); - shake.doFinal(prngOutput, 0, prngOutput.length); - - // Convert bytes to GF16 array - byte[] gf16PrngOutput = new byte[gf16sPrngPrivate]; - GF16Utils.decode(prngOutput, gf16PrngOutput, gf16sPrngPrivate); - - // Generate T12 matrices - int ptArray = 0; - int l = params.getL(); - for (int j = 0; j < params.getV(); j++) - { - for (int k = 0; k < params.getO(); k++) - { - //gen_a_FqS_ct - engine.genAFqSCT(gf16PrngOutput, ptArray, T12[j][k]); - ptArray += l; - } - } - } - - private void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) - { - int l = params.getL(); - int lsq = l * l; - int m = params.getM(); - int alpha = params.getAlpha(); - int v = params.getV(); - int o = params.getO(); - int n = v + o; - - int gf16sPrngPublic = lsq * (2 * m * alpha + m * (n * n - m * m)) + l * 2 * m * alpha; - byte[] qTemp = new byte[(m * alpha * lsq + m * alpha * lsq) / l]; - byte[] prngOutput = new byte[(gf16sPrngPublic + 1) >> 1]; - - if (params.isPkExpandShake()) - { - snovaShake(pkSeed, prngOutput.length, prngOutput); - } - else - { - // Create a 16-byte IV (all zeros) - byte[] iv = new byte[16]; // automatically zero-initialized - // AES-CTR-based expansion - // Set up AES engine in CTR (SIC) mode. - BlockCipher aesEngine = AESEngine.newInstance(); - // SICBlockCipher implements CTR mode for AES. - CTRModeCipher ctrCipher = SICBlockCipher.newInstance(aesEngine); - ParametersWithIV params = new ParametersWithIV(new KeyParameter(pkSeed), iv); - ctrCipher.init(true, params); - int blockSize = ctrCipher.getBlockSize(); // typically 16 bytes - byte[] zeroBlock = new byte[blockSize]; // block of zeros - byte[] blockOut = new byte[blockSize]; - - int offset = 0; - // Process full blocks - while (offset + blockSize <= prngOutput.length) - { - ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); - System.arraycopy(blockOut, 0, prngOutput, offset, blockSize); - offset += blockSize; - } - // Process any remaining partial block. - if (offset < prngOutput.length) - { - ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); - int remaining = prngOutput.length - offset; - System.arraycopy(blockOut, 0, prngOutput, offset, remaining); - } - } - byte[] temp = new byte[gf16sPrngPublic - qTemp.length]; - GF16Utils.decode(prngOutput, temp, temp.length); - map1.fill(temp); - if (l >= 4) - { - GF16Utils.decode(prngOutput, temp.length >> 1, qTemp, 0, qTemp.length); - - // Post-processing for invertible matrices - for (int pi = 0; pi < m; ++pi) - { - for (int a = 0; a < alpha; ++a) - { - engine.makeInvertibleByAddingAS(map1.aAlpha[pi][a], 0); - } - } - for (int pi = 0; pi < m; ++pi) - { - for (int a = 0; a < alpha; ++a) - { - engine.makeInvertibleByAddingAS(map1.bAlpha[pi][a], 0); - } - } - - int ptArray = 0; - for (int pi = 0; pi < m; ++pi) - { - for (int a = 0; a < alpha; ++a) - { - engine.genAFqS(qTemp, ptArray, map1.qAlpha1[pi][a], 0); - ptArray += l; - } - } - for (int pi = 0; pi < m; ++pi) - { - for (int a = 0; a < alpha; ++a) - { - engine.genAFqS(qTemp, ptArray, map1.qAlpha2[pi][a], 0); - ptArray += l; - } - } - } - else - { - MapGroup1.fillAlpha(fixedAbq, 0, map1.aAlpha, m * o * alpha * lsq); - MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq, map1.bAlpha, (m - 1) * o * alpha * lsq); - MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq * 2, map1.qAlpha1, (m - 2) * o * alpha * lsq); - MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq * 3, map1.qAlpha2, (m - 3) * o * alpha * lsq); - } - } - - public static void snovaShake(byte[] ptSeed, int outputBytes, byte[] out) - { - final int SHAKE128_RATE = 168; // 1344-bit rate = 168 bytes - long blockCounter = 0; - int offset = 0; - int remaining = outputBytes; - - while (remaining > 0) - { - SHAKEDigest shake = new SHAKEDigest(128); - - // Process seed + counter - shake.update(ptSeed, 0, ptSeed.length); - updateWithCounter(shake, blockCounter); - - // Calculate bytes to generate in this iteration - int bytesToGenerate = Math.min(remaining, SHAKE128_RATE); - - // Generate output (XOF mode) - shake.doFinal(out, offset, bytesToGenerate); - - offset += bytesToGenerate; - remaining -= bytesToGenerate; - blockCounter++; - } - } - - private static void updateWithCounter(SHAKEDigest shake, long counter) - { - byte[] counterBytes = new byte[8]; - // Little-endian conversion - for (int i = 0; i < 8; i++) - { - counterBytes[i] = (byte)(counter >> (i * 8)); - } - shake.update(counterBytes, 0, 8); - } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index f03f4e810d..a14b51e372 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -1,13 +1,13 @@ package org.bouncycastle.pqc.crypto.snova; import java.security.SecureRandom; -import java.util.Arrays; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.pqc.crypto.MessageSigner; +import org.bouncycastle.util.Arrays; public class SnovaSigner implements MessageSigner @@ -59,18 +59,27 @@ public byte[] generateSignature(byte[] message) byte[] salt = new byte[params.getSaltLength()]; random.nextBytes(salt); byte[] signature = new byte[((params.getN() * params.getLsq() + 1) >>> 1) + params.getSaltLength()]; + SnovaKeyElements keyElements = new SnovaKeyElements(params, engine); if (params.isSkIsSeed()) { + byte[] seedPair = privKey.getPrivateKey(); + keyElements.publicKey.publicKeySeed = Arrays.copyOfRange(seedPair, 0, SnovaKeyPairGenerator.publicSeedLength); + keyElements.ptPrivateKeySeed = Arrays.copyOfRange(seedPair, SnovaKeyPairGenerator.publicSeedLength, seedPair.length); + engine.genSeedsAndT12(keyElements.T12, keyElements.ptPrivateKeySeed); + // Generate map components + engine.genABQP(keyElements.map1, keyElements.publicKey.publicKeySeed, keyElements.fixedAbq); + + // Generate F matrices + engine.genF(keyElements.map2, keyElements.map1, keyElements.T12); } else { - SnovaKeyElements esk = new SnovaKeyElements(params, engine); - esk.skUnpack(privKey.getPrivateKey()); - signDigestCore(signature, hash, salt, esk.map1.aAlpha, esk.map1.bAlpha, esk.map1.qAlpha1, esk.map1.qAlpha2, - esk.T12, esk.map2.f11, esk.map2.f12, esk.map2.f21, esk.publicKey.publicKeySeed, esk.ptPrivateKeySeed); + keyElements.skUnpack(privKey.getPrivateKey()); } - return new byte[0]; + signDigestCore(signature, hash, salt, keyElements.map1.aAlpha, keyElements.map1.bAlpha, keyElements.map1.qAlpha1, keyElements.map1.qAlpha2, + keyElements.T12, keyElements.map2.f11, keyElements.map2.f12, keyElements.map2.f21, keyElements.publicKey.publicKeySeed, keyElements.ptPrivateKeySeed); + return Arrays.concatenate(signature, message); } @Override @@ -127,6 +136,8 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, byte[][][][][] Left = new byte[m][alpha][v][l][l]; byte[][][][][] Right = new byte[m][alpha][v][l][l]; + byte[][] leftXTmp = new byte[l][l]; + byte[][] rightXtmp = new byte[l][l]; byte[][][] XInGF16Matrix = new byte[n][l][l]; byte[][] FvvGF16Matrix = new byte[m][lsq]; byte[] hashInGF16 = new byte[m * lsq]; @@ -193,7 +204,10 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, } // Matrix operations for Fvv - Arrays.stream(FvvGF16Matrix).forEach(row -> Arrays.fill(row, (byte)0)); + for (int i = 0; i < FvvGF16Matrix.length; ++i) + { + Arrays.fill(FvvGF16Matrix[i], (byte)0); + } for (int mi = 0; mi < m; mi++) { for (int a = 0; a < alpha; a++) @@ -211,36 +225,113 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, } } + int idx2 = m * lsq; // Gaussian elimination setup for (int i = 0; i < m; i++) { - for (int j = 0; j < params.getL(); j++) + for (int j = 0; j < l; j++) { - for (int k = 0; k < params.getL(); k++) + for (int k = 0; k < l; k++) { - int idx1 = i * lsq + j * params.getL() + k; - Gauss[idx1][m * lsq] = GF16Utils.add( - Gauss[idx1][m * lsq], - engine.getGF16m(FvvGF16Matrix[i], j, k) - ); + int idx1 = i * lsq + j * l + k; + Gauss[idx1][idx2] ^= engine.getGF16m(FvvGF16Matrix[i], j, k); } } } + // Compute the coefficients of Xo and put into Gauss matrix and compute the coefficients of Xo^t and add into Gauss matrix + for (int mi = 0; mi < m; ++mi) + { + for (int index = 0; index < o; ++index) + { + for (int a = 0; a < alpha; ++a) + { + int mi_prime = iPrime(mi, a); + // Initialize Temp to zero + for (int ti = 0; ti < lsq; ++ti) + { + Arrays.fill(Temp[ti], (byte)0); + } + // Process each j for Left part + for (int j = 0; j < v; ++j) + { + multiplyGF16Matrices(Left[mi][a][j], F12[mi_prime][j][index], gf16mTemp0); + multiplyGF16Matrices(gf16mTemp0, Qalpha2[mi][a], leftXTmp); + // Accumulate into Temp from leftXTmp and Balpha[mi][a] + for (int ti = 0; ti < lsq; ++ti) + { + for (int tj = 0; tj < lsq; ++tj) + { + int rowLeft = ti / l; + int colLeft = tj / l; + byte valLeft = leftXTmp[rowLeft][colLeft]; + int rowB = tj % l; + int colB = ti % l; + byte valB = engine.getGF16m(Balpha[mi][a], rowB, colB); + byte product = GF16Utils.mul(valLeft, valB); + Temp[ti][tj] ^= product; + } + } + } + // Process each j for Right part + for (int j = 0; j < v; ++j) + { + multiplyGF16Matrices(Qalpha1[mi][a], F21[mi_prime][index][j], gf16mTemp0); + multiplyGF16Matrices(gf16mTemp0, Right[mi][a][j], rightXtmp); + // Accumulate into Temp from Aalpha[mi][a] and rightXtmp + for (int ti = 0; ti < lsq; ++ti) + { + for (int tj = 0; tj < lsq; ++tj) + { + int rowA = ti / l; + int colA = tj % l; + byte valA = engine.getGF16m(Aalpha[mi][a], rowA, colA); + int rowRight = tj / l; + int colRight = ti % l; + byte valRight = rightXtmp[rowRight][colRight]; + byte product = GF16Utils.mul(valA, valRight); + Temp[ti][tj] = GF16Utils.add(Temp[ti][tj], product); + } + } + } + // Add Temp to Gauss matrix + for (int ti = 0; ti < lsq; ++ti) + { + for (int tj = 0; tj < lsq; ++tj) + { + int gaussRow = mi * lsq + ti; + int gaussCol = index * lsq + tj; + Gauss[gaussRow][gaussCol] = GF16Utils.add(Gauss[gaussRow][gaussCol], Temp[ti][tj]); + } + } + } + } + } + + // Gaussian elimination implementation flagRedo = performGaussianElimination(Gauss, solution, m * lsq); } while (flagRedo != 0); - // Build final signature - //buildSignature(XInGF16Matrix, signatureGF16Matrix, T12, v, o, lsq); - //void buildSignature(byte[][][] XIn, byte[][] signature, - // byte[][][][] T12, int v, int o, int lsq) + for (int index = 0; index < o; ++index) + { + for (int i = 0; i < l; ++i) + { + for (int j = 0; j < l; ++j) + { + XInGF16Matrix[index + v][i][j] = solution[index * lsq + i * l + j]; + } + } + } // Copy vinegar variables for (int idx = 0; idx < v; idx++) { - System.arraycopy(XInGF16Matrix[idx], 0, signatureGF16Matrix[idx], 0, lsq); + for (int i = 0; i < l; ++i) + { + System.arraycopy(XInGF16Matrix[idx][i], 0, signatureGF16Matrix[idx], i * l, l); + } } // Process oil variables with T12 matrix @@ -256,37 +347,19 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, // Copy remaining oil variables for (int idx = 0; idx < o; idx++) { - System.arraycopy(XInGF16Matrix[v + idx], 0, signatureGF16Matrix[v + idx], 0, lsq); - } - - // Convert to packed bytes - int bytePos = 0; - for (int matIdx = 0; matIdx < signatureGF16Matrix.length; matIdx++) - { - for (int i = 0; i < l; i++) + for (int i = 0; i < l; ++i) { - for (int j = 0; j < l; j++) - { - int nibblePos = bytePos % 2; - if (nibblePos == 0) - { - ptSignature[bytePos / 2] = (byte)(engine.getGF16m(signatureGF16Matrix[matIdx], i, j) << 4); - } - else - { - ptSignature[bytePos / 2] |= engine.getGF16m(signatureGF16Matrix[matIdx], i, j) & 0x0F; - } - bytePos++; - } + System.arraycopy(XInGF16Matrix[v + idx][i], 0, signatureGF16Matrix[v + idx], i * l, l); } } byte[] tmp = new byte[n * lsq]; - for (int i = 0; i < signatureGF16Matrix.length; ++i) + for (int idx = 0; idx < signatureGF16Matrix.length; ++idx) { - System.arraycopy(signatureGF16Matrix[i], 0, tmp, 0, signatureGF16Matrix[i].length); + System.arraycopy(signatureGF16Matrix[idx], 0, tmp, idx * lsq, lsq); } GF16Utils.encode(tmp, ptSignature, 0, tmp.length); - System.arraycopy(arraySalt, 0, ptSignature, 0, bytesSalt); + + System.arraycopy(arraySalt, 0, ptSignature, ptSignature.length - bytesSalt, bytesSalt); // Clear sensitive data Arrays.fill(gf16mSecretTemp0, (byte)0); @@ -363,6 +436,26 @@ private void multiplyGF16Matrices(byte[] a, byte[][] b, byte[][] result) } } + private void multiplyGF16Matrices(byte[] a, byte[] b, byte[][] result) + { + for (int i = 0; i < params.getL(); i++) + { + Arrays.fill(result[i], (byte)0); + for (int j = 0; j < params.getL(); j++) + { + byte sum = 0; + for (int k = 0; k < params.getL(); k++) + { + sum = GF16Utils.add(sum, GF16Utils.mul( + engine.getGF16m(a, i, k), + engine.getGF16m(b, k, j) + )); + } + result[i][j] = sum; + } + } + } + private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size) { final int cols = size + 1; @@ -439,7 +532,7 @@ private void addGF16Matrices(byte[] a, byte[][] b, byte[] result) private int iPrime(int mi, int alpha) { // Implement index calculation based on SNOVA specification - return (mi + alpha) % params.getM(); + return (mi + alpha) % params.getO(); } } From 465731d605ada3d07fa8ee462c79e3e957bed5fd Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 27 Mar 2025 11:28:33 +1030 Subject: [PATCH 264/890] Pass all test vectors of Snova --- .../pqc/crypto/snova/SnovaSigner.java | 191 +++++++++++++++++- .../pqc/crypto/test/AllTests.java | 2 +- 2 files changed, 191 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index a14b51e372..e0d6dab1d3 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -85,7 +85,19 @@ public byte[] generateSignature(byte[] message) @Override public boolean verifySignature(byte[] message, byte[] signature) { - return false; + byte[] hash = new byte[digest.getDigestSize()]; + digest.update(message, 0, message.length); + digest.doFinal(hash, 0); + SnovaKeyElements keyElements = new SnovaKeyElements(params, engine); + byte[] pk = pubKey.getEncoded(); + System.arraycopy(pk, 0, keyElements.publicKey.publicKeySeed, 0, SnovaKeyPairGenerator.publicSeedLength); + System.arraycopy(pk, SnovaKeyPairGenerator.publicSeedLength, keyElements.publicKey.P22, 0, keyElements.publicKey.P22.length); + engine.genABQP(keyElements.map1, keyElements.publicKey.publicKeySeed, keyElements.fixedAbq); + byte[] p22_gf16s = new byte[keyElements.publicKey.P22.length << 1]; + GF16Utils.decode(keyElements.publicKey.P22, p22_gf16s, p22_gf16s.length); + byte[][][][] p22 = new byte[params.getM()][params.getO()][params.getO()][params.getLsq()]; + MapGroup1.fillP(p22_gf16s, 0, p22, p22_gf16s.length); + return verifySignatureCore(hash, signature, keyElements.publicKey, keyElements.map1, p22); } public static void createSignedHash( @@ -365,6 +377,172 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, Arrays.fill(gf16mSecretTemp0, (byte)0); } + public boolean verifySignatureCore(byte[] digest, byte[] signature, PublicKey pkx, MapGroup1 map1, byte[][][][] p22) + { + final int bytesHash = (params.getO() * params.getLsq() + 1) >>> 1; + final int bytesSalt = params.getSaltLength(); + final int l = params.getL(); + final int lsq = params.getLsq(); + final int m = params.getM(); + final int n = params.getN(); + final int v = params.getV(); + final int o = params.getO(); + int bytesSignature = ((n * lsq) + 1) >>> 1; + + // Extract salt from signature + byte[] ptSalt = Arrays.copyOfRange(signature, bytesSignature, bytesSignature + bytesSalt); + //byte[] signatureBody = Arrays.copyOf(signature, signature.length - bytesSalt); + + // Step 1: Regenerate signed hash using public key seed, digest and salt + byte[] signedHash = new byte[bytesHash]; + SHAKEDigest shake = new SHAKEDigest(256); + shake.update(pkx.publicKeySeed, 0, pkx.publicKeySeed.length); + shake.update(digest, 0, digest.length); + shake.update(ptSalt, 0, ptSalt.length); + shake.doFinal(signedHash, 0, bytesHash); + + // Handle odd-length adjustment (if needed) + if ((o * lsq) % 2 != 0) + { + signedHash[bytesHash - 1] &= 0x0F; + } + + // Step 2: Convert signature to GF16 matrices + byte[][][] signatureGF16Matrix = new byte[n][l][l]; + byte[] decodedSig = new byte[n * lsq]; + GF16Utils.decode(signature, 0, decodedSig, 0, decodedSig.length); + + for (int i = 0; i < n; i++) + { + for (int row = 0; row < l; row++) + { + System.arraycopy(decodedSig, i * lsq + row * l, + signatureGF16Matrix[i][row], 0, l); + } + } + + // Step 3: Evaluate signature using public key + byte[][][] computedHashMatrix = new byte[m][l][l]; + evaluation(computedHashMatrix, map1, p22, signatureGF16Matrix); + + // Convert computed hash matrix to bytes + byte[] computedHashBytes = new byte[m * lsq]; + for (int i = 0; i < m; i++) + { + for (int row = 0; row < l; row++) + { + System.arraycopy(computedHashMatrix[i][row], 0, + computedHashBytes, i * lsq + row * l, l); + } + } + byte[] encodedHash = new byte[bytesHash]; + GF16Utils.encode(computedHashBytes, encodedHash, 0, computedHashBytes.length); + + // Step 4: Compare hashes + return Arrays.areEqual(signedHash, encodedHash); + } + + private void evaluation(byte[][][] hashMatrix, MapGroup1 map1, byte[][][][] p22, byte[][][] signature) + { + final int m = params.getM(); + final int alpha = params.getAlpha(); + final int n = params.getN(); + final int v = params.getV(); + final int l = params.getL(); + + byte[][][][][] Left = new byte[m][alpha][n][l][l]; + byte[][][][][] Right = new byte[m][alpha][n][l][l]; + byte[][] temp = new byte[l][l]; + byte[][] transposedSig = new byte[l][l]; + + // Evaluate Left and Right matrices + for (int mi = 0; mi < m; mi++) + { + for (int si = 0; si < n; si++) + { + transposeGF16Matrix(signature[si], transposedSig); + for (int a = 0; a < alpha; a++) + { + // Left[mi][a][si] = Aalpha * (sig^T * Qalpha1) + multiplyGF16Matrices(transposedSig, map1.qAlpha1[mi][a], temp); + multiplyGF16Matrices(map1.aAlpha[mi][a], temp, Left[mi][a][si]); + + // Right[mi][a][si] = (Qalpha2 * sig) * Balpha + multiplyGF16Matrices(map1.qAlpha2[mi][a], signature[si], temp); + multiplyGF16Matrices(temp, map1.bAlpha[mi][a], Right[mi][a][si]); + } + } + } + + // Initialize hash matrix to zero + for (int mi = 0; mi < m; mi++) + { + for (int i = 0; i < l; i++) + { + Arrays.fill(hashMatrix[mi][i], (byte)0); + } + } + + // Process P matrices and accumulate results + byte[][] sumTemp = new byte[l][l]; + byte[][] pTemp = new byte[l][l]; + for (int mi = 0; mi < m; mi++) + { + for (int a = 0; a < alpha; a++) + { + int miPrime = iPrime(mi, a); + + for (int ni = 0; ni < n; ni++) + { + // sum_t0 = sum(P[miPrime][ni][nj] * Right[mi][a][nj]) + for (int i = 0; i < l; i++) + { + Arrays.fill(sumTemp[i], (byte)0); + } + + for (int nj = 0; nj < n; nj++) + { + byte[] p = getPMatrix(map1, p22, miPrime, ni, nj); + multiplyGF16Matrices(p, Right[mi][a][nj], pTemp); + addGF16Matrices(sumTemp, pTemp, sumTemp); + } + + // hashMatrix += Left[mi][a][ni] * sumTemp + multiplyGF16Matrices(Left[mi][a][ni], sumTemp, temp); + addGF16Matrices(hashMatrix[mi], temp, hashMatrix[mi]); + } + } + } + } + + // Helper method to get appropriate P matrix based on indices + private byte[] getPMatrix(MapGroup1 map1, byte[][][][] p22, int mi, int ni, int nj) + { + final int v = params.getV(); + if (ni < v) + { + if (nj < v) + { + return map1.p11[mi][ni][nj]; + } + else + { + return map1.p12[mi][ni][nj - v]; + } + } + else + { + if (nj < v) + { + return map1.p21[mi][ni - v][nj]; + } + else + { + return p22[mi][ni - v][nj - v]; + } + } + } + private void transposeGF16Matrix(byte[][] src, byte[][] dest) { for (int i = 0; i < params.getL(); i++) @@ -529,6 +707,17 @@ private void addGF16Matrices(byte[] a, byte[][] b, byte[] result) } } + private void addGF16Matrices(byte[][] a, byte[][] b, byte[][] result) + { + for (int i = 0; i < b.length; i++) + { + for (int j = 0; j < b[i].length; ++j) + { + result[i][j] = GF16Utils.add(a[i][j], b[i][j]); + } + } + } + private int iPrime(int mi, int alpha) { // Implement index calculation based on SNOVA specification diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/AllTests.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/AllTests.java index fbf60a3de7..5f0f99a177 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/AllTests.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/AllTests.java @@ -52,7 +52,7 @@ public static Test suite() suite.addTestSuite(AllTests.SimpleTestTest.class); suite.addTestSuite(SLHDSATest.class); suite.addTestSuite(MayoTest.class); - + suite.addTestSuite(SnovaTest.class); return new BCTestSetup(suite); } From 25045931f26094b35619f2edbaf7a2744de7a4a4 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 27 Mar 2025 11:58:35 +1030 Subject: [PATCH 265/890] Remove unused code --- .../pqc/crypto/snova/GF16Matrix.java | 105 ----------------- .../pqc/crypto/snova/GF16Utils.java | 31 ----- .../pqc/crypto/snova/MapGroup1.java | 66 ----------- .../pqc/crypto/snova/MapGroup2.java | 2 +- .../pqc/crypto/snova/PublicKeyExpanded.java | 19 --- .../crypto/snova/PublicKeyExpandedPack.java | 19 --- .../pqc/crypto/snova/SnovaEngine.java | 109 +++++++----------- 7 files changed, 44 insertions(+), 307 deletions(-) delete mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Matrix.java delete mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpanded.java delete mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpandedPack.java diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Matrix.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Matrix.java deleted file mode 100644 index a08aa7a122..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Matrix.java +++ /dev/null @@ -1,105 +0,0 @@ -//package org.bouncycastle.pqc.crypto.snova; -// -//class GF16Matrix -//{ -// private final byte[][] data; -// private final int rank; -// -// public GF16Matrix(int rank) -// { -// this.rank = rank; -// this.data = new byte[rank][rank]; -// } -// -// public void set(int x, int y, byte value) -// { -// data[x][y] = (byte)(value & 0xF); -// } -// -// public byte get(int x, int y) -// { -// return data[x][y]; -// } -// -//// public void add(GF16Matrix other) -//// { -////// for (int i = 0; i < size; i++) -////// { -////// for (int j = 0; j < size; j++) -////// { -////// data[i][j] = add(data[i][j], other.data[i][j]); -////// } -////// } -//// } -// -// public void mul(GF16Matrix a, GF16Matrix b) -// { -// byte[][] temp = new byte[rank][rank]; -// for (int i = 0; i < rank; i++) -// { -// for (int j = 0; j < rank; j++) -// { -// byte sum = 0; -//// for (int k = 0; k < size; k++) -//// { -//// sum = add(sum, mul(a.data[i][k], b.data[k][j])); -//// } -// temp[i][j] = sum; -// } -// } -// System.arraycopy(temp, 0, data, 0, temp.length); -// } -// -// public void scale(byte scalar) -// { -//// for (int i = 0; i < size; i++) -//// { -//// for (int j = 0; j < size; j++) -//// { -//// data[i][j] = mul(data[i][j], scalar); -//// } -//// } -// } -// -// public void transpose() -// { -// byte[][] temp = new byte[rank][rank]; -// for (int i = 0; i < rank; i++) -// { -// for (int j = 0; j < rank; j++) -// { -// temp[j][i] = data[i][j]; -// } -// } -// System.arraycopy(temp, 0, data, 0, temp.length); -// } -// -//// public void makeInvertible() -//// { -//// // Implementation of be_invertible_by_add_aS -//// GF16Matrix temp = new GF16Matrix(rank); -//// if (determinant() == 0) -//// { -//// for (byte a = 1; a < 16; a++) -//// { -//// temp.scale(a); -//// add(temp); -//// if (determinant() != 0) -//// { -//// return; -//// } -//// } -//// } -//// } -// -//// private byte determinant() -//// { -//// // Simplified determinant calculation for small matrices -////// if (rank == 2) -////// { -////// return add(mul(data[0][0], data[1][1]), mul(data[0][1], data[1][0])); -////// } -//// // Add implementations for larger matrices as needed -//// throw new UnsupportedOperationException("Determinant for size " + rank + " not implemented"); -//// } -//} \ No newline at end of file diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java index 65c9025155..5c7db6a54a 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java @@ -1,6 +1,5 @@ package org.bouncycastle.pqc.crypto.snova; - public class GF16Utils { private static final byte[] F_STAR = {1, 2, 4, 8, 3, 6, 12, 11, 5, 10, 7, 14, 15, 13, 9}; @@ -14,7 +13,6 @@ static byte mt(int p, int q) static { - // Initialize multiplication table for (int i = 0; i < 15; i++) { @@ -219,35 +217,6 @@ public static byte inv(byte a) return INV4B[a & 0xF]; } -// static GF16Matrix[][][] create3DArray(int d1, int d2, int d3, int rank) -// { -// GF16Matrix[][][] arr = new GF16Matrix[d1][d2][d3]; -// for (int i = 0; i < d1; i++) -// { -// for (int j = 0; j < d2; j++) -// { -// for (int k = 0; k < d3; k++) -// { -// arr[i][j][k] = new GF16Matrix(rank); -// } -// } -// } -// return arr; -// } - -// static GF16Matrix[][] create2DArray(int d1, int d2, int rank) -// { -// GF16Matrix[][] arr = new GF16Matrix[d1][d2]; -// for (int i = 0; i < d1; i++) -// { -// for (int j = 0; j < d2; j++) -// { -// arr[i][j] = new GF16Matrix(rank); -// } -// } -// return arr; -// } - private static final int GF16_MASK = 0x249; // Mask for GF(2^4) reduction // Constant-time GF16 != 0 check diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java index fa0ab86d92..c1a2c37d12 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -26,18 +26,6 @@ public MapGroup1(SnovaParameters params) qAlpha2 = new byte[m][alpha][lsq]; } -// public int decode(byte[] input, int len) -// { -// int inOff = decodeP(input, 0, p11, len); -// inOff += decodeP(input, inOff, p12, len - inOff); -// inOff += decodeP(input, inOff, p21, len - inOff); -// inOff += decodeAlpha(input, inOff, aAlpha, len - inOff); -// inOff += decodeAlpha(input, inOff, bAlpha, len - inOff); -// inOff += decodeAlpha(input, inOff, qAlpha1, len - inOff); -// inOff += decodeAlpha(input, inOff, qAlpha2, len - inOff); -// return inOff; -// } - public void fill(byte[] input) { int inOff = fillP(input, 0, p11, input.length); @@ -89,58 +77,4 @@ static void copyTo(byte[][][][] alpha, byte[] output) } } } - - -// static int decodeP(byte[] input, int inOff, byte[][][][] p, int len) -// { -// int rlt = 0; -// for (int i = 0; i < p.length; ++i) -// { -// rlt += decodeAlpha(input, inOff + rlt, p[i], len); -// } -// return rlt; -// } - -// private static int decodeAlpha(byte[] input, int inOff, byte[][][] alpha, int len) -// { -// int rlt = 0; -// for (int i = 0; i < alpha.length; ++i) -// { -// for (int j = 0; j < alpha[i].length; ++j) -// { -// int tmp = Math.min(alpha[i][j].length, len << 1); -// GF16Utils.decode(input, inOff + rlt, alpha[i][j], 0, tmp); -// rlt += (tmp + 1) >> 1; -// len -= (tmp + 1) >> 1; -// } -// } -// return rlt; -// } - -// static int encodeP(byte[][][][] p, byte[] output, int outOff, int len) -// { -// int rlt = 0; -// for (int i = 0; i < p.length; ++i) -// { -// rlt += encodeAlpha(p[i], output, outOff + rlt, len - rlt); -// } -// return rlt; -// } - -// static int encodeAlpha(byte[][][] alpha, byte[] output, int outOff, int len) -// { -// int rlt = 0; -// for (int i = 0; i < alpha.length; ++i) -// { -// for (int j = 0; j < alpha[i].length; ++j) -// { -// int tmp = Math.min(alpha[i][j].length, len << 1); -// GF16Utils.encode(alpha[i][j], output, outOff + rlt, tmp); -// rlt += (tmp + 1) >> 1; -// len -= (tmp + 1) >> 1; -// } -// } -// return rlt; -// } - } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java index a4c9cb834e..359accce3a 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java @@ -1,6 +1,6 @@ package org.bouncycastle.pqc.crypto.snova; -public class MapGroup2 +class MapGroup2 { public final byte[][][][] f11; // [m][v][v] public final byte[][][][] f12; // [m][v][o] diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpanded.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpanded.java deleted file mode 100644 index 950f103f40..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpanded.java +++ /dev/null @@ -1,19 +0,0 @@ -//package org.bouncycastle.pqc.crypto.snova; -// -//class PublicKeyExpanded -//{ -// public final byte[] publicKeySeed; -// public final GF16Matrix[][][] P22; // [m][o][o] -// public final MapGroup1 map1; -// -// public PublicKeyExpanded(SnovaParameters params) -// { -// int m = params.getM(); -// int o = params.getO(); -// int rank = params.getL(); -// -// publicKeySeed = new byte[SnovaKeyPairGenerator.publicSeedLength]; -// P22 = GF16Utils.create3DArray(m, o, o, rank); -// map1 = new MapGroup1(params); -// } -//} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpandedPack.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpandedPack.java deleted file mode 100644 index fc73b52dec..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKeyExpandedPack.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.bouncycastle.pqc.crypto.snova; - -class PublicKeyExpandedPack -{ - public final byte[] publicKeySeed; - public final byte[] packedData; - - public PublicKeyExpandedPack(SnovaParameters params) - { - publicKeySeed = new byte[SnovaKeyPairGenerator.publicSeedLength]; - int m = params.getM(); - int o = params.getO(); - int v = params.getV(); - int alpha = params.getAlpha(); - packedData = new byte[(((m * o * o) << 4) + // P22_t - (m * v * v * 16 + m * v * o * 16 + m * o * v * 16 + m * alpha * 16 * 4) // map_group1 - + 1) >> 1]; - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 64ca2d51ce..21dc9de146 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -13,7 +13,6 @@ public class SnovaEngine { private final SnovaParameters params; private final int l; - private final int lsq; final byte[][] S; final int[][] xS; @@ -21,7 +20,7 @@ public SnovaEngine(SnovaParameters params) { this.params = params; this.l = params.getL(); - this.lsq = l * l; + int lsq = l * l; S = new byte[l][lsq]; xS = new int[l][lsq]; be_aI(S[0], 0, (byte)1); @@ -145,7 +144,6 @@ public void makeInvertibleByAddingAS(byte[] source, int off) return; } } - //throw new IllegalStateException("Failed to make matrix invertible"); } private byte gf16Determinant(byte[] matrix, int off) @@ -155,7 +153,7 @@ private byte gf16Determinant(byte[] matrix, int off) case 2: return determinant2x2(matrix, off); case 3: - return determinant3x3(matrix, off, 0, 1, 2, 0, 1, 2); + return determinant3x3(matrix, off, 0, 1, 2); case 4: return determinant4x4(matrix, off); case 5: @@ -172,22 +170,22 @@ private byte determinant2x2(byte[] m, int off) gf16Mul(getGF16m(m, 0, off + 1), getGF16m(m, 1, off))); } - private byte determinant3x3(byte[] m, int off, int i0, int i1, int i2, int j0, int j1, int j2) + private byte determinant3x3(byte[] m, int off, int i0, int i1, int i2) { return gf16Add( gf16Add( - gf16Mul(getGF16m(m, j0, off + i0), gf16Add( - gf16Mul(getGF16m(m, j1, off + i1), getGF16m(m, j2, off + i2)), - gf16Mul(getGF16m(m, j1, off + i2), getGF16m(m, j2, off + i1)) + gf16Mul(getGF16m(m, 0, off + i0), gf16Add( + gf16Mul(getGF16m(m, 1, off + i1), getGF16m(m, 2, off + i2)), + gf16Mul(getGF16m(m, 1, off + i2), getGF16m(m, 2, off + i1)) )), - gf16Mul(getGF16m(m, j0, off + i1), gf16Add( - gf16Mul(getGF16m(m, j1, off + i0), getGF16m(m, j2, off + i2)), - gf16Mul(getGF16m(m, j1, off + i2), getGF16m(m, j2, off + i0)) + gf16Mul(getGF16m(m, 0, off + i1), gf16Add( + gf16Mul(getGF16m(m, 1, off + i0), getGF16m(m, 2, off + i2)), + gf16Mul(getGF16m(m, 1, off + i2), getGF16m(m, 2, off + i0)) )) ), - gf16Mul(getGF16m(m, j0, off + i2), gf16Add( - gf16Mul(getGF16m(m, j1, off + i0), getGF16m(m, j2, off + i1)), - gf16Mul(getGF16m(m, j1, off + i1), getGF16m(m, j2, off + i0)) + gf16Mul(getGF16m(m, 0, off + i2), gf16Add( + gf16Mul(getGF16m(m, 1, off + i0), getGF16m(m, 2, off + i1)), + gf16Mul(getGF16m(m, 1, off + i1), getGF16m(m, 2, off + i0)) )) ); } @@ -196,34 +194,34 @@ private byte determinant4x4(byte[] m, int off) { byte d0 = gf16Mul(getGF16m(m, 0, off), gf16Add( gf16Add( - pod(m, off, 1, 1, 2, 2, 3, 3, 2, 3, 3, 2), - pod(m, off, 1, 2, 2, 1, 3, 3, 2, 3, 3, 1) + pod(m, off, 1, 2, 3, 3, 2), + pod(m, off, 2, 1, 3, 3, 1) ), - pod(m, off, 1, 3, 2, 1, 3, 2, 2, 2, 3, 1) + pod(m, off, 3, 1, 2, 2, 1) )); byte d1 = gf16Mul(getGF16m(m, 0, off + 1), gf16Add( gf16Add( - pod(m, off, 1, 0, 2, 2, 3, 3, 2, 3, 3, 2), - pod(m, off, 1, 2, 2, 0, 3, 3, 2, 3, 3, 0) + pod(m, off, 0, 2, 3, 3, 2), + pod(m, off, 2, 0, 3, 3, 0) ), - pod(m, off, 1, 3, 2, 0, 3, 2, 2, 2, 3, 0) + pod(m, off, 3, 0, 2, 2, 0) )); byte d2 = gf16Mul(getGF16m(m, 0, off + 2), gf16Add( gf16Add( - pod(m, off, 1, 0, 2, 1, 3, 3, 2, 3, 3, 1), - pod(m, off, 1, 1, 2, 0, 3, 3, 2, 3, 3, 0) + pod(m, off, 0, 1, 3, 3, 1), + pod(m, off, 1, 0, 3, 3, 0) ), - pod(m, off, 1, 3, 2, 0, 3, 1, 2, 1, 3, 0) + pod(m, off, 3, 0, 1, 1, 0) )); byte d3 = gf16Mul(getGF16m(m, 0, off + 3), gf16Add( gf16Add( - pod(m, off, 1, 0, 2, 1, 3, 2, 2, 2, 3, 1), - pod(m, off, 1, 1, 2, 0, 3, 2, 2, 2, 3, 0) + pod(m, off, 0, 1, 2, 2, 1), + pod(m, off, 1, 0, 2, 2, 0) ), - pod(m, off, 1, 2, 2, 0, 3, 1, 2, 1, 3, 0) + pod(m, off, 2, 0, 1, 1, 0) )); return (byte)(d0 ^ d1 ^ d2 ^ d3); @@ -231,47 +229,26 @@ private byte determinant4x4(byte[] m, int off) private byte determinant5x5(byte[] m, int off) { - byte result = gf16Mul(determinant3x3(m, off, 0, 1, 2, 0, 1, 2), + byte result = gf16Mul(determinant3x3(m, off, 0, 1, 2), gf16Add(gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off + 4)), gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 3)))); - result ^= gf16Mul(determinant3x3(m, off, 0, 1, 3, 0, 1, 2), + result ^= gf16Mul(determinant3x3(m, off, 0, 1, 3), gf16Add(gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off + 4)), gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 2)))); - result ^= gf16Mul(determinant3x3(m, off, 0, 1, 4, 0, 1, 2), + result ^= gf16Mul(determinant3x3(m, off, 0, 1, 4), gf16Add(gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off + 3)), gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off + 2)))); - result ^= gf16Mul(determinant3x3(m, off, 0, 2, 3, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3,off + 1), getGF16m(m, 4,off + 4)), gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4,off + 1)))); - result ^= gf16Mul(determinant3x3(m, off, 0, 2, 4, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 3)), gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4,off + 1)))); - result ^= gf16Mul(determinant3x3(m, off, 0, 3, 4, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 2)), gf16Mul(getGF16m(m, 3,off + 2), getGF16m(m, 4, off + 1)))); - result ^= gf16Mul(determinant3x3(m, off, 1, 2, 3, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3, off + 0), getGF16m(m, 4, off + 4)), gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 0)))); - result ^= gf16Mul(determinant3x3(m, off, 1, 2, 4, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3, off + 0), getGF16m(m, 4, off + 3)), gf16Mul(getGF16m(m, 3,off + 3), getGF16m(m, 4, off + 0)))); - result ^= gf16Mul(determinant3x3(m, off, 1, 3, 4, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3, off + 0), getGF16m(m, 4, off + 2)), gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4,off + 0)))); - result ^= gf16Mul(determinant3x3(m, off, 2, 3, 4, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3, off + 0), getGF16m(m, 4, off + 1)), gf16Mul(getGF16m(m, 3,off + 1), getGF16m(m, 4,off + 0)))); -// return result; -// byte a012 = determinant3x3(m, 0, 1, 2, 0, 1, 2); -// byte b012 = gf16Add(gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 3))); -// byte a013 = determinant3x3(m, 0, 1, 3, 0, 1, 2); -// byte b013 = gf16Add(gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 2))); -// byte a014 = determinant3x3(m, 0, 1, 4, 0, 1, 2); -// byte b014 = gf16Add(gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 3)), gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 2))); -// byte a023 = determinant3x3(m, 0, 2, 3, 0, 1, 2); -// byte b023 = gf16Add(gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 1))); -// byte a024 = determinant3x3(m, 0, 2, 4, 0, 1, 2); -// byte b024 = gf16Add(gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 3)), gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 1))); -// byte a034 = determinant3x3(m, 0, 3, 4, 0, 1, 2); -// byte b034 = gf16Add(gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 2)), gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 1))); -// byte a123 = determinant3x3(m, 1, 2, 3, 0, 1, 2); -// byte b123 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 4)), gf16Mul(getGF16m(m, 3, 4), getGF16m(m, 4, 0))); -// byte a124 = determinant3x3(m, 1, 2, 4, 0, 1, 2); -// byte b124 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 3)), gf16Mul(getGF16m(m, 3, 3), getGF16m(m, 4, 0))); -// byte a134 = determinant3x3(m, 1, 3, 4, 0, 1, 2); -// byte b134 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 2)), gf16Mul(getGF16m(m, 3, 2), getGF16m(m, 4, 0))); -// byte a234 = determinant3x3(m, 2, 3, 4, 0, 1, 2); -// byte b234 = gf16Add(gf16Mul(getGF16m(m, 3, 0), getGF16m(m, 4, 1)), gf16Mul(getGF16m(m, 3, 1), getGF16m(m, 4, 0))); + result ^= gf16Mul(determinant3x3(m, off, 0, 2, 3), + gf16Add(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 4)), gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 1)))); + result ^= gf16Mul(determinant3x3(m, off, 0, 2, 4), + gf16Add(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 3)), gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off + 1)))); + result ^= gf16Mul(determinant3x3(m, off, 0, 3, 4), + gf16Add(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 2)), gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off + 1)))); + result ^= gf16Mul(determinant3x3(m, off, 1, 2, 3), + gf16Add(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 4)), gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off)))); + result ^= gf16Mul(determinant3x3(m, off, 1, 2, 4), + gf16Add(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 3)), gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off)))); + result ^= gf16Mul(determinant3x3(m, off, 1, 3, 4), + gf16Add(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 2)), gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off)))); + result ^= gf16Mul(determinant3x3(m, off, 2, 3, 4), + gf16Add(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 1)), gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off)))); return result; } @@ -292,9 +269,9 @@ private void generateASMatrix(byte[] target, byte a) } // POD -> entry[a][b] * (entry[c][d] * entry[e][f] + entry[g][h] * entry[i][j]) - private byte pod(byte[] m, int off, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j) + private byte pod(byte[] m, int off, int b, int d, int f, int h, int j) { - return gf16Mul(getGF16m(m, a, off + b), (byte)(gf16Mul(getGF16m(m, c, off + d), getGF16m(m, e, off + f)) ^ gf16Mul(getGF16m(m, g, off + h), getGF16m(m, i, off + j)))); + return gf16Mul(getGF16m(m, 1, off + b), (byte)(gf16Mul(getGF16m(m, 2, off + d), getGF16m(m, 3, off + f)) ^ gf16Mul(getGF16m(m, 2, off + h), getGF16m(m, 3, off + j)))); } private void addMatrices(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int cOff) From 35349abdd669fdf54c511ecda92edde9a9329d9c Mon Sep 17 00:00:00 2001 From: David Hook Date: Thu, 27 Mar 2025 12:30:13 +1100 Subject: [PATCH 266/890] added extra doFinal test (relates to github #2035) --- .../jce/provider/test/BlockCipherTest.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java index 2209f35b66..e13ffcafe9 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java @@ -7,6 +7,7 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.InvalidParameterException; @@ -1738,6 +1739,29 @@ public void performTest() testExceptions(); testIncorrectCipherModes(); + doFinalTest(); + } + + private void doFinalTest() + { + try + { + int INPUT_LENGTH = 32; + int offset = 1; + byte[] PT = new byte[INPUT_LENGTH + offset]; + SecretKey KEY = new SecretKeySpec(new byte[16], "AES"); + Cipher c = Cipher.getInstance("AES/ECB/NoPadding", "BC"); + c.init(Cipher.ENCRYPT_MODE, KEY); + int len = c.doFinal(PT, 0, INPUT_LENGTH, PT, offset); + + byte[] expected = Hex.decode("0066e94bd4ef8a2c3b884cfa59ca342b2e66e94bd4ef8a2c3b884cfa59ca342b2e"); + + isTrue("expected not match PT", areEqual(expected, PT)); + } + catch (GeneralSecurityException e) + { + fail(e.toString()); + } } public static void main( From 55bea317b776c9c931230c639fecfb2dd706b6a7 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 27 Mar 2025 14:07:07 +1030 Subject: [PATCH 267/890] Initial fix in DefaultBufferedBlockCipher --- .../crypto/DefaultBufferedBlockCipher.java | 118 ++++++++++-------- 1 file changed, 66 insertions(+), 52 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java index 662a23a011..67bc9f8daf 100644 --- a/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java @@ -14,15 +14,15 @@ public class DefaultBufferedBlockCipher extends BufferedBlockCipher { - protected byte[] buf; - protected int bufOff; + protected byte[] buf; + protected int bufOff; - protected boolean forEncryption; - protected BlockCipher cipher; + protected boolean forEncryption; + protected BlockCipher cipher; protected MultiBlockCipher mbCipher; - protected boolean partialBlockOkay; - protected boolean pgpCFB; + protected boolean partialBlockOkay; + protected boolean pgpCFB; /** * constructor for subclasses @@ -37,7 +37,7 @@ protected DefaultBufferedBlockCipher() * @param cipher the underlying block cipher this buffering object wraps. */ public DefaultBufferedBlockCipher( - BlockCipher cipher) + BlockCipher cipher) { this.cipher = cipher; @@ -57,8 +57,8 @@ public DefaultBufferedBlockCipher( // // check if we can handle partial blocks on doFinal. // - String name = cipher.getAlgorithmName(); - int idx = name.indexOf('/') + 1; + String name = cipher.getAlgorithmName(); + int idx = name.indexOf('/') + 1; pgpCFB = (idx > 0 && name.startsWith("PGP", idx)); @@ -86,14 +86,14 @@ public BlockCipher getUnderlyingCipher() * initialise the cipher. * * @param forEncryption if true the cipher is initialised for - * encryption, if false for decryption. - * @param params the key and other data required by the cipher. - * @exception IllegalArgumentException if the params argument is - * inappropriate. + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @throws IllegalArgumentException if the params argument is + * inappropriate. */ public void init( - boolean forEncryption, - CipherParameters params) + boolean forEncryption, + CipherParameters params) throws IllegalArgumentException { this.forEncryption = forEncryption; @@ -114,7 +114,7 @@ public int getBlockSize() } /** - * return the size of the output buffer required for an update + * return the size of the output buffer required for an update * an input of len bytes. * * @param len the length of the input. @@ -124,7 +124,7 @@ public int getBlockSize() public int getUpdateOutputSize( int len) { - int total = len + bufOff; + int total = len + bufOff; int leftOver; if (pgpCFB) @@ -140,7 +140,7 @@ public int getUpdateOutputSize( } else { - leftOver = total % buf.length; + leftOver = total % buf.length; } return total - leftOver; @@ -169,20 +169,20 @@ public int getOutputSize( /** * process a single byte, producing an output block if necessary. * - * @param in the input byte. - * @param out the space for any output that might be produced. + * @param in the input byte. + * @param out the space for any output that might be produced. * @param outOff the offset from which the output will be copied. * @return the number of output bytes copied to out. - * @exception DataLengthException if there isn't enough space in out. - * @exception IllegalStateException if the cipher isn't initialised. + * @throws DataLengthException if there isn't enough space in out. + * @throws IllegalStateException if the cipher isn't initialised. */ public int processByte( - byte in, - byte[] out, - int outOff) + byte in, + byte[] out, + int outOff) throws DataLengthException, IllegalStateException { - int resultLen = 0; + int resultLen = 0; buf[bufOff++] = in; @@ -198,21 +198,21 @@ public int processByte( /** * process an array of bytes, producing output if necessary. * - * @param in the input byte array. - * @param inOff the offset at which the input data starts. - * @param len the number of bytes to be copied out of the input array. - * @param out the space for any output that might be produced. + * @param in the input byte array. + * @param inOff the offset at which the input data starts. + * @param len the number of bytes to be copied out of the input array. + * @param out the space for any output that might be produced. * @param outOff the offset from which the output will be copied. * @return the number of output bytes copied to out. - * @exception DataLengthException if there isn't enough space in out. - * @exception IllegalStateException if the cipher isn't initialised. + * @throws DataLengthException if there isn't enough space in out. + * @throws IllegalStateException if the cipher isn't initialised. */ public int processBytes( - byte[] in, - int inOff, - int len, - byte[] out, - int outOff) + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) throws DataLengthException, IllegalStateException { if (len < 0) @@ -220,9 +220,9 @@ public int processBytes( throw new IllegalArgumentException("Can't have a negative input length!"); } - int blockSize = getBlockSize(); - int length = getUpdateOutputSize(len); - + int blockSize = getBlockSize(); + int length = getUpdateOutputSize(len); + if (length > 0) { if ((outOff + length) > out.length) @@ -237,12 +237,26 @@ public int processBytes( if (len > gapLen) { System.arraycopy(in, inOff, buf, bufOff, gapLen); + inOff += gapLen; + len -= gapLen; + if (in == out) + { + int inEnd = inOff + len; + int outEnd = outOff + length; + if ((inOff <= outOff && outOff <= inEnd) || + (outOff <= inOff && inOff <= outEnd)) + { + byte[] overlap = new byte[len]; + System.arraycopy(in, inOff, overlap, 0, len); + in = overlap; + inOff = 0; + } + } resultLen += cipher.processBlock(buf, 0, out, outOff); bufOff = 0; - len -= gapLen; - inOff += gapLen; + if (mbCipher != null) { @@ -286,20 +300,20 @@ public int processBytes( /** * Process the last block in the buffer. * - * @param out the array the block currently being held is copied into. + * @param out the array the block currently being held is copied into. * @param outOff the offset at which the copying starts. * @return the number of output bytes copied to out. - * @exception DataLengthException if there is insufficient space in out for - * the output, or the input is not block size aligned and should be. - * @exception IllegalStateException if the underlying cipher is not - * initialised. - * @exception InvalidCipherTextException if padding is expected and not found. - * @exception DataLengthException if the input is not block size - * aligned. + * @throws DataLengthException if there is insufficient space in out for + * the output, or the input is not block size aligned and should be. + * @throws IllegalStateException if the underlying cipher is not + * initialised. + * @throws InvalidCipherTextException if padding is expected and not found. + * @throws DataLengthException if the input is not block size + * aligned. */ public int doFinal( - byte[] out, - int outOff) + byte[] out, + int outOff) throws DataLengthException, IllegalStateException, InvalidCipherTextException { try From 96a59e69cd6b999f646be9d213124b7a1863612c Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 27 Mar 2025 14:53:24 +1030 Subject: [PATCH 268/890] Refactor in verifySignatureCore --- .../pqc/crypto/snova/SnovaSigner.java | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index e0d6dab1d3..fe840ec709 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -16,7 +16,6 @@ public class SnovaSigner private SnovaEngine engine; private SecureRandom random; private final SHAKEDigest digest = new SHAKEDigest(256); - private SnovaPublicKeyParameters pubKey; private SnovaPrivateKeyParameters privKey; @@ -104,8 +103,7 @@ public static void createSignedHash( byte[] digest, int bytesDigest, byte[] ptPublicKeySeed, int seedLengthPublic, byte[] arraySalt, int bytesSalt, - byte[] signedHashOut, int bytesHash - ) + byte[] signedHashOut, int bytesHash) { // Initialize SHAKE256 XOF SHAKEDigest shake = new SHAKEDigest(256); @@ -385,20 +383,15 @@ public boolean verifySignatureCore(byte[] digest, byte[] signature, PublicKey pk final int lsq = params.getLsq(); final int m = params.getM(); final int n = params.getN(); - final int v = params.getV(); final int o = params.getO(); int bytesSignature = ((n * lsq) + 1) >>> 1; - // Extract salt from signature - byte[] ptSalt = Arrays.copyOfRange(signature, bytesSignature, bytesSignature + bytesSalt); - //byte[] signatureBody = Arrays.copyOf(signature, signature.length - bytesSalt); - // Step 1: Regenerate signed hash using public key seed, digest and salt byte[] signedHash = new byte[bytesHash]; SHAKEDigest shake = new SHAKEDigest(256); shake.update(pkx.publicKeySeed, 0, pkx.publicKeySeed.length); shake.update(digest, 0, digest.length); - shake.update(ptSalt, 0, ptSalt.length); + shake.update(signature, bytesSignature, bytesSalt); shake.doFinal(signedHash, 0, bytesHash); // Handle odd-length adjustment (if needed) @@ -412,14 +405,7 @@ public boolean verifySignatureCore(byte[] digest, byte[] signature, PublicKey pk byte[] decodedSig = new byte[n * lsq]; GF16Utils.decode(signature, 0, decodedSig, 0, decodedSig.length); - for (int i = 0; i < n; i++) - { - for (int row = 0; row < l; row++) - { - System.arraycopy(decodedSig, i * lsq + row * l, - signatureGF16Matrix[i][row], 0, l); - } - } + MapGroup1.fillAlpha(decodedSig, 0, signatureGF16Matrix, decodedSig.length); // Step 3: Evaluate signature using public key byte[][][] computedHashMatrix = new byte[m][l][l]; @@ -447,7 +433,6 @@ private void evaluation(byte[][][] hashMatrix, MapGroup1 map1, byte[][][][] p22, final int m = params.getM(); final int alpha = params.getAlpha(); final int n = params.getN(); - final int v = params.getV(); final int l = params.getL(); byte[][][][][] Left = new byte[m][alpha][n][l][l]; @@ -515,7 +500,6 @@ private void evaluation(byte[][][] hashMatrix, MapGroup1 map1, byte[][][][] p22, } } - // Helper method to get appropriate P matrix based on indices private byte[] getPMatrix(MapGroup1 map1, byte[][][][] p22, int mi, int ni, int nj) { final int v = params.getV(); @@ -637,7 +621,6 @@ private void multiplyGF16Matrices(byte[] a, byte[] b, byte[][] result) private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size) { final int cols = size + 1; - byte tGF16; for (int i = 0; i < size; i++) { @@ -723,5 +706,4 @@ private int iPrime(int mi, int alpha) // Implement index calculation based on SNOVA specification return (mi + alpha) % params.getO(); } - } From 64026c292fc434ee74690b79a55c2f404b9e6239 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 27 Mar 2025 14:54:12 +1030 Subject: [PATCH 269/890] Simplify the fix --- .../org/bouncycastle/crypto/DefaultBufferedBlockCipher.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java index 67bc9f8daf..89b6f8c812 100644 --- a/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java @@ -246,9 +246,8 @@ public int processBytes( if ((inOff <= outOff && outOff <= inEnd) || (outOff <= inOff && inOff <= outEnd)) { - byte[] overlap = new byte[len]; - System.arraycopy(in, inOff, overlap, 0, len); - in = overlap; + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); inOff = 0; } } From 67fb4d8b889e765b41f11aa5ee4a16b29d1d3ad4 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 27 Mar 2025 16:03:11 +1030 Subject: [PATCH 270/890] Fix #2035 issue in AEADBaseEngine. --- .../crypto/engines/AEADBaseEngine.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java index 09b8382124..9b7d9cbbf6 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java @@ -733,9 +733,22 @@ protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output m_bufPos += len; return 0; } - resultLength = processor.getUpdateOutputSize(len) + m_bufPos - (forEncryption ? 0 : MAC_SIZE); + int length = processor.getUpdateOutputSize(len); + resultLength = length + m_bufPos - (forEncryption ? 0 : MAC_SIZE); ensureSufficientOutputBuffer(output, outOff, resultLength - resultLength % BlockSize); resultLength = 0; + if (input == output) + { + int inEnd = inOff + len; + int outEnd = outOff + length; + if ((inOff <= outOff && outOff <= inEnd) || + (outOff <= inOff && inOff <= outEnd)) + { + input = new byte[len]; + System.arraycopy(output, inOff, input, 0, len); + inOff = 0; + } + } if (forEncryption) { if (m_bufPos > 0) @@ -746,6 +759,7 @@ protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output processBufferEncrypt(m_buf, 0, output, outOff); resultLength = BlockSize; } + // The function is just an operator >= or > while (processor.isLengthExceedingBlockSize(len, BlockSize)) { From 4e80c99b26c081afe813b0dee3ac645d116f521f Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 27 Mar 2025 16:30:33 +1030 Subject: [PATCH 271/890] Fix input/output overlapping in PaddedBufferedBlockCipher --- .../paddings/PaddedBufferedBlockCipher.java | 16 +- .../bouncycastle/crypto/test/PaddingTest.java | 139 +++++++++++------- 2 files changed, 102 insertions(+), 53 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java index 28ec78bff9..03a41635b4 100644 --- a/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java @@ -202,12 +202,24 @@ public int processBytes( if (len > gapLen) { System.arraycopy(in, inOff, buf, bufOff, gapLen); + inOff += gapLen; + len -= gapLen; + if (in == out) + { + int inEnd = inOff + len; + int outEnd = outOff + length; + if ((inOff <= outOff && outOff <= inEnd) || + (outOff <= inOff && inOff <= outEnd)) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } + } resultLen += cipher.processBlock(buf, 0, out, outOff); bufOff = 0; - len -= gapLen; - inOff += gapLen; while (len > buf.length) { diff --git a/core/src/test/java/org/bouncycastle/crypto/test/PaddingTest.java b/core/src/test/java/org/bouncycastle/crypto/test/PaddingTest.java index c963b26e4f..379a6c2dc0 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/PaddingTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/PaddingTest.java @@ -13,6 +13,7 @@ import org.bouncycastle.crypto.paddings.ZeroBytePadding; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -27,28 +28,28 @@ public PaddingTest() } private void blockCheck( - PaddedBufferedBlockCipher cipher, - BlockCipherPadding padding, - KeyParameter key, - byte[] data) + PaddedBufferedBlockCipher cipher, + BlockCipherPadding padding, + KeyParameter key, + byte[] data) { - byte[] out = new byte[data.length + 8]; - byte[] dec = new byte[data.length]; - + byte[] out = new byte[data.length + 8]; + byte[] dec = new byte[data.length]; + try - { + { cipher.init(true, key); - - int len = cipher.processBytes(data, 0, data.length, out, 0); - + + int len = cipher.processBytes(data, 0, data.length, out, 0); + len += cipher.doFinal(out, len); - + cipher.init(false, key); - - int decLen = cipher.processBytes(out, 0, len, dec, 0); - + + int decLen = cipher.processBytes(out, 0, len, dec, 0); + decLen += cipher.doFinal(dec, decLen); - + if (!areEqual(data, dec)) { fail("failed to decrypt - i = " + data.length + ", padding = " + padding.getPaddingName()); @@ -59,31 +60,31 @@ private void blockCheck( fail("Exception - " + e.toString(), e); } } - + public void testPadding( - BlockCipherPadding padding, - SecureRandom rand, - byte[] ffVector, - byte[] ZeroVector) + BlockCipherPadding padding, + SecureRandom rand, + byte[] ffVector, + byte[] ZeroVector) { - PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new DESEngine(), padding); - KeyParameter key = new KeyParameter(Hex.decode("0011223344556677")); - + PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new DESEngine(), padding); + KeyParameter key = new KeyParameter(Hex.decode("0011223344556677")); + // // ff test // - byte[] data = { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0, (byte)0, (byte)0, (byte)0, (byte)0 }; - + byte[] data = {(byte)0xff, (byte)0xff, (byte)0xff, (byte)0, (byte)0, (byte)0, (byte)0, (byte)0}; + if (ffVector != null) { padding.addPadding(data, 3); - + if (!areEqual(data, ffVector)) { fail("failed ff test for " + padding.getPaddingName()); } } - + // // zero test // @@ -91,23 +92,23 @@ public void testPadding( { data = new byte[8]; padding.addPadding(data, 4); - + if (!areEqual(data, ZeroVector)) { fail("failed zero test for " + padding.getPaddingName()); } } - + for (int i = 1; i != 200; i++) { data = new byte[i]; - + rand.nextBytes(data); blockCheck(cipher, padding, key, data); } } - + private void testOutputSizes() { PaddedBufferedBlockCipher bc = new PaddedBufferedBlockCipher(new DESEngine(), new PKCS7Padding()); @@ -138,15 +139,51 @@ private void testOutputSizes() } } + private void testOverlapping() + { + //Skip the dofinal of the test + PaddedBufferedBlockCipher bc = new PaddedBufferedBlockCipher(new DESEngine(), new PKCS7Padding()); + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[8]; + random.nextBytes(keyBytes); + KeyParameter key = new KeyParameter(keyBytes); + + int offset = 2 + random.nextInt(bc.getBlockSize() - 1); + byte[] data = new byte[bc.getBlockSize() * 2 + offset]; + byte[] expected = new byte[bc.getOutputSize(bc.getBlockSize() * 2)]; + random.nextBytes(data); + + bc.init(true, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, expected, 0); + bc.init(true, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) + { + fail("failed to overlapping of encryption"); + } + + bc.init(false, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, expected, 0); + bc.init(false, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) + { + fail("failed to overlapping of encryption"); + } + } + public void performTest() { - SecureRandom rand = new SecureRandom(new byte[20]); - + testOverlapping(); + SecureRandom rand = new SecureRandom(new byte[20]); + rand.setSeed(System.currentTimeMillis()); - + testPadding(new PKCS7Padding(), rand, - Hex.decode("ffffff0505050505"), - Hex.decode("0000000004040404")); + Hex.decode("ffffff0505050505"), + Hex.decode("0000000004040404")); PKCS7Padding padder = new PKCS7Padding(); try @@ -161,27 +198,27 @@ public void performTest() { fail("wrong exception for corrupt padding: " + e); } - } + } testPadding(new ISO10126d2Padding(), rand, - null, - null); - + null, + null); + testPadding(new X923Padding(), rand, - null, - null); + null, + null); testPadding(new TBCPadding(), rand, - Hex.decode("ffffff0000000000"), - Hex.decode("00000000ffffffff")); + Hex.decode("ffffff0000000000"), + Hex.decode("00000000ffffffff")); testPadding(new ZeroBytePadding(), rand, - Hex.decode("ffffff0000000000"), - null); - + Hex.decode("ffffff0000000000"), + null); + testPadding(new ISO7816d4Padding(), rand, - Hex.decode("ffffff8000000000"), - Hex.decode("0000000080000000")); + Hex.decode("ffffff8000000000"), + Hex.decode("0000000080000000")); testOutputSizes(); @@ -193,7 +230,7 @@ public String getName() } public static void main( - String[] args) + String[] args) { runTest(new PaddingTest()); } From 2214d2dcc0f5470afc0ae0294020eb58f4ca380b Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 27 Mar 2025 17:30:21 +1030 Subject: [PATCH 272/890] TODO: Fix the bug in Romulus-M --- .../crypto/engines/AEADBaseEngine.java | 12 +++++++ .../bouncycastle/crypto/test/AsconTest.java | 6 ++++ .../bouncycastle/crypto/test/CipherTest.java | 35 +++++++++++++++++++ .../crypto/test/ElephantTest.java | 3 ++ .../crypto/test/GiftCofbTest.java | 1 + .../crypto/test/Grain128AEADTest.java | 2 ++ .../bouncycastle/crypto/test/ISAPTest.java | 4 +++ .../crypto/test/PhotonBeetleTest.java | 2 ++ .../bouncycastle/crypto/test/SparkleTest.java | 5 +++ .../bouncycastle/crypto/test/XoodyakTest.java | 1 + 10 files changed, 71 insertions(+) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java index 9b7d9cbbf6..15519e1a0e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java @@ -587,6 +587,18 @@ public int processByte(byte input, byte[] output, int outOff) @Override public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { + if (input == output) + { + int inEnd = inOff + len; + int outEnd = outOff + processor.getUpdateOutputSize(len); + if ((inOff <= outOff && outOff <= inEnd) || + (outOff <= inOff && inOff <= outEnd)) + { + input = new byte[len]; + System.arraycopy(output, inOff, input, 0, len); + inOff = 0; + } + } boolean forEncryption = checkData(false); if (forEncryption) { diff --git a/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java b/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java index d92c10575d..cebd5f8f9a 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java @@ -108,6 +108,12 @@ public void performTest() CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon128a)); CipherTest.checkAEADParemeter(this, 20, 16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon80pq)); + CipherTest.testOverlapping(this,16, 16, 16, 16, new AsconAEAD128()); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon128)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon128a)); + CipherTest.testOverlapping(this, 20, 16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon80pq)); + + CipherTest.checkCipher(32, 16, 100, 128, new CipherTest.Instance() { @Override diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java index e88201d44e..19ac878202 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java @@ -908,4 +908,39 @@ static void implTestExceptionsGetUpdateOutputSize(AEADCipher cipher, boolean for } } } + + static void testOverlapping(SimpleTest test, int keySize, int ivSize, int macSize, int blockSize, AEADCipher cipher) + throws Exception + { + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[keySize]; + byte[] ivBytes = new byte[ivSize]; + int offset = 1 + random.nextInt(blockSize - 1); + byte[] data = new byte[blockSize * 2 + offset + macSize]; + byte[] expected; + random.nextBytes(keyBytes); + random.nextBytes(ivBytes); + random.nextBytes(data); + AEADParameters parameters = new AEADParameters(new KeyParameter(new byte[keySize]), macSize * 8, new byte[ivSize], null); + cipher.init(true, parameters); + expected = new byte[cipher.getOutputSize(blockSize * 2)]; + int len = cipher.processBytes(data, 0, blockSize * 2, expected, 0); + cipher.doFinal(expected, len); + cipher.init(true, parameters); + len = cipher.processBytes(data, 0, blockSize * 2, data, offset); + cipher.doFinal(data, len + offset); + test.isTrue("fail on testing overlapping of encryption for " + cipher.getAlgorithmName(), + Arrays.areEqual(expected, 0, expected.length, data, offset, offset + expected.length)); + System.arraycopy(data, offset, data, 0, expected.length); + cipher.init(false, parameters); + expected = new byte[cipher.getOutputSize(data.length)]; + len = cipher.processBytes(data, 0, blockSize * 2 + macSize, expected, 0); + cipher.doFinal(expected, len); + cipher.init(false, parameters); + len = cipher.processBytes(data, 0, blockSize * 2 + macSize, data, offset); + cipher.doFinal(data, len + offset); + test.isTrue("fail on testing overlapping of decryption for " + cipher.getAlgorithmName(), + Arrays.areEqual(expected, 0, blockSize * 2, data, offset, offset + blockSize * 2)); + + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java index 82e332918c..5d2408a8d7 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java @@ -38,6 +38,9 @@ public void performTest() CipherTest.checkAEADParemeter(this, 16, 12, 8, 20, new ElephantEngine(ElephantEngine.ElephantParameters.elephant160)); CipherTest.checkAEADParemeter(this, 16, 12, 8, 22, new ElephantEngine(ElephantEngine.ElephantParameters.elephant176)); CipherTest.checkAEADParemeter(this, 16, 12, 16, 25, new ElephantEngine(ElephantEngine.ElephantParameters.elephant200)); + CipherTest.testOverlapping(this, 16, 12, 8, 20, new ElephantEngine(ElephantEngine.ElephantParameters.elephant160)); + CipherTest.testOverlapping(this, 16, 12, 8, 22, new ElephantEngine(ElephantEngine.ElephantParameters.elephant176)); + CipherTest.testOverlapping(this, 16, 12, 16, 25, new ElephantEngine(ElephantEngine.ElephantParameters.elephant200)); CipherTest.checkAEADCipherOutputSize(this, 16, 12, 20, 8, new ElephantEngine(ElephantEngine.ElephantParameters.elephant160)); CipherTest.checkAEADCipherOutputSize(this, 16, 12, 22, 8, new ElephantEngine(ElephantEngine.ElephantParameters.elephant176)); CipherTest.checkAEADCipherOutputSize(this, 16, 12, 25, 16, new ElephantEngine(ElephantEngine.ElephantParameters.elephant200)); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java b/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java index 26935bedb8..a7d18239f1 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java @@ -38,6 +38,7 @@ public AEADCipher createInstance() }); implTestParametersEngine(new GiftCofbEngine(), 16, 16, 16); CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new GiftCofbEngine()); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new GiftCofbEngine()); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new GiftCofbEngine()); CipherTest.checkCipher(16, 16, 40, 128, new CipherTest.Instance() diff --git a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java index 89da222118..5937166965 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java @@ -24,6 +24,7 @@ public String getName() public void performTest() throws Exception { + CipherTest.testOverlapping(this, 16, 12, 8, 20, new Grain128AEADEngine()); CipherTest.implTestVectorsEngine(new Grain128AEADEngine(), "crypto", "LWC_AEAD_KAT_128_96.txt", this); checkAEADCipherOutputSize(this, 16, 12, 8, new Grain128AEADEngine()); CipherTest.checkCipher(32, 12, 100, 128, new CipherTest.Instance() @@ -38,6 +39,7 @@ public AEADCipher createInstance() CipherTest.checkAEADParemeter(this, 16, 12, 8, 20, new Grain128AEADEngine()); + testSplitUpdate(); testExceptions(); testLongAEAD(); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java index ba91962ad4..007fa98f90 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java @@ -88,6 +88,10 @@ public AEADCipher createInstance() CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new ISAPEngine(IsapType.ISAP_K_128)); CipherTest.checkAEADParemeter(this, 16, 16, 16, 8, new ISAPEngine(IsapType.ISAP_A_128A)); CipherTest.checkAEADParemeter(this, 16, 16, 16, 8, new ISAPEngine(IsapType.ISAP_A_128)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new ISAPEngine(IsapType.ISAP_K_128A)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new ISAPEngine(IsapType.ISAP_K_128)); + CipherTest.testOverlapping(this, 16, 16, 16, 8, new ISAPEngine(IsapType.ISAP_A_128A)); + CipherTest.testOverlapping(this, 16, 16, 16, 8, new ISAPEngine(IsapType.ISAP_A_128)); CipherTest.checkAEADCipherOutputSize(this, 16, 16, 18, 16, new ISAPEngine(IsapType.ISAP_K_128A)); CipherTest.checkAEADCipherOutputSize(this, 16, 16, 18, 16, new ISAPEngine(IsapType.ISAP_K_128)); CipherTest.checkAEADCipherOutputSize(this, 16, 16, 8, 16, new ISAPEngine(IsapType.ISAP_A_128A)); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java b/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java index 1814847c0d..3e223b93c5 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java @@ -50,6 +50,8 @@ public void performTest() testExceptions(new PhotonBeetleDigest(), 32); CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb128)); CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb32)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb128)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb32)); CipherTest.checkAEADCipherOutputSize(this, 16, 16, 16, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb128)); CipherTest.checkAEADCipherOutputSize(this, 16, 16, 4, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb32)); } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java index f9fd990e8c..ab76af8a2a 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java @@ -70,6 +70,11 @@ public void performTest() CipherTest.checkAEADParemeter(this, 16, 32, 16, 16, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_128)); CipherTest.checkAEADParemeter(this, 32, 32, 32, 32, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_256)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM128_128)); + CipherTest.testOverlapping(this, 24, 24, 24, 24, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM192_192)); + CipherTest.testOverlapping(this, 16, 32, 16, 16, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_128)); + CipherTest.testOverlapping(this, 32, 32, 32, 32, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_256)); + CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM128_128)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 24, 192, 24, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM192_192)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 32, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_128)); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java b/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java index b32d581a74..37bb098399 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java @@ -53,6 +53,7 @@ public AEADCipher createInstance() testExceptions(new XoodyakDigest(), 32); CipherTest.checkAEADCipherOutputSize(this, 16, 16, 24, 16, new XoodyakEngine()); CipherTest.checkAEADParemeter(this, 16, 16, 16, 24, new XoodyakEngine()); + CipherTest.testOverlapping(this, 16, 16, 16, 24, new XoodyakEngine()); } private void testVectors() From 0161defaed2347ade8f11b77af1a53bf4b8dfca5 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 27 Mar 2025 17:33:54 +1030 Subject: [PATCH 273/890] Fix the bug in Romulus-M --- .../java/org/bouncycastle/crypto/engines/RomulusEngine.java | 2 +- .../test/java/org/bouncycastle/crypto/test/RomulusTest.java | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index f0b2ba1484..43603495cd 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -137,7 +137,7 @@ public void processFinalBlock(byte[] output, int outOff) int adlen = aadOperator.getLen(); int mlen = dataOperator.getLen() - (forEncryption ? 0 : MAC_SIZE); byte[] m = ((StreamDataOperator)dataOperator).getBytes(); - int xlen, mOff = 0, mauth = 0; + int xlen, mOff = 0, mauth = outOff; xlen = mlen; if ((adlen & 31) == 0 && adlen != 0) { diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java index cf20ff06ec..4b0bf9c3e8 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java @@ -83,6 +83,9 @@ public AEADCipher createInstance() CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM)); CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT)); CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN)); From d3ed581c4ab574ca21d721d0e54a9dd91e420825 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 27 Mar 2025 17:38:41 +1030 Subject: [PATCH 274/890] Refactor in snovaShake --- .../pqc/crypto/snova/SnovaEngine.java | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 21dc9de146..90b0178b44 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -8,6 +8,7 @@ import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Pack; public class SnovaEngine { @@ -589,14 +590,15 @@ public static void snovaShake(byte[] ptSeed, int outputBytes, byte[] out) long blockCounter = 0; int offset = 0; int remaining = outputBytes; - + byte[] counterBytes = new byte[8]; while (remaining > 0) { SHAKEDigest shake = new SHAKEDigest(128); // Process seed + counter shake.update(ptSeed, 0, ptSeed.length); - updateWithCounter(shake, blockCounter); + Pack.longToLittleEndian(blockCounter, counterBytes, 0); + shake.update(counterBytes, 0, 8); // Calculate bytes to generate in this iteration int bytesToGenerate = Math.min(remaining, SHAKE128_RATE); @@ -609,15 +611,4 @@ public static void snovaShake(byte[] ptSeed, int outputBytes, byte[] out) blockCounter++; } } - - private static void updateWithCounter(SHAKEDigest shake, long counter) - { - byte[] counterBytes = new byte[8]; - // Little-endian conversion - for (int i = 0; i < 8; i++) - { - counterBytes[i] = (byte)(counter >> (i * 8)); - } - shake.update(counterBytes, 0, 8); - } } From 7d4df682d0c1431bee1bc98440170156a3b91b3e Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 27 Mar 2025 17:45:13 +1030 Subject: [PATCH 275/890] Refactor in SnovaEngine --- .../pqc/crypto/snova/SnovaEngine.java | 85 ++++++++----------- 1 file changed, 36 insertions(+), 49 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 90b0178b44..731d2bef6f 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -132,7 +132,6 @@ public void makeInvertibleByAddingAS(byte[] source, int off) return; } - byte[] temp = new byte[l * l]; for (int a = 1; a < 16; a++) @@ -166,29 +165,23 @@ private byte gf16Determinant(byte[] matrix, int off) private byte determinant2x2(byte[] m, int off) { - return gf16Add( - gf16Mul(getGF16m(m, 0, off), getGF16m(m, 1, off + 1)), - gf16Mul(getGF16m(m, 0, off + 1), getGF16m(m, 1, off))); + return (byte) + (gf16Mul(getGF16m(m, 0, off), getGF16m(m, 1, off + 1)) ^ + gf16Mul(getGF16m(m, 0, off + 1), getGF16m(m, 1, off))); } private byte determinant3x3(byte[] m, int off, int i0, int i1, int i2) { - return gf16Add( - gf16Add( - gf16Mul(getGF16m(m, 0, off + i0), gf16Add( - gf16Mul(getGF16m(m, 1, off + i1), getGF16m(m, 2, off + i2)), - gf16Mul(getGF16m(m, 1, off + i2), getGF16m(m, 2, off + i1)) - )), - gf16Mul(getGF16m(m, 0, off + i1), gf16Add( - gf16Mul(getGF16m(m, 1, off + i0), getGF16m(m, 2, off + i2)), - gf16Mul(getGF16m(m, 1, off + i2), getGF16m(m, 2, off + i0)) - )) - ), - gf16Mul(getGF16m(m, 0, off + i2), gf16Add( - gf16Mul(getGF16m(m, 1, off + i0), getGF16m(m, 2, off + i1)), - gf16Mul(getGF16m(m, 1, off + i1), getGF16m(m, 2, off + i0)) - )) - ); + return (byte)( + gf16Mul(getGF16m(m, 0, off + i0), (byte)( + gf16Mul(getGF16m(m, 1, off + i1), getGF16m(m, 2, off + i2)) ^ + gf16Mul(getGF16m(m, 1, off + i2), getGF16m(m, 2, off + i1)))) ^ + gf16Mul(getGF16m(m, 0, off + i1), (byte)( + gf16Mul(getGF16m(m, 1, off + i0), getGF16m(m, 2, off + i2)) ^ + gf16Mul(getGF16m(m, 1, off + i2), getGF16m(m, 2, off + i0)))) ^ + gf16Mul(getGF16m(m, 0, off + i2), (byte)( + gf16Mul(getGF16m(m, 1, off + i0), getGF16m(m, 2, off + i1)) ^ + gf16Mul(getGF16m(m, 1, off + i1), getGF16m(m, 2, off + i0))))); } private byte determinant4x4(byte[] m, int off) @@ -501,7 +494,29 @@ void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) if (params.isPkExpandShake()) { - snovaShake(pkSeed, prngOutput.length, prngOutput); + final int SHAKE128_RATE = 168; // 1344-bit rate = 168 bytes + long blockCounter = 0; + int offset = 0; + int remaining = prngOutput.length; + byte[] counterBytes = new byte[8]; + SHAKEDigest shake = new SHAKEDigest(128); + while (remaining > 0) + { + // Process seed + counter + shake.update(pkSeed, 0, pkSeed.length); + Pack.longToLittleEndian(blockCounter, counterBytes, 0); + shake.update(counterBytes, 0, 8); + + // Calculate bytes to generate in this iteration + int bytesToGenerate = Math.min(remaining, SHAKE128_RATE); + + // Generate output (XOF mode) + shake.doFinal(prngOutput, offset, bytesToGenerate); + + offset += bytesToGenerate; + remaining -= bytesToGenerate; + blockCounter++; + } } else { @@ -583,32 +598,4 @@ void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq * 3, map1.qAlpha2, (m - 3) * o * alpha * lsq); } } - - public static void snovaShake(byte[] ptSeed, int outputBytes, byte[] out) - { - final int SHAKE128_RATE = 168; // 1344-bit rate = 168 bytes - long blockCounter = 0; - int offset = 0; - int remaining = outputBytes; - byte[] counterBytes = new byte[8]; - while (remaining > 0) - { - SHAKEDigest shake = new SHAKEDigest(128); - - // Process seed + counter - shake.update(ptSeed, 0, ptSeed.length); - Pack.longToLittleEndian(blockCounter, counterBytes, 0); - shake.update(counterBytes, 0, 8); - - // Calculate bytes to generate in this iteration - int bytesToGenerate = Math.min(remaining, SHAKE128_RATE); - - // Generate output (XOF mode) - shake.doFinal(out, offset, bytesToGenerate); - - offset += bytesToGenerate; - remaining -= bytesToGenerate; - blockCounter++; - } - } } From 89286d29c5edc94a2617af710076054e85b38140 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 28 Mar 2025 09:55:50 +1030 Subject: [PATCH 276/890] Fix the issues in BufferedBlockCipher and CTSBlockCipher --- .../crypto/BufferedBlockCipher.java | 16 ++- .../crypto/modes/CTSBlockCipher.java | 18 ++- .../org/bouncycastle/crypto/test/CTSTest.java | 132 ++++++++++++------ .../jce/provider/test/BlockCipherTest.java | 42 ++++++ 4 files changed, 160 insertions(+), 48 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java index 6a2974147c..93feecf704 100644 --- a/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java @@ -235,12 +235,24 @@ public int processBytes( if (len > gapLen) { System.arraycopy(in, inOff, buf, bufOff, gapLen); + inOff += gapLen; + len -= gapLen; + if (in == out) + { + int inEnd = inOff + len; + int outEnd = outOff + length; + if ((inOff <= outOff && outOff <= inEnd) || + (outOff <= inOff && inOff <= outEnd)) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } + } resultLen += cipher.processBlock(buf, 0, out, outOff); bufOff = 0; - len -= gapLen; - inOff += gapLen; if (mbCipher != null) { diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java index af1fa46ded..b2a441c982 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java @@ -146,15 +146,25 @@ public int processBytes( if (len > gapLen) { System.arraycopy(in, inOff, buf, bufOff, gapLen); - + inOff += gapLen; + len -= gapLen; + if (in == out) + { + int inEnd = inOff + len; + int outEnd = outOff + length; + if ((inOff <= outOff && outOff <= inEnd) || + (outOff <= inOff && inOff <= outEnd)) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } + } resultLen += cipher.processBlock(buf, 0, out, outOff); System.arraycopy(buf, blockSize, buf, 0, blockSize); bufOff = blockSize; - len -= gapLen; - inOff += gapLen; - while (len > blockSize) { System.arraycopy(in, inOff, buf, bufOff, blockSize); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java index e59c95844b..da0ed221db 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java @@ -1,5 +1,7 @@ package org.bouncycastle.crypto.test; +import java.security.SecureRandom; + import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.CipherParameters; @@ -14,6 +16,7 @@ import org.bouncycastle.crypto.modes.SICBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -23,22 +26,22 @@ public class CTSTest extends SimpleTest { - static byte[] in1 = Hex.decode("4e6f7720697320746865207420"); - static byte[] in2 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f0aaa"); - static byte[] out1 = Hex.decode("9952f131588465033fa40e8a98"); - static byte[] out2 = Hex.decode("358f84d01eb42988dc34efb994"); - static byte[] out3 = Hex.decode("170171cfad3f04530c509b0c1f0be0aefbd45a8e3755a873bff5ea198504b71683c6"); - + static byte[] in1 = Hex.decode("4e6f7720697320746865207420"); + static byte[] in2 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f0aaa"); + static byte[] out1 = Hex.decode("9952f131588465033fa40e8a98"); + static byte[] out2 = Hex.decode("358f84d01eb42988dc34efb994"); + static byte[] out3 = Hex.decode("170171cfad3f04530c509b0c1f0be0aefbd45a8e3755a873bff5ea198504b71683c6"); + private void testCTS( - int id, - BlockCipher cipher, - CipherParameters params, - byte[] input, - byte[] output) + int id, + BlockCipher cipher, + CipherParameters params, + byte[] input, + byte[] output) throws Exception { - byte[] out = new byte[input.length]; - BufferedBlockCipher engine = new CTSBlockCipher(cipher); + byte[] out = new byte[input.length]; + BufferedBlockCipher engine = new CTSBlockCipher(cipher); engine.init(true, params); @@ -64,15 +67,15 @@ private void testCTS( } private void testOldCTS( - int id, - BlockCipher cipher, - CipherParameters params, - byte[] input, - byte[] output) - throws Exception + int id, + BlockCipher cipher, + CipherParameters params, + byte[] input, + byte[] output) + throws Exception { - byte[] out = new byte[input.length]; - BufferedBlockCipher engine = new OldCTSBlockCipher(cipher); + byte[] out = new byte[input.length]; + BufferedBlockCipher engine = new OldCTSBlockCipher(cipher); engine.init(true, params); @@ -97,77 +100,121 @@ private void testOldCTS( } } - private void testExceptions() throws InvalidCipherTextException + private void testExceptions() + throws InvalidCipherTextException { BufferedBlockCipher engine = new CTSBlockCipher(new DESEngine()); CipherParameters params = new KeyParameter(new byte[engine.getBlockSize()]); engine.init(true, params); byte[] out = new byte[engine.getOutputSize(engine.getBlockSize())]; - + engine.processBytes(new byte[engine.getBlockSize() - 1], 0, engine.getBlockSize() - 1, out, 0); - try + try { engine.doFinal(out, 0); fail("Expected CTS encrypt error on < 1 block input"); - } catch(DataLengthException e) + } + catch (DataLengthException e) { // Expected } engine.init(true, params); engine.processBytes(new byte[engine.getBlockSize()], 0, engine.getBlockSize(), out, 0); - try + try { engine.doFinal(out, 0); - } catch(DataLengthException e) + } + catch (DataLengthException e) { fail("Unexpected CTS encrypt error on == 1 block input"); } engine.init(false, params); engine.processBytes(new byte[engine.getBlockSize() - 1], 0, engine.getBlockSize() - 1, out, 0); - try + try { engine.doFinal(out, 0); fail("Expected CTS decrypt error on < 1 block input"); - } catch(DataLengthException e) + } + catch (DataLengthException e) { // Expected } engine.init(false, params); engine.processBytes(new byte[engine.getBlockSize()], 0, engine.getBlockSize(), out, 0); - try + try { engine.doFinal(out, 0); - } catch(DataLengthException e) + } + catch (DataLengthException e) { fail("Unexpected CTS decrypt error on == 1 block input"); } - try + try { new CTSBlockCipher(SICBlockCipher.newInstance(AESEngine.newInstance())); fail("Expected CTS construction error - only ECB/CBC supported."); - } catch(IllegalArgumentException e) + } + catch (IllegalArgumentException e) { // Expected } } + private void testOverlapping() + throws Exception + { + //Skip the dofinal of the test + CTSBlockCipher bc = new CTSBlockCipher(AESEngine.newInstance()); + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[16]; + random.nextBytes(keyBytes); + KeyParameter key = new KeyParameter(keyBytes); + + int offset = 1 + random.nextInt(bc.getBlockSize() - 1) + bc.getBlockSize(); + byte[] data = new byte[bc.getBlockSize() * 4 + offset]; + byte[] expected = new byte[bc.getOutputSize(bc.getBlockSize() * 3)]; + random.nextBytes(data); + + bc.init(true, key); + int len = bc.processBytes(data, 0, expected.length, expected, 0); + bc.doFinal(expected, len); + bc.init(true, key); + len = bc.processBytes(data, 0, expected.length, data, offset); + bc.doFinal(data, offset + len); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + + bc.init(false, key); + bc.processBytes(data, 0, expected.length, expected, 0); + bc.init(false, key); + bc.processBytes(data, 0, expected.length, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + } + public String getName() { return "CTS"; } - public void performTest() + public void performTest() throws Exception { - byte[] key1 = { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF }; - byte[] key2 = { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF, (byte)0xee, (byte)0xff }; - byte[] iv = { 1, 2, 3, 4, 5, 6, 7, 8 }; + byte[] key1 = {(byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF}; + byte[] key2 = {(byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF, (byte)0xee, (byte)0xff}; + byte[] iv = {1, 2, 3, 4, 5, 6, 7, 8}; testCTS(1, new DESEngine(), new KeyParameter(key1), in1, out1); testCTS(2, new CBCBlockCipher(new DESEngine()), new ParametersWithIV(new KeyParameter(key1), iv), in1, out2); @@ -177,11 +224,11 @@ public void performTest() // test vectors from rfc3962 // byte[] aes128 = Hex.decode("636869636b656e207465726979616b69"); - byte[] aesIn1 = Hex.decode("4920776f756c64206c696b652074686520"); + byte[] aesIn1 = Hex.decode("4920776f756c64206c696b652074686520"); byte[] aesOut1 = Hex.decode("c6353568f2bf8cb4d8a580362da7ff7f97"); - byte[] aesIn2 = Hex.decode("4920776f756c64206c696b65207468652047656e6572616c20476175277320"); + byte[] aesIn2 = Hex.decode("4920776f756c64206c696b65207468652047656e6572616c20476175277320"); byte[] aesOut2 = Hex.decode("fc00783e0efdb2c1d445d4c8eff7ed2297687268d6ecccc0c07b25e25ecfe5"); - byte[] aesIn3 = Hex.decode("4920776f756c64206c696b65207468652047656e6572616c2047617527732043"); + byte[] aesIn3 = Hex.decode("4920776f756c64206c696b65207468652047656e6572616c2047617527732043"); byte[] aesOut3 = Hex.decode("39312523a78662d5be7fcbcc98ebf5a897687268d6ecccc0c07b25e25ecfe584"); testCTS(4, new CBCBlockCipher(AESEngine.newInstance()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aesIn1, aesOut1); @@ -202,16 +249,17 @@ public void performTest() testOldCTS(9, new CBCBlockCipher(AESEngine.newInstance()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aes1Block, preErrata); byte[] aes128b = Hex.decode("aafd12f659cae63489b479e5076ddec2f06cb58faafd12f6"); - byte[] aesIn1b = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f"); + byte[] aesIn1b = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f"); byte[] aesOut1b = Hex.decode("6db2f802d99e1ef0a5940f306079e083cf87f4d8bb9d1abb36cdd9f44ead7d04"); testCTS(10, new CBCBlockCipher(AESEngine.newInstance()), new ParametersWithIV(new KeyParameter(aes128b), Hex.decode("aafd12f659cae63489b479e5076ddec2")), aesIn1b, aesOut1b); testExceptions(); + testOverlapping(); } public static void main( - String[] args) + String[] args) { runTest(new CTSTest()); } diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java index e13ffcafe9..feac0ce1cd 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java @@ -38,6 +38,12 @@ import javax.crypto.spec.RC5ParameterSpec; import javax.crypto.spec.SecretKeySpec; +import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.engines.DESEngine; +import org.bouncycastle.crypto.paddings.PKCS7Padding; +import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; @@ -1740,6 +1746,7 @@ public void performTest() testExceptions(); testIncorrectCipherModes(); doFinalTest(); + testOverlapping(); } private void doFinalTest() @@ -1764,6 +1771,41 @@ private void doFinalTest() } } + private void testOverlapping() + { + //Skip the dofinal of the test + BufferedBlockCipher bc = new BufferedBlockCipher(AESEngine.newInstance()); + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[16]; + random.nextBytes(keyBytes); + KeyParameter key = new KeyParameter(keyBytes); + + int offset = 2 + random.nextInt(bc.getBlockSize() - 1); + byte[] data = new byte[bc.getBlockSize() * 2 + offset]; + byte[] expected = new byte[bc.getOutputSize(bc.getBlockSize() * 2)]; + random.nextBytes(data); + + bc.init(true, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, expected, 0); + bc.init(true, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) + { + fail("failed to overlapping of encryption"); + } + + bc.init(false, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, expected, 0); + bc.init(false, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) + { + fail("failed to overlapping of encryption"); + } + } + public static void main( String[] args) { From 9ed602e4b1b98247c46fb7b2ec5f504495a1a099 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 28 Mar 2025 10:30:16 +1030 Subject: [PATCH 277/890] Update segmentsOverlap --- .../crypto/BufferedBlockCipher.java | 120 +++++++++--------- .../crypto/DefaultBufferedBlockCipher.java | 14 +- .../crypto/engines/AEADBaseEngine.java | 34 ++--- .../crypto/modes/CTSBlockCipher.java | 14 +- .../paddings/PaddedBufferedBlockCipher.java | 14 +- 5 files changed, 86 insertions(+), 110 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java index 93feecf704..5ca338cf99 100644 --- a/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java @@ -11,15 +11,15 @@ */ public class BufferedBlockCipher { - protected byte[] buf; - protected int bufOff; + protected byte[] buf; + protected int bufOff; - protected boolean forEncryption; - protected BlockCipher cipher; + protected boolean forEncryption; + protected BlockCipher cipher; protected MultiBlockCipher mbCipher; - protected boolean partialBlockOkay; - protected boolean pgpCFB; + protected boolean partialBlockOkay; + protected boolean pgpCFB; /** * constructor for subclasses @@ -35,7 +35,7 @@ public class BufferedBlockCipher * @deprecated use the constructor on DefaultBufferedBlockCipher. */ public BufferedBlockCipher( - BlockCipher cipher) + BlockCipher cipher) { this.cipher = cipher; @@ -55,8 +55,8 @@ public BufferedBlockCipher( // // check if we can handle partial blocks on doFinal. // - String name = cipher.getAlgorithmName(); - int idx = name.indexOf('/') + 1; + String name = cipher.getAlgorithmName(); + int idx = name.indexOf('/') + 1; pgpCFB = (idx > 0 && name.startsWith("PGP", idx)); @@ -84,14 +84,14 @@ public BlockCipher getUnderlyingCipher() * initialise the cipher. * * @param forEncryption if true the cipher is initialised for - * encryption, if false for decryption. - * @param params the key and other data required by the cipher. - * @exception IllegalArgumentException if the params argument is - * inappropriate. + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @throws IllegalArgumentException if the params argument is + * inappropriate. */ public void init( - boolean forEncryption, - CipherParameters params) + boolean forEncryption, + CipherParameters params) throws IllegalArgumentException { this.forEncryption = forEncryption; @@ -112,7 +112,7 @@ public int getBlockSize() } /** - * return the size of the output buffer required for an update + * return the size of the output buffer required for an update * an input of len bytes. * * @param len the length of the input. @@ -122,7 +122,7 @@ public int getBlockSize() public int getUpdateOutputSize( int len) { - int total = len + bufOff; + int total = len + bufOff; int leftOver; if (pgpCFB) @@ -138,7 +138,7 @@ public int getUpdateOutputSize( } else { - leftOver = total % buf.length; + leftOver = total % buf.length; } return total - leftOver; @@ -167,20 +167,20 @@ public int getOutputSize( /** * process a single byte, producing an output block if necessary. * - * @param in the input byte. - * @param out the space for any output that might be produced. + * @param in the input byte. + * @param out the space for any output that might be produced. * @param outOff the offset from which the output will be copied. * @return the number of output bytes copied to out. - * @exception DataLengthException if there isn't enough space in out. - * @exception IllegalStateException if the cipher isn't initialised. + * @throws DataLengthException if there isn't enough space in out. + * @throws IllegalStateException if the cipher isn't initialised. */ public int processByte( - byte in, - byte[] out, - int outOff) + byte in, + byte[] out, + int outOff) throws DataLengthException, IllegalStateException { - int resultLen = 0; + int resultLen = 0; buf[bufOff++] = in; @@ -196,21 +196,21 @@ public int processByte( /** * process an array of bytes, producing output if necessary. * - * @param in the input byte array. - * @param inOff the offset at which the input data starts. - * @param len the number of bytes to be copied out of the input array. - * @param out the space for any output that might be produced. + * @param in the input byte array. + * @param inOff the offset at which the input data starts. + * @param len the number of bytes to be copied out of the input array. + * @param out the space for any output that might be produced. * @param outOff the offset from which the output will be copied. * @return the number of output bytes copied to out. - * @exception DataLengthException if there isn't enough space in out. - * @exception IllegalStateException if the cipher isn't initialised. + * @throws DataLengthException if there isn't enough space in out. + * @throws IllegalStateException if the cipher isn't initialised. */ public int processBytes( - byte[] in, - int inOff, - int len, - byte[] out, - int outOff) + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) throws DataLengthException, IllegalStateException { if (len < 0) @@ -218,9 +218,9 @@ public int processBytes( throw new IllegalArgumentException("Can't have a negative input length!"); } - int blockSize = getBlockSize(); - int length = getUpdateOutputSize(len); - + int blockSize = getBlockSize(); + int length = getUpdateOutputSize(len); + if (length > 0) { if ((outOff + length) > out.length) @@ -237,17 +237,11 @@ public int processBytes( System.arraycopy(in, inOff, buf, bufOff, gapLen); inOff += gapLen; len -= gapLen; - if (in == out) + if (in == out && segmentsOverlap(inOff, len, outOff, length)) { - int inEnd = inOff + len; - int outEnd = outOff + length; - if ((inOff <= outOff && outOff <= inEnd) || - (outOff <= inOff && inOff <= outEnd)) - { - in = new byte[len]; - System.arraycopy(out, inOff, in, 0, len); - inOff = 0; - } + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; } resultLen += cipher.processBlock(buf, 0, out, outOff); @@ -296,20 +290,20 @@ public int processBytes( /** * Process the last block in the buffer. * - * @param out the array the block currently being held is copied into. + * @param out the array the block currently being held is copied into. * @param outOff the offset at which the copying starts. * @return the number of output bytes copied to out. - * @exception DataLengthException if there is insufficient space in out for - * the output, or the input is not block size aligned and should be. - * @exception IllegalStateException if the underlying cipher is not - * initialised. - * @exception InvalidCipherTextException if padding is expected and not found. - * @exception DataLengthException if the input is not block size - * aligned. + * @throws DataLengthException if there is insufficient space in out for + * the output, or the input is not block size aligned and should be. + * @throws IllegalStateException if the underlying cipher is not + * initialised. + * @throws InvalidCipherTextException if padding is expected and not found. + * @throws DataLengthException if the input is not block size + * aligned. */ public int doFinal( - byte[] out, - int outOff) + byte[] out, + int outOff) throws DataLengthException, IllegalStateException, InvalidCipherTextException { try @@ -363,4 +357,10 @@ public void reset() // cipher.reset(); } + + protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) + { + // please ensure a valid check for inLen > 0 and outLen > 0 outside this function + return inOff <= outOff + outLen && outOff <= inOff + inLen; + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java index 89b6f8c812..0721c3a872 100644 --- a/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java @@ -239,17 +239,11 @@ public int processBytes( System.arraycopy(in, inOff, buf, bufOff, gapLen); inOff += gapLen; len -= gapLen; - if (in == out) + if (in == out && segmentsOverlap(inOff, len, outOff, length)) { - int inEnd = inOff + len; - int outEnd = outOff + length; - if ((inOff <= outOff && outOff <= inEnd) || - (outOff <= inOff && inOff <= outEnd)) - { - in = new byte[len]; - System.arraycopy(out, inOff, in, 0, len); - inOff = 0; - } + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; } resultLen += cipher.processBlock(buf, 0, out, outOff); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java index 15519e1a0e..6cd536837b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java @@ -587,17 +587,11 @@ public int processByte(byte input, byte[] output, int outOff) @Override public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { - if (input == output) + if (input == output && segmentsOverlap(inOff, len, outOff, processor.getUpdateOutputSize(len))) { - int inEnd = inOff + len; - int outEnd = outOff + processor.getUpdateOutputSize(len); - if ((inOff <= outOff && outOff <= inEnd) || - (outOff <= inOff && inOff <= outEnd)) - { - input = new byte[len]; - System.arraycopy(output, inOff, input, 0, len); - inOff = 0; - } + input = new byte[len]; + System.arraycopy(output, inOff, input, 0, len); + inOff = 0; } boolean forEncryption = checkData(false); if (forEncryption) @@ -749,17 +743,11 @@ protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output resultLength = length + m_bufPos - (forEncryption ? 0 : MAC_SIZE); ensureSufficientOutputBuffer(output, outOff, resultLength - resultLength % BlockSize); resultLength = 0; - if (input == output) + if (input == output && segmentsOverlap(inOff, len, outOff, length)) { - int inEnd = inOff + len; - int outEnd = outOff + length; - if ((inOff <= outOff && outOff <= inEnd) || - (outOff <= inOff && inOff <= outEnd)) - { - input = new byte[len]; - System.arraycopy(output, inOff, input, 0, len); - inOff = 0; - } + input = new byte[len]; + System.arraycopy(output, inOff, input, 0, len); + inOff = 0; } if (forEncryption) { @@ -1039,6 +1027,12 @@ protected void finishAAD3(State nextState, boolean isDoFinal) m_state = nextState; } + private boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) + { + // please ensure a valid check for inLen > 0 and outLen > 0 outside this function + return inOff <= outOff + outLen && outOff <= inOff + inLen; + } + protected abstract void finishAAD(State nextState, boolean isDoFinal); protected abstract void init(byte[] key, byte[] iv); diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java index b2a441c982..fd29ece1ea 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java @@ -148,17 +148,11 @@ public int processBytes( System.arraycopy(in, inOff, buf, bufOff, gapLen); inOff += gapLen; len -= gapLen; - if (in == out) + if (in == out && segmentsOverlap(inOff, len, outOff, length)) { - int inEnd = inOff + len; - int outEnd = outOff + length; - if ((inOff <= outOff && outOff <= inEnd) || - (outOff <= inOff && inOff <= outEnd)) - { - in = new byte[len]; - System.arraycopy(out, inOff, in, 0, len); - inOff = 0; - } + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; } resultLen += cipher.processBlock(buf, 0, out, outOff); System.arraycopy(buf, blockSize, buf, 0, blockSize); diff --git a/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java index 03a41635b4..7b42234e26 100644 --- a/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java @@ -204,17 +204,11 @@ public int processBytes( System.arraycopy(in, inOff, buf, bufOff, gapLen); inOff += gapLen; len -= gapLen; - if (in == out) + if (in == out && segmentsOverlap(inOff, len, outOff, length)) { - int inEnd = inOff + len; - int outEnd = outOff + length; - if ((inOff <= outOff && outOff <= inEnd) || - (outOff <= inOff && inOff <= outEnd)) - { - in = new byte[len]; - System.arraycopy(out, inOff, in, 0, len); - inOff = 0; - } + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; } resultLen += cipher.processBlock(buf, 0, out, outOff); From 4a369f08c2b8109b41f4c42b6cb86db51b0b5cf1 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 28 Mar 2025 11:19:42 +1030 Subject: [PATCH 278/890] Fix KXTSBlockCipher --- .../crypto/modes/KXTSBlockCipher.java | 7 +++- .../crypto/test/DSTU7624Test.java | 38 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java index 2062fea183..083ef1cd6e 100755 --- a/core/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java @@ -123,7 +123,12 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output, int out { throw new IllegalArgumentException("Partial blocks not supported"); } - + if (input == output && segmentsOverlap(inOff, len, outOff, len)) + { + input = new byte[len]; + System.arraycopy(output, inOff, input, 0, len); + inOff = 0; + } for (int pos = 0; pos < len; pos += blockSize) { processBlocks(input, inOff + pos, output, outOff + pos); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/DSTU7624Test.java b/core/src/test/java/org/bouncycastle/crypto/test/DSTU7624Test.java index 2af7f7978c..fd3a0d531e 100755 --- a/core/src/test/java/org/bouncycastle/crypto/test/DSTU7624Test.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/DSTU7624Test.java @@ -96,6 +96,7 @@ public void performTest() CCMModeTests(); XTSModeTests(); GCMModeTests(); + testOverlapping(); } public static void main( @@ -1464,4 +1465,41 @@ private void doFinalTest(AEADBlockCipher cipher, byte[] key, byte[] iv, byte[] a fail("Failed doFinal test - after: " + cipher.getAlgorithmName()); } } + + private void testOverlapping() + { + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[16]; + byte[] iv = new byte[16]; + random.nextBytes(keyBytes); + KXTSBlockCipher bc = new KXTSBlockCipher(new DSTU7624Engine(128)); + ParametersWithIV param = new ParametersWithIV(new KeyParameter(keyBytes), iv); + + int offset = 1 + random.nextInt(bc.getBlockSize() - 1) + bc.getBlockSize(); + byte[] data = new byte[bc.getBlockSize() * 4 + offset]; + byte[] expected = new byte[bc.getOutputSize(bc.getBlockSize() * 3)]; + random.nextBytes(data); + + bc.init(true, param); + int len = bc.processBytes(data, 0, expected.length, expected, 0); + bc.doFinal(expected, len); + bc.init(true, param); + len = bc.processBytes(data, 0, expected.length, data, offset); + bc.doFinal(data, offset + len); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + + bc.init(false, param); + bc.processBytes(data, 0, expected.length, expected, 0); + bc.init(false, param); + bc.processBytes(data, 0, expected.length, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + } } From 07bb6a3bad9007203913ac22cf606baac272fa11 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 28 Mar 2025 11:25:12 +1030 Subject: [PATCH 279/890] Fix NISTCTSBlockCipher and OldCTSBlockCipher --- .../crypto/modes/NISTCTSBlockCipher.java | 12 +- .../crypto/modes/OldCTSBlockCipher.java | 12 +- .../org/bouncycastle/crypto/test/CTSTest.java | 39 +++++++ .../bouncycastle/crypto/test/NISTCTSTest.java | 103 +++++++++++++----- 4 files changed, 131 insertions(+), 35 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java index e13c6ea9a5..f159d277fe 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java @@ -155,15 +155,19 @@ public int processBytes( if (len > gapLen) { System.arraycopy(in, inOff, buf, bufOff, gapLen); - + inOff += gapLen; + len -= gapLen; + if (in == out && segmentsOverlap(inOff, len, outOff, length)) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } resultLen += cipher.processBlock(buf, 0, out, outOff); System.arraycopy(buf, blockSize, buf, 0, blockSize); bufOff = blockSize; - len -= gapLen; - inOff += gapLen; - while (len > blockSize) { System.arraycopy(in, inOff, buf, bufOff, blockSize); diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java index 13ba147753..c1fcfdb839 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java @@ -149,15 +149,19 @@ public int processBytes( if (len > gapLen) { System.arraycopy(in, inOff, buf, bufOff, gapLen); - + inOff += gapLen; + len -= gapLen; + if (in == out && segmentsOverlap(inOff, len, outOff, length)) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } resultLen += cipher.processBlock(buf, 0, out, outOff); System.arraycopy(buf, blockSize, buf, 0, blockSize); bufOff = blockSize; - len -= gapLen; - inOff += gapLen; - while (len > blockSize) { System.arraycopy(in, inOff, buf, bufOff, blockSize); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java index da0ed221db..37d38330e7 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java @@ -204,6 +204,44 @@ private void testOverlapping() } } + private void testOverlapping2() + throws Exception + { + //Skip the dofinal of the test + OldCTSBlockCipher bc = new OldCTSBlockCipher(AESEngine.newInstance()); + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[16]; + random.nextBytes(keyBytes); + KeyParameter key = new KeyParameter(keyBytes); + + int offset = 1 + random.nextInt(bc.getBlockSize() - 1) + bc.getBlockSize(); + byte[] data = new byte[bc.getBlockSize() * 4 + offset]; + byte[] expected = new byte[bc.getOutputSize(bc.getBlockSize() * 3)]; + random.nextBytes(data); + + bc.init(true, key); + int len = bc.processBytes(data, 0, expected.length, expected, 0); + bc.doFinal(expected, len); + bc.init(true, key); + len = bc.processBytes(data, 0, expected.length, data, offset); + bc.doFinal(data, offset + len); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + + bc.init(false, key); + bc.processBytes(data, 0, expected.length, expected, 0); + bc.init(false, key); + bc.processBytes(data, 0, expected.length, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + } + public String getName() { return "CTS"; @@ -256,6 +294,7 @@ public void performTest() testExceptions(); testOverlapping(); + testOverlapping2(); } public static void main( diff --git a/core/src/test/java/org/bouncycastle/crypto/test/NISTCTSTest.java b/core/src/test/java/org/bouncycastle/crypto/test/NISTCTSTest.java index b1febcd4ca..77f4e64bd2 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/NISTCTSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/NISTCTSTest.java @@ -1,14 +1,19 @@ package org.bouncycastle.crypto.test; +import java.security.SecureRandom; + import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.engines.DSTU7624Engine; +import org.bouncycastle.crypto.modes.KXTSBlockCipher; import org.bouncycastle.crypto.modes.NISTCTSBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -36,23 +41,23 @@ public class NISTCTSTest private static byte[] cs2NotQuiteTwoBlockOut = Hex.decode("f098097ca69b72e3a46e9ca21bb5ebbc22ecf2ac77"); private static byte[] cs3NotQuiteTwoBlockOut = Hex.decode("f098097ca69b72e3a46e9ca21bb5ebbc22ecf2ac77"); - static byte[] in1 = Hex.decode("4e6f7720697320746865207420"); - static byte[] in2 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f0aaa"); - static byte[] out1 = Hex.decode("9952f131588465033fa40e8a98"); - static byte[] out2 = Hex.decode("358f84d01eb42988dc34efb994"); - static byte[] out3 = Hex.decode("170171cfad3f04530c509b0c1f0be0aefbd45a8e3755a873bff5ea198504b71683c6"); - + static byte[] in1 = Hex.decode("4e6f7720697320746865207420"); + static byte[] in2 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f0aaa"); + static byte[] out1 = Hex.decode("9952f131588465033fa40e8a98"); + static byte[] out2 = Hex.decode("358f84d01eb42988dc34efb994"); + static byte[] out3 = Hex.decode("170171cfad3f04530c509b0c1f0be0aefbd45a8e3755a873bff5ea198504b71683c6"); + private void testCTS( - int id, - int type, - BlockCipher cipher, - CipherParameters params, - byte[] input, - byte[] output) + int id, + int type, + BlockCipher cipher, + CipherParameters params, + byte[] input, + byte[] output) throws Exception { - byte[] out = new byte[input.length]; - BufferedBlockCipher engine = new NISTCTSBlockCipher(type, cipher); + byte[] out = new byte[input.length]; + BufferedBlockCipher engine = new NISTCTSBlockCipher(type, cipher); engine.init(true, params); @@ -77,63 +82,106 @@ private void testCTS( } } - private void testExceptions() throws InvalidCipherTextException + private void testExceptions() + throws InvalidCipherTextException { BufferedBlockCipher engine = new NISTCTSBlockCipher(NISTCTSBlockCipher.CS1, AESEngine.newInstance()); CipherParameters params = new KeyParameter(new byte[engine.getBlockSize()]); engine.init(true, params); byte[] out = new byte[engine.getOutputSize(engine.getBlockSize())]; - + engine.processBytes(new byte[engine.getBlockSize() - 1], 0, engine.getBlockSize() - 1, out, 0); - try + try { engine.doFinal(out, 0); fail("Expected CTS encrypt error on < 1 block input"); - } catch(DataLengthException e) + } + catch (DataLengthException e) { // Expected } engine.init(true, params); engine.processBytes(new byte[engine.getBlockSize()], 0, engine.getBlockSize(), out, 0); - try + try { engine.doFinal(out, 0); - } catch(DataLengthException e) + } + catch (DataLengthException e) { fail("Unexpected CTS encrypt error on == 1 block input"); } engine.init(false, params); engine.processBytes(new byte[engine.getBlockSize() - 1], 0, engine.getBlockSize() - 1, out, 0); - try + try { engine.doFinal(out, 0); fail("Expected CTS decrypt error on < 1 block input"); - } catch(DataLengthException e) + } + catch (DataLengthException e) { // Expected } engine.init(false, params); engine.processBytes(new byte[engine.getBlockSize()], 0, engine.getBlockSize(), out, 0); - try + try { engine.doFinal(out, 0); - } catch(DataLengthException e) + } + catch (DataLengthException e) { fail("Unexpected CTS decrypt error on == 1 block input"); } } + private void testOverlapping() + throws Exception + { + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[16]; + byte[] iv = new byte[16]; + random.nextBytes(keyBytes); + BufferedBlockCipher bc = new NISTCTSBlockCipher(NISTCTSBlockCipher.CS1, AESEngine.newInstance()); + ParametersWithIV param = new ParametersWithIV(new KeyParameter(keyBytes), iv); + + int offset = 1 + random.nextInt(bc.getBlockSize() - 1) + bc.getBlockSize(); + byte[] data = new byte[bc.getBlockSize() * 4 + offset]; + byte[] expected = new byte[bc.getOutputSize(bc.getBlockSize() * 3)]; + random.nextBytes(data); + + bc.init(true, param); + int len = bc.processBytes(data, 0, expected.length, expected, 0); + bc.doFinal(expected, len); + bc.init(true, param); + len = bc.processBytes(data, 0, expected.length, data, offset); + bc.doFinal(data, offset + len); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + + bc.init(false, param); + bc.processBytes(data, 0, expected.length, expected, 0); + bc.init(false, param); + bc.processBytes(data, 0, expected.length, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + } + public String getName() { return "NISTCTS"; } - public void performTest() + public void performTest() throws Exception { testCTS(1, NISTCTSBlockCipher.CS1, AESEngine.newInstance(), new ParametersWithIV(key, iv), singleBlock, singleOut); @@ -149,7 +197,7 @@ public void performTest() testCTS(9, NISTCTSBlockCipher.CS3, AESEngine.newInstance(), new ParametersWithIV(key, iv), notQuiteTwo, cs3NotQuiteTwoBlockOut); byte[] aes128b = Hex.decode("aafd12f659cae63489b479e5076ddec2f06cb58faafd12f6"); - byte[] aesIn1b = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f"); + byte[] aesIn1b = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f"); byte[] aesOut1b = Hex.decode("6db2f802d99e1ef0a5940f306079e083cf87f4d8bb9d1abb36cdd9f44ead7d04"); testCTS(10, NISTCTSBlockCipher.CS3, AESEngine.newInstance(), new ParametersWithIV(new KeyParameter(aes128b), Hex.decode("aafd12f659cae63489b479e5076ddec2")), aesIn1b, aesOut1b); @@ -160,10 +208,11 @@ public void performTest() testCTS(11, NISTCTSBlockCipher.CS3, AESEngine.newInstance(), new ParametersWithIV(new KeyParameter(aes128c), Hex.decode("aafd12f659cae63489b479e5076ddec2")), aesIn1b, aesOut1c); testExceptions(); + testOverlapping(); } public static void main( - String[] args) + String[] args) { runTest(new NISTCTSTest()); } From b524d1ef8e519eb8d9afe6ee80a1bdfe116a956c Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 28 Mar 2025 13:49:54 +1030 Subject: [PATCH 280/890] Remove GF16Utils.add --- .../pqc/crypto/snova/GF16Utils.java | 5 -- .../pqc/crypto/snova/SnovaEngine.java | 78 +++++++------------ .../pqc/crypto/snova/SnovaSigner.java | 31 ++++---- .../pqc/crypto/test/TestUtils.java | 49 ++++-------- 4 files changed, 60 insertions(+), 103 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java index 5c7db6a54a..56d10a609d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java @@ -207,11 +207,6 @@ public static byte mul(byte a, byte b) return MT4B[(a & 0xF) << 4 | (b & 0xF)]; } - public static byte add(byte a, byte b) - { - return (byte)((a ^ b) & 0xF); - } - public static byte inv(byte a) { return INV4B[a & 0xF]; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 731d2bef6f..acf96f28ec 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -186,37 +186,25 @@ private byte determinant3x3(byte[] m, int off, int i0, int i1, int i2) private byte determinant4x4(byte[] m, int off) { - byte d0 = gf16Mul(getGF16m(m, 0, off), gf16Add( - gf16Add( - pod(m, off, 1, 2, 3, 3, 2), - pod(m, off, 2, 1, 3, 3, 1) - ), - pod(m, off, 3, 1, 2, 2, 1) - )); - - byte d1 = gf16Mul(getGF16m(m, 0, off + 1), gf16Add( - gf16Add( - pod(m, off, 0, 2, 3, 3, 2), - pod(m, off, 2, 0, 3, 3, 0) - ), - pod(m, off, 3, 0, 2, 2, 0) - )); - - byte d2 = gf16Mul(getGF16m(m, 0, off + 2), gf16Add( - gf16Add( - pod(m, off, 0, 1, 3, 3, 1), - pod(m, off, 1, 0, 3, 3, 0) - ), - pod(m, off, 3, 0, 1, 1, 0) - )); - - byte d3 = gf16Mul(getGF16m(m, 0, off + 3), gf16Add( - gf16Add( - pod(m, off, 0, 1, 2, 2, 1), - pod(m, off, 1, 0, 2, 2, 0) - ), - pod(m, off, 2, 0, 1, 1, 0) - )); + byte d0 = gf16Mul(getGF16m(m, 0, off), (byte)( + pod(m, off, 1, 2, 3, 3, 2) ^ + pod(m, off, 2, 1, 3, 3, 1) ^ + pod(m, off, 3, 1, 2, 2, 1))); + + byte d1 = gf16Mul(getGF16m(m, 0, off + 1), (byte)( + pod(m, off, 0, 2, 3, 3, 2) ^ + pod(m, off, 2, 0, 3, 3, 0) ^ + pod(m, off, 3, 0, 2, 2, 0))); + + byte d2 = gf16Mul(getGF16m(m, 0, off + 2), (byte)( + pod(m, off, 0, 1, 3, 3, 1) ^ + pod(m, off, 1, 0, 3, 3, 0) ^ + pod(m, off, 3, 0, 1, 1, 0))); + + byte d3 = gf16Mul(getGF16m(m, 0, off + 3), (byte)( + pod(m, off, 0, 1, 2, 2, 1) ^ + pod(m, off, 1, 0, 2, 2, 0) ^ + pod(m, off, 2, 0, 1, 1, 0))); return (byte)(d0 ^ d1 ^ d2 ^ d3); } @@ -224,25 +212,25 @@ private byte determinant4x4(byte[] m, int off) private byte determinant5x5(byte[] m, int off) { byte result = gf16Mul(determinant3x3(m, off, 0, 1, 2), - gf16Add(gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off + 4)), gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 3)))); + (byte)(gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off + 4)) ^ gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 3)))); result ^= gf16Mul(determinant3x3(m, off, 0, 1, 3), - gf16Add(gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off + 4)), gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 2)))); + (byte)(gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off + 4)) ^ gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 2)))); result ^= gf16Mul(determinant3x3(m, off, 0, 1, 4), - gf16Add(gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off + 3)), gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off + 2)))); + (byte)(gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off + 3)) ^ gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off + 2)))); result ^= gf16Mul(determinant3x3(m, off, 0, 2, 3), - gf16Add(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 4)), gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 1)))); + (byte)(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 4)) ^ gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 1)))); result ^= gf16Mul(determinant3x3(m, off, 0, 2, 4), - gf16Add(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 3)), gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off + 1)))); + (byte)(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 3)) ^ gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off + 1)))); result ^= gf16Mul(determinant3x3(m, off, 0, 3, 4), - gf16Add(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 2)), gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off + 1)))); + (byte)(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 2)) ^ gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off + 1)))); result ^= gf16Mul(determinant3x3(m, off, 1, 2, 3), - gf16Add(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 4)), gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off)))); + (byte)(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 4)) ^ gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off)))); result ^= gf16Mul(determinant3x3(m, off, 1, 2, 4), - gf16Add(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 3)), gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off)))); + (byte)(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 3)) ^ gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off)))); result ^= gf16Mul(determinant3x3(m, off, 1, 3, 4), - gf16Add(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 2)), gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off)))); + (byte)(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 2)) ^ gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off)))); result ^= gf16Mul(determinant3x3(m, off, 2, 3, 4), - gf16Add(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 1)), gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off)))); + (byte)(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 1)) ^ gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off)))); return result; } @@ -274,17 +262,11 @@ private void addMatrices(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int c { for (int j = 0; j < l; j++) { - setGF16m(c, i, cOff + j, gf16Add(getGF16m(a, i, aOff + j), getGF16m(b, i, bOff + j))); + setGF16m(c, i, cOff + j, (byte)(getGF16m(a, i, aOff + j) ^ getGF16m(b, i, bOff + j))); } } } - // GF(16) arithmetic - private static byte gf16Add(byte a, byte b) - { - return (byte)(a ^ b); - } - // GF(16) multiplication using lookup table private static byte gf16Mul(byte a, byte b) { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index fe840ec709..002ae32553 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -300,7 +300,7 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, int colRight = ti % l; byte valRight = rightXtmp[rowRight][colRight]; byte product = GF16Utils.mul(valA, valRight); - Temp[ti][tj] = GF16Utils.add(Temp[ti][tj], product); + Temp[ti][tj] ^= product; } } } @@ -311,7 +311,7 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, { int gaussRow = mi * lsq + ti; int gaussCol = index * lsq + tj; - Gauss[gaussRow][gaussCol] = GF16Utils.add(Gauss[gaussRow][gaussCol], Temp[ti][tj]); + Gauss[gaussRow][gaussCol] ^= Temp[ti][tj]; } } } @@ -548,10 +548,10 @@ private void multiplyGF16Matrices(byte[][] a, byte[][] b, byte[][] result) byte sum = 0; for (int k = 0; k < params.getL(); k++) { - sum = GF16Utils.add(sum, GF16Utils.mul( + sum ^= GF16Utils.mul( a[i][k], b[k][j] - )); + ); } result[i][j] = sum; } @@ -568,10 +568,9 @@ private void multiplyGF16Matrices(byte[][] a, byte[] b, byte[][] result) byte sum = 0; for (int k = 0; k < params.getL(); k++) { - sum = GF16Utils.add(sum, GF16Utils.mul( + sum ^= GF16Utils.mul( a[i][k], - engine.getGF16m(b, k, j) - )); + engine.getGF16m(b, k, j)); } result[i][j] = sum; } @@ -588,10 +587,9 @@ private void multiplyGF16Matrices(byte[] a, byte[][] b, byte[][] result) byte sum = 0; for (int k = 0; k < params.getL(); k++) { - sum = GF16Utils.add(sum, GF16Utils.mul( + sum ^= GF16Utils.mul( engine.getGF16m(a, i, k), - b[k][j] - )); + b[k][j]); } result[i][j] = sum; } @@ -608,10 +606,9 @@ private void multiplyGF16Matrices(byte[] a, byte[] b, byte[][] result) byte sum = 0; for (int k = 0; k < params.getL(); k++) { - sum = GF16Utils.add(sum, GF16Utils.mul( + sum ^= GF16Utils.mul( engine.getGF16m(a, i, k), - engine.getGF16m(b, k, j) - )); + engine.getGF16m(b, k, j)); } result[i][j] = sum; } @@ -660,7 +657,7 @@ private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size { for (int k = i; k < cols; k++) { - Gauss[j][k] = GF16Utils.add(Gauss[j][k], GF16Utils.mul(Gauss[i][k], factor)); + Gauss[j][k] ^= GF16Utils.mul(Gauss[i][k], factor); } } } @@ -672,7 +669,7 @@ private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size solution[i] = Gauss[i][size]; for (int j = i + 1; j < size; j++) { - solution[i] = GF16Utils.add(solution[i], GF16Utils.mul(Gauss[i][j], solution[j])); + solution[i] ^= GF16Utils.mul(Gauss[i][j], solution[j]); } } @@ -685,7 +682,7 @@ private void addGF16Matrices(byte[] a, byte[][] b, byte[] result) { for (int j = 0; j < b[i].length; ++j) { - engine.setGF16m(result, i, j, GF16Utils.add(engine.getGF16m(a, i, j), b[i][j])); + engine.setGF16m(result, i, j, (byte)(engine.getGF16m(a, i, j) ^ b[i][j])); } } } @@ -696,7 +693,7 @@ private void addGF16Matrices(byte[][] a, byte[][] b, byte[][] result) { for (int j = 0; j < b[i].length; ++j) { - result[i][j] = GF16Utils.add(a[i][j], b[i][j]); + result[i][j] = (byte)(a[i][j] ^ b[i][j]); } } } diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java index 3fbacf3049..51458c078d 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java @@ -53,7 +53,7 @@ public static void testTestVector(boolean enableFactory, boolean isSigner, Strin String name = files[fileIndex]; InputStream src = TestResourceFinder.findTestResource(homeDir, name); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); - + //System.out.println(files[fileIndex]); String line; HashMap buf = new HashMap(); while ((line = bin.readLine()) != null) @@ -101,24 +101,7 @@ public static void testTestVector(boolean enableFactory, boolean isSigner, Strin pubParams = kp.getPublic(); privParams = kp.getPrivate(); } -// byte[] pk2 = operation.getPublicKeyEncoded(pubParams); -// for (int i = 0; i < pk2.length; ++i) -// { -// if (pk[i] != pk2[i]) -// { -// System.out.println(i + " " + pk[i] + " " + pk2[i]); -// } -// } -// -// byte[] sk2 = operation.getPrivateKeyEncoded(privParams); -// System.out.println(new String(Hex.encode(sk2))); -// for (int i = 0; i < sk2.length; ++i) -// { -// if (sk[i] != sk2[i]) -// { -// System.out.println(i + " " + sk[i] + " " + sk2[i]); -// } -// } + Assert.assertTrue(name + ": public key", Arrays.areEqual(pk, operation.getPublicKeyEncoded(pubParams))); Assert.assertTrue(name + ": secret key", Arrays.areEqual(sk, operation.getPrivateKeyEncoded(privParams))); @@ -140,20 +123,20 @@ public static void testTestVector(boolean enableFactory, boolean isSigner, Strin Assert.assertTrue(Arrays.areEqual(sigGenerated, signature)); -// if (isSigner) -// { -// Signer signer = operation.getSigner(); -// signer.init(false, pubParams); -// signer.update(message, 0, message.length); -// Assert.assertTrue(signer.verifySignature(sigGenerated)); -// } -// else -// { -// MessageSigner signer = operation.getMessageSigner(); -// signer.init(false, pubParams); -// Assert.assertTrue(signer.verifySignature(message, sigGenerated)); -// } -// System.out.println("Count " + count + " pass"); + if (isSigner) + { + Signer signer = operation.getSigner(); + signer.init(false, pubParams); + signer.update(message, 0, message.length); + Assert.assertTrue(signer.verifySignature(sigGenerated)); + } + else + { + MessageSigner signer = operation.getMessageSigner(); + signer.init(false, pubParams); + Assert.assertTrue(signer.verifySignature(message, sigGenerated)); + } + System.out.println("Count " + count + " pass"); } buf.clear(); continue; From 8cbbf6102beda9dff77f99673248077d1becf739 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 28 Mar 2025 16:43:19 +1030 Subject: [PATCH 281/890] Refactor of determinant4x4 --- .../pqc/crypto/snova/SnovaEngine.java | 141 ++++++++++-------- 1 file changed, 76 insertions(+), 65 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index acf96f28ec..8862777d49 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -166,71 +166,93 @@ private byte gf16Determinant(byte[] matrix, int off) private byte determinant2x2(byte[] m, int off) { return (byte) - (gf16Mul(getGF16m(m, 0, off), getGF16m(m, 1, off + 1)) ^ - gf16Mul(getGF16m(m, 0, off + 1), getGF16m(m, 1, off))); + (GF16Utils.mul(getGF16m(m, 0, off), getGF16m(m, 1, off + 1)) ^ + GF16Utils.mul(getGF16m(m, 0, off + 1), getGF16m(m, 1, off))); } private byte determinant3x3(byte[] m, int off, int i0, int i1, int i2) { return (byte)( - gf16Mul(getGF16m(m, 0, off + i0), (byte)( - gf16Mul(getGF16m(m, 1, off + i1), getGF16m(m, 2, off + i2)) ^ - gf16Mul(getGF16m(m, 1, off + i2), getGF16m(m, 2, off + i1)))) ^ - gf16Mul(getGF16m(m, 0, off + i1), (byte)( - gf16Mul(getGF16m(m, 1, off + i0), getGF16m(m, 2, off + i2)) ^ - gf16Mul(getGF16m(m, 1, off + i2), getGF16m(m, 2, off + i0)))) ^ - gf16Mul(getGF16m(m, 0, off + i2), (byte)( - gf16Mul(getGF16m(m, 1, off + i0), getGF16m(m, 2, off + i1)) ^ - gf16Mul(getGF16m(m, 1, off + i1), getGF16m(m, 2, off + i0))))); + GF16Utils.mul(getGF16m(m, 0, off + i0), (byte)( + GF16Utils.mul(getGF16m(m, 1, off + i1), getGF16m(m, 2, off + i2)) ^ + GF16Utils.mul(getGF16m(m, 1, off + i2), getGF16m(m, 2, off + i1)))) ^ + GF16Utils.mul(getGF16m(m, 0, off + i1), (byte)( + GF16Utils.mul(getGF16m(m, 1, off + i0), getGF16m(m, 2, off + i2)) ^ + GF16Utils.mul(getGF16m(m, 1, off + i2), getGF16m(m, 2, off + i0)))) ^ + GF16Utils.mul(getGF16m(m, 0, off + i2), (byte)( + GF16Utils.mul(getGF16m(m, 1, off + i0), getGF16m(m, 2, off + i1)) ^ + GF16Utils.mul(getGF16m(m, 1, off + i1), getGF16m(m, 2, off + i0))))); } private byte determinant4x4(byte[] m, int off) { - byte d0 = gf16Mul(getGF16m(m, 0, off), (byte)( - pod(m, off, 1, 2, 3, 3, 2) ^ - pod(m, off, 2, 1, 3, 3, 1) ^ - pod(m, off, 3, 1, 2, 2, 1))); - - byte d1 = gf16Mul(getGF16m(m, 0, off + 1), (byte)( - pod(m, off, 0, 2, 3, 3, 2) ^ - pod(m, off, 2, 0, 3, 3, 0) ^ - pod(m, off, 3, 0, 2, 2, 0))); - - byte d2 = gf16Mul(getGF16m(m, 0, off + 2), (byte)( - pod(m, off, 0, 1, 3, 3, 1) ^ - pod(m, off, 1, 0, 3, 3, 0) ^ - pod(m, off, 3, 0, 1, 1, 0))); - - byte d3 = gf16Mul(getGF16m(m, 0, off + 3), (byte)( - pod(m, off, 0, 1, 2, 2, 1) ^ - pod(m, off, 1, 0, 2, 2, 0) ^ - pod(m, off, 2, 0, 1, 1, 0))); - - return (byte)(d0 ^ d1 ^ d2 ^ d3); + byte m00 = m[off++]; + byte m01 = m[off++]; + byte m02 = m[off++]; + byte m03 = m[off++]; + byte m10 = m[off++]; + byte m11 = m[off++]; + byte m12 = m[off++]; + byte m13 = m[off++]; + byte m20 = m[off++]; + byte m21 = m[off++]; + byte m22 = m[off++]; + byte m23 = m[off++]; + byte m30 = m[off++]; + byte m31 = m[off++]; + byte m32 = m[off++]; + byte m33 = m[off]; + + byte m22xm33_m23xm32 = (byte)(GF16Utils.mul(m22, m33) ^ GF16Utils.mul(m23, m32)); + byte m21xm33_m23xm31 = (byte)(GF16Utils.mul(m21, m33) ^ GF16Utils.mul(m23, m31)); + byte m21xm32_m22xm31 = (byte)(GF16Utils.mul(m21, m32) ^ GF16Utils.mul(m22, m31)); + byte m20xm33_m23xm30 = (byte)(GF16Utils.mul(m20, m33) ^ GF16Utils.mul(m23, m30)); + byte m20xm32_m32xm30 = (byte)(GF16Utils.mul(m20, m32) ^ GF16Utils.mul(m22, m30)); + byte m20xm31_m21xm30 = (byte)(GF16Utils.mul(m20, m31) ^ GF16Utils.mul(m21, m30)); + // POD -> entry[a][b] * (entry[c][d] * entry[e][f] + entry[g][h] * entry[i][j]) + return (byte)(GF16Utils.mul(m00, (byte)(GF16Utils.mul(m11, m22xm33_m23xm32) ^ + GF16Utils.mul(m12, m21xm33_m23xm31) ^ GF16Utils.mul(m13, m21xm32_m22xm31))) ^ + GF16Utils.mul(m01, (byte)(GF16Utils.mul(m10, m22xm33_m23xm32) ^ + GF16Utils.mul(m12, m20xm33_m23xm30) ^ GF16Utils.mul(m13, m20xm32_m32xm30))) ^ + GF16Utils.mul(m02, (byte)(GF16Utils.mul(m10, m21xm33_m23xm31) ^ + GF16Utils.mul(m11, m20xm33_m23xm30) ^ GF16Utils.mul(m13, m20xm31_m21xm30))) ^ + GF16Utils.mul(m03, (byte)(GF16Utils.mul(m10, m21xm32_m22xm31) ^ + GF16Utils.mul(m11, m20xm32_m32xm30) ^ GF16Utils.mul(m12, m20xm31_m21xm30)))); } private byte determinant5x5(byte[] m, int off) { - byte result = gf16Mul(determinant3x3(m, off, 0, 1, 2), - (byte)(gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off + 4)) ^ gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 3)))); - result ^= gf16Mul(determinant3x3(m, off, 0, 1, 3), - (byte)(gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off + 4)) ^ gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 2)))); - result ^= gf16Mul(determinant3x3(m, off, 0, 1, 4), - (byte)(gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off + 3)) ^ gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off + 2)))); - result ^= gf16Mul(determinant3x3(m, off, 0, 2, 3), - (byte)(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 4)) ^ gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off + 1)))); - result ^= gf16Mul(determinant3x3(m, off, 0, 2, 4), - (byte)(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 3)) ^ gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off + 1)))); - result ^= gf16Mul(determinant3x3(m, off, 0, 3, 4), - (byte)(gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off + 2)) ^ gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off + 1)))); - result ^= gf16Mul(determinant3x3(m, off, 1, 2, 3), - (byte)(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 4)) ^ gf16Mul(getGF16m(m, 3, off + 4), getGF16m(m, 4, off)))); - result ^= gf16Mul(determinant3x3(m, off, 1, 2, 4), - (byte)(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 3)) ^ gf16Mul(getGF16m(m, 3, off + 3), getGF16m(m, 4, off)))); - result ^= gf16Mul(determinant3x3(m, off, 1, 3, 4), - (byte)(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 2)) ^ gf16Mul(getGF16m(m, 3, off + 2), getGF16m(m, 4, off)))); - result ^= gf16Mul(determinant3x3(m, off, 2, 3, 4), - (byte)(gf16Mul(getGF16m(m, 3, off), getGF16m(m, 4, off + 1)) ^ gf16Mul(getGF16m(m, 3, off + 1), getGF16m(m, 4, off)))); + byte m30 = getGF16m(m, 3, off); + byte m31 = getGF16m(m, 3, off + 1); + byte m32 = getGF16m(m, 3, off + 2); + byte m33 = getGF16m(m, 3, off + 3); + byte m34 = getGF16m(m, 3, off + 4); + + byte m40 = getGF16m(m, 4, off); + byte m41 = getGF16m(m, 4, off + 1); + byte m42 = getGF16m(m, 4, off + 2); + byte m43 = getGF16m(m, 4, off + 3); + byte m44 = getGF16m(m, 4, off + 4); + byte result = GF16Utils.mul(determinant3x3(m, off, 0, 1, 2), + (byte)(GF16Utils.mul(m33, m44) ^ GF16Utils.mul(m34, m43))); + result ^= GF16Utils.mul(determinant3x3(m, off, 0, 1, 3), + (byte)(GF16Utils.mul(m32, m44) ^ GF16Utils.mul(m34, m42))); + result ^= GF16Utils.mul(determinant3x3(m, off, 0, 1, 4), + (byte)(GF16Utils.mul(m32, m43) ^ GF16Utils.mul(m33, m42))); + result ^= GF16Utils.mul(determinant3x3(m, off, 0, 2, 3), + (byte)(GF16Utils.mul(m31, m44) ^ GF16Utils.mul(m34, m41))); + result ^= GF16Utils.mul(determinant3x3(m, off, 0, 2, 4), + (byte)(GF16Utils.mul(m31, m43) ^ GF16Utils.mul(m33, m41))); + result ^= GF16Utils.mul(determinant3x3(m, off, 0, 3, 4), + (byte)(GF16Utils.mul(m31, m42) ^ GF16Utils.mul(m32, m41))); + result ^= GF16Utils.mul(determinant3x3(m, off, 1, 2, 3), + (byte)(GF16Utils.mul(m30, m44) ^ GF16Utils.mul(m34, m40))); + result ^= GF16Utils.mul(determinant3x3(m, off, 1, 2, 4), + (byte)(GF16Utils.mul(m30, m43) ^ GF16Utils.mul(m33, m40))); + result ^= GF16Utils.mul(determinant3x3(m, off, 1, 3, 4), + (byte)(GF16Utils.mul(m30, m42) ^ GF16Utils.mul(m32, m40))); + result ^= GF16Utils.mul(determinant3x3(m, off, 2, 3, 4), + (byte)(GF16Utils.mul(m30, m41) ^ GF16Utils.mul(m31, m40))); return result; } @@ -245,16 +267,11 @@ private void generateASMatrix(byte[] target, byte a) { coefficient = 9; } - setGF16m(target, i, j, gf16Mul(coefficient, a)); + setGF16m(target, i, j, GF16Utils.mul(coefficient, a)); } } } - // POD -> entry[a][b] * (entry[c][d] * entry[e][f] + entry[g][h] * entry[i][j]) - private byte pod(byte[] m, int off, int b, int d, int f, int h, int j) - { - return gf16Mul(getGF16m(m, 1, off + b), (byte)(gf16Mul(getGF16m(m, 2, off + d), getGF16m(m, 3, off + f)) ^ gf16Mul(getGF16m(m, 2, off + h), getGF16m(m, 3, off + j)))); - } private void addMatrices(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int cOff) { @@ -267,12 +284,6 @@ private void addMatrices(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int c } } - // GF(16) multiplication using lookup table - private static byte gf16Mul(byte a, byte b) - { - return GF16Utils.mul(a, b); - } - public void genAFqS(byte[] c, int cOff, byte[] ptMatrix, int off) { byte[] temp = new byte[l * l]; @@ -302,7 +313,7 @@ private void gf16mScale(byte[] a, byte k, byte[] result) { for (int j = 0; j < l; ++j) { - setGF16m(result, i, j, gf16Mul(getGF16m(a, i, j), k)); + setGF16m(result, i, j, GF16Utils.mul(getGF16m(a, i, j), k)); } } } From de9c7b87fee201c9479f4aea584e40455556daf7 Mon Sep 17 00:00:00 2001 From: gefeili Date: Sat, 29 Mar 2025 14:28:42 +1030 Subject: [PATCH 282/890] Refactor of SnovaSigner --- .../pqc/crypto/snova/GF16Utils.java | 35 ++- .../pqc/crypto/snova/SnovaEngine.java | 206 +++++++++++------- .../pqc/crypto/snova/SnovaKeyElements.java | 12 +- .../pqc/crypto/snova/SnovaSigner.java | 138 +++++------- 4 files changed, 202 insertions(+), 189 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java index 56d10a609d..4f70fc7500 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java @@ -86,9 +86,9 @@ public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mde * @param menc the output byte array that will hold the encoded bytes * @param mlen the number of nibbles in the input array */ - public static void encode(byte[] m, byte[] menc, int outOff, int mlen) + public static void encode(byte[] m, byte[] menc, int mlen) { - int i, srcIndex = 0; + int i, srcIndex = 0, outOff = 0; // Process pairs of 4-bit values for (i = 0; i < mlen / 2; i++) { @@ -158,7 +158,6 @@ public static void decodeMergeInHalf(byte[] byteArray, byte[] gf16Array, int nGf public static void gf16mMul(byte[] a, byte[] b, byte[] c, int rank) { - for (int i = 0; i < rank; i++) { for (int j = 0; j < rank; j++) @@ -173,6 +172,21 @@ public static void gf16mMul(byte[] a, byte[] b, byte[] c, int rank) } } + public static void gf16mMulTo(byte[] a, byte[] b, byte[] c, int rank) + { + for (int i = 0; i < rank; i++) + { + for (int j = 0; j < rank; j++) + { + int cIndex = i * rank + j; + for (int k = 0; k < rank; ++k) + { + c[cIndex] ^= mt(getGf16m(a, i, k, rank), getGf16m(b, k, j, rank)); + } + } + } + } + static byte getGf16m(byte[] gf16m, int x, int y, int rank) { return gf16m[x * rank + y]; @@ -187,21 +201,6 @@ public static int gf16FromNibble(int idx) return ((middle & 0x41) | ((middle << 2) & 0x208)); } - public static void gf16mAdd(byte[] a, byte[] b, byte[] c, int rank) - { - - for (int i = 0; i < rank; ++i) - { - for (int j = 0; j < rank; ++j) - { - int index = i * rank + j; - // GF16 addition is XOR operation (equivalent to GF(2^4) addition) - // Mask with 0x0F to ensure we only keep 4-bit values - c[index] = (byte)((a[index] ^ b[index]) & 0x0F); - } - } - } - public static byte mul(byte a, byte b) { return MT4B[(a & 0xF) << 4 | (b & 0xF)]; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 8862777d49..f4cb973011 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -137,7 +137,7 @@ public void makeInvertibleByAddingAS(byte[] source, int off) for (int a = 1; a < 16; a++) { generateASMatrix(temp, (byte)a); - addMatrices(temp, 0, source, off, source, off); + xorTo(source, off, temp); if (gf16Determinant(source, off) != 0) { @@ -153,7 +153,7 @@ private byte gf16Determinant(byte[] matrix, int off) case 2: return determinant2x2(matrix, off); case 3: - return determinant3x3(matrix, off, 0, 1, 2); + return determinant3x3(matrix, off); case 4: return determinant4x4(matrix, off); case 5: @@ -165,23 +165,23 @@ private byte gf16Determinant(byte[] matrix, int off) private byte determinant2x2(byte[] m, int off) { - return (byte) - (GF16Utils.mul(getGF16m(m, 0, off), getGF16m(m, 1, off + 1)) ^ - GF16Utils.mul(getGF16m(m, 0, off + 1), getGF16m(m, 1, off))); + return (byte)(GF16Utils.mul(m[off], m[off + 3]) ^ GF16Utils.mul(m[off + 1], m[off + 2])); } - private byte determinant3x3(byte[] m, int off, int i0, int i1, int i2) + private byte determinant3x3(byte[] m, int off) { - return (byte)( - GF16Utils.mul(getGF16m(m, 0, off + i0), (byte)( - GF16Utils.mul(getGF16m(m, 1, off + i1), getGF16m(m, 2, off + i2)) ^ - GF16Utils.mul(getGF16m(m, 1, off + i2), getGF16m(m, 2, off + i1)))) ^ - GF16Utils.mul(getGF16m(m, 0, off + i1), (byte)( - GF16Utils.mul(getGF16m(m, 1, off + i0), getGF16m(m, 2, off + i2)) ^ - GF16Utils.mul(getGF16m(m, 1, off + i2), getGF16m(m, 2, off + i0)))) ^ - GF16Utils.mul(getGF16m(m, 0, off + i2), (byte)( - GF16Utils.mul(getGF16m(m, 1, off + i0), getGF16m(m, 2, off + i1)) ^ - GF16Utils.mul(getGF16m(m, 1, off + i1), getGF16m(m, 2, off + i0))))); + byte m00 = m[off++]; + byte m01 = m[off++]; + byte m02 = m[off++]; + byte m10 = m[off++]; + byte m11 = m[off++]; + byte m12 = m[off++]; + byte m20 = m[off++]; + byte m21 = m[off++]; + byte m22 = m[off]; + return (byte)(GF16Utils.mul(m00, (byte)(GF16Utils.mul(m11, m22) ^ GF16Utils.mul(m12, m21))) ^ + GF16Utils.mul(m01, (byte)(GF16Utils.mul(m10, m22) ^ GF16Utils.mul(m12, m20))) ^ + GF16Utils.mul(m02, (byte)(GF16Utils.mul(m10, m21) ^ GF16Utils.mul(m11, m20)))); } private byte determinant4x4(byte[] m, int off) @@ -222,36 +222,102 @@ private byte determinant4x4(byte[] m, int off) private byte determinant5x5(byte[] m, int off) { - byte m30 = getGF16m(m, 3, off); - byte m31 = getGF16m(m, 3, off + 1); - byte m32 = getGF16m(m, 3, off + 2); - byte m33 = getGF16m(m, 3, off + 3); - byte m34 = getGF16m(m, 3, off + 4); - - byte m40 = getGF16m(m, 4, off); - byte m41 = getGF16m(m, 4, off + 1); - byte m42 = getGF16m(m, 4, off + 2); - byte m43 = getGF16m(m, 4, off + 3); - byte m44 = getGF16m(m, 4, off + 4); - byte result = GF16Utils.mul(determinant3x3(m, off, 0, 1, 2), + byte m00 = m[off++]; + byte m01 = m[off++]; + byte m02 = m[off++]; + byte m03 = m[off++]; + byte m04 = m[off++]; + byte m10 = m[off++]; + byte m11 = m[off++]; + byte m12 = m[off++]; + byte m13 = m[off++]; + byte m14 = m[off++]; + byte m20 = m[off++]; + byte m21 = m[off++]; + byte m22 = m[off++]; + byte m23 = m[off++]; + byte m24 = m[off++]; + byte m30 = m[off++]; + byte m31 = m[off++]; + byte m32 = m[off++]; + byte m33 = m[off++]; + byte m34 = m[off++]; + byte m40 = m[off++]; + byte m41 = m[off++]; + byte m42 = m[off++]; + byte m43 = m[off++]; + byte m44 = m[off]; + + byte m10xm21_m11xm20 = (byte)(GF16Utils.mul(m10, m21) ^ GF16Utils.mul(m11, m20)); + byte m10xm22_m12xm20 = (byte)(GF16Utils.mul(m10, m22) ^ GF16Utils.mul(m12, m20)); + byte m10xm23_m13xm20 = (byte)(GF16Utils.mul(m10, m23) ^ GF16Utils.mul(m13, m20)); + byte m10xm24_m14xm20 = (byte)(GF16Utils.mul(m10, m24) ^ GF16Utils.mul(m14, m20)); + byte m11xm22_m12xm21 = (byte)(GF16Utils.mul(m11, m22) ^ GF16Utils.mul(m12, m21)); + byte m11xm23_m13xm21 = (byte)(GF16Utils.mul(m11, m23) ^ GF16Utils.mul(m13, m21)); + byte m11xm24_m14xm21 = (byte)(GF16Utils.mul(m11, m24) ^ GF16Utils.mul(m14, m21)); + byte m12xm23_m13xm22 = (byte)(GF16Utils.mul(m12, m23) ^ GF16Utils.mul(m13, m22)); + byte m12xm24_m14xm22 = (byte)(GF16Utils.mul(m12, m24) ^ GF16Utils.mul(m14, m22)); + byte m13xm24_m14xm23 = (byte)(GF16Utils.mul(m13, m24) ^ GF16Utils.mul(m14, m23)); + + byte result = GF16Utils.mul(//determinant3x3(m, off, 0, 1, 2), + (byte)( + GF16Utils.mul(m00, m11xm22_m12xm21) ^ + GF16Utils.mul(m01, m10xm22_m12xm20) ^ + GF16Utils.mul(m02, m10xm21_m11xm20)), (byte)(GF16Utils.mul(m33, m44) ^ GF16Utils.mul(m34, m43))); - result ^= GF16Utils.mul(determinant3x3(m, off, 0, 1, 3), + result ^= GF16Utils.mul(//determinant3x3(m, off, 0, 1, 3), + (byte)( + GF16Utils.mul(m00, m11xm23_m13xm21) ^ + GF16Utils.mul(m01, m10xm23_m13xm20) ^ + GF16Utils.mul(m03, m10xm21_m11xm20)), (byte)(GF16Utils.mul(m32, m44) ^ GF16Utils.mul(m34, m42))); - result ^= GF16Utils.mul(determinant3x3(m, off, 0, 1, 4), + result ^= GF16Utils.mul(//determinant3x3(m, off, 0, 1, 4), + (byte)( + GF16Utils.mul(m00, m11xm24_m14xm21) ^ + GF16Utils.mul(m01, m10xm24_m14xm20) ^ + GF16Utils.mul(m04, m10xm21_m11xm20)), (byte)(GF16Utils.mul(m32, m43) ^ GF16Utils.mul(m33, m42))); - result ^= GF16Utils.mul(determinant3x3(m, off, 0, 2, 3), + result ^= GF16Utils.mul(//determinant3x3(m, off, 0, 2, 3), + (byte)( + GF16Utils.mul(m00, m12xm23_m13xm22) ^ + GF16Utils.mul(m02, m10xm23_m13xm20) ^ + GF16Utils.mul(m03, m10xm22_m12xm20)), (byte)(GF16Utils.mul(m31, m44) ^ GF16Utils.mul(m34, m41))); - result ^= GF16Utils.mul(determinant3x3(m, off, 0, 2, 4), + result ^= GF16Utils.mul(//determinant3x3(m, off, 0, 2, 4), + (byte)( + GF16Utils.mul(m00, m12xm24_m14xm22) ^ + GF16Utils.mul(m02, m10xm24_m14xm20) ^ + GF16Utils.mul(m04, m10xm22_m12xm20)), (byte)(GF16Utils.mul(m31, m43) ^ GF16Utils.mul(m33, m41))); - result ^= GF16Utils.mul(determinant3x3(m, off, 0, 3, 4), + result ^= GF16Utils.mul(//determinant3x3(m, off, 0, 3, 4), + (byte)( + GF16Utils.mul(m00, m13xm24_m14xm23) ^ + GF16Utils.mul(m03, m10xm24_m14xm20) ^ + GF16Utils.mul(m04, m10xm23_m13xm20)), (byte)(GF16Utils.mul(m31, m42) ^ GF16Utils.mul(m32, m41))); - result ^= GF16Utils.mul(determinant3x3(m, off, 1, 2, 3), + result ^= GF16Utils.mul(//determinant3x3(m, off, 1, 2, 3), + (byte)( + GF16Utils.mul(m01, m12xm23_m13xm22) ^ + GF16Utils.mul(m02, m11xm23_m13xm21) ^ + GF16Utils.mul(m03, m11xm22_m12xm21)), (byte)(GF16Utils.mul(m30, m44) ^ GF16Utils.mul(m34, m40))); - result ^= GF16Utils.mul(determinant3x3(m, off, 1, 2, 4), + result ^= GF16Utils.mul(//determinant3x3(m, off, 1, 2, 4), + (byte)( + GF16Utils.mul(m01, m12xm24_m14xm22) ^ + GF16Utils.mul(m02, m11xm24_m14xm21) ^ + GF16Utils.mul(m04, m11xm22_m12xm21)), (byte)(GF16Utils.mul(m30, m43) ^ GF16Utils.mul(m33, m40))); - result ^= GF16Utils.mul(determinant3x3(m, off, 1, 3, 4), + result ^= GF16Utils.mul(//determinant3x3(m, off, 1, 3, 4), + (byte)( + GF16Utils.mul(m01, m13xm24_m14xm23) ^ + GF16Utils.mul(m03, m11xm24_m14xm21) ^ + GF16Utils.mul(m04, m11xm23_m13xm21)), (byte)(GF16Utils.mul(m30, m42) ^ GF16Utils.mul(m32, m40))); - result ^= GF16Utils.mul(determinant3x3(m, off, 2, 3, 4), + result ^= GF16Utils.mul(//determinant3x3(m, off, 2, 3, 4), + (byte)( + GF16Utils.mul(m02, m13xm24_m14xm23) ^ + GF16Utils.mul(m03, m12xm24_m14xm22) ^ + GF16Utils.mul(m04, m12xm23_m13xm22)), (byte)(GF16Utils.mul(m30, m41) ^ GF16Utils.mul(m31, m40))); return result; } @@ -273,13 +339,13 @@ private void generateASMatrix(byte[] target, byte a) } - private void addMatrices(byte[] a, int aOff, byte[] b, int bOff, byte[] c, int cOff) + private void xorTo(byte[] a, int aOff, byte[] b) { for (int i = 0; i < l; i++) { for (int j = 0; j < l; j++) { - setGF16m(c, i, cOff + j, (byte)(getGF16m(a, i, aOff + j) ^ getGF16m(b, i, bOff + j))); + setGF16m(a, i, aOff + j, (byte)(getGF16m(a, i, aOff + j) ^ getGF16m(b, i, j))); } } } @@ -295,13 +361,13 @@ public void genAFqS(byte[] c, int cOff, byte[] ptMatrix, int off) for (int i = 1; i < l - 1; ++i) { gf16mScale(S[i], c[cOff + i], temp); - addMatrices(ptMatrix, off, temp, 0, ptMatrix, off); + xorTo(ptMatrix, off, temp); } // Handle last term with special case byte lastScalar = (byte)((c[cOff + l - 1] != 0) ? c[cOff + l - 1] : 16 - (c[cOff] + (c[cOff] == 0 ? 1 : 0))); gf16mScale(S[l - 1], lastScalar, temp); - addMatrices(ptMatrix, off, temp, 0, ptMatrix, off); + xorTo(ptMatrix, off, temp); // Clear temporary matrix //clearMatrix(temp); @@ -342,8 +408,8 @@ public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) { for (int index = 0; index < v; index++) { - GF16Utils.gf16mMul(map1.p11[i][j][index], T12[index][k], temp, l); - GF16Utils.gf16mAdd(map2.f12[i][j][k], temp, map2.f12[i][j][k], l); + + GF16Utils.gf16mMulTo(map1.p11[i][j][index], T12[index][k], map2.f12[i][j][k], l); } } } @@ -358,8 +424,7 @@ public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) { for (int index = 0; index < v; index++) { - GF16Utils.gf16mMul(T12[index][j], map1.p11[i][index][k], temp, l); - GF16Utils.gf16mAdd(map2.f21[i][j][k], temp, map2.f21[i][j][k], l); + GF16Utils.gf16mMulTo(T12[index][j], map1.p11[i][index][k], map2.f21[i][j][k], l); } } } @@ -378,11 +443,7 @@ private static void copy4DMatrix(byte[][][][] src, byte[][][][] dest, { for (int k = 0; k < dim3; k++) { - System.arraycopy( - src[i][j][k], 0, - dest[i][j][k], 0, - lsq - ); + System.arraycopy(src[i][j][k], 0, dest[i][j][k], 0, lsq); } } } @@ -399,47 +460,28 @@ public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] // Initialize P22 with zeros byte[][][][] P22 = new byte[m][o][o][lsq]; - // Temporary buffers - byte[] temp1 = new byte[lsq]; - byte[] temp2 = new byte[lsq]; - - try + for (int i = 0; i < m; i++) { - for (int i = 0; i < m; i++) + for (int j = 0; j < o; j++) { - for (int j = 0; j < o; j++) + for (int k = 0; k < o; k++) { - for (int k = 0; k < o; k++) + for (int index = 0; index < v; index++) { - for (int index = 0; index < v; index++) - { - // temp1 = T12[index][j] * F12[i][index][k] - GF16Utils.gf16mMul(T12[index][j], F12[i][index][k], temp1, l); - - // temp2 = P21[i][j][index] * T12[index][k] - GF16Utils.gf16mMul(P21[i][j][index], T12[index][k], temp2, l); - - // temp1 += temp2 - GF16Utils.gf16mAdd(temp1, temp2, temp1, l); + // temp1 = T12[index][j] * F12[i][index][k] + GF16Utils.gf16mMulTo(T12[index][j], F12[i][index][k], P22[i][j][k], l); - // P22[i][j][k] += temp1 - GF16Utils.gf16mAdd(P22[i][j][k], temp1, P22[i][j][k], l); - } + // temp2 = P21[i][j][index] * T12[index][k] + GF16Utils.gf16mMulTo(P21[i][j][index], T12[index][k], P22[i][j][k], l); } } } - - // Convert GF16 elements to packed bytes - byte[] tmp = new byte[outP22.length << 1]; - MapGroup1.copyTo(P22, tmp); - GF16Utils.encode(tmp, outP22, 0, tmp.length); - } - finally - { - // Secure clear temporary buffers - Arrays.fill(temp1, (byte)0); - Arrays.fill(temp2, (byte)0); } + + // Convert GF16 elements to packed bytes + byte[] tmp = new byte[outP22.length << 1]; + MapGroup1.copyTo(P22, tmp); + GF16Utils.encode(tmp, outP22, tmp.length); } void genSeedsAndT12(byte[][][] T12, byte[] skSeed) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java index 10d6b9537a..960499500b 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java @@ -72,7 +72,7 @@ public void encodeMergerInHalf(byte[] output) inOff = copy3d(T12, input, inOff); inOff = copy4d(map2.f11, input, inOff); inOff = copy4d(map2.f12, input, inOff); - inOff = copy4d(map2.f21, input, inOff); + copy4d(map2.f21, input, inOff); GF16Utils.encodeMergeInHalf(input, length, output); } @@ -88,13 +88,13 @@ public void skUnpack(byte[] input) inOff = copy3d(tmp, inOff, T12); inOff = copy4d(tmp, inOff, map2.f11); inOff = copy4d(tmp, inOff, map2.f12); - inOff = copy4d(tmp, inOff, map2.f21); + copy4d(tmp, inOff, map2.f21); System.arraycopy(input, input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength, publicKey.publicKeySeed, 0, publicKey.publicKeySeed.length); ptPrivateKeySeed = new byte[SnovaKeyPairGenerator.privateSeedLength]; System.arraycopy(input, input.length - SnovaKeyPairGenerator.privateSeedLength, ptPrivateKeySeed, 0, ptPrivateKeySeed.length); } - public int copy3d(byte[][][] alpha, byte[] output, int outOff) + private int copy3d(byte[][][] alpha, byte[] output, int outOff) { for (int i = 0; i < alpha.length; ++i) { @@ -107,7 +107,7 @@ public int copy3d(byte[][][] alpha, byte[] output, int outOff) return outOff; } - public int copy4d(byte[][][][] alpha, byte[] output, int outOff) + private int copy4d(byte[][][][] alpha, byte[] output, int outOff) { for (int i = 0; i < alpha.length; ++i) { @@ -116,7 +116,7 @@ public int copy4d(byte[][][][] alpha, byte[] output, int outOff) return outOff; } - public int copy3d(byte[] input, int inOff, byte[][][] alpha) + private int copy3d(byte[] input, int inOff, byte[][][] alpha) { for (int i = 0; i < alpha.length; ++i) { @@ -129,7 +129,7 @@ public int copy3d(byte[] input, int inOff, byte[][][] alpha) return inOff; } - public int copy4d(byte[] input, int inOff, byte[][][][] alpha) + private int copy4d(byte[] input, int inOff, byte[][][][] alpha) { for (int i = 0; i < alpha.length; ++i) { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index 002ae32553..26f6927576 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -144,11 +144,11 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, byte[][] Temp = new byte[lsq][lsq]; byte[] solution = new byte[m * lsq]; - byte[][][][][] Left = new byte[m][alpha][v][l][l]; - byte[][][][][] Right = new byte[m][alpha][v][l][l]; - byte[][] leftXTmp = new byte[l][l]; - byte[][] rightXtmp = new byte[l][l]; - byte[][][] XInGF16Matrix = new byte[n][l][l]; + byte[][][][] Left = new byte[m][alpha][v][lsq]; + byte[][][][] Right = new byte[m][alpha][v][lsq]; + byte[] leftXTmp = new byte[lsq]; + byte[] rightXtmp = new byte[lsq]; + byte[][] XInGF16Matrix = new byte[n][lsq]; byte[][] FvvGF16Matrix = new byte[m][lsq]; byte[] hashInGF16 = new byte[m * lsq]; byte[][] signatureGF16Matrix = new byte[n][lsq]; @@ -157,8 +157,8 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, byte[] vinegarBytes = new byte[(v * lsq + 1) / 2]; // Temporary matrices - byte[][] gf16mTemp0 = new byte[l][l]; - byte[][] gf16mTemp1 = new byte[l][l]; + byte[] gf16mTemp0 = new byte[lsq]; + byte[] gf16mTemp1 = new byte[lsq]; byte[] gf16mSecretTemp0 = new byte[lsq]; int flagRedo; @@ -194,7 +194,8 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, shake.doFinal(vinegarBytes, 0, vinegarBytes.length); byte[] tmp = new byte[vinegarBytes.length << 1]; GF16Utils.decode(vinegarBytes, tmp, tmp.length); - MapGroup1.fillAlpha(tmp, 0, XInGF16Matrix, tmp.length); + fill(tmp, 0, XInGF16Matrix, tmp.length); + //MapGroup1.fillAlpha(tmp, 0, XInGF16Matrix, tmp.length); // Evaluate vinegar part of central map for (int mi = 0; mi < m; mi++) @@ -274,7 +275,7 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, { int rowLeft = ti / l; int colLeft = tj / l; - byte valLeft = leftXTmp[rowLeft][colLeft]; + byte valLeft = engine.getGF16m(leftXTmp, rowLeft, colLeft); int rowB = tj % l; int colB = ti % l; byte valB = engine.getGF16m(Balpha[mi][a], rowB, colB); @@ -298,7 +299,7 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, byte valA = engine.getGF16m(Aalpha[mi][a], rowA, colA); int rowRight = tj / l; int colRight = ti % l; - byte valRight = rightXtmp[rowRight][colRight]; + byte valRight = engine.getGF16m(rightXtmp, rowRight, colRight); byte product = GF16Utils.mul(valA, valRight); Temp[ti][tj] ^= product; } @@ -331,17 +332,14 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, { for (int j = 0; j < l; ++j) { - XInGF16Matrix[index + v][i][j] = solution[index * lsq + i * l + j]; + engine.setGF16m(XInGF16Matrix[index + v], i, j, solution[index * lsq + i * l + j]); } } } // Copy vinegar variables for (int idx = 0; idx < v; idx++) { - for (int i = 0; i < l; ++i) - { - System.arraycopy(XInGF16Matrix[idx][i], 0, signatureGF16Matrix[idx], i * l, l); - } + System.arraycopy(XInGF16Matrix[idx], 0, signatureGF16Matrix[idx], 0, lsq); } // Process oil variables with T12 matrix @@ -357,17 +355,14 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, // Copy remaining oil variables for (int idx = 0; idx < o; idx++) { - for (int i = 0; i < l; ++i) - { - System.arraycopy(XInGF16Matrix[v + idx][i], 0, signatureGF16Matrix[v + idx], i * l, l); - } + System.arraycopy(XInGF16Matrix[v + idx], 0, signatureGF16Matrix[v + idx], 0, lsq); } byte[] tmp = new byte[n * lsq]; for (int idx = 0; idx < signatureGF16Matrix.length; ++idx) { System.arraycopy(signatureGF16Matrix[idx], 0, tmp, idx * lsq, lsq); } - GF16Utils.encode(tmp, ptSignature, 0, tmp.length); + GF16Utils.encode(tmp, ptSignature, tmp.length); System.arraycopy(arraySalt, 0, ptSignature, ptSignature.length - bytesSalt, bytesSalt); @@ -422,7 +417,7 @@ public boolean verifySignatureCore(byte[] digest, byte[] signature, PublicKey pk } } byte[] encodedHash = new byte[bytesHash]; - GF16Utils.encode(computedHashBytes, encodedHash, 0, computedHashBytes.length); + GF16Utils.encode(computedHashBytes, encodedHash, computedHashBytes.length); // Step 4: Compare hashes return Arrays.areEqual(signedHash, encodedHash); @@ -434,11 +429,12 @@ private void evaluation(byte[][][] hashMatrix, MapGroup1 map1, byte[][][][] p22, final int alpha = params.getAlpha(); final int n = params.getN(); final int l = params.getL(); + final int lsq = l * l; - byte[][][][][] Left = new byte[m][alpha][n][l][l]; - byte[][][][][] Right = new byte[m][alpha][n][l][l]; - byte[][] temp = new byte[l][l]; - byte[][] transposedSig = new byte[l][l]; + byte[][][][] Left = new byte[m][alpha][n][lsq]; + byte[][][][] Right = new byte[m][alpha][n][lsq]; + byte[] temp = new byte[lsq]; + byte[] transposedSig = new byte[lsq]; // Evaluate Left and Right matrices for (int mi = 0; mi < m; mi++) @@ -469,22 +465,17 @@ private void evaluation(byte[][][] hashMatrix, MapGroup1 map1, byte[][][][] p22, } // Process P matrices and accumulate results - byte[][] sumTemp = new byte[l][l]; - byte[][] pTemp = new byte[l][l]; + byte[] sumTemp = new byte[lsq]; + byte[] pTemp = new byte[lsq]; for (int mi = 0; mi < m; mi++) { for (int a = 0; a < alpha; a++) { int miPrime = iPrime(mi, a); - for (int ni = 0; ni < n; ni++) { // sum_t0 = sum(P[miPrime][ni][nj] * Right[mi][a][nj]) - for (int i = 0; i < l; i++) - { - Arrays.fill(sumTemp[i], (byte)0); - } - + Arrays.fill(sumTemp, (byte)0); for (int nj = 0; nj < n; nj++) { byte[] p = getPMatrix(map1, p22, miPrime, ni, nj); @@ -527,61 +518,50 @@ private byte[] getPMatrix(MapGroup1 map1, byte[][][][] p22, int mi, int ni, int } } - private void transposeGF16Matrix(byte[][] src, byte[][] dest) + private void transposeGF16Matrix(byte[][] src, byte[] dest) { for (int i = 0; i < params.getL(); i++) { for (int j = 0; j < params.getL(); j++) { - dest[i][j] = src[j][i]; + engine.setGF16m(dest, i, j, src[j][i]); } } } - private void multiplyGF16Matrices(byte[][] a, byte[][] b, byte[][] result) + private void transposeGF16Matrix(byte[] src, byte[] dest) { for (int i = 0; i < params.getL(); i++) { - Arrays.fill(result[i], (byte)0); for (int j = 0; j < params.getL(); j++) { - byte sum = 0; - for (int k = 0; k < params.getL(); k++) - { - sum ^= GF16Utils.mul( - a[i][k], - b[k][j] - ); - } - result[i][j] = sum; + engine.setGF16m(dest, i, j, engine.getGF16m(src, j, i)); } } } - private void multiplyGF16Matrices(byte[][] a, byte[] b, byte[][] result) + private void multiplyGF16Matrices(byte[] a, byte[] b, byte[] result) { + Arrays.fill(result, (byte)0); for (int i = 0; i < params.getL(); i++) { - Arrays.fill(result[i], (byte)0); for (int j = 0; j < params.getL(); j++) { byte sum = 0; for (int k = 0; k < params.getL(); k++) { - sum ^= GF16Utils.mul( - a[i][k], - engine.getGF16m(b, k, j)); + sum ^= GF16Utils.mul(engine.getGF16m(a, i, k), engine.getGF16m(b, k, j)); } - result[i][j] = sum; + engine.setGF16m(result, i, j, sum); } } } - private void multiplyGF16Matrices(byte[] a, byte[][] b, byte[][] result) + private void multiplyGF16Matrices(byte[] a, byte[][] b, byte[] result) { + Arrays.fill(result, (byte)0); for (int i = 0; i < params.getL(); i++) { - Arrays.fill(result[i], (byte)0); for (int j = 0; j < params.getL(); j++) { byte sum = 0; @@ -591,26 +571,7 @@ private void multiplyGF16Matrices(byte[] a, byte[][] b, byte[][] result) engine.getGF16m(a, i, k), b[k][j]); } - result[i][j] = sum; - } - } - } - - private void multiplyGF16Matrices(byte[] a, byte[] b, byte[][] result) - { - for (int i = 0; i < params.getL(); i++) - { - Arrays.fill(result[i], (byte)0); - for (int j = 0; j < params.getL(); j++) - { - byte sum = 0; - for (int k = 0; k < params.getL(); k++) - { - sum ^= GF16Utils.mul( - engine.getGF16m(a, i, k), - engine.getGF16m(b, k, j)); - } - result[i][j] = sum; + engine.setGF16m(result, i, j, sum); } } } @@ -672,28 +633,27 @@ private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size solution[i] ^= GF16Utils.mul(Gauss[i][j], solution[j]); } } - return 0; } - private void addGF16Matrices(byte[] a, byte[][] b, byte[] result) + private void addGF16Matrices(byte[] a, byte[] b, byte[] result) { - for (int i = 0; i < b.length; i++) + for (int i = 0; i < params.getL(); i++) { - for (int j = 0; j < b[i].length; ++j) + for (int j = 0; j < params.getL(); ++j) { - engine.setGF16m(result, i, j, (byte)(engine.getGF16m(a, i, j) ^ b[i][j])); + engine.setGF16m(result, i, j, (byte)(engine.getGF16m(a, i, j) ^ engine.getGF16m(b, i, j))); } } } - private void addGF16Matrices(byte[][] a, byte[][] b, byte[][] result) + private void addGF16Matrices(byte[][] a, byte[] b, byte[][] result) { - for (int i = 0; i < b.length; i++) + for (int i = 0; i < params.getL(); i++) { - for (int j = 0; j < b[i].length; ++j) + for (int j = 0; j < params.getL(); ++j) { - result[i][j] = (byte)(a[i][j] ^ b[i][j]); + result[i][j] = (byte)(a[i][j] ^ engine.getGF16m(b, i, j)); } } } @@ -703,4 +663,16 @@ private int iPrime(int mi, int alpha) // Implement index calculation based on SNOVA specification return (mi + alpha) % params.getO(); } + + static int fill(byte[] input, int inOff, byte[][] output, int len) + { + int rlt = 0; + for (int i = 0; i < output.length; ++i) + { + int tmp = Math.min(output[i].length, len - rlt); + System.arraycopy(input, inOff + rlt, output[i], 0, tmp); + rlt += tmp; + } + return rlt; + } } From 4ec69f2d7d4cb6fbafa1e9cb267614d4fc8722cd Mon Sep 17 00:00:00 2001 From: Gefei Li Date: Sat, 29 Mar 2025 23:56:30 +0000 Subject: [PATCH 283/890] input/output overlap for DefaultBufferedBlockCipher and PaddedBufferedBlockCipher --- .../crypto/DefaultBufferedBlockCipher.java | 117 ++++++++------- .../paddings/PaddedBufferedBlockCipher.java | 16 +- .../bouncycastle/crypto/test/PaddingTest.java | 139 +++++++++++------- 3 files changed, 167 insertions(+), 105 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java index 662a23a011..89b6f8c812 100644 --- a/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java @@ -14,15 +14,15 @@ public class DefaultBufferedBlockCipher extends BufferedBlockCipher { - protected byte[] buf; - protected int bufOff; + protected byte[] buf; + protected int bufOff; - protected boolean forEncryption; - protected BlockCipher cipher; + protected boolean forEncryption; + protected BlockCipher cipher; protected MultiBlockCipher mbCipher; - protected boolean partialBlockOkay; - protected boolean pgpCFB; + protected boolean partialBlockOkay; + protected boolean pgpCFB; /** * constructor for subclasses @@ -37,7 +37,7 @@ protected DefaultBufferedBlockCipher() * @param cipher the underlying block cipher this buffering object wraps. */ public DefaultBufferedBlockCipher( - BlockCipher cipher) + BlockCipher cipher) { this.cipher = cipher; @@ -57,8 +57,8 @@ public DefaultBufferedBlockCipher( // // check if we can handle partial blocks on doFinal. // - String name = cipher.getAlgorithmName(); - int idx = name.indexOf('/') + 1; + String name = cipher.getAlgorithmName(); + int idx = name.indexOf('/') + 1; pgpCFB = (idx > 0 && name.startsWith("PGP", idx)); @@ -86,14 +86,14 @@ public BlockCipher getUnderlyingCipher() * initialise the cipher. * * @param forEncryption if true the cipher is initialised for - * encryption, if false for decryption. - * @param params the key and other data required by the cipher. - * @exception IllegalArgumentException if the params argument is - * inappropriate. + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @throws IllegalArgumentException if the params argument is + * inappropriate. */ public void init( - boolean forEncryption, - CipherParameters params) + boolean forEncryption, + CipherParameters params) throws IllegalArgumentException { this.forEncryption = forEncryption; @@ -114,7 +114,7 @@ public int getBlockSize() } /** - * return the size of the output buffer required for an update + * return the size of the output buffer required for an update * an input of len bytes. * * @param len the length of the input. @@ -124,7 +124,7 @@ public int getBlockSize() public int getUpdateOutputSize( int len) { - int total = len + bufOff; + int total = len + bufOff; int leftOver; if (pgpCFB) @@ -140,7 +140,7 @@ public int getUpdateOutputSize( } else { - leftOver = total % buf.length; + leftOver = total % buf.length; } return total - leftOver; @@ -169,20 +169,20 @@ public int getOutputSize( /** * process a single byte, producing an output block if necessary. * - * @param in the input byte. - * @param out the space for any output that might be produced. + * @param in the input byte. + * @param out the space for any output that might be produced. * @param outOff the offset from which the output will be copied. * @return the number of output bytes copied to out. - * @exception DataLengthException if there isn't enough space in out. - * @exception IllegalStateException if the cipher isn't initialised. + * @throws DataLengthException if there isn't enough space in out. + * @throws IllegalStateException if the cipher isn't initialised. */ public int processByte( - byte in, - byte[] out, - int outOff) + byte in, + byte[] out, + int outOff) throws DataLengthException, IllegalStateException { - int resultLen = 0; + int resultLen = 0; buf[bufOff++] = in; @@ -198,21 +198,21 @@ public int processByte( /** * process an array of bytes, producing output if necessary. * - * @param in the input byte array. - * @param inOff the offset at which the input data starts. - * @param len the number of bytes to be copied out of the input array. - * @param out the space for any output that might be produced. + * @param in the input byte array. + * @param inOff the offset at which the input data starts. + * @param len the number of bytes to be copied out of the input array. + * @param out the space for any output that might be produced. * @param outOff the offset from which the output will be copied. * @return the number of output bytes copied to out. - * @exception DataLengthException if there isn't enough space in out. - * @exception IllegalStateException if the cipher isn't initialised. + * @throws DataLengthException if there isn't enough space in out. + * @throws IllegalStateException if the cipher isn't initialised. */ public int processBytes( - byte[] in, - int inOff, - int len, - byte[] out, - int outOff) + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) throws DataLengthException, IllegalStateException { if (len < 0) @@ -220,9 +220,9 @@ public int processBytes( throw new IllegalArgumentException("Can't have a negative input length!"); } - int blockSize = getBlockSize(); - int length = getUpdateOutputSize(len); - + int blockSize = getBlockSize(); + int length = getUpdateOutputSize(len); + if (length > 0) { if ((outOff + length) > out.length) @@ -237,12 +237,25 @@ public int processBytes( if (len > gapLen) { System.arraycopy(in, inOff, buf, bufOff, gapLen); + inOff += gapLen; + len -= gapLen; + if (in == out) + { + int inEnd = inOff + len; + int outEnd = outOff + length; + if ((inOff <= outOff && outOff <= inEnd) || + (outOff <= inOff && inOff <= outEnd)) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } + } resultLen += cipher.processBlock(buf, 0, out, outOff); bufOff = 0; - len -= gapLen; - inOff += gapLen; + if (mbCipher != null) { @@ -286,20 +299,20 @@ public int processBytes( /** * Process the last block in the buffer. * - * @param out the array the block currently being held is copied into. + * @param out the array the block currently being held is copied into. * @param outOff the offset at which the copying starts. * @return the number of output bytes copied to out. - * @exception DataLengthException if there is insufficient space in out for - * the output, or the input is not block size aligned and should be. - * @exception IllegalStateException if the underlying cipher is not - * initialised. - * @exception InvalidCipherTextException if padding is expected and not found. - * @exception DataLengthException if the input is not block size - * aligned. + * @throws DataLengthException if there is insufficient space in out for + * the output, or the input is not block size aligned and should be. + * @throws IllegalStateException if the underlying cipher is not + * initialised. + * @throws InvalidCipherTextException if padding is expected and not found. + * @throws DataLengthException if the input is not block size + * aligned. */ public int doFinal( - byte[] out, - int outOff) + byte[] out, + int outOff) throws DataLengthException, IllegalStateException, InvalidCipherTextException { try diff --git a/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java index 28ec78bff9..03a41635b4 100644 --- a/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java @@ -202,12 +202,24 @@ public int processBytes( if (len > gapLen) { System.arraycopy(in, inOff, buf, bufOff, gapLen); + inOff += gapLen; + len -= gapLen; + if (in == out) + { + int inEnd = inOff + len; + int outEnd = outOff + length; + if ((inOff <= outOff && outOff <= inEnd) || + (outOff <= inOff && inOff <= outEnd)) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } + } resultLen += cipher.processBlock(buf, 0, out, outOff); bufOff = 0; - len -= gapLen; - inOff += gapLen; while (len > buf.length) { diff --git a/core/src/test/java/org/bouncycastle/crypto/test/PaddingTest.java b/core/src/test/java/org/bouncycastle/crypto/test/PaddingTest.java index c963b26e4f..379a6c2dc0 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/PaddingTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/PaddingTest.java @@ -13,6 +13,7 @@ import org.bouncycastle.crypto.paddings.ZeroBytePadding; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -27,28 +28,28 @@ public PaddingTest() } private void blockCheck( - PaddedBufferedBlockCipher cipher, - BlockCipherPadding padding, - KeyParameter key, - byte[] data) + PaddedBufferedBlockCipher cipher, + BlockCipherPadding padding, + KeyParameter key, + byte[] data) { - byte[] out = new byte[data.length + 8]; - byte[] dec = new byte[data.length]; - + byte[] out = new byte[data.length + 8]; + byte[] dec = new byte[data.length]; + try - { + { cipher.init(true, key); - - int len = cipher.processBytes(data, 0, data.length, out, 0); - + + int len = cipher.processBytes(data, 0, data.length, out, 0); + len += cipher.doFinal(out, len); - + cipher.init(false, key); - - int decLen = cipher.processBytes(out, 0, len, dec, 0); - + + int decLen = cipher.processBytes(out, 0, len, dec, 0); + decLen += cipher.doFinal(dec, decLen); - + if (!areEqual(data, dec)) { fail("failed to decrypt - i = " + data.length + ", padding = " + padding.getPaddingName()); @@ -59,31 +60,31 @@ private void blockCheck( fail("Exception - " + e.toString(), e); } } - + public void testPadding( - BlockCipherPadding padding, - SecureRandom rand, - byte[] ffVector, - byte[] ZeroVector) + BlockCipherPadding padding, + SecureRandom rand, + byte[] ffVector, + byte[] ZeroVector) { - PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new DESEngine(), padding); - KeyParameter key = new KeyParameter(Hex.decode("0011223344556677")); - + PaddedBufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new DESEngine(), padding); + KeyParameter key = new KeyParameter(Hex.decode("0011223344556677")); + // // ff test // - byte[] data = { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0, (byte)0, (byte)0, (byte)0, (byte)0 }; - + byte[] data = {(byte)0xff, (byte)0xff, (byte)0xff, (byte)0, (byte)0, (byte)0, (byte)0, (byte)0}; + if (ffVector != null) { padding.addPadding(data, 3); - + if (!areEqual(data, ffVector)) { fail("failed ff test for " + padding.getPaddingName()); } } - + // // zero test // @@ -91,23 +92,23 @@ public void testPadding( { data = new byte[8]; padding.addPadding(data, 4); - + if (!areEqual(data, ZeroVector)) { fail("failed zero test for " + padding.getPaddingName()); } } - + for (int i = 1; i != 200; i++) { data = new byte[i]; - + rand.nextBytes(data); blockCheck(cipher, padding, key, data); } } - + private void testOutputSizes() { PaddedBufferedBlockCipher bc = new PaddedBufferedBlockCipher(new DESEngine(), new PKCS7Padding()); @@ -138,15 +139,51 @@ private void testOutputSizes() } } + private void testOverlapping() + { + //Skip the dofinal of the test + PaddedBufferedBlockCipher bc = new PaddedBufferedBlockCipher(new DESEngine(), new PKCS7Padding()); + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[8]; + random.nextBytes(keyBytes); + KeyParameter key = new KeyParameter(keyBytes); + + int offset = 2 + random.nextInt(bc.getBlockSize() - 1); + byte[] data = new byte[bc.getBlockSize() * 2 + offset]; + byte[] expected = new byte[bc.getOutputSize(bc.getBlockSize() * 2)]; + random.nextBytes(data); + + bc.init(true, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, expected, 0); + bc.init(true, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) + { + fail("failed to overlapping of encryption"); + } + + bc.init(false, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, expected, 0); + bc.init(false, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) + { + fail("failed to overlapping of encryption"); + } + } + public void performTest() { - SecureRandom rand = new SecureRandom(new byte[20]); - + testOverlapping(); + SecureRandom rand = new SecureRandom(new byte[20]); + rand.setSeed(System.currentTimeMillis()); - + testPadding(new PKCS7Padding(), rand, - Hex.decode("ffffff0505050505"), - Hex.decode("0000000004040404")); + Hex.decode("ffffff0505050505"), + Hex.decode("0000000004040404")); PKCS7Padding padder = new PKCS7Padding(); try @@ -161,27 +198,27 @@ public void performTest() { fail("wrong exception for corrupt padding: " + e); } - } + } testPadding(new ISO10126d2Padding(), rand, - null, - null); - + null, + null); + testPadding(new X923Padding(), rand, - null, - null); + null, + null); testPadding(new TBCPadding(), rand, - Hex.decode("ffffff0000000000"), - Hex.decode("00000000ffffffff")); + Hex.decode("ffffff0000000000"), + Hex.decode("00000000ffffffff")); testPadding(new ZeroBytePadding(), rand, - Hex.decode("ffffff0000000000"), - null); - + Hex.decode("ffffff0000000000"), + null); + testPadding(new ISO7816d4Padding(), rand, - Hex.decode("ffffff8000000000"), - Hex.decode("0000000080000000")); + Hex.decode("ffffff8000000000"), + Hex.decode("0000000080000000")); testOutputSizes(); @@ -193,7 +230,7 @@ public String getName() } public static void main( - String[] args) + String[] args) { runTest(new PaddingTest()); } From 7868d58074be2ca31e3c20788a07fc6b8502f947 Mon Sep 17 00:00:00 2001 From: gefeili Date: Sun, 30 Mar 2025 10:58:26 +1030 Subject: [PATCH 284/890] fix in DefaultMultiBlockCipher --- .../crypto/DefaultMultiBlockCipher.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java index 3bc565cf0e..c64d44b5d2 100644 --- a/core/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java @@ -20,7 +20,13 @@ public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int o int resultLen = 0; int blockSize = this.getMultiBlockSize(); - + int len = blockCount * blockSize; + if (in == out && segmentsOverlap(inOff, len, outOff, len)) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } for (int i = 0; i != blockCount; i++) { resultLen += this.processBlock(in, inOff, out, outOff + resultLen); @@ -30,4 +36,10 @@ public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int o return resultLen; } + + protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) + { + // please ensure a valid check for inLen > 0 and outLen > 0 outside this function + return inOff <= outOff + outLen && outOff <= inOff + inLen; + } } From b4133b92233b0dc16753b3d0475d34a8ac07eabc Mon Sep 17 00:00:00 2001 From: gefeili Date: Sun, 30 Mar 2025 11:08:37 +1030 Subject: [PATCH 285/890] Add test for DefaultMultiBlockCipher --- .../jce/provider/test/BlockCipherTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java index feac0ce1cd..e11733022b 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java @@ -39,6 +39,7 @@ import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.DefaultMultiBlockCipher; import org.bouncycastle.crypto.engines.AESEngine; import org.bouncycastle.crypto.engines.DESEngine; import org.bouncycastle.crypto.paddings.PKCS7Padding; @@ -1747,6 +1748,7 @@ public void performTest() testIncorrectCipherModes(); doFinalTest(); testOverlapping(); + testOverlapping2(); } private void doFinalTest() @@ -1806,6 +1808,41 @@ private void testOverlapping() } } + private void testOverlapping2() + { + //Skip the dofinal of the test + DefaultMultiBlockCipher bc = new AESEngine(); + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[16]; + random.nextBytes(keyBytes); + KeyParameter key = new KeyParameter(keyBytes); + + int offset = 2 + random.nextInt(bc.getBlockSize() - 1); + byte[] data = new byte[bc.getBlockSize() * 2 + offset]; + byte[] expected = new byte[bc.getBlockSize() * 2]; + random.nextBytes(data); + + bc.init(true, key); + bc.processBlocks(data, 0, 2, expected, 0); + bc.init(true, key); + bc.processBlocks(data, 0, 2, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) + { + fail("failed to overlapping of encryption"); + } + + bc.init(false, key); + bc.processBlocks(data, 0, 2, expected, 0); + bc.init(false, key); + bc.processBlocks(data, 0, 2, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) + { + fail("failed to overlapping of encryption"); + } + } + public static void main( String[] args) { From 212f3fe20728482e19f4bb71034506a2e1b8fc45 Mon Sep 17 00:00:00 2001 From: Gefei Li Date: Mon, 31 Mar 2025 03:59:35 +0000 Subject: [PATCH 286/890] Io overlap dbbc child --- .../crypto/BufferedBlockCipher.java | 116 ++++++------ .../crypto/DefaultBufferedBlockCipher.java | 114 ++++++------ .../crypto/DefaultMultiBlockCipher.java | 14 +- .../crypto/engines/AEADBaseEngine.java | 22 ++- .../crypto/engines/RomulusEngine.java | 2 +- .../crypto/modes/CTSBlockCipher.java | 12 +- .../crypto/modes/KXTSBlockCipher.java | 7 +- .../crypto/modes/NISTCTSBlockCipher.java | 12 +- .../crypto/modes/OldCTSBlockCipher.java | 12 +- .../paddings/PaddedBufferedBlockCipher.java | 14 +- .../bouncycastle/crypto/test/AsconTest.java | 6 + .../org/bouncycastle/crypto/test/CTSTest.java | 171 +++++++++++++----- .../bouncycastle/crypto/test/CipherTest.java | 35 ++++ .../crypto/test/DSTU7624Test.java | 38 ++++ .../crypto/test/ElephantTest.java | 3 + .../crypto/test/GiftCofbTest.java | 1 + .../crypto/test/Grain128AEADTest.java | 2 + .../bouncycastle/crypto/test/ISAPTest.java | 4 + .../bouncycastle/crypto/test/NISTCTSTest.java | 103 ++++++++--- .../crypto/test/PhotonBeetleTest.java | 2 + .../bouncycastle/crypto/test/RomulusTest.java | 3 + .../bouncycastle/crypto/test/SparkleTest.java | 5 + .../bouncycastle/crypto/test/XoodyakTest.java | 1 + .../jce/provider/test/BlockCipherTest.java | 79 ++++++++ 24 files changed, 571 insertions(+), 207 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java index 6a2974147c..5ca338cf99 100644 --- a/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java @@ -11,15 +11,15 @@ */ public class BufferedBlockCipher { - protected byte[] buf; - protected int bufOff; + protected byte[] buf; + protected int bufOff; - protected boolean forEncryption; - protected BlockCipher cipher; + protected boolean forEncryption; + protected BlockCipher cipher; protected MultiBlockCipher mbCipher; - protected boolean partialBlockOkay; - protected boolean pgpCFB; + protected boolean partialBlockOkay; + protected boolean pgpCFB; /** * constructor for subclasses @@ -35,7 +35,7 @@ public class BufferedBlockCipher * @deprecated use the constructor on DefaultBufferedBlockCipher. */ public BufferedBlockCipher( - BlockCipher cipher) + BlockCipher cipher) { this.cipher = cipher; @@ -55,8 +55,8 @@ public BufferedBlockCipher( // // check if we can handle partial blocks on doFinal. // - String name = cipher.getAlgorithmName(); - int idx = name.indexOf('/') + 1; + String name = cipher.getAlgorithmName(); + int idx = name.indexOf('/') + 1; pgpCFB = (idx > 0 && name.startsWith("PGP", idx)); @@ -84,14 +84,14 @@ public BlockCipher getUnderlyingCipher() * initialise the cipher. * * @param forEncryption if true the cipher is initialised for - * encryption, if false for decryption. - * @param params the key and other data required by the cipher. - * @exception IllegalArgumentException if the params argument is - * inappropriate. + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @throws IllegalArgumentException if the params argument is + * inappropriate. */ public void init( - boolean forEncryption, - CipherParameters params) + boolean forEncryption, + CipherParameters params) throws IllegalArgumentException { this.forEncryption = forEncryption; @@ -112,7 +112,7 @@ public int getBlockSize() } /** - * return the size of the output buffer required for an update + * return the size of the output buffer required for an update * an input of len bytes. * * @param len the length of the input. @@ -122,7 +122,7 @@ public int getBlockSize() public int getUpdateOutputSize( int len) { - int total = len + bufOff; + int total = len + bufOff; int leftOver; if (pgpCFB) @@ -138,7 +138,7 @@ public int getUpdateOutputSize( } else { - leftOver = total % buf.length; + leftOver = total % buf.length; } return total - leftOver; @@ -167,20 +167,20 @@ public int getOutputSize( /** * process a single byte, producing an output block if necessary. * - * @param in the input byte. - * @param out the space for any output that might be produced. + * @param in the input byte. + * @param out the space for any output that might be produced. * @param outOff the offset from which the output will be copied. * @return the number of output bytes copied to out. - * @exception DataLengthException if there isn't enough space in out. - * @exception IllegalStateException if the cipher isn't initialised. + * @throws DataLengthException if there isn't enough space in out. + * @throws IllegalStateException if the cipher isn't initialised. */ public int processByte( - byte in, - byte[] out, - int outOff) + byte in, + byte[] out, + int outOff) throws DataLengthException, IllegalStateException { - int resultLen = 0; + int resultLen = 0; buf[bufOff++] = in; @@ -196,21 +196,21 @@ public int processByte( /** * process an array of bytes, producing output if necessary. * - * @param in the input byte array. - * @param inOff the offset at which the input data starts. - * @param len the number of bytes to be copied out of the input array. - * @param out the space for any output that might be produced. + * @param in the input byte array. + * @param inOff the offset at which the input data starts. + * @param len the number of bytes to be copied out of the input array. + * @param out the space for any output that might be produced. * @param outOff the offset from which the output will be copied. * @return the number of output bytes copied to out. - * @exception DataLengthException if there isn't enough space in out. - * @exception IllegalStateException if the cipher isn't initialised. + * @throws DataLengthException if there isn't enough space in out. + * @throws IllegalStateException if the cipher isn't initialised. */ public int processBytes( - byte[] in, - int inOff, - int len, - byte[] out, - int outOff) + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) throws DataLengthException, IllegalStateException { if (len < 0) @@ -218,9 +218,9 @@ public int processBytes( throw new IllegalArgumentException("Can't have a negative input length!"); } - int blockSize = getBlockSize(); - int length = getUpdateOutputSize(len); - + int blockSize = getBlockSize(); + int length = getUpdateOutputSize(len); + if (length > 0) { if ((outOff + length) > out.length) @@ -235,12 +235,18 @@ public int processBytes( if (len > gapLen) { System.arraycopy(in, inOff, buf, bufOff, gapLen); + inOff += gapLen; + len -= gapLen; + if (in == out && segmentsOverlap(inOff, len, outOff, length)) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } resultLen += cipher.processBlock(buf, 0, out, outOff); bufOff = 0; - len -= gapLen; - inOff += gapLen; if (mbCipher != null) { @@ -284,20 +290,20 @@ public int processBytes( /** * Process the last block in the buffer. * - * @param out the array the block currently being held is copied into. + * @param out the array the block currently being held is copied into. * @param outOff the offset at which the copying starts. * @return the number of output bytes copied to out. - * @exception DataLengthException if there is insufficient space in out for - * the output, or the input is not block size aligned and should be. - * @exception IllegalStateException if the underlying cipher is not - * initialised. - * @exception InvalidCipherTextException if padding is expected and not found. - * @exception DataLengthException if the input is not block size - * aligned. + * @throws DataLengthException if there is insufficient space in out for + * the output, or the input is not block size aligned and should be. + * @throws IllegalStateException if the underlying cipher is not + * initialised. + * @throws InvalidCipherTextException if padding is expected and not found. + * @throws DataLengthException if the input is not block size + * aligned. */ public int doFinal( - byte[] out, - int outOff) + byte[] out, + int outOff) throws DataLengthException, IllegalStateException, InvalidCipherTextException { try @@ -351,4 +357,10 @@ public void reset() // cipher.reset(); } + + protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) + { + // please ensure a valid check for inLen > 0 and outLen > 0 outside this function + return inOff <= outOff + outLen && outOff <= inOff + inLen; + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java index 89b6f8c812..52868125e4 100644 --- a/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java @@ -14,15 +14,15 @@ public class DefaultBufferedBlockCipher extends BufferedBlockCipher { - protected byte[] buf; - protected int bufOff; + protected byte[] buf; + protected int bufOff; - protected boolean forEncryption; - protected BlockCipher cipher; + protected boolean forEncryption; + protected BlockCipher cipher; protected MultiBlockCipher mbCipher; - protected boolean partialBlockOkay; - protected boolean pgpCFB; + protected boolean partialBlockOkay; + protected boolean pgpCFB; /** * constructor for subclasses @@ -37,7 +37,7 @@ protected DefaultBufferedBlockCipher() * @param cipher the underlying block cipher this buffering object wraps. */ public DefaultBufferedBlockCipher( - BlockCipher cipher) + BlockCipher cipher) { this.cipher = cipher; @@ -57,8 +57,8 @@ public DefaultBufferedBlockCipher( // // check if we can handle partial blocks on doFinal. // - String name = cipher.getAlgorithmName(); - int idx = name.indexOf('/') + 1; + String name = cipher.getAlgorithmName(); + int idx = name.indexOf('/') + 1; pgpCFB = (idx > 0 && name.startsWith("PGP", idx)); @@ -86,14 +86,14 @@ public BlockCipher getUnderlyingCipher() * initialise the cipher. * * @param forEncryption if true the cipher is initialised for - * encryption, if false for decryption. - * @param params the key and other data required by the cipher. - * @throws IllegalArgumentException if the params argument is - * inappropriate. + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. */ public void init( - boolean forEncryption, - CipherParameters params) + boolean forEncryption, + CipherParameters params) throws IllegalArgumentException { this.forEncryption = forEncryption; @@ -114,7 +114,7 @@ public int getBlockSize() } /** - * return the size of the output buffer required for an update + * return the size of the output buffer required for an update * an input of len bytes. * * @param len the length of the input. @@ -124,7 +124,7 @@ public int getBlockSize() public int getUpdateOutputSize( int len) { - int total = len + bufOff; + int total = len + bufOff; int leftOver; if (pgpCFB) @@ -140,7 +140,7 @@ public int getUpdateOutputSize( } else { - leftOver = total % buf.length; + leftOver = total % buf.length; } return total - leftOver; @@ -169,20 +169,20 @@ public int getOutputSize( /** * process a single byte, producing an output block if necessary. * - * @param in the input byte. - * @param out the space for any output that might be produced. + * @param in the input byte. + * @param out the space for any output that might be produced. * @param outOff the offset from which the output will be copied. * @return the number of output bytes copied to out. - * @throws DataLengthException if there isn't enough space in out. - * @throws IllegalStateException if the cipher isn't initialised. + * @exception DataLengthException if there isn't enough space in out. + * @exception IllegalStateException if the cipher isn't initialised. */ public int processByte( - byte in, - byte[] out, - int outOff) + byte in, + byte[] out, + int outOff) throws DataLengthException, IllegalStateException { - int resultLen = 0; + int resultLen = 0; buf[bufOff++] = in; @@ -198,21 +198,21 @@ public int processByte( /** * process an array of bytes, producing output if necessary. * - * @param in the input byte array. - * @param inOff the offset at which the input data starts. - * @param len the number of bytes to be copied out of the input array. - * @param out the space for any output that might be produced. + * @param in the input byte array. + * @param inOff the offset at which the input data starts. + * @param len the number of bytes to be copied out of the input array. + * @param out the space for any output that might be produced. * @param outOff the offset from which the output will be copied. * @return the number of output bytes copied to out. - * @throws DataLengthException if there isn't enough space in out. - * @throws IllegalStateException if the cipher isn't initialised. + * @exception DataLengthException if there isn't enough space in out. + * @exception IllegalStateException if the cipher isn't initialised. */ public int processBytes( - byte[] in, - int inOff, - int len, - byte[] out, - int outOff) + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) throws DataLengthException, IllegalStateException { if (len < 0) @@ -220,9 +220,9 @@ public int processBytes( throw new IllegalArgumentException("Can't have a negative input length!"); } - int blockSize = getBlockSize(); - int length = getUpdateOutputSize(len); - + int blockSize = getBlockSize(); + int length = getUpdateOutputSize(len); + if (length > 0) { if ((outOff + length) > out.length) @@ -239,17 +239,11 @@ public int processBytes( System.arraycopy(in, inOff, buf, bufOff, gapLen); inOff += gapLen; len -= gapLen; - if (in == out) + if (in == out && segmentsOverlap(inOff, len, outOff, length)) { - int inEnd = inOff + len; - int outEnd = outOff + length; - if ((inOff <= outOff && outOff <= inEnd) || - (outOff <= inOff && inOff <= outEnd)) - { - in = new byte[len]; - System.arraycopy(out, inOff, in, 0, len); - inOff = 0; - } + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; } resultLen += cipher.processBlock(buf, 0, out, outOff); @@ -299,20 +293,20 @@ public int processBytes( /** * Process the last block in the buffer. * - * @param out the array the block currently being held is copied into. + * @param out the array the block currently being held is copied into. * @param outOff the offset at which the copying starts. * @return the number of output bytes copied to out. - * @throws DataLengthException if there is insufficient space in out for - * the output, or the input is not block size aligned and should be. - * @throws IllegalStateException if the underlying cipher is not - * initialised. - * @throws InvalidCipherTextException if padding is expected and not found. - * @throws DataLengthException if the input is not block size - * aligned. + * @exception DataLengthException if there is insufficient space in out for + * the output, or the input is not block size aligned and should be. + * @exception IllegalStateException if the underlying cipher is not + * initialised. + * @exception InvalidCipherTextException if padding is expected and not found. + * @exception DataLengthException if the input is not block size + * aligned. */ public int doFinal( - byte[] out, - int outOff) + byte[] out, + int outOff) throws DataLengthException, IllegalStateException, InvalidCipherTextException { try diff --git a/core/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java index 3bc565cf0e..c64d44b5d2 100644 --- a/core/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java @@ -20,7 +20,13 @@ public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int o int resultLen = 0; int blockSize = this.getMultiBlockSize(); - + int len = blockCount * blockSize; + if (in == out && segmentsOverlap(inOff, len, outOff, len)) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } for (int i = 0; i != blockCount; i++) { resultLen += this.processBlock(in, inOff, out, outOff + resultLen); @@ -30,4 +36,10 @@ public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int o return resultLen; } + + protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) + { + // please ensure a valid check for inLen > 0 and outLen > 0 outside this function + return inOff <= outOff + outLen && outOff <= inOff + inLen; + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java index 09b8382124..6cd536837b 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java @@ -587,6 +587,12 @@ public int processByte(byte input, byte[] output, int outOff) @Override public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { + if (input == output && segmentsOverlap(inOff, len, outOff, processor.getUpdateOutputSize(len))) + { + input = new byte[len]; + System.arraycopy(output, inOff, input, 0, len); + inOff = 0; + } boolean forEncryption = checkData(false); if (forEncryption) { @@ -733,9 +739,16 @@ protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output m_bufPos += len; return 0; } - resultLength = processor.getUpdateOutputSize(len) + m_bufPos - (forEncryption ? 0 : MAC_SIZE); + int length = processor.getUpdateOutputSize(len); + resultLength = length + m_bufPos - (forEncryption ? 0 : MAC_SIZE); ensureSufficientOutputBuffer(output, outOff, resultLength - resultLength % BlockSize); resultLength = 0; + if (input == output && segmentsOverlap(inOff, len, outOff, length)) + { + input = new byte[len]; + System.arraycopy(output, inOff, input, 0, len); + inOff = 0; + } if (forEncryption) { if (m_bufPos > 0) @@ -746,6 +759,7 @@ protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output processBufferEncrypt(m_buf, 0, output, outOff); resultLength = BlockSize; } + // The function is just an operator >= or > while (processor.isLengthExceedingBlockSize(len, BlockSize)) { @@ -1013,6 +1027,12 @@ protected void finishAAD3(State nextState, boolean isDoFinal) m_state = nextState; } + private boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) + { + // please ensure a valid check for inLen > 0 and outLen > 0 outside this function + return inOff <= outOff + outLen && outOff <= inOff + inLen; + } + protected abstract void finishAAD(State nextState, boolean isDoFinal); protected abstract void init(byte[] key, byte[] iv); diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index f0b2ba1484..43603495cd 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -137,7 +137,7 @@ public void processFinalBlock(byte[] output, int outOff) int adlen = aadOperator.getLen(); int mlen = dataOperator.getLen() - (forEncryption ? 0 : MAC_SIZE); byte[] m = ((StreamDataOperator)dataOperator).getBytes(); - int xlen, mOff = 0, mauth = 0; + int xlen, mOff = 0, mauth = outOff; xlen = mlen; if ((adlen & 31) == 0 && adlen != 0) { diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java index af1fa46ded..fd29ece1ea 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java @@ -146,15 +146,19 @@ public int processBytes( if (len > gapLen) { System.arraycopy(in, inOff, buf, bufOff, gapLen); - + inOff += gapLen; + len -= gapLen; + if (in == out && segmentsOverlap(inOff, len, outOff, length)) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } resultLen += cipher.processBlock(buf, 0, out, outOff); System.arraycopy(buf, blockSize, buf, 0, blockSize); bufOff = blockSize; - len -= gapLen; - inOff += gapLen; - while (len > blockSize) { System.arraycopy(in, inOff, buf, bufOff, blockSize); diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java index 2062fea183..083ef1cd6e 100755 --- a/core/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java @@ -123,7 +123,12 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output, int out { throw new IllegalArgumentException("Partial blocks not supported"); } - + if (input == output && segmentsOverlap(inOff, len, outOff, len)) + { + input = new byte[len]; + System.arraycopy(output, inOff, input, 0, len); + inOff = 0; + } for (int pos = 0; pos < len; pos += blockSize) { processBlocks(input, inOff + pos, output, outOff + pos); diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java index e13c6ea9a5..f159d277fe 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java @@ -155,15 +155,19 @@ public int processBytes( if (len > gapLen) { System.arraycopy(in, inOff, buf, bufOff, gapLen); - + inOff += gapLen; + len -= gapLen; + if (in == out && segmentsOverlap(inOff, len, outOff, length)) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } resultLen += cipher.processBlock(buf, 0, out, outOff); System.arraycopy(buf, blockSize, buf, 0, blockSize); bufOff = blockSize; - len -= gapLen; - inOff += gapLen; - while (len > blockSize) { System.arraycopy(in, inOff, buf, bufOff, blockSize); diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java index 13ba147753..c1fcfdb839 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java @@ -149,15 +149,19 @@ public int processBytes( if (len > gapLen) { System.arraycopy(in, inOff, buf, bufOff, gapLen); - + inOff += gapLen; + len -= gapLen; + if (in == out && segmentsOverlap(inOff, len, outOff, length)) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } resultLen += cipher.processBlock(buf, 0, out, outOff); System.arraycopy(buf, blockSize, buf, 0, blockSize); bufOff = blockSize; - len -= gapLen; - inOff += gapLen; - while (len > blockSize) { System.arraycopy(in, inOff, buf, bufOff, blockSize); diff --git a/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java index 03a41635b4..7b42234e26 100644 --- a/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java @@ -204,17 +204,11 @@ public int processBytes( System.arraycopy(in, inOff, buf, bufOff, gapLen); inOff += gapLen; len -= gapLen; - if (in == out) + if (in == out && segmentsOverlap(inOff, len, outOff, length)) { - int inEnd = inOff + len; - int outEnd = outOff + length; - if ((inOff <= outOff && outOff <= inEnd) || - (outOff <= inOff && inOff <= outEnd)) - { - in = new byte[len]; - System.arraycopy(out, inOff, in, 0, len); - inOff = 0; - } + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; } resultLen += cipher.processBlock(buf, 0, out, outOff); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java b/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java index d92c10575d..cebd5f8f9a 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/AsconTest.java @@ -108,6 +108,12 @@ public void performTest() CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon128a)); CipherTest.checkAEADParemeter(this, 20, 16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon80pq)); + CipherTest.testOverlapping(this,16, 16, 16, 16, new AsconAEAD128()); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon128)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon128a)); + CipherTest.testOverlapping(this, 20, 16, 16, 16, new AsconEngine(AsconEngine.AsconParameters.ascon80pq)); + + CipherTest.checkCipher(32, 16, 100, 128, new CipherTest.Instance() { @Override diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java index e59c95844b..37d38330e7 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java @@ -1,5 +1,7 @@ package org.bouncycastle.crypto.test; +import java.security.SecureRandom; + import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.CipherParameters; @@ -14,6 +16,7 @@ import org.bouncycastle.crypto.modes.SICBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -23,22 +26,22 @@ public class CTSTest extends SimpleTest { - static byte[] in1 = Hex.decode("4e6f7720697320746865207420"); - static byte[] in2 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f0aaa"); - static byte[] out1 = Hex.decode("9952f131588465033fa40e8a98"); - static byte[] out2 = Hex.decode("358f84d01eb42988dc34efb994"); - static byte[] out3 = Hex.decode("170171cfad3f04530c509b0c1f0be0aefbd45a8e3755a873bff5ea198504b71683c6"); - + static byte[] in1 = Hex.decode("4e6f7720697320746865207420"); + static byte[] in2 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f0aaa"); + static byte[] out1 = Hex.decode("9952f131588465033fa40e8a98"); + static byte[] out2 = Hex.decode("358f84d01eb42988dc34efb994"); + static byte[] out3 = Hex.decode("170171cfad3f04530c509b0c1f0be0aefbd45a8e3755a873bff5ea198504b71683c6"); + private void testCTS( - int id, - BlockCipher cipher, - CipherParameters params, - byte[] input, - byte[] output) + int id, + BlockCipher cipher, + CipherParameters params, + byte[] input, + byte[] output) throws Exception { - byte[] out = new byte[input.length]; - BufferedBlockCipher engine = new CTSBlockCipher(cipher); + byte[] out = new byte[input.length]; + BufferedBlockCipher engine = new CTSBlockCipher(cipher); engine.init(true, params); @@ -64,15 +67,15 @@ private void testCTS( } private void testOldCTS( - int id, - BlockCipher cipher, - CipherParameters params, - byte[] input, - byte[] output) - throws Exception + int id, + BlockCipher cipher, + CipherParameters params, + byte[] input, + byte[] output) + throws Exception { - byte[] out = new byte[input.length]; - BufferedBlockCipher engine = new OldCTSBlockCipher(cipher); + byte[] out = new byte[input.length]; + BufferedBlockCipher engine = new OldCTSBlockCipher(cipher); engine.init(true, params); @@ -97,77 +100,159 @@ private void testOldCTS( } } - private void testExceptions() throws InvalidCipherTextException + private void testExceptions() + throws InvalidCipherTextException { BufferedBlockCipher engine = new CTSBlockCipher(new DESEngine()); CipherParameters params = new KeyParameter(new byte[engine.getBlockSize()]); engine.init(true, params); byte[] out = new byte[engine.getOutputSize(engine.getBlockSize())]; - + engine.processBytes(new byte[engine.getBlockSize() - 1], 0, engine.getBlockSize() - 1, out, 0); - try + try { engine.doFinal(out, 0); fail("Expected CTS encrypt error on < 1 block input"); - } catch(DataLengthException e) + } + catch (DataLengthException e) { // Expected } engine.init(true, params); engine.processBytes(new byte[engine.getBlockSize()], 0, engine.getBlockSize(), out, 0); - try + try { engine.doFinal(out, 0); - } catch(DataLengthException e) + } + catch (DataLengthException e) { fail("Unexpected CTS encrypt error on == 1 block input"); } engine.init(false, params); engine.processBytes(new byte[engine.getBlockSize() - 1], 0, engine.getBlockSize() - 1, out, 0); - try + try { engine.doFinal(out, 0); fail("Expected CTS decrypt error on < 1 block input"); - } catch(DataLengthException e) + } + catch (DataLengthException e) { // Expected } engine.init(false, params); engine.processBytes(new byte[engine.getBlockSize()], 0, engine.getBlockSize(), out, 0); - try + try { engine.doFinal(out, 0); - } catch(DataLengthException e) + } + catch (DataLengthException e) { fail("Unexpected CTS decrypt error on == 1 block input"); } - try + try { new CTSBlockCipher(SICBlockCipher.newInstance(AESEngine.newInstance())); fail("Expected CTS construction error - only ECB/CBC supported."); - } catch(IllegalArgumentException e) + } + catch (IllegalArgumentException e) { // Expected } } + private void testOverlapping() + throws Exception + { + //Skip the dofinal of the test + CTSBlockCipher bc = new CTSBlockCipher(AESEngine.newInstance()); + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[16]; + random.nextBytes(keyBytes); + KeyParameter key = new KeyParameter(keyBytes); + + int offset = 1 + random.nextInt(bc.getBlockSize() - 1) + bc.getBlockSize(); + byte[] data = new byte[bc.getBlockSize() * 4 + offset]; + byte[] expected = new byte[bc.getOutputSize(bc.getBlockSize() * 3)]; + random.nextBytes(data); + + bc.init(true, key); + int len = bc.processBytes(data, 0, expected.length, expected, 0); + bc.doFinal(expected, len); + bc.init(true, key); + len = bc.processBytes(data, 0, expected.length, data, offset); + bc.doFinal(data, offset + len); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + + bc.init(false, key); + bc.processBytes(data, 0, expected.length, expected, 0); + bc.init(false, key); + bc.processBytes(data, 0, expected.length, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + } + + private void testOverlapping2() + throws Exception + { + //Skip the dofinal of the test + OldCTSBlockCipher bc = new OldCTSBlockCipher(AESEngine.newInstance()); + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[16]; + random.nextBytes(keyBytes); + KeyParameter key = new KeyParameter(keyBytes); + + int offset = 1 + random.nextInt(bc.getBlockSize() - 1) + bc.getBlockSize(); + byte[] data = new byte[bc.getBlockSize() * 4 + offset]; + byte[] expected = new byte[bc.getOutputSize(bc.getBlockSize() * 3)]; + random.nextBytes(data); + + bc.init(true, key); + int len = bc.processBytes(data, 0, expected.length, expected, 0); + bc.doFinal(expected, len); + bc.init(true, key); + len = bc.processBytes(data, 0, expected.length, data, offset); + bc.doFinal(data, offset + len); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + + bc.init(false, key); + bc.processBytes(data, 0, expected.length, expected, 0); + bc.init(false, key); + bc.processBytes(data, 0, expected.length, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + } + public String getName() { return "CTS"; } - public void performTest() + public void performTest() throws Exception { - byte[] key1 = { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF }; - byte[] key2 = { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF, (byte)0xee, (byte)0xff }; - byte[] iv = { 1, 2, 3, 4, 5, 6, 7, 8 }; + byte[] key1 = {(byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF}; + byte[] key2 = {(byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF, (byte)0xee, (byte)0xff}; + byte[] iv = {1, 2, 3, 4, 5, 6, 7, 8}; testCTS(1, new DESEngine(), new KeyParameter(key1), in1, out1); testCTS(2, new CBCBlockCipher(new DESEngine()), new ParametersWithIV(new KeyParameter(key1), iv), in1, out2); @@ -177,11 +262,11 @@ public void performTest() // test vectors from rfc3962 // byte[] aes128 = Hex.decode("636869636b656e207465726979616b69"); - byte[] aesIn1 = Hex.decode("4920776f756c64206c696b652074686520"); + byte[] aesIn1 = Hex.decode("4920776f756c64206c696b652074686520"); byte[] aesOut1 = Hex.decode("c6353568f2bf8cb4d8a580362da7ff7f97"); - byte[] aesIn2 = Hex.decode("4920776f756c64206c696b65207468652047656e6572616c20476175277320"); + byte[] aesIn2 = Hex.decode("4920776f756c64206c696b65207468652047656e6572616c20476175277320"); byte[] aesOut2 = Hex.decode("fc00783e0efdb2c1d445d4c8eff7ed2297687268d6ecccc0c07b25e25ecfe5"); - byte[] aesIn3 = Hex.decode("4920776f756c64206c696b65207468652047656e6572616c2047617527732043"); + byte[] aesIn3 = Hex.decode("4920776f756c64206c696b65207468652047656e6572616c2047617527732043"); byte[] aesOut3 = Hex.decode("39312523a78662d5be7fcbcc98ebf5a897687268d6ecccc0c07b25e25ecfe584"); testCTS(4, new CBCBlockCipher(AESEngine.newInstance()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aesIn1, aesOut1); @@ -202,16 +287,18 @@ public void performTest() testOldCTS(9, new CBCBlockCipher(AESEngine.newInstance()), new ParametersWithIV(new KeyParameter(aes128), new byte[16]), aes1Block, preErrata); byte[] aes128b = Hex.decode("aafd12f659cae63489b479e5076ddec2f06cb58faafd12f6"); - byte[] aesIn1b = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f"); + byte[] aesIn1b = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f"); byte[] aesOut1b = Hex.decode("6db2f802d99e1ef0a5940f306079e083cf87f4d8bb9d1abb36cdd9f44ead7d04"); testCTS(10, new CBCBlockCipher(AESEngine.newInstance()), new ParametersWithIV(new KeyParameter(aes128b), Hex.decode("aafd12f659cae63489b479e5076ddec2")), aesIn1b, aesOut1b); testExceptions(); + testOverlapping(); + testOverlapping2(); } public static void main( - String[] args) + String[] args) { runTest(new CTSTest()); } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java index e88201d44e..19ac878202 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CipherTest.java @@ -908,4 +908,39 @@ static void implTestExceptionsGetUpdateOutputSize(AEADCipher cipher, boolean for } } } + + static void testOverlapping(SimpleTest test, int keySize, int ivSize, int macSize, int blockSize, AEADCipher cipher) + throws Exception + { + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[keySize]; + byte[] ivBytes = new byte[ivSize]; + int offset = 1 + random.nextInt(blockSize - 1); + byte[] data = new byte[blockSize * 2 + offset + macSize]; + byte[] expected; + random.nextBytes(keyBytes); + random.nextBytes(ivBytes); + random.nextBytes(data); + AEADParameters parameters = new AEADParameters(new KeyParameter(new byte[keySize]), macSize * 8, new byte[ivSize], null); + cipher.init(true, parameters); + expected = new byte[cipher.getOutputSize(blockSize * 2)]; + int len = cipher.processBytes(data, 0, blockSize * 2, expected, 0); + cipher.doFinal(expected, len); + cipher.init(true, parameters); + len = cipher.processBytes(data, 0, blockSize * 2, data, offset); + cipher.doFinal(data, len + offset); + test.isTrue("fail on testing overlapping of encryption for " + cipher.getAlgorithmName(), + Arrays.areEqual(expected, 0, expected.length, data, offset, offset + expected.length)); + System.arraycopy(data, offset, data, 0, expected.length); + cipher.init(false, parameters); + expected = new byte[cipher.getOutputSize(data.length)]; + len = cipher.processBytes(data, 0, blockSize * 2 + macSize, expected, 0); + cipher.doFinal(expected, len); + cipher.init(false, parameters); + len = cipher.processBytes(data, 0, blockSize * 2 + macSize, data, offset); + cipher.doFinal(data, len + offset); + test.isTrue("fail on testing overlapping of decryption for " + cipher.getAlgorithmName(), + Arrays.areEqual(expected, 0, blockSize * 2, data, offset, offset + blockSize * 2)); + + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/DSTU7624Test.java b/core/src/test/java/org/bouncycastle/crypto/test/DSTU7624Test.java index 2af7f7978c..fd3a0d531e 100755 --- a/core/src/test/java/org/bouncycastle/crypto/test/DSTU7624Test.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/DSTU7624Test.java @@ -96,6 +96,7 @@ public void performTest() CCMModeTests(); XTSModeTests(); GCMModeTests(); + testOverlapping(); } public static void main( @@ -1464,4 +1465,41 @@ private void doFinalTest(AEADBlockCipher cipher, byte[] key, byte[] iv, byte[] a fail("Failed doFinal test - after: " + cipher.getAlgorithmName()); } } + + private void testOverlapping() + { + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[16]; + byte[] iv = new byte[16]; + random.nextBytes(keyBytes); + KXTSBlockCipher bc = new KXTSBlockCipher(new DSTU7624Engine(128)); + ParametersWithIV param = new ParametersWithIV(new KeyParameter(keyBytes), iv); + + int offset = 1 + random.nextInt(bc.getBlockSize() - 1) + bc.getBlockSize(); + byte[] data = new byte[bc.getBlockSize() * 4 + offset]; + byte[] expected = new byte[bc.getOutputSize(bc.getBlockSize() * 3)]; + random.nextBytes(data); + + bc.init(true, param); + int len = bc.processBytes(data, 0, expected.length, expected, 0); + bc.doFinal(expected, len); + bc.init(true, param); + len = bc.processBytes(data, 0, expected.length, data, offset); + bc.doFinal(data, offset + len); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + + bc.init(false, param); + bc.processBytes(data, 0, expected.length, expected, 0); + bc.init(false, param); + bc.processBytes(data, 0, expected.length, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java index 82e332918c..5d2408a8d7 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ElephantTest.java @@ -38,6 +38,9 @@ public void performTest() CipherTest.checkAEADParemeter(this, 16, 12, 8, 20, new ElephantEngine(ElephantEngine.ElephantParameters.elephant160)); CipherTest.checkAEADParemeter(this, 16, 12, 8, 22, new ElephantEngine(ElephantEngine.ElephantParameters.elephant176)); CipherTest.checkAEADParemeter(this, 16, 12, 16, 25, new ElephantEngine(ElephantEngine.ElephantParameters.elephant200)); + CipherTest.testOverlapping(this, 16, 12, 8, 20, new ElephantEngine(ElephantEngine.ElephantParameters.elephant160)); + CipherTest.testOverlapping(this, 16, 12, 8, 22, new ElephantEngine(ElephantEngine.ElephantParameters.elephant176)); + CipherTest.testOverlapping(this, 16, 12, 16, 25, new ElephantEngine(ElephantEngine.ElephantParameters.elephant200)); CipherTest.checkAEADCipherOutputSize(this, 16, 12, 20, 8, new ElephantEngine(ElephantEngine.ElephantParameters.elephant160)); CipherTest.checkAEADCipherOutputSize(this, 16, 12, 22, 8, new ElephantEngine(ElephantEngine.ElephantParameters.elephant176)); CipherTest.checkAEADCipherOutputSize(this, 16, 12, 25, 16, new ElephantEngine(ElephantEngine.ElephantParameters.elephant200)); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java b/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java index 26935bedb8..a7d18239f1 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/GiftCofbTest.java @@ -38,6 +38,7 @@ public AEADCipher createInstance() }); implTestParametersEngine(new GiftCofbEngine(), 16, 16, 16); CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new GiftCofbEngine()); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new GiftCofbEngine()); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new GiftCofbEngine()); CipherTest.checkCipher(16, 16, 40, 128, new CipherTest.Instance() diff --git a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java index 89da222118..5937166965 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/Grain128AEADTest.java @@ -24,6 +24,7 @@ public String getName() public void performTest() throws Exception { + CipherTest.testOverlapping(this, 16, 12, 8, 20, new Grain128AEADEngine()); CipherTest.implTestVectorsEngine(new Grain128AEADEngine(), "crypto", "LWC_AEAD_KAT_128_96.txt", this); checkAEADCipherOutputSize(this, 16, 12, 8, new Grain128AEADEngine()); CipherTest.checkCipher(32, 12, 100, 128, new CipherTest.Instance() @@ -38,6 +39,7 @@ public AEADCipher createInstance() CipherTest.checkAEADParemeter(this, 16, 12, 8, 20, new Grain128AEADEngine()); + testSplitUpdate(); testExceptions(); testLongAEAD(); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java b/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java index ba91962ad4..007fa98f90 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ISAPTest.java @@ -88,6 +88,10 @@ public AEADCipher createInstance() CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new ISAPEngine(IsapType.ISAP_K_128)); CipherTest.checkAEADParemeter(this, 16, 16, 16, 8, new ISAPEngine(IsapType.ISAP_A_128A)); CipherTest.checkAEADParemeter(this, 16, 16, 16, 8, new ISAPEngine(IsapType.ISAP_A_128)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new ISAPEngine(IsapType.ISAP_K_128A)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new ISAPEngine(IsapType.ISAP_K_128)); + CipherTest.testOverlapping(this, 16, 16, 16, 8, new ISAPEngine(IsapType.ISAP_A_128A)); + CipherTest.testOverlapping(this, 16, 16, 16, 8, new ISAPEngine(IsapType.ISAP_A_128)); CipherTest.checkAEADCipherOutputSize(this, 16, 16, 18, 16, new ISAPEngine(IsapType.ISAP_K_128A)); CipherTest.checkAEADCipherOutputSize(this, 16, 16, 18, 16, new ISAPEngine(IsapType.ISAP_K_128)); CipherTest.checkAEADCipherOutputSize(this, 16, 16, 8, 16, new ISAPEngine(IsapType.ISAP_A_128A)); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/NISTCTSTest.java b/core/src/test/java/org/bouncycastle/crypto/test/NISTCTSTest.java index b1febcd4ca..77f4e64bd2 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/NISTCTSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/NISTCTSTest.java @@ -1,14 +1,19 @@ package org.bouncycastle.crypto.test; +import java.security.SecureRandom; + import org.bouncycastle.crypto.BlockCipher; import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.engines.DSTU7624Engine; +import org.bouncycastle.crypto.modes.KXTSBlockCipher; import org.bouncycastle.crypto.modes.NISTCTSBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -36,23 +41,23 @@ public class NISTCTSTest private static byte[] cs2NotQuiteTwoBlockOut = Hex.decode("f098097ca69b72e3a46e9ca21bb5ebbc22ecf2ac77"); private static byte[] cs3NotQuiteTwoBlockOut = Hex.decode("f098097ca69b72e3a46e9ca21bb5ebbc22ecf2ac77"); - static byte[] in1 = Hex.decode("4e6f7720697320746865207420"); - static byte[] in2 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f0aaa"); - static byte[] out1 = Hex.decode("9952f131588465033fa40e8a98"); - static byte[] out2 = Hex.decode("358f84d01eb42988dc34efb994"); - static byte[] out3 = Hex.decode("170171cfad3f04530c509b0c1f0be0aefbd45a8e3755a873bff5ea198504b71683c6"); - + static byte[] in1 = Hex.decode("4e6f7720697320746865207420"); + static byte[] in2 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f0aaa"); + static byte[] out1 = Hex.decode("9952f131588465033fa40e8a98"); + static byte[] out2 = Hex.decode("358f84d01eb42988dc34efb994"); + static byte[] out3 = Hex.decode("170171cfad3f04530c509b0c1f0be0aefbd45a8e3755a873bff5ea198504b71683c6"); + private void testCTS( - int id, - int type, - BlockCipher cipher, - CipherParameters params, - byte[] input, - byte[] output) + int id, + int type, + BlockCipher cipher, + CipherParameters params, + byte[] input, + byte[] output) throws Exception { - byte[] out = new byte[input.length]; - BufferedBlockCipher engine = new NISTCTSBlockCipher(type, cipher); + byte[] out = new byte[input.length]; + BufferedBlockCipher engine = new NISTCTSBlockCipher(type, cipher); engine.init(true, params); @@ -77,63 +82,106 @@ private void testCTS( } } - private void testExceptions() throws InvalidCipherTextException + private void testExceptions() + throws InvalidCipherTextException { BufferedBlockCipher engine = new NISTCTSBlockCipher(NISTCTSBlockCipher.CS1, AESEngine.newInstance()); CipherParameters params = new KeyParameter(new byte[engine.getBlockSize()]); engine.init(true, params); byte[] out = new byte[engine.getOutputSize(engine.getBlockSize())]; - + engine.processBytes(new byte[engine.getBlockSize() - 1], 0, engine.getBlockSize() - 1, out, 0); - try + try { engine.doFinal(out, 0); fail("Expected CTS encrypt error on < 1 block input"); - } catch(DataLengthException e) + } + catch (DataLengthException e) { // Expected } engine.init(true, params); engine.processBytes(new byte[engine.getBlockSize()], 0, engine.getBlockSize(), out, 0); - try + try { engine.doFinal(out, 0); - } catch(DataLengthException e) + } + catch (DataLengthException e) { fail("Unexpected CTS encrypt error on == 1 block input"); } engine.init(false, params); engine.processBytes(new byte[engine.getBlockSize() - 1], 0, engine.getBlockSize() - 1, out, 0); - try + try { engine.doFinal(out, 0); fail("Expected CTS decrypt error on < 1 block input"); - } catch(DataLengthException e) + } + catch (DataLengthException e) { // Expected } engine.init(false, params); engine.processBytes(new byte[engine.getBlockSize()], 0, engine.getBlockSize(), out, 0); - try + try { engine.doFinal(out, 0); - } catch(DataLengthException e) + } + catch (DataLengthException e) { fail("Unexpected CTS decrypt error on == 1 block input"); } } + private void testOverlapping() + throws Exception + { + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[16]; + byte[] iv = new byte[16]; + random.nextBytes(keyBytes); + BufferedBlockCipher bc = new NISTCTSBlockCipher(NISTCTSBlockCipher.CS1, AESEngine.newInstance()); + ParametersWithIV param = new ParametersWithIV(new KeyParameter(keyBytes), iv); + + int offset = 1 + random.nextInt(bc.getBlockSize() - 1) + bc.getBlockSize(); + byte[] data = new byte[bc.getBlockSize() * 4 + offset]; + byte[] expected = new byte[bc.getOutputSize(bc.getBlockSize() * 3)]; + random.nextBytes(data); + + bc.init(true, param); + int len = bc.processBytes(data, 0, expected.length, expected, 0); + bc.doFinal(expected, len); + bc.init(true, param); + len = bc.processBytes(data, 0, expected.length, data, offset); + bc.doFinal(data, offset + len); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + + bc.init(false, param); + bc.processBytes(data, 0, expected.length, expected, 0); + bc.init(false, param); + bc.processBytes(data, 0, expected.length, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + } + public String getName() { return "NISTCTS"; } - public void performTest() + public void performTest() throws Exception { testCTS(1, NISTCTSBlockCipher.CS1, AESEngine.newInstance(), new ParametersWithIV(key, iv), singleBlock, singleOut); @@ -149,7 +197,7 @@ public void performTest() testCTS(9, NISTCTSBlockCipher.CS3, AESEngine.newInstance(), new ParametersWithIV(key, iv), notQuiteTwo, cs3NotQuiteTwoBlockOut); byte[] aes128b = Hex.decode("aafd12f659cae63489b479e5076ddec2f06cb58faafd12f6"); - byte[] aesIn1b = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f"); + byte[] aesIn1b = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f"); byte[] aesOut1b = Hex.decode("6db2f802d99e1ef0a5940f306079e083cf87f4d8bb9d1abb36cdd9f44ead7d04"); testCTS(10, NISTCTSBlockCipher.CS3, AESEngine.newInstance(), new ParametersWithIV(new KeyParameter(aes128b), Hex.decode("aafd12f659cae63489b479e5076ddec2")), aesIn1b, aesOut1b); @@ -160,10 +208,11 @@ public void performTest() testCTS(11, NISTCTSBlockCipher.CS3, AESEngine.newInstance(), new ParametersWithIV(new KeyParameter(aes128c), Hex.decode("aafd12f659cae63489b479e5076ddec2")), aesIn1b, aesOut1c); testExceptions(); + testOverlapping(); } public static void main( - String[] args) + String[] args) { runTest(new NISTCTSTest()); } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java b/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java index 1814847c0d..3e223b93c5 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/PhotonBeetleTest.java @@ -50,6 +50,8 @@ public void performTest() testExceptions(new PhotonBeetleDigest(), 32); CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb128)); CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb32)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb128)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb32)); CipherTest.checkAEADCipherOutputSize(this, 16, 16, 16, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb128)); CipherTest.checkAEADCipherOutputSize(this, 16, 16, 4, 16, new PhotonBeetleEngine(PhotonBeetleEngine.PhotonBeetleParameters.pb32)); } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java index cf20ff06ec..4b0bf9c3e8 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java @@ -83,6 +83,9 @@ public AEADCipher createInstance() CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM)); CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT)); CipherTest.checkAEADParemeter(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusT)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new RomulusEngine(RomulusEngine.RomulusParameters.RomulusN)); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java index f9fd990e8c..ab76af8a2a 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SparkleTest.java @@ -70,6 +70,11 @@ public void performTest() CipherTest.checkAEADParemeter(this, 16, 32, 16, 16, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_128)); CipherTest.checkAEADParemeter(this, 32, 32, 32, 32, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_256)); + CipherTest.testOverlapping(this, 16, 16, 16, 16, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM128_128)); + CipherTest.testOverlapping(this, 24, 24, 24, 24, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM192_192)); + CipherTest.testOverlapping(this, 16, 32, 16, 16, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_128)); + CipherTest.testOverlapping(this, 32, 32, 32, 32, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_256)); + CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 16, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM128_128)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 24, 192, 24, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM192_192)); CipherTest.checkAEADCipherMultipleBlocks(this, 1025, 33, 16, 128, 32, new SparkleEngine(SparkleEngine.SparkleParameters.SCHWAEMM256_128)); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java b/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java index b32d581a74..37bb098399 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/XoodyakTest.java @@ -53,6 +53,7 @@ public AEADCipher createInstance() testExceptions(new XoodyakDigest(), 32); CipherTest.checkAEADCipherOutputSize(this, 16, 16, 24, 16, new XoodyakEngine()); CipherTest.checkAEADParemeter(this, 16, 16, 16, 24, new XoodyakEngine()); + CipherTest.testOverlapping(this, 16, 16, 16, 24, new XoodyakEngine()); } private void testVectors() diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java index e13ffcafe9..e11733022b 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java @@ -38,6 +38,13 @@ import javax.crypto.spec.RC5ParameterSpec; import javax.crypto.spec.SecretKeySpec; +import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.DefaultMultiBlockCipher; +import org.bouncycastle.crypto.engines.AESEngine; +import org.bouncycastle.crypto.engines.DESEngine; +import org.bouncycastle.crypto.paddings.PKCS7Padding; +import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; @@ -1740,6 +1747,8 @@ public void performTest() testExceptions(); testIncorrectCipherModes(); doFinalTest(); + testOverlapping(); + testOverlapping2(); } private void doFinalTest() @@ -1764,6 +1773,76 @@ private void doFinalTest() } } + private void testOverlapping() + { + //Skip the dofinal of the test + BufferedBlockCipher bc = new BufferedBlockCipher(AESEngine.newInstance()); + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[16]; + random.nextBytes(keyBytes); + KeyParameter key = new KeyParameter(keyBytes); + + int offset = 2 + random.nextInt(bc.getBlockSize() - 1); + byte[] data = new byte[bc.getBlockSize() * 2 + offset]; + byte[] expected = new byte[bc.getOutputSize(bc.getBlockSize() * 2)]; + random.nextBytes(data); + + bc.init(true, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, expected, 0); + bc.init(true, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) + { + fail("failed to overlapping of encryption"); + } + + bc.init(false, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, expected, 0); + bc.init(false, key); + bc.processBytes(data, 0, bc.getBlockSize() * 2 + 1, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) + { + fail("failed to overlapping of encryption"); + } + } + + private void testOverlapping2() + { + //Skip the dofinal of the test + DefaultMultiBlockCipher bc = new AESEngine(); + SecureRandom random = new SecureRandom(); + byte[] keyBytes = new byte[16]; + random.nextBytes(keyBytes); + KeyParameter key = new KeyParameter(keyBytes); + + int offset = 2 + random.nextInt(bc.getBlockSize() - 1); + byte[] data = new byte[bc.getBlockSize() * 2 + offset]; + byte[] expected = new byte[bc.getBlockSize() * 2]; + random.nextBytes(data); + + bc.init(true, key); + bc.processBlocks(data, 0, 2, expected, 0); + bc.init(true, key); + bc.processBlocks(data, 0, 2, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) + { + fail("failed to overlapping of encryption"); + } + + bc.init(false, key); + bc.processBlocks(data, 0, 2, expected, 0); + bc.init(false, key); + bc.processBlocks(data, 0, 2, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) + { + fail("failed to overlapping of encryption"); + } + } + public static void main( String[] args) { From ff08d4a816c3e064765d2a07eefd3e4c8f92f770 Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 31 Mar 2025 17:42:14 +1030 Subject: [PATCH 287/890] GF16 --- .../pqc/crypto/mayo/GF16Utils.java | 33 +- .../pqc/crypto/mayo/MayoKeyPairGenerator.java | 3 +- .../pqc/crypto/mayo/MayoSigner.java | 29 +- .../bouncycastle/pqc/crypto/mayo/Utils.java | 72 ---- .../pqc/crypto/snova/GF16Utils.java | 178 ++-------- .../pqc/crypto/snova/SnovaEngine.java | 301 +++++++---------- .../pqc/crypto/snova/SnovaKeyElements.java | 18 +- .../pqc/crypto/snova/SnovaSigner.java | 308 +++++------------- .../main/java/org/bouncycastle/util/GF16.java | 165 ++++++++++ 9 files changed, 413 insertions(+), 694 deletions(-) create mode 100644 core/src/main/java/org/bouncycastle/util/GF16.java diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java index c12f35b289..01308df98d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java @@ -1,5 +1,7 @@ package org.bouncycastle.pqc.crypto.mayo; +import org.bouncycastle.util.GF16; + class GF16Utils { static final long NIBBLE_MASK_MSB = 0x7777777777777777L; @@ -206,36 +208,17 @@ static void mulAddMUpperTriangularMatXMatTrans(int mVecLimbs, long[] bsMat, byte } } - /** - * GF(16) multiplication mod x^4 + x + 1. - *

    - * This method multiplies two elements in GF(16) (represented as integers 0–15) - * using carryless multiplication followed by reduction modulo x^4 + x + 1. - * - * @param a an element in GF(16) (only the lower 4 bits are used) - * @param b an element in GF(16) (only the lower 4 bits are used) - * @return the product a * b in GF(16) - */ - static int mulF(int a, int b) - { - // Carryless multiply: multiply b by each bit of a and XOR. - int p = (-(a & 1) & b) ^ (-((a >> 1) & 1) & (b << 1)) ^ (-((a >> 2) & 1) & (b << 2)) ^ (-((a >> 3) & 1) & (b << 3)); - // Reduce modulo f(X) = x^4 + x + 1. - int topP = p & 0xF0; - return (p ^ (topP >> 4) ^ (topP >> 3)) & 0x0F; - } - /** * Computes the multiplicative inverse in GF(16) for a GF(16) element. */ static byte inverseF(int a) { // In GF(16), the inverse can be computed via exponentiation. - int a2 = mulF(a, a); - int a4 = mulF(a2, a2); - int a8 = mulF(a4, a4); - int a6 = mulF(a2, a4); - return (byte)mulF(a8, a6); + int a2 = GF16.mul(a, a); + int a4 = GF16.mul(a2, a2); + int a8 = GF16.mul(a4, a4); + int a6 = GF16.mul(a2, a4); + return (byte)GF16.mul(a8, a6); } /** @@ -266,7 +249,7 @@ static void matMul(byte[] a, byte[] b, int bOff, byte[] c, int colrowAB, int row byte result = 0; for (int k = 0; k < colrowAB; k++) { - result ^= mulF(a[aRowStart++], b[bOff + k]); + result ^= GF16.mul(a[aRowStart++], b[bOff + k]); } c[cOff++] = result; } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java index 95797cbf62..4d0df2b02b 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoKeyPairGenerator.java @@ -7,6 +7,7 @@ import org.bouncycastle.crypto.KeyGenerationParameters; import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.GF16; import org.bouncycastle.util.Longs; /** @@ -94,7 +95,7 @@ public AsymmetricCipherKeyPair generateKeyPair() // o ← Decode_o(S[ param_pk_seed_bytes : param_pk_seed_bytes + O_bytes ]) // Decode nibbles from S starting at offset param_pk_seed_bytes into O, // with expected output length = param_v * param_o. - Utils.decode(seed_pk, pkSeedBytes, O, 0, O.length); + GF16.decode(seed_pk, pkSeedBytes, O, 0, O.length); // Expand P1 and P2 into the array P using seed_pk. Utils.expandP1P2(p, P, seed_pk); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index 8b5e9e6b44..0c1d69fa06 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -10,6 +10,7 @@ import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Bytes; +import org.bouncycastle.util.GF16; import org.bouncycastle.util.Longs; import org.bouncycastle.util.Pack; @@ -134,7 +135,7 @@ public byte[] generateSignature(byte[] message) // Decode the portion of S after the first param_pk_seed_bytes into O. // (In C, this is: decode(S + param_pk_seed_bytes, O, param_v * param_o)) - Utils.decode(seed_pk, pk_seed_bytes, O, 0, O.length); + GF16.decode(seed_pk, pk_seed_bytes, O, 0, O.length); // Expand P1 and P2 into the long array P using seed_pk. Utils.expandP1P2(params, P, seed_pk); @@ -186,7 +187,7 @@ public byte[] generateSignature(byte[] message) System.arraycopy(salt, 0, tmp, digestBytes, saltBytes); shake.update(tmp, 0, digestBytes + saltBytes); shake.doFinal(tenc, 0, params.getMBytes()); - Utils.decode(tenc, t, m); + GF16.decode(tenc, t, m); int size = v * k * mVecLimbs; long[] Pv = new long[size]; byte[] Ox = new byte[v]; @@ -201,7 +202,7 @@ public byte[] generateSignature(byte[] message) // Decode vectors for (int i = 0; i < k; i++) { - Utils.decode(V, i * vbytes, Vdec, i * v, v); + GF16.decode(V, i * vbytes, Vdec, i * v, v); } //computeMandVPV(params, Vdec, P, params.getP1Limbs(), P, Mtmp, vPv); @@ -224,7 +225,7 @@ public byte[] generateSignature(byte[] message) // A[(i + 1) * (ok + 1) - 1] = 0; // } - Utils.decode(V, k * vbytes, r, 0, ok); + GF16.decode(V, k * vbytes, r, 0, ok); if (sampleSolution(A, y, r, x)) { @@ -247,7 +248,7 @@ public byte[] generateSignature(byte[] message) } // Encode and add salt - Utils.encode(s, sig, nk); + GF16.encode(s, sig, nk); System.arraycopy(salt, 0, sig, sig.length - saltBytes, saltBytes); return Arrays.concatenate(sig, message); @@ -315,10 +316,10 @@ public boolean verifySignature(byte[] message, byte[] signature) shake.update(tmp, 0, digestBytes); shake.update(signature, sigBytes - saltBytes, saltBytes); shake.doFinal(tEnc, 0, mBytes); - Utils.decode(tEnc, t, m); + GF16.decode(tEnc, t, m); // Decode signature - Utils.decode(signature, s, kn); + GF16.decode(signature, s, kn); // Evaluate public map //evalPublicMap(params, s, P1, P2, P3, y); @@ -384,7 +385,7 @@ void computeRHS(long[] vPv, byte[] t, byte[] y) continue; } - long product = GF16Utils.mulF(top, ft); + long product = GF16.mul(top, ft); if ((jj & 1) == 0) { tempBytes[jj >> 1] ^= (byte)(product & 0xF); @@ -514,10 +515,10 @@ void computeA(long[] Mtmp, byte[] AOut) for (int i = 0, idx = 0; i < F_TAIL_LEN; i++) { int ft = fTailArr[i]; - tab[idx++] = (byte)GF16Utils.mulF(ft, 1); - tab[idx++] = (byte)GF16Utils.mulF(ft, 2); - tab[idx++] = (byte)GF16Utils.mulF(ft, 4); - tab[idx++] = (byte)GF16Utils.mulF(ft, 8); + tab[idx++] = (byte)GF16.mul(ft, 1); + tab[idx++] = (byte)GF16.mul(ft, 2); + tab[idx++] = (byte)GF16.mul(ft, 4); + tab[idx++] = (byte)GF16.mul(ft, 8); } // Final processing @@ -549,7 +550,7 @@ void computeA(long[] Mtmp, byte[] AOut) { for (int i = 0; i + r < m; i++) { - Utils.decode(Abytes, (((r * AWidth) >> 4) + c + i) << 3, + GF16.decode(Abytes, (((r * AWidth) >> 4) + c + i) << 3, AOut, (r + i) * ACols + c, Math.min(16, ACols - 1 - c)); } } @@ -797,7 +798,7 @@ void ef(byte[] A, int nrows, int ncols) for (int i = 0, irowLen = 0; i < nrows; i++, irowLen += rowLen) { Pack.longToLittleEndian(packedA, irowLen, len_4, bytes, 0); - Utils.decode(bytes, 0, A, outIndex, ncols); + GF16.decode(bytes, 0, A, outIndex, ncols); outIndex += ncols; } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java index f1205c3843..934b5ae2f1 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/Utils.java @@ -11,78 +11,6 @@ class Utils { - /** - * Decodes an encoded byte array. - * Each byte in the input contains two nibbles (4-bit values); the lower nibble is stored first, - * followed by the upper nibble. - * - * @param m the input byte array (each byte holds two 4-bit values) - * @param mdec the output array that will hold the decoded nibbles (one per byte) - * @param mdecLen the total number of nibbles to decode - */ - public static void decode(byte[] m, byte[] mdec, int mdecLen) - { - int i, decIndex = 0, blocks = mdecLen >> 1; - // Process pairs of nibbles from each byte - for (i = 0; i < blocks; i++) - { - // Extract the lower nibble - mdec[decIndex++] = (byte)(m[i] & 0x0F); - // Extract the upper nibble (shift right 4 bits) - mdec[decIndex++] = (byte)((m[i] >> 4) & 0x0F); - } - // If there is an extra nibble (odd number of nibbles), decode only the lower nibble - if ((mdecLen & 1) == 1) - { - mdec[decIndex] = (byte)(m[i] & 0x0F); - } - } - - public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mdecLen) - { - // Process pairs of nibbles from each byte - int blocks = mdecLen >> 1; - for (int i = 0; i < blocks; i++) - { - // Extract the lower nibble - mdec[decIndex++] = (byte)(m[mOff] & 0x0F); - // Extract the upper nibble (shift right 4 bits) - mdec[decIndex++] = (byte)((m[mOff++] >> 4) & 0x0F); - } - // If there is an extra nibble (odd number of nibbles), decode only the lower nibble - if ((mdecLen & 1) == 1) - { - mdec[decIndex] = (byte)(m[mOff] & 0x0F); - } - } - - /** - * Encodes an array of 4-bit values into a byte array. - * Two 4-bit values are packed into one byte, with the first nibble stored in the lower 4 bits - * and the second nibble stored in the upper 4 bits. - * - * @param m the input array of 4-bit values (stored as bytes, only lower 4 bits used) - * @param menc the output byte array that will hold the encoded bytes - * @param mlen the number of nibbles in the input array - */ - public static void encode(byte[] m, byte[] menc, int mlen) - { - int i, srcIndex = 0; - // Process pairs of 4-bit values - for (i = 0; i < mlen / 2; i++) - { - int lowerNibble = m[srcIndex] & 0x0F; - int upperNibble = (m[srcIndex + 1] & 0x0F) << 4; - menc[i] = (byte)(lowerNibble | upperNibble); - srcIndex += 2; - } - // If there is an extra nibble (odd number of nibbles), store it directly in lower 4 bits. - if ((mlen & 1) == 1) - { - menc[i] = (byte)(m[srcIndex] & 0x0F); - } - } - public static void unpackMVecs(byte[] in, int inOff, long[] out, int outOff, int vecs, int m) { int mVecLimbs = (m + 15) >> 4; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java index 4f70fc7500..34e34b278c 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java @@ -1,109 +1,9 @@ package org.bouncycastle.pqc.crypto.snova; -public class GF16Utils -{ - private static final byte[] F_STAR = {1, 2, 4, 8, 3, 6, 12, 11, 5, 10, 7, 14, 15, 13, 9}; - private static final byte[] MT4B = new byte[256]; - private static final byte[] INV4B = new byte[16]; - - static byte mt(int p, int q) - { - return MT4B[((p) << 4) ^ (q)]; - } - - static - { - // Initialize multiplication table - for (int i = 0; i < 15; i++) - { - for (int j = 0; j < 15; j++) - { - MT4B[(F_STAR[i] << 4) ^ F_STAR[j]] = F_STAR[(i + j) % 15]; - } - } - - int g = F_STAR[1], g_inv = F_STAR[14], gn = 1, gn_inv = 1; - // Initialize inversion table - INV4B[0] = 0; - INV4B[1] = 1; - for (int i = 0; i < 14; i++) - { - gn = mt(gn, g); - gn_inv = mt(gn_inv, g_inv); - INV4B[gn] = (byte)gn_inv; - } - } - - /** - * Convert one byte of data to GF16 representation (using only half of the - * byte). Example: -> - * - * @param m the input byte array (each byte holds two 4-bit values) - * @param mdec the output array that will hold the decoded nibbles (one per byte) - * @param mdecLen the total number of nibbles to decode - */ - public static void decode(byte[] m, byte[] mdec, int mdecLen) - { - int i, decIndex = 0, blocks = mdecLen >> 1; - // Process pairs of nibbles from each byte - for (i = 0; i < blocks; i++) - { - // Extract the lower nibble - mdec[decIndex++] = (byte)(m[i] & 0x0F); - // Extract the upper nibble (shift right 4 bits) - mdec[decIndex++] = (byte)((m[i] >> 4) & 0x0F); - } - // If there is an extra nibble (odd number of nibbles), decode only the lower nibble - if ((mdecLen & 1) == 1) - { - mdec[decIndex] = (byte)((m[i] & 0xFF) & 0x0F); - } - } - - public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mdecLen) - { - // Process pairs of nibbles from each byte - int blocks = mdecLen >> 1; - for (int i = 0; i < blocks; i++) - { - // Extract the lower nibble - mdec[decIndex++] = (byte)(m[mOff] & 0x0F); - // Extract the upper nibble (shift right 4 bits) - mdec[decIndex++] = (byte)((m[mOff++] >> 4) & 0x0F); - } - // If there is an extra nibble (odd number of nibbles), decode only the lower nibble - if ((mdecLen & 1) == 1) - { - mdec[decIndex] = (byte)(m[mOff] & 0x0F); - } - } - - /** - * Convert two GF16 values to one byte. - * - * @param m the input array of 4-bit values (stored as bytes, only lower 4 bits used) - * @param menc the output byte array that will hold the encoded bytes - * @param mlen the number of nibbles in the input array - */ - public static void encode(byte[] m, byte[] menc, int mlen) - { - int i, srcIndex = 0, outOff = 0; - // Process pairs of 4-bit values - for (i = 0; i < mlen / 2; i++) - { - int lowerNibble = m[srcIndex] & 0x0F; - int upperNibble = (m[srcIndex + 1] & 0x0F) << 4; - menc[outOff++] = (byte)(lowerNibble | upperNibble); - srcIndex += 2; - } - // If there is an extra nibble (odd number of nibbles), store it directly in lower 4 bits. - if ((mlen & 1) == 1) - { - menc[outOff] = (byte)(m[srcIndex] & 0x0F); - } - } +import org.bouncycastle.util.GF16; +class GF16Utils +{ public static void encodeMergeInHalf(byte[] m, int mlen, byte[] menc) { int i, half = (mlen + 1) >>> 1; @@ -119,28 +19,6 @@ public static void encodeMergeInHalf(byte[] m, int mlen, byte[] menc) } } - /** - * Decodes a nibble-packed byte array into an output array. - * - * @param input the input byte array. - * @param inputOffset the offset in input from which to start decoding. - * @param output the output byte array to hold the decoded nibbles. - * @param mdecLen the total number of nibbles to decode. - */ - public static void decode(byte[] input, int inputOffset, byte[] output, int mdecLen) - { - int decIndex = 0, blocks = mdecLen >> 1; - for (int i = 0; i < blocks; i++) - { - output[decIndex++] = (byte)(input[inputOffset] & 0x0F); - output[decIndex++] = (byte)((input[inputOffset++] >> 4) & 0x0F); - } - if ((mdecLen & 1) == 1) - { - output[decIndex] = (byte)(input[inputOffset] & 0x0F); - } - } - public static void decodeMergeInHalf(byte[] byteArray, byte[] gf16Array, int nGf16) { int i, half = (nGf16 + 1) >>> 1; @@ -148,48 +26,52 @@ public static void decodeMergeInHalf(byte[] byteArray, byte[] gf16Array, int nGf for (i = 0; i < half; i++) { gf16Array[i] = (byte)(byteArray[i] & 0x0F); + gf16Array[i + half] = (byte)((byteArray[i] >>> 4) & 0x0F); } - // If there is an extra nibble (odd number of nibbles), store it directly in lower 4 bits. - for (i = 0; i < nGf16 >>> 1; i++) + } + + public static void gf16mTranMul(byte[] a, byte[] b, byte[] c, int rank) + { + for (int i = 0, cOff = 0; i < rank; i++) { - gf16Array[i + half] = (byte)((byteArray[i] >>> 4) & 0x0F); + for (int j = 0, jl = 0; j < rank; j++, jl += rank) + { + c[cOff++] = GF16.dotProduct(a, i, b, j, rank); + } } } public static void gf16mMul(byte[] a, byte[] b, byte[] c, int rank) { - for (int i = 0; i < rank; i++) + for (int i = 0, aOff = 0, cOff = 0; i < rank; i++, aOff += rank) { for (int j = 0; j < rank; j++) { - int cIndex = i * rank + j; - c[cIndex] = mt(getGf16m(a, i, 0, rank), getGf16m(b, 0, j, rank)); - for (int k = 1; k < rank; ++k) - { - c[cIndex] ^= mt(getGf16m(a, i, k, rank), getGf16m(b, k, j, rank)); - } + c[cOff++] = GF16.innerProduct(a, aOff, b, j, rank); } } } public static void gf16mMulTo(byte[] a, byte[] b, byte[] c, int rank) { - for (int i = 0; i < rank; i++) + for (int i = 0, aOff = 0, cOff = 0; i < rank; i++, aOff += rank) { for (int j = 0; j < rank; j++) { - int cIndex = i * rank + j; - for (int k = 0; k < rank; ++k) - { - c[cIndex] ^= mt(getGf16m(a, i, k, rank), getGf16m(b, k, j, rank)); - } + c[cOff++] ^= GF16.innerProduct(a, aOff, b, j, rank); } } } - static byte getGf16m(byte[] gf16m, int x, int y, int rank) + public static void gf16mMulTo(byte[] a, byte[] b, int bOff, byte[] c, int cOff, int rank) { - return gf16m[x * rank + y]; + for (int i = 0, aOff = 0; i < rank; i++, aOff += rank) + { + for (int j = 0; j < rank; j++) + { + c[cOff++] ^= GF16.innerProduct(a, aOff, b, bOff + j, rank); + } + } } /** @@ -201,16 +83,6 @@ public static int gf16FromNibble(int idx) return ((middle & 0x41) | ((middle << 2) & 0x208)); } - public static byte mul(byte a, byte b) - { - return MT4B[(a & 0xF) << 4 | (b & 0xF)]; - } - - public static byte inv(byte a) - { - return INV4B[a & 0xF]; - } - private static final int GF16_MASK = 0x249; // Mask for GF(2^4) reduction // Constant-time GF16 != 0 check diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index f4cb973011..28a4bea37a 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -8,6 +8,7 @@ import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.GF16; import org.bouncycastle.util.Pack; public class SnovaEngine @@ -40,47 +41,33 @@ public SnovaEngine(SnovaParameters params) } } - public byte getGF16m(byte[] gf16m, int x, int y) - { - return gf16m[x * l + y]; - } - - public void setGF16m(byte[] gf16m, int x, int y, byte value) - { - gf16m[x * l + y] = value; - } - public void be_aI(byte[] target, int off, byte a) { - // Mask 'a' to ensure it's a valid 4-bit GF16 element - a = (byte)(a & 0x0F); - - for (int i = 0; i < l; ++i) +// // Mask 'a' to ensure it's a valid 4-bit GF16 element +// a = (byte)(a & 0x0F); + int l1 = l + 1; + for (int i = 0; i < l; ++i, off += l1) { - for (int j = 0; j < l; ++j) - { - int index = i * l + j + off; - target[index] = (i == j) ? a : (byte)0; - } + target[off] = a; } } private void beTheS(byte[] target) { // Set all elements to 8 - (i + j) in GF16 (4-bit values) - for (int i = 0; i < l; ++i) + for (int i = 0, il = 0; i < l; ++i, il += l) { for (int j = 0; j < l; ++j) { int value = 8 - (i + j); - target[i * l + j] = (byte)(value & 0x0F); // Mask to 4 bits + target[il + j] = (byte)(value & 0x0F); // Mask to 4 bits } } // Special case for rank 5 if (l == 5) { - target[4 * 5 + 4] = (byte)(9 & 0x0F); // Set (4,4) to 9 + target[24] = (byte)9; // Set (4,4) to 9 } } @@ -89,12 +76,12 @@ public void genAFqSCT(byte[] c, int cOff, byte[] ptMatrix) { int lsq = l * l; int[] xTemp = new int[lsq]; - + int l1 = l + 1; // Initialize diagonal with c[0] int cX = GF16Utils.gf16FromNibble(c[cOff]); - for (int ij = 0; ij < l; ij++) + for (int ij = 0, ijl1 = 0; ij < l; ij++, ijl1 += l1) { - xTemp[ij * l + ij] = cX; + xTemp[ijl1] = cX; } // Process middle coefficients @@ -132,12 +119,9 @@ public void makeInvertibleByAddingAS(byte[] source, int off) return; } - byte[] temp = new byte[l * l]; - for (int a = 1; a < 16; a++) { - generateASMatrix(temp, (byte)a); - xorTo(source, off, temp); + generateASMatrixTo(source, off, (byte)a); if (gf16Determinant(source, off) != 0) { @@ -165,7 +149,7 @@ private byte gf16Determinant(byte[] matrix, int off) private byte determinant2x2(byte[] m, int off) { - return (byte)(GF16Utils.mul(m[off], m[off + 3]) ^ GF16Utils.mul(m[off + 1], m[off + 2])); + return (byte)(GF16.mul(m[off], m[off + 3]) ^ GF16.mul(m[off + 1], m[off + 2])); } private byte determinant3x3(byte[] m, int off) @@ -179,9 +163,9 @@ private byte determinant3x3(byte[] m, int off) byte m20 = m[off++]; byte m21 = m[off++]; byte m22 = m[off]; - return (byte)(GF16Utils.mul(m00, (byte)(GF16Utils.mul(m11, m22) ^ GF16Utils.mul(m12, m21))) ^ - GF16Utils.mul(m01, (byte)(GF16Utils.mul(m10, m22) ^ GF16Utils.mul(m12, m20))) ^ - GF16Utils.mul(m02, (byte)(GF16Utils.mul(m10, m21) ^ GF16Utils.mul(m11, m20)))); + return (byte)(GF16.mul(m00, GF16.mul(m11, m22) ^ GF16.mul(m12, m21)) ^ + GF16.mul(m01, GF16.mul(m10, m22) ^ GF16.mul(m12, m20)) ^ + GF16.mul(m02, GF16.mul(m10, m21) ^ GF16.mul(m11, m20))); } private byte determinant4x4(byte[] m, int off) @@ -203,21 +187,21 @@ private byte determinant4x4(byte[] m, int off) byte m32 = m[off++]; byte m33 = m[off]; - byte m22xm33_m23xm32 = (byte)(GF16Utils.mul(m22, m33) ^ GF16Utils.mul(m23, m32)); - byte m21xm33_m23xm31 = (byte)(GF16Utils.mul(m21, m33) ^ GF16Utils.mul(m23, m31)); - byte m21xm32_m22xm31 = (byte)(GF16Utils.mul(m21, m32) ^ GF16Utils.mul(m22, m31)); - byte m20xm33_m23xm30 = (byte)(GF16Utils.mul(m20, m33) ^ GF16Utils.mul(m23, m30)); - byte m20xm32_m32xm30 = (byte)(GF16Utils.mul(m20, m32) ^ GF16Utils.mul(m22, m30)); - byte m20xm31_m21xm30 = (byte)(GF16Utils.mul(m20, m31) ^ GF16Utils.mul(m21, m30)); + byte m22xm33_m23xm32 = (byte)(GF16.mul(m22, m33) ^ GF16.mul(m23, m32)); + byte m21xm33_m23xm31 = (byte)(GF16.mul(m21, m33) ^ GF16.mul(m23, m31)); + byte m21xm32_m22xm31 = (byte)(GF16.mul(m21, m32) ^ GF16.mul(m22, m31)); + byte m20xm33_m23xm30 = (byte)(GF16.mul(m20, m33) ^ GF16.mul(m23, m30)); + byte m20xm32_m32xm30 = (byte)(GF16.mul(m20, m32) ^ GF16.mul(m22, m30)); + byte m20xm31_m21xm30 = (byte)(GF16.mul(m20, m31) ^ GF16.mul(m21, m30)); // POD -> entry[a][b] * (entry[c][d] * entry[e][f] + entry[g][h] * entry[i][j]) - return (byte)(GF16Utils.mul(m00, (byte)(GF16Utils.mul(m11, m22xm33_m23xm32) ^ - GF16Utils.mul(m12, m21xm33_m23xm31) ^ GF16Utils.mul(m13, m21xm32_m22xm31))) ^ - GF16Utils.mul(m01, (byte)(GF16Utils.mul(m10, m22xm33_m23xm32) ^ - GF16Utils.mul(m12, m20xm33_m23xm30) ^ GF16Utils.mul(m13, m20xm32_m32xm30))) ^ - GF16Utils.mul(m02, (byte)(GF16Utils.mul(m10, m21xm33_m23xm31) ^ - GF16Utils.mul(m11, m20xm33_m23xm30) ^ GF16Utils.mul(m13, m20xm31_m21xm30))) ^ - GF16Utils.mul(m03, (byte)(GF16Utils.mul(m10, m21xm32_m22xm31) ^ - GF16Utils.mul(m11, m20xm32_m32xm30) ^ GF16Utils.mul(m12, m20xm31_m21xm30)))); + return (byte)(GF16.mul(m00, GF16.mul(m11, m22xm33_m23xm32) ^ + GF16.mul(m12, m21xm33_m23xm31) ^ GF16.mul(m13, m21xm32_m22xm31)) ^ + GF16.mul(m01, GF16.mul(m10, m22xm33_m23xm32) ^ + GF16.mul(m12, m20xm33_m23xm30) ^ GF16.mul(m13, m20xm32_m32xm30)) ^ + GF16.mul(m02, GF16.mul(m10, m21xm33_m23xm31) ^ + GF16.mul(m11, m20xm33_m23xm30) ^ GF16.mul(m13, m20xm31_m21xm30)) ^ + GF16.mul(m03, GF16.mul(m10, m21xm32_m22xm31) ^ + GF16.mul(m11, m20xm32_m32xm30) ^ GF16.mul(m12, m20xm31_m21xm30))); } private byte determinant5x5(byte[] m, int off) @@ -248,81 +232,71 @@ private byte determinant5x5(byte[] m, int off) byte m43 = m[off++]; byte m44 = m[off]; - byte m10xm21_m11xm20 = (byte)(GF16Utils.mul(m10, m21) ^ GF16Utils.mul(m11, m20)); - byte m10xm22_m12xm20 = (byte)(GF16Utils.mul(m10, m22) ^ GF16Utils.mul(m12, m20)); - byte m10xm23_m13xm20 = (byte)(GF16Utils.mul(m10, m23) ^ GF16Utils.mul(m13, m20)); - byte m10xm24_m14xm20 = (byte)(GF16Utils.mul(m10, m24) ^ GF16Utils.mul(m14, m20)); - byte m11xm22_m12xm21 = (byte)(GF16Utils.mul(m11, m22) ^ GF16Utils.mul(m12, m21)); - byte m11xm23_m13xm21 = (byte)(GF16Utils.mul(m11, m23) ^ GF16Utils.mul(m13, m21)); - byte m11xm24_m14xm21 = (byte)(GF16Utils.mul(m11, m24) ^ GF16Utils.mul(m14, m21)); - byte m12xm23_m13xm22 = (byte)(GF16Utils.mul(m12, m23) ^ GF16Utils.mul(m13, m22)); - byte m12xm24_m14xm22 = (byte)(GF16Utils.mul(m12, m24) ^ GF16Utils.mul(m14, m22)); - byte m13xm24_m14xm23 = (byte)(GF16Utils.mul(m13, m24) ^ GF16Utils.mul(m14, m23)); - - byte result = GF16Utils.mul(//determinant3x3(m, off, 0, 1, 2), - (byte)( - GF16Utils.mul(m00, m11xm22_m12xm21) ^ - GF16Utils.mul(m01, m10xm22_m12xm20) ^ - GF16Utils.mul(m02, m10xm21_m11xm20)), - (byte)(GF16Utils.mul(m33, m44) ^ GF16Utils.mul(m34, m43))); - result ^= GF16Utils.mul(//determinant3x3(m, off, 0, 1, 3), - (byte)( - GF16Utils.mul(m00, m11xm23_m13xm21) ^ - GF16Utils.mul(m01, m10xm23_m13xm20) ^ - GF16Utils.mul(m03, m10xm21_m11xm20)), - (byte)(GF16Utils.mul(m32, m44) ^ GF16Utils.mul(m34, m42))); - result ^= GF16Utils.mul(//determinant3x3(m, off, 0, 1, 4), - (byte)( - GF16Utils.mul(m00, m11xm24_m14xm21) ^ - GF16Utils.mul(m01, m10xm24_m14xm20) ^ - GF16Utils.mul(m04, m10xm21_m11xm20)), - (byte)(GF16Utils.mul(m32, m43) ^ GF16Utils.mul(m33, m42))); - result ^= GF16Utils.mul(//determinant3x3(m, off, 0, 2, 3), - (byte)( - GF16Utils.mul(m00, m12xm23_m13xm22) ^ - GF16Utils.mul(m02, m10xm23_m13xm20) ^ - GF16Utils.mul(m03, m10xm22_m12xm20)), - (byte)(GF16Utils.mul(m31, m44) ^ GF16Utils.mul(m34, m41))); - result ^= GF16Utils.mul(//determinant3x3(m, off, 0, 2, 4), - (byte)( - GF16Utils.mul(m00, m12xm24_m14xm22) ^ - GF16Utils.mul(m02, m10xm24_m14xm20) ^ - GF16Utils.mul(m04, m10xm22_m12xm20)), - (byte)(GF16Utils.mul(m31, m43) ^ GF16Utils.mul(m33, m41))); - result ^= GF16Utils.mul(//determinant3x3(m, off, 0, 3, 4), - (byte)( - GF16Utils.mul(m00, m13xm24_m14xm23) ^ - GF16Utils.mul(m03, m10xm24_m14xm20) ^ - GF16Utils.mul(m04, m10xm23_m13xm20)), - (byte)(GF16Utils.mul(m31, m42) ^ GF16Utils.mul(m32, m41))); - result ^= GF16Utils.mul(//determinant3x3(m, off, 1, 2, 3), - (byte)( - GF16Utils.mul(m01, m12xm23_m13xm22) ^ - GF16Utils.mul(m02, m11xm23_m13xm21) ^ - GF16Utils.mul(m03, m11xm22_m12xm21)), - (byte)(GF16Utils.mul(m30, m44) ^ GF16Utils.mul(m34, m40))); - result ^= GF16Utils.mul(//determinant3x3(m, off, 1, 2, 4), - (byte)( - GF16Utils.mul(m01, m12xm24_m14xm22) ^ - GF16Utils.mul(m02, m11xm24_m14xm21) ^ - GF16Utils.mul(m04, m11xm22_m12xm21)), - (byte)(GF16Utils.mul(m30, m43) ^ GF16Utils.mul(m33, m40))); - result ^= GF16Utils.mul(//determinant3x3(m, off, 1, 3, 4), - (byte)( - GF16Utils.mul(m01, m13xm24_m14xm23) ^ - GF16Utils.mul(m03, m11xm24_m14xm21) ^ - GF16Utils.mul(m04, m11xm23_m13xm21)), - (byte)(GF16Utils.mul(m30, m42) ^ GF16Utils.mul(m32, m40))); - result ^= GF16Utils.mul(//determinant3x3(m, off, 2, 3, 4), - (byte)( - GF16Utils.mul(m02, m13xm24_m14xm23) ^ - GF16Utils.mul(m03, m12xm24_m14xm22) ^ - GF16Utils.mul(m04, m12xm23_m13xm22)), - (byte)(GF16Utils.mul(m30, m41) ^ GF16Utils.mul(m31, m40))); + byte m10xm21_m11xm20 = (byte)(GF16.mul(m10, m21) ^ GF16.mul(m11, m20)); + byte m10xm22_m12xm20 = (byte)(GF16.mul(m10, m22) ^ GF16.mul(m12, m20)); + byte m10xm23_m13xm20 = (byte)(GF16.mul(m10, m23) ^ GF16.mul(m13, m20)); + byte m10xm24_m14xm20 = (byte)(GF16.mul(m10, m24) ^ GF16.mul(m14, m20)); + byte m11xm22_m12xm21 = (byte)(GF16.mul(m11, m22) ^ GF16.mul(m12, m21)); + byte m11xm23_m13xm21 = (byte)(GF16.mul(m11, m23) ^ GF16.mul(m13, m21)); + byte m11xm24_m14xm21 = (byte)(GF16.mul(m11, m24) ^ GF16.mul(m14, m21)); + byte m12xm23_m13xm22 = (byte)(GF16.mul(m12, m23) ^ GF16.mul(m13, m22)); + byte m12xm24_m14xm22 = (byte)(GF16.mul(m12, m24) ^ GF16.mul(m14, m22)); + byte m13xm24_m14xm23 = (byte)(GF16.mul(m13, m24) ^ GF16.mul(m14, m23)); + + byte result = (byte)GF16.mul(//determinant3x3(m, off, 0, 1, 2), + (GF16.mul(m00, m11xm22_m12xm21) ^ + GF16.mul(m01, m10xm22_m12xm20) ^ + GF16.mul(m02, m10xm21_m11xm20)), + (GF16.mul(m33, m44) ^ GF16.mul(m34, m43))); + result ^= GF16.mul(//determinant3x3(m, off, 0, 1, 3), + (GF16.mul(m00, m11xm23_m13xm21) ^ + GF16.mul(m01, m10xm23_m13xm20) ^ + GF16.mul(m03, m10xm21_m11xm20)), + (GF16.mul(m32, m44) ^ GF16.mul(m34, m42))); + result ^= GF16.mul(//determinant3x3(m, off, 0, 1, 4), + (GF16.mul(m00, m11xm24_m14xm21) ^ + GF16.mul(m01, m10xm24_m14xm20) ^ + GF16.mul(m04, m10xm21_m11xm20)), + (GF16.mul(m32, m43) ^ GF16.mul(m33, m42))); + result ^= GF16.mul(//determinant3x3(m, off, 0, 2, 3), + (GF16.mul(m00, m12xm23_m13xm22) ^ + GF16.mul(m02, m10xm23_m13xm20) ^ + GF16.mul(m03, m10xm22_m12xm20)), + (GF16.mul(m31, m44) ^ GF16.mul(m34, m41))); + result ^= GF16.mul(//determinant3x3(m, off, 0, 2, 4), + (GF16.mul(m00, m12xm24_m14xm22) ^ + GF16.mul(m02, m10xm24_m14xm20) ^ + GF16.mul(m04, m10xm22_m12xm20)), + (GF16.mul(m31, m43) ^ GF16.mul(m33, m41))); + result ^= GF16.mul(//determinant3x3(m, off, 0, 3, 4), + (GF16.mul(m00, m13xm24_m14xm23) ^ + GF16.mul(m03, m10xm24_m14xm20) ^ + GF16.mul(m04, m10xm23_m13xm20)), + (GF16.mul(m31, m42) ^ GF16.mul(m32, m41))); + result ^= GF16.mul(//determinant3x3(m, off, 1, 2, 3), + (GF16.mul(m01, m12xm23_m13xm22) ^ + GF16.mul(m02, m11xm23_m13xm21) ^ + GF16.mul(m03, m11xm22_m12xm21)), + (GF16.mul(m30, m44) ^ GF16.mul(m34, m40))); + result ^= GF16.mul(//determinant3x3(m, off, 1, 2, 4), + (GF16.mul(m01, m12xm24_m14xm22) ^ + GF16.mul(m02, m11xm24_m14xm21) ^ + GF16.mul(m04, m11xm22_m12xm21)), + (GF16.mul(m30, m43) ^ GF16.mul(m33, m40))); + result ^= GF16.mul(//determinant3x3(m, off, 1, 3, 4), + (GF16.mul(m01, m13xm24_m14xm23) ^ + GF16.mul(m03, m11xm24_m14xm21) ^ + GF16.mul(m04, m11xm23_m13xm21)), + (GF16.mul(m30, m42) ^ GF16.mul(m32, m40))); + result ^= GF16.mul(//determinant3x3(m, off, 2, 3, 4), + (GF16.mul(m02, m13xm24_m14xm23) ^ + GF16.mul(m03, m12xm24_m14xm22) ^ + GF16.mul(m04, m12xm23_m13xm22)), + (GF16.mul(m30, m41) ^ GF16.mul(m31, m40))); return result; } - private void generateASMatrix(byte[] target, byte a) + private void generateASMatrixTo(byte[] target, int off, byte a) { for (int i = 0; i < l; i++) { @@ -333,53 +307,34 @@ private void generateASMatrix(byte[] target, byte a) { coefficient = 9; } - setGF16m(target, i, j, GF16Utils.mul(coefficient, a)); - } - } - } - - - private void xorTo(byte[] a, int aOff, byte[] b) - { - for (int i = 0; i < l; i++) - { - for (int j = 0; j < l; j++) - { - setGF16m(a, i, aOff + j, (byte)(getGF16m(a, i, aOff + j) ^ getGF16m(b, i, j))); + target[i * l + j + off] ^= GF16.mul(coefficient, a); } } } public void genAFqS(byte[] c, int cOff, byte[] ptMatrix, int off) { - byte[] temp = new byte[l * l]; - // Initialize with be_aI be_aI(ptMatrix, off, c[cOff]); // Process middle terms for (int i = 1; i < l - 1; ++i) { - gf16mScale(S[i], c[cOff + i], temp); - xorTo(ptMatrix, off, temp); + gf16mScaleTo(S[i], c[cOff + i], ptMatrix, off); } // Handle last term with special case byte lastScalar = (byte)((c[cOff + l - 1] != 0) ? c[cOff + l - 1] : 16 - (c[cOff] + (c[cOff] == 0 ? 1 : 0))); - gf16mScale(S[l - 1], lastScalar, temp); - xorTo(ptMatrix, off, temp); - - // Clear temporary matrix - //clearMatrix(temp); + gf16mScaleTo(S[l - 1], lastScalar, ptMatrix, off); } - private void gf16mScale(byte[] a, byte k, byte[] result) + private void gf16mScaleTo(byte[] a, byte k, byte[] c, int cOff) { - for (int i = 0; i < l; ++i) + for (int i = 0, il = 0; i < l; ++i, il += l) { for (int j = 0; j < l; ++j) { - setGF16m(result, i, j, GF16Utils.mul(getGF16m(a, i, j), k)); + c[il + j + cOff] ^= GF16.mul(a[il + j], k); } } } @@ -397,9 +352,6 @@ public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) copy4DMatrix(map1.p12, map2.f12, m, v, o, lsq); copy4DMatrix(map1.p21, map2.f21, m, o, v, lsq); - byte[] temp = new byte[lsq]; - - // First matrix operation sequence for (int i = 0; i < m; i++) { for (int j = 0; j < v; j++) @@ -408,34 +360,17 @@ public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) { for (int index = 0; index < v; index++) { - + // First matrix operation sequence GF16Utils.gf16mMulTo(map1.p11[i][j][index], T12[index][k], map2.f12[i][j][k], l); + // Second matrix operation sequence + GF16Utils.gf16mMulTo(T12[index][k], map1.p11[i][index][j], map2.f21[i][k][j], l); } } } } - - // Second matrix operation sequence - for (int i = 0; i < m; i++) - { - for (int j = 0; j < o; j++) - { - for (int k = 0; k < v; k++) - { - for (int index = 0; index < v; index++) - { - GF16Utils.gf16mMulTo(T12[index][j], map1.p11[i][index][k], map2.f21[i][j][k], l); - } - } - } - } - - // Secure clear temporary buffer - Arrays.fill(temp, (byte)0); } - private static void copy4DMatrix(byte[][][][] src, byte[][][][] dest, - int dim1, int dim2, int dim3, int lsq) + private static void copy4DMatrix(byte[][][][] src, byte[][][][] dest, int dim1, int dim2, int dim3, int lsq) { for (int i = 0; i < dim1; i++) { @@ -458,7 +393,7 @@ public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] int lsq = l * l; // Initialize P22 with zeros - byte[][][][] P22 = new byte[m][o][o][lsq]; + byte[] P22 = new byte[m * o * o * lsq]; for (int i = 0; i < m; i++) { @@ -468,20 +403,19 @@ public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] { for (int index = 0; index < v; index++) { - // temp1 = T12[index][j] * F12[i][index][k] - GF16Utils.gf16mMulTo(T12[index][j], F12[i][index][k], P22[i][j][k], l); + int idx = ((i * o + j) * o + k) * lsq; + // P22[i][j][k] ^= T12[index][j] * F12[i][index][k] + GF16Utils.gf16mMulTo(T12[index][j], F12[i][index][k], 0, P22, idx, l); - // temp2 = P21[i][j][index] * T12[index][k] - GF16Utils.gf16mMulTo(P21[i][j][index], T12[index][k], P22[i][j][k], l); + // P22[i][j][k] ^= P21[i][j][index] * T12[index][k] + GF16Utils.gf16mMulTo(P21[i][j][index], T12[index][k], 0, P22, idx, l); } } } } // Convert GF16 elements to packed bytes - byte[] tmp = new byte[outP22.length << 1]; - MapGroup1.copyTo(P22, tmp); - GF16Utils.encode(tmp, outP22, tmp.length); + GF16.encode(P22, outP22, P22.length); } void genSeedsAndT12(byte[][][] T12, byte[] skSeed) @@ -497,7 +431,7 @@ void genSeedsAndT12(byte[][][] T12, byte[] skSeed) // Convert bytes to GF16 array byte[] gf16PrngOutput = new byte[gf16sPrngPrivate]; - GF16Utils.decode(prngOutput, gf16PrngOutput, gf16sPrngPrivate); + GF16.decode(prngOutput, gf16PrngOutput, gf16sPrngPrivate); // Generate T12 matrices int ptArray = 0; @@ -572,8 +506,7 @@ void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) // Process full blocks while (offset + blockSize <= prngOutput.length) { - ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); - System.arraycopy(blockOut, 0, prngOutput, offset, blockSize); + ctrCipher.processBlock(zeroBlock, 0, prngOutput, offset); offset += blockSize; } // Process any remaining partial block. @@ -585,11 +518,11 @@ void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) } } byte[] temp = new byte[gf16sPrngPublic - qTemp.length]; - GF16Utils.decode(prngOutput, temp, temp.length); + GF16.decode(prngOutput, temp, temp.length); map1.fill(temp); if (l >= 4) { - GF16Utils.decode(prngOutput, temp.length >> 1, qTemp, 0, qTemp.length); + GF16.decode(prngOutput, temp.length >> 1, qTemp, 0, qTemp.length); // Post-processing for invertible matrices for (int pi = 0; pi < m; ++pi) @@ -597,12 +530,6 @@ void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) for (int a = 0; a < alpha; ++a) { makeInvertibleByAddingAS(map1.aAlpha[pi][a], 0); - } - } - for (int pi = 0; pi < m; ++pi) - { - for (int a = 0; a < alpha; ++a) - { makeInvertibleByAddingAS(map1.bAlpha[pi][a], 0); } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java index 960499500b..e084d5c17e 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java @@ -1,6 +1,7 @@ package org.bouncycastle.pqc.crypto.snova; import org.bouncycastle.crypto.digests.SHAKEDigest; +import org.bouncycastle.util.GF16; class SnovaKeyElements { @@ -34,27 +35,16 @@ public SnovaKeyElements(SnovaParameters params, SnovaEngine engine) SHAKEDigest shake = new SHAKEDigest(256); shake.update(seed, 0, seed.length); shake.doFinal(rngOut, 0, rngOut.length); - GF16Utils.decode(rngOut, fixedAbq, 2 * o * alpha * lsq); - GF16Utils.decode(rngOut, alpha * lsq, q12, 2 * o * alpha * l); + GF16.decode(rngOut, fixedAbq, 2 * o * alpha * lsq); + GF16.decode(rngOut, alpha * lsq, q12, 0, 2 * o * alpha * l); // Post-processing for invertible matrices for (int pi = 0; pi < o; ++pi) { for (int a = 0; a < alpha; ++a) { engine.makeInvertibleByAddingAS(fixedAbq, (pi * alpha + a) * lsq); - } - for (int a = 0; a < alpha; ++a) - { engine.makeInvertibleByAddingAS(fixedAbq, ((o + pi) * alpha + a) * lsq); - } - - for (int a = 0; a < alpha; ++a) - { engine.genAFqS(q12, (pi * alpha + a) * l, fixedAbq, ((2 * o + pi) * alpha + a) * lsq); - } - - for (int a = 0; a < alpha; ++a) - { engine.genAFqS(q12, ((o + pi) * alpha + a) * l, fixedAbq, ((3 * o + pi) * alpha + a) * lsq); } } @@ -78,7 +68,7 @@ public void encodeMergerInHalf(byte[] output) public void skUnpack(byte[] input) { - byte[] tmp = new byte[(input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength)<< 1]; + byte[] tmp = new byte[(input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength) << 1]; GF16Utils.decodeMergeInHalf(input, tmp, tmp.length); int inOff = 0; inOff = copy3d(tmp, inOff, map1.aAlpha); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index 26f6927576..a8be7d6c4e 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -8,6 +8,7 @@ import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.pqc.crypto.MessageSigner; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.GF16; public class SnovaSigner implements MessageSigner @@ -15,7 +16,7 @@ public class SnovaSigner private SnovaParameters params; private SnovaEngine engine; private SecureRandom random; - private final SHAKEDigest digest = new SHAKEDigest(256); + private final SHAKEDigest shake = new SHAKEDigest(256); private SnovaPublicKeyParameters pubKey; private SnovaPrivateKeyParameters privKey; @@ -52,9 +53,9 @@ public void init(boolean forSigning, CipherParameters param) @Override public byte[] generateSignature(byte[] message) { - byte[] hash = new byte[digest.getDigestSize()]; - digest.update(message, 0, message.length); - digest.doFinal(hash, 0); + byte[] hash = new byte[shake.getDigestSize()]; + shake.update(message, 0, message.length); + shake.doFinal(hash, 0); byte[] salt = new byte[params.getSaltLength()]; random.nextBytes(salt); byte[] signature = new byte[((params.getN() * params.getLsq() + 1) >>> 1) + params.getSaltLength()]; @@ -84,30 +85,27 @@ public byte[] generateSignature(byte[] message) @Override public boolean verifySignature(byte[] message, byte[] signature) { - byte[] hash = new byte[digest.getDigestSize()]; - digest.update(message, 0, message.length); - digest.doFinal(hash, 0); + byte[] hash = new byte[shake.getDigestSize()]; + shake.update(message, 0, message.length); + shake.doFinal(hash, 0); SnovaKeyElements keyElements = new SnovaKeyElements(params, engine); byte[] pk = pubKey.getEncoded(); System.arraycopy(pk, 0, keyElements.publicKey.publicKeySeed, 0, SnovaKeyPairGenerator.publicSeedLength); System.arraycopy(pk, SnovaKeyPairGenerator.publicSeedLength, keyElements.publicKey.P22, 0, keyElements.publicKey.P22.length); engine.genABQP(keyElements.map1, keyElements.publicKey.publicKeySeed, keyElements.fixedAbq); byte[] p22_gf16s = new byte[keyElements.publicKey.P22.length << 1]; - GF16Utils.decode(keyElements.publicKey.P22, p22_gf16s, p22_gf16s.length); + GF16.decode(keyElements.publicKey.P22, p22_gf16s, p22_gf16s.length); byte[][][][] p22 = new byte[params.getM()][params.getO()][params.getO()][params.getLsq()]; MapGroup1.fillP(p22_gf16s, 0, p22, p22_gf16s.length); return verifySignatureCore(hash, signature, keyElements.publicKey, keyElements.map1, p22); } - public static void createSignedHash( + public void createSignedHash( byte[] digest, int bytesDigest, byte[] ptPublicKeySeed, int seedLengthPublic, byte[] arraySalt, int bytesSalt, byte[] signedHashOut, int bytesHash) { - // Initialize SHAKE256 XOF - SHAKEDigest shake = new SHAKEDigest(256); - // 1. Absorb public key seed shake.update(ptPublicKeySeed, 0, seedLengthPublic); @@ -148,26 +146,23 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, byte[][][][] Right = new byte[m][alpha][v][lsq]; byte[] leftXTmp = new byte[lsq]; byte[] rightXtmp = new byte[lsq]; - byte[][] XInGF16Matrix = new byte[n][lsq]; + byte[][] XInGF16Matrix = new byte[v][lsq]; byte[][] FvvGF16Matrix = new byte[m][lsq]; byte[] hashInGF16 = new byte[m * lsq]; - byte[][] signatureGF16Matrix = new byte[n][lsq]; byte[] signedHash = new byte[bytesHash]; byte[] vinegarBytes = new byte[(v * lsq + 1) / 2]; // Temporary matrices byte[] gf16mTemp0 = new byte[lsq]; - byte[] gf16mTemp1 = new byte[lsq]; - byte[] gf16mSecretTemp0 = new byte[lsq]; int flagRedo; byte numSign = 0; - + byte valLeft, valB, valA, valRight; // Step 1: Create signed hash createSignedHash(digest, digest.length, ptPublicKeySeed, ptPublicKeySeed.length, arraySalt, arraySalt.length, signedHash, bytesHash); - GF16Utils.decode(signedHash, 0, hashInGF16, 0, hashInGF16.length); + GF16.decode(signedHash, 0, hashInGF16, 0, hashInGF16.length); do { @@ -177,7 +172,6 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, Arrays.fill(Gauss[i], (byte)0); } numSign++; - //flagRedo = 0; // Fill last column of Gauss matrix for (int i = 0; i < m * lsq; i++) @@ -186,16 +180,15 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, } // Generate vinegar values - SHAKEDigest shake = new SHAKEDigest(256); + shake.update(ptPrivateKeySeed, 0, ptPrivateKeySeed.length); shake.update(digest, 0, digest.length); shake.update(arraySalt, 0, arraySalt.length); shake.update(numSign); shake.doFinal(vinegarBytes, 0, vinegarBytes.length); byte[] tmp = new byte[vinegarBytes.length << 1]; - GF16Utils.decode(vinegarBytes, tmp, tmp.length); - fill(tmp, 0, XInGF16Matrix, tmp.length); - //MapGroup1.fillAlpha(tmp, 0, XInGF16Matrix, tmp.length); + GF16.decode(vinegarBytes, tmp, tmp.length); + fill(tmp, XInGF16Matrix, tmp.length); // Evaluate vinegar part of central map for (int mi = 0; mi < m; mi++) @@ -204,12 +197,11 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, { for (int idx = 0; idx < v; idx++) { - transposeGF16Matrix(XInGF16Matrix[idx], gf16mTemp0); - multiplyGF16Matrices(gf16mTemp0, Qalpha1[mi][a], gf16mTemp1); - multiplyGF16Matrices(Aalpha[mi][a], gf16mTemp1, Left[mi][a][idx]); + GF16Utils.gf16mTranMul(XInGF16Matrix[idx], Qalpha1[mi][a], gf16mTemp0, l); + GF16Utils.gf16mMul(Aalpha[mi][a], gf16mTemp0, Left[mi][a][idx], l); - multiplyGF16Matrices(Qalpha2[mi][a], XInGF16Matrix[idx], gf16mTemp1); - multiplyGF16Matrices(gf16mTemp1, Balpha[mi][a], Right[mi][a][idx]); + GF16Utils.gf16mMul(Qalpha2[mi][a], XInGF16Matrix[idx], gf16mTemp0, l); + GF16Utils.gf16mMul(gf16mTemp0, Balpha[mi][a], Right[mi][a][idx], l); } } } @@ -228,9 +220,8 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, { for (int k = 0; k < v; k++) { - multiplyGF16Matrices(Left[mi][a][j], F11[miPrime][j][k], gf16mTemp0); - multiplyGF16Matrices(gf16mTemp0, Right[mi][a][k], gf16mTemp1); - addGF16Matrices(FvvGF16Matrix[mi], gf16mTemp1, FvvGF16Matrix[mi]); + GF16Utils.gf16mMul(Left[mi][a][j], F11[miPrime][j][k], gf16mTemp0, l); + GF16Utils.gf16mMulTo(gf16mTemp0, Right[mi][a][k], FvvGF16Matrix[mi], l); } } } @@ -245,7 +236,7 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, for (int k = 0; k < l; k++) { int idx1 = i * lsq + j * l + k; - Gauss[idx1][idx2] ^= engine.getGF16m(FvvGF16Matrix[i], j, k); + Gauss[idx1][idx2] ^= FvvGF16Matrix[i][j * l + k]; } } } @@ -266,42 +257,38 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, // Process each j for Left part for (int j = 0; j < v; ++j) { - multiplyGF16Matrices(Left[mi][a][j], F12[mi_prime][j][index], gf16mTemp0); - multiplyGF16Matrices(gf16mTemp0, Qalpha2[mi][a], leftXTmp); + GF16Utils.gf16mMul(Left[mi][a][j], F12[mi_prime][j][index], gf16mTemp0, l); + GF16Utils.gf16mMul(gf16mTemp0, Qalpha2[mi][a], leftXTmp, l); + GF16Utils.gf16mMul(Qalpha1[mi][a], F21[mi_prime][index][j], gf16mTemp0, l); + GF16Utils.gf16mMul(gf16mTemp0, Right[mi][a][j], rightXtmp, l); // Accumulate into Temp from leftXTmp and Balpha[mi][a] - for (int ti = 0; ti < lsq; ++ti) + // rlra_l is short for "rowLeft_rowA times l" + for (int ti = 0, colB_colRight = 0, rlraxl = 0; ti < lsq; ++ti, ++colB_colRight) { - for (int tj = 0; tj < lsq; ++tj) + if (colB_colRight == l) { - int rowLeft = ti / l; - int colLeft = tj / l; - byte valLeft = engine.getGF16m(leftXTmp, rowLeft, colLeft); - int rowB = tj % l; - int colB = ti % l; - byte valB = engine.getGF16m(Balpha[mi][a], rowB, colB); - byte product = GF16Utils.mul(valLeft, valB); - Temp[ti][tj] ^= product; + colB_colRight = 0; + rlraxl += l; } - } - } - // Process each j for Right part - for (int j = 0; j < v; ++j) - { - multiplyGF16Matrices(Qalpha1[mi][a], F21[mi_prime][index][j], gf16mTemp0); - multiplyGF16Matrices(gf16mTemp0, Right[mi][a][j], rightXtmp); - // Accumulate into Temp from Aalpha[mi][a] and rightXtmp - for (int ti = 0; ti < lsq; ++ti) - { - for (int tj = 0; tj < lsq; ++tj) + valLeft = leftXTmp[rlraxl]; + valRight = rightXtmp[colB_colRight]; + // clrrxl is short for "rowLeft_rowA times l" + // rbcaxl is short for "rowB_colA times l" + for (int tj = 0, rowB_colA = 0, colLeft_rowRight = 0, clrrxl = 0, rbcaxl = 0; tj < lsq; + ++tj, ++rowB_colA, rbcaxl += l) { - int rowA = ti / l; - int colA = tj % l; - byte valA = engine.getGF16m(Aalpha[mi][a], rowA, colA); - int rowRight = tj / l; - int colRight = ti % l; - byte valRight = engine.getGF16m(rightXtmp, rowRight, colRight); - byte product = GF16Utils.mul(valA, valRight); - Temp[ti][tj] ^= product; + if (rowB_colA == l) + { + rowB_colA = 0; + rbcaxl = 0; + colLeft_rowRight++; + clrrxl += l; + valLeft = leftXTmp[rlraxl + colLeft_rowRight]; + valRight = rightXtmp[clrrxl + colB_colRight]; + } + valB = Balpha[mi][a][rbcaxl + colB_colRight]; + valA = Aalpha[mi][a][rlraxl + rowB_colA]; + Temp[ti][tj] ^= GF16.mul(valLeft, valB) ^ GF16.mul(valA, valRight); } } } @@ -318,63 +305,33 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, } } } - - // Gaussian elimination implementation flagRedo = performGaussianElimination(Gauss, solution, m * lsq); - } while (flagRedo != 0); - for (int index = 0; index < o; ++index) - { - for (int i = 0; i < l; ++i) - { - for (int j = 0; j < l; ++j) - { - engine.setGF16m(XInGF16Matrix[index + v], i, j, solution[index * lsq + i * l + j]); - } - } - } // Copy vinegar variables + byte[] tmp = new byte[n * lsq]; for (int idx = 0; idx < v; idx++) { - System.arraycopy(XInGF16Matrix[idx], 0, signatureGF16Matrix[idx], 0, lsq); - } - - // Process oil variables with T12 matrix - for (int idx = 0; idx < v; idx++) - { + System.arraycopy(XInGF16Matrix[idx], 0, tmp, idx * lsq, lsq); for (int i = 0; i < o; i++) { - multiplyGF16Matrices(T12[idx][i], XInGF16Matrix[v + i], gf16mTemp0); - addGF16Matrices(signatureGF16Matrix[idx], gf16mTemp0, signatureGF16Matrix[idx]); + GF16Utils.gf16mMulTo(T12[idx][i], solution, i * lsq, tmp, idx * lsq, l); } } // Copy remaining oil variables - for (int idx = 0; idx < o; idx++) - { - System.arraycopy(XInGF16Matrix[v + idx], 0, signatureGF16Matrix[v + idx], 0, lsq); - } - byte[] tmp = new byte[n * lsq]; - for (int idx = 0; idx < signatureGF16Matrix.length; ++idx) - { - System.arraycopy(signatureGF16Matrix[idx], 0, tmp, idx * lsq, lsq); - } - GF16Utils.encode(tmp, ptSignature, tmp.length); + System.arraycopy(solution, 0, tmp, v * lsq, lsq * o); + GF16.encode(tmp, ptSignature, tmp.length); System.arraycopy(arraySalt, 0, ptSignature, ptSignature.length - bytesSalt, bytesSalt); - - // Clear sensitive data - Arrays.fill(gf16mSecretTemp0, (byte)0); } public boolean verifySignatureCore(byte[] digest, byte[] signature, PublicKey pkx, MapGroup1 map1, byte[][][][] p22) { final int bytesHash = (params.getO() * params.getLsq() + 1) >>> 1; final int bytesSalt = params.getSaltLength(); - final int l = params.getL(); final int lsq = params.getLsq(); final int m = params.getM(); final int n = params.getN(); @@ -383,7 +340,7 @@ public boolean verifySignatureCore(byte[] digest, byte[] signature, PublicKey pk // Step 1: Regenerate signed hash using public key seed, digest and salt byte[] signedHash = new byte[bytesHash]; - SHAKEDigest shake = new SHAKEDigest(256); + shake.update(pkx.publicKeySeed, 0, pkx.publicKeySeed.length); shake.update(digest, 0, digest.length); shake.update(signature, bytesSignature, bytesSalt); @@ -396,34 +353,24 @@ public boolean verifySignatureCore(byte[] digest, byte[] signature, PublicKey pk } // Step 2: Convert signature to GF16 matrices - byte[][][] signatureGF16Matrix = new byte[n][l][l]; + byte[][] signatureGF16Matrix = new byte[n][lsq]; byte[] decodedSig = new byte[n * lsq]; - GF16Utils.decode(signature, 0, decodedSig, 0, decodedSig.length); - - MapGroup1.fillAlpha(decodedSig, 0, signatureGF16Matrix, decodedSig.length); + GF16.decode(signature, 0, decodedSig, 0, decodedSig.length); + fill(decodedSig, signatureGF16Matrix, decodedSig.length); // Step 3: Evaluate signature using public key - byte[][][] computedHashMatrix = new byte[m][l][l]; - evaluation(computedHashMatrix, map1, p22, signatureGF16Matrix); + byte[] computedHashBytes = new byte[m * lsq]; + evaluation(computedHashBytes, map1, p22, signatureGF16Matrix); // Convert computed hash matrix to bytes - byte[] computedHashBytes = new byte[m * lsq]; - for (int i = 0; i < m; i++) - { - for (int row = 0; row < l; row++) - { - System.arraycopy(computedHashMatrix[i][row], 0, - computedHashBytes, i * lsq + row * l, l); - } - } byte[] encodedHash = new byte[bytesHash]; - GF16Utils.encode(computedHashBytes, encodedHash, computedHashBytes.length); + GF16.encode(computedHashBytes, encodedHash, computedHashBytes.length); // Step 4: Compare hashes return Arrays.areEqual(signedHash, encodedHash); } - private void evaluation(byte[][][] hashMatrix, MapGroup1 map1, byte[][][][] p22, byte[][][] signature) + private void evaluation(byte[] hashMatrix, MapGroup1 map1, byte[][][][] p22, byte[][] signature) { final int m = params.getM(); final int alpha = params.getAlpha(); @@ -434,39 +381,26 @@ private void evaluation(byte[][][] hashMatrix, MapGroup1 map1, byte[][][][] p22, byte[][][][] Left = new byte[m][alpha][n][lsq]; byte[][][][] Right = new byte[m][alpha][n][lsq]; byte[] temp = new byte[lsq]; - byte[] transposedSig = new byte[lsq]; // Evaluate Left and Right matrices for (int mi = 0; mi < m; mi++) { for (int si = 0; si < n; si++) { - transposeGF16Matrix(signature[si], transposedSig); for (int a = 0; a < alpha; a++) { // Left[mi][a][si] = Aalpha * (sig^T * Qalpha1) - multiplyGF16Matrices(transposedSig, map1.qAlpha1[mi][a], temp); - multiplyGF16Matrices(map1.aAlpha[mi][a], temp, Left[mi][a][si]); + GF16Utils.gf16mTranMul(signature[si], map1.qAlpha1[mi][a], temp, l); + GF16Utils.gf16mMul(map1.aAlpha[mi][a], temp, Left[mi][a][si], l); // Right[mi][a][si] = (Qalpha2 * sig) * Balpha - multiplyGF16Matrices(map1.qAlpha2[mi][a], signature[si], temp); - multiplyGF16Matrices(temp, map1.bAlpha[mi][a], Right[mi][a][si]); + GF16Utils.gf16mMul(map1.qAlpha2[mi][a], signature[si], temp, l); + GF16Utils.gf16mMul(temp, map1.bAlpha[mi][a], Right[mi][a][si], l); } } } - // Initialize hash matrix to zero - for (int mi = 0; mi < m; mi++) - { - for (int i = 0; i < l; i++) - { - Arrays.fill(hashMatrix[mi][i], (byte)0); - } - } - // Process P matrices and accumulate results - byte[] sumTemp = new byte[lsq]; - byte[] pTemp = new byte[lsq]; for (int mi = 0; mi < m; mi++) { for (int a = 0; a < alpha; a++) @@ -475,17 +409,16 @@ private void evaluation(byte[][][] hashMatrix, MapGroup1 map1, byte[][][][] p22, for (int ni = 0; ni < n; ni++) { // sum_t0 = sum(P[miPrime][ni][nj] * Right[mi][a][nj]) - Arrays.fill(sumTemp, (byte)0); - for (int nj = 0; nj < n; nj++) + byte[] p = getPMatrix(map1, p22, miPrime, ni, 0); + GF16Utils.gf16mMul(p, Right[mi][a][0], temp, l); + for (int nj = 1; nj < n; nj++) { - byte[] p = getPMatrix(map1, p22, miPrime, ni, nj); - multiplyGF16Matrices(p, Right[mi][a][nj], pTemp); - addGF16Matrices(sumTemp, pTemp, sumTemp); + p = getPMatrix(map1, p22, miPrime, ni, nj); + GF16Utils.gf16mMulTo(p, Right[mi][a][nj], temp, l); } - // hashMatrix += Left[mi][a][ni] * sumTemp - multiplyGF16Matrices(Left[mi][a][ni], sumTemp, temp); - addGF16Matrices(hashMatrix[mi], temp, hashMatrix[mi]); + // hashMatrix += Left[mi][a][ni] * temp + GF16Utils.gf16mMulTo(Left[mi][a][ni], temp, 0, hashMatrix, mi * lsq, l); } } } @@ -518,64 +451,6 @@ private byte[] getPMatrix(MapGroup1 map1, byte[][][][] p22, int mi, int ni, int } } - private void transposeGF16Matrix(byte[][] src, byte[] dest) - { - for (int i = 0; i < params.getL(); i++) - { - for (int j = 0; j < params.getL(); j++) - { - engine.setGF16m(dest, i, j, src[j][i]); - } - } - } - - private void transposeGF16Matrix(byte[] src, byte[] dest) - { - for (int i = 0; i < params.getL(); i++) - { - for (int j = 0; j < params.getL(); j++) - { - engine.setGF16m(dest, i, j, engine.getGF16m(src, j, i)); - } - } - } - - private void multiplyGF16Matrices(byte[] a, byte[] b, byte[] result) - { - Arrays.fill(result, (byte)0); - for (int i = 0; i < params.getL(); i++) - { - for (int j = 0; j < params.getL(); j++) - { - byte sum = 0; - for (int k = 0; k < params.getL(); k++) - { - sum ^= GF16Utils.mul(engine.getGF16m(a, i, k), engine.getGF16m(b, k, j)); - } - engine.setGF16m(result, i, j, sum); - } - } - } - - private void multiplyGF16Matrices(byte[] a, byte[][] b, byte[] result) - { - Arrays.fill(result, (byte)0); - for (int i = 0; i < params.getL(); i++) - { - for (int j = 0; j < params.getL(); j++) - { - byte sum = 0; - for (int k = 0; k < params.getL(); k++) - { - sum ^= GF16Utils.mul( - engine.getGF16m(a, i, k), - b[k][j]); - } - engine.setGF16m(result, i, j, sum); - } - } - } - private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size) { final int cols = size + 1; @@ -604,10 +479,10 @@ private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size } // Normalize pivot row - byte invPivot = GF16Utils.inv(Gauss[i][i]); + byte invPivot = GF16.inv(Gauss[i][i]); for (int j = i; j < cols; j++) { - Gauss[i][j] = GF16Utils.mul(Gauss[i][j], invPivot); + Gauss[i][j] = GF16.mul(Gauss[i][j], invPivot); } // Eliminate below @@ -618,7 +493,7 @@ private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size { for (int k = i; k < cols; k++) { - Gauss[j][k] ^= GF16Utils.mul(Gauss[i][k], factor); + Gauss[j][k] ^= GF16.mul(Gauss[i][k], factor); } } } @@ -630,49 +505,26 @@ private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size solution[i] = Gauss[i][size]; for (int j = i + 1; j < size; j++) { - solution[i] ^= GF16Utils.mul(Gauss[i][j], solution[j]); + solution[i] ^= GF16.mul(Gauss[i][j], solution[j]); } } return 0; } - private void addGF16Matrices(byte[] a, byte[] b, byte[] result) - { - for (int i = 0; i < params.getL(); i++) - { - for (int j = 0; j < params.getL(); ++j) - { - engine.setGF16m(result, i, j, (byte)(engine.getGF16m(a, i, j) ^ engine.getGF16m(b, i, j))); - } - } - } - - private void addGF16Matrices(byte[][] a, byte[] b, byte[][] result) - { - for (int i = 0; i < params.getL(); i++) - { - for (int j = 0; j < params.getL(); ++j) - { - result[i][j] = (byte)(a[i][j] ^ engine.getGF16m(b, i, j)); - } - } - } - private int iPrime(int mi, int alpha) { // Implement index calculation based on SNOVA specification return (mi + alpha) % params.getO(); } - static int fill(byte[] input, int inOff, byte[][] output, int len) + static void fill(byte[] input, byte[][] output, int len) { int rlt = 0; for (int i = 0; i < output.length; ++i) { int tmp = Math.min(output[i].length, len - rlt); - System.arraycopy(input, inOff + rlt, output[i], 0, tmp); + System.arraycopy(input, rlt, output[i], 0, tmp); rlt += tmp; } - return rlt; } } diff --git a/core/src/main/java/org/bouncycastle/util/GF16.java b/core/src/main/java/org/bouncycastle/util/GF16.java new file mode 100644 index 0000000000..de0fb22cd3 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/util/GF16.java @@ -0,0 +1,165 @@ +package org.bouncycastle.util; + +public class GF16 +{ + private static final byte[] F_STAR = {1, 2, 4, 8, 3, 6, 12, 11, 5, 10, 7, 14, 15, 13, 9}; + private static final byte[] MT4B = new byte[256]; + private static final byte[] INV4B = new byte[16]; + + static byte mt(int p, int q) + { + return MT4B[((p) << 4) ^ (q)]; + } + + static + { + // Initialize multiplication table + for (int i = 0; i < 15; i++) + { + for (int j = 0; j < 15; j++) + { + MT4B[(F_STAR[i] << 4) ^ F_STAR[j]] = F_STAR[(i + j) % 15]; + } + } + + int g = F_STAR[1], g_inv = F_STAR[14], gn = 1, gn_inv = 1; + // Initialize inversion table + INV4B[0] = 0; + INV4B[1] = 1; + for (int i = 0; i < 14; i++) + { + gn = mt(gn, g); + gn_inv = mt(gn_inv, g_inv); + INV4B[gn] = (byte)gn_inv; + } + } + + /** + * GF(16) multiplication mod x^4 + x + 1. + *

    + * This method multiplies two elements in GF(16) (represented as integers 0–15) + * using carryless multiplication followed by reduction modulo x^4 + x + 1. + * Please ensure a<=0x0F and b<=0x0F + * + * @param a an element in GF(16) (only the lower 4 bits are used) + * @param b an element in GF(16) (only the lower 4 bits are used) + * @return the product a * b in GF(16) + */ + public static byte mul(byte a, byte b) + { + return MT4B[a << 4 | b]; + } + + /** + * GF(16) multiplication mod x^4 + x + 1. + *

    + * This method multiplies two elements in GF(16) (represented as integers 0–15) + * using carryless multiplication followed by reduction modulo x^4 + x + 1. + * Please ensure a<=0x0F and b<=0x0F + * + * @param a an element in GF(16) (only the lower 4 bits are used) + * @param b an element in GF(16) (only the lower 4 bits are used) + * @return the product a * b in GF(16) + */ + public static int mul(int a, int b) + { + return MT4B[a << 4 | b]; + } + + public static byte inv(byte a) + { + return INV4B[a & 0xF]; + } + + /** + * Decodes an encoded byte array. + * Each byte in the input contains two nibbles (4-bit values); the lower nibble is stored first, + * followed by the upper nibble. + * + * @param m the input byte array (each byte holds two 4-bit values) + * @param mdec the output array that will hold the decoded nibbles (one per byte) + * @param mdecLen the total number of nibbles to decode + */ + public static void decode(byte[] m, byte[] mdec, int mdecLen) + { + int i, decIndex = 0, blocks = mdecLen >> 1; + // Process pairs of nibbles from each byte + for (i = 0; i < blocks; i++) + { + // Extract the lower nibble + mdec[decIndex++] = (byte)(m[i] & 0x0F); + // Extract the upper nibble (shift right 4 bits) + mdec[decIndex++] = (byte)((m[i] >> 4) & 0x0F); + } + // If there is an extra nibble (odd number of nibbles), decode only the lower nibble + if ((mdecLen & 1) == 1) + { + mdec[decIndex] = (byte)(m[i] & 0x0F); + } + } + + public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mdecLen) + { + // Process pairs of nibbles from each byte + int blocks = mdecLen >> 1; + for (int i = 0; i < blocks; i++) + { + // Extract the lower nibble + mdec[decIndex++] = (byte)(m[mOff] & 0x0F); + // Extract the upper nibble (shift right 4 bits) + mdec[decIndex++] = (byte)((m[mOff++] >> 4) & 0x0F); + } + // If there is an extra nibble (odd number of nibbles), decode only the lower nibble + if ((mdecLen & 1) == 1) + { + mdec[decIndex] = (byte)(m[mOff] & 0x0F); + } + } + + /** + * Encodes an array of 4-bit values into a byte array. + * Two 4-bit values are packed into one byte, with the first nibble stored in the lower 4 bits + * and the second nibble stored in the upper 4 bits. + * + * @param m the input array of 4-bit values (stored as bytes, only lower 4 bits used) + * @param menc the output byte array that will hold the encoded bytes + * @param mlen the number of nibbles in the input array + */ + public static void encode(byte[] m, byte[] menc, int mlen) + { + int i, srcIndex = 0; + // Process pairs of 4-bit values + for (i = 0; i < mlen / 2; i++) + { + int lowerNibble = m[srcIndex] & 0x0F; + int upperNibble = (m[srcIndex + 1] & 0x0F) << 4; + menc[i] = (byte)(lowerNibble | upperNibble); + srcIndex += 2; + } + // If there is an extra nibble (odd number of nibbles), store it directly in lower 4 bits. + if ((mlen & 1) == 1) + { + menc[i] = (byte)(m[srcIndex] & 0x0F); + } + } + + public static byte innerProduct(byte[] a, int aOff, byte[] b, int bOff, int rank) + { + byte result = 0; + for (int k = 0; k < rank; ++k, bOff += rank) + { + result ^= mul(a[aOff++], b[bOff]); + } + return result; + } + + public static byte dotProduct(byte[] a, int aOff, byte[] b, int bOff, int rank) + { + byte result = 0; + for (int k = 0; k < rank; ++k, aOff += rank, bOff += rank) + { + result ^= mul(a[aOff], b[bOff]); + } + return result; + } +} From 1bb0a7653da44c767d843386a2b4a13b53b5ee4e Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 1 Apr 2025 12:51:59 +1030 Subject: [PATCH 288/890] Try to refactor MapGroup1. --- .../pqc/crypto/snova/MapGroup1.java | 114 +++++++++++++++--- .../pqc/crypto/snova/SnovaEngine.java | 32 ++--- .../pqc/crypto/snova/SnovaSigner.java | 44 ++++--- .../main/java/org/bouncycastle/util/GF16.java | 54 ++++----- 4 files changed, 158 insertions(+), 86 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java index c1a2c37d12..5ee8626de1 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -1,5 +1,7 @@ package org.bouncycastle.pqc.crypto.snova; +import org.bouncycastle.util.GF16; + class MapGroup1 { public final byte[][][][] p11; // [m][v][v] @@ -26,6 +28,102 @@ public MapGroup1(SnovaParameters params) qAlpha2 = new byte[m][alpha][lsq]; } + public void decode(byte[] input, SnovaParameters params, int len) + { +// int m = params.getM(); +// int v = params.getV(); +// int o = params.getO(); +// int alpha = params.getAlpha(); + int lsq = params.getLsq(); + if ((lsq & 1) == 0) + { + int inOff = decodeP(input, 0, p11, len); + inOff += decodeP(input, inOff, p12, len - inOff); + inOff += decodeP(input, inOff, p21, len - inOff); + inOff += decodeAlpha(input, inOff, aAlpha, len - inOff); + inOff += decodeAlpha(input, inOff, bAlpha, len - inOff); + inOff += decodeAlpha(input, inOff, qAlpha1, len - inOff); + decodeAlpha(input, inOff, qAlpha2, len - inOff); + } +// else +// { +// +// } + } + +// public boolean decodeArrayLsqOdd(byte[] input, int inOff, boolean isLower, byte[] output, int lsqHalf) +// { +// int outOff = 0; +// if (isLower) +// { +// for (int i = 0; i < lsqHalf; ++i) +// { +// output[outOff++] = (byte)(input[inOff] & 0x0F); +// output[outOff++] = (byte)((input[inOff++] >>> 4) & 0x0F); +// } +// output[outOff] = (byte)(input[inOff] & 0x0F); +// return false; +// } +// else +// { +// for (int i = 0; i < lsqHalf; ++i) +// { +// output[outOff++] = (byte)((input[inOff++] >>> 4) & 0x0F); +// output[outOff++] = (byte)(input[inOff] & 0x0F); +// } +// output[outOff] = (byte)((input[inOff] >>> 4) & 0x0F); +// return true; +// } +// } + + private int decodeP(byte[] input, int inOff, byte[][][][] p, int len) + { + int rlt = 0; + for (int i = 0; i < p.length; ++i) + { + rlt += decodeAlpha(input, inOff + rlt, p[i], len); + } + return rlt; + } + + private static int decodeAlpha(byte[] input, int inOff, byte[][][] alpha, int len) + { + int rlt = 0; + for (int i = 0; i < alpha.length; ++i) + { + for (int j = 0; j < alpha[i].length; ++j) + { + int tmp = Math.min(alpha[i][j].length, len << 1); + GF16.decode(input, inOff + rlt, alpha[i][j], 0, tmp); + rlt += (tmp + 1) >> 1; + len -= (tmp + 1) >> 1; + } + } + return rlt; + } + +// private int decodeP(byte[] input, int inOff, boolean isLower,byte[][][][] p, int lsqHalf) +// { +// for (int i = 0; i < p.length; ++i) +// { +// inOff = decodeAlpha(input, inOff, p[i]); +// } +// return inOff; +// } + +// private boolean decodeAlpha(byte[] input, int inOff, boolean isLower, byte[][][] alpha, int lsqHalf) +// { +// for (int i = 0; i < alpha.length; ++i) +// { +// for (int j = 0; j < alpha[i].length; ++j) +// { +// isLower = decodeArrayLsqOdd(input, inOff, isLower, alpha[i][j], lsqHalf); +// inOff += lsqHalf + (isLower ? 1 : 0); +// } +// } +// return isLower; +// } + public void fill(byte[] input) { int inOff = fillP(input, 0, p11, input.length); @@ -61,20 +159,4 @@ static int fillAlpha(byte[] input, int inOff, byte[][][] alpha, int len) } return rlt; } - - static void copyTo(byte[][][][] alpha, byte[] output) - { - int outOff = 0; - for (int i = 0; i < alpha.length; ++i) - { - for (int j = 0; j < alpha[i].length; ++j) - { - for (int k = 0; k < alpha[i][j].length; ++k) - { - System.arraycopy(alpha[i][j][k], 0, output, outOff, alpha[i][j][k].length); - outOff += alpha[i][j][k].length; - } - } - } - } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 28a4bea37a..a5fd6738d9 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -11,7 +11,7 @@ import org.bouncycastle.util.GF16; import org.bouncycastle.util.Pack; -public class SnovaEngine +class SnovaEngine { private final SnovaParameters params; private final int l; @@ -517,38 +517,32 @@ void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) System.arraycopy(blockOut, 0, prngOutput, offset, remaining); } } +// if ((lsq & 1) == 0) +// { +// map1.decode(prngOutput, params, (gf16sPrngPublic - qTemp.length) >> 1); +// } +// else +// { byte[] temp = new byte[gf16sPrngPublic - qTemp.length]; GF16.decode(prngOutput, temp, temp.length); map1.fill(temp); +// } if (l >= 4) { - GF16.decode(prngOutput, temp.length >> 1, qTemp, 0, qTemp.length); - + GF16.decode(prngOutput, (gf16sPrngPublic - qTemp.length) >> 1, qTemp, 0, qTemp.length); + int ptArray = 0; // Post-processing for invertible matrices + int offset = m * alpha * l; for (int pi = 0; pi < m; ++pi) { for (int a = 0; a < alpha; ++a) { makeInvertibleByAddingAS(map1.aAlpha[pi][a], 0); makeInvertibleByAddingAS(map1.bAlpha[pi][a], 0); - } - } - - int ptArray = 0; - for (int pi = 0; pi < m; ++pi) - { - for (int a = 0; a < alpha; ++a) - { genAFqS(qTemp, ptArray, map1.qAlpha1[pi][a], 0); ptArray += l; - } - } - for (int pi = 0; pi < m; ++pi) - { - for (int a = 0; a < alpha; ++a) - { - genAFqS(qTemp, ptArray, map1.qAlpha2[pi][a], 0); - ptArray += l; + genAFqS(qTemp, offset, map1.qAlpha2[pi][a], 0); + offset += l; } } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index a8be7d6c4e..26713a1799 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -134,13 +134,15 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, final int v = params.getV(); final int o = params.getO(); final int n = params.getN(); - final int bytesHash = (o * lsq + 1) >>> 1; + final int mxlsq = m * lsq; + final int oxlsq = o * lsq; + final int bytesHash = (oxlsq + 1) >>> 1; final int bytesSalt = 16; // Initialize matrices and arrays - byte[][] Gauss = new byte[m * lsq][m * lsq + 1]; + byte[][] Gauss = new byte[mxlsq][mxlsq + 1]; byte[][] Temp = new byte[lsq][lsq]; - byte[] solution = new byte[m * lsq]; + byte[] solution = new byte[mxlsq]; byte[][][][] Left = new byte[m][alpha][v][lsq]; byte[][][][] Right = new byte[m][alpha][v][lsq]; @@ -148,7 +150,7 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, byte[] rightXtmp = new byte[lsq]; byte[][] XInGF16Matrix = new byte[v][lsq]; byte[][] FvvGF16Matrix = new byte[m][lsq]; - byte[] hashInGF16 = new byte[m * lsq]; + byte[] hashInGF16 = new byte[mxlsq]; byte[] signedHash = new byte[bytesHash]; byte[] vinegarBytes = new byte[(v * lsq + 1) / 2]; @@ -174,9 +176,9 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, numSign++; // Fill last column of Gauss matrix - for (int i = 0; i < m * lsq; i++) + for (int i = 0; i < mxlsq; i++) { - Gauss[i][m * lsq] = hashInGF16[i]; + Gauss[i][mxlsq] = hashInGF16[i]; } // Generate vinegar values @@ -227,24 +229,22 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, } } - int idx2 = m * lsq; // Gaussian elimination setup - for (int i = 0; i < m; i++) + for (int i = 0, ixlsq = 0; i < m; i++, ixlsq += lsq) { - for (int j = 0; j < l; j++) + for (int j = 0, jxl = 0; j < l; j++, jxl += l) { - for (int k = 0; k < l; k++) + for (int k = 0, jxl_k = jxl; k < l; k++, jxl_k++) { - int idx1 = i * lsq + j * l + k; - Gauss[idx1][idx2] ^= FvvGF16Matrix[i][j * l + k]; + Gauss[ixlsq + jxl_k][mxlsq] ^= FvvGF16Matrix[i][jxl_k]; } } } // Compute the coefficients of Xo and put into Gauss matrix and compute the coefficients of Xo^t and add into Gauss matrix - for (int mi = 0; mi < m; ++mi) + for (int mi = 0, mixlsq = 0; mi < m; ++mi, mixlsq += lsq) { - for (int index = 0; index < o; ++index) + for (int index = 0, idxlsq = 0; index < o; ++index, idxlsq += lsq) { for (int a = 0; a < alpha; ++a) { @@ -297,32 +297,30 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, { for (int tj = 0; tj < lsq; ++tj) { - int gaussRow = mi * lsq + ti; - int gaussCol = index * lsq + tj; - Gauss[gaussRow][gaussCol] ^= Temp[ti][tj]; + Gauss[mixlsq + ti][idxlsq + tj] ^= Temp[ti][tj]; } } } } } // Gaussian elimination implementation - flagRedo = performGaussianElimination(Gauss, solution, m * lsq); + flagRedo = performGaussianElimination(Gauss, solution, mxlsq); } while (flagRedo != 0); // Copy vinegar variables byte[] tmp = new byte[n * lsq]; - for (int idx = 0; idx < v; idx++) + for (int idx = 0, idxlsq = 0; idx < v; idx++, idxlsq += lsq) { - System.arraycopy(XInGF16Matrix[idx], 0, tmp, idx * lsq, lsq); - for (int i = 0; i < o; i++) + System.arraycopy(XInGF16Matrix[idx], 0, tmp, idxlsq, lsq); + for (int i = 0, ixlsq = 0; i < o; i++, ixlsq += lsq) { - GF16Utils.gf16mMulTo(T12[idx][i], solution, i * lsq, tmp, idx * lsq, l); + GF16Utils.gf16mMulTo(T12[idx][i], solution, ixlsq, tmp, idxlsq, l); } } // Copy remaining oil variables - System.arraycopy(solution, 0, tmp, v * lsq, lsq * o); + System.arraycopy(solution, 0, tmp, v * lsq, oxlsq); GF16.encode(tmp, ptSignature, tmp.length); System.arraycopy(arraySalt, 0, ptSignature, ptSignature.length - bytesSalt, bytesSalt); diff --git a/core/src/main/java/org/bouncycastle/util/GF16.java b/core/src/main/java/org/bouncycastle/util/GF16.java index de0fb22cd3..eb60a18f60 100644 --- a/core/src/main/java/org/bouncycastle/util/GF16.java +++ b/core/src/main/java/org/bouncycastle/util/GF16.java @@ -24,7 +24,6 @@ static byte mt(int p, int q) int g = F_STAR[1], g_inv = F_STAR[14], gn = 1, gn_inv = 1; // Initialize inversion table - INV4B[0] = 0; INV4B[1] = 1; for (int i = 0; i < 14; i++) { @@ -76,43 +75,43 @@ public static byte inv(byte a) * Each byte in the input contains two nibbles (4-bit values); the lower nibble is stored first, * followed by the upper nibble. * - * @param m the input byte array (each byte holds two 4-bit values) - * @param mdec the output array that will hold the decoded nibbles (one per byte) - * @param mdecLen the total number of nibbles to decode + * @param input the input byte array (each byte holds two 4-bit values) + * @param output the output array that will hold the decoded nibbles (one per byte) + * @param outputLen the total number of nibbles to decode */ - public static void decode(byte[] m, byte[] mdec, int mdecLen) + public static void decode(byte[] input, byte[] output, int outputLen) { - int i, decIndex = 0, blocks = mdecLen >> 1; + int i, decIndex = 0, blocks = outputLen >> 1; // Process pairs of nibbles from each byte for (i = 0; i < blocks; i++) { // Extract the lower nibble - mdec[decIndex++] = (byte)(m[i] & 0x0F); + output[decIndex++] = (byte)(input[i] & 0x0F); // Extract the upper nibble (shift right 4 bits) - mdec[decIndex++] = (byte)((m[i] >> 4) & 0x0F); + output[decIndex++] = (byte)((input[i] >>> 4) & 0x0F); } // If there is an extra nibble (odd number of nibbles), decode only the lower nibble - if ((mdecLen & 1) == 1) + if ((outputLen & 1) == 1) { - mdec[decIndex] = (byte)(m[i] & 0x0F); + output[decIndex] = (byte)(input[i] & 0x0F); } } - public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mdecLen) + public static void decode(byte[] input, int inOff, byte[] output, int outOff, int outputLen) { // Process pairs of nibbles from each byte - int blocks = mdecLen >> 1; + int blocks = outputLen >> 1; for (int i = 0; i < blocks; i++) { // Extract the lower nibble - mdec[decIndex++] = (byte)(m[mOff] & 0x0F); + output[outOff++] = (byte)(input[inOff] & 0x0F); // Extract the upper nibble (shift right 4 bits) - mdec[decIndex++] = (byte)((m[mOff++] >> 4) & 0x0F); + output[outOff++] = (byte)((input[inOff++] >>> 4) & 0x0F); } // If there is an extra nibble (odd number of nibbles), decode only the lower nibble - if ((mdecLen & 1) == 1) + if ((outputLen & 1) == 1) { - mdec[decIndex] = (byte)(m[mOff] & 0x0F); + output[outOff] = (byte)(input[inOff] & 0x0F); } } @@ -121,25 +120,24 @@ public static void decode(byte[] m, int mOff, byte[] mdec, int decIndex, int mde * Two 4-bit values are packed into one byte, with the first nibble stored in the lower 4 bits * and the second nibble stored in the upper 4 bits. * - * @param m the input array of 4-bit values (stored as bytes, only lower 4 bits used) - * @param menc the output byte array that will hold the encoded bytes - * @param mlen the number of nibbles in the input array + * @param input the input array of 4-bit values (stored as bytes, only lower 4 bits used) + * @param output the output byte array that will hold the encoded bytes + * @param outputLen the number of nibbles in the input array */ - public static void encode(byte[] m, byte[] menc, int mlen) + public static void encode(byte[] input, byte[] output, int outputLen) { - int i, srcIndex = 0; + int i, inOff = 0; // Process pairs of 4-bit values - for (i = 0; i < mlen / 2; i++) + for (i = 0; i < outputLen / 2; i++) { - int lowerNibble = m[srcIndex] & 0x0F; - int upperNibble = (m[srcIndex + 1] & 0x0F) << 4; - menc[i] = (byte)(lowerNibble | upperNibble); - srcIndex += 2; + int lowerNibble = input[inOff++] & 0x0F; + int upperNibble = (input[inOff++] & 0x0F) << 4; + output[i] = (byte)(lowerNibble | upperNibble); } // If there is an extra nibble (odd number of nibbles), store it directly in lower 4 bits. - if ((mlen & 1) == 1) + if ((outputLen & 1) == 1) { - menc[i] = (byte)(m[srcIndex] & 0x0F); + output[i] = (byte)(input[inOff] & 0x0F); } } From 26783d5819480520a2edd6e8013a3ccd2bbebdf9 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 1 Apr 2025 15:05:48 +1030 Subject: [PATCH 289/890] Add more constant values. --- .../pqc/crypto/snova/MapGroup1.java | 2 +- .../pqc/crypto/snova/SnovaEngine.java | 121 +++++------------- .../pqc/crypto/snova/SnovaKeyElements.java | 25 +--- .../crypto/snova/SnovaKeyPairGenerator.java | 2 +- .../pqc/crypto/snova/SnovaParameters.java | 82 +++++++++++- .../pqc/crypto/snova/SnovaSigner.java | 4 +- 6 files changed, 121 insertions(+), 115 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java index 5ee8626de1..9f3d990cd1 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -18,7 +18,7 @@ public MapGroup1(SnovaParameters params) int v = params.getV(); int o = params.getO(); int alpha = params.getAlpha(); - int lsq = params.getL() * params.getL(); + int lsq = params.getLsq(); p11 = new byte[m][v][v][lsq]; p12 = new byte[m][v][o][lsq]; p21 = new byte[m][o][v][lsq]; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index a5fd6738d9..8bee46f026 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -15,6 +15,12 @@ class SnovaEngine { private final SnovaParameters params; private final int l; + private final int lsq; + private final int m; + private final int v; + private final int o; + private final int alpha; + private final int n; final byte[][] S; final int[][] xS; @@ -22,29 +28,19 @@ public SnovaEngine(SnovaParameters params) { this.params = params; this.l = params.getL(); - int lsq = l * l; - S = new byte[l][lsq]; - xS = new int[l][lsq]; - be_aI(S[0], 0, (byte)1); - beTheS(S[1]); - for (int index = 2; index < l; ++index) - { - GF16Utils.gf16mMul(S[index - 1], S[1], S[index], l); - } - - for (int index = 0; index < l; ++index) - { - for (int ij = 0; ij < lsq; ++ij) - { - xS[index][ij] = GF16Utils.gf16FromNibble(S[index][ij]); - } - } + this.lsq = params.getLsq(); + this.m = params.getM(); + this.v = params.getV(); + this.o = params.getO(); + this.alpha = params.getAlpha(); + this.n = params.getN(); + S = SnovaParameters.sSet.get(l); + xS = SnovaParameters.xSSet.get(l); } - public void be_aI(byte[] target, int off, byte a) + static void be_aI(byte[] target, int off, byte a, int l) { -// // Mask 'a' to ensure it's a valid 4-bit GF16 element -// a = (byte)(a & 0x0F); + // Ensure 'a' iss a valid 4-bit GF16 element int l1 = l + 1; for (int i = 0; i < l; ++i, off += l1) { @@ -52,29 +48,9 @@ public void be_aI(byte[] target, int off, byte a) } } - private void beTheS(byte[] target) - { - // Set all elements to 8 - (i + j) in GF16 (4-bit values) - for (int i = 0, il = 0; i < l; ++i, il += l) - { - for (int j = 0; j < l; ++j) - { - int value = 8 - (i + j); - target[il + j] = (byte)(value & 0x0F); // Mask to 4 bits - } - } - - // Special case for rank 5 - if (l == 5) - { - target[24] = (byte)9; // Set (4,4) to 9 - } - } - // Constant-time GF16 matrix generation public void genAFqSCT(byte[] c, int cOff, byte[] ptMatrix) { - int lsq = l * l; int[] xTemp = new int[lsq]; int l1 = l + 1; // Initialize diagonal with c[0] @@ -102,11 +78,6 @@ public void genAFqSCT(byte[] c, int cOff, byte[] ptMatrix) for (int ij = 0; ij < lsq; ij++) { xTemp[ij] ^= cX * xS[l - 1][ij]; - } - - // Convert to nibbles and clear temp - for (int ij = 0; ij < lsq; ij++) - { ptMatrix[ij] = GF16Utils.gf16ToNibble(xTemp[ij]); } Arrays.fill(xTemp, 0); // Secure clear @@ -298,7 +269,7 @@ private byte determinant5x5(byte[] m, int off) private void generateASMatrixTo(byte[] target, int off, byte a) { - for (int i = 0; i < l; i++) + for (int i = 0, ixl = off; i < l; i++, ixl += l) { for (int j = 0; j < l; j++) { @@ -307,7 +278,7 @@ private void generateASMatrixTo(byte[] target, int off, byte a) { coefficient = 9; } - target[i * l + j + off] ^= GF16.mul(coefficient, a); + target[ixl + j] ^= GF16.mul(coefficient, a); } } } @@ -315,7 +286,7 @@ private void generateASMatrixTo(byte[] target, int off, byte a) public void genAFqS(byte[] c, int cOff, byte[] ptMatrix, int off) { // Initialize with be_aI - be_aI(ptMatrix, off, c[cOff]); + be_aI(ptMatrix, off, c[cOff], l); // Process middle terms for (int i = 1; i < l - 1; ++i) @@ -341,12 +312,6 @@ private void gf16mScaleTo(byte[] a, byte k, byte[] c, int cOff) public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) { - int m = params.getM(); - int v = params.getV(); - int o = params.getO(); - int l = params.getL(); - int lsq = l * l; - // Copy initial matrices copy4DMatrix(map1.p11, map2.f11, m, v, v, lsq); copy4DMatrix(map1.p12, map2.f12, m, v, o, lsq); @@ -386,12 +351,6 @@ private static void copy4DMatrix(byte[][][][] src, byte[][][][] dest, int dim1, public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] F12) { - int m = params.getM(); - int o = params.getO(); - int v = params.getV(); - int l = params.getL(); - int lsq = l * l; - // Initialize P22 with zeros byte[] P22 = new byte[m * o * o * lsq]; @@ -420,8 +379,8 @@ public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] void genSeedsAndT12(byte[][][] T12, byte[] skSeed) { - int bytesPrngPrivate = (params.getV() * params.getO() * params.getL() + 1) >>> 1; - int gf16sPrngPrivate = params.getV() * params.getO() * params.getL(); + int bytesPrngPrivate = (v * o * l + 1) >>> 1; + int gf16sPrngPrivate = v * o * l; byte[] prngOutput = new byte[bytesPrngPrivate]; // Generate PRNG output using SHAKE-256 @@ -435,10 +394,9 @@ void genSeedsAndT12(byte[][][] T12, byte[] skSeed) // Generate T12 matrices int ptArray = 0; - int l = params.getL(); - for (int j = 0; j < params.getV(); j++) + for (int j = 0; j < v; j++) { - for (int k = 0; k < params.getO(); k++) + for (int k = 0; k < o; k++) { //gen_a_FqS_ct genAFqSCT(gf16PrngOutput, ptArray, T12[j][k]); @@ -449,14 +407,6 @@ void genSeedsAndT12(byte[][][] T12, byte[] skSeed) void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) { - int l = params.getL(); - int lsq = l * l; - int m = params.getM(); - int alpha = params.getAlpha(); - int v = params.getV(); - int o = params.getO(); - int n = v + o; - int gf16sPrngPublic = lsq * (2 * m * alpha + m * (n * n - m * m)) + l * 2 * m * alpha; byte[] qTemp = new byte[(m * alpha * lsq + m * alpha * lsq) / l]; byte[] prngOutput = new byte[(gf16sPrngPublic + 1) >> 1]; @@ -500,7 +450,6 @@ void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) ctrCipher.init(true, params); int blockSize = ctrCipher.getBlockSize(); // typically 16 bytes byte[] zeroBlock = new byte[blockSize]; // block of zeros - byte[] blockOut = new byte[blockSize]; int offset = 0; // Process full blocks @@ -512,21 +461,21 @@ void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) // Process any remaining partial block. if (offset < prngOutput.length) { - ctrCipher.processBlock(zeroBlock, 0, blockOut, 0); + ctrCipher.processBlock(zeroBlock, 0, zeroBlock, 0); int remaining = prngOutput.length - offset; - System.arraycopy(blockOut, 0, prngOutput, offset, remaining); + System.arraycopy(zeroBlock, 0, prngOutput, offset, remaining); } } -// if ((lsq & 1) == 0) -// { -// map1.decode(prngOutput, params, (gf16sPrngPublic - qTemp.length) >> 1); -// } -// else -// { - byte[] temp = new byte[gf16sPrngPublic - qTemp.length]; - GF16.decode(prngOutput, temp, temp.length); - map1.fill(temp); -// } + if ((lsq & 1) == 0) + { + map1.decode(prngOutput, params, (gf16sPrngPublic - qTemp.length) >> 1); + } + else + { + byte[] temp = new byte[gf16sPrngPublic - qTemp.length]; + GF16.decode(prngOutput, temp, temp.length); + map1.fill(temp); + } if (l >= 4) { GF16.decode(prngOutput, (gf16sPrngPublic - qTemp.length) >> 1, qTemp, 0, qTemp.length); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java index e084d5c17e..1f79794735 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java @@ -13,12 +13,11 @@ class SnovaKeyElements private final int length; byte[] fixedAbq; - public SnovaKeyElements(SnovaParameters params, SnovaEngine engine) + public SnovaKeyElements(SnovaParameters params) { int o = params.getO(); int l = params.getL(); int v = params.getV(); - int alpha = params.getAlpha(); int lsq = l * l; map1 = new MapGroup1(params); T12 = new byte[v][o][lsq]; @@ -27,27 +26,7 @@ public SnovaKeyElements(SnovaParameters params, SnovaEngine engine) length = o * params.getAlpha() * lsq * 4 + v * o * lsq + (o * v * v + o * v * o + o * o * v) * lsq; if (l < 4) { - fixedAbq = new byte[4 * o * alpha * lsq]; - //genABQ(byte[] abqSeed) - byte[] rngOut = new byte[o * alpha * (lsq + l)]; - byte[] q12 = new byte[2 * o * alpha * l]; - byte[] seed = "SNOVA_ABQ".getBytes(); - SHAKEDigest shake = new SHAKEDigest(256); - shake.update(seed, 0, seed.length); - shake.doFinal(rngOut, 0, rngOut.length); - GF16.decode(rngOut, fixedAbq, 2 * o * alpha * lsq); - GF16.decode(rngOut, alpha * lsq, q12, 0, 2 * o * alpha * l); - // Post-processing for invertible matrices - for (int pi = 0; pi < o; ++pi) - { - for (int a = 0; a < alpha; ++a) - { - engine.makeInvertibleByAddingAS(fixedAbq, (pi * alpha + a) * lsq); - engine.makeInvertibleByAddingAS(fixedAbq, ((o + pi) * alpha + a) * lsq); - engine.genAFqS(q12, (pi * alpha + a) * l, fixedAbq, ((2 * o + pi) * alpha + a) * lsq); - engine.genAFqS(q12, ((o + pi) * alpha + a) * l, fixedAbq, ((3 * o + pi) * alpha + a) * lsq); - } - } + fixedAbq = SnovaParameters.fixedAbqSet.get(o); } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index b7244dced8..0c4606b5db 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -46,7 +46,7 @@ public AsymmetricCipherKeyPair generateKeyPair() byte[] ptPublicKeySeed = Arrays.copyOfRange(seedPair, 0, publicSeedLength); byte[] ptPrivateKeySeed = Arrays.copyOfRange(seedPair, publicSeedLength, seedPair.length); - SnovaKeyElements keyElements = new SnovaKeyElements(params, engine); + SnovaKeyElements keyElements = new SnovaKeyElements(params); generateKeysCore(keyElements, ptPublicKeySeed, ptPrivateKeySeed); // Pack public key components diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java index 468be6070a..cfaa36a8d3 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java @@ -1,7 +1,17 @@ package org.bouncycastle.pqc.crypto.snova; +import java.util.HashMap; +import java.util.Map; + +import org.bouncycastle.crypto.digests.SHAKEDigest; +import org.bouncycastle.util.GF16; + public class SnovaParameters { + static Map fixedAbqSet = new HashMap();//key is o + static Map sSet = new HashMap(); //key is l + static Map xSSet = new HashMap(); + public static final SnovaParameters SNOVA_24_5_16_4_SSK = new SnovaParameters("SNOVA_24_5_16_4_SSK", 24, 5, 4, true, false); public static final SnovaParameters SNOVA_24_5_16_4_ESK = @@ -111,6 +121,7 @@ public class SnovaParameters private final int v; private final int o; private final int l; + private final int lsq; private final int alpha; private final boolean skIsSeed; private final boolean pkExpandShake; @@ -121,9 +132,57 @@ public SnovaParameters(String name, int v, int o, int l, boolean skIsSeed, boole this.v = v; this.o = o; this.l = l; - this.alpha = l * l + l; + this.lsq = l * l; + this.alpha = lsq + l; this.skIsSeed = skIsSeed; this.pkExpandShake = pkExpandShake; + if (!xSSet.containsKey(l)) + { + byte[][] S = new byte[l][lsq]; + int[][] xS = new int[l][lsq]; + SnovaEngine.be_aI(S[0], 0, (byte)1, l); + beTheS(S[1]); + for (int index = 2; index < l; ++index) + { + GF16Utils.gf16mMul(S[index - 1], S[1], S[index], l); + } + + for (int index = 0; index < l; ++index) + { + for (int ij = 0; ij < lsq; ++ij) + { + xS[index][ij] = GF16Utils.gf16FromNibble(S[index][ij]); + } + } + sSet.put(l, S); + xSSet.put(l, xS); + } + if (l < 4 && !fixedAbqSet.containsKey(o)) + { + SnovaEngine engine = new SnovaEngine(this); + byte[] fixedAbq = new byte[4 * o * alpha * lsq]; + //genABQ(byte[] abqSeed) + byte[] rngOut = new byte[o * alpha * (lsq + l)]; + byte[] q12 = new byte[2 * o * alpha * l]; + byte[] seed = "SNOVA_ABQ".getBytes(); + SHAKEDigest shake = new SHAKEDigest(256); + shake.update(seed, 0, seed.length); + shake.doFinal(rngOut, 0, rngOut.length); + GF16.decode(rngOut, fixedAbq, 2 * o * alpha * lsq); + GF16.decode(rngOut, alpha * lsq, q12, 0, 2 * o * alpha * l); + // Post-processing for invertible matrices + for (int pi = 0; pi < o; ++pi) + { + for (int a = 0; a < alpha; ++a) + { + engine.makeInvertibleByAddingAS(fixedAbq, (pi * alpha + a) * lsq); + engine.makeInvertibleByAddingAS(fixedAbq, ((o + pi) * alpha + a) * lsq); + engine.genAFqS(q12, (pi * alpha + a) * l, fixedAbq, ((2 * o + pi) * alpha + a) * lsq); + engine.genAFqS(q12, ((o + pi) * alpha + a) * l, fixedAbq, ((3 * o + pi) * alpha + a) * lsq); + } + } + fixedAbqSet.put(o, fixedAbq); + } } // Getter methods @@ -185,11 +244,30 @@ public int getN() public int getLsq() { - return l * l; + return lsq; } public int getSaltLength() { return 16; } + + void beTheS(byte[] target) + { + // Set all elements to 8 - (i + j) in GF16 (4-bit values) + for (int i = 0, il = 0; i < l; ++i, il += l) + { + for (int j = 0; j < l; ++j) + { + int value = 8 - (i + j); + target[il + j] = (byte)(value & 0x0F); // Mask to 4 bits + } + } + + // Special case for rank 5 + if (l == 5) + { + target[24] = (byte)9; // Set (4,4) to 9 + } + } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index 26713a1799..f9d81fa22d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -59,7 +59,7 @@ public byte[] generateSignature(byte[] message) byte[] salt = new byte[params.getSaltLength()]; random.nextBytes(salt); byte[] signature = new byte[((params.getN() * params.getLsq() + 1) >>> 1) + params.getSaltLength()]; - SnovaKeyElements keyElements = new SnovaKeyElements(params, engine); + SnovaKeyElements keyElements = new SnovaKeyElements(params); if (params.isSkIsSeed()) { byte[] seedPair = privKey.getPrivateKey(); @@ -88,7 +88,7 @@ public boolean verifySignature(byte[] message, byte[] signature) byte[] hash = new byte[shake.getDigestSize()]; shake.update(message, 0, message.length); shake.doFinal(hash, 0); - SnovaKeyElements keyElements = new SnovaKeyElements(params, engine); + SnovaKeyElements keyElements = new SnovaKeyElements(params); byte[] pk = pubKey.getEncoded(); System.arraycopy(pk, 0, keyElements.publicKey.publicKeySeed, 0, SnovaKeyPairGenerator.publicSeedLength); System.arraycopy(pk, SnovaKeyPairGenerator.publicSeedLength, keyElements.publicKey.P22, 0, keyElements.publicKey.P22.length); From a1544a849661a178eb23db57b91d03ad20b44e69 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 1 Apr 2025 16:55:46 +1030 Subject: [PATCH 290/890] Remove PublicKey class from Snova --- .../pqc/crypto/snova/MapGroup1.java | 26 +++--- .../pqc/crypto/snova/PublicKey.java | 14 ---- .../pqc/crypto/snova/SnovaEngine.java | 2 +- .../pqc/crypto/snova/SnovaKeyElements.java | 79 +++++++++---------- .../crypto/snova/SnovaKeyPairGenerator.java | 26 ++++-- .../pqc/crypto/snova/SnovaSigner.java | 49 +++++++++--- 6 files changed, 108 insertions(+), 88 deletions(-) delete mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java index 9f3d990cd1..e23a08de44 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -28,23 +28,23 @@ public MapGroup1(SnovaParameters params) qAlpha2 = new byte[m][alpha][lsq]; } - public void decode(byte[] input, SnovaParameters params, int len) + public void decode(byte[] input, int len) { // int m = params.getM(); // int v = params.getV(); // int o = params.getO(); // int alpha = params.getAlpha(); - int lsq = params.getLsq(); - if ((lsq & 1) == 0) - { - int inOff = decodeP(input, 0, p11, len); - inOff += decodeP(input, inOff, p12, len - inOff); - inOff += decodeP(input, inOff, p21, len - inOff); - inOff += decodeAlpha(input, inOff, aAlpha, len - inOff); - inOff += decodeAlpha(input, inOff, bAlpha, len - inOff); - inOff += decodeAlpha(input, inOff, qAlpha1, len - inOff); - decodeAlpha(input, inOff, qAlpha2, len - inOff); - } +// int lsq = params.getLsq(); +// if ((lsq & 1) == 0) +// { + int inOff = decodeP(input, 0, p11, len); + inOff += decodeP(input, inOff, p12, len - inOff); + inOff += decodeP(input, inOff, p21, len - inOff); + inOff += decodeAlpha(input, inOff, aAlpha, len - inOff); + inOff += decodeAlpha(input, inOff, bAlpha, len - inOff); + inOff += decodeAlpha(input, inOff, qAlpha1, len - inOff); + decodeAlpha(input, inOff, qAlpha2, len - inOff); +// } // else // { // @@ -76,7 +76,7 @@ public void decode(byte[] input, SnovaParameters params, int len) // } // } - private int decodeP(byte[] input, int inOff, byte[][][][] p, int len) + static int decodeP(byte[] input, int inOff, byte[][][][] p, int len) { int rlt = 0; for (int i = 0; i < p.length; ++i) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java deleted file mode 100644 index 74e2896dbb..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/PublicKey.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.bouncycastle.pqc.crypto.snova; - -class PublicKey -{ - public byte[] publicKeySeed; - public final byte[] P22; - - public PublicKey(SnovaParameters params) - { - publicKeySeed = new byte[SnovaKeyPairGenerator.publicSeedLength]; - P22 = new byte[(params.getM() * params.getO() * params.getO() * params.getL() * params.getL() + 1) >> 1]; - } -} - diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 8bee46f026..b96f05fcf9 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -468,7 +468,7 @@ void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) } if ((lsq & 1) == 0) { - map1.decode(prngOutput, params, (gf16sPrngPublic - qTemp.length) >> 1); + map1.decode(prngOutput, (gf16sPrngPublic - qTemp.length) >> 1); } else { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java index 1f79794735..31f803937b 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java @@ -1,16 +1,12 @@ package org.bouncycastle.pqc.crypto.snova; -import org.bouncycastle.crypto.digests.SHAKEDigest; -import org.bouncycastle.util.GF16; - class SnovaKeyElements { public final MapGroup1 map1; public final byte[][][] T12; // [v][o] public final MapGroup2 map2; - public final PublicKey publicKey; public byte[] ptPrivateKeySeed; - private final int length; + byte[] fixedAbq; public SnovaKeyElements(SnovaParameters params) @@ -22,48 +18,47 @@ public SnovaKeyElements(SnovaParameters params) map1 = new MapGroup1(params); T12 = new byte[v][o][lsq]; map2 = new MapGroup2(params); - publicKey = new PublicKey(params); - length = o * params.getAlpha() * lsq * 4 + v * o * lsq + (o * v * v + o * v * o + o * o * v) * lsq; + if (l < 4) { fixedAbq = SnovaParameters.fixedAbqSet.get(o); } } - public void encodeMergerInHalf(byte[] output) - { - byte[] input = new byte[length]; - int inOff = 0; - inOff = copy3d(map1.aAlpha, input, inOff); - inOff = copy3d(map1.bAlpha, input, inOff); - inOff = copy3d(map1.qAlpha1, input, inOff); - inOff = copy3d(map1.qAlpha2, input, inOff); - inOff = copy3d(T12, input, inOff); - inOff = copy4d(map2.f11, input, inOff); - inOff = copy4d(map2.f12, input, inOff); - copy4d(map2.f21, input, inOff); - GF16Utils.encodeMergeInHalf(input, length, output); - } +// public void encodeMergerInHalf(byte[] output) +// { +// byte[] input = new byte[length]; +// int inOff = 0; +// inOff = copy3d(map1.aAlpha, input, inOff); +// inOff = copy3d(map1.bAlpha, input, inOff); +// inOff = copy3d(map1.qAlpha1, input, inOff); +// inOff = copy3d(map1.qAlpha2, input, inOff); +// inOff = copy3d(T12, input, inOff); +// inOff = copy4d(map2.f11, input, inOff); +// inOff = copy4d(map2.f12, input, inOff); +// copy4d(map2.f21, input, inOff); +// GF16Utils.encodeMergeInHalf(input, length, output); +// } - public void skUnpack(byte[] input) - { - byte[] tmp = new byte[(input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength) << 1]; - GF16Utils.decodeMergeInHalf(input, tmp, tmp.length); - int inOff = 0; - inOff = copy3d(tmp, inOff, map1.aAlpha); - inOff = copy3d(tmp, inOff, map1.bAlpha); - inOff = copy3d(tmp, inOff, map1.qAlpha1); - inOff = copy3d(tmp, inOff, map1.qAlpha2); - inOff = copy3d(tmp, inOff, T12); - inOff = copy4d(tmp, inOff, map2.f11); - inOff = copy4d(tmp, inOff, map2.f12); - copy4d(tmp, inOff, map2.f21); - System.arraycopy(input, input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength, publicKey.publicKeySeed, 0, publicKey.publicKeySeed.length); - ptPrivateKeySeed = new byte[SnovaKeyPairGenerator.privateSeedLength]; - System.arraycopy(input, input.length - SnovaKeyPairGenerator.privateSeedLength, ptPrivateKeySeed, 0, ptPrivateKeySeed.length); - } +// public void skUnpack(byte[] input) +// { +// byte[] tmp = new byte[(input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength) << 1]; +// GF16Utils.decodeMergeInHalf(input, tmp, tmp.length); +// int inOff = 0; +// inOff = copy3d(tmp, inOff, map1.aAlpha); +// inOff = copy3d(tmp, inOff, map1.bAlpha); +// inOff = copy3d(tmp, inOff, map1.qAlpha1); +// inOff = copy3d(tmp, inOff, map1.qAlpha2); +// inOff = copy3d(tmp, inOff, T12); +// inOff = copy4d(tmp, inOff, map2.f11); +// inOff = copy4d(tmp, inOff, map2.f12); +// copy4d(tmp, inOff, map2.f21); +// System.arraycopy(input, input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength, publicKey.publicKeySeed, 0, publicKey.publicKeySeed.length); +// ptPrivateKeySeed = new byte[SnovaKeyPairGenerator.privateSeedLength]; +// System.arraycopy(input, input.length - SnovaKeyPairGenerator.privateSeedLength, ptPrivateKeySeed, 0, ptPrivateKeySeed.length); +// } - private int copy3d(byte[][][] alpha, byte[] output, int outOff) + static int copy3d(byte[][][] alpha, byte[] output, int outOff) { for (int i = 0; i < alpha.length; ++i) { @@ -76,7 +71,7 @@ private int copy3d(byte[][][] alpha, byte[] output, int outOff) return outOff; } - private int copy4d(byte[][][][] alpha, byte[] output, int outOff) + static int copy4d(byte[][][][] alpha, byte[] output, int outOff) { for (int i = 0; i < alpha.length; ++i) { @@ -85,7 +80,7 @@ private int copy4d(byte[][][][] alpha, byte[] output, int outOff) return outOff; } - private int copy3d(byte[] input, int inOff, byte[][][] alpha) + static int copy3d(byte[] input, int inOff, byte[][][] alpha) { for (int i = 0; i < alpha.length; ++i) { @@ -98,7 +93,7 @@ private int copy3d(byte[] input, int inOff, byte[][][] alpha) return inOff; } - private int copy4d(byte[] input, int inOff, byte[][][][] alpha) + static int copy4d(byte[] input, int inOff, byte[][][][] alpha) { for (int i = 0; i < alpha.length; ++i) { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index 0c4606b5db..2722d32e5a 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -47,11 +47,12 @@ public AsymmetricCipherKeyPair generateKeyPair() byte[] ptPrivateKeySeed = Arrays.copyOfRange(seedPair, publicSeedLength, seedPair.length); SnovaKeyElements keyElements = new SnovaKeyElements(params); - generateKeysCore(keyElements, ptPublicKeySeed, ptPrivateKeySeed); + byte[] p22 = new byte[(params.getM() * params.getO() * params.getO() * params.getL() * params.getL() + 1) >> 1]; + generateKeysCore(keyElements, ptPublicKeySeed, ptPrivateKeySeed, p22); // Pack public key components System.arraycopy(ptPublicKeySeed, 0, pk, 0, ptPublicKeySeed.length); - System.arraycopy(keyElements.publicKey.P22, 0, pk, ptPublicKeySeed.length, keyElements.publicKey.P22.length); + System.arraycopy(p22, 0, pk, ptPublicKeySeed.length, p22.length); if (params.isSkIsSeed()) { @@ -59,7 +60,22 @@ public AsymmetricCipherKeyPair generateKeyPair() } else { - keyElements.encodeMergerInHalf(sk); + int o = params.getO(); + int lsq = params.getLsq(); + int v = params.getV(); + int length = o * params.getAlpha() * lsq * 4 + v * o * lsq + (o * v * v + o * v * o + o * o * v) * lsq; + //keyElements.encodeMergerInHalf(sk); + byte[] input = new byte[length]; + int inOff = 0; + inOff = SnovaKeyElements.copy3d(keyElements.map1.aAlpha, input, inOff); + inOff = SnovaKeyElements.copy3d(keyElements.map1.bAlpha, input, inOff); + inOff = SnovaKeyElements.copy3d(keyElements.map1.qAlpha1, input, inOff); + inOff = SnovaKeyElements.copy3d(keyElements.map1.qAlpha2, input, inOff); + inOff = SnovaKeyElements.copy3d(keyElements.T12, input, inOff); + inOff = SnovaKeyElements.copy4d(keyElements.map2.f11, input, inOff); + inOff = SnovaKeyElements.copy4d(keyElements.map2.f12, input, inOff); + SnovaKeyElements.copy4d(keyElements.map2.f21, input, inOff); + GF16Utils.encodeMergeInHalf(input, length, sk); System.arraycopy(seedPair, 0, sk, sk.length - seedLength, seedLength); } @@ -69,7 +85,7 @@ public AsymmetricCipherKeyPair generateKeyPair() ); } - private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[] skSeed) + private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[] skSeed, byte[] p22) { // Generate T12 matrix engine.genSeedsAndT12(keyElements.T12, skSeed); @@ -81,6 +97,6 @@ private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[ engine.genF(keyElements.map2, keyElements.map1, keyElements.T12); // Generate P22 matrix - engine.genP22(keyElements.publicKey.P22, keyElements.T12, keyElements.map1.p21, keyElements.map2.f12); + engine.genP22(p22, keyElements.T12, keyElements.map1.p21, keyElements.map2.f12); } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index f9d81fa22d..e8ece88dbe 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -60,25 +60,40 @@ public byte[] generateSignature(byte[] message) random.nextBytes(salt); byte[] signature = new byte[((params.getN() * params.getLsq() + 1) >>> 1) + params.getSaltLength()]; SnovaKeyElements keyElements = new SnovaKeyElements(params); + byte[] publicKeySeed; if (params.isSkIsSeed()) { byte[] seedPair = privKey.getPrivateKey(); - keyElements.publicKey.publicKeySeed = Arrays.copyOfRange(seedPair, 0, SnovaKeyPairGenerator.publicSeedLength); + publicKeySeed = Arrays.copyOfRange(seedPair, 0, SnovaKeyPairGenerator.publicSeedLength); keyElements.ptPrivateKeySeed = Arrays.copyOfRange(seedPair, SnovaKeyPairGenerator.publicSeedLength, seedPair.length); engine.genSeedsAndT12(keyElements.T12, keyElements.ptPrivateKeySeed); // Generate map components - engine.genABQP(keyElements.map1, keyElements.publicKey.publicKeySeed, keyElements.fixedAbq); + engine.genABQP(keyElements.map1, publicKeySeed, keyElements.fixedAbq); // Generate F matrices engine.genF(keyElements.map2, keyElements.map1, keyElements.T12); } else { - keyElements.skUnpack(privKey.getPrivateKey()); + byte[] input = privKey.getPrivateKey(); + byte[] tmp = new byte[(input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength) << 1]; + GF16Utils.decodeMergeInHalf(input, tmp, tmp.length); + int inOff = 0; + inOff = SnovaKeyElements.copy3d(tmp, inOff, keyElements.map1.aAlpha); + inOff = SnovaKeyElements.copy3d(tmp, inOff, keyElements.map1.bAlpha); + inOff = SnovaKeyElements.copy3d(tmp, inOff, keyElements.map1.qAlpha1); + inOff = SnovaKeyElements.copy3d(tmp, inOff, keyElements.map1.qAlpha2); + inOff = SnovaKeyElements.copy3d(tmp, inOff, keyElements.T12); + inOff = SnovaKeyElements.copy4d(tmp, inOff, keyElements.map2.f11); + inOff = SnovaKeyElements.copy4d(tmp, inOff, keyElements.map2.f12); + SnovaKeyElements.copy4d(tmp, inOff, keyElements.map2.f21); + publicKeySeed = Arrays.copyOfRange(input, input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength, input.length - SnovaKeyPairGenerator.privateSeedLength); + keyElements.ptPrivateKeySeed = new byte[SnovaKeyPairGenerator.privateSeedLength]; + System.arraycopy(input, input.length - SnovaKeyPairGenerator.privateSeedLength, keyElements.ptPrivateKeySeed, 0, keyElements.ptPrivateKeySeed.length); } signDigestCore(signature, hash, salt, keyElements.map1.aAlpha, keyElements.map1.bAlpha, keyElements.map1.qAlpha1, keyElements.map1.qAlpha2, - keyElements.T12, keyElements.map2.f11, keyElements.map2.f12, keyElements.map2.f21, keyElements.publicKey.publicKeySeed, keyElements.ptPrivateKeySeed); + keyElements.T12, keyElements.map2.f11, keyElements.map2.f12, keyElements.map2.f21, publicKeySeed, keyElements.ptPrivateKeySeed); return Arrays.concatenate(signature, message); } @@ -90,14 +105,22 @@ public boolean verifySignature(byte[] message, byte[] signature) shake.doFinal(hash, 0); SnovaKeyElements keyElements = new SnovaKeyElements(params); byte[] pk = pubKey.getEncoded(); - System.arraycopy(pk, 0, keyElements.publicKey.publicKeySeed, 0, SnovaKeyPairGenerator.publicSeedLength); - System.arraycopy(pk, SnovaKeyPairGenerator.publicSeedLength, keyElements.publicKey.P22, 0, keyElements.publicKey.P22.length); - engine.genABQP(keyElements.map1, keyElements.publicKey.publicKeySeed, keyElements.fixedAbq); - byte[] p22_gf16s = new byte[keyElements.publicKey.P22.length << 1]; - GF16.decode(keyElements.publicKey.P22, p22_gf16s, p22_gf16s.length); + byte[] publicKeySeed = Arrays.copyOf(pk, SnovaKeyPairGenerator.publicSeedLength); + byte[] p22_source = Arrays.copyOfRange(pk, SnovaKeyPairGenerator.publicSeedLength, pk.length); + engine.genABQP(keyElements.map1, publicKeySeed, keyElements.fixedAbq); byte[][][][] p22 = new byte[params.getM()][params.getO()][params.getO()][params.getLsq()]; - MapGroup1.fillP(p22_gf16s, 0, p22, p22_gf16s.length); - return verifySignatureCore(hash, signature, keyElements.publicKey, keyElements.map1, p22); + if ((params.getLsq() & 1) == 0) + { + MapGroup1.decodeP(p22_source, 0, p22, p22_source.length << 1); + } + else + { + byte[] p22_gf16s = new byte[p22_source.length << 1]; + GF16.decode(p22_source, p22_gf16s, p22_gf16s.length); + MapGroup1.fillP(p22_gf16s, 0, p22, p22_gf16s.length); + } + + return verifySignatureCore(hash, signature, publicKeySeed, keyElements.map1, p22); } public void createSignedHash( @@ -326,7 +349,7 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, System.arraycopy(arraySalt, 0, ptSignature, ptSignature.length - bytesSalt, bytesSalt); } - public boolean verifySignatureCore(byte[] digest, byte[] signature, PublicKey pkx, MapGroup1 map1, byte[][][][] p22) + public boolean verifySignatureCore(byte[] digest, byte[] signature, byte[] publicKeySeed, MapGroup1 map1, byte[][][][] p22) { final int bytesHash = (params.getO() * params.getLsq() + 1) >>> 1; final int bytesSalt = params.getSaltLength(); @@ -339,7 +362,7 @@ public boolean verifySignatureCore(byte[] digest, byte[] signature, PublicKey pk // Step 1: Regenerate signed hash using public key seed, digest and salt byte[] signedHash = new byte[bytesHash]; - shake.update(pkx.publicKeySeed, 0, pkx.publicKeySeed.length); + shake.update(publicKeySeed, 0, publicKeySeed.length); shake.update(digest, 0, digest.length); shake.update(signature, bytesSignature, bytesSalt); shake.doFinal(signedHash, 0, bytesHash); From 26ce10e9649d03a359182a39a1785f6d1b7f393e Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 1 Apr 2025 17:25:59 +1030 Subject: [PATCH 291/890] Reduce the size of SnovaKeyElements --- .../pqc/crypto/snova/SnovaEngine.java | 19 +++++++-- .../pqc/crypto/snova/SnovaKeyElements.java | 40 ------------------- .../crypto/snova/SnovaKeyPairGenerator.java | 18 +++------ .../pqc/crypto/snova/SnovaSigner.java | 14 ++----- .../main/java/org/bouncycastle/util/GF16.java | 29 +++++++++++--- 5 files changed, 48 insertions(+), 72 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index b96f05fcf9..fe1fd9e000 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -349,7 +349,7 @@ private static void copy4DMatrix(byte[][][][] src, byte[][][][] dest, int dim1, } } - public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] F12) + public void genP22(byte[] outP22, int outOff, byte[][][] T12, byte[][][][] P21, byte[][][][] F12) { // Initialize P22 with zeros byte[] P22 = new byte[m * o * o * lsq]; @@ -374,7 +374,7 @@ public void genP22(byte[] outP22, byte[][][] T12, byte[][][][] P21, byte[][][][] } // Convert GF16 elements to packed bytes - GF16.encode(P22, outP22, P22.length); + GF16.encode(P22, outP22, outOff, P22.length); } void genSeedsAndT12(byte[][][] T12, byte[] skSeed) @@ -405,7 +405,7 @@ void genSeedsAndT12(byte[][][] T12, byte[] skSeed) } } - void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) + void genABQP(MapGroup1 map1, byte[] pkSeed) { int gf16sPrngPublic = lsq * (2 * m * alpha + m * (n * n - m * m)) + l * 2 * m * alpha; byte[] qTemp = new byte[(m * alpha * lsq + m * alpha * lsq) / l]; @@ -497,10 +497,23 @@ void genABQP(MapGroup1 map1, byte[] pkSeed, byte[] fixedAbq) } else { + byte[] fixedAbq = SnovaParameters.fixedAbqSet.get(o); MapGroup1.fillAlpha(fixedAbq, 0, map1.aAlpha, m * o * alpha * lsq); MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq, map1.bAlpha, (m - 1) * o * alpha * lsq); MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq * 2, map1.qAlpha1, (m - 2) * o * alpha * lsq); MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq * 3, map1.qAlpha2, (m - 3) * o * alpha * lsq); } } + + void genMap1T12Map2(SnovaKeyElements keyElements, byte[] pkSeed, byte[] skSeed) + { + // Generate T12 matrix + genSeedsAndT12(keyElements.T12, skSeed); + + // Generate map components + genABQP(keyElements.map1, pkSeed); + + // Generate F matrices + genF(keyElements.map2, keyElements.map1, keyElements.T12); + } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java index 31f803937b..b26a752f7d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java @@ -7,8 +7,6 @@ class SnovaKeyElements public final MapGroup2 map2; public byte[] ptPrivateKeySeed; - byte[] fixedAbq; - public SnovaKeyElements(SnovaParameters params) { int o = params.getO(); @@ -18,46 +16,8 @@ public SnovaKeyElements(SnovaParameters params) map1 = new MapGroup1(params); T12 = new byte[v][o][lsq]; map2 = new MapGroup2(params); - - if (l < 4) - { - fixedAbq = SnovaParameters.fixedAbqSet.get(o); - } } -// public void encodeMergerInHalf(byte[] output) -// { -// byte[] input = new byte[length]; -// int inOff = 0; -// inOff = copy3d(map1.aAlpha, input, inOff); -// inOff = copy3d(map1.bAlpha, input, inOff); -// inOff = copy3d(map1.qAlpha1, input, inOff); -// inOff = copy3d(map1.qAlpha2, input, inOff); -// inOff = copy3d(T12, input, inOff); -// inOff = copy4d(map2.f11, input, inOff); -// inOff = copy4d(map2.f12, input, inOff); -// copy4d(map2.f21, input, inOff); -// GF16Utils.encodeMergeInHalf(input, length, output); -// } - -// public void skUnpack(byte[] input) -// { -// byte[] tmp = new byte[(input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength) << 1]; -// GF16Utils.decodeMergeInHalf(input, tmp, tmp.length); -// int inOff = 0; -// inOff = copy3d(tmp, inOff, map1.aAlpha); -// inOff = copy3d(tmp, inOff, map1.bAlpha); -// inOff = copy3d(tmp, inOff, map1.qAlpha1); -// inOff = copy3d(tmp, inOff, map1.qAlpha2); -// inOff = copy3d(tmp, inOff, T12); -// inOff = copy4d(tmp, inOff, map2.f11); -// inOff = copy4d(tmp, inOff, map2.f12); -// copy4d(tmp, inOff, map2.f21); -// System.arraycopy(input, input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength, publicKey.publicKeySeed, 0, publicKey.publicKeySeed.length); -// ptPrivateKeySeed = new byte[SnovaKeyPairGenerator.privateSeedLength]; -// System.arraycopy(input, input.length - SnovaKeyPairGenerator.privateSeedLength, ptPrivateKeySeed, 0, ptPrivateKeySeed.length); -// } - static int copy3d(byte[][][] alpha, byte[] output, int outOff) { for (int i = 0; i < alpha.length; ++i) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index 2722d32e5a..eadcf41252 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -47,12 +47,11 @@ public AsymmetricCipherKeyPair generateKeyPair() byte[] ptPrivateKeySeed = Arrays.copyOfRange(seedPair, publicSeedLength, seedPair.length); SnovaKeyElements keyElements = new SnovaKeyElements(params); - byte[] p22 = new byte[(params.getM() * params.getO() * params.getO() * params.getL() * params.getL() + 1) >> 1]; - generateKeysCore(keyElements, ptPublicKeySeed, ptPrivateKeySeed, p22); + System.arraycopy(ptPublicKeySeed, 0, pk, 0, ptPublicKeySeed.length); + generateKeysCore(keyElements, ptPublicKeySeed, ptPrivateKeySeed, pk, ptPublicKeySeed.length); // Pack public key components System.arraycopy(ptPublicKeySeed, 0, pk, 0, ptPublicKeySeed.length); - System.arraycopy(p22, 0, pk, ptPublicKeySeed.length, p22.length); if (params.isSkIsSeed()) { @@ -85,18 +84,11 @@ public AsymmetricCipherKeyPair generateKeyPair() ); } - private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[] skSeed, byte[] p22) + private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[] skSeed, byte[] p22, int p22Off) { - // Generate T12 matrix - engine.genSeedsAndT12(keyElements.T12, skSeed); - - // Generate map components - engine.genABQP(keyElements.map1, pkSeed, keyElements.fixedAbq); - - // Generate F matrices - engine.genF(keyElements.map2, keyElements.map1, keyElements.T12); + engine.genMap1T12Map2(keyElements, pkSeed, skSeed); // Generate P22 matrix - engine.genP22(p22, keyElements.T12, keyElements.map1.p21, keyElements.map2.f12); + engine.genP22(p22, p22Off, keyElements.T12, keyElements.map1.p21, keyElements.map2.f12); } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index e8ece88dbe..728ded9dc6 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -66,13 +66,7 @@ public byte[] generateSignature(byte[] message) byte[] seedPair = privKey.getPrivateKey(); publicKeySeed = Arrays.copyOfRange(seedPair, 0, SnovaKeyPairGenerator.publicSeedLength); keyElements.ptPrivateKeySeed = Arrays.copyOfRange(seedPair, SnovaKeyPairGenerator.publicSeedLength, seedPair.length); - engine.genSeedsAndT12(keyElements.T12, keyElements.ptPrivateKeySeed); - - // Generate map components - engine.genABQP(keyElements.map1, publicKeySeed, keyElements.fixedAbq); - - // Generate F matrices - engine.genF(keyElements.map2, keyElements.map1, keyElements.T12); + engine.genMap1T12Map2(keyElements, publicKeySeed, keyElements.ptPrivateKeySeed); } else { @@ -103,11 +97,11 @@ public boolean verifySignature(byte[] message, byte[] signature) byte[] hash = new byte[shake.getDigestSize()]; shake.update(message, 0, message.length); shake.doFinal(hash, 0); - SnovaKeyElements keyElements = new SnovaKeyElements(params); + MapGroup1 map1 = new MapGroup1(params); byte[] pk = pubKey.getEncoded(); byte[] publicKeySeed = Arrays.copyOf(pk, SnovaKeyPairGenerator.publicSeedLength); byte[] p22_source = Arrays.copyOfRange(pk, SnovaKeyPairGenerator.publicSeedLength, pk.length); - engine.genABQP(keyElements.map1, publicKeySeed, keyElements.fixedAbq); + engine.genABQP(map1, publicKeySeed); byte[][][][] p22 = new byte[params.getM()][params.getO()][params.getO()][params.getLsq()]; if ((params.getLsq() & 1) == 0) { @@ -120,7 +114,7 @@ public boolean verifySignature(byte[] message, byte[] signature) MapGroup1.fillP(p22_gf16s, 0, p22, p22_gf16s.length); } - return verifySignatureCore(hash, signature, publicKeySeed, keyElements.map1, p22); + return verifySignatureCore(hash, signature, publicKeySeed, map1, p22); } public void createSignedHash( diff --git a/core/src/main/java/org/bouncycastle/util/GF16.java b/core/src/main/java/org/bouncycastle/util/GF16.java index eb60a18f60..3076aa76bb 100644 --- a/core/src/main/java/org/bouncycastle/util/GF16.java +++ b/core/src/main/java/org/bouncycastle/util/GF16.java @@ -120,27 +120,44 @@ public static void decode(byte[] input, int inOff, byte[] output, int outOff, in * Two 4-bit values are packed into one byte, with the first nibble stored in the lower 4 bits * and the second nibble stored in the upper 4 bits. * - * @param input the input array of 4-bit values (stored as bytes, only lower 4 bits used) - * @param output the output byte array that will hold the encoded bytes - * @param outputLen the number of nibbles in the input array + * @param input the input array of 4-bit values (stored as bytes, only lower 4 bits used) + * @param output the output byte array that will hold the encoded bytes + * @param inputLen the number of nibbles in the input array */ - public static void encode(byte[] input, byte[] output, int outputLen) + public static void encode(byte[] input, byte[] output, int inputLen) { int i, inOff = 0; // Process pairs of 4-bit values - for (i = 0; i < outputLen / 2; i++) + for (i = 0; i < inputLen / 2; i++) { int lowerNibble = input[inOff++] & 0x0F; int upperNibble = (input[inOff++] & 0x0F) << 4; output[i] = (byte)(lowerNibble | upperNibble); } // If there is an extra nibble (odd number of nibbles), store it directly in lower 4 bits. - if ((outputLen & 1) == 1) + if ((inputLen & 1) == 1) { output[i] = (byte)(input[inOff] & 0x0F); } } + public static void encode(byte[] input, byte[] output, int outOff, int inputLen) + { + int i, inOff = 0; + // Process pairs of 4-bit values + for (i = 0; i < inputLen / 2; i++) + { + int lowerNibble = input[inOff++] & 0x0F; + int upperNibble = (input[inOff++] & 0x0F) << 4; + output[outOff++] = (byte)(lowerNibble | upperNibble); + } + // If there is an extra nibble (odd number of nibbles), store it directly in lower 4 bits. + if ((inputLen & 1) == 1) + { + output[outOff] = (byte)(input[inOff] & 0x0F); + } + } + public static byte innerProduct(byte[] a, int aOff, byte[] b, int bOff, int rank) { byte result = 0; From 7bb88a60effd2b6e6ad6e0a7de77e6bc8c644a02 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 2 Apr 2025 11:10:45 +1030 Subject: [PATCH 292/890] Update AEAD modes --- .../crypto/modes/EAXBlockCipher.java | 12 ++++ .../crypto/modes/GCMBlockCipher.java | 13 ++++- .../crypto/modes/OCBBlockCipher.java | 13 ++++- .../jce/provider/test/BlockCipherTest.java | 56 +++++++++++++++++++ 4 files changed, 92 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java index 6eb5ca4fb1..70ac9f8b0c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java @@ -224,6 +224,12 @@ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) { throw new DataLengthException("Input buffer too short"); } + if (in == out && segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len))) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } int resultLen = 0; @@ -384,4 +390,10 @@ private boolean verifyMac(byte[] mac, int off) return nonEqual == 0; } + + protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) + { + // please ensure a valid check for inLen > 0 and outLen > 0 outside this function + return inOff <= outOff + outLen && outOff <= inOff + inLen; + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java index 017847ef8d..79e9587be7 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java @@ -383,7 +383,12 @@ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) { throw new DataLengthException("Input buffer too short"); } - + if (in == out && segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len))) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } int resultLen = 0; if (forEncryption) @@ -749,4 +754,10 @@ private void checkStatus() throw new IllegalStateException("GCM cipher needs to be initialised"); } } + + protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) + { + // please ensure a valid check for inLen > 0 and outLen > 0 outside this function + return inOff <= outOff + outLen && outOff <= inOff + inLen; + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java index 93854eadc1..65b1cb1daa 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java @@ -333,7 +333,12 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output, int out throw new DataLengthException("Input buffer too short"); } int resultLen = 0; - + if (input == output && segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len))) + { + input = new byte[len]; + System.arraycopy(output, inOff, input, 0, len); + inOff = 0; + } for (int i = 0; i < len; ++i) { mainBlock[mainBlockPos] = input[inOff + i]; @@ -592,4 +597,10 @@ protected static void xor(byte[] block, byte[] val) { Bytes.xorTo(16, val, block); } + + protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) + { + // please ensure a valid check for inLen > 0 and outLen > 0 outside this function + return inOff <= outOff + outLen && outOff <= inOff + inLen; + } } diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java index e11733022b..d0c3304e5b 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java @@ -50,6 +50,7 @@ import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; import org.bouncycastle.util.test.TestFailedException; +import org.junit.Assert; /** * basic test class for a block cipher, basically this just exercises the provider, and makes sure we @@ -850,6 +851,61 @@ else if (algorithm.startsWith("RC5")) fail("" + algorithm + " failed encryption - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(bytes))); } + // + // Try the doFinal - same input/output same index, index + // + byte[] data = Arrays.concatenate(input, new byte[2 * in.getBlockSize()]); + int len = 0; + try + { + if (algorithm.indexOf("GCM") > 0) + { + out = Cipher.getInstance(algorithm, "BC"); + out.init(Cipher.ENCRYPT_MODE, key, rand); + } + + len = out.doFinal(data, 0, input.length, data, 0); + } + catch (Exception e) + { + Assert.fail(e.toString()); + } + + if (!Arrays.areEqual(data, 0, len, output, 0, output.length)) + { + Assert.fail("" + algorithm + " failed doFinal - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(data))); + } + + // + // Try the doFinal - same input/output shifted offset + // + data = Arrays.concatenate(input, new byte[2 * in.getBlockSize()]); + len = 0; + try + { + + if (algorithm.indexOf("GCM") > 0) + { + out = Cipher.getInstance(algorithm, "BC"); + out.init(Cipher.ENCRYPT_MODE, key, rand); + } + + len = out.doFinal(data, 0, input.length, data, 1); + +// System.out.println(Hex.toHexString(output)); +// System.out.println(Hex.toHexString(Arrays.copyOfRange(data, 1, 1 + len))); +// System.out.println(len + " " + output.length); + } + catch (Exception e) + { + Assert.fail(e.toString()); + } + + if (!Arrays.areEqual(data, 1, 1 + len, output, 0, output.length)) + { + Assert.fail("" + algorithm + " failed doFinal - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(data))); + } + // // decryption pass // From 9dda4061be8d7d5562015fa342775609457174e0 Mon Sep 17 00:00:00 2001 From: gefeili Date: Wed, 2 Apr 2025 17:19:43 +1030 Subject: [PATCH 293/890] TODO: add Snova to PQC Provider --- .../pqc/crypto/snova/MapGroup1.java | 49 +++-- .../pqc/crypto/snova/SnovaEngine.java | 24 +- .../pqc/crypto/snova/SnovaParameters.java | 208 +++++++++--------- .../pqc/crypto/snova/SnovaSigner.java | 26 ++- .../pqc/crypto/test/SnovaTest.java | 88 ++++---- .../pqc/jcajce/interfaces/SnovaKey.java | 16 ++ .../provider/snova/BCSnovaPrivateKey.java | 5 + .../pqc/jcajce/spec/SnovaParameterSpec.java | 145 ++++++++++++ 8 files changed, 379 insertions(+), 182 deletions(-) create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/SnovaKey.java create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/BCSnovaPrivateKey.java create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/spec/SnovaParameterSpec.java diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java index e23a08de44..007ee0eb33 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -28,7 +28,7 @@ public MapGroup1(SnovaParameters params) qAlpha2 = new byte[m][alpha][lsq]; } - public void decode(byte[] input, int len) + public void decode(byte[] input, int len, boolean isl4or5) { // int m = params.getM(); // int v = params.getV(); @@ -37,13 +37,17 @@ public void decode(byte[] input, int len) // int lsq = params.getLsq(); // if ((lsq & 1) == 0) // { + int inOff = decodeP(input, 0, p11, len); inOff += decodeP(input, inOff, p12, len - inOff); inOff += decodeP(input, inOff, p21, len - inOff); - inOff += decodeAlpha(input, inOff, aAlpha, len - inOff); - inOff += decodeAlpha(input, inOff, bAlpha, len - inOff); - inOff += decodeAlpha(input, inOff, qAlpha1, len - inOff); - decodeAlpha(input, inOff, qAlpha2, len - inOff); + if (isl4or5) + { + inOff += decodeAlpha(input, inOff, aAlpha, len - inOff); + inOff += decodeAlpha(input, inOff, bAlpha, len - inOff); + inOff += decodeAlpha(input, inOff, qAlpha1, len - inOff); + decodeAlpha(input, inOff, qAlpha2, len - inOff); + } // } // else // { @@ -91,13 +95,21 @@ private static int decodeAlpha(byte[] input, int inOff, byte[][][] alpha, int le int rlt = 0; for (int i = 0; i < alpha.length; ++i) { - for (int j = 0; j < alpha[i].length; ++j) - { - int tmp = Math.min(alpha[i][j].length, len << 1); - GF16.decode(input, inOff + rlt, alpha[i][j], 0, tmp); - rlt += (tmp + 1) >> 1; - len -= (tmp + 1) >> 1; - } + rlt += decodeArray(input, inOff + rlt, alpha[i], len - rlt); + } + return rlt; + } + + static int decodeArray(byte[] input, int inOff, byte[][] array, int len) + { + int rlt = 0; + for (int j = 0; j < array.length; ++j) + { + int tmp = Math.min(array[j].length, len << 1); + GF16.decode(input, inOff + rlt, array[j], 0, tmp); + tmp = (tmp + 1) >> 1; + rlt += tmp; + len -= tmp; } return rlt; } @@ -124,15 +136,18 @@ private static int decodeAlpha(byte[] input, int inOff, byte[][][] alpha, int le // return isLower; // } - public void fill(byte[] input) + public void fill(byte[] input, boolean isl4or5) { int inOff = fillP(input, 0, p11, input.length); inOff += fillP(input, inOff, p12, input.length - inOff); inOff += fillP(input, inOff, p21, input.length - inOff); - inOff += fillAlpha(input, inOff, aAlpha, input.length - inOff); - inOff += fillAlpha(input, inOff, bAlpha, input.length - inOff); - inOff += fillAlpha(input, inOff, qAlpha1, input.length - inOff); - fillAlpha(input, inOff, qAlpha2, input.length - inOff); + if (isl4or5) + { + inOff += fillAlpha(input, inOff, aAlpha, input.length - inOff); + inOff += fillAlpha(input, inOff, bAlpha, input.length - inOff); + inOff += fillAlpha(input, inOff, qAlpha1, input.length - inOff); + fillAlpha(input, inOff, qAlpha2, input.length - inOff); + } } static int fillP(byte[] input, int inOff, byte[][][][] p, int len) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index fe1fd9e000..68edcb658c 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -352,22 +352,24 @@ private static void copy4DMatrix(byte[][][][] src, byte[][][][] dest, int dim1, public void genP22(byte[] outP22, int outOff, byte[][][] T12, byte[][][][] P21, byte[][][][] F12) { // Initialize P22 with zeros - byte[] P22 = new byte[m * o * o * lsq]; + int oxlsq = o * lsq; + int oxoxlsq = oxlsq * o; + byte[] P22 = new byte[m * oxoxlsq]; - for (int i = 0; i < m; i++) + for (int i = 0, ixoxolsq = 0; i < m; i++, ixoxolsq += oxoxlsq) { - for (int j = 0; j < o; j++) + for (int j = 0, jxoxlsq = ixoxolsq; j < o; j++, jxoxlsq += oxlsq) { - for (int k = 0; k < o; k++) + for (int k = 0, kxlsq = jxoxlsq; k < o; k++, kxlsq += lsq) { + //int idx = ((i * o + j) * o + k) * lsq; for (int index = 0; index < v; index++) { - int idx = ((i * o + j) * o + k) * lsq; // P22[i][j][k] ^= T12[index][j] * F12[i][index][k] - GF16Utils.gf16mMulTo(T12[index][j], F12[i][index][k], 0, P22, idx, l); + GF16Utils.gf16mMulTo(T12[index][j], F12[i][index][k], 0, P22, kxlsq, l); // P22[i][j][k] ^= P21[i][j][index] * T12[index][k] - GF16Utils.gf16mMulTo(P21[i][j][index], T12[index][k], 0, P22, idx, l); + GF16Utils.gf16mMulTo(P21[i][j][index], T12[index][k], 0, P22, kxlsq, l); } } } @@ -379,8 +381,8 @@ public void genP22(byte[] outP22, int outOff, byte[][][] T12, byte[][][][] P21, void genSeedsAndT12(byte[][][] T12, byte[] skSeed) { - int bytesPrngPrivate = (v * o * l + 1) >>> 1; int gf16sPrngPrivate = v * o * l; + int bytesPrngPrivate = (gf16sPrngPrivate + 1) >>> 1; byte[] prngOutput = new byte[bytesPrngPrivate]; // Generate PRNG output using SHAKE-256 @@ -408,7 +410,7 @@ void genSeedsAndT12(byte[][][] T12, byte[] skSeed) void genABQP(MapGroup1 map1, byte[] pkSeed) { int gf16sPrngPublic = lsq * (2 * m * alpha + m * (n * n - m * m)) + l * 2 * m * alpha; - byte[] qTemp = new byte[(m * alpha * lsq + m * alpha * lsq) / l]; + byte[] qTemp = new byte[(m * alpha * l) << 1]; byte[] prngOutput = new byte[(gf16sPrngPublic + 1) >> 1]; if (params.isPkExpandShake()) @@ -468,13 +470,13 @@ void genABQP(MapGroup1 map1, byte[] pkSeed) } if ((lsq & 1) == 0) { - map1.decode(prngOutput, (gf16sPrngPublic - qTemp.length) >> 1); + map1.decode(prngOutput, (gf16sPrngPublic - qTemp.length) >> 1, l >= 4); } else { byte[] temp = new byte[gf16sPrngPublic - qTemp.length]; GF16.decode(prngOutput, temp, temp.length); - map1.fill(temp); + map1.fill(temp, l >= 4); } if (l >= 4) { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java index cfaa36a8d3..8ece75e892 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java @@ -12,110 +12,110 @@ public class SnovaParameters static Map sSet = new HashMap(); //key is l static Map xSSet = new HashMap(); - public static final SnovaParameters SNOVA_24_5_16_4_SSK = - new SnovaParameters("SNOVA_24_5_16_4_SSK", 24, 5, 4, true, false); - public static final SnovaParameters SNOVA_24_5_16_4_ESK = - new SnovaParameters("SNOVA_24_5_16_4_ESK", 24, 5, 4, false, false); - public static final SnovaParameters SNOVA_24_5_16_4_SHAKE_SSK = - new SnovaParameters("SNOVA_24_5_16_4_SHAKE_SSK", 24, 5, 4, true, true); - public static final SnovaParameters SNOVA_24_5_16_4_SHAKE_ESK = - new SnovaParameters("SNOVA_24_5_16_4_SHAKE_ESK", 24, 5, 4, false, true); - - public static final SnovaParameters SNOVA_24_5_16_5_SSK = - new SnovaParameters("SNOVA_24_5_16_5_SSK", 24, 5, 5, true, false); - public static final SnovaParameters SNOVA_24_5_16_5_ESK = - new SnovaParameters("SNOVA_24_5_16_5_ESK", 24, 5, 5, false, false); - public static final SnovaParameters SNOVA_24_5_16_5_SHAKE_SSK = - new SnovaParameters("SNOVA_24_5_16_5_SHAKE_SSK", 24, 5, 5, true, true); - public static final SnovaParameters SNOVA_24_5_16_5_SHAKE_ESK = - new SnovaParameters("SNOVA_24_5_16_5_SHAKE_ESK", 24, 5, 5, false, true); - - public static final SnovaParameters SNOVA_25_8_16_3_SSK = - new SnovaParameters("SNOVA_25_8_16_3_SSK", 25, 8, 3, true, false); - public static final SnovaParameters SNOVA_25_8_16_3_ESK = - new SnovaParameters("SNOVA_25_8_16_3_ESK", 25, 8, 3, false, false); - public static final SnovaParameters SNOVA_25_8_16_3_SHAKE_SSK = - new SnovaParameters("SNOVA_25_8_16_3_SHAKE_SSK", 25, 8, 3, true, true); - public static final SnovaParameters SNOVA_25_8_16_3_SHAKE_ESK = - new SnovaParameters("SNOVA_25_8_16_3_SHAKE_ESK", 25, 8, 3, false, true); - - public static final SnovaParameters SNOVA_29_6_16_5_SSK = - new SnovaParameters("SNOVA_29_6_16_5_SSK", 29, 6, 5, true, false); - public static final SnovaParameters SNOVA_29_6_16_5_ESK = - new SnovaParameters("SNOVA_29_6_16_5_ESK", 29, 6, 5, false, false); - public static final SnovaParameters SNOVA_29_6_16_5_SHAKE_SSK = - new SnovaParameters("SNOVA_29_6_16_5_SHAKE_SSK", 29, 6, 5, true, true); - public static final SnovaParameters SNOVA_29_6_16_5_SHAKE_ESK = - new SnovaParameters("SNOVA_29_6_16_5_SHAKE_ESK", 29, 6, 5, false, true); - - public static final SnovaParameters SNOVA_37_8_16_4_SSK = - new SnovaParameters("SNOVA_37_8_16_4_SSK", 37, 8, 4, true, false); - public static final SnovaParameters SNOVA_37_8_16_4_ESK = - new SnovaParameters("SNOVA_37_8_16_4_ESK", 37, 8, 4, false, false); - public static final SnovaParameters SNOVA_37_8_16_4_SHAKE_SSK = - new SnovaParameters("SNOVA_37_8_16_4_SHAKE_SSK", 37, 8, 4, true, true); - public static final SnovaParameters SNOVA_37_8_16_4_SHAKE_ESK = - new SnovaParameters("SNOVA_37_8_16_4_SHAKE_ESK", 37, 8, 4, false, true); - - // SNOVA_37_17_16_2 variants - public static final SnovaParameters SNOVA_37_17_16_2_SSK = - new SnovaParameters("SNOVA_37_17_16_2_SSK", 37, 17, 2, true, false); - public static final SnovaParameters SNOVA_37_17_16_2_ESK = - new SnovaParameters("SNOVA_37_17_16_2_ESK", 37, 17, 2, false, false); - public static final SnovaParameters SNOVA_37_17_16_2_SHAKE_SSK = - new SnovaParameters("SNOVA_37_17_16_2_SHAKE_SSK", 37, 17, 2, true, true); - public static final SnovaParameters SNOVA_37_17_16_2_SHAKE_ESK = - new SnovaParameters("SNOVA_37_17_16_2_SHAKE_ESK", 37, 17, 2, false, true); - - // SNOVA_49_11_16_3 variants - public static final SnovaParameters SNOVA_49_11_16_3_SSK = - new SnovaParameters("SNOVA_49_11_16_3_SSK", 49, 11, 3, true, false); - public static final SnovaParameters SNOVA_49_11_16_3_ESK = - new SnovaParameters("SNOVA_49_11_16_3_ESK", 49, 11, 3, false, false); - public static final SnovaParameters SNOVA_49_11_16_3_SHAKE_SSK = - new SnovaParameters("SNOVA_49_11_16_3_SHAKE_SSK", 49, 11, 3, true, true); - public static final SnovaParameters SNOVA_49_11_16_3_SHAKE_ESK = - new SnovaParameters("SNOVA_49_11_16_3_SHAKE_ESK", 49, 11, 3, false, true); - - // SNOVA_56_25_16_2 variants - public static final SnovaParameters SNOVA_56_25_16_2_SSK = - new SnovaParameters("SNOVA_56_25_16_2_SSK", 56, 25, 2, true, false); - public static final SnovaParameters SNOVA_56_25_16_2_ESK = - new SnovaParameters("SNOVA_56_25_16_2_ESK", 56, 25, 2, false, false); - public static final SnovaParameters SNOVA_56_25_16_2_SHAKE_SSK = - new SnovaParameters("SNOVA_56_25_16_2_SHAKE_SSK", 56, 25, 2, true, true); - public static final SnovaParameters SNOVA_56_25_16_2_SHAKE_ESK = - new SnovaParameters("SNOVA_56_25_16_2_SHAKE_ESK", 56, 25, 2, false, true); - - // SNOVA_60_10_16_4 variants - public static final SnovaParameters SNOVA_60_10_16_4_SSK = - new SnovaParameters("SNOVA_60_10_16_4_SSK", 60, 10, 4, true, false); - public static final SnovaParameters SNOVA_60_10_16_4_ESK = - new SnovaParameters("SNOVA_60_10_16_4_ESK", 60, 10, 4, false, false); - public static final SnovaParameters SNOVA_60_10_16_4_SHAKE_SSK = - new SnovaParameters("SNOVA_60_10_16_4_SHAKE_SSK", 60, 10, 4, true, true); - public static final SnovaParameters SNOVA_60_10_16_4_SHAKE_ESK = - new SnovaParameters("SNOVA_60_10_16_4_SHAKE_ESK", 60, 10, 4, false, true); - - // SNOVA_66_15_16_4 variants - public static final SnovaParameters SNOVA_66_15_16_3_SSK = - new SnovaParameters("SNOVA_66_15_16_3_SSK", 66, 15, 3, true, false); - public static final SnovaParameters SNOVA_66_15_16_3_ESK = - new SnovaParameters("SNOVA_66_15_16_3_ESK", 66, 15, 3, false, false); - public static final SnovaParameters SNOVA_66_15_16_3_SHAKE_SSK = - new SnovaParameters("SNOVA_66_15_16_3_SHAKE_SSK", 66, 15, 3, true, true); - public static final SnovaParameters SNOVA_66_15_16_3_SHAKE_ESK = - new SnovaParameters("SNOVA_66_15_16_3_SHAKE_ESK", 66, 15, 3, false, true); - - // SNOVA_75_33_16_2 variants - public static final SnovaParameters SNOVA_75_33_16_2_SSK = - new SnovaParameters("SNOVA_75_33_16_2_SSK", 75, 33, 2, true, false); - public static final SnovaParameters SNOVA_75_33_16_2_ESK = - new SnovaParameters("SNOVA_75_33_16_2_ESK", 75, 33, 2, false, false); - public static final SnovaParameters SNOVA_75_33_16_2_SHAKE_SSK = - new SnovaParameters("SNOVA_75_33_16_2_SHAKE_SSK", 75, 33, 2, true, true); - public static final SnovaParameters SNOVA_75_33_16_2_SHAKE_ESK = - new SnovaParameters("SNOVA_75_33_16_2_SHAKE_ESK", 75, 33, 2, false, true); + public static final SnovaParameters SNOVA_24_5_4_SSK = + new SnovaParameters("SNOVA_24_5_4_SSK", 24, 5, 4, true, false); + public static final SnovaParameters SNOVA_24_5_4_ESK = + new SnovaParameters("SNOVA_24_5_4_ESK", 24, 5, 4, false, false); + public static final SnovaParameters SNOVA_24_5_4_SHAKE_SSK = + new SnovaParameters("SNOVA_24_5_4_SHAKE_SSK", 24, 5, 4, true, true); + public static final SnovaParameters SNOVA_24_5_4_SHAKE_ESK = + new SnovaParameters("SNOVA_24_5_4_SHAKE_ESK", 24, 5, 4, false, true); + + public static final SnovaParameters SNOVA_24_5_5_SSK = + new SnovaParameters("SNOVA_24_5_5_SSK", 24, 5, 5, true, false); + public static final SnovaParameters SNOVA_24_5_5_ESK = + new SnovaParameters("SNOVA_24_5_5_ESK", 24, 5, 5, false, false); + public static final SnovaParameters SNOVA_24_5_5_SHAKE_SSK = + new SnovaParameters("SNOVA_24_5_5_SHAKE_SSK", 24, 5, 5, true, true); + public static final SnovaParameters SNOVA_24_5_5_SHAKE_ESK = + new SnovaParameters("SNOVA_24_5_5_SHAKE_ESK", 24, 5, 5, false, true); + + public static final SnovaParameters SNOVA_25_8_3_SSK = + new SnovaParameters("SNOVA_25_8_3_SSK", 25, 8, 3, true, false); + public static final SnovaParameters SNOVA_25_8_3_ESK = + new SnovaParameters("SNOVA_25_8_3_ESK", 25, 8, 3, false, false); + public static final SnovaParameters SNOVA_25_8_3_SHAKE_SSK = + new SnovaParameters("SNOVA_25_8_3_SHAKE_SSK", 25, 8, 3, true, true); + public static final SnovaParameters SNOVA_25_8_3_SHAKE_ESK = + new SnovaParameters("SNOVA_25_8_3_SHAKE_ESK", 25, 8, 3, false, true); + + public static final SnovaParameters SNOVA_29_6_5_SSK = + new SnovaParameters("SNOVA_29_6_5_SSK", 29, 6, 5, true, false); + public static final SnovaParameters SNOVA_29_6_5_ESK = + new SnovaParameters("SNOVA_29_6_5_ESK", 29, 6, 5, false, false); + public static final SnovaParameters SNOVA_29_6_5_SHAKE_SSK = + new SnovaParameters("SNOVA_29_6_5_SHAKE_SSK", 29, 6, 5, true, true); + public static final SnovaParameters SNOVA_29_6_5_SHAKE_ESK = + new SnovaParameters("SNOVA_29_6_5_SHAKE_ESK", 29, 6, 5, false, true); + + public static final SnovaParameters SNOVA_37_8_4_SSK = + new SnovaParameters("SNOVA_37_8_4_SSK", 37, 8, 4, true, false); + public static final SnovaParameters SNOVA_37_8_4_ESK = + new SnovaParameters("SNOVA_37_8_4_ESK", 37, 8, 4, false, false); + public static final SnovaParameters SNOVA_37_8_4_SHAKE_SSK = + new SnovaParameters("SNOVA_37_8_4_SHAKE_SSK", 37, 8, 4, true, true); + public static final SnovaParameters SNOVA_37_8_4_SHAKE_ESK = + new SnovaParameters("SNOVA_37_8_4_SHAKE_ESK", 37, 8, 4, false, true); + + // SNOVA_37_17_2 variants + public static final SnovaParameters SNOVA_37_17_2_SSK = + new SnovaParameters("SNOVA_37_17_2_SSK", 37, 17, 2, true, false); + public static final SnovaParameters SNOVA_37_17_2_ESK = + new SnovaParameters("SNOVA_37_17_2_ESK", 37, 17, 2, false, false); + public static final SnovaParameters SNOVA_37_17_2_SHAKE_SSK = + new SnovaParameters("SNOVA_37_17_2_SHAKE_SSK", 37, 17, 2, true, true); + public static final SnovaParameters SNOVA_37_17_2_SHAKE_ESK = + new SnovaParameters("SNOVA_37_17_2_SHAKE_ESK", 37, 17, 2, false, true); + + // SNOVA_49_11_3 variants + public static final SnovaParameters SNOVA_49_11_3_SSK = + new SnovaParameters("SNOVA_49_11_3_SSK", 49, 11, 3, true, false); + public static final SnovaParameters SNOVA_49_11_3_ESK = + new SnovaParameters("SNOVA_49_11_3_ESK", 49, 11, 3, false, false); + public static final SnovaParameters SNOVA_49_11_3_SHAKE_SSK = + new SnovaParameters("SNOVA_49_11_3_SHAKE_SSK", 49, 11, 3, true, true); + public static final SnovaParameters SNOVA_49_11_3_SHAKE_ESK = + new SnovaParameters("SNOVA_49_11_3_SHAKE_ESK", 49, 11, 3, false, true); + + // SNOVA_56_25_2 variants + public static final SnovaParameters SNOVA_56_25_2_SSK = + new SnovaParameters("SNOVA_56_25_2_SSK", 56, 25, 2, true, false); + public static final SnovaParameters SNOVA_56_25_2_ESK = + new SnovaParameters("SNOVA_56_25_2_ESK", 56, 25, 2, false, false); + public static final SnovaParameters SNOVA_56_25_2_SHAKE_SSK = + new SnovaParameters("SNOVA_56_25_2_SHAKE_SSK", 56, 25, 2, true, true); + public static final SnovaParameters SNOVA_56_25_2_SHAKE_ESK = + new SnovaParameters("SNOVA_56_25_2_SHAKE_ESK", 56, 25, 2, false, true); + + // SNOVA_60_10_4 variants + public static final SnovaParameters SNOVA_60_10_4_SSK = + new SnovaParameters("SNOVA_60_10_4_SSK", 60, 10, 4, true, false); + public static final SnovaParameters SNOVA_60_10_4_ESK = + new SnovaParameters("SNOVA_60_10_4_ESK", 60, 10, 4, false, false); + public static final SnovaParameters SNOVA_60_10_4_SHAKE_SSK = + new SnovaParameters("SNOVA_60_10_4_SHAKE_SSK", 60, 10, 4, true, true); + public static final SnovaParameters SNOVA_60_10_4_SHAKE_ESK = + new SnovaParameters("SNOVA_60_10_4_SHAKE_ESK", 60, 10, 4, false, true); + + // SNOVA_66_15_4 variants + public static final SnovaParameters SNOVA_66_15_3_SSK = + new SnovaParameters("SNOVA_66_15_3_SSK", 66, 15, 3, true, false); + public static final SnovaParameters SNOVA_66_15_3_ESK = + new SnovaParameters("SNOVA_66_15_3_ESK", 66, 15, 3, false, false); + public static final SnovaParameters SNOVA_66_15_3_SHAKE_SSK = + new SnovaParameters("SNOVA_66_15_3_SHAKE_SSK", 66, 15, 3, true, true); + public static final SnovaParameters SNOVA_66_15_3_SHAKE_ESK = + new SnovaParameters("SNOVA_66_15_3_SHAKE_ESK", 66, 15, 3, false, true); + + // SNOVA_75_33_2 variants + public static final SnovaParameters SNOVA_75_33_2_SSK = + new SnovaParameters("SNOVA_75_33_2_SSK", 75, 33, 2, true, false); + public static final SnovaParameters SNOVA_75_33_2_ESK = + new SnovaParameters("SNOVA_75_33_2_ESK", 75, 33, 2, false, false); + public static final SnovaParameters SNOVA_75_33_2_SHAKE_SSK = + new SnovaParameters("SNOVA_75_33_2_SHAKE_SSK", 75, 33, 2, true, true); + public static final SnovaParameters SNOVA_75_33_2_SHAKE_ESK = + new SnovaParameters("SNOVA_75_33_2_SHAKE_ESK", 75, 33, 2, false, true); private final String name; private final int v; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index 728ded9dc6..2f0fa983e5 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -205,9 +205,16 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, shake.update(arraySalt, 0, arraySalt.length); shake.update(numSign); shake.doFinal(vinegarBytes, 0, vinegarBytes.length); - byte[] tmp = new byte[vinegarBytes.length << 1]; - GF16.decode(vinegarBytes, tmp, tmp.length); - fill(tmp, XInGF16Matrix, tmp.length); + if ((lsq & 1) == 1) + { + byte[] tmp = new byte[vinegarBytes.length << 1]; + GF16.decode(vinegarBytes, tmp, tmp.length); + fill(tmp, XInGF16Matrix, tmp.length); + } + else + { + MapGroup1.decodeArray(vinegarBytes, 0, XInGF16Matrix, vinegarBytes.length); + } // Evaluate vinegar part of central map for (int mi = 0; mi < m; mi++) @@ -369,9 +376,16 @@ public boolean verifySignatureCore(byte[] digest, byte[] signature, byte[] publi // Step 2: Convert signature to GF16 matrices byte[][] signatureGF16Matrix = new byte[n][lsq]; - byte[] decodedSig = new byte[n * lsq]; - GF16.decode(signature, 0, decodedSig, 0, decodedSig.length); - fill(decodedSig, signatureGF16Matrix, decodedSig.length); + if ((lsq & 1) == 1) + { + byte[] decodedSig = new byte[n * lsq]; + GF16.decode(signature, 0, decodedSig, 0, decodedSig.length); + fill(decodedSig, signatureGF16Matrix, decodedSig.length); + } + else + { + MapGroup1.decodeArray(signature, 0, signatureGF16Matrix, signature.length); + } // Step 3: Evaluate signature using public key byte[] computedHashBytes = new byte[m * lsq]; diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java index f4f834b8a5..8ca0634ee0 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java @@ -28,50 +28,50 @@ public static void main(String[] args) private static final SnovaParameters[] PARAMETER_SETS = new SnovaParameters[] { - SnovaParameters.SNOVA_24_5_16_4_ESK, - SnovaParameters.SNOVA_24_5_16_4_SHAKE_ESK, - SnovaParameters.SNOVA_24_5_16_4_SHAKE_SSK, - SnovaParameters.SNOVA_24_5_16_4_SSK, - SnovaParameters.SNOVA_24_5_16_5_ESK, - SnovaParameters.SNOVA_24_5_16_5_SHAKE_ESK, - SnovaParameters.SNOVA_24_5_16_5_SHAKE_SSK, - SnovaParameters.SNOVA_24_5_16_5_SSK, - SnovaParameters.SNOVA_25_8_16_3_ESK, - SnovaParameters.SNOVA_25_8_16_3_SHAKE_ESK, - SnovaParameters.SNOVA_25_8_16_3_SHAKE_SSK, - SnovaParameters.SNOVA_25_8_16_3_SSK, - SnovaParameters.SNOVA_29_6_16_5_ESK, - SnovaParameters.SNOVA_29_6_16_5_SHAKE_ESK, - SnovaParameters.SNOVA_29_6_16_5_SHAKE_SSK, - SnovaParameters.SNOVA_29_6_16_5_SSK, - SnovaParameters.SNOVA_37_8_16_4_ESK, - SnovaParameters.SNOVA_37_8_16_4_SHAKE_ESK, - SnovaParameters.SNOVA_37_8_16_4_SHAKE_SSK, - SnovaParameters.SNOVA_37_8_16_4_SSK, - SnovaParameters.SNOVA_37_17_16_2_ESK, - SnovaParameters.SNOVA_37_17_16_2_SHAKE_ESK, - SnovaParameters.SNOVA_37_17_16_2_SHAKE_SSK, - SnovaParameters.SNOVA_37_17_16_2_SSK, - SnovaParameters.SNOVA_49_11_16_3_ESK, - SnovaParameters.SNOVA_49_11_16_3_SHAKE_ESK, - SnovaParameters.SNOVA_49_11_16_3_SHAKE_SSK, - SnovaParameters.SNOVA_49_11_16_3_SSK, - SnovaParameters.SNOVA_56_25_16_2_ESK, - SnovaParameters.SNOVA_56_25_16_2_SHAKE_ESK, - SnovaParameters.SNOVA_56_25_16_2_SHAKE_SSK, - SnovaParameters.SNOVA_56_25_16_2_SSK, - SnovaParameters.SNOVA_60_10_16_4_ESK, - SnovaParameters.SNOVA_60_10_16_4_SHAKE_ESK, - SnovaParameters.SNOVA_60_10_16_4_SHAKE_SSK, - SnovaParameters.SNOVA_60_10_16_4_SSK, - SnovaParameters.SNOVA_66_15_16_3_ESK, - SnovaParameters.SNOVA_66_15_16_3_SHAKE_ESK, - SnovaParameters.SNOVA_66_15_16_3_SHAKE_SSK, - SnovaParameters.SNOVA_66_15_16_3_SSK, - SnovaParameters.SNOVA_75_33_16_2_ESK, - SnovaParameters.SNOVA_75_33_16_2_SHAKE_ESK, - SnovaParameters.SNOVA_75_33_16_2_SHAKE_SSK, - SnovaParameters.SNOVA_75_33_16_2_SSK, + SnovaParameters.SNOVA_24_5_4_ESK, + SnovaParameters.SNOVA_24_5_4_SHAKE_ESK, + SnovaParameters.SNOVA_24_5_4_SHAKE_SSK, + SnovaParameters.SNOVA_24_5_4_SSK, + SnovaParameters.SNOVA_24_5_5_ESK, + SnovaParameters.SNOVA_24_5_5_SHAKE_ESK, + SnovaParameters.SNOVA_24_5_5_SHAKE_SSK, + SnovaParameters.SNOVA_24_5_5_SSK, + SnovaParameters.SNOVA_25_8_3_ESK, + SnovaParameters.SNOVA_25_8_3_SHAKE_ESK, + SnovaParameters.SNOVA_25_8_3_SHAKE_SSK, + SnovaParameters.SNOVA_25_8_3_SSK, + SnovaParameters.SNOVA_29_6_5_ESK, + SnovaParameters.SNOVA_29_6_5_SHAKE_ESK, + SnovaParameters.SNOVA_29_6_5_SHAKE_SSK, + SnovaParameters.SNOVA_29_6_5_SSK, + SnovaParameters.SNOVA_37_8_4_ESK, + SnovaParameters.SNOVA_37_8_4_SHAKE_ESK, + SnovaParameters.SNOVA_37_8_4_SHAKE_SSK, + SnovaParameters.SNOVA_37_8_4_SSK, + SnovaParameters.SNOVA_37_17_2_ESK, + SnovaParameters.SNOVA_37_17_2_SHAKE_ESK, + SnovaParameters.SNOVA_37_17_2_SHAKE_SSK, + SnovaParameters.SNOVA_37_17_2_SSK, + SnovaParameters.SNOVA_49_11_3_ESK, + SnovaParameters.SNOVA_49_11_3_SHAKE_ESK, + SnovaParameters.SNOVA_49_11_3_SHAKE_SSK, + SnovaParameters.SNOVA_49_11_3_SSK, + SnovaParameters.SNOVA_56_25_2_ESK, + SnovaParameters.SNOVA_56_25_2_SHAKE_ESK, + SnovaParameters.SNOVA_56_25_2_SHAKE_SSK, + SnovaParameters.SNOVA_56_25_2_SSK, + SnovaParameters.SNOVA_60_10_4_ESK, + SnovaParameters.SNOVA_60_10_4_SHAKE_ESK, + SnovaParameters.SNOVA_60_10_4_SHAKE_SSK, + SnovaParameters.SNOVA_60_10_4_SSK, + SnovaParameters.SNOVA_66_15_3_ESK, + SnovaParameters.SNOVA_66_15_3_SHAKE_ESK, + SnovaParameters.SNOVA_66_15_3_SHAKE_SSK, + SnovaParameters.SNOVA_66_15_3_SSK, + SnovaParameters.SNOVA_75_33_2_ESK, + SnovaParameters.SNOVA_75_33_2_SHAKE_ESK, + SnovaParameters.SNOVA_75_33_2_SHAKE_SSK, + SnovaParameters.SNOVA_75_33_2_SSK, }; private static final String[] files = new String[]{ diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/SnovaKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/SnovaKey.java new file mode 100644 index 0000000000..0615e1acc7 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/SnovaKey.java @@ -0,0 +1,16 @@ +package org.bouncycastle.pqc.jcajce.interfaces; + +import java.security.Key; + +import org.bouncycastle.pqc.jcajce.spec.SnovaParameterSpec; + +public interface SnovaKey + extends Key +{ + /** + * Return the parameters for this key. + * + * @return a SnovaParameterSpec + */ + SnovaParameterSpec getParameterSpec(); +} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/BCSnovaPrivateKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/BCSnovaPrivateKey.java new file mode 100644 index 0000000000..2bda389890 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/BCSnovaPrivateKey.java @@ -0,0 +1,5 @@ +package org.bouncycastle.pqc.jcajce.provider.snova; + +public class BCSnovaPrivateKey +{ +} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/spec/SnovaParameterSpec.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/spec/SnovaParameterSpec.java new file mode 100644 index 0000000000..2e72629f24 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/spec/SnovaParameterSpec.java @@ -0,0 +1,145 @@ +package org.bouncycastle.pqc.jcajce.spec; + +import java.security.spec.AlgorithmParameterSpec; +import java.util.HashMap; +import java.util.Map; + +import org.bouncycastle.pqc.crypto.snova.SnovaParameters; +import org.bouncycastle.util.Strings; + +public class SnovaParameterSpec + implements AlgorithmParameterSpec +{ + public static final SnovaParameterSpec SNOVA_24_5_4_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_24_5_4_SSK); + public static final SnovaParameterSpec SNOVA_24_5_4_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_24_5_4_ESK); + public static final SnovaParameterSpec SNOVA_24_5_4_SHAKE_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_24_5_4_SHAKE_SSK); + public static final SnovaParameterSpec SNOVA_24_5_4_SHAKE_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_24_5_4_SHAKE_ESK); + + public static final SnovaParameterSpec SNOVA_24_5_5_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_24_5_5_SSK); + public static final SnovaParameterSpec SNOVA_24_5_5_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_24_5_5_ESK); + public static final SnovaParameterSpec SNOVA_24_5_5_SHAKE_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_24_5_5_SHAKE_SSK); + public static final SnovaParameterSpec SNOVA_24_5_5_SHAKE_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_24_5_5_SHAKE_ESK); + + public static final SnovaParameterSpec SNOVA_25_8_3_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_25_8_3_SSK); + public static final SnovaParameterSpec SNOVA_25_8_3_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_25_8_3_ESK); + public static final SnovaParameterSpec SNOVA_25_8_3_SHAKE_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_25_8_3_SHAKE_SSK); + public static final SnovaParameterSpec SNOVA_25_8_3_SHAKE_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_25_8_3_SHAKE_ESK); + + public static final SnovaParameterSpec SNOVA_29_6_5_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_29_6_5_SSK); + public static final SnovaParameterSpec SNOVA_29_6_5_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_29_6_5_ESK); + public static final SnovaParameterSpec SNOVA_29_6_5_SHAKE_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_29_6_5_SHAKE_SSK); + public static final SnovaParameterSpec SNOVA_29_6_5_SHAKE_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_29_6_5_SHAKE_ESK); + + public static final SnovaParameterSpec SNOVA_37_8_4_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_37_8_4_SSK); + public static final SnovaParameterSpec SNOVA_37_8_4_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_37_8_4_ESK); + public static final SnovaParameterSpec SNOVA_37_8_4_SHAKE_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_37_8_4_SHAKE_SSK); + public static final SnovaParameterSpec SNOVA_37_8_4_SHAKE_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_37_8_4_SHAKE_ESK); + + public static final SnovaParameterSpec SNOVA_37_17_2_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_37_17_2_SSK); + public static final SnovaParameterSpec SNOVA_37_17_2_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_37_17_2_ESK); + public static final SnovaParameterSpec SNOVA_37_17_2_SHAKE_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_37_17_2_SHAKE_SSK); + public static final SnovaParameterSpec SNOVA_37_17_2_SHAKE_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_37_17_2_SHAKE_ESK); + + public static final SnovaParameterSpec SNOVA_49_11_3_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_49_11_3_SSK); + public static final SnovaParameterSpec SNOVA_49_11_3_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_49_11_3_ESK); + public static final SnovaParameterSpec SNOVA_49_11_3_SHAKE_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_49_11_3_SHAKE_SSK); + public static final SnovaParameterSpec SNOVA_49_11_3_SHAKE_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_49_11_3_SHAKE_ESK); + + public static final SnovaParameterSpec SNOVA_56_25_2_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_56_25_2_SSK); + public static final SnovaParameterSpec SNOVA_56_25_2_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_56_25_2_ESK); + public static final SnovaParameterSpec SNOVA_56_25_2_SHAKE_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_56_25_2_SHAKE_SSK); + public static final SnovaParameterSpec SNOVA_56_25_2_SHAKE_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_56_25_2_SHAKE_ESK); + + public static final SnovaParameterSpec SNOVA_60_10_4_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_60_10_4_SSK); + public static final SnovaParameterSpec SNOVA_60_10_4_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_60_10_4_ESK); + public static final SnovaParameterSpec SNOVA_60_10_4_SHAKE_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_60_10_4_SHAKE_SSK); + public static final SnovaParameterSpec SNOVA_60_10_4_SHAKE_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_60_10_4_SHAKE_ESK); + + public static final SnovaParameterSpec SNOVA_66_15_3_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_66_15_3_SSK); + public static final SnovaParameterSpec SNOVA_66_15_3_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_66_15_3_ESK); + public static final SnovaParameterSpec SNOVA_66_15_3_SHAKE_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_66_15_3_SHAKE_SSK); + public static final SnovaParameterSpec SNOVA_66_15_3_SHAKE_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_66_15_3_SHAKE_ESK); + + public static final SnovaParameterSpec SNOVA_75_33_2_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_75_33_2_SSK); + public static final SnovaParameterSpec SNOVA_75_33_2_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_75_33_2_ESK); + public static final SnovaParameterSpec SNOVA_75_33_2_SHAKE_SSK = new SnovaParameterSpec(SnovaParameters.SNOVA_75_33_2_SHAKE_SSK); + public static final SnovaParameterSpec SNOVA_75_33_2_SHAKE_ESK = new SnovaParameterSpec(SnovaParameters.SNOVA_75_33_2_SHAKE_ESK); + + + private static Map parameters = new HashMap(); + + static + { + parameters.put("SNOVA_24_5_4_SSK", SNOVA_24_5_4_SSK); + parameters.put("SNOVA_24_5_4_ESK", SNOVA_24_5_4_ESK); + parameters.put("SNOVA_24_5_4_SHAKE_SSK", SNOVA_24_5_4_SHAKE_SSK); + parameters.put("SNOVA_24_5_4_SHAKE_ESK", SNOVA_24_5_4_SHAKE_ESK); + + parameters.put("SNOVA_24_5_5_SSK", SNOVA_24_5_5_SSK); + parameters.put("SNOVA_24_5_5_ESK", SNOVA_24_5_5_ESK); + parameters.put("SNOVA_24_5_5_SHAKE_SSK", SNOVA_24_5_5_SHAKE_SSK); + parameters.put("SNOVA_24_5_5_SHAKE_ESK", SNOVA_24_5_5_SHAKE_ESK); + + parameters.put("SNOVA_25_8_3_SSK", SNOVA_25_8_3_SSK); + parameters.put("SNOVA_25_8_3_ESK", SNOVA_25_8_3_ESK); + parameters.put("SNOVA_25_8_3_SHAKE_SSK", SNOVA_25_8_3_SHAKE_SSK); + parameters.put("SNOVA_25_8_3_SHAKE_ESK", SNOVA_25_8_3_SHAKE_ESK); + + parameters.put("SNOVA_29_6_5_SSK", SNOVA_29_6_5_SSK); + parameters.put("SNOVA_29_6_5_ESK", SNOVA_29_6_5_ESK); + parameters.put("SNOVA_29_6_5_SHAKE_SSK", SNOVA_29_6_5_SHAKE_SSK); + parameters.put("SNOVA_29_6_5_SHAKE_ESK", SNOVA_29_6_5_SHAKE_ESK); + + parameters.put("SNOVA_37_8_4_SSK", SNOVA_37_8_4_SSK); + parameters.put("SNOVA_37_8_4_ESK", SNOVA_37_8_4_ESK); + parameters.put("SNOVA_37_8_4_SHAKE_SSK", SNOVA_37_8_4_SHAKE_SSK); + parameters.put("SNOVA_37_8_4_SHAKE_ESK", SNOVA_37_8_4_SHAKE_ESK); + + parameters.put("SNOVA_37_17_2_SSK", SNOVA_37_17_2_SSK); + parameters.put("SNOVA_37_17_2_ESK", SNOVA_37_17_2_ESK); + parameters.put("SNOVA_37_17_2_SHAKE_SSK", SNOVA_37_17_2_SHAKE_SSK); + parameters.put("SNOVA_37_17_2_SHAKE_ESK", SNOVA_37_17_2_SHAKE_ESK); + + parameters.put("SNOVA_49_11_3_SSK", SNOVA_49_11_3_SSK); + parameters.put("SNOVA_49_11_3_ESK", SNOVA_49_11_3_ESK); + parameters.put("SNOVA_49_11_3_SHAKE_SSK", SNOVA_49_11_3_SHAKE_SSK); + parameters.put("SNOVA_49_11_3_SHAKE_ESK", SNOVA_49_11_3_SHAKE_ESK); + + parameters.put("SNOVA_56_25_2_SSK", SNOVA_56_25_2_SSK); + parameters.put("SNOVA_56_25_2_ESK", SNOVA_56_25_2_ESK); + parameters.put("SNOVA_56_25_2_SHAKE_SSK", SNOVA_56_25_2_SHAKE_SSK); + parameters.put("SNOVA_56_25_2_SHAKE_ESK", SNOVA_56_25_2_SHAKE_ESK); + + parameters.put("SNOVA_60_10_4_SSK", SNOVA_60_10_4_SSK); + parameters.put("SNOVA_60_10_4_ESK", SNOVA_60_10_4_ESK); + parameters.put("SNOVA_60_10_4_SHAKE_SSK", SNOVA_60_10_4_SHAKE_SSK); + parameters.put("SNOVA_60_10_4_SHAKE_ESK", SNOVA_60_10_4_SHAKE_ESK); + + parameters.put("SNOVA_66_15_3_SSK", SNOVA_66_15_3_SSK); + parameters.put("SNOVA_66_15_3_ESK", SNOVA_66_15_3_ESK); + parameters.put("SNOVA_66_15_3_SHAKE_SSK", SNOVA_66_15_3_SHAKE_SSK); + parameters.put("SNOVA_66_15_3_SHAKE_ESK", SNOVA_66_15_3_SHAKE_ESK); + + parameters.put("SNOVA_75_33_2_SSK", SNOVA_75_33_2_SSK); + parameters.put("SNOVA_75_33_2_ESK", SNOVA_75_33_2_ESK); + parameters.put("SNOVA_75_33_2_SHAKE_SSK", SNOVA_75_33_2_SHAKE_SSK); + parameters.put("SNOVA_75_33_2_SHAKE_ESK", SNOVA_75_33_2_SHAKE_ESK); + } + + private final String name; + + private SnovaParameterSpec(SnovaParameters parameters) + { + this.name = parameters.getName(); + } + + public String getName() + { + return name; + } + + public static SnovaParameterSpec fromName(String name) + { + return (SnovaParameterSpec)parameters.get(Strings.toLowerCase(name)); + } +} From e23335e372f358ba83e6c4719377a89b63fddceb Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 3 Apr 2025 14:39:50 +1030 Subject: [PATCH 294/890] Refactor on Snova is almost done --- .../pqc/crypto/snova/GF16Utils.java | 102 ++++++++- .../pqc/crypto/snova/MapGroup1.java | 65 +----- .../pqc/crypto/snova/MapGroup2.java | 2 +- .../pqc/crypto/snova/SnovaEngine.java | 120 ++++++++--- .../pqc/crypto/snova/SnovaKeyElements.java | 4 +- .../crypto/snova/SnovaKeyPairGenerator.java | 16 +- .../pqc/crypto/snova/SnovaParameters.java | 81 +------ .../pqc/crypto/snova/SnovaSigner.java | 199 ++++++++---------- .../main/java/org/bouncycastle/util/GF16.java | 10 - .../jcajce/provider/mayo/BCMayoPublicKey.java | 2 +- .../mayo/MayoKeyPairGeneratorSpi.java | 2 - .../provider/snova/BCSnovaPrivateKey.java | 127 +++++++++++ .../provider/snova/BCSnovaPublicKey.java | 128 +++++++++++ 13 files changed, 543 insertions(+), 315 deletions(-) create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/BCSnovaPublicKey.java diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java index 34e34b278c..260e1da6d9 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java @@ -30,13 +30,55 @@ public static void decodeMergeInHalf(byte[] byteArray, byte[] gf16Array, int nGf } } - public static void gf16mTranMul(byte[] a, byte[] b, byte[] c, int rank) + public static void gf16mTranMulMul(byte[] sign, byte[] a, byte[] b, byte[] q1, byte[] q2, byte[] tmp, + byte[] left, byte[] right, int rank) { - for (int i = 0, cOff = 0; i < rank; i++) + for (int i = 0, leftOff = 0, dOff = 0; i < rank; i++, leftOff += rank) { - for (int j = 0, jl = 0; j < rank; j++, jl += rank) + for (int j = 0; j < rank; j++) + { + byte result = 0; + for (int k = 0, aOff = j, bOff = i; k < rank; ++k, aOff += rank, bOff += rank) + { + result ^= GF16.mul(sign[aOff], q1[bOff]); + } + tmp[j] = result; + } + + for (int j = 0, jxl = 0; j < rank; j++, jxl += rank) + { + byte result = 0; + for (int k = 0; k < rank; ++k) + { + result ^= GF16.mul(a[jxl + k], tmp[k]); + } + left[i + jxl] = result; + } + for (int j = 0; j < rank; j++) + { + tmp[j] = GF16.innerProduct(q2, leftOff, sign, j, rank); + } + + for (int j = 0; j < rank; j++) { - c[cOff++] = GF16.dotProduct(a, i, b, j, rank); + right[dOff++] = GF16.innerProduct(tmp, 0, b, j, rank); + } + } + } + + // tmp = a * b, d = tmp * c -> d = (a * b) * c + public static void gf16mMulMul(byte[] a, byte[] b, byte[] c, byte[] tmp, byte[] d, int rank) + { + for (int i = 0, leftOff = 0, dOff = 0; i < rank; i++, leftOff += rank) + { + for (int j = 0; j < rank; j++) + { + tmp[j] = GF16.innerProduct(a, leftOff, b, j, rank); + } + + for (int j = 0; j < rank; j++) + { + d[dOff++] = GF16.innerProduct(tmp, 0, c, j, rank); } } } @@ -52,6 +94,22 @@ public static void gf16mMul(byte[] a, byte[] b, byte[] c, int rank) } } + public static void gf16mMulMulTo(byte[] a, byte[] b, byte[] c, byte[] tmp, byte[] d, int rank) + { + for (int i = 0, leftOff = 0, dOff = 0; i < rank; i++, leftOff += rank) + { + for (int j = 0; j < rank; j++) + { + tmp[j] = GF16.innerProduct(a, leftOff, b, j, rank); + } + + for (int j = 0; j < rank; j++) + { + d[dOff++] ^= GF16.innerProduct(tmp, 0, c, j, rank); + } + } + } + public static void gf16mMulTo(byte[] a, byte[] b, byte[] c, int rank) { for (int i = 0, aOff = 0, cOff = 0; i < rank; i++, aOff += rank) @@ -63,6 +121,42 @@ public static void gf16mMulTo(byte[] a, byte[] b, byte[] c, int rank) } } + // d = a * b, e = b * c + public static void gf16mMulToTo(byte[] a, byte[] b, byte[] c, byte[] d, byte[] e, int rank) + { + for (int i = 0, leftOff = 0, outOff = 0; i < rank; i++, leftOff += rank) + { + for (int j = 0; j < rank; j++) + { + d[outOff] ^= GF16.innerProduct(a, leftOff, b, j, rank); + e[outOff++] ^= GF16.innerProduct(b, leftOff, c, j, rank); + } + } + } + + public static void gf16mMulTo(byte[] a, byte[] b, byte[] c, int cOff, int rank) + { + for (int i = 0, aOff = 0; i < rank; i++, aOff += rank) + { + for (int j = 0; j < rank; j++) + { + c[cOff++] ^= GF16.innerProduct(a, aOff, b, j, rank); + } + } + } + + // d ^= a * b + c * d + public static void gf16mMulTo(byte[] a, byte[] b, byte[] c, byte[] d, byte[] e, int eOff, int rank) + { + for (int i = 0, leftOff = 0; i < rank; i++, leftOff += rank) + { + for (int j = 0; j < rank; j++) + { + e[eOff++] ^= GF16.innerProduct(a, leftOff, b, j, rank) ^ GF16.innerProduct(c, leftOff, d, j, rank); + } + } + } + public static void gf16mMulTo(byte[] a, byte[] b, int bOff, byte[] c, int cOff, int rank) { for (int i = 0, aOff = 0; i < rank; i++, aOff += rank) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java index 007ee0eb33..ff4e7531ee 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup1.java @@ -28,16 +28,9 @@ public MapGroup1(SnovaParameters params) qAlpha2 = new byte[m][alpha][lsq]; } - public void decode(byte[] input, int len, boolean isl4or5) + void decode(byte[] input, int len, boolean isl4or5) { -// int m = params.getM(); -// int v = params.getV(); -// int o = params.getO(); -// int alpha = params.getAlpha(); -// int lsq = params.getLsq(); -// if ((lsq & 1) == 0) -// { - + //TODO: when (lsq & 1) == 1 int inOff = decodeP(input, 0, p11, len); inOff += decodeP(input, inOff, p12, len - inOff); inOff += decodeP(input, inOff, p21, len - inOff); @@ -48,38 +41,8 @@ public void decode(byte[] input, int len, boolean isl4or5) inOff += decodeAlpha(input, inOff, qAlpha1, len - inOff); decodeAlpha(input, inOff, qAlpha2, len - inOff); } -// } -// else -// { -// -// } } -// public boolean decodeArrayLsqOdd(byte[] input, int inOff, boolean isLower, byte[] output, int lsqHalf) -// { -// int outOff = 0; -// if (isLower) -// { -// for (int i = 0; i < lsqHalf; ++i) -// { -// output[outOff++] = (byte)(input[inOff] & 0x0F); -// output[outOff++] = (byte)((input[inOff++] >>> 4) & 0x0F); -// } -// output[outOff] = (byte)(input[inOff] & 0x0F); -// return false; -// } -// else -// { -// for (int i = 0; i < lsqHalf; ++i) -// { -// output[outOff++] = (byte)((input[inOff++] >>> 4) & 0x0F); -// output[outOff++] = (byte)(input[inOff] & 0x0F); -// } -// output[outOff] = (byte)((input[inOff] >>> 4) & 0x0F); -// return true; -// } -// } - static int decodeP(byte[] input, int inOff, byte[][][][] p, int len) { int rlt = 0; @@ -114,29 +77,7 @@ static int decodeArray(byte[] input, int inOff, byte[][] array, int len) return rlt; } -// private int decodeP(byte[] input, int inOff, boolean isLower,byte[][][][] p, int lsqHalf) -// { -// for (int i = 0; i < p.length; ++i) -// { -// inOff = decodeAlpha(input, inOff, p[i]); -// } -// return inOff; -// } - -// private boolean decodeAlpha(byte[] input, int inOff, boolean isLower, byte[][][] alpha, int lsqHalf) -// { -// for (int i = 0; i < alpha.length; ++i) -// { -// for (int j = 0; j < alpha[i].length; ++j) -// { -// isLower = decodeArrayLsqOdd(input, inOff, isLower, alpha[i][j], lsqHalf); -// inOff += lsqHalf + (isLower ? 1 : 0); -// } -// } -// return isLower; -// } - - public void fill(byte[] input, boolean isl4or5) + void fill(byte[] input, boolean isl4or5) { int inOff = fillP(input, 0, p11, input.length); inOff += fillP(input, inOff, p12, input.length - inOff); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java index 359accce3a..946ad2ab99 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/MapGroup2.java @@ -11,7 +11,7 @@ public MapGroup2(SnovaParameters params) int m = params.getM(); int v = params.getV(); int o = params.getO(); - int lsq = params.getL() * params.getL(); + int lsq = params.getLsq(); f11 = new byte[m][v][v][lsq]; f12 = new byte[m][v][o][lsq]; f21 = new byte[m][o][v][lsq]; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 68edcb658c..24ec8ce949 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -1,6 +1,8 @@ package org.bouncycastle.pqc.crypto.snova; -import org.bouncycastle.crypto.BlockCipher; +import java.util.HashMap; +import java.util.Map; + import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.crypto.engines.AESEngine; import org.bouncycastle.crypto.modes.CTRModeCipher; @@ -13,6 +15,9 @@ class SnovaEngine { + private final static Map fixedAbqSet = new HashMap();//key is o + private final static Map sSet = new HashMap(); //key is l + private final static Map xSSet = new HashMap(); //key is l private final SnovaParameters params; private final int l; private final int lsq; @@ -34,11 +39,79 @@ public SnovaEngine(SnovaParameters params) this.o = params.getO(); this.alpha = params.getAlpha(); this.n = params.getN(); - S = SnovaParameters.sSet.get(l); - xS = SnovaParameters.xSSet.get(l); + if (!xSSet.containsKey(l)) + { + byte[][] S = new byte[l][lsq]; + int[][] xS = new int[l][lsq]; + be_aI(S[0], 0, (byte)1); + beTheS(S[1]); + for (int index = 2; index < l; ++index) + { + GF16Utils.gf16mMul(S[index - 1], S[1], S[index], l); + } + + for (int index = 0; index < l; ++index) + { + for (int ij = 0; ij < lsq; ++ij) + { + xS[index][ij] = GF16Utils.gf16FromNibble(S[index][ij]); + } + } + sSet.put(l, S); + xSSet.put(l, xS); + } + S = sSet.get(l); + xS = xSSet.get(l); + if (l < 4 && !fixedAbqSet.containsKey(o)) + { + int alphaxl = alpha * l; + int alphaxlsq = alphaxl * l; + int oxalphaxl = o * alphaxl; + int oxalphaxlsq = o * alphaxlsq; + byte[] fixedAbq = new byte[oxalphaxlsq << 2]; + byte[] rngOut = new byte[oxalphaxlsq + oxalphaxl]; + byte[] q12 = new byte[oxalphaxl << 2]; + byte[] seed = "SNOVA_ABQ".getBytes(); + SHAKEDigest shake = new SHAKEDigest(256); + shake.update(seed, 0, seed.length); + shake.doFinal(rngOut, 0, rngOut.length); + GF16.decode(rngOut, fixedAbq, oxalphaxlsq << 1); + GF16.decode(rngOut, alphaxlsq, q12, 0, oxalphaxl << 1); + // Post-processing for invertible matrices + for (int pi = 0, pixAlphaxlsq = 0, pixalphaxl = 0; pi < o; ++pi, pixAlphaxlsq += alphaxlsq, pixalphaxl += alphaxl) + { + for (int a = 0, axl = pixalphaxl, axlsq = pixAlphaxlsq; a < alpha; ++a, axl += l, axlsq += lsq) + { + makeInvertibleByAddingAS(fixedAbq, axlsq); + makeInvertibleByAddingAS(fixedAbq, oxalphaxlsq + axlsq); + genAFqS(q12, axl, fixedAbq, (oxalphaxlsq << 1) + axlsq); + genAFqS(q12, oxalphaxl + axl, fixedAbq, (oxalphaxlsq << 1) + oxalphaxlsq + axlsq); + } + } + fixedAbqSet.put(o, fixedAbq); + } + } + + private void beTheS(byte[] target) + { + // Set all elements to 8 - (i + j) in GF16 (4-bit values) + for (int i = 0, il = 0; i < l; ++i, il += l) + { + for (int j = 0; j < l; ++j) + { + int value = 8 - (i + j); + target[il + j] = (byte)(value & 0x0F); // Mask to 4 bits + } + } + + // Special case for rank 5 + if (l == 5) + { + target[24] = (byte)9; // Set (4,4) to 9 + } } - static void be_aI(byte[] target, int off, byte a, int l) + private void be_aI(byte[] target, int off, byte a) { // Ensure 'a' iss a valid 4-bit GF16 element int l1 = l + 1; @@ -49,7 +122,7 @@ static void be_aI(byte[] target, int off, byte a, int l) } // Constant-time GF16 matrix generation - public void genAFqSCT(byte[] c, int cOff, byte[] ptMatrix) + private void genAFqSCT(byte[] c, int cOff, byte[] ptMatrix) { int[] xTemp = new int[lsq]; int l1 = l + 1; @@ -83,7 +156,7 @@ public void genAFqSCT(byte[] c, int cOff, byte[] ptMatrix) Arrays.fill(xTemp, 0); // Secure clear } - public void makeInvertibleByAddingAS(byte[] source, int off) + private void makeInvertibleByAddingAS(byte[] source, int off) { if (gf16Determinant(source, off) != 0) { @@ -283,10 +356,10 @@ private void generateASMatrixTo(byte[] target, int off, byte a) } } - public void genAFqS(byte[] c, int cOff, byte[] ptMatrix, int off) + private void genAFqS(byte[] c, int cOff, byte[] ptMatrix, int off) { // Initialize with be_aI - be_aI(ptMatrix, off, c[cOff], l); + be_aI(ptMatrix, off, c[cOff]); // Process middle terms for (int i = 1; i < l - 1; ++i) @@ -310,7 +383,7 @@ private void gf16mScaleTo(byte[] a, byte k, byte[] c, int cOff) } } - public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) + private void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) { // Copy initial matrices copy4DMatrix(map1.p11, map2.f11, m, v, v, lsq); @@ -325,10 +398,7 @@ public void genF(MapGroup2 map2, MapGroup1 map1, byte[][][] T12) { for (int index = 0; index < v; index++) { - // First matrix operation sequence - GF16Utils.gf16mMulTo(map1.p11[i][j][index], T12[index][k], map2.f12[i][j][k], l); - // Second matrix operation sequence - GF16Utils.gf16mMulTo(T12[index][k], map1.p11[i][index][j], map2.f21[i][k][j], l); + GF16Utils.gf16mMulToTo(map1.p11[i][j][index], T12[index][k], map1.p11[i][index][j], map2.f12[i][j][k], map2.f21[i][k][j], l); } } } @@ -362,14 +432,10 @@ public void genP22(byte[] outP22, int outOff, byte[][][] T12, byte[][][][] P21, { for (int k = 0, kxlsq = jxoxlsq; k < o; k++, kxlsq += lsq) { - //int idx = ((i * o + j) * o + k) * lsq; for (int index = 0; index < v; index++) { - // P22[i][j][k] ^= T12[index][j] * F12[i][index][k] - GF16Utils.gf16mMulTo(T12[index][j], F12[i][index][k], 0, P22, kxlsq, l); - - // P22[i][j][k] ^= P21[i][j][index] * T12[index][k] - GF16Utils.gf16mMulTo(P21[i][j][index], T12[index][k], 0, P22, kxlsq, l); + // P22[i][j][k] ^= (T12[index][j] * F12[i][index][k]) ^ (P21[i][j][index] * T12[index][k]) + GF16Utils.gf16mMulTo(T12[index][j], F12[i][index][k], P21[i][j][index], T12[index][k], P22, kxlsq, l); } } } @@ -379,7 +445,7 @@ public void genP22(byte[] outP22, int outOff, byte[][][] T12, byte[][][][] P21, GF16.encode(P22, outP22, outOff, P22.length); } - void genSeedsAndT12(byte[][][] T12, byte[] skSeed) + private void genSeedsAndT12(byte[][][] T12, byte[] skSeed) { int gf16sPrngPrivate = v * o * l; int bytesPrngPrivate = (gf16sPrngPrivate + 1) >>> 1; @@ -407,7 +473,7 @@ void genSeedsAndT12(byte[][][] T12, byte[] skSeed) } } - void genABQP(MapGroup1 map1, byte[] pkSeed) + public void genABQP(MapGroup1 map1, byte[] pkSeed) { int gf16sPrngPublic = lsq * (2 * m * alpha + m * (n * n - m * m)) + l * 2 * m * alpha; byte[] qTemp = new byte[(m * alpha * l) << 1]; @@ -445,11 +511,9 @@ void genABQP(MapGroup1 map1, byte[] pkSeed) byte[] iv = new byte[16]; // automatically zero-initialized // AES-CTR-based expansion // Set up AES engine in CTR (SIC) mode. - BlockCipher aesEngine = AESEngine.newInstance(); // SICBlockCipher implements CTR mode for AES. - CTRModeCipher ctrCipher = SICBlockCipher.newInstance(aesEngine); - ParametersWithIV params = new ParametersWithIV(new KeyParameter(pkSeed), iv); - ctrCipher.init(true, params); + CTRModeCipher ctrCipher = SICBlockCipher.newInstance(AESEngine.newInstance()); + ctrCipher.init(true, new ParametersWithIV(new KeyParameter(pkSeed), iv)); int blockSize = ctrCipher.getBlockSize(); // typically 16 bytes byte[] zeroBlock = new byte[blockSize]; // block of zeros @@ -491,15 +555,15 @@ void genABQP(MapGroup1 map1, byte[] pkSeed) makeInvertibleByAddingAS(map1.aAlpha[pi][a], 0); makeInvertibleByAddingAS(map1.bAlpha[pi][a], 0); genAFqS(qTemp, ptArray, map1.qAlpha1[pi][a], 0); - ptArray += l; genAFqS(qTemp, offset, map1.qAlpha2[pi][a], 0); + ptArray += l; offset += l; } } } else { - byte[] fixedAbq = SnovaParameters.fixedAbqSet.get(o); + byte[] fixedAbq = fixedAbqSet.get(o); MapGroup1.fillAlpha(fixedAbq, 0, map1.aAlpha, m * o * alpha * lsq); MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq, map1.bAlpha, (m - 1) * o * alpha * lsq); MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq * 2, map1.qAlpha1, (m - 2) * o * alpha * lsq); @@ -507,7 +571,7 @@ void genABQP(MapGroup1 map1, byte[] pkSeed) } } - void genMap1T12Map2(SnovaKeyElements keyElements, byte[] pkSeed, byte[] skSeed) + public void genMap1T12Map2(SnovaKeyElements keyElements, byte[] pkSeed, byte[] skSeed) { // Generate T12 matrix genSeedsAndT12(keyElements.T12, skSeed); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java index b26a752f7d..0915e590e2 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyElements.java @@ -5,14 +5,12 @@ class SnovaKeyElements public final MapGroup1 map1; public final byte[][][] T12; // [v][o] public final MapGroup2 map2; - public byte[] ptPrivateKeySeed; public SnovaKeyElements(SnovaParameters params) { int o = params.getO(); - int l = params.getL(); int v = params.getV(); - int lsq = l * l; + int lsq = params.getLsq(); map1 = new MapGroup1(params); T12 = new byte[v][o][lsq]; map2 = new MapGroup2(params); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java index eadcf41252..5eb5c11c38 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaKeyPairGenerator.java @@ -48,7 +48,11 @@ public AsymmetricCipherKeyPair generateKeyPair() SnovaKeyElements keyElements = new SnovaKeyElements(params); System.arraycopy(ptPublicKeySeed, 0, pk, 0, ptPublicKeySeed.length); - generateKeysCore(keyElements, ptPublicKeySeed, ptPrivateKeySeed, pk, ptPublicKeySeed.length); + engine.genMap1T12Map2(keyElements, ptPublicKeySeed, ptPrivateKeySeed); + + // Generate P22 matrix + engine.genP22(pk, ptPublicKeySeed.length, keyElements.T12, keyElements.map1.p21, keyElements.map2.f12); + // Pack public key components System.arraycopy(ptPublicKeySeed, 0, pk, 0, ptPublicKeySeed.length); @@ -63,7 +67,7 @@ public AsymmetricCipherKeyPair generateKeyPair() int lsq = params.getLsq(); int v = params.getV(); int length = o * params.getAlpha() * lsq * 4 + v * o * lsq + (o * v * v + o * v * o + o * o * v) * lsq; - //keyElements.encodeMergerInHalf(sk); + byte[] input = new byte[length]; int inOff = 0; inOff = SnovaKeyElements.copy3d(keyElements.map1.aAlpha, input, inOff); @@ -83,12 +87,4 @@ public AsymmetricCipherKeyPair generateKeyPair() new SnovaPrivateKeyParameters(params, sk) ); } - - private void generateKeysCore(SnovaKeyElements keyElements, byte[] pkSeed, byte[] skSeed, byte[] p22, int p22Off) - { - engine.genMap1T12Map2(keyElements, pkSeed, skSeed); - - // Generate P22 matrix - engine.genP22(p22, p22Off, keyElements.T12, keyElements.map1.p21, keyElements.map2.f12); - } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java index 8ece75e892..fcce21df1c 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaParameters.java @@ -1,16 +1,7 @@ package org.bouncycastle.pqc.crypto.snova; -import java.util.HashMap; -import java.util.Map; - -import org.bouncycastle.crypto.digests.SHAKEDigest; -import org.bouncycastle.util.GF16; - public class SnovaParameters { - static Map fixedAbqSet = new HashMap();//key is o - static Map sSet = new HashMap(); //key is l - static Map xSSet = new HashMap(); public static final SnovaParameters SNOVA_24_5_4_SSK = new SnovaParameters("SNOVA_24_5_4_SSK", 24, 5, 4, true, false); @@ -126,7 +117,7 @@ public class SnovaParameters private final boolean skIsSeed; private final boolean pkExpandShake; - public SnovaParameters(String name, int v, int o, int l, boolean skIsSeed, boolean pkExpandShake) + private SnovaParameters(String name, int v, int o, int l, boolean skIsSeed, boolean pkExpandShake) { this.name = name; this.v = v; @@ -136,53 +127,6 @@ public SnovaParameters(String name, int v, int o, int l, boolean skIsSeed, boole this.alpha = lsq + l; this.skIsSeed = skIsSeed; this.pkExpandShake = pkExpandShake; - if (!xSSet.containsKey(l)) - { - byte[][] S = new byte[l][lsq]; - int[][] xS = new int[l][lsq]; - SnovaEngine.be_aI(S[0], 0, (byte)1, l); - beTheS(S[1]); - for (int index = 2; index < l; ++index) - { - GF16Utils.gf16mMul(S[index - 1], S[1], S[index], l); - } - - for (int index = 0; index < l; ++index) - { - for (int ij = 0; ij < lsq; ++ij) - { - xS[index][ij] = GF16Utils.gf16FromNibble(S[index][ij]); - } - } - sSet.put(l, S); - xSSet.put(l, xS); - } - if (l < 4 && !fixedAbqSet.containsKey(o)) - { - SnovaEngine engine = new SnovaEngine(this); - byte[] fixedAbq = new byte[4 * o * alpha * lsq]; - //genABQ(byte[] abqSeed) - byte[] rngOut = new byte[o * alpha * (lsq + l)]; - byte[] q12 = new byte[2 * o * alpha * l]; - byte[] seed = "SNOVA_ABQ".getBytes(); - SHAKEDigest shake = new SHAKEDigest(256); - shake.update(seed, 0, seed.length); - shake.doFinal(rngOut, 0, rngOut.length); - GF16.decode(rngOut, fixedAbq, 2 * o * alpha * lsq); - GF16.decode(rngOut, alpha * lsq, q12, 0, 2 * o * alpha * l); - // Post-processing for invertible matrices - for (int pi = 0; pi < o; ++pi) - { - for (int a = 0; a < alpha; ++a) - { - engine.makeInvertibleByAddingAS(fixedAbq, (pi * alpha + a) * lsq); - engine.makeInvertibleByAddingAS(fixedAbq, ((o + pi) * alpha + a) * lsq); - engine.genAFqS(q12, (pi * alpha + a) * l, fixedAbq, ((2 * o + pi) * alpha + a) * lsq); - engine.genAFqS(q12, ((o + pi) * alpha + a) * l, fixedAbq, ((3 * o + pi) * alpha + a) * lsq); - } - } - fixedAbqSet.put(o, fixedAbq); - } } // Getter methods @@ -228,12 +172,12 @@ public int getAlpha() public int getPublicKeyLength() { - return SnovaKeyPairGenerator.publicSeedLength + ((o * o * o * l * l + 1) >>> 1); + return SnovaKeyPairGenerator.publicSeedLength + ((o * o * o * lsq + 1) >>> 1); } public int getPrivateKeyLength() { - return ((l * l * (4 * o * alpha + o * (v * v + v * o + o * v) + v * o) + 1) >> 1) + return ((lsq * (4 * o * alpha + o * (v * v + v * o + o * v) + v * o) + 1) >> 1) + SnovaKeyPairGenerator.privateSeedLength + SnovaKeyPairGenerator.publicSeedLength; } @@ -251,23 +195,4 @@ public int getSaltLength() { return 16; } - - void beTheS(byte[] target) - { - // Set all elements to 8 - (i + j) in GF16 (4-bit values) - for (int i = 0, il = 0; i < l; ++i, il += l) - { - for (int j = 0; j < l; ++j) - { - int value = 8 - (i + j); - target[il + j] = (byte)(value & 0x0F); // Mask to 4 bits - } - } - - // Special case for rank 5 - if (l == 5) - { - target[24] = (byte)9; // Set (4,4) to 9 - } - } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index 2f0fa983e5..133233e94c 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -53,26 +53,25 @@ public void init(boolean forSigning, CipherParameters param) @Override public byte[] generateSignature(byte[] message) { - byte[] hash = new byte[shake.getDigestSize()]; - shake.update(message, 0, message.length); - shake.doFinal(hash, 0); + byte[] hash = getMessageHash(message); byte[] salt = new byte[params.getSaltLength()]; random.nextBytes(salt); byte[] signature = new byte[((params.getN() * params.getLsq() + 1) >>> 1) + params.getSaltLength()]; SnovaKeyElements keyElements = new SnovaKeyElements(params); byte[] publicKeySeed; + byte[] ptPrivateKeySeed; if (params.isSkIsSeed()) { byte[] seedPair = privKey.getPrivateKey(); publicKeySeed = Arrays.copyOfRange(seedPair, 0, SnovaKeyPairGenerator.publicSeedLength); - keyElements.ptPrivateKeySeed = Arrays.copyOfRange(seedPair, SnovaKeyPairGenerator.publicSeedLength, seedPair.length); - engine.genMap1T12Map2(keyElements, publicKeySeed, keyElements.ptPrivateKeySeed); + ptPrivateKeySeed = Arrays.copyOfRange(seedPair, SnovaKeyPairGenerator.publicSeedLength, seedPair.length); + engine.genMap1T12Map2(keyElements, publicKeySeed, ptPrivateKeySeed); } else { - byte[] input = privKey.getPrivateKey(); - byte[] tmp = new byte[(input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength) << 1]; - GF16Utils.decodeMergeInHalf(input, tmp, tmp.length); + byte[] privateKey = privKey.getPrivateKey(); + byte[] tmp = new byte[(privateKey.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength) << 1]; + GF16Utils.decodeMergeInHalf(privateKey, tmp, tmp.length); int inOff = 0; inOff = SnovaKeyElements.copy3d(tmp, inOff, keyElements.map1.aAlpha); inOff = SnovaKeyElements.copy3d(tmp, inOff, keyElements.map1.bAlpha); @@ -82,21 +81,18 @@ public byte[] generateSignature(byte[] message) inOff = SnovaKeyElements.copy4d(tmp, inOff, keyElements.map2.f11); inOff = SnovaKeyElements.copy4d(tmp, inOff, keyElements.map2.f12); SnovaKeyElements.copy4d(tmp, inOff, keyElements.map2.f21); - publicKeySeed = Arrays.copyOfRange(input, input.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength, input.length - SnovaKeyPairGenerator.privateSeedLength); - keyElements.ptPrivateKeySeed = new byte[SnovaKeyPairGenerator.privateSeedLength]; - System.arraycopy(input, input.length - SnovaKeyPairGenerator.privateSeedLength, keyElements.ptPrivateKeySeed, 0, keyElements.ptPrivateKeySeed.length); + publicKeySeed = Arrays.copyOfRange(privateKey, privateKey.length - SnovaKeyPairGenerator.publicSeedLength - SnovaKeyPairGenerator.privateSeedLength, privateKey.length - SnovaKeyPairGenerator.privateSeedLength); + ptPrivateKeySeed = Arrays.copyOfRange(privateKey, privateKey.length - SnovaKeyPairGenerator.privateSeedLength, privateKey.length); } signDigestCore(signature, hash, salt, keyElements.map1.aAlpha, keyElements.map1.bAlpha, keyElements.map1.qAlpha1, keyElements.map1.qAlpha2, - keyElements.T12, keyElements.map2.f11, keyElements.map2.f12, keyElements.map2.f21, publicKeySeed, keyElements.ptPrivateKeySeed); + keyElements.T12, keyElements.map2.f11, keyElements.map2.f12, keyElements.map2.f21, publicKeySeed, ptPrivateKeySeed); return Arrays.concatenate(signature, message); } @Override public boolean verifySignature(byte[] message, byte[] signature) { - byte[] hash = new byte[shake.getDigestSize()]; - shake.update(message, 0, message.length); - shake.doFinal(hash, 0); + byte[] hash = getMessageHash(message); MapGroup1 map1 = new MapGroup1(params); byte[] pk = pubKey.getEncoded(); byte[] publicKeySeed = Arrays.copyOf(pk, SnovaKeyPairGenerator.publicSeedLength); @@ -117,10 +113,10 @@ public boolean verifySignature(byte[] message, byte[] signature) return verifySignatureCore(hash, signature, publicKeySeed, map1, p22); } - public void createSignedHash( - byte[] digest, int bytesDigest, + void createSignedHash( byte[] ptPublicKeySeed, int seedLengthPublic, - byte[] arraySalt, int bytesSalt, + byte[] digest, int bytesDigest, + byte[] arraySalt, int saltOff, int bytesSalt, byte[] signedHashOut, int bytesHash) { // 1. Absorb public key seed @@ -130,18 +126,18 @@ public void createSignedHash( shake.update(digest, 0, bytesDigest); // 3. Absorb salt - shake.update(arraySalt, 0, bytesSalt); + shake.update(arraySalt, saltOff, bytesSalt); // 4. Finalize absorption and squeeze output shake.doFinal(signedHashOut, 0, bytesHash); } - public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, - byte[][][] Aalpha, byte[][][] Balpha, - byte[][][] Qalpha1, byte[][][] Qalpha2, - byte[][][] T12, byte[][][][] F11, - byte[][][][] F12, byte[][][][] F21, - byte[] ptPublicKeySeed, byte[] ptPrivateKeySeed) + void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, + byte[][][] Aalpha, byte[][][] Balpha, + byte[][][] Qalpha1, byte[][][] Qalpha2, + byte[][][] T12, byte[][][][] F11, + byte[][][][] F12, byte[][][][] F21, + byte[] ptPublicKeySeed, byte[] ptPrivateKeySeed) { // Initialize constants from parameters final int m = params.getM(); @@ -161,26 +157,26 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, byte[][] Temp = new byte[lsq][lsq]; byte[] solution = new byte[mxlsq]; - byte[][][][] Left = new byte[m][alpha][v][lsq]; - byte[][][][] Right = new byte[m][alpha][v][lsq]; + byte[][][] Left = new byte[alpha][v][lsq]; + byte[][][] Right = new byte[alpha][v][lsq]; byte[] leftXTmp = new byte[lsq]; byte[] rightXtmp = new byte[lsq]; byte[][] XInGF16Matrix = new byte[v][lsq]; - byte[][] FvvGF16Matrix = new byte[m][lsq]; + byte[] FvvGF16Matrix = new byte[lsq]; byte[] hashInGF16 = new byte[mxlsq]; - + byte[] vinegarGf16 = new byte[n * lsq]; byte[] signedHash = new byte[bytesHash]; - byte[] vinegarBytes = new byte[(v * lsq + 1) / 2]; + byte[] vinegarBytes = new byte[(v * lsq + 1) >>> 1]; // Temporary matrices - byte[] gf16mTemp0 = new byte[lsq]; + byte[] gf16mTemp0 = new byte[l]; int flagRedo; byte numSign = 0; byte valLeft, valB, valA, valRight; // Step 1: Create signed hash - createSignedHash(digest, digest.length, ptPublicKeySeed, ptPublicKeySeed.length, - arraySalt, arraySalt.length, signedHash, bytesHash); + createSignedHash(ptPublicKeySeed, ptPublicKeySeed.length, digest, digest.length, + arraySalt, 0, arraySalt.length, signedHash, bytesHash); GF16.decode(signedHash, 0, hashInGF16, 0, hashInGF16.length); do @@ -199,80 +195,55 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, } // Generate vinegar values - shake.update(ptPrivateKeySeed, 0, ptPrivateKeySeed.length); shake.update(digest, 0, digest.length); shake.update(arraySalt, 0, arraySalt.length); shake.update(numSign); shake.doFinal(vinegarBytes, 0, vinegarBytes.length); - if ((lsq & 1) == 1) - { - byte[] tmp = new byte[vinegarBytes.length << 1]; - GF16.decode(vinegarBytes, tmp, tmp.length); - fill(tmp, XInGF16Matrix, tmp.length); - } - else - { - MapGroup1.decodeArray(vinegarBytes, 0, XInGF16Matrix, vinegarBytes.length); - } - // Evaluate vinegar part of central map - for (int mi = 0; mi < m; mi++) + GF16.decode(vinegarBytes, vinegarGf16, vinegarBytes.length << 1); + fill(vinegarGf16, XInGF16Matrix, vinegarBytes.length << 1); + + for (int i = 0, ixlsq = 0; i < m; i++, ixlsq += lsq) { + Arrays.fill(FvvGF16Matrix, (byte)0); + // Evaluate vinegar part of central map for (int a = 0; a < alpha; a++) { - for (int idx = 0; idx < v; idx++) + int miPrime = iPrime(i, a); + for (int j = 0; j < v; j++) { - GF16Utils.gf16mTranMul(XInGF16Matrix[idx], Qalpha1[mi][a], gf16mTemp0, l); - GF16Utils.gf16mMul(Aalpha[mi][a], gf16mTemp0, Left[mi][a][idx], l); - - GF16Utils.gf16mMul(Qalpha2[mi][a], XInGF16Matrix[idx], gf16mTemp0, l); - GF16Utils.gf16mMul(gf16mTemp0, Balpha[mi][a], Right[mi][a][idx], l); + GF16Utils.gf16mTranMulMul(XInGF16Matrix[j], Aalpha[i][a], Balpha[i][a], Qalpha1[i][a], + Qalpha2[i][a], gf16mTemp0, Left[a][j], Right[a][j], l); } - } - } - // Matrix operations for Fvv - for (int i = 0; i < FvvGF16Matrix.length; ++i) - { - Arrays.fill(FvvGF16Matrix[i], (byte)0); - } - for (int mi = 0; mi < m; mi++) - { - for (int a = 0; a < alpha; a++) - { - int miPrime = iPrime(mi, a); for (int j = 0; j < v; j++) { + // Gaussian elimination setup for (int k = 0; k < v; k++) { - GF16Utils.gf16mMul(Left[mi][a][j], F11[miPrime][j][k], gf16mTemp0, l); - GF16Utils.gf16mMulTo(gf16mTemp0, Right[mi][a][k], FvvGF16Matrix[mi], l); + GF16Utils.gf16mMulMulTo(Left[a][j], F11[miPrime][j][k], Right[a][k], gf16mTemp0, FvvGF16Matrix, l); } } } - } - // Gaussian elimination setup - for (int i = 0, ixlsq = 0; i < m; i++, ixlsq += lsq) - { - for (int j = 0, jxl = 0; j < l; j++, jxl += l) + for (int j = 0, off = 0; j < l; j++) { - for (int k = 0, jxl_k = jxl; k < l; k++, jxl_k++) + for (int k = 0; k < l; k++) { - Gauss[ixlsq + jxl_k][mxlsq] ^= FvvGF16Matrix[i][jxl_k]; + Gauss[ixlsq + off][mxlsq] ^= FvvGF16Matrix[off++]; } } - } - - // Compute the coefficients of Xo and put into Gauss matrix and compute the coefficients of Xo^t and add into Gauss matrix - for (int mi = 0, mixlsq = 0; mi < m; ++mi, mixlsq += lsq) - { +// } +// // TODO: think about how this two loops can merge together? +// // Compute the coefficients of Xo and put into Gauss matrix and compute the coefficients of Xo^t and add into Gauss matrix +// for (int i = 0, ixlsq = 0; i < m; ++i, ixlsq += lsq) +// { for (int index = 0, idxlsq = 0; index < o; ++index, idxlsq += lsq) { for (int a = 0; a < alpha; ++a) { - int mi_prime = iPrime(mi, a); + int mi_prime = iPrime(i, a); // Initialize Temp to zero for (int ti = 0; ti < lsq; ++ti) { @@ -281,10 +252,8 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, // Process each j for Left part for (int j = 0; j < v; ++j) { - GF16Utils.gf16mMul(Left[mi][a][j], F12[mi_prime][j][index], gf16mTemp0, l); - GF16Utils.gf16mMul(gf16mTemp0, Qalpha2[mi][a], leftXTmp, l); - GF16Utils.gf16mMul(Qalpha1[mi][a], F21[mi_prime][index][j], gf16mTemp0, l); - GF16Utils.gf16mMul(gf16mTemp0, Right[mi][a][j], rightXtmp, l); + GF16Utils.gf16mMulMul(Left[a][j], F12[mi_prime][j][index], Qalpha2[i][a], gf16mTemp0, leftXTmp, l); + GF16Utils.gf16mMulMul(Qalpha1[i][a], F21[mi_prime][index][j], Right[a][j], gf16mTemp0, rightXtmp, l); // Accumulate into Temp from leftXTmp and Balpha[mi][a] // rlra_l is short for "rowLeft_rowA times l" for (int ti = 0, colB_colRight = 0, rlraxl = 0; ti < lsq; ++ti, ++colB_colRight) @@ -310,8 +279,8 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, valLeft = leftXTmp[rlraxl + colLeft_rowRight]; valRight = rightXtmp[clrrxl + colB_colRight]; } - valB = Balpha[mi][a][rbcaxl + colB_colRight]; - valA = Aalpha[mi][a][rlraxl + rowB_colA]; + valB = Balpha[i][a][rbcaxl + colB_colRight]; + valA = Aalpha[i][a][rlraxl + rowB_colA]; Temp[ti][tj] ^= GF16.mul(valLeft, valB) ^ GF16.mul(valA, valRight); } } @@ -321,7 +290,7 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, { for (int tj = 0; tj < lsq; ++tj) { - Gauss[mixlsq + ti][idxlsq + tj] ^= Temp[ti][tj]; + Gauss[ixlsq + ti][idxlsq + tj] ^= Temp[ti][tj]; } } } @@ -333,24 +302,22 @@ public void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, while (flagRedo != 0); // Copy vinegar variables - byte[] tmp = new byte[n * lsq]; for (int idx = 0, idxlsq = 0; idx < v; idx++, idxlsq += lsq) { - System.arraycopy(XInGF16Matrix[idx], 0, tmp, idxlsq, lsq); for (int i = 0, ixlsq = 0; i < o; i++, ixlsq += lsq) { - GF16Utils.gf16mMulTo(T12[idx][i], solution, ixlsq, tmp, idxlsq, l); + GF16Utils.gf16mMulTo(T12[idx][i], solution, ixlsq, vinegarGf16, idxlsq, l); } } // Copy remaining oil variables - System.arraycopy(solution, 0, tmp, v * lsq, oxlsq); - GF16.encode(tmp, ptSignature, tmp.length); + System.arraycopy(solution, 0, vinegarGf16, v * lsq, oxlsq); + GF16.encode(vinegarGf16, ptSignature, vinegarGf16.length); System.arraycopy(arraySalt, 0, ptSignature, ptSignature.length - bytesSalt, bytesSalt); } - public boolean verifySignatureCore(byte[] digest, byte[] signature, byte[] publicKeySeed, MapGroup1 map1, byte[][][][] p22) + boolean verifySignatureCore(byte[] digest, byte[] signature, byte[] publicKeySeed, MapGroup1 map1, byte[][][][] p22) { final int bytesHash = (params.getO() * params.getLsq() + 1) >>> 1; final int bytesSalt = params.getSaltLength(); @@ -362,14 +329,11 @@ public boolean verifySignatureCore(byte[] digest, byte[] signature, byte[] publi // Step 1: Regenerate signed hash using public key seed, digest and salt byte[] signedHash = new byte[bytesHash]; - - shake.update(publicKeySeed, 0, publicKeySeed.length); - shake.update(digest, 0, digest.length); - shake.update(signature, bytesSignature, bytesSalt); - shake.doFinal(signedHash, 0, bytesHash); + createSignedHash(publicKeySeed, publicKeySeed.length, digest, digest.length, + signature, bytesSignature, bytesSalt, signedHash, bytesHash); // Handle odd-length adjustment (if needed) - if ((o * lsq) % 2 != 0) + if (((o * lsq) & 1) != 0) { signedHash[bytesHash - 1] &= 0x0F; } @@ -405,33 +369,27 @@ private void evaluation(byte[] hashMatrix, MapGroup1 map1, byte[][][][] p22, byt final int alpha = params.getAlpha(); final int n = params.getN(); final int l = params.getL(); - final int lsq = l * l; + final int lsq = params.getLsq(); - byte[][][][] Left = new byte[m][alpha][n][lsq]; - byte[][][][] Right = new byte[m][alpha][n][lsq]; + byte[][][] Left = new byte[alpha][n][lsq]; + byte[][][] Right = new byte[alpha][n][lsq]; byte[] temp = new byte[lsq]; // Evaluate Left and Right matrices - for (int mi = 0; mi < m; mi++) + for (int mi = 0, mixlsq = 0; mi < m; mi++, mixlsq += lsq) { for (int si = 0; si < n; si++) { for (int a = 0; a < alpha; a++) { // Left[mi][a][si] = Aalpha * (sig^T * Qalpha1) - GF16Utils.gf16mTranMul(signature[si], map1.qAlpha1[mi][a], temp, l); - GF16Utils.gf16mMul(map1.aAlpha[mi][a], temp, Left[mi][a][si], l); - // Right[mi][a][si] = (Qalpha2 * sig) * Balpha - GF16Utils.gf16mMul(map1.qAlpha2[mi][a], signature[si], temp, l); - GF16Utils.gf16mMul(temp, map1.bAlpha[mi][a], Right[mi][a][si], l); + GF16Utils.gf16mTranMulMul(signature[si], map1.aAlpha[mi][a], map1.bAlpha[mi][a], map1.qAlpha1[mi][a], + map1.qAlpha2[mi][a], temp, Left[a][si], Right[a][si], l); } } - } - // Process P matrices and accumulate results - for (int mi = 0; mi < m; mi++) - { + // Process P matrices and accumulate results for (int a = 0; a < alpha; a++) { int miPrime = iPrime(mi, a); @@ -439,15 +397,15 @@ private void evaluation(byte[] hashMatrix, MapGroup1 map1, byte[][][][] p22, byt { // sum_t0 = sum(P[miPrime][ni][nj] * Right[mi][a][nj]) byte[] p = getPMatrix(map1, p22, miPrime, ni, 0); - GF16Utils.gf16mMul(p, Right[mi][a][0], temp, l); + GF16Utils.gf16mMul(p, Right[a][0], temp, l); for (int nj = 1; nj < n; nj++) { p = getPMatrix(map1, p22, miPrime, ni, nj); - GF16Utils.gf16mMulTo(p, Right[mi][a][nj], temp, l); + GF16Utils.gf16mMulTo(p, Right[a][nj], temp, l); } // hashMatrix += Left[mi][a][ni] * temp - GF16Utils.gf16mMulTo(Left[mi][a][ni], temp, 0, hashMatrix, mi * lsq, l); + GF16Utils.gf16mMulTo(Left[a][ni], temp, hashMatrix, mixlsq, l); } } } @@ -531,11 +489,12 @@ private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size // Back substitution for (int i = size - 1; i >= 0; i--) { - solution[i] = Gauss[i][size]; + byte tmp = Gauss[i][size]; for (int j = i + 1; j < size; j++) { - solution[i] ^= GF16.mul(Gauss[i][j], solution[j]); + tmp ^= GF16.mul(Gauss[i][j], solution[j]); } + solution[i] = tmp; } return 0; } @@ -556,4 +515,12 @@ static void fill(byte[] input, byte[][] output, int len) rlt += tmp; } } + + private byte[] getMessageHash(byte[] message) + { + byte[] hash = new byte[shake.getDigestSize()]; + shake.update(message, 0, message.length); + shake.doFinal(hash, 0); + return hash; + } } diff --git a/core/src/main/java/org/bouncycastle/util/GF16.java b/core/src/main/java/org/bouncycastle/util/GF16.java index 3076aa76bb..62d2b8026b 100644 --- a/core/src/main/java/org/bouncycastle/util/GF16.java +++ b/core/src/main/java/org/bouncycastle/util/GF16.java @@ -167,14 +167,4 @@ public static byte innerProduct(byte[] a, int aOff, byte[] b, int bOff, int rank } return result; } - - public static byte dotProduct(byte[] a, int aOff, byte[] b, int bOff, int rank) - { - byte result = 0; - for (int k = 0; k < rank; ++k, aOff += rank, bOff += rank) - { - result ^= mul(a[aOff], b[bOff]); - } - return result; - } } diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/BCMayoPublicKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/BCMayoPublicKey.java index 30d861d3b2..5d53ecc839 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/BCMayoPublicKey.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/BCMayoPublicKey.java @@ -68,7 +68,7 @@ public int hashCode() } /** - * @return name of the algorithm - "BIKE" + * @return name of the algorithm - "Mayo[1|2|3|5]" */ public final String getAlgorithm() { diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/MayoKeyPairGeneratorSpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/MayoKeyPairGeneratorSpi.java index 9f0eded803..892730e20d 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/MayoKeyPairGeneratorSpi.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/MayoKeyPairGeneratorSpi.java @@ -9,13 +9,11 @@ import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.CryptoServicesRegistrar; -import org.bouncycastle.pqc.crypto.falcon.FalconParameters; import org.bouncycastle.pqc.crypto.mayo.MayoKeyGenerationParameters; import org.bouncycastle.pqc.crypto.mayo.MayoKeyPairGenerator; import org.bouncycastle.pqc.crypto.mayo.MayoParameters; import org.bouncycastle.pqc.crypto.mayo.MayoPrivateKeyParameters; import org.bouncycastle.pqc.crypto.mayo.MayoPublicKeyParameters; -import org.bouncycastle.pqc.jcajce.provider.falcon.FalconKeyPairGeneratorSpi; import org.bouncycastle.pqc.jcajce.provider.util.SpecUtil; import org.bouncycastle.pqc.jcajce.spec.MayoParameterSpec; import org.bouncycastle.util.Strings; diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/BCSnovaPrivateKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/BCSnovaPrivateKey.java index 2bda389890..34f47b2df0 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/BCSnovaPrivateKey.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/BCSnovaPrivateKey.java @@ -1,5 +1,132 @@ package org.bouncycastle.pqc.jcajce.provider.snova; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.security.PrivateKey; + +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.pqc.crypto.snova.SnovaPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.util.PrivateKeyFactory; +import org.bouncycastle.pqc.crypto.util.PrivateKeyInfoFactory; +import org.bouncycastle.pqc.jcajce.interfaces.SnovaKey; +import org.bouncycastle.pqc.jcajce.spec.SnovaParameterSpec; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; + public class BCSnovaPrivateKey + implements PrivateKey, SnovaKey { + private static final long serialVersionUID = 1L; + + private transient SnovaPrivateKeyParameters params; + private transient ASN1Set attributes; + + public BCSnovaPrivateKey( + SnovaPrivateKeyParameters params) + { + this.params = params; + } + + public BCSnovaPrivateKey(PrivateKeyInfo keyInfo) + throws IOException + { + init(keyInfo); + } + + private void init(PrivateKeyInfo keyInfo) + throws IOException + { + this.attributes = keyInfo.getAttributes(); + this.params = (SnovaPrivateKeyParameters) PrivateKeyFactory.createKey(keyInfo); + } + + /** + * Compare this private key with another object. + * + * @param o the other object + * @return the result of the comparison + */ + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (o instanceof BCSnovaPrivateKey) + { + BCSnovaPrivateKey otherKey = (BCSnovaPrivateKey)o; + + return Arrays.areEqual(params.getEncoded(), otherKey.params.getEncoded()); + } + + return false; + } + + public int hashCode() + { + return Arrays.hashCode(params.getEncoded()); + } + + /** + * @return name of the algorithm - "Snova_[v]_[o]_[l]" + */ + public final String getAlgorithm() + { + return Strings.toUpperCase(params.getParameters().getName()); + } + + public byte[] getEncoded() + { + + try + { + PrivateKeyInfo pki = PrivateKeyInfoFactory.createPrivateKeyInfo(params, attributes); + + return pki.getEncoded(); + } + catch (IOException e) + { + return null; + } + } + + public SnovaParameterSpec getParameterSpec() + { + return SnovaParameterSpec.fromName(params.getParameters().getName()); + } + + public String getFormat() + { + return "PKCS#8"; + } + + SnovaPrivateKeyParameters getKeyParams() + { + return params; + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + init(PrivateKeyInfo.getInstance(enc)); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } } + + diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/BCSnovaPublicKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/BCSnovaPublicKey.java new file mode 100644 index 0000000000..3c6ec6b946 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/BCSnovaPublicKey.java @@ -0,0 +1,128 @@ +package org.bouncycastle.pqc.jcajce.provider.snova; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.security.PublicKey; + +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.pqc.crypto.snova.SnovaPublicKeyParameters; +import org.bouncycastle.pqc.crypto.util.PublicKeyFactory; +import org.bouncycastle.pqc.crypto.util.SubjectPublicKeyInfoFactory; +import org.bouncycastle.pqc.jcajce.interfaces.SnovaKey; +import org.bouncycastle.pqc.jcajce.spec.SnovaParameterSpec; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; + +public class BCSnovaPublicKey + implements PublicKey, SnovaKey +{ + private static final long serialVersionUID = 1L; + + private transient SnovaPublicKeyParameters params; + + public BCSnovaPublicKey( + SnovaPublicKeyParameters params) + { + this.params = params; + } + + public BCSnovaPublicKey(SubjectPublicKeyInfo keyInfo) + throws IOException + { + init(keyInfo); + } + + private void init(SubjectPublicKeyInfo keyInfo) + throws IOException + { + this.params = (SnovaPublicKeyParameters) PublicKeyFactory.createKey(keyInfo); + } + + /** + * Compare this BIKE public key with another object. + * + * @param o the other object + * @return the result of the comparison + */ + public boolean equals(Object o) + { + if (o == this) + { + return true; + } + + if (o instanceof BCSnovaPublicKey) + { + BCSnovaPublicKey otherKey = (BCSnovaPublicKey)o; + + return Arrays.areEqual(params.getEncoded(), otherKey.params.getEncoded()); + } + + return false; + } + + public int hashCode() + { + return Arrays.hashCode(params.getEncoded()); + } + + /** + * @return name of the algorithm - "Snova_[v]_[o]_[l]" + */ + public final String getAlgorithm() + { + return Strings.toUpperCase(params.getParameters().getName()); + } + + public byte[] getEncoded() + { + try + { + SubjectPublicKeyInfo pki = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(params); + + return pki.getEncoded(); + } + catch (IOException e) + { + return null; + } + } + + public String getFormat() + { + return "X.509"; + } + + public SnovaParameterSpec getParameterSpec() + { + return SnovaParameterSpec.fromName(params.getParameters().getName()); + } + + SnovaPublicKeyParameters getKeyParams() + { + return params; + } + + private void readObject( + ObjectInputStream in) + throws IOException, ClassNotFoundException + { + in.defaultReadObject(); + + byte[] enc = (byte[])in.readObject(); + + init(SubjectPublicKeyInfo.getInstance(enc)); + } + + private void writeObject( + ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + + out.writeObject(this.getEncoded()); + } +} + + From 98409a833caa55730fa91df7f7b488738de9598b Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 3 Apr 2025 17:26:02 +1030 Subject: [PATCH 295/890] Refactor on Snova --- .../asn1/bc/BCObjectIdentifiers.java | 49 ++ .../pqc/crypto/snova/GF16Utils.java | 30 +- .../pqc/crypto/snova/SnovaEngine.java | 9 +- .../pqc/crypto/snova/SnovaSigner.java | 86 ++- .../provider/snova/SnovaKeyFactorySpi.java | 531 ++++++++++++++++ .../snova/SnovaKeyPairGeneratorSpi.java | 590 ++++++++++++++++++ 6 files changed, 1226 insertions(+), 69 deletions(-) create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SnovaKeyFactorySpi.java create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SnovaKeyPairGeneratorSpi.java diff --git a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java index b0e8f7726f..5b1800e6be 100644 --- a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java @@ -451,4 +451,53 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier mayo2 = mayo.branch("2"); ASN1ObjectIdentifier mayo3 = mayo.branch("3"); ASN1ObjectIdentifier mayo5 = mayo.branch("4"); + + /** + * Snova + */ + ASN1ObjectIdentifier snova = bc_sig.branch("11"); + ASN1ObjectIdentifier snova_24_5_4_ssk = snova.branch("1"); + ASN1ObjectIdentifier snova_24_5_4_esk = snova.branch("2"); + ASN1ObjectIdentifier snova_24_5_4_shake_ssk = snova.branch("3"); + ASN1ObjectIdentifier snova_24_5_4_shake_esk = snova.branch("4"); + ASN1ObjectIdentifier snova_24_5_5_ssk = snova.branch("5"); + ASN1ObjectIdentifier snova_24_5_5_esk = snova.branch("6"); + ASN1ObjectIdentifier snova_24_5_5_shake_ssk = snova.branch("7"); + ASN1ObjectIdentifier snova_24_5_5_shake_esk = snova.branch("8"); + ASN1ObjectIdentifier snova_25_8_3_ssk = snova.branch("9"); + ASN1ObjectIdentifier snova_25_8_3_esk = snova.branch("10"); + ASN1ObjectIdentifier snova_25_8_3_shake_ssk = snova.branch("11"); + ASN1ObjectIdentifier snova_25_8_3_shake_esk = snova.branch("12"); + ASN1ObjectIdentifier snova_29_6_5_ssk = snova.branch("13"); + ASN1ObjectIdentifier snova_29_6_5_esk = snova.branch("14"); + ASN1ObjectIdentifier snova_29_6_5_shake_ssk = snova.branch("15"); + ASN1ObjectIdentifier snova_29_6_5_shake_esk = snova.branch("16"); + ASN1ObjectIdentifier snova_37_8_4_ssk = snova.branch("17"); + ASN1ObjectIdentifier snova_37_8_4_esk = snova.branch("18"); + ASN1ObjectIdentifier snova_37_8_4_shake_ssk = snova.branch("19"); + ASN1ObjectIdentifier snova_37_8_4_shake_esk = snova.branch("20"); + ASN1ObjectIdentifier snova_37_17_2_ssk = snova.branch("21"); + ASN1ObjectIdentifier snova_37_17_2_esk = snova.branch("22"); + ASN1ObjectIdentifier snova_37_17_2_shake_ssk = snova.branch("23"); + ASN1ObjectIdentifier snova_37_17_2_shake_esk = snova.branch("24"); + ASN1ObjectIdentifier snova_49_11_3_ssk = snova.branch("25"); + ASN1ObjectIdentifier snova_49_11_3_esk = snova.branch("26"); + ASN1ObjectIdentifier snova_49_11_3_shake_ssk = snova.branch("27"); + ASN1ObjectIdentifier snova_49_11_3_shake_esk = snova.branch("28"); + ASN1ObjectIdentifier snova_56_25_2_ssk = snova.branch("29"); + ASN1ObjectIdentifier snova_56_25_2_esk = snova.branch("30"); + ASN1ObjectIdentifier snova_56_25_2_shake_ssk = snova.branch("31"); + ASN1ObjectIdentifier snova_56_25_2_shake_esk = snova.branch("32"); + ASN1ObjectIdentifier snova_60_10_4_ssk = snova.branch("33"); + ASN1ObjectIdentifier snova_60_10_4_esk = snova.branch("34"); + ASN1ObjectIdentifier snova_60_10_4_shake_ssk = snova.branch("35"); + ASN1ObjectIdentifier snova_60_10_4_shake_esk = snova.branch("36"); + ASN1ObjectIdentifier snova_66_15_3_ssk = snova.branch("37"); + ASN1ObjectIdentifier snova_66_15_3_esk = snova.branch("38"); + ASN1ObjectIdentifier snova_66_15_3_shake_ssk = snova.branch("39"); + ASN1ObjectIdentifier snova_66_15_3_shake_esk = snova.branch("40"); + ASN1ObjectIdentifier snova_75_33_2_ssk = snova.branch("41"); + ASN1ObjectIdentifier snova_75_33_2_esk = snova.branch("42"); + ASN1ObjectIdentifier snova_75_33_2_shake_ssk = snova.branch("43"); + ASN1ObjectIdentifier snova_75_33_2_shake_esk = snova.branch("44"); } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java index 260e1da6d9..2da96b17c2 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/GF16Utils.java @@ -4,7 +4,7 @@ class GF16Utils { - public static void encodeMergeInHalf(byte[] m, int mlen, byte[] menc) + static void encodeMergeInHalf(byte[] m, int mlen, byte[] menc) { int i, half = (mlen + 1) >>> 1; // Process pairs of 4-bit values @@ -19,7 +19,7 @@ public static void encodeMergeInHalf(byte[] m, int mlen, byte[] menc) } } - public static void decodeMergeInHalf(byte[] byteArray, byte[] gf16Array, int nGf16) + static void decodeMergeInHalf(byte[] byteArray, byte[] gf16Array, int nGf16) { int i, half = (nGf16 + 1) >>> 1; // Process pairs of 4-bit values @@ -30,15 +30,15 @@ public static void decodeMergeInHalf(byte[] byteArray, byte[] gf16Array, int nGf } } - public static void gf16mTranMulMul(byte[] sign, byte[] a, byte[] b, byte[] q1, byte[] q2, byte[] tmp, - byte[] left, byte[] right, int rank) + static void gf16mTranMulMul(byte[] sign, int signOff, byte[] a, byte[] b, byte[] q1, byte[] q2, byte[] tmp, + byte[] left, byte[] right, int rank) { for (int i = 0, leftOff = 0, dOff = 0; i < rank; i++, leftOff += rank) { for (int j = 0; j < rank; j++) { byte result = 0; - for (int k = 0, aOff = j, bOff = i; k < rank; ++k, aOff += rank, bOff += rank) + for (int k = 0, aOff = signOff + j, bOff = i; k < rank; ++k, aOff += rank, bOff += rank) { result ^= GF16.mul(sign[aOff], q1[bOff]); } @@ -56,7 +56,7 @@ public static void gf16mTranMulMul(byte[] sign, byte[] a, byte[] b, byte[] q1, b } for (int j = 0; j < rank; j++) { - tmp[j] = GF16.innerProduct(q2, leftOff, sign, j, rank); + tmp[j] = GF16.innerProduct(q2, leftOff, sign, signOff + j, rank); } for (int j = 0; j < rank; j++) @@ -67,7 +67,7 @@ public static void gf16mTranMulMul(byte[] sign, byte[] a, byte[] b, byte[] q1, b } // tmp = a * b, d = tmp * c -> d = (a * b) * c - public static void gf16mMulMul(byte[] a, byte[] b, byte[] c, byte[] tmp, byte[] d, int rank) + static void gf16mMulMul(byte[] a, byte[] b, byte[] c, byte[] tmp, byte[] d, int rank) { for (int i = 0, leftOff = 0, dOff = 0; i < rank; i++, leftOff += rank) { @@ -83,7 +83,7 @@ public static void gf16mMulMul(byte[] a, byte[] b, byte[] c, byte[] tmp, byte[] } } - public static void gf16mMul(byte[] a, byte[] b, byte[] c, int rank) + static void gf16mMul(byte[] a, byte[] b, byte[] c, int rank) { for (int i = 0, aOff = 0, cOff = 0; i < rank; i++, aOff += rank) { @@ -94,7 +94,7 @@ public static void gf16mMul(byte[] a, byte[] b, byte[] c, int rank) } } - public static void gf16mMulMulTo(byte[] a, byte[] b, byte[] c, byte[] tmp, byte[] d, int rank) + static void gf16mMulMulTo(byte[] a, byte[] b, byte[] c, byte[] tmp, byte[] d, int rank) { for (int i = 0, leftOff = 0, dOff = 0; i < rank; i++, leftOff += rank) { @@ -110,7 +110,7 @@ public static void gf16mMulMulTo(byte[] a, byte[] b, byte[] c, byte[] tmp, byte[ } } - public static void gf16mMulTo(byte[] a, byte[] b, byte[] c, int rank) + static void gf16mMulTo(byte[] a, byte[] b, byte[] c, int rank) { for (int i = 0, aOff = 0, cOff = 0; i < rank; i++, aOff += rank) { @@ -122,7 +122,7 @@ public static void gf16mMulTo(byte[] a, byte[] b, byte[] c, int rank) } // d = a * b, e = b * c - public static void gf16mMulToTo(byte[] a, byte[] b, byte[] c, byte[] d, byte[] e, int rank) + static void gf16mMulToTo(byte[] a, byte[] b, byte[] c, byte[] d, byte[] e, int rank) { for (int i = 0, leftOff = 0, outOff = 0; i < rank; i++, leftOff += rank) { @@ -134,7 +134,7 @@ public static void gf16mMulToTo(byte[] a, byte[] b, byte[] c, byte[] d, byte[] e } } - public static void gf16mMulTo(byte[] a, byte[] b, byte[] c, int cOff, int rank) + static void gf16mMulTo(byte[] a, byte[] b, byte[] c, int cOff, int rank) { for (int i = 0, aOff = 0; i < rank; i++, aOff += rank) { @@ -146,7 +146,7 @@ public static void gf16mMulTo(byte[] a, byte[] b, byte[] c, int cOff, int rank) } // d ^= a * b + c * d - public static void gf16mMulTo(byte[] a, byte[] b, byte[] c, byte[] d, byte[] e, int eOff, int rank) + static void gf16mMulTo(byte[] a, byte[] b, byte[] c, byte[] d, byte[] e, int eOff, int rank) { for (int i = 0, leftOff = 0; i < rank; i++, leftOff += rank) { @@ -157,7 +157,7 @@ public static void gf16mMulTo(byte[] a, byte[] b, byte[] c, byte[] d, byte[] e, } } - public static void gf16mMulTo(byte[] a, byte[] b, int bOff, byte[] c, int cOff, int rank) + static void gf16mMulTo(byte[] a, byte[] b, int bOff, byte[] c, int cOff, int rank) { for (int i = 0, aOff = 0; i < rank; i++, aOff += rank) { @@ -171,7 +171,7 @@ public static void gf16mMulTo(byte[] a, byte[] b, int bOff, byte[] c, int cOff, /** * Conversion 4 bit -> 32 bit representation */ - public static int gf16FromNibble(int idx) + static int gf16FromNibble(int idx) { int middle = idx | (idx << 4); return ((middle & 0x41) | ((middle << 2) & 0x208)); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 24ec8ce949..12123152af 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -563,11 +563,12 @@ public void genABQP(MapGroup1 map1, byte[] pkSeed) } else { + int oxalphaxlsq = o * alpha * lsq; byte[] fixedAbq = fixedAbqSet.get(o); - MapGroup1.fillAlpha(fixedAbq, 0, map1.aAlpha, m * o * alpha * lsq); - MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq, map1.bAlpha, (m - 1) * o * alpha * lsq); - MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq * 2, map1.qAlpha1, (m - 2) * o * alpha * lsq); - MapGroup1.fillAlpha(fixedAbq, o * alpha * lsq * 3, map1.qAlpha2, (m - 3) * o * alpha * lsq); + MapGroup1.fillAlpha(fixedAbq, 0, map1.aAlpha, m * oxalphaxlsq); + MapGroup1.fillAlpha(fixedAbq, oxalphaxlsq, map1.bAlpha, (m - 1) * oxalphaxlsq); + MapGroup1.fillAlpha(fixedAbq, oxalphaxlsq * 2, map1.qAlpha1, (m - 2) * oxalphaxlsq); + MapGroup1.fillAlpha(fixedAbq, oxalphaxlsq * 3, map1.qAlpha2, (m - 3) * oxalphaxlsq); } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java index 133233e94c..764a289458 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaSigner.java @@ -149,6 +149,7 @@ void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, final int n = params.getN(); final int mxlsq = m * lsq; final int oxlsq = o * lsq; + final int vxlsq = v * lsq; final int bytesHash = (oxlsq + 1) >>> 1; final int bytesSalt = 16; @@ -161,12 +162,11 @@ void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, byte[][][] Right = new byte[alpha][v][lsq]; byte[] leftXTmp = new byte[lsq]; byte[] rightXtmp = new byte[lsq]; - byte[][] XInGF16Matrix = new byte[v][lsq]; byte[] FvvGF16Matrix = new byte[lsq]; byte[] hashInGF16 = new byte[mxlsq]; byte[] vinegarGf16 = new byte[n * lsq]; byte[] signedHash = new byte[bytesHash]; - byte[] vinegarBytes = new byte[(v * lsq + 1) >>> 1]; + byte[] vinegarBytes = new byte[(vxlsq + 1) >>> 1]; // Temporary matrices byte[] gf16mTemp0 = new byte[l]; @@ -202,18 +202,20 @@ void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, shake.doFinal(vinegarBytes, 0, vinegarBytes.length); GF16.decode(vinegarBytes, vinegarGf16, vinegarBytes.length << 1); - fill(vinegarGf16, XInGF16Matrix, vinegarBytes.length << 1); for (int i = 0, ixlsq = 0; i < m; i++, ixlsq += lsq) { Arrays.fill(FvvGF16Matrix, (byte)0); // Evaluate vinegar part of central map - for (int a = 0; a < alpha; a++) + for (int a = 0, miPrime = i; a < alpha; a++, miPrime++) { - int miPrime = iPrime(i, a); - for (int j = 0; j < v; j++) + if (miPrime >= o) + { + miPrime -= o; + } + for (int j = 0, jxlsq = 0; j < v; j++, jxlsq += lsq) { - GF16Utils.gf16mTranMulMul(XInGF16Matrix[j], Aalpha[i][a], Balpha[i][a], Qalpha1[i][a], + GF16Utils.gf16mTranMulMul(vinegarGf16, jxlsq, Aalpha[i][a], Balpha[i][a], Qalpha1[i][a], Qalpha2[i][a], gf16mTemp0, Left[a][j], Right[a][j], l); } @@ -235,15 +237,18 @@ void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, } } // } -// // TODO: think about how this two loops can merge together? +// // TODO: think about why this two loops can merge together? // // Compute the coefficients of Xo and put into Gauss matrix and compute the coefficients of Xo^t and add into Gauss matrix // for (int i = 0, ixlsq = 0; i < m; ++i, ixlsq += lsq) // { for (int index = 0, idxlsq = 0; index < o; ++index, idxlsq += lsq) { - for (int a = 0; a < alpha; ++a) + for (int a = 0, mi_prime = i; a < alpha; ++a, ++mi_prime) { - int mi_prime = iPrime(i, a); + if (mi_prime >= o) + { + mi_prime -= o; + } // Initialize Temp to zero for (int ti = 0; ti < lsq; ++ti) { @@ -311,7 +316,7 @@ void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, } // Copy remaining oil variables - System.arraycopy(solution, 0, vinegarGf16, v * lsq, oxlsq); + System.arraycopy(solution, 0, vinegarGf16, vxlsq, oxlsq); GF16.encode(vinegarGf16, ptSignature, vinegarGf16.length); System.arraycopy(arraySalt, 0, ptSignature, ptSignature.length - bytesSalt, bytesSalt); @@ -319,13 +324,16 @@ void signDigestCore(byte[] ptSignature, byte[] digest, byte[] arraySalt, boolean verifySignatureCore(byte[] digest, byte[] signature, byte[] publicKeySeed, MapGroup1 map1, byte[][][][] p22) { - final int bytesHash = (params.getO() * params.getLsq() + 1) >>> 1; - final int bytesSalt = params.getSaltLength(); final int lsq = params.getLsq(); + final int o = params.getO(); + final int oxlsq = o * lsq; + final int bytesHash = (oxlsq + 1) >>> 1; + final int bytesSalt = params.getSaltLength(); final int m = params.getM(); final int n = params.getN(); - final int o = params.getO(); - int bytesSignature = ((n * lsq) + 1) >>> 1; + final int nxlsq = n * lsq; + + int bytesSignature = ((nxlsq) + 1) >>> 1; // Step 1: Regenerate signed hash using public key seed, digest and salt byte[] signedHash = new byte[bytesHash]; @@ -333,27 +341,18 @@ boolean verifySignatureCore(byte[] digest, byte[] signature, byte[] publicKeySee signature, bytesSignature, bytesSalt, signedHash, bytesHash); // Handle odd-length adjustment (if needed) - if (((o * lsq) & 1) != 0) + if (((oxlsq) & 1) != 0) { signedHash[bytesHash - 1] &= 0x0F; } // Step 2: Convert signature to GF16 matrices - byte[][] signatureGF16Matrix = new byte[n][lsq]; - if ((lsq & 1) == 1) - { - byte[] decodedSig = new byte[n * lsq]; - GF16.decode(signature, 0, decodedSig, 0, decodedSig.length); - fill(decodedSig, signatureGF16Matrix, decodedSig.length); - } - else - { - MapGroup1.decodeArray(signature, 0, signatureGF16Matrix, signature.length); - } + byte[] decodedSig = new byte[nxlsq]; + GF16.decode(signature, 0, decodedSig, 0, decodedSig.length); // Step 3: Evaluate signature using public key byte[] computedHashBytes = new byte[m * lsq]; - evaluation(computedHashBytes, map1, p22, signatureGF16Matrix); + evaluation(computedHashBytes, map1, p22, decodedSig);//signatureGF16Matrix); // Convert computed hash matrix to bytes byte[] encodedHash = new byte[bytesHash]; @@ -363,13 +362,14 @@ boolean verifySignatureCore(byte[] digest, byte[] signature, byte[] publicKeySee return Arrays.areEqual(signedHash, encodedHash); } - private void evaluation(byte[] hashMatrix, MapGroup1 map1, byte[][][][] p22, byte[][] signature) + private void evaluation(byte[] hashMatrix, MapGroup1 map1, byte[][][][] p22, byte[] signature) { final int m = params.getM(); final int alpha = params.getAlpha(); final int n = params.getN(); final int l = params.getL(); final int lsq = params.getLsq(); + final int o = params.getO(); byte[][][] Left = new byte[alpha][n][lsq]; byte[][][] Right = new byte[alpha][n][lsq]; @@ -378,21 +378,24 @@ private void evaluation(byte[] hashMatrix, MapGroup1 map1, byte[][][][] p22, byt // Evaluate Left and Right matrices for (int mi = 0, mixlsq = 0; mi < m; mi++, mixlsq += lsq) { - for (int si = 0; si < n; si++) + for (int si = 0, sixlsq = 0; si < n; si++, sixlsq += lsq) { for (int a = 0; a < alpha; a++) { // Left[mi][a][si] = Aalpha * (sig^T * Qalpha1) // Right[mi][a][si] = (Qalpha2 * sig) * Balpha - GF16Utils.gf16mTranMulMul(signature[si], map1.aAlpha[mi][a], map1.bAlpha[mi][a], map1.qAlpha1[mi][a], + GF16Utils.gf16mTranMulMul(signature, sixlsq, map1.aAlpha[mi][a], map1.bAlpha[mi][a], map1.qAlpha1[mi][a], map1.qAlpha2[mi][a], temp, Left[a][si], Right[a][si], l); } } // Process P matrices and accumulate results - for (int a = 0; a < alpha; a++) + for (int a = 0, miPrime = mi; a < alpha; a++, miPrime++) { - int miPrime = iPrime(mi, a); + if (miPrime >= o) + { + miPrime -= o; + } for (int ni = 0; ni < n; ni++) { // sum_t0 = sum(P[miPrime][ni][nj] * Right[mi][a][nj]) @@ -499,23 +502,6 @@ private int performGaussianElimination(byte[][] Gauss, byte[] solution, int size return 0; } - private int iPrime(int mi, int alpha) - { - // Implement index calculation based on SNOVA specification - return (mi + alpha) % params.getO(); - } - - static void fill(byte[] input, byte[][] output, int len) - { - int rlt = 0; - for (int i = 0; i < output.length; ++i) - { - int tmp = Math.min(output[i].length, len - rlt); - System.arraycopy(input, rlt, output[i], 0, tmp); - rlt += tmp; - } - } - private byte[] getMessageHash(byte[] message) { byte[] hash = new byte[shake.getDigestSize()]; diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SnovaKeyFactorySpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SnovaKeyFactorySpi.java new file mode 100644 index 0000000000..060696c0f5 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SnovaKeyFactorySpi.java @@ -0,0 +1,531 @@ +package org.bouncycastle.pqc.jcajce.provider.snova; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; +import java.util.HashSet; +import java.util.Set; + +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.bc.BCObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.pqc.jcajce.provider.util.BaseKeyFactorySpi; + +public class SnovaKeyFactorySpi + extends BaseKeyFactorySpi +{ + private static final Set keyOids = new HashSet(); + + static + { + keyOids.add(BCObjectIdentifiers.snova_24_5_4_ssk); + keyOids.add(BCObjectIdentifiers.snova_24_5_4_esk); + keyOids.add(BCObjectIdentifiers.snova_24_5_4_shake_ssk); + keyOids.add(BCObjectIdentifiers.snova_24_5_4_shake_esk); + keyOids.add(BCObjectIdentifiers.snova_24_5_5_ssk); + keyOids.add(BCObjectIdentifiers.snova_24_5_5_esk); + keyOids.add(BCObjectIdentifiers.snova_24_5_5_shake_ssk); + keyOids.add(BCObjectIdentifiers.snova_24_5_5_shake_esk); + keyOids.add(BCObjectIdentifiers.snova_25_8_3_ssk); + keyOids.add(BCObjectIdentifiers.snova_25_8_3_esk); + keyOids.add(BCObjectIdentifiers.snova_25_8_3_shake_ssk); + keyOids.add(BCObjectIdentifiers.snova_25_8_3_shake_esk); + keyOids.add(BCObjectIdentifiers.snova_29_6_5_ssk); + keyOids.add(BCObjectIdentifiers.snova_29_6_5_esk); + keyOids.add(BCObjectIdentifiers.snova_29_6_5_shake_ssk); + keyOids.add(BCObjectIdentifiers.snova_29_6_5_shake_esk); + keyOids.add(BCObjectIdentifiers.snova_37_8_4_ssk); + keyOids.add(BCObjectIdentifiers.snova_37_8_4_esk); + keyOids.add(BCObjectIdentifiers.snova_37_8_4_shake_ssk); + keyOids.add(BCObjectIdentifiers.snova_37_8_4_shake_esk); + keyOids.add(BCObjectIdentifiers.snova_37_17_2_ssk); + keyOids.add(BCObjectIdentifiers.snova_37_17_2_esk); + keyOids.add(BCObjectIdentifiers.snova_37_17_2_shake_ssk); + keyOids.add(BCObjectIdentifiers.snova_37_17_2_shake_esk); + keyOids.add(BCObjectIdentifiers.snova_49_11_3_ssk); + keyOids.add(BCObjectIdentifiers.snova_49_11_3_esk); + keyOids.add(BCObjectIdentifiers.snova_49_11_3_shake_ssk); + keyOids.add(BCObjectIdentifiers.snova_49_11_3_shake_esk); + keyOids.add(BCObjectIdentifiers.snova_56_25_2_ssk); + keyOids.add(BCObjectIdentifiers.snova_56_25_2_esk); + keyOids.add(BCObjectIdentifiers.snova_56_25_2_shake_ssk); + keyOids.add(BCObjectIdentifiers.snova_56_25_2_shake_esk); + keyOids.add(BCObjectIdentifiers.snova_60_10_4_ssk); + keyOids.add(BCObjectIdentifiers.snova_60_10_4_esk); + keyOids.add(BCObjectIdentifiers.snova_60_10_4_shake_ssk); + keyOids.add(BCObjectIdentifiers.snova_60_10_4_shake_esk); + keyOids.add(BCObjectIdentifiers.snova_66_15_3_ssk); + keyOids.add(BCObjectIdentifiers.snova_66_15_3_esk); + keyOids.add(BCObjectIdentifiers.snova_66_15_3_shake_ssk); + keyOids.add(BCObjectIdentifiers.snova_66_15_3_shake_esk); + keyOids.add(BCObjectIdentifiers.snova_75_33_2_ssk); + keyOids.add(BCObjectIdentifiers.snova_75_33_2_esk); + keyOids.add(BCObjectIdentifiers.snova_75_33_2_shake_ssk); + keyOids.add(BCObjectIdentifiers.snova_75_33_2_shake_esk); + } + + public SnovaKeyFactorySpi() + { + super(keyOids); + } + + public SnovaKeyFactorySpi(ASN1ObjectIdentifier keyOid) + { + super(keyOid); + } + + public final KeySpec engineGetKeySpec(Key key, Class keySpec) + throws InvalidKeySpecException + { + if (key instanceof BCSnovaPrivateKey) + { + if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) + { + return new PKCS8EncodedKeySpec(key.getEncoded()); + } + } + else if (key instanceof BCSnovaPublicKey) + { + if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) + { + return new X509EncodedKeySpec(key.getEncoded()); + } + } + else + { + throw new InvalidKeySpecException("Unsupported key type: " + + key.getClass() + "."); + } + + throw new InvalidKeySpecException("Unknown key specification: " + + keySpec + "."); + } + + public final Key engineTranslateKey(Key key) + throws InvalidKeyException + { + if (key instanceof BCSnovaPrivateKey || key instanceof BCSnovaPublicKey) + { + return key; + } + + throw new InvalidKeyException("Unsupported key type"); + } + + public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) + throws IOException + { + return new BCSnovaPrivateKey(keyInfo); + } + + public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) + throws IOException + { + return new BCSnovaPublicKey(keyInfo); + } + + public static class SNOVA_24_5_4_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_24_5_4_SSK() + { + super(BCObjectIdentifiers.snova_24_5_4_ssk); + } + } + + public static class SNOVA_24_5_4_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_24_5_4_ESK() + { + super(BCObjectIdentifiers.snova_24_5_4_esk); + } + } + + public static class SNOVA_24_5_4_SHAKE_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_24_5_4_SHAKE_SSK() + { + super(BCObjectIdentifiers.snova_24_5_4_shake_ssk); + } + } + + public static class SNOVA_24_5_4_SHAKE_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_24_5_4_SHAKE_ESK() + { + super(BCObjectIdentifiers.snova_24_5_4_shake_esk); + } + } + + public static class SNOVA_24_5_5_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_24_5_5_SSK() + { + super(BCObjectIdentifiers.snova_24_5_5_ssk); + } + } + + public static class SNOVA_24_5_5_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_24_5_5_ESK() + { + super(BCObjectIdentifiers.snova_24_5_5_esk); + } + } + + public static class SNOVA_24_5_5_SHAKE_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_24_5_5_SHAKE_SSK() + { + super(BCObjectIdentifiers.snova_24_5_5_shake_ssk); + } + } + + public static class SNOVA_24_5_5_SHAKE_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_24_5_5_SHAKE_ESK() + { + super(BCObjectIdentifiers.snova_24_5_5_shake_esk); + } + } + + public static class SNOVA_25_8_3_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_25_8_3_SSK() + { + super(BCObjectIdentifiers.snova_25_8_3_ssk); + } + } + + public static class SNOVA_25_8_3_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_25_8_3_ESK() + { + super(BCObjectIdentifiers.snova_25_8_3_esk); + } + } + + public static class SNOVA_25_8_3_SHAKE_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_25_8_3_SHAKE_SSK() + { + super(BCObjectIdentifiers.snova_25_8_3_shake_ssk); + } + } + + public static class SNOVA_25_8_3_SHAKE_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_25_8_3_SHAKE_ESK() + { + super(BCObjectIdentifiers.snova_25_8_3_shake_esk); + } + } + + public static class SNOVA_29_6_5_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_29_6_5_SSK() + { + super(BCObjectIdentifiers.snova_29_6_5_ssk); + } + } + + public static class SNOVA_29_6_5_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_29_6_5_ESK() + { + super(BCObjectIdentifiers.snova_29_6_5_esk); + } + } + + public static class SNOVA_29_6_5_SHAKE_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_29_6_5_SHAKE_SSK() + { + super(BCObjectIdentifiers.snova_29_6_5_shake_ssk); + } + } + + public static class SNOVA_29_6_5_SHAKE_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_29_6_5_SHAKE_ESK() + { + super(BCObjectIdentifiers.snova_29_6_5_shake_esk); + } + } + + public static class SNOVA_37_8_4_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_37_8_4_SSK() + { + super(BCObjectIdentifiers.snova_37_8_4_ssk); + } + } + + public static class SNOVA_37_8_4_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_37_8_4_ESK() + { + super(BCObjectIdentifiers.snova_37_8_4_esk); + } + } + + public static class SNOVA_37_8_4_SHAKE_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_37_8_4_SHAKE_SSK() + { + super(BCObjectIdentifiers.snova_37_8_4_shake_ssk); + } + } + + public static class SNOVA_37_8_4_SHAKE_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_37_8_4_SHAKE_ESK() + { + super(BCObjectIdentifiers.snova_37_8_4_shake_esk); + } + } + + public static class SNOVA_37_17_2_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_37_17_2_SSK() + { + super(BCObjectIdentifiers.snova_37_17_2_ssk); + } + } + + public static class SNOVA_37_17_2_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_37_17_2_ESK() + { + super(BCObjectIdentifiers.snova_37_17_2_esk); + } + } + + public static class SNOVA_37_17_2_SHAKE_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_37_17_2_SHAKE_SSK() + { + super(BCObjectIdentifiers.snova_37_17_2_shake_ssk); + } + } + + public static class SNOVA_37_17_2_SHAKE_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_37_17_2_SHAKE_ESK() + { + super(BCObjectIdentifiers.snova_37_17_2_shake_esk); + } + } + + public static class SNOVA_49_11_3_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_49_11_3_SSK() + { + super(BCObjectIdentifiers.snova_49_11_3_ssk); + } + } + + public static class SNOVA_49_11_3_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_49_11_3_ESK() + { + super(BCObjectIdentifiers.snova_49_11_3_esk); + } + } + + public static class SNOVA_49_11_3_SHAKE_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_49_11_3_SHAKE_SSK() + { + super(BCObjectIdentifiers.snova_49_11_3_shake_ssk); + } + } + + public static class SNOVA_49_11_3_SHAKE_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_49_11_3_SHAKE_ESK() + { + super(BCObjectIdentifiers.snova_49_11_3_shake_esk); + } + } + + public static class SNOVA_56_25_2_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_56_25_2_SSK() + { + super(BCObjectIdentifiers.snova_56_25_2_ssk); + } + } + + public static class SNOVA_56_25_2_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_56_25_2_ESK() + { + super(BCObjectIdentifiers.snova_56_25_2_esk); + } + } + + public static class SNOVA_56_25_2_SHAKE_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_56_25_2_SHAKE_SSK() + { + super(BCObjectIdentifiers.snova_56_25_2_shake_ssk); + } + } + + public static class SNOVA_56_25_2_SHAKE_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_56_25_2_SHAKE_ESK() + { + super(BCObjectIdentifiers.snova_56_25_2_shake_esk); + } + } + + public static class SNOVA_60_10_4_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_60_10_4_SSK() + { + super(BCObjectIdentifiers.snova_60_10_4_ssk); + } + } + + public static class SNOVA_60_10_4_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_60_10_4_ESK() + { + super(BCObjectIdentifiers.snova_60_10_4_esk); + } + } + + public static class SNOVA_60_10_4_SHAKE_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_60_10_4_SHAKE_SSK() + { + super(BCObjectIdentifiers.snova_60_10_4_shake_ssk); + } + } + + public static class SNOVA_60_10_4_SHAKE_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_60_10_4_SHAKE_ESK() + { + super(BCObjectIdentifiers.snova_60_10_4_shake_esk); + } + } + + public static class SNOVA_66_15_3_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_66_15_3_SSK() + { + super(BCObjectIdentifiers.snova_66_15_3_ssk); + } + } + + public static class SNOVA_66_15_3_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_66_15_3_ESK() + { + super(BCObjectIdentifiers.snova_66_15_3_esk); + } + } + + public static class SNOVA_66_15_3_SHAKE_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_66_15_3_SHAKE_SSK() + { + super(BCObjectIdentifiers.snova_66_15_3_shake_ssk); + } + } + + public static class SNOVA_66_15_3_SHAKE_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_66_15_3_SHAKE_ESK() + { + super(BCObjectIdentifiers.snova_66_15_3_shake_esk); + } + } + + public static class SNOVA_75_33_2_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_75_33_2_SSK() + { + super(BCObjectIdentifiers.snova_75_33_2_ssk); + } + } + + public static class SNOVA_75_33_2_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_75_33_2_ESK() + { + super(BCObjectIdentifiers.snova_75_33_2_esk); + } + } + + public static class SNOVA_75_33_2_SHAKE_SSK + extends SnovaKeyFactorySpi + { + public SNOVA_75_33_2_SHAKE_SSK() + { + super(BCObjectIdentifiers.snova_75_33_2_shake_ssk); + } + } + + public static class SNOVA_75_33_2_SHAKE_ESK + extends SnovaKeyFactorySpi + { + public SNOVA_75_33_2_SHAKE_ESK() + { + super(BCObjectIdentifiers.snova_75_33_2_shake_esk); + } + } +} + + diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SnovaKeyPairGeneratorSpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SnovaKeyPairGeneratorSpi.java new file mode 100644 index 0000000000..3a78437f73 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SnovaKeyPairGeneratorSpi.java @@ -0,0 +1,590 @@ +package org.bouncycastle.pqc.jcajce.provider.snova; + + +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyPair; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.util.HashMap; +import java.util.Map; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.pqc.crypto.snova.SnovaKeyGenerationParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaKeyPairGenerator; +import org.bouncycastle.pqc.crypto.snova.SnovaParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaPublicKeyParameters; +import org.bouncycastle.pqc.jcajce.provider.util.SpecUtil; +import org.bouncycastle.pqc.jcajce.spec.SnovaParameterSpec; +import org.bouncycastle.util.Strings; + +public class SnovaKeyPairGeneratorSpi + extends java.security.KeyPairGenerator +{ + private static Map parameters = new HashMap(); + + static + { + parameters.put("SNOVA_24_5_4_SSK", SnovaParameters.SNOVA_24_5_4_SSK); + parameters.put("SNOVA_24_5_4_ESK", SnovaParameters.SNOVA_24_5_4_ESK); + parameters.put("SNOVA_24_5_4_SHAKE_SSK", SnovaParameters.SNOVA_24_5_4_SHAKE_SSK); + parameters.put("SNOVA_24_5_4_SHAKE_ESK", SnovaParameters.SNOVA_24_5_4_SHAKE_ESK); + parameters.put("SNOVA_24_5_5_SSK", SnovaParameters.SNOVA_24_5_5_SSK); + parameters.put("SNOVA_24_5_5_ESK", SnovaParameters.SNOVA_24_5_5_ESK); + parameters.put("SNOVA_24_5_5_SHAKE_SSK", SnovaParameters.SNOVA_24_5_5_SHAKE_SSK); + parameters.put("SNOVA_24_5_5_SHAKE_ESK", SnovaParameters.SNOVA_24_5_5_SHAKE_ESK); + parameters.put("SNOVA_25_8_3_SSK", SnovaParameters.SNOVA_25_8_3_SSK); + parameters.put("SNOVA_25_8_3_ESK", SnovaParameters.SNOVA_25_8_3_ESK); + parameters.put("SNOVA_25_8_3_SHAKE_SSK", SnovaParameters.SNOVA_25_8_3_SHAKE_SSK); + parameters.put("SNOVA_25_8_3_SHAKE_ESK", SnovaParameters.SNOVA_25_8_3_SHAKE_ESK); + parameters.put("SNOVA_29_6_5_SSK", SnovaParameters.SNOVA_29_6_5_SSK); + parameters.put("SNOVA_29_6_5_ESK", SnovaParameters.SNOVA_29_6_5_ESK); + parameters.put("SNOVA_29_6_5_SHAKE_SSK", SnovaParameters.SNOVA_29_6_5_SHAKE_SSK); + parameters.put("SNOVA_29_6_5_SHAKE_ESK", SnovaParameters.SNOVA_29_6_5_SHAKE_ESK); + parameters.put("SNOVA_37_8_4_SSK", SnovaParameters.SNOVA_37_8_4_SSK); + parameters.put("SNOVA_37_8_4_ESK", SnovaParameters.SNOVA_37_8_4_ESK); + parameters.put("SNOVA_37_8_4_SHAKE_SSK", SnovaParameters.SNOVA_37_8_4_SHAKE_SSK); + parameters.put("SNOVA_37_8_4_SHAKE_ESK", SnovaParameters.SNOVA_37_8_4_SHAKE_ESK); + parameters.put("SNOVA_37_17_2_SSK", SnovaParameters.SNOVA_37_17_2_SSK); + parameters.put("SNOVA_37_17_2_ESK", SnovaParameters.SNOVA_37_17_2_ESK); + parameters.put("SNOVA_37_17_2_SHAKE_SSK", SnovaParameters.SNOVA_37_17_2_SHAKE_SSK); + parameters.put("SNOVA_37_17_2_SHAKE_ESK", SnovaParameters.SNOVA_37_17_2_SHAKE_ESK); + parameters.put("SNOVA_49_11_3_SSK", SnovaParameters.SNOVA_49_11_3_SSK); + parameters.put("SNOVA_49_11_3_ESK", SnovaParameters.SNOVA_49_11_3_ESK); + parameters.put("SNOVA_49_11_3_SHAKE_SSK", SnovaParameters.SNOVA_49_11_3_SHAKE_SSK); + parameters.put("SNOVA_49_11_3_SHAKE_ESK", SnovaParameters.SNOVA_49_11_3_SHAKE_ESK); + parameters.put("SNOVA_56_25_2_SSK", SnovaParameters.SNOVA_56_25_2_SSK); + parameters.put("SNOVA_56_25_2_ESK", SnovaParameters.SNOVA_56_25_2_ESK); + parameters.put("SNOVA_56_25_2_SHAKE_SSK", SnovaParameters.SNOVA_56_25_2_SHAKE_SSK); + parameters.put("SNOVA_56_25_2_SHAKE_ESK", SnovaParameters.SNOVA_56_25_2_SHAKE_ESK); + parameters.put("SNOVA_60_10_4_SSK", SnovaParameters.SNOVA_60_10_4_SSK); + parameters.put("SNOVA_60_10_4_ESK", SnovaParameters.SNOVA_60_10_4_ESK); + parameters.put("SNOVA_60_10_4_SHAKE_SSK", SnovaParameters.SNOVA_60_10_4_SHAKE_SSK); + parameters.put("SNOVA_60_10_4_SHAKE_ESK", SnovaParameters.SNOVA_60_10_4_SHAKE_ESK); + parameters.put("SNOVA_66_15_3_SSK", SnovaParameters.SNOVA_66_15_3_SSK); + parameters.put("SNOVA_66_15_3_ESK", SnovaParameters.SNOVA_66_15_3_ESK); + parameters.put("SNOVA_66_15_3_SHAKE_SSK", SnovaParameters.SNOVA_66_15_3_SHAKE_SSK); + parameters.put("SNOVA_66_15_3_SHAKE_ESK", SnovaParameters.SNOVA_66_15_3_SHAKE_ESK); + parameters.put("SNOVA_75_33_2_SSK", SnovaParameters.SNOVA_75_33_2_SSK); + parameters.put("SNOVA_75_33_2_ESK", SnovaParameters.SNOVA_75_33_2_ESK); + parameters.put("SNOVA_75_33_2_SHAKE_SSK", SnovaParameters.SNOVA_75_33_2_SHAKE_SSK); + parameters.put("SNOVA_75_33_2_SHAKE_ESK", SnovaParameters.SNOVA_75_33_2_SHAKE_ESK); + parameters.put(SnovaParameterSpec.SNOVA_24_5_4_SSK.getName(), SnovaParameters.SNOVA_24_5_4_SSK); + parameters.put(SnovaParameterSpec.SNOVA_24_5_4_ESK.getName(), SnovaParameters.SNOVA_24_5_4_ESK); + parameters.put(SnovaParameterSpec.SNOVA_24_5_4_SHAKE_SSK.getName(), SnovaParameters.SNOVA_24_5_4_SHAKE_SSK); + parameters.put(SnovaParameterSpec.SNOVA_24_5_4_SHAKE_ESK.getName(), SnovaParameters.SNOVA_24_5_4_SHAKE_ESK); + parameters.put(SnovaParameterSpec.SNOVA_24_5_5_SSK.getName(), SnovaParameters.SNOVA_24_5_5_SSK); + parameters.put(SnovaParameterSpec.SNOVA_24_5_5_ESK.getName(), SnovaParameters.SNOVA_24_5_5_ESK); + parameters.put(SnovaParameterSpec.SNOVA_24_5_5_SHAKE_SSK.getName(), SnovaParameters.SNOVA_24_5_5_SHAKE_SSK); + parameters.put(SnovaParameterSpec.SNOVA_24_5_5_SHAKE_ESK.getName(), SnovaParameters.SNOVA_24_5_5_SHAKE_ESK); + parameters.put(SnovaParameterSpec.SNOVA_25_8_3_SSK.getName(), SnovaParameters.SNOVA_25_8_3_SSK); + parameters.put(SnovaParameterSpec.SNOVA_25_8_3_ESK.getName(), SnovaParameters.SNOVA_25_8_3_ESK); + parameters.put(SnovaParameterSpec.SNOVA_25_8_3_SHAKE_SSK.getName(), SnovaParameters.SNOVA_25_8_3_SHAKE_SSK); + parameters.put(SnovaParameterSpec.SNOVA_25_8_3_SHAKE_ESK.getName(), SnovaParameters.SNOVA_25_8_3_SHAKE_ESK); + parameters.put(SnovaParameterSpec.SNOVA_29_6_5_SSK.getName(), SnovaParameters.SNOVA_29_6_5_SSK); + parameters.put(SnovaParameterSpec.SNOVA_29_6_5_ESK.getName(), SnovaParameters.SNOVA_29_6_5_ESK); + parameters.put(SnovaParameterSpec.SNOVA_29_6_5_SHAKE_SSK.getName(), SnovaParameters.SNOVA_29_6_5_SHAKE_SSK); + parameters.put(SnovaParameterSpec.SNOVA_29_6_5_SHAKE_ESK.getName(), SnovaParameters.SNOVA_29_6_5_SHAKE_ESK); + parameters.put(SnovaParameterSpec.SNOVA_37_8_4_SSK.getName(), SnovaParameters.SNOVA_37_8_4_SSK); + parameters.put(SnovaParameterSpec.SNOVA_37_8_4_ESK.getName(), SnovaParameters.SNOVA_37_8_4_ESK); + parameters.put(SnovaParameterSpec.SNOVA_37_8_4_SHAKE_SSK.getName(), SnovaParameters.SNOVA_37_8_4_SHAKE_SSK); + parameters.put(SnovaParameterSpec.SNOVA_37_8_4_SHAKE_ESK.getName(), SnovaParameters.SNOVA_37_8_4_SHAKE_ESK); + parameters.put(SnovaParameterSpec.SNOVA_37_17_2_SSK.getName(), SnovaParameters.SNOVA_37_17_2_SSK); + parameters.put(SnovaParameterSpec.SNOVA_37_17_2_ESK.getName(), SnovaParameters.SNOVA_37_17_2_ESK); + parameters.put(SnovaParameterSpec.SNOVA_37_17_2_SHAKE_SSK.getName(), SnovaParameters.SNOVA_37_17_2_SHAKE_SSK); + parameters.put(SnovaParameterSpec.SNOVA_37_17_2_SHAKE_ESK.getName(), SnovaParameters.SNOVA_37_17_2_SHAKE_ESK); + parameters.put(SnovaParameterSpec.SNOVA_49_11_3_SSK.getName(), SnovaParameters.SNOVA_49_11_3_SSK); + parameters.put(SnovaParameterSpec.SNOVA_49_11_3_ESK.getName(), SnovaParameters.SNOVA_49_11_3_ESK); + parameters.put(SnovaParameterSpec.SNOVA_49_11_3_SHAKE_SSK.getName(), SnovaParameters.SNOVA_49_11_3_SHAKE_SSK); + parameters.put(SnovaParameterSpec.SNOVA_49_11_3_SHAKE_ESK.getName(), SnovaParameters.SNOVA_49_11_3_SHAKE_ESK); + parameters.put(SnovaParameterSpec.SNOVA_56_25_2_SSK.getName(), SnovaParameters.SNOVA_56_25_2_SSK); + parameters.put(SnovaParameterSpec.SNOVA_56_25_2_ESK.getName(), SnovaParameters.SNOVA_56_25_2_ESK); + parameters.put(SnovaParameterSpec.SNOVA_56_25_2_SHAKE_SSK.getName(), SnovaParameters.SNOVA_56_25_2_SHAKE_SSK); + parameters.put(SnovaParameterSpec.SNOVA_56_25_2_SHAKE_ESK.getName(), SnovaParameters.SNOVA_56_25_2_SHAKE_ESK); + parameters.put(SnovaParameterSpec.SNOVA_60_10_4_SSK.getName(), SnovaParameters.SNOVA_60_10_4_SSK); + parameters.put(SnovaParameterSpec.SNOVA_60_10_4_ESK.getName(), SnovaParameters.SNOVA_60_10_4_ESK); + parameters.put(SnovaParameterSpec.SNOVA_60_10_4_SHAKE_SSK.getName(), SnovaParameters.SNOVA_60_10_4_SHAKE_SSK); + parameters.put(SnovaParameterSpec.SNOVA_60_10_4_SHAKE_ESK.getName(), SnovaParameters.SNOVA_60_10_4_SHAKE_ESK); + parameters.put(SnovaParameterSpec.SNOVA_66_15_3_SSK.getName(), SnovaParameters.SNOVA_66_15_3_SSK); + parameters.put(SnovaParameterSpec.SNOVA_66_15_3_ESK.getName(), SnovaParameters.SNOVA_66_15_3_ESK); + parameters.put(SnovaParameterSpec.SNOVA_66_15_3_SHAKE_SSK.getName(), SnovaParameters.SNOVA_66_15_3_SHAKE_SSK); + parameters.put(SnovaParameterSpec.SNOVA_66_15_3_SHAKE_ESK.getName(), SnovaParameters.SNOVA_66_15_3_SHAKE_ESK); + parameters.put(SnovaParameterSpec.SNOVA_75_33_2_SSK.getName(), SnovaParameters.SNOVA_75_33_2_SSK); + parameters.put(SnovaParameterSpec.SNOVA_75_33_2_ESK.getName(), SnovaParameters.SNOVA_75_33_2_ESK); + parameters.put(SnovaParameterSpec.SNOVA_75_33_2_SHAKE_SSK.getName(), SnovaParameters.SNOVA_75_33_2_SHAKE_SSK); + parameters.put(SnovaParameterSpec.SNOVA_75_33_2_SHAKE_ESK.getName(), SnovaParameters.SNOVA_75_33_2_SHAKE_ESK); + } + + SnovaKeyGenerationParameters param; + private SnovaParameters snovaParameters; + SnovaKeyPairGenerator engine = new SnovaKeyPairGenerator(); + + SecureRandom random = CryptoServicesRegistrar.getSecureRandom(); + boolean initialised = false; + + public SnovaKeyPairGeneratorSpi() + { + super("Snova"); + } + + protected SnovaKeyPairGeneratorSpi(SnovaParameters SnovaParameters) + { + super(SnovaParameters.getName()); + this.snovaParameters = SnovaParameters; + } + + public void initialize( + int strength, + SecureRandom random) + { + throw new IllegalArgumentException("use AlgorithmParameterSpec"); + } + + public void initialize( + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException + { + String name = getNameFromParams(params); + + if (name != null) + { + param = new SnovaKeyGenerationParameters(random, (SnovaParameters)parameters.get(name)); + + engine.init(param); + initialised = true; + } + else + { + throw new InvalidAlgorithmParameterException("invalid ParameterSpec: " + params); + } + } + + private static String getNameFromParams(AlgorithmParameterSpec paramSpec) + { + if (paramSpec instanceof SnovaParameterSpec) + { + SnovaParameterSpec SnovaParams = (SnovaParameterSpec)paramSpec; + return SnovaParams.getName(); + } + else + { + return Strings.toLowerCase(SpecUtil.getNameFrom(paramSpec)); + } + } + + public KeyPair generateKeyPair() + { + if (!initialised) + { + param = new SnovaKeyGenerationParameters(random, SnovaParameters.SNOVA_24_5_4_SSK); + + engine.init(param); + initialised = true; + } + + AsymmetricCipherKeyPair pair = engine.generateKeyPair(); + SnovaPublicKeyParameters pub = (SnovaPublicKeyParameters)pair.getPublic(); + SnovaPrivateKeyParameters priv = (SnovaPrivateKeyParameters)pair.getPrivate(); + + return new KeyPair(new BCSnovaPublicKey(pub), new BCSnovaPrivateKey(priv)); + } + + public static class SNOVA_24_5_4_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_24_5_4_SSK() + { + super(SnovaParameters.SNOVA_24_5_4_SSK); + } + } + + public static class SNOVA_24_5_4_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_24_5_4_ESK() + { + super(SnovaParameters.SNOVA_24_5_4_ESK); + } + } + + public static class SNOVA_24_5_4_SHAKE_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_24_5_4_SHAKE_SSK() + { + super(SnovaParameters.SNOVA_24_5_4_SHAKE_SSK); + } + } + + public static class SNOVA_24_5_4_SHAKE_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_24_5_4_SHAKE_ESK() + { + super(SnovaParameters.SNOVA_24_5_4_SHAKE_ESK); + } + } + + public static class SNOVA_24_5_5_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_24_5_5_SSK() + { + super(SnovaParameters.SNOVA_24_5_5_SSK); + } + } + + public static class SNOVA_24_5_5_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_24_5_5_ESK() + { + super(SnovaParameters.SNOVA_24_5_5_ESK); + } + } + + public static class SNOVA_24_5_5_SHAKE_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_24_5_5_SHAKE_SSK() + { + super(SnovaParameters.SNOVA_24_5_5_SHAKE_SSK); + } + } + + public static class SNOVA_24_5_5_SHAKE_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_24_5_5_SHAKE_ESK() + { + super(SnovaParameters.SNOVA_24_5_5_SHAKE_ESK); + } + } + + public static class SNOVA_25_8_3_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_25_8_3_SSK() + { + super(SnovaParameters.SNOVA_25_8_3_SSK); + } + } + + public static class SNOVA_25_8_3_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_25_8_3_ESK() + { + super(SnovaParameters.SNOVA_25_8_3_ESK); + } + } + + public static class SNOVA_25_8_3_SHAKE_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_25_8_3_SHAKE_SSK() + { + super(SnovaParameters.SNOVA_25_8_3_SHAKE_SSK); + } + } + + public static class SNOVA_25_8_3_SHAKE_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_25_8_3_SHAKE_ESK() + { + super(SnovaParameters.SNOVA_25_8_3_SHAKE_ESK); + } + } + + public static class SNOVA_29_6_5_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_29_6_5_SSK() + { + super(SnovaParameters.SNOVA_29_6_5_SSK); + } + } + + public static class SNOVA_29_6_5_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_29_6_5_ESK() + { + super(SnovaParameters.SNOVA_29_6_5_ESK); + } + } + + public static class SNOVA_29_6_5_SHAKE_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_29_6_5_SHAKE_SSK() + { + super(SnovaParameters.SNOVA_29_6_5_SHAKE_SSK); + } + } + + public static class SNOVA_29_6_5_SHAKE_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_29_6_5_SHAKE_ESK() + { + super(SnovaParameters.SNOVA_29_6_5_SHAKE_ESK); + } + } + + public static class SNOVA_37_8_4_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_37_8_4_SSK() + { + super(SnovaParameters.SNOVA_37_8_4_SSK); + } + } + + public static class SNOVA_37_8_4_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_37_8_4_ESK() + { + super(SnovaParameters.SNOVA_37_8_4_ESK); + } + } + + public static class SNOVA_37_8_4_SHAKE_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_37_8_4_SHAKE_SSK() + { + super(SnovaParameters.SNOVA_37_8_4_SHAKE_SSK); + } + } + + public static class SNOVA_37_8_4_SHAKE_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_37_8_4_SHAKE_ESK() + { + super(SnovaParameters.SNOVA_37_8_4_SHAKE_ESK); + } + } + + public static class SNOVA_37_17_2_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_37_17_2_SSK() + { + super(SnovaParameters.SNOVA_37_17_2_SSK); + } + } + + public static class SNOVA_37_17_2_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_37_17_2_ESK() + { + super(SnovaParameters.SNOVA_37_17_2_ESK); + } + } + + public static class SNOVA_37_17_2_SHAKE_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_37_17_2_SHAKE_SSK() + { + super(SnovaParameters.SNOVA_37_17_2_SHAKE_SSK); + } + } + + public static class SNOVA_37_17_2_SHAKE_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_37_17_2_SHAKE_ESK() + { + super(SnovaParameters.SNOVA_37_17_2_SHAKE_ESK); + } + } + + public static class SNOVA_49_11_3_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_49_11_3_SSK() + { + super(SnovaParameters.SNOVA_49_11_3_SSK); + } + } + + public static class SNOVA_49_11_3_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_49_11_3_ESK() + { + super(SnovaParameters.SNOVA_49_11_3_ESK); + } + } + + public static class SNOVA_49_11_3_SHAKE_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_49_11_3_SHAKE_SSK() + { + super(SnovaParameters.SNOVA_49_11_3_SHAKE_SSK); + } + } + + public static class SNOVA_49_11_3_SHAKE_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_49_11_3_SHAKE_ESK() + { + super(SnovaParameters.SNOVA_49_11_3_SHAKE_ESK); + } + } + + public static class SNOVA_56_25_2_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_56_25_2_SSK() + { + super(SnovaParameters.SNOVA_56_25_2_SSK); + } + } + + public static class SNOVA_56_25_2_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_56_25_2_ESK() + { + super(SnovaParameters.SNOVA_56_25_2_ESK); + } + } + + public static class SNOVA_56_25_2_SHAKE_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_56_25_2_SHAKE_SSK() + { + super(SnovaParameters.SNOVA_56_25_2_SHAKE_SSK); + } + } + + public static class SNOVA_56_25_2_SHAKE_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_56_25_2_SHAKE_ESK() + { + super(SnovaParameters.SNOVA_56_25_2_SHAKE_ESK); + } + } + + public static class SNOVA_60_10_4_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_60_10_4_SSK() + { + super(SnovaParameters.SNOVA_60_10_4_SSK); + } + } + + public static class SNOVA_60_10_4_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_60_10_4_ESK() + { + super(SnovaParameters.SNOVA_60_10_4_ESK); + } + } + + public static class SNOVA_60_10_4_SHAKE_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_60_10_4_SHAKE_SSK() + { + super(SnovaParameters.SNOVA_60_10_4_SHAKE_SSK); + } + } + + public static class SNOVA_60_10_4_SHAKE_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_60_10_4_SHAKE_ESK() + { + super(SnovaParameters.SNOVA_60_10_4_SHAKE_ESK); + } + } + + public static class SNOVA_66_15_3_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_66_15_3_SSK() + { + super(SnovaParameters.SNOVA_66_15_3_SSK); + } + } + + public static class SNOVA_66_15_3_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_66_15_3_ESK() + { + super(SnovaParameters.SNOVA_66_15_3_ESK); + } + } + + public static class SNOVA_66_15_3_SHAKE_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_66_15_3_SHAKE_SSK() + { + super(SnovaParameters.SNOVA_66_15_3_SHAKE_SSK); + } + } + + public static class SNOVA_66_15_3_SHAKE_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_66_15_3_SHAKE_ESK() + { + super(SnovaParameters.SNOVA_66_15_3_SHAKE_ESK); + } + } + + public static class SNOVA_75_33_2_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_75_33_2_SSK() + { + super(SnovaParameters.SNOVA_75_33_2_SSK); + } + } + + public static class SNOVA_75_33_2_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_75_33_2_ESK() + { + super(SnovaParameters.SNOVA_75_33_2_ESK); + } + } + + public static class SNOVA_75_33_2_SHAKE_SSK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_75_33_2_SHAKE_SSK() + { + super(SnovaParameters.SNOVA_75_33_2_SHAKE_SSK); + } + } + + public static class SNOVA_75_33_2_SHAKE_ESK + extends SnovaKeyPairGeneratorSpi + { + public SNOVA_75_33_2_SHAKE_ESK() + { + super(SnovaParameters.SNOVA_75_33_2_SHAKE_ESK); + } + } +} + From 9d609da04ddc6d0b22e43be2d922bc4b3c60815f Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 4 Apr 2025 10:54:07 +1030 Subject: [PATCH 296/890] Add Snova to PQC provider --- .../pqc/crypto/util/PrivateKeyFactory.java | 8 + .../crypto/util/PrivateKeyInfoFactory.java | 8 + .../pqc/crypto/util/PublicKeyFactory.java | 61 ++ .../util/SubjectPublicKeyInfoFactory.java | 8 + .../bouncycastle/pqc/crypto/util/Utils.java | 104 ++++ .../pqc/crypto/test/MayoTest.java | 2 +- .../pqc/crypto/test/SnovaTest.java | 2 +- .../jce/provider/BouncyCastleProvider.java | 42 ++ .../provider/BouncyCastlePQCProvider.java | 2 +- .../pqc/jcajce/provider/Falcon.java | 1 - .../pqc/jcajce/provider/Snova.java | 164 +++++ .../jcajce/provider/snova/SignatureSpi.java | 578 ++++++++++++++++++ 12 files changed, 976 insertions(+), 4 deletions(-) create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Snova.java create mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SignatureSpi.java diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java index e582bfd41e..b07b66ddb6 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java @@ -66,6 +66,8 @@ import org.bouncycastle.pqc.crypto.saber.SABERPrivateKeyParameters; import org.bouncycastle.pqc.crypto.slhdsa.SLHDSAParameters; import org.bouncycastle.pqc.crypto.slhdsa.SLHDSAPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaPrivateKeyParameters; import org.bouncycastle.pqc.crypto.sphincs.SPHINCSPrivateKeyParameters; import org.bouncycastle.pqc.crypto.sphincsplus.SPHINCSPlusParameters; import org.bouncycastle.pqc.crypto.sphincsplus.SPHINCSPlusPrivateKeyParameters; @@ -487,6 +489,12 @@ else if (algOID.on(BCObjectIdentifiers.mayo)) MayoParameters mayoParams = Utils.mayoParamsLookup(algOID); return new MayoPrivateKeyParameters(mayoParams, keyEnc); } + else if (algOID.on(BCObjectIdentifiers.snova)) + { + byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePrivateKey()).getOctets(); + SnovaParameters snovaParams = Utils.snovaParamsLookup(algOID); + return new SnovaPrivateKeyParameters(snovaParams, keyEnc); + } else { throw new RuntimeException("algorithm identifier in private key not recognised"); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java index eb43ac859c..b5d8f692dd 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java @@ -44,6 +44,7 @@ import org.bouncycastle.pqc.crypto.rainbow.RainbowPrivateKeyParameters; import org.bouncycastle.pqc.crypto.saber.SABERPrivateKeyParameters; import org.bouncycastle.pqc.crypto.slhdsa.SLHDSAPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaPrivateKeyParameters; import org.bouncycastle.pqc.crypto.sphincs.SPHINCSPrivateKeyParameters; import org.bouncycastle.pqc.crypto.sphincsplus.SPHINCSPlusPrivateKeyParameters; import org.bouncycastle.pqc.crypto.xmss.BDS; @@ -344,6 +345,13 @@ else if (privateKey instanceof MayoPrivateKeyParameters) byte[] encoding = params.getEncoded(); return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(encoding), attributes); } + else if (privateKey instanceof SnovaPrivateKeyParameters) + { + SnovaPrivateKeyParameters params = (SnovaPrivateKeyParameters)privateKey; + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.snovaOidLookup(params.getParameters())); + byte[] encoding = params.getEncoded(); + return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(encoding), attributes); + } else { throw new IOException("key parameters not recognized"); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java index fb8ebab79a..56e4f38f52 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java @@ -61,6 +61,8 @@ import org.bouncycastle.pqc.crypto.saber.SABERPublicKeyParameters; import org.bouncycastle.pqc.crypto.slhdsa.SLHDSAParameters; import org.bouncycastle.pqc.crypto.slhdsa.SLHDSAPublicKeyParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaPublicKeyParameters; import org.bouncycastle.pqc.crypto.sphincs.SPHINCSPublicKeyParameters; import org.bouncycastle.pqc.crypto.sphincsplus.SPHINCSPlusParameters; import org.bouncycastle.pqc.crypto.sphincsplus.SPHINCSPlusPublicKeyParameters; @@ -260,6 +262,51 @@ public class PublicKeyFactory converters.put(BCObjectIdentifiers.mayo2, new MayoConverter()); converters.put(BCObjectIdentifiers.mayo3, new MayoConverter()); converters.put(BCObjectIdentifiers.mayo5, new MayoConverter()); + + converters.put(BCObjectIdentifiers.snova_24_5_4_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_24_5_4_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_24_5_4_shake_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_24_5_4_shake_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_24_5_5_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_24_5_5_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_24_5_5_shake_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_24_5_5_shake_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_25_8_3_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_25_8_3_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_25_8_3_shake_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_25_8_3_shake_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_29_6_5_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_29_6_5_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_29_6_5_shake_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_29_6_5_shake_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_37_8_4_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_37_8_4_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_37_8_4_shake_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_37_8_4_shake_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_37_17_2_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_37_17_2_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_37_17_2_shake_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_37_17_2_shake_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_49_11_3_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_49_11_3_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_49_11_3_shake_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_49_11_3_shake_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_56_25_2_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_56_25_2_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_56_25_2_shake_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_56_25_2_shake_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_60_10_4_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_60_10_4_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_60_10_4_shake_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_60_10_4_shake_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_66_15_3_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_66_15_3_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_66_15_3_shake_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_66_15_3_shake_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_75_33_2_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_75_33_2_ssk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_75_33_2_shake_esk, new SnovaConverter()); + converters.put(BCObjectIdentifiers.snova_75_33_2_shake_ssk, new SnovaConverter()); } /** @@ -868,4 +915,18 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje return new MayoPublicKeyParameters(mayoParams, keyEnc); } } + + private static class SnovaConverter + extends SubjectPublicKeyInfoConverter + { + AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + throws IOException + { + byte[] keyEnc = ASN1OctetString.getInstance(keyInfo.parsePublicKey()).getOctets(); + + SnovaParameters snovaParams = Utils.snovaParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); + + return new SnovaPublicKeyParameters(snovaParams, keyEnc); + } + } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java index 7edfd3599e..f8f9c436bf 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/SubjectPublicKeyInfoFactory.java @@ -36,6 +36,7 @@ import org.bouncycastle.pqc.crypto.rainbow.RainbowPublicKeyParameters; import org.bouncycastle.pqc.crypto.saber.SABERPublicKeyParameters; import org.bouncycastle.pqc.crypto.slhdsa.SLHDSAPublicKeyParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaPublicKeyParameters; import org.bouncycastle.pqc.crypto.sphincs.SPHINCSPublicKeyParameters; import org.bouncycastle.pqc.crypto.sphincsplus.SPHINCSPlusPublicKeyParameters; import org.bouncycastle.pqc.crypto.xmss.XMSSMTPublicKeyParameters; @@ -310,6 +311,13 @@ else if (publicKey instanceof MayoPublicKeyParameters) AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.mayoOidLookup(params.getParameters())); return new SubjectPublicKeyInfo(algorithmIdentifier, new DEROctetString(encoding)); } + else if (publicKey instanceof SnovaPublicKeyParameters) + { + SnovaPublicKeyParameters params = (SnovaPublicKeyParameters)publicKey; + byte[] encoding = params.getEncoded(); + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.snovaOidLookup(params.getParameters())); + return new SubjectPublicKeyInfo(algorithmIdentifier, new DEROctetString(encoding)); + } else { throw new IOException("key parameters not recognized"); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java index 750504067c..e00540567b 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java @@ -37,6 +37,7 @@ import org.bouncycastle.pqc.crypto.rainbow.RainbowParameters; import org.bouncycastle.pqc.crypto.saber.SABERParameters; import org.bouncycastle.pqc.crypto.slhdsa.SLHDSAParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaParameters; import org.bouncycastle.pqc.crypto.sphincs.SPHINCSKeyParameters; import org.bouncycastle.pqc.crypto.sphincsplus.SPHINCSPlusParameters; import org.bouncycastle.pqc.crypto.xmss.XMSSKeyParameters; @@ -112,6 +113,9 @@ class Utils static final Map mayoOids = new HashMap(); static final Map mayoParams = new HashMap(); + static final Map snovaOids = new HashMap(); + static final Map snovaParams = new HashMap(); + static { categories.put(PQCObjectIdentifiers.qTESLA_p_I, Integers.valueOf(QTESLASecurityCategory.PROVABLY_SECURE_I)); @@ -490,6 +494,96 @@ class Utils mayoParams.put(BCObjectIdentifiers.mayo2, MayoParameters.mayo2); mayoParams.put(BCObjectIdentifiers.mayo3, MayoParameters.mayo3); mayoParams.put(BCObjectIdentifiers.mayo5, MayoParameters.mayo5); + + snovaOids.put(SnovaParameters.SNOVA_24_5_4_SSK, BCObjectIdentifiers.snova_24_5_4_ssk); + snovaOids.put(SnovaParameters.SNOVA_24_5_4_ESK, BCObjectIdentifiers.snova_24_5_4_esk); + snovaOids.put(SnovaParameters.SNOVA_24_5_4_SHAKE_SSK, BCObjectIdentifiers.snova_24_5_4_shake_ssk); + snovaOids.put(SnovaParameters.SNOVA_24_5_4_SHAKE_ESK, BCObjectIdentifiers.snova_24_5_4_shake_esk); + snovaOids.put(SnovaParameters.SNOVA_24_5_5_SSK, BCObjectIdentifiers.snova_24_5_5_ssk); + snovaOids.put(SnovaParameters.SNOVA_24_5_5_ESK, BCObjectIdentifiers.snova_24_5_5_esk); + snovaOids.put(SnovaParameters.SNOVA_24_5_5_SHAKE_SSK, BCObjectIdentifiers.snova_24_5_5_shake_ssk); + snovaOids.put(SnovaParameters.SNOVA_24_5_5_SHAKE_ESK, BCObjectIdentifiers.snova_24_5_5_shake_esk); + snovaOids.put(SnovaParameters.SNOVA_25_8_3_SSK, BCObjectIdentifiers.snova_25_8_3_ssk); + snovaOids.put(SnovaParameters.SNOVA_25_8_3_ESK, BCObjectIdentifiers.snova_25_8_3_esk); + snovaOids.put(SnovaParameters.SNOVA_25_8_3_SHAKE_SSK, BCObjectIdentifiers.snova_25_8_3_shake_ssk); + snovaOids.put(SnovaParameters.SNOVA_25_8_3_SHAKE_ESK, BCObjectIdentifiers.snova_25_8_3_shake_esk); + snovaOids.put(SnovaParameters.SNOVA_29_6_5_SSK, BCObjectIdentifiers.snova_29_6_5_ssk); + snovaOids.put(SnovaParameters.SNOVA_29_6_5_ESK, BCObjectIdentifiers.snova_29_6_5_esk); + snovaOids.put(SnovaParameters.SNOVA_29_6_5_SHAKE_SSK, BCObjectIdentifiers.snova_29_6_5_shake_ssk); + snovaOids.put(SnovaParameters.SNOVA_29_6_5_SHAKE_ESK, BCObjectIdentifiers.snova_29_6_5_shake_esk); + snovaOids.put(SnovaParameters.SNOVA_37_8_4_SSK, BCObjectIdentifiers.snova_37_8_4_ssk); + snovaOids.put(SnovaParameters.SNOVA_37_8_4_ESK, BCObjectIdentifiers.snova_37_8_4_esk); + snovaOids.put(SnovaParameters.SNOVA_37_8_4_SHAKE_SSK, BCObjectIdentifiers.snova_37_8_4_shake_ssk); + snovaOids.put(SnovaParameters.SNOVA_37_8_4_SHAKE_ESK, BCObjectIdentifiers.snova_37_8_4_shake_esk); + snovaOids.put(SnovaParameters.SNOVA_37_17_2_SSK, BCObjectIdentifiers.snova_37_17_2_ssk); + snovaOids.put(SnovaParameters.SNOVA_37_17_2_ESK, BCObjectIdentifiers.snova_37_17_2_esk); + snovaOids.put(SnovaParameters.SNOVA_37_17_2_SHAKE_SSK, BCObjectIdentifiers.snova_37_17_2_shake_ssk); + snovaOids.put(SnovaParameters.SNOVA_37_17_2_SHAKE_ESK, BCObjectIdentifiers.snova_37_17_2_shake_esk); + snovaOids.put(SnovaParameters.SNOVA_49_11_3_SSK, BCObjectIdentifiers.snova_49_11_3_ssk); + snovaOids.put(SnovaParameters.SNOVA_49_11_3_ESK, BCObjectIdentifiers.snova_49_11_3_esk); + snovaOids.put(SnovaParameters.SNOVA_49_11_3_SHAKE_SSK, BCObjectIdentifiers.snova_49_11_3_shake_ssk); + snovaOids.put(SnovaParameters.SNOVA_49_11_3_SHAKE_ESK, BCObjectIdentifiers.snova_49_11_3_shake_esk); + snovaOids.put(SnovaParameters.SNOVA_56_25_2_SSK, BCObjectIdentifiers.snova_56_25_2_ssk); + snovaOids.put(SnovaParameters.SNOVA_56_25_2_ESK, BCObjectIdentifiers.snova_56_25_2_esk); + snovaOids.put(SnovaParameters.SNOVA_56_25_2_SHAKE_SSK, BCObjectIdentifiers.snova_56_25_2_shake_ssk); + snovaOids.put(SnovaParameters.SNOVA_56_25_2_SHAKE_ESK, BCObjectIdentifiers.snova_56_25_2_shake_esk); + snovaOids.put(SnovaParameters.SNOVA_60_10_4_SSK, BCObjectIdentifiers.snova_60_10_4_ssk); + snovaOids.put(SnovaParameters.SNOVA_60_10_4_ESK, BCObjectIdentifiers.snova_60_10_4_esk); + snovaOids.put(SnovaParameters.SNOVA_60_10_4_SHAKE_SSK, BCObjectIdentifiers.snova_60_10_4_shake_ssk); + snovaOids.put(SnovaParameters.SNOVA_60_10_4_SHAKE_ESK, BCObjectIdentifiers.snova_60_10_4_shake_esk); + snovaOids.put(SnovaParameters.SNOVA_66_15_3_SSK, BCObjectIdentifiers.snova_66_15_3_ssk); + snovaOids.put(SnovaParameters.SNOVA_66_15_3_ESK, BCObjectIdentifiers.snova_66_15_3_esk); + snovaOids.put(SnovaParameters.SNOVA_66_15_3_SHAKE_SSK, BCObjectIdentifiers.snova_66_15_3_shake_ssk); + snovaOids.put(SnovaParameters.SNOVA_66_15_3_SHAKE_ESK, BCObjectIdentifiers.snova_66_15_3_shake_esk); + snovaOids.put(SnovaParameters.SNOVA_75_33_2_SSK, BCObjectIdentifiers.snova_75_33_2_ssk); + snovaOids.put(SnovaParameters.SNOVA_75_33_2_ESK, BCObjectIdentifiers.snova_75_33_2_esk); + snovaOids.put(SnovaParameters.SNOVA_75_33_2_SHAKE_SSK, BCObjectIdentifiers.snova_75_33_2_shake_ssk); + snovaOids.put(SnovaParameters.SNOVA_75_33_2_SHAKE_ESK, BCObjectIdentifiers.snova_75_33_2_shake_esk); + + snovaParams.put(BCObjectIdentifiers.snova_24_5_4_ssk, SnovaParameters.SNOVA_24_5_4_SSK); + snovaParams.put(BCObjectIdentifiers.snova_24_5_4_esk, SnovaParameters.SNOVA_24_5_4_ESK); + snovaParams.put(BCObjectIdentifiers.snova_24_5_4_shake_ssk, SnovaParameters.SNOVA_24_5_4_SHAKE_SSK); + snovaParams.put(BCObjectIdentifiers.snova_24_5_4_shake_esk, SnovaParameters.SNOVA_24_5_4_SHAKE_ESK); + snovaParams.put(BCObjectIdentifiers.snova_24_5_5_ssk, SnovaParameters.SNOVA_24_5_5_SSK); + snovaParams.put(BCObjectIdentifiers.snova_24_5_5_esk, SnovaParameters.SNOVA_24_5_5_ESK); + snovaParams.put(BCObjectIdentifiers.snova_24_5_5_shake_ssk, SnovaParameters.SNOVA_24_5_5_SHAKE_SSK); + snovaParams.put(BCObjectIdentifiers.snova_24_5_5_shake_esk, SnovaParameters.SNOVA_24_5_5_SHAKE_ESK); + snovaParams.put(BCObjectIdentifiers.snova_25_8_3_ssk, SnovaParameters.SNOVA_25_8_3_SSK); + snovaParams.put(BCObjectIdentifiers.snova_25_8_3_esk, SnovaParameters.SNOVA_25_8_3_ESK); + snovaParams.put(BCObjectIdentifiers.snova_25_8_3_shake_ssk, SnovaParameters.SNOVA_25_8_3_SHAKE_SSK); + snovaParams.put(BCObjectIdentifiers.snova_25_8_3_shake_esk, SnovaParameters.SNOVA_25_8_3_SHAKE_ESK); + snovaParams.put(BCObjectIdentifiers.snova_29_6_5_ssk, SnovaParameters.SNOVA_29_6_5_SSK); + snovaParams.put(BCObjectIdentifiers.snova_29_6_5_esk, SnovaParameters.SNOVA_29_6_5_ESK); + snovaParams.put(BCObjectIdentifiers.snova_29_6_5_shake_ssk, SnovaParameters.SNOVA_29_6_5_SHAKE_SSK); + snovaParams.put(BCObjectIdentifiers.snova_29_6_5_shake_esk, SnovaParameters.SNOVA_29_6_5_SHAKE_ESK); + snovaParams.put(BCObjectIdentifiers.snova_37_8_4_ssk, SnovaParameters.SNOVA_37_8_4_SSK); + snovaParams.put(BCObjectIdentifiers.snova_37_8_4_esk, SnovaParameters.SNOVA_37_8_4_ESK); + snovaParams.put(BCObjectIdentifiers.snova_37_8_4_shake_ssk, SnovaParameters.SNOVA_37_8_4_SHAKE_SSK); + snovaParams.put(BCObjectIdentifiers.snova_37_8_4_shake_esk, SnovaParameters.SNOVA_37_8_4_SHAKE_ESK); + snovaParams.put(BCObjectIdentifiers.snova_37_17_2_ssk, SnovaParameters.SNOVA_37_17_2_SSK); + snovaParams.put(BCObjectIdentifiers.snova_37_17_2_esk, SnovaParameters.SNOVA_37_17_2_ESK); + snovaParams.put(BCObjectIdentifiers.snova_37_17_2_shake_ssk, SnovaParameters.SNOVA_37_17_2_SHAKE_SSK); + snovaParams.put(BCObjectIdentifiers.snova_37_17_2_shake_esk, SnovaParameters.SNOVA_37_17_2_SHAKE_ESK); + snovaParams.put(BCObjectIdentifiers.snova_49_11_3_ssk, SnovaParameters.SNOVA_49_11_3_SSK); + snovaParams.put(BCObjectIdentifiers.snova_49_11_3_esk, SnovaParameters.SNOVA_49_11_3_ESK); + snovaParams.put(BCObjectIdentifiers.snova_49_11_3_shake_ssk, SnovaParameters.SNOVA_49_11_3_SHAKE_SSK); + snovaParams.put(BCObjectIdentifiers.snova_49_11_3_shake_esk, SnovaParameters.SNOVA_49_11_3_SHAKE_ESK); + snovaParams.put(BCObjectIdentifiers.snova_56_25_2_ssk, SnovaParameters.SNOVA_56_25_2_SSK); + snovaParams.put(BCObjectIdentifiers.snova_56_25_2_esk, SnovaParameters.SNOVA_56_25_2_ESK); + snovaParams.put(BCObjectIdentifiers.snova_56_25_2_shake_ssk, SnovaParameters.SNOVA_56_25_2_SHAKE_SSK); + snovaParams.put(BCObjectIdentifiers.snova_56_25_2_shake_esk, SnovaParameters.SNOVA_56_25_2_SHAKE_ESK); + snovaParams.put(BCObjectIdentifiers.snova_60_10_4_ssk, SnovaParameters.SNOVA_60_10_4_SSK); + snovaParams.put(BCObjectIdentifiers.snova_60_10_4_esk, SnovaParameters.SNOVA_60_10_4_ESK); + snovaParams.put(BCObjectIdentifiers.snova_60_10_4_shake_ssk, SnovaParameters.SNOVA_60_10_4_SHAKE_SSK); + snovaParams.put(BCObjectIdentifiers.snova_60_10_4_shake_esk, SnovaParameters.SNOVA_60_10_4_SHAKE_ESK); + snovaParams.put(BCObjectIdentifiers.snova_66_15_3_ssk, SnovaParameters.SNOVA_66_15_3_SSK); + snovaParams.put(BCObjectIdentifiers.snova_66_15_3_esk, SnovaParameters.SNOVA_66_15_3_ESK); + snovaParams.put(BCObjectIdentifiers.snova_66_15_3_shake_ssk, SnovaParameters.SNOVA_66_15_3_SHAKE_SSK); + snovaParams.put(BCObjectIdentifiers.snova_66_15_3_shake_esk, SnovaParameters.SNOVA_66_15_3_SHAKE_ESK); + snovaParams.put(BCObjectIdentifiers.snova_75_33_2_ssk, SnovaParameters.SNOVA_75_33_2_SSK); + snovaParams.put(BCObjectIdentifiers.snova_75_33_2_esk, SnovaParameters.SNOVA_75_33_2_ESK); + snovaParams.put(BCObjectIdentifiers.snova_75_33_2_shake_ssk, SnovaParameters.SNOVA_75_33_2_SHAKE_SSK); + snovaParams.put(BCObjectIdentifiers.snova_75_33_2_shake_esk, SnovaParameters.SNOVA_75_33_2_SHAKE_ESK); } static ASN1ObjectIdentifier slhdsaOidLookup(SLHDSAParameters params) @@ -812,6 +906,16 @@ static MayoParameters mayoParamsLookup(ASN1ObjectIdentifier oid) return (MayoParameters)mayoParams.get(oid); } + static ASN1ObjectIdentifier snovaOidLookup(SnovaParameters params) + { + return (ASN1ObjectIdentifier)snovaOids.get(params); + } + + static SnovaParameters snovaParamsLookup(ASN1ObjectIdentifier oid) + { + return (SnovaParameters)snovaParams.get(oid); + } + private static boolean isRaw(byte[] data) { // check well-formed first diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java index d68d04930f..4cdab9d282 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java @@ -46,7 +46,7 @@ public void testTestVectors() throws Exception { long start = System.currentTimeMillis(); - TestUtils.testTestVector(false, false, "pqc/crypto/mayo", files, new TestUtils.KeyGenerationOperation() + TestUtils.testTestVector(true, false, "pqc/crypto/mayo", files, new TestUtils.KeyGenerationOperation() { @Override public SecureRandom getSecureRanom(byte[] seed) diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java index 8ca0634ee0..0b23a9af97 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java @@ -126,7 +126,7 @@ public void testTestVectors() throws Exception { long start = System.currentTimeMillis(); - TestUtils.testTestVector(false, false, "pqc/crypto/snova", files, new TestUtils.KeyGenerationOperation() + TestUtils.testTestVector(true, false, "pqc/crypto/snova", files, new TestUtils.KeyGenerationOperation() { @Override public SecureRandom getSecureRanom(byte[] seed) diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java index f59524555f..39e3940476 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java @@ -42,6 +42,7 @@ import org.bouncycastle.pqc.jcajce.provider.newhope.NHKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.ntru.NTRUKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.picnic.PicnicKeyFactorySpi; +import org.bouncycastle.pqc.jcajce.provider.snova.SnovaKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.sphincs.Sphincs256KeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.sphincsplus.SPHINCSPlusKeyFactorySpi; import org.bouncycastle.pqc.jcajce.provider.xmss.XMSSKeyFactorySpi; @@ -443,6 +444,47 @@ private void loadPQCKeys() addKeyInfoConverter(BCObjectIdentifiers.mayo2, new MayoKeyFactorySpi()); addKeyInfoConverter(BCObjectIdentifiers.mayo3, new MayoKeyFactorySpi()); addKeyInfoConverter(BCObjectIdentifiers.mayo5, new MayoKeyFactorySpi()); + + addKeyInfoConverter(BCObjectIdentifiers.snova_24_5_4_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_24_5_4_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_24_5_4_shake_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_24_5_4_shake_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_24_5_5_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_24_5_5_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_24_5_5_shake_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_24_5_5_shake_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_25_8_3_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_25_8_3_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_25_8_3_shake_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_25_8_3_shake_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_37_8_4_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_37_8_4_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_37_8_4_shake_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_37_8_4_shake_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_37_17_2_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_37_17_2_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_37_17_2_shake_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_37_17_2_shake_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_49_11_3_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_49_11_3_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_49_11_3_shake_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_49_11_3_shake_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_56_25_2_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_56_25_2_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_56_25_2_shake_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_56_25_2_shake_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_60_10_4_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_60_10_4_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_60_10_4_shake_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_60_10_4_shake_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_66_15_3_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_66_15_3_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_66_15_3_shake_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_66_15_3_shake_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_75_33_2_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_75_33_2_esk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_75_33_2_shake_ssk, new SnovaKeyFactorySpi()); + addKeyInfoConverter(BCObjectIdentifiers.snova_75_33_2_shake_esk, new SnovaKeyFactorySpi()); } public void setParameter(String parameterName, Object parameter) diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java index b616736f5b..947a221945 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java @@ -41,7 +41,7 @@ public class BouncyCastlePQCProvider "SPHINCS", "LMS", "NH", "XMSS", "SPHINCSPlus", "CMCE", "Frodo", "SABER", "Picnic", "NTRU", "Falcon", "Kyber", "Dilithium", "NTRUPrime", "BIKE", "HQC", "Rainbow", - "Mayo" + "Mayo", "Snova" }; /** diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Falcon.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Falcon.java index 0af7244a1a..c8e56d21ee 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Falcon.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Falcon.java @@ -3,7 +3,6 @@ import org.bouncycastle.asn1.bc.BCObjectIdentifiers; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; -import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; import org.bouncycastle.pqc.jcajce.provider.falcon.FalconKeyFactorySpi; public class Falcon diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Snova.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Snova.java new file mode 100644 index 0000000000..4ad9ea91e8 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Snova.java @@ -0,0 +1,164 @@ +package org.bouncycastle.pqc.jcajce.provider; + +import org.bouncycastle.asn1.bc.BCObjectIdentifiers; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; +import org.bouncycastle.pqc.jcajce.provider.snova.SnovaKeyFactorySpi; + +public class Snova +{ + private static final String PREFIX = "org.bouncycastle.pqc.jcajce.provider.snova."; + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("KeyFactory.Snova", PREFIX + "SnovaKeyFactorySpi"); + + addKeyFactoryAlgorithm(provider, "SNOVA_24_5_4_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_24_5_4_SSK", BCObjectIdentifiers.snova_24_5_4_ssk, new SnovaKeyFactorySpi.SNOVA_24_5_4_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_24_5_4_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_24_5_4_ESK", BCObjectIdentifiers.snova_24_5_4_esk, new SnovaKeyFactorySpi.SNOVA_24_5_4_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_24_5_4_SHAKE_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_24_5_4_SHAKE_SSK", BCObjectIdentifiers.snova_24_5_4_shake_ssk, new SnovaKeyFactorySpi.SNOVA_24_5_4_SHAKE_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_24_5_4_SHAKE_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_24_5_4_SHAKE_ESK", BCObjectIdentifiers.snova_24_5_4_shake_esk, new SnovaKeyFactorySpi.SNOVA_24_5_4_SHAKE_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_24_5_5_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_24_5_5_SSK", BCObjectIdentifiers.snova_24_5_5_ssk, new SnovaKeyFactorySpi.SNOVA_24_5_5_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_24_5_5_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_24_5_5_ESK", BCObjectIdentifiers.snova_24_5_5_esk, new SnovaKeyFactorySpi.SNOVA_24_5_5_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_24_5_5_SHAKE_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_24_5_5_SHAKE_SSK", BCObjectIdentifiers.snova_24_5_5_shake_ssk, new SnovaKeyFactorySpi.SNOVA_24_5_5_SHAKE_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_24_5_5_SHAKE_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_24_5_5_SHAKE_ESK", BCObjectIdentifiers.snova_24_5_5_shake_esk, new SnovaKeyFactorySpi.SNOVA_24_5_5_SHAKE_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_25_8_3_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_25_8_3_SSK", BCObjectIdentifiers.snova_25_8_3_ssk, new SnovaKeyFactorySpi.SNOVA_25_8_3_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_25_8_3_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_25_8_3_ESK", BCObjectIdentifiers.snova_25_8_3_esk, new SnovaKeyFactorySpi.SNOVA_25_8_3_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_25_8_3_SHAKE_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_25_8_3_SHAKE_SSK", BCObjectIdentifiers.snova_25_8_3_shake_ssk, new SnovaKeyFactorySpi.SNOVA_25_8_3_SHAKE_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_25_8_3_SHAKE_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_25_8_3_SHAKE_ESK", BCObjectIdentifiers.snova_25_8_3_shake_esk, new SnovaKeyFactorySpi.SNOVA_25_8_3_SHAKE_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_29_6_5_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_29_6_5_SSK", BCObjectIdentifiers.snova_29_6_5_ssk, new SnovaKeyFactorySpi.SNOVA_29_6_5_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_29_6_5_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_29_6_5_ESK", BCObjectIdentifiers.snova_29_6_5_esk, new SnovaKeyFactorySpi.SNOVA_29_6_5_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_29_6_5_SHAKE_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_29_6_5_SHAKE_SSK", BCObjectIdentifiers.snova_29_6_5_shake_ssk, new SnovaKeyFactorySpi.SNOVA_29_6_5_SHAKE_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_29_6_5_SHAKE_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_29_6_5_SHAKE_ESK", BCObjectIdentifiers.snova_29_6_5_shake_esk, new SnovaKeyFactorySpi.SNOVA_29_6_5_SHAKE_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_37_8_4_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_37_8_4_SSK", BCObjectIdentifiers.snova_37_8_4_ssk, new SnovaKeyFactorySpi.SNOVA_37_8_4_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_37_8_4_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_37_8_4_ESK", BCObjectIdentifiers.snova_37_8_4_esk, new SnovaKeyFactorySpi.SNOVA_37_8_4_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_37_8_4_SHAKE_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_37_8_4_SHAKE_SSK", BCObjectIdentifiers.snova_37_8_4_shake_ssk, new SnovaKeyFactorySpi.SNOVA_37_8_4_SHAKE_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_37_8_4_SHAKE_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_37_8_4_SHAKE_ESK", BCObjectIdentifiers.snova_37_8_4_shake_esk, new SnovaKeyFactorySpi.SNOVA_37_8_4_SHAKE_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_37_17_2_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_37_17_2_SSK", BCObjectIdentifiers.snova_37_17_2_ssk, new SnovaKeyFactorySpi.SNOVA_37_17_2_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_37_17_2_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_37_17_2_ESK", BCObjectIdentifiers.snova_37_17_2_esk, new SnovaKeyFactorySpi.SNOVA_37_17_2_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_37_17_2_SHAKE_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_37_17_2_SHAKE_SSK", BCObjectIdentifiers.snova_37_17_2_shake_ssk, new SnovaKeyFactorySpi.SNOVA_37_17_2_SHAKE_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_37_17_2_SHAKE_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_37_17_2_SHAKE_ESK", BCObjectIdentifiers.snova_37_17_2_shake_esk, new SnovaKeyFactorySpi.SNOVA_37_17_2_SHAKE_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_49_11_3_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_49_11_3_SSK", BCObjectIdentifiers.snova_49_11_3_ssk, new SnovaKeyFactorySpi.SNOVA_49_11_3_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_49_11_3_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_49_11_3_ESK", BCObjectIdentifiers.snova_49_11_3_esk, new SnovaKeyFactorySpi.SNOVA_49_11_3_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_49_11_3_SHAKE_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_49_11_3_SHAKE_SSK", BCObjectIdentifiers.snova_49_11_3_shake_ssk, new SnovaKeyFactorySpi.SNOVA_49_11_3_SHAKE_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_49_11_3_SHAKE_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_49_11_3_SHAKE_ESK", BCObjectIdentifiers.snova_49_11_3_shake_esk, new SnovaKeyFactorySpi.SNOVA_49_11_3_SHAKE_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_56_25_2_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_56_25_2_SSK", BCObjectIdentifiers.snova_56_25_2_ssk, new SnovaKeyFactorySpi.SNOVA_56_25_2_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_56_25_2_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_56_25_2_ESK", BCObjectIdentifiers.snova_56_25_2_esk, new SnovaKeyFactorySpi.SNOVA_56_25_2_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_56_25_2_SHAKE_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_56_25_2_SHAKE_SSK", BCObjectIdentifiers.snova_56_25_2_shake_ssk, new SnovaKeyFactorySpi.SNOVA_56_25_2_SHAKE_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_56_25_2_SHAKE_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_56_25_2_SHAKE_ESK", BCObjectIdentifiers.snova_56_25_2_shake_esk, new SnovaKeyFactorySpi.SNOVA_56_25_2_SHAKE_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_60_10_4_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_60_10_4_SSK", BCObjectIdentifiers.snova_60_10_4_ssk, new SnovaKeyFactorySpi.SNOVA_60_10_4_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_60_10_4_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_60_10_4_ESK", BCObjectIdentifiers.snova_60_10_4_esk, new SnovaKeyFactorySpi.SNOVA_60_10_4_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_60_10_4_SHAKE_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_60_10_4_SHAKE_SSK", BCObjectIdentifiers.snova_60_10_4_shake_ssk, new SnovaKeyFactorySpi.SNOVA_60_10_4_SHAKE_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_60_10_4_SHAKE_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_60_10_4_SHAKE_ESK", BCObjectIdentifiers.snova_60_10_4_shake_esk, new SnovaKeyFactorySpi.SNOVA_60_10_4_SHAKE_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_66_15_3_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_66_15_3_SSK", BCObjectIdentifiers.snova_66_15_3_ssk, new SnovaKeyFactorySpi.SNOVA_66_15_3_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_66_15_3_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_66_15_3_ESK", BCObjectIdentifiers.snova_66_15_3_esk, new SnovaKeyFactorySpi.SNOVA_66_15_3_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_66_15_3_SHAKE_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_66_15_3_SHAKE_SSK", BCObjectIdentifiers.snova_66_15_3_shake_ssk, new SnovaKeyFactorySpi.SNOVA_66_15_3_SHAKE_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_66_15_3_SHAKE_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_66_15_3_SHAKE_ESK", BCObjectIdentifiers.snova_66_15_3_shake_esk, new SnovaKeyFactorySpi.SNOVA_66_15_3_SHAKE_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_75_33_2_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_75_33_2_SSK", BCObjectIdentifiers.snova_75_33_2_ssk, new SnovaKeyFactorySpi.SNOVA_75_33_2_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_75_33_2_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_75_33_2_ESK", BCObjectIdentifiers.snova_75_33_2_esk, new SnovaKeyFactorySpi.SNOVA_75_33_2_ESK()); + addKeyFactoryAlgorithm(provider, "SNOVA_75_33_2_SHAKE_SSK", PREFIX + "SnovaKeyFactorySpi$SNOVA_75_33_2_SHAKE_SSK", BCObjectIdentifiers.snova_75_33_2_shake_ssk, new SnovaKeyFactorySpi.SNOVA_75_33_2_SHAKE_SSK()); + addKeyFactoryAlgorithm(provider, "SNOVA_75_33_2_SHAKE_ESK", PREFIX + "SnovaKeyFactorySpi$SNOVA_75_33_2_SHAKE_ESK", BCObjectIdentifiers.snova_75_33_2_shake_esk, new SnovaKeyFactorySpi.SNOVA_75_33_2_SHAKE_ESK()); + + + provider.addAlgorithm("KeyPairGenerator.Snova", PREFIX + "SnovaKeyPairGeneratorSpi"); + + addKeyPairGeneratorAlgorithm(provider, "SNOVA_24_5_4_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_24_5_4_SSK", BCObjectIdentifiers.snova_24_5_4_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_24_5_4_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_24_5_4_ESK", BCObjectIdentifiers.snova_24_5_4_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_24_5_4_SHAKE_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_24_5_4_SHAKE_SSK", BCObjectIdentifiers.snova_24_5_4_shake_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_24_5_4_SHAKE_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_24_5_4_SHAKE_ESK", BCObjectIdentifiers.snova_24_5_4_shake_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_24_5_5_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_24_5_5_SSK", BCObjectIdentifiers.snova_24_5_5_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_24_5_5_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_24_5_5_ESK", BCObjectIdentifiers.snova_24_5_5_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_24_5_5_SHAKE_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_24_5_5_SHAKE_SSK", BCObjectIdentifiers.snova_24_5_5_shake_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_24_5_5_SHAKE_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_24_5_5_SHAKE_ESK", BCObjectIdentifiers.snova_24_5_5_shake_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_25_8_3_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_25_8_3_SSK", BCObjectIdentifiers.snova_25_8_3_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_25_8_3_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_25_8_3_ESK", BCObjectIdentifiers.snova_25_8_3_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_25_8_3_SHAKE_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_25_8_3_SHAKE_SSK", BCObjectIdentifiers.snova_25_8_3_shake_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_25_8_3_SHAKE_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_25_8_3_SHAKE_ESK", BCObjectIdentifiers.snova_25_8_3_shake_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_29_6_5_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_29_6_5_SSK", BCObjectIdentifiers.snova_29_6_5_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_29_6_5_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_29_6_5_ESK", BCObjectIdentifiers.snova_29_6_5_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_29_6_5_SHAKE_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_29_6_5_SHAKE_SSK", BCObjectIdentifiers.snova_29_6_5_shake_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_29_6_5_SHAKE_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_29_6_5_SHAKE_ESK", BCObjectIdentifiers.snova_29_6_5_shake_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_37_8_4_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_37_8_4_SSK", BCObjectIdentifiers.snova_37_8_4_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_37_8_4_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_37_8_4_ESK", BCObjectIdentifiers.snova_37_8_4_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_37_8_4_SHAKE_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_37_8_4_SHAKE_SSK", BCObjectIdentifiers.snova_37_8_4_shake_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_37_8_4_SHAKE_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_37_8_4_SHAKE_ESK", BCObjectIdentifiers.snova_37_8_4_shake_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_37_17_2_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_37_17_2_SSK", BCObjectIdentifiers.snova_37_17_2_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_37_17_2_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_37_17_2_ESK", BCObjectIdentifiers.snova_37_17_2_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_37_17_2_SHAKE_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_37_17_2_SHAKE_SSK", BCObjectIdentifiers.snova_37_17_2_shake_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_37_17_2_SHAKE_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_37_17_2_SHAKE_ESK", BCObjectIdentifiers.snova_37_17_2_shake_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_49_11_3_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_49_11_3_SSK", BCObjectIdentifiers.snova_49_11_3_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_49_11_3_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_49_11_3_ESK", BCObjectIdentifiers.snova_49_11_3_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_49_11_3_SHAKE_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_49_11_3_SHAKE_SSK", BCObjectIdentifiers.snova_49_11_3_shake_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_49_11_3_SHAKE_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_49_11_3_SHAKE_ESK", BCObjectIdentifiers.snova_49_11_3_shake_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_56_25_2_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_56_25_2_SSK", BCObjectIdentifiers.snova_56_25_2_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_56_25_2_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_56_25_2_ESK", BCObjectIdentifiers.snova_56_25_2_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_56_25_2_SHAKE_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_56_25_2_SHAKE_SSK", BCObjectIdentifiers.snova_56_25_2_shake_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_56_25_2_SHAKE_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_56_25_2_SHAKE_ESK", BCObjectIdentifiers.snova_56_25_2_shake_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_60_10_4_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_60_10_4_SSK", BCObjectIdentifiers.snova_60_10_4_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_60_10_4_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_60_10_4_ESK", BCObjectIdentifiers.snova_60_10_4_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_60_10_4_SHAKE_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_60_10_4_SHAKE_SSK", BCObjectIdentifiers.snova_60_10_4_shake_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_60_10_4_SHAKE_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_60_10_4_SHAKE_ESK", BCObjectIdentifiers.snova_60_10_4_shake_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_66_15_3_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_66_15_3_SSK", BCObjectIdentifiers.snova_66_15_3_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_66_15_3_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_66_15_3_ESK", BCObjectIdentifiers.snova_66_15_3_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_66_15_3_SHAKE_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_66_15_3_SHAKE_SSK", BCObjectIdentifiers.snova_66_15_3_shake_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_66_15_3_SHAKE_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_66_15_3_SHAKE_ESK", BCObjectIdentifiers.snova_66_15_3_shake_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_75_33_2_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_75_33_2_SSK", BCObjectIdentifiers.snova_75_33_2_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_75_33_2_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_75_33_2_ESK", BCObjectIdentifiers.snova_75_33_2_esk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_75_33_2_SHAKE_SSK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_75_33_2_SHAKE_SSK", BCObjectIdentifiers.snova_75_33_2_shake_ssk); + addKeyPairGeneratorAlgorithm(provider, "SNOVA_75_33_2_SHAKE_ESK", PREFIX + "SnovaKeyPairGeneratorSpi$SNOVA_75_33_2_SHAKE_ESK", BCObjectIdentifiers.snova_75_33_2_shake_esk); + + addSignatureAlgorithm(provider, "Snova", PREFIX + "SignatureSpi$Base", BCObjectIdentifiers.snova); + + addSignatureAlgorithm(provider, "SNOVA_24_5_4_SSK", PREFIX + "SignatureSpi$SNOVA_24_5_4_SSK", BCObjectIdentifiers.snova_24_5_4_ssk); + addSignatureAlgorithm(provider, "SNOVA_24_5_4_ESK", PREFIX + "SignatureSpi$SNOVA_24_5_4_ESK", BCObjectIdentifiers.snova_24_5_4_esk); + addSignatureAlgorithm(provider, "SNOVA_24_5_4_SHAKE_SSK", PREFIX + "SignatureSpi$SNOVA_24_5_4_SHAKE_SSK", BCObjectIdentifiers.snova_24_5_4_shake_ssk); + addSignatureAlgorithm(provider, "SNOVA_24_5_4_SHAKE_ESK", PREFIX + "SignatureSpi$SNOVA_24_5_4_SHAKE_ESK", BCObjectIdentifiers.snova_24_5_4_shake_esk); + addSignatureAlgorithm(provider, "SNOVA_24_5_5_SSK", PREFIX + "SignatureSpi$SNOVA_24_5_5_SSK", BCObjectIdentifiers.snova_24_5_5_ssk); + addSignatureAlgorithm(provider, "SNOVA_24_5_5_ESK", PREFIX + "SignatureSpi$SNOVA_24_5_5_ESK", BCObjectIdentifiers.snova_24_5_5_esk); + addSignatureAlgorithm(provider, "SNOVA_24_5_5_SHAKE_SSK", PREFIX + "SignatureSpi$SNOVA_24_5_5_SHAKE_SSK", BCObjectIdentifiers.snova_24_5_5_shake_ssk); + addSignatureAlgorithm(provider, "SNOVA_24_5_5_SHAKE_ESK", PREFIX + "SignatureSpi$SNOVA_24_5_5_SHAKE_ESK", BCObjectIdentifiers.snova_24_5_5_shake_esk); + addSignatureAlgorithm(provider, "SNOVA_25_8_3_SSK", PREFIX + "SignatureSpi$SNOVA_25_8_3_SSK", BCObjectIdentifiers.snova_25_8_3_ssk); + addSignatureAlgorithm(provider, "SNOVA_25_8_3_ESK", PREFIX + "SignatureSpi$SNOVA_25_8_3_ESK", BCObjectIdentifiers.snova_25_8_3_esk); + addSignatureAlgorithm(provider, "SNOVA_25_8_3_SHAKE_SSK", PREFIX + "SignatureSpi$SNOVA_25_8_3_SHAKE_SSK", BCObjectIdentifiers.snova_25_8_3_shake_ssk); + addSignatureAlgorithm(provider, "SNOVA_25_8_3_SHAKE_ESK", PREFIX + "SignatureSpi$SNOVA_25_8_3_SHAKE_ESK", BCObjectIdentifiers.snova_25_8_3_shake_esk); + addSignatureAlgorithm(provider, "SNOVA_29_6_5_SSK", PREFIX + "SignatureSpi$SNOVA_29_6_5_SSK", BCObjectIdentifiers.snova_29_6_5_ssk); + addSignatureAlgorithm(provider, "SNOVA_29_6_5_ESK", PREFIX + "SignatureSpi$SNOVA_29_6_5_ESK", BCObjectIdentifiers.snova_29_6_5_esk); + addSignatureAlgorithm(provider, "SNOVA_29_6_5_SHAKE_SSK", PREFIX + "SignatureSpi$SNOVA_29_6_5_SHAKE_SSK", BCObjectIdentifiers.snova_29_6_5_shake_ssk); + addSignatureAlgorithm(provider, "SNOVA_29_6_5_SHAKE_ESK", PREFIX + "SignatureSpi$SNOVA_29_6_5_SHAKE_ESK", BCObjectIdentifiers.snova_29_6_5_shake_esk); + addSignatureAlgorithm(provider, "SNOVA_37_8_4_SSK", PREFIX + "SignatureSpi$SNOVA_37_8_4_SSK", BCObjectIdentifiers.snova_37_8_4_ssk); + addSignatureAlgorithm(provider, "SNOVA_37_8_4_ESK", PREFIX + "SignatureSpi$SNOVA_37_8_4_ESK", BCObjectIdentifiers.snova_37_8_4_esk); + addSignatureAlgorithm(provider, "SNOVA_37_8_4_SHAKE_SSK", PREFIX + "SignatureSpi$SNOVA_37_8_4_SHAKE_SSK", BCObjectIdentifiers.snova_37_8_4_shake_ssk); + addSignatureAlgorithm(provider, "SNOVA_37_8_4_SHAKE_ESK", PREFIX + "SignatureSpi$SNOVA_37_8_4_SHAKE_ESK", BCObjectIdentifiers.snova_37_8_4_shake_esk); + addSignatureAlgorithm(provider, "SNOVA_37_17_2_SSK", PREFIX + "SignatureSpi$SNOVA_37_17_2_SSK", BCObjectIdentifiers.snova_37_17_2_ssk); + addSignatureAlgorithm(provider, "SNOVA_37_17_2_ESK", PREFIX + "SignatureSpi$SNOVA_37_17_2_ESK", BCObjectIdentifiers.snova_37_17_2_esk); + addSignatureAlgorithm(provider, "SNOVA_37_17_2_SHAKE_SSK", PREFIX + "SignatureSpi$SNOVA_37_17_2_SHAKE_SSK", BCObjectIdentifiers.snova_37_17_2_shake_ssk); + addSignatureAlgorithm(provider, "SNOVA_37_17_2_SHAKE_ESK", PREFIX + "SignatureSpi$SNOVA_37_17_2_SHAKE_ESK", BCObjectIdentifiers.snova_37_17_2_shake_esk); + addSignatureAlgorithm(provider, "SNOVA_49_11_3_SSK", PREFIX + "SignatureSpi$SNOVA_49_11_3_SSK", BCObjectIdentifiers.snova_49_11_3_ssk); + addSignatureAlgorithm(provider, "SNOVA_49_11_3_ESK", PREFIX + "SignatureSpi$SNOVA_49_11_3_ESK", BCObjectIdentifiers.snova_49_11_3_esk); + addSignatureAlgorithm(provider, "SNOVA_49_11_3_SHAKE_SSK", PREFIX + "SignatureSpi$SNOVA_49_11_3_SHAKE_SSK", BCObjectIdentifiers.snova_49_11_3_shake_ssk); + addSignatureAlgorithm(provider, "SNOVA_49_11_3_SHAKE_ESK", PREFIX + "SignatureSpi$SNOVA_49_11_3_SHAKE_ESK", BCObjectIdentifiers.snova_49_11_3_shake_esk); + addSignatureAlgorithm(provider, "SNOVA_56_25_2_SSK", PREFIX + "SignatureSpi$SNOVA_56_25_2_SSK", BCObjectIdentifiers.snova_56_25_2_ssk); + addSignatureAlgorithm(provider, "SNOVA_56_25_2_ESK", PREFIX + "SignatureSpi$SNOVA_56_25_2_ESK", BCObjectIdentifiers.snova_56_25_2_esk); + addSignatureAlgorithm(provider, "SNOVA_56_25_2_SHAKE_SSK", PREFIX + "SignatureSpi$SNOVA_56_25_2_SHAKE_SSK", BCObjectIdentifiers.snova_56_25_2_shake_ssk); + addSignatureAlgorithm(provider, "SNOVA_56_25_2_SHAKE_ESK", PREFIX + "SignatureSpi$SNOVA_56_25_2_SHAKE_ESK", BCObjectIdentifiers.snova_56_25_2_shake_esk); + addSignatureAlgorithm(provider, "SNOVA_60_10_4_SSK", PREFIX + "SignatureSpi$SNOVA_60_10_4_SSK", BCObjectIdentifiers.snova_60_10_4_ssk); + addSignatureAlgorithm(provider, "SNOVA_60_10_4_ESK", PREFIX + "SignatureSpi$SNOVA_60_10_4_ESK", BCObjectIdentifiers.snova_60_10_4_esk); + addSignatureAlgorithm(provider, "SNOVA_60_10_4_SHAKE_SSK", PREFIX + "SignatureSpi$SNOVA_60_10_4_SHAKE_SSK", BCObjectIdentifiers.snova_60_10_4_shake_ssk); + addSignatureAlgorithm(provider, "SNOVA_60_10_4_SHAKE_ESK", PREFIX + "SignatureSpi$SNOVA_60_10_4_SHAKE_ESK", BCObjectIdentifiers.snova_60_10_4_shake_esk); + addSignatureAlgorithm(provider, "SNOVA_66_15_3_SSK", PREFIX + "SignatureSpi$SNOVA_66_15_3_SSK", BCObjectIdentifiers.snova_66_15_3_ssk); + addSignatureAlgorithm(provider, "SNOVA_66_15_3_ESK", PREFIX + "SignatureSpi$SNOVA_66_15_3_ESK", BCObjectIdentifiers.snova_66_15_3_esk); + addSignatureAlgorithm(provider, "SNOVA_66_15_3_SHAKE_SSK", PREFIX + "SignatureSpi$SNOVA_66_15_3_SHAKE_SSK", BCObjectIdentifiers.snova_66_15_3_shake_ssk); + addSignatureAlgorithm(provider, "SNOVA_66_15_3_SHAKE_ESK", PREFIX + "SignatureSpi$SNOVA_66_15_3_SHAKE_ESK", BCObjectIdentifiers.snova_66_15_3_shake_esk); + addSignatureAlgorithm(provider, "SNOVA_75_33_2_SSK", PREFIX + "SignatureSpi$SNOVA_75_33_2_SSK", BCObjectIdentifiers.snova_75_33_2_ssk); + addSignatureAlgorithm(provider, "SNOVA_75_33_2_ESK", PREFIX + "SignatureSpi$SNOVA_75_33_2_ESK", BCObjectIdentifiers.snova_75_33_2_esk); + addSignatureAlgorithm(provider, "SNOVA_75_33_2_SHAKE_SSK", PREFIX + "SignatureSpi$SNOVA_75_33_2_SHAKE_SSK", BCObjectIdentifiers.snova_75_33_2_shake_ssk); + addSignatureAlgorithm(provider, "SNOVA_75_33_2_SHAKE_ESK", PREFIX + "SignatureSpi$SNOVA_75_33_2_SHAKE_ESK", BCObjectIdentifiers.snova_75_33_2_shake_esk); + } + } +} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SignatureSpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SignatureSpi.java new file mode 100644 index 0000000000..ef3af0ba47 --- /dev/null +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SignatureSpi.java @@ -0,0 +1,578 @@ +package org.bouncycastle.pqc.jcajce.provider.snova; + +import java.io.ByteArrayOutputStream; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.SignatureException; +import java.security.spec.AlgorithmParameterSpec; + +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.pqc.crypto.snova.SnovaParameters; +import org.bouncycastle.pqc.crypto.snova.SnovaSigner; +import org.bouncycastle.util.Strings; + +public class SignatureSpi + extends java.security.Signature +{ + private final ByteArrayOutputStream bOut; + private final SnovaSigner signer; + private SecureRandom random; + private final SnovaParameters parameters; + + protected SignatureSpi(SnovaSigner signer) + { + super("Snova"); + + this.bOut = new ByteArrayOutputStream(); + this.signer = signer; + this.parameters = null; + } + + protected SignatureSpi(SnovaSigner signer, SnovaParameters parameters) + { + super(Strings.toUpperCase(parameters.getName())); + this.parameters = parameters; + + this.bOut = new ByteArrayOutputStream(); + this.signer = signer; + } + + protected void engineInitVerify(PublicKey publicKey) + throws InvalidKeyException + { + if (!(publicKey instanceof BCSnovaPublicKey)) + { + try + { + publicKey = new BCSnovaPublicKey(SubjectPublicKeyInfo.getInstance(publicKey.getEncoded())); + } + catch (Exception e) + { + throw new InvalidKeyException("unknown public key passed to Snova: " + e.getMessage(), e); + } + } + + BCSnovaPublicKey key = (BCSnovaPublicKey)publicKey; + + if (parameters != null) + { + String canonicalAlg = Strings.toUpperCase(parameters.getName()); + if (!canonicalAlg.equals(key.getAlgorithm())) + { + throw new InvalidKeyException("signature configured for " + canonicalAlg); + } + } + + signer.init(false, key.getKeyParams()); + } + + protected void engineInitSign(PrivateKey privateKey, SecureRandom random) + throws InvalidKeyException + { + this.random = random; + engineInitSign(privateKey); + } + + protected void engineInitSign(PrivateKey privateKey) + throws InvalidKeyException + { + if (privateKey instanceof BCSnovaPrivateKey) + { + BCSnovaPrivateKey key = (BCSnovaPrivateKey)privateKey; + CipherParameters param = key.getKeyParams(); + + if (parameters != null) + { + String canonicalAlg = Strings.toUpperCase(parameters.getName()); + if (!canonicalAlg.equals(key.getAlgorithm())) + { + throw new InvalidKeyException("signature configured for " + canonicalAlg); + } + } + + if (random != null) + { + signer.init(true, new ParametersWithRandom(param, random)); + } + else + { + signer.init(true, param); + } + } + else + { + throw new InvalidKeyException("unknown private key passed to Snova"); + } + } + + protected void engineUpdate(byte b) + throws SignatureException + { + bOut.write(b); + } + + protected void engineUpdate(byte[] b, int off, int len) + throws SignatureException + { + bOut.write(b, off, len); + } + + protected byte[] engineSign() + throws SignatureException + { + try + { + byte[] message = bOut.toByteArray(); + + bOut.reset(); + + return signer.generateSignature(message); + } + catch (Exception e) + { + throw new SignatureException(e.toString()); + } + } + + protected boolean engineVerify(byte[] sigBytes) + throws SignatureException + { + byte[] message = bOut.toByteArray(); + + bOut.reset(); + + return signer.verifySignature(message, sigBytes); + } + + protected void engineSetParameter(AlgorithmParameterSpec params) + { + // TODO + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec) + */ + protected void engineSetParameter(String param, Object value) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + /** + * @deprecated + */ + protected Object engineGetParameter(String param) + { + throw new UnsupportedOperationException("engineSetParameter unsupported"); + } + + public static class Base + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public Base() + { + super(new SnovaSigner()); + } + } + + public static class SNOVA_24_5_4_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_24_5_4_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_24_5_4_SSK); + } + } + + public static class SNOVA_24_5_4_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_24_5_4_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_24_5_4_ESK); + } + } + + public static class SNOVA_24_5_4_SHAKE_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_24_5_4_SHAKE_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_24_5_4_SHAKE_ESK); + } + } + + public static class SNOVA_24_5_4_SHAKE_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_24_5_4_SHAKE_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_24_5_4_SHAKE_SSK); + } + } + + public static class SNOVA_24_5_5_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_24_5_5_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_24_5_5_SSK); + } + } + + public static class SNOVA_24_5_5_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_24_5_5_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_24_5_5_ESK); + } + } + + public static class SNOVA_24_5_5_SHAKE_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_24_5_5_SHAKE_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_24_5_5_SHAKE_ESK); + } + } + + public static class SNOVA_24_5_5_SHAKE_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_24_5_5_SHAKE_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_24_5_5_SHAKE_SSK); + } + } + + public static class SNOVA_25_8_3_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_25_8_3_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_25_8_3_SSK); + } + } + + public static class SNOVA_25_8_3_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_25_8_3_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_25_8_3_ESK); + } + } + + public static class SNOVA_25_8_3_SHAKE_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_25_8_3_SHAKE_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_25_8_3_SHAKE_ESK); + } + } + + public static class SNOVA_25_8_3_SHAKE_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_25_8_3_SHAKE_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_25_8_3_SHAKE_SSK); + } + } + + public static class SNOVA_29_6_5_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_29_6_5_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_29_6_5_SSK); + } + } + + public static class SNOVA_29_6_5_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_29_6_5_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_29_6_5_ESK); + } + } + + public static class SNOVA_29_6_5_SHAKE_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_29_6_5_SHAKE_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_29_6_5_SHAKE_ESK); + } + } + + public static class SNOVA_29_6_5_SHAKE_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_29_6_5_SHAKE_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_29_6_5_SHAKE_SSK); + } + } + + public static class SNOVA_37_8_4_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_37_8_4_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_37_8_4_SSK); + } + } + + public static class SNOVA_37_8_4_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_37_8_4_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_37_8_4_ESK); + } + } + + public static class SNOVA_37_8_4_SHAKE_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_37_8_4_SHAKE_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_37_8_4_SHAKE_ESK); + } + } + + public static class SNOVA_37_8_4_SHAKE_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_37_8_4_SHAKE_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_37_8_4_SHAKE_SSK); + } + } + + public static class SNOVA_37_17_2_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_37_17_2_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_37_17_2_SSK); + } + } + + public static class SNOVA_37_17_2_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_37_17_2_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_37_17_2_ESK); + } + } + + public static class SNOVA_37_17_2_SHAKE_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_37_17_2_SHAKE_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_37_17_2_SHAKE_ESK); + } + } + + public static class SNOVA_37_17_2_SHAKE_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_37_17_2_SHAKE_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_37_17_2_SHAKE_SSK); + } + } + + public static class SNOVA_49_11_3_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_49_11_3_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_49_11_3_SSK); + } + } + + public static class SNOVA_49_11_3_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_49_11_3_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_49_11_3_ESK); + } + } + + public static class SNOVA_49_11_3_SHAKE_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_49_11_3_SHAKE_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_49_11_3_SHAKE_ESK); + } + } + + public static class SNOVA_49_11_3_SHAKE_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_49_11_3_SHAKE_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_49_11_3_SHAKE_SSK); + } + } + + public static class SNOVA_56_25_2_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_56_25_2_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_56_25_2_SSK); + } + } + + public static class SNOVA_56_25_2_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_56_25_2_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_56_25_2_ESK); + } + } + + public static class SNOVA_56_25_2_SHAKE_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_56_25_2_SHAKE_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_56_25_2_SHAKE_ESK); + } + } + + public static class SNOVA_56_25_2_SHAKE_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_56_25_2_SHAKE_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_56_25_2_SHAKE_SSK); + } + } + + public static class SNOVA_60_10_4_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_60_10_4_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_60_10_4_SSK); + } + } + + public static class SNOVA_60_10_4_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_60_10_4_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_60_10_4_ESK); + } + } + + public static class SNOVA_60_10_4_SHAKE_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_60_10_4_SHAKE_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_60_10_4_SHAKE_ESK); + } + } + + public static class SNOVA_60_10_4_SHAKE_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_60_10_4_SHAKE_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_60_10_4_SHAKE_SSK); + } + } + + public static class SNOVA_66_15_3_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_66_15_3_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_66_15_3_SSK); + } + } + + public static class SNOVA_66_15_3_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_66_15_3_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_66_15_3_ESK); + } + } + + public static class SNOVA_66_15_3_SHAKE_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_66_15_3_SHAKE_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_66_15_3_SHAKE_ESK); + } + } + + public static class SNOVA_66_15_3_SHAKE_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_66_15_3_SHAKE_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_66_15_3_SHAKE_SSK); + } + } + + public static class SNOVA_75_33_2_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_75_33_2_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_75_33_2_SSK); + } + } + + public static class SNOVA_75_33_2_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_75_33_2_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_75_33_2_ESK); + } + } + + public static class SNOVA_75_33_2_SHAKE_ESK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_75_33_2_SHAKE_ESK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_75_33_2_SHAKE_ESK); + } + } + + public static class SNOVA_75_33_2_SHAKE_SSK + extends org.bouncycastle.pqc.jcajce.provider.snova.SignatureSpi + { + public SNOVA_75_33_2_SHAKE_SSK() + { + super(new SnovaSigner(), SnovaParameters.SNOVA_75_33_2_SHAKE_SSK); + } + } +} + From f76b52ae868715bcf2168fdc628808055d5dce5e Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 4 Apr 2025 11:04:29 +1030 Subject: [PATCH 297/890] replace Mayo.GF16Utils.inverseF with GF16.inv --- .../org/bouncycastle/pqc/crypto/mayo/GF16Utils.java | 13 ------------- .../bouncycastle/pqc/crypto/mayo/MayoSigner.java | 2 +- core/src/main/java/org/bouncycastle/util/GF16.java | 8 ++++++++ 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java index 01308df98d..20d993d0c5 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/GF16Utils.java @@ -208,19 +208,6 @@ static void mulAddMUpperTriangularMatXMatTrans(int mVecLimbs, long[] bsMat, byte } } - /** - * Computes the multiplicative inverse in GF(16) for a GF(16) element. - */ - static byte inverseF(int a) - { - // In GF(16), the inverse can be computed via exponentiation. - int a2 = GF16.mul(a, a); - int a4 = GF16.mul(a2, a2); - int a8 = GF16.mul(a4, a4); - int a6 = GF16.mul(a2, a4); - return (byte)GF16.mul(a8, a6); - } - /** * Performs a GF(16) carryless multiplication of a nibble (lower 4 bits of a) * with a 64-bit word b, then reduces modulo the polynomial x⁴ + x + 1 on each byte. diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java index 0c1d69fa06..3909641eb6 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mayo/MayoSigner.java @@ -762,7 +762,7 @@ void ef(byte[] A, int nrows, int ncols) } // Multiply the pivot row by the inverse of the pivot element. - vecMulAddU64(rowLen, pivotRow, GF16Utils.inverseF(pivot), pivotRow2); + vecMulAddU64(rowLen, pivotRow, GF16.inv((byte)pivot), pivotRow2); // Conditionally write the pivot row back into the correct row (if pivot is nonzero). for (int row = lowerBound, rowRowLen = lowerBound * rowLen; row <= upperBound; row++, rowRowLen += rowLen) diff --git a/core/src/main/java/org/bouncycastle/util/GF16.java b/core/src/main/java/org/bouncycastle/util/GF16.java index 62d2b8026b..57d57d3335 100644 --- a/core/src/main/java/org/bouncycastle/util/GF16.java +++ b/core/src/main/java/org/bouncycastle/util/GF16.java @@ -65,9 +65,17 @@ public static int mul(int a, int b) return MT4B[a << 4 | b]; } + /** + * Computes the multiplicative inverse in GF(16) for a GF(16) element. + */ public static byte inv(byte a) { return INV4B[a & 0xF]; +// int a2 = GF16.mul(a, a); +// int a4 = GF16.mul(a2, a2); +// int a8 = GF16.mul(a4, a4); +// int a6 = GF16.mul(a2, a4); +// return (byte)GF16.mul(a8, a6); } /** From bfa82f1efb5baf895a6770ef9e61dd8ca0ee3f70 Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 4 Apr 2025 11:06:00 +1030 Subject: [PATCH 298/890] Minor changes in GF16.encode --- core/src/main/java/org/bouncycastle/util/GF16.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/util/GF16.java b/core/src/main/java/org/bouncycastle/util/GF16.java index 57d57d3335..8ffa203a03 100644 --- a/core/src/main/java/org/bouncycastle/util/GF16.java +++ b/core/src/main/java/org/bouncycastle/util/GF16.java @@ -134,9 +134,9 @@ public static void decode(byte[] input, int inOff, byte[] output, int outOff, in */ public static void encode(byte[] input, byte[] output, int inputLen) { - int i, inOff = 0; + int i, inOff = 0, blocks = inputLen >> 1; // Process pairs of 4-bit values - for (i = 0; i < inputLen / 2; i++) + for (i = 0; i < blocks; i++) { int lowerNibble = input[inOff++] & 0x0F; int upperNibble = (input[inOff++] & 0x0F) << 4; @@ -151,9 +151,9 @@ public static void encode(byte[] input, byte[] output, int inputLen) public static void encode(byte[] input, byte[] output, int outOff, int inputLen) { - int i, inOff = 0; + int i, inOff = 0, blocks = inputLen >> 1; // Process pairs of 4-bit values - for (i = 0; i < inputLen / 2; i++) + for (i = 0; i < blocks; i++) { int lowerNibble = input[inOff++] & 0x0F; int upperNibble = (input[inOff++] & 0x0F) << 4; From c4523c80bc4ca0a4bf75ab09bfeb366c716be31b Mon Sep 17 00:00:00 2001 From: gefeili Date: Fri, 4 Apr 2025 13:54:40 +1030 Subject: [PATCH 299/890] Fix the input/output issue in ChaCha20Poly1305 --- .../crypto/modes/ChaCha20Poly1305.java | 12 +++++ .../crypto/test/ChaCha20Poly1305Test.java | 53 +++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/ChaCha20Poly1305.java b/core/src/main/java/org/bouncycastle/crypto/modes/ChaCha20Poly1305.java index 2425dec220..ed1291419c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/ChaCha20Poly1305.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/ChaCha20Poly1305.java @@ -307,6 +307,12 @@ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) t { throw new IllegalArgumentException("'outOff' cannot be negative"); } + if (in == out && segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len))) + { + in = new byte[len]; + System.arraycopy(out, inOff, in, 0, len); + inOff = 0; + } checkData(); @@ -611,4 +617,10 @@ private void reset(boolean clearMac, boolean resetCipher) processAADBytes(initialAAD, 0, initialAAD.length); } } + + protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) + { + // please ensure a valid check for inLen > 0 and outLen > 0 outside this function + return inOff <= outOff + outLen && outOff <= inOff + inLen; + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ChaCha20Poly1305Test.java b/core/src/test/java/org/bouncycastle/crypto/test/ChaCha20Poly1305Test.java index 042049f03b..362e5f5b3a 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ChaCha20Poly1305Test.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ChaCha20Poly1305Test.java @@ -3,10 +3,13 @@ import java.security.SecureRandom; import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.engines.AESEngine; import org.bouncycastle.crypto.macs.SipHash; +import org.bouncycastle.crypto.modes.CTSBlockCipher; import org.bouncycastle.crypto.modes.ChaCha20Poly1305; import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; import org.bouncycastle.util.Times; import org.bouncycastle.util.encoders.Hex; @@ -48,6 +51,7 @@ public String getName() public void performTest() throws Exception { + testOverlapping(); for (int i = 0; i < TEST_VECTORS.length; ++i) { runTestCase(TEST_VECTORS[i]); @@ -439,6 +443,55 @@ private void testExceptions() throws InvalidCipherTextException } } + private void testOverlapping() + throws Exception + { + SecureRandom random = new SecureRandom(); + int kLength = 32; + byte[] K = new byte[kLength]; + random.nextBytes(K); + + int aLength = random.nextInt() >>> 24; + byte[] A = new byte[aLength]; + random.nextBytes(A); + + int nonceLength = 12; + byte[] nonce = new byte[nonceLength]; + random.nextBytes(nonce); + + AEADParameters parameters = new AEADParameters(new KeyParameter(K), 16 * 8, nonce, A); + + ChaCha20Poly1305 bc = initCipher(true, parameters); + + final int blockSize = 64; + int offset = 1 + random.nextInt(blockSize - 1) + blockSize; + byte[] data = new byte[blockSize * 4 + offset]; + byte[] expected = new byte[bc.getOutputSize(blockSize * 3)]; + random.nextBytes(data); + + + int len = bc.processBytes(data, 0, blockSize * 3, expected, 0); + bc.doFinal(expected, len); + bc = initCipher(true, parameters); + len = bc.processBytes(data, 0, blockSize * 3, data, offset); + bc.doFinal(data, offset + len); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + + bc = initCipher(false, parameters); + bc.processBytes(data, 0, expected.length, expected, 0); + bc = initCipher(true, parameters); + bc.processBytes(data, 0, expected.length, data, offset); + + if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) + { + fail("failed to overlapping of encryption"); + } + } + public static void main(String[] args) { runTest(new ChaCha20Poly1305Test()); From 027f515ff2c5b1fadcfb1c90af60ecab30f50c7b Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 4 Apr 2025 21:54:31 +0700 Subject: [PATCH 300/890] Restore testKeyFactory --- .../bouncycastle/pqc/jcajce/provider/test/SLHDSATest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/SLHDSATest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/SLHDSATest.java index 7f87ae00e7..e1247839dd 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/SLHDSATest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/SLHDSATest.java @@ -151,14 +151,14 @@ public void testKeyFactory() NISTObjectIdentifiers.id_hash_slh_dsa_shake_256f_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_256s_with_shake256, }; - - for (int i = 0; i != 1; i++) + + for (int i = 0; i != names.length; i++) { KeyPairGenerator kpGen = KeyPairGenerator.getInstance(names[i]); KeyPair kp = kpGen.generateKeyPair(); - System.err.println(names[i]); + tryKeyFact(KeyFactory.getInstance(names[i], "BC"), kp, kp44, "2.16.840.1.101.3.4.3.17"); - // tryKeyFact(KeyFactory.getInstance(oids[i].toString(), "BC"), kp, kp44, "2.16.840.1.101.3.4.3.17"); + tryKeyFact(KeyFactory.getInstance(oids[i].toString(), "BC"), kp, kp44, "2.16.840.1.101.3.4.3.17"); } } From 9077f2f62743c62058a0a8b0b541a76be827bafd Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 6 Apr 2025 10:34:12 +1000 Subject: [PATCH 301/890] cleaned up use of prototype seed only OID. --- .../asn1/bc/BCObjectIdentifiers.java | 15 +-------------- .../org/bouncycastle/pqc/crypto/util/Utils.java | 3 --- .../asymmetric/mldsa/MLDSAKeyFactorySpi.java | 17 ++++++++--------- .../jcajce/provider/util/BaseKeyFactorySpi.java | 11 ----------- 4 files changed, 9 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java index b0e8f7726f..6a48684661 100644 --- a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java @@ -429,20 +429,7 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier hqc128 = pqc_kem_hqc.branch("1"); ASN1ObjectIdentifier hqc192 = pqc_kem_hqc.branch("2"); ASN1ObjectIdentifier hqc256 = pqc_kem_hqc.branch("3"); - - /** - * ML-KEM/ML-DSA seed parameters algorithms - temporary - */ - //TODO: delete before release - ASN1ObjectIdentifier id_id_alg_seed = bc.branch("10"); - - ASN1ObjectIdentifier id_id_alg_ml_dsa_44_seed = id_id_alg_seed.branch("1"); - ASN1ObjectIdentifier id_id_alg_ml_dsa_65_seed = id_id_alg_seed.branch("2"); - ASN1ObjectIdentifier id_id_alg_ml_dsa_87_seed = id_id_alg_seed.branch("3"); - ASN1ObjectIdentifier id_id_alg_ml_kem_512_seed = id_id_alg_seed.branch("4"); - ASN1ObjectIdentifier id_id_alg_ml_kem_768_seed = id_id_alg_seed.branch("5"); - ASN1ObjectIdentifier id_id_alg_ml_kem_1024_seed = id_id_alg_seed.branch("6"); - + /** * Mayo */ diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java index 750504067c..6c4a5ed1cd 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/Utils.java @@ -284,9 +284,6 @@ class Utils mldsaParams.put(NISTObjectIdentifiers.id_ml_dsa_44, MLDSAParameters.ml_dsa_44); mldsaParams.put(NISTObjectIdentifiers.id_ml_dsa_65, MLDSAParameters.ml_dsa_65); mldsaParams.put(NISTObjectIdentifiers.id_ml_dsa_87, MLDSAParameters.ml_dsa_87); - mldsaParams.put(BCObjectIdentifiers.id_id_alg_ml_dsa_44_seed, MLDSAParameters.ml_dsa_44); - mldsaParams.put(BCObjectIdentifiers.id_id_alg_ml_dsa_65_seed, MLDSAParameters.ml_dsa_65); - mldsaParams.put(BCObjectIdentifiers.id_id_alg_ml_dsa_87_seed, MLDSAParameters.ml_dsa_87); mldsaParams.put(NISTObjectIdentifiers.id_hash_ml_dsa_44_with_sha512, MLDSAParameters.ml_dsa_44_with_sha512); mldsaParams.put(NISTObjectIdentifiers.id_hash_ml_dsa_65_with_sha512, MLDSAParameters.ml_dsa_65_with_sha512); mldsaParams.put(NISTObjectIdentifiers.id_hash_ml_dsa_87_with_sha512, MLDSAParameters.ml_dsa_87_with_sha512); diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/MLDSAKeyFactorySpi.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/MLDSAKeyFactorySpi.java index fba0040d6a..9256293d6e 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/MLDSAKeyFactorySpi.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mldsa/MLDSAKeyFactorySpi.java @@ -13,7 +13,6 @@ import java.util.Set; import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.bc.BCObjectIdentifiers; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; @@ -54,9 +53,9 @@ public MLDSAKeyFactorySpi(Set keyOids) this.isHashOnly = false; } - public MLDSAKeyFactorySpi(ASN1ObjectIdentifier keyOid, ASN1ObjectIdentifier seedOid) + public MLDSAKeyFactorySpi(ASN1ObjectIdentifier keyOid) { - super(setOf(keyOid, seedOid)); + super(keyOid); this.isHashOnly = (keyOid.equals(NISTObjectIdentifiers.id_hash_ml_dsa_44_with_sha512) || keyOid.equals(NISTObjectIdentifiers.id_hash_ml_dsa_65_with_sha512) @@ -227,7 +226,7 @@ public static class MLDSA44 { public MLDSA44() { - super(NISTObjectIdentifiers.id_ml_dsa_44, BCObjectIdentifiers.id_id_alg_ml_dsa_44_seed); + super(NISTObjectIdentifiers.id_ml_dsa_44); } } @@ -236,7 +235,7 @@ public static class MLDSA65 { public MLDSA65() { - super(NISTObjectIdentifiers.id_ml_dsa_65, BCObjectIdentifiers.id_id_alg_ml_dsa_65_seed); + super(NISTObjectIdentifiers.id_ml_dsa_65); } } @@ -245,7 +244,7 @@ public static class MLDSA87 { public MLDSA87() { - super(NISTObjectIdentifiers.id_ml_dsa_87, BCObjectIdentifiers.id_id_alg_ml_dsa_87_seed); + super(NISTObjectIdentifiers.id_ml_dsa_87); } } @@ -263,7 +262,7 @@ public static class HashMLDSA44 { public HashMLDSA44() { - super(NISTObjectIdentifiers.id_hash_ml_dsa_44_with_sha512, BCObjectIdentifiers.id_id_alg_ml_dsa_44_seed); + super(NISTObjectIdentifiers.id_hash_ml_dsa_44_with_sha512); } } @@ -272,7 +271,7 @@ public static class HashMLDSA65 { public HashMLDSA65() { - super(NISTObjectIdentifiers.id_hash_ml_dsa_65_with_sha512, BCObjectIdentifiers.id_id_alg_ml_dsa_65_seed); + super(NISTObjectIdentifiers.id_hash_ml_dsa_65_with_sha512); } } @@ -281,7 +280,7 @@ public static class HashMLDSA87 { public HashMLDSA87() { - super(NISTObjectIdentifiers.id_hash_ml_dsa_87_with_sha512, BCObjectIdentifiers.id_id_alg_ml_dsa_87_seed); + super(NISTObjectIdentifiers.id_hash_ml_dsa_87_with_sha512); } } } diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/BaseKeyFactorySpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/BaseKeyFactorySpi.java index e4be781da1..2a64e117e3 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/BaseKeyFactorySpi.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/BaseKeyFactorySpi.java @@ -7,7 +7,6 @@ import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; -import java.util.HashSet; import java.util.Set; import org.bouncycastle.asn1.ASN1ObjectIdentifier; @@ -34,16 +33,6 @@ protected BaseKeyFactorySpi(ASN1ObjectIdentifier keyOid) this.keyOids = null; } - protected static Set setOf(ASN1ObjectIdentifier oid1, ASN1ObjectIdentifier oid2) - { - Set hashSet = new HashSet(2); - - hashSet.add(oid1); - hashSet.add(oid2); - - return hashSet; - } - public PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException { From 15291dcbcbe41e6c044659eea3b9530709fbce63 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 7 Apr 2025 22:08:25 +0700 Subject: [PATCH 302/890] Update parameter names --- .../pqc/crypto/util/PublicKeyFactory.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java index fb8ebab79a..a655d370ce 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java @@ -615,23 +615,23 @@ static class MLKEMConverter AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) throws IOException { - MLKEMParameters kyberParameters = Utils.mlkemParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); + MLKEMParameters parameters = Utils.mlkemParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); try { ASN1Primitive obj = keyInfo.parsePublicKey(); KyberPublicKey kyberKey = KyberPublicKey.getInstance(obj); - return new MLKEMPublicKeyParameters(kyberParameters, kyberKey.getT(), kyberKey.getRho()); + return new MLKEMPublicKeyParameters(parameters, kyberKey.getT(), kyberKey.getRho()); } catch (Exception e) { // we're a raw encoding - return new MLKEMPublicKeyParameters(kyberParameters, keyInfo.getPublicKeyData().getOctets()); + return new MLKEMPublicKeyParameters(parameters, keyInfo.getPublicKeyData().getOctets()); } } - static MLKEMPublicKeyParameters getPublicKeyParams(MLKEMParameters dilithiumParams, ASN1BitString publicKeyData) + static MLKEMPublicKeyParameters getPublicKeyParams(MLKEMParameters parameters, ASN1BitString publicKeyData) { try { @@ -640,7 +640,7 @@ static MLKEMPublicKeyParameters getPublicKeyParams(MLKEMParameters dilithiumPara { ASN1Sequence keySeq = ASN1Sequence.getInstance(obj); - return new MLKEMPublicKeyParameters(dilithiumParams, + return new MLKEMPublicKeyParameters(parameters, ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(), ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets()); } @@ -648,13 +648,13 @@ static MLKEMPublicKeyParameters getPublicKeyParams(MLKEMParameters dilithiumPara { byte[] encKey = ASN1OctetString.getInstance(obj).getOctets(); - return new MLKEMPublicKeyParameters(dilithiumParams, encKey); + return new MLKEMPublicKeyParameters(parameters, encKey); } } catch (Exception e) { // we're a raw encoding - return new MLKEMPublicKeyParameters(dilithiumParams, publicKeyData.getOctets()); + return new MLKEMPublicKeyParameters(parameters, publicKeyData.getOctets()); } } } @@ -737,7 +737,7 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje return getPublicKeyParams(dilithiumParams, keyInfo.getPublicKeyData()); } - static MLDSAPublicKeyParameters getPublicKeyParams(MLDSAParameters dilithiumParams, ASN1BitString publicKeyData) + static MLDSAPublicKeyParameters getPublicKeyParams(MLDSAParameters mlDsaParams, ASN1BitString publicKeyData) { try { @@ -746,7 +746,7 @@ static MLDSAPublicKeyParameters getPublicKeyParams(MLDSAParameters dilithiumPara { ASN1Sequence keySeq = ASN1Sequence.getInstance(obj); - return new MLDSAPublicKeyParameters(dilithiumParams, + return new MLDSAPublicKeyParameters(mlDsaParams, ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(), ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets()); } @@ -754,13 +754,13 @@ static MLDSAPublicKeyParameters getPublicKeyParams(MLDSAParameters dilithiumPara { byte[] encKey = ASN1OctetString.getInstance(obj).getOctets(); - return new MLDSAPublicKeyParameters(dilithiumParams, encKey); + return new MLDSAPublicKeyParameters(mlDsaParams, encKey); } } catch (Exception e) { // we're a raw encoding - return new MLDSAPublicKeyParameters(dilithiumParams, publicKeyData.getOctets()); + return new MLDSAPublicKeyParameters(mlDsaParams, publicKeyData.getOctets()); } } } From 46e036035dde052501eed803e8fd85cfdfece32d Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 7 Apr 2025 22:15:55 +0700 Subject: [PATCH 303/890] Refactor NewSignedDataTest -detachedTest should not encapsulate message --- .../cms/test/NewSignedDataTest.java | 70 +++++++++---------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java index 9127390840..b1676a5250 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java @@ -1,7 +1,6 @@ package org.bouncycastle.cms.test; import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -32,7 +31,6 @@ import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Encoding; -import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1OctetString; @@ -985,10 +983,7 @@ public void testSHA1AndMD5WithRSAEncapsulatedRepeated() CMSSignedData s = gen.generate(msg, true); - ByteArrayInputStream bIn = new ByteArrayInputStream(s.getEncoded()); - ASN1InputStream aIn = new ASN1InputStream(bIn); - - s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject())); + s = new CMSSignedData(s.getEncoded()); certs = s.getCertificates(); @@ -1041,10 +1036,7 @@ public void testSHA1AndMD5WithRSAEncapsulatedRepeated() s = gen.generate(msg, true); - bIn = new ByteArrayInputStream(s.getEncoded()); - aIn = new ASN1InputStream(bIn); - - s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject())); + s = new CMSSignedData(s.getEncoded()); certs = s.getCertificates(); @@ -1851,6 +1843,20 @@ public void testEd25519() expectedDigAlgId); } + public void testEd25519Detached() + throws Exception + { + /* + * RFC 8419 3.1. When signing with Ed25519, the digestAlgorithm MUST be id-sha512, and the algorithm + * parameters field MUST be absent. + * + * We confirm here that our implementation defaults to SHA-512 for the digest algorithm. + */ + AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512); + + detachedTest(_signEd25519KP, _signEd25519Cert, "Ed25519", EdECObjectIdentifiers.id_Ed25519, expectedDigAlgId); + } + public void testEd448() throws Exception { @@ -1867,16 +1873,20 @@ public void testEd448() encapsulatedTest(_signEd448KP, _signEd448Cert, "Ed448", EdECObjectIdentifiers.id_Ed448, expectedDigAlgId); } - public void testDetachedEd25519() + public void testEd448Detached() throws Exception { - detachedTest(_signEd25519KP, _signEd25519Cert, "Ed25519", EdECObjectIdentifiers.id_Ed25519, new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512)); - } + /* + * RFC 8419 3.1. When signing with Ed448, the digestAlgorithm MUST be id-shake256-len, the algorithm + * parameters field MUST be present, and the parameter MUST contain 512, encoded as a positive integer + * value. + * + * We confirm here that our implementation defaults to id-shake256-len/512 for the digest algorithm. + */ + AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256_len, + new ASN1Integer(512)); - public void testEdDetached448() - throws Exception - { - detachedTest(_signEd448KP, _signEd448Cert, "Ed448", EdECObjectIdentifiers.id_Ed448, new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256_len, new ASN1Integer(512))); + detachedTest(_signEd448KP, _signEd448Cert, "Ed448", EdECObjectIdentifiers.id_Ed448, expectedDigAlgId); } public void testEd25519WithNoAttr() @@ -2519,11 +2529,8 @@ private void subjectKeyIDTest( CMSSignedData s = gen.generate(msg, true); assertEquals(3, s.getVersion()); - - ByteArrayInputStream bIn = new ByteArrayInputStream(s.getEncoded()); - ASN1InputStream aIn = new ASN1InputStream(bIn); - s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject())); + s = new CMSSignedData(s.getEncoded()); certStore = s.getCertificates(); @@ -2563,10 +2570,7 @@ private void subjectKeyIDTest( s = gen.generate(msg, true); - bIn = new ByteArrayInputStream(s.getEncoded()); - aIn = new ASN1InputStream(bIn); - - s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject())); + s = new CMSSignedData(s.getEncoded()); certStore = s.getCertificates(); @@ -2641,7 +2645,7 @@ private void encapsulatedTest( CMSSignedData s = gen.generate(msg, true); - s = new CMSSignedData(ContentInfo.getInstance(s.getEncoded())); + s = new CMSSignedData(s.getEncoded()); Set digestAlgorithms = new HashSet(s.getDigestAlgorithmIDs()); @@ -2720,7 +2724,7 @@ private void encapsulatedTest( s = gen.generate(msg, true); - s = new CMSSignedData(ContentInfo.getInstance(s.getEncoded())); + s = new CMSSignedData(s.getEncoded()); certStore = s.getCertificates(); crlStore = s.getCRLs(); @@ -2776,12 +2780,9 @@ private void detachedTest( gen.addCertificates(certs); - CMSSignedData s = gen.generate(msg, true); - - ByteArrayInputStream bIn = new ByteArrayInputStream(s.getEncoded()); - ASN1InputStream aIn = new ASN1InputStream(bIn); + CMSSignedData s = gen.generate(msg); - s = new CMSSignedData(msg, ContentInfo.getInstance(aIn.readObject())); + s = new CMSSignedData(msg, s.getEncoded()); Set digestAlgorithms = new HashSet(s.getDigestAlgorithmIDs()); @@ -2948,10 +2949,7 @@ public void testNullContentWithSigner() CMSSignedData s = gen.generate(new CMSAbsentContent(), false); - ByteArrayInputStream bIn = new ByteArrayInputStream(s.getEncoded()); - ASN1InputStream aIn = new ASN1InputStream(bIn); - - s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject())); + s = new CMSSignedData(s.getEncoded()); verifySignatures(s); } From 823cbd92af3c9537b79aee347c1b16714772e940 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 8 Apr 2025 09:35:43 +0930 Subject: [PATCH 304/890] Update OQS OIDs (falcon etc). --- .../asn1/bc/BCObjectIdentifiers.java | 223 +++++++++++++++++- 1 file changed, 217 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java index 6a48684661..942fc4ba87 100644 --- a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java @@ -215,7 +215,62 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier sphincsPlus_shake_192s = new ASN1ObjectIdentifier("1.3.9999.6.8.12"); ASN1ObjectIdentifier sphincsPlus_shake_256f = new ASN1ObjectIdentifier("1.3.9999.6.9.10"); ASN1ObjectIdentifier sphincsPlus_shake_256s = new ASN1ObjectIdentifier("1.3.9999.6.9.12"); - + /** 1.3.9999.6.4.13 OQS_OID_SPHINCSSHA2128FSIMPLE */ + ASN1ObjectIdentifier sphincs_sha2_128f_simple = new ASN1ObjectIdentifier("1.3.9999.6.4.13"); + /** 1.3.9999.6.4.14 OQS_OID_P256_SPHINCSSHA2128FSIMPLE */ + ASN1ObjectIdentifier p256_sphincs_sha2_128f_simple = new ASN1ObjectIdentifier("1.3.9999.6.4.14"); + /** 1.3.9999.6.4.15 OQS_OID_RSA3072_SPHINCSSHA2128FSIMPLE */ + ASN1ObjectIdentifier rsa_3072_sphincs_sha2_128f_simple = new ASN1ObjectIdentifier("1.3.9999.6.4.15"); + /** 1.3.9999.6.4.16 OQS_OID_SPHINCSSHA2128SSIMPLE */ + ASN1ObjectIdentifier sphincs_sha_2_128s_simple = new ASN1ObjectIdentifier("1.3.9999.6.4.16"); + /** 1.3.9999.6.4.17 OQS_OID_P256_SPHINCSSHA2128SSIMPLE */ + ASN1ObjectIdentifier p256_sphincs_sha2_128s_simple = new ASN1ObjectIdentifier("1.3.9999.6.4.17"); + /** 1.3.9999.6.4.18 OQS_OID_RSA3072_SPHINCSSHA2128SSIMPLE */ + ASN1ObjectIdentifier rsa_3072_sphincs_sha2_128s_simple = new ASN1ObjectIdentifier("1.3.9999.6.4.18"); + /** 1.3.9999.6.5.10 OQS_OID_SPHINCSSHA2192FSIMPLE */ + ASN1ObjectIdentifier sphincs_sha2_192f_simple = new ASN1ObjectIdentifier("1.3.9999.6.5.10"); + /** 1.3.9999.6.5.11 OQS_OID_P384_SPHINCSSHA2192FSIMPLE */ + ASN1ObjectIdentifier p384_sphincs_sha2_192f_simple = new ASN1ObjectIdentifier("1.3.9999.6.5.11"); + /** 1.3.9999.6.5.12 OQS_OID_SPHINCSSHA2192SSIMPLE */ + ASN1ObjectIdentifier sphincs_sha2_192s_simple = new ASN1ObjectIdentifier("1.3.9999.6.5.12"); + /** 1.3.9999.6.5.13 OQS_OID_P384_SPHINCSSHA2192SSIMPLE */ + ASN1ObjectIdentifier p384_sphincs_sha2192s_simple = new ASN1ObjectIdentifier("1.3.9999.6.5.13"); + /** 1.3.9999.6.6.10 OQS_OID_SPHINCSSHA2256FSIMPLE */ + ASN1ObjectIdentifier sphincs_sha2_256f_simple = new ASN1ObjectIdentifier("1.3.9999.6.6.10"); + /** 1.3.9999.6.6.11 OQS_OID_P521_SPHINCSSHA2256FSIMPLE */ + ASN1ObjectIdentifier p521_sphincs_sha2_256f_simple = new ASN1ObjectIdentifier("1.3.9999.6.6.11"); + /** 1.3.9999.6.6.12 OQS_OID_SPHINCSSHA2256SSIMPLE */ + ASN1ObjectIdentifier sphincs_sha2_256s_simple = new ASN1ObjectIdentifier("1.3.9999.6.6.12"); + /** 1.3.9999.6.6.13 OQS_OID_P521_SPHINCSSHA2256SSIMPLE */ + ASN1ObjectIdentifier p521_sphincs_sha2_256s_simple = new ASN1ObjectIdentifier("1.3.9999.6.6.13"); + /** 1.3.9999.6.7.13 OQS_OID_SPHINCSSHAKE128FSIMPLE */ + ASN1ObjectIdentifier sphincs_shake_128f_simple = new ASN1ObjectIdentifier("1.3.9999.6.7.13"); + /** 1.3.9999.6.7.14 OQS_OID_P256_SPHINCSSHAKE128FSIMPLE */ + ASN1ObjectIdentifier p256_sphincs_shake_128f_simple = new ASN1ObjectIdentifier("1.3.9999.6.7.14"); + /** 1.3.9999.6.7.15 OQS_OID_RSA3072_SPHINCSSHAKE128FSIMPLE */ + ASN1ObjectIdentifier rsa_3072_sphincs_shake_128f_simple = new ASN1ObjectIdentifier("1.3.9999.6.7.15"); + /** 1.3.9999.6.7.16 OQS_OID_SPHINCSSHAKE128SSIMPLE */ + ASN1ObjectIdentifier sphincs_shake_128s_simple = new ASN1ObjectIdentifier("1.3.9999.6.7.16"); + /** 1.3.9999.6.7.17 OQS_OID_P256_SPHINCSSHAKE128SSIMPLE */ + ASN1ObjectIdentifier p256_sphincs_shake_128s_simple = new ASN1ObjectIdentifier("1.3.9999.6.7.17"); + /** 1.3.9999.6.7.18 OQS_OID_RSA3072_SPHINCSSHAKE128SSIMPLE */ + ASN1ObjectIdentifier rsa_3072_sphincs_shake_128s_simple = new ASN1ObjectIdentifier("1.3.9999.6.7.18"); + /** 1.3.9999.6.8.10 OQS_OID_SPHINCSSHAKE192FSIMPLE */ + ASN1ObjectIdentifier sphincs_shake_192f_simple = new ASN1ObjectIdentifier("1.3.9999.6.8.10"); + /** 1.3.9999.6.8.11 OQS_OID_P384_SPHINCSSHAKE192FSIMPLE */ + ASN1ObjectIdentifier p384_sphincs_shake_192f_simple = new ASN1ObjectIdentifier("1.3.9999.6.8.11"); + /** 1.3.9999.6.8.12 OQS_OID_SPHINCSSHAKE192SSIMPLE */ + ASN1ObjectIdentifier sphincs_shake_192s_simple = new ASN1ObjectIdentifier("1.3.9999.6.8.12"); + /** 1.3.9999.6.8.13 OQS_OID_P384_SPHINCSSHAKE192SSIMPLE */ + ASN1ObjectIdentifier p384_sphincs_shake_192s_simple = new ASN1ObjectIdentifier("1.3.9999.6.8.13"); + /** 1.3.9999.6.9.10 OQS_OID_SPHINCSSHAKE256FSIMPLE */ + ASN1ObjectIdentifier sphincs_shake_256f_simple = new ASN1ObjectIdentifier("1.3.9999.6.9.10"); + /** 1.3.9999.6.9.11 OQS_OID_P521_SPHINCSSHAKE256FSIMPLE */ + ASN1ObjectIdentifier p521_sphincs_shake256f_simple = new ASN1ObjectIdentifier("1.3.9999.6.9.11"); + /** 1.3.9999.6.9.12 OQS_OID_SPHINCSSHAKE256SSIMPLE */ + ASN1ObjectIdentifier sphincs_shake_256s_simple = new ASN1ObjectIdentifier("1.3.9999.6.9.12"); + /** 1.3.9999.6.9.13 OQS_OID_P521_SPHINCSSHAKE256SSIMPLE */ + ASN1ObjectIdentifier p521_sphincs_shake256s_simple = new ASN1ObjectIdentifier("1.3.9999.6.9.13"); /** * Picnic */ @@ -246,10 +301,26 @@ public interface BCObjectIdentifiers * Falcon */ ASN1ObjectIdentifier falcon = bc_sig.branch("7"); - - ASN1ObjectIdentifier falcon_512 = new ASN1ObjectIdentifier("1.3.9999.3.6"); // falcon.branch("1"); - ASN1ObjectIdentifier falcon_1024 = new ASN1ObjectIdentifier("1.3.9999.3.9"); // falcon.branch("2"); - + /** 1.3.9999.3.11 OQS_OID_FALCON512 */ + ASN1ObjectIdentifier falcon_512 = new ASN1ObjectIdentifier("1.3.9999.3.11"); + /** 1.3.9999.3.12 OQS_OID_P256_FALCON512 */ + ASN1ObjectIdentifier p256_falcon_512 = new ASN1ObjectIdentifier("1.3.9999.3.12"); + /** 1.3.9999.3.13 OQS_OID_RSA3072_FALCON512 */ + ASN1ObjectIdentifier rsa_3072_falcon_512 = new ASN1ObjectIdentifier("1.3.9999.3.13"); + /** 1.3.9999.3.16 OQS_OID_FALCONPADDED512 */ + ASN1ObjectIdentifier falcon_padded_512 = new ASN1ObjectIdentifier("1.3.9999.3.16"); + /** 1.3.9999.3.17 OQS_OID_P256_FALCONPADDED512 */ + ASN1ObjectIdentifier p256_falcon_padded512 = new ASN1ObjectIdentifier("1.3.9999.3.17"); + /** 1.3.9999.3.18 OQS_OID_RSA3072_FALCONPADDED512 */ + ASN1ObjectIdentifier rsa_3072_falconpadded512 = new ASN1ObjectIdentifier("1.3.9999.3.18"); + /** 1.3.9999.3.14 OQS_OID_FALCON1024 */ + ASN1ObjectIdentifier falcon_1024 = new ASN1ObjectIdentifier("1.3.9999.3.14"); + /** 1.3.9999.3.15 OQS_OID_P521_FALCON1024 */ + ASN1ObjectIdentifier p521_falcon1024 = new ASN1ObjectIdentifier("1.3.9999.3.15"); + /** 1.3.9999.3.19 OQS_OID_FALCONPADDED1024 */ + ASN1ObjectIdentifier falcon_padded_1024 = new ASN1ObjectIdentifier("1.3.9999.3.19"); + /** 1.3.9999.3.20 OQS_OID_P521_FALCONPADDED1024 */ + ASN1ObjectIdentifier p521_falcon_padded_1024 = new ASN1ObjectIdentifier("1.3.9999.3.20"); /* * Dilithium */ @@ -262,7 +333,43 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier dilithium2_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.4.4"); // dilithium.branch("4"); ASN1ObjectIdentifier dilithium3_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.6.5"); // dilithium.branch("5"); ASN1ObjectIdentifier dilithium5_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.8.7"); // dilithium.branch("6"); - + /** 1.3.9999.7.5 OQS_OID_P256_MLDSA44 */ + ASN1ObjectIdentifier p256_mldsa44 = new ASN1ObjectIdentifier("1.3.9999.7.5"); + /** 1.3.9999.7.6 OQS_OID_RSA3072_MLDSA44 */ + ASN1ObjectIdentifier rsa3072_mldsa44 = new ASN1ObjectIdentifier("1.3.9999.7.6"); + /** 2.16.840.1.114027.80.8.1.1 OQS_OID_MLDSA44_pss2048 */ + ASN1ObjectIdentifier mldsa44_pss2048 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.1"); + /** 2.16.840.1.114027.80.8.1.2 OQS_OID_MLDSA44_rsa2048 */ + ASN1ObjectIdentifier mldsa44_rsa2048 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.2"); + /** 2.16.840.1.114027.80.8.1.3 OQS_OID_MLDSA44_ed25519 */ + ASN1ObjectIdentifier mldsa44_ed25519 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.3"); + /** 2.16.840.1.114027.80.8.1.4 OQS_OID_MLDSA44_p256 */ + ASN1ObjectIdentifier mldsa44_p256 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.4"); + /** 2.16.840.1.114027.80.8.1.5 OQS_OID_MLDSA44_bp256 */ + ASN1ObjectIdentifier mldsa44_bp256 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.5"); + ///** 2.16.840.1.101.3.4.3.18 OQS_OID_MLDSA65 */ + /** 1.3.9999.7.7 OQS_OID_P384_MLDSA65 */ + ASN1ObjectIdentifier p384_mldsa65 = new ASN1ObjectIdentifier("1.3.9999.7.7"); + /** 2.16.840.1.114027.80.8.1.6 OQS_OID_MLDSA65_pss3072 */ + ASN1ObjectIdentifier mldsa65_pss3072 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.6"); + /** 2.16.840.1.114027.80.8.1.7 OQS_OID_MLDSA65_rsa3072 */ + ASN1ObjectIdentifier mldsa65_rsa3072 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.7"); + /** 2.16.840.1.114027.80.8.1.8 OQS_OID_MLDSA65_p256 */ + ASN1ObjectIdentifier mldsa65_p256 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.8"); + /** 2.16.840.1.114027.80.8.1.9 OQS_OID_MLDSA65_bp256 */ + ASN1ObjectIdentifier mldsa65_bp256 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.9"); + /** 2.16.840.1.114027.80.8.1.10 OQS_OID_MLDSA65_ed25519 */ + ASN1ObjectIdentifier mldsa65_ed25519 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.10"); + ///** 2.16.840.1.101.3.4.3.19 OQS_OID_MLDSA87 */ + + /** 1.3.9999.7.8 OQS_OID_P521_MLDSA87 */ + ASN1ObjectIdentifier p521_mldsa87 = new ASN1ObjectIdentifier("1.3.9999.7.8"); + /** 2.16.840.1.114027.80.8.1.11 OQS_OID_MLDSA87_p384 */ + ASN1ObjectIdentifier mldsa87_p384 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.11"); + /** 2.16.840.1.114027.80.8.1.12 OQS_OID_MLDSA87_bp384 */ + ASN1ObjectIdentifier mldsa87_bp384 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.12"); + /** 2.16.840.1.114027.80.8.1.13 OQS_OID_MLDSA87_ed448 */ + ASN1ObjectIdentifier mldsa87_ed448 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.13"); /* * Rainbow */ @@ -438,4 +545,108 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier mayo2 = mayo.branch("2"); ASN1ObjectIdentifier mayo3 = mayo.branch("3"); ASN1ObjectIdentifier mayo5 = mayo.branch("4"); + /** 1.3.9999.8.1.3 OQS_OID_MAYO1 */ + ASN1ObjectIdentifier mayo_1 = new ASN1ObjectIdentifier("1.3.9999.8.1.3"); + /** 1.3.9999.8.1.4 OQS_OID_P256_MAYO1 */ + ASN1ObjectIdentifier p256_mayo1 = new ASN1ObjectIdentifier("1.3.9999.8.1.4"); + /** 1.3.9999.8.2.3 OQS_OID_MAYO2 */ + ASN1ObjectIdentifier mayo_2 = new ASN1ObjectIdentifier("1.3.9999.8.2.3"); + /** 1.3.9999.8.2.4 OQS_OID_P256_MAYO2 */ + ASN1ObjectIdentifier p256_mayo2 = new ASN1ObjectIdentifier("1.3.9999.8.2.4"); + /** 1.3.9999.8.3.3 OQS_OID_MAYO3 */ + ASN1ObjectIdentifier mayo_3 = new ASN1ObjectIdentifier("1.3.9999.8.3.3"); + /** 1.3.9999.8.3.4 OQS_OID_P384_MAYO3 */ + ASN1ObjectIdentifier p384_mayo3 = new ASN1ObjectIdentifier("1.3.9999.8.3.4"); + /** 1.3.9999.8.5.3 OQS_OID_MAYO5 */ + ASN1ObjectIdentifier mayo_5 = new ASN1ObjectIdentifier("1.3.9999.8.5.3"); + /** 1.3.9999.8.5.4 OQS_OID_P521_MAYO5 */ + ASN1ObjectIdentifier p521_mayo5 = new ASN1ObjectIdentifier("1.3.9999.8.5.4"); + + /** + * cross + */ +// /** 1.3.6.1.4.1.62245.2.1.1.2 OQS_OID_CROSSRSDP128BALANCED */ +// ASN1ObjectIdentifier crossrsdp_128balanced = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.1.2"); +// /** 1.3.6.1.4.1.62245.2.1.2.2 OQS_OID_CROSSRSDP128FAST */ +// ASN1ObjectIdentifier crossrsdp_128fast = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.2.2"); +// /** 1.3.6.1.4.1.62245.2.1.3.2 OQS_OID_CROSSRSDP128SMALL */ +// ASN1ObjectIdentifier crossrsdp_128small = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.3.2"); +// /** 1.3.6.1.4.1.62245.2.1.4.2 OQS_OID_CROSSRSDP192BALANCED */ +// ASN1ObjectIdentifier crossrsdp_192balanced = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.4.2"); +// /** 1.3.6.1.4.1.62245.2.1.5.2 OQS_OID_CROSSRSDP192FAST */ +// ASN1ObjectIdentifier crossrsdp_192fast = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.5.2"); +// /** 1.3.6.1.4.1.62245.2.1.6.2 OQS_OID_CROSSRSDP192SMALL */ +// ASN1ObjectIdentifier crossrsdp_192small = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.6.2"); +// /** 1.3.6.1.4.1.62245.2.1.9.2 OQS_OID_CROSSRSDP256SMALL */ +// ASN1ObjectIdentifier crossrsdp256small = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.9.2"); +// /** 1.3.6.1.4.1.62245.2.1.10.2 OQS_OID_CROSSRSDPG128BALANCED */ +// ASN1ObjectIdentifier crossrsdpg_128balanced = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.10.2"); +// /** 1.3.6.1.4.1.62245.2.1.11.2 OQS_OID_CROSSRSDPG128FAST */ +// ASN1ObjectIdentifier crossrsdpg_128fast = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.11.2"); +// /** 1.3.6.1.4.1.62245.2.1.12.2 OQS_OID_CROSSRSDPG128SMALL */ +// ASN1ObjectIdentifier crossrsdpg_128small = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.12.2"); +// /** 1.3.6.1.4.1.62245.2.1.13.2 OQS_OID_CROSSRSDPG192BALANCED */ +// ASN1ObjectIdentifier crossrsdpg_192balanced = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.13.2"); +// /** 1.3.6.1.4.1.62245.2.1.14.2 OQS_OID_CROSSRSDPG192FAST */ +// ASN1ObjectIdentifier crossrsdpg_192fast = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.14.2"); +// /** 1.3.6.1.4.1.62245.2.1.15.2 OQS_OID_CROSSRSDPG192SMALL */ +// ASN1ObjectIdentifier crossrsdpg_192small = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.15.2"); +// /** 1.3.6.1.4.1.62245.2.1.16.2 OQS_OID_CROSSRSDPG256BALANCED */ +// ASN1ObjectIdentifier crossrsdpg_256balanced = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.16.2"); +// /** 1.3.6.1.4.1.62245.2.1.17.2 OQS_OID_CROSSRSDPG256FAST */ +// ASN1ObjectIdentifier crossrsdpg_256fast = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.17.2"); +// /** 1.3.6.1.4.1.62245.2.1.18.2 OQS_OID_CROSSRSDPG256SMALL */ +// ASN1ObjectIdentifier crossrsdpg_256small = new ASN1ObjectIdentifier("1.3.6.1.4.1.62245.2.1.18.2"); + + /** + * OV + * */ +// /** 1.3.9999.9.1.1 OQS_OID_OV_IS */ +// ASN1ObjectIdentifier ov_is = new ASN1ObjectIdentifier("1.3.9999.9.1.1"); +// /** 1.3.9999.9.1.2 OQS_OID_P256_OV_IS */ +// ASN1ObjectIdentifier p256_ov_is = new ASN1ObjectIdentifier("1.3.9999.9.1.2"); +// /** 1.3.9999.9.2.1 OQS_OID_OV_IP */ +// ASN1ObjectIdentifier ov_ip = new ASN1ObjectIdentifier("1.3.9999.9.2.1"); +// /** 1.3.9999.9.2.2 OQS_OID_P256_OV_IP */ +// ASN1ObjectIdentifier p256_ov_ip = new ASN1ObjectIdentifier("1.3.9999.9.2.2"); +// /** 1.3.9999.9.3.1 OQS_OID_OV_III */ +// ASN1ObjectIdentifier ov_iii = new ASN1ObjectIdentifier("1.3.9999.9.3.1"); +// /** 1.3.9999.9.3.2 OQS_OID_P384_OV_III */ +// ASN1ObjectIdentifier p384_ov_iii = new ASN1ObjectIdentifier("1.3.9999.9.3.2"); +// /** 1.3.9999.9.4.1 OQS_OID_OV_V */ +// ASN1ObjectIdentifier ov_v = new ASN1ObjectIdentifier("1.3.9999.9.4.1"); +// /** 1.3.9999.9.4.2 OQS_OID_P521_OV_V */ +// ASN1ObjectIdentifier p521_ov_v = new ASN1ObjectIdentifier("1.3.9999.9.4.2"); +// /** 1.3.9999.9.5.1 OQS_OID_OV_IS_PKC */ +// ASN1ObjectIdentifier ov_is_pkc = new ASN1ObjectIdentifier("1.3.9999.9.5.1"); +// /** 1.3.9999.9.5.2 OQS_OID_P256_OV_IS_PKC */ +// ASN1ObjectIdentifier p256_ov_is_pkc = new ASN1ObjectIdentifier("1.3.9999.9.5.2"); +// /** 1.3.9999.9.6.1 OQS_OID_OV_IP_PKC */ +// ASN1ObjectIdentifier ov_ip_pkc = new ASN1ObjectIdentifier("1.3.9999.9.6.1"); +// /** 1.3.9999.9.6.2 OQS_OID_P256_OV_IP_PKC */ +// ASN1ObjectIdentifier p256_ov_ip_pkc = new ASN1ObjectIdentifier("1.3.9999.9.6.2"); +// /** 1.3.9999.9.7.1 OQS_OID_OV_III_PKC */ +// ASN1ObjectIdentifier ov_iii_pkc = new ASN1ObjectIdentifier("1.3.9999.9.7.1"); +// /** 1.3.9999.9.7.2 OQS_OID_P384_OV_III_PKC */ +// ASN1ObjectIdentifier p384_ov_iii_pkc = new ASN1ObjectIdentifier("1.3.9999.9.7.2"); +// /** 1.3.9999.9.8.1 OQS_OID_OV_V_PKC */ +// ASN1ObjectIdentifier ov_v_pkc = new ASN1ObjectIdentifier("1.3.9999.9.8.1"); +// /** 1.3.9999.9.8.2 OQS_OID_P521_OV_V_PKC */ +// ASN1ObjectIdentifier p521_ov_v_pkc = new ASN1ObjectIdentifier("1.3.9999.9.8.2"); +// /** 1.3.9999.9.9.1 OQS_OID_OV_IS_PKC_SKC */ +// ASN1ObjectIdentifier ov_is_pkc_skc = new ASN1ObjectIdentifier("1.3.9999.9.9.1"); +// /** 1.3.9999.9.9.2 OQS_OID_P256_OV_IS_PKC_SKC */ +// ASN1ObjectIdentifier p256_ov_is_pkc_skc = new ASN1ObjectIdentifier("1.3.9999.9.9.2"); +// /** 1.3.9999.9.10.1 OQS_OID_OV_IP_PKC_SKC */ +// ASN1ObjectIdentifier ov_ip_pkc_skc = new ASN1ObjectIdentifier("1.3.9999.9.10.1"); +// /** 1.3.9999.9.10.2 OQS_OID_P256_OV_IP_PKC_SKC */ +// ASN1ObjectIdentifier p256_ov_ip_pkc_skc = new ASN1ObjectIdentifier("1.3.9999.9.10.2"); +// /** 1.3.9999.9.11.1 OQS_OID_OV_III_PKC_SKC */ +// ASN1ObjectIdentifier ov_iii_pkc_skc = new ASN1ObjectIdentifier("1.3.9999.9.11.1"); +// /** 1.3.9999.9.11.2 OQS_OID_P384_OV_III_PKC_SKC */ +// ASN1ObjectIdentifier p384_ov_iii_pkc_skc = new ASN1ObjectIdentifier("1.3.9999.9.11.2"); +// /** 1.3.9999.9.12.1 OQS_OID_OV_V_PKC_SKC */ +// ASN1ObjectIdentifier ov_v_pkc_skc = new ASN1ObjectIdentifier("1.3.9999.9.12.1"); +// /** 1.3.9999.9.12.2 OQS_OID_P521_OV_V_PKC_SKC */ +// ASN1ObjectIdentifier p521_ov_v_pkc_skc = new ASN1ObjectIdentifier("1.3.9999.9.12.2"); } From 1e3cf00ec35578fa937f7a8f607edab8cdc841b2 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 8 Apr 2025 09:43:05 +0930 Subject: [PATCH 305/890] Remove duplicated SphincsPlust OIDs --- .../asn1/bc/BCObjectIdentifiers.java | 36 +++++++------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java index 942fc4ba87..fd9fe3868d 100644 --- a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java @@ -202,73 +202,61 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier sphincsPlus_interop = new ASN1ObjectIdentifier("1.3.9999.6"); + /** 1.3.9999.6.4.13 OQS_OID_SPHINCSSHA2128FSIMPLE */ ASN1ObjectIdentifier sphincsPlus_sha2_128f = new ASN1ObjectIdentifier("1.3.9999.6.4.13"); + /** 1.3.9999.6.4.16 OQS_OID_SPHINCSSHA2128SSIMPLE */ ASN1ObjectIdentifier sphincsPlus_sha2_128s = new ASN1ObjectIdentifier("1.3.9999.6.4.16"); + /** 1.3.9999.6.5.10 OQS_OID_SPHINCSSHA2192FSIMPLE */ ASN1ObjectIdentifier sphincsPlus_sha2_192f = new ASN1ObjectIdentifier("1.3.9999.6.5.10"); + /** 1.3.9999.6.5.12 OQS_OID_SPHINCSSHA2192SSIMPLE */ ASN1ObjectIdentifier sphincsPlus_sha2_192s = new ASN1ObjectIdentifier("1.3.9999.6.5.12"); + /** 1.3.9999.6.6.10 OQS_OID_SPHINCSSHA2256FSIMPLE */ ASN1ObjectIdentifier sphincsPlus_sha2_256f = new ASN1ObjectIdentifier("1.3.9999.6.6.10"); + /** 1.3.9999.6.6.12 OQS_OID_SPHINCSSHA2256SSIMPLE */ ASN1ObjectIdentifier sphincsPlus_sha2_256s = new ASN1ObjectIdentifier("1.3.9999.6.6.12"); + /** 1.3.9999.6.7.13 OQS_OID_SPHINCSSHAKE128FSIMPLE */ ASN1ObjectIdentifier sphincsPlus_shake_128f = new ASN1ObjectIdentifier("1.3.9999.6.7.13"); + /** 1.3.9999.6.7.16 OQS_OID_SPHINCSSHAKE128SSIMPLE */ ASN1ObjectIdentifier sphincsPlus_shake_128s = new ASN1ObjectIdentifier("1.3.9999.6.7.16"); + /** 1.3.9999.6.8.10 OQS_OID_SPHINCSSHAKE192FSIMPLE */ ASN1ObjectIdentifier sphincsPlus_shake_192f = new ASN1ObjectIdentifier("1.3.9999.6.8.10"); + /** 1.3.9999.6.8.12 OQS_OID_SPHINCSSHAKE192SSIMPLE */ ASN1ObjectIdentifier sphincsPlus_shake_192s = new ASN1ObjectIdentifier("1.3.9999.6.8.12"); + /** 1.3.9999.6.9.10 OQS_OID_SPHINCSSHAKE256FSIMPLE */ ASN1ObjectIdentifier sphincsPlus_shake_256f = new ASN1ObjectIdentifier("1.3.9999.6.9.10"); + /** 1.3.9999.6.9.12 OQS_OID_SPHINCSSHAKE256SSIMPLE */ ASN1ObjectIdentifier sphincsPlus_shake_256s = new ASN1ObjectIdentifier("1.3.9999.6.9.12"); - /** 1.3.9999.6.4.13 OQS_OID_SPHINCSSHA2128FSIMPLE */ - ASN1ObjectIdentifier sphincs_sha2_128f_simple = new ASN1ObjectIdentifier("1.3.9999.6.4.13"); /** 1.3.9999.6.4.14 OQS_OID_P256_SPHINCSSHA2128FSIMPLE */ ASN1ObjectIdentifier p256_sphincs_sha2_128f_simple = new ASN1ObjectIdentifier("1.3.9999.6.4.14"); /** 1.3.9999.6.4.15 OQS_OID_RSA3072_SPHINCSSHA2128FSIMPLE */ ASN1ObjectIdentifier rsa_3072_sphincs_sha2_128f_simple = new ASN1ObjectIdentifier("1.3.9999.6.4.15"); - /** 1.3.9999.6.4.16 OQS_OID_SPHINCSSHA2128SSIMPLE */ - ASN1ObjectIdentifier sphincs_sha_2_128s_simple = new ASN1ObjectIdentifier("1.3.9999.6.4.16"); /** 1.3.9999.6.4.17 OQS_OID_P256_SPHINCSSHA2128SSIMPLE */ ASN1ObjectIdentifier p256_sphincs_sha2_128s_simple = new ASN1ObjectIdentifier("1.3.9999.6.4.17"); /** 1.3.9999.6.4.18 OQS_OID_RSA3072_SPHINCSSHA2128SSIMPLE */ ASN1ObjectIdentifier rsa_3072_sphincs_sha2_128s_simple = new ASN1ObjectIdentifier("1.3.9999.6.4.18"); - /** 1.3.9999.6.5.10 OQS_OID_SPHINCSSHA2192FSIMPLE */ - ASN1ObjectIdentifier sphincs_sha2_192f_simple = new ASN1ObjectIdentifier("1.3.9999.6.5.10"); /** 1.3.9999.6.5.11 OQS_OID_P384_SPHINCSSHA2192FSIMPLE */ ASN1ObjectIdentifier p384_sphincs_sha2_192f_simple = new ASN1ObjectIdentifier("1.3.9999.6.5.11"); - /** 1.3.9999.6.5.12 OQS_OID_SPHINCSSHA2192SSIMPLE */ - ASN1ObjectIdentifier sphincs_sha2_192s_simple = new ASN1ObjectIdentifier("1.3.9999.6.5.12"); /** 1.3.9999.6.5.13 OQS_OID_P384_SPHINCSSHA2192SSIMPLE */ ASN1ObjectIdentifier p384_sphincs_sha2192s_simple = new ASN1ObjectIdentifier("1.3.9999.6.5.13"); - /** 1.3.9999.6.6.10 OQS_OID_SPHINCSSHA2256FSIMPLE */ - ASN1ObjectIdentifier sphincs_sha2_256f_simple = new ASN1ObjectIdentifier("1.3.9999.6.6.10"); /** 1.3.9999.6.6.11 OQS_OID_P521_SPHINCSSHA2256FSIMPLE */ ASN1ObjectIdentifier p521_sphincs_sha2_256f_simple = new ASN1ObjectIdentifier("1.3.9999.6.6.11"); - /** 1.3.9999.6.6.12 OQS_OID_SPHINCSSHA2256SSIMPLE */ - ASN1ObjectIdentifier sphincs_sha2_256s_simple = new ASN1ObjectIdentifier("1.3.9999.6.6.12"); /** 1.3.9999.6.6.13 OQS_OID_P521_SPHINCSSHA2256SSIMPLE */ ASN1ObjectIdentifier p521_sphincs_sha2_256s_simple = new ASN1ObjectIdentifier("1.3.9999.6.6.13"); - /** 1.3.9999.6.7.13 OQS_OID_SPHINCSSHAKE128FSIMPLE */ - ASN1ObjectIdentifier sphincs_shake_128f_simple = new ASN1ObjectIdentifier("1.3.9999.6.7.13"); /** 1.3.9999.6.7.14 OQS_OID_P256_SPHINCSSHAKE128FSIMPLE */ ASN1ObjectIdentifier p256_sphincs_shake_128f_simple = new ASN1ObjectIdentifier("1.3.9999.6.7.14"); /** 1.3.9999.6.7.15 OQS_OID_RSA3072_SPHINCSSHAKE128FSIMPLE */ ASN1ObjectIdentifier rsa_3072_sphincs_shake_128f_simple = new ASN1ObjectIdentifier("1.3.9999.6.7.15"); - /** 1.3.9999.6.7.16 OQS_OID_SPHINCSSHAKE128SSIMPLE */ - ASN1ObjectIdentifier sphincs_shake_128s_simple = new ASN1ObjectIdentifier("1.3.9999.6.7.16"); /** 1.3.9999.6.7.17 OQS_OID_P256_SPHINCSSHAKE128SSIMPLE */ ASN1ObjectIdentifier p256_sphincs_shake_128s_simple = new ASN1ObjectIdentifier("1.3.9999.6.7.17"); /** 1.3.9999.6.7.18 OQS_OID_RSA3072_SPHINCSSHAKE128SSIMPLE */ ASN1ObjectIdentifier rsa_3072_sphincs_shake_128s_simple = new ASN1ObjectIdentifier("1.3.9999.6.7.18"); - /** 1.3.9999.6.8.10 OQS_OID_SPHINCSSHAKE192FSIMPLE */ - ASN1ObjectIdentifier sphincs_shake_192f_simple = new ASN1ObjectIdentifier("1.3.9999.6.8.10"); /** 1.3.9999.6.8.11 OQS_OID_P384_SPHINCSSHAKE192FSIMPLE */ ASN1ObjectIdentifier p384_sphincs_shake_192f_simple = new ASN1ObjectIdentifier("1.3.9999.6.8.11"); - /** 1.3.9999.6.8.12 OQS_OID_SPHINCSSHAKE192SSIMPLE */ - ASN1ObjectIdentifier sphincs_shake_192s_simple = new ASN1ObjectIdentifier("1.3.9999.6.8.12"); /** 1.3.9999.6.8.13 OQS_OID_P384_SPHINCSSHAKE192SSIMPLE */ ASN1ObjectIdentifier p384_sphincs_shake_192s_simple = new ASN1ObjectIdentifier("1.3.9999.6.8.13"); - /** 1.3.9999.6.9.10 OQS_OID_SPHINCSSHAKE256FSIMPLE */ - ASN1ObjectIdentifier sphincs_shake_256f_simple = new ASN1ObjectIdentifier("1.3.9999.6.9.10"); /** 1.3.9999.6.9.11 OQS_OID_P521_SPHINCSSHAKE256FSIMPLE */ ASN1ObjectIdentifier p521_sphincs_shake256f_simple = new ASN1ObjectIdentifier("1.3.9999.6.9.11"); - /** 1.3.9999.6.9.12 OQS_OID_SPHINCSSHAKE256SSIMPLE */ - ASN1ObjectIdentifier sphincs_shake_256s_simple = new ASN1ObjectIdentifier("1.3.9999.6.9.12"); /** 1.3.9999.6.9.13 OQS_OID_P521_SPHINCSSHAKE256SSIMPLE */ ASN1ObjectIdentifier p521_sphincs_shake256s_simple = new ASN1ObjectIdentifier("1.3.9999.6.9.13"); /** From 4200648c942d16fbd228b79c903fd4cc29fe688d Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 8 Apr 2025 09:45:45 +0930 Subject: [PATCH 306/890] Add the test of OQS OIDs --- .../pqc/jcajce/provider/test/FalconTest.java | 85 +++++++++++++++++-- 1 file changed, 78 insertions(+), 7 deletions(-) diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/FalconTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/FalconTest.java index 5908b82825..0a37418a78 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/FalconTest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/FalconTest.java @@ -2,6 +2,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; +import java.io.InputStreamReader; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.security.InvalidAlgorithmParameterException; @@ -9,6 +10,8 @@ import java.security.KeyFactory; import java.security.KeyPair; import java.security.KeyPairGenerator; +import java.security.Provider; +import java.security.PublicKey; import java.security.SecureRandom; import java.security.Security; import java.security.Signature; @@ -23,10 +26,13 @@ import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.pqc.jcajce.interfaces.FalconKey; import org.bouncycastle.pqc.jcajce.interfaces.FalconPrivateKey; +import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider; import org.bouncycastle.pqc.jcajce.spec.FalconParameterSpec; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.io.pem.PemObject; +import org.bouncycastle.util.io.pem.PemReader; public class FalconTest extends TestCase @@ -41,8 +47,72 @@ public void setUp() } } + public void testOQSPublicKeyExample() + throws Exception + { + try + { + // Register BouncyCastle PQC provider + Security.addProvider(new BouncyCastlePQCProvider()); + + for (Provider provider : Security.getProviders()) + { + if ("BCPQC".equals(provider.getName())) + { + System.out.println("Provider: " + provider.getName() + " " + provider.getVersion()); + } + } + + // Read public key + byte[] data = ("-----BEGIN PUBLIC KEY-----\n" + + "MIIDjzAHBgUrzg8DCwOCA4IACQTGjlTc0dE3Gt3rHNvjaQ1VdPgRBbdS3LK+50W7\n" + + "FEESAMeCdwFMSFiBhjlVDvCHnodDXnzLRacPjekdlvWew6pAYSITnjmk6hXQQCOJ\n" + + "NSnppbCbCC+R1XEjBrCPvFnTNm5maSjzUoMT75iZ9sNu0SALQeFGa0w9vvaK4g7S\n" + + "UiU02OYjZdUdTVG7DCK8RxaGRVCmCH5G60YmJNuYcExZtV01L6jYuDprjjd05aoT\n" + + "2Kp0pjp1Ms2gj6AsubmcG7c0MFEN07pCvEyROYWqMgST6FseW6l5y1XVHrA8tsDH\n" + + "ga0xum3zcRaNIEXhXgKaXeEusTQRAxqVvfI5dghVaiRp1eIWgTgRwaya7IuGkFXK\n" + + "CXWDPy9myAGUn5pEvL2WM5uZtrJC5daKQreojXT96rmg+MoKlhnkaYbOcgWBGVAJ\n" + + "+akOqdwBoK6UU58cpVR+yGnrqE6cAwZdgthQyAWYBaxSI86ydoIia0tacomTrLw7\n" + + "ujD1CC2YuzJjRQOpjyPd+7SCpSEzacao4CNvso3H5+jkJIW15hjbn1sVupYyk+cJ\n" + + "bnk91o5NuqbYTQbKnohShayHfZh6NiJAmm0QB2WSGgQVq5J1BKjGHcSOp9hOSMLM\n" + + "TGr6PvIlQRVy8XQjA1Tdj7CFt9iNX5eYSSlWIgVuIkLrKClvMBmnS+zErVnR/OBO\n" + + "xokvoKFWHKqpTn5NCw1bxIBO5XTZ4UWplh37h29WYgKwt6QljS6+6xCXTL2m1tZh\n" + + "q8grMflqsctTmlQl14TWVqllkAATrUQned44pmahupl1TETfHVp9cMbCfBYHWbTe\n" + + "IRVkpYbgCCjMJKSDjocEqONhQkktxC0lxlQIVKlWTJ83tFMHW9Z7tdlJyHefF6dh\n" + + "R4Ozi6eEyGE49ZkAC3JmR2EKDXUYUFNfIoKX5LIBwBLCN/B6pKMSpBENe0jhbtlL\n" + + "XSHIpy0yl8rKsudMFjxJapSzyxs4rbFTe0SFMDjgwz9MJHROcpgHLEmOGBlFr8bP\n" + + "uJm+A/yWn3sh+vS1A+NsBH4R8gedRK7YSdoJBnUkp/LUyPJK+oGAwoJZXwiFyQxN\n" + + "r0R8x8Iqy3Y4oXWGLcYnQUEpAPjFQbCEONrlpUanxPSoQ9sMLKinxemTWD35rsXN\n" + + "iW09ZO6hYaiVggY8ETeZHXnN8eCcG7s88GejwisLGVY1I4cdkHLuNxmEn9yr4adL\n" + + "Huc6\n" + + "-----END PUBLIC KEY-----").getBytes(); + + PemReader reader = new PemReader(new InputStreamReader(new ByteArrayInputStream(data))); + PemObject pemObject = reader.readPemObject(); + reader.close(); + + byte[] keyBytes = pemObject.getContent(); + + // Load the public key + KeyFactory keyFactory = KeyFactory.getInstance("Falcon", "BCPQC"); + PublicKey publicKey = keyFactory.generatePublic(new X509EncodedKeySpec(keyBytes)); + + // Print information + System.out.println("Public Key Algorithm : " + publicKey.getAlgorithm()); + System.out.println("Public Key Format : " + publicKey.getFormat()); + System.out.println("Encoded Key Length : " + publicKey.getEncoded().length + " bytes"); + System.out.println("Encoded Key (Base64) : " + java.util.Base64.getEncoder().encodeToString(publicKey.getEncoded())); + + } + catch (Exception e) + { + System.err.println("Failed to read Falcon-512 public key."); + e.printStackTrace(); + } + } + public void testPrivateKeyRecovery() - throws Exception + throws Exception { KeyPairGenerator kpg = KeyPairGenerator.getInstance("Falcon", "BC"); @@ -80,7 +150,7 @@ public void testPrivateKeyRecovery() } public void testPublicKeyRecovery() - throws Exception + throws Exception { KeyPairGenerator kpg = KeyPairGenerator.getInstance("Falcon", "BC"); @@ -233,7 +303,7 @@ private void doTestRestrictedKeyPairGen(FalconParameterSpec spec, FalconParamete } public void testFalconRandomSig() - throws Exception + throws Exception { KeyPairGenerator kpg = KeyPairGenerator.getInstance("Falcon", "BC"); @@ -266,10 +336,11 @@ public void testFalconRandomSig() * pk = 096BA86CB658A8F445C9A5E4C28374BEC879C8655F68526923240918074D0147C03162E4A49200648C652803C6FD7509AE9AA799D6310D0BD42724E0635920186207000767CA5A8546B1755308C304B84FC93B069E265985B398D6B834698287FF829AA820F17A7F4226AB21F601EBD7175226BAB256D8888F009032566D6383D68457EA155A94301870D589C678ED304259E9D37B193BC2A7CCBCBEC51D69158C44073AEC9792630253318BC954DBF50D15028290DC2D309C7B7B02A6823744D463DA17749595CB77E6D16D20D1B4C3AAD89D320EBE5A672BB96D6CD5C1EFEC8B811200CBB062E473352540EDDEF8AF9499F8CDD1DC7C6873F0C7A6BCB7097560271F946849B7F373640BB69CA9B518AA380A6EB0A7275EE84E9C221AED88F5BFBAF43A3EDE8E6AA42558104FAF800E018441930376C6F6E751569971F47ADBCA5CA00C801988F317A18722A29298925EA154DBC9024E120524A2D41DC0F18FD8D909F6C50977404E201767078BA9A1F9E40A8B2BA9C01B7DA3A0B73A4C2A6B4F518BBEE3455D0AF2204DDC031C805C72CCB647940B1E6794D859AAEBCEA0DEB581D61B9248BD9697B5CB974A8176E8F910469CAE0AB4ED92D2AEE9F7EB50296DAF8057476305C1189D1D9840A0944F0447FB81E511420E67891B98FA6C257034D5A063437D379177CE8D3FA6EAF12E2DBB7EB8E498481612B1929617DA5FB45E4CDF893927D8BA842AA861D9C50471C6D0C6DF7E2BB26465A0EB6A3A709DE792AAFAAF922AA95DD5920B72B4B8856C6E632860B10F5CC08450003671AF388961872B466400ADB815BA81EA794945D19A100622A6CA0D41C4EA620C21DC125119E372418F04402D9FA7180F7BC89AFA54F8082244A42F46E5B5ABCE87B50A7D6FEBE8D7BBBAC92657CBDA1DB7C25572A4C1D0BAEA30447A865A2B1036B880037E2F4D26D453E9E913259779E9169B28A62EB809A5C744E04E260E1F2BBDA874F1AC674839DDB47B3148C5946DE0180148B7973D63C58193B17CD05D16E80CD7928C2A338363A23A81C0608C87505589B9DA1C617E7B70786B6754FBB30A5816810B9E126CFCC5AA49326E9D842973874B6359B5DB75610BA68A98C7B5E83F125A82522E13B83FB8F864E2A97B73B5D544A7415B6504A13939EAB1595D64FAF41FAB25A864A574DE524405E878339877886D2FC07FA0311508252413EDFA1158466667AFF78386DAF7CB4C9B850992F96E20525330599AB601D454688E294C8C3E * sk = 59044102F3CFBE1BE03C144102F7EF75FBEF83043F7CFC20C20BEEC007DE3F041FBF0BFF401041030C40040FAE7E103F7E100085FC013D1410C80C2F000810461C2F480BEE8017D17F07F1411BA24013C1BDF83DC407D17E07C13917F0F9044045FC40BD0FF07D07EF0003DFC1F3CFFD1FC03FEFC0B8FC6E7B0BBDBD0FE0BE17D14307EFFE0FBFC6F81FBFF43EC1F87041D42083EC3DC2F4407BF84EC4140FC403F037F3FEC013E0FEE02180082F83FBE07BFFE043F40EC6FFB1BF200007FFBFFA0FFF6FFBCE83EBFEBEFC0FFDF3F103FC6F3FF0500A18718308007D03F200E4213BF04FFD17D000F0017A17F180E04FFF07DEC2244048148E8704503EE06F86080243F81FFF03BF4003F07EF3DE02FBFFC00420C1F40FBDF0707E043FF5FFD0000430400C4F49F4207C142F80EC3E010BFF7C13F07FF85F7F17E07C17FF33FC4EC303FFBCFFEEC41830FF0831BDF45F05F06FC503B0C0F84E4013E100E7E1441450C2FBEEBC0C0FBEFC60BCFFEF3CFBDF4303EF800BF2BE0BF001F01F43F41FFE08517B001141E00144F7EF8007CEBDFFFF4213A0B9F8A0FE04103C17E0820BB1C30C30C00FFFFC00007D18017CFF90C3101E7E103040FC4FBE04213E07AF80FFEFC80FBFBD0810BCFB8FBC087FB8FFF1010C2E81002F3EF3BF01F07E41FBC07F2C0FB8F43F401C5D81FFCEBE07C07E0BF17EEBEE830C514003FF7EF3E08403D1FFFFE105F840C20BDF0607FFFEF46E7EFFF08000400DE830000F3F82EF9D82E84EFFF3CEC4E81E01002103102EFC080F3B0801041BAE42F7F040F83EC31010031BC0410FAFF9F0004010133A089FFEF7BE8317A0020FEF010052BA04107E100F821C2F41F44F4EF7B000F02E41F82F380830FE08A1F707FF82EC7F42E81004041103E8307B13D0FDFF8F830F9FC5FFCD7E040F410FFFB9F423750860C11C5FFA144EC0080F02DC0F420820450790020BCF80EFFFBCEC4FBFF4200AFC00C02060C004303EF81FFA104107E4117AF01F81202FC1E44143FFE206EB3E881BB13F13920403FF7A000144102E7FFC2143E7FF4AF3F13F07E181DC317E240F4500303F2DDDCF1E1513E3EF15E8DC1309E50AEE03EFDC17081706FD03E6ECE4F30EBD1909051906E90CE806EB0B19E719EFFBF10D0DF1DC0CF6F1F4F8FEFBE9F9550E2107FCDCCBDFE9F4F7EE1AF8142115F910002AF2F5FF141ADA220AECFE040CEF0B29EB201930F2D3E401E5DEEFF4DDEA17F1FE141217F81C36050109F8F61F02DD19F90310C7F40208E9052C3942F8FFF2CCF9FDF83CFA12DC091C0D02F00411F5281E40D7F92DBA11D73D04C10BFD13E617110AF3ED05F6CFE705E0F70E1FF80533FC120C002CE81FF52638190FE3FED6F0FBBB23E6F408EF32220B13DD27F007E5FA00D72614F0E302210707EC111E070E2A032DF91DE3FCE800F1F9F2F7FE170101180412CBD1E90019F2011522DAEAED13F8E5F425DCEF24E01CE614E7DCEC01F2F4F914F4010107ED26E2E9DF0BF5F007EA07FAFBC6D7E607FAFCFD270DFD0D17FC4EF0EE00071AECDE09F8F215E113F80209CCF308D7E6251ECE0EDFED0CC9F4050B2714F61BF703F0EBF104010DEBFBF21AFC1BF01823FEDEFAF7F807E3F3020AEB01FE19EEE8E90D00E5FAED1EFDF628E5F0E6F0FC13F4FB05FB0B09EA0A0E08EE13293212E90CE4FEF223F4FF030BEBED1B402ED2F6171102BC0CF9E9F335ED0C01FAF0FEFAE41DF0050A162C11171CD90BEE211218EDFAFA0F03F4171412F319D60B01FAEE1F2823F0D6EF12D6DFEAFBFC170DECDA06E7CED500031E * sm = 026833B3C07507E4201748494D832B6EE2A6C93BFF9B0EE343B550D1F85A3D0DE0D704C6D17842951309D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8290765843D1E460D17A527D2BCA405BD55BBC7DA09A8C620BE0AF4A767D9DB96B80F55E466676751EAABA7B93B86D71132DAA0EB376782B9EEE37519CE10FDD33FE9F29312C31D8736206D165CF4C528AA3DDC017845E1F0DD5B0A44FF961C42D874A95533E5B438982F524CA954D87533BFBE42C63FF2ABC77A34C79DB55A99171BBCB72C842A6530AF2F753F0C34AC632F9F1E7949F0BF6C67665B27722A8857D626B6FF1A136D923A39F4069B7477FF946E5247A6627791D49B59EDC9E2525A860E6E9828D18F64A9F17222E8166A02453859BBDA0B8186D8C9928BB571E4146401D7430E225904673AD21CCAC54C146C248A1DD69AB6491E901D6D71B152155BE97DE057F3916A3F1B4273308C29B2F4D9697167B90681B1583ED930A71E990467DEA368134BECEEBD597F9BEC922E816F1B0570D728F4AE0464C1F797657F87A4E52DCDCAEB9272662EA66D7C6CD8781B31AF555AD93F5F65E75816CB8DC306BB67E592B5261BACA7C509629EA2AF8ABB80CBA89EE535B76DFD9CCBBE3BF48F2BC8AA34B26E1103291053F5CB8DE3A45AFA5A76DF8B2122ED2C82FBCF2259290D41A14F86B12F35F5D49762B34CFF13EE7E42EDEC70201D7F37C33316288FA3078E36E58108865C3CFE263D563692043DECC62F3426F86061285B7B1B336F56FF41BB65E9CD6D9B92FD90F864AA1C923CB8C755F5CDE1770D862595427149D7721AAAB5D194AEA9ACDECA15BE43CBA6A62B5A33909E9FC4DA1C5814FBD7CD6A2FA572E318B42C6C319140B86E66392580A11A2B431F44C1F9270E4F7B2490F3B325A9977A71A575915636635B9969DBD6D220B24C3D99CEBBBD834B88222BD08C3ABE124E80 + * * @throws Exception */ public void testFalconKATSig() - throws Exception + throws Exception { byte[] pubK = Hex.decode("096BA86CB658A8F445C9A5E4C28374BEC879C8655F68526923240918074D0147C03162E4A49200648C652803C6FD7509AE9AA799D6310D0BD42724E0635920186207000767CA5A8546B1755308C304B84FC93B069E265985B398D6B834698287FF829AA820F17A7F4226AB21F601EBD7175226BAB256D8888F009032566D6383D68457EA155A94301870D589C678ED304259E9D37B193BC2A7CCBCBEC51D69158C44073AEC9792630253318BC954DBF50D15028290DC2D309C7B7B02A6823744D463DA17749595CB77E6D16D20D1B4C3AAD89D320EBE5A672BB96D6CD5C1EFEC8B811200CBB062E473352540EDDEF8AF9499F8CDD1DC7C6873F0C7A6BCB7097560271F946849B7F373640BB69CA9B518AA380A6EB0A7275EE84E9C221AED88F5BFBAF43A3EDE8E6AA42558104FAF800E018441930376C6F6E751569971F47ADBCA5CA00C801988F317A18722A29298925EA154DBC9024E120524A2D41DC0F18FD8D909F6C50977404E201767078BA9A1F9E40A8B2BA9C01B7DA3A0B73A4C2A6B4F518BBEE3455D0AF2204DDC031C805C72CCB647940B1E6794D859AAEBCEA0DEB581D61B9248BD9697B5CB974A8176E8F910469CAE0AB4ED92D2AEE9F7EB50296DAF8057476305C1189D1D9840A0944F0447FB81E511420E67891B98FA6C257034D5A063437D379177CE8D3FA6EAF12E2DBB7EB8E498481612B1929617DA5FB45E4CDF893927D8BA842AA861D9C50471C6D0C6DF7E2BB26465A0EB6A3A709DE792AAFAAF922AA95DD5920B72B4B8856C6E632860B10F5CC08450003671AF388961872B466400ADB815BA81EA794945D19A100622A6CA0D41C4EA620C21DC125119E372418F04402D9FA7180F7BC89AFA54F8082244A42F46E5B5ABCE87B50A7D6FEBE8D7BBBAC92657CBDA1DB7C25572A4C1D0BAEA30447A865A2B1036B880037E2F4D26D453E9E913259779E9169B28A62EB809A5C744E04E260E1F2BBDA874F1AC674839DDB47B3148C5946DE0180148B7973D63C58193B17CD05D16E80CD7928C2A338363A23A81C0608C87505589B9DA1C617E7B70786B6754FBB30A5816810B9E126CFCC5AA49326E9D842973874B6359B5DB75610BA68A98C7B5E83F125A82522E13B83FB8F864E2A97B73B5D544A7415B6504A13939EAB1595D64FAF41FAB25A864A574DE524405E878339877886D2FC07FA0311508252413EDFA1158466667AFF78386DAF7CB4C9B850992F96E20525330599AB601D454688E294C8C3E"); byte[] privK = Hex.decode("59044102F3CFBE1BE03C144102F7EF75FBEF83043F7CFC20C20BEEC007DE3F041FBF0BFF401041030C40040FAE7E103F7E100085FC013D1410C80C2F000810461C2F480BEE8017D17F07F1411BA24013C1BDF83DC407D17E07C13917F0F9044045FC40BD0FF07D07EF0003DFC1F3CFFD1FC03FEFC0B8FC6E7B0BBDBD0FE0BE17D14307EFFE0FBFC6F81FBFF43EC1F87041D42083EC3DC2F4407BF84EC4140FC403F037F3FEC013E0FEE02180082F83FBE07BFFE043F40EC6FFB1BF200007FFBFFA0FFF6FFBCE83EBFEBEFC0FFDF3F103FC6F3FF0500A18718308007D03F200E4213BF04FFD17D000F0017A17F180E04FFF07DEC2244048148E8704503EE06F86080243F81FFF03BF4003F07EF3DE02FBFFC00420C1F40FBDF0707E043FF5FFD0000430400C4F49F4207C142F80EC3E010BFF7C13F07FF85F7F17E07C17FF33FC4EC303FFBCFFEEC41830FF0831BDF45F05F06FC503B0C0F84E4013E100E7E1441450C2FBEEBC0C0FBEFC60BCFFEF3CFBDF4303EF800BF2BE0BF001F01F43F41FFE08517B001141E00144F7EF8007CEBDFFFF4213A0B9F8A0FE04103C17E0820BB1C30C30C00FFFFC00007D18017CFF90C3101E7E103040FC4FBE04213E07AF80FFEFC80FBFBD0810BCFB8FBC087FB8FFF1010C2E81002F3EF3BF01F07E41FBC07F2C0FB8F43F401C5D81FFCEBE07C07E0BF17EEBEE830C514003FF7EF3E08403D1FFFFE105F840C20BDF0607FFFEF46E7EFFF08000400DE830000F3F82EF9D82E84EFFF3CEC4E81E01002103102EFC080F3B0801041BAE42F7F040F83EC31010031BC0410FAFF9F0004010133A089FFEF7BE8317A0020FEF010052BA04107E100F821C2F41F44F4EF7B000F02E41F82F380830FE08A1F707FF82EC7F42E81004041103E8307B13D0FDFF8F830F9FC5FFCD7E040F410FFFB9F423750860C11C5FFA144EC0080F02DC0F420820450790020BCF80EFFFBCEC4FBFF4200AFC00C02060C004303EF81FFA104107E4117AF01F81202FC1E44143FFE206EB3E881BB13F13920403FF7A000144102E7FFC2143E7FF4AF3F13F07E181DC317E240F4500303F2DDDCF1E1513E3EF15E8DC1309E50AEE03EFDC17081706FD03E6ECE4F30EBD1909051906E90CE806EB0B19E719EFFBF10D0DF1DC0CF6F1F4F8FEFBE9F9550E2107FCDCCBDFE9F4F7EE1AF8142115F910002AF2F5FF141ADA220AECFE040CEF0B29EB201930F2D3E401E5DEEFF4DDEA17F1FE141217F81C36050109F8F61F02DD19F90310C7F40208E9052C3942F8FFF2CCF9FDF83CFA12DC091C0D02F00411F5281E40D7F92DBA11D73D04C10BFD13E617110AF3ED05F6CFE705E0F70E1FF80533FC120C002CE81FF52638190FE3FED6F0FBBB23E6F408EF32220B13DD27F007E5FA00D72614F0E302210707EC111E070E2A032DF91DE3FCE800F1F9F2F7FE170101180412CBD1E90019F2011522DAEAED13F8E5F425DCEF24E01CE614E7DCEC01F2F4F914F4010107ED26E2E9DF0BF5F007EA07FAFBC6D7E607FAFCFD270DFD0D17FC4EF0EE00071AECDE09F8F215E113F80209CCF308D7E6251ECE0EDFED0CC9F4050B2714F61BF703F0EBF104010DEBFBF21AFC1BF01823FEDEFAF7F807E3F3020AEB01FE19EEE8E90D00E5FAED1EFDF628E5F0E6F0FC13F4FB05FB0B09EA0A0E08EE13293212E90CE4FEF223F4FF030BEBED1B402ED2F6171102BC0CF9E9F335ED0C01FAF0FEFAE41DF0050A162C11171CD90BEE211218EDFAFA0F03F4171412F319D60B01FAEE1F2823F0D6EF12D6DFEAFBFC170DECDA06E7CED500031E"); @@ -292,9 +363,9 @@ public void testFalconKATSig() PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp.getPrivate().getEncoded()); ASN1Sequence privSeq = ASN1Sequence.getInstance(privInfo.parsePrivateKey()); - + byte[] privCat = Arrays.concatenate( - new byte[] { 0x59 }, + new byte[]{0x59}, ASN1OctetString.getInstance(privSeq.getObjectAt(1)).getOctets(), ASN1OctetString.getInstance(privSeq.getObjectAt(2)).getOctets(), ASN1OctetString.getInstance(privSeq.getObjectAt(3)).getOctets()); @@ -321,7 +392,7 @@ public void testFalconKATSig() } private static class RiggedRandom - extends SecureRandom + extends SecureRandom { public void nextBytes(byte[] bytes) { From 09b0db73c6ec19ad364a2f002f78ec69b0d3f969 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 8 Apr 2025 09:54:30 +0930 Subject: [PATCH 307/890] Refactor of Falcon code --- .../crypto/falcon/ComplexNumberWrapper.java | 13 - .../pqc/crypto/falcon/FPREngine.java | 2375 ++++++++--------- .../pqc/crypto/falcon/FalconCodec.java | 301 +-- .../pqc/crypto/falcon/FalconCommon.java | 467 ++-- .../pqc/crypto/falcon/FalconConversions.java | 77 - .../pqc/crypto/falcon/FalconFFT.java | 682 +++-- .../pqc/crypto/falcon/FalconFPR.java | 11 - .../pqc/crypto/falcon/FalconKeyGen.java | 805 +++--- .../crypto/falcon/FalconKeyPairGenerator.java | 15 +- .../pqc/crypto/falcon/FalconNIST.java | 220 +- .../falcon/FalconPublicKeyParameters.java | 2 +- .../pqc/crypto/falcon/FalconRNG.java | 162 +- .../pqc/crypto/falcon/FalconSign.java | 1424 +++++----- .../pqc/crypto/falcon/FalconSigner.java | 6 +- .../pqc/crypto/falcon/FalconVrfy.java | 312 +-- .../pqc/crypto/falcon/SHAKE256.java | 562 ---- .../pqc/crypto/falcon/SamplerCtx.java | 4 +- .../pqc/crypto/falcon/SamplerZ.java | 47 +- 18 files changed, 3364 insertions(+), 4121 deletions(-) delete mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/falcon/ComplexNumberWrapper.java delete mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconConversions.java delete mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconFPR.java delete mode 100644 core/src/main/java/org/bouncycastle/pqc/crypto/falcon/SHAKE256.java diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/ComplexNumberWrapper.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/ComplexNumberWrapper.java deleted file mode 100644 index f3a9345381..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/ComplexNumberWrapper.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.bouncycastle.pqc.crypto.falcon; - -class ComplexNumberWrapper -{ - FalconFPR re; - FalconFPR im; - - ComplexNumberWrapper(FalconFPR re, FalconFPR im) - { - this.re = re; - this.im = im; - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FPREngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FPREngine.java index f90b25f456..37906426d1 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FPREngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FPREngine.java @@ -2,1144 +2,1136 @@ class FPREngine { - private static final FalconFPR[] inv_sigma; - private static final FalconFPR[] sigma_min; - private static final FalconFPR[] gm_tab; - private static final FalconFPR[] p2_tab; +// static final double[] inv_sigma; +// static final double[] sigma_min; +// static final double[] gm_tab; +// static final double[] p2_tab; static { - inv_sigma = new FalconFPR[]{ - new FalconFPR(0.0), /* unused */ - new FalconFPR(0.0069054793295940891952143765991630516), - new FalconFPR(0.0068102267767177975961393730687908629), - new FalconFPR(0.0067188101910722710707826117910434131), - new FalconFPR(0.0065883354370073665545865037227681924), - new FalconFPR(0.0064651781207602900738053897763485516), - new FalconFPR(0.0063486788828078995327741182928037856), - new FalconFPR(0.0062382586529084374473367528433697537), - new FalconFPR(0.0061334065020930261548984001431770281), - new FalconFPR(0.0060336696681577241031668062510953022), - new FalconFPR(0.0059386453095331159950250124336477482) + fpr_inv_sigma = new double[]{ + 0.0, /* unused */ + 0.0069054793295940891952143765991630516, + 0.0068102267767177975961393730687908629, + 0.0067188101910722710707826117910434131, + 0.0065883354370073665545865037227681924, + 0.0064651781207602900738053897763485516, + 0.0063486788828078995327741182928037856, + 0.0062382586529084374473367528433697537, + 0.0061334065020930261548984001431770281, + 0.0060336696681577241031668062510953022, + 0.0059386453095331159950250124336477482 }; - sigma_min = new FalconFPR[]{ - new FalconFPR(0.0), /* unused */ - new FalconFPR(1.1165085072329102588881898380334015), - new FalconFPR(1.1321247692325272405718031785357108), - new FalconFPR(1.1475285353733668684571123112513188), - new FalconFPR(1.1702540788534828939713084716509250), - new FalconFPR(1.1925466358390344011122170489094133), - new FalconFPR(1.2144300507766139921088487776957699), - new FalconFPR(1.2359260567719808790104525941706723), - new FalconFPR(1.2570545284063214162779743112075080), - new FalconFPR(1.2778336969128335860256340575729042), - new FalconFPR(1.2982803343442918539708792538826807) + fpr_sigma_min = new double[]{ + 0.0, /* unused */ + 1.1165085072329102588881898380334015, + 1.1321247692325272405718031785357108, + 1.1475285353733668684571123112513188, + 1.1702540788534828939713084716509250, + 1.1925466358390344011122170489094133, + 1.2144300507766139921088487776957699, + 1.2359260567719808790104525941706723, + 1.2570545284063214162779743112075080, + 1.2778336969128335860256340575729042, + 1.2982803343442918539708792538826807 }; - gm_tab = new FalconFPR[]{ - new FalconFPR(0), new FalconFPR(0), /* unused */ - new FalconFPR(-0.000000000000000000000000000), new FalconFPR(1.000000000000000000000000000), - new FalconFPR(0.707106781186547524400844362), new FalconFPR(0.707106781186547524400844362), - new FalconFPR(-0.707106781186547524400844362), new FalconFPR(0.707106781186547524400844362), - new FalconFPR(0.923879532511286756128183189), new FalconFPR(0.382683432365089771728459984), - new FalconFPR(-0.382683432365089771728459984), new FalconFPR(0.923879532511286756128183189), - new FalconFPR(0.382683432365089771728459984), new FalconFPR(0.923879532511286756128183189), - new FalconFPR(-0.923879532511286756128183189), new FalconFPR(0.382683432365089771728459984), - new FalconFPR(0.980785280403230449126182236), new FalconFPR(0.195090322016128267848284868), - new FalconFPR(-0.195090322016128267848284868), new FalconFPR(0.980785280403230449126182236), - new FalconFPR(0.555570233019602224742830814), new FalconFPR(0.831469612302545237078788378), - new FalconFPR(-0.831469612302545237078788378), new FalconFPR(0.555570233019602224742830814), - new FalconFPR(0.831469612302545237078788378), new FalconFPR(0.555570233019602224742830814), - new FalconFPR(-0.555570233019602224742830814), new FalconFPR(0.831469612302545237078788378), - new FalconFPR(0.195090322016128267848284868), new FalconFPR(0.980785280403230449126182236), - new FalconFPR(-0.980785280403230449126182236), new FalconFPR(0.195090322016128267848284868), - new FalconFPR(0.995184726672196886244836953), new FalconFPR(0.098017140329560601994195564), - new FalconFPR(-0.098017140329560601994195564), new FalconFPR(0.995184726672196886244836953), - new FalconFPR(0.634393284163645498215171613), new FalconFPR(0.773010453362736960810906610), - new FalconFPR(-0.773010453362736960810906610), new FalconFPR(0.634393284163645498215171613), - new FalconFPR(0.881921264348355029712756864), new FalconFPR(0.471396736825997648556387626), - new FalconFPR(-0.471396736825997648556387626), new FalconFPR(0.881921264348355029712756864), - new FalconFPR(0.290284677254462367636192376), new FalconFPR(0.956940335732208864935797887), - new FalconFPR(-0.956940335732208864935797887), new FalconFPR(0.290284677254462367636192376), - new FalconFPR(0.956940335732208864935797887), new FalconFPR(0.290284677254462367636192376), - new FalconFPR(-0.290284677254462367636192376), new FalconFPR(0.956940335732208864935797887), - new FalconFPR(0.471396736825997648556387626), new FalconFPR(0.881921264348355029712756864), - new FalconFPR(-0.881921264348355029712756864), new FalconFPR(0.471396736825997648556387626), - new FalconFPR(0.773010453362736960810906610), new FalconFPR(0.634393284163645498215171613), - new FalconFPR(-0.634393284163645498215171613), new FalconFPR(0.773010453362736960810906610), - new FalconFPR(0.098017140329560601994195564), new FalconFPR(0.995184726672196886244836953), - new FalconFPR(-0.995184726672196886244836953), new FalconFPR(0.098017140329560601994195564), - new FalconFPR(0.998795456205172392714771605), new FalconFPR(0.049067674327418014254954977), - new FalconFPR(-0.049067674327418014254954977), new FalconFPR(0.998795456205172392714771605), - new FalconFPR(0.671558954847018400625376850), new FalconFPR(0.740951125354959091175616897), - new FalconFPR(-0.740951125354959091175616897), new FalconFPR(0.671558954847018400625376850), - new FalconFPR(0.903989293123443331586200297), new FalconFPR(0.427555093430282094320966857), - new FalconFPR(-0.427555093430282094320966857), new FalconFPR(0.903989293123443331586200297), - new FalconFPR(0.336889853392220050689253213), new FalconFPR(0.941544065183020778412509403), - new FalconFPR(-0.941544065183020778412509403), new FalconFPR(0.336889853392220050689253213), - new FalconFPR(0.970031253194543992603984207), new FalconFPR(0.242980179903263889948274162), - new FalconFPR(-0.242980179903263889948274162), new FalconFPR(0.970031253194543992603984207), - new FalconFPR(0.514102744193221726593693839), new FalconFPR(0.857728610000272069902269984), - new FalconFPR(-0.857728610000272069902269984), new FalconFPR(0.514102744193221726593693839), - new FalconFPR(0.803207531480644909806676513), new FalconFPR(0.595699304492433343467036529), - new FalconFPR(-0.595699304492433343467036529), new FalconFPR(0.803207531480644909806676513), - new FalconFPR(0.146730474455361751658850130), new FalconFPR(0.989176509964780973451673738), - new FalconFPR(-0.989176509964780973451673738), new FalconFPR(0.146730474455361751658850130), - new FalconFPR(0.989176509964780973451673738), new FalconFPR(0.146730474455361751658850130), - new FalconFPR(-0.146730474455361751658850130), new FalconFPR(0.989176509964780973451673738), - new FalconFPR(0.595699304492433343467036529), new FalconFPR(0.803207531480644909806676513), - new FalconFPR(-0.803207531480644909806676513), new FalconFPR(0.595699304492433343467036529), - new FalconFPR(0.857728610000272069902269984), new FalconFPR(0.514102744193221726593693839), - new FalconFPR(-0.514102744193221726593693839), new FalconFPR(0.857728610000272069902269984), - new FalconFPR(0.242980179903263889948274162), new FalconFPR(0.970031253194543992603984207), - new FalconFPR(-0.970031253194543992603984207), new FalconFPR(0.242980179903263889948274162), - new FalconFPR(0.941544065183020778412509403), new FalconFPR(0.336889853392220050689253213), - new FalconFPR(-0.336889853392220050689253213), new FalconFPR(0.941544065183020778412509403), - new FalconFPR(0.427555093430282094320966857), new FalconFPR(0.903989293123443331586200297), - new FalconFPR(-0.903989293123443331586200297), new FalconFPR(0.427555093430282094320966857), - new FalconFPR(0.740951125354959091175616897), new FalconFPR(0.671558954847018400625376850), - new FalconFPR(-0.671558954847018400625376850), new FalconFPR(0.740951125354959091175616897), - new FalconFPR(0.049067674327418014254954977), new FalconFPR(0.998795456205172392714771605), - new FalconFPR(-0.998795456205172392714771605), new FalconFPR(0.049067674327418014254954977), - new FalconFPR(0.999698818696204220115765650), new FalconFPR(0.024541228522912288031734529), - new FalconFPR(-0.024541228522912288031734529), new FalconFPR(0.999698818696204220115765650), - new FalconFPR(0.689540544737066924616730630), new FalconFPR(0.724247082951466920941069243), - new FalconFPR(-0.724247082951466920941069243), new FalconFPR(0.689540544737066924616730630), - new FalconFPR(0.914209755703530654635014829), new FalconFPR(0.405241314004989870908481306), - new FalconFPR(-0.405241314004989870908481306), new FalconFPR(0.914209755703530654635014829), - new FalconFPR(0.359895036534988148775104572), new FalconFPR(0.932992798834738887711660256), - new FalconFPR(-0.932992798834738887711660256), new FalconFPR(0.359895036534988148775104572), - new FalconFPR(0.975702130038528544460395766), new FalconFPR(0.219101240156869797227737547), - new FalconFPR(-0.219101240156869797227737547), new FalconFPR(0.975702130038528544460395766), - new FalconFPR(0.534997619887097210663076905), new FalconFPR(0.844853565249707073259571205), - new FalconFPR(-0.844853565249707073259571205), new FalconFPR(0.534997619887097210663076905), - new FalconFPR(0.817584813151583696504920884), new FalconFPR(0.575808191417845300745972454), - new FalconFPR(-0.575808191417845300745972454), new FalconFPR(0.817584813151583696504920884), - new FalconFPR(0.170961888760301226363642357), new FalconFPR(0.985277642388941244774018433), - new FalconFPR(-0.985277642388941244774018433), new FalconFPR(0.170961888760301226363642357), - new FalconFPR(0.992479534598709998156767252), new FalconFPR(0.122410675199216198498704474), - new FalconFPR(-0.122410675199216198498704474), new FalconFPR(0.992479534598709998156767252), - new FalconFPR(0.615231590580626845484913563), new FalconFPR(0.788346427626606262009164705), - new FalconFPR(-0.788346427626606262009164705), new FalconFPR(0.615231590580626845484913563), - new FalconFPR(0.870086991108711418652292404), new FalconFPR(0.492898192229784036873026689), - new FalconFPR(-0.492898192229784036873026689), new FalconFPR(0.870086991108711418652292404), - new FalconFPR(0.266712757474898386325286515), new FalconFPR(0.963776065795439866686464356), - new FalconFPR(-0.963776065795439866686464356), new FalconFPR(0.266712757474898386325286515), - new FalconFPR(0.949528180593036667195936074), new FalconFPR(0.313681740398891476656478846), - new FalconFPR(-0.313681740398891476656478846), new FalconFPR(0.949528180593036667195936074), - new FalconFPR(0.449611329654606600046294579), new FalconFPR(0.893224301195515320342416447), - new FalconFPR(-0.893224301195515320342416447), new FalconFPR(0.449611329654606600046294579), - new FalconFPR(0.757208846506484547575464054), new FalconFPR(0.653172842953776764084203014), - new FalconFPR(-0.653172842953776764084203014), new FalconFPR(0.757208846506484547575464054), - new FalconFPR(0.073564563599667423529465622), new FalconFPR(0.997290456678690216135597140), - new FalconFPR(-0.997290456678690216135597140), new FalconFPR(0.073564563599667423529465622), - new FalconFPR(0.997290456678690216135597140), new FalconFPR(0.073564563599667423529465622), - new FalconFPR(-0.073564563599667423529465622), new FalconFPR(0.997290456678690216135597140), - new FalconFPR(0.653172842953776764084203014), new FalconFPR(0.757208846506484547575464054), - new FalconFPR(-0.757208846506484547575464054), new FalconFPR(0.653172842953776764084203014), - new FalconFPR(0.893224301195515320342416447), new FalconFPR(0.449611329654606600046294579), - new FalconFPR(-0.449611329654606600046294579), new FalconFPR(0.893224301195515320342416447), - new FalconFPR(0.313681740398891476656478846), new FalconFPR(0.949528180593036667195936074), - new FalconFPR(-0.949528180593036667195936074), new FalconFPR(0.313681740398891476656478846), - new FalconFPR(0.963776065795439866686464356), new FalconFPR(0.266712757474898386325286515), - new FalconFPR(-0.266712757474898386325286515), new FalconFPR(0.963776065795439866686464356), - new FalconFPR(0.492898192229784036873026689), new FalconFPR(0.870086991108711418652292404), - new FalconFPR(-0.870086991108711418652292404), new FalconFPR(0.492898192229784036873026689), - new FalconFPR(0.788346427626606262009164705), new FalconFPR(0.615231590580626845484913563), - new FalconFPR(-0.615231590580626845484913563), new FalconFPR(0.788346427626606262009164705), - new FalconFPR(0.122410675199216198498704474), new FalconFPR(0.992479534598709998156767252), - new FalconFPR(-0.992479534598709998156767252), new FalconFPR(0.122410675199216198498704474), - new FalconFPR(0.985277642388941244774018433), new FalconFPR(0.170961888760301226363642357), - new FalconFPR(-0.170961888760301226363642357), new FalconFPR(0.985277642388941244774018433), - new FalconFPR(0.575808191417845300745972454), new FalconFPR(0.817584813151583696504920884), - new FalconFPR(-0.817584813151583696504920884), new FalconFPR(0.575808191417845300745972454), - new FalconFPR(0.844853565249707073259571205), new FalconFPR(0.534997619887097210663076905), - new FalconFPR(-0.534997619887097210663076905), new FalconFPR(0.844853565249707073259571205), - new FalconFPR(0.219101240156869797227737547), new FalconFPR(0.975702130038528544460395766), - new FalconFPR(-0.975702130038528544460395766), new FalconFPR(0.219101240156869797227737547), - new FalconFPR(0.932992798834738887711660256), new FalconFPR(0.359895036534988148775104572), - new FalconFPR(-0.359895036534988148775104572), new FalconFPR(0.932992798834738887711660256), - new FalconFPR(0.405241314004989870908481306), new FalconFPR(0.914209755703530654635014829), - new FalconFPR(-0.914209755703530654635014829), new FalconFPR(0.405241314004989870908481306), - new FalconFPR(0.724247082951466920941069243), new FalconFPR(0.689540544737066924616730630), - new FalconFPR(-0.689540544737066924616730630), new FalconFPR(0.724247082951466920941069243), - new FalconFPR(0.024541228522912288031734529), new FalconFPR(0.999698818696204220115765650), - new FalconFPR(-0.999698818696204220115765650), new FalconFPR(0.024541228522912288031734529), - new FalconFPR(0.999924701839144540921646491), new FalconFPR(0.012271538285719926079408262), - new FalconFPR(-0.012271538285719926079408262), new FalconFPR(0.999924701839144540921646491), - new FalconFPR(0.698376249408972853554813503), new FalconFPR(0.715730825283818654125532623), - new FalconFPR(-0.715730825283818654125532623), new FalconFPR(0.698376249408972853554813503), - new FalconFPR(0.919113851690057743908477789), new FalconFPR(0.393992040061048108596188661), - new FalconFPR(-0.393992040061048108596188661), new FalconFPR(0.919113851690057743908477789), - new FalconFPR(0.371317193951837543411934967), new FalconFPR(0.928506080473215565937167396), - new FalconFPR(-0.928506080473215565937167396), new FalconFPR(0.371317193951837543411934967), - new FalconFPR(0.978317370719627633106240097), new FalconFPR(0.207111376192218549708116020), - new FalconFPR(-0.207111376192218549708116020), new FalconFPR(0.978317370719627633106240097), - new FalconFPR(0.545324988422046422313987347), new FalconFPR(0.838224705554838043186996856), - new FalconFPR(-0.838224705554838043186996856), new FalconFPR(0.545324988422046422313987347), - new FalconFPR(0.824589302785025264474803737), new FalconFPR(0.565731810783613197389765011), - new FalconFPR(-0.565731810783613197389765011), new FalconFPR(0.824589302785025264474803737), - new FalconFPR(0.183039887955140958516532578), new FalconFPR(0.983105487431216327180301155), - new FalconFPR(-0.983105487431216327180301155), new FalconFPR(0.183039887955140958516532578), - new FalconFPR(0.993906970002356041546922813), new FalconFPR(0.110222207293883058807899140), - new FalconFPR(-0.110222207293883058807899140), new FalconFPR(0.993906970002356041546922813), - new FalconFPR(0.624859488142386377084072816), new FalconFPR(0.780737228572094478301588484), - new FalconFPR(-0.780737228572094478301588484), new FalconFPR(0.624859488142386377084072816), - new FalconFPR(0.876070094195406607095844268), new FalconFPR(0.482183772079122748517344481), - new FalconFPR(-0.482183772079122748517344481), new FalconFPR(0.876070094195406607095844268), - new FalconFPR(0.278519689385053105207848526), new FalconFPR(0.960430519415565811199035138), - new FalconFPR(-0.960430519415565811199035138), new FalconFPR(0.278519689385053105207848526), - new FalconFPR(0.953306040354193836916740383), new FalconFPR(0.302005949319228067003463232), - new FalconFPR(-0.302005949319228067003463232), new FalconFPR(0.953306040354193836916740383), - new FalconFPR(0.460538710958240023633181487), new FalconFPR(0.887639620402853947760181617), - new FalconFPR(-0.887639620402853947760181617), new FalconFPR(0.460538710958240023633181487), - new FalconFPR(0.765167265622458925888815999), new FalconFPR(0.643831542889791465068086063), - new FalconFPR(-0.643831542889791465068086063), new FalconFPR(0.765167265622458925888815999), - new FalconFPR(0.085797312344439890461556332), new FalconFPR(0.996312612182778012627226190), - new FalconFPR(-0.996312612182778012627226190), new FalconFPR(0.085797312344439890461556332), - new FalconFPR(0.998118112900149207125155861), new FalconFPR(0.061320736302208577782614593), - new FalconFPR(-0.061320736302208577782614593), new FalconFPR(0.998118112900149207125155861), - new FalconFPR(0.662415777590171761113069817), new FalconFPR(0.749136394523459325469203257), - new FalconFPR(-0.749136394523459325469203257), new FalconFPR(0.662415777590171761113069817), - new FalconFPR(0.898674465693953843041976744), new FalconFPR(0.438616238538527637647025738), - new FalconFPR(-0.438616238538527637647025738), new FalconFPR(0.898674465693953843041976744), - new FalconFPR(0.325310292162262934135954708), new FalconFPR(0.945607325380521325730945387), - new FalconFPR(-0.945607325380521325730945387), new FalconFPR(0.325310292162262934135954708), - new FalconFPR(0.966976471044852109087220226), new FalconFPR(0.254865659604514571553980779), - new FalconFPR(-0.254865659604514571553980779), new FalconFPR(0.966976471044852109087220226), - new FalconFPR(0.503538383725717558691867071), new FalconFPR(0.863972856121586737918147054), - new FalconFPR(-0.863972856121586737918147054), new FalconFPR(0.503538383725717558691867071), - new FalconFPR(0.795836904608883536262791915), new FalconFPR(0.605511041404325513920626941), - new FalconFPR(-0.605511041404325513920626941), new FalconFPR(0.795836904608883536262791915), - new FalconFPR(0.134580708507126186316358409), new FalconFPR(0.990902635427780025108237011), - new FalconFPR(-0.990902635427780025108237011), new FalconFPR(0.134580708507126186316358409), - new FalconFPR(0.987301418157858382399815802), new FalconFPR(0.158858143333861441684385360), - new FalconFPR(-0.158858143333861441684385360), new FalconFPR(0.987301418157858382399815802), - new FalconFPR(0.585797857456438860328080838), new FalconFPR(0.810457198252594791726703434), - new FalconFPR(-0.810457198252594791726703434), new FalconFPR(0.585797857456438860328080838), - new FalconFPR(0.851355193105265142261290312), new FalconFPR(0.524589682678468906215098464), - new FalconFPR(-0.524589682678468906215098464), new FalconFPR(0.851355193105265142261290312), - new FalconFPR(0.231058108280671119643236018), new FalconFPR(0.972939952205560145467720114), - new FalconFPR(-0.972939952205560145467720114), new FalconFPR(0.231058108280671119643236018), - new FalconFPR(0.937339011912574923201899593), new FalconFPR(0.348418680249434568419308588), - new FalconFPR(-0.348418680249434568419308588), new FalconFPR(0.937339011912574923201899593), - new FalconFPR(0.416429560097637182562598911), new FalconFPR(0.909167983090522376563884788), - new FalconFPR(-0.909167983090522376563884788), new FalconFPR(0.416429560097637182562598911), - new FalconFPR(0.732654271672412834615546649), new FalconFPR(0.680600997795453050594430464), - new FalconFPR(-0.680600997795453050594430464), new FalconFPR(0.732654271672412834615546649), - new FalconFPR(0.036807222941358832324332691), new FalconFPR(0.999322384588349500896221011), - new FalconFPR(-0.999322384588349500896221011), new FalconFPR(0.036807222941358832324332691), - new FalconFPR(0.999322384588349500896221011), new FalconFPR(0.036807222941358832324332691), - new FalconFPR(-0.036807222941358832324332691), new FalconFPR(0.999322384588349500896221011), - new FalconFPR(0.680600997795453050594430464), new FalconFPR(0.732654271672412834615546649), - new FalconFPR(-0.732654271672412834615546649), new FalconFPR(0.680600997795453050594430464), - new FalconFPR(0.909167983090522376563884788), new FalconFPR(0.416429560097637182562598911), - new FalconFPR(-0.416429560097637182562598911), new FalconFPR(0.909167983090522376563884788), - new FalconFPR(0.348418680249434568419308588), new FalconFPR(0.937339011912574923201899593), - new FalconFPR(-0.937339011912574923201899593), new FalconFPR(0.348418680249434568419308588), - new FalconFPR(0.972939952205560145467720114), new FalconFPR(0.231058108280671119643236018), - new FalconFPR(-0.231058108280671119643236018), new FalconFPR(0.972939952205560145467720114), - new FalconFPR(0.524589682678468906215098464), new FalconFPR(0.851355193105265142261290312), - new FalconFPR(-0.851355193105265142261290312), new FalconFPR(0.524589682678468906215098464), - new FalconFPR(0.810457198252594791726703434), new FalconFPR(0.585797857456438860328080838), - new FalconFPR(-0.585797857456438860328080838), new FalconFPR(0.810457198252594791726703434), - new FalconFPR(0.158858143333861441684385360), new FalconFPR(0.987301418157858382399815802), - new FalconFPR(-0.987301418157858382399815802), new FalconFPR(0.158858143333861441684385360), - new FalconFPR(0.990902635427780025108237011), new FalconFPR(0.134580708507126186316358409), - new FalconFPR(-0.134580708507126186316358409), new FalconFPR(0.990902635427780025108237011), - new FalconFPR(0.605511041404325513920626941), new FalconFPR(0.795836904608883536262791915), - new FalconFPR(-0.795836904608883536262791915), new FalconFPR(0.605511041404325513920626941), - new FalconFPR(0.863972856121586737918147054), new FalconFPR(0.503538383725717558691867071), - new FalconFPR(-0.503538383725717558691867071), new FalconFPR(0.863972856121586737918147054), - new FalconFPR(0.254865659604514571553980779), new FalconFPR(0.966976471044852109087220226), - new FalconFPR(-0.966976471044852109087220226), new FalconFPR(0.254865659604514571553980779), - new FalconFPR(0.945607325380521325730945387), new FalconFPR(0.325310292162262934135954708), - new FalconFPR(-0.325310292162262934135954708), new FalconFPR(0.945607325380521325730945387), - new FalconFPR(0.438616238538527637647025738), new FalconFPR(0.898674465693953843041976744), - new FalconFPR(-0.898674465693953843041976744), new FalconFPR(0.438616238538527637647025738), - new FalconFPR(0.749136394523459325469203257), new FalconFPR(0.662415777590171761113069817), - new FalconFPR(-0.662415777590171761113069817), new FalconFPR(0.749136394523459325469203257), - new FalconFPR(0.061320736302208577782614593), new FalconFPR(0.998118112900149207125155861), - new FalconFPR(-0.998118112900149207125155861), new FalconFPR(0.061320736302208577782614593), - new FalconFPR(0.996312612182778012627226190), new FalconFPR(0.085797312344439890461556332), - new FalconFPR(-0.085797312344439890461556332), new FalconFPR(0.996312612182778012627226190), - new FalconFPR(0.643831542889791465068086063), new FalconFPR(0.765167265622458925888815999), - new FalconFPR(-0.765167265622458925888815999), new FalconFPR(0.643831542889791465068086063), - new FalconFPR(0.887639620402853947760181617), new FalconFPR(0.460538710958240023633181487), - new FalconFPR(-0.460538710958240023633181487), new FalconFPR(0.887639620402853947760181617), - new FalconFPR(0.302005949319228067003463232), new FalconFPR(0.953306040354193836916740383), - new FalconFPR(-0.953306040354193836916740383), new FalconFPR(0.302005949319228067003463232), - new FalconFPR(0.960430519415565811199035138), new FalconFPR(0.278519689385053105207848526), - new FalconFPR(-0.278519689385053105207848526), new FalconFPR(0.960430519415565811199035138), - new FalconFPR(0.482183772079122748517344481), new FalconFPR(0.876070094195406607095844268), - new FalconFPR(-0.876070094195406607095844268), new FalconFPR(0.482183772079122748517344481), - new FalconFPR(0.780737228572094478301588484), new FalconFPR(0.624859488142386377084072816), - new FalconFPR(-0.624859488142386377084072816), new FalconFPR(0.780737228572094478301588484), - new FalconFPR(0.110222207293883058807899140), new FalconFPR(0.993906970002356041546922813), - new FalconFPR(-0.993906970002356041546922813), new FalconFPR(0.110222207293883058807899140), - new FalconFPR(0.983105487431216327180301155), new FalconFPR(0.183039887955140958516532578), - new FalconFPR(-0.183039887955140958516532578), new FalconFPR(0.983105487431216327180301155), - new FalconFPR(0.565731810783613197389765011), new FalconFPR(0.824589302785025264474803737), - new FalconFPR(-0.824589302785025264474803737), new FalconFPR(0.565731810783613197389765011), - new FalconFPR(0.838224705554838043186996856), new FalconFPR(0.545324988422046422313987347), - new FalconFPR(-0.545324988422046422313987347), new FalconFPR(0.838224705554838043186996856), - new FalconFPR(0.207111376192218549708116020), new FalconFPR(0.978317370719627633106240097), - new FalconFPR(-0.978317370719627633106240097), new FalconFPR(0.207111376192218549708116020), - new FalconFPR(0.928506080473215565937167396), new FalconFPR(0.371317193951837543411934967), - new FalconFPR(-0.371317193951837543411934967), new FalconFPR(0.928506080473215565937167396), - new FalconFPR(0.393992040061048108596188661), new FalconFPR(0.919113851690057743908477789), - new FalconFPR(-0.919113851690057743908477789), new FalconFPR(0.393992040061048108596188661), - new FalconFPR(0.715730825283818654125532623), new FalconFPR(0.698376249408972853554813503), - new FalconFPR(-0.698376249408972853554813503), new FalconFPR(0.715730825283818654125532623), - new FalconFPR(0.012271538285719926079408262), new FalconFPR(0.999924701839144540921646491), - new FalconFPR(-0.999924701839144540921646491), new FalconFPR(0.012271538285719926079408262), - new FalconFPR(0.999981175282601142656990438), new FalconFPR(0.006135884649154475359640235), - new FalconFPR(-0.006135884649154475359640235), new FalconFPR(0.999981175282601142656990438), - new FalconFPR(0.702754744457225302452914421), new FalconFPR(0.711432195745216441522130290), - new FalconFPR(-0.711432195745216441522130290), new FalconFPR(0.702754744457225302452914421), - new FalconFPR(0.921514039342041943465396332), new FalconFPR(0.388345046698826291624993541), - new FalconFPR(-0.388345046698826291624993541), new FalconFPR(0.921514039342041943465396332), - new FalconFPR(0.377007410216418256726567823), new FalconFPR(0.926210242138311341974793388), - new FalconFPR(-0.926210242138311341974793388), new FalconFPR(0.377007410216418256726567823), - new FalconFPR(0.979569765685440534439326110), new FalconFPR(0.201104634842091911558443546), - new FalconFPR(-0.201104634842091911558443546), new FalconFPR(0.979569765685440534439326110), - new FalconFPR(0.550457972936604802977289893), new FalconFPR(0.834862874986380056304401383), - new FalconFPR(-0.834862874986380056304401383), new FalconFPR(0.550457972936604802977289893), - new FalconFPR(0.828045045257755752067527592), new FalconFPR(0.560661576197336023839710223), - new FalconFPR(-0.560661576197336023839710223), new FalconFPR(0.828045045257755752067527592), - new FalconFPR(0.189068664149806212754997837), new FalconFPR(0.981963869109555264072848154), - new FalconFPR(-0.981963869109555264072848154), new FalconFPR(0.189068664149806212754997837), - new FalconFPR(0.994564570734255452119106243), new FalconFPR(0.104121633872054579120943880), - new FalconFPR(-0.104121633872054579120943880), new FalconFPR(0.994564570734255452119106243), - new FalconFPR(0.629638238914927025372981341), new FalconFPR(0.776888465673232450040827983), - new FalconFPR(-0.776888465673232450040827983), new FalconFPR(0.629638238914927025372981341), - new FalconFPR(0.879012226428633477831323711), new FalconFPR(0.476799230063322133342158117), - new FalconFPR(-0.476799230063322133342158117), new FalconFPR(0.879012226428633477831323711), - new FalconFPR(0.284407537211271843618310615), new FalconFPR(0.958703474895871555374645792), - new FalconFPR(-0.958703474895871555374645792), new FalconFPR(0.284407537211271843618310615), - new FalconFPR(0.955141168305770721498157712), new FalconFPR(0.296150888243623824121786128), - new FalconFPR(-0.296150888243623824121786128), new FalconFPR(0.955141168305770721498157712), - new FalconFPR(0.465976495767966177902756065), new FalconFPR(0.884797098430937780104007041), - new FalconFPR(-0.884797098430937780104007041), new FalconFPR(0.465976495767966177902756065), - new FalconFPR(0.769103337645579639346626069), new FalconFPR(0.639124444863775743801488193), - new FalconFPR(-0.639124444863775743801488193), new FalconFPR(0.769103337645579639346626069), - new FalconFPR(0.091908956497132728624990979), new FalconFPR(0.995767414467659793982495643), - new FalconFPR(-0.995767414467659793982495643), new FalconFPR(0.091908956497132728624990979), - new FalconFPR(0.998475580573294752208559038), new FalconFPR(0.055195244349689939809447526), - new FalconFPR(-0.055195244349689939809447526), new FalconFPR(0.998475580573294752208559038), - new FalconFPR(0.666999922303637506650154222), new FalconFPR(0.745057785441465962407907310), - new FalconFPR(-0.745057785441465962407907310), new FalconFPR(0.666999922303637506650154222), - new FalconFPR(0.901348847046022014570746093), new FalconFPR(0.433093818853151968484222638), - new FalconFPR(-0.433093818853151968484222638), new FalconFPR(0.901348847046022014570746093), - new FalconFPR(0.331106305759876401737190737), new FalconFPR(0.943593458161960361495301445), - new FalconFPR(-0.943593458161960361495301445), new FalconFPR(0.331106305759876401737190737), - new FalconFPR(0.968522094274417316221088329), new FalconFPR(0.248927605745720168110682816), - new FalconFPR(-0.248927605745720168110682816), new FalconFPR(0.968522094274417316221088329), - new FalconFPR(0.508830142543107036931749324), new FalconFPR(0.860866938637767279344583877), - new FalconFPR(-0.860866938637767279344583877), new FalconFPR(0.508830142543107036931749324), - new FalconFPR(0.799537269107905033500246232), new FalconFPR(0.600616479383868926653875896), - new FalconFPR(-0.600616479383868926653875896), new FalconFPR(0.799537269107905033500246232), - new FalconFPR(0.140658239332849230714788846), new FalconFPR(0.990058210262297105505906464), - new FalconFPR(-0.990058210262297105505906464), new FalconFPR(0.140658239332849230714788846), - new FalconFPR(0.988257567730749491404792538), new FalconFPR(0.152797185258443427720336613), - new FalconFPR(-0.152797185258443427720336613), new FalconFPR(0.988257567730749491404792538), - new FalconFPR(0.590759701858874228423887908), new FalconFPR(0.806847553543799272206514313), - new FalconFPR(-0.806847553543799272206514313), new FalconFPR(0.590759701858874228423887908), - new FalconFPR(0.854557988365400520767862276), new FalconFPR(0.519355990165589587361829932), - new FalconFPR(-0.519355990165589587361829932), new FalconFPR(0.854557988365400520767862276), - new FalconFPR(0.237023605994367206867735915), new FalconFPR(0.971503890986251775537099622), - new FalconFPR(-0.971503890986251775537099622), new FalconFPR(0.237023605994367206867735915), - new FalconFPR(0.939459223602189911962669246), new FalconFPR(0.342660717311994397592781983), - new FalconFPR(-0.342660717311994397592781983), new FalconFPR(0.939459223602189911962669246), - new FalconFPR(0.422000270799799685941287941), new FalconFPR(0.906595704514915365332960588), - new FalconFPR(-0.906595704514915365332960588), new FalconFPR(0.422000270799799685941287941), - new FalconFPR(0.736816568877369875090132520), new FalconFPR(0.676092703575315960360419228), - new FalconFPR(-0.676092703575315960360419228), new FalconFPR(0.736816568877369875090132520), - new FalconFPR(0.042938256934940823077124540), new FalconFPR(0.999077727752645382888781997), - new FalconFPR(-0.999077727752645382888781997), new FalconFPR(0.042938256934940823077124540), - new FalconFPR(0.999529417501093163079703322), new FalconFPR(0.030674803176636625934021028), - new FalconFPR(-0.030674803176636625934021028), new FalconFPR(0.999529417501093163079703322), - new FalconFPR(0.685083667772700381362052545), new FalconFPR(0.728464390448225196492035438), - new FalconFPR(-0.728464390448225196492035438), new FalconFPR(0.685083667772700381362052545), - new FalconFPR(0.911706032005429851404397325), new FalconFPR(0.410843171057903942183466675), - new FalconFPR(-0.410843171057903942183466675), new FalconFPR(0.911706032005429851404397325), - new FalconFPR(0.354163525420490382357395796), new FalconFPR(0.935183509938947577642207480), - new FalconFPR(-0.935183509938947577642207480), new FalconFPR(0.354163525420490382357395796), - new FalconFPR(0.974339382785575860518721668), new FalconFPR(0.225083911359792835991642120), - new FalconFPR(-0.225083911359792835991642120), new FalconFPR(0.974339382785575860518721668), - new FalconFPR(0.529803624686294668216054671), new FalconFPR(0.848120344803297251279133563), - new FalconFPR(-0.848120344803297251279133563), new FalconFPR(0.529803624686294668216054671), - new FalconFPR(0.814036329705948361654516690), new FalconFPR(0.580813958095764545075595272), - new FalconFPR(-0.580813958095764545075595272), new FalconFPR(0.814036329705948361654516690), - new FalconFPR(0.164913120489969921418189113), new FalconFPR(0.986308097244598647863297524), - new FalconFPR(-0.986308097244598647863297524), new FalconFPR(0.164913120489969921418189113), - new FalconFPR(0.991709753669099522860049931), new FalconFPR(0.128498110793793172624415589), - new FalconFPR(-0.128498110793793172624415589), new FalconFPR(0.991709753669099522860049931), - new FalconFPR(0.610382806276309452716352152), new FalconFPR(0.792106577300212351782342879), - new FalconFPR(-0.792106577300212351782342879), new FalconFPR(0.610382806276309452716352152), - new FalconFPR(0.867046245515692651480195629), new FalconFPR(0.498227666972781852410983869), - new FalconFPR(-0.498227666972781852410983869), new FalconFPR(0.867046245515692651480195629), - new FalconFPR(0.260794117915275518280186509), new FalconFPR(0.965394441697689374550843858), - new FalconFPR(-0.965394441697689374550843858), new FalconFPR(0.260794117915275518280186509), - new FalconFPR(0.947585591017741134653387321), new FalconFPR(0.319502030816015677901518272), - new FalconFPR(-0.319502030816015677901518272), new FalconFPR(0.947585591017741134653387321), - new FalconFPR(0.444122144570429231642069418), new FalconFPR(0.895966249756185155914560282), - new FalconFPR(-0.895966249756185155914560282), new FalconFPR(0.444122144570429231642069418), - new FalconFPR(0.753186799043612482483430486), new FalconFPR(0.657806693297078656931182264), - new FalconFPR(-0.657806693297078656931182264), new FalconFPR(0.753186799043612482483430486), - new FalconFPR(0.067443919563664057897972422), new FalconFPR(0.997723066644191609848546728), - new FalconFPR(-0.997723066644191609848546728), new FalconFPR(0.067443919563664057897972422), - new FalconFPR(0.996820299291165714972629398), new FalconFPR(0.079682437971430121147120656), - new FalconFPR(-0.079682437971430121147120656), new FalconFPR(0.996820299291165714972629398), - new FalconFPR(0.648514401022112445084560551), new FalconFPR(0.761202385484261814029709836), - new FalconFPR(-0.761202385484261814029709836), new FalconFPR(0.648514401022112445084560551), - new FalconFPR(0.890448723244757889952150560), new FalconFPR(0.455083587126343823535869268), - new FalconFPR(-0.455083587126343823535869268), new FalconFPR(0.890448723244757889952150560), - new FalconFPR(0.307849640041534893682063646), new FalconFPR(0.951435020969008369549175569), - new FalconFPR(-0.951435020969008369549175569), new FalconFPR(0.307849640041534893682063646), - new FalconFPR(0.962121404269041595429604316), new FalconFPR(0.272621355449948984493347477), - new FalconFPR(-0.272621355449948984493347477), new FalconFPR(0.962121404269041595429604316), - new FalconFPR(0.487550160148435954641485027), new FalconFPR(0.873094978418290098636085973), - new FalconFPR(-0.873094978418290098636085973), new FalconFPR(0.487550160148435954641485027), - new FalconFPR(0.784556597155575233023892575), new FalconFPR(0.620057211763289178646268191), - new FalconFPR(-0.620057211763289178646268191), new FalconFPR(0.784556597155575233023892575), - new FalconFPR(0.116318630911904767252544319), new FalconFPR(0.993211949234794533104601012), - new FalconFPR(-0.993211949234794533104601012), new FalconFPR(0.116318630911904767252544319), - new FalconFPR(0.984210092386929073193874387), new FalconFPR(0.177004220412148756196839844), - new FalconFPR(-0.177004220412148756196839844), new FalconFPR(0.984210092386929073193874387), - new FalconFPR(0.570780745886967280232652864), new FalconFPR(0.821102514991104679060430820), - new FalconFPR(-0.821102514991104679060430820), new FalconFPR(0.570780745886967280232652864), - new FalconFPR(0.841554977436898409603499520), new FalconFPR(0.540171472729892881297845480), - new FalconFPR(-0.540171472729892881297845480), new FalconFPR(0.841554977436898409603499520), - new FalconFPR(0.213110319916091373967757518), new FalconFPR(0.977028142657754351485866211), - new FalconFPR(-0.977028142657754351485866211), new FalconFPR(0.213110319916091373967757518), - new FalconFPR(0.930766961078983731944872340), new FalconFPR(0.365612997804773870011745909), - new FalconFPR(-0.365612997804773870011745909), new FalconFPR(0.930766961078983731944872340), - new FalconFPR(0.399624199845646828544117031), new FalconFPR(0.916679059921042663116457013), - new FalconFPR(-0.916679059921042663116457013), new FalconFPR(0.399624199845646828544117031), - new FalconFPR(0.720002507961381629076682999), new FalconFPR(0.693971460889654009003734389), - new FalconFPR(-0.693971460889654009003734389), new FalconFPR(0.720002507961381629076682999), - new FalconFPR(0.018406729905804820927366313), new FalconFPR(0.999830581795823422015722275), - new FalconFPR(-0.999830581795823422015722275), new FalconFPR(0.018406729905804820927366313), - new FalconFPR(0.999830581795823422015722275), new FalconFPR(0.018406729905804820927366313), - new FalconFPR(-0.018406729905804820927366313), new FalconFPR(0.999830581795823422015722275), - new FalconFPR(0.693971460889654009003734389), new FalconFPR(0.720002507961381629076682999), - new FalconFPR(-0.720002507961381629076682999), new FalconFPR(0.693971460889654009003734389), - new FalconFPR(0.916679059921042663116457013), new FalconFPR(0.399624199845646828544117031), - new FalconFPR(-0.399624199845646828544117031), new FalconFPR(0.916679059921042663116457013), - new FalconFPR(0.365612997804773870011745909), new FalconFPR(0.930766961078983731944872340), - new FalconFPR(-0.930766961078983731944872340), new FalconFPR(0.365612997804773870011745909), - new FalconFPR(0.977028142657754351485866211), new FalconFPR(0.213110319916091373967757518), - new FalconFPR(-0.213110319916091373967757518), new FalconFPR(0.977028142657754351485866211), - new FalconFPR(0.540171472729892881297845480), new FalconFPR(0.841554977436898409603499520), - new FalconFPR(-0.841554977436898409603499520), new FalconFPR(0.540171472729892881297845480), - new FalconFPR(0.821102514991104679060430820), new FalconFPR(0.570780745886967280232652864), - new FalconFPR(-0.570780745886967280232652864), new FalconFPR(0.821102514991104679060430820), - new FalconFPR(0.177004220412148756196839844), new FalconFPR(0.984210092386929073193874387), - new FalconFPR(-0.984210092386929073193874387), new FalconFPR(0.177004220412148756196839844), - new FalconFPR(0.993211949234794533104601012), new FalconFPR(0.116318630911904767252544319), - new FalconFPR(-0.116318630911904767252544319), new FalconFPR(0.993211949234794533104601012), - new FalconFPR(0.620057211763289178646268191), new FalconFPR(0.784556597155575233023892575), - new FalconFPR(-0.784556597155575233023892575), new FalconFPR(0.620057211763289178646268191), - new FalconFPR(0.873094978418290098636085973), new FalconFPR(0.487550160148435954641485027), - new FalconFPR(-0.487550160148435954641485027), new FalconFPR(0.873094978418290098636085973), - new FalconFPR(0.272621355449948984493347477), new FalconFPR(0.962121404269041595429604316), - new FalconFPR(-0.962121404269041595429604316), new FalconFPR(0.272621355449948984493347477), - new FalconFPR(0.951435020969008369549175569), new FalconFPR(0.307849640041534893682063646), - new FalconFPR(-0.307849640041534893682063646), new FalconFPR(0.951435020969008369549175569), - new FalconFPR(0.455083587126343823535869268), new FalconFPR(0.890448723244757889952150560), - new FalconFPR(-0.890448723244757889952150560), new FalconFPR(0.455083587126343823535869268), - new FalconFPR(0.761202385484261814029709836), new FalconFPR(0.648514401022112445084560551), - new FalconFPR(-0.648514401022112445084560551), new FalconFPR(0.761202385484261814029709836), - new FalconFPR(0.079682437971430121147120656), new FalconFPR(0.996820299291165714972629398), - new FalconFPR(-0.996820299291165714972629398), new FalconFPR(0.079682437971430121147120656), - new FalconFPR(0.997723066644191609848546728), new FalconFPR(0.067443919563664057897972422), - new FalconFPR(-0.067443919563664057897972422), new FalconFPR(0.997723066644191609848546728), - new FalconFPR(0.657806693297078656931182264), new FalconFPR(0.753186799043612482483430486), - new FalconFPR(-0.753186799043612482483430486), new FalconFPR(0.657806693297078656931182264), - new FalconFPR(0.895966249756185155914560282), new FalconFPR(0.444122144570429231642069418), - new FalconFPR(-0.444122144570429231642069418), new FalconFPR(0.895966249756185155914560282), - new FalconFPR(0.319502030816015677901518272), new FalconFPR(0.947585591017741134653387321), - new FalconFPR(-0.947585591017741134653387321), new FalconFPR(0.319502030816015677901518272), - new FalconFPR(0.965394441697689374550843858), new FalconFPR(0.260794117915275518280186509), - new FalconFPR(-0.260794117915275518280186509), new FalconFPR(0.965394441697689374550843858), - new FalconFPR(0.498227666972781852410983869), new FalconFPR(0.867046245515692651480195629), - new FalconFPR(-0.867046245515692651480195629), new FalconFPR(0.498227666972781852410983869), - new FalconFPR(0.792106577300212351782342879), new FalconFPR(0.610382806276309452716352152), - new FalconFPR(-0.610382806276309452716352152), new FalconFPR(0.792106577300212351782342879), - new FalconFPR(0.128498110793793172624415589), new FalconFPR(0.991709753669099522860049931), - new FalconFPR(-0.991709753669099522860049931), new FalconFPR(0.128498110793793172624415589), - new FalconFPR(0.986308097244598647863297524), new FalconFPR(0.164913120489969921418189113), - new FalconFPR(-0.164913120489969921418189113), new FalconFPR(0.986308097244598647863297524), - new FalconFPR(0.580813958095764545075595272), new FalconFPR(0.814036329705948361654516690), - new FalconFPR(-0.814036329705948361654516690), new FalconFPR(0.580813958095764545075595272), - new FalconFPR(0.848120344803297251279133563), new FalconFPR(0.529803624686294668216054671), - new FalconFPR(-0.529803624686294668216054671), new FalconFPR(0.848120344803297251279133563), - new FalconFPR(0.225083911359792835991642120), new FalconFPR(0.974339382785575860518721668), - new FalconFPR(-0.974339382785575860518721668), new FalconFPR(0.225083911359792835991642120), - new FalconFPR(0.935183509938947577642207480), new FalconFPR(0.354163525420490382357395796), - new FalconFPR(-0.354163525420490382357395796), new FalconFPR(0.935183509938947577642207480), - new FalconFPR(0.410843171057903942183466675), new FalconFPR(0.911706032005429851404397325), - new FalconFPR(-0.911706032005429851404397325), new FalconFPR(0.410843171057903942183466675), - new FalconFPR(0.728464390448225196492035438), new FalconFPR(0.685083667772700381362052545), - new FalconFPR(-0.685083667772700381362052545), new FalconFPR(0.728464390448225196492035438), - new FalconFPR(0.030674803176636625934021028), new FalconFPR(0.999529417501093163079703322), - new FalconFPR(-0.999529417501093163079703322), new FalconFPR(0.030674803176636625934021028), - new FalconFPR(0.999077727752645382888781997), new FalconFPR(0.042938256934940823077124540), - new FalconFPR(-0.042938256934940823077124540), new FalconFPR(0.999077727752645382888781997), - new FalconFPR(0.676092703575315960360419228), new FalconFPR(0.736816568877369875090132520), - new FalconFPR(-0.736816568877369875090132520), new FalconFPR(0.676092703575315960360419228), - new FalconFPR(0.906595704514915365332960588), new FalconFPR(0.422000270799799685941287941), - new FalconFPR(-0.422000270799799685941287941), new FalconFPR(0.906595704514915365332960588), - new FalconFPR(0.342660717311994397592781983), new FalconFPR(0.939459223602189911962669246), - new FalconFPR(-0.939459223602189911962669246), new FalconFPR(0.342660717311994397592781983), - new FalconFPR(0.971503890986251775537099622), new FalconFPR(0.237023605994367206867735915), - new FalconFPR(-0.237023605994367206867735915), new FalconFPR(0.971503890986251775537099622), - new FalconFPR(0.519355990165589587361829932), new FalconFPR(0.854557988365400520767862276), - new FalconFPR(-0.854557988365400520767862276), new FalconFPR(0.519355990165589587361829932), - new FalconFPR(0.806847553543799272206514313), new FalconFPR(0.590759701858874228423887908), - new FalconFPR(-0.590759701858874228423887908), new FalconFPR(0.806847553543799272206514313), - new FalconFPR(0.152797185258443427720336613), new FalconFPR(0.988257567730749491404792538), - new FalconFPR(-0.988257567730749491404792538), new FalconFPR(0.152797185258443427720336613), - new FalconFPR(0.990058210262297105505906464), new FalconFPR(0.140658239332849230714788846), - new FalconFPR(-0.140658239332849230714788846), new FalconFPR(0.990058210262297105505906464), - new FalconFPR(0.600616479383868926653875896), new FalconFPR(0.799537269107905033500246232), - new FalconFPR(-0.799537269107905033500246232), new FalconFPR(0.600616479383868926653875896), - new FalconFPR(0.860866938637767279344583877), new FalconFPR(0.508830142543107036931749324), - new FalconFPR(-0.508830142543107036931749324), new FalconFPR(0.860866938637767279344583877), - new FalconFPR(0.248927605745720168110682816), new FalconFPR(0.968522094274417316221088329), - new FalconFPR(-0.968522094274417316221088329), new FalconFPR(0.248927605745720168110682816), - new FalconFPR(0.943593458161960361495301445), new FalconFPR(0.331106305759876401737190737), - new FalconFPR(-0.331106305759876401737190737), new FalconFPR(0.943593458161960361495301445), - new FalconFPR(0.433093818853151968484222638), new FalconFPR(0.901348847046022014570746093), - new FalconFPR(-0.901348847046022014570746093), new FalconFPR(0.433093818853151968484222638), - new FalconFPR(0.745057785441465962407907310), new FalconFPR(0.666999922303637506650154222), - new FalconFPR(-0.666999922303637506650154222), new FalconFPR(0.745057785441465962407907310), - new FalconFPR(0.055195244349689939809447526), new FalconFPR(0.998475580573294752208559038), - new FalconFPR(-0.998475580573294752208559038), new FalconFPR(0.055195244349689939809447526), - new FalconFPR(0.995767414467659793982495643), new FalconFPR(0.091908956497132728624990979), - new FalconFPR(-0.091908956497132728624990979), new FalconFPR(0.995767414467659793982495643), - new FalconFPR(0.639124444863775743801488193), new FalconFPR(0.769103337645579639346626069), - new FalconFPR(-0.769103337645579639346626069), new FalconFPR(0.639124444863775743801488193), - new FalconFPR(0.884797098430937780104007041), new FalconFPR(0.465976495767966177902756065), - new FalconFPR(-0.465976495767966177902756065), new FalconFPR(0.884797098430937780104007041), - new FalconFPR(0.296150888243623824121786128), new FalconFPR(0.955141168305770721498157712), - new FalconFPR(-0.955141168305770721498157712), new FalconFPR(0.296150888243623824121786128), - new FalconFPR(0.958703474895871555374645792), new FalconFPR(0.284407537211271843618310615), - new FalconFPR(-0.284407537211271843618310615), new FalconFPR(0.958703474895871555374645792), - new FalconFPR(0.476799230063322133342158117), new FalconFPR(0.879012226428633477831323711), - new FalconFPR(-0.879012226428633477831323711), new FalconFPR(0.476799230063322133342158117), - new FalconFPR(0.776888465673232450040827983), new FalconFPR(0.629638238914927025372981341), - new FalconFPR(-0.629638238914927025372981341), new FalconFPR(0.776888465673232450040827983), - new FalconFPR(0.104121633872054579120943880), new FalconFPR(0.994564570734255452119106243), - new FalconFPR(-0.994564570734255452119106243), new FalconFPR(0.104121633872054579120943880), - new FalconFPR(0.981963869109555264072848154), new FalconFPR(0.189068664149806212754997837), - new FalconFPR(-0.189068664149806212754997837), new FalconFPR(0.981963869109555264072848154), - new FalconFPR(0.560661576197336023839710223), new FalconFPR(0.828045045257755752067527592), - new FalconFPR(-0.828045045257755752067527592), new FalconFPR(0.560661576197336023839710223), - new FalconFPR(0.834862874986380056304401383), new FalconFPR(0.550457972936604802977289893), - new FalconFPR(-0.550457972936604802977289893), new FalconFPR(0.834862874986380056304401383), - new FalconFPR(0.201104634842091911558443546), new FalconFPR(0.979569765685440534439326110), - new FalconFPR(-0.979569765685440534439326110), new FalconFPR(0.201104634842091911558443546), - new FalconFPR(0.926210242138311341974793388), new FalconFPR(0.377007410216418256726567823), - new FalconFPR(-0.377007410216418256726567823), new FalconFPR(0.926210242138311341974793388), - new FalconFPR(0.388345046698826291624993541), new FalconFPR(0.921514039342041943465396332), - new FalconFPR(-0.921514039342041943465396332), new FalconFPR(0.388345046698826291624993541), - new FalconFPR(0.711432195745216441522130290), new FalconFPR(0.702754744457225302452914421), - new FalconFPR(-0.702754744457225302452914421), new FalconFPR(0.711432195745216441522130290), - new FalconFPR(0.006135884649154475359640235), new FalconFPR(0.999981175282601142656990438), - new FalconFPR(-0.999981175282601142656990438), new FalconFPR(0.006135884649154475359640235), - new FalconFPR(0.999995293809576171511580126), new FalconFPR(0.003067956762965976270145365), - new FalconFPR(-0.003067956762965976270145365), new FalconFPR(0.999995293809576171511580126), - new FalconFPR(0.704934080375904908852523758), new FalconFPR(0.709272826438865651316533772), - new FalconFPR(-0.709272826438865651316533772), new FalconFPR(0.704934080375904908852523758), - new FalconFPR(0.922701128333878570437264227), new FalconFPR(0.385516053843918864075607949), - new FalconFPR(-0.385516053843918864075607949), new FalconFPR(0.922701128333878570437264227), - new FalconFPR(0.379847208924051170576281147), new FalconFPR(0.925049240782677590302371869), - new FalconFPR(-0.925049240782677590302371869), new FalconFPR(0.379847208924051170576281147), - new FalconFPR(0.980182135968117392690210009), new FalconFPR(0.198098410717953586179324918), - new FalconFPR(-0.198098410717953586179324918), new FalconFPR(0.980182135968117392690210009), - new FalconFPR(0.553016705580027531764226988), new FalconFPR(0.833170164701913186439915922), - new FalconFPR(-0.833170164701913186439915922), new FalconFPR(0.553016705580027531764226988), - new FalconFPR(0.829761233794523042469023765), new FalconFPR(0.558118531220556115693702964), - new FalconFPR(-0.558118531220556115693702964), new FalconFPR(0.829761233794523042469023765), - new FalconFPR(0.192080397049892441679288205), new FalconFPR(0.981379193313754574318224190), - new FalconFPR(-0.981379193313754574318224190), new FalconFPR(0.192080397049892441679288205), - new FalconFPR(0.994879330794805620591166107), new FalconFPR(0.101069862754827824987887585), - new FalconFPR(-0.101069862754827824987887585), new FalconFPR(0.994879330794805620591166107), - new FalconFPR(0.632018735939809021909403706), new FalconFPR(0.774953106594873878359129282), - new FalconFPR(-0.774953106594873878359129282), new FalconFPR(0.632018735939809021909403706), - new FalconFPR(0.880470889052160770806542929), new FalconFPR(0.474100214650550014398580015), - new FalconFPR(-0.474100214650550014398580015), new FalconFPR(0.880470889052160770806542929), - new FalconFPR(0.287347459544729526477331841), new FalconFPR(0.957826413027532890321037029), - new FalconFPR(-0.957826413027532890321037029), new FalconFPR(0.287347459544729526477331841), - new FalconFPR(0.956045251349996443270479823), new FalconFPR(0.293219162694258650606608599), - new FalconFPR(-0.293219162694258650606608599), new FalconFPR(0.956045251349996443270479823), - new FalconFPR(0.468688822035827933697617870), new FalconFPR(0.883363338665731594736308015), - new FalconFPR(-0.883363338665731594736308015), new FalconFPR(0.468688822035827933697617870), - new FalconFPR(0.771060524261813773200605759), new FalconFPR(0.636761861236284230413943435), - new FalconFPR(-0.636761861236284230413943435), new FalconFPR(0.771060524261813773200605759), - new FalconFPR(0.094963495329638998938034312), new FalconFPR(0.995480755491926941769171600), - new FalconFPR(-0.995480755491926941769171600), new FalconFPR(0.094963495329638998938034312), - new FalconFPR(0.998640218180265222418199049), new FalconFPR(0.052131704680283321236358216), - new FalconFPR(-0.052131704680283321236358216), new FalconFPR(0.998640218180265222418199049), - new FalconFPR(0.669282588346636065720696366), new FalconFPR(0.743007952135121693517362293), - new FalconFPR(-0.743007952135121693517362293), new FalconFPR(0.669282588346636065720696366), - new FalconFPR(0.902673318237258806751502391), new FalconFPR(0.430326481340082633908199031), - new FalconFPR(-0.430326481340082633908199031), new FalconFPR(0.902673318237258806751502391), - new FalconFPR(0.333999651442009404650865481), new FalconFPR(0.942573197601446879280758735), - new FalconFPR(-0.942573197601446879280758735), new FalconFPR(0.333999651442009404650865481), - new FalconFPR(0.969281235356548486048290738), new FalconFPR(0.245955050335794611599924709), - new FalconFPR(-0.245955050335794611599924709), new FalconFPR(0.969281235356548486048290738), - new FalconFPR(0.511468850437970399504391001), new FalconFPR(0.859301818357008404783582139), - new FalconFPR(-0.859301818357008404783582139), new FalconFPR(0.511468850437970399504391001), - new FalconFPR(0.801376171723140219430247777), new FalconFPR(0.598160706996342311724958652), - new FalconFPR(-0.598160706996342311724958652), new FalconFPR(0.801376171723140219430247777), - new FalconFPR(0.143695033150294454819773349), new FalconFPR(0.989622017463200834623694454), - new FalconFPR(-0.989622017463200834623694454), new FalconFPR(0.143695033150294454819773349), - new FalconFPR(0.988721691960323767604516485), new FalconFPR(0.149764534677321517229695737), - new FalconFPR(-0.149764534677321517229695737), new FalconFPR(0.988721691960323767604516485), - new FalconFPR(0.593232295039799808047809426), new FalconFPR(0.805031331142963597922659282), - new FalconFPR(-0.805031331142963597922659282), new FalconFPR(0.593232295039799808047809426), - new FalconFPR(0.856147328375194481019630732), new FalconFPR(0.516731799017649881508753876), - new FalconFPR(-0.516731799017649881508753876), new FalconFPR(0.856147328375194481019630732), - new FalconFPR(0.240003022448741486568922365), new FalconFPR(0.970772140728950302138169611), - new FalconFPR(-0.970772140728950302138169611), new FalconFPR(0.240003022448741486568922365), - new FalconFPR(0.940506070593268323787291309), new FalconFPR(0.339776884406826857828825803), - new FalconFPR(-0.339776884406826857828825803), new FalconFPR(0.940506070593268323787291309), - new FalconFPR(0.424779681209108833357226189), new FalconFPR(0.905296759318118774354048329), - new FalconFPR(-0.905296759318118774354048329), new FalconFPR(0.424779681209108833357226189), - new FalconFPR(0.738887324460615147933116508), new FalconFPR(0.673829000378756060917568372), - new FalconFPR(-0.673829000378756060917568372), new FalconFPR(0.738887324460615147933116508), - new FalconFPR(0.046003182130914628814301788), new FalconFPR(0.998941293186856850633930266), - new FalconFPR(-0.998941293186856850633930266), new FalconFPR(0.046003182130914628814301788), - new FalconFPR(0.999618822495178597116830637), new FalconFPR(0.027608145778965741612354872), - new FalconFPR(-0.027608145778965741612354872), new FalconFPR(0.999618822495178597116830637), - new FalconFPR(0.687315340891759108199186948), new FalconFPR(0.726359155084345976817494315), - new FalconFPR(-0.726359155084345976817494315), new FalconFPR(0.687315340891759108199186948), - new FalconFPR(0.912962190428398164628018233), new FalconFPR(0.408044162864978680820747499), - new FalconFPR(-0.408044162864978680820747499), new FalconFPR(0.912962190428398164628018233), - new FalconFPR(0.357030961233430032614954036), new FalconFPR(0.934092550404258914729877883), - new FalconFPR(-0.934092550404258914729877883), new FalconFPR(0.357030961233430032614954036), - new FalconFPR(0.975025345066994146844913468), new FalconFPR(0.222093620973203534094094721), - new FalconFPR(-0.222093620973203534094094721), new FalconFPR(0.975025345066994146844913468), - new FalconFPR(0.532403127877197971442805218), new FalconFPR(0.846490938774052078300544488), - new FalconFPR(-0.846490938774052078300544488), new FalconFPR(0.532403127877197971442805218), - new FalconFPR(0.815814410806733789010772660), new FalconFPR(0.578313796411655563342245019), - new FalconFPR(-0.578313796411655563342245019), new FalconFPR(0.815814410806733789010772660), - new FalconFPR(0.167938294974731178054745536), new FalconFPR(0.985797509167567424700995000), - new FalconFPR(-0.985797509167567424700995000), new FalconFPR(0.167938294974731178054745536), - new FalconFPR(0.992099313142191757112085445), new FalconFPR(0.125454983411546238542336453), - new FalconFPR(-0.125454983411546238542336453), new FalconFPR(0.992099313142191757112085445), - new FalconFPR(0.612810082429409703935211936), new FalconFPR(0.790230221437310055030217152), - new FalconFPR(-0.790230221437310055030217152), new FalconFPR(0.612810082429409703935211936), - new FalconFPR(0.868570705971340895340449876), new FalconFPR(0.495565261825772531150266670), - new FalconFPR(-0.495565261825772531150266670), new FalconFPR(0.868570705971340895340449876), - new FalconFPR(0.263754678974831383611349322), new FalconFPR(0.964589793289812723836432159), - new FalconFPR(-0.964589793289812723836432159), new FalconFPR(0.263754678974831383611349322), - new FalconFPR(0.948561349915730288158494826), new FalconFPR(0.316593375556165867243047035), - new FalconFPR(-0.316593375556165867243047035), new FalconFPR(0.948561349915730288158494826), - new FalconFPR(0.446868840162374195353044389), new FalconFPR(0.894599485631382678433072126), - new FalconFPR(-0.894599485631382678433072126), new FalconFPR(0.446868840162374195353044389), - new FalconFPR(0.755201376896536527598710756), new FalconFPR(0.655492852999615385312679701), - new FalconFPR(-0.655492852999615385312679701), new FalconFPR(0.755201376896536527598710756), - new FalconFPR(0.070504573389613863027351471), new FalconFPR(0.997511456140303459699448390), - new FalconFPR(-0.997511456140303459699448390), new FalconFPR(0.070504573389613863027351471), - new FalconFPR(0.997060070339482978987989949), new FalconFPR(0.076623861392031492278332463), - new FalconFPR(-0.076623861392031492278332463), new FalconFPR(0.997060070339482978987989949), - new FalconFPR(0.650846684996380915068975573), new FalconFPR(0.759209188978388033485525443), - new FalconFPR(-0.759209188978388033485525443), new FalconFPR(0.650846684996380915068975573), - new FalconFPR(0.891840709392342727796478697), new FalconFPR(0.452349587233770874133026703), - new FalconFPR(-0.452349587233770874133026703), new FalconFPR(0.891840709392342727796478697), - new FalconFPR(0.310767152749611495835997250), new FalconFPR(0.950486073949481721759926101), - new FalconFPR(-0.950486073949481721759926101), new FalconFPR(0.310767152749611495835997250), - new FalconFPR(0.962953266873683886347921481), new FalconFPR(0.269668325572915106525464462), - new FalconFPR(-0.269668325572915106525464462), new FalconFPR(0.962953266873683886347921481), - new FalconFPR(0.490226483288291154229598449), new FalconFPR(0.871595086655951034842481435), - new FalconFPR(-0.871595086655951034842481435), new FalconFPR(0.490226483288291154229598449), - new FalconFPR(0.786455213599085757522319464), new FalconFPR(0.617647307937803932403979402), - new FalconFPR(-0.617647307937803932403979402), new FalconFPR(0.786455213599085757522319464), - new FalconFPR(0.119365214810991364593637790), new FalconFPR(0.992850414459865090793563344), - new FalconFPR(-0.992850414459865090793563344), new FalconFPR(0.119365214810991364593637790), - new FalconFPR(0.984748501801904218556553176), new FalconFPR(0.173983873387463827950700807), - new FalconFPR(-0.173983873387463827950700807), new FalconFPR(0.984748501801904218556553176), - new FalconFPR(0.573297166698042212820171239), new FalconFPR(0.819347520076796960824689637), - new FalconFPR(-0.819347520076796960824689637), new FalconFPR(0.573297166698042212820171239), - new FalconFPR(0.843208239641845437161743865), new FalconFPR(0.537587076295645482502214932), - new FalconFPR(-0.537587076295645482502214932), new FalconFPR(0.843208239641845437161743865), - new FalconFPR(0.216106797076219509948385131), new FalconFPR(0.976369731330021149312732194), - new FalconFPR(-0.976369731330021149312732194), new FalconFPR(0.216106797076219509948385131), - new FalconFPR(0.931884265581668106718557199), new FalconFPR(0.362755724367397216204854462), - new FalconFPR(-0.362755724367397216204854462), new FalconFPR(0.931884265581668106718557199), - new FalconFPR(0.402434650859418441082533934), new FalconFPR(0.915448716088267819566431292), - new FalconFPR(-0.915448716088267819566431292), new FalconFPR(0.402434650859418441082533934), - new FalconFPR(0.722128193929215321243607198), new FalconFPR(0.691759258364157774906734132), - new FalconFPR(-0.691759258364157774906734132), new FalconFPR(0.722128193929215321243607198), - new FalconFPR(0.021474080275469507418374898), new FalconFPR(0.999769405351215321657617036), - new FalconFPR(-0.999769405351215321657617036), new FalconFPR(0.021474080275469507418374898), - new FalconFPR(0.999882347454212525633049627), new FalconFPR(0.015339206284988101044151868), - new FalconFPR(-0.015339206284988101044151868), new FalconFPR(0.999882347454212525633049627), - new FalconFPR(0.696177131491462944788582591), new FalconFPR(0.717870045055731736211325329), - new FalconFPR(-0.717870045055731736211325329), new FalconFPR(0.696177131491462944788582591), - new FalconFPR(0.917900775621390457642276297), new FalconFPR(0.396809987416710328595290911), - new FalconFPR(-0.396809987416710328595290911), new FalconFPR(0.917900775621390457642276297), - new FalconFPR(0.368466829953372331712746222), new FalconFPR(0.929640895843181265457918066), - new FalconFPR(-0.929640895843181265457918066), new FalconFPR(0.368466829953372331712746222), - new FalconFPR(0.977677357824509979943404762), new FalconFPR(0.210111836880469621717489972), - new FalconFPR(-0.210111836880469621717489972), new FalconFPR(0.977677357824509979943404762), - new FalconFPR(0.542750784864515906586768661), new FalconFPR(0.839893794195999504583383987), - new FalconFPR(-0.839893794195999504583383987), new FalconFPR(0.542750784864515906586768661), - new FalconFPR(0.822849781375826332046780034), new FalconFPR(0.568258952670131549790548489), - new FalconFPR(-0.568258952670131549790548489), new FalconFPR(0.822849781375826332046780034), - new FalconFPR(0.180022901405699522679906590), new FalconFPR(0.983662419211730274396237776), - new FalconFPR(-0.983662419211730274396237776), new FalconFPR(0.180022901405699522679906590), - new FalconFPR(0.993564135520595333782021697), new FalconFPR(0.113270952177564349018228733), - new FalconFPR(-0.113270952177564349018228733), new FalconFPR(0.993564135520595333782021697), - new FalconFPR(0.622461279374149972519166721), new FalconFPR(0.782650596166575738458949301), - new FalconFPR(-0.782650596166575738458949301), new FalconFPR(0.622461279374149972519166721), - new FalconFPR(0.874586652278176112634431897), new FalconFPR(0.484869248000791101822951699), - new FalconFPR(-0.484869248000791101822951699), new FalconFPR(0.874586652278176112634431897), - new FalconFPR(0.275571819310958163076425168), new FalconFPR(0.961280485811320641748659653), - new FalconFPR(-0.961280485811320641748659653), new FalconFPR(0.275571819310958163076425168), - new FalconFPR(0.952375012719765858529893608), new FalconFPR(0.304929229735402406490728633), - new FalconFPR(-0.304929229735402406490728633), new FalconFPR(0.952375012719765858529893608), - new FalconFPR(0.457813303598877221904961155), new FalconFPR(0.889048355854664562540777729), - new FalconFPR(-0.889048355854664562540777729), new FalconFPR(0.457813303598877221904961155), - new FalconFPR(0.763188417263381271704838297), new FalconFPR(0.646176012983316364832802220), - new FalconFPR(-0.646176012983316364832802220), new FalconFPR(0.763188417263381271704838297), - new FalconFPR(0.082740264549375693111987083), new FalconFPR(0.996571145790554847093566910), - new FalconFPR(-0.996571145790554847093566910), new FalconFPR(0.082740264549375693111987083), - new FalconFPR(0.997925286198596012623025462), new FalconFPR(0.064382630929857460819324537), - new FalconFPR(-0.064382630929857460819324537), new FalconFPR(0.997925286198596012623025462), - new FalconFPR(0.660114342067420478559490747), new FalconFPR(0.751165131909686411205819422), - new FalconFPR(-0.751165131909686411205819422), new FalconFPR(0.660114342067420478559490747), - new FalconFPR(0.897324580705418281231391836), new FalconFPR(0.441371268731716692879988968), - new FalconFPR(-0.441371268731716692879988968), new FalconFPR(0.897324580705418281231391836), - new FalconFPR(0.322407678801069848384807478), new FalconFPR(0.946600913083283570044599823), - new FalconFPR(-0.946600913083283570044599823), new FalconFPR(0.322407678801069848384807478), - new FalconFPR(0.966190003445412555433832961), new FalconFPR(0.257831102162159005614471295), - new FalconFPR(-0.257831102162159005614471295), new FalconFPR(0.966190003445412555433832961), - new FalconFPR(0.500885382611240786241285004), new FalconFPR(0.865513624090569082825488358), - new FalconFPR(-0.865513624090569082825488358), new FalconFPR(0.500885382611240786241285004), - new FalconFPR(0.793975477554337164895083757), new FalconFPR(0.607949784967773667243642671), - new FalconFPR(-0.607949784967773667243642671), new FalconFPR(0.793975477554337164895083757), - new FalconFPR(0.131540028702883111103387493), new FalconFPR(0.991310859846115418957349799), - new FalconFPR(-0.991310859846115418957349799), new FalconFPR(0.131540028702883111103387493), - new FalconFPR(0.986809401814185476970235952), new FalconFPR(0.161886393780111837641387995), - new FalconFPR(-0.161886393780111837641387995), new FalconFPR(0.986809401814185476970235952), - new FalconFPR(0.583308652937698294392830961), new FalconFPR(0.812250586585203913049744181), - new FalconFPR(-0.812250586585203913049744181), new FalconFPR(0.583308652937698294392830961), - new FalconFPR(0.849741768000852489471268395), new FalconFPR(0.527199134781901348464274575), - new FalconFPR(-0.527199134781901348464274575), new FalconFPR(0.849741768000852489471268395), - new FalconFPR(0.228072083170885739254457379), new FalconFPR(0.973644249650811925318383912), - new FalconFPR(-0.973644249650811925318383912), new FalconFPR(0.228072083170885739254457379), - new FalconFPR(0.936265667170278246576310996), new FalconFPR(0.351292756085567125601307623), - new FalconFPR(-0.351292756085567125601307623), new FalconFPR(0.936265667170278246576310996), - new FalconFPR(0.413638312238434547471944324), new FalconFPR(0.910441292258067196934095369), - new FalconFPR(-0.910441292258067196934095369), new FalconFPR(0.413638312238434547471944324), - new FalconFPR(0.730562769227827561177758850), new FalconFPR(0.682845546385248068164596123), - new FalconFPR(-0.682845546385248068164596123), new FalconFPR(0.730562769227827561177758850), - new FalconFPR(0.033741171851377584833716112), new FalconFPR(0.999430604555461772019008327), - new FalconFPR(-0.999430604555461772019008327), new FalconFPR(0.033741171851377584833716112), - new FalconFPR(0.999204758618363895492950001), new FalconFPR(0.039872927587739811128578738), - new FalconFPR(-0.039872927587739811128578738), new FalconFPR(0.999204758618363895492950001), - new FalconFPR(0.678350043129861486873655042), new FalconFPR(0.734738878095963464563223604), - new FalconFPR(-0.734738878095963464563223604), new FalconFPR(0.678350043129861486873655042), - new FalconFPR(0.907886116487666212038681480), new FalconFPR(0.419216888363223956433010020), - new FalconFPR(-0.419216888363223956433010020), new FalconFPR(0.907886116487666212038681480), - new FalconFPR(0.345541324963989065539191723), new FalconFPR(0.938403534063108112192420774), - new FalconFPR(-0.938403534063108112192420774), new FalconFPR(0.345541324963989065539191723), - new FalconFPR(0.972226497078936305708321144), new FalconFPR(0.234041958583543423191242045), - new FalconFPR(-0.234041958583543423191242045), new FalconFPR(0.972226497078936305708321144), - new FalconFPR(0.521975292937154342694258318), new FalconFPR(0.852960604930363657746588082), - new FalconFPR(-0.852960604930363657746588082), new FalconFPR(0.521975292937154342694258318), - new FalconFPR(0.808656181588174991946968128), new FalconFPR(0.588281548222645304786439813), - new FalconFPR(-0.588281548222645304786439813), new FalconFPR(0.808656181588174991946968128), - new FalconFPR(0.155828397654265235743101486), new FalconFPR(0.987784141644572154230969032), - new FalconFPR(-0.987784141644572154230969032), new FalconFPR(0.155828397654265235743101486), - new FalconFPR(0.990485084256457037998682243), new FalconFPR(0.137620121586486044948441663), - new FalconFPR(-0.137620121586486044948441663), new FalconFPR(0.990485084256457037998682243), - new FalconFPR(0.603066598540348201693430617), new FalconFPR(0.797690840943391108362662755), - new FalconFPR(-0.797690840943391108362662755), new FalconFPR(0.603066598540348201693430617), - new FalconFPR(0.862423956111040538690933878), new FalconFPR(0.506186645345155291048942344), - new FalconFPR(-0.506186645345155291048942344), new FalconFPR(0.862423956111040538690933878), - new FalconFPR(0.251897818154216950498106628), new FalconFPR(0.967753837093475465243391912), - new FalconFPR(-0.967753837093475465243391912), new FalconFPR(0.251897818154216950498106628), - new FalconFPR(0.944604837261480265659265493), new FalconFPR(0.328209843579092526107916817), - new FalconFPR(-0.328209843579092526107916817), new FalconFPR(0.944604837261480265659265493), - new FalconFPR(0.435857079922255491032544080), new FalconFPR(0.900015892016160228714535267), - new FalconFPR(-0.900015892016160228714535267), new FalconFPR(0.435857079922255491032544080), - new FalconFPR(0.747100605980180144323078847), new FalconFPR(0.664710978203344868130324985), - new FalconFPR(-0.664710978203344868130324985), new FalconFPR(0.747100605980180144323078847), - new FalconFPR(0.058258264500435759613979782), new FalconFPR(0.998301544933892840738782163), - new FalconFPR(-0.998301544933892840738782163), new FalconFPR(0.058258264500435759613979782), - new FalconFPR(0.996044700901251989887944810), new FalconFPR(0.088853552582524596561586535), - new FalconFPR(-0.088853552582524596561586535), new FalconFPR(0.996044700901251989887944810), - new FalconFPR(0.641481012808583151988739898), new FalconFPR(0.767138911935820381181694573), - new FalconFPR(-0.767138911935820381181694573), new FalconFPR(0.641481012808583151988739898), - new FalconFPR(0.886222530148880631647990821), new FalconFPR(0.463259783551860197390719637), - new FalconFPR(-0.463259783551860197390719637), new FalconFPR(0.886222530148880631647990821), - new FalconFPR(0.299079826308040476750336973), new FalconFPR(0.954228095109105629780430732), - new FalconFPR(-0.954228095109105629780430732), new FalconFPR(0.299079826308040476750336973), - new FalconFPR(0.959571513081984528335528181), new FalconFPR(0.281464937925757984095231007), - new FalconFPR(-0.281464937925757984095231007), new FalconFPR(0.959571513081984528335528181), - new FalconFPR(0.479493757660153026679839798), new FalconFPR(0.877545290207261291668470750), - new FalconFPR(-0.877545290207261291668470750), new FalconFPR(0.479493757660153026679839798), - new FalconFPR(0.778816512381475953374724325), new FalconFPR(0.627251815495144113509622565), - new FalconFPR(-0.627251815495144113509622565), new FalconFPR(0.778816512381475953374724325), - new FalconFPR(0.107172424956808849175529148), new FalconFPR(0.994240449453187946358413442), - new FalconFPR(-0.994240449453187946358413442), new FalconFPR(0.107172424956808849175529148), - new FalconFPR(0.982539302287441255907040396), new FalconFPR(0.186055151663446648105438304), - new FalconFPR(-0.186055151663446648105438304), new FalconFPR(0.982539302287441255907040396), - new FalconFPR(0.563199344013834115007363772), new FalconFPR(0.826321062845663480311195452), - new FalconFPR(-0.826321062845663480311195452), new FalconFPR(0.563199344013834115007363772), - new FalconFPR(0.836547727223511984524285790), new FalconFPR(0.547894059173100165608820571), - new FalconFPR(-0.547894059173100165608820571), new FalconFPR(0.836547727223511984524285790), - new FalconFPR(0.204108966092816874181696950), new FalconFPR(0.978948175319062194715480124), - new FalconFPR(-0.978948175319062194715480124), new FalconFPR(0.204108966092816874181696950), - new FalconFPR(0.927362525650401087274536959), new FalconFPR(0.374164062971457997104393020), - new FalconFPR(-0.374164062971457997104393020), new FalconFPR(0.927362525650401087274536959), - new FalconFPR(0.391170384302253888687512949), new FalconFPR(0.920318276709110566440076541), - new FalconFPR(-0.920318276709110566440076541), new FalconFPR(0.391170384302253888687512949), - new FalconFPR(0.713584868780793592903125099), new FalconFPR(0.700568793943248366792866380), - new FalconFPR(-0.700568793943248366792866380), new FalconFPR(0.713584868780793592903125099), - new FalconFPR(0.009203754782059819315102378), new FalconFPR(0.999957644551963866333120920), - new FalconFPR(-0.999957644551963866333120920), new FalconFPR(0.009203754782059819315102378), - new FalconFPR(0.999957644551963866333120920), new FalconFPR(0.009203754782059819315102378), - new FalconFPR(-0.009203754782059819315102378), new FalconFPR(0.999957644551963866333120920), - new FalconFPR(0.700568793943248366792866380), new FalconFPR(0.713584868780793592903125099), - new FalconFPR(-0.713584868780793592903125099), new FalconFPR(0.700568793943248366792866380), - new FalconFPR(0.920318276709110566440076541), new FalconFPR(0.391170384302253888687512949), - new FalconFPR(-0.391170384302253888687512949), new FalconFPR(0.920318276709110566440076541), - new FalconFPR(0.374164062971457997104393020), new FalconFPR(0.927362525650401087274536959), - new FalconFPR(-0.927362525650401087274536959), new FalconFPR(0.374164062971457997104393020), - new FalconFPR(0.978948175319062194715480124), new FalconFPR(0.204108966092816874181696950), - new FalconFPR(-0.204108966092816874181696950), new FalconFPR(0.978948175319062194715480124), - new FalconFPR(0.547894059173100165608820571), new FalconFPR(0.836547727223511984524285790), - new FalconFPR(-0.836547727223511984524285790), new FalconFPR(0.547894059173100165608820571), - new FalconFPR(0.826321062845663480311195452), new FalconFPR(0.563199344013834115007363772), - new FalconFPR(-0.563199344013834115007363772), new FalconFPR(0.826321062845663480311195452), - new FalconFPR(0.186055151663446648105438304), new FalconFPR(0.982539302287441255907040396), - new FalconFPR(-0.982539302287441255907040396), new FalconFPR(0.186055151663446648105438304), - new FalconFPR(0.994240449453187946358413442), new FalconFPR(0.107172424956808849175529148), - new FalconFPR(-0.107172424956808849175529148), new FalconFPR(0.994240449453187946358413442), - new FalconFPR(0.627251815495144113509622565), new FalconFPR(0.778816512381475953374724325), - new FalconFPR(-0.778816512381475953374724325), new FalconFPR(0.627251815495144113509622565), - new FalconFPR(0.877545290207261291668470750), new FalconFPR(0.479493757660153026679839798), - new FalconFPR(-0.479493757660153026679839798), new FalconFPR(0.877545290207261291668470750), - new FalconFPR(0.281464937925757984095231007), new FalconFPR(0.959571513081984528335528181), - new FalconFPR(-0.959571513081984528335528181), new FalconFPR(0.281464937925757984095231007), - new FalconFPR(0.954228095109105629780430732), new FalconFPR(0.299079826308040476750336973), - new FalconFPR(-0.299079826308040476750336973), new FalconFPR(0.954228095109105629780430732), - new FalconFPR(0.463259783551860197390719637), new FalconFPR(0.886222530148880631647990821), - new FalconFPR(-0.886222530148880631647990821), new FalconFPR(0.463259783551860197390719637), - new FalconFPR(0.767138911935820381181694573), new FalconFPR(0.641481012808583151988739898), - new FalconFPR(-0.641481012808583151988739898), new FalconFPR(0.767138911935820381181694573), - new FalconFPR(0.088853552582524596561586535), new FalconFPR(0.996044700901251989887944810), - new FalconFPR(-0.996044700901251989887944810), new FalconFPR(0.088853552582524596561586535), - new FalconFPR(0.998301544933892840738782163), new FalconFPR(0.058258264500435759613979782), - new FalconFPR(-0.058258264500435759613979782), new FalconFPR(0.998301544933892840738782163), - new FalconFPR(0.664710978203344868130324985), new FalconFPR(0.747100605980180144323078847), - new FalconFPR(-0.747100605980180144323078847), new FalconFPR(0.664710978203344868130324985), - new FalconFPR(0.900015892016160228714535267), new FalconFPR(0.435857079922255491032544080), - new FalconFPR(-0.435857079922255491032544080), new FalconFPR(0.900015892016160228714535267), - new FalconFPR(0.328209843579092526107916817), new FalconFPR(0.944604837261480265659265493), - new FalconFPR(-0.944604837261480265659265493), new FalconFPR(0.328209843579092526107916817), - new FalconFPR(0.967753837093475465243391912), new FalconFPR(0.251897818154216950498106628), - new FalconFPR(-0.251897818154216950498106628), new FalconFPR(0.967753837093475465243391912), - new FalconFPR(0.506186645345155291048942344), new FalconFPR(0.862423956111040538690933878), - new FalconFPR(-0.862423956111040538690933878), new FalconFPR(0.506186645345155291048942344), - new FalconFPR(0.797690840943391108362662755), new FalconFPR(0.603066598540348201693430617), - new FalconFPR(-0.603066598540348201693430617), new FalconFPR(0.797690840943391108362662755), - new FalconFPR(0.137620121586486044948441663), new FalconFPR(0.990485084256457037998682243), - new FalconFPR(-0.990485084256457037998682243), new FalconFPR(0.137620121586486044948441663), - new FalconFPR(0.987784141644572154230969032), new FalconFPR(0.155828397654265235743101486), - new FalconFPR(-0.155828397654265235743101486), new FalconFPR(0.987784141644572154230969032), - new FalconFPR(0.588281548222645304786439813), new FalconFPR(0.808656181588174991946968128), - new FalconFPR(-0.808656181588174991946968128), new FalconFPR(0.588281548222645304786439813), - new FalconFPR(0.852960604930363657746588082), new FalconFPR(0.521975292937154342694258318), - new FalconFPR(-0.521975292937154342694258318), new FalconFPR(0.852960604930363657746588082), - new FalconFPR(0.234041958583543423191242045), new FalconFPR(0.972226497078936305708321144), - new FalconFPR(-0.972226497078936305708321144), new FalconFPR(0.234041958583543423191242045), - new FalconFPR(0.938403534063108112192420774), new FalconFPR(0.345541324963989065539191723), - new FalconFPR(-0.345541324963989065539191723), new FalconFPR(0.938403534063108112192420774), - new FalconFPR(0.419216888363223956433010020), new FalconFPR(0.907886116487666212038681480), - new FalconFPR(-0.907886116487666212038681480), new FalconFPR(0.419216888363223956433010020), - new FalconFPR(0.734738878095963464563223604), new FalconFPR(0.678350043129861486873655042), - new FalconFPR(-0.678350043129861486873655042), new FalconFPR(0.734738878095963464563223604), - new FalconFPR(0.039872927587739811128578738), new FalconFPR(0.999204758618363895492950001), - new FalconFPR(-0.999204758618363895492950001), new FalconFPR(0.039872927587739811128578738), - new FalconFPR(0.999430604555461772019008327), new FalconFPR(0.033741171851377584833716112), - new FalconFPR(-0.033741171851377584833716112), new FalconFPR(0.999430604555461772019008327), - new FalconFPR(0.682845546385248068164596123), new FalconFPR(0.730562769227827561177758850), - new FalconFPR(-0.730562769227827561177758850), new FalconFPR(0.682845546385248068164596123), - new FalconFPR(0.910441292258067196934095369), new FalconFPR(0.413638312238434547471944324), - new FalconFPR(-0.413638312238434547471944324), new FalconFPR(0.910441292258067196934095369), - new FalconFPR(0.351292756085567125601307623), new FalconFPR(0.936265667170278246576310996), - new FalconFPR(-0.936265667170278246576310996), new FalconFPR(0.351292756085567125601307623), - new FalconFPR(0.973644249650811925318383912), new FalconFPR(0.228072083170885739254457379), - new FalconFPR(-0.228072083170885739254457379), new FalconFPR(0.973644249650811925318383912), - new FalconFPR(0.527199134781901348464274575), new FalconFPR(0.849741768000852489471268395), - new FalconFPR(-0.849741768000852489471268395), new FalconFPR(0.527199134781901348464274575), - new FalconFPR(0.812250586585203913049744181), new FalconFPR(0.583308652937698294392830961), - new FalconFPR(-0.583308652937698294392830961), new FalconFPR(0.812250586585203913049744181), - new FalconFPR(0.161886393780111837641387995), new FalconFPR(0.986809401814185476970235952), - new FalconFPR(-0.986809401814185476970235952), new FalconFPR(0.161886393780111837641387995), - new FalconFPR(0.991310859846115418957349799), new FalconFPR(0.131540028702883111103387493), - new FalconFPR(-0.131540028702883111103387493), new FalconFPR(0.991310859846115418957349799), - new FalconFPR(0.607949784967773667243642671), new FalconFPR(0.793975477554337164895083757), - new FalconFPR(-0.793975477554337164895083757), new FalconFPR(0.607949784967773667243642671), - new FalconFPR(0.865513624090569082825488358), new FalconFPR(0.500885382611240786241285004), - new FalconFPR(-0.500885382611240786241285004), new FalconFPR(0.865513624090569082825488358), - new FalconFPR(0.257831102162159005614471295), new FalconFPR(0.966190003445412555433832961), - new FalconFPR(-0.966190003445412555433832961), new FalconFPR(0.257831102162159005614471295), - new FalconFPR(0.946600913083283570044599823), new FalconFPR(0.322407678801069848384807478), - new FalconFPR(-0.322407678801069848384807478), new FalconFPR(0.946600913083283570044599823), - new FalconFPR(0.441371268731716692879988968), new FalconFPR(0.897324580705418281231391836), - new FalconFPR(-0.897324580705418281231391836), new FalconFPR(0.441371268731716692879988968), - new FalconFPR(0.751165131909686411205819422), new FalconFPR(0.660114342067420478559490747), - new FalconFPR(-0.660114342067420478559490747), new FalconFPR(0.751165131909686411205819422), - new FalconFPR(0.064382630929857460819324537), new FalconFPR(0.997925286198596012623025462), - new FalconFPR(-0.997925286198596012623025462), new FalconFPR(0.064382630929857460819324537), - new FalconFPR(0.996571145790554847093566910), new FalconFPR(0.082740264549375693111987083), - new FalconFPR(-0.082740264549375693111987083), new FalconFPR(0.996571145790554847093566910), - new FalconFPR(0.646176012983316364832802220), new FalconFPR(0.763188417263381271704838297), - new FalconFPR(-0.763188417263381271704838297), new FalconFPR(0.646176012983316364832802220), - new FalconFPR(0.889048355854664562540777729), new FalconFPR(0.457813303598877221904961155), - new FalconFPR(-0.457813303598877221904961155), new FalconFPR(0.889048355854664562540777729), - new FalconFPR(0.304929229735402406490728633), new FalconFPR(0.952375012719765858529893608), - new FalconFPR(-0.952375012719765858529893608), new FalconFPR(0.304929229735402406490728633), - new FalconFPR(0.961280485811320641748659653), new FalconFPR(0.275571819310958163076425168), - new FalconFPR(-0.275571819310958163076425168), new FalconFPR(0.961280485811320641748659653), - new FalconFPR(0.484869248000791101822951699), new FalconFPR(0.874586652278176112634431897), - new FalconFPR(-0.874586652278176112634431897), new FalconFPR(0.484869248000791101822951699), - new FalconFPR(0.782650596166575738458949301), new FalconFPR(0.622461279374149972519166721), - new FalconFPR(-0.622461279374149972519166721), new FalconFPR(0.782650596166575738458949301), - new FalconFPR(0.113270952177564349018228733), new FalconFPR(0.993564135520595333782021697), - new FalconFPR(-0.993564135520595333782021697), new FalconFPR(0.113270952177564349018228733), - new FalconFPR(0.983662419211730274396237776), new FalconFPR(0.180022901405699522679906590), - new FalconFPR(-0.180022901405699522679906590), new FalconFPR(0.983662419211730274396237776), - new FalconFPR(0.568258952670131549790548489), new FalconFPR(0.822849781375826332046780034), - new FalconFPR(-0.822849781375826332046780034), new FalconFPR(0.568258952670131549790548489), - new FalconFPR(0.839893794195999504583383987), new FalconFPR(0.542750784864515906586768661), - new FalconFPR(-0.542750784864515906586768661), new FalconFPR(0.839893794195999504583383987), - new FalconFPR(0.210111836880469621717489972), new FalconFPR(0.977677357824509979943404762), - new FalconFPR(-0.977677357824509979943404762), new FalconFPR(0.210111836880469621717489972), - new FalconFPR(0.929640895843181265457918066), new FalconFPR(0.368466829953372331712746222), - new FalconFPR(-0.368466829953372331712746222), new FalconFPR(0.929640895843181265457918066), - new FalconFPR(0.396809987416710328595290911), new FalconFPR(0.917900775621390457642276297), - new FalconFPR(-0.917900775621390457642276297), new FalconFPR(0.396809987416710328595290911), - new FalconFPR(0.717870045055731736211325329), new FalconFPR(0.696177131491462944788582591), - new FalconFPR(-0.696177131491462944788582591), new FalconFPR(0.717870045055731736211325329), - new FalconFPR(0.015339206284988101044151868), new FalconFPR(0.999882347454212525633049627), - new FalconFPR(-0.999882347454212525633049627), new FalconFPR(0.015339206284988101044151868), - new FalconFPR(0.999769405351215321657617036), new FalconFPR(0.021474080275469507418374898), - new FalconFPR(-0.021474080275469507418374898), new FalconFPR(0.999769405351215321657617036), - new FalconFPR(0.691759258364157774906734132), new FalconFPR(0.722128193929215321243607198), - new FalconFPR(-0.722128193929215321243607198), new FalconFPR(0.691759258364157774906734132), - new FalconFPR(0.915448716088267819566431292), new FalconFPR(0.402434650859418441082533934), - new FalconFPR(-0.402434650859418441082533934), new FalconFPR(0.915448716088267819566431292), - new FalconFPR(0.362755724367397216204854462), new FalconFPR(0.931884265581668106718557199), - new FalconFPR(-0.931884265581668106718557199), new FalconFPR(0.362755724367397216204854462), - new FalconFPR(0.976369731330021149312732194), new FalconFPR(0.216106797076219509948385131), - new FalconFPR(-0.216106797076219509948385131), new FalconFPR(0.976369731330021149312732194), - new FalconFPR(0.537587076295645482502214932), new FalconFPR(0.843208239641845437161743865), - new FalconFPR(-0.843208239641845437161743865), new FalconFPR(0.537587076295645482502214932), - new FalconFPR(0.819347520076796960824689637), new FalconFPR(0.573297166698042212820171239), - new FalconFPR(-0.573297166698042212820171239), new FalconFPR(0.819347520076796960824689637), - new FalconFPR(0.173983873387463827950700807), new FalconFPR(0.984748501801904218556553176), - new FalconFPR(-0.984748501801904218556553176), new FalconFPR(0.173983873387463827950700807), - new FalconFPR(0.992850414459865090793563344), new FalconFPR(0.119365214810991364593637790), - new FalconFPR(-0.119365214810991364593637790), new FalconFPR(0.992850414459865090793563344), - new FalconFPR(0.617647307937803932403979402), new FalconFPR(0.786455213599085757522319464), - new FalconFPR(-0.786455213599085757522319464), new FalconFPR(0.617647307937803932403979402), - new FalconFPR(0.871595086655951034842481435), new FalconFPR(0.490226483288291154229598449), - new FalconFPR(-0.490226483288291154229598449), new FalconFPR(0.871595086655951034842481435), - new FalconFPR(0.269668325572915106525464462), new FalconFPR(0.962953266873683886347921481), - new FalconFPR(-0.962953266873683886347921481), new FalconFPR(0.269668325572915106525464462), - new FalconFPR(0.950486073949481721759926101), new FalconFPR(0.310767152749611495835997250), - new FalconFPR(-0.310767152749611495835997250), new FalconFPR(0.950486073949481721759926101), - new FalconFPR(0.452349587233770874133026703), new FalconFPR(0.891840709392342727796478697), - new FalconFPR(-0.891840709392342727796478697), new FalconFPR(0.452349587233770874133026703), - new FalconFPR(0.759209188978388033485525443), new FalconFPR(0.650846684996380915068975573), - new FalconFPR(-0.650846684996380915068975573), new FalconFPR(0.759209188978388033485525443), - new FalconFPR(0.076623861392031492278332463), new FalconFPR(0.997060070339482978987989949), - new FalconFPR(-0.997060070339482978987989949), new FalconFPR(0.076623861392031492278332463), - new FalconFPR(0.997511456140303459699448390), new FalconFPR(0.070504573389613863027351471), - new FalconFPR(-0.070504573389613863027351471), new FalconFPR(0.997511456140303459699448390), - new FalconFPR(0.655492852999615385312679701), new FalconFPR(0.755201376896536527598710756), - new FalconFPR(-0.755201376896536527598710756), new FalconFPR(0.655492852999615385312679701), - new FalconFPR(0.894599485631382678433072126), new FalconFPR(0.446868840162374195353044389), - new FalconFPR(-0.446868840162374195353044389), new FalconFPR(0.894599485631382678433072126), - new FalconFPR(0.316593375556165867243047035), new FalconFPR(0.948561349915730288158494826), - new FalconFPR(-0.948561349915730288158494826), new FalconFPR(0.316593375556165867243047035), - new FalconFPR(0.964589793289812723836432159), new FalconFPR(0.263754678974831383611349322), - new FalconFPR(-0.263754678974831383611349322), new FalconFPR(0.964589793289812723836432159), - new FalconFPR(0.495565261825772531150266670), new FalconFPR(0.868570705971340895340449876), - new FalconFPR(-0.868570705971340895340449876), new FalconFPR(0.495565261825772531150266670), - new FalconFPR(0.790230221437310055030217152), new FalconFPR(0.612810082429409703935211936), - new FalconFPR(-0.612810082429409703935211936), new FalconFPR(0.790230221437310055030217152), - new FalconFPR(0.125454983411546238542336453), new FalconFPR(0.992099313142191757112085445), - new FalconFPR(-0.992099313142191757112085445), new FalconFPR(0.125454983411546238542336453), - new FalconFPR(0.985797509167567424700995000), new FalconFPR(0.167938294974731178054745536), - new FalconFPR(-0.167938294974731178054745536), new FalconFPR(0.985797509167567424700995000), - new FalconFPR(0.578313796411655563342245019), new FalconFPR(0.815814410806733789010772660), - new FalconFPR(-0.815814410806733789010772660), new FalconFPR(0.578313796411655563342245019), - new FalconFPR(0.846490938774052078300544488), new FalconFPR(0.532403127877197971442805218), - new FalconFPR(-0.532403127877197971442805218), new FalconFPR(0.846490938774052078300544488), - new FalconFPR(0.222093620973203534094094721), new FalconFPR(0.975025345066994146844913468), - new FalconFPR(-0.975025345066994146844913468), new FalconFPR(0.222093620973203534094094721), - new FalconFPR(0.934092550404258914729877883), new FalconFPR(0.357030961233430032614954036), - new FalconFPR(-0.357030961233430032614954036), new FalconFPR(0.934092550404258914729877883), - new FalconFPR(0.408044162864978680820747499), new FalconFPR(0.912962190428398164628018233), - new FalconFPR(-0.912962190428398164628018233), new FalconFPR(0.408044162864978680820747499), - new FalconFPR(0.726359155084345976817494315), new FalconFPR(0.687315340891759108199186948), - new FalconFPR(-0.687315340891759108199186948), new FalconFPR(0.726359155084345976817494315), - new FalconFPR(0.027608145778965741612354872), new FalconFPR(0.999618822495178597116830637), - new FalconFPR(-0.999618822495178597116830637), new FalconFPR(0.027608145778965741612354872), - new FalconFPR(0.998941293186856850633930266), new FalconFPR(0.046003182130914628814301788), - new FalconFPR(-0.046003182130914628814301788), new FalconFPR(0.998941293186856850633930266), - new FalconFPR(0.673829000378756060917568372), new FalconFPR(0.738887324460615147933116508), - new FalconFPR(-0.738887324460615147933116508), new FalconFPR(0.673829000378756060917568372), - new FalconFPR(0.905296759318118774354048329), new FalconFPR(0.424779681209108833357226189), - new FalconFPR(-0.424779681209108833357226189), new FalconFPR(0.905296759318118774354048329), - new FalconFPR(0.339776884406826857828825803), new FalconFPR(0.940506070593268323787291309), - new FalconFPR(-0.940506070593268323787291309), new FalconFPR(0.339776884406826857828825803), - new FalconFPR(0.970772140728950302138169611), new FalconFPR(0.240003022448741486568922365), - new FalconFPR(-0.240003022448741486568922365), new FalconFPR(0.970772140728950302138169611), - new FalconFPR(0.516731799017649881508753876), new FalconFPR(0.856147328375194481019630732), - new FalconFPR(-0.856147328375194481019630732), new FalconFPR(0.516731799017649881508753876), - new FalconFPR(0.805031331142963597922659282), new FalconFPR(0.593232295039799808047809426), - new FalconFPR(-0.593232295039799808047809426), new FalconFPR(0.805031331142963597922659282), - new FalconFPR(0.149764534677321517229695737), new FalconFPR(0.988721691960323767604516485), - new FalconFPR(-0.988721691960323767604516485), new FalconFPR(0.149764534677321517229695737), - new FalconFPR(0.989622017463200834623694454), new FalconFPR(0.143695033150294454819773349), - new FalconFPR(-0.143695033150294454819773349), new FalconFPR(0.989622017463200834623694454), - new FalconFPR(0.598160706996342311724958652), new FalconFPR(0.801376171723140219430247777), - new FalconFPR(-0.801376171723140219430247777), new FalconFPR(0.598160706996342311724958652), - new FalconFPR(0.859301818357008404783582139), new FalconFPR(0.511468850437970399504391001), - new FalconFPR(-0.511468850437970399504391001), new FalconFPR(0.859301818357008404783582139), - new FalconFPR(0.245955050335794611599924709), new FalconFPR(0.969281235356548486048290738), - new FalconFPR(-0.969281235356548486048290738), new FalconFPR(0.245955050335794611599924709), - new FalconFPR(0.942573197601446879280758735), new FalconFPR(0.333999651442009404650865481), - new FalconFPR(-0.333999651442009404650865481), new FalconFPR(0.942573197601446879280758735), - new FalconFPR(0.430326481340082633908199031), new FalconFPR(0.902673318237258806751502391), - new FalconFPR(-0.902673318237258806751502391), new FalconFPR(0.430326481340082633908199031), - new FalconFPR(0.743007952135121693517362293), new FalconFPR(0.669282588346636065720696366), - new FalconFPR(-0.669282588346636065720696366), new FalconFPR(0.743007952135121693517362293), - new FalconFPR(0.052131704680283321236358216), new FalconFPR(0.998640218180265222418199049), - new FalconFPR(-0.998640218180265222418199049), new FalconFPR(0.052131704680283321236358216), - new FalconFPR(0.995480755491926941769171600), new FalconFPR(0.094963495329638998938034312), - new FalconFPR(-0.094963495329638998938034312), new FalconFPR(0.995480755491926941769171600), - new FalconFPR(0.636761861236284230413943435), new FalconFPR(0.771060524261813773200605759), - new FalconFPR(-0.771060524261813773200605759), new FalconFPR(0.636761861236284230413943435), - new FalconFPR(0.883363338665731594736308015), new FalconFPR(0.468688822035827933697617870), - new FalconFPR(-0.468688822035827933697617870), new FalconFPR(0.883363338665731594736308015), - new FalconFPR(0.293219162694258650606608599), new FalconFPR(0.956045251349996443270479823), - new FalconFPR(-0.956045251349996443270479823), new FalconFPR(0.293219162694258650606608599), - new FalconFPR(0.957826413027532890321037029), new FalconFPR(0.287347459544729526477331841), - new FalconFPR(-0.287347459544729526477331841), new FalconFPR(0.957826413027532890321037029), - new FalconFPR(0.474100214650550014398580015), new FalconFPR(0.880470889052160770806542929), - new FalconFPR(-0.880470889052160770806542929), new FalconFPR(0.474100214650550014398580015), - new FalconFPR(0.774953106594873878359129282), new FalconFPR(0.632018735939809021909403706), - new FalconFPR(-0.632018735939809021909403706), new FalconFPR(0.774953106594873878359129282), - new FalconFPR(0.101069862754827824987887585), new FalconFPR(0.994879330794805620591166107), - new FalconFPR(-0.994879330794805620591166107), new FalconFPR(0.101069862754827824987887585), - new FalconFPR(0.981379193313754574318224190), new FalconFPR(0.192080397049892441679288205), - new FalconFPR(-0.192080397049892441679288205), new FalconFPR(0.981379193313754574318224190), - new FalconFPR(0.558118531220556115693702964), new FalconFPR(0.829761233794523042469023765), - new FalconFPR(-0.829761233794523042469023765), new FalconFPR(0.558118531220556115693702964), - new FalconFPR(0.833170164701913186439915922), new FalconFPR(0.553016705580027531764226988), - new FalconFPR(-0.553016705580027531764226988), new FalconFPR(0.833170164701913186439915922), - new FalconFPR(0.198098410717953586179324918), new FalconFPR(0.980182135968117392690210009), - new FalconFPR(-0.980182135968117392690210009), new FalconFPR(0.198098410717953586179324918), - new FalconFPR(0.925049240782677590302371869), new FalconFPR(0.379847208924051170576281147), - new FalconFPR(-0.379847208924051170576281147), new FalconFPR(0.925049240782677590302371869), - new FalconFPR(0.385516053843918864075607949), new FalconFPR(0.922701128333878570437264227), - new FalconFPR(-0.922701128333878570437264227), new FalconFPR(0.385516053843918864075607949), - new FalconFPR(0.709272826438865651316533772), new FalconFPR(0.704934080375904908852523758), - new FalconFPR(-0.704934080375904908852523758), new FalconFPR(0.709272826438865651316533772), - new FalconFPR(0.003067956762965976270145365), new FalconFPR(0.999995293809576171511580126), - new FalconFPR(-0.999995293809576171511580126), new FalconFPR(0.003067956762965976270145365) + fpr_gm_tab = new double[]{ + 0, 0, /* unused */ + -0.000000000000000000000000000, 1.000000000000000000000000000, + 0.707106781186547524400844362, 0.707106781186547524400844362, + -0.707106781186547524400844362, 0.707106781186547524400844362, + 0.923879532511286756128183189, 0.382683432365089771728459984, + -0.382683432365089771728459984, 0.923879532511286756128183189, + 0.382683432365089771728459984, 0.923879532511286756128183189, + -0.923879532511286756128183189, 0.382683432365089771728459984, + 0.980785280403230449126182236, 0.195090322016128267848284868, + -0.195090322016128267848284868, 0.980785280403230449126182236, + 0.555570233019602224742830814, 0.831469612302545237078788378, + -0.831469612302545237078788378, 0.555570233019602224742830814, + 0.831469612302545237078788378, 0.555570233019602224742830814, + -0.555570233019602224742830814, 0.831469612302545237078788378, + 0.195090322016128267848284868, 0.980785280403230449126182236, + -0.980785280403230449126182236, 0.195090322016128267848284868, + 0.995184726672196886244836953, 0.098017140329560601994195564, + -0.098017140329560601994195564, 0.995184726672196886244836953, + 0.634393284163645498215171613, 0.773010453362736960810906610, + -0.773010453362736960810906610, 0.634393284163645498215171613, + 0.881921264348355029712756864, 0.471396736825997648556387626, + -0.471396736825997648556387626, 0.881921264348355029712756864, + 0.290284677254462367636192376, 0.956940335732208864935797887, + -0.956940335732208864935797887, 0.290284677254462367636192376, + 0.956940335732208864935797887, 0.290284677254462367636192376, + -0.290284677254462367636192376, 0.956940335732208864935797887, + 0.471396736825997648556387626, 0.881921264348355029712756864, + -0.881921264348355029712756864, 0.471396736825997648556387626, + 0.773010453362736960810906610, 0.634393284163645498215171613, + -0.634393284163645498215171613, 0.773010453362736960810906610, + 0.098017140329560601994195564, 0.995184726672196886244836953, + -0.995184726672196886244836953, 0.098017140329560601994195564, + 0.998795456205172392714771605, 0.049067674327418014254954977, + -0.049067674327418014254954977, 0.998795456205172392714771605, + 0.671558954847018400625376850, 0.740951125354959091175616897, + -0.740951125354959091175616897, 0.671558954847018400625376850, + 0.903989293123443331586200297, 0.427555093430282094320966857, + -0.427555093430282094320966857, 0.903989293123443331586200297, + 0.336889853392220050689253213, 0.941544065183020778412509403, + -0.941544065183020778412509403, 0.336889853392220050689253213, + 0.970031253194543992603984207, 0.242980179903263889948274162, + -0.242980179903263889948274162, 0.970031253194543992603984207, + 0.514102744193221726593693839, 0.857728610000272069902269984, + -0.857728610000272069902269984, 0.514102744193221726593693839, + 0.803207531480644909806676513, 0.595699304492433343467036529, + -0.595699304492433343467036529, 0.803207531480644909806676513, + 0.146730474455361751658850130, 0.989176509964780973451673738, + -0.989176509964780973451673738, 0.146730474455361751658850130, + 0.989176509964780973451673738, 0.146730474455361751658850130, + -0.146730474455361751658850130, 0.989176509964780973451673738, + 0.595699304492433343467036529, 0.803207531480644909806676513, + -0.803207531480644909806676513, 0.595699304492433343467036529, + 0.857728610000272069902269984, 0.514102744193221726593693839, + -0.514102744193221726593693839, 0.857728610000272069902269984, + 0.242980179903263889948274162, 0.970031253194543992603984207, + -0.970031253194543992603984207, 0.242980179903263889948274162, + 0.941544065183020778412509403, 0.336889853392220050689253213, + -0.336889853392220050689253213, 0.941544065183020778412509403, + 0.427555093430282094320966857, 0.903989293123443331586200297, + -0.903989293123443331586200297, 0.427555093430282094320966857, + 0.740951125354959091175616897, 0.671558954847018400625376850, + -0.671558954847018400625376850, 0.740951125354959091175616897, + 0.049067674327418014254954977, 0.998795456205172392714771605, + -0.998795456205172392714771605, 0.049067674327418014254954977, + 0.999698818696204220115765650, 0.024541228522912288031734529, + -0.024541228522912288031734529, 0.999698818696204220115765650, + 0.689540544737066924616730630, 0.724247082951466920941069243, + -0.724247082951466920941069243, 0.689540544737066924616730630, + 0.914209755703530654635014829, 0.405241314004989870908481306, + -0.405241314004989870908481306, 0.914209755703530654635014829, + 0.359895036534988148775104572, 0.932992798834738887711660256, + -0.932992798834738887711660256, 0.359895036534988148775104572, + 0.975702130038528544460395766, 0.219101240156869797227737547, + -0.219101240156869797227737547, 0.975702130038528544460395766, + 0.534997619887097210663076905, 0.844853565249707073259571205, + -0.844853565249707073259571205, 0.534997619887097210663076905, + 0.817584813151583696504920884, 0.575808191417845300745972454, + -0.575808191417845300745972454, 0.817584813151583696504920884, + 0.170961888760301226363642357, 0.985277642388941244774018433, + -0.985277642388941244774018433, 0.170961888760301226363642357, + 0.992479534598709998156767252, 0.122410675199216198498704474, + -0.122410675199216198498704474, 0.992479534598709998156767252, + 0.615231590580626845484913563, 0.788346427626606262009164705, + -0.788346427626606262009164705, 0.615231590580626845484913563, + 0.870086991108711418652292404, 0.492898192229784036873026689, + -0.492898192229784036873026689, 0.870086991108711418652292404, + 0.266712757474898386325286515, 0.963776065795439866686464356, + -0.963776065795439866686464356, 0.266712757474898386325286515, + 0.949528180593036667195936074, 0.313681740398891476656478846, + -0.313681740398891476656478846, 0.949528180593036667195936074, + 0.449611329654606600046294579, 0.893224301195515320342416447, + -0.893224301195515320342416447, 0.449611329654606600046294579, + 0.757208846506484547575464054, 0.653172842953776764084203014, + -0.653172842953776764084203014, 0.757208846506484547575464054, + 0.073564563599667423529465622, 0.997290456678690216135597140, + -0.997290456678690216135597140, 0.073564563599667423529465622, + 0.997290456678690216135597140, 0.073564563599667423529465622, + -0.073564563599667423529465622, 0.997290456678690216135597140, + 0.653172842953776764084203014, 0.757208846506484547575464054, + -0.757208846506484547575464054, 0.653172842953776764084203014, + 0.893224301195515320342416447, 0.449611329654606600046294579, + -0.449611329654606600046294579, 0.893224301195515320342416447, + 0.313681740398891476656478846, 0.949528180593036667195936074, + -0.949528180593036667195936074, 0.313681740398891476656478846, + 0.963776065795439866686464356, 0.266712757474898386325286515, + -0.266712757474898386325286515, 0.963776065795439866686464356, + 0.492898192229784036873026689, 0.870086991108711418652292404, + -0.870086991108711418652292404, 0.492898192229784036873026689, + 0.788346427626606262009164705, 0.615231590580626845484913563, + -0.615231590580626845484913563, 0.788346427626606262009164705, + 0.122410675199216198498704474, 0.992479534598709998156767252, + -0.992479534598709998156767252, 0.122410675199216198498704474, + 0.985277642388941244774018433, 0.170961888760301226363642357, + -0.170961888760301226363642357, 0.985277642388941244774018433, + 0.575808191417845300745972454, 0.817584813151583696504920884, + -0.817584813151583696504920884, 0.575808191417845300745972454, + 0.844853565249707073259571205, 0.534997619887097210663076905, + -0.534997619887097210663076905, 0.844853565249707073259571205, + 0.219101240156869797227737547, 0.975702130038528544460395766, + -0.975702130038528544460395766, 0.219101240156869797227737547, + 0.932992798834738887711660256, 0.359895036534988148775104572, + -0.359895036534988148775104572, 0.932992798834738887711660256, + 0.405241314004989870908481306, 0.914209755703530654635014829, + -0.914209755703530654635014829, 0.405241314004989870908481306, + 0.724247082951466920941069243, 0.689540544737066924616730630, + -0.689540544737066924616730630, 0.724247082951466920941069243, + 0.024541228522912288031734529, 0.999698818696204220115765650, + -0.999698818696204220115765650, 0.024541228522912288031734529, + 0.999924701839144540921646491, 0.012271538285719926079408262, + -0.012271538285719926079408262, 0.999924701839144540921646491, + 0.698376249408972853554813503, 0.715730825283818654125532623, + -0.715730825283818654125532623, 0.698376249408972853554813503, + 0.919113851690057743908477789, 0.393992040061048108596188661, + -0.393992040061048108596188661, 0.919113851690057743908477789, + 0.371317193951837543411934967, 0.928506080473215565937167396, + -0.928506080473215565937167396, 0.371317193951837543411934967, + 0.978317370719627633106240097, 0.207111376192218549708116020, + -0.207111376192218549708116020, 0.978317370719627633106240097, + 0.545324988422046422313987347, 0.838224705554838043186996856, + -0.838224705554838043186996856, 0.545324988422046422313987347, + 0.824589302785025264474803737, 0.565731810783613197389765011, + -0.565731810783613197389765011, 0.824589302785025264474803737, + 0.183039887955140958516532578, 0.983105487431216327180301155, + -0.983105487431216327180301155, 0.183039887955140958516532578, + 0.993906970002356041546922813, 0.110222207293883058807899140, + -0.110222207293883058807899140, 0.993906970002356041546922813, + 0.624859488142386377084072816, 0.780737228572094478301588484, + -0.780737228572094478301588484, 0.624859488142386377084072816, + 0.876070094195406607095844268, 0.482183772079122748517344481, + -0.482183772079122748517344481, 0.876070094195406607095844268, + 0.278519689385053105207848526, 0.960430519415565811199035138, + -0.960430519415565811199035138, 0.278519689385053105207848526, + 0.953306040354193836916740383, 0.302005949319228067003463232, + -0.302005949319228067003463232, 0.953306040354193836916740383, + 0.460538710958240023633181487, 0.887639620402853947760181617, + -0.887639620402853947760181617, 0.460538710958240023633181487, + 0.765167265622458925888815999, 0.643831542889791465068086063, + -0.643831542889791465068086063, 0.765167265622458925888815999, + 0.085797312344439890461556332, 0.996312612182778012627226190, + -0.996312612182778012627226190, 0.085797312344439890461556332, + 0.998118112900149207125155861, 0.061320736302208577782614593, + -0.061320736302208577782614593, 0.998118112900149207125155861, + 0.662415777590171761113069817, 0.749136394523459325469203257, + -0.749136394523459325469203257, 0.662415777590171761113069817, + 0.898674465693953843041976744, 0.438616238538527637647025738, + -0.438616238538527637647025738, 0.898674465693953843041976744, + 0.325310292162262934135954708, 0.945607325380521325730945387, + -0.945607325380521325730945387, 0.325310292162262934135954708, + 0.966976471044852109087220226, 0.254865659604514571553980779, + -0.254865659604514571553980779, 0.966976471044852109087220226, + 0.503538383725717558691867071, 0.863972856121586737918147054, + -0.863972856121586737918147054, 0.503538383725717558691867071, + 0.795836904608883536262791915, 0.605511041404325513920626941, + -0.605511041404325513920626941, 0.795836904608883536262791915, + 0.134580708507126186316358409, 0.990902635427780025108237011, + -0.990902635427780025108237011, 0.134580708507126186316358409, + 0.987301418157858382399815802, 0.158858143333861441684385360, + -0.158858143333861441684385360, 0.987301418157858382399815802, + 0.585797857456438860328080838, 0.810457198252594791726703434, + -0.810457198252594791726703434, 0.585797857456438860328080838, + 0.851355193105265142261290312, 0.524589682678468906215098464, + -0.524589682678468906215098464, 0.851355193105265142261290312, + 0.231058108280671119643236018, 0.972939952205560145467720114, + -0.972939952205560145467720114, 0.231058108280671119643236018, + 0.937339011912574923201899593, 0.348418680249434568419308588, + -0.348418680249434568419308588, 0.937339011912574923201899593, + 0.416429560097637182562598911, 0.909167983090522376563884788, + -0.909167983090522376563884788, 0.416429560097637182562598911, + 0.732654271672412834615546649, 0.680600997795453050594430464, + -0.680600997795453050594430464, 0.732654271672412834615546649, + 0.036807222941358832324332691, 0.999322384588349500896221011, + -0.999322384588349500896221011, 0.036807222941358832324332691, + 0.999322384588349500896221011, 0.036807222941358832324332691, + -0.036807222941358832324332691, 0.999322384588349500896221011, + 0.680600997795453050594430464, 0.732654271672412834615546649, + -0.732654271672412834615546649, 0.680600997795453050594430464, + 0.909167983090522376563884788, 0.416429560097637182562598911, + -0.416429560097637182562598911, 0.909167983090522376563884788, + 0.348418680249434568419308588, 0.937339011912574923201899593, + -0.937339011912574923201899593, 0.348418680249434568419308588, + 0.972939952205560145467720114, 0.231058108280671119643236018, + -0.231058108280671119643236018, 0.972939952205560145467720114, + 0.524589682678468906215098464, 0.851355193105265142261290312, + -0.851355193105265142261290312, 0.524589682678468906215098464, + 0.810457198252594791726703434, 0.585797857456438860328080838, + -0.585797857456438860328080838, 0.810457198252594791726703434, + 0.158858143333861441684385360, 0.987301418157858382399815802, + -0.987301418157858382399815802, 0.158858143333861441684385360, + 0.990902635427780025108237011, 0.134580708507126186316358409, + -0.134580708507126186316358409, 0.990902635427780025108237011, + 0.605511041404325513920626941, 0.795836904608883536262791915, + -0.795836904608883536262791915, 0.605511041404325513920626941, + 0.863972856121586737918147054, 0.503538383725717558691867071, + -0.503538383725717558691867071, 0.863972856121586737918147054, + 0.254865659604514571553980779, 0.966976471044852109087220226, + -0.966976471044852109087220226, 0.254865659604514571553980779, + 0.945607325380521325730945387, 0.325310292162262934135954708, + -0.325310292162262934135954708, 0.945607325380521325730945387, + 0.438616238538527637647025738, 0.898674465693953843041976744, + -0.898674465693953843041976744, 0.438616238538527637647025738, + 0.749136394523459325469203257, 0.662415777590171761113069817, + -0.662415777590171761113069817, 0.749136394523459325469203257, + 0.061320736302208577782614593, 0.998118112900149207125155861, + -0.998118112900149207125155861, 0.061320736302208577782614593, + 0.996312612182778012627226190, 0.085797312344439890461556332, + -0.085797312344439890461556332, 0.996312612182778012627226190, + 0.643831542889791465068086063, 0.765167265622458925888815999, + -0.765167265622458925888815999, 0.643831542889791465068086063, + 0.887639620402853947760181617, 0.460538710958240023633181487, + -0.460538710958240023633181487, 0.887639620402853947760181617, + 0.302005949319228067003463232, 0.953306040354193836916740383, + -0.953306040354193836916740383, 0.302005949319228067003463232, + 0.960430519415565811199035138, 0.278519689385053105207848526, + -0.278519689385053105207848526, 0.960430519415565811199035138, + 0.482183772079122748517344481, 0.876070094195406607095844268, + -0.876070094195406607095844268, 0.482183772079122748517344481, + 0.780737228572094478301588484, 0.624859488142386377084072816, + -0.624859488142386377084072816, 0.780737228572094478301588484, + 0.110222207293883058807899140, 0.993906970002356041546922813, + -0.993906970002356041546922813, 0.110222207293883058807899140, + 0.983105487431216327180301155, 0.183039887955140958516532578, + -0.183039887955140958516532578, 0.983105487431216327180301155, + 0.565731810783613197389765011, 0.824589302785025264474803737, + -0.824589302785025264474803737, 0.565731810783613197389765011, + 0.838224705554838043186996856, 0.545324988422046422313987347, + -0.545324988422046422313987347, 0.838224705554838043186996856, + 0.207111376192218549708116020, 0.978317370719627633106240097, + -0.978317370719627633106240097, 0.207111376192218549708116020, + 0.928506080473215565937167396, 0.371317193951837543411934967, + -0.371317193951837543411934967, 0.928506080473215565937167396, + 0.393992040061048108596188661, 0.919113851690057743908477789, + -0.919113851690057743908477789, 0.393992040061048108596188661, + 0.715730825283818654125532623, 0.698376249408972853554813503, + -0.698376249408972853554813503, 0.715730825283818654125532623, + 0.012271538285719926079408262, 0.999924701839144540921646491, + -0.999924701839144540921646491, 0.012271538285719926079408262, + 0.999981175282601142656990438, 0.006135884649154475359640235, + -0.006135884649154475359640235, 0.999981175282601142656990438, + 0.702754744457225302452914421, 0.711432195745216441522130290, + -0.711432195745216441522130290, 0.702754744457225302452914421, + 0.921514039342041943465396332, 0.388345046698826291624993541, + -0.388345046698826291624993541, 0.921514039342041943465396332, + 0.377007410216418256726567823, 0.926210242138311341974793388, + -0.926210242138311341974793388, 0.377007410216418256726567823, + 0.979569765685440534439326110, 0.201104634842091911558443546, + -0.201104634842091911558443546, 0.979569765685440534439326110, + 0.550457972936604802977289893, 0.834862874986380056304401383, + -0.834862874986380056304401383, 0.550457972936604802977289893, + 0.828045045257755752067527592, 0.560661576197336023839710223, + -0.560661576197336023839710223, 0.828045045257755752067527592, + 0.189068664149806212754997837, 0.981963869109555264072848154, + -0.981963869109555264072848154, 0.189068664149806212754997837, + 0.994564570734255452119106243, 0.104121633872054579120943880, + -0.104121633872054579120943880, 0.994564570734255452119106243, + 0.629638238914927025372981341, 0.776888465673232450040827983, + -0.776888465673232450040827983, 0.629638238914927025372981341, + 0.879012226428633477831323711, 0.476799230063322133342158117, + -0.476799230063322133342158117, 0.879012226428633477831323711, + 0.284407537211271843618310615, 0.958703474895871555374645792, + -0.958703474895871555374645792, 0.284407537211271843618310615, + 0.955141168305770721498157712, 0.296150888243623824121786128, + -0.296150888243623824121786128, 0.955141168305770721498157712, + 0.465976495767966177902756065, 0.884797098430937780104007041, + -0.884797098430937780104007041, 0.465976495767966177902756065, + 0.769103337645579639346626069, 0.639124444863775743801488193, + -0.639124444863775743801488193, 0.769103337645579639346626069, + 0.091908956497132728624990979, 0.995767414467659793982495643, + -0.995767414467659793982495643, 0.091908956497132728624990979, + 0.998475580573294752208559038, 0.055195244349689939809447526, + -0.055195244349689939809447526, 0.998475580573294752208559038, + 0.666999922303637506650154222, 0.745057785441465962407907310, + -0.745057785441465962407907310, 0.666999922303637506650154222, + 0.901348847046022014570746093, 0.433093818853151968484222638, + -0.433093818853151968484222638, 0.901348847046022014570746093, + 0.331106305759876401737190737, 0.943593458161960361495301445, + -0.943593458161960361495301445, 0.331106305759876401737190737, + 0.968522094274417316221088329, 0.248927605745720168110682816, + -0.248927605745720168110682816, 0.968522094274417316221088329, + 0.508830142543107036931749324, 0.860866938637767279344583877, + -0.860866938637767279344583877, 0.508830142543107036931749324, + 0.799537269107905033500246232, 0.600616479383868926653875896, + -0.600616479383868926653875896, 0.799537269107905033500246232, + 0.140658239332849230714788846, 0.990058210262297105505906464, + -0.990058210262297105505906464, 0.140658239332849230714788846, + 0.988257567730749491404792538, 0.152797185258443427720336613, + -0.152797185258443427720336613, 0.988257567730749491404792538, + 0.590759701858874228423887908, 0.806847553543799272206514313, + -0.806847553543799272206514313, 0.590759701858874228423887908, + 0.854557988365400520767862276, 0.519355990165589587361829932, + -0.519355990165589587361829932, 0.854557988365400520767862276, + 0.237023605994367206867735915, 0.971503890986251775537099622, + -0.971503890986251775537099622, 0.237023605994367206867735915, + 0.939459223602189911962669246, 0.342660717311994397592781983, + -0.342660717311994397592781983, 0.939459223602189911962669246, + 0.422000270799799685941287941, 0.906595704514915365332960588, + -0.906595704514915365332960588, 0.422000270799799685941287941, + 0.736816568877369875090132520, 0.676092703575315960360419228, + -0.676092703575315960360419228, 0.736816568877369875090132520, + 0.042938256934940823077124540, 0.999077727752645382888781997, + -0.999077727752645382888781997, 0.042938256934940823077124540, + 0.999529417501093163079703322, 0.030674803176636625934021028, + -0.030674803176636625934021028, 0.999529417501093163079703322, + 0.685083667772700381362052545, 0.728464390448225196492035438, + -0.728464390448225196492035438, 0.685083667772700381362052545, + 0.911706032005429851404397325, 0.410843171057903942183466675, + -0.410843171057903942183466675, 0.911706032005429851404397325, + 0.354163525420490382357395796, 0.935183509938947577642207480, + -0.935183509938947577642207480, 0.354163525420490382357395796, + 0.974339382785575860518721668, 0.225083911359792835991642120, + -0.225083911359792835991642120, 0.974339382785575860518721668, + 0.529803624686294668216054671, 0.848120344803297251279133563, + -0.848120344803297251279133563, 0.529803624686294668216054671, + 0.814036329705948361654516690, 0.580813958095764545075595272, + -0.580813958095764545075595272, 0.814036329705948361654516690, + 0.164913120489969921418189113, 0.986308097244598647863297524, + -0.986308097244598647863297524, 0.164913120489969921418189113, + 0.991709753669099522860049931, 0.128498110793793172624415589, + -0.128498110793793172624415589, 0.991709753669099522860049931, + 0.610382806276309452716352152, 0.792106577300212351782342879, + -0.792106577300212351782342879, 0.610382806276309452716352152, + 0.867046245515692651480195629, 0.498227666972781852410983869, + -0.498227666972781852410983869, 0.867046245515692651480195629, + 0.260794117915275518280186509, 0.965394441697689374550843858, + -0.965394441697689374550843858, 0.260794117915275518280186509, + 0.947585591017741134653387321, 0.319502030816015677901518272, + -0.319502030816015677901518272, 0.947585591017741134653387321, + 0.444122144570429231642069418, 0.895966249756185155914560282, + -0.895966249756185155914560282, 0.444122144570429231642069418, + 0.753186799043612482483430486, 0.657806693297078656931182264, + -0.657806693297078656931182264, 0.753186799043612482483430486, + 0.067443919563664057897972422, 0.997723066644191609848546728, + -0.997723066644191609848546728, 0.067443919563664057897972422, + 0.996820299291165714972629398, 0.079682437971430121147120656, + -0.079682437971430121147120656, 0.996820299291165714972629398, + 0.648514401022112445084560551, 0.761202385484261814029709836, + -0.761202385484261814029709836, 0.648514401022112445084560551, + 0.890448723244757889952150560, 0.455083587126343823535869268, + -0.455083587126343823535869268, 0.890448723244757889952150560, + 0.307849640041534893682063646, 0.951435020969008369549175569, + -0.951435020969008369549175569, 0.307849640041534893682063646, + 0.962121404269041595429604316, 0.272621355449948984493347477, + -0.272621355449948984493347477, 0.962121404269041595429604316, + 0.487550160148435954641485027, 0.873094978418290098636085973, + -0.873094978418290098636085973, 0.487550160148435954641485027, + 0.784556597155575233023892575, 0.620057211763289178646268191, + -0.620057211763289178646268191, 0.784556597155575233023892575, + 0.116318630911904767252544319, 0.993211949234794533104601012, + -0.993211949234794533104601012, 0.116318630911904767252544319, + 0.984210092386929073193874387, 0.177004220412148756196839844, + -0.177004220412148756196839844, 0.984210092386929073193874387, + 0.570780745886967280232652864, 0.821102514991104679060430820, + -0.821102514991104679060430820, 0.570780745886967280232652864, + 0.841554977436898409603499520, 0.540171472729892881297845480, + -0.540171472729892881297845480, 0.841554977436898409603499520, + 0.213110319916091373967757518, 0.977028142657754351485866211, + -0.977028142657754351485866211, 0.213110319916091373967757518, + 0.930766961078983731944872340, 0.365612997804773870011745909, + -0.365612997804773870011745909, 0.930766961078983731944872340, + 0.399624199845646828544117031, 0.916679059921042663116457013, + -0.916679059921042663116457013, 0.399624199845646828544117031, + 0.720002507961381629076682999, 0.693971460889654009003734389, + -0.693971460889654009003734389, 0.720002507961381629076682999, + 0.018406729905804820927366313, 0.999830581795823422015722275, + -0.999830581795823422015722275, 0.018406729905804820927366313, + 0.999830581795823422015722275, 0.018406729905804820927366313, + -0.018406729905804820927366313, 0.999830581795823422015722275, + 0.693971460889654009003734389, 0.720002507961381629076682999, + -0.720002507961381629076682999, 0.693971460889654009003734389, + 0.916679059921042663116457013, 0.399624199845646828544117031, + -0.399624199845646828544117031, 0.916679059921042663116457013, + 0.365612997804773870011745909, 0.930766961078983731944872340, + -0.930766961078983731944872340, 0.365612997804773870011745909, + 0.977028142657754351485866211, 0.213110319916091373967757518, + -0.213110319916091373967757518, 0.977028142657754351485866211, + 0.540171472729892881297845480, 0.841554977436898409603499520, + -0.841554977436898409603499520, 0.540171472729892881297845480, + 0.821102514991104679060430820, 0.570780745886967280232652864, + -0.570780745886967280232652864, 0.821102514991104679060430820, + 0.177004220412148756196839844, 0.984210092386929073193874387, + -0.984210092386929073193874387, 0.177004220412148756196839844, + 0.993211949234794533104601012, 0.116318630911904767252544319, + -0.116318630911904767252544319, 0.993211949234794533104601012, + 0.620057211763289178646268191, 0.784556597155575233023892575, + -0.784556597155575233023892575, 0.620057211763289178646268191, + 0.873094978418290098636085973, 0.487550160148435954641485027, + -0.487550160148435954641485027, 0.873094978418290098636085973, + 0.272621355449948984493347477, 0.962121404269041595429604316, + -0.962121404269041595429604316, 0.272621355449948984493347477, + 0.951435020969008369549175569, 0.307849640041534893682063646, + -0.307849640041534893682063646, 0.951435020969008369549175569, + 0.455083587126343823535869268, 0.890448723244757889952150560, + -0.890448723244757889952150560, 0.455083587126343823535869268, + 0.761202385484261814029709836, 0.648514401022112445084560551, + -0.648514401022112445084560551, 0.761202385484261814029709836, + 0.079682437971430121147120656, 0.996820299291165714972629398, + -0.996820299291165714972629398, 0.079682437971430121147120656, + 0.997723066644191609848546728, 0.067443919563664057897972422, + -0.067443919563664057897972422, 0.997723066644191609848546728, + 0.657806693297078656931182264, 0.753186799043612482483430486, + -0.753186799043612482483430486, 0.657806693297078656931182264, + 0.895966249756185155914560282, 0.444122144570429231642069418, + -0.444122144570429231642069418, 0.895966249756185155914560282, + 0.319502030816015677901518272, 0.947585591017741134653387321, + -0.947585591017741134653387321, 0.319502030816015677901518272, + 0.965394441697689374550843858, 0.260794117915275518280186509, + -0.260794117915275518280186509, 0.965394441697689374550843858, + 0.498227666972781852410983869, 0.867046245515692651480195629, + -0.867046245515692651480195629, 0.498227666972781852410983869, + 0.792106577300212351782342879, 0.610382806276309452716352152, + -0.610382806276309452716352152, 0.792106577300212351782342879, + 0.128498110793793172624415589, 0.991709753669099522860049931, + -0.991709753669099522860049931, 0.128498110793793172624415589, + 0.986308097244598647863297524, 0.164913120489969921418189113, + -0.164913120489969921418189113, 0.986308097244598647863297524, + 0.580813958095764545075595272, 0.814036329705948361654516690, + -0.814036329705948361654516690, 0.580813958095764545075595272, + 0.848120344803297251279133563, 0.529803624686294668216054671, + -0.529803624686294668216054671, 0.848120344803297251279133563, + 0.225083911359792835991642120, 0.974339382785575860518721668, + -0.974339382785575860518721668, 0.225083911359792835991642120, + 0.935183509938947577642207480, 0.354163525420490382357395796, + -0.354163525420490382357395796, 0.935183509938947577642207480, + 0.410843171057903942183466675, 0.911706032005429851404397325, + -0.911706032005429851404397325, 0.410843171057903942183466675, + 0.728464390448225196492035438, 0.685083667772700381362052545, + -0.685083667772700381362052545, 0.728464390448225196492035438, + 0.030674803176636625934021028, 0.999529417501093163079703322, + -0.999529417501093163079703322, 0.030674803176636625934021028, + 0.999077727752645382888781997, 0.042938256934940823077124540, + -0.042938256934940823077124540, 0.999077727752645382888781997, + 0.676092703575315960360419228, 0.736816568877369875090132520, + -0.736816568877369875090132520, 0.676092703575315960360419228, + 0.906595704514915365332960588, 0.422000270799799685941287941, + -0.422000270799799685941287941, 0.906595704514915365332960588, + 0.342660717311994397592781983, 0.939459223602189911962669246, + -0.939459223602189911962669246, 0.342660717311994397592781983, + 0.971503890986251775537099622, 0.237023605994367206867735915, + -0.237023605994367206867735915, 0.971503890986251775537099622, + 0.519355990165589587361829932, 0.854557988365400520767862276, + -0.854557988365400520767862276, 0.519355990165589587361829932, + 0.806847553543799272206514313, 0.590759701858874228423887908, + -0.590759701858874228423887908, 0.806847553543799272206514313, + 0.152797185258443427720336613, 0.988257567730749491404792538, + -0.988257567730749491404792538, 0.152797185258443427720336613, + 0.990058210262297105505906464, 0.140658239332849230714788846, + -0.140658239332849230714788846, 0.990058210262297105505906464, + 0.600616479383868926653875896, 0.799537269107905033500246232, + -0.799537269107905033500246232, 0.600616479383868926653875896, + 0.860866938637767279344583877, 0.508830142543107036931749324, + -0.508830142543107036931749324, 0.860866938637767279344583877, + 0.248927605745720168110682816, 0.968522094274417316221088329, + -0.968522094274417316221088329, 0.248927605745720168110682816, + 0.943593458161960361495301445, 0.331106305759876401737190737, + -0.331106305759876401737190737, 0.943593458161960361495301445, + 0.433093818853151968484222638, 0.901348847046022014570746093, + -0.901348847046022014570746093, 0.433093818853151968484222638, + 0.745057785441465962407907310, 0.666999922303637506650154222, + -0.666999922303637506650154222, 0.745057785441465962407907310, + 0.055195244349689939809447526, 0.998475580573294752208559038, + -0.998475580573294752208559038, 0.055195244349689939809447526, + 0.995767414467659793982495643, 0.091908956497132728624990979, + -0.091908956497132728624990979, 0.995767414467659793982495643, + 0.639124444863775743801488193, 0.769103337645579639346626069, + -0.769103337645579639346626069, 0.639124444863775743801488193, + 0.884797098430937780104007041, 0.465976495767966177902756065, + -0.465976495767966177902756065, 0.884797098430937780104007041, + 0.296150888243623824121786128, 0.955141168305770721498157712, + -0.955141168305770721498157712, 0.296150888243623824121786128, + 0.958703474895871555374645792, 0.284407537211271843618310615, + -0.284407537211271843618310615, 0.958703474895871555374645792, + 0.476799230063322133342158117, 0.879012226428633477831323711, + -0.879012226428633477831323711, 0.476799230063322133342158117, + 0.776888465673232450040827983, 0.629638238914927025372981341, + -0.629638238914927025372981341, 0.776888465673232450040827983, + 0.104121633872054579120943880, 0.994564570734255452119106243, + -0.994564570734255452119106243, 0.104121633872054579120943880, + 0.981963869109555264072848154, 0.189068664149806212754997837, + -0.189068664149806212754997837, 0.981963869109555264072848154, + 0.560661576197336023839710223, 0.828045045257755752067527592, + -0.828045045257755752067527592, 0.560661576197336023839710223, + 0.834862874986380056304401383, 0.550457972936604802977289893, + -0.550457972936604802977289893, 0.834862874986380056304401383, + 0.201104634842091911558443546, 0.979569765685440534439326110, + -0.979569765685440534439326110, 0.201104634842091911558443546, + 0.926210242138311341974793388, 0.377007410216418256726567823, + -0.377007410216418256726567823, 0.926210242138311341974793388, + 0.388345046698826291624993541, 0.921514039342041943465396332, + -0.921514039342041943465396332, 0.388345046698826291624993541, + 0.711432195745216441522130290, 0.702754744457225302452914421, + -0.702754744457225302452914421, 0.711432195745216441522130290, + 0.006135884649154475359640235, 0.999981175282601142656990438, + -0.999981175282601142656990438, 0.006135884649154475359640235, + 0.999995293809576171511580126, 0.003067956762965976270145365, + -0.003067956762965976270145365, 0.999995293809576171511580126, + 0.704934080375904908852523758, 0.709272826438865651316533772, + -0.709272826438865651316533772, 0.704934080375904908852523758, + 0.922701128333878570437264227, 0.385516053843918864075607949, + -0.385516053843918864075607949, 0.922701128333878570437264227, + 0.379847208924051170576281147, 0.925049240782677590302371869, + -0.925049240782677590302371869, 0.379847208924051170576281147, + 0.980182135968117392690210009, 0.198098410717953586179324918, + -0.198098410717953586179324918, 0.980182135968117392690210009, + 0.553016705580027531764226988, 0.833170164701913186439915922, + -0.833170164701913186439915922, 0.553016705580027531764226988, + 0.829761233794523042469023765, 0.558118531220556115693702964, + -0.558118531220556115693702964, 0.829761233794523042469023765, + 0.192080397049892441679288205, 0.981379193313754574318224190, + -0.981379193313754574318224190, 0.192080397049892441679288205, + 0.994879330794805620591166107, 0.101069862754827824987887585, + -0.101069862754827824987887585, 0.994879330794805620591166107, + 0.632018735939809021909403706, 0.774953106594873878359129282, + -0.774953106594873878359129282, 0.632018735939809021909403706, + 0.880470889052160770806542929, 0.474100214650550014398580015, + -0.474100214650550014398580015, 0.880470889052160770806542929, + 0.287347459544729526477331841, 0.957826413027532890321037029, + -0.957826413027532890321037029, 0.287347459544729526477331841, + 0.956045251349996443270479823, 0.293219162694258650606608599, + -0.293219162694258650606608599, 0.956045251349996443270479823, + 0.468688822035827933697617870, 0.883363338665731594736308015, + -0.883363338665731594736308015, 0.468688822035827933697617870, + 0.771060524261813773200605759, 0.636761861236284230413943435, + -0.636761861236284230413943435, 0.771060524261813773200605759, + 0.094963495329638998938034312, 0.995480755491926941769171600, + -0.995480755491926941769171600, 0.094963495329638998938034312, + 0.998640218180265222418199049, 0.052131704680283321236358216, + -0.052131704680283321236358216, 0.998640218180265222418199049, + 0.669282588346636065720696366, 0.743007952135121693517362293, + -0.743007952135121693517362293, 0.669282588346636065720696366, + 0.902673318237258806751502391, 0.430326481340082633908199031, + -0.430326481340082633908199031, 0.902673318237258806751502391, + 0.333999651442009404650865481, 0.942573197601446879280758735, + -0.942573197601446879280758735, 0.333999651442009404650865481, + 0.969281235356548486048290738, 0.245955050335794611599924709, + -0.245955050335794611599924709, 0.969281235356548486048290738, + 0.511468850437970399504391001, 0.859301818357008404783582139, + -0.859301818357008404783582139, 0.511468850437970399504391001, + 0.801376171723140219430247777, 0.598160706996342311724958652, + -0.598160706996342311724958652, 0.801376171723140219430247777, + 0.143695033150294454819773349, 0.989622017463200834623694454, + -0.989622017463200834623694454, 0.143695033150294454819773349, + 0.988721691960323767604516485, 0.149764534677321517229695737, + -0.149764534677321517229695737, 0.988721691960323767604516485, + 0.593232295039799808047809426, 0.805031331142963597922659282, + -0.805031331142963597922659282, 0.593232295039799808047809426, + 0.856147328375194481019630732, 0.516731799017649881508753876, + -0.516731799017649881508753876, 0.856147328375194481019630732, + 0.240003022448741486568922365, 0.970772140728950302138169611, + -0.970772140728950302138169611, 0.240003022448741486568922365, + 0.940506070593268323787291309, 0.339776884406826857828825803, + -0.339776884406826857828825803, 0.940506070593268323787291309, + 0.424779681209108833357226189, 0.905296759318118774354048329, + -0.905296759318118774354048329, 0.424779681209108833357226189, + 0.738887324460615147933116508, 0.673829000378756060917568372, + -0.673829000378756060917568372, 0.738887324460615147933116508, + 0.046003182130914628814301788, 0.998941293186856850633930266, + -0.998941293186856850633930266, 0.046003182130914628814301788, + 0.999618822495178597116830637, 0.027608145778965741612354872, + -0.027608145778965741612354872, 0.999618822495178597116830637, + 0.687315340891759108199186948, 0.726359155084345976817494315, + -0.726359155084345976817494315, 0.687315340891759108199186948, + 0.912962190428398164628018233, 0.408044162864978680820747499, + -0.408044162864978680820747499, 0.912962190428398164628018233, + 0.357030961233430032614954036, 0.934092550404258914729877883, + -0.934092550404258914729877883, 0.357030961233430032614954036, + 0.975025345066994146844913468, 0.222093620973203534094094721, + -0.222093620973203534094094721, 0.975025345066994146844913468, + 0.532403127877197971442805218, 0.846490938774052078300544488, + -0.846490938774052078300544488, 0.532403127877197971442805218, + 0.815814410806733789010772660, 0.578313796411655563342245019, + -0.578313796411655563342245019, 0.815814410806733789010772660, + 0.167938294974731178054745536, 0.985797509167567424700995000, + -0.985797509167567424700995000, 0.167938294974731178054745536, + 0.992099313142191757112085445, 0.125454983411546238542336453, + -0.125454983411546238542336453, 0.992099313142191757112085445, + 0.612810082429409703935211936, 0.790230221437310055030217152, + -0.790230221437310055030217152, 0.612810082429409703935211936, + 0.868570705971340895340449876, 0.495565261825772531150266670, + -0.495565261825772531150266670, 0.868570705971340895340449876, + 0.263754678974831383611349322, 0.964589793289812723836432159, + -0.964589793289812723836432159, 0.263754678974831383611349322, + 0.948561349915730288158494826, 0.316593375556165867243047035, + -0.316593375556165867243047035, 0.948561349915730288158494826, + 0.446868840162374195353044389, 0.894599485631382678433072126, + -0.894599485631382678433072126, 0.446868840162374195353044389, + 0.755201376896536527598710756, 0.655492852999615385312679701, + -0.655492852999615385312679701, 0.755201376896536527598710756, + 0.070504573389613863027351471, 0.997511456140303459699448390, + -0.997511456140303459699448390, 0.070504573389613863027351471, + 0.997060070339482978987989949, 0.076623861392031492278332463, + -0.076623861392031492278332463, 0.997060070339482978987989949, + 0.650846684996380915068975573, 0.759209188978388033485525443, + -0.759209188978388033485525443, 0.650846684996380915068975573, + 0.891840709392342727796478697, 0.452349587233770874133026703, + -0.452349587233770874133026703, 0.891840709392342727796478697, + 0.310767152749611495835997250, 0.950486073949481721759926101, + -0.950486073949481721759926101, 0.310767152749611495835997250, + 0.962953266873683886347921481, 0.269668325572915106525464462, + -0.269668325572915106525464462, 0.962953266873683886347921481, + 0.490226483288291154229598449, 0.871595086655951034842481435, + -0.871595086655951034842481435, 0.490226483288291154229598449, + 0.786455213599085757522319464, 0.617647307937803932403979402, + -0.617647307937803932403979402, 0.786455213599085757522319464, + 0.119365214810991364593637790, 0.992850414459865090793563344, + -0.992850414459865090793563344, 0.119365214810991364593637790, + 0.984748501801904218556553176, 0.173983873387463827950700807, + -0.173983873387463827950700807, 0.984748501801904218556553176, + 0.573297166698042212820171239, 0.819347520076796960824689637, + -0.819347520076796960824689637, 0.573297166698042212820171239, + 0.843208239641845437161743865, 0.537587076295645482502214932, + -0.537587076295645482502214932, 0.843208239641845437161743865, + 0.216106797076219509948385131, 0.976369731330021149312732194, + -0.976369731330021149312732194, 0.216106797076219509948385131, + 0.931884265581668106718557199, 0.362755724367397216204854462, + -0.362755724367397216204854462, 0.931884265581668106718557199, + 0.402434650859418441082533934, 0.915448716088267819566431292, + -0.915448716088267819566431292, 0.402434650859418441082533934, + 0.722128193929215321243607198, 0.691759258364157774906734132, + -0.691759258364157774906734132, 0.722128193929215321243607198, + 0.021474080275469507418374898, 0.999769405351215321657617036, + -0.999769405351215321657617036, 0.021474080275469507418374898, + 0.999882347454212525633049627, 0.015339206284988101044151868, + -0.015339206284988101044151868, 0.999882347454212525633049627, + 0.696177131491462944788582591, 0.717870045055731736211325329, + -0.717870045055731736211325329, 0.696177131491462944788582591, + 0.917900775621390457642276297, 0.396809987416710328595290911, + -0.396809987416710328595290911, 0.917900775621390457642276297, + 0.368466829953372331712746222, 0.929640895843181265457918066, + -0.929640895843181265457918066, 0.368466829953372331712746222, + 0.977677357824509979943404762, 0.210111836880469621717489972, + -0.210111836880469621717489972, 0.977677357824509979943404762, + 0.542750784864515906586768661, 0.839893794195999504583383987, + -0.839893794195999504583383987, 0.542750784864515906586768661, + 0.822849781375826332046780034, 0.568258952670131549790548489, + -0.568258952670131549790548489, 0.822849781375826332046780034, + 0.180022901405699522679906590, 0.983662419211730274396237776, + -0.983662419211730274396237776, 0.180022901405699522679906590, + 0.993564135520595333782021697, 0.113270952177564349018228733, + -0.113270952177564349018228733, 0.993564135520595333782021697, + 0.622461279374149972519166721, 0.782650596166575738458949301, + -0.782650596166575738458949301, 0.622461279374149972519166721, + 0.874586652278176112634431897, 0.484869248000791101822951699, + -0.484869248000791101822951699, 0.874586652278176112634431897, + 0.275571819310958163076425168, 0.961280485811320641748659653, + -0.961280485811320641748659653, 0.275571819310958163076425168, + 0.952375012719765858529893608, 0.304929229735402406490728633, + -0.304929229735402406490728633, 0.952375012719765858529893608, + 0.457813303598877221904961155, 0.889048355854664562540777729, + -0.889048355854664562540777729, 0.457813303598877221904961155, + 0.763188417263381271704838297, 0.646176012983316364832802220, + -0.646176012983316364832802220, 0.763188417263381271704838297, + 0.082740264549375693111987083, 0.996571145790554847093566910, + -0.996571145790554847093566910, 0.082740264549375693111987083, + 0.997925286198596012623025462, 0.064382630929857460819324537, + -0.064382630929857460819324537, 0.997925286198596012623025462, + 0.660114342067420478559490747, 0.751165131909686411205819422, + -0.751165131909686411205819422, 0.660114342067420478559490747, + 0.897324580705418281231391836, 0.441371268731716692879988968, + -0.441371268731716692879988968, 0.897324580705418281231391836, + 0.322407678801069848384807478, 0.946600913083283570044599823, + -0.946600913083283570044599823, 0.322407678801069848384807478, + 0.966190003445412555433832961, 0.257831102162159005614471295, + -0.257831102162159005614471295, 0.966190003445412555433832961, + 0.500885382611240786241285004, 0.865513624090569082825488358, + -0.865513624090569082825488358, 0.500885382611240786241285004, + 0.793975477554337164895083757, 0.607949784967773667243642671, + -0.607949784967773667243642671, 0.793975477554337164895083757, + 0.131540028702883111103387493, 0.991310859846115418957349799, + -0.991310859846115418957349799, 0.131540028702883111103387493, + 0.986809401814185476970235952, 0.161886393780111837641387995, + -0.161886393780111837641387995, 0.986809401814185476970235952, + 0.583308652937698294392830961, 0.812250586585203913049744181, + -0.812250586585203913049744181, 0.583308652937698294392830961, + 0.849741768000852489471268395, 0.527199134781901348464274575, + -0.527199134781901348464274575, 0.849741768000852489471268395, + 0.228072083170885739254457379, 0.973644249650811925318383912, + -0.973644249650811925318383912, 0.228072083170885739254457379, + 0.936265667170278246576310996, 0.351292756085567125601307623, + -0.351292756085567125601307623, 0.936265667170278246576310996, + 0.413638312238434547471944324, 0.910441292258067196934095369, + -0.910441292258067196934095369, 0.413638312238434547471944324, + 0.730562769227827561177758850, 0.682845546385248068164596123, + -0.682845546385248068164596123, 0.730562769227827561177758850, + 0.033741171851377584833716112, 0.999430604555461772019008327, + -0.999430604555461772019008327, 0.033741171851377584833716112, + 0.999204758618363895492950001, 0.039872927587739811128578738, + -0.039872927587739811128578738, 0.999204758618363895492950001, + 0.678350043129861486873655042, 0.734738878095963464563223604, + -0.734738878095963464563223604, 0.678350043129861486873655042, + 0.907886116487666212038681480, 0.419216888363223956433010020, + -0.419216888363223956433010020, 0.907886116487666212038681480, + 0.345541324963989065539191723, 0.938403534063108112192420774, + -0.938403534063108112192420774, 0.345541324963989065539191723, + 0.972226497078936305708321144, 0.234041958583543423191242045, + -0.234041958583543423191242045, 0.972226497078936305708321144, + 0.521975292937154342694258318, 0.852960604930363657746588082, + -0.852960604930363657746588082, 0.521975292937154342694258318, + 0.808656181588174991946968128, 0.588281548222645304786439813, + -0.588281548222645304786439813, 0.808656181588174991946968128, + 0.155828397654265235743101486, 0.987784141644572154230969032, + -0.987784141644572154230969032, 0.155828397654265235743101486, + 0.990485084256457037998682243, 0.137620121586486044948441663, + -0.137620121586486044948441663, 0.990485084256457037998682243, + 0.603066598540348201693430617, 0.797690840943391108362662755, + -0.797690840943391108362662755, 0.603066598540348201693430617, + 0.862423956111040538690933878, 0.506186645345155291048942344, + -0.506186645345155291048942344, 0.862423956111040538690933878, + 0.251897818154216950498106628, 0.967753837093475465243391912, + -0.967753837093475465243391912, 0.251897818154216950498106628, + 0.944604837261480265659265493, 0.328209843579092526107916817, + -0.328209843579092526107916817, 0.944604837261480265659265493, + 0.435857079922255491032544080, 0.900015892016160228714535267, + -0.900015892016160228714535267, 0.435857079922255491032544080, + 0.747100605980180144323078847, 0.664710978203344868130324985, + -0.664710978203344868130324985, 0.747100605980180144323078847, + 0.058258264500435759613979782, 0.998301544933892840738782163, + -0.998301544933892840738782163, 0.058258264500435759613979782, + 0.996044700901251989887944810, 0.088853552582524596561586535, + -0.088853552582524596561586535, 0.996044700901251989887944810, + 0.641481012808583151988739898, 0.767138911935820381181694573, + -0.767138911935820381181694573, 0.641481012808583151988739898, + 0.886222530148880631647990821, 0.463259783551860197390719637, + -0.463259783551860197390719637, 0.886222530148880631647990821, + 0.299079826308040476750336973, 0.954228095109105629780430732, + -0.954228095109105629780430732, 0.299079826308040476750336973, + 0.959571513081984528335528181, 0.281464937925757984095231007, + -0.281464937925757984095231007, 0.959571513081984528335528181, + 0.479493757660153026679839798, 0.877545290207261291668470750, + -0.877545290207261291668470750, 0.479493757660153026679839798, + 0.778816512381475953374724325, 0.627251815495144113509622565, + -0.627251815495144113509622565, 0.778816512381475953374724325, + 0.107172424956808849175529148, 0.994240449453187946358413442, + -0.994240449453187946358413442, 0.107172424956808849175529148, + 0.982539302287441255907040396, 0.186055151663446648105438304, + -0.186055151663446648105438304, 0.982539302287441255907040396, + 0.563199344013834115007363772, 0.826321062845663480311195452, + -0.826321062845663480311195452, 0.563199344013834115007363772, + 0.836547727223511984524285790, 0.547894059173100165608820571, + -0.547894059173100165608820571, 0.836547727223511984524285790, + 0.204108966092816874181696950, 0.978948175319062194715480124, + -0.978948175319062194715480124, 0.204108966092816874181696950, + 0.927362525650401087274536959, 0.374164062971457997104393020, + -0.374164062971457997104393020, 0.927362525650401087274536959, + 0.391170384302253888687512949, 0.920318276709110566440076541, + -0.920318276709110566440076541, 0.391170384302253888687512949, + 0.713584868780793592903125099, 0.700568793943248366792866380, + -0.700568793943248366792866380, 0.713584868780793592903125099, + 0.009203754782059819315102378, 0.999957644551963866333120920, + -0.999957644551963866333120920, 0.009203754782059819315102378, + 0.999957644551963866333120920, 0.009203754782059819315102378, + -0.009203754782059819315102378, 0.999957644551963866333120920, + 0.700568793943248366792866380, 0.713584868780793592903125099, + -0.713584868780793592903125099, 0.700568793943248366792866380, + 0.920318276709110566440076541, 0.391170384302253888687512949, + -0.391170384302253888687512949, 0.920318276709110566440076541, + 0.374164062971457997104393020, 0.927362525650401087274536959, + -0.927362525650401087274536959, 0.374164062971457997104393020, + 0.978948175319062194715480124, 0.204108966092816874181696950, + -0.204108966092816874181696950, 0.978948175319062194715480124, + 0.547894059173100165608820571, 0.836547727223511984524285790, + -0.836547727223511984524285790, 0.547894059173100165608820571, + 0.826321062845663480311195452, 0.563199344013834115007363772, + -0.563199344013834115007363772, 0.826321062845663480311195452, + 0.186055151663446648105438304, 0.982539302287441255907040396, + -0.982539302287441255907040396, 0.186055151663446648105438304, + 0.994240449453187946358413442, 0.107172424956808849175529148, + -0.107172424956808849175529148, 0.994240449453187946358413442, + 0.627251815495144113509622565, 0.778816512381475953374724325, + -0.778816512381475953374724325, 0.627251815495144113509622565, + 0.877545290207261291668470750, 0.479493757660153026679839798, + -0.479493757660153026679839798, 0.877545290207261291668470750, + 0.281464937925757984095231007, 0.959571513081984528335528181, + -0.959571513081984528335528181, 0.281464937925757984095231007, + 0.954228095109105629780430732, 0.299079826308040476750336973, + -0.299079826308040476750336973, 0.954228095109105629780430732, + 0.463259783551860197390719637, 0.886222530148880631647990821, + -0.886222530148880631647990821, 0.463259783551860197390719637, + 0.767138911935820381181694573, 0.641481012808583151988739898, + -0.641481012808583151988739898, 0.767138911935820381181694573, + 0.088853552582524596561586535, 0.996044700901251989887944810, + -0.996044700901251989887944810, 0.088853552582524596561586535, + 0.998301544933892840738782163, 0.058258264500435759613979782, + -0.058258264500435759613979782, 0.998301544933892840738782163, + 0.664710978203344868130324985, 0.747100605980180144323078847, + -0.747100605980180144323078847, 0.664710978203344868130324985, + 0.900015892016160228714535267, 0.435857079922255491032544080, + -0.435857079922255491032544080, 0.900015892016160228714535267, + 0.328209843579092526107916817, 0.944604837261480265659265493, + -0.944604837261480265659265493, 0.328209843579092526107916817, + 0.967753837093475465243391912, 0.251897818154216950498106628, + -0.251897818154216950498106628, 0.967753837093475465243391912, + 0.506186645345155291048942344, 0.862423956111040538690933878, + -0.862423956111040538690933878, 0.506186645345155291048942344, + 0.797690840943391108362662755, 0.603066598540348201693430617, + -0.603066598540348201693430617, 0.797690840943391108362662755, + 0.137620121586486044948441663, 0.990485084256457037998682243, + -0.990485084256457037998682243, 0.137620121586486044948441663, + 0.987784141644572154230969032, 0.155828397654265235743101486, + -0.155828397654265235743101486, 0.987784141644572154230969032, + 0.588281548222645304786439813, 0.808656181588174991946968128, + -0.808656181588174991946968128, 0.588281548222645304786439813, + 0.852960604930363657746588082, 0.521975292937154342694258318, + -0.521975292937154342694258318, 0.852960604930363657746588082, + 0.234041958583543423191242045, 0.972226497078936305708321144, + -0.972226497078936305708321144, 0.234041958583543423191242045, + 0.938403534063108112192420774, 0.345541324963989065539191723, + -0.345541324963989065539191723, 0.938403534063108112192420774, + 0.419216888363223956433010020, 0.907886116487666212038681480, + -0.907886116487666212038681480, 0.419216888363223956433010020, + 0.734738878095963464563223604, 0.678350043129861486873655042, + -0.678350043129861486873655042, 0.734738878095963464563223604, + 0.039872927587739811128578738, 0.999204758618363895492950001, + -0.999204758618363895492950001, 0.039872927587739811128578738, + 0.999430604555461772019008327, 0.033741171851377584833716112, + -0.033741171851377584833716112, 0.999430604555461772019008327, + 0.682845546385248068164596123, 0.730562769227827561177758850, + -0.730562769227827561177758850, 0.682845546385248068164596123, + 0.910441292258067196934095369, 0.413638312238434547471944324, + -0.413638312238434547471944324, 0.910441292258067196934095369, + 0.351292756085567125601307623, 0.936265667170278246576310996, + -0.936265667170278246576310996, 0.351292756085567125601307623, + 0.973644249650811925318383912, 0.228072083170885739254457379, + -0.228072083170885739254457379, 0.973644249650811925318383912, + 0.527199134781901348464274575, 0.849741768000852489471268395, + -0.849741768000852489471268395, 0.527199134781901348464274575, + 0.812250586585203913049744181, 0.583308652937698294392830961, + -0.583308652937698294392830961, 0.812250586585203913049744181, + 0.161886393780111837641387995, 0.986809401814185476970235952, + -0.986809401814185476970235952, 0.161886393780111837641387995, + 0.991310859846115418957349799, 0.131540028702883111103387493, + -0.131540028702883111103387493, 0.991310859846115418957349799, + 0.607949784967773667243642671, 0.793975477554337164895083757, + -0.793975477554337164895083757, 0.607949784967773667243642671, + 0.865513624090569082825488358, 0.500885382611240786241285004, + -0.500885382611240786241285004, 0.865513624090569082825488358, + 0.257831102162159005614471295, 0.966190003445412555433832961, + -0.966190003445412555433832961, 0.257831102162159005614471295, + 0.946600913083283570044599823, 0.322407678801069848384807478, + -0.322407678801069848384807478, 0.946600913083283570044599823, + 0.441371268731716692879988968, 0.897324580705418281231391836, + -0.897324580705418281231391836, 0.441371268731716692879988968, + 0.751165131909686411205819422, 0.660114342067420478559490747, + -0.660114342067420478559490747, 0.751165131909686411205819422, + 0.064382630929857460819324537, 0.997925286198596012623025462, + -0.997925286198596012623025462, 0.064382630929857460819324537, + 0.996571145790554847093566910, 0.082740264549375693111987083, + -0.082740264549375693111987083, 0.996571145790554847093566910, + 0.646176012983316364832802220, 0.763188417263381271704838297, + -0.763188417263381271704838297, 0.646176012983316364832802220, + 0.889048355854664562540777729, 0.457813303598877221904961155, + -0.457813303598877221904961155, 0.889048355854664562540777729, + 0.304929229735402406490728633, 0.952375012719765858529893608, + -0.952375012719765858529893608, 0.304929229735402406490728633, + 0.961280485811320641748659653, 0.275571819310958163076425168, + -0.275571819310958163076425168, 0.961280485811320641748659653, + 0.484869248000791101822951699, 0.874586652278176112634431897, + -0.874586652278176112634431897, 0.484869248000791101822951699, + 0.782650596166575738458949301, 0.622461279374149972519166721, + -0.622461279374149972519166721, 0.782650596166575738458949301, + 0.113270952177564349018228733, 0.993564135520595333782021697, + -0.993564135520595333782021697, 0.113270952177564349018228733, + 0.983662419211730274396237776, 0.180022901405699522679906590, + -0.180022901405699522679906590, 0.983662419211730274396237776, + 0.568258952670131549790548489, 0.822849781375826332046780034, + -0.822849781375826332046780034, 0.568258952670131549790548489, + 0.839893794195999504583383987, 0.542750784864515906586768661, + -0.542750784864515906586768661, 0.839893794195999504583383987, + 0.210111836880469621717489972, 0.977677357824509979943404762, + -0.977677357824509979943404762, 0.210111836880469621717489972, + 0.929640895843181265457918066, 0.368466829953372331712746222, + -0.368466829953372331712746222, 0.929640895843181265457918066, + 0.396809987416710328595290911, 0.917900775621390457642276297, + -0.917900775621390457642276297, 0.396809987416710328595290911, + 0.717870045055731736211325329, 0.696177131491462944788582591, + -0.696177131491462944788582591, 0.717870045055731736211325329, + 0.015339206284988101044151868, 0.999882347454212525633049627, + -0.999882347454212525633049627, 0.015339206284988101044151868, + 0.999769405351215321657617036, 0.021474080275469507418374898, + -0.021474080275469507418374898, 0.999769405351215321657617036, + 0.691759258364157774906734132, 0.722128193929215321243607198, + -0.722128193929215321243607198, 0.691759258364157774906734132, + 0.915448716088267819566431292, 0.402434650859418441082533934, + -0.402434650859418441082533934, 0.915448716088267819566431292, + 0.362755724367397216204854462, 0.931884265581668106718557199, + -0.931884265581668106718557199, 0.362755724367397216204854462, + 0.976369731330021149312732194, 0.216106797076219509948385131, + -0.216106797076219509948385131, 0.976369731330021149312732194, + 0.537587076295645482502214932, 0.843208239641845437161743865, + -0.843208239641845437161743865, 0.537587076295645482502214932, + 0.819347520076796960824689637, 0.573297166698042212820171239, + -0.573297166698042212820171239, 0.819347520076796960824689637, + 0.173983873387463827950700807, 0.984748501801904218556553176, + -0.984748501801904218556553176, 0.173983873387463827950700807, + 0.992850414459865090793563344, 0.119365214810991364593637790, + -0.119365214810991364593637790, 0.992850414459865090793563344, + 0.617647307937803932403979402, 0.786455213599085757522319464, + -0.786455213599085757522319464, 0.617647307937803932403979402, + 0.871595086655951034842481435, 0.490226483288291154229598449, + -0.490226483288291154229598449, 0.871595086655951034842481435, + 0.269668325572915106525464462, 0.962953266873683886347921481, + -0.962953266873683886347921481, 0.269668325572915106525464462, + 0.950486073949481721759926101, 0.310767152749611495835997250, + -0.310767152749611495835997250, 0.950486073949481721759926101, + 0.452349587233770874133026703, 0.891840709392342727796478697, + -0.891840709392342727796478697, 0.452349587233770874133026703, + 0.759209188978388033485525443, 0.650846684996380915068975573, + -0.650846684996380915068975573, 0.759209188978388033485525443, + 0.076623861392031492278332463, 0.997060070339482978987989949, + -0.997060070339482978987989949, 0.076623861392031492278332463, + 0.997511456140303459699448390, 0.070504573389613863027351471, + -0.070504573389613863027351471, 0.997511456140303459699448390, + 0.655492852999615385312679701, 0.755201376896536527598710756, + -0.755201376896536527598710756, 0.655492852999615385312679701, + 0.894599485631382678433072126, 0.446868840162374195353044389, + -0.446868840162374195353044389, 0.894599485631382678433072126, + 0.316593375556165867243047035, 0.948561349915730288158494826, + -0.948561349915730288158494826, 0.316593375556165867243047035, + 0.964589793289812723836432159, 0.263754678974831383611349322, + -0.263754678974831383611349322, 0.964589793289812723836432159, + 0.495565261825772531150266670, 0.868570705971340895340449876, + -0.868570705971340895340449876, 0.495565261825772531150266670, + 0.790230221437310055030217152, 0.612810082429409703935211936, + -0.612810082429409703935211936, 0.790230221437310055030217152, + 0.125454983411546238542336453, 0.992099313142191757112085445, + -0.992099313142191757112085445, 0.125454983411546238542336453, + 0.985797509167567424700995000, 0.167938294974731178054745536, + -0.167938294974731178054745536, 0.985797509167567424700995000, + 0.578313796411655563342245019, 0.815814410806733789010772660, + -0.815814410806733789010772660, 0.578313796411655563342245019, + 0.846490938774052078300544488, 0.532403127877197971442805218, + -0.532403127877197971442805218, 0.846490938774052078300544488, + 0.222093620973203534094094721, 0.975025345066994146844913468, + -0.975025345066994146844913468, 0.222093620973203534094094721, + 0.934092550404258914729877883, 0.357030961233430032614954036, + -0.357030961233430032614954036, 0.934092550404258914729877883, + 0.408044162864978680820747499, 0.912962190428398164628018233, + -0.912962190428398164628018233, 0.408044162864978680820747499, + 0.726359155084345976817494315, 0.687315340891759108199186948, + -0.687315340891759108199186948, 0.726359155084345976817494315, + 0.027608145778965741612354872, 0.999618822495178597116830637, + -0.999618822495178597116830637, 0.027608145778965741612354872, + 0.998941293186856850633930266, 0.046003182130914628814301788, + -0.046003182130914628814301788, 0.998941293186856850633930266, + 0.673829000378756060917568372, 0.738887324460615147933116508, + -0.738887324460615147933116508, 0.673829000378756060917568372, + 0.905296759318118774354048329, 0.424779681209108833357226189, + -0.424779681209108833357226189, 0.905296759318118774354048329, + 0.339776884406826857828825803, 0.940506070593268323787291309, + -0.940506070593268323787291309, 0.339776884406826857828825803, + 0.970772140728950302138169611, 0.240003022448741486568922365, + -0.240003022448741486568922365, 0.970772140728950302138169611, + 0.516731799017649881508753876, 0.856147328375194481019630732, + -0.856147328375194481019630732, 0.516731799017649881508753876, + 0.805031331142963597922659282, 0.593232295039799808047809426, + -0.593232295039799808047809426, 0.805031331142963597922659282, + 0.149764534677321517229695737, 0.988721691960323767604516485, + -0.988721691960323767604516485, 0.149764534677321517229695737, + 0.989622017463200834623694454, 0.143695033150294454819773349, + -0.143695033150294454819773349, 0.989622017463200834623694454, + 0.598160706996342311724958652, 0.801376171723140219430247777, + -0.801376171723140219430247777, 0.598160706996342311724958652, + 0.859301818357008404783582139, 0.511468850437970399504391001, + -0.511468850437970399504391001, 0.859301818357008404783582139, + 0.245955050335794611599924709, 0.969281235356548486048290738, + -0.969281235356548486048290738, 0.245955050335794611599924709, + 0.942573197601446879280758735, 0.333999651442009404650865481, + -0.333999651442009404650865481, 0.942573197601446879280758735, + 0.430326481340082633908199031, 0.902673318237258806751502391, + -0.902673318237258806751502391, 0.430326481340082633908199031, + 0.743007952135121693517362293, 0.669282588346636065720696366, + -0.669282588346636065720696366, 0.743007952135121693517362293, + 0.052131704680283321236358216, 0.998640218180265222418199049, + -0.998640218180265222418199049, 0.052131704680283321236358216, + 0.995480755491926941769171600, 0.094963495329638998938034312, + -0.094963495329638998938034312, 0.995480755491926941769171600, + 0.636761861236284230413943435, 0.771060524261813773200605759, + -0.771060524261813773200605759, 0.636761861236284230413943435, + 0.883363338665731594736308015, 0.468688822035827933697617870, + -0.468688822035827933697617870, 0.883363338665731594736308015, + 0.293219162694258650606608599, 0.956045251349996443270479823, + -0.956045251349996443270479823, 0.293219162694258650606608599, + 0.957826413027532890321037029, 0.287347459544729526477331841, + -0.287347459544729526477331841, 0.957826413027532890321037029, + 0.474100214650550014398580015, 0.880470889052160770806542929, + -0.880470889052160770806542929, 0.474100214650550014398580015, + 0.774953106594873878359129282, 0.632018735939809021909403706, + -0.632018735939809021909403706, 0.774953106594873878359129282, + 0.101069862754827824987887585, 0.994879330794805620591166107, + -0.994879330794805620591166107, 0.101069862754827824987887585, + 0.981379193313754574318224190, 0.192080397049892441679288205, + -0.192080397049892441679288205, 0.981379193313754574318224190, + 0.558118531220556115693702964, 0.829761233794523042469023765, + -0.829761233794523042469023765, 0.558118531220556115693702964, + 0.833170164701913186439915922, 0.553016705580027531764226988, + -0.553016705580027531764226988, 0.833170164701913186439915922, + 0.198098410717953586179324918, 0.980182135968117392690210009, + -0.980182135968117392690210009, 0.198098410717953586179324918, + 0.925049240782677590302371869, 0.379847208924051170576281147, + -0.379847208924051170576281147, 0.925049240782677590302371869, + 0.385516053843918864075607949, 0.922701128333878570437264227, + -0.922701128333878570437264227, 0.385516053843918864075607949, + 0.709272826438865651316533772, 0.704934080375904908852523758, + -0.704934080375904908852523758, 0.709272826438865651316533772, + 0.003067956762965976270145365, 0.999995293809576171511580126, + -0.999995293809576171511580126, 0.003067956762965976270145365 }; - p2_tab = new FalconFPR[]{ - new FalconFPR(2.00000000000), - new FalconFPR(1.00000000000), - new FalconFPR(0.50000000000), - new FalconFPR(0.25000000000), - new FalconFPR(0.12500000000), - new FalconFPR(0.06250000000), - new FalconFPR(0.03125000000), - new FalconFPR(0.01562500000), - new FalconFPR(0.00781250000), - new FalconFPR(0.00390625000), - new FalconFPR(0.00195312500) + fpr_p2_tab = new double[]{ + 2.00000000000, + 1.00000000000, + 0.50000000000, + 0.25000000000, + 0.12500000000, + 0.06250000000, + 0.03125000000, + 0.01562500000, + 0.00781250000, + 0.00390625000, + 0.00195312500 }; } FPREngine() { - this.fpr_q = new FalconFPR(12289.0); - this.fpr_inverse_of_q = new FalconFPR(1.0 / 12289.0); - this.fpr_inv_2sqrsigma0 = new FalconFPR(0.150865048875372721532312163019); - this.fpr_inv_sigma = inv_sigma; - this.fpr_sigma_min = sigma_min; - this.fpr_log2 = new FalconFPR(0.69314718055994530941723212146); - this.fpr_inv_log2 = new FalconFPR(1.4426950408889634073599246810); - this.fpr_bnorm_max = new FalconFPR(16822.4121); - this.fpr_zero = new FalconFPR(0.0); - this.fpr_one = new FalconFPR(1.0); - this.fpr_two = new FalconFPR(2.0); - this.fpr_onehalf = new FalconFPR(0.5); - this.fpr_invsqrt2 = new FalconFPR(0.707106781186547524400844362105); - this.fpr_invsqrt8 = new FalconFPR(0.353553390593273762200422181052); - this.fpr_ptwo31 = new FalconFPR(2147483648.0); - this.fpr_ptwo31m1 = new FalconFPR(2147483647.0); - this.fpr_mtwo31m1 = new FalconFPR(-2147483647.0); - this.fpr_ptwo63m1 = new FalconFPR(9223372036854775807.0); - this.fpr_mtwo63m1 = new FalconFPR(-9223372036854775807.0); - this.fpr_ptwo63 = new FalconFPR(9223372036854775808.0); - this.fpr_gm_tab = gm_tab; - this.fpr_p2_tab = p2_tab; + //this.fpr_inverse_of_q = 1.0 / 12289.0; + //this.fpr_inv_2sqrsigma0 = 0.150865048875372721532312163019; + //this.fpr_inv_sigma = inv_sigma; + //this.fpr_sigma_min = sigma_min; + //this.fpr_log2 = 0.69314718055994530941723212146; + //this.fpr_inv_log2 = 1.4426950408889634073599246810; + //this.fpr_bnorm_max = 16822.4121; +// this.fpr_zero = 0.0; +// this.fpr_one = 1.0; +// this.fpr_two = 2.0; + //this.fpr_onehalf = 0.5; +// this.fpr_invsqrt2 = 0.707106781186547524400844362105; +// this.fpr_invsqrt8 = 0.353553390593273762200422181052; +// this.fpr_ptwo31 = 2147483648.0; +// this.fpr_ptwo31m1 = 2147483647.0; +// this.fpr_mtwo31m1 = -2147483647.0; +// this.fpr_ptwo63m1 = 9223372036854775807.0; +// this.fpr_mtwo63m1 = -9223372036854775807.0; +// this.fpr_ptwo63 = 9223372036854775808.0; + //this.fpr_gm_tab = gm_tab; + //this.fpr_p2_tab = p2_tab; } - FalconFPR FPR(double v) - { - FalconFPR x = new FalconFPR(v); - return x; - } - - FalconFPR fpr_of(long i) - { - return FPR((double)i); - } +// static double fpr_of(long i) +// { +// return (double)i; +// } - final FalconFPR fpr_q; - final FalconFPR fpr_inverse_of_q; - final FalconFPR fpr_inv_2sqrsigma0; - final FalconFPR[] fpr_inv_sigma; - final FalconFPR[] fpr_sigma_min; - final FalconFPR fpr_log2; - final FalconFPR fpr_inv_log2; - final FalconFPR fpr_bnorm_max; - final FalconFPR fpr_zero; - final FalconFPR fpr_one; - final FalconFPR fpr_two; - final FalconFPR fpr_onehalf; - final FalconFPR fpr_invsqrt2; - final FalconFPR fpr_invsqrt8; - final FalconFPR fpr_ptwo31; - final FalconFPR fpr_ptwo31m1; - final FalconFPR fpr_mtwo31m1; - final FalconFPR fpr_ptwo63m1; - final FalconFPR fpr_mtwo63m1; - final FalconFPR fpr_ptwo63; - final FalconFPR[] fpr_gm_tab; - final FalconFPR[] fpr_p2_tab; + static final double fpr_q = 12289.0; + static final double fpr_inverse_of_q = 1.0 / 12289.0; + static final double fpr_inv_2sqrsigma0 = 0.150865048875372721532312163019; + static final double[] fpr_inv_sigma; + static final double[] fpr_sigma_min; + static final double fpr_log2 = 0.69314718055994530941723212146; + static final double fpr_inv_log2 = 1.4426950408889634073599246810; + static final double fpr_bnorm_max = 16822.4121; + static final double fpr_zero = 0.0; + static final double fpr_one = 1.0; + static final double fpr_two = 2.0; + static final double fpr_onehalf = 0.5; +// static final double fpr_invsqrt2 = 0.707106781186547524400844362105; +// static final double fpr_invsqrt8 = 0.353553390593273762200422181052; + static final double fpr_ptwo31 = 2147483648.0; + static final double fpr_ptwo31m1 = 2147483647.0; + static final double fpr_mtwo31m1 = -2147483647.0; + static final double fpr_ptwo63m1 = 9223372036854775807.0; + static final double fpr_mtwo63m1 = -9223372036854775807.0; + static final double fpr_ptwo63 = 9223372036854775808.0; + static final double[] fpr_gm_tab; + static final double[] fpr_p2_tab; - long - fpr_rint(FalconFPR x) + static long fpr_rint(double x) { /* * We do not want to use llrint() since it might be not @@ -1158,10 +1150,10 @@ FalconFPR fpr_of(long i) long sx, tx, rp, rn, m; int ub; - sx = (long)(x.v - 1.0); - tx = (long)x.v; - rp = (long)(x.v + 4503599627370496.0) - 4503599627370496l; - rn = (long)(x.v - 4503599627370496.0) + 4503599627370496l; + sx = (long)(x - 1.0); + tx = (long)x; + rp = (long)(x + 4503599627370496.0) - 4503599627370496L; + rn = (long)(x - 4503599627370496.0) + 4503599627370496L; /* * If tx >= 2^52 or tx < -2^52, then result is tx. @@ -1198,7 +1190,7 @@ FalconFPR fpr_of(long i) return tx | rn | rp; } - long fpr_floor(FalconFPR x) + static long fpr_floor(double x) { long r; @@ -1212,85 +1204,82 @@ long fpr_floor(FalconFPR x) * if it is false on a given arch, then chances are that the FPU * itself is not constant-time, making the point moot). */ - r = (long)x.v; - return r - (x.v < (double)r ? 1 : 0); + r = (long)x; + return r - (x < (double)r ? 1 : 0); } - long - fpr_trunc(FalconFPR x) - { - return (long)x.v; - } +// long +// fpr_trunc(double x) +// { +// return (long)x; +// } - FalconFPR - fpr_add(FalconFPR x, FalconFPR y) - { - return FPR(x.v + y.v); - } +// static double fpr_add(double x, double y) +// { +// return x + y; +// } - FalconFPR - fpr_sub(FalconFPR x, FalconFPR y) - { - return FPR(x.v - y.v); - } +// static double fpr_sub(double x, double y) +// { +// return x - y; +// } - FalconFPR - fpr_neg(FalconFPR x) - { - return FPR(-x.v); - } +// static double fpr_neg(double x) +// { +// return -x; +// } - FalconFPR - fpr_half(FalconFPR x) - { - return FPR(x.v * 0.5); - } +// static double +// fpr_half(double x) +// { +// return x * 0.5; +// } - FalconFPR - fpr_double(FalconFPR x) - { - return FPR(x.v + x.v); - } +// double +// fpr_double(double x) +// { +// return x + x; +// } - FalconFPR - fpr_mul(FalconFPR x, FalconFPR y) - { - return FPR(x.v * y.v); - } +// static double +// fpr_mul(double x, double y) +// { +// return x * y; +// } - FalconFPR - fpr_sqr(FalconFPR x) - { - return FPR(x.v * x.v); - } +// static double +// fpr_sqr(double x) +// { +// return x * x; +// } - FalconFPR - fpr_inv(FalconFPR x) - { - return FPR(1.0 / x.v); - } +// static double +// fpr_inv(double x) +// { +// return 1.0 / x; +// } - FalconFPR - fpr_div(FalconFPR x, FalconFPR y) - { - return FPR(x.v / y.v); - } +// double +// fpr_div(double x, double y) +// { +// return FPR(x / y); +// } - FalconFPR - fpr_sqrt(FalconFPR x) - { - return FPR(Math.sqrt(x.v)); - } +// static double +// fpr_sqrt(double x) +// { +// return Math.sqrt(x); +// } - boolean - fpr_lt(FalconFPR x, FalconFPR y) - { - return x.v < y.v; - } +// static boolean +// fpr_lt(double x, double y) +// { +// return x < y; +// } - long - fpr_expm_p63(FalconFPR x, FalconFPR ccs) + static long + fpr_expm_p63(double x, double ccs) { /* * Polynomial approximation of exp(-x) is taken from FACCT: @@ -1311,7 +1300,7 @@ long fpr_floor(FalconFPR x) double d, y; - d = x.v; + d = x; y = 0.000000002073772366009083061987; y = 0.000000025299506379442070029551 - y * d; y = 0.000000275607356160477811864927 - y * d; @@ -1325,7 +1314,7 @@ long fpr_floor(FalconFPR x) y = 0.500000000000019206858326015208 - y * d; y = 0.999999999999994892974086724280 - y * d; y = 1.000000000000000000000000000000 - y * d; - y *= ccs.v; - return (long)(y * fpr_ptwo63.v); + y *= ccs; + return (long)(y * fpr_ptwo63); } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconCodec.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconCodec.java index 1452ccb849..271473d744 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconCodec.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconCodec.java @@ -2,15 +2,10 @@ class FalconCodec { - - FalconCodec() - { - } - /* see inner.h */ - int modq_encode( - byte[] srcout, int out, int max_out_len, - short[] srcx, int x, int logn) + static int modq_encode( + byte[] srcout, int max_out_len, + short[] srcx, int logn) { int n, out_len, u; int buf; @@ -20,7 +15,7 @@ int modq_encode( n = 1 << logn; for (u = 0; u < n; u++) { - if ((srcx[x + u] & 0x0000ffff) >= 12289) + if ((srcx[u] & 0x0000ffff) >= 12289) { return 0; } @@ -34,12 +29,12 @@ int modq_encode( { return 0; } - buf = out; + buf = 1; acc = 0; acc_len = 0; for (u = 0; u < n; u++) { - acc = (acc << 14) | (srcx[x + u] & 0xffff); + acc = (acc << 14) | (srcx[u] & 0xffff); acc_len += 14; while (acc_len >= 8) { @@ -55,9 +50,9 @@ int modq_encode( } /* see inner.h */ - int modq_decode( - short[] srcx, int x, int logn, - byte[] srcin, int in, int max_in_len) + static int modq_decode( + short[] srcx, int logn, + byte[] srcin, int max_in_len) { int n, in_len, u; int buf; @@ -70,7 +65,7 @@ int modq_decode( { return 0; } - buf = in; + buf = 0; acc = 0; acc_len = 0; u = 0; @@ -88,7 +83,7 @@ int modq_decode( { return 0; } - srcx[x + u] = (short)w; + srcx[u] = (short)w; u++; } } @@ -100,116 +95,116 @@ int modq_decode( } /* see inner.h */ - int trim_i16_encode( - byte[] srcout, int out, int max_out_len, - short[] srcx, int x, int logn, int bits) - { - int n, u, out_len; - int minv, maxv; - int buf; - int acc, mask; - int acc_len; - - n = 1 << logn; - maxv = (1 << (bits - 1)) - 1; - minv = -maxv; - for (u = 0; u < n; u++) - { - if (srcx[x + u] < minv || srcx[x + u] > maxv) - { - return 0; - } - } - out_len = ((n * bits) + 7) >> 3; - if (srcout == null) - { - return out_len; - } - if (out_len > max_out_len) - { - return 0; - } - buf = out; - acc = 0; - acc_len = 0; - mask = (1 << bits) - 1; - for (u = 0; u < n; u++) - { - acc = (acc << bits) | ((srcx[x + u] & 0xfff) & mask); - acc_len += bits; - while (acc_len >= 8) - { - acc_len -= 8; - srcout[buf++] = (byte)(acc >> acc_len); - } - } - if (acc_len > 0) - { - srcout[buf++] = (byte)(acc << (8 - acc_len)); - } - return out_len; - } +// int trim_i16_encode( +// byte[] srcout, int out, int max_out_len, +// short[] srcx, int x, int logn, int bits) +// { +// int n, u, out_len; +// int minv, maxv; +// int buf; +// int acc, mask; +// int acc_len; +// +// n = 1 << logn; +// maxv = (1 << (bits - 1)) - 1; +// minv = -maxv; +// for (u = 0; u < n; u++) +// { +// if (srcx[x + u] < minv || srcx[x + u] > maxv) +// { +// return 0; +// } +// } +// out_len = ((n * bits) + 7) >> 3; +// if (srcout == null) +// { +// return out_len; +// } +// if (out_len > max_out_len) +// { +// return 0; +// } +// buf = out; +// acc = 0; +// acc_len = 0; +// mask = (1 << bits) - 1; +// for (u = 0; u < n; u++) +// { +// acc = (acc << bits) | ((srcx[x + u] & 0xfff) & mask); +// acc_len += bits; +// while (acc_len >= 8) +// { +// acc_len -= 8; +// srcout[buf++] = (byte)(acc >> acc_len); +// } +// } +// if (acc_len > 0) +// { +// srcout[buf++] = (byte)(acc << (8 - acc_len)); +// } +// return out_len; +// } /* see inner.h */ - int trim_i16_decode( - short[] srcx, int x, int logn, int bits, - byte[] srcin, int in, int max_in_len) - { - int n, in_len; - int buf; - int u; - int acc, mask1, mask2; - int acc_len; - - n = 1 << logn; - in_len = ((n * bits) + 7) >> 3; - if (in_len > max_in_len) - { - return 0; - } - buf = in; - u = 0; - acc = 0; - acc_len = 0; - mask1 = (1 << bits) - 1; - mask2 = 1 << (bits - 1); - while (u < n) - { - acc = (acc << 8) | (srcin[buf++] & 0xff); - acc_len += 8; - while (acc_len >= bits && u < n) - { - int w; - - acc_len -= bits; - w = (acc >>> acc_len) & mask1; - w |= -(w & mask2); - if (w == -mask2) - { - /* - * The -2^(bits-1) value is forbidden. - */ - return 0; - } - w |= -(w & mask2); - srcx[x + u] = (short)w; - u++; - } - } - if ((acc & ((1 << acc_len) - 1)) != 0) - { - /* - * Extra bits in the last byte must be zero. - */ - return 0; - } - return in_len; - } +// int trim_i16_decode( +// short[] srcx, int x, int logn, int bits, +// byte[] srcin, int in, int max_in_len) +// { +// int n, in_len; +// int buf; +// int u; +// int acc, mask1, mask2; +// int acc_len; +// +// n = 1 << logn; +// in_len = ((n * bits) + 7) >> 3; +// if (in_len > max_in_len) +// { +// return 0; +// } +// buf = in; +// u = 0; +// acc = 0; +// acc_len = 0; +// mask1 = (1 << bits) - 1; +// mask2 = 1 << (bits - 1); +// while (u < n) +// { +// acc = (acc << 8) | (srcin[buf++] & 0xff); +// acc_len += 8; +// while (acc_len >= bits && u < n) +// { +// int w; +// +// acc_len -= bits; +// w = (acc >>> acc_len) & mask1; +// w |= -(w & mask2); +// if (w == -mask2) +// { +// /* +// * The -2^(bits-1) value is forbidden. +// */ +// return 0; +// } +// w |= -(w & mask2); +// srcx[x + u] = (short)w; +// u++; +// } +// } +// if ((acc & ((1 << acc_len) - 1)) != 0) +// { +// /* +// * Extra bits in the last byte must be zero. +// */ +// return 0; +// } +// return in_len; +// } /* see inner.h */ - int trim_i8_encode( + static int trim_i8_encode( byte[] srcout, int out, int max_out_len, - byte[] srcx, int x, int logn, int bits) + byte[] srcx, int logn, int bits) { int n, u, out_len; int minv, maxv; @@ -222,7 +217,7 @@ int trim_i8_encode( minv = -maxv; for (u = 0; u < n; u++) { - if (srcx[x + u] < minv || srcx[x + u] > maxv) + if (srcx[u] < minv || srcx[u] > maxv) { return 0; } @@ -242,7 +237,7 @@ int trim_i8_encode( mask = (1 << bits) - 1; for (u = 0; u < n; u++) { - acc = (acc << bits) | ((srcx[x + u] & 0xffff) & mask); + acc = (acc << bits) | ((srcx[u] & 0xffff) & mask); acc_len += bits; while (acc_len >= 8) { @@ -252,14 +247,14 @@ int trim_i8_encode( } if (acc_len > 0) { - srcout[buf++] = (byte)(acc << (8 - acc_len)); + srcout[buf] = (byte)(acc << (8 - acc_len)); } return out_len; } /* see inner.h */ - int trim_i8_decode( - byte[] srcx, int x, int logn, int bits, + static int trim_i8_decode( + byte[] srcx, int logn, int bits, byte[] srcin, int in, int max_in_len) { int n, in_len; @@ -298,7 +293,7 @@ int trim_i8_decode( */ return 0; } - srcx[x + u] = (byte)w; + srcx[u] = (byte)w; u++; } } @@ -313,9 +308,9 @@ int trim_i8_decode( } /* see inner.h */ - int comp_encode( - byte[] srcout, int out, int max_out_len, - short[] srcx, int x, int logn) + static int comp_encode( + byte[] srcout, int max_out_len, + short[] srcx, int logn) { int buf; int n, u, v; @@ -323,14 +318,14 @@ int comp_encode( int acc_len; n = 1 << logn; - buf = out; + buf = 0; /* * Make sure that all values are within the -2047..+2047 range. */ for (u = 0; u < n; u++) { - if (srcx[x + u] < -2047 || srcx[x + u] > +2047) + if (srcx[u] < -2047 || srcx[u] > 2047) { return 0; } @@ -349,7 +344,7 @@ int comp_encode( * sign bit. */ acc <<= 1; - t = srcx[x + u]; + t = srcx[u]; if (t < 0) { t = -t; @@ -419,9 +414,9 @@ int comp_encode( } /* see inner.h */ - int comp_decode( - short[] srcx, int x, int logn, - byte[] srcin, int in, int max_in_len) + static int comp_decode( + short[] srcx, int logn, + byte[] srcin, int max_in_len) { int buf; int n, u, v; @@ -429,7 +424,7 @@ int comp_decode( int acc_len; n = 1 << logn; - buf = in; + buf = 0; acc = 0; acc_len = 0; v = 0; @@ -486,7 +481,7 @@ int comp_decode( return 0; } - srcx[x + u] = (short)(s != 0 ? -m : m); + srcx[u] = (short)(s != 0 ? -m : m); } /* @@ -532,7 +527,7 @@ int comp_decode( * of max_fg_bits[] and max_FG_bits[] shall be greater than 8. */ - final byte[] max_fg_bits = { + static final byte[] max_fg_bits = { 0, /* unused */ 8, 8, @@ -546,7 +541,7 @@ int comp_decode( 5 }; - final byte[] max_FG_bits = { + static final byte[] max_FG_bits = { 0, /* unused */ 8, 8, @@ -588,18 +583,18 @@ int comp_decode( * in -2047..2047, i.e. 12 bits. */ - final byte[] max_sig_bits = { - 0, /* unused */ - 10, - 11, - 11, - 12, - 12, - 12, - 12, - 12, - 12, - 12 - }; +// final byte[] max_sig_bits = { +// 0, /* unused */ +// 10, +// 11, +// 11, +// 12, +// 12, +// 12, +// 12, +// 12, +// 12, +// 12 +// }; } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconCommon.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconCommon.java index a4f266a8df..2a5c19ab42 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconCommon.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconCommon.java @@ -1,13 +1,12 @@ package org.bouncycastle.pqc.crypto.falcon; +import org.bouncycastle.crypto.digests.SHAKEDigest; + class FalconCommon { - FalconCommon() - { - } /* see inner.h */ - void hash_to_point_vartime(SHAKE256 sc, short[] srcx, int x, int logn) + static void hash_to_point_vartime(SHAKEDigest sc, short[] srcx, int logn) { /* * This is the straightforward per-the-spec implementation. It @@ -20,251 +19,253 @@ void hash_to_point_vartime(SHAKE256 sc, short[] srcx, int x, int logn) * plaintexts). */ int n; - + int x = 0; n = 1 << logn; + byte[] buf = new byte[2]; while (n > 0) { - byte[] buf = new byte[2]; + int w; // unsigned // inner_shake256_extract(sc, (void *)buf, sizeof buf); - sc.inner_shake256_extract(buf, 0, 2); + sc.doOutput(buf, 0, 2); w = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff); if (w < 61445) { - while (w >= 12289) - { - w -= 12289; - } +// while (w >= 12289) +// { +// w -= 12289; +// } + w %= 12289; srcx[x++] = (short)w; n--; } } } - void hash_to_point_ct( - SHAKE256 sc, - short[] srcx, int x, int logn, short[] srctmp, int tmp) - { - /* - * Each 16-bit sample is a value in 0..65535. The value is - * kept if it falls in 0..61444 (because 61445 = 5*12289) - * and rejected otherwise; thus, each sample has probability - * about 0.93758 of being selected. - * - * We want to oversample enough to be sure that we will - * have enough values with probability at least 1 - 2^(-256). - * Depending on degree N, this leads to the following - * required oversampling: - * - * logn n oversampling - * 1 2 65 - * 2 4 67 - * 3 8 71 - * 4 16 77 - * 5 32 86 - * 6 64 100 - * 7 128 122 - * 8 256 154 - * 9 512 205 - * 10 1024 287 - * - * If logn >= 7, then the provided temporary buffer is large - * enough. Otherwise, we use a stack buffer of 63 entries - * (i.e. 126 bytes) for the values that do not fit in tmp[]. - */ - - short overtab[] = { - 0, /* unused */ - 65, - 67, - 71, - 77, - 86, - 100, - 122, - 154, - 205, - 287 - }; - - int n, n2, u, m, p, over; - int tt1; - short[] tt2 = new short[63]; - - /* - * We first generate m 16-bit value. Values 0..n-1 go to x[]. - * Values n..2*n-1 go to tt1[]. Values 2*n and later go to tt2[]. - * We also reduce modulo q the values; rejected values are set - * to 0xFFFF. - */ - n = 1 << logn; - n2 = n << 1; - over = overtab[logn]; - m = n + over; - tt1 = tmp; - for (u = 0; u < m; u++) - { - byte[] buf = new byte[2]; - int w, wr; - - sc.inner_shake256_extract(buf, 0, buf.length); - w = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff); - wr = w - (24578 & (((w - 24578) >>> 31) - 1)); - wr = wr - (24578 & (((wr - 24578) >>> 31) - 1)); - wr = wr - (12289 & (((wr - 12289) >>> 31) - 1)); - wr |= ((w - 61445) >>> 31) - 1; - if (u < n) - { - srcx[x + u] = (short)wr; - } - else if (u < n2) - { - srctmp[tt1 + u - n] = (short)wr; - } - else - { - tt2[u - n2] = (short)wr; - } - } - - /* - * Now we must "squeeze out" the invalid values. We do this in - * a logarithmic sequence of passes; each pass computes where a - * value should go, and moves it down by 'p' slots if necessary, - * where 'p' uses an increasing powers-of-two scale. It can be - * shown that in all cases where the loop decides that a value - * has to be moved down by p slots, the destination slot is - * "free" (i.e. contains an invalid value). - */ - for (p = 1; p <= over; p <<= 1) - { - int v; - - /* - * In the loop below: - * - * - v contains the index of the final destination of - * the value; it is recomputed dynamically based on - * whether values are valid or not. - * - * - u is the index of the value we consider ("source"); - * its address is s. - * - * - The loop may swap the value with the one at index - * u-p. The address of the swap destination is d. - */ - v = 0; - for (u = 0; u < m; u++) - { - int s, d; - int sp, dp; - int j, sv, dv, mk; - - if (u < n) - { - sp = 1; - s = x + u; - sv = srcx[s]; - } - else if (u < n2) - { - sp = 2; - s = tt1 + u - n; - sv = srctmp[s]; - } - else - { - sp = 3; - s = u - n2; - sv = tt2[s]; - } - - /* - * The value in sv should ultimately go to - * address v, i.e. jump back by u-v slots. - */ - j = u - v; - - /* - * We increment v for the next iteration, but - * only if the source value is valid. The mask - * 'mk' is -1 if the value is valid, 0 otherwise, - * so we _subtract_ mk. - */ - mk = (sv >>> 15) - 1; - v -= mk; - - /* - * In this loop we consider jumps by p slots; if - * u < p then there is nothing more to do. - */ - if (u < p) - { - continue; - } - - /* - * Destination for the swap: value at address u-p. - */ - if ((u - p) < n) - { - dp = 1; - d = x + u - p; - dv = srcx[d]; - } - else if ((u - p) < n2) - { - dp = 2; - d = tt1 + (u - p) - n; - dv = srctmp[d]; - } - else - { - dp = 3; - d = (u - p) - n2; - dv = tt2[d]; - } - - /* - * The swap should be performed only if the source - * is valid AND the jump j has its 'p' bit set. - */ - mk &= -(((j & p) + 0x1FF) >> 9); - if (sp == 1) - { - srcx[s] = (short)(sv ^ (mk & (sv ^ dv))); - } - else if (sp == 2) - { - srctmp[s] = (short)(sv ^ (mk & (sv ^ dv))); - } - else - { - tt2[s] = (short)(sv ^ (mk & (sv ^ dv))); - } - if (dp == 1) - { - srcx[d] = (short)(dv ^ (mk & (sv ^ dv))); - } - else if (dp == 2) - { - srctmp[d] = (short)(dv ^ (mk & (sv ^ dv))); - } - else - { - tt2[d] = (short)(dv ^ (mk & (sv ^ dv))); - } - } - } - } +// void hash_to_point_ct( +// SHAKE256 sc, +// short[] srcx, int x, int logn, short[] srctmp, int tmp) +// { +// /* +// * Each 16-bit sample is a value in 0..65535. The value is +// * kept if it falls in 0..61444 (because 61445 = 5*12289) +// * and rejected otherwise; thus, each sample has probability +// * about 0.93758 of being selected. +// * +// * We want to oversample enough to be sure that we will +// * have enough values with probability at least 1 - 2^(-256). +// * Depending on degree N, this leads to the following +// * required oversampling: +// * +// * logn n oversampling +// * 1 2 65 +// * 2 4 67 +// * 3 8 71 +// * 4 16 77 +// * 5 32 86 +// * 6 64 100 +// * 7 128 122 +// * 8 256 154 +// * 9 512 205 +// * 10 1024 287 +// * +// * If logn >= 7, then the provided temporary buffer is large +// * enough. Otherwise, we use a stack buffer of 63 entries +// * (i.e. 126 bytes) for the values that do not fit in tmp[]. +// */ +// +// short overtab[] = { +// 0, /* unused */ +// 65, +// 67, +// 71, +// 77, +// 86, +// 100, +// 122, +// 154, +// 205, +// 287 +// }; +// +// int n, n2, u, m, p, over; +// int tt1; +// short[] tt2 = new short[63]; +// +// /* +// * We first generate m 16-bit value. Values 0..n-1 go to x[]. +// * Values n..2*n-1 go to tt1[]. Values 2*n and later go to tt2[]. +// * We also reduce modulo q the values; rejected values are set +// * to 0xFFFF. +// */ +// n = 1 << logn; +// n2 = n << 1; +// over = overtab[logn]; +// m = n + over; +// tt1 = tmp; +// for (u = 0; u < m; u++) +// { +// byte[] buf = new byte[2]; +// int w, wr; +// +// sc.inner_shake256_extract(buf, 0, buf.length); +// w = ((buf[0] & 0xff) << 8) | (buf[1] & 0xff); +// wr = w - (24578 & (((w - 24578) >>> 31) - 1)); +// wr = wr - (24578 & (((wr - 24578) >>> 31) - 1)); +// wr = wr - (12289 & (((wr - 12289) >>> 31) - 1)); +// wr |= ((w - 61445) >>> 31) - 1; +// if (u < n) +// { +// srcx[x + u] = (short)wr; +// } +// else if (u < n2) +// { +// srctmp[tt1 + u - n] = (short)wr; +// } +// else +// { +// tt2[u - n2] = (short)wr; +// } +// } +// +// /* +// * Now we must "squeeze out" the invalid values. We do this in +// * a logarithmic sequence of passes; each pass computes where a +// * value should go, and moves it down by 'p' slots if necessary, +// * where 'p' uses an increasing powers-of-two scale. It can be +// * shown that in all cases where the loop decides that a value +// * has to be moved down by p slots, the destination slot is +// * "free" (i.e. contains an invalid value). +// */ +// for (p = 1; p <= over; p <<= 1) +// { +// int v; +// +// /* +// * In the loop below: +// * +// * - v contains the index of the final destination of +// * the value; it is recomputed dynamically based on +// * whether values are valid or not. +// * +// * - u is the index of the value we consider ("source"); +// * its address is s. +// * +// * - The loop may swap the value with the one at index +// * u-p. The address of the swap destination is d. +// */ +// v = 0; +// for (u = 0; u < m; u++) +// { +// int s, d; +// int sp, dp; +// int j, sv, dv, mk; +// +// if (u < n) +// { +// sp = 1; +// s = x + u; +// sv = srcx[s]; +// } +// else if (u < n2) +// { +// sp = 2; +// s = tt1 + u - n; +// sv = srctmp[s]; +// } +// else +// { +// sp = 3; +// s = u - n2; +// sv = tt2[s]; +// } +// +// /* +// * The value in sv should ultimately go to +// * address v, i.e. jump back by u-v slots. +// */ +// j = u - v; +// +// /* +// * We increment v for the next iteration, but +// * only if the source value is valid. The mask +// * 'mk' is -1 if the value is valid, 0 otherwise, +// * so we _subtract_ mk. +// */ +// mk = (sv >>> 15) - 1; +// v -= mk; +// +// /* +// * In this loop we consider jumps by p slots; if +// * u < p then there is nothing more to do. +// */ +// if (u < p) +// { +// continue; +// } +// +// /* +// * Destination for the swap: value at address u-p. +// */ +// if ((u - p) < n) +// { +// dp = 1; +// d = x + u - p; +// dv = srcx[d]; +// } +// else if ((u - p) < n2) +// { +// dp = 2; +// d = tt1 + (u - p) - n; +// dv = srctmp[d]; +// } +// else +// { +// dp = 3; +// d = (u - p) - n2; +// dv = tt2[d]; +// } +// +// /* +// * The swap should be performed only if the source +// * is valid AND the jump j has its 'p' bit set. +// */ +// mk &= -(((j & p) + 0x1FF) >> 9); +// if (sp == 1) +// { +// srcx[s] = (short)(sv ^ (mk & (sv ^ dv))); +// } +// else if (sp == 2) +// { +// srctmp[s] = (short)(sv ^ (mk & (sv ^ dv))); +// } +// else +// { +// tt2[s] = (short)(sv ^ (mk & (sv ^ dv))); +// } +// if (dp == 1) +// { +// srcx[d] = (short)(dv ^ (mk & (sv ^ dv))); +// } +// else if (dp == 2) +// { +// srctmp[d] = (short)(dv ^ (mk & (sv ^ dv))); +// } +// else +// { +// tt2[d] = (short)(dv ^ (mk & (sv ^ dv))); +// } +// } +// } +// } /* * Acceptance bound for the (squared) l2-norm of the signature depends * on the degree. This array is indexed by logn (1 to 10). These bounds * are _inclusive_ (they are equal to floor(beta^2)). */ - static final int l2bound[] = { + static final int[] l2bound = { 0, /* unused */ 101498, 208714, @@ -279,8 +280,7 @@ else if (dp == 2) }; /* see inner.h */ - int is_short( - short[] srcs1, int s1, short[] srcs2, int s2, int logn) + static int is_short(short[] srcs1, int s1, short[] srcs2, int logn) { /* * We use the l2-norm. Code below uses only 32-bit operations to @@ -300,7 +300,7 @@ int is_short( z = srcs1[s1 + u]; s += (z * z); ng |= s; - z = srcs2[s2 + u]; + z = srcs2[u]; s += (z * z); ng |= s; } @@ -310,8 +310,7 @@ int is_short( } /* see inner.h */ - int is_short_half( - int sqn, short[] srcs2, int s2, int logn) + static int is_short_half(int sqn, short[] srcs2, int logn) { int n, u; int ng; @@ -322,7 +321,7 @@ int is_short_half( { int z; - z = srcs2[s2 + u]; + z = srcs2[u]; sqn += (z * z); ng |= sqn; } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconConversions.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconConversions.java deleted file mode 100644 index 873236ae08..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconConversions.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.bouncycastle.pqc.crypto.falcon; - -class FalconConversions -{ - - FalconConversions() - { - } - - byte[] int_to_bytes(int x) - { - byte[] res = new byte[4]; - res[0] = (byte)(x >>> 0); - res[1] = (byte)(x >>> 8); - res[2] = (byte)(x >>> 16); - res[3] = (byte)(x >>> 24); - return res; - } - - int bytes_to_int(byte[] src, int pos) - { - int acc = 0; - acc = toUnsignedInt(src[pos + 0]) << 0 | - toUnsignedInt(src[pos + 1]) << 8 | - toUnsignedInt(src[pos + 2]) << 16 | - toUnsignedInt(src[pos + 3]) << 24; - return acc; - } - - int[] bytes_to_int_array(byte[] src, int pos, int num) - { - int[] res = new int[num]; - for (int i = 0; i < num; i++) - { - res[i] = bytes_to_int(src, pos + (4 * i)); - } - return res; - } - - byte[] long_to_bytes(long x) - { - byte[] res = new byte[8]; - res[0] = (byte)(x >>> 0); - res[1] = (byte)(x >>> 8); - res[2] = (byte)(x >>> 16); - res[3] = (byte)(x >>> 24); - res[4] = (byte)(x >>> 32); - res[5] = (byte)(x >>> 40); - res[6] = (byte)(x >>> 48); - res[7] = (byte)(x >>> 56); - return res; - } - - long bytes_to_long(byte[] src, int pos) - { - long acc = 0; - acc = toUnsignedLong(src[pos + 0]) << 0 | - toUnsignedLong(src[pos + 1]) << 8 | - toUnsignedLong(src[pos + 2]) << 16 | - toUnsignedLong(src[pos + 3]) << 24 | - toUnsignedLong(src[pos + 4]) << 32 | - toUnsignedLong(src[pos + 5]) << 40 | - toUnsignedLong(src[pos + 6]) << 48 | - toUnsignedLong(src[pos + 7]) << 56; - return acc; - } - - private int toUnsignedInt(byte b) - { - return b & 0xff; - } - - private long toUnsignedLong(byte b) - { - return b & 0xffL; - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconFFT.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconFFT.java index bbd72a54f0..adb338947a 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconFFT.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconFFT.java @@ -2,95 +2,58 @@ class FalconFFT { - FPREngine fpr; - - FalconFFT() - { - fpr = new FPREngine(); - } +// FalconFFT() +// { +// } // complex number functions - ComplexNumberWrapper FPC_ADD(FalconFPR a_re, FalconFPR a_im, FalconFPR b_re, FalconFPR b_im) - { - FalconFPR fpct_re, fpct_im; - fpct_re = fpr.fpr_add(a_re, b_re); - fpct_im = fpr.fpr_add(a_im, b_im); - return new ComplexNumberWrapper(fpct_re, fpct_im); - } - - ComplexNumberWrapper FPC_SUB(FalconFPR a_re, FalconFPR a_im, FalconFPR b_re, FalconFPR b_im) - { - FalconFPR fpct_re, fpct_im; - fpct_re = fpr.fpr_sub(a_re, b_re); - fpct_im = fpr.fpr_sub(a_im, b_im); - return new ComplexNumberWrapper(fpct_re, fpct_im); - } - - ComplexNumberWrapper FPC_MUL(FalconFPR a_re, FalconFPR a_im, FalconFPR b_re, FalconFPR b_im) - { - FalconFPR fpct_a_re, fpct_a_im; - FalconFPR fpct_b_re, fpct_b_im; - FalconFPR fpct_d_re, fpct_d_im; - fpct_a_re = (a_re); - fpct_a_im = (a_im); - fpct_b_re = (b_re); - fpct_b_im = (b_im); - fpct_d_re = fpr.fpr_sub( - fpr.fpr_mul(fpct_a_re, fpct_b_re), - fpr.fpr_mul(fpct_a_im, fpct_b_im)); - fpct_d_im = fpr.fpr_add( - fpr.fpr_mul(fpct_a_re, fpct_b_im), - fpr.fpr_mul(fpct_a_im, fpct_b_re)); - return new ComplexNumberWrapper(fpct_d_re, fpct_d_im); - } - - ComplexNumberWrapper FPC_SQR(FalconFPR a_re, FalconFPR a_im) - { - FalconFPR fpct_a_re, fpct_a_im; - FalconFPR fpct_d_re, fpct_d_im; - fpct_a_re = (a_re); - fpct_a_im = (a_im); - fpct_d_re = fpr.fpr_sub(fpr.fpr_sqr(fpct_a_re), fpr.fpr_sqr(fpct_a_im)); - fpct_d_im = fpr.fpr_double(fpr.fpr_mul(fpct_a_re, fpct_a_im)); - return new ComplexNumberWrapper(fpct_d_re, fpct_d_im); - } - - ComplexNumberWrapper FPC_INV(FalconFPR a_re, FalconFPR a_im) - { - FalconFPR fpct_a_re, fpct_a_im; - FalconFPR fpct_d_re, fpct_d_im; - FalconFPR fpct_m; - fpct_a_re = (a_re); - fpct_a_im = (a_im); - fpct_m = fpr.fpr_add(fpr.fpr_sqr(fpct_a_re), fpr.fpr_sqr(fpct_a_im)); - fpct_m = fpr.fpr_inv(fpct_m); - fpct_d_re = fpr.fpr_mul(fpct_a_re, fpct_m); - fpct_d_im = fpr.fpr_mul(fpr.fpr_neg(fpct_a_im), fpct_m); - return new ComplexNumberWrapper(fpct_d_re, fpct_d_im); - } - - ComplexNumberWrapper FPC_DIV(FalconFPR a_re, FalconFPR a_im, FalconFPR b_re, FalconFPR b_im) - { - FalconFPR fpct_a_re, fpct_a_im; - FalconFPR fpct_b_re, fpct_b_im; - FalconFPR fpct_d_re, fpct_d_im; - FalconFPR fpct_m; - fpct_a_re = (a_re); - fpct_a_im = (a_im); - fpct_b_re = (b_re); - fpct_b_im = (b_im); - fpct_m = fpr.fpr_add(fpr.fpr_sqr(fpct_b_re), fpr.fpr_sqr(fpct_b_im)); - fpct_m = fpr.fpr_inv(fpct_m); - fpct_b_re = fpr.fpr_mul(fpct_b_re, fpct_m); - fpct_b_im = fpr.fpr_mul(fpr.fpr_neg(fpct_b_im), fpct_m); - fpct_d_re = fpr.fpr_sub( - fpr.fpr_mul(fpct_a_re, fpct_b_re), - fpr.fpr_mul(fpct_a_im, fpct_b_im)); - fpct_d_im = fpr.fpr_add( - fpr.fpr_mul(fpct_a_re, fpct_b_im), - fpr.fpr_mul(fpct_a_im, fpct_b_re)); - return new ComplexNumberWrapper(fpct_d_re, fpct_d_im); - } +// static ComplexNumberWrapper FPC_ADD(double a_re, double a_im, double b_re, double b_im) +// { +// return new ComplexNumberWrapper(a_re + b_re, a_im + b_im); +// } +// +// static ComplexNumberWrapper FPC_SUB(double a_re, double a_im, double b_re, double b_im) +// { +// return new ComplexNumberWrapper(a_re - b_re, a_im - b_im); +// } + +// static ComplexNumberWrapper FPC_MUL(double a_re, double a_im, double b_re, double b_im) +// { +// return new ComplexNumberWrapper(a_re * b_re - a_im * b_im, a_re * b_im + a_im * b_re); +// } + +// ComplexNumberWrapper FPC_SQR(double a_re, double a_im) +// { +// double fpct_a_re, fpct_a_im; +// double fpct_d_re, fpct_d_im; +// fpct_a_re = (a_re); +// fpct_a_im = (a_im); +// fpct_d_re = FPREngine.fpr_sub(FPREngine.fpr_sqr(fpct_a_re), FPREngine.fpr_sqr(fpct_a_im)); +// fpct_d_im = FPREngine.fpr_double(FPREngine.fpr_mul(fpct_a_re, fpct_a_im)); +// return new ComplexNumberWrapper(fpct_d_re, fpct_d_im); +// } + +// ComplexNumberWrapper FPC_INV(double a_re, double a_im) +// { +// double fpct_a_re, fpct_a_im; +// double fpct_d_re, fpct_d_im; +// double fpct_m; +// fpct_a_re = (a_re); +// fpct_a_im = (a_im); +// fpct_m = FPREngine.fpr_add(FPREngine.fpr_sqr(fpct_a_re), FPREngine.fpr_sqr(fpct_a_im)); +// fpct_m = FPREngine.fpr_inv(fpct_m); +// fpct_d_re = FPREngine.fpr_mul(fpct_a_re, fpct_m); +// fpct_d_im = FPREngine.fpr_mul(FPREngine.fpr_neg(fpct_a_im), fpct_m); +// return new ComplexNumberWrapper(fpct_d_re, fpct_d_im); +// } + +// static ComplexNumberWrapper FPC_DIV(double a_re, double a_im, double b_re, double b_im) +// { +// double fpct_m = 1.0 / (b_re * b_re + b_im * b_im); +// b_re = b_re * fpct_m; +// b_im = -b_im * fpct_m; +// return new ComplexNumberWrapper(a_re * b_re - a_im * b_im, a_re * b_im + a_im * b_re); +// } /* * Let w = exp(i*pi/N); w is a primitive 2N-th root of 1. We define the @@ -117,7 +80,7 @@ ComplexNumberWrapper FPC_DIV(FalconFPR a_re, FalconFPR a_im, FalconFPR b_re, Fal */ /* see inner.h */ - void FFT(FalconFPR[] srcf, int f, int logn) + static void FFT(double[] srcf, int f, int logn) { /* * FFT algorithm in bit-reversal order uses the following @@ -165,41 +128,33 @@ void FFT(FalconFPR[] srcf, int f, int logn) n = 1 << logn; hn = n >> 1; t = hn; + int ht, hm, i1, j1; + int j2, fj, fjhn, fjht, fjhthn; + double s_re, s_im; + double x_re, x_im, y_re, y_im, a_re, a_im; for (u = 1, m = 2; u < logn; u++, m <<= 1) { - int ht, hm, i1, j1; - ht = t >> 1; hm = m >> 1; for (i1 = 0, j1 = 0; i1 < hm; i1++, j1 += t) { - int j, j2; - - j2 = j1 + ht; - FalconFPR s_re, s_im; - - s_re = fpr.fpr_gm_tab[((m + i1) << 1) + 0]; - s_im = fpr.fpr_gm_tab[((m + i1) << 1) + 1]; - for (j = j1; j < j2; j++) + j2 = j1 + ht + f; + fj = ((m + i1) << 1); + s_re = FPREngine.fpr_gm_tab[fj]; + s_im = FPREngine.fpr_gm_tab[fj + 1]; + for (fj = f + j1, fjhn = fj + hn, fjht = fj + ht, fjhthn = fjht + hn; fj < j2; + fj++, fjhn++, fjht++, fjhthn++) { - FalconFPR x_re, x_im, y_re, y_im; - ComplexNumberWrapper res; - - x_re = srcf[f + j]; - x_im = srcf[f + j + hn]; - y_re = srcf[f + j + ht]; - y_im = srcf[f + j + ht + hn]; - res = FPC_MUL(y_re, y_im, s_re, s_im); - y_re = res.re; - y_im = res.im; - - res = FPC_ADD(x_re, x_im, y_re, y_im); - srcf[f + j] = res.re; - srcf[f + j + hn] = res.im; - - res = FPC_SUB(x_re, x_im, y_re, y_im); - srcf[f + j + ht] = res.re; - srcf[f + j + ht + hn] = res.im; + x_re = srcf[fj]; + x_im = srcf[fjhn]; + a_re = srcf[fjht]; + a_im = srcf[fjhthn]; + y_re = a_re * s_re - a_im * s_im; + y_im = a_re * s_im + a_im * s_re; + srcf[fj] = x_re + y_re; + srcf[fjhn] = x_im + y_im; + srcf[fjht] = x_re - y_re; + srcf[fjhthn] = x_im - y_im; } } t = ht; @@ -207,7 +162,7 @@ void FFT(FalconFPR[] srcf, int f, int logn) } /* see inner.h */ - void iFFT(FalconFPR[] srcf, int f, int logn) + static void iFFT(double[] srcf, int f, int logn) { /* * Inverse FFT algorithm in bit-reversal order uses the following @@ -252,46 +207,37 @@ void iFFT(FalconFPR[] srcf, int f, int logn) * division into a division by N/2, not N. */ int u, n, hn, t, m; - + int dt, hm, i1, j1; + int j2, fj, fjhn, fjt, fjthn; + double s_re, s_im; + double x_re, x_im, y_re, y_im; n = 1 << logn; t = 1; m = n; hn = n >> 1; for (u = logn; u > 1; u--) { - int hm, dt, i1, j1; - hm = m >> 1; dt = t << 1; for (i1 = 0, j1 = 0; j1 < hn; i1++, j1 += dt) { - int j, j2; - - j2 = j1 + t; - FalconFPR s_re, s_im; - - s_re = fpr.fpr_gm_tab[((hm + i1) << 1) + 0]; - s_im = fpr.fpr_neg(fpr.fpr_gm_tab[((hm + i1) << 1) + 1]); - for (j = j1; j < j2; j++) + j2 = j1 + t + f; + fj = (hm + i1) << 1; + s_re = FPREngine.fpr_gm_tab[fj]; + s_im = -FPREngine.fpr_gm_tab[fj + 1]; + for (fj = f + j1, fjhn = fj + hn, fjt = fj + t, fjthn = fjt + hn; fj < j2; + fj++, fjhn++, fjt++, fjthn++) { - FalconFPR x_re, x_im, y_re, y_im; - ComplexNumberWrapper res; - - x_re = srcf[f + j]; - x_im = srcf[f + j + hn]; - y_re = srcf[f + j + t]; - y_im = srcf[f + j + t + hn]; - res = FPC_ADD(x_re, x_im, y_re, y_im); - srcf[f + j] = res.re; - srcf[f + j + hn] = res.im; - - res = FPC_SUB(x_re, x_im, y_re, y_im); - x_re = res.re; - x_im = res.im; - - res = FPC_MUL(x_re, x_im, s_re, s_im); - srcf[f + j + t] = res.re; - srcf[f + j + t + hn] = res.im; + x_re = srcf[fj]; + x_im = srcf[fjhn]; + y_re = srcf[fjt]; + y_im = srcf[fjthn]; + srcf[fj] = x_re + y_re; + srcf[fjhn] = x_im + y_im; + x_re -= y_re; + x_im -= y_im; + srcf[fjt] = x_re * s_re - x_im * s_im; + srcf[fjthn] = x_re * s_im + x_im * s_re; } } t = dt; @@ -304,114 +250,108 @@ void iFFT(FalconFPR[] srcf, int f, int logn) */ if (logn > 0) { - FalconFPR ni; + double ni; - ni = fpr.fpr_p2_tab[logn]; + ni = FPREngine.fpr_p2_tab[logn]; for (u = 0; u < n; u++) { - srcf[f + u] = fpr.fpr_mul(srcf[f + u], ni); + srcf[f + u] = srcf[f + u] * ni; } } } /* see inner.h */ - void poly_add( - FalconFPR[] srca, int a, FalconFPR[] srcb, int b, int logn) + static void poly_add( + double[] srca, int a, double[] srcb, int b, int logn) { int n, u; n = 1 << logn; for (u = 0; u < n; u++) { - srca[a + u] = fpr.fpr_add(srca[a + u], srcb[b + u]); + srca[a + u] += srcb[b + u]; } } /* see inner.h */ - void poly_sub( - FalconFPR[] srca, int a, FalconFPR[] srcb, int b, int logn) + static void poly_sub( + double[] srca, int a, double[] srcb, int b, int logn) { int n, u; n = 1 << logn; for (u = 0; u < n; u++) { - srca[a + u] = fpr.fpr_sub(srca[a + u], srcb[b + u]); + srca[a + u] -= srcb[b + u]; } } /* see inner.h */ - void poly_neg(FalconFPR[] srca, int a, int logn) + static void poly_neg(double[] srca, int a, int logn) { int n, u; n = 1 << logn; for (u = 0; u < n; u++) { - srca[a + u] = fpr.fpr_neg(srca[a + u]); + srca[a + u] = -srca[a + u]; } } /* see inner.h */ - void poly_adj_fft(FalconFPR[] srca, int a, int logn) + static void poly_adj_fft(double[] srca, int a, int logn) { int n, u; n = 1 << logn; for (u = (n >> 1); u < n; u++) { - srca[a + u] = fpr.fpr_neg(srca[a + u]); + srca[a + u] = -srca[a + u]; } } /* see inner.h */ - void poly_mul_fft( - FalconFPR[] srca, int a, FalconFPR[] srcb, int b, int logn) + static void poly_mul_fft( + double[] srca, int a, double[] srcb, int b, int logn) { int n, hn, u; n = 1 << logn; hn = n >> 1; - for (u = 0; u < hn; u++) + double a_re, a_im, b_re, b_im; + int au, auhn, bu; + for (u = 0, au = a, auhn = a + hn, bu = b; u < hn; u++, au++, bu++, auhn++) { - FalconFPR a_re, a_im, b_re, b_im; - ComplexNumberWrapper res; - - a_re = srca[a + u]; - a_im = srca[a + u + hn]; - b_re = srcb[b + u]; - b_im = srcb[b + u + hn]; - res = FPC_MUL(a_re, a_im, b_re, b_im); - srca[a + u] = res.re; - srca[a + u + hn] = res.im; + a_re = srca[au]; + a_im = srca[auhn]; + b_re = srcb[bu]; + b_im = srcb[bu + hn]; + srca[au] = a_re * b_re - a_im * b_im; + srca[auhn] = a_re * b_im + a_im * b_re; } } /* see inner.h */ - void poly_muladj_fft( - FalconFPR[] srca, int a, FalconFPR[] srcb, int b, int logn) + static void poly_muladj_fft( + double[] srca, int a, double[] srcb, int b, int logn) { - int n, hn, u; - + int n, hn, u, au; + double a_re, a_im, b_re, b_im; n = 1 << logn; hn = n >> 1; - for (u = 0; u < hn; u++) + for (u = 0, au = a; u < hn; u++, au++) { - FalconFPR a_re, a_im, b_re, b_im; - ComplexNumberWrapper res; - - a_re = srca[a + u]; - a_im = srca[a + u + hn]; + a_re = srca[au]; + a_im = srca[au + hn]; b_re = srcb[b + u]; - b_im = fpr.fpr_neg(srcb[b + u + hn]); - res = FPC_MUL(a_re, a_im, b_re, b_im); - srca[a + u] = res.re; - srca[a + u + hn] = res.im; + b_im = srcb[b + u + hn]; + srca[au] = a_re * b_re + a_im * b_im; + srca[au + hn] = a_im * b_re - a_re * b_im; } } /* see inner.h */ - void poly_mulselfadj_fft(FalconFPR[] srca, int a, int logn) + static void poly_mulselfadj_fft(double[] srca, int a, int logn) { /* * Since each coefficient is multiplied with its own conjugate, @@ -423,113 +363,108 @@ void poly_mulselfadj_fft(FalconFPR[] srca, int a, int logn) hn = n >> 1; for (u = 0; u < hn; u++) { - FalconFPR a_re, a_im; - ComplexNumberWrapper res; + double a_re, a_im; + //ComplexNumberWrapper res; a_re = srca[a + u]; a_im = srca[a + u + hn]; - srca[a + u] = fpr.fpr_add(fpr.fpr_sqr(a_re), fpr.fpr_sqr(a_im)); - srca[a + u + hn] = fpr.fpr_zero; + srca[a + u] = a_re * a_re + a_im * a_im; + srca[a + u + hn] = FPREngine.fpr_zero; } } /* see inner.h */ - void poly_mulconst(FalconFPR[] srca, int a, FalconFPR x, int logn) + static void poly_mulconst(double[] srca, int a, double x, int logn) { int n, u; n = 1 << logn; for (u = 0; u < n; u++) { - srca[a + u] = fpr.fpr_mul(srca[a + u], x); + srca[a + u] = srca[a + u] * x; } } /* see inner.h */ - void poly_div_fft( - FalconFPR[] srca, int a, FalconFPR[] srcb, int b, int logn) - { - int n, hn, u; - - n = 1 << logn; - hn = n >> 1; - for (u = 0; u < hn; u++) - { - FalconFPR a_re, a_im, b_re, b_im; - ComplexNumberWrapper res; - - a_re = srca[a + u]; - a_im = srca[a + u + hn]; - b_re = srcb[b + u]; - b_im = srcb[b + u + hn]; - res = FPC_DIV(a_re, a_im, b_re, b_im); - srca[a + u] = res.re; - srca[a + u + hn] = res.im; - } - } +// void poly_div_fft( +// double[] srca, int a, double[] srcb, int b, int logn) +// { +// int n, hn, u; +// +// n = 1 << logn; +// hn = n >> 1; +// for (u = 0; u < hn; u++) +// { +// double a_re, a_im, b_re, b_im; +// ComplexNumberWrapper res; +// +// a_re = srca[a + u]; +// a_im = srca[a + u + hn]; +// b_re = srcb[b + u]; +// b_im = srcb[b + u + hn]; +// res = FPC_DIV(a_re, a_im, b_re, b_im); +// srca[a + u] = res.re; +// srca[a + u + hn] = res.im; +// } +// } /* see inner.h */ - void poly_invnorm2_fft(FalconFPR[] srcd, int d, - FalconFPR[] srca, int a, FalconFPR[] srcb, int b, int logn) + static void poly_invnorm2_fft(double[] srcd, int d, + double[] srca, int a, double[] srcb, int b, int logn) { int n, hn, u; - + double a_re, a_im; + double b_re, b_im; n = 1 << logn; hn = n >> 1; for (u = 0; u < hn; u++) { - FalconFPR a_re, a_im; - FalconFPR b_re, b_im; + a_re = srca[a + u]; a_im = srca[a + u + hn]; b_re = srcb[b + u]; b_im = srcb[b + u + hn]; - srcd[d + u] = fpr.fpr_inv(fpr.fpr_add( - fpr.fpr_add(fpr.fpr_sqr(a_re), fpr.fpr_sqr(a_im)), - fpr.fpr_add(fpr.fpr_sqr(b_re), fpr.fpr_sqr(b_im)))); + srcd[d + u] = 1.0 / (a_re * a_re + a_im * a_im + + b_re * b_re + b_im * b_im); } } /* see inner.h */ - void poly_add_muladj_fft(FalconFPR[] srcd, int d, - FalconFPR[] srcF, int F, FalconFPR[] srcG, int G, - FalconFPR[] srcf, int f, FalconFPR[] srcg, int g, int logn) + static void poly_add_muladj_fft(double[] srcd, + double[] srcF, double[] srcG, + double[] srcf, double[] srcg, int logn) { int n, hn, u; - + double F_re, F_im, G_re, G_im; + double f_re, f_im, g_re, g_im; + double a_re, a_im, b_re, b_im; n = 1 << logn; hn = n >> 1; for (u = 0; u < hn; u++) { - FalconFPR F_re, F_im, G_re, G_im; - FalconFPR f_re, f_im, g_re, g_im; - FalconFPR a_re, a_im, b_re, b_im; - ComplexNumberWrapper res; - - F_re = srcF[F + u]; - F_im = srcF[F + u + hn]; - G_re = srcG[G + u]; - G_im = srcG[G + u + hn]; - f_re = srcf[f + u]; - f_im = srcf[f + u + hn]; - g_re = srcg[g + u]; - g_im = srcg[g + u + hn]; - - res = FPC_MUL(F_re, F_im, f_re, fpr.fpr_neg(f_im)); - a_re = res.re; - a_im = res.im; - res = FPC_MUL(G_re, G_im, g_re, fpr.fpr_neg(g_im)); - b_re = res.re; - b_im = res.im; - srcd[d + u] = fpr.fpr_add(a_re, b_re); - srcd[d + u + hn] = fpr.fpr_add(a_im, b_im); + int uhn = u + hn; + F_re = srcF[u]; + F_im = srcF[uhn]; + G_re = srcG[u]; + G_im = srcG[uhn]; + f_re = srcf[u]; + f_im = srcf[uhn]; + g_re = srcg[u]; + g_im = srcg[uhn]; + + a_re = F_re * f_re + F_im * f_im; + a_im = F_im * f_re - F_re * f_im; + b_re = G_re * g_re + G_im * g_im; + b_im = G_im * g_re - G_re * g_im; + srcd[u] = a_re + b_re; + srcd[uhn] = a_im + b_im; } } /* see inner.h */ - void poly_mul_autoadj_fft( - FalconFPR[] srca, int a, FalconFPR[] srcb, int b, int logn) + static void poly_mul_autoadj_fft( + double[] srca, int a, double[] srcb, int b, int logn) { int n, hn, u; @@ -537,14 +472,14 @@ void poly_mul_autoadj_fft( hn = n >> 1; for (u = 0; u < hn; u++) { - srca[a + u] = fpr.fpr_mul(srca[a + u], srcb[b + u]); - srca[a + u + hn] = fpr.fpr_mul(srca[a + u + hn], srcb[b + u]); + srca[a + u] *= srcb[b + u]; + srca[a + u + hn] *= srcb[b + u]; } } /* see inner.h */ - void poly_div_autoadj_fft( - FalconFPR[] srca, int a, FalconFPR[] srcb, int b, int logn) + static void poly_div_autoadj_fft( + double[] srca, int a, double[] srcb, int b, int logn) { int n, hn, u; @@ -552,89 +487,85 @@ void poly_div_autoadj_fft( hn = n >> 1; for (u = 0; u < hn; u++) { - FalconFPR ib; - - ib = fpr.fpr_inv(srcb[b + u]); - srca[a + u] = fpr.fpr_mul(srca[a + u], ib); - srca[a + u + hn] = fpr.fpr_mul(srca[a + u + hn], ib); + double ib = 1.0 / srcb[b + u]; + srca[a + u] *= ib; + srca[a + u + hn] *= ib; } } /* see inner.h */ - void poly_LDL_fft( - FalconFPR[] srcg00, int g00, - FalconFPR[] srcg01, int g01, FalconFPR[] srcg11, int g11, int logn) + static void poly_LDL_fft( + double[] srcg00, int g00, + double[] srcg01, int g01, double[] srcg11, int g11, int logn) { - int n, hn, u; - + int n, hn, u, uhn, g01u, g01uhn; + double g00_re, g00_im, g01_re, g01_im, g11_re, g11_im; n = 1 << logn; hn = n >> 1; - for (u = 0; u < hn; u++) + for (u = 0, uhn = hn, g01u = g01, g01uhn = g01 + hn; + u < hn; u++, uhn++, g01u++, g01uhn++) { - FalconFPR g00_re, g00_im, g01_re, g01_im, g11_re, g11_im; - FalconFPR mu_re, mu_im; - ComplexNumberWrapper res; - g00_re = srcg00[g00 + u]; - g00_im = srcg00[g00 + u + hn]; - g01_re = srcg01[g01 + u]; - g01_im = srcg01[g01 + u + hn]; - g11_re = srcg11[g11 + u]; - g11_im = srcg11[g11 + u + hn]; - res = FPC_DIV(g01_re, g01_im, g00_re, g00_im); - mu_re = res.re; - mu_im = res.im; - res = FPC_MUL(mu_re, mu_im, g01_re, fpr.fpr_neg(g01_im)); - g01_re = res.re; - g01_im = res.im; - res = FPC_SUB(g11_re, g11_im, g01_re, g01_im); - srcg11[g11 + u] = res.re; - srcg11[g11 + u + hn] = res.im; - srcg01[g01 + u] = mu_re; - srcg01[g01 + u + hn] = fpr.fpr_neg(mu_im); + g00_im = srcg00[g00 + uhn]; + g01_re = srcg01[g01u]; + g01_im = srcg01[g01uhn]; + + g11_im = 1.0 / (g00_re * g00_re + g00_im * g00_im); + g11_re = g00_re * g11_im; + g11_im *= -g00_im; + g00_re = g01_re * g11_re - g01_im * g11_im; + g00_im = g01_re * g11_im + g01_im * g11_re; + g11_re = g01_re; + g11_im = g01_im; + g01_re = g00_re * g11_re + g00_im * g11_im; + g01_im = g00_re * -g11_im + g00_im * g11_re; + srcg11[g11 + u] -= g01_re; + srcg11[g11 + uhn] -= g01_im; + srcg01[g01u] = g00_re; + srcg01[g01uhn] = -g00_im; } } /* see inner.h */ - void poly_LDLmv_fft( - FalconFPR[] srcd11, int d11, FalconFPR[] srcl10, int l10, - FalconFPR[] srcg00, int g00, FalconFPR[] srcg01, int g01, - FalconFPR[] srcg11, int g11, int logn) - { - int n, hn, u; - - n = 1 << logn; - hn = n >> 1; - for (u = 0; u < hn; u++) - { - FalconFPR g00_re, g00_im, g01_re, g01_im, g11_re, g11_im; - FalconFPR mu_re, mu_im; - ComplexNumberWrapper res; - - g00_re = srcg00[g00 + u]; - g00_im = srcg00[g00 + u + hn]; - g01_re = srcg01[g01 + u]; - g01_im = srcg01[g01 + u + hn]; - g11_re = srcg11[g11 + u]; - g11_im = srcg11[g11 + u + hn]; - res = FPC_DIV(g01_re, g01_im, g00_re, g00_im); - mu_re = res.re; - mu_im = res.im; - res = FPC_MUL(mu_re, mu_im, g01_re, fpr.fpr_neg(g01_im)); - g01_re = res.re; - g01_im = res.im; - res = FPC_SUB(g11_re, g11_im, g01_re, g01_im); - srcd11[d11 + u] = res.re; - srcd11[d11 + u + hn] = res.im; - srcl10[l10 + u] = mu_re; - srcl10[l10 + u + hn] = fpr.fpr_neg(mu_im); - } - } +// void poly_LDLmv_fft( +// double[] srcd11, int d11, double[] srcl10, int l10, +// double[] srcg00, int g00, double[] srcg01, int g01, +// double[] srcg11, int g11, int logn) +// { +// int n, hn, u; +// +// n = 1 << logn; +// hn = n >> 1; +// for (u = 0; u < hn; u++) +// { +// double g00_re, g00_im, g01_re, g01_im, g11_re, g11_im; +// double mu_re, mu_im; +// ComplexNumberWrapper res; +// +// g00_re = srcg00[g00 + u]; +// g00_im = srcg00[g00 + u + hn]; +// g01_re = srcg01[g01 + u]; +// g01_im = srcg01[g01 + u + hn]; +// g11_re = srcg11[g11 + u]; +// g11_im = srcg11[g11 + u + hn]; +// res = FPC_DIV(g01_re, g01_im, g00_re, g00_im); +// mu_re = res.re; +// mu_im = res.im; +// res = FPC_MUL(mu_re, mu_im, g01_re, FPREngine.fpr_neg(g01_im)); +// g01_re = res.re; +// g01_im = res.im; +// res = FPC_SUB(g11_re, g11_im, g01_re, g01_im); +// srcd11[d11 + u] = res.re; +// srcd11[d11 + u + hn] = res.im; +// srcl10[l10 + u] = mu_re; +// srcl10[l10 + u + hn] = FPREngine.fpr_neg(mu_im); +// } +// } /* see inner.h */ - void poly_split_fft( - FalconFPR[] srcf0, int f0, FalconFPR[] srcf1, int f1, - FalconFPR[] srcf, int f, int logn) + static void poly_split_fft( + double[] srcf0, int f0, double[] srcf1, int f1, + double[] srcf, int f, int logn) { /* * The FFT representation we use is in bit-reversed order @@ -643,57 +574,52 @@ void poly_split_fft( * indexes with regards to the Falcon specification. */ int n, hn, qn, u; - + double a_re, a_im, b_re, b_im; + double t_re, t_im; n = 1 << logn; hn = n >> 1; qn = hn >> 1; - + int idx; /* * We process complex values by pairs. For logn = 1, there is only * one complex value (the other one is the implicit conjugate), * so we add the two lines below because the loop will be * skipped. */ - srcf0[f0 + 0] = srcf[f + 0]; - srcf1[f1 + 0] = srcf[f + hn]; + srcf0[f0] = srcf[f]; + srcf1[f1] = srcf[f + hn]; for (u = 0; u < qn; u++) { - FalconFPR a_re, a_im, b_re, b_im; - FalconFPR t_re, t_im; - ComplexNumberWrapper res; - - a_re = srcf[f + (u << 1) + 0]; - a_im = srcf[f + (u << 1) + 0 + hn]; - b_re = srcf[f + (u << 1) + 1]; - b_im = srcf[f + (u << 1) + 1 + hn]; - - res = FPC_ADD(a_re, a_im, b_re, b_im); - t_re = res.re; - t_im = res.im; - srcf0[f0 + u] = fpr.fpr_half(t_re); - srcf0[f0 + u + qn] = fpr.fpr_half(t_im); - - res = FPC_SUB(a_re, a_im, b_re, b_im); - t_re = res.re; - t_im = res.im; - res = FPC_MUL(t_re, t_im, - fpr.fpr_gm_tab[((u + hn) << 1) + 0], - fpr.fpr_neg(fpr.fpr_gm_tab[((u + hn) << 1) + 1])); - t_re = res.re; - t_im = res.im; - srcf1[f1 + u] = fpr.fpr_half(t_re); - srcf1[f1 + u + qn] = fpr.fpr_half(t_im); + idx = f + (u << 1); + a_re = srcf[idx]; + a_im = srcf[idx++ + hn]; + b_re = srcf[idx]; + b_im = srcf[idx + hn]; + + srcf0[f0 + u] = (a_re + b_re) * 0.5; + srcf0[f0 + u + qn] = (a_im + b_im) * 0.5; + + t_re = a_re - b_re; + t_im = a_im - b_im; + + idx = ((u + hn) << 1); + b_re = FPREngine.fpr_gm_tab[idx]; + b_im = -FPREngine.fpr_gm_tab[idx + 1]; + idx = f1 + u; + srcf1[idx] = (t_re * b_re - t_im * b_im) * 0.5; + srcf1[idx + qn] = (t_re * b_im + t_im * b_re) * 0.5; } } /* see inner.h */ - void poly_merge_fft( - FalconFPR[] srcf, int f, - FalconFPR[] srcf0, int f0, FalconFPR[] srcf1, int f1, int logn) + static void poly_merge_fft( + double[] srcf, int f, + double[] srcf0, int f0, double[] srcf1, int f1, int logn) { - int n, hn, qn, u; - + int n, hn, qn, u, idx; + double a_re, a_im, b_re, b_im; + double t_re, t_im; n = 1 << logn; hn = n >> 1; qn = hn >> 1; @@ -701,32 +627,30 @@ void poly_merge_fft( /* * An extra copy to handle the special case logn = 1. */ - srcf[f + 0] = srcf0[f0 + 0]; - srcf[f + hn] = srcf1[f1 + 0]; + srcf[f] = srcf0[f0]; + srcf[f + hn] = srcf1[f1]; for (u = 0; u < qn; u++) { - FalconFPR a_re, a_im, b_re, b_im; - FalconFPR t_re, t_im; - ComplexNumberWrapper res; - - a_re = srcf0[f0 + u]; - a_im = srcf0[f0 + u + qn]; - res = FPC_MUL(srcf1[f1 + u], srcf1[f1 + u + qn], - fpr.fpr_gm_tab[((u + hn) << 1) + 0], - fpr.fpr_gm_tab[((u + hn) << 1) + 1]); - b_re = res.re; - b_im = res.im; - res = FPC_ADD(a_re, a_im, b_re, b_im); - t_re = res.re; - t_im = res.im; - srcf[f + (u << 1) + 0] = t_re; - srcf[f + (u << 1) + 0 + hn] = t_im; - res = FPC_SUB(a_re, a_im, b_re, b_im); - t_re = res.re; - t_im = res.im; - srcf[f + (u << 1) + 1] = t_re; - srcf[f + (u << 1) + 1 + hn] = t_im; + idx = f1 + u; + a_re = srcf1[idx]; + a_im = srcf1[idx + qn]; + idx = ((u + hn) << 1); + t_re = FPREngine.fpr_gm_tab[idx]; + t_im = FPREngine.fpr_gm_tab[idx + 1]; + b_re = a_re * t_re - a_im * t_im; + b_im = a_re * t_im + a_im * t_re; + + idx = f0 + u; + a_re = srcf0[idx]; + a_im = srcf0[idx + qn]; + + idx = f + (u << 1); + srcf[idx] = a_re + b_re; + srcf[idx++ + hn] = a_im + b_im; + + srcf[idx] = a_re - b_re; + srcf[idx + hn] = a_im - b_im; } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconFPR.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconFPR.java deleted file mode 100644 index ec1f90343a..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconFPR.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.bouncycastle.pqc.crypto.falcon; - -class FalconFPR -{ - double v; - - FalconFPR(double v) - { - this.v = v; - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconKeyGen.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconKeyGen.java index 2932bc8c27..d1b26add7b 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconKeyGen.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconKeyGen.java @@ -1,20 +1,23 @@ package org.bouncycastle.pqc.crypto.falcon; +import org.bouncycastle.crypto.digests.SHAKEDigest; +import org.bouncycastle.util.Pack; + class FalconKeyGen { - FPREngine fpr; - FalconSmallPrimeList primes; - FalconFFT fft; - FalconCodec codec; - FalconVrfy vrfy; + // FPREngine fpr; + //FalconSmallPrimeList primes; + //FalconFFT fft; +// FalconCodec codec; +// FalconVrfy vrfy; FalconKeyGen() { - this.fpr = new FPREngine(); - this.primes = new FalconSmallPrimeList(); - this.fft = new FalconFFT(); - this.codec = new FalconCodec(); - this.vrfy = new FalconVrfy(); +// this.fpr = new FPREngine(); + //this.primes = new FalconSmallPrimeList(); + //this.fft = new FalconFFT(); +// this.codec = new FalconCodec(); +// this.vrfy = new FalconVrfy(); } private static int mkn(int logn) @@ -26,11 +29,9 @@ private static int mkn(int logn) * Reduce a small signed integer modulo a small prime. The source * value x MUST be such that -p < x < p. */ - int modp_set(int x, int p) + private static int modp_set(int x, int p) { - int w; - - w = x; + int w = x; w += p & -(w >>> 31); return w; } @@ -38,7 +39,7 @@ int modp_set(int x, int p) /* * Normalize a modular integer around 0. */ - int modp_norm(int x, int p) + private static int modp_norm(int x, int p) { return (x - (p & (((x - ((p + 1) >>> 1)) >>> 31) - 1))); } @@ -47,11 +48,9 @@ int modp_norm(int x, int p) * Compute -1/p mod 2^31. This works for all odd integers p that fit * on 31 bits. */ - int modp_ninv31(int p) + private static int modp_ninv31(int p) { - int y; - - y = 2 - p; + int y = 2 - p; y *= 2 - p * y; y *= 2 - p * y; y *= 2 - p * y; @@ -62,7 +61,7 @@ int modp_ninv31(int p) /* * Compute R = 2^31 mod p. */ - int modp_R(int p) + private static int modp_R(int p) { /* * Since 2^30 < p < 2^31, we know that 2^31 mod p is simply @@ -74,11 +73,9 @@ int modp_R(int p) /* * Addition modulo p. */ - int modp_add(int a, int b, int p) + private static int modp_add(int a, int b, int p) { - int d; - - d = a + b - p; + int d = a + b - p; d += p & -(d >>> 31); return d; } @@ -86,11 +83,9 @@ int modp_add(int a, int b, int p) /* * Subtraction modulo p. */ - int modp_sub(int a, int b, int p) + private static int modp_sub(int a, int b, int p) { - int d; - - d = a - b; + int d = a - b; d += p & -(d >>> 31); return d; } @@ -99,13 +94,13 @@ int modp_sub(int a, int b, int p) * Montgomery multiplication modulo p. The 'p0i' value is -1/p mod 2^31. * It is required that p is an odd integer. */ - int modp_montymul(int a, int b, int p, int p0i) + private static int modp_montymul(int a, int b, int p, int p0i) { long z, w; int d; z = toUnsignedLong(a) * toUnsignedLong(b); - w = ((z * p0i) & toUnsignedLong(0x7FFFFFFF)) * p; + w = ((z * p0i) & 0x7FFFFFFFL) * p; d = (int)((z + w) >>> 31) - p; d += p & -(d >>> 31); return d; @@ -114,7 +109,7 @@ int modp_montymul(int a, int b, int p, int p0i) /* * Compute R2 = 2^62 mod p. */ - int modp_R2(int p, int p0i) + private static int modp_R2(int p, int p0i) { int z; @@ -147,7 +142,7 @@ int modp_R2(int p, int p0i) * p must be prime such that 2^30 < p < 2^31; p0i must be equal to * -1/p mod 2^31; R2 must be equal to 2^62 mod p. */ - int modp_Rx(int x, int p, int p0i, int R2) + private static int modp_Rx(int x, int p, int p0i, int R2) { int i; int r, z; @@ -181,7 +176,7 @@ int modp_Rx(int x, int p, int p0i, int R2) * p0i -1/p mod 2^31 * R 2^31 mod R */ - int modp_div(int a, int b, int p, int p0i, int R) + private static int modp_div(int a, int b, int p, int p0i, int R) { int z, e; int i; @@ -215,7 +210,7 @@ int modp_div(int a, int b, int p, int p0i, int R) /* * Bit-reversal index table. */ - private short REV10[] = { + private static final short[] REV10 = { 0, 512, 256, 768, 128, 640, 384, 896, 64, 576, 320, 832, 192, 704, 448, 960, 32, 544, 288, 800, 160, 672, 416, 928, 96, 608, 352, 864, 224, 736, 480, 992, 16, 528, 272, 784, @@ -317,8 +312,8 @@ int modp_div(int a, int b, int p, int p0i, int R) * * p must be a prime such that p = 1 mod 2048. */ - void modp_mkgm2(int[] srcgm, int gm, int[] srcigm, int igm, int logn, - int g, int p, int p0i) + private static void modp_mkgm2(int[] srcgm, int gm, int[] srcigm, int igm, int logn, + int g, int p, int p0i) { int u, n; int k; @@ -356,8 +351,8 @@ void modp_mkgm2(int[] srcgm, int gm, int[] srcigm, int igm, int logn, * Compute the NTT over a polynomial (binary case). Polynomial elements * are a[0], a[stride], a[2 * stride]... */ - void modp_NTT2_ext(int[] srca, int a, int stride, int[] srcgm, int gm, int logn, - int p, int p0i) + private static void modp_NTT2_ext(int[] srca, int a, int stride, int[] srcgm, int gm, int logn, + int p, int p0i) { int t, m, n; @@ -398,8 +393,8 @@ void modp_NTT2_ext(int[] srca, int a, int stride, int[] srcgm, int gm, int logn, /* * Compute the inverse NTT over a polynomial (binary case). */ - void modp_iNTT2_ext(int[] srca, int a, int stride, int[] srcigm, int igm, int logn, - int p, int p0i) + private static void modp_iNTT2_ext(int[] srca, int a, int stride, int[] srcigm, int igm, int logn, + int p, int p0i) { int t, m, n, k; int ni; @@ -435,7 +430,6 @@ void modp_iNTT2_ext(int[] srca, int a, int stride, int[] srcigm, int igm, int lo srca[r1] = modp_add(x, y, p); srca[r2] = modp_montymul( modp_sub(x, y, p), s, p, p0i); - ; } } t = dt; @@ -458,13 +452,13 @@ void modp_iNTT2_ext(int[] srca, int a, int stride, int[] srcigm, int igm, int lo * are consecutive in RAM. */ // #define modp_NTT2(a, gm, logn, p, p0i) modp_NTT2_ext(a, 1, gm, logn, p, p0i) - void modp_NTT2(int[] srca, int a, int[] srcgm, int gm, int logn, int p, int p0i) + private static void modp_NTT2(int[] srca, int a, int[] srcgm, int gm, int logn, int p, int p0i) { modp_NTT2_ext(srca, a, 1, srcgm, gm, logn, p, p0i); } // #define modp_iNTT2(a, igm, logn, p, p0i) modp_iNTT2_ext(a, 1, igm, logn, p, p0i) - void modp_iNTT2(int[] srca, int a, int[] srcigm, int igm, int logn, int p, int p0i) + private static void modp_iNTT2(int[] srca, int a, int[] srcigm, int igm, int logn, int p, int p0i) { modp_iNTT2_ext(srca, a, 1, srcigm, igm, logn, p, p0i); } @@ -483,8 +477,8 @@ void modp_iNTT2(int[] srca, int a, int[] srcigm, int igm, int logn, int p, int p * This function applies only to the binary case; it is invoked from * solve_NTRU_binary_depth1(). */ - void modp_poly_rec_res(int[] srcf, int f, int logn, - int p, int p0i, int R2) + private static void modp_poly_rec_res(int[] srcf, int f, int logn, + int p, int p0i, int R2) { int hn, u; @@ -493,7 +487,7 @@ void modp_poly_rec_res(int[] srcf, int f, int logn, { int w0, w1; - w0 = srcf[f + (u << 1) + 0]; + w0 = srcf[f + (u << 1)]; w1 = srcf[f + (u << 1) + 1]; srcf[f + u] = modp_montymul(modp_montymul(w0, w1, p, p0i), R2, p, p0i); } @@ -540,32 +534,30 @@ void modp_poly_rec_res(int[] srcf, int f, int logn, * ctl = 0, the value a[] is unmodified, but all memory accesses are * still performed, and the carry is computed and returned. */ - int zint_sub(int[] srca, int a, int[] srcb, int b, int len, - int ctl) + private static void zint_sub(int[] srca, int a, int[] srcb, int b, int len, + int ctl) { int u; int cc, m; - + int aw, w, au; cc = 0; m = -ctl; for (u = 0; u < len; u++) { - int aw, w; - - aw = srca[a + u]; + au = a + u; + aw = srca[au]; w = aw - srcb[b + u] - cc; cc = w >>> 31; aw ^= ((w & 0x7FFFFFFF) ^ aw) & m; - srca[a + u] = aw; + srca[au] = aw; } - return cc; } /* * Mutiply the provided big integer m with a small value x. * This function assumes that x < 2^31. The carry word is returned. */ - int zint_mul_small(int[] srcm, int m, int mlen, int x) + private static int zint_mul_small(int[] srcm, int m, int mlen, int x) { int u; int cc; @@ -591,20 +583,17 @@ int zint_mul_small(int[] srcm, int m, int mlen, int x) * p0i = -(1/p) mod 2^31 * R2 = 2^62 mod p */ - int zint_mod_small_unsigned(int[] srcd, int d, int dlen, - int p, int p0i, int R2) + private static int zint_mod_small_unsigned(int[] srcd, int d, int dlen, + int p, int p0i, int R2) { - int x; - int u; - /* * Algorithm: we inject words one by one, starting with the high * word. Each step is: * - multiply x by 2^31 * - add new word */ - x = 0; - u = dlen; + int x = 0; + int u = dlen; while (u-- > 0) { int w; @@ -621,8 +610,8 @@ int zint_mod_small_unsigned(int[] srcd, int d, int dlen, * Similar to zint_mod_small_unsigned(), except that d may be signed. * Extra parameter is Rx = 2^(31*dlen) mod p. */ - int zint_mod_small_signed(int[] srcd, int d, int dlen, - int p, int p0i, int R2, int Rx) + private static int zint_mod_small_signed(int[] srcd, int d, int dlen, + int p, int p0i, int R2, int Rx) { int z; @@ -640,8 +629,8 @@ int zint_mod_small_signed(int[] srcd, int d, int dlen, * has length 'len+1' words. 's' must fit on 31 bits. x[] and y[] must * not overlap. */ - void zint_add_mul_small(int[] srcx, int x, - int[] srcy, int y, int len, int s) + private static void zint_add_mul_small(int[] srcx, int x, + int[] srcy, int y, int len, int s) { int u; int cc; @@ -666,7 +655,7 @@ void zint_add_mul_small(int[] srcx, int x, * with x - p (signed encoding with two's complement); otherwise, x is * untouched. The two integers x and p are encoded over the same length. */ - void zint_norm_zero(int[] srcx, int x, int[] srcp, int p, int len) + private static void zint_norm_zero(int[] srcx, int x, int[] srcp, int p, int len) { int u; int r, bb; @@ -727,14 +716,14 @@ void zint_norm_zero(int[] srcx, int x, int[] srcp, int p, int len) * normalized to the -m/2..m/2 interval (where m is the product of all * small prime moduli); two's complement is used for negative values. */ - void zint_rebuild_CRT(int[] srcxx, int xx, int xlen, int xstride, - int num, FalconSmallPrime[] primes, int normalize_signed, - int[] srctmp, int tmp) + private static void zint_rebuild_CRT(int[] srcxx, int xx, int xlen, int xstride, + int num, int normalize_signed, + int[] srctmp, int tmp) { int u; int x; - srctmp[tmp + 0] = primes[0].p; + srctmp[tmp] = FalconSmallPrimeList.PRIMES[0].p; for (u = 1; u < xlen; u++) { /* @@ -749,8 +738,8 @@ void zint_rebuild_CRT(int[] srcxx, int xx, int xlen, int xstride, int p, p0i, s, R2; int v; - p = primes[u].p; - s = primes[u].s; + p = FalconSmallPrimeList.PRIMES[u].p; + s = FalconSmallPrimeList.PRIMES[u].s; p0i = modp_ninv31(p); R2 = modp_R2(p, p0i); @@ -794,7 +783,7 @@ void zint_rebuild_CRT(int[] srcxx, int xx, int xlen, int xstride, * Negate a big integer conditionally: value a is replaced with -a if * and only if ctl = 1. Control value ctl must be 0 or 1. */ - void zint_negate(int[] srca, int a, int len, int ctl) + private static void zint_negate(int[] srca, int a, int len, int ctl) { int u; int cc, m; @@ -831,8 +820,8 @@ void zint_negate(int[] srca, int a, int len, int ctl) * * Coefficients xa, xb, ya and yb may use the full signed 32-bit range. */ - int zint_co_reduce(int[] srca, int a, int[] srcb, int b, int len, - long xa, long xb, long ya, long yb) + private static int zint_co_reduce(int[] srca, int a, int[] srcb, int b, int len, + long xa, long xb, long ya, long yb) { int u; long cca, ccb; @@ -879,7 +868,7 @@ int zint_co_reduce(int[] srca, int a, int[] srcb, int b, int len, * * Modulus m must be odd. */ - void zint_finish_mod(int[] srca, int a, int len, int[] srcm, int m, int neg) + private static void zint_finish_mod(int[] srca, int a, int len, int[] srcm, int m, int neg) { int u; int cc, xm, ym; @@ -923,8 +912,8 @@ void zint_finish_mod(int[] srca, int a, int len, int[] srcm, int m, int neg) * Replace a with (a*xa+b*xb)/(2^31) mod m, and b with * (a*ya+b*yb)/(2^31) mod m. Modulus m must be odd; m0i = -1/m[0] mod 2^31. */ - void zint_co_reduce_mod(int[] srca, int a, int[] srcb, int b, int[] srcm, int m, int len, - int m0i, long xa, long xb, long ya, long yb) + private static void zint_co_reduce_mod(int[] srca, int a, int[] srcb, int b, int[] srcm, int m, int len, + int m0i, long xa, long xb, long ya, long yb) { int u; long cca, ccb; @@ -935,8 +924,8 @@ void zint_co_reduce_mod(int[] srca, int a, int[] srcb, int b, int[] srcm, int m, */ cca = 0; ccb = 0; - fa = ((srca[a + 0] * (int)xa + srcb[b + 0] * (int)xb) * m0i) & 0x7FFFFFFF; - fb = ((srca[a + 0] * (int)ya + srcb[b + 0] * (int)yb) * m0i) & 0x7FFFFFFF; + fa = ((srca[a] * (int)xa + srcb[b] * (int)xb) * m0i) & 0x7FFFFFFF; + fb = ((srca[a] * (int)ya + srcb[b] * (int)yb) * m0i) & 0x7FFFFFFF; for (u = 0; u < len; u++) { int wa, wb; @@ -984,9 +973,9 @@ void zint_co_reduce_mod(int[] srca, int a, int[] srcb, int b, int[] srcm, int m, * extra values of that length. Arrays u, v and tmp may not overlap with * each other, or with either x or y. */ - int zint_bezout(int[] srcu, int u, int[] srcv, int v, - int[] srcx, int x, int[] srcy, int y, - int len, int[] srctmp, int tmp) + private static int zint_bezout(int[] srcu, int u, int[] srcv, int v, + int[] srcx, int x, int[] srcy, int y, + int len, int[] srctmp, int tmp) { /* * Algorithm is an extended binary GCD. We maintain 6 values @@ -1121,8 +1110,8 @@ int zint_bezout(int[] srcu, int u, int[] srcv, int v, /* * We'll need the Montgomery reduction coefficients. */ - x0i = modp_ninv31(srcx[x + 0]); - y0i = modp_ninv31(srcy[y + 0]); + x0i = modp_ninv31(srcx[x]); + y0i = modp_ninv31(srcy[y]); /* * Initialize a, b, u0, u1, v0 and v1. @@ -1135,10 +1124,10 @@ int zint_bezout(int[] srcu, int u, int[] srcv, int v, // memcpy(b, y, len * sizeof *y); System.arraycopy(srcy, y, srctmp, b, len); // u0[0] = 1; - srcu[u0 + 0] = 1; + srcu[u0] = 1; // memset(u0 + 1, 0, (len - 1) * sizeof *u0); // memset(v0, 0, len * sizeof *v0); - srcv[v0 + 0] = 0; + srcv[v0] = 0; for (int i = 1; i < len; i++) { srcu[u0 + i] = 0; @@ -1149,7 +1138,7 @@ int zint_bezout(int[] srcu, int u, int[] srcv, int v, // memcpy(v1, x, len * sizeof *v1); System.arraycopy(srcx, x, srctmp, v1, len); // v1[0] --; - srctmp[v1 + 0]--; + srctmp[v1]--; /* * Each input operand may be as large as 31*len bits, and we * reduce the total length by at least 30 bits at each iteration. @@ -1204,8 +1193,8 @@ int zint_bezout(int[] srcu, int u, int[] srcv, int v, b0 &= ~c1; a_hi = (toUnsignedLong(a0) << 31) + toUnsignedLong(a1); b_hi = (toUnsignedLong(b0) << 31) + toUnsignedLong(b1); - a_lo = srctmp[a + 0]; - b_lo = srctmp[b + 0]; + a_lo = srctmp[a]; + b_lo = srctmp[b]; /* * Compute reduction factors: @@ -1306,12 +1295,12 @@ int zint_bezout(int[] srcu, int u, int[] srcv, int v, * is indeed 1. We also check that the two operands x and y * are odd. */ - rc = srctmp[a + 0] ^ 1; + rc = srctmp[a] ^ 1; for (j = 1; j < len; j++) { rc |= srctmp[a + j]; } - return ((1 - ((rc | -rc) >>> 31)) & srcx[x + 0] & srcy[y + 0]); + return ((1 - ((rc | -rc) >>> 31)) & srcx[x] & srcy[y]); } /* @@ -1325,9 +1314,9 @@ int zint_bezout(int[] srcu, int u, int[] srcv, int v, * x[] and y[] are both signed integers, using two's complement for * negative values. */ - void zint_add_scaled_mul_small(int[] srcx, int x, int xlen, - int[] srcy, int y, int ylen, int k, - int sch, int scl) + private static void zint_add_scaled_mul_small(int[] srcx, int x, int xlen, + int[] srcy, int y, int ylen, int k, + int sch, int scl) { int u; int ysign, tw; @@ -1387,8 +1376,8 @@ void zint_add_scaled_mul_small(int[] srcx, int x, int xlen, * x[] and y[] are both signed integers, using two's complement for * negative values. */ - void zint_sub_scaled(int[] srcx, int x, int xlen, - int[] srcy, int y, int ylen, int sch, int scl) + private static void zint_sub_scaled(int[] srcx, int x, int xlen, + int[] srcy, int y, int ylen, int sch, int scl) { int u; int ysign, tw; @@ -1424,11 +1413,11 @@ void zint_sub_scaled(int[] srcx, int x, int xlen, /* * Convert a one-word signed big integer into a signed value. */ - int zint_one_to_plain(int[] srcx, int x) + private static int zint_one_to_plain(int[] srcx, int x) { int w; - w = srcx[x + 0]; + w = srcx[x]; w |= (w & 0x40000000) << 1; return w; } @@ -1446,8 +1435,8 @@ int zint_one_to_plain(int[] srcx, int x) * they should be "trimmed" by pointing not to the lowest word of each, * but upper. */ - void poly_big_to_fp(FalconFPR[] srcd, int d, int[] srcf, int f, int flen, int fstride, - int logn) + private static void poly_big_to_fp(double[] srcd, int[] srcf, int f, int flen, int fstride, + int logn) { int n, u; @@ -1456,7 +1445,7 @@ void poly_big_to_fp(FalconFPR[] srcd, int d, int[] srcf, int f, int flen, int fs { for (u = 0; u < n; u++) { - srcd[d + u] = fpr.fpr_zero; + srcd[u] = FPREngine.fpr_zero; } return; } @@ -1464,7 +1453,7 @@ void poly_big_to_fp(FalconFPR[] srcd, int d, int[] srcf, int f, int flen, int fs { int v; int neg, cc, xm; - FalconFPR x, fsc; + double x, fsc; /* * Get sign of the integer; if it is negative, then we @@ -1474,19 +1463,17 @@ void poly_big_to_fp(FalconFPR[] srcd, int d, int[] srcf, int f, int flen, int fs neg = -(srcf[f + flen - 1] >>> 30); xm = neg >>> 1; cc = neg & 1; - x = fpr.fpr_zero; - fsc = fpr.fpr_one; - for (v = 0; v < flen; v++, fsc = fpr.fpr_mul(fsc, fpr.fpr_ptwo31)) + x = FPREngine.fpr_zero; + fsc = FPREngine.fpr_one; + for (v = 0; v < flen; v++, fsc *= FPREngine.fpr_ptwo31) { - int w; - - w = (srcf[f + v] ^ xm) + cc; + int w = (srcf[f + v] ^ xm) + cc; cc = w >>> 31; w &= 0x7FFFFFFF; w -= (w << 1) & neg; - x = fpr.fpr_add(x, fpr.fpr_mul(fpr.fpr_of(w), fsc)); + x += w * fsc; } - srcd[d + u] = x; + srcd[u] = x; } } @@ -1500,7 +1487,7 @@ void poly_big_to_fp(FalconFPR[] srcd, int d, int[] srcf, int f, int flen, int fs * any failure, the NTRU-solving process will be deemed to have failed * and the (f,g) polynomials will be discarded. */ - int poly_big_to_small(byte[] srcd, int d, int[] srcs, int s, int lim, int logn) + private static int poly_big_to_small(byte[] srcd, int d, int[] srcs, int s, int lim, int logn) { int n, u; @@ -1529,9 +1516,9 @@ int poly_big_to_small(byte[] srcd, int d, int[] srcs, int s, int lim, int logn) * which is efficient in space (no extra buffer needed) but slow at * high degree. */ - void poly_sub_scaled(int[] srcF, int F, int Flen, int Fstride, - int[] srcf, int f, int flen, int fstride, - int[] srck, int k, int sch, int scl, int logn) + private static void poly_sub_scaled(int[] srcF, int F, int Flen, int Fstride, + int[] srcf, int f, int flen, int fstride, + int[] srck, int sch, int scl, int logn) { int n, u; @@ -1543,7 +1530,7 @@ void poly_sub_scaled(int[] srcF, int F, int Flen, int Fstride, int x; int y; - kf = -srck[k + u]; + kf = -srck[u]; x = F + u * Fstride; y = f; for (v = 0; v < n; v++) @@ -1570,15 +1557,15 @@ void poly_sub_scaled(int[] srcF, int F, int Flen, int Fstride, * assumes that the degree is large, and integers relatively small. * The value sc is provided as sch = sc / 31 and scl = sc % 31. */ - void poly_sub_scaled_ntt(int[] srcF, int F, int Flen, int Fstride, - int[] srcf, int f, int flen, int fstride, - int[] srck, int k, int sch, int scl, int logn, - int[] srctmp, int tmp) + private static void poly_sub_scaled_ntt(int[] srcF, int F, int Flen, int Fstride, + int[] srcf, int f, int flen, int fstride, + int[] srck, int sch, int scl, int logn, + int[] srctmp, int tmp) { int gm, igm, fk, t1, x; int y; int n, u, tlen; - FalconSmallPrime[] primes; +// FalconSmallPrime[] primes; n = mkn(logn); tlen = flen + 1; @@ -1587,7 +1574,7 @@ void poly_sub_scaled_ntt(int[] srcF, int F, int Flen, int Fstride, fk = igm + mkn(logn); t1 = fk + n * tlen; - primes = this.primes.PRIMES; +// primes = this.primes.PRIMES; /* * Compute k*f in fk[], in RNS notation. @@ -1597,15 +1584,15 @@ void poly_sub_scaled_ntt(int[] srcF, int F, int Flen, int Fstride, int p, p0i, R2, Rx; int v; - p = primes[u].p; + p = FalconSmallPrimeList.PRIMES[u].p; p0i = modp_ninv31(p); R2 = modp_R2(p, p0i); Rx = modp_Rx(flen, p, p0i, R2); - modp_mkgm2(srctmp, gm, srctmp, igm, logn, primes[u].g, p, p0i); + modp_mkgm2(srctmp, gm, srctmp, igm, logn, FalconSmallPrimeList.PRIMES[u].g, p, p0i); for (v = 0; v < n; v++) { - srctmp[t1 + v] = modp_set(srck[k + v], p); + srctmp[t1 + v] = modp_set(srck[v], p); } modp_NTT2(srctmp, t1, srctmp, gm, logn, p, p0i); for (v = 0, y = f, x = fk + u; @@ -1625,7 +1612,7 @@ void poly_sub_scaled_ntt(int[] srcF, int F, int Flen, int Fstride, /* * Rebuild k*f. */ - zint_rebuild_CRT(srctmp, fk, tlen, tlen, n, primes, 1, srctmp, t1); + zint_rebuild_CRT(srctmp, fk, tlen, tlen, n, 1, srctmp, t1); /* * Subtract k*f, scaled, from F. @@ -1644,7 +1631,7 @@ void poly_sub_scaled_ntt(int[] srcF, int F, int Flen, int Fstride, * the same values will be obtained over different platforms, in case * a known seed is used. */ - long get_rng_u64(SHAKE256 rng) + private static long get_rng_u64(SHAKEDigest rng) { /* * We enforce little-endian representation. @@ -1652,15 +1639,16 @@ long get_rng_u64(SHAKE256 rng) byte[] tmp = new byte[8]; - rng.inner_shake256_extract(tmp, 0, tmp.length); - return (tmp[0] & 0xffL) - | ((tmp[1] & 0xffL) << 8) - | ((tmp[2] & 0xffL) << 16) - | ((tmp[3] & 0xffL) << 24) - | ((tmp[4] & 0xffL) << 32) - | ((tmp[5] & 0xffL) << 40) - | ((tmp[6] & 0xffL) << 48) - | ((tmp[7] & 0xffL) << 56); + rng.doOutput(tmp, 0, tmp.length); + return Pack.littleEndianToLong(tmp, 0); +// return (tmp[0] & 0xffL) +// | ((tmp[1] & 0xffL) << 8) +// | ((tmp[2] & 0xffL) << 16) +// | ((tmp[3] & 0xffL) << 24) +// | ((tmp[4] & 0xffL) << 32) +// | ((tmp[5] & 0xffL) << 40) +// | ((tmp[6] & 0xffL) << 48) +// | ((tmp[7] & 0xffL) << 56); } @@ -1672,16 +1660,16 @@ long get_rng_u64(SHAKE256 rng) * For k > 0, element k is P(x >= k+1 | x > 0). * Probabilities are scaled up by 2^63. */ - final long[] gauss_1024_12289 = { - 1283868770400643928l, 6416574995475331444l, 4078260278032692663l, - 2353523259288686585l, 1227179971273316331l, 575931623374121527l, - 242543240509105209l, 91437049221049666l, 30799446349977173l, - 9255276791179340l, 2478152334826140l, 590642893610164l, - 125206034929641l, 23590435911403l, 3948334035941l, - 586753615614l, 77391054539l, 9056793210l, - 940121950l, 86539696l, 7062824l, - 510971l, 32764l, 1862l, - 94l, 4l, 0l + private static final long[] gauss_1024_12289 = { + 1283868770400643928L, 6416574995475331444L, 4078260278032692663L, + 2353523259288686585L, 1227179971273316331L, 575931623374121527L, + 242543240509105209L, 91437049221049666L, 30799446349977173L, + 9255276791179340L, 2478152334826140L, 590642893610164L, + 125206034929641L, 23590435911403L, 3948334035941L, + 586753615614L, 77391054539L, 9056793210L, + 940121950L, 86539696L, 7062824L, + 510971L, 32764L, 1862L, + 94L, 4L, 0L }; /* @@ -1694,7 +1682,7 @@ long get_rng_u64(SHAKE256 rng) * sigma*sqrt(2), then we can just generate more values and add them * together for lower dimensions. */ - int mkgauss(SHAKE256 rng, int logn) + private static int mkgauss(SHAKEDigest rng, int logn) { int u, g; int val; @@ -1728,7 +1716,7 @@ int mkgauss(SHAKE256 rng, int logn) */ r = get_rng_u64(rng); neg = (int)(r >>> 63); - r &= ~(1l << 63); + r &= ~(1L << 63); f = (int)((r - gauss_1024_12289[0]) >>> 63); /* @@ -1739,7 +1727,7 @@ int mkgauss(SHAKE256 rng, int logn) */ v = 0; r = get_rng_u64(rng); - r &= ~(1l << 63); + r &= ~(1L << 63); for (k = 1; k < gauss_1024_12289.length; k++) { int t; @@ -1819,11 +1807,11 @@ int mkgauss(SHAKE256 rng, int logn) * accordingly. */ - final int[] MAX_BL_SMALL = { + private static final int[] MAX_BL_SMALL = { 1, 1, 2, 2, 4, 7, 14, 27, 53, 106, 209 }; - final int[] MAX_BL_LARGE = { + private static final int[] MAX_BL_LARGE = { 2, 2, 5, 7, 12, 21, 40, 78, 157, 308 }; @@ -1832,7 +1820,7 @@ int mkgauss(SHAKE256 rng, int logn) * coefficients of (f,g), depending on depth. These values are used * to compute bounds for Babai's reduction. */ - final int[] bitlength_avg = { + private static final int[] bitlength_avg = { 4, 11, 24, @@ -1845,7 +1833,7 @@ int mkgauss(SHAKE256 rng, int logn) 3138, 6308 }; - final int[] bitlength_std = { + private static final int[] bitlength_std = { 0, 1, 1, @@ -1863,13 +1851,13 @@ int mkgauss(SHAKE256 rng, int logn) * Minimal recursion depth at which we rebuild intermediate values * when reconstructing f and g. */ - final int DEPTH_INT_FG = 4; + private static final int DEPTH_INT_FG = 4; /* * Compute squared norm of a short vector. Returned value is saturated to * 2^32-1 if it is not lower than 2^31. */ - int poly_small_sqnorm(byte[] srcf, int f, int logn) + private static int poly_small_sqnorm(byte[] srcf, int logn) { int n, u; int s, ng; @@ -1881,7 +1869,7 @@ int poly_small_sqnorm(byte[] srcf, int f, int logn) { int z; - z = srcf[f + u]; + z = srcf[u]; s += (z * z); ng |= s; } @@ -1891,14 +1879,14 @@ int poly_small_sqnorm(byte[] srcf, int f, int logn) /* * Convert a small vector to floating point. */ - void poly_small_to_fp(FalconFPR[] srcx, int x, byte[] srcf, int f, int logn) + private static void poly_small_to_fp(double[] srcx, int x, byte[] srcf, int logn) { int n, u; n = mkn(logn); for (u = 0; u < n; u++) { - srcx[x + u] = fpr.fpr_of(srcf[f + u]); + srcx[x + u] = srcf[u]; } } @@ -1910,19 +1898,19 @@ void poly_small_to_fp(FalconFPR[] srcx, int x, byte[] srcf, int f, int logn) * * Values are in RNS; input and/or output may also be in NTT. */ - void make_fg_step(int[] srcdata, int data, int logn, int depth, - int in_ntt, int out_ntt) + private static void make_fg_step(int[] srcdata, int data, int logn, int depth, + int in_ntt, int out_ntt) { int n, hn, u; int slen, tlen; int fd, gd, fs, gs, gm, igm, t1; - FalconSmallPrime[] primes; + //FalconSmallPrime[] primes; n = 1 << logn; hn = n >> 1; slen = MAX_BL_SMALL[depth]; tlen = MAX_BL_SMALL[depth + 1]; - primes = this.primes.PRIMES; + //primes = FalconSmallPrimeList.PRIMES; /* * Prepare room for the result. @@ -1947,10 +1935,10 @@ void make_fg_step(int[] srcdata, int data, int logn, int depth, int v; int x; - p = primes[u].p; + p = FalconSmallPrimeList.PRIMES[u].p; p0i = modp_ninv31(p); R2 = modp_R2(p, p0i); - modp_mkgm2(srcdata, gm, srcdata, igm, logn, primes[u].g, p, p0i); + modp_mkgm2(srcdata, gm, srcdata, igm, logn, FalconSmallPrimeList.PRIMES[u].g, p, p0i); for (v = 0, x = fs + u; v < n; v++, x += slen) { @@ -1964,7 +1952,7 @@ void make_fg_step(int[] srcdata, int data, int logn, int depth, { int w0, w1; - w0 = srcdata[t1 + (v << 1) + 0]; + w0 = srcdata[t1 + (v << 1)]; w1 = srcdata[t1 + (v << 1) + 1]; srcdata[x] = modp_montymul( modp_montymul(w0, w1, p, p0i), R2, p, p0i); @@ -1986,7 +1974,7 @@ void make_fg_step(int[] srcdata, int data, int logn, int depth, { int w0, w1; - w0 = srcdata[t1 + (v << 1) + 0]; + w0 = srcdata[t1 + (v << 1)]; w1 = srcdata[t1 + (v << 1) + 1]; srcdata[x] = modp_montymul( modp_montymul(w0, w1, p, p0i), R2, p, p0i); @@ -2007,8 +1995,8 @@ void make_fg_step(int[] srcdata, int data, int logn, int depth, * Since the fs and gs words have been de-NTTized, we can use the * CRT to rebuild the values. */ - zint_rebuild_CRT(srcdata, fs, slen, slen, n, primes, 1, srcdata, gm); - zint_rebuild_CRT(srcdata, gs, slen, slen, n, primes, 1, srcdata, gm); + zint_rebuild_CRT(srcdata, fs, slen, slen, n, 1, srcdata, gm); + zint_rebuild_CRT(srcdata, gs, slen, slen, n, 1, srcdata, gm); /* * Remaining words: use modular reductions to extract the values. @@ -2019,11 +2007,11 @@ void make_fg_step(int[] srcdata, int data, int logn, int depth, int v; int x; - p = primes[u].p; + p = FalconSmallPrimeList.PRIMES[u].p; p0i = modp_ninv31(p); R2 = modp_R2(p, p0i); Rx = modp_Rx(slen, p, p0i, R2); - modp_mkgm2(srcdata, gm, srcdata, igm, logn, primes[u].g, p, p0i); + modp_mkgm2(srcdata, gm, srcdata, igm, logn, FalconSmallPrimeList.PRIMES[u].g, p, p0i); for (v = 0, x = fs; v < n; v++, x += slen) { srcdata[t1 + v] = zint_mod_small_signed(srcdata, x, slen, p, p0i, R2, Rx); @@ -2033,7 +2021,7 @@ void make_fg_step(int[] srcdata, int data, int logn, int depth, { int w0, w1; - w0 = srcdata[t1 + (v << 1) + 0]; + w0 = srcdata[t1 + (v << 1)]; w1 = srcdata[t1 + (v << 1) + 1]; srcdata[x] = modp_montymul( modp_montymul(w0, w1, p, p0i), R2, p, p0i); @@ -2047,7 +2035,7 @@ void make_fg_step(int[] srcdata, int data, int logn, int depth, { int w0, w1; - w0 = srcdata[t1 + (v << 1) + 0]; + w0 = srcdata[t1 + (v << 1)]; w1 = srcdata[t1 + (v << 1) + 1]; srcdata[x] = modp_montymul( modp_montymul(w0, w1, p, p0i), R2, p, p0i); @@ -2069,23 +2057,23 @@ void make_fg_step(int[] srcdata, int data, int logn, int depth, * Space use in data[]: enough room for any two successive values (f', g', * f and g). */ - void make_fg(int[] srcdata, int data, byte[] srcf, int f, byte[] srcg, int g, - int logn, int depth, int out_ntt) + private static void make_fg(int[] srcdata, int data, byte[] srcf, byte[] srcg, + int logn, int depth, int out_ntt) { int n, u; int ft, gt, p0; int d; - FalconSmallPrime[] primes; + n = mkn(logn); ft = data; gt = ft + n; - primes = this.primes.PRIMES; - p0 = primes[0].p; + + p0 = FalconSmallPrimeList.PRIMES[0].p; for (u = 0; u < n; u++) { - srcdata[ft + u] = modp_set(srcf[f + u], p0); - srcdata[gt + u] = modp_set(srcg[g + u], p0); + srcdata[ft + u] = modp_set(srcf[u], p0); + srcdata[gt + u] = modp_set(srcg[u], p0); } if (depth == 0 && out_ntt != 0) @@ -2093,11 +2081,11 @@ void make_fg(int[] srcdata, int data, byte[] srcf, int f, byte[] srcg, int g, int gm, igm; int p, p0i; - p = primes[0].p; + p = FalconSmallPrimeList.PRIMES[0].p; p0i = modp_ninv31(p); gm = gt + n; igm = gm + n; - modp_mkgm2(srcdata, gm, srcdata, igm, logn, primes[0].g, p, p0i); + modp_mkgm2(srcdata, gm, srcdata, igm, logn, FalconSmallPrimeList.PRIMES[0].g, p, p0i); modp_NTT2(srcdata, ft, srcdata, gm, logn, p, p0i); modp_NTT2(srcdata, gt, srcdata, gm, logn, p, p0i); return; @@ -2117,30 +2105,30 @@ void make_fg(int[] srcdata, int data, byte[] srcf, int f, byte[] srcg, int g, * * Returned value: 1 on success, 0 on error. */ - int solve_NTRU_deepest(int logn_top, - byte[] srcf, int f, byte[] srcg, int g, int[] srctmp, int tmp) + private static int solve_NTRU_deepest(int logn_top, + byte[] srcf, byte[] srcg, int[] srctmp) { int len; int Fp, Gp, fp, gp, t1, q; - FalconSmallPrime[] primes; + //FalconSmallPrime[] primes; len = MAX_BL_SMALL[logn_top]; - primes = this.primes.PRIMES; + //primes = this.primes.PRIMES; - Fp = tmp; + Fp = 0; Gp = Fp + len; fp = Gp + len; gp = fp + len; t1 = gp + len; - make_fg(srctmp, fp, srcf, f, srcg, g, logn_top, logn_top, 0); + make_fg(srctmp, fp, srcf, srcg, logn_top, logn_top, 0); /* * We use the CRT to rebuild the resultants as big integers. * There are two such big integers. The resultants are always * nonnegative. */ - zint_rebuild_CRT(srctmp, fp, len, len, 2, primes, 0, srctmp, t1); + zint_rebuild_CRT(srctmp, fp, len, len, 2, 0, srctmp, t1); /* * Apply the binary GCD. The zint_bezout() function works only @@ -2179,8 +2167,8 @@ int solve_NTRU_deepest(int logn_top, * * Returned value: 1 on success, 0 on error. */ - int solve_NTRU_intermediate(int logn_top, - byte[] srcf, int f, byte[] srcg, int g, int depth, int[] srctmp, int tmp) + private static int solve_NTRU_intermediate(int logn_top, + byte[] srcf, byte[] srcg, int depth, int[] srctmp) { /* * In this function, 'logn' is the log2 of the degree for @@ -2192,11 +2180,11 @@ int solve_NTRU_intermediate(int logn_top, int logn; int n, hn, slen, dlen, llen, rlen, FGlen, u; int Fd, Gd, Ft, Gt, ft, gt, t1; - FalconFPR[] rt1, rt2, rt3, rt4, rt5; + double[] rt1, rt2, rt3, rt4, rt5; int scale_fg, minbl_fg, maxbl_fg, maxbl_FG, scale_k; int x, y; int[] k; - FalconSmallPrime[] primes; + //FalconSmallPrime[] primes; logn = logn_top - depth; n = 1 << logn; @@ -2217,12 +2205,12 @@ int solve_NTRU_intermediate(int logn_top, slen = MAX_BL_SMALL[depth]; dlen = MAX_BL_SMALL[depth + 1]; llen = MAX_BL_LARGE[depth]; - primes = this.primes.PRIMES; + //primes = this.primes.PRIMES; /* * Fd and Gd are the F and G from the deeper level. */ - Fd = tmp; + Fd = 0; Gd = Fd + dlen * hn; /* @@ -2230,28 +2218,30 @@ int solve_NTRU_intermediate(int logn_top, * and g in RNS + NTT representation. */ ft = Gd + dlen * hn; - make_fg(srctmp, ft, srcf, f, srcg, g, logn_top, depth, 1); + make_fg(srctmp, ft, srcf, srcg, logn_top, depth, 1); /* * Move the newly computed f and g to make room for our candidate * F and G (unreduced). */ - Ft = tmp; + Ft = 0; Gt = Ft + n * llen; t1 = Gt + n * llen; // memmove(t1, ft, 2 * n * slen * sizeof *ft); - System.arraycopy(srctmp, ft, srctmp, t1, 2 * n * slen); + int tmp = n * slen; + System.arraycopy(srctmp, ft, srctmp, t1, tmp + tmp); ft = t1; - gt = ft + slen * n; - t1 = gt + slen * n; + gt = ft + tmp; + t1 = gt + tmp; /* * Move Fd and Gd _after_ f and g. */ // memmove(t1, Fd, 2 * hn * dlen * sizeof *Fd); - System.arraycopy(srctmp, Fd, srctmp, t1, 2 * hn * dlen); + tmp = hn * dlen; + System.arraycopy(srctmp, Fd, srctmp, t1, tmp + tmp); Fd = t1; - Gd = Fd + hn * dlen; + Gd = Fd + tmp; /* * We reduce Fd and Gd modulo all the small primes we will need, @@ -2263,7 +2253,7 @@ int solve_NTRU_intermediate(int logn_top, int v; int xs, ys, xd, yd; - p = primes[u].p; + p = FalconSmallPrimeList.PRIMES[u].p; p0i = modp_ninv31(p); R2 = modp_R2(p, p0i); Rx = modp_Rx(dlen, p, p0i, R2); @@ -2292,7 +2282,7 @@ int solve_NTRU_intermediate(int logn_top, /* * All computations are done modulo p. */ - p = primes[u].p; + p = FalconSmallPrimeList.PRIMES[u].p; p0i = modp_ninv31(p); R2 = modp_R2(p, p0i); @@ -2302,8 +2292,8 @@ int solve_NTRU_intermediate(int logn_top, */ if (u == slen) { - zint_rebuild_CRT(srctmp, ft, slen, slen, n, primes, 1, srctmp, t1); - zint_rebuild_CRT(srctmp, gt, slen, slen, n, primes, 1, srctmp, t1); + zint_rebuild_CRT(srctmp, ft, slen, slen, n, 1, srctmp, t1); + zint_rebuild_CRT(srctmp, gt, slen, slen, n, 1, srctmp, t1); } gm = t1; @@ -2311,7 +2301,7 @@ int solve_NTRU_intermediate(int logn_top, fx = igm + n; gx = fx + n; - modp_mkgm2(srctmp, gm, srctmp, igm, logn, primes[u].g, p, p0i); + modp_mkgm2(srctmp, gm, srctmp, igm, logn, FalconSmallPrimeList.PRIMES[u].g, p, p0i); if (u < slen) { @@ -2393,15 +2383,15 @@ int solve_NTRU_intermediate(int logn_top, int ftA, ftB, gtA, gtB; int mFp, mGp; - ftA = srctmp[fx + (v << 1) + 0]; + ftA = srctmp[fx + (v << 1)]; ftB = srctmp[fx + (v << 1) + 1]; - gtA = srctmp[gx + (v << 1) + 0]; + gtA = srctmp[gx + (v << 1)]; gtB = srctmp[gx + (v << 1) + 1]; mFp = modp_montymul(srctmp[Fp + v], R2, p, p0i); mGp = modp_montymul(srctmp[Gp + v], R2, p, p0i); - srctmp[x + 0] = modp_montymul(gtB, mFp, p, p0i); + srctmp[x] = modp_montymul(gtB, mFp, p, p0i); srctmp[x + llen] = modp_montymul(gtA, mFp, p, p0i); - srctmp[y + 0] = modp_montymul(ftB, mGp, p, p0i); + srctmp[y] = modp_montymul(ftB, mGp, p, p0i); srctmp[y + llen] = modp_montymul(ftA, mGp, p, p0i); } modp_iNTT2_ext(srctmp, Ft + u, llen, srctmp, igm, logn, p, p0i); @@ -2411,8 +2401,8 @@ int solve_NTRU_intermediate(int logn_top, /* * Rebuild F and G with the CRT. */ - zint_rebuild_CRT(srctmp, Ft, llen, llen, n, primes, 1, srctmp, t1); - zint_rebuild_CRT(srctmp, Gt, llen, llen, n, primes, 1, srctmp, t1); + zint_rebuild_CRT(srctmp, Ft, llen, llen, n, 1, srctmp, t1); + zint_rebuild_CRT(srctmp, Gt, llen, llen, n, 1, srctmp, t1); /* * At that point, Ft, Gt, ft and gt are consecutive in RAM (in that @@ -2477,11 +2467,11 @@ int solve_NTRU_intermediate(int logn_top, * We ensure that the base is at a properly aligned offset (the * source array tmp[] is supposed to be already aligned). */ - rt1 = new FalconFPR[n]; - rt2 = new FalconFPR[n]; - rt3 = new FalconFPR[n]; - rt4 = new FalconFPR[n]; - rt5 = new FalconFPR[n >> 1]; + rt1 = new double[n]; + rt2 = new double[n]; + rt3 = new double[n]; + rt4 = new double[n]; + rt5 = new double[n >> 1]; k = new int[n]; /* @@ -2494,9 +2484,9 @@ int solve_NTRU_intermediate(int logn_top, * computed so that average maximum length will fall in the * middle or the upper half of these top 10 words. */ - rlen = (slen > 10) ? 10 : slen; - poly_big_to_fp(rt3, 0, srctmp, ft + slen - rlen, rlen, slen, logn); - poly_big_to_fp(rt4, 0, srctmp, gt + slen - rlen, rlen, slen, logn); + rlen = Math.min(slen, 10); + poly_big_to_fp(rt3, srctmp, ft + slen - rlen, rlen, slen, logn); + poly_big_to_fp(rt4, srctmp, gt + slen - rlen, rlen, slen, logn); /* * Values in rt3 and rt4 are downscaled by 2^(scale_fg). @@ -2516,11 +2506,11 @@ int solve_NTRU_intermediate(int logn_top, * Compute 1/(f*adj(f)+g*adj(g)) in rt5. We also keep adj(f) * and adj(g) in rt3 and rt4, respectively. */ - fft.FFT(rt3, 0, logn); - fft.FFT(rt4, 0, logn); - fft.poly_invnorm2_fft(rt5, 0, rt3, 0, rt4, 0, logn); - fft.poly_adj_fft(rt3, 0, logn); - fft.poly_adj_fft(rt4, 0, logn); + FalconFFT.FFT(rt3, 0, logn); + FalconFFT.FFT(rt4, 0, logn); + FalconFFT.poly_invnorm2_fft(rt5, 0, rt3, 0, rt4, 0, logn); + FalconFFT.poly_adj_fft(rt3, 0, logn); + FalconFFT.poly_adj_fft(rt4, 0, logn); /* * Reduce F and G repeatedly. @@ -2563,27 +2553,27 @@ int solve_NTRU_intermediate(int logn_top, { int scale_FG, dc, new_maxbl_FG; int scl, sch; - FalconFPR pdc, pt; + double pdc, pt; /* * Convert current F and G into floating-point. We apply * scaling if the current length is more than 10 words. */ - rlen = (FGlen > 10) ? 10 : FGlen; - scale_FG = 31 * (int)(FGlen - rlen); - poly_big_to_fp(rt1, 0, srctmp, Ft + FGlen - rlen, rlen, llen, logn); - poly_big_to_fp(rt2, 0, srctmp, Gt + FGlen - rlen, rlen, llen, logn); + rlen = Math.min(FGlen, 10); + scale_FG = 31 * (FGlen - rlen); + poly_big_to_fp(rt1, srctmp, Ft + FGlen - rlen, rlen, llen, logn); + poly_big_to_fp(rt2, srctmp, Gt + FGlen - rlen, rlen, llen, logn); /* * Compute (F*adj(f)+G*adj(g))/(f*adj(f)+g*adj(g)) in rt2. */ - fft.FFT(rt1, 0, logn); - fft.FFT(rt2, 0, logn); - fft.poly_mul_fft(rt1, 0, rt3, 0, logn); - fft.poly_mul_fft(rt2, 0, rt4, 0, logn); - fft.poly_add(rt2, 0, rt1, 0, logn); - fft.poly_mul_autoadj_fft(rt2, 0, rt5, 0, logn); - fft.iFFT(rt2, 0, logn); + FalconFFT.FFT(rt1, 0, logn); + FalconFFT.FFT(rt2, 0, logn); + FalconFFT.poly_mul_fft(rt1, 0, rt3, 0, logn); + FalconFFT.poly_mul_fft(rt2, 0, rt4, 0, logn); + FalconFFT.poly_add(rt2, 0, rt1, 0, logn); + FalconFFT.poly_mul_autoadj_fft(rt2, 0, rt5, 0, logn); + FalconFFT.iFFT(rt2, 0, logn); /* * (f,g) are scaled by 'scale_fg', meaning that the @@ -2611,28 +2601,26 @@ int solve_NTRU_intermediate(int logn_top, if (dc < 0) { dc = -dc; - pt = fpr.fpr_two; + pt = FPREngine.fpr_two; } else { - pt = fpr.fpr_onehalf; + pt = FPREngine.fpr_onehalf; } - pdc = fpr.fpr_one; + pdc = FPREngine.fpr_one; while (dc != 0) { if ((dc & 1) != 0) { - pdc = fpr.fpr_mul(pdc, pt); + pdc = pdc * pt; } dc >>= 1; - pt = fpr.fpr_sqr(pt); + pt *= pt; } for (u = 0; u < n; u++) { - FalconFPR xv; - - xv = fpr.fpr_mul(rt2[u], pdc); + double xv = rt2[u] * pdc; /* * Sometimes the values can be out-of-bounds if @@ -2643,12 +2631,12 @@ int solve_NTRU_intermediate(int logn_top, * failure here implies that we discard the current * secret key (f,g). */ - if (!fpr.fpr_lt(fpr.fpr_mtwo31m1, xv) - || !fpr.fpr_lt(xv, fpr.fpr_ptwo31m1)) + if (FPREngine.fpr_mtwo31m1 >= xv + || xv >= FPREngine.fpr_ptwo31m1) { return 0; } - k[u] = (int)fpr.fpr_rint(xv); + k[u] = (int)FPREngine.fpr_rint(xv); } /* @@ -2663,16 +2651,16 @@ int solve_NTRU_intermediate(int logn_top, if (depth <= DEPTH_INT_FG) { poly_sub_scaled_ntt(srctmp, Ft, FGlen, llen, srctmp, ft, slen, slen, - k, 0, sch, scl, logn, srctmp, t1); + k, sch, scl, logn, srctmp, t1); poly_sub_scaled_ntt(srctmp, Gt, FGlen, llen, srctmp, gt, slen, slen, - k, 0, sch, scl, logn, srctmp, t1); + k, sch, scl, logn, srctmp, t1); } else { poly_sub_scaled(srctmp, Ft, FGlen, llen, srctmp, ft, slen, slen, - k, 0, sch, scl, logn); + k, sch, scl, logn); poly_sub_scaled(srctmp, Gt, FGlen, llen, srctmp, gt, slen, slen, - k, 0, sch, scl, logn); + k, sch, scl, logn); } /* @@ -2735,7 +2723,7 @@ int solve_NTRU_intermediate(int logn_top, * Compress encoding of all values to 'slen' words (this is the * expected output format). */ - for (u = 0, x = tmp, y = tmp; + for (u = 0, x = 0, y = 0; u < (n << 1); u++, x += slen, y += llen) { // memmove(x, y, slen * sizeof *y); @@ -2750,8 +2738,8 @@ int solve_NTRU_intermediate(int logn_top, * * Returned value: 1 on success, 0 on error. */ - int solve_NTRU_binary_depth1(int logn_top, - byte[] srcf, int f, byte[] srcg, int g, int[] srctmp, int tmp) + private static int solve_NTRU_binary_depth1(int logn_top, + byte[] srcf, byte[] srcg, int[] srctmp) { /* * The first half of this function is a copy of the corresponding @@ -2764,7 +2752,7 @@ int solve_NTRU_binary_depth1(int logn_top, int depth, logn; int n_top, n, hn, slen, dlen, llen, u; int Fd, Gd, Ft, Gt, ft, gt, t1; - FalconFPR[] rt1, rt2, rt3, rt4, rt5, rt6; + double[] rt1, rt2, rt3, rt4, rt5, rt6; int x, y; depth = 1; @@ -2806,7 +2794,7 @@ int solve_NTRU_binary_depth1(int logn_top, * Fd and Gd are the F and G from the deeper level. Ft and Gt * are the destination arrays for the unreduced F and G. */ - Fd = tmp; + Fd = 0; Gd = Fd + dlen * hn; Ft = Gd + dlen * hn; Gt = Ft + llen * n; @@ -2821,7 +2809,7 @@ int solve_NTRU_binary_depth1(int logn_top, int v; int xs, ys, xd, yd; - p = this.primes.PRIMES[u].p; + p = FalconSmallPrimeList.PRIMES[u].p; p0i = modp_ninv31(p); R2 = modp_R2(p, p0i); Rx = modp_Rx(dlen, p, p0i, R2); @@ -2838,8 +2826,8 @@ int solve_NTRU_binary_depth1(int logn_top, * Now Fd and Gd are not needed anymore; we can squeeze them out. */ // memmove(tmp, Ft, llen * n * sizeof(uint32_t)); - System.arraycopy(srctmp, Ft, srctmp, tmp, llen * n); - Ft = tmp; + System.arraycopy(srctmp, Ft, srctmp, 0, llen * n); + Ft = 0; // memmove(Ft + llen * n, Gt, llen * n * sizeof(uint32_t)); System.arraycopy(srctmp, Gt, srctmp, Ft + llen * n, llen * n); Gt = Ft + llen * n; @@ -2861,7 +2849,7 @@ int solve_NTRU_binary_depth1(int logn_top, /* * All computations are done modulo p. */ - p = this.primes.PRIMES[u].p; + p = FalconSmallPrimeList.PRIMES[u].p; p0i = modp_ninv31(p); R2 = modp_R2(p, p0i); @@ -2877,15 +2865,15 @@ int solve_NTRU_binary_depth1(int logn_top, igm = gm + n_top; fx = igm + n; gx = fx + n_top; - modp_mkgm2(srctmp, gm, srctmp, igm, logn_top, this.primes.PRIMES[u].g, p, p0i); + modp_mkgm2(srctmp, gm, srctmp, igm, logn_top, FalconSmallPrimeList.PRIMES[u].g, p, p0i); /* * Set ft and gt to f and g modulo p, respectively. */ for (v = 0; v < n_top; v++) { - srctmp[fx + v] = modp_set(srcf[f + v], p); - srctmp[gx + v] = modp_set(srcg[g + v], p); + srctmp[fx + v] = modp_set(srcf[v], p); + srctmp[gx + v] = modp_set(srcg[v], p); } /* @@ -2903,18 +2891,18 @@ int solve_NTRU_binary_depth1(int logn_top, * From that point onward, we only need tables for * degree n, so we can save some space. */ - if (depth > 0) - { /* always true */ +// if (depth > 0) +// { /* always true */ // memmove(gm + n, igm, n * sizeof *igm); - System.arraycopy(srctmp, igm, srctmp, gm + n, n); - igm = gm + n; + System.arraycopy(srctmp, igm, srctmp, gm + n, n); + igm = gm + n; // memmove(igm + n, fx, n * sizeof *ft); - System.arraycopy(srctmp, fx, srctmp, igm + n, n); - fx = igm + n; + System.arraycopy(srctmp, fx, srctmp, igm + n, n); + fx = igm + n; // memmove(fx + n, gx, n * sizeof *gt); - System.arraycopy(srctmp, gx, srctmp, fx + n, n); - gx = fx + n; - } + System.arraycopy(srctmp, gx, srctmp, fx + n, n); + gx = fx + n; +// } /* * Get F' and G' modulo p and in NTT representation @@ -2975,15 +2963,15 @@ int solve_NTRU_binary_depth1(int logn_top, int ftA, ftB, gtA, gtB; int mFp, mGp; - ftA = srctmp[fx + (v << 1) + 0]; + ftA = srctmp[fx + (v << 1)]; ftB = srctmp[fx + (v << 1) + 1]; - gtA = srctmp[gx + (v << 1) + 0]; + gtA = srctmp[gx + (v << 1)]; gtB = srctmp[gx + (v << 1) + 1]; mFp = modp_montymul(srctmp[Fp + v], R2, p, p0i); mGp = modp_montymul(srctmp[Gp + v], R2, p, p0i); - srctmp[x + 0] = modp_montymul(gtB, mFp, p, p0i); + srctmp[x] = modp_montymul(gtB, mFp, p, p0i); srctmp[x + llen] = modp_montymul(gtA, mFp, p, p0i); - srctmp[y + 0] = modp_montymul(ftB, mGp, p, p0i); + srctmp[y] = modp_montymul(ftB, mGp, p, p0i); srctmp[y + llen] = modp_montymul(ftA, mGp, p, p0i); } modp_iNTT2_ext(srctmp, Ft + u, llen, srctmp, igm, logn, p, p0i); @@ -3010,8 +2998,8 @@ int solve_NTRU_binary_depth1(int logn_top, * and G are consecutive, and thus can be rebuilt in a single * loop; similarly, the elements of f and g are consecutive. */ - zint_rebuild_CRT(srctmp, Ft, llen, llen, n << 1, this.primes.PRIMES, 1, srctmp, t1); - zint_rebuild_CRT(srctmp, ft, slen, slen, n << 1, this.primes.PRIMES, 1, srctmp, t1); + zint_rebuild_CRT(srctmp, Ft, llen, llen, n << 1, 1, srctmp, t1); + zint_rebuild_CRT(srctmp, ft, slen, slen, n << 1, 1, srctmp, t1); /* * Here starts the Babai reduction, specialized for depth = 1. @@ -3025,31 +3013,31 @@ int solve_NTRU_binary_depth1(int logn_top, * Convert F and G into floating point (rt1 and rt2). */ // rt1 = align_fpr(tmp, gt + slen * n); - rt1 = new FalconFPR[n]; - rt2 = new FalconFPR[n]; - poly_big_to_fp(rt1, 0, srctmp, Ft, llen, llen, logn); - poly_big_to_fp(rt2, 0, srctmp, Gt, llen, llen, logn); + rt1 = new double[n]; + rt2 = new double[n]; + poly_big_to_fp(rt1, srctmp, Ft, llen, llen, logn); + poly_big_to_fp(rt2, srctmp, Gt, llen, llen, logn); /* * Integer representation of F and G is no longer needed, we * can remove it. */ // memmove(tmp, ft, 2 * slen * n * sizeof *ft); - System.arraycopy(srctmp, ft, srctmp, tmp, 2 * slen * n); - ft = tmp; + System.arraycopy(srctmp, ft, srctmp, 0, 2 * slen * n); + ft = 0; gt = ft + slen * n; // rt3 = align_fpr(tmp, gt + slen * n); // memmove(rt3, rt1, 2 * n * sizeof *rt1); // rt1 = rt3; // rt2 = rt1 + n; - rt3 = new FalconFPR[n]; - rt4 = new FalconFPR[n]; + rt3 = new double[n]; + rt4 = new double[n]; /* * Convert f and g into floating point (rt3 and rt4). */ - poly_big_to_fp(rt3, 0, srctmp, ft, slen, slen, logn); - poly_big_to_fp(rt4, 0, srctmp, gt, slen, slen, logn); + poly_big_to_fp(rt3, srctmp, ft, slen, slen, logn); + poly_big_to_fp(rt4, srctmp, gt, slen, slen, logn); /* * Remove unneeded ft and gt. - not required as we have rt_ in separate array @@ -3068,10 +3056,10 @@ int solve_NTRU_binary_depth1(int logn_top, * rt4 = g * in that order in RAM. We convert all of them to FFT. */ - fft.FFT(rt1, 0, logn); - fft.FFT(rt2, 0, logn); - fft.FFT(rt3, 0, logn); - fft.FFT(rt4, 0, logn); + FalconFFT.FFT(rt1, 0, logn); + FalconFFT.FFT(rt2, 0, logn); + FalconFFT.FFT(rt3, 0, logn); + FalconFFT.FFT(rt4, 0, logn); /* * Compute: @@ -3079,16 +3067,16 @@ int solve_NTRU_binary_depth1(int logn_top, * rt6 = 1 / (f*adj(f) + g*adj(g)) * (Note that rt6 is half-length.) */ - rt5 = new FalconFPR[n]; - rt6 = new FalconFPR[n >> 1]; - fft.poly_add_muladj_fft(rt5, 0, rt1, 0, rt2, 0, rt3, 0, rt4, 0, logn); - fft.poly_invnorm2_fft(rt6, 0, rt3, 0, rt4, 0, logn); + rt5 = new double[n]; + rt6 = new double[n >> 1]; + FalconFFT.poly_add_muladj_fft(rt5, rt1, rt2, rt3, rt4, logn); + FalconFFT.poly_invnorm2_fft(rt6, 0, rt3, 0, rt4, 0, logn); /* * Compute: * rt5 = (F*adj(f)+G*adj(g)) / (f*adj(f)+g*adj(g)) */ - fft.poly_mul_autoadj_fft(rt5, 0, rt6, 0, logn); + FalconFFT.poly_mul_autoadj_fft(rt5, 0, rt6, 0, logn); /* * Compute k as the rounded version of rt5. Check that none of @@ -3097,34 +3085,35 @@ int solve_NTRU_binary_depth1(int logn_top, * note that any out-of-bounds value here implies a failure and * (f,g) will be discarded, so we can make a simple test. */ - fft.iFFT(rt5, 0, logn); + FalconFFT.iFFT(rt5, 0, logn); for (u = 0; u < n; u++) { - FalconFPR z; + double z; z = rt5[u]; - if (!fpr.fpr_lt(z, fpr.fpr_ptwo63m1) || !fpr.fpr_lt(fpr.fpr_mtwo63m1, z)) +// if (!FPREngine.fpr_lt(z, FPREngine.fpr_ptwo63m1) || !FPREngine.fpr_lt(FPREngine.fpr_mtwo63m1, z)) + if (z >= FPREngine.fpr_ptwo63m1 || FPREngine.fpr_mtwo63m1 >= z) { return 0; } - rt5[u] = fpr.fpr_of(fpr.fpr_rint(z)); + rt5[u] = FPREngine.fpr_rint(z); } - fft.FFT(rt5, 0, logn); + FalconFFT.FFT(rt5, 0, logn); /* * Subtract k*f from F, and k*g from G. */ - fft.poly_mul_fft(rt3, 0, rt5, 0, logn); - fft.poly_mul_fft(rt4, 0, rt5, 0, logn); - fft.poly_sub(rt1, 0, rt3, 0, logn); - fft.poly_sub(rt2, 0, rt4, 0, logn); - fft.iFFT(rt1, 0, logn); - fft.iFFT(rt2, 0, logn); + FalconFFT.poly_mul_fft(rt3, 0, rt5, 0, logn); + FalconFFT.poly_mul_fft(rt4, 0, rt5, 0, logn); + FalconFFT.poly_sub(rt1, 0, rt3, 0, logn); + FalconFFT.poly_sub(rt2, 0, rt4, 0, logn); + FalconFFT.iFFT(rt1, 0, logn); + FalconFFT.iFFT(rt2, 0, logn); /* * Convert back F and G to integers, and return. */ - Ft = tmp; + //Ft = 0; Gt = Ft + n; // rt3 = align_fpr(tmp, Gt + n); // memmove(rt3, rt1, 2 * n * sizeof *rt1); @@ -3132,8 +3121,8 @@ int solve_NTRU_binary_depth1(int logn_top, // rt2 = rt1 + n; for (u = 0; u < n; u++) { - srctmp[Ft + u] = (int)fpr.fpr_rint(rt1[u]); - srctmp[Gt + u] = (int)fpr.fpr_rint(rt2[u]); + srctmp[Ft + u] = (int)FPREngine.fpr_rint(rt1[u]); + srctmp[Gt + u] = (int)FPREngine.fpr_rint(rt2[u]); } return 1; @@ -3145,8 +3134,8 @@ int solve_NTRU_binary_depth1(int logn_top, * * Returned value: 1 on success, 0 on error. */ - int solve_NTRU_binary_depth0(int logn, - byte[] srcf, int f, byte[] srcg, int g, int[] srctmp, int tmp) + private static int solve_NTRU_binary_depth0(int logn, + byte[] srcf, byte[] srcg, int[] srctmp) { int n, hn, u; int p, p0i, R2; @@ -3172,18 +3161,18 @@ int solve_NTRU_binary_depth0(int logn, * Everything should fit in 31-bit integers, hence we can just use * the first small prime p = 2147473409. */ - p = this.primes.PRIMES[0].p; + p = FalconSmallPrimeList.PRIMES[0].p; p0i = modp_ninv31(p); R2 = modp_R2(p, p0i); - Fp = tmp; + Fp = 0; Gp = Fp + hn; ft = Gp + hn; gt = ft + n; gm = gt + n; igm = gm + n; - modp_mkgm2(srctmp, gm, srctmp, igm, logn, this.primes.PRIMES[0].g, p, p0i); + modp_mkgm2(srctmp, gm, srctmp, igm, logn, FalconSmallPrimeList.PRIMES[0].g, p, p0i); /* * Convert F' anf G' in NTT representation. @@ -3201,8 +3190,8 @@ int solve_NTRU_binary_depth0(int logn, */ for (u = 0; u < n; u++) { - srctmp[ft + u] = modp_set(srcf[f + u], p); - srctmp[gt + u] = modp_set(srcg[g + u], p); + srctmp[ft + u] = modp_set(srcf[u], p); + srctmp[gt + u] = modp_set(srcg[u], p); } modp_NTT2(srctmp, ft, srctmp, gm, logn, p, p0i); modp_NTT2(srctmp, gt, srctmp, gm, logn, p, p0i); @@ -3215,15 +3204,15 @@ int solve_NTRU_binary_depth0(int logn, int ftA, ftB, gtA, gtB; int mFp, mGp; - ftA = srctmp[ft + u + 0]; + ftA = srctmp[ft + u]; ftB = srctmp[ft + u + 1]; - gtA = srctmp[gt + u + 0]; + gtA = srctmp[gt + u]; gtB = srctmp[gt + u + 1]; mFp = modp_montymul(srctmp[Fp + (u >> 1)], R2, p, p0i); mGp = modp_montymul(srctmp[Gp + (u >> 1)], R2, p, p0i); - srctmp[ft + u + 0] = modp_montymul(gtB, mFp, p, p0i); + srctmp[ft + u] = modp_montymul(gtB, mFp, p, p0i); srctmp[ft + u + 1] = modp_montymul(gtA, mFp, p, p0i); - srctmp[gt + u + 0] = modp_montymul(ftB, mGp, p, p0i); + srctmp[gt + u] = modp_montymul(ftB, mGp, p, p0i); srctmp[gt + u + 1] = modp_montymul(ftA, mGp, p, p0i); } modp_iNTT2(srctmp, ft, srctmp, igm, logn, p, p0i); @@ -3251,7 +3240,7 @@ int solve_NTRU_binary_depth0(int logn, * Compute the NTT tables in t1 and t2. We do not keep t2 * (we'll recompute it later on). */ - modp_mkgm2(srctmp, t1, srctmp, t2, logn, this.primes.PRIMES[0].g, p, p0i); + modp_mkgm2(srctmp, t1, srctmp, t2, logn, FalconSmallPrimeList.PRIMES[0].g, p, p0i); /* * Convert F and G to NTT. @@ -3263,11 +3252,11 @@ int solve_NTRU_binary_depth0(int logn, * Load f and adj(f) in t4 and t5, and convert them to NTT * representation. */ - srctmp[t4 + 0] = srctmp[t5 + 0] = modp_set(srcf[f + 0], p); + srctmp[t4] = srctmp[t5] = modp_set(srcf[0], p); for (u = 1; u < n; u++) { - srctmp[t4 + u] = modp_set(srcf[f + u], p); - srctmp[t5 + n - u] = modp_set(-srcf[f + u], p); + srctmp[t4 + u] = modp_set(srcf[u], p); + srctmp[t5 + n - u] = modp_set(-srcf[u], p); } modp_NTT2(srctmp, t4, srctmp, t1, logn, p, p0i); modp_NTT2(srctmp, t5, srctmp, t1, logn, p, p0i); @@ -3288,11 +3277,11 @@ int solve_NTRU_binary_depth0(int logn, * Load g and adj(g) in t4 and t5, and convert them to NTT * representation. */ - srctmp[t4 + 0] = srctmp[t5 + 0] = modp_set(srcg[g + 0], p); + srctmp[t4] = srctmp[t5] = modp_set(srcg[0], p); for (u = 1; u < n; u++) { - srctmp[t4 + u] = modp_set(srcg[g + u], p); - srctmp[t5 + n - u] = modp_set(-srcg[g + u], p); + srctmp[t4 + u] = modp_set(srcg[u], p); + srctmp[t5 + n - u] = modp_set(-srcg[u], p); } modp_NTT2(srctmp, t4, srctmp, t1, logn, p, p0i); modp_NTT2(srctmp, t5, srctmp, t1, logn, p, p0i); @@ -3317,7 +3306,7 @@ int solve_NTRU_binary_depth0(int logn, * move them to t1 and t2. We first need to recompute the * inverse table for NTT. */ - modp_mkgm2(srctmp, t1, srctmp, t4, logn, this.primes.PRIMES[0].g, p, p0i); + modp_mkgm2(srctmp, t1, srctmp, t4, logn, FalconSmallPrimeList.PRIMES[0].g, p, p0i); modp_iNTT2(srctmp, t2, srctmp, t4, logn, p, p0i); modp_iNTT2(srctmp, t3, srctmp, t4, logn, p, p0i); // TODO fix binary_depth0 -> t1 value is wrong for (u = 0; u < n; u++) @@ -3344,17 +3333,16 @@ int solve_NTRU_binary_depth0(int logn, * representation are actually real, so we can truncate off * the imaginary parts. */ - FalconFPR[] - tmp2 = new FalconFPR[3 * n]; + double[] tmp2 = new double[3 * n]; // rt3 = align_fpr(tmp, t3); rt1 = 0; rt2 = rt1 + n; rt3 = rt2 + n; for (u = 0; u < n; u++) { - tmp2[rt3 + u] = fpr.fpr_of(srctmp[t2 + u]); + tmp2[rt3 + u] = srctmp[t2 + u]; } - fft.FFT(tmp2, rt3, logn); + FalconFFT.FFT(tmp2, rt3, logn); // rt2 = align_fpr(tmp, t2); // memmove(rt2, rt3, hn * sizeof *rt3); System.arraycopy(tmp2, rt3, tmp2, rt2, hn); @@ -3365,19 +3353,19 @@ int solve_NTRU_binary_depth0(int logn, rt3 = rt2 + hn; for (u = 0; u < n; u++) { - tmp2[rt3 + u] = fpr.fpr_of(srctmp[t1 + u]); + tmp2[rt3 + u] = srctmp[t1 + u]; } - fft.FFT(tmp2, rt3, logn); + FalconFFT.FFT(tmp2, rt3, logn); /* * Compute (F*adj(f)+G*adj(g))/(f*adj(f)+g*adj(g)) and get * its rounded normal representation in t1. */ - fft.poly_div_autoadj_fft(tmp2, rt3, tmp2, rt2, logn); - fft.iFFT(tmp2, rt3, logn); + FalconFFT.poly_div_autoadj_fft(tmp2, rt3, tmp2, rt2, logn); + FalconFFT.iFFT(tmp2, rt3, logn); for (u = 0; u < n; u++) { - srctmp[t1 + u] = modp_set((int)fpr.fpr_rint(tmp2[rt3 + u]), p); + srctmp[t1 + u] = modp_set((int)FPREngine.fpr_rint(tmp2[rt3 + u]), p); } /* @@ -3393,11 +3381,11 @@ int solve_NTRU_binary_depth0(int logn, t3 = t2 + n; t4 = t3 + n; t5 = t4 + n; - modp_mkgm2(srctmp, t2, srctmp, t3, logn, this.primes.PRIMES[0].g, p, p0i); + modp_mkgm2(srctmp, t2, srctmp, t3, logn, FalconSmallPrimeList.PRIMES[0].g, p, p0i); for (u = 0; u < n; u++) { - srctmp[t4 + u] = modp_set(srcf[f + u], p); - srctmp[t5 + u] = modp_set(srcg[g + u], p); + srctmp[t4 + u] = modp_set(srcf[u], p); + srctmp[t5 + u] = modp_set(srcg[u], p); } modp_NTT2(srctmp, t1, srctmp, t2, logn, p, p0i); modp_NTT2(srctmp, t4, srctmp, t2, logn, p, p0i); @@ -3429,17 +3417,16 @@ int solve_NTRU_binary_depth0(int logn, * If any of the coefficients of F and G exceeds lim (in absolute value), * then 0 is returned. */ - int solve_NTRU(int logn, byte[] srcF, int F, byte[] srcG, int G, - byte[] srcf, int f, byte[] srcg, int g, int lim, int[] srctmp, int tmp) + private static int solve_NTRU(int logn, byte[] srcF, //byte[] srcG, + byte[] srcf, byte[] srcg, int lim, int[] srctmp) { int n, u; int ft, gt, Ft, Gt, gm; int p, p0i, r; - FalconSmallPrime[] primes; - + int G = 0; n = mkn(logn); - if (solve_NTRU_deepest(logn, srcf, f, srcg, g, srctmp, tmp) == 0) + if (solve_NTRU_deepest(logn, srcf, srcg, srctmp) == 0) { return 0; } @@ -3456,7 +3443,7 @@ int solve_NTRU(int logn, byte[] srcF, int F, byte[] srcG, int G, depth = logn; while (depth-- > 0) { - if (solve_NTRU_intermediate(logn, srcf, f, srcg, g, depth, srctmp, tmp) == 0) + if (solve_NTRU_intermediate(logn, srcf, srcg, depth, srctmp) == 0) { return 0; } @@ -3464,21 +3451,19 @@ int solve_NTRU(int logn, byte[] srcF, int F, byte[] srcG, int G, } else { - int depth; - - depth = logn; + int depth = logn; while (depth-- > 2) { - if (solve_NTRU_intermediate(logn, srcf, f, srcg, g, depth, srctmp, tmp) == 0) + if (solve_NTRU_intermediate(logn, srcf, srcg, depth, srctmp) == 0) { return 0; } } - if (solve_NTRU_binary_depth1(logn, srcf, f, srcg, g, srctmp, tmp) == 0) + if (solve_NTRU_binary_depth1(logn, srcf, srcg, srctmp) == 0) { return 0; } - if (solve_NTRU_binary_depth0(logn, srcf, f, srcg, g, srctmp, tmp) == 0) + if (solve_NTRU_binary_depth0(logn, srcf, srcg, srctmp) == 0) { return 0; } @@ -3487,18 +3472,19 @@ int solve_NTRU(int logn, byte[] srcF, int F, byte[] srcG, int G, /* * If no buffer has been provided for G, use a temporary one. */ - if (srcG == null) - { - G = 0; - srcG = new byte[n]; - } +// if (srcG == null) +// { +// G = 0; +// srcG = new byte[n]; +// } + byte[] srcG = new byte[n]; /* * Final F and G are in fk->tmp, one word per coefficient * (signed value over 31 bits). */ - if (poly_big_to_small(srcF, F, srctmp, tmp, lim, logn) == 0 - || poly_big_to_small(srcG, G, srctmp, tmp + n, lim, logn) == 0) + if (poly_big_to_small(srcF, 0, srctmp, 0, lim, logn) == 0 + || poly_big_to_small(srcG, G, srctmp, n, lim, logn) == 0) { return 0; } @@ -3511,25 +3497,24 @@ int solve_NTRU(int logn, byte[] srcF, int F, byte[] srcG, int G, * We put Gt[] first in tmp[], and process it first, so that it does * not overlap with G[] in case we allocated it ourselves. */ - Gt = tmp; + Gt = 0; ft = Gt + n; gt = ft + n; Ft = gt + n; gm = Ft + n; - primes = this.primes.PRIMES; - p = primes[0].p; + p = FalconSmallPrimeList.PRIMES[0].p; p0i = modp_ninv31(p); - modp_mkgm2(srctmp, gm, srctmp, tmp, logn, primes[0].g, p, p0i); + modp_mkgm2(srctmp, gm, srctmp, 0, logn, FalconSmallPrimeList.PRIMES[0].g, p, p0i); for (u = 0; u < n; u++) { srctmp[Gt + u] = modp_set(srcG[G + u], p); } for (u = 0; u < n; u++) { - srctmp[ft + u] = modp_set(srcf[f + u], p); - srctmp[gt + u] = modp_set(srcg[g + u], p); - srctmp[Ft + u] = modp_set(srcF[F + u], p); + srctmp[ft + u] = modp_set(srcf[u], p); + srctmp[gt + u] = modp_set(srcg[u], p); + srctmp[Ft + u] = modp_set(srcF[u], p); } modp_NTT2(srctmp, ft, srctmp, gm, logn, p, p0i); modp_NTT2(srctmp, gt, srctmp, gm, logn, p, p0i); @@ -3555,7 +3540,7 @@ int solve_NTRU(int logn, byte[] srcF, int F, byte[] srcG, int G, * Generate a random polynomial with a Gaussian distribution. This function * also makes sure that the resultant of the polynomial with phi is odd. */ - void poly_small_mkgauss(SHAKE256 rng, byte[] srcf, int f, int logn) + private static void poly_small_mkgauss(SHAKEDigest rng, byte[] srcf, int logn) { int n, u; int mod2; @@ -3597,16 +3582,16 @@ void poly_small_mkgauss(SHAKE256 rng, byte[] srcf, int f, int logn) { mod2 ^= (s & 1); } - srcf[f + u] = (byte)s; + srcf[u] = (byte)s; break; } } } /* see falcon.h */ - void keygen(SHAKE256 rng, - byte[] srcf, int f, byte[] srcg, int g, byte[] srcF, int F, byte[] srcG, int G, short[] srch, int h, - int logn) + static void keygen(SHAKEDigest rc, + byte[] srcf, byte[] srcg, byte[] srcF, short[] srch, + int logn) { /* * Algorithm is the following: @@ -3629,14 +3614,14 @@ void keygen(SHAKE256 rng, */ int n, u; int[] itmp; - byte[] btmp; + //byte[] btmp; short[] stmp; - FalconFPR[] ftmp; + double[] ftmp; int h2, tmp2; - SHAKE256 rc; + //SHAKE256 rc; n = mkn(logn); - rc = rng; + //rc = rng; /* * We need to generate f and g randomly, until we find values @@ -3659,9 +3644,9 @@ void keygen(SHAKE256 rng, */ for (; ; ) { - ftmp = new FalconFPR[3 * n]; + ftmp = new double[3 * n]; int rt1, rt2, rt3; - FalconFPR bnorm; + double bnorm; int normf, normg, norm; int lim; @@ -3671,8 +3656,8 @@ void keygen(SHAKE256 rng, * (i.e. the resultant of the polynomial with phi * will be odd). */ - poly_small_mkgauss(rc, srcf, f, logn); - poly_small_mkgauss(rc, srcg, g, logn); + poly_small_mkgauss(rc, srcf, logn); + poly_small_mkgauss(rc, srcg, logn); /* * Verify that all coefficients are within the bounds @@ -3680,15 +3665,15 @@ void keygen(SHAKE256 rng, * overwhelming probability; this guarantees that the * key will be encodable with FALCON_COMP_TRIM. */ - lim = 1 << (codec.max_fg_bits[logn] - 1); + lim = 1 << (FalconCodec.max_fg_bits[logn] - 1); for (u = 0; u < n; u++) { /* * We can use non-CT tests since on any failure * we will discard f and g. */ - if (srcf[f + u] >= lim || srcf[f + u] <= -lim - || srcg[g + u] >= lim || srcg[g + u] <= -lim) + if (srcf[u] >= lim || srcf[u] <= -lim + || srcg[u] >= lim || srcg[u] <= -lim) { lim = -1; break; @@ -3706,8 +3691,8 @@ void keygen(SHAKE256 rng, * Since f and g are integral, the squared norm * of (g,-f) is an integer. */ - normf = poly_small_sqnorm(srcf, f, logn); - normg = poly_small_sqnorm(srcg, g, logn); + normf = poly_small_sqnorm(srcf, logn); + normg = poly_small_sqnorm(srcg, logn); norm = (normf + normg) | -((normf | normg) >>> 31); if ((norm & 0xffffffffL) >= 16823L) { @@ -3720,26 +3705,26 @@ void keygen(SHAKE256 rng, rt1 = 0; rt2 = rt1 + n; rt3 = rt2 + n; - poly_small_to_fp(ftmp, rt1, srcf, f, logn); - poly_small_to_fp(ftmp, rt2, srcg, g, logn); - fft.FFT(ftmp, rt1, logn); - fft.FFT(ftmp, rt2, logn); - fft.poly_invnorm2_fft(ftmp, rt3, ftmp, rt1, ftmp, rt2, logn); - fft.poly_adj_fft(ftmp, rt1, logn); - fft.poly_adj_fft(ftmp, rt2, logn); - fft.poly_mulconst(ftmp, rt1, fpr.fpr_q, logn); - fft.poly_mulconst(ftmp, rt2, fpr.fpr_q, logn); - fft.poly_mul_autoadj_fft(ftmp, rt1, ftmp, rt3, logn); - fft.poly_mul_autoadj_fft(ftmp, rt2, ftmp, rt3, logn); - fft.iFFT(ftmp, rt1, logn); - fft.iFFT(ftmp, rt2, logn); - bnorm = fpr.fpr_zero; + poly_small_to_fp(ftmp, rt1, srcf, logn); + poly_small_to_fp(ftmp, rt2, srcg, logn); + FalconFFT.FFT(ftmp, rt1, logn); + FalconFFT.FFT(ftmp, rt2, logn); + FalconFFT.poly_invnorm2_fft(ftmp, rt3, ftmp, rt1, ftmp, rt2, logn); + FalconFFT.poly_adj_fft(ftmp, rt1, logn); + FalconFFT.poly_adj_fft(ftmp, rt2, logn); + FalconFFT.poly_mulconst(ftmp, rt1, FPREngine.fpr_q, logn); + FalconFFT.poly_mulconst(ftmp, rt2, FPREngine.fpr_q, logn); + FalconFFT.poly_mul_autoadj_fft(ftmp, rt1, ftmp, rt3, logn); + FalconFFT.poly_mul_autoadj_fft(ftmp, rt2, ftmp, rt3, logn); + FalconFFT.iFFT(ftmp, rt1, logn); + FalconFFT.iFFT(ftmp, rt2, logn); + bnorm = FPREngine.fpr_zero; for (u = 0; u < n; u++) { - bnorm = fpr.fpr_add(bnorm, fpr.fpr_sqr(ftmp[rt1 + u])); - bnorm = fpr.fpr_add(bnorm, fpr.fpr_sqr(ftmp[rt2 + u])); + bnorm += ftmp[rt1 + u] * ftmp[rt1 + u] + ftmp[rt2 + u] * ftmp[rt2 + u]; } - if (!fpr.fpr_lt(bnorm, fpr.fpr_bnorm_max)) + //if (!FPREngine.fpr_lt(bnorm, FPREngine.fpr_bnorm_max)) + if (bnorm >= FPREngine.fpr_bnorm_max) { continue; } @@ -3757,10 +3742,10 @@ void keygen(SHAKE256 rng, } else { - h2 = h; + h2 = 0; tmp2 = 0; } - if (vrfy.compute_public(srch, h2, srcf, f, srcg, g, logn, stmp, tmp2) == 0) + if (FalconVrfy.compute_public(srch, h2, srcf, srcg, logn, stmp, tmp2) == 0) { continue; } @@ -3769,8 +3754,8 @@ void keygen(SHAKE256 rng, * Solve the NTRU equation to get F and G. */ itmp = logn > 2 ? new int[28 * n] : new int[28 * n * 3]; - lim = (1 << (codec.max_FG_bits[logn] - 1)) - 1; - if (solve_NTRU(logn, srcF, F, srcG, G, srcf, f, srcg, g, lim, itmp, 0) == 0) + lim = (1 << (FalconCodec.max_FG_bits[logn] - 1)) - 1; + if (solve_NTRU(logn, srcF, srcf, srcg, lim, itmp) == 0) { continue; } @@ -3782,7 +3767,7 @@ void keygen(SHAKE256 rng, } } - private long toUnsignedLong(int x) + private static long toUnsignedLong(int x) { return x & 0xffffffffL; } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconKeyPairGenerator.java index fcd94de366..1f2d6e02c7 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconKeyPairGenerator.java @@ -11,10 +11,7 @@ public class FalconKeyPairGenerator { private FalconKeyGenerationParameters params; - private SecureRandom random; private FalconNIST nist; - private int logn; - private int noncelen; private int pk_size; private int sk_size; @@ -22,11 +19,11 @@ public class FalconKeyPairGenerator public void init(KeyGenerationParameters param) { this.params = (FalconKeyGenerationParameters)param; - this.random = param.getRandom(); - this.logn = ((FalconKeyGenerationParameters)param).getParameters().getLogN(); - this.noncelen = ((FalconKeyGenerationParameters)param).getParameters().getNonceLength(); + SecureRandom random = param.getRandom(); + int logn = ((FalconKeyGenerationParameters)param).getParameters().getLogN(); + int noncelen = ((FalconKeyGenerationParameters)param).getParameters().getNonceLength(); this.nist = new FalconNIST(logn, noncelen, random); - int n = 1 << this.logn; + int n = 1 << logn; int sk_coeff_size = 8; if (n == 1024) { @@ -49,8 +46,8 @@ public AsymmetricCipherKeyPair generateKeyPair() byte[] pk, sk; pk = new byte[pk_size]; sk = new byte[sk_size]; - byte[][] keyData = nist.crypto_sign_keypair(pk, 0, sk, 0); - FalconParameters p = ((FalconKeyGenerationParameters)this.params).getParameters(); + byte[][] keyData = nist.crypto_sign_keypair(pk, sk); + FalconParameters p = this.params.getParameters(); FalconPrivateKeyParameters privk = new FalconPrivateKeyParameters(p, keyData[1], keyData[2], keyData[3], keyData[0]); FalconPublicKeyParameters pubk = new FalconPublicKeyParameters(p, keyData[0]); return new AsymmetricCipherKeyPair(pubk, privk); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconNIST.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconNIST.java index a70ebb07dd..b3c47c5c81 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconNIST.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconNIST.java @@ -1,26 +1,25 @@ package org.bouncycastle.pqc.crypto.falcon; - import java.security.SecureRandom; +import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.util.Arrays; class FalconNIST { + final int NONCELEN; + final int LOGN; + private final int N; + private final SecureRandom rand; + private final int CRYPTO_SECRETKEYBYTES; + private final int CRYPTO_PUBLICKEYBYTES; + final int CRYPTO_BYTES; - int NONCELEN; - int LOGN; - private int N; - private SecureRandom rand; - private int CRYPTO_SECRETKEYBYTES; - private int CRYPTO_PUBLICKEYBYTES; - int CRYPTO_BYTES; - - private FalconCodec codec; +// private FalconCodec codec; FalconNIST(int logn, int noncelen, SecureRandom random) { - codec = new FalconCodec(); +// codec = new FalconCodec(); this.rand = random; this.LOGN = logn; this.NONCELEN = noncelen; @@ -48,7 +47,7 @@ else if (logn == 7 || logn == 6) } } - byte[][] crypto_sign_keypair(byte[] srcpk, int pk, byte[] srcsk, int sk) + byte[][] crypto_sign_keypair(byte[] srcpk, byte[] srcsk) { // TODO: clean up required byte[] f = new byte[N], @@ -56,9 +55,10 @@ byte[][] crypto_sign_keypair(byte[] srcpk, int pk, byte[] srcsk, int sk) F = new byte[N]; short[] h = new short[N]; byte[] seed = new byte[48]; - SHAKE256 rng = new SHAKE256(); + //SHAKE256 rng = new SHAKE256(); + SHAKEDigest rng = new SHAKEDigest(256); int u, v; - FalconKeyGen keygen = new FalconKeyGen(); +// FalconKeyGen keygen = new FalconKeyGen(); // savcw = set_fpu_cw(2); @@ -70,12 +70,12 @@ byte[][] crypto_sign_keypair(byte[] srcpk, int pk, byte[] srcsk, int sk) // inner_shake256_init(&rng); // inner_shake256_inject(&rng, seed, sizeof seed); // inner_shake256_flip(&rng); - rng.inner_shake256_init(); - rng.inner_shake256_inject(seed, 0, seed.length); - rng.i_shake256_flip(); + //rng.inner_shake256_init(); + rng.update(seed, 0, seed.length); + //rng.i_shake256_flip(); // Zf(keygen)(&rng, f, g, F, NULL, h, 10, tmp.b); - keygen.keygen(rng, f, 0, g, 0, F, 0, null, 0, h, 0, LOGN); + FalconKeyGen.keygen(rng, f, g, F, h, LOGN); // set_fpu_cw(savcw); @@ -83,32 +83,32 @@ byte[][] crypto_sign_keypair(byte[] srcpk, int pk, byte[] srcsk, int sk) /* * Encode private key. */ - srcsk[sk + 0] = (byte)(0x50 + LOGN); // old python header + srcsk[0] = (byte)(0x50 + LOGN); // old python header u = 1; - v = codec.trim_i8_encode(srcsk, sk + u, CRYPTO_SECRETKEYBYTES - u, - f, 0, LOGN, codec.max_fg_bits[LOGN]); + v = FalconCodec.trim_i8_encode(srcsk, u, CRYPTO_SECRETKEYBYTES - u, + f, LOGN, FalconCodec.max_fg_bits[LOGN]); if (v == 0) { throw new IllegalStateException("f encode failed"); } - byte[] fEnc = Arrays.copyOfRange(srcsk, sk + u, u + v); + byte[] fEnc = Arrays.copyOfRange(srcsk, u, u + v); u += v; - v = codec.trim_i8_encode(srcsk, sk + u, CRYPTO_SECRETKEYBYTES - u, - g, 0, LOGN, codec.max_fg_bits[LOGN]); + v = FalconCodec.trim_i8_encode(srcsk, u, CRYPTO_SECRETKEYBYTES - u, + g, LOGN, FalconCodec.max_fg_bits[LOGN]); if (v == 0) { throw new IllegalStateException("g encode failed"); } - byte[] gEnc = Arrays.copyOfRange(srcsk, sk + u, u + v); + byte[] gEnc = Arrays.copyOfRange(srcsk, u, u + v); u += v; - v = codec.trim_i8_encode(srcsk, sk + u, CRYPTO_SECRETKEYBYTES - u, - F, 0, LOGN, codec.max_FG_bits[LOGN]); + v = FalconCodec.trim_i8_encode(srcsk, u, CRYPTO_SECRETKEYBYTES - u, + F, LOGN, FalconCodec.max_FG_bits[LOGN]); if (v == 0) { throw new IllegalStateException("F encode failed"); } - byte[] FEnc = Arrays.copyOfRange(srcsk, sk + u, u + v); + byte[] FEnc = Arrays.copyOfRange(srcsk, u, u + v); u += v; if (u != CRYPTO_SECRETKEYBYTES) { @@ -118,24 +118,24 @@ byte[][] crypto_sign_keypair(byte[] srcpk, int pk, byte[] srcsk, int sk) /* * Encode public key. */ - srcpk[pk + 0] = (byte)(0x00 + LOGN); - v = codec.modq_encode(srcpk, pk + 1, CRYPTO_PUBLICKEYBYTES - 1, h, 0, LOGN); + srcpk[0] = (byte)(LOGN); + v = FalconCodec.modq_encode(srcpk, CRYPTO_PUBLICKEYBYTES - 1, h, LOGN); if (v != CRYPTO_PUBLICKEYBYTES - 1) { throw new IllegalStateException("public key encoding failed"); } - return new byte[][] { Arrays.copyOfRange(srcpk, 1, srcpk.length), fEnc, gEnc, FEnc }; + return new byte[][]{Arrays.copyOfRange(srcpk, 1, srcpk.length), fEnc, gEnc, FEnc}; } - byte[] crypto_sign(boolean attached, byte[] srcsm, - byte[] srcm, int m, int mlen, - byte[] srcsk, int sk) + byte[] crypto_sign(byte[] srcsm, + byte[] srcm, int mlen, + byte[] srcsk) { byte[] f = new byte[N], - g = new byte[N], - F = new byte[N], - G = new byte[N]; + g = new byte[N], + F = new byte[N], + G = new byte[N]; short[] sig = new short[N]; short[] hm = new short[N]; @@ -144,11 +144,11 @@ byte[] crypto_sign(boolean attached, byte[] srcsm, nonce = new byte[NONCELEN]; - SHAKE256 sc = new SHAKE256(); + SHAKEDigest sc = new SHAKEDigest(256); int u, v, sig_len; FalconSign sign = new FalconSign(); - FalconVrfy vrfy = new FalconVrfy(); - FalconCommon common = new FalconCommon(); + //FalconVrfy vrfy = new FalconVrfy(); +// FalconCommon common = new FalconCommon(); /* * Decode the private key. @@ -158,22 +158,22 @@ byte[] crypto_sign(boolean attached, byte[] srcsm, // throw new IllegalArgumentException("private key header incorrect"); // } u = 0; - v = codec.trim_i8_decode(f, 0, LOGN, codec.max_fg_bits[LOGN], - srcsk, sk + u, CRYPTO_SECRETKEYBYTES - u); + v = FalconCodec.trim_i8_decode(f, LOGN, FalconCodec.max_fg_bits[LOGN], + srcsk, 0, CRYPTO_SECRETKEYBYTES - u); if (v == 0) { throw new IllegalStateException("f decode failed"); } u += v; - v = codec.trim_i8_decode(g, 0, LOGN, codec.max_fg_bits[LOGN], - srcsk, sk + u, CRYPTO_SECRETKEYBYTES - u); + v = FalconCodec.trim_i8_decode(g, LOGN, FalconCodec.max_fg_bits[LOGN], + srcsk, u, CRYPTO_SECRETKEYBYTES - u); if (v == 0) { throw new IllegalStateException("g decode failed"); } u += v; - v = codec.trim_i8_decode(F, 0, LOGN, codec.max_FG_bits[LOGN], - srcsk, sk + u, CRYPTO_SECRETKEYBYTES - u); + v = FalconCodec.trim_i8_decode(F, LOGN, FalconCodec.max_FG_bits[LOGN], + srcsk, u, CRYPTO_SECRETKEYBYTES - u); if (v == 0) { throw new IllegalArgumentException("F decode failed"); @@ -184,7 +184,7 @@ byte[] crypto_sign(boolean attached, byte[] srcsm, throw new IllegalStateException("full key not used"); } - if (!vrfy.complete_private(G, 0, f, 0, g, 0, F, 0, LOGN, new short[2 * N], 0)) + if (!FalconVrfy.complete_private(G, f, g, F, LOGN, new short[2 * N])) { throw new IllegalStateException("complete_private failed"); } @@ -202,12 +202,12 @@ byte[] crypto_sign(boolean attached, byte[] srcsm, // inner_shake256_inject(&sc, nonce, sizeof nonce); // inner_shake256_inject(&sc, m, mlen); // inner_shake256_flip(&sc); - sc.inner_shake256_init(); - sc.inner_shake256_inject(nonce, 0, NONCELEN); - sc.inner_shake256_inject(srcm, m, mlen); - sc.i_shake256_flip(); + //sc.inner_shake256_init(); + sc.update(nonce, 0, NONCELEN); + sc.update(srcm, 0, mlen); + //sc.i_shake256_flip(); // Zf(hash_to_point_vartime)(&sc, r.hm, 10); - common.hash_to_point_vartime(sc, hm, 0, LOGN); // TODO check if this needs to be ct + FalconCommon.hash_to_point_vartime(sc, hm, LOGN); // TODO check if this needs to be ct // System.out.println(String.format("%x %x %x %x %x %x %x %x", hm[0], hm[1], hm[2], hm[3], hm[4], hm[5], hm[6], hm[7])); /* @@ -218,9 +218,10 @@ byte[] crypto_sign(boolean attached, byte[] srcsm, // inner_shake256_init(&sc); // inner_shake256_inject(&sc, seed, sizeof seed); // inner_shake256_flip(&sc); - sc.inner_shake256_init(); - sc.inner_shake256_inject(seed, 0, seed.length); - sc.i_shake256_flip(); + sc.reset(); + //sc.inner_shake256_init(); + sc.update(seed, 0, seed.length); + //sc.i_shake256_flip(); // savcw = set_fpu_cw(2); @@ -228,35 +229,35 @@ byte[] crypto_sign(boolean attached, byte[] srcsm, * Compute the signature. */ // Zf(sign_dyn)(r.sig, &sc, f, g, F, G, r.hm, 10, tmp.b); - sign.sign_dyn(sig, 0, sc, f, 0, g, 0, F, 0, G, 0, hm, 0, LOGN, new FalconFPR[10 * N], 0); + sign.sign_dyn(sig, sc, f, g, F, G, hm, LOGN, new double[10 * N]); // set_fpu_cw(savcw); byte[] esig = new byte[CRYPTO_BYTES - 2 - NONCELEN]; - if (attached) - { - /* - * Encode the signature. Format is: - * signature header 1 bytes - * nonce 40 bytes - * signature slen bytes - */ - esig[0] = (byte)(0x20 + LOGN); - sig_len = codec.comp_encode(esig, 1, esig.length - 1, sig, 0, LOGN); - if (sig_len == 0) - { - throw new IllegalStateException("signature failed to generate"); - } - sig_len++; - } - else +// if (attached) +// { +// /* +// * Encode the signature. Format is: +// * signature header 1 bytes +// * nonce 40 bytes +// * signature slen bytes +// */ +// esig[0] = (byte)(0x20 + LOGN); +// sig_len = FalconCodec.comp_encode(esig, 1, esig.length - 1, sig, LOGN); +// if (sig_len == 0) +// { +// throw new IllegalStateException("signature failed to generate"); +// } +// sig_len++; +// } +// else +// { + sig_len = FalconCodec.comp_encode(esig, esig.length, sig, LOGN); + if (sig_len == 0) { - sig_len = codec.comp_encode(esig, 0, esig.length, sig, 0, LOGN); - if (sig_len == 0) - { - throw new IllegalStateException("signature failed to generate"); - } + throw new IllegalStateException("signature failed to generate"); } +// } // header srcsm[0] = (byte)(0x30 + LOGN); @@ -269,16 +270,17 @@ byte[] crypto_sign(boolean attached, byte[] srcsm, return Arrays.copyOfRange(srcsm, 0, 1 + NONCELEN + sig_len); } - int crypto_sign_open(boolean attached, byte[] sig_encoded, byte[] nonce, byte[] msg, - byte[] srcpk, int pk) + int crypto_sign_open(byte[] sig_encoded, byte[] nonce, byte[] msg, + byte[] srcpk) { short[] h = new short[N], hm = new short[N]; short[] sig = new short[N]; - SHAKE256 sc = new SHAKE256(); + //SHAKE256 sc = new SHAKE256(); + SHAKEDigest sc = new SHAKEDigest(256); int sig_len, msg_len; - FalconVrfy vrfy = new FalconVrfy(); - FalconCommon common = new FalconCommon(); + //FalconVrfy vrfy = new FalconVrfy(); +// FalconCommon common = new FalconCommon(); /* * Decode public key. @@ -287,12 +289,12 @@ int crypto_sign_open(boolean attached, byte[] sig_encoded, byte[] nonce, byte[] // { // return -1; // } - if (codec.modq_decode(h, 0, LOGN, srcpk, pk, CRYPTO_PUBLICKEYBYTES - 1) + if (FalconCodec.modq_decode(h, LOGN, srcpk, CRYPTO_PUBLICKEYBYTES - 1) != CRYPTO_PUBLICKEYBYTES - 1) { return -1; } - vrfy.to_ntt_monty(h, 0, LOGN); + FalconVrfy.to_ntt_monty(h, LOGN); /* * Find nonce, signature, message length. @@ -313,40 +315,40 @@ int crypto_sign_open(boolean attached, byte[] sig_encoded, byte[] nonce, byte[] * Decode signature. */ // Check only required for attached signatures - see 3.11.3 and 3.11.6 in the spec - if (attached) - { - if (sig_len < 1 || sig_encoded[0] != (byte)(0x20 + LOGN)) - { - return -1; - } - if (codec.comp_decode(sig, 0, LOGN, - sig_encoded, 1, sig_len - 1) != sig_len - 1) - { - return -1; - } - } - else +// if (attached) +// { +// if (sig_len < 1 || sig_encoded[0] != (byte)(0x20 + LOGN)) +// { +// return -1; +// } +// if (FalconCodec.comp_decode(sig, LOGN, +// sig_encoded, 1, sig_len - 1) != sig_len - 1) +// { +// return -1; +// } +// } +// else +// { + if (sig_len < 1 || FalconCodec.comp_decode(sig, LOGN, + sig_encoded, sig_len) != sig_len) { - if (sig_len < 1 || codec.comp_decode(sig, 0, LOGN, - sig_encoded, 0, sig_len) != sig_len) - { - return -1; - } + return -1; } +// } /* * Hash nonce + message into a vector. */ - sc.inner_shake256_init(); - sc.inner_shake256_inject(nonce, 0, NONCELEN); - sc.inner_shake256_inject(msg, 0, msg_len); - sc.i_shake256_flip(); - common.hash_to_point_vartime(sc, hm, 0, LOGN); // TODO check if this needs to become ct + //sc.inner_shake256_init(); + sc.update(nonce, 0, NONCELEN); + sc.update(msg, 0, msg_len); + //sc.i_shake256_flip(); + FalconCommon.hash_to_point_vartime(sc, hm, LOGN); // TODO check if this needs to become ct /* * Verify signature. */ - if (vrfy.verify_raw(hm, 0, sig, 0, h, 0, LOGN, new short[N], 0) == 0) + if (FalconVrfy.verify_raw(hm, sig, h, LOGN, new short[N]) == 0) { return -1; } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconPublicKeyParameters.java index 31f58be5a2..756341c56b 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconPublicKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconPublicKeyParameters.java @@ -5,7 +5,7 @@ public class FalconPublicKeyParameters extends FalconKeyParameters { - private byte[] H; + private final byte[] H; public FalconPublicKeyParameters(FalconParameters parameters, byte[] H) { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconRNG.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconRNG.java index bb191b0933..21e9e99baf 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconRNG.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconRNG.java @@ -1,57 +1,63 @@ package org.bouncycastle.pqc.crypto.falcon; +import org.bouncycastle.crypto.digests.SHAKEDigest; +import org.bouncycastle.util.Pack; + class FalconRNG { byte[] bd; - long bdummy_u64; + // long bdummy_u64; int ptr; byte[] sd; - long sdummy_u64; - int type; +// long sdummy_u64; +// int type; - FalconConversions convertor; + //FalconConversions convertor; FalconRNG() { this.bd = new byte[512]; - this.bdummy_u64 = 0; +// this.bdummy_u64 = 0; this.ptr = 0; this.sd = new byte[256]; - this.sdummy_u64 = 0; - this.type = 0; - this.convertor = new FalconConversions(); +// this.sdummy_u64 = 0; +// this.type = 0; + //this.convertor = new FalconConversions(); } - void prng_init(SHAKE256 src) + void prng_init(SHAKEDigest src) { /* * To ensure reproducibility for a given seed, we * must enforce little-endian interpretation of * the state words. */ - byte[] tmp = new byte[56]; - long th, tl; - int i; - - src.inner_shake256_extract(tmp, 0, 56); - for (i = 0; i < 14; i++) - { - int w; - - w = (tmp[(i << 2) + 0] & 0xff) - | ((tmp[(i << 2) + 1] & 0xff) << 8) - | ((tmp[(i << 2) + 2] & 0xff) << 16) - | ((tmp[(i << 2) + 3] & 0xff) << 24); +// byte[] tmp = new byte[56]; +// long th, tl; +// int i; - System.arraycopy(convertor.int_to_bytes(w), 0, this.sd, i << 2, 4); - } - - tl = (convertor.bytes_to_int(this.sd, 48) & 0xffffffffL); + src.doOutput(this.sd, 0, 56); +// System.arraycopy(tmp, 0, this.sd, 0, 56); +// for (i = 0; i < 14; i++) +// { +// int w = (tmp[(i << 2)] & 0xff) +// | ((tmp[(i << 2) + 1] & 0xff) << 8) +// | ((tmp[(i << 2) + 2] & 0xff) << 16) +// | ((tmp[(i << 2) + 3] & 0xff) << 24); +// +// +// System.arraycopy(Pack.intToLittleEndian(w), 0, this.sd, i << 2, 4); +// } - th = (convertor.bytes_to_int(this.sd, 52) & 0xffffffffL); + //tl = (convertor.bytes_to_int(this.sd, 48) & 0xffffffffL); +// tl = Pack.littleEndianToInt(this.sd, 48) & 0xffffffffL; +// +// //th = (convertor.bytes_to_int(this.sd, 52) & 0xffffffffL); +// th = Pack.littleEndianToInt(this.sd, 52) & 0xffffffffL; +// Pack.longToLittleEndian(tl + (th << 32), this.sd, 48); - System.arraycopy(convertor.long_to_bytes(tl + (th << 32)), 0, this.sd, 48, 8); + //System.arraycopy(convertor.long_to_bytes(tl + (th << 32)), 0, this.sd, 48, 8); this.prng_refill(); } @@ -84,17 +90,19 @@ void prng_refill() * converted to little endian (if used on a big-endian machine). */ // cc = *(uint64_t *)(p->state.d + 48); - cc = convertor.bytes_to_long(this.sd, 48); + cc = Pack.littleEndianToLong(this.sd, 48); + //cc = convertor.bytes_to_long(this.sd, 48); + int[] state = new int[16]; for (u = 0; u < 8; u++) { - int[] state = new int[16]; int v; int i; // memcpy(&state[0], CW, sizeof CW); System.arraycopy(CW, 0, state, 0, CW.length); // memcpy(&state[4], p->state.d, 48); - System.arraycopy(convertor.bytes_to_int_array(this.sd, 0, 12), 0, state, 4, 12); + Pack.littleEndianToInt(this.sd, 0, state, 4, 12); + //System.arraycopy(convertor.bytes_to_int_array(this.sd, 0, 12), 0, state, 4, 12); state[14] ^= (int)cc; state[15] ^= (int)(cc >>> 32); for (i = 0; i < 10; i++) @@ -117,14 +125,17 @@ void prng_refill() { // state[v] += ((uint32_t *)p->state.d)[v - 4]; // we multiply the -4 by 4 to account for 4 bytes per int - state[v] += convertor.bytes_to_int(sd, (4 * v) - 16); + //state[v] += convertor.bytes_to_int(sd, (4 * v) - 16); + state[v] += Pack.littleEndianToInt(sd, (4 * v) - 16); } // state[14] += ((uint32_t *)p->state.d)[10] // ^ (uint32_t)cc; - state[14] += convertor.bytes_to_int(sd, 40) ^ ((int)cc); + //state[14] += convertor.bytes_to_int(sd, 40) ^ ((int)cc); + state[14] += Pack.littleEndianToInt(sd, 40) ^ ((int)cc); // state[15] += ((uint32_t *)p->state.d)[11] // ^ (uint32_t)(cc >> 32); - state[15] += convertor.bytes_to_int(sd, 44) ^ ((int)(cc >>> 32)); + //state[15] += convertor.bytes_to_int(sd, 44) ^ ((int)(cc >>> 32)); + state[15] += Pack.littleEndianToInt(sd, 44) ^ ((int)(cc >>> 32)); cc++; /* @@ -141,49 +152,47 @@ void prng_refill() // (uint8_t)(state[v] >> 16); // p->buf.d[(u << 2) + (v << 5) + 3] = // (uint8_t)(state[v] >> 24); - bd[(u << 2) + (v << 5) + 0] = - (byte)state[v]; - bd[(u << 2) + (v << 5) + 1] = - (byte)(state[v] >>> 8); - bd[(u << 2) + (v << 5) + 2] = - (byte)(state[v] >>> 16); - bd[(u << 2) + (v << 5) + 3] = - (byte)(state[v] >>> 24); + Pack.intToLittleEndian(state[v], bd, (u << 2) + (v << 5)); +// bd[index] = (byte)state[v]; +// bd[index + 1] = (byte)(state[v] >>> 8); +// bd[index + 2] = (byte)(state[v] >>> 16); +// bd[index + 3] = (byte)(state[v] >>> 24); } } // *(uint64_t *)(p->state.d + 48) = cc; - System.arraycopy(convertor.long_to_bytes(cc), 0, sd, 48, 8); + //System.arraycopy(convertor.long_to_bytes(cc), 0, sd, 48, 8); + Pack.longToLittleEndian(cc, this.sd, 48); this.ptr = 0; } /* see inner.h */ - void prng_get_bytes(byte[] srcdst, int dst, int len) - { - int buf; - - buf = dst; - while (len > 0) - { - int clen; - - clen = (bd.length) - ptr; - if (clen > len) - { - clen = len; - } -// memcpy(buf, p->buf.d, clen); - System.arraycopy(bd, 0, srcdst, buf, clen); - buf += clen; - len -= clen; - ptr += clen; - if (ptr == bd.length) - { - this.prng_refill(); - } - } - } +// void prng_get_bytes(byte[] srcdst, int dst, int len) +// { +// int buf; +// +// buf = dst; +// while (len > 0) +// { +// int clen; +// +// clen = (bd.length) - ptr; +// if (clen > len) +// { +// clen = len; +// } +//// memcpy(buf, p->buf.d, clen); +// System.arraycopy(bd, 0, srcdst, buf, clen); +// buf += clen; +// len -= clen; +// ptr += clen; +// if (ptr == bd.length) +// { +// this.prng_refill(); +// } +// } +// } private void QROUND(int a, int b, int c, int d, int[] state) { @@ -223,14 +232,15 @@ long prng_get_u64() * On systems that use little-endian encoding and allow * unaligned accesses, we can simply read the data where it is. */ - return (this.bd[u + 0] & 0xffL) - | ((this.bd[u + 1] & 0xffL) << 8) - | ((this.bd[u + 2] & 0xffL) << 16) - | ((this.bd[u + 3] & 0xffL) << 24) - | ((this.bd[u + 4] & 0xffL) << 32) - | ((this.bd[u + 5] & 0xffL) << 40) - | ((this.bd[u + 6] & 0xffL) << 48) - | ((this.bd[u + 7] & 0xffL) << 56); + return Pack.littleEndianToLong(this.bd, u); +// return (this.bd[u] & 0xffL) +// | ((this.bd[u + 1] & 0xffL) << 8) +// | ((this.bd[u + 2] & 0xffL) << 16) +// | ((this.bd[u + 3] & 0xffL) << 24) +// | ((this.bd[u + 4] & 0xffL) << 32) +// | ((this.bd[u + 5] & 0xffL) << 40) +// | ((this.bd[u + 6] & 0xffL) << 48) +// | ((this.bd[u + 7] & 0xffL) << 56); } byte prng_get_u8() diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconSign.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconSign.java index ff25fb7ed8..f8ef315cf4 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconSign.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconSign.java @@ -1,23 +1,25 @@ package org.bouncycastle.pqc.crypto.falcon; +import org.bouncycastle.crypto.digests.SHAKEDigest; + class FalconSign { - FPREngine fpr; - FalconFFT fft; - FalconCommon common; + //FPREngine fpr; + //FalconFFT fft; +// FalconCommon common; FalconSign() { - this.fpr = new FPREngine(); - this.fft = new FalconFFT(); - this.common = new FalconCommon(); + //this.fpr = new FPREngine(); + //this.fft = new FalconFFT(); +// this.common = new FalconCommon(); } - private static int MKN(int logn) - { - return 1 << logn; - } +// private static int MKN(int logn) +// { +// return 1 << logn; +// } /* * Binary case: @@ -29,19 +31,19 @@ private static int MKN(int logn) * Get the size of the LDL tree for an input with polynomials of size * 2^logn. The size is expressed in the number of elements. */ - int ffLDL_treesize(int logn) - { - /* - * For logn = 0 (polynomials are constant), the "tree" is a - * single element. Otherwise, the tree node has size 2^logn, and - * has two child trees for size logn-1 each. Thus, treesize s() - * must fulfill these two relations: - * - * s(0) = 1 - * s(logn) = (2^logn) + 2*s(logn-1) - */ - return (logn + 1) << logn; - } +// int ffLDL_treesize(int logn) +// { +// /* +// * For logn = 0 (polynomials are constant), the "tree" is a +// * single element. Otherwise, the tree node has size 2^logn, and +// * has two child trees for size logn-1 each. Thus, treesize s() +// * must fulfill these two relations: +// * +// * s(0) = 1 +// * s(logn) = (2^logn) + 2*s(logn-1) +// */ +// return (logn + 1) << logn; +// } /* * Inner function for ffLDL_fft(). It expects the matrix to be both @@ -50,45 +52,45 @@ int ffLDL_treesize(int logn) * * tmp[] must have room for at least one polynomial. */ - void ffLDL_fft_inner(FalconFPR[] srctree, int tree, - FalconFPR[] srcg0, int g0, FalconFPR[] srcg1, int g1, - int logn, FalconFPR[] srctmp, int tmp) - { - int n, hn; - - n = MKN(logn); - if (n == 1) - { - srctree[tree + 0] = srcg0[g0 + 0]; - return; - } - hn = n >> 1; - - /* - * The LDL decomposition yields L (which is written in the tree) - * and the diagonal of D. Since d00 = g0, we just write d11 - * into tmp. - */ - fft.poly_LDLmv_fft(srctmp, tmp, srctree, tree, srcg0, g0, srcg1, g1, srcg0, g0, logn); - - /* - * Split d00 (currently in g0) and d11 (currently in tmp). We - * reuse g0 and g1 as temporary storage spaces: - * d00 splits into g1, g1+hn - * d11 splits into g0, g0+hn - */ - fft.poly_split_fft(srcg1, g1, srcg1, g1 + hn, srcg0, g0, logn); - fft.poly_split_fft(srcg0, g0, srcg0, g0 + hn, srctmp, tmp, logn); - - /* - * Each split result is the first row of a new auto-adjoint - * quasicyclic matrix for the next recursive step. - */ - ffLDL_fft_inner(srctree, tree + n, - srcg1, g1, srcg1, g1 + hn, logn - 1, srctmp, tmp); - ffLDL_fft_inner(srctree, tree + n + ffLDL_treesize(logn - 1), - srcg0, g0, srcg0, g0 + hn, logn - 1, srctmp, tmp); - } +// void ffLDL_fft_inner(double[] srctree, int tree, +// double[] srcg0, int g0, double[] srcg1, int g1, +// int logn, double[] srctmp, int tmp) +// { +// int n, hn; +// +// n = MKN(logn); +// if (n == 1) +// { +// srctree[tree] = srcg0[g0]; +// return; +// } +// hn = n >> 1; +// +// /* +// * The LDL decomposition yields L (which is written in the tree) +// * and the diagonal of D. Since d00 = g0, we just write d11 +// * into tmp. +// */ +// fft.poly_LDLmv_fft(srctmp, tmp, srctree, tree, srcg0, g0, srcg1, g1, srcg0, g0, logn); +// +// /* +// * Split d00 (currently in g0) and d11 (currently in tmp). We +// * reuse g0 and g1 as temporary storage spaces: +// * d00 splits into g1, g1+hn +// * d11 splits into g0, g0+hn +// */ +// fft.poly_split_fft(srcg1, g1, srcg1, g1 + hn, srcg0, g0, logn); +// fft.poly_split_fft(srcg0, g0, srcg0, g0 + hn, srctmp, tmp, logn); +// +// /* +// * Each split result is the first row of a new auto-adjoint +// * quasicyclic matrix for the next recursive step. +// */ +// ffLDL_fft_inner(srctree, tree + n, +// srcg1, g1, srcg1, g1 + hn, logn - 1, srctmp, tmp); +// ffLDL_fft_inner(srctree, tree + n + ffLDL_treesize(logn - 1), +// srcg0, g0, srcg0, g0 + hn, logn - 1, srctmp, tmp); +// } /* * Compute the ffLDL tree of an auto-adjoint matrix G. The matrix @@ -101,66 +103,66 @@ void ffLDL_fft_inner(FalconFPR[] srctree, int tree, * arrays g00, g01 and g11. tmp[] should have room for at least three * polynomials of 2^logn elements each. */ - void ffLDL_fft(FalconFPR[] srctree, int tree, FalconFPR[] srcg00, int g00, - FalconFPR[] srcg01, int g01, FalconFPR[] srcg11, int g11, - int logn, FalconFPR[] srctmp, int tmp) - { - int n, hn; - int d00, d11; - - n = MKN(logn); - if (n == 1) - { - srctree[tree + 0] = srcg00[g00 + 0]; - return; - } - hn = n >> 1; - d00 = tmp; - d11 = tmp + n; - tmp += n << 1; - -// memcpy(d00, g00, n * sizeof *g00); - System.arraycopy(srcg00, g00, srctmp, d00, n); - fft.poly_LDLmv_fft(srctmp, d11, srctree, tree, srcg00, g00, srcg01, g01, srcg11, g11, logn); - - fft.poly_split_fft(srctmp, tmp, srctmp, tmp + hn, srctmp, d00, logn); - fft.poly_split_fft(srctmp, d00, srctmp, d00 + hn, srctmp, d11, logn); -// memcpy(d11, tmp, n * sizeof *tmp); - System.arraycopy(srctmp, tmp, srctmp, d11, n); - ffLDL_fft_inner(srctree, tree + n, - srctmp, d11, srctmp, d11 + hn, logn - 1, srctmp, tmp); - ffLDL_fft_inner(srctree, tree + n + ffLDL_treesize(logn - 1), - srctmp, d00, srctmp, d00 + hn, logn - 1, srctmp, tmp); - } +// void ffLDL_fft(double[] srctree, int tree, double[] srcg00, int g00, +// double[] srcg01, int g01, double[] srcg11, int g11, +// int logn, double[] srctmp, int tmp) +// { +// int n, hn; +// int d00, d11; +// +// n = MKN(logn); +// if (n == 1) +// { +// srctree[tree + 0] = srcg00[g00 + 0]; +// return; +// } +// hn = n >> 1; +// d00 = tmp; +// d11 = tmp + n; +// tmp += n << 1; +// +//// memcpy(d00, g00, n * sizeof *g00); +// System.arraycopy(srcg00, g00, srctmp, d00, n); +// fft.poly_LDLmv_fft(srctmp, d11, srctree, tree, srcg00, g00, srcg01, g01, srcg11, g11, logn); +// +// fft.poly_split_fft(srctmp, tmp, srctmp, tmp + hn, srctmp, d00, logn); +// fft.poly_split_fft(srctmp, d00, srctmp, d00 + hn, srctmp, d11, logn); +//// memcpy(d11, tmp, n * sizeof *tmp); +// System.arraycopy(srctmp, tmp, srctmp, d11, n); +// ffLDL_fft_inner(srctree, tree + n, +// srctmp, d11, srctmp, d11 + hn, logn - 1, srctmp, tmp); +// ffLDL_fft_inner(srctree, tree + n + ffLDL_treesize(logn - 1), +// srctmp, d00, srctmp, d00 + hn, logn - 1, srctmp, tmp); +// } /* * Normalize an ffLDL tree: each leaf of value x is replaced with * sigma / sqrt(x). */ - void ffLDL_binary_normalize(FalconFPR[] srctree, int tree, int orig_logn, int logn) - { - /* - * TODO: make an iterative version. - */ - int n; - - n = MKN(logn); - if (n == 1) - { - /* - * We actually store in the tree leaf the inverse of - * the value mandated by the specification: this - * saves a division both here and in the sampler. - */ - srctree[tree + 0] = fpr.fpr_mul(fpr.fpr_sqrt(srctree[tree + 0]), fpr.fpr_inv_sigma[orig_logn]); - } - else - { - ffLDL_binary_normalize(srctree, tree + n, orig_logn, logn - 1); - ffLDL_binary_normalize(srctree, tree + n + ffLDL_treesize(logn - 1), - orig_logn, logn - 1); - } - } +// void ffLDL_binary_normalize(double[] srctree, int tree, int orig_logn, int logn) +// { +// /* +// * TODO: make an iterative version. +// */ +// int n; +// +// n = MKN(logn); +// if (n == 1) +// { +// /* +// * We actually store in the tree leaf the inverse of +// * the value mandated by the specification: this +// * saves a division both here and in the sampler. +// */ +// srctree[tree + 0] = fpr.fpr_mul(fpr.fpr_sqrt(srctree[tree + 0]), fpr.fpr_inv_sigma[orig_logn]); +// } +// else +// { +// ffLDL_binary_normalize(srctree, tree + n, orig_logn, logn - 1); +// ffLDL_binary_normalize(srctree, tree + n + ffLDL_treesize(logn - 1), +// orig_logn, logn - 1); +// } +// } /* =================================================================== */ @@ -168,14 +170,14 @@ void ffLDL_binary_normalize(FalconFPR[] srctree, int tree, int orig_logn, int lo * Convert an integer polynomial (with small values) into the * representation with complex numbers. */ - void smallints_to_fpr(FalconFPR[] srcr, int r, byte[] srct, int t, int logn) + void smallints_to_fpr(double[] srcr, int r, byte[] srct, int logn) { int n, u; - n = MKN(logn); + n = 1 << logn; for (u = 0; u < n; u++) { - srcr[r + u] = fpr.fpr_of(srct[t + u]); // t is signed + srcr[r + u] = srct[u]; // t is signed } } @@ -185,124 +187,124 @@ void smallints_to_fpr(FalconFPR[] srcr, int r, byte[] srct, int t, int logn) * - The ffLDL tree */ - int skoff_b00(int logn) - { -// (void)logn; - return 0; - } - - int skoff_b01(int logn) - { - return MKN(logn); - } - - int skoff_b10(int logn) - { - return 2 * MKN(logn); - } - - int skoff_b11(int logn) - { - return 3 * MKN(logn); - } - - int skoff_tree(int logn) - { - return 4 * MKN(logn); - } +// int skoff_b00(int logn) +// { +//// (void)logn; +// return 0; +// } +// +// int skoff_b01(int logn) +// { +// return MKN(logn); +// } +// +// int skoff_b10(int logn) +// { +// return 2 * MKN(logn); +// } +// +// int skoff_b11(int logn) +// { +// return 3 * MKN(logn); +// } +// +// int skoff_tree(int logn) +// { +// return 4 * MKN(logn); +// } /* see inner.h */ - void expand_privkey(FalconFPR[] srcexpanded_key, int expanded_key, - byte[] srcf, int f, byte[] srcg, int g, - byte[] srcF, int F, byte[] srcG, int G, - int logn, FalconFPR[] srctmp, int tmp) - { - int n; - int rf, rg, rF, rG; - int b00, b01, b10, b11; - int g00, g01, g11, gxx; - int tree; - - n = MKN(logn); - b00 = expanded_key + skoff_b00(logn); - b01 = expanded_key + skoff_b01(logn); - b10 = expanded_key + skoff_b10(logn); - b11 = expanded_key + skoff_b11(logn); - tree = expanded_key + skoff_tree(logn); - - /* - * We load the private key elements directly into the B0 matrix, - * since B0 = [[g, -f], [G, -F]]. - */ - rf = b01; - rg = b00; - rF = b11; - rG = b10; - - smallints_to_fpr(srcexpanded_key, rf, srcf, f, logn); - smallints_to_fpr(srcexpanded_key, rg, srcg, g, logn); - smallints_to_fpr(srcexpanded_key, rF, srcF, F, logn); - smallints_to_fpr(srcexpanded_key, rG, srcG, G, logn); - - /* - * Compute the FFT for the key elements, and negate f and F. - */ - fft.FFT(srcexpanded_key, rf, logn); - fft.FFT(srcexpanded_key, rg, logn); - fft.FFT(srcexpanded_key, rF, logn); - fft.FFT(srcexpanded_key, rG, logn); - fft.poly_neg(srcexpanded_key, rf, logn); - fft.poly_neg(srcexpanded_key, rF, logn); - - /* - * The Gram matrix is G = B·B*. Formulas are: - * g00 = b00*adj(b00) + b01*adj(b01) - * g01 = b00*adj(b10) + b01*adj(b11) - * g10 = b10*adj(b00) + b11*adj(b01) - * g11 = b10*adj(b10) + b11*adj(b11) - * - * For historical reasons, this implementation uses - * g00, g01 and g11 (upper triangle). - */ - g00 = tmp; // the b__ are in srcexpanded_key and g__ are int srctmp - g01 = g00 + n; - g11 = g01 + n; - gxx = g11 + n; - -// memcpy(g00, b00, n * sizeof *b00); - System.arraycopy(srcexpanded_key, b00, srctmp, g00, n); - fft.poly_mulselfadj_fft(srctmp, g00, logn); -// memcpy(gxx, b01, n * sizeof *b01); - System.arraycopy(srcexpanded_key, b01, srctmp, gxx, n); - fft.poly_mulselfadj_fft(srctmp, gxx, logn); - fft.poly_add(srctmp, g00, srctmp, gxx, logn); - -// memcpy(g01, b00, n * sizeof *b00); - System.arraycopy(srcexpanded_key, b00, srctmp, g01, n); - fft.poly_muladj_fft(srctmp, g01, srcexpanded_key, b10, logn); -// memcpy(gxx, b01, n * sizeof *b01); - System.arraycopy(srcexpanded_key, b01, srctmp, gxx, n); - fft.poly_muladj_fft(srctmp, gxx, srcexpanded_key, b11, logn); - fft.poly_add(srctmp, g01, srctmp, gxx, logn); - -// memcpy(g11, b10, n * sizeof *b10); - System.arraycopy(srcexpanded_key, b10, srctmp, g11, n); - fft.poly_mulselfadj_fft(srctmp, g11, logn); -// memcpy(gxx, b11, n * sizeof *b11); - System.arraycopy(srcexpanded_key, b11, srctmp, gxx, n); - fft.poly_mulselfadj_fft(srctmp, gxx, logn); - fft.poly_add(srctmp, g11, srctmp, gxx, logn); - - /* - * Compute the Falcon tree. - */ - ffLDL_fft(srcexpanded_key, tree, srctmp, g00, srctmp, g01, srctmp, g11, logn, srctmp, gxx); - - /* - * Normalize tree. - */ - ffLDL_binary_normalize(srcexpanded_key, tree, logn, logn); - } +// void expand_privkey(double[] srcexpanded_key, int expanded_key, +// byte[] srcf, int f, byte[] srcg, int g, +// byte[] srcF, int F, byte[] srcG, int G, +// int logn, double[] srctmp, int tmp) +// { +// int n; +// int rf, rg, rF, rG; +// int b00, b01, b10, b11; +// int g00, g01, g11, gxx; +// int tree; +// +// n = MKN(logn); +// b00 = expanded_key + skoff_b00(logn); +// b01 = expanded_key + skoff_b01(logn); +// b10 = expanded_key + skoff_b10(logn); +// b11 = expanded_key + skoff_b11(logn); +// tree = expanded_key + skoff_tree(logn); +// +// /* +// * We load the private key elements directly into the B0 matrix, +// * since B0 = [[g, -f], [G, -F]]. +// */ +// rf = b01; +// rg = b00; +// rF = b11; +// rG = b10; +// +// smallints_to_fpr(srcexpanded_key, rf, srcf, f, logn); +// smallints_to_fpr(srcexpanded_key, rg, srcg, g, logn); +// smallints_to_fpr(srcexpanded_key, rF, srcF, F, logn); +// smallints_to_fpr(srcexpanded_key, rG, srcG, G, logn); +// +// /* +// * Compute the FFT for the key elements, and negate f and F. +// */ +// fft.FFT(srcexpanded_key, rf, logn); +// fft.FFT(srcexpanded_key, rg, logn); +// fft.FFT(srcexpanded_key, rF, logn); +// fft.FFT(srcexpanded_key, rG, logn); +// fft.poly_neg(srcexpanded_key, rf, logn); +// fft.poly_neg(srcexpanded_key, rF, logn); +// +// /* +// * The Gram matrix is G = B·B*. Formulas are: +// * g00 = b00*adj(b00) + b01*adj(b01) +// * g01 = b00*adj(b10) + b01*adj(b11) +// * g10 = b10*adj(b00) + b11*adj(b01) +// * g11 = b10*adj(b10) + b11*adj(b11) +// * +// * For historical reasons, this implementation uses +// * g00, g01 and g11 (upper triangle). +// */ +// g00 = tmp; // the b__ are in srcexpanded_key and g__ are int srctmp +// g01 = g00 + n; +// g11 = g01 + n; +// gxx = g11 + n; +// +//// memcpy(g00, b00, n * sizeof *b00); +// System.arraycopy(srcexpanded_key, b00, srctmp, g00, n); +// fft.poly_mulselfadj_fft(srctmp, g00, logn); +//// memcpy(gxx, b01, n * sizeof *b01); +// System.arraycopy(srcexpanded_key, b01, srctmp, gxx, n); +// fft.poly_mulselfadj_fft(srctmp, gxx, logn); +// fft.poly_add(srctmp, g00, srctmp, gxx, logn); +// +//// memcpy(g01, b00, n * sizeof *b00); +// System.arraycopy(srcexpanded_key, b00, srctmp, g01, n); +// fft.poly_muladj_fft(srctmp, g01, srcexpanded_key, b10, logn); +//// memcpy(gxx, b01, n * sizeof *b01); +// System.arraycopy(srcexpanded_key, b01, srctmp, gxx, n); +// fft.poly_muladj_fft(srctmp, gxx, srcexpanded_key, b11, logn); +// fft.poly_add(srctmp, g01, srctmp, gxx, logn); +// +//// memcpy(g11, b10, n * sizeof *b10); +// System.arraycopy(srcexpanded_key, b10, srctmp, g11, n); +// fft.poly_mulselfadj_fft(srctmp, g11, logn); +//// memcpy(gxx, b11, n * sizeof *b11); +// System.arraycopy(srcexpanded_key, b11, srctmp, gxx, n); +// fft.poly_mulselfadj_fft(srctmp, gxx, logn); +// fft.poly_add(srctmp, g11, srctmp, gxx, logn); +// +// /* +// * Compute the Falcon tree. +// */ +// ffLDL_fft(srcexpanded_key, tree, srctmp, g00, srctmp, g01, srctmp, g11, logn, srctmp, gxx); +// +// /* +// * Normalize tree. +// */ +// ffLDL_binary_normalize(srcexpanded_key, tree, logn, logn); +// } /* * Perform Fast Fourier Sampling for target vector t. The Gram matrix @@ -310,10 +312,10 @@ void expand_privkey(FalconFPR[] srcexpanded_key, int expanded_key, * is written over (t0,t1). The Gram matrix is modified as well. The * tmp[] buffer must have room for four polynomials. */ - void ffSampling_fft_dyntree(SamplerZ samp, SamplerCtx samp_ctx, - FalconFPR[] srct0, int t0, FalconFPR[] srct1, int t1, - FalconFPR[] srcg00, int g00, FalconFPR[] srcg01, int g01, FalconFPR[] srcg11, int g11, - int orig_logn, int logn, FalconFPR[] srctmp, int tmp) + void ffSampling_fft_dyntree(SamplerCtx samp_ctx, + double[] srct0, int t0, double[] srct1, int t1, + double[] srcg00, int g00, double[] srcg01, int g01, double[] srcg11, int g11, + int orig_logn, int logn, double[] srctmp, int tmp) { int n, hn; int z0, z1; @@ -325,12 +327,16 @@ void ffSampling_fft_dyntree(SamplerZ samp, SamplerCtx samp_ctx, */ if (logn == 0) { - FalconFPR leaf; - - leaf = srcg00[g00 + 0]; - leaf = fpr.fpr_mul(fpr.fpr_sqrt(leaf), fpr.fpr_inv_sigma[orig_logn]); - srct0[t0 + 0] = fpr.fpr_of(samp.sample(samp_ctx, srct0[t0 + 0], leaf)); - srct1[t1 + 0] = fpr.fpr_of(samp.sample(samp_ctx, srct1[t1 + 0], leaf)); + double leaf; + +// leaf = srcg00[g00 + 0]; +// leaf = fpr.fpr_mul(fpr.fpr_sqrt(leaf), fpr.fpr_inv_sigma[orig_logn]); +// srct0[t0 + 0] = fpr.fpr_of(samp.sample(samp_ctx, srct0[t0 + 0], leaf)); +// srct1[t1 + 0] = fpr.fpr_of(samp.sample(samp_ctx, srct1[t1 + 0], leaf)); + leaf = srcg00[g00]; + leaf = Math.sqrt(leaf) * FPREngine.fpr_inv_sigma[orig_logn]; + srct0[t0] = SamplerZ.sample(samp_ctx, srct0[t0], leaf); + srct1[t1] = SamplerZ.sample(samp_ctx, srct1[t1], leaf); return; } @@ -341,16 +347,16 @@ void ffSampling_fft_dyntree(SamplerZ samp, SamplerCtx samp_ctx, * Decompose G into LDL. We only need d00 (identical to g00), * d11, and l10; we do that in place. */ - fft.poly_LDL_fft(srcg00, g00, srcg01, g01, srcg11, g11, logn); + FalconFFT.poly_LDL_fft(srcg00, g00, srcg01, g01, srcg11, g11, logn); /* * Split d00 and d11 and expand them into half-size quasi-cyclic * Gram matrices. We also save l10 in tmp[]. */ - fft.poly_split_fft(srctmp, tmp, srctmp, tmp + hn, srcg00, g00, logn); + FalconFFT.poly_split_fft(srctmp, tmp, srctmp, tmp + hn, srcg00, g00, logn); // memcpy(g00, tmp, n * sizeof *tmp); System.arraycopy(srctmp, tmp, srcg00, g00, n); - fft.poly_split_fft(srctmp, tmp, srctmp, tmp + hn, srcg11, g11, logn); + FalconFFT.poly_split_fft(srctmp, tmp, srctmp, tmp + hn, srcg11, g11, logn); // memcpy(g11, tmp, n * sizeof *tmp); System.arraycopy(srctmp, tmp, srcg11, g11, n); // memcpy(tmp, g01, n * sizeof *g01); @@ -374,10 +380,10 @@ void ffSampling_fft_dyntree(SamplerZ samp, SamplerCtx samp_ctx, * back into tmp + 2*n. */ z1 = tmp + n; - fft.poly_split_fft(srctmp, z1, srctmp, z1 + hn, srct1, t1, logn); - ffSampling_fft_dyntree(samp, samp_ctx, srctmp, z1, srctmp, z1 + hn, + FalconFFT.poly_split_fft(srctmp, z1, srctmp, z1 + hn, srct1, t1, logn); + ffSampling_fft_dyntree(samp_ctx, srctmp, z1, srctmp, z1 + hn, srcg11, g11, srcg11, g11 + hn, srcg01, g01 + hn, orig_logn, logn - 1, srctmp, z1 + n); - fft.poly_merge_fft(srctmp, tmp + (n << 1), srctmp, z1, srctmp, z1 + hn, logn); + FalconFFT.poly_merge_fft(srctmp, tmp + (n << 1), srctmp, z1, srctmp, z1 + hn, logn); /* * Compute tb0 = t0 + (t1 - z1) * l10. @@ -388,250 +394,250 @@ void ffSampling_fft_dyntree(SamplerZ samp, SamplerCtx samp_ctx, */ // memcpy(z1, t1, n * sizeof *t1); System.arraycopy(srct1, t1, srctmp, z1, n); - fft.poly_sub(srctmp, z1, srctmp, tmp + (n << 1), logn); + FalconFFT.poly_sub(srctmp, z1, srctmp, tmp + (n << 1), logn); // memcpy(t1, tmp + (n << 1), n * sizeof *tmp); System.arraycopy(srctmp, tmp + (n << 1), srct1, t1, n); - fft.poly_mul_fft(srctmp, tmp, srctmp, z1, logn); - fft.poly_add(srct0, t0, srctmp, tmp, logn); + FalconFFT.poly_mul_fft(srctmp, tmp, srctmp, z1, logn); + FalconFFT.poly_add(srct0, t0, srctmp, tmp, logn); /* * Second recursive invocation, on the split tb0 (currently in t0) * and the left sub-tree. */ z0 = tmp; - fft.poly_split_fft(srctmp, z0, srctmp, z0 + hn, srct0, t0, logn); - ffSampling_fft_dyntree(samp, samp_ctx, srctmp, z0, srctmp, z0 + hn, + FalconFFT.poly_split_fft(srctmp, z0, srctmp, z0 + hn, srct0, t0, logn); + ffSampling_fft_dyntree(samp_ctx, srctmp, z0, srctmp, z0 + hn, srcg00, g00, srcg00, g00 + hn, srcg01, g01, orig_logn, logn - 1, srctmp, z0 + n); - fft.poly_merge_fft(srct0, t0, srctmp, z0, srctmp, z0 + hn, logn); + FalconFFT.poly_merge_fft(srct0, t0, srctmp, z0, srctmp, z0 + hn, logn); } /* * Perform Fast Fourier Sampling for target vector t and LDL tree T. * tmp[] must have size for at least two polynomials of size 2^logn. */ - void ffSampling_fft(SamplerZ samp, SamplerCtx samp_ctx, - FalconFPR[] srcz0, int z0, FalconFPR[] srcz1, int z1, - FalconFPR[] srctree, int tree, - FalconFPR[] srct0, int t0, FalconFPR[] srct1, int t1, int logn, - FalconFPR[] srctmp, int tmp) - { - int n, hn; - int tree0, tree1; - - /* - * When logn == 2, we inline the last two recursion levels. - */ - if (logn == 2) - { - FalconFPR x0, x1, y0, y1, w0, w1, w2, w3, sigma; - FalconFPR a_re, a_im, b_re, b_im, c_re, c_im; - - tree0 = tree + 4; - tree1 = tree + 8; - - /* - * We split t1 into w*, then do the recursive invocation, - * with output in w*. We finally merge back into z1. - */ - a_re = srct1[t1 + 0]; - a_im = srct1[t1 + 2]; - b_re = srct1[t1 + 1]; - b_im = srct1[t1 + 3]; - c_re = fpr.fpr_add(a_re, b_re); - c_im = fpr.fpr_add(a_im, b_im); - w0 = fpr.fpr_half(c_re); - w1 = fpr.fpr_half(c_im); - c_re = fpr.fpr_sub(a_re, b_re); - c_im = fpr.fpr_sub(a_im, b_im); - w2 = fpr.fpr_mul(fpr.fpr_add(c_re, c_im), fpr.fpr_invsqrt8); - w3 = fpr.fpr_mul(fpr.fpr_sub(c_im, c_re), fpr.fpr_invsqrt8); - - x0 = w2; - x1 = w3; - sigma = srctree[tree1 + 3]; - w2 = fpr.fpr_of(samp.sample(samp_ctx, x0, sigma)); - w3 = fpr.fpr_of(samp.sample(samp_ctx, x1, sigma)); - a_re = fpr.fpr_sub(x0, w2); - a_im = fpr.fpr_sub(x1, w3); - b_re = srctree[tree1 + 0]; - b_im = srctree[tree1 + 1]; - c_re = fpr.fpr_sub(fpr.fpr_mul(a_re, b_re), fpr.fpr_mul(a_im, b_im)); - c_im = fpr.fpr_add(fpr.fpr_mul(a_re, b_im), fpr.fpr_mul(a_im, b_re)); - x0 = fpr.fpr_add(c_re, w0); - x1 = fpr.fpr_add(c_im, w1); - sigma = srctree[tree1 + 2]; - w0 = fpr.fpr_of(samp.sample(samp_ctx, x0, sigma)); - w1 = fpr.fpr_of(samp.sample(samp_ctx, x1, sigma)); - - a_re = w0; - a_im = w1; - b_re = w2; - b_im = w3; - c_re = fpr.fpr_mul(fpr.fpr_sub(b_re, b_im), fpr.fpr_invsqrt2); - c_im = fpr.fpr_mul(fpr.fpr_add(b_re, b_im), fpr.fpr_invsqrt2); - srcz1[z1 + 0] = w0 = fpr.fpr_add(a_re, c_re); - srcz1[z1 + 2] = w2 = fpr.fpr_add(a_im, c_im); - srcz1[z1 + 1] = w1 = fpr.fpr_sub(a_re, c_re); - srcz1[z1 + 3] = w3 = fpr.fpr_sub(a_im, c_im); - - /* - * Compute tb0 = t0 + (t1 - z1) * L. Value tb0 ends up in w*. - */ - w0 = fpr.fpr_sub(srct1[t1 + 0], w0); - w1 = fpr.fpr_sub(srct1[t1 + 1], w1); - w2 = fpr.fpr_sub(srct1[t1 + 2], w2); - w3 = fpr.fpr_sub(srct1[t1 + 3], w3); - - a_re = w0; - a_im = w2; - b_re = srctree[tree + 0]; - b_im = srctree[tree + 2]; - w0 = fpr.fpr_sub(fpr.fpr_mul(a_re, b_re), fpr.fpr_mul(a_im, b_im)); - w2 = fpr.fpr_add(fpr.fpr_mul(a_re, b_im), fpr.fpr_mul(a_im, b_re)); - a_re = w1; - a_im = w3; - b_re = srctree[tree + 1]; - b_im = srctree[tree + 3]; - w1 = fpr.fpr_sub(fpr.fpr_mul(a_re, b_re), fpr.fpr_mul(a_im, b_im)); - w3 = fpr.fpr_add(fpr.fpr_mul(a_re, b_im), fpr.fpr_mul(a_im, b_re)); - - w0 = fpr.fpr_add(w0, srct0[t0 + 0]); - w1 = fpr.fpr_add(w1, srct0[t0 + 1]); - w2 = fpr.fpr_add(w2, srct0[t0 + 2]); - w3 = fpr.fpr_add(w3, srct0[t0 + 3]); - - /* - * Second recursive invocation. - */ - a_re = w0; - a_im = w2; - b_re = w1; - b_im = w3; - c_re = fpr.fpr_add(a_re, b_re); - c_im = fpr.fpr_add(a_im, b_im); - w0 = fpr.fpr_half(c_re); - w1 = fpr.fpr_half(c_im); - c_re = fpr.fpr_sub(a_re, b_re); - c_im = fpr.fpr_sub(a_im, b_im); - w2 = fpr.fpr_mul(fpr.fpr_add(c_re, c_im), fpr.fpr_invsqrt8); - w3 = fpr.fpr_mul(fpr.fpr_sub(c_im, c_re), fpr.fpr_invsqrt8); - - x0 = w2; - x1 = w3; - sigma = srctree[tree0 + 3]; - w2 = y0 = fpr.fpr_of(samp.sample(samp_ctx, x0, sigma)); - w3 = y1 = fpr.fpr_of(samp.sample(samp_ctx, x1, sigma)); - a_re = fpr.fpr_sub(x0, y0); - a_im = fpr.fpr_sub(x1, y1); - b_re = srctree[tree0 + 0]; - b_im = srctree[tree0 + 1]; - c_re = fpr.fpr_sub(fpr.fpr_mul(a_re, b_re), fpr.fpr_mul(a_im, b_im)); - c_im = fpr.fpr_add(fpr.fpr_mul(a_re, b_im), fpr.fpr_mul(a_im, b_re)); - x0 = fpr.fpr_add(c_re, w0); - x1 = fpr.fpr_add(c_im, w1); - sigma = srctree[tree0 + 2]; - w0 = fpr.fpr_of(samp.sample(samp_ctx, x0, sigma)); - w1 = fpr.fpr_of(samp.sample(samp_ctx, x1, sigma)); - - a_re = w0; - a_im = w1; - b_re = w2; - b_im = w3; - c_re = fpr.fpr_mul(fpr.fpr_sub(b_re, b_im), fpr.fpr_invsqrt2); - c_im = fpr.fpr_mul(fpr.fpr_add(b_re, b_im), fpr.fpr_invsqrt2); - srcz0[z0 + 0] = fpr.fpr_add(a_re, c_re); - srcz0[z0 + 2] = fpr.fpr_add(a_im, c_im); - srcz0[z0 + 1] = fpr.fpr_sub(a_re, c_re); - srcz0[z0 + 3] = fpr.fpr_sub(a_im, c_im); - - return; - } - - /* - * Case logn == 1 is reachable only when using Falcon-2 (the - * smallest size for which Falcon is mathematically defined, but - * of course way too insecure to be of any use). - */ - if (logn == 1) - { - FalconFPR x0, x1, y0, y1, sigma; - FalconFPR a_re, a_im, b_re, b_im, c_re, c_im; - - x0 = srct1[t1 + 0]; - x1 = srct1[t1 + 1]; - sigma = srctree[tree + 3]; - srcz1[z1 + 0] = y0 = fpr.fpr_of(samp.sample(samp_ctx, x0, sigma)); - srcz1[z1 + 1] = y1 = fpr.fpr_of(samp.sample(samp_ctx, x1, sigma)); - a_re = fpr.fpr_sub(x0, y0); - a_im = fpr.fpr_sub(x1, y1); - b_re = srctree[tree + 0]; - b_im = srctree[tree + 1]; - c_re = fpr.fpr_sub(fpr.fpr_mul(a_re, b_re), fpr.fpr_mul(a_im, b_im)); - c_im = fpr.fpr_add(fpr.fpr_mul(a_re, b_im), fpr.fpr_mul(a_im, b_re)); - x0 = fpr.fpr_add(c_re, srct0[t0 + 0]); - x1 = fpr.fpr_add(c_im, srct0[t0 + 1]); - sigma = srctree[tree + 2]; - srcz0[z0 + 0] = fpr.fpr_of(samp.sample(samp_ctx, x0, sigma)); - srcz0[z0 + 1] = fpr.fpr_of(samp.sample(samp_ctx, x1, sigma)); - - return; - } - - /* - * Normal end of recursion is for logn == 0. Since the last - * steps of the recursions were inlined in the blocks above - * (when logn == 1 or 2), this case is not reachable, and is - * retained here only for documentation purposes. - - if (logn == 0) { - fpr x0, x1, sigma; - - x0 = t0[0]; - x1 = t1[0]; - sigma = tree[0]; - z0[0] = fpr_of(samp(samp_ctx, x0, sigma)); - z1[0] = fpr_of(samp(samp_ctx, x1, sigma)); - return; - } - - */ - - /* - * General recursive case (logn >= 3). - */ - - n = 1 << logn; - hn = n >> 1; - tree0 = tree + n; - tree1 = tree + n + ffLDL_treesize(logn - 1); - - /* - * We split t1 into z1 (reused as temporary storage), then do - * the recursive invocation, with output in tmp. We finally - * merge back into z1. - */ - fft.poly_split_fft(srcz1, z1, srcz1, z1 + hn, srct1, t1, logn); - ffSampling_fft(samp, samp_ctx, srctmp, tmp, srctmp, tmp + hn, - srctree, tree1, srcz1, z1, srcz1, z1 + hn, logn - 1, srctmp, tmp + n); - fft.poly_merge_fft(srcz1, z1, srctmp, tmp, srctmp, tmp + hn, logn); - - /* - * Compute tb0 = t0 + (t1 - z1) * L. Value tb0 ends up in tmp[]. - */ -// memcpy(tmp, t1, n * sizeof *t1); - System.arraycopy(srct1, t1, srctmp, tmp, n); - fft.poly_sub(srctmp, tmp, srcz1, z1, logn); - fft.poly_mul_fft(srctmp, tmp, srctree, tree, logn); - fft.poly_add(srctmp, tmp, srct0, t0, logn); - - /* - * Second recursive invocation. - */ - fft.poly_split_fft(srcz0, z0, srcz0, z0 + hn, srctmp, tmp, logn); - ffSampling_fft(samp, samp_ctx, srctmp, tmp, srctmp, tmp + hn, - srctree, tree0, srcz0, z0, srcz0, z0 + hn, logn - 1, srctmp, tmp + n); - fft.poly_merge_fft(srcz0, z0, srctmp, tmp, srctmp, tmp + hn, logn); - } +// void ffSampling_fft(SamplerZ samp, SamplerCtx samp_ctx, +// double[] srcz0, int z0, double[] srcz1, int z1, +// double[] srctree, int tree, +// double[] srct0, int t0, double[] srct1, int t1, int logn, +// double[] srctmp, int tmp) +// { +// int n, hn; +// int tree0, tree1; +// +// /* +// * When logn == 2, we inline the last two recursion levels. +// */ +// if (logn == 2) +// { +// double x0, x1, y0, y1, w0, w1, w2, w3, sigma; +// double a_re, a_im, b_re, b_im, c_re, c_im; +// +// tree0 = tree + 4; +// tree1 = tree + 8; +// +// /* +// * We split t1 into w*, then do the recursive invocation, +// * with output in w*. We finally merge back into z1. +// */ +// a_re = srct1[t1 + 0]; +// a_im = srct1[t1 + 2]; +// b_re = srct1[t1 + 1]; +// b_im = srct1[t1 + 3]; +// c_re = fpr.fpr_add(a_re, b_re); +// c_im = fpr.fpr_add(a_im, b_im); +// w0 = fpr.fpr_half(c_re); +// w1 = fpr.fpr_half(c_im); +// c_re = fpr.fpr_sub(a_re, b_re); +// c_im = fpr.fpr_sub(a_im, b_im); +// w2 = fpr.fpr_mul(fpr.fpr_add(c_re, c_im), fpr.fpr_invsqrt8); +// w3 = fpr.fpr_mul(fpr.fpr_sub(c_im, c_re), fpr.fpr_invsqrt8); +// +// x0 = w2; +// x1 = w3; +// sigma = srctree[tree1 + 3]; +// w2 = fpr.fpr_of(samp.sample(samp_ctx, x0, sigma)); +// w3 = fpr.fpr_of(samp.sample(samp_ctx, x1, sigma)); +// a_re = fpr.fpr_sub(x0, w2); +// a_im = fpr.fpr_sub(x1, w3); +// b_re = srctree[tree1 + 0]; +// b_im = srctree[tree1 + 1]; +// c_re = fpr.fpr_sub(fpr.fpr_mul(a_re, b_re), fpr.fpr_mul(a_im, b_im)); +// c_im = fpr.fpr_add(fpr.fpr_mul(a_re, b_im), fpr.fpr_mul(a_im, b_re)); +// x0 = fpr.fpr_add(c_re, w0); +// x1 = fpr.fpr_add(c_im, w1); +// sigma = srctree[tree1 + 2]; +// w0 = fpr.fpr_of(samp.sample(samp_ctx, x0, sigma)); +// w1 = fpr.fpr_of(samp.sample(samp_ctx, x1, sigma)); +// +// a_re = w0; +// a_im = w1; +// b_re = w2; +// b_im = w3; +// c_re = fpr.fpr_mul(fpr.fpr_sub(b_re, b_im), fpr.fpr_invsqrt2); +// c_im = fpr.fpr_mul(fpr.fpr_add(b_re, b_im), fpr.fpr_invsqrt2); +// srcz1[z1 + 0] = w0 = fpr.fpr_add(a_re, c_re); +// srcz1[z1 + 2] = w2 = fpr.fpr_add(a_im, c_im); +// srcz1[z1 + 1] = w1 = fpr.fpr_sub(a_re, c_re); +// srcz1[z1 + 3] = w3 = fpr.fpr_sub(a_im, c_im); +// +// /* +// * Compute tb0 = t0 + (t1 - z1) * L. Value tb0 ends up in w*. +// */ +// w0 = fpr.fpr_sub(srct1[t1 + 0], w0); +// w1 = fpr.fpr_sub(srct1[t1 + 1], w1); +// w2 = fpr.fpr_sub(srct1[t1 + 2], w2); +// w3 = fpr.fpr_sub(srct1[t1 + 3], w3); +// +// a_re = w0; +// a_im = w2; +// b_re = srctree[tree + 0]; +// b_im = srctree[tree + 2]; +// w0 = fpr.fpr_sub(fpr.fpr_mul(a_re, b_re), fpr.fpr_mul(a_im, b_im)); +// w2 = fpr.fpr_add(fpr.fpr_mul(a_re, b_im), fpr.fpr_mul(a_im, b_re)); +// a_re = w1; +// a_im = w3; +// b_re = srctree[tree + 1]; +// b_im = srctree[tree + 3]; +// w1 = fpr.fpr_sub(fpr.fpr_mul(a_re, b_re), fpr.fpr_mul(a_im, b_im)); +// w3 = fpr.fpr_add(fpr.fpr_mul(a_re, b_im), fpr.fpr_mul(a_im, b_re)); +// +// w0 = fpr.fpr_add(w0, srct0[t0 + 0]); +// w1 = fpr.fpr_add(w1, srct0[t0 + 1]); +// w2 = fpr.fpr_add(w2, srct0[t0 + 2]); +// w3 = fpr.fpr_add(w3, srct0[t0 + 3]); +// +// /* +// * Second recursive invocation. +// */ +// a_re = w0; +// a_im = w2; +// b_re = w1; +// b_im = w3; +// c_re = fpr.fpr_add(a_re, b_re); +// c_im = fpr.fpr_add(a_im, b_im); +// w0 = fpr.fpr_half(c_re); +// w1 = fpr.fpr_half(c_im); +// c_re = fpr.fpr_sub(a_re, b_re); +// c_im = fpr.fpr_sub(a_im, b_im); +// w2 = fpr.fpr_mul(fpr.fpr_add(c_re, c_im), fpr.fpr_invsqrt8); +// w3 = fpr.fpr_mul(fpr.fpr_sub(c_im, c_re), fpr.fpr_invsqrt8); +// +// x0 = w2; +// x1 = w3; +// sigma = srctree[tree0 + 3]; +// w2 = y0 = fpr.fpr_of(samp.sample(samp_ctx, x0, sigma)); +// w3 = y1 = fpr.fpr_of(samp.sample(samp_ctx, x1, sigma)); +// a_re = fpr.fpr_sub(x0, y0); +// a_im = fpr.fpr_sub(x1, y1); +// b_re = srctree[tree0 + 0]; +// b_im = srctree[tree0 + 1]; +// c_re = fpr.fpr_sub(fpr.fpr_mul(a_re, b_re), fpr.fpr_mul(a_im, b_im)); +// c_im = fpr.fpr_add(fpr.fpr_mul(a_re, b_im), fpr.fpr_mul(a_im, b_re)); +// x0 = fpr.fpr_add(c_re, w0); +// x1 = fpr.fpr_add(c_im, w1); +// sigma = srctree[tree0 + 2]; +// w0 = fpr.fpr_of(samp.sample(samp_ctx, x0, sigma)); +// w1 = fpr.fpr_of(samp.sample(samp_ctx, x1, sigma)); +// +// a_re = w0; +// a_im = w1; +// b_re = w2; +// b_im = w3; +// c_re = fpr.fpr_mul(fpr.fpr_sub(b_re, b_im), fpr.fpr_invsqrt2); +// c_im = fpr.fpr_mul(fpr.fpr_add(b_re, b_im), fpr.fpr_invsqrt2); +// srcz0[z0 + 0] = fpr.fpr_add(a_re, c_re); +// srcz0[z0 + 2] = fpr.fpr_add(a_im, c_im); +// srcz0[z0 + 1] = fpr.fpr_sub(a_re, c_re); +// srcz0[z0 + 3] = fpr.fpr_sub(a_im, c_im); +// +// return; +// } +// +// /* +// * Case logn == 1 is reachable only when using Falcon-2 (the +// * smallest size for which Falcon is mathematically defined, but +// * of course way too insecure to be of any use). +// */ +// if (logn == 1) +// { +// double x0, x1, y0, y1, sigma; +// double a_re, a_im, b_re, b_im, c_re, c_im; +// +// x0 = srct1[t1 + 0]; +// x1 = srct1[t1 + 1]; +// sigma = srctree[tree + 3]; +// srcz1[z1 + 0] = y0 = fpr.fpr_of(samp.sample(samp_ctx, x0, sigma)); +// srcz1[z1 + 1] = y1 = fpr.fpr_of(samp.sample(samp_ctx, x1, sigma)); +// a_re = fpr.fpr_sub(x0, y0); +// a_im = fpr.fpr_sub(x1, y1); +// b_re = srctree[tree + 0]; +// b_im = srctree[tree + 1]; +// c_re = fpr.fpr_sub(fpr.fpr_mul(a_re, b_re), fpr.fpr_mul(a_im, b_im)); +// c_im = fpr.fpr_add(fpr.fpr_mul(a_re, b_im), fpr.fpr_mul(a_im, b_re)); +// x0 = fpr.fpr_add(c_re, srct0[t0 + 0]); +// x1 = fpr.fpr_add(c_im, srct0[t0 + 1]); +// sigma = srctree[tree + 2]; +// srcz0[z0 + 0] = fpr.fpr_of(samp.sample(samp_ctx, x0, sigma)); +// srcz0[z0 + 1] = fpr.fpr_of(samp.sample(samp_ctx, x1, sigma)); +// +// return; +// } +// +// /* +// * Normal end of recursion is for logn == 0. Since the last +// * steps of the recursions were inlined in the blocks above +// * (when logn == 1 or 2), this case is not reachable, and is +// * retained here only for documentation purposes. +// +// if (logn == 0) { +// fpr x0, x1, sigma; +// +// x0 = t0[0]; +// x1 = t1[0]; +// sigma = tree[0]; +// z0[0] = fpr_of(samp(samp_ctx, x0, sigma)); +// z1[0] = fpr_of(samp(samp_ctx, x1, sigma)); +// return; +// } +// +// */ +// +// /* +// * General recursive case (logn >= 3). +// */ +// +// n = 1 << logn; +// hn = n >> 1; +// tree0 = tree + n; +// tree1 = tree + n + ffLDL_treesize(logn - 1); +// +// /* +// * We split t1 into z1 (reused as temporary storage), then do +// * the recursive invocation, with output in tmp. We finally +// * merge back into z1. +// */ +// fft.poly_split_fft(srcz1, z1, srcz1, z1 + hn, srct1, t1, logn); +// ffSampling_fft(samp, samp_ctx, srctmp, tmp, srctmp, tmp + hn, +// srctree, tree1, srcz1, z1, srcz1, z1 + hn, logn - 1, srctmp, tmp + n); +// fft.poly_merge_fft(srcz1, z1, srctmp, tmp, srctmp, tmp + hn, logn); +// +// /* +// * Compute tb0 = t0 + (t1 - z1) * L. Value tb0 ends up in tmp[]. +// */ +//// memcpy(tmp, t1, n * sizeof *t1); +// System.arraycopy(srct1, t1, srctmp, tmp, n); +// fft.poly_sub(srctmp, tmp, srcz1, z1, logn); +// fft.poly_mul_fft(srctmp, tmp, srctree, tree, logn); +// fft.poly_add(srctmp, tmp, srct0, t0, logn); +// +// /* +// * Second recursive invocation. +// */ +// fft.poly_split_fft(srcz0, z0, srcz0, z0 + hn, srctmp, tmp, logn); +// ffSampling_fft(samp, samp_ctx, srctmp, tmp, srctmp, tmp + hn, +// srctree, tree0, srcz0, z0, srcz0, z0 + hn, logn - 1, srctmp, tmp + n); +// fft.poly_merge_fft(srcz0, z0, srctmp, tmp, srctmp, tmp + hn, logn); +// } /* * Compute a signature: the signature contains two vectors, s1 and s2. @@ -643,123 +649,123 @@ void ffSampling_fft(SamplerZ samp, SamplerCtx samp_ctx, * * tmp[] must have room for at least six polynomials. */ - int do_sign_tree(SamplerZ samp, SamplerCtx samp_ctx, short[] srcs2, int s2, - FalconFPR[] srcexpanded_key, int expanded_key, - short[] srchm, int hm, - int logn, FalconFPR[] srctmp, int tmp) - { - int n, u; - int t0, t1, tx, ty; - int b00, b01, b10, b11, tree; - FalconFPR ni; - int sqn, ng; - short[] s1tmp, s2tmp; - - n = MKN(logn); - t0 = tmp; - t1 = t0 + n; - b00 = expanded_key + skoff_b00(logn); - b01 = expanded_key + skoff_b01(logn); - b10 = expanded_key + skoff_b10(logn); - b11 = expanded_key + skoff_b11(logn); - tree = expanded_key + skoff_tree(logn); - - /* - * Set the target vector to [hm, 0] (hm is the hashed message). - */ - for (u = 0; u < n; u++) - { - srctmp[t0 + u] = fpr.fpr_of(srchm[hm + u]); - /* This is implicit. - t1[u] = fpr_zero; - */ - } - - /* - * Apply the lattice basis to obtain the real target - * vector (after normalization with regards to modulus). - */ - fft.FFT(srctmp, t0, logn); - ni = fpr.fpr_inverse_of_q; -// memcpy(t1, t0, n * sizeof *t0); - System.arraycopy(srctmp, t0, srctmp, t1, n); - fft.poly_mul_fft(srctmp, t1, srcexpanded_key, b01, logn); - fft.poly_mulconst(srctmp, t1, fpr.fpr_neg(ni), logn); - fft.poly_mul_fft(srctmp, t0, srcexpanded_key, b11, logn); - fft.poly_mulconst(srctmp, t0, ni, logn); - - tx = t1 + n; - ty = tx + n; - - /* - * Apply sampling. Output is written back in [tx, ty]. - */ - ffSampling_fft(samp, samp_ctx, srctmp, tx, srctmp, ty, srcexpanded_key, tree, - srctmp, t0, srctmp, t1, logn, srctmp, ty + n); - - /* - * Get the lattice point corresponding to that tiny vector. - */ -// memcpy(t0, tx, n * sizeof *tx); - System.arraycopy(srctmp, tx, srctmp, t0, n); -// memcpy(t1, ty, n * sizeof *ty); - System.arraycopy(srctmp, ty, srctmp, t1, n); - fft.poly_mul_fft(srctmp, tx, srcexpanded_key, b00, logn); - fft.poly_mul_fft(srctmp, ty, srcexpanded_key, b10, logn); - fft.poly_add(srctmp, tx, srctmp, ty, logn); -// memcpy(ty, t0, n * sizeof *t0); - System.arraycopy(srctmp, t0, srctmp, ty, n); - fft.poly_mul_fft(srctmp, ty, srcexpanded_key, b01, logn); - -// memcpy(t0, tx, n * sizeof *tx); - System.arraycopy(srctmp, tx, srctmp, t0, n); - fft.poly_mul_fft(srctmp, t1, srcexpanded_key, b11, logn); - fft.poly_add(srctmp, t1, srctmp, ty, logn); - - fft.iFFT(srctmp, t0, logn); - fft.iFFT(srctmp, t1, logn); - - /* - * Compute the signature. - */ - s1tmp = new short[n]; - sqn = 0; - ng = 0; - for (u = 0; u < n; u++) - { - int z; - // note: hm is unsigned - z = (srchm[hm + u] & 0xffff) - (int)fpr.fpr_rint(srctmp[t0 + u]); - sqn += (z * z); - ng |= sqn; - s1tmp[u] = (short)z; - } - sqn |= -(ng >>> 31); - - /* - * With "normal" degrees (e.g. 512 or 1024), it is very - * improbable that the computed vector is not short enough; - * however, it may happen in practice for the very reduced - * versions (e.g. degree 16 or below). In that case, the caller - * will loop, and we must not write anything into s2[] because - * s2[] may overlap with the hashed message hm[] and we need - * hm[] for the next iteration. - */ - s2tmp = new short[n]; - for (u = 0; u < n; u++) - { - s2tmp[u] = (short)-fpr.fpr_rint(srctmp[t1 + u]); - } - if (common.is_short_half(sqn, s2tmp, 0, logn) != 0) - { -// memcpy(s2, s2tmp, n * sizeof *s2); - System.arraycopy(s2tmp, 0, srcs2, s2, n); -// memcpy(tmp, s1tmp, n * sizeof *s1tmp); - System.arraycopy(s1tmp, 0, srctmp, tmp, n); - return 1; - } - return 0; - } +// int do_sign_tree(SamplerZ samp, SamplerCtx samp_ctx, short[] srcs2, int s2, +// double[] srcexpanded_key, int expanded_key, +// short[] srchm, int hm, +// int logn, double[] srctmp, int tmp) +// { +// int n, u; +// int t0, t1, tx, ty; +// int b00, b01, b10, b11, tree; +// double ni; +// int sqn, ng; +// short[] s1tmp, s2tmp; +// +// n = MKN(logn); +// t0 = tmp; +// t1 = t0 + n; +// b00 = expanded_key + skoff_b00(logn); +// b01 = expanded_key + skoff_b01(logn); +// b10 = expanded_key + skoff_b10(logn); +// b11 = expanded_key + skoff_b11(logn); +// tree = expanded_key + skoff_tree(logn); +// +// /* +// * Set the target vector to [hm, 0] (hm is the hashed message). +// */ +// for (u = 0; u < n; u++) +// { +// srctmp[t0 + u] = fpr.fpr_of(srchm[hm + u]); +// /* This is implicit. +// t1[u] = fpr_zero; +// */ +// } +// +// /* +// * Apply the lattice basis to obtain the real target +// * vector (after normalization with regards to modulus). +// */ +// fft.FFT(srctmp, t0, logn); +// ni = fpr.fpr_inverse_of_q; +//// memcpy(t1, t0, n * sizeof *t0); +// System.arraycopy(srctmp, t0, srctmp, t1, n); +// fft.poly_mul_fft(srctmp, t1, srcexpanded_key, b01, logn); +// fft.poly_mulconst(srctmp, t1, fpr.fpr_neg(ni), logn); +// fft.poly_mul_fft(srctmp, t0, srcexpanded_key, b11, logn); +// fft.poly_mulconst(srctmp, t0, ni, logn); +// +// tx = t1 + n; +// ty = tx + n; +// +// /* +// * Apply sampling. Output is written back in [tx, ty]. +// */ +// ffSampling_fft(samp, samp_ctx, srctmp, tx, srctmp, ty, srcexpanded_key, tree, +// srctmp, t0, srctmp, t1, logn, srctmp, ty + n); +// +// /* +// * Get the lattice point corresponding to that tiny vector. +// */ +//// memcpy(t0, tx, n * sizeof *tx); +// System.arraycopy(srctmp, tx, srctmp, t0, n); +//// memcpy(t1, ty, n * sizeof *ty); +// System.arraycopy(srctmp, ty, srctmp, t1, n); +// fft.poly_mul_fft(srctmp, tx, srcexpanded_key, b00, logn); +// fft.poly_mul_fft(srctmp, ty, srcexpanded_key, b10, logn); +// fft.poly_add(srctmp, tx, srctmp, ty, logn); +//// memcpy(ty, t0, n * sizeof *t0); +// System.arraycopy(srctmp, t0, srctmp, ty, n); +// fft.poly_mul_fft(srctmp, ty, srcexpanded_key, b01, logn); +// +//// memcpy(t0, tx, n * sizeof *tx); +// System.arraycopy(srctmp, tx, srctmp, t0, n); +// fft.poly_mul_fft(srctmp, t1, srcexpanded_key, b11, logn); +// fft.poly_add(srctmp, t1, srctmp, ty, logn); +// +// fft.iFFT(srctmp, t0, logn); +// fft.iFFT(srctmp, t1, logn); +// +// /* +// * Compute the signature. +// */ +// s1tmp = new short[n]; +// sqn = 0; +// ng = 0; +// for (u = 0; u < n; u++) +// { +// int z; +// // note: hm is unsigned +// z = (srchm[hm + u] & 0xffff) - (int)fpr.fpr_rint(srctmp[t0 + u]); +// sqn += (z * z); +// ng |= sqn; +// s1tmp[u] = (short)z; +// } +// sqn |= -(ng >>> 31); +// +// /* +// * With "normal" degrees (e.g. 512 or 1024), it is very +// * improbable that the computed vector is not short enough; +// * however, it may happen in practice for the very reduced +// * versions (e.g. degree 16 or below). In that case, the caller +// * will loop, and we must not write anything into s2[] because +// * s2[] may overlap with the hashed message hm[] and we need +// * hm[] for the next iteration. +// */ +// s2tmp = new short[n]; +// for (u = 0; u < n; u++) +// { +// s2tmp[u] = (short)-fpr.fpr_rint(srctmp[t1 + u]); +// } +// if (common.is_short_half(sqn, s2tmp, logn) != 0) +// { +//// memcpy(s2, s2tmp, n * sizeof *s2); +// System.arraycopy(s2tmp, 0, srcs2, s2, n); +//// memcpy(tmp, s1tmp, n * sizeof *s1tmp); +// System.arraycopy(s1tmp, 0, srctmp, tmp, n); +// return 1; +// } +// return 0; +// } /* * Compute a signature: the signature contains two vectors, s1 and s2. @@ -770,19 +776,19 @@ int do_sign_tree(SamplerZ samp, SamplerCtx samp_ctx, short[] srcs2, int s2, * * tmp[] must have room for at least nine polynomials. */ - int do_sign_dyn(SamplerZ samp, SamplerCtx samp_ctx, short[] srcs2, int s2, - byte[] srcf, int f, byte[] srcg, int g, - byte[] srcF, int F, byte[] srcG, int G, - short[] srchm, int hm, int logn, FalconFPR[] srctmp, int tmp) + int do_sign_dyn(SamplerCtx samp_ctx, short[] srcs2, + byte[] srcf, byte[] srcg, + byte[] srcF, byte[] srcG, + short[] srchm, int logn, double[] srctmp, int tmp) { int n, u; int t0, t1, tx, ty; int b00, b01, b10, b11, g00, g01, g11; - FalconFPR ni; + double ni; int sqn, ng; - short[] s1tmp, s2tmp; + short[] s2tmp; //s1tmp, - n = MKN(logn); + n = 1 << logn; /* * Lattice basis is B = [[g, -f], [G, -F]]. We convert it to FFT. @@ -791,16 +797,16 @@ int do_sign_dyn(SamplerZ samp, SamplerCtx samp_ctx, short[] srcs2, int s2, b01 = b00 + n; b10 = b01 + n; b11 = b10 + n; - smallints_to_fpr(srctmp, b01, srcf, f, logn); - smallints_to_fpr(srctmp, b00, srcg, g, logn); - smallints_to_fpr(srctmp, b11, srcF, F, logn); - smallints_to_fpr(srctmp, b10, srcG, G, logn); - fft.FFT(srctmp, b01, logn); - fft.FFT(srctmp, b00, logn); - fft.FFT(srctmp, b11, logn); - fft.FFT(srctmp, b10, logn); - fft.poly_neg(srctmp, b01, logn); - fft.poly_neg(srctmp, b11, logn); + smallints_to_fpr(srctmp, b01, srcf, logn); + smallints_to_fpr(srctmp, b00, srcg, logn); + smallints_to_fpr(srctmp, b11, srcF, logn); + smallints_to_fpr(srctmp, b10, srcG, logn); + FalconFFT.FFT(srctmp, b01, logn); + FalconFFT.FFT(srctmp, b00, logn); + FalconFFT.FFT(srctmp, b11, logn); + FalconFFT.FFT(srctmp, b10, logn); + FalconFFT.poly_neg(srctmp, b01, logn); + FalconFFT.poly_neg(srctmp, b11, logn); /* * Compute the Gram matrix G = B·B*. Formulas are: @@ -821,23 +827,23 @@ int do_sign_dyn(SamplerZ samp, SamplerCtx samp_ctx, short[] srcs2, int s2, // memcpy(t0, b01, n * sizeof *b01); System.arraycopy(srctmp, b01, srctmp, t0, n); - fft.poly_mulselfadj_fft(srctmp, t0, logn); // t0 <- b01*adj(b01) + FalconFFT.poly_mulselfadj_fft(srctmp, t0, logn); // t0 <- b01*adj(b01) // memcpy(t1, b00, n * sizeof *b00); System.arraycopy(srctmp, b00, srctmp, t1, n); - fft.poly_muladj_fft(srctmp, t1, srctmp, b10, logn); // t1 <- b00*adj(b10) - fft.poly_mulselfadj_fft(srctmp, b00, logn); // b00 <- b00*adj(b00) - fft.poly_add(srctmp, b00, srctmp, t0, logn); // b00 <- g00 + FalconFFT.poly_muladj_fft(srctmp, t1, srctmp, b10, logn); // t1 <- b00*adj(b10) + FalconFFT.poly_mulselfadj_fft(srctmp, b00, logn); // b00 <- b00*adj(b00) + FalconFFT.poly_add(srctmp, b00, srctmp, t0, logn); // b00 <- g00 // memcpy(t0, b01, n * sizeof *b01); System.arraycopy(srctmp, b01, srctmp, t0, n); - fft.poly_muladj_fft(srctmp, b01, srctmp, b11, logn); // b01 <- b01*adj(b11) - fft.poly_add(srctmp, b01, srctmp, t1, logn); // b01 <- g01 + FalconFFT.poly_muladj_fft(srctmp, b01, srctmp, b11, logn); // b01 <- b01*adj(b11) + FalconFFT.poly_add(srctmp, b01, srctmp, t1, logn); // b01 <- g01 - fft.poly_mulselfadj_fft(srctmp, b10, logn); // b10 <- b10*adj(b10) + FalconFFT.poly_mulselfadj_fft(srctmp, b10, logn); // b10 <- b10*adj(b10) // memcpy(t1, b11, n * sizeof *b11); System.arraycopy(srctmp, b11, srctmp, t1, n); - fft.poly_mulselfadj_fft(srctmp, t1, logn); // t1 <- b11*adj(b11) - fft.poly_add(srctmp, b10, srctmp, t1, logn); // b10 <- g11 + FalconFFT.poly_mulselfadj_fft(srctmp, t1, logn); // t1 <- b11*adj(b11) + FalconFFT.poly_add(srctmp, b10, srctmp, t1, logn); // b10 <- g11 /* * We rename variables to make things clearer. The three elements @@ -861,7 +867,7 @@ int do_sign_dyn(SamplerZ samp, SamplerCtx samp_ctx, short[] srcs2, int s2, */ for (u = 0; u < n; u++) { - srctmp[t0 + u] = fpr.fpr_of(srchm[hm + u]); + srctmp[t0 + u] = srchm[u]; /* This is implicit. t1[u] = fpr_zero; */ @@ -871,14 +877,14 @@ int do_sign_dyn(SamplerZ samp, SamplerCtx samp_ctx, short[] srcs2, int s2, * Apply the lattice basis to obtain the real target * vector (after normalization with regards to modulus). */ - fft.FFT(srctmp, t0, logn); - ni = fpr.fpr_inverse_of_q; + FalconFFT.FFT(srctmp, t0, logn); + ni = FPREngine.fpr_inverse_of_q; // memcpy(t1, t0, n * sizeof *t0); System.arraycopy(srctmp, t0, srctmp, t1, n); - fft.poly_mul_fft(srctmp, t1, srctmp, b01, logn); - fft.poly_mulconst(srctmp, t1, fpr.fpr_neg(ni), logn); - fft.poly_mul_fft(srctmp, t0, srctmp, b11, logn); - fft.poly_mulconst(srctmp, t0, ni, logn); + FalconFFT.poly_mul_fft(srctmp, t1, srctmp, b01, logn); + FalconFFT.poly_mulconst(srctmp, t1, -ni, logn); + FalconFFT.poly_mul_fft(srctmp, t0, srctmp, b11, logn); + FalconFFT.poly_mulconst(srctmp, t0, ni, logn); /* * b01 and b11 can be discarded, so we move back (t0,t1). @@ -893,7 +899,7 @@ int do_sign_dyn(SamplerZ samp, SamplerCtx samp_ctx, short[] srcs2, int s2, /* * Apply sampling; result is written over (t0,t1). */ - ffSampling_fft_dyntree(samp, samp_ctx, + ffSampling_fft_dyntree(samp_ctx, srctmp, t0, srctmp, t1, srctmp, g00, srctmp, g01, srctmp, g11, logn, logn, srctmp, t1 + n); @@ -905,7 +911,7 @@ int do_sign_dyn(SamplerZ samp, SamplerCtx samp_ctx, short[] srcs2, int s2, * We did not conserve the matrix basis, so we must recompute * it now. */ - b00 = tmp; + //b00 = tmp; b01 = b00 + n; b10 = b01 + n; b11 = b10 + n; @@ -913,16 +919,16 @@ int do_sign_dyn(SamplerZ samp, SamplerCtx samp_ctx, short[] srcs2, int s2, System.arraycopy(srctmp, t0, srctmp, b11 + n, n * 2); t0 = b11 + n; t1 = t0 + n; - smallints_to_fpr(srctmp, b01, srcf, f, logn); - smallints_to_fpr(srctmp, b00, srcg, g, logn); - smallints_to_fpr(srctmp, b11, srcF, F, logn); - smallints_to_fpr(srctmp, b10, srcG, G, logn); - fft.FFT(srctmp, b01, logn); - fft.FFT(srctmp, b00, logn); - fft.FFT(srctmp, b11, logn); - fft.FFT(srctmp, b10, logn); - fft.poly_neg(srctmp, b01, logn); - fft.poly_neg(srctmp, b11, logn); + smallints_to_fpr(srctmp, b01, srcf, logn); + smallints_to_fpr(srctmp, b00, srcg, logn); + smallints_to_fpr(srctmp, b11, srcF, logn); + smallints_to_fpr(srctmp, b10, srcG, logn); + FalconFFT.FFT(srctmp, b01, logn); + FalconFFT.FFT(srctmp, b00, logn); + FalconFFT.FFT(srctmp, b11, logn); + FalconFFT.FFT(srctmp, b10, logn); + FalconFFT.poly_neg(srctmp, b01, logn); + FalconFFT.poly_neg(srctmp, b11, logn); tx = t1 + n; ty = tx + n; @@ -933,31 +939,31 @@ int do_sign_dyn(SamplerZ samp, SamplerCtx samp_ctx, short[] srcs2, int s2, System.arraycopy(srctmp, t0, srctmp, tx, n); // memcpy(ty, t1, n * sizeof *t1); System.arraycopy(srctmp, t1, srctmp, ty, n); - fft.poly_mul_fft(srctmp, tx, srctmp, b00, logn); - fft.poly_mul_fft(srctmp, ty, srctmp, b10, logn); - fft.poly_add(srctmp, tx, srctmp, ty, logn); + FalconFFT.poly_mul_fft(srctmp, tx, srctmp, b00, logn); + FalconFFT.poly_mul_fft(srctmp, ty, srctmp, b10, logn); + FalconFFT.poly_add(srctmp, tx, srctmp, ty, logn); // memcpy(ty, t0, n * sizeof *t0); System.arraycopy(srctmp, t0, srctmp, ty, n); - fft.poly_mul_fft(srctmp, ty, srctmp, b01, logn); + FalconFFT.poly_mul_fft(srctmp, ty, srctmp, b01, logn); // memcpy(t0, tx, n * sizeof *tx); System.arraycopy(srctmp, tx, srctmp, t0, n); - fft.poly_mul_fft(srctmp, t1, srctmp, b11, logn); - fft.poly_add(srctmp, t1, srctmp, ty, logn); - fft.iFFT(srctmp, t0, logn); - fft.iFFT(srctmp, t1, logn); + FalconFFT.poly_mul_fft(srctmp, t1, srctmp, b11, logn); + FalconFFT.poly_add(srctmp, t1, srctmp, ty, logn); + FalconFFT.iFFT(srctmp, t0, logn); + FalconFFT.iFFT(srctmp, t1, logn); - s1tmp = new short[n]; + //s1tmp = new short[n]; sqn = 0; ng = 0; for (u = 0; u < n; u++) { int z; - z = (srchm[hm + u] & 0xffff) - (int)fpr.fpr_rint(srctmp[t0 + u]); + z = (srchm[u] & 0xffff) - (int)FPREngine.fpr_rint(srctmp[t0 + u]); sqn += (z * z); ng |= sqn; - s1tmp[u] = (short)z; + //s1tmp[u] = (short)z; } sqn |= -(ng >>> 31); @@ -973,12 +979,12 @@ int do_sign_dyn(SamplerZ samp, SamplerCtx samp_ctx, short[] srcs2, int s2, s2tmp = new short[n]; for (u = 0; u < n; u++) { - s2tmp[u] = (short)-fpr.fpr_rint(srctmp[t1 + u]); + s2tmp[u] = (short)-FPREngine.fpr_rint(srctmp[t1 + u]); } - if (common.is_short_half(sqn, s2tmp, 0, logn) != 0) + if (FalconCommon.is_short_half(sqn, s2tmp, logn) != 0) { // memcpy(s2, s2tmp, n * sizeof *s2); - System.arraycopy(s2tmp, 0, srcs2, s2, n); + System.arraycopy(s2tmp, 0, srcs2, 0, n); // memcpy(tmp, s1tmp, n * sizeof *s1tmp); // System.arraycopy(s1tmp, 0, srctmp, tmp, n); return 1; @@ -988,57 +994,57 @@ int do_sign_dyn(SamplerZ samp, SamplerCtx samp_ctx, short[] srcs2, int s2, /* see inner.h */ - void sign_tree(short[] srcsig, int sig, SHAKE256 rng, - FalconFPR[] srcexpanded_key, int expanded_key, - short[] srchm, int hm, int logn, FalconFPR[] srctmp, int tmp) - { - int ftmp; - - ftmp = tmp; - for (; ; ) - { - /* - * Signature produces short vectors s1 and s2. The - * signature is acceptable only if the aggregate vector - * s1,s2 is short; we must use the same bound as the - * verifier. - * - * If the signature is acceptable, then we return only s2 - * (the verifier recomputes s1 from s2, the hashed message, - * and the public key). - */ - SamplerCtx spc = new SamplerCtx(); - SamplerZ samp = new SamplerZ(); - SamplerCtx samp_ctx; - - /* - * Normal sampling. We use a fast PRNG seeded from our - * SHAKE context ('rng'). - */ - spc.sigma_min = fpr.fpr_sigma_min[logn]; - spc.p.prng_init(rng); - samp_ctx = spc; - - /* - * Do the actual signature. - */ - if (do_sign_tree(samp, samp_ctx, srcsig, sig, - srcexpanded_key, expanded_key, srchm, hm, logn, srctmp, ftmp) != 0) - { - break; - } - } - } +// void sign_tree(short[] srcsig, int sig, SHAKE256 rng, +// double[] srcexpanded_key, int expanded_key, +// short[] srchm, int hm, int logn, double[] srctmp, int tmp) +// { +// int ftmp; +// +// ftmp = tmp; +// for (; ; ) +// { +// /* +// * Signature produces short vectors s1 and s2. The +// * signature is acceptable only if the aggregate vector +// * s1,s2 is short; we must use the same bound as the +// * verifier. +// * +// * If the signature is acceptable, then we return only s2 +// * (the verifier recomputes s1 from s2, the hashed message, +// * and the public key). +// */ +// SamplerCtx spc = new SamplerCtx(); +// SamplerZ samp = new SamplerZ(); +// SamplerCtx samp_ctx; +// +// /* +// * Normal sampling. We use a fast PRNG seeded from our +// * SHAKE context ('rng'). +// */ +// spc.sigma_min = fpr.fpr_sigma_min[logn]; +// spc.p.prng_init(rng); +// samp_ctx = spc; +// +// /* +// * Do the actual signature. +// */ +// if (do_sign_tree(samp, samp_ctx, srcsig, sig, +// srcexpanded_key, expanded_key, srchm, hm, logn, srctmp, ftmp) != 0) +// { +// break; +// } +// } +// } /* see inner.h */ - void sign_dyn(short[] srcsig, int sig, SHAKE256 rng, - byte[] srcf, int f, byte[] srcg, int g, - byte[] srcF, int F, byte[] srcG, int G, - short[] srchm, int hm, int logn, FalconFPR[] srctmp, int tmp) + void sign_dyn(short[] srcsig, SHAKEDigest rng, + byte[] srcf, byte[] srcg, + byte[] srcF, byte[] srcG, + short[] srchm, int logn, double[] srctmp) { int ftmp; - ftmp = tmp; + ftmp = 0; for (; ; ) { /* @@ -1052,22 +1058,22 @@ void sign_dyn(short[] srcsig, int sig, SHAKE256 rng, * and the public key). */ SamplerCtx spc = new SamplerCtx(); - SamplerZ samp = new SamplerZ(); - SamplerCtx samp_ctx; +// SamplerZ samp = new SamplerZ(); +// SamplerCtx samp_ctx; /* * Normal sampling. We use a fast PRNG seeded from our * SHAKE context ('rng'). */ - spc.sigma_min = fpr.fpr_sigma_min[logn]; + spc.sigma_min = FPREngine.fpr_sigma_min[logn]; spc.p.prng_init(rng); - samp_ctx = spc; +// samp_ctx = spc; /* * Do the actual signature. */ - if (do_sign_dyn(samp, samp_ctx, srcsig, sig, - srcf, f, srcg, g, srcF, F, srcG, G, srchm, hm, logn, srctmp, ftmp) != 0) + if (do_sign_dyn(spc, srcsig, + srcf, srcg, srcF, srcG, srchm, logn, srctmp, ftmp) != 0) { break; } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconSigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconSigner.java index 43e567df1e..f5c9bd18c7 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconSigner.java @@ -4,7 +4,6 @@ import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.pqc.crypto.MessageSigner; -import org.bouncycastle.util.encoders.Hex; public class FalconSigner implements MessageSigner @@ -47,7 +46,7 @@ public byte[] generateSignature(byte[] message) { byte[] sm = new byte[nist.CRYPTO_BYTES]; - return nist.crypto_sign(false, sm, message, 0, message.length, encodedkey, 0); + return nist.crypto_sign(sm, message, message.length, encodedkey); } public boolean verifySignature(byte[] message, byte[] signature) @@ -60,7 +59,6 @@ public boolean verifySignature(byte[] message, byte[] signature) byte[] sig = new byte[signature.length - nist.NONCELEN - 1]; System.arraycopy(signature, 1, nonce, 0, nist.NONCELEN); System.arraycopy(signature, nist.NONCELEN + 1, sig, 0, signature.length - nist.NONCELEN - 1); - boolean res = nist.crypto_sign_open(false, sig,nonce,message,encodedkey,0) == 0; - return res; + return nist.crypto_sign_open(sig,nonce,message,encodedkey) == 0; } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconVrfy.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconVrfy.java index f1bebf2ad0..636912ae46 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconVrfy.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/FalconVrfy.java @@ -3,11 +3,11 @@ class FalconVrfy { - FalconCommon common; +// FalconCommon common; FalconVrfy() { - this.common = new FalconCommon(); +// this.common = new FalconCommon(); } /* ===================================================================== */ /* @@ -25,10 +25,10 @@ class FalconVrfy // #define Q0I 12287 // #define R 4091 // #define R2 10952 - final int Q = 12289; - final int Q0I = 12287; - final int R = 4091; - final int R2 = 10952; + static final int Q = 12289; + static final int Q0I = 12287; + static final int R = 4091; + static final int R2 = 10952; /* * Table for NTT, binary case: @@ -36,7 +36,7 @@ class FalconVrfy * where g = 7 (it is a 2048-th primitive root of 1 modulo q) * and rev() is the bit-reversal function over 10 bits. */ - final short[] GMb = { + static final short[] GMb = { 4091, 7888, 11060, 11208, 6960, 4342, 6275, 9759, 1591, 6399, 9477, 5266, 586, 5825, 7538, 9710, 1134, 6407, 1711, 965, 7099, 7674, 3743, 6442, @@ -172,7 +172,7 @@ class FalconVrfy * iGMb[x] = R*((1/g)^rev(x)) mod q * Since g = 7, 1/g = 8778 mod 12289. */ - final short[] iGMb = { + static final short[] iGMb = { 4091, 4401, 1081, 1229, 2530, 6014, 7947, 5329, 2579, 4751, 6464, 11703, 7023, 2812, 5890, 10698, 3109, 2125, 1960, 10925, 10601, 10404, 4189, 1875, @@ -307,23 +307,23 @@ class FalconVrfy * Reduce a small signed integer modulo q. The source integer MUST * be between -q/2 and +q/2. */ - int mq_conv_small(int x) + static int mq_conv_small(int x) { /* * If x < 0, the cast to uint32_t will set the high bit to 1. * ^ in Java, integers already use 2s complement so high bit will be 1 if negative */ - int y; - - y = x; - y += Q & -(y >>> 31); - return y; +// int x; +// +// x = x; + x += Q & -(x >>> 31); + return x; } /* * Addition modulo q. Operands must be in the 0..q-1 range. */ - int mq_add(int x, int y) + static int mq_add(int x, int y) { /* * We compute x + y - q. If the result is negative, then the @@ -342,7 +342,7 @@ int mq_add(int x, int y) /* * Subtraction modulo q. Operands must be in the 0..q-1 range. */ - int mq_sub(int x, int y) + static int mq_sub(int x, int y) { /* * As in mq_add(), we use a conditional addition to ensure the @@ -358,7 +358,7 @@ int mq_sub(int x, int y) /* * Division by 2 modulo q. Operand must be in the 0..q-1 range. */ - int mq_rshift1(int x) + static int mq_rshift1(int x) { x += Q & -(x & 1); return (x >>> 1); @@ -369,7 +369,7 @@ int mq_rshift1(int x) * this function computes: x * y / R mod q * Operands must be in the 0..q-1 range. */ - int mq_montymul(int x, int y) + static int mq_montymul(int x, int y) { int z, w; @@ -404,7 +404,7 @@ int mq_montymul(int x, int y) /* * Montgomery squaring (computes (x^2)/R). */ - int mq_montysqr(int x) + static int mq_montysqr(int x) { return mq_montymul(x, x); } @@ -412,7 +412,7 @@ int mq_montysqr(int x) /* * Divide x by y modulo q = 12289. */ - int mq_div_12289(int x, int y) + static int mq_div_12289(int x, int y) { /* * We invert y by computing y^(q-2) mod q. @@ -477,7 +477,7 @@ int mq_div_12289(int x, int y) /* * Compute NTT on a ring element. */ - void mq_NTT(short[] srca, int a, int logn) + static void mq_NTT(short[] srca, int a, int logn) { int n, t, m; @@ -512,7 +512,7 @@ void mq_NTT(short[] srca, int a, int logn) /* * Compute the inverse NTT on a ring element, binary case. */ - void mq_iNTT(short[] srca, int a, int logn) + static void mq_iNTT(short[] srca, int a, int logn) { int n, t, m; int ni; @@ -571,7 +571,7 @@ void mq_iNTT(short[] srca, int a, int logn) /* * Convert a polynomial (mod q) to Montgomery representation. */ - void mq_poly_tomonty(short[] srcf, int f, int logn) + static void mq_poly_tomonty(short[] srcf, int f, int logn) { int u, n; @@ -586,7 +586,7 @@ void mq_poly_tomonty(short[] srcf, int f, int logn) * Multiply two polynomials together (NTT representation, and using * a Montgomery multiplication). Result f*g is written over f. */ - void mq_poly_montymul_ntt(short[] srcf, int f, short[] srcg, int g, int logn) + static void mq_poly_montymul_ntt(short[] srcf, int f, short[] srcg, int g, int logn) { int u, n; @@ -600,35 +600,35 @@ void mq_poly_montymul_ntt(short[] srcf, int f, short[] srcg, int g, int logn) /* * Subtract polynomial g from polynomial f. */ - void mq_poly_sub(short[] srcf, int f, short[] srcg, int g, int logn) + static void mq_poly_sub(short[] srcf, int f, short[] srcg, int logn) { int u, n; n = 1 << logn; for (u = 0; u < n; u++) { - srcf[f + u] = (short)mq_sub(srcf[f + u], srcg[g + u]); + srcf[f + u] = (short)mq_sub(srcf[f + u], srcg[u]); } } /* ===================================================================== */ /* see inner.h */ - void to_ntt_monty(short[] srch, int h, int logn) + static void to_ntt_monty(short[] srch, int logn) { - mq_NTT(srch, h, logn); - mq_poly_tomonty(srch, h, logn); + mq_NTT(srch, 0, logn); + mq_poly_tomonty(srch, 0, logn); } /* see inner.h */ - int verify_raw(short[] srcc0, int c0, short[] srcs2, int s2, - short[] srch, int h, int logn, short[] srctmp, int tmp) + static int verify_raw(short[] srcc0, short[] srcs2, + short[] srch, int logn, short[] srctmp) { int u, n; int tt; n = 1 << logn; - tt = tmp; + tt = 0; /* * Reduce s2 elements modulo q ([0..q-1] range). @@ -637,7 +637,7 @@ int verify_raw(short[] srcc0, int c0, short[] srcs2, int s2, { int w; - w = (int)srcs2[s2 + u]; // s2 is signed, so ( & 0xffff) is not needed + w = srcs2[u]; // s2 is signed, so ( & 0xffff) is not needed w += Q & -(w >>> 31); srctmp[tt + u] = (short)w; } @@ -646,9 +646,9 @@ int verify_raw(short[] srcc0, int c0, short[] srcs2, int s2, * Compute -s1 = s2*h - c0 mod phi mod q (in tt[]). */ mq_NTT(srctmp, tt, logn); - mq_poly_montymul_ntt(srctmp, tt, srch, h, logn); + mq_poly_montymul_ntt(srctmp, tt, srch, 0, logn); mq_iNTT(srctmp, tt, logn); - mq_poly_sub(srctmp, tt, srcc0, c0, logn); + mq_poly_sub(srctmp, tt, srcc0, logn); /* * Normalize -s1 elements into the [-q/2..q/2] range. @@ -666,12 +666,12 @@ int verify_raw(short[] srcc0, int c0, short[] srcs2, int s2, * Signature is valid if and only if the aggregate (-s1,s2) vector * is short enough. */ - return common.is_short(srctmp, tt, srcs2, s2, logn); + return FalconCommon.is_short(srctmp, tt, srcs2, logn); } /* see inner.h */ - int compute_public(short[] srch, int h, - byte[] srcf, int f, byte[] srcg, int g, int logn, short[] srctmp, int tmp) + static int compute_public(short[] srch, int h, + byte[] srcf, byte[] srcg, int logn, short[] srctmp, int tmp) { int u, n; int tt; @@ -680,8 +680,8 @@ int compute_public(short[] srch, int h, tt = tmp; for (u = 0; u < n; u++) { - srctmp[tt + u] = (short)mq_conv_small(srcf[f + u]); - srch[h + u] = (short)mq_conv_small(srcg[g + u]); + srctmp[tt + u] = (short)mq_conv_small(srcf[u]); + srch[h + u] = (short)mq_conv_small(srcg[u]); } mq_NTT(srch, h, logn); mq_NTT(srctmp, tt, logn); @@ -698,21 +698,21 @@ int compute_public(short[] srch, int h, } /* see inner.h */ - boolean complete_private(byte[] srcG, int G, - byte[] srcf, int f, byte[] srcg, int g, byte[] srcF, int F, - int logn, short[] srctmp, int tmp) + static boolean complete_private(byte[] srcG, + byte[] srcf, byte[] srcg, byte[] srcF, + int logn, short[] srctmp) { int success = -1; int u, n; int t1, t2; n = 1 << logn; - t1 = tmp; + t1 = 0; t2 = t1 + n; for (u = 0; u < n; u++) { - srctmp[t1 + u] = (short)mq_conv_small(srcg[g + u]); - srctmp[t2 + u] = (short)mq_conv_small(srcF[F + u]); + srctmp[t1 + u] = (short)mq_conv_small(srcg[u]); + srctmp[t2 + u] = (short)mq_conv_small(srcF[u]); } mq_NTT(srctmp, t1, logn); mq_NTT(srctmp, t2, logn); @@ -720,7 +720,7 @@ boolean complete_private(byte[] srcG, int G, mq_poly_montymul_ntt(srctmp, t1, srctmp, t2, logn); for (u = 0; u < n; u++) { - srctmp[t2 + u] = (short)mq_conv_small(srcf[f + u]); + srctmp[t2 + u] = (short)mq_conv_small(srcf[u]); } mq_NTT(srctmp, t2, logn); for (u = 0; u < n; u++) @@ -734,124 +734,124 @@ boolean complete_private(byte[] srcG, int G, { int w = srctmp[t1 + u] & 0xffff; int gi = w - (Q & (((Q >> 1) - w) >> 31)); - success &= +gi - 128; // check +gi < 128 + success &= gi - 128; // check +gi < 128 success &= -gi - 128; // check -gi < 128 - srcG[G + u] = (byte)gi; + srcG[u] = (byte)gi; } return success < 0; } /* see inner.h */ - int is_invertible(short[] srcs2, int s2, int logn, short[] srctmp, int tmp) - { - int u, n; - int tt; - int r; - - n = 1 << logn; - tt = tmp; - for (u = 0; u < n; u++) - { - int w; - - w = (int)srcs2[s2 + u]; // s2 is signed - w += Q & -(w >>> 31); - srctmp[tt + u] = (short)w; - } - mq_NTT(srctmp, tt, logn); - r = 0; - for (u = 0; u < n; u++) - { - r |= (srctmp[tt + u] & 0xffff) - 1; - } - return (1 - (r >>> 31)); - } +// int is_invertible(short[] srcs2, int s2, int logn, short[] srctmp, int tmp) +// { +// int u, n; +// int tt; +// int r; +// +// n = 1 << logn; +// tt = tmp; +// for (u = 0; u < n; u++) +// { +// int w; +// +// w = (int)srcs2[s2 + u]; // s2 is signed +// w += Q & -(w >>> 31); +// srctmp[tt + u] = (short)w; +// } +// mq_NTT(srctmp, tt, logn); +// r = 0; +// for (u = 0; u < n; u++) +// { +// r |= (srctmp[tt + u] & 0xffff) - 1; +// } +// return (1 - (r >>> 31)); +// } /* see inner.h */ - int verify_recover(short[] srch, int h, - short[] srcc0, int c0, short[] srcs1, int s1, short[] srcs2, int s2, - int logn, short[] srctmp, int tmp) - { - int u, n; - int tt; - int r; - - n = 1 << logn; - - /* - * Reduce elements of s1 and s2 modulo q; then write s2 into tt[] - * and c0 - s1 into h[]. - */ - tt = tmp; - for (u = 0; u < n; u++) - { - int w; - - w = (int)srcs2[s2 + u]; // s2 is signed - w += Q & -(w >> 31); - srctmp[tt + u] = (short)w; - - w = (int)srcs1[s1 + u]; // s2 is signed - w += Q & -(w >>> 31); - w = mq_sub((srcc0[c0 + u]), w & 0xffff); // c0 is unsigned - srch[h + u] = (short)w; - } - - /* - * Compute h = (c0 - s1) / s2. If one of the coefficients of s2 - * is zero (in NTT representation) then the operation fails. We - * keep that information into a flag so that we do not deviate - * from strict constant-time processing; if all coefficients of - * s2 are non-zero, then the high bit of r will be zero. - */ - mq_NTT(srctmp, tt, logn); - mq_NTT(srctmp, h, logn); - r = 0; - for (u = 0; u < n; u++) - { - r |= (srctmp[tt + u] & 0xffff) - 1; - srch[h + u] = (short)mq_div_12289((srch[h + u] & 0xffff), - (srctmp[tt + u]) & 0xffff); - } - mq_iNTT(srch, h, logn); - - /* - * Signature is acceptable if and only if it is short enough, - * and s2 was invertible mod phi mod q. The caller must still - * check that the rebuilt public key matches the expected - * value (e.g. through a hash). - */ - r = ~r & -common.is_short(srcs1, s1, srcs2, s2, logn); - return (int)(r >>> 31); - } +// int verify_recover(short[] srch, int h, +// short[] srcc0, int c0, short[] srcs1, int s1, short[] srcs2, int s2, +// int logn, short[] srctmp, int tmp) +// { +// int u, n; +// int tt; +// int r; +// +// n = 1 << logn; +// +// /* +// * Reduce elements of s1 and s2 modulo q; then write s2 into tt[] +// * and c0 - s1 into h[]. +// */ +// tt = tmp; +// for (u = 0; u < n; u++) +// { +// int w; +// +// w = (int)srcs2[s2 + u]; // s2 is signed +// w += Q & -(w >> 31); +// srctmp[tt + u] = (short)w; +// +// w = (int)srcs1[s1 + u]; // s2 is signed +// w += Q & -(w >>> 31); +// w = mq_sub((srcc0[c0 + u]), w & 0xffff); // c0 is unsigned +// srch[h + u] = (short)w; +// } +// +// /* +// * Compute h = (c0 - s1) / s2. If one of the coefficients of s2 +// * is zero (in NTT representation) then the operation fails. We +// * keep that information into a flag so that we do not deviate +// * from strict constant-time processing; if all coefficients of +// * s2 are non-zero, then the high bit of r will be zero. +// */ +// mq_NTT(srctmp, tt, logn); +// mq_NTT(srctmp, h, logn); +// r = 0; +// for (u = 0; u < n; u++) +// { +// r |= (srctmp[tt + u] & 0xffff) - 1; +// srch[h + u] = (short)mq_div_12289((srch[h + u] & 0xffff), +// (srctmp[tt + u]) & 0xffff); +// } +// mq_iNTT(srch, h, logn); +// +// /* +// * Signature is acceptable if and only if it is short enough, +// * and s2 was invertible mod phi mod q. The caller must still +// * check that the rebuilt public key matches the expected +// * value (e.g. through a hash). +// */ +// r = ~r & -FalconCommon.is_short(srcs1, s1, srcs2, s2, logn); +// return (int)(r >>> 31); +// } /* see inner.h */ - int count_nttzero(short[] srcsig, int sig, int logn, short[] srctmp, int tmp) - { - int s2; - int u, n; - int r; - - n = 1 << logn; - s2 = tmp; - for (u = 0; u < n; u++) - { - int w; - - w = (int)srcsig[sig + u]; // sig is signed - w += Q & -(w >>> 31); - srctmp[s2 + u] = (short)w; - } - mq_NTT(srctmp, s2, logn); - r = 0; - for (u = 0; u < n; u++) - { - int w; - - w = (srctmp[s2 + u] & 0xffff) - 1; // s2 is unsigned - r += (w >>> 31); - } - return (int)r; - } +// int count_nttzero(short[] srcsig, int sig, int logn, short[] srctmp, int tmp) +// { +// int s2; +// int u, n; +// int r; +// +// n = 1 << logn; +// s2 = tmp; +// for (u = 0; u < n; u++) +// { +// int w; +// +// w = (int)srcsig[sig + u]; // sig is signed +// w += Q & -(w >>> 31); +// srctmp[s2 + u] = (short)w; +// } +// mq_NTT(srctmp, s2, logn); +// r = 0; +// for (u = 0; u < n; u++) +// { +// int w; +// +// w = (srctmp[s2 + u] & 0xffff) - 1; // s2 is unsigned +// r += (w >>> 31); +// } +// return (int)r; +// } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/SHAKE256.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/SHAKE256.java deleted file mode 100644 index eae8ee6133..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/SHAKE256.java +++ /dev/null @@ -1,562 +0,0 @@ -package org.bouncycastle.pqc.crypto.falcon; - -class SHAKE256 -{ - long[] A; - byte[] dbuf; - long dptr; - - SHAKE256() - { - this.A = new long[25]; - this.dbuf = new byte[200]; - this.dptr = 0; - } - - /* - * Round constants. - */ - private long RC[] = { - 0x0000000000000001l, 0x0000000000008082l, - 0x800000000000808Al, 0x8000000080008000l, - 0x000000000000808Bl, 0x0000000080000001l, - 0x8000000080008081l, 0x8000000000008009l, - 0x000000000000008Al, 0x0000000000000088l, - 0x0000000080008009l, 0x000000008000000Al, - 0x000000008000808Bl, 0x800000000000008Bl, - 0x8000000000008089l, 0x8000000000008003l, - 0x8000000000008002l, 0x8000000000000080l, - 0x000000000000800Al, 0x800000008000000Al, - 0x8000000080008081l, 0x8000000000008080l, - 0x0000000080000001l, 0x8000000080008008l - }; - - /* - * Process the provided state. - */ - void process_block(long[] A) - { - long t0, t1, t2, t3, t4; - long tt0, tt1, tt2, tt3; - long t, kt; - long c0, c1, c2, c3, c4, bnn; - int j; - - /* - * Invert some words (alternate internal representation, which - * saves some operations). - */ - A[1] = ~A[1]; - A[2] = ~A[2]; - A[8] = ~A[8]; - A[12] = ~A[12]; - A[17] = ~A[17]; - A[20] = ~A[20]; - - /* - * Compute the 24 rounds. This loop is partially unrolled (each - * iteration computes two rounds). - */ - for (j = 0; j < 24; j += 2) - { - - tt0 = A[1] ^ A[6]; - tt1 = A[11] ^ A[16]; - tt0 ^= A[21] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[4] ^ A[9]; - tt3 = A[14] ^ A[19]; - tt0 ^= A[24]; - tt2 ^= tt3; - t0 = tt0 ^ tt2; - - tt0 = A[2] ^ A[7]; - tt1 = A[12] ^ A[17]; - tt0 ^= A[22] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[0] ^ A[5]; - tt3 = A[10] ^ A[15]; - tt0 ^= A[20]; - tt2 ^= tt3; - t1 = tt0 ^ tt2; - - tt0 = A[3] ^ A[8]; - tt1 = A[13] ^ A[18]; - tt0 ^= A[23] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[1] ^ A[6]; - tt3 = A[11] ^ A[16]; - tt0 ^= A[21]; - tt2 ^= tt3; - t2 = tt0 ^ tt2; - - tt0 = A[4] ^ A[9]; - tt1 = A[14] ^ A[19]; - tt0 ^= A[24] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[2] ^ A[7]; - tt3 = A[12] ^ A[17]; - tt0 ^= A[22]; - tt2 ^= tt3; - t3 = tt0 ^ tt2; - - tt0 = A[0] ^ A[5]; - tt1 = A[10] ^ A[15]; - tt0 ^= A[20] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[3] ^ A[8]; - tt3 = A[13] ^ A[18]; - tt0 ^= A[23]; - tt2 ^= tt3; - t4 = tt0 ^ tt2; - - A[0] = A[0] ^ t0; - A[5] = A[5] ^ t0; - A[10] = A[10] ^ t0; - A[15] = A[15] ^ t0; - A[20] = A[20] ^ t0; - A[1] = A[1] ^ t1; - A[6] = A[6] ^ t1; - A[11] = A[11] ^ t1; - A[16] = A[16] ^ t1; - A[21] = A[21] ^ t1; - A[2] = A[2] ^ t2; - A[7] = A[7] ^ t2; - A[12] = A[12] ^ t2; - A[17] = A[17] ^ t2; - A[22] = A[22] ^ t2; - A[3] = A[3] ^ t3; - A[8] = A[8] ^ t3; - A[13] = A[13] ^ t3; - A[18] = A[18] ^ t3; - A[23] = A[23] ^ t3; - A[4] = A[4] ^ t4; - A[9] = A[9] ^ t4; - A[14] = A[14] ^ t4; - A[19] = A[19] ^ t4; - A[24] = A[24] ^ t4; - A[5] = (A[5] << 36) | (A[5] >>> (64 - 36)); - A[10] = (A[10] << 3) | (A[10] >>> (64 - 3)); - A[15] = (A[15] << 41) | (A[15] >>> (64 - 41)); - A[20] = (A[20] << 18) | (A[20] >>> (64 - 18)); - A[1] = (A[1] << 1) | (A[1] >>> (64 - 1)); - A[6] = (A[6] << 44) | (A[6] >>> (64 - 44)); - A[11] = (A[11] << 10) | (A[11] >>> (64 - 10)); - A[16] = (A[16] << 45) | (A[16] >>> (64 - 45)); - A[21] = (A[21] << 2) | (A[21] >>> (64 - 2)); - A[2] = (A[2] << 62) | (A[2] >>> (64 - 62)); - A[7] = (A[7] << 6) | (A[7] >>> (64 - 6)); - A[12] = (A[12] << 43) | (A[12] >>> (64 - 43)); - A[17] = (A[17] << 15) | (A[17] >>> (64 - 15)); - A[22] = (A[22] << 61) | (A[22] >>> (64 - 61)); - A[3] = (A[3] << 28) | (A[3] >>> (64 - 28)); - A[8] = (A[8] << 55) | (A[8] >>> (64 - 55)); - A[13] = (A[13] << 25) | (A[13] >>> (64 - 25)); - A[18] = (A[18] << 21) | (A[18] >>> (64 - 21)); - A[23] = (A[23] << 56) | (A[23] >>> (64 - 56)); - A[4] = (A[4] << 27) | (A[4] >>> (64 - 27)); - A[9] = (A[9] << 20) | (A[9] >>> (64 - 20)); - A[14] = (A[14] << 39) | (A[14] >>> (64 - 39)); - A[19] = (A[19] << 8) | (A[19] >>> (64 - 8)); - A[24] = (A[24] << 14) | (A[24] >>> (64 - 14)); - - bnn = ~A[12]; - kt = A[6] | A[12]; - c0 = A[0] ^ kt; - kt = bnn | A[18]; - c1 = A[6] ^ kt; - kt = A[18] & A[24]; - c2 = A[12] ^ kt; - kt = A[24] | A[0]; - c3 = A[18] ^ kt; - kt = A[0] & A[6]; - c4 = A[24] ^ kt; - A[0] = c0; - A[6] = c1; - A[12] = c2; - A[18] = c3; - A[24] = c4; - bnn = ~A[22]; - kt = A[9] | A[10]; - c0 = A[3] ^ kt; - kt = A[10] & A[16]; - c1 = A[9] ^ kt; - kt = A[16] | bnn; - c2 = A[10] ^ kt; - kt = A[22] | A[3]; - c3 = A[16] ^ kt; - kt = A[3] & A[9]; - c4 = A[22] ^ kt; - A[3] = c0; - A[9] = c1; - A[10] = c2; - A[16] = c3; - A[22] = c4; - bnn = ~A[19]; - kt = A[7] | A[13]; - c0 = A[1] ^ kt; - kt = A[13] & A[19]; - c1 = A[7] ^ kt; - kt = bnn & A[20]; - c2 = A[13] ^ kt; - kt = A[20] | A[1]; - c3 = bnn ^ kt; - kt = A[1] & A[7]; - c4 = A[20] ^ kt; - A[1] = c0; - A[7] = c1; - A[13] = c2; - A[19] = c3; - A[20] = c4; - bnn = ~A[17]; - kt = A[5] & A[11]; - c0 = A[4] ^ kt; - kt = A[11] | A[17]; - c1 = A[5] ^ kt; - kt = bnn | A[23]; - c2 = A[11] ^ kt; - kt = A[23] & A[4]; - c3 = bnn ^ kt; - kt = A[4] | A[5]; - c4 = A[23] ^ kt; - A[4] = c0; - A[5] = c1; - A[11] = c2; - A[17] = c3; - A[23] = c4; - bnn = ~A[8]; - kt = bnn & A[14]; - c0 = A[2] ^ kt; - kt = A[14] | A[15]; - c1 = bnn ^ kt; - kt = A[15] & A[21]; - c2 = A[14] ^ kt; - kt = A[21] | A[2]; - c3 = A[15] ^ kt; - kt = A[2] & A[8]; - c4 = A[21] ^ kt; - A[2] = c0; - A[8] = c1; - A[14] = c2; - A[15] = c3; - A[21] = c4; - A[0] = A[0] ^ RC[j + 0]; - - tt0 = A[6] ^ A[9]; - tt1 = A[7] ^ A[5]; - tt0 ^= A[8] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[24] ^ A[22]; - tt3 = A[20] ^ A[23]; - tt0 ^= A[21]; - tt2 ^= tt3; - t0 = tt0 ^ tt2; - - tt0 = A[12] ^ A[10]; - tt1 = A[13] ^ A[11]; - tt0 ^= A[14] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[0] ^ A[3]; - tt3 = A[1] ^ A[4]; - tt0 ^= A[2]; - tt2 ^= tt3; - t1 = tt0 ^ tt2; - - tt0 = A[18] ^ A[16]; - tt1 = A[19] ^ A[17]; - tt0 ^= A[15] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[6] ^ A[9]; - tt3 = A[7] ^ A[5]; - tt0 ^= A[8]; - tt2 ^= tt3; - t2 = tt0 ^ tt2; - - tt0 = A[24] ^ A[22]; - tt1 = A[20] ^ A[23]; - tt0 ^= A[21] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[12] ^ A[10]; - tt3 = A[13] ^ A[11]; - tt0 ^= A[14]; - tt2 ^= tt3; - t3 = tt0 ^ tt2; - - tt0 = A[0] ^ A[3]; - tt1 = A[1] ^ A[4]; - tt0 ^= A[2] ^ tt1; - tt0 = (tt0 << 1) | (tt0 >>> 63); - tt2 = A[18] ^ A[16]; - tt3 = A[19] ^ A[17]; - tt0 ^= A[15]; - tt2 ^= tt3; - t4 = tt0 ^ tt2; - - A[0] = A[0] ^ t0; - A[3] = A[3] ^ t0; - A[1] = A[1] ^ t0; - A[4] = A[4] ^ t0; - A[2] = A[2] ^ t0; - A[6] = A[6] ^ t1; - A[9] = A[9] ^ t1; - A[7] = A[7] ^ t1; - A[5] = A[5] ^ t1; - A[8] = A[8] ^ t1; - A[12] = A[12] ^ t2; - A[10] = A[10] ^ t2; - A[13] = A[13] ^ t2; - A[11] = A[11] ^ t2; - A[14] = A[14] ^ t2; - A[18] = A[18] ^ t3; - A[16] = A[16] ^ t3; - A[19] = A[19] ^ t3; - A[17] = A[17] ^ t3; - A[15] = A[15] ^ t3; - A[24] = A[24] ^ t4; - A[22] = A[22] ^ t4; - A[20] = A[20] ^ t4; - A[23] = A[23] ^ t4; - A[21] = A[21] ^ t4; - A[3] = (A[3] << 36) | (A[3] >>> (64 - 36)); - A[1] = (A[1] << 3) | (A[1] >>> (64 - 3)); - A[4] = (A[4] << 41) | (A[4] >>> (64 - 41)); - A[2] = (A[2] << 18) | (A[2] >>> (64 - 18)); - A[6] = (A[6] << 1) | (A[6] >>> (64 - 1)); - A[9] = (A[9] << 44) | (A[9] >>> (64 - 44)); - A[7] = (A[7] << 10) | (A[7] >>> (64 - 10)); - A[5] = (A[5] << 45) | (A[5] >>> (64 - 45)); - A[8] = (A[8] << 2) | (A[8] >>> (64 - 2)); - A[12] = (A[12] << 62) | (A[12] >>> (64 - 62)); - A[10] = (A[10] << 6) | (A[10] >>> (64 - 6)); - A[13] = (A[13] << 43) | (A[13] >>> (64 - 43)); - A[11] = (A[11] << 15) | (A[11] >>> (64 - 15)); - A[14] = (A[14] << 61) | (A[14] >>> (64 - 61)); - A[18] = (A[18] << 28) | (A[18] >>> (64 - 28)); - A[16] = (A[16] << 55) | (A[16] >>> (64 - 55)); - A[19] = (A[19] << 25) | (A[19] >>> (64 - 25)); - A[17] = (A[17] << 21) | (A[17] >>> (64 - 21)); - A[15] = (A[15] << 56) | (A[15] >>> (64 - 56)); - A[24] = (A[24] << 27) | (A[24] >>> (64 - 27)); - A[22] = (A[22] << 20) | (A[22] >>> (64 - 20)); - A[20] = (A[20] << 39) | (A[20] >>> (64 - 39)); - A[23] = (A[23] << 8) | (A[23] >>> (64 - 8)); - A[21] = (A[21] << 14) | (A[21] >>> (64 - 14)); - - bnn = ~A[13]; - kt = A[9] | A[13]; - c0 = A[0] ^ kt; - kt = bnn | A[17]; - c1 = A[9] ^ kt; - kt = A[17] & A[21]; - c2 = A[13] ^ kt; - kt = A[21] | A[0]; - c3 = A[17] ^ kt; - kt = A[0] & A[9]; - c4 = A[21] ^ kt; - A[0] = c0; - A[9] = c1; - A[13] = c2; - A[17] = c3; - A[21] = c4; - bnn = ~A[14]; - kt = A[22] | A[1]; - c0 = A[18] ^ kt; - kt = A[1] & A[5]; - c1 = A[22] ^ kt; - kt = A[5] | bnn; - c2 = A[1] ^ kt; - kt = A[14] | A[18]; - c3 = A[5] ^ kt; - kt = A[18] & A[22]; - c4 = A[14] ^ kt; - A[18] = c0; - A[22] = c1; - A[1] = c2; - A[5] = c3; - A[14] = c4; - bnn = ~A[23]; - kt = A[10] | A[19]; - c0 = A[6] ^ kt; - kt = A[19] & A[23]; - c1 = A[10] ^ kt; - kt = bnn & A[2]; - c2 = A[19] ^ kt; - kt = A[2] | A[6]; - c3 = bnn ^ kt; - kt = A[6] & A[10]; - c4 = A[2] ^ kt; - A[6] = c0; - A[10] = c1; - A[19] = c2; - A[23] = c3; - A[2] = c4; - bnn = ~A[11]; - kt = A[3] & A[7]; - c0 = A[24] ^ kt; - kt = A[7] | A[11]; - c1 = A[3] ^ kt; - kt = bnn | A[15]; - c2 = A[7] ^ kt; - kt = A[15] & A[24]; - c3 = bnn ^ kt; - kt = A[24] | A[3]; - c4 = A[15] ^ kt; - A[24] = c0; - A[3] = c1; - A[7] = c2; - A[11] = c3; - A[15] = c4; - bnn = ~A[16]; - kt = bnn & A[20]; - c0 = A[12] ^ kt; - kt = A[20] | A[4]; - c1 = bnn ^ kt; - kt = A[4] & A[8]; - c2 = A[20] ^ kt; - kt = A[8] | A[12]; - c3 = A[4] ^ kt; - kt = A[12] & A[16]; - c4 = A[8] ^ kt; - A[12] = c0; - A[16] = c1; - A[20] = c2; - A[4] = c3; - A[8] = c4; - A[0] = A[0] ^ RC[j + 1]; - t = A[5]; - A[5] = A[18]; - A[18] = A[11]; - A[11] = A[10]; - A[10] = A[6]; - A[6] = A[22]; - A[22] = A[20]; - A[20] = A[12]; - A[12] = A[19]; - A[19] = A[15]; - A[15] = A[24]; - A[24] = A[8]; - A[8] = t; - t = A[1]; - A[1] = A[9]; - A[9] = A[14]; - A[14] = A[2]; - A[2] = A[13]; - A[13] = A[23]; - A[23] = A[4]; - A[4] = A[21]; - A[21] = A[16]; - A[16] = A[3]; - A[3] = A[17]; - A[17] = A[7]; - A[7] = t; - } - - /* - * Invert some words back to normal representation. - */ - A[1] = ~A[1]; - A[2] = ~A[2]; - A[8] = ~A[8]; - A[12] = ~A[12]; - A[17] = ~A[17]; - A[20] = ~A[20]; - } - - - /* see inner.h */ - void inner_shake256_init() - { - this.dptr = 0; - - /* - * Representation of an all-ones uint64_t is the same regardless - * of local endianness. - */ - for (int i = 0; i < this.A.length; i++) - { - this.A[i] = 0; - } - } - - /* see inner.h */ - void inner_shake256_inject(byte[] srcin, int in, int len) - { - long dptr; - - dptr = this.dptr; - while (len > 0) - { - long clen, u; - - clen = 136 - dptr; - if (clen > len) - { - clen = len; - } - for (u = 0; u < clen; u++) - { - long v; - - v = u + dptr; - this.A[(int)(v >> 3)] ^=(srcin[in + (int)u] & 0xffL) << ((v & 7) << 3); - } - dptr += clen; - in += clen; - len -= clen; - if (dptr == 136) - { - process_block(this.A); - dptr = 0; - } - } - this.dptr = dptr; - } - - /* see falcon.h */ - void i_shake256_flip() - { - /* - * We apply padding and pre-XOR the value into the state. We - * set dptr to the end of the buffer, so that first call to - * shake_extract() will process the block. - */ - int v; - - v = (int)this.dptr; - this.A[v >> 3] ^= (0x1FL) << ((v & 7) << 3); - this.A[16] ^= (0x80L) << 56; - this.dptr = 136; - } - - /* see falcon.h */ - void inner_shake256_extract(byte[] srcout, int out, int len) - { - int dptr; - int o = out; - - dptr = (int)this.dptr; - while (len > 0) - { - int clen; - - if (dptr == 136) - { - process_block(this.A); - dptr = 0; - } - clen = 136 - dptr; - if (clen > len) - { - clen = len; - } - len -= clen; - while (clen-- > 0) - { - srcout[o++] = (byte)(this.A[dptr >> 3] >>> ((dptr & 7) << 3)); - dptr++; - } - } - this.dptr = dptr; - } - -} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/SamplerCtx.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/SamplerCtx.java index 351878f0ba..104c258c4d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/SamplerCtx.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/SamplerCtx.java @@ -3,12 +3,12 @@ class SamplerCtx { - FalconFPR sigma_min; + double sigma_min; FalconRNG p; SamplerCtx() { - this.sigma_min = new FalconFPR(0.0); + this.sigma_min = 0.0; this.p = new FalconRNG(); } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/SamplerZ.java b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/SamplerZ.java index bde395f3fc..32e066ccd2 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/SamplerZ.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/falcon/SamplerZ.java @@ -3,14 +3,14 @@ class SamplerZ { - FPREngine fpr; + //FPREngine fpr; - SamplerZ() - { - this.fpr = new FPREngine(); - } +// SamplerZ() +// { +// //this.fpr = new FPREngine(); +// } - int sample(SamplerCtx ctx, FalconFPR mu, FalconFPR iSigma) + static int sample(SamplerCtx ctx, double mu, double iSigma) { return sampler(ctx, mu, iSigma); } @@ -19,7 +19,7 @@ int sample(SamplerCtx ctx, FalconFPR mu, FalconFPR iSigma) * Sample an integer value along a half-gaussian distribution centered * on zero and standard deviation 1.8205, with a precision of 72 bits. */ - int gaussian0_sampler(FalconRNG p) + static int gaussian0_sampler(FalconRNG p) { int[] dist = { @@ -68,11 +68,11 @@ int gaussian0_sampler(FalconRNG p) w0 = dist[u + 2]; w1 = dist[u + 1]; - w2 = dist[u + 0]; + w2 = dist[u]; cc = (v0 - w0) >>> 31; cc = (v1 - w1 - cc) >>> 31; cc = (v2 - w2 - cc) >>> 31; - z += (int)cc; + z += cc; } return z; @@ -81,10 +81,10 @@ int gaussian0_sampler(FalconRNG p) /* * Sample a bit with probability exp(-x) for some x >= 0. */ - int BerExp(FalconRNG p, FalconFPR x, FalconFPR ccs) + private static int BerExp(FalconRNG p, double x, double ccs) { int s, i; - FalconFPR r; + double r; int sw, w; long z; @@ -92,8 +92,8 @@ int BerExp(FalconRNG p, FalconFPR x, FalconFPR ccs) * Reduce x modulo log(2): x = s*log(2) + r, with s an integer, * and 0 <= r < log(2). Since x >= 0, we can use fpr_trunc(). */ - s = (int)fpr.fpr_trunc(fpr.fpr_mul(x, fpr.fpr_inv_log2)); - r = fpr.fpr_sub(x, fpr.fpr_mul(fpr.fpr_of(s), fpr.fpr_log2)); + s = (int)(x * FPREngine.fpr_inv_log2);//(int)fpr.fpr_trunc(fpr.fpr_mul(x, fpr.fpr_inv_log2)); + r = x - s * FPREngine.fpr_log2; /* * It may happen (quite rarely) that s >= 64; if sigma = 1.2 @@ -119,7 +119,7 @@ int BerExp(FalconRNG p, FalconFPR x, FalconFPR ccs) * case). The bias is negligible since fpr_expm_p63() only computes * with 51 bits of precision or so. */ - z = ((fpr.fpr_expm_p63(r, ccs) << 1) - 1) >>> s; + z = ((FPREngine.fpr_expm_p63(r, ccs) << 1) - 1) >>> s; /* * Sample a bit with probability exp(-x). Since x = s*log(2) + r, @@ -145,11 +145,11 @@ int BerExp(FalconRNG p, FalconFPR x, FalconFPR ccs) * The value of sigma MUST lie between 1 and 2 (i.e. isigma lies between * 0.5 and 1); in Falcon, sigma should always be between 1.2 and 1.9. */ - int sampler(SamplerCtx ctx, FalconFPR mu, FalconFPR isigma) + private static int sampler(SamplerCtx ctx, double mu, double isigma) { SamplerCtx spc; int s; - FalconFPR r, dss, ccs; + double r, dss, ccs; spc = ctx; @@ -157,18 +157,18 @@ int sampler(SamplerCtx ctx, FalconFPR mu, FalconFPR isigma) * Center is mu. We compute mu = s + r where s is an integer * and 0 <= r < 1. */ - s = (int)fpr.fpr_floor(mu); - r = fpr.fpr_sub(mu, fpr.fpr_of(s)); + s = (int)FPREngine.fpr_floor(mu); + r = mu - s; /* * dss = 1/(2*sigma^2) = 0.5*(isigma^2). */ - dss = fpr.fpr_half(fpr.fpr_sqr(isigma)); + dss = isigma * isigma * 0.5; /* * ccs = sigma_min / sigma = sigma_min * isigma. */ - ccs = fpr.fpr_mul(isigma, spc.sigma_min); + ccs = isigma * spc.sigma_min; /* * We now need to sample on center r. @@ -176,7 +176,7 @@ int sampler(SamplerCtx ctx, FalconFPR mu, FalconFPR isigma) for (; ; ) { int z0, z, b; - FalconFPR x; + double x; /* * Sample z for a Gaussian distribution. Then get a @@ -218,8 +218,9 @@ int sampler(SamplerCtx ctx, FalconFPR mu, FalconFPR isigma) * center and standard deviation that the whole sampler * can be said to be constant-time. */ - x = fpr.fpr_mul(fpr.fpr_sqr(fpr.fpr_sub(fpr.fpr_of(z), r)), dss); - x = fpr.fpr_sub(x, fpr.fpr_mul(fpr.fpr_of(z0 * z0), fpr.fpr_inv_2sqrsigma0)); + x = z - r; + x = x * x * dss; + x -= (double)(z0 * z0) * FPREngine.fpr_inv_2sqrsigma0; if (BerExp(spc.p, x, ccs) != 0) { /* From d01a0d60a45410631054429cb6cb9dc238e1c3d8 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 8 Apr 2025 17:04:53 +0930 Subject: [PATCH 308/890] Initial push of the branch --- .../org/bouncycastle/bcpg/KeyIdentifier.java | 39 +++++++++++++++++-- .../openpgp/test/KeyIdentifierTest.java | 16 ++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/KeyIdentifier.java b/pg/src/main/java/org/bouncycastle/bcpg/KeyIdentifier.java index 16c58f15ef..3dc86a4b34 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/KeyIdentifier.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/KeyIdentifier.java @@ -2,6 +2,7 @@ import java.util.Iterator; import java.util.List; +import java.util.Locale; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; @@ -31,6 +32,14 @@ public KeyIdentifier(String hexEncoded) */ public KeyIdentifier(byte[] fingerprint) { + // Long KeyID + if (fingerprint.length == 8) + { + keyId = FingerprintUtil.longFromRightMostBytes(fingerprint); + this.fingerprint = null; + return; + } + this.fingerprint = Arrays.clone(fingerprint); // v4 @@ -69,7 +78,16 @@ public KeyIdentifier(byte[] fingerprint, long keyId) */ public KeyIdentifier(long keyId) { - this(null, keyId); + if (keyId == 0L) + { + this.keyId = 0L; + this.fingerprint = new byte[0]; + } + else + { + this.keyId = keyId; + this.fingerprint = null; + } } /** @@ -77,7 +95,7 @@ public KeyIdentifier(long keyId) */ private KeyIdentifier() { - this(new byte[0], 0L); + this(0L); } /** @@ -123,7 +141,7 @@ public long getKeyId() */ public boolean isWildcard() { - return keyId == 0L && fingerprint.length == 0; + return keyId == 0L && (fingerprint == null || fingerprint.length == 0); } /** @@ -247,6 +265,19 @@ public String toString() } // -DM Hex.toHexString - return Hex.toHexString(fingerprint).toUpperCase(); + return Hex.toHexString(fingerprint).toUpperCase(Locale.getDefault()); + } + + public String toPrettyPrint() + { + if (isWildcard()) + { + return "*"; + } + if (fingerprint == null) + { + return "0x" + Long.toHexString(keyId).toUpperCase(Locale.getDefault()); + } + return FingerprintUtil.prettifyFingerprint(fingerprint); } } diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/KeyIdentifierTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/KeyIdentifierTest.java index 4f19aa6ed6..54bdb93570 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/KeyIdentifierTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/KeyIdentifierTest.java @@ -39,6 +39,8 @@ public void performTest() testWildcardIdentifier(); testIdentifierFromKeyId(); + testIdentifierFromLongKeyId(); + testIdentifierFromV4Fingerprint(); testIdentifierFromV6Fingerprint(); @@ -57,6 +59,9 @@ private void testWildcardIdentifier() wildcard.isWildcard()); isEquals("*", wildcard.toString()); + + KeyIdentifier id = new KeyIdentifier(0L); + isTrue(id.isWildcard()); } private void testIdentifierFromKeyId() @@ -70,6 +75,17 @@ private void testIdentifierFromKeyId() isEquals("1234", identifier.toString()); } + private void testIdentifierFromLongKeyId() + { + isEquals(5145070902336167606L, new KeyIdentifier("4766F6B9D5F21EB6").getKeyId()); + isEquals(5145070902336167606L, new KeyIdentifier("4766f6b9d5f21eb6").getKeyId()); + + isEquals(5507497285755629956L, new KeyIdentifier("4C6E8F99F6E47184").getKeyId()); + isEquals(1745434690267590572L, new KeyIdentifier("1839079A640B2FAC").getKeyId()); + + isTrue(new KeyIdentifier("1839079A640B2FAC").getFingerprint() == null); + } + private void testIdentifierFromV4Fingerprint() { String hexFingerprint = "D1A66E1A23B182C9980F788CFBFCC82A015E7330"; From 1b1a40f1d709b64abf820cbd0f94252e298b884c Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 10 Apr 2025 13:17:48 +0930 Subject: [PATCH 309/890] Add SnovaTest to jcajce.provider.test --- .../pqc/jcajce/provider/test/AllTests.java | 1 + .../pqc/jcajce/provider/test/SnovaTest.java | 318 ++++++++++++++++++ 2 files changed, 319 insertions(+) create mode 100644 prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/SnovaTest.java diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java index a01a130495..6782a44a9e 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java @@ -79,6 +79,7 @@ public static Test suite() suite.addTestSuite(RainbowTest.class); suite.addTestSuite(MayoKeyPairGeneratorTest.class); suite.addTestSuite(MayoTest.class); + suite.addTestSuite(SnovaTest.class); return new BCTestSetup(suite); } diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/SnovaTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/SnovaTest.java new file mode 100644 index 0000000000..4dd563ca1f --- /dev/null +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/SnovaTest.java @@ -0,0 +1,318 @@ +package org.bouncycastle.pqc.jcajce.provider.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.security.Security; +import java.security.Signature; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import junit.framework.TestCase; +import org.bouncycastle.pqc.jcajce.interfaces.SnovaKey; +import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider; +import org.bouncycastle.pqc.jcajce.spec.SnovaParameterSpec; +import org.bouncycastle.util.Strings; + +public class SnovaTest + extends TestCase +{ + public static void main(String[] args) + throws Exception + { + SnovaTest test = new SnovaTest(); + test.setUp(); + test.testPrivateKeyRecovery(); + test.testPublicKeyRecovery(); + test.testRestrictedKeyPairGen(); + test.testSnovaRandomSig(); + test.testSnovaSign(); + } + + byte[] msg = Strings.toByteArray("Hello World!"); + + public void setUp() + { + if (Security.getProvider(BouncyCastlePQCProvider.PROVIDER_NAME) == null) + { + Security.addProvider(new BouncyCastlePQCProvider()); + } + } + + public void testPrivateKeyRecovery() + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("Snova", "BCPQC"); + + kpg.initialize(SnovaParameterSpec.SNOVA_24_5_4_ESK, new RiggedRandom()); + + KeyPair kp = kpg.generateKeyPair(); + + KeyFactory kFact = KeyFactory.getInstance("Snova", "BCPQC"); + + SnovaKey privKey = (SnovaKey)kFact.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded())); + + assertEquals(kp.getPrivate(), privKey); + assertEquals(kp.getPrivate().getAlgorithm(), privKey.getAlgorithm()); + assertEquals(kp.getPrivate().hashCode(), privKey.hashCode()); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ObjectOutputStream oOut = new ObjectOutputStream(bOut); + + oOut.writeObject(privKey); + + oOut.close(); + + ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray())); + + SnovaKey privKey2 = (SnovaKey)oIn.readObject(); + + assertEquals(privKey, privKey2); + assertEquals(privKey.getAlgorithm(), privKey2.getAlgorithm()); + assertEquals(privKey.hashCode(), privKey2.hashCode()); + } + + public void testPublicKeyRecovery() + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("Snova", "BCPQC"); + + kpg.initialize(SnovaParameterSpec.SNOVA_25_8_3_ESK, new SnovaTest.RiggedRandom()); + + KeyPair kp = kpg.generateKeyPair(); + + KeyFactory kFact = KeyFactory.getInstance(SnovaParameterSpec.SNOVA_25_8_3_ESK.getName(), "BCPQC"); + + SnovaKey pubKey = (SnovaKey)kFact.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded())); + + assertEquals(kp.getPublic(), pubKey); + assertEquals(kp.getPublic().getAlgorithm(), pubKey.getAlgorithm()); + assertEquals(kp.getPublic().hashCode(), pubKey.hashCode()); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ObjectOutputStream oOut = new ObjectOutputStream(bOut); + + oOut.writeObject(pubKey); + + oOut.close(); + + ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray())); + + SnovaKey pubKey2 = (SnovaKey)oIn.readObject(); + + assertEquals(pubKey, pubKey2); + assertEquals(pubKey.getAlgorithm(), pubKey2.getAlgorithm()); + assertEquals(pubKey.hashCode(), pubKey2.hashCode()); + } + + public void testSnovaSign() + throws Exception + { + testSnova(SnovaParameterSpec.SNOVA_24_5_4_ESK, SnovaParameterSpec.SNOVA_24_5_4_SSK); + testSnova(SnovaParameterSpec.SNOVA_24_5_4_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_24_5_4_SHAKE_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_24_5_4_SHAKE_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_24_5_5_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_24_5_5_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_24_5_5_SHAKE_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_24_5_5_SHAKE_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_25_8_3_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_25_8_3_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_25_8_3_SHAKE_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_25_8_3_SHAKE_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_29_6_5_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_29_6_5_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_29_6_5_SHAKE_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_29_6_5_SHAKE_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_37_8_4_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_37_8_4_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_37_8_4_SHAKE_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_37_8_4_SHAKE_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_37_17_2_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_37_17_2_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_37_17_2_SHAKE_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_37_17_2_SHAKE_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_49_11_3_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_49_11_3_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_49_11_3_SHAKE_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_49_11_3_SHAKE_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_56_25_2_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_56_25_2_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_56_25_2_SHAKE_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_56_25_2_SHAKE_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_60_10_4_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_60_10_4_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_60_10_4_SHAKE_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_60_10_4_SHAKE_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_66_15_3_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_66_15_3_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_66_15_3_SHAKE_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_66_15_3_SHAKE_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_75_33_2_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_75_33_2_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_75_33_2_SHAKE_ESK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + testSnova(SnovaParameterSpec.SNOVA_75_33_2_SHAKE_SSK, SnovaParameterSpec.SNOVA_24_5_4_ESK); + } + + private void testSnova(SnovaParameterSpec spec, SnovaParameterSpec wrongSpec) + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("Snova", "BCPQC"); + + kpg.initialize(spec, new SecureRandom()); + + KeyPair kp = kpg.generateKeyPair(); + + Signature sig = Signature.getInstance(spec.getName(), "BCPQC"); + + sig.initSign(kp.getPrivate(), new SecureRandom()); + + sig.update(msg, 0, msg.length); + + byte[] s = sig.sign(); + + sig = Signature.getInstance(spec.getName(), "BCPQC"); + + assertEquals(spec.getName(), Strings.toUpperCase(sig.getAlgorithm())); + + sig.initVerify(kp.getPublic()); + + sig.update(msg, 0, msg.length); + + assertTrue(sig.verify(s)); + + kpg = KeyPairGenerator.getInstance("Snova", "BCPQC"); + + kpg.initialize(wrongSpec, new SecureRandom()); + + kp = kpg.generateKeyPair(); + + try + { + sig.initVerify(kp.getPublic()); + fail("no exception"); + } + catch (InvalidKeyException e) + { + assertEquals("signature configured for " + spec.getName(), e.getMessage()); + } + } + + public void testRestrictedKeyPairGen() + throws Exception + { + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_24_5_4_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_24_5_4_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_24_5_4_SHAKE_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_24_5_4_SHAKE_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_24_5_5_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_24_5_5_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_24_5_5_SHAKE_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_24_5_5_SHAKE_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_25_8_3_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_25_8_3_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_25_8_3_SHAKE_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_25_8_3_SHAKE_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_29_6_5_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_29_6_5_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_29_6_5_SHAKE_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_29_6_5_SHAKE_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_37_8_4_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_37_8_4_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_37_8_4_SHAKE_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_37_8_4_SHAKE_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_37_17_2_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_37_17_2_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_37_17_2_SHAKE_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_37_17_2_SHAKE_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_49_11_3_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_49_11_3_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_49_11_3_SHAKE_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_49_11_3_SHAKE_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_56_25_2_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_56_25_2_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_56_25_2_SHAKE_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_56_25_2_SHAKE_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_60_10_4_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_60_10_4_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_60_10_4_SHAKE_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_60_10_4_SHAKE_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_66_15_3_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_66_15_3_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_66_15_3_SHAKE_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_66_15_3_SHAKE_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_75_33_2_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_75_33_2_SSK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_75_33_2_SHAKE_ESK); + doTestRestrictedKeyPairGen(SnovaParameterSpec.SNOVA_75_33_2_SHAKE_SSK); + } + + private void doTestRestrictedKeyPairGen(SnovaParameterSpec spec) + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance(spec.getName(), "BCPQC"); + + kpg.initialize(spec, new SecureRandom()); + + KeyPair kp = kpg.generateKeyPair(); + + assertEquals(spec.getName(), kp.getPublic().getAlgorithm()); + assertEquals(spec.getName(), kp.getPrivate().getAlgorithm()); + + //kpg = KeyPairGenerator.getInstance(spec.getName(), "BCPQC"); + +// try +// { +// kpg.initialize(altSpec, new SecureRandom()); +// fail("no exception"); +// } +// catch (InvalidAlgorithmParameterException e) +// { +// assertEquals("key pair generator locked to " + spec.getName(), e.getMessage()); +// } + } + + public void testSnovaRandomSig() + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("Snova", "BCPQC"); + + kpg.initialize(SnovaParameterSpec.SNOVA_24_5_5_SHAKE_SSK, new SecureRandom()); + + KeyPair kp = kpg.generateKeyPair(); + + Signature sig = Signature.getInstance("Snova", "BCPQC"); + + sig.initSign(kp.getPrivate(), new SecureRandom()); + + sig.update(msg, 0, msg.length); + + byte[] s = sig.sign(); + + sig = Signature.getInstance("Snova", "BCPQC"); + + sig.initVerify(kp.getPublic()); + + sig.update(msg, 0, msg.length); + + assertTrue(sig.verify(s)); + } + + private static class RiggedRandom + extends SecureRandom + { + public void nextBytes(byte[] bytes) + { + for (int i = 0; i != bytes.length; i++) + { + bytes[i] = (byte)(i & 0xff); + } + } + } +} From 220a315a60563505ee1923019b017199eba9a315 Mon Sep 17 00:00:00 2001 From: Andrea Cosentino Date: Fri, 11 Apr 2025 10:38:56 +0200 Subject: [PATCH 310/890] Fixed typo in GMSSRootSig.java --- .../bouncycastle/pqc/legacy/crypto/gmss/GMSSRootSig.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gmss/GMSSRootSig.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gmss/GMSSRootSig.java index 10eee9f453..e4c81301a5 100644 --- a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gmss/GMSSRootSig.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gmss/GMSSRootSig.java @@ -414,7 +414,7 @@ else if (test > 0) test--; } if (test == 0) - { // if all hashes done copy result to siganture + { // if all hashes done copy result to signature // array System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize, mdsize); @@ -477,7 +477,7 @@ else if (test > 0) test--; } if (test == 0) - { // if all hashes done copy result to siganture + { // if all hashes done copy result to signature // array System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize, mdsize); @@ -542,7 +542,7 @@ else if (test8 > 0) test8--; } if (test8 == 0) - { // if all hashes done copy result to siganture + { // if all hashes done copy result to signature // array System.arraycopy(privateKeyOTS, 0, sign, counter * mdsize, mdsize); From 7ec58ff6ee4ae8acf9692343ff66e57eea5a5578 Mon Sep 17 00:00:00 2001 From: David Hook Date: Fri, 11 Apr 2025 23:29:24 +1000 Subject: [PATCH 311/890] added constraint check on "only*Certs" fields plus test for same. Relates to github #2051. --- .../asn1/x509/IssuingDistributionPoint.java | 6 +++++ .../IssuingDistributionPointUnitTest.java | 24 ++++++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java b/core/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java index ef78f62aa7..12230cec1f 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/IssuingDistributionPoint.java @@ -84,6 +84,12 @@ public IssuingDistributionPoint( boolean indirectCRL, boolean onlyContainsAttributeCerts) { + if ((onlyContainsCACerts && (onlyContainsUserCerts || onlyContainsAttributeCerts)) + || (onlyContainsUserCerts && onlyContainsAttributeCerts)) + { + throw new IllegalArgumentException("only one of onlyContainsCACerts, onlyContainsUserCerts, or onlyContainsAttributeCerts can be true"); + } + this.distributionPoint = distributionPoint; this.indirectCRL = indirectCRL; this.onlyContainsAttributeCerts = onlyContainsAttributeCerts; diff --git a/core/src/test/java/org/bouncycastle/asn1/test/IssuingDistributionPointUnitTest.java b/core/src/test/java/org/bouncycastle/asn1/test/IssuingDistributionPointUnitTest.java index fa0af30aca..75eb9a2ef2 100644 --- a/core/src/test/java/org/bouncycastle/asn1/test/IssuingDistributionPointUnitTest.java +++ b/core/src/test/java/org/bouncycastle/asn1/test/IssuingDistributionPointUnitTest.java @@ -27,7 +27,9 @@ public void performTest() new GeneralNames(new GeneralName(new X500Name("cn=test")))); ReasonFlags reasonFlags = new ReasonFlags(ReasonFlags.cACompromise); - checkPoint(6, name, true, true, reasonFlags, true, true); + checkOnlyException(name, true, true, reasonFlags, true, true); + checkOnlyException(name, true, true, reasonFlags, true, false); + checkOnlyException(name, true, false, reasonFlags, true, true); checkPoint(2, name, false, false, reasonFlags, false, false); @@ -45,6 +47,26 @@ public void performTest() } } + private void checkOnlyException( + DistributionPointName distributionPoint, + boolean onlyContainsUserCerts, + boolean onlyContainsCACerts, + ReasonFlags onlySomeReasons, + boolean indirectCRL, + boolean onlyContainsAttributeCerts) + throws IOException + { + try + { + new IssuingDistributionPoint(distributionPoint, onlyContainsUserCerts, onlyContainsCACerts, onlySomeReasons, indirectCRL, onlyContainsAttributeCerts); + fail("no exception"); + } + catch (IllegalArgumentException e) + { + isEquals("only one of onlyContainsCACerts, onlyContainsUserCerts, or onlyContainsAttributeCerts can be true", e.getMessage()); + } + } + private void checkPoint( int size, DistributionPointName distributionPoint, From bdb10169f590ab46d986b5b756c78dceccf08721 Mon Sep 17 00:00:00 2001 From: David Hook Date: Fri, 11 Apr 2025 23:39:56 +1000 Subject: [PATCH 312/890] added KeyStore test for new MAC algorithm --- pkix/src/test/java/org/bouncycastle/pkcs/test/PfxPduTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkix/src/test/java/org/bouncycastle/pkcs/test/PfxPduTest.java b/pkix/src/test/java/org/bouncycastle/pkcs/test/PfxPduTest.java index 3336fccb2e..4468002dd7 100644 --- a/pkix/src/test/java/org/bouncycastle/pkcs/test/PfxPduTest.java +++ b/pkix/src/test/java/org/bouncycastle/pkcs/test/PfxPduTest.java @@ -1158,6 +1158,10 @@ public void testPfxPduMac1() assertTrue(pfx.hasMac()); assertTrue(pfx.isMacValid(new BcPKCS12PBMac1CalculatorBuilderProvider(), passwd)); assertFalse(pfx.isMacValid(new BcPKCS12PBMac1CalculatorBuilderProvider(), "not right".toCharArray())); + + KeyStore pkcs12 = KeyStore.getInstance("PKCS12", "BC"); + + pkcs12.load(new ByteArrayInputStream(pfx.getEncoded()), passwd); } public void testPfxPduPBMac1PBKdf2() From 7bc638a722158ca00c30079f65ef366135d151dc Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sun, 13 Apr 2025 20:24:25 +0700 Subject: [PATCH 313/890] Refactor PQC private key parsing - add some TODOs --- .../mldsa/MLDSAPrivateKeyParameters.java | 22 +++++++- .../mlkem/MLKEMPrivateKeyParameters.java | 22 +++++++- .../pqc/crypto/util/PrivateKeyFactory.java | 56 ++++++++++--------- 3 files changed, 70 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAPrivateKeyParameters.java index 03f56b6abd..3fe15f5739 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAPrivateKeyParameters.java @@ -113,10 +113,28 @@ private MLDSAPrivateKeyParameters(MLDSAPrivateKeyParameters params, int preferre public MLDSAPrivateKeyParameters getParametersWithFormat(int format) { - if (this.seed == null && format == SEED_ONLY) + if (this.prefFormat == format) { - throw new IllegalArgumentException("no seed available"); + return this; } + + switch (format) + { + case BOTH: + case SEED_ONLY: + { + if (this.seed == null) + { + throw new IllegalStateException("no seed available"); + } + break; + } + case EXPANDED_KEY: + break; + default: + throw new IllegalArgumentException("unknown format"); + } + return new MLDSAPrivateKeyParameters(this, format); } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java index 2c91a70f46..596bb9d864 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mlkem/MLKEMPrivateKeyParameters.java @@ -99,10 +99,28 @@ private MLKEMPrivateKeyParameters(MLKEMPrivateKeyParameters params, int preferre public MLKEMPrivateKeyParameters getParametersWithFormat(int format) { - if (this.seed == null && format == SEED_ONLY) + if (this.prefFormat == format) { - throw new IllegalArgumentException("no seed available"); + return this; } + + switch (format) + { + case BOTH: + case SEED_ONLY: + { + if (this.seed == null) + { + throw new IllegalStateException("no seed available"); + } + break; + } + case EXPANDED_KEY: + break; + default: + throw new IllegalArgumentException("unknown format"); + } + return new MLKEMPrivateKeyParameters(this, format); } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java index e582bfd41e..9fe6df20ba 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java @@ -240,24 +240,28 @@ else if (algOID.equals(NISTObjectIdentifiers.id_alg_ml_kem_512) || pubParams = PublicKeyFactory.MLKEMConverter.getPublicKeyParams(mlkemParams, keyInfo.getPublicKeyData()); } - if (mlkemKey instanceof ASN1Sequence) + if (mlkemKey instanceof ASN1OctetString) { - ASN1Sequence keySeq = ASN1Sequence.getInstance(mlkemKey); + // TODO This should be explicitly EXPANDED_KEY or SEED (tag already removed) but is length-flexible + return new MLKEMPrivateKeyParameters(mlkemParams, ((ASN1OctetString)mlkemKey).getOctets(), pubParams); + } + else if (mlkemKey instanceof ASN1Sequence) + { + ASN1Sequence keySeq = (ASN1Sequence)mlkemKey; + byte[] seed = ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(); + byte[] encoding = ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets(); - MLKEMPrivateKeyParameters mlkemPriv = new MLKEMPrivateKeyParameters(mlkemParams, ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(), pubParams); - if (!Arrays.constantTimeAreEqual(mlkemPriv.getEncoded(), ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets())) + // TODO This should only allow seed but is length-flexible + MLKEMPrivateKeyParameters mlkemPriv = new MLKEMPrivateKeyParameters(mlkemParams, seed, pubParams); + if (!Arrays.constantTimeAreEqual(mlkemPriv.getEncoded(), encoding)) { - throw new IllegalStateException("seed/expanded-key mismatch"); + throw new IllegalArgumentException("inconsistent " + mlkemParams.getName() + " private key"); } return mlkemPriv; } - else if (mlkemKey instanceof ASN1OctetString) - { - return new MLKEMPrivateKeyParameters(mlkemParams, ASN1OctetString.getInstance(mlkemKey).getOctets()); - } - throw new IllegalArgumentException("unknown key format"); + throw new IllegalArgumentException("invalid " + mlkemParams.getName() + " private key"); } else if (algOID.on(BCObjectIdentifiers.pqc_kem_ntrulprime)) { @@ -286,37 +290,37 @@ else if (algOID.on(BCObjectIdentifiers.pqc_kem_sntruprime)) } else if (Utils.mldsaParams.containsKey(algOID)) { - ASN1Encodable keyObj = parsePrimitiveString(keyInfo.getPrivateKey(), 32); - MLDSAParameters spParams = Utils.mldsaParamsLookup(algOID); + ASN1Encodable mldsaKey = parsePrimitiveString(keyInfo.getPrivateKey(), 32); + MLDSAParameters mldsaParams = Utils.mldsaParamsLookup(algOID); MLDSAPublicKeyParameters pubParams = null; if (keyInfo.getPublicKeyData() != null) { - pubParams = PublicKeyFactory.MLDSAConverter.getPublicKeyParams(spParams, keyInfo.getPublicKeyData()); + pubParams = PublicKeyFactory.MLDSAConverter.getPublicKeyParams(mldsaParams, keyInfo.getPublicKeyData()); } - if (keyObj instanceof ASN1OctetString) + if (mldsaKey instanceof ASN1OctetString) { - byte[] data = ASN1OctetString.getInstance(keyObj).getOctets(); - - return new MLDSAPrivateKeyParameters(spParams, data, pubParams); + // TODO This should be explicitly EXPANDED_KEY or SEED (tag already removed) but is length-flexible + return new MLDSAPrivateKeyParameters(mldsaParams, ((ASN1OctetString)mldsaKey).getOctets(), pubParams); } - else if (keyObj instanceof ASN1Sequence) + else if (mldsaKey instanceof ASN1Sequence) { - ASN1Sequence keySeq = ASN1Sequence.getInstance(keyObj); + ASN1Sequence keySeq = (ASN1Sequence)mldsaKey; + byte[] seed = ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(); + byte[] encoding = ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets(); - MLDSAPrivateKeyParameters mldsaPriv = new MLDSAPrivateKeyParameters(spParams, ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(), pubParams); - if (!Arrays.constantTimeAreEqual(mldsaPriv.getEncoded(), ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets())) + // TODO This should only allow seed but is length-flexible + MLDSAPrivateKeyParameters mldsaPriv = new MLDSAPrivateKeyParameters(mldsaParams, seed, pubParams); + if (!Arrays.constantTimeAreEqual(mldsaPriv.getEncoded(), encoding)) { - throw new IllegalStateException("seed/expanded-key mismatch"); + throw new IllegalArgumentException("inconsistent " + mldsaParams.getName() + " private key"); } return mldsaPriv; } - else - { - throw new IOException("not supported"); - } + + throw new IllegalArgumentException("invalid " + mldsaParams.getName() + " private key"); } else if (algOID.equals(BCObjectIdentifiers.dilithium2) || algOID.equals(BCObjectIdentifiers.dilithium3) || algOID.equals(BCObjectIdentifiers.dilithium5)) From ad54856eea1600798536ca12fa414dabbe590bc2 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sun, 13 Apr 2025 21:56:19 +0700 Subject: [PATCH 314/890] Refactoring in tsp --- .../bouncycastle/tsp/TimeStampRequest.java | 65 +++++++--- .../bouncycastle/tsp/TimeStampResponse.java | 119 ++++++++++-------- .../org/bouncycastle/tsp/TimeStampToken.java | 2 +- .../tsp/TimeStampTokenGenerator.java | 18 ++- .../bouncycastle/asn1/cmp/PKIStatusInfo.java | 5 + .../bouncycastle/asn1/tsp/MessageImprint.java | 9 +- 6 files changed, 131 insertions(+), 87 deletions(-) diff --git a/pkix/src/main/java/org/bouncycastle/tsp/TimeStampRequest.java b/pkix/src/main/java/org/bouncycastle/tsp/TimeStampRequest.java index 76531d7aca..8760188d13 100644 --- a/pkix/src/main/java/org/bouncycastle/tsp/TimeStampRequest.java +++ b/pkix/src/main/java/org/bouncycastle/tsp/TimeStampRequest.java @@ -1,6 +1,5 @@ package org.bouncycastle.tsp; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.math.BigInteger; @@ -15,6 +14,7 @@ import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.cmp.PKIFailureInfo; +import org.bouncycastle.asn1.tsp.MessageImprint; import org.bouncycastle.asn1.tsp.TimeStampReq; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.Extension; @@ -27,6 +27,40 @@ public class TimeStampRequest { private static Set EMPTY_SET = Collections.unmodifiableSet(new HashSet()); + private static TimeStampReq parseTimeStampReq(byte[] encoding) + throws IOException + { + try + { + return TimeStampReq.getInstance(encoding); + } + catch (ClassCastException e) + { + throw new IOException("malformed request: " + e); + } + catch (IllegalArgumentException e) + { + throw new IOException("malformed request: " + e); + } + } + + private static TimeStampReq parseTimeStampReq(InputStream in) + throws IOException + { + try + { + return TimeStampReq.getInstance(new ASN1InputStream(in).readObject()); + } + catch (ClassCastException e) + { + throw new IOException("malformed request: " + e); + } + catch (IllegalArgumentException e) + { + throw new IOException("malformed request: " + e); + } + } + private TimeStampReq req; private Extensions extensions; @@ -45,7 +79,7 @@ public TimeStampRequest(TimeStampReq req) public TimeStampRequest(byte[] req) throws IOException { - this(new ByteArrayInputStream(req)); + this(parseTimeStampReq(req)); } /** @@ -57,24 +91,12 @@ public TimeStampRequest(byte[] req) public TimeStampRequest(InputStream in) throws IOException { - this(loadRequest(in)); + this(parseTimeStampReq(in)); } - private static TimeStampReq loadRequest(InputStream in) - throws IOException + public TimeStampReq toASN1Structure() { - try - { - return TimeStampReq.getInstance(new ASN1InputStream(in).readObject()); - } - catch (ClassCastException e) - { - throw new IOException("malformed request: " + e); - } - catch (IllegalArgumentException e) - { - throw new IOException("malformed request: " + e); - } + return req; } public int getVersion() @@ -82,6 +104,11 @@ public int getVersion() return req.getVersion().intValueExact(); } + public MessageImprint getMessageImprint() + { + return req.getMessageImprint(); + } + public ASN1ObjectIdentifier getMessageImprintAlgOID() { return req.getMessageImprint().getHashAlgorithm().getAlgorithm(); @@ -172,7 +199,7 @@ public void validate( Enumeration en = this.getExtensions().oids(); while(en.hasMoreElements()) { - ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)en.nextElement(); + ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)en.nextElement(); if (!extensions.contains(oid)) { throw new TSPValidationException("request contains unknown extension", PKIFailureInfo.unacceptedExtension); @@ -182,7 +209,7 @@ public void validate( int digestLength = TSPUtil.getDigestLength(this.getMessageImprintAlgOID().getId()); - if (digestLength != this.getMessageImprintDigest().length) + if (digestLength != this.getMessageImprint().getHashedMessageLength()) { throw new TSPValidationException("imprint digest the wrong length", PKIFailureInfo.badDataFormat); } diff --git a/pkix/src/main/java/org/bouncycastle/tsp/TimeStampResponse.java b/pkix/src/main/java/org/bouncycastle/tsp/TimeStampResponse.java index 9d399b00a1..b5e8c3258f 100644 --- a/pkix/src/main/java/org/bouncycastle/tsp/TimeStampResponse.java +++ b/pkix/src/main/java/org/bouncycastle/tsp/TimeStampResponse.java @@ -1,12 +1,11 @@ package org.bouncycastle.tsp; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Object; import org.bouncycastle.asn1.DLSequence; import org.bouncycastle.asn1.cmp.PKIFailureInfo; import org.bouncycastle.asn1.cmp.PKIFreeText; @@ -22,18 +21,50 @@ */ public class TimeStampResponse { - TimeStampResp resp; - TimeStampToken timeStampToken; + private static TimeStampResp parseTimeStampResp(byte[] encoding) + throws IOException, TSPException + { + try + { + return TimeStampResp.getInstance(encoding); + } + catch (IllegalArgumentException e) + { + throw new TSPException("malformed timestamp response: " + e, e); + } + catch (ClassCastException e) + { + throw new TSPException("malformed timestamp response: " + e, e); + } + } + + private static TimeStampResp parseTimeStampResp(InputStream in) + throws IOException, TSPException + { + try + { + return TimeStampResp.getInstance(new ASN1InputStream(in).readObject()); + } + catch (IllegalArgumentException e) + { + throw new TSPException("malformed timestamp response: " + e, e); + } + catch (ClassCastException e) + { + throw new TSPException("malformed timestamp response: " + e, e); + } + } + + private final TimeStampResp resp; + private final TimeStampToken timeStampToken; public TimeStampResponse(TimeStampResp resp) throws TSPException, IOException { this.resp = resp; - - if (resp.getTimeStampToken() != null) - { - timeStampToken = new TimeStampToken(resp.getTimeStampToken()); - } + + ContentInfo timeStampToken = resp.getTimeStampToken(); + this.timeStampToken = timeStampToken == null ? null : new TimeStampToken(timeStampToken); } /** @@ -46,7 +77,7 @@ public TimeStampResponse(TimeStampResp resp) public TimeStampResponse(byte[] resp) throws TSPException, IOException { - this(new ByteArrayInputStream(resp)); + this(parseTimeStampResp(resp)); } /** @@ -59,7 +90,7 @@ public TimeStampResponse(byte[] resp) public TimeStampResponse(InputStream in) throws TSPException, IOException { - this(readTimeStampResp(in)); + this(parseTimeStampResp(in)); } TimeStampResponse(DLSequence dlSequence) @@ -80,45 +111,25 @@ public TimeStampResponse(InputStream in) } } - private static TimeStampResp readTimeStampResp( - InputStream in) - throws IOException, TSPException - { - try - { - return TimeStampResp.getInstance(new ASN1InputStream(in).readObject()); - } - catch (IllegalArgumentException e) - { - throw new TSPException("malformed timestamp response: " + e, e); - } - catch (ClassCastException e) - { - throw new TSPException("malformed timestamp response: " + e, e); - } - } - public int getStatus() { - return resp.getStatus().getStatus().intValue(); + return resp.getStatus().getStatusObject().intValueExact(); } public String getStatusString() { - if (resp.getStatus().getStatusString() != null) + if (resp.getStatus().getStatusString() == null) { - StringBuffer statusStringBuf = new StringBuffer(); - PKIFreeText text = resp.getStatus().getStatusString(); - for (int i = 0; i != text.size(); i++) - { - statusStringBuf.append(text.getStringAtUTF8(i).getString()); - } - return statusStringBuf.toString(); + return null; } - else + + StringBuffer statusStringBuf = new StringBuffer(); + PKIFreeText text = resp.getStatus().getStatusString(); + for (int i = 0; i != text.size(); i++) { - return null; + statusStringBuf.append(text.getStringAtUTF8(i).getString()); } + return statusStringBuf.toString(); } public PKIFailureInfo getFailInfo() @@ -152,7 +163,7 @@ public void validate( if (tok != null) { - TimeStampTokenInfo tstInfo = tok.getTimeStampInfo(); + TimeStampTokenInfo tstInfo = tok.getTimeStampInfo(); if (request.getNonce() != null && !request.getNonce().equals(tstInfo.getNonce())) { @@ -163,17 +174,18 @@ public void validate( { throw new TSPValidationException("time stamp token found in failed request."); } - - if (!Arrays.constantTimeAreEqual(request.getMessageImprintDigest(), tstInfo.getMessageImprintDigest())) - { - throw new TSPValidationException("response for different message imprint digest."); - } - + + // TODO Should be (absent-parameters-flexible) equality of the whole AlgorithmIdentifier? if (!tstInfo.getMessageImprintAlgOID().equals(request.getMessageImprintAlgOID())) { throw new TSPValidationException("response for different message imprint algorithm."); } + if (!Arrays.constantTimeAreEqual(request.getMessageImprintDigest(), tstInfo.getMessageImprintDigest())) + { + throw new TSPValidationException("response for different message imprint digest."); + } + Attribute scV1 = tok.getSignedAttributes().get(PKCSObjectIdentifiers.id_aa_signingCertificate); Attribute scV2 = tok.getSignedAttributes().get(PKCSObjectIdentifiers.id_aa_signingCertificateV2); @@ -216,16 +228,13 @@ public byte[] getEncoded() throws IOException */ public byte[] getEncoded(String encoding) throws IOException { + ASN1Object asn1Object = resp; if (ASN1Encoding.DL.equals(encoding)) { - if (timeStampToken == null) - { - return new DLSequence(resp.getStatus()).getEncoded(encoding); - } - - return new DLSequence(new ASN1Encodable[] { resp.getStatus(), - timeStampToken.toCMSSignedData().toASN1Structure() }).getEncoded(encoding); + asn1Object = timeStampToken == null + ? new DLSequence(resp.getStatus()) + : new DLSequence(resp.getStatus(), timeStampToken.toCMSSignedData().toASN1Structure()); } - return resp.getEncoded(encoding); + return asn1Object.getEncoded(encoding); } } diff --git a/pkix/src/main/java/org/bouncycastle/tsp/TimeStampToken.java b/pkix/src/main/java/org/bouncycastle/tsp/TimeStampToken.java index 6a7f197aad..d6713cf427 100644 --- a/pkix/src/main/java/org/bouncycastle/tsp/TimeStampToken.java +++ b/pkix/src/main/java/org/bouncycastle/tsp/TimeStampToken.java @@ -195,7 +195,7 @@ public void validate( cOut.write(certHolder.getEncoded()); cOut.close(); - if (!Arrays.constantTimeAreEqual(certID.getCertHash(), calc.getDigest())) + if (!Arrays.constantTimeAreEqual(certID.getCertHashObject().getOctets(), calc.getDigest())) { throw new TSPValidationException("certificate hash does not match certID hash."); } diff --git a/pkix/src/main/java/org/bouncycastle/tsp/TimeStampTokenGenerator.java b/pkix/src/main/java/org/bouncycastle/tsp/TimeStampTokenGenerator.java index e670c194d3..3c353feed1 100644 --- a/pkix/src/main/java/org/bouncycastle/tsp/TimeStampTokenGenerator.java +++ b/pkix/src/main/java/org/bouncycastle/tsp/TimeStampTokenGenerator.java @@ -32,6 +32,7 @@ import org.bouncycastle.asn1.tsp.Accuracy; import org.bouncycastle.asn1.tsp.MessageImprint; import org.bouncycastle.asn1.tsp.TSTInfo; +import org.bouncycastle.asn1.tsp.TimeStampReq; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.Extensions; import org.bouncycastle.asn1.x509.ExtensionsGenerator; @@ -371,8 +372,9 @@ public TimeStampToken generate( Extensions additionalExtensions) throws TSPException { - AlgorithmIdentifier algID = request.getMessageImprintAlgID(); - MessageImprint messageImprint = new MessageImprint(algID, request.getMessageImprintDigest()); + TimeStampReq timeStampReq = request.toASN1Structure(); + + MessageImprint messageImprint = timeStampReq.getMessageImprint(); Accuracy accuracy = null; if (accuracySeconds > 0 || accuracyMillis > 0 || accuracyMicros > 0) @@ -404,16 +406,12 @@ public TimeStampToken generate( derOrdering = ASN1Boolean.getInstance(ordering); } - ASN1Integer nonce = null; - if (request.getNonce() != null) - { - nonce = new ASN1Integer(request.getNonce()); - } + ASN1Integer nonce = timeStampReq.getNonce(); - ASN1ObjectIdentifier tsaPolicy = tsaPolicyOID; - if (request.getReqPolicy() != null) + ASN1ObjectIdentifier tsaPolicy = timeStampReq.getReqPolicy(); + if (tsaPolicy == null) { - tsaPolicy = request.getReqPolicy(); + tsaPolicy = this.tsaPolicyOID; } Extensions respExtensions = request.getExtensions(); diff --git a/util/src/main/java/org/bouncycastle/asn1/cmp/PKIStatusInfo.java b/util/src/main/java/org/bouncycastle/asn1/cmp/PKIStatusInfo.java index 7e656e0770..595e02934a 100644 --- a/util/src/main/java/org/bouncycastle/asn1/cmp/PKIStatusInfo.java +++ b/util/src/main/java/org/bouncycastle/asn1/cmp/PKIStatusInfo.java @@ -110,6 +110,11 @@ public BigInteger getStatus() return status.getValue(); } + public ASN1Integer getStatusObject() + { + return status; + } + public PKIFreeText getStatusString() { return statusString; diff --git a/util/src/main/java/org/bouncycastle/asn1/tsp/MessageImprint.java b/util/src/main/java/org/bouncycastle/asn1/tsp/MessageImprint.java index 891abde318..56b9e09c68 100644 --- a/util/src/main/java/org/bouncycastle/asn1/tsp/MessageImprint.java +++ b/util/src/main/java/org/bouncycastle/asn1/tsp/MessageImprint.java @@ -62,12 +62,17 @@ public AlgorithmIdentifier getHashAlgorithm() { return hashAlgorithm; } - + public byte[] getHashedMessage() { return Arrays.clone(hashedMessage); } - + + public int getHashedMessageLength() + { + return hashedMessage.length; + } + /** *

          *    MessageImprint ::= SEQUENCE  {
    
    From 4e2ec8bfca36b420713ac4730489dd46626d754b Mon Sep 17 00:00:00 2001
    From: Peter Dettman 
    Date: Tue, 15 Apr 2025 01:16:58 +0700
    Subject: [PATCH 315/890] New ASN1Util methods to help with CHOICE types
    
    ---
     .../java/org/bouncycastle/asn1/ASN1Util.java  | 61 +++++++++++++++++++
     1 file changed, 61 insertions(+)
    
    diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Util.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Util.java
    index 01c689ea54..78be766fa6 100644
    --- a/core/src/main/java/org/bouncycastle/asn1/ASN1Util.java
    +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Util.java
    @@ -68,6 +68,42 @@ static ASN1TaggedObjectParser checkTagClass(ASN1TaggedObjectParser taggedObjectP
             return taggedObjectParser;
         }
     
    +    public static Object getInstanceChoiceBaseObject(ASN1TaggedObject taggedObject, boolean declaredExplicit,
    +        String choiceName)
    +    {
    +        if (!declaredExplicit)
    +        {
    +            String message = "Implicit tagging cannot be used with untagged choice type " + choiceName +
    +                " (X.680 30.6, 30.8).";
    +
    +            throw new IllegalArgumentException(message);
    +        }
    +        if (taggedObject == null)
    +        {
    +            throw new NullPointerException("'taggedObject' cannot be null");
    +        }
    +
    +        return getExplicitContextBaseObject(taggedObject);
    +    }
    +
    +    public static Object getTaggedChoiceBaseObject(ASN1TaggedObject taggedObject, boolean declaredExplicit,
    +        String choiceName)
    +    {
    +        if (!declaredExplicit)
    +        {
    +            String message = "Implicit tagging cannot be used with untagged choice type " + choiceName +
    +                " (X.680 30.6, 30.8).";
    +
    +            throw new IllegalArgumentException(message);
    +        }
    +        if (taggedObject == null)
    +        {
    +            throw new NullPointerException("'taggedObject' cannot be null");
    +        }
    +
    +        return taggedObject.getExplicitBaseObject();
    +    }
    +
     
         /*
          * Tag text methods
    @@ -138,16 +174,36 @@ public static String getTagText(int tagClass, int tagNo)
          * Wrappers for ASN1TaggedObject#getExplicitBaseObject
          */
     
    +    public static ASN1Object getExplicitBaseObject(ASN1TaggedObject taggedObject, int tagClass)
    +    {
    +        return checkTagClass(taggedObject, tagClass).getExplicitBaseObject();
    +    }
    +
         public static ASN1Object getExplicitBaseObject(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
         {
             return checkTag(taggedObject, tagClass, tagNo).getExplicitBaseObject();
         }
     
    +    public static ASN1Object getExplicitContextBaseObject(ASN1TaggedObject taggedObject)
    +    {
    +        return getExplicitBaseObject(taggedObject, BERTags.CONTEXT_SPECIFIC);
    +    }
    +
         public static ASN1Object getExplicitContextBaseObject(ASN1TaggedObject taggedObject, int tagNo)
         {
             return getExplicitBaseObject(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo);
         }
     
    +    public static ASN1Object tryGetExplicitBaseObject(ASN1TaggedObject taggedObject, int tagClass)
    +    {
    +        if (!taggedObject.hasTagClass(tagClass))
    +        {
    +            return null;
    +        }
    +
    +        return taggedObject.getExplicitBaseObject();
    +    }
    +
         public static ASN1Object tryGetExplicitBaseObject(ASN1TaggedObject taggedObject, int tagClass, int tagNo)
         {
             if (!taggedObject.hasTag(tagClass, tagNo))
    @@ -158,6 +214,11 @@ public static ASN1Object tryGetExplicitBaseObject(ASN1TaggedObject taggedObject,
             return taggedObject.getExplicitBaseObject();
         }
     
    +    public static ASN1Object tryGetExplicitContextBaseObject(ASN1TaggedObject taggedObject)
    +    {
    +        return tryGetExplicitBaseObject(taggedObject, BERTags.CONTEXT_SPECIFIC);
    +    }
    +
         public static ASN1Object tryGetExplicitContextBaseObject(ASN1TaggedObject taggedObject, int tagNo)
         {
             return tryGetExplicitBaseObject(taggedObject, BERTags.CONTEXT_SPECIFIC, tagNo);
    
    From dbd34623470d6925dd004bc21b8f4e14f9b3d788 Mon Sep 17 00:00:00 2001
    From: Peter Dettman 
    Date: Tue, 15 Apr 2025 01:22:05 +0700
    Subject: [PATCH 316/890] Refactor DistributionPointName
    
    ---
     .../asn1/x509/DistributionPointName.java      | 44 +++++++++++--------
     1 file changed, 25 insertions(+), 19 deletions(-)
    
    diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java b/core/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java
    index f69ccb3a40..078c577afc 100644
    --- a/core/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java
    +++ b/core/src/main/java/org/bouncycastle/asn1/x509/DistributionPointName.java
    @@ -6,6 +6,7 @@
     import org.bouncycastle.asn1.ASN1Primitive;
     import org.bouncycastle.asn1.ASN1Set;
     import org.bouncycastle.asn1.ASN1TaggedObject;
    +import org.bouncycastle.asn1.ASN1Util;
     import org.bouncycastle.asn1.DERTaggedObject;
     import org.bouncycastle.util.Strings;
     
    @@ -22,21 +23,10 @@ public class DistributionPointName
         extends ASN1Object
         implements ASN1Choice
     {
    -    ASN1Encodable        name;
    -    int                 type;
    -
         public static final int FULL_NAME = 0;
         public static final int NAME_RELATIVE_TO_CRL_ISSUER = 1;
     
    -    public static DistributionPointName getInstance(
    -        ASN1TaggedObject obj,
    -        boolean          explicit)
    -    {
    -        return getInstance(ASN1TaggedObject.getInstance(obj, true));
    -    }
    -
    -    public static DistributionPointName getInstance(
    -        Object  obj)
    +    public static DistributionPointName getInstance(Object obj)
         {
             if (obj == null || obj instanceof DistributionPointName)
             {
    @@ -50,6 +40,19 @@ else if (obj instanceof ASN1TaggedObject)
             throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
         }
     
    +    public static DistributionPointName getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit)
    +    {
    +        return getInstance(ASN1Util.getInstanceChoiceBaseObject(taggedObject, declaredExplicit, "DistributionPointName"));
    +    }
    +
    +    public static DistributionPointName getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit)
    +    {
    +        return getInstance(ASN1Util.getTaggedChoiceBaseObject(taggedObject, declaredExplicit, "DistributionPointName"));
    +    }
    +
    +    private final ASN1Encodable name;
    +    private final int type;
    +
         public DistributionPointName(
             int             type,
             ASN1Encodable   name)
    @@ -83,22 +86,25 @@ public ASN1Encodable getName()
         {
             return (ASN1Encodable)name;
         }
    -    
    -    public DistributionPointName(
    -        ASN1TaggedObject    obj)
    +
    +    public DistributionPointName(ASN1TaggedObject obj)
         {
             this.type = obj.getTagNo();
    -        
    -        if (type == 0)
    +
    +        if (obj.hasContextTag(FULL_NAME))
             {
                 this.name = GeneralNames.getInstance(obj, false);
             }
    -        else
    +        else if (obj.hasContextTag(NAME_RELATIVE_TO_CRL_ISSUER))
             {
                 this.name = ASN1Set.getInstance(obj, false);
             }
    +        else
    +        {
    +            throw new IllegalArgumentException("unknown tag: " + ASN1Util.getTagText(obj));
    +        }
         }
    -    
    +
         public ASN1Primitive toASN1Primitive()
         {
             return new DERTaggedObject(false, type, name);
    
    From f4295a4408e8fbcc783ebdf6fab4c40738d03cd3 Mon Sep 17 00:00:00 2001
    From: Peter Dettman 
    Date: Tue, 15 Apr 2025 21:34:33 +0700
    Subject: [PATCH 317/890] BCJSSE: Strip trailing dot from hostname for SNI,
     endpointID checks
    
    ---
     .../org/bouncycastle/jsse/provider/JsseUtils.java   | 13 +++++++++++++
     .../bouncycastle/jsse/provider/ProvTlsClient.java   |  6 +++++-
     .../jsse/provider/ProvX509TrustManager.java         |  7 +++++++
     3 files changed, 25 insertions(+), 1 deletion(-)
    
    diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/JsseUtils.java b/tls/src/main/java/org/bouncycastle/jsse/provider/JsseUtils.java
    index deb109461d..37935697e7 100644
    --- a/tls/src/main/java/org/bouncycastle/jsse/provider/JsseUtils.java
    +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/JsseUtils.java
    @@ -937,6 +937,19 @@ private static String stripOuterChars(String s, char openChar, char closeChar)
             return s;
         }
     
    +    static String stripTrailingDot(String s)
    +    {
    +        if (s != null && s.endsWith("."))
    +        {
    +            int sLast = s.length() - 1;
    +            if (sLast >= 0 && s.charAt(sLast) == '.')
    +            {
    +                return s.substring(0, sLast);
    +            }
    +        }
    +        return s;
    +    }
    +
         static boolean useCompatibilityMode()
         {
             return provTlsUseCompatibilityMode;
    diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsClient.java b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsClient.java
    index 11f89da0c0..7fbcfd8d33 100644
    --- a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsClient.java
    +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsClient.java
    @@ -174,7 +174,11 @@ protected Vector getSNIServerNames()
                 List sniServerNames = sslParameters.getServerNames();
                 if (null == sniServerNames)
                 {
    -                String peerHostSNI = manager.getPeerHostSNI();
    +                /*
    +                 * A fully qualified domain name (FQDN) may contain a trailing dot. We remove it for the
    +                 * purpose of SNI and endpoint ID checks (e.g. SNIHostName doesn't permit it).
    +                 */
    +                String peerHostSNI = JsseUtils.stripTrailingDot(manager.getPeerHostSNI());
     
                     /*
                      * TODO[jsse] Consider removing the restriction that the name must contain a '.'
    diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvX509TrustManager.java b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvX509TrustManager.java
    index 4e19c6452d..e7de360341 100644
    --- a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvX509TrustManager.java
    +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvX509TrustManager.java
    @@ -427,6 +427,13 @@ private static void checkEndpointID(X509Certificate certificate, String endpoint
             BCExtendedSSLSession sslSession) throws CertificateException
         {
             String peerHost = sslSession.getPeerHost();
    +
    +        /*
    +         * A fully qualified domain name (FQDN) may contain a trailing dot. We remove it for the purpose of
    +         * SNI and endpoint ID checks (e.g. SNIHostName doesn't permit it).
    +         */
    +        peerHost = JsseUtils.stripTrailingDot(peerHost);
    +
             if (checkServerTrusted)
             {
                 BCSNIHostName sniHostName = JsseUtils.getSNIHostName(sslSession.getRequestedServerNames());
    
    From 6689bc9f1b3dcdeb72b68766f67bd1f3f4e13cc5 Mon Sep 17 00:00:00 2001
    From: Peter Dettman 
    Date: Tue, 15 Apr 2025 22:09:53 +0700
    Subject: [PATCH 318/890] Update HandshakeType values
    
    ---
     .../org/bouncycastle/tls/HandshakeType.java   | 28 +++++++++++++++++++
     1 file changed, 28 insertions(+)
    
    diff --git a/tls/src/main/java/org/bouncycastle/tls/HandshakeType.java b/tls/src/main/java/org/bouncycastle/tls/HandshakeType.java
    index b87f24c9ce..8901144bf7 100644
    --- a/tls/src/main/java/org/bouncycastle/tls/HandshakeType.java
    +++ b/tls/src/main/java/org/bouncycastle/tls/HandshakeType.java
    @@ -42,11 +42,27 @@ public class HandshakeType
         public static final short key_update = 24;
         public static final short message_hash = 254;
     
    +    /*
    +     * RFC 8870 
    +     */
    +    public static final short ekt_key = 26;
    +
         /*
          * RFC 8879 
          */
         public static final short compressed_certificate = 25;
     
    +    /*
    +     * RFC 9147 
    +     */
    +    public static final short request_connection_id = 9;
    +    public static final short new_connection_id = 10;
    +
    +    /*
    +     * RFC 9261 
    +     */
    +    public static final short client_certificate_request = 17;
    +
         public static String getName(short handshakeType)
         {
             switch (handshakeType)
    @@ -91,8 +107,16 @@ public static String getName(short handshakeType)
                 return "key_update";
             case message_hash:
                 return "message_hash";
    +        case ekt_key:
    +            return "ekt_key";
             case compressed_certificate:
                 return "compressed_certificate";
    +        case request_connection_id:
    +            return "request_connection_id";
    +        case new_connection_id:
    +            return "new_connection_id";
    +        case client_certificate_request:
    +            return "client_certificate_request";
             default:
                 return "UNKNOWN";
             }
    @@ -127,7 +151,11 @@ public static boolean isRecognized(short handshakeType)
             case encrypted_extensions:
             case key_update:
             case message_hash:
    +        case ekt_key:
             case compressed_certificate:
    +        case request_connection_id:
    +        case new_connection_id:
    +        case client_certificate_request:
                 return true;
             default:
                 return false;
    
    From bd4dccc8f8012a079a3ce65354b255fe9128377a Mon Sep 17 00:00:00 2001
    From: Peter Dettman 
    Date: Wed, 16 Apr 2025 16:14:49 +0700
    Subject: [PATCH 319/890] Example SLH-DSA credentials from
     draft-ietf-lamps-x509-slhdsa-04
    
    ---
     .../org/bouncycastle/cert/test/AllTests.java  |  1 +
     .../cert/test/SLHDSACredentialsTest.java      | 39 +++++++++++++++++++
     .../cert/test/SampleCredentials.java          |  3 ++
     3 files changed, 43 insertions(+)
     create mode 100644 pkix/src/test/java/org/bouncycastle/cert/test/SLHDSACredentialsTest.java
    
    diff --git a/pkix/src/test/java/org/bouncycastle/cert/test/AllTests.java b/pkix/src/test/java/org/bouncycastle/cert/test/AllTests.java
    index f39effdbb5..f71b205e71 100644
    --- a/pkix/src/test/java/org/bouncycastle/cert/test/AllTests.java
    +++ b/pkix/src/test/java/org/bouncycastle/cert/test/AllTests.java
    @@ -35,6 +35,7 @@ public void testSimpleTests()
                 new GOSTR3410_2012_256GenerateCertificate(),
                 new MLDSACredentialsTest(),
                 new PKCS10Test(),
    +            new SLHDSACredentialsTest(),
                 new X509ExtensionUtilsTest(),
             };
     
    diff --git a/pkix/src/test/java/org/bouncycastle/cert/test/SLHDSACredentialsTest.java b/pkix/src/test/java/org/bouncycastle/cert/test/SLHDSACredentialsTest.java
    new file mode 100644
    index 0000000000..c11a5a8131
    --- /dev/null
    +++ b/pkix/src/test/java/org/bouncycastle/cert/test/SLHDSACredentialsTest.java
    @@ -0,0 +1,39 @@
    +package org.bouncycastle.cert.test;
    +
    +import java.security.GeneralSecurityException;
    +import java.security.PublicKey;
    +import java.security.Security;
    +import java.security.cert.X509Certificate;
    +
    +import org.bouncycastle.jce.provider.BouncyCastleProvider;
    +import org.bouncycastle.util.test.SimpleTest;
    +
    +public class SLHDSACredentialsTest
    +    extends SimpleTest
    +{
    +    public String getName()
    +    {
    +        return "SLHDSACredentials";
    +    }
    +
    +    public void performTest()
    +        throws Exception
    +    {
    +        checkSampleCredentials(SampleCredentials.SLH_DSA_SHA2_128S);
    +    }
    +
    +    private static void checkSampleCredentials(SampleCredentials creds)
    +        throws GeneralSecurityException
    +    {
    +        X509Certificate cert = creds.getCertificate();
    +        PublicKey pubKey = cert.getPublicKey();
    +        cert.verify(pubKey, BouncyCastleProvider.PROVIDER_NAME);
    +    }
    +
    +    public static void main(String[] args)
    +    {
    +        Security.addProvider(new BouncyCastleProvider());
    +
    +        runTest(new SLHDSACredentialsTest());
    +    }
    +}
    diff --git a/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java b/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java
    index b3f86c2b34..790c03a4ce 100644
    --- a/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java
    +++ b/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java
    @@ -28,6 +28,9 @@ public class SampleCredentials
         public static final SampleCredentials ML_DSA_65 = load("ML-DSA-65", "pkix/cert/mldsa", "ML-DSA-65.pem");
         public static final SampleCredentials ML_DSA_87 = load("ML-DSA-87", "pkix/cert/mldsa", "ML-DSA-87.pem");
     
    +    public static final SampleCredentials SLH_DSA_SHA2_128S = load("SLH-DSA-SHA2-128S", "pkix/cert/slhdsa",
    +        "SLH-DSA-SHA2-128S.pem");
    +
         private static PemObject expectPemObject(PemReader pemReader, String type)
             throws IOException
         {
    
    From 242f960aa349ea697e47e4c4058dec4234647810 Mon Sep 17 00:00:00 2001
    From: Peter Dettman 
    Date: Wed, 16 Apr 2025 16:33:33 +0700
    Subject: [PATCH 320/890] Merge related test cases
    
    ---
     .../cms/test/NewSignedDataTest.java           | 43 ++++++-------------
     1 file changed, 13 insertions(+), 30 deletions(-)
    
    diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java
    index b1676a5250..f7ee2391cb 100644
    --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java
    +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java
    @@ -1839,24 +1839,12 @@ public void testEd25519()
              */
             AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512);
     
    +        detachedTest(_signEd25519KP, _signEd25519Cert, "Ed25519", EdECObjectIdentifiers.id_Ed25519, expectedDigAlgId);
    +
             encapsulatedTest(_signEd25519KP, _signEd25519Cert, "Ed25519", EdECObjectIdentifiers.id_Ed25519,
                 expectedDigAlgId);
         }
     
    -    public void testEd25519Detached()
    -        throws Exception
    -    {
    -        /*
    -         * RFC 8419 3.1. When signing with Ed25519, the digestAlgorithm MUST be id-sha512, and the algorithm
    -         * parameters field MUST be absent.
    -         * 
    -         * We confirm here that our implementation defaults to SHA-512 for the digest algorithm.
    -         */
    -        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512);
    -
    -        detachedTest(_signEd25519KP, _signEd25519Cert, "Ed25519", EdECObjectIdentifiers.id_Ed25519, expectedDigAlgId);
    -    }
    -
         public void testEd448()
             throws Exception
         {
    @@ -1870,23 +1858,9 @@ public void testEd448()
             AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256_len,
                 new ASN1Integer(512));
     
    -        encapsulatedTest(_signEd448KP, _signEd448Cert, "Ed448", EdECObjectIdentifiers.id_Ed448, expectedDigAlgId);
    -    }
    -
    -    public void testEd448Detached()
    -        throws Exception
    -    {
    -        /*
    -         * RFC 8419 3.1. When signing with Ed448, the digestAlgorithm MUST be id-shake256-len, the algorithm
    -         * parameters field MUST be present, and the parameter MUST contain 512, encoded as a positive integer
    -         * value.
    -         * 
    -         * We confirm here that our implementation defaults to id-shake256-len/512 for the digest algorithm.
    -         */
    -        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256_len,
    -            new ASN1Integer(512));
    -
             detachedTest(_signEd448KP, _signEd448Cert, "Ed448", EdECObjectIdentifiers.id_Ed448, expectedDigAlgId);
    +
    +        encapsulatedTest(_signEd448KP, _signEd448Cert, "Ed448", EdECObjectIdentifiers.id_Ed448, expectedDigAlgId);
         }
     
         public void testEd25519WithNoAttr()
    @@ -2362,6 +2336,9 @@ public SignerInformationVerifier get(SignerId signerId)
     //         */
     //        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512);
     //
    +//        detachedTest(_signMLDsa44KP, _signMLDsa44Cert, "ML-DSA-44", NISTObjectIdentifiers.id_ml_dsa_44,
    +//            expectedDigAlgId);
    +//
     //        encapsulatedTest(_signMLDsa44KP, _signMLDsa44Cert, "ML-DSA-44", NISTObjectIdentifiers.id_ml_dsa_44,
     //            expectedDigAlgId);
     //    }
    @@ -2379,6 +2356,9 @@ public SignerInformationVerifier get(SignerId signerId)
     //         */
     //        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512);
     //
    +//        detachedTest(_signMLDsa65KP, _signMLDsa65Cert, "ML-DSA-65", NISTObjectIdentifiers.id_ml_dsa_65,
    +//            expectedDigAlgId);
    +//
     //        encapsulatedTest(_signMLDsa65KP, _signMLDsa65Cert, "ML-DSA-65", NISTObjectIdentifiers.id_ml_dsa_65,
     //            expectedDigAlgId);
     //    }
    @@ -2396,6 +2376,9 @@ public SignerInformationVerifier get(SignerId signerId)
     //         */
     //        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512);
     //
    +//        detachedTest(_signMLDsa87KP, _signMLDsa87Cert, "ML-DSA-87", NISTObjectIdentifiers.id_ml_dsa_87,
    +//            expectedDigAlgId);
    +//
     //        encapsulatedTest(_signMLDsa87KP, _signMLDsa87Cert, "ML-DSA-87", NISTObjectIdentifiers.id_ml_dsa_87,
     //            expectedDigAlgId);
     //    }
    
    From 14f3c263ab66151a021aeb219a8af5a9aafc988a Mon Sep 17 00:00:00 2001
    From: Peter Dettman 
    Date: Wed, 16 Apr 2025 17:07:22 +0700
    Subject: [PATCH 321/890] CMS: Prepare SLH-DSA tests (not working yet)
    
    ---
     .../bouncycastle/cms/test/CMSTestUtil.java    |  85 ++++++
     .../cms/test/NewSignedDataTest.java           | 277 ++++++++++++++++++
     2 files changed, 362 insertions(+)
    
    diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java b/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java
    index dfc7134ef5..d143965ede 100644
    --- a/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java
    +++ b/pkix/src/test/java/org/bouncycastle/cms/test/CMSTestUtil.java
    @@ -68,6 +68,18 @@ public class CMSTestUtil
         public static KeyPairGenerator mlKem768Kpg;
         public static KeyPairGenerator mlKem1024Kpg;
         public static KeyPairGenerator ntruKpg;
    +    public static KeyPairGenerator slhDsa_Sha2_128f_Kpg;
    +    public static KeyPairGenerator slhDsa_Sha2_128s_Kpg;
    +    public static KeyPairGenerator slhDsa_Sha2_192f_Kpg;
    +    public static KeyPairGenerator slhDsa_Sha2_192s_Kpg;
    +    public static KeyPairGenerator slhDsa_Sha2_256f_Kpg;
    +    public static KeyPairGenerator slhDsa_Sha2_256s_Kpg;
    +    public static KeyPairGenerator slhDsa_Shake_128f_Kpg;
    +    public static KeyPairGenerator slhDsa_Shake_128s_Kpg;
    +    public static KeyPairGenerator slhDsa_Shake_192f_Kpg;
    +    public static KeyPairGenerator slhDsa_Shake_192s_Kpg;
    +    public static KeyPairGenerator slhDsa_Shake_256f_Kpg;
    +    public static KeyPairGenerator slhDsa_Shake_256s_Kpg;
         public static KeyGenerator     aes192kg;
         public static KeyGenerator     desede128kg;
         public static KeyGenerator     desede192kg;
    @@ -182,6 +194,19 @@ public class CMSTestUtil
                 mlKem768Kpg = KeyPairGenerator.getInstance("ML-KEM-768", "BC");
                 mlKem1024Kpg = KeyPairGenerator.getInstance("ML-KEM-1024", "BC");
     
    +            slhDsa_Sha2_128f_Kpg = KeyPairGenerator.getInstance("SLH-DSA-SHA2-128F", "BC");
    +            slhDsa_Sha2_128s_Kpg = KeyPairGenerator.getInstance("SLH-DSA-SHA2-128S", "BC");
    +            slhDsa_Sha2_192f_Kpg = KeyPairGenerator.getInstance("SLH-DSA-SHA2-192F", "BC");
    +            slhDsa_Sha2_192s_Kpg = KeyPairGenerator.getInstance("SLH-DSA-SHA2-192S", "BC");
    +            slhDsa_Sha2_256f_Kpg = KeyPairGenerator.getInstance("SLH-DSA-SHA2-256F", "BC");
    +            slhDsa_Sha2_256s_Kpg = KeyPairGenerator.getInstance("SLH-DSA-SHA2-256S", "BC");
    +            slhDsa_Shake_128f_Kpg = KeyPairGenerator.getInstance("SLH-DSA-SHAKE-128F", "BC");
    +            slhDsa_Shake_128s_Kpg = KeyPairGenerator.getInstance("SLH-DSA-SHAKE-128S", "BC");
    +            slhDsa_Shake_192f_Kpg = KeyPairGenerator.getInstance("SLH-DSA-SHAKE-192F", "BC");
    +            slhDsa_Shake_192s_Kpg = KeyPairGenerator.getInstance("SLH-DSA-SHAKE-192S", "BC");
    +            slhDsa_Shake_256f_Kpg = KeyPairGenerator.getInstance("SLH-DSA-SHAKE-256F", "BC");
    +            slhDsa_Shake_256s_Kpg = KeyPairGenerator.getInstance("SLH-DSA-SHAKE-256S", "BC");
    +
                 aes192kg = KeyGenerator.getInstance("AES", "BC");
                 aes192kg.init(192, rand);
     
    @@ -323,6 +348,66 @@ public static KeyPair makeMLDsa87KeyPair()
             return mlDsa87Kpg.generateKeyPair();
         }
     
    +    public static KeyPair makeSlhDsa_Sha2_128f_KeyPair()
    +    {
    +        return slhDsa_Sha2_128f_Kpg.generateKeyPair();
    +    }
    +
    +    public static KeyPair makeSlhDsa_Sha2_128s_KeyPair()
    +    {
    +        return slhDsa_Sha2_128s_Kpg.generateKeyPair();
    +    }
    +
    +    public static KeyPair makeSlhDsa_Sha2_192f_KeyPair()
    +    {
    +        return slhDsa_Sha2_192f_Kpg.generateKeyPair();
    +    }
    +
    +    public static KeyPair makeSlhDsa_Sha2_192s_KeyPair()
    +    {
    +        return slhDsa_Sha2_192s_Kpg.generateKeyPair();
    +    }
    +
    +    public static KeyPair makeSlhDsa_Sha2_256f_KeyPair()
    +    {
    +        return slhDsa_Sha2_256f_Kpg.generateKeyPair();
    +    }
    +
    +    public static KeyPair makeSlhDsa_Sha2_256s_KeyPair()
    +    {
    +        return slhDsa_Sha2_256s_Kpg.generateKeyPair();
    +    }
    +
    +    public static KeyPair makeSlhDsa_Shake_128f_KeyPair()
    +    {
    +        return slhDsa_Shake_128f_Kpg.generateKeyPair();
    +    }
    +
    +    public static KeyPair makeSlhDsa_Shake_128s_KeyPair()
    +    {
    +        return slhDsa_Shake_128s_Kpg.generateKeyPair();
    +    }
    +
    +    public static KeyPair makeSlhDsa_Shake_192f_KeyPair()
    +    {
    +        return slhDsa_Shake_192f_Kpg.generateKeyPair();
    +    }
    +
    +    public static KeyPair makeSlhDsa_Shake_192s_KeyPair()
    +    {
    +        return slhDsa_Shake_192s_Kpg.generateKeyPair();
    +    }
    +
    +    public static KeyPair makeSlhDsa_Shake_256f_KeyPair()
    +    {
    +        return slhDsa_Shake_256f_Kpg.generateKeyPair();
    +    }
    +
    +    public static KeyPair makeSlhDsa_Shake_256s_KeyPair()
    +    {
    +        return slhDsa_Shake_256s_Kpg.generateKeyPair();
    +    }
    +
         public static SecretKey makeDesede128Key()
         {
             return desede128kg.generateKey();
    diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java
    index f7ee2391cb..28eee7602a 100644
    --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java
    +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java
    @@ -157,6 +157,31 @@ public class NewSignedDataTest
         private static KeyPair         _signMLDsa87KP;
         private static X509Certificate _signMLDsa87Cert;
     
    +    private static KeyPair         _signSlhDsa_Sha2_128f_KP;
    +    private static X509Certificate _signSlhDsa_Sha2_128f_Cert;
    +    private static KeyPair         _signSlhDsa_Sha2_128s_KP;
    +    private static X509Certificate _signSlhDsa_Sha2_128s_Cert;
    +    private static KeyPair         _signSlhDsa_Sha2_192f_KP;
    +    private static X509Certificate _signSlhDsa_Sha2_192f_Cert;
    +    private static KeyPair         _signSlhDsa_Sha2_192s_KP;
    +    private static X509Certificate _signSlhDsa_Sha2_192s_Cert;
    +    private static KeyPair         _signSlhDsa_Sha2_256f_KP;
    +    private static X509Certificate _signSlhDsa_Sha2_256f_Cert;
    +    private static KeyPair         _signSlhDsa_Sha2_256s_KP;
    +    private static X509Certificate _signSlhDsa_Sha2_256s_Cert;
    +    private static KeyPair         _signSlhDsa_Shake_128f_KP;
    +    private static X509Certificate _signSlhDsa_Shake_128f_Cert;
    +    private static KeyPair         _signSlhDsa_Shake_128s_KP;
    +    private static X509Certificate _signSlhDsa_Shake_128s_Cert;
    +    private static KeyPair         _signSlhDsa_Shake_192f_KP;
    +    private static X509Certificate _signSlhDsa_Shake_192f_Cert;
    +    private static KeyPair         _signSlhDsa_Shake_192s_KP;
    +    private static X509Certificate _signSlhDsa_Shake_192s_Cert;
    +    private static KeyPair         _signSlhDsa_Shake_256f_KP;
    +    private static X509Certificate _signSlhDsa_Shake_256f_Cert;
    +    private static KeyPair         _signSlhDsa_Shake_256s_KP;
    +    private static X509Certificate _signSlhDsa_Shake_256s_Cert;
    +
         private static String          _reciDN;
         private static KeyPair         _reciKP;
         private static X509Certificate _reciCert;
    @@ -743,6 +768,18 @@ private static byte[] loadPemContents(String path, String name)
             noParams.add(NISTObjectIdentifiers.id_ml_dsa_44);
             noParams.add(NISTObjectIdentifiers.id_ml_dsa_65);
             noParams.add(NISTObjectIdentifiers.id_ml_dsa_87);
    +        noParams.add(NISTObjectIdentifiers.id_slh_dsa_sha2_128f);
    +        noParams.add(NISTObjectIdentifiers.id_slh_dsa_sha2_128s);
    +        noParams.add(NISTObjectIdentifiers.id_slh_dsa_sha2_192f);
    +        noParams.add(NISTObjectIdentifiers.id_slh_dsa_sha2_192s);
    +        noParams.add(NISTObjectIdentifiers.id_slh_dsa_sha2_256f);
    +        noParams.add(NISTObjectIdentifiers.id_slh_dsa_sha2_256s);
    +        noParams.add(NISTObjectIdentifiers.id_slh_dsa_shake_128f);
    +        noParams.add(NISTObjectIdentifiers.id_slh_dsa_shake_128s);
    +        noParams.add(NISTObjectIdentifiers.id_slh_dsa_shake_192f);
    +        noParams.add(NISTObjectIdentifiers.id_slh_dsa_shake_192s);
    +        noParams.add(NISTObjectIdentifiers.id_slh_dsa_shake_256f);
    +        noParams.add(NISTObjectIdentifiers.id_slh_dsa_shake_256s);
         }
     
         public NewSignedDataTest(String name)
    @@ -824,6 +861,42 @@ private static void init()
                 _signMLDsa87KP   = CMSTestUtil.makeMLDsa87KeyPair();
                 _signMLDsa87Cert = CMSTestUtil.makeCertificate(_signMLDsa87KP, _signDN, _origKP, _origDN);
     
    +            _signSlhDsa_Sha2_128f_KP   = CMSTestUtil.makeSlhDsa_Sha2_128f_KeyPair();
    +            _signSlhDsa_Sha2_128f_Cert = CMSTestUtil.makeCertificate(_signSlhDsa_Sha2_128f_KP, _signDN, _origKP, _origDN);
    +
    +            _signSlhDsa_Sha2_128s_KP   = CMSTestUtil.makeSlhDsa_Sha2_128s_KeyPair();
    +            _signSlhDsa_Sha2_128s_Cert = CMSTestUtil.makeCertificate(_signSlhDsa_Sha2_128s_KP, _signDN, _origKP, _origDN);
    +
    +            _signSlhDsa_Sha2_192f_KP   = CMSTestUtil.makeSlhDsa_Sha2_192f_KeyPair();
    +            _signSlhDsa_Sha2_192f_Cert = CMSTestUtil.makeCertificate(_signSlhDsa_Sha2_192f_KP, _signDN, _origKP, _origDN);
    +
    +            _signSlhDsa_Sha2_192s_KP   = CMSTestUtil.makeSlhDsa_Sha2_192s_KeyPair();
    +            _signSlhDsa_Sha2_192s_Cert = CMSTestUtil.makeCertificate(_signSlhDsa_Sha2_192s_KP, _signDN, _origKP, _origDN);
    +
    +            _signSlhDsa_Sha2_256f_KP   = CMSTestUtil.makeSlhDsa_Sha2_256f_KeyPair();
    +            _signSlhDsa_Sha2_256f_Cert = CMSTestUtil.makeCertificate(_signSlhDsa_Sha2_256f_KP, _signDN, _origKP, _origDN);
    +
    +            _signSlhDsa_Sha2_256s_KP   = CMSTestUtil.makeSlhDsa_Sha2_256s_KeyPair();
    +            _signSlhDsa_Sha2_256s_Cert = CMSTestUtil.makeCertificate(_signSlhDsa_Sha2_256s_KP, _signDN, _origKP, _origDN);
    +
    +            _signSlhDsa_Shake_128f_KP   = CMSTestUtil.makeSlhDsa_Shake_128f_KeyPair();
    +            _signSlhDsa_Shake_128f_Cert = CMSTestUtil.makeCertificate(_signSlhDsa_Shake_128f_KP, _signDN, _origKP, _origDN);
    +
    +            _signSlhDsa_Shake_128s_KP   = CMSTestUtil.makeSlhDsa_Shake_128s_KeyPair();
    +            _signSlhDsa_Shake_128s_Cert = CMSTestUtil.makeCertificate(_signSlhDsa_Shake_128s_KP, _signDN, _origKP, _origDN);
    +
    +            _signSlhDsa_Shake_192f_KP   = CMSTestUtil.makeSlhDsa_Shake_192f_KeyPair();
    +            _signSlhDsa_Shake_192f_Cert = CMSTestUtil.makeCertificate(_signSlhDsa_Shake_192f_KP, _signDN, _origKP, _origDN);
    +
    +            _signSlhDsa_Shake_192s_KP   = CMSTestUtil.makeSlhDsa_Shake_192s_KeyPair();
    +            _signSlhDsa_Shake_192s_Cert = CMSTestUtil.makeCertificate(_signSlhDsa_Shake_192s_KP, _signDN, _origKP, _origDN);
    +
    +            _signSlhDsa_Shake_256f_KP   = CMSTestUtil.makeSlhDsa_Shake_256f_KeyPair();
    +            _signSlhDsa_Shake_256f_Cert = CMSTestUtil.makeCertificate(_signSlhDsa_Shake_256f_KP, _signDN, _origKP, _origDN);
    +
    +            _signSlhDsa_Shake_256s_KP   = CMSTestUtil.makeSlhDsa_Shake_256s_KeyPair();
    +            _signSlhDsa_Shake_256s_Cert = CMSTestUtil.makeCertificate(_signSlhDsa_Shake_256s_KP, _signDN, _origKP, _origDN);
    +
                 _reciDN   = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU";
                 _reciKP   = CMSTestUtil.makeKeyPair();
                 _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
    @@ -2381,6 +2454,210 @@ public SignerInformationVerifier get(SignerId signerId)
     //
     //        encapsulatedTest(_signMLDsa87KP, _signMLDsa87Cert, "ML-DSA-87", NISTObjectIdentifiers.id_ml_dsa_87,
     //            expectedDigAlgId);
    +//    }
    +
    +//    public void testSlhDsa_Sha2_128f()
    +//        throws Exception
    +//    {
    +//        /*
    +//         * draft-ietf-lamps-cms-sphincs-plus-19 4. (we initially only support the MUST-support algorithm)
    +//         *
    +//         * We confirm here that our implementation defaults to SHA-256 for the digest algorithm.
    +//         */
    +//        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256);
    +//
    +//        detachedTest(_signSlhDsa_Sha2_128f_KP, _signSlhDsa_Sha2_128f_Cert, "SLH-DSA-SHA2-128F",
    +//            NISTObjectIdentifiers.id_slh_dsa_sha2_128f, expectedDigAlgId);
    +//
    +//        encapsulatedTest(_signSlhDsa_Sha2_128f_KP, _signSlhDsa_Sha2_128f_Cert, "SLH-DSA-SHA2-128F",
    +//            NISTObjectIdentifiers.id_slh_dsa_sha2_128f, expectedDigAlgId);
    +//    }
    +//
    +//    public void testSlhDsa_Sha2_128s()
    +//        throws Exception
    +//    {
    +//        /*
    +//         * draft-ietf-lamps-cms-sphincs-plus-19 4. (we initially only support the MUST-support algorithm)
    +//         *
    +//         * We confirm here that our implementation defaults to SHA-256 for the digest algorithm.
    +//         */
    +//        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256);
    +//
    +//        detachedTest(_signSlhDsa_Sha2_128s_KP, _signSlhDsa_Sha2_128s_Cert, "SLH-DSA-SHA2-128S",
    +//            NISTObjectIdentifiers.id_slh_dsa_sha2_128s, expectedDigAlgId);
    +//
    +//        encapsulatedTest(_signSlhDsa_Sha2_128s_KP, _signSlhDsa_Sha2_128s_Cert, "SLH-DSA-SHA2-128S",
    +//            NISTObjectIdentifiers.id_slh_dsa_sha2_128s, expectedDigAlgId);
    +//    }
    +//
    +//    public void testSlhDsa_Sha2_192f()
    +//        throws Exception
    +//    {
    +//        /*
    +//         * draft-ietf-lamps-cms-sphincs-plus-19 4. (we initially only support the MUST-support algorithm)
    +//         *
    +//         * We confirm here that our implementation defaults to SHA-512 for the digest algorithm.
    +//         */
    +//        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512);
    +//
    +//        detachedTest(_signSlhDsa_Sha2_192f_KP, _signSlhDsa_Sha2_192f_Cert, "SLH-DSA-SHA2-192F",
    +//            NISTObjectIdentifiers.id_slh_dsa_sha2_192f, expectedDigAlgId);
    +//
    +//        encapsulatedTest(_signSlhDsa_Sha2_192f_KP, _signSlhDsa_Sha2_192f_Cert, "SLH-DSA-SHA2-192F",
    +//            NISTObjectIdentifiers.id_slh_dsa_sha2_192f, expectedDigAlgId);
    +//    }
    +//
    +//    public void testSlhDsa_Sha2_192s()
    +//        throws Exception
    +//    {
    +//        /*
    +//         * draft-ietf-lamps-cms-sphincs-plus-19 4. (we initially only support the MUST-support algorithm)
    +//         *
    +//         * We confirm here that our implementation defaults to SHA-512 for the digest algorithm.
    +//         */
    +//        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512);
    +//
    +//        detachedTest(_signSlhDsa_Sha2_192s_KP, _signSlhDsa_Sha2_192s_Cert, "SLH-DSA-SHA2-192S",
    +//            NISTObjectIdentifiers.id_slh_dsa_sha2_192s, expectedDigAlgId);
    +//
    +//        encapsulatedTest(_signSlhDsa_Sha2_192s_KP, _signSlhDsa_Sha2_192s_Cert, "SLH-DSA-SHA2-192S",
    +//            NISTObjectIdentifiers.id_slh_dsa_sha2_192s, expectedDigAlgId);
    +//    }
    +//
    +//    public void testSlhDsa_Sha2_256f()
    +//        throws Exception
    +//    {
    +//        /*
    +//         * draft-ietf-lamps-cms-sphincs-plus-19 4. (we initially only support the MUST-support algorithm)
    +//         *
    +//         * We confirm here that our implementation defaults to SHA-512 for the digest algorithm.
    +//         */
    +//        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512);
    +//
    +//        detachedTest(_signSlhDsa_Sha2_256f_KP, _signSlhDsa_Sha2_256f_Cert, "SLH-DSA-SHA2-256F",
    +//            NISTObjectIdentifiers.id_slh_dsa_sha2_256f, expectedDigAlgId);
    +//
    +//        encapsulatedTest(_signSlhDsa_Sha2_256f_KP, _signSlhDsa_Sha2_256f_Cert, "SLH-DSA-SHA2-256F",
    +//            NISTObjectIdentifiers.id_slh_dsa_sha2_256f, expectedDigAlgId);
    +//    }
    +//
    +//    public void testSlhDsa_Sha2_256s()
    +//        throws Exception
    +//    {
    +//        /*
    +//         * draft-ietf-lamps-cms-sphincs-plus-19 4. (we initially only support the MUST-support algorithm)
    +//         *
    +//         * We confirm here that our implementation defaults to SHA-512 for the digest algorithm.
    +//         */
    +//        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512);
    +//
    +//        detachedTest(_signSlhDsa_Sha2_256s_KP, _signSlhDsa_Sha2_256s_Cert, "SLH-DSA-SHA2-256S",
    +//            NISTObjectIdentifiers.id_slh_dsa_sha2_256s, expectedDigAlgId);
    +//
    +//        encapsulatedTest(_signSlhDsa_Sha2_256s_KP, _signSlhDsa_Sha2_256s_Cert, "SLH-DSA-SHA2-256S",
    +//            NISTObjectIdentifiers.id_slh_dsa_sha2_256s, expectedDigAlgId);
    +//    }
    +//
    +//    public void testSlhDsa_Shake_128f()
    +//        throws Exception
    +//    {
    +//        /*
    +//         * draft-ietf-lamps-cms-sphincs-plus-19 4. (we initially only support the MUST-support algorithm)
    +//         *
    +//         * We confirm here that our implementation defaults to SHAKE-128 for the digest algorithm.
    +//         */
    +//        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake128);
    +//
    +//        detachedTest(_signSlhDsa_Shake_128f_KP, _signSlhDsa_Shake_128f_Cert, "SLH-DSA-SHAKE-128F",
    +//            NISTObjectIdentifiers.id_slh_dsa_shake_128f, expectedDigAlgId);
    +//
    +//        encapsulatedTest(_signSlhDsa_Shake_128f_KP, _signSlhDsa_Shake_128f_Cert, "SLH-DSA-SHAKE-128F",
    +//            NISTObjectIdentifiers.id_slh_dsa_shake_128f, expectedDigAlgId);
    +//    }
    +//
    +//    public void testSlhDsa_Shake_128s()
    +//        throws Exception
    +//    {
    +//        /*
    +//         * draft-ietf-lamps-cms-sphincs-plus-19 4. (we initially only support the MUST-support algorithm)
    +//         *
    +//         * We confirm here that our implementation defaults to SHAKE-128 for the digest algorithm.
    +//         */
    +//        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake128);
    +//
    +//        detachedTest(_signSlhDsa_Shake_128s_KP, _signSlhDsa_Shake_128s_Cert, "SLH-DSA-SHAKE-128S",
    +//            NISTObjectIdentifiers.id_slh_dsa_shake_128s, expectedDigAlgId);
    +//
    +//        encapsulatedTest(_signSlhDsa_Shake_128s_KP, _signSlhDsa_Shake_128s_Cert, "SLH-DSA-SHAKE-128S",
    +//            NISTObjectIdentifiers.id_slh_dsa_shake_128s, expectedDigAlgId);
    +//    }
    +//
    +//    public void testSlhDsa_Shake_192f()
    +//        throws Exception
    +//    {
    +//        /*
    +//         * draft-ietf-lamps-cms-sphincs-plus-19 4. (we initially only support the MUST-support algorithm)
    +//         *
    +//         * We confirm here that our implementation defaults to SHAKE-256 for the digest algorithm.
    +//         */
    +//        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256);
    +//
    +//        detachedTest(_signSlhDsa_Shake_192f_KP, _signSlhDsa_Shake_192f_Cert, "SLH-DSA-SHAKE-192F",
    +//            NISTObjectIdentifiers.id_slh_dsa_shake_192f, expectedDigAlgId);
    +//
    +//        encapsulatedTest(_signSlhDsa_Shake_192f_KP, _signSlhDsa_Shake_192f_Cert, "SLH-DSA-SHAKE-192F",
    +//            NISTObjectIdentifiers.id_slh_dsa_shake_192f, expectedDigAlgId);
    +//    }
    +//
    +//    public void testSlhDsa_Shake_192s()
    +//        throws Exception
    +//    {
    +//        /*
    +//         * draft-ietf-lamps-cms-sphincs-plus-19 4. (we initially only support the MUST-support algorithm)
    +//         *
    +//         * We confirm here that our implementation defaults to SHAKE-256 for the digest algorithm.
    +//         */
    +//        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256);
    +//
    +//        detachedTest(_signSlhDsa_Shake_192s_KP, _signSlhDsa_Shake_192s_Cert, "SLH-DSA-SHAKE-192S",
    +//            NISTObjectIdentifiers.id_slh_dsa_shake_192s, expectedDigAlgId);
    +//
    +//        encapsulatedTest(_signSlhDsa_Shake_192s_KP, _signSlhDsa_Shake_192s_Cert, "SLH-DSA-SHAKE-192S",
    +//            NISTObjectIdentifiers.id_slh_dsa_shake_192s, expectedDigAlgId);
    +//    }
    +//
    +//    public void testSlhDsa_Shake_256f()
    +//        throws Exception
    +//    {
    +//        /*
    +//         * draft-ietf-lamps-cms-sphincs-plus-19 4. (we initially only support the MUST-support algorithm)
    +//         *
    +//         * We confirm here that our implementation defaults to SHAKE-256 for the digest algorithm.
    +//         */
    +//        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256);
    +//
    +//        detachedTest(_signSlhDsa_Shake_256f_KP, _signSlhDsa_Shake_256f_Cert, "SLH-DSA-SHAKE-256F",
    +//            NISTObjectIdentifiers.id_slh_dsa_shake_256f, expectedDigAlgId);
    +//
    +//        encapsulatedTest(_signSlhDsa_Shake_256f_KP, _signSlhDsa_Shake_256f_Cert, "SLH-DSA-SHAKE-256F",
    +//            NISTObjectIdentifiers.id_slh_dsa_shake_256f, expectedDigAlgId);
    +//    }
    +//
    +//    public void testSlhDsa_Shake_256s()
    +//        throws Exception
    +//    {
    +//        /*
    +//         * draft-ietf-lamps-cms-sphincs-plus-19 4. (we initially only support the MUST-support algorithm)
    +//         *
    +//         * We confirm here that our implementation defaults to SHAKE-256 for the digest algorithm.
    +//         */
    +//        AlgorithmIdentifier expectedDigAlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_shake256);
    +//
    +//        detachedTest(_signSlhDsa_Shake_256s_KP, _signSlhDsa_Shake_256s_Cert, "SLH-DSA-SHAKE-256S",
    +//            NISTObjectIdentifiers.id_slh_dsa_shake_256s, expectedDigAlgId);
    +//
    +//        encapsulatedTest(_signSlhDsa_Shake_256s_KP, _signSlhDsa_Shake_256s_Cert, "SLH-DSA-SHAKE-256S",
    +//            NISTObjectIdentifiers.id_slh_dsa_shake_256s, expectedDigAlgId);
     //    }
     
         private void rsaPSSTest(String signatureAlgorithmName)
    
    From 07b1001cbe8d856868ac2dcb648d5f9ed9fef714 Mon Sep 17 00:00:00 2001
    From: Peter Dettman 
    Date: Fri, 18 Apr 2025 20:05:20 +0700
    Subject: [PATCH 322/890] Minor update in comments
    
    ---
     core/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java | 2 ++
     1 file changed, 2 insertions(+)
    
    diff --git a/core/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java b/core/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java
    index ed1eec6892..bc4f597737 100644
    --- a/core/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java
    +++ b/core/src/main/java/org/bouncycastle/asn1/ocsp/CertStatus.java
    @@ -104,6 +104,8 @@ public ASN1Encodable getStatus()
          *                  good        [0]     IMPLICIT NULL,
          *                  revoked     [1]     IMPLICIT RevokedInfo,
          *                  unknown     [2]     IMPLICIT UnknownInfo }
    +     *
    +     * UnknownInfo ::= NULL
          * 
    */ public ASN1Primitive toASN1Primitive() From ffa16b15ec68d9e1d7a5d07c9d86f23e75e8b2a8 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 18 Apr 2025 20:12:16 +0700 Subject: [PATCH 323/890] Fix fieldID init in X9ECParameters --- .../org/bouncycastle/asn1/x9/X9Curve.java | 9 ++++-- .../bouncycastle/asn1/x9/X9ECParameters.java | 28 ++++++++----------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java b/core/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java index f0c7ffc0f8..567997c699 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java +++ b/core/src/main/java/org/bouncycastle/asn1/x9/X9Curve.java @@ -115,16 +115,21 @@ else if (ECAlgorithms.isF2mCurve(curve)) } } - public ECCurve getCurve() + public ECCurve getCurve() { return curve; } - public byte[] getSeed() + public byte[] getSeed() { return Arrays.clone(seed); } + public boolean hasSeed() + { + return seed != null; + } + /** * Produce an object suitable for an ASN1OutputStream. *
    diff --git a/core/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java b/core/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
    index 3dc42979b6..bb05755265 100644
    --- a/core/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
    +++ b/core/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
    @@ -14,7 +14,6 @@
     import org.bouncycastle.math.ec.ECPoint;
     import org.bouncycastle.math.field.FiniteField;
     import org.bouncycastle.math.field.PolynomialExtensionField;
    -import org.bouncycastle.util.Arrays;
     
     /**
      * ASN.1 def for Elliptic-Curve ECParameters structure. See
    @@ -27,11 +26,10 @@ public class X9ECParameters
         private static final BigInteger   ONE = BigInteger.valueOf(1);
     
         private X9FieldID           fieldID;
    -    private ECCurve             curve;
    +    private X9Curve             curve;
         private X9ECPoint           g;
         private BigInteger          n;
         private BigInteger          h;
    -    private byte[]              seed;
     
         private X9ECParameters(
             ASN1Sequence  seq)
    @@ -49,11 +47,10 @@ private X9ECParameters(
                 this.h = ((ASN1Integer)seq.getObjectAt(5)).getValue();
             }
     
    -        X9Curve x9c = new X9Curve(
    -            X9FieldID.getInstance(seq.getObjectAt(1)), n, h,
    -            ASN1Sequence.getInstance(seq.getObjectAt(2)));
    +        this.fieldID = X9FieldID.getInstance(seq.getObjectAt(1));
    +
    +        this.curve = new X9Curve(fieldID, n, h, ASN1Sequence.getInstance(seq.getObjectAt(2)));
     
    -        this.curve = x9c.getCurve();
             Object p = seq.getObjectAt(3);
     
             if (p instanceof X9ECPoint)
    @@ -62,10 +59,8 @@ private X9ECParameters(
             }
             else
             {
    -            this.g = new X9ECPoint(curve, (ASN1OctetString)p);
    +            this.g = new X9ECPoint(curve.getCurve(), (ASN1OctetString)p);
             }
    -
    -        this.seed = x9c.getSeed();
         }
     
         public static X9ECParameters getInstance(Object obj)
    @@ -107,11 +102,10 @@ public X9ECParameters(
             BigInteger  h,
             byte[]      seed)
         {
    -        this.curve = curve;
    +        this.curve = new X9Curve(curve, seed);
             this.g = g;
             this.n = n;
             this.h = h;
    -        this.seed = Arrays.clone(seed);
     
             FiniteField field = curve.getField();
             if (ECAlgorithms.isFpField(field))
    @@ -143,7 +137,7 @@ else if (exponents.length == 5)
     
         public ECCurve getCurve()
         {
    -        return curve;
    +        return curve.getCurve();
         }
     
         public ECPoint getG()
    @@ -163,12 +157,12 @@ public BigInteger getH()
     
         public byte[] getSeed()
         {
    -        return Arrays.clone(seed);
    +        return curve.getSeed();
         }
     
         public boolean hasSeed()
         {
    -        return null != seed;
    +        return curve.hasSeed();
         }
     
         /**
    @@ -178,7 +172,7 @@ public boolean hasSeed()
          */
         public X9Curve getCurveEntry()
         {
    -        return new X9Curve(curve, seed);
    +        return curve;
         }
     
         /**
    @@ -220,7 +214,7 @@ public ASN1Primitive toASN1Primitive()
     
             v.add(new ASN1Integer(ONE));
             v.add(fieldID);
    -        v.add(new X9Curve(curve, seed));
    +        v.add(curve);
             v.add(g);
             v.add(new ASN1Integer(n));
     
    
    From f84cdc7a83156277375cb0c5c1d730dc2948c571 Mon Sep 17 00:00:00 2001
    From: David Hook 
    Date: Sun, 20 Apr 2025 18:16:23 +1000
    Subject: [PATCH 324/890] removed legacy rainbow, moved gemss to legacy
    
    ---
     .../pqc/asn1/RainbowPrivateKey.java           | 349 -------------
     .../pqc/asn1/RainbowPublicKey.java            | 174 -------
     .../crypto/gemss/GeMSSEngine.java             |   2 +-
     .../crypto/gemss/GeMSSEngineProvider.java     |   2 +-
     .../gemss/GeMSSKeyGenerationParameters.java   |   2 +-
     .../crypto/gemss/GeMSSKeyPairGenerator.java   |   2 +-
     .../crypto/gemss/GeMSSKeyParameters.java      |   2 +-
     .../crypto/gemss/GeMSSParameters.java         |   2 +-
     .../gemss/GeMSSPrivateKeyParameters.java      |   2 +-
     .../gemss/GeMSSPublicKeyParameters.java       |   2 +-
     .../crypto/gemss/GeMSSSigner.java             |   2 +-
     .../{ => legacy}/crypto/gemss/GeMSSUtils.java |   2 +-
     .../{ => legacy}/crypto/gemss/Mul_GF2x.java   |   2 +-
     .../{ => legacy}/crypto/gemss/Pointer.java    |   2 +-
     .../crypto/gemss/PointerUnion.java            |   2 +-
     .../{ => legacy}/crypto/gemss/Rem_GF2n.java   |   2 +-
     .../crypto/gemss/SecretKeyHFE.java            |   2 +-
     .../pqc/legacy/crypto/rainbow/Layer.java      | 322 ------------
     .../RainbowKeyGenerationParameters.java       |  26 -
     .../rainbow/RainbowKeyPairGenerator.java      | 418 ---------------
     .../crypto/rainbow/RainbowKeyParameters.java  |  25 -
     .../crypto/rainbow/RainbowParameters.java     | 104 ----
     .../rainbow/RainbowPrivateKeyParameters.java  | 117 -----
     .../rainbow/RainbowPublicKeyParameters.java   |  53 --
     .../legacy/crypto/rainbow/RainbowSigner.java  | 311 -----------
     .../crypto/rainbow/util/ComputeInField.java   | 493 ------------------
     .../legacy/crypto/rainbow/util/GF2Field.java  | 139 -----
     .../crypto/rainbow/util/RainbowUtil.java      | 230 --------
     .../pqc/crypto/test/AllTests.java             |   3 +-
     .../pqc/crypto/test/RainbowTest.java          |  72 ---
     .../pqc/crypto/test/RainbowVectorTest.java    | 143 -----
     .../pqc/legacy/crypto/test/AllTests.java      |   1 +
     .../{ => legacy}/crypto/test/GeMSSTest.java   |  15 +-
     .../legacy/crypto/test/RainbowSignerTest.java |  62 ---
     .../legacy/crypto/test/RegressionTest.java    |   3 +-
     .../pqc/legacy/crypto/test/TestSampler.java   |  31 ++
     prov/src/main/ext-jdk1.9/module-info.java     |   4 +-
     .../pqc/jcajce/interfaces/QTESLAKey.java      |  16 -
     .../pqc/jcajce/interfaces/RainbowKey.java     |  16 -
     .../jcajce/interfaces/RainbowPrivateKey.java  |  14 -
     .../jcajce/interfaces/RainbowPublicKey.java   |   9 -
     .../pqc/jcajce/provider/Rainbow.java          |  51 --
     .../provider/rainbow/BCRainbowPrivateKey.java | 140 -----
     .../provider/rainbow/BCRainbowPublicKey.java  | 130 -----
     .../rainbow/RainbowKeyFactorySpi.java         | 116 -----
     .../rainbow/RainbowKeyPairGeneratorSpi.java   | 180 -------
     .../jcajce/provider/rainbow/SignatureSpi.java | 235 ---------
     .../pqc/jcajce/spec/RainbowParameterSpec.java |  50 --
     prov/src/main/jdk1.9/module-info.java         |   5 +-
     .../pqc/jcajce/provider/test/AllTests.java    |   2 -
     .../test/RainbowKeyPairGeneratorTest.java     |  50 --
     .../pqc/jcajce/provider/test/RainbowTest.java | 393 --------------
     .../asn1/bsi/BSIObjectIdentifiers.java        |   8 +-
     53 files changed, 64 insertions(+), 4476 deletions(-)
     delete mode 100644 core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPrivateKey.java
     delete mode 100644 core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPublicKey.java
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/GeMSSEngine.java (99%)
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/GeMSSEngineProvider.java (62%)
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/GeMSSKeyGenerationParameters.java (90%)
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/GeMSSKeyPairGenerator.java (99%)
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/GeMSSKeyParameters.java (89%)
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/GeMSSParameters.java (99%)
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/GeMSSPrivateKeyParameters.java (89%)
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/GeMSSPublicKeyParameters.java (90%)
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/GeMSSSigner.java (97%)
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/GeMSSUtils.java (97%)
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/Mul_GF2x.java (99%)
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/Pointer.java (99%)
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/PointerUnion.java (99%)
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/Rem_GF2n.java (99%)
     rename core/src/main/java/org/bouncycastle/pqc/{ => legacy}/crypto/gemss/SecretKeyHFE.java (92%)
     delete mode 100644 core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/Layer.java
     delete mode 100644 core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowKeyGenerationParameters.java
     delete mode 100644 core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowKeyPairGenerator.java
     delete mode 100644 core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowKeyParameters.java
     delete mode 100644 core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowParameters.java
     delete mode 100644 core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowPrivateKeyParameters.java
     delete mode 100644 core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowPublicKeyParameters.java
     delete mode 100644 core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowSigner.java
     delete mode 100644 core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/util/ComputeInField.java
     delete mode 100644 core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/util/GF2Field.java
     delete mode 100644 core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/util/RainbowUtil.java
     delete mode 100644 core/src/test/java/org/bouncycastle/pqc/crypto/test/RainbowTest.java
     delete mode 100644 core/src/test/java/org/bouncycastle/pqc/crypto/test/RainbowVectorTest.java
     rename core/src/test/java/org/bouncycastle/pqc/{ => legacy}/crypto/test/GeMSSTest.java (93%)
     delete mode 100644 core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/RainbowSignerTest.java
     create mode 100644 core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/TestSampler.java
     delete mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/QTESLAKey.java
     delete mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/RainbowKey.java
     delete mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/RainbowPrivateKey.java
     delete mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/RainbowPublicKey.java
     delete mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Rainbow.java
     delete mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/BCRainbowPrivateKey.java
     delete mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/BCRainbowPublicKey.java
     delete mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyFactorySpi.java
     delete mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyPairGeneratorSpi.java
     delete mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/SignatureSpi.java
     delete mode 100644 prov/src/main/java/org/bouncycastle/pqc/jcajce/spec/RainbowParameterSpec.java
     delete mode 100644 prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/RainbowKeyPairGeneratorTest.java
     delete mode 100644 prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/RainbowTest.java
    
    diff --git a/core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPrivateKey.java b/core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPrivateKey.java
    deleted file mode 100644
    index e194699be1..0000000000
    --- a/core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPrivateKey.java
    +++ /dev/null
    @@ -1,349 +0,0 @@
    -package org.bouncycastle.pqc.asn1;
    -
    -import org.bouncycastle.asn1.ASN1EncodableVector;
    -import org.bouncycastle.asn1.ASN1Integer;
    -import org.bouncycastle.asn1.ASN1Object;
    -import org.bouncycastle.asn1.ASN1ObjectIdentifier;
    -import org.bouncycastle.asn1.ASN1OctetString;
    -import org.bouncycastle.asn1.ASN1Primitive;
    -import org.bouncycastle.asn1.ASN1Sequence;
    -import org.bouncycastle.asn1.DEROctetString;
    -import org.bouncycastle.asn1.DERSequence;
    -import org.bouncycastle.pqc.legacy.crypto.rainbow.Layer;
    -import org.bouncycastle.pqc.legacy.crypto.rainbow.util.RainbowUtil;
    -
    -/**
    - * Return the key data to encode in the PrivateKeyInfo structure.
    - * 

    - * The ASN.1 definition of the key structure is - *

    - *   RainbowPrivateKey ::= SEQUENCE {
    - *         CHOICE
    - *         {
    - *         oid        OBJECT IDENTIFIER         -- OID identifying the algorithm
    - *         version    INTEGER                    -- 0
    - *         }
    - *     A1inv      SEQUENCE OF OCTET STRING  -- inversed matrix of L1
    - *     b1         OCTET STRING              -- translation vector of L1
    - *     A2inv      SEQUENCE OF OCTET STRING  -- inversed matrix of L2
    - *     b2         OCTET STRING              -- translation vector of L2
    - *     vi         OCTET STRING              -- num of elmts in each Set S
    - *     layers     SEQUENCE OF Layer         -- layers of F
    - *   }
    - *
    - *   Layer             ::= SEQUENCE OF Poly
    - *
    - *   Poly              ::= SEQUENCE {
    - *     alpha      SEQUENCE OF OCTET STRING
    - *     beta       SEQUENCE OF OCTET STRING
    - *     gamma      OCTET STRING
    - *     eta        INTEGER
    - *   }
    - * 
    - */ -public class RainbowPrivateKey - extends ASN1Object -{ - private ASN1Integer version; - private ASN1ObjectIdentifier oid; - - private byte[][] invA1; - private byte[] b1; - private byte[][] invA2; - private byte[] b2; - private byte[] vi; - private Layer[] layers; - - private RainbowPrivateKey(ASN1Sequence seq) - { - // or version - if (seq.getObjectAt(0) instanceof ASN1Integer) - { - version = ASN1Integer.getInstance(seq.getObjectAt(0)); - } - else - { - oid = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); - } - - // - ASN1Sequence asnA1 = (ASN1Sequence)seq.getObjectAt(1); - invA1 = new byte[asnA1.size()][]; - for (int i = 0; i < asnA1.size(); i++) - { - invA1[i] = ((ASN1OctetString)asnA1.getObjectAt(i)).getOctets(); - } - - // - ASN1Sequence asnb1 = (ASN1Sequence)seq.getObjectAt(2); - b1 = ((ASN1OctetString)asnb1.getObjectAt(0)).getOctets(); - - // - ASN1Sequence asnA2 = (ASN1Sequence)seq.getObjectAt(3); - invA2 = new byte[asnA2.size()][]; - for (int j = 0; j < asnA2.size(); j++) - { - invA2[j] = ((ASN1OctetString)asnA2.getObjectAt(j)).getOctets(); - } - - // - ASN1Sequence asnb2 = (ASN1Sequence)seq.getObjectAt(4); - b2 = ((ASN1OctetString)asnb2.getObjectAt(0)).getOctets(); - - // - ASN1Sequence asnvi = (ASN1Sequence)seq.getObjectAt(5); - vi = ((ASN1OctetString)asnvi.getObjectAt(0)).getOctets(); - - // - ASN1Sequence asnLayers = (ASN1Sequence)seq.getObjectAt(6); - - byte[][][][] alphas = new byte[asnLayers.size()][][][]; - byte[][][][] betas = new byte[asnLayers.size()][][][]; - byte[][][] gammas = new byte[asnLayers.size()][][]; - byte[][] etas = new byte[asnLayers.size()][]; - // a layer: - for (int l = 0; l < asnLayers.size(); l++) - { - ASN1Sequence asnLayer = (ASN1Sequence)asnLayers.getObjectAt(l); - - // alphas (num of alpha-2d-array = oi) - ASN1Sequence alphas3d = (ASN1Sequence)asnLayer.getObjectAt(0); - alphas[l] = new byte[alphas3d.size()][][]; - for (int m = 0; m < alphas3d.size(); m++) - { - ASN1Sequence alphas2d = (ASN1Sequence)alphas3d.getObjectAt(m); - alphas[l][m] = new byte[alphas2d.size()][]; - for (int n = 0; n < alphas2d.size(); n++) - { - alphas[l][m][n] = ((ASN1OctetString)alphas2d.getObjectAt(n)).getOctets(); - } - } - - // betas .... - ASN1Sequence betas3d = (ASN1Sequence)asnLayer.getObjectAt(1); - betas[l] = new byte[betas3d.size()][][]; - for (int mb = 0; mb < betas3d.size(); mb++) - { - ASN1Sequence betas2d = (ASN1Sequence)betas3d.getObjectAt(mb); - betas[l][mb] = new byte[betas2d.size()][]; - for (int nb = 0; nb < betas2d.size(); nb++) - { - betas[l][mb][nb] = ((ASN1OctetString)betas2d.getObjectAt(nb)).getOctets(); - } - } - - // gammas ... - ASN1Sequence gammas2d = (ASN1Sequence)asnLayer.getObjectAt(2); - gammas[l] = new byte[gammas2d.size()][]; - for (int mg = 0; mg < gammas2d.size(); mg++) - { - gammas[l][mg] = ((ASN1OctetString)gammas2d.getObjectAt(mg)).getOctets(); - } - - // eta ... - etas[l] = ((ASN1OctetString)asnLayer.getObjectAt(3)).getOctets(); - } - - int numOfLayers = vi.length - 1; - this.layers = new Layer[numOfLayers]; - for (int i = 0; i < numOfLayers; i++) - { - Layer l = new Layer(vi[i], vi[i + 1], RainbowUtil.convertArray(alphas[i]), - RainbowUtil.convertArray(betas[i]), RainbowUtil.convertArray(gammas[i]), RainbowUtil.convertArray(etas[i])); - this.layers[i] = l; - - } - } - - public RainbowPrivateKey(short[][] invA1, short[] b1, short[][] invA2, - short[] b2, int[] vi, Layer[] layers) - { - this.version = new ASN1Integer(1); - this.invA1 = RainbowUtil.convertArray(invA1); - this.b1 = RainbowUtil.convertArray(b1); - this.invA2 = RainbowUtil.convertArray(invA2); - this.b2 = RainbowUtil.convertArray(b2); - this.vi = RainbowUtil.convertIntArray(vi); - this.layers = layers; - } - - public static RainbowPrivateKey getInstance(Object o) - { - if (o instanceof RainbowPrivateKey) - { - return (RainbowPrivateKey)o; - } - else if (o != null) - { - return new RainbowPrivateKey(ASN1Sequence.getInstance(o)); - } - - return null; - } - - public ASN1Integer getVersion() - { - return version; - } - - /** - * Getter for the inverse matrix of A1. - * - * @return the A1inv inverse - */ - public short[][] getInvA1() - { - return RainbowUtil.convertArray(invA1); - } - - /** - * Getter for the translation part of the private quadratic map L1. - * - * @return b1 the translation part of L1 - */ - public short[] getB1() - { - return RainbowUtil.convertArray(b1); - } - - /** - * Getter for the translation part of the private quadratic map L2. - * - * @return b2 the translation part of L2 - */ - public short[] getB2() - { - return RainbowUtil.convertArray(b2); - } - - /** - * Getter for the inverse matrix of A2 - * - * @return the A2inv - */ - public short[][] getInvA2() - { - return RainbowUtil.convertArray(invA2); - } - - /** - * Returns the layers contained in the private key - * - * @return layers - */ - public Layer[] getLayers() - { - return this.layers; - } - - /** - * Returns the array of vi-s - * - * @return the vi - */ - public int[] getVi() - { - return RainbowUtil.convertArraytoInt(vi); - } - - public ASN1Primitive toASN1Primitive() - { - ASN1EncodableVector v = new ASN1EncodableVector(); - - // encode or version - if (version != null) - { - v.add(version); - } - else - { - v.add(oid); - } - - // encode - ASN1EncodableVector asnA1 = new ASN1EncodableVector(); - for (int i = 0; i < invA1.length; i++) - { - asnA1.add(new DEROctetString(invA1[i])); - } - v.add(new DERSequence(asnA1)); - - // encode - ASN1EncodableVector asnb1 = new ASN1EncodableVector(); - asnb1.add(new DEROctetString(b1)); - v.add(new DERSequence(asnb1)); - - // encode - ASN1EncodableVector asnA2 = new ASN1EncodableVector(); - for (int i = 0; i < invA2.length; i++) - { - asnA2.add(new DEROctetString(invA2[i])); - } - v.add(new DERSequence(asnA2)); - - // encode - ASN1EncodableVector asnb2 = new ASN1EncodableVector(); - asnb2.add(new DEROctetString(b2)); - v.add(new DERSequence(asnb2)); - - // encode - ASN1EncodableVector asnvi = new ASN1EncodableVector(); - asnvi.add(new DEROctetString(vi)); - v.add(new DERSequence(asnvi)); - - // encode - ASN1EncodableVector asnLayers = new ASN1EncodableVector(); - // a layer: - for (int l = 0; l < layers.length; l++) - { - ASN1EncodableVector aLayer = new ASN1EncodableVector(); - - // alphas (num of alpha-2d-array = oi) - byte[][][] alphas = RainbowUtil.convertArray(layers[l].getCoeffAlpha()); - ASN1EncodableVector alphas3d = new ASN1EncodableVector(); - for (int i = 0; i < alphas.length; i++) - { - ASN1EncodableVector alphas2d = new ASN1EncodableVector(); - for (int j = 0; j < alphas[i].length; j++) - { - alphas2d.add(new DEROctetString(alphas[i][j])); - } - alphas3d.add(new DERSequence(alphas2d)); - } - aLayer.add(new DERSequence(alphas3d)); - - // betas .... - byte[][][] betas = RainbowUtil.convertArray(layers[l].getCoeffBeta()); - ASN1EncodableVector betas3d = new ASN1EncodableVector(); - for (int i = 0; i < betas.length; i++) - { - ASN1EncodableVector betas2d = new ASN1EncodableVector(); - for (int j = 0; j < betas[i].length; j++) - { - betas2d.add(new DEROctetString(betas[i][j])); - } - betas3d.add(new DERSequence(betas2d)); - } - aLayer.add(new DERSequence(betas3d)); - - // gammas ... - byte[][] gammas = RainbowUtil.convertArray(layers[l].getCoeffGamma()); - ASN1EncodableVector asnG = new ASN1EncodableVector(); - for (int i = 0; i < gammas.length; i++) - { - asnG.add(new DEROctetString(gammas[i])); - } - aLayer.add(new DERSequence(asnG)); - - // eta - aLayer.add(new DEROctetString(RainbowUtil.convertArray(layers[l].getCoeffEta()))); - - // now, layer built up. add it! - asnLayers.add(new DERSequence(aLayer)); - } - - v.add(new DERSequence(asnLayers)); - - return new DERSequence(v); - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPublicKey.java b/core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPublicKey.java deleted file mode 100644 index 0e36f91f23..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/asn1/RainbowPublicKey.java +++ /dev/null @@ -1,174 +0,0 @@ -package org.bouncycastle.pqc.asn1; - -import org.bouncycastle.asn1.ASN1EncodableVector; -import org.bouncycastle.asn1.ASN1Integer; -import org.bouncycastle.asn1.ASN1Object; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.asn1.ASN1OctetString; -import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.ASN1Sequence; -import org.bouncycastle.asn1.DEROctetString; -import org.bouncycastle.asn1.DERSequence; -import org.bouncycastle.pqc.legacy.crypto.rainbow.util.RainbowUtil; - -/** - * This class implements an ASN.1 encoded Rainbow public key. The ASN.1 definition - * of this structure is: - *
    - *       RainbowPublicKey ::= SEQUENCE {
    - *         CHOICE
    - *         {
    - *         oid        OBJECT IDENTIFIER         -- OID identifying the algorithm
    - *         version    INTEGER                    -- 0
    - *         }
    - *         docLength        Integer               -- length of the code
    - *         coeffquadratic   SEQUENCE OF OCTET STRING -- quadratic (mixed) coefficients
    - *         coeffsingular    SEQUENCE OF OCTET STRING -- singular coefficients
    - *         coeffscalar    SEQUENCE OF OCTET STRING -- scalar coefficients
    - *       }
    - * 
    - */ -public class RainbowPublicKey - extends ASN1Object -{ - private ASN1Integer version; - private ASN1ObjectIdentifier oid; - private ASN1Integer docLength; - private byte[][] coeffQuadratic; - private byte[][] coeffSingular; - private byte[] coeffScalar; - - private RainbowPublicKey(ASN1Sequence seq) - { - // or version - if (seq.getObjectAt(0) instanceof ASN1Integer) - { - version = ASN1Integer.getInstance(seq.getObjectAt(0)); - } - else - { - oid = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0)); - } - - docLength = ASN1Integer.getInstance(seq.getObjectAt(1)); - - ASN1Sequence asnCoeffQuad = ASN1Sequence.getInstance(seq.getObjectAt(2)); - coeffQuadratic = new byte[asnCoeffQuad.size()][]; - for (int quadSize = 0; quadSize < asnCoeffQuad.size(); quadSize++) - { - coeffQuadratic[quadSize] = ASN1OctetString.getInstance(asnCoeffQuad.getObjectAt(quadSize)).getOctets(); - } - - ASN1Sequence asnCoeffSing = (ASN1Sequence)seq.getObjectAt(3); - coeffSingular = new byte[asnCoeffSing.size()][]; - for (int singSize = 0; singSize < asnCoeffSing.size(); singSize++) - { - coeffSingular[singSize] = ASN1OctetString.getInstance(asnCoeffSing.getObjectAt(singSize)).getOctets(); - } - - ASN1Sequence asnCoeffScalar = (ASN1Sequence)seq.getObjectAt(4); - coeffScalar = ASN1OctetString.getInstance(asnCoeffScalar.getObjectAt(0)).getOctets(); - } - - public RainbowPublicKey(int docLength, short[][] coeffQuadratic, short[][] coeffSingular, short[] coeffScalar) - { - this.version = new ASN1Integer(0); - this.docLength = new ASN1Integer(docLength); - this.coeffQuadratic = RainbowUtil.convertArray(coeffQuadratic); - this.coeffSingular = RainbowUtil.convertArray(coeffSingular); - this.coeffScalar = RainbowUtil.convertArray(coeffScalar); - } - - public static RainbowPublicKey getInstance(Object o) - { - if (o instanceof RainbowPublicKey) - { - return (RainbowPublicKey)o; - } - else if (o != null) - { - return new RainbowPublicKey(ASN1Sequence.getInstance(o)); - } - - return null; - } - - public ASN1Integer getVersion() - { - return version; - } - - /** - * @return the docLength - */ - public int getDocLength() - { - return this.docLength.intValueExact(); - } - - /** - * @return the coeffquadratic - */ - public short[][] getCoeffQuadratic() - { - return RainbowUtil.convertArray(coeffQuadratic); - } - - /** - * @return the coeffsingular - */ - public short[][] getCoeffSingular() - { - return RainbowUtil.convertArray(coeffSingular); - } - - /** - * @return the coeffscalar - */ - public short[] getCoeffScalar() - { - return RainbowUtil.convertArray(coeffScalar); - } - - public ASN1Primitive toASN1Primitive() - { - ASN1EncodableVector v = new ASN1EncodableVector(); - - // encode or version - if (version != null) - { - v.add(version); - } - else - { - v.add(oid); - } - - // encode - v.add(docLength); - - // encode - ASN1EncodableVector asnCoeffQuad = new ASN1EncodableVector(); - for (int i = 0; i < coeffQuadratic.length; i++) - { - asnCoeffQuad.add(new DEROctetString(coeffQuadratic[i])); - } - v.add(new DERSequence(asnCoeffQuad)); - - // encode - ASN1EncodableVector asnCoeffSing = new ASN1EncodableVector(); - for (int i = 0; i < coeffSingular.length; i++) - { - asnCoeffSing.add(new DEROctetString(coeffSingular[i])); - } - v.add(new DERSequence(asnCoeffSing)); - - // encode - ASN1EncodableVector asnCoeffScalar = new ASN1EncodableVector(); - asnCoeffScalar.add(new DEROctetString(coeffScalar)); - v.add(new DERSequence(asnCoeffScalar)); - - - return new DERSequence(v); - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSEngine.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSEngine.java similarity index 99% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSEngine.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSEngine.java index c0dec946d5..676ac77ab0 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSEngine.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; import java.math.BigInteger; import java.security.SecureRandom; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSEngineProvider.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSEngineProvider.java similarity index 62% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSEngineProvider.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSEngineProvider.java index 8fad283be1..7546096bc1 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSEngineProvider.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSEngineProvider.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; public interface GeMSSEngineProvider { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSKeyGenerationParameters.java similarity index 90% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSKeyGenerationParameters.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSKeyGenerationParameters.java index adfd87efc7..bc7922f5b2 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSKeyGenerationParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSKeyGenerationParameters.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; import java.security.SecureRandom; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSKeyPairGenerator.java similarity index 99% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSKeyPairGenerator.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSKeyPairGenerator.java index f16272d316..716331e94e 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSKeyPairGenerator.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; import java.security.SecureRandom; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSKeyParameters.java similarity index 89% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSKeyParameters.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSKeyParameters.java index 507d71c39d..211a7fdcc3 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSKeyParameters.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSParameters.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSParameters.java similarity index 99% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSParameters.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSParameters.java index 4c23af6dcf..3dd7c071de 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSParameters.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; import java.util.HashMap; import java.util.Map; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSPrivateKeyParameters.java similarity index 89% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSPrivateKeyParameters.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSPrivateKeyParameters.java index c270d897c7..4505d91e79 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSPrivateKeyParameters.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; import org.bouncycastle.util.Arrays; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSPublicKeyParameters.java similarity index 90% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSPublicKeyParameters.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSPublicKeyParameters.java index 9d841bcb66..33b3fb1802 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSPublicKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSPublicKeyParameters.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; import org.bouncycastle.util.Arrays; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSSigner.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSSigner.java similarity index 97% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSSigner.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSSigner.java index a84d7f93bf..2c72583bf5 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSSigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSSigner.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; import java.security.SecureRandom; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSUtils.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSUtils.java similarity index 97% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSUtils.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSUtils.java index 0af8f7bd32..9c03ed5c71 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/GeMSSUtils.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/GeMSSUtils.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; public class GeMSSUtils { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/Mul_GF2x.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/Mul_GF2x.java similarity index 99% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/Mul_GF2x.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/Mul_GF2x.java index 3f2d1f1671..fa05339512 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/Mul_GF2x.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/Mul_GF2x.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; abstract class Mul_GF2x { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/Pointer.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/Pointer.java similarity index 99% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/Pointer.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/Pointer.java index 140024c63c..b7184d4b59 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/Pointer.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/Pointer.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; import java.security.SecureRandom; import java.util.Arrays; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/PointerUnion.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/PointerUnion.java similarity index 99% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/PointerUnion.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/PointerUnion.java index 02512b0676..a74db502e2 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/PointerUnion.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/PointerUnion.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; import java.security.SecureRandom; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/Rem_GF2n.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/Rem_GF2n.java similarity index 99% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/Rem_GF2n.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/Rem_GF2n.java index 175158a2a9..1b61c3d253 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/Rem_GF2n.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/Rem_GF2n.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; abstract class Rem_GF2n { diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/SecretKeyHFE.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/SecretKeyHFE.java similarity index 92% rename from core/src/main/java/org/bouncycastle/pqc/crypto/gemss/SecretKeyHFE.java rename to core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/SecretKeyHFE.java index 7b431bea66..8a34aa79f6 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/gemss/SecretKeyHFE.java +++ b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/gemss/SecretKeyHFE.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.gemss; +package org.bouncycastle.pqc.legacy.crypto.gemss; class SecretKeyHFE { diff --git a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/Layer.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/Layer.java deleted file mode 100644 index 4cc2aed6de..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/Layer.java +++ /dev/null @@ -1,322 +0,0 @@ -package org.bouncycastle.pqc.legacy.crypto.rainbow; - -import java.security.SecureRandom; - -import org.bouncycastle.pqc.legacy.crypto.rainbow.util.GF2Field; -import org.bouncycastle.pqc.legacy.crypto.rainbow.util.RainbowUtil; -import org.bouncycastle.util.Arrays; - - -/** - * This class represents a layer of the Rainbow Oil- and Vinegar Map. Each Layer - * consists of oi polynomials with their coefficients, generated at random. - *

    - * To sign a document, we solve a LES (linear equation system) for each layer in - * order to find the oil variables of that layer and to be able to use the - * variables to compute the signature. This functionality is implemented in the - * RainbowSignature-class, by the aid of the private key. - *

    - * Each layer is a part of the private key. - *

    - * More information about the layer can be found in the paper of Jintai Ding, - * Dieter Schmidt: Rainbow, a New Multivariable Polynomial Signature Scheme. - * ACNS 2005: 164-175 (https://dx.doi.org/10.1007/11496137_12) - */ -public class Layer -{ - private int vi; // number of vinegars in this layer - private int viNext; // number of vinegars in next layer - private int oi; // number of oils in this layer - - /* - * k : index of polynomial - * - * i,j : indices of oil and vinegar variables - */ - private short[/* k */][/* i */][/* j */] coeff_alpha; - private short[/* k */][/* i */][/* j */] coeff_beta; - private short[/* k */][/* i */] coeff_gamma; - private short[/* k */] coeff_eta; - - /** - * Constructor - * - * @param vi number of vinegar variables of this layer - * @param viNext number of vinegar variables of next layer. It's the same as - * (num of oils) + (num of vinegars) of this layer. - * @param coeffAlpha alpha-coefficients in the polynomials of this layer - * @param coeffBeta beta-coefficients in the polynomials of this layer - * @param coeffGamma gamma-coefficients in the polynomials of this layer - * @param coeffEta eta-coefficients in the polynomials of this layer - */ - public Layer(byte vi, byte viNext, short[][][] coeffAlpha, - short[][][] coeffBeta, short[][] coeffGamma, short[] coeffEta) - { - this.vi = vi & 0xff; - this.viNext = viNext & 0xff; - this.oi = this.viNext - this.vi; - - // the secret coefficients of all polynomials in this layer - this.coeff_alpha = coeffAlpha; - this.coeff_beta = coeffBeta; - this.coeff_gamma = coeffGamma; - this.coeff_eta = coeffEta; - } - - /** - * This function generates the coefficients of all polynomials in this layer - * at random using random generator. - * - * @param sr the random generator which is to be used - */ - public Layer(int vi, int viNext, SecureRandom sr) - { - this.vi = vi; - this.viNext = viNext; - this.oi = viNext - vi; - - // the coefficients of all polynomials in this layer - this.coeff_alpha = new short[this.oi][this.oi][this.vi]; - this.coeff_beta = new short[this.oi][this.vi][this.vi]; - this.coeff_gamma = new short[this.oi][this.viNext]; - this.coeff_eta = new short[this.oi]; - - int numOfPoly = this.oi; // number of polynomials per layer - - // Alpha coeffs - for (int k = 0; k < numOfPoly; k++) - { - for (int i = 0; i < this.oi; i++) - { - for (int j = 0; j < this.vi; j++) - { - coeff_alpha[k][i][j] = (short)(sr.nextInt() & GF2Field.MASK); - } - } - } - // Beta coeffs - for (int k = 0; k < numOfPoly; k++) - { - for (int i = 0; i < this.vi; i++) - { - for (int j = 0; j < this.vi; j++) - { - coeff_beta[k][i][j] = (short)(sr.nextInt() & GF2Field.MASK); - } - } - } - // Gamma coeffs - for (int k = 0; k < numOfPoly; k++) - { - for (int i = 0; i < this.viNext; i++) - { - coeff_gamma[k][i] = (short)(sr.nextInt() & GF2Field.MASK); - } - } - // Eta - for (int k = 0; k < numOfPoly; k++) - { - coeff_eta[k] = (short)(sr.nextInt() & GF2Field.MASK); - } - } - - /** - * This method plugs in the vinegar variables into the polynomials of this - * layer and computes the coefficients of the Oil-variables as well as the - * free coefficient in each polynomial. - *

    - * It is needed for computing the Oil variables while signing. - * - * @param x vinegar variables of this layer that should be plugged into - * the polynomials. - * @return coeff the coefficients of Oil variables and the free coeff in the - * polynomials of this layer. - */ - public short[][] plugInVinegars(short[] x) - { - // temporary variable needed for the multiplication - short tmpMult = 0; - // coeff: 1st index = which polynomial, 2nd index=which variable - short[][] coeff = new short[oi][oi + 1]; // gets returned - // free coefficient per polynomial - short[] sum = new short[oi]; - - /* - * evaluate the beta-part of the polynomials (it contains no oil - * variables) - */ - for (int k = 0; k < oi; k++) - { - for (int i = 0; i < vi; i++) - { - for (int j = 0; j < vi; j++) - { - // tmp = beta * xi (plug in) - tmpMult = GF2Field.multElem(coeff_beta[k][i][j], x[i]); - // tmp = tmp * xj - tmpMult = GF2Field.multElem(tmpMult, x[j]); - // accumulate into the array for the free coefficients. - sum[k] = GF2Field.addElem(sum[k], tmpMult); - } - } - } - - /* evaluate the alpha-part (it contains oils) */ - for (int k = 0; k < oi; k++) - { - for (int i = 0; i < oi; i++) - { - for (int j = 0; j < vi; j++) - { - // alpha * xj (plug in) - tmpMult = GF2Field.multElem(coeff_alpha[k][i][j], x[j]); - // accumulate - coeff[k][i] = GF2Field.addElem(coeff[k][i], tmpMult); - } - } - } - /* evaluate the gama-part of the polynomial (containing no oils) */ - for (int k = 0; k < oi; k++) - { - for (int i = 0; i < vi; i++) - { - // gamma * xi (plug in) - tmpMult = GF2Field.multElem(coeff_gamma[k][i], x[i]); - // accumulate in the array for the free coefficients (per - // polynomial). - sum[k] = GF2Field.addElem(sum[k], tmpMult); - } - } - /* evaluate the gama-part of the polynomial (but containing oils) */ - for (int k = 0; k < oi; k++) - { - for (int i = vi; i < viNext; i++) - { // oils - // accumulate the coefficients of the oil variables (per - // polynomial). - coeff[k][i - vi] = GF2Field.addElem(coeff_gamma[k][i], - coeff[k][i - vi]); - } - } - /* evaluate the eta-part of the polynomial */ - for (int k = 0; k < oi; k++) - { - // accumulate in the array for the free coefficients per polynomial. - sum[k] = GF2Field.addElem(sum[k], coeff_eta[k]); - } - - /* put the free coefficients (sum) into the coeff-array as last column */ - for (int k = 0; k < oi; k++) - { - coeff[k][oi] = sum[k]; - } - return coeff; - } - - /** - * Getter for the number of vinegar variables of this layer. - * - * @return the number of vinegar variables of this layer. - */ - public int getVi() - { - return vi; - } - - /** - * Getter for the number of vinegar variables of the next layer. - * - * @return the number of vinegar variables of the next layer. - */ - public int getViNext() - { - return viNext; - } - - /** - * Getter for the number of Oil variables of this layer. - * - * @return the number of oil variables of this layer. - */ - public int getOi() - { - return oi; - } - - /** - * Getter for the alpha-coefficients of the polynomials in this layer. - * - * @return the coefficients of alpha-terms of this layer. - */ - public short[][][] getCoeffAlpha() - { - return coeff_alpha; - } - - /** - * Getter for the beta-coefficients of the polynomials in this layer. - * - * @return the coefficients of beta-terms of this layer. - */ - - public short[][][] getCoeffBeta() - { - return coeff_beta; - } - - /** - * Getter for the gamma-coefficients of the polynomials in this layer. - * - * @return the coefficients of gamma-terms of this layer - */ - public short[][] getCoeffGamma() - { - return coeff_gamma; - } - - /** - * Getter for the eta-coefficients of the polynomials in this layer. - * - * @return the coefficients eta of this layer - */ - public short[] getCoeffEta() - { - return coeff_eta; - } - - /** - * This function compares this Layer with another object. - * - * @param other the other object - * @return the result of the comparison - */ - public boolean equals(Object other) - { - if (other == null || !(other instanceof Layer)) - { - return false; - } - Layer otherLayer = (Layer)other; - - return vi == otherLayer.getVi() - && viNext == otherLayer.getViNext() - && oi == otherLayer.getOi() - && RainbowUtil.equals(coeff_alpha, otherLayer.getCoeffAlpha()) - && RainbowUtil.equals(coeff_beta, otherLayer.getCoeffBeta()) - && RainbowUtil.equals(coeff_gamma, otherLayer.getCoeffGamma()) - && RainbowUtil.equals(coeff_eta, otherLayer.getCoeffEta()); - } - - public int hashCode() - { - int hash = vi; - hash = hash * 37 + viNext; - hash = hash * 37 + oi; - hash = hash * 37 + Arrays.hashCode(coeff_alpha); - hash = hash * 37 + Arrays.hashCode(coeff_beta); - hash = hash * 37 + Arrays.hashCode(coeff_gamma); - hash = hash * 37 + Arrays.hashCode(coeff_eta); - - return hash; - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowKeyGenerationParameters.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowKeyGenerationParameters.java deleted file mode 100644 index 6fac377c1f..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowKeyGenerationParameters.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.bouncycastle.pqc.legacy.crypto.rainbow; - -import java.security.SecureRandom; - -import org.bouncycastle.crypto.KeyGenerationParameters; - -public class RainbowKeyGenerationParameters - extends KeyGenerationParameters -{ - private RainbowParameters params; - - public RainbowKeyGenerationParameters( - SecureRandom random, - RainbowParameters params) - { - // TODO: key size? - super(random, params.getVi()[params.getVi().length - 1] - params.getVi()[0]); - this.params = params; - } - - public RainbowParameters getParameters() - { - return params; - } -} - diff --git a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowKeyPairGenerator.java deleted file mode 100644 index b936c4b528..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowKeyPairGenerator.java +++ /dev/null @@ -1,418 +0,0 @@ -package org.bouncycastle.pqc.legacy.crypto.rainbow; - -import java.security.SecureRandom; - -import org.bouncycastle.crypto.AsymmetricCipherKeyPair; -import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; -import org.bouncycastle.crypto.CryptoServicesRegistrar; -import org.bouncycastle.crypto.KeyGenerationParameters; -import org.bouncycastle.pqc.legacy.crypto.rainbow.util.ComputeInField; -import org.bouncycastle.pqc.legacy.crypto.rainbow.util.GF2Field; - -/** - * This class implements AsymmetricCipherKeyPairGenerator. It is used - * as a generator for the private and public key of the Rainbow Signature - * Scheme. - *

    - * Detailed information about the key generation is to be found in the paper of - * Jintai Ding, Dieter Schmidt: Rainbow, a New Multivariable Polynomial - * Signature Scheme. ACNS 2005: 164-175 (https://dx.doi.org/10.1007/11496137_12) - */ -public class RainbowKeyPairGenerator - implements AsymmetricCipherKeyPairGenerator -{ - private boolean initialized = false; - private SecureRandom sr; - private RainbowKeyGenerationParameters rainbowParams; - - /* linear affine map L1: */ - private short[][] A1; // matrix of the lin. affine map L1(n-v1 x n-v1 matrix) - private short[][] A1inv; // inverted A1 - private short[] b1; // translation element of the lin.affine map L1 - - /* linear affine map L2: */ - private short[][] A2; // matrix of the lin. affine map (n x n matrix) - private short[][] A2inv; // inverted A2 - private short[] b2; // translation elemt of the lin.affine map L2 - - /* components of F: */ - private int numOfLayers; // u (number of sets S) - private Layer layers[]; // layers of polynomials of F - private int[] vi; // set of vinegar vars per layer. - - /* components of Public Key */ - private short[][] pub_quadratic; // quadratic(mixed) coefficients - private short[][] pub_singular; // singular coefficients - private short[] pub_scalar; // scalars - - // TODO - - /** - * The standard constructor tries to generate the Rainbow algorithm identifier - * with the corresponding OID. - */ - public RainbowKeyPairGenerator() - { - } - - - /** - * This function generates a Rainbow key pair. - * - * @return the generated key pair - */ - public AsymmetricCipherKeyPair genKeyPair() - { - RainbowPrivateKeyParameters privKey; - RainbowPublicKeyParameters pubKey; - - if (!initialized) - { - initializeDefault(); - } - - /* choose all coefficients at random */ - keygen(); - - /* now marshall them to PrivateKey */ - privKey = new RainbowPrivateKeyParameters(A1inv, b1, A2inv, b2, vi, layers); - - - /* marshall to PublicKey */ - pubKey = new RainbowPublicKeyParameters(vi[vi.length - 1] - vi[0], pub_quadratic, pub_singular, pub_scalar); - - return new AsymmetricCipherKeyPair(pubKey, privKey); - } - - // TODO - public void initialize( - KeyGenerationParameters param) - { - this.rainbowParams = (RainbowKeyGenerationParameters)param; - - // set source of randomness - this.sr = rainbowParams.getRandom(); - - // unmarshalling: - this.vi = this.rainbowParams.getParameters().getVi(); - this.numOfLayers = this.rainbowParams.getParameters().getNumOfLayers(); - - this.initialized = true; - } - - private void initializeDefault() - { - RainbowKeyGenerationParameters rbKGParams = new RainbowKeyGenerationParameters(CryptoServicesRegistrar.getSecureRandom(), new RainbowParameters()); - initialize(rbKGParams); - } - - /** - * This function calls the functions for the random generation of the coefficients - * and the matrices needed for the private key and the method for computing the public key. - */ - private void keygen() - { - generateL1(); - generateL2(); - generateF(); - computePublicKey(); - } - - /** - * This function generates the invertible affine linear map L1 = A1*x + b1 - *

    - * The translation part b1, is stored in a separate array. The inverse of - * the matrix-part of L1 A1inv is also computed here. - *

    - * This linear map hides the output of the map F. It is on k^(n-v1). - *

    - */ - private void generateL1() - { - - // dimension = n-v1 = vi[last] - vi[first] - int dim = vi[vi.length - 1] - vi[0]; - this.A1 = new short[dim][dim]; - this.A1inv = null; - ComputeInField c = new ComputeInField(); - - /* generation of A1 at random */ - while (A1inv == null) - { - for (int i = 0; i < dim; i++) - { - for (int j = 0; j < dim; j++) - { - A1[i][j] = (short)(sr.nextInt() & GF2Field.MASK); - } - } - A1inv = c.inverse(A1); - } - - /* generation of the translation vector at random */ - b1 = new short[dim]; - for (int i = 0; i < dim; i++) - { - b1[i] = (short)(sr.nextInt() & GF2Field.MASK); - } - } - - /** - * This function generates the invertible affine linear map L2 = A2*x + b2 - *

    - * The translation part b2, is stored in a separate array. The inverse of - * the matrix-part of L2 A2inv is also computed here. - *

    - * This linear map hides the output of the map F. It is on k^(n). - *

    - */ - private void generateL2() - { - - // dimension = n = vi[last] - int dim = vi[vi.length - 1]; - this.A2 = new short[dim][dim]; - this.A2inv = null; - ComputeInField c = new ComputeInField(); - - /* generation of A2 at random */ - while (this.A2inv == null) - { - for (int i = 0; i < dim; i++) - { - for (int j = 0; j < dim; j++) - { // one col extra for b - A2[i][j] = (short)(sr.nextInt() & GF2Field.MASK); - } - } - this.A2inv = c.inverse(A2); - } - /* generation of the translation vector at random */ - b2 = new short[dim]; - for (int i = 0; i < dim; i++) - { - b2[i] = (short)(sr.nextInt() & GF2Field.MASK); - } - - } - - /** - * This function generates the private map F, which consists of u-1 layers. - * Each layer consists of oi polynomials where oi = vi[i+1]-vi[i]. - *

    - * The methods for the generation of the coefficients of these polynomials - * are called here. - *

    - */ - private void generateF() - { - - this.layers = new Layer[this.numOfLayers]; - for (int i = 0; i < this.numOfLayers; i++) - { - layers[i] = new Layer(this.vi[i], this.vi[i + 1], sr); - } - } - - /** - * This function computes the public key from the private key. - *

    - * The composition of F with L2 is computed, followed by applying L1 to the - * composition's result. The singular and scalar values constitute to the - * public key as is, the quadratic terms are compacted in - * compactPublicKey() - *

    - */ - private void computePublicKey() - { - - ComputeInField c = new ComputeInField(); - int rows = this.vi[this.vi.length - 1] - this.vi[0]; - int vars = this.vi[this.vi.length - 1]; - // Fpub - short[][][] coeff_quadratic_3dim = new short[rows][vars][vars]; - this.pub_singular = new short[rows][vars]; - this.pub_scalar = new short[rows]; - - // Coefficients of layers of Private Key F - short[][][] coeff_alpha; - short[][][] coeff_beta; - short[][] coeff_gamma; - short[] coeff_eta; - - // Needed for counters; - int oils = 0; - int vins = 0; - int crnt_row = 0; // current row (polynomial) - - short vect_tmp[] = new short[vars]; // vector tmp; - short sclr_tmp = 0; - - // Composition of F and L2: Insert L2 = A2*x+b2 in F - for (int l = 0; l < this.layers.length; l++) - { - // get coefficients of current layer - coeff_alpha = this.layers[l].getCoeffAlpha(); - coeff_beta = this.layers[l].getCoeffBeta(); - coeff_gamma = this.layers[l].getCoeffGamma(); - coeff_eta = this.layers[l].getCoeffEta(); - oils = coeff_alpha[0].length;// this.layers[l].getOi(); - vins = coeff_beta[0].length;// this.layers[l].getVi(); - // compute polynomials of layer - for (int p = 0; p < oils; p++) - { - // multiply alphas - for (int x1 = 0; x1 < oils; x1++) - { - for (int x2 = 0; x2 < vins; x2++) - { - // multiply polynomial1 with polynomial2 - vect_tmp = c.multVect(coeff_alpha[p][x1][x2], - this.A2[x1 + vins]); - coeff_quadratic_3dim[crnt_row + p] = c.addSquareMatrix( - coeff_quadratic_3dim[crnt_row + p], c - .multVects(vect_tmp, this.A2[x2])); - // mul poly1 with scalar2 - vect_tmp = c.multVect(this.b2[x2], vect_tmp); - this.pub_singular[crnt_row + p] = c.addVect(vect_tmp, - this.pub_singular[crnt_row + p]); - // mul scalar1 with poly2 - vect_tmp = c.multVect(coeff_alpha[p][x1][x2], - this.A2[x2]); - vect_tmp = c.multVect(b2[x1 + vins], vect_tmp); - this.pub_singular[crnt_row + p] = c.addVect(vect_tmp, - this.pub_singular[crnt_row + p]); - // mul scalar1 with scalar2 - sclr_tmp = GF2Field.multElem(coeff_alpha[p][x1][x2], - this.b2[x1 + vins]); - this.pub_scalar[crnt_row + p] = GF2Field.addElem( - this.pub_scalar[crnt_row + p], GF2Field - .multElem(sclr_tmp, this.b2[x2])); - } - } - // multiply betas - for (int x1 = 0; x1 < vins; x1++) - { - for (int x2 = 0; x2 < vins; x2++) - { - // multiply polynomial1 with polynomial2 - vect_tmp = c.multVect(coeff_beta[p][x1][x2], - this.A2[x1]); - coeff_quadratic_3dim[crnt_row + p] = c.addSquareMatrix( - coeff_quadratic_3dim[crnt_row + p], c - .multVects(vect_tmp, this.A2[x2])); - // mul poly1 with scalar2 - vect_tmp = c.multVect(this.b2[x2], vect_tmp); - this.pub_singular[crnt_row + p] = c.addVect(vect_tmp, - this.pub_singular[crnt_row + p]); - // mul scalar1 with poly2 - vect_tmp = c.multVect(coeff_beta[p][x1][x2], - this.A2[x2]); - vect_tmp = c.multVect(this.b2[x1], vect_tmp); - this.pub_singular[crnt_row + p] = c.addVect(vect_tmp, - this.pub_singular[crnt_row + p]); - // mul scalar1 with scalar2 - sclr_tmp = GF2Field.multElem(coeff_beta[p][x1][x2], - this.b2[x1]); - this.pub_scalar[crnt_row + p] = GF2Field.addElem( - this.pub_scalar[crnt_row + p], GF2Field - .multElem(sclr_tmp, this.b2[x2])); - } - } - // multiply gammas - for (int n = 0; n < vins + oils; n++) - { - // mul poly with scalar - vect_tmp = c.multVect(coeff_gamma[p][n], this.A2[n]); - this.pub_singular[crnt_row + p] = c.addVect(vect_tmp, - this.pub_singular[crnt_row + p]); - // mul scalar with scalar - this.pub_scalar[crnt_row + p] = GF2Field.addElem( - this.pub_scalar[crnt_row + p], GF2Field.multElem( - coeff_gamma[p][n], this.b2[n])); - } - // add eta - this.pub_scalar[crnt_row + p] = GF2Field.addElem( - this.pub_scalar[crnt_row + p], coeff_eta[p]); - } - crnt_row = crnt_row + oils; - } - - // Apply L1 = A1*x+b1 to composition of F and L2 - { - // temporary coefficient arrays - short[][][] tmp_c_quad = new short[rows][vars][vars]; - short[][] tmp_c_sing = new short[rows][vars]; - short[] tmp_c_scal = new short[rows]; - for (int r = 0; r < rows; r++) - { - for (int q = 0; q < A1.length; q++) - { - tmp_c_quad[r] = c.addSquareMatrix(tmp_c_quad[r], c - .multMatrix(A1[r][q], coeff_quadratic_3dim[q])); - tmp_c_sing[r] = c.addVect(tmp_c_sing[r], c.multVect( - A1[r][q], this.pub_singular[q])); - tmp_c_scal[r] = GF2Field.addElem(tmp_c_scal[r], GF2Field - .multElem(A1[r][q], this.pub_scalar[q])); - } - tmp_c_scal[r] = GF2Field.addElem(tmp_c_scal[r], b1[r]); - } - // set public key - coeff_quadratic_3dim = tmp_c_quad; - this.pub_singular = tmp_c_sing; - this.pub_scalar = tmp_c_scal; - } - compactPublicKey(coeff_quadratic_3dim); - } - - /** - * The quadratic (or mixed) terms of the public key are compacted from a n x - * n matrix per polynomial to an upper diagonal matrix stored in one integer - * array of n (n + 1) / 2 elements per polynomial. The ordering of elements - * is lexicographic and the result is updating this.pub_quadratic, - * which stores the quadratic elements of the public key. - * - * @param coeff_quadratic_to_compact 3-dimensional array containing a n x n Matrix for each of the - * n - v1 polynomials - */ - private void compactPublicKey(short[][][] coeff_quadratic_to_compact) - { - int polynomials = coeff_quadratic_to_compact.length; - int n = coeff_quadratic_to_compact[0].length; - int entries = n * (n + 1) / 2;// the small gauss - this.pub_quadratic = new short[polynomials][entries]; - int offset = 0; - - for (int p = 0; p < polynomials; p++) - { - offset = 0; - for (int x = 0; x < n; x++) - { - for (int y = x; y < n; y++) - { - if (y == x) - { - this.pub_quadratic[p][offset] = coeff_quadratic_to_compact[p][x][y]; - } - else - { - this.pub_quadratic[p][offset] = GF2Field.addElem( - coeff_quadratic_to_compact[p][x][y], - coeff_quadratic_to_compact[p][y][x]); - } - offset++; - } - } - } - } - - public void init(KeyGenerationParameters param) - { - this.initialize(param); - } - - public AsymmetricCipherKeyPair generateKeyPair() - { - return genKeyPair(); - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowKeyParameters.java deleted file mode 100644 index 5e164fa41b..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowKeyParameters.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.bouncycastle.pqc.legacy.crypto.rainbow; - -import org.bouncycastle.crypto.params.AsymmetricKeyParameter; - -public class RainbowKeyParameters - extends AsymmetricKeyParameter -{ - private int docLength; - - public RainbowKeyParameters( - boolean isPrivate, - int docLength) - { - super(isPrivate); - this.docLength = docLength; - } - - /** - * @return the docLength - */ - public int getDocLength() - { - return this.docLength; - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowParameters.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowParameters.java deleted file mode 100644 index 3872ded350..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowParameters.java +++ /dev/null @@ -1,104 +0,0 @@ -package org.bouncycastle.pqc.legacy.crypto.rainbow; - -import org.bouncycastle.crypto.CipherParameters; - -public class RainbowParameters - implements CipherParameters -{ - - /** - * DEFAULT PARAMS - */ - /* - * Vi = vinegars per layer whereas n is vu (vu = 33 = n) such that - * - * v1 = 6; o1 = 12-6 = 6 - * - * v2 = 12; o2 = 17-12 = 5 - * - * v3 = 17; o3 = 22-17 = 5 - * - * v4 = 22; o4 = 33-22 = 11 - * - * v5 = 33; (o5 = 0) - */ - private final int[] DEFAULT_VI = {6, 12, 17, 22, 33}; - - private int[] vi;// set of vinegar vars per layer. - - /** - * Default Constructor The elements of the array containing the number of - * Vinegar variables in each layer are set to the default values here. - */ - public RainbowParameters() - { - this.vi = this.DEFAULT_VI; - } - - /** - * Constructor with parameters - * - * @param vi The elements of the array containing the number of Vinegar - * variables per layer are set to the values of the input array. - */ - public RainbowParameters(int[] vi) - { - this.vi = vi; - - checkParams(); - } - - private void checkParams() - { - if (vi == null) - { - throw new IllegalArgumentException("no layers defined."); - } - if (vi.length > 1) - { - for (int i = 0; i < vi.length - 1; i++) - { - if (vi[i] >= vi[i + 1]) - { - throw new IllegalArgumentException( - "v[i] has to be smaller than v[i+1]"); - } - } - } - else - { - throw new IllegalArgumentException( - "Rainbow needs at least 1 layer, such that v1 < v2."); - } - } - - /** - * Getter for the number of layers - * - * @return the number of layers - */ - public int getNumOfLayers() - { - return this.vi.length - 1; - } - - /** - * Getter for the number of all the polynomials in Rainbow - * - * @return the number of the polynomials - */ - public int getDocLength() - { - return vi[vi.length - 1] - vi[0]; - } - - /** - * Getter for the array containing the number of Vinegar-variables per layer - * - * @return the numbers of vinegars per layer - */ - public int[] getVi() - { - return this.vi; - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowPrivateKeyParameters.java deleted file mode 100644 index 90d4b3459f..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowPrivateKeyParameters.java +++ /dev/null @@ -1,117 +0,0 @@ -package org.bouncycastle.pqc.legacy.crypto.rainbow; - -public class RainbowPrivateKeyParameters - extends RainbowKeyParameters -{ - /** - * Constructor - * - * @param A1inv the inverse of A1(the matrix part of the affine linear map L1) - * (n-v1 x n-v1 matrix) - * @param b1 translation vector, part of the linear affine map L1 - * @param A2inv the inverse of A2(the matrix part of the affine linear map L2) - * (n x n matrix) - * @param b2 translation vector, part of the linear affine map L2 - * @param vi the number of Vinegar-variables per layer - * @param layers the polynomials with their coefficients of private map F - */ - public RainbowPrivateKeyParameters(short[][] A1inv, short[] b1, - short[][] A2inv, short[] b2, int[] vi, Layer[] layers) - { - super(true, vi[vi.length - 1] - vi[0]); - - this.A1inv = A1inv; - this.b1 = b1; - this.A2inv = A2inv; - this.b2 = b2; - this.vi = vi; - this.layers = layers; - } - - /* - * invertible affine linear map L1 - */ - // the inverse of A1, (n-v1 x n-v1 matrix) - private short[][] A1inv; - - // translation vector of L1 - private short[] b1; - - /* - * invertible affine linear map L2 - */ - // the inverse of A2, (n x n matrix) - private short[][] A2inv; - - // translation vector of L2 - private short[] b2; - - /* - * components of F - */ - // the number of Vinegar-variables per layer. - private int[] vi; - - // contains the polynomials with their coefficients of private map F - private Layer[] layers; - - /** - * Getter for the translation part of the private quadratic map L1. - * - * @return b1 the translation part of L1 - */ - public short[] getB1() - { - return this.b1; - } - - /** - * Getter for the inverse matrix of A1. - * - * @return the A1inv inverse - */ - public short[][] getInvA1() - { - return this.A1inv; - } - - /** - * Getter for the translation part of the private quadratic map L2. - * - * @return b2 the translation part of L2 - */ - public short[] getB2() - { - return this.b2; - } - - /** - * Getter for the inverse matrix of A2 - * - * @return the A2inv - */ - public short[][] getInvA2() - { - return this.A2inv; - } - - /** - * Returns the layers contained in the private key - * - * @return layers - */ - public Layer[] getLayers() - { - return this.layers; - } - - /** - * /** Returns the array of vi-s - * - * @return the vi - */ - public int[] getVi() - { - return vi; - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowPublicKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowPublicKeyParameters.java deleted file mode 100644 index baff2225f7..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowPublicKeyParameters.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.bouncycastle.pqc.legacy.crypto.rainbow; - -public class RainbowPublicKeyParameters - extends RainbowKeyParameters -{ - private short[][] coeffquadratic; - private short[][] coeffsingular; - private short[] coeffscalar; - - /** - * Constructor - * - * @param docLength - * @param coeffQuadratic - * @param coeffSingular - * @param coeffScalar - */ - public RainbowPublicKeyParameters(int docLength, - short[][] coeffQuadratic, short[][] coeffSingular, - short[] coeffScalar) - { - super(false, docLength); - - this.coeffquadratic = coeffQuadratic; - this.coeffsingular = coeffSingular; - this.coeffscalar = coeffScalar; - - } - - /** - * @return the coeffquadratic - */ - public short[][] getCoeffQuadratic() - { - return coeffquadratic; - } - - /** - * @return the coeffsingular - */ - public short[][] getCoeffSingular() - { - return coeffsingular; - } - - /** - * @return the coeffscalar - */ - public short[] getCoeffScalar() - { - return coeffscalar; - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowSigner.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowSigner.java deleted file mode 100644 index 05e522503b..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/RainbowSigner.java +++ /dev/null @@ -1,311 +0,0 @@ -package org.bouncycastle.pqc.legacy.crypto.rainbow; - -import java.security.SecureRandom; - -import org.bouncycastle.crypto.CipherParameters; -import org.bouncycastle.crypto.CryptoServicesRegistrar; -import org.bouncycastle.crypto.params.ParametersWithRandom; -import org.bouncycastle.pqc.crypto.MessageSigner; -import org.bouncycastle.pqc.legacy.crypto.rainbow.util.ComputeInField; -import org.bouncycastle.pqc.legacy.crypto.rainbow.util.GF2Field; - -/** - * It implements the sign and verify functions for the Rainbow Signature Scheme. - * Here the message, which has to be signed, is updated. The use of - * different hash functions is possible. - *

    - * Detailed information about the signature and the verify-method is to be found - * in the paper of Jintai Ding, Dieter Schmidt: Rainbow, a New Multivariable - * Polynomial Signature Scheme. ACNS 2005: 164-175 - * (https://dx.doi.org/10.1007/11496137_12) - */ -public class RainbowSigner - implements MessageSigner -{ - private static final int MAXITS = 65536; - - // Source of randomness - private SecureRandom random; - - // The length of a document that can be signed with the privKey - int signableDocumentLength; - - // Container for the oil and vinegar variables of all the layers - private short[] x; - - private ComputeInField cf = new ComputeInField(); - - RainbowKeyParameters key; - - public void init(boolean forSigning, - CipherParameters param) - { - if (forSigning) - { - if (param instanceof ParametersWithRandom) - { - ParametersWithRandom rParam = (ParametersWithRandom)param; - - this.random = rParam.getRandom(); - this.key = (RainbowPrivateKeyParameters)rParam.getParameters(); - - } - else - { - - this.random = CryptoServicesRegistrar.getSecureRandom(); - this.key = (RainbowPrivateKeyParameters)param; - } - } - else - { - this.key = (RainbowPublicKeyParameters)param; - } - - this.signableDocumentLength = this.key.getDocLength(); - } - - - /** - * initial operations before solving the Linear equation system. - * - * @param layer the current layer for which a LES is to be solved. - * @param msg the message that should be signed. - * @return Y_ the modified document needed for solving LES, (Y_ = - * A1^{-1}*(Y-b1)) linear map L1 = A1 x + b1. - */ - private short[] initSign(Layer[] layer, short[] msg) - { - - /* preparation: Modifies the document with the inverse of L1 */ - // tmp = Y - b1: - short[] tmpVec = new short[msg.length]; - - tmpVec = cf.addVect(((RainbowPrivateKeyParameters)this.key).getB1(), msg); - - // Y_ = A1^{-1} * (Y - b1) : - short[] Y_ = cf.multiplyMatrix(((RainbowPrivateKeyParameters)this.key).getInvA1(), tmpVec); - - /* generates the vinegar vars of the first layer at random */ - for (int i = 0; i < layer[0].getVi(); i++) - { - x[i] = (short)random.nextInt(); - x[i] = (short)(x[i] & GF2Field.MASK); - } - - return Y_; - } - - /** - * This function signs the message that has been updated, making use of the - * private key. - *

    - * For computing the signature, L1 and L2 are needed, as well as LES should - * be solved for each layer in order to find the Oil-variables in the layer. - *

    - * The Vinegar-variables of the first layer are random generated. - * - * @param message the message - * @return the signature of the message. - */ - public byte[] generateSignature(byte[] message) - { - Layer[] layer = ((RainbowPrivateKeyParameters)this.key).getLayers(); - int numberOfLayers = layer.length; - - x = new short[((RainbowPrivateKeyParameters)this.key).getInvA2().length]; // all variables - - short[] Y_; // modified document - short[] y_i; // part of Y_ each polynomial - int counter; // index of the current part of the doc - - short[] solVec; // the solution of LES pro layer - short[] tmpVec; - - // the signature as an array of shorts: - short[] signature; - // the signature as a byte-array: - byte[] S = new byte[layer[numberOfLayers - 1].getViNext()]; - - short[] msgHashVals = makeMessageRepresentative(message); - int itCount = 0; - - // shows if an exception is caught - boolean ok; - do - { - ok = true; - counter = 0; - try - { - Y_ = initSign(layer, msgHashVals); - - for (int i = 0; i < numberOfLayers; i++) - { - - y_i = new short[layer[i].getOi()]; - solVec = new short[layer[i].getOi()]; // solution of LES - - /* copy oi elements of Y_ into y_i */ - for (int k = 0; k < layer[i].getOi(); k++) - { - y_i[k] = Y_[counter]; - counter++; // current index of Y_ - } - - /* - * plug in the vars of the previous layer in order to get - * the vars of the current layer - */ - solVec = cf.solveEquation(layer[i].plugInVinegars(x), y_i); - - if (solVec == null) - { // LES is not solveable - throw new Exception("LES is not solveable!"); - } - - /* copy the new vars into the x-array */ - for (int j = 0; j < solVec.length; j++) - { - x[layer[i].getVi() + j] = solVec[j]; - } - } - - /* apply the inverse of L2: (signature = A2^{-1}*(b2+x)) */ - tmpVec = cf.addVect(((RainbowPrivateKeyParameters)this.key).getB2(), x); - signature = cf.multiplyMatrix(((RainbowPrivateKeyParameters)this.key).getInvA2(), tmpVec); - - /* cast signature from short[] to byte[] */ - for (int i = 0; i < S.length; i++) - { - S[i] = ((byte)signature[i]); - } - } - catch (Exception se) - { - // if one of the LESs was not solveable - sign again - ok = false; - } - } - while (!ok && ++itCount < MAXITS); - /* return the signature in bytes */ - - if (itCount == MAXITS) - { - throw new IllegalStateException("unable to generate signature - LES not solvable"); - } - - return S; - } - - /** - * This function verifies the signature of the message that has been - * updated, with the aid of the public key. - * - * @param message the message - * @param signature the signature of the message - * @return true if the signature has been verified, false otherwise. - */ - public boolean verifySignature(byte[] message, byte[] signature) - { - short[] sigInt = new short[signature.length]; - short tmp; - - for (int i = 0; i < signature.length; i++) - { - tmp = (short)signature[i]; - tmp &= (short)0xff; - sigInt[i] = tmp; - } - - short[] msgHashVal = makeMessageRepresentative(message); - - // verify - short[] verificationResult = verifySignatureIntern(sigInt); - - // compare - boolean verified = true; - if (msgHashVal.length != verificationResult.length) - { - return false; - } - for (int i = 0; i < msgHashVal.length; i++) - { - verified = verified && msgHashVal[i] == verificationResult[i]; - } - - return verified; - } - - /** - * Signature verification using public key - * - * @param signature vector of dimension n - * @return document hash of length n - v1 - */ - private short[] verifySignatureIntern(short[] signature) - { - - short[][] coeff_quadratic = ((RainbowPublicKeyParameters)this.key).getCoeffQuadratic(); - short[][] coeff_singular = ((RainbowPublicKeyParameters)this.key).getCoeffSingular(); - short[] coeff_scalar = ((RainbowPublicKeyParameters)this.key).getCoeffScalar(); - - short[] rslt = new short[coeff_quadratic.length];// n - v1 - int n = coeff_singular[0].length; - int offset = 0; // array position - short tmp = 0; // for scalar - - for (int p = 0; p < coeff_quadratic.length; p++) - { // no of polynomials - offset = 0; - for (int x = 0; x < n; x++) - { - // calculate quadratic terms - for (int y = x; y < n; y++) - { - tmp = GF2Field.multElem(coeff_quadratic[p][offset], - GF2Field.multElem(signature[x], signature[y])); - rslt[p] = GF2Field.addElem(rslt[p], tmp); - offset++; - } - // calculate singular terms - tmp = GF2Field.multElem(coeff_singular[p][x], signature[x]); - rslt[p] = GF2Field.addElem(rslt[p], tmp); - } - // add scalar - rslt[p] = GF2Field.addElem(rslt[p], coeff_scalar[p]); - } - - return rslt; - } - - /** - * This function creates the representative of the message which gets signed - * or verified. - * - * @param message the message - * @return message representative - */ - private short[] makeMessageRepresentative(byte[] message) - { - // the message representative - short[] output = new short[this.signableDocumentLength]; - - int h = 0; - int i = 0; - do - { - if (i >= message.length) - { - break; - } - output[i] = (short)message[h]; - output[i] &= (short)0xff; - h++; - i++; - } - while (i < output.length); - - return output; - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/util/ComputeInField.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/util/ComputeInField.java deleted file mode 100644 index 181a6c4967..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/util/ComputeInField.java +++ /dev/null @@ -1,493 +0,0 @@ -package org.bouncycastle.pqc.legacy.crypto.rainbow.util; - -/** - * This class offers different operations on matrices in field GF2^8. - *

    - * Implemented are functions: - * - finding inverse of a matrix - * - solving linear equation systems using the Gauss-Elimination method - * - basic operations like matrix multiplication, addition and so on. - */ - -public class ComputeInField -{ - - private short[][] A; // used by solveEquation and inverse - short[] x; - - /** - * Constructor with no parameters - */ - public ComputeInField() - { - } - - - /** - * This function finds a solution of the equation Bx = b. - * Exception is thrown if the linear equation system has no solution - * - * @param B this matrix is the left part of the - * equation (B in the equation above) - * @param b the right part of the equation - * (b in the equation above) - * @return x the solution of the equation if it is solvable - * null otherwise - * @throws RuntimeException if LES is not solvable - */ - public short[] solveEquation(short[][] B, short[] b) - { - if (B.length != b.length) - { - return null; // not solvable in this form - } - - try - { - - - // initialize - // this matrix stores B and b from the equation B*x = b - // b is stored as the last column. - // B contains one column more than rows. - // In this column we store a free coefficient that should be later subtracted from b - A = new short[B.length][B.length + 1]; - // stores the solution of the LES - x = new short[B.length]; - - // copy B into the global matrix A - for (int i = 0; i < B.length; i++) - { // rows - for (int j = 0; j < B[0].length; j++) - { // cols - A[i][j] = B[i][j]; - } - } - - // copy the vector b into the global A - //the free coefficient, stored in the last column of A( A[i][b.length] - // is to be subtracted from b - for (int i = 0; i < b.length; i++) - { - A[i][b.length] = GF2Field.addElem(b[i], A[i][b.length]); - } - - // call the methods for gauss elimination and backward substitution - computeZerosUnder(false); // obtain zeros under the diagonal - substitute(); - - return x; - - } - catch (RuntimeException rte) - { - return null; // the LES is not solvable! - } - } - - /** - * This function computes the inverse of a given matrix using the Gauss- - * Elimination method. - *

    - * An exception is thrown if the matrix has no inverse - * - * @param coef the matrix which inverse matrix is needed - * @return inverse matrix of the input matrix. - * If the matrix is singular, null is returned. - * @throws RuntimeException if the given matrix is not invertible - */ - public short[][] inverse(short[][] coef) - { - try - { - // Initialization: - short factor; - short[][] inverse; - A = new short[coef.length][2 * coef.length]; - if (coef.length != coef[0].length) - { - throw new RuntimeException( - "The matrix is not invertible. Please choose another one!"); - } - - // prepare: Copy coef and the identity matrix into the global A. - for (int i = 0; i < coef.length; i++) - { - for (int j = 0; j < coef.length; j++) - { - //copy the input matrix coef into A - A[i][j] = coef[i][j]; - } - // copy the identity matrix into A. - for (int j = coef.length; j < 2 * coef.length; j++) - { - A[i][j] = 0; - } - A[i][i + A.length] = 1; - } - - // Elimination operations to get the identity matrix from the left side of A. - // modify A to get 0s under the diagonal. - computeZerosUnder(true); - - // modify A to get only 1s on the diagonal: A[i][j] =A[i][j]/A[i][i]. - for (int i = 0; i < A.length; i++) - { - factor = GF2Field.invElem(A[i][i]); - for (int j = i; j < 2 * A.length; j++) - { - A[i][j] = GF2Field.multElem(A[i][j], factor); - } - } - - //modify A to get only 0s above the diagonal. - computeZerosAbove(); - - // copy the result (the second half of A) in the matrix inverse. - inverse = new short[A.length][A.length]; - for (int i = 0; i < A.length; i++) - { - for (int j = A.length; j < 2 * A.length; j++) - { - inverse[i][j - A.length] = A[i][j]; - } - } - return inverse; - - } - catch (RuntimeException rte) - { - // The matrix is not invertible! A new one should be generated! - return null; - } - } - - /** - * Elimination under the diagonal. - * This function changes a matrix so that it contains only zeros under the - * diagonal(Ai,i) using only Gauss-Elimination operations. - *

    - * It is used in solveEquaton as well as in the function for - * finding an inverse of a matrix: {@link}inverse. Both of them use the - * Gauss-Elimination Method. - *

    - * The result is stored in the global matrix A - *

    - * - * @param usedForInverse This parameter shows if the function is used by the - * solveEquation-function or by the inverse-function and according - * to this creates matrices of different sizes. - * @throws RuntimeException in case a multiplicative inverse of 0 is needed - */ - private void computeZerosUnder(boolean usedForInverse) - throws RuntimeException - { - - //the number of columns in the global A where the tmp results are stored - int length; - short tmp = 0; - - //the function is used in inverse() - A should have 2 times more columns than rows - if (usedForInverse) - { - length = 2 * A.length; - } - //the function is used in solveEquation - A has 1 column more than rows - else - { - length = A.length + 1; - } - - //elimination operations to modify A so that that it contains only 0s under the diagonal - for (int k = 0; k < A.length - 1; k++) - { // the fixed row - for (int i = k + 1; i < A.length; i++) - { // rows - short factor1 = A[i][k]; - short factor2 = GF2Field.invElem(A[k][k]); - - //The element which multiplicative inverse is needed, is 0 - //in this case is the input matrix not invertible - if (factor2 == 0) - { - throw new IllegalStateException("Matrix not invertible! We have to choose another one!"); - } - - for (int j = k; j < length; j++) - {// columns - // tmp=A[k,j] / A[k,k] - tmp = GF2Field.multElem(A[k][j], factor2); - // tmp = A[i,k] * A[k,j] / A[k,k] - tmp = GF2Field.multElem(factor1, tmp); - // A[i,j]=A[i,j]-A[i,k]/A[k,k]*A[k,j]; - A[i][j] = GF2Field.addElem(A[i][j], tmp); - } - } - } - } - - /** - * Elimination above the diagonal. - * This function changes a matrix so that it contains only zeros above the - * diagonal(Ai,i) using only Gauss-Elimination operations. - *

    - * It is used in the inverse-function - * The result is stored in the global matrix A - *

    - * - * @throws RuntimeException in case a multiplicative inverse of 0 is needed - */ - private void computeZerosAbove() - throws RuntimeException - { - short tmp = 0; - for (int k = A.length - 1; k > 0; k--) - { // the fixed row - for (int i = k - 1; i >= 0; i--) - { // rows - short factor1 = A[i][k]; - short factor2 = GF2Field.invElem(A[k][k]); - if (factor2 == 0) - { - throw new RuntimeException("The matrix is not invertible"); - } - for (int j = k; j < 2 * A.length; j++) - { // columns - // tmp = A[k,j] / A[k,k] - tmp = GF2Field.multElem(A[k][j], factor2); - // tmp = A[i,k] * A[k,j] / A[k,k] - tmp = GF2Field.multElem(factor1, tmp); - // A[i,j] = A[i,j] - A[i,k] / A[k,k] * A[k,j]; - A[i][j] = GF2Field.addElem(A[i][j], tmp); - } - } - } - } - - - /** - * This function uses backward substitution to find x - * of the linear equation system (LES) B*x = b, - * where A a triangle-matrix is (contains only zeros under the diagonal) - * and b is a vector - *

    - * If the multiplicative inverse of 0 is needed, an exception is thrown. - * In this case is the LES not solvable - *

    - * - * @throws RuntimeException in case a multiplicative inverse of 0 is needed - */ - private void substitute() - throws IllegalStateException - { - - // for the temporary results of the operations in field - short tmp, temp; - - temp = GF2Field.invElem(A[A.length - 1][A.length - 1]); - if (temp == 0) - { - throw new IllegalStateException("The equation system is not solvable"); - } - - // backward substitution - x[A.length - 1] = GF2Field.multElem(A[A.length - 1][A.length], temp); - for (int i = A.length - 2; i >= 0; i--) - { - tmp = A[i][A.length]; - for (int j = A.length - 1; j > i; j--) - { - temp = GF2Field.multElem(A[i][j], x[j]); - tmp = GF2Field.addElem(tmp, temp); - } - - temp = GF2Field.invElem(A[i][i]); - if (temp == 0) - { - throw new IllegalStateException("Not solvable equation system"); - } - x[i] = GF2Field.multElem(tmp, temp); - } - } - - - /** - * This function multiplies two given matrices. - * If the given matrices cannot be multiplied due - * to different sizes, an exception is thrown. - * - * @param M1 -the 1st matrix - * @param M2 -the 2nd matrix - * @return A = M1*M2 - * @throws RuntimeException in case the given matrices cannot be multiplied - * due to different dimensions. - */ - public short[][] multiplyMatrix(short[][] M1, short[][] M2) - throws RuntimeException - { - - if (M1[0].length != M2.length) - { - throw new RuntimeException("Multiplication is not possible!"); - } - short tmp = 0; - A = new short[M1.length][M2[0].length]; - for (int i = 0; i < M1.length; i++) - { - for (int j = 0; j < M2.length; j++) - { - for (int k = 0; k < M2[0].length; k++) - { - tmp = GF2Field.multElem(M1[i][j], M2[j][k]); - A[i][k] = GF2Field.addElem(A[i][k], tmp); - } - } - } - return A; - } - - /** - * This function multiplies a given matrix with a one-dimensional array. - *

    - * An exception is thrown, if the number of columns in the matrix and - * the number of rows in the one-dim. array differ. - * - * @param M1 the matrix to be multiplied - * @param m the one-dimensional array to be multiplied - * @return M1*m - * @throws RuntimeException in case of dimension inconsistency - */ - public short[] multiplyMatrix(short[][] M1, short[] m) - throws RuntimeException - { - if (M1[0].length != m.length) - { - throw new RuntimeException("Multiplication is not possible!"); - } - short tmp = 0; - short[] B = new short[M1.length]; - for (int i = 0; i < M1.length; i++) - { - for (int j = 0; j < m.length; j++) - { - tmp = GF2Field.multElem(M1[i][j], m[j]); - B[i] = GF2Field.addElem(B[i], tmp); - } - } - return B; - } - - /** - * Addition of two vectors - * - * @param vector1 first summand, always of dim n - * @param vector2 second summand, always of dim n - * @return addition of vector1 and vector2 - * @throws RuntimeException in case the addition is impossible - * due to inconsistency in the dimensions - */ - public short[] addVect(short[] vector1, short[] vector2) - { - if (vector1.length != vector2.length) - { - throw new RuntimeException("Multiplication is not possible!"); - } - short rslt[] = new short[vector1.length]; - for (int n = 0; n < rslt.length; n++) - { - rslt[n] = GF2Field.addElem(vector1[n], vector2[n]); - } - return rslt; - } - - /** - * Multiplication of column vector with row vector - * - * @param vector1 column vector, always n x 1 - * @param vector2 row vector, always 1 x n - * @return resulting n x n matrix of multiplication - * @throws RuntimeException in case the multiplication is impossible due to - * inconsistency in the dimensions - */ - public short[][] multVects(short[] vector1, short[] vector2) - { - if (vector1.length != vector2.length) - { - throw new RuntimeException("Multiplication is not possible!"); - } - short rslt[][] = new short[vector1.length][vector2.length]; - for (int i = 0; i < vector1.length; i++) - { - for (int j = 0; j < vector2.length; j++) - { - rslt[i][j] = GF2Field.multElem(vector1[i], vector2[j]); - } - } - return rslt; - } - - /** - * Multiplies vector with scalar - * - * @param scalar galois element to multiply vector with - * @param vector vector to be multiplied - * @return vector multiplied with scalar - */ - public short[] multVect(short scalar, short[] vector) - { - short rslt[] = new short[vector.length]; - for (int n = 0; n < rslt.length; n++) - { - rslt[n] = GF2Field.multElem(scalar, vector[n]); - } - return rslt; - } - - /** - * Multiplies matrix with scalar - * - * @param scalar galois element to multiply matrix with - * @param matrix 2-dim n x n matrix to be multiplied - * @return matrix multiplied with scalar - */ - public short[][] multMatrix(short scalar, short[][] matrix) - { - short[][] rslt = new short[matrix.length][matrix[0].length]; - for (int i = 0; i < matrix.length; i++) - { - for (int j = 0; j < matrix[0].length; j++) - { - rslt[i][j] = GF2Field.multElem(scalar, matrix[i][j]); - } - } - return rslt; - } - - /** - * Adds the n x n matrices matrix1 and matrix2 - * - * @param matrix1 first summand - * @param matrix2 second summand - * @return addition of matrix1 and matrix2; both having the dimensions n x n - * @throws RuntimeException in case the addition is not possible because of - * different dimensions of the matrices - */ - public short[][] addSquareMatrix(short[][] matrix1, short[][] matrix2) - { - if (matrix1.length != matrix2.length || matrix1[0].length != matrix2[0].length) - { - throw new RuntimeException("Addition is not possible!"); - } - - short[][] rslt = new short[matrix1.length][matrix1.length];// - for (int i = 0; i < matrix1.length; i++) - { - for (int j = 0; j < matrix2.length; j++) - { - rslt[i][j] = GF2Field.addElem(matrix1[i][j], matrix2[i][j]); - } - } - return rslt; - } - -} diff --git a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/util/GF2Field.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/util/GF2Field.java deleted file mode 100644 index 2016cb7fc6..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/util/GF2Field.java +++ /dev/null @@ -1,139 +0,0 @@ -package org.bouncycastle.pqc.legacy.crypto.rainbow.util; - -/** - * This class provides the basic operations like addition, multiplication and - * finding the multiplicative inverse of an element in GF2^8. - *

    - * The operations are implemented using the irreducible polynomial - * 1+x^2+x^3+x^6+x^8 ( 1 0100 1101 = 0x14d ) - *

    - * This class makes use of lookup tables(exps and logs) for implementing the - * operations in order to increase the efficiency of Rainbow. - */ -public class GF2Field -{ - - public static final int MASK = 0xff; - - /* - * this lookup table is needed for multiplication and computing the - * multiplicative inverse - */ - static final short exps[] = {1, 2, 4, 8, 16, 32, 64, 128, 77, 154, 121, 242, - 169, 31, 62, 124, 248, 189, 55, 110, 220, 245, 167, 3, 6, 12, 24, - 48, 96, 192, 205, 215, 227, 139, 91, 182, 33, 66, 132, 69, 138, 89, - 178, 41, 82, 164, 5, 10, 20, 40, 80, 160, 13, 26, 52, 104, 208, - 237, 151, 99, 198, 193, 207, 211, 235, 155, 123, 246, 161, 15, 30, - 60, 120, 240, 173, 23, 46, 92, 184, 61, 122, 244, 165, 7, 14, 28, - 56, 112, 224, 141, 87, 174, 17, 34, 68, 136, 93, 186, 57, 114, 228, - 133, 71, 142, 81, 162, 9, 18, 36, 72, 144, 109, 218, 249, 191, 51, - 102, 204, 213, 231, 131, 75, 150, 97, 194, 201, 223, 243, 171, 27, - 54, 108, 216, 253, 183, 35, 70, 140, 85, 170, 25, 50, 100, 200, - 221, 247, 163, 11, 22, 44, 88, 176, 45, 90, 180, 37, 74, 148, 101, - 202, 217, 255, 179, 43, 86, 172, 21, 42, 84, 168, 29, 58, 116, 232, - 157, 119, 238, 145, 111, 222, 241, 175, 19, 38, 76, 152, 125, 250, - 185, 63, 126, 252, 181, 39, 78, 156, 117, 234, 153, 127, 254, 177, - 47, 94, 188, 53, 106, 212, 229, 135, 67, 134, 65, 130, 73, 146, - 105, 210, 233, 159, 115, 230, 129, 79, 158, 113, 226, 137, 95, 190, - 49, 98, 196, 197, 199, 195, 203, 219, 251, 187, 59, 118, 236, 149, - 103, 206, 209, 239, 147, 107, 214, 225, 143, 83, 166, 1}; - - /* - * this lookup table is needed for multiplication and computing the - * multiplicative inverse - */ - static final short logs[] = {0, 0, 1, 23, 2, 46, 24, 83, 3, 106, 47, 147, - 25, 52, 84, 69, 4, 92, 107, 182, 48, 166, 148, 75, 26, 140, 53, - 129, 85, 170, 70, 13, 5, 36, 93, 135, 108, 155, 183, 193, 49, 43, - 167, 163, 149, 152, 76, 202, 27, 230, 141, 115, 54, 205, 130, 18, - 86, 98, 171, 240, 71, 79, 14, 189, 6, 212, 37, 210, 94, 39, 136, - 102, 109, 214, 156, 121, 184, 8, 194, 223, 50, 104, 44, 253, 168, - 138, 164, 90, 150, 41, 153, 34, 77, 96, 203, 228, 28, 123, 231, 59, - 142, 158, 116, 244, 55, 216, 206, 249, 131, 111, 19, 178, 87, 225, - 99, 220, 172, 196, 241, 175, 72, 10, 80, 66, 15, 186, 190, 199, 7, - 222, 213, 120, 38, 101, 211, 209, 95, 227, 40, 33, 137, 89, 103, - 252, 110, 177, 215, 248, 157, 243, 122, 58, 185, 198, 9, 65, 195, - 174, 224, 219, 51, 68, 105, 146, 45, 82, 254, 22, 169, 12, 139, - 128, 165, 74, 91, 181, 151, 201, 42, 162, 154, 192, 35, 134, 78, - 188, 97, 239, 204, 17, 229, 114, 29, 61, 124, 235, 232, 233, 60, - 234, 143, 125, 159, 236, 117, 30, 245, 62, 56, 246, 217, 63, 207, - 118, 250, 31, 132, 160, 112, 237, 20, 144, 179, 126, 88, 251, 226, - 32, 100, 208, 221, 119, 173, 218, 197, 64, 242, 57, 176, 247, 73, - 180, 11, 127, 81, 21, 67, 145, 16, 113, 187, 238, 191, 133, 200, - 161}; - - /** - * This function calculates the sum of two elements as an operation in GF2^8 - * - * @param x the first element that is to be added - * @param y the second element that should be add - * @return the sum of the two elements x and y in GF2^8 - */ - public static short addElem(short x, short y) - { - return (short)(x ^ y); - } - - /** - * This function computes the multiplicative inverse of a given element in - * GF2^8 The 0 has no multiplicative inverse and in this case 0 is returned. - * - * @param x the element which multiplicative inverse is to be computed - * @return the multiplicative inverse of the given element, in case it - * exists or 0, otherwise - */ - public static short invElem(short x) - { - if (x == 0) - { - return 0; - } - return (exps[255 - logs[x]]); - } - - /** - * This function multiplies two elements in GF2^8. If one of the two - * elements is 0, 0 is returned. - * - * @param x the first element to be multiplied. - * @param y the second element to be multiplied. - * @return the product of the two input elements in GF2^8. - */ - public static short multElem(short x, short y) - { - if (x == 0 || y == 0) - { - return 0; - } - else - { - return (exps[(logs[x] + logs[y]) % 255]); - } - } - - /** - * This function returns the values of exps-lookup table which correspond to - * the input - * - * @param x the index in the lookup table exps - * @return exps-value, corresponding to the input - */ - public static short getExp(short x) - { - return exps[x]; - } - - /** - * This function returns the values of logs-lookup table which correspond to - * the input - * - * @param x the index in the lookup table logs - * @return logs-value, corresponding to the input - */ - public static short getLog(short x) - { - return logs[x]; - } - - -} diff --git a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/util/RainbowUtil.java b/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/util/RainbowUtil.java deleted file mode 100644 index da65aa6301..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/legacy/crypto/rainbow/util/RainbowUtil.java +++ /dev/null @@ -1,230 +0,0 @@ -package org.bouncycastle.pqc.legacy.crypto.rainbow.util; - -/** - * This class is needed for the conversions while encoding and decoding, as well as for - * comparison between arrays of some dimensions - */ -public class RainbowUtil -{ - - /** - * This function converts an one-dimensional array of bytes into a - * one-dimensional array of int - * - * @param in the array to be converted - * @return out - * the one-dimensional int-array that corresponds the input - */ - public static int[] convertArraytoInt(byte[] in) - { - int[] out = new int[in.length]; - for (int i = 0; i < in.length; i++) - { - out[i] = in[i] & GF2Field.MASK; - } - return out; - } - - /** - * This function converts an one-dimensional array of bytes into a - * one-dimensional array of type short - * - * @param in the array to be converted - * @return out - * one-dimensional short-array that corresponds the input - */ - public static short[] convertArray(byte[] in) - { - short[] out = new short[in.length]; - for (int i = 0; i < in.length; i++) - { - out[i] = (short)(in[i] & GF2Field.MASK); - } - return out; - } - - /** - * This function converts a matrix of bytes into a matrix of type short - * - * @param in the matrix to be converted - * @return out - * short-matrix that corresponds the input - */ - public static short[][] convertArray(byte[][] in) - { - short[][] out = new short[in.length][in[0].length]; - for (int i = 0; i < in.length; i++) - { - for (int j = 0; j < in[0].length; j++) - { - out[i][j] = (short)(in[i][j] & GF2Field.MASK); - } - } - return out; - } - - /** - * This function converts a 3-dimensional array of bytes into a 3-dimensional array of type short - * - * @param in the array to be converted - * @return out - * short-array that corresponds the input - */ - public static short[][][] convertArray(byte[][][] in) - { - short[][][] out = new short[in.length][in[0].length][in[0][0].length]; - for (int i = 0; i < in.length; i++) - { - for (int j = 0; j < in[0].length; j++) - { - for (int k = 0; k < in[0][0].length; k++) - { - out[i][j][k] = (short)(in[i][j][k] & GF2Field.MASK); - } - } - } - return out; - } - - /** - * This function converts an array of type int into an array of type byte - * - * @param in the array to be converted - * @return out - * the byte-array that corresponds the input - */ - public static byte[] convertIntArray(int[] in) - { - byte[] out = new byte[in.length]; - for (int i = 0; i < in.length; i++) - { - out[i] = (byte)in[i]; - } - return out; - } - - - /** - * This function converts an array of type short into an array of type byte - * - * @param in the array to be converted - * @return out - * the byte-array that corresponds the input - */ - public static byte[] convertArray(short[] in) - { - byte[] out = new byte[in.length]; - for (int i = 0; i < in.length; i++) - { - out[i] = (byte)in[i]; - } - return out; - } - - /** - * This function converts a matrix of type short into a matrix of type byte - * - * @param in the matrix to be converted - * @return out - * the byte-matrix that corresponds the input - */ - public static byte[][] convertArray(short[][] in) - { - byte[][] out = new byte[in.length][in[0].length]; - for (int i = 0; i < in.length; i++) - { - for (int j = 0; j < in[0].length; j++) - { - out[i][j] = (byte)in[i][j]; - } - } - return out; - } - - /** - * This function converts a 3-dimensional array of type short into a 3-dimensional array of type byte - * - * @param in the array to be converted - * @return out - * the byte-array that corresponds the input - */ - public static byte[][][] convertArray(short[][][] in) - { - byte[][][] out = new byte[in.length][in[0].length][in[0][0].length]; - for (int i = 0; i < in.length; i++) - { - for (int j = 0; j < in[0].length; j++) - { - for (int k = 0; k < in[0][0].length; k++) - { - out[i][j][k] = (byte)in[i][j][k]; - } - } - } - return out; - } - - /** - * Compare two short arrays. No null checks are performed. - * - * @param left the first short array - * @param right the second short array - * @return the result of the comparison - */ - public static boolean equals(short[] left, short[] right) - { - if (left.length != right.length) - { - return false; - } - boolean result = true; - for (int i = left.length - 1; i >= 0; i--) - { - result &= left[i] == right[i]; - } - return result; - } - - /** - * Compare two two-dimensional short arrays. No null checks are performed. - * - * @param left the first short array - * @param right the second short array - * @return the result of the comparison - */ - public static boolean equals(short[][] left, short[][] right) - { - if (left.length != right.length) - { - return false; - } - boolean result = true; - for (int i = left.length - 1; i >= 0; i--) - { - result &= equals(left[i], right[i]); - } - return result; - } - - /** - * Compare two three-dimensional short arrays. No null checks are performed. - * - * @param left the first short array - * @param right the second short array - * @return the result of the comparison - */ - public static boolean equals(short[][][] left, short[][][] right) - { - if (left.length != right.length) - { - return false; - } - boolean result = true; - for (int i = left.length - 1; i >= 0; i--) - { - result &= equals(left[i], right[i]); - } - return result; - } - -} diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/AllTests.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/AllTests.java index 5f0f99a177..221427954b 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/AllTests.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/AllTests.java @@ -4,6 +4,7 @@ import junit.framework.Test; import junit.framework.TestCase; import junit.framework.TestSuite; + import org.bouncycastle.test.PrintTestResult; import org.bouncycastle.util.test.SimpleTestResult; @@ -46,8 +47,6 @@ public static Test suite() suite.addTestSuite(SNTRUPrimeTest.class); suite.addTestSuite(BIKETest.class); suite.addTestSuite(HQCTest.class); - suite.addTestSuite(RainbowVectorTest.class); - suite.addTestSuite(GeMSSTest.class); suite.addTestSuite(XWingTest.class); suite.addTestSuite(AllTests.SimpleTestTest.class); suite.addTestSuite(SLHDSATest.class); diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/RainbowTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/RainbowTest.java deleted file mode 100644 index e3c6458c8f..0000000000 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/RainbowTest.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.bouncycastle.pqc.crypto.test; - -import java.math.BigInteger; -import java.security.SecureRandom; - -import org.bouncycastle.crypto.AsymmetricCipherKeyPair; -import org.bouncycastle.crypto.params.ParametersWithRandom; -import org.bouncycastle.pqc.crypto.MessageSigner; -import org.bouncycastle.pqc.crypto.rainbow.RainbowKeyGenerationParameters; -import org.bouncycastle.pqc.crypto.rainbow.RainbowKeyPairGenerator; -import org.bouncycastle.pqc.crypto.rainbow.RainbowParameters; -import org.bouncycastle.pqc.crypto.rainbow.RainbowSigner; -import org.bouncycastle.util.BigIntegers; -import org.bouncycastle.util.test.SimpleTest; - -public class RainbowTest - extends SimpleTest -{ - private RainbowParameters params; - - public RainbowTest(RainbowParameters params) - { - this.params = params; - } - - public String getName() - { - return params.getName(); - } - - public void performTest() - { - byte[] seed = new byte[64]; - SecureRandom sr = new SecureRandom(); - sr.nextBytes(seed); - NISTSecureRandom random = new NISTSecureRandom(seed, null); - - RainbowKeyPairGenerator rainbowKeyGen = new RainbowKeyPairGenerator(); - RainbowKeyGenerationParameters genParam = new RainbowKeyGenerationParameters(random, params); - - rainbowKeyGen.init(genParam); - - AsymmetricCipherKeyPair pair = rainbowKeyGen.generateKeyPair(); - - ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), random); - - MessageSigner rainbowSigner = new RainbowSigner(); - - rainbowSigner.init(true, param); - - byte[] message = BigIntegers.asUnsignedByteArray(new BigInteger("968236873715988614170569073515315707566766479517")); - - byte[] sig = rainbowSigner.generateSignature(message); - - rainbowSigner.init(false, pair.getPublic()); - - if (!rainbowSigner.verifySignature(message, sig)) - { - fail("verification fails"); - } - } - - public static void main(String[] args) - { - runTest(new RainbowTest(RainbowParameters.rainbowIIIclassic)); - runTest(new RainbowTest(RainbowParameters.rainbowIIIcircumzenithal)); - runTest(new RainbowTest(RainbowParameters.rainbowIIIcompressed)); - runTest(new RainbowTest(RainbowParameters.rainbowVclassic)); - runTest(new RainbowTest(RainbowParameters.rainbowVcircumzenithal)); - runTest(new RainbowTest(RainbowParameters.rainbowVcompressed)); - } -} diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/RainbowVectorTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/RainbowVectorTest.java deleted file mode 100644 index 34cf596015..0000000000 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/RainbowVectorTest.java +++ /dev/null @@ -1,143 +0,0 @@ -package org.bouncycastle.pqc.crypto.test; - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.HashMap; -import java.util.Random; - -import junit.framework.TestCase; -import org.bouncycastle.crypto.AsymmetricCipherKeyPair; -import org.bouncycastle.crypto.params.ParametersWithRandom; -import org.bouncycastle.pqc.crypto.MessageSigner; -import org.bouncycastle.pqc.crypto.rainbow.RainbowKeyGenerationParameters; -import org.bouncycastle.pqc.crypto.rainbow.RainbowKeyPairGenerator; -import org.bouncycastle.pqc.crypto.rainbow.RainbowParameters; -import org.bouncycastle.pqc.crypto.rainbow.RainbowPrivateKeyParameters; -import org.bouncycastle.pqc.crypto.rainbow.RainbowPublicKeyParameters; -import org.bouncycastle.pqc.crypto.rainbow.RainbowSigner; -import org.bouncycastle.test.TestResourceFinder; -import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.encoders.Hex; - -public class RainbowVectorTest - extends TestCase -{ - public void testVectors() - throws Exception - { - String[] files = new String[]{ - "rainbowIIIclassic.rsp", - "rainbowIIIcircumzenithal.rsp", - "rainbowIIIcompressed.rsp", - "rainbowVclassic.rsp", - "rainbowVcircumzenithal.rsp", - "rainbowVcompressed.rsp" - }; - RainbowParameters[] params = new RainbowParameters[]{ - RainbowParameters.rainbowIIIclassic, - RainbowParameters.rainbowIIIcircumzenithal, - RainbowParameters.rainbowIIIcompressed, - RainbowParameters.rainbowVclassic, - RainbowParameters.rainbowVcircumzenithal, - RainbowParameters.rainbowVcompressed - }; - - TestSampler sampler = new TestSampler(); - Random rd = new Random(System.currentTimeMillis()); - - int offSet = rd.nextInt(10); - - for (int fileIndex = 0; fileIndex != files.length; fileIndex++) - { - String name = files[fileIndex]; - // System.out.println("testing: " + name); - InputStream src = TestResourceFinder.findTestResource("pqc/crypto/rainbow", name); - BufferedReader bin = new BufferedReader(new InputStreamReader(src)); - - String line = null; - HashMap buf = new HashMap(); - while ((line = bin.readLine()) != null) - { - line = line.trim(); - - if (line.startsWith("#")) - { - continue; - } - if (line.length() == 0) - { - if (buf.size() > 0) - { - String count = (String)buf.get("count"); -// if (sampler.skipTest(count)) -// { -// continue; -// } - - if (Integer.parseInt(count) != offSet) - { - continue; - } - // System.out.println("test case: " + count); - byte[] seed = Hex.decode((String)buf.get("seed")); // seed for Rainbow secure random - int mlen = Integer.parseInt((String)buf.get("mlen")); // message length - byte[] msg = Hex.decode((String)buf.get("msg")); // message - byte[] pk = Hex.decode((String)buf.get("pk")); // public key - byte[] sk = Hex.decode((String)buf.get("sk")); // private key - int smlen = Integer.parseInt((String)buf.get("smlen")); // signature length - byte[] sigExpected = Hex.decode((String)buf.get("sm")); // signature - - NISTSecureRandom random = new NISTSecureRandom(seed, null); - - RainbowParameters parameters = params[fileIndex]; - - RainbowKeyPairGenerator kpGen = new RainbowKeyPairGenerator(); - RainbowKeyGenerationParameters genParams = new RainbowKeyGenerationParameters(random, parameters); - // - // Generate keys and test. - // - kpGen.init(genParams); - AsymmetricCipherKeyPair kp = kpGen.generateKeyPair(); - - RainbowPublicKeyParameters pubParams = (RainbowPublicKeyParameters)kp.getPublic(); - RainbowPrivateKeyParameters privParams = (RainbowPrivateKeyParameters)kp.getPrivate(); - assertTrue(name + " " + count + ": public key", Arrays.areEqual(pk, pubParams.getEncoded())); - assertTrue(name + " " + count + ": secret key", Arrays.areEqual(sk, privParams.getPrivateKey())); - - // - // Signature test - // - ParametersWithRandom param = new ParametersWithRandom(kp.getPrivate(), random); - MessageSigner signer = new RainbowSigner(); - - signer.init(true, param); - - byte[] sigGenerated = signer.generateSignature(msg); - byte[] attachedSig = Arrays.concatenate(msg, sigGenerated); - - //// System.out.println("expected:\t" + Hex.toHexString(sigExpected).toUpperCase().substring(msg.length*2, sigExpected.length*2)); - //// System.out.println("generated:\t" + Hex.toHexString(sigGenerated).toUpperCase()); - //// System.out.println("attached:\t" + Hex.toHexString(attachedSig).toUpperCase()); - - signer.init(false, kp.getPublic()); - - assertTrue(name + " " + count + ": signature verify", signer.verifySignature(msg, sigGenerated)); - assertTrue(name + " " + count + ": signature gen match", Arrays.areEqual(sigExpected, attachedSig)); - } - buf.clear(); - - continue; - } - - int a = line.indexOf("="); - if (a > -1) - { - buf.put(line.substring(0, a).trim(), line.substring(a + 1).trim()); - } - } - // System.out.println("testing successful!"); - } - } - -} diff --git a/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/AllTests.java b/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/AllTests.java index f85072288a..7d2bdd4ddf 100644 --- a/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/AllTests.java +++ b/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/AllTests.java @@ -29,6 +29,7 @@ public static Test suite() suite.addTestSuite(NTRUSignerTest.class); suite.addTestSuite(NTRUSigningParametersTest.class); suite.addTestSuite(QTESLATest.class); + suite.addTestSuite(GeMSSTest.class); suite.addTestSuite(SimpleTestTest.class); return new BCTestSetup(suite); diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/GeMSSTest.java b/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/GeMSSTest.java similarity index 93% rename from core/src/test/java/org/bouncycastle/pqc/crypto/test/GeMSSTest.java rename to core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/GeMSSTest.java index ad405dca88..bbaa20111a 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/GeMSSTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/GeMSSTest.java @@ -1,4 +1,4 @@ -package org.bouncycastle.pqc.crypto.test; +package org.bouncycastle.pqc.legacy.crypto.test; import java.io.BufferedReader; import java.io.InputStream; @@ -12,12 +12,13 @@ import junit.framework.TestCase; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.params.ParametersWithRandom; -import org.bouncycastle.pqc.crypto.gemss.GeMSSKeyGenerationParameters; -import org.bouncycastle.pqc.crypto.gemss.GeMSSKeyPairGenerator; -import org.bouncycastle.pqc.crypto.gemss.GeMSSParameters; -import org.bouncycastle.pqc.crypto.gemss.GeMSSPrivateKeyParameters; -import org.bouncycastle.pqc.crypto.gemss.GeMSSPublicKeyParameters; -import org.bouncycastle.pqc.crypto.gemss.GeMSSSigner; +import org.bouncycastle.pqc.crypto.test.NISTSecureRandom; +import org.bouncycastle.pqc.legacy.crypto.gemss.GeMSSKeyGenerationParameters; +import org.bouncycastle.pqc.legacy.crypto.gemss.GeMSSKeyPairGenerator; +import org.bouncycastle.pqc.legacy.crypto.gemss.GeMSSParameters; +import org.bouncycastle.pqc.legacy.crypto.gemss.GeMSSPrivateKeyParameters; +import org.bouncycastle.pqc.legacy.crypto.gemss.GeMSSPublicKeyParameters; +import org.bouncycastle.pqc.legacy.crypto.gemss.GeMSSSigner; import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; diff --git a/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/RainbowSignerTest.java b/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/RainbowSignerTest.java deleted file mode 100644 index 00eed90fc2..0000000000 --- a/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/RainbowSignerTest.java +++ /dev/null @@ -1,62 +0,0 @@ -package org.bouncycastle.pqc.legacy.crypto.test; - - -import java.math.BigInteger; -import java.security.SecureRandom; - -import org.bouncycastle.crypto.AsymmetricCipherKeyPair; -import org.bouncycastle.crypto.digests.SHA224Digest; -import org.bouncycastle.crypto.params.ParametersWithRandom; -import org.bouncycastle.pqc.crypto.DigestingMessageSigner; -import org.bouncycastle.pqc.legacy.crypto.rainbow.RainbowKeyGenerationParameters; -import org.bouncycastle.pqc.legacy.crypto.rainbow.RainbowKeyPairGenerator; -import org.bouncycastle.pqc.legacy.crypto.rainbow.RainbowParameters; -import org.bouncycastle.pqc.legacy.crypto.rainbow.RainbowSigner; -import org.bouncycastle.util.BigIntegers; -import org.bouncycastle.util.test.SimpleTest; - - -public class RainbowSignerTest -extends SimpleTest -{ - public String getName() - { - return "Rainbow"; - } - - public void performTest() - { - RainbowParameters params = new RainbowParameters(); - - RainbowKeyPairGenerator rainbowKeyGen = new RainbowKeyPairGenerator(); - RainbowKeyGenerationParameters genParam = new RainbowKeyGenerationParameters(new SecureRandom(), params); - - rainbowKeyGen.init(genParam); - - AsymmetricCipherKeyPair pair = rainbowKeyGen.generateKeyPair(); - - ParametersWithRandom param = new ParametersWithRandom(pair.getPrivate(), new SecureRandom()); - - DigestingMessageSigner rainbowSigner = new DigestingMessageSigner(new RainbowSigner() , new SHA224Digest()); - - rainbowSigner.init(true, param); - - byte[] message = BigIntegers.asUnsignedByteArray(new BigInteger("968236873715988614170569073515315707566766479517")); - rainbowSigner.update(message, 0, message.length); - byte[] sig = rainbowSigner.generateSignature(); - - rainbowSigner.init(false, pair.getPublic()); - rainbowSigner.update(message, 0, message.length); - - if (!rainbowSigner.verifySignature(sig)) - { - fail("verification fails"); - } - } - - public static void main( - String[] args) - { - runTest(new RainbowSignerTest()); - } -} diff --git a/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/RegressionTest.java b/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/RegressionTest.java index d60a243c62..ce9fc8ab90 100644 --- a/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/RegressionTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/RegressionTest.java @@ -10,8 +10,7 @@ public class RegressionTest new McElieceFujisakiCipherTest(), new McElieceKobaraImaiCipherTest(), new McElieceCipherTest(), - new McEliecePointchevalCipherTest(), - new RainbowSignerTest() , + new McEliecePointchevalCipherTest() }; public static void main(String[] args) diff --git a/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/TestSampler.java b/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/TestSampler.java new file mode 100644 index 0000000000..de2fb053e1 --- /dev/null +++ b/core/src/test/java/org/bouncycastle/pqc/legacy/crypto/test/TestSampler.java @@ -0,0 +1,31 @@ +package org.bouncycastle.pqc.legacy.crypto.test; + +import java.util.Random; + +import org.bouncycastle.util.Properties; + +class TestSampler +{ + private final boolean isFull; + private final int offSet; + + TestSampler() + { + isFull = Properties.isOverrideSet("test.full"); + + Random random = new Random(System.currentTimeMillis()); + + this.offSet = random.nextInt(10); + } + + boolean skipTest(String count) + { + int c = Integer.parseInt(count); + return !isFull && c != 0 && ((c + offSet) % 9 != 0); + } + + boolean skipTest(int count) + { + return !isFull && count != 0 && ((count + offSet) % 9 != 0); + } +} diff --git a/prov/src/main/ext-jdk1.9/module-info.java b/prov/src/main/ext-jdk1.9/module-info.java index 97a1f88810..e7f8f7b498 100644 --- a/prov/src/main/ext-jdk1.9/module-info.java +++ b/prov/src/main/ext-jdk1.9/module-info.java @@ -113,14 +113,14 @@ exports org.bouncycastle.pqc.crypto.mlkem; exports org.bouncycastle.pqc.crypto.falcon; exports org.bouncycastle.pqc.crypto.frodo; - exports org.bouncycastle.pqc.crypto.gemss; + exports org.bouncycastle.pqc.legacy.crypto.gemss; exports org.bouncycastle.pqc.crypto.hqc; exports org.bouncycastle.pqc.crypto.lms; exports org.bouncycastle.pqc.crypto.newhope; exports org.bouncycastle.pqc.crypto.ntru; exports org.bouncycastle.pqc.crypto.ntruprime; exports org.bouncycastle.pqc.crypto.picnic; - exports org.bouncycastle.pqc.crypto.rainbow; + exports org.bouncycastle.pqc.legacy.crypto.rainbow; exports org.bouncycastle.pqc.crypto.saber; exports org.bouncycastle.pqc.crypto.sphincs; exports org.bouncycastle.pqc.crypto.sphincsplus; diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/QTESLAKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/QTESLAKey.java deleted file mode 100644 index c6fc80f917..0000000000 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/QTESLAKey.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.bouncycastle.pqc.jcajce.interfaces; - -import org.bouncycastle.pqc.jcajce.spec.QTESLAParameterSpec; - -/** - * Base interface for a qTESLA key. - */ -public interface QTESLAKey -{ - /** - * Return the parameters for this key - in this case the security category. - * - * @return a QTESLAParameterSpec - */ - QTESLAParameterSpec getParams(); -} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/RainbowKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/RainbowKey.java deleted file mode 100644 index 827b2f4ddd..0000000000 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/RainbowKey.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.bouncycastle.pqc.jcajce.interfaces; - -import java.security.Key; - -import org.bouncycastle.pqc.jcajce.spec.RainbowParameterSpec; - -public interface RainbowKey - extends Key -{ - /** - * Return the parameters for this key. - * - * @return a RainbowParameterSpec - */ - RainbowParameterSpec getParameterSpec(); -} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/RainbowPrivateKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/RainbowPrivateKey.java deleted file mode 100644 index a60f912d5d..0000000000 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/RainbowPrivateKey.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.bouncycastle.pqc.jcajce.interfaces; - -import java.security.PrivateKey; - -public interface RainbowPrivateKey - extends PrivateKey, RainbowKey -{ - /** - * Return the public key corresponding to this private key. - * - * @return a Rainbow Public Key - */ - RainbowPublicKey getPublicKey(); -} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/RainbowPublicKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/RainbowPublicKey.java deleted file mode 100644 index 61dcce91a9..0000000000 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/interfaces/RainbowPublicKey.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.bouncycastle.pqc.jcajce.interfaces; - -import java.security.PublicKey; - -public interface RainbowPublicKey - extends PublicKey, RainbowKey -{ - -} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Rainbow.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Rainbow.java deleted file mode 100644 index 7c1fcf8012..0000000000 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/Rainbow.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.bouncycastle.pqc.jcajce.provider; - -import org.bouncycastle.asn1.bc.BCObjectIdentifiers; -import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; -import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; -import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; -import org.bouncycastle.pqc.jcajce.provider.rainbow.RainbowKeyFactorySpi; - -public class Rainbow -{ - private static final String PREFIX = "org.bouncycastle.pqc.jcajce.provider" + ".rainbow."; - - public static class Mappings - extends AsymmetricAlgorithmProvider - { - public Mappings() - { - } - - public void configure(ConfigurableProvider provider) - { - provider.addAlgorithm("KeyFactory.RAINBOW", PREFIX + "RainbowKeyFactorySpi"); - provider.addAlgorithm("KeyPairGenerator.RAINBOW", PREFIX + "RainbowKeyPairGeneratorSpi"); - - addKeyPairGeneratorAlgorithm(provider, "RAINBOW-III-CLASSIC", PREFIX + "RainbowKeyPairGeneratorSpi$RainbowIIIclassic", BCObjectIdentifiers.rainbow_III_classic); - addKeyPairGeneratorAlgorithm(provider, "RAINBOW-III-CIRCUMZENITHAL", PREFIX + "RainbowKeyPairGeneratorSpi$RainbowIIIcircum", BCObjectIdentifiers.rainbow_III_circumzenithal); - addKeyPairGeneratorAlgorithm(provider, "RAINBOW-III-COMPRESSED", PREFIX + "RainbowKeyPairGeneratorSpi$RainbowIIIcomp", BCObjectIdentifiers.rainbow_III_compressed); - addKeyPairGeneratorAlgorithm(provider, "RAINBOW-V-CLASSIC", PREFIX + "RainbowKeyPairGeneratorSpi$RainbowVclassic", BCObjectIdentifiers.rainbow_V_classic); - addKeyPairGeneratorAlgorithm(provider, "RAINBOW-V-CIRCUMZENITHAL", PREFIX + "RainbowKeyPairGeneratorSpi$RainbowVcircum", BCObjectIdentifiers.rainbow_V_circumzenithal); - addKeyPairGeneratorAlgorithm(provider, "RAINBOW-V-COMPRESSED", PREFIX + "RainbowKeyPairGeneratorSpi$RainbowVcomp", BCObjectIdentifiers.rainbow_V_compressed); - - addSignatureAlgorithm(provider, "RAINBOW", PREFIX + "SignatureSpi$Base", BCObjectIdentifiers.rainbow); - - addSignatureAlgorithm(provider, "RAINBOW-III-CLASSIC", PREFIX + "SignatureSpi$RainbowIIIclassic", BCObjectIdentifiers.rainbow_III_classic); - addSignatureAlgorithm(provider, "RAINBOW-III-CIRCUMZENITHAL", PREFIX + "SignatureSpi$RainbowIIIcircum", BCObjectIdentifiers.rainbow_III_circumzenithal); - addSignatureAlgorithm(provider, "RAINBOW-III-COMPRESSED", PREFIX + "SignatureSpi$RainbowIIIcomp", BCObjectIdentifiers.rainbow_III_compressed); - addSignatureAlgorithm(provider, "RAINBOW-V-CLASSIC", PREFIX + "SignatureSpi$RainbowVclassic", BCObjectIdentifiers.rainbow_V_classic); - addSignatureAlgorithm(provider, "RAINBOW-V-CIRCUMZENITHAL", PREFIX + "SignatureSpi$RainbowVcircum", BCObjectIdentifiers.rainbow_V_circumzenithal); - addSignatureAlgorithm(provider, "RAINBOW-v-COMPRESSED", PREFIX + "SignatureSpi$RainbowVcomp", BCObjectIdentifiers.rainbow_V_compressed); - - AsymmetricKeyInfoConverter keyFact = new RainbowKeyFactorySpi(); - - registerKeyFactoryOid(provider, BCObjectIdentifiers.rainbow_III_classic, "RAINBOW", keyFact); - registerKeyFactoryOid(provider, BCObjectIdentifiers.rainbow_III_circumzenithal, "RAINBOW", keyFact); - registerKeyFactoryOid(provider, BCObjectIdentifiers.rainbow_III_compressed, "RAINBOW", keyFact); - registerKeyFactoryOid(provider, BCObjectIdentifiers.rainbow_V_classic, "RAINBOW", keyFact); - registerKeyFactoryOid(provider, BCObjectIdentifiers.rainbow_V_circumzenithal, "RAINBOW", keyFact); - registerKeyFactoryOid(provider, BCObjectIdentifiers.rainbow_V_compressed, "RAINBOW", keyFact); - } - } -} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/BCRainbowPrivateKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/BCRainbowPrivateKey.java deleted file mode 100644 index e1e1e53f8b..0000000000 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/BCRainbowPrivateKey.java +++ /dev/null @@ -1,140 +0,0 @@ -package org.bouncycastle.pqc.jcajce.provider.rainbow; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -import org.bouncycastle.asn1.ASN1Set; -import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; -import org.bouncycastle.pqc.crypto.rainbow.RainbowPrivateKeyParameters; -import org.bouncycastle.pqc.crypto.rainbow.RainbowPublicKeyParameters; -import org.bouncycastle.pqc.crypto.util.PrivateKeyFactory; -import org.bouncycastle.pqc.jcajce.interfaces.RainbowPrivateKey; -import org.bouncycastle.pqc.jcajce.interfaces.RainbowPublicKey; -import org.bouncycastle.pqc.jcajce.provider.util.KeyUtil; -import org.bouncycastle.pqc.jcajce.spec.RainbowParameterSpec; -import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Strings; -import org.bouncycastle.util.encoders.Hex; - -public class BCRainbowPrivateKey - implements RainbowPrivateKey -{ - private static final long serialVersionUID = 1L; - - private transient RainbowPrivateKeyParameters params; - private transient String algorithm; - private transient byte[] encoding; - private transient ASN1Set attributes; - - public BCRainbowPrivateKey( - RainbowPrivateKeyParameters params) - { - init(params, null); - } - - public BCRainbowPrivateKey(PrivateKeyInfo keyInfo) - throws IOException - { - init(keyInfo); - } - - private void init(PrivateKeyInfo keyInfo) - throws IOException - { - init((RainbowPrivateKeyParameters) PrivateKeyFactory.createKey(keyInfo), keyInfo.getAttributes()); - } - - private void init(RainbowPrivateKeyParameters params, ASN1Set attributes) - { - this.attributes = attributes; - this.params = params; - this.algorithm = Strings.toUpperCase(params.getParameters().getName()); - } - - /** - * Compare this Rainbow private key with another object. - * - * @param o the other object - * @return the result of the comparison - */ - public boolean equals(Object o) - { - if (o == this) - { - return true; - } - - if (o instanceof BCRainbowPrivateKey) - { - BCRainbowPrivateKey otherKey = (BCRainbowPrivateKey)o; - - return Arrays.areEqual(getEncoded(), otherKey.getEncoded()); - } - - return false; - } - - public int hashCode() - { - return Arrays.hashCode(getEncoded()); - } - - /** - * @return name of the algorithm - */ - public final String getAlgorithm() - { - return algorithm; - } - - public byte[] getEncoded() - { - if (encoding == null) - { - encoding = KeyUtil.getEncodedPrivateKeyInfo(params, attributes); - } - - return Arrays.clone(encoding); - } - - public RainbowParameterSpec getParameterSpec() - { - return RainbowParameterSpec.fromName(params.getParameters().getName()); - } - - public String getFormat() - { - return "PKCS#8"; - } - - public RainbowPublicKey getPublicKey() - { - return new BCRainbowPublicKey(new RainbowPublicKeyParameters(params.getParameters(), params.getPublicKey())); - } - - RainbowPrivateKeyParameters getKeyParams() - { - return params; - } - - private void readObject( - ObjectInputStream in) - throws IOException, ClassNotFoundException - { - in.defaultReadObject(); - - byte[] enc = (byte[])in.readObject(); - - init(PrivateKeyInfo.getInstance(enc)); - } - - private void writeObject( - ObjectOutputStream out) - throws IOException - { - out.defaultWriteObject(); - - out.writeObject(this.getEncoded()); - } -} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/BCRainbowPublicKey.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/BCRainbowPublicKey.java deleted file mode 100644 index a8f0c63bfe..0000000000 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/BCRainbowPublicKey.java +++ /dev/null @@ -1,130 +0,0 @@ -package org.bouncycastle.pqc.jcajce.provider.rainbow; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; - -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.pqc.crypto.rainbow.RainbowPublicKeyParameters; -import org.bouncycastle.pqc.crypto.util.PublicKeyFactory; -import org.bouncycastle.pqc.jcajce.interfaces.RainbowPublicKey; -import org.bouncycastle.pqc.jcajce.provider.util.KeyUtil; -import org.bouncycastle.pqc.jcajce.spec.RainbowParameterSpec; -import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Strings; -import org.bouncycastle.util.encoders.Hex; - -public class BCRainbowPublicKey - implements RainbowPublicKey -{ - private static final long serialVersionUID = 1L; - - private transient RainbowPublicKeyParameters params; - private transient String algorithm; - private transient byte[] encoding; - - public BCRainbowPublicKey( - RainbowPublicKeyParameters params) - { - init(params); - } - - public BCRainbowPublicKey(SubjectPublicKeyInfo keyInfo) - throws IOException - { - init(keyInfo); - } - - private void init(SubjectPublicKeyInfo keyInfo) - throws IOException - { - init((RainbowPublicKeyParameters) PublicKeyFactory.createKey(keyInfo)); - } - - private void init(RainbowPublicKeyParameters params) - { - this.params = params; - this.algorithm = Strings.toUpperCase(params.getParameters().getName()); - } - - /** - * Compare this Rainbow public key with another object. - * - * @param o the other object - * @return the result of the comparison - */ - public boolean equals(Object o) - { - if (o == this) - { - return true; - } - - if (o instanceof BCRainbowPublicKey) - { - BCRainbowPublicKey otherKey = (BCRainbowPublicKey)o; - - return Arrays.areEqual(getEncoded(), otherKey.getEncoded()); - } - - return false; - } - - public int hashCode() - { - return Arrays.hashCode(getEncoded()); - } - - /** - * @return name of the algorithm - */ - public final String getAlgorithm() - { - return algorithm; - } - - public byte[] getEncoded() - { - if (encoding == null) - { - encoding = KeyUtil.getEncodedSubjectPublicKeyInfo(params); - } - - return Arrays.clone(encoding); - } - - public String getFormat() - { - return "X.509"; - } - - public RainbowParameterSpec getParameterSpec() - { - return RainbowParameterSpec.fromName(params.getParameters().getName()); - } - - RainbowPublicKeyParameters getKeyParams() - { - return params; - } - - private void readObject( - ObjectInputStream in) - throws IOException, ClassNotFoundException - { - in.defaultReadObject(); - - byte[] enc = (byte[])in.readObject(); - - init(SubjectPublicKeyInfo.getInstance(enc)); - } - - private void writeObject( - ObjectOutputStream out) - throws IOException - { - out.defaultWriteObject(); - - out.writeObject(this.getEncoded()); - } -} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyFactorySpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyFactorySpi.java deleted file mode 100644 index 5c3c142a19..0000000000 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyFactorySpi.java +++ /dev/null @@ -1,116 +0,0 @@ -package org.bouncycastle.pqc.jcajce.provider.rainbow; - -import java.io.IOException; -import java.security.InvalidKeyException; -import java.security.Key; -import java.security.KeyFactorySpi; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.spec.InvalidKeySpecException; -import java.security.spec.KeySpec; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; - -import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; - -public class RainbowKeyFactorySpi - extends KeyFactorySpi - implements AsymmetricKeyInfoConverter -{ - public PrivateKey engineGeneratePrivate(KeySpec keySpec) - throws InvalidKeySpecException - { - if (keySpec instanceof PKCS8EncodedKeySpec) - { - // get the DER-encoded Key according to PKCS#8 from the spec - byte[] encKey = ((PKCS8EncodedKeySpec)keySpec).getEncoded(); - - try - { - return generatePrivate(PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(encKey))); - } - catch (Exception e) - { - throw new InvalidKeySpecException(e.toString()); - } - } - - throw new InvalidKeySpecException("Unsupported key specification: " - + keySpec.getClass() + "."); - } - - public PublicKey engineGeneratePublic(KeySpec keySpec) - throws InvalidKeySpecException - { - if (keySpec instanceof X509EncodedKeySpec) - { - // get the DER-encoded Key according to X.509 from the spec - byte[] encKey = ((X509EncodedKeySpec)keySpec).getEncoded(); - - // decode the SubjectPublicKeyInfo data structure to the pki object - try - { - return generatePublic(SubjectPublicKeyInfo.getInstance(encKey)); - } - catch (Exception e) - { - throw new InvalidKeySpecException(e.toString(), e); - } - } - - throw new InvalidKeySpecException("Unknown key specification: " + keySpec + "."); - } - - public final KeySpec engineGetKeySpec(Key key, Class keySpec) - throws InvalidKeySpecException - { - if (key instanceof BCRainbowPrivateKey) - { - if (PKCS8EncodedKeySpec.class.isAssignableFrom(keySpec)) - { - return new PKCS8EncodedKeySpec(key.getEncoded()); - } - } - else if (key instanceof BCRainbowPublicKey) - { - if (X509EncodedKeySpec.class.isAssignableFrom(keySpec)) - { - return new X509EncodedKeySpec(key.getEncoded()); - } - } - else - { - throw new InvalidKeySpecException("Unsupported key type: " - + key.getClass() + "."); - } - - throw new InvalidKeySpecException("Unknown key specification: " - + keySpec + "."); - } - - public final Key engineTranslateKey(Key key) - throws InvalidKeyException - { - if (key instanceof BCRainbowPrivateKey || key instanceof BCRainbowPublicKey) - { - return key; - } - - throw new InvalidKeyException("Unsupported key type"); - } - - public PrivateKey generatePrivate(PrivateKeyInfo keyInfo) - throws IOException - { - return new BCRainbowPrivateKey(keyInfo); - } - - public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) - throws IOException - { - return new BCRainbowPublicKey(keyInfo); - } -} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyPairGeneratorSpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyPairGeneratorSpi.java deleted file mode 100644 index 0bafb9af47..0000000000 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/RainbowKeyPairGeneratorSpi.java +++ /dev/null @@ -1,180 +0,0 @@ -package org.bouncycastle.pqc.jcajce.provider.rainbow; - -import java.security.InvalidAlgorithmParameterException; -import java.security.KeyPair; -import java.security.SecureRandom; -import java.security.spec.AlgorithmParameterSpec; -import java.util.HashMap; -import java.util.Map; - -import org.bouncycastle.crypto.AsymmetricCipherKeyPair; -import org.bouncycastle.crypto.CryptoServicesRegistrar; -import org.bouncycastle.pqc.crypto.rainbow.RainbowKeyGenerationParameters; -import org.bouncycastle.pqc.crypto.rainbow.RainbowKeyPairGenerator; -import org.bouncycastle.pqc.crypto.rainbow.RainbowParameters; -import org.bouncycastle.pqc.crypto.rainbow.RainbowPrivateKeyParameters; -import org.bouncycastle.pqc.crypto.rainbow.RainbowPublicKeyParameters; -import org.bouncycastle.pqc.jcajce.provider.util.SpecUtil; -import org.bouncycastle.pqc.jcajce.spec.RainbowParameterSpec; -import org.bouncycastle.util.Strings; - -public class RainbowKeyPairGeneratorSpi - extends java.security.KeyPairGenerator -{ - private static Map parameters = new HashMap(); - - static - { - parameters.put(RainbowParameterSpec.rainbowIIIclassic.getName(), RainbowParameters.rainbowIIIclassic); - parameters.put(RainbowParameterSpec.rainbowIIIcircumzenithal.getName(), RainbowParameters.rainbowIIIcircumzenithal); - parameters.put(RainbowParameterSpec.rainbowIIIcompressed.getName(), RainbowParameters.rainbowIIIcompressed); - parameters.put(RainbowParameterSpec.rainbowVclassic.getName(), RainbowParameters.rainbowVclassic); - parameters.put(RainbowParameterSpec.rainbowVcircumzenithal.getName(), RainbowParameters.rainbowVcircumzenithal); - parameters.put(RainbowParameterSpec.rainbowVcompressed.getName(), RainbowParameters.rainbowVcompressed); - } - - private final RainbowParameters rainbowParameters; - - RainbowKeyGenerationParameters param; - RainbowKeyPairGenerator engine = new RainbowKeyPairGenerator(); - - SecureRandom random = CryptoServicesRegistrar.getSecureRandom(); - boolean initialised = false; - - public RainbowKeyPairGeneratorSpi() - { - super("RAINBOW"); - this.rainbowParameters = null; - } - - protected RainbowKeyPairGeneratorSpi(RainbowParameters rainbowParameters) - { - super(rainbowParameters.getName()); - this.rainbowParameters = rainbowParameters; - } - - public void initialize( - int strength, - SecureRandom random) - { - throw new IllegalArgumentException("use AlgorithmParameterSpec"); - } - - public void initialize( - AlgorithmParameterSpec params, - SecureRandom random) - throws InvalidAlgorithmParameterException - { - String name = getNameFromParams(params); - - if (name != null && parameters.containsKey(name)) - { - RainbowParameters rainbowParams = (RainbowParameters)parameters.get(name); - - param = new RainbowKeyGenerationParameters(random, rainbowParams); - - if (rainbowParameters != null && !rainbowParams.getName().equals(rainbowParameters.getName())) - { - throw new InvalidAlgorithmParameterException("key pair generator locked to " + Strings.toUpperCase(rainbowParameters.getName())); - } - - engine.init(param); - initialised = true; - } - else - { - throw new InvalidAlgorithmParameterException("invalid ParameterSpec: " + params); - } - } - - private static String getNameFromParams(AlgorithmParameterSpec paramSpec) - { - if (paramSpec instanceof RainbowParameterSpec) - { - RainbowParameterSpec rainbowParams = (RainbowParameterSpec)paramSpec; - return rainbowParams.getName(); - } - else - { - return Strings.toLowerCase(SpecUtil.getNameFrom(paramSpec)); - } - } - - public KeyPair generateKeyPair() - { - if (!initialised) - { - if (rainbowParameters != null) - { - param = new RainbowKeyGenerationParameters(random, rainbowParameters); - } - else - { - param = new RainbowKeyGenerationParameters(random, RainbowParameters.rainbowIIIclassic); - } - - engine.init(param); - initialised = true; - } - - AsymmetricCipherKeyPair pair = engine.generateKeyPair(); - RainbowPublicKeyParameters pub = (RainbowPublicKeyParameters)pair.getPublic(); - RainbowPrivateKeyParameters priv = (RainbowPrivateKeyParameters)pair.getPrivate(); - - return new KeyPair(new BCRainbowPublicKey(pub), new BCRainbowPrivateKey(priv)); - } - - public static class RainbowIIIclassic - extends RainbowKeyPairGeneratorSpi - { - public RainbowIIIclassic() - { - super(RainbowParameters.rainbowIIIclassic); - } - } - - public static class RainbowIIIcircum - extends RainbowKeyPairGeneratorSpi - { - public RainbowIIIcircum() - { - super(RainbowParameters.rainbowIIIcircumzenithal); - } - } - - public static class RainbowIIIcomp - extends RainbowKeyPairGeneratorSpi - { - public RainbowIIIcomp() - { - super(RainbowParameters.rainbowIIIcompressed); - } - } - - public static class RainbowVclassic - extends RainbowKeyPairGeneratorSpi - { - public RainbowVclassic() - { - super(RainbowParameters.rainbowVclassic); - } - } - - public static class RainbowVcircum - extends RainbowKeyPairGeneratorSpi - { - public RainbowVcircum() - { - super(RainbowParameters.rainbowVcircumzenithal); - } - } - - public static class RainbowVcomp - extends RainbowKeyPairGeneratorSpi - { - public RainbowVcomp() - { - super(RainbowParameters.rainbowVcompressed); - } - } -} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/SignatureSpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/SignatureSpi.java deleted file mode 100644 index 14213bb3ac..0000000000 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/rainbow/SignatureSpi.java +++ /dev/null @@ -1,235 +0,0 @@ -package org.bouncycastle.pqc.jcajce.provider.rainbow; - -import java.io.ByteArrayOutputStream; -import java.security.InvalidKeyException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.SecureRandom; -import java.security.SignatureException; -import java.security.spec.AlgorithmParameterSpec; - -import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.crypto.CipherParameters; -import org.bouncycastle.crypto.params.ParametersWithRandom; -import org.bouncycastle.pqc.crypto.rainbow.RainbowParameters; -import org.bouncycastle.pqc.crypto.rainbow.RainbowSigner; -import org.bouncycastle.util.Strings; - -public class SignatureSpi - extends java.security.Signature -{ - private ByteArrayOutputStream bOut; - private RainbowSigner signer; - private SecureRandom random; - private RainbowParameters parameters; - - protected SignatureSpi(RainbowSigner signer) - { - super("RAINBOW"); - - this.bOut = new ByteArrayOutputStream(); - this.signer = signer; - this.parameters = null; - } - - protected SignatureSpi(RainbowSigner signer, RainbowParameters parameters) - { - super(Strings.toUpperCase(parameters.getName())); - this.parameters = parameters; - - this.bOut = new ByteArrayOutputStream(); - this.signer = signer; - } - - protected void engineInitVerify(PublicKey publicKey) - throws InvalidKeyException - { - if (!(publicKey instanceof BCRainbowPublicKey)) - { - try - { - publicKey = new BCRainbowPublicKey(SubjectPublicKeyInfo.getInstance(publicKey.getEncoded())); - } - catch (Exception e) - { - throw new InvalidKeyException("unknown public key passed to Rainbow: " + e.getMessage(), e); - } - } - - BCRainbowPublicKey key = (BCRainbowPublicKey)publicKey; - - if (parameters != null) - { - String canonicalAlg = Strings.toUpperCase(parameters.getName()); - if (!canonicalAlg.equals(key.getAlgorithm())) - { - throw new InvalidKeyException("signature configured for " + canonicalAlg); - } - } - - signer.init(false, key.getKeyParams()); - } - - protected void engineInitSign(PrivateKey privateKey, SecureRandom random) - throws InvalidKeyException - { - this.random = random; - engineInitSign(privateKey); - } - - protected void engineInitSign(PrivateKey privateKey) - throws InvalidKeyException - { - if (privateKey instanceof BCRainbowPrivateKey) - { - BCRainbowPrivateKey key = (BCRainbowPrivateKey)privateKey; - CipherParameters param = key.getKeyParams(); - - if (parameters != null) - { - String canonicalAlg = Strings.toUpperCase(parameters.getName()); - if (!canonicalAlg.equals(key.getAlgorithm())) - { - throw new InvalidKeyException("signature configured for " + canonicalAlg); - } - } - - if (random != null) - { - signer.init(true, new ParametersWithRandom(param, random)); - } - else - { - signer.init(true, param); - } - } - else - { - throw new InvalidKeyException("unknown private key passed to Rainbow"); - } - } - - protected void engineUpdate(byte b) - throws SignatureException - { - bOut.write(b); - } - - protected void engineUpdate(byte[] b, int off, int len) - throws SignatureException - { - bOut.write(b, off, len); - } - - protected byte[] engineSign() - throws SignatureException - { - try - { - byte[] message = bOut.toByteArray(); - - bOut.reset(); - - return signer.generateSignature(message); - } - catch (Exception e) - { - throw new SignatureException(e.toString()); - } - } - - protected boolean engineVerify(byte[] sigBytes) - throws SignatureException - { - byte[] message = bOut.toByteArray(); - - bOut.reset(); - - return signer.verifySignature(message, sigBytes); - } - - protected void engineSetParameter(AlgorithmParameterSpec params) - { - // TODO - throw new UnsupportedOperationException("engineSetParameter unsupported"); - } - - /** - * @deprecated replaced with #engineSetParameter(java.security.spec.AlgorithmParameterSpec) - */ - protected void engineSetParameter(String param, Object value) - { - throw new UnsupportedOperationException("engineSetParameter unsupported"); - } - - /** - * @deprecated - */ - protected Object engineGetParameter(String param) - { - throw new UnsupportedOperationException("engineSetParameter unsupported"); - } - - public static class Base - extends SignatureSpi - { - public Base() - { - super(new RainbowSigner()); - } - } - - public static class RainbowIIIclassic - extends SignatureSpi - { - public RainbowIIIclassic() - { - super(new RainbowSigner(), RainbowParameters.rainbowIIIclassic); - } - } - - public static class RainbowIIIcircum - extends SignatureSpi - { - public RainbowIIIcircum() - { - super(new RainbowSigner(), RainbowParameters.rainbowIIIcircumzenithal); - } - } - - public static class RainbowIIIcomp - extends SignatureSpi - { - public RainbowIIIcomp() - { - super(new RainbowSigner(), RainbowParameters.rainbowIIIcompressed); - } - } - - public static class RainbowVclassic - extends SignatureSpi - { - public RainbowVclassic() - { - super(new RainbowSigner(), RainbowParameters.rainbowVclassic); - } - } - - public static class RainbowVcircum - extends SignatureSpi - { - public RainbowVcircum() - { - super(new RainbowSigner(), RainbowParameters.rainbowVcircumzenithal); - } - } - - public static class RainbowVcomp - extends SignatureSpi - { - public RainbowVcomp() - { - super(new RainbowSigner(), RainbowParameters.rainbowVcompressed); - } - } -} diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/spec/RainbowParameterSpec.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/spec/RainbowParameterSpec.java deleted file mode 100644 index b1efc87996..0000000000 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/spec/RainbowParameterSpec.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.bouncycastle.pqc.jcajce.spec; - -import java.security.spec.AlgorithmParameterSpec; -import java.util.HashMap; -import java.util.Map; - -import org.bouncycastle.pqc.crypto.crystals.dilithium.DilithiumParameters; -import org.bouncycastle.pqc.crypto.rainbow.RainbowParameters; -import org.bouncycastle.util.Strings; - -public class RainbowParameterSpec - implements AlgorithmParameterSpec -{ - public static final RainbowParameterSpec rainbowIIIclassic = new RainbowParameterSpec(RainbowParameters.rainbowIIIclassic); - public static final RainbowParameterSpec rainbowIIIcircumzenithal = new RainbowParameterSpec(RainbowParameters.rainbowIIIcircumzenithal); - public static final RainbowParameterSpec rainbowIIIcompressed = new RainbowParameterSpec(RainbowParameters.rainbowIIIcompressed); - - public static final RainbowParameterSpec rainbowVclassic = new RainbowParameterSpec(RainbowParameters.rainbowVclassic); - public static final RainbowParameterSpec rainbowVcircumzenithal = new RainbowParameterSpec(RainbowParameters.rainbowVcircumzenithal); - public static final RainbowParameterSpec rainbowVcompressed = new RainbowParameterSpec(RainbowParameters.rainbowVcompressed); - - private static Map parameters = new HashMap(); - - static - { - parameters.put("rainbow-iii-classic", rainbowIIIclassic); - parameters.put("rainbow-iii-circumzenithal", rainbowIIIcircumzenithal); - parameters.put("rainbow-iii-compressed", rainbowIIIcompressed); - parameters.put("rainbow-v-classic", rainbowVclassic); - parameters.put("rainbow-v-circumzenithal", rainbowVcircumzenithal); - parameters.put("rainbow-v-compressed", rainbowVcompressed); - } - - private final String name; - - private RainbowParameterSpec(RainbowParameters parameters) - { - this.name = Strings.toUpperCase(parameters.getName()); - } - - public String getName() - { - return name; - } - - public static RainbowParameterSpec fromName(String name) - { - return (RainbowParameterSpec)parameters.get(Strings.toLowerCase(name)); - } -} diff --git a/prov/src/main/jdk1.9/module-info.java b/prov/src/main/jdk1.9/module-info.java index 237e3c8fa2..a8219f239d 100644 --- a/prov/src/main/jdk1.9/module-info.java +++ b/prov/src/main/jdk1.9/module-info.java @@ -125,7 +125,6 @@ exports org.bouncycastle.pqc.crypto.mlkem; exports org.bouncycastle.pqc.crypto.falcon; exports org.bouncycastle.pqc.crypto.frodo; - exports org.bouncycastle.pqc.crypto.gemss; exports org.bouncycastle.pqc.crypto.hqc; exports org.bouncycastle.pqc.crypto.lms; exports org.bouncycastle.pqc.crypto.newhope; @@ -157,19 +156,17 @@ exports org.bouncycastle.pqc.jcajce.provider.ntruprime; exports org.bouncycastle.pqc.jcajce.provider.newhope; exports org.bouncycastle.pqc.jcajce.provider.picnic; - exports org.bouncycastle.pqc.jcajce.provider.rainbow; exports org.bouncycastle.pqc.jcajce.provider.saber; exports org.bouncycastle.pqc.jcajce.provider.sphincs; exports org.bouncycastle.pqc.jcajce.provider.sphincsplus; exports org.bouncycastle.pqc.jcajce.provider.util; exports org.bouncycastle.pqc.jcajce.provider.xmss; exports org.bouncycastle.pqc.jcajce.spec; + exports org.bouncycastle.pqc.legacy.crypto.gemss; exports org.bouncycastle.pqc.legacy.crypto.gmss; exports org.bouncycastle.pqc.legacy.crypto.gmss.util; exports org.bouncycastle.pqc.legacy.crypto.qtesla; exports org.bouncycastle.pqc.legacy.crypto.mceliece; - exports org.bouncycastle.pqc.legacy.crypto.rainbow; - exports org.bouncycastle.pqc.legacy.crypto.rainbow.util; exports org.bouncycastle.pqc.legacy.math.linearalgebra; exports org.bouncycastle.util; exports org.bouncycastle.util.encoders; diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java index 6782a44a9e..51e9d6ece8 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/AllTests.java @@ -75,8 +75,6 @@ public static Test suite() suite.addTestSuite(BIKETest.class); suite.addTestSuite(HQCKeyPairGeneratorTest.class); suite.addTestSuite(HQCTest.class); - suite.addTestSuite(RainbowKeyPairGeneratorTest.class); - suite.addTestSuite(RainbowTest.class); suite.addTestSuite(MayoKeyPairGeneratorTest.class); suite.addTestSuite(MayoTest.class); suite.addTestSuite(SnovaTest.class); diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/RainbowKeyPairGeneratorTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/RainbowKeyPairGeneratorTest.java deleted file mode 100644 index 44ad589782..0000000000 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/RainbowKeyPairGeneratorTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package org.bouncycastle.pqc.jcajce.provider.test; - -import java.security.KeyFactory; -import java.security.KeyPairGenerator; -import java.security.SecureRandom; - -import org.bouncycastle.pqc.jcajce.spec.RainbowParameterSpec; - -/** - * KeyFactory/KeyPairGenerator tests for Falcon with BCPQC provider. - */ -public class RainbowKeyPairGeneratorTest - extends KeyPairGeneratorTest -{ - protected void setUp() - { - super.setUp(); - } - - public void testKeyFactory() - throws Exception - { - kf = KeyFactory.getInstance("Rainbow", "BCPQC"); - } - - public void testKeyPairEncoding() - throws Exception - { - RainbowParameterSpec[] specs = - new RainbowParameterSpec[] - { - RainbowParameterSpec.rainbowIIIclassic, - RainbowParameterSpec.rainbowIIIcircumzenithal, - RainbowParameterSpec.rainbowIIIcompressed, - RainbowParameterSpec.rainbowVclassic, - RainbowParameterSpec.rainbowVcircumzenithal, - RainbowParameterSpec.rainbowVcompressed, - }; - kf = KeyFactory.getInstance("Rainbow", "BCPQC"); - - kpg = KeyPairGenerator.getInstance("Rainbow", "BCPQC"); - - for (int i = 0; i != specs.length; i++) - { - kpg.initialize(specs[i], new SecureRandom()); - performKeyPairEncodingTest(kpg.generateKeyPair()); - } - } - -} diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/RainbowTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/RainbowTest.java deleted file mode 100644 index a6fd39d1ab..0000000000 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/RainbowTest.java +++ /dev/null @@ -1,393 +0,0 @@ -package org.bouncycastle.pqc.jcajce.provider.test; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.security.InvalidAlgorithmParameterException; -import java.security.InvalidKeyException; -import java.security.KeyFactory; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.SecureRandom; -import java.security.Security; -import java.security.Signature; -import java.security.SignatureException; -import java.security.spec.AlgorithmParameterSpec; -import java.security.spec.PKCS8EncodedKeySpec; -import java.security.spec.X509EncodedKeySpec; - -import junit.framework.TestCase; -import org.bouncycastle.asn1.ASN1OctetString; -import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; -import org.bouncycastle.pqc.jcajce.interfaces.RainbowKey; -import org.bouncycastle.pqc.jcajce.interfaces.RainbowPrivateKey; -import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider; -import org.bouncycastle.pqc.jcajce.spec.RainbowParameterSpec; -import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Strings; -import org.bouncycastle.util.encoders.Hex; - -public class RainbowTest - extends TestCase -{ - byte[] msg = Strings.toByteArray("Hello World!"); - - public void setUp() - { - if (Security.getProvider(BouncyCastlePQCProvider.PROVIDER_NAME) == null) - { - Security.addProvider(new BouncyCastlePQCProvider()); - } - } - - public void testPrivateKeyRecovery() - throws Exception - { - KeyPairGenerator kpg = KeyPairGenerator.getInstance("Rainbow", "BCPQC"); - - kpg.initialize(RainbowParameterSpec.rainbowIIIclassic, new RainbowTest.RiggedRandom()); - - KeyPair kp = kpg.generateKeyPair(); - - KeyFactory kFact = KeyFactory.getInstance("Rainbow", "BCPQC"); - - RainbowKey privKey = (RainbowKey)kFact.generatePrivate(new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded())); - - assertEquals(kp.getPrivate(), privKey); - assertEquals(kp.getPrivate().getAlgorithm(), privKey.getAlgorithm()); - assertEquals(kp.getPrivate().hashCode(), privKey.hashCode()); - - assertEquals(((RainbowPrivateKey)kp.getPrivate()).getPublicKey(), ((RainbowPrivateKey)privKey).getPublicKey()); - - ByteArrayOutputStream bOut = new ByteArrayOutputStream(); - ObjectOutputStream oOut = new ObjectOutputStream(bOut); - - oOut.writeObject(privKey); - - oOut.close(); - - ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray())); - - RainbowKey privKey2 = (RainbowKey)oIn.readObject(); - - assertEquals(privKey, privKey2); - assertEquals(privKey.getAlgorithm(), privKey2.getAlgorithm()); - assertEquals(privKey.hashCode(), privKey2.hashCode()); - - assertEquals(kp.getPublic(), ((RainbowPrivateKey)privKey2).getPublicKey()); - assertEquals(((RainbowPrivateKey)privKey).getPublicKey(), ((RainbowPrivateKey)privKey2).getPublicKey()); - } - - public void testPublicKeyRecovery() - throws Exception - { - KeyPairGenerator kpg = KeyPairGenerator.getInstance("Rainbow", "BCPQC"); - - kpg.initialize(RainbowParameterSpec.rainbowVclassic, new RainbowTest.RiggedRandom()); - - KeyPair kp = kpg.generateKeyPair(); - - KeyFactory kFact = KeyFactory.getInstance("Rainbow", "BCPQC"); - - RainbowKey pubKey = (RainbowKey)kFact.generatePublic(new X509EncodedKeySpec(kp.getPublic().getEncoded())); - - assertEquals(kp.getPublic(), pubKey); - assertEquals(kp.getPublic().getAlgorithm(), pubKey.getAlgorithm()); - assertEquals(kp.getPublic().hashCode(), pubKey.hashCode()); - - ByteArrayOutputStream bOut = new ByteArrayOutputStream(); - ObjectOutputStream oOut = new ObjectOutputStream(bOut); - - oOut.writeObject(pubKey); - - oOut.close(); - - ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray())); - - RainbowKey pubKey2 = (RainbowKey)oIn.readObject(); - - assertEquals(pubKey, pubKey2); - assertEquals(pubKey.getAlgorithm(), pubKey2.getAlgorithm()); - assertEquals(pubKey.hashCode(), pubKey2.hashCode()); - } - - public void testRainbowIIIclassic() - throws Exception - { - doConfSigTest("Rainbow-III-Classic", RainbowParameterSpec.rainbowIIIclassic, RainbowParameterSpec.rainbowVclassic); - } - - public void testRainbowIIIcircum() - throws Exception - { - doConfSigTest("Rainbow-III-Circumzenithal", RainbowParameterSpec.rainbowIIIcircumzenithal, RainbowParameterSpec.rainbowVclassic); - } - - public void testRainbowIIIcomp() - throws Exception - { - doConfSigTest("Rainbow-III-Compressed", RainbowParameterSpec.rainbowIIIcompressed, RainbowParameterSpec.rainbowVclassic); - } - - public void testRainbowVclassic() - throws Exception - { - doConfSigTest("Rainbow-V-Classic", RainbowParameterSpec.rainbowVclassic, RainbowParameterSpec.rainbowIIIclassic); - } - - public void testRainbowVcircum() - throws Exception - { - doConfSigTest("Rainbow-V-Circumzenithal", RainbowParameterSpec.rainbowVcircumzenithal, RainbowParameterSpec.rainbowIIIclassic); - } - - public void testRainbowVcompressed() - throws Exception - { - doConfSigTest("Rainbow-V-Compressed", RainbowParameterSpec.rainbowVcompressed, RainbowParameterSpec.rainbowIIIclassic); - } - - private void doConfSigTest(String algorithmName, AlgorithmParameterSpec algSpec, AlgorithmParameterSpec altSpec) - throws Exception - { - KeyPairGenerator kpg = KeyPairGenerator.getInstance("Rainbow", "BCPQC"); - - kpg.initialize(algSpec, new SecureRandom()); - - KeyPair kp = kpg.generateKeyPair(); - - Signature sig = Signature.getInstance("Rainbow", "BCPQC"); - - sig.initSign(kp.getPrivate(), new SecureRandom()); - - sig.update(msg, 0, msg.length); - - byte[] s = sig.sign(); - - sig = Signature.getInstance(algorithmName, "BCPQC"); - - assertEquals(Strings.toUpperCase(algorithmName), Strings.toUpperCase(sig.getAlgorithm())); - - sig.initVerify(kp.getPublic()); - - sig.update(msg, 0, msg.length); - - assertTrue(sig.verify(s)); - - kpg = KeyPairGenerator.getInstance("Rainbow", "BCPQC"); - - kpg.initialize(altSpec, new SecureRandom()); - - kp = kpg.generateKeyPair(); - - try - { - sig.initVerify(kp.getPublic()); - fail("no exception"); - } - catch (InvalidKeyException e) - { - assertEquals("signature configured for " + Strings.toUpperCase(algorithmName), e.getMessage()); - } - } - - public void testRestrictedKeyPairGen() - throws Exception - { - doTestRestrictedKeyPairGen(RainbowParameterSpec.rainbowIIIclassic, RainbowParameterSpec.rainbowVclassic); - doTestRestrictedKeyPairGen(RainbowParameterSpec.rainbowIIIcircumzenithal, RainbowParameterSpec.rainbowVclassic); - doTestRestrictedKeyPairGen(RainbowParameterSpec.rainbowIIIcompressed, RainbowParameterSpec.rainbowVclassic); - doTestRestrictedKeyPairGen(RainbowParameterSpec.rainbowVclassic, RainbowParameterSpec.rainbowIIIclassic); - doTestRestrictedKeyPairGen(RainbowParameterSpec.rainbowVcircumzenithal, RainbowParameterSpec.rainbowIIIclassic); - doTestRestrictedKeyPairGen(RainbowParameterSpec.rainbowVcompressed, RainbowParameterSpec.rainbowIIIclassic); - } - - private void doTestRestrictedKeyPairGen(RainbowParameterSpec spec, RainbowParameterSpec altSpec) - throws Exception - { - KeyPairGenerator kpg = KeyPairGenerator.getInstance(spec.getName(), "BCPQC"); - - kpg.initialize(spec, new SecureRandom()); - - KeyPair kp = kpg.generateKeyPair(); - - assertEquals(spec.getName(), kp.getPublic().getAlgorithm()); - assertEquals(spec.getName(), kp.getPrivate().getAlgorithm()); - - kpg = KeyPairGenerator.getInstance(spec.getName(), "BCPQC"); - - try - { - kpg.initialize(altSpec, new SecureRandom()); - fail("no exception"); - } - catch (InvalidAlgorithmParameterException e) - { - assertEquals("key pair generator locked to " + spec.getName(), e.getMessage()); - } - } - - public void testRainbowRandomSig() - throws Exception - { - KeyPairGenerator kpg = KeyPairGenerator.getInstance("Rainbow", "BCPQC"); - - kpg.initialize(RainbowParameterSpec.rainbowIIIcompressed, new SecureRandom()); - - KeyPair kp = kpg.generateKeyPair(); - - Signature sig = Signature.getInstance("Rainbow", "BCPQC"); - - sig.initSign(kp.getPrivate(), new SecureRandom()); - - sig.update(msg, 0, msg.length); - - byte[] s = sig.sign(); - - sig = Signature.getInstance("Rainbow", "BCPQC"); - - sig.initVerify(kp.getPublic()); - - sig.update(msg, 0, msg.length); - - assertTrue(sig.verify(s)); - } - - /** - * count = 0 - * seed = 061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1 - * No public key as it's a bit "big". - */ - public void testRainbowKATSigCompressedIII() - throws Exception - { - byte[] privK = Hex.decode("8626ED79D451140800E03B59B956F8210E556067407D13DC90FA9E8B872BFB8F7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2D"); - byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8"); - byte[] s = Hex.decode(" 451F524FEF128EDBE93814C041D5EDD2C8A0226E05E13942B5B832C864A96184261745A5B530D09D51773C3E6F3C8297E3A8E6E4DBD23E56BDA10B5C3A491F7A5D9EA819D712FC6565429F965FD7264041E5F2007085DE29930B20B187BB9E5BC4BCAC01C35CABC97F5EC6476C42138C3D18A1DBD23BA22B31B21BDBE5421AC1B837A793123C80E2B5028A0763872E76E45F6AA9D675E2D667E6F68024D5EF1143D21713"); - KeyPairGenerator kpg = KeyPairGenerator.getInstance("Rainbow", "BCPQC"); - SecureRandom katRandom = new NISTSecureRandom(Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"), null); - - kpg.initialize(RainbowParameterSpec.rainbowIIIcompressed, katRandom); - - KeyPair kp = kpg.generateKeyPair(); - - PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp.getPrivate().getEncoded()); - - ASN1OctetString privEnc = ASN1OctetString.getInstance(privInfo.parsePrivateKey()); - - assertTrue(Arrays.areEqual(privK, privEnc.getOctets())); - - doKatTest(kp, msg, s, katRandom); - } - - public void testRainbowKATSigCompressedV() - throws Exception - { - byte[] privK = Hex.decode("8626ED79D451140800E03B59B956F8210E556067407D13DC90FA9E8B872BFB8F7C9935A0B07694AA0C6D10E4DB6B1ADD2FD81A25CCB148032DCD739936737F2D"); - byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8"); - byte[] s = Hex.decode(" D1F97D1310F57AF3509F66307985B7F341234CE8F7516E4B61F9E53B1282CE66B9526321C66954E1753D1A9C8BA4012B9C5A211F0287C72705141F71A9AAEC350E81F6EC67ED10E1BD61DCDFA4AC87553563E0FEE31927E5877741D5DCDF03C44E50CF80BB3D15856AF49F2C68A7EDAC52FD2957F96A7113DCE51785EDF0AB8538C1EAAD694E8514CDC7872664412BCF9884C185BADE87781016826E32E08C1EC6275C6F8588A11FF6575D704505D4AB794D047BEC1104C00DAD3BCFC2DE42267B3552BD74090543C9478050169FCCFBC0E9BA11"); - KeyPairGenerator kpg = KeyPairGenerator.getInstance("Rainbow", "BCPQC"); - SecureRandom katRandom = new NISTSecureRandom(Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"), null); - - kpg.initialize(RainbowParameterSpec.rainbowVcompressed, katRandom); - - KeyPair kp = kpg.generateKeyPair(); - - PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp.getPrivate().getEncoded()); - - ASN1OctetString privEnc = ASN1OctetString.getInstance(privInfo.parsePrivateKey()); - - assertTrue(Arrays.areEqual(privK, privEnc.getOctets())); - - doKatTest(kp, msg, s, katRandom); - } - - public void testRainbowKATSigClassicIII() - throws Exception - { - byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8"); - byte[] s = Hex.decode("6033C99A65042BE545EED707341BD14F73CA178F2A5B244A87E847DCAB29A9086676D7A7A4B35E3904A9EDD7B399B1BD104A19373A415029BCCD4C707B416EED683F13A9189EF0BDC151116CBF6D6A9D4BC019FAA58FD770B6F567A410C700B48C488A375C33866F3FEBB8DEDF239C64FF9A36F092E3D6192B9A0726B06672A540A892FA7BA47DBE7F3E66BF394ED328A107B8EDCEB39AD2E43C6EE441F39ECE871397AC"); - KeyPairGenerator kpg = KeyPairGenerator.getInstance("Rainbow", "BCPQC"); - SecureRandom katRandom = new NISTSecureRandom(Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"), null); - - kpg.initialize(RainbowParameterSpec.rainbowIIIclassic, katRandom); - - doKatTest(kpg.generateKeyPair(), msg, s, katRandom); - } - - public void testRainbowKATSigClassicV() - throws Exception - { - byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8"); - byte[] s = Hex.decode("15040F890F2BF56F8B04B1D8B9BA21D303C490868A0A10C9FFC04A2AF9D1F3122D14F7C6D5E0B1D914CC23D763C061B2FD34DF8CB0D75F12111244241FA7A136C440C2D40782390FE5EF3C15ED5539285B437DA0447E361853E98982E1F16AA0506BABFFBBA8282BAA0A307C50EBA79596AD26EBECE897E7B4DE3B601A515C08775526522915ED03F08BAA23AFED4224C8E50ED67FBCCFAB62C58872CE880C850D3A03F21B2703C5C085FA410A5FCB3559E50D6BBC6A06FABA309962F2922E0D014C5EB074090543C9478050169FCCFBC0E9BA11"); - KeyPairGenerator kpg = KeyPairGenerator.getInstance("Rainbow", "BCPQC"); - SecureRandom katRandom = new NISTSecureRandom(Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"), null); - - kpg.initialize(RainbowParameterSpec.rainbowVclassic, katRandom); - - doKatTest(kpg.generateKeyPair(), msg, s, katRandom); - } - - public void testRainbowKATSigCircumIII() - throws Exception - { - byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8"); - byte[] s = Hex.decode("451F524FEF128EDBE93814C041D5EDD2C8A0226E05E13942B5B832C864A96184261745A5B530D09D51773C3E6F3C8297E3A8E6E4DBD23E56BDA10B5C3A491F7A5D9EA819D712FC6565429F965FD7264041E5F2007085DE29930B20B187BB9E5BC4BCAC01C35CABC97F5EC6476C42138C3D18A1DBD23BA22B31B21BDBE5421AC1B837A793123C80E2B5028A0763872E76E45F6AA9D675E2D667E6F68024D5EF1143D21713"); - KeyPairGenerator kpg = KeyPairGenerator.getInstance("Rainbow", "BCPQC"); - SecureRandom katRandom = new NISTSecureRandom(Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"), null); - - kpg.initialize(RainbowParameterSpec.rainbowIIIcircumzenithal, katRandom); - - doKatTest(kpg.generateKeyPair(), msg, s, katRandom); - } - - public void testRainbowKATSigCircumV() - throws Exception - { - byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8"); - byte[] s = Hex.decode("D1F97D1310F57AF3509F66307985B7F341234CE8F7516E4B61F9E53B1282CE66B9526321C66954E1753D1A9C8BA4012B9C5A211F0287C72705141F71A9AAEC350E81F6EC67ED10E1BD61DCDFA4AC87553563E0FEE31927E5877741D5DCDF03C44E50CF80BB3D15856AF49F2C68A7EDAC52FD2957F96A7113DCE51785EDF0AB8538C1EAAD694E8514CDC7872664412BCF9884C185BADE87781016826E32E08C1EC6275C6F8588A11FF6575D704505D4AB794D047BEC1104C00DAD3BCFC2DE42267B3552BD74090543C9478050169FCCFBC0E9BA11"); - KeyPairGenerator kpg = KeyPairGenerator.getInstance("Rainbow", "BCPQC"); - SecureRandom katRandom = new NISTSecureRandom(Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"), null); - - kpg.initialize(RainbowParameterSpec.rainbowVcircumzenithal, katRandom); - - doKatTest(kpg.generateKeyPair(), msg, s, katRandom); - } - - private static void doKatTest(KeyPair kp, byte[] msg, byte[] s, SecureRandom katRandom) - throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException, SignatureException - { - Signature sig = Signature.getInstance("Rainbow", "BCPQC"); - - sig.initSign(kp.getPrivate(), katRandom); - - sig.update(msg, 0, msg.length); - - byte[] genS = sig.sign(); - - assertTrue(Arrays.areEqual(s, genS)); - - sig = Signature.getInstance("Rainbow", "BCPQC"); - - sig.initVerify(kp.getPublic()); - - sig.update(msg, 0, msg.length); - - assertTrue(sig.verify(s)); - } - - private static class RiggedRandom - extends SecureRandom - { - public void nextBytes(byte[] bytes) - { - for (int i = 0; i != bytes.length; i++) - { - bytes[i] = (byte)(i & 0xff); - } - } - } -} diff --git a/util/src/main/java/org/bouncycastle/asn1/bsi/BSIObjectIdentifiers.java b/util/src/main/java/org/bouncycastle/asn1/bsi/BSIObjectIdentifiers.java index 290b3f2640..ec27b05e1f 100644 --- a/util/src/main/java/org/bouncycastle/asn1/bsi/BSIObjectIdentifiers.java +++ b/util/src/main/java/org/bouncycastle/asn1/bsi/BSIObjectIdentifiers.java @@ -93,23 +93,23 @@ public interface BSIObjectIdentifiers static final ASN1ObjectIdentifier ecka_eg_SessionKDF_AES192 = ecka_eg_SessionKDF.branch("3"); static final ASN1ObjectIdentifier ecka_eg_SessionKDF_AES256 = ecka_eg_SessionKDF.branch("4"); - /** AES encryption (CBC) and authentication (CMAC) + /* AES encryption (CBC) and authentication (CMAC) * OID: 0.4.0.127.0.7.1.x */ //TODO: replace "1" with correct OID //static final ASN1ObjectIdentifier aes_cbc_cmac = algorithm.branch("1"); - /** AES encryption (CBC) and authentication (CMAC) with 128 bit + /* AES encryption (CBC) and authentication (CMAC) with 128 bit * OID: 0.4.0.127.0.7.1.x.y1 */ //TODO: replace "1" with correct OID //static final ASN1ObjectIdentifier id_aes128_CBC_CMAC = aes_cbc_cmac.branch("1"); - /** AES encryption (CBC) and authentication (CMAC) with 192 bit + /* AES encryption (CBC) and authentication (CMAC) with 192 bit * OID: 0.4.0.127.0.7.1.x.y2 */ //TODO: replace "1" with correct OID //static final ASN1ObjectIdentifier id_aes192_CBC_CMAC = aes_cbc_cmac.branch("1"); - /** AES encryption (CBC) and authentication (CMAC) with 256 bit + /* AES encryption (CBC) and authentication (CMAC) with 256 bit * OID: 0.4.0.127.0.7.1.x.y3 */ //TODO: replace "1" with correct OID //static final ASN1ObjectIdentifier id_aes256_CBC_CMAC = aes_cbc_cmac.branch("1"); From c56443592730db9e34590b080727545c2e588aa1 Mon Sep 17 00:00:00 2001 From: Gefei Li Date: Sun, 20 Apr 2025 08:17:50 +0000 Subject: [PATCH 325/890] Update AEADProtectedPGPSecretKeyTest --- .../test/AEADProtectedPGPSecretKeyTest.java | 47 ++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/AEADProtectedPGPSecretKeyTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/AEADProtectedPGPSecretKeyTest.java index c9eb712af7..df48813c75 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/AEADProtectedPGPSecretKeyTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/AEADProtectedPGPSecretKeyTest.java @@ -20,6 +20,7 @@ import org.bouncycastle.bcpg.SecretKeyPacket; import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator; import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters; import org.bouncycastle.jce.provider.BouncyCastleProvider; @@ -45,6 +46,7 @@ import org.bouncycastle.openpgp.operator.jcajce.JcePBEProtectionRemoverFactory; import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilder; import org.bouncycastle.util.Strings; +import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyEncryptorBuilder; import org.bouncycastle.util.encoders.Hex; public class AEADProtectedPGPSecretKeyTest @@ -365,14 +367,57 @@ private void lockUnlockKeyJca( keyPair.getPrivateKey().getPrivateKeyDataPacket().getEncoded(), dec.getPrivateKeyDataPacket().getEncoded()); } - private void reencryptKey() throws PGPException { + private void reencryptKey() + throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException + { reencryptKeyBc(); reencryptKeyJca(); } private void reencryptKeyJca() + throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, PGPException { + BouncyCastleProvider prov = new BouncyCastleProvider(); + KeyPairGenerator eddsaGen = KeyPairGenerator.getInstance("EdDSA", prov); + eddsaGen.initialize(new ECNamedCurveGenParameterSpec("ed25519")); + KeyPair kp = eddsaGen.generateKeyPair(); + Date creationTime = currentTimeRounded(); + String passphrase = "recycle"; + + PGPKeyPair keyPair = new JcaPGPKeyPair(PublicKeyPacket.VERSION_6, PublicKeyAlgorithmTags.Ed25519, kp, creationTime); + PBESecretKeyEncryptor cfbEncBuilder = new JcePBESecretKeyEncryptorBuilder(SymmetricKeyAlgorithmTags.AES_128) + .setProvider(prov) + .setSecureRandom(CryptoServicesRegistrar.getSecureRandom()) + .build(passphrase.toCharArray()); + PGPDigestCalculatorProvider digestProv = new JcaPGPDigestCalculatorProviderBuilder() + .setProvider(prov) + .build(); + + // Encrypt key using CFB mode + PGPSecretKey cfbEncKey = new PGPSecretKey( + keyPair.getPrivateKey(), + keyPair.getPublicKey(), + digestProv.get(HashAlgorithmTags.SHA1), + true, + cfbEncBuilder); + + PBESecretKeyDecryptor cfbDecryptor = new JcePBESecretKeyDecryptorBuilder(digestProv) + .setProvider(prov) + .build(passphrase.toCharArray()); + + JcaAEADSecretKeyEncryptorBuilder aeadEncBuilder = new JcaAEADSecretKeyEncryptorBuilder( + AEADAlgorithmTags.OCB, SymmetricKeyAlgorithmTags.AES_128, S2K.Argon2Params.memoryConstrainedParameters()) + .setProvider(prov); + + PGPSecretKey aeadEncKey = PGPSecretKey.copyWithNewPassword( + cfbEncKey, + cfbDecryptor, + aeadEncBuilder.build(passphrase.toCharArray(), cfbEncKey.getPublicKey().getPublicKeyPacket())); + PBESecretKeyDecryptor aeadDecryptor = new JcePBESecretKeyDecryptorBuilder(digestProv) + .setProvider(prov) + .build(passphrase.toCharArray()); + isNotNull(aeadEncKey.extractPrivateKey(aeadDecryptor)); } private void reencryptKeyBc() From f0f60721b67431f9bb69a269040020854734f402 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 21 Apr 2025 14:55:57 +1000 Subject: [PATCH 326/890] Java 4 compatibility. --- .../crypto/digests/BufferBaseDigest.java | 22 +- .../crypto/engines/AEADBaseEngine.java | 218 +++++++++++------- .../crypto/engines/AsconBaseEngine.java | 6 +- .../crypto/engines/ElephantEngine.java | 80 +++---- .../crypto/engines/RomulusEngine.java | 27 ++- 5 files changed, 212 insertions(+), 141 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java index 55c27d83ce..c6cdaed6f2 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/BufferBaseDigest.java @@ -8,10 +8,20 @@ abstract class BufferBaseDigest implements ExtendedDigest { - protected enum ProcessingBufferType + protected static class ProcessingBufferType { - Buffered, - Immediate, + public static final int BUFFERED = 0; + public static final int IMMEDIATE = 1; + + public static final ProcessingBufferType Buffered = new ProcessingBufferType(BUFFERED); + public static final ProcessingBufferType Immediate = new ProcessingBufferType(IMMEDIATE); + + private final int ord; + + ProcessingBufferType(int ord) + { + this.ord = ord; + } } protected int DigestSize; @@ -25,12 +35,12 @@ protected BufferBaseDigest(ProcessingBufferType type, int BlockSize) { this.BlockSize = BlockSize; m_buf = new byte[BlockSize]; - switch (type) + switch (type.ord) { - case Buffered: + case ProcessingBufferType.BUFFERED: processor = new BufferedProcessor(); break; - case Immediate: + case ProcessingBufferType.IMMEDIATE: processor = new ImmediateProcessor(); break; } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java index 6cd536837b..2f41a40749 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java @@ -17,38 +17,88 @@ abstract class AEADBaseEngine implements AEADCipher { - protected enum ProcessingBufferType + protected static class ProcessingBufferType { - Buffered, // Store a (aad) block size of input and process after the input size exceeds the buffer size - Immediate, //process the input immediately when the input size is equal or greater than the block size + public static final int BUFFERED = 0; // Store a (aad) block size of input and process after the input size exceeds the buffer size + public static final int IMMEDIATE = 1; //process the input immediately when the input size is equal or greater than the block size + + public static final ProcessingBufferType Buffered = new ProcessingBufferType(BUFFERED); + public static final ProcessingBufferType Immediate = new ProcessingBufferType(IMMEDIATE); + + private final int ord; + + ProcessingBufferType(int ord) + { + this.ord = ord; + } } - protected enum AADOperatorType + protected static class AADOperatorType { - Default, - Counter,//add a counter to count the size of AAD - Stream //process AAD data during the process data, used for elephant + public static final int DEFAULT = 0; + public static final int COUNTER = 1;//add a counter to count the size of AAD + public static final int STREAM = 2; //process AAD data during the process data, used for elephant + + public static final AADOperatorType Default = new AADOperatorType(DEFAULT); + public static final AADOperatorType Counter = new AADOperatorType(COUNTER); + public static final AADOperatorType Stream = new AADOperatorType(STREAM); + + private final int ord; + + AADOperatorType(int ord) + { + this.ord = ord; + } } - protected enum DataOperatorType + protected static class DataOperatorType { - Default, - Counter, - Stream, - StreamCipher + public static final int DEFAULT = 0; + public static final int COUNTER = 1; + public static final int STREAM = 2; + public static final int STREAM_CIPHER = 3; + + public static final DataOperatorType Default = new DataOperatorType(DEFAULT); + public static final DataOperatorType Counter = new DataOperatorType(COUNTER); + public static final DataOperatorType Stream = new DataOperatorType(STREAM); + public static final DataOperatorType StreamCipher = new DataOperatorType(STREAM_CIPHER); + + private final int ord; + + DataOperatorType(int ord) + { + this.ord = ord; + } } - protected enum State + protected static class State { - Uninitialized, - EncInit, - EncAad, // can process AAD - EncData, // cannot process AAD - EncFinal, - DecInit, - DecAad, // can process AAD - DecData, // cannot process AAD - DecFinal, + public static final int UNINITIALIZED = 0; + public static final int ENC_INIT = 1; + public static final int ENC_AAD = 2; // can process AAD + public static final int ENC_DATA = 3; // cannot process AAD + public static final int ENC_FINAL = 4; + public static final int DEC_INIT = 5; + public static final int DEC_AAD = 6; // can process AAD + public static final int DEC_DATA = 7; // cannot process AAD + public static final int DEC_FINAL = 8; + + public static final State Uninitialized = new State(UNINITIALIZED); + public static final State EncInit = new State(ENC_INIT); + public static final State EncAad = new State(ENC_AAD); + public static final State EncData = new State(ENC_DATA); + public static final State EncFinal = new State(ENC_FINAL); + public static final State DecInit = new State(DEC_INIT); + public static final State DecAad = new State(DEC_AAD); + public static final State DecData = new State(DEC_DATA); + public static final State DecFinal = new State(DEC_FINAL); + + final int ord; + + State(int ord) + { + this.ord = ord; + } } protected boolean forEncryption; @@ -174,19 +224,19 @@ protected void reset(boolean clearMac) Arrays.fill(m_aad, (byte)0); m_aadPos = 0; } - switch (m_state) + switch (m_state.ord) { - case DecInit: - case EncInit: + case State.DEC_INIT: + case State.ENC_INIT: break; - case DecAad: - case DecData: - case DecFinal: + case State.DEC_AAD: + case State.DEC_DATA: + case State.DEC_FINAL: m_state = State.DecFinal; break; - case EncAad: - case EncData: - case EncFinal: + case State.ENC_AAD: + case State.ENC_DATA: + case State.ENC_FINAL: m_state = State.EncFinal; return; default: @@ -198,49 +248,49 @@ protected void reset(boolean clearMac) protected void setInnerMembers(ProcessingBufferType type, AADOperatorType aadOperatorType, DataOperatorType dataOperatorType) { - switch (type) + switch (type.ord) { - case Buffered: + case ProcessingBufferType.BUFFERED: processor = new BufferedAADProcessor(); break; - case Immediate: + case ProcessingBufferType.IMMEDIATE: processor = new ImmediateAADProcessor(); break; } m_bufferSizeDecrypt = BlockSize + MAC_SIZE; - switch (aadOperatorType) + switch (aadOperatorType.ord) { - case Default: + case AADOperatorType.DEFAULT: m_aad = new byte[AADBufferSize]; aadOperator = new DefaultAADOperator(); break; - case Counter: + case AADOperatorType.COUNTER: m_aad = new byte[AADBufferSize]; aadOperator = new CounterAADOperator(); break; - case Stream: + case AADOperatorType.STREAM: AADBufferSize = 0; aadOperator = new StreamAADOperator(); break; } - switch (dataOperatorType) + switch (dataOperatorType.ord) { - case Default: + case DataOperatorType.DEFAULT: m_buf = new byte[m_bufferSizeDecrypt]; dataOperator = new DefaultDataOperator(); break; - case Counter: + case DataOperatorType.COUNTER: m_buf = new byte[m_bufferSizeDecrypt]; dataOperator = new CounterDataOperator(); break; - case Stream: + case DataOperatorType.STREAM: m_buf = new byte[MAC_SIZE]; dataOperator = new StreamDataOperator(); break; - case StreamCipher: + case DataOperatorType.STREAM_CIPHER: BlockSize = 0; m_buf = new byte[m_bufferSizeDecrypt]; dataOperator = new StreamCipherOperator(); @@ -862,16 +912,16 @@ public int getUpdateOutputSize(int len) protected int getTotalBytesForUpdate(int len) { int total = processor.getUpdateOutputSize(len); - switch (m_state) + switch (m_state.ord) { - case DecInit: - case DecAad: - case DecData: - case DecFinal: + case State.DEC_INIT: + case State.DEC_AAD: + case State.DEC_DATA: + case State.DEC_FINAL: total = Math.max(0, total + m_bufPos - MAC_SIZE); break; - case EncData: - case EncFinal: + case State.ENC_DATA: + case State.ENC_FINAL: total = Math.max(0, total + m_bufPos); break; default: @@ -884,15 +934,15 @@ public int getOutputSize(int len) { int total = Math.max(0, len); - switch (m_state) + switch (m_state.ord) { - case DecInit: - case DecAad: - case DecData: - case DecFinal: + case State.DEC_INIT: + case State.DEC_AAD: + case State.DEC_DATA: + case State.DEC_FINAL: return Math.max(0, total + m_bufPos - MAC_SIZE); - case EncData: - case EncFinal: + case State.ENC_DATA: + case State.ENC_FINAL: return total + m_bufPos + MAC_SIZE; default: return total + MAC_SIZE; @@ -901,18 +951,18 @@ public int getOutputSize(int len) protected void checkAAD() { - switch (m_state) + switch (m_state.ord) { - case DecInit: + case State.DEC_INIT: m_state = State.DecAad; break; - case EncInit: + case State.ENC_INIT: m_state = State.EncAad; break; - case DecAad: - case EncAad: + case State.DEC_AAD: + case State.ENC_AAD: break; - case EncFinal: + case State.ENC_FINAL: throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption"); default: throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); @@ -921,21 +971,21 @@ protected void checkAAD() protected boolean checkData(boolean isDoFinal) { - switch (m_state) + switch (m_state.ord) { - case DecInit: - case DecAad: + case State.DEC_INIT: + case State.DEC_AAD: finishAAD(State.DecData, isDoFinal); return false; - case EncInit: - case EncAad: + case State.ENC_INIT: + case State.ENC_AAD: finishAAD(State.EncData, isDoFinal); return true; - case DecData: + case State.DEC_DATA: return false; - case EncData: + case State.ENC_DATA: return true; - case EncFinal: + case State.ENC_FINAL: throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption"); default: throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); @@ -969,12 +1019,12 @@ protected final void ensureInitialized() // Used for Grain128 AEAD and Romulus Engine protected void finishAAD1(State nextState) { - switch (m_state) + switch (m_state.ord) { - case DecInit: - case DecAad: - case EncInit: - case EncAad: + case State.DEC_INIT: + case State.DEC_AAD: + case State.ENC_INIT: + case State.ENC_AAD: { processFinalAAD(); break; @@ -989,10 +1039,10 @@ protected void finishAAD1(State nextState) protected void finishAAD2(State nextState) { // State indicates whether we ever received AAD - switch (m_state) + switch (m_state.ord) { - case DecAad: - case EncAad: + case State.DEC_AAD: + case State.ENC_AAD: { processFinalAAD(); break; @@ -1009,16 +1059,16 @@ protected void finishAAD2(State nextState) protected void finishAAD3(State nextState, boolean isDoFinal) { // State indicates whether we ever received AAD - switch (m_state) + switch (m_state.ord) { - case DecInit: - case DecAad: + case State.DEC_INIT: + case State.DEC_AAD: if (!isDoFinal && dataOperator.getLen() <= MAC_SIZE) { return; } - case EncInit: - case EncAad: + case State.ENC_INIT: + case State.ENC_AAD: processFinalAAD(); break; } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java index b72a3efd3e..6f4792c428 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AsconBaseEngine.java @@ -23,10 +23,10 @@ abstract class AsconBaseEngine protected void finishAAD(State nextState, boolean isDofinal) { // State indicates whether we ever received AAD - switch (m_state) + switch (m_state.ord) { - case DecAad: - case EncAad: + case State.DEC_AAD: + case State.ENC_AAD: this.processFinalAAD(); p.p(nr); break; diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java index aa2488bdd9..fa5e7a1b4f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/ElephantEngine.java @@ -396,23 +396,23 @@ protected void processBufferAAD(byte[] input, int inOff) @Override public int getUpdateOutputSize(int len) { - switch (m_state) + switch (m_state.ord) { - case Uninitialized: + case State.UNINITIALIZED: throw new IllegalArgumentException(algorithmName + " needs call init function before getUpdateOutputSize"); - case DecFinal: - case EncFinal: + case State.DEC_FINAL: + case State.ENC_FINAL: return 0; - case EncAad: - case EncData: - case EncInit: + case State.ENC_AAD: + case State.ENC_DATA: + case State.ENC_INIT: { int total = m_bufPos + len; return total - total % BlockSize; } - case DecAad: - case DecData: - case DecInit: + case State.DEC_AAD: + case State.DEC_DATA: + case State.DEC_INIT: { int total = Math.max(0, m_bufPos + len - MAC_SIZE); return total - total % BlockSize; @@ -424,16 +424,16 @@ public int getUpdateOutputSize(int len) @Override public int getOutputSize(int len) { - switch (m_state) + switch (m_state.ord) { - case Uninitialized: + case State.UNINITIALIZED: throw new IllegalArgumentException(algorithmName + " needs call init function before getUpdateOutputSize"); - case DecFinal: - case EncFinal: + case State.DEC_FINAL: + case State.ENC_FINAL: return 0; - case EncAad: - case EncData: - case EncInit: + case State.ENC_AAD: + case State.ENC_DATA: + case State.ENC_INIT: return len + m_bufPos + MAC_SIZE; } return Math.max(0, len + m_bufPos - MAC_SIZE); @@ -454,10 +454,10 @@ protected void processFinalAAD() adlen = aadOperator.getLen(); aadOperator.reset(); } - switch (m_state) + switch (m_state.ord) { - case EncInit: - case DecInit: + case State.ENC_INIT: + case State.DEC_INIT: processAADBytes(tag_buffer); break; } @@ -474,13 +474,13 @@ protected void reset(boolean clearMac) protected void checkAAD() { - switch (m_state) + switch (m_state.ord) { - case DecData: + case State.DEC_DATA: throw new IllegalArgumentException(algorithmName + " cannot process AAD when the length of the plaintext to be processed exceeds the a block size"); - case EncData: + case State.ENC_DATA: throw new IllegalArgumentException(algorithmName + " cannot process AAD when the length of the ciphertext to be processed exceeds the a block size"); - case EncFinal: + case State.ENC_FINAL: throw new IllegalArgumentException(algorithmName + " cannot be reused for encryption"); default: break; @@ -489,17 +489,17 @@ protected void checkAAD() protected boolean checkData(boolean isDofinal) { - switch (m_state) + switch (m_state.ord) { - case DecInit: - case DecAad: - case DecData: + case State.DEC_INIT: + case State.DEC_AAD: + case State.DEC_DATA: return false; - case EncInit: - case EncAad: - case EncData: + case State.ENC_INIT: + case State.ENC_AAD: + case State.ENC_DATA: return true; - case EncFinal: + case State.ENC_FINAL: throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption"); default: throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); @@ -509,22 +509,22 @@ protected boolean checkData(boolean isDofinal) private void processAADBytes(byte[] output) { int len = 0; - switch (m_state) + switch (m_state.ord) { - case DecInit: + case State.DEC_INIT: System.arraycopy(expanded_key, 0, current_mask, 0, BlockSize); System.arraycopy(npub, 0, output, 0, IV_SIZE); len += IV_SIZE; m_state = State.DecAad; break; - case EncInit: + case State.ENC_INIT: System.arraycopy(expanded_key, 0, current_mask, 0, BlockSize); System.arraycopy(npub, 0, output, 0, IV_SIZE); len += IV_SIZE; m_state = State.EncAad; break; - case DecAad: - case EncAad: + case State.DEC_AAD: + case State.ENC_AAD: // If adlen is divisible by BLOCK_SIZE, add an additional padding block if (adOff == adlen) { @@ -551,12 +551,12 @@ private void processAADBytes(byte[] output) } Arrays.fill(output, len + r_adlen, len + r_outlen, (byte)0); output[len + r_adlen] = 0x01; - switch (m_state) + switch (m_state.ord) { - case DecAad: + case State.DEC_AAD: m_state = State.DecData; break; - case EncAad: + case State.ENC_AAD: m_state = State.EncData; break; } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java index 43603495cd..06535cf482 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/RomulusEngine.java @@ -13,11 +13,22 @@ public class RomulusEngine extends AEADBaseEngine { - public enum RomulusParameters + public static class RomulusParameters { - RomulusM, - RomulusN, - RomulusT, + public static final int ROMULUS_M = 0; + public static final int ROMULUS_N = 1; + public static final int ROMULUS_T = 2; + + public static final RomulusParameters RomulusM = new RomulusParameters(ROMULUS_M); + public static final RomulusParameters RomulusN = new RomulusParameters(ROMULUS_N); + public static final RomulusParameters RomulusT = new RomulusParameters(ROMULUS_T); + + private final int ord; + + RomulusParameters(int ord) + { + this.ord = ord; + } } private byte[] k; @@ -81,17 +92,17 @@ public RomulusEngine(RomulusParameters romulusParameters) { KEY_SIZE = IV_SIZE = MAC_SIZE = BlockSize = AADBufferSize = 16; CNT = new byte[7]; - switch (romulusParameters) + switch (romulusParameters.ord) { - case RomulusM: + case RomulusParameters.ROMULUS_M: algorithmName = "Romulus-M"; instance = new RomulusM(); break; - case RomulusN: + case RomulusParameters.ROMULUS_N: algorithmName = "Romulus-N"; instance = new RomulusN(); break; - case RomulusT: + case RomulusParameters.ROMULUS_T: algorithmName = "Romulus-T"; AADBufferSize = 32; instance = new RomulusT(); From 095e0cc5e65634b4ef8712951e001c2c7006fa44 Mon Sep 17 00:00:00 2001 From: Gefei Li Date: Tue, 22 Apr 2025 02:18:10 +0000 Subject: [PATCH 327/890] OpenPGP API Phase 1 --- .../bcpg/ArmoredOutputStream.java | 35 + .../org/bouncycastle/bcpg/PublicKeyUtils.java | 1 - .../bcpg/sig/PreferredAEADCiphersuites.java | 5 + .../IntegrityProtectedInputStream.java | 84 + .../openpgp/PGPEncryptedData.java | 5 + .../openpgp/PGPEncryptedDataGenerator.java | 15 + .../bouncycastle/openpgp/PGPSignature.java | 66 +- .../openpgp/PGPSignatureException.java | 15 + ...ractOpenPGPDocumentSignatureGenerator.java | 306 ++ .../AbstractOpenPGPKeySignatureGenerator.java | 181 + .../openpgp/api/EncryptedDataPacketType.java | 37 + .../openpgp/api/KeyPairGeneratorCallback.java | 44 +- .../openpgp/api/KeyPassphraseProvider.java | 131 + .../api/MessageEncryptionMechanism.java | 140 + .../api/MissingMessagePassphraseCallback.java | 13 + .../bouncycastle/openpgp/api/OpenPGPApi.java | 240 ++ .../openpgp/api/OpenPGPCertificate.java | 3605 +++++++++++++++++ .../openpgp/api/OpenPGPDefaultPolicy.java | 261 ++ .../OpenPGPDetachedSignatureGenerator.java | 96 + .../OpenPGPDetachedSignatureProcessor.java | 298 ++ .../api/OpenPGPEncryptionNegotiator.java | 305 ++ .../openpgp/api/OpenPGPImplementation.java | 210 + .../bouncycastle/openpgp/api/OpenPGPKey.java | 583 +++ .../openpgp/api/OpenPGPKeyEditor.java | 477 +++ .../openpgp/api/OpenPGPKeyGenerator.java | 732 ++++ .../openpgp/api/OpenPGPKeyMaterialPool.java | 207 + .../api/OpenPGPKeyMaterialProvider.java | 40 + .../openpgp/api/OpenPGPKeyReader.java | 346 ++ .../openpgp/api/OpenPGPMessageGenerator.java | 650 +++ .../api/OpenPGPMessageInputStream.java | 1146 ++++++ .../api/OpenPGPMessageOutputStream.java | 471 +++ .../openpgp/api/OpenPGPMessageProcessor.java | 543 +++ .../openpgp/api/OpenPGPPolicy.java | 324 ++ .../openpgp/api/OpenPGPSignature.java | 716 ++++ .../openpgp/api/SignatureParameters.java | 331 ++ .../api/SignatureSubpacketsFunction.java | 2 +- .../openpgp/api/SubkeySelector.java | 24 + .../org/bouncycastle/openpgp/api/Utils.java | 118 + .../openpgp/api/bc/BcOpenPGPApi.java | 60 + .../api/bc/BcOpenPGPImplementation.java | 161 + .../openpgp/api/bc/BcOpenPGPKeyGenerator.java | 51 + .../api/bc/BcOpenPGPV6KeyGenerator.java | 79 - .../IncorrectOpenPGPSignatureException.java | 15 + .../InvalidEncryptionKeyException.java | 37 + .../exception/InvalidSigningKeyException.java | 33 + .../api/exception/KeyPassphraseException.java | 34 + .../MalformedOpenPGPSignatureException.java | 16 + .../exception/MissingIssuerCertException.java | 15 + .../api/exception/OpenPGPKeyException.java | 68 + .../exception/OpenPGPSignatureException.java | 21 + .../openpgp/api/jcajce/JcaOpenPGPApi.java | 63 + .../api/jcajce/JcaOpenPGPImplementation.java | 229 ++ .../api/jcajce/JcaOpenPGPKeyGenerator.java | 43 + .../api/jcajce/JcaOpenPGPV6KeyGenerator.java | 73 - .../openpgp/api/util/UTCUtil.java | 51 + .../PBESecretKeyDecryptorBuilder.java | 9 + .../PBESecretKeyDecryptorBuilderProvider.java | 15 + .../bc/BcCFBSecretKeyEncryptorFactory.java | 15 +- .../bc/BcPBESecretKeyDecryptorBuilder.java | 2 + ...cPBESecretKeyDecryptorBuilderProvider.java | 14 + .../JcaCFBSecretKeyEncryptorFactory.java | 10 +- .../JcePBESecretKeyDecryptorBuilder.java | 2 + ...ePBESecretKeyDecryptorBuilderProvider.java | 37 + .../bouncycastle/openpgp/OpenPGPTestKeys.java | 453 +++ .../openpgp/api/test/APITest.java | 32 + .../openpgp/api/test/AllTests.java | 67 + .../api/test/ChangeKeyPassphraseTest.java | 118 + .../api/test/OpenPGPCertificateTest.java | 886 ++++ ...OpenPGPDetachedSignatureProcessorTest.java | 275 ++ .../api/test/OpenPGPKeyEditorTest.java | 281 ++ .../api/test/OpenPGPKeyReaderTest.java | 91 + .../api/test/OpenPGPMessageGeneratorTest.java | 177 + .../api/test/OpenPGPMessageProcessorTest.java | 652 +++ .../api/test/OpenPGPV4KeyGenerationTest.java | 64 + .../api/test/OpenPGPV6KeyGeneratorTest.java | 384 +- .../openpgp/api/test/RegressionTest.java | 30 + .../test/StackMessagePassphraseCallback.java | 38 + .../StaticV6OpenPGPMessageGeneratorTest.java | 111 + .../openpgp/test/RegressionTest.java | 4 +- 79 files changed, 17300 insertions(+), 319 deletions(-) create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/IntegrityProtectedInputStream.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureException.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPKeySignatureGenerator.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/EncryptedDataPacketType.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/KeyPassphraseProvider.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/MessageEncryptionMechanism.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/MissingMessagePassphraseCallback.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPApi.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDefaultPolicy.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureGenerator.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureProcessor.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPEncryptionNegotiator.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPImplementation.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKey.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyEditor.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialProvider.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyReader.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageInputStream.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageOutputStream.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageProcessor.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPPolicy.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/SignatureParameters.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/SubkeySelector.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/Utils.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPApi.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPImplementation.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPKeyGenerator.java delete mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPV6KeyGenerator.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/exception/IncorrectOpenPGPSignatureException.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/exception/InvalidEncryptionKeyException.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/exception/InvalidSigningKeyException.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/exception/KeyPassphraseException.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/exception/MalformedOpenPGPSignatureException.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/exception/MissingIssuerCertException.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/exception/OpenPGPKeyException.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/exception/OpenPGPSignatureException.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPApi.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPImplementation.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPKeyGenerator.java delete mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPV6KeyGenerator.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/util/UTCUtil.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyDecryptorBuilder.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyDecryptorBuilderProvider.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBESecretKeyDecryptorBuilderProvider.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBESecretKeyDecryptorBuilderProvider.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/OpenPGPTestKeys.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/api/test/APITest.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/api/test/AllTests.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/api/test/ChangeKeyPassphraseTest.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPKeyEditorTest.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPKeyReaderTest.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV4KeyGenerationTest.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/api/test/RegressionTest.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/api/test/StackMessagePassphraseCallback.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/api/test/StaticV6OpenPGPMessageGeneratorTest.java diff --git a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredOutputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredOutputStream.java index aa957720ad..76947cfb8d 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredOutputStream.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredOutputStream.java @@ -612,6 +612,41 @@ public Builder addComment(String comment) return addHeader(COMMENT_HDR, comment); } + public Builder addEllipsizedComment(String comment) + { + int availableCommentCharsPerLine = 64 - (COMMENT_HDR.length() + 2); // ASCII armor width - header len + comment = comment.trim(); + + if (comment.length() > availableCommentCharsPerLine) + { + comment = comment.substring(0, availableCommentCharsPerLine - 1) + '…'; + } + addComment(comment); + return this; + } + + public Builder addSplitMultilineComment(String comment) + { + int availableCommentCharsPerLine = 64 - (COMMENT_HDR.length() + 2); // ASCII armor width - header len + + comment = comment.trim(); + for (String line : comment.split("\n")) + { + while (line.length() > availableCommentCharsPerLine) + { + // split comment into multiple lines + addComment(comment.substring(0, availableCommentCharsPerLine)); + line = line.substring(availableCommentCharsPerLine).trim(); + } + + if (!line.isEmpty()) + { + addComment(line); + } + } + return this; + } + /** * Set and replace the given header value with a single-line header. * If the value is

    null
    , this method will remove the header entirely. diff --git a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyUtils.java b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyUtils.java index 48c77ba566..70213770a4 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyUtils.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/PublicKeyUtils.java @@ -32,7 +32,6 @@ public static boolean isSigningAlgorithm(int publicKeyAlgorithm) // /** // * Return true, if the public key algorithm that corresponds to the given ID is capable of encryption. -// * // * @param publicKeyAlgorithm public key algorithm id // * @return true if algorithm can encrypt // */ diff --git a/pg/src/main/java/org/bouncycastle/bcpg/sig/PreferredAEADCiphersuites.java b/pg/src/main/java/org/bouncycastle/bcpg/sig/PreferredAEADCiphersuites.java index f58b25bb76..d99a01e9fa 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/sig/PreferredAEADCiphersuites.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/sig/PreferredAEADCiphersuites.java @@ -30,6 +30,11 @@ public class PreferredAEADCiphersuites */ private static final Combination AES_128_OCB = new Combination(SymmetricKeyAlgorithmTags.AES_128, AEADAlgorithmTags.OCB); + public static PreferredAEADCiphersuites DEFAULT() + { + return new PreferredAEADCiphersuites(false, new Combination[]{AES_128_OCB}); + } + /** * Create a new PreferredAEADAlgorithms signature subpacket from raw data. * diff --git a/pg/src/main/java/org/bouncycastle/openpgp/IntegrityProtectedInputStream.java b/pg/src/main/java/org/bouncycastle/openpgp/IntegrityProtectedInputStream.java new file mode 100644 index 0000000000..d553201d09 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/IntegrityProtectedInputStream.java @@ -0,0 +1,84 @@ +package org.bouncycastle.openpgp; + +import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * {@link InputStream} that performs verification of integrity protection upon {@link #close()}. + */ +public class IntegrityProtectedInputStream + extends FilterInputStream +{ + + private final PGPEncryptedData esk; + + public IntegrityProtectedInputStream(InputStream in, PGPEncryptedData dataPacket) + { + super(in); + this.esk = dataPacket; + } + + @Override + public int read() + throws IOException + { + int i = in.read(); + if (i == -1) + { + close(); + } + return i; + } + + @Override + public int read(byte[] b) + throws IOException + { + int r = in.read(b); + if (r == -1) + { + close(); + } + return r; + } + + @Override + public int read(byte[] b, int off, int len) + throws IOException + { + int r = in.read(b, off, len); + if (r == -1) + { + close(); + } + return r; + } + + @Override + public void close() + throws IOException + { + super.close(); + if (esk.getEncData() instanceof SymmetricEncIntegrityPacket) + { + SymmetricEncIntegrityPacket seipd = (SymmetricEncIntegrityPacket) esk.getEncData(); + if (seipd.getVersion() == SymmetricEncIntegrityPacket.VERSION_1) + { + try + { + if (!esk.verify()) + { + throw new PGPException("Malformed integrity protected data."); + } + } + catch (PGPException e) + { + throw new IOException(e); + } + } + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedData.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedData.java index fba7395286..c5083b851f 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedData.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedData.java @@ -175,6 +175,11 @@ public boolean isIntegrityProtected() return (encData instanceof SymmetricEncIntegrityPacket); } + public InputStreamPacket getEncData() + { + return encData; + } + /** * Checks whether the packet is protected using an AEAD algorithm. * diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedDataGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedDataGenerator.java index a0e3d295ca..046e5c1c1e 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedDataGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedDataGenerator.java @@ -88,6 +88,7 @@ public class PGPEncryptedDataGenerator // If true, force generation of a session key, even if we only have a single password-based encryption method // and could therefore use the S2K output as session key directly. private boolean forceSessionKey = true; + private SessionKeyExtractionCallback sessionKeyExtractionCallback = null; /** * Base constructor. @@ -140,6 +141,10 @@ public void addMethod(PGPKeyEncryptionMethodGenerator method) methods.add(method); } + public void setSessionKeyExtractionCallback(SessionKeyExtractionCallback callback) + { + this.sessionKeyExtractionCallback = callback; + } /** * Create an OutputStream based on the configured methods. @@ -213,6 +218,11 @@ else if (directS2K) messageKey = sessionKey; } + if (sessionKeyExtractionCallback != null) + { + sessionKeyExtractionCallback.extractSessionKey(new PGPSessionKey(defAlgorithm, sessionKey)); + } + PGPDataEncryptor dataEncryptor = dataEncryptorBuilder.build(messageKey); digestCalc = dataEncryptor.getIntegrityCalculator(); BCPGHeaderObject encOut; @@ -441,4 +451,9 @@ public void close() this.finish(); } } + + public interface SessionKeyExtractionCallback + { + void extractSessionKey(PGPSessionKey sessionKey); + } } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java index 79419f1a11..68cf06c2b0 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java @@ -5,6 +5,7 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.Date; +import java.util.Iterator; import java.util.List; import org.bouncycastle.asn1.ASN1EncodableVector; @@ -23,6 +24,8 @@ import org.bouncycastle.bcpg.TrustPacket; import org.bouncycastle.bcpg.sig.IssuerFingerprint; import org.bouncycastle.bcpg.sig.IssuerKeyID; +import org.bouncycastle.bcpg.sig.RevocationReason; +import org.bouncycastle.bcpg.sig.RevocationReasonTags; import org.bouncycastle.math.ec.rfc8032.Ed25519; import org.bouncycastle.math.ec.rfc8032.Ed448; import org.bouncycastle.openpgp.operator.PGPContentVerifier; @@ -635,11 +638,30 @@ public long getKeyID() public List getKeyIdentifiers() { List identifiers = new ArrayList(); - identifiers.addAll(getHashedKeyIdentifiers()); - identifiers.addAll(getUnhashedKeyIdentifiers()); + if (getVersion() <= SignaturePacket.VERSION_3) + { + identifiers.add(new KeyIdentifier(getKeyID())); + } + else + { + identifiers.addAll(getHashedKeyIdentifiers()); + identifiers.addAll(getUnhashedKeyIdentifiers()); + } return identifiers; } + public boolean hasKeyIdentifier(KeyIdentifier identifier) + { + for (Iterator it = getKeyIdentifiers().iterator(); it.hasNext(); ) + { + if (((KeyIdentifier)it.next()).matches(identifier)) + { + return true; + } + } + return false; + } + /** * Return a list of all {@link KeyIdentifier KeyIdentifiers} that could be derived from * any {@link IssuerFingerprint} or {@link IssuerKeyID} subpackets of the hashed signature @@ -905,6 +927,46 @@ public static boolean isCertification(int signatureType) || PGPSignature.POSITIVE_CERTIFICATION == signatureType; } + public static boolean isRevocation(int signatureType) + { + return PGPSignature.KEY_REVOCATION == signatureType + || PGPSignature.CERTIFICATION_REVOCATION == signatureType + || PGPSignature.SUBKEY_REVOCATION == signatureType; + } + + public boolean isHardRevocation() + { + if (!isRevocation(getSignatureType())) + { + return false; // no revocation + } + + if (!hasSubpackets()) + { + return true; // consider missing subpackets (and therefore missing reason) as hard revocation + } + + // only consider reasons from the hashed packet area + RevocationReason reason = getHashedSubPackets() != null ? + getHashedSubPackets().getRevocationReason() : null; + if (reason == null) + { + return true; // missing reason packet is hard + } + + byte code = reason.getRevocationReason(); + if (code >= 100 && code <= 110) + { + // private / experimental reasons are considered hard + return true; + } + + // Reason is not from the set of known soft reasons + return code != RevocationReasonTags.KEY_SUPERSEDED && + code != RevocationReasonTags.KEY_RETIRED && + code != RevocationReasonTags.USER_NO_LONGER_VALID; + } + /** * Return true, if the cryptographic signature encoding of the two signatures match. * diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureException.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureException.java new file mode 100644 index 0000000000..88b2887319 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureException.java @@ -0,0 +1,15 @@ +package org.bouncycastle.openpgp; + +public class PGPSignatureException + extends PGPException +{ + public PGPSignatureException(String message) + { + super(message); + } + + public PGPSignatureException(String message, Exception cause) + { + super(message, cause); + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java new file mode 100644 index 0000000000..a959f2c202 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java @@ -0,0 +1,306 @@ +package org.bouncycastle.openpgp.api; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.function.IntPredicate; + +import org.bouncycastle.bcpg.sig.PreferredAlgorithms; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyPair; +import org.bouncycastle.openpgp.PGPSignatureGenerator; +import org.bouncycastle.openpgp.api.exception.InvalidSigningKeyException; +import org.bouncycastle.openpgp.api.exception.KeyPassphraseException; + +public class AbstractOpenPGPDocumentSignatureGenerator> +{ + + protected final OpenPGPImplementation implementation; + protected final OpenPGPPolicy policy; + + // Below lists all use the same indexing + protected final List signatureGenerators = new ArrayList<>(); + protected final List signingKeys = new ArrayList<>(); + protected final List signatureCallbacks = new ArrayList<>(); + protected final List signingKeyPassphraseProviders = new ArrayList<>(); + + protected final KeyPassphraseProvider.DefaultKeyPassphraseProvider defaultKeyPassphraseProvider = + new KeyPassphraseProvider.DefaultKeyPassphraseProvider(); + + protected SubkeySelector signingKeySelector = new SubkeySelector() + { + @Override + public List select(OpenPGPCertificate certificate, + final OpenPGPPolicy policy) + { + List result = new ArrayList<>(); + for (Iterator it = certificate.getSigningKeys().iterator(); it.hasNext(); ) + { + OpenPGPCertificate.OpenPGPComponentKey key = it.next(); + if (policy.isAcceptablePublicKey(key.getPGPPublicKey())) + { + result.add(key); + } + } + return result; + } + }; + + public AbstractOpenPGPDocumentSignatureGenerator(OpenPGPImplementation implementation, OpenPGPPolicy policy) + { + this.implementation = implementation; + this.policy = policy; + } + + /** + * Replace the default signing key selector with a custom implementation. + * The signing key selector is responsible for selecting one or more signing subkeys from a signing key. + * + * @param signingKeySelector selector for signing (sub-)keys + * @return this + */ + public T setSigningKeySelector(SubkeySelector signingKeySelector) + { + this.signingKeySelector = Objects.requireNonNull(signingKeySelector); + return (T)this; + } + + /** + * Add a passphrase for unlocking signing keys to the set of available passphrases. + * + * @param passphrase passphrase + * @return this + */ + public T addKeyPassphrase(char[] passphrase) + { + defaultKeyPassphraseProvider.addPassphrase(passphrase); + return (T)this; + } + + /** + * Add an {@link OpenPGPKey} for message signing. + * The {@link #signingKeySelector} is responsible for selecting one or more subkeys of the key to sign with. + * If no (sub-)key in the signing key is capable of creating signatures, or if the key is expired or revoked, + * this method will throw an {@link InvalidSigningKeyException}. + * + * @param key OpenPGP key + * @return this + * @throws InvalidSigningKeyException if the key is not capable of signing + */ + public T addSigningKey( + OpenPGPKey key) + throws InvalidSigningKeyException + { + return addSigningKey(key, defaultKeyPassphraseProvider); + } + + /** + * Add an {@link OpenPGPKey} for message signing, using the provided {@link KeyPassphraseProvider} to + * unlock protected subkeys. + * The {@link #signingKeySelector} is responsible for selecting one or more subkeys of the key to sign with. + * If no (sub-)key in the signing key is capable of creating signatures, or if the key is expired or revoked, + * this method will throw an {@link InvalidSigningKeyException}. + * + * @param key OpenPGP key + * @param passphraseProvider provides the passphrase to unlock the signing key + * @return this + * @throws InvalidSigningKeyException if the OpenPGP key does not contain a usable signing subkey + */ + public T addSigningKey( + OpenPGPKey key, + KeyPassphraseProvider passphraseProvider) + throws InvalidSigningKeyException + { + return addSigningKey(key, passphraseProvider, null); + } + + /** + * Add an {@link OpenPGPKey} for message signing, using the {@link SignatureParameters.Callback} to + * allow modification of the signature contents. + * The {@link #signingKeySelector} is responsible for selecting one or more subkeys of the key to sign with. + * If no (sub-)key in the signing key is capable of creating signatures, or if the key is expired or revoked, + * this method will throw an {@link InvalidSigningKeyException}. + * + * @param key OpenPGP key + * @param signatureCallback optional callback to modify the signature contents with + * @return this + * @throws InvalidSigningKeyException if the OpenPGP key does not contain a usable signing subkey + */ + public T addSigningKey( + OpenPGPKey key, + SignatureParameters.Callback signatureCallback) + throws InvalidSigningKeyException + { + return addSigningKey(key, defaultKeyPassphraseProvider, signatureCallback); + } + + /** + * Add an {@link OpenPGPKey} for message signing, using the given {@link KeyPassphraseProvider} + * for unlocking protected subkeys and using the {@link SignatureParameters.Callback} to allow + * modification of the signature contents. + * The {@link #signingKeySelector} is responsible for selecting one or more subkeys of the key to sign with. + * If no (sub-)key in the signing key is capable of creating signatures, or if the key is expired or revoked, + * this method will throw an {@link InvalidSigningKeyException}. + * + * @param key OpenPGP key + * @param passphraseProvider key passphrase provider + * @param signatureCallback optional callback to modify the signature contents with + * @return this + * @throws InvalidSigningKeyException if the OpenPGP key does not contain a usable signing subkey + */ + public T addSigningKey( + OpenPGPKey key, + KeyPassphraseProvider passphraseProvider, + SignatureParameters.Callback signatureCallback) + throws InvalidSigningKeyException + { + List signingSubkeys = signingKeySelector.select(key, policy); + if (signingSubkeys.isEmpty()) + { + throw new InvalidSigningKeyException(key); + } + + for (Iterator it = signingSubkeys.iterator(); it.hasNext(); ) + { + OpenPGPKey.OpenPGPSecretKey signingKey = key.getSecretKey((OpenPGPCertificate.OpenPGPComponentKey)it.next()); + addSigningKey(signingKey, passphraseProvider, signatureCallback); + } + + return (T)this; + } + + /** + * Add the given signing (sub-)key for message signing, using the optional passphrase to unlock the + * key in case its locked, and using the given {@link SignatureParameters.Callback} to allow + * modification of the signature contents. + * + * @param signingKey signing (sub-)key + * @param passphrase optional subkey passphrase + * @param signatureCallback optional callback to modify the signature contents + * @return this + * @throws InvalidSigningKeyException if the subkey is not signing-capable + */ + public T addSigningKey( + OpenPGPKey.OpenPGPSecretKey signingKey, + char[] passphrase, + SignatureParameters.Callback signatureCallback) + throws InvalidSigningKeyException + { + return addSigningKey( + signingKey, + defaultKeyPassphraseProvider.addPassphrase(signingKey, passphrase), + signatureCallback); + } + + /** + * Add the given signing (sub-)key for message signing, using the passphrase provider to unlock the + * key in case its locked, and using the given {@link SignatureParameters.Callback} to allow + * modification of the signature contents. + * + * @param signingKey signing (sub-)key + * @param passphraseProvider passphrase provider for unlocking the subkey + * @param signatureCallback optional callback to modify the signature contents + * @return this + * @throws InvalidSigningKeyException if the subkey is not signing-capable + */ + public T addSigningKey( + OpenPGPKey.OpenPGPSecretKey signingKey, + KeyPassphraseProvider passphraseProvider, + SignatureParameters.Callback signatureCallback) + throws InvalidSigningKeyException + { + if (!signingKey.isSigningKey()) + { + throw new InvalidSigningKeyException(signingKey); + } + + signingKeys.add(signingKey); + signingKeyPassphraseProviders.add(passphraseProvider); + signatureCallbacks.add(signatureCallback); + return (T)this; + } + + protected PGPSignatureGenerator initSignatureGenerator( + OpenPGPKey.OpenPGPSecretKey signingKey, + KeyPassphraseProvider passphraseProvider, + SignatureParameters.Callback signatureCallback) + throws PGPException + { + SignatureParameters parameters = Utils.applySignatureParameters(signatureCallback, + SignatureParameters.dataSignature(policy).setSignatureHashAlgorithm(getPreferredHashAlgorithm(signingKey))); + + if (parameters == null) + { + throw new IllegalStateException("SignatureParameters Callback MUST NOT return null."); + } + + if (!signingKey.isSigningKey(parameters.getSignatureCreationTime())) + { + throw new InvalidSigningKeyException(signingKey); + } + + char[] passphrase = passphraseProvider.getKeyPassword(signingKey); + PGPKeyPair unlockedKey = signingKey.unlock(passphrase).getKeyPair(); + if (unlockedKey == null) + { + throw new KeyPassphraseException(signingKey, new PGPException("Cannot unlock secret key.")); + } + + return Utils.getPgpSignatureGenerator(implementation, signingKey.getPGPPublicKey(), + unlockedKey.getPrivateKey(), parameters, null, null); + } + + private int getPreferredHashAlgorithm(OpenPGPCertificate.OpenPGPComponentKey key) + { + // Determine the Hash Algorithm to use by inspecting the signing key's hash algorithm preferences + // TODO: Instead inspect the hash algorithm preferences of recipient certificates? + PreferredAlgorithms hashPreferences = key.getHashAlgorithmPreferences(); + if (hashPreferences != null) + { + int[] pref = Arrays.stream(hashPreferences.getPreferences()) + .filter(new IntPredicate() + { // Replace lambda with anonymous class for IntPredicate + @Override + public boolean test(int it) + { + return policy.isAcceptableDocumentSignatureHashAlgorithm(it, new Date()); + } + }) + .toArray(); + if (pref.length != 0) + { + return pref[0]; + } + } + return policy.getDefaultDocumentSignatureHashAlgorithm(); + } + + /** + * Set a callback that will be fired, if a passphrase for a protected signing key is missing. + * This can be used for example to implement interactive on-demand passphrase prompting. + * + * @param callback passphrase provider + * @return builder + */ + public T setMissingKeyPassphraseCallback(KeyPassphraseProvider callback) + { + defaultKeyPassphraseProvider.setMissingPassphraseCallback(callback); + return (T)this; + } + + protected void addSignToGenerator() + throws PGPException + { + for (int i = 0; i < signingKeys.size(); i++) + { + OpenPGPKey.OpenPGPSecretKey signingKey = signingKeys.get(i); + KeyPassphraseProvider keyPassphraseProvider = signingKeyPassphraseProviders.get(i); + SignatureParameters.Callback signatureCallback = signatureCallbacks.get(i); + PGPSignatureGenerator sigGen = initSignatureGenerator(signingKey, keyPassphraseProvider, signatureCallback); + signatureGenerators.add(sigGen); + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPKeySignatureGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPKeySignatureGenerator.java new file mode 100644 index 0000000000..927131bce9 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPKeySignatureGenerator.java @@ -0,0 +1,181 @@ +package org.bouncycastle.openpgp.api; + +import org.bouncycastle.bcpg.AEADAlgorithmTags; +import org.bouncycastle.bcpg.CompressionAlgorithmTags; +import org.bouncycastle.bcpg.HashAlgorithmTags; +import org.bouncycastle.bcpg.SignatureSubpacketTags; +import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.bouncycastle.bcpg.sig.Features; +import org.bouncycastle.bcpg.sig.KeyFlags; +import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites; +import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; + +public abstract class AbstractOpenPGPKeySignatureGenerator +{ + + /** + * Standard AEAD encryption preferences (SEIPDv2). + * By default, only announce support for OCB + AES. + */ + protected SignatureSubpacketsFunction defaultAeadAlgorithmPreferences = new SignatureSubpacketsFunction() + { + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS); + subpackets.setPreferredAEADCiphersuites(PreferredAEADCiphersuites.builder(false) + .addCombination(SymmetricKeyAlgorithmTags.AES_256, AEADAlgorithmTags.OCB) + .addCombination(SymmetricKeyAlgorithmTags.AES_192, AEADAlgorithmTags.OCB) + .addCombination(SymmetricKeyAlgorithmTags.AES_128, AEADAlgorithmTags.OCB)); + return subpackets; + } + }; + + /** + * Standard symmetric-key encryption preferences (SEIPDv1). + * By default, announce support for AES. + */ + protected SignatureSubpacketsFunction defaultSymmetricKeyPreferences = new SignatureSubpacketsFunction() + { + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_SYM_ALGS); + subpackets.setPreferredSymmetricAlgorithms(false, new int[]{ + SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192, SymmetricKeyAlgorithmTags.AES_128 + }); + return subpackets; + } + }; + + /** + * Standard signature hash algorithm preferences. + * By default, only announce SHA3 and SHA2 algorithms. + */ + protected SignatureSubpacketsFunction defaultHashAlgorithmPreferences = new SignatureSubpacketsFunction() + { + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_HASH_ALGS); + subpackets.setPreferredHashAlgorithms(false, new int[]{ + HashAlgorithmTags.SHA3_512, HashAlgorithmTags.SHA3_256, + HashAlgorithmTags.SHA512, HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA256 + }); + return subpackets; + } + }; + + /** + * Standard compression algorithm preferences. + * By default, announce support for all known algorithms. + */ + protected SignatureSubpacketsFunction defaultCompressionAlgorithmPreferences = new SignatureSubpacketsFunction() + { + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_COMP_ALGS); + subpackets.setPreferredCompressionAlgorithms(false, new int[]{ + CompressionAlgorithmTags.UNCOMPRESSED, CompressionAlgorithmTags.ZIP, + CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2 + }); + return subpackets; + } + }; + + /** + * Standard features to announce. + * By default, announce SEIPDv1 (modification detection) and SEIPDv2. + */ + protected SignatureSubpacketsFunction defaultFeatures = new SignatureSubpacketsFunction() + { + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.removePacketsOfType(SignatureSubpacketTags.FEATURES); + subpackets.setFeature(false, (byte)(Features.FEATURE_MODIFICATION_DETECTION | Features.FEATURE_SEIPD_V2)); + return subpackets; + } + }; + + /** + * Standard signature subpackets for signing subkey's binding signatures. + * Sets the keyflag subpacket to SIGN_DATA. + */ + protected SignatureSubpacketsFunction signingSubkeySubpackets = new SignatureSubpacketsFunction() + { + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS); + subpackets.setKeyFlags(true, KeyFlags.SIGN_DATA); + return subpackets; + } + }; + + /** + * Standard signature subpackets for encryption subkey's binding signatures. + * Sets the keyflag subpacket to ENCRYPT_STORAGE|ENCRYPT_COMMS. + */ + protected SignatureSubpacketsFunction encryptionSubkeySubpackets = new SignatureSubpacketsFunction() + { + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS); + subpackets.setKeyFlags(true, KeyFlags.ENCRYPT_STORAGE | KeyFlags.ENCRYPT_COMMS); + return subpackets; + } + }; + + /** + * Standard signature subpackets for the direct-key signature. + * Sets default features, hash-, compression-, symmetric-key-, and AEAD algorithm preferences. + */ + protected SignatureSubpacketsFunction directKeySignatureSubpackets = new SignatureSubpacketsFunction() + { + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets = defaultFeatures.apply(subpackets); + subpackets = defaultHashAlgorithmPreferences.apply(subpackets); + subpackets = defaultCompressionAlgorithmPreferences.apply(subpackets); + subpackets = defaultSymmetricKeyPreferences.apply(subpackets); + subpackets = defaultAeadAlgorithmPreferences.apply(subpackets); + return subpackets; + } + }; + + public void setDefaultAeadAlgorithmPreferences(SignatureSubpacketsFunction aeadAlgorithmPreferences) + { + this.defaultAeadAlgorithmPreferences = aeadAlgorithmPreferences; + } + + public void setDefaultSymmetricKeyPreferences(SignatureSubpacketsFunction symmetricKeyPreferences) + { + this.defaultSymmetricKeyPreferences = symmetricKeyPreferences; + } + + public void setDefaultHashAlgorithmPreferences(SignatureSubpacketsFunction hashAlgorithmPreferences) + { + this.defaultHashAlgorithmPreferences = hashAlgorithmPreferences; + } + + public void setDefaultCompressionAlgorithmPreferences(SignatureSubpacketsFunction compressionAlgorithmPreferences) + { + this.defaultCompressionAlgorithmPreferences = compressionAlgorithmPreferences; + } + + public void setDirectKeySignatureSubpackets(SignatureSubpacketsFunction directKeySignatureSubpackets) + { + this.directKeySignatureSubpackets = directKeySignatureSubpackets; + } + + public void setDefaultFeatures(SignatureSubpacketsFunction features) + { + this.defaultFeatures = features; + } + + public void setSigningSubkeySubpackets(SignatureSubpacketsFunction signingSubkeySubpackets) + { + this.signingSubkeySubpackets = signingSubkeySubpackets; + } + + public void setEncryptionSubkeySubpackets(SignatureSubpacketsFunction encryptionSubkeySubpackets) + { + this.encryptionSubkeySubpackets = encryptionSubkeySubpackets; + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/EncryptedDataPacketType.java b/pg/src/main/java/org/bouncycastle/openpgp/api/EncryptedDataPacketType.java new file mode 100644 index 0000000000..e8f5a67aa8 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/EncryptedDataPacketType.java @@ -0,0 +1,37 @@ +package org.bouncycastle.openpgp.api; + +/** + * Encryption Mode. + */ +public enum EncryptedDataPacketType +{ + /** + * Symmetrically-Encrypted Data packet. + * This method is deprecated, as it does not protect against malleability. + * + * @deprecated + */ + @Deprecated + SED, // deprecated + /** + * Symmetrically-Encrypted-Integrity-Protected Data packet version 1. + * This method protects the message using symmetric encryption as specified in RFC4880. + * Support for this encryption mode is signalled using + * {@link org.bouncycastle.bcpg.sig.Features#FEATURE_MODIFICATION_DETECTION}. + */ + SEIPDv1, // v4 + + /** + * Symmetrically-Encrypted-Integrity-Protected Data packet version 2. + * This method protects the message using an AEAD encryption scheme specified in RFC9580. + * Support for this feature is signalled using {@link org.bouncycastle.bcpg.sig.Features#FEATURE_SEIPD_V2}. + */ + SEIPDv2, // v6 + + /** + * LibrePGP OCB-Encrypted Data packet. + * This method protects the message using an AEAD encryption scheme specified in LibrePGP. + * Support for this feature is signalled using {@link org.bouncycastle.bcpg.sig.Features#FEATURE_AEAD_ENCRYPTED_DATA}. + */ + LIBREPGP_OED // "v5" +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPairGeneratorCallback.java b/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPairGeneratorCallback.java index e30bb22cc2..68825e773b 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPairGeneratorCallback.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPairGeneratorCallback.java @@ -7,8 +7,7 @@ /** * Callback to generate a {@link PGPKeyPair} from a {@link PGPKeyPairGenerator} instance. */ -@FunctionalInterface -public interface KeyPairGeneratorCallback +public abstract class KeyPairGeneratorCallback { /** * Generate a {@link PGPKeyPair} by calling a factory method on a given generator instance. @@ -17,6 +16,45 @@ public interface KeyPairGeneratorCallback * @return generated key pair * @throws PGPException */ - PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + public abstract PGPKeyPair generateFrom(PGPKeyPairGenerator generator) throws PGPException; + + public static KeyPairGeneratorCallback primaryKey() + { + return new KeyPairGeneratorCallback() + { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generatePrimaryKey(); + } + }; + } + + public static KeyPairGeneratorCallback encryptionKey() + { + return new KeyPairGeneratorCallback() + { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateEncryptionSubkey(); + } + }; + } + + public static KeyPairGeneratorCallback signingKey() + { + return new KeyPairGeneratorCallback() + { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateSigningSubkey(); + } + }; + } } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPassphraseProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPassphraseProvider.java new file mode 100644 index 0000000000..e05a7ea2da --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPassphraseProvider.java @@ -0,0 +1,131 @@ +package org.bouncycastle.openpgp.api; + +import org.bouncycastle.util.Arrays; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public interface KeyPassphraseProvider +{ + /** + * Return the passphrase for the given key. + * This callback is only fired, if the key is locked and a passphrase is required to unlock it. + * Returning null means, that the passphrase is not available. + * + * @param key the locked (sub-)key. + * @return passphrase or null + */ + char[] getKeyPassword(OpenPGPKey.OpenPGPSecretKey key); + + class DefaultKeyPassphraseProvider + implements KeyPassphraseProvider + { + private final Map passphraseMap = new HashMap<>(); + private final List allPassphrases = new ArrayList<>(); + private KeyPassphraseProvider callback; + + public DefaultKeyPassphraseProvider() + { + + } + + public DefaultKeyPassphraseProvider(OpenPGPKey key, char[] passphrase) + { + allPassphrases.add(passphrase); + + for (Iterator it = key.getSecretKeys().values().iterator(); it.hasNext(); ) + { + OpenPGPKey.OpenPGPSecretKey subkey = (OpenPGPKey.OpenPGPSecretKey)it.next(); + passphraseMap.put(subkey, passphrase); + } + } + + @Override + public char[] getKeyPassword(OpenPGPKey.OpenPGPSecretKey key) + { + if (!key.isLocked()) + { + passphraseMap.put(key, null); + return null; + } + + char[] passphrase = passphraseMap.get(key); + if (passphrase != null) + { + return passphrase; + } + + for (char[] knownPassphrase : allPassphrases) + { + if (key.isPassphraseCorrect(knownPassphrase)) + { + addPassphrase(key, knownPassphrase); + return knownPassphrase; + } + } + + if (callback != null) + { + passphrase = callback.getKeyPassword(key); + addPassphrase(key, passphrase); + } + return passphrase; + } + + public DefaultKeyPassphraseProvider addPassphrase(char[] passphrase) + { + boolean found = false; + for (char[] existing : allPassphrases) + { + found |= (Arrays.areEqual(existing, passphrase)); + } + + if (!found) + { + allPassphrases.add(passphrase); + } + return this; + } + + public DefaultKeyPassphraseProvider addPassphrase(OpenPGPKey key, char[] passphrase) + { + for (OpenPGPKey.OpenPGPSecretKey subkey : key.getSecretKeys().values()) + { + if (!subkey.isLocked()) + { + passphraseMap.put(subkey, null); + continue; + } + + char[] existentPassphrase = passphraseMap.get(subkey); + if (existentPassphrase == null || !subkey.isPassphraseCorrect(existentPassphrase)) + { + passphraseMap.put(subkey, passphrase); + } + } + return this; + } + + public DefaultKeyPassphraseProvider addPassphrase(OpenPGPKey.OpenPGPSecretKey key, char[] passphrase) + { + if (!key.isLocked()) + { + passphraseMap.put(key, null); + return this; + } + + passphraseMap.put(key, passphrase); + + return this; + } + + public DefaultKeyPassphraseProvider setMissingPassphraseCallback(KeyPassphraseProvider callback) + { + this.callback = callback; + return this; + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/MessageEncryptionMechanism.java b/pg/src/main/java/org/bouncycastle/openpgp/api/MessageEncryptionMechanism.java new file mode 100644 index 0000000000..ddb5b8666a --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/MessageEncryptionMechanism.java @@ -0,0 +1,140 @@ +package org.bouncycastle.openpgp.api; + +import org.bouncycastle.bcpg.AEADAlgorithmTags; +import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites; + +/** + * Encryption mode (SEIPDv1 / SEIPDv2 / OED) and algorithms. + */ +public class MessageEncryptionMechanism +{ + private final EncryptedDataPacketType mode; + private final int symmetricKeyAlgorithm; + private final int aeadAlgorithm; + + /** + * Create a {@link MessageEncryptionMechanism} tuple. + * + * @param mode encryption mode (packet type) + * @param symmetricKeyAlgorithm symmetric key algorithm for message encryption + * @param aeadAlgorithm aead algorithm for message encryption + */ + private MessageEncryptionMechanism(EncryptedDataPacketType mode, + int symmetricKeyAlgorithm, + int aeadAlgorithm) + { + this.mode = mode; + this.symmetricKeyAlgorithm = symmetricKeyAlgorithm; + this.aeadAlgorithm = aeadAlgorithm; + } + + public EncryptedDataPacketType getMode() + { + return mode; + } + + public int getSymmetricKeyAlgorithm() + { + return symmetricKeyAlgorithm; + } + + public int getAeadAlgorithm() + { + return aeadAlgorithm; + } + + /** + * The data will not be encrypted. + * Useful for sign-only operations. + * + * @return unencrypted encryption setup + */ + public static MessageEncryptionMechanism unencrypted() + { + int none = 0; + return new MessageEncryptionMechanism(EncryptedDataPacketType.SEIPDv1, + SymmetricKeyAlgorithmTags.NULL, none); + } + + /** + * The data will be encrypted and integrity protected using a SEIPDv1 packet. + * + * @param symmetricKeyAlgorithm symmetric cipher algorithm for message encryption + * @return sym. enc. integrity protected encryption setup + */ + public static MessageEncryptionMechanism integrityProtected(int symmetricKeyAlgorithm) + { + int none = 0; + return new MessageEncryptionMechanism(EncryptedDataPacketType.SEIPDv1, symmetricKeyAlgorithm, none); + } + + /** + * The data will be OCB-encrypted as specified by the non-standard LibrePGP document. + * + * @param symmetricKeyAlgorithm symmetric key algorithm which will be combined with OCB to form + * an OCB-encrypted data packet + * @return LibrePGP OCB encryption setup + */ + public static MessageEncryptionMechanism librePgp(int symmetricKeyAlgorithm) + { + return new MessageEncryptionMechanism(EncryptedDataPacketType.LIBREPGP_OED, + symmetricKeyAlgorithm, AEADAlgorithmTags.OCB); + } + + /** + * The data will be AEAD-encrypted using the method described in RFC9580. + * + * @param symmetricKeyAlgorithm symmetric cipher algorithm + * @param aeadAlgorithm AEAD algorithm + * @return AEAD encryption setup + */ + public static MessageEncryptionMechanism aead(int symmetricKeyAlgorithm, int aeadAlgorithm) + { + return new MessageEncryptionMechanism(EncryptedDataPacketType.SEIPDv2, symmetricKeyAlgorithm, aeadAlgorithm); + } + + public static MessageEncryptionMechanism aead(PreferredAEADCiphersuites.Combination combination) + { + return aead(combination.getSymmetricAlgorithm(), combination.getAeadAlgorithm()); + } + + /** + * Return true, if the message will be encrypted. + * + * @return is encrypted + */ + public boolean isEncrypted() + { + return symmetricKeyAlgorithm != SymmetricKeyAlgorithmTags.NULL; + } + + @Override + public int hashCode() + { + return mode.hashCode() + + 13 * symmetricKeyAlgorithm + + 17 * aeadAlgorithm; + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + if (this == obj) + { + return true; + } + if (!(obj instanceof MessageEncryptionMechanism)) + { + return false; + } + MessageEncryptionMechanism m = (MessageEncryptionMechanism)obj; + return getMode() == m.getMode() + && getSymmetricKeyAlgorithm() == m.getSymmetricKeyAlgorithm() + && getAeadAlgorithm() == m.getAeadAlgorithm(); + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/MissingMessagePassphraseCallback.java b/pg/src/main/java/org/bouncycastle/openpgp/api/MissingMessagePassphraseCallback.java new file mode 100644 index 0000000000..b3ff38af90 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/MissingMessagePassphraseCallback.java @@ -0,0 +1,13 @@ +package org.bouncycastle.openpgp.api; + +public interface MissingMessagePassphraseCallback +{ + /** + * Return a passphrase for message decryption. + * Returning null means, that no passphrase is available and decryption is aborted. + * + * @return passphrase + */ + char[] getMessagePassphrase(); + +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPApi.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPApi.java new file mode 100644 index 0000000000..db6e63d410 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPApi.java @@ -0,0 +1,240 @@ +package org.bouncycastle.openpgp.api; + +import java.util.Date; + +import org.bouncycastle.bcpg.PublicKeyPacket; +import org.bouncycastle.openpgp.PGPException; + +/** + * Main entry to the high level OpenPGP API. + */ +public abstract class OpenPGPApi +{ + private final OpenPGPImplementation implementation; + private final OpenPGPPolicy policy; + + /** + * Instantiate an {@link OpenPGPApi} based on the given {@link OpenPGPImplementation}. + * + * @param implementation OpenPGP implementation + */ + public OpenPGPApi(OpenPGPImplementation implementation) + { + this(implementation, implementation.policy()); + } + + /** + * Instantiate an {@link OpenPGPApi} object, passing in an {@link OpenPGPImplementation} and custom + * {@link OpenPGPPolicy}. + * + * @param implementation OpenPGP implementation + * @param policy algorithm policy + */ + public OpenPGPApi(OpenPGPImplementation implementation, OpenPGPPolicy policy) + { + this.implementation = implementation; + this.policy = policy; + } + + /** + * Return an {@link OpenPGPKeyReader} which can be used to parse binary or ASCII armored + * {@link OpenPGPKey OpenPGPKeys} or {@link OpenPGPCertificate OpenPGPCertificates}. + * + * @return key reader + */ + public OpenPGPKeyReader readKeyOrCertificate() + { + return new OpenPGPKeyReader(implementation, policy); + } + + /** + * Return an {@link OpenPGPKeyGenerator} which can be used to generate {@link OpenPGPKey OpenPGPKeys}. + * This method returns a generator for OpenPGP v6 keys as defined by rfc9580. + * + * @return key generator + * @throws PGPException if the key generator cannot be set up + */ + public OpenPGPKeyGenerator generateKey() + throws PGPException + { + return generateKey(PublicKeyPacket.VERSION_6); + } + + /** + * Return an {@link OpenPGPKeyGenerator} which can be used to generate {@link OpenPGPKey OpenPGPKeys} + * of the given key version. + * Valid version numbers are: + *
      + *
    • {@link PublicKeyPacket#VERSION_4} (rfc4880)
    • + *
    • {@link PublicKeyPacket#VERSION_6} (rfc9580)
    • + *
    • {@link PublicKeyPacket#LIBREPGP_5} (LibrePGP; experimental)
    • + *
    + * + * @param version key version number + * @return key generator + * @throws PGPException if the key generator cannot be set up + */ + public abstract OpenPGPKeyGenerator generateKey(int version) + throws PGPException; + + /** + * Return an {@link OpenPGPKeyGenerator} which can be used to generate {@link OpenPGPKey OpenPGPKeys}. + * The key and signatures will have a creation time of the passed creationTime. + * This method returns a generator for OpenPGP v6 keys as defined by rfc9580. + * + * @param creationTime key + signature creation time + * @return key generator + * @throws PGPException if the key generator cannot be set up + */ + public OpenPGPKeyGenerator generateKey(Date creationTime) + throws PGPException + { + return generateKey(PublicKeyPacket.VERSION_6, creationTime); + } + + /** + * Return an {@link OpenPGPKeyGenerator} which can be used to generate {@link OpenPGPKey OpenPGPKeys} + * of the given key version. + * The key and signatures will have a creation time of the passed creationTime. + * Valid version numbers are: + *
      + *
    • {@link PublicKeyPacket#VERSION_4} (rfc4880)
    • + *
    • {@link PublicKeyPacket#VERSION_6} (rfc9580)
    • + *
    • {@link PublicKeyPacket#LIBREPGP_5} (LibrePGP; experimental)
    • + *
    + * + * @param version key version number + * @param creationTime key + signatures creation time + * @return key generator + * @throws PGPException if the key generator cannot be set up + */ + public abstract OpenPGPKeyGenerator generateKey(int version, + Date creationTime) + throws PGPException; + + /** + * Return an {@link OpenPGPKeyGenerator} which can be used to generate {@link OpenPGPKey OpenPGPKeys}. + * The key and signatures will have a creation time of the passed creationTime. + * If aeadProtection is true, the key will use AEAD+Argon2 to protect the secret key material, + * otherwise it will use salted+iterated CFB mode. + * This method returns a generator for OpenPGP v6 keys as defined by rfc9580. + * + * @param creationTime key + signature creation time + * @param aeadProtection whether to use AEAD or CFB protection + * @return key generator + * @throws PGPException if the key generator cannot be set up + */ + public OpenPGPKeyGenerator generateKey(Date creationTime, boolean aeadProtection) + throws PGPException + { + return generateKey(PublicKeyPacket.VERSION_6, creationTime, aeadProtection); + } + + /** + * Return an {@link OpenPGPKeyGenerator} which can be used to generate {@link OpenPGPKey OpenPGPKeys} + * of the given key version. + * The key and signatures will have a creation time of the passed creationTime. + * If aeadProtection is true, the key will use AEAD+Argon2 to protect the secret key material, + * otherwise it will use salted+iterated CFB mode. + * Valid version numbers are: + *
      + *
    • {@link PublicKeyPacket#VERSION_4} (rfc4880)
    • + *
    • {@link PublicKeyPacket#VERSION_6} (rfc9580)
    • + *
    • {@link PublicKeyPacket#LIBREPGP_5} (LibrePGP; experimental)
    • + *
    + * + * @param creationTime key + signature creation time + * @param aeadProtection whether to use AEAD or CFB protection + * @return key generator + * @throws PGPException if the key generator cannot be set up + */ + public abstract OpenPGPKeyGenerator generateKey(int version, + Date creationTime, + boolean aeadProtection) + throws PGPException; + + /** + * Create an inline-signed and/or encrypted OpenPGP message. + * + * @return message generator + */ + public OpenPGPMessageGenerator signAndOrEncryptMessage() + { + return new OpenPGPMessageGenerator(implementation, policy); + } + + /** + * Create one or more detached signatures over some data. + * + * @return signature generator + */ + public OpenPGPDetachedSignatureGenerator createDetachedSignature() + { + return new OpenPGPDetachedSignatureGenerator(implementation, policy); + } + + /** + * Decrypt and/or verify an OpenPGP message. + * + * @return message processor + */ + public OpenPGPMessageProcessor decryptAndOrVerifyMessage() + { + return new OpenPGPMessageProcessor(implementation, policy); + } + + /** + * Verify detached signatures over some data. + * + * @return signature processor + */ + public OpenPGPDetachedSignatureProcessor verifyDetachedSignature() + { + return new OpenPGPDetachedSignatureProcessor(implementation, policy); + } + + public OpenPGPKeyEditor editKey(OpenPGPKey key) + throws PGPException + { + return editKey(key, (char[]) null); + } + + public OpenPGPKeyEditor editKey(OpenPGPKey key, final char[] primaryKeyPassphrase) + throws PGPException + { + return new OpenPGPKeyEditor( + key, + new KeyPassphraseProvider() + { + @Override + public char[] getKeyPassword(OpenPGPKey.OpenPGPSecretKey key) + { + return primaryKeyPassphrase; + } + }, + implementation, + policy); + } + + /** + * Modify an {@link OpenPGPKey}. + * + * @param key OpenPGP key + * @return key editor + */ + public OpenPGPKeyEditor editKey(OpenPGPKey key, KeyPassphraseProvider primaryKeyPassphraseProvider) + throws PGPException + { + return new OpenPGPKeyEditor(key, primaryKeyPassphraseProvider, implementation, policy); + } + + /** + * Return the underlying {@link OpenPGPImplementation} of this API handle. + * + * @return OpenPGP implementation + */ + public OpenPGPImplementation getImplementation() + { + return implementation; + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java new file mode 100644 index 0000000000..c332992d54 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java @@ -0,0 +1,3605 @@ +package org.bouncycastle.openpgp.api; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; +import java.util.function.Function; + +import org.bouncycastle.bcpg.ArmoredOutputStream; +import org.bouncycastle.bcpg.BCPGInputStream; +import org.bouncycastle.bcpg.BCPGOutputStream; +import org.bouncycastle.bcpg.FingerprintUtil; +import org.bouncycastle.bcpg.KeyIdentifier; +import org.bouncycastle.bcpg.PacketFormat; +import org.bouncycastle.bcpg.PublicKeyUtils; +import org.bouncycastle.bcpg.SignatureSubpacket; +import org.bouncycastle.bcpg.SignatureSubpacketTags; +import org.bouncycastle.bcpg.sig.Features; +import org.bouncycastle.bcpg.sig.KeyExpirationTime; +import org.bouncycastle.bcpg.sig.KeyFlags; +import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites; +import org.bouncycastle.bcpg.sig.PreferredAlgorithms; +import org.bouncycastle.bcpg.sig.PrimaryUserID; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyRing; +import org.bouncycastle.openpgp.PGPObjectFactory; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureException; +import org.bouncycastle.openpgp.PGPSignatureList; +import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; +import org.bouncycastle.openpgp.PGPUserAttributeSubpacketVector; +import org.bouncycastle.openpgp.PGPUtil; +import org.bouncycastle.openpgp.api.exception.IncorrectOpenPGPSignatureException; +import org.bouncycastle.openpgp.api.exception.MalformedOpenPGPSignatureException; +import org.bouncycastle.openpgp.api.exception.MissingIssuerCertException; +import org.bouncycastle.openpgp.api.util.UTCUtil; +import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider; + +/** + * OpenPGP certificates (TPKs - transferable public keys) are long-living structures that may change during + * their lifetime. A key-holder may add new components like subkeys or identities, along with associated + * binding self-signatures to the certificate and old components may expire / get revoked at some point. + * Since any such changes may have an influence on whether a data signature is valid at a given time, or what subkey + * should be used when generating an encrypted / signed message, an API is needed that provides a view on the + * certificate that takes into consideration a relevant window in time. + *

    + * Compared to a {@link PGPPublicKeyRing}, an {@link OpenPGPCertificate} has been evaluated at (or rather for) + * a given evaluation time. It offers a clean API for accessing the key-holder's preferences at a specific + * point in time and makes sure, that relevant self-signatures on certificate components are validated and verified. + * + * @see OpenPGP for Application Developers - Chapter 4 + * for background information on the terminology used in this class. + */ +public class OpenPGPCertificate +{ + final OpenPGPImplementation implementation; + final OpenPGPPolicy policy; + + protected PGPKeyRing keyRing; + + private final OpenPGPPrimaryKey primaryKey; + private final Map subkeys; + + // Note: get() needs to be accessed with OpenPGPCertificateComponent.getPublicComponent() to ensure + // proper functionality with secret key components. + private final Map componentSignatureChains; + + /** + * Instantiate an {@link OpenPGPCertificate} from a passed {@link PGPKeyRing} using the default + * {@link OpenPGPImplementation} and its {@link OpenPGPPolicy}. + * + * @param keyRing key ring + */ + public OpenPGPCertificate(PGPKeyRing keyRing) + { + this(keyRing, OpenPGPImplementation.getInstance()); + } + + /** + * Instantiate an {@link OpenPGPCertificate} from a parsed {@link PGPKeyRing} + * using the provided {@link OpenPGPImplementation} and its {@link OpenPGPPolicy}. + * + * @param keyRing public key ring + * @param implementation OpenPGP implementation + */ + public OpenPGPCertificate(PGPKeyRing keyRing, OpenPGPImplementation implementation) + { + this(keyRing, implementation, implementation.policy()); + } + + /** + * Instantiate an {@link OpenPGPCertificate} from a parsed {@link PGPKeyRing} + * using the provided {@link OpenPGPImplementation} and provided {@link OpenPGPPolicy}. + * + * @param keyRing public key ring + * @param implementation OpenPGP implementation + * @param policy OpenPGP policy + */ + public OpenPGPCertificate(PGPKeyRing keyRing, OpenPGPImplementation implementation, OpenPGPPolicy policy) + { + this.implementation = implementation; + this.policy = policy; + + this.keyRing = keyRing; + this.subkeys = new LinkedHashMap<>(); + this.componentSignatureChains = new LinkedHashMap<>(); + + Iterator rawKeys = keyRing.getPublicKeys(); + + PGPPublicKey rawPrimaryKey = rawKeys.next(); + this.primaryKey = new OpenPGPPrimaryKey(rawPrimaryKey, this); + processPrimaryKey(primaryKey); + + while (rawKeys.hasNext()) + { + PGPPublicKey rawSubkey = rawKeys.next(); + OpenPGPSubkey subkey = new OpenPGPSubkey(rawSubkey, this); + subkeys.put(rawSubkey.getKeyIdentifier(), subkey); + processSubkey(subkey); + } + } + + /** + * Return true, if this object is an {@link OpenPGPKey}, false otherwise. + * + * @return true if this is a secret key + */ + public boolean isSecretKey() + { + return false; + } + + /** + * Return a {@link List} of all {@link OpenPGPUserId OpenPGPUserIds} on the certificate, regardless of their + * validity. + * + * @return all user ids + */ + public List getAllUserIds() + { + return getPrimaryKey().getUserIDs(); + } + + /** + * Return a {@link List} of all valid {@link OpenPGPUserId OpenPGPUserIds} on the certificate. + * + * @return valid user ids + */ + public List getValidUserIds() + { + return getValidUserIds(new Date()); + } + + /** + * Return a {@link List} containing all {@link OpenPGPUserId OpenPGPUserIds} that are valid at the given + * evaluation time. + * + * @param evaluationTime reference time + * @return user ids that are valid at the given evaluation time + */ + public List getValidUserIds(Date evaluationTime) + { + return getPrimaryKey().getValidUserIDs(evaluationTime); + } + + /** + * Get a {@link Map} of all public {@link OpenPGPComponentKey component keys} keyed by their {@link KeyIdentifier}. + * + * @return all public keys + */ + public Map getPublicKeys() + { + Map keys = new HashMap<>(); + keys.put(primaryKey.getKeyIdentifier(), primaryKey); + keys.putAll(subkeys); + return keys; + } + + /** + * Return the primary key of the certificate. + * + * @return primary key + */ + public OpenPGPPrimaryKey getPrimaryKey() + { + return primaryKey; + } + + /** + * Return a {@link Map} containing the subkeys of this certificate, keyed by their {@link KeyIdentifier}. + * Note: This map does NOT contain the primary key ({@link #getPrimaryKey()}). + * + * @return subkeys + */ + public Map getSubkeys() + { + return new LinkedHashMap<>(subkeys); + } + + /** + * Return a {@link List} containing all {@link OpenPGPComponentKey component keys} that carry any of the + * given key flags at evaluation time. + * + * Note: To get all component keys that have EITHER {@link KeyFlags#ENCRYPT_COMMS} OR {@link KeyFlags#ENCRYPT_STORAGE}, + * call this method like this: + *

    +     * keys = getComponentKeysWithFlag(date, KeyFlags.ENCRYPT_COMMS, KeyFlags.ENCRYPT_STORAGE);
    +     * 
    + * If you instead want to access all keys, that have BOTH flags, you need to
    &
    both flags: + *
    +     * keys = getComponentKeysWithFlag(date, KeyFlags.ENCRYPT_COMMS & KeyFlags.ENCRYPT_STORAGE);
    +     * 
    + * + * @param evaluationTime reference time + * @param keyFlags key flags + * @return list of keys that carry any of the given key flags at evaluation time + */ + public List getComponentKeysWithFlag(Date evaluationTime, final int... keyFlags) + { + return filterKeys(evaluationTime, new KeyFilter() + { + @Override + public boolean test(OpenPGPComponentKey key, Date time) + { + return key.hasKeyFlags(time, keyFlags); + } + }); + } + + /** + * Return a {@link List} containing all {@link OpenPGPCertificateComponent components} of the certificate. + * Components are primary key, subkeys and identities (user-ids, user attributes). + * + * @return list of components + */ + public List getComponents() + { + return new ArrayList<>(componentSignatureChains.keySet()); + } + + /** + * Return all {@link OpenPGPComponentKey OpenPGPComponentKeys} in the certificate. + * The return value is a {@link List} containing the {@link OpenPGPPrimaryKey} and all + * {@link OpenPGPSubkey OpenPGPSubkeys}. + * + * @return list of all component keys + */ + public List getKeys() + { + List keys = new ArrayList<>(); + keys.add(primaryKey); + keys.addAll(subkeys.values()); + return keys; + } + + /** + * Return a {@link List} of all {@link OpenPGPComponentKey component keys} that are valid right now. + * + * @return all valid keys + */ + public List getValidKeys() + { + return getValidKeys(new Date()); + } + + /** + * Return a {@link List} of all {@link OpenPGPComponentKey component keys} that are valid at the given + * evaluation time. + * + * @param evaluationTime reference time + * @return all keys that are valid at evaluation time + */ + public List getValidKeys(Date evaluationTime) + { + return filterKeys(evaluationTime, new KeyFilter() + { + @Override + public boolean test(OpenPGPComponentKey key, Date time) + { + return true; + } + }); + } + + /** + * Return the {@link OpenPGPComponentKey} identified by the passed in {@link KeyIdentifier}. + * + * @param identifier key identifier + * @return component key + */ + public OpenPGPComponentKey getKey(KeyIdentifier identifier) + { + if (identifier.matches(getPrimaryKey().getPGPPublicKey().getKeyIdentifier())) + { + return primaryKey; + } + + return subkeys.get(identifier); + } + + /** + * Return the {@link OpenPGPComponentKey} that likely issued the passed in {@link PGPSignature}. + * + * @param signature signature + * @return issuer (sub-)key + */ + public OpenPGPComponentKey getSigningKeyFor(PGPSignature signature) + { + List keyIdentifiers = signature.getKeyIdentifiers(); + + // Subkey binding signatures do not require issuer + int type = signature.getSignatureType(); + if (type == PGPSignature.SUBKEY_BINDING || + type == PGPSignature.SUBKEY_REVOCATION) + { + return primaryKey; + } + + // issuer is primary key + if (KeyIdentifier.matches(keyIdentifiers, getPrimaryKey().getKeyIdentifier(), true)) + { + return primaryKey; + } + + for (Iterator it = subkeys.keySet().iterator(); it.hasNext(); ) + { + KeyIdentifier subkeyIdentifier = it.next(); + if (KeyIdentifier.matches(keyIdentifiers, subkeyIdentifier, true)) + { + return subkeys.get(subkeyIdentifier); + } + } + + return null; // external issuer + } + + /** + * Return the {@link PGPKeyRing} that this certificate is based on. + * + * @return underlying key ring + */ + public PGPKeyRing getPGPKeyRing() + { + return keyRing; + } + + /** + * Return the underlying {@link PGPPublicKeyRing}. + * + * @return public keys + */ + public PGPPublicKeyRing getPGPPublicKeyRing() + { + if (keyRing instanceof PGPPublicKeyRing) + { + return (PGPPublicKeyRing)keyRing; + } + + List list = new ArrayList<>(); + for (Iterator it = keyRing.getPublicKeys(); it.hasNext(); ) + { + list.add(it.next()); + } + return new PGPPublicKeyRing(list); + } + + /** + * Return the {@link KeyIdentifier} of the certificates primary key. + * + * @return primary key identifier + */ + public KeyIdentifier getKeyIdentifier() + { + return primaryKey.getKeyIdentifier(); + } + + /** + * Return a list of ALL (sub-)key's identifiers, including those of expired / revoked / unbound keys. + * + * @return all keys identifiers + */ + public List getAllKeyIdentifiers() + { + List identifiers = new ArrayList<>(); + for (Iterator it = keyRing.getPublicKeys(); it.hasNext(); ) + { + PGPPublicKey key = it.next(); + identifiers.add(key.getKeyIdentifier()); + } + return identifiers; + } + + /** + * Return the current self-certification signature. + * This is either a DirectKey signature on the primary key, or the latest self-certification on + * a {@link OpenPGPUserId}. + * + * @return latest certification signature + */ + public OpenPGPComponentSignature getCertification() + { + return getCertification(new Date()); + } + + /** + * Return the most recent self-certification signature at evaluation time. + * This is either a DirectKey signature on the primary key, or the (at evaluation time) latest + * self-certification on an {@link OpenPGPUserId}. + * + * @param evaluationTime reference time + * @return latest certification signature + */ + public OpenPGPComponentSignature getCertification(Date evaluationTime) + { + return primaryKey.getCertification(evaluationTime); + } + + /** + * Return the most recent revocation signature on the certificate. + * This is either a KeyRevocation signature on the primary key, or the latest certification revocation + * signature on an {@link OpenPGPUserId}. + * + * @return latest certification revocation + */ + public OpenPGPComponentSignature getRevocation() + { + return getRevocation(new Date()); + } + + /** + * Return the (at evaluation time) most recent revocation signature on the certificate. + * This is either a KeyRevocation signature on the primary key, or the latest certification revocation + * signature on an {@link OpenPGPUserId}. + * + * @param evaluationTime reference time + * @return latest certification revocation + */ + public OpenPGPComponentSignature getRevocation(Date evaluationTime) + { + return primaryKey.getRevocation(evaluationTime); + } + + /** + * Return the last time, the key was modified (before right now). + * A modification is the addition of a new subkey, or key signature. + * + * @return last modification time + */ + public Date getLastModificationDate() + { + return getLastModificationDateAt(new Date()); + } + + /** + * Return the last time, the key was modified before or at the given evaluation time. + * + * @param evaluationTime evaluation time + * @return last modification time before or at evaluation time + */ + public Date getLastModificationDateAt(Date evaluationTime) + { + Date latestModification = null; + // Signature creation times + for (Iterator it = getComponents().iterator(); it.hasNext(); ) + { + OpenPGPSignatureChains componentChains = getAllSignatureChainsFor(it.next()); + + componentChains = componentChains.getChainsAt(evaluationTime); + for (Iterator it2 = componentChains.iterator(); it2.hasNext(); ) + { + for (Iterator it3 = it2.next().iterator(); it3.hasNext(); ) + { + OpenPGPSignatureChain.Link link = it3.next(); + if (latestModification == null || link.since().after(latestModification)) + { + latestModification = link.since(); + } + } + } + } + + if (latestModification != null) + { + return latestModification; + } + + // Key creation times + for (Iterator it = getKeys().iterator(); it.hasNext(); ) + { + OpenPGPComponentKey key = it.next(); + if (key.getCreationTime().after(evaluationTime)) + { + continue; + } + + if (latestModification == null || key.getCreationTime().after(latestModification)) + { + latestModification = key.getCreationTime(); + } + } + return latestModification; + } + + /** + * Join two copies of the same {@link OpenPGPCertificate}, merging its {@link OpenPGPCertificateComponent components} + * into a single instance. + * The ASCII armored {@link String} might contain more than one {@link OpenPGPCertificate}. + * Items that are not a copy of the base certificate are silently ignored. + * + * @param certificate base certificate + * @param armored ASCII armored {@link String} containing one or more copies of the same certificate, + * possibly containing a different set of components + * @return merged certificate + * @throws IOException if the armored data cannot be processed + * @throws PGPException if a protocol level error occurs + */ + public static OpenPGPCertificate join(OpenPGPCertificate certificate, String armored) + throws IOException, PGPException + { + ByteArrayInputStream bIn = new ByteArrayInputStream(armored.getBytes()); + InputStream decoderStream = PGPUtil.getDecoderStream(bIn); + BCPGInputStream wrapper = BCPGInputStream.wrap(decoderStream); + PGPObjectFactory objFac = certificate.implementation.pgpObjectFactory(wrapper); + + Object next; + while ((next = objFac.nextObject()) != null) + { + if (next instanceof PGPPublicKeyRing) + { + PGPPublicKeyRing publicKeys = (PGPPublicKeyRing)next; + OpenPGPCertificate otherCert = new OpenPGPCertificate(publicKeys, certificate.implementation); + try + { + return join(certificate, otherCert); + } + catch (IllegalArgumentException e) + { + // skip over wrong certificate + } + } + + else if (next instanceof PGPSecretKeyRing) + { + throw new IllegalArgumentException("Joining with a secret key is not supported."); + } + + else if (next instanceof PGPSignatureList) + { + // parse and join delegations / revocations + // those are signatures of type DIRECT_KEY or KEY_REVOCATION issued either by the primary key itself + // (self-signatures) or by a 3rd party (delegations / delegation revocations) + PGPSignatureList signatures = (PGPSignatureList)next; + + PGPPublicKeyRing publicKeys = certificate.getPGPPublicKeyRing(); + PGPPublicKey primaryKey = publicKeys.getPublicKey(); + for (Iterator it = signatures.iterator(); it.hasNext(); ) + { + primaryKey = PGPPublicKey.addCertification(primaryKey, it.next()); + } + publicKeys = PGPPublicKeyRing.insertPublicKey(publicKeys, primaryKey); + return new OpenPGPCertificate(publicKeys, certificate.implementation); + } + } + return null; + } + + /** + * Join two copies of the same {@link OpenPGPCertificate}, merging its {@link OpenPGPCertificateComponent components} + * into a single instance. + * + * @param certificate base certificate + * @param other copy of the same certificate, potentially carrying a different set of components + * @return merged certificate + * @throws PGPException if a protocol level error occurs + */ + public static OpenPGPCertificate join(OpenPGPCertificate certificate, OpenPGPCertificate other) + throws PGPException + { + PGPPublicKeyRing joined = PGPPublicKeyRing.join( + certificate.getPGPPublicKeyRing(), other.getPGPPublicKeyRing()); + return new OpenPGPCertificate(joined, certificate.implementation); + } + + /** + * Return the primary keys fingerprint in binary format. + * + * @return primary key fingerprint + */ + public byte[] getFingerprint() + { + return primaryKey.getPGPPublicKey().getFingerprint(); + } + + /** + * Return the primary keys fingerprint as a pretty-printed {@link String}. + * + * @return pretty-printed primary key fingerprint + */ + public String getPrettyFingerprint() + { + return FingerprintUtil.prettifyFingerprint(getFingerprint()); + } + + /** + * Return an ASCII armored {@link String} containing the certificate. + * + * @return armored certificate + * @throws IOException if the cert cannot be encoded + */ + public String toAsciiArmoredString() + throws IOException + { + return toAsciiArmoredString(PacketFormat.ROUNDTRIP); + } + + /** + * Return an ASCII armored {@link String} containing the certificate. + * + * @param packetFormat packet length encoding format + * @return armored certificate + * @throws IOException if the cert cannot be encoded + */ + public String toAsciiArmoredString(PacketFormat packetFormat) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ArmoredOutputStream.Builder armorBuilder = ArmoredOutputStream.builder() + .clearHeaders(); + // Add fingerprint comment + armorBuilder.addSplitMultilineComment(getPrettyFingerprint()); + + // Add user-id comments + for (Iterator it = getPrimaryKey().getUserIDs().iterator(); it.hasNext(); ) + { + armorBuilder.addEllipsizedComment(it.next().getUserId()); + } + + ArmoredOutputStream aOut = armorBuilder.build(bOut); + aOut.write(getEncoded(packetFormat)); + aOut.close(); + return bOut.toString(); + } + + /** + * Return a byte array containing the binary representation of the certificate. + * + * @return binary encoded certificate + * @throws IOException if the certificate cannot be encoded + */ + public byte[] getEncoded() + throws IOException + { + return getEncoded(PacketFormat.ROUNDTRIP); + } + + /** + * Return a byte array containing the binary representation of the certificate, encoded using the + * given packet length encoding format. + * + * @param format packet length encoding format + * @return binary encoded certificate + * @throws IOException if the certificate cannot be encoded + */ + public byte[] getEncoded(PacketFormat format) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut, format); + + // Make sure we export a TPK + List list = new ArrayList<>(); + for (Iterator it = getPGPKeyRing().getPublicKeys(); it.hasNext(); ) + { + list.add(it.next()); + } + PGPPublicKeyRing publicKeys = new PGPPublicKeyRing(list); + + publicKeys.encode(pOut, true); + pOut.close(); + return bOut.toByteArray(); + } + + private OpenPGPSignatureChain getSignatureChainFor(OpenPGPCertificateComponent component, + OpenPGPComponentKey origin, + Date evaluationDate) + { + // Check if there are signatures at all for the component + OpenPGPSignatureChains chainsForComponent = getAllSignatureChainsFor(component); + boolean isPrimaryKey = component == getPrimaryKey(); + if (isPrimaryKey && chainsForComponent.getCertificationAt(evaluationDate) == null) + { + // If cert has no direct-key signatures, consider primary UID bindings instead + OpenPGPUserId primaryUserId = getPrimaryUserId(evaluationDate); + if (primaryUserId != null) + { + chainsForComponent.addAll(getAllSignatureChainsFor(primaryUserId)); + } + } + + // Isolate chains which originate from the passed origin key component + OpenPGPSignatureChains fromOrigin = chainsForComponent.fromOrigin(origin); + if (fromOrigin == null) + { + return null; + } + + // Return chain that currently takes precedence + return fromOrigin.getChainAt(evaluationDate); + } + + /** + * Return all {@link OpenPGPSignatureChain OpenPGPSignatureChains} binding the given + * {@link OpenPGPCertificateComponent}. + * + * @param component certificate component + * @return all chains of the component + */ + private OpenPGPSignatureChains getAllSignatureChainsFor(OpenPGPCertificateComponent component) + { + OpenPGPSignatureChains chains = new OpenPGPSignatureChains(component.getPublicComponent()); + chains.addAll(componentSignatureChains.get(component.getPublicComponent())); + return chains; + } + + /** + * Process the given {@link OpenPGPPrimaryKey}, parsing all its signatures and identities. + * + * @param primaryKey primary key + */ + private void processPrimaryKey(OpenPGPPrimaryKey primaryKey) + { + OpenPGPSignatureChains keySignatureChains = new OpenPGPSignatureChains(primaryKey); + List keySignatures = primaryKey.getKeySignatures(); + + // Key Signatures + addSignaturesToChains(keySignatures, keySignatureChains); + componentSignatureChains.put(primaryKey, keySignatureChains); + + // Identities + for (Iterator it = primaryKey.identityComponents.iterator(); it.hasNext(); ) + { + OpenPGPIdentityComponent identity = it.next(); + OpenPGPSignatureChains identityChains = new OpenPGPSignatureChains(identity); + List bindings; + + if (identity instanceof OpenPGPUserId) + { + bindings = primaryKey.getUserIdSignatures((OpenPGPUserId)identity); + } + else + { + bindings = primaryKey.getUserAttributeSignatures((OpenPGPUserAttribute)identity); + } + addSignaturesToChains(bindings, identityChains); + componentSignatureChains.put(identity, identityChains); + } + } + + /** + * Process the given {@link OpenPGPSubkey}, parsing all its binding signatures. + * + * @param subkey subkey + */ + private void processSubkey(OpenPGPSubkey subkey) + { + List bindingSignatures = subkey.getKeySignatures(); + OpenPGPSignatureChains subkeyChains = new OpenPGPSignatureChains(subkey); + + for (Iterator it = bindingSignatures.iterator(); it.hasNext(); ) + { + OpenPGPComponentSignature sig = it.next(); + OpenPGPComponentKey issuer = subkey.getCertificate().getSigningKeyFor(sig.getSignature()); + if (issuer == null) + { + continue; // external key + } + + OpenPGPSignatureChains issuerChains = getAllSignatureChainsFor(issuer); + if (!issuerChains.chains.isEmpty()) + { + for (Iterator it2 = issuerChains.chains.iterator(); it2.hasNext(); ) + { + subkeyChains.add(it2.next().plus(sig)); + } + } + else + { + subkeyChains.add(new OpenPGPSignatureChain(OpenPGPSignatureChain.Link.create(sig))); + } + } + this.componentSignatureChains.put(subkey, subkeyChains); + } + + /** + * Return true, if the passed in component is - at evaluation time - properly bound to the certificate. + * + * @param component OpenPGP certificate component + * @param evaluationTime evaluation time + * @return true if component is bound at evaluation time, false otherwise + */ + private boolean isBound(OpenPGPCertificateComponent component, + Date evaluationTime) + { + return isBoundBy(component, getPrimaryKey(), evaluationTime); + } + + /** + * Return true, if the passed in component is - at evaluation time - properly bound to the certificate with + * a signature chain originating at the passed in root component. + * + * @param component OpenPGP certificate component + * @param root root certificate component + * @param evaluationTime evaluation time + * @return true if component is bound at evaluation time, originating at root, false otherwise + */ + private boolean isBoundBy(OpenPGPCertificateComponent component, + OpenPGPComponentKey root, + Date evaluationTime) + { + OpenPGPSignature.OpenPGPSignatureSubpacket keyExpiration = + component.getApplyingSubpacket(evaluationTime, SignatureSubpacketTags.KEY_EXPIRE_TIME); + if (keyExpiration != null) + { + KeyExpirationTime kexp = (KeyExpirationTime)keyExpiration.getSubpacket(); + if (kexp.getTime() != 0) + { + OpenPGPComponentKey key = component.getKeyComponent(); + Date expirationDate = new Date(1000 * kexp.getTime() + key.getCreationTime().getTime()); + if (expirationDate.before(evaluationTime)) + { + // Key is expired. + return false; + } + } + } + + try + { + OpenPGPSignatureChain chain = getSignatureChainFor(component, root, evaluationTime); + if (chain == null) + { + // Component is not bound at all + return false; + } + + // Chain needs to be valid (signatures correct) + if (chain.isValid(implementation.pgpContentVerifierBuilderProvider(), policy)) + { + // Chain needs to not contain a revocation signature, otherwise the component is considered revoked + return !chain.isRevocation(); + } + + // Signature is not correct + return false; + } + catch (PGPException e) + { + // Signature verification failed (signature broken?) + return false; + } + } + + /** + * Return a {@link List} containing all currently marked, valid encryption keys. + * + * @return encryption keys + */ + public List getEncryptionKeys() + { + return getEncryptionKeys(new Date()); + } + + /** + * Return a list of all keys that are - at evaluation time - valid encryption keys. + * + * @param evaluationTime evaluation time + * @return encryption keys + */ + public List getEncryptionKeys(Date evaluationTime) + { + return filterKeys(evaluationTime, new KeyFilter() + { + @Override + public boolean test(OpenPGPComponentKey key, Date time) + { + return key.isEncryptionKey(time); + } + }); + } + + /** + * Return a {@link List} containing all currently valid marked signing keys. + * + * @return list of signing keys + */ + public List getSigningKeys() + { + return getSigningKeys(new Date()); + } + + /** + * Return a list of all keys that - at evaluation time - are validly marked as signing keys. + * + * @param evaluationTime evaluation time + * @return list of signing keys + */ + public List getSigningKeys(Date evaluationTime) + { + return filterKeys(evaluationTime, new KeyFilter() + { + @Override + public boolean test(OpenPGPComponentKey key, Date time) + { + return key.isSigningKey(time); + } + }); + } + + /** + * Return a {@link List} containing all currently valid marked certification keys. + * + * @return list of certification keys + */ + public List getCertificationKeys() + { + return getCertificationKeys(new Date()); + } + + /** + * Return a list of all keys that - at evaluation time - are validly marked as certification keys. + * + * @param evaluationTime evaluation time + * @return list of certification keys + */ + public List getCertificationKeys(Date evaluationTime) + { + return filterKeys(evaluationTime, new KeyFilter() + { + @Override + public boolean test(OpenPGPComponentKey key, Date time) + { + return key.isCertificationKey(time); + } + }); + } + + /** + * Return {@link OpenPGPSignatureChains} that contain preference information. + * + * @return signature chain containing certificate-wide preferences (typically DK signature) + */ + private OpenPGPSignatureChain getPreferenceSignature(Date evaluationTime) + { + OpenPGPSignatureChain directKeyBinding = getPrimaryKey().getSignatureChains() + .fromOrigin(getPrimaryKey()) + .getCertificationAt(evaluationTime); + + if (directKeyBinding != null) + { + return directKeyBinding; + } + + List uidBindings = new ArrayList<>(); + for (Iterator it = getPrimaryKey().getUserIDs().iterator(); it.hasNext(); ) + { + OpenPGPSignatureChain uidBinding = getAllSignatureChainsFor(it.next()) + .fromOrigin(getPrimaryKey()).getCertificationAt(evaluationTime); + + if (uidBinding != null) + { + uidBindings.add(uidBinding); + } + } + + //uidBindings.sort(Comparator.comparing(OpenPGPSignatureChain::getSince).reversed()); + uidBindings.sort(new Comparator() + { + @Override + public int compare(OpenPGPSignatureChain o1, OpenPGPSignatureChain o2) + { + // Reverse comparison for descending order (mimics .reversed()) + return o2.getSince().compareTo(o1.getSince()); + } + }); + for (Iterator it = uidBindings.iterator(); it.hasNext(); ) + { + OpenPGPSignatureChain binding = it.next(); + PGPSignature sig = binding.getSignature().getSignature(); + if (sig.getHashedSubPackets().isPrimaryUserID()) + { + return binding; + } + } + + return uidBindings.isEmpty() ? null : uidBindings.get(0); + } + + /** + * Return all identities ({@link OpenPGPUserId User IDs}, {@link OpenPGPUserAttribute User Attributes} + * of the certificate. + * + * @return identities + */ + public List getIdentities() + { + return new ArrayList<>(primaryKey.identityComponents); + } + + /** + * Return the current primary {@link OpenPGPUserId} of the certificate. + * + * @return primary user id + */ + public OpenPGPUserId getPrimaryUserId() + { + return getPrimaryUserId(new Date()); + } + + /** + * Return the {@link OpenPGPUserId} that is considered primary at the given evaluation time. + * + * @param evaluationTime evaluation time + * @return primary user-id at evaluation time + */ + public OpenPGPUserId getPrimaryUserId(Date evaluationTime) + { + return primaryKey.getExplicitOrImplicitPrimaryUserId(evaluationTime); + } + + /** + * Return the {@link OpenPGPUserId} object matching the given user-id {@link String}. + * + * @param userId user-id + * @return user-id + */ + public OpenPGPUserId getUserId(String userId) + { + for (Iterator it = primaryKey.getUserIDs().iterator(); it.hasNext(); ) + { + OpenPGPUserId uid = (OpenPGPUserId)it.next(); + if (uid.getUserId().equals(userId)) + { + return uid; + } + } + return null; + } + + /** + * Return the time at which the certificate expires. + * + * @return expiration time of the certificate + */ + public Date getExpirationTime() + { + return getExpirationTime(new Date()); + } + + /** + * Return the time at which the certificate is expected to expire, considering the given evaluation time. + * + * @param evaluationTime reference time + * @return expiration time at evaluation time + */ + public Date getExpirationTime(Date evaluationTime) + { + return getPrimaryKey().getKeyExpirationDateAt(evaluationTime); + } + + /** + * Return an {@link OpenPGPSignatureChain} from the given 3rd-party certificate to this certificate, + * which represents a delegation of trust. + * If no delegation signature is found, return null. + * + * @param thirdPartyCertificate {@link OpenPGPCertificate} of a 3rd party. + * @return chain containing the latest delegation issued by the 3rd-party certificate + */ + public OpenPGPSignatureChain getDelegationBy(OpenPGPCertificate thirdPartyCertificate) + { + return getDelegationBy(thirdPartyCertificate, new Date()); + } + + /** + * Return an {@link OpenPGPSignatureChain} from the given 3rd-party certificate to this certificate, + * which represents a delegation of trust at evaluation time. + * If no delegation signature is found, return null. + * + * @param thirdPartyCertificate {@link OpenPGPCertificate} of a 3rd party. + * @param evaluationTime reference time + * @return chain containing the (at evaluation time) latest delegation issued by the 3rd-party certificate + */ + public OpenPGPSignatureChain getDelegationBy( + OpenPGPCertificate thirdPartyCertificate, + Date evaluationTime) + { + OpenPGPSignatureChains chainsBy = getPrimaryKey(). + getMergedDanglingExternalSignatureChainEndsFrom(thirdPartyCertificate, evaluationTime); + return chainsBy.getCertificationAt(evaluationTime); + } + + /** + * Return an {@link OpenPGPSignatureChain} from the given 3rd-party certificate to this certificate, + * which represents a revocation of trust. + * + * @param thirdPartyCertificate {@link OpenPGPCertificate} of a 3rd party. + * @return chain containing the latest revocation issued by the 3rd party certificate + */ + public OpenPGPSignatureChain getRevocationBy(OpenPGPCertificate thirdPartyCertificate) + { + return getRevocationBy(thirdPartyCertificate, new Date()); + } + + /** + * Return an {@link OpenPGPSignatureChain} from the given 3rd-party certificate to this certificate, + * which (at evaluation time) represents a revocation of trust. + * + * @param thirdPartyCertificate {@link OpenPGPCertificate} of a 3rd party. + * @param evaluationTime reference time + * @return chain containing the (at evaluation time) latest revocation issued by the 3rd party certificate + */ + public OpenPGPSignatureChain getRevocationBy( + OpenPGPCertificate thirdPartyCertificate, + Date evaluationTime) + { + OpenPGPSignatureChains chainsBy = getPrimaryKey() + .getMergedDanglingExternalSignatureChainEndsFrom(thirdPartyCertificate, evaluationTime); + return chainsBy.getRevocationAt(evaluationTime); + } + + /** + * Component on an OpenPGP certificate. + * Components can either be {@link OpenPGPComponentKey keys} or {@link OpenPGPIdentityComponent identities}. + */ + public static abstract class OpenPGPCertificateComponent + { + private final OpenPGPCertificate certificate; + + public OpenPGPCertificateComponent(OpenPGPCertificate certificate) + { + this.certificate = certificate; + } + + /** + * Return this components {@link OpenPGPCertificate}. + * + * @return certificate + */ + public OpenPGPCertificate getCertificate() + { + return certificate; + } + + /** + * Return a detailed String representation of this component. + * + * @return detailed String representation + */ + public abstract String toDetailString(); + + /** + * Return true, if the component is currently validly bound to the certificate. + * + * @return true if bound + */ + public boolean isBound() + { + return isBoundAt(new Date()); + } + + /** + * Return true, if this component is - at evaluation time - properly bound to its certificate. + * + * @param evaluationTime evaluation time + * @return true if bound, false otherwise + */ + public boolean isBoundAt(Date evaluationTime) + { + return getCertificate().isBound(this, evaluationTime); + } + + /** + * Return all {@link OpenPGPSignatureChains} that bind this component. + * + * @return signature chains + */ + public OpenPGPSignatureChains getSignatureChains() + { + OpenPGPSignatureChains chains = getCertificate().getAllSignatureChainsFor(this); + if (getPublicComponent() instanceof OpenPGPPrimaryKey) + { + OpenPGPPrimaryKey pk = (OpenPGPPrimaryKey)getPublicComponent(); + if (!pk.getUserIDs().isEmpty()) + { + chains.addAll(getCertificate().getAllSignatureChainsFor(pk.getUserIDs().get(0))); + } + } + return chains; + } + + /** + * Return the (at evaluation time) latest certification signature binding this component. + * + * @param evaluationTime reference time + * @return latest component certification signature + */ + public OpenPGPComponentSignature getCertification(Date evaluationTime) + { + OpenPGPSignatureChain certification = getSignatureChains().getCertificationAt(evaluationTime); + if (certification != null) + { + return certification.getSignature(); + } + return null; + } + + /** + * Return the (at evaluation time) latest revocation signature revoking this component. + * + * @param evaluationTime reference time + * @return latest component revocation signature + */ + public OpenPGPComponentSignature getRevocation(Date evaluationTime) + { + OpenPGPSignatureChain revocation = getSignatureChains().getRevocationAt(evaluationTime); + if (revocation != null) + { + return revocation.getSignature(); + } + return null; + } + + /** + * Return the latest self-signature on the component. + * That might either be a certification signature, or a revocation. + * + * @return latest self signature + */ + public OpenPGPComponentSignature getLatestSelfSignature() + { + return getLatestSelfSignature(new Date()); + } + + /** + * Return the (at evaluation time) latest self-signature on the component. + * That might either be a certification signature, or a revocation. + * + * @param evaluationTime reference time + * @return latest self signature + */ + public abstract OpenPGPComponentSignature getLatestSelfSignature(Date evaluationTime); + + /** + * Return the public {@link OpenPGPCertificateComponent} that belongs to this component. + * For public components (pubkeys, identities...), that's simply this, while secret components + * return their corresponding public component. + * This is used to properly map secret key and public key components in {@link Map Maps} that use + * {@link OpenPGPCertificateComponent components} as map keys. + * + * @return public certificate component + */ + protected OpenPGPCertificateComponent getPublicComponent() + { + return this; + } + + /** + * Return the {@link OpenPGPComponentKey} belonging to this {@link OpenPGPCertificateComponent}. + * If this {@link OpenPGPCertificateComponent} is an instance of {@link OpenPGPComponentKey}, + * the method simply returns
    this
    . + * If instead, the {@link OpenPGPCertificateComponent} is an {@link OpenPGPIdentityComponent}, + * the primary key it is bound to is returned. + * + * @return {@link OpenPGPComponentKey} of this {@link OpenPGPCertificateComponent}. + */ + protected abstract OpenPGPComponentKey getKeyComponent(); + + /** + * Return the {@link KeyFlags} signature subpacket that currently applies to the key. + * + * @return key flags subpacket + */ + public KeyFlags getKeyFlags() + { + return getKeyFlags(new Date()); + } + + /** + * Return the {@link KeyFlags} signature subpacket that - at evaluation time - applies to the key. + * + * @param evaluationTime evaluation time + * @return key flags subpacket + */ + public KeyFlags getKeyFlags(Date evaluationTime) + { + OpenPGPSignature.OpenPGPSignatureSubpacket subpacket = getApplyingSubpacket( + evaluationTime, SignatureSubpacketTags.KEY_FLAGS); + if (subpacket != null) + { + return (KeyFlags)subpacket.getSubpacket(); + } + return null; + } + + /** + * Return
    true
    , if the key has any of the given key flags. + *

    + * Note: To check if the key has EITHER flag A or B, call

    hasKeyFlags(evalTime, A, B)
    . + * To instead check, if the key has BOTH flags A AND B, call
    hasKeyFlags(evalTime, A & B)
    . + * + * @param evaluationTime evaluation time + * @param flags key flags (see {@link KeyFlags} for possible values) + * @return true if the key has ANY of the provided flags + */ + public boolean hasKeyFlags(Date evaluationTime, int... flags) + { + KeyFlags keyFlags = getKeyFlags(evaluationTime); + if (keyFlags == null) + { + // Key has no key-flags + return false; + } + + // Check if key has the desired key-flags + for (int i = 0; i < flags.length; ++i) + { + if (((keyFlags.getFlags() & flags[i]) == flags[i])) + { + return true; + } + } + return false; + } + + /** + * Return the {@link Features} signature subpacket that currently applies to the key. + * + * @return feature signature subpacket + */ + public Features getFeatures() + { + return getFeatures(new Date()); + } + + /** + * Return the {@link Features} signature subpacket that - at evaluation time - applies to the key. + * + * @param evaluationTime evaluation time + * @return features subpacket + */ + public Features getFeatures(Date evaluationTime) + { + OpenPGPSignature.OpenPGPSignatureSubpacket subpacket = getApplyingSubpacket(evaluationTime, SignatureSubpacketTags.FEATURES); + if (subpacket != null) + { + return (Features)subpacket.getSubpacket(); + } + return null; + } + + /** + * Return the {@link PreferredAEADCiphersuites} that apply to this (sub-)key. + * Note: This refers to AEAD preferences as defined in rfc9580, NOT LibrePGP AEAD algorithms. + * + * @return AEAD algorithm preferences + */ + public PreferredAEADCiphersuites getAEADCipherSuitePreferences() + { + return getAEADCipherSuitePreferences(new Date()); + } + + /** + * Return the {@link PreferredAEADCiphersuites} that - at evaluation time - apply to this (sub-)key. + * Note: This refers to AEAD preferences as defined in rfc9580, NOT LibrePGP AEAD algorithms. + * + * @param evaluationTime evaluation time + * @return AEAD algorithm preferences at evaluation time + */ + public PreferredAEADCiphersuites getAEADCipherSuitePreferences(Date evaluationTime) + { + OpenPGPSignature.OpenPGPSignatureSubpacket subpacket = getApplyingSubpacket(evaluationTime, + SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS); + if (subpacket != null) + { + return (PreferredAEADCiphersuites)subpacket.getSubpacket(); + } + return null; + } + + /** + * Return the current symmetric encryption algorithm preferences of this (sub-)key. + * + * @return current preferred symmetric-key algorithm preferences + */ + public PreferredAlgorithms getSymmetricCipherPreferences() + { + return getSymmetricCipherPreferences(new Date()); + } + + /** + * Return the symmetric encryption algorithm preferences of this (sub-)key at evaluation time. + * + * @param evaluationTime evaluation time + * @return current preferred symmetric-key algorithm preferences + */ + public PreferredAlgorithms getSymmetricCipherPreferences(Date evaluationTime) + { + OpenPGPSignature.OpenPGPSignatureSubpacket subpacket = getApplyingSubpacket(evaluationTime, SignatureSubpacketTags.PREFERRED_SYM_ALGS); + if (subpacket != null) + { + return (PreferredAlgorithms)subpacket.getSubpacket(); + } + return null; + } + + /** + * Return the current signature hash algorithm preferences of this (sub-)key. + * + * @return hash algorithm preferences + */ + public PreferredAlgorithms getHashAlgorithmPreferences() + { + return getHashAlgorithmPreferences(new Date()); + } + + /** + * Return the signature hash algorithm preferences of this (sub-)key at evaluation time. + * + * @param evaluationTime evaluation time + * @return hash algorithm preferences + */ + public PreferredAlgorithms getHashAlgorithmPreferences(Date evaluationTime) + { + OpenPGPSignature.OpenPGPSignatureSubpacket subpacket = getApplyingSubpacket(evaluationTime, SignatureSubpacketTags.PREFERRED_HASH_ALGS); + if (subpacket != null) + { + return (PreferredAlgorithms)subpacket.getSubpacket(); + } + return null; + } + + /** + * Return the compression algorithm preferences of this (sub-)key. + * + * @return compression algorithm preferences + */ + public PreferredAlgorithms getCompressionAlgorithmPreferences() + { + return getCompressionAlgorithmPreferences(new Date()); + } + + /** + * Return the compression algorithm preferences of this (sub-)key at evaluation time. + * + * @param evaluationTime reference time + * @return compression algorithm preferences + */ + public PreferredAlgorithms getCompressionAlgorithmPreferences(Date evaluationTime) + { + OpenPGPSignature.OpenPGPSignatureSubpacket subpacket = getApplyingSubpacket(evaluationTime, SignatureSubpacketTags.PREFERRED_COMP_ALGS); + if (subpacket != null) + { + return (PreferredAlgorithms)subpacket.getSubpacket(); + } + return null; + } + + /** + * Return the {@link Date}, at which the key expires. + * + * @return key expiration time + */ + public Date getKeyExpirationDate() + { + return getKeyExpirationDateAt(new Date()); + } + + /** + * Return the {@link Date}, at which the key - at evaluation time - expires. + * + * @param evaluationTime evaluation time + * @return key expiration time + */ + public Date getKeyExpirationDateAt(Date evaluationTime) + { + return getLatestSelfSignature(evaluationTime).getKeyExpirationTime(); + } + + /** + * Return the {@link SignatureSubpacket} instance of the given subpacketType, which currently applies to + * the key. Since subpackets from the Direct-Key signature apply to all subkeys of a certificate, + * this method first inspects the signature that immediately applies to this key (e.g. a subkey-binding + * signature), and - if the queried subpacket is found in there, returns that instance. + * Otherwise, indirectly applying signatures (e.g. Direct-Key signatures) are queried. + * That way, preferences from the direct-key signature are considered, but per-key overwrites take precedence. + * + * @param evaluationTime evaluation time + * @param subpacketType subpacket type that is being searched for + * @return subpacket from directly or indirectly applying signature + * @see + * OpenPGP for application developers - Attribute Shadowing + */ + protected OpenPGPSignature.OpenPGPSignatureSubpacket getApplyingSubpacket(Date evaluationTime, int subpacketType) + { + OpenPGPSignatureChain binding = getSignatureChains().getCertificationAt(evaluationTime); + if (binding == null) + { + // is not bound + return null; + } + + // Check signatures + try + { + if (!binding.isValid()) + { + // Binding is incorrect + return null; + } + } + catch (PGPSignatureException e) + { + // Binding cannot be verified + return null; + } + + // find signature "closest to the key", e.g. subkey binding signature + OpenPGPComponentSignature keySignature = binding.getSignature(); + + PGPSignatureSubpacketVector hashedSubpackets = keySignature.getSignature().getHashedSubPackets(); + if (hashedSubpackets == null || !hashedSubpackets.hasSubpacket(subpacketType)) + { + // If the subkey binding signature doesn't carry the desired subpacket, + // check direct-key or primary uid sig instead + OpenPGPSignatureChain preferenceBinding = getCertificate().getPreferenceSignature(evaluationTime); + if (preferenceBinding == null) + { + // No direct-key / primary uid sig found -> No subpacket + return null; + } + keySignature = preferenceBinding.getSignature(); + hashedSubpackets = keySignature.getSignature().getHashedSubPackets(); + } + // else -> attribute from DK sig is shadowed by SB sig + + // Extract subpacket from hashed area + SignatureSubpacket subpacket = hashedSubpackets.getSubpacket(subpacketType); + if (subpacket == null) + { + return null; + } + return OpenPGPSignature.OpenPGPSignatureSubpacket.hashed(subpacket, keySignature); + } + + /** + * Iterate over signatures issued over this component by the given 3rd-party certificate, + * merge them with the (at evaluation time) valid self-certification chain and return the + * results. + * + * @param thirdPartyCertificate certificate of a 3rd party + * @param evaluationTime reference time + * @return all 3rd party signatures on this component, merged with their issuer chains + */ + protected OpenPGPSignatureChains getMergedDanglingExternalSignatureChainEndsFrom( + OpenPGPCertificate thirdPartyCertificate, + Date evaluationTime) + { + OpenPGPSignatureChains chainsBy = new OpenPGPSignatureChains(this); + + OpenPGPSignatureChains allChains = getCertificate().getAllSignatureChainsFor(this) + .getChainsAt(evaluationTime); + for (Iterator it = allChains.iterator(); it.hasNext(); ) + { + OpenPGPSignatureChain.Link rootLink = it.next().getRootLink(); + for (Iterator it2 = thirdPartyCertificate.getKeys().iterator(); it2.hasNext(); ) + { + OpenPGPComponentKey issuerKey = it2.next(); + if (KeyIdentifier.matches( + rootLink.getSignature().getKeyIdentifiers(), + issuerKey.getKeyIdentifier(), + true)) + { + OpenPGPSignatureChain externalChain = issuerKey.getSignatureChains().getChainAt(evaluationTime); + externalChain = externalChain.plus( + new OpenPGPComponentSignature(rootLink.signature.getSignature(), issuerKey, this)); + chainsBy.add(externalChain); + } + } + } + return chainsBy; + } + } + + /** + * OpenPGP Signature made over some {@link OpenPGPCertificateComponent} on a {@link OpenPGPCertificate}. + */ + public static class OpenPGPComponentSignature + extends OpenPGPSignature + { + + private final OpenPGPCertificateComponent target; + + /** + * Component signature. + * + * @param signature signature + * @param issuer key that issued the signature. + * Is nullable (e.g. for 3rd party sigs where the certificate is not available). + * @param target signed certificate component + */ + public OpenPGPComponentSignature(PGPSignature signature, + OpenPGPComponentKey issuer, + OpenPGPCertificateComponent target) + { + super(signature, issuer); + this.target = target; + } + + /** + * Return the {@link OpenPGPComponentKey} that issued this signature. + * + * @return issuer + */ + public OpenPGPComponentKey getIssuerComponent() + { + return getIssuer(); + } + + /** + * Return the {@link OpenPGPCertificateComponent} that this signature was calculated over. + * + * @return target + */ + public OpenPGPCertificateComponent getTargetComponent() + { + return target; + } + + /** + * Return the {@link OpenPGPComponentKey} that this signature is calculated over. + * Contrary to {@link #getTargetComponent()}, which returns the actual target, this method returns the + * {@link OpenPGPComponentKey} "closest" to the target. + * For a subkey-binding signature, this is the target subkey, while for an identity-binding signature + * (binding for a user-id or attribute) the return value is the {@link OpenPGPComponentKey} which + * carries the identity. + * + * @return target key component of the signature + */ + public OpenPGPComponentKey getTargetKeyComponent() + { + if (getTargetComponent() instanceof OpenPGPIdentityComponent) + { + // Identity signatures indirectly authenticate the primary key + return ((OpenPGPIdentityComponent)getTargetComponent()).getPrimaryKey(); + } + if (getTargetComponent() instanceof OpenPGPComponentKey) + { + // Key signatures authenticate the target key + return (OpenPGPComponentKey)getTargetComponent(); + } + throw new IllegalArgumentException("Unknown target type."); + } + + /** + * Return the key expiration time stored in the signature. + * + * @return key expiration time + */ + public Date getKeyExpirationTime() + { + PGPSignatureSubpacketVector hashed = signature.getHashedSubPackets(); + if (hashed == null) + { + // v3 sigs have no expiration + return null; + } + long exp = hashed.getKeyExpirationTime(); + if (exp < 0) + { + throw new RuntimeException("Negative key expiration time"); + } + + if (exp == 0L) + { + // Explicit or implicit no expiration + return null; + } + + return new Date(getTargetKeyComponent().getCreationTime().getTime() + 1000 * exp); + } + + /** + * Verify this signature. + * + * @param implementation OpenPGP implementation + * @throws PGPSignatureException if the signature cannot be verified successfully + */ + public void verify(OpenPGPImplementation implementation) + throws PGPSignatureException + { + verify(implementation.pgpContentVerifierBuilderProvider(), implementation.policy()); + } + + /** + * Verify this signature. + * + * @param contentVerifierBuilderProvider provider for verifiers + * @param policy algorithm policy + * @throws PGPSignatureException if the signature cannot be verified successfully + */ + public void verify(PGPContentVerifierBuilderProvider contentVerifierBuilderProvider, + OpenPGPPolicy policy) + throws PGPSignatureException + { + if (issuer == null) + { + // No issuer available + throw new MissingIssuerCertException(this, "Issuer certificate unavailable."); + } + + sanitize(issuer, policy); + + // Direct-Key signature + if (signature.getSignatureType() == PGPSignature.DIRECT_KEY + || signature.getSignatureType() == PGPSignature.KEY_REVOCATION) + { + verifyKeySignature( + issuer, + target.getKeyComponent(), + contentVerifierBuilderProvider); + } + + // Subkey binding signature + else if (signature.getSignatureType() == PGPSignature.SUBKEY_BINDING) + { + // For signing-capable subkeys, check the embedded primary key binding signature + verifyEmbeddedPrimaryKeyBinding(contentVerifierBuilderProvider, policy, getCreationTime()); + + // Binding signature MUST NOT predate the subkey itself + if (((OpenPGPSubkey)target).getCreationTime().after(signature.getCreationTime())) + { + isCorrect = false; + throw new MalformedOpenPGPSignatureException(this, "Subkey binding signature predates subkey creation time."); + } + + verifyKeySignature( + issuer, + (OpenPGPSubkey)target, + contentVerifierBuilderProvider); + } + + else if (signature.getSignatureType() == PGPSignature.SUBKEY_REVOCATION) + { + // Binding signature MUST NOT predate the subkey itself + if (((OpenPGPSubkey)target).getCreationTime().after(signature.getCreationTime())) + { + isCorrect = false; + throw new MalformedOpenPGPSignatureException(this, "Subkey revocation signature predates subkey creation time."); + } + + verifyKeySignature( + issuer, + (OpenPGPSubkey)target, + contentVerifierBuilderProvider); + } + + // User-ID binding + else if (target instanceof OpenPGPUserId) + { + verifyUserIdSignature( + issuer, + (OpenPGPUserId)target, + contentVerifierBuilderProvider); + } + + // User-Attribute binding + else if (target instanceof OpenPGPUserAttribute) + { + verifyUserAttributeSignature( + issuer, + (OpenPGPUserAttribute)target, + contentVerifierBuilderProvider); + } + + else + { + throw new PGPSignatureException("Unexpected signature type: " + getType()); + } + } + + /** + * Verify a signature of type {@link PGPSignature#PRIMARYKEY_BINDING}, which is typically embedded as + * {@link org.bouncycastle.bcpg.sig.EmbeddedSignature} inside a signature of type + * {@link PGPSignature#SUBKEY_BINDING} for a signing capable subkey. + * + * @param contentVerifierBuilderProvider provider for content verifier builders + * @param policy algorithm policy + * @param signatureCreationTime creation time of the signature + * @throws PGPSignatureException if an exception happens during signature verification + */ + private void verifyEmbeddedPrimaryKeyBinding(PGPContentVerifierBuilderProvider contentVerifierBuilderProvider, + OpenPGPPolicy policy, Date signatureCreationTime) + throws PGPSignatureException + { + int keyFlags = signature.getHashedSubPackets().getKeyFlags(); + if ((keyFlags & KeyFlags.SIGN_DATA) != KeyFlags.SIGN_DATA) + { + // Non-signing key - no embedded primary key binding sig required + return; + } + + OpenPGPComponentKey subkey = getTargetKeyComponent(); + // Signing subkey needs embedded primary key binding signature + List embeddedSignatures = new ArrayList<>(); + try + { + PGPSignatureList sigList = signature.getHashedSubPackets().getEmbeddedSignatures(); + addSignatures(embeddedSignatures, sigList); + sigList = signature.getUnhashedSubPackets().getEmbeddedSignatures(); + addSignatures(embeddedSignatures, sigList); + } + catch (PGPException e) + { + throw new PGPSignatureException("Cannot extract embedded signature.", e); + } + + if (embeddedSignatures.isEmpty()) + { + throw new MalformedOpenPGPSignatureException( + this, + "Signing key SubkeyBindingSignature MUST contain embedded PrimaryKeyBindingSignature."); + } + + PGPSignatureException exception = null; + for (Iterator it = embeddedSignatures.iterator(); it.hasNext(); ) + { + PGPSignature primaryKeyBinding = it.next(); + OpenPGPCertificate.OpenPGPComponentSignature backSig = + new OpenPGPCertificate.OpenPGPComponentSignature( + primaryKeyBinding, + subkey, + issuer); + + if (primaryKeyBinding.getSignatureType() != PGPSignature.PRIMARYKEY_BINDING) + { + exception = new PGPSignatureException("Unexpected embedded signature type: " + primaryKeyBinding.getSignatureType()); + continue; + } + + if (!backSig.isEffectiveAt(signatureCreationTime)) + { + exception = new PGPSignatureException("Embedded PrimaryKeyBinding signature is expired or not yet effective."); + continue; + } + + try + { + backSig.sanitize(subkey, policy); + + // needs to be called last to prevent false positives + backSig.verifyKeySignature(subkey, issuer, contentVerifierBuilderProvider); + + // valid -> return successfully + return; + } + catch (PGPSignatureException e) + { + exception = e; + } + } + + // if we end up here, it means we have only found invalid sigs + throw exception; + } + + private static void addSignatures(List embeddedSignatures, PGPSignatureList sigList) + { + for (Iterator it = sigList.iterator(); it.hasNext(); ) + { + embeddedSignatures.add(it.next()); + } + } + + /** + * Verify a signature of type {@link PGPSignature#DIRECT_KEY}, {@link PGPSignature#KEY_REVOCATION}, + * {@link PGPSignature#SUBKEY_BINDING} or {@link PGPSignature#SUBKEY_REVOCATION}. + * + * @param issuer issuing component key + * @param target targeted component key + * @param contentVerifierBuilderProvider provider for content verifier builders + * @throws PGPSignatureException if an exception happens during signature verification + */ + protected void verifyKeySignature( + OpenPGPComponentKey issuer, + OpenPGPComponentKey target, + PGPContentVerifierBuilderProvider contentVerifierBuilderProvider) + throws PGPSignatureException + { + this.isTested = true; + try + { + signature.init(contentVerifierBuilderProvider, issuer.getPGPPublicKey()); + if (signature.getSignatureType() == PGPSignature.DIRECT_KEY + || signature.getSignatureType() == PGPSignature.KEY_REVOCATION) + { + // Direct-Key Signature + isCorrect = signature.verifyCertification(target.getPGPPublicKey()); + } + else if (signature.getSignatureType() == PGPSignature.PRIMARYKEY_BINDING) + { + isCorrect = signature.verifyCertification(target.getPGPPublicKey(), issuer.getPGPPublicKey()); + } + else + { + // Subkey Binding Signature + isCorrect = signature.verifyCertification(issuer.getPGPPublicKey(), target.getPGPPublicKey()); + } + + if (!isCorrect) + { + throw new IncorrectOpenPGPSignatureException(this, "Key Signature is not correct."); + } + } + catch (PGPException e) + { + this.isCorrect = false; + throw new PGPSignatureException("Key Signature could not be verified.", e); + } + } + + /** + * Verify a certification signature over an {@link OpenPGPUserId}. + * The signature is of type {@link PGPSignature#DEFAULT_CERTIFICATION}, {@link PGPSignature#NO_CERTIFICATION}, + * {@link PGPSignature#CASUAL_CERTIFICATION}, {@link PGPSignature#POSITIVE_CERTIFICATION} or + * {@link PGPSignature#CERTIFICATION_REVOCATION}. + * + * @param issuer issuing component key + * @param target targeted userid + * @param contentVerifierBuilderProvider provider for content verifier builders + * @throws PGPSignatureException if an exception happens during signature verification + */ + protected void verifyUserIdSignature(OpenPGPComponentKey issuer, + OpenPGPUserId target, + PGPContentVerifierBuilderProvider contentVerifierBuilderProvider) + throws PGPSignatureException + { + this.isTested = true; + try + { + signature.init(contentVerifierBuilderProvider, issuer.getPGPPublicKey()); + isCorrect = signature.verifyCertification(target.getUserId(), target.getPrimaryKey().getPGPPublicKey()); + if (!isCorrect) + { + throw new IncorrectOpenPGPSignatureException(this, "UserID Signature is not correct."); + } + } + catch (PGPException e) + { + this.isCorrect = false; + throw new PGPSignatureException("UserID Signature could not be verified.", e); + } + } + + /** + * Verify a certification signature over an {@link OpenPGPUserAttribute}. + * The signature is of type {@link PGPSignature#DEFAULT_CERTIFICATION}, {@link PGPSignature#NO_CERTIFICATION}, + * {@link PGPSignature#CASUAL_CERTIFICATION}, {@link PGPSignature#POSITIVE_CERTIFICATION} or + * {@link PGPSignature#CERTIFICATION_REVOCATION}. + * + * @param issuer issuing component key + * @param target targeted userid + * @param contentVerifierBuilderProvider provider for content verifier builders + * @throws PGPSignatureException if an exception happens during signature verification + */ + protected void verifyUserAttributeSignature(OpenPGPComponentKey issuer, + OpenPGPUserAttribute target, + PGPContentVerifierBuilderProvider contentVerifierBuilderProvider) + throws PGPSignatureException + { + this.isTested = true; + try + { + signature.init(contentVerifierBuilderProvider, issuer.getPGPPublicKey()); + isCorrect = signature.verifyCertification(target.getUserAttribute(), target.getPrimaryKey().getPGPPublicKey()); + if (!isCorrect) + { + throw new IncorrectOpenPGPSignatureException(this, "UserAttribute Signature is not correct."); + } + } + catch (PGPException e) + { + this.isCorrect = false; + throw new PGPSignatureException("Could not verify UserAttribute Signature.", e); + } + } + + @Override + protected String getTargetDisplay() + { + return target.toString(); + } + } + + /** + * A component key is either an {@link OpenPGPPrimaryKey}, or an {@link OpenPGPSubkey}. + * + * @see + * OpenPGP for Application Developers - Layers of keys in OpenPGP + */ + public static abstract class OpenPGPComponentKey + extends OpenPGPCertificateComponent + { + protected final PGPPublicKey rawPubkey; + + /** + * Constructor. + * + * @param rawPubkey public key + * @param certificate certificate + */ + public OpenPGPComponentKey(PGPPublicKey rawPubkey, OpenPGPCertificate certificate) + { + super(certificate); + this.rawPubkey = rawPubkey; + } + + /** + * Return the underlying {@link PGPPublicKey} of this {@link OpenPGPComponentKey}. + * + * @return public key + */ + public PGPPublicKey getPGPPublicKey() + { + return rawPubkey; + } + + /** + * Return the {@link KeyIdentifier} of this key. + * + * @return key identifier + */ + public KeyIdentifier getKeyIdentifier() + { + return rawPubkey.getKeyIdentifier(); + } + + /** + * Return the public key algorithm. + * + * @return public key algorithm id + * @see org.bouncycastle.bcpg.PublicKeyAlgorithmTags + */ + public int getAlgorithm() + { + return getPGPPublicKey().getAlgorithm(); + } + + /** + * Return the public key version. + * + * @return key version + */ + public int getVersion() + { + return getPGPPublicKey().getVersion(); + } + + /** + * Return the creation time of this key. + * + * @return creation time + */ + public Date getCreationTime() + { + return rawPubkey.getCreationTime(); + } + + /** + * Return true, if this {@link OpenPGPComponentKey} represents the primary key of an {@link OpenPGPCertificate}. + * + * @return true if primary, false if subkey + */ + public abstract boolean isPrimaryKey(); + + @Override + public OpenPGPComponentSignature getLatestSelfSignature(Date evaluationTime) + { + OpenPGPSignatureChain currentDKChain = getSignatureChains().getChainAt(evaluationTime); + if (currentDKChain != null && !currentDKChain.chainLinks.isEmpty()) + { + return currentDKChain.getSignature(); + } + return null; + } + + /** + * Return true, if the key is currently marked as encryption key. + * + * @return true if the key is an encryption key, false otherwise + */ + public boolean isEncryptionKey() + { + return isEncryptionKey(new Date()); + } + + /** + * Return true, if the is - at evaluation time - marked as an encryption key. + * + * @param evaluationTime evaluation time + * @return true if key is an encryption key at evaluation time, false otherwise + */ + public boolean isEncryptionKey(Date evaluationTime) + { + if (!rawPubkey.isEncryptionKey()) + { + // Skip keys that are not encryption-capable by algorithm + return false; + } + + return hasKeyFlags(evaluationTime, KeyFlags.ENCRYPT_STORAGE) || + hasKeyFlags(evaluationTime, KeyFlags.ENCRYPT_COMMS); + } + + /** + * Return true, if the key is currently marked as a signing key for message signing. + * + * @return true, if key is currently signing key + */ + public boolean isSigningKey() + { + return isSigningKey(new Date()); + } + + /** + * Return true, if the key is - at evaluation time - marked as signing key for message signing. + * + * @param evaluationTime evaluation time + * @return true if key is signing key at evaluation time + */ + public boolean isSigningKey(Date evaluationTime) + { + if (!PublicKeyUtils.isSigningAlgorithm(rawPubkey.getAlgorithm())) + { + // Key is not signing-capable by algorithm + return false; + } + + return hasKeyFlags(evaluationTime, KeyFlags.SIGN_DATA); + } + + /** + * Return true, if the key is currently marked as certification key that can sign 3rd-party certificates. + * + * @return true, if key is certification key + */ + public boolean isCertificationKey() + { + return isCertificationKey(new Date()); + } + + /** + * Return true, if the key is - at evaluation time - marked as certification key that can sign 3rd-party + * certificates. + * + * @param evaluationTime evaluation time + * @return true if key is certification key at evaluation time + */ + public boolean isCertificationKey(Date evaluationTime) + { + if (!PublicKeyUtils.isSigningAlgorithm(rawPubkey.getAlgorithm())) + { + // Key is not signing-capable by algorithm + return false; + } + + return hasKeyFlags(evaluationTime, KeyFlags.CERTIFY_OTHER); + } + + @Override + protected OpenPGPComponentKey getKeyComponent() + { + // This already IS a component key + return this; + } + + @Override + public int hashCode() + { + return getPGPPublicKey().hashCode(); + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + if (this == obj) + { + return true; + } + if (!(obj instanceof OpenPGPComponentKey)) + { + return false; + } + OpenPGPComponentKey other = (OpenPGPComponentKey)obj; + return getPGPPublicKey().equals(other.getPGPPublicKey()); + } + } + + /** + * The primary key of a {@link OpenPGPCertificate}. + */ + public static class OpenPGPPrimaryKey + extends OpenPGPComponentKey + { + @Override + public String toString() + { + return "PrimaryKey[" + Long.toHexString(getKeyIdentifier().getKeyId()).toUpperCase(Locale.getDefault()) + "]"; + } + + @Override + public String toDetailString() + { + return "PrimaryKey[" + getKeyIdentifier() + "] (" + UTCUtil.format(getCreationTime()) + ")"; + } + + protected final List identityComponents; + + public OpenPGPPrimaryKey(PGPPublicKey rawPubkey, OpenPGPCertificate certificate) + { + super(rawPubkey, certificate); + this.identityComponents = new ArrayList<>(); + + Iterator userIds = rawPubkey.getUserIDs(); + while (userIds.hasNext()) + { + identityComponents.add(new OpenPGPUserId(userIds.next(), this)); + } + + Iterator userAttributes = rawPubkey.getUserAttributes(); + while (userAttributes.hasNext()) + { + identityComponents.add(new OpenPGPUserAttribute(userAttributes.next(), this)); + } + } + + @Override + public boolean isPrimaryKey() + { + return true; + } + + /** + * Return the latest DirectKey self-signature on this primary key. + * + * @return latest direct key self-signature. + */ + public OpenPGPComponentSignature getLatestDirectKeySelfSignature() + { + return getLatestDirectKeySelfSignature(new Date()); + } + + /** + * Return the (at evaluation time) latest DirectKey self-signature on this primary key. + * + * @param evaluationTime reference time + * @return latest (at evaluation time) direct key self-signature + */ + public OpenPGPComponentSignature getLatestDirectKeySelfSignature(Date evaluationTime) + { + OpenPGPSignatureChain currentDKChain = getCertificate().getAllSignatureChainsFor(this) + .getCertificationAt(evaluationTime); + if (currentDKChain != null && !currentDKChain.chainLinks.isEmpty()) + { + return currentDKChain.getSignature(); + } + + return null; + } + + /** + * Return the latest KeyRevocation self-signature on this primary key. + * + * @return latest key revocation self-signature + */ + public OpenPGPComponentSignature getLatestKeyRevocationSelfSignature() + { + return getLatestKeyRevocationSelfSignature(new Date()); + } + + /** + * Return the (at evaluation time) latest KeyRevocation self-signature on this primary key. + * + * @param evaluationTime reference time + * @return latest (at evaluation time) key revocation self-signature + */ + public OpenPGPComponentSignature getLatestKeyRevocationSelfSignature(Date evaluationTime) + { + OpenPGPSignatureChain currentRevocationChain = getCertificate().getAllSignatureChainsFor(this) + .getRevocationAt(evaluationTime); + if (currentRevocationChain != null && !currentRevocationChain.chainLinks.isEmpty()) + { + return currentRevocationChain.getSignature(); + } + return null; + } + + @Override + public OpenPGPComponentSignature getLatestSelfSignature(Date evaluationTime) + { + List signatures = new ArrayList<>(); + + OpenPGPComponentSignature directKeySig = getLatestDirectKeySelfSignature(evaluationTime); + if (directKeySig != null) + { + signatures.add(directKeySig); + } + + OpenPGPComponentSignature keyRevocation = getLatestKeyRevocationSelfSignature(evaluationTime); + if (keyRevocation != null) + { + signatures.add(keyRevocation); + } + + for (Iterator it = getCertificate().getIdentities().iterator(); it.hasNext(); ) + { + OpenPGPComponentSignature identitySig = it.next().getLatestSelfSignature(evaluationTime); + if (identitySig != null) + { + signatures.add(identitySig); + } + } + + OpenPGPComponentSignature latest = null; + for (Iterator it = signatures.iterator(); it.hasNext(); ) + { + OpenPGPComponentSignature signature = it.next(); + if (latest == null || signature.getCreationTime().after(latest.getCreationTime())) + { + latest = signature; + } + } + return latest; + } + + /** + * Return all {@link OpenPGPUserId OpenPGPUserIds} on this key. + * + * @return user ids + */ + public List getUserIDs() + { + List userIds = new ArrayList<>(); + for (Iterator it = identityComponents.iterator(); it.hasNext(); ) + { + OpenPGPIdentityComponent identity = it.next(); + if (identity instanceof OpenPGPUserId) + { + userIds.add((OpenPGPUserId)identity); + } + } + return userIds; + } + + /** + * Return a {@link List} containing all currently valid {@link OpenPGPUserId OpenPGPUserIds} on this + * primary key. + * + * @return valid userids + */ + public List getValidUserIds() + { + return getValidUserIDs(new Date()); + } + + /** + * Return a {@link List} containing all valid (at evaluation time) {@link OpenPGPUserId OpenPGPUserIds} + * on this primary key. + * + * @param evaluationTime reference time + * @return valid (at evaluation time) userids + */ + public List getValidUserIDs(Date evaluationTime) + { + List userIds = new ArrayList<>(); + for (Iterator it = identityComponents.iterator(); it.hasNext(); ) + { + OpenPGPIdentityComponent identity = it.next(); + if (identity instanceof OpenPGPUserId && identity.isBoundAt(evaluationTime)) + { + userIds.add((OpenPGPUserId)identity); + } + } + return userIds; + } + + /** + * Return the {@link OpenPGPUserId}, which is - at evaluation time - explicitly marked as primary. + * + * @param evaluationTime evaluation time + * @return explicit primary userid + */ + public OpenPGPUserId getExplicitPrimaryUserId(Date evaluationTime) + { + // Return the latest, valid, explicitly marked as primary UserID + OpenPGPSignature latestBinding = null; + OpenPGPUserId latestUid = null; + + for (Iterator it = getUserIDs().iterator(); it.hasNext(); ) + { + OpenPGPUserId userId = it.next(); + OpenPGPSignature.OpenPGPSignatureSubpacket subpacket = + userId.getApplyingSubpacket(evaluationTime, SignatureSubpacketTags.PRIMARY_USER_ID); + if (subpacket == null) + { + // Not bound at this time, or not explicit + continue; + } + + PrimaryUserID primaryUserId = (PrimaryUserID)subpacket.getSubpacket(); + if (!primaryUserId.isPrimaryUserID()) + { + // explicitly marked as not primary + continue; + } + + if (latestBinding == null || + subpacket.getSignature().getCreationTime().after(latestBinding.getCreationTime())) + { + latestBinding = subpacket.getSignature(); + latestUid = userId; + } + } + return latestUid; + } + + /** + * Return the {@link OpenPGPUserId}, which is - at evaluation time - considered primary, + * either because it is explicitly marked as primary userid, or because it is implicitly primary + * (e.g. because it is the sole user-id on the key). + * + * @param evaluationTime evaluation time + * @return primary user-id + */ + public OpenPGPUserId getExplicitOrImplicitPrimaryUserId(Date evaluationTime) + { + OpenPGPUserId explicitPrimaryUserId = getExplicitPrimaryUserId(evaluationTime); + if (explicitPrimaryUserId != null) + { + return explicitPrimaryUserId; + } + + // If no explicitly marked, valid primary UserID is found, return the oldest, valid UserId instead. + OpenPGPSignature oldestBinding = null; + OpenPGPUserId oldestUid = null; + + for (Iterator it = getUserIDs().iterator(); it.hasNext(); ) + { + OpenPGPUserId userId = it.next(); + OpenPGPSignatureChain chain = userId.getSignatureChains() + .getCertificationAt(evaluationTime); + if (chain == null) + { + // Not valid at this time + continue; + } + + OpenPGPSignature binding = chain.getSignature(); + if (oldestBinding == null || + binding.getCreationTime().before(oldestBinding.getCreationTime())) + { + oldestBinding = binding; + oldestUid = userId; + } + } + return oldestUid; + } + + /** + * Return all {@link OpenPGPUserAttribute OpenPGPUserAttributes} on this key. + * + * @return user attributes + */ + public List getUserAttributes() + { + List userAttributes = new ArrayList<>(); + for (Iterator it = identityComponents.iterator(); it.hasNext(); ) + { + OpenPGPIdentityComponent identity = it.next(); + if (identity instanceof OpenPGPUserAttribute) + { + userAttributes.add((OpenPGPUserAttribute)identity); + } + } + return userAttributes; + } + + /** + * Return all direct-key and key-revocation signatures on the primary key. + * + * @return key signatures + */ + protected List getKeySignatures() + { + Iterator iterator = rawPubkey.getSignatures(); + List list = new ArrayList<>(); + while (iterator.hasNext()) + { + PGPSignature sig = iterator.next(); + int type = sig.getSignatureType(); + if (type != PGPSignature.DIRECT_KEY && type != PGPSignature.KEY_REVOCATION) + { + continue; + } + // try to find issuer for self-signature + OpenPGPCertificate.OpenPGPComponentKey issuer = getCertificate() + .getSigningKeyFor(sig); + + list.add(new OpenPGPCertificate.OpenPGPComponentSignature(sig, issuer, this)); + } + return list; + } + + /** + * Return all signatures on the given {@link OpenPGPUserId}. + * + * @param identity user-id + * @return list of user-id signatures + */ + protected List getUserIdSignatures(OpenPGPUserId identity) + { + Iterator iterator = rawPubkey.getSignaturesForID(identity.getUserId()); + return signIterToList(identity, iterator); + } + + /** + * Return all signatures on the given {@link OpenPGPUserAttribute}. + * + * @param identity user-attribute + * @return list of user-attribute signatures + */ + protected List getUserAttributeSignatures(OpenPGPUserAttribute identity) + { + Iterator iterator = rawPubkey.getSignaturesForUserAttribute(identity.getUserAttribute()); + return signIterToList(identity, iterator); + } + + private List signIterToList(OpenPGPIdentityComponent identity, Iterator iterator) + { + List list = new ArrayList<>(); + while (iterator.hasNext()) + { + PGPSignature sig = iterator.next(); + // try to find issuer for self-signature + OpenPGPComponentKey issuer = getCertificate() + .getSigningKeyFor(sig); + + list.add(new OpenPGPComponentSignature(sig, issuer, identity)); + } + return list; + } + } + + /** + * A subkey on a {@link OpenPGPCertificate}. + */ + public static class OpenPGPSubkey + extends OpenPGPComponentKey + { + public OpenPGPSubkey(PGPPublicKey rawPubkey, OpenPGPCertificate certificate) + { + super(rawPubkey, certificate); + } + + @Override + public boolean isPrimaryKey() + { + return false; + } + + @Override + public String toString() + { + return "Subkey[" + Long.toHexString(getKeyIdentifier().getKeyId()).toUpperCase(Locale.getDefault()) + "]"; + } + + @Override + public String toDetailString() + { + return "Subkey[" + getKeyIdentifier() + "] (" + UTCUtil.format(getCreationTime()) + ")"; + } + + /** + * Return all subkey-binding and -revocation signatures on the subkey. + * + * @return subkey signatures + */ + protected List getKeySignatures() + { + Iterator iterator = rawPubkey.getSignatures(); + List list = new ArrayList<>(); + while (iterator.hasNext()) + { + PGPSignature sig = iterator.next(); + int type = sig.getSignatureType(); + if (type != PGPSignature.SUBKEY_BINDING && type != PGPSignature.SUBKEY_REVOCATION) + { + continue; + } + // try to find issuer for self-signature + OpenPGPCertificate.OpenPGPComponentKey issuer = getCertificate() + .getSigningKeyFor(sig); + + list.add(new OpenPGPCertificate.OpenPGPComponentSignature(sig, issuer, this)); + } + return list; + } + } + + /** + * An identity bound to the {@link OpenPGPPrimaryKey} of a {@link OpenPGPCertificate}. + * An identity may either be a {@link OpenPGPUserId} or (deprecated) {@link OpenPGPUserAttribute}. + */ + public static abstract class OpenPGPIdentityComponent + extends OpenPGPCertificateComponent + { + private final OpenPGPPrimaryKey primaryKey; + + public OpenPGPIdentityComponent(OpenPGPPrimaryKey primaryKey) + { + super(primaryKey.getCertificate()); + this.primaryKey = primaryKey; + } + + /** + * Return the primary key, which this identity belongs to. + * + * @return primary key + */ + public OpenPGPPrimaryKey getPrimaryKey() + { + return primaryKey; + } + + @Override + public OpenPGPComponentSignature getLatestSelfSignature(Date evaluationTime) + { + OpenPGPSignatureChain currentChain = getSignatureChains().getChainAt(evaluationTime); + if (currentChain != null && !currentChain.chainLinks.isEmpty()) + { + return currentChain.getSignature(); + } + return null; + } + + @Override + protected OpenPGPComponentKey getKeyComponent() + { + return primaryKey; + } + + /** + * Return the latest {@link OpenPGPSignatureChain} containing a certification issued by the given + * 3rd-party certificate over this identity component. + * + * @param thirdPartyCertificate certificate of a 3rd party + * @return 3rd party certification + */ + public OpenPGPSignatureChain getCertificationBy(OpenPGPCertificate thirdPartyCertificate) + { + return getCertificationBy(thirdPartyCertificate, new Date()); + } + + /** + * Return the latest (at evaluation time) {@link OpenPGPSignatureChain} containing a certification + * issued by the given 3rd-party certificate over this identity component. + * + * @param evaluationTime reference time + * @param thirdPartyCertificate certificate of a 3rd party + * @return 3rd party certification + */ + public OpenPGPSignatureChain getCertificationBy( + OpenPGPCertificate thirdPartyCertificate, + Date evaluationTime) + { + OpenPGPSignatureChains chainsBy = getMergedDanglingExternalSignatureChainEndsFrom(thirdPartyCertificate, evaluationTime); + return chainsBy.getCertificationAt(evaluationTime); + } + + /** + * Return the latest {@link OpenPGPSignatureChain} containing a revocation issued by the given + * 3rd-party certificate over this identity component. + * + * @param thirdPartyCertificate certificate of a 3rd party + * @return 3rd party revocation signature + */ + public OpenPGPSignatureChain getRevocationBy(OpenPGPCertificate thirdPartyCertificate) + { + return getRevocationBy(thirdPartyCertificate, new Date()); + } + + /** + * Return the latest (at evaluation time) {@link OpenPGPSignatureChain} containing a revocation issued by the given + * 3rd-party certificate over this identity component. + * + * @param thirdPartyCertificate certificate of a 3rd party + * @param evaluationTime reference time + * @return 3rd party revocation signature + */ + public OpenPGPSignatureChain getRevocationBy( + OpenPGPCertificate thirdPartyCertificate, + Date evaluationTime) + { + OpenPGPSignatureChains chainsBy = getMergedDanglingExternalSignatureChainEndsFrom(thirdPartyCertificate, evaluationTime); + return chainsBy.getRevocationAt(evaluationTime); + } + + @Override + public String toDetailString() + { + return toString(); + } + } + + /** + * A UserId. + */ + public static class OpenPGPUserId + extends OpenPGPIdentityComponent + { + private final String userId; + + public OpenPGPUserId(String userId, OpenPGPPrimaryKey primaryKey) + { + super(primaryKey); + this.userId = userId; + } + + /** + * Return the {@link String} representation of the {@link OpenPGPUserId}. + * + * @return user-id + */ + public String getUserId() + { + return userId; + } + + @Override + public String toString() + { + return "UserID[" + userId + "]"; + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) + { + return false; + } + if (this == obj) + { + return true; + } + if (!(obj instanceof OpenPGPUserId)) + { + return false; + } + return getUserId().equals(((OpenPGPUserId)obj).getUserId()); + } + + @Override + public int hashCode() + { + return userId.hashCode(); + } + } + + /** + * A UserAttribute. + * Use of UserAttributes is deprecated in RFC9580. + */ + public static class OpenPGPUserAttribute + extends OpenPGPIdentityComponent + { + + private final PGPUserAttributeSubpacketVector userAttribute; + + public OpenPGPUserAttribute(PGPUserAttributeSubpacketVector userAttribute, OpenPGPPrimaryKey primaryKey) + { + super(primaryKey); + this.userAttribute = userAttribute; + } + + /** + * Return the underlying {@link PGPUserAttributeSubpacketVector} representing this {@link OpenPGPUserAttribute}. + * + * @return user attribute subpacket vector + */ + public PGPUserAttributeSubpacketVector getUserAttribute() + { + return userAttribute; + } + + @Override + public String toString() + { + return "UserAttribute" + userAttribute.toString(); + } + } + + /** + * Chain of {@link OpenPGPSignature signatures}. + * Such a chain originates from a certificates primary key and points towards some certificate component that + * is bound to the certificate. + * As for example a subkey can only be bound by a primary key that holds either at least one + * direct-key self-signature or at least one user-id binding signature, multiple signatures may form + * a validity chain. + * An {@link OpenPGPSignatureChain} can either be a certification + * ({@link #isCertification()}), e.g. it represents a positive binding, + * or it can be a revocation ({@link #isRevocation()}) which invalidates a positive binding. + */ + public static class OpenPGPSignatureChain + implements Comparable, Iterable + { + private final List chainLinks = new ArrayList<>(); + + private OpenPGPSignatureChain(Link rootLink) + { + this.chainLinks.add(rootLink); + } + + private OpenPGPSignatureChain(List links) + { + this.chainLinks.addAll(links); + } + + // copy constructor + private OpenPGPSignatureChain(OpenPGPSignatureChain copy) + { + this(copy.chainLinks); + } + + /** + * Return the signature from the leaf of the chain, which directly applies to the + * {@link OpenPGPCertificateComponent}. + * + * @return signature + */ + public OpenPGPComponentSignature getSignature() + { + return getLeafLink().getSignature(); + } + + /** + * Return the first revocation signature in the chain, or null if the chain does not contain any revocations. + * + * @return first revocation signature + */ + public OpenPGPComponentSignature getRevocation() + { + for (OpenPGPComponentSignature signature : getSignatures()) + { + if (signature.isRevocation()) + { + return signature; + } + } + return null; + } + + /** + * Return a List of all signatures in the chain. + * + * @return list of signatures + */ + public List getSignatures() + { + List signatures = new ArrayList<>(); + for (Link link : chainLinks) + { + signatures.add(link.getSignature()); + } + return signatures; + } + + /** + * Return an NEW instance of the {@link OpenPGPSignatureChain} with the new link appended. + * + * @param sig signature + * @return new instance + */ + public OpenPGPSignatureChain plus(OpenPGPComponentSignature sig) + { + if (getLeafLinkTargetKey() != sig.getIssuerComponent()) + { + throw new IllegalArgumentException("Chain head is not equal to link issuer."); + } + + OpenPGPSignatureChain chain = new OpenPGPSignatureChain(this); + + chain.chainLinks.add(Link.create(sig)); + + return chain; + } + + /** + * Factory method for creating an {@link OpenPGPSignatureChain} with only a single link. + * + * @param sig signature + * @return chain + */ + public static OpenPGPSignatureChain direct(OpenPGPComponentSignature sig) + { + return new OpenPGPSignatureChain(Link.create(sig)); + } + + /** + * Return the very first link in the chain. + * This is typically a link that originates from the issuing certificates primary key. + * + * @return root link + */ + public Link getRootLink() + { + return chainLinks.get(0); + } + + /** + * Return the issuer of the root link. This is typically the issuing certificates primary key. + * + * @return root links issuer + */ + public OpenPGPComponentKey getRootLinkIssuer() + { + return getRootLink().getSignature().getIssuer(); + } + + /** + * Return the last link in the chain, which applies to the chains target component. + * + * @return leaf link + */ + public Link getLeafLink() + { + return chainLinks.get(chainLinks.size() - 1); + } + + /** + * Return the {@link OpenPGPComponentKey} to which the leaf link applies to. + * For subkey binding signatures, this is the subkey. + * For user-id certification signatures, it is the primary key. + * + * @return target key component of the leaf link + */ + public OpenPGPComponentKey getLeafLinkTargetKey() + { + return getSignature().getTargetKeyComponent(); + } + + /** + * Return true, if the chain only consists of non-revocation signatures and is therefore a certification chain. + * + * @return true if the chain is a certification, false if it contains a revocation link. + */ + public boolean isCertification() + { + for (Iterator it = chainLinks.iterator(); it.hasNext(); ) + { + if (it.next() instanceof Revocation) + { + return false; + } + } + return true; + } + + /** + * Return true, if the chain contains at least one revocation signature. + * + * @return true if the chain is a revocation. + */ + public boolean isRevocation() + { + for (Iterator it = chainLinks.iterator(); it.hasNext(); ) + { + if (it.next() instanceof Revocation) + { + return true; + } + } + return false; + } + + /** + * Return true, if the chain contains at least one link that represents a hard revocation. + * + * @return true if chain is hard revocation, false if it is a certification or soft revocation + */ + public boolean isHardRevocation() + { + for (Iterator it = chainLinks.iterator(); it.hasNext(); ) + { + if (it.next().signature.signature.isHardRevocation()) + { + return true; + } + } + return false; + } + + /** + * Return the date since which this signature chain is valid. + * This is the creation time of the most recent link in the chain. + * + * @return most recent signature creation time + */ + public Date getSince() + { + // Find most recent chain link +// return chainLinks.stream() +// .map(it -> it.signature) +// .max(Comparator.comparing(OpenPGPComponentSignature::getCreationTime)) +// .map(OpenPGPComponentSignature::getCreationTime) +// .orElse(null); + return chainLinks.stream() + .map(new Function() + { + @Override + public OpenPGPComponentSignature apply(Link it) + { + return it.signature; // Replace lambda: `it -> it.signature` + } + + }) + .max(new Comparator() + { + @Override + public int compare(Object o1, Object o2) + { + // Replace method reference: `Comparator.comparing(OpenPGPComponentSignature::getCreationTime)` + return ((OpenPGPComponentSignature)o1).getCreationTime().compareTo(((OpenPGPComponentSignature)o2).getCreationTime()); + } + }) + .map(new Function() + { + @Override + public Date apply(Object sig) + { + return ((OpenPGPComponentSignature)sig).getCreationTime(); // Replace method reference: `OpenPGPComponentSignature::getCreationTime` + } + }) + .orElse(null); + } + + /** + * Return the date until which the chain link is valid. + * This is the earliest expiration time of any signature in the chain. + * + * @return earliest expiration time + */ + public Date getUntil() + { + Date soonestExpiration = null; + for (Iterator it = chainLinks.iterator(); it.hasNext(); ) + { + Link link = it.next(); + Date until = link.until(); + if (until != null) + { + soonestExpiration = (soonestExpiration == null) ? until : + (until.before(soonestExpiration) ? until : soonestExpiration); + } + } + return soonestExpiration; + } + + /** + * Return true if the chain is effective at the given evaluation date, meaning all link signatures have + * been created before the evaluation time, and none signature expires before the evaluation time. + * + * @param evaluationDate reference time + * @return true if chain is effective at evaluation date + */ + public boolean isEffectiveAt(Date evaluationDate) + { + if (isHardRevocation()) + { + return true; + } + Date since = getSince(); + Date until = getUntil(); + // since <= eval <= until + return !evaluationDate.before(since) && (until == null || !evaluationDate.after(until)); + } + + /** + * Return true if the signature chain is valid, meaning all its chain links are valid. + * + * @return true if chain is valid + * @throws PGPSignatureException if an exception occurs during signature verification + */ + public boolean isValid() + throws PGPSignatureException + { + OpenPGPComponentKey rootKey = getRootLinkIssuer(); + if (rootKey == null) + { + throw new MissingIssuerCertException(getRootLink().signature, "Missing issuer certificate."); + } + OpenPGPCertificate cert = rootKey.getCertificate(); + return isValid(cert.implementation.pgpContentVerifierBuilderProvider(), cert.policy); + } + + /** + * Return true if the signature chain is valid, meaning all its chain links are valid. + * + * @param contentVerifierBuilderProvider provider for content verifier builders + * @param policy algorithm policy + * @return true if chain is valid + * @throws PGPSignatureException if an exception occurs during signature verification + */ + public boolean isValid(PGPContentVerifierBuilderProvider contentVerifierBuilderProvider, OpenPGPPolicy policy) + throws PGPSignatureException + { + boolean correct = true; + for (Iterator it = chainLinks.iterator(); it.hasNext(); ) + { + Link link = it.next(); + if (!link.signature.isTested) + { + link.verify(contentVerifierBuilderProvider, policy); + } + + if (!link.signature.isCorrect) + { + correct = false; + } + } + return correct; + } + + @Override + public String toString() + { + StringBuilder b = new StringBuilder(); + String until = getUntil() == null ? "EndOfTime" : UTCUtil.format(getUntil()); + b.append("From ").append(UTCUtil.format(getSince())).append(" until ").append(until).append("\n"); + for (Iterator it = chainLinks.iterator(); it.hasNext(); ) + { + Link link = it.next(); + b.append(" ").append(link.toString()).append("\n"); + } + return b.toString(); + } + + @Override + public int compareTo(OpenPGPSignatureChain other) + { + if (this == other) + { + return 0; + } + + if (isHardRevocation()) + { + return -1; + } + + if (other.isHardRevocation()) + { + return 1; + } + + int compare = -getRootLink().since().compareTo(other.getRootLink().since()); + if (compare != 0) + { + return compare; + } + + compare = -getLeafLink().since().compareTo(other.getLeafLink().since()); + if (compare != 0) + { + return compare; + } + + if (isRevocation()) + { + return 1; + } + return -1; + } + + @Override + public Iterator iterator() + { + return chainLinks.iterator(); + } + + /** + * Link in a {@link OpenPGPSignatureChain}. + */ + public static abstract class Link + { + protected final OpenPGPComponentSignature signature; + + public Link(OpenPGPComponentSignature signature) + { + this.signature = signature; + } + + /** + * Return the {@link Date} since when the link is effective. + * This is the creation time of the signature. + * + * @return signature creation time + */ + public Date since() + { + return signature.getCreationTime(); + } + + /** + * Return the {@link Date} until the signature is effective. + * This is, depending on which event is earlier in time, either the signature expiration time, + * or the key expiration time. + * + * @return time until the link is valid + */ + public Date until() + { + Date backSigExpiration = getBackSigExpirationTime(); + Date expirationTime = signature.getExpirationTime(); + + if (expirationTime == null) + { + return backSigExpiration; + } + + if (backSigExpiration == null || expirationTime.before(backSigExpiration)) + { + return expirationTime; + } + return backSigExpiration; + } + + /** + * Return the expiration time of the primary key binding signature. + * + * @return primary key binding signature expiration time + */ + private Date getBackSigExpirationTime() + { + if (signature.getSignature().getSignatureType() != PGPSignature.SUBKEY_BINDING) + { + return null; + } + + PGPSignatureSubpacketVector hashedSubpackets = signature.getSignature().getHashedSubPackets(); + if (hashedSubpackets == null) + { + return null; + } + + int keyFlags = signature.getSignature().getHashedSubPackets().getKeyFlags(); + if ((keyFlags & KeyFlags.SIGN_DATA) != KeyFlags.SIGN_DATA) + { + return null; + } + + try + { + PGPSignatureList embeddedSigs = hashedSubpackets.getEmbeddedSignatures(); + if (!embeddedSigs.isEmpty()) + { + OpenPGPComponentSignature backSig = new OpenPGPComponentSignature( + embeddedSigs.get(0), + // Primary Key Binding Signature has issuer and target swapped + /* issuer= */getSignature().getTargetKeyComponent(), + /* target= */getSignature().getIssuer()); + return backSig.getExpirationTime(); + } + return null; + } + catch (PGPException e) + { + return null; + } + } + + /** + * Verify the link signature. + * + * @param contentVerifierBuilderProvider provider for content verifier builders + * @param policy algorithm policy + * @return true if the signature is valid, false otherwise + * @throws PGPSignatureException if an exception occurs during signature verification + */ + public boolean verify(PGPContentVerifierBuilderProvider contentVerifierBuilderProvider, + OpenPGPPolicy policy) + throws PGPSignatureException + { + signature.verify(contentVerifierBuilderProvider, policy); // throws if invalid + return true; + } + + @Override + public String toString() + { + return signature.toString(); + } + + /** + * Factory method for creating Links from component signatures. + * Returns either a {@link Certification} in case the signature is a binding, + * or a {@link Revocation} in case the signature is a revocation signature. + * + * @param signature component signature + * @return link + */ + public static Link create(OpenPGPComponentSignature signature) + { + if (signature.isRevocation()) + { + return new Revocation(signature); + } + else + { + return new Certification(signature); + } + } + + /** + * Return the signature of the link. + * + * @return signature + */ + public OpenPGPComponentSignature getSignature() + { + return signature; + } + } + + /** + * "Positive" signature chain link. + */ + public static class Certification + extends Link + { + /** + * Positive certification. + * + * @param signature signature + */ + public Certification(OpenPGPComponentSignature signature) + { + super(signature); + } + } + + /** + * "Negative" signature chain link. + */ + public static class Revocation + extends Link + { + /** + * Revocation. + * + * @param signature signature + */ + public Revocation(OpenPGPComponentSignature signature) + { + super(signature); + } + + @Override + public Date since() + { + if (signature.signature.isHardRevocation()) + { + // hard revocations are valid retroactively, so we return the beginning of time here + return new Date(0L); + } + return super.since(); + } + + @Override + public Date until() + { + if (signature.signature.isHardRevocation()) + { + // hard revocations do not expire, so they are effective indefinitely + return new Date(Long.MAX_VALUE); + } + return super.until(); + } + } + } + + /** + * Collection of multiple {@link OpenPGPSignatureChain} objects. + */ + public static class OpenPGPSignatureChains + implements Iterable + { + private final OpenPGPCertificateComponent targetComponent; + private final Set chains = new TreeSet<>(); + + public OpenPGPSignatureChains(OpenPGPCertificateComponent component) + { + this.targetComponent = component; + } + + /** + * Add a single chain to the collection. + * + * @param chain chain + */ + public void add(OpenPGPSignatureChain chain) + { + this.chains.add(chain); + } + + /** + * Add all chains to the collection. + * + * @param otherChains other chains + */ + public void addAll(OpenPGPSignatureChains otherChains) + { + this.chains.addAll(otherChains.chains); + } + + /** + * Return true if the collection is empty. + * + * @return true if empty + */ + public boolean isEmpty() + { + return chains.isEmpty(); + } + + /** + * Return a positive certification chain for the component for the given evaluationTime. + * + * @param evaluationTime time for which validity of the {@link OpenPGPCertificateComponent} is checked. + * @return positive certification chain or null + */ + public OpenPGPSignatureChain getCertificationAt(Date evaluationTime) + { + for (Iterator it = chains.iterator(); it.hasNext(); ) + { + OpenPGPSignatureChain chain = it.next(); + boolean isEffective = chain.isEffectiveAt(evaluationTime); + boolean isCertification = chain.isCertification(); + if (isEffective && isCertification) + { + return chain; + } + } + return null; + } + + /** + * Return all {@link OpenPGPSignatureChain} objects, which are valid at the given evaluation time. + * + * @param evaluationTime reference time + * @return valid chains at reference time + */ + public OpenPGPSignatureChains getChainsAt(Date evaluationTime) + { + OpenPGPSignatureChains effectiveChains = new OpenPGPSignatureChains(targetComponent); + for (Iterator it = chains.iterator(); it.hasNext(); ) + { + OpenPGPSignatureChain chain = it.next(); + if (chain.isEffectiveAt(evaluationTime)) + { + effectiveChains.add(chain); + } + } + return effectiveChains; + } + + /** + * Return a negative certification chain for the component for the given evaluationTime. + * + * @param evaluationTime time for which revocation-ness of the {@link OpenPGPCertificateComponent} is checked. + * @return negative certification chain or null + */ + public OpenPGPSignatureChain getRevocationAt(Date evaluationTime) + { + for (Iterator it = chains.iterator(); it.hasNext(); ) + { + OpenPGPSignatureChain chain = it.next(); + if (chain.isRevocation() && chain.isEffectiveAt(evaluationTime)) + { + return chain; + } + } + return null; + } + + @Override + public String toString() + { + StringBuilder b = new StringBuilder(targetComponent.toDetailString()) + .append(" is bound with ").append(chains.size()).append(" chains:").append("\n"); + for (Iterator it = chains.iterator(); it.hasNext(); ) + { + OpenPGPSignatureChain chain = it.next(); + b.append(chain.toString()); + } + return b.toString(); + } + + /** + * Return all {@link OpenPGPSignatureChain} items which originate from the root {@link OpenPGPComponentKey}. + * + * @param root root key + * @return all chains with root key as origin + */ + public OpenPGPSignatureChains fromOrigin(OpenPGPComponentKey root) + { + OpenPGPSignatureChains chainsFromRoot = new OpenPGPSignatureChains(root); + for (Iterator it = chains.iterator(); it.hasNext(); ) + { + OpenPGPSignatureChain chain = it.next(); + if (chain.getRootLinkIssuer() == root) + { + chainsFromRoot.add(chain); + } + } + return chainsFromRoot; + } + + /** + * Return the latest chain, which is valid at the given evaluation time. + * + * @param evaluationDate reference time + * @return latest valid chain + */ + public OpenPGPSignatureChain getChainAt(Date evaluationDate) + { + OpenPGPSignatureChains atDate = getChainsAt(evaluationDate); + Iterator it = atDate.chains.iterator(); + if (it.hasNext()) + { + return it.next(); + } + return null; + } + + @Override + public Iterator iterator() + { + return chains.iterator(); + } + } + + private interface KeyFilter + { + boolean test(OpenPGPComponentKey key, Date evaluationTime); + } + + private List filterKeys(Date evaluationTime, KeyFilter filter) + { + List result = new ArrayList<>(); + for (Iterator it = getKeys().iterator(); it.hasNext(); ) + { + OpenPGPComponentKey key = it.next(); + if (isBound(key, evaluationTime) && filter.test(key, evaluationTime)) + { + result.add(key); + } + } + return result; + } + + private void addSignaturesToChains(List signatures, OpenPGPSignatureChains chains) + { + for (Iterator it = signatures.iterator(); it.hasNext(); ) + { + chains.add(OpenPGPSignatureChain.direct(it.next())); + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDefaultPolicy.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDefaultPolicy.java new file mode 100644 index 0000000000..de580d1c1d --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDefaultPolicy.java @@ -0,0 +1,261 @@ +package org.bouncycastle.openpgp.api; + +import org.bouncycastle.bcpg.HashAlgorithmTags; +import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; +import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.bouncycastle.openpgp.api.util.UTCUtil; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +public class OpenPGPDefaultPolicy + implements OpenPGPPolicy +{ + private final Map documentHashAlgorithmCutoffDates = new HashMap<>(); + private final Map certificateHashAlgorithmCutoffDates = new HashMap<>(); + private final Map symmetricKeyAlgorithmCutoffDates = new HashMap<>(); + private final Map publicKeyMinimalBitStrengths = new HashMap<>(); + private int defaultDocumentSignatureHashAlgorithm = HashAlgorithmTags.SHA512; + private int defaultCertificationSignatureHashAlgorithm = HashAlgorithmTags.SHA512; + private int defaultSymmetricKeyAlgorithm = SymmetricKeyAlgorithmTags.AES_128; + + public OpenPGPDefaultPolicy() + { + /* + * Certification Signature Hash Algorithms + */ + setDefaultCertificationSignatureHashAlgorithm(HashAlgorithmTags.SHA512); + // SHA-3 + acceptCertificationSignatureHashAlgorithm(HashAlgorithmTags.SHA3_512); + acceptCertificationSignatureHashAlgorithm(HashAlgorithmTags.SHA3_256); + // SHA-2 + acceptCertificationSignatureHashAlgorithm(HashAlgorithmTags.SHA512); + acceptCertificationSignatureHashAlgorithm(HashAlgorithmTags.SHA384); + acceptCertificationSignatureHashAlgorithm(HashAlgorithmTags.SHA256); + acceptCertificationSignatureHashAlgorithm(HashAlgorithmTags.SHA224); + // SHA-1 + acceptCertificationSignatureHashAlgorithmUntil(HashAlgorithmTags.SHA1, UTCUtil.parse("2023-02-01 00:00:00 UTC")); + + acceptCertificationSignatureHashAlgorithmUntil(HashAlgorithmTags.RIPEMD160, UTCUtil.parse("2023-02-01 00:00:00 UTC")); + acceptCertificationSignatureHashAlgorithmUntil(HashAlgorithmTags.MD5, UTCUtil.parse("1997-02-01 00:00:00 UTC")); + + /* + * Document Signature Hash Algorithms + */ + setDefaultDocumentSignatureHashAlgorithm(HashAlgorithmTags.SHA512); + // SHA-3 + acceptDocumentSignatureHashAlgorithm(HashAlgorithmTags.SHA3_512); + acceptDocumentSignatureHashAlgorithm(HashAlgorithmTags.SHA3_256); + // SHA-2 + acceptDocumentSignatureHashAlgorithm(HashAlgorithmTags.SHA512); + acceptDocumentSignatureHashAlgorithm(HashAlgorithmTags.SHA384); + acceptDocumentSignatureHashAlgorithm(HashAlgorithmTags.SHA256); + acceptDocumentSignatureHashAlgorithm(HashAlgorithmTags.SHA224); + + /* + * Symmetric Key Algorithms + */ + setDefaultSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.AES_128); + acceptSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.AES_256); + acceptSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.AES_192); + acceptSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.AES_128); + acceptSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.TWOFISH); + acceptSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.CAMELLIA_256); + acceptSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.CAMELLIA_192); + acceptSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.CAMELLIA_128); + + /* + * Public Key Algorithms and key strengths + */ + acceptPublicKeyAlgorithmWithMinimalStrength(PublicKeyAlgorithmTags.RSA_GENERAL, 2000); + acceptPublicKeyAlgorithmWithMinimalStrength(PublicKeyAlgorithmTags.RSA_ENCRYPT, 2000); + acceptPublicKeyAlgorithmWithMinimalStrength(PublicKeyAlgorithmTags.RSA_SIGN, 2000); + + acceptPublicKeyAlgorithmWithMinimalStrength(PublicKeyAlgorithmTags.ECDSA, 250); + acceptPublicKeyAlgorithmWithMinimalStrength(PublicKeyAlgorithmTags.EDDSA_LEGACY, 250); + acceptPublicKeyAlgorithmWithMinimalStrength(PublicKeyAlgorithmTags.ECDH, 250); + + acceptPublicKeyAlgorithm(PublicKeyAlgorithmTags.X25519); + acceptPublicKeyAlgorithm(PublicKeyAlgorithmTags.X448); + acceptPublicKeyAlgorithm(PublicKeyAlgorithmTags.Ed25519); + acceptPublicKeyAlgorithm(PublicKeyAlgorithmTags.Ed448); + } + + public OpenPGPDefaultPolicy rejectHashAlgorithm(int hashAlgorithmId) + { + certificateHashAlgorithmCutoffDates.remove(hashAlgorithmId); + documentHashAlgorithmCutoffDates.remove(hashAlgorithmId); + return this; + } + + public OpenPGPDefaultPolicy acceptCertificationSignatureHashAlgorithm(int hashAlgorithmId) + { + return acceptCertificationSignatureHashAlgorithmUntil(hashAlgorithmId, null); + } + + public OpenPGPDefaultPolicy acceptCertificationSignatureHashAlgorithmUntil(int hashAlgorithmId, Date until) + { + certificateHashAlgorithmCutoffDates.put(hashAlgorithmId, until); + return this; + } + + public OpenPGPDefaultPolicy acceptDocumentSignatureHashAlgorithm(int hashAlgorithmId) + { + return acceptDocumentSignatureHashAlgorithmUntil(hashAlgorithmId, null); + } + + public OpenPGPDefaultPolicy acceptDocumentSignatureHashAlgorithmUntil(int hashAlgorithmId, Date until) + { + documentHashAlgorithmCutoffDates.put(hashAlgorithmId, until); + return this; + } + + public OpenPGPDefaultPolicy rejectSymmetricKeyAlgorithm(int symmetricKeyAlgorithmId) + { + symmetricKeyAlgorithmCutoffDates.remove(symmetricKeyAlgorithmId); + return this; + } + + public OpenPGPDefaultPolicy acceptSymmetricKeyAlgorithm(int symmetricKeyAlgorithmId) + { + return acceptSymmetricKeyAlgorithmUntil(symmetricKeyAlgorithmId, null); + } + + public OpenPGPDefaultPolicy acceptSymmetricKeyAlgorithmUntil(int symmetricKeyAlgorithmId, Date until) + { + symmetricKeyAlgorithmCutoffDates.put(symmetricKeyAlgorithmId, until); + return this; + } + + public OpenPGPDefaultPolicy rejectPublicKeyAlgorithm(int publicKeyAlgorithmId) + { + publicKeyMinimalBitStrengths.remove(publicKeyAlgorithmId); + return this; + } + + public OpenPGPDefaultPolicy acceptPublicKeyAlgorithm(int publicKeyAlgorithmId) + { + publicKeyMinimalBitStrengths.put(publicKeyAlgorithmId, null); + return this; + } + + public OpenPGPDefaultPolicy acceptPublicKeyAlgorithmWithMinimalStrength(int publicKeyAlgorithmId, int minBitStrength) + { + publicKeyMinimalBitStrengths.put(publicKeyAlgorithmId, minBitStrength); + return this; + } + + @Override + public boolean isAcceptableDocumentSignatureHashAlgorithm(int hashAlgorithmId, Date signatureCreationTime) + { + return isAcceptable(hashAlgorithmId, signatureCreationTime, documentHashAlgorithmCutoffDates); + } + + @Override + public boolean isAcceptableRevocationSignatureHashAlgorithm(int hashAlgorithmId, Date signatureCreationTime) + { + return isAcceptable(hashAlgorithmId, signatureCreationTime, certificateHashAlgorithmCutoffDates); + } + + @Override + public boolean isAcceptableCertificationSignatureHashAlgorithm(int hashAlgorithmId, Date signatureCreationTime) + { + return isAcceptable(hashAlgorithmId, signatureCreationTime, certificateHashAlgorithmCutoffDates); + } + + @Override + public int getDefaultCertificationSignatureHashAlgorithm() + { + return defaultCertificationSignatureHashAlgorithm; + } + + public OpenPGPDefaultPolicy setDefaultCertificationSignatureHashAlgorithm(int hashAlgorithmId) + { + defaultCertificationSignatureHashAlgorithm = hashAlgorithmId; + return this; + } + + @Override + public int getDefaultDocumentSignatureHashAlgorithm() + { + return defaultDocumentSignatureHashAlgorithm; + } + + public OpenPGPDefaultPolicy setDefaultDocumentSignatureHashAlgorithm(int hashAlgorithmId) + { + defaultDocumentSignatureHashAlgorithm = hashAlgorithmId; + return this; + } + + @Override + public boolean isAcceptableSymmetricKeyAlgorithm(int symmetricKeyAlgorithmId) + { + return isAcceptable(symmetricKeyAlgorithmId, symmetricKeyAlgorithmCutoffDates); + } + + @Override + public int getDefaultSymmetricKeyAlgorithm() + { + return defaultSymmetricKeyAlgorithm; + } + + public OpenPGPDefaultPolicy setDefaultSymmetricKeyAlgorithm(int symmetricKeyAlgorithmId) + { + defaultSymmetricKeyAlgorithm = symmetricKeyAlgorithmId; + return this; + } + + @Override + public boolean isAcceptablePublicKeyStrength(int publicKeyAlgorithmId, int bitStrength) + { + return isAcceptable(publicKeyAlgorithmId, bitStrength, publicKeyMinimalBitStrengths); + } + + @Override + public OpenPGPNotationRegistry getNotationRegistry() + { + return null; + } + + private boolean isAcceptable(int algorithmId, Date usageDate, Map cutoffTable) + { + if (!cutoffTable.containsKey(algorithmId)) + { + // algorithm is not listed in the map at all + return false; + } + + Date cutoffDate = cutoffTable.get(algorithmId); + if (cutoffDate == null) + { + // no cutoff date given -> algorithm is acceptable indefinitely + return true; + } + + return usageDate.before(cutoffDate); + } + + private boolean isAcceptable(int algorithmId, Map cutoffTable) + { + return cutoffTable.containsKey(algorithmId); + } + + private boolean isAcceptable(int algorithmId, int bitStrength, Map minBitStrengths) + { + if (!minBitStrengths.containsKey(algorithmId)) + { + // algorithm is not listed in the map at all + return false; + } + + Integer minBitStrength = minBitStrengths.get(algorithmId); + if (minBitStrength == null) + { + // no minimal bit strength defined -> accept all strengths + return true; + } + + return bitStrength >= minBitStrength; + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureGenerator.java new file mode 100644 index 0000000000..ad17c471f6 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureGenerator.java @@ -0,0 +1,96 @@ +package org.bouncycastle.openpgp.api; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureGenerator; + +/** + * High-Level OpenPGP Signature Generator for Detached Signatures. + * Detached signatures can be stored and distributed as a distinct object alongside the signed data. + * They are used for example to sign Release files of some Linux software distributions. + *

    + * To use this class, instantiate it, optionally providing a concrete {@link OpenPGPImplementation} and + * {@link OpenPGPPolicy} for algorithm policing. + * Then, add the desired {@link OpenPGPKey} you want to use for signing the data via one or more + * calls to {@link #addSigningKey(OpenPGPKey, KeyPassphraseProvider)}. + * You have fine-grained control over the signature by using the method + * {@link #addSigningKey(OpenPGPKey.OpenPGPSecretKey, char[], SignatureParameters.Callback)}. + * Lastly, retrieve a list of detached {@link OpenPGPSignature.OpenPGPDocumentSignature signatures} by calling + * {@link #sign(InputStream)}, passing in an {@link InputStream} containing the data you want to sign. + */ +public class OpenPGPDetachedSignatureGenerator + extends AbstractOpenPGPDocumentSignatureGenerator +{ + /** + * Instantiate a signature generator using the default {@link OpenPGPImplementation} and its {@link OpenPGPPolicy}. + */ + public OpenPGPDetachedSignatureGenerator() + { + this(OpenPGPImplementation.getInstance()); + } + + /** + * Instantiate a signature generator using the passed in {@link OpenPGPImplementation} and its {@link OpenPGPPolicy}. + * + * @param implementation custom OpenPGP implementation + */ + public OpenPGPDetachedSignatureGenerator(OpenPGPImplementation implementation) + { + this(implementation, implementation.policy()); + } + + /** + * Instantiate a signature generator using a custom {@link OpenPGPImplementation} and custom {@link OpenPGPPolicy}. + * + * @param implementation custom OpenPGP implementation + * @param policy custom OpenPGP policy + */ + public OpenPGPDetachedSignatureGenerator(OpenPGPImplementation implementation, OpenPGPPolicy policy) + { + super(implementation, policy); + } + + /** + * Pass in an {@link InputStream} containing the data that shall be signed and return a list of detached + * signatures. + * + * @param inputStream data to be signed + * @return detached signatures + * + * @throws IOException if something goes wrong processing the data + * @throws PGPException if signing fails + */ + public List sign(InputStream inputStream) + throws IOException, PGPException + { + addSignToGenerator(); + + byte[] buf = new byte[2048]; + int r; + while ((r = inputStream.read(buf)) != -1) + { + for (Iterator it = signatureGenerators.iterator(); it.hasNext();) + { + ((PGPSignatureGenerator) it.next()).update(buf, 0, r); + } + } + + List documentSignatures = new ArrayList<>(); + for (int i = 0; i < signatureGenerators.size(); i++) + { + PGPSignatureGenerator sigGen = signatureGenerators.get(i); + PGPSignature signature = sigGen.generate(); + OpenPGPSignature.OpenPGPDocumentSignature docSig = new OpenPGPSignature.OpenPGPDocumentSignature( + signature, signingKeys.get(i)); + documentSignatures.add(docSig); + } + + return documentSignatures; + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureProcessor.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureProcessor.java new file mode 100644 index 0000000000..3ef8aca52d --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureProcessor.java @@ -0,0 +1,298 @@ +package org.bouncycastle.openpgp.api; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.bouncycastle.bcpg.BCPGInputStream; +import org.bouncycastle.bcpg.KeyIdentifier; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPObjectFactory; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureException; +import org.bouncycastle.openpgp.PGPSignatureList; +import org.bouncycastle.openpgp.PGPUtil; + +/** + * High-Level Processor for Messages Signed Using Detached OpenPGP Signatures. + *

    + * To use this class, first instantiate the processor, optionally passing in a concrete + * {@link OpenPGPImplementation} and {@link OpenPGPPolicy}. + * Then, pass in any detached signatures you want to verify using {@link #addSignatures(InputStream)}. + * Next, provide the expected issuers {@link OpenPGPCertificate OpenPGPCertificates} for signature + * verification using {@link #addVerificationCertificate(OpenPGPCertificate)}. + * Signatures for which no certificate was provided, and certificates for which no signature was added, + * are ignored. + * Optionally, you can specify a validity date range for the signatures using + * {@link #verifyNotBefore(Date)} and {@link #verifyNotAfter(Date)}. + * Signatures outside this range will be ignored as invalid. + * Lastly, provide an {@link InputStream} containing the original plaintext data, over which you want to + * verify the detached signatures using {@link #process(InputStream)}. + * As a result you will receive a list containing all processed + * {@link OpenPGPSignature.OpenPGPDocumentSignature OpenPGPDocumentSignatures}. + * For these, you can check validity by calling {@link OpenPGPSignature.OpenPGPDocumentSignature#isValid()}. + */ +public class OpenPGPDetachedSignatureProcessor +{ + private final OpenPGPImplementation implementation; + private final OpenPGPPolicy policy; + private final OpenPGPKeyMaterialPool.OpenPGPCertificatePool certificatePool = new OpenPGPKeyMaterialPool.OpenPGPCertificatePool(); + private final List pgpSignatures = new ArrayList<>(); + private Date verifyNotAfter = new Date(); // now + private Date verifyNotBefore = new Date(0L); // beginning of time + + private OpenPGPMessageProcessor.PGPExceptionCallback exceptionCallback = null; + + /** + * Instantiate a signature processor using the default {@link OpenPGPImplementation} and its {@link OpenPGPPolicy}. + */ + public OpenPGPDetachedSignatureProcessor() + { + this(OpenPGPImplementation.getInstance()); + } + + /** + * Instantiate a signature processor using a custom {@link OpenPGPImplementation} and its {@link OpenPGPPolicy}. + * + * @param implementation custom OpenPGP implementation + */ + public OpenPGPDetachedSignatureProcessor(OpenPGPImplementation implementation) + { + this(implementation, implementation.policy()); + } + + /** + * Instantiate a signature processor using a custom {@link OpenPGPImplementation} and custom {@link OpenPGPPolicy}. + * + * @param implementation custom OpenPGP implementation + * @param policy custom OpenPGP policy + */ + public OpenPGPDetachedSignatureProcessor(OpenPGPImplementation implementation, OpenPGPPolicy policy) + { + this.implementation = implementation; + this.policy = policy; + } + + /** + * Read one or more {@link PGPSignature detached signatures} from the provided {@link InputStream} and + * add them to the processor. + * + * @param inputStream input stream of armored or unarmored detached OpenPGP signatures + * @return this + * @throws IOException if something goes wrong reading from the stream + */ + public OpenPGPDetachedSignatureProcessor addSignatures(InputStream inputStream) + throws IOException + { + InputStream decoderStream = PGPUtil.getDecoderStream(inputStream); + BCPGInputStream pIn = BCPGInputStream.wrap(decoderStream); + PGPObjectFactory objFac = implementation.pgpObjectFactory(pIn); + Object next; + while ((next = objFac.nextObject()) != null) + { + if (next instanceof PGPSignatureList) + { + addSignatures((PGPSignatureList)next); + } + else if (next instanceof PGPSignature) + { + addSignature((PGPSignature)next); + } + } + return this; + } + + /** + * Add one or more {@link PGPSignature detached signatures} from the given {@link PGPSignatureList} to the + * processor. + * + * @param signatures detached signature list + * @return this + */ + public OpenPGPDetachedSignatureProcessor addSignatures(PGPSignatureList signatures) + { + for (Iterator it = signatures.iterator(); it.hasNext(); ) + { + addSignature(it.next()); + } + return this; + } + + /** + * Add a single {@link PGPSignature detached signature} to the processor. + * + * @param signature detached signature + * @return this + */ + public OpenPGPDetachedSignatureProcessor addSignature(PGPSignature signature) + { + pgpSignatures.add(signature); + return this; + } + + /** + * Add an issuers {@link OpenPGPCertificate} for signature verification. + * + * @param certificate OpenPGP certificate + * @return this + */ + public OpenPGPDetachedSignatureProcessor addVerificationCertificate(OpenPGPCertificate certificate) + { + this.certificatePool.addItem(certificate); + return this; + } + + /** + * Reject detached signatures made before

    date
    . + * By default, this value is set to the beginning of time. + * + * @param date date + * @return this + */ + public OpenPGPDetachedSignatureProcessor verifyNotBefore(Date date) + { + this.verifyNotBefore = date; + return this; + } + + /** + * Reject detached signatures made after the given
    date
    . + * By default, this value is set to the current time at instantiation time, in order to prevent + * verification of signatures from the future. + * + * @param date date + * @return this + */ + public OpenPGPDetachedSignatureProcessor verifyNotAfter(Date date) + { + this.verifyNotAfter = date; + return this; + } + + /** + * Process the plaintext data from the given {@link InputStream} and return a list of processed + * detached signatures. + * Note: This list will NOT contain any malformed signatures, or signatures for which no verification key was found. + * Correctness of these signatures can be checked via {@link OpenPGPSignature.OpenPGPDocumentSignature#isValid()}. + * + * @param inputStream data over which the detached signatures are calculated + * @return list of processed detached signatures + * @throws IOException if the data cannot be processed + */ + public List process(InputStream inputStream) + throws IOException + { + List documentSignatures = new ArrayList<>(); + for (Iterator it = pgpSignatures.iterator(); it.hasNext(); ) + { + PGPSignature signature = (PGPSignature)it.next(); + // Match up signatures with certificates + + KeyIdentifier identifier = OpenPGPSignature.getMostExpressiveIdentifier(signature.getKeyIdentifiers()); + if (identifier == null) + { + // Missing issuer -> ignore sig + continue; + } + + OpenPGPCertificate certificate = certificatePool.provide(identifier); + if (certificate == null) + { + // missing cert -> ignore sig + continue; + } + + OpenPGPCertificate.OpenPGPComponentKey signingKey = certificate.getKey(identifier); + if (signingKey == null) + { + // unbound signing subkey -> ignore sig + continue; + } + + // Initialize signatures with verification key + try + { + signature.init(implementation.pgpContentVerifierBuilderProvider(), signingKey.getPGPPublicKey()); + } + catch (PGPException e) + { + if (exceptionCallback != null) + { + exceptionCallback.onException(e); + } + continue; + } + + OpenPGPSignature.OpenPGPDocumentSignature sig = + new OpenPGPSignature.OpenPGPDocumentSignature(signature, signingKey); + try + { + // sanitize signature (required subpackets, check algorithm policy...) + sig.sanitize(signingKey, policy); + } + catch (PGPSignatureException e) + { + if (exceptionCallback != null) + { + exceptionCallback.onException(e); + } + continue; + } + + // check allowed date range + if (!sig.createdInBounds(verifyNotBefore, verifyNotAfter)) + { + continue; + } + + // sig qualifies for further processing :) + documentSignatures.add(sig); + } + + // Process plaintext + byte[] buf = new byte[2048]; + int r; + while ((r = inputStream.read(buf)) != -1) + { + for (Iterator it = documentSignatures.iterator(); it.hasNext(); ) + { + ((OpenPGPSignature.OpenPGPDocumentSignature)it.next()).getSignature().update(buf, 0, r); + } + } + + // Verify signatures + for (Iterator it = documentSignatures.iterator(); it.hasNext(); ) + { + try + { + // verify the signature. Correctness can be checked via + ((OpenPGPSignature.OpenPGPDocumentSignature)it.next()).verify(); + } + catch (PGPException e) + { + if (exceptionCallback != null) + { + exceptionCallback.onException(e); + } + } + } + + return documentSignatures; + } + + /** + * Add a callback to which any OpenPGP-related exceptions are forwarded. + * Useful for debugging purposes. + * + * @param callback callback + * @return this + */ + public OpenPGPDetachedSignatureProcessor setExceptionCallback(OpenPGPMessageProcessor.PGPExceptionCallback callback) + { + this.exceptionCallback = callback; + return this; + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPEncryptionNegotiator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPEncryptionNegotiator.java new file mode 100644 index 0000000000..3b9286c31c --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPEncryptionNegotiator.java @@ -0,0 +1,305 @@ +package org.bouncycastle.openpgp.api; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.bouncycastle.bcpg.sig.Features; +import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites; +import org.bouncycastle.bcpg.sig.PreferredAlgorithms; + +public abstract class OpenPGPEncryptionNegotiator +{ + /** + * Negotiate encryption mode and algorithms. + * + * @param configuration message generator configuration + * @return negotiated encryption mode and algorithms + */ + public abstract MessageEncryptionMechanism negotiateEncryption(OpenPGPMessageGenerator configuration); + + static PreferredAEADCiphersuites negotiateAEADCiphersuite(List certificates, OpenPGPPolicy policy) + { + return new PreferredAEADCiphersuites(false, new PreferredAEADCiphersuites.Combination[]{ + bestAEADCiphersuiteByWeight(certificates, policy) + }); + } + + /** + * Return true, if all recipient {@link OpenPGPCertificate certificates} contain at least one subkey that supports + * {@link Features#FEATURE_SEIPD_V2}. + * + * @param certificates certificates + * @return true if all certificates support the feature, false otherwise + */ + static boolean allRecipientsSupportSeipd2(List certificates) + { + return allRecipientsSupportEncryptionFeature(certificates, Features.FEATURE_SEIPD_V2); + } + + static boolean allRecipientsSupportLibrePGPOED(List certificates) + { + return allRecipientsSupportEncryptionFeature(certificates, Features.FEATURE_AEAD_ENCRYPTED_DATA); + } + + static boolean allRecipientsSupportEncryptionFeature(List certificates, byte feature) + { + for (Iterator it = certificates.iterator(); it.hasNext(); ) + { + List encryptionKeys = ((OpenPGPCertificate)it.next()).getEncryptionKeys(); + if (encryptionKeys.isEmpty()) + { + continue; + } + + boolean recipientHasSupport = false; + for (Iterator ckIt = encryptionKeys.iterator(); ckIt.hasNext(); ) + { + Features features = ((OpenPGPCertificate.OpenPGPComponentKey)ckIt.next()).getFeatures(); + if (features != null && features.supportsFeature(feature)) + { + recipientHasSupport = true; + break; + } + } + + if (!recipientHasSupport) + { + return false; + } + } + return true; + } + + public static PreferredAEADCiphersuites.Combination bestAEADCiphersuiteByWeight( + Collection certificates, OpenPGPPolicy policy) + { + return processCertificates( + certificates, + policy, + new KeyProcessor() + { + public boolean processKey(OpenPGPCertificate.OpenPGPComponentKey key, Map capableKeys) + { + Features features = key.getFeatures(); + if (features != null && features.supportsSEIPDv2()) + { + PreferredAEADCiphersuites prefs = key.getAEADCipherSuitePreferences(); + if (prefs != null) + { + capableKeys.put(key, prefs); + return true; + } + } + return false; + } + + public List getAlgorithms(PreferredAEADCiphersuites prefs, OpenPGPPolicy policy) + { + // Weigh the preferences descending by index: w(p_i) = 1/(i+1) + // This way, combinations with a smaller index have a higher weight than combinations with larger index. + // Additionally, we divide this weight by the number of capable subkeys per cert in order to + // prevent a certificate with many capable subkeys from outvoting other certificates + List result = new ArrayList<>(); + for (PreferredAEADCiphersuites.Combination c : prefs.getAlgorithms()) + { + if (c.getSymmetricAlgorithm() != SymmetricKeyAlgorithmTags.NULL + && policy.isAcceptableSymmetricKeyAlgorithm(c.getSymmetricAlgorithm())) + { + result.add(c); + } + } + return result; + } + }, + PreferredAEADCiphersuites.DEFAULT().getAlgorithms()[0] + ); + } + + static int bestSymmetricKeyAlgorithmByWeight( + Collection certificates, + OpenPGPPolicy policy) + { + return processCertificates( + certificates, + policy, + new KeyProcessor() + { + @Override + public boolean processKey(OpenPGPCertificate.OpenPGPComponentKey key, + Map capableKeys) + { + Features features = key.getFeatures(); + if (features != null && features.supportsModificationDetection()) + { + PreferredAlgorithms prefs = key.getSymmetricCipherPreferences(); + if (prefs != null) + { + capableKeys.put(key, prefs); + return true; + } + } + return false; + } + + @Override + public List getAlgorithms(PreferredAlgorithms preferences, OpenPGPPolicy policy) + { + // Weigh the preferences descending by index: w(p_i) = 1/(i+1) + // This way, combinations with a smaller index have a higher weight than combinations with larger index. + // Additionally, we divide this weight by the number of capable subkeys per cert in order to + // prevent a certificate with many capable subkeys from outvoting other certificates + List result = new ArrayList<>(); + int[] prefs = preferences.getPreferences(); + for (int i = 0; i < prefs.length; i++) + { + int alg = prefs[i]; + if (alg != SymmetricKeyAlgorithmTags.NULL && + policy.isAcceptableSymmetricKeyAlgorithm(alg)) + { + result.add(alg); + } + } + return result; + } + }, + SymmetricKeyAlgorithmTags.AES_128 // Default value + ); + } + + static int bestOEDEncryptionModeByWeight(Collection certificates, + final OpenPGPPolicy policy) + { + return processCertificates( + certificates, + policy, + new KeyProcessor() + { + @Override + public boolean processKey(OpenPGPCertificate.OpenPGPComponentKey key, + Map capableKeys) + { + // Only consider encryption keys capable of OED + Features features = key.getFeatures(); + if (features != null && features.supportsFeature(Features.FEATURE_AEAD_ENCRYPTED_DATA)) + { + PreferredAlgorithms prefs = key.getSymmetricCipherPreferences(); + if (prefs != null) + { + capableKeys.put(key, prefs); + return true; + } + } + return false; + } + + @Override + public List getAlgorithms(PreferredAlgorithms preferences, OpenPGPPolicy policy) + { + // Count the keys symmetric key preferences (that can be used with OED) and update the weight map + + List result = new ArrayList<>(); + int[] prefs = preferences.getPreferences(); + for (int i = 0; i < prefs.length; i++) + { + int alg = prefs[i]; + if (isOEDCompatible(alg) && + policy.isAcceptableSymmetricKeyAlgorithm(alg)) + { + result.add(alg); + } + } + return result; + } + + private boolean isOEDCompatible(int alg) + { + switch (alg) + { + case SymmetricKeyAlgorithmTags.AES_128: + case SymmetricKeyAlgorithmTags.AES_192: + case SymmetricKeyAlgorithmTags.AES_256: + case SymmetricKeyAlgorithmTags.CAMELLIA_128: + case SymmetricKeyAlgorithmTags.CAMELLIA_192: + case SymmetricKeyAlgorithmTags.CAMELLIA_256: + return true; + default: + return false; + } + } + }, + SymmetricKeyAlgorithmTags.AES_128 // Default value + ); + } + + private interface KeyProcessor + { + /** + * Process a certificate's encryption key and return true to include it + */ + boolean processKey(OpenPGPCertificate.OpenPGPComponentKey key, Map capableKeys); + + /** + * Process preferences and return algorithms to consider + */ + List getAlgorithms(T preferences, OpenPGPPolicy policy); + } + + private static R processCertificates( + Collection certificates, + OpenPGPPolicy policy, + KeyProcessor keyProcessor, + R defaultResult) + { + Map weights = new HashMap<>(); + + // Go through all certificate's capable subkeys + for (Iterator it = certificates.iterator(); it.hasNext(); ) + { + List encryptionKeys = it.next().getEncryptionKeys(); + if (encryptionKeys.isEmpty()) + { + continue; + } + + // Only consider encryption keys capable of SEIPDv1/OED + Map capableKeys = new HashMap<>(); + for (Iterator ckIt = encryptionKeys.iterator(); ckIt.hasNext(); ) + { + keyProcessor.processKey(ckIt.next(), capableKeys); + } + + // Count the keys [AEAD preferences | symmetric key preferences (that can be used with OED)] + // and update the weight map + for (Iterator ckIt = capableKeys.keySet().iterator(); ckIt.hasNext(); ) + { + T prefs = capableKeys.get(ckIt.next()); + List algorithms = keyProcessor.getAlgorithms(prefs, policy); + for (int i = 0; i < algorithms.size(); i++) + { + R c = algorithms.get(i); + float current = weights.containsKey(c) ? weights.get(c) : 0; + weights.put(c, current + (1f / (i + 1)) / capableKeys.size()); + } + } + } + + R maxKey = defaultResult; + float maxWeight = -1; + // Select the entry with the highest weight + for (Iterator> it = weights.entrySet().iterator(); it.hasNext(); ) + { + Map.Entry entry = it.next(); + if (entry.getValue() > maxWeight) + { + maxWeight = entry.getValue(); + maxKey = entry.getKey(); + } + } + return maxKey; + } +} \ No newline at end of file diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPImplementation.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPImplementation.java new file mode 100644 index 0000000000..1697e54879 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPImplementation.java @@ -0,0 +1,210 @@ +package org.bouncycastle.openpgp.api; + +import java.io.InputStream; + +import org.bouncycastle.bcpg.S2K; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPObjectFactory; +import org.bouncycastle.openpgp.PGPPrivateKey; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSessionKey; +import org.bouncycastle.openpgp.api.bc.BcOpenPGPImplementation; +import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.PBEKeyEncryptionMethodGenerator; +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptorBuilderProvider; +import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptorFactory; +import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder; +import org.bouncycastle.openpgp.operator.PGPContentSignerBuilderProvider; +import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider; +import org.bouncycastle.openpgp.operator.PGPDataEncryptorBuilder; +import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider; +import org.bouncycastle.openpgp.operator.PGPKeyPairGeneratorProvider; +import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.PublicKeyKeyEncryptionMethodGenerator; +import org.bouncycastle.openpgp.operator.SessionKeyDataDecryptorFactory; + +/** + * Bouncy Castle provides two implementations of OpenPGP operators. + * The
    JCA/JCE
    implementation makes use of Java Cryptography Architecture and the + * Java Cryptography Extension, while
    Bc
    uses Bouncy Castles Lightweight Cryptography API. + * The purpose of {@link OpenPGPImplementation} is to define a shared interface for instantiating concrete + * objects of either API. + * It is advised to define the desired implementation by calling {@link #setInstance(OpenPGPImplementation)} and + * acquiring it via {@link #getInstance()}, as swapping out the entire implementation can then be done by + * replacing the instance in one single place. + * This pattern was successfully explored by PGPainless. + */ +public abstract class OpenPGPImplementation +{ + private static OpenPGPImplementation INSTANCE; + private OpenPGPPolicy policy = new OpenPGPDefaultPolicy(); + + /** + * Replace the {@link OpenPGPImplementation} instance that is returned by {@link #getInstance()}. + * @param implementation instance + */ + public static void setInstance(OpenPGPImplementation implementation) + { + INSTANCE = implementation; + } + + /** + * Return the currently set {@link OpenPGPImplementation} instance. + * The default is {@link BcOpenPGPImplementation}. + * + * @return instance + */ + public static OpenPGPImplementation getInstance() + { + if (INSTANCE == null) + { + setInstance(new BcOpenPGPImplementation()); + } + return INSTANCE; + } + + public OpenPGPPolicy policy() + { + return policy; + } + + public OpenPGPImplementation setPolicy(OpenPGPPolicy policy) + { + this.policy = policy; + return this; + } + + /** + * Return an instance of {@link PGPObjectFactory} based on the given {@link InputStream}. + * + * @param packetInputStream packet input stream + * @return object factory + */ + public abstract PGPObjectFactory pgpObjectFactory(InputStream packetInputStream); + + /** + * Return an instance of {@link PGPContentVerifierBuilderProvider} which is responsible for providing + * implementations needed for signature verification. + * + * @return content verifier builder provider + */ + public abstract PGPContentVerifierBuilderProvider pgpContentVerifierBuilderProvider(); + + /** + * Return an instance of {@link PBESecretKeyDecryptorBuilderProvider} which is responsible for providing + * implementations needed for secret key unlocking. + * + * @return secret key decryptor builder provider + */ + public abstract PBESecretKeyDecryptorBuilderProvider pbeSecretKeyDecryptorBuilderProvider(); + + /** + * Return an instance of {@link PGPDataEncryptorBuilder} which is responsible for providing implementations + * needed for creating encrypted data packets. + * + * @param symmetricKeyAlgorithm symmetric encryption algorithm + * @return data encryptor builder + */ + public abstract PGPDataEncryptorBuilder pgpDataEncryptorBuilder( + int symmetricKeyAlgorithm); + + /** + * Return an instance of {@link PublicKeyKeyEncryptionMethodGenerator} which is responsible for + * creating public-key-based encryptors for OpenPGP messages. + * Public-key-based encryptors are used when a message is encrypted for a recipients public key. + * + * @param encryptionSubkey subkey for which a message shall be encrypted + * @return public-key key-encryption method generator + */ + public abstract PublicKeyKeyEncryptionMethodGenerator publicKeyKeyEncryptionMethodGenerator( + PGPPublicKey encryptionSubkey); + + /** + * Return an instance of {@link PBEKeyEncryptionMethodGenerator} which is responsible for creating + * symmetric-key-based encryptors for OpenPGP messages, using {@link S2K#SALTED_AND_ITERATED} mode. + * Symmetric-key-based encryptors are used when a message is encrypted using a passphrase. + * + * @param messagePassphrase passphrase to encrypt the message with + * @return pbe key encryption method generator + */ + public abstract PBEKeyEncryptionMethodGenerator pbeKeyEncryptionMethodGenerator( + char[] messagePassphrase); + + /** + * Return an instance of {@link PBEKeyEncryptionMethodGenerator} which is responsible for creating + * symmetric-key-based encryptors for OpenPGP messages, using {@link S2K#ARGON_2} mode. + * Symmetric-key-based encryptors are used when a message is encrypted using a passphrase. + * + * @param messagePassphrase passphrase to encrypt the message with + * @param argon2Params parameters for the Argon2 hash function + * @return pbe key encryption method generator + */ + public abstract PBEKeyEncryptionMethodGenerator pbeKeyEncryptionMethodGenerator( + char[] messagePassphrase, + S2K.Argon2Params argon2Params); + + /** + * Return an instance of {@link PGPContentSignerBuilder}, which is responsible for providing concrete + * implementations needed for signature creation. + * + * @param publicKeyAlgorithm the signing-keys public-key algorithm + * @param hashAlgorithm signature hash algorithm + * @return content signer builder + */ + public abstract PGPContentSignerBuilder pgpContentSignerBuilder( + int publicKeyAlgorithm, + int hashAlgorithm); + + /** + * Return an instance of the {@link PBEDataDecryptorFactory}, which is responsible for providing concrete + * implementations needed to decrypt OpenPGP messages that were encrypted symmetrically with a passphrase. + * + * @param messagePassphrase message passphrase + * @return pbe data decryptor factory + * @throws PGPException if the factory cannot be instantiated + */ + public abstract PBEDataDecryptorFactory pbeDataDecryptorFactory( + char[] messagePassphrase) + throws PGPException; + + /** + * Return an instance of the {@link SessionKeyDataDecryptorFactory}, which is responsible for providing + * concrete implementations needed to decrypt OpenPGP messages using a {@link PGPSessionKey}. + * + * @param sessionKey session key + * @return session-key data decryptor factory + */ + public abstract SessionKeyDataDecryptorFactory sessionKeyDataDecryptorFactory( + PGPSessionKey sessionKey); + + /** + * Return an instance of the {@link PublicKeyDataDecryptorFactory}, which is responsible for providing + * concrete implementations needed to decrypt OpenPGP messages using a {@link PGPPrivateKey}. + * + * @param decryptionKey private decryption key + * @return public-key data decryptor factory + */ + public abstract PublicKeyDataDecryptorFactory publicKeyDataDecryptorFactory( + PGPPrivateKey decryptionKey); + + /** + * Return an instance of the {@link PGPDigestCalculatorProvider}, which is responsible for providing + * concrete {@link org.bouncycastle.openpgp.operator.PGPDigestCalculator} implementations. + * + * @return pgp digest calculator provider + * @throws PGPException if the provider cannot be instantiated + */ + public abstract PGPDigestCalculatorProvider pgpDigestCalculatorProvider() + throws PGPException; + + public abstract PGPKeyPairGeneratorProvider pgpKeyPairGeneratorProvider(); + + public abstract PGPContentSignerBuilderProvider pgpContentSignerBuilderProvider(int hashAlgorithmId); + + public abstract KeyFingerPrintCalculator keyFingerPrintCalculator(); + + public abstract PBESecretKeyEncryptorFactory pbeSecretKeyEncryptorFactory(boolean aead) throws PGPException; + + public abstract PBESecretKeyEncryptorFactory pbeSecretKeyEncryptorFactory(boolean aead, int symmetricKeyAlgorithm, int iterationCount) throws PGPException; +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKey.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKey.java new file mode 100644 index 0000000000..d8b3b9ff2f --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKey.java @@ -0,0 +1,583 @@ +package org.bouncycastle.openpgp.api; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.bouncycastle.bcpg.BCPGOutputStream; +import org.bouncycastle.bcpg.HashAlgorithmTags; +import org.bouncycastle.bcpg.KeyIdentifier; +import org.bouncycastle.bcpg.PacketFormat; +import org.bouncycastle.bcpg.PublicKeyPacket; +import org.bouncycastle.bcpg.S2K; +import org.bouncycastle.bcpg.SecretKeyPacket; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyPair; +import org.bouncycastle.openpgp.PGPKeyValidationException; +import org.bouncycastle.openpgp.PGPPrivateKey; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.api.exception.KeyPassphraseException; +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptorBuilderProvider; +import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptorFactory; + +/** + * An {@link OpenPGPKey} (TSK - transferable secret key) is the pendant to an {@link OpenPGPCertificate}, + * but containing the secret key material in addition to the public components. + * It consists of one or multiple {@link OpenPGPSecretKey} objects. + */ +public class OpenPGPKey + extends OpenPGPCertificate +{ + // This class extends OpenPGPCertificate, but also holds secret key components in a dedicated map. + private final Map secretKeys; + + /** + * Create an {@link OpenPGPKey} instance based on a {@link PGPSecretKeyRing}. + * The {@link OpenPGPImplementation} will be acquired by invoking {@link OpenPGPImplementation#getInstance()}. + * + * @param keyRing secret key ring + */ + public OpenPGPKey(PGPSecretKeyRing keyRing) + { + this(keyRing, OpenPGPImplementation.getInstance()); + } + + /** + * Create an {@link OpenPGPKey} instance based on a {@link PGPSecretKeyRing}, + * a provided {@link OpenPGPImplementation} and its {@link OpenPGPPolicy}. + * + * @param keyRing secret key ring + * @param implementation OpenPGP implementation + */ + public OpenPGPKey(PGPSecretKeyRing keyRing, OpenPGPImplementation implementation) + { + this(keyRing, implementation, implementation.policy()); + } + + /** + * Create an {@link OpenPGPKey} instance based on a {@link PGPSecretKeyRing}, + * a provided {@link OpenPGPImplementation} and {@link OpenPGPPolicy}. + * + * @param keyRing secret key ring + * @param implementation OpenPGP implementation + * @param policy OpenPGP policy + */ + public OpenPGPKey(PGPSecretKeyRing keyRing, OpenPGPImplementation implementation, OpenPGPPolicy policy) + { + super(keyRing, implementation, policy); + + // Process and map secret keys + this.secretKeys = new HashMap<>(); + for (Iterator it = getKeys().iterator(); it.hasNext(); ) + { + OpenPGPComponentKey key = (OpenPGPComponentKey)it.next(); + KeyIdentifier identifier = key.getKeyIdentifier(); + PGPSecretKey secretKey = keyRing.getSecretKey(identifier); + if (secretKey == null) + { + continue; + } + + secretKeys.put(identifier, new OpenPGPSecretKey(key, secretKey, implementation.pbeSecretKeyDecryptorBuilderProvider())); + } + } + + @Override + public boolean isSecretKey() + { + return true; + } + + /** + * Return the {@link OpenPGPCertificate} of this {@link OpenPGPKey}. + * + * @return certificate + */ + public OpenPGPCertificate toCertificate() + { + return new OpenPGPCertificate(getPGPPublicKeyRing(), implementation, policy); + } + + @Override + public List getComponents() + { + // We go through the list of components returned by OpenPGPCertificate and replace those components + // where we have the secret key available + + // contains only public components + List components = super.getComponents(); + for (int i = components.size() - 1; i >= 0; i--) + { + OpenPGPCertificateComponent component = components.get(i); + if (component instanceof OpenPGPComponentKey) + { + OpenPGPSecretKey secretKey = getSecretKey((OpenPGPComponentKey)component); + if (secretKey != null) + { + // swap in secret component + components.remove(i); + components.add(i, secretKey); + } + } + } + return components; + } + + /** + * Return the {@link OpenPGPSecretKey} of this key's primary key. + * + * @return primary secret key + */ + public OpenPGPSecretKey getPrimarySecretKey() + { + return getSecretKey(getPrimaryKey()); + } + + /** + * Return a {@link Map} containing all {@link OpenPGPSecretKey} components (secret subkeys) of the key. + * + * @return secret key components + */ + public Map getSecretKeys() + { + return new HashMap<>(secretKeys); + } + + /** + * Return the {@link OpenPGPSecretKey} identified by the passed {@link KeyIdentifier}. + * + * @param identifier key identifier + * @return corresponding secret key or null + */ + public OpenPGPSecretKey getSecretKey(KeyIdentifier identifier) + { + return secretKeys.get(identifier); + } + + /** + * Return the {@link OpenPGPSecretKey} that corresponds to the passed {@link OpenPGPComponentKey}. + * + * @param key component key + * @return corresponding secret key or null + */ + public OpenPGPSecretKey getSecretKey(OpenPGPComponentKey key) + { + return getSecretKey(key.getKeyIdentifier()); + } + + /** + * Replace the given secret key component. + * + * @param secretKey secret key + */ + void replaceSecretKey(OpenPGPSecretKey secretKey) + { + keyRing = PGPSecretKeyRing.insertSecretKey((PGPSecretKeyRing)keyRing, secretKey.rawSecKey); + secretKeys.put(secretKey.getKeyIdentifier(), secretKey); + } + + @Override + public PGPSecretKeyRing getPGPKeyRing() + { + return getPGPSecretKeyRing(); + } + + /** + * Return the underlying {@link PGPSecretKeyRing}. + * + * @return secret key ring + */ + public PGPSecretKeyRing getPGPSecretKeyRing() + { + return (PGPSecretKeyRing)super.getPGPKeyRing(); + } + + @Override + public byte[] getEncoded(PacketFormat packetFormat) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut, packetFormat); + getPGPSecretKeyRing().encode(pOut); + pOut.close(); + return bOut.toByteArray(); + } + + /** + * Secret key component of a {@link org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPPrimaryKey} or + * {@link org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPSubkey}. + */ + public static class OpenPGPSecretKey + extends OpenPGPComponentKey + { + private final PGPSecretKey rawSecKey; + private final OpenPGPComponentKey pubKey; + private final PBESecretKeyDecryptorBuilderProvider decryptorBuilderProvider; + + /** + * Constructor. + * + * @param pubKey corresponding public key component + * @param secKey secret key + * @param decryptorBuilderProvider for unlocking private keys + */ + public OpenPGPSecretKey(OpenPGPComponentKey pubKey, + PGPSecretKey secKey, + PBESecretKeyDecryptorBuilderProvider decryptorBuilderProvider) + { + super(pubKey.getPGPPublicKey(), pubKey.getCertificate()); + this.decryptorBuilderProvider = decryptorBuilderProvider; + this.rawSecKey = secKey; + this.pubKey = pubKey; + } + + @Override + protected OpenPGPCertificateComponent getPublicComponent() + { + // return the public key component to properly map this secret key to its public key component when + // the public key component is used as key in a map. + return pubKey; + } + + @Override + public boolean isPrimaryKey() + { + return getPublicKey().isPrimaryKey(); + } + + @Override + public OpenPGPComponentSignature getLatestSelfSignature(Date evaluationTime) + { + return getPublicKey().getLatestSelfSignature(evaluationTime); + } + + /** + * Return the {@link OpenPGPKey} which this {@link OpenPGPSecretKey} belongs to. + * + * @return OpenPGPKey + */ + public OpenPGPKey getOpenPGPKey() + { + return (OpenPGPKey)getCertificate(); + } + + @Override + public String toDetailString() + { + return "Private" + pubKey.toDetailString(); + } + + /** + * Return the underlying {@link PGPSecretKey}. + * + * @return secret key + */ + public PGPSecretKey getPGPSecretKey() + { + return rawSecKey; + } + + /** + * Return the public {@link OpenPGPComponentKey} corresponding to this {@link OpenPGPSecretKey}. + * + * @return public component key + */ + public OpenPGPComponentKey getPublicKey() + { + return pubKey; + } + + /** + * If true, the secret key is not available in plain and likely needs to be decrypted by providing + * a key passphrase. + * + * @return true if the key is locked + */ + public boolean isLocked() + { + return getPGPSecretKey().getS2KUsage() != SecretKeyPacket.USAGE_NONE; + } + + /** + * Unlock an unprotected {@link OpenPGPSecretKey}. + * + * @return unlocked private key + * @throws PGPException if the key cannot be unlocked + */ + public OpenPGPPrivateKey unlock() + throws PGPException + { + return unlock((char[])null); + } + + /** + * Unlock a protected {@link OpenPGPSecretKey}. + * + * @param passphraseProvider provider for key passphrases + * @return unlocked private key + * @throws PGPException if the key cannot be unlocked + */ + public OpenPGPPrivateKey unlock(KeyPassphraseProvider passphraseProvider) + throws PGPException + { + if (!isLocked()) + { + return unlock((char[])null); + } + return unlock(passphraseProvider.getKeyPassword(this)); + } + + /** + * Access the {@link PGPKeyPair} by unlocking the potentially locked secret key using the provided + * passphrase. Note: If the key is not locked, it is sufficient to pass null as passphrase. + * + * @param passphrase passphrase or null + * @return keypair containing unlocked private key + * @throws PGPException if the key cannot be unlocked + */ + public OpenPGPPrivateKey unlock(char[] passphrase) + throws PGPException + { + sanitizeProtectionMode(); + PBESecretKeyDecryptor decryptor = null; + try + { + if (passphrase != null) + { + decryptor = decryptorBuilderProvider.provide().build(passphrase); + } + + PGPPrivateKey privateKey = getPGPSecretKey().extractPrivateKey(decryptor); + if (privateKey == null) + { + return null; + } + + PGPKeyPair unlockedKey = new PGPKeyPair(getPGPSecretKey().getPublicKey(), privateKey); + return new OpenPGPPrivateKey(this, unlockedKey); + } + catch (PGPException e) + { + throw new KeyPassphraseException(this, e); + } + } + + private void sanitizeProtectionMode() + throws PGPException + { + if (!isLocked()) + { + return; + } + + PGPSecretKey secretKey = getPGPSecretKey(); + S2K s2k = secretKey.getS2K(); + if (s2k == null) + { + throw new PGPKeyValidationException("Legacy CFB using MD5 is not allowed."); + } + + if (s2k.getType() == S2K.ARGON_2 && secretKey.getS2KUsage() != SecretKeyPacket.USAGE_AEAD) + { + throw new PGPKeyValidationException("Argon2 without AEAD is not allowed."); + } + + if (getVersion() == PublicKeyPacket.VERSION_6) + { + if (secretKey.getS2KUsage() == SecretKeyPacket.USAGE_CHECKSUM) + { + throw new PGPKeyValidationException("Version 6 keys MUST NOT use malleable CFB."); + } + if (s2k.getType() == S2K.SIMPLE) + { + throw new PGPKeyValidationException("Version 6 keys MUST NOT use SIMPLE S2K."); + } + } + } + + /** + * Return true if the provided passphrase is correct. + * + * @param passphrase passphrase + * @return true if the passphrase is correct + */ + public boolean isPassphraseCorrect(char[] passphrase) + { + if (passphrase != null && !isLocked()) + { + return false; + } + + try + { + OpenPGPPrivateKey privateKey = unlock(passphrase); + return privateKey.unlockedKey != null; + } + catch (PGPException e) + { + return false; + } + } + } + + /** + * Unlocked {@link OpenPGPSecretKey}. + */ + public static class OpenPGPPrivateKey + { + private final OpenPGPSecretKey secretKey; + private final PGPKeyPair unlockedKey; + + public OpenPGPPrivateKey(OpenPGPSecretKey secretKey, PGPKeyPair unlockedKey) + { + this.secretKey = secretKey; + this.unlockedKey = unlockedKey; + } + + /** + * Return the public {@link OpenPGPComponentKey} of this {@link OpenPGPPrivateKey}. + * + * @return public component key + */ + public OpenPGPComponentKey getPublicKey() + { + return secretKey.getPublicKey(); + } + + /** + * Return the {@link OpenPGPSecretKey} in its potentially locked form. + * + * @return secret key + */ + public OpenPGPSecretKey getSecretKey() + { + return secretKey; + } + + /** + * Return the unlocked {@link PGPKeyPair} containing the decrypted {@link PGPPrivateKey}. + * + * @return unlocked private key + */ + public PGPKeyPair getKeyPair() + { + return unlockedKey; + } + + /** + * Return the used {@link OpenPGPImplementation}. + * + * @return implementation + */ + private OpenPGPImplementation getImplementation() + { + return getSecretKey().getOpenPGPKey().implementation; + } + + /** + * Return a NEW instance of the {@link OpenPGPSecretKey} locked with the new passphrase. + * If the key was unprotected before, or if it was protected using AEAD, the new instance will be + * protected using AEAD as well. + * + * @param newPassphrase new passphrase + * @return new instance of the key, locked with the new passphrase + * @throws PGPException if the key cannot be locked + */ + public OpenPGPSecretKey changePassphrase(char[] newPassphrase) + throws PGPException + { + boolean useAead = !secretKey.isLocked() || + secretKey.getPGPSecretKey().getS2KUsage() == SecretKeyPacket.USAGE_AEAD; + + return changePassphrase(newPassphrase, getImplementation(), useAead); + } + + /** + * Return a NEW instance of the {@link OpenPGPSecretKey} locked with the new passphrase. + * + * @param newPassphrase new passphrase + * @param implementation OpenPGP implementation + * @param useAEAD whether to protect the key using AEAD + * @return new instance of the key, locked with the new passphrase + * @throws PGPException if the key cannot be locked + */ + public OpenPGPSecretKey changePassphrase(char[] newPassphrase, + OpenPGPImplementation implementation, + boolean useAEAD) + throws PGPException + { + return changePassphrase(newPassphrase, implementation.pbeSecretKeyEncryptorFactory(useAEAD)); + } + + /** + * Return a NEW instance of the {@link OpenPGPSecretKey} locked with the new passphrase. + * + * @param newPassphrase new passphrase + * @param keyEncryptorFactory factory for {@link PBESecretKeyEncryptor} instances + * @return new instance of the key, locked with the new passphrase + * @throws PGPException if the key cannot be locked + */ + public OpenPGPSecretKey changePassphrase(char[] newPassphrase, + PBESecretKeyEncryptorFactory keyEncryptorFactory) + throws PGPException + { + PBESecretKeyEncryptor keyEncryptor; + if (newPassphrase == null || newPassphrase.length == 0) + { + keyEncryptor = null; + } + else + { + keyEncryptor = keyEncryptorFactory.build( + newPassphrase, + getKeyPair().getPublicKey().getPublicKeyPacket()); + } + + return changePassphrase(keyEncryptor); + } + + /** + * Return a NEW instance of the {@link OpenPGPSecretKey} locked using the given {@link PBESecretKeyEncryptor}. + * + * @param keyEncryptor encryptor + * @return new instance of the key, locked with the key encryptor + * @throws PGPException if the key cannot be locked + */ + public OpenPGPSecretKey changePassphrase(PBESecretKeyEncryptor keyEncryptor) + throws PGPException + { + PGPSecretKey encrypted = new PGPSecretKey( + getKeyPair().getPrivateKey(), + getKeyPair().getPublicKey(), + getImplementation().pgpDigestCalculatorProvider().get(HashAlgorithmTags.SHA1), + getSecretKey().isPrimaryKey(), + keyEncryptor); + + OpenPGPSecretKey sk = new OpenPGPSecretKey( + getSecretKey().getPublicKey(), + encrypted, + getImplementation().pbeSecretKeyDecryptorBuilderProvider()); + sk.sanitizeProtectionMode(); + return sk; + } + + /** + * Return a NEW instance of the {@link OpenPGPSecretKey} with removed passphrase protection. + * + * @return unlocked new instance of the key + * @throws PGPException if the key cannot be unlocked + */ + public OpenPGPSecretKey removePassphrase() + throws PGPException + { + return changePassphrase((PBESecretKeyEncryptor)null); + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyEditor.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyEditor.java new file mode 100644 index 0000000000..ee2143d709 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyEditor.java @@ -0,0 +1,477 @@ +package org.bouncycastle.openpgp.api; + +import java.util.Date; + +import org.bouncycastle.bcpg.HashAlgorithmTags; +import org.bouncycastle.bcpg.KeyIdentifier; +import org.bouncycastle.bcpg.PublicKeyUtils; +import org.bouncycastle.bcpg.sig.KeyFlags; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyPair; +import org.bouncycastle.openpgp.PGPKeyValidationException; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureGenerator; +import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.bouncycastle.openpgp.api.exception.OpenPGPKeyException; +import org.bouncycastle.openpgp.operator.PGPKeyPairGenerator; + +public class OpenPGPKeyEditor + extends AbstractOpenPGPKeySignatureGenerator +{ + + private final OpenPGPImplementation implementation; + private final OpenPGPPolicy policy; + private OpenPGPKey key; + private final OpenPGPKey.OpenPGPPrivateKey primaryKey; + + public OpenPGPKeyEditor(OpenPGPKey key, KeyPassphraseProvider passphraseProvider) + throws PGPException + { + this(key, passphraseProvider, key.implementation); + } + + public OpenPGPKeyEditor(OpenPGPKey key, + KeyPassphraseProvider passphraseProvider, + OpenPGPImplementation implementation) + throws PGPException + { + this(key, passphraseProvider, implementation, implementation.policy()); + } + + public OpenPGPKeyEditor(OpenPGPKey key, + KeyPassphraseProvider passphraseProvider, + OpenPGPImplementation implementation, + OpenPGPPolicy policy) + throws PGPException + { + this.key = key; + this.primaryKey = key.getPrimarySecretKey().unlock(passphraseProvider); + this.implementation = implementation; + this.policy = policy; + } + + public OpenPGPKeyEditor addDirectKeySignature(SignatureParameters.Callback signatureCallback) + throws PGPException + { + SignatureParameters parameters = Utils.applySignatureParameters(signatureCallback, + SignatureParameters.directKeySignature(policy)); + + if (parameters != null) + { + PGPPublicKey publicPrimaryKey = key.getPrimaryKey().getPGPPublicKey(); + + PGPSignatureGenerator dkSigGen = Utils.getPgpSignatureGenerator(implementation, publicPrimaryKey, + primaryKey.getKeyPair().getPrivateKey(), parameters, parameters.getSignatureCreationTime(), null); + + PGPPublicKey pubKey = Utils.injectCertification(publicPrimaryKey, dkSigGen); + this.key = generateOpenPGPKey(pubKey); + } + return this; + } + + /** + * Add a user-id to the primary key. + * If the key already contains the given user-id, a new certification signature will be added to the user-id. + * + * @param userId user-id + * @return this + * @throws PGPException if the key cannot be modified + */ + public OpenPGPKeyEditor addUserId(String userId) + throws PGPException + { + return addUserId(userId, null); + } + + /** + * Add a user-id to the primary key, modifying the contents of the certification signature using the given + * {@link SignatureParameters.Callback}. + * If the key already contains the given user-id, a new certification signature will be added to the user-id. + * + * @param userId user-id + * @param signatureCallback callback to modify the certification signature contents + * @return this + * @throws PGPException if the key cannot be modified + */ + public OpenPGPKeyEditor addUserId(String userId, + SignatureParameters.Callback signatureCallback) + throws PGPException + { + if (userId == null || userId.trim().isEmpty()) + { + throw new IllegalArgumentException("User-ID cannot be null or empty."); + } + + SignatureParameters parameters = Utils.applySignatureParameters(signatureCallback, + SignatureParameters.certification(policy)); + + if (parameters != null) + { + PGPPublicKey publicPrimaryKey = key.getPrimaryKey().getPGPPublicKey(); + + PGPSignatureGenerator uidSigGen = Utils.getPgpSignatureGenerator(implementation, publicPrimaryKey, + primaryKey.getKeyPair().getPrivateKey(), parameters, parameters.getSignatureCreationTime(), null); + + this.key = generateOpenPGPKey(Utils.injectCertification(userId, publicPrimaryKey, uidSigGen)); + } + return this; + } + + /** + * Revoke the given {@link OpenPGPCertificate.OpenPGPIdentityComponent}. + * + * @param identity user-id to be revoked + * @return this + * @throws PGPException if the key cannot be modified + */ + public OpenPGPKeyEditor revokeIdentity(OpenPGPCertificate.OpenPGPIdentityComponent identity) + throws PGPException + { + return revokeIdentity(identity, null); + } + + /** + * Revoke the given {@link OpenPGPCertificate.OpenPGPUserId}, allowing modification of the revocation signature + * using the given {@link SignatureParameters.Callback}. + * + * @param identity user-id to revoke + * @param signatureCallback callback to modify the revocation signature contents + * @return this + * @throws PGPException if the key cannot be modified + */ + public OpenPGPKeyEditor revokeIdentity(OpenPGPCertificate.OpenPGPIdentityComponent identity, + SignatureParameters.Callback signatureCallback) + throws PGPException + { + if (!key.getComponents().contains(identity)) + { + throw new IllegalArgumentException("UserID or UserAttribute is not part of the certificate."); + } + + SignatureParameters parameters = Utils.applySignatureParameters(signatureCallback, + SignatureParameters.certificationRevocation(policy)); + + if (parameters != null) + { + PGPPublicKey publicPrimaryKey = key.getPrimaryKey().getPGPPublicKey(); + + PGPSignatureGenerator idSigGen = Utils.getPgpSignatureGenerator(implementation, publicPrimaryKey, + primaryKey.getKeyPair().getPrivateKey(), parameters, parameters.getSignatureCreationTime(), null); + + // Inject signature into the certificate + PGPPublicKey pubKey; + if (identity instanceof OpenPGPCertificate.OpenPGPUserId) + { + OpenPGPCertificate.OpenPGPUserId userId = (OpenPGPCertificate.OpenPGPUserId)identity; + pubKey = Utils.injectCertification(userId.getUserId(), publicPrimaryKey, idSigGen); + } + else + { + OpenPGPCertificate.OpenPGPUserAttribute userAttribute = (OpenPGPCertificate.OpenPGPUserAttribute)identity; + PGPSignature uattrSig = idSigGen.generateCertification(userAttribute.getUserAttribute(), publicPrimaryKey); + pubKey = PGPPublicKey.addCertification(publicPrimaryKey, userAttribute.getUserAttribute(), uattrSig); + } + this.key = generateOpenPGPKey(pubKey); + } + return this; + } + + public OpenPGPKeyEditor addEncryptionSubkey() + throws PGPException + { + return addEncryptionSubkey(KeyPairGeneratorCallback.encryptionKey()); + } + + public OpenPGPKeyEditor addEncryptionSubkey(KeyPairGeneratorCallback keyGenCallback) + throws PGPException + { + return addEncryptionSubkey(keyGenCallback, key.getPrimaryKey().getVersion(), new Date()); + } + + public OpenPGPKeyEditor addEncryptionSubkey(KeyPairGeneratorCallback keyGenCallback, + int version, + Date creationTime) + throws PGPException + { + PGPKeyPairGenerator kpGen = implementation.pgpKeyPairGeneratorProvider() + .get(version, creationTime); + return addEncryptionSubkey(keyGenCallback.generateFrom(kpGen), null); + } + + public OpenPGPKeyEditor addEncryptionSubkey(PGPKeyPair encryptionSubkey, + SignatureParameters.Callback bindingSigCallback) + throws PGPException + { + if (!encryptionSubkey.getPublicKey().isEncryptionKey()) + { + throw new PGPKeyValidationException("Provided subkey is not encryption-capable."); + } + + updateKey(encryptionSubkey, bindingSigCallback, key.getPrimaryKey().getPGPPublicKey(), new Utils.HashedSubpacketsOperation() + { + @Override + public void operate(PGPSignatureSubpacketGenerator hashedSubpackets) + throws PGPException + { + hashedSubpackets.setKeyFlags(KeyFlags.ENCRYPT_STORAGE | KeyFlags.ENCRYPT_COMMS); + } + }); + + return this; + } + + public OpenPGPKeyEditor addSigningSubkey() + throws PGPException + { + return addSigningSubkey(KeyPairGeneratorCallback.signingKey()); + } + + public OpenPGPKeyEditor addSigningSubkey(KeyPairGeneratorCallback keyGenCallback) + throws PGPException + { + return addSigningSubkey(keyGenCallback, key.getPrimaryKey().getVersion(), new Date()); + } + + public OpenPGPKeyEditor addSigningSubkey(KeyPairGeneratorCallback keyGenCallback, + int version, + Date creationTime) + throws PGPException + { + PGPKeyPairGenerator kpGen = implementation.pgpKeyPairGeneratorProvider() + .get(version, creationTime); + return addSigningSubkey(keyGenCallback.generateFrom(kpGen), null, null); + } + + public OpenPGPKeyEditor addSigningSubkey(PGPKeyPair signingSubkey, + SignatureParameters.Callback bindingSigCallback, + SignatureParameters.Callback backSigCallback) + throws PGPException + { + if (!PublicKeyUtils.isSigningAlgorithm(signingSubkey.getPublicKey().getAlgorithm())) + { + throw new PGPKeyValidationException("Provided subkey is not signing-capable."); + } + + SignatureParameters backSigParameters = Utils.applySignatureParameters(backSigCallback, + SignatureParameters.primaryKeyBinding(policy)); + + PGPPublicKey publicPrimaryKey = key.getPrimaryKey().getPGPPublicKey(); + + final PGPSignature backSig = Utils.getBackSignature(signingSubkey, backSigParameters, publicPrimaryKey, implementation, null); + + updateKey(signingSubkey, bindingSigCallback, publicPrimaryKey, new Utils.HashedSubpacketsOperation() + { + @Override + public void operate(PGPSignatureSubpacketGenerator hashedSubpackets) + throws PGPException + { + hashedSubpackets.setKeyFlags(KeyFlags.SIGN_DATA); + Utils.addEmbeddedSiganture(backSig, hashedSubpackets); + } + }); + + return this; + } + + /** + * Add a component key to the certificate. + * The bindingSigCallback can be used to modify the subkey binding signature. + * If it is null, no subkey binding signature will be generated. + * The backSigCallback can be used to modify the embedded primary key binding signature. + * If it is null, no primary key binding signature will be generated. + * You MUST only pass a non-null value here, if the subkey is capable of creating signatures. + * + * @param subkey component key + * @param bindingSigCallback callback to modify the subkey binding signature + * @param backSigCallback callback to modify the embedded primary key binding signature + * @return this + * @throws PGPException + */ + public OpenPGPKeyEditor addSubkey(PGPKeyPair subkey, + SignatureParameters.Callback bindingSigCallback, + SignatureParameters.Callback backSigCallback) + throws PGPException + { + if (PublicKeyUtils.isSigningAlgorithm(subkey.getPublicKey().getAlgorithm()) + && backSigCallback != null) + { + throw new PGPKeyValidationException("Provided subkey is not signing-capable, so we cannot create a back-signature."); + } + + PGPPublicKey publicSubKey = subkey.getPublicKey(); + + SignatureParameters backSigParameters = Utils.applySignatureParameters(backSigCallback, + SignatureParameters.primaryKeyBinding(policy)); + + PGPPublicKey publicPrimaryKey = key.getPrimaryKey().getPGPPublicKey(); + + final PGPSignature backSig = Utils.getBackSignature(subkey, backSigParameters, publicPrimaryKey, implementation, null); + + SignatureParameters parameters = Utils.applySignatureParameters(bindingSigCallback, + SignatureParameters.subkeyBinding(policy)); + + if (parameters != null) + { + PGPSignatureGenerator subKeySigGen = Utils.getPgpSignatureGenerator(implementation, publicPrimaryKey, + primaryKey.getKeyPair().getPrivateKey(), parameters, parameters.getSignatureCreationTime(), + new Utils.HashedSubpacketsOperation() + { + @Override + public void operate(PGPSignatureSubpacketGenerator hashedSubpackets) + throws PGPException + { + Utils.addEmbeddedSiganture(backSig, hashedSubpackets); + } + }); + + // Inject signature into the certificate + publicSubKey = Utils.injectCertification(publicSubKey, subKeySigGen, publicPrimaryKey); + } + + this.key = generateOpenPGPKey(subkey, publicSubKey); + + return this; + } + + public OpenPGPKeyEditor revokeComponentKey(OpenPGPCertificate.OpenPGPComponentKey componentKey) + throws PGPException + { + return revokeComponentKey(componentKey, null); + } + + public OpenPGPKeyEditor revokeComponentKey(OpenPGPCertificate.OpenPGPComponentKey componentKey, + SignatureParameters.Callback revocationSignatureCallback) + throws PGPException + { + boolean contained = key.getKey(componentKey.getKeyIdentifier()) != null; + if (!contained) + { + throw new IllegalArgumentException("Provided component key is not part of the OpenPGP key."); + } + + boolean isSubkeyRevocation = !componentKey.getKeyIdentifier().equals(key.getKeyIdentifier()); + SignatureParameters parameters; + if (isSubkeyRevocation) + { + // Generate Subkey Revocation Signature + parameters = SignatureParameters.subkeyRevocation(policy); + } + else + { + // Generate Key Revocation Signature + parameters = SignatureParameters.keyRevocation(policy); + } + + parameters = Utils.applySignatureParameters(revocationSignatureCallback, parameters); + + PGPPublicKey publicPrimaryKey = key.getPrimaryKey().getPGPPublicKey(); + PGPSignatureGenerator revGen = Utils.getPgpSignatureGenerator(implementation, publicPrimaryKey, + primaryKey.getKeyPair().getPrivateKey(), parameters, parameters.getSignatureCreationTime(), null); + + if (isSubkeyRevocation) + { + publicPrimaryKey = Utils.injectCertification(componentKey.getPGPPublicKey(), revGen, publicPrimaryKey); + } + else + { + publicPrimaryKey = Utils.injectCertification(publicPrimaryKey, revGen); + } + this.key = generateOpenPGPKey(publicPrimaryKey); + + return this; + } + + public OpenPGPKeyEditor revokeKey() + throws PGPException + { + return revokeKey(null); + } + + public OpenPGPKeyEditor revokeKey(SignatureParameters.Callback revocationSignatureCallback) + throws PGPException + { + return revokeComponentKey(key.getPrimaryKey(), revocationSignatureCallback); + } + + /** + * Change the passphrase of the given component key. + * + * @param componentKeyIdentifier identifier of the component key, whose passphrase shall be changed + * @param oldPassphrase old passphrase (or null) + * @param newPassphrase new passphrase (or null) + * @param useAEAD whether to use AEAD + * @return this + * @throws OpenPGPKeyException if the secret component of the component key is missing + * @throws PGPException if the key passphrase cannot be changed + */ + public OpenPGPKeyEditor changePassphrase(KeyIdentifier componentKeyIdentifier, + char[] oldPassphrase, + char[] newPassphrase, + boolean useAEAD) + throws OpenPGPKeyException, PGPException + { + OpenPGPKey.OpenPGPSecretKey secretKey = key.getSecretKey(componentKeyIdentifier); + if (secretKey == null) + { + throw new OpenPGPKeyException(key, "Secret component key " + componentKeyIdentifier + + " is missing from the key."); + } + + OpenPGPKey.OpenPGPPrivateKey privateKey = secretKey.unlock(oldPassphrase); + secretKey = privateKey.changePassphrase(newPassphrase, implementation, useAEAD); + + key.replaceSecretKey(secretKey); + return this; + } + + /** + * Return the modified {@link OpenPGPKey}. + * + * @return modified key + */ + public OpenPGPKey done() + { + return key; + } + + private OpenPGPKey generateOpenPGPKey(PGPPublicKey publicPrimaryKey) + { + PGPPublicKeyRing publicKeyRing = PGPPublicKeyRing.insertPublicKey(key.getPGPPublicKeyRing(), publicPrimaryKey); + PGPSecretKeyRing secretKeyRing = PGPSecretKeyRing.replacePublicKeys(key.getPGPKeyRing(), publicKeyRing); + return new OpenPGPKey(secretKeyRing, implementation, policy); + } + + private OpenPGPKey generateOpenPGPKey(PGPKeyPair subkey, PGPPublicKey publicSubKey) + throws PGPException + { + PGPSecretKey secretSubkey = new PGPSecretKey( + subkey.getPrivateKey(), + publicSubKey, + implementation.pgpDigestCalculatorProvider().get(HashAlgorithmTags.SHA1), + false, + null); + PGPSecretKeyRing secretKeyRing = PGPSecretKeyRing.insertSecretKey(key.getPGPKeyRing(), secretSubkey); + return new OpenPGPKey(secretKeyRing, implementation, policy); + } + + private void updateKey(PGPKeyPair subkey, SignatureParameters.Callback bindingSigCallback, PGPPublicKey publicPrimaryKey, Utils.HashedSubpacketsOperation operation) + throws PGPException + { + SignatureParameters parameters = Utils.applySignatureParameters(bindingSigCallback, + SignatureParameters.subkeyBinding(policy)); + + if (parameters != null) + { + PGPSignatureGenerator subKeySigGen = Utils.getPgpSignatureGenerator(implementation, publicPrimaryKey, + primaryKey.getKeyPair().getPrivateKey(), parameters, parameters.getSignatureCreationTime(), + operation); + + PGPPublicKey publicSubKey = Utils.injectCertification(subkey.getPublicKey(), subKeySigGen, publicPrimaryKey); + this.key = generateOpenPGPKey(subkey, publicSubKey); + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java new file mode 100644 index 0000000000..7fb7fc235c --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java @@ -0,0 +1,732 @@ +package org.bouncycastle.openpgp.api; + +import java.util.ArrayList; +import java.util.Date; +import java.util.Iterator; +import java.util.List; + +import org.bouncycastle.bcpg.HashAlgorithmTags; +import org.bouncycastle.bcpg.PublicKeyPacket; +import org.bouncycastle.bcpg.PublicKeyUtils; +import org.bouncycastle.bcpg.PublicSubkeyPacket; +import org.bouncycastle.bcpg.S2K; +import org.bouncycastle.bcpg.SignatureSubpacketTags; +import org.bouncycastle.bcpg.sig.KeyFlags; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyPair; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSecretKey; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureGenerator; +import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; +import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptorFactory; +import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider; +import org.bouncycastle.openpgp.operator.PGPKeyPairGenerator; +import org.bouncycastle.openpgp.operator.PGPKeyPairGeneratorProvider; +import org.bouncycastle.util.Arrays; + +/** + * High-level generator class for OpenPGP v6 keys. + */ +public class OpenPGPKeyGenerator + extends AbstractOpenPGPKeySignatureGenerator +{ + // SECONDS + private static final long SECONDS_PER_MINUTE = 60; + private static final long SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE; + private static final long SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR; + private static final long SECONDS_PER_YEAR = 365 * SECONDS_PER_DAY; + + private final int keyVersion; + private final OpenPGPImplementation implementationProvider; + private final Configuration configuration; // contains BC or JCA/JCE implementations + + public OpenPGPKeyGenerator(OpenPGPImplementation implementation, + boolean aead, + Date creationTime) + throws PGPException + { + this(implementation, PublicKeyPacket.VERSION_6, aead, creationTime); + } + + public OpenPGPKeyGenerator(OpenPGPImplementation implementationProvider, + int version, + boolean aead, + Date creationTime) + throws PGPException + { + this( + implementationProvider, + version, + implementationProvider.pgpKeyPairGeneratorProvider(), + implementationProvider.pgpDigestCalculatorProvider(), + implementationProvider.pbeSecretKeyEncryptorFactory(aead), + implementationProvider.keyFingerPrintCalculator(), + creationTime + ); + } + + /** + * Generate a new OpenPGP key generator for v6 keys. + * + * @param kpGenProvider key pair generator provider + * @param digestCalculatorProvider digest calculator provider + * @param keyEncryptionBuilderProvider secret key encryption builder provider (AEAD) + * @param keyFingerPrintCalculator calculator for key fingerprints + * @param creationTime key creation time + */ + public OpenPGPKeyGenerator( + OpenPGPImplementation implementationProvider, + int keyVersion, + PGPKeyPairGeneratorProvider kpGenProvider, + PGPDigestCalculatorProvider digestCalculatorProvider, + PBESecretKeyEncryptorFactory keyEncryptionBuilderProvider, + KeyFingerPrintCalculator keyFingerPrintCalculator, + Date creationTime) + { + if (keyVersion != PublicKeyPacket.VERSION_4 && + keyVersion != PublicKeyPacket.LIBREPGP_5 && + keyVersion != PublicKeyPacket.VERSION_6) + { + throw new IllegalArgumentException("Generating keys of version " + keyVersion + " is not supported."); + } + + this.implementationProvider = implementationProvider; + this.keyVersion = keyVersion; + this.configuration = new Configuration(creationTime, kpGenProvider, digestCalculatorProvider, keyEncryptionBuilderProvider, keyFingerPrintCalculator); + } + + /** + * Generate an OpenPGP key consisting of a certify-only primary key, + * a dedicated signing-subkey and dedicated encryption-subkey. + * The key will optionally carry the provided user-id. + * See {@link PGPKeyPairGenerator#generatePrimaryKey()} for the primary key type, + * {@link PGPKeyPairGenerator#generateSigningSubkey()} for the signing-subkey type and + * {@link PGPKeyPairGenerator#generateEncryptionSubkey()} for the encryption-subkey key type. + * + * @param userId nullable user id + * @return OpenPGP key + * @throws PGPException if the key cannot be prepared + */ + public WithPrimaryKey classicKey(String userId) + throws PGPException + { + WithPrimaryKey builder = withPrimaryKey() + .addSigningSubkey() + .addEncryptionSubkey(); + + if (userId != null) + { + builder.addUserId(userId); + } + + return builder; + } + + /** + * Generate an OpenPGP key consisting of an Ed25519 certify-only primary key, + * a dedicated Ed25519 sign-only subkey and dedicated X25519 encryption-only subkey. + * The key will optionally carry the provided user-id. + * + * @param userId nullable user id + * @return OpenPGP key + * @throws PGPException if the key cannot be generated + */ + public WithPrimaryKey ed25519x25519Key(String userId) + throws PGPException + { + WithPrimaryKey builder = withPrimaryKey(new KeyPairGeneratorCallback() + { + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateEd25519KeyPair(); + } + }) + .addSigningSubkey(new KeyPairGeneratorCallback() + { + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateEd25519KeyPair(); + } + }) + .addEncryptionSubkey(new KeyPairGeneratorCallback() + { + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateX25519KeyPair(); + } + }); + + if (userId != null) + { + builder.addUserId(userId); + } + + return builder; + } + + + /** + * Generate an OpenPGP key consisting of an Ed448 certify-only primary key, + * a dedicated Ed448 sign-only subkey and dedicated X448 encryption-only subkey. + * The key will optionally carry the provided user-id. + * + * @param userId nullable user id + * @return OpenPGP key + * @throws PGPException if the key cannot be generated + */ + public WithPrimaryKey ed448x448Key(String userId) + throws PGPException + { + WithPrimaryKey builder = withPrimaryKey(new KeyPairGeneratorCallback() + { + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateEd448KeyPair(); + } + }) + .addSigningSubkey(new KeyPairGeneratorCallback() + { + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateEd448KeyPair(); + } + }) + .addEncryptionSubkey(new KeyPairGeneratorCallback() + { + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateX448KeyPair(); + } + }); + + if (userId != null) + { + builder.addUserId(userId); + } + + return builder; + } + + /** + * Generate a sign-only OpenPGP key. + * The key consists of a single, user-id-less primary key, which is capable of signing and certifying. + * See {@link PGPKeyPairGenerator#generatePrimaryKey()} for the key type. + * + * @return sign-only (+certify) OpenPGP key + * @throws PGPException if the key cannot be generated + */ + public WithPrimaryKey signOnlyKey() + throws PGPException + { + return withPrimaryKey( + KeyPairGeneratorCallback.primaryKey(), + SignatureParameters.Callback.modifyHashedSubpackets(new SignatureSubpacketsFunction() + { + @Override + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS); + subpackets.setKeyFlags(true, KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA); + return subpackets; + } + })); + } + + /** + * Generate an OpenPGP key with a certification-capable primary key. + * See {@link PGPKeyPairGenerator#generatePrimaryKey()} for the primary key type + * + * @return builder + * @throws PGPException if the key cannot be generated + */ + public WithPrimaryKey withPrimaryKey() + throws PGPException + { + return withPrimaryKey(KeyPairGeneratorCallback.primaryKey()); + } + + /** + * Generate an OpenPGP key with a certification-capable primary key. + * The primary key type can be decided using the {@link KeyPairGeneratorCallback}. + * + * @param keyGenCallback callback to decide the key type + * @return builder + * @throws PGPException if the key cannot be generated + */ + public WithPrimaryKey withPrimaryKey( + KeyPairGeneratorCallback keyGenCallback) + throws PGPException + { + return withPrimaryKey(keyGenCallback, null); + } + + /** + * Generate an OpenPGP key with a certification-capable primary key. + * The primary key type can be decided using the {@link KeyPairGeneratorCallback}. + * The {@link SignatureParameters.Callback} can be used to modify the preferences in the direct-key self signature. + * If the callback itself is null, the generator will create a default direct-key signature. + * If the result of {@link SignatureParameters.Callback#apply(SignatureParameters)} is null, no direct-key + * signature will be generated for the key. + * + * @param keyGenCallback callback to decide the key type + * @param preferenceSignatureCallback callback to modify the direct-key signature + * @return builder + * @throws PGPException if the key cannot be generated + */ + public WithPrimaryKey withPrimaryKey( + KeyPairGeneratorCallback keyGenCallback, + SignatureParameters.Callback preferenceSignatureCallback) + throws PGPException + { + PGPKeyPair primaryKeyPair = keyGenCallback.generateFrom(configuration.kpGenProvider.get( + keyVersion, configuration.keyCreationTime)); + + if (primaryKeyPair.getPublicKey().getPublicKeyPacket() instanceof PublicSubkeyPacket) + { + throw new IllegalArgumentException("Primary key MUST NOT consist of subkey packet."); + } + + if (!PublicKeyUtils.isSigningAlgorithm(primaryKeyPair.getPublicKey().getAlgorithm())) + { + throw new PGPException("Primary key MUST use signing-capable algorithm."); + } + + SignatureParameters parameters = Utils.applySignatureParameters(preferenceSignatureCallback, + SignatureParameters.directKeySignature(implementationProvider.policy())); + + if (parameters != null) + { + PGPSignatureGenerator preferenceSigGen = Utils.getPgpSignatureGenerator(implementationProvider, + primaryKeyPair.getPublicKey(), primaryKeyPair.getPrivateKey(), parameters, configuration.keyCreationTime, + new Utils.HashedSubpacketsOperation() + { + @Override + public void operate(PGPSignatureSubpacketGenerator hashedSubpackets) + throws PGPException + { + hashedSubpackets = directKeySignatureSubpackets.apply(hashedSubpackets); + hashedSubpackets.setKeyFlags(true, KeyFlags.CERTIFY_OTHER); + hashedSubpackets.setKeyExpirationTime(false, 5 * SECONDS_PER_YEAR); + } + }); + + primaryKeyPair = new PGPKeyPair( + Utils.injectCertification(primaryKeyPair.getPublicKey(), preferenceSigGen), + primaryKeyPair.getPrivateKey()); + } + + return new WithPrimaryKey(implementationProvider, configuration, primaryKeyPair); + } + + /** + * Intermediate builder class. + * Constructs an OpenPGP key from a specified primary key. + */ + public class WithPrimaryKey + { + private final OpenPGPImplementation implementation; + private final Configuration configuration; + private PGPKeyPair primaryKey; + private final List subkeys = new ArrayList(); + + /** + * Builder. + * + * @param implementation cryptographic implementation + * @param primaryKey specified primary key + */ + private WithPrimaryKey(OpenPGPImplementation implementation, Configuration configuration, PGPKeyPair primaryKey) + { + this.implementation = implementation; + this.configuration = configuration; + this.primaryKey = primaryKey; + } + + /** + * Attach a User-ID with a positive certification to the key. + * + * @param userId user-id + * @return builder + * @throws PGPException if the user-id cannot be added + */ + public WithPrimaryKey addUserId(String userId) + throws PGPException + { + return addUserId(userId, null); + } + + /** + * Attach a User-ID with a positive certification to the key. + * The subpackets of the user-id certification can be modified using the userIdSubpackets callback. + * + * @param userId user-id + * @param signatureParameters signature parameters + * @return builder + * @throws PGPException if the user-id cannot be added + */ + public WithPrimaryKey addUserId( + String userId, + SignatureParameters.Callback signatureParameters) + throws PGPException + { + if (userId == null || userId.trim().isEmpty()) + { + throw new IllegalArgumentException("User-ID cannot be null or empty."); + } + + SignatureParameters parameters = Utils.applySignatureParameters(signatureParameters, + SignatureParameters.certification(implementation.policy())); + + if (parameters != null) + { + PGPSignatureGenerator uidSigGen = Utils.getPgpSignatureGenerator(implementation, primaryKey.getPublicKey(), + primaryKey.getPrivateKey(), parameters, configuration.keyCreationTime, null); + primaryKey = new PGPKeyPair(Utils.injectCertification(userId, primaryKey.getPublicKey(), uidSigGen), primaryKey.getPrivateKey()); + } + + return this; + } + + /** + * Add an encryption-capable subkey to the OpenPGP key. + * See {@link PGPKeyPairGenerator#generateEncryptionSubkey()} for the key type. + * + * @return builder + * @throws PGPException if the key cannot be generated + */ + public WithPrimaryKey addEncryptionSubkey() + throws PGPException + { + return addEncryptionSubkey(KeyPairGeneratorCallback.encryptionKey()); + } + + /** + * Add an encryption-capable subkey to the OpenPGP key. + * The type of the subkey can be decided by implementing the {@link KeyPairGeneratorCallback}. + * + * @param keyGenCallback callback to decide the encryption subkey type + * @return builder + * @throws PGPException if the key cannot be generated + */ + public WithPrimaryKey addEncryptionSubkey(KeyPairGeneratorCallback keyGenCallback) + throws PGPException + { + return addEncryptionSubkey(keyGenCallback, null); + } + + /** + * Add an encryption-capable subkey to the OpenPGP key. + * The type of the subkey can be decided by implementing the {@link KeyPairGeneratorCallback}. + * The binding signature can be modified by implementing the {@link SignatureSubpacketsFunction}. + * + * @param generatorCallback callback to specify the encryption key type. + * @param bindingSubpacketsCallback nullable callback to modify the binding signature subpackets + * @return builder + * @throws PGPException if the key cannot be generated + */ + public WithPrimaryKey addEncryptionSubkey( + KeyPairGeneratorCallback generatorCallback, + SignatureParameters.Callback bindingSubpacketsCallback) + throws PGPException + { + PGPKeyPairGenerator generator = configuration.kpGenProvider.get( + keyVersion, configuration.keyCreationTime); + PGPKeyPair subkey = generatorCallback.generateFrom(generator); + subkey = subkey.asSubkey(implementation.keyFingerPrintCalculator()); + + return addEncryptionSubkey(subkey, bindingSubpacketsCallback); + } + + /** + * Add an encryption-capable subkey to the OpenPGP key. + * IMPORTANT: The custom key encryptor will only be used, if in the final step the key is retrieved + * using {@link #build()}. + * If instead {@link #build(char[])} is used, the key-specific encryptor is overwritten with an encryptor + * built from the argument passed into {@link #build(char[])}. + * + * @param encryptionSubkey encryption subkey + * @param bindingSubpacketsCallback nullable callback to modify the subkey binding signature subpackets + * @return builder + * @throws PGPException if the key cannot be generated + */ + public WithPrimaryKey addEncryptionSubkey( + PGPKeyPair encryptionSubkey, + SignatureParameters.Callback bindingSubpacketsCallback) + throws PGPException + { + if (!(encryptionSubkey.getPublicKey().getPublicKeyPacket() instanceof PublicSubkeyPacket)) + { + throw new IllegalArgumentException("Encryption subkey MUST NOT consist of a primary key packet."); + } + + if (!encryptionSubkey.getPublicKey().isEncryptionKey()) + { + throw new PGPException("Encryption key MUST use encryption-capable algorithm."); + } + + encryptionSubkey = updateSubkey(encryptionSubkey, bindingSubpacketsCallback, new Utils.HashedSubpacketsOperation() + { + @Override + public void operate(PGPSignatureSubpacketGenerator hashedSubpackets) + throws PGPException + { + hashedSubpackets = encryptionSubkeySubpackets.apply(hashedSubpackets); + } + }); + + subkeys.add(encryptionSubkey); + return this; + } + + /** + * Add a signing-capable subkey to the OpenPGP key. + * The binding signature will contain a primary-key back-signature. + * See {@link PGPKeyPairGenerator#generateSigningSubkey()} for the key type. + * + * @return builder + * @throws PGPException if the key cannot be generated + */ + public WithPrimaryKey addSigningSubkey() + throws PGPException + { + return addSigningSubkey(KeyPairGeneratorCallback.signingKey()); + } + + /** + * Add a signing-capable subkey to the OpenPGP key. + * The signing-key type can be specified by overriding the {@link KeyPairGeneratorCallback}. + * The binding signature will contain a primary-key back-signature. + * IMPORTANT: The custom subkey passphrase will only be used, if in the final step the key is retrieved + * using {@link #build()}. + * If instead {@link #build(char[])} is used, the key-specific passphrase is overwritten with the argument + * passed into {@link #build(char[])}. + * + * @param keyGenCallback callback to specify the signing-key type + * @return builder + * @throws PGPException if the key cannot be generated + */ + public WithPrimaryKey addSigningSubkey(KeyPairGeneratorCallback keyGenCallback) + throws PGPException + { + return addSigningSubkey(keyGenCallback, null, null); + } + + /** + * Add a signing-capable subkey to the OpenPGP key. + * The signing-key type can be specified by overriding the {@link KeyPairGeneratorCallback}. + * The binding signature will contain a primary-key back-signature. + * The contents of the binding signature(s) can be modified by overriding the respective + * {@link SignatureSubpacketsFunction} instances. + * IMPORTANT: The custom subkey passphrase will only be used, if in the final step the key is retrieved + * using {@link #build()}. + * If instead {@link #build(char[])} is used, the key-specific passphrase is overwritten with the argument + * passed into {@link #build(char[])}. + * + * @param keyGenCallback callback to specify the signing-key type + * @param bindingSignatureCallback callback to modify the contents of the signing subkey binding signature + * @param backSignatureCallback callback to modify the contents of the embedded primary key binding signature + * @return builder + * @throws PGPException if the key cannot be generated + */ + public WithPrimaryKey addSigningSubkey(KeyPairGeneratorCallback keyGenCallback, + SignatureParameters.Callback bindingSignatureCallback, + SignatureParameters.Callback backSignatureCallback) + throws PGPException + { + PGPKeyPair subkey = keyGenCallback.generateFrom(configuration.kpGenProvider.get( + keyVersion, configuration.keyCreationTime)); + subkey = subkey.asSubkey(configuration.keyFingerprintCalculator); + return addSigningSubkey(subkey, bindingSignatureCallback, backSignatureCallback); + } + + /** + * Add a signing-capable subkey to the OpenPGP key. + * The signing-key type can be specified by overriding the {@link KeyPairGeneratorCallback}. + * The binding signature will contain a primary-key back-signature. + * The contents of the binding signature(s) can be modified by overriding the respective + * {@link SignatureSubpacketsFunction} instances. + * IMPORTANT: The custom key encryptor will only be used, if in the final step the key is retrieved + * using {@link #build()}. + * If instead {@link #build(char[])} is used, the key-specific encryptor is overwritten with an encryptor + * built from the argument passed into {@link #build(char[])}. + * + * @param signingSubkey signing subkey + * @param bindingSignatureCallback callback to modify the contents of the signing subkey binding signature + * @param backSignatureCallback callback to modify the contents of the embedded primary key binding signature + * @return builder + * @throws PGPException if the key cannot be generated + */ + public WithPrimaryKey addSigningSubkey(PGPKeyPair signingSubkey, + SignatureParameters.Callback bindingSignatureCallback, + SignatureParameters.Callback backSignatureCallback) + throws PGPException + { + if (!(signingSubkey.getPublicKey().getPublicKeyPacket() instanceof PublicSubkeyPacket)) + { + throw new IllegalArgumentException("Signing subkey MUST NOT consist of primary key packet."); + } + + if (!PublicKeyUtils.isSigningAlgorithm(signingSubkey.getPublicKey().getAlgorithm())) + { + throw new PGPException("Signing key MUST use signing-capable algorithm."); + } + + SignatureParameters parameters = Utils.applySignatureParameters(backSignatureCallback, + SignatureParameters.primaryKeyBinding(implementation.policy())); + + // Generate PrimaryKeySignature (Back-Signature) + final PGPSignature backSig = Utils.getBackSignature(signingSubkey, parameters, primaryKey.getPublicKey(), + implementation, configuration.keyCreationTime); + + signingSubkey = updateSubkey(signingSubkey, bindingSignatureCallback, new Utils.HashedSubpacketsOperation() + { + @Override + public void operate(PGPSignatureSubpacketGenerator hashedSubpackets) + throws PGPException + { + hashedSubpackets = signingSubkeySubpackets.apply(hashedSubpackets); + Utils.addEmbeddedSiganture(backSig, hashedSubpackets); + } + }); + + subkeys.add(signingSubkey); + + return this; + } + + + /** + * Build the {@link PGPSecretKeyRing OpenPGP key} without protecting the secret keys. + * + * @return OpenPGP key + * @throws PGPException if the key cannot be generated + */ + public OpenPGPKey build() + throws PGPException + { + return build(null); + } + + /** + * Build the {@link PGPSecretKeyRing OpenPGP key} using a single passphrase used to protect all subkeys. + * The passphrase will override whichever key protectors were specified in previous builder steps. + * + * @param passphrase nullable passphrase + * @return OpenPGP key + * @throws PGPException if the key cannot be generated + */ + public OpenPGPKey build(char[] passphrase) + throws PGPException + { + PBESecretKeyEncryptor primaryKeyEncryptor = configuration.keyEncryptorBuilderProvider + .build(passphrase, primaryKey.getPublicKey().getPublicKeyPacket()); + PGPSecretKey primarySecretKey = new PGPSecretKey( + primaryKey.getPrivateKey(), + primaryKey.getPublicKey(), + configuration.digestCalculatorProvider.get(HashAlgorithmTags.SHA1), + true, + primaryKeyEncryptor); + sanitizeKeyEncryptor(primaryKeyEncryptor); + List keys = new ArrayList(); + keys.add(primarySecretKey); + + for (Iterator it = subkeys.iterator(); it.hasNext(); ) + { + PGPKeyPair key = (PGPKeyPair)it.next(); + PBESecretKeyEncryptor subkeyEncryptor = configuration.keyEncryptorBuilderProvider + .build(passphrase, key.getPublicKey().getPublicKeyPacket()); + PGPSecretKey subkey = new PGPSecretKey( + key.getPrivateKey(), + key.getPublicKey(), + configuration.digestCalculatorProvider.get(HashAlgorithmTags.SHA1), + false, + subkeyEncryptor); + sanitizeKeyEncryptor(subkeyEncryptor); + keys.add(subkey); + } + + if (passphrase != null) + { + Arrays.fill(passphrase, (char)0); + } + + PGPSecretKeyRing secretKeys = new PGPSecretKeyRing(keys); + return new OpenPGPKey(secretKeys, implementation); + } + + protected void sanitizeKeyEncryptor(PBESecretKeyEncryptor keyEncryptor) + { + if (keyEncryptor == null) + { + // Unprotected is okay + return; + } + + S2K s2k = keyEncryptor.getS2K(); + if (s2k.getType() == S2K.SIMPLE || s2k.getType() == S2K.SALTED) + { + throw new IllegalArgumentException("S2K specifiers SIMPLE and SALTED are not allowed for secret key encryption."); + } + else if (s2k.getType() == S2K.ARGON_2) + { + if (keyEncryptor.getAeadAlgorithm() == 0) + { + throw new IllegalArgumentException("Argon2 MUST be used with AEAD."); + } + } + } + + private PGPKeyPair updateSubkey(PGPKeyPair subkey, SignatureParameters.Callback bindingSubpacketsCallback, + Utils.HashedSubpacketsOperation operation) + throws PGPException + { + SignatureParameters parameters = Utils.applySignatureParameters(bindingSubpacketsCallback, + SignatureParameters.subkeyBinding(implementation.policy()).setSignatureCreationTime(configuration.keyCreationTime)); + + if (parameters != null) + { + PGPSignatureGenerator bindingSigGen = Utils.getPgpSignatureGenerator(implementation, primaryKey.getPublicKey(), + primaryKey.getPrivateKey(), parameters, parameters.getSignatureCreationTime(), operation); + + PGPPublicKey publicSubkey = Utils.injectCertification(subkey.getPublicKey(), bindingSigGen, primaryKey.getPublicKey()); + subkey = new PGPKeyPair(publicSubkey, subkey.getPrivateKey()); + } + return subkey; + } + } + + /** + * Bundle implementation-specific provider classes. + */ + private static class Configuration + { + final Date keyCreationTime; + final PGPKeyPairGeneratorProvider kpGenProvider; + final PGPDigestCalculatorProvider digestCalculatorProvider; + final PBESecretKeyEncryptorFactory keyEncryptorBuilderProvider; + final KeyFingerPrintCalculator keyFingerprintCalculator; + + public Configuration(Date keyCreationTime, + PGPKeyPairGeneratorProvider keyPairGeneratorProvider, + PGPDigestCalculatorProvider digestCalculatorProvider, + PBESecretKeyEncryptorFactory keyEncryptorBuilderProvider, + KeyFingerPrintCalculator keyFingerPrintCalculator) + { + this.keyCreationTime = new Date((keyCreationTime.getTime() / 1000) * 1000); + this.kpGenProvider = keyPairGeneratorProvider; + this.digestCalculatorProvider = digestCalculatorProvider; + this.keyEncryptorBuilderProvider = keyEncryptorBuilderProvider; + this.keyFingerprintCalculator = keyFingerPrintCalculator; + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java new file mode 100644 index 0000000000..96ab4b10ee --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java @@ -0,0 +1,207 @@ +package org.bouncycastle.openpgp.api; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import org.bouncycastle.bcpg.KeyIdentifier; + +/** + * Implementation of the {@link OpenPGPKeyMaterialProvider} which caches items in a {@link HashMap}. + * It allows to provide key or certificates dynamically via a {@link #callback} that can be set using + * {@link #setMissingItemCallback(OpenPGPKeyMaterialProvider)}. + * Results from this callback are automatically cached for later access. This behavior can be adjusted via + * {@link #setCacheResultsFromCallback(boolean)}. + * + * @param {@link OpenPGPCertificate} or {@link OpenPGPKey} + */ +public abstract class OpenPGPKeyMaterialPool + implements OpenPGPKeyMaterialProvider +{ + private final Map pool = new HashMap<>(); + private OpenPGPKeyMaterialProvider callback = null; + private boolean cacheResultsFromCallback = true; + + /** + * Create an empty pool. + */ + public OpenPGPKeyMaterialPool() + { + + } + + /** + * Create a pool from the single provided item. + * @param item item + */ + public OpenPGPKeyMaterialPool(M item) + { + addItem(item); + } + + /** + * Create a pool and initialize its contents with the provided collection of items. + * @param items collection of keys or certificates + */ + public OpenPGPKeyMaterialPool(Collection items) + { + for (M item : items) + { + addItem(item); + } + } + + /** + * Set a callback that gets fired whenever an item is requested, which is not found in the pool. + * + * @param callback callback + * @return this + */ + public OpenPGPKeyMaterialPool setMissingItemCallback(OpenPGPKeyMaterialProvider callback) + { + this.callback = Objects.requireNonNull(callback); + return this; + } + + /** + * Decide, whether the implementation should add {@link OpenPGPCertificate certificates} returned by + * {@link #callback} to the pool of cached certificates. + * + * @param cacheResults if true, cache certificates from callback + * @return this + */ + public OpenPGPKeyMaterialPool setCacheResultsFromCallback(boolean cacheResults) + { + this.cacheResultsFromCallback = cacheResults; + return this; + } + + @Override + public M provide(KeyIdentifier componentKeyIdentifier) + { + M result = pool.get(componentKeyIdentifier); + if (result == null && callback != null) + { + // dynamically request certificate or key from callback + result = callback.provide(componentKeyIdentifier); + if (cacheResultsFromCallback) + { + addItem(result); + } + } + return result; + } + + /** + * Add a certificate to the pool. + * Note: If multiple items share the same subkey material, adding an item might overwrite the reference to + * another item for that subkey. + * + * @param item OpenPGP key or certificate that shall be added into the pool + * @return this + */ + public OpenPGPKeyMaterialPool addItem(M item) + { + if (item != null) + { + for (Iterator it = item.getAllKeyIdentifiers().iterator(); it.hasNext();) + { + pool.put(it.next(), item); + } + } + return this; + } + + /** + * Return all items from the pool. + * @return all items + */ + public Collection getAllItems() + { + return pool.values().stream() + .distinct() + .collect(Collectors.toList()); + } + + /** + * Implementation of {@link OpenPGPKeyMaterialPool} tailored to provide {@link OpenPGPKey OpenPGPKeys}. + */ + public static class OpenPGPKeyPool + extends OpenPGPKeyMaterialPool + implements OpenPGPKeyProvider + { + public OpenPGPKeyPool() + { + super(); + } + + public OpenPGPKeyPool(Collection items) + { + super(items); + } + + @Override + public OpenPGPKeyPool setMissingItemCallback(OpenPGPKeyMaterialProvider callback) + { + super.setMissingItemCallback(callback); + return this; + } + + @Override + public OpenPGPKeyPool setCacheResultsFromCallback(boolean cacheResults) + { + super.setCacheResultsFromCallback(cacheResults); + return this; + } + + @Override + public OpenPGPKeyPool addItem(OpenPGPKey item) + { + super.addItem(item); + return this; + } + } + + /** + * Implementation of {@link OpenPGPKeyMaterialPool} tailored to providing + * {@link OpenPGPCertificate OpenPGPCertificates}. + */ + public static class OpenPGPCertificatePool + extends OpenPGPKeyMaterialPool + implements OpenPGPCertificateProvider + { + public OpenPGPCertificatePool() + { + super(); + } + + public OpenPGPCertificatePool(Collection items) + { + super(items); + } + + @Override + public OpenPGPCertificatePool setMissingItemCallback(OpenPGPKeyMaterialProvider callback) + { + super.setMissingItemCallback(callback); + return this; + } + + @Override + public OpenPGPCertificatePool setCacheResultsFromCallback(boolean cacheResults) + { + super.setCacheResultsFromCallback(cacheResults); + return this; + } + + @Override + public OpenPGPCertificatePool addItem(OpenPGPCertificate item) + { + super.addItem(item); + return this; + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialProvider.java new file mode 100644 index 0000000000..61b0af8c73 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialProvider.java @@ -0,0 +1,40 @@ +package org.bouncycastle.openpgp.api; + +import org.bouncycastle.bcpg.KeyIdentifier; + +/** + * Interface for providing OpenPGP keys or certificates. + * + * @param either {@link OpenPGPCertificate} or {@link OpenPGPKey} + */ +public interface OpenPGPKeyMaterialProvider +{ + /** + * Provide the requested {@link OpenPGPCertificate} or {@link OpenPGPKey} containing the component key identified + * by the passed in {@link KeyIdentifier}. + * + * @param componentKeyIdentifier identifier of a component key (primary key or subkey) + * @return the OpenPGP certificate or key containing the identified component key + */ + M provide(KeyIdentifier componentKeyIdentifier); + + /** + * Interface for requesting {@link OpenPGPCertificate OpenPGPCertificates} by providing a {@link KeyIdentifier}. + * The {@link KeyIdentifier} can either be that of the certificates primary key, or of a subkey. + */ + interface OpenPGPCertificateProvider + extends OpenPGPKeyMaterialProvider + { + + } + + /** + * Interface for requesting {@link OpenPGPKey OpenPGPKeys} by providing a {@link KeyIdentifier}. + * The {@link KeyIdentifier} can either be that of the keys primary key, or of a subkey. + */ + interface OpenPGPKeyProvider + extends OpenPGPKeyMaterialProvider + { + + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyReader.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyReader.java new file mode 100644 index 0000000000..130b38abf8 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyReader.java @@ -0,0 +1,346 @@ +package org.bouncycastle.openpgp.api; + +import org.bouncycastle.bcpg.BCPGInputStream; +import org.bouncycastle.openpgp.PGPMarker; +import org.bouncycastle.openpgp.PGPObjectFactory; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPUtil; +import org.bouncycastle.util.io.Streams; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + +/** + * Reader for {@link OpenPGPKey OpenPGPKeys} or {@link OpenPGPCertificate OpenPGPCertificates}. + */ +public class OpenPGPKeyReader +{ + private final OpenPGPImplementation implementation; + private final OpenPGPPolicy policy; + + public OpenPGPKeyReader() + { + this(OpenPGPImplementation.getInstance()); + } + + public OpenPGPKeyReader(OpenPGPImplementation implementation) + { + this(implementation, implementation.policy()); + } + + public OpenPGPKeyReader(OpenPGPImplementation implementation, OpenPGPPolicy policy) + { + this.implementation = implementation; + this.policy = policy; + } + + /** + * Parse a single {@link OpenPGPCertificate} from an ASCII armored string. + * + * @param armored ASCII armored string + * @return parsed certificate + * @throws IOException if the parsed object is a secret key or if the cert cannot be parsed + */ + public OpenPGPCertificate parseCertificate(String armored) + throws IOException + { + OpenPGPCertificate certificate = parseCertificateOrKey(armored); + if (certificate instanceof OpenPGPKey) + { + throw new IOException("Could not parse OpenPGPCertificate: Is OpenPGPKey."); + } + return certificate; + } + + /** + * Parse a single {@link OpenPGPCertificate} from an {@link InputStream}. + * + * @param inputStream ASCII armored or binary input stream + * @return parsed certificate + * @throws IOException if the parsed object is a secret key or if the cert cannot be parsed + */ + public OpenPGPCertificate parseCertificate(InputStream inputStream) + throws IOException + { + OpenPGPCertificate certificate = parseCertificateOrKey(inputStream); + if (certificate instanceof OpenPGPKey) + { + throw new IOException("Could not parse OpenPGPCertificate: Is OpenPGPKey."); + } + return certificate; + } + + /** + * Parse a single {@link OpenPGPCertificate} from bytes. + * + * @param bytes ASCII armored or binary bytes + * @return parsed certificate + * @throws IOException if the parsed object is a secret key or if the cert cannot be parsed + */ + public OpenPGPCertificate parseCertificate(byte[] bytes) + throws IOException + { + OpenPGPCertificate certificate = parseCertificateOrKey(bytes); + if (certificate instanceof OpenPGPKey) + { + throw new IOException("Could not parse OpenPGPCertificate: Is OpenPGPKey."); + } + return certificate; + } + + /** + * Parse a single {@link OpenPGPCertificate} or {@link OpenPGPKey} from an ASCII armored string. + * + * @param armored ASCII armored string + * @return parsed certificate or key + * @throws IOException if the key or certificate cannot be parsed + */ + public OpenPGPCertificate parseCertificateOrKey(String armored) + throws IOException + { + return parseCertificateOrKey(armored.getBytes(StandardCharsets.UTF_8)); + } + + /** + * Parse a single {@link OpenPGPCertificate} or {@link OpenPGPKey} from an {@link InputStream}. + * + * @param inputStream input stream containing the ASCII armored or binary key or certificate + * @return parsed certificate or key + * @throws IOException if the key or certificate cannot be parsed + */ + public OpenPGPCertificate parseCertificateOrKey(InputStream inputStream) + throws IOException + { + return parseCertificateOrKey(Streams.readAll(inputStream)); + } + + /** + * Parse a single {@link OpenPGPCertificate} or {@link OpenPGPKey} from bytes. + * + * @param bytes ASCII armored or binary key or certificate + * @return parsed certificate or key + * @throws IOException if the key or certificate cannot be parsed + */ + public OpenPGPCertificate parseCertificateOrKey(byte[] bytes) + throws IOException + { + ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); + InputStream decoderStream = PGPUtil.getDecoderStream(bIn); + BCPGInputStream pIn = BCPGInputStream.wrap(decoderStream); + PGPObjectFactory objectFactory = implementation.pgpObjectFactory(pIn); + Object object = objectFactory.nextObject(); + + while (object instanceof PGPMarker) + { + object = objectFactory.nextObject(); + } + if (object instanceof PGPSecretKeyRing) + { + return new OpenPGPKey((PGPSecretKeyRing) object, implementation, policy); + } + else if (object instanceof PGPPublicKeyRing) + { + return new OpenPGPCertificate((PGPPublicKeyRing) object, implementation, policy); + } + else + { + throw new IOException("Neither a certificate, nor secret key."); + } + } + + /** + * Parse an {@link OpenPGPKey} from an ASCII armored string. + * + * @param armored ASCII armored string + * @return parsed key + * @throws IOException if the key cannot be parsed. + */ + public OpenPGPKey parseKey(String armored) + throws IOException + { + return parseKey(armored.getBytes(StandardCharsets.UTF_8)); + } + + /** + * Parse an {@link OpenPGPKey} from an {@link InputStream} + * + * @param inputStream containing the ASCII armored or binary key + * @return parsed key + * @throws IOException if the key cannot be parsed. + */ + public OpenPGPKey parseKey(InputStream inputStream) + throws IOException + { + return parseKey(Streams.readAll(inputStream)); + } + + /** + * Parse an {@link OpenPGPKey} from bytes. + * + * @param bytes ASCII armored or binary key + * @return parsed key + * @throws IOException if the key cannot be parsed. + */ + public OpenPGPKey parseKey(byte[] bytes) + throws IOException + { + ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); + InputStream decoderStream = PGPUtil.getDecoderStream(bIn); + BCPGInputStream pIn = BCPGInputStream.wrap(decoderStream); + PGPObjectFactory objectFactory = implementation.pgpObjectFactory(pIn); + + Object object = objectFactory.nextObject(); + while (object instanceof PGPMarker) + { + object = objectFactory.nextObject(); + } + if (!(object instanceof PGPSecretKeyRing)) + { + throw new IOException("Not a secret key."); + } + + PGPSecretKeyRing keyRing = (PGPSecretKeyRing) object; + return new OpenPGPKey(keyRing, implementation, policy); + } + + public List parseKeysOrCertificates(String armored) + throws IOException + { + return parseKeysOrCertificates(armored.getBytes(StandardCharsets.UTF_8)); + } + + public List parseKeysOrCertificates(InputStream inputStream) + throws IOException + { + return parseKeysOrCertificates(Streams.readAll(inputStream)); + } + + public List parseKeysOrCertificates(byte[] bytes) + throws IOException + { + List certsOrKeys = new ArrayList<>(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); + InputStream decoderStream = PGPUtil.getDecoderStream(bIn); + // Call getDecoderStream() twice, to make sure the stream is a BufferedInputStreamExt. + // This is necessary, so that for streams containing multiple concatenated armored blocks of keys, + // we parse all of them and do not quit after reading the first one. + decoderStream = PGPUtil.getDecoderStream(decoderStream); + PGPObjectFactory objectFactory = implementation.pgpObjectFactory(decoderStream); + Object object; + + while ((object = objectFactory.nextObject()) != null) + { + if (object instanceof PGPMarker) + { + continue; + } + if (object instanceof PGPSecretKeyRing) + { + certsOrKeys.add(new OpenPGPKey((PGPSecretKeyRing) object, implementation, policy)); + } + else if (object instanceof PGPPublicKeyRing) + { + certsOrKeys.add(new OpenPGPCertificate((PGPPublicKeyRing) object, implementation, policy)); + } + else + { + throw new IOException("Neither a certificate, nor secret key."); + } + } + return certsOrKeys; + } + + public List parseCertificates(String armored) + throws IOException + { + return parseCertificates(armored.getBytes(StandardCharsets.UTF_8)); + } + + public List parseCertificates(InputStream inputStream) + throws IOException + { + return parseCertificates(Streams.readAll(inputStream)); + } + + public List parseCertificates(byte[] bytes) + throws IOException + { + List certs = new ArrayList<>(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); + InputStream decoderStream = PGPUtil.getDecoderStream(bIn); + // Call getDecoderStream() twice, to make sure the stream is a BufferedInputStreamExt. + // This is necessary, so that for streams containing multiple concatenated armored blocks of certs, + // we parse all of them and do not quit after reading the first one. + decoderStream = PGPUtil.getDecoderStream(decoderStream); + PGPObjectFactory objectFactory = implementation.pgpObjectFactory(decoderStream); + Object object; + + while ((object = objectFactory.nextObject()) != null) + { + if (object instanceof PGPMarker) + { + continue; + } + else if (object instanceof PGPPublicKeyRing) + { + certs.add(new OpenPGPCertificate((PGPPublicKeyRing) object, implementation, policy)); + } + else + { + throw new IOException("Encountered unexpected packet: " + object.getClass().getName()); + } + } + return certs; + } + + public List parseKeys(String armored) + throws IOException + { + return parseKeys(armored.getBytes(StandardCharsets.UTF_8)); + } + + public List parseKeys(InputStream inputStream) + throws IOException + { + return parseKeys(Streams.readAll(inputStream)); + } + + public List parseKeys(byte[] bytes) + throws IOException + { + List keys = new ArrayList<>(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); + InputStream decoderStream = PGPUtil.getDecoderStream(bIn); + // Call getDecoderStream() twice, to make sure the stream is a BufferedInputStreamExt. + // This is necessary, so that for streams containing multiple concatenated armored blocks of keys, + // we parse all of them and do not quit after reading the first one. + decoderStream = PGPUtil.getDecoderStream(decoderStream); + PGPObjectFactory objectFactory = implementation.pgpObjectFactory(decoderStream); + Object object; + + while ((object = objectFactory.nextObject()) != null) + { + if (object instanceof PGPMarker) + { + continue; + } + else if (object instanceof PGPSecretKeyRing) + { + keys.add(new OpenPGPKey((PGPSecretKeyRing) object, implementation, policy)); + } + else + { + throw new IOException("Encountered unexpected packet: " + object.getClass().getName()); + } + } + return keys; + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java new file mode 100644 index 0000000000..8f1e249482 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java @@ -0,0 +1,650 @@ +package org.bouncycastle.openpgp.api; + +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import org.bouncycastle.bcpg.AEADAlgorithmTags; +import org.bouncycastle.bcpg.ArmoredOutputStream; +import org.bouncycastle.bcpg.CompressionAlgorithmTags; +import org.bouncycastle.bcpg.S2K; +import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.openpgp.PGPCompressedDataGenerator; +import org.bouncycastle.openpgp.PGPEncryptedDataGenerator; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPLiteralData; +import org.bouncycastle.openpgp.PGPLiteralDataGenerator; +import org.bouncycastle.openpgp.PGPOnePassSignature; +import org.bouncycastle.openpgp.PGPPadding; +import org.bouncycastle.openpgp.PGPSignatureGenerator; +import org.bouncycastle.openpgp.api.exception.InvalidEncryptionKeyException; +import org.bouncycastle.openpgp.operator.PBEKeyEncryptionMethodGenerator; +import org.bouncycastle.openpgp.operator.PGPDataEncryptorBuilder; +import org.bouncycastle.openpgp.operator.PublicKeyKeyEncryptionMethodGenerator; + +/** + * Generator for OpenPGP messages. + * This class can generate armored/unarmored, encrypted and/or signed OpenPGP message artifacts. + * By default, the generator will merely pack plaintext into an armored + * {@link org.bouncycastle.bcpg.LiteralDataPacket}. + * If however, the user provides one or more recipient certificates/keys + * ({@link #addEncryptionCertificate(OpenPGPCertificate)} / + * {@link #addEncryptionCertificate(OpenPGPCertificate.OpenPGPComponentKey)}) + * or message passphrases {@link #addEncryptionPassphrase(char[])}, the message will be encrypted. + * The encryption mechanism is automatically decided, based on the provided recipient certificates, aiming to maximize + * interoperability. + * If the user provides one or more signing keys by calling {@link #addSigningKey(OpenPGPKey)} or + * {@link #addSigningKey(OpenPGPKey.OpenPGPSecretKey, KeyPassphraseProvider, SignatureParameters.Callback)}, + * the message will be signed. + */ +public class OpenPGPMessageGenerator + extends AbstractOpenPGPDocumentSignatureGenerator +{ + public static final int BUFFER_SIZE = 1024; + + private boolean isArmored = true; + public boolean isAllowPadding = true; + private final List encryptionKeys = new ArrayList<>(); + private final List messagePassphrases = new ArrayList<>(); + + // Literal Data metadata + private Date fileModificationDate = null; + private String filename = null; + private char format = PGPLiteralData.BINARY; + private PGPEncryptedDataGenerator.SessionKeyExtractionCallback sessionKeyExtractionCallback; + + public OpenPGPMessageGenerator() + { + this(OpenPGPImplementation.getInstance()); + } + + public OpenPGPMessageGenerator(OpenPGPImplementation implementation) + { + this(implementation, implementation.policy()); + } + + public OpenPGPMessageGenerator(OpenPGPImplementation implementation, OpenPGPPolicy policy) + { + super(implementation, policy); + } + + /** + * Add a recipients certificate to the set of encryption keys. + * Subkeys will be selected using the default {@link SubkeySelector}, which can be replaced by calling + * {@link #setEncryptionKeySelector(SubkeySelector)}. + * The recipient will be able to decrypt the message using their corresponding secret key. + * + * @param recipientCertificate recipient certificate (public key) + * @return this + */ + public OpenPGPMessageGenerator addEncryptionCertificate(OpenPGPCertificate recipientCertificate) + throws InvalidEncryptionKeyException + { + return addEncryptionCertificate(recipientCertificate, encryptionKeySelector); + } + + /** + * Add a recipients certificate to the set of encryption keys. + * Subkeys will be selected using the provided {@link SubkeySelector}. + * The recipient will be able to decrypt the message using their corresponding secret key. + * + * @param recipientCertificate recipient certificate (public key) + * @param subkeySelector selector for encryption subkeys + * @return this + * @throws InvalidEncryptionKeyException if the certificate is not capable of encryption + */ + public OpenPGPMessageGenerator addEncryptionCertificate(OpenPGPCertificate recipientCertificate, + SubkeySelector subkeySelector) + throws InvalidEncryptionKeyException + { + List subkeys = + subkeySelector.select(recipientCertificate, policy); + if (subkeys.isEmpty()) + { + throw new InvalidEncryptionKeyException(recipientCertificate); + } + this.encryptionKeys.addAll(subkeys); + return this; + } + + /** + * Add a (sub-)key to the set of recipient encryption keys. + * The recipient will be able to decrypt the message using their corresponding secret key. + * + * @param encryptionKey encryption capable subkey + * @return this + * @throws InvalidEncryptionKeyException if the key is not capable of encryption + */ + public OpenPGPMessageGenerator addEncryptionCertificate(OpenPGPCertificate.OpenPGPComponentKey encryptionKey) + throws InvalidEncryptionKeyException + { + if (!encryptionKey.isEncryptionKey()) + { + throw new InvalidEncryptionKeyException(encryptionKey); + } + encryptionKeys.add(encryptionKey); + return this; + } + + /** + * Add a message passphrase. + * In addition to optional public key encryption, the message will be decryptable using the given passphrase. + * + * @param passphrase passphrase + * @return this + */ + public OpenPGPMessageGenerator addEncryptionPassphrase(char[] passphrase) + { + messagePassphrases.add(passphrase); + return this; + } + + /** + * Specify, whether the output OpenPGP message will be ASCII armored or not. + * + * @param armored boolean + * @return this + */ + public OpenPGPMessageGenerator setArmored(boolean armored) + { + this.isArmored = armored; + return this; + } + + public OpenPGPMessageGenerator setAllowPadding(boolean allowPadding) + { + this.isAllowPadding = allowPadding; + return this; + } + + /** + * Set metadata (filename, modification date, binary format) from a file. + * + * @param file file + * @return this + */ + public OpenPGPMessageGenerator setFileMetadata(File file) + { + this.filename = file.getName(); + this.fileModificationDate = new Date(file.lastModified()); + this.format = PGPLiteralData.BINARY; + return this; + } + + /** + * Set a callback which fires once the session key for message encryption is known. + * This callback can be used to extract the session key, e.g. to emit it to the user (in case of SOP). + * + * @param callback callback + * @return this + */ + public OpenPGPMessageGenerator setSessionKeyExtractionCallback( + PGPEncryptedDataGenerator.SessionKeyExtractionCallback callback) + { + this.sessionKeyExtractionCallback = callback; + return this; + } + + /** + * Open an {@link OpenPGPMessageOutputStream} over the given output stream. + * + * @param out output stream + * @return OpenPGP message output stream + * @throws PGPException if the output stream cannot be created + */ + public OpenPGPMessageOutputStream open(OutputStream out) + throws PGPException, IOException + { + OpenPGPMessageOutputStream.Builder streamBuilder = OpenPGPMessageOutputStream.builder(); + + applyOptionalAsciiArmor(streamBuilder); + applyOptionalEncryption(streamBuilder, sessionKeyExtractionCallback); + applySignatures(streamBuilder); + applyOptionalCompression(streamBuilder); + applyLiteralDataWrap(streamBuilder); + + return streamBuilder.build(out); + } + + /** + * Apply ASCII armor if necessary. + * The output will only be wrapped in ASCII armor, if {@link #setArmored(boolean)} is set + * to true (is true by default). + * The {@link ArmoredOutputStream} will be instantiated using the {@link ArmoredOutputStreamFactory} + * which can be replaced using {@link #setArmorStreamFactory(ArmoredOutputStreamFactory)}. + * + * @param builder OpenPGP message output stream builder + */ + private void applyOptionalAsciiArmor(OpenPGPMessageOutputStream.Builder builder) + { + if (isArmored) + { + builder.armor(armorStreamFactory); + } + } + + /** + * Optionally apply message encryption. + * If no recipient certificates and no encryption passphrases were supplied, no encryption + * will be applied. + * Otherwise, encryption mode and algorithms will be negotiated and message encryption will be applied. + * + * @param builder OpenPGP message output stream builder + * @param sessionKeyExtractionCallback callback to extract the session key (nullable) + */ + private void applyOptionalEncryption( + OpenPGPMessageOutputStream.Builder builder, + PGPEncryptedDataGenerator.SessionKeyExtractionCallback sessionKeyExtractionCallback) + { + MessageEncryptionMechanism encryption = encryptionNegotiator.negotiateEncryption(this); + if (!encryption.isEncrypted()) + { + return; // No encryption + } + + PGPDataEncryptorBuilder encBuilder = implementation.pgpDataEncryptorBuilder( + encryption.getSymmetricKeyAlgorithm()); + + // Specify container type for the plaintext + switch (encryption.getMode()) + { + case SEIPDv1: + encBuilder.setWithIntegrityPacket(true); + break; + + case SEIPDv2: + encBuilder.setWithAEAD(encryption.getAeadAlgorithm(), 6); + encBuilder.setUseV6AEAD(); + break; + + case LIBREPGP_OED: + encBuilder.setWithAEAD(encryption.getAeadAlgorithm(), 6); + encBuilder.setUseV5AEAD(); + break; + } + + final PGPEncryptedDataGenerator encGen = new PGPEncryptedDataGenerator(encBuilder); + // For sake of interoperability and simplicity, we always use a dedicated session key for message encryption + // even if only a single PBE encryption method was added and S2K result could be used as session-key directly. + encGen.setForceSessionKey(true); + encGen.setSessionKeyExtractionCallback(sessionKeyExtractionCallback); + + // Setup asymmetric message encryption + for (OpenPGPCertificate.OpenPGPComponentKey encryptionSubkey : encryptionKeys) + { + PublicKeyKeyEncryptionMethodGenerator method = implementation.publicKeyKeyEncryptionMethodGenerator( + encryptionSubkey.getPGPPublicKey()); + encGen.addMethod(method); + } + + // Setup symmetric (password-based) message encryption + for (char[] passphrase : messagePassphrases) + { + PBEKeyEncryptionMethodGenerator skeskGen; + switch (encryption.getMode()) + { + case SEIPDv1: + case LIBREPGP_OED: + // "v4" and LibrePGP use symmetric-key encrypted session key packets version 4 (SKESKv4) + skeskGen = implementation.pbeKeyEncryptionMethodGenerator(passphrase); + break; + + case SEIPDv2: + // v6 uses symmetric-key encrypted session key packets version 6 (SKESKv6) using AEAD + skeskGen = implementation.pbeKeyEncryptionMethodGenerator( + passphrase, S2K.Argon2Params.memoryConstrainedParameters()); + break; + default: + continue; + } + + skeskGen.setSecureRandom(CryptoServicesRegistrar.getSecureRandom()); // Prevent NPE + encGen.addMethod(skeskGen); + } + + // Finally apply encryption + builder.encrypt(new OpenPGPMessageOutputStream.OutputStreamFactory() + { + @Override + public OutputStream get(OutputStream o) + throws PGPException, IOException + { + try + { + return encGen.open(o, new byte[BUFFER_SIZE]); + } + catch (IOException e) + { + throw new PGPException("Could not open encryptor OutputStream", e); + } + } + }); + + // Optionally, append a padding packet as the last packet inside the SEIPDv2 packet. + if (encryption.getMode() == EncryptedDataPacketType.SEIPDv2 && isAllowPadding) + { + builder.padding(new OpenPGPMessageOutputStream.OutputStreamFactory() + { + @Override + public OutputStream get(OutputStream o) + { + return new OpenPGPMessageOutputStream.PaddingPacketAppenderOutputStream(o, new OpenPGPMessageOutputStream.PaddingPacketFactory() + { + @Override + public PGPPadding providePaddingPacket() + { + return new PGPPadding(); + } + }); + } + }); + } + } + + /** + * Apply OpenPGP inline-signatures. + * + * @param builder OpenPGP message output stream builder + */ + private void applySignatures(OpenPGPMessageOutputStream.Builder builder) + { + builder.sign(new OpenPGPMessageOutputStream.OutputStreamFactory() + { + @Override + public OutputStream get(OutputStream o) + throws PGPException, IOException + { + addSignToGenerator(); + + // One-Pass-Signatures + Iterator sigGens = signatureGenerators.iterator(); + while (sigGens.hasNext()) + { + PGPSignatureGenerator gen = sigGens.next(); + PGPOnePassSignature ops = gen.generateOnePassVersion(sigGens.hasNext()); + ops.encode(o); + } + + return new OpenPGPMessageOutputStream.SignatureGeneratorOutputStream(o, signatureGenerators); + } + }); + } + + private void applyOptionalCompression(OpenPGPMessageOutputStream.Builder builder) + { + int compressionAlgorithm = compressionNegotiator.negotiateCompression(this, policy); + if (compressionAlgorithm == CompressionAlgorithmTags.UNCOMPRESSED) + { + return; // Uncompressed + } + + final PGPCompressedDataGenerator compGen = new PGPCompressedDataGenerator(compressionAlgorithm); + + builder.compress(new OpenPGPMessageOutputStream.OutputStreamFactory() + { + @Override + public OutputStream get(OutputStream o) + throws PGPException, IOException + { + try + { + return compGen.open(o, new byte[BUFFER_SIZE]); + } + catch (IOException e) + { + throw new PGPException("Could not apply compression", e); + } + } + }); + } + + /** + * Setup wrapping of the message plaintext in a literal data packet. + * + * @param builder OpenPGP message output stream + */ + private void applyLiteralDataWrap(OpenPGPMessageOutputStream.Builder builder) + { + PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator(); + builder.literalData(new OpenPGPMessageOutputStream.OutputStreamFactory() + { + @Override + public OutputStream get(OutputStream o) + throws PGPException, IOException + { + try + { + return litGen.open(o, + format, + filename != null ? filename : "", + fileModificationDate != null ? fileModificationDate : PGPLiteralData.NOW, + new byte[BUFFER_SIZE]); + } + catch (IOException e) + { + throw new PGPException("Could not apply literal data wrapping", e); + } + } + }); + } + + // Factory for creating ASCII armor + private ArmoredOutputStreamFactory armorStreamFactory = + new ArmoredOutputStreamFactory() + { + @Override + public ArmoredOutputStream get(OutputStream outputStream) + { + return ArmoredOutputStream.builder() + .clearHeaders() // Hide version + .enableCRC(false) // Disable CRC sum + .build(outputStream); + } + }; + + private SubkeySelector encryptionKeySelector = new SubkeySelector() + { + @Override + public List select(OpenPGPCertificate certificate, + OpenPGPPolicy policy) + { + List result = new ArrayList<>(); + for (Iterator it = certificate.getEncryptionKeys().iterator(); it.hasNext(); ) + { + OpenPGPCertificate.OpenPGPComponentKey key = it.next(); + if (policy.isAcceptablePublicKey(key.getPGPPublicKey())) + { + result.add(key); + } + } + return result; + } + }; + + // Encryption method negotiator for when only password-based encryption is requested + private OpenPGPEncryptionNegotiator passwordBasedEncryptionNegotiator = new OpenPGPEncryptionNegotiator() + { + @Override + public MessageEncryptionMechanism negotiateEncryption(OpenPGPMessageGenerator configuration) + { + return MessageEncryptionMechanism.aead(SymmetricKeyAlgorithmTags.AES_256, AEADAlgorithmTags.OCB); + } + }; + + // Encryption method negotiator for when public-key encryption is requested + private OpenPGPEncryptionNegotiator publicKeyBasedEncryptionNegotiator = new OpenPGPEncryptionNegotiator() + { + @Override + public MessageEncryptionMechanism negotiateEncryption(OpenPGPMessageGenerator configuration) + { +// List certificates = encryptionKeys.stream() +// .map(OpenPGPCertificate.OpenPGPCertificateComponent::getCertificate) +// .distinct() +// .collect(Collectors.toList()); + + List certificates = new ArrayList<>(); + Set uniqueCertificates = new HashSet<>(); // For distinctness + + for (Iterator it = encryptionKeys.iterator(); it.hasNext(); ) + { + OpenPGPCertificate cert = it.next().getCertificate(); + if (uniqueCertificates.add(cert)) + { // `Set.add()` returns true if the element was new + certificates.add(cert); + } + } + + // Decide, if SEIPDv2 (OpenPGP v6-style AEAD) is supported by all recipients. + if (OpenPGPEncryptionNegotiator.allRecipientsSupportSeipd2(certificates)) + { + PreferredAEADCiphersuites commonDenominator = + OpenPGPEncryptionNegotiator.negotiateAEADCiphersuite(certificates, policy); + return MessageEncryptionMechanism.aead(commonDenominator.getAlgorithms()[0]); + } + else if (OpenPGPEncryptionNegotiator.allRecipientsSupportLibrePGPOED(certificates)) + { + return MessageEncryptionMechanism.librePgp( + OpenPGPEncryptionNegotiator.bestOEDEncryptionModeByWeight(certificates, policy)); + } + else + { + return MessageEncryptionMechanism.integrityProtected( + OpenPGPEncryptionNegotiator.bestSymmetricKeyAlgorithmByWeight( + certificates, policy)); + } + + } + }; + + // Primary encryption method negotiator + private final OpenPGPEncryptionNegotiator encryptionNegotiator = new OpenPGPEncryptionNegotiator() + { + @Override + public MessageEncryptionMechanism negotiateEncryption(OpenPGPMessageGenerator configuration) + { + // No encryption methods provided -> Unencrypted message + if (encryptionKeys.isEmpty() && messagePassphrases.isEmpty()) + { + return MessageEncryptionMechanism.unencrypted(); + } + + // No public-key encryption requested -> password-based encryption + else if (encryptionKeys.isEmpty()) + { + // delegate negotiation to pbe negotiator + return passwordBasedEncryptionNegotiator.negotiateEncryption(configuration); + } + else + { + // delegate negotiation to pkbe negotiator + return publicKeyBasedEncryptionNegotiator.negotiateEncryption(configuration); + } + } + }; + + + // TODO: Implement properly, taking encryption into account (sign-only should not compress) + private CompressionNegotiator compressionNegotiator = new CompressionNegotiator() + { + @Override + public int negotiateCompression(OpenPGPMessageGenerator configuration, OpenPGPPolicy policy) + { + return CompressionAlgorithmTags.UNCOMPRESSED; + } + }; + + /** + * Replace the default {@link OpenPGPEncryptionNegotiator} that gets to decide, which + * {@link MessageEncryptionMechanism} mode to use if only password-based encryption is used. + * + * @param pbeNegotiator custom PBE negotiator. + * @return this + */ + public OpenPGPMessageGenerator setPasswordBasedEncryptionNegotiator(OpenPGPEncryptionNegotiator pbeNegotiator) + { + this.passwordBasedEncryptionNegotiator = Objects.requireNonNull(pbeNegotiator); + return this; + } + + /** + * Replace the default {@link OpenPGPEncryptionNegotiator} that decides, which + * {@link MessageEncryptionMechanism} mode to use if public-key encryption is used. + * + * @param pkbeNegotiator custom encryption negotiator that gets to decide if PK-based encryption is used + * @return this + */ + public OpenPGPMessageGenerator setPublicKeyBasedEncryptionNegotiator(OpenPGPEncryptionNegotiator pkbeNegotiator) + { + this.publicKeyBasedEncryptionNegotiator = Objects.requireNonNull(pkbeNegotiator); + return this; + } + + /** + * Replace the default encryption key selector with a custom implementation. + * The encryption key selector is responsible for selecting one or more encryption subkeys from a + * recipient certificate. + * + * @param encryptionKeySelector selector for encryption (sub-)keys + * @return this + */ + public OpenPGPMessageGenerator setEncryptionKeySelector(SubkeySelector encryptionKeySelector) + { + this.encryptionKeySelector = Objects.requireNonNull(encryptionKeySelector); + return this; + } + + + /** + * Replace the default {@link CompressionNegotiator} with a custom implementation. + * The {@link CompressionNegotiator} is used to negotiate, whether and how to compress the literal data packet. + * + * @param compressionNegotiator negotiator + * @return this + */ + public OpenPGPMessageGenerator setCompressionNegotiator(CompressionNegotiator compressionNegotiator) + { + this.compressionNegotiator = Objects.requireNonNull(compressionNegotiator); + return this; + } + + /** + * Replace the {@link ArmoredOutputStreamFactory} with a custom implementation. + * + * @param factory factory for {@link ArmoredOutputStream} instances + * @return this + */ + public OpenPGPMessageGenerator setArmorStreamFactory(ArmoredOutputStreamFactory factory) + { + this.armorStreamFactory = Objects.requireNonNull(factory); + return this; + } + + + public interface ArmoredOutputStreamFactory + extends OpenPGPMessageOutputStream.OutputStreamFactory + { + ArmoredOutputStream get(OutputStream out); + } + + public interface CompressionNegotiator + { + /** + * Negotiate a compression algorithm. + * Returning {@link org.bouncycastle.bcpg.CompressionAlgorithmTags#UNCOMPRESSED} will result in no compression. + * + * @param messageGenerator message generator + * @return negotiated compression algorithm ID + */ + int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPGPPolicy policy); + } + +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageInputStream.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageInputStream.java new file mode 100644 index 0000000000..bd714cb32e --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageInputStream.java @@ -0,0 +1,1146 @@ +package org.bouncycastle.openpgp.api; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.bouncycastle.bcpg.AEADEncDataPacket; +import org.bouncycastle.bcpg.BCPGInputStream; +import org.bouncycastle.bcpg.KeyIdentifier; +import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket; +import org.bouncycastle.openpgp.PGPCompressedData; +import org.bouncycastle.openpgp.PGPEncryptedDataList; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPLiteralData; +import org.bouncycastle.openpgp.PGPMarker; +import org.bouncycastle.openpgp.PGPObjectFactory; +import org.bouncycastle.openpgp.PGPOnePassSignature; +import org.bouncycastle.openpgp.PGPOnePassSignatureList; +import org.bouncycastle.openpgp.PGPPadding; +import org.bouncycastle.openpgp.PGPSessionKey; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureException; +import org.bouncycastle.openpgp.PGPSignatureList; + +/** + * An {@link InputStream} that processes an OpenPGP message. + * Its contents are the plaintext from the messages LiteralData packet. + * You can get information about the message (signatures, encryption method, message metadata) + * by reading ALL data from the stream, closing it with {@link #close()} and then retrieving a {@link Result} object + * by calling {@link #getResult()}. + */ +public class OpenPGPMessageInputStream + extends InputStream +{ + public static int MAX_RECURSION = 16; + + private final PGPObjectFactory objectFactory; + private final OpenPGPImplementation implementation; + + private final OpenPGPMessageProcessor processor; + + private final Result.Builder resultBuilder; + private final Layer layer; // the packet layer processed by this input stream + + private InputStream in; + private final List packetHandlers = new ArrayList() + {{ + add(new SignatureListHandler()); + add(new OnePassSignatureHandler()); + add(new MarkerHandler()); + add(new LiteralDataHandler()); + add(new CompressedDataHandler()); + add(new EncryptedDataHandler()); + add(new DefaultPacketHandler()); // Must be last + }}; + + private final List closeHandlers = new ArrayList() + {{ + add(new SignatureListHandler()); + add(new PaddingHandler()); + add(new MarkerHandler()); + add(new DefaultPacketHandler()); + }}; + + OpenPGPMessageInputStream(PGPObjectFactory objectFactory, + OpenPGPMessageProcessor processor) + { + this(objectFactory, processor, Result.builder()); + } + + private OpenPGPMessageInputStream(PGPObjectFactory objectFactory, + OpenPGPMessageProcessor processor, + Result.Builder resultBuilder) + { + this.objectFactory = objectFactory; + this.processor = processor; + this.implementation = processor.getImplementation(); + this.resultBuilder = resultBuilder; + try + { + this.layer = resultBuilder.openLayer(); + } + catch (PGPException e) + { + // cannot happen + throw new AssertionError(e); + } + } + + void process() + throws IOException, PGPException + { + Object next; + while ((next = objectFactory.nextObject()) != null) + { + for (PacketHandler handler : packetHandlers) + { + if (handler.canHandle(next)) + { + handler.handle(next); + break; + } + } + + if (in != null) + { + return; // Found data stream, stop processing + } + } + } + + @Override + public void close() + throws IOException + { + in.close(); + Object next; + + while ((next = objectFactory.nextObject()) != null) + { + boolean handled = false; + for (PacketHandler handler : closeHandlers) + { + if (handler.canHandle(next)) + { + handler.close(next); + handled = true; + break; + } + } + + if (!handled) + { + processor.onException(new PGPException("Unexpected trailing packet encountered: " + + next.getClass().getName())); + } + } + + resultBuilder.verifySignatures(processor); + resultBuilder.closeLayer(); + } + + @Override + public int read() + throws IOException + { + int i = in.read(); + if (i >= 0) + { + layer.onePassSignatures.update(i); + layer.prefixedSignatures.update(i); +// byte b = (byte)i; +// layer.onePassVerifier.update(b); +// layer.prefixedVerifier.update(b); + } + return i; + } + + @Override + public int read(byte[] b) + throws IOException + { + int i = in.read(b); + if (i >= 0) + { + layer.onePassSignatures.update(b, 0, i); + layer.prefixedSignatures.update(b, 0, i); +// layer.onePassVerifier.update(b, 0, i); +// layer.prefixedVerifier.update(b, 0, i); + } + return i; + } + + @Override + public int read(byte[] b, int off, int len) + throws IOException + { + int bytesRead = in.read(b, off, len); + if (bytesRead > 0) + { + layer.onePassSignatures.update(b, off, bytesRead); + layer.prefixedSignatures.update(b, off, bytesRead); +// layer.onePassVerifier.update(b, off, bytesRead); +// layer.prefixedVerifier.update(b, off, bytesRead); + } + return bytesRead; + } + + public Result getResult() + { + return resultBuilder.build(); + } + + public static class Result + { + private final List documentSignatures = new ArrayList<>(); + private OpenPGPCertificate.OpenPGPComponentKey decryptionKey; + private char[] decryptionPassphrase; + private PGPSessionKey sessionKey; + private MessageEncryptionMechanism encryptionMethod = MessageEncryptionMechanism.unencrypted(); + private int compressionAlgorithm = 0; + private String filename; + private char fileFormat; + private Date fileModificationTime; + + private Result(List layers) + { + for (Iterator it = layers.iterator(); it.hasNext(); ) + { + Layer l = it.next(); + if (l.signatures != null) + { + documentSignatures.addAll(l.signatures); + } + + if (l.nested instanceof EncryptedData) + { + EncryptedData encryptedData = (EncryptedData)l.nested; + encryptionMethod = encryptedData.encryption; + sessionKey = encryptedData.sessionKey; + decryptionKey = encryptedData.decryptionKey; + decryptionPassphrase = encryptedData.decryptionPassphrase; + } + else if (l.nested instanceof CompressedData) + { + CompressedData compressedData = (CompressedData)l.nested; + compressionAlgorithm = compressedData.compressionAlgorithm; + } + else if (l.nested instanceof LiteralData) + { + LiteralData literalData = (LiteralData)l.nested; + filename = literalData.filename; + fileFormat = literalData.encoding; + fileModificationTime = literalData.modificationTime; + } + } + } + + static Builder builder() + { + return new Builder(); + } + + public MessageEncryptionMechanism getEncryptionMethod() + { + return encryptionMethod; + } + + public OpenPGPCertificate.OpenPGPComponentKey getDecryptionKey() + { + return decryptionKey; + } + + public char[] getDecryptionPassphrase() + { + return decryptionPassphrase; + } + + public PGPSessionKey getSessionKey() + { + return sessionKey; + } + + public int getCompressionAlgorithm() + { + return compressionAlgorithm; + } + + public String getFilename() + { + return filename; + } + + public char getFileFormat() + { + return fileFormat; + } + + public Date getFileModificationTime() + { + return fileModificationTime; + } + + public List getSignatures() + { + return new ArrayList<>(documentSignatures); + } + + static class Builder + { + private final List layers = new ArrayList<>(); + + private Builder() + { + + } + + Layer last() + { + return layers.get(layers.size() - 1); + } + + /** + * Enter a nested OpenPGP packet layer. + * + * @return the new layer + * @throws PGPException if the parser exceeded the maximum nesting depth ({@link #MAX_RECURSION}). + */ + Layer openLayer() + throws PGPException + { + if (layers.size() >= MAX_RECURSION) + { + throw new PGPException("Exceeded maximum packet nesting depth."); + } + Layer layer = new Layer(); + layers.add(layer); + return layer; + } + + /** + * Close a nested OpenPGP packet layer. + */ + void closeLayer() + { + for (int i = layers.size() - 1; i >= 0; i--) + { + Layer l = layers.get(i); + if (l.isOpen()) + { + l.close(); + return; + } + } + } + + /** + * Set the nested packet type of the current layer to {@link CompressedData}. + * + * @param compressionAlgorithm compression algorithm ID + */ + void compressed(int compressionAlgorithm) + { + last().setNested(new CompressedData(compressionAlgorithm)); + } + + /** + * Add One-Pass-Signature packets on the current layer. + * + * @param pgpOnePassSignatures one pass signature packets + */ + void onePassSignatures(PGPOnePassSignatureList pgpOnePassSignatures) + { +// last().onePassVerifier.addSignatures(pgpOnePassSignatures.iterator()); + last().onePassSignatures.addOnePassSignatures(pgpOnePassSignatures); + } + + /** + * Build the {@link Result}. + * + * @return result + */ + Result build() + { + return new Result(layers); + } + + /** + * Add prefixed signatures on the current layer. + * + * @param prefixedSigs prefixed signatures + */ + void prefixedSignatures(PGPSignatureList prefixedSigs) + { + last().prefixedSignatures.addAll(prefixedSigs); + //last().prefixedVerifier.addSignatures(prefixedSigs.iterator()); + } + + /** + * Initialize any signatures on the current layer, prefixed and one-pass-signatures. + * + * @param processor message processor + */ + void initSignatures(OpenPGPMessageProcessor processor) + { + last().onePassSignatures.init(processor); + last().prefixedSignatures.init(processor); +// last().onePassVerifier.commonInit(processor); +// last().prefixedVerifier.commonInit(processor); + } + + /** + * Verify all signatures on the current layer, prefixed and one-pass-signatures. + * + * @param processor message processor + */ + void verifySignatures(OpenPGPMessageProcessor processor) + { + Layer last = last(); + if (last.signatures != null) + { + return; + } + + last.signatures = new ArrayList<>(); + last.signatures.addAll(last.onePassSignatures.verify(processor)); + last.signatures.addAll(last.prefixedSignatures.verify(processor)); +// last.signatures.addAll(last.onePassVerifier.verify(processor)); +// last.signatures.addAll(last.prefixedVerifier.verify(processor)); + } + + /** + * Set literal data metadata on the current layer. + * + * @param fileName filename + * @param format data format + * @param modificationTime modification time + */ + void literalData(String fileName, char format, Date modificationTime) + { + last().setNested(new LiteralData(fileName, format, modificationTime)); + } + + /** + * Set metadata from an encrypted data packet on the current layer. + * + * @param decrypted decryption result + */ + void encrypted(OpenPGPMessageProcessor.Decrypted decrypted) + { + last().setNested(new EncryptedData(decrypted)); + } + } + } + + static class Layer + { + private final OnePassSignatures onePassSignatures = new OnePassSignatures(); + private final PrefixedSignatures prefixedSignatures = new PrefixedSignatures(); +// private final OnePassSignatureVerifier onePassVerifier = new OnePassSignatureVerifier(); +// private final PrefixedSignatureVerifier prefixedVerifier = new PrefixedSignatureVerifier(); + private List signatures = null; + + private Nested nested; + private boolean open = true; + + void setNested(Nested nested) + { + this.nested = nested; + } + + void close() + { + this.open = false; + } + + boolean isOpen() + { + return open; + } + } + + static class Nested + { + } + + static class CompressedData + extends Nested + { + private final int compressionAlgorithm; + + public CompressedData(int algorithm) + { + this.compressionAlgorithm = algorithm; + } + } + + static class LiteralData + extends Nested + { + private final String filename; + private final char encoding; + private final Date modificationTime; + + LiteralData(String filename, char encoding, Date modificationTime) + { + this.filename = filename; + this.encoding = encoding; + this.modificationTime = modificationTime; + } + } + + static class EncryptedData + extends Nested + { + private final OpenPGPCertificate.OpenPGPComponentKey decryptionKey; + private final char[] decryptionPassphrase; + private final PGPSessionKey sessionKey; + private final MessageEncryptionMechanism encryption; + + EncryptedData(OpenPGPMessageProcessor.Decrypted decrypted) + { + this.decryptionKey = decrypted.decryptionKey; + this.decryptionPassphrase = decrypted.decryptionPassphrase; + this.sessionKey = decrypted.sessionKey; + if (decrypted.esk.getEncData() instanceof SymmetricEncIntegrityPacket) + { + SymmetricEncIntegrityPacket seipd = (SymmetricEncIntegrityPacket)decrypted.esk.getEncData(); + if (seipd.getVersion() == SymmetricEncIntegrityPacket.VERSION_2) + { + encryption = MessageEncryptionMechanism.aead( + seipd.getCipherAlgorithm(), seipd.getAeadAlgorithm()); + } + else + { + encryption = MessageEncryptionMechanism.integrityProtected(sessionKey.getAlgorithm()); + } + } + else if (decrypted.esk.getEncData() instanceof AEADEncDataPacket) + { + encryption = MessageEncryptionMechanism.librePgp(sessionKey.getAlgorithm()); + } + else + { + throw new RuntimeException("Unexpected encrypted data packet type: " + decrypted.esk.getClass().getName()); + } + } + } + + private static class PacketHandler + { + public boolean canHandle(Object packet) + { + return false; + } + + public void handle(Object packet) + throws IOException, PGPException + { + + } + + public void close(Object packet) + throws IOException + { + + } + } + + private class SignatureListHandler + extends PacketHandler + { + public boolean canHandle(Object packet) + { + return packet instanceof PGPSignatureList; + } + + public void handle(Object packet) + { + PGPSignatureList prefixedSigs = (PGPSignatureList)packet; + resultBuilder.prefixedSignatures(prefixedSigs); + } + + public void close(Object packet) + { + PGPSignatureList sigList = (PGPSignatureList)packet; + resultBuilder.last().onePassSignatures.addSignatures(sigList); + //resultBuilder.last().onePassVerifier.addPGPSinatures(sigList.iterator()); + } + } + + private class LiteralDataHandler + extends PacketHandler + { + @Override + public boolean canHandle(Object packet) + { + return packet instanceof PGPLiteralData; + } + + @Override + public void handle(Object packet) + throws IOException, PGPException + { + PGPLiteralData literalData = (PGPLiteralData)packet; + resultBuilder.literalData( + literalData.getFileName(), + (char)literalData.getFormat(), + literalData.getModificationTime() + ); + in = literalData.getDataStream(); + resultBuilder.initSignatures(processor); + } + } + + private class OnePassSignatureHandler + extends PacketHandler + { + @Override + public boolean canHandle(Object packet) + { + return packet instanceof PGPOnePassSignatureList; + } + + @Override + public void handle(Object packet) + throws IOException, PGPException + { + PGPOnePassSignatureList pgpOnePassSignatures = (PGPOnePassSignatureList)packet; + resultBuilder.onePassSignatures(pgpOnePassSignatures); + } + } + + private static class MarkerHandler + extends PacketHandler + { + @Override + public boolean canHandle(Object packet) + { + return packet instanceof PGPMarker; + } + } + + private class CompressedDataHandler + extends PacketHandler + { + @Override + public boolean canHandle(Object packet) + { + return packet instanceof PGPCompressedData; + } + + @Override + public void handle(Object packet) + throws IOException, PGPException + { + PGPCompressedData compressedData = (PGPCompressedData)packet; + resultBuilder.compressed(compressedData.getAlgorithm()); + + InputStream decompressed = compressedData.getDataStream(); + processNestedStream(decompressed); + } + + private void processNestedStream(InputStream input) + throws IOException, PGPException + { + InputStream decodeIn = BCPGInputStream.wrap(input); + PGPObjectFactory decFac = implementation.pgpObjectFactory(decodeIn); + OpenPGPMessageInputStream nestedIn = + new OpenPGPMessageInputStream(decFac, processor, resultBuilder); + in = nestedIn; + nestedIn.process(); + } + } + + private class EncryptedDataHandler + extends PacketHandler + { + @Override + public boolean canHandle(Object packet) + { + return packet instanceof PGPEncryptedDataList; + } + + @Override + public void handle(Object packet) + throws IOException, PGPException + { + PGPEncryptedDataList encryptedDataList = (PGPEncryptedDataList)packet; + OpenPGPMessageProcessor.Decrypted decrypted = processor.decrypt(encryptedDataList); + + resultBuilder.encrypted(decrypted); + processNestedStream(decrypted.inputStream); + } + + private void processNestedStream(InputStream input) + throws IOException, PGPException + { + InputStream decodeIn = BCPGInputStream.wrap(input); + PGPObjectFactory decFac = implementation.pgpObjectFactory(decodeIn); + OpenPGPMessageInputStream nestedIn = + new OpenPGPMessageInputStream(decFac, processor, resultBuilder); + in = nestedIn; + nestedIn.process(); + } + } + + private static class PaddingHandler + extends PacketHandler + { + public boolean canHandle(Object packet) + { + return packet instanceof PGPPadding; + } + } + + private class DefaultPacketHandler + extends PacketHandler + { + @Override + public boolean canHandle(Object packet) + { + return true; // Catch-all handler + } + + @Override + public void handle(Object packet) + throws PGPException + { + processor.onException(new PGPException("Unexpected packet: " + packet.getClass().getName())); + } + } + + static class OnePassSignatures + { + private final List onePassSignatures = new ArrayList<>(); + private final List signatures = new ArrayList<>(); + private final Map issuers = new HashMap<>(); + + OnePassSignatures() + { + + } + + void addOnePassSignatures(PGPOnePassSignatureList onePassSignatures) + { + for (PGPOnePassSignature ops : onePassSignatures) + { + this.onePassSignatures.add(ops); + } + } + + void addSignatures(PGPSignatureList signatures) + { + for (PGPSignature signature : signatures) + { + this.signatures.add(signature); + } + } + + void init(OpenPGPMessageProcessor processor) + { + + for (PGPOnePassSignature ops : onePassSignatures) + { + KeyIdentifier identifier = ops.getKeyIdentifier(); + OpenPGPCertificate cert = processor.provideCertificate(identifier); + if (cert == null) + { + continue; + } + + try + { + OpenPGPCertificate.OpenPGPComponentKey key = cert.getKey(identifier); + issuers.put(ops, key); + ops.init(processor.getImplementation().pgpContentVerifierBuilderProvider(), + key.getPGPPublicKey()); + } + catch (PGPException e) + { + processor.onException(e); + } + } + } + + void update(int i) + { + for (PGPOnePassSignature onePassSignature : onePassSignatures) + { + if (issuers.containsKey(onePassSignature)) + { + onePassSignature.update((byte) i); + } + } + } + + void update(byte[] b, int off, int len) + { + for (PGPOnePassSignature onePassSignature : onePassSignatures) + { + if (issuers.containsKey(onePassSignature)) + { + onePassSignature.update(b, off, len); + } + } + } + + List verify( + OpenPGPMessageProcessor processor) + { + OpenPGPPolicy policy = processor.getImplementation().policy(); + List dataSignatures = new ArrayList<>(); + int num = onePassSignatures.size(); + for (int i = 0; i < signatures.size(); i++) + { + PGPSignature signature = signatures.get(i); + PGPOnePassSignature ops = onePassSignatures.get(num - i - 1); + OpenPGPCertificate.OpenPGPComponentKey key = issuers.get(ops); + if (key == null) + { + continue; + } + + OpenPGPSignature.OpenPGPDocumentSignature dataSignature = + new OpenPGPSignature.OpenPGPDocumentSignature(signature, key); + try + { + dataSignature.sanitize(key, policy); + } + catch (PGPSignatureException e) + { + // continue + } + + if (!dataSignature.createdInBounds(processor.getVerifyNotBefore(), processor.getVerifyNotAfter())) + { + // sig is not in bounds + continue; + } + + try + { + dataSignature.verify(ops); + } + catch (PGPException e) + { + processor.onException(e); + } + dataSignatures.add(dataSignature); + } + return dataSignatures; + } + } + + static class PrefixedSignatures + { + private final List prefixedSignatures = new ArrayList<>(); + private final List dataSignatures = new ArrayList<>(); + + PrefixedSignatures() + { + + } + + void addAll(PGPSignatureList signatures) + { + for (PGPSignature signature : signatures) + { + this.prefixedSignatures.add(signature); + } + } + + void init(OpenPGPMessageProcessor processor) + { + for (PGPSignature sig : prefixedSignatures) + { + KeyIdentifier identifier = OpenPGPSignature.getMostExpressiveIdentifier(sig.getKeyIdentifiers()); + if (identifier == null) + { + dataSignatures.add(new OpenPGPSignature.OpenPGPDocumentSignature(sig, null)); + continue; + } + OpenPGPCertificate cert = processor.provideCertificate(identifier); + if (cert == null) + { + dataSignatures.add(new OpenPGPSignature.OpenPGPDocumentSignature(sig, null)); + continue; + } + + OpenPGPCertificate.OpenPGPComponentKey key = cert.getKey(identifier); + OpenPGPSignature.OpenPGPDocumentSignature signature = new OpenPGPSignature.OpenPGPDocumentSignature(sig, key); + dataSignatures.add(signature); + try + { + signature.signature.init( + processor.getImplementation().pgpContentVerifierBuilderProvider(), + cert.getKey(identifier).getPGPPublicKey()); + } + catch (PGPException e) + { + processor.onException(e); + } + } + } + + void update(int i) + { + for(PGPSignature signature : prefixedSignatures) + { + signature.update((byte) i); + } + } + + void update(byte[] buf, int off, int len) + { + for (PGPSignature signature : prefixedSignatures) + { + signature.update(buf, off, len); + } + } + + List verify(OpenPGPMessageProcessor processor) + { + List verifiedSignatures = new ArrayList<>(); + OpenPGPPolicy policy = processor.getImplementation().policy(); + for (OpenPGPSignature.OpenPGPDocumentSignature sig : dataSignatures) + { + try + { + sig.sanitize(sig.issuer, policy); + } + catch (PGPSignatureException e) + { + processor.onException(e); + continue; + } + + try + { + sig.verify(); + } + catch (PGPException e) + { + processor.onException(e); + } + verifiedSignatures.add(sig); + } + return verifiedSignatures; + } + } + +// private static abstract class BaseSignatureVerifier +// { +// protected final List signatures = new ArrayList<>(); +// protected final Map issuers = new HashMap<>(); +// +// public void addSignatures(Iterator it) +// { +// while (it.hasNext()) +// { +// this.signatures.add(it.next()); +// } +// } +// +// protected void commonInit(OpenPGPMessageProcessor processor) +// throws PGPException +// { +// for (R sig : signatures) +// { +// KeyIdentifier identifier = getKeyIdentifier(sig); +// OpenPGPCertificate cert = processor.provideCertificate(identifier); +// +// if (cert != null) +// { +// OpenPGPCertificate.OpenPGPComponentKey key = cert.getKey(identifier); +// issuers.put(sig, key); +// initSignature(sig, key, processor); +// } +// } +// } +// +// public abstract void update(byte i); +// +// public abstract void update(byte[] buf, int off, int len); +// +// public List verify( +// OpenPGPMessageProcessor processor) +// { +// List results = new ArrayList<>(); +// OpenPGPPolicy policy = processor.getImplementation().policy(); +// +// for (R sig : signatures) +// { +// KeyIdentifier keyId = getKeyIdentifier(sig); +// OpenPGPCertificate.OpenPGPComponentKey key = issuers.get(keyId); +// +// OpenPGPSignature.OpenPGPDocumentSignature docSig = +// new OpenPGPSignature.OpenPGPDocumentSignature((PGPSignature)sig, key); +// +// try +// { +// if (key != null) +// { +// docSig.verify(); +// } +// docSig.sanitize(key, policy); +// } +// catch (PGPException e) +// { +// processor.onException(e); +// } +// +// results.add(docSig); +// } +// return results; +// } +// +// protected abstract KeyIdentifier getKeyIdentifier(R sig); +// +// protected abstract void initSignature(R sig, +// OpenPGPCertificate.OpenPGPComponentKey key, +// OpenPGPMessageProcessor processor) +// throws PGPException; +// } +// +// private static class OnePassSignatureVerifier +// extends BaseSignatureVerifier +// { +// private final List pgpSignatureList = new ArrayList<>(); +// +// public void addPGPSinatures(Iterator it) +// { +// while (it.hasNext()) +// { +// pgpSignatureList.add(it.next()); +// } +// } +// +// @Override +// protected KeyIdentifier getKeyIdentifier(PGPOnePassSignature sig) +// { +// // One-pass signatures directly include their key ID +// return sig.getKeyIdentifier(); +// } +// +// @Override +// protected void initSignature(PGPOnePassSignature sig, +// OpenPGPCertificate.OpenPGPComponentKey key, +// OpenPGPMessageProcessor processor) +// throws PGPException +// { +// // Initialize for one-pass signature verification +// sig.init(processor.getImplementation().pgpContentVerifierBuilderProvider(), +// key.getPGPPublicKey() +// ); +// } +// +// public void update(byte i) +// { +// for (PGPOnePassSignature sig : signatures) +// { +// sig.update(i); +// } +// } +// +// public void update(byte[] buf, int off, int len) +// { +// for (PGPOnePassSignature sig : signatures) +// { +// sig.update(buf, off, len); +// } +// } +// +// public List verify( +// OpenPGPMessageProcessor processor) +// { +// OpenPGPPolicy policy = processor.getImplementation().policy(); +// List dataSignatures = new ArrayList<>(); +// int num = signatures.size(); +// for (int i = 0; i < signatures.size(); i++) +// { +// PGPSignature signature = pgpSignatureList.get(i); +// PGPOnePassSignature ops = signatures.get(num - i - 1); +// OpenPGPCertificate.OpenPGPComponentKey key = issuers.get(ops); +// if (key == null) +// { +// continue; +// } +// +// OpenPGPSignature.OpenPGPDocumentSignature dataSignature = +// new OpenPGPSignature.OpenPGPDocumentSignature(signature, key); +// try +// { +// dataSignature.sanitize(key, policy); +// } +// catch (PGPSignatureException e) +// { +// // continue +// } +// +// if (!dataSignature.createdInBounds(processor.getVerifyNotBefore(), processor.getVerifyNotAfter())) +// { +// // sig is not in bounds +// continue; +// } +// +// try +// { +// dataSignature.verify(ops); +// } +// catch (PGPException e) +// { +// processor.onException(e); +// } +// dataSignatures.add(dataSignature); +// } +// return dataSignatures; +// } +// } +// +// private static class PrefixedSignatureVerifier +// extends BaseSignatureVerifier +// { +// @Override +// protected KeyIdentifier getKeyIdentifier(PGPSignature sig) +// { +// // Prefixed signatures may have multiple key identifiers +// return OpenPGPSignature.getMostExpressiveIdentifier(sig.getKeyIdentifiers()); +// } +// +// @Override +// protected void initSignature(PGPSignature sig, +// OpenPGPCertificate.OpenPGPComponentKey key, +// OpenPGPMessageProcessor processor) +// throws PGPException +// { +// // Initialize for prefixed signature verification +// sig.init( +// processor.getImplementation().pgpContentVerifierBuilderProvider(), +// key.getPGPPublicKey() +// ); +// } +// +// public void update(byte i) +// { +// for (PGPSignature sig : signatures) +// { +// sig.update(i); +// } +// } +// +// public void update(byte[] buf, int off, int len) +// { +// for (PGPSignature sig : signatures) +// { +// sig.update(buf, off, len); +// } +// } +// } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageOutputStream.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageOutputStream.java new file mode 100644 index 0000000000..b5ba7ae790 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageOutputStream.java @@ -0,0 +1,471 @@ +package org.bouncycastle.openpgp.api; + +import org.bouncycastle.bcpg.BCPGOutputStream; +import org.bouncycastle.bcpg.PacketFormat; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPPadding; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureGenerator; +import org.bouncycastle.util.io.TeeOutputStream; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; +import java.util.Stack; + +/** + * Implementation of an {@link OutputStream} tailored to creating OpenPGP messages. + * Since not all OpenPGP-related OutputStreams forward {@link #close()} calls, we need to keep track of nested streams + * and close them in order. + * This stream can create OpenPGP messages following the following EBNF (which is a subset of the EBNF defined in RFC9580): + *
      + *
    • OpenPGP Message := ASCII-Armor(Optionally Encrypted Message) | Optionally Encrypted Message
    • + *
    • Literal Message := LiteralDataPacket
    • + *
    • Optionally Compressed Message := Literal Message | + * CompressedDataPacket(Literal Message)
    • + *
    • Optionally Signed Message := Optionally Compressed Message | + * OnePassSignaturePacket + Optionally Signed Message + SignaturePacket
    • + *
    • Optionally Padded Message := Optionally Signed Message | Optionally Signed Message + PaddingPacket
    • + *
    • Encrypted Message := SEIPDv1(Optionally Padded Message) | + * SEIPDv2(Optionally Padded Message) | + * OED(Optionally Padded Message)
    • + *
    • Optionally Encrypted Message := Optionally Padded Message | Encrypted Message
    • + *
    + * Therefore, this stream is capable of creating a wide variety of OpenPGP message, from simply + * LiteralDataPacket-wrapped plaintext over signed messages to encrypted, signed and padded messages. + * The following considerations lead to why this particular subset was chosen: + *
      + *
    • An encrypted message is not distinguishable from randomness, so it makes no sense to compress it
    • + *
    • Since signatures also consist of data which is not distinguishable from randomness, + * it makes no sense to compress them either
    • + *
    • Padding packets are used to prevent traffic analysis. + * Since they contain random data, we do not compress them. + * If a message is encrypted, we want to encrypt the padding packet to prevent an intermediate from stripping it
    • + *
    • Since (v4) signatures leak some metadata about the message plaintext (the hash and the issuer), + * for encrypted messages we always place signatures inside the encryption container (sign-then-encrypt)
    • + *
    • Prefix-signed messages (where a SignaturePacket is prefixed to an OpenPGP message) are + * inferior to One-Pass-Signed messages, so this stream cannot produce those.
    • + *
    • Messages using the Cleartext-Signature Framework are "different enough" to deserve their own + * message generator / stream.
    • + *
    + */ +public class OpenPGPMessageOutputStream + extends OutputStream +{ + // sink for the OpenPGP message + private final OutputStream baseOut; // non-null + + private final OutputStream armorOut; // nullable + private final OutputStream encodeOut; // non-null + private final OutputStream encryptOut; // nullable + private final OutputStream paddingOut; // nullable + private final OutputStream signOut; // nullable + private final OutputStream compressOut; // nullable + private final OutputStream literalOut; // non-null + + // pipe plaintext data into this stream + private final OutputStream plaintextOut; // non-null + + OpenPGPMessageOutputStream(OutputStream baseOut, Builder builder) + throws PGPException, IOException + { + this.baseOut = baseOut; + OutputStream innermostOut = baseOut; + + // ASCII ARMOR + if (builder.armorFactory != null) + { + armorOut = builder.armorFactory.get(innermostOut); + innermostOut = armorOut; + } + else + { + armorOut = null; + } + + // BCPG (decide packet length encoding format) + encodeOut = new BCPGOutputStream(innermostOut, PacketFormat.CURRENT); + innermostOut = encodeOut; + + // ENCRYPT + if (builder.encryptionStreamFactory != null) + { + encryptOut = builder.encryptionStreamFactory.get(innermostOut); + innermostOut = encryptOut; + } + else + { + encryptOut = null; + } + + // PADDING + if (builder.paddingStreamFactory != null) + { + paddingOut = builder.paddingStreamFactory.get(innermostOut); + innermostOut = paddingOut; + } + else + { + paddingOut = null; + } + + // SIGN + if (builder.signatureStreamFactory != null) + { + signOut = builder.signatureStreamFactory.get(innermostOut); + // signOut does not forward write() calls down, so we do *not* set innermostOut to it + } + else + { + signOut = null; + } + + // COMPRESS + if (builder.compressionStreamFactory != null) + { + compressOut = builder.compressionStreamFactory.get(innermostOut); + innermostOut = compressOut; + } + else + { + compressOut = null; + } + + // LITERAL DATA + if (builder.literalDataStreamFactory == null) + { + throw new PGPException("Missing instructions for LiteralData encoding."); + } + literalOut = builder.literalDataStreamFactory.get(innermostOut); + + if (signOut != null) + { + this.plaintextOut = new TeeOutputStream(literalOut, signOut); + } + else + { + this.plaintextOut = literalOut; + } + } + + @Override + public void write(int i) + throws IOException + { + plaintextOut.write(i); + } + + @Override + public void write(byte[] b) + throws IOException + { + write(b, 0, b.length); + } + + @Override + public void write(byte[] b, int off, int len) + throws IOException + { + plaintextOut.write(b, off, len); + } + + @Override + public void flush() + throws IOException + { + literalOut.flush(); + if (compressOut != null) + { + compressOut.flush(); + } + if (signOut != null) + { + signOut.flush(); + } + if (paddingOut != null) + { + paddingOut.flush(); + } + if (encryptOut != null) + { + encryptOut.flush(); + } + encodeOut.flush(); + if (armorOut != null) + { + armorOut.flush(); + } + baseOut.flush(); + } + + @Override + public void close() + throws IOException + { + literalOut.close(); + if (compressOut != null) + { + compressOut.close(); + } + if (signOut != null) + { + signOut.close(); + } + if (paddingOut != null) + { + paddingOut.close(); + } + if (encryptOut != null) + { + encryptOut.close(); + } + encodeOut.close(); + if (armorOut != null) + { + armorOut.close(); + } + baseOut.close(); + } + + /** + * Factory class for wrapping output streams. + */ + public interface OutputStreamFactory + { + /** + * Wrap the given base stream with another {@link OutputStream} and return the result. + * @param base base output stream + * @return wrapped output stream + * @throws PGPException if the wrapping stream cannot be instantiated + */ + OutputStream get(OutputStream base) throws PGPException, IOException; + } + + static Builder builder() + { + return new Builder(); + } + + /** + * Builder class for {@link OpenPGPMessageOutputStream} instances. + */ + static class Builder + { + private OpenPGPMessageGenerator.ArmoredOutputStreamFactory armorFactory; + private OutputStreamFactory paddingStreamFactory; + private OutputStreamFactory encryptionStreamFactory; + private OutputStreamFactory signatureStreamFactory; + private OutputStreamFactory compressionStreamFactory; + private OutputStreamFactory literalDataStreamFactory; + + /** + * Specify a factory for ASCII armoring the message. + * + * @param factory armor stream factory + * @return this + */ + public Builder armor(OpenPGPMessageGenerator.ArmoredOutputStreamFactory factory) + { + this.armorFactory = factory; + return this; + } + + /** + * Specify a factory for encrypting the message. + * + * @param factory encryption stream factory + * @return this + */ + public Builder encrypt(OutputStreamFactory factory) + { + this.encryptionStreamFactory = factory; + return this; + } + + /** + * Specify a factory for padding the message. + * + * @param factory padding stream factory + * @return this + */ + public Builder padding(OutputStreamFactory factory) + { + this.paddingStreamFactory = factory; + return this; + } + + /** + * Specify a factory for signing the message. + * + * @param factory signing stream factory + * @return this + */ + public Builder sign(OutputStreamFactory factory) + { + this.signatureStreamFactory = factory; + return this; + } + + /** + * Specify a factory for compressing the message. + * ' + * @param factory compression stream factory + * @return this + */ + public Builder compress(OutputStreamFactory factory) + { + this.compressionStreamFactory = factory; + return this; + } + + /** + * Specify a factory for literal-data-wrapping the message. + * + * @param factory literal data stream factory + * @return this + */ + public Builder literalData(OutputStreamFactory factory) + { + this.literalDataStreamFactory = factory; + return this; + } + + /** + * Construct the {@link OpenPGPMessageOutputStream} over the base output stream. + * + * @param baseOut base output stream + * @return OpenPGP message output stream + * @throws PGPException if a stream cannot be created + * @throws IOException if a signature cannot be generated + */ + public OpenPGPMessageOutputStream build(OutputStream baseOut) + throws PGPException, IOException + { + return new OpenPGPMessageOutputStream(baseOut, this); + } + } + + /** + * OutputStream which updates {@link PGPSignatureGenerator} instances with data that is written to it. + * Note: Data written to this stream will *NOT* be forwarded to the underlying {@link OutputStream}. + * Once {@link #close()} is called, it will generate {@link PGPSignature} objects from the generators and write + * those to the underlying {@link OutputStream}. + */ + static class SignatureGeneratorOutputStream + extends OutputStream + { + + private final OutputStream out; + private final List signatureGenerators; + + public SignatureGeneratorOutputStream(OutputStream out, List signatureGenerators) + { + this.out = out; + this.signatureGenerators = signatureGenerators; + } + + @Override + public void write(int i) + throws IOException + { + for (PGPSignatureGenerator sigGen : signatureGenerators) + { + sigGen.update((byte)i); + } + } + + @Override + public void write(byte[] b) + throws IOException + { + for (PGPSignatureGenerator sigGen : signatureGenerators) + { + sigGen.update(b); + } + } + + @Override + public void write(byte[] b, int off, int len) + throws IOException + { + for (PGPSignatureGenerator sigGen : signatureGenerators) + { + sigGen.update(b, off, len); + } + } + + @Override + public void close() + throws IOException + { + for (int i = signatureGenerators.size() - 1; i >= 0; i--) + { + PGPSignatureGenerator gen = signatureGenerators.get(i); + PGPSignature sig = null; + try + { + sig = gen.generate(); + } + catch (PGPException e) + { + throw new RuntimeException(e); + } + sig.encode(out); + } + } + } + + /** + * OutputStream which appends a {@link org.bouncycastle.bcpg.PaddingPacket} to the data + * once {@link #close()} is called. + */ + static class PaddingPacketAppenderOutputStream + extends OutputStream + { + private final OutputStream out; + private final PaddingPacketFactory packetFactory; + + public PaddingPacketAppenderOutputStream(OutputStream out, PaddingPacketFactory packetFactory) + { + this.out = out; + this.packetFactory = packetFactory; + } + + @Override + public void write(byte[] b) + throws IOException + { + out.write(b); + } + + @Override + public void write(byte[] b, int off, int len) + throws IOException + { + out.write(b, off, len); + } + + @Override + public void write(int i) + throws IOException + { + out.write(i); + } + + @Override + public void close() + throws IOException + { + packetFactory.providePaddingPacket().encode(out); + out.close(); + } + } + + /** + * Factory interface for creating PGPPadding objects. + */ + public interface PaddingPacketFactory + { + PGPPadding providePaddingPacket(); + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageProcessor.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageProcessor.java new file mode 100644 index 0000000000..ee8a4f9792 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageProcessor.java @@ -0,0 +1,543 @@ +package org.bouncycastle.openpgp.api; + +import org.bouncycastle.bcpg.KeyIdentifier; +import org.bouncycastle.openpgp.PGPEncryptedData; +import org.bouncycastle.openpgp.PGPEncryptedDataList; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyPair; +import org.bouncycastle.openpgp.PGPObjectFactory; +import org.bouncycastle.openpgp.PGPPBEEncryptedData; +import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData; +import org.bouncycastle.openpgp.PGPSessionKey; +import org.bouncycastle.openpgp.PGPSessionKeyEncryptedData; +import org.bouncycastle.openpgp.PGPUtil; +import org.bouncycastle.openpgp.IntegrityProtectedInputStream; +import org.bouncycastle.openpgp.api.exception.KeyPassphraseException; +import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.SessionKeyDataDecryptorFactory; +import org.bouncycastle.util.Arrays; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class OpenPGPMessageProcessor +{ + private final OpenPGPImplementation implementation; + private final Configuration configuration; + + /** + * Create a new {@link OpenPGPMessageProcessor} using the default {@link OpenPGPImplementation}. + */ + public OpenPGPMessageProcessor() + { + this(OpenPGPImplementation.getInstance()); + } + + /** + * Create a new {@link OpenPGPMessageProcessor} using the given {@link OpenPGPImplementation}. + * + * @param implementation openpgp implementation + */ + public OpenPGPMessageProcessor(OpenPGPImplementation implementation) + { + this(implementation, implementation.policy()); + } + + public OpenPGPMessageProcessor(OpenPGPImplementation implementation, OpenPGPPolicy policy) + { + this.implementation = implementation; + this.configuration = new Configuration(policy); + } + + /** + * Add an {@link OpenPGPCertificate} for signature verification. + * If the message contains any signatures, the provided certificate will be considered as a candidate to verify + * the signature. + * + * @param issuerCertificate OpenPGP certificate + * @return this + */ + public OpenPGPMessageProcessor addVerificationCertificate(OpenPGPCertificate issuerCertificate) + { + configuration.certificatePool.addItem(issuerCertificate); + return this; + } + + public OpenPGPMessageProcessor verifyNotAfter(Date date) + { + configuration.verifyNotAfter = date; + return this; + } + + public OpenPGPMessageProcessor verifyNotBefore(Date date) + { + configuration.verifyNotBefore = date; + return this; + } + + /** + * Add an {@link OpenPGPKey} as potential decryption key. + * If the message is encrypted for an {@link OpenPGPKey}, this key can be tried to decrypt the message. + * Keys added via this method will also be available for message decryption if the message was encrypted + * to an anonymous recipient (wildcard key-id / fingerprint). + * + * @param key OpenPGP key + * @return this + */ + public OpenPGPMessageProcessor addDecryptionKey(OpenPGPKey key) + { + configuration.keyPool.addItem(key); + return this; + } + + /** + * Add an {@link OpenPGPKey} as potential decryption key, along with a {@link KeyPassphraseProvider} dedicated + * to this key. + * If the message is encrypted for an {@link OpenPGPKey}, this key can be tried to decrypt the message. + * Keys added via this method will also be available for message decryption if the message was encrypted + * to an anonymous recipient (wildcard key-id / fingerprint). + * + * @param key OpenPGP key + * @return this + */ + public OpenPGPMessageProcessor addDecryptionKey(OpenPGPKey key, char[] passphrase) + { + configuration.keyPool.addItem(key); + configuration.keyPassphraseProvider.addPassphrase(key, passphrase); + return this; + } + + /** + * Add a passphrase for secret key decryption. + * If the corresponding {@link OpenPGPKey} which key this passphrase is for is known in advance, + * it is highly advised to call {@link #addDecryptionKey(OpenPGPKey, char[])} instead, due to performance reasons. + * + * @param passphrase key-passphrase + * @return this + */ + public OpenPGPMessageProcessor addDecryptionKeyPassphrase(char[] passphrase) + { + configuration.keyPassphraseProvider.addPassphrase(passphrase); + return this; + } + + /** + * Set a provider for dynamically requesting missing passphrases used to unlock encrypted + * {@link OpenPGPKey OpenPGPKeys}. + * This provider is called, if a key cannot be unlocked using any passphrase provided via + * {@link #addDecryptionKey(OpenPGPKey, char[])}. + * + * @param keyPassphraseProvider key passphrase provider + * @return this + */ + public OpenPGPMessageProcessor setMissingOpenPGPKeyPassphraseProvider( + KeyPassphraseProvider keyPassphraseProvider) + { + this.configuration.keyPassphraseProvider.setMissingPassphraseCallback(keyPassphraseProvider); + return this; + } + + /** + * Set a {@link OpenPGPKeyMaterialProvider.OpenPGPCertificateProvider} to allow dynamic requesting certificates + * for signature verification. + * This provider is called if the requested {@link OpenPGPCertificate} has not yet been added explicitly + * via {@link #addVerificationCertificate(OpenPGPCertificate)}. + * This allows lazily requesting verification certificates at runtime. + * + * @param certificateProvider provider for OpenPGP certificates + * @return this + */ + public OpenPGPMessageProcessor setMissingOpenPGPCertificateProvider( + OpenPGPKeyMaterialProvider.OpenPGPCertificateProvider certificateProvider) + { + configuration.certificatePool.setMissingItemCallback(certificateProvider); + return this; + } + + /** + * Set a provider for {@link OpenPGPKey OpenPGPKeys}, which can be used to decrypt encrypted messages. + * This provider is called if an {@link OpenPGPKey} required to decrypt the message has not yet been + * explicitly added via {@link #addDecryptionKey(OpenPGPKey)}. + * This allows lazily requesting decryption keys at runtime. + * + * @param keyProvider provider for OpenPGP keys + * @return this + */ + public OpenPGPMessageProcessor setMissingOpenPGPKeyProvider( + OpenPGPKeyMaterialProvider.OpenPGPKeyProvider keyProvider) + { + configuration.keyPool.setMissingItemCallback(keyProvider); + return this; + } + + /** + * Set a passphrase to decrypt a symmetrically encrypted OpenPGP message. + * + * @param messagePassphrase passphrase for message decryption + * @return this + */ + public OpenPGPMessageProcessor addMessagePassphrase(char[] messagePassphrase) + { + this.configuration.addMessagePassphrase(messagePassphrase); + return this; + } + + /** + * Set a {@link MissingMessagePassphraseCallback} which will be invoked if the message is encrypted using a passphrase, + * but no working passphrase was provided. + * + * @param callback callback + * @return this + */ + public OpenPGPMessageProcessor setMissingMessagePassphraseCallback( + MissingMessagePassphraseCallback callback) + { + this.configuration.missingMessagePassphraseCallback = callback; + return this; + } + + /** + * Set a {@link PGPSessionKey} with which an encrypted OpenPGP message can be decrypted without the need for + * using a private key or passphrase. + * Typically, this method can be used, if the {@link PGPSessionKey} of a message is already known (e.g. because + * the message has already been decrypted before). + * The benefit of this is, that public-key operations can be costly. + * + * @param sessionKey session key + * @return this + */ + public OpenPGPMessageProcessor setSessionKey(PGPSessionKey sessionKey) + { + configuration.sessionKey = sessionKey; + return this; + } + + /** + * Process an OpenPGP message. + * + * @param messageIn input stream of the OpenPGP message + * @return plaintext input stream + * @throws IOException + * @throws PGPException + */ + public OpenPGPMessageInputStream process(InputStream messageIn) + throws IOException, PGPException + { + // Remove potential ASCII armoring + InputStream packetInputStream = PGPUtil.getDecoderStream(messageIn); + + PGPObjectFactory objectFactory = implementation.pgpObjectFactory(packetInputStream); + OpenPGPMessageInputStream in = new OpenPGPMessageInputStream(objectFactory, this); + in.process(); + return in; + } + + Date getVerifyNotBefore() + { + return configuration.verifyNotBefore; + } + + Date getVerifyNotAfter() + { + return configuration.verifyNotAfter; + } + + /** + * Bundle together metadata about the decryption result. + * That includes the encrypted data packet itself, the passphrase or (sub-)key that was used to decrypt the + * session-key, the session-key itself and lastly the resulting decrypted packet input stream. + */ + static class Decrypted + { + final InputStream inputStream; + final PGPSessionKey sessionKey; + final PGPEncryptedData esk; + OpenPGPCertificate.OpenPGPComponentKey decryptionKey; + char[] decryptionPassphrase; + + public Decrypted(PGPEncryptedData encryptedData, + PGPSessionKey decryptedSessionKey, + InputStream decryptedIn) + { + this.esk = encryptedData; + this.sessionKey = decryptedSessionKey; + this.inputStream = decryptedIn; + } + } + + /** + * Decrypt an encrypted data packet by trying passphrases and/or decryption keys. + * + * @param encDataList encrypted data + * @return decrypted data + * @throws PGPException in case of an error + */ + Decrypted decrypt(PGPEncryptedDataList encDataList) + throws PGPException + { + // Since decryption using session key is the most "deliberate" and "specific", we'll try that first + if (configuration.sessionKey != null) + { + // decrypt with provided session key + SessionKeyDataDecryptorFactory decryptorFactory = + implementation.sessionKeyDataDecryptorFactory(configuration.sessionKey); + PGPSessionKeyEncryptedData encData = encDataList.extractSessionKeyEncryptedData(); + InputStream decryptedIn = encData.getDataStream(decryptorFactory); + IntegrityProtectedInputStream verifyingIn = new IntegrityProtectedInputStream(decryptedIn, encData); + + return new Decrypted(encData, configuration.sessionKey, verifyingIn); + } + + List skesks = skesks(encDataList); + List pkesks = pkesks(encDataList); + + PGPException exception = null; + + // If the user explicitly provided a message passphrase, we'll try that next + if (!skesks.isEmpty() && !configuration.messagePassphrases.isEmpty()) + { + for (PGPPBEEncryptedData skesk : skesks) + { + for (char[] passphrase : configuration.messagePassphrases) + { + try + { + // Extract message session key with passphrase + PBEDataDecryptorFactory passphraseDecryptorFactory = + implementation.pbeDataDecryptorFactory(passphrase); + PGPSessionKey decryptedSessionKey = skesk.getSessionKey(passphraseDecryptorFactory); + + // Decrypt the message with the decrypted session key + SessionKeyDataDecryptorFactory skDecryptorFactory = + implementation.sessionKeyDataDecryptorFactory(decryptedSessionKey); + PGPSessionKeyEncryptedData encData = encDataList.extractSessionKeyEncryptedData(); + InputStream decryptedIn = encData.getDataStream(skDecryptorFactory); + IntegrityProtectedInputStream verifyingIn = new IntegrityProtectedInputStream(decryptedIn, encData); + + Decrypted decrypted = new Decrypted(encData, decryptedSessionKey, verifyingIn); + decrypted.decryptionPassphrase = passphrase; + + return decrypted; + } + catch (PGPException e) + { + onException(e); + // cache first exception, then continue to try next skesk if present + exception = exception != null ? exception : e; + } + } + } + } + + // Then we'll try decryption using secret key(s) + for (PGPPublicKeyEncryptedData pkesk : pkesks) + { + KeyIdentifier identifier = pkesk.getKeyIdentifier(); + OpenPGPKey key = configuration.keyPool.provide(identifier); + if (key == null) + { + continue; + } + + OpenPGPKey.OpenPGPSecretKey decryptionKey = key.getSecretKeys().get(identifier); + if (decryptionKey == null) + { + continue; + } + + try + { + if (!decryptionKey.isEncryptionKey()) + { + throw new PGPException("Key is not an encryption key and can therefore not decrypt."); + } + + char[] keyPassphrase = configuration.keyPassphraseProvider.getKeyPassword(decryptionKey); + PGPKeyPair unlockedKey = decryptionKey.unlock(keyPassphrase).getKeyPair(); + if (unlockedKey == null) + { + throw new KeyPassphraseException(decryptionKey, new PGPException("Cannot unlock secret key.")); + } + + // Decrypt the message session key using the private key + PublicKeyDataDecryptorFactory pkDecryptorFactory = + implementation.publicKeyDataDecryptorFactory(unlockedKey.getPrivateKey()); + PGPSessionKey decryptedSessionKey = pkesk.getSessionKey(pkDecryptorFactory); + + // Decrypt the message using the decrypted session key + SessionKeyDataDecryptorFactory skDecryptorFactory = + implementation.sessionKeyDataDecryptorFactory(decryptedSessionKey); + PGPSessionKeyEncryptedData encData = encDataList.extractSessionKeyEncryptedData(); + InputStream decryptedIn = encData.getDataStream(skDecryptorFactory); + IntegrityProtectedInputStream verifyingIn = new IntegrityProtectedInputStream(decryptedIn, encData); + Decrypted decrypted = new Decrypted(encData, decryptedSessionKey, verifyingIn); + decrypted.decryptionKey = decryptionKey; + return decrypted; + } + catch (PGPException e) + { + onException(e); + } + } + + // And lastly, we'll prompt the user dynamically for a message passphrase + if (!skesks.isEmpty() && configuration.missingMessagePassphraseCallback != null) + { + char[] passphrase; + while ((passphrase = configuration.missingMessagePassphraseCallback.getMessagePassphrase()) != null) + { + for (PGPPBEEncryptedData skesk : skesks) + { + try + { + // Decrypt the message session key using a passphrase + PBEDataDecryptorFactory passphraseDecryptorFactory = implementation.pbeDataDecryptorFactory(passphrase); + PGPSessionKey decryptedSessionKey = skesk.getSessionKey(passphraseDecryptorFactory); + + // Decrypt the data using the decrypted session key + SessionKeyDataDecryptorFactory skDecryptorFactory = implementation.sessionKeyDataDecryptorFactory(decryptedSessionKey); + PGPSessionKeyEncryptedData encData = encDataList.extractSessionKeyEncryptedData(); + InputStream decryptedIn = encData.getDataStream(skDecryptorFactory); + IntegrityProtectedInputStream verifyingIn = new IntegrityProtectedInputStream(decryptedIn, encData); + Decrypted decrypted = new Decrypted(encData, decryptedSessionKey, verifyingIn); + decrypted.decryptionPassphrase = passphrase; + return decrypted; + } + catch (PGPException e) + { + onException(e); + // cache first exception, then continue to try next skesk if present + exception = exception != null ? exception : e; + } + } + } + + if (exception != null) + { + throw exception; + } + } + + throw new PGPException("No working decryption method found."); + } + + /** + * Return all symmetric-key-encrypted-session-key (SKESK) packets leading the encrypted data packet. + * + * @param encDataList encrypted data list + * @return list of skesk packets (might be empty) + */ + private List skesks(PGPEncryptedDataList encDataList) + { + List list = new ArrayList<>(); + for (PGPEncryptedData encData : encDataList) + { + if (encData instanceof PGPPBEEncryptedData) + { + list.add((PGPPBEEncryptedData) encData); + } + } + return list; + } + + /** + * Return all public-key-encrypted-session-key (PKESK) packets leading the encrypted data packet. + * + * @param encDataList encrypted data list + * @return list of pkesk packets (might be empty) + */ + private List pkesks(PGPEncryptedDataList encDataList) + { + List list = new ArrayList<>(); + for (PGPEncryptedData encData : encDataList) + { + if (encData instanceof PGPPublicKeyEncryptedData) + { + list.add((PGPPublicKeyEncryptedData) encData); + } + } + return list; + } + + OpenPGPCertificate provideCertificate(KeyIdentifier identifier) + { + return configuration.certificatePool.provide(identifier); + } + + OpenPGPImplementation getImplementation() + { + return implementation; + } + + /** + * Method that can be called if a {@link PGPException} is thrown. + * If the user provided a {@link PGPExceptionCallback} ({@link Configuration#exceptionCallback} is not null), + * the exception will be passed along to that callback. + * Otherwise, nothing happens. + * + * @param e exception + */ + void onException(PGPException e) + { + if (configuration.exceptionCallback != null) + { + configuration.exceptionCallback.onException(e); + } + } + + public static class Configuration + { + private final OpenPGPPolicy policy; + private final OpenPGPKeyMaterialPool.OpenPGPCertificatePool certificatePool; + private final OpenPGPKeyMaterialPool.OpenPGPKeyPool keyPool; + private final KeyPassphraseProvider.DefaultKeyPassphraseProvider keyPassphraseProvider; + public final List messagePassphrases = new ArrayList<>(); + private MissingMessagePassphraseCallback missingMessagePassphraseCallback; + private PGPExceptionCallback exceptionCallback = null; + private PGPSessionKey sessionKey; + private Date verifyNotAfter = new Date(); // now + private Date verifyNotBefore = new Date(0L); // beginning of time + + public Configuration(OpenPGPPolicy policy) + { + this.policy = policy; + this.certificatePool = new OpenPGPKeyMaterialPool.OpenPGPCertificatePool(); + this.keyPool = new OpenPGPKeyMaterialPool.OpenPGPKeyPool(); + this.keyPassphraseProvider = new KeyPassphraseProvider.DefaultKeyPassphraseProvider(); + } + + /** + * Add a passphrase that will be tried when a symmetric-key-encrypted-session-key packet is found + * during the decryption process. + * + * @param messagePassphrase passphrase to decrypt the message with + * @return this + */ + public Configuration addMessagePassphrase(char[] messagePassphrase) + { + boolean found = false; + for (char[] existing : messagePassphrases) + { + found |= Arrays.areEqual(existing, messagePassphrase); + } + + if (!found) + { + messagePassphrases.add(messagePassphrase); + } + return this; + } + } + + /** + * Callback to handle {@link PGPException PGPExceptions}. + */ + public interface PGPExceptionCallback + { + void onException(PGPException e); + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPPolicy.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPPolicy.java new file mode 100644 index 0000000000..c1307bb516 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPPolicy.java @@ -0,0 +1,324 @@ +package org.bouncycastle.openpgp.api; + +import org.bouncycastle.bcpg.SignatureSubpacket; +import org.bouncycastle.bcpg.sig.NotationData; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; + +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +/** + * Policy for OpenPGP algorithms and features. + */ +public interface OpenPGPPolicy +{ + /** + * Return true, if the given {@link PGPPublicKey} is an acceptable signing key. + * Note: Although signing requires a secret key, we perform checks on the public part for consistency. + * + * @param key key + * @return true if acceptable signing key + */ + default boolean isAcceptableSigningKey(PGPPublicKey key) + { + return isAcceptablePublicKey(key); + } + + /** + * Return true, if the given {@link PGPPublicKey} is an acceptable signature verification key. + * Note: The asymmetry between this and {@link #isAcceptableSigningKey(PGPPublicKey)} is useful + * to prevent creation of signatures using a legacy key, while still allowing verification of + * signatures made using the same key. + * + * @param key key + * @return true if acceptable verification key + */ + default boolean isAcceptableVerificationKey(PGPPublicKey key) + { + return isAcceptablePublicKey(key); + } + + /** + * Return true, if the given {@link PGPPublicKey} is acceptable for encrypting messages. + * + * @param key key + * @return true if acceptable encryption key + */ + default boolean isAcceptableEncryptionKey(PGPPublicKey key) + { + return isAcceptablePublicKey(key); + } + + /** + * Return true, if the given {@link PGPPublicKey} is acceptable for decrypting messages. + * Note: Although decryption requires a secret key, we perform checks on the public part for consistency. + * The asymmetry between this and {@link #isAcceptableEncryptionKey(PGPPublicKey)} is useful + * to prevent creation of new encrypted messages using a legacy key, while still allowing decryption + * of existing messages using the same key. + * + * @param key key + * @return true if acceptable decryption key + */ + default boolean isAcceptableDecryptionKey(PGPPublicKey key) + { + return isAcceptablePublicKey(key); + } + + /** + * Return true, if the given {@link PGPPublicKey} is acceptable. + * + * @param key key + * @return true if acceptable key + */ + default boolean isAcceptablePublicKey(PGPPublicKey key) + { + return isAcceptablePublicKeyStrength(key.getAlgorithm(), key.getBitStrength()); + } + + /** + * Return true, if the given {@link PGPSignature} is acceptable (uses acceptable hash algorithm, + * does not contain unknown critical notations or subpackets). + * Note: A signature being acceptable does NOT mean that it is correct or valid. + * + * @param signature signature + * @return true if acceptable + */ + default boolean isAcceptableSignature(PGPSignature signature) + { + return hasAcceptableSignatureHashAlgorithm(signature) && + hasNoCriticalUnknownNotations(signature) && + hasNoCriticalUnknownSubpackets(signature); + } + + /** + * Return true, if the given {@link PGPSignature} was made using an acceptable signature hash algorithm. + * + * @param signature signature + * @return true if hash algorithm is acceptable + */ + default boolean hasAcceptableSignatureHashAlgorithm(PGPSignature signature) + { + switch (signature.getSignatureType()) + { + case PGPSignature.DEFAULT_CERTIFICATION: + case PGPSignature.NO_CERTIFICATION: + case PGPSignature.CASUAL_CERTIFICATION: + case PGPSignature.POSITIVE_CERTIFICATION: + case PGPSignature.DIRECT_KEY: + case PGPSignature.SUBKEY_BINDING: + case PGPSignature.PRIMARYKEY_BINDING: + return hasAcceptableCertificationSignatureHashAlgorithm(signature); + + case PGPSignature.CERTIFICATION_REVOCATION: + case PGPSignature.KEY_REVOCATION: + case PGPSignature.SUBKEY_REVOCATION: + return hasAcceptableRevocationSignatureHashAlgorithm(signature); + + case PGPSignature.BINARY_DOCUMENT: + case PGPSignature.CANONICAL_TEXT_DOCUMENT: + default: + return hasAcceptableDocumentSignatureHashAlgorithm(signature); + } + } + + /** + * Return true, if the {@link PGPSignature} uses an acceptable data/document signature hash algorithm. + * + * @param signature data / document signature + * @return true if hash algorithm is acceptable + */ + default boolean hasAcceptableDocumentSignatureHashAlgorithm(PGPSignature signature) + { + return isAcceptableDocumentSignatureHashAlgorithm(signature.getHashAlgorithm(), signature.getCreationTime()); + } + + /** + * Return true, if the {@link PGPSignature} uses an acceptable revocation signature hash algorithm. + * + * @param signature revocation signature + * @return true if hash algorithm is acceptable + */ + default boolean hasAcceptableRevocationSignatureHashAlgorithm(PGPSignature signature) + { + return isAcceptableRevocationSignatureHashAlgorithm(signature.getHashAlgorithm(), signature.getCreationTime()); + } + + /** + * Return true, if the {@link PGPSignature} uses an acceptable certification signature hash algorithm. + * + * @param signature certification signature + * @return true if hash algorithm is acceptable + */ + default boolean hasAcceptableCertificationSignatureHashAlgorithm(PGPSignature signature) + { + return isAcceptableCertificationSignatureHashAlgorithm(signature.getHashAlgorithm(), signature.getCreationTime()); + } + + /** + * Return true, if the hashed subpacket area of the signature does NOT contain unknown critical notations. + * @param signature signature + * @return true if signature is free from unknown critical notations + */ + default boolean hasNoCriticalUnknownNotations(PGPSignature signature) + { + PGPSignatureSubpacketVector hashedSubpackets = signature.getHashedSubPackets(); + if (hashedSubpackets == null) + { + return true; + } + + OpenPGPNotationRegistry registry = getNotationRegistry(); + + NotationData[] notations = hashedSubpackets.getNotationDataOccurrences(); + for (NotationData notation : notations) + { + if (notation.isCritical() && !registry.isNotationKnown(notation.getNotationName())) + { + return false; + } + } + return true; + } + + /** + * Return true, if the hashed subpacket area of the signature does NOT contain unknown critical subpackets. + * @param signature signature + * @return true if signature is free from unknown critical subpackets + */ + default boolean hasNoCriticalUnknownSubpackets(PGPSignature signature) + { + PGPSignatureSubpacketVector hashedSubpackets = signature.getHashedSubPackets(); + if (hashedSubpackets == null) + { + return true; + } + + for (SignatureSubpacket subpacket : hashedSubpackets.toArray()) + { + if (subpacket.isCritical() && + // only consider subpackets which are not recognized by SignatureSubpacketInputStream + subpacket.getClass().equals(SignatureSubpacket.class)) + { + if (!isKnownSignatureSubpacket(subpacket.getType())) + { + return false; + } + } + } + return true; + } + + /** + * Return true, if the given signature subpacket ID is known by the implementation. + * Note: This method is only called for subpackets not recognized by + * {@link org.bouncycastle.bcpg.SignatureSubpacketInputStream}. + * + * @param signatureSubpacketTag signature subpacket ID + * @return true if subpacket tag is known + */ + default boolean isKnownSignatureSubpacket(int signatureSubpacketTag) + { + // Overwrite this, allowing custom critical signature subpackets + return false; + } + + /** + * Return true, if the given hash algorithm is - at signature creation time - an acceptable document signature + * hash algorithm. + * + * @param hashAlgorithmId hash algorithm ID + * @param signatureCreationTime optional signature creation time + * @return true if hash algorithm is acceptable at creation time + */ + boolean isAcceptableDocumentSignatureHashAlgorithm(int hashAlgorithmId, Date signatureCreationTime); + + /** + * Return true, if the given hash algorithm is - at signature creation time - an acceptable revocation signature + * hash algorithm. + * + * @param hashAlgorithmId hash algorithm ID + * @param signatureCreationTime optional signature creation time + * @return true if hash algorithm is acceptable at creation time + */ + boolean isAcceptableRevocationSignatureHashAlgorithm(int hashAlgorithmId, Date signatureCreationTime); + + /** + * Return true, if the given hash algorithm is - at signature creation time - an acceptable certification signature + * hash algorithm. + * + * @param hashAlgorithmId hash algorithm ID + * @param signatureCreationTime optional signature creation time + * @return true if hash algorithm is acceptable at creation time + */ + boolean isAcceptableCertificationSignatureHashAlgorithm(int hashAlgorithmId, Date signatureCreationTime); + + /** + * Return the default certification signature hash algorithm ID. + * This is used as fallback, if negotiation of a commonly supported hash algorithm fails. + * + * @return default certification signature hash algorithm ID + */ + int getDefaultCertificationSignatureHashAlgorithm(); + + /** + * Return the default document signature hash algorithm ID. + * This is used as fallback, if negotiation of a commonly supported hash algorithm fails. + * + * @return default document signature hash algorithm ID + */ + int getDefaultDocumentSignatureHashAlgorithm(); + + /** + * Return true, if the given symmetric-key algorithm is acceptable. + * + * @param symmetricKeyAlgorithmId symmetric-key algorithm + * @return true if symmetric-key algorithm is acceptable + */ + boolean isAcceptableSymmetricKeyAlgorithm(int symmetricKeyAlgorithmId); + + /** + * Return the default symmetric-key algorithm, which is used as a fallback if symmetric encryption algorithm + * negotiation fails. + * + * @return default symmetric-key algorithm + */ + int getDefaultSymmetricKeyAlgorithm(); + + /** + * Return true, if the given bitStrength is acceptable for the given public key algorithm ID. + * + * @param publicKeyAlgorithmId ID of a public key algorithm + * @param bitStrength key bit strength + * @return true if strength is acceptable + */ + boolean isAcceptablePublicKeyStrength(int publicKeyAlgorithmId, int bitStrength); + + /** + * Return the policies {@link OpenPGPNotationRegistry} containing known notation names. + * + * @return notation registry + */ + OpenPGPNotationRegistry getNotationRegistry(); + + /** + * The {@link OpenPGPNotationRegistry} can be used to register known notations, such that signatures containing + * notation instances of the same name, which are marked as critical do not invalidate the signature. + */ + class OpenPGPNotationRegistry + { + private final Set knownNotations = new HashSet<>(); + + public boolean isNotationKnown(String notationName) + { + return knownNotations.contains(notationName); + } + + public void addKnownNotation(String notationName) + { + this.knownNotations.add(notationName); + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java new file mode 100644 index 0000000000..7ab5d29c8f --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java @@ -0,0 +1,716 @@ +package org.bouncycastle.openpgp.api; + +import org.bouncycastle.bcpg.ArmoredOutputStream; +import org.bouncycastle.bcpg.BCPGOutputStream; +import org.bouncycastle.bcpg.KeyIdentifier; +import org.bouncycastle.bcpg.PacketFormat; +import org.bouncycastle.bcpg.SignaturePacket; +import org.bouncycastle.bcpg.SignatureSubpacket; +import org.bouncycastle.bcpg.SignatureSubpacketTags; +import org.bouncycastle.bcpg.sig.NotationData; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPOnePassSignature; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureException; +import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; +import org.bouncycastle.openpgp.api.exception.MalformedOpenPGPSignatureException; +import org.bouncycastle.openpgp.api.util.UTCUtil; +import org.bouncycastle.util.encoders.Hex; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +/** + * An OpenPGP signature. + * This is a wrapper around {@link PGPSignature} which tracks the verification state of the signature. + */ +public abstract class OpenPGPSignature +{ + protected final PGPSignature signature; + protected final OpenPGPCertificate.OpenPGPComponentKey issuer; + protected boolean isTested = false; + protected boolean isCorrect = false; + + /** + * Create an {@link OpenPGPSignature}. + * + * @param signature signature + * @param issuer issuer subkey + */ + public OpenPGPSignature(PGPSignature signature, OpenPGPCertificate.OpenPGPComponentKey issuer) + { + this.signature = signature; + this.issuer = issuer; + } + + /** + * Return the {@link PGPSignature}. + * + * @return signature + */ + public PGPSignature getSignature() + { + return signature; + } + + /** + * Return the {@link OpenPGPCertificate.OpenPGPComponentKey} subkey that issued this signature. + * This method might return null, if the issuer certificate is not available. + * + * @return issuer subkey or null + */ + public OpenPGPCertificate.OpenPGPComponentKey getIssuer() + { + return issuer; + } + + /** + * Return the {@link OpenPGPCertificate} that contains the subkey that issued this signature. + * This method might return null if the issuer certificate is not available + * + * @return issuer certificate or null + */ + public OpenPGPCertificate getIssuerCertificate() + { + return issuer != null ? issuer.getCertificate() : null; + } + + /** + * Return a {@link List} of possible {@link KeyIdentifier} candidates. + * + * @return key identifier candidates + */ + public List getKeyIdentifiers() + { + return signature.getKeyIdentifiers(); + } + + /** + * Return the most expressive {@link KeyIdentifier} from available candidates. + * + * @return most expressive key identifier + */ + public KeyIdentifier getKeyIdentifier() + { + List identifiers = getKeyIdentifiers(); + return getMostExpressiveIdentifier(identifiers); + } + + /** + * Return the most expressive issuer {@link KeyIdentifier}. + * Due to historic reasons, signatures MAY contain more than one issuer packet, which might contain inconsistent + * information (issuer key-ids / issuer fingerprints). + * Throw wildcards (anonymous issuers) into the mix, and it becomes apparent, that there needs to be a way to + * select the "best" issuer identifier. + * If there are more than one issuer packet, this method returns the most expressive (prefer fingerprints over + * key-ids, prefer non-wildcard over wildcard) and returns that. + * + * @param identifiers list of available identifiers + * @return the best identifier + */ + public static KeyIdentifier getMostExpressiveIdentifier(List identifiers) + { + if (identifiers.isEmpty()) + { + // none + return null; + } + if (identifiers.size() == 1) + { + // single + return identifiers.get(0); + } + + // Find most expressive identifier + for (KeyIdentifier identifier : identifiers) + { + // non-wildcard and has fingerprint + if (!identifier.isWildcard() && identifier.getFingerprint() != null) + { + return identifier; + } + } + + // Find non-wildcard identifier + for (KeyIdentifier identifier : identifiers) + { + // non-wildcard (and no fingerprint) + if (!identifier.isWildcard()) + { + return identifier; + } + } + // else return first identifier + return identifiers.get(0); + } + + /** + * Return true, if this signature has been tested and is correct. + * + * @return true if the signature is tested and is correct, false otherwise + */ + public boolean isTestedCorrect() + { + return isTested && isCorrect; + } + + /** + * Return the creation time of the signature. + * + * @return signature creation time + */ + public Date getCreationTime() + { + return signature.getCreationTime(); + } + + /** + * Return the expiration time of the signature. + * If no expiration time was included (or if the signature was explicitly marked as non-expiring), + * return null, otherwise return the time of expiration. + * The signature is no longer valid, once the expiration time is exceeded. + * + * @return expiration time + */ + public Date getExpirationTime() + { + PGPSignatureSubpacketVector hashed = signature.getHashedSubPackets(); + if (hashed == null) + { + // v3 sigs have no expiration + return null; + } + long exp = hashed.getSignatureExpirationTime(); + if (exp < 0) + { + throw new RuntimeException("Negative expiration time"); + } + + if (exp == 0L) + { + // Explicit or implicit no expiration + return null; + } + + return new Date(getCreationTime().getTime() + 1000 * exp); + } + + /** + * Return true, if the signature is not a hard revocation, and if the evaluation time falls into the period + * between signature creation time and expiration or revocation. + * + * @param evaluationTime time for which you want to determine effectiveness of the signature + * @return true if the signature is effective at the given evaluation time + */ + public boolean isEffectiveAt(Date evaluationTime) + { + if (isHardRevocation()) + { + // hard revocation is valid at all times + return true; + } + + // creation <= eval < expiration + Date creation = getCreationTime(); + Date expiration = getExpirationTime(); + return !evaluationTime.before(creation) && (expiration == null || evaluationTime.before(expiration)); + } + + /** + * Return true, if this signature is a hard revocation. + * Contrary to soft revocations (the key / signature / user-id was gracefully retired), a hard revocation + * has a serious reason, like key compromise, or no reason at all. + * Hard revocations invalidate the key / signature / user-id retroactively, while soft revocations only + * invalidate from the time of revocation signature creation onwards. + * + * @return true if the signature is a hard revocation + */ + public boolean isHardRevocation() + { + return signature.isHardRevocation(); + } + + /** + * Return true, if this signature is a certification. + * Certification signatures are used to bind user-ids to a key. + * + * @return true if the signature is a certification + */ + public boolean isCertification() + { + return signature.isCertification(); + } + + + /** + * Check certain requirements for OpenPGP signatures. + * + * @param issuer signature issuer + * @throws MalformedOpenPGPSignatureException if the signature is malformed + */ + void sanitize(OpenPGPCertificate.OpenPGPComponentKey issuer, + OpenPGPPolicy policy) + throws PGPSignatureException + { + if (!policy.isAcceptablePublicKey(issuer.getPGPPublicKey())) + { + throw new PGPSignatureException("Unacceptable issuer key."); + } + if (!policy.hasAcceptableSignatureHashAlgorithm(signature)) + { + throw new PGPSignatureException("Unacceptable hash algorithm: " + signature.getHashAlgorithm()); + } + + if (signature.getVersion() < SignaturePacket.VERSION_4) + { + if (signature.getCreationTime().before(issuer.getCreationTime())) + { + throw new MalformedOpenPGPSignatureException( + this, "Signature predates issuer key creation time."); + } + return; + } + + PGPSignatureSubpacketVector hashed = signature.getHashedSubPackets(); + if (hashed == null) + { + throw new MalformedOpenPGPSignatureException( + this, "Missing hashed signature subpacket area."); + } + PGPSignatureSubpacketVector unhashed = signature.getUnhashedSubPackets(); + + if (hashed.getSignatureCreationTime() == null) + { + // Signatures MUST have hashed creation time subpacket + throw new MalformedOpenPGPSignatureException( + this, "Signature does not have a hashed SignatureCreationTime subpacket."); + } + + if (hashed.getSignatureCreationTime().before(issuer.getCreationTime())) + { + throw new MalformedOpenPGPSignatureException( + this, "Signature predates issuer key creation time."); + } + + for (NotationData notation : hashed.getNotationDataOccurrences()) + { + if (notation.isCritical()) + { + throw new MalformedOpenPGPSignatureException( + this, "Critical unknown NotationData encountered: " + notation.getNotationName()); + } + } + + for (SignatureSubpacket unknownSubpacket : hashed.toArray()) + { + // SignatureSubpacketInputStream returns unknown subpackets as SignatureSubpacket + if (unknownSubpacket.isCritical() && + unknownSubpacket.getClass().equals(SignatureSubpacket.class)) + { + throw new MalformedOpenPGPSignatureException( + this, "Critical hashed unknown SignatureSubpacket encountered: " + unknownSubpacket.getType()); + } + } + + switch (signature.getVersion()) + { + case SignaturePacket.VERSION_4: + case SignaturePacket.VERSION_5: + if (hashed.getIssuerFingerprint() == null && + unhashed.getIssuerFingerprint() == null && + hashed.getSubpacket(SignatureSubpacketTags.ISSUER_KEY_ID) == null && + unhashed.getSubpacket(SignatureSubpacketTags.ISSUER_KEY_ID) == null) + { + int type = signature.getSignatureType(); + if (type != PGPSignature.SUBKEY_BINDING && type != PGPSignature.PRIMARYKEY_BINDING) + { + throw new MalformedOpenPGPSignatureException( + this, "Missing IssuerKeyID and IssuerFingerprint subpacket."); + } + } + break; + + case SignaturePacket.VERSION_6: + if (hashed.getSubpacket(SignatureSubpacketTags.ISSUER_KEY_ID) != null) + { + throw new MalformedOpenPGPSignatureException( + this, "v6 signature MUST NOT contain IssuerKeyID subpacket."); + } + if (hashed.getIssuerFingerprint() == null && unhashed.getIssuerFingerprint() == null) + { + throw new MalformedOpenPGPSignatureException( + this, "v6 signature MUST contain IssuerFingerprint subpacket."); + } + break; + + default: + } + } + + /** + * Return true, if this signature is a revocation, false otherwise. + * @return true if signature is revocation + */ + public boolean isRevocation() + { + return PGPSignature.isRevocation(signature.getSignatureType()); + } + + @Override + public String toString() + { + String issuerInfo = getIssuerDisplay(); + String period = UTCUtil.format(getCreationTime()) + + (getExpirationTime() == null ? "" : ">" + UTCUtil.format(getExpirationTime())); + String validity = isTested ? (isCorrect ? "✓" : "✗") : "❓"; + // -DM Hex.toHexString + return getType() + (signature.isHardRevocation() ? "(hard)" : "") + " " + Hex.toHexString(signature.getDigestPrefix()) + + " " + issuerInfo + " -> " + getTargetDisplay() + " (" + period + ") " + validity; + } + + protected String getIssuerDisplay() + { + if (issuer != null) + { + return issuer.toString(); + } + + KeyIdentifier issuerIdentifier = getKeyIdentifier(); + if (issuerIdentifier == null) + { + return "External[unknown]"; + } + + if (issuerIdentifier.isWildcard()) + { + return "Anonymous"; + } + return "External[" + Long.toHexString(issuerIdentifier.getKeyId()) + .toUpperCase(Locale.getDefault()) + "]"; + } + + protected abstract String getTargetDisplay(); + + protected String getType() + { + switch (signature.getSignatureType()) + { + case PGPSignature.BINARY_DOCUMENT: + return "BINARY_DOCUMENT"; + case PGPSignature.CANONICAL_TEXT_DOCUMENT: + return "CANONICAL_TEXT_DOCUMENT"; + case PGPSignature.STAND_ALONE: + return "STANDALONE"; + case PGPSignature.DEFAULT_CERTIFICATION: + return "DEFAULT_CERTIFICATION"; + case PGPSignature.NO_CERTIFICATION: + return "NO_CERTIFICATION"; + case PGPSignature.CASUAL_CERTIFICATION: + return "CASUAL_CERTIFICATION"; + case PGPSignature.POSITIVE_CERTIFICATION: + return "POSITIVE_CERTIFICATION"; + case PGPSignature.SUBKEY_BINDING: + return "SUBKEY_BINDING"; + case PGPSignature.PRIMARYKEY_BINDING: + return "PRIMARYKEY_BINDING"; + case PGPSignature.DIRECT_KEY: + return "DIRECT_KEY"; + case PGPSignature.KEY_REVOCATION: + return "KEY_REVOCATION"; + case PGPSignature.SUBKEY_REVOCATION: + return "SUBKEY_REVOCATION"; + case PGPSignature.CERTIFICATION_REVOCATION: + return "CERTIFICATION_REVOCATION"; + case PGPSignature.TIMESTAMP: + return "TIMESTAMP"; + case PGPSignature.THIRD_PARTY_CONFIRMATION: + return "THIRD_PARTY_CONFIRMATION"; + default: + return "UNKNOWN (" + signature.getSignatureType() + ")"; + } + } + + /** + * Return an ASCII armored String representation of the signature. + * If the signature contains issuer information, the fingerprint or key-id of the issuer will be added + * to the ASCII armor as a comment header. + * + * @return ASCII armored signature + * @throws IOException if the signature cannot be encoded + */ + public String toAsciiArmoredString() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ArmoredOutputStream.Builder aBuilder = ArmoredOutputStream.builder() + .clearHeaders(); + if (getKeyIdentifier() != null) + { + aBuilder.addSplitMultilineComment(getKeyIdentifier().toPrettyPrint()); + } + ArmoredOutputStream aOut = aBuilder.build(bOut); + BCPGOutputStream pOut = new BCPGOutputStream(aOut, PacketFormat.CURRENT); + getSignature().encode(pOut); + pOut.close(); + aOut.close(); + return bOut.toString(); + } + + /** + * {@link SignatureSubpacket} and the {@link OpenPGPSignature} that contains it. + */ + public static final class OpenPGPSignatureSubpacket + { + private final SignatureSubpacket subpacket; + private final OpenPGPSignature signature; + private final boolean hashed; + + private OpenPGPSignatureSubpacket(SignatureSubpacket subpacket, + OpenPGPSignature signature, + boolean hashed) + { + this.signature = signature; + this.subpacket = subpacket; + this.hashed = hashed; + } + + /** + * Create a {@link OpenPGPSignatureSubpacket} contained in the hashed area of an {@link OpenPGPSignature}. + * + * @param subpacket subpacket + * @param signature the signature containing the subpacket + * @return OpenPGPSignatureSubpacket + */ + public static OpenPGPSignatureSubpacket hashed(SignatureSubpacket subpacket, OpenPGPSignature signature) + { + return new OpenPGPSignatureSubpacket(subpacket, signature, true); + } + + /** + * Create a {@link OpenPGPSignatureSubpacket} contained in the unhashed area of an {@link OpenPGPSignature}. + * + * @param subpacket subpacket + * @param signature the signature containing the subpacket + * @return OpenPGPSignatureSubpacket + */ + public static OpenPGPSignatureSubpacket unhashed(SignatureSubpacket subpacket, OpenPGPSignature signature) + { + return new OpenPGPSignatureSubpacket(subpacket, signature, false); + } + + /** + * Return the {@link OpenPGPSignature} that contains the {@link SignatureSubpacket}. + * @return signature + */ + public OpenPGPSignature getSignature() + { + return signature; + } + + /** + * Return the {@link SignatureSubpacket} itself. + * @return + */ + public SignatureSubpacket getSubpacket() + { + return subpacket; + } + + /** + * Return
    true
    if the subpacket is contained in the hashed area of the {@link OpenPGPSignature}, + * false otherwise. + * @return true if the subpacket is hashed, false if it is unhashed + */ + public boolean isHashed() + { + return hashed; + } + } + + /** + * An {@link OpenPGPSignature} made over a binary or textual document (e.g. a message). + * Also known as a Data Signature. + * An {@link OpenPGPDocumentSignature} CANNOT live on a {@link OpenPGPCertificate}. + */ + public static class OpenPGPDocumentSignature + extends OpenPGPSignature + { + protected final OpenPGPDocumentSignature attestedSignature; + + /** + * Create a document signature of level 0 (signature is made directly over the document). + * + * @param signature signature + * @param issuer public issuer-signing-key-component (or null if not available) + */ + public OpenPGPDocumentSignature(PGPSignature signature, OpenPGPCertificate.OpenPGPComponentKey issuer) + { + super(signature, issuer); + this.attestedSignature = null; + } + + @Override + protected String getTargetDisplay() + { + return ""; + } + + /** + * Create a document signature of level greater than 0 (signature is made as an attestation over + * other signature(s) + document). + * If the attested signature is itself an attestation, it will recursively contain its attested signature. + * + * @param signature attestation signature + * @param issuer public issuer signing-key-component (or null if not available) + * @param attestedSignature the attested signature + */ + public OpenPGPDocumentSignature(PGPSignature signature, + OpenPGPCertificate.OpenPGPComponentKey issuer, + OpenPGPDocumentSignature attestedSignature) + { + super(signature, issuer); + this.attestedSignature = attestedSignature; + } + + /** + * Return the signature attestation level of this signature. + * If this signature was created directly over a document, this method returns 0. + * A level greater than 0 indicates that the signature is an attestation over at least one other signature. + * + * @return signature attestation level + */ + public int getSignatureLevel() + { + if (attestedSignature == null) + { + return 0; // signature over data + } + else + { + return 1 + attestedSignature.getSignatureLevel(); + } + } + + /** + * Return the attested signature (or null if this is not an attestation signature). + * + * @return attested signature or null + */ + public OpenPGPDocumentSignature getAttestedSignature() + { + return attestedSignature; + } + + /** + * Verify the correctness of an inline signature by evaluating the corresponding {@link PGPOnePassSignature}. + * + * @param ops one-pass-signature packet + * @return true if the signature is correct, false otherwise + * @throws PGPException if the signature cannot be verified + */ + public boolean verify(PGPOnePassSignature ops) + throws PGPException + { + isTested = true; + isCorrect = ops.verify(signature); + return isCorrect; + } + + /** + * Verify the correctness of a prefixed-signature. + * + * @return true if the signature is correct, false otherwise + * @throws PGPException if the signature cannot be verified + */ + public boolean verify() + throws PGPException + { + isTested = true; + isCorrect = signature.verify(); + return isCorrect; + } + + /** + * Return true, if the signature is valid at this moment. + * A valid signature is effective, correct and was issued by a valid signing key. + * + * @return true if the signature is valid now. + */ + public boolean isValid() + throws PGPSignatureException + { + return isValid(OpenPGPImplementation.getInstance().policy()); + } + + /** + * Return true, if the signature is valid at this moment using the given policy. + * A valid signature is effective, correct and was issued by a valid signing key. + * + * @param policy policy + * @return true if the signature is valid now. + */ + public boolean isValid(OpenPGPPolicy policy) + throws PGPSignatureException + { + return isValidAt(getCreationTime(), policy); + } + + /** + * Return true, if th signature is valid at the given date. + * A valid signature is effective, correct and was issued by a valid signing key. + * + * @param date evaluation time + * @return true if the signature is valid at the given date + * @throws IllegalStateException if the signature has not yet been tested using a
    verify()
    method. + */ + public boolean isValidAt(Date date) + throws PGPSignatureException + { + return isValidAt(date, OpenPGPImplementation.getInstance().policy()); + } + + /** + * Return true, if th signature is valid at the given date using the given policy. + * A valid signature is effective, correct and was issued by a valid signing key. + * + * @param date evaluation time + * @param policy policy + * @return true if the signature is valid at the given date + * @throws IllegalStateException if the signature has not yet been tested using a
    verify()
    method. + */ + public boolean isValidAt(Date date, OpenPGPPolicy policy) + throws PGPSignatureException + { + if (!isTested) + { + throw new IllegalStateException("Signature has not yet been verified."); + } + if (!isTestedCorrect()) + { + return false; + } + + sanitize(issuer, policy); + + return issuer.getCertificate().getPrimaryKey().isBoundAt(date) && + issuer.isBoundAt(date) && + issuer.isSigningKey(date); + } + + /** + * Check, if the creation time of the signature is within the interval + *
    notBefore <= creationTime <= notAfter
    + * + * @param notBefore earliest accepted creation time + * @param notAfter latest accepted creation time + * @return true if sig was created in bounds, false otherwise + */ + public boolean createdInBounds(Date notBefore, Date notAfter) + { + return !getCreationTime().before(notBefore) && !getCreationTime().after(notAfter); + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureParameters.java b/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureParameters.java new file mode 100644 index 0000000000..02408b62e0 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureParameters.java @@ -0,0 +1,331 @@ +package org.bouncycastle.openpgp.api; + +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.bouncycastle.util.Arrays; + +import java.util.Date; +import java.util.Objects; + +/** + * Parameters for signature generation. + * Some signature builders allow the user to pass in a {@link Callback}, which can be used to modify + * {@link SignatureParameters} instances prior to signature generation. + */ +public class SignatureParameters +{ + private int signatureType; + private Date signatureCreationTime = new Date(); + private int signatureHashAlgorithmId; + private SignatureSubpacketsFunction hashedSubpacketsFunction; + private SignatureSubpacketsFunction unhashedSubpacketsFunction; + + private final int[] allowedSignatureTypes; + + private SignatureParameters(int... allowedSignatureTypes) + { + this.allowedSignatureTypes = allowedSignatureTypes; + } + + /** + * Create default signature parameters object for a direct-key signature. + * When issued as a self-signature, direct-key signatures can be used to store algorithm preferences + * on the key, which apply to the entire certificate (including all subkeys). + * When issued as a third-party signature, direct-key signatures act as delegations, with which for example the + * web-of-trust can be built. + * + * @param policy algorithm policy + * @return parameters + * @see + * OpenPGP Web-of-Trust + */ + public static SignatureParameters directKeySignature(OpenPGPPolicy policy) + { + return new SignatureParameters(PGPSignature.DIRECT_KEY) + .setSignatureType(PGPSignature.DIRECT_KEY) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Create default signature parameters for a key revocation signature. + * When issued as a self-signature, key revocation signatures can be used to revoke an entire certificate. + * To revoke only individual subkeys, see {@link #subkeyRevocation(OpenPGPPolicy)} instead. + * When issued as a third-party signature, key revocation signatures are used to revoke earlier delegation + * signatures. + * + * @param policy algorithm policy + * @return parameters + */ + public static SignatureParameters keyRevocation(OpenPGPPolicy policy) + { + return new SignatureParameters(PGPSignature.KEY_REVOCATION) + .setSignatureType(PGPSignature.KEY_REVOCATION) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Create a default signature parameters object for a certification signature. + * The default signature type is {@link PGPSignature#POSITIVE_CERTIFICATION}, but can be changed to + * {@link PGPSignature#DEFAULT_CERTIFICATION}, {@link PGPSignature#NO_CERTIFICATION}, + * {@link PGPSignature#CASUAL_CERTIFICATION}. + * When issued as a self-signature, certifications can be used to bind user-ids to the certificate. + * When issued as third-party signatures, certificates act as a statement, expressing that the issuer + * is convinced that the user-id "belongs to" the certificate. + * + * @param policy algorithm policy + * @return parameters + */ + public static SignatureParameters certification(OpenPGPPolicy policy) + { + return new SignatureParameters( + PGPSignature.DEFAULT_CERTIFICATION, + PGPSignature.NO_CERTIFICATION, + PGPSignature.CASUAL_CERTIFICATION, + PGPSignature.POSITIVE_CERTIFICATION) + .setSignatureType(PGPSignature.POSITIVE_CERTIFICATION) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Create a default signature parameters object for a subkey binding signature. + * + * @param policy algorithm policy + * @return parameters + */ + public static SignatureParameters subkeyBinding(OpenPGPPolicy policy) + { + return new SignatureParameters(PGPSignature.SUBKEY_BINDING) + .setSignatureType(PGPSignature.SUBKEY_BINDING) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Create default signature parameters for a subkey revocation signature. + * + * @param policy algorithm policy + * @return parameters + */ + public static SignatureParameters subkeyRevocation(OpenPGPPolicy policy) + { + return new SignatureParameters(PGPSignature.SUBKEY_REVOCATION) + .setSignatureType(PGPSignature.SUBKEY_REVOCATION) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Create a default signature parameters object for a primary-key binding (back-sig) signature. + * + * @param policy algorithm policy + * @return parameters + */ + public static SignatureParameters primaryKeyBinding(OpenPGPPolicy policy) + { + return new SignatureParameters(PGPSignature.PRIMARYKEY_BINDING) + .setSignatureType(PGPSignature.PRIMARYKEY_BINDING) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Create a default signature parameters object for a certification-revocation signature. + * + * @param policy algorithm policy + * @return parameters + */ + public static SignatureParameters certificationRevocation(OpenPGPPolicy policy) + { + return new SignatureParameters(PGPSignature.CERTIFICATION_REVOCATION) + .setSignatureType(PGPSignature.CERTIFICATION_REVOCATION) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Create a default signature parameters object for a data/document signature. + * The default signature type is {@link PGPSignature#BINARY_DOCUMENT}, but can be changed to + * {@link PGPSignature#CANONICAL_TEXT_DOCUMENT}. + * + * @param policy algorithm policy + * @return parameters + */ + public static SignatureParameters dataSignature(OpenPGPPolicy policy) + { + return new SignatureParameters(PGPSignature.BINARY_DOCUMENT, PGPSignature.CANONICAL_TEXT_DOCUMENT) + .setSignatureType(PGPSignature.BINARY_DOCUMENT) + .setSignatureHashAlgorithm(policy.getDefaultDocumentSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Change the signature type of the signature to-be-generated to the given type. + * Depending on which factory method was used to instantiate the signature parameters object, + * only certain signature types are allowed. Passing an illegal signature type causes an + * {@link IllegalArgumentException} to be thrown. + * + * @param signatureType signature type + * @return parameters + * @throws IllegalArgumentException if an illegal signature type is passed + */ + public SignatureParameters setSignatureType(int signatureType) + { + if (!Arrays.contains(allowedSignatureTypes, signatureType)) + { + throw new IllegalArgumentException("Illegal signature type provided."); + } + + this.signatureType = signatureType; + return this; + } + + /** + * Return the signature type for the signature to-be-generated. + * + * @return signature type + */ + public int getSignatureType() + { + return signatureType; + } + + /** + * Change the creation time of the signature to-be-generated. + * + * @param signatureCreationTime signature creation time + * @return parameters + */ + public SignatureParameters setSignatureCreationTime(Date signatureCreationTime) + { + this.signatureCreationTime = Objects.requireNonNull(signatureCreationTime, + "Signature creation time cannot be null."); + return this; + } + + /** + * Return the creation time of the signature to-be-generated. + * + * @return signature creation time + */ + public Date getSignatureCreationTime() + { + return signatureCreationTime; + } + + /** + * Change the hash algorithm for the signature to-be-generated. + * + * @param signatureHashAlgorithmId signature hash algorithm id + * @return parameters + */ + public SignatureParameters setSignatureHashAlgorithm(int signatureHashAlgorithmId) + { + this.signatureHashAlgorithmId = signatureHashAlgorithmId; + return this; + } + + /** + * Return the hash algorithm id of the signature to-be-generated. + * + * @return hash algorithm id + */ + public int getSignatureHashAlgorithmId() + { + return signatureHashAlgorithmId; + } + + /** + * Set a function, which is applied to the hashed subpackets area of the signature to-be-generated. + * + * @param subpacketsFunction function to apply to the hashed signature subpackets + * @return parameters + */ + public SignatureParameters setHashedSubpacketsFunction(SignatureSubpacketsFunction subpacketsFunction) + { + this.hashedSubpacketsFunction = subpacketsFunction; + return this; + } + + /** + * Apply the hashed subpackets function set via {@link #setHashedSubpacketsFunction(SignatureSubpacketsFunction)} + * to the given hashed subpackets. + * + * @param hashedSubpackets hashed signature subpackets + * @return modified hashed subpackets + */ + PGPSignatureSubpacketGenerator applyToHashedSubpackets(PGPSignatureSubpacketGenerator hashedSubpackets) + { + if (hashedSubpacketsFunction != null) + { + return hashedSubpacketsFunction.apply(hashedSubpackets); + } + return hashedSubpackets; + } + + /** + * Set a function, which is applied to the unhashed subpackets area of the signature to-be-generated. + * + * @param subpacketsFunction function to apply to the unhashed signature subpackets + * @return parameters + */ + public SignatureParameters setUnhashedSubpacketsFunction(SignatureSubpacketsFunction subpacketsFunction) + { + this.unhashedSubpacketsFunction = subpacketsFunction; + return this; + } + + /** + * Apply the unhashed subpackets function set via {@link #setUnhashedSubpacketsFunction(SignatureSubpacketsFunction)} + * to the given unhashed subpackets. + * + * @param unhashedSubpackets unhashed signature subpackets + * @return modified unhashed subpackets + */ + PGPSignatureSubpacketGenerator applyToUnhashedSubpackets(PGPSignatureSubpacketGenerator unhashedSubpackets) + { + if (unhashedSubpacketsFunction != null) + { + return unhashedSubpacketsFunction.apply(unhashedSubpackets); + } + return unhashedSubpackets; + } + + /** + * Callback, allowing the user to modify {@link SignatureParameters} before use. + */ + public interface Callback + { + /** + * Apply custom changes to {@link SignatureParameters}. + * + * @param parameters parameters instance + * @return modified parameters, or null + */ + default SignatureParameters apply(SignatureParameters parameters) + { + return parameters; + } + + /** + * Shortcut method returning a {@link Callback} which only applies the given + * {@link SignatureSubpacketsFunction} to the hashed signature subpacket area of a signature. + * + * @param function signature subpackets function to apply to the hashed area + * @return callback + */ + static Callback modifyHashedSubpackets(SignatureSubpacketsFunction function) + { + return new Callback() + { + @Override + public SignatureParameters apply(SignatureParameters parameters) + { + return parameters.setHashedSubpacketsFunction(function); + } + }; + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureSubpacketsFunction.java b/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureSubpacketsFunction.java index 177954b692..9d7637e89a 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureSubpacketsFunction.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureSubpacketsFunction.java @@ -5,7 +5,7 @@ /** * Callback to modify the contents of a {@link PGPSignatureSubpacketGenerator}. - * The {@link OpenPGPV6KeyGenerator} already prepopulates the hashed subpacket areas of signatures during + * The {@link OpenPGPKeyGenerator} already prepopulates the hashed subpacket areas of signatures during * key generation. This callback is useful to apply custom changes to the hashed subpacket area during the * generation process. */ diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/SubkeySelector.java b/pg/src/main/java/org/bouncycastle/openpgp/api/SubkeySelector.java new file mode 100644 index 0000000000..5369b4a2f4 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/SubkeySelector.java @@ -0,0 +1,24 @@ +package org.bouncycastle.openpgp.api; + +import org.bouncycastle.bcpg.KeyIdentifier; +import org.bouncycastle.openpgp.PGPKeyRing; + +import java.util.List; + +/** + * Interface for selecting a subset of keys from a {@link PGPKeyRing}. + * This is useful e.g. for selecting a signing key from an OpenPGP key, or a for selecting all + * encryption capable subkeys of a certificate. + */ +public interface SubkeySelector +{ + /** + * Given a {@link PGPKeyRing}, select a subset of the key rings (sub-)keys and return their + * {@link KeyIdentifier KeyIdentifiers}. + * + * @param certificate OpenPGP key or certificate + * @param policy OpenPGP algorithm policy + * @return non-null list of identifiers + */ + List select(OpenPGPCertificate certificate, OpenPGPPolicy policy); +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/Utils.java b/pg/src/main/java/org/bouncycastle/openpgp/api/Utils.java new file mode 100644 index 0000000000..cd506e99ce --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/Utils.java @@ -0,0 +1,118 @@ +package org.bouncycastle.openpgp.api; + +import java.io.IOException; +import java.util.Date; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyPair; +import org.bouncycastle.openpgp.PGPPrivateKey; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureGenerator; +import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; + +class Utils +{ + static void addEmbeddedSiganture(final PGPSignature backSig, PGPSignatureSubpacketGenerator hashedSubpackets) + throws PGPException + { + if (backSig != null) + { + try + { + hashedSubpackets.addEmbeddedSignature(true, backSig); + } + catch (IOException e) + { + throw new PGPException("Cannot encode embedded back-signature.", e); + } + } + } + + static PGPSignature getBackSignature(PGPKeyPair signingSubkey, SignatureParameters backSigParameters, + PGPPublicKey publicPrimaryKey, OpenPGPImplementation implementation, Date date) + throws PGPException + { + PGPSignature backSig = null; + if (backSigParameters != null) + { + PGPSignatureGenerator backSigGen = getPgpSignatureGenerator(implementation, signingSubkey.getPublicKey(), + signingSubkey.getPrivateKey(), backSigParameters, date, null); + + backSig = backSigGen.generateCertification(publicPrimaryKey, signingSubkey.getPublicKey()); + } + return backSig; + } + + static PGPPublicKey injectCertification(PGPPublicKey publicKey, PGPSignatureGenerator revGen, PGPPublicKey publicPrimaryKey) + throws PGPException + { + PGPSignature revocation = revGen.generateCertification(publicPrimaryKey, publicKey); + return PGPPublicKey.addCertification(publicKey, revocation); + } + + static PGPPublicKey injectCertification(PGPPublicKey publicKey, PGPSignatureGenerator revGen) + throws PGPException + { + // Inject signature into the certificate + PGPSignature revocation = revGen.generateCertification(publicKey); + return PGPPublicKey.addCertification(publicKey, revocation); + } + + static PGPPublicKey injectCertification(String userId, PGPPublicKey publicPrimaryKey, PGPSignatureGenerator uidSigGen) + throws PGPException + { + // Inject UID and signature into the certificate + PGPSignature uidSig = uidSigGen.generateCertification(userId, publicPrimaryKey); + return PGPPublicKey.addCertification(publicPrimaryKey, userId, uidSig); + } + + public interface HashedSubpacketsOperation + { + void operate(PGPSignatureSubpacketGenerator hashedSubpackets) + throws PGPException; + } + + static PGPSignatureGenerator getPgpSignatureGenerator(OpenPGPImplementation implementationProvider, + PGPPublicKey publicKey, + PGPPrivateKey privateKey, + SignatureParameters parameters, + Date date, + HashedSubpacketsOperation operation) + throws PGPException + { + PGPSignatureGenerator sigGen = new PGPSignatureGenerator( + implementationProvider.pgpContentSignerBuilder( + publicKey.getAlgorithm(), + parameters.getSignatureHashAlgorithmId()), + publicKey); + sigGen.init(parameters.getSignatureType(), privateKey); + + final PGPSignatureSubpacketGenerator hashedSubpackets = new PGPSignatureSubpacketGenerator(); + hashedSubpackets.setIssuerFingerprint(true, publicKey); + if (date != null) + { + hashedSubpackets.setSignatureCreationTime(date); + } + if (operation != null) + { + operation.operate(hashedSubpackets); + } + parameters.applyToHashedSubpackets(hashedSubpackets); + sigGen.setHashedSubpackets(hashedSubpackets.generate()); + + PGPSignatureSubpacketGenerator unhashedSubpackets = new PGPSignatureSubpacketGenerator(); + unhashedSubpackets = parameters.applyToUnhashedSubpackets(unhashedSubpackets); + sigGen.setUnhashedSubpackets(unhashedSubpackets.generate()); + return sigGen; + } + + static SignatureParameters applySignatureParameters(SignatureParameters.Callback signatureCallback, SignatureParameters parameters) + { + if (signatureCallback != null) + { + parameters = signatureCallback.apply(parameters); + } + return parameters; + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPApi.java b/pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPApi.java new file mode 100644 index 0000000000..297cd8cdac --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPApi.java @@ -0,0 +1,60 @@ +package org.bouncycastle.openpgp.api.bc; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.OpenPGPImplementation; +import org.bouncycastle.openpgp.api.OpenPGPPolicy; +import org.bouncycastle.openpgp.api.OpenPGPKeyGenerator; + +import java.util.Date; + +/** + * Implementation of {@link OpenPGPApi} using Bouncy Castles implementation of OpenPGP classes. + */ +public class BcOpenPGPApi + extends OpenPGPApi +{ + public BcOpenPGPApi() + { + this(new BcOpenPGPImplementation()); + } + + public BcOpenPGPApi(OpenPGPImplementation implementation) + { + super(implementation); + } + + public BcOpenPGPApi(OpenPGPPolicy policy) + { + this(new BcOpenPGPImplementation(), policy); + } + + public BcOpenPGPApi(OpenPGPImplementation implementation, OpenPGPPolicy policy) + { + super(implementation, policy); + } + + @Override + public OpenPGPKeyGenerator generateKey(int version) + throws PGPException + { + return new BcOpenPGPKeyGenerator(version); + } + + @Override + public OpenPGPKeyGenerator generateKey(int version, + Date creationTime) + throws PGPException + { + return new BcOpenPGPKeyGenerator(version, creationTime); + } + + @Override + public OpenPGPKeyGenerator generateKey(int version, + Date creationTime, + boolean aeadProtection) + throws PGPException + { + return new BcOpenPGPKeyGenerator(version, creationTime, aeadProtection); + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPImplementation.java b/pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPImplementation.java new file mode 100644 index 0000000000..ff6f24b9d2 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPImplementation.java @@ -0,0 +1,161 @@ +package org.bouncycastle.openpgp.api.bc; + +import java.io.InputStream; + +import org.bouncycastle.bcpg.S2K; +import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPObjectFactory; +import org.bouncycastle.openpgp.PGPPrivateKey; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSessionKey; +import org.bouncycastle.openpgp.api.OpenPGPImplementation; +import org.bouncycastle.openpgp.bc.BcPGPObjectFactory; +import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.PBEKeyEncryptionMethodGenerator; +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptorBuilderProvider; +import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptorFactory; +import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder; +import org.bouncycastle.openpgp.operator.PGPContentSignerBuilderProvider; +import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider; +import org.bouncycastle.openpgp.operator.PGPDataEncryptorBuilder; +import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider; +import org.bouncycastle.openpgp.operator.PGPKeyPairGeneratorProvider; +import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.PublicKeyKeyEncryptionMethodGenerator; +import org.bouncycastle.openpgp.operator.SessionKeyDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.bc.BcAEADSecretKeyEncryptorFactory; +import org.bouncycastle.openpgp.operator.bc.BcCFBSecretKeyEncryptorFactory; +import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; +import org.bouncycastle.openpgp.operator.bc.BcPBEDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.bc.BcPBEKeyEncryptionMethodGenerator; +import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilderProvider; +import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder; +import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilderProvider; +import org.bouncycastle.openpgp.operator.bc.BcPGPContentVerifierBuilderProvider; +import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder; +import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; +import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPairGeneratorProvider; +import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator; +import org.bouncycastle.openpgp.operator.bc.BcSessionKeyDataDecryptorFactory; + +/** + * Implementation of {@link OpenPGPImplementation} using Bouncy Castles implementation of OpenPGP classes. + */ +public class BcOpenPGPImplementation + extends OpenPGPImplementation +{ + @Override + public PGPObjectFactory pgpObjectFactory(InputStream packetInputStream) + { + return new BcPGPObjectFactory(packetInputStream) + .setThrowForUnknownCriticalPackets(true); + } + + @Override + public PGPContentVerifierBuilderProvider pgpContentVerifierBuilderProvider() + { + return new BcPGPContentVerifierBuilderProvider(); + } + + @Override + public PBESecretKeyDecryptorBuilderProvider pbeSecretKeyDecryptorBuilderProvider() + { + return new BcPBESecretKeyDecryptorBuilderProvider(); + } + + @Override + public PGPDataEncryptorBuilder pgpDataEncryptorBuilder(int symmetricKeyAlgorithm) + { + return new BcPGPDataEncryptorBuilder(symmetricKeyAlgorithm); + } + + @Override + public PublicKeyKeyEncryptionMethodGenerator publicKeyKeyEncryptionMethodGenerator(PGPPublicKey encryptionSubkey) + { + return new BcPublicKeyKeyEncryptionMethodGenerator(encryptionSubkey); + } + + @Override + public PBEKeyEncryptionMethodGenerator pbeKeyEncryptionMethodGenerator(char[] messagePassphrase) + { + return new BcPBEKeyEncryptionMethodGenerator(messagePassphrase); + } + + @Override + public PBEKeyEncryptionMethodGenerator pbeKeyEncryptionMethodGenerator(char[] messagePassphrase, S2K.Argon2Params argon2Params) + { + return new BcPBEKeyEncryptionMethodGenerator(messagePassphrase, argon2Params); + } + + @Override + public PGPContentSignerBuilder pgpContentSignerBuilder(int publicKeyAlgorithm, int hashAlgorithm) + { + return new BcPGPContentSignerBuilder(publicKeyAlgorithm, hashAlgorithm); + } + + @Override + public PBEDataDecryptorFactory pbeDataDecryptorFactory(char[] messagePassphrase) + throws PGPException + { + return new BcPBEDataDecryptorFactory(messagePassphrase, pgpDigestCalculatorProvider()); + } + + @Override + public SessionKeyDataDecryptorFactory sessionKeyDataDecryptorFactory(PGPSessionKey sessionKey) + { + return new BcSessionKeyDataDecryptorFactory(sessionKey); + } + + @Override + public PublicKeyDataDecryptorFactory publicKeyDataDecryptorFactory(PGPPrivateKey decryptionKey) + { + return new BcPublicKeyDataDecryptorFactory(decryptionKey); + } + + @Override + public PGPDigestCalculatorProvider pgpDigestCalculatorProvider() + throws PGPException + { + return new BcPGPDigestCalculatorProvider(); + } + + @Override + public PGPKeyPairGeneratorProvider pgpKeyPairGeneratorProvider() + { + return new BcPGPKeyPairGeneratorProvider(); + } + + @Override + public PGPContentSignerBuilderProvider pgpContentSignerBuilderProvider(int hashAlgorithmId) + { + return new BcPGPContentSignerBuilderProvider(hashAlgorithmId); + } + + @Override + public KeyFingerPrintCalculator keyFingerPrintCalculator() + { + return new BcKeyFingerprintCalculator(); + } + + @Override + public PBESecretKeyEncryptorFactory pbeSecretKeyEncryptorFactory(boolean aead) + { + return pbeSecretKeyEncryptorFactory(aead, SymmetricKeyAlgorithmTags.AES_128, 0x60); + } + + @Override + public PBESecretKeyEncryptorFactory pbeSecretKeyEncryptorFactory(boolean aead, int symmetricKeyAlgorithm, int iterationCount) + { + if (aead) + { + return new BcAEADSecretKeyEncryptorFactory(); + } + else + { + return new BcCFBSecretKeyEncryptorFactory(symmetricKeyAlgorithm, iterationCount); + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPKeyGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPKeyGenerator.java new file mode 100644 index 0000000000..218fb6c73d --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPKeyGenerator.java @@ -0,0 +1,51 @@ +package org.bouncycastle.openpgp.api.bc; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.api.OpenPGPKeyGenerator; + +import java.util.Date; + +/** + * Bouncy Castle implementation of {@link OpenPGPKeyGenerator}. + */ +public class BcOpenPGPKeyGenerator + extends OpenPGPKeyGenerator +{ + + /** + * Create a new key generator for OpenPGP v6 keys. + * + * @param version key version + */ + public BcOpenPGPKeyGenerator(int version) + throws PGPException + { + this(version, new Date()); + } + + /** + * Create a new key generator for OpenPGP v6 keys. + * The key creation time will be set to {@code creationTime} + * + * @param version key version + * @param creationTime creation time of the generated OpenPGP key + */ + public BcOpenPGPKeyGenerator(int version, Date creationTime) + throws PGPException + { + this(version, creationTime, true); + } + + /** + * Create a new OpenPGP key generator for v6 keys. + * + * @param version key version + * @param creationTime creation time of the key and signatures + * @param aeadProtection whether the key shall be protected using AEAD. If false, the key is protected using CFB. + */ + public BcOpenPGPKeyGenerator(int version, Date creationTime, boolean aeadProtection) + throws PGPException + { + super(new BcOpenPGPImplementation(), version, aeadProtection, creationTime); + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPV6KeyGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPV6KeyGenerator.java deleted file mode 100644 index 8953dc4ce9..0000000000 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/bc/BcOpenPGPV6KeyGenerator.java +++ /dev/null @@ -1,79 +0,0 @@ -package org.bouncycastle.openpgp.api.bc; - -import org.bouncycastle.openpgp.api.OpenPGPV6KeyGenerator; -import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptorFactory; -import org.bouncycastle.openpgp.operator.bc.BcAEADSecretKeyEncryptorFactory; -import org.bouncycastle.openpgp.operator.bc.BcCFBSecretKeyEncryptorFactory; -import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; -import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilderProvider; -import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; -import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPairGeneratorProvider; - -import java.util.Date; - -/** - * Bouncy Castle implementation of {@link OpenPGPV6KeyGenerator}. - */ -public class BcOpenPGPV6KeyGenerator - extends OpenPGPV6KeyGenerator -{ - - /** - * Create a new key generator for OpenPGP v6 keys. - */ - public BcOpenPGPV6KeyGenerator() - { - this(new Date()); - } - - /** - * Create a new key generator for OpenPGP v6 keys. - * The key creation time will be set to {@code creationTime} - * - * @param creationTime creation time of the generated OpenPGP key - */ - public BcOpenPGPV6KeyGenerator(Date creationTime) - { - this(DEFAULT_SIGNATURE_HASH_ALGORITHM, creationTime, true); - } - - /** - * Create a new key generator for OpenPGP v6 keys. - * Signatures on the key will be generated using the specified {@code signatureHashAlgorithm}. - * - * @param signatureHashAlgorithm ID of the hash algorithm to be used for signature generation - */ - public BcOpenPGPV6KeyGenerator(int signatureHashAlgorithm) - { - this(signatureHashAlgorithm, new Date(), true); - } - - /** - * Create a new OpenPGP key generator for v6 keys. - * - * @param signatureHashAlgorithm ID of the hash algorithm used for signatures on the key - * @param creationTime creation time of the key and signatures - */ - public BcOpenPGPV6KeyGenerator(int signatureHashAlgorithm, Date creationTime, boolean aeadProtection) - { - super( - new BcPGPKeyPairGeneratorProvider(), - new BcPGPContentSignerBuilderProvider(signatureHashAlgorithm), - new BcPGPDigestCalculatorProvider(), - keyEncryptorFactory(aeadProtection), - new BcKeyFingerprintCalculator(), - creationTime); - } - - private static PBESecretKeyEncryptorFactory keyEncryptorFactory(boolean aeadProtection) - { - if (aeadProtection) - { - return new BcAEADSecretKeyEncryptorFactory(); - } - else - { - return new BcCFBSecretKeyEncryptorFactory(); - } - } -} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/exception/IncorrectOpenPGPSignatureException.java b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/IncorrectOpenPGPSignatureException.java new file mode 100644 index 0000000000..44d5c34aa4 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/IncorrectOpenPGPSignatureException.java @@ -0,0 +1,15 @@ +package org.bouncycastle.openpgp.api.exception; + +import org.bouncycastle.openpgp.api.OpenPGPSignature; + +/** + * An OpenPGP signature is not correct. + */ +public class IncorrectOpenPGPSignatureException + extends OpenPGPSignatureException +{ + public IncorrectOpenPGPSignatureException(OpenPGPSignature signature, String message) + { + super(signature, message); + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/exception/InvalidEncryptionKeyException.java b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/InvalidEncryptionKeyException.java new file mode 100644 index 0000000000..9e3f04b488 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/InvalidEncryptionKeyException.java @@ -0,0 +1,37 @@ +package org.bouncycastle.openpgp.api.exception; + +import org.bouncycastle.openpgp.api.OpenPGPCertificate; +import org.bouncycastle.util.Arrays; + +/** + * Exception that gets thrown if the user tries to encrypt a message for an + * {@link org.bouncycastle.openpgp.api.OpenPGPCertificate} that does not contain any usable, valid encryption keys. + */ +public class InvalidEncryptionKeyException + extends OpenPGPKeyException +{ + + public InvalidEncryptionKeyException(OpenPGPCertificate certificate) + { + super(certificate, "Certificate " + certificate.getKeyIdentifier() + + " does not contain any usable subkeys capable of encryption."); + } + + public InvalidEncryptionKeyException(OpenPGPCertificate.OpenPGPComponentKey encryptionSubkey) + { + super(encryptionSubkey, componentKeyErrorMessage(encryptionSubkey)); + } + + private static String componentKeyErrorMessage(OpenPGPCertificate.OpenPGPComponentKey componentKey) + { + if (componentKey.getKeyIdentifier().equals(componentKey.getCertificate().getKeyIdentifier())) + { + return "The primary key " + componentKey.getKeyIdentifier() + " is not usable for encryption."; + } + else + { + return "The subkey " + componentKey.getKeyIdentifier() + " from the certificate " + + componentKey.getCertificate().getKeyIdentifier() + " is not usable for encryption."; + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/exception/InvalidSigningKeyException.java b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/InvalidSigningKeyException.java new file mode 100644 index 0000000000..fccdefecb9 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/InvalidSigningKeyException.java @@ -0,0 +1,33 @@ +package org.bouncycastle.openpgp.api.exception; + +import org.bouncycastle.openpgp.api.OpenPGPCertificate; +import org.bouncycastle.openpgp.api.OpenPGPKey; + +public class InvalidSigningKeyException + extends OpenPGPKeyException +{ + + public InvalidSigningKeyException(OpenPGPKey key) + { + super(key, "The key " + key.getKeyIdentifier() + + " does not contain any usable component keys capable of signing."); + } + + public InvalidSigningKeyException(OpenPGPCertificate.OpenPGPComponentKey componentKey) + { + super(componentKey, componentKeyErrorMessage(componentKey)); + } + + private static String componentKeyErrorMessage(OpenPGPCertificate.OpenPGPComponentKey componentKey) + { + if (componentKey.getKeyIdentifier().equals(componentKey.getCertificate().getKeyIdentifier())) + { + return "The primary key " + componentKey.getKeyIdentifier() + " is not usable for signing."; + } + else + { + return "The subkey " + componentKey.getKeyIdentifier() + " from the certificate " + + componentKey.getCertificate().getKeyIdentifier() + " is not usable for signing."; + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/exception/KeyPassphraseException.java b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/KeyPassphraseException.java new file mode 100644 index 0000000000..7a51216fdf --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/KeyPassphraseException.java @@ -0,0 +1,34 @@ +package org.bouncycastle.openpgp.api.exception; + + +import org.bouncycastle.openpgp.api.OpenPGPCertificate; + +public class KeyPassphraseException + extends OpenPGPKeyException +{ + private final Exception cause; + + public KeyPassphraseException(OpenPGPCertificate.OpenPGPComponentKey key, Exception cause) + { + super(key, componentKeyErrorMessage(key, cause)); + this.cause = cause; + } + + private static String componentKeyErrorMessage(OpenPGPCertificate.OpenPGPComponentKey key, Exception cause) + { + if (key.getKeyIdentifier().equals(key.getCertificate().getKeyIdentifier())) + { + return "Cannot unlock primary key " + key.getKeyIdentifier() + ": " + cause.getMessage(); + } + else + { + return "Cannot unlock subkey " + key.getKeyIdentifier() + " from key " + + key.getCertificate().getKeyIdentifier() + ": " + cause.getMessage(); + } + } + + public Exception getCause() + { + return cause; + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/exception/MalformedOpenPGPSignatureException.java b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/MalformedOpenPGPSignatureException.java new file mode 100644 index 0000000000..06d941a7cf --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/MalformedOpenPGPSignatureException.java @@ -0,0 +1,16 @@ +package org.bouncycastle.openpgp.api.exception; + +import org.bouncycastle.openpgp.api.OpenPGPSignature; + +/** + * An OpenPGP Signature is malformed (missing required subpackets, etc.). + */ +public class MalformedOpenPGPSignatureException + extends OpenPGPSignatureException +{ + + public MalformedOpenPGPSignatureException(OpenPGPSignature signature, String message) + { + super(signature, message); + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/exception/MissingIssuerCertException.java b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/MissingIssuerCertException.java new file mode 100644 index 0000000000..4a37432966 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/MissingIssuerCertException.java @@ -0,0 +1,15 @@ +package org.bouncycastle.openpgp.api.exception; + +import org.bouncycastle.openpgp.api.OpenPGPSignature; + +/** + * The OpenPGP certificate (public key) required to verify a signature is not available. + */ +public class MissingIssuerCertException + extends OpenPGPSignatureException +{ + public MissingIssuerCertException(OpenPGPSignature signature, String message) + { + super(signature, message); + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/exception/OpenPGPKeyException.java b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/OpenPGPKeyException.java new file mode 100644 index 0000000000..04e5787ea5 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/OpenPGPKeyException.java @@ -0,0 +1,68 @@ +package org.bouncycastle.openpgp.api.exception; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.api.OpenPGPCertificate; + +/** + * Exception representing an unusable or invalid {@link org.bouncycastle.openpgp.api.OpenPGPKey} + * or {@link OpenPGPCertificate}. + * Note: The term "key" is used to refer to both a certificate and a key. + */ +public class OpenPGPKeyException + extends PGPException +{ + private final OpenPGPCertificate key; + private final OpenPGPCertificate.OpenPGPComponentKey componentKey; + + private OpenPGPKeyException(OpenPGPCertificate key, + OpenPGPCertificate.OpenPGPComponentKey componentKey, + String message) + { + super(message); + this.key = key; + this.componentKey = componentKey; + } + + /** + * Something is wrong with a key or certificate in general (no particular subkey). + * + * @param key certificate or key + * @param message message + */ + public OpenPGPKeyException(OpenPGPCertificate key, String message) + { + this(key, null, message); + } + + /** + * Something is wrong with an individual component key of a key or certificate. + * + * @param componentKey component key + * @param message message + */ + public OpenPGPKeyException(OpenPGPCertificate.OpenPGPComponentKey componentKey, String message) + { + this(componentKey.getCertificate(), componentKey, message); + } + + /** + * Return the problematic key or certificate. + * + * @return key or certificate + */ + public OpenPGPCertificate getKey() + { + return key; + } + + /** + * Return the problematic component key. + * Might be null, if the problem affects the entire key or certificate. + * + * @return component key + */ + public OpenPGPCertificate.OpenPGPComponentKey getComponentKey() + { + return componentKey; + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/exception/OpenPGPSignatureException.java b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/OpenPGPSignatureException.java new file mode 100644 index 0000000000..16df89d966 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/exception/OpenPGPSignatureException.java @@ -0,0 +1,21 @@ +package org.bouncycastle.openpgp.api.exception; + +import org.bouncycastle.openpgp.PGPSignatureException; +import org.bouncycastle.openpgp.api.OpenPGPSignature; + +public class OpenPGPSignatureException + extends PGPSignatureException +{ + private final OpenPGPSignature signature; + + public OpenPGPSignatureException(OpenPGPSignature signature, String message) + { + super(message); + this.signature = signature; + } + + public OpenPGPSignature getSignature() + { + return signature; + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPApi.java b/pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPApi.java new file mode 100644 index 0000000000..771208cdab --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPApi.java @@ -0,0 +1,63 @@ +package org.bouncycastle.openpgp.api.jcajce; + +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.OpenPGPPolicy; +import org.bouncycastle.openpgp.api.OpenPGPKeyGenerator; + +import java.security.Provider; +import java.security.SecureRandom; +import java.util.Date; + +/** + * Implementation of {@link OpenPGPApi} using the JCA/JCE implementation of OpenPGP classes. + */ +public class JcaOpenPGPApi + extends OpenPGPApi +{ + private final Provider provider; + + public JcaOpenPGPApi(Provider provider) + { + this(provider, CryptoServicesRegistrar.getSecureRandom()); + } + + public JcaOpenPGPApi(Provider provider, SecureRandom random) + { + super(new JcaOpenPGPImplementation(provider, random)); + this.provider = provider; + } + + public JcaOpenPGPApi(Provider provider, OpenPGPPolicy policy) + { + this(provider, CryptoServicesRegistrar.getSecureRandom(), policy); + } + + public JcaOpenPGPApi(Provider provider, SecureRandom random, OpenPGPPolicy policy) + { + super(new JcaOpenPGPImplementation(provider, random), policy); + this.provider = provider; + } + + @Override + public OpenPGPKeyGenerator generateKey(int version) + throws PGPException + { + return new JcaOpenPGPKeyGenerator(version, provider); + } + + @Override + public OpenPGPKeyGenerator generateKey(int version, Date creationTime) + throws PGPException + { + return new JcaOpenPGPKeyGenerator(version, creationTime, provider); + } + + @Override + public OpenPGPKeyGenerator generateKey(int version, Date creationTime, boolean aeadProtection) + throws PGPException + { + return new JcaOpenPGPKeyGenerator(version, creationTime, aeadProtection, provider); + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPImplementation.java b/pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPImplementation.java new file mode 100644 index 0000000000..08425937ef --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPImplementation.java @@ -0,0 +1,229 @@ +package org.bouncycastle.openpgp.api.jcajce; + +import java.io.InputStream; +import java.security.Provider; +import java.security.SecureRandom; + +import org.bouncycastle.bcpg.S2K; +import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPObjectFactory; +import org.bouncycastle.openpgp.PGPPrivateKey; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSessionKey; +import org.bouncycastle.openpgp.api.OpenPGPImplementation; +import org.bouncycastle.openpgp.jcajce.JcaPGPObjectFactory; +import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator; +import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.PBEKeyEncryptionMethodGenerator; +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptorBuilderProvider; +import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptorFactory; +import org.bouncycastle.openpgp.operator.PGPContentSignerBuilder; +import org.bouncycastle.openpgp.operator.PGPContentSignerBuilderProvider; +import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider; +import org.bouncycastle.openpgp.operator.PGPDataEncryptorBuilder; +import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider; +import org.bouncycastle.openpgp.operator.PGPKeyPairGeneratorProvider; +import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.PublicKeyKeyEncryptionMethodGenerator; +import org.bouncycastle.openpgp.operator.SessionKeyDataDecryptorFactory; +import org.bouncycastle.openpgp.operator.jcajce.JcaAEADSecretKeyEncryptorFactory; +import org.bouncycastle.openpgp.operator.jcajce.JcaCFBSecretKeyEncryptorFactory; +import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilderProvider; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentVerifierBuilderProvider; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPairGeneratorProvider; +import org.bouncycastle.openpgp.operator.jcajce.JcePBEDataDecryptorFactoryBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcePBEKeyEncryptionMethodGenerator; +import org.bouncycastle.openpgp.operator.jcajce.JcePBESecretKeyDecryptorBuilderProvider; +import org.bouncycastle.openpgp.operator.jcajce.JcePGPDataEncryptorBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyDataDecryptorFactoryBuilder; +import org.bouncycastle.openpgp.operator.jcajce.JcePublicKeyKeyEncryptionMethodGenerator; +import org.bouncycastle.openpgp.operator.jcajce.JceSessionKeyDataDecryptorFactoryBuilder; + +/** + * Implementation of {@link OpenPGPImplementation} using the JCA/JCE implementation of OpenPGP classes. + */ +public class JcaOpenPGPImplementation + extends OpenPGPImplementation +{ + private final Provider provider; + private final SecureRandom secureRandom; + + public JcaOpenPGPImplementation() + { + this(new BouncyCastleProvider(), CryptoServicesRegistrar.getSecureRandom()); + } + + public JcaOpenPGPImplementation(Provider provider, SecureRandom secureRandom) + { + this.provider = provider; + this.secureRandom = secureRandom; + } + + @Override + public PGPObjectFactory pgpObjectFactory(InputStream packetInputStream) + { + return new JcaPGPObjectFactory(packetInputStream) + .setThrowForUnknownCriticalPackets(true); + } + + @Override + public PGPContentVerifierBuilderProvider pgpContentVerifierBuilderProvider() + { + JcaPGPContentVerifierBuilderProvider p = new JcaPGPContentVerifierBuilderProvider(); + p.setProvider(provider); + return p; + } + + @Override + public PBESecretKeyDecryptorBuilderProvider pbeSecretKeyDecryptorBuilderProvider() + { + JcaPGPDigestCalculatorProviderBuilder dp = new JcaPGPDigestCalculatorProviderBuilder(); + dp.setProvider(provider); + JcePBESecretKeyDecryptorBuilderProvider p = new JcePBESecretKeyDecryptorBuilderProvider(dp) + .setProvider(provider); + return p; + } + + @Override + public PGPDataEncryptorBuilder pgpDataEncryptorBuilder(int symmetricKeyAlgorithm) + { + JcePGPDataEncryptorBuilder b = new JcePGPDataEncryptorBuilder(symmetricKeyAlgorithm); + b.setProvider(provider); + b.setSecureRandom(secureRandom); + return b; + } + + @Override + public PublicKeyKeyEncryptionMethodGenerator publicKeyKeyEncryptionMethodGenerator(PGPPublicKey encryptionSubkey) + { + JcePublicKeyKeyEncryptionMethodGenerator g = new JcePublicKeyKeyEncryptionMethodGenerator(encryptionSubkey); + g.setProvider(provider); + g.setSecureRandom(secureRandom); + return g; + } + + @Override + public PBEKeyEncryptionMethodGenerator pbeKeyEncryptionMethodGenerator(char[] messagePassphrase) + { + JcePBEKeyEncryptionMethodGenerator g = new JcePBEKeyEncryptionMethodGenerator(messagePassphrase); + g.setProvider(provider); + g.setSecureRandom(secureRandom); + return g; + } + + @Override + public PBEKeyEncryptionMethodGenerator pbeKeyEncryptionMethodGenerator(char[] messagePassphrase, S2K.Argon2Params argon2Params) + { + JcePBEKeyEncryptionMethodGenerator g = new JcePBEKeyEncryptionMethodGenerator(messagePassphrase, argon2Params); + g.setProvider(provider); + g.setSecureRandom(secureRandom); + return g; + } + + @Override + public PGPContentSignerBuilder pgpContentSignerBuilder(int publicKeyAlgorithm, int hashAlgorithm) + { + JcaPGPContentSignerBuilder b = new JcaPGPContentSignerBuilder(publicKeyAlgorithm, hashAlgorithm); + b.setProvider(provider); + b.setDigestProvider(provider); + b.setSecureRandom(secureRandom); + return b; + } + + @Override + public PBEDataDecryptorFactory pbeDataDecryptorFactory(char[] messagePassphrase) + throws PGPException + { + return new JcePBEDataDecryptorFactoryBuilder(pgpDigestCalculatorProvider()) + .setProvider(provider) + .build(messagePassphrase); + } + + @Override + public SessionKeyDataDecryptorFactory sessionKeyDataDecryptorFactory(PGPSessionKey sessionKey) + { + return new JceSessionKeyDataDecryptorFactoryBuilder() + .setProvider(provider) + .build(sessionKey); + } + + @Override + public PublicKeyDataDecryptorFactory publicKeyDataDecryptorFactory(PGPPrivateKey decryptionKey) + { + return new JcePublicKeyDataDecryptorFactoryBuilder() + .setProvider(provider) + .setContentProvider(provider) + .build(decryptionKey); + } + + @Override + public PGPDigestCalculatorProvider pgpDigestCalculatorProvider() + throws PGPException + { + return new JcaPGPDigestCalculatorProviderBuilder() + .setProvider(provider) + .build(); + } + + @Override + public PGPKeyPairGeneratorProvider pgpKeyPairGeneratorProvider() + { + return new JcaPGPKeyPairGeneratorProvider() + .setProvider(provider) + .setSecureRandom(secureRandom); + } + + @Override + public PGPContentSignerBuilderProvider pgpContentSignerBuilderProvider(int hashAlgorithmId) + { + return new JcaPGPContentSignerBuilderProvider(hashAlgorithmId) + .setSecurityProvider(provider) + .setDigestProvider(provider) + .setSecureRandom(secureRandom); + } + + @Override + public KeyFingerPrintCalculator keyFingerPrintCalculator() + { + return new JcaKeyFingerprintCalculator() + .setProvider(provider); + } + + @Override + public PBESecretKeyEncryptorFactory pbeSecretKeyEncryptorFactory(boolean aead) + throws PGPException + { + if (aead) + { + return new JcaAEADSecretKeyEncryptorFactory() + .setProvider(provider); + } + else + { + return new JcaCFBSecretKeyEncryptorFactory(SymmetricKeyAlgorithmTags.AES_128, 0x60) + .setProvider(provider); + } + } + + @Override + public PBESecretKeyEncryptorFactory pbeSecretKeyEncryptorFactory(boolean aead, int symmetricKeyAlgorithm, int iterationCount) + throws PGPException + { + if (aead) + { + return new JcaAEADSecretKeyEncryptorFactory() + .setProvider(provider); + } + else + { + return new JcaCFBSecretKeyEncryptorFactory(symmetricKeyAlgorithm, iterationCount) + .setProvider(provider); + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPKeyGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPKeyGenerator.java new file mode 100644 index 0000000000..c0401336f7 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPKeyGenerator.java @@ -0,0 +1,43 @@ +package org.bouncycastle.openpgp.api.jcajce; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.api.OpenPGPKeyGenerator; + +import java.security.Provider; +import java.security.SecureRandom; +import java.util.Date; + +/** + * JCA/JCE implementation of the {@link OpenPGPKeyGenerator}. + */ +public class JcaOpenPGPKeyGenerator + extends OpenPGPKeyGenerator +{ + + public JcaOpenPGPKeyGenerator(int version, Provider provider) + throws PGPException + { + this(version, new Date(), provider); + } + + public JcaOpenPGPKeyGenerator(int version, Date creationTime, Provider provider) + throws PGPException + { + this(version, creationTime, true, provider); + } + + /** + * Create a new OpenPGP key generator for v6 keys. + * + * @param creationTime creation time of the key and signatures + */ + public JcaOpenPGPKeyGenerator(int version, Date creationTime, boolean aeadProtection, Provider provider) + throws PGPException + { + super( + new JcaOpenPGPImplementation(provider, new SecureRandom()), + version, + aeadProtection, + creationTime); + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPV6KeyGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPV6KeyGenerator.java deleted file mode 100644 index d0890f321e..0000000000 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/jcajce/JcaOpenPGPV6KeyGenerator.java +++ /dev/null @@ -1,73 +0,0 @@ -package org.bouncycastle.openpgp.api.jcajce; - -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.api.OpenPGPV6KeyGenerator; -import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptorFactory; -import org.bouncycastle.openpgp.operator.jcajce.JcaAEADSecretKeyEncryptorFactory; -import org.bouncycastle.openpgp.operator.jcajce.JcaCFBSecretKeyEncryptorFactory; -import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; -import org.bouncycastle.openpgp.operator.jcajce.JcaPGPContentSignerBuilderProvider; -import org.bouncycastle.openpgp.operator.jcajce.JcaPGPDigestCalculatorProviderBuilder; -import org.bouncycastle.openpgp.operator.jcajce.JcaPGPKeyPairGeneratorProvider; - -import java.security.Provider; -import java.util.Date; - -public class JcaOpenPGPV6KeyGenerator - extends OpenPGPV6KeyGenerator -{ - - public JcaOpenPGPV6KeyGenerator(Provider provider) - throws PGPException - { - this(new Date(), provider); - } - - public JcaOpenPGPV6KeyGenerator(Date creationTime, Provider provider) - throws PGPException - { - this(DEFAULT_SIGNATURE_HASH_ALGORITHM, creationTime, true, provider); - } - - public JcaOpenPGPV6KeyGenerator(int signatureHashAlgorithm, Provider provider) - throws PGPException - { - this(signatureHashAlgorithm, new Date(), true, provider); - } - - /** - * Create a new OpenPGP key generator for v6 keys. - * - * @param signatureHashAlgorithm ID of the hash algorithm used for signatures on the key - * @param creationTime creation time of the key and signatures - */ - public JcaOpenPGPV6KeyGenerator(int signatureHashAlgorithm, Date creationTime, boolean aeadProtection, Provider provider) - throws PGPException - { - super( - new JcaPGPKeyPairGeneratorProvider() - .setProvider(provider), - new JcaPGPContentSignerBuilderProvider(signatureHashAlgorithm) - .setSecurityProvider(provider), - new JcaPGPDigestCalculatorProviderBuilder() - .setProvider(provider) - .build(), - keyEncryptorFactory(provider, aeadProtection), - new JcaKeyFingerprintCalculator(), - creationTime); - } - - private static PBESecretKeyEncryptorFactory keyEncryptorFactory(Provider provider, boolean aeadProtection) - throws PGPException - { - if (aeadProtection) - { - return new JcaAEADSecretKeyEncryptorFactory().setProvider(provider); - } - else - { - return new JcaCFBSecretKeyEncryptorFactory().setProvider(provider); - - } - } -} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/util/UTCUtil.java b/pg/src/main/java/org/bouncycastle/openpgp/api/util/UTCUtil.java new file mode 100644 index 0000000000..78ce9a5f7d --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/util/UTCUtil.java @@ -0,0 +1,51 @@ +package org.bouncycastle.openpgp.api.util; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +/** + * Utility class for parsing and formatting UTC timestamps. + */ +public class UTCUtil +{ + private static SimpleDateFormat utc() + { + // Java's SimpleDateFormat is not thread-safe, therefore we return a new instance on every invocation. + // See https://stackoverflow.com/a/6840856/11150851 + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z"); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + return format; + } + + /** + * Format a {@link Date} as UTC timestamp. + * + * @param timestamp date + * @return formatted timestamp + */ + public static String format(Date timestamp) + { + return utc().format(timestamp); + } + + /** + * Parse a UTC timestamp. + * The timestamp needs to be provided in the form 'yyyy-MM-dd HH:mm:ss z'. + * + * @param utcTimestamp timestamp + * @return date + */ + public static Date parse(String utcTimestamp) + { + try + { + return utc().parse(utcTimestamp); + } + catch (ParseException e) + { + throw new IllegalArgumentException("Malformed UTC timestamp: " + utcTimestamp, e); + } + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyDecryptorBuilder.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyDecryptorBuilder.java new file mode 100644 index 0000000000..b19389b50e --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyDecryptorBuilder.java @@ -0,0 +1,9 @@ +package org.bouncycastle.openpgp.operator; + +import org.bouncycastle.openpgp.PGPException; + +public interface PBESecretKeyDecryptorBuilder +{ + PBESecretKeyDecryptor build(char[] passphrase) + throws PGPException; +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyDecryptorBuilderProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyDecryptorBuilderProvider.java new file mode 100644 index 0000000000..e94ddb551b --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/PBESecretKeyDecryptorBuilderProvider.java @@ -0,0 +1,15 @@ +package org.bouncycastle.openpgp.operator; + +import org.bouncycastle.openpgp.PGPException; + +/** + * Provider for {@link PBESecretKeyDecryptorBuilder} instances. + * The purpose of this class is to act as an abstract factory, whose subclasses can decide, which concrete + * implementation of {@link PBESecretKeyDecryptorBuilder} (builder for objects that can unlock encrypted + * secret keys) to return. + */ +public interface PBESecretKeyDecryptorBuilderProvider +{ + PBESecretKeyDecryptorBuilder provide() + throws PGPException; +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcCFBSecretKeyEncryptorFactory.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcCFBSecretKeyEncryptorFactory.java index 93bc4a3650..ccde1f85fa 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcCFBSecretKeyEncryptorFactory.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcCFBSecretKeyEncryptorFactory.java @@ -2,7 +2,6 @@ import org.bouncycastle.bcpg.HashAlgorithmTags; import org.bouncycastle.bcpg.PublicKeyPacket; -import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptorFactory; @@ -19,6 +18,16 @@ public class BcCFBSecretKeyEncryptorFactory implements PBESecretKeyEncryptorFactory { + private final int symmetricKeyAlgorithm; + private final int iterationCount; + + public BcCFBSecretKeyEncryptorFactory(int symmetricKeyAlgorithm, + int iterationCount) + { + this.symmetricKeyAlgorithm = symmetricKeyAlgorithm; + this.iterationCount = iterationCount; + } + @Override public PBESecretKeyEncryptor build(char[] passphrase, PublicKeyPacket pubKeyPacket) { @@ -38,9 +47,9 @@ public PBESecretKeyEncryptor build(char[] passphrase, PublicKeyPacket pubKeyPack } return new BcPBESecretKeyEncryptorBuilder( - SymmetricKeyAlgorithmTags.AES_256, + symmetricKeyAlgorithm, checksumCalc, - 0xff) // MAX iteration count + iterationCount) // MAX iteration count .build(passphrase); } } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBESecretKeyDecryptorBuilder.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBESecretKeyDecryptorBuilder.java index cd1d20b1c3..d95754c7df 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBESecretKeyDecryptorBuilder.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBESecretKeyDecryptorBuilder.java @@ -3,9 +3,11 @@ import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptorBuilder; import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider; public class BcPBESecretKeyDecryptorBuilder + implements PBESecretKeyDecryptorBuilder { private PGPDigestCalculatorProvider calculatorProvider; diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBESecretKeyDecryptorBuilderProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBESecretKeyDecryptorBuilderProvider.java new file mode 100644 index 0000000000..532ca3bced --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPBESecretKeyDecryptorBuilderProvider.java @@ -0,0 +1,14 @@ +package org.bouncycastle.openpgp.operator.bc; + +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptorBuilder; +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptorBuilderProvider; + +public class BcPBESecretKeyDecryptorBuilderProvider + implements PBESecretKeyDecryptorBuilderProvider +{ + @Override + public PBESecretKeyDecryptorBuilder provide() + { + return new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider()); + } +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaCFBSecretKeyEncryptorFactory.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaCFBSecretKeyEncryptorFactory.java index b7fca675f2..c8a576b7fe 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaCFBSecretKeyEncryptorFactory.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaCFBSecretKeyEncryptorFactory.java @@ -12,13 +12,17 @@ public class JcaCFBSecretKeyEncryptorFactory implements PBESecretKeyEncryptorFactory { + private final int symmetricKeyAlgorithm; + private final int iterationCount; private JcaPGPDigestCalculatorProviderBuilder digestCalcProviderBuilder = new JcaPGPDigestCalculatorProviderBuilder(); private JcePBESecretKeyEncryptorBuilder encBuilder; - public JcaCFBSecretKeyEncryptorFactory() + public JcaCFBSecretKeyEncryptorFactory(int symmetricKeyAlgorithm, int iterationCount) throws PGPException { + this.symmetricKeyAlgorithm = symmetricKeyAlgorithm; + this.iterationCount = iterationCount; encBuilder = builder(); } @@ -34,9 +38,9 @@ private JcePBESecretKeyEncryptorBuilder builder() throws PGPException { return new JcePBESecretKeyEncryptorBuilder( - SymmetricKeyAlgorithmTags.AES_256, + symmetricKeyAlgorithm, digestCalcProviderBuilder.build().get(HashAlgorithmTags.SHA1), - 0x60 + iterationCount ); } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBESecretKeyDecryptorBuilder.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBESecretKeyDecryptorBuilder.java index d99af1cc70..d08c9fc190 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBESecretKeyDecryptorBuilder.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBESecretKeyDecryptorBuilder.java @@ -15,9 +15,11 @@ import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPUtil; import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor; +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptorBuilder; import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider; public class JcePBESecretKeyDecryptorBuilder + implements PBESecretKeyDecryptorBuilder { private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); private PGPDigestCalculatorProvider calculatorProvider; diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBESecretKeyDecryptorBuilderProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBESecretKeyDecryptorBuilderProvider.java new file mode 100644 index 0000000000..8818fdf969 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcePBESecretKeyDecryptorBuilderProvider.java @@ -0,0 +1,37 @@ +package org.bouncycastle.openpgp.operator.jcajce; + +import java.security.Provider; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptorBuilder; +import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptorBuilderProvider; + +public class JcePBESecretKeyDecryptorBuilderProvider + implements PBESecretKeyDecryptorBuilderProvider +{ + private final JcaPGPDigestCalculatorProviderBuilder digestCalculatorProviderBuilder; + private Provider provider; + + public JcePBESecretKeyDecryptorBuilderProvider(JcaPGPDigestCalculatorProviderBuilder digestCalculatorProviderBuilder) + { + this.digestCalculatorProviderBuilder = digestCalculatorProviderBuilder; + } + + public JcePBESecretKeyDecryptorBuilderProvider setProvider(Provider provider) + { + this.provider = provider; + return this; + } + + @Override + public PBESecretKeyDecryptorBuilder provide() + throws PGPException + { + JcePBESecretKeyDecryptorBuilder b = new JcePBESecretKeyDecryptorBuilder(digestCalculatorProviderBuilder.build()); + if (provider != null) + { + b.setProvider(provider); + } + return b; + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/OpenPGPTestKeys.java b/pg/src/test/java/org/bouncycastle/openpgp/OpenPGPTestKeys.java new file mode 100644 index 0000000000..28c19bd875 --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/OpenPGPTestKeys.java @@ -0,0 +1,453 @@ +package org.bouncycastle.openpgp; + +import org.bouncycastle.bcpg.ArmoredInputStream; +import org.bouncycastle.bcpg.BCPGInputStream; +import org.bouncycastle.openpgp.bc.BcPGPObjectFactory; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +public class OpenPGPTestKeys +{ + /** + * Alice's Ed25519 OpenPGP key. + * + * @see + * Alice's OpenPGP Secret Key Material + */ + public static final String ALICE_KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + + "Comment: Alice's OpenPGP Transferable Secret Key\n" + + "Comment: https://www.ietf.org/id/draft-bre-openpgp-samples-01.html\n" + + "\n" + + "lFgEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U\n" + + "b7O1u10AAP9XBeW6lzGOLx7zHH9AsUDUTb2pggYGMzd0P3ulJ2AfvQ4RtCZBbGlj\n" + + "ZSBMb3ZlbGFjZSA8YWxpY2VAb3BlbnBncC5leGFtcGxlPoiQBBMWCAA4AhsDBQsJ\n" + + "CAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE64W7X6M6deFelE5j8jFVDE9H444FAl2l\n" + + "nzoACgkQ8jFVDE9H447pKwD6A5xwUqIDprBzrHfahrImaYEZzncqb25vkLV2arYf\n" + + "a78A/R3AwtLQvjxwLDuzk4dUtUwvUYibL2sAHwj2kGaHnfICnF0EXEcE6RIKKwYB\n" + + "BAGXVQEFAQEHQEL/BiGtq0k84Km1wqQw2DIikVYrQrMttN8d7BPfnr4iAwEIBwAA\n" + + "/3/xFPG6U17rhTuq+07gmEvaFYKfxRB6sgAYiW6TMTpQEK6IeAQYFggAIBYhBOuF\n" + + "u1+jOnXhXpROY/IxVQxPR+OOBQJcRwTpAhsMAAoJEPIxVQxPR+OOWdABAMUdSzpM\n" + + "hzGs1O0RkWNQWbUzQ8nUOeD9wNbjE3zR+yfRAQDbYqvtWQKN4AQLTxVJN5X5AWyb\n" + + "Pnn+We1aTBhaGa86AQ==\n" + + "=n8OM\n" + + "-----END PGP PRIVATE KEY BLOCK-----"; + /** + * Alice's Ed25519 OpenPGP v4 certificate. + * + * @see + * Alice's OpenPGP Certificate + */ + public static final String ALICE_CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "Comment: Alice's OpenPGP certificate\n" + + "Comment: https://www.ietf.org/id/draft-bre-openpgp-samples-01.html\n" + + "\n" + + "mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U\n" + + "b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE\n" + + "ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy\n" + + "MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO\n" + + "dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4\n" + + "OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s\n" + + "E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb\n" + + "DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn\n" + + "0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE=\n" + + "=iIGO\n" + + "-----END PGP PUBLIC KEY BLOCK-----"; + /** + * Alice's Ed25519 OpenPGP v4 revocation certificate. + * + * @see + * Alice's Revocation Certificate + */ + public static final String ALICE_REVOCATION_CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "Comment: Alice's revocation certificate\n" + + "Comment: https://www.ietf.org/id/draft-bre-openpgp-samples-01.html\n" + + "\n" + + "iHgEIBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXaWkOwIdAAAKCRDyMVUM\n" + + "T0fjjoBlAQDA9ukZFKRFGCooVcVoDVmxTaHLUXlIg9TPh2f7zzI9KgD/SLNXUOaH\n" + + "O6TozOS7C9lwIHwwdHdAxgf5BzuhLT9iuAM=\n" + + "=Tm8h\n" + + "-----END PGP PUBLIC KEY BLOCK-----"; + + /** + * Bob's RSA-3072 OpenPGP v4 Secret Key Material. + * + * @see + * Bob's OpenPGP Secret Key Material + */ + public static final String BOB_KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + + "Comment: D1A6 6E1A 23B1 82C9 980F 788C FBFC C82A 015E 7330\n" + + "Comment: Bob Babbage \n" + + "\n" + + "xcSYBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + + "/seOXpgecTdOcVttfzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz\n" + + "/56fW2O0F23qIRd8UUJp5IIlN4RDdRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/\n" + + "5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0kIzDxrEqW+7Ba8nocQlecMF3\n" + + "X5KMN5kp2zraLv9dlBBpWW43XktjcCZgMy20SouraVma8Je/ECwUWYUiAZxLIlMv\n" + + "9CurEOtxUw6N3RdOtLmYZS9uEnn5y1UkF88o8Nku890uk6BrewFzJyLAx5wRZ4F0\n" + + "qV/yq36UWQ0JB/AUGhHVPdFf6pl6eaxBwT5GXvbBUibtf8YI2og5RsgTWtXfU7eb\n" + + "SGXrl5ZMpbA6mbfhd0R8aPxWfmDWiIOhBufhMCvUHh1sApMKVZnvIff9/0Dca3wb\n" + + "vLIwa3T4CyshfT0AEQEAAQAL/RZqbJW2IqQDCnJi4Ozm++gPqBPiX1RhTWSjwxfM\n" + + "cJKUZfzLj414rMKm6Jh1cwwGY9jekROhB9WmwaaKT8HtcIgrZNAlYzANGRCM4TLK\n" + + "3VskxfSwKKna8l+s+mZglqbAjUg3wmFuf9Tj2xcUZYmyRm1DEmcN2ZzpvRtHgX7z\n" + + "Wn1mAKUlSDJZSQks0zjuMNbupcpyJokdlkUg2+wBznBOTKzgMxVNC9b2g5/tMPUs\n" + + "hGGWmF1UH+7AHMTaS6dlmr2ZBIyogdnfUqdNg5sZwsxSNrbglKP4sqe7X61uEAIQ\n" + + "bD7rT3LonLbhkrj3I8wilUD8usIwt5IecoHhd9HziqZjRCc1BUBkboUEoyedbDV4\n" + + "i4qfsFZ6CEWoLuD5pW7dEp0M+WeuHXO164Rc+LnH6i1VQrpb1Okl4qO6ejIpIjBI\n" + + "1t3GshtUu/mwGBBxs60KBX5g77mFQ9lLCRj8lSYqOsHRKBhUp4qM869VA+fD0BRP\n" + + "fqPT0I9IH4Oa/A3jYJcg622GwQYA1LhnP208Waf6PkQSJ6kyr8ymY1yVh9VBE/g6\n" + + "fRDYA+pkqKnw9wfH2Qho3ysAA+OmVOX8Hldg+Pc0Zs0e5pCavb0En8iFLvTA0Q2E\n" + + "LR5rLue9uD7aFuKFU/VdcddY9Ww/vo4k5p/tVGp7F8RYCFn9rSjIWbfvvZi1q5Tx\n" + + "+akoZbga+4qQ4WYzB/obdX6SCmi6BndcQ1QdjCCQU6gpYx0MddVERbIp9+2SXDyL\n" + + "hpxjSyz+RGsZi/9UAshT4txP4+MZBgDfK3ZqtW+h2/eMRxkANqOJpxSjMyLO/FXN\n" + + "WxzTDYeWtHNYiAlOwlQZEPOydZFty9IVzzNFQCIUCGjQ/nNyhw7adSgUk3+BXEx/\n" + + "MyJPYY0BYuhLxLYcrfQ9nrhaVKxRJj25SVHj2ASsiwGJRZW4CC3uw40OYxfKEvNC\n" + + "mer/VxM3kg8qqGf9KUzJ1dVdAvjyx2Hz6jY2qWCyRQ6IMjWHyd43C4r3jxooYKUC\n" + + "YnstRQyb/gCSKahveSEjo07CiXMr88UGALwzEr3npFAsPW3osGaFLj49y1oRe11E\n" + + "he9gCHFm+fuzbXrWmdPjYU5/ZdqdojzDqfu4ThfnipknpVUM1o6MQqkjM896FHm8\n" + + "zbKVFSMhEP6DPHSCexMFrrSgN03PdwHTO6iBaIBBFqmGY01tmJ03SxvSpiBPON9P\n" + + "NVvy/6UZFedTq8A07OUAxO62YUSNtT5pmK2vzs3SAZJmbFbMh+NN204TRI72GlqT\n" + + "t5hcfkuv8hrmwPS/ZR6q312mKQ6w/1pqO9qizSFCb2IgQmFiYmFnZSA8Ym9iQG9w\n" + + "ZW5wZ3AuZXhhbXBsZT7CwQ4EEwEKADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgEC\n" + + "F4AWIQTRpm4aI7GCyZgPeIz7/MgqAV5zMAUCXaWe+gAKCRD7/MgqAV5zMG9sC/9U\n" + + "2T3RrqEbw533FPNfEflhEVRIZ8gDXKM8hU6cqqEzCmzZT6xYTe6sv4y+PJBGXJFX\n" + + "yhj0g6FDkSyboM5litOcTupURObVqMgA/Y4UKERznm4fzzH9qek85c4ljtLyNufe\n" + + "doL2pp3vkGtn7eD0QFRaLLmnxPKQ/TlZKdLE1G3u8Uot8QHicaR6GnAdc5UXQJE3\n" + + "BiV7jZuDyWmZ1cUNwJkKL6oRtp+ZNDOQCrLNLecKHcgCqrpjSQG5oouba1I1Q6Vl\n" + + "sP44dhA1nkmLHtxlTOzpeHj4jnk1FaXmyasurrrI5CgU/L2Oi39DGKTH/A/cywDN\n" + + "4ZplIQ9zR8enkbXquUZvFDe+Xz+6xRXtb5MwQyWODB3nHw85HocLwRoIN9WdQEI+\n" + + "L8a/56AuOwhs8llkSuiITjR7r9SgKJC2WlAHl7E8lhJ3VDW3ELC56KH308d6mwOG\n" + + "ZRAqIAKzM1T5FGjMBhq7ZV0eqdEntBh3EcOIfj2M8rg1MzJv+0mHZOIjByawikbH\n" + + "xJgEXaWc8gEMANYwv1xsYyunXYK0X1vY/rP1NNPvhLyLIE7NpK90YNBj+xS1ldGD\n" + + "bUdZqZeef2xJe8gMQg05DoD1DF3GipZ0Ies65beh+d5hegb7N4pzh0LzrBrVNHar\n" + + "29b5ExdI7i4iYD5TO6Vr/qTUOiAN/byqELEzAb+L+b2DVz/RoCm4PIp1DU9ewcc2\n" + + "WB38Ofqut3nLYA5tqJ9XvAiEQme+qAVcM3ZFcaMt4I4dXhDZZNg+D9LiTWcxdUPB\n" + + "leu8iwDRjAgyAhPzpFp+nWoqWA81uIiULWD1Fj+IVoY3ZvgivoYOiEFBJ9lbb4te\n" + + "g9m5UT/AaVDTWuHzbspVlbiVe+qyB77C2daWzNyx6UYBPLOo4r0t0c91kbNE5lgj\n" + + "Z7xz6los0N1U8vq91EFSeQJoSQ62XWavYmlCLmdNT6BNfgh4icLsT7Vr1QMX9jzn\n" + + "JtTPxdXytSdHvpSpULsqJ016l0dtmONcK3z9mj5N5z0k1tg1AH970TGYOe2aUcSx\n" + + "IRDMXDOPyzEfjwARAQABAAv9F2CwsjS+Sjh1M1vegJbZjei4gF1HHpEM0K0PSXsp\n" + + "SfVvpR4AoSJ4He6CXSMWg0ot8XKtDuZoV9jnJaES5UL9pMAD7JwIOqZm/DYVJM5h\n" + + "OASCh1c356/wSbFbzRHPtUdZO9Q30WFNJM5pHbCJPjtNoRmRGkf71RxtvHBzy7np\n" + + "Ga+W6U/NVKHw0i0CYwMI0YlKDakYW3Pm+QL+gHZFvngGweTod0f9l2VLLAmeQR/c\n" + + "+EZs7lNumhuZ8mXcwhUc9JQIhOkpO+wreDysEFkAcsKbkQP3UDUsA1gFx9pbMzT0\n" + + "tr1oZq2a4QBtxShHzP/ph7KLpN+6qtjks3xB/yjTgaGmtrwM8tSe0wD1RwXS+/1o\n" + + "BHpXTnQ7TfeOGUAu4KCoOQLv6ELpKWbRBLWuiPwMdbGpvVFALO8+kvKAg9/r+/ny\n" + + "zM2GQHY+J3Jh5JxPiJnHfXNZjIKLbFbIPdSKNyJBuazXW8xIa//mEHMI5OcvsZBK\n" + + "clAIp7LXzjEjKXIwHwDcTn9pBgDpdOKTHOtJ3JUKx0rWVsDH6wq6iKV/FTVSY5jl\n" + + "zN+puOEsskF1Lfxn9JsJihAVO3yNsp6RvkKtyNlFazaCVKtDAmkjoh60XNxcNRqr\n" + + "gCnwdpbgdHP6v/hvZY54ZaJjz6L2e8unNEkYLxDt8cmAyGPgH2XgL7giHIp9jrsQ\n" + + "aS381gnYwNX6wE1aEikgtY91nqJjwPlibF9avSyYQoMtEqM/1UjTjB2KdD/MitK5\n" + + "fP0VpvuXpNYZedmyq4UOMwdkiNMGAOrfmOeT0olgLrTMT5H97Cn3Yxbk13uXHNu/\n" + + "ZUZZNe8s+QtuLfUlKAJtLEUutN33TlWQY522FV0m17S+b80xJib3yZVJteVurrh5\n" + + "HSWHAM+zghQAvCesg5CLXa2dNMkTCmZKgCBvfDLZuZbjFwnwCI6u/NhOY9egKuUf\n" + + "SA/je/RXaT8m5VxLYMxwqQXKApzD87fv0tLPlVIEvjEsaf992tFEFSNPcG1l/jpd\n" + + "5AVXw6kKuf85UkJtYR1x2MkQDrqY1QX/XMw00kt8y9kMZUre19aCArcmor+hDhRJ\n" + + "E3Gt4QJrD9z/bICESw4b4z2DbgD/Xz9IXsA/r9cKiM1h5QMtXvuhyfVeM01enhxM\n" + + "GbOH3gjqqGNKysx0UODGEwr6AV9hAd8RWXMchJLaExK9J5SRawSg671ObAU24SdY\n" + + "vMQ9Z4kAQ2+1ReUZzf3ogSMRZtMT+d18gT6L90/y+APZIaoArLPhebIAGq39HLmJ\n" + + "26x3z0WAgrpA1kNsjXEXkoiZGPLKIGoe3hrCwPYEGAEKACAWIQTRpm4aI7GCyZgP\n" + + "eIz7/MgqAV5zMAUCXaWc8gIbDAAKCRD7/MgqAV5zMOn/C/9ugt+HZIwX308zI+QX\n" + + "c5vDLReuzmJ3ieE0DMO/uNSC+K1XEioSIZP91HeZJ2kbT9nn9fuReuoff0T0Dief\n" + + "rbwcIQQHFFkrqSp1K3VWmUGp2JrUsXFVdjy/fkBIjTd7c5boWljv/6wAsSfiv2V0\n" + + "JSM8EFU6TYXxswGjFVfc6X97tJNeIrXL+mpSmPPqy2bztcCCHkWS5lNLWQw+R7Vg\n" + + "71Fe6yBSNVrqC2/imYG2J9zlowjx1XU63Wdgqp2Wxt0l8OmsB/W80S1fRF5G4SDH\n" + + "s9HXglXXqPsBRZJYfP+VStm9L5P/sKjCcX6WtZR7yS6G8zj/X767MLK/djANvpPd\n" + + "NVniEke6hM3CNBXYPAMhQBMWhCulcoz+0lxi8L34rMN+Dsbma96psdUrn7uLaB91\n" + + "6we0CTfF8qqm7BsVAgalon/UUiuMY80U3ueoj3okiSTiHIjD/YtpXSPioC8nMng7\n" + + "xqAY9Bwizt4FWgXuLm1a4+So4V9j1TRCXd12Uc2l2RNmgDE=\n" + + "=FAzO\n" + + "-----END PGP PRIVATE KEY BLOCK-----\n"; + /** + * Bob's RSA-3072 OpenPGP v4 Certificate. + * @see + * Bob's OpenPGP Certificate + */ + public static final String BOB_CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "Comment: D1A6 6E1A 23B1 82C9 980F 788C FBFC C82A 015E 7330\n" + + "Comment: Bob Babbage \n" + + "\n" + + "xsDNBF2lnPIBDAC5cL9PQoQLTMuhjbYvb4Ncuuo0bfmgPRFywX53jPhoFf4Zg6mv\n" + + "/seOXpgecTdOcVttfzC8ycIKrt3aQTiwOG/ctaR4Bk/t6ayNFfdUNxHWk4WCKzdz\n" + + "/56fW2O0F23qIRd8UUJp5IIlN4RDdRCtdhVQIAuzvp2oVy/LaS2kxQoKvph/5pQ/\n" + + "5whqsyroEWDJoSV0yOb25B/iwk/pLUFoyhDG9bj0kIzDxrEqW+7Ba8nocQlecMF3\n" + + "X5KMN5kp2zraLv9dlBBpWW43XktjcCZgMy20SouraVma8Je/ECwUWYUiAZxLIlMv\n" + + "9CurEOtxUw6N3RdOtLmYZS9uEnn5y1UkF88o8Nku890uk6BrewFzJyLAx5wRZ4F0\n" + + "qV/yq36UWQ0JB/AUGhHVPdFf6pl6eaxBwT5GXvbBUibtf8YI2og5RsgTWtXfU7eb\n" + + "SGXrl5ZMpbA6mbfhd0R8aPxWfmDWiIOhBufhMCvUHh1sApMKVZnvIff9/0Dca3wb\n" + + "vLIwa3T4CyshfT0AEQEAAc0hQm9iIEJhYmJhZ2UgPGJvYkBvcGVucGdwLmV4YW1w\n" + + "bGU+wsEOBBMBCgA4AhsDBQsJCAcCBhUKCQgLAgQWAgMBAh4BAheAFiEE0aZuGiOx\n" + + "gsmYD3iM+/zIKgFeczAFAl2lnvoACgkQ+/zIKgFeczBvbAv/VNk90a6hG8Od9xTz\n" + + "XxH5YRFUSGfIA1yjPIVOnKqhMwps2U+sWE3urL+MvjyQRlyRV8oY9IOhQ5Esm6DO\n" + + "ZYrTnE7qVETm1ajIAP2OFChEc55uH88x/anpPOXOJY7S8jbn3naC9qad75BrZ+3g\n" + + "9EBUWiy5p8TykP05WSnSxNRt7vFKLfEB4nGkehpwHXOVF0CRNwYle42bg8lpmdXF\n" + + "DcCZCi+qEbafmTQzkAqyzS3nCh3IAqq6Y0kBuaKLm2tSNUOlZbD+OHYQNZ5Jix7c\n" + + "ZUzs6Xh4+I55NRWl5smrLq66yOQoFPy9jot/Qxikx/wP3MsAzeGaZSEPc0fHp5G1\n" + + "6rlGbxQ3vl8/usUV7W+TMEMljgwd5x8POR6HC8EaCDfVnUBCPi/Gv+egLjsIbPJZ\n" + + "ZEroiE40e6/UoCiQtlpQB5exPJYSd1Q1txCwueih99PHepsDhmUQKiACszNU+RRo\n" + + "zAYau2VdHqnRJ7QYdxHDiH49jPK4NTMyb/tJh2TiIwcmsIpGzsDNBF2lnPIBDADW\n" + + "ML9cbGMrp12CtF9b2P6z9TTT74S8iyBOzaSvdGDQY/sUtZXRg21HWamXnn9sSXvI\n" + + "DEINOQ6A9QxdxoqWdCHrOuW3ofneYXoG+zeKc4dC86wa1TR2q9vW+RMXSO4uImA+\n" + + "Uzula/6k1DogDf28qhCxMwG/i/m9g1c/0aApuDyKdQ1PXsHHNlgd/Dn6rrd5y2AO\n" + + "baifV7wIhEJnvqgFXDN2RXGjLeCOHV4Q2WTYPg/S4k1nMXVDwZXrvIsA0YwIMgIT\n" + + "86Rafp1qKlgPNbiIlC1g9RY/iFaGN2b4Ir6GDohBQSfZW2+LXoPZuVE/wGlQ01rh\n" + + "827KVZW4lXvqsge+wtnWlszcselGATyzqOK9LdHPdZGzROZYI2e8c+paLNDdVPL6\n" + + "vdRBUnkCaEkOtl1mr2JpQi5nTU+gTX4IeInC7E+1a9UDF/Y85ybUz8XV8rUnR76U\n" + + "qVC7KidNepdHbZjjXCt8/Zo+Tec9JNbYNQB/e9ExmDntmlHEsSEQzFwzj8sxH48A\n" + + "EQEAAcLA9gQYAQoAIBYhBNGmbhojsYLJmA94jPv8yCoBXnMwBQJdpZzyAhsMAAoJ\n" + + "EPv8yCoBXnMw6f8L/26C34dkjBffTzMj5Bdzm8MtF67OYneJ4TQMw7+41IL4rVcS\n" + + "KhIhk/3Ud5knaRtP2ef1+5F66h9/RPQOJ5+tvBwhBAcUWSupKnUrdVaZQanYmtSx\n" + + "cVV2PL9+QEiNN3tzluhaWO//rACxJ+K/ZXQlIzwQVTpNhfGzAaMVV9zpf3u0k14i\n" + + "tcv6alKY8+rLZvO1wIIeRZLmU0tZDD5HtWDvUV7rIFI1WuoLb+KZgbYn3OWjCPHV\n" + + "dTrdZ2CqnZbG3SXw6awH9bzRLV9EXkbhIMez0deCVdeo+wFFklh8/5VK2b0vk/+w\n" + + "qMJxfpa1lHvJLobzOP9fvrswsr92MA2+k901WeISR7qEzcI0Fdg8AyFAExaEK6Vy\n" + + "jP7SXGLwvfisw34OxuZr3qmx1Sufu4toH3XrB7QJN8XyqqbsGxUCBqWif9RSK4xj\n" + + "zRTe56iPeiSJJOIciMP9i2ldI+KgLycyeDvGoBj0HCLO3gVaBe4ubVrj5KjhX2PV\n" + + "NEJd3XZRzaXZE2aAMQ==\n" + + "=F9yX\n" + + "-----END PGP PUBLIC KEY BLOCK-----\n"; + /** + * Bob's RSA-3072 Revocation Certificate. + * @see + * Bob's Revocation Certificate + */ + public static final String BOB_REVOCATION_CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "Comment: Bob's revocation certificate\n" + + "Comment: https://www.ietf.org/id/draft-bre-openpgp-samples-01.html\n" + + "\n" + + "iQG2BCABCgAgFiEE0aZuGiOxgsmYD3iM+/zIKgFeczAFAl2lnQQCHQAACgkQ+/zI\n" + + "KgFeczAIHAv/RrlGlPFKsW0BShC8sVtPfbT1N9lUqyrsgBhrUryM/i+rBtkbnSjp\n" + + "28R5araupt0og1g2L5VsCRM+ql0jf0zrZXOorKfAO70HCP3X+MlEquvztMUZGJRZ\n" + + "7TSMgIY1MeFgLmOw9pDKf3tSoouBOpPe5eVfXviEDDo2zOfdntjPyCMlxHgAcjZo\n" + + "XqMaurV+nKWoIx0zbdpNLsRy4JZcmnOSFdPw37R8U2miPi2qNyVwcyCxQy0LjN7Y\n" + + "AWadrs9vE0DrneSVP2OpBhl7g+Dj2uXJQRPVXcq6w9g5Fir6DnlhekTLsa78T5cD\n" + + "n8q7aRusMlALPAOosENOgINgsVcjuILkPN1eD+zGAgHgdiKaep1+P3pbo5n0CLki\n" + + "UCAsLnCEo8eBV9DCb/n1FlI5yhQhgQyMYlp/49H0JSc3IY9KHhv6f0zIaRWs0JuD\n" + + "ajcXTJ9AyB+SA6GBb9Q+XsNXjZ1gj75ekUD1sQ3ezTvVfovgP5bD+vPvILhSImKB\n" + + "aU6V3zld/x/1\n" + + "=mMwU\n" + + "-----END PGP PUBLIC KEY BLOCK-----"; + + /** + * Carol's OpenPGP v4 key. + */ + public static final String CAROL_KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + + "\n" + + "xcQTBF3+CmgRDADZhdKTM3ms3XpXnQke83FgaIBtP1g1qhqpCfg50WiPS0kjiMC0\n" + + "OJz2vh59nusbBLzgI//Y1VMhKfIWYbqMcIY+lWbseHjl52rqW6AaJ0TH4NgVt7vh\n" + + "yVeJt0k/NnxvNhMd0587KXmfpDxrwBqc/l5cVB+p0rL8vs8kxojHXAi5V3koM0Uj\n" + + "REWs5Jpj/XU9LhEoyXZkeJC/pes1u6UKoFYn7dFIP49Kkd1kb+1bNfdPYtA0JpcG\n" + + "zYgeMNOvdWJwn43dNhxoeuXfmAEhA8LdzT0C0O+7akXOKWrfhXJ8MTBqvPgWZYx7\n" + + "MNuQx/ejIMZHl+Iaf7hG976ILH+NCGiKkhidd9GIuA/WteHiQbXLyfiQ4n8P12q9\n" + + "+4dq6ybUM65tnozRyyN+1m3rU2a/+Ly3JCh4TeO27w+cxMWkaeHyTQaJVMbMbDpX\n" + + "duVd32MA33UVNH5/KXMVczVi5asVjuKDSojJDV1QwX8izZNl1t+AI0L3balCabV0\n" + + "SFhlfnBEUj1my1sBAMOSO/I67BvBS3IPHZWXHjgclhs26mPzRlZLryAUWR2DDACH\n" + + "5fx+yUAdZ8Vu/2zWTHxwWJ/X6gGTLqa9CmfDq5UDqYFFzuWwN4HJ+ryOuak1CGwS\n" + + "KJUBSA75HExbv0naWg+suy+pEDvF0VALPU9VUkSQtHyR10YO2FWOe3AEtpbYDRwp\n" + + "dr1ZwEbb3L6IGQ5i/4CNHbJ2u3yUeXsDNAvrpVSEcIjA01RPCOKmf58SDZp4yDdP\n" + + "xGhM8w6a18+fdQr22f2cJ0xgfPlbzFbO+FUsEgKvn6QTLhbaYw4zs7rdQDejWHV8\n" + + "2hP4K+rb9FwknYdV9uo4m77MgGlU+4yvJnGEYaL3jwjI3bH9aooNOl6XbvVAzNzo\n" + + "mYmaTO7mp6xFAu43yuGyd9K+1E4k7CQTROxTZ+RdtQjV95hSsEmMg792nQvDSBW4\n" + + "xwfOQ7pf3kC7r9fm8u9nBlEN12HsbQ8Yvux/ld5q5RaIlD19jzfVR6+hJzbj2ZnU\n" + + "yQs4ksAfIHTzTdLttRxS9lTRTkVx2vbUnoSBy6TYF1mf6nRPpSm1riZxnkR4+BQL\n" + + "/0rUAxwegTNIG/5M612s2a45QvYK1turZ7spI1RGitJUIjBXUuR76jIsyqagIhBl\n" + + "5nEsQ4HLv8OQ3EgJ5T9gldLFpHNczLxBQnnNwfPoD2e0kC/iy0rfiNX8HWpTgQpb\n" + + "zAosLj5/E0iNlildynIhuqBosyRWFqGva0O6qioL90srlzlfKCloe9R9w3HizjCb\n" + + "f59yEspuJt9iHVNOPOW2Wj5ub0KTiJPp9vBmrFaB79/IlgojpQoYvQ77Hx5A9CJq\n" + + "paMCHGOW6Uz9euN1ozzETEkIPtL8XAxcogfpe2JKE1uS7ugxsKEGEDfxOQFKAGV0\n" + + "XFtIx50vFCr2vQro0WB858CGN47dCxChhNUxNtGc11JNEkNv/X7hKtRf/5VCmnaz\n" + + "GWwNK47cqZ7GJfEBnElD7s/tQvTC5Qp7lg9gEt47TUX0bjzUTCxNvLosuKL9+J1W\n" + + "ln1myRpff/5ZOAnZTPHR+AbX4bRB4sK5zijQe4139Dn2oRYK+EIYoBAxFxSOzehP\n" + + "IQAA/2BCN5HryGjVff2t7Q6fVrQQS9hsMisszZl5rWwUOO6zETHCigQfEQgAPAUC\n" + + "Xf4KaQMLCQoJEJunidx21oSaBBUKCQgCFgECF4ACGwMCHgEWIQRx/9oARAnl3bDD\n" + + "6PGbp4ncdtaEmgAAYoUA/1VpxdR2wYT/pC8FrKsbmIxLJRLDNlED3ihivWp/B2e/\n" + + "AQCT2oi9zqbjprCKAnzoIYTGTil4yFfmeey8GjMOxUHz4M0mQ2Fyb2wgT2xkc3R5\n" + + "bGUgPGNhcm9sQG9wZW5wZ3AuZXhhbXBsZT7CigQTEQgAPAUCXf4KaQMLCQoJEJun\n" + + "idx21oSaBBUKCQgCFgECF4ACGwMCHgEWIQRx/9oARAnl3bDD6PGbp4ncdtaEmgAA\n" + + "UEwA/2TFwL0mymjCSaQH8KdQuygI+itpNggM+Y8FF8hn9fo1AP9ogDIl9V3C8t59\n" + + "C/Mrc4HvP1ABR2nwZeK5+A5lLoH4Y8fD8QRd/gpoEAwA2YXSkzN5rN16V50JHvNx\n" + + "YGiAbT9YNaoaqQn4OdFoj0tJI4jAtDic9r4efZ7rGwS84CP/2NVTISnyFmG6jHCG\n" + + "PpVm7Hh45edq6lugGidEx+DYFbe74clXibdJPzZ8bzYTHdOfOyl5n6Q8a8AanP5e\n" + + "XFQfqdKy/L7PJMaIx1wIuVd5KDNFI0RFrOSaY/11PS4RKMl2ZHiQv6XrNbulCqBW\n" + + "J+3RSD+PSpHdZG/tWzX3T2LQNCaXBs2IHjDTr3VicJ+N3TYcaHrl35gBIQPC3c09\n" + + "AtDvu2pFzilq34VyfDEwarz4FmWMezDbkMf3oyDGR5fiGn+4Rve+iCx/jQhoipIY\n" + + "nXfRiLgP1rXh4kG1y8n4kOJ/D9dqvfuHausm1DOubZ6M0csjftZt61Nmv/i8tyQo\n" + + "eE3jtu8PnMTFpGnh8k0GiVTGzGw6V3blXd9jAN91FTR+fylzFXM1YuWrFY7ig0qI\n" + + "yQ1dUMF/Is2TZdbfgCNC922pQmm1dEhYZX5wRFI9ZstbDACH5fx+yUAdZ8Vu/2zW\n" + + "THxwWJ/X6gGTLqa9CmfDq5UDqYFFzuWwN4HJ+ryOuak1CGwSKJUBSA75HExbv0na\n" + + "Wg+suy+pEDvF0VALPU9VUkSQtHyR10YO2FWOe3AEtpbYDRwpdr1ZwEbb3L6IGQ5i\n" + + "/4CNHbJ2u3yUeXsDNAvrpVSEcIjA01RPCOKmf58SDZp4yDdPxGhM8w6a18+fdQr2\n" + + "2f2cJ0xgfPlbzFbO+FUsEgKvn6QTLhbaYw4zs7rdQDejWHV82hP4K+rb9FwknYdV\n" + + "9uo4m77MgGlU+4yvJnGEYaL3jwjI3bH9aooNOl6XbvVAzNzomYmaTO7mp6xFAu43\n" + + "yuGyd9K+1E4k7CQTROxTZ+RdtQjV95hSsEmMg792nQvDSBW4xwfOQ7pf3kC7r9fm\n" + + "8u9nBlEN12HsbQ8Yvux/ld5q5RaIlD19jzfVR6+hJzbj2ZnUyQs4ksAfIHTzTdLt\n" + + "tRxS9lTRTkVx2vbUnoSBy6TYF1mf6nRPpSm1riZxnkR4+BQL/jEGmn1tLhxfjfDA\n" + + "5vFFj73+FXdFCdFKSI0VpdoU1fgR5DX72ZQUYYUCKYTYikXv1mqdH/5VthptrktC\n" + + "oAco4zVxM04sK7Xthl+uTOhei8/Dd9ZLdSIoNcRjrr/uh5sUzUfIC9iuT3SXiZ/D\n" + + "0yVq0Uu/gWPB3ZIG/sFacxOXAr6RYhvz9MqnwXS1sVT5TyO3XIQ5JseIgIRyV/Sf\n" + + "4F/4Qui9wMzzSajTwCsttMGKf67k228AaJVv+IpFoo+OtCa7wbJukqfNQN3m2ojf\n" + + "V5CcoCzsoRsoTInhrpQmM+gGoQBXBArT1xk3KK3VdZibYfMoxeIGXw0MoNJzFuGK\n" + + "+PcnhV3ETFMNcszd0Pb9s86g7hYtpRmE12Jlai2MzPSmyztlsRP9tcZwYy7JdPZf\n" + + "xXQP24XWat7eP2qWxTnkEP4/wKYb81m7CZ4RvUO/nd1aA5c9IBYknbgmCAAKvHVD\n" + + "iTY61E5GbC9aTiI4WIwjItroikukUJE+p77rpjxfw/1U51BnmQAA/ih5jIthn2ZE\n" + + "r1YoOsUs8CBhylTsRZK6VS4ZCErcyl2tD2LCigQYEQgAPAUCXf4KaQMLCQoJEJun\n" + + "idx21oSaBBUKCQgCFgECF4ACGwwCHgEWIQRx/9oARAnl3bDD6PGbp4ncdtaEmgAA\n" + + "QSkA/3WEWqZxvZmpVxpEMxJWaGQRwUhGake8OhC1WfywCtarAQCLwfBsyEv5jBEi\n" + + "1FkOSekLi8WNMdUx3XMyvP8nJ65P2Q==\n" + + "=Xj8h\n" + + "-----END PGP PRIVATE KEY BLOCK-----\n"; + /** + * Carol's OpenPGP v4 certificate. + */ + public static final String CAROL_CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "\n" + + "xsPuBF3+CmgRDADZhdKTM3ms3XpXnQke83FgaIBtP1g1qhqpCfg50WiPS0kjiMC0\n" + + "OJz2vh59nusbBLzgI//Y1VMhKfIWYbqMcIY+lWbseHjl52rqW6AaJ0TH4NgVt7vh\n" + + "yVeJt0k/NnxvNhMd0587KXmfpDxrwBqc/l5cVB+p0rL8vs8kxojHXAi5V3koM0Uj\n" + + "REWs5Jpj/XU9LhEoyXZkeJC/pes1u6UKoFYn7dFIP49Kkd1kb+1bNfdPYtA0JpcG\n" + + "zYgeMNOvdWJwn43dNhxoeuXfmAEhA8LdzT0C0O+7akXOKWrfhXJ8MTBqvPgWZYx7\n" + + "MNuQx/ejIMZHl+Iaf7hG976ILH+NCGiKkhidd9GIuA/WteHiQbXLyfiQ4n8P12q9\n" + + "+4dq6ybUM65tnozRyyN+1m3rU2a/+Ly3JCh4TeO27w+cxMWkaeHyTQaJVMbMbDpX\n" + + "duVd32MA33UVNH5/KXMVczVi5asVjuKDSojJDV1QwX8izZNl1t+AI0L3balCabV0\n" + + "SFhlfnBEUj1my1sBAMOSO/I67BvBS3IPHZWXHjgclhs26mPzRlZLryAUWR2DDACH\n" + + "5fx+yUAdZ8Vu/2zWTHxwWJ/X6gGTLqa9CmfDq5UDqYFFzuWwN4HJ+ryOuak1CGwS\n" + + "KJUBSA75HExbv0naWg+suy+pEDvF0VALPU9VUkSQtHyR10YO2FWOe3AEtpbYDRwp\n" + + "dr1ZwEbb3L6IGQ5i/4CNHbJ2u3yUeXsDNAvrpVSEcIjA01RPCOKmf58SDZp4yDdP\n" + + "xGhM8w6a18+fdQr22f2cJ0xgfPlbzFbO+FUsEgKvn6QTLhbaYw4zs7rdQDejWHV8\n" + + "2hP4K+rb9FwknYdV9uo4m77MgGlU+4yvJnGEYaL3jwjI3bH9aooNOl6XbvVAzNzo\n" + + "mYmaTO7mp6xFAu43yuGyd9K+1E4k7CQTROxTZ+RdtQjV95hSsEmMg792nQvDSBW4\n" + + "xwfOQ7pf3kC7r9fm8u9nBlEN12HsbQ8Yvux/ld5q5RaIlD19jzfVR6+hJzbj2ZnU\n" + + "yQs4ksAfIHTzTdLttRxS9lTRTkVx2vbUnoSBy6TYF1mf6nRPpSm1riZxnkR4+BQL\n" + + "/0rUAxwegTNIG/5M612s2a45QvYK1turZ7spI1RGitJUIjBXUuR76jIsyqagIhBl\n" + + "5nEsQ4HLv8OQ3EgJ5T9gldLFpHNczLxBQnnNwfPoD2e0kC/iy0rfiNX8HWpTgQpb\n" + + "zAosLj5/E0iNlildynIhuqBosyRWFqGva0O6qioL90srlzlfKCloe9R9w3HizjCb\n" + + "f59yEspuJt9iHVNOPOW2Wj5ub0KTiJPp9vBmrFaB79/IlgojpQoYvQ77Hx5A9CJq\n" + + "paMCHGOW6Uz9euN1ozzETEkIPtL8XAxcogfpe2JKE1uS7ugxsKEGEDfxOQFKAGV0\n" + + "XFtIx50vFCr2vQro0WB858CGN47dCxChhNUxNtGc11JNEkNv/X7hKtRf/5VCmnaz\n" + + "GWwNK47cqZ7GJfEBnElD7s/tQvTC5Qp7lg9gEt47TUX0bjzUTCxNvLosuKL9+J1W\n" + + "ln1myRpff/5ZOAnZTPHR+AbX4bRB4sK5zijQe4139Dn2oRYK+EIYoBAxFxSOzehP\n" + + "IcKKBB8RCAA8BQJd/gppAwsJCgkQm6eJ3HbWhJoEFQoJCAIWAQIXgAIbAwIeARYh\n" + + "BHH/2gBECeXdsMPo8Zunidx21oSaAABihQD/VWnF1HbBhP+kLwWsqxuYjEslEsM2\n" + + "UQPeKGK9an8HZ78BAJPaiL3OpuOmsIoCfOghhMZOKXjIV+Z57LwaMw7FQfPgzSZD\n" + + "YXJvbCBPbGRzdHlsZSA8Y2Fyb2xAb3BlbnBncC5leGFtcGxlPsKKBBMRCAA8BQJd\n" + + "/gppAwsJCgkQm6eJ3HbWhJoEFQoJCAIWAQIXgAIbAwIeARYhBHH/2gBECeXdsMPo\n" + + "8Zunidx21oSaAABQTAD/ZMXAvSbKaMJJpAfwp1C7KAj6K2k2CAz5jwUXyGf1+jUA\n" + + "/2iAMiX1XcLy3n0L8ytzge8/UAFHafBl4rn4DmUugfhjzsPMBF3+CmgQDADZhdKT\n" + + "M3ms3XpXnQke83FgaIBtP1g1qhqpCfg50WiPS0kjiMC0OJz2vh59nusbBLzgI//Y\n" + + "1VMhKfIWYbqMcIY+lWbseHjl52rqW6AaJ0TH4NgVt7vhyVeJt0k/NnxvNhMd0587\n" + + "KXmfpDxrwBqc/l5cVB+p0rL8vs8kxojHXAi5V3koM0UjREWs5Jpj/XU9LhEoyXZk\n" + + "eJC/pes1u6UKoFYn7dFIP49Kkd1kb+1bNfdPYtA0JpcGzYgeMNOvdWJwn43dNhxo\n" + + "euXfmAEhA8LdzT0C0O+7akXOKWrfhXJ8MTBqvPgWZYx7MNuQx/ejIMZHl+Iaf7hG\n" + + "976ILH+NCGiKkhidd9GIuA/WteHiQbXLyfiQ4n8P12q9+4dq6ybUM65tnozRyyN+\n" + + "1m3rU2a/+Ly3JCh4TeO27w+cxMWkaeHyTQaJVMbMbDpXduVd32MA33UVNH5/KXMV\n" + + "czVi5asVjuKDSojJDV1QwX8izZNl1t+AI0L3balCabV0SFhlfnBEUj1my1sMAIfl\n" + + "/H7JQB1nxW7/bNZMfHBYn9fqAZMupr0KZ8OrlQOpgUXO5bA3gcn6vI65qTUIbBIo\n" + + "lQFIDvkcTFu/SdpaD6y7L6kQO8XRUAs9T1VSRJC0fJHXRg7YVY57cAS2ltgNHCl2\n" + + "vVnARtvcvogZDmL/gI0dsna7fJR5ewM0C+ulVIRwiMDTVE8I4qZ/nxINmnjIN0/E\n" + + "aEzzDprXz591CvbZ/ZwnTGB8+VvMVs74VSwSAq+fpBMuFtpjDjOzut1AN6NYdXza\n" + + "E/gr6tv0XCSdh1X26jibvsyAaVT7jK8mcYRhovePCMjdsf1qig06Xpdu9UDM3OiZ\n" + + "iZpM7uanrEUC7jfK4bJ30r7UTiTsJBNE7FNn5F21CNX3mFKwSYyDv3adC8NIFbjH\n" + + "B85Dul/eQLuv1+by72cGUQ3XYextDxi+7H+V3mrlFoiUPX2PN9VHr6EnNuPZmdTJ\n" + + "CziSwB8gdPNN0u21HFL2VNFORXHa9tSehIHLpNgXWZ/qdE+lKbWuJnGeRHj4FAv+\n" + + "MQaafW0uHF+N8MDm8UWPvf4Vd0UJ0UpIjRWl2hTV+BHkNfvZlBRhhQIphNiKRe/W\n" + + "ap0f/lW2Gm2uS0KgByjjNXEzTiwrte2GX65M6F6Lz8N31kt1Iig1xGOuv+6HmxTN\n" + + "R8gL2K5PdJeJn8PTJWrRS7+BY8Hdkgb+wVpzE5cCvpFiG/P0yqfBdLWxVPlPI7dc\n" + + "hDkmx4iAhHJX9J/gX/hC6L3AzPNJqNPAKy20wYp/ruTbbwBolW/4ikWij460JrvB\n" + + "sm6Sp81A3ebaiN9XkJygLOyhGyhMieGulCYz6AahAFcECtPXGTcordV1mJth8yjF\n" + + "4gZfDQyg0nMW4Yr49yeFXcRMUw1yzN3Q9v2zzqDuFi2lGYTXYmVqLYzM9KbLO2Wx\n" + + "E/21xnBjLsl09l/FdA/bhdZq3t4/apbFOeQQ/j/AphvzWbsJnhG9Q7+d3VoDlz0g\n" + + "FiSduCYIAAq8dUOJNjrUTkZsL1pOIjhYjCMi2uiKS6RQkT6nvuumPF/D/VTnUGeZ\n" + + "wooEGBEIADwFAl3+CmkDCwkKCRCbp4ncdtaEmgQVCgkIAhYBAheAAhsMAh4BFiEE\n" + + "cf/aAEQJ5d2ww+jxm6eJ3HbWhJoAAEEpAP91hFqmcb2ZqVcaRDMSVmhkEcFIRmpH\n" + + "vDoQtVn8sArWqwEAi8HwbMhL+YwRItRZDknpC4vFjTHVMd1zMrz/JyeuT9k=\n" + + "=pa/S\n" + + "-----END PGP PUBLIC KEY BLOCK-----\n"; + + /** + * Minimal OpenPGP v6 key. + * @see + * Sample Version 6 Secret Key + */ + public static final String V6_KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + + "\n" + + "xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB\n" + + "exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ\n" + + "BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6\n" + + "2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh\n" + + "RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe\n" + + "7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/\n" + + "LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG\n" + + "GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6\n" + + "2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE\n" + + "M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr\n" + + "k0mXubZvyl4GBg==\n" + + "-----END PGP PRIVATE KEY BLOCK-----"; + /** + * Locked, minimal OpenPGP v6 key. + * @see + * Sample Locked Version 6 Secret Key + */ + public static final String V6_KEY_LOCKED = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" + + "\n" + + "xYIGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laP9JgkC\n" + + "FARdb9ccngltHraRe25uHuyuAQQVtKipJ0+r5jL4dacGWSAheCWPpITYiyfyIOPS\n" + + "3gIDyg8f7strd1OB4+LZsUhcIjOMpVHgmiY/IutJkulneoBYwrEGHxsKAAAAQgWC\n" + + "Y4d/4wMLCQcFFQoOCAwCFgACmwMCHgkiIQbLGGxPBgmml+TVLfpscisMHx4nwYpW\n" + + "cI9lJewnutmsyQUnCQIHAgAAAACtKCAQPi19In7A5tfORHHbNr/JcIMlNpAnFJin\n" + + "7wV2wH+q4UWFs7kDsBJ+xP2i8CMEWi7Ha8tPlXGpZR4UruETeh1mhELIj5UeM8T/\n" + + "0z+5oX1RHu11j8bZzFDLX9eTsgOdWATHggZjh3/jGQAAACCGkySDZ/nlAV25Ivj0\n" + + "gJXdp4SYfy1ZhbEvutFsr15ENf0mCQIUBA5hhGgp2oaavg6mFUXcFMwBBBUuE8qf\n" + + "9Ock+xwusd+GAglBr5LVyr/lup3xxQvHXFSjjA2haXfoN6xUGRdDEHI6+uevKjVR\n" + + "v5oAxgu7eJpaXNjCmwYYGwoAAAAsBYJjh3/jApsMIiEGyxhsTwYJppfk1S36bHIr\n" + + "DB8eJ8GKVnCPZSXsJ7rZrMkAAAAABAEgpukYbZ1ZNfyP5WMUzbUnSGpaUSD5t2Ki\n" + + "Nacp8DkBClZRa2c3AMQzSDXa9jGhYzxjzVb5scHDzTkjyRZWRdTq8U6L4da+/+Kt\n" + + "ruh8m7Xo2ehSSFyWRSuTSZe5tm/KXgYG\n" + + "-----END PGP PRIVATE KEY BLOCK-----"; + /** + * Passphrase to unlock {@link #V6_KEY_LOCKED} with. + */ + public static final String V6_KEY_LOCKED_PASSPHRASE = "correct horse battery staple"; + /** + * Sample Version 6 Certificate. + * @see + * Sample Version 6 Certificate + */ + public static final String V6_CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "\n" + + "xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf\n" + + "GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy\n" + + "KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw\n" + + "gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE\n" + + "QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn\n" + + "+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh\n" + + "BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8\n" + + "j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805\n" + + "I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg==\n" + + "-----END PGP PUBLIC KEY BLOCK-----"; + + public static PGPPublicKeyRing readPGPPublicKeyRing(String armor) + throws IOException + { + ByteArrayInputStream bIn = new ByteArrayInputStream(armor.getBytes()); + ArmoredInputStream aIn = new ArmoredInputStream(bIn); + BCPGInputStream pIn = new BCPGInputStream(aIn); + PGPObjectFactory objFac = new BcPGPObjectFactory(pIn); + PGPPublicKeyRing publicKeys = (PGPPublicKeyRing) objFac.nextObject(); + pIn.close(); + aIn.close(); + return publicKeys; + } + + public static PGPSecretKeyRing readPGPSecretKeyRing(String armor) + throws IOException + { + ByteArrayInputStream bIn = new ByteArrayInputStream(armor.getBytes()); + ArmoredInputStream aIn = new ArmoredInputStream(bIn); + BCPGInputStream pIn = new BCPGInputStream(aIn); + PGPObjectFactory objFac = new BcPGPObjectFactory(pIn); + PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) objFac.nextObject(); + pIn.close(); + aIn.close(); + return secretKeys; + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/APITest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/APITest.java new file mode 100644 index 0000000000..2a597d8e9e --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/APITest.java @@ -0,0 +1,32 @@ +package org.bouncycastle.openpgp.api.test; + +import org.bouncycastle.bcpg.test.AbstractPacketTest; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.bc.BcOpenPGPApi; +import org.bouncycastle.openpgp.api.jcajce.JcaOpenPGPApi; + +import java.io.IOException; +import java.util.Date; + +public abstract class APITest + extends AbstractPacketTest +{ + @Override + public void performTest() + throws Exception + { + performTestWith(new BcOpenPGPApi()); + performTestWith(new JcaOpenPGPApi(new BouncyCastleProvider())); + } + + public Date currentTimeRounded() + { + Date now = new Date(); + return new Date((now.getTime() / 1000) * 1000); // rounded to seconds + } + + protected abstract void performTestWith(OpenPGPApi api) + throws PGPException, IOException; +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/AllTests.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/AllTests.java new file mode 100644 index 0000000000..7d3004c6ab --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/AllTests.java @@ -0,0 +1,67 @@ +package org.bouncycastle.openpgp.api.test; + +import java.security.Security; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.test.PrintTestResult; +import org.bouncycastle.util.test.SimpleTestResult; + +public class AllTests + extends TestCase +{ + public void testAPI() + { + Security.addProvider(new BouncyCastleProvider()); + + org.bouncycastle.util.test.Test[] tests = RegressionTest.tests; + + for (int i = 0; i != tests.length; i++) + { + SimpleTestResult result = (SimpleTestResult)tests[i].perform(); + + if (!result.isSuccessful()) + { + fail(result.toString()); + } + } + } + + + public static void main(String[] args) + { + PrintTestResult.printResult(junit.textui.TestRunner.run(suite())); + } + + public static Test suite() + { + TestSuite suite = new TestSuite("OpenPGP Packet Tests"); + + suite.addTestSuite(AllTests.class); + + return new BCPacketTests(suite); + } + + static class BCPacketTests + extends TestSetup + { + public BCPacketTests(Test test) + { + super(test); + } + + protected void setUp() + { + Security.addProvider(new BouncyCastleProvider()); + } + + protected void tearDown() + { + Security.removeProvider("BC"); + } + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/ChangeKeyPassphraseTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/ChangeKeyPassphraseTest.java new file mode 100644 index 0000000000..d4a6fe23cf --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/ChangeKeyPassphraseTest.java @@ -0,0 +1,118 @@ +package org.bouncycastle.openpgp.api.test; + +import java.io.IOException; + +import org.bouncycastle.bcpg.SecretKeyPacket; +import org.bouncycastle.openpgp.OpenPGPTestKeys; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.OpenPGPKey; + +public class ChangeKeyPassphraseTest + extends APITest +{ + @Override + protected void performTestWith(OpenPGPApi api) + throws PGPException, IOException + { + removeAEADPassphrase(api); + addAEADPassphrase(api); + changeAEADPassphrase(api); + + testChangingCFBPassphrase(api); + } + + private void removeAEADPassphrase(OpenPGPApi api) + throws IOException, PGPException { + OpenPGPKey key = api.readKeyOrCertificate() + .parseKey(OpenPGPTestKeys.V6_KEY_LOCKED); + + OpenPGPKey.OpenPGPSecretKey secretKey = key.getPrimarySecretKey(); + isTrue("Expect test key to be locked initially", secretKey.isLocked()); + OpenPGPKey.OpenPGPPrivateKey privateKey = secretKey.unlock(OpenPGPTestKeys.V6_KEY_LOCKED_PASSPHRASE.toCharArray()); + OpenPGPKey.OpenPGPSecretKey unlocked = privateKey.removePassphrase(); + isFalse("Expect key to be unlocked after unlocking - duh", unlocked.isLocked()); + + OpenPGPKey expected = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); + isEncodingEqual("Expect unlocked key encoding to equal the unprotected test vector", + expected.getPrimarySecretKey().getPGPSecretKey().getEncoded(), + unlocked.getPGPSecretKey().getEncoded()); + } + + private void addAEADPassphrase(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); + OpenPGPKey.OpenPGPSecretKey secretKey = key.getPrimarySecretKey(); + isFalse("Expect unlocked test vector to be unlocked", secretKey.isLocked()); + + OpenPGPKey.OpenPGPPrivateKey privateKey = secretKey.unlock(); + OpenPGPKey.OpenPGPSecretKey locked = privateKey.changePassphrase( + "sw0rdf1sh".toCharArray(), + api.getImplementation(), + true); + isTrue("Expect test key to be locked after locking", locked.isLocked()); + isEquals("Expect locked key to use AEAD", + SecretKeyPacket.USAGE_AEAD, locked.getPGPSecretKey().getS2KUsage()); + isTrue("Expect key to be unlockable with used passphrase", + locked.isPassphraseCorrect("sw0rdf1sh".toCharArray())); + } + + private void changeAEADPassphrase(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate() + .parseKey(OpenPGPTestKeys.V6_KEY_LOCKED); + + OpenPGPKey.OpenPGPSecretKey secretKey = key.getPrimarySecretKey(); + isTrue("Expect locked test vector to be locked initially", + secretKey.isLocked()); + OpenPGPKey.OpenPGPPrivateKey privateKey = secretKey.unlock(OpenPGPTestKeys.V6_KEY_LOCKED_PASSPHRASE.toCharArray()); + OpenPGPKey.OpenPGPSecretKey relocked = privateKey.changePassphrase("sw0rdf1sh".toCharArray()); + isTrue("Expect key to still be locked after changing passphrase", relocked.isLocked()); + isTrue("Expect key to be unlockable with used passphrase", + relocked.isPassphraseCorrect("sw0rdf1sh".toCharArray())); + isEquals("Expect re-locked key to use AEAD", + relocked.getPGPSecretKey().getS2KUsage(), SecretKeyPacket.USAGE_AEAD); + } + + private void testChangingCFBPassphrase(OpenPGPApi api) + throws PGPException, IOException + { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.ALICE_KEY); + + OpenPGPKey.OpenPGPSecretKey secretKey = key.getPrimarySecretKey(); + isFalse("Expect Alice' key to not be locked initially", secretKey.isLocked()); + + OpenPGPKey.OpenPGPPrivateKey privateKey = secretKey.unlock(); + OpenPGPKey.OpenPGPSecretKey locked = privateKey.changePassphrase( + "sw0rdf1sh".toCharArray(), api.getImplementation(), false); + isTrue("Expect Alice' key to be locked after locking", locked.isLocked()); + isEquals("Expect CFB mode to be used for locking, since we did not use AEAD.", + locked.getPGPSecretKey().getS2KUsage(), SecretKeyPacket.USAGE_SHA1); + isTrue("Expect key to be unlockable with used passphrase", + locked.isPassphraseCorrect("sw0rdf1sh".toCharArray())); + + privateKey = locked.unlock("sw0rdf1sh".toCharArray()); + OpenPGPKey.OpenPGPSecretKey relocked = privateKey.changePassphrase("0r4ng3".toCharArray()); + isEquals("Expect CFB to be used after changing passphrase of CFB-protected key", + relocked.getPGPSecretKey().getS2KUsage(), SecretKeyPacket.USAGE_SHA1); + isTrue("Expect key to be unlockable with new passphrase", + relocked.isPassphraseCorrect("0r4ng3".toCharArray())); + + privateKey = relocked.unlock("0r4ng3".toCharArray()); + OpenPGPKey.OpenPGPSecretKey unlocked = privateKey.removePassphrase(); + isFalse("Expect key to be unlocked after removing passphrase", unlocked.isLocked()); + } + + @Override + public String getName() + { + return "ChangeKeyPassphraseTest"; + } + + public static void main(String[] args) + { + runTest(new ChangeKeyPassphraseTest()); + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java new file mode 100644 index 0000000000..1b797c5b4d --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java @@ -0,0 +1,886 @@ +package org.bouncycastle.openpgp.api.test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.List; + +import org.bouncycastle.bcpg.ArmoredInputStream; +import org.bouncycastle.bcpg.BCPGInputStream; +import org.bouncycastle.bcpg.KeyIdentifier; +import org.bouncycastle.bcpg.SignatureSubpacketTags; +import org.bouncycastle.bcpg.sig.Features; +import org.bouncycastle.bcpg.sig.KeyFlags; +import org.bouncycastle.openpgp.OpenPGPTestKeys; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPObjectFactory; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureList; +import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.OpenPGPCertificate; +import org.bouncycastle.openpgp.api.OpenPGPKey; +import org.bouncycastle.openpgp.api.OpenPGPKeyGenerator; +import org.bouncycastle.openpgp.api.SignatureParameters; +import org.bouncycastle.openpgp.api.SignatureSubpacketsFunction; +import org.bouncycastle.openpgp.api.util.UTCUtil; +import org.bouncycastle.openpgp.bc.BcPGPObjectFactory; + +public class OpenPGPCertificateTest + extends APITest +{ + + @Override + public String getName() + { + return "OpenPGPCertificateTest"; + } + + @Override + protected void performTestWith(OpenPGPApi api) + throws IOException, PGPException + { + testOpenPGPv6Key(api); + + testBaseCasePrimaryKeySigns(api); + testBaseCaseSubkeySigns(api); + testPKSignsPKRevokedNoSubpacket(api); + testSKSignsPKRevokedNoSubpacket(api); + testPKSignsPKRevocationSuperseded(api); + testGetPrimaryUserId(api); + } + + private void testOpenPGPv6Key(OpenPGPApi api) + throws IOException + { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); + + isTrue("Test key has no identities", key.getIdentities().isEmpty()); + + OpenPGPCertificate.OpenPGPPrimaryKey primaryKey = key.getPrimaryKey(); + isEquals("Primary key identifier mismatch", + new KeyIdentifier("CB186C4F0609A697E4D52DFA6C722B0C1F1E27C18A56708F6525EC27BAD9ACC9"), + primaryKey.getKeyIdentifier()); + OpenPGPKey.OpenPGPSecretKey secretPrimaryKey = key.getSecretKey(primaryKey); + isTrue("Secret Primary key MUST have reference to its public component", + primaryKey == secretPrimaryKey.getPublicKey()); + isTrue("Primary key is expected to be signing key", primaryKey.isSigningKey()); + isTrue("Primary secret key is expected to be signing key", secretPrimaryKey.isSigningKey()); + isTrue("Primary secret key is expected to be certification key", secretPrimaryKey.isCertificationKey()); + isTrue("Primary key is expected to be certification key", primaryKey.isCertificationKey()); + + List signingKeys = key.getSigningKeys(); + isEquals("Expected exactly 1 signing key", 1, signingKeys.size()); + OpenPGPCertificate.OpenPGPPrimaryKey signingKey = (OpenPGPCertificate.OpenPGPPrimaryKey) signingKeys.get(0); + isEquals("Signing key is expected to be the same as primary key", primaryKey, signingKey); + + Features signingKeyFeatures = signingKey.getFeatures(); + // Features are extracted from direct-key signature + isEquals("Signing key features mismatch. Expect features to be extracted from DK signature.", + Features.FEATURE_MODIFICATION_DETECTION | Features.FEATURE_SEIPD_V2, + signingKeyFeatures.getFeatures()); + + List encryptionKeys = key.getEncryptionKeys(); + isEquals("Expected exactly 1 encryption key", 1, encryptionKeys.size()); + OpenPGPCertificate.OpenPGPSubkey encryptionKey = (OpenPGPCertificate.OpenPGPSubkey) encryptionKeys.get(0); + isTrue("Subkey MUST be encryption key", encryptionKey.isEncryptionKey()); + isEquals("Encryption subkey identifier mismatch", + new KeyIdentifier("12C83F1E706F6308FE151A417743A1F033790E93E9978488D1DB378DA9930885"), + encryptionKey.getKeyIdentifier()); + + KeyFlags encryptionKeyFlags = encryptionKey.getKeyFlags(); + // Key Flags are extracted from subkey-binding signature + isEquals("Encryption key flag mismatch. Expected key flags to be extracted from SB sig.", + KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, + encryptionKeyFlags.getFlags()); + + Features encryptionKeyFeatures = encryptionKey.getFeatures(); + // Features are extracted from direct-key signature + isEquals("Encryption key features mismatch. Expected features to be extracted from DK sig.", + Features.FEATURE_MODIFICATION_DETECTION | Features.FEATURE_SEIPD_V2, + encryptionKeyFeatures.getFeatures()); + } + + private void testBaseCasePrimaryKeySigns(OpenPGPApi api) + throws IOException + { + // https://sequoia-pgp.gitlab.io/openpgp-interoperability-test-suite/results.html#Key_revocation_test__primary_key_signs_and_is_not_revoked__base_case_ + String cert = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "\n" + + "xsBNBFpJegABCACzr1V+GxVkrtfDjihYK+HtyEIcO52uw7O2kd7JbduYp4RK17jy\n" + + "75N3EnsgmiIkSxXCWr+rTtonNs1zCJeUa/gwnNfs7mVgjL2rMOZU/KZ4MP0yOYU5\n" + + "u5FjNPWz8hpFQ9GKqfdj0Op61h1pCQO45IjUQ3dCDj9Rfn44zHMB1ZrbmIH9nTR1\n" + + "YIGHWmdm0LItb2WxIkwzWBAJ5acTlsmLyZZEQ1+8NDqktyzwFoQqTJvLU4StY2k6\n" + + "h18ZKZdPyrdLoEyOuWkvjxmbhDk1Gt5KiS/yy7mrzIPLr0dmJe4vc8WLV+bXoyNE\n" + + "x3H8o9CFcYehLfyqsy40lg92d6Kp96ww8dZ5ABEBAAHCwMQEHwEKAHgFgl4L4QAJ\n" + + "EAitUcrkcPAGRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9y\n" + + "Z4csZe1ah1tj2AjxfdDMsH2wvSEwZjb/73ICKnm7BySQAhUKApsDAh4BFiEE4yy2\n" + + "2oICkbfnbbGoCK1RyuRw8AYAAGYFCACiKnCb2NBZa/Jj1aJe4R2rxPZj2ERXWe3b\n" + + "JKNPKT7K0rVDkTw1JRiTfCsuAY2lY9sKJdhQZl+azXm64vvTc6hEGRQ/+XssDlE2\n" + + "DIn8C34HDc495ZnryHNB8Dd5l1HdjqxfGIY6HBPJUdx0dedwP42Oisg9t5KsC8zl\n" + + "d/+MIRgzkp+Dg0LXJVnDuwWEPoo2N6WhAr5ReLvXxALX5ht9Lb3lP0DASZvAKy9B\n" + + "O/wRCr294J8dg/CowAfloyf0Ko+JjyjanmZn3acy5CGkVN2mc+PFUekGZDDy5ooY\n" + + "kgXO/CmApuTNvabct+A7IVVdWWM5SWb90JvaV9SWji6nQphVm7StwsDEBB8BCgB4\n" + + "BYJaSXoACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lh\n" + + "LXBncC5vcmfVZdjLYZxDX2hvy3aGrsE4i0avLDMzf3e9kVHmaD6PAgIVCgKbAwIe\n" + + "ARYhBOMsttqCApG3522xqAitUcrkcPAGAABQYwgArfIRxq95npUKAOPXs25nZlvy\n" + + "+xQbrmsTxHhAYW8eGFcz82QwumoqrR8VfrojxM+eCZdTI85nM5kzznYDU2+cMhsZ\n" + + "Vm5+VhGZy3e3QH4J/E31D7t1opCvj5g1eRJ4LgywB+cYGcZBYp/bQT9SUYuhZH2O\n" + + "XCR04qSbpVUCIApnhBHxKNtOlqjAkHeaOdW/8XePsbfvrtVOLGYgrZXfY7Nqy3+W\n" + + "zbdm8UvVPFXH+uHEzTgyvYbnJBYkjORmCqUKs860PL8ekeg+sL4PHSRj1UUfwcQD\n" + + "55q0m3Vtew2KiIUi4wKi5LceDtprjoO5utU/1YfEAiNMeSQHXKq83dpazvjrUs0S\n" + + "anVsaWV0QGV4YW1wbGUub3JnwsDEBBMBCgB4BYJaSXoACRAIrVHK5HDwBkcUAAAA\n" + + "AAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmc6Rix7CeIfWwnaQjk3\n" + + "bBrkAiY7jS9N+shuRdHZ0gKKsgIVCgKbAwIeARYhBOMsttqCApG3522xqAitUcrk\n" + + "cPAGAACf9QgAsxtfAbyGbtofjrXTs9lsKEWvGgk02fSYyKjPbyaRqh72MlIlUXwq\n" + + "q1ih2TJc3vwF8aNVDrcb9DnBabdt2M1vI3PUaeG31BmakC/XZCNCrbbJkyd/vdML\n" + + "qw7prLrp0auVNNhLYxOK9usXbClNxluo4i/lSFVo5B9ai+ne1kKKiplzqy2qqhde\n" + + "plomcwGHbB1CkZ04DmCMbSSFAGxYqUC/bBm0bolCebw/KIz9sEojNKt6mvsFN67/\n" + + "hMYeJS0HVlwwc6i8iKSzC2D53iywhtvkdiKECXQeXDf9zNXAn1wpK01SLJ0iig7c\n" + + "DFrtoqkfPYzbNfC0bt34fNx9iz3w9aEH8c7ATQRaSsuAAQgAu5yau9psltmWiUn7\n" + + "fsRSqbQInO0iWnu4DK9IXB3ghNYMcii3JJEjHzgIxGf3GiJEjzubyRQaX5J/p7yB\n" + + "1fOH8z7FYUuax1saGf9c1/b02N9gyXNlHam31hNaaL3ffFczI95p7MNrTtroTt5o\n" + + "Zqsc+i+oKLZn7X0YAI4tEYwhSnUQYB/F7YqkkI4eV+7CxZPA8pBhXiAOK/zn416P\n" + + "sZ6JS5wsM65yCtOHcAAIBnKDnC+bQi+f1WZesSocy/rXx3QEQmodDu3ojhS+VxcY\n" + + "GeZCUcFF0FyZBIkGjHIVQLyOfjP3FRJ4qFXMz9/YIVoM4Y6guTERMTEj/KDG4BP7\n" + + "RfJHTQARAQABwsI8BBgBCgHwBYJeC+EACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0\n" + + "QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmfcAa1ZPWTtg60w3Oo4dt4Fa8cKFYbZ\n" + + "YsqDSHV5pwEfMwKbAsC8oAQZAQoAbwWCXgvhAAkQEPy8/w6Op5FHFAAAAAAAHgAg\n" + + "c2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnL6I2+VyN5T1FoVgj3cdnMLYC\n" + + "pcB5i/FRSCVKybuLzrgWIQTOphDQhPpR8hHhxGwQ/Lz/Do6nkQAArk8H/AhjM9lq\n" + + "bffFL6RRR4HTjelspy4A3nyTicCljrDuXDUh23GfLvajTR5h16ZBqAF7cpb9rrlz\n" + + "1C1WcS5JLVxzXAe7f+KOfXu+eyLhpTzZ8VT3pK3hHGaYwlVlXrBZP0JXgL8hm6hD\n" + + "SXZQZtcpsnQ1uIHC9ONxUB4liNFhTqQCQYdQJFiFs1umUbo/C4KdzlDI08bM3CqE\n" + + "Kat9vUFuGG68mDg0CrRZEWt946L5i8kZmBUkSShIm2k5e2qE/muYeM6qKQNsxlx3\n" + + "VIf5eUhtxCi9fg7SjvHkdUSFstYcxAdaohWCFCEsDJI12hzcKQazSjvtKF4BNBKg\n" + + "X/wLsbVQnYLd9ggWIQTjLLbaggKRt+dtsagIrVHK5HDwBgAANjMH/1MY7DJyxkiT\n" + + "jc/jzmnVxqtHOZDCSmUqk0eh/6BHs+ostWqkGC6+7dfxDnptwcqandYey4KF2ajt\n" + + "4nOwu0xQw/NEF3i81h7IiewY7G+YT69DUd+DvVUQemfKNYVOrMqoH7QU5o4YojdJ\n" + + "iDeIp2d/JyJrqyof78JFAHnNZgHC2T2zo9E54dnOTY9VNUNCOUct5Rby0GXjTIUR\n" + + "O0f485eGuZxVWdLRllDYOiCrQHPSHhrxHVXVMbYJoroPy+IyaJanVoAWgyipBmmI\n" + + "DV8aINM2RLMsGkuPTRtITI2ZlGOQN7xgy4LqWzjPnrzMXfwBEDx/nrwdG6zEGMK8\n" + + "AkVkMT5uJJvCwjwEGAEKAfAFglro/4AJEAitUcrkcPAGRxQAAAAAAB4AIHNhbHRA\n" + + "bm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ/Q0Z6WDH2+8/F1xEEuiApsjnn2lGNZ2\n" + + "DeIaklJzdqQOApsCwLygBBkBCgBvBYJa6P+ACRAQ/Lz/Do6nkUcUAAAAAAAeACBz\n" + + "YWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmfrVATyX3tgcM2z41fqYquxVhJR\n" + + "avN6+w2SU4xEG++SqBYhBM6mENCE+lHyEeHEbBD8vP8OjqeRAABGVggAsB8M2KI5\n" + + "cxXKKgVHL1dEfzg9halVavktfcT6ZVC/+aDp94tvBCL16Guhq4ccN7DATrWx430/\n" + + "GecY6E77qvhDzmCclSbdLbiZmsrVX9kCmTfrJzFQ64KfvIS5GgbL21+ZJ+pKW2HO\n" + + "MBGn6sgAPmTqM5UsDCpsEKDt5CJcJr3sTc8D9NhEnc0dKsQ91+n9ms3W5tyyE6r9\n" + + "pyM6ThBCMhbQkR7hE9XWAQeO1ILSFGnie0aFcTU0Oo0wL1MaiSyA/8XpKq23xfx1\n" + + "kNS9hQkdq0aWehNoTJdCt1Nq1cWABy2rQR0x+qhGWowfsAjnBautxvet28t2kPCA\n" + + "IMniYpWc89BwfhYhBOMsttqCApG3522xqAitUcrkcPAGAACq1gf/Q7H9Re5SWk+U\n" + + "On/NQPRedf544YJ/YdQnve/hSaPGL33cUzf4yxzFILnK19Ird5f8/mTT1pg99L3i\n" + + "xE3N5031JJKwFpCB69Rsysg88ZLDL2VLc3xdsAQdUbVaCqeRHKwtMtpBvbAFvF9p\n" + + "lwam0SSXHHr/JkYm5ufXN6I8ib/nwr1bFbf/Se0Wuk9RG4ne9JUBCrGxakyVd+Og\n" + + "LLhvzOmJa7fDC0uUZhTKFbjMxLhaas4HFYiRbfz2T0xz9gyDytDWsEFM+XoKHlEH\n" + + "8Fx/U2B5/8N0Q+pIFoEuOmBO+5EPvPIlxNByHgiaNIuKt1Mu+UAb2Spl6D5zbDfX\n" + + "/3vqxdhYHw==\n" + + "=Ric2\n" + + "-----END PGP PUBLIC KEY BLOCK-----\n"; + TestSignature t0 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJYaEaACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmeoPMfalw2oS7uyOKnOXJSN8Gx7pr/BMlo3Xn8nTgx6\n" + + "ORYhBOMsttqCApG3522xqAitUcrkcPAGAABXbAf/WfWaQYNuATAKwxYrJx4fd5kt\n" + + "0M6sn1q7wK1MIxursG2+FuKafV25O9+pde8Nog77OEgegwk+HokOVFpVXfOzHQjs\n" + + "8dwWTtTQlX5NIBNvtqS7cvCKhjsqaHKgmzsenMjCEbpDZ3C5CoqcYicykqEU/Ia0\n" + + "ZGC4lzRByrgNy/w+/iLN748S707bzBLVc/sE73k9N5pANAlE+cA/sHI1Gp2WxJR9\n" + + "t2Fk4x6/85PEnF1RHI16p/wSEeuRaBpyw9QGZBbVDVt5wvgttxZjteGGSwBM3WI/\n" + + "gPfC0LW+JQ2W+dwY0PN/7yuARVRhXpKiBI4xqp7x3OanQX6quU77g3B8nXAt3A==\n" + + "=StqT\n" + + "-----END PGP SIGNATURE-----\n", false, "Sig predates primary key"); + TestSignature t1 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJa564ACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmfM0EN4Ei0bQv6UO9BRq2wtUfV948cRynRMBb8TSGCG\n" + + "tBYhBOMsttqCApG3522xqAitUcrkcPAGAAAlNwf+L0KQK9i/xmYKOMV2EX13QUoZ\n" + + "vvb/pHGZaCQ9JtvEF2l2DT0DqByZ+tOv5Y4isU+un7CraoyvyajAwR0Yqk937B6C\n" + + "HQHKMkmIl+5R4/xqSoWYmOidbrgilojPMBEhB3INQ8/THjjFijtLzitVhnWBd7+u\n" + + "s0kcqnWnOdx2By4aDe+UEiyCfSE02e/0tIsM71RqiU91zH6dl6+q8nml7PsYuTFV\n" + + "V09oQTbBuuvUe+YgN/uvyKVIsA64lQ+YhqEeIA8Quek7fHhW+du9OIhSPsbYodyx\n" + + "VWMTXwSWKGNvZNAkpmgUYqFjS2Cx5ZUWblZLjrNKBwnnmt50qvUN7+o2pjlnfA==\n" + + "=UuXb\n" + + "-----END PGP SIGNATURE-----\n", true); + TestSignature t2 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJdP4iACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmfFzYGoiuSjN+gz1IDD4ZvRXGuPTHks0/pIiGY90mrZ\n" + + "WxYhBOMsttqCApG3522xqAitUcrkcPAGAABGPAf/ck7tJAFoPIDd9fTPZANpNGoW\n" + + "Fq6VuNfy/nLjz2gkHFX/lLAxQ0N3McIdRA++Ik/omb0lis3R2DVNgwqNm2OF34HE\n" + + "qxmPmrQHBgk2q0fDH4NCE0XnYQjQT65V99IfiaQu+oS3Mq8MuYsDYvRVvRKMwt49\n" + + "fcDnvFtAtCqEETdv6wV5cUZmdQ3L9NU9bApJ0jk+EHVdpfTUIbOYYGnsIe/4Aa0d\n" + + "jgzu4Em79ynosOn//953XJ7OO8LCDi1EKt+nFuZARUlt/Jwwull6zzp7HUPw6HPt\n" + + "Upp7os8TIPC4STwoSeEKaxEkrbMGFnDcoDajnKKRt5+MkB24Oq7PHvnzgnPpVg==\n" + + "=Ljv7\n" + + "-----END PGP SIGNATURE-----\n", true); + TestSignature t3 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJmhTYiCRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmfbjQf/zfoJQT0hhna4RDjOESBLgGaCbc5HLeo751F4\n" + + "NxYhBOMsttqCApG3522xqAitUcrkcPAGAABqBQgAkkNmYf6yLPvox+ZayrLtMb9D\n" + + "ghgt0nau72DSazsJ6SAq2QqIdr0RRhRa2gCETkp4PpeoDWmIvoVj35ZnfyeO/jqy\n" + + "HECvRwO0WPA5FXQM6uG7s40vDTRFjlJMpPyHWnn2igcR64iDxBGmc40xi9CcmJP9\n" + + "tmA26+1Nzj1LcfNvknKZ2UIOmnXiZY0QssIdyqsmJrdFpXs4UCLUzdXkfFLoxksU\n" + + "mk4B6hig2IKMj5mnbWy/JQSXtjjI+HHmtzgWfXs7d9iQ61CklbtCOiPeWxvoqlGG\n" + + "oK1wV1olcSar/RPKTlMmQpAg9dztQgrNs1oF7EF3i9kwNP7I5JzekPiOLH6oMw==\n" + + "=5KMU\n" + + "-----END PGP SIGNATURE-----\n", true); + + signatureValidityTest(api, cert, t0, t1, t2, t3); + } + + private void testBaseCaseSubkeySigns(OpenPGPApi api) + throws IOException + { + // https://sequoia-pgp.gitlab.io/openpgp-interoperability-test-suite/results.html#Key_revocation_test__subkey_signs__primary_key_is_not_revoked__base_case_ + String cert = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "\n" + + "xsBNBFpJegABCACzr1V+GxVkrtfDjihYK+HtyEIcO52uw7O2kd7JbduYp4RK17jy\n" + + "75N3EnsgmiIkSxXCWr+rTtonNs1zCJeUa/gwnNfs7mVgjL2rMOZU/KZ4MP0yOYU5\n" + + "u5FjNPWz8hpFQ9GKqfdj0Op61h1pCQO45IjUQ3dCDj9Rfn44zHMB1ZrbmIH9nTR1\n" + + "YIGHWmdm0LItb2WxIkwzWBAJ5acTlsmLyZZEQ1+8NDqktyzwFoQqTJvLU4StY2k6\n" + + "h18ZKZdPyrdLoEyOuWkvjxmbhDk1Gt5KiS/yy7mrzIPLr0dmJe4vc8WLV+bXoyNE\n" + + "x3H8o9CFcYehLfyqsy40lg92d6Kp96ww8dZ5ABEBAAHCwMQEHwEKAHgFgl4L4QAJ\n" + + "EAitUcrkcPAGRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9y\n" + + "Z4csZe1ah1tj2AjxfdDMsH2wvSEwZjb/73ICKnm7BySQAhUKApsDAh4BFiEE4yy2\n" + + "2oICkbfnbbGoCK1RyuRw8AYAAGYFCACiKnCb2NBZa/Jj1aJe4R2rxPZj2ERXWe3b\n" + + "JKNPKT7K0rVDkTw1JRiTfCsuAY2lY9sKJdhQZl+azXm64vvTc6hEGRQ/+XssDlE2\n" + + "DIn8C34HDc495ZnryHNB8Dd5l1HdjqxfGIY6HBPJUdx0dedwP42Oisg9t5KsC8zl\n" + + "d/+MIRgzkp+Dg0LXJVnDuwWEPoo2N6WhAr5ReLvXxALX5ht9Lb3lP0DASZvAKy9B\n" + + "O/wRCr294J8dg/CowAfloyf0Ko+JjyjanmZn3acy5CGkVN2mc+PFUekGZDDy5ooY\n" + + "kgXO/CmApuTNvabct+A7IVVdWWM5SWb90JvaV9SWji6nQphVm7StwsDEBB8BCgB4\n" + + "BYJaSXoACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lh\n" + + "LXBncC5vcmfVZdjLYZxDX2hvy3aGrsE4i0avLDMzf3e9kVHmaD6PAgIVCgKbAwIe\n" + + "ARYhBOMsttqCApG3522xqAitUcrkcPAGAABQYwgArfIRxq95npUKAOPXs25nZlvy\n" + + "+xQbrmsTxHhAYW8eGFcz82QwumoqrR8VfrojxM+eCZdTI85nM5kzznYDU2+cMhsZ\n" + + "Vm5+VhGZy3e3QH4J/E31D7t1opCvj5g1eRJ4LgywB+cYGcZBYp/bQT9SUYuhZH2O\n" + + "XCR04qSbpVUCIApnhBHxKNtOlqjAkHeaOdW/8XePsbfvrtVOLGYgrZXfY7Nqy3+W\n" + + "zbdm8UvVPFXH+uHEzTgyvYbnJBYkjORmCqUKs860PL8ekeg+sL4PHSRj1UUfwcQD\n" + + "55q0m3Vtew2KiIUi4wKi5LceDtprjoO5utU/1YfEAiNMeSQHXKq83dpazvjrUs0S\n" + + "anVsaWV0QGV4YW1wbGUub3JnwsDEBBMBCgB4BYJaSXoACRAIrVHK5HDwBkcUAAAA\n" + + "AAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmc6Rix7CeIfWwnaQjk3\n" + + "bBrkAiY7jS9N+shuRdHZ0gKKsgIVCgKbAwIeARYhBOMsttqCApG3522xqAitUcrk\n" + + "cPAGAACf9QgAsxtfAbyGbtofjrXTs9lsKEWvGgk02fSYyKjPbyaRqh72MlIlUXwq\n" + + "q1ih2TJc3vwF8aNVDrcb9DnBabdt2M1vI3PUaeG31BmakC/XZCNCrbbJkyd/vdML\n" + + "qw7prLrp0auVNNhLYxOK9usXbClNxluo4i/lSFVo5B9ai+ne1kKKiplzqy2qqhde\n" + + "plomcwGHbB1CkZ04DmCMbSSFAGxYqUC/bBm0bolCebw/KIz9sEojNKt6mvsFN67/\n" + + "hMYeJS0HVlwwc6i8iKSzC2D53iywhtvkdiKECXQeXDf9zNXAn1wpK01SLJ0iig7c\n" + + "DFrtoqkfPYzbNfC0bt34fNx9iz3w9aEH8c7ATQRaSsuAAQgAu5yau9psltmWiUn7\n" + + "fsRSqbQInO0iWnu4DK9IXB3ghNYMcii3JJEjHzgIxGf3GiJEjzubyRQaX5J/p7yB\n" + + "1fOH8z7FYUuax1saGf9c1/b02N9gyXNlHam31hNaaL3ffFczI95p7MNrTtroTt5o\n" + + "Zqsc+i+oKLZn7X0YAI4tEYwhSnUQYB/F7YqkkI4eV+7CxZPA8pBhXiAOK/zn416P\n" + + "sZ6JS5wsM65yCtOHcAAIBnKDnC+bQi+f1WZesSocy/rXx3QEQmodDu3ojhS+VxcY\n" + + "GeZCUcFF0FyZBIkGjHIVQLyOfjP3FRJ4qFXMz9/YIVoM4Y6guTERMTEj/KDG4BP7\n" + + "RfJHTQARAQABwsI8BBgBCgHwBYJeC+EACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0\n" + + "QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmfcAa1ZPWTtg60w3Oo4dt4Fa8cKFYbZ\n" + + "YsqDSHV5pwEfMwKbAsC8oAQZAQoAbwWCXgvhAAkQEPy8/w6Op5FHFAAAAAAAHgAg\n" + + "c2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnL6I2+VyN5T1FoVgj3cdnMLYC\n" + + "pcB5i/FRSCVKybuLzrgWIQTOphDQhPpR8hHhxGwQ/Lz/Do6nkQAArk8H/AhjM9lq\n" + + "bffFL6RRR4HTjelspy4A3nyTicCljrDuXDUh23GfLvajTR5h16ZBqAF7cpb9rrlz\n" + + "1C1WcS5JLVxzXAe7f+KOfXu+eyLhpTzZ8VT3pK3hHGaYwlVlXrBZP0JXgL8hm6hD\n" + + "SXZQZtcpsnQ1uIHC9ONxUB4liNFhTqQCQYdQJFiFs1umUbo/C4KdzlDI08bM3CqE\n" + + "Kat9vUFuGG68mDg0CrRZEWt946L5i8kZmBUkSShIm2k5e2qE/muYeM6qKQNsxlx3\n" + + "VIf5eUhtxCi9fg7SjvHkdUSFstYcxAdaohWCFCEsDJI12hzcKQazSjvtKF4BNBKg\n" + + "X/wLsbVQnYLd9ggWIQTjLLbaggKRt+dtsagIrVHK5HDwBgAANjMH/1MY7DJyxkiT\n" + + "jc/jzmnVxqtHOZDCSmUqk0eh/6BHs+ostWqkGC6+7dfxDnptwcqandYey4KF2ajt\n" + + "4nOwu0xQw/NEF3i81h7IiewY7G+YT69DUd+DvVUQemfKNYVOrMqoH7QU5o4YojdJ\n" + + "iDeIp2d/JyJrqyof78JFAHnNZgHC2T2zo9E54dnOTY9VNUNCOUct5Rby0GXjTIUR\n" + + "O0f485eGuZxVWdLRllDYOiCrQHPSHhrxHVXVMbYJoroPy+IyaJanVoAWgyipBmmI\n" + + "DV8aINM2RLMsGkuPTRtITI2ZlGOQN7xgy4LqWzjPnrzMXfwBEDx/nrwdG6zEGMK8\n" + + "AkVkMT5uJJvCwjwEGAEKAfAFglro/4AJEAitUcrkcPAGRxQAAAAAAB4AIHNhbHRA\n" + + "bm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ/Q0Z6WDH2+8/F1xEEuiApsjnn2lGNZ2\n" + + "DeIaklJzdqQOApsCwLygBBkBCgBvBYJa6P+ACRAQ/Lz/Do6nkUcUAAAAAAAeACBz\n" + + "YWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmfrVATyX3tgcM2z41fqYquxVhJR\n" + + "avN6+w2SU4xEG++SqBYhBM6mENCE+lHyEeHEbBD8vP8OjqeRAABGVggAsB8M2KI5\n" + + "cxXKKgVHL1dEfzg9halVavktfcT6ZVC/+aDp94tvBCL16Guhq4ccN7DATrWx430/\n" + + "GecY6E77qvhDzmCclSbdLbiZmsrVX9kCmTfrJzFQ64KfvIS5GgbL21+ZJ+pKW2HO\n" + + "MBGn6sgAPmTqM5UsDCpsEKDt5CJcJr3sTc8D9NhEnc0dKsQ91+n9ms3W5tyyE6r9\n" + + "pyM6ThBCMhbQkR7hE9XWAQeO1ILSFGnie0aFcTU0Oo0wL1MaiSyA/8XpKq23xfx1\n" + + "kNS9hQkdq0aWehNoTJdCt1Nq1cWABy2rQR0x+qhGWowfsAjnBautxvet28t2kPCA\n" + + "IMniYpWc89BwfhYhBOMsttqCApG3522xqAitUcrkcPAGAACq1gf/Q7H9Re5SWk+U\n" + + "On/NQPRedf544YJ/YdQnve/hSaPGL33cUzf4yxzFILnK19Ird5f8/mTT1pg99L3i\n" + + "xE3N5031JJKwFpCB69Rsysg88ZLDL2VLc3xdsAQdUbVaCqeRHKwtMtpBvbAFvF9p\n" + + "lwam0SSXHHr/JkYm5ufXN6I8ib/nwr1bFbf/Se0Wuk9RG4ne9JUBCrGxakyVd+Og\n" + + "LLhvzOmJa7fDC0uUZhTKFbjMxLhaas4HFYiRbfz2T0xz9gyDytDWsEFM+XoKHlEH\n" + + "8Fx/U2B5/8N0Q+pIFoEuOmBO+5EPvPIlxNByHgiaNIuKt1Mu+UAb2Spl6D5zbDfX\n" + + "/3vqxdhYHw==\n" + + "=Ric2\n" + + "-----END PGP PUBLIC KEY BLOCK-----\n"; + TestSignature t0 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJYaEaACRAQ/Lz/Do6nkUcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmdVa4OG6WfRoRlj5+Zb6avhJUIZFvcIFiLuvrJp8Hio\n" + + "iBYhBM6mENCE+lHyEeHEbBD8vP8OjqeRAAAbaQgAjhBh0dLO0Sqiqkb2M3KWc25V\n" + + "hJlcP3isFROJ0jikmXxkG9W04AvlA78tSxEP2n8a0CbxH/hT4g8mFb/qM5FKZcKf\n" + + "HQxjbjUxBmVHa3EfMkwT7u1mVRmoWtJ59oVsKoqRb/kZ14i6VZ9NzfK8MRlL0e24\n" + + "oNjkksZQ8ImjwwtvxSinxhezA6BtWi+dDnXAnG5Vva+6N/GRNPAAd8kFTPrlEqEz\n" + + "uRbpq76r4taPjRjzMNcwZJoRVHSahWhDcXxNTalVUwt0DZFAskZ3gI+0VgU11bK1\n" + + "QmIw2iR4itQY5f10HFNcl7uHLKnul0YyuvA5509HwCuEpdYUV/OxtlpVRaJ+yg==\n" + + "=Rc6K\n" + + "-----END PGP SIGNATURE-----\n", false, "Signature predates primary key"); + TestSignature t1 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJa564ACRAQ/Lz/Do6nkUcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmfcG7Iqn3OOKVjeJ61MlgERt08kcxh0x+BZFD7a8K7V\n" + + "VBYhBM6mENCE+lHyEeHEbBD8vP8OjqeRAACBIwf9EoS24IFeT3cPFf/nWxLFkbZK\n" + + "fiy9WzyK4wlpO3VTyWPbXi6zpC4I5Rbp2jDk/c7Q3DnOZqFDv6TriTwuLYTJGPxr\n" + + "U3dtDsFcKp4FcbgFyCDKIuLB+3kLaNpMXqdttEkY3Wd5m33XrBB7M0l5xZCk56Jm\n" + + "H5L1sGNNNkCzG6P44qu69o5fkWxbYuX22fyhdeyxucJHMztqiMQYDwT7eSA92A1v\n" + + "5OwA5D/k7GeyYFBFisxRijkdVtxstC9zkagC19VnZo7MRekA9gXj7kIna4XYRhfb\n" + + "uQnN47HXdiWQytwypLvZ8JEJpRruyMAaHjX5OBXh0SK11xYWb6wB93+QfOahtg==\n" + + "=UlUZ\n" + + "-----END PGP SIGNATURE-----\n", false, "Subkey is not bound at this time"); + TestSignature t2 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJdP4iACRAQ/Lz/Do6nkUcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmcgkZw3ZSg8CZCKqJw2r4VqCpTuUhz6N0zX43d+1xop\n" + + "2hYhBM6mENCE+lHyEeHEbBD8vP8OjqeRAADnqAgAq+m6dDZpNOBaXH9nwv8/+HgR\n" + + "MvRjnuLoa6zB5tcUhGPPVS0gg1PW0wfxlo1GPmgW3QDlV1zvcfYAZmV9uEC61wn/\n" + + "+FkqN0Tceo487UvkWARE/mmRj5L8OgUTfqm1eebFQlMu/MeG9YOg+tXBy7XS7hy3\n" + + "UdntIbtsv5oRTcybTnn5oiU2OFDlFC6sBNzOQt7wpyB1TKp2BdcsAv1RwmyCCCK4\n" + + "bnmrpYH6woWMyVEVeMYfOHAx9vHD+od8Vf/v5L1M2N0nHzRWjjkobTVUr+xt/CyW\n" + + "nq8SoazKYu3ETpZLeWX6Bciuv9+pzUCeClOSmBB1MFyyrTgbkOacHgrYnLvvtQ==\n" + + "=WCKA\n" + + "-----END PGP SIGNATURE-----\n", true); + TestSignature t3 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJmhTYiCRAQ/Lz/Do6nkUcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmdi3dCpJ4nZincNH5owv8+fJ5YpXljqtegtoBEnbbHP\n" + + "thYhBM6mENCE+lHyEeHEbBD8vP8OjqeRAAD0cQf/e8RHocRESJPbosqUuvC3ELnD\n" + + "oSsJomDMUDfSfgpS5EhkOyJhvcrHkCbsHH2xlUEQ+zjJWY/dwM3FUkoj+p3kb/JC\n" + + "Rn5cqQYlME+uJzjdHMyQCSOI1SvYwKCLCGPARDbCpeINrV++Oy29e6cv6/IcPlgo\n" + + "k/0A7XuNq0YNxC7oopCj5ye3yVUvUmSCG2iV4oiWW5GhhPRzMeW7MFQmS0NUkAI8\n" + + "hzJ8juTG4xP8SXnHCMakasZhJmtpMDd2BDZ7CrhWiWUQGrtd0eYkuyodreqVMGIF\n" + + "BN80YgTNFW2MrblhDRRmxAqWzD9FedBwwSdgYbtkDwjsSq0S1jQV6aPndJqiLw==\n" + + "=CIl0\n" + + "-----END PGP SIGNATURE-----\n", true); + + signatureValidityTest(api, cert, t0, t1, t2, t3); + } + + private void testPKSignsPKRevokedNoSubpacket(OpenPGPApi api) + throws IOException + { + String cert = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "\n" + + "xsBNBFpJegABCACzr1V+GxVkrtfDjihYK+HtyEIcO52uw7O2kd7JbduYp4RK17jy\n" + + "75N3EnsgmiIkSxXCWr+rTtonNs1zCJeUa/gwnNfs7mVgjL2rMOZU/KZ4MP0yOYU5\n" + + "u5FjNPWz8hpFQ9GKqfdj0Op61h1pCQO45IjUQ3dCDj9Rfn44zHMB1ZrbmIH9nTR1\n" + + "YIGHWmdm0LItb2WxIkwzWBAJ5acTlsmLyZZEQ1+8NDqktyzwFoQqTJvLU4StY2k6\n" + + "h18ZKZdPyrdLoEyOuWkvjxmbhDk1Gt5KiS/yy7mrzIPLr0dmJe4vc8WLV+bXoyNE\n" + + "x3H8o9CFcYehLfyqsy40lg92d6Kp96ww8dZ5ABEBAAHCwLsEIAEKAG8FglwqrYAJ\n" + + "EAitUcrkcPAGRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9y\n" + + "Z4KjdWVHTHye8HeUynibpgE5TYfFnnBt9bbOj99oplaTFiEE4yy22oICkbfnbbGo\n" + + "CK1RyuRw8AYAAMxeB/4+QAncX1+678HeO1fweQ0Zkf4O6+Ew6EgCp4I2UZu+a5H8\n" + + "ryI3B4WNShCDoV3CfOcUtUSUA8EOyrpYSW/3jPVfb01uxDNsZpf9piZG7DelIAef\n" + + "wvQaZHJeytchv5+Wo+Jo6qg26BgvUlXW2x5NNcScGvCZt1RQ712PRDAfUnppRXBj\n" + + "+IXWzOs52uYGFDFzJSLEUy6dtTdNCJk78EMoHsOwC7g5uUyHbjSfrdQncxgMwikl\n" + + "C2LFSS7xYZwDgkkb70AT10Ot2jL6rLIT/1ChQZ0oRGJLBHiz3FUpanDQIDD49+dp\n" + + "6FUmUUsubwwFkxBHyCbQ8cdbfBILNiD1pEo31dPTwsDEBB8BCgB4BYJeC+EACRAI\n" + + "rVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmeH\n" + + "LGXtWodbY9gI8X3QzLB9sL0hMGY2/+9yAip5uwckkAIVCgKbAwIeARYhBOMsttqC\n" + + "ApG3522xqAitUcrkcPAGAABmBQgAoipwm9jQWWvyY9WiXuEdq8T2Y9hEV1nt2ySj\n" + + "Tyk+ytK1Q5E8NSUYk3wrLgGNpWPbCiXYUGZfms15uuL703OoRBkUP/l7LA5RNgyJ\n" + + "/At+Bw3OPeWZ68hzQfA3eZdR3Y6sXxiGOhwTyVHcdHXncD+NjorIPbeSrAvM5Xf/\n" + + "jCEYM5Kfg4NC1yVZw7sFhD6KNjeloQK+UXi718QC1+YbfS295T9AwEmbwCsvQTv8\n" + + "EQq9veCfHYPwqMAH5aMn9CqPiY8o2p5mZ92nMuQhpFTdpnPjxVHpBmQw8uaKGJIF\n" + + "zvwpgKbkzb2m3LfgOyFVXVljOUlm/dCb2lfUlo4up0KYVZu0rcLAxAQfAQoAeAWC\n" + + "Wkl6AAkQCK1RyuRw8AZHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1w\n" + + "Z3Aub3Jn1WXYy2GcQ19ob8t2hq7BOItGrywzM393vZFR5mg+jwICFQoCmwMCHgEW\n" + + "IQTjLLbaggKRt+dtsagIrVHK5HDwBgAAUGMIAK3yEcaveZ6VCgDj17NuZ2Zb8vsU\n" + + "G65rE8R4QGFvHhhXM/NkMLpqKq0fFX66I8TPngmXUyPOZzOZM852A1NvnDIbGVZu\n" + + "flYRmct3t0B+CfxN9Q+7daKQr4+YNXkSeC4MsAfnGBnGQWKf20E/UlGLoWR9jlwk\n" + + "dOKkm6VVAiAKZ4QR8SjbTpaowJB3mjnVv/F3j7G3767VTixmIK2V32Ozast/ls23\n" + + "ZvFL1TxVx/rhxM04Mr2G5yQWJIzkZgqlCrPOtDy/HpHoPrC+Dx0kY9VFH8HEA+ea\n" + + "tJt1bXsNioiFIuMCouS3Hg7aa46DubrVP9WHxAIjTHkkB1yqvN3aWs7461LNEmp1\n" + + "bGlldEBleGFtcGxlLm9yZ8LAxAQTAQoAeAWCWkl6AAkQCK1RyuRw8AZHFAAAAAAA\n" + + "HgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnOkYsewniH1sJ2kI5N2wa\n" + + "5AImO40vTfrIbkXR2dICirICFQoCmwMCHgEWIQTjLLbaggKRt+dtsagIrVHK5HDw\n" + + "BgAAn/UIALMbXwG8hm7aH46107PZbChFrxoJNNn0mMioz28mkaoe9jJSJVF8KqtY\n" + + "odkyXN78BfGjVQ63G/Q5wWm3bdjNbyNz1Gnht9QZmpAv12QjQq22yZMnf73TC6sO\n" + + "6ay66dGrlTTYS2MTivbrF2wpTcZbqOIv5UhVaOQfWovp3tZCioqZc6stqqoXXqZa\n" + + "JnMBh2wdQpGdOA5gjG0khQBsWKlAv2wZtG6JQnm8PyiM/bBKIzSrepr7BTeu/4TG\n" + + "HiUtB1ZcMHOovIikswtg+d4ssIbb5HYihAl0Hlw3/czVwJ9cKStNUiydIooO3Axa\n" + + "7aKpHz2M2zXwtG7d+HzcfYs98PWhB/HOwE0EWkrLgAEIALucmrvabJbZlolJ+37E\n" + + "Uqm0CJztIlp7uAyvSFwd4ITWDHIotySRIx84CMRn9xoiRI87m8kUGl+Sf6e8gdXz\n" + + "h/M+xWFLmsdbGhn/XNf29NjfYMlzZR2pt9YTWmi933xXMyPeaezDa07a6E7eaGar\n" + + "HPovqCi2Z+19GACOLRGMIUp1EGAfxe2KpJCOHlfuwsWTwPKQYV4gDiv85+Nej7Ge\n" + + "iUucLDOucgrTh3AACAZyg5wvm0Ivn9VmXrEqHMv618d0BEJqHQ7t6I4UvlcXGBnm\n" + + "QlHBRdBcmQSJBoxyFUC8jn4z9xUSeKhVzM/f2CFaDOGOoLkxETExI/ygxuAT+0Xy\n" + + "R00AEQEAAcLCPAQYAQoB8AWCXgvhAAkQCK1RyuRw8AZHFAAAAAAAHgAgc2FsdEBu\n" + + "b3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn3AGtWT1k7YOtMNzqOHbeBWvHChWG2WLK\n" + + "g0h1eacBHzMCmwLAvKAEGQEKAG8Fgl4L4QAJEBD8vP8OjqeRRxQAAAAAAB4AIHNh\n" + + "bHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZy+iNvlcjeU9RaFYI93HZzC2AqXA\n" + + "eYvxUUglSsm7i864FiEEzqYQ0IT6UfIR4cRsEPy8/w6Op5EAAK5PB/wIYzPZam33\n" + + "xS+kUUeB043pbKcuAN58k4nApY6w7lw1Idtxny72o00eYdemQagBe3KW/a65c9Qt\n" + + "VnEuSS1cc1wHu3/ijn17vnsi4aU82fFU96St4RxmmMJVZV6wWT9CV4C/IZuoQ0l2\n" + + "UGbXKbJ0NbiBwvTjcVAeJYjRYU6kAkGHUCRYhbNbplG6PwuCnc5QyNPGzNwqhCmr\n" + + "fb1BbhhuvJg4NAq0WRFrfeOi+YvJGZgVJEkoSJtpOXtqhP5rmHjOqikDbMZcd1SH\n" + + "+XlIbcQovX4O0o7x5HVEhbLWHMQHWqIVghQhLAySNdoc3CkGs0o77SheATQSoF/8\n" + + "C7G1UJ2C3fYIFiEE4yy22oICkbfnbbGoCK1RyuRw8AYAADYzB/9TGOwycsZIk43P\n" + + "485p1carRzmQwkplKpNHof+gR7PqLLVqpBguvu3X8Q56bcHKmp3WHsuChdmo7eJz\n" + + "sLtMUMPzRBd4vNYeyInsGOxvmE+vQ1Hfg71VEHpnyjWFTqzKqB+0FOaOGKI3SYg3\n" + + "iKdnfycia6sqH+/CRQB5zWYBwtk9s6PROeHZzk2PVTVDQjlHLeUW8tBl40yFETtH\n" + + "+POXhrmcVVnS0ZZQ2Dogq0Bz0h4a8R1V1TG2CaK6D8viMmiWp1aAFoMoqQZpiA1f\n" + + "GiDTNkSzLBpLj00bSEyNmZRjkDe8YMuC6ls4z568zF38ARA8f568HRusxBjCvAJF\n" + + "ZDE+biSbwsI8BBgBCgHwBYJa6P+ACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5v\n" + + "dGF0aW9ucy5zZXF1b2lhLXBncC5vcmf0NGelgx9vvPxdcRBLogKbI559pRjWdg3i\n" + + "GpJSc3akDgKbAsC8oAQZAQoAbwWCWuj/gAkQEPy8/w6Op5FHFAAAAAAAHgAgc2Fs\n" + + "dEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn61QE8l97YHDNs+NX6mKrsVYSUWrz\n" + + "evsNklOMRBvvkqgWIQTOphDQhPpR8hHhxGwQ/Lz/Do6nkQAARlYIALAfDNiiOXMV\n" + + "yioFRy9XRH84PYWpVWr5LX3E+mVQv/mg6feLbwQi9ehroauHHDewwE61seN9Pxnn\n" + + "GOhO+6r4Q85gnJUm3S24mZrK1V/ZApk36ycxUOuCn7yEuRoGy9tfmSfqSlthzjAR\n" + + "p+rIAD5k6jOVLAwqbBCg7eQiXCa97E3PA/TYRJ3NHSrEPdfp/ZrN1ubcshOq/acj\n" + + "Ok4QQjIW0JEe4RPV1gEHjtSC0hRp4ntGhXE1NDqNMC9TGoksgP/F6Sqtt8X8dZDU\n" + + "vYUJHatGlnoTaEyXQrdTatXFgActq0EdMfqoRlqMH7AI5wWrrcb3rdvLdpDwgCDJ\n" + + "4mKVnPPQcH4WIQTjLLbaggKRt+dtsagIrVHK5HDwBgAAqtYH/0Ox/UXuUlpPlDp/\n" + + "zUD0XnX+eOGCf2HUJ73v4Umjxi993FM3+MscxSC5ytfSK3eX/P5k09aYPfS94sRN\n" + + "zedN9SSSsBaQgevUbMrIPPGSwy9lS3N8XbAEHVG1WgqnkRysLTLaQb2wBbxfaZcG\n" + + "ptEklxx6/yZGJubn1zeiPIm/58K9WxW3/0ntFrpPURuJ3vSVAQqxsWpMlXfjoCy4\n" + + "b8zpiWu3wwtLlGYUyhW4zMS4WmrOBxWIkW389k9Mc/YMg8rQ1rBBTPl6Ch5RB/Bc\n" + + "f1Ngef/DdEPqSBaBLjpgTvuRD7zyJcTQch4ImjSLirdTLvlAG9kqZeg+c2w31/97\n" + + "6sXYWB8=\n" + + "=13Sf\n" + + "-----END PGP PUBLIC KEY BLOCK-----\n"; + TestSignature t0 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJYaEaACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmeoPMfalw2oS7uyOKnOXJSN8Gx7pr/BMlo3Xn8nTgx6\n" + + "ORYhBOMsttqCApG3522xqAitUcrkcPAGAABXbAf/WfWaQYNuATAKwxYrJx4fd5kt\n" + + "0M6sn1q7wK1MIxursG2+FuKafV25O9+pde8Nog77OEgegwk+HokOVFpVXfOzHQjs\n" + + "8dwWTtTQlX5NIBNvtqS7cvCKhjsqaHKgmzsenMjCEbpDZ3C5CoqcYicykqEU/Ia0\n" + + "ZGC4lzRByrgNy/w+/iLN748S707bzBLVc/sE73k9N5pANAlE+cA/sHI1Gp2WxJR9\n" + + "t2Fk4x6/85PEnF1RHI16p/wSEeuRaBpyw9QGZBbVDVt5wvgttxZjteGGSwBM3WI/\n" + + "gPfC0LW+JQ2W+dwY0PN/7yuARVRhXpKiBI4xqp7x3OanQX6quU77g3B8nXAt3A==\n" + + "=StqT\n" + + "-----END PGP SIGNATURE-----\n", false, "Signature predates primary key"); + TestSignature t1 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJa564ACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmfM0EN4Ei0bQv6UO9BRq2wtUfV948cRynRMBb8TSGCG\n" + + "tBYhBOMsttqCApG3522xqAitUcrkcPAGAAAlNwf+L0KQK9i/xmYKOMV2EX13QUoZ\n" + + "vvb/pHGZaCQ9JtvEF2l2DT0DqByZ+tOv5Y4isU+un7CraoyvyajAwR0Yqk937B6C\n" + + "HQHKMkmIl+5R4/xqSoWYmOidbrgilojPMBEhB3INQ8/THjjFijtLzitVhnWBd7+u\n" + + "s0kcqnWnOdx2By4aDe+UEiyCfSE02e/0tIsM71RqiU91zH6dl6+q8nml7PsYuTFV\n" + + "V09oQTbBuuvUe+YgN/uvyKVIsA64lQ+YhqEeIA8Quek7fHhW+du9OIhSPsbYodyx\n" + + "VWMTXwSWKGNvZNAkpmgUYqFjS2Cx5ZUWblZLjrNKBwnnmt50qvUN7+o2pjlnfA==\n" + + "=UuXb\n" + + "-----END PGP SIGNATURE-----\n", false, "Hard revocations invalidate key at all times"); + TestSignature t2 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJdP4iACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmfFzYGoiuSjN+gz1IDD4ZvRXGuPTHks0/pIiGY90mrZ\n" + + "WxYhBOMsttqCApG3522xqAitUcrkcPAGAABGPAf/ck7tJAFoPIDd9fTPZANpNGoW\n" + + "Fq6VuNfy/nLjz2gkHFX/lLAxQ0N3McIdRA++Ik/omb0lis3R2DVNgwqNm2OF34HE\n" + + "qxmPmrQHBgk2q0fDH4NCE0XnYQjQT65V99IfiaQu+oS3Mq8MuYsDYvRVvRKMwt49\n" + + "fcDnvFtAtCqEETdv6wV5cUZmdQ3L9NU9bApJ0jk+EHVdpfTUIbOYYGnsIe/4Aa0d\n" + + "jgzu4Em79ynosOn//953XJ7OO8LCDi1EKt+nFuZARUlt/Jwwull6zzp7HUPw6HPt\n" + + "Upp7os8TIPC4STwoSeEKaxEkrbMGFnDcoDajnKKRt5+MkB24Oq7PHvnzgnPpVg==\n" + + "=Ljv7\n" + + "-----END PGP SIGNATURE-----\n", false, "Hard revocations invalidate key at all times"); + TestSignature t3 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJmhTYiCRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmfbjQf/zfoJQT0hhna4RDjOESBLgGaCbc5HLeo751F4\n" + + "NxYhBOMsttqCApG3522xqAitUcrkcPAGAABqBQgAkkNmYf6yLPvox+ZayrLtMb9D\n" + + "ghgt0nau72DSazsJ6SAq2QqIdr0RRhRa2gCETkp4PpeoDWmIvoVj35ZnfyeO/jqy\n" + + "HECvRwO0WPA5FXQM6uG7s40vDTRFjlJMpPyHWnn2igcR64iDxBGmc40xi9CcmJP9\n" + + "tmA26+1Nzj1LcfNvknKZ2UIOmnXiZY0QssIdyqsmJrdFpXs4UCLUzdXkfFLoxksU\n" + + "mk4B6hig2IKMj5mnbWy/JQSXtjjI+HHmtzgWfXs7d9iQ61CklbtCOiPeWxvoqlGG\n" + + "oK1wV1olcSar/RPKTlMmQpAg9dztQgrNs1oF7EF3i9kwNP7I5JzekPiOLH6oMw==\n" + + "=5KMU\n" + + "-----END PGP SIGNATURE-----\n", false, "Hard revocations invalidate key at all times"); + + signatureValidityTest(api, cert, t0, t1, t2, t3); + } + + private void testSKSignsPKRevokedNoSubpacket(OpenPGPApi api) + throws IOException + { + // https://sequoia-pgp.gitlab.io/openpgp-interoperability-test-suite/results.html#Key_revocation_test__subkey_signs__primary_key_is_revoked__revoked__no_subpacket + String cert = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "\n" + + "xsBNBFpJegABCACzr1V+GxVkrtfDjihYK+HtyEIcO52uw7O2kd7JbduYp4RK17jy\n" + + "75N3EnsgmiIkSxXCWr+rTtonNs1zCJeUa/gwnNfs7mVgjL2rMOZU/KZ4MP0yOYU5\n" + + "u5FjNPWz8hpFQ9GKqfdj0Op61h1pCQO45IjUQ3dCDj9Rfn44zHMB1ZrbmIH9nTR1\n" + + "YIGHWmdm0LItb2WxIkwzWBAJ5acTlsmLyZZEQ1+8NDqktyzwFoQqTJvLU4StY2k6\n" + + "h18ZKZdPyrdLoEyOuWkvjxmbhDk1Gt5KiS/yy7mrzIPLr0dmJe4vc8WLV+bXoyNE\n" + + "x3H8o9CFcYehLfyqsy40lg92d6Kp96ww8dZ5ABEBAAHCwLsEIAEKAG8FglwqrYAJ\n" + + "EAitUcrkcPAGRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9y\n" + + "Z4KjdWVHTHye8HeUynibpgE5TYfFnnBt9bbOj99oplaTFiEE4yy22oICkbfnbbGo\n" + + "CK1RyuRw8AYAAMxeB/4+QAncX1+678HeO1fweQ0Zkf4O6+Ew6EgCp4I2UZu+a5H8\n" + + "ryI3B4WNShCDoV3CfOcUtUSUA8EOyrpYSW/3jPVfb01uxDNsZpf9piZG7DelIAef\n" + + "wvQaZHJeytchv5+Wo+Jo6qg26BgvUlXW2x5NNcScGvCZt1RQ712PRDAfUnppRXBj\n" + + "+IXWzOs52uYGFDFzJSLEUy6dtTdNCJk78EMoHsOwC7g5uUyHbjSfrdQncxgMwikl\n" + + "C2LFSS7xYZwDgkkb70AT10Ot2jL6rLIT/1ChQZ0oRGJLBHiz3FUpanDQIDD49+dp\n" + + "6FUmUUsubwwFkxBHyCbQ8cdbfBILNiD1pEo31dPTwsDEBB8BCgB4BYJeC+EACRAI\n" + + "rVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmeH\n" + + "LGXtWodbY9gI8X3QzLB9sL0hMGY2/+9yAip5uwckkAIVCgKbAwIeARYhBOMsttqC\n" + + "ApG3522xqAitUcrkcPAGAABmBQgAoipwm9jQWWvyY9WiXuEdq8T2Y9hEV1nt2ySj\n" + + "Tyk+ytK1Q5E8NSUYk3wrLgGNpWPbCiXYUGZfms15uuL703OoRBkUP/l7LA5RNgyJ\n" + + "/At+Bw3OPeWZ68hzQfA3eZdR3Y6sXxiGOhwTyVHcdHXncD+NjorIPbeSrAvM5Xf/\n" + + "jCEYM5Kfg4NC1yVZw7sFhD6KNjeloQK+UXi718QC1+YbfS295T9AwEmbwCsvQTv8\n" + + "EQq9veCfHYPwqMAH5aMn9CqPiY8o2p5mZ92nMuQhpFTdpnPjxVHpBmQw8uaKGJIF\n" + + "zvwpgKbkzb2m3LfgOyFVXVljOUlm/dCb2lfUlo4up0KYVZu0rcLAxAQfAQoAeAWC\n" + + "Wkl6AAkQCK1RyuRw8AZHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1w\n" + + "Z3Aub3Jn1WXYy2GcQ19ob8t2hq7BOItGrywzM393vZFR5mg+jwICFQoCmwMCHgEW\n" + + "IQTjLLbaggKRt+dtsagIrVHK5HDwBgAAUGMIAK3yEcaveZ6VCgDj17NuZ2Zb8vsU\n" + + "G65rE8R4QGFvHhhXM/NkMLpqKq0fFX66I8TPngmXUyPOZzOZM852A1NvnDIbGVZu\n" + + "flYRmct3t0B+CfxN9Q+7daKQr4+YNXkSeC4MsAfnGBnGQWKf20E/UlGLoWR9jlwk\n" + + "dOKkm6VVAiAKZ4QR8SjbTpaowJB3mjnVv/F3j7G3767VTixmIK2V32Ozast/ls23\n" + + "ZvFL1TxVx/rhxM04Mr2G5yQWJIzkZgqlCrPOtDy/HpHoPrC+Dx0kY9VFH8HEA+ea\n" + + "tJt1bXsNioiFIuMCouS3Hg7aa46DubrVP9WHxAIjTHkkB1yqvN3aWs7461LNEmp1\n" + + "bGlldEBleGFtcGxlLm9yZ8LAxAQTAQoAeAWCWkl6AAkQCK1RyuRw8AZHFAAAAAAA\n" + + "HgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3JnOkYsewniH1sJ2kI5N2wa\n" + + "5AImO40vTfrIbkXR2dICirICFQoCmwMCHgEWIQTjLLbaggKRt+dtsagIrVHK5HDw\n" + + "BgAAn/UIALMbXwG8hm7aH46107PZbChFrxoJNNn0mMioz28mkaoe9jJSJVF8KqtY\n" + + "odkyXN78BfGjVQ63G/Q5wWm3bdjNbyNz1Gnht9QZmpAv12QjQq22yZMnf73TC6sO\n" + + "6ay66dGrlTTYS2MTivbrF2wpTcZbqOIv5UhVaOQfWovp3tZCioqZc6stqqoXXqZa\n" + + "JnMBh2wdQpGdOA5gjG0khQBsWKlAv2wZtG6JQnm8PyiM/bBKIzSrepr7BTeu/4TG\n" + + "HiUtB1ZcMHOovIikswtg+d4ssIbb5HYihAl0Hlw3/czVwJ9cKStNUiydIooO3Axa\n" + + "7aKpHz2M2zXwtG7d+HzcfYs98PWhB/HOwE0EWkrLgAEIALucmrvabJbZlolJ+37E\n" + + "Uqm0CJztIlp7uAyvSFwd4ITWDHIotySRIx84CMRn9xoiRI87m8kUGl+Sf6e8gdXz\n" + + "h/M+xWFLmsdbGhn/XNf29NjfYMlzZR2pt9YTWmi933xXMyPeaezDa07a6E7eaGar\n" + + "HPovqCi2Z+19GACOLRGMIUp1EGAfxe2KpJCOHlfuwsWTwPKQYV4gDiv85+Nej7Ge\n" + + "iUucLDOucgrTh3AACAZyg5wvm0Ivn9VmXrEqHMv618d0BEJqHQ7t6I4UvlcXGBnm\n" + + "QlHBRdBcmQSJBoxyFUC8jn4z9xUSeKhVzM/f2CFaDOGOoLkxETExI/ygxuAT+0Xy\n" + + "R00AEQEAAcLCPAQYAQoB8AWCXgvhAAkQCK1RyuRw8AZHFAAAAAAAHgAgc2FsdEBu\n" + + "b3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn3AGtWT1k7YOtMNzqOHbeBWvHChWG2WLK\n" + + "g0h1eacBHzMCmwLAvKAEGQEKAG8Fgl4L4QAJEBD8vP8OjqeRRxQAAAAAAB4AIHNh\n" + + "bHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZy+iNvlcjeU9RaFYI93HZzC2AqXA\n" + + "eYvxUUglSsm7i864FiEEzqYQ0IT6UfIR4cRsEPy8/w6Op5EAAK5PB/wIYzPZam33\n" + + "xS+kUUeB043pbKcuAN58k4nApY6w7lw1Idtxny72o00eYdemQagBe3KW/a65c9Qt\n" + + "VnEuSS1cc1wHu3/ijn17vnsi4aU82fFU96St4RxmmMJVZV6wWT9CV4C/IZuoQ0l2\n" + + "UGbXKbJ0NbiBwvTjcVAeJYjRYU6kAkGHUCRYhbNbplG6PwuCnc5QyNPGzNwqhCmr\n" + + "fb1BbhhuvJg4NAq0WRFrfeOi+YvJGZgVJEkoSJtpOXtqhP5rmHjOqikDbMZcd1SH\n" + + "+XlIbcQovX4O0o7x5HVEhbLWHMQHWqIVghQhLAySNdoc3CkGs0o77SheATQSoF/8\n" + + "C7G1UJ2C3fYIFiEE4yy22oICkbfnbbGoCK1RyuRw8AYAADYzB/9TGOwycsZIk43P\n" + + "485p1carRzmQwkplKpNHof+gR7PqLLVqpBguvu3X8Q56bcHKmp3WHsuChdmo7eJz\n" + + "sLtMUMPzRBd4vNYeyInsGOxvmE+vQ1Hfg71VEHpnyjWFTqzKqB+0FOaOGKI3SYg3\n" + + "iKdnfycia6sqH+/CRQB5zWYBwtk9s6PROeHZzk2PVTVDQjlHLeUW8tBl40yFETtH\n" + + "+POXhrmcVVnS0ZZQ2Dogq0Bz0h4a8R1V1TG2CaK6D8viMmiWp1aAFoMoqQZpiA1f\n" + + "GiDTNkSzLBpLj00bSEyNmZRjkDe8YMuC6ls4z568zF38ARA8f568HRusxBjCvAJF\n" + + "ZDE+biSbwsI8BBgBCgHwBYJa6P+ACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5v\n" + + "dGF0aW9ucy5zZXF1b2lhLXBncC5vcmf0NGelgx9vvPxdcRBLogKbI559pRjWdg3i\n" + + "GpJSc3akDgKbAsC8oAQZAQoAbwWCWuj/gAkQEPy8/w6Op5FHFAAAAAAAHgAgc2Fs\n" + + "dEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn61QE8l97YHDNs+NX6mKrsVYSUWrz\n" + + "evsNklOMRBvvkqgWIQTOphDQhPpR8hHhxGwQ/Lz/Do6nkQAARlYIALAfDNiiOXMV\n" + + "yioFRy9XRH84PYWpVWr5LX3E+mVQv/mg6feLbwQi9ehroauHHDewwE61seN9Pxnn\n" + + "GOhO+6r4Q85gnJUm3S24mZrK1V/ZApk36ycxUOuCn7yEuRoGy9tfmSfqSlthzjAR\n" + + "p+rIAD5k6jOVLAwqbBCg7eQiXCa97E3PA/TYRJ3NHSrEPdfp/ZrN1ubcshOq/acj\n" + + "Ok4QQjIW0JEe4RPV1gEHjtSC0hRp4ntGhXE1NDqNMC9TGoksgP/F6Sqtt8X8dZDU\n" + + "vYUJHatGlnoTaEyXQrdTatXFgActq0EdMfqoRlqMH7AI5wWrrcb3rdvLdpDwgCDJ\n" + + "4mKVnPPQcH4WIQTjLLbaggKRt+dtsagIrVHK5HDwBgAAqtYH/0Ox/UXuUlpPlDp/\n" + + "zUD0XnX+eOGCf2HUJ73v4Umjxi993FM3+MscxSC5ytfSK3eX/P5k09aYPfS94sRN\n" + + "zedN9SSSsBaQgevUbMrIPPGSwy9lS3N8XbAEHVG1WgqnkRysLTLaQb2wBbxfaZcG\n" + + "ptEklxx6/yZGJubn1zeiPIm/58K9WxW3/0ntFrpPURuJ3vSVAQqxsWpMlXfjoCy4\n" + + "b8zpiWu3wwtLlGYUyhW4zMS4WmrOBxWIkW389k9Mc/YMg8rQ1rBBTPl6Ch5RB/Bc\n" + + "f1Ngef/DdEPqSBaBLjpgTvuRD7zyJcTQch4ImjSLirdTLvlAG9kqZeg+c2w31/97\n" + + "6sXYWB8=\n" + + "=13Sf\n" + + "-----END PGP PUBLIC KEY BLOCK-----\n"; + TestSignature t0 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJYaEaACRAQ/Lz/Do6nkUcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmdVa4OG6WfRoRlj5+Zb6avhJUIZFvcIFiLuvrJp8Hio\n" + + "iBYhBM6mENCE+lHyEeHEbBD8vP8OjqeRAAAbaQgAjhBh0dLO0Sqiqkb2M3KWc25V\n" + + "hJlcP3isFROJ0jikmXxkG9W04AvlA78tSxEP2n8a0CbxH/hT4g8mFb/qM5FKZcKf\n" + + "HQxjbjUxBmVHa3EfMkwT7u1mVRmoWtJ59oVsKoqRb/kZ14i6VZ9NzfK8MRlL0e24\n" + + "oNjkksZQ8ImjwwtvxSinxhezA6BtWi+dDnXAnG5Vva+6N/GRNPAAd8kFTPrlEqEz\n" + + "uRbpq76r4taPjRjzMNcwZJoRVHSahWhDcXxNTalVUwt0DZFAskZ3gI+0VgU11bK1\n" + + "QmIw2iR4itQY5f10HFNcl7uHLKnul0YyuvA5509HwCuEpdYUV/OxtlpVRaJ+yg==\n" + + "=Rc6K\n" + + "-----END PGP SIGNATURE-----\n", false, "Signature predates primary key"); + TestSignature t1 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJa564ACRAQ/Lz/Do6nkUcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmfcG7Iqn3OOKVjeJ61MlgERt08kcxh0x+BZFD7a8K7V\n" + + "VBYhBM6mENCE+lHyEeHEbBD8vP8OjqeRAACBIwf9EoS24IFeT3cPFf/nWxLFkbZK\n" + + "fiy9WzyK4wlpO3VTyWPbXi6zpC4I5Rbp2jDk/c7Q3DnOZqFDv6TriTwuLYTJGPxr\n" + + "U3dtDsFcKp4FcbgFyCDKIuLB+3kLaNpMXqdttEkY3Wd5m33XrBB7M0l5xZCk56Jm\n" + + "H5L1sGNNNkCzG6P44qu69o5fkWxbYuX22fyhdeyxucJHMztqiMQYDwT7eSA92A1v\n" + + "5OwA5D/k7GeyYFBFisxRijkdVtxstC9zkagC19VnZo7MRekA9gXj7kIna4XYRhfb\n" + + "uQnN47HXdiWQytwypLvZ8JEJpRruyMAaHjX5OBXh0SK11xYWb6wB93+QfOahtg==\n" + + "=UlUZ\n" + + "-----END PGP SIGNATURE-----\n", false, "Hard revocations invalidate key at all times"); + TestSignature t2 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJdP4iACRAQ/Lz/Do6nkUcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmcgkZw3ZSg8CZCKqJw2r4VqCpTuUhz6N0zX43d+1xop\n" + + "2hYhBM6mENCE+lHyEeHEbBD8vP8OjqeRAADnqAgAq+m6dDZpNOBaXH9nwv8/+HgR\n" + + "MvRjnuLoa6zB5tcUhGPPVS0gg1PW0wfxlo1GPmgW3QDlV1zvcfYAZmV9uEC61wn/\n" + + "+FkqN0Tceo487UvkWARE/mmRj5L8OgUTfqm1eebFQlMu/MeG9YOg+tXBy7XS7hy3\n" + + "UdntIbtsv5oRTcybTnn5oiU2OFDlFC6sBNzOQt7wpyB1TKp2BdcsAv1RwmyCCCK4\n" + + "bnmrpYH6woWMyVEVeMYfOHAx9vHD+od8Vf/v5L1M2N0nHzRWjjkobTVUr+xt/CyW\n" + + "nq8SoazKYu3ETpZLeWX6Bciuv9+pzUCeClOSmBB1MFyyrTgbkOacHgrYnLvvtQ==\n" + + "=WCKA\n" + + "-----END PGP SIGNATURE-----\n", false, "Hard revocations invalidate key at all times"); + TestSignature t3 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJmhTYiCRAQ/Lz/Do6nkUcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmdi3dCpJ4nZincNH5owv8+fJ5YpXljqtegtoBEnbbHP\n" + + "thYhBM6mENCE+lHyEeHEbBD8vP8OjqeRAAD0cQf/e8RHocRESJPbosqUuvC3ELnD\n" + + "oSsJomDMUDfSfgpS5EhkOyJhvcrHkCbsHH2xlUEQ+zjJWY/dwM3FUkoj+p3kb/JC\n" + + "Rn5cqQYlME+uJzjdHMyQCSOI1SvYwKCLCGPARDbCpeINrV++Oy29e6cv6/IcPlgo\n" + + "k/0A7XuNq0YNxC7oopCj5ye3yVUvUmSCG2iV4oiWW5GhhPRzMeW7MFQmS0NUkAI8\n" + + "hzJ8juTG4xP8SXnHCMakasZhJmtpMDd2BDZ7CrhWiWUQGrtd0eYkuyodreqVMGIF\n" + + "BN80YgTNFW2MrblhDRRmxAqWzD9FedBwwSdgYbtkDwjsSq0S1jQV6aPndJqiLw==\n" + + "=CIl0\n" + + "-----END PGP SIGNATURE-----\n", false, "Hard revocations invalidate key at all times"); + + signatureValidityTest(api, cert, t0, t1, t2, t3); + } + + private void testPKSignsPKRevocationSuperseded(OpenPGPApi api) + throws IOException + { + // https://sequoia-pgp.gitlab.io/openpgp-interoperability-test-suite/results.html#Key_revocation_test__primary_key_signs_and_is_revoked__revoked__superseded + String CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" + + "\n" + + "xsBNBFpJegABCACzr1V+GxVkrtfDjihYK+HtyEIcO52uw7O2kd7JbduYp4RK17jy\n" + + "75N3EnsgmiIkSxXCWr+rTtonNs1zCJeUa/gwnNfs7mVgjL2rMOZU/KZ4MP0yOYU5\n" + + "u5FjNPWz8hpFQ9GKqfdj0Op61h1pCQO45IjUQ3dCDj9Rfn44zHMB1ZrbmIH9nTR1\n" + + "YIGHWmdm0LItb2WxIkwzWBAJ5acTlsmLyZZEQ1+8NDqktyzwFoQqTJvLU4StY2k6\n" + + "h18ZKZdPyrdLoEyOuWkvjxmbhDk1Gt5KiS/yy7mrzIPLr0dmJe4vc8WLV+bXoyNE\n" + + "x3H8o9CFcYehLfyqsy40lg92d6Kp96ww8dZ5ABEBAAHCwM8EIAEKAIMFglwqrYAJ\n" + + "EAitUcrkcPAGRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9y\n" + + "Z1X0jZPeNNpSsn78ulDPJNHa0QaeI5oAUdBGbIKSOT0uEx0BS2V5IGlzIHN1cGVy\n" + + "c2VkZWQWIQTjLLbaggKRt+dtsagIrVHK5HDwBgAAr2QIAKAY5bHFbRkoItYBJBN1\n" + + "aV1jjrpYdwLM+0LHf8GcRCeO1Pt9I1J021crwTw14sTCxi6WH4qbQSBxRqAEej/A\n" + + "wfk1kmkm4WF7zTUT+fXIHDJxFJJXqFZ+LWldYYEVqSi02gpbYkyLm9hxoLDoAxS2\n" + + "bj/sFaH4Bxr/eUCqjOiEsGzdY1m65+cp5jv8cJK05jwqxO5/3KZcF/ShA7AN3dJi\n" + + "NAokoextBtXBWlGvrDIfFafOy/uCnsO6NeORWbgZ88TOXPD816ff5Y8kMwkDkIk2\n" + + "9dK4m0aL/MDI+Fgx78zRYwn5xHbTMaFz+hex+gjo4grx3KYXeoxBAchUuTsVNoo4\n" + + "kbfCwMQEHwEKAHgFgl4L4QAJEAitUcrkcPAGRxQAAAAAAB4AIHNhbHRAbm90YXRp\n" + + "b25zLnNlcXVvaWEtcGdwLm9yZ4csZe1ah1tj2AjxfdDMsH2wvSEwZjb/73ICKnm7\n" + + "BySQAhUKApsDAh4BFiEE4yy22oICkbfnbbGoCK1RyuRw8AYAAGYFCACiKnCb2NBZ\n" + + "a/Jj1aJe4R2rxPZj2ERXWe3bJKNPKT7K0rVDkTw1JRiTfCsuAY2lY9sKJdhQZl+a\n" + + "zXm64vvTc6hEGRQ/+XssDlE2DIn8C34HDc495ZnryHNB8Dd5l1HdjqxfGIY6HBPJ\n" + + "Udx0dedwP42Oisg9t5KsC8zld/+MIRgzkp+Dg0LXJVnDuwWEPoo2N6WhAr5ReLvX\n" + + "xALX5ht9Lb3lP0DASZvAKy9BO/wRCr294J8dg/CowAfloyf0Ko+JjyjanmZn3acy\n" + + "5CGkVN2mc+PFUekGZDDy5ooYkgXO/CmApuTNvabct+A7IVVdWWM5SWb90JvaV9SW\n" + + "ji6nQphVm7StwsDEBB8BCgB4BYJaSXoACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0\n" + + "QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmfVZdjLYZxDX2hvy3aGrsE4i0avLDMz\n" + + "f3e9kVHmaD6PAgIVCgKbAwIeARYhBOMsttqCApG3522xqAitUcrkcPAGAABQYwgA\n" + + "rfIRxq95npUKAOPXs25nZlvy+xQbrmsTxHhAYW8eGFcz82QwumoqrR8VfrojxM+e\n" + + "CZdTI85nM5kzznYDU2+cMhsZVm5+VhGZy3e3QH4J/E31D7t1opCvj5g1eRJ4Lgyw\n" + + "B+cYGcZBYp/bQT9SUYuhZH2OXCR04qSbpVUCIApnhBHxKNtOlqjAkHeaOdW/8XeP\n" + + "sbfvrtVOLGYgrZXfY7Nqy3+Wzbdm8UvVPFXH+uHEzTgyvYbnJBYkjORmCqUKs860\n" + + "PL8ekeg+sL4PHSRj1UUfwcQD55q0m3Vtew2KiIUi4wKi5LceDtprjoO5utU/1YfE\n" + + "AiNMeSQHXKq83dpazvjrUs0SanVsaWV0QGV4YW1wbGUub3JnwsDEBBMBCgB4BYJa\n" + + "SXoACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBn\n" + + "cC5vcmc6Rix7CeIfWwnaQjk3bBrkAiY7jS9N+shuRdHZ0gKKsgIVCgKbAwIeARYh\n" + + "BOMsttqCApG3522xqAitUcrkcPAGAACf9QgAsxtfAbyGbtofjrXTs9lsKEWvGgk0\n" + + "2fSYyKjPbyaRqh72MlIlUXwqq1ih2TJc3vwF8aNVDrcb9DnBabdt2M1vI3PUaeG3\n" + + "1BmakC/XZCNCrbbJkyd/vdMLqw7prLrp0auVNNhLYxOK9usXbClNxluo4i/lSFVo\n" + + "5B9ai+ne1kKKiplzqy2qqhdeplomcwGHbB1CkZ04DmCMbSSFAGxYqUC/bBm0bolC\n" + + "ebw/KIz9sEojNKt6mvsFN67/hMYeJS0HVlwwc6i8iKSzC2D53iywhtvkdiKECXQe\n" + + "XDf9zNXAn1wpK01SLJ0iig7cDFrtoqkfPYzbNfC0bt34fNx9iz3w9aEH8c7ATQRa\n" + + "SsuAAQgAu5yau9psltmWiUn7fsRSqbQInO0iWnu4DK9IXB3ghNYMcii3JJEjHzgI\n" + + "xGf3GiJEjzubyRQaX5J/p7yB1fOH8z7FYUuax1saGf9c1/b02N9gyXNlHam31hNa\n" + + "aL3ffFczI95p7MNrTtroTt5oZqsc+i+oKLZn7X0YAI4tEYwhSnUQYB/F7YqkkI4e\n" + + "V+7CxZPA8pBhXiAOK/zn416PsZ6JS5wsM65yCtOHcAAIBnKDnC+bQi+f1WZesSoc\n" + + "y/rXx3QEQmodDu3ojhS+VxcYGeZCUcFF0FyZBIkGjHIVQLyOfjP3FRJ4qFXMz9/Y\n" + + "IVoM4Y6guTERMTEj/KDG4BP7RfJHTQARAQABwsI8BBgBCgHwBYJeC+EACRAIrVHK\n" + + "5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmfcAa1Z\n" + + "PWTtg60w3Oo4dt4Fa8cKFYbZYsqDSHV5pwEfMwKbAsC8oAQZAQoAbwWCXgvhAAkQ\n" + + "EPy8/w6Op5FHFAAAAAAAHgAgc2FsdEBub3RhdGlvbnMuc2VxdW9pYS1wZ3Aub3Jn\n" + + "L6I2+VyN5T1FoVgj3cdnMLYCpcB5i/FRSCVKybuLzrgWIQTOphDQhPpR8hHhxGwQ\n" + + "/Lz/Do6nkQAArk8H/AhjM9lqbffFL6RRR4HTjelspy4A3nyTicCljrDuXDUh23Gf\n" + + "LvajTR5h16ZBqAF7cpb9rrlz1C1WcS5JLVxzXAe7f+KOfXu+eyLhpTzZ8VT3pK3h\n" + + "HGaYwlVlXrBZP0JXgL8hm6hDSXZQZtcpsnQ1uIHC9ONxUB4liNFhTqQCQYdQJFiF\n" + + "s1umUbo/C4KdzlDI08bM3CqEKat9vUFuGG68mDg0CrRZEWt946L5i8kZmBUkSShI\n" + + "m2k5e2qE/muYeM6qKQNsxlx3VIf5eUhtxCi9fg7SjvHkdUSFstYcxAdaohWCFCEs\n" + + "DJI12hzcKQazSjvtKF4BNBKgX/wLsbVQnYLd9ggWIQTjLLbaggKRt+dtsagIrVHK\n" + + "5HDwBgAANjMH/1MY7DJyxkiTjc/jzmnVxqtHOZDCSmUqk0eh/6BHs+ostWqkGC6+\n" + + "7dfxDnptwcqandYey4KF2ajt4nOwu0xQw/NEF3i81h7IiewY7G+YT69DUd+DvVUQ\n" + + "emfKNYVOrMqoH7QU5o4YojdJiDeIp2d/JyJrqyof78JFAHnNZgHC2T2zo9E54dnO\n" + + "TY9VNUNCOUct5Rby0GXjTIURO0f485eGuZxVWdLRllDYOiCrQHPSHhrxHVXVMbYJ\n" + + "oroPy+IyaJanVoAWgyipBmmIDV8aINM2RLMsGkuPTRtITI2ZlGOQN7xgy4LqWzjP\n" + + "nrzMXfwBEDx/nrwdG6zEGMK8AkVkMT5uJJvCwjwEGAEKAfAFglro/4AJEAitUcrk\n" + + "cPAGRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdwLm9yZ/Q0Z6WD\n" + + "H2+8/F1xEEuiApsjnn2lGNZ2DeIaklJzdqQOApsCwLygBBkBCgBvBYJa6P+ACRAQ\n" + + "/Lz/Do6nkUcUAAAAAAAeACBzYWx0QG5vdGF0aW9ucy5zZXF1b2lhLXBncC5vcmfr\n" + + "VATyX3tgcM2z41fqYquxVhJRavN6+w2SU4xEG++SqBYhBM6mENCE+lHyEeHEbBD8\n" + + "vP8OjqeRAABGVggAsB8M2KI5cxXKKgVHL1dEfzg9halVavktfcT6ZVC/+aDp94tv\n" + + "BCL16Guhq4ccN7DATrWx430/GecY6E77qvhDzmCclSbdLbiZmsrVX9kCmTfrJzFQ\n" + + "64KfvIS5GgbL21+ZJ+pKW2HOMBGn6sgAPmTqM5UsDCpsEKDt5CJcJr3sTc8D9NhE\n" + + "nc0dKsQ91+n9ms3W5tyyE6r9pyM6ThBCMhbQkR7hE9XWAQeO1ILSFGnie0aFcTU0\n" + + "Oo0wL1MaiSyA/8XpKq23xfx1kNS9hQkdq0aWehNoTJdCt1Nq1cWABy2rQR0x+qhG\n" + + "WowfsAjnBautxvet28t2kPCAIMniYpWc89BwfhYhBOMsttqCApG3522xqAitUcrk\n" + + "cPAGAACq1gf/Q7H9Re5SWk+UOn/NQPRedf544YJ/YdQnve/hSaPGL33cUzf4yxzF\n" + + "ILnK19Ird5f8/mTT1pg99L3ixE3N5031JJKwFpCB69Rsysg88ZLDL2VLc3xdsAQd\n" + + "UbVaCqeRHKwtMtpBvbAFvF9plwam0SSXHHr/JkYm5ufXN6I8ib/nwr1bFbf/Se0W\n" + + "uk9RG4ne9JUBCrGxakyVd+OgLLhvzOmJa7fDC0uUZhTKFbjMxLhaas4HFYiRbfz2\n" + + "T0xz9gyDytDWsEFM+XoKHlEH8Fx/U2B5/8N0Q+pIFoEuOmBO+5EPvPIlxNByHgia\n" + + "NIuKt1Mu+UAb2Spl6D5zbDfX/3vqxdhYHw==\n" + + "=9epL\n" + + "-----END PGP PUBLIC KEY BLOCK-----\n"; + TestSignature t0 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJYaEaACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmeoPMfalw2oS7uyOKnOXJSN8Gx7pr/BMlo3Xn8nTgx6\n" + + "ORYhBOMsttqCApG3522xqAitUcrkcPAGAABXbAf/WfWaQYNuATAKwxYrJx4fd5kt\n" + + "0M6sn1q7wK1MIxursG2+FuKafV25O9+pde8Nog77OEgegwk+HokOVFpVXfOzHQjs\n" + + "8dwWTtTQlX5NIBNvtqS7cvCKhjsqaHKgmzsenMjCEbpDZ3C5CoqcYicykqEU/Ia0\n" + + "ZGC4lzRByrgNy/w+/iLN748S707bzBLVc/sE73k9N5pANAlE+cA/sHI1Gp2WxJR9\n" + + "t2Fk4x6/85PEnF1RHI16p/wSEeuRaBpyw9QGZBbVDVt5wvgttxZjteGGSwBM3WI/\n" + + "gPfC0LW+JQ2W+dwY0PN/7yuARVRhXpKiBI4xqp7x3OanQX6quU77g3B8nXAt3A==\n" + + "=StqT\n" + + "-----END PGP SIGNATURE-----\n", false, "Signature predates primary key"); + TestSignature t1 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJa564ACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmfM0EN4Ei0bQv6UO9BRq2wtUfV948cRynRMBb8TSGCG\n" + + "tBYhBOMsttqCApG3522xqAitUcrkcPAGAAAlNwf+L0KQK9i/xmYKOMV2EX13QUoZ\n" + + "vvb/pHGZaCQ9JtvEF2l2DT0DqByZ+tOv5Y4isU+un7CraoyvyajAwR0Yqk937B6C\n" + + "HQHKMkmIl+5R4/xqSoWYmOidbrgilojPMBEhB3INQ8/THjjFijtLzitVhnWBd7+u\n" + + "s0kcqnWnOdx2By4aDe+UEiyCfSE02e/0tIsM71RqiU91zH6dl6+q8nml7PsYuTFV\n" + + "V09oQTbBuuvUe+YgN/uvyKVIsA64lQ+YhqEeIA8Quek7fHhW+du9OIhSPsbYodyx\n" + + "VWMTXwSWKGNvZNAkpmgUYqFjS2Cx5ZUWblZLjrNKBwnnmt50qvUN7+o2pjlnfA==\n" + + "=UuXb\n" + + "-----END PGP SIGNATURE-----\n", true); + TestSignature t2 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJdP4iACRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmfFzYGoiuSjN+gz1IDD4ZvRXGuPTHks0/pIiGY90mrZ\n" + + "WxYhBOMsttqCApG3522xqAitUcrkcPAGAABGPAf/ck7tJAFoPIDd9fTPZANpNGoW\n" + + "Fq6VuNfy/nLjz2gkHFX/lLAxQ0N3McIdRA++Ik/omb0lis3R2DVNgwqNm2OF34HE\n" + + "qxmPmrQHBgk2q0fDH4NCE0XnYQjQT65V99IfiaQu+oS3Mq8MuYsDYvRVvRKMwt49\n" + + "fcDnvFtAtCqEETdv6wV5cUZmdQ3L9NU9bApJ0jk+EHVdpfTUIbOYYGnsIe/4Aa0d\n" + + "jgzu4Em79ynosOn//953XJ7OO8LCDi1EKt+nFuZARUlt/Jwwull6zzp7HUPw6HPt\n" + + "Upp7os8TIPC4STwoSeEKaxEkrbMGFnDcoDajnKKRt5+MkB24Oq7PHvnzgnPpVg==\n" + + "=Ljv7\n" + + "-----END PGP SIGNATURE-----\n", false, "Key is revoked at this time"); + TestSignature t3 = new TestSignature("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsC7BAABCgBvBYJmhTYiCRAIrVHK5HDwBkcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmfbjQf/zfoJQT0hhna4RDjOESBLgGaCbc5HLeo751F4\n" + + "NxYhBOMsttqCApG3522xqAitUcrkcPAGAABqBQgAkkNmYf6yLPvox+ZayrLtMb9D\n" + + "ghgt0nau72DSazsJ6SAq2QqIdr0RRhRa2gCETkp4PpeoDWmIvoVj35ZnfyeO/jqy\n" + + "HECvRwO0WPA5FXQM6uG7s40vDTRFjlJMpPyHWnn2igcR64iDxBGmc40xi9CcmJP9\n" + + "tmA26+1Nzj1LcfNvknKZ2UIOmnXiZY0QssIdyqsmJrdFpXs4UCLUzdXkfFLoxksU\n" + + "mk4B6hig2IKMj5mnbWy/JQSXtjjI+HHmtzgWfXs7d9iQ61CklbtCOiPeWxvoqlGG\n" + + "oK1wV1olcSar/RPKTlMmQpAg9dztQgrNs1oF7EF3i9kwNP7I5JzekPiOLH6oMw==\n" + + "=5KMU\n" + + "-----END PGP SIGNATURE-----\n", true); + + signatureValidityTest(api, CERT, t0, t1, t2, t3); + } + + private void signatureValidityTest(OpenPGPApi api, String cert, TestSignature... testSignatures) + throws IOException + { + OpenPGPCertificate certificate = api.readKeyOrCertificate().parseCertificate(cert); + + for (TestSignature test : testSignatures) + { + PGPSignature signature = test.getSignature(); + OpenPGPCertificate.OpenPGPComponentKey signingKey = certificate.getSigningKeyFor(signature); + + boolean valid = signingKey.isBoundAt(signature.getCreationTime()); + if (valid != test.isExpectValid()) + { + StringBuilder sb = new StringBuilder("Key validity mismatch. Expected " + signingKey.toString() + + (test.isExpectValid() ? (" to be valid at ") : (" to be invalid at ")) + UTCUtil.format(signature.getCreationTime())); + if (test.getMsg() != null) + { + sb.append(" because:\n").append(test.getMsg()); + } + sb.append("\n").append(signingKey.getSignatureChains()); + fail(sb.toString()); + } + } + } + + private void testGetPrimaryUserId(OpenPGPApi api) + throws PGPException + { + Date now = new Date((new Date().getTime() / 1000) * 1000); + Date oneHourAgo = new Date(now.getTime() - 1000 * 60 * 60); + + OpenPGPKeyGenerator gen = api.generateKey(oneHourAgo); + OpenPGPKey key = gen.withPrimaryKey() + .addUserId("Old non-primary ") + .addUserId("New primary ", + SignatureParameters.Callback.modifyHashedSubpackets(new SignatureSubpacketsFunction() + { + @Override + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.removePacketsOfType(SignatureSubpacketTags.CREATION_TIME); + subpackets.setSignatureCreationTime(now); + subpackets.setPrimaryUserID(false, true); + return subpackets; + } + })) + .build(null); + isEquals("Expect to find valid, explicit primary user ID", + key.getUserId("New primary "), + key.getPrimaryUserId()); + + isEquals("Explicit primary UserID is not yet valid, so return implicit UID", + key.getUserId("Old non-primary "), + key.getPrimaryUserId(oneHourAgo)); + } + + public static class TestSignature + { + private final PGPSignature signature; + private final boolean expectValid; + private final String msg; + + public TestSignature(String armoredSignature, boolean expectValid) + throws IOException + { + this(armoredSignature, expectValid, null); + } + + public TestSignature(String armoredSignature, boolean expectValid, String msg) + throws IOException + { + this.signature = parseSignature(armoredSignature); + this.expectValid = expectValid; + this.msg = msg; + } + + private static PGPSignature parseSignature(String armoredSignature) + throws IOException + { + ByteArrayInputStream bIn = new ByteArrayInputStream(armoredSignature.getBytes(StandardCharsets.UTF_8)); + ArmoredInputStream aIn = new ArmoredInputStream(bIn); + BCPGInputStream pIn = new BCPGInputStream(aIn); + PGPObjectFactory objFac = new BcPGPObjectFactory(pIn); + + PGPSignatureList sigs = (PGPSignatureList) objFac.nextObject(); + + pIn.close(); + aIn.close(); + bIn.close(); + + return sigs.get(0); + } + + public PGPSignature getSignature() + { + return signature; + } + + public boolean isExpectValid() + { + return expectValid; + } + + public String getMsg() + { + return msg; + } + } + + public static void main(String[] args) + { + runTest(new OpenPGPCertificateTest()); + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java new file mode 100644 index 0000000000..8d08d4fcf1 --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java @@ -0,0 +1,275 @@ +package org.bouncycastle.openpgp.api.test; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.List; + +import org.bouncycastle.bcpg.SignatureSubpacketTags; +import org.bouncycastle.bcpg.sig.KeyFlags; +import org.bouncycastle.openpgp.OpenPGPTestKeys; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyPair; +import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.bouncycastle.openpgp.api.KeyPairGeneratorCallback; +import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.OpenPGPDetachedSignatureGenerator; +import org.bouncycastle.openpgp.api.OpenPGPDetachedSignatureProcessor; +import org.bouncycastle.openpgp.api.OpenPGPKey; +import org.bouncycastle.openpgp.api.OpenPGPSignature; +import org.bouncycastle.openpgp.api.SignatureParameters; +import org.bouncycastle.openpgp.api.SignatureSubpacketsFunction; +import org.bouncycastle.openpgp.operator.PGPKeyPairGenerator; + +public class OpenPGPDetachedSignatureProcessorTest + extends APITest +{ + @Override + public String getName() + { + return "OpenPGPDetachedSignatureProcessorTest"; + } + + protected void performTestWith(OpenPGPApi api) + throws PGPException, IOException + { + createVerifyV4Signature(api); + createVerifyV6Signature(api); + + keyPassphrasesArePairedUpProperly_keyAddedFirst(api); + keyPassphrasesArePairedUpProperly_passphraseAddedFirst(api); + + missingPassphraseThrows(api); + wrongPassphraseThrows(api); + + withoutSigningSubkeyFails(api); + nonSigningSubkeyFails(api); + } + + private void createVerifyV4Signature(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPDetachedSignatureGenerator gen = api.createDetachedSignature(); + gen.addSigningKey( + api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.ALICE_KEY)); + + byte[] plaintext = "Hello, World!\n".getBytes(StandardCharsets.UTF_8); + ByteArrayInputStream plaintextIn = new ByteArrayInputStream(plaintext); + + List signatures = gen.sign(plaintextIn); + isEquals(1, signatures.size()); + OpenPGPSignature.OpenPGPDocumentSignature signature = signatures.get(0); + isEquals(4, signature.getSignature().getVersion()); + String armored = signature.toAsciiArmoredString(); + isTrue(armored.startsWith("-----BEGIN PGP SIGNATURE-----\n")); + + // Verify detached signatures + OpenPGPDetachedSignatureProcessor processor = api.verifyDetachedSignature(); + processor.addSignature(signature.getSignature()); + processor.addVerificationCertificate(api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.ALICE_CERT)); + + List verified = processor.process(new ByteArrayInputStream(plaintext)); + isEquals(1, verified.size()); + isTrue(verified.get(0).isValid()); + } + + private void createVerifyV6Signature(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPDetachedSignatureGenerator gen = api.createDetachedSignature(); + gen.addSigningKey( + api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY)); + + byte[] plaintext = "Hello, World!\n".getBytes(StandardCharsets.UTF_8); + ByteArrayInputStream plaintextIn = new ByteArrayInputStream(plaintext); + + List signatures = gen.sign(plaintextIn); + isEquals(1, signatures.size()); + OpenPGPSignature.OpenPGPDocumentSignature signature = signatures.get(0); + isEquals(6, signature.getSignature().getVersion()); + String armored = signature.toAsciiArmoredString(); + isTrue(armored.startsWith("-----BEGIN PGP SIGNATURE-----\n")); + + // Verify detached signatures + OpenPGPDetachedSignatureProcessor processor = api.verifyDetachedSignature(); + processor.addSignature(signature.getSignature()); + processor.addVerificationCertificate(api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.V6_CERT)); + + List verified = processor.process(new ByteArrayInputStream(plaintext)); + isEquals(1, verified.size()); + isTrue(verified.get(0).isValid()); + } + + private void missingPassphraseThrows(OpenPGPApi api) + { + isNotNull(testException( + "Cannot unlock primary key CB186C4F0609A697E4D52DFA6C722B0C1F1E27C18A56708F6525EC27BAD9ACC9: Exception decrypting key", + "KeyPassphraseException", + new TestExceptionOperation() + { + @Override + public void operation() + throws Exception + { + api.createDetachedSignature() + .addSigningKey(api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY_LOCKED)) + .sign(new ByteArrayInputStream("Test Data".getBytes(StandardCharsets.UTF_8))); + } + })); + } + + private void wrongPassphraseThrows(OpenPGPApi api) + { + isNotNull(testException( + "Cannot unlock primary key CB186C4F0609A697E4D52DFA6C722B0C1F1E27C18A56708F6525EC27BAD9ACC9: Exception decrypting key", + "KeyPassphraseException", + new TestExceptionOperation() + { + @Override + public void operation() + throws Exception + { + api.createDetachedSignature() + .addKeyPassphrase("thisIsWrong".toCharArray()) + .addSigningKey(api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY_LOCKED)) + .sign(new ByteArrayInputStream("Test Data".getBytes(StandardCharsets.UTF_8))); + } + })); + } + + private void keyPassphrasesArePairedUpProperly_keyAddedFirst(OpenPGPApi api) + throws PGPException, IOException + { + OpenPGPKey key = api.generateKey(new Date(), false) + .signOnlyKey() + .build("password".toCharArray()); + + OpenPGPDetachedSignatureGenerator gen = api.createDetachedSignature(); + gen.addSigningKey(key); + + gen.addKeyPassphrase("penguin".toCharArray()); + gen.addKeyPassphrase("password".toCharArray()); + gen.addKeyPassphrase("beluga".toCharArray()); + + byte[] plaintext = "arctic\ndeep sea\nice field\n".getBytes(StandardCharsets.UTF_8); + InputStream plaintextIn = new ByteArrayInputStream(plaintext); + + List signatures = gen.sign(plaintextIn); + isEquals(1, signatures.size()); + } + + private void keyPassphrasesArePairedUpProperly_passphraseAddedFirst(OpenPGPApi api) + throws PGPException, IOException + { + OpenPGPKey key = api.generateKey(new Date(), false) + .signOnlyKey() + .build("password".toCharArray()); + + OpenPGPDetachedSignatureGenerator gen = api.createDetachedSignature(); + + gen.addKeyPassphrase("sloth".toCharArray()); + gen.addKeyPassphrase("password".toCharArray()); + gen.addKeyPassphrase("tapir".toCharArray()); + + gen.addSigningKey(key); + + byte[] plaintext = "jungle\ntropics\nswamp\n".getBytes(StandardCharsets.UTF_8); + InputStream plaintextIn = new ByteArrayInputStream(plaintext); + + List signatures = gen.sign(plaintextIn); + isEquals(1, signatures.size()); + } + + private void withoutSigningSubkeyFails(OpenPGPApi api) + throws PGPException + { + OpenPGPKey noSigningKey = api.generateKey() + .withPrimaryKey( + new KeyPairGeneratorCallback() { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException { + return generator.generatePrimaryKey(); + } + }, + SignatureParameters.Callback.modifyHashedSubpackets( + new SignatureSubpacketsFunction() + { + @Override + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS); + // No SIGN_DATA key flag + subpackets.setKeyFlags(KeyFlags.CERTIFY_OTHER); + return subpackets; + } + } + ) + ).build(); + + isNotNull(testException( + "The key " + noSigningKey.getKeyIdentifier() + " does not contain any usable component keys capable of signing.", + "InvalidSigningKeyException", + new TestExceptionOperation() + { + @Override + public void operation() + throws Exception + { + api.createDetachedSignature() + .addSigningKey(noSigningKey) + .sign(new ByteArrayInputStream("Test Data".getBytes(StandardCharsets.UTF_8))); + } + })); + } + + private void nonSigningSubkeyFails(OpenPGPApi api) + throws PGPException + { + OpenPGPKey noSigningKey = api.generateKey() + .withPrimaryKey( + new KeyPairGeneratorCallback() { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException { + return generator.generatePrimaryKey(); + } + }, + SignatureParameters.Callback.modifyHashedSubpackets( + new SignatureSubpacketsFunction() + { + @Override + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS); + // No SIGN_DATA key flag + subpackets.setKeyFlags(KeyFlags.CERTIFY_OTHER); + return subpackets; + } + } + ) + ).build(); + + isNotNull(testException( + "The primary key " + noSigningKey.getPrimaryKey().getKeyIdentifier() + " is not usable for signing.", + "InvalidSigningKeyException", + new TestExceptionOperation() + { + @Override + public void operation() + throws Exception + { + api.createDetachedSignature() + .addSigningKey(noSigningKey.getPrimarySecretKey(), (char[])null, null) + .sign(new ByteArrayInputStream("Test Data".getBytes(StandardCharsets.UTF_8))); + } + })); + } + + public static void main(String[] args) + { + runTest(new OpenPGPDetachedSignatureProcessorTest()); + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPKeyEditorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPKeyEditorTest.java new file mode 100644 index 0000000000..b2846220c9 --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPKeyEditorTest.java @@ -0,0 +1,281 @@ +package org.bouncycastle.openpgp.api.test; + +import java.io.IOException; +import java.util.Date; + +import org.bouncycastle.bcpg.SecretKeyPacket; +import org.bouncycastle.bcpg.sig.RevocationReasonTags; +import org.bouncycastle.openpgp.OpenPGPTestKeys; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.OpenPGPCertificate; +import org.bouncycastle.openpgp.api.OpenPGPKey; +import org.bouncycastle.openpgp.api.SignatureParameters; +import org.bouncycastle.openpgp.api.SignatureSubpacketsFunction; + +public class OpenPGPKeyEditorTest + extends APITest +{ + + @Override + public String getName() + { + return "OpenPGPKeyEditorTest"; + } + + protected void performTestWith(OpenPGPApi api) + throws PGPException, IOException + { + doNothingTest(api); + + addUserIdTest(api); + softRevokeUserIdTest(api); + hardRevokeUserIdTest(api); + + addEncryptionSubkeyTest(api); + revokeEncryptionSubkeyTest(api); + + addSigningSubkeyTest(api); + revokeSigningSubkeyTest(api); + + extendExpirationTimeTest(api); + revokeCertificateTest(api); + + changePassphraseUnprotectedToCFBTest(api); + changePassphraseUnprotectedToAEADTest(api); + } + + private void doNothingTest(OpenPGPApi api) + throws PGPException + { + OpenPGPKey key = api.generateKey() + .ed25519x25519Key("Alice ") + .build(); + OpenPGPKey editedKey = api.editKey(key) + .done(); + + isTrue("Key was not changed, so the reference MUST be the same", + key == editedKey); + } + + private void addUserIdTest(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate() + .parseKey(OpenPGPTestKeys.V6_KEY); + isNull("Expect primary user-id to be null", key.getPrimaryUserId()); + + key = api.editKey(key) + .addUserId("Alice ") + .done(); + + isEquals("Expect the new user-id to be primary now", + "Alice ", key.getPrimaryUserId().getUserId()); + } + + private void softRevokeUserIdTest(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate() + .parseKey(OpenPGPTestKeys.ALICE_KEY); + Date now = currentTimeRounded(); + Date oneHourAgo = new Date(now.getTime() - (1000 * 60 * 60)); + OpenPGPCertificate.OpenPGPUserId userId = key.getPrimaryUserId(now); + isNotNull(userId); + isTrue(userId.isBound()); + isEquals("Alice Lovelace ", userId.getUserId()); + + key = api.editKey(key) + .revokeIdentity(userId, new SignatureParameters.Callback() + { + @Override + public SignatureParameters apply(SignatureParameters parameters) + { + parameters.setSignatureCreationTime(now); + parameters.setHashedSubpacketsFunction(new SignatureSubpacketsFunction() + { + @Override + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.setRevocationReason(true, RevocationReasonTags.USER_NO_LONGER_VALID, ""); + return subpackets; + } + }); + return parameters; + } + }) + .done(); + isTrue(key.getPrimaryUserId().isBoundAt(oneHourAgo)); + isFalse(key.getPrimaryUserId().isBoundAt(now)); + } + + private void hardRevokeUserIdTest(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate() + .parseKey(OpenPGPTestKeys.ALICE_KEY); + Date now = currentTimeRounded(); + Date oneHourAgo = new Date(now.getTime() - (1000 * 60 * 60)); + OpenPGPCertificate.OpenPGPUserId userId = key.getPrimaryUserId(now); + isNotNull(userId); + isTrue(userId.isBound()); + isEquals("Alice Lovelace ", userId.getUserId()); + + key = api.editKey(key) + .revokeIdentity(userId, new SignatureParameters.Callback() + { + @Override + public SignatureParameters apply(SignatureParameters parameters) + { + parameters.setSignatureCreationTime(now); + // no reason -> hard revocation + return parameters; + } + }) + .done(); + isFalse(key.getPrimaryUserId().isBoundAt(oneHourAgo)); + isFalse(key.getPrimaryUserId().isBoundAt(now)); + } + + private void addEncryptionSubkeyTest(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); + isEquals(1, key.getEncryptionKeys().size()); + + key = api.editKey(key) + .addEncryptionSubkey() + .done(); + + isEquals(2, key.getEncryptionKeys().size()); + } + + private void revokeEncryptionSubkeyTest(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); + OpenPGPCertificate.OpenPGPComponentKey encryptionSubkey = key.getEncryptionKeys().get(0); + + key = api.editKey(key) + .revokeComponentKey(encryptionSubkey) + .done(); + + isEquals(0, key.getEncryptionKeys().size()); + } + + private void addSigningSubkeyTest(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); + isEquals(1, key.getSigningKeys().size()); + + key = api.editKey(key) + .addSigningSubkey() + .done(); + + isEquals(2, key.getSigningKeys().size()); + } + + private void revokeSigningSubkeyTest(OpenPGPApi api) + throws PGPException + { + OpenPGPKey key = api.generateKey() + .classicKey(null) + .build(); + isEquals(1, key.getSigningKeys().size()); + + OpenPGPCertificate.OpenPGPComponentKey signingKey = key.getSigningKeys().get(0); + key = api.editKey(key) + .revokeComponentKey(signingKey) + .done(); + isEquals(0, key.getSigningKeys().size()); + } + + private void extendExpirationTimeTest(OpenPGPApi api) + throws PGPException + { + Date n0 = currentTimeRounded(); + OpenPGPKey key = api.generateKey(n0, false) + .classicKey(null) + .build(); + isEquals("Default key generation MUST set expiration time of +5years", + key.getExpirationTime().getTime(), n0.getTime() + 5L * 31536000 * 1000); + + Date n1 = new Date(n0.getTime() + 1000); // 1 sec later + + key = api.editKey(key) + .addDirectKeySignature(new SignatureParameters.Callback() + { + @Override + public SignatureParameters apply(SignatureParameters parameters) + { + parameters.setSignatureCreationTime(n1); + parameters.setHashedSubpacketsFunction(new SignatureSubpacketsFunction() + { + @Override + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.setKeyExpirationTime(8L * 31536000); + return subpackets; + } + }); + return parameters; + } + }) + .done(); + + isEquals("At n1, the expiration time of the key MUST have changed to n0+8years", + key.getExpirationTime(n1).getTime(), n0.getTime() + 8L * 31536000 * 1000); + } + + private void revokeCertificateTest(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); + + key = api.editKey(key) + .revokeKey() + .done(); + + isEquals(0, key.getEncryptionKeys().size()); + } + + private void changePassphraseUnprotectedToCFBTest(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); + isFalse(key.getPrimarySecretKey().isLocked()); + + key = api.editKey(key) + .changePassphrase(key.getPrimaryKey().getKeyIdentifier(), null, "sw0rdf1sh".toCharArray(), false) + .done(); + isTrue("Expect key to be locked", key.getPrimarySecretKey().isLocked()); + isTrue("Expect sw0rdf1sh to be the correct passphrase", + key.getPrimarySecretKey().isPassphraseCorrect("sw0rdf1sh".toCharArray())); + isEquals("Expect use of USAGE_CHECKSUM for key protection", + SecretKeyPacket.USAGE_SHA1, key.getPrimarySecretKey().getPGPSecretKey().getS2KUsage()); + } + + private void changePassphraseUnprotectedToAEADTest(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); + isFalse("Expect key to be unprotected", key.getPrimarySecretKey().isLocked()); + + key = api.editKey(key) + .changePassphrase(key.getPrimaryKey().getKeyIdentifier(), null, "sw0rdf1sh".toCharArray(), true) + .done(); + isTrue("Expect key to be locked after changing passphrase", + key.getPrimarySecretKey().isLocked()); + isTrue("Expect sw0rdf1sh to be the correct passphrase using AEAD", + key.getPrimarySecretKey().isPassphraseCorrect("sw0rdf1sh".toCharArray())); + isEquals("Expect use of AEAD for key protection", + SecretKeyPacket.USAGE_AEAD, key.getPrimarySecretKey().getPGPSecretKey().getS2KUsage()); + } + + public static void main(String[] args) + { + runTest(new OpenPGPKeyEditorTest()); + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPKeyReaderTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPKeyReaderTest.java new file mode 100644 index 0000000000..e89be77bee --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPKeyReaderTest.java @@ -0,0 +1,91 @@ +package org.bouncycastle.openpgp.api.test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.List; + +import org.bouncycastle.bcpg.ArmoredOutputStream; +import org.bouncycastle.bcpg.BCPGOutputStream; +import org.bouncycastle.bcpg.PacketFormat; +import org.bouncycastle.openpgp.OpenPGPTestKeys; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.OpenPGPCertificate; +import org.bouncycastle.openpgp.api.OpenPGPKey; + +public class OpenPGPKeyReaderTest + extends APITest +{ + @Override + public String getName() + { + return "OpenPGPKeyReaderTest"; + } + + @Override + protected void performTestWith(OpenPGPApi api) + throws PGPException, IOException + { + testParseEmptyCollection(api); + testParse2CertsCertificateCollection(api); + testParseCertAndKeyToCertificateCollection(api); + } + + private void testParseEmptyCollection(OpenPGPApi api) + throws IOException + { + byte[] empty = new byte[0]; + List certs = api.readKeyOrCertificate().parseKeysOrCertificates(empty); + isTrue(certs.isEmpty()); + } + + private void testParse2CertsCertificateCollection(OpenPGPApi api) + throws IOException + { + OpenPGPCertificate alice = api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.ALICE_CERT); + OpenPGPCertificate bob = api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.BOB_CERT); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ArmoredOutputStream aOut = ArmoredOutputStream.builder().clearHeaders().build(bOut); + BCPGOutputStream pOut = new BCPGOutputStream(aOut, PacketFormat.CURRENT); + alice.getPGPPublicKeyRing().encode(pOut); + bob.getPGPPublicKeyRing().encode(pOut); + pOut.close(); + aOut.close(); + + List certs = api.readKeyOrCertificate().parseKeysOrCertificates(bOut.toByteArray()); + isEquals("Collection MUST contain both items", 2, certs.size()); + + isEquals(alice.getKeyIdentifier(), certs.get(0).getKeyIdentifier()); + isEquals(bob.getKeyIdentifier(), certs.get(1).getKeyIdentifier()); + } + + private void testParseCertAndKeyToCertificateCollection(OpenPGPApi api) + throws IOException + { + OpenPGPCertificate alice = api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.ALICE_CERT); + OpenPGPKey bob = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.BOB_KEY); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ArmoredOutputStream aOut = ArmoredOutputStream.builder().clearHeaders().build(bOut); + BCPGOutputStream pOut = new BCPGOutputStream(aOut, PacketFormat.CURRENT); + alice.getPGPPublicKeyRing().encode(pOut); + bob.getPGPSecretKeyRing().encode(pOut); + pOut.close(); + aOut.close(); + + List certs = api.readKeyOrCertificate().parseKeysOrCertificates(bOut.toByteArray()); + isEquals("Collection MUST contain both items", 2, certs.size()); + + isEquals(alice.getKeyIdentifier(), certs.get(0).getKeyIdentifier()); + isFalse(certs.get(0).isSecretKey()); + + isEquals(bob.getKeyIdentifier(), certs.get(1).getKeyIdentifier()); + isTrue(certs.get(1).isSecretKey()); + } + + public static void main(String[] args) + { + runTest(new OpenPGPKeyReaderTest()); + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java new file mode 100644 index 0000000000..4dd960f1a0 --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java @@ -0,0 +1,177 @@ +package org.bouncycastle.openpgp.api.test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; + +import org.bouncycastle.bcpg.CompressionAlgorithmTags; +import org.bouncycastle.openpgp.OpenPGPTestKeys; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.OpenPGPCertificate; +import org.bouncycastle.openpgp.api.OpenPGPKey; +import org.bouncycastle.openpgp.api.OpenPGPMessageGenerator; +import org.bouncycastle.openpgp.api.OpenPGPMessageOutputStream; +import org.bouncycastle.util.encoders.Hex; + +public class OpenPGPMessageGeneratorTest + extends APITest +{ + @Override + public String getName() + { + return "OpenPGPMessageGeneratorTest"; + } + + protected void performTestWith(OpenPGPApi api) + throws PGPException, IOException + { + armoredLiteralDataPacket(api); + unarmoredLiteralDataPacket(api); + + armoredCompressedLiteralDataPacket(api); + unarmoredCompressedLiteralDataPacket(api); + + seipd1EncryptedMessage(api); + seipd2EncryptedMessage(api); + + seipd2EncryptedSignedMessage(api); + } + + private void armoredLiteralDataPacket(OpenPGPApi api) + throws PGPException, IOException + { + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() + .setAllowPadding(false); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OpenPGPMessageOutputStream msgOut = gen.open(bOut); + + // Only write a LiteralData packet with "Hello, World!" as content + msgOut.write("Hello, World!".getBytes(StandardCharsets.UTF_8)); + + msgOut.close(); + + isEquals( + "-----BEGIN PGP MESSAGE-----\n" + + "\n" + + "yxNiAAAAAABIZWxsbywgV29ybGQh\n" + + "-----END PGP MESSAGE-----\n", + bOut.toString()); + } + + private void unarmoredLiteralDataPacket(OpenPGPApi api) + throws PGPException, IOException + { + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() + .setArmored(false) // disable ASCII armor + .setAllowPadding(false); // disable padding + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OpenPGPMessageOutputStream msgOut = gen.open(bOut); + + // Only write a LiteralData packet with "Hello, World!" as content + msgOut.write("Hello, World!".getBytes(StandardCharsets.UTF_8)); + + msgOut.close(); + + isEncodingEqual(Hex.decode("cb1362000000000048656c6c6f2c20576f726c6421"), bOut.toByteArray()); + } + + private void armoredCompressedLiteralDataPacket(OpenPGPApi api) + throws PGPException, IOException + { + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() + .setAllowPadding(false) + .setCompressionNegotiator((conf, neg) -> CompressionAlgorithmTags.ZIP); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OpenPGPMessageOutputStream msgOut = gen.open(bOut); + + // Only write a LiteralData packet with "Hello, World!" as content + msgOut.write("Hello, World!".getBytes(StandardCharsets.UTF_8)); + + msgOut.close(); + + isEquals("-----BEGIN PGP MESSAGE-----\n" + + "\n" + + "yBUBOy2cxAACHqk5Ofk6CuH5RTkpigA=\n" + + "-----END PGP MESSAGE-----\n", + bOut.toString()); + } + + private void unarmoredCompressedLiteralDataPacket(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() + .setArmored(false) // no armor + .setAllowPadding(false) + .setCompressionNegotiator((conf, neg) -> CompressionAlgorithmTags.ZIP); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OpenPGPMessageOutputStream msgOut = gen.open(bOut); + + // Only write a LiteralData packet with "Hello, World!" as content + msgOut.write("Hello, World!".getBytes(StandardCharsets.UTF_8)); + + msgOut.close(); + + isEncodingEqual(Hex.decode("c815013b2d9cc400021ea93939f93a0ae1f94539298a00"), bOut.toByteArray()); + } + + private void seipd2EncryptedMessage(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPCertificate cert = api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.V6_CERT); + + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage(); + gen.addEncryptionCertificate(cert); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OutputStream encOut = gen.open(bOut); + encOut.write("Hello World!\n".getBytes(StandardCharsets.UTF_8)); + encOut.close(); + + System.out.println(bOut); + } + + private void seipd1EncryptedMessage(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.BOB_KEY); + + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage(); + gen.addEncryptionCertificate(key); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OutputStream encOut = gen.open(bOut); + encOut.write("Hello World!\n".getBytes(StandardCharsets.UTF_8)); + encOut.close(); + + System.out.println(bOut); + } + + private void seipd2EncryptedSignedMessage(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); + + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() + .setAllowPadding(true) + .setArmored(true) + .addSigningKey(key) + .addEncryptionCertificate(key); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OutputStream encOut = gen.open(bOut); + encOut.write("Hello, World!\n".getBytes()); + encOut.close(); + + System.out.println(bOut); + } + + public static void main(String[] args) + { + runTest(new OpenPGPMessageGeneratorTest()); + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java new file mode 100644 index 0000000000..ec634a4e5b --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java @@ -0,0 +1,652 @@ +package org.bouncycastle.openpgp.api.test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import org.bouncycastle.bcpg.AEADAlgorithmTags; +import org.bouncycastle.bcpg.CompressionAlgorithmTags; +import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; +import org.bouncycastle.openpgp.OpenPGPTestKeys; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPSessionKey; +import org.bouncycastle.openpgp.api.MessageEncryptionMechanism; +import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.OpenPGPCertificate; +import org.bouncycastle.openpgp.api.OpenPGPEncryptionNegotiator; +import org.bouncycastle.openpgp.api.OpenPGPKey; +import org.bouncycastle.openpgp.api.OpenPGPMessageGenerator; +import org.bouncycastle.openpgp.api.OpenPGPMessageInputStream; +import org.bouncycastle.openpgp.api.OpenPGPMessageOutputStream; +import org.bouncycastle.openpgp.api.OpenPGPMessageProcessor; +import org.bouncycastle.openpgp.api.OpenPGPSignature; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.io.Streams; + +public class OpenPGPMessageProcessorTest + extends APITest +{ + private static final byte[] PLAINTEXT = "Hello, World!\n".getBytes(StandardCharsets.UTF_8); + + private PGPSessionKey encryptionSessionKey; + + @Override + public String getName() + { + return "OpenPGPMessageProcessorTest"; + } + + protected void performTestWith(OpenPGPApi api) + throws PGPException, IOException + { + testVerificationOfSEIPD1MessageWithTamperedCiphertext(api); + + roundtripUnarmoredPlaintextMessage(api); + roundtripArmoredPlaintextMessage(api); + roundTripCompressedMessage(api); + roundTripCompressedSymEncMessageMessage(api); + + roundTripSymEncMessageWithMultiplePassphrases(api); + + roundTripV4KeyEncryptedMessageAlice(api); + roundTripV4KeyEncryptedMessageBob(api); + + roundTripV6KeyEncryptedMessage(api); + encryptWithV4V6KeyDecryptWithV4(api); + encryptWithV4V6KeyDecryptWithV6(api); + + encryptDecryptWithLockedKey(api); + encryptDecryptWithMissingKey(api); + + inlineSignWithV4KeyAlice(api); + inlineSignWithV4KeyBob(api); + inlineSignWithV6Key(api); + + verifyMessageByRevokedKey(api); + incompleteMessageProcessing(api); + } + + private void roundtripUnarmoredPlaintextMessage(OpenPGPApi api) + throws PGPException, IOException + { + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() + .setArmored(false) + .setAllowPadding(false) + .setCompressionNegotiator((conf, neg) -> CompressionAlgorithmTags.UNCOMPRESSED); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OutputStream msgOut = gen.open(bOut); + msgOut.write(PLAINTEXT); + msgOut.close(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage(); + OpenPGPMessageInputStream plainIn = processor.process(bIn); + ByteArrayOutputStream plainOut = new ByteArrayOutputStream(); + Streams.pipeAll(plainIn, plainOut); + plainIn.close(); + isEquals(MessageEncryptionMechanism.unencrypted(), plainIn.getResult().getEncryptionMethod()); + + isEncodingEqual(PLAINTEXT, plainOut.toByteArray()); + } + + private void roundtripArmoredPlaintextMessage(OpenPGPApi api) + throws PGPException, IOException + { + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() + .setArmored(true) + .setAllowPadding(false) + .setCompressionNegotiator((conf, neg) -> CompressionAlgorithmTags.UNCOMPRESSED); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OutputStream msgOut = gen.open(bOut); + msgOut.write(PLAINTEXT); + msgOut.close(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage(); + OpenPGPMessageInputStream plainIn = processor.process(bIn); + ByteArrayOutputStream plainOut = new ByteArrayOutputStream(); + Streams.pipeAll(plainIn, plainOut); + plainIn.close(); + OpenPGPMessageInputStream.Result result = plainIn.getResult(); + isEquals(MessageEncryptionMechanism.unencrypted(), result.getEncryptionMethod()); + + isEncodingEqual(PLAINTEXT, plainOut.toByteArray()); + } + + private void roundTripCompressedMessage(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() + .setArmored(true) + .setAllowPadding(false) + .setCompressionNegotiator((conf, neg) -> CompressionAlgorithmTags.ZIP); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OutputStream msgOut = gen.open(bOut); + msgOut.write(PLAINTEXT); + msgOut.close(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage(); + InputStream plainIn = processor.process(bIn); + ByteArrayOutputStream plainOut = new ByteArrayOutputStream(); + Streams.pipeAll(plainIn, plainOut); + plainIn.close(); + + isEncodingEqual(PLAINTEXT, plainOut.toByteArray()); + } + + private void roundTripCompressedSymEncMessageMessage(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() + .setArmored(true) + .addEncryptionPassphrase("lal".toCharArray()) + .setSessionKeyExtractionCallback( + sk -> this.encryptionSessionKey = sk + ) + .setAllowPadding(false) + .setPasswordBasedEncryptionNegotiator(new OpenPGPEncryptionNegotiator() + { + @Override + public MessageEncryptionMechanism negotiateEncryption(OpenPGPMessageGenerator configuration) + { + return MessageEncryptionMechanism.integrityProtected(SymmetricKeyAlgorithmTags.AES_256); + } + }) + .setCompressionNegotiator( + (conf, neg) -> CompressionAlgorithmTags.ZIP); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OutputStream msgOut = gen.open(bOut); + msgOut.write(PLAINTEXT); + msgOut.close(); + isNotNull(encryptionSessionKey); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); + OpenPGPMessageInputStream plainIn = api.decryptAndOrVerifyMessage() + .addMessagePassphrase("lal".toCharArray()) + .process(bIn); + ByteArrayOutputStream plainOut = new ByteArrayOutputStream(); + Streams.pipeAll(plainIn, plainOut); + plainIn.close(); + OpenPGPMessageInputStream.Result result = plainIn.getResult(); + isEquals(CompressionAlgorithmTags.ZIP, result.getCompressionAlgorithm()); + isTrue(Arrays.areEqual("lal".toCharArray(), result.getDecryptionPassphrase())); + isEncodingEqual(encryptionSessionKey.getKey(), result.getSessionKey().getKey()); + + isEncodingEqual(PLAINTEXT, plainOut.toByteArray()); + } + + private void roundTripSymEncMessageWithMultiplePassphrases(OpenPGPApi api) + throws PGPException, IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() + .addEncryptionPassphrase("orange".toCharArray()) + .addEncryptionPassphrase("violet".toCharArray()) + .setSessionKeyExtractionCallback(sk -> this.encryptionSessionKey = sk) + .setPasswordBasedEncryptionNegotiator( + new OpenPGPEncryptionNegotiator() + { + @Override + public MessageEncryptionMechanism negotiateEncryption(OpenPGPMessageGenerator configuration) + { + return MessageEncryptionMechanism.aead(SymmetricKeyAlgorithmTags.AES_128, AEADAlgorithmTags.OCB); + } + } + ); + + OutputStream encOut = gen.open(bOut); + encOut.write(PLAINTEXT); + encOut.close(); + + byte[] ciphertext = bOut.toByteArray(); + ByteArrayInputStream bIn = new ByteArrayInputStream(ciphertext); + bOut = new ByteArrayOutputStream(); + + // Try decryption with explicitly set message passphrase + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage(); + processor.addMessagePassphrase("violet".toCharArray()); + OpenPGPMessageInputStream decIn = processor.process(bIn); + Streams.pipeAll(decIn, bOut); + decIn.close(); + OpenPGPMessageInputStream.Result result = decIn.getResult(); + isTrue(Arrays.areEqual("violet".toCharArray(), result.getDecryptionPassphrase())); + isEncodingEqual(encryptionSessionKey.getKey(), result.getSessionKey().getKey()); + isEncodingEqual(PLAINTEXT, bOut.toByteArray()); + isEquals(result.getEncryptionMethod(), + MessageEncryptionMechanism.aead(SymmetricKeyAlgorithmTags.AES_128, AEADAlgorithmTags.OCB)); + + // Try decryption with wrong passphrase and then request proper one dynamically + bOut = new ByteArrayOutputStream(); + bIn = new ByteArrayInputStream(ciphertext); + processor = api.decryptAndOrVerifyMessage(); + decIn = processor.setMissingMessagePassphraseCallback(new StackMessagePassphraseCallback("orange".toCharArray())) + // wrong passphrase, so missing callback is invoked + .addMessagePassphrase("yellow".toCharArray()) + .process(bIn); + + Streams.pipeAll(decIn, bOut); + decIn.close(); + result = decIn.getResult(); + isTrue(Arrays.areEqual("orange".toCharArray(), result.getDecryptionPassphrase())); + isEncodingEqual(encryptionSessionKey.getKey(), result.getSessionKey().getKey()); + isEncodingEqual(PLAINTEXT, bOut.toByteArray()); + } + + private void roundTripV4KeyEncryptedMessageAlice(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage(); + gen.addEncryptionCertificate(api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.ALICE_CERT)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OutputStream enc = gen.open(bOut); + enc.write(PLAINTEXT); + enc.close(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage(); + processor.addDecryptionKey(api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.ALICE_KEY)); + + OpenPGPMessageInputStream decIn = processor.process(bIn); + + bOut = new ByteArrayOutputStream(); + Streams.pipeAll(decIn, bOut); + isEncodingEqual(bOut.toByteArray(), PLAINTEXT); + OpenPGPMessageInputStream.Result result = decIn.getResult(); + isEquals(MessageEncryptionMechanism.integrityProtected(SymmetricKeyAlgorithmTags.AES_256), + result.getEncryptionMethod()); + } + + private void roundTripV4KeyEncryptedMessageBob(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage(); + gen.addEncryptionCertificate(api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.BOB_CERT)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OutputStream enc = gen.open(bOut); + enc.write(PLAINTEXT); + enc.close(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage(); + processor.addDecryptionKey(api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.BOB_KEY)); + + OpenPGPMessageInputStream decIn = processor.process(bIn); + + bOut = new ByteArrayOutputStream(); + Streams.pipeAll(decIn, bOut); + decIn.close(); + OpenPGPMessageInputStream.Result result = decIn.getResult(); + isEquals(MessageEncryptionMechanism.integrityProtected(SymmetricKeyAlgorithmTags.AES_256), + result.getEncryptionMethod()); + isEncodingEqual(bOut.toByteArray(), PLAINTEXT); + } + + private void roundTripV6KeyEncryptedMessage(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); + + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() + .setArmored(true) + .addEncryptionCertificate(key) + .setAllowPadding(false); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OutputStream msgOut = gen.open(bOut); + msgOut.write(PLAINTEXT); + msgOut.close(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage() + .addDecryptionKey(key); + + OpenPGPMessageInputStream plainIn = processor.process(bIn); + ByteArrayOutputStream plainOut = new ByteArrayOutputStream(); + Streams.pipeAll(plainIn, plainOut); + plainIn.close(); + OpenPGPMessageInputStream.Result result = plainIn.getResult(); + isEquals(MessageEncryptionMechanism.aead(SymmetricKeyAlgorithmTags.AES_256, AEADAlgorithmTags.OCB), + result.getEncryptionMethod()); + + isEncodingEqual(PLAINTEXT, plainOut.toByteArray()); + } + + private void encryptWithV4V6KeyDecryptWithV4(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage(); + gen.addEncryptionCertificate(api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.ALICE_CERT)); + gen.addEncryptionCertificate(api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.V6_CERT)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OutputStream enc = gen.open(bOut); + enc.write(PLAINTEXT); + enc.close(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage() + .addDecryptionKey(api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.ALICE_KEY)); + + OpenPGPMessageInputStream decIn = processor.process(bIn); + + bOut = new ByteArrayOutputStream(); + Streams.pipeAll(decIn, bOut); + decIn.close(); + isEncodingEqual(bOut.toByteArray(), PLAINTEXT); + OpenPGPMessageInputStream.Result result = decIn.getResult(); + isEquals(MessageEncryptionMechanism.integrityProtected(SymmetricKeyAlgorithmTags.AES_256), + result.getEncryptionMethod()); + } + + private void encryptWithV4V6KeyDecryptWithV6(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage(); + gen.addEncryptionCertificate(api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.ALICE_CERT)); + gen.addEncryptionCertificate(api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.V6_CERT)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OutputStream enc = gen.open(bOut); + enc.write(PLAINTEXT); + enc.close(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage() + .addDecryptionKey(api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY)); + + OpenPGPMessageInputStream decIn = processor.process(bIn); + + bOut = new ByteArrayOutputStream(); + Streams.pipeAll(decIn, bOut); + isEncodingEqual(bOut.toByteArray(), PLAINTEXT); + OpenPGPMessageInputStream.Result result = decIn.getResult(); + isEquals(MessageEncryptionMechanism.integrityProtected(SymmetricKeyAlgorithmTags.AES_256), + result.getEncryptionMethod()); + } + + private void encryptDecryptWithLockedKey(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY_LOCKED); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + OpenPGPMessageOutputStream encOut = api.signAndOrEncryptMessage() + .addEncryptionCertificate(key) + .open(bOut); + + encOut.write(PLAINTEXT); + encOut.close(); + + byte[] ciphertext = bOut.toByteArray(); + + // Provide passphrase and key together + ByteArrayInputStream bIn = new ByteArrayInputStream(ciphertext); + bOut = new ByteArrayOutputStream(); + OpenPGPMessageInputStream decIn = api.decryptAndOrVerifyMessage() + .addDecryptionKey(key, OpenPGPTestKeys.V6_KEY_LOCKED_PASSPHRASE.toCharArray()) + .process(bIn); + Streams.pipeAll(decIn, bOut); + decIn.close(); + isEncodingEqual(PLAINTEXT, bOut.toByteArray()); + OpenPGPMessageInputStream.Result result = decIn.getResult(); + PGPSessionKey sk = result.getSessionKey(); + + // Provide passphrase and key separate from another + bIn = new ByteArrayInputStream(ciphertext); + bOut = new ByteArrayOutputStream(); + decIn = api.decryptAndOrVerifyMessage() + .addDecryptionKey(key) + .addDecryptionKeyPassphrase(OpenPGPTestKeys.V6_KEY_LOCKED_PASSPHRASE.toCharArray()) + .process(bIn); + Streams.pipeAll(decIn, bOut); + decIn.close(); + isEncodingEqual(PLAINTEXT, bOut.toByteArray()); + result = decIn.getResult(); + isEncodingEqual(sk.getKey(), result.getSessionKey().getKey()); + + // Provide passphrase dynamically + bIn = new ByteArrayInputStream(ciphertext); + bOut = new ByteArrayOutputStream(); + decIn = api.decryptAndOrVerifyMessage() + .addDecryptionKey(key) + .setMissingOpenPGPKeyPassphraseProvider(k -> + OpenPGPTestKeys.V6_KEY_LOCKED_PASSPHRASE.toCharArray()) + .process(bIn); + Streams.pipeAll(decIn, bOut); + decIn.close(); + isEncodingEqual(PLAINTEXT, bOut.toByteArray()); + + result = decIn.getResult(); + isEncodingEqual(sk.getKey(), result.getSessionKey().getKey()); + } + + private void encryptDecryptWithMissingKey(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + OutputStream encOut = api.signAndOrEncryptMessage() + .addEncryptionCertificate(key) + .open(bOut); + + encOut.write(PLAINTEXT); + encOut.close(); + + byte[] ciphertext = bOut.toByteArray(); + + // Provide passphrase and key together + ByteArrayInputStream bIn = new ByteArrayInputStream(ciphertext); + bOut = new ByteArrayOutputStream(); + OpenPGPMessageInputStream decIn = api.decryptAndOrVerifyMessage() + .setMissingOpenPGPKeyProvider(id -> key) + .process(bIn); + Streams.pipeAll(decIn, bOut); + decIn.close(); + isEncodingEqual(PLAINTEXT, bOut.toByteArray()); + + OpenPGPMessageInputStream.Result result = decIn.getResult(); + isEquals(key, result.getDecryptionKey().getCertificate()); + isNotNull(result.getSessionKey()); + } + + private void inlineSignWithV4KeyAlice(OpenPGPApi api) + throws IOException, PGPException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage(); + OpenPGPKey aliceKey = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.ALICE_KEY); + gen.addSigningKey(aliceKey); + + OutputStream signOut = gen.open(bOut); + signOut.write(PLAINTEXT); + signOut.close(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); + bOut = new ByteArrayOutputStream(); + + OpenPGPCertificate aliceCert = api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.ALICE_CERT); + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage() + .addVerificationCertificate(aliceCert); + + OpenPGPMessageInputStream verifIn = processor.process(bIn); + Streams.pipeAll(verifIn, bOut); + verifIn.close(); + OpenPGPMessageInputStream.Result result = verifIn.getResult(); + isEquals(MessageEncryptionMechanism.unencrypted(), result.getEncryptionMethod()); + List signatures = result.getSignatures(); + isEquals(1, signatures.size()); + OpenPGPSignature.OpenPGPDocumentSignature sig = signatures.get(0); + isEquals(aliceCert, sig.getIssuerCertificate()); + + isEncodingEqual(PLAINTEXT, bOut.toByteArray()); + } + + private void inlineSignWithV4KeyBob(OpenPGPApi api) + throws IOException, PGPException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage(); + OpenPGPKey bobKey = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.BOB_KEY); + gen.addSigningKey(bobKey); + + OutputStream signOut = gen.open(bOut); + signOut.write(PLAINTEXT); + signOut.close(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); + bOut = new ByteArrayOutputStream(); + + OpenPGPCertificate bobCert = api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.BOB_CERT); + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage() + .addVerificationCertificate(bobCert); + + OpenPGPMessageInputStream verifIn = processor.process(bIn); + Streams.pipeAll(verifIn, bOut); + verifIn.close(); + OpenPGPMessageInputStream.Result result = verifIn.getResult(); + List signatures = result.getSignatures(); + isEquals(1, signatures.size()); + OpenPGPSignature.OpenPGPDocumentSignature sig = signatures.get(0); + isEquals(bobCert, sig.getIssuerCertificate()); + + isEncodingEqual(PLAINTEXT, bOut.toByteArray()); + } + + private void inlineSignWithV6Key(OpenPGPApi api) + throws PGPException, IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage(); + OpenPGPKey v6Key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); + gen.addSigningKey(v6Key); + + OutputStream signOut = gen.open(bOut); + signOut.write(PLAINTEXT); + signOut.close(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); + bOut = new ByteArrayOutputStream(); + + OpenPGPCertificate v6Cert = api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.V6_CERT); + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage() + .addVerificationCertificate(v6Cert); + + OpenPGPMessageInputStream verifIn = processor.process(bIn); + Streams.pipeAll(verifIn, bOut); + verifIn.close(); + OpenPGPMessageInputStream.Result result = verifIn.getResult(); + List signatures = result.getSignatures(); + isEquals(1, signatures.size()); + OpenPGPSignature.OpenPGPDocumentSignature sig = signatures.get(0); + isEquals(v6Cert, sig.getIssuerCertificate()); + + isEncodingEqual(PLAINTEXT, bOut.toByteArray()); + } + + private void verifyMessageByRevokedKey(OpenPGPApi api) + throws PGPException, IOException + { + // Create a minimal signed message + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.ALICE_KEY); + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage(); + gen.addSigningKey(key); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OpenPGPMessageOutputStream oOut = gen.open(bOut); + oOut.write("Hello, World!\n".getBytes()); + oOut.close(); + + // Load the certificate and import its revocation signature + OpenPGPCertificate cert = api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.ALICE_CERT); + cert = OpenPGPCertificate.join(cert, OpenPGPTestKeys.ALICE_REVOCATION_CERT); + + // Process the signed message using the revoked key + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage(); + processor.addVerificationCertificate(cert); + ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); + OpenPGPMessageInputStream oIn = processor.process(bIn); + Streams.drain(oIn); + oIn.close(); + + OpenPGPMessageInputStream.Result result = oIn.getResult(); + OpenPGPSignature.OpenPGPDocumentSignature sig = result.getSignatures().get(0); + // signature is no valid + isFalse(sig.isValid()); + } + + private void incompleteMessageProcessing(OpenPGPApi api) + throws IOException, PGPException + { + OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() + .addEncryptionCertificate(api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.ALICE_CERT)) + .addSigningKey(api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.BOB_KEY)); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OpenPGPMessageOutputStream out = gen.open(bOut); + + out.write("Some Data".getBytes(StandardCharsets.UTF_8)); + out.close(); + + ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage() + .addVerificationCertificate(api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.BOB_CERT)) + .addDecryptionKey(api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.ALICE_KEY)); + OpenPGPMessageInputStream in = processor.process(bIn); + + // read a single byte (not the entire message) + in.read(); + + in.close(); + OpenPGPMessageInputStream.Result result = in.getResult(); + OpenPGPSignature.OpenPGPDocumentSignature sig = result.getSignatures().get(0); + isFalse(sig.isValid()); + } + + private void testVerificationOfSEIPD1MessageWithTamperedCiphertext(OpenPGPApi api) + throws IOException, PGPException + { + String MSG = "-----BEGIN PGP MESSAGE-----\n" + + "\n" + + "wcDMA3wvqk35PDeyAQv/c0eFDZud8YCzKu0qzq7xOUeF0KiFFv58RSAookfyce9B\n" + + "LSXH7g/F/3Pdp9EHcrtBsxYRXUdWmZHvwFRvAiwCl9unjUgRendopmuNJ5zNgB2w\n" + + "DkuMA2J2J5HGTicvCwGrWALDG6Dc56UEFTwCsip8uKNG+Q3X5IwpU7Vztqywkt4/\n" + + "RNp8+neu+oJELWn3mC3oZrMzYIaD2SlyVaW5Vpksjz32VGKXCm4/hGC/03tGuE1i\n" + + "5sOZicHpeN24BD2tr3MMOdHKPXKxVPPx5T1MIJYUoYjMp7Tnml6F4Obhf+VllAli\n" + + "mkQHj6vevbEkLcJX67pvD04PJiQqm5ea1GwOZDW/nPLih80AJWHpXME36WBzk4X2\n" + + "bHaK3qQxyxqfpvMvWcargI3neWNLaSzqY/2eCrY/OEbAcj18W+9u7phkEoVRmrC7\n" + + "mqIeEUXtGjWSywtJXF8tIcxOU3+IqekXLW9yFIzRrHWEzRVKzP2P5q7mwOp2ddjg\n" + + "8vqe/DOz1r8VxN6orUue0kwBJVHfkYpW8cwX2AtIPYk90ct2qCTbCtNQul+txpRY\n" + + "IwBVELjaaSGpdOuIHkETYssCNfqPSv0rNmaTDq78xItvhjuc4lRaKkpF9DdE\n" + + "=I5BA\n" + + "-----END PGP MESSAGE-----"; + OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.BOB_KEY); + OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage(); + processor.addDecryptionKey(key); + OpenPGPMessageInputStream oIn = processor.process(new ByteArrayInputStream(MSG.getBytes(StandardCharsets.UTF_8))); + Streams.drain(oIn); + try + { + oIn.close(); + } + catch (IOException e) + { + // expected + } + } + + public static void main(String[] args) + { + runTest(new OpenPGPMessageProcessorTest()); + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV4KeyGenerationTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV4KeyGenerationTest.java new file mode 100644 index 0000000000..ebd77c492c --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV4KeyGenerationTest.java @@ -0,0 +1,64 @@ +package org.bouncycastle.openpgp.api.test; + +import org.bouncycastle.bcpg.PublicKeyPacket; +import org.bouncycastle.bcpg.SignatureSubpacketTags; +import org.bouncycastle.bcpg.sig.KeyFlags; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPKeyPair; +import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.bouncycastle.openpgp.api.KeyPairGeneratorCallback; +import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.OpenPGPKey; +import org.bouncycastle.openpgp.api.SignatureParameters; +import org.bouncycastle.openpgp.api.SignatureSubpacketsFunction; +import org.bouncycastle.openpgp.operator.PGPKeyPairGenerator; + +public class OpenPGPV4KeyGenerationTest + extends APITest +{ + @Override + public String getName() + { + return "OpenPGPV4KeyGenerationTest"; + } + + @Override + protected void performTestWith(OpenPGPApi api) + throws PGPException + { + generateRSAKey(api); + } + + private void generateRSAKey(OpenPGPApi api) + throws PGPException + { + OpenPGPKey key = api.generateKey(PublicKeyPacket.VERSION_4) + .withPrimaryKey(new KeyPairGeneratorCallback() + { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateRsaKeyPair(3072); + } + }, SignatureParameters.Callback.modifyHashedSubpackets(new SignatureSubpacketsFunction() + { + @Override + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS); + subpackets.setKeyFlags(KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA | KeyFlags.ENCRYPT_STORAGE | KeyFlags.ENCRYPT_COMMS); + return subpackets; + } + })) + .addUserId("Alice ") + .build(); + + isEquals(PublicKeyPacket.VERSION_4, key.getPrimaryKey().getVersion()); + } + + public static void main(String[] args) + { + runTest(new OpenPGPV4KeyGenerationTest()); + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV6KeyGeneratorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV6KeyGeneratorTest.java index e69954ee6c..49872f3022 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV6KeyGeneratorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV6KeyGeneratorTest.java @@ -12,29 +12,30 @@ import org.bouncycastle.bcpg.SignatureSubpacketTags; import org.bouncycastle.bcpg.sig.Features; import org.bouncycastle.bcpg.sig.KeyFlags; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPKeyPair; import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; import org.bouncycastle.openpgp.api.KeyPairGeneratorCallback; -import org.bouncycastle.openpgp.api.OpenPGPV6KeyGenerator; +import org.bouncycastle.openpgp.api.OpenPGPApi; +import org.bouncycastle.openpgp.api.OpenPGPCertificate; +import org.bouncycastle.openpgp.api.OpenPGPKey; +import org.bouncycastle.openpgp.api.OpenPGPKeyGenerator; +import org.bouncycastle.openpgp.api.SignatureParameters; import org.bouncycastle.openpgp.api.SignatureSubpacketsFunction; -import org.bouncycastle.openpgp.api.bc.BcOpenPGPV6KeyGenerator; -import org.bouncycastle.openpgp.api.jcajce.JcaOpenPGPV6KeyGenerator; import org.bouncycastle.openpgp.operator.PGPKeyPairGenerator; import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator; import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder; import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider; import org.bouncycastle.openpgp.operator.bc.BcPGPKeyPairGeneratorProvider; -import org.bouncycastle.openpgp.test.AbstractPgpKeyPairTest; public class OpenPGPV6KeyGeneratorTest - extends AbstractPgpKeyPairTest + extends APITest { @Override public String getName() @@ -43,59 +44,32 @@ public String getName() } @Override - public void performTest() - throws Exception - { - // Run tests using the BC implementation - performTests(new APIProvider() - { - @Override - public OpenPGPV6KeyGenerator getKeyGenerator(int signatureHashAlgorithm, - Date creationTime, - boolean aeadProtection) - { - return new BcOpenPGPV6KeyGenerator(signatureHashAlgorithm, creationTime, aeadProtection); - } - }); - - // Run tests using the JCA/JCE implementation - performTests(new APIProvider() - { - @Override - public OpenPGPV6KeyGenerator getKeyGenerator(int signatureHashAlgorithm, - Date creationTime, - boolean aeadProtection) - throws PGPException - { - return new JcaOpenPGPV6KeyGenerator(signatureHashAlgorithm, creationTime, aeadProtection, - new BouncyCastleProvider()); - } - }); - } - - private void performTests(APIProvider apiProvider) + protected void performTestWith(OpenPGPApi api) throws PGPException, IOException { - testGenerateCustomKey(apiProvider); + testGenerateCustomKey(api); + testGenerateMinimalKey(api); - testGenerateSignOnlyKeyBaseCase(apiProvider); - testGenerateAEADProtectedSignOnlyKey(apiProvider); - testGenerateCFBProtectedSignOnlyKey(apiProvider); + testGenerateSignOnlyKeyBaseCase(api); + testGenerateAEADProtectedSignOnlyKey(api); + testGenerateCFBProtectedSignOnlyKey(api); - testGenerateClassicKeyBaseCase(apiProvider); - testGenerateProtectedTypicalKey(apiProvider); + testGenerateClassicKeyBaseCase(api); + testGenerateProtectedTypicalKey(api); - testGenerateEd25519x25519Key(apiProvider); - testGenerateEd448x448Key(apiProvider); + testGenerateEd25519x25519Key(api); + testGenerateEd448x448Key(api); - testEnforcesPrimaryOrSubkeyType(apiProvider); + testEnforcesPrimaryOrSubkeyType(api); + testGenerateKeyWithoutSignatures(api); } - private void testGenerateSignOnlyKeyBaseCase(APIProvider apiProvider) + private void testGenerateSignOnlyKeyBaseCase(OpenPGPApi api) throws PGPException { - OpenPGPV6KeyGenerator generator = apiProvider.getKeyGenerator(); - PGPSecretKeyRing secretKeys = generator.signOnlyKey(null); + OpenPGPKeyGenerator generator = api.generateKey(); + OpenPGPKey key = generator.signOnlyKey().build(); + PGPSecretKeyRing secretKeys = key.getPGPKeyRing(); Iterator it = secretKeys.getSecretKeys(); PGPSecretKey primaryKey = (PGPSecretKey)it.next(); @@ -118,11 +92,12 @@ private void testGenerateSignOnlyKeyBaseCase(APIProvider apiProvider) isEquals("Key MUST be unprotected", SecretKeyPacket.USAGE_NONE, primaryKey.getS2KUsage()); } - private void testGenerateAEADProtectedSignOnlyKey(APIProvider apiProvider) + private void testGenerateAEADProtectedSignOnlyKey(OpenPGPApi api) throws PGPException { - OpenPGPV6KeyGenerator generator = apiProvider.getKeyGenerator(true); - PGPSecretKeyRing secretKeys = generator.signOnlyKey("passphrase".toCharArray()); + OpenPGPKeyGenerator generator = api.generateKey(new Date(), true); + OpenPGPKey key = generator.signOnlyKey().build("passphrase".toCharArray()); + PGPSecretKeyRing secretKeys = key.getPGPKeyRing(); Iterator it = secretKeys.getSecretKeys(); PGPSecretKey primaryKey = (PGPSecretKey)it.next(); @@ -135,11 +110,12 @@ private void testGenerateAEADProtectedSignOnlyKey(APIProvider apiProvider) .build("passphrase".toCharArray()))); } - private void testGenerateCFBProtectedSignOnlyKey(APIProvider apiProvider) + private void testGenerateCFBProtectedSignOnlyKey(OpenPGPApi api) throws PGPException { - OpenPGPV6KeyGenerator generator = apiProvider.getKeyGenerator(false); - PGPSecretKeyRing secretKeys = generator.signOnlyKey("passphrase".toCharArray()); + OpenPGPKeyGenerator generator = api.generateKey(new Date(), false); + OpenPGPKey key = generator.signOnlyKey().build("passphrase".toCharArray()); + PGPSecretKeyRing secretKeys = key.getPGPKeyRing(); Iterator it = secretKeys.getSecretKeys(); PGPSecretKey primaryKey = (PGPSecretKey)it.next(); @@ -152,13 +128,14 @@ private void testGenerateCFBProtectedSignOnlyKey(APIProvider apiProvider) .build("passphrase".toCharArray()))); } - private void testGenerateClassicKeyBaseCase(APIProvider apiProvider) + private void testGenerateClassicKeyBaseCase(OpenPGPApi api) throws PGPException { Date creationTime = currentTimeRounded(); - OpenPGPV6KeyGenerator generator = apiProvider.getKeyGenerator(creationTime); - PGPSecretKeyRing secretKeys = generator - .classicKey("Alice ", null); + OpenPGPKeyGenerator generator = api.generateKey(creationTime); + OpenPGPKey key = generator + .classicKey("Alice ").build(); + PGPSecretKeyRing secretKeys = key.getPGPKeyRing(); Iterator keys = secretKeys.getSecretKeys(); PGPSecretKey primaryKey = (PGPSecretKey)keys.next(); @@ -207,25 +184,26 @@ private void testGenerateClassicKeyBaseCase(APIProvider apiProvider) // Test all keys are unprotected for (Iterator it = secretKeys.getSecretKeys(); it.hasNext();) { - PGPSecretKey key = (PGPSecretKey)it.next(); - isEquals("(Sub-)keys MUST be unprotected", SecretKeyPacket.USAGE_NONE, key.getS2KUsage()); + PGPSecretKey k = (PGPSecretKey)it.next(); + isEquals("(Sub-)keys MUST be unprotected", SecretKeyPacket.USAGE_NONE, k.getS2KUsage()); } } - private void testGenerateProtectedTypicalKey(APIProvider apiProvider) + private void testGenerateProtectedTypicalKey(OpenPGPApi api) throws PGPException { Date creationTime = currentTimeRounded(); - OpenPGPV6KeyGenerator generator = apiProvider.getKeyGenerator(creationTime); - PGPSecretKeyRing secretKeys = generator - .classicKey("Alice ", "passphrase".toCharArray()); + OpenPGPKeyGenerator generator = api.generateKey(creationTime); + OpenPGPKey key = generator + .classicKey("Alice ").build("passphrase".toCharArray()); + PGPSecretKeyRing secretKeys = key.getPGPKeyRing(); // Test creation time for (Iterator it = secretKeys.toCertificate().iterator(); it.hasNext();) { - PGPPublicKey key = (PGPPublicKey)it.next(); - isEquals(creationTime, key.getCreationTime()); - for (Iterator its = key.getSignatures(); its.hasNext(); ) + PGPPublicKey k = (PGPPublicKey)it.next(); + isEquals(creationTime, k.getCreationTime()); + for (Iterator its = k.getSignatures(); its.hasNext(); ) { PGPSignature sig = (PGPSignature)its.next(); isEquals(creationTime, sig.getCreationTime()); @@ -240,19 +218,21 @@ private void testGenerateProtectedTypicalKey(APIProvider apiProvider) for (Iterator it = secretKeys.getSecretKeys(); it.hasNext();) { - PGPSecretKey key = (PGPSecretKey)it.next(); - isEquals("(Sub-)keys MUST be protected", SecretKeyPacket.USAGE_AEAD, key.getS2KUsage()); + PGPSecretKey k = (PGPSecretKey)it.next(); + isEquals("(Sub-)keys MUST be protected", SecretKeyPacket.USAGE_AEAD, k.getS2KUsage()); + } } - private void testGenerateEd25519x25519Key(APIProvider apiProvider) + private void testGenerateEd25519x25519Key(OpenPGPApi api) throws PGPException { Date currentTime = currentTimeRounded(); String userId = "Foo "; - OpenPGPV6KeyGenerator generator = apiProvider.getKeyGenerator(currentTime); + OpenPGPKeyGenerator generator = api.generateKey(currentTime); - PGPSecretKeyRing secretKey = generator.ed25519x25519Key(userId, null); + OpenPGPKey key = generator.ed25519x25519Key(userId).build(); + PGPSecretKeyRing secretKey = key.getPGPKeyRing(); Iterator iterator = secretKey.getSecretKeys(); PGPSecretKey primaryKey = (PGPSecretKey)iterator.next(); @@ -292,14 +272,15 @@ private void testGenerateEd25519x25519Key(APIProvider apiProvider) isEquals(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, hashedSubpackets.getKeyFlags()); } - private void testGenerateEd448x448Key(APIProvider apiProvider) + private void testGenerateEd448x448Key(OpenPGPApi api) throws PGPException { Date currentTime = currentTimeRounded(); String userId = "Foo "; - OpenPGPV6KeyGenerator generator = apiProvider.getKeyGenerator(currentTime); + OpenPGPKeyGenerator generator = api.generateKey(currentTime); - PGPSecretKeyRing secretKey = generator.ed448x448Key(userId, null); + OpenPGPKey key = generator.ed448x448Key(userId).build(); + PGPSecretKeyRing secretKey = key.getPGPKeyRing(); Iterator iterator = secretKey.getSecretKeys(); PGPSecretKey primaryKey = (PGPSecretKey)iterator.next(); @@ -339,61 +320,61 @@ private void testGenerateEd448x448Key(APIProvider apiProvider) isEquals(KeyFlags.ENCRYPT_COMMS | KeyFlags.ENCRYPT_STORAGE, hashedSubpackets.getKeyFlags()); } - private void testGenerateCustomKey(APIProvider apiProvider) + private void testGenerateCustomKey(OpenPGPApi api) throws PGPException { Date creationTime = currentTimeRounded(); - OpenPGPV6KeyGenerator generator = apiProvider.getKeyGenerator(creationTime); + OpenPGPKeyGenerator generator = api.generateKey(creationTime, false); - PGPSecretKeyRing secretKey = generator + OpenPGPKey key = generator .withPrimaryKey( - new KeyPairGeneratorCallback() - { - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException + new KeyPairGeneratorCallback() { - return generator.generateRsaKeyPair(4096); - } - }, - new SignatureSubpacketsFunction() - { - public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateRsaKeyPair(4096); + } + }, + SignatureParameters.Callback.modifyHashedSubpackets(new SignatureSubpacketsFunction() { - subpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS); - subpackets.setKeyFlags(KeyFlags.CERTIFY_OTHER); + @Override + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS); + subpackets.setKeyFlags(KeyFlags.CERTIFY_OTHER); - subpackets.removePacketsOfType(SignatureSubpacketTags.FEATURES); - subpackets.setFeature(false, Features.FEATURE_SEIPD_V2); + subpackets.removePacketsOfType(SignatureSubpacketTags.FEATURES); + subpackets.setFeature(false, Features.FEATURE_SEIPD_V2); - subpackets.addNotationData(false, true, - "notation@example.com", "CYBER"); + subpackets.addNotationData(false, true, + "notation@example.com", "CYBER"); - subpackets.setPreferredKeyServer(false, "https://example.com/openpgp/cert.asc"); - return subpackets; - } - }, - "primary-key-passphrase".toCharArray()) - .addUserId("Alice ", PGPSignature.DEFAULT_CERTIFICATION, null) + subpackets.setPreferredKeyServer(false, "https://example.com/openpgp/cert.asc"); + return subpackets; + } + })) + .addUserId("Alice ") .addSigningSubkey( - new KeyPairGeneratorCallback() - { - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException + new KeyPairGeneratorCallback() { - return generator.generateEd448KeyPair(); - } - }, - new SignatureSubpacketsFunction() - { - public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator bindingSubpackets) + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateEd448KeyPair(); + } + }, + SignatureParameters.Callback.modifyHashedSubpackets(new SignatureSubpacketsFunction() { - bindingSubpackets.addNotationData(false, true, - "notation@example.com", "ZAUBER"); - return bindingSubpackets; - } - }, - null, - "signing-key-passphrase".toCharArray()) + @Override + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.addNotationData(false, true, + "notation@example.com", "ZAUBER"); + return subpackets; + } + }), + null) .addEncryptionSubkey( new KeyPairGeneratorCallback() { @@ -402,10 +383,22 @@ public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) { return generator.generateX448KeyPair(); } - }, - "encryption-key-passphrase".toCharArray()) - .build(); - + }) + .build("primary-key-passphrase".toCharArray()); + OpenPGPCertificate.OpenPGPComponentKey encryptionKey = key.getEncryptionKeys().get(0); + OpenPGPCertificate.OpenPGPComponentKey signingKey = key.getSigningKeys().get(0); + key = api.editKey(key, "primary-key-passphrase".toCharArray()) + .changePassphrase(encryptionKey.getKeyIdentifier(), + "primary-key-passphrase".toCharArray(), + "encryption-key-passphrase".toCharArray(), + false) + .changePassphrase(signingKey.getKeyIdentifier(), + "primary-key-passphrase".toCharArray(), + "signing-key-passphrase".toCharArray(), + false) + .done(); + + PGPSecretKeyRing secretKey = key.getPGPKeyRing(); Iterator keyIt = secretKey.getSecretKeys(); PGPSecretKey primaryKey = (PGPSecretKey)keyIt.next(); isEquals("Primary key MUST be RSA_GENERAL", @@ -428,7 +421,7 @@ public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) isFalse("Unexpected additional UID", uids.hasNext()); PGPSignature uidSig = (PGPSignature)primaryKey.getPublicKey().getSignaturesForID(uid).next(); isEquals("UID binding sig type mismatch", - PGPSignature.DEFAULT_CERTIFICATION, uidSig.getSignatureType()); + PGPSignature.POSITIVE_CERTIFICATION, uidSig.getSignatureType()); PGPSecretKey signingSubkey = (PGPSecretKey)keyIt.next(); isEquals("Subkey MUST be Ed448", @@ -469,7 +462,55 @@ public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) encryptionSubkey.extractPrivateKey(keyDecryptorBuilder.build("encryption-key-passphrase".toCharArray()))); } - private void testEnforcesPrimaryOrSubkeyType(final APIProvider apiProvider) + private void testGenerateMinimalKey(OpenPGPApi api) + throws PGPException + { + Date creationTime = currentTimeRounded(); + OpenPGPKeyGenerator gen = api.generateKey(creationTime, false); + OpenPGPKey key = gen.withPrimaryKey( + new KeyPairGeneratorCallback() + { + @Override + public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) + throws PGPException + { + return generator.generateEd25519KeyPair(); + } + }, + SignatureParameters.Callback.modifyHashedSubpackets(new SignatureSubpacketsFunction() + { + @Override + public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) + { + subpackets.addNotationData(false, true, "foo@bouncycastle.org", "bar"); + return subpackets; + } + })) + .addUserId("Alice ") + .addEncryptionSubkey() + .addSigningSubkey() + .build(); + PGPSecretKeyRing secretKeys = key.getPGPKeyRing(); + + // Test creation time + for(Iterator it = secretKeys.toCertificate().iterator(); it.hasNext(); ) + { + PGPPublicKey k = (PGPPublicKey)it.next(); + isEquals(creationTime, k.getCreationTime()); + for (Iterator itSign = k.getSignatures(); itSign.hasNext(); ) { + PGPSignature sig = itSign.next(); + isEquals(creationTime, sig.getCreationTime()); + } + } + + PGPPublicKey primaryKey = secretKeys.getPublicKey(); + // Test UIDs + Iterator uids = primaryKey.getUserIDs(); + isEquals("Alice ", uids.next()); + isFalse(uids.hasNext()); + } + + private void testEnforcesPrimaryOrSubkeyType(final OpenPGPApi api) throws PGPException { isNotNull(testException( @@ -481,7 +522,7 @@ private void testEnforcesPrimaryOrSubkeyType(final APIProvider apiProvider) public void operation() throws Exception { - apiProvider.getKeyGenerator().withPrimaryKey( + api.generateKey().withPrimaryKey( new KeyPairGeneratorCallback() { public PGPKeyPair generateFrom(PGPKeyPairGenerator keyGenCallback) @@ -504,10 +545,12 @@ public PGPKeyPair generateFrom(PGPKeyPairGenerator keyGenCallback) public void operation() throws Exception { - apiProvider.getKeyGenerator().withPrimaryKey() - .addEncryptionSubkey(new BcPGPKeyPairGeneratorProvider() - .get(6, new Date()) - .generateX25519KeyPair(), null, null); // primary key as subkey is illegal + api.generateKey().withPrimaryKey() + .addEncryptionSubkey( + new BcPGPKeyPairGeneratorProvider() + .get(6, new Date()) + .generateX25519KeyPair(), + null); // primary key as subkey is illegal } } )); @@ -521,37 +564,76 @@ public void operation() public void operation() throws Exception { - apiProvider.getKeyGenerator().withPrimaryKey() - .addSigningSubkey(new BcPGPKeyPairGeneratorProvider() - .get(6, new Date()) - .generateEd25519KeyPair(), null, null, null); // primary key as subkey is illegal + api.generateKey().withPrimaryKey() + .addSigningSubkey( + new BcPGPKeyPairGeneratorProvider() + .get(6, new Date()) + .generateEd25519KeyPair(), + null, + null); // primary key as subkey is illegal } } )); } - private abstract static class APIProvider - { - public OpenPGPV6KeyGenerator getKeyGenerator() + private void testGenerateKeyWithoutSignatures(OpenPGPApi api) throws PGPException - { - return getKeyGenerator(new Date()); - } + { + OpenPGPKey key = api.generateKey() + .withPrimaryKey( + KeyPairGeneratorCallback.primaryKey(), + // No direct-key sig + new SignatureParameters.Callback() + { + @Override + public SignatureParameters apply(SignatureParameters parameters) { + return null; + } + }) + .addSigningSubkey( + KeyPairGeneratorCallback.signingKey(), + // No subkey binding sig + new SignatureParameters.Callback() + { + @Override + public SignatureParameters apply(SignatureParameters parameters) + { + return null; + } + }, + // No primary key binding sig + new SignatureParameters.Callback() + { + @Override + public SignatureParameters apply(SignatureParameters parameters) + { + return null; + } + }) + .addEncryptionSubkey( + KeyPairGeneratorCallback.encryptionKey(), + // No subkey binding sig + new SignatureParameters.Callback() + { + @Override + public SignatureParameters apply(SignatureParameters parameters) + { + return null; + } + }) + .build(); - public OpenPGPV6KeyGenerator getKeyGenerator(Date creationTime) - throws PGPException - { - return getKeyGenerator(OpenPGPV6KeyGenerator.DEFAULT_SIGNATURE_HASH_ALGORITHM, creationTime, true); - } + PGPPublicKeyRing publicKeys = key.getPGPPublicKeyRing(); + Iterator it = publicKeys.getPublicKeys(); - public OpenPGPV6KeyGenerator getKeyGenerator(boolean aeadProtection) - throws PGPException - { - return getKeyGenerator(OpenPGPV6KeyGenerator.DEFAULT_SIGNATURE_HASH_ALGORITHM, new Date(), aeadProtection); - } + PGPPublicKey primaryKey = it.next(); + isFalse(primaryKey.getSignatures().hasNext()); + + PGPPublicKey signingSubkey = it.next(); + isFalse(signingSubkey.getSignatures().hasNext()); - public abstract OpenPGPV6KeyGenerator getKeyGenerator(int signatureHashAlgorithm, Date creationTime, boolean aeadProtection) - throws PGPException; + PGPPublicKey encryptionSubkey = it.next(); + isFalse(encryptionSubkey.getSignatures().hasNext()); } public static void main(String[] args) diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/RegressionTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/RegressionTest.java new file mode 100644 index 0000000000..1c52bda358 --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/RegressionTest.java @@ -0,0 +1,30 @@ +package org.bouncycastle.openpgp.api.test; + +import java.security.Security; + +import org.bouncycastle.util.test.SimpleTest; +import org.bouncycastle.util.test.Test; + +public class RegressionTest +{ + public static Test[] tests = { + new ChangeKeyPassphraseTest(), +// new DoubleBufferedInputStreamTest(), + new OpenPGPCertificateTest(), + new OpenPGPDetachedSignatureProcessorTest(), + new OpenPGPKeyEditorTest(), + new OpenPGPKeyReaderTest(), + new OpenPGPMessageGeneratorTest(), + new OpenPGPMessageProcessorTest(), + new OpenPGPV4KeyGenerationTest(), + new OpenPGPV6KeyGeneratorTest(), + new StaticV6OpenPGPMessageGeneratorTest(), + }; + + public static void main(String[] args) + { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + + SimpleTest.runTests(tests); + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/StackMessagePassphraseCallback.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/StackMessagePassphraseCallback.java new file mode 100644 index 0000000000..87d43cf551 --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/StackMessagePassphraseCallback.java @@ -0,0 +1,38 @@ +package org.bouncycastle.openpgp.api.test; + +import org.bouncycastle.openpgp.api.MissingMessagePassphraseCallback; + +import java.util.Collection; +import java.util.Collections; +import java.util.Stack; + +/** + * Test implementation of {@link MissingMessagePassphraseCallback} which provides passphrases by popping + * them from a provided {@link Stack}. + */ +public class StackMessagePassphraseCallback + implements MissingMessagePassphraseCallback +{ + private final Stack passphases; + + public StackMessagePassphraseCallback(char[] passphrase) + { + this(Collections.singleton(passphrase)); + } + + public StackMessagePassphraseCallback(Collection passphrases) + { + this.passphases = new Stack<>(); + this.passphases.addAll(passphrases); + } + + @Override + public char[] getMessagePassphrase() + { + if (passphases.isEmpty()) + { + return null; + } + return passphases.pop(); + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/StaticV6OpenPGPMessageGeneratorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/StaticV6OpenPGPMessageGeneratorTest.java new file mode 100644 index 0000000000..16e9fc96ff --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/StaticV6OpenPGPMessageGeneratorTest.java @@ -0,0 +1,111 @@ +package org.bouncycastle.openpgp.api.test; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; + +import org.bouncycastle.bcpg.KeyIdentifier; +import org.bouncycastle.bcpg.test.AbstractPacketTest; +import org.bouncycastle.openpgp.OpenPGPTestKeys; +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.api.OpenPGPCertificate; +import org.bouncycastle.openpgp.api.OpenPGPKey; +import org.bouncycastle.openpgp.api.OpenPGPKeyReader; +import org.bouncycastle.openpgp.api.OpenPGPMessageGenerator; +import org.bouncycastle.openpgp.api.OpenPGPMessageOutputStream; +import org.bouncycastle.openpgp.api.OpenPGPPolicy; +import org.bouncycastle.openpgp.api.SubkeySelector; +import org.bouncycastle.util.encoders.Hex; + +public class StaticV6OpenPGPMessageGeneratorTest + extends AbstractPacketTest +{ + private final OpenPGPKeyReader reader = new OpenPGPKeyReader(); + + KeyIdentifier signingKeyIdentifier = new KeyIdentifier( + Hex.decode("CB186C4F0609A697E4D52DFA6C722B0C1F1E27C18A56708F6525EC27BAD9ACC9")); + KeyIdentifier encryptionKeyIdentifier = new KeyIdentifier( + Hex.decode("12C83F1E706F6308FE151A417743A1F033790E93E9978488D1DB378DA9930885")); + + @Override + public String getName() + { + return "StaticV6OpenPGPMessageGeneratorTest"; + } + + @Override + public void performTest() + throws Exception + { + staticEncryptedMessage(); + staticSignedMessage(); + } + + private void staticEncryptedMessage() + throws IOException, PGPException + { + OpenPGPKey key = reader.parseKey(OpenPGPTestKeys.V6_KEY); + + OpenPGPMessageGenerator gen = getStaticGenerator() + .addEncryptionCertificate(key); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OpenPGPMessageOutputStream pgOut = (OpenPGPMessageOutputStream) gen.open(bOut); + pgOut.write("Hello, World!\n".getBytes(StandardCharsets.UTF_8)); + pgOut.close(); + + System.out.println(bOut); + } + + private void staticSignedMessage() + throws IOException, PGPException + { + OpenPGPKey key = reader.parseKey(OpenPGPTestKeys.V6_KEY); + OpenPGPMessageGenerator gen = getStaticGenerator() + .addSigningKey(key); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + OpenPGPMessageOutputStream pgOut = (OpenPGPMessageOutputStream) gen.open(bOut); + pgOut.write("Hello, World!\n".getBytes(StandardCharsets.UTF_8)); + pgOut.close(); + + System.out.println(bOut); + } + + /** + * Return a pre-configured {@link OpenPGPMessageGenerator} which has the complex logic of evaluating + * recipient keys to determine suitable subkeys, algorithms etc. swapped out for static configuration + * tailored to the V6 test key. + * + * @return static message generator + */ + public OpenPGPMessageGenerator getStaticGenerator() + { + OpenPGPMessageGenerator gen = new OpenPGPMessageGenerator() + .setSigningKeySelector(new SubkeySelector() + { + @Override + public List select( + OpenPGPCertificate certificate, OpenPGPPolicy policy) + { + return Collections.singletonList(certificate.getKey(signingKeyIdentifier)); + } + }) + .setEncryptionKeySelector( + new SubkeySelector() { + @Override + public List select(OpenPGPCertificate certificate, OpenPGPPolicy policy) { + return Collections.singletonList(certificate.getKey(encryptionKeyIdentifier)); + } + }); + + return gen; + } + + public static void main(String[] args) + { + runTest(new StaticV6OpenPGPMessageGeneratorTest()); + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java index 270336aec4..0856437688 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/RegressionTest.java @@ -3,7 +3,6 @@ import java.security.Security; import org.bouncycastle.bcpg.test.SignatureSubpacketsTest; -import org.bouncycastle.openpgp.api.test.OpenPGPV6KeyGeneratorTest; import org.bouncycastle.util.test.SimpleTest; import org.bouncycastle.util.test.Test; @@ -87,8 +86,7 @@ public class RegressionTest new PGPv5MessageDecryptionTest(), new PGPv6SignatureTest(), new PGPKeyPairGeneratorTest(), - new OpenPGPV6KeyGeneratorTest(), - new PGPKeyRingGeneratorTest() + new PGPKeyRingGeneratorTest(), }; public static void main(String[] args) From c6d6af4ed86a8c3743a2ef77b8559eb13f95a2b5 Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 24 Apr 2025 14:54:53 +0930 Subject: [PATCH 328/890] Complete code of jcajce part on cms for chacha20-poly1305 --- .../asn1/kisa/KISAObjectIdentifiers.java | 3 + .../org/bouncycastle/cms/CMSAlgorithm.java | 1 + .../cms/CMSAuthEnvelopedDataGenerator.java | 18 +- .../cms/jcajce/EnvelopedDataHelper.java | 2 + .../jcajce/JceCMSContentEncryptorBuilder.java | 15 +- .../JcePasswordAuthEnvelopedRecipient.java | 31 +++ .../cms/test/AuthEnvelopedDataTest.java | 48 +++++ .../test/NewAuthEnvelopedDataStreamTest.java | 199 ++++++++++++++++-- 8 files changed, 294 insertions(+), 23 deletions(-) create mode 100644 pkix/src/main/java/org/bouncycastle/cms/jcajce/JcePasswordAuthEnvelopedRecipient.java diff --git a/core/src/main/java/org/bouncycastle/internal/asn1/kisa/KISAObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/internal/asn1/kisa/KISAObjectIdentifiers.java index 19df01ad3a..5f94ce2c1b 100644 --- a/core/src/main/java/org/bouncycastle/internal/asn1/kisa/KISAObjectIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/internal/asn1/kisa/KISAObjectIdentifiers.java @@ -31,4 +31,7 @@ public interface KISAObjectIdentifiers /** RFC 9708 MTS-HashSig-2013; OID 1.2.840.113549.1.9.16.0.64 */ static final ASN1ObjectIdentifier id_mod_mts_hashsig_2013 = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.64"); + + /** RFC 8103 id-mod-CMS-AEADChaCha20Poly1305; OID 1.2.840.113549.1.9.16.0.66 */ + static final ASN1ObjectIdentifier id_mod_CMS_AEADChaCha20Poly1305 = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.66"); } diff --git a/pkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java b/pkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java index 0418b5ac6f..4c0499bf47 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java +++ b/pkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java @@ -105,4 +105,5 @@ public class CMSAlgorithm public static final ASN1ObjectIdentifier SHAKE128_LEN = NISTObjectIdentifiers.id_shake128_len.intern(); public static final ASN1ObjectIdentifier SHAKE256_LEN = NISTObjectIdentifiers.id_shake256_len.intern(); + public static final ASN1ObjectIdentifier ChaCha20Poly1305 = PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305.intern(); } diff --git a/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedDataGenerator.java b/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedDataGenerator.java index e84f2a69af..4665295e4e 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedDataGenerator.java +++ b/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedDataGenerator.java @@ -12,6 +12,7 @@ import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; import org.bouncycastle.asn1.cms.ContentInfo; import org.bouncycastle.asn1.cms.EncryptedContentInfo; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.operator.OutputAEADEncryptor; public class CMSAuthEnvelopedDataGenerator @@ -36,10 +37,17 @@ private CMSAuthEnvelopedData doGenerate( try { OutputStream cOut = contentEncryptor.getOutputStream(bOut); - - content.write(cOut); - - authenticatedAttrSet = CMSUtils.processAuthAttrSet(authAttrsGenerator, contentEncryptor); + if (contentEncryptor.getAlgorithmIdentifier().getAlgorithm().equals(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305)) + { + // AEAD Ciphers process AAD at first + authenticatedAttrSet = CMSUtils.processAuthAttrSet(authAttrsGenerator, contentEncryptor); + content.write(cOut); + } + else + { + content.write(cOut); + authenticatedAttrSet = CMSUtils.processAuthAttrSet(authAttrsGenerator, contentEncryptor); + } cOut.close(); } @@ -66,7 +74,7 @@ private CMSAuthEnvelopedData doGenerate( * generate an auth-enveloped object that contains an CMS Enveloped Data * object using the given provider. * - * @param content the content to be encrypted + * @param content the content to be encrypted * @param contentEncryptor the symmetric key based encryptor to encrypt the content with. */ public CMSAuthEnvelopedData generate( diff --git a/pkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java b/pkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java index 65aa3fba4b..8a70df6f9b 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java +++ b/pkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java @@ -110,6 +110,7 @@ public class EnvelopedDataHelper MAC_ALG_NAMES.put(CMSAlgorithm.AES192_CBC, "AESMac"); MAC_ALG_NAMES.put(CMSAlgorithm.AES256_CBC, "AESMac"); MAC_ALG_NAMES.put(CMSAlgorithm.RC2_CBC, "RC2Mac"); + MAC_ALG_NAMES.put(CMSAlgorithm.ChaCha20Poly1305, "ChaCha20Poly1305Mac"); PBKDF2_ALG_NAMES.put(PasswordRecipient.PRF.HMacSHA1.getAlgorithmID(), "PBKDF2WITHHMACSHA1"); PBKDF2_ALG_NAMES.put(PasswordRecipient.PRF.HMacSHA224.getAlgorithmID(), "PBKDF2WITHHMACSHA224"); @@ -123,6 +124,7 @@ public class EnvelopedDataHelper authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes128_CCM); authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes192_CCM); authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes256_CCM); + authEnvelopedAlgorithms.add(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305); } private static final short[] rc2Table = { diff --git a/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java b/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java index 91b4ae4047..18e91bbf1e 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java +++ b/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java @@ -376,10 +376,17 @@ public OutputStream getOutputStream(OutputStream dOut) { algId = algorithmIdentifier; } - - // TODO: works for CCM too, but others will follow. - GCMParameters p = GCMParameters.getInstance(algId.getParameters()); - macOut = new MacCaptureStream(dOut, p.getIcvLen()); + + if (algorithmIdentifier.getAlgorithm().equals(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305)) + { + macOut = new MacCaptureStream(dOut, 16); + } + else + { + // TODO: works for CCM too, but others will follow. + GCMParameters p = GCMParameters.getInstance(algId.getParameters()); + macOut = new MacCaptureStream(dOut, p.getIcvLen()); + } return new CipherOutputStream(macOut, cipher); } diff --git a/pkix/src/main/java/org/bouncycastle/cms/jcajce/JcePasswordAuthEnvelopedRecipient.java b/pkix/src/main/java/org/bouncycastle/cms/jcajce/JcePasswordAuthEnvelopedRecipient.java new file mode 100644 index 0000000000..42f2e14297 --- /dev/null +++ b/pkix/src/main/java/org/bouncycastle/cms/jcajce/JcePasswordAuthEnvelopedRecipient.java @@ -0,0 +1,31 @@ +package org.bouncycastle.cms.jcajce; + +import java.security.Key; + +import javax.crypto.Cipher; + +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.cms.CMSException; +import org.bouncycastle.cms.RecipientOperator; + +public class JcePasswordAuthEnvelopedRecipient + extends JcePasswordRecipient +{ + public JcePasswordAuthEnvelopedRecipient(char[] password) + { + super(password); + } + + public RecipientOperator getRecipientOperator(AlgorithmIdentifier keyEncryptionAlgorithm, + final AlgorithmIdentifier contentMacAlgorithm, + byte[] derivedKey, + byte[] encryptedContentEncryptionKey) + throws CMSException + { + Key secretKey = extractSecretKey(keyEncryptionAlgorithm, contentMacAlgorithm, derivedKey, encryptedContentEncryptionKey); + + final Cipher dataCipher = helper.createContentCipher(secretKey, contentMacAlgorithm); + + return new RecipientOperator(new CMSInputAEADDecryptor(contentMacAlgorithm, dataCipher)); + } +} diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/AuthEnvelopedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/AuthEnvelopedDataTest.java index 1f6736766a..3472aa57f5 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/AuthEnvelopedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/AuthEnvelopedDataTest.java @@ -22,6 +22,7 @@ import org.bouncycastle.asn1.cms.GCMParameters; import org.bouncycastle.asn1.cms.Time; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.cms.CMSAttributeTableGenerationException; import org.bouncycastle.cms.CMSAttributeTableGenerator; @@ -207,6 +208,53 @@ public AttributeTable getAttributes(Map parameters) assertEquals("Hello, world!", Strings.fromByteArray(recData)); } + public void testChacha20Poly1305() + throws Exception + { + if (!CMSTestUtil.isAeadAvailable()) + { + return; + } + byte[] message = Strings.toByteArray("Hello, world!"); + OutputEncryptor candidate = new JceCMSContentEncryptorBuilder(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305).setProvider(BC).build(); + + assertEquals(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305, candidate.getAlgorithmIdentifier().getAlgorithm()); + //assertNotNull(GCMParameters.getInstance(candidate.getAlgorithmIdentifier().getParameters())); + + assertTrue(candidate instanceof OutputAEADEncryptor); + + OutputAEADEncryptor macProvider = (OutputAEADEncryptor)candidate; + + CMSAuthEnvelopedDataGenerator authGen = new CMSAuthEnvelopedDataGenerator(); + + authGen.setAuthenticatedAttributeGenerator(new CMSAttributeTableGenerator() + { + public AttributeTable getAttributes(Map parameters) + throws CMSAttributeTableGenerationException + { + Hashtable attrs = new Hashtable(); + Attribute testAttr = new Attribute(CMSAttributes.signingTime, + new DERSet(new Time(new Date()))); + attrs.put(testAttr.getAttrType(), testAttr); + return new AttributeTable(attrs); + } + }); + + authGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert)); + + CMSAuthEnvelopedData authData = authGen.generate(new CMSProcessableByteArray(message), macProvider); + + CMSAuthEnvelopedData encAuthData = new CMSAuthEnvelopedData(authData.getEncoded()); + + RecipientInformationStore recipients = encAuthData.getRecipientInfos(); + + RecipientInformation recipient = (RecipientInformation)recipients.getRecipients().iterator().next(); + + byte[] recData = recipient.getContent(new JceKeyTransAuthEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)); + + assertEquals("Hello, world!", Strings.fromByteArray(recData)); + } + public void testGCMwithHKDF() throws Exception { diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewAuthEnvelopedDataStreamTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewAuthEnvelopedDataStreamTest.java index bb6911e39f..1ab81dbfa5 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewAuthEnvelopedDataStreamTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewAuthEnvelopedDataStreamTest.java @@ -8,8 +8,10 @@ import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collection; +import java.util.Date; import java.util.Hashtable; import java.util.Iterator; +import java.util.Map; import javax.crypto.SecretKey; @@ -23,16 +25,26 @@ import org.bouncycastle.asn1.DERUTF8String; import org.bouncycastle.asn1.cms.Attribute; import org.bouncycastle.asn1.cms.AttributeTable; +import org.bouncycastle.asn1.cms.CMSAttributes; +import org.bouncycastle.asn1.cms.Time; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.cms.CMSAlgorithm; +import org.bouncycastle.cms.CMSAttributeTableGenerationException; +import org.bouncycastle.cms.CMSAttributeTableGenerator; +import org.bouncycastle.cms.CMSAuthEnvelopedData; +import org.bouncycastle.cms.CMSAuthEnvelopedDataGenerator; import org.bouncycastle.cms.CMSAuthEnvelopedDataParser; import org.bouncycastle.cms.CMSAuthEnvelopedDataStreamGenerator; +import org.bouncycastle.cms.CMSAuthenticatedDataGenerator; import org.bouncycastle.cms.CMSEnvelopedDataParser; import org.bouncycastle.cms.CMSEnvelopedDataStreamGenerator; +import org.bouncycastle.cms.CMSProcessableByteArray; import org.bouncycastle.cms.CMSTypedStream; import org.bouncycastle.cms.KEKRecipientId; import org.bouncycastle.cms.OriginatorInfoGenerator; +import org.bouncycastle.cms.PasswordRecipient; +import org.bouncycastle.cms.PasswordRecipientInformation; import org.bouncycastle.cms.RecipientId; import org.bouncycastle.cms.RecipientInformation; import org.bouncycastle.cms.RecipientInformationStore; @@ -46,8 +58,12 @@ import org.bouncycastle.cms.jcajce.JceKeyTransAuthEnvelopedRecipient; import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient; import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator; +import org.bouncycastle.cms.jcajce.JcePasswordAuthEnvelopedRecipient; +import org.bouncycastle.cms.jcajce.JcePasswordRecipientInfoGenerator; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.operator.OutputAEADEncryptor; +import org.bouncycastle.operator.OutputEncryptor; +import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; public class NewAuthEnvelopedDataStreamTest @@ -212,7 +228,7 @@ public void testKeyTransAES128GCM() ByteArrayOutputStream bOut = new ByteArrayOutputStream(); JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM); - OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor)encryptorBuilder.setProvider(BC).build()); for (int i = 0; i != 2000; i++) { @@ -234,7 +250,7 @@ public void testKeyTransAES128GCM() bOut = new ByteArrayOutputStream(); - out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + out = edGen.open(bOut, (OutputAEADEncryptor)encryptorBuilder.setProvider(BC).build()); BufferedOutputStream bfOut = new BufferedOutputStream(out, 300); @@ -267,7 +283,7 @@ public void testKeyTransAES128Der() ByteArrayOutputStream bOut = new ByteArrayOutputStream(); JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM); - OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor)encryptorBuilder.setProvider(BC).build()); for (int i = 0; i != 2000; i++) { @@ -308,7 +324,7 @@ public void testKeyTransAES128Throughput() ByteArrayOutputStream bOut = new ByteArrayOutputStream(); JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM); - OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor)encryptorBuilder.setProvider(BC).build()); for (int i = 0; i != data.length; i++) { @@ -329,7 +345,7 @@ public void testKeyTransAES128Throughput() assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId()); CMSTypedStream recData = recipient.getContentStream( - new JceKeyTransAuthEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)); + new JceKeyTransAuthEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)); InputStream dataStream = recData.getContentStream(); ByteArrayOutputStream dataOut = new ByteArrayOutputStream(); @@ -372,7 +388,7 @@ public void testKeyTransAES128AndOriginatorInfo() ByteArrayOutputStream bOut = new ByteArrayOutputStream(); JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM); - OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor)encryptorBuilder.setProvider(BC).build()); out.write(data); @@ -396,7 +412,7 @@ public void testKeyTransAES128AndOriginatorInfo() assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId()); CMSTypedStream recData = recipient.getContentStream( - new JceKeyTransAuthEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)); + new JceKeyTransAuthEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC)); assertTrue(Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream()))); } @@ -416,7 +432,7 @@ public void testKeyTransAES128() ByteArrayOutputStream bOut = new ByteArrayOutputStream(); JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM); - OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor)encryptorBuilder.setProvider(BC).build()); out.write(data); @@ -460,7 +476,7 @@ public void testAESKEK() ByteArrayOutputStream bOut = new ByteArrayOutputStream(); JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM); - OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor)encryptorBuilder.setProvider(BC).build()); out.write(data); @@ -487,6 +503,48 @@ public void testAESKEK() ep.close(); } + public void testChaCha20Poly1305KEK() + throws Exception + { + byte[] data = "WallaWallaWashington".getBytes(); + SecretKey kek = CMSTestUtil.makeAES192Key(); + + CMSAuthEnvelopedDataStreamGenerator edGen = new CMSAuthEnvelopedDataStreamGenerator(); + + byte[] kekId = new byte[]{1, 2, 3, 4, 5}; + + edGen.addRecipientInfoGenerator(new JceKEKRecipientInfoGenerator(kekId, kek).setProvider(BC)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.ChaCha20Poly1305); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor)encryptorBuilder.setProvider(BC).build()); + + out.write(data); + + out.close(); + + CMSAuthEnvelopedDataParser ep = new CMSAuthEnvelopedDataParser(bOut.toByteArray()); + + RecipientInformationStore recipients = ep.getRecipientInfos(); + + assertEquals(ep.getEncAlgOID(), CMSAlgorithm.ChaCha20Poly1305.getId()); + + Collection c = recipients.getRecipients(); + Iterator it = c.iterator(); + + while (it.hasNext()) + { + RecipientInformation recipient = it.next(); + + CMSTypedStream recData = recipient.getContentStream(new JceKEKAuthEnvelopedRecipient(kek).setProvider(BC)); + + assertTrue(Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream()))); + } + + ep.close(); + } + public void testTwoAESKEK() throws Exception { @@ -505,7 +563,7 @@ public void testTwoAESKEK() ByteArrayOutputStream bOut = new ByteArrayOutputStream(); JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES192_GCM); - OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor)encryptorBuilder.setProvider(BC).build()); out.write(data); out.close(); @@ -535,8 +593,8 @@ public void testECKeyAgree() CMSAuthEnvelopedDataStreamGenerator edGen = new CMSAuthEnvelopedDataStreamGenerator(); JceKeyAgreeRecipientInfoGenerator recipientGenerator = new JceKeyAgreeRecipientInfoGenerator( - CMSAlgorithm.ECDH_SHA1KDF, _origEcKP.getPrivate(), _origEcKP.getPublic(), - CMSAlgorithm.AES128_WRAP).setProvider(BC); + CMSAlgorithm.ECDH_SHA1KDF, _origEcKP.getPrivate(), _origEcKP.getPublic(), + CMSAlgorithm.AES128_WRAP).setProvider(BC); recipientGenerator.addRecipient(_reciEcCert); @@ -545,7 +603,7 @@ public void testECKeyAgree() ByteArrayOutputStream bOut = new ByteArrayOutputStream(); JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM); - OutputStream out = edGen.open(bOut, (OutputAEADEncryptor) encryptorBuilder.setProvider(BC).build()); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor)encryptorBuilder.setProvider(BC).build()); out.write(data); out.close(); @@ -561,16 +619,129 @@ public void testECKeyAgree() RecipientInformation recipient = recipients.get(recSel); CMSTypedStream recData = recipient.getContentStream( - new JceKeyAgreeAuthEnvelopedRecipient(_reciEcKP.getPrivate()).setProvider(BC)); + new JceKeyAgreeAuthEnvelopedRecipient(_reciEcKP.getPrivate()).setProvider(BC)); + + assertEquals(true, Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream()))); + + ep.close(); + } + + public void testECKeyAgreeChacha20Poly1305() + throws Exception + { + byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65"); + + CMSAuthEnvelopedDataStreamGenerator edGen = new CMSAuthEnvelopedDataStreamGenerator(); + + JceKeyAgreeRecipientInfoGenerator recipientGenerator = new JceKeyAgreeRecipientInfoGenerator( + CMSAlgorithm.ECDH_SHA1KDF, _origEcKP.getPrivate(), _origEcKP.getPublic(), + CMSAlgorithm.AES128_WRAP).setProvider(BC); + + recipientGenerator.addRecipient(_reciEcCert); + + edGen.addRecipientInfoGenerator(recipientGenerator); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + JceCMSContentEncryptorBuilder encryptorBuilder = new JceCMSContentEncryptorBuilder(CMSAlgorithm.ChaCha20Poly1305); + OutputStream out = edGen.open(bOut, (OutputAEADEncryptor)encryptorBuilder.setProvider(BC).build()); + out.write(data); + + out.close(); + + CMSAuthEnvelopedDataParser ep = new CMSAuthEnvelopedDataParser(bOut.toByteArray()); + + RecipientInformationStore recipients = ep.getRecipientInfos(); + + assertEquals(ep.getEncAlgOID(), CMSAlgorithm.ChaCha20Poly1305.getId()); + + RecipientId recSel = new JceKeyAgreeRecipientId(_reciEcCert); + + RecipientInformation recipient = recipients.get(recSel); + + CMSTypedStream recData = recipient.getContentStream( + new JceKeyAgreeAuthEnvelopedRecipient(_reciEcKP.getPrivate()).setProvider(BC)); assertEquals(true, Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream()))); ep.close(); } + public void testPasswordChaCha20Poly1305() + throws Exception + { + if (!CMSTestUtil.isAeadAvailable()) + { + return; + } + byte[] message = Strings.toByteArray("Hello, world!"); + OutputEncryptor candidate = new JceCMSContentEncryptorBuilder(CMSAlgorithm.ChaCha20Poly1305).setProvider(BC).build(); + + assertEquals(CMSAlgorithm.ChaCha20Poly1305, candidate.getAlgorithmIdentifier().getAlgorithm()); + //assertNotNull(GCMParameters.getInstance(candidate.getAlgorithmIdentifier().getParameters())); + + assertTrue(candidate instanceof OutputAEADEncryptor); + + OutputAEADEncryptor macProvider = (OutputAEADEncryptor)candidate; + + CMSAuthEnvelopedDataGenerator authGen = new CMSAuthEnvelopedDataGenerator(); + + authGen.setAuthenticatedAttributeGenerator(new CMSAttributeTableGenerator() + { + public AttributeTable getAttributes(Map parameters) + throws CMSAttributeTableGenerationException + { + Hashtable attrs = new Hashtable(); + Attribute testAttr = new Attribute(CMSAttributes.signingTime, + new DERSet(new Time(new Date()))); + attrs.put(testAttr.getAttrType(), testAttr); + return new AttributeTable(attrs); + } + }); + + authGen.addRecipientInfoGenerator(new JcePasswordRecipientInfoGenerator(new ASN1ObjectIdentifier(CMSAuthenticatedDataGenerator.AES256_CBC), + "password".toCharArray()).setProvider(BC).setSaltAndIterationCount(new byte[20], 5)); + + CMSAuthEnvelopedData authData = authGen.generate(new CMSProcessableByteArray(message), macProvider); + + CMSAuthEnvelopedData encAuthData = new CMSAuthEnvelopedData(authData.getEncoded()); + + RecipientInformationStore recipients = encAuthData.getRecipientInfos(); + + Collection c = recipients.getRecipients(); + Iterator it = c.iterator(); + + if (it.hasNext()) + { + PasswordRecipientInformation recipient = (PasswordRecipientInformation)it.next(); + + PasswordRecipient pbeRep = new JcePasswordAuthEnvelopedRecipient("password".toCharArray()).setProvider(BC); + + byte[] recData = recipient.getContent(pbeRep); + + assertTrue(Arrays.equals(message, recData)); + assertTrue(Arrays.equals(authData.getMac(), recipient.getMac())); + } + else + { + fail("no recipient found"); + } + } + public static Test suite() throws Exception { return new CMSTestSetup(new TestSuite(NewAuthEnvelopedDataStreamTest.class)); } + +// public static void main(String[] args) +// throws Exception +// { +// NewAuthEnvelopedDataStreamTest test = new NewAuthEnvelopedDataStreamTest(); +// test.setUp(); +// test.testPasswordChaCha20Poly1305(); +// test.testECKeyAgreeChacha20Poly1305(); +// test.testChaCha20Poly1305KEK(); +// System.out.println("OK"); +// } } From 3e31062e0c8be27a9fb7a92d025bce7502a8dc93 Mon Sep 17 00:00:00 2001 From: David Hook Date: Thu, 24 Apr 2025 15:58:42 +1000 Subject: [PATCH 329/890] added valid date for CheckNameConstraintsTest removed use of junit from SimpleTest --- .../pkix/test/CheckNameConstraintsTest.java | 3 ++- .../jce/provider/test/BlockCipherTest.java | 13 +++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/pkix/src/test/java/org/bouncycastle/pkix/test/CheckNameConstraintsTest.java b/pkix/src/test/java/org/bouncycastle/pkix/test/CheckNameConstraintsTest.java index f3055dbdad..e24392b6ca 100644 --- a/pkix/src/test/java/org/bouncycastle/pkix/test/CheckNameConstraintsTest.java +++ b/pkix/src/test/java/org/bouncycastle/pkix/test/CheckNameConstraintsTest.java @@ -83,7 +83,7 @@ public void testPKIXCertPathBuilder() PKIXBuilderParameters buildParams = new PKIXBuilderParameters(Collections.singleton(new TrustAnchor(rootCert, null)), pathConstraints); buildParams.addCertStore(store); - buildParams.setDate(new Date()); + buildParams.setDate(new Date(1744869361113L)); // 17th April 2025 buildParams.setRevocationEnabled(false); PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult)builder.build(buildParams); @@ -116,6 +116,7 @@ public void testPKIXCertPathValidator() CertPathValidator cpv = CertPathValidator.getInstance("PKIX", "BC"); PKIXParameters param = new PKIXParameters(trust); param.setRevocationEnabled(false); + param.setDate(new Date(1744869361113L)); // 17th April 2025 cpv.validate(certPath, param); } diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java index d0c3304e5b..c188a1bca8 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java @@ -41,16 +41,13 @@ import org.bouncycastle.crypto.BufferedBlockCipher; import org.bouncycastle.crypto.DefaultMultiBlockCipher; import org.bouncycastle.crypto.engines.AESEngine; -import org.bouncycastle.crypto.engines.DESEngine; -import org.bouncycastle.crypto.paddings.PKCS7Padding; -import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; import org.bouncycastle.util.test.TestFailedException; -import org.junit.Assert; + /** * basic test class for a block cipher, basically this just exercises the provider, and makes sure we @@ -868,12 +865,12 @@ else if (algorithm.startsWith("RC5")) } catch (Exception e) { - Assert.fail(e.toString()); + fail(e.toString()); } if (!Arrays.areEqual(data, 0, len, output, 0, output.length)) { - Assert.fail("" + algorithm + " failed doFinal - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(data))); + fail("" + algorithm + " failed doFinal - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(data))); } // @@ -898,12 +895,12 @@ else if (algorithm.startsWith("RC5")) } catch (Exception e) { - Assert.fail(e.toString()); + fail(e.toString()); } if (!Arrays.areEqual(data, 1, 1 + len, output, 0, output.length)) { - Assert.fail("" + algorithm + " failed doFinal - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(data))); + fail("" + algorithm + " failed doFinal - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(data))); } // From e3fa081f68c0d0d15e4d1f27a09443150f208e0a Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 24 Apr 2025 17:41:45 +0930 Subject: [PATCH 330/890] Add a test for Chacha20-Poly1305 in SMIME --- .../smime/test/NewSMIMEAuthEnvelopedTest.java | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/mail/src/test/java/org/bouncycastle/mail/smime/test/NewSMIMEAuthEnvelopedTest.java b/mail/src/test/java/org/bouncycastle/mail/smime/test/NewSMIMEAuthEnvelopedTest.java index 0b6fa27126..5865303bc8 100644 --- a/mail/src/test/java/org/bouncycastle/mail/smime/test/NewSMIMEAuthEnvelopedTest.java +++ b/mail/src/test/java/org/bouncycastle/mail/smime/test/NewSMIMEAuthEnvelopedTest.java @@ -348,6 +348,43 @@ public void testCapEncrypt() SMIMETestUtil.verifyMessageBytes(msg, res); } + public void testChacha20Poly1305Encrypt() + throws Exception + { + MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington"); + + SMIMEAuthEnvelopedGenerator gen = new SMIMEAuthEnvelopedGenerator(); + + // + // create a subject key id - this has to be done the same way as + // it is done in the certificate associated with the private key + // + MessageDigest dig = MessageDigest.getInstance("SHA256", BC); + + dig.update(_reciCert.getPublicKey().getEncoded()); + + gen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(dig.digest(), _reciCert.getPublicKey()).setProvider(BC)); + + // + // generate a MimeBodyPart object which encapsulates the content + // we want encrypted. + // + MimeBodyPart mp = gen.generate(msg, new JceCMSContentEncryptorBuilder(CMSAlgorithm.ChaCha20Poly1305).setProvider(BC).build()); + + SMIMEAuthEnveloped m = new SMIMEAuthEnveloped(mp); + + dig.update(_reciCert.getPublicKey().getEncoded()); + + RecipientId recId = new KeyTransRecipientId(dig.digest()); + + RecipientInformationStore recipients = m.getRecipientInfos(); + RecipientInformation recipient = recipients.get(recId); + + MimeBodyPart res = SMIMEUtil.toMimeBodyPart(recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC))); + + SMIMETestUtil.verifyMessageBytes(msg, res); + } + public void testTwoRecipients() throws Exception { From 46a92284fda7dfbe1bbd6e97812cdf1c71e9a314 Mon Sep 17 00:00:00 2001 From: David Hook Date: Thu, 24 Apr 2025 20:21:19 +1000 Subject: [PATCH 331/890] moved CMS mod OIDs to PKCS object identifiers. --- .../asn1/pkcs/PKCSObjectIdentifiers.java | 15 +++++++++++ .../asn1/kisa/KISAObjectIdentifiers.java | 25 +++++++++---------- .../asn1/kisa/KISAObjectIdentifiers.java | 6 ----- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java index 9343de210d..7663ab59c2 100644 --- a/core/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java @@ -224,6 +224,19 @@ public interface PKCSObjectIdentifiers /** PKCS#9: 1.2.840.113549.1.9.15.3 -- smime capability */ ASN1ObjectIdentifier sMIMECapabilitiesVersions = pkcs_9.branch("15.3"); + // + // id-mod OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) mod(0)} + // + /** RFC 4010: SeedEncryptionAlgorithmInCMS; OID 1.2.840.113549.1.9.16.0.24 */ + ASN1ObjectIdentifier id_mod_cms_seed = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.24"); + + /** RFC 9708 MTS-HashSig-2013; OID 1.2.840.113549.1.9.16.0.64 */ + ASN1ObjectIdentifier id_mod_mts_hashsig_2013 = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.64"); + + /** RFC 8103 id-mod-CMS-AEADChaCha20Poly1305; OID 1.2.840.113549.1.9.16.0.66 */ + ASN1ObjectIdentifier id_mod_CMS_AEADChaCha20Poly1305 = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.66"); + // // id-ct OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1)} @@ -261,6 +274,8 @@ public interface PKCSObjectIdentifiers /** PKCS#9: 1.2.840.113549.1.9.16.3.10 */ ASN1ObjectIdentifier id_alg_SSDH = smime_alg.branch("10"); + + /** *
          * -- RSA-KEM Key Transport Algorithm  RFC 5990
    diff --git a/core/src/main/java/org/bouncycastle/internal/asn1/kisa/KISAObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/internal/asn1/kisa/KISAObjectIdentifiers.java
    index 5f94ce2c1b..978d19b04f 100644
    --- a/core/src/main/java/org/bouncycastle/internal/asn1/kisa/KISAObjectIdentifiers.java
    +++ b/core/src/main/java/org/bouncycastle/internal/asn1/kisa/KISAObjectIdentifiers.java
    @@ -14,24 +14,23 @@
      */
     public interface KISAObjectIdentifiers
     {
    -    /** RFC 4010, 4269: id-seedCBC; OID 1.2.410.200004.1.4 */
    +    /**
    +     * RFC 4010, 4269: id-seedCBC; OID 1.2.410.200004.1.4
    +     */
         static final ASN1ObjectIdentifier id_seedCBC = new ASN1ObjectIdentifier("1.2.410.200004.1.4");
     
    -    /** RFC 4269: id-seedMAC; OID 1.2.410.200004.1.7 */
    +    /**
    +     * RFC 4269: id-seedMAC; OID 1.2.410.200004.1.7
    +     */
         static final ASN1ObjectIdentifier id_seedMAC = new ASN1ObjectIdentifier("1.2.410.200004.1.7");
     
    -    /** RFC 4269: pbeWithSHA1AndSEED-CBC; OID 1.2.410.200004.1.15 */
    +    /**
    +     * RFC 4269: pbeWithSHA1AndSEED-CBC; OID 1.2.410.200004.1.15
    +     */
         static final ASN1ObjectIdentifier pbeWithSHA1AndSEED_CBC = new ASN1ObjectIdentifier("1.2.410.200004.1.15");
     
    -    /** RFC 4010: id-npki-app-cmsSeed-wrap; OID 1.2.410.200004.7.1.1.1 */
    +    /**
    +     * RFC 4010: id-npki-app-cmsSeed-wrap; OID 1.2.410.200004.7.1.1.1
    +     */
         static final ASN1ObjectIdentifier id_npki_app_cmsSeed_wrap = new ASN1ObjectIdentifier("1.2.410.200004.7.1.1.1");
    -
    -    /** RFC 4010: SeedEncryptionAlgorithmInCMS; OID 1.2.840.113549.1.9.16.0.24 */
    -    static final ASN1ObjectIdentifier id_mod_cms_seed = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.24");
    -
    -    /** RFC 9708 MTS-HashSig-2013; OID 1.2.840.113549.1.9.16.0.64 */
    -    static final ASN1ObjectIdentifier id_mod_mts_hashsig_2013 = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.64");
    -
    -    /** RFC 8103 id-mod-CMS-AEADChaCha20Poly1305; OID 1.2.840.113549.1.9.16.0.66 */
    -    static final ASN1ObjectIdentifier id_mod_CMS_AEADChaCha20Poly1305 = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.66");
     }
    diff --git a/util/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java b/util/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java
    index 459aaebde1..1cdb668bba 100644
    --- a/util/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java
    +++ b/util/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java
    @@ -25,10 +25,4 @@ public interface KISAObjectIdentifiers
     
         /** RFC 4010: id-npki-app-cmsSeed-wrap; OID 1.2.410.200004.7.1.1.1 */
         static final ASN1ObjectIdentifier id_npki_app_cmsSeed_wrap = new ASN1ObjectIdentifier("1.2.410.200004.7.1.1.1");
    -
    -    /** RFC 4010: SeedEncryptionAlgorithmInCMS; OID 1.2.840.113549.1.9.16.0.24 */
    -    static final ASN1ObjectIdentifier id_mod_cms_seed = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.24");
    -
    -    /** RFC 9708 MTS-HashSig-2013; OID 1.2.840.113549.1.9.16.0.64 */
    -    static final ASN1ObjectIdentifier id_mod_mts_hashsig_2013 = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.64");
     }
    
    From 2f4d33d57797dcc3fe9bd4ecb07ee0557ff58185 Mon Sep 17 00:00:00 2001
    From: David Hook 
    Date: Thu, 24 Apr 2025 20:22:24 +1000
    Subject: [PATCH 332/890] added additional overlap test.
    
    ---
     .../jce/provider/test/BlockCipherTest.java    | 58 +++++++++++++++++++
     1 file changed, 58 insertions(+)
    
    diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java
    index c188a1bca8..6e3bea071d 100644
    --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java
    +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java
    @@ -1802,6 +1802,7 @@ public void performTest()
             doFinalTest();
             testOverlapping();
             testOverlapping2();
    +        testOverlap();
         }
     
         private void doFinalTest()
    @@ -1896,6 +1897,63 @@ private void testOverlapping2()
             }
         }
     
    +    public void testOverlap()
    +    {
    +        try
    +        {
    +            int l = 32;
    +            byte[] msg = new byte[l];
    +            Arrays.fill(msg, (byte)1);
    +
    +            byte[] workingArray = new byte[l * 2];
    +            Arrays.fill(workingArray, (byte)1);
    +            System.arraycopy(msg, 0, workingArray, 0, msg.length);
    +
    +            byte[] originalWorkingArray = new byte[workingArray.length];
    +            System.arraycopy(workingArray, 0, originalWorkingArray, 0, workingArray.length);
    +
    +            Cipher javaEncrypt = Cipher.getInstance("AES/ECB/NoPadding", BouncyCastleProvider.PROVIDER_NAME);
    +            javaEncrypt.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(new byte[16], "AES"));
    +
    +            //
    +            // Expected encryption
    +            //
    +            byte[] expectedOutput = new byte[msg.length];
    +            javaEncrypt.doFinal(msg, 0, msg.length, expectedOutput, 0);
    +
    +
    +            //
    +            // We expect to see the "expectedOutput" being written at each offset.
    +            //
    +            for (int outputOffset = 0; outputOffset < msg.length; outputOffset++)
    +            {
    +                javaEncrypt.doFinal(workingArray, 0, msg.length, workingArray, outputOffset);
    +
    +                // Grab a copy of the produced cipher text
    +                byte[] ct = Arrays.copyOfRange(workingArray, outputOffset, outputOffset + msg.length);
    +                System.out.println("\nOutput Offset: " + outputOffset);
    +                System.out.println("Expected: " + pad(outputOffset * 2) + Hex.toHexString(expectedOutput));
    +                System.out.println("Actual  : " + Hex.toHexString(workingArray));
    +
    +                isTrue(Arrays.areEqual(ct, expectedOutput));
    +
    +                System.arraycopy(originalWorkingArray, 0, workingArray, 0, originalWorkingArray.length);
    +            }
    +        }
    +        catch (Exception e)
    +        {
    +            fail(e.getMessage(), e);
    +        }
    +
    +    }
    +
    +    public String pad(int len)
    +    {
    +        char[] buf = new char[len];
    +        Arrays.fill(buf, ' ');
    +        return new String(buf);
    +    }
    +
         public static void main(
             String[]    args)
         {
    
    From 701b2bfa09a808d23e7c7b5f5e20c5f46b7dc530 Mon Sep 17 00:00:00 2001
    From: mt-johan <172122831+mt-johan@users.noreply.github.com>
    Date: Thu, 1 May 2025 16:31:03 +0200
    Subject: [PATCH 333/890] Keep PBMAC1s PBKDF2 PRF when initializing from
     protectionAlgorithm. Contributed under the Bouncy Caste License.
    
    ---
     .../jcajce/JcePBMac1CalculatorBuilder.java    |  1 +
     .../org/bouncycastle/pkcs/test/PBETest.java   | 25 +++++++++++++++++++
     2 files changed, 26 insertions(+)
    
    diff --git a/pkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePBMac1CalculatorBuilder.java b/pkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePBMac1CalculatorBuilder.java
    index 2869097e8d..18d47405cb 100644
    --- a/pkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePBMac1CalculatorBuilder.java
    +++ b/pkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePBMac1CalculatorBuilder.java
    @@ -187,6 +187,7 @@ public MacCalculator build(final char[] password)
                     salt = pbeParams.getSalt();
                     iterationCount = BigIntegers.intValueExact(pbeParams.getIterationCount());
                     keySize = BigIntegers.intValueExact(pbeParams.getKeyLength()) * 8;
    +                prf = pbeParams.getPrf();
                 }
                 
                 SecretKeyFactory secFact = helper.createSecretKeyFactory("PBKDF2");
    diff --git a/pkix/src/test/java/org/bouncycastle/pkcs/test/PBETest.java b/pkix/src/test/java/org/bouncycastle/pkcs/test/PBETest.java
    index e213c9f4d2..817ae9bd0a 100644
    --- a/pkix/src/test/java/org/bouncycastle/pkcs/test/PBETest.java
    +++ b/pkix/src/test/java/org/bouncycastle/pkcs/test/PBETest.java
    @@ -3,9 +3,17 @@
     import java.security.Security;
     
     import junit.framework.TestCase;
    +
    +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
    +import org.bouncycastle.asn1.pkcs.PBKDF2Params;
    +import org.bouncycastle.asn1.pkcs.PBMAC1Params;
    +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
    +import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
     import org.bouncycastle.jce.provider.BouncyCastleProvider;
     import org.bouncycastle.operator.MacCalculator;
    +import org.bouncycastle.operator.OperatorCreationException;
     import org.bouncycastle.pkcs.jcajce.JcePBMac1CalculatorBuilder;
    +import org.bouncycastle.pkcs.jcajce.JcePBMac1CalculatorProviderBuilder;
     import org.bouncycastle.util.Strings;
     import org.bouncycastle.util.encoders.Hex;
     
    @@ -29,4 +37,21 @@ public void testPBESHA256()
             assertEquals("55ac046e56e3089fec1691c22544b605f94185216dde0465e68b9d57c20dacbc", Hex.toHexString((byte[])pbCalculator.getKey().getRepresentation()));
     
         }
    +
    +    void testPbmac1PrfPropagation() throws OperatorCreationException {
    +        AlgorithmIdentifier prf = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_512, null);;
    +        AlgorithmIdentifier protectionAlgorithm = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBMAC1,
    +            new PBMAC1Params(
    +                new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params("salt".getBytes(), 1234, 64, prf)),
    +                new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_512, null)
    +            )
    +        );
    +        MacCalculator calculator = new JcePBMac1CalculatorProviderBuilder()
    +                .setProvider(new BouncyCastleProvider()).build().get(protectionAlgorithm, "foobar123".toCharArray());
    +        AlgorithmIdentifier actualPrf = PBKDF2Params.getInstance(
    +            PBMAC1Params.getInstance(calculator.getKey().getAlgorithmIdentifier().getParameters()).getKeyDerivationFunc().getParameters()
    +        ).getPrf();
    +        System.out.println("Should be true: " + prf.equals(actualPrf));
    +    }
    +
     }
    
    From 2984a3899888b41a8eb4b5725677507b8cccaefb Mon Sep 17 00:00:00 2001
    From: Peter Dettman 
    Date: Tue, 6 May 2025 17:10:26 +0700
    Subject: [PATCH 334/890] Javadoc escape chars
    
    ---
     core/src/main/java/org/bouncycastle/util/GF16.java | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/core/src/main/java/org/bouncycastle/util/GF16.java b/core/src/main/java/org/bouncycastle/util/GF16.java
    index 8ffa203a03..3cb0878965 100644
    --- a/core/src/main/java/org/bouncycastle/util/GF16.java
    +++ b/core/src/main/java/org/bouncycastle/util/GF16.java
    @@ -38,7 +38,7 @@ static byte mt(int p, int q)
          * 

    * This method multiplies two elements in GF(16) (represented as integers 0–15) * using carryless multiplication followed by reduction modulo x^4 + x + 1. - * Please ensure a<=0x0F and b<=0x0F + * Please ensure a <= 0x0F and b <= 0x0F * * @param a an element in GF(16) (only the lower 4 bits are used) * @param b an element in GF(16) (only the lower 4 bits are used) @@ -54,7 +54,7 @@ public static byte mul(byte a, byte b) *

    * This method multiplies two elements in GF(16) (represented as integers 0–15) * using carryless multiplication followed by reduction modulo x^4 + x + 1. - * Please ensure a<=0x0F and b<=0x0F + * Please ensure a <= 0x0F and b <= 0x0F * * @param a an element in GF(16) (only the lower 4 bits are used) * @param b an element in GF(16) (only the lower 4 bits are used) From 02114009bc2bfab08b460d0e031f8c2cb87c3cb0 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 6 May 2025 17:40:02 +0700 Subject: [PATCH 335/890] Use test sampling with SNOVA --- .../pqc/crypto/test/MayoTest.java | 4 ++-- .../pqc/crypto/test/SnovaTest.java | 4 ++-- .../pqc/crypto/test/TestUtils.java | 21 +++++++++++-------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java index 4cdab9d282..e275ecf2de 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MayoTest.java @@ -46,10 +46,10 @@ public void testTestVectors() throws Exception { long start = System.currentTimeMillis(); - TestUtils.testTestVector(true, false, "pqc/crypto/mayo", files, new TestUtils.KeyGenerationOperation() + TestUtils.testTestVector(false, true, false, "pqc/crypto/mayo", files, new TestUtils.KeyGenerationOperation() { @Override - public SecureRandom getSecureRanom(byte[] seed) + public SecureRandom getSecureRandom(byte[] seed) { return new NISTSecureRandom(seed, null); } diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java index 0b23a9af97..2800c307b5 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SnovaTest.java @@ -126,10 +126,10 @@ public void testTestVectors() throws Exception { long start = System.currentTimeMillis(); - TestUtils.testTestVector(true, false, "pqc/crypto/snova", files, new TestUtils.KeyGenerationOperation() + TestUtils.testTestVector(true, true, false, "pqc/crypto/snova", files, new TestUtils.KeyGenerationOperation() { @Override - public SecureRandom getSecureRanom(byte[] seed) + public SecureRandom getSecureRandom(byte[] seed) { return new NISTSecureRandom(seed, null); } diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java index 51458c078d..32072717f1 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestUtils.java @@ -32,7 +32,7 @@ static boolean parseBoolean(String value) public interface KeyGenerationOperation { - SecureRandom getSecureRanom(byte[] seed); + SecureRandom getSecureRandom(byte[] seed); AsymmetricCipherKeyPairGenerator getAsymmetricCipherKeyPairGenerator(int fileIndex, SecureRandom random); @@ -45,17 +45,19 @@ public interface KeyGenerationOperation MessageSigner getMessageSigner(); } - public static void testTestVector(boolean enableFactory, boolean isSigner, String homeDir, String[] files, KeyGenerationOperation operation) + public static void testTestVector(boolean sampleOnly, boolean enableFactory, boolean isSigner, String homeDir, String[] files, KeyGenerationOperation operation) throws Exception { for (int fileIndex = 0; fileIndex != files.length; fileIndex++) { String name = files[fileIndex]; + InputStream src = TestResourceFinder.findTestResource(homeDir, name); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); - //System.out.println(files[fileIndex]); + String line; HashMap buf = new HashMap(); + TestSampler sampler = sampleOnly ? new TestSampler() : null; while ((line = bin.readLine()) != null) { line = line.trim(); @@ -68,18 +70,19 @@ public static void testTestVector(boolean enableFactory, boolean isSigner, Strin { if (buf.size() > 0) { - int count = Integer.parseInt(buf.get("count")); -// if (count == 99) -// { -// System.out.println("break"); -// } + String count = (String)buf.get("count"); + if (sampler != null && sampler.skipTest(count)) + { + continue; + } + byte[] seed = Hex.decode((String)buf.get("seed")); byte[] pk = Hex.decode((String)buf.get("pk")); byte[] sk = Hex.decode((String)buf.get("sk")); byte[] message = Hex.decode((String)buf.get("msg")); byte[] signature = Hex.decode((String)buf.get("sm")); - SecureRandom random = operation.getSecureRanom(seed); + SecureRandom random = operation.getSecureRandom(seed); AsymmetricCipherKeyPairGenerator kpGen = operation.getAsymmetricCipherKeyPairGenerator(fileIndex, random); From 25b4969719174d8dd92017e69df4df277b9dd2f0 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 6 May 2025 17:40:28 +0700 Subject: [PATCH 336/890] IntelliJ .gitignore updates --- .gitignore | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 72ab296f63..6c3cd331f6 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ *.swp *.iml */*.iml +*.ipr +*.iws bin/ build/ @@ -22,7 +24,7 @@ pg/*.bak pg/*.bpg pg/*.txt -.idea +.idea/ codesigning.jks From de3da9f9403b4e996f0b6a21b6720ed0790a81df Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 6 May 2025 17:41:47 +0700 Subject: [PATCH 337/890] Use a new TestSampler per file/group --- .../pqc/crypto/test/BIKETest.java | 5 +---- .../pqc/crypto/test/CMCEVectorTest.java | 4 ++-- .../crypto/test/CrystalsDilithiumTest.java | 12 ++++++------ .../pqc/crypto/test/FalconTest.java | 6 +++--- .../pqc/crypto/test/FrodoVectorTest.java | 4 ++-- .../bouncycastle/pqc/crypto/test/HQCTest.java | 8 ++------ .../bouncycastle/pqc/crypto/test/NTRUKAT.java | 3 +-- .../pqc/crypto/test/NTRULPRimeTest.java | 3 +-- .../pqc/crypto/test/PicnicVectorTest.java | 5 ++--- .../pqc/crypto/test/SABERVectorTest.java | 5 ++--- .../pqc/crypto/test/SNTRUPrimeTest.java | 2 +- .../pqc/crypto/test/SphincsPlusTest.java | 19 +++++-------------- .../pqc/crypto/test/TestSampler.java | 10 +++++++--- .../pqc/crypto/test/XMSSPrivateKeyTest.java | 1 - 14 files changed, 35 insertions(+), 52 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/BIKETest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/BIKETest.java index f1ca00279a..f5f433eda6 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/BIKETest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/BIKETest.java @@ -4,7 +4,6 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; -import java.util.Random; import junit.framework.TestCase; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; @@ -52,7 +51,6 @@ public void testVectors() BIKEParameters.bike256 }; - TestSampler sampler = new TestSampler(); for (int fileIndex = 0; fileIndex < files.length; fileIndex++) { String name = files[fileIndex]; @@ -62,8 +60,7 @@ public void testVectors() String line = null; HashMap buf = new HashMap(); - Random rnd = new Random(System.currentTimeMillis()); - + TestSampler sampler = new TestSampler(); while ((line = bin.readLine()) != null) { line = line.trim(); diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/CMCEVectorTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/CMCEVectorTest.java index b5f4ef3480..92f6525b74 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/CMCEVectorTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/CMCEVectorTest.java @@ -71,16 +71,16 @@ public void testVectors() CMCEParameters.mceliece8192128fr3 }; - TestSampler sampler = new TestSampler(); for (int fileIndex = 0; fileIndex != files.length; fileIndex++) { String name = files[fileIndex]; - // System.out.println("testing: " + name); + InputStream src = TestResourceFinder.findTestResource("pqc/crypto/cmce", name); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); String line = null; HashMap buf = new HashMap(); + TestSampler sampler = new TestSampler(); while ((line = bin.readLine()) != null) { line = line.trim(); diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/CrystalsDilithiumTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/CrystalsDilithiumTest.java index ef8a6dd383..c50a69b206 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/CrystalsDilithiumTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/CrystalsDilithiumTest.java @@ -42,16 +42,16 @@ public void testKeyGen() throws IOException DilithiumParameters.dilithium5, }; - TestSampler sampler = new TestSampler(); for (int fileIndex = 0; fileIndex != files.length; fileIndex++) { String name = files[fileIndex]; - // System.out.println("testing: " + name); + InputStream src = TestResourceFinder.findTestResource("pqc/crypto/dilithium/acvp", name); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); String line = null; HashMap buf = new HashMap(); + TestSampler sampler = new TestSampler(); while ((line = bin.readLine()) != null) { line = line.trim(); @@ -115,16 +115,16 @@ public void testSigGen() throws IOException DilithiumParameters.dilithium5, }; - TestSampler sampler = new TestSampler(); for (int fileIndex = 0; fileIndex != files.length; fileIndex++) { String name = files[fileIndex]; - // System.out.println("testing: " + name); + InputStream src = TestResourceFinder.findTestResource("pqc/crypto/dilithium/acvp", name); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); String line = null; HashMap buf = new HashMap(); + TestSampler sampler = new TestSampler(); while ((line = bin.readLine()) != null) { line = line.trim(); @@ -194,16 +194,16 @@ public void testSigVer() throws IOException DilithiumParameters.dilithium5, }; - TestSampler sampler = new TestSampler(); for (int fileIndex = 0; fileIndex != files.length; fileIndex++) { String name = files[fileIndex]; - // System.out.println("testing: " + name); + InputStream src = TestResourceFinder.findTestResource("pqc/crypto/dilithium/acvp", name); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); String line = null; HashMap buf = new HashMap(); + TestSampler sampler = new TestSampler(); while ((line = bin.readLine()) != null) { line = line.trim(); diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/FalconTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/FalconTest.java index 5126132033..e7ab435d10 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/FalconTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/FalconTest.java @@ -39,16 +39,16 @@ public void testVectors() FalconParameters.falcon_1024 }; - TestSampler sampler = new TestSampler(); - for (int fileindex = 0; fileindex < files.length; fileindex++) { String name = files[fileindex]; - // System.out.println("testing: " + name); + InputStream src = TestResourceFinder.findTestResource("pqc/crypto/falcon", name); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); + String line = null; HashMap buf = new HashMap(); + TestSampler sampler = new TestSampler(); while ((line = bin.readLine()) != null) { line = line.trim(); diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/FrodoVectorTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/FrodoVectorTest.java index 1e6c6d7ef6..3d0b7031b6 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/FrodoVectorTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/FrodoVectorTest.java @@ -58,16 +58,16 @@ public void testVectors() FrodoParameters.frodokem1344shake }; - TestSampler sampler = new TestSampler(); for (int fileIndex = 0; fileIndex != files.length; fileIndex++) { String name = files[fileIndex]; - // System.out.println("testing: " + name); + InputStream src = TestResourceFinder.findTestResource("pqc/crypto/frodo", name); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); String line = null; HashMap buf = new HashMap(); + TestSampler sampler = new TestSampler(); while ((line = bin.readLine()) != null) { line = line.trim(); diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/HQCTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/HQCTest.java index e9cb95fe2b..b133b888b3 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/HQCTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/HQCTest.java @@ -4,7 +4,6 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashMap; -import java.util.Random; import junit.framework.TestCase; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; @@ -53,19 +52,16 @@ public void testVectors() HQCParameters.hqc256 }; - TestSampler sampler = new TestSampler(); for (int fileIndex = 0; fileIndex < files.length; fileIndex++) { - // System.out.println("Working Directory = " + System.getProperty("user.dir")); String name = files[fileIndex]; - // System.out.println("testing: " + name); + InputStream src = TestResourceFinder.findTestResource("pqc/crypto/hqc", name); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); String line = null; HashMap buf = new HashMap(); - Random rnd = new Random(System.currentTimeMillis()); - + TestSampler sampler = new TestSampler(); while ((line = bin.readLine()) != null) { line = line.trim(); diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/NTRUKAT.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/NTRUKAT.java index e6888c3951..f400db2f59 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/NTRUKAT.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/NTRUKAT.java @@ -31,13 +31,12 @@ public class NTRUKAT */ public byte[] ss; - private static final TestSampler sampler = new TestSampler(); - public static List getKAT(InputStream src) { List kats = new ArrayList(); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); HashMap buf = new HashMap(); + TestSampler sampler = new TestSampler(); try { for (String line = bin.readLine(); line != null; line = bin.readLine()) diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/NTRULPRimeTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/NTRULPRimeTest.java index 774c6e59f1..13e2d4afd3 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/NTRULPRimeTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/NTRULPRimeTest.java @@ -37,8 +37,6 @@ public void testKEM() NTRULPRimeParameters.ntrulpr1277 }; - TestSampler sampler = new TestSampler(); - for (int i = 0; i != paramList.length; i++) { NTRULPRimeParameters paramSpec = paramList[i]; @@ -47,6 +45,7 @@ public void testKEM() BufferedReader resourceReader = new BufferedReader(new InputStreamReader(resource)); String line; + TestSampler sampler = new TestSampler(); while ((line = resourceReader.readLine()) != null) { if (! line.startsWith("count")) diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/PicnicVectorTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/PicnicVectorTest.java index 5d048aff09..765e592c0e 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/PicnicVectorTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/PicnicVectorTest.java @@ -90,17 +90,16 @@ public void testVectors() }; } - TestSampler sampler = new TestSampler(); - for (int fileIndex = 0; fileIndex != files.length; fileIndex++) { String name = files[fileIndex]; - // System.out.println("testing: " + name); + InputStream src = TestResourceFinder.findTestResource("pqc/crypto/picnic", name); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); String line = null; HashMap buf = new HashMap(); + TestSampler sampler = new TestSampler(); while ((line = bin.readLine()) != null) { line = line.trim(); diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SABERVectorTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SABERVectorTest.java index ce5f0d1ea7..d63527e933 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SABERVectorTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SABERVectorTest.java @@ -79,17 +79,16 @@ public void testVectors() "ufiresaber-90s.rsp", }; - TestSampler sampler = new TestSampler(); - for (int fileIndex = 0; fileIndex != files.length; fileIndex++) { String name = files[fileIndex]; - // System.out.println("testing: " + name); + InputStream src = TestResourceFinder.findTestResource("pqc/crypto/saber", name); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); String line = null; HashMap buf = new HashMap(); + TestSampler sampler = new TestSampler(); while ((line = bin.readLine()) != null) { line = line.trim(); diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SNTRUPrimeTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SNTRUPrimeTest.java index f10d8dd235..865a5d460f 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SNTRUPrimeTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SNTRUPrimeTest.java @@ -37,7 +37,6 @@ public void testKEM() SNTRUPrimeParameters.sntrup1277 }; - TestSampler sampler = new TestSampler(); for (int i = 0; i != paramList.length; i++) { SNTRUPrimeParameters paramSpec = paramList[i]; @@ -46,6 +45,7 @@ public void testKEM() BufferedReader resourceReader = new BufferedReader(new InputStreamReader(resource)); String line; + TestSampler sampler = new TestSampler(); while ((line = resourceReader.readLine()) != null) { if (! line.startsWith("count")) diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SphincsPlusTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SphincsPlusTest.java index 95856025a2..5fea355d8f 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/SphincsPlusTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/SphincsPlusTest.java @@ -8,7 +8,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import java.util.Random; import junit.framework.TestCase; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; @@ -45,22 +44,18 @@ public void testVectors() " haraka-128s-simple.rsp haraka-256f-simple.rsp" + " haraka-192f-simple.rsp haraka-256s-simple.rsp"; - TestSampler sampler = new TestSampler(); - - Random rd = new Random(System.currentTimeMillis()); - - int offSet = rd.nextInt(10); - String[] fileList = splitOn(files, ' '); - //long startTime = System.currentTimeMillis(); + for (int i = 0; i != fileList.length; i++) { String name = fileList[i]; + InputStream src = TestResourceFinder.findTestResource("pqc/crypto/sphincs_plus", "subset_" + name); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); - // System.out.println(name); + String line = null; HashMap buf = new HashMap(); + TestSampler sampler = new TestSampler(); while ((line = bin.readLine()) != null) { line = line.trim(); @@ -80,14 +75,10 @@ public void testVectors() byte[] sigExpected = Hex.decode((String)buf.get("sm")); byte[] oprR = Hex.decode((String)buf.get("optrand")); - if (Integer.parseInt(count) != offSet) + if (sampler.skipTest(count)) { continue; } -// if (sampler.skipTest(count)) -// { -// continue; -// } SPHINCSPlusKeyPairGenerator kpGen = new SPHINCSPlusKeyPairGenerator(); SecureRandom random = new FixedSecureRandom(sk); diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestSampler.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestSampler.java index 054663747f..75ff701b15 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestSampler.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/TestSampler.java @@ -20,12 +20,16 @@ class TestSampler boolean skipTest(String count) { - int c = Integer.parseInt(count); - return !isFull && c != 0 && ((c + offSet) % 9 != 0); + return !isFull && shouldSkip(Integer.parseInt(count)); } boolean skipTest(int count) { - return !isFull && count != 0 && ((count + offSet) % 9 != 0); + return !isFull && shouldSkip(count); + } + + private boolean shouldSkip(int count) + { + return count != 0 && ((count + offSet) % 9 != 0); } } diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/XMSSPrivateKeyTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/XMSSPrivateKeyTest.java index 7118bada5e..5f18de9089 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/XMSSPrivateKeyTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/XMSSPrivateKeyTest.java @@ -4,7 +4,6 @@ import junit.framework.TestCase; import org.bouncycastle.crypto.Digest; -import org.bouncycastle.crypto.Xof; import org.bouncycastle.crypto.digests.SHA256Digest; import org.bouncycastle.crypto.digests.SHA512Digest; import org.bouncycastle.crypto.digests.SHAKEDigest; From 1545fc7664c929f88e8b3433cceb64322a35851a Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 6 May 2025 20:50:32 +0700 Subject: [PATCH 338/890] Fix expected exception message --- .../java/org/bouncycastle/asn1/misc/test/CMPUpdates16Test.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/util/src/test/java/org/bouncycastle/asn1/misc/test/CMPUpdates16Test.java b/util/src/test/java/org/bouncycastle/asn1/misc/test/CMPUpdates16Test.java index 4e760e1792..7e1ebafeb0 100644 --- a/util/src/test/java/org/bouncycastle/asn1/misc/test/CMPUpdates16Test.java +++ b/util/src/test/java/org/bouncycastle/asn1/misc/test/CMPUpdates16Test.java @@ -61,7 +61,7 @@ public void testCRLSource() } catch (IllegalArgumentException ilex) { - assertEquals("unknown tag 3", ilex.getMessage()); + assertEquals("unknown tag [CONTEXT 3]", ilex.getMessage()); } // Check that both values are not set at construction From 37278055c22f109458c7a703a99b22e3ab87c09a Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 6 May 2025 23:27:25 +0700 Subject: [PATCH 339/890] Allow CRLF line endings in some PGP tests --- ...OpenPGPDetachedSignatureProcessorTest.java | 5 ++-- .../api/test/OpenPGPMessageGeneratorTest.java | 26 +++++++++++-------- .../openpgp/test/ArmorCRCTest.java | 18 ++++++------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java index 8d08d4fcf1..0cb339b7ee 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java @@ -22,6 +22,7 @@ import org.bouncycastle.openpgp.api.SignatureParameters; import org.bouncycastle.openpgp.api.SignatureSubpacketsFunction; import org.bouncycastle.openpgp.operator.PGPKeyPairGenerator; +import org.bouncycastle.util.Strings; public class OpenPGPDetachedSignatureProcessorTest extends APITest @@ -63,7 +64,7 @@ private void createVerifyV4Signature(OpenPGPApi api) OpenPGPSignature.OpenPGPDocumentSignature signature = signatures.get(0); isEquals(4, signature.getSignature().getVersion()); String armored = signature.toAsciiArmoredString(); - isTrue(armored.startsWith("-----BEGIN PGP SIGNATURE-----\n")); + isTrue(armored.startsWith("-----BEGIN PGP SIGNATURE-----" + Strings.lineSeparator())); // Verify detached signatures OpenPGPDetachedSignatureProcessor processor = api.verifyDetachedSignature(); @@ -90,7 +91,7 @@ private void createVerifyV6Signature(OpenPGPApi api) OpenPGPSignature.OpenPGPDocumentSignature signature = signatures.get(0); isEquals(6, signature.getSignature().getVersion()); String armored = signature.toAsciiArmoredString(); - isTrue(armored.startsWith("-----BEGIN PGP SIGNATURE-----\n")); + isTrue(armored.startsWith("-----BEGIN PGP SIGNATURE-----" + Strings.lineSeparator())); // Verify detached signatures OpenPGPDetachedSignatureProcessor processor = api.verifyDetachedSignature(); diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java index 4dd960f1a0..f6cd62842b 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java @@ -13,6 +13,7 @@ import org.bouncycastle.openpgp.api.OpenPGPKey; import org.bouncycastle.openpgp.api.OpenPGPMessageGenerator; import org.bouncycastle.openpgp.api.OpenPGPMessageOutputStream; +import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; public class OpenPGPMessageGeneratorTest @@ -52,12 +53,13 @@ private void armoredLiteralDataPacket(OpenPGPApi api) msgOut.close(); - isEquals( - "-----BEGIN PGP MESSAGE-----\n" + - "\n" + - "yxNiAAAAAABIZWxsbywgV29ybGQh\n" + - "-----END PGP MESSAGE-----\n", - bOut.toString()); + String nl = Strings.lineSeparator(); + String expected = + "-----BEGIN PGP MESSAGE-----" + nl + + nl + + "yxNiAAAAAABIZWxsbywgV29ybGQh" + nl + + "-----END PGP MESSAGE-----" + nl; + isEquals(expected, bOut.toString()); } private void unarmoredLiteralDataPacket(OpenPGPApi api) @@ -93,11 +95,13 @@ private void armoredCompressedLiteralDataPacket(OpenPGPApi api) msgOut.close(); - isEquals("-----BEGIN PGP MESSAGE-----\n" + - "\n" + - "yBUBOy2cxAACHqk5Ofk6CuH5RTkpigA=\n" + - "-----END PGP MESSAGE-----\n", - bOut.toString()); + String nl = Strings.lineSeparator(); + String expected = + "-----BEGIN PGP MESSAGE-----" + nl + + nl + + "yBUBOy2cxAACHqk5Ofk6CuH5RTkpigA=" + nl + + "-----END PGP MESSAGE-----" + nl; + isEquals(expected, bOut.toString()); } private void unarmoredCompressedLiteralDataPacket(OpenPGPApi api) diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/ArmorCRCTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/ArmorCRCTest.java index a2aca2ad3c..7aa5e7fafb 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/ArmorCRCTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/ArmorCRCTest.java @@ -19,17 +19,17 @@ public class ArmorCRCTest extends SimpleTest { - + private static final String NL = Strings.lineSeparator(); private static final String WITHOUT_CRC = "" + - "-----BEGIN PGP MESSAGE-----\n" + - "\n" + - "yxR0AAAAAABIZWxsbywgV29ybGQhCg==\n" + - "-----END PGP MESSAGE-----\n"; + "-----BEGIN PGP MESSAGE-----" + NL + + NL + + "yxR0AAAAAABIZWxsbywgV29ybGQhCg==" + NL + + "-----END PGP MESSAGE-----" + NL; private static final String FAULTY_CRC = "" + - "-----BEGIN PGP MESSAGE-----\n" + - "\n" + - "yxR0AAAAAABIZWxsbywgV29ybGQhCg==\n" + - "=TRA9\n" + + "-----BEGIN PGP MESSAGE-----" + NL + + NL + + "yxR0AAAAAABIZWxsbywgV29ybGQhCg==" + NL + + "=TRA9" + NL + "-----END PGP MESSAGE-----"; @Override From 233706c10fe874ebeddc0c82d376c9ea5dbda437 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 8 May 2025 10:14:04 +0700 Subject: [PATCH 340/890] Remove unused code --- .../org/bouncycastle/jcajce/provider/asymmetric/MLKEM.java | 3 +-- .../provider/asymmetric/mlkem/MLKEMKeyPairGeneratorSpi.java | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/MLKEM.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/MLKEM.java index 127c40f460..4fc74ce683 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/MLKEM.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/MLKEM.java @@ -1,6 +1,5 @@ package org.bouncycastle.jcajce.provider.asymmetric; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.jcajce.provider.asymmetric.mlkem.MLKEMKeyFactorySpi; import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; @@ -48,7 +47,7 @@ public void configure(ConfigurableProvider provider) addCipherAlgorithm(provider, "ML-KEM-512", PREFIX + "MLKEMCipherSpi$MLKEM512", NISTObjectIdentifiers.id_alg_ml_kem_512); addCipherAlgorithm(provider, "ML-KEM-768", PREFIX + "MLKEMCipherSpi$MLKEM768", NISTObjectIdentifiers.id_alg_ml_kem_768); addCipherAlgorithm(provider, "ML-KEM-1024", PREFIX + "MLKEMCipherSpi$MLKEM1024", NISTObjectIdentifiers.id_alg_ml_kem_1024); - + provider.addKeyInfoConverter(NISTObjectIdentifiers.id_alg_ml_kem_512, keyFact); provider.addKeyInfoConverter(NISTObjectIdentifiers.id_alg_ml_kem_768, keyFact); provider.addKeyInfoConverter(NISTObjectIdentifiers.id_alg_ml_kem_1024, keyFact); diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMKeyPairGeneratorSpi.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMKeyPairGeneratorSpi.java index 3393ac3489..facce8a400 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMKeyPairGeneratorSpi.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/mlkem/MLKEMKeyPairGeneratorSpi.java @@ -75,8 +75,6 @@ public void initialize( { String name = getNameFromParams(params); - MLKEMParameters kyberParams = Utils.getParameters(name); - if (name != null) { MLKEMParameters mlkemParams = Utils.getParameters(name); From b5fa6abb898e1d5a84b2449cc3d18a7091ed8554 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 8 May 2025 10:25:39 +0700 Subject: [PATCH 341/890] Add KemUtil class --- .../tls/crypto/impl/jcajce/JcaTlsCrypto.java | 1 + .../tls/crypto/impl/jcajce/KemUtil.java | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index a42f4e201f..9decf287d0 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -1162,6 +1162,7 @@ protected Boolean isSupportedNamedGroup(int namedGroup) else if (NamedGroup.refersToASpecificKem(namedGroup)) { // TODO[tls-kem] When implemented via provider, need to check for support dynamically +// return Boolean.valueOf(KemUtil.isKemSupported(this, NamedGroup.getKemName(namedGroup))); return Boolean.TRUE; } else if (NamedGroup.refersToAnECDSACurve(namedGroup)) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java new file mode 100644 index 0000000000..d17285805c --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java @@ -0,0 +1,27 @@ +package org.bouncycastle.tls.crypto.impl.jcajce; + +import javax.crypto.Cipher; + +class KemUtil +{ + static Cipher getCipher(JcaTlsCrypto crypto, String kemName) + { + try + { + return crypto.getHelper().createCipher(kemName); + } + catch (AssertionError e) + { + return null; + } + catch (Exception e) + { + return null; + } + } + + static boolean isKemSupported(JcaTlsCrypto crypto, String kemName) + { + return kemName != null && getCipher(crypto, kemName) != null; + } +} From b031bf3ad958e07cbb42720d28c33fc190ec84b8 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 8 May 2025 11:21:00 +0700 Subject: [PATCH 342/890] Move TODOs in to KemUtil --- .../tls/crypto/impl/jcajce/JcaTlsCrypto.java | 18 ++----------- .../tls/crypto/impl/jcajce/KemUtil.java | 26 ++++++++++++++++--- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index 9decf287d0..25835122a4 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -447,19 +447,7 @@ else if (NamedGroup.refersToASpecificFiniteField(namedGroup)) } else if (NamedGroup.refersToASpecificKem(namedGroup)) { - switch (namedGroup) - { - /* - * TODO[tls-kem] Return AlgorithmParameters to check against disabled algorithms? - */ - case NamedGroup.OQS_mlkem512: - case NamedGroup.OQS_mlkem768: - case NamedGroup.OQS_mlkem1024: - case NamedGroup.MLKEM512: - case NamedGroup.MLKEM768: - case NamedGroup.MLKEM1024: - return null; - } + return KemUtil.getAlgorithmParameters(this, NamedGroup.getKemName(namedGroup)); } throw new IllegalArgumentException("NamedGroup not supported: " + NamedGroup.getText(namedGroup)); @@ -1161,9 +1149,7 @@ protected Boolean isSupportedNamedGroup(int namedGroup) } else if (NamedGroup.refersToASpecificKem(namedGroup)) { - // TODO[tls-kem] When implemented via provider, need to check for support dynamically -// return Boolean.valueOf(KemUtil.isKemSupported(this, NamedGroup.getKemName(namedGroup))); - return Boolean.TRUE; + return Boolean.valueOf(KemUtil.isKemSupported(this, NamedGroup.getKemName(namedGroup))); } else if (NamedGroup.refersToAnECDSACurve(namedGroup)) { diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java index d17285805c..0cb8501e05 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java @@ -1,9 +1,27 @@ package org.bouncycastle.tls.crypto.impl.jcajce; +import java.security.AlgorithmParameters; + import javax.crypto.Cipher; class KemUtil { + static AlgorithmParameters getAlgorithmParameters(JcaTlsCrypto crypto, String kemName) + { + try + { + // TODO[tls-kem] Return AlgorithmParameters to check against disabled algorithms? + } + catch (AssertionError e) + { + } + catch (Exception e) + { + } + + return null; + } + static Cipher getCipher(JcaTlsCrypto crypto, String kemName) { try @@ -12,16 +30,18 @@ static Cipher getCipher(JcaTlsCrypto crypto, String kemName) } catch (AssertionError e) { - return null; } catch (Exception e) { - return null; } + + return null; } static boolean isKemSupported(JcaTlsCrypto crypto, String kemName) { - return kemName != null && getCipher(crypto, kemName) != null; + // TODO[tls-kem] When implemented via provider, need to check for support dynamically +// return kemName != null && getCipher(crypto, kemName) != null; + return true; } } From 0b0c782e756b9b8ccf01eb1f8a02a26290097db8 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 8 May 2025 11:31:27 +0700 Subject: [PATCH 343/890] Fix tabs --- .../org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java | 2 +- .../java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index 25835122a4..6af9714cba 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -447,7 +447,7 @@ else if (NamedGroup.refersToASpecificFiniteField(namedGroup)) } else if (NamedGroup.refersToASpecificKem(namedGroup)) { - return KemUtil.getAlgorithmParameters(this, NamedGroup.getKemName(namedGroup)); + return KemUtil.getAlgorithmParameters(this, NamedGroup.getKemName(namedGroup)); } throw new IllegalArgumentException("NamedGroup not supported: " + NamedGroup.getText(namedGroup)); diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java index 0cb8501e05..4c7be748d4 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java @@ -41,7 +41,7 @@ static Cipher getCipher(JcaTlsCrypto crypto, String kemName) static boolean isKemSupported(JcaTlsCrypto crypto, String kemName) { // TODO[tls-kem] When implemented via provider, need to check for support dynamically -// return kemName != null && getCipher(crypto, kemName) != null; +// return kemName != null && getCipher(crypto, kemName) != null; return true; } } From 49341952c2508c0de0eb52e4e0751e9ce4ca60d7 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 8 May 2025 12:25:56 +0700 Subject: [PATCH 344/890] Formatting --- .../java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java index 4c7be748d4..0d4e1872ff 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java @@ -10,7 +10,7 @@ static AlgorithmParameters getAlgorithmParameters(JcaTlsCrypto crypto, String ke { try { - // TODO[tls-kem] Return AlgorithmParameters to check against disabled algorithms? + // TODO[tls-kem] Return AlgorithmParameters to check against disabled algorithms? } catch (AssertionError e) { @@ -42,6 +42,6 @@ static boolean isKemSupported(JcaTlsCrypto crypto, String kemName) { // TODO[tls-kem] When implemented via provider, need to check for support dynamically // return kemName != null && getCipher(crypto, kemName) != null; - return true; + return true; } } From ff55bc9b0c3a62886563de43295a5be530420461 Mon Sep 17 00:00:00 2001 From: David Hook Date: Thu, 8 May 2025 21:47:32 +1000 Subject: [PATCH 345/890] added support for altProvider for the named mode ("," separated). Used named helper to allow for lazy loading of PKCS#11 providers under WildFly. --- .../provider/BouncyCastleJsseProvider.java | 38 ++++- .../tls/crypto/impl/jcajce/JcaTlsCrypto.java | 158 ++++++++++++------ .../impl/jcajce/JcaTlsCryptoProvider.java | 37 +++- 3 files changed, 176 insertions(+), 57 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/BouncyCastleJsseProvider.java b/tls/src/main/java/org/bouncycastle/jsse/provider/BouncyCastleJsseProvider.java index 9ad9564429..486ff1d083 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/provider/BouncyCastleJsseProvider.java +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/BouncyCastleJsseProvider.java @@ -73,6 +73,7 @@ public BouncyCastleJsseProvider(String config) boolean fipsMode = false; String cryptoName = config; + String altCryptoName = null; int colonPos = config.indexOf(':'); if (colonPos >= 0) @@ -81,13 +82,24 @@ public BouncyCastleJsseProvider(String config) String second = config.substring(colonPos + 1).trim(); fipsMode = first.equalsIgnoreCase("fips"); - cryptoName = second; + config = second; + } + + int commaPos = config.indexOf(','); + if (commaPos >= 0) + { + cryptoName = config.substring(0, commaPos).trim(); + altCryptoName = config.substring(commaPos + 1).trim(); + } + else + { + cryptoName = config; } JcaTlsCryptoProvider cryptoProvider; try { - cryptoProvider = createCryptoProvider(cryptoName); + cryptoProvider = createCryptoProvider(cryptoName, altCryptoName); } catch (GeneralSecurityException e) { @@ -116,7 +128,7 @@ public Provider configure(String configArg) return new BouncyCastleJsseProvider(configArg); } - private JcaTlsCryptoProvider createCryptoProvider(String cryptoName) + private JcaTlsCryptoProvider createCryptoProvider(String cryptoName, String altCryptoName) throws GeneralSecurityException { if (cryptoName.equalsIgnoreCase("default")) @@ -127,9 +139,18 @@ private JcaTlsCryptoProvider createCryptoProvider(String cryptoName) Provider provider = Security.getProvider(cryptoName); if (provider != null) { - return new JcaTlsCryptoProvider().setProvider(provider); + JcaTlsCryptoProvider cryptoProvider = new JcaTlsCryptoProvider().setProvider(provider); + + if (altCryptoName != null) + { + // this has to be done by name as a PKCS#11 login may be required. + cryptoProvider.setAlternateProvider(altCryptoName); + } + + return cryptoProvider; } + // TODO: should we support alt name here? try { Class cryptoProviderClass = Class.forName(cryptoName); @@ -234,7 +255,8 @@ public Object createInstance(Object constructorParameter) addAlgorithmImplementation("SSLContext.DEFAULT", "org.bouncycastle.jsse.provider.SSLContext.Default", new EngineCreator() { - public Object createInstance(Object constructorParameter) throws GeneralSecurityException + public Object createInstance(Object constructorParameter) + throws GeneralSecurityException { return new DefaultSSLContextSpi(fipsMode, cryptoProvider); } @@ -281,7 +303,7 @@ public final Provider.Service getService(String type, String algorithm) { String upperCaseAlgName = Strings.toUpperCase(algorithm); String serviceKey = type + "." + upperCaseAlgName; - + BcJsseService service = serviceMap.get(serviceKey); if (service == null) @@ -345,7 +367,7 @@ public synchronized final Set getServices() Set serviceSet = super.getServices(); Set bcServiceSet = new HashSet(); - for (Provider.Service service: serviceSet) + for (Provider.Service service : serviceSet) { bcServiceSet.add(getService(service.getType(), service.getAlgorithm())); } @@ -405,7 +427,7 @@ private static class BcJsseService * @param attributes Map of attributes or null if this implementation * has no attributes * @throws NullPointerException if provider, type, algorithm, or - * className is null + * className is null */ public BcJsseService(Provider provider, String type, String algorithm, String className, List aliases, Map attributes, EngineCreator creator) { diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index 6af9714cba..5d5d04224a 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -77,14 +77,15 @@ /** * Class for providing cryptographic services for TLS based on implementations in the JCA/JCE. *

    - * This class provides default implementations for everything. If you need to customise it, extend the class - * and override the appropriate methods. + * This class provides default implementations for everything. If you need to customise it, extend the class + * and override the appropriate methods. *

    */ public class JcaTlsCrypto extends AbstractTlsCrypto { private final JcaJceHelper helper; + private final JcaJceHelper altHelper; private final SecureRandom entropySource; private final SecureRandom nonceEntropySource; @@ -95,13 +96,27 @@ public class JcaTlsCrypto /** * Base constructor. * - * @param helper a JCA/JCE helper configured for the class's default provider. - * @param entropySource primary entropy source, used for key generation. + * @param helper a JCA/JCE helper configured for the class's default provider. + * @param entropySource primary entropy source, used for key generation. * @param nonceEntropySource secondary entropy source, used for nonce and IV generation. */ protected JcaTlsCrypto(JcaJceHelper helper, SecureRandom entropySource, SecureRandom nonceEntropySource) + { + this(helper, null, entropySource, nonceEntropySource); + } + + /** + * Base constructor. + * + * @param helper a JCA/JCE helper configured for the class's default provider. + * @param altHelper a JCA/JCE helper configured for the class's secondary provider (tried for private keys). + * @param entropySource primary entropy source, used for key generation. + * @param nonceEntropySource secondary entropy source, used for nonce and IV generation. + */ + protected JcaTlsCrypto(JcaJceHelper helper, JcaJceHelper altHelper, SecureRandom entropySource, SecureRandom nonceEntropySource) { this.helper = helper; + this.altHelper = altHelper; this.entropySource = entropySource; this.nonceEntropySource = nonceEntropySource; } @@ -111,7 +126,8 @@ JceTlsSecret adoptLocalSecret(byte[] data) return new JceTlsSecret(this, data); } - Cipher createRSAEncryptionCipher() throws GeneralSecurityException + Cipher createRSAEncryptionCipher() + throws GeneralSecurityException { try { @@ -326,7 +342,7 @@ public TlsSRP6Client createSRP6Client(TlsSRPConfig srpConfig) final SRP6Client srpClient = new SRP6Client(); BigInteger[] ng = srpConfig.getExplicitNG(); - SRP6Group srpGroup= new SRP6Group(ng[0], ng[1]); + SRP6Group srpGroup = new SRP6Group(ng[0], ng[1]); srpClient.init(srpGroup, createHash(CryptoHashAlgorithm.sha1), this.getSecureRandom()); return new TlsSRP6Client() @@ -355,7 +371,7 @@ public TlsSRP6Server createSRP6Server(TlsSRPConfig srpConfig, BigInteger srpVeri { final SRP6Server srpServer = new SRP6Server(); BigInteger[] ng = srpConfig.getExplicitNG(); - SRP6Group srpGroup= new SRP6Group(ng[0], ng[1]); + SRP6Group srpGroup = new SRP6Group(ng[0], ng[1]); srpServer.init(srpGroup, srpVerifier, createHash(CryptoHashAlgorithm.sha1), this.getSecureRandom()); return new TlsSRP6Server() { @@ -420,7 +436,8 @@ String getHMACAlgorithmName(int cryptoHashAlgorithm) } } - public AlgorithmParameters getNamedGroupAlgorithmParameters(int namedGroup) throws GeneralSecurityException + public AlgorithmParameters getNamedGroupAlgorithmParameters(int namedGroup) + throws GeneralSecurityException { if (NamedGroup.refersToAnXDHCurve(namedGroup)) { @@ -428,7 +445,7 @@ public AlgorithmParameters getNamedGroupAlgorithmParameters(int namedGroup) thro { /* * TODO Return AlgorithmParameters to check against disabled algorithms - * + * * NOTE: The JDK doesn't even support AlgorithmParameters for XDH, so SunJSSE also winds * up using null AlgorithmParameters when checking algorithm constraints. */ @@ -555,7 +572,7 @@ public boolean hasCryptoSignatureAlgorithm(int cryptoSignatureAlgorithm) case CryptoSignatureAlgorithm.gostr34102012_256: case CryptoSignatureAlgorithm.gostr34102012_512: - // TODO[RFC 8998] + // TODO[RFC 8998] case CryptoSignatureAlgorithm.sm2: default: @@ -737,7 +754,7 @@ public boolean hasSignatureAlgorithm(short signatureAlgorithm) case SignatureAlgorithm.gostr34102012_256: case SignatureAlgorithm.gostr34102012_512: - // TODO[RFC 8998] + // TODO[RFC 8998] // case SignatureAlgorithm.sm2: default: @@ -766,7 +783,7 @@ public boolean hasSignatureScheme(int signatureScheme) switch (signatureScheme) { case SignatureScheme.sm2sig_sm3: - // TODO[tls] Implement before adding + // TODO[tls] Implement before adding case SignatureScheme.DRAFT_mldsa44: case SignatureScheme.DRAFT_mldsa65: case SignatureScheme.DRAFT_mldsa87: @@ -775,7 +792,7 @@ public boolean hasSignatureScheme(int signatureScheme) { short signature = SignatureScheme.getSignatureAlgorithm(signatureScheme); - switch(SignatureScheme.getCryptoHashAlgorithm(signatureScheme)) + switch (SignatureScheme.getCryptoHashAlgorithm(signatureScheme)) { case CryptoHashAlgorithm.md5: return SignatureAlgorithm.rsa == signature && hasSignatureAlgorithm(signature); @@ -847,7 +864,7 @@ public TlsECDomain createECDomain(TlsECConfig ecConfig) return new JceTlsECDomain(this, ecConfig); } } - + public TlsKemDomain createKemDomain(TlsKemConfig kemConfig) { return new JceTlsMLKemDomain(this, kemConfig); @@ -885,7 +902,7 @@ protected TlsAEADCipherImpl createAEADCipher(String cipherName, String algorithm * @throws GeneralSecurityException in case of failure. */ protected TlsBlockCipherImpl createBlockCipher(String cipherName, String algorithm, int keySize, - boolean isEncrypting) + boolean isEncrypting) throws GeneralSecurityException { return new JceBlockCipherImpl(this, helper.createCipher(cipherName), algorithm, keySize, isEncrypting); @@ -902,7 +919,7 @@ protected TlsBlockCipherImpl createBlockCipher(String cipherName, String algorit * @throws GeneralSecurityException in case of failure. */ protected TlsBlockCipherImpl createBlockCipherWithCBCImplicitIV(String cipherName, String algorithm, int keySize, - boolean isEncrypting) + boolean isEncrypting) throws GeneralSecurityException { return new JceBlockCipherWithCBCImplicitIVImpl(this, helper.createCipher(cipherName), algorithm, isEncrypting); @@ -926,7 +943,7 @@ protected TlsHash createHash(String digestName) * * @param macAlgorithm the name of the algorithm supporting the MAC. * @return a null cipher suite implementation. - * @throws IOException in case of failure. + * @throws IOException in case of failure. * @throws GeneralSecurityException in case of a specific failure in the JCA/JCE layer. */ protected TlsNullCipher createNullCipher(TlsCryptoParameters cryptoParams, int macAlgorithm) @@ -937,7 +954,8 @@ protected TlsNullCipher createNullCipher(TlsCryptoParameters cryptoParams, int m } protected TlsStreamSigner createStreamSigner(SignatureAndHashAlgorithm algorithm, PrivateKey privateKey, - boolean needsRandom) throws IOException + boolean needsRandom) + throws IOException { String algorithmName = JcaUtils.getJcaAlgorithmName(algorithm); @@ -945,39 +963,22 @@ protected TlsStreamSigner createStreamSigner(SignatureAndHashAlgorithm algorithm } protected TlsStreamSigner createStreamSigner(String algorithmName, AlgorithmParameterSpec parameter, - PrivateKey privateKey, boolean needsRandom) throws IOException + PrivateKey privateKey, boolean needsRandom) + throws IOException { + SecureRandom random = needsRandom ? getSecureRandom() : null; + try { - SecureRandom random = needsRandom ? getSecureRandom() : null; - - JcaJceHelper helper = getHelper(); - try { - if (null != parameter) - { - Signature dummySigner = helper.createSignature(algorithmName); - dummySigner.initSign(privateKey, random); - helper = new ProviderJcaJceHelper(dummySigner.getProvider()); - } - - Signature signer = helper.createSignature(algorithmName); - if (null != parameter) - { - signer.setParameter(parameter); - } - signer.initSign(privateKey, random); - return new JcaTlsStreamSigner(signer); + return createStreamSigner(getHelper(), algorithmName, parameter, privateKey, random); } catch (InvalidKeyException e) { - String upperAlg = Strings.toUpperCase(algorithmName); - if (upperAlg.endsWith("MGF1")) + if (altHelper != null) { - // ANDMGF1 has vanished from the Sun PKCS11 provider. - algorithmName = upperAlg.replace("ANDMGF1", "SSA-PSS"); - return createStreamSigner(algorithmName, parameter, privateKey, needsRandom); + return createStreamSigner(altHelper, algorithmName, parameter, privateKey, random); } else { @@ -991,7 +992,66 @@ protected TlsStreamSigner createStreamSigner(String algorithmName, AlgorithmPara } } - protected TlsStreamVerifier createStreamVerifier(DigitallySigned digitallySigned, PublicKey publicKey) throws IOException + private TlsStreamSigner createStreamSigner(JcaJceHelper helper, String algorithmName, AlgorithmParameterSpec parameter, + PrivateKey privateKey, SecureRandom random) + throws GeneralSecurityException + { + try + { + if (null != parameter) + { + try + { + Signature dummySigner = helper.createSignature(algorithmName); + dummySigner.initSign(privateKey, random); + helper = new ProviderJcaJceHelper(dummySigner.getProvider()); + } + catch (NoSuchAlgorithmException e) + { + // more PKCS#11 mischief + String upperAlg = Strings.toUpperCase(algorithmName); + if (upperAlg.endsWith("MGF1")) + { + // ANDMGF1 has vanished from the Sun PKCS11 provider. + algorithmName = upperAlg.replace("ANDMGF1", "SSA-PSS"); + Signature dummySigner = helper.createSignature(algorithmName); + + dummySigner.initSign(privateKey, random); + helper = new ProviderJcaJceHelper(dummySigner.getProvider()); + } + else + { + throw e; + } + } + } + + Signature signer = helper.createSignature(algorithmName); + if (null != parameter) + { + signer.setParameter(parameter); + } + signer.initSign(privateKey, random); + return new JcaTlsStreamSigner(signer); + } + catch (InvalidKeyException e) + { + String upperAlg = Strings.toUpperCase(algorithmName); + if (upperAlg.endsWith("MGF1")) + { + // ANDMGF1 has vanished from the Sun PKCS11 provider. + algorithmName = upperAlg.replace("ANDMGF1", "SSA-PSS"); + return createStreamSigner(helper, algorithmName, parameter, privateKey, random); + } + else + { + throw e; + } + } + } + + protected TlsStreamVerifier createStreamVerifier(DigitallySigned digitallySigned, PublicKey publicKey) + throws IOException { String algorithmName = JcaUtils.getJcaAlgorithmName(digitallySigned.getAlgorithm()); @@ -999,7 +1059,8 @@ protected TlsStreamVerifier createStreamVerifier(DigitallySigned digitallySigned } protected TlsStreamVerifier createStreamVerifier(String algorithmName, AlgorithmParameterSpec parameter, - byte[] signature, PublicKey publicKey) throws IOException + byte[] signature, PublicKey publicKey) + throws IOException { try { @@ -1026,7 +1087,8 @@ protected TlsStreamVerifier createStreamVerifier(String algorithmName, Algorithm } protected Tls13Verifier createTls13Verifier(String algorithmName, AlgorithmParameterSpec parameter, - PublicKey publicKey) throws IOException + PublicKey publicKey) + throws IOException { try { @@ -1201,7 +1263,8 @@ public JcaJceHelper getHelper() } protected TlsBlockCipherImpl createCBCBlockCipherImpl(TlsCryptoParameters cryptoParams, String algorithm, - int cipherKeySize, boolean forEncryption) throws GeneralSecurityException + int cipherKeySize, boolean forEncryption) + throws GeneralSecurityException { String cipherName = algorithm + "/CBC/NoPadding"; @@ -1256,7 +1319,8 @@ private TlsAEADCipher createCipher_Camellia_GCM(TlsCryptoParameters cryptoParams } protected TlsCipher createCipher_CBC(TlsCryptoParameters cryptoParams, String algorithm, int cipherKeySize, - int macAlgorithm) throws GeneralSecurityException, IOException + int macAlgorithm) + throws GeneralSecurityException, IOException { TlsBlockCipherImpl encrypt = createCBCBlockCipherImpl(cryptoParams, algorithm, cipherKeySize, true); TlsBlockCipherImpl decrypt = createCBCBlockCipherImpl(cryptoParams, algorithm, cipherKeySize, false); diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCryptoProvider.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCryptoProvider.java index 8368e8682c..88200e247b 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCryptoProvider.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCryptoProvider.java @@ -20,6 +20,7 @@ public class JcaTlsCryptoProvider implements TlsCryptoProvider { private JcaJceHelper helper = new DefaultJcaJceHelper(); + private JcaJceHelper altHelper = helper; public JcaTlsCryptoProvider() { @@ -33,7 +34,21 @@ public JcaTlsCryptoProvider() */ public JcaTlsCryptoProvider setProvider(Provider provider) { - this.helper = new ProviderJcaJceHelper(provider); + this.helper = this.altHelper = new ProviderJcaJceHelper(provider); + + return this; + } + + /** + * Set the alternate provider of cryptographic services for any JcaTlsCrypto we build (usually points to a + * HSM). + * + * @param provider the provider class to source cryptographic services from. + * @return the current builder instance. + */ + public JcaTlsCryptoProvider setAlternateProvider(Provider provider) + { + this.altHelper = new ProviderJcaJceHelper(provider); return this; } @@ -46,7 +61,20 @@ public JcaTlsCryptoProvider setProvider(Provider provider) */ public JcaTlsCryptoProvider setProvider(String providerName) { - this.helper = new NamedJcaJceHelper(providerName); + this.helper = this.altHelper = new NamedJcaJceHelper(providerName); + + return this; + } + + /** + * Set the provider of cryptographic services for any JcaTlsCrypto we build by name (usually refers to a HSM). + * + * @param providerName the name of the provider class to source cryptographic services from. + * @return the current builder instance. + */ + public JcaTlsCryptoProvider setAlternateProvider(String providerName) + { + this.altHelper = new NamedJcaJceHelper(providerName); return this; } @@ -92,6 +120,11 @@ public JcaTlsCrypto create(SecureRandom random) */ public JcaTlsCrypto create(SecureRandom keyRandom, SecureRandom nonceRandom) { + if (helper != altHelper) + { + return new JcaTlsCrypto(getHelper(), altHelper, keyRandom, nonceRandom); + } + return new JcaTlsCrypto(getHelper(), keyRandom, nonceRandom); } From 7626743f7c193c9e1c37e6f4bad1ad53f0b63146 Mon Sep 17 00:00:00 2001 From: David Hook Date: Thu, 8 May 2025 23:49:01 +1000 Subject: [PATCH 346/890] added support for PKCS#11 alt provider for EC TLS 1.3 --- .../tls/crypto/impl/jcajce/JcaTlsCrypto.java | 5 +++++ .../crypto/impl/jcajce/JcaTlsECDSA13Signer.java | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index 5d5d04224a..b63158e884 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -1262,6 +1262,11 @@ public JcaJceHelper getHelper() return helper; } + public JcaJceHelper getAltHelper() + { + return altHelper; + } + protected TlsBlockCipherImpl createCBCBlockCipherImpl(TlsCryptoParameters cryptoParams, String algorithm, int cipherKeySize, boolean forEncryption) throws GeneralSecurityException diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsECDSA13Signer.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsECDSA13Signer.java index 6e97fd5b16..8954bd52bd 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsECDSA13Signer.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsECDSA13Signer.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; import java.security.PrivateKey; import java.security.Signature; @@ -57,6 +58,21 @@ public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] h signer.update(hash, 0, hash.length); return signer.sign(); } + catch (InvalidKeyException e) + { + // try with PKCS#11 (usually) alternative provider + try + { + Signature signer = crypto.getAltHelper().createSignature("NoneWithECDSA"); + signer.initSign(privateKey, crypto.getSecureRandom()); + signer.update(hash, 0, hash.length); + return signer.sign(); + } + catch (GeneralSecurityException ex) + { + throw new TlsFatalAlert(AlertDescription.internal_error, ex); + } + } catch (GeneralSecurityException e) { throw new TlsFatalAlert(AlertDescription.internal_error, e); From 8d0ff575fcfed40ac5249ba5fb22cb676b5b29bc Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 12 May 2025 13:54:12 +0700 Subject: [PATCH 347/890] Followup alt provider changes --- .../tls/crypto/impl/jcajce/JcaTlsCrypto.java | 64 ++++++++----------- .../impl/jcajce/JcaTlsCryptoProvider.java | 20 +++--- .../impl/jcajce/JcaTlsECDSA13Signer.java | 37 +++++++---- .../jsse/provider/test/FipsJcaTlsCrypto.java | 6 ++ .../test/FipsJcaTlsCryptoProvider.java | 2 +- 5 files changed, 68 insertions(+), 61 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index b63158e884..751112a211 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -113,7 +113,8 @@ protected JcaTlsCrypto(JcaJceHelper helper, SecureRandom entropySource, SecureRa * @param entropySource primary entropy source, used for key generation. * @param nonceEntropySource secondary entropy source, used for nonce and IV generation. */ - protected JcaTlsCrypto(JcaJceHelper helper, JcaJceHelper altHelper, SecureRandom entropySource, SecureRandom nonceEntropySource) + protected JcaTlsCrypto(JcaJceHelper helper, JcaJceHelper altHelper, SecureRandom entropySource, + SecureRandom nonceEntropySource) { this.helper = helper; this.altHelper = altHelper; @@ -572,7 +573,7 @@ public boolean hasCryptoSignatureAlgorithm(int cryptoSignatureAlgorithm) case CryptoSignatureAlgorithm.gostr34102012_256: case CryptoSignatureAlgorithm.gostr34102012_512: - // TODO[RFC 8998] + // TODO[RFC 8998] case CryptoSignatureAlgorithm.sm2: default: @@ -754,7 +755,7 @@ public boolean hasSignatureAlgorithm(short signatureAlgorithm) case SignatureAlgorithm.gostr34102012_256: case SignatureAlgorithm.gostr34102012_512: - // TODO[RFC 8998] + // TODO[RFC 8998] // case SignatureAlgorithm.sm2: default: @@ -783,7 +784,7 @@ public boolean hasSignatureScheme(int signatureScheme) switch (signatureScheme) { case SignatureScheme.sm2sig_sm3: - // TODO[tls] Implement before adding + // TODO[tls] Implement before adding case SignatureScheme.DRAFT_mldsa44: case SignatureScheme.DRAFT_mldsa65: case SignatureScheme.DRAFT_mldsa87: @@ -902,8 +903,7 @@ protected TlsAEADCipherImpl createAEADCipher(String cipherName, String algorithm * @throws GeneralSecurityException in case of failure. */ protected TlsBlockCipherImpl createBlockCipher(String cipherName, String algorithm, int keySize, - boolean isEncrypting) - throws GeneralSecurityException + boolean isEncrypting) throws GeneralSecurityException { return new JceBlockCipherImpl(this, helper.createCipher(cipherName), algorithm, keySize, isEncrypting); } @@ -919,8 +919,7 @@ protected TlsBlockCipherImpl createBlockCipher(String cipherName, String algorit * @throws GeneralSecurityException in case of failure. */ protected TlsBlockCipherImpl createBlockCipherWithCBCImplicitIV(String cipherName, String algorithm, int keySize, - boolean isEncrypting) - throws GeneralSecurityException + boolean isEncrypting) throws GeneralSecurityException { return new JceBlockCipherWithCBCImplicitIVImpl(this, helper.createCipher(cipherName), algorithm, isEncrypting); } @@ -954,8 +953,7 @@ protected TlsNullCipher createNullCipher(TlsCryptoParameters cryptoParams, int m } protected TlsStreamSigner createStreamSigner(SignatureAndHashAlgorithm algorithm, PrivateKey privateKey, - boolean needsRandom) - throws IOException + boolean needsRandom) throws IOException { String algorithmName = JcaUtils.getJcaAlgorithmName(algorithm); @@ -963,8 +961,7 @@ protected TlsStreamSigner createStreamSigner(SignatureAndHashAlgorithm algorithm } protected TlsStreamSigner createStreamSigner(String algorithmName, AlgorithmParameterSpec parameter, - PrivateKey privateKey, boolean needsRandom) - throws IOException + PrivateKey privateKey, boolean needsRandom) throws IOException { SecureRandom random = needsRandom ? getSecureRandom() : null; @@ -976,14 +973,13 @@ protected TlsStreamSigner createStreamSigner(String algorithmName, AlgorithmPara } catch (InvalidKeyException e) { - if (altHelper != null) - { - return createStreamSigner(altHelper, algorithmName, parameter, privateKey, random); - } - else + JcaJceHelper altHelper = getAltHelper(); + if (altHelper == null) { throw e; } + + return createStreamSigner(altHelper, algorithmName, parameter, privateKey, random); } } catch (GeneralSecurityException e) @@ -992,38 +988,36 @@ protected TlsStreamSigner createStreamSigner(String algorithmName, AlgorithmPara } } - private TlsStreamSigner createStreamSigner(JcaJceHelper helper, String algorithmName, AlgorithmParameterSpec parameter, - PrivateKey privateKey, SecureRandom random) - throws GeneralSecurityException + protected TlsStreamSigner createStreamSigner(JcaJceHelper helper, String algorithmName, + AlgorithmParameterSpec parameter, PrivateKey privateKey, SecureRandom random) throws GeneralSecurityException { try { if (null != parameter) { + Signature dummySigner; try { - Signature dummySigner = helper.createSignature(algorithmName); - dummySigner.initSign(privateKey, random); - helper = new ProviderJcaJceHelper(dummySigner.getProvider()); + dummySigner = helper.createSignature(algorithmName); } catch (NoSuchAlgorithmException e) { // more PKCS#11 mischief String upperAlg = Strings.toUpperCase(algorithmName); - if (upperAlg.endsWith("MGF1")) + if (upperAlg.endsWith("ANDMGF1")) { // ANDMGF1 has vanished from the Sun PKCS11 provider. algorithmName = upperAlg.replace("ANDMGF1", "SSA-PSS"); - Signature dummySigner = helper.createSignature(algorithmName); - - dummySigner.initSign(privateKey, random); - helper = new ProviderJcaJceHelper(dummySigner.getProvider()); + dummySigner = helper.createSignature(algorithmName); } else { throw e; } } + + dummySigner.initSign(privateKey, random); + helper = new ProviderJcaJceHelper(dummySigner.getProvider()); } Signature signer = helper.createSignature(algorithmName); @@ -1037,7 +1031,7 @@ private TlsStreamSigner createStreamSigner(JcaJceHelper helper, String algorithm catch (InvalidKeyException e) { String upperAlg = Strings.toUpperCase(algorithmName); - if (upperAlg.endsWith("MGF1")) + if (upperAlg.endsWith("ANDMGF1")) { // ANDMGF1 has vanished from the Sun PKCS11 provider. algorithmName = upperAlg.replace("ANDMGF1", "SSA-PSS"); @@ -1059,8 +1053,7 @@ protected TlsStreamVerifier createStreamVerifier(DigitallySigned digitallySigned } protected TlsStreamVerifier createStreamVerifier(String algorithmName, AlgorithmParameterSpec parameter, - byte[] signature, PublicKey publicKey) - throws IOException + byte[] signature, PublicKey publicKey) throws IOException { try { @@ -1087,8 +1080,7 @@ protected TlsStreamVerifier createStreamVerifier(String algorithmName, Algorithm } protected Tls13Verifier createTls13Verifier(String algorithmName, AlgorithmParameterSpec parameter, - PublicKey publicKey) - throws IOException + PublicKey publicKey) throws IOException { try { @@ -1268,8 +1260,7 @@ public JcaJceHelper getAltHelper() } protected TlsBlockCipherImpl createCBCBlockCipherImpl(TlsCryptoParameters cryptoParams, String algorithm, - int cipherKeySize, boolean forEncryption) - throws GeneralSecurityException + int cipherKeySize, boolean forEncryption) throws GeneralSecurityException { String cipherName = algorithm + "/CBC/NoPadding"; @@ -1324,8 +1315,7 @@ private TlsAEADCipher createCipher_Camellia_GCM(TlsCryptoParameters cryptoParams } protected TlsCipher createCipher_CBC(TlsCryptoParameters cryptoParams, String algorithm, int cipherKeySize, - int macAlgorithm) - throws GeneralSecurityException, IOException + int macAlgorithm) throws GeneralSecurityException, IOException { TlsBlockCipherImpl encrypt = createCBCBlockCipherImpl(cryptoParams, algorithm, cipherKeySize, true); TlsBlockCipherImpl decrypt = createCBCBlockCipherImpl(cryptoParams, algorithm, cipherKeySize, false); diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCryptoProvider.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCryptoProvider.java index 88200e247b..9b5d81f364 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCryptoProvider.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCryptoProvider.java @@ -20,7 +20,7 @@ public class JcaTlsCryptoProvider implements TlsCryptoProvider { private JcaJceHelper helper = new DefaultJcaJceHelper(); - private JcaJceHelper altHelper = helper; + private JcaJceHelper altHelper = null; public JcaTlsCryptoProvider() { @@ -34,7 +34,8 @@ public JcaTlsCryptoProvider() */ public JcaTlsCryptoProvider setProvider(Provider provider) { - this.helper = this.altHelper = new ProviderJcaJceHelper(provider); + this.helper = new ProviderJcaJceHelper(provider); + this.altHelper = null; return this; } @@ -61,7 +62,8 @@ public JcaTlsCryptoProvider setAlternateProvider(Provider provider) */ public JcaTlsCryptoProvider setProvider(String providerName) { - this.helper = this.altHelper = new NamedJcaJceHelper(providerName); + this.helper = new NamedJcaJceHelper(providerName); + this.altHelper = null; return this; } @@ -120,12 +122,7 @@ public JcaTlsCrypto create(SecureRandom random) */ public JcaTlsCrypto create(SecureRandom keyRandom, SecureRandom nonceRandom) { - if (helper != altHelper) - { - return new JcaTlsCrypto(getHelper(), altHelper, keyRandom, nonceRandom); - } - - return new JcaTlsCrypto(getHelper(), keyRandom, nonceRandom); + return new JcaTlsCrypto(getHelper(), getAltHelper(), keyRandom, nonceRandom); } public JcaJceHelper getHelper() @@ -133,6 +130,11 @@ public JcaJceHelper getHelper() return helper; } + public JcaJceHelper getAltHelper() + { + return altHelper; + } + @SuppressWarnings("serial") private static class NonceEntropySource extends SecureRandom diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsECDSA13Signer.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsECDSA13Signer.java index 8954bd52bd..804a8932dc 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsECDSA13Signer.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsECDSA13Signer.java @@ -4,8 +4,10 @@ import java.security.GeneralSecurityException; import java.security.InvalidKeyException; import java.security.PrivateKey; +import java.security.SecureRandom; import java.security.Signature; +import org.bouncycastle.jcajce.util.JcaJceHelper; import org.bouncycastle.tls.AlertDescription; import org.bouncycastle.tls.SignatureAndHashAlgorithm; import org.bouncycastle.tls.SignatureScheme; @@ -51,26 +53,24 @@ public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] h throw new IllegalStateException("Invalid algorithm: " + algorithm); } + SecureRandom random = crypto.getSecureRandom(); + try { - Signature signer = crypto.getHelper().createSignature("NoneWithECDSA"); - signer.initSign(privateKey, crypto.getSecureRandom()); - signer.update(hash, 0, hash.length); - return signer.sign(); - } - catch (InvalidKeyException e) - { - // try with PKCS#11 (usually) alternative provider try { - Signature signer = crypto.getAltHelper().createSignature("NoneWithECDSA"); - signer.initSign(privateKey, crypto.getSecureRandom()); - signer.update(hash, 0, hash.length); - return signer.sign(); + return implGenerateRawSignature(crypto.getHelper(), privateKey, random, hash); } - catch (GeneralSecurityException ex) + catch (InvalidKeyException e) { - throw new TlsFatalAlert(AlertDescription.internal_error, ex); + // try with PKCS#11 (usually) alternative provider + JcaJceHelper altHelper = crypto.getAltHelper(); + if (altHelper == null) + { + throw e; + } + + return implGenerateRawSignature(altHelper, privateKey, random, hash); } } catch (GeneralSecurityException e) @@ -84,4 +84,13 @@ public TlsStreamSigner getStreamSigner(SignatureAndHashAlgorithm algorithm) { return null; } + + private static byte[] implGenerateRawSignature(JcaJceHelper helper, PrivateKey privateKey, SecureRandom random, + byte[] hash) throws GeneralSecurityException + { + Signature signer = helper.createSignature("NoneWithECDSA"); + signer.initSign(privateKey, random); + signer.update(hash, 0, hash.length); + return signer.sign(); + } } diff --git a/tls/src/test/java/org/bouncycastle/jsse/provider/test/FipsJcaTlsCrypto.java b/tls/src/test/java/org/bouncycastle/jsse/provider/test/FipsJcaTlsCrypto.java index a7fceeb31f..a7b1a4fef4 100644 --- a/tls/src/test/java/org/bouncycastle/jsse/provider/test/FipsJcaTlsCrypto.java +++ b/tls/src/test/java/org/bouncycastle/jsse/provider/test/FipsJcaTlsCrypto.java @@ -14,6 +14,12 @@ public FipsJcaTlsCrypto(JcaJceHelper helper, SecureRandom entropySource, SecureR super(helper, entropySource, nonceEntropySource); } + public FipsJcaTlsCrypto(JcaJceHelper helper, JcaJceHelper altHelper, SecureRandom entropySource, + SecureRandom nonceEntropySource) + { + super(helper, altHelper, entropySource, nonceEntropySource); + } + @Override public AEADNonceGeneratorFactory getFipsGCMNonceGeneratorFactory() { diff --git a/tls/src/test/java/org/bouncycastle/jsse/provider/test/FipsJcaTlsCryptoProvider.java b/tls/src/test/java/org/bouncycastle/jsse/provider/test/FipsJcaTlsCryptoProvider.java index 8bb004e806..4781f119db 100644 --- a/tls/src/test/java/org/bouncycastle/jsse/provider/test/FipsJcaTlsCryptoProvider.java +++ b/tls/src/test/java/org/bouncycastle/jsse/provider/test/FipsJcaTlsCryptoProvider.java @@ -10,6 +10,6 @@ public class FipsJcaTlsCryptoProvider extends JcaTlsCryptoProvider @Override public JcaTlsCrypto create(SecureRandom keyRandom, SecureRandom nonceRandom) { - return new FipsJcaTlsCrypto(getHelper(), keyRandom, nonceRandom); + return new FipsJcaTlsCrypto(getHelper(), getAltHelper(), keyRandom, nonceRandom); } } From a35bbd294a0b8f069841e86e643a643f7a600cae Mon Sep 17 00:00:00 2001 From: royb Date: Mon, 12 May 2025 15:51:20 -0400 Subject: [PATCH 348/890] replaced internal ML-KEM usage in JcaTlsCrypto with JCE providers --- .../bouncycastle/tls/crypto/TlsKemConfig.java | 16 +++ .../tls/crypto/impl/jcajce/JcaTlsCrypto.java | 1 + .../tls/crypto/impl/jcajce/JceTlsMLKem.java | 22 ++-- .../crypto/impl/jcajce/JceTlsMLKemDomain.java | 123 ++++++++++++++++-- .../tls/crypto/impl/jcajce/KemUtil.java | 28 +++- .../tls/test/BcTlsProtocolKemTest.java | 12 ++ .../tls/test/JcaTlsProtocolKemTest.java | 15 +++ .../tls/test/MockTlsKemClient.java | 7 +- .../tls/test/MockTlsKemServer.java | 9 +- .../tls/test/TlsProtocolKemTest.java | 23 +++- 10 files changed, 217 insertions(+), 39 deletions(-) create mode 100644 tls/src/test/java/org/bouncycastle/tls/test/BcTlsProtocolKemTest.java create mode 100644 tls/src/test/java/org/bouncycastle/tls/test/JcaTlsProtocolKemTest.java diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/TlsKemConfig.java b/tls/src/main/java/org/bouncycastle/tls/crypto/TlsKemConfig.java index d064bc8d68..27d09d3b25 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/TlsKemConfig.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/TlsKemConfig.java @@ -1,7 +1,11 @@ package org.bouncycastle.tls.crypto; +import org.bouncycastle.jcajce.spec.KEMParameterSpec; +import org.bouncycastle.jcajce.spec.KTSParameterSpec; + public class TlsKemConfig { + protected final KTSParameterSpec ktsParameterSpec; protected final int namedGroup; protected final boolean isServer; @@ -9,6 +13,13 @@ public TlsKemConfig(int namedGroup, boolean isServer) { this.namedGroup = namedGroup; this.isServer = isServer; + this.ktsParameterSpec = new KTSParameterSpec.Builder("AES-KWP", 256).withNoKdf().build(); + } + public TlsKemConfig(int namedGroup, boolean isServer, KTSParameterSpec ktsParameterSpec) + { + this.namedGroup = namedGroup; + this.isServer = isServer; + this.ktsParameterSpec = ktsParameterSpec; } public int getNamedGroup() @@ -20,4 +31,9 @@ public boolean isServer() { return isServer; } + + public KTSParameterSpec getKtsParameterSpec() + { + return ktsParameterSpec; + } } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index b63158e884..981c76cd4c 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -464,6 +464,7 @@ else if (NamedGroup.refersToASpecificFiniteField(namedGroup)) } else if (NamedGroup.refersToASpecificKem(namedGroup)) { + //Note: There is no AlgorithmParametersSpi for ML-KEM return KemUtil.getAlgorithmParameters(this, NamedGroup.getKemName(namedGroup)); } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKem.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKem.java index 44f73f2bbb..319651c306 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKem.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKem.java @@ -1,11 +1,11 @@ package org.bouncycastle.tls.crypto.impl.jcajce; import java.io.IOException; +import java.security.KeyPair; -import org.bouncycastle.crypto.AsymmetricCipherKeyPair; -import org.bouncycastle.crypto.SecretWithEncapsulation; -import org.bouncycastle.pqc.crypto.mlkem.MLKEMPrivateKeyParameters; -import org.bouncycastle.pqc.crypto.mlkem.MLKEMPublicKeyParameters; +import org.bouncycastle.jcajce.SecretKeyWithEncapsulation; +import org.bouncycastle.jcajce.provider.asymmetric.mlkem.BCMLKEMPrivateKey; +import org.bouncycastle.jcajce.provider.asymmetric.mlkem.BCMLKEMPublicKey; import org.bouncycastle.tls.crypto.TlsAgreement; import org.bouncycastle.tls.crypto.TlsSecret; @@ -13,8 +13,8 @@ public class JceTlsMLKem implements TlsAgreement { protected final JceTlsMLKemDomain domain; - protected MLKEMPrivateKeyParameters privateKey; - protected MLKEMPublicKeyParameters publicKey; + protected BCMLKEMPrivateKey privateKey; + protected BCMLKEMPublicKey publicKey; protected TlsSecret secret; public JceTlsMLKem(JceTlsMLKemDomain domain) @@ -26,16 +26,16 @@ public byte[] generateEphemeral() throws IOException { if (domain.isServer()) { - SecretWithEncapsulation encap = domain.encapsulate(publicKey); + SecretKeyWithEncapsulation encap = domain.encapsulate(publicKey); this.publicKey = null; - this.secret = domain.adoptLocalSecret(encap.getSecret()); + this.secret = domain.adoptLocalSecret(encap.getEncoded()); return encap.getEncapsulation(); } else { - AsymmetricCipherKeyPair kp = domain.generateKeyPair(); - this.privateKey = (MLKEMPrivateKeyParameters)kp.getPrivate(); - return domain.encodePublicKey((MLKEMPublicKeyParameters)kp.getPublic()); + KeyPair kp = domain.generateKeyPair(); + this.privateKey = (BCMLKEMPrivateKey)kp.getPrivate(); + return ((BCMLKEMPublicKey)kp.getPublic()).getPublicData(); } } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java index 5aaf97b7db..b34b5cb947 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java @@ -2,10 +2,17 @@ import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.SecretWithEncapsulation; +import org.bouncycastle.jcajce.SecretKeyWithEncapsulation; +import org.bouncycastle.jcajce.provider.asymmetric.mlkem.BCMLKEMPrivateKey; +import org.bouncycastle.jcajce.provider.asymmetric.mlkem.BCMLKEMPublicKey; +import org.bouncycastle.jcajce.spec.KEMExtractSpec; +import org.bouncycastle.jcajce.spec.KEMGenerateSpec; +import org.bouncycastle.jcajce.spec.KEMParameterSpec; +import org.bouncycastle.jcajce.spec.KTSParameterSpec; +import org.bouncycastle.jcajce.spec.MLKEMParameterSpec; import org.bouncycastle.pqc.crypto.mlkem.MLKEMExtractor; import org.bouncycastle.pqc.crypto.mlkem.MLKEMGenerator; import org.bouncycastle.pqc.crypto.mlkem.MLKEMKeyGenerationParameters; -import org.bouncycastle.pqc.crypto.mlkem.MLKEMKeyPairGenerator; import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMPrivateKeyParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMPublicKeyParameters; @@ -13,6 +20,23 @@ import org.bouncycastle.tls.crypto.TlsAgreement; import org.bouncycastle.tls.crypto.TlsKemConfig; import org.bouncycastle.tls.crypto.TlsKemDomain; +import org.bouncycastle.util.encoders.Hex; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.Key; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; public class JceTlsMLKemDomain implements TlsKemDomain { @@ -38,6 +62,10 @@ public static MLKEMParameters getDomainParameters(TlsKemConfig kemConfig) protected final TlsKemConfig config; protected final MLKEMParameters domainParameters; protected final boolean isServer; + protected KeyGenerator keyGen; +// protected KeyPairGenerator kpg; +// protected Cipher cipher; + public JceTlsMLKemDomain(JcaTlsCrypto crypto, TlsKemConfig kemConfig) { @@ -45,6 +73,18 @@ public JceTlsMLKemDomain(JcaTlsCrypto crypto, TlsKemConfig kemConfig) this.config = kemConfig; this.domainParameters = getDomainParameters(kemConfig); this.isServer = kemConfig.isServer(); + try + { + this.keyGen = keyGen = crypto.getHelper().createKeyGenerator(domainParameters.getName()); + } + catch (NoSuchAlgorithmException e) + { + throw new RuntimeException(e); + } + catch (NoSuchProviderException e) + { + throw new RuntimeException(e); + } } public JceTlsSecret adoptLocalSecret(byte[] secret) @@ -57,22 +97,42 @@ public TlsAgreement createKem() return new JceTlsMLKem(this); } - public JceTlsSecret decapsulate(MLKEMPrivateKeyParameters privateKey, byte[] ciphertext) + public JceTlsSecret decapsulate(PrivateKey privateKey, byte[] ciphertext) { - MLKEMExtractor kemExtract = new MLKEMExtractor(privateKey); - byte[] secret = kemExtract.extractSecret(ciphertext); - return adoptLocalSecret(secret); + try + { + keyGen.init(new KEMExtractSpec.Builder(privateKey, ciphertext, "DEF", 256).withNoKdf().build()); + SecretKeyWithEncapsulation secEnc = (SecretKeyWithEncapsulation)keyGen.generateKey(); + + return adoptLocalSecret(secEnc.getEncoded()); + } + catch (Exception e) + { + throw Exceptions.illegalArgumentException("invalid key: " + e.getMessage(), e); + } + + +// MLKEMExtractor kemExtract = new MLKEMExtractor(privateKey); +// byte[] secret = kemExtract.extractSecret(ciphertext); +// return adoptLocalSecret(secret); } - public MLKEMPublicKeyParameters decodePublicKey(byte[] encoding) + public BCMLKEMPublicKey decodePublicKey(byte[] encoding) { - return new MLKEMPublicKeyParameters(domainParameters, encoding); + return new BCMLKEMPublicKey(new MLKEMPublicKeyParameters(domainParameters, encoding)); } - public SecretWithEncapsulation encapsulate(MLKEMPublicKeyParameters publicKey) + public SecretKeyWithEncapsulation encapsulate(PublicKey publicKey) { - MLKEMGenerator kemGen = new MLKEMGenerator(crypto.getSecureRandom()); - return kemGen.generateEncapsulated(publicKey); + try + { + keyGen.init(new KEMGenerateSpec.Builder(publicKey, "DEF", 256).withNoKdf().build()); + return (SecretKeyWithEncapsulation)keyGen.generateKey(); + } + catch (Exception e) + { + throw Exceptions.illegalArgumentException("invalid key: " + e.getMessage(), e); + } } public byte[] encodePublicKey(MLKEMPublicKeyParameters publicKey) @@ -80,11 +140,46 @@ public byte[] encodePublicKey(MLKEMPublicKeyParameters publicKey) return publicKey.getEncoded(); } - public AsymmetricCipherKeyPair generateKeyPair() + private void init() + { +// try +// { +//// kpg = KeyPairGenerator.getInstance("MLKEM"); +//// kpg.initialize(MLKEMParameterSpec.fromName(domainParameters.getName()), crypto.getSecureRandom()); +//// keyGen = KeyGenerator.getInstance(domainParameters.getName(), "BC"); +// +//// cipher = KemUtil.getCipher(crypto, domainParameters.getName()); +// +// +// } +// catch (GeneralSecurityException e) +// { +// throw Exceptions.illegalStateException("unable to create key pair: " + e.getMessage(), e); +// } + + + } + public KeyPair generateKeyPair() { - MLKEMKeyPairGenerator keyPairGenerator = new MLKEMKeyPairGenerator(); - keyPairGenerator.init(new MLKEMKeyGenerationParameters(crypto.getSecureRandom(), domainParameters)); - return keyPairGenerator.generateKeyPair(); +// AlgorithmParameters params = KemUtil.getAlgorithmParameters(crypto, domainParameters.getName()); +// if (params == null) +// { +// throw new IllegalStateException("KEM parameters unavailable"); +// } + KeyPairGenerator kpg = null; + try + { + kpg = crypto.getHelper().createKeyPairGenerator(domainParameters.getName()); + } + catch (NoSuchAlgorithmException e) + { + throw new RuntimeException(e); + } + catch (NoSuchProviderException e) + { + throw new RuntimeException(e); + } + return kpg.generateKeyPair(); } public boolean isServer() diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java index 0d4e1872ff..f7fb9506d3 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java @@ -1,6 +1,9 @@ package org.bouncycastle.tls.crypto.impl.jcajce; +import org.bouncycastle.jcajce.spec.MLKEMParameterSpec; + import java.security.AlgorithmParameters; +import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; @@ -10,7 +13,11 @@ static AlgorithmParameters getAlgorithmParameters(JcaTlsCrypto crypto, String ke { try { - // TODO[tls-kem] Return AlgorithmParameters to check against disabled algorithms? + return null; +// AlgorithmParameters algParams = AlgorithmParameters.getInstance(kemName, "BC"); +// MLKEMParameterSpec mlkemSpec = MLKEMParameterSpec.fromName(kemName); +// algParams.init(mlkemSpec); +// return algParams; } catch (AssertionError e) { @@ -33,6 +40,7 @@ static Cipher getCipher(JcaTlsCrypto crypto, String kemName) } catch (Exception e) { + throw new IllegalStateException("KEM cipher failed: " + kemName, e); } return null; @@ -41,7 +49,21 @@ static Cipher getCipher(JcaTlsCrypto crypto, String kemName) static boolean isKemSupported(JcaTlsCrypto crypto, String kemName) { // TODO[tls-kem] When implemented via provider, need to check for support dynamically -// return kemName != null && getCipher(crypto, kemName) != null; - return true; + return kemName != null && getCipher(crypto, kemName) != null; + } + + static int getEncapsulationLength(String kemName) + { + switch (kemName) + { + case "ML-KEM-512": + return 768; + case "ML-KEM-768": + return 1088; + case "ML-KEM-1024": + return 1568; + default: + throw new IllegalArgumentException("unknown kem name " + kemName); + } } } diff --git a/tls/src/test/java/org/bouncycastle/tls/test/BcTlsProtocolKemTest.java b/tls/src/test/java/org/bouncycastle/tls/test/BcTlsProtocolKemTest.java new file mode 100644 index 0000000000..e99d8d01e3 --- /dev/null +++ b/tls/src/test/java/org/bouncycastle/tls/test/BcTlsProtocolKemTest.java @@ -0,0 +1,12 @@ +package org.bouncycastle.tls.test; + +import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto; + +public class BcTlsProtocolKemTest + extends TlsProtocolKemTest +{ + public BcTlsProtocolKemTest() + { + super(new BcTlsCrypto()); + } +} diff --git a/tls/src/test/java/org/bouncycastle/tls/test/JcaTlsProtocolKemTest.java b/tls/src/test/java/org/bouncycastle/tls/test/JcaTlsProtocolKemTest.java new file mode 100644 index 0000000000..ae23d5a57f --- /dev/null +++ b/tls/src/test/java/org/bouncycastle/tls/test/JcaTlsProtocolKemTest.java @@ -0,0 +1,15 @@ +package org.bouncycastle.tls.test; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider; + +import java.security.SecureRandom; + +public class JcaTlsProtocolKemTest + extends TlsProtocolKemTest +{ + public JcaTlsProtocolKemTest() + { + super(new JcaTlsCryptoProvider().setProvider(new BouncyCastleProvider()).create(new SecureRandom())); + } +} diff --git a/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemClient.java b/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemClient.java index 6e799c9511..c40ba27195 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemClient.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemClient.java @@ -2,10 +2,12 @@ import java.io.IOException; import java.io.PrintStream; +import java.security.SecureRandom; import java.util.Hashtable; import java.util.Vector; import org.bouncycastle.asn1.x509.Certificate; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.tls.AlertDescription; import org.bouncycastle.tls.AlertLevel; import org.bouncycastle.tls.CertificateRequest; @@ -28,6 +30,7 @@ import org.bouncycastle.tls.crypto.TlsCertificate; import org.bouncycastle.tls.crypto.TlsCrypto; import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Integers; import org.bouncycastle.util.encoders.Hex; @@ -44,9 +47,9 @@ class MockTlsKemClient NamedGroup.MLKEM1024, }; - MockTlsKemClient(TlsSession session) + MockTlsKemClient(TlsCrypto crypto, TlsSession session) { - super(new BcTlsCrypto()); + super(crypto); this.session = session; } diff --git a/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemServer.java b/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemServer.java index 5a41afd9fe..6b790fc844 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemServer.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemServer.java @@ -2,11 +2,13 @@ import java.io.IOException; import java.io.PrintStream; +import java.security.SecureRandom; import java.util.Hashtable; import java.util.Vector; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.Certificate; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.tls.AlertDescription; import org.bouncycastle.tls.AlertLevel; import org.bouncycastle.tls.CertificateRequest; @@ -23,7 +25,10 @@ import org.bouncycastle.tls.TlsFatalAlert; import org.bouncycastle.tls.TlsUtils; import org.bouncycastle.tls.crypto.TlsCertificate; +import org.bouncycastle.tls.crypto.TlsCrypto; import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider; import org.bouncycastle.util.encoders.Hex; class MockTlsKemServer @@ -37,9 +42,9 @@ class MockTlsKemServer NamedGroup.x25519, }; - MockTlsKemServer() + MockTlsKemServer(TlsCrypto crypto) { - super(new BcTlsCrypto()); + super(crypto); } protected Vector getProtocolNames() diff --git a/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolKemTest.java b/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolKemTest.java index 2f0d8dc9d5..caa01ee5f4 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolKemTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolKemTest.java @@ -8,14 +8,21 @@ import org.bouncycastle.tls.NamedGroup; import org.bouncycastle.tls.TlsClientProtocol; import org.bouncycastle.tls.TlsServerProtocol; +import org.bouncycastle.tls.crypto.TlsCrypto; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.io.Streams; import junit.framework.TestCase; -public class TlsProtocolKemTest +public abstract class TlsProtocolKemTest extends TestCase { + protected final TlsCrypto crypto; + + protected TlsProtocolKemTest(TlsCrypto crypto) + { + this.crypto = crypto; + } // mismatched ML-KEM strengths w/o classical crypto public void testMismatchStrength() throws Exception { @@ -27,7 +34,7 @@ public void testMismatchStrength() throws Exception TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); - ServerThread serverThread = new ServerThread(serverProtocol, new int[]{ NamedGroup.MLKEM768 }, true); + ServerThread serverThread = new ServerThread(crypto, serverProtocol, new int[]{ NamedGroup.MLKEM768 }, true); try { serverThread.start(); @@ -35,7 +42,7 @@ public void testMismatchStrength() throws Exception catch (Exception ignored) { } - MockTlsKemClient client = new MockTlsKemClient(null); + MockTlsKemClient client = new MockTlsKemClient(crypto, null); client.setNamedGroups(new int[]{ NamedGroup.MLKEM512 }); try { @@ -59,10 +66,10 @@ public void testClientServer() throws Exception TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); - ServerThread serverThread = new ServerThread(serverProtocol, null, false); + ServerThread serverThread = new ServerThread(crypto, serverProtocol, null, false); serverThread.start(); - MockTlsKemClient client = new MockTlsKemClient(null); + MockTlsKemClient client = new MockTlsKemClient(crypto, null); clientProtocol.connect(client); // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity @@ -88,12 +95,14 @@ public void testClientServer() throws Exception static class ServerThread extends Thread { + private final TlsCrypto crypto; private final TlsServerProtocol serverProtocol; private final int[] namedGroups; private boolean shouldFail = false; - ServerThread(TlsServerProtocol serverProtocol, int[] namedGroups, boolean shouldFail) + ServerThread(TlsCrypto crypto, TlsServerProtocol serverProtocol, int[] namedGroups, boolean shouldFail) { + this.crypto = crypto; this.serverProtocol = serverProtocol; this.namedGroups = namedGroups; this.shouldFail = shouldFail; @@ -103,7 +112,7 @@ public void run() { try { - MockTlsKemServer server = new MockTlsKemServer(); + MockTlsKemServer server = new MockTlsKemServer(crypto); if (namedGroups != null) { server.setNamedGroups(namedGroups); From 7f1ca8bb8b25e31caf6cb1888f7429c724304e56 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 13 May 2025 18:28:39 +0700 Subject: [PATCH 349/890] Fix potential NPE --- .../bouncycastle/pqc/jcajce/provider/util/KdfUtil.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/KdfUtil.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/KdfUtil.java index f3c30e7163..56ec3ba431 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/KdfUtil.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/util/KdfUtil.java @@ -34,7 +34,7 @@ public class KdfUtil */ public static byte[] makeKeyBytes(KEMKDFSpec kdfSpec, byte[] secret) { - byte[] keyBytes = null; + byte[] keyBytes; try { if (kdfSpec == null) @@ -42,8 +42,11 @@ public static byte[] makeKeyBytes(KEMKDFSpec kdfSpec, byte[] secret) keyBytes = new byte[secret.length]; System.arraycopy(secret, 0, keyBytes, 0, keyBytes.length); } - - keyBytes = makeKeyBytes(kdfSpec.getKdfAlgorithm(), secret, kdfSpec.getOtherInfo(), kdfSpec.getKeySize()); + else + { + keyBytes = makeKeyBytes(kdfSpec.getKdfAlgorithm(), secret, kdfSpec.getOtherInfo(), + kdfSpec.getKeySize()); + } } finally { From af941f3bb5c8aafff354c633c1a820b62c5a30e2 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 13 May 2025 20:28:54 +0700 Subject: [PATCH 350/890] Cleanup tls.crypto domain classes --- .../tls/crypto/impl/bc/BcTlsDHDomain.java | 14 +++++++------- .../tls/crypto/impl/bc/BcTlsECDomain.java | 2 -- .../tls/crypto/impl/bc/BcTlsMLKemDomain.java | 2 -- .../tls/crypto/impl/jcajce/JceTlsDHDomain.java | 10 +++++----- .../tls/crypto/impl/jcajce/JceTlsECDomain.java | 2 -- 5 files changed, 12 insertions(+), 18 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsDHDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsDHDomain.java index 9a66fa2e97..34567de251 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsDHDomain.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsDHDomain.java @@ -57,20 +57,20 @@ public static DHParameters getDomainParameters(TlsDHConfig dhConfig) return new DHParameters(dhGroup.getP(), dhGroup.getG(), dhGroup.getQ(), dhGroup.getL()); } - protected BcTlsCrypto crypto; - protected TlsDHConfig config; - protected DHParameters domainParameters; + protected final BcTlsCrypto crypto; + protected final DHParameters domainParameters; + protected final boolean isPadded; public BcTlsDHDomain(BcTlsCrypto crypto, TlsDHConfig dhConfig) { this.crypto = crypto; - this.config = dhConfig; this.domainParameters = getDomainParameters(dhConfig); + this.isPadded = dhConfig.isPadded(); } public BcTlsSecret calculateDHAgreement(DHPrivateKeyParameters privateKey, DHPublicKeyParameters publicKey) { - return calculateDHAgreement(crypto, privateKey, publicKey, config.isPadded()); + return calculateDHAgreement(crypto, privateKey, publicKey, isPadded); } public TlsAgreement createDH() @@ -80,7 +80,7 @@ public TlsAgreement createDH() public BigInteger decodeParameter(byte[] encoding) throws IOException { - if (config.isPadded() && getValueLength(domainParameters) != encoding.length) + if (isPadded && getValueLength(domainParameters) != encoding.length) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } @@ -109,7 +109,7 @@ public DHPublicKeyParameters decodePublicKey(byte[] encoding) throws IOException public byte[] encodeParameter(BigInteger x) { - return encodeValue(domainParameters, config.isPadded(), x); + return encodeValue(domainParameters, isPadded, x); } public byte[] encodePublicKey(DHPublicKeyParameters publicKey) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsECDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsECDomain.java index 0714878296..2aab2663fb 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsECDomain.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsECDomain.java @@ -79,13 +79,11 @@ public static ECDomainParameters getDomainParameters(int namedGroup) } protected final BcTlsCrypto crypto; - protected final TlsECConfig config; protected final ECDomainParameters domainParameters; public BcTlsECDomain(BcTlsCrypto crypto, TlsECConfig ecConfig) { this.crypto = crypto; - this.config = ecConfig; this.domainParameters = getDomainParameters(ecConfig); } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLKemDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLKemDomain.java index 4b2fc43294..bda0607a85 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLKemDomain.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsMLKemDomain.java @@ -35,14 +35,12 @@ public static MLKEMParameters getDomainParameters(TlsKemConfig kemConfig) } protected final BcTlsCrypto crypto; - protected final TlsKemConfig config; protected final MLKEMParameters domainParameters; protected final boolean isServer; public BcTlsMLKemDomain(BcTlsCrypto crypto, TlsKemConfig kemConfig) { this.crypto = crypto; - this.config = kemConfig; this.domainParameters = getDomainParameters(kemConfig); this.isServer = kemConfig.isServer(); } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsDHDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsDHDomain.java index 578f3d486d..b7e1d96f1a 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsDHDomain.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsDHDomain.java @@ -71,8 +71,8 @@ public static JceTlsSecret calculateDHAgreement(JcaTlsCrypto crypto, DHPrivateKe } protected final JcaTlsCrypto crypto; - protected final TlsDHConfig dhConfig; protected final DHParameterSpec dhSpec; + protected final boolean isPadded; public JceTlsDHDomain(JcaTlsCrypto crypto, TlsDHConfig dhConfig) { @@ -83,8 +83,8 @@ public JceTlsDHDomain(JcaTlsCrypto crypto, TlsDHConfig dhConfig) if (null != spec) { this.crypto = crypto; - this.dhConfig = dhConfig; this.dhSpec = spec; + this.isPadded = dhConfig.isPadded(); return; } } @@ -95,7 +95,7 @@ public JceTlsDHDomain(JcaTlsCrypto crypto, TlsDHConfig dhConfig) public JceTlsSecret calculateDHAgreement(DHPrivateKey privateKey, DHPublicKey publicKey) throws IOException { - return calculateDHAgreement(crypto, privateKey, publicKey, dhConfig.isPadded()); + return calculateDHAgreement(crypto, privateKey, publicKey, isPadded); } public TlsAgreement createDH() @@ -105,7 +105,7 @@ public TlsAgreement createDH() public BigInteger decodeParameter(byte[] encoding) throws IOException { - if (dhConfig.isPadded() && getValueLength(dhSpec) != encoding.length) + if (isPadded && getValueLength(dhSpec) != encoding.length) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); } @@ -140,7 +140,7 @@ public DHPublicKey decodePublicKey(byte[] encoding) throws IOException public byte[] encodeParameter(BigInteger x) throws IOException { - return encodeValue(dhSpec, dhConfig.isPadded(), x); + return encodeValue(dhSpec, isPadded, x); } public byte[] encodePublicKey(DHPublicKey publicKey) throws IOException diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsECDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsECDomain.java index 8c332cbf04..084a1a658f 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsECDomain.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsECDomain.java @@ -29,7 +29,6 @@ public class JceTlsECDomain implements TlsECDomain { protected final JcaTlsCrypto crypto; - protected final TlsECConfig ecConfig; protected final ECParameterSpec ecSpec; protected final ECCurve ecCurve; @@ -42,7 +41,6 @@ public JceTlsECDomain(JcaTlsCrypto crypto, TlsECConfig ecConfig) if (null != spec) { this.crypto = crypto; - this.ecConfig = ecConfig; this.ecSpec = spec; this.ecCurve = ECUtil.convertCurve(spec.getCurve(), spec.getOrder(), spec.getCofactor()); return; From 7ab26167795ca5bfb02560602eec143cbdc05819 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 13 May 2025 20:40:09 +0700 Subject: [PATCH 351/890] TLS test cleanup --- .../java/org/bouncycastle/tls/crypto/test/BcTlsCryptoTest.java | 2 -- tls/src/test/java/org/bouncycastle/tls/test/AllTests.java | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tls/src/test/java/org/bouncycastle/tls/crypto/test/BcTlsCryptoTest.java b/tls/src/test/java/org/bouncycastle/tls/crypto/test/BcTlsCryptoTest.java index 96f70b3903..5f9917cf77 100644 --- a/tls/src/test/java/org/bouncycastle/tls/crypto/test/BcTlsCryptoTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/crypto/test/BcTlsCryptoTest.java @@ -1,7 +1,5 @@ package org.bouncycastle.tls.crypto.test; -import java.security.SecureRandom; - import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto; public class BcTlsCryptoTest diff --git a/tls/src/test/java/org/bouncycastle/tls/test/AllTests.java b/tls/src/test/java/org/bouncycastle/tls/test/AllTests.java index b2ecab6091..be1480096c 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/AllTests.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/AllTests.java @@ -22,16 +22,17 @@ public static Test suite() TestSuite suite = new TestSuite("TLS tests"); suite.addTestSuite(BasicTlsTest.class); + suite.addTestSuite(BcTlsProtocolKemTest.class); suite.addTestSuite(ByteQueueInputStreamTest.class); suite.addTestSuite(DTLSAggregatedHandshakeRetransmissionTest.class); suite.addTestSuite(DTLSHandshakeRetransmissionTest.class); suite.addTestSuite(DTLSProtocolTest.class); suite.addTestSuite(DTLSPSKProtocolTest.class); suite.addTestSuite(DTLSRawKeysProtocolTest.class); + suite.addTestSuite(JcaTlsProtocolKemTest.class); suite.addTestSuite(OCSPTest.class); suite.addTestSuite(PRFTest.class); suite.addTestSuite(Tls13PSKProtocolTest.class); - suite.addTestSuite(TlsProtocolKemTest.class); suite.addTestSuite(TlsProtocolNonBlockingTest.class); suite.addTestSuite(TlsProtocolTest.class); suite.addTestSuite(TlsPSKProtocolTest.class); From 037912017db93eafc6a933da68ce91235fabbf7c Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 14 May 2025 13:45:35 +0700 Subject: [PATCH 352/890] Add a few fatal alert detail messages --- .../bouncycastle/tls/TlsClientProtocol.java | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java index d581f10ae8..59f38a8d85 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java @@ -840,7 +840,7 @@ protected void process13HelloRetryRequest(ServerHello helloRetryRequest) final Hashtable extensions = helloRetryRequest.getExtensions(); if (null == extensions) { - throw new TlsFatalAlert(AlertDescription.illegal_parameter); + throw new TlsFatalAlert(AlertDescription.illegal_parameter, "no extensions found"); } TlsUtils.checkExtensionData13(extensions, HandshakeType.hello_retry_request, AlertDescription.illegal_parameter); @@ -855,15 +855,17 @@ protected void process13HelloRetryRequest(ServerHello helloRetryRequest) while (e.hasMoreElements()) { Integer extType = (Integer)e.nextElement(); + int extensionType = extType.intValue(); - if (ExtensionType.cookie == extType.intValue()) + if (ExtensionType.cookie == extensionType) { continue; } if (null == TlsUtils.getExtensionData(clientExtensions, extType)) { - throw new TlsFatalAlert(AlertDescription.unsupported_extension); + throw new TlsFatalAlert(AlertDescription.unsupported_extension, + "received unrequested extension response: " + ExtensionType.getText(extensionType)); } } } @@ -871,14 +873,19 @@ protected void process13HelloRetryRequest(ServerHello helloRetryRequest) final ProtocolVersion server_version = TlsExtensionsUtils.getSupportedVersionsExtensionServer(extensions); if (null == server_version) { - throw new TlsFatalAlert(AlertDescription.missing_extension); + throw new TlsFatalAlert(AlertDescription.missing_extension, + "missing extension response: " + ExtensionType.getText(ExtensionType.supported_versions)); } if (!ProtocolVersion.TLSv13.isEqualOrEarlierVersionOf(server_version) || - !ProtocolVersion.contains(tlsClientContext.getClientSupportedVersions(), server_version) || - !TlsUtils.isValidVersionForCipherSuite(cipherSuite, server_version)) + !ProtocolVersion.contains(tlsClientContext.getClientSupportedVersions(), server_version)) { - throw new TlsFatalAlert(AlertDescription.illegal_parameter); + throw new TlsFatalAlert(AlertDescription.illegal_parameter, "invalid version selected: " + server_version); + } + + if (!TlsUtils.isValidVersionForCipherSuite(cipherSuite, server_version)) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter, "invalid cipher suite for selected version"); } if (null != clientBinders) @@ -891,6 +898,20 @@ protected void process13HelloRetryRequest(ServerHello helloRetryRequest) } } + final int selected_group = TlsExtensionsUtils.getKeyShareHelloRetryRequest(extensions); + + /* + * TODO[tls:psk_ke] + * + * RFC 8446 4.2.8. Servers [..] MUST NOT send a KeyShareEntry when using the "psk_ke" + * PskKeyExchangeMode. + */ + if (selected_group < 0) + { + throw new TlsFatalAlert(AlertDescription.missing_extension, + "missing extension response: " + ExtensionType.getText(ExtensionType.key_share)); + } + /* * RFC 8446 4.2.8. Upon receipt of this [Key Share] extension in a HelloRetryRequest, the * client MUST verify that (1) the selected_group field corresponds to a group which was @@ -899,12 +920,10 @@ protected void process13HelloRetryRequest(ServerHello helloRetryRequest) * extension in the original ClientHello. If either of these checks fails, then the client * MUST abort the handshake with an "illegal_parameter" alert. */ - final int selected_group = TlsExtensionsUtils.getKeyShareHelloRetryRequest(extensions); - if (!TlsUtils.isValidKeyShareSelection(server_version, securityParameters.getClientSupportedGroups(), clientAgreements, selected_group)) { - throw new TlsFatalAlert(AlertDescription.illegal_parameter); + throw new TlsFatalAlert(AlertDescription.illegal_parameter, "invalid key_share selected"); } final byte[] cookie = TlsExtensionsUtils.getCookieExtension(extensions); From e378985825f7c847d4ba9c6f33faf786dc6a2a01 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 14 May 2025 15:44:56 +0700 Subject: [PATCH 353/890] Use proper 'public' API --- .../java/org/bouncycastle/tls/crypto/impl/bc/BcX25519.java | 4 ++-- .../main/java/org/bouncycastle/tls/crypto/impl/bc/BcX448.java | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcX25519.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcX25519.java index e2928c878e..fa382ebfa8 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcX25519.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcX25519.java @@ -25,10 +25,10 @@ public BcX25519(BcTlsCrypto crypto) public byte[] generateEphemeral() throws IOException { - crypto.getSecureRandom().nextBytes(privateKey); + X25519.generatePrivateKey(crypto.getSecureRandom(), privateKey); byte[] publicKey = new byte[X25519.POINT_SIZE]; - X25519.scalarMultBase(privateKey, 0, publicKey, 0); + X25519.generatePublicKey(privateKey, 0, publicKey, 0); return publicKey; } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcX448.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcX448.java index ec3bd1d617..85e2cf5082 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcX448.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcX448.java @@ -25,10 +25,10 @@ public BcX448(BcTlsCrypto crypto) public byte[] generateEphemeral() throws IOException { - crypto.getSecureRandom().nextBytes(privateKey); + X448.generatePrivateKey(crypto.getSecureRandom(), privateKey); byte[] publicKey = new byte[X448.POINT_SIZE]; - X448.scalarMultBase(privateKey, 0, publicKey, 0); + X448.generatePublicKey(privateKey, 0, publicKey, 0); return publicKey; } From 1778ccf6a18a109db55d9fa7ec1dfe7c85032a67 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 15 May 2025 12:16:56 +0700 Subject: [PATCH 354/890] Test for empty X500Name equality - fix legacy X509Name construction from empty string --- core/src/main/java/org/bouncycastle/asn1/x509/X509Name.java | 5 +++++ .../java/org/bouncycastle/asn1/x509/X509NameTokenizer.java | 6 +++--- .../test/java/org/bouncycastle/asn1/test/X500NameTest.java | 2 ++ .../test/java/org/bouncycastle/asn1/test/X509NameTest.java | 2 ++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/X509Name.java b/core/src/main/java/org/bouncycastle/asn1/x509/X509Name.java index 031ff10dd0..69e67f662c 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/X509Name.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/X509Name.java @@ -1079,6 +1079,11 @@ public boolean equals(Object obj) { return false; } + + if (orderingSize == 0) + { + return true; + } boolean[] indexes = new boolean[orderingSize]; int start, end, delta; diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java b/core/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java index 7f99235bf9..773390e4cf 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/X509NameTokenizer.java @@ -25,18 +25,18 @@ public X509NameTokenizer( char separator) { this.value = oid; - this.index = -1; + this.index = oid.length() < 1 ? 0 : -1; this.separator = separator; } public boolean hasMoreTokens() { - return (index != value.length()); + return index < value.length(); } public String nextToken() { - if (index == value.length()) + if (index >= value.length()) { return null; } diff --git a/core/src/test/java/org/bouncycastle/asn1/test/X500NameTest.java b/core/src/test/java/org/bouncycastle/asn1/test/X500NameTest.java index e05b9ccb73..8ddbdc87f4 100644 --- a/core/src/test/java/org/bouncycastle/asn1/test/X500NameTest.java +++ b/core/src/test/java/org/bouncycastle/asn1/test/X500NameTest.java @@ -435,6 +435,8 @@ public void performTest() fail("strict comparison failed"); } + equalityTest(new X500Name(""), new X500Name("")); + // // inequality to sequences // diff --git a/core/src/test/java/org/bouncycastle/asn1/test/X509NameTest.java b/core/src/test/java/org/bouncycastle/asn1/test/X509NameTest.java index 9d5880cf28..0378c83e74 100644 --- a/core/src/test/java/org/bouncycastle/asn1/test/X509NameTest.java +++ b/core/src/test/java/org/bouncycastle/asn1/test/X509NameTest.java @@ -402,6 +402,8 @@ public void performTest() equalityTest(n1, n2); + equalityTest(new X509Name(""), new X509Name("")); + // // inequality to sequences // From e855013c58ff94d5ee3e0288083972bf9fc40410 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sat, 17 May 2025 17:14:38 +0700 Subject: [PATCH 355/890] Add to javadoc a pointer to SMIMESignedParser in case of large data. --- mail/src/main/java/org/bouncycastle/mail/smime/SMIMESigned.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMESigned.java b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMESigned.java index fd8698cc04..2a080a76dd 100644 --- a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMESigned.java +++ b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMESigned.java @@ -23,6 +23,8 @@ /** * general class for handling a pkcs7-signature message. *

    + * (SMIMESignedParser may be preferred e.g. for large files, since it avoids loading all the data at once). + *

    * A simple example of usage - note, in the example below the validity of * the certificate isn't verified, just the fact that one of the certs * matches the given signer... From 2bc0300dea7871e7240421e7f78562ee644ad856 Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 20 May 2025 16:31:12 +0930 Subject: [PATCH 356/890] update PGPKeyPairGeneratorTest --- .../openpgp/operator/PGPKeyPairGenerator.java | 112 +++++++++ .../bc/BcPGPKeyPairGeneratorProvider.java | 33 ++- .../JcaPGPKeyPairGeneratorProvider.java | 39 +++ .../openpgp/test/PGPKeyPairGeneratorTest.java | 238 ++++++++++++++++++ 4 files changed, 421 insertions(+), 1 deletion(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyPairGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyPairGenerator.java index a4c5c4953e..2dc54f5c42 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyPairGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPKeyPairGenerator.java @@ -1,5 +1,7 @@ package org.bouncycastle.openpgp.operator; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.sec.SECObjectIdentifiers; import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPKeyPair; @@ -178,4 +180,114 @@ public abstract PGPKeyPair generateLegacyEd25519KeyPair() */ public abstract PGPKeyPair generateLegacyX25519KeyPair() throws PGPException; + + /** + * Generate an ECDH elliptic curve encryption key over the NIST p-256 curve. + * + * @return NIST p-256 ECDSA encryption key pair + * @throws PGPException if the key pair cannot be generated + * + * @see + * RFC6637 - Elliptic Curve Cryptography in OpenPGP + */ + public PGPKeyPair generateNistP256ECDHKeyPair() + throws PGPException + { + return generateECDHKeyPair(SECObjectIdentifiers.secp256r1); + } + + /** + * Generate an ECDH elliptic curve encryption key over the NIST p-384 curve. + * + * @return NIST p-384 ECDSA encryption key pair + * @throws PGPException if the key pair cannot be generated + * + * @see + * RFC6637 - Elliptic Curve Cryptography in OpenPGP + */ + public PGPKeyPair generateNistP384ECDHKeyPair() + throws PGPException + { + return generateECDHKeyPair(SECObjectIdentifiers.secp384r1); + } + + /** + * Generate an ECDH elliptic curve encryption key over the NIST p-521 curve. + * + * @return NIST p-521 ECDSA encryption key pair + * @throws PGPException if the key pair cannot be generated + * + * @see + * RFC6637 - Elliptic Curve Cryptography in OpenPGP + */ + public PGPKeyPair generateNistP521ECDHKeyPair() + throws PGPException + { + return generateECDHKeyPair(SECObjectIdentifiers.secp521r1); + } + + /** + * Generate an ECDSA elliptic curve signing key over the NIST p-256 curve. + * + * @return NIST p-256 ECDSA signing key pair + * @throws PGPException if the key pair cannot be generated + * + * @see + * RFC6637 - Elliptic Curve Cryptography in OpenPGP + */ + public PGPKeyPair generateNistP256ECDSAKeyPair() + throws PGPException + { + return generateECDSAKeyPair(SECObjectIdentifiers.secp256r1); + } + + /** + * Generate an ECDSA elliptic curve signing key over the NIST p-384 curve. + * + * @return NIST p-384 ECDSA signing key pair + * @throws PGPException if the key pair cannot be generated + * + * @see + * RFC6637 - Elliptic Curve Cryptography in OpenPGP + */ + public PGPKeyPair generateNistP384ECDSAKeyPair() + throws PGPException + { + return generateECDSAKeyPair(SECObjectIdentifiers.secp384r1); + } + + /** + * Generate an ECDSA elliptic curve signing key over the NIST p-521 curve. + * + * @return NIST p-521 ECDSA signing key pair + * @throws PGPException if the key pair cannot be generated + * + * @see + * RFC6637 - Elliptic Curve Cryptography in OpenPGP + */ + public PGPKeyPair generateNistP521ECDSAKeyPair() + throws PGPException + { + return generateECDSAKeyPair(SECObjectIdentifiers.secp521r1); + } + + /** + * Generate an elliptic curve Diffie-Hellman encryption key pair over the curve identified by the given OID. + * + * @param curveOID OID of the elliptic curve + * @return PGP key pair + * @throws PGPException if the key pair cannot be generated + */ + public abstract PGPKeyPair generateECDHKeyPair(ASN1ObjectIdentifier curveOID) + throws PGPException; + + /** + * Generate an elliptic curve signing key over the curve identified by the given OID. + * + * @param curveOID OID of the elliptic curve + * @return PGP key pair + * @throws PGPException if the key pair cannot be generated + */ + public abstract PGPKeyPair generateECDSAKeyPair(ASN1ObjectIdentifier curveOID) + throws PGPException; } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPairGeneratorProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPairGeneratorProvider.java index 42e1eef9af..3ecc447a8e 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPairGeneratorProvider.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPairGeneratorProvider.java @@ -1,19 +1,24 @@ package org.bouncycastle.openpgp.operator.bc; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; import org.bouncycastle.bcpg.PublicKeyPacket; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator; import org.bouncycastle.crypto.generators.Ed448KeyPairGenerator; import org.bouncycastle.crypto.generators.RSAKeyPairGenerator; import org.bouncycastle.crypto.generators.X25519KeyPairGenerator; import org.bouncycastle.crypto.generators.X448KeyPairGenerator; +import org.bouncycastle.crypto.params.ECKeyGenerationParameters; +import org.bouncycastle.crypto.params.ECNamedDomainParameters; import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters; import org.bouncycastle.crypto.params.Ed448KeyGenerationParameters; import org.bouncycastle.crypto.params.RSAKeyGenerationParameters; import org.bouncycastle.crypto.params.X25519KeyGenerationParameters; import org.bouncycastle.crypto.params.X448KeyGenerationParameters; +import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPKeyPair; import org.bouncycastle.openpgp.operator.PGPKeyPairGenerator; @@ -24,7 +29,7 @@ import java.util.Date; public class BcPGPKeyPairGeneratorProvider - extends PGPKeyPairGeneratorProvider + extends PGPKeyPairGeneratorProvider { private SecureRandom random = CryptoServicesRegistrar.getSecureRandom(); @@ -128,5 +133,31 @@ public PGPKeyPair generateLegacyX25519KeyPair() AsymmetricCipherKeyPair keyPair = gen.generateKeyPair(); return new BcPGPKeyPair(version, PublicKeyAlgorithmTags.ECDH, keyPair, creationTime); } + + @Override + public PGPKeyPair generateECDHKeyPair(ASN1ObjectIdentifier curveOID) + throws PGPException + { + ECKeyPairGenerator gen = new ECKeyPairGenerator(); + gen.init(new ECKeyGenerationParameters( + new ECNamedDomainParameters(curveOID, ECUtil.getNamedCurveByOid(curveOID)), + CryptoServicesRegistrar.getSecureRandom())); + + AsymmetricCipherKeyPair keyPair = gen.generateKeyPair(); + return new BcPGPKeyPair(version, PublicKeyAlgorithmTags.ECDH, keyPair, creationTime); + } + + @Override + public PGPKeyPair generateECDSAKeyPair(ASN1ObjectIdentifier curveOID) + throws PGPException + { + ECKeyPairGenerator gen = new ECKeyPairGenerator(); + gen.init(new ECKeyGenerationParameters( + new ECNamedDomainParameters(curveOID, ECUtil.getNamedCurveByOid(curveOID)), + CryptoServicesRegistrar.getSecureRandom())); + + AsymmetricCipherKeyPair keyPair = gen.generateKeyPair(); + return new BcPGPKeyPair(version, PublicKeyAlgorithmTags.ECDSA, keyPair, creationTime); + } } } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyPairGeneratorProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyPairGeneratorProvider.java index 329bf9f052..bb69846cc0 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyPairGeneratorProvider.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/jcajce/JcaPGPKeyPairGeneratorProvider.java @@ -1,13 +1,16 @@ package org.bouncycastle.openpgp.operator.jcajce; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; import org.bouncycastle.bcpg.PublicKeyPacket; import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; import org.bouncycastle.jcajce.spec.EdDSAParameterSpec; import org.bouncycastle.jcajce.spec.XDHParameterSpec; import org.bouncycastle.jcajce.util.DefaultJcaJceHelper; import org.bouncycastle.jcajce.util.NamedJcaJceHelper; import org.bouncycastle.jcajce.util.ProviderJcaJceHelper; +import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPKeyPair; import org.bouncycastle.openpgp.operator.PGPKeyPairGenerator; @@ -212,5 +215,41 @@ public PGPKeyPair generateLegacyX25519KeyPair() throw new PGPException("Cannot generate LegacyX25519 key pair.", e); } } + + @Override + public PGPKeyPair generateECDHKeyPair(ASN1ObjectIdentifier curveOID) + throws PGPException + { + try + { + KeyPairGenerator gen = helper.createKeyPairGenerator("ECDH"); + String curveName = ECUtil.getCurveName(curveOID); + gen.initialize(new ECNamedCurveGenParameterSpec(curveName)); + KeyPair keyPair = gen.generateKeyPair(); + return new JcaPGPKeyPair(version, PublicKeyAlgorithmTags.ECDH, keyPair, creationTime); + } + catch (GeneralSecurityException e) + { + throw new PGPException("Cannot generate ECDH key pair.", e); + } + } + + @Override + public PGPKeyPair generateECDSAKeyPair(ASN1ObjectIdentifier curveOID) + throws PGPException + { + try + { + KeyPairGenerator gen = helper.createKeyPairGenerator("ECDSA"); + String curveName = ECUtil.getCurveName(curveOID); + gen.initialize(new ECNamedCurveGenParameterSpec(curveName)); + KeyPair keyPair = gen.generateKeyPair(); + return new JcaPGPKeyPair(version, PublicKeyAlgorithmTags.ECDSA, keyPair, creationTime); + } + catch (GeneralSecurityException e) + { + throw new PGPException("Cannot generate ECDSA key pair.", e); + } + } } } diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPKeyPairGeneratorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPKeyPairGeneratorTest.java index 1c16c272d8..a0f71064f0 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/PGPKeyPairGeneratorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/PGPKeyPairGeneratorTest.java @@ -1,5 +1,8 @@ package org.bouncycastle.openpgp.test; +import org.bouncycastle.asn1.sec.SECObjectIdentifiers; +import org.bouncycastle.bcpg.ECDHPublicBCPGKey; +import org.bouncycastle.bcpg.ECDSAPublicBCPGKey; import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; import org.bouncycastle.bcpg.PublicKeyPacket; import org.bouncycastle.jce.provider.BouncyCastleProvider; @@ -70,6 +73,25 @@ private void performWith(Factory factory) testGenerateV6LegacyX25519KeyFails(factory); testGenerateV4LegacyX215519Key(factory); + + // NIST + testGenerateV4P256ECDHKey(factory); + testGenerateV6P256ECDHKey(factory); + + testGenerateV4P384ECDHKey(factory); + testGenerateV6P384ECDHKey(factory); + + testGenerateV4P521ECDHKey(factory); + testGenerateV6P521ECDHKey(factory); + + testGenerateV4P256ECDSAKey(factory); + testGenerateV6P256ECDSAKey(factory); + + testGenerateV4P384ECDSAKey(factory); + testGenerateV6P384ECDSAKey(factory); + + testGenerateV4P521ECDSAKey(factory); + testGenerateV6P521ECDSAKey(factory); } private void testGenerateV4RsaKey(Factory factory) @@ -318,6 +340,222 @@ private void testGenerateV4LegacyX215519Key(Factory factory) kp.getPublicKey().getCreationTime(), creationTime); } + private void testGenerateV4P256ECDHKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_4, creationTime); + + PGPKeyPair kp = gen.generateNistP256ECDHKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_4); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDH); + ECDHPublicBCPGKey k = (ECDHPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(SECObjectIdentifiers.secp256r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV4P384ECDHKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_4, creationTime); + + PGPKeyPair kp = gen.generateNistP384ECDHKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_4); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDH); + ECDHPublicBCPGKey k = (ECDHPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(SECObjectIdentifiers.secp384r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV4P521ECDHKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_4, creationTime); + + PGPKeyPair kp = gen.generateNistP521ECDHKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_4); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDH); + ECDHPublicBCPGKey k = (ECDHPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(SECObjectIdentifiers.secp521r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV4P256ECDSAKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_4, creationTime); + + PGPKeyPair kp = gen.generateNistP256ECDSAKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_4); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDSA); + ECDSAPublicBCPGKey k = (ECDSAPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(SECObjectIdentifiers.secp256r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV4P384ECDSAKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_4, creationTime); + + PGPKeyPair kp = gen.generateNistP384ECDSAKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_4); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDSA); + ECDSAPublicBCPGKey k = (ECDSAPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(SECObjectIdentifiers.secp384r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV4P521ECDSAKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_4, creationTime); + + PGPKeyPair kp = gen.generateNistP521ECDSAKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_4); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDSA); + ECDSAPublicBCPGKey k = (ECDSAPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(SECObjectIdentifiers.secp521r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV6P256ECDHKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_6, creationTime); + + PGPKeyPair kp = gen.generateNistP256ECDHKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_6); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDH); + ECDHPublicBCPGKey k = (ECDHPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(SECObjectIdentifiers.secp256r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV6P384ECDHKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_6, creationTime); + + PGPKeyPair kp = gen.generateNistP384ECDHKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_6); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDH); + ECDHPublicBCPGKey k = (ECDHPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(SECObjectIdentifiers.secp384r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV6P521ECDHKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_6, creationTime); + + PGPKeyPair kp = gen.generateNistP521ECDHKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_6); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDH); + ECDHPublicBCPGKey k = (ECDHPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(SECObjectIdentifiers.secp521r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV6P256ECDSAKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_6, creationTime); + + PGPKeyPair kp = gen.generateNistP256ECDSAKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_6); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDSA); + ECDSAPublicBCPGKey k = (ECDSAPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(SECObjectIdentifiers.secp256r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV6P384ECDSAKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_6, creationTime); + + PGPKeyPair kp = gen.generateNistP384ECDSAKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_6); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDSA); + ECDSAPublicBCPGKey k = (ECDSAPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(SECObjectIdentifiers.secp384r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + + private void testGenerateV6P521ECDSAKey(Factory factory) + throws PGPException + { + Date creationTime = currentTimeRounded(); + PGPKeyPairGenerator gen = factory.create(PublicKeyPacket.VERSION_6, creationTime); + + PGPKeyPair kp = gen.generateNistP521ECDSAKeyPair(); + + isEquals("Key version mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getVersion(), PublicKeyPacket.VERSION_6); + isEquals("Key algorithm mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getAlgorithm(), PublicKeyAlgorithmTags.ECDSA); + ECDSAPublicBCPGKey k = (ECDSAPublicBCPGKey) kp.getPublicKey().getPublicKeyPacket().getKey(); + isEquals(SECObjectIdentifiers.secp521r1, k.getCurveOID()); + isEquals("Key creation time mismatch (" + gen.getClass().getName() + ")", + kp.getPublicKey().getCreationTime(), creationTime); + } + public static void main(String[] args) { runTest(new PGPKeyPairGeneratorTest()); From de7a2f0a58ae13e8674cd7851e4f4bb7a47cf8f1 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 22 May 2025 22:03:17 +0700 Subject: [PATCH 357/890] Clarify tagging of certIssuer field (CHOICE type) --- .../java/org/bouncycastle/asn1/bc/LinkedCertificate.java | 4 ++-- .../org/bouncycastle/asn1/test/LinkedCertificateTest.java | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/bc/LinkedCertificate.java b/core/src/main/java/org/bouncycastle/asn1/bc/LinkedCertificate.java index d812087700..8041fe78c9 100644 --- a/core/src/main/java/org/bouncycastle/asn1/bc/LinkedCertificate.java +++ b/core/src/main/java/org/bouncycastle/asn1/bc/LinkedCertificate.java @@ -59,7 +59,7 @@ private LinkedCertificate(ASN1Sequence seq) switch (tagged.getTagNo()) { case 0: - certIssuer = X500Name.getInstance(tagged, false); + certIssuer = X500Name.getInstance(tagged, true); // CHOICE break; case 1: cACerts = GeneralNames.getInstance(tagged, false); @@ -114,7 +114,7 @@ public ASN1Primitive toASN1Primitive() if (certIssuer != null) { - v.add(new DERTaggedObject(false, 0, certIssuer)); + v.add(new DERTaggedObject(true, 0, certIssuer)); // CHOICE } if (cACerts != null) { diff --git a/core/src/test/java/org/bouncycastle/asn1/test/LinkedCertificateTest.java b/core/src/test/java/org/bouncycastle/asn1/test/LinkedCertificateTest.java index 340e5f317d..72e9eb5294 100644 --- a/core/src/test/java/org/bouncycastle/asn1/test/LinkedCertificateTest.java +++ b/core/src/test/java/org/bouncycastle/asn1/test/LinkedCertificateTest.java @@ -70,11 +70,7 @@ private void checkConstruction( checkValues(linked, digestInfo, certLocation, certIssuer, caCerts); - ASN1InputStream aIn = new ASN1InputStream(linked.toASN1Primitive().getEncoded()); - - ASN1Sequence seq = (ASN1Sequence)aIn.readObject(); - - linked = LinkedCertificate.getInstance(seq); + linked = LinkedCertificate.getInstance(linked.getEncoded()); checkValues(linked, digestInfo, certLocation, certIssuer, caCerts); } From 5184b269f0f1dd25af03d6751a2f6772813feda1 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 22 May 2025 22:06:17 +0700 Subject: [PATCH 358/890] Extra tests for signed-data properties --- .../org/bouncycastle/cms/test/NewSignedDataTest.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java index 28eee7602a..06c2b2e964 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java @@ -1500,6 +1500,9 @@ public void testSHA1WithRSAAndAttributeTable() // compute expected content digest // + assertTrue(s.isDetachedSignature()); + assertFalse(s.isCertificateManagementMessage()); + verifySignatures(s, md.digest("Hello world!".getBytes())); verifyRSASignatures(s, md.digest("Hello world!".getBytes())); } @@ -3553,11 +3556,14 @@ public void testCertificateManagement() CMSSignedData sData = sGen.generate(new CMSAbsentContent(), true); - CMSSignedData rsData = new CMSSignedData(sData.getEncoded()); - assertTrue(sData.isCertificateManagementMessage()); assertFalse(sData.isDetachedSignature()); + CMSSignedData rsData = new CMSSignedData(sData.getEncoded()); + + assertTrue(rsData.isCertificateManagementMessage()); + assertFalse(rsData.isDetachedSignature()); + assertEquals(2, rsData.getCertificates().getMatches(null).size()); } From e3692ee5049d00679ddff42a70d4390bb40f5369 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 22 May 2025 22:13:32 +0700 Subject: [PATCH 359/890] Clarify dependency b/w getSessionToResume and getCipherSuites --- .../main/java/org/bouncycastle/tls/DTLSClientProtocol.java | 5 +++-- .../main/java/org/bouncycastle/tls/TlsClientProtocol.java | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java b/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java index e3bce90b85..a0e7d45123 100644 --- a/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java @@ -420,10 +420,11 @@ protected byte[] generateClientHello(ClientHandshakeState state) TlsSession sessionToResume = offeringDTLSv12Minus ? client.getSessionToResume() : null; - boolean fallback = client.isFallback(); - + // NOTE: Client is free to modify the cipher suites up until getSessionToResume state.offeredCipherSuites = client.getCipherSuites(); + boolean fallback = client.isFallback(); + state.clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(client.getClientExtensions()); final boolean shouldUseEMS = client.shouldUseExtendedMasterSecret(); diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java index 59f38a8d85..e01366d485 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java @@ -1808,10 +1808,11 @@ protected void sendClientHello() TlsSession sessionToResume = offeringTLSv12Minus ? tlsClient.getSessionToResume() : null; - boolean fallback = tlsClient.isFallback(); - + // NOTE: Client is free to modify the cipher suites up until getSessionToResume int[] offeredCipherSuites = tlsClient.getCipherSuites(); + boolean fallback = tlsClient.isFallback(); + this.clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(tlsClient.getClientExtensions()); final boolean shouldUseEMS = tlsClient.shouldUseExtendedMasterSecret(); From e797ed10403e284d2a22c867360a7cc588904d3b Mon Sep 17 00:00:00 2001 From: David Hook Date: Sat, 24 May 2025 22:23:25 +1000 Subject: [PATCH 360/890] removed import of ECUtil --- .../bc/BcPGPKeyPairGeneratorProvider.java | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPairGeneratorProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPairGeneratorProvider.java index 3ecc447a8e..a36cebc79e 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPairGeneratorProvider.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPairGeneratorProvider.java @@ -1,10 +1,17 @@ package org.bouncycastle.openpgp.operator.bc; +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Date; + import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.x9.ECNamedCurveTable; +import org.bouncycastle.asn1.x9.X9ECParameters; import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; import org.bouncycastle.bcpg.PublicKeyPacket; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.ec.CustomNamedCurves; import org.bouncycastle.crypto.generators.ECKeyPairGenerator; import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator; import org.bouncycastle.crypto.generators.Ed448KeyPairGenerator; @@ -18,16 +25,11 @@ import org.bouncycastle.crypto.params.RSAKeyGenerationParameters; import org.bouncycastle.crypto.params.X25519KeyGenerationParameters; import org.bouncycastle.crypto.params.X448KeyGenerationParameters; -import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPKeyPair; import org.bouncycastle.openpgp.operator.PGPKeyPairGenerator; import org.bouncycastle.openpgp.operator.PGPKeyPairGeneratorProvider; -import java.math.BigInteger; -import java.security.SecureRandom; -import java.util.Date; - public class BcPGPKeyPairGeneratorProvider extends PGPKeyPairGeneratorProvider { @@ -140,7 +142,7 @@ public PGPKeyPair generateECDHKeyPair(ASN1ObjectIdentifier curveOID) { ECKeyPairGenerator gen = new ECKeyPairGenerator(); gen.init(new ECKeyGenerationParameters( - new ECNamedDomainParameters(curveOID, ECUtil.getNamedCurveByOid(curveOID)), + new ECNamedDomainParameters(curveOID, getNamedCurveByOid(curveOID)), CryptoServicesRegistrar.getSecureRandom())); AsymmetricCipherKeyPair keyPair = gen.generateKeyPair(); @@ -153,11 +155,24 @@ public PGPKeyPair generateECDSAKeyPair(ASN1ObjectIdentifier curveOID) { ECKeyPairGenerator gen = new ECKeyPairGenerator(); gen.init(new ECKeyGenerationParameters( - new ECNamedDomainParameters(curveOID, ECUtil.getNamedCurveByOid(curveOID)), + new ECNamedDomainParameters(curveOID, getNamedCurveByOid(curveOID)), CryptoServicesRegistrar.getSecureRandom())); AsymmetricCipherKeyPair keyPair = gen.generateKeyPair(); return new BcPGPKeyPair(version, PublicKeyAlgorithmTags.ECDSA, keyPair, creationTime); } } + + private static X9ECParameters getNamedCurveByOid( + ASN1ObjectIdentifier oid) + { + X9ECParameters params = CustomNamedCurves.getByOID(oid); + + if (params == null) + { + params = ECNamedCurveTable.getByOID(oid); + } + + return params; + } } From 5d5c1961e303e47ed5ef3bc38db40e33fd9c3ad6 Mon Sep 17 00:00:00 2001 From: Gefei Li Date: Sat, 24 May 2025 12:24:45 +0000 Subject: [PATCH 361/890] Signature Packet format fixes #2078 --- .../bouncycastle/bcpg/SignaturePacket.java | 21 ++++++++++++++++--- .../bouncycastle/openpgp/PGPSignature.java | 4 +++- .../PGPSignatureSubpacketGenerator.java | 12 ++++++++++- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java index de2d3b5ac2..853a22090f 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/SignaturePacket.java @@ -353,7 +353,22 @@ public SignaturePacket( byte[] fingerPrint, MPInteger[] signature) { - super(SIGNATURE); + this(version, false, signatureType, keyID, keyAlgorithm, hashAlgorithm, hashedData, unhashedData, fingerPrint, signature); + } + + public SignaturePacket( + int version, + boolean hasNewPacketFormat, + int signatureType, + long keyID, + int keyAlgorithm, + int hashAlgorithm, + SignatureSubpacket[] hashedData, + SignatureSubpacket[] unhashedData, + byte[] fingerPrint, + MPInteger[] signature) + { + super(SIGNATURE, hasNewPacketFormat); this.version = version; this.signatureType = signatureType; @@ -383,7 +398,7 @@ public SignaturePacket( byte[] signatureEncoding, byte[] salt) { - super(SIGNATURE); + super(SIGNATURE, true); this.version = version; this.signatureType = signatureType; @@ -413,7 +428,7 @@ public SignaturePacket( MPInteger[] signature, byte[] salt) { - super(SIGNATURE); + super(SIGNATURE, true); this.version = version; this.signatureType = signatureType; diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java index 68cf06c2b0..0107154a7b 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java @@ -198,7 +198,7 @@ public class PGPSignature */ public static final int THIRD_PARTY_CONFIRMATION = 0x50; - private final SignaturePacket sigPck; + final SignaturePacket sigPck; private final TrustPacket trustPck; private volatile PGPContentVerifier verifier; @@ -1034,6 +1034,8 @@ public static PGPSignature join(PGPSignature sig1, PGPSignature sig2) SignatureSubpacket[] unhashed = (SignatureSubpacket[])merged.toArray(new SignatureSubpacket[0]); return new PGPSignature( new SignaturePacket( + sig1.getVersion(), + sig1.sigPck.hasNewPacketFormat(), sig1.getSignatureType(), sig1.getKeyID(), sig1.getKeyAlgorithm(), diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureSubpacketGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureSubpacketGenerator.java index 2d008bd7d3..33087c6c3c 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureSubpacketGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignatureSubpacketGenerator.java @@ -1,11 +1,14 @@ package org.bouncycastle.openpgp; +import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.List; +import org.bouncycastle.bcpg.BCPGOutputStream; +import org.bouncycastle.bcpg.PacketFormat; import org.bouncycastle.bcpg.SignatureSubpacket; import org.bouncycastle.bcpg.SignatureSubpacketTags; import org.bouncycastle.bcpg.sig.EmbeddedSignature; @@ -445,9 +448,16 @@ public void setEmbeddedSignature(boolean isCritical, PGPSignature pgpSignature) public void addEmbeddedSignature(boolean isCritical, PGPSignature pgpSignature) throws IOException { - byte[] sig = pgpSignature.getEncoded(); + // Encode the signature forcing legacy packet format, such that we consistently cut off the proper amount + // of header bytes + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + BCPGOutputStream pOut = new BCPGOutputStream(bOut, PacketFormat.LEGACY); + pgpSignature.encode(pOut); + pOut.close(); + byte[] sig = bOut.toByteArray(); byte[] data; + // Cut off the header bytes if (sig.length - 1 > 256) { data = new byte[sig.length - 3]; From 778c040a87fa87f8b18d0295df633e35abb4d5ee Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 25 May 2025 00:34:39 +1000 Subject: [PATCH 362/890] Java 4, Java 5 compatibility changes. Minor cleanups. --- ant/jdk14.xml | 4 +- bc-build.properties | 6 +- .../crypto/kems/SAKKEKEMSGenerator.java | 8 +- .../pqc/crypto/snova/SnovaEngine.java | 17 +- .../crypto/engines/AEADBufferBaseEngine.java | 420 ------------------ .../util/SubjectPublicKeyInfoFactory.java | 218 +++++++++ .../jdk1.4/org/bouncycastle/util/Arrays.java | 22 + .../jdk1.4/org/bouncycastle/util/Bytes.java | 34 +- .../jdk1.4/org/bouncycastle/util/Longs.java | 8 + .../bouncycastle/crypto/test/DigestTest.java | 2 +- gradle.properties | 4 +- ...ractOpenPGPDocumentSignatureGenerator.java | 10 +- .../openpgp/api/KeyPassphraseProvider.java | 11 +- .../openpgp/api/OpenPGPCertificate.java | 48 +- .../openpgp/api/OpenPGPDefaultPolicy.java | 16 +- .../OpenPGPDetachedSignatureGenerator.java | 2 +- .../OpenPGPDetachedSignatureProcessor.java | 4 +- .../api/OpenPGPEncryptionNegotiator.java | 10 +- .../bouncycastle/openpgp/api/OpenPGPKey.java | 6 +- .../openpgp/api/OpenPGPKeyMaterialPool.java | 2 +- .../openpgp/api/OpenPGPKeyReader.java | 20 +- .../openpgp/api/OpenPGPMessageGenerator.java | 10 +- .../api/OpenPGPMessageInputStream.java | 55 ++- .../openpgp/api/OpenPGPMessageProcessor.java | 20 +- .../openpgp/api/OpenPGPPolicy.java | 10 +- .../jcajce/provider/mayo/SignatureSpi.java | 2 +- .../jcajce/provider/snova/SignatureSpi.java | 2 +- .../crypto/impl/jcajce/JceTlsMLKemDomain.java | 38 +- .../tls/crypto/impl/jcajce/KemUtil.java | 23 +- .../tls/crypto/impl/jcajce/JcaTlsCrypto.java | 203 ++++++--- 30 files changed, 585 insertions(+), 650 deletions(-) delete mode 100644 core/src/main/jdk1.4/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java create mode 100644 core/src/main/jdk1.4/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java diff --git a/ant/jdk14.xml b/ant/jdk14.xml index d6769e5cbd..aa3e45add3 100644 --- a/ant/jdk14.xml +++ b/ant/jdk14.xml @@ -31,7 +31,7 @@ - + @@ -45,6 +45,8 @@ + + diff --git a/bc-build.properties b/bc-build.properties index 8a6c20e92b..eeb835d760 100644 --- a/bc-build.properties +++ b/bc-build.properties @@ -3,9 +3,9 @@ # intended to hold user-specific settings that are *not* committed to # the repository. -release.suffix: 1.80 -release.name: 1.80 -release.version: 1.80 +release.suffix: 1.81 +release.name: 1.81 +release.version: 1.81 release.debug: false mail.jar.home: ./libs/javax.mail-1.4.7.jar diff --git a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java index eb14f47b04..2ab72dce8c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/kems/SAKKEKEMSGenerator.java @@ -1,5 +1,8 @@ package org.bouncycastle.crypto.kems; +import java.math.BigInteger; +import java.security.SecureRandom; + import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.EncapsulatedSecretGenerator; import org.bouncycastle.crypto.SecretWithEncapsulation; @@ -10,9 +13,6 @@ import org.bouncycastle.util.Arrays; import org.bouncycastle.util.BigIntegers; -import java.math.BigInteger; -import java.security.SecureRandom; - /** * This class implements the SAKKE (Sakai-Kasahara Key Encryption) Key Encapsulation Mechanism * as defined in RFC 6508. It generates an encapsulated shared secret value (SSV) using @@ -146,7 +146,7 @@ static BigInteger hashToIntegerRange(byte[] input, BigInteger q, Digest digest) // Step 1: Compute A = hashfn(s) digest.update(input, 0, input.length); digest.doFinal(hash, 0); - byte[] A = hash.clone(); + byte[] A = Arrays.clone(hash); // Step 2: Initialize h_0 to all-zero bytes of hashlen size byte[] h = new byte[digest.getDigestSize()]; diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java index 12123152af..1651dab43d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/snova/SnovaEngine.java @@ -11,6 +11,7 @@ import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.GF16; +import org.bouncycastle.util.Integers; import org.bouncycastle.util.Pack; class SnovaEngine @@ -39,7 +40,7 @@ public SnovaEngine(SnovaParameters params) this.o = params.getO(); this.alpha = params.getAlpha(); this.n = params.getN(); - if (!xSSet.containsKey(l)) + if (!xSSet.containsKey(Integers.valueOf(l))) { byte[][] S = new byte[l][lsq]; int[][] xS = new int[l][lsq]; @@ -57,12 +58,12 @@ public SnovaEngine(SnovaParameters params) xS[index][ij] = GF16Utils.gf16FromNibble(S[index][ij]); } } - sSet.put(l, S); - xSSet.put(l, xS); + sSet.put(Integers.valueOf(l), S); + xSSet.put(Integers.valueOf(l), xS); } - S = sSet.get(l); - xS = xSSet.get(l); - if (l < 4 && !fixedAbqSet.containsKey(o)) + S = (byte[][])sSet.get(Integers.valueOf(l)); + xS = (int[][])xSSet.get(Integers.valueOf(l)); + if (l < 4 && !fixedAbqSet.containsKey(Integers.valueOf(o))) { int alphaxl = alpha * l; int alphaxlsq = alphaxl * l; @@ -88,7 +89,7 @@ public SnovaEngine(SnovaParameters params) genAFqS(q12, oxalphaxl + axl, fixedAbq, (oxalphaxlsq << 1) + oxalphaxlsq + axlsq); } } - fixedAbqSet.put(o, fixedAbq); + fixedAbqSet.put(Integers.valueOf(o), fixedAbq); } } @@ -564,7 +565,7 @@ public void genABQP(MapGroup1 map1, byte[] pkSeed) else { int oxalphaxlsq = o * alpha * lsq; - byte[] fixedAbq = fixedAbqSet.get(o); + byte[] fixedAbq = (byte[])fixedAbqSet.get(Integers.valueOf(o)); MapGroup1.fillAlpha(fixedAbq, 0, map1.aAlpha, m * oxalphaxlsq); MapGroup1.fillAlpha(fixedAbq, oxalphaxlsq, map1.bAlpha, (m - 1) * oxalphaxlsq); MapGroup1.fillAlpha(fixedAbq, oxalphaxlsq * 2, map1.qAlpha1, (m - 2) * oxalphaxlsq); diff --git a/core/src/main/jdk1.4/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java b/core/src/main/jdk1.4/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java deleted file mode 100644 index 23f0ee7fb6..0000000000 --- a/core/src/main/jdk1.4/org/bouncycastle/crypto/engines/AEADBufferBaseEngine.java +++ /dev/null @@ -1,420 +0,0 @@ -package org.bouncycastle.crypto.engines; - -import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.InvalidCipherTextException; -import org.bouncycastle.crypto.OutputLengthException; -import org.bouncycastle.util.Arrays; - -abstract class AEADBufferBaseEngine - extends AEADBaseEngine -{ - protected static final int UNINITIALIZED = 0; - protected static final int ENCINIT = 1; - protected static final int ENCAAD = 2; - protected static final int ENCDATA = 3; - protected static final int ENCFINAL = 4; - protected static final int DECINIT = 5; - protected static final int DECAAD = 6; - protected static final int DECDATA = 7; - protected static final int DECFINAL = 8; - - protected static final State Uninitialized = new State(UNINITIALIZED); - protected static final State EncInit = new State(ENCINIT); - protected static final State EncAad = new State(ENCAAD); - protected static final State EncData = new State(ENCDATA); - protected static final State EncFinal = new State(ENCFINAL); - protected static final State DecInit = new State(DECINIT); - protected static final State DecAad = new State(DECAAD); - protected static final State DecData = new State(DECDATA); - protected static final State DecFinal = new State(DECFINAL); - - protected static class State - { - int ord; - - private State(int ord) - { - this.ord = ord; - } - } - - protected byte[] m_buf; - protected byte[] m_aad; - protected int m_bufPos; - protected int m_aadPos; - protected boolean aadFinished; - protected boolean initialised = false; - protected int AADBufferSize; - protected int BlockSize; - protected State m_state = Uninitialized; - - @Override - public void processAADByte(byte input) - { - checkAAD(); - if (m_aadPos == AADBufferSize) - { - processBufferAAD(m_aad, 0); - m_aadPos = 0; - } - m_aad[m_aadPos++] = input; - } - - @Override - public void processAADBytes(byte[] input, int inOff, int len) - { - if ((inOff + len) > input.length) - { - throw new DataLengthException("input buffer too short"); - } - // Don't enter AAD state until we actually get input - if (len <= 0) - { - return; - } - - checkAAD(); - if (m_aadPos > 0) - { - int available = AADBufferSize - m_aadPos; - if (len <= available) - { - System.arraycopy(input, inOff, m_aad, m_aadPos, len); - m_aadPos += len; - return; - } - - System.arraycopy(input, inOff, m_aad, m_aadPos, available); - inOff += available; - len -= available; - - processBufferAAD(m_aad, 0); - m_aadPos = 0; - } - while (len > AADBufferSize) - { - processBufferAAD(input, inOff); - inOff += AADBufferSize; - len -= AADBufferSize; - } - System.arraycopy(input, inOff, m_aad, m_aadPos, len); - m_aadPos += len; - } - - @Override - public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) - throws DataLengthException - { - if (inOff + len > input.length) - { - throw new DataLengthException("input buffer too short"); - } - - boolean forEncryption = checkData(); - - int resultLength = 0; - - if (forEncryption) - { - if (m_bufPos > 0) - { - int available = BlockSize - m_bufPos; - if (len <= available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } - - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - - validateAndProcessBuffer(m_buf, 0, output, outOff); - resultLength = BlockSize; - //m_bufPos = 0; - } - - while (len > BlockSize) - { - validateAndProcessBuffer(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; - } - } - else - { - int available = BlockSize + MAC_SIZE - m_bufPos; - if (len <= available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return 0; - } - if (BlockSize >= MAC_SIZE) - { - if (m_bufPos > BlockSize) - { - validateAndProcessBuffer(m_buf, 0, output, outOff); - m_bufPos -= BlockSize; - System.arraycopy(m_buf, BlockSize, m_buf, 0, m_bufPos); - resultLength = BlockSize; - - available += BlockSize; - if (len <= available) - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return resultLength; - } - } - - available = BlockSize - m_bufPos; - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - len -= available; - validateAndProcessBuffer(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - //m_bufPos = 0; - } - else - { - while (m_bufPos > BlockSize && len + m_bufPos > BlockSize + MAC_SIZE) - { - validateAndProcessBuffer(m_buf, resultLength, output, outOff + resultLength); - m_bufPos -= BlockSize; - resultLength += BlockSize; - } - if (m_bufPos != 0) - { - System.arraycopy(m_buf, resultLength, m_buf, 0, m_bufPos); - if (m_bufPos + len > BlockSize + MAC_SIZE) - { - available = Math.max(BlockSize - m_bufPos, 0); - System.arraycopy(input, inOff, m_buf, m_bufPos, available); - inOff += available; - validateAndProcessBuffer(m_buf, 0, output, outOff + resultLength); - resultLength += BlockSize; - len -= available; - } - else - { - System.arraycopy(input, inOff, m_buf, m_bufPos, len); - m_bufPos += len; - return resultLength; - } - } - } - while (len > BlockSize + MAC_SIZE) - { - validateAndProcessBuffer(input, inOff, output, outOff + resultLength); - inOff += BlockSize; - len -= BlockSize; - resultLength += BlockSize; - } - } - - System.arraycopy(input, inOff, m_buf, 0, len); - m_bufPos = len; - - return resultLength; - } - - @Override - public int doFinal(byte[] output, int outOff) - throws IllegalStateException, InvalidCipherTextException - { - boolean forEncryption = checkData(); - int resultLength; - if (forEncryption) - { - resultLength = m_bufPos + MAC_SIZE; - } - else - { - if (m_bufPos < MAC_SIZE) - { - throw new InvalidCipherTextException("data too short"); - } - - m_bufPos -= MAC_SIZE; - - resultLength = m_bufPos; - } - - if (outOff > output.length - resultLength) - { - throw new OutputLengthException("output buffer too short"); - } - processFinalBlock(output, outOff); - if (forEncryption) - { - System.arraycopy(mac, 0, output, outOff + resultLength - MAC_SIZE, MAC_SIZE); - } - else - { - if (!Arrays.constantTimeAreEqual(MAC_SIZE, mac, 0, m_buf, m_bufPos)) - { - throw new InvalidCipherTextException(algorithmName + " mac does not match"); - } - } - reset(!forEncryption); - return resultLength; - } - - public int getBlockSize() - { - return BlockSize; - } - - public int getUpdateOutputSize(int len) - { - // The -1 is to account for the lazy processing of a full buffer - int total = Math.max(0, len) - 1; - - switch (m_state.ord) - { - case DECINIT: - case DECAAD: - total = Math.max(0, total - MAC_SIZE); - break; - case DECDATA: - case DECFINAL: - total = Math.max(0, total + m_bufPos - MAC_SIZE); - break; - case ENCDATA: - case ENCFINAL: - total = Math.max(0, total + m_bufPos); - break; - default: - break; - } - return total - total % BlockSize; - } - - public int getOutputSize(int len) - { - int total = Math.max(0, len); - - switch (m_state.ord) - { - case DECINIT: - case DECAAD: - return Math.max(0, total - MAC_SIZE); - case DECDATA: - case DECFINAL: - return Math.max(0, total + m_bufPos - MAC_SIZE); - case ENCDATA: - case ENCFINAL: - return total + m_bufPos + MAC_SIZE; - default: - return total + MAC_SIZE; - } - } - - protected void checkAAD() - { - switch (m_state.ord) - { - case DECINIT: - m_state = DecAad; - break; - case ENCINIT: - m_state = EncAad; - break; - case DECAAD: - case ENCAAD: - break; - case ENCFINAL: - throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption"); - default: - throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); - } - } - - protected boolean checkData() - { - switch (m_state.ord) - { - case DECINIT: - case DECAAD: - finishAAD(DecData); - return false; - case ENCINIT: - case ENCAAD: - finishAAD(EncData); - return true; - case DECDATA: - return false; - case ENCDATA: - return true; - case ENCFINAL: - throw new IllegalStateException(getAlgorithmName() + " cannot be reused for encryption"); - default: - throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); - } - } - - private void finishAAD(State nextState) - { - // State indicates whether we ever received AAD - switch (m_state.ord) - { - case DECAAD: - case ENCAAD: - { - processFinalAAD(); - break; - } - default: - break; - } - - m_aadPos = 0; - m_state = nextState; - } - - protected void bufferReset() - { - Arrays.fill(m_buf, (byte)0); - Arrays.fill(m_aad, (byte)0); - m_bufPos = 0; - m_aadPos = 0; - switch (m_state.ord) - { - case DECINIT: - case ENCINIT: - break; - case DECAAD: - case DECDATA: - case DECFINAL: - m_state = DecInit; - break; - case ENCAAD: - case ENCDATA: - case ENCFINAL: - m_state = EncFinal; - return; - default: - throw new IllegalStateException(getAlgorithmName() + " needs to be initialized"); - } - } - - protected void validateAndProcessBuffer(byte[] input, int inOff, byte[] output, int outOff) - { - if (outOff > output.length - BlockSize) - { - throw new OutputLengthException("output buffer too short"); - } - processBuffer(input, inOff, output, outOff); - } - - protected abstract void processFinalBlock(byte[] output, int outOff); - - protected abstract void processBufferAAD(byte[] input, int inOff); - - protected abstract void processFinalAAD(); - - protected abstract void processBuffer(byte[] input, int inOff, byte[] output, int outOff); -} diff --git a/core/src/main/jdk1.4/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java b/core/src/main/jdk1.4/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java new file mode 100644 index 0000000000..a4b3b89fb6 --- /dev/null +++ b/core/src/main/jdk1.4/org/bouncycastle/crypto/util/SubjectPublicKeyInfoFactory.java @@ -0,0 +1,218 @@ +package org.bouncycastle.crypto.util; + +import java.io.IOException; +import java.math.BigInteger; +import java.util.HashSet; +import java.util.Set; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.RSAPublicKey; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.DSAParameter; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.asn1.x9.X962Parameters; +import org.bouncycastle.asn1.x9.X9ECParameters; +import org.bouncycastle.asn1.x9.X9ECPoint; +import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.params.DSAParameters; +import org.bouncycastle.crypto.params.DSAPublicKeyParameters; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECGOST3410Parameters; +import org.bouncycastle.crypto.params.ECNamedDomainParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; +import org.bouncycastle.crypto.params.Ed448PublicKeyParameters; +import org.bouncycastle.crypto.params.RSAKeyParameters; +import org.bouncycastle.crypto.params.X25519PublicKeyParameters; +import org.bouncycastle.crypto.params.X448PublicKeyParameters; +import org.bouncycastle.internal.asn1.edec.EdECObjectIdentifiers; +import org.bouncycastle.internal.asn1.rosstandart.RosstandartObjectIdentifiers; +import org.bouncycastle.util.Arrays; + +/** + * Factory to create ASN.1 subject public key info objects from lightweight public keys. + */ +public class SubjectPublicKeyInfoFactory +{ + private static final byte tag_OctetString = (byte)0x04; + private static Set cryptoProOids = new HashSet(5); + + static + { + cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A); + cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B); + cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C); + cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchA); + cryptoProOids.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchB); + } + + private SubjectPublicKeyInfoFactory() + { + + } + + /** + * Create a SubjectPublicKeyInfo public key. + * + * @param publicKey the key to be encoded into the info object. + * @return a SubjectPublicKeyInfo representing the key. + * @throws java.io.IOException on an error encoding the key + */ + public static SubjectPublicKeyInfo createSubjectPublicKeyInfo(AsymmetricKeyParameter publicKey) + throws IOException + { + if (publicKey instanceof RSAKeyParameters) + { + RSAKeyParameters pub = (RSAKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new RSAPublicKey(pub.getModulus(), pub.getExponent())); + } + else if (publicKey instanceof DSAPublicKeyParameters) + { + DSAPublicKeyParameters pub = (DSAPublicKeyParameters)publicKey; + + DSAParameter params = null; + DSAParameters dsaParams = pub.getParameters(); + if (dsaParams != null) + { + params = new DSAParameter(dsaParams.getP(), dsaParams.getQ(), dsaParams.getG()); + } + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, params), new ASN1Integer(pub.getY())); + } + else if (publicKey instanceof ECPublicKeyParameters) + { + ECPublicKeyParameters pub = (ECPublicKeyParameters)publicKey; + ECDomainParameters domainParams = pub.getParameters(); + ASN1Encodable params; + + if (domainParams == null) + { + params = new X962Parameters(DERNull.INSTANCE); // Implicitly CA + } + else if (domainParams instanceof ECGOST3410Parameters) + { + ECGOST3410Parameters gostParams = (ECGOST3410Parameters)domainParams; + + BigInteger bX = pub.getQ().getAffineXCoord().toBigInteger(); + BigInteger bY = pub.getQ().getAffineYCoord().toBigInteger(); + + params = new GOST3410PublicKeyAlgParameters(gostParams.getPublicKeyParamSet(), gostParams.getDigestParamSet()); + + int encKeySize; + int offset; + ASN1ObjectIdentifier algIdentifier; + + + if (cryptoProOids.contains(gostParams.getPublicKeyParamSet())) + { + encKeySize = 64; + offset = 32; + algIdentifier = CryptoProObjectIdentifiers.gostR3410_2001; + } + else + { + boolean is512 = (bX.bitLength() > 256); + if (is512) + { + encKeySize = 128; + offset = 64; + algIdentifier = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512; + } + else + { + encKeySize = 64; + offset = 32; + algIdentifier = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256; + } + } + + byte[] encKey = new byte[encKeySize]; + extractBytes(encKey, encKeySize / 2, 0, bX); + extractBytes(encKey, encKeySize / 2, offset, bY); + + try + { + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(algIdentifier, params), new DEROctetString(encKey)); + } + catch (IOException e) + { + return null; + } + } + else if (domainParams instanceof ECNamedDomainParameters) + { + params = new X962Parameters(((ECNamedDomainParameters)domainParams).getName()); + } + else + { + X9ECParameters ecP = new X9ECParameters( + domainParams.getCurve(), + // TODO Support point compression + new X9ECPoint(domainParams.getG(), false), + domainParams.getN(), + domainParams.getH(), + domainParams.getSeed()); + + params = new X962Parameters(ecP); + } + + // TODO Support point compression + byte[] pubKeyOctets = pub.getQ().getEncoded(false); + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), pubKeyOctets); + } + else if (publicKey instanceof X448PublicKeyParameters) + { + X448PublicKeyParameters key = (X448PublicKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X448), key.getEncoded()); + } + else if (publicKey instanceof X25519PublicKeyParameters) + { + X25519PublicKeyParameters key = (X25519PublicKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_X25519), key.getEncoded()); + } + else if (publicKey instanceof Ed448PublicKeyParameters) + { + Ed448PublicKeyParameters key = (Ed448PublicKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed448), key.getEncoded()); + } + else if (publicKey instanceof Ed25519PublicKeyParameters) + { + Ed25519PublicKeyParameters key = (Ed25519PublicKeyParameters)publicKey; + + return new SubjectPublicKeyInfo(new AlgorithmIdentifier(EdECObjectIdentifiers.id_Ed25519), key.getEncoded()); + } + else + { + throw new IOException("key parameters not recognized"); + } + } + + private static void extractBytes(byte[] encKey, int size, int offSet, BigInteger bI) + { + byte[] val = bI.toByteArray(); + if (val.length < size) + { + byte[] tmp = new byte[size]; + System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length); + val = tmp; + } + + for (int i = 0; i != size; i++) + { + encKey[offSet + i] = val[val.length - 1 - i]; + } + } +} diff --git a/core/src/main/jdk1.4/org/bouncycastle/util/Arrays.java b/core/src/main/jdk1.4/org/bouncycastle/util/Arrays.java index ef5d23662f..f5fabb74e0 100644 --- a/core/src/main/jdk1.4/org/bouncycastle/util/Arrays.java +++ b/core/src/main/jdk1.4/org/bouncycastle/util/Arrays.java @@ -962,6 +962,14 @@ public static void clear(int[] data) } } + public static void clear(long[] data) + { + if (null != data) + { + java.util.Arrays.fill(data, 0); + } + } + public static boolean isNullOrContainsNull(Object[] array) { if (null == array) @@ -1137,6 +1145,20 @@ public static int hashCode(Object[] data) return hc; } + public static void validateSegment(byte[] buf, int off, int len) + { + if (buf == null) + { + throw new NullPointerException("'buf' cannot be null"); + } + int available = buf.length - off; + int remaining = available - len; + if ((off | len | available | remaining) < 0) + { + throw new IndexOutOfBoundsException("buf.length: " + buf.length + ", off: " + off + ", len: " + len); + } + } + /** * Iterator backed by a specific array. */ diff --git a/core/src/main/jdk1.4/org/bouncycastle/util/Bytes.java b/core/src/main/jdk1.4/org/bouncycastle/util/Bytes.java index 526a27659e..25a9ce4431 100644 --- a/core/src/main/jdk1.4/org/bouncycastle/util/Bytes.java +++ b/core/src/main/jdk1.4/org/bouncycastle/util/Bytes.java @@ -16,6 +16,14 @@ public static void xor(int len, byte[] x, byte[] y, byte[] z) } } + public static void xor(int len, byte[] x, int xOff, byte[] y, byte[] z, int zOff) + { + for (int i = 0; i < len; ++i) + { + z[zOff++] = (byte)(x[xOff++] ^ y[i]); + } + } + public static void xor(int len, byte[] x, int xOff, byte[] y, int yOff, byte[] z, int zOff) { for (int i = 0; i < len; ++i) @@ -23,7 +31,23 @@ public static void xor(int len, byte[] x, int xOff, byte[] y, int yOff, byte[] z z[zOff + i] = (byte)(x[xOff + i] ^ y[yOff + i]); } } - + + public static void xor(int len, byte[] x, byte[] y, byte[] z, int zOff) + { + for (int i = 0; i < len; ++i) + { + z[zOff++] = (byte)(x[i] ^ y[i]); + } + } + + public static void xor(int len, byte[] x, byte[] y, int yOff, byte[] z, int zOff) + { + for (int i = 0; i < len; ++i) + { + z[zOff++] = (byte)(x[i] ^ y[yOff++]); + } + } + public static void xorTo(int len, byte[] x, byte[] z) { for (int i = 0; i < len; ++i) @@ -32,6 +56,14 @@ public static void xorTo(int len, byte[] x, byte[] z) } } + public static void xorTo(int len, byte[] x, int xOff, byte[] z) + { + for (int i = 0; i < len; ++i) + { + z[i] ^= x[xOff++]; + } + } + public static void xorTo(int len, byte[] x, int xOff, byte[] z, int zOff) { for (int i = 0; i < len; ++i) diff --git a/core/src/main/jdk1.4/org/bouncycastle/util/Longs.java b/core/src/main/jdk1.4/org/bouncycastle/util/Longs.java index 5baea19a67..4e6d5f09a8 100644 --- a/core/src/main/jdk1.4/org/bouncycastle/util/Longs.java +++ b/core/src/main/jdk1.4/org/bouncycastle/util/Longs.java @@ -77,4 +77,12 @@ public static Long valueOf(long value) { return new Long(value); } + + public static void xorTo(int len, long[] x, int xOff, long[] z, int zOff) + { + for (int i = 0; i < len; ++i) + { + z[zOff + i] ^= x[xOff + i]; + } + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java index d06daacc24..a5d2cd82c7 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java @@ -326,7 +326,7 @@ static void implTestVectorsDigest(SimpleTest test, ExtendedDigest digest, String int a = line.indexOf('='); if (a < 0) { - int count = Integer.parseInt(map.get("Count")); + int count = Integer.parseInt((String)map.get("Count")); if (count != 21) { continue; diff --git a/gradle.properties b/gradle.properties index 88edbf693d..35de375e4f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ org.gradle.jvmargs=-Xmx2g -version=1.80-SNAPSHOT -maxVersion=1.81 +version=1.81 +maxVersion=1.82 org.gradle.java.installations.auto-detect=false org.gradle.java.installations.auto-download=false org.gradle.java.installations.fromEnv=BC_JDK8,BC_JDK11,BC_JDK17 diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java index a959f2c202..fc50d3b7f9 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java @@ -22,10 +22,10 @@ public class AbstractOpenPGPDocumentSignatureGenerator signatureGenerators = new ArrayList<>(); - protected final List signingKeys = new ArrayList<>(); - protected final List signatureCallbacks = new ArrayList<>(); - protected final List signingKeyPassphraseProviders = new ArrayList<>(); + protected final List signatureGenerators = new ArrayList(); + protected final List signingKeys = new ArrayList(); + protected final List signatureCallbacks = new ArrayList(); + protected final List signingKeyPassphraseProviders = new ArrayList(); protected final KeyPassphraseProvider.DefaultKeyPassphraseProvider defaultKeyPassphraseProvider = new KeyPassphraseProvider.DefaultKeyPassphraseProvider(); @@ -36,7 +36,7 @@ public class AbstractOpenPGPDocumentSignatureGenerator select(OpenPGPCertificate certificate, final OpenPGPPolicy policy) { - List result = new ArrayList<>(); + List result = new ArrayList(); for (Iterator it = certificate.getSigningKeys().iterator(); it.hasNext(); ) { OpenPGPCertificate.OpenPGPComponentKey key = it.next(); diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPassphraseProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPassphraseProvider.java index e05a7ea2da..8c8c81eccb 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPassphraseProvider.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/KeyPassphraseProvider.java @@ -1,13 +1,13 @@ package org.bouncycastle.openpgp.api; -import org.bouncycastle.util.Arrays; - import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import org.bouncycastle.util.Arrays; + public interface KeyPassphraseProvider { /** @@ -23,8 +23,8 @@ public interface KeyPassphraseProvider class DefaultKeyPassphraseProvider implements KeyPassphraseProvider { - private final Map passphraseMap = new HashMap<>(); - private final List allPassphrases = new ArrayList<>(); + private final Map passphraseMap = new HashMap(); + private final List allPassphrases = new ArrayList(); private KeyPassphraseProvider callback; public DefaultKeyPassphraseProvider() @@ -78,8 +78,9 @@ public char[] getKeyPassword(OpenPGPKey.OpenPGPSecretKey key) public DefaultKeyPassphraseProvider addPassphrase(char[] passphrase) { boolean found = false; - for (char[] existing : allPassphrases) + for (Iterator it = allPassphrases.iterator(); it.hasNext();) { + char[] existing = (char[])it.next(); found |= (Arrays.areEqual(existing, passphrase)); } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java index c332992d54..cbcf97b8e9 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java @@ -116,8 +116,8 @@ public OpenPGPCertificate(PGPKeyRing keyRing, OpenPGPImplementation implementati this.policy = policy; this.keyRing = keyRing; - this.subkeys = new LinkedHashMap<>(); - this.componentSignatureChains = new LinkedHashMap<>(); + this.subkeys = new LinkedHashMap(); + this.componentSignatureChains = new LinkedHashMap(); Iterator rawKeys = keyRing.getPublicKeys(); @@ -184,7 +184,7 @@ public List getValidUserIds(Date evaluationTime) */ public Map getPublicKeys() { - Map keys = new HashMap<>(); + Map keys = new HashMap(); keys.put(primaryKey.getKeyIdentifier(), primaryKey); keys.putAll(subkeys); return keys; @@ -208,7 +208,7 @@ public OpenPGPPrimaryKey getPrimaryKey() */ public Map getSubkeys() { - return new LinkedHashMap<>(subkeys); + return new LinkedHashMap(subkeys); } /** @@ -249,7 +249,7 @@ public boolean test(OpenPGPComponentKey key, Date time) */ public List getComponents() { - return new ArrayList<>(componentSignatureChains.keySet()); + return new ArrayList(componentSignatureChains.keySet()); } /** @@ -261,7 +261,7 @@ public List getComponents() */ public List getKeys() { - List keys = new ArrayList<>(); + List keys = new ArrayList(); keys.add(primaryKey); keys.addAll(subkeys.values()); return keys; @@ -370,7 +370,7 @@ public PGPPublicKeyRing getPGPPublicKeyRing() return (PGPPublicKeyRing)keyRing; } - List list = new ArrayList<>(); + List list = new ArrayList(); for (Iterator it = keyRing.getPublicKeys(); it.hasNext(); ) { list.add(it.next()); @@ -395,7 +395,7 @@ public KeyIdentifier getKeyIdentifier() */ public List getAllKeyIdentifiers() { - List identifiers = new ArrayList<>(); + List identifiers = new ArrayList(); for (Iterator it = keyRing.getPublicKeys(); it.hasNext(); ) { PGPPublicKey key = it.next(); @@ -682,7 +682,7 @@ public byte[] getEncoded(PacketFormat format) BCPGOutputStream pOut = new BCPGOutputStream(bOut, format); // Make sure we export a TPK - List list = new ArrayList<>(); + List list = new ArrayList(); for (Iterator it = getPGPKeyRing().getPublicKeys(); it.hasNext(); ) { list.add(it.next()); @@ -974,7 +974,7 @@ private OpenPGPSignatureChain getPreferenceSignature(Date evaluationTime) return directKeyBinding; } - List uidBindings = new ArrayList<>(); + List uidBindings = new ArrayList(); for (Iterator it = getPrimaryKey().getUserIDs().iterator(); it.hasNext(); ) { OpenPGPSignatureChain uidBinding = getAllSignatureChainsFor(it.next()) @@ -1017,7 +1017,7 @@ public int compare(OpenPGPSignatureChain o1, OpenPGPSignatureChain o2) */ public List getIdentities() { - return new ArrayList<>(primaryKey.identityComponents); + return new ArrayList(primaryKey.identityComponents); } /** @@ -1824,7 +1824,7 @@ private void verifyEmbeddedPrimaryKeyBinding(PGPContentVerifierBuilderProvider c OpenPGPComponentKey subkey = getTargetKeyComponent(); // Signing subkey needs embedded primary key binding signature - List embeddedSignatures = new ArrayList<>(); + List embeddedSignatures = new ArrayList(); try { PGPSignatureList sigList = signature.getHashedSubPackets().getEmbeddedSignatures(); @@ -2245,7 +2245,7 @@ public String toDetailString() public OpenPGPPrimaryKey(PGPPublicKey rawPubkey, OpenPGPCertificate certificate) { super(rawPubkey, certificate); - this.identityComponents = new ArrayList<>(); + this.identityComponents = new ArrayList(); Iterator userIds = rawPubkey.getUserIDs(); while (userIds.hasNext()) @@ -2324,7 +2324,7 @@ public OpenPGPComponentSignature getLatestKeyRevocationSelfSignature(Date evalua @Override public OpenPGPComponentSignature getLatestSelfSignature(Date evaluationTime) { - List signatures = new ArrayList<>(); + List signatures = new ArrayList(); OpenPGPComponentSignature directKeySig = getLatestDirectKeySelfSignature(evaluationTime); if (directKeySig != null) @@ -2366,7 +2366,7 @@ public OpenPGPComponentSignature getLatestSelfSignature(Date evaluationTime) */ public List getUserIDs() { - List userIds = new ArrayList<>(); + List userIds = new ArrayList(); for (Iterator it = identityComponents.iterator(); it.hasNext(); ) { OpenPGPIdentityComponent identity = it.next(); @@ -2398,7 +2398,7 @@ public List getValidUserIds() */ public List getValidUserIDs(Date evaluationTime) { - List userIds = new ArrayList<>(); + List userIds = new ArrayList(); for (Iterator it = identityComponents.iterator(); it.hasNext(); ) { OpenPGPIdentityComponent identity = it.next(); @@ -2499,7 +2499,7 @@ public OpenPGPUserId getExplicitOrImplicitPrimaryUserId(Date evaluationTime) */ public List getUserAttributes() { - List userAttributes = new ArrayList<>(); + List userAttributes = new ArrayList(); for (Iterator it = identityComponents.iterator(); it.hasNext(); ) { OpenPGPIdentityComponent identity = it.next(); @@ -2519,7 +2519,7 @@ public List getUserAttributes() protected List getKeySignatures() { Iterator iterator = rawPubkey.getSignatures(); - List list = new ArrayList<>(); + List list = new ArrayList(); while (iterator.hasNext()) { PGPSignature sig = iterator.next(); @@ -2563,7 +2563,7 @@ protected List getUserAttributeSignatures(OpenPGPUser private List signIterToList(OpenPGPIdentityComponent identity, Iterator iterator) { - List list = new ArrayList<>(); + List list = new ArrayList(); while (iterator.hasNext()) { PGPSignature sig = iterator.next(); @@ -2614,7 +2614,7 @@ public String toDetailString() protected List getKeySignatures() { Iterator iterator = rawPubkey.getSignatures(); - List list = new ArrayList<>(); + List list = new ArrayList(); while (iterator.hasNext()) { PGPSignature sig = iterator.next(); @@ -2840,7 +2840,7 @@ public String toString() public static class OpenPGPSignatureChain implements Comparable, Iterable { - private final List chainLinks = new ArrayList<>(); + private final List chainLinks = new ArrayList(); private OpenPGPSignatureChain(Link rootLink) { @@ -2893,7 +2893,7 @@ public OpenPGPComponentSignature getRevocation() */ public List getSignatures() { - List signatures = new ArrayList<>(); + List signatures = new ArrayList(); for (Link link : chainLinks) { signatures.add(link.getSignature()); @@ -3422,7 +3422,7 @@ public static class OpenPGPSignatureChains implements Iterable { private final OpenPGPCertificateComponent targetComponent; - private final Set chains = new TreeSet<>(); + private final Set chains = new TreeSet(); public OpenPGPSignatureChains(OpenPGPCertificateComponent component) { @@ -3583,7 +3583,7 @@ private interface KeyFilter private List filterKeys(Date evaluationTime, KeyFilter filter) { - List result = new ArrayList<>(); + List result = new ArrayList(); for (Iterator it = getKeys().iterator(); it.hasNext(); ) { OpenPGPComponentKey key = it.next(); diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDefaultPolicy.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDefaultPolicy.java index de580d1c1d..b751ce27ee 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDefaultPolicy.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDefaultPolicy.java @@ -1,21 +1,21 @@ package org.bouncycastle.openpgp.api; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + import org.bouncycastle.bcpg.HashAlgorithmTags; import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; import org.bouncycastle.openpgp.api.util.UTCUtil; -import java.util.Date; -import java.util.HashMap; -import java.util.Map; - public class OpenPGPDefaultPolicy implements OpenPGPPolicy { - private final Map documentHashAlgorithmCutoffDates = new HashMap<>(); - private final Map certificateHashAlgorithmCutoffDates = new HashMap<>(); - private final Map symmetricKeyAlgorithmCutoffDates = new HashMap<>(); - private final Map publicKeyMinimalBitStrengths = new HashMap<>(); + private final Map documentHashAlgorithmCutoffDates = new HashMap(); + private final Map certificateHashAlgorithmCutoffDates = new HashMap(); + private final Map symmetricKeyAlgorithmCutoffDates = new HashMap(); + private final Map publicKeyMinimalBitStrengths = new HashMap(); private int defaultDocumentSignatureHashAlgorithm = HashAlgorithmTags.SHA512; private int defaultCertificationSignatureHashAlgorithm = HashAlgorithmTags.SHA512; private int defaultSymmetricKeyAlgorithm = SymmetricKeyAlgorithmTags.AES_128; diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureGenerator.java index ad17c471f6..68b5390c12 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureGenerator.java @@ -81,7 +81,7 @@ public List sign(InputStream inputStr } } - List documentSignatures = new ArrayList<>(); + List documentSignatures = new ArrayList(); for (int i = 0; i < signatureGenerators.size(); i++) { PGPSignatureGenerator sigGen = signatureGenerators.get(i); diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureProcessor.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureProcessor.java index 3ef8aca52d..b2146e5ba6 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureProcessor.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPDetachedSignatureProcessor.java @@ -40,7 +40,7 @@ public class OpenPGPDetachedSignatureProcessor private final OpenPGPImplementation implementation; private final OpenPGPPolicy policy; private final OpenPGPKeyMaterialPool.OpenPGPCertificatePool certificatePool = new OpenPGPKeyMaterialPool.OpenPGPCertificatePool(); - private final List pgpSignatures = new ArrayList<>(); + private final List pgpSignatures = new ArrayList(); private Date verifyNotAfter = new Date(); // now private Date verifyNotBefore = new Date(0L); // beginning of time @@ -185,7 +185,7 @@ public OpenPGPDetachedSignatureProcessor verifyNotAfter(Date date) public List process(InputStream inputStream) throws IOException { - List documentSignatures = new ArrayList<>(); + List documentSignatures = new ArrayList(); for (Iterator it = pgpSignatures.iterator(); it.hasNext(); ) { PGPSignature signature = (PGPSignature)it.next(); diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPEncryptionNegotiator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPEncryptionNegotiator.java index 3b9286c31c..953bf33940 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPEncryptionNegotiator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPEncryptionNegotiator.java @@ -104,7 +104,7 @@ public List getAlgorithms(PreferredAEADCi // This way, combinations with a smaller index have a higher weight than combinations with larger index. // Additionally, we divide this weight by the number of capable subkeys per cert in order to // prevent a certificate with many capable subkeys from outvoting other certificates - List result = new ArrayList<>(); + List result = new ArrayList(); for (PreferredAEADCiphersuites.Combination c : prefs.getAlgorithms()) { if (c.getSymmetricAlgorithm() != SymmetricKeyAlgorithmTags.NULL @@ -153,7 +153,7 @@ public List getAlgorithms(PreferredAlgorithms preferences, OpenPGPPolic // This way, combinations with a smaller index have a higher weight than combinations with larger index. // Additionally, we divide this weight by the number of capable subkeys per cert in order to // prevent a certificate with many capable subkeys from outvoting other certificates - List result = new ArrayList<>(); + List result = new ArrayList(); int[] prefs = preferences.getPreferences(); for (int i = 0; i < prefs.length; i++) { @@ -202,7 +202,7 @@ public List getAlgorithms(PreferredAlgorithms preferences, OpenPGPPolic { // Count the keys symmetric key preferences (that can be used with OED) and update the weight map - List result = new ArrayList<>(); + List result = new ArrayList(); int[] prefs = preferences.getPreferences(); for (int i = 0; i < prefs.length; i++) { @@ -255,7 +255,7 @@ private static R processCertificates( KeyProcessor keyProcessor, R defaultResult) { - Map weights = new HashMap<>(); + Map weights = new HashMap(); // Go through all certificate's capable subkeys for (Iterator it = certificates.iterator(); it.hasNext(); ) @@ -267,7 +267,7 @@ private static R processCertificates( } // Only consider encryption keys capable of SEIPDv1/OED - Map capableKeys = new HashMap<>(); + Map capableKeys = new HashMap(); for (Iterator ckIt = encryptionKeys.iterator(); ckIt.hasNext(); ) { keyProcessor.processKey(ckIt.next(), capableKeys); diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKey.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKey.java index d8b3b9ff2f..2a0009ddda 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKey.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKey.java @@ -74,7 +74,7 @@ public OpenPGPKey(PGPSecretKeyRing keyRing, OpenPGPImplementation implementation super(keyRing, implementation, policy); // Process and map secret keys - this.secretKeys = new HashMap<>(); + this.secretKeys = new HashMap(); for (Iterator it = getKeys().iterator(); it.hasNext(); ) { OpenPGPComponentKey key = (OpenPGPComponentKey)it.next(); @@ -115,7 +115,7 @@ public List getComponents() List components = super.getComponents(); for (int i = components.size() - 1; i >= 0; i--) { - OpenPGPCertificateComponent component = components.get(i); + OpenPGPCertificateComponent component = (OpenPGPCertificateComponent)components.get(i); if (component instanceof OpenPGPComponentKey) { OpenPGPSecretKey secretKey = getSecretKey((OpenPGPComponentKey)component); @@ -147,7 +147,7 @@ public OpenPGPSecretKey getPrimarySecretKey() */ public Map getSecretKeys() { - return new HashMap<>(secretKeys); + return new HashMap(secretKeys); } /** diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java index 96ab4b10ee..e033f820ef 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java @@ -21,7 +21,7 @@ public abstract class OpenPGPKeyMaterialPool implements OpenPGPKeyMaterialProvider { - private final Map pool = new HashMap<>(); + private final Map pool = new HashMap(); private OpenPGPKeyMaterialProvider callback = null; private boolean cacheResultsFromCallback = true; diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyReader.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyReader.java index 130b38abf8..0891cd9df2 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyReader.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyReader.java @@ -1,5 +1,12 @@ package org.bouncycastle.openpgp.api; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; + import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.openpgp.PGPMarker; import org.bouncycastle.openpgp.PGPObjectFactory; @@ -8,13 +15,6 @@ import org.bouncycastle.openpgp.PGPUtil; import org.bouncycastle.util.io.Streams; -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; - /** * Reader for {@link OpenPGPKey OpenPGPKeys} or {@link OpenPGPCertificate OpenPGPCertificates}. */ @@ -223,7 +223,7 @@ public List parseKeysOrCertificates(InputStream inputStream) public List parseKeysOrCertificates(byte[] bytes) throws IOException { - List certsOrKeys = new ArrayList<>(); + List certsOrKeys = new ArrayList(); ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); InputStream decoderStream = PGPUtil.getDecoderStream(bIn); @@ -271,7 +271,7 @@ public List parseCertificates(InputStream inputStream) public List parseCertificates(byte[] bytes) throws IOException { - List certs = new ArrayList<>(); + List certs = new ArrayList(); ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); InputStream decoderStream = PGPUtil.getDecoderStream(bIn); @@ -315,7 +315,7 @@ public List parseKeys(InputStream inputStream) public List parseKeys(byte[] bytes) throws IOException { - List keys = new ArrayList<>(); + List keys = new ArrayList(); ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); InputStream decoderStream = PGPUtil.getDecoderStream(bIn); diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java index 8f1e249482..7da170c074 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java @@ -53,8 +53,8 @@ public class OpenPGPMessageGenerator private boolean isArmored = true; public boolean isAllowPadding = true; - private final List encryptionKeys = new ArrayList<>(); - private final List messagePassphrases = new ArrayList<>(); + private final List encryptionKeys = new ArrayList(); + private final List messagePassphrases = new ArrayList(); // Literal Data metadata private Date fileModificationDate = null; @@ -457,7 +457,7 @@ public ArmoredOutputStream get(OutputStream outputStream) public List select(OpenPGPCertificate certificate, OpenPGPPolicy policy) { - List result = new ArrayList<>(); + List result = new ArrayList(); for (Iterator it = certificate.getEncryptionKeys().iterator(); it.hasNext(); ) { OpenPGPCertificate.OpenPGPComponentKey key = it.next(); @@ -491,8 +491,8 @@ public MessageEncryptionMechanism negotiateEncryption(OpenPGPMessageGenerator co // .distinct() // .collect(Collectors.toList()); - List certificates = new ArrayList<>(); - Set uniqueCertificates = new HashSet<>(); // For distinctness + List certificates = new ArrayList(); + Set uniqueCertificates = new HashSet(); // For distinctness for (Iterator it = encryptionKeys.iterator(); it.hasNext(); ) { diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageInputStream.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageInputStream.java index bd714cb32e..a89b1de0ba 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageInputStream.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageInputStream.java @@ -124,8 +124,9 @@ public void close() while ((next = objectFactory.nextObject()) != null) { boolean handled = false; - for (PacketHandler handler : closeHandlers) + for (Iterator it = closeHandlers.iterator(); it.hasNext();) { + PacketHandler handler = (PacketHandler)it.next(); if (handler.canHandle(next)) { handler.close(next); @@ -198,7 +199,7 @@ public Result getResult() public static class Result { - private final List documentSignatures = new ArrayList<>(); + private final List documentSignatures = new ArrayList(); private OpenPGPCertificate.OpenPGPComponentKey decryptionKey; private char[] decryptionPassphrase; private PGPSessionKey sessionKey; @@ -288,12 +289,12 @@ public Date getFileModificationTime() public List getSignatures() { - return new ArrayList<>(documentSignatures); + return new ArrayList(documentSignatures); } static class Builder { - private final List layers = new ArrayList<>(); + private final List layers = new ArrayList(); private Builder() { @@ -407,7 +408,7 @@ void verifySignatures(OpenPGPMessageProcessor processor) return; } - last.signatures = new ArrayList<>(); + last.signatures = new ArrayList(); last.signatures.addAll(last.onePassSignatures.verify(processor)); last.signatures.addAll(last.prefixedSignatures.verify(processor)); // last.signatures.addAll(last.onePassVerifier.verify(processor)); @@ -718,9 +719,9 @@ public void handle(Object packet) static class OnePassSignatures { - private final List onePassSignatures = new ArrayList<>(); - private final List signatures = new ArrayList<>(); - private final Map issuers = new HashMap<>(); + private final List onePassSignatures = new ArrayList(); + private final List signatures = new ArrayList(); + private final Map issuers = new HashMap(); OnePassSignatures() { @@ -729,16 +730,18 @@ static class OnePassSignatures void addOnePassSignatures(PGPOnePassSignatureList onePassSignatures) { - for (PGPOnePassSignature ops : onePassSignatures) + for (Iterator it = onePassSignatures.iterator(); it.hasNext();) { + PGPOnePassSignature ops = (PGPOnePassSignature)it.next(); this.onePassSignatures.add(ops); } } void addSignatures(PGPSignatureList signatures) { - for (PGPSignature signature : signatures) + for (Iterator it = signatures.iterator(); it.hasNext();) { + PGPSignature signature = it.next(); this.signatures.add(signature); } } @@ -746,8 +749,9 @@ void addSignatures(PGPSignatureList signatures) void init(OpenPGPMessageProcessor processor) { - for (PGPOnePassSignature ops : onePassSignatures) + for (Iterator it = onePassSignatures.iterator(); it.hasNext();) { + PGPOnePassSignature onePassSignature = (PGPOnePassSignature)it.next(); KeyIdentifier identifier = ops.getKeyIdentifier(); OpenPGPCertificate cert = processor.provideCertificate(identifier); if (cert == null) @@ -771,8 +775,9 @@ void init(OpenPGPMessageProcessor processor) void update(int i) { - for (PGPOnePassSignature onePassSignature : onePassSignatures) + for (Iterator it = onePassSignatures.iterator(); it.hasNext();) { + PGPOnePassSignature onePassSignature = (PGPOnePassSignature)it.next(); if (issuers.containsKey(onePassSignature)) { onePassSignature.update((byte) i); @@ -782,8 +787,9 @@ void update(int i) void update(byte[] b, int off, int len) { - for (PGPOnePassSignature onePassSignature : onePassSignatures) + for (Iterator it = onePassSignatures.iterator(); it.hasNext();) { + PGPOnePassSignature onePassSignature = (PGPOnePassSignature)it.next(); if (issuers.containsKey(onePassSignature)) { onePassSignature.update(b, off, len); @@ -795,7 +801,7 @@ List verify( OpenPGPMessageProcessor processor) { OpenPGPPolicy policy = processor.getImplementation().policy(); - List dataSignatures = new ArrayList<>(); + List dataSignatures = new ArrayList(); int num = onePassSignatures.size(); for (int i = 0; i < signatures.size(); i++) { @@ -840,8 +846,8 @@ List verify( static class PrefixedSignatures { - private final List prefixedSignatures = new ArrayList<>(); - private final List dataSignatures = new ArrayList<>(); + private final List prefixedSignatures = new ArrayList(); + private final List dataSignatures = new ArrayList(); PrefixedSignatures() { @@ -850,16 +856,18 @@ static class PrefixedSignatures void addAll(PGPSignatureList signatures) { - for (PGPSignature signature : signatures) + for (Iterator it = signatures.iterator(); it.hasNext();) { + PGPSignature signature = (PGPSignature)it.next(); this.prefixedSignatures.add(signature); } } void init(OpenPGPMessageProcessor processor) { - for (PGPSignature sig : prefixedSignatures) + for (Iterator it = prefixedSignatures.iterator(); it.hasNext();) { + PGPSignature sig = (PGPSignature)it.next(); KeyIdentifier identifier = OpenPGPSignature.getMostExpressiveIdentifier(sig.getKeyIdentifiers()); if (identifier == null) { @@ -891,26 +899,29 @@ void init(OpenPGPMessageProcessor processor) void update(int i) { - for(PGPSignature signature : prefixedSignatures) + for (Iterator it = prefixedSignatures.iterator(); it.hasNext();) { + PGPSignature signature = (PGPSignature)it.next(); signature.update((byte) i); } } void update(byte[] buf, int off, int len) { - for (PGPSignature signature : prefixedSignatures) + for (Iterator it = prefixedSignatures.iterator(); it.hasNext();) { + PGPSignature signature = (PGPSignature)it.next(); signature.update(buf, off, len); } } List verify(OpenPGPMessageProcessor processor) { - List verifiedSignatures = new ArrayList<>(); + List verifiedSignatures = new ArrayList(); OpenPGPPolicy policy = processor.getImplementation().policy(); - for (OpenPGPSignature.OpenPGPDocumentSignature sig : dataSignatures) + for (Iterator it = dataSignatures.iterator(); it.hasNext();) { + OpenPGPSignature.OpenPGPDocumentSignature sig = (OpenPGPSignature.OpenPGPDocumentSignature)it.next(); try { sig.sanitize(sig.issuer, policy); diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageProcessor.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageProcessor.java index ee8a4f9792..546d70a1bd 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageProcessor.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageProcessor.java @@ -1,6 +1,13 @@ package org.bouncycastle.openpgp.api; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + import org.bouncycastle.bcpg.KeyIdentifier; +import org.bouncycastle.openpgp.IntegrityProtectedInputStream; import org.bouncycastle.openpgp.PGPEncryptedData; import org.bouncycastle.openpgp.PGPEncryptedDataList; import org.bouncycastle.openpgp.PGPException; @@ -11,19 +18,12 @@ import org.bouncycastle.openpgp.PGPSessionKey; import org.bouncycastle.openpgp.PGPSessionKeyEncryptedData; import org.bouncycastle.openpgp.PGPUtil; -import org.bouncycastle.openpgp.IntegrityProtectedInputStream; import org.bouncycastle.openpgp.api.exception.KeyPassphraseException; import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory; import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory; import org.bouncycastle.openpgp.operator.SessionKeyDataDecryptorFactory; import org.bouncycastle.util.Arrays; -import java.io.IOException; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - public class OpenPGPMessageProcessor { private final OpenPGPImplementation implementation; @@ -433,7 +433,7 @@ Decrypted decrypt(PGPEncryptedDataList encDataList) */ private List skesks(PGPEncryptedDataList encDataList) { - List list = new ArrayList<>(); + List list = new ArrayList(); for (PGPEncryptedData encData : encDataList) { if (encData instanceof PGPPBEEncryptedData) @@ -452,7 +452,7 @@ private List skesks(PGPEncryptedDataList encDataList) */ private List pkesks(PGPEncryptedDataList encDataList) { - List list = new ArrayList<>(); + List list = new ArrayList(); for (PGPEncryptedData encData : encDataList) { if (encData instanceof PGPPublicKeyEncryptedData) @@ -495,7 +495,7 @@ public static class Configuration private final OpenPGPKeyMaterialPool.OpenPGPCertificatePool certificatePool; private final OpenPGPKeyMaterialPool.OpenPGPKeyPool keyPool; private final KeyPassphraseProvider.DefaultKeyPassphraseProvider keyPassphraseProvider; - public final List messagePassphrases = new ArrayList<>(); + public final List messagePassphrases = new ArrayList(); private MissingMessagePassphraseCallback missingMessagePassphraseCallback; private PGPExceptionCallback exceptionCallback = null; private PGPSessionKey sessionKey; diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPPolicy.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPPolicy.java index c1307bb516..e8f870a479 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPPolicy.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPPolicy.java @@ -1,15 +1,15 @@ package org.bouncycastle.openpgp.api; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + import org.bouncycastle.bcpg.SignatureSubpacket; import org.bouncycastle.bcpg.sig.NotationData; import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; -import java.util.Date; -import java.util.HashSet; -import java.util.Set; - /** * Policy for OpenPGP algorithms and features. */ @@ -309,7 +309,7 @@ default boolean isKnownSignatureSubpacket(int signatureSubpacketTag) */ class OpenPGPNotationRegistry { - private final Set knownNotations = new HashSet<>(); + private final Set knownNotations = new HashSet(); public boolean isNotationKnown(String notationName) { diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/SignatureSpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/SignatureSpi.java index dd791cba9a..0a1075657e 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/SignatureSpi.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/mayo/SignatureSpi.java @@ -52,7 +52,7 @@ protected void engineInitVerify(PublicKey publicKey) } catch (Exception e) { - throw new InvalidKeyException("unknown public key passed to Mayo: " + e.getMessage(), e); + throw new InvalidKeyException("unknown public key passed to Mayo: " + e.getMessage()); } } diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SignatureSpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SignatureSpi.java index ef3af0ba47..e544734348 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SignatureSpi.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/snova/SignatureSpi.java @@ -52,7 +52,7 @@ protected void engineInitVerify(PublicKey publicKey) } catch (Exception e) { - throw new InvalidKeyException("unknown public key passed to Snova: " + e.getMessage(), e); + throw new InvalidKeyException("unknown public key passed to Snova: " + e.getMessage()); } } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java index b34b5cb947..f9bf87f060 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java @@ -1,42 +1,24 @@ package org.bouncycastle.tls.crypto.impl.jcajce; -import org.bouncycastle.crypto.AsymmetricCipherKeyPair; -import org.bouncycastle.crypto.SecretWithEncapsulation; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.PublicKey; + +import javax.crypto.KeyGenerator; + import org.bouncycastle.jcajce.SecretKeyWithEncapsulation; -import org.bouncycastle.jcajce.provider.asymmetric.mlkem.BCMLKEMPrivateKey; import org.bouncycastle.jcajce.provider.asymmetric.mlkem.BCMLKEMPublicKey; import org.bouncycastle.jcajce.spec.KEMExtractSpec; import org.bouncycastle.jcajce.spec.KEMGenerateSpec; -import org.bouncycastle.jcajce.spec.KEMParameterSpec; -import org.bouncycastle.jcajce.spec.KTSParameterSpec; -import org.bouncycastle.jcajce.spec.MLKEMParameterSpec; -import org.bouncycastle.pqc.crypto.mlkem.MLKEMExtractor; -import org.bouncycastle.pqc.crypto.mlkem.MLKEMGenerator; -import org.bouncycastle.pqc.crypto.mlkem.MLKEMKeyGenerationParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters; -import org.bouncycastle.pqc.crypto.mlkem.MLKEMPrivateKeyParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMPublicKeyParameters; import org.bouncycastle.tls.NamedGroup; import org.bouncycastle.tls.crypto.TlsAgreement; import org.bouncycastle.tls.crypto.TlsKemConfig; import org.bouncycastle.tls.crypto.TlsKemDomain; -import org.bouncycastle.util.encoders.Hex; - -import javax.crypto.Cipher; -import javax.crypto.KeyGenerator; -import javax.crypto.spec.SecretKeySpec; -import java.nio.charset.StandardCharsets; -import java.security.AlgorithmParameters; -import java.security.GeneralSecurityException; -import java.security.InvalidAlgorithmParameterException; -import java.security.Key; -import java.security.KeyPair; -import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; -import java.security.PrivateKey; -import java.security.PublicKey; -import java.security.SecureRandom; public class JceTlsMLKemDomain implements TlsKemDomain { @@ -75,7 +57,7 @@ public JceTlsMLKemDomain(JcaTlsCrypto crypto, TlsKemConfig kemConfig) this.isServer = kemConfig.isServer(); try { - this.keyGen = keyGen = crypto.getHelper().createKeyGenerator(domainParameters.getName()); + this.keyGen = crypto.getHelper().createKeyGenerator(domainParameters.getName()); } catch (NoSuchAlgorithmException e) { diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java index f7fb9506d3..ce55c02524 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java @@ -1,12 +1,11 @@ package org.bouncycastle.tls.crypto.impl.jcajce; -import org.bouncycastle.jcajce.spec.MLKEMParameterSpec; - import java.security.AlgorithmParameters; -import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; +import org.bouncycastle.util.Exceptions; + class KemUtil { static AlgorithmParameters getAlgorithmParameters(JcaTlsCrypto crypto, String kemName) @@ -40,7 +39,7 @@ static Cipher getCipher(JcaTlsCrypto crypto, String kemName) } catch (Exception e) { - throw new IllegalStateException("KEM cipher failed: " + kemName, e); + throw Exceptions.illegalStateException("KEM cipher failed: " + kemName, e); } return null; @@ -52,17 +51,23 @@ static boolean isKemSupported(JcaTlsCrypto crypto, String kemName) return kemName != null && getCipher(crypto, kemName) != null; } + // TODO: not used? static int getEncapsulationLength(String kemName) { - switch (kemName) + if ("ML-KEM-512".equals(kemName)) { - case "ML-KEM-512": return 768; - case "ML-KEM-768": + } + else if ("ML-KEM-768".equals(kemName)) + { return 1088; - case "ML-KEM-1024": + } + else if ("ML-KEM-1024".equals(kemName)) + { return 1568; - default: + } + else + { throw new IllegalArgumentException("unknown kem name " + kemName); } } diff --git a/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index 9c8639254d..9ee1e18b05 100644 --- a/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -58,6 +58,8 @@ import org.bouncycastle.tls.crypto.TlsSecret; import org.bouncycastle.tls.crypto.TlsStreamSigner; import org.bouncycastle.tls.crypto.TlsStreamVerifier; +import org.bouncycastle.tls.crypto.impl.AEADNonceGenerator; +import org.bouncycastle.tls.crypto.impl.AEADNonceGeneratorFactory; import org.bouncycastle.tls.crypto.impl.AbstractTlsCrypto; import org.bouncycastle.tls.crypto.impl.TlsAEADCipher; import org.bouncycastle.tls.crypto.impl.TlsAEADCipherImpl; @@ -75,14 +77,15 @@ /** * Class for providing cryptographic services for TLS based on implementations in the JCA/JCE. *

    - * This class provides default implementations for everything. If you need to customise it, extend the class - * and override the appropriate methods. + * This class provides default implementations for everything. If you need to customise it, extend the class + * and override the appropriate methods. *

    */ public class JcaTlsCrypto extends AbstractTlsCrypto { private final JcaJceHelper helper; + private final JcaJceHelper altHelper; private final SecureRandom entropySource; private final SecureRandom nonceEntropySource; @@ -93,13 +96,28 @@ public class JcaTlsCrypto /** * Base constructor. * - * @param helper a JCA/JCE helper configured for the class's default provider. - * @param entropySource primary entropy source, used for key generation. + * @param helper a JCA/JCE helper configured for the class's default provider. + * @param entropySource primary entropy source, used for key generation. * @param nonceEntropySource secondary entropy source, used for nonce and IV generation. */ protected JcaTlsCrypto(JcaJceHelper helper, SecureRandom entropySource, SecureRandom nonceEntropySource) + { + this(helper, null, entropySource, nonceEntropySource); + } + + /** + * Base constructor. + * + * @param helper a JCA/JCE helper configured for the class's default provider. + * @param altHelper a JCA/JCE helper configured for the class's secondary provider (tried for private keys). + * @param entropySource primary entropy source, used for key generation. + * @param nonceEntropySource secondary entropy source, used for nonce and IV generation. + */ + protected JcaTlsCrypto(JcaJceHelper helper, JcaJceHelper altHelper, SecureRandom entropySource, + SecureRandom nonceEntropySource) { this.helper = helper; + this.altHelper = altHelper; this.entropySource = entropySource; this.nonceEntropySource = nonceEntropySource; } @@ -109,7 +127,8 @@ JceTlsSecret adoptLocalSecret(byte[] data) return new JceTlsSecret(this, data); } - Cipher createRSAEncryptionCipher() throws GeneralSecurityException + Cipher createRSAEncryptionCipher() + throws GeneralSecurityException { try { @@ -324,7 +343,7 @@ public TlsSRP6Client createSRP6Client(TlsSRPConfig srpConfig) final SRP6Client srpClient = new SRP6Client(); BigInteger[] ng = srpConfig.getExplicitNG(); - SRP6Group srpGroup= new SRP6Group(ng[0], ng[1]); + SRP6Group srpGroup = new SRP6Group(ng[0], ng[1]); srpClient.init(srpGroup, createHash(CryptoHashAlgorithm.sha1), this.getSecureRandom()); return new TlsSRP6Client() @@ -353,7 +372,7 @@ public TlsSRP6Server createSRP6Server(TlsSRPConfig srpConfig, BigInteger srpVeri { final SRP6Server srpServer = new SRP6Server(); BigInteger[] ng = srpConfig.getExplicitNG(); - SRP6Group srpGroup= new SRP6Group(ng[0], ng[1]); + SRP6Group srpGroup = new SRP6Group(ng[0], ng[1]); srpServer.init(srpGroup, srpVerifier, createHash(CryptoHashAlgorithm.sha1), this.getSecureRandom()); return new TlsSRP6Server() { @@ -418,7 +437,8 @@ String getHMACAlgorithmName(int cryptoHashAlgorithm) } } - public AlgorithmParameters getNamedGroupAlgorithmParameters(int namedGroup) throws GeneralSecurityException + public AlgorithmParameters getNamedGroupAlgorithmParameters(int namedGroup) + throws GeneralSecurityException { if (NamedGroup.refersToAnXDHCurve(namedGroup)) { @@ -426,7 +446,7 @@ public AlgorithmParameters getNamedGroupAlgorithmParameters(int namedGroup) thro { /* * TODO Return AlgorithmParameters to check against disabled algorithms - * + * * NOTE: The JDK doesn't even support AlgorithmParameters for XDH, so SunJSSE also winds * up using null AlgorithmParameters when checking algorithm constraints. */ @@ -445,19 +465,8 @@ else if (NamedGroup.refersToASpecificFiniteField(namedGroup)) } else if (NamedGroup.refersToASpecificKem(namedGroup)) { - switch (namedGroup) - { - /* - * TODO[tls-kem] Return AlgorithmParameters to check against disabled algorithms? - */ - case NamedGroup.OQS_mlkem512: - case NamedGroup.OQS_mlkem768: - case NamedGroup.OQS_mlkem1024: - case NamedGroup.MLKEM512: - case NamedGroup.MLKEM768: - case NamedGroup.MLKEM1024: - return null; - } + //Note: There is no AlgorithmParametersSpi for ML-KEM + return KemUtil.getAlgorithmParameters(this, NamedGroup.getKemName(namedGroup)); } throw new IllegalArgumentException("NamedGroup not supported: " + NamedGroup.getText(namedGroup)); @@ -582,11 +591,6 @@ public boolean hasECDHAgreement() { return true; } - - public boolean hasKemAgreement() - { - return true; - } public boolean hasEncryptionAlgorithm(int encryptionAlgorithm) { @@ -636,6 +640,11 @@ public boolean hasHKDFAlgorithm(int cryptoHashAlgorithm) } } + public boolean hasKemAgreement() + { + return true; + } + public boolean hasMacAlgorithm(int macAlgorithm) { switch (macAlgorithm) @@ -776,7 +785,7 @@ public boolean hasSignatureScheme(int signatureScheme) switch (signatureScheme) { case SignatureScheme.sm2sig_sm3: - // TODO[tls] Implement before adding + // TODO[tls] Implement before adding case SignatureScheme.DRAFT_mldsa44: case SignatureScheme.DRAFT_mldsa65: case SignatureScheme.DRAFT_mldsa87: @@ -785,7 +794,7 @@ public boolean hasSignatureScheme(int signatureScheme) { short signature = SignatureScheme.getSignatureAlgorithm(signatureScheme); - switch(SignatureScheme.getCryptoHashAlgorithm(signatureScheme)) + switch (SignatureScheme.getCryptoHashAlgorithm(signatureScheme)) { case CryptoHashAlgorithm.md5: return SignatureAlgorithm.rsa == signature && hasSignatureAlgorithm(signature); @@ -857,7 +866,7 @@ public TlsECDomain createECDomain(TlsECConfig ecConfig) return new JceTlsECDomain(this, ecConfig); } } - + public TlsKemDomain createKemDomain(TlsKemConfig kemConfig) { return new JceTlsMLKemDomain(this, kemConfig); @@ -895,8 +904,7 @@ protected TlsAEADCipherImpl createAEADCipher(String cipherName, String algorithm * @throws GeneralSecurityException in case of failure. */ protected TlsBlockCipherImpl createBlockCipher(String cipherName, String algorithm, int keySize, - boolean isEncrypting) - throws GeneralSecurityException + boolean isEncrypting) throws GeneralSecurityException { return new JceBlockCipherImpl(this, helper.createCipher(cipherName), algorithm, keySize, isEncrypting); } @@ -912,8 +920,7 @@ protected TlsBlockCipherImpl createBlockCipher(String cipherName, String algorit * @throws GeneralSecurityException in case of failure. */ protected TlsBlockCipherImpl createBlockCipherWithCBCImplicitIV(String cipherName, String algorithm, int keySize, - boolean isEncrypting) - throws GeneralSecurityException + boolean isEncrypting) throws GeneralSecurityException { return new JceBlockCipherWithCBCImplicitIVImpl(this, helper.createCipher(cipherName), algorithm, isEncrypting); } @@ -936,7 +943,7 @@ protected TlsHash createHash(String digestName) * * @param macAlgorithm the name of the algorithm supporting the MAC. * @return a null cipher suite implementation. - * @throws IOException in case of failure. + * @throws IOException in case of failure. * @throws GeneralSecurityException in case of a specific failure in the JCA/JCE layer. */ protected TlsNullCipher createNullCipher(TlsCryptoParameters cryptoParams, int macAlgorithm) @@ -957,32 +964,77 @@ protected TlsStreamSigner createStreamSigner(SignatureAndHashAlgorithm algorithm protected TlsStreamSigner createStreamSigner(String algorithmName, AlgorithmParameterSpec parameter, PrivateKey privateKey, boolean needsRandom) throws IOException { + SecureRandom random = needsRandom ? getSecureRandom() : null; + try { - SecureRandom random = needsRandom ? getSecureRandom() : null; - - JcaJceHelper helper = getHelper(); - try { - if (null != parameter) + return createStreamSigner(getHelper(), algorithmName, parameter, privateKey, random); + } + catch (InvalidKeyException e) + { + JcaJceHelper altHelper = getAltHelper(); + if (altHelper == null) { - Signature dummySigner = helper.createSignature(algorithmName); - dummySigner.initSign(privateKey, random); - helper = new ProviderJcaJceHelper(dummySigner.getProvider()); + throw e; } - Signature signer = helper.createSignature(algorithmName); - if (null != parameter) + return createStreamSigner(altHelper, algorithmName, parameter, privateKey, random); + } + } + catch (GeneralSecurityException e) + { + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + } + + protected TlsStreamSigner createStreamSigner(JcaJceHelper helper, String algorithmName, + AlgorithmParameterSpec parameter, PrivateKey privateKey, SecureRandom random) throws GeneralSecurityException + { + try + { + if (null != parameter) + { + Signature dummySigner; + try { - signer.setParameter(parameter); + dummySigner = helper.createSignature(algorithmName); } - signer.initSign(privateKey, random); - return new JcaTlsStreamSigner(signer); + catch (NoSuchAlgorithmException e) + { +// // more PKCS#11 mischief +// String upperAlg = Strings.toUpperCase(algorithmName); +// if (upperAlg.endsWith("ANDMGF1")) +// { +// // ANDMGF1 has vanished from the Sun PKCS11 provider. +// algorithmName = upperAlg.replace("ANDMGF1", "SSA-PSS"); +// dummySigner = helper.createSignature(algorithmName); +// } +// else +// { + throw e; +// } + } + + dummySigner.initSign(privateKey, random); + helper = new ProviderJcaJceHelper(dummySigner.getProvider()); } - catch (InvalidKeyException e) + + Signature signer = helper.createSignature(algorithmName); + if (null != parameter) { - // not a concern in 1.4 it's all over if we get here. + signer.setParameter(parameter); + } + signer.initSign(privateKey, random); + return new JcaTlsStreamSigner(signer); + } + catch (InvalidKeyException e) + { + // not a concern in 1.4 it's all over if we get here. +// String upperAlg = Strings.toUpperCase(algorithmName); +// if (upperAlg.endsWith("ANDMGF1")) +// { // String upperAlg = Strings.toUpperCase(algorithmName); // if (upperAlg.endsWith("MGF1")) // { @@ -994,15 +1046,12 @@ protected TlsStreamSigner createStreamSigner(String algorithmName, AlgorithmPara // { throw e; // } - } - } - catch (GeneralSecurityException e) - { - throw new TlsFatalAlert(AlertDescription.internal_error, e); +// } } } - protected TlsStreamVerifier createStreamVerifier(DigitallySigned digitallySigned, PublicKey publicKey) throws IOException + protected TlsStreamVerifier createStreamVerifier(DigitallySigned digitallySigned, PublicKey publicKey) + throws IOException { String algorithmName = JcaUtils.getJcaAlgorithmName(digitallySigned.getAlgorithm()); @@ -1160,8 +1209,7 @@ protected Boolean isSupportedNamedGroup(int namedGroup) } else if (NamedGroup.refersToASpecificKem(namedGroup)) { - // TODO[tls-kem] When implemented via provider, need to check for support dynamically - return Boolean.TRUE; + return Boolean.valueOf(KemUtil.isKemSupported(this, NamedGroup.getKemName(namedGroup))); } else if (NamedGroup.refersToAnECDSACurve(namedGroup)) { @@ -1214,6 +1262,11 @@ public JcaJceHelper getHelper() return helper; } + public JcaJceHelper getAltHelper() + { + return altHelper; + } + protected TlsBlockCipherImpl createCBCBlockCipherImpl(TlsCryptoParameters cryptoParams, String algorithm, int cipherKeySize, boolean forEncryption) throws GeneralSecurityException { @@ -1233,7 +1286,7 @@ private TlsCipher createChaCha20Poly1305(TlsCryptoParameters cryptoParams) throws IOException, GeneralSecurityException { return new TlsAEADCipher(cryptoParams, new JceChaCha20Poly1305(this, helper, true), - new JceChaCha20Poly1305(this, helper, false), 32, 16, TlsAEADCipher.AEAD_CHACHA20_POLY1305); + new JceChaCha20Poly1305(this, helper, false), 32, 16, TlsAEADCipher.AEAD_CHACHA20_POLY1305, null); } private TlsAEADCipher createCipher_AES_CCM(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize) @@ -1241,7 +1294,7 @@ private TlsAEADCipher createCipher_AES_CCM(TlsCryptoParameters cryptoParams, int { return new TlsAEADCipher(cryptoParams, createAEADCipher("AES/CCM/NoPadding", "AES", cipherKeySize, true), createAEADCipher("AES/CCM/NoPadding", "AES", cipherKeySize, false), cipherKeySize, macSize, - TlsAEADCipher.AEAD_CCM); + TlsAEADCipher.AEAD_CCM, null); } private TlsAEADCipher createCipher_AES_GCM(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize) @@ -1249,7 +1302,7 @@ private TlsAEADCipher createCipher_AES_GCM(TlsCryptoParameters cryptoParams, int { return new TlsAEADCipher(cryptoParams, createAEADCipher("AES/GCM/NoPadding", "AES", cipherKeySize, true), createAEADCipher("AES/GCM/NoPadding", "AES", cipherKeySize, false), cipherKeySize, macSize, - TlsAEADCipher.AEAD_GCM); + TlsAEADCipher.AEAD_GCM, getFipsGCMNonceGeneratorFactory()); } private TlsAEADCipher createCipher_ARIA_GCM(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize) @@ -1257,7 +1310,7 @@ private TlsAEADCipher createCipher_ARIA_GCM(TlsCryptoParameters cryptoParams, in { return new TlsAEADCipher(cryptoParams, createAEADCipher("ARIA/GCM/NoPadding", "ARIA", cipherKeySize, true), createAEADCipher("ARIA/GCM/NoPadding", "ARIA", cipherKeySize, false), cipherKeySize, macSize, - TlsAEADCipher.AEAD_GCM); + TlsAEADCipher.AEAD_GCM, getFipsGCMNonceGeneratorFactory()); } private TlsAEADCipher createCipher_Camellia_GCM(TlsCryptoParameters cryptoParams, int cipherKeySize, int macSize) @@ -1266,7 +1319,7 @@ private TlsAEADCipher createCipher_Camellia_GCM(TlsCryptoParameters cryptoParams return new TlsAEADCipher(cryptoParams, createAEADCipher("Camellia/GCM/NoPadding", "Camellia", cipherKeySize, true), createAEADCipher("Camellia/GCM/NoPadding", "Camellia", cipherKeySize, false), cipherKeySize, macSize, - TlsAEADCipher.AEAD_GCM); + TlsAEADCipher.AEAD_GCM, getFipsGCMNonceGeneratorFactory()); } protected TlsCipher createCipher_CBC(TlsCryptoParameters cryptoParams, String algorithm, int cipherKeySize, @@ -1287,7 +1340,7 @@ private TlsAEADCipher createCipher_SM4_CCM(TlsCryptoParameters cryptoParams) int cipherKeySize = 16, macSize = 16; return new TlsAEADCipher(cryptoParams, createAEADCipher("SM4/CCM/NoPadding", "SM4", cipherKeySize, true), createAEADCipher("SM4/CCM/NoPadding", "SM4", cipherKeySize, false), cipherKeySize, macSize, - TlsAEADCipher.AEAD_CCM); + TlsAEADCipher.AEAD_CCM, null); } private TlsAEADCipher createCipher_SM4_GCM(TlsCryptoParameters cryptoParams) @@ -1296,7 +1349,27 @@ private TlsAEADCipher createCipher_SM4_GCM(TlsCryptoParameters cryptoParams) int cipherKeySize = 16, macSize = 16; return new TlsAEADCipher(cryptoParams, createAEADCipher("SM4/GCM/NoPadding", "SM4", cipherKeySize, true), createAEADCipher("SM4/GCM/NoPadding", "SM4", cipherKeySize, false), cipherKeySize, macSize, - TlsAEADCipher.AEAD_GCM); + TlsAEADCipher.AEAD_GCM, getFipsGCMNonceGeneratorFactory()); + } + + /** + * Optionally return an {@link AEADNonceGeneratorFactory} that creates {@link AEADNonceGenerator} + * instances suitable for generating TLS 1.2 GCM nonces in a FIPS approved way (or null). It is not needed + * or intended to be used in a non-FIPS context. + *

    + * Clients of this {@link JcaTlsCrypto} instance MAY assume from a non-null return value that the + * resulting {@link AEADNonceGenerator} implementation(s) are FIPS compliant; implementations that violate + * this assumption risk FIPS compliance failures. + *

    + * In particular, when BCJSSE is configured in FIPS mode, GCM cipher suites are enabled for TLS 1.2 if + * (and only if) a call to this method returns a non-null value. This can be achieved by configuring + * BCJSSE with a user-defined {@link JcaTlsCryptoProvider} subclass, which in turn creates instances of a + * {@link JcaTlsCrypto} subclass, with this method overridden to return a suitable + * {@link AEADNonceGeneratorFactory}. + */ + public AEADNonceGeneratorFactory getFipsGCMNonceGeneratorFactory() + { + return GCMFipsUtil.getDefaultFipsGCMNonceGeneratorFactory(); } String getDigestName(int cryptoHashAlgorithm) From afbf75c5ce7fcff6eec88279e04c2b1d3e5c17ad Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 25 May 2025 08:37:22 +1000 Subject: [PATCH 363/890] Typo and cast fix. --- .../bouncycastle/openpgp/api/OpenPGPMessageInputStream.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageInputStream.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageInputStream.java index a89b1de0ba..6601c07e35 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageInputStream.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageInputStream.java @@ -741,7 +741,7 @@ void addSignatures(PGPSignatureList signatures) { for (Iterator it = signatures.iterator(); it.hasNext();) { - PGPSignature signature = it.next(); + PGPSignature signature = (PGPSignature)it.next(); this.signatures.add(signature); } } @@ -751,7 +751,7 @@ void init(OpenPGPMessageProcessor processor) for (Iterator it = onePassSignatures.iterator(); it.hasNext();) { - PGPOnePassSignature onePassSignature = (PGPOnePassSignature)it.next(); + PGPOnePassSignature ops = (PGPOnePassSignature)it.next(); KeyIdentifier identifier = ops.getKeyIdentifier(); OpenPGPCertificate cert = processor.provideCertificate(identifier); if (cert == null) From 1bfddfe9112e503fc0e75e5372c9506b6c89e2e4 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 25 May 2025 10:43:55 +1000 Subject: [PATCH 364/890] Java 5 to Java 8 compatibility fixes. --- .../openpgp/api/OpenPGPKeyGenerator.java | 2 +- .../openpgp/api/OpenPGPKeyMaterialPool.java | 6 +- .../openpgp/api/OpenPGPMessageGenerator.java | 4 +- .../openpgp/api/SignatureParameters.java | 97 ++-- .../openpgp/api/OpenPGPDefaultPolicy.java | 479 ++++++++++++++++++ .../openpgp/api/OpenPGPPolicy.java | 228 +++++++++ .../openpgp/api/SignatureParameters.java | 331 ++++++++++++ .../api/test/OpenPGPCertificateTest.java | 4 +- ...OpenPGPDetachedSignatureProcessorTest.java | 16 +- .../api/test/OpenPGPKeyEditorTest.java | 6 +- .../api/test/OpenPGPMessageGeneratorTest.java | 75 +-- .../api/test/OpenPGPMessageProcessorTest.java | 81 ++- .../api/test/OpenPGPV4KeyGenerationTest.java | 2 +- .../api/test/OpenPGPV6KeyGeneratorTest.java | 6 +- .../test/StackMessagePassphraseCallback.java | 6 +- .../cms/test/NewSignedDataTest.java | 2 +- 16 files changed, 1228 insertions(+), 117 deletions(-) create mode 100644 pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/OpenPGPDefaultPolicy.java create mode 100644 pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/OpenPGPPolicy.java create mode 100644 pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/SignatureParameters.java diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java index 7fb7fc235c..cdcaa774de 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java @@ -230,7 +230,7 @@ public WithPrimaryKey signOnlyKey() { return withPrimaryKey( KeyPairGeneratorCallback.primaryKey(), - SignatureParameters.Callback.modifyHashedSubpackets(new SignatureSubpacketsFunction() + SignatureParameters.Callback.Util.modifyHashedSubpackets(new SignatureSubpacketsFunction() { @Override public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java index e033f820ef..74d102a8ac 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java @@ -6,6 +6,7 @@ import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; +import java.util.stream.Stream; import org.bouncycastle.bcpg.KeyIdentifier; @@ -121,9 +122,8 @@ public OpenPGPKeyMaterialPool addItem(M item) */ public Collection getAllItems() { - return pool.values().stream() - .distinct() - .collect(Collectors.toList()); + Stream distinct = pool.values().stream().distinct(); + return distinct.collect(Collectors.toList()); } /** diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java index 7da170c074..a6369b387a 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java @@ -414,11 +414,11 @@ public OutputStream get(OutputStream o) */ private void applyLiteralDataWrap(OpenPGPMessageOutputStream.Builder builder) { - PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator(); + final PGPLiteralDataGenerator litGen = new PGPLiteralDataGenerator(); builder.literalData(new OpenPGPMessageOutputStream.OutputStreamFactory() { @Override - public OutputStream get(OutputStream o) + public OutputStream get(final OutputStream o) throws PGPException, IOException { try diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureParameters.java b/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureParameters.java index 02408b62e0..61e30b923b 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureParameters.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureParameters.java @@ -1,12 +1,12 @@ package org.bouncycastle.openpgp.api; +import java.util.Date; +import java.util.Objects; + import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; import org.bouncycastle.util.Arrays; -import java.util.Date; -import java.util.Objects; - /** * Parameters for signature generation. * Some signature builders allow the user to pass in a {@link Callback}, which can be used to modify @@ -37,14 +37,14 @@ private SignatureParameters(int... allowedSignatureTypes) * @param policy algorithm policy * @return parameters * @see - * OpenPGP Web-of-Trust + * OpenPGP Web-of-Trust */ public static SignatureParameters directKeySignature(OpenPGPPolicy policy) { return new SignatureParameters(PGPSignature.DIRECT_KEY) - .setSignatureType(PGPSignature.DIRECT_KEY) - .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) - .setSignatureCreationTime(new Date()); + .setSignatureType(PGPSignature.DIRECT_KEY) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); } /** @@ -60,9 +60,9 @@ public static SignatureParameters directKeySignature(OpenPGPPolicy policy) public static SignatureParameters keyRevocation(OpenPGPPolicy policy) { return new SignatureParameters(PGPSignature.KEY_REVOCATION) - .setSignatureType(PGPSignature.KEY_REVOCATION) - .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) - .setSignatureCreationTime(new Date()); + .setSignatureType(PGPSignature.KEY_REVOCATION) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); } /** @@ -80,13 +80,13 @@ public static SignatureParameters keyRevocation(OpenPGPPolicy policy) public static SignatureParameters certification(OpenPGPPolicy policy) { return new SignatureParameters( - PGPSignature.DEFAULT_CERTIFICATION, - PGPSignature.NO_CERTIFICATION, - PGPSignature.CASUAL_CERTIFICATION, - PGPSignature.POSITIVE_CERTIFICATION) - .setSignatureType(PGPSignature.POSITIVE_CERTIFICATION) - .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) - .setSignatureCreationTime(new Date()); + PGPSignature.DEFAULT_CERTIFICATION, + PGPSignature.NO_CERTIFICATION, + PGPSignature.CASUAL_CERTIFICATION, + PGPSignature.POSITIVE_CERTIFICATION) + .setSignatureType(PGPSignature.POSITIVE_CERTIFICATION) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); } /** @@ -98,9 +98,9 @@ public static SignatureParameters certification(OpenPGPPolicy policy) public static SignatureParameters subkeyBinding(OpenPGPPolicy policy) { return new SignatureParameters(PGPSignature.SUBKEY_BINDING) - .setSignatureType(PGPSignature.SUBKEY_BINDING) - .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) - .setSignatureCreationTime(new Date()); + .setSignatureType(PGPSignature.SUBKEY_BINDING) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); } /** @@ -112,9 +112,9 @@ public static SignatureParameters subkeyBinding(OpenPGPPolicy policy) public static SignatureParameters subkeyRevocation(OpenPGPPolicy policy) { return new SignatureParameters(PGPSignature.SUBKEY_REVOCATION) - .setSignatureType(PGPSignature.SUBKEY_REVOCATION) - .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) - .setSignatureCreationTime(new Date()); + .setSignatureType(PGPSignature.SUBKEY_REVOCATION) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); } /** @@ -126,9 +126,9 @@ public static SignatureParameters subkeyRevocation(OpenPGPPolicy policy) public static SignatureParameters primaryKeyBinding(OpenPGPPolicy policy) { return new SignatureParameters(PGPSignature.PRIMARYKEY_BINDING) - .setSignatureType(PGPSignature.PRIMARYKEY_BINDING) - .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) - .setSignatureCreationTime(new Date()); + .setSignatureType(PGPSignature.PRIMARYKEY_BINDING) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); } /** @@ -140,9 +140,9 @@ public static SignatureParameters primaryKeyBinding(OpenPGPPolicy policy) public static SignatureParameters certificationRevocation(OpenPGPPolicy policy) { return new SignatureParameters(PGPSignature.CERTIFICATION_REVOCATION) - .setSignatureType(PGPSignature.CERTIFICATION_REVOCATION) - .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) - .setSignatureCreationTime(new Date()); + .setSignatureType(PGPSignature.CERTIFICATION_REVOCATION) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); } /** @@ -156,9 +156,9 @@ public static SignatureParameters certificationRevocation(OpenPGPPolicy policy) public static SignatureParameters dataSignature(OpenPGPPolicy policy) { return new SignatureParameters(PGPSignature.BINARY_DOCUMENT, PGPSignature.CANONICAL_TEXT_DOCUMENT) - .setSignatureType(PGPSignature.BINARY_DOCUMENT) - .setSignatureHashAlgorithm(policy.getDefaultDocumentSignatureHashAlgorithm()) - .setSignatureCreationTime(new Date()); + .setSignatureType(PGPSignature.BINARY_DOCUMENT) + .setSignatureHashAlgorithm(policy.getDefaultDocumentSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); } /** @@ -201,7 +201,7 @@ public int getSignatureType() public SignatureParameters setSignatureCreationTime(Date signatureCreationTime) { this.signatureCreationTime = Objects.requireNonNull(signatureCreationTime, - "Signature creation time cannot be null."); + "Signature creation time cannot be null."); return this; } @@ -309,23 +309,26 @@ default SignatureParameters apply(SignatureParameters parameters) return parameters; } - /** - * Shortcut method returning a {@link Callback} which only applies the given - * {@link SignatureSubpacketsFunction} to the hashed signature subpacket area of a signature. - * - * @param function signature subpackets function to apply to the hashed area - * @return callback - */ - static Callback modifyHashedSubpackets(SignatureSubpacketsFunction function) + static class Util { - return new Callback() + /** + * Shortcut method returning a {@link Callback} which only applies the given + * {@link SignatureSubpacketsFunction} to the hashed signature subpacket area of a signature. + * + * @param function signature subpackets function to apply to the hashed area + * @return callback + */ + public static Callback modifyHashedSubpackets(SignatureSubpacketsFunction function) { - @Override - public SignatureParameters apply(SignatureParameters parameters) + return new Callback() { - return parameters.setHashedSubpacketsFunction(function); - } - }; + @Override + public SignatureParameters apply(SignatureParameters parameters) + { + return parameters.setHashedSubpacketsFunction(function); + } + }; + } } } } diff --git a/pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/OpenPGPDefaultPolicy.java b/pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/OpenPGPDefaultPolicy.java new file mode 100644 index 0000000000..7cc3d037cb --- /dev/null +++ b/pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/OpenPGPDefaultPolicy.java @@ -0,0 +1,479 @@ +package org.bouncycastle.openpgp.api; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import org.bouncycastle.bcpg.HashAlgorithmTags; +import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; +import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; + +import org.bouncycastle.bcpg.SignatureSubpacket; +import org.bouncycastle.bcpg.sig.NotationData; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; +import org.bouncycastle.openpgp.api.util.UTCUtil; + +public class OpenPGPDefaultPolicy + implements OpenPGPPolicy +{ + private final Map documentHashAlgorithmCutoffDates = new HashMap(); + private final Map certificateHashAlgorithmCutoffDates = new HashMap(); + private final Map symmetricKeyAlgorithmCutoffDates = new HashMap(); + private final Map publicKeyMinimalBitStrengths = new HashMap(); + private int defaultDocumentSignatureHashAlgorithm = HashAlgorithmTags.SHA512; + private int defaultCertificationSignatureHashAlgorithm = HashAlgorithmTags.SHA512; + private int defaultSymmetricKeyAlgorithm = SymmetricKeyAlgorithmTags.AES_128; + + public OpenPGPDefaultPolicy() + { + /* + * Certification Signature Hash Algorithms + */ + setDefaultCertificationSignatureHashAlgorithm(HashAlgorithmTags.SHA512); + // SHA-3 + acceptCertificationSignatureHashAlgorithm(HashAlgorithmTags.SHA3_512); + acceptCertificationSignatureHashAlgorithm(HashAlgorithmTags.SHA3_256); + // SHA-2 + acceptCertificationSignatureHashAlgorithm(HashAlgorithmTags.SHA512); + acceptCertificationSignatureHashAlgorithm(HashAlgorithmTags.SHA384); + acceptCertificationSignatureHashAlgorithm(HashAlgorithmTags.SHA256); + acceptCertificationSignatureHashAlgorithm(HashAlgorithmTags.SHA224); + // SHA-1 + acceptCertificationSignatureHashAlgorithmUntil(HashAlgorithmTags.SHA1, UTCUtil.parse("2023-02-01 00:00:00 UTC")); + + acceptCertificationSignatureHashAlgorithmUntil(HashAlgorithmTags.RIPEMD160, UTCUtil.parse("2023-02-01 00:00:00 UTC")); + acceptCertificationSignatureHashAlgorithmUntil(HashAlgorithmTags.MD5, UTCUtil.parse("1997-02-01 00:00:00 UTC")); + + /* + * Document Signature Hash Algorithms + */ + setDefaultDocumentSignatureHashAlgorithm(HashAlgorithmTags.SHA512); + // SHA-3 + acceptDocumentSignatureHashAlgorithm(HashAlgorithmTags.SHA3_512); + acceptDocumentSignatureHashAlgorithm(HashAlgorithmTags.SHA3_256); + // SHA-2 + acceptDocumentSignatureHashAlgorithm(HashAlgorithmTags.SHA512); + acceptDocumentSignatureHashAlgorithm(HashAlgorithmTags.SHA384); + acceptDocumentSignatureHashAlgorithm(HashAlgorithmTags.SHA256); + acceptDocumentSignatureHashAlgorithm(HashAlgorithmTags.SHA224); + + /* + * Symmetric Key Algorithms + */ + setDefaultSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.AES_128); + acceptSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.AES_256); + acceptSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.AES_192); + acceptSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.AES_128); + acceptSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.TWOFISH); + acceptSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.CAMELLIA_256); + acceptSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.CAMELLIA_192); + acceptSymmetricKeyAlgorithm(SymmetricKeyAlgorithmTags.CAMELLIA_128); + + /* + * Public Key Algorithms and key strengths + */ + acceptPublicKeyAlgorithmWithMinimalStrength(PublicKeyAlgorithmTags.RSA_GENERAL, 2000); + acceptPublicKeyAlgorithmWithMinimalStrength(PublicKeyAlgorithmTags.RSA_ENCRYPT, 2000); + acceptPublicKeyAlgorithmWithMinimalStrength(PublicKeyAlgorithmTags.RSA_SIGN, 2000); + + acceptPublicKeyAlgorithmWithMinimalStrength(PublicKeyAlgorithmTags.ECDSA, 250); + acceptPublicKeyAlgorithmWithMinimalStrength(PublicKeyAlgorithmTags.EDDSA_LEGACY, 250); + acceptPublicKeyAlgorithmWithMinimalStrength(PublicKeyAlgorithmTags.ECDH, 250); + + acceptPublicKeyAlgorithm(PublicKeyAlgorithmTags.X25519); + acceptPublicKeyAlgorithm(PublicKeyAlgorithmTags.X448); + acceptPublicKeyAlgorithm(PublicKeyAlgorithmTags.Ed25519); + acceptPublicKeyAlgorithm(PublicKeyAlgorithmTags.Ed448); + } + + /** + * Return true, if the given {@link PGPPublicKey} is an acceptable signing key. + * Note: Although signing requires a secret key, we perform checks on the public part for consistency. + * + * @param key key + * @return true if acceptable signing key + */ + public boolean isAcceptableSigningKey(PGPPublicKey key) + { + return isAcceptablePublicKey(key); + } + + /** + * Return true, if the given {@link PGPPublicKey} is an acceptable signature verification key. + * Note: The asymmetry between this and {@link #isAcceptableSigningKey(PGPPublicKey)} is useful + * to prevent creation of signatures using a legacy key, while still allowing verification of + * signatures made using the same key. + * + * @param key key + * @return true if acceptable verification key + */ + public boolean isAcceptableVerificationKey(PGPPublicKey key) + { + return isAcceptablePublicKey(key); + } + + /** + * Return true, if the given {@link PGPPublicKey} is acceptable for encrypting messages. + * + * @param key key + * @return true if acceptable encryption key + */ + public boolean isAcceptableEncryptionKey(PGPPublicKey key) + { + return isAcceptablePublicKey(key); + } + + /** + * Return true, if the given {@link PGPPublicKey} is acceptable for decrypting messages. + * Note: Although decryption requires a secret key, we perform checks on the public part for consistency. + * The asymmetry between this and {@link #isAcceptableEncryptionKey(PGPPublicKey)} is useful + * to prevent creation of new encrypted messages using a legacy key, while still allowing decryption + * of existing messages using the same key. + * + * @param key key + * @return true if acceptable decryption key + */ + public boolean isAcceptableDecryptionKey(PGPPublicKey key) + { + return isAcceptablePublicKey(key); + } + + /** + * Return true, if the given {@link PGPPublicKey} is acceptable. + * + * @param key key + * @return true if acceptable key + */ + public boolean isAcceptablePublicKey(PGPPublicKey key) + { + return isAcceptablePublicKeyStrength(key.getAlgorithm(), key.getBitStrength()); + } + + /** + * Return true, if the given {@link PGPSignature} is acceptable (uses acceptable hash algorithm, + * does not contain unknown critical notations or subpackets). + * Note: A signature being acceptable does NOT mean that it is correct or valid. + * + * @param signature signature + * @return true if acceptable + */ + public boolean isAcceptableSignature(PGPSignature signature) + { + return hasAcceptableSignatureHashAlgorithm(signature) && + hasNoCriticalUnknownNotations(signature) && + hasNoCriticalUnknownSubpackets(signature); + } + + /** + * Return true, if the given {@link PGPSignature} was made using an acceptable signature hash algorithm. + * + * @param signature signature + * @return true if hash algorithm is acceptable + */ + public boolean hasAcceptableSignatureHashAlgorithm(PGPSignature signature) + { + switch (signature.getSignatureType()) + { + case PGPSignature.DEFAULT_CERTIFICATION: + case PGPSignature.NO_CERTIFICATION: + case PGPSignature.CASUAL_CERTIFICATION: + case PGPSignature.POSITIVE_CERTIFICATION: + case PGPSignature.DIRECT_KEY: + case PGPSignature.SUBKEY_BINDING: + case PGPSignature.PRIMARYKEY_BINDING: + return hasAcceptableCertificationSignatureHashAlgorithm(signature); + + case PGPSignature.CERTIFICATION_REVOCATION: + case PGPSignature.KEY_REVOCATION: + case PGPSignature.SUBKEY_REVOCATION: + return hasAcceptableRevocationSignatureHashAlgorithm(signature); + + case PGPSignature.BINARY_DOCUMENT: + case PGPSignature.CANONICAL_TEXT_DOCUMENT: + default: + return hasAcceptableDocumentSignatureHashAlgorithm(signature); + } + } + + /** + * Return true, if the {@link PGPSignature} uses an acceptable data/document signature hash algorithm. + * + * @param signature data / document signature + * @return true if hash algorithm is acceptable + */ + public boolean hasAcceptableDocumentSignatureHashAlgorithm(PGPSignature signature) + { + return isAcceptableDocumentSignatureHashAlgorithm(signature.getHashAlgorithm(), signature.getCreationTime()); + } + + /** + * Return true, if the {@link PGPSignature} uses an acceptable revocation signature hash algorithm. + * + * @param signature revocation signature + * @return true if hash algorithm is acceptable + */ + public boolean hasAcceptableRevocationSignatureHashAlgorithm(PGPSignature signature) + { + return isAcceptableRevocationSignatureHashAlgorithm(signature.getHashAlgorithm(), signature.getCreationTime()); + } + + /** + * Return true, if the {@link PGPSignature} uses an acceptable certification signature hash algorithm. + * + * @param signature certification signature + * @return true if hash algorithm is acceptable + */ + public boolean hasAcceptableCertificationSignatureHashAlgorithm(PGPSignature signature) + { + return isAcceptableCertificationSignatureHashAlgorithm(signature.getHashAlgorithm(), signature.getCreationTime()); + } + + /** + * Return true, if the hashed subpacket area of the signature does NOT contain unknown critical notations. + * + * @param signature signature + * @return true if signature is free from unknown critical notations + */ + public boolean hasNoCriticalUnknownNotations(PGPSignature signature) + { + PGPSignatureSubpacketVector hashedSubpackets = signature.getHashedSubPackets(); + if (hashedSubpackets == null) + { + return true; + } + + OpenPGPNotationRegistry registry = getNotationRegistry(); + + NotationData[] notations = hashedSubpackets.getNotationDataOccurrences(); + for (NotationData notation : notations) + { + if (notation.isCritical() && !registry.isNotationKnown(notation.getNotationName())) + { + return false; + } + } + return true; + } + + /** + * Return true, if the hashed subpacket area of the signature does NOT contain unknown critical subpackets. + * + * @param signature signature + * @return true if signature is free from unknown critical subpackets + */ + public boolean hasNoCriticalUnknownSubpackets(PGPSignature signature) + { + PGPSignatureSubpacketVector hashedSubpackets = signature.getHashedSubPackets(); + if (hashedSubpackets == null) + { + return true; + } + + for (SignatureSubpacket subpacket : hashedSubpackets.toArray()) + { + if (subpacket.isCritical() && + // only consider subpackets which are not recognized by SignatureSubpacketInputStream + subpacket.getClass().equals(SignatureSubpacket.class)) + { + if (!isKnownSignatureSubpacket(subpacket.getType())) + { + return false; + } + } + } + return true; + } + + /** + * Return true, if the given signature subpacket ID is known by the implementation. + * Note: This method is only called for subpackets not recognized by + * {@link org.bouncycastle.bcpg.SignatureSubpacketInputStream}. + * + * @param signatureSubpacketTag signature subpacket ID + * @return true if subpacket tag is known + */ + public boolean isKnownSignatureSubpacket(int signatureSubpacketTag) + { + // Overwrite this, allowing custom critical signature subpackets + return false; + } + + public OpenPGPDefaultPolicy rejectHashAlgorithm(int hashAlgorithmId) + { + certificateHashAlgorithmCutoffDates.remove(hashAlgorithmId); + documentHashAlgorithmCutoffDates.remove(hashAlgorithmId); + return this; + } + + public OpenPGPDefaultPolicy acceptCertificationSignatureHashAlgorithm(int hashAlgorithmId) + { + return acceptCertificationSignatureHashAlgorithmUntil(hashAlgorithmId, null); + } + + public OpenPGPDefaultPolicy acceptCertificationSignatureHashAlgorithmUntil(int hashAlgorithmId, Date until) + { + certificateHashAlgorithmCutoffDates.put(hashAlgorithmId, until); + return this; + } + + public OpenPGPDefaultPolicy acceptDocumentSignatureHashAlgorithm(int hashAlgorithmId) + { + return acceptDocumentSignatureHashAlgorithmUntil(hashAlgorithmId, null); + } + + public OpenPGPDefaultPolicy acceptDocumentSignatureHashAlgorithmUntil(int hashAlgorithmId, Date until) + { + documentHashAlgorithmCutoffDates.put(hashAlgorithmId, until); + return this; + } + + public OpenPGPDefaultPolicy rejectSymmetricKeyAlgorithm(int symmetricKeyAlgorithmId) + { + symmetricKeyAlgorithmCutoffDates.remove(symmetricKeyAlgorithmId); + return this; + } + + public OpenPGPDefaultPolicy acceptSymmetricKeyAlgorithm(int symmetricKeyAlgorithmId) + { + return acceptSymmetricKeyAlgorithmUntil(symmetricKeyAlgorithmId, null); + } + + public OpenPGPDefaultPolicy acceptSymmetricKeyAlgorithmUntil(int symmetricKeyAlgorithmId, Date until) + { + symmetricKeyAlgorithmCutoffDates.put(symmetricKeyAlgorithmId, until); + return this; + } + + public OpenPGPDefaultPolicy rejectPublicKeyAlgorithm(int publicKeyAlgorithmId) + { + publicKeyMinimalBitStrengths.remove(publicKeyAlgorithmId); + return this; + } + + public OpenPGPDefaultPolicy acceptPublicKeyAlgorithm(int publicKeyAlgorithmId) + { + publicKeyMinimalBitStrengths.put(publicKeyAlgorithmId, null); + return this; + } + + public OpenPGPDefaultPolicy acceptPublicKeyAlgorithmWithMinimalStrength(int publicKeyAlgorithmId, int minBitStrength) + { + publicKeyMinimalBitStrengths.put(publicKeyAlgorithmId, minBitStrength); + return this; + } + + @Override + public boolean isAcceptableDocumentSignatureHashAlgorithm(int hashAlgorithmId, Date signatureCreationTime) + { + return isAcceptable(hashAlgorithmId, signatureCreationTime, documentHashAlgorithmCutoffDates); + } + + @Override + public boolean isAcceptableRevocationSignatureHashAlgorithm(int hashAlgorithmId, Date signatureCreationTime) + { + return isAcceptable(hashAlgorithmId, signatureCreationTime, certificateHashAlgorithmCutoffDates); + } + + @Override + public boolean isAcceptableCertificationSignatureHashAlgorithm(int hashAlgorithmId, Date signatureCreationTime) + { + return isAcceptable(hashAlgorithmId, signatureCreationTime, certificateHashAlgorithmCutoffDates); + } + + @Override + public int getDefaultCertificationSignatureHashAlgorithm() + { + return defaultCertificationSignatureHashAlgorithm; + } + + public OpenPGPDefaultPolicy setDefaultCertificationSignatureHashAlgorithm(int hashAlgorithmId) + { + defaultCertificationSignatureHashAlgorithm = hashAlgorithmId; + return this; + } + + @Override + public int getDefaultDocumentSignatureHashAlgorithm() + { + return defaultDocumentSignatureHashAlgorithm; + } + + public OpenPGPDefaultPolicy setDefaultDocumentSignatureHashAlgorithm(int hashAlgorithmId) + { + defaultDocumentSignatureHashAlgorithm = hashAlgorithmId; + return this; + } + + @Override + public boolean isAcceptableSymmetricKeyAlgorithm(int symmetricKeyAlgorithmId) + { + return isAcceptable(symmetricKeyAlgorithmId, symmetricKeyAlgorithmCutoffDates); + } + + @Override + public int getDefaultSymmetricKeyAlgorithm() + { + return defaultSymmetricKeyAlgorithm; + } + + public OpenPGPDefaultPolicy setDefaultSymmetricKeyAlgorithm(int symmetricKeyAlgorithmId) + { + defaultSymmetricKeyAlgorithm = symmetricKeyAlgorithmId; + return this; + } + + @Override + public boolean isAcceptablePublicKeyStrength(int publicKeyAlgorithmId, int bitStrength) + { + return isAcceptable(publicKeyAlgorithmId, bitStrength, publicKeyMinimalBitStrengths); + } + + @Override + public OpenPGPNotationRegistry getNotationRegistry() + { + return null; + } + + private boolean isAcceptable(int algorithmId, Date usageDate, Map cutoffTable) + { + if (!cutoffTable.containsKey(algorithmId)) + { + // algorithm is not listed in the map at all + return false; + } + + Date cutoffDate = cutoffTable.get(algorithmId); + if (cutoffDate == null) + { + // no cutoff date given -> algorithm is acceptable indefinitely + return true; + } + + return usageDate.before(cutoffDate); + } + + private boolean isAcceptable(int algorithmId, Map cutoffTable) + { + return cutoffTable.containsKey(algorithmId); + } + + private boolean isAcceptable(int algorithmId, int bitStrength, Map minBitStrengths) + { + if (!minBitStrengths.containsKey(algorithmId)) + { + // algorithm is not listed in the map at all + return false; + } + + Integer minBitStrength = minBitStrengths.get(algorithmId); + if (minBitStrength == null) + { + // no minimal bit strength defined -> accept all strengths + return true; + } + + return bitStrength >= minBitStrength; + } +} diff --git a/pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/OpenPGPPolicy.java b/pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/OpenPGPPolicy.java new file mode 100644 index 0000000000..9a9d1d4c27 --- /dev/null +++ b/pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/OpenPGPPolicy.java @@ -0,0 +1,228 @@ +package org.bouncycastle.openpgp.api; + +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import org.bouncycastle.bcpg.SignatureSubpacket; +import org.bouncycastle.bcpg.sig.NotationData; +import org.bouncycastle.openpgp.PGPPublicKey; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureSubpacketVector; + +/** + * Policy for OpenPGP algorithms and features. + */ +public interface OpenPGPPolicy +{ + /** + * Return true, if the given {@link PGPPublicKey} is an acceptable signing key. + * Note: Although signing requires a secret key, we perform checks on the public part for consistency. + * + * @param key key + * @return true if acceptable signing key + */ + boolean isAcceptableSigningKey(PGPPublicKey key); + + /** + * Return true, if the given {@link PGPPublicKey} is an acceptable signature verification key. + * Note: The asymmetry between this and {@link #isAcceptableSigningKey(PGPPublicKey)} is useful + * to prevent creation of signatures using a legacy key, while still allowing verification of + * signatures made using the same key. + * + * @param key key + * @return true if acceptable verification key + */ + boolean isAcceptableVerificationKey(PGPPublicKey key); + + /** + * Return true, if the given {@link PGPPublicKey} is acceptable for encrypting messages. + * + * @param key key + * @return true if acceptable encryption key + */ + boolean isAcceptableEncryptionKey(PGPPublicKey key); + + /** + * Return true, if the given {@link PGPPublicKey} is acceptable for decrypting messages. + * Note: Although decryption requires a secret key, we perform checks on the public part for consistency. + * The asymmetry between this and {@link #isAcceptableEncryptionKey(PGPPublicKey)} is useful + * to prevent creation of new encrypted messages using a legacy key, while still allowing decryption + * of existing messages using the same key. + * + * @param key key + * @return true if acceptable decryption key + */ + boolean isAcceptableDecryptionKey(PGPPublicKey key); + + /** + * Return true, if the given {@link PGPPublicKey} is acceptable. + * + * @param key key + * @return true if acceptable key + */ + boolean isAcceptablePublicKey(PGPPublicKey key); + + /** + * Return true, if the given {@link PGPSignature} is acceptable (uses acceptable hash algorithm, + * does not contain unknown critical notations or subpackets). + * Note: A signature being acceptable does NOT mean that it is correct or valid. + * + * @param signature signature + * @return true if acceptable + */ + boolean isAcceptableSignature(PGPSignature signature); + + /** + * Return true, if the given {@link PGPSignature} was made using an acceptable signature hash algorithm. + * + * @param signature signature + * @return true if hash algorithm is acceptable + */ + boolean hasAcceptableSignatureHashAlgorithm(PGPSignature signature); + + /** + * Return true, if the {@link PGPSignature} uses an acceptable data/document signature hash algorithm. + * + * @param signature data / document signature + * @return true if hash algorithm is acceptable + */ + boolean hasAcceptableDocumentSignatureHashAlgorithm(PGPSignature signature); + + /** + * Return true, if the {@link PGPSignature} uses an acceptable revocation signature hash algorithm. + * + * @param signature revocation signature + * @return true if hash algorithm is acceptable + */ + boolean hasAcceptableRevocationSignatureHashAlgorithm(PGPSignature signature); + + /** + * Return true, if the {@link PGPSignature} uses an acceptable certification signature hash algorithm. + * + * @param signature certification signature + * @return true if hash algorithm is acceptable + */ + boolean hasAcceptableCertificationSignatureHashAlgorithm(PGPSignature signature); + + /** + * Return true, if the hashed subpacket area of the signature does NOT contain unknown critical notations. + * @param signature signature + * @return true if signature is free from unknown critical notations + */ + boolean hasNoCriticalUnknownNotations(PGPSignature signature); + + /** + * Return true, if the hashed subpacket area of the signature does NOT contain unknown critical subpackets. + * @param signature signature + * @return true if signature is free from unknown critical subpackets + */ + boolean hasNoCriticalUnknownSubpackets(PGPSignature signature); + + /** + * Return true, if the given signature subpacket ID is known by the implementation. + * Note: This method is only called for subpackets not recognized by + * {@link org.bouncycastle.bcpg.SignatureSubpacketInputStream}. + * + * @param signatureSubpacketTag signature subpacket ID + * @return true if subpacket tag is known + */ + boolean isKnownSignatureSubpacket(int signatureSubpacketTag); + + /** + * Return true, if the given hash algorithm is - at signature creation time - an acceptable document signature + * hash algorithm. + * + * @param hashAlgorithmId hash algorithm ID + * @param signatureCreationTime optional signature creation time + * @return true if hash algorithm is acceptable at creation time + */ + boolean isAcceptableDocumentSignatureHashAlgorithm(int hashAlgorithmId, Date signatureCreationTime); + + /** + * Return true, if the given hash algorithm is - at signature creation time - an acceptable revocation signature + * hash algorithm. + * + * @param hashAlgorithmId hash algorithm ID + * @param signatureCreationTime optional signature creation time + * @return true if hash algorithm is acceptable at creation time + */ + boolean isAcceptableRevocationSignatureHashAlgorithm(int hashAlgorithmId, Date signatureCreationTime); + + /** + * Return true, if the given hash algorithm is - at signature creation time - an acceptable certification signature + * hash algorithm. + * + * @param hashAlgorithmId hash algorithm ID + * @param signatureCreationTime optional signature creation time + * @return true if hash algorithm is acceptable at creation time + */ + boolean isAcceptableCertificationSignatureHashAlgorithm(int hashAlgorithmId, Date signatureCreationTime); + + /** + * Return the default certification signature hash algorithm ID. + * This is used as fallback, if negotiation of a commonly supported hash algorithm fails. + * + * @return default certification signature hash algorithm ID + */ + int getDefaultCertificationSignatureHashAlgorithm(); + + /** + * Return the default document signature hash algorithm ID. + * This is used as fallback, if negotiation of a commonly supported hash algorithm fails. + * + * @return default document signature hash algorithm ID + */ + int getDefaultDocumentSignatureHashAlgorithm(); + + /** + * Return true, if the given symmetric-key algorithm is acceptable. + * + * @param symmetricKeyAlgorithmId symmetric-key algorithm + * @return true if symmetric-key algorithm is acceptable + */ + boolean isAcceptableSymmetricKeyAlgorithm(int symmetricKeyAlgorithmId); + + /** + * Return the default symmetric-key algorithm, which is used as a fallback if symmetric encryption algorithm + * negotiation fails. + * + * @return default symmetric-key algorithm + */ + int getDefaultSymmetricKeyAlgorithm(); + + /** + * Return true, if the given bitStrength is acceptable for the given public key algorithm ID. + * + * @param publicKeyAlgorithmId ID of a public key algorithm + * @param bitStrength key bit strength + * @return true if strength is acceptable + */ + boolean isAcceptablePublicKeyStrength(int publicKeyAlgorithmId, int bitStrength); + + /** + * Return the policies {@link OpenPGPNotationRegistry} containing known notation names. + * + * @return notation registry + */ + OpenPGPNotationRegistry getNotationRegistry(); + + /** + * The {@link OpenPGPNotationRegistry} can be used to register known notations, such that signatures containing + * notation instances of the same name, which are marked as critical do not invalidate the signature. + */ + class OpenPGPNotationRegistry + { + private final Set knownNotations = new HashSet(); + + public boolean isNotationKnown(String notationName) + { + return knownNotations.contains(notationName); + } + + public void addKnownNotation(String notationName) + { + this.knownNotations.add(notationName); + } + } +} diff --git a/pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/SignatureParameters.java b/pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/SignatureParameters.java new file mode 100644 index 0000000000..cde370b70c --- /dev/null +++ b/pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/SignatureParameters.java @@ -0,0 +1,331 @@ +package org.bouncycastle.openpgp.api; + +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; +import org.bouncycastle.util.Arrays; + +import java.util.Date; +import java.util.Objects; + +/** + * Parameters for signature generation. + * Some signature builders allow the user to pass in a {@link Callback}, which can be used to modify + * {@link SignatureParameters} instances prior to signature generation. + */ +public class SignatureParameters +{ + private int signatureType; + private Date signatureCreationTime = new Date(); + private int signatureHashAlgorithmId; + private SignatureSubpacketsFunction hashedSubpacketsFunction; + private SignatureSubpacketsFunction unhashedSubpacketsFunction; + + private final int[] allowedSignatureTypes; + + private SignatureParameters(int... allowedSignatureTypes) + { + this.allowedSignatureTypes = allowedSignatureTypes; + } + + /** + * Create default signature parameters object for a direct-key signature. + * When issued as a self-signature, direct-key signatures can be used to store algorithm preferences + * on the key, which apply to the entire certificate (including all subkeys). + * When issued as a third-party signature, direct-key signatures act as delegations, with which for example the + * web-of-trust can be built. + * + * @param policy algorithm policy + * @return parameters + * @see + * OpenPGP Web-of-Trust + */ + public static SignatureParameters directKeySignature(OpenPGPPolicy policy) + { + return new SignatureParameters(PGPSignature.DIRECT_KEY) + .setSignatureType(PGPSignature.DIRECT_KEY) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Create default signature parameters for a key revocation signature. + * When issued as a self-signature, key revocation signatures can be used to revoke an entire certificate. + * To revoke only individual subkeys, see {@link #subkeyRevocation(OpenPGPPolicy)} instead. + * When issued as a third-party signature, key revocation signatures are used to revoke earlier delegation + * signatures. + * + * @param policy algorithm policy + * @return parameters + */ + public static SignatureParameters keyRevocation(OpenPGPPolicy policy) + { + return new SignatureParameters(PGPSignature.KEY_REVOCATION) + .setSignatureType(PGPSignature.KEY_REVOCATION) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Create a default signature parameters object for a certification signature. + * The default signature type is {@link PGPSignature#POSITIVE_CERTIFICATION}, but can be changed to + * {@link PGPSignature#DEFAULT_CERTIFICATION}, {@link PGPSignature#NO_CERTIFICATION}, + * {@link PGPSignature#CASUAL_CERTIFICATION}. + * When issued as a self-signature, certifications can be used to bind user-ids to the certificate. + * When issued as third-party signatures, certificates act as a statement, expressing that the issuer + * is convinced that the user-id "belongs to" the certificate. + * + * @param policy algorithm policy + * @return parameters + */ + public static SignatureParameters certification(OpenPGPPolicy policy) + { + return new SignatureParameters( + PGPSignature.DEFAULT_CERTIFICATION, + PGPSignature.NO_CERTIFICATION, + PGPSignature.CASUAL_CERTIFICATION, + PGPSignature.POSITIVE_CERTIFICATION) + .setSignatureType(PGPSignature.POSITIVE_CERTIFICATION) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Create a default signature parameters object for a subkey binding signature. + * + * @param policy algorithm policy + * @return parameters + */ + public static SignatureParameters subkeyBinding(OpenPGPPolicy policy) + { + return new SignatureParameters(PGPSignature.SUBKEY_BINDING) + .setSignatureType(PGPSignature.SUBKEY_BINDING) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Create default signature parameters for a subkey revocation signature. + * + * @param policy algorithm policy + * @return parameters + */ + public static SignatureParameters subkeyRevocation(OpenPGPPolicy policy) + { + return new SignatureParameters(PGPSignature.SUBKEY_REVOCATION) + .setSignatureType(PGPSignature.SUBKEY_REVOCATION) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Create a default signature parameters object for a primary-key binding (back-sig) signature. + * + * @param policy algorithm policy + * @return parameters + */ + public static SignatureParameters primaryKeyBinding(OpenPGPPolicy policy) + { + return new SignatureParameters(PGPSignature.PRIMARYKEY_BINDING) + .setSignatureType(PGPSignature.PRIMARYKEY_BINDING) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Create a default signature parameters object for a certification-revocation signature. + * + * @param policy algorithm policy + * @return parameters + */ + public static SignatureParameters certificationRevocation(OpenPGPPolicy policy) + { + return new SignatureParameters(PGPSignature.CERTIFICATION_REVOCATION) + .setSignatureType(PGPSignature.CERTIFICATION_REVOCATION) + .setSignatureHashAlgorithm(policy.getDefaultCertificationSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Create a default signature parameters object for a data/document signature. + * The default signature type is {@link PGPSignature#BINARY_DOCUMENT}, but can be changed to + * {@link PGPSignature#CANONICAL_TEXT_DOCUMENT}. + * + * @param policy algorithm policy + * @return parameters + */ + public static SignatureParameters dataSignature(OpenPGPPolicy policy) + { + return new SignatureParameters(PGPSignature.BINARY_DOCUMENT, PGPSignature.CANONICAL_TEXT_DOCUMENT) + .setSignatureType(PGPSignature.BINARY_DOCUMENT) + .setSignatureHashAlgorithm(policy.getDefaultDocumentSignatureHashAlgorithm()) + .setSignatureCreationTime(new Date()); + } + + /** + * Change the signature type of the signature to-be-generated to the given type. + * Depending on which factory method was used to instantiate the signature parameters object, + * only certain signature types are allowed. Passing an illegal signature type causes an + * {@link IllegalArgumentException} to be thrown. + * + * @param signatureType signature type + * @return parameters + * @throws IllegalArgumentException if an illegal signature type is passed + */ + public SignatureParameters setSignatureType(int signatureType) + { + if (!Arrays.contains(allowedSignatureTypes, signatureType)) + { + throw new IllegalArgumentException("Illegal signature type provided."); + } + + this.signatureType = signatureType; + return this; + } + + /** + * Return the signature type for the signature to-be-generated. + * + * @return signature type + */ + public int getSignatureType() + { + return signatureType; + } + + /** + * Change the creation time of the signature to-be-generated. + * + * @param signatureCreationTime signature creation time + * @return parameters + */ + public SignatureParameters setSignatureCreationTime(Date signatureCreationTime) + { + this.signatureCreationTime = Objects.requireNonNull(signatureCreationTime, + "Signature creation time cannot be null."); + return this; + } + + /** + * Return the creation time of the signature to-be-generated. + * + * @return signature creation time + */ + public Date getSignatureCreationTime() + { + return signatureCreationTime; + } + + /** + * Change the hash algorithm for the signature to-be-generated. + * + * @param signatureHashAlgorithmId signature hash algorithm id + * @return parameters + */ + public SignatureParameters setSignatureHashAlgorithm(int signatureHashAlgorithmId) + { + this.signatureHashAlgorithmId = signatureHashAlgorithmId; + return this; + } + + /** + * Return the hash algorithm id of the signature to-be-generated. + * + * @return hash algorithm id + */ + public int getSignatureHashAlgorithmId() + { + return signatureHashAlgorithmId; + } + + /** + * Set a function, which is applied to the hashed subpackets area of the signature to-be-generated. + * + * @param subpacketsFunction function to apply to the hashed signature subpackets + * @return parameters + */ + public SignatureParameters setHashedSubpacketsFunction(SignatureSubpacketsFunction subpacketsFunction) + { + this.hashedSubpacketsFunction = subpacketsFunction; + return this; + } + + /** + * Apply the hashed subpackets function set via {@link #setHashedSubpacketsFunction(SignatureSubpacketsFunction)} + * to the given hashed subpackets. + * + * @param hashedSubpackets hashed signature subpackets + * @return modified hashed subpackets + */ + PGPSignatureSubpacketGenerator applyToHashedSubpackets(PGPSignatureSubpacketGenerator hashedSubpackets) + { + if (hashedSubpacketsFunction != null) + { + return hashedSubpacketsFunction.apply(hashedSubpackets); + } + return hashedSubpackets; + } + + /** + * Set a function, which is applied to the unhashed subpackets area of the signature to-be-generated. + * + * @param subpacketsFunction function to apply to the unhashed signature subpackets + * @return parameters + */ + public SignatureParameters setUnhashedSubpacketsFunction(SignatureSubpacketsFunction subpacketsFunction) + { + this.unhashedSubpacketsFunction = subpacketsFunction; + return this; + } + + /** + * Apply the unhashed subpackets function set via {@link #setUnhashedSubpacketsFunction(SignatureSubpacketsFunction)} + * to the given unhashed subpackets. + * + * @param unhashedSubpackets unhashed signature subpackets + * @return modified unhashed subpackets + */ + PGPSignatureSubpacketGenerator applyToUnhashedSubpackets(PGPSignatureSubpacketGenerator unhashedSubpackets) + { + if (unhashedSubpacketsFunction != null) + { + return unhashedSubpacketsFunction.apply(unhashedSubpackets); + } + return unhashedSubpackets; + } + + /** + * Callback, allowing the user to modify {@link SignatureParameters} before use. + */ + public interface Callback + { + /** + * Apply custom changes to {@link SignatureParameters}. + * + * @param parameters parameters instance + * @return modified parameters, or null + */ + SignatureParameters apply(SignatureParameters parameters); + + static class Util + { + /** + * Shortcut method returning a {@link Callback} which only applies the given + * {@link SignatureSubpacketsFunction} to the hashed signature subpacket area of a signature. + * + * @param function signature subpackets function to apply to the hashed area + * @return callback + */ + public static Callback modifyHashedSubpackets(final SignatureSubpacketsFunction function) + { + return new Callback() + { + @Override + public SignatureParameters apply(SignatureParameters parameters) + { + return parameters.setHashedSubpacketsFunction(function); + } + }; + } + } + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java index 1b797c5b4d..7163fa70b6 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java @@ -798,14 +798,14 @@ private void signatureValidityTest(OpenPGPApi api, String cert, TestSignature... private void testGetPrimaryUserId(OpenPGPApi api) throws PGPException { - Date now = new Date((new Date().getTime() / 1000) * 1000); + final Date now = new Date((new Date().getTime() / 1000) * 1000); Date oneHourAgo = new Date(now.getTime() - 1000 * 60 * 60); OpenPGPKeyGenerator gen = api.generateKey(oneHourAgo); OpenPGPKey key = gen.withPrimaryKey() .addUserId("Old non-primary ") .addUserId("New primary ", - SignatureParameters.Callback.modifyHashedSubpackets(new SignatureSubpacketsFunction() + SignatureParameters.Callback.Util.modifyHashedSubpackets(new SignatureSubpacketsFunction() { @Override public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java index 0cb339b7ee..90e4cccf3d 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java @@ -103,7 +103,7 @@ private void createVerifyV6Signature(OpenPGPApi api) isTrue(verified.get(0).isValid()); } - private void missingPassphraseThrows(OpenPGPApi api) + private void missingPassphraseThrows(final OpenPGPApi api) { isNotNull(testException( "Cannot unlock primary key CB186C4F0609A697E4D52DFA6C722B0C1F1E27C18A56708F6525EC27BAD9ACC9: Exception decrypting key", @@ -121,7 +121,7 @@ public void operation() })); } - private void wrongPassphraseThrows(OpenPGPApi api) + private void wrongPassphraseThrows(final OpenPGPApi api) { isNotNull(testException( "Cannot unlock primary key CB186C4F0609A697E4D52DFA6C722B0C1F1E27C18A56708F6525EC27BAD9ACC9: Exception decrypting key", @@ -183,10 +183,10 @@ private void keyPassphrasesArePairedUpProperly_passphraseAddedFirst(OpenPGPApi a isEquals(1, signatures.size()); } - private void withoutSigningSubkeyFails(OpenPGPApi api) + private void withoutSigningSubkeyFails(final OpenPGPApi api) throws PGPException { - OpenPGPKey noSigningKey = api.generateKey() + final OpenPGPKey noSigningKey = api.generateKey() .withPrimaryKey( new KeyPairGeneratorCallback() { @Override @@ -195,7 +195,7 @@ public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) return generator.generatePrimaryKey(); } }, - SignatureParameters.Callback.modifyHashedSubpackets( + SignatureParameters.Callback.Util.modifyHashedSubpackets( new SignatureSubpacketsFunction() { @Override @@ -226,10 +226,10 @@ public void operation() })); } - private void nonSigningSubkeyFails(OpenPGPApi api) + private void nonSigningSubkeyFails(final OpenPGPApi api) throws PGPException { - OpenPGPKey noSigningKey = api.generateKey() + final OpenPGPKey noSigningKey = api.generateKey() .withPrimaryKey( new KeyPairGeneratorCallback() { @Override @@ -238,7 +238,7 @@ public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) return generator.generatePrimaryKey(); } }, - SignatureParameters.Callback.modifyHashedSubpackets( + SignatureParameters.Callback.Util.modifyHashedSubpackets( new SignatureSubpacketsFunction() { @Override diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPKeyEditorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPKeyEditorTest.java index b2846220c9..90e28f9b32 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPKeyEditorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPKeyEditorTest.java @@ -79,7 +79,7 @@ private void softRevokeUserIdTest(OpenPGPApi api) { OpenPGPKey key = api.readKeyOrCertificate() .parseKey(OpenPGPTestKeys.ALICE_KEY); - Date now = currentTimeRounded(); + final Date now = currentTimeRounded(); Date oneHourAgo = new Date(now.getTime() - (1000 * 60 * 60)); OpenPGPCertificate.OpenPGPUserId userId = key.getPrimaryUserId(now); isNotNull(userId); @@ -115,7 +115,7 @@ private void hardRevokeUserIdTest(OpenPGPApi api) { OpenPGPKey key = api.readKeyOrCertificate() .parseKey(OpenPGPTestKeys.ALICE_KEY); - Date now = currentTimeRounded(); + final Date now = currentTimeRounded(); Date oneHourAgo = new Date(now.getTime() - (1000 * 60 * 60)); OpenPGPCertificate.OpenPGPUserId userId = key.getPrimaryUserId(now); isNotNull(userId); @@ -202,7 +202,7 @@ private void extendExpirationTimeTest(OpenPGPApi api) isEquals("Default key generation MUST set expiration time of +5years", key.getExpirationTime().getTime(), n0.getTime() + 5L * 31536000 * 1000); - Date n1 = new Date(n0.getTime() + 1000); // 1 sec later + final Date n1 = new Date(n0.getTime() + 1000); // 1 sec later key = api.editKey(key) .addDirectKeySignature(new SignatureParameters.Callback() diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java index f6cd62842b..332b4b2129 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java @@ -13,11 +13,12 @@ import org.bouncycastle.openpgp.api.OpenPGPKey; import org.bouncycastle.openpgp.api.OpenPGPMessageGenerator; import org.bouncycastle.openpgp.api.OpenPGPMessageOutputStream; +import org.bouncycastle.openpgp.api.OpenPGPPolicy; import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; public class OpenPGPMessageGeneratorTest - extends APITest + extends APITest { @Override public String getName() @@ -26,7 +27,7 @@ public String getName() } protected void performTestWith(OpenPGPApi api) - throws PGPException, IOException + throws PGPException, IOException { armoredLiteralDataPacket(api); unarmoredLiteralDataPacket(api); @@ -41,10 +42,10 @@ protected void performTestWith(OpenPGPApi api) } private void armoredLiteralDataPacket(OpenPGPApi api) - throws PGPException, IOException + throws PGPException, IOException { OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() - .setAllowPadding(false); + .setAllowPadding(false); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OpenPGPMessageOutputStream msgOut = gen.open(bOut); @@ -54,20 +55,20 @@ private void armoredLiteralDataPacket(OpenPGPApi api) msgOut.close(); String nl = Strings.lineSeparator(); - String expected = - "-----BEGIN PGP MESSAGE-----" + nl + - nl + - "yxNiAAAAAABIZWxsbywgV29ybGQh" + nl + - "-----END PGP MESSAGE-----" + nl; + String expected = + "-----BEGIN PGP MESSAGE-----" + nl + + nl + + "yxNiAAAAAABIZWxsbywgV29ybGQh" + nl + + "-----END PGP MESSAGE-----" + nl; isEquals(expected, bOut.toString()); } private void unarmoredLiteralDataPacket(OpenPGPApi api) - throws PGPException, IOException + throws PGPException, IOException { OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() - .setArmored(false) // disable ASCII armor - .setAllowPadding(false); // disable padding + .setArmored(false) // disable ASCII armor + .setAllowPadding(false); // disable padding ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OpenPGPMessageOutputStream msgOut = gen.open(bOut); @@ -81,11 +82,18 @@ private void unarmoredLiteralDataPacket(OpenPGPApi api) } private void armoredCompressedLiteralDataPacket(OpenPGPApi api) - throws PGPException, IOException + throws PGPException, IOException { OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() - .setAllowPadding(false) - .setCompressionNegotiator((conf, neg) -> CompressionAlgorithmTags.ZIP); + .setAllowPadding(false) + .setCompressionNegotiator(new OpenPGPMessageGenerator.CompressionNegotiator() + { + @Override + public int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPGPPolicy policy) + { + return CompressionAlgorithmTags.ZIP; + } + }); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OpenPGPMessageOutputStream msgOut = gen.open(bOut); @@ -97,20 +105,27 @@ private void armoredCompressedLiteralDataPacket(OpenPGPApi api) String nl = Strings.lineSeparator(); String expected = - "-----BEGIN PGP MESSAGE-----" + nl + - nl + - "yBUBOy2cxAACHqk5Ofk6CuH5RTkpigA=" + nl + - "-----END PGP MESSAGE-----" + nl; + "-----BEGIN PGP MESSAGE-----" + nl + + nl + + "yBUBOy2cxAACHqk5Ofk6CuH5RTkpigA=" + nl + + "-----END PGP MESSAGE-----" + nl; isEquals(expected, bOut.toString()); } private void unarmoredCompressedLiteralDataPacket(OpenPGPApi api) - throws IOException, PGPException + throws IOException, PGPException { OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() - .setArmored(false) // no armor - .setAllowPadding(false) - .setCompressionNegotiator((conf, neg) -> CompressionAlgorithmTags.ZIP); + .setArmored(false) // no armor + .setAllowPadding(false) + .setCompressionNegotiator(new OpenPGPMessageGenerator.CompressionNegotiator() + { + @Override + public int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPGPPolicy policy) + { + return CompressionAlgorithmTags.ZIP; + } + }); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OpenPGPMessageOutputStream msgOut = gen.open(bOut); @@ -124,7 +139,7 @@ private void unarmoredCompressedLiteralDataPacket(OpenPGPApi api) } private void seipd2EncryptedMessage(OpenPGPApi api) - throws IOException, PGPException + throws IOException, PGPException { OpenPGPCertificate cert = api.readKeyOrCertificate().parseCertificate(OpenPGPTestKeys.V6_CERT); @@ -140,7 +155,7 @@ private void seipd2EncryptedMessage(OpenPGPApi api) } private void seipd1EncryptedMessage(OpenPGPApi api) - throws IOException, PGPException + throws IOException, PGPException { OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.BOB_KEY); @@ -156,15 +171,15 @@ private void seipd1EncryptedMessage(OpenPGPApi api) } private void seipd2EncryptedSignedMessage(OpenPGPApi api) - throws IOException, PGPException + throws IOException, PGPException { OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() - .setAllowPadding(true) - .setArmored(true) - .addSigningKey(key) - .addEncryptionCertificate(key); + .setAllowPadding(true) + .setArmored(true) + .addSigningKey(key) + .addEncryptionCertificate(key); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OutputStream encOut = gen.open(bOut); diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java index ec634a4e5b..da11358db7 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java @@ -10,19 +10,24 @@ import org.bouncycastle.bcpg.AEADAlgorithmTags; import org.bouncycastle.bcpg.CompressionAlgorithmTags; +import org.bouncycastle.bcpg.KeyIdentifier; import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; import org.bouncycastle.openpgp.OpenPGPTestKeys; +import org.bouncycastle.openpgp.PGPEncryptedDataGenerator; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPSessionKey; +import org.bouncycastle.openpgp.api.KeyPassphraseProvider; import org.bouncycastle.openpgp.api.MessageEncryptionMechanism; import org.bouncycastle.openpgp.api.OpenPGPApi; import org.bouncycastle.openpgp.api.OpenPGPCertificate; import org.bouncycastle.openpgp.api.OpenPGPEncryptionNegotiator; import org.bouncycastle.openpgp.api.OpenPGPKey; +import org.bouncycastle.openpgp.api.OpenPGPKeyMaterialProvider; import org.bouncycastle.openpgp.api.OpenPGPMessageGenerator; import org.bouncycastle.openpgp.api.OpenPGPMessageInputStream; import org.bouncycastle.openpgp.api.OpenPGPMessageOutputStream; import org.bouncycastle.openpgp.api.OpenPGPMessageProcessor; +import org.bouncycastle.openpgp.api.OpenPGPPolicy; import org.bouncycastle.openpgp.api.OpenPGPSignature; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.io.Streams; @@ -76,7 +81,14 @@ private void roundtripUnarmoredPlaintextMessage(OpenPGPApi api) OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() .setArmored(false) .setAllowPadding(false) - .setCompressionNegotiator((conf, neg) -> CompressionAlgorithmTags.UNCOMPRESSED); + .setCompressionNegotiator(new OpenPGPMessageGenerator.CompressionNegotiator() + { + @Override + public int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPGPPolicy policy) + { + return CompressionAlgorithmTags.UNCOMPRESSED; + } + }); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OutputStream msgOut = gen.open(bOut); @@ -100,7 +112,14 @@ private void roundtripArmoredPlaintextMessage(OpenPGPApi api) OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() .setArmored(true) .setAllowPadding(false) - .setCompressionNegotiator((conf, neg) -> CompressionAlgorithmTags.UNCOMPRESSED); + .setCompressionNegotiator(new OpenPGPMessageGenerator.CompressionNegotiator() + { + @Override + public int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPGPPolicy policy) + { + return CompressionAlgorithmTags.UNCOMPRESSED; + } + }); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OutputStream msgOut = gen.open(bOut); @@ -125,7 +144,14 @@ private void roundTripCompressedMessage(OpenPGPApi api) OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() .setArmored(true) .setAllowPadding(false) - .setCompressionNegotiator((conf, neg) -> CompressionAlgorithmTags.ZIP); + .setCompressionNegotiator(new OpenPGPMessageGenerator.CompressionNegotiator() + { + @Override + public int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPGPPolicy policy) + { + return CompressionAlgorithmTags.ZIP; + } + }); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OutputStream msgOut = gen.open(bOut); @@ -148,9 +174,13 @@ private void roundTripCompressedSymEncMessageMessage(OpenPGPApi api) OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() .setArmored(true) .addEncryptionPassphrase("lal".toCharArray()) - .setSessionKeyExtractionCallback( - sk -> this.encryptionSessionKey = sk - ) + .setSessionKeyExtractionCallback(new PGPEncryptedDataGenerator.SessionKeyExtractionCallback() + { + public void extractSessionKey(PGPSessionKey sessionKey) + { + OpenPGPMessageProcessorTest.this.encryptionSessionKey = sessionKey; + } + }) .setAllowPadding(false) .setPasswordBasedEncryptionNegotiator(new OpenPGPEncryptionNegotiator() { @@ -160,8 +190,14 @@ public MessageEncryptionMechanism negotiateEncryption(OpenPGPMessageGenerator co return MessageEncryptionMechanism.integrityProtected(SymmetricKeyAlgorithmTags.AES_256); } }) - .setCompressionNegotiator( - (conf, neg) -> CompressionAlgorithmTags.ZIP); + .setCompressionNegotiator(new OpenPGPMessageGenerator.CompressionNegotiator() + { + @Override + public int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPGPPolicy policy) + { + return CompressionAlgorithmTags.ZIP; + } + }); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OutputStream msgOut = gen.open(bOut); @@ -191,7 +227,13 @@ private void roundTripSymEncMessageWithMultiplePassphrases(OpenPGPApi api) OpenPGPMessageGenerator gen = api.signAndOrEncryptMessage() .addEncryptionPassphrase("orange".toCharArray()) .addEncryptionPassphrase("violet".toCharArray()) - .setSessionKeyExtractionCallback(sk -> this.encryptionSessionKey = sk) + .setSessionKeyExtractionCallback(new PGPEncryptedDataGenerator.SessionKeyExtractionCallback() + { + public void extractSessionKey(PGPSessionKey sessionKey) + { + OpenPGPMessageProcessorTest.this.encryptionSessionKey = sessionKey; + } + }) .setPasswordBasedEncryptionNegotiator( new OpenPGPEncryptionNegotiator() { @@ -420,8 +462,14 @@ private void encryptDecryptWithLockedKey(OpenPGPApi api) bOut = new ByteArrayOutputStream(); decIn = api.decryptAndOrVerifyMessage() .addDecryptionKey(key) - .setMissingOpenPGPKeyPassphraseProvider(k -> - OpenPGPTestKeys.V6_KEY_LOCKED_PASSPHRASE.toCharArray()) + .setMissingOpenPGPKeyPassphraseProvider(new KeyPassphraseProvider() + { + @Override + public char[] getKeyPassword(OpenPGPKey.OpenPGPSecretKey key) + { + return OpenPGPTestKeys.V6_KEY_LOCKED_PASSPHRASE.toCharArray(); + } + }) .process(bIn); Streams.pipeAll(decIn, bOut); decIn.close(); @@ -434,7 +482,7 @@ private void encryptDecryptWithLockedKey(OpenPGPApi api) private void encryptDecryptWithMissingKey(OpenPGPApi api) throws IOException, PGPException { - OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); + final OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OutputStream encOut = api.signAndOrEncryptMessage() @@ -450,7 +498,14 @@ private void encryptDecryptWithMissingKey(OpenPGPApi api) ByteArrayInputStream bIn = new ByteArrayInputStream(ciphertext); bOut = new ByteArrayOutputStream(); OpenPGPMessageInputStream decIn = api.decryptAndOrVerifyMessage() - .setMissingOpenPGPKeyProvider(id -> key) + .setMissingOpenPGPKeyProvider(new OpenPGPKeyMaterialProvider.OpenPGPKeyProvider() + { + @Override + public OpenPGPKey provide(KeyIdentifier componentKeyIdentifier) + { + return key; + } + }) .process(bIn); Streams.pipeAll(decIn, bOut); decIn.close(); diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV4KeyGenerationTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV4KeyGenerationTest.java index ebd77c492c..b8456e63c4 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV4KeyGenerationTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV4KeyGenerationTest.java @@ -41,7 +41,7 @@ public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) { return generator.generateRsaKeyPair(3072); } - }, SignatureParameters.Callback.modifyHashedSubpackets(new SignatureSubpacketsFunction() + }, SignatureParameters.Callback.Util.modifyHashedSubpackets(new SignatureSubpacketsFunction() { @Override public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV6KeyGeneratorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV6KeyGeneratorTest.java index 49872f3022..7043bbfef5 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV6KeyGeneratorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPV6KeyGeneratorTest.java @@ -336,7 +336,7 @@ public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) return generator.generateRsaKeyPair(4096); } }, - SignatureParameters.Callback.modifyHashedSubpackets(new SignatureSubpacketsFunction() + SignatureParameters.Callback.Util.modifyHashedSubpackets(new SignatureSubpacketsFunction() { @Override public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) @@ -364,7 +364,7 @@ public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) return generator.generateEd448KeyPair(); } }, - SignatureParameters.Callback.modifyHashedSubpackets(new SignatureSubpacketsFunction() + SignatureParameters.Callback.Util.modifyHashedSubpackets(new SignatureSubpacketsFunction() { @Override public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) @@ -477,7 +477,7 @@ public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) return generator.generateEd25519KeyPair(); } }, - SignatureParameters.Callback.modifyHashedSubpackets(new SignatureSubpacketsFunction() + SignatureParameters.Callback.Util.modifyHashedSubpackets(new SignatureSubpacketsFunction() { @Override public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/StackMessagePassphraseCallback.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/StackMessagePassphraseCallback.java index 87d43cf551..83d47fc163 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/StackMessagePassphraseCallback.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/StackMessagePassphraseCallback.java @@ -1,11 +1,11 @@ package org.bouncycastle.openpgp.api.test; -import org.bouncycastle.openpgp.api.MissingMessagePassphraseCallback; - import java.util.Collection; import java.util.Collections; import java.util.Stack; +import org.bouncycastle.openpgp.api.MissingMessagePassphraseCallback; + /** * Test implementation of {@link MissingMessagePassphraseCallback} which provides passphrases by popping * them from a provided {@link Stack}. @@ -22,7 +22,7 @@ public StackMessagePassphraseCallback(char[] passphrase) public StackMessagePassphraseCallback(Collection passphrases) { - this.passphases = new Stack<>(); + this.passphases = new Stack(); this.passphases.addAll(passphrases); } diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java index 06c2b2e964..5e41a1c9a0 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewSignedDataTest.java @@ -3781,7 +3781,7 @@ private void verifySignatures(CMSSignedDataParser sp) } } - private static void implTestVerifySignedData(byte[] signedData, SampleCredentials credentials) + private static void implTestVerifySignedData(byte[] signedData, final SampleCredentials credentials) throws Exception { CMSSignedData sd = new CMSSignedData(signedData); From 8c9500fd6d7f68a26895af6f8ce866e524817051 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 25 May 2025 10:49:05 +1000 Subject: [PATCH 365/890] Java 5 to Java 8 compatibility fixes. --- .../openpgp/api/test/StackMessagePassphraseCallback.java | 1 - 1 file changed, 1 deletion(-) diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/StackMessagePassphraseCallback.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/StackMessagePassphraseCallback.java index 83d47fc163..6cd042fc1b 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/StackMessagePassphraseCallback.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/StackMessagePassphraseCallback.java @@ -26,7 +26,6 @@ public StackMessagePassphraseCallback(Collection passphrases) this.passphases.addAll(passphrases); } - @Override public char[] getMessagePassphrase() { if (passphases.isEmpty()) From 60afb39a0ebe5d96b464d4c8ffa023c57b56565e Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 25 May 2025 20:57:29 +1000 Subject: [PATCH 366/890] Java 5 to Java 8 compatibility fixes. --- .../openpgp/api/OpenPGPKeyReader.java | 12 ++++---- .../openpgp/api/OpenPGPSignature.java | 29 ++++++++++++------- .../api/test/OpenPGPMessageProcessorTest.java | 14 +++------ ...GOSTR3410_2012_256GenerateCertificate.java | 9 +++--- .../pqc/jcajce/provider/test/FalconTest.java | 3 +- 5 files changed, 34 insertions(+), 33 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyReader.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyReader.java index 0891cd9df2..de20829c78 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyReader.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyReader.java @@ -3,7 +3,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; @@ -13,6 +12,7 @@ import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPUtil; +import org.bouncycastle.util.Strings; import org.bouncycastle.util.io.Streams; /** @@ -103,7 +103,7 @@ public OpenPGPCertificate parseCertificate(byte[] bytes) public OpenPGPCertificate parseCertificateOrKey(String armored) throws IOException { - return parseCertificateOrKey(armored.getBytes(StandardCharsets.UTF_8)); + return parseCertificateOrKey(Strings.toUTF8ByteArray(armored)); } /** @@ -163,7 +163,7 @@ else if (object instanceof PGPPublicKeyRing) public OpenPGPKey parseKey(String armored) throws IOException { - return parseKey(armored.getBytes(StandardCharsets.UTF_8)); + return parseKey(Strings.toUTF8ByteArray(armored)); } /** @@ -211,7 +211,7 @@ public OpenPGPKey parseKey(byte[] bytes) public List parseKeysOrCertificates(String armored) throws IOException { - return parseKeysOrCertificates(armored.getBytes(StandardCharsets.UTF_8)); + return parseKeysOrCertificates(Strings.toUTF8ByteArray(armored)); } public List parseKeysOrCertificates(InputStream inputStream) @@ -259,7 +259,7 @@ else if (object instanceof PGPPublicKeyRing) public List parseCertificates(String armored) throws IOException { - return parseCertificates(armored.getBytes(StandardCharsets.UTF_8)); + return parseCertificates(Strings.toUTF8ByteArray(armored)); } public List parseCertificates(InputStream inputStream) @@ -303,7 +303,7 @@ else if (object instanceof PGPPublicKeyRing) public List parseKeys(String armored) throws IOException { - return parseKeys(armored.getBytes(StandardCharsets.UTF_8)); + return parseKeys(Strings.toUTF8ByteArray(armored)); } public List parseKeys(InputStream inputStream) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java index 7ab5d29c8f..afbcac914c 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java @@ -1,5 +1,12 @@ package org.bouncycastle.openpgp.api; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Date; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; + import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.bcpg.BCPGOutputStream; import org.bouncycastle.bcpg.KeyIdentifier; @@ -17,12 +24,6 @@ import org.bouncycastle.openpgp.api.util.UTCUtil; import org.bouncycastle.util.encoders.Hex; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.util.Date; -import java.util.List; -import java.util.Locale; - /** * An OpenPGP signature. * This is a wrapper around {@link PGPSignature} which tracks the verification state of the signature. @@ -125,8 +126,10 @@ public static KeyIdentifier getMostExpressiveIdentifier(List iden } // Find most expressive identifier - for (KeyIdentifier identifier : identifiers) + for (Iterator it = identifiers.iterator(); it.hasNext();) { + KeyIdentifier identifier = (KeyIdentifier)it.next(); + // non-wildcard and has fingerprint if (!identifier.isWildcard() && identifier.getFingerprint() != null) { @@ -135,8 +138,9 @@ public static KeyIdentifier getMostExpressiveIdentifier(List iden } // Find non-wildcard identifier - for (KeyIdentifier identifier : identifiers) + for (Iterator it = identifiers.iterator(); it.hasNext();) { + KeyIdentifier identifier = (KeyIdentifier)it.next(); // non-wildcard (and no fingerprint) if (!identifier.isWildcard()) { @@ -295,17 +299,20 @@ void sanitize(OpenPGPCertificate.OpenPGPComponentKey issuer, this, "Signature predates issuer key creation time."); } - for (NotationData notation : hashed.getNotationDataOccurrences()) + for (Iterator it = hashed.getNotationDataOccurrences().iterator(); it.hasNext(); ) { + NotationData notation = (NotationData)it.next(); if (notation.isCritical()) { throw new MalformedOpenPGPSignatureException( - this, "Critical unknown NotationData encountered: " + notation.getNotationName()); + this, "Critical unknown NotationData encountered: " + notation.getNotationName()); } } - for (SignatureSubpacket unknownSubpacket : hashed.toArray()) + SignatureSubpacket[] signatureSubpackets = hashed.toArray(); + for (int i = 0; i != signatureSubpackets.length; i++) { + SignatureSubpacket unknownSubpacket = signatureSubpackets[i]; // SignatureSubpacketInputStream returns unknown subpackets as SignatureSubpacket if (unknownSubpacket.isCritical() && unknownSubpacket.getClass().equals(SignatureSubpacket.class)) diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java index da11358db7..02ba0238db 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java @@ -5,7 +5,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.nio.charset.StandardCharsets; import java.util.List; import org.bouncycastle.bcpg.AEADAlgorithmTags; @@ -30,12 +29,13 @@ import org.bouncycastle.openpgp.api.OpenPGPPolicy; import org.bouncycastle.openpgp.api.OpenPGPSignature; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; import org.bouncycastle.util.io.Streams; public class OpenPGPMessageProcessorTest extends APITest { - private static final byte[] PLAINTEXT = "Hello, World!\n".getBytes(StandardCharsets.UTF_8); + private static final byte[] PLAINTEXT = Strings.toUTF8ByteArray("Hello, World!\n"); private PGPSessionKey encryptionSessionKey; @@ -83,7 +83,6 @@ private void roundtripUnarmoredPlaintextMessage(OpenPGPApi api) .setAllowPadding(false) .setCompressionNegotiator(new OpenPGPMessageGenerator.CompressionNegotiator() { - @Override public int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPGPPolicy policy) { return CompressionAlgorithmTags.UNCOMPRESSED; @@ -114,7 +113,6 @@ private void roundtripArmoredPlaintextMessage(OpenPGPApi api) .setAllowPadding(false) .setCompressionNegotiator(new OpenPGPMessageGenerator.CompressionNegotiator() { - @Override public int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPGPPolicy policy) { return CompressionAlgorithmTags.UNCOMPRESSED; @@ -146,7 +144,6 @@ private void roundTripCompressedMessage(OpenPGPApi api) .setAllowPadding(false) .setCompressionNegotiator(new OpenPGPMessageGenerator.CompressionNegotiator() { - @Override public int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPGPPolicy policy) { return CompressionAlgorithmTags.ZIP; @@ -192,7 +189,6 @@ public MessageEncryptionMechanism negotiateEncryption(OpenPGPMessageGenerator co }) .setCompressionNegotiator(new OpenPGPMessageGenerator.CompressionNegotiator() { - @Override public int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPGPPolicy policy) { return CompressionAlgorithmTags.ZIP; @@ -464,7 +460,6 @@ private void encryptDecryptWithLockedKey(OpenPGPApi api) .addDecryptionKey(key) .setMissingOpenPGPKeyPassphraseProvider(new KeyPassphraseProvider() { - @Override public char[] getKeyPassword(OpenPGPKey.OpenPGPSecretKey key) { return OpenPGPTestKeys.V6_KEY_LOCKED_PASSPHRASE.toCharArray(); @@ -500,7 +495,6 @@ private void encryptDecryptWithMissingKey(OpenPGPApi api) OpenPGPMessageInputStream decIn = api.decryptAndOrVerifyMessage() .setMissingOpenPGPKeyProvider(new OpenPGPKeyMaterialProvider.OpenPGPKeyProvider() { - @Override public OpenPGPKey provide(KeyIdentifier componentKeyIdentifier) { return key; @@ -650,7 +644,7 @@ private void incompleteMessageProcessing(OpenPGPApi api) ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OpenPGPMessageOutputStream out = gen.open(bOut); - out.write("Some Data".getBytes(StandardCharsets.UTF_8)); + out.write(Strings.toUTF8ByteArray("Some Data")); out.close(); ByteArrayInputStream bIn = new ByteArrayInputStream(bOut.toByteArray()); @@ -688,7 +682,7 @@ private void testVerificationOfSEIPD1MessageWithTamperedCiphertext(OpenPGPApi ap OpenPGPKey key = api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.BOB_KEY); OpenPGPMessageProcessor processor = api.decryptAndOrVerifyMessage(); processor.addDecryptionKey(key); - OpenPGPMessageInputStream oIn = processor.process(new ByteArrayInputStream(MSG.getBytes(StandardCharsets.UTF_8))); + OpenPGPMessageInputStream oIn = processor.process(new ByteArrayInputStream(Strings.toUTF8ByteArray(MSG))); Streams.drain(oIn); try { diff --git a/pkix/src/test/java/org/bouncycastle/cert/test/GOSTR3410_2012_256GenerateCertificate.java b/pkix/src/test/java/org/bouncycastle/cert/test/GOSTR3410_2012_256GenerateCertificate.java index 8ef1842940..ac2e486f79 100644 --- a/pkix/src/test/java/org/bouncycastle/cert/test/GOSTR3410_2012_256GenerateCertificate.java +++ b/pkix/src/test/java/org/bouncycastle/cert/test/GOSTR3410_2012_256GenerateCertificate.java @@ -9,7 +9,6 @@ import java.security.Provider; import java.security.Security; import java.security.spec.ECGenParameterSpec; -import java.time.ZonedDateTime; import java.util.Date; import org.bouncycastle.asn1.ASN1Sequence; @@ -60,14 +59,14 @@ private static X509CertificateHolder generateSelfSignedCertificate() X500Name subject = new X500Name("CN=TEST"); X500Name issuer = subject; BigInteger serial = BigInteger.ONE; - ZonedDateTime notBefore = ZonedDateTime.now(); - ZonedDateTime notAfter = notBefore.plusYears(1); + Date notBefore = new Date(); + Date notAfter = new Date(notBefore.getTime() + 1000L * 60 * 60 * 24 * 365); X509v3CertificateBuilder certificateBuilder = new JcaX509v3CertificateBuilder( issuer, serial, - Date.from(notBefore.toInstant()), - Date.from(notAfter.toInstant()), + notBefore, + notAfter, subject, keyPair.getPublic() ); diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/FalconTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/FalconTest.java index 0a37418a78..df2c864705 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/FalconTest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/FalconTest.java @@ -30,6 +30,7 @@ import org.bouncycastle.pqc.jcajce.spec.FalconParameterSpec; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; +import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemReader; @@ -101,7 +102,7 @@ public void testOQSPublicKeyExample() System.out.println("Public Key Algorithm : " + publicKey.getAlgorithm()); System.out.println("Public Key Format : " + publicKey.getFormat()); System.out.println("Encoded Key Length : " + publicKey.getEncoded().length + " bytes"); - System.out.println("Encoded Key (Base64) : " + java.util.Base64.getEncoder().encodeToString(publicKey.getEncoded())); + System.out.println("Encoded Key (Base64) : " + Base64.toBase64String(publicKey.getEncoded())); } catch (Exception e) From 691ddc79eb259272f4adf8a0448b461616f882d5 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 25 May 2025 21:33:22 +1000 Subject: [PATCH 367/890] Java 4 compatibility fixes. --- ant/jdk14.xml | 6 +++++- .../openpgp/IntegrityProtectedInputStream.java | 7 ++++--- .../openpgp/api/test/OpenPGPCertificateTest.java | 3 ++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/ant/jdk14.xml b/ant/jdk14.xml index aa3e45add3..dbc66883d4 100644 --- a/ant/jdk14.xml +++ b/ant/jdk14.xml @@ -41,6 +41,8 @@ + + @@ -213,7 +215,9 @@ - + + + diff --git a/pg/src/main/java/org/bouncycastle/openpgp/IntegrityProtectedInputStream.java b/pg/src/main/java/org/bouncycastle/openpgp/IntegrityProtectedInputStream.java index d553201d09..8d8efe5576 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/IntegrityProtectedInputStream.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/IntegrityProtectedInputStream.java @@ -1,11 +1,12 @@ package org.bouncycastle.openpgp; -import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket; - import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; +import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket; +import org.bouncycastle.util.Exceptions; + /** * {@link InputStream} that performs verification of integrity protection upon {@link #close()}. */ @@ -76,7 +77,7 @@ public void close() } catch (PGPException e) { - throw new IOException(e); + throw Exceptions.ioException(e.getMessage(), e); } } } diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java index 7163fa70b6..7b0d2129f1 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java @@ -775,8 +775,9 @@ private void signatureValidityTest(OpenPGPApi api, String cert, TestSignature... { OpenPGPCertificate certificate = api.readKeyOrCertificate().parseCertificate(cert); - for (TestSignature test : testSignatures) + for (int i = 0; i != testSignatures.length; i++) { + TestSignature test = testSignatures[i]; PGPSignature signature = test.getSignature(); OpenPGPCertificate.OpenPGPComponentKey signingKey = certificate.getSigningKeyFor(signature); From 8d2d9d4941c7ccb600aa224fe34a067850f849dd Mon Sep 17 00:00:00 2001 From: gefeili Date: Sun, 25 May 2025 21:07:40 +0930 Subject: [PATCH 368/890] Fix the issue in OpenPGPSignature --- .../java/org/bouncycastle/openpgp/api/OpenPGPSignature.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java index afbcac914c..0421bdc8c1 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPSignature.java @@ -299,9 +299,10 @@ void sanitize(OpenPGPCertificate.OpenPGPComponentKey issuer, this, "Signature predates issuer key creation time."); } - for (Iterator it = hashed.getNotationDataOccurrences().iterator(); it.hasNext(); ) + NotationData[] notations = hashed.getNotationDataOccurrences(); + for (int i = 0; i< notations.length; i++ ) { - NotationData notation = (NotationData)it.next(); + NotationData notation = notations[i]; if (notation.isCritical()) { throw new MalformedOpenPGPSignatureException( From b2d783235bbf50292fa2bb96daa8509c5ab6b155 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 25 May 2025 22:31:15 +1000 Subject: [PATCH 369/890] Java 4 compatibility fixes. --- ant/jdk14.xml | 5 + .../cert/test/SampleCredentials.java | 6 +- .../org/bouncycastle/cert/test/AllTests.java | 101 ++++++++++++++++++ 3 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 pkix/src/test/jdk1.4/org/bouncycastle/cert/test/AllTests.java diff --git a/ant/jdk14.xml b/ant/jdk14.xml index dbc66883d4..8c401848dd 100644 --- a/ant/jdk14.xml +++ b/ant/jdk14.xml @@ -136,10 +136,14 @@ + + + + @@ -161,6 +165,7 @@ + diff --git a/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java b/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java index 790c03a4ce..60a83f1f29 100644 --- a/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java +++ b/pkix/src/test/java/org/bouncycastle/cert/test/SampleCredentials.java @@ -20,7 +20,6 @@ import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemReader; -import org.junit.Assert; public class SampleCredentials { @@ -70,7 +69,10 @@ private static SampleCredentials load(String algorithm, String path, String name X509Certificate certificate = (X509Certificate)cf.generateCertificate( new ByteArrayInputStream(pemCert.getContent())); - Assert.assertEquals(publicKey, certificate.getPublicKey()); + if (!publicKey.equals(certificate.getPublicKey())) + { + throw new IllegalStateException("public key mismatch"); + } return new SampleCredentials(keyPair, certificate); } diff --git a/pkix/src/test/jdk1.4/org/bouncycastle/cert/test/AllTests.java b/pkix/src/test/jdk1.4/org/bouncycastle/cert/test/AllTests.java new file mode 100644 index 0000000000..55c6c6c946 --- /dev/null +++ b/pkix/src/test/jdk1.4/org/bouncycastle/cert/test/AllTests.java @@ -0,0 +1,101 @@ +package org.bouncycastle.cert.test; + +import java.security.Security; + +import junit.extensions.TestSetup; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.test.PrintTestResult; +import org.bouncycastle.util.test.SimpleTestResult; + +public class AllTests + extends TestCase +{ + public void setUp() + { + if (Security.getProvider("BC") == null) + { + Security.addProvider(new BouncyCastleProvider()); + } + } + + public void testSimpleTests() + { + org.bouncycastle.util.test.Test[] tests = new org.bouncycastle.util.test.Test[] + { + new AttrCertSelectorTest(), + new AttrCertTest(), + new CertPathLoopTest(), + new CertTest(), + new DANETest(), + new ExternalKeyTest(), + new GOST3410_2012CMSTest(), + new MLDSACredentialsTest(), + new PKCS10Test(), + new SLHDSACredentialsTest(), + new X509ExtensionUtilsTest(), + }; + + for (int i = 0; i != tests.length; i++) + { + SimpleTestResult result = (SimpleTestResult)tests[i].perform(); + + if (!result.isSuccessful()) + { + if (result.getException() != null) + { + result.getException().printStackTrace(); + } + fail(result.toString()); + } + } + } + + public static void main (String[] args) + { + PrintTestResult.printResult(junit.textui.TestRunner.run(suite())); + } + + public static Test suite() + { + TestSuite suite = new TestSuite("Cert Tests"); + + if (Security.getProvider("BC") == null) + { + Security.addProvider(new BouncyCastleProvider()); + } + + suite.addTestSuite(AllTests.class); + suite.addTestSuite(BcAttrCertSelectorTest.class); + suite.addTestSuite(BcAttrCertSelectorTest.class); + suite.addTestSuite(BcAttrCertTest.class); + suite.addTestSuite(BcCertTest.class); + suite.addTestSuite(BcPKCS10Test.class); + suite.addTestSuite(PQCPKCS10Test.class); + suite.addTest(ConverterTest.suite()); + + return new BCTestSetup(suite); + } + + static class BCTestSetup + extends TestSetup + { + public BCTestSetup(Test test) + { + super(test); + } + + protected void setUp() + { + Security.addProvider(new BouncyCastleProvider()); + } + + protected void tearDown() + { + Security.removeProvider("BC"); + } + } + +} \ No newline at end of file From 70c767afeda4ccd2b259dd31658838eb0efeb08b Mon Sep 17 00:00:00 2001 From: gefeili Date: Sun, 25 May 2025 22:24:56 +0930 Subject: [PATCH 370/890] Refactor of OpenPGPCertificate.getSince --- .../openpgp/api/OpenPGPCertificate.java | 82 +++++++++++-------- .../bc/BcPGPKeyPairGeneratorProvider.java | 8 +- 2 files changed, 52 insertions(+), 38 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java index cbcf97b8e9..925d99da6f 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java @@ -15,7 +15,6 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; -import java.util.function.Function; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.bcpg.BCPGInputStream; @@ -3034,41 +3033,56 @@ public boolean isHardRevocation() */ public Date getSince() { - // Find most recent chain link -// return chainLinks.stream() -// .map(it -> it.signature) -// .max(Comparator.comparing(OpenPGPComponentSignature::getCreationTime)) -// .map(OpenPGPComponentSignature::getCreationTime) -// .orElse(null); - return chainLinks.stream() - .map(new Function() - { - @Override - public OpenPGPComponentSignature apply(Link it) - { - return it.signature; // Replace lambda: `it -> it.signature` - } - - }) - .max(new Comparator() - { - @Override - public int compare(Object o1, Object o2) - { - // Replace method reference: `Comparator.comparing(OpenPGPComponentSignature::getCreationTime)` - return ((OpenPGPComponentSignature)o1).getCreationTime().compareTo(((OpenPGPComponentSignature)o2).getCreationTime()); - } - }) - .map(new Function() + Date latestDate = null; + for (Iterator it = chainLinks.iterator(); it.hasNext(); ) + { + Link link = (Link)it.next(); + OpenPGPComponentSignature signature = link.getSignature(); + Date currentDate = signature.getCreationTime(); + if (latestDate == null || currentDate.after(latestDate)) { - @Override - public Date apply(Object sig) - { - return ((OpenPGPComponentSignature)sig).getCreationTime(); // Replace method reference: `OpenPGPComponentSignature::getCreationTime` - } - }) - .orElse(null); + latestDate = currentDate; + } + } + return latestDate; } +// public Date getSince() +// { +// // Find most recent chain link +//// return chainLinks.stream() +//// .map(it -> it.signature) +//// .max(Comparator.comparing(OpenPGPComponentSignature::getCreationTime)) +//// .map(OpenPGPComponentSignature::getCreationTime) +//// .orElse(null); +// return chainLinks.stream() +// .map(new Function() +// { +// @Override +// public OpenPGPComponentSignature apply(Link it) +// { +// return it.signature; // Replace lambda: `it -> it.signature` +// } +// +// }) +// .max(new Comparator() +// { +// @Override +// public int compare(Object o1, Object o2) +// { +// // Replace method reference: `Comparator.comparing(OpenPGPComponentSignature::getCreationTime)` +// return ((OpenPGPComponentSignature)o1).getCreationTime().compareTo(((OpenPGPComponentSignature)o2).getCreationTime()); +// } +// }) +// .map(new Function() +// { +// @Override +// public Date apply(Object sig) +// { +// return ((OpenPGPComponentSignature)sig).getCreationTime(); // Replace method reference: `OpenPGPComponentSignature::getCreationTime` +// } +// }) +// .orElse(null); +// } /** * Return the date until which the chain link is valid. diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPairGeneratorProvider.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPairGeneratorProvider.java index a36cebc79e..45ab6a85e4 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPairGeneratorProvider.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPGPKeyPairGeneratorProvider.java @@ -142,8 +142,8 @@ public PGPKeyPair generateECDHKeyPair(ASN1ObjectIdentifier curveOID) { ECKeyPairGenerator gen = new ECKeyPairGenerator(); gen.init(new ECKeyGenerationParameters( - new ECNamedDomainParameters(curveOID, getNamedCurveByOid(curveOID)), - CryptoServicesRegistrar.getSecureRandom())); + new ECNamedDomainParameters(curveOID, getNamedCurveByOid(curveOID)), + CryptoServicesRegistrar.getSecureRandom())); AsymmetricCipherKeyPair keyPair = gen.generateKeyPair(); return new BcPGPKeyPair(version, PublicKeyAlgorithmTags.ECDH, keyPair, creationTime); @@ -155,8 +155,8 @@ public PGPKeyPair generateECDSAKeyPair(ASN1ObjectIdentifier curveOID) { ECKeyPairGenerator gen = new ECKeyPairGenerator(); gen.init(new ECKeyGenerationParameters( - new ECNamedDomainParameters(curveOID, getNamedCurveByOid(curveOID)), - CryptoServicesRegistrar.getSecureRandom())); + new ECNamedDomainParameters(curveOID, getNamedCurveByOid(curveOID)), + CryptoServicesRegistrar.getSecureRandom())); AsymmetricCipherKeyPair keyPair = gen.generateKeyPair(); return new BcPGPKeyPair(version, PublicKeyAlgorithmTags.ECDSA, keyPair, creationTime); From 5944b0ceca588b31d57cc71e9ad939a733320202 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 26 May 2025 08:30:38 +1000 Subject: [PATCH 371/890] added mayo/snova --- prov/src/main/ext-jdk1.9/module-info.java | 7 ++++++- prov/src/main/jdk1.9/module-info.java | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/prov/src/main/ext-jdk1.9/module-info.java b/prov/src/main/ext-jdk1.9/module-info.java index e7f8f7b498..b4f0ba7462 100644 --- a/prov/src/main/ext-jdk1.9/module-info.java +++ b/prov/src/main/ext-jdk1.9/module-info.java @@ -8,6 +8,9 @@ opens org.bouncycastle.jcajce.provider.asymmetric.edec to java.base; opens org.bouncycastle.pqc.jcajce.provider.lms to java.base; + opens org.bouncycastle.jcajce.provider.asymmetric.mldsa to java.base; + opens org.bouncycastle.jcajce.provider.asymmetric.mlkem to java.base; + opens org.bouncycastle.jcajce.provider.asymmetric.slhdsa to java.base; exports org.bouncycastle; exports org.bouncycastle.asn1; @@ -110,20 +113,22 @@ exports org.bouncycastle.pqc.crypto.bike; exports org.bouncycastle.pqc.crypto.cmce; exports org.bouncycastle.pqc.crypto.crystals.dilithium; + exports org.bouncycastle.pqc.crypto.mldsa; exports org.bouncycastle.pqc.crypto.mlkem; exports org.bouncycastle.pqc.crypto.falcon; exports org.bouncycastle.pqc.crypto.frodo; exports org.bouncycastle.pqc.legacy.crypto.gemss; exports org.bouncycastle.pqc.crypto.hqc; exports org.bouncycastle.pqc.crypto.lms; + exports org.bouncycastle.pqc.crypto.mayo; exports org.bouncycastle.pqc.crypto.newhope; exports org.bouncycastle.pqc.crypto.ntru; exports org.bouncycastle.pqc.crypto.ntruprime; exports org.bouncycastle.pqc.crypto.picnic; - exports org.bouncycastle.pqc.legacy.crypto.rainbow; exports org.bouncycastle.pqc.crypto.saber; exports org.bouncycastle.pqc.crypto.sphincs; exports org.bouncycastle.pqc.crypto.sphincsplus; + exports org.bouncycastle.pqc.crypto.snova; exports org.bouncycastle.pqc.crypto.util; exports org.bouncycastle.pqc.crypto.xmss; exports org.bouncycastle.pqc.math.ntru; diff --git a/prov/src/main/jdk1.9/module-info.java b/prov/src/main/jdk1.9/module-info.java index a8219f239d..d13c8420c1 100644 --- a/prov/src/main/jdk1.9/module-info.java +++ b/prov/src/main/jdk1.9/module-info.java @@ -127,6 +127,7 @@ exports org.bouncycastle.pqc.crypto.frodo; exports org.bouncycastle.pqc.crypto.hqc; exports org.bouncycastle.pqc.crypto.lms; + exports org.bouncycastle.pqc.crypto.mayo; exports org.bouncycastle.pqc.crypto.newhope; exports org.bouncycastle.pqc.crypto.ntru; exports org.bouncycastle.pqc.crypto.ntruprime; @@ -136,6 +137,7 @@ exports org.bouncycastle.pqc.crypto.sphincs; exports org.bouncycastle.pqc.crypto.sphincsplus; exports org.bouncycastle.pqc.crypto.slhdsa; + exports org.bouncycastle.pqc.crypto.snova; exports org.bouncycastle.pqc.crypto.util; exports org.bouncycastle.pqc.crypto.xmss; exports org.bouncycastle.pqc.math.ntru; From 6a62b22519f6570b085d6262a1b807020b5db219 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 26 May 2025 09:03:12 +1000 Subject: [PATCH 372/890] Updated provider version numbers. --- .../org/bouncycastle/jce/provider/BouncyCastleProvider.java | 4 ++-- .../pqc/jcajce/provider/BouncyCastlePQCProvider.java | 4 ++-- .../org/bouncycastle/jce/provider/BouncyCastleProvider.java | 4 ++-- .../org/bouncycastle/jce/provider/BouncyCastleProvider.java | 4 ++-- .../bouncycastle/jsse/provider/BouncyCastleJsseProvider.java | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java index 39e3940476..df5e014c01 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java @@ -78,7 +78,7 @@ public final class BouncyCastleProvider extends Provider { private static final Logger LOG = Logger.getLogger(BouncyCastleProvider.class.getName()); - private static String info = "BouncyCastle Security Provider v1.80"; + private static String info = "BouncyCastle Security Provider v1.81"; public static final String PROVIDER_NAME = "BC"; @@ -171,7 +171,7 @@ public final class BouncyCastleProvider extends Provider */ public BouncyCastleProvider() { - super(PROVIDER_NAME, 1.80, info); + super(PROVIDER_NAME, 1.81, info); AccessController.doPrivileged(new PrivilegedAction() { diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java index 947a221945..1fb5c14a72 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java @@ -22,7 +22,7 @@ public class BouncyCastlePQCProvider extends Provider implements ConfigurableProvider { - private static String info = "BouncyCastle Post-Quantum Security Provider v1.80"; + private static String info = "BouncyCastle Post-Quantum Security Provider v1.81"; public static String PROVIDER_NAME = "BCPQC"; @@ -51,7 +51,7 @@ public class BouncyCastlePQCProvider */ public BouncyCastlePQCProvider() { - super(PROVIDER_NAME, 1.80, info); + super(PROVIDER_NAME, 1.81, info); AccessController.doPrivileged(new PrivilegedAction() { diff --git a/prov/src/main/jdk1.1/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/prov/src/main/jdk1.1/org/bouncycastle/jce/provider/BouncyCastleProvider.java index 2e22ef7c08..b5c1f9fd3d 100644 --- a/prov/src/main/jdk1.1/org/bouncycastle/jce/provider/BouncyCastleProvider.java +++ b/prov/src/main/jdk1.1/org/bouncycastle/jce/provider/BouncyCastleProvider.java @@ -43,7 +43,7 @@ public final class BouncyCastleProvider extends Provider implements ConfigurableProvider { - private static String info = "BouncyCastle Security Provider v1.80"; + private static String info = "BouncyCastle Security Provider v1.81"; public static final String PROVIDER_NAME = "BC"; @@ -118,7 +118,7 @@ public final class BouncyCastleProvider extends Provider */ public BouncyCastleProvider() { - super(PROVIDER_NAME, 1.8000, info); + super(PROVIDER_NAME, 1.8100, info); setup(); } diff --git a/prov/src/main/jdk1.4/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/prov/src/main/jdk1.4/org/bouncycastle/jce/provider/BouncyCastleProvider.java index 7cfaade391..a652e5cb59 100644 --- a/prov/src/main/jdk1.4/org/bouncycastle/jce/provider/BouncyCastleProvider.java +++ b/prov/src/main/jdk1.4/org/bouncycastle/jce/provider/BouncyCastleProvider.java @@ -51,7 +51,7 @@ public final class BouncyCastleProvider extends Provider implements ConfigurableProvider { - private static String info = "BouncyCastle Security Provider v1.80"; + private static String info = "BouncyCastle Security Provider v1.81"; public static final String PROVIDER_NAME = "BC"; @@ -135,7 +135,7 @@ public final class BouncyCastleProvider extends Provider */ public BouncyCastleProvider() { - super(PROVIDER_NAME, 1.8000, info); + super(PROVIDER_NAME, 1.8100, info); AccessController.doPrivileged(new PrivilegedAction() { diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/BouncyCastleJsseProvider.java b/tls/src/main/java/org/bouncycastle/jsse/provider/BouncyCastleJsseProvider.java index 486ff1d083..b466a3c397 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/provider/BouncyCastleJsseProvider.java +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/BouncyCastleJsseProvider.java @@ -26,8 +26,8 @@ public class BouncyCastleJsseProvider private static final String JSSE_CONFIG_PROPERTY = "org.bouncycastle.jsse.config"; - private static final double PROVIDER_VERSION = 1.0020; - private static final String PROVIDER_INFO = "Bouncy Castle JSSE Provider Version 1.0.20"; + private static final double PROVIDER_VERSION = 1.0021; + private static final String PROVIDER_INFO = "Bouncy Castle JSSE Provider Version 1.0.21"; private final Map serviceMap = new ConcurrentHashMap(); private final Map creatorMap = new HashMap(); From 2421ab92d89867cc0d5d7566b3404e642da729b0 Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 26 May 2025 10:48:09 +0930 Subject: [PATCH 373/890] Rename JVMVersionTestProv to JVMVersionTest --- prov/src/test/jdk1.5/org/bouncycastle/test/JVMVersionTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prov/src/test/jdk1.5/org/bouncycastle/test/JVMVersionTest.java b/prov/src/test/jdk1.5/org/bouncycastle/test/JVMVersionTest.java index 06e26c6851..ca7db6a612 100644 --- a/prov/src/test/jdk1.5/org/bouncycastle/test/JVMVersionTest.java +++ b/prov/src/test/jdk1.5/org/bouncycastle/test/JVMVersionTest.java @@ -12,7 +12,7 @@ * if -Dtest.java.version.prefix=17 and System.getProperty("java.version") = 17.0.4.1 * Then this test will pass. */ -public class JVMVersionTestProv extends TestCase +public class JVMVersionTest extends TestCase { private static final String expectedVersionPropName = "test.java.version.prefix"; From 32dab66e545549c6bb4d127bc6d9143bd2023a58 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 26 May 2025 12:50:13 +0700 Subject: [PATCH 374/890] TLS: Cleanup JCA ML-KEM code - reduce dependency on BC provider --- .../bouncycastle/tls/crypto/TlsKemConfig.java | 16 -- .../tls/crypto/impl/jcajce/JcaTlsCrypto.java | 17 +-- .../tls/crypto/impl/jcajce/JceTlsMLKem.java | 12 +- .../crypto/impl/jcajce/JceTlsMLKemDomain.java | 110 +++----------- .../tls/crypto/impl/jcajce/KemUtil.java | 142 ++++++++++++++---- 5 files changed, 152 insertions(+), 145 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/TlsKemConfig.java b/tls/src/main/java/org/bouncycastle/tls/crypto/TlsKemConfig.java index 27d09d3b25..d064bc8d68 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/TlsKemConfig.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/TlsKemConfig.java @@ -1,11 +1,7 @@ package org.bouncycastle.tls.crypto; -import org.bouncycastle.jcajce.spec.KEMParameterSpec; -import org.bouncycastle.jcajce.spec.KTSParameterSpec; - public class TlsKemConfig { - protected final KTSParameterSpec ktsParameterSpec; protected final int namedGroup; protected final boolean isServer; @@ -13,13 +9,6 @@ public TlsKemConfig(int namedGroup, boolean isServer) { this.namedGroup = namedGroup; this.isServer = isServer; - this.ktsParameterSpec = new KTSParameterSpec.Builder("AES-KWP", 256).withNoKdf().build(); - } - public TlsKemConfig(int namedGroup, boolean isServer, KTSParameterSpec ktsParameterSpec) - { - this.namedGroup = namedGroup; - this.isServer = isServer; - this.ktsParameterSpec = ktsParameterSpec; } public int getNamedGroup() @@ -31,9 +20,4 @@ public boolean isServer() { return isServer; } - - public KTSParameterSpec getKtsParameterSpec() - { - return ktsParameterSpec; - } } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index b90892f148..2f3f3a1452 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -442,18 +442,13 @@ public AlgorithmParameters getNamedGroupAlgorithmParameters(int namedGroup) { if (NamedGroup.refersToAnXDHCurve(namedGroup)) { - switch (namedGroup) - { /* - * TODO Return AlgorithmParameters to check against disabled algorithms + * TODO Return AlgorithmParameters to check against disabled algorithms? * * NOTE: The JDK doesn't even support AlgorithmParameters for XDH, so SunJSSE also winds * up using null AlgorithmParameters when checking algorithm constraints. */ - case NamedGroup.x25519: - case NamedGroup.x448: - return null; - } + return null; } else if (NamedGroup.refersToAnECDSACurve(namedGroup)) { @@ -465,8 +460,12 @@ else if (NamedGroup.refersToASpecificFiniteField(namedGroup)) } else if (NamedGroup.refersToASpecificKem(namedGroup)) { - //Note: There is no AlgorithmParametersSpi for ML-KEM - return KemUtil.getAlgorithmParameters(this, NamedGroup.getKemName(namedGroup)); + /* + * TODO Return AlgorithmParameters to check against disabled algorithms? + * + * NOTE: See what the JDK/SunJSSE implementation does. + */ + return null; } throw new IllegalArgumentException("NamedGroup not supported: " + NamedGroup.getText(namedGroup)); diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKem.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKem.java index 319651c306..325eaf82a1 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKem.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKem.java @@ -2,10 +2,10 @@ import java.io.IOException; import java.security.KeyPair; +import java.security.PrivateKey; +import java.security.PublicKey; import org.bouncycastle.jcajce.SecretKeyWithEncapsulation; -import org.bouncycastle.jcajce.provider.asymmetric.mlkem.BCMLKEMPrivateKey; -import org.bouncycastle.jcajce.provider.asymmetric.mlkem.BCMLKEMPublicKey; import org.bouncycastle.tls.crypto.TlsAgreement; import org.bouncycastle.tls.crypto.TlsSecret; @@ -13,8 +13,8 @@ public class JceTlsMLKem implements TlsAgreement { protected final JceTlsMLKemDomain domain; - protected BCMLKEMPrivateKey privateKey; - protected BCMLKEMPublicKey publicKey; + protected PrivateKey privateKey; + protected PublicKey publicKey; protected TlsSecret secret; public JceTlsMLKem(JceTlsMLKemDomain domain) @@ -34,8 +34,8 @@ public byte[] generateEphemeral() throws IOException else { KeyPair kp = domain.generateKeyPair(); - this.privateKey = (BCMLKEMPrivateKey)kp.getPrivate(); - return ((BCMLKEMPublicKey)kp.getPublic()).getPublicData(); + this.privateKey = kp.getPrivate(); + return KemUtil.encodePublicKey(kp.getPublic()); } } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java index f9bf87f060..48259ce14c 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java @@ -1,20 +1,16 @@ package org.bouncycastle.tls.crypto.impl.jcajce; +import java.io.IOException; import java.security.KeyPair; import java.security.KeyPairGenerator; -import java.security.NoSuchAlgorithmException; -import java.security.NoSuchProviderException; import java.security.PrivateKey; import java.security.PublicKey; import javax.crypto.KeyGenerator; import org.bouncycastle.jcajce.SecretKeyWithEncapsulation; -import org.bouncycastle.jcajce.provider.asymmetric.mlkem.BCMLKEMPublicKey; import org.bouncycastle.jcajce.spec.KEMExtractSpec; import org.bouncycastle.jcajce.spec.KEMGenerateSpec; -import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters; -import org.bouncycastle.pqc.crypto.mlkem.MLKEMPublicKeyParameters; import org.bouncycastle.tls.NamedGroup; import org.bouncycastle.tls.crypto.TlsAgreement; import org.bouncycastle.tls.crypto.TlsKemConfig; @@ -22,51 +18,15 @@ public class JceTlsMLKemDomain implements TlsKemDomain { - public static MLKEMParameters getDomainParameters(TlsKemConfig kemConfig) - { - switch (kemConfig.getNamedGroup()) - { - case NamedGroup.OQS_mlkem512: - case NamedGroup.MLKEM512: - return MLKEMParameters.ml_kem_512; - case NamedGroup.OQS_mlkem768: - case NamedGroup.MLKEM768: - return MLKEMParameters.ml_kem_768; - case NamedGroup.OQS_mlkem1024: - case NamedGroup.MLKEM1024: - return MLKEMParameters.ml_kem_1024; - default: - throw new IllegalArgumentException("No ML-KEM configuration provided"); - } - } - protected final JcaTlsCrypto crypto; - protected final TlsKemConfig config; - protected final MLKEMParameters domainParameters; + protected final String kemName; protected final boolean isServer; - protected KeyGenerator keyGen; -// protected KeyPairGenerator kpg; -// protected Cipher cipher; - public JceTlsMLKemDomain(JcaTlsCrypto crypto, TlsKemConfig kemConfig) { this.crypto = crypto; - this.config = kemConfig; - this.domainParameters = getDomainParameters(kemConfig); + this.kemName = NamedGroup.getKemName(kemConfig.getNamedGroup()); this.isServer = kemConfig.isServer(); - try - { - this.keyGen = crypto.getHelper().createKeyGenerator(domainParameters.getName()); - } - catch (NoSuchAlgorithmException e) - { - throw new RuntimeException(e); - } - catch (NoSuchProviderException e) - { - throw new RuntimeException(e); - } } public JceTlsSecret adoptLocalSecret(byte[] secret) @@ -83,33 +43,30 @@ public JceTlsSecret decapsulate(PrivateKey privateKey, byte[] ciphertext) { try { - keyGen.init(new KEMExtractSpec.Builder(privateKey, ciphertext, "DEF", 256).withNoKdf().build()); - SecretKeyWithEncapsulation secEnc = (SecretKeyWithEncapsulation)keyGen.generateKey(); - + KeyGenerator keyGenerator = KemUtil.getKeyGenerator(crypto, kemName); + keyGenerator.init(new KEMExtractSpec.Builder(privateKey, ciphertext, "DEF", 256).withNoKdf().build()); + SecretKeyWithEncapsulation secEnc = (SecretKeyWithEncapsulation)keyGenerator.generateKey(); return adoptLocalSecret(secEnc.getEncoded()); } catch (Exception e) { throw Exceptions.illegalArgumentException("invalid key: " + e.getMessage(), e); } - - -// MLKEMExtractor kemExtract = new MLKEMExtractor(privateKey); -// byte[] secret = kemExtract.extractSecret(ciphertext); -// return adoptLocalSecret(secret); } - public BCMLKEMPublicKey decodePublicKey(byte[] encoding) + public PublicKey decodePublicKey(byte[] encoding) + throws IOException { - return new BCMLKEMPublicKey(new MLKEMPublicKeyParameters(domainParameters, encoding)); + return KemUtil.decodePublicKey(crypto, kemName, encoding); } public SecretKeyWithEncapsulation encapsulate(PublicKey publicKey) { try { - keyGen.init(new KEMGenerateSpec.Builder(publicKey, "DEF", 256).withNoKdf().build()); - return (SecretKeyWithEncapsulation)keyGen.generateKey(); + KeyGenerator keyGenerator = KemUtil.getKeyGenerator(crypto, kemName); + keyGenerator.init(new KEMGenerateSpec.Builder(publicKey, "DEF", 256).withNoKdf().build()); + return (SecretKeyWithEncapsulation)keyGenerator.generateKey(); } catch (Exception e) { @@ -117,51 +74,28 @@ public SecretKeyWithEncapsulation encapsulate(PublicKey publicKey) } } - public byte[] encodePublicKey(MLKEMPublicKeyParameters publicKey) + public byte[] encodePublicKey(PublicKey publicKey) + throws IOException { - return publicKey.getEncoded(); + return KemUtil.encodePublicKey(publicKey); } - private void init() + public KeyPair generateKeyPair() { + // TODO How to pass only the SecureRandom? // try // { -//// kpg = KeyPairGenerator.getInstance("MLKEM"); -//// kpg.initialize(MLKEMParameterSpec.fromName(domainParameters.getName()), crypto.getSecureRandom()); -//// keyGen = KeyGenerator.getInstance(domainParameters.getName(), "BC"); -// -//// cipher = KemUtil.getCipher(crypto, domainParameters.getName()); -// -// +// KeyPairGenerator keyPairGenerator = KemUtil.getKeyPairGenerator(crypto, kemName); +// keyPairGenerator.initialize((AlgorithmParameterSpec)null, crypto.getSecureRandom()); +// return keyPairGenerator.generateKeyPair(); // } // catch (GeneralSecurityException e) // { // throw Exceptions.illegalStateException("unable to create key pair: " + e.getMessage(), e); // } - - } - public KeyPair generateKeyPair() - { -// AlgorithmParameters params = KemUtil.getAlgorithmParameters(crypto, domainParameters.getName()); -// if (params == null) -// { -// throw new IllegalStateException("KEM parameters unavailable"); -// } - KeyPairGenerator kpg = null; - try - { - kpg = crypto.getHelper().createKeyPairGenerator(domainParameters.getName()); - } - catch (NoSuchAlgorithmException e) - { - throw new RuntimeException(e); - } - catch (NoSuchProviderException e) - { - throw new RuntimeException(e); - } - return kpg.generateKeyPair(); + KeyPairGenerator keyPairGenerator = KemUtil.getKeyPairGenerator(crypto, kemName); + return keyPairGenerator.generateKeyPair(); } public boolean isServer() diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java index ce55c02524..9f747143d9 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java @@ -1,22 +1,90 @@ package org.bouncycastle.tls.crypto.impl.jcajce; -import java.security.AlgorithmParameters; +import java.io.IOException; +import java.security.KeyFactory; +import java.security.KeyPairGenerator; +import java.security.PublicKey; +import java.security.spec.EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; -import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; -import org.bouncycastle.util.Exceptions; +import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.jcajce.interfaces.MLKEMPublicKey; +import org.bouncycastle.jcajce.spec.MLKEMParameterSpec; +import org.bouncycastle.jcajce.spec.MLKEMPublicKeySpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.tls.AlertDescription; +import org.bouncycastle.tls.TlsFatalAlert; class KemUtil { - static AlgorithmParameters getAlgorithmParameters(JcaTlsCrypto crypto, String kemName) + static PublicKey decodePublicKey(JcaTlsCrypto crypto, String kemName, byte[] encoding) throws TlsFatalAlert { try { - return null; -// AlgorithmParameters algParams = AlgorithmParameters.getInstance(kemName, "BC"); -// MLKEMParameterSpec mlkemSpec = MLKEMParameterSpec.fromName(kemName); -// algParams.init(mlkemSpec); -// return algParams; + KeyFactory kf = crypto.getHelper().createKeyFactory(kemName); + + // More efficient BC-specific method + if (kf.getProvider() instanceof BouncyCastleProvider) + { + try + { + // TODO Add RawEncodedKeySpec support to BC? + + MLKEMParameterSpec params = MLKEMParameterSpec.fromName(kemName); + MLKEMPublicKeySpec keySpec = new MLKEMPublicKeySpec(params, encoding); + return kf.generatePublic(keySpec); + } + catch (Exception e) + { + // Fallback to X.509 + } + } + + EncodedKeySpec keySpec = createX509EncodedKeySpec(getAlgorithmOID(kemName), encoding); + return kf.generatePublic(keySpec); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.illegal_parameter, e); + } + } + + static byte[] encodePublicKey(PublicKey publicKey) throws TlsFatalAlert + { + // More efficient BC-specific method + if (publicKey instanceof MLKEMPublicKey) + { + return ((MLKEMPublicKey)publicKey).getPublicData(); + } + + if (!"X.509".equals(publicKey.getFormat())) + { + throw new TlsFatalAlert(AlertDescription.internal_error, "Public key format unrecognized"); + } + + try + { + SubjectPublicKeyInfo spki = SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()); + return spki.getPublicKeyData().getOctets(); + } + catch (Exception e) + { + throw new TlsFatalAlert(AlertDescription.internal_error, e); + } + } + + static KeyFactory getKeyFactory(JcaTlsCrypto crypto, String kemName) + + { + try + { + return crypto.getHelper().createKeyFactory(kemName); } catch (AssertionError e) { @@ -28,18 +96,33 @@ static AlgorithmParameters getAlgorithmParameters(JcaTlsCrypto crypto, String ke return null; } - static Cipher getCipher(JcaTlsCrypto crypto, String kemName) + static KeyGenerator getKeyGenerator(JcaTlsCrypto crypto, String kemName) { try { - return crypto.getHelper().createCipher(kemName); + return crypto.getHelper().createKeyGenerator(kemName); + } + catch (AssertionError e) + { + } + catch (Exception e) + { + } + + return null; + } + + static KeyPairGenerator getKeyPairGenerator(JcaTlsCrypto crypto, String kemName) + { + try + { + return crypto.getHelper().createKeyPairGenerator(kemName); } catch (AssertionError e) { } catch (Exception e) { - throw Exceptions.illegalStateException("KEM cipher failed: " + kemName, e); } return null; @@ -47,28 +130,35 @@ static Cipher getCipher(JcaTlsCrypto crypto, String kemName) static boolean isKemSupported(JcaTlsCrypto crypto, String kemName) { - // TODO[tls-kem] When implemented via provider, need to check for support dynamically - return kemName != null && getCipher(crypto, kemName) != null; + return kemName != null + && getKeyFactory(crypto, kemName) != null + && getKeyGenerator(crypto, kemName) != null + && getKeyPairGenerator(crypto, kemName) != null; } - // TODO: not used? - static int getEncapsulationLength(String kemName) + private static X509EncodedKeySpec createX509EncodedKeySpec(ASN1ObjectIdentifier oid, byte[] encoding) + throws IOException { - if ("ML-KEM-512".equals(kemName)) - { - return 768; - } - else if ("ML-KEM-768".equals(kemName)) + AlgorithmIdentifier algID = new AlgorithmIdentifier(oid); + SubjectPublicKeyInfo spki = new SubjectPublicKeyInfo(algID, encoding); + return new X509EncodedKeySpec(spki.getEncoded(ASN1Encoding.DER)); + } + + private static ASN1ObjectIdentifier getAlgorithmOID(String kemName) + { + if ("ML-KEM-512".equalsIgnoreCase(kemName)) { - return 1088; + return NISTObjectIdentifiers.id_alg_ml_kem_512; } - else if ("ML-KEM-1024".equals(kemName)) + if ("ML-KEM-768".equalsIgnoreCase(kemName)) { - return 1568; + return NISTObjectIdentifiers.id_alg_ml_kem_768; } - else + if ("ML-KEM-1024".equalsIgnoreCase(kemName)) { - throw new IllegalArgumentException("unknown kem name " + kemName); + return NISTObjectIdentifiers.id_alg_ml_kem_1024; } + + throw Exceptions.illegalArgumentException("unknown kem name " + kemName, null); } } From 800e5f17785d98aee11d86f32ca66a5e965e087a Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 26 May 2025 17:15:57 +1000 Subject: [PATCH 375/890] Java 5 updates. --- .../bcpg/ArmoredOutputStream.java | 2 +- ...ractOpenPGPDocumentSignatureGenerator.java | 7 +++-- .../openpgp/api/OpenPGPKeyEditor.java | 3 +- .../openpgp/api/OpenPGPKeyGenerator.java | 3 +- .../openpgp/api/OpenPGPKeyMaterialPool.java | 7 +++-- .../openpgp/api/OpenPGPMessageGenerator.java | 31 +++++++++++++++---- .../openpgp/api/SignatureParameters.java | 10 ++++-- .../openpgp/api/SignatureParameters.java | 8 +++-- .../api/test/ChangeKeyPassphraseTest.java | 13 +++++--- .../api/test/OpenPGPCertificateTest.java | 5 ++- ...OpenPGPDetachedSignatureProcessorTest.java | 23 +++++--------- .../api/test/OpenPGPMessageGeneratorTest.java | 15 ++++----- .../StaticV6OpenPGPMessageGeneratorTest.java | 8 ++--- 13 files changed, 79 insertions(+), 56 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredOutputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredOutputStream.java index 76947cfb8d..77cabdcb76 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredOutputStream.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredOutputStream.java @@ -639,7 +639,7 @@ public Builder addSplitMultilineComment(String comment) line = line.substring(availableCommentCharsPerLine).trim(); } - if (!line.isEmpty()) + if (line.length() != 0) { addComment(line); } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java index fc50d3b7f9..aeed3dd545 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java @@ -5,7 +5,6 @@ import java.util.Date; import java.util.Iterator; import java.util.List; -import java.util.Objects; import java.util.function.IntPredicate; import org.bouncycastle.bcpg.sig.PreferredAlgorithms; @@ -64,7 +63,11 @@ public AbstractOpenPGPDocumentSignatureGenerator(OpenPGPImplementation implement */ public T setSigningKeySelector(SubkeySelector signingKeySelector) { - this.signingKeySelector = Objects.requireNonNull(signingKeySelector); + if (signingKeySelector == null) + { + throw new NullPointerException(); + } + this.signingKeySelector = signingKeySelector; return (T)this; } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyEditor.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyEditor.java index ee2143d709..0f9b9a1f59 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyEditor.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyEditor.java @@ -101,7 +101,8 @@ public OpenPGPKeyEditor addUserId(String userId, SignatureParameters.Callback signatureCallback) throws PGPException { - if (userId == null || userId.trim().isEmpty()) + // care needs to run with Java 5 + if (userId == null || userId.trim().length() == 0) { throw new IllegalArgumentException("User-ID cannot be null or empty."); } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java index cdcaa774de..7ce19c3ad3 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyGenerator.java @@ -379,7 +379,8 @@ public WithPrimaryKey addUserId( SignatureParameters.Callback signatureParameters) throws PGPException { - if (userId == null || userId.trim().isEmpty()) + // care - needs to run with Java 5. + if (userId == null || userId.trim().length() == 0) { throw new IllegalArgumentException("User-ID cannot be null or empty."); } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java index 74d102a8ac..9dd543316a 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPKeyMaterialPool.java @@ -4,7 +4,6 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -63,7 +62,11 @@ public OpenPGPKeyMaterialPool(Collection items) */ public OpenPGPKeyMaterialPool setMissingItemCallback(OpenPGPKeyMaterialProvider callback) { - this.callback = Objects.requireNonNull(callback); + if (callback == null) + { + throw new NullPointerException(); + } + this.callback = callback; return this; } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java index a6369b387a..09f751bb05 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPMessageGenerator.java @@ -8,7 +8,6 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Objects; import java.util.Set; import org.bouncycastle.bcpg.AEADAlgorithmTags; @@ -571,7 +570,11 @@ public int negotiateCompression(OpenPGPMessageGenerator configuration, OpenPGPPo */ public OpenPGPMessageGenerator setPasswordBasedEncryptionNegotiator(OpenPGPEncryptionNegotiator pbeNegotiator) { - this.passwordBasedEncryptionNegotiator = Objects.requireNonNull(pbeNegotiator); + if (pbeNegotiator == null) + { + throw new NullPointerException(); + } + this.passwordBasedEncryptionNegotiator = pbeNegotiator; return this; } @@ -584,7 +587,11 @@ public OpenPGPMessageGenerator setPasswordBasedEncryptionNegotiator(OpenPGPEncry */ public OpenPGPMessageGenerator setPublicKeyBasedEncryptionNegotiator(OpenPGPEncryptionNegotiator pkbeNegotiator) { - this.publicKeyBasedEncryptionNegotiator = Objects.requireNonNull(pkbeNegotiator); + if (pkbeNegotiator == null) + { + throw new NullPointerException(); + } + this.publicKeyBasedEncryptionNegotiator = pkbeNegotiator; return this; } @@ -598,7 +605,11 @@ public OpenPGPMessageGenerator setPublicKeyBasedEncryptionNegotiator(OpenPGPEncr */ public OpenPGPMessageGenerator setEncryptionKeySelector(SubkeySelector encryptionKeySelector) { - this.encryptionKeySelector = Objects.requireNonNull(encryptionKeySelector); + if (encryptionKeySelector == null) + { + throw new NullPointerException(); + } + this.encryptionKeySelector = encryptionKeySelector; return this; } @@ -612,7 +623,11 @@ public OpenPGPMessageGenerator setEncryptionKeySelector(SubkeySelector encryptio */ public OpenPGPMessageGenerator setCompressionNegotiator(CompressionNegotiator compressionNegotiator) { - this.compressionNegotiator = Objects.requireNonNull(compressionNegotiator); + if (compressionNegotiator == null) + { + throw new NullPointerException(); + } + this.compressionNegotiator = compressionNegotiator; return this; } @@ -624,7 +639,11 @@ public OpenPGPMessageGenerator setCompressionNegotiator(CompressionNegotiator co */ public OpenPGPMessageGenerator setArmorStreamFactory(ArmoredOutputStreamFactory factory) { - this.armorStreamFactory = Objects.requireNonNull(factory); + if (factory == null) + { + throw new NullPointerException(); + } + this.armorStreamFactory = factory; return this; } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureParameters.java b/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureParameters.java index 61e30b923b..26611a7d0c 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureParameters.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/SignatureParameters.java @@ -1,7 +1,6 @@ package org.bouncycastle.openpgp.api; import java.util.Date; -import java.util.Objects; import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; @@ -200,8 +199,13 @@ public int getSignatureType() */ public SignatureParameters setSignatureCreationTime(Date signatureCreationTime) { - this.signatureCreationTime = Objects.requireNonNull(signatureCreationTime, - "Signature creation time cannot be null."); + if (signatureCreationTime == null) + { + throw new NullPointerException("Signature creation time cannot be null."); + } + + this.signatureCreationTime = signatureCreationTime; + return this; } diff --git a/pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/SignatureParameters.java b/pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/SignatureParameters.java index cde370b70c..0c28b40b88 100644 --- a/pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/SignatureParameters.java +++ b/pg/src/main/jdk1.5/org/bouncycastle/openpgp/api/SignatureParameters.java @@ -5,7 +5,6 @@ import org.bouncycastle.util.Arrays; import java.util.Date; -import java.util.Objects; /** * Parameters for signature generation. @@ -200,8 +199,11 @@ public int getSignatureType() */ public SignatureParameters setSignatureCreationTime(Date signatureCreationTime) { - this.signatureCreationTime = Objects.requireNonNull(signatureCreationTime, - "Signature creation time cannot be null."); + if (signatureCreationTime == null) + { + throw new NullPointerException("Signature creation time cannot be null."); + } + this.signatureCreationTime = signatureCreationTime; return this; } diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/ChangeKeyPassphraseTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/ChangeKeyPassphraseTest.java index d4a6fe23cf..ed24018f57 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/ChangeKeyPassphraseTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/ChangeKeyPassphraseTest.java @@ -15,11 +15,14 @@ public class ChangeKeyPassphraseTest protected void performTestWith(OpenPGPApi api) throws PGPException, IOException { - removeAEADPassphrase(api); - addAEADPassphrase(api); - changeAEADPassphrase(api); - - testChangingCFBPassphrase(api); + if (System.getProperty("java.version").indexOf("1.5.") < 0) + { + removeAEADPassphrase(api); + addAEADPassphrase(api); + changeAEADPassphrase(api); + + testChangingCFBPassphrase(api); + } } private void removeAEADPassphrase(OpenPGPApi api) diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java index 7b0d2129f1..7fbd93f5a2 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPCertificateTest.java @@ -2,7 +2,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.List; @@ -26,6 +25,7 @@ import org.bouncycastle.openpgp.api.SignatureSubpacketsFunction; import org.bouncycastle.openpgp.api.util.UTCUtil; import org.bouncycastle.openpgp.bc.BcPGPObjectFactory; +import org.bouncycastle.util.Strings; public class OpenPGPCertificateTest extends APITest @@ -808,7 +808,6 @@ private void testGetPrimaryUserId(OpenPGPApi api) .addUserId("New primary ", SignatureParameters.Callback.Util.modifyHashedSubpackets(new SignatureSubpacketsFunction() { - @Override public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) { subpackets.removePacketsOfType(SignatureSubpacketTags.CREATION_TIME); @@ -850,7 +849,7 @@ public TestSignature(String armoredSignature, boolean expectValid, String msg) private static PGPSignature parseSignature(String armoredSignature) throws IOException { - ByteArrayInputStream bIn = new ByteArrayInputStream(armoredSignature.getBytes(StandardCharsets.UTF_8)); + ByteArrayInputStream bIn = new ByteArrayInputStream(Strings.toUTF8ByteArray(armoredSignature)); ArmoredInputStream aIn = new ArmoredInputStream(bIn); BCPGInputStream pIn = new BCPGInputStream(aIn); PGPObjectFactory objFac = new BcPGPObjectFactory(pIn); diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java index 90e4cccf3d..a7f76eb646 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java @@ -3,7 +3,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.List; @@ -56,7 +55,7 @@ private void createVerifyV4Signature(OpenPGPApi api) gen.addSigningKey( api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.ALICE_KEY)); - byte[] plaintext = "Hello, World!\n".getBytes(StandardCharsets.UTF_8); + byte[] plaintext = Strings.toUTF8ByteArray("Hello, World!\n"); ByteArrayInputStream plaintextIn = new ByteArrayInputStream(plaintext); List signatures = gen.sign(plaintextIn); @@ -83,7 +82,7 @@ private void createVerifyV6Signature(OpenPGPApi api) gen.addSigningKey( api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY)); - byte[] plaintext = "Hello, World!\n".getBytes(StandardCharsets.UTF_8); + byte[] plaintext = Strings.toUTF8ByteArray("Hello, World!\n"); ByteArrayInputStream plaintextIn = new ByteArrayInputStream(plaintext); List signatures = gen.sign(plaintextIn); @@ -110,13 +109,12 @@ private void missingPassphraseThrows(final OpenPGPApi api) "KeyPassphraseException", new TestExceptionOperation() { - @Override public void operation() throws Exception { api.createDetachedSignature() .addSigningKey(api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY_LOCKED)) - .sign(new ByteArrayInputStream("Test Data".getBytes(StandardCharsets.UTF_8))); + .sign(new ByteArrayInputStream(Strings.toUTF8ByteArray("Test Data"))); } })); } @@ -128,14 +126,13 @@ private void wrongPassphraseThrows(final OpenPGPApi api) "KeyPassphraseException", new TestExceptionOperation() { - @Override public void operation() throws Exception { api.createDetachedSignature() .addKeyPassphrase("thisIsWrong".toCharArray()) .addSigningKey(api.readKeyOrCertificate().parseKey(OpenPGPTestKeys.V6_KEY_LOCKED)) - .sign(new ByteArrayInputStream("Test Data".getBytes(StandardCharsets.UTF_8))); + .sign(new ByteArrayInputStream(Strings.toUTF8ByteArray("Test Data"))); } })); } @@ -154,7 +151,7 @@ private void keyPassphrasesArePairedUpProperly_keyAddedFirst(OpenPGPApi api) gen.addKeyPassphrase("password".toCharArray()); gen.addKeyPassphrase("beluga".toCharArray()); - byte[] plaintext = "arctic\ndeep sea\nice field\n".getBytes(StandardCharsets.UTF_8); + byte[] plaintext = Strings.toUTF8ByteArray("arctic\ndeep sea\nice field\n"); InputStream plaintextIn = new ByteArrayInputStream(plaintext); List signatures = gen.sign(plaintextIn); @@ -176,7 +173,7 @@ private void keyPassphrasesArePairedUpProperly_passphraseAddedFirst(OpenPGPApi a gen.addSigningKey(key); - byte[] plaintext = "jungle\ntropics\nswamp\n".getBytes(StandardCharsets.UTF_8); + byte[] plaintext = Strings.toUTF8ByteArray("jungle\ntropics\nswamp\n"); InputStream plaintextIn = new ByteArrayInputStream(plaintext); List signatures = gen.sign(plaintextIn); @@ -198,7 +195,6 @@ public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) SignatureParameters.Callback.Util.modifyHashedSubpackets( new SignatureSubpacketsFunction() { - @Override public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) { subpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS); @@ -215,13 +211,12 @@ public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpa "InvalidSigningKeyException", new TestExceptionOperation() { - @Override public void operation() throws Exception { api.createDetachedSignature() .addSigningKey(noSigningKey) - .sign(new ByteArrayInputStream("Test Data".getBytes(StandardCharsets.UTF_8))); + .sign(new ByteArrayInputStream(Strings.toUTF8ByteArray("Test Data"))); } })); } @@ -241,7 +236,6 @@ public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) SignatureParameters.Callback.Util.modifyHashedSubpackets( new SignatureSubpacketsFunction() { - @Override public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) { subpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS); @@ -258,13 +252,12 @@ public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpa "InvalidSigningKeyException", new TestExceptionOperation() { - @Override public void operation() throws Exception { api.createDetachedSignature() .addSigningKey(noSigningKey.getPrimarySecretKey(), (char[])null, null) - .sign(new ByteArrayInputStream("Test Data".getBytes(StandardCharsets.UTF_8))); + .sign(new ByteArrayInputStream(Strings.toUTF8ByteArray("Test Data"))); } })); } diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java index 332b4b2129..bd709d7355 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageGeneratorTest.java @@ -3,7 +3,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; -import java.nio.charset.StandardCharsets; import org.bouncycastle.bcpg.CompressionAlgorithmTags; import org.bouncycastle.openpgp.OpenPGPTestKeys; @@ -50,7 +49,7 @@ private void armoredLiteralDataPacket(OpenPGPApi api) OpenPGPMessageOutputStream msgOut = gen.open(bOut); // Only write a LiteralData packet with "Hello, World!" as content - msgOut.write("Hello, World!".getBytes(StandardCharsets.UTF_8)); + msgOut.write(Strings.toUTF8ByteArray("Hello, World!")); msgOut.close(); @@ -74,7 +73,7 @@ private void unarmoredLiteralDataPacket(OpenPGPApi api) OpenPGPMessageOutputStream msgOut = gen.open(bOut); // Only write a LiteralData packet with "Hello, World!" as content - msgOut.write("Hello, World!".getBytes(StandardCharsets.UTF_8)); + msgOut.write(Strings.toUTF8ByteArray("Hello, World!")); msgOut.close(); @@ -88,7 +87,6 @@ private void armoredCompressedLiteralDataPacket(OpenPGPApi api) .setAllowPadding(false) .setCompressionNegotiator(new OpenPGPMessageGenerator.CompressionNegotiator() { - @Override public int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPGPPolicy policy) { return CompressionAlgorithmTags.ZIP; @@ -99,7 +97,7 @@ public int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPG OpenPGPMessageOutputStream msgOut = gen.open(bOut); // Only write a LiteralData packet with "Hello, World!" as content - msgOut.write("Hello, World!".getBytes(StandardCharsets.UTF_8)); + msgOut.write(Strings.toUTF8ByteArray("Hello, World!")); msgOut.close(); @@ -120,7 +118,6 @@ private void unarmoredCompressedLiteralDataPacket(OpenPGPApi api) .setAllowPadding(false) .setCompressionNegotiator(new OpenPGPMessageGenerator.CompressionNegotiator() { - @Override public int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPGPPolicy policy) { return CompressionAlgorithmTags.ZIP; @@ -131,7 +128,7 @@ public int negotiateCompression(OpenPGPMessageGenerator messageGenerator, OpenPG OpenPGPMessageOutputStream msgOut = gen.open(bOut); // Only write a LiteralData packet with "Hello, World!" as content - msgOut.write("Hello, World!".getBytes(StandardCharsets.UTF_8)); + msgOut.write(Strings.toUTF8ByteArray("Hello, World!")); msgOut.close(); @@ -148,7 +145,7 @@ private void seipd2EncryptedMessage(OpenPGPApi api) ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OutputStream encOut = gen.open(bOut); - encOut.write("Hello World!\n".getBytes(StandardCharsets.UTF_8)); + encOut.write(Strings.toUTF8ByteArray("Hello, World!")); encOut.close(); System.out.println(bOut); @@ -164,7 +161,7 @@ private void seipd1EncryptedMessage(OpenPGPApi api) ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OutputStream encOut = gen.open(bOut); - encOut.write("Hello World!\n".getBytes(StandardCharsets.UTF_8)); + encOut.write(Strings.toUTF8ByteArray("Hello, World!")); encOut.close(); System.out.println(bOut); diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/StaticV6OpenPGPMessageGeneratorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/StaticV6OpenPGPMessageGeneratorTest.java index 16e9fc96ff..a42709331b 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/StaticV6OpenPGPMessageGeneratorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/StaticV6OpenPGPMessageGeneratorTest.java @@ -2,7 +2,6 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.List; @@ -17,6 +16,7 @@ import org.bouncycastle.openpgp.api.OpenPGPMessageOutputStream; import org.bouncycastle.openpgp.api.OpenPGPPolicy; import org.bouncycastle.openpgp.api.SubkeySelector; +import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; public class StaticV6OpenPGPMessageGeneratorTest @@ -53,7 +53,7 @@ private void staticEncryptedMessage() ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OpenPGPMessageOutputStream pgOut = (OpenPGPMessageOutputStream) gen.open(bOut); - pgOut.write("Hello, World!\n".getBytes(StandardCharsets.UTF_8)); + pgOut.write(Strings.toUTF8ByteArray("Hello, World!\n")); pgOut.close(); System.out.println(bOut); @@ -68,7 +68,7 @@ private void staticSignedMessage() ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OpenPGPMessageOutputStream pgOut = (OpenPGPMessageOutputStream) gen.open(bOut); - pgOut.write("Hello, World!\n".getBytes(StandardCharsets.UTF_8)); + pgOut.write(Strings.toUTF8ByteArray("Hello, World!\n")); pgOut.close(); System.out.println(bOut); @@ -86,7 +86,6 @@ public OpenPGPMessageGenerator getStaticGenerator() OpenPGPMessageGenerator gen = new OpenPGPMessageGenerator() .setSigningKeySelector(new SubkeySelector() { - @Override public List select( OpenPGPCertificate certificate, OpenPGPPolicy policy) { @@ -95,7 +94,6 @@ public List select( }) .setEncryptionKeySelector( new SubkeySelector() { - @Override public List select(OpenPGPCertificate certificate, OpenPGPPolicy policy) { return Collections.singletonList(certificate.getKey(encryptionKeyIdentifier)); } From 1ac8c027b4cf6373d66695cd3bbefffc46849657 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 26 May 2025 22:46:55 +0700 Subject: [PATCH 376/890] TLS: Cleanup KEM tests --- .../tls/test/MockTlsKemClient.java | 4 --- .../tls/test/MockTlsKemServer.java | 5 ---- .../tls/test/TlsProtocolKemTest.java | 27 ++++++++++++++++--- 3 files changed, 23 insertions(+), 13 deletions(-) diff --git a/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemClient.java b/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemClient.java index c40ba27195..9893b905dc 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemClient.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemClient.java @@ -2,12 +2,10 @@ import java.io.IOException; import java.io.PrintStream; -import java.security.SecureRandom; import java.util.Hashtable; import java.util.Vector; import org.bouncycastle.asn1.x509.Certificate; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.tls.AlertDescription; import org.bouncycastle.tls.AlertLevel; import org.bouncycastle.tls.CertificateRequest; @@ -29,8 +27,6 @@ import org.bouncycastle.tls.TlsUtils; import org.bouncycastle.tls.crypto.TlsCertificate; import org.bouncycastle.tls.crypto.TlsCrypto; -import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto; -import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Integers; import org.bouncycastle.util.encoders.Hex; diff --git a/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemServer.java b/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemServer.java index 6b790fc844..333af53f90 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemServer.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/MockTlsKemServer.java @@ -2,13 +2,11 @@ import java.io.IOException; import java.io.PrintStream; -import java.security.SecureRandom; import java.util.Hashtable; import java.util.Vector; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x509.Certificate; -import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.tls.AlertDescription; import org.bouncycastle.tls.AlertLevel; import org.bouncycastle.tls.CertificateRequest; @@ -26,9 +24,6 @@ import org.bouncycastle.tls.TlsUtils; import org.bouncycastle.tls.crypto.TlsCertificate; import org.bouncycastle.tls.crypto.TlsCrypto; -import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto; -import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCrypto; -import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider; import org.bouncycastle.util.encoders.Hex; class MockTlsKemServer diff --git a/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolKemTest.java b/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolKemTest.java index caa01ee5f4..2dcaedca6d 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolKemTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolKemTest.java @@ -23,8 +23,9 @@ protected TlsProtocolKemTest(TlsCrypto crypto) { this.crypto = crypto; } - // mismatched ML-KEM strengths w/o classical crypto - public void testMismatchStrength() throws Exception + + // mismatched ML-KEM groups w/o classical crypto + public void testMismatchedGroups() throws Exception { PipedInputStream clientRead = TlsTestUtils.createPipedInputStream(); PipedInputStream serverRead = TlsTestUtils.createPipedInputStream(); @@ -42,6 +43,7 @@ public void testMismatchStrength() throws Exception catch (Exception ignored) { } + MockTlsKemClient client = new MockTlsKemClient(crypto, null); client.setNamedGroups(new int[]{ NamedGroup.MLKEM512 }); try @@ -56,7 +58,22 @@ public void testMismatchStrength() throws Exception serverThread.join(); } - public void testClientServer() throws Exception + public void testMLKEM512() throws Exception + { + implTestClientServer(NamedGroup.MLKEM512); + } + + public void testMLKEM768() throws Exception + { + implTestClientServer(NamedGroup.MLKEM768); + } + + public void testMLKEM1024() throws Exception + { + implTestClientServer(NamedGroup.MLKEM1024); + } + + private void implTestClientServer(int kemGroup) throws Exception { PipedInputStream clientRead = TlsTestUtils.createPipedInputStream(); PipedInputStream serverRead = TlsTestUtils.createPipedInputStream(); @@ -66,10 +83,12 @@ public void testClientServer() throws Exception TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); - ServerThread serverThread = new ServerThread(crypto, serverProtocol, null, false); + ServerThread serverThread = new ServerThread(crypto, serverProtocol, new int[]{ kemGroup }, false); serverThread.start(); MockTlsKemClient client = new MockTlsKemClient(crypto, null); + client.setNamedGroups(new int[]{ kemGroup }); + clientProtocol.connect(client); // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity From 146c93ac38b6d08d7d07f6eb43102452330f40e6 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 26 May 2025 23:07:33 +0700 Subject: [PATCH 377/890] TLS: low-level hybrid ECDHE-MLKEM - see draft-ietf-tls-ecdhe-mlkem-00 --- .../java/org/bouncycastle/tls/NamedGroup.java | 89 ++++++- .../bouncycastle/tls/TlsServerProtocol.java | 18 +- .../java/org/bouncycastle/tls/TlsUtils.java | 159 ++++++++---- .../bouncycastle/tls/crypto/TlsCrypto.java | 2 + .../tls/crypto/TlsHybridAgreement.java | 48 ++++ .../tls/crypto/impl/bc/BcTlsCrypto.java | 9 +- .../tls/crypto/impl/jcajce/JcaTlsCrypto.java | 5 + .../org/bouncycastle/tls/test/AllTests.java | 2 + .../tls/test/BcTlsProtocolHybridTest.java | 12 + .../tls/test/JcaTlsProtocolHybridTest.java | 15 ++ .../tls/test/MockTlsHybridClient.java | 244 +++++++++++++++++ .../tls/test/MockTlsHybridServer.java | 245 ++++++++++++++++++ .../tls/test/TlsProtocolHybridTest.java | 165 ++++++++++++ 13 files changed, 940 insertions(+), 73 deletions(-) create mode 100644 tls/src/main/java/org/bouncycastle/tls/crypto/TlsHybridAgreement.java create mode 100644 tls/src/test/java/org/bouncycastle/tls/test/BcTlsProtocolHybridTest.java create mode 100644 tls/src/test/java/org/bouncycastle/tls/test/JcaTlsProtocolHybridTest.java create mode 100644 tls/src/test/java/org/bouncycastle/tls/test/MockTlsHybridClient.java create mode 100644 tls/src/test/java/org/bouncycastle/tls/test/MockTlsHybridServer.java create mode 100644 tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolHybridTest.java diff --git a/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java b/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java index a69ecd1bc9..d8338c2929 100644 --- a/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java +++ b/tls/src/main/java/org/bouncycastle/tls/NamedGroup.java @@ -116,6 +116,13 @@ public class NamedGroup public static final int MLKEM768 = 0x0201; public static final int MLKEM1024 = 0x0202; + /* + * draft-ietf-tls-ecdhe-mlkem-00 + */ + public static final int SecP256r1MLKEM768 = 0x11EB; + public static final int X25519MLKEM768 = 0x11EC; + public static final int SecP384r1MLKEM1024 = 0x11ED; + /* Names of the actual underlying elliptic curves (not necessarily matching the NamedGroup names). */ private static final String[] CURVE_NAMES = new String[]{ "sect163k1", "sect163r1", "sect163r2", "sect193r1", "sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1", @@ -163,7 +170,7 @@ public static boolean canBeNegotiated(int namedGroup, ProtocolVersion version) } } - if (refersToASpecificKem(namedGroup)) + if (refersToASpecificHybrid(namedGroup) || refersToASpecificKem(namedGroup)) { return isTLSv13; } @@ -297,6 +304,66 @@ public static String getFiniteFieldName(int namedGroup) return null; } + public static int getHybridFirst(int namedGroup) + { + switch (namedGroup) + { + case SecP256r1MLKEM768: + return secp256r1; + case X25519MLKEM768: + return MLKEM768; + case SecP384r1MLKEM1024: + return secp384r1; + default: + return -1; + } + } + + public static int getHybridSecond(int namedGroup) + { + switch (namedGroup) + { + case SecP256r1MLKEM768: + return MLKEM768; + case X25519MLKEM768: + return x25519; + case SecP384r1MLKEM1024: + return MLKEM1024; + default: + return -1; + } + } + + // TODO Temporary until crypto implementations become more self-documenting around lengths + static int getHybridSplitClientShare(int namedGroup) + { + switch (namedGroup) + { + case secp256r1: + return 65; + case secp384r1: + return 97; + case MLKEM768: + return 1184; + } + return -1; + } + + // TODO Temporary until crypto implementations become more self-documenting around lengths + static int getHybridSplitServerShare(int namedGroup) + { + switch (namedGroup) + { + case secp256r1: + return 65; + case secp384r1: + return 97; + case MLKEM768: + return 1088; + } + return -1; + } + public static String getKemName(int namedGroup) { switch (namedGroup) @@ -382,6 +449,12 @@ public static String getName(int namedGroup) return "MLKEM768"; case MLKEM1024: return "MLKEM1024"; + case SecP256r1MLKEM768: + return "SecP256r1MLKEM768"; + case X25519MLKEM768: + return "X25519MLKEM768"; + case SecP384r1MLKEM1024: + return "SecP384r1MLKEM1024"; case arbitrary_explicit_prime_curves: return "arbitrary_explicit_prime_curves"; case arbitrary_explicit_char2_curves: @@ -489,9 +562,23 @@ public static boolean refersToASpecificGroup(int namedGroup) { return refersToASpecificCurve(namedGroup) || refersToASpecificFiniteField(namedGroup) + || refersToASpecificHybrid(namedGroup) || refersToASpecificKem(namedGroup); } + public static boolean refersToASpecificHybrid(int namedGroup) + { + switch (namedGroup) + { + case SecP256r1MLKEM768: + case X25519MLKEM768: + case SecP384r1MLKEM1024: + return true; + default: + return false; + } + } + public static boolean refersToASpecificKem(int namedGroup) { switch (namedGroup) diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java index 3ace602565..5b2d3ae82b 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java @@ -396,21 +396,9 @@ protected ServerHello generate13ServerHello(ClientHello clientHello, HandshakeMe TlsSecret sharedSecret; { int namedGroup = clientShare.getNamedGroup(); - - TlsAgreement agreement; - if (NamedGroup.refersToAnECDHCurve(namedGroup)) - { - agreement = crypto.createECDomain(new TlsECConfig(namedGroup)).createECDH(); - } - else if (NamedGroup.refersToASpecificFiniteField(namedGroup)) - { - agreement = crypto.createDHDomain(new TlsDHConfig(namedGroup, true)).createDH(); - } - else if (NamedGroup.refersToASpecificKem(namedGroup)) - { - agreement = crypto.createKemDomain(new TlsKemConfig(namedGroup, true)).createKem(); - } - else + + TlsAgreement agreement = TlsUtils.createKeyShare(crypto, namedGroup, true); + if (agreement == null) { throw new TlsFatalAlert(AlertDescription.internal_error); } diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java b/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java index 005bf51c8a..eb9415cee1 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java @@ -40,6 +40,7 @@ import org.bouncycastle.tls.crypto.TlsEncryptor; import org.bouncycastle.tls.crypto.TlsHash; import org.bouncycastle.tls.crypto.TlsHashOutputStream; +import org.bouncycastle.tls.crypto.TlsHybridAgreement; import org.bouncycastle.tls.crypto.TlsKemConfig; import org.bouncycastle.tls.crypto.TlsSecret; import org.bouncycastle.tls.crypto.TlsStreamSigner; @@ -1149,7 +1150,7 @@ public static void addIfSupported(Vector supportedAlgs, TlsCrypto crypto, Signat public static void addIfSupported(Vector supportedGroups, TlsCrypto crypto, int namedGroup) { - if (crypto.hasNamedGroup(namedGroup)) + if (isSupportedNamedGroup(crypto, namedGroup)) { supportedGroups.addElement(Integers.valueOf(namedGroup)); } @@ -4449,6 +4450,17 @@ public static boolean isSupportedKeyExchange(TlsCrypto crypto, int keyExchangeAl } } + public static boolean isSupportedNamedGroup(TlsCrypto crypto, int namedGroup) + { + if (!NamedGroup.refersToASpecificHybrid(namedGroup)) + { + return crypto.hasNamedGroup(namedGroup); + } + + return crypto.hasNamedGroup(NamedGroup.getHybridFirst(namedGroup)) + && crypto.hasNamedGroup(NamedGroup.getHybridSecond(namedGroup)); + } + static boolean hasAnyRSASigAlgs(TlsCrypto crypto) { return crypto.hasSignatureAlgorithm(SignatureAlgorithm.rsa) @@ -5362,45 +5374,79 @@ private static void collectKeyShares(TlsCrypto crypto, int[] supportedGroups, Ve int supportedGroup = supportedGroups[i]; Integer supportedGroupElement = Integers.valueOf(supportedGroup); - if (!keyShareGroups.contains(supportedGroupElement) - || clientAgreements.containsKey(supportedGroupElement) - || !crypto.hasNamedGroup(supportedGroup)) + if (!keyShareGroups.contains(supportedGroupElement) || + clientAgreements.containsKey(supportedGroupElement)) { continue; } - TlsAgreement agreement = null; - if (NamedGroup.refersToAnECDHCurve(supportedGroup)) + TlsAgreement agreement = createKeyShare(crypto, supportedGroup, false); + if (agreement != null) + { + byte[] key_exchange = agreement.generateEphemeral(); + KeyShareEntry clientShare = new KeyShareEntry(supportedGroup, key_exchange); + + clientShares.addElement(clientShare); + clientAgreements.put(supportedGroupElement, agreement); + } + } + } + + static TlsAgreement createKeyShare(TlsCrypto crypto, int keyShareGroup, boolean isServer) + { + if (!NamedGroup.refersToASpecificHybrid(keyShareGroup)) + { + return createKeyShareSimple(crypto, keyShareGroup, isServer); + } + + int hybridFirst = NamedGroup.getHybridFirst(keyShareGroup); + TlsAgreement firstAgreement = createKeyShareSimple(crypto, hybridFirst, isServer); + if (firstAgreement == null) + { + return null; + } + + int hybridSecond = NamedGroup.getHybridSecond(keyShareGroup); + TlsAgreement secondAgreement = createKeyShareSimple(crypto, hybridSecond, isServer); + if (secondAgreement == null) + { + return null; + } + + int peerValueSplit = isServer + ? NamedGroup.getHybridSplitClientShare(hybridFirst) + : NamedGroup.getHybridSplitServerShare(hybridFirst); + + return new TlsHybridAgreement(crypto, firstAgreement, secondAgreement, peerValueSplit); + } + + private static TlsAgreement createKeyShareSimple(TlsCrypto crypto, int keyShareGroup, boolean isServer) + { + if (crypto.hasNamedGroup(keyShareGroup)) + { + if (NamedGroup.refersToAnECDHCurve(keyShareGroup)) { if (crypto.hasECDHAgreement()) { - agreement = crypto.createECDomain(new TlsECConfig(supportedGroup)).createECDH(); + return crypto.createECDomain(new TlsECConfig(keyShareGroup)).createECDH(); } } - else if (NamedGroup.refersToASpecificFiniteField(supportedGroup)) + else if (NamedGroup.refersToASpecificFiniteField(keyShareGroup)) { if (crypto.hasDHAgreement()) { - agreement = crypto.createDHDomain(new TlsDHConfig(supportedGroup, true)).createDH(); + return crypto.createDHDomain(new TlsDHConfig(keyShareGroup, true)).createDH(); } } - else if (NamedGroup.refersToASpecificKem(supportedGroup)) + else if (NamedGroup.refersToASpecificKem(keyShareGroup)) { if (crypto.hasKemAgreement()) { - agreement = crypto.createKemDomain(new TlsKemConfig(supportedGroup, false)).createKem(); + return crypto.createKemDomain(new TlsKemConfig(keyShareGroup, isServer)).createKem(); } } - - if (null != agreement) - { - byte[] key_exchange = agreement.generateEphemeral(); - KeyShareEntry clientShare = new KeyShareEntry(supportedGroup, key_exchange); - - clientShares.addElement(clientShare); - clientAgreements.put(supportedGroupElement, agreement); - } } + return null; } static KeyShareEntry selectKeyShare(Vector clientShares, int keyShareGroup) @@ -5427,25 +5473,10 @@ static KeyShareEntry selectKeyShare(TlsCrypto crypto, ProtocolVersion negotiated int group = clientShare.getNamedGroup(); - if (!NamedGroup.canBeNegotiated(group, negotiatedVersion)) - { - continue; - } - - if (!Arrays.contains(serverSupportedGroups, group) || - !Arrays.contains(clientSupportedGroups, group)) - { - continue; - } - - if (!crypto.hasNamedGroup(group)) - { - continue; - } - - if ((NamedGroup.refersToAnECDHCurve(group) && crypto.hasECDHAgreement()) || - (NamedGroup.refersToASpecificFiniteField(group) && crypto.hasDHAgreement()) || - (NamedGroup.refersToASpecificKem(group) && crypto.hasKemAgreement())) + if (NamedGroup.canBeNegotiated(group, negotiatedVersion) && + Arrays.contains(serverSupportedGroups, group) && + Arrays.contains(clientSupportedGroups, group) && + supportsKeyShareGroup(crypto, group)) { return clientShare; } @@ -5463,30 +5494,46 @@ static int selectKeyShareGroup(TlsCrypto crypto, ProtocolVersion negotiatedVersi { int group = clientSupportedGroups[i]; - if (!NamedGroup.canBeNegotiated(group, negotiatedVersion)) + if (NamedGroup.canBeNegotiated(group, negotiatedVersion) && + Arrays.contains(serverSupportedGroups, group) && + supportsKeyShareGroup(crypto, group)) { - continue; + return group; } + } + } + return -1; + } - if (!Arrays.contains(serverSupportedGroups, group)) - { - continue; - } + private static boolean supportsKeyShareGroup(TlsCrypto crypto, int keyShareGroup) + { + if (!NamedGroup.refersToASpecificHybrid(keyShareGroup)) + { + return supportsKeyShareGroupSimple(crypto, keyShareGroup); + } - if (!crypto.hasNamedGroup(group)) - { - continue; - } + return supportsKeyShareGroupSimple(crypto, NamedGroup.getHybridFirst(keyShareGroup)) + && supportsKeyShareGroupSimple(crypto, NamedGroup.getHybridSecond(keyShareGroup)); + } - if ((NamedGroup.refersToAnECDHCurve(group) && crypto.hasECDHAgreement()) || - (NamedGroup.refersToASpecificFiniteField(group) && crypto.hasDHAgreement()) || - (NamedGroup.refersToASpecificKem(group) && crypto.hasKemAgreement())) - { - return group; - } + private static boolean supportsKeyShareGroupSimple(TlsCrypto crypto, int keyShareGroup) + { + if (crypto.hasNamedGroup(keyShareGroup)) + { + if (NamedGroup.refersToAnECDHCurve(keyShareGroup)) + { + return crypto.hasECDHAgreement(); + } + else if (NamedGroup.refersToASpecificFiniteField(keyShareGroup)) + { + return crypto.hasDHAgreement(); + } + else if (NamedGroup.refersToASpecificKem(keyShareGroup)) + { + return crypto.hasKemAgreement(); } } - return -1; + return false; } static byte[] readEncryptedPMS(TlsContext context, InputStream input) throws IOException diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/TlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/TlsCrypto.java index ff08bc607f..46b41a1d23 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/TlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/TlsCrypto.java @@ -146,6 +146,8 @@ public interface TlsCrypto */ boolean hasSRPAuthentication(); + TlsSecret createHybridSecret(TlsSecret s1, TlsSecret s2); + /** * Create a TlsSecret object based on provided data. * diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/TlsHybridAgreement.java b/tls/src/main/java/org/bouncycastle/tls/crypto/TlsHybridAgreement.java new file mode 100644 index 0000000000..88d06b0395 --- /dev/null +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/TlsHybridAgreement.java @@ -0,0 +1,48 @@ +package org.bouncycastle.tls.crypto; + +import java.io.IOException; + +import org.bouncycastle.util.Arrays; + +public class TlsHybridAgreement + implements TlsAgreement +{ + private final TlsCrypto crypto; + private final TlsAgreement firstAgreement; + private final TlsAgreement secondAgreement; + private final int peerValueSplit; + + public TlsHybridAgreement(TlsCrypto crypto, TlsAgreement firstAgreement, TlsAgreement secondAgreement, + int peerValueSplit) + { + this.crypto = crypto; + this.firstAgreement = firstAgreement; + this.secondAgreement = secondAgreement; + this.peerValueSplit = peerValueSplit; + } + + public byte[] generateEphemeral() throws IOException + { + byte[] firstEphemeral = firstAgreement.generateEphemeral(); + byte[] secondEphemeral = secondAgreement.generateEphemeral(); + return Arrays.concatenate(firstEphemeral, secondEphemeral); + } + + public void receivePeerValue(byte[] peerValue) throws IOException + { + if (peerValue.length < peerValueSplit) + { + throw new IllegalArgumentException("'peerValue' is too short"); + } + + this.firstAgreement.receivePeerValue(Arrays.copyOfRange(peerValue, 0, peerValueSplit)); + this.secondAgreement.receivePeerValue(Arrays.copyOfRange(peerValue, peerValueSplit, peerValue.length)); + } + + public TlsSecret calculateSecret() throws IOException + { + TlsSecret firstSecret = firstAgreement.calculateSecret(); + TlsSecret secondSecret = secondAgreement.calculateSecret(); + return crypto.createHybridSecret(firstSecret, secondSecret); + } +} diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java index 6265353d9e..0b9d6cda35 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java @@ -397,7 +397,9 @@ public boolean hasMacAlgorithm(int macAlgorithm) public boolean hasNamedGroup(int namedGroup) { - return NamedGroup.refersToASpecificGroup(namedGroup); + return NamedGroup.refersToASpecificCurve(namedGroup) + || NamedGroup.refersToASpecificFiniteField(namedGroup) + || NamedGroup.refersToASpecificKem(namedGroup); } public boolean hasRSAEncryption() @@ -480,6 +482,11 @@ public boolean hasSRPAuthentication() return true; } + public TlsSecret createHybridSecret(TlsSecret s1, TlsSecret s2) + { + return adoptLocalSecret(Arrays.concatenate(s1.extract(), s2.extract())); + } + public TlsSecret createSecret(byte[] data) { try diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index 2f3f3a1452..08ba8595e3 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -812,6 +812,11 @@ public boolean hasSRPAuthentication() return true; } + public TlsSecret createHybridSecret(TlsSecret s1, TlsSecret s2) + { + return adoptLocalSecret(Arrays.concatenate(s1.extract(), s2.extract())); + } + public TlsSecret createSecret(byte[] data) { try diff --git a/tls/src/test/java/org/bouncycastle/tls/test/AllTests.java b/tls/src/test/java/org/bouncycastle/tls/test/AllTests.java index be1480096c..224d241e9b 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/AllTests.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/AllTests.java @@ -22,6 +22,7 @@ public static Test suite() TestSuite suite = new TestSuite("TLS tests"); suite.addTestSuite(BasicTlsTest.class); + suite.addTestSuite(BcTlsProtocolHybridTest.class); suite.addTestSuite(BcTlsProtocolKemTest.class); suite.addTestSuite(ByteQueueInputStreamTest.class); suite.addTestSuite(DTLSAggregatedHandshakeRetransmissionTest.class); @@ -29,6 +30,7 @@ public static Test suite() suite.addTestSuite(DTLSProtocolTest.class); suite.addTestSuite(DTLSPSKProtocolTest.class); suite.addTestSuite(DTLSRawKeysProtocolTest.class); + suite.addTestSuite(JcaTlsProtocolHybridTest.class); suite.addTestSuite(JcaTlsProtocolKemTest.class); suite.addTestSuite(OCSPTest.class); suite.addTestSuite(PRFTest.class); diff --git a/tls/src/test/java/org/bouncycastle/tls/test/BcTlsProtocolHybridTest.java b/tls/src/test/java/org/bouncycastle/tls/test/BcTlsProtocolHybridTest.java new file mode 100644 index 0000000000..8499f7ad5e --- /dev/null +++ b/tls/src/test/java/org/bouncycastle/tls/test/BcTlsProtocolHybridTest.java @@ -0,0 +1,12 @@ +package org.bouncycastle.tls.test; + +import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto; + +public class BcTlsProtocolHybridTest + extends TlsProtocolHybridTest +{ + public BcTlsProtocolHybridTest() + { + super(new BcTlsCrypto()); + } +} diff --git a/tls/src/test/java/org/bouncycastle/tls/test/JcaTlsProtocolHybridTest.java b/tls/src/test/java/org/bouncycastle/tls/test/JcaTlsProtocolHybridTest.java new file mode 100644 index 0000000000..85c35d87be --- /dev/null +++ b/tls/src/test/java/org/bouncycastle/tls/test/JcaTlsProtocolHybridTest.java @@ -0,0 +1,15 @@ +package org.bouncycastle.tls.test; + +import java.security.SecureRandom; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.tls.crypto.impl.jcajce.JcaTlsCryptoProvider; + +public class JcaTlsProtocolHybridTest + extends TlsProtocolHybridTest +{ + public JcaTlsProtocolHybridTest() + { + super(new JcaTlsCryptoProvider().setProvider(new BouncyCastleProvider()).create(new SecureRandom())); + } +} diff --git a/tls/src/test/java/org/bouncycastle/tls/test/MockTlsHybridClient.java b/tls/src/test/java/org/bouncycastle/tls/test/MockTlsHybridClient.java new file mode 100644 index 0000000000..cf0d48519b --- /dev/null +++ b/tls/src/test/java/org/bouncycastle/tls/test/MockTlsHybridClient.java @@ -0,0 +1,244 @@ +package org.bouncycastle.tls.test; + +import java.io.IOException; +import java.io.PrintStream; +import java.util.Hashtable; +import java.util.Vector; + +import org.bouncycastle.asn1.x509.Certificate; +import org.bouncycastle.tls.AlertDescription; +import org.bouncycastle.tls.AlertLevel; +import org.bouncycastle.tls.CertificateRequest; +import org.bouncycastle.tls.ChannelBinding; +import org.bouncycastle.tls.ClientCertificateType; +import org.bouncycastle.tls.DefaultTlsClient; +import org.bouncycastle.tls.MaxFragmentLength; +import org.bouncycastle.tls.NamedGroup; +import org.bouncycastle.tls.NamedGroupRole; +import org.bouncycastle.tls.ProtocolName; +import org.bouncycastle.tls.ProtocolVersion; +import org.bouncycastle.tls.SignatureAlgorithm; +import org.bouncycastle.tls.TlsAuthentication; +import org.bouncycastle.tls.TlsCredentials; +import org.bouncycastle.tls.TlsExtensionsUtils; +import org.bouncycastle.tls.TlsFatalAlert; +import org.bouncycastle.tls.TlsServerCertificate; +import org.bouncycastle.tls.TlsSession; +import org.bouncycastle.tls.TlsUtils; +import org.bouncycastle.tls.crypto.TlsCertificate; +import org.bouncycastle.tls.crypto.TlsCrypto; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Integers; +import org.bouncycastle.util.encoders.Hex; + +class MockTlsHybridClient + extends DefaultTlsClient +{ + TlsSession session; + + int[] namedGroups = new int[] + { + NamedGroup.SecP256r1MLKEM768, + NamedGroup.X25519MLKEM768, + NamedGroup.SecP384r1MLKEM1024, + }; + + MockTlsHybridClient(TlsCrypto crypto, TlsSession session) + { + super(crypto); + + this.session = session; + } + + protected Vector getProtocolNames() + { + Vector protocolNames = new Vector(); + protocolNames.addElement(ProtocolName.HTTP_1_1); + protocolNames.addElement(ProtocolName.HTTP_2_TLS); + return protocolNames; + } + + void setNamedGroups(int[] namedGroups) + { + this.namedGroups = namedGroups; + } + + protected Vector getSupportedGroups(Vector namedGroupRoles) { + TlsCrypto crypto = getCrypto(); + Vector supportedGroups = new Vector(); + + if (namedGroupRoles.contains(Integers.valueOf(NamedGroupRole.kem))) + { + TlsUtils.addIfSupported(supportedGroups, crypto, this.namedGroups); + } + return supportedGroups; + } + + public TlsSession getSessionToResume() + { + return this.session; + } + + public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause) + { + PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out; + out.println("TLS hybrid client raised alert: " + AlertLevel.getText(alertLevel) + + ", " + AlertDescription.getText(alertDescription)); + if (message != null) + { + out.println("> " + message); + } + if (cause != null) + { + cause.printStackTrace(out); + } + } + + public void notifyAlertReceived(short alertLevel, short alertDescription) + { + PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out; + out.println("TLS hybrid client received alert: " + AlertLevel.getText(alertLevel) + + ", " + AlertDescription.getText(alertDescription)); + } + + public Hashtable getClientExtensions() throws IOException + { + if (context.getSecurityParametersHandshake().getClientRandom() == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + Hashtable clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(super.getClientExtensions()); + { + /* + * NOTE: If you are copying test code, do not blindly set these extensions in your own client. + */ + TlsExtensionsUtils.addMaxFragmentLengthExtension(clientExtensions, MaxFragmentLength.pow2_9); + TlsExtensionsUtils.addPaddingExtension(clientExtensions, context.getCrypto().getSecureRandom().nextInt(16)); + TlsExtensionsUtils.addTruncatedHMacExtension(clientExtensions); + } + return clientExtensions; + } + + public void notifyServerVersion(ProtocolVersion serverVersion) throws IOException + { + super.notifyServerVersion(serverVersion); + + System.out.println("TLS hybrid client negotiated " + serverVersion); + } + + public TlsAuthentication getAuthentication() throws IOException + { + return new TlsAuthentication() + { + public void notifyServerCertificate(TlsServerCertificate serverCertificate) throws IOException + { + TlsCertificate[] chain = serverCertificate.getCertificate().getCertificateList(); + + System.out.println("TLS hybrid client received server certificate chain of length " + chain.length); + for (int i = 0; i != chain.length; i++) + { + Certificate entry = Certificate.getInstance(chain[i].getEncoded()); + // TODO Create fingerprint based on certificate signature algorithm digest + System.out.println(" fingerprint:SHA-256 " + TlsTestUtils.fingerprint(entry) + " (" + + entry.getSubject() + ")"); + } + + boolean isEmpty = serverCertificate == null || serverCertificate.getCertificate() == null + || serverCertificate.getCertificate().isEmpty(); + + if (isEmpty) + { + throw new TlsFatalAlert(AlertDescription.bad_certificate); + } + + String[] trustedCertResources = new String[]{ "x509-server-dsa.pem", "x509-server-ecdh.pem", + "x509-server-ecdsa.pem", "x509-server-ed25519.pem", "x509-server-ed448.pem", + "x509-server-rsa_pss_256.pem", "x509-server-rsa_pss_384.pem", "x509-server-rsa_pss_512.pem", + "x509-server-rsa-enc.pem", "x509-server-rsa-sign.pem" }; + + TlsCertificate[] certPath = TlsTestUtils.getTrustedCertPath(context.getCrypto(), chain[0], + trustedCertResources); + + if (null == certPath) + { + throw new TlsFatalAlert(AlertDescription.bad_certificate); + } + + TlsUtils.checkPeerSigAlgs(context, certPath); + } + + public TlsCredentials getClientCredentials(CertificateRequest certificateRequest) throws IOException + { + short[] certificateTypes = certificateRequest.getCertificateTypes(); + if (certificateTypes == null || !Arrays.contains(certificateTypes, ClientCertificateType.rsa_sign)) + { + return null; + } + + return TlsTestUtils.loadSignerCredentials(context, certificateRequest.getSupportedSignatureAlgorithms(), + SignatureAlgorithm.rsa, "x509-client-rsa.pem", "x509-client-key-rsa.pem"); + } + }; + } + + public void notifyHandshakeComplete() throws IOException + { + super.notifyHandshakeComplete(); + + ProtocolName protocolName = context.getSecurityParametersConnection().getApplicationProtocol(); + if (protocolName != null) + { + System.out.println("Client ALPN: " + protocolName.getUtf8Decoding()); + } + + TlsSession newSession = context.getSession(); + if (newSession != null) + { + if (newSession.isResumable()) + { + byte[] newSessionID = newSession.getSessionID(); + String hex = hex(newSessionID); + + if (this.session != null && Arrays.areEqual(this.session.getSessionID(), newSessionID)) + { + System.out.println("Client resumed session: " + hex); + } + else + { + System.out.println("Client established session: " + hex); + } + + this.session = newSession; + } + + byte[] tlsServerEndPoint = context.exportChannelBinding(ChannelBinding.tls_server_end_point); + if (null != tlsServerEndPoint) + { + System.out.println("Client 'tls-server-end-point': " + hex(tlsServerEndPoint)); + } + + byte[] tlsUnique = context.exportChannelBinding(ChannelBinding.tls_unique); + System.out.println("Client 'tls-unique': " + hex(tlsUnique)); + + byte[] tlsExporter = context.exportChannelBinding(ChannelBinding.tls_exporter); + System.out.println("Client 'tls-exporter': " + hex(tlsExporter)); + } + } + + public void processServerExtensions(Hashtable serverExtensions) throws IOException + { + if (context.getSecurityParametersHandshake().getServerRandom() == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + super.processServerExtensions(serverExtensions); + } + + protected String hex(byte[] data) + { + return data == null ? "(null)" : Hex.toHexString(data); + } +} + diff --git a/tls/src/test/java/org/bouncycastle/tls/test/MockTlsHybridServer.java b/tls/src/test/java/org/bouncycastle/tls/test/MockTlsHybridServer.java new file mode 100644 index 0000000000..6ca3846ca0 --- /dev/null +++ b/tls/src/test/java/org/bouncycastle/tls/test/MockTlsHybridServer.java @@ -0,0 +1,245 @@ +package org.bouncycastle.tls.test; + +import java.io.IOException; +import java.io.PrintStream; +import java.util.Hashtable; +import java.util.Vector; + +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.Certificate; +import org.bouncycastle.tls.AlertDescription; +import org.bouncycastle.tls.AlertLevel; +import org.bouncycastle.tls.CertificateRequest; +import org.bouncycastle.tls.ChannelBinding; +import org.bouncycastle.tls.ClientCertificateType; +import org.bouncycastle.tls.DefaultTlsServer; +import org.bouncycastle.tls.NamedGroup; +import org.bouncycastle.tls.ProtocolName; +import org.bouncycastle.tls.ProtocolVersion; +import org.bouncycastle.tls.SignatureAlgorithm; +import org.bouncycastle.tls.TlsCredentialedDecryptor; +import org.bouncycastle.tls.TlsCredentialedSigner; +import org.bouncycastle.tls.TlsCredentials; +import org.bouncycastle.tls.TlsFatalAlert; +import org.bouncycastle.tls.TlsUtils; +import org.bouncycastle.tls.crypto.TlsCertificate; +import org.bouncycastle.tls.crypto.TlsCrypto; +import org.bouncycastle.util.encoders.Hex; + +class MockTlsHybridServer + extends DefaultTlsServer +{ + int[] namedGroups = new int[] + { + NamedGroup.SecP256r1MLKEM768, + NamedGroup.X25519MLKEM768, + NamedGroup.SecP384r1MLKEM1024, + NamedGroup.x25519, + }; + + MockTlsHybridServer(TlsCrypto crypto) + { + super(crypto); + } + + protected Vector getProtocolNames() + { + Vector protocolNames = new Vector(); + protocolNames.addElement(ProtocolName.HTTP_2_TLS); + protocolNames.addElement(ProtocolName.HTTP_1_1); + return protocolNames; + } + + void setNamedGroups(int[] namedGroups) + { + this.namedGroups = namedGroups; + } + + public int[] getSupportedGroups() throws IOException + { + return namedGroups; + } + + public TlsCredentials getCredentials() throws IOException + { + /* + * TODO[tls13] Should really be finding the first client-supported signature scheme that the + * server also supports and has credentials for. + */ + if (TlsUtils.isTLSv13(context)) + { + return getRSASignerCredentials(); + } + + return super.getCredentials(); + } + + public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause) + { + PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out; + out.println("TLS hybrid server raised alert: " + AlertLevel.getText(alertLevel) + + ", " + AlertDescription.getText(alertDescription)); + if (message != null) + { + out.println("> " + message); + } + if (cause != null) + { + cause.printStackTrace(out); + } + } + + public void notifyAlertReceived(short alertLevel, short alertDescription) + { + PrintStream out = (alertLevel == AlertLevel.fatal) ? System.err : System.out; + out.println("TLS hybrid server received alert: " + AlertLevel.getText(alertLevel) + + ", " + AlertDescription.getText(alertDescription)); + } + + public ProtocolVersion getServerVersion() throws IOException + { + ProtocolVersion serverVersion = super.getServerVersion(); + + System.out.println("TLS hybrid server negotiated " + serverVersion); + + return serverVersion; + } + + public CertificateRequest getCertificateRequest() throws IOException + { + Vector serverSigAlgs = null; + if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(context.getServerVersion())) + { + serverSigAlgs = TlsUtils.getDefaultSupportedSignatureAlgorithms(context); + } + + Vector certificateAuthorities = new Vector(); +// certificateAuthorities.addElement(TlsTestUtils.loadBcCertificateResource("x509-ca-dsa.pem").getSubject()); +// certificateAuthorities.addElement(TlsTestUtils.loadBcCertificateResource("x509-ca-ecdsa.pem").getSubject()); +// certificateAuthorities.addElement(TlsTestUtils.loadBcCertificateResource("x509-ca-rsa.pem").getSubject()); + + // All the CA certificates are currently configured with this subject + certificateAuthorities.addElement(new X500Name("CN=BouncyCastle TLS Test CA")); + + if (TlsUtils.isTLSv13(context)) + { + // TODO[tls13] Support for non-empty request context + byte[] certificateRequestContext = TlsUtils.EMPTY_BYTES; + + // TODO[tls13] Add TlsTestConfig.serverCertReqSigAlgsCert + Vector serverSigAlgsCert = null; + + return new CertificateRequest(certificateRequestContext, serverSigAlgs, serverSigAlgsCert, + certificateAuthorities); + } + else + { + short[] certificateTypes = new short[]{ ClientCertificateType.rsa_sign, + ClientCertificateType.dss_sign, ClientCertificateType.ecdsa_sign }; + + return new CertificateRequest(certificateTypes, serverSigAlgs, certificateAuthorities); + } + } + + public void notifyClientCertificate(org.bouncycastle.tls.Certificate clientCertificate) throws IOException + { + TlsCertificate[] chain = clientCertificate.getCertificateList(); + + System.out.println("TLS hybrid server received client certificate chain of length " + chain.length); + for (int i = 0; i != chain.length; i++) + { + Certificate entry = Certificate.getInstance(chain[i].getEncoded()); + // TODO Create fingerprint based on certificate signature algorithm digest + System.out.println(" fingerprint:SHA-256 " + TlsTestUtils.fingerprint(entry) + " (" + + entry.getSubject() + ")"); + } + + boolean isEmpty = (clientCertificate == null || clientCertificate.isEmpty()); + + if (isEmpty) + { + return; + } + + String[] trustedCertResources = new String[]{ "x509-client-dsa.pem", "x509-client-ecdh.pem", + "x509-client-ecdsa.pem", "x509-client-ed25519.pem", "x509-client-ed448.pem", "x509-client-rsa_pss_256.pem", + "x509-client-rsa_pss_384.pem", "x509-client-rsa_pss_512.pem", "x509-client-rsa.pem" }; + + TlsCertificate[] certPath = TlsTestUtils.getTrustedCertPath(context.getCrypto(), chain[0], + trustedCertResources); + + if (null == certPath) + { + throw new TlsFatalAlert(AlertDescription.bad_certificate); + } + + TlsUtils.checkPeerSigAlgs(context, certPath); + } + + public void notifyHandshakeComplete() throws IOException + { + super.notifyHandshakeComplete(); + + ProtocolName protocolName = context.getSecurityParametersConnection().getApplicationProtocol(); + if (protocolName != null) + { + System.out.println("Server ALPN: " + protocolName.getUtf8Decoding()); + } + + byte[] tlsServerEndPoint = context.exportChannelBinding(ChannelBinding.tls_server_end_point); + System.out.println("Server 'tls-server-end-point': " + hex(tlsServerEndPoint)); + + byte[] tlsUnique = context.exportChannelBinding(ChannelBinding.tls_unique); + System.out.println("Server 'tls-unique': " + hex(tlsUnique)); + + byte[] tlsExporter = context.exportChannelBinding(ChannelBinding.tls_exporter); + System.out.println("Server 'tls-exporter': " + hex(tlsExporter)); + } + + public void processClientExtensions(Hashtable clientExtensions) throws IOException + { + if (context.getSecurityParametersHandshake().getClientRandom() == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + super.processClientExtensions(clientExtensions); + } + + public Hashtable getServerExtensions() throws IOException + { + if (context.getSecurityParametersHandshake().getServerRandom() == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + return super.getServerExtensions(); + } + + public void getServerExtensionsForConnection(Hashtable serverExtensions) throws IOException + { + if (context.getSecurityParametersHandshake().getServerRandom() == null) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + super.getServerExtensionsForConnection(serverExtensions); + } + + protected TlsCredentialedDecryptor getRSAEncryptionCredentials() throws IOException + { + return TlsTestUtils.loadEncryptionCredentials(context, new String[]{ "x509-server-rsa-enc.pem", "x509-ca-rsa.pem" }, + "x509-server-key-rsa-enc.pem"); + } + + protected TlsCredentialedSigner getRSASignerCredentials() throws IOException + { + Vector clientSigAlgs = context.getSecurityParametersHandshake().getClientSigAlgs(); + return TlsTestUtils.loadSignerCredentialsServer(context, clientSigAlgs, SignatureAlgorithm.rsa); + } + + protected String hex(byte[] data) + { + return data == null ? "(null)" : Hex.toHexString(data); + } +} diff --git a/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolHybridTest.java b/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolHybridTest.java new file mode 100644 index 0000000000..7ebbe7af26 --- /dev/null +++ b/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolHybridTest.java @@ -0,0 +1,165 @@ +package org.bouncycastle.tls.test; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; + +import org.bouncycastle.tls.NamedGroup; +import org.bouncycastle.tls.TlsClientProtocol; +import org.bouncycastle.tls.TlsServerProtocol; +import org.bouncycastle.tls.crypto.TlsCrypto; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.io.Streams; + +import junit.framework.TestCase; + +public abstract class TlsProtocolHybridTest + extends TestCase +{ + protected final TlsCrypto crypto; + + protected TlsProtocolHybridTest(TlsCrypto crypto) + { + this.crypto = crypto; + } + + // mismatched hybrid groups w/o non-hybrids + public void testMismatchedGroups() throws Exception + { + PipedInputStream clientRead = TlsTestUtils.createPipedInputStream(); + PipedInputStream serverRead = TlsTestUtils.createPipedInputStream(); + PipedOutputStream clientWrite = new PipedOutputStream(serverRead); + PipedOutputStream serverWrite = new PipedOutputStream(clientRead); + + TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); + TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); + + ServerThread serverThread = new ServerThread(crypto, serverProtocol, new int[]{ NamedGroup.X25519MLKEM768 }, true); + try + { + serverThread.start(); + } + catch (Exception ignored) + { + } + + MockTlsHybridClient client = new MockTlsHybridClient(crypto, null); + client.setNamedGroups(new int[]{ NamedGroup.SecP256r1MLKEM768 }); + try + { + clientProtocol.connect(client); + fail(); + } + catch (Exception ignored) + { + } + + serverThread.join(); + } + + public void testSecP256r1MLKEM768() throws Exception + { + implTestClientServer(NamedGroup.SecP256r1MLKEM768); + } + + public void testSecP384r1MLKEM1024() throws Exception + { + implTestClientServer(NamedGroup.SecP384r1MLKEM1024); + } + + public void testX25519MLKEM768() throws Exception + { + implTestClientServer(NamedGroup.X25519MLKEM768); + } + + private void implTestClientServer(int hybridGroup) throws Exception + { + PipedInputStream clientRead = TlsTestUtils.createPipedInputStream(); + PipedInputStream serverRead = TlsTestUtils.createPipedInputStream(); + PipedOutputStream clientWrite = new PipedOutputStream(serverRead); + PipedOutputStream serverWrite = new PipedOutputStream(clientRead); + + TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); + TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); + + ServerThread serverThread = new ServerThread(crypto, serverProtocol, new int[]{ hybridGroup }, false); + serverThread.start(); + + MockTlsHybridClient client = new MockTlsHybridClient(crypto, null); + client.setNamedGroups(new int[]{ hybridGroup }); + + clientProtocol.connect(client); + + // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity + int length = 1000; + + byte[] data = new byte[length]; + client.getCrypto().getSecureRandom().nextBytes(data); + + OutputStream output = clientProtocol.getOutputStream(); + output.write(data); + + byte[] echo = new byte[data.length]; + int count = Streams.readFully(clientProtocol.getInputStream(), echo); + + assertEquals(count, data.length); + assertTrue(Arrays.areEqual(data, echo)); + + output.close(); + + serverThread.join(); + } + + static class ServerThread + extends Thread + { + private final TlsCrypto crypto; + private final TlsServerProtocol serverProtocol; + private final int[] namedGroups; + private boolean shouldFail = false; + + ServerThread(TlsCrypto crypto, TlsServerProtocol serverProtocol, int[] namedGroups, boolean shouldFail) + { + this.crypto = crypto; + this.serverProtocol = serverProtocol; + this.namedGroups = namedGroups; + this.shouldFail = shouldFail; + } + + public void run() + { + try + { + MockTlsHybridServer server = new MockTlsHybridServer(crypto); + if (namedGroups != null) + { + server.setNamedGroups(namedGroups); + } + + try + { + serverProtocol.accept(server); + if (shouldFail) + { + fail(); + } + } + catch (IOException ignored) + { + if (!shouldFail) + { + fail(); + } + } + + Streams.pipeAll(serverProtocol.getInputStream(), serverProtocol.getOutputStream()); + serverProtocol.close(); + } + catch (Exception e) + { +// throw new RuntimeException(e); + } + } + } +} From 87f6317909903dc6b59d90486263ac64c8302878 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 27 May 2025 10:29:02 +0700 Subject: [PATCH 378/890] TLS: negotiate group before looking for early share --- .../bouncycastle/tls/TlsServerProtocol.java | 20 +++++------ .../java/org/bouncycastle/tls/TlsUtils.java | 33 ++++++++----------- 2 files changed, 22 insertions(+), 31 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java index 5b2d3ae82b..fe9f08bfb4 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java @@ -10,9 +10,6 @@ import org.bouncycastle.tls.crypto.TlsAgreement; import org.bouncycastle.tls.crypto.TlsCrypto; -import org.bouncycastle.tls.crypto.TlsDHConfig; -import org.bouncycastle.tls.crypto.TlsECConfig; -import org.bouncycastle.tls.crypto.TlsKemConfig; import org.bouncycastle.tls.crypto.TlsSecret; import org.bouncycastle.util.Arrays; @@ -219,7 +216,7 @@ protected ServerHello generate13ServerHello(ClientHello clientHello, HandshakeMe } this.retryCookie = null; - clientShare = TlsUtils.selectKeyShare(clientShares, retryGroup); + clientShare = TlsUtils.getRetryKeyShare(clientShares, retryGroup); if (null == clientShare) { throw new TlsFatalAlert(AlertDescription.illegal_parameter); @@ -297,17 +294,18 @@ protected ServerHello generate13ServerHello(ClientHello clientHello, HandshakeMe int[] clientSupportedGroups = securityParameters.getClientSupportedGroups(); int[] serverSupportedGroups = securityParameters.getServerSupportedGroups(); - clientShare = TlsUtils.selectKeyShare(crypto, serverVersion, clientShares, clientSupportedGroups, + int selectedGroup = TlsUtils.selectKeyShareGroup(crypto, serverVersion, clientSupportedGroups, serverSupportedGroups); + if (selectedGroup < 0) + { + throw new TlsFatalAlert(AlertDescription.handshake_failure); + } + + clientShare = TlsUtils.findEarlyKeyShare(clientShares, selectedGroup); if (null == clientShare) { - this.retryGroup = TlsUtils.selectKeyShareGroup(crypto, serverVersion, clientSupportedGroups, - serverSupportedGroups); - if (retryGroup < 0) - { - throw new TlsFatalAlert(AlertDescription.handshake_failure); - } + this.retryGroup = selectedGroup; this.retryCookie = tlsServerContext.getNonceGenerator().generateNonce(16); diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java b/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java index eb9415cee1..4ce45f5fea 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java @@ -5449,37 +5449,30 @@ else if (NamedGroup.refersToASpecificKem(keyShareGroup)) return null; } - static KeyShareEntry selectKeyShare(Vector clientShares, int keyShareGroup) + static KeyShareEntry findEarlyKeyShare(Vector clientShares, int keyShareGroup) { - if (null != clientShares && 1 == clientShares.size()) + if (null != clientShares) { - KeyShareEntry clientShare = (KeyShareEntry)clientShares.elementAt(0); - if (null != clientShare && clientShare.getNamedGroup() == keyShareGroup) + for (int i = 0; i < clientShares.size(); ++i) { - return clientShare; + KeyShareEntry clientShare = (KeyShareEntry)clientShares.elementAt(i); + if (null != clientShare && clientShare.getNamedGroup() == keyShareGroup) + { + return clientShare; + } } } return null; } - static KeyShareEntry selectKeyShare(TlsCrypto crypto, ProtocolVersion negotiatedVersion, Vector clientShares, - int[] clientSupportedGroups, int[] serverSupportedGroups) + static KeyShareEntry getRetryKeyShare(Vector clientShares, int keyShareGroup) { - if (null != clientShares && !isNullOrEmpty(clientSupportedGroups) && !isNullOrEmpty(serverSupportedGroups)) + if (null != clientShares && 1 == clientShares.size()) { - for (int i = 0; i < clientShares.size(); ++i) + KeyShareEntry clientShare = (KeyShareEntry)clientShares.elementAt(0); + if (null != clientShare && clientShare.getNamedGroup() == keyShareGroup) { - KeyShareEntry clientShare = (KeyShareEntry)clientShares.elementAt(i); - - int group = clientShare.getNamedGroup(); - - if (NamedGroup.canBeNegotiated(group, negotiatedVersion) && - Arrays.contains(serverSupportedGroups, group) && - Arrays.contains(clientSupportedGroups, group) && - supportsKeyShareGroup(crypto, group)) - { - return clientShare; - } + return clientShare; } } return null; From dcdbca512459b7ee57cce254f97c1536b974aa1c Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 27 May 2025 11:04:27 +0700 Subject: [PATCH 379/890] TLS: 1.3 server may send supported_groups --- .../bouncycastle/tls/TlsExtensionsUtils.java | 15 ++++++++ .../bouncycastle/tls/TlsServerProtocol.java | 38 +++++++++++-------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsExtensionsUtils.java b/tls/src/main/java/org/bouncycastle/tls/TlsExtensionsUtils.java index a44904459e..072f721ca7 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsExtensionsUtils.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsExtensionsUtils.java @@ -250,6 +250,11 @@ public static void addSupportedGroupsExtension(Hashtable extensions, Vector name extensions.put(EXT_supported_groups, createSupportedGroupsExtension(namedGroups)); } + public static void addSupportedGroupsExtension(Hashtable extensions, int[] namedGroups) throws IOException + { + extensions.put(EXT_supported_groups, createSupportedGroupsExtension(namedGroups)); + } + public static void addSupportedPointFormatsExtension(Hashtable extensions, short[] ecPointFormats) throws IOException { @@ -934,6 +939,16 @@ public static byte[] createSupportedGroupsExtension(Vector namedGroups) throws I return TlsUtils.encodeUint16ArrayWithUint16Length(values); } + public static byte[] createSupportedGroupsExtension(int[] namedGroups) throws IOException + { + if (TlsUtils.isNullOrEmpty(namedGroups)) + { + throw new TlsFatalAlert(AlertDescription.internal_error); + } + + return TlsUtils.encodeUint16ArrayWithUint16Length(namedGroups); + } + public static byte[] createSupportedPointFormatsExtension(short[] ecPointFormats) throws IOException { if (ecPointFormats == null || !Arrays.contains(ecPointFormats, ECPointFormat.uncompressed)) diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java index fe9f08bfb4..8049eacc42 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java @@ -311,22 +311,6 @@ protected ServerHello generate13ServerHello(ClientHello clientHello, HandshakeMe return generate13HelloRetryRequest(clientHello); } - - if (clientShare.getNamedGroup() != serverSupportedGroups[0]) - { - /* - * TODO[tls13] RFC 8446 4.2.7. As of TLS 1.3, servers are permitted to send the - * "supported_groups" extension to the client. Clients MUST NOT act upon any - * information found in "supported_groups" prior to successful completion of the - * handshake but MAY use the information learned from a successfully completed - * handshake to change what groups they use in their "key_share" extension in - * subsequent connections. If the server has a group it prefers to the ones in the - * "key_share" extension but is still willing to accept the ClientHello, it SHOULD - * send "supported_groups" to update the client's view of its preferences; this - * extension SHOULD contain all groups the server supports, regardless of whether - * they are currently supported by the client. - */ - } } @@ -408,6 +392,28 @@ protected ServerHello generate13ServerHello(ClientHello clientHello, HandshakeMe TlsExtensionsUtils.addKeyShareServerHello(serverHelloExtensions, serverShare); sharedSecret = agreement.calculateSecret(); + + /* + * RFC 8446 4.2.7. As of TLS 1.3, servers are permitted to send the "supported_groups" extension to + * the client. Clients MUST NOT act upon any information found in "supported_groups" prior to + * successful completion of the handshake but MAY use the information learned from a successfully + * completed handshake to change what groups they use in their "key_share" extension in subsequent + * connections. If the server has a group it prefers to the ones in the "key_share" extension but is + * still willing to accept the ClientHello, it SHOULD send "supported_groups" to update the client's + * view of its preferences; this extension SHOULD contain all groups the server supports, regardless + * of whether they are currently supported by the client. + */ + if (!afterHelloRetryRequest) + { + int[] serverSupportedGroups = securityParameters.getServerSupportedGroups(); + + if (!TlsUtils.isNullOrEmpty(serverSupportedGroups) && + namedGroup != serverSupportedGroups[0] && + !serverEncryptedExtensions.containsKey(TlsExtensionsUtils.EXT_supported_groups)) + { + TlsExtensionsUtils.addSupportedGroupsExtension(serverEncryptedExtensions, serverSupportedGroups); + } + } } TlsUtils.establish13PhaseSecrets(tlsServerContext, pskEarlySecret, sharedSecret); From 4fc041ff4731c439440ef0a50d71fea4150be0b5 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 27 May 2025 13:54:58 +0700 Subject: [PATCH 380/890] Add ASN1TaggedObject getOptional methods --- .../bouncycastle/asn1/ASN1TaggedObject.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java b/core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java index efd7fdac28..d81c92bf8a 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java @@ -75,6 +75,41 @@ public static ASN1TaggedObject getInstance(ASN1TaggedObject taggedObject, int ta return ASN1Util.getExplicitBaseTagged(checkInstance(taggedObject, declaredExplicit), tagClass, tagNo); } + public static ASN1TaggedObject getOptional(ASN1Object element) + { + if (element == null) + { + throw new NullPointerException("'element' cannot be null"); + } + + if (element instanceof ASN1TaggedObject) + { + return (ASN1TaggedObject)element; + } + + return null; + } + + public static ASN1TaggedObject getOptional(ASN1Object element, int tagClass) + { + ASN1TaggedObject taggedObject = getOptional(element); + if (taggedObject != null && taggedObject.hasTagClass(tagClass)) + { + return taggedObject; + } + return null; + } + + public static ASN1TaggedObject getOptional(ASN1Object element, int tagClass, int tagNo) + { + ASN1TaggedObject taggedObject = getOptional(element); + if (taggedObject != null && taggedObject.hasTag(tagClass, tagNo)) + { + return taggedObject; + } + return null; + } + private static ASN1TaggedObject checkInstance(Object obj) { if (obj == null) From 14d0a87168f89078bd10b0d053cfeca6f32a0974 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 27 May 2025 14:07:08 +0700 Subject: [PATCH 381/890] Refactor the sending of server supported groups --- .../bouncycastle/tls/TlsServerProtocol.java | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java index 8049eacc42..d50ba3aeff 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java @@ -319,6 +319,25 @@ protected ServerHello generate13ServerHello(ClientHello clientHello, HandshakeMe tlsServer.getServerExtensionsForConnection(serverEncryptedExtensions); + /* + * RFC 8446 4.2.7. As of TLS 1.3, servers are permitted to send the "supported_groups" extension to + * the client. [..] If the server has a group it prefers to the ones in the "key_share" extension + * but is still willing to accept the ClientHello, it SHOULD send "supported_groups" to update the + * client's view of its preferences; this extension SHOULD contain all groups the server supports, + * regardless of whether they are currently supported by the client. + */ + if (!afterHelloRetryRequest) + { + int[] serverSupportedGroups = securityParameters.getServerSupportedGroups(); + + if (!TlsUtils.isNullOrEmpty(serverSupportedGroups) && + clientShare.getNamedGroup() != serverSupportedGroups[0] && + !serverEncryptedExtensions.containsKey(TlsExtensionsUtils.EXT_supported_groups)) + { + TlsExtensionsUtils.addSupportedGroupsExtension(serverEncryptedExtensions, serverSupportedGroups); + } + } + ProtocolVersion serverLegacyVersion = ProtocolVersion.TLSv12; TlsExtensionsUtils.addSupportedVersionsExtensionServer(serverHelloExtensions, serverVersion); @@ -392,28 +411,6 @@ protected ServerHello generate13ServerHello(ClientHello clientHello, HandshakeMe TlsExtensionsUtils.addKeyShareServerHello(serverHelloExtensions, serverShare); sharedSecret = agreement.calculateSecret(); - - /* - * RFC 8446 4.2.7. As of TLS 1.3, servers are permitted to send the "supported_groups" extension to - * the client. Clients MUST NOT act upon any information found in "supported_groups" prior to - * successful completion of the handshake but MAY use the information learned from a successfully - * completed handshake to change what groups they use in their "key_share" extension in subsequent - * connections. If the server has a group it prefers to the ones in the "key_share" extension but is - * still willing to accept the ClientHello, it SHOULD send "supported_groups" to update the client's - * view of its preferences; this extension SHOULD contain all groups the server supports, regardless - * of whether they are currently supported by the client. - */ - if (!afterHelloRetryRequest) - { - int[] serverSupportedGroups = securityParameters.getServerSupportedGroups(); - - if (!TlsUtils.isNullOrEmpty(serverSupportedGroups) && - namedGroup != serverSupportedGroups[0] && - !serverEncryptedExtensions.containsKey(TlsExtensionsUtils.EXT_supported_groups)) - { - TlsExtensionsUtils.addSupportedGroupsExtension(serverEncryptedExtensions, serverSupportedGroups); - } - } } TlsUtils.establish13PhaseSecrets(tlsServerContext, pskEarlySecret, sharedSecret); From ec1a2cde61f2aefb10e2e901e8eddaa1f5711668 Mon Sep 17 00:00:00 2001 From: Gefei Li Date: Tue, 27 May 2025 07:14:29 +0000 Subject: [PATCH 382/890] AbstractOpenPGPDocumentSignatureGenerator.getPreferredHashAlgorithm --- ant/jdk15+.xml | 1 + .../org/bouncycastle/util/Exceptions.java | 22 +++++++++ ...ractOpenPGPDocumentSignatureGenerator.java | 45 +++++++++++++------ ...OpenPGPDetachedSignatureProcessorTest.java | 9 +++- .../api/test/OpenPGPMessageProcessorTest.java | 12 +++-- 5 files changed, 71 insertions(+), 18 deletions(-) create mode 100644 core/src/main/jdk1.5/org/bouncycastle/util/Exceptions.java diff --git a/ant/jdk15+.xml b/ant/jdk15+.xml index 35a23c11d4..00be081a3d 100644 --- a/ant/jdk15+.xml +++ b/ant/jdk15+.xml @@ -72,6 +72,7 @@ + diff --git a/core/src/main/jdk1.5/org/bouncycastle/util/Exceptions.java b/core/src/main/jdk1.5/org/bouncycastle/util/Exceptions.java new file mode 100644 index 0000000000..af4f07eac4 --- /dev/null +++ b/core/src/main/jdk1.5/org/bouncycastle/util/Exceptions.java @@ -0,0 +1,22 @@ +package org.bouncycastle.util; + +import java.io.IOException; + +public class Exceptions +{ + public static IllegalArgumentException illegalArgumentException(String message, Throwable cause) + { + return new IllegalArgumentException(message, cause); + } + + public static IllegalStateException illegalStateException(String message, Throwable cause) + { + return new IllegalStateException(message, cause); + } + + public static IOException ioException(String message, Throwable cause) + { + return new IOException(message + "-" + cause.getMessage()); + } + +} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java index aeed3dd545..4111697472 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/AbstractOpenPGPDocumentSignatureGenerator.java @@ -1,11 +1,9 @@ package org.bouncycastle.openpgp.api; import java.util.ArrayList; -import java.util.Arrays; import java.util.Date; import java.util.Iterator; import java.util.List; -import java.util.function.IntPredicate; import org.bouncycastle.bcpg.sig.PreferredAlgorithms; import org.bouncycastle.openpgp.PGPException; @@ -263,22 +261,41 @@ private int getPreferredHashAlgorithm(OpenPGPCertificate.OpenPGPComponentKey key PreferredAlgorithms hashPreferences = key.getHashAlgorithmPreferences(); if (hashPreferences != null) { - int[] pref = Arrays.stream(hashPreferences.getPreferences()) - .filter(new IntPredicate() - { // Replace lambda with anonymous class for IntPredicate - @Override - public boolean test(int it) - { - return policy.isAcceptableDocumentSignatureHashAlgorithm(it, new Date()); - } - }) - .toArray(); - if (pref.length != 0) + int[] prefs = hashPreferences.getPreferences(); + List acceptablePrefs = new ArrayList(); + for (int i = 0; i < prefs.length; i++) { - return pref[0]; + int algo = prefs[i]; + if (policy.isAcceptableDocumentSignatureHashAlgorithm(algo, new Date())) + { + acceptablePrefs.add(algo); + } + } + if (!acceptablePrefs.isEmpty()) + { + return acceptablePrefs.get(0); } } return policy.getDefaultDocumentSignatureHashAlgorithm(); +// PreferredAlgorithms hashPreferences = key.getHashAlgorithmPreferences(); +// if (hashPreferences != null) +// { +// int[] pref = Arrays.stream(hashPreferences.getPreferences()) +// .filter(new IntPredicate() +// { // Replace lambda with anonymous class for IntPredicate +// @Override +// public boolean test(int it) +// { +// return policy.isAcceptableDocumentSignatureHashAlgorithm(it, new Date()); +// } +// }) +// .toArray(); +// if (pref.length != 0) +// { +// return pref[0]; +// } +// } +// return policy.getDefaultDocumentSignatureHashAlgorithm(); } /** diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java index a7f76eb646..1d86ee630d 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPDetachedSignatureProcessorTest.java @@ -35,6 +35,9 @@ public String getName() protected void performTestWith(OpenPGPApi api) throws PGPException, IOException { + String javaVersion = System.getProperty("java.version"); + boolean oldJDK = javaVersion.startsWith("1.5") || javaVersion.startsWith("1.6"); + createVerifyV4Signature(api); createVerifyV6Signature(api); @@ -42,7 +45,11 @@ protected void performTestWith(OpenPGPApi api) keyPassphrasesArePairedUpProperly_passphraseAddedFirst(api); missingPassphraseThrows(api); - wrongPassphraseThrows(api); + + if (!oldJDK) + { + wrongPassphraseThrows(api); + } withoutSigningSubkeyFails(api); nonSigningSubkeyFails(api); diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java index 02ba0238db..159960393a 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/OpenPGPMessageProcessorTest.java @@ -48,6 +48,9 @@ public String getName() protected void performTestWith(OpenPGPApi api) throws PGPException, IOException { + String javaVersion = System.getProperty("java.version"); + boolean oldJDK = javaVersion.startsWith("1.5") || javaVersion.startsWith("1.6"); + testVerificationOfSEIPD1MessageWithTamperedCiphertext(api); roundtripUnarmoredPlaintextMessage(api); @@ -64,9 +67,12 @@ protected void performTestWith(OpenPGPApi api) encryptWithV4V6KeyDecryptWithV4(api); encryptWithV4V6KeyDecryptWithV6(api); - encryptDecryptWithLockedKey(api); - encryptDecryptWithMissingKey(api); - + if (!oldJDK) + { + encryptDecryptWithLockedKey(api); + encryptDecryptWithMissingKey(api); + } + inlineSignWithV4KeyAlice(api); inlineSignWithV4KeyBob(api); inlineSignWithV6Key(api); From 6aa5b5759d8c4162c1b7521261a46388aa036bd7 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 27 May 2025 16:09:24 +0700 Subject: [PATCH 383/890] BCJSSE: Support hybrid ECDHE-MLKEM groups --- .../jsse/provider/NamedGroupInfo.java | 112 ++++++++++++++---- 1 file changed, 87 insertions(+), 25 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/NamedGroupInfo.java b/tls/src/main/java/org/bouncycastle/jsse/provider/NamedGroupInfo.java index f2af7facd9..b5a85639a1 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/provider/NamedGroupInfo.java +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/NamedGroupInfo.java @@ -1,6 +1,7 @@ package org.bouncycastle.jsse.provider; import java.security.AlgorithmParameters; +import java.security.GeneralSecurityException; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; @@ -81,13 +82,19 @@ private enum All OQS_mlkem1024(NamedGroup.OQS_mlkem1024, "ML-KEM"), MLKEM512(NamedGroup.MLKEM512, "ML-KEM"), MLKEM768(NamedGroup.MLKEM768, "ML-KEM"), - MLKEM1024(NamedGroup.MLKEM1024, "ML-KEM"); + MLKEM1024(NamedGroup.MLKEM1024, "ML-KEM"), + + SecP256r1MLKEM768(NamedGroup.SecP256r1MLKEM768, "EC", "ML-KEM"), + X25519MLKEM768(NamedGroup.X25519MLKEM768, "ML-KEM", "XDH"), + SecP384r1MLKEM1024(NamedGroup.SecP384r1MLKEM1024, "EC", "ML-KEM"); private final int namedGroup; private final String name; private final String text; - private final String jcaAlgorithm; - private final String jcaGroup; + private final String jcaAlgorithm1; + private final String jcaAlgorithm2; + private final String jcaGroup1; + private final String jcaGroup2; private final boolean char2; private final boolean supportedPost13; private final boolean supportedPre13; @@ -96,17 +103,45 @@ private enum All private All(int namedGroup, String jcaAlgorithm) { + if (NamedGroup.refersToASpecificHybrid(namedGroup)) + { + throw new IllegalArgumentException("Non-hybrid constructor only"); + } + this.namedGroup = namedGroup; this.name = NamedGroup.getName(namedGroup); this.text = NamedGroup.getText(namedGroup); - this.jcaAlgorithm = jcaAlgorithm; - this.jcaGroup = NamedGroup.getStandardName(namedGroup); + this.jcaAlgorithm1 = jcaAlgorithm; + this.jcaAlgorithm2 = null; + this.jcaGroup1 = NamedGroup.getStandardName(namedGroup); + this.jcaGroup2 = null; this.supportedPost13 = NamedGroup.canBeNegotiated(namedGroup, ProtocolVersion.TLSv13); this.supportedPre13 = NamedGroup.canBeNegotiated(namedGroup, ProtocolVersion.TLSv12); this.char2 = NamedGroup.isChar2Curve(namedGroup); this.bitsECDH = NamedGroup.getCurveBits(namedGroup); this.bitsFFDHE = NamedGroup.getFiniteFieldBits(namedGroup); } + + private All(int namedGroup, String jcaAlgorithm1, String jcaAlgorithm2) + { + if (!NamedGroup.refersToASpecificHybrid(namedGroup)) + { + throw new IllegalArgumentException("Hybrid constructor only"); + } + + this.namedGroup = namedGroup; + this.name = NamedGroup.getName(namedGroup); + this.text = NamedGroup.getText(namedGroup); + this.jcaAlgorithm1 = jcaAlgorithm1; + this.jcaAlgorithm2 = jcaAlgorithm2; + this.jcaGroup1 = NamedGroup.getStandardName(NamedGroup.getHybridFirst(namedGroup)); + this.jcaGroup2 = NamedGroup.getStandardName(NamedGroup.getHybridSecond(namedGroup)); + this.supportedPost13 = NamedGroup.canBeNegotiated(namedGroup, ProtocolVersion.TLSv13); + this.supportedPre13 = NamedGroup.canBeNegotiated(namedGroup, ProtocolVersion.TLSv12); + this.char2 = false; + this.bitsECDH = -1; + this.bitsFFDHE = -1; + } } private static final int[] CANDIDATES_DEFAULT = { @@ -439,23 +474,37 @@ private static void addNamedGroup(boolean isFipsContext, JcaTlsCrypto crypto, bo boolean disable = (disableChar2 && all.char2) || (disableFFDHE && all.bitsFFDHE > 0); - boolean enabled = !disable && (null != all.jcaGroup) && crypto.hasNamedGroup(namedGroup); + boolean enabled = !disable && (null != all.jcaGroup1) && (null == all.jcaAlgorithm2 || null != all.jcaGroup2) + && TlsUtils.isSupportedNamedGroup(crypto, namedGroup); + + AlgorithmParameters algorithmParameters1 = null; + AlgorithmParameters algorithmParameters2 = null; - AlgorithmParameters algorithmParameters = null; if (enabled) { - // TODO[jsse] Consider also fetching 'jcaAlgorithm' + // TODO[jsse] Consider also fetching 'jcaAlgorithm1', 'jcaAlgorithm2' + try { - algorithmParameters = crypto.getNamedGroupAlgorithmParameters(namedGroup); + if (NamedGroup.refersToASpecificHybrid(namedGroup)) + { + algorithmParameters1 = getAlgorithmParameters(crypto, NamedGroup.getHybridFirst(namedGroup)); + algorithmParameters2 = getAlgorithmParameters(crypto, NamedGroup.getHybridSecond(namedGroup)); + } + else + { + algorithmParameters1 = getAlgorithmParameters(crypto, namedGroup); + } } catch (Exception e) { enabled = false; + algorithmParameters1 = null; + algorithmParameters2 = null; } } - NamedGroupInfo namedGroupInfo = new NamedGroupInfo(all, algorithmParameters, enabled); + NamedGroupInfo namedGroupInfo = new NamedGroupInfo(all, algorithmParameters1, algorithmParameters2, enabled); if (null != ng.put(namedGroup, namedGroupInfo)) { @@ -531,6 +580,12 @@ private static Map createIndex(boolean isFipsContext, J return ng; } + private static AlgorithmParameters getAlgorithmParameters(JcaTlsCrypto crypto, int namedGroup) + throws GeneralSecurityException + { + return crypto.getNamedGroupAlgorithmParameters(namedGroup); + } + private static int getNamedGroupByName(String name) { for (All all : All.values()) @@ -589,13 +644,16 @@ private static boolean hasAnyECDSA(Map local) } private final All all; - private final AlgorithmParameters algorithmParameters; + private final AlgorithmParameters algorithmParameters1; + private final AlgorithmParameters algorithmParameters2; private final boolean enabled; - NamedGroupInfo(All all, AlgorithmParameters algorithmParameters, boolean enabled) + NamedGroupInfo(All all, AlgorithmParameters algorithmParameters1, AlgorithmParameters algorithmParameters2, + boolean enabled) { this.all = all; - this.algorithmParameters = algorithmParameters; + this.algorithmParameters1 = algorithmParameters1; + this.algorithmParameters2 = algorithmParameters2; this.enabled = enabled; } @@ -609,16 +667,6 @@ int getBitsFFDHE() return all.bitsFFDHE; } - String getJcaAlgorithm() - { - return all.jcaAlgorithm; - } - - String getJcaGroup() - { - return all.jcaGroup; - } - int getNamedGroup() { return all.namedGroup; @@ -656,7 +704,21 @@ private boolean isPermittedBy(BCAlgorithmConstraints algorithmConstraints) { Set primitives = JsseUtils.KEY_AGREEMENT_CRYPTO_PRIMITIVES_BC; - return algorithmConstraints.permits(primitives, getJcaGroup(), null) - && algorithmConstraints.permits(primitives, getJcaAlgorithm(), algorithmParameters); + if (!algorithmConstraints.permits(primitives, all.jcaGroup1, null) || + !algorithmConstraints.permits(primitives, all.jcaAlgorithm1, algorithmParameters1)) + { + return false; + } + + if (all.jcaAlgorithm2 != null) + { + if (!algorithmConstraints.permits(primitives, all.jcaGroup2, null) || + !algorithmConstraints.permits(primitives, all.jcaAlgorithm2, algorithmParameters2)) + { + return false; + } + } + + return true; } } From 62bd4d43068b8d1088853dd036c04e8c743630f3 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 27 May 2025 17:23:23 +0700 Subject: [PATCH 384/890] useCipherSuitesOrder should default to false --- .../java/org/bouncycastle/jsse/provider/ProvSSLParameters.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvSSLParameters.java b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvSSLParameters.java index d1a191b418..af2a4b2f94 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvSSLParameters.java +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvSSLParameters.java @@ -39,7 +39,7 @@ private static List copyList(Collection list) private BCAlgorithmConstraints algorithmConstraints = ProvAlgorithmConstraints.DEFAULT; private List sniServerNames; private List sniMatchers; - private boolean useCipherSuitesOrder = true; + private boolean useCipherSuitesOrder = false; private boolean enableRetransmissions = true; private int maximumPacketSize = 0; private String[] applicationProtocols = TlsUtils.EMPTY_STRINGS; From da49073f942e79941807156e1afead152ddc48ba Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 27 May 2025 19:07:05 +0700 Subject: [PATCH 385/890] BCJSSE: optionally prefer server's supported_groups order - see BCSSLParameters.useNamedGroupsOrder property --- .../org/bouncycastle/jsse/BCSSLParameters.java | 11 +++++++++++ .../jsse/provider/ProvSSLParameters.java | 12 ++++++++++++ .../jsse/provider/ProvTlsServer.java | 6 ++++++ .../bouncycastle/tls/AbstractTlsServer.java | 5 +++++ .../java/org/bouncycastle/tls/TlsServer.java | 2 ++ .../bouncycastle/tls/TlsServerProtocol.java | 3 ++- .../java/org/bouncycastle/tls/TlsUtils.java | 17 ++++++++++------- .../jsse/provider/SSLParametersUtil.java | 18 ++++++++++++++++++ .../jsse/provider/SSLParametersUtil.java | 18 ++++++++++++++++++ 9 files changed, 84 insertions(+), 8 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/jsse/BCSSLParameters.java b/tls/src/main/java/org/bouncycastle/jsse/BCSSLParameters.java index 0936596a90..11992286e0 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/BCSSLParameters.java +++ b/tls/src/main/java/org/bouncycastle/jsse/BCSSLParameters.java @@ -37,6 +37,7 @@ private static List copyList(Collection list) private List serverNames; private List sniMatchers; private boolean useCipherSuitesOrder; + private boolean useNamedGroupsOrder; private boolean enableRetransmissions = true; private int maximumPacketSize = 0; private String[] applicationProtocols = TlsUtils.EMPTY_STRINGS; @@ -189,6 +190,16 @@ public void setUseCipherSuitesOrder(boolean useCipherSuitesOrder) this.useCipherSuitesOrder = useCipherSuitesOrder; } + public boolean getUseNamedGroupsOrder() + { + return useNamedGroupsOrder; + } + + public void setUseNamedGroupsOrder(boolean useNamedGroupsOrder) + { + this.useNamedGroupsOrder = useNamedGroupsOrder; + } + public boolean getEnableRetransmissions() { return enableRetransmissions; diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvSSLParameters.java b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvSSLParameters.java index af2a4b2f94..7e8fb1621b 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvSSLParameters.java +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvSSLParameters.java @@ -40,6 +40,7 @@ private static List copyList(Collection list) private List sniServerNames; private List sniMatchers; private boolean useCipherSuitesOrder = false; + private boolean useNamedGroupsOrder = false; private boolean enableRetransmissions = true; private int maximumPacketSize = 0; private String[] applicationProtocols = TlsUtils.EMPTY_STRINGS; @@ -69,6 +70,7 @@ ProvSSLParameters copy() p.sniServerNames = sniServerNames; p.sniMatchers = sniMatchers; p.useCipherSuitesOrder = useCipherSuitesOrder; + p.useNamedGroupsOrder = useNamedGroupsOrder; p.enableRetransmissions = enableRetransmissions; p.maximumPacketSize = maximumPacketSize; p.applicationProtocols = applicationProtocols; @@ -214,6 +216,16 @@ public void setUseCipherSuitesOrder(boolean useCipherSuitesOrder) this.useCipherSuitesOrder = useCipherSuitesOrder; } + public boolean getUseNamedGroupsOrder() + { + return useNamedGroupsOrder; + } + + public void setUseNamedGroupsOrder(boolean useNamedGroupsOrder) + { + this.useNamedGroupsOrder = useNamedGroupsOrder; + } + public boolean getEnableRetransmissions() { return enableRetransmissions; diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsServer.java b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsServer.java index 44d991ad5a..c43076da5d 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsServer.java +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/ProvTlsServer.java @@ -313,6 +313,12 @@ protected boolean preferLocalCipherSuites() return sslParameters.getUseCipherSuitesOrder(); } + @Override + public boolean preferLocalSupportedGroups() + { + return sslParameters.getUseNamedGroupsOrder(); + } + @Override protected boolean selectCipherSuite(int cipherSuite) throws IOException { diff --git a/tls/src/main/java/org/bouncycastle/tls/AbstractTlsServer.java b/tls/src/main/java/org/bouncycastle/tls/AbstractTlsServer.java index 1214988396..e04ffa5179 100644 --- a/tls/src/main/java/org/bouncycastle/tls/AbstractTlsServer.java +++ b/tls/src/main/java/org/bouncycastle/tls/AbstractTlsServer.java @@ -155,6 +155,11 @@ protected boolean preferLocalCipherSuites() return false; } + public boolean preferLocalSupportedGroups() + { + return false; + } + protected boolean selectCipherSuite(int cipherSuite) throws IOException { this.selectedCipherSuite = cipherSuite; diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsServer.java b/tls/src/main/java/org/bouncycastle/tls/TlsServer.java index ee78dfbde5..8f092eeaf2 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsServer.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsServer.java @@ -13,6 +13,8 @@ public interface TlsServer extends TlsPeer { + boolean preferLocalSupportedGroups(); + void init(TlsServerContext context); /** diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java index d50ba3aeff..3c94732a8c 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java @@ -293,9 +293,10 @@ protected ServerHello generate13ServerHello(ClientHello clientHello, HandshakeMe int[] clientSupportedGroups = securityParameters.getClientSupportedGroups(); int[] serverSupportedGroups = securityParameters.getServerSupportedGroups(); + boolean useServerOrder = tlsServer.preferLocalSupportedGroups(); int selectedGroup = TlsUtils.selectKeyShareGroup(crypto, serverVersion, clientSupportedGroups, - serverSupportedGroups); + serverSupportedGroups, useServerOrder); if (selectedGroup < 0) { throw new TlsFatalAlert(AlertDescription.handshake_failure); diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java b/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java index 4ce45f5fea..18567b730e 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java @@ -5479,19 +5479,22 @@ static KeyShareEntry getRetryKeyShare(Vector clientShares, int keyShareGroup) } static int selectKeyShareGroup(TlsCrypto crypto, ProtocolVersion negotiatedVersion, - int[] clientSupportedGroups, int[] serverSupportedGroups) + int[] clientSupportedGroups, int[] serverSupportedGroups, boolean useServerOrder) { if (!isNullOrEmpty(clientSupportedGroups) && !isNullOrEmpty(serverSupportedGroups)) { - for (int i = 0; i < clientSupportedGroups.length; ++i) + int[] ordered = useServerOrder ? serverSupportedGroups : clientSupportedGroups; + int[] unordered = useServerOrder ? clientSupportedGroups : serverSupportedGroups; + + for (int i = 0; i < ordered.length; ++i) { - int group = clientSupportedGroups[i]; + int candidate = ordered[i]; - if (NamedGroup.canBeNegotiated(group, negotiatedVersion) && - Arrays.contains(serverSupportedGroups, group) && - supportsKeyShareGroup(crypto, group)) + if (Arrays.contains(unordered, candidate) && + NamedGroup.canBeNegotiated(candidate, negotiatedVersion) && + supportsKeyShareGroup(crypto, candidate)) { - return group; + return candidate; } } } diff --git a/tls/src/main/jdk1.5/org/bouncycastle/jsse/provider/SSLParametersUtil.java b/tls/src/main/jdk1.5/org/bouncycastle/jsse/provider/SSLParametersUtil.java index 237ca8cf24..39d5abfa89 100644 --- a/tls/src/main/jdk1.5/org/bouncycastle/jsse/provider/SSLParametersUtil.java +++ b/tls/src/main/jdk1.5/org/bouncycastle/jsse/provider/SSLParametersUtil.java @@ -79,6 +79,7 @@ static BCSSLParameters getParameters(ProvSSLParameters prov) ssl.setServerNames(prov.getServerNames()); ssl.setSNIMatchers(prov.getSNIMatchers()); ssl.setUseCipherSuitesOrder(prov.getUseCipherSuitesOrder()); + ssl.setUseNamedGroupsOrder(prov.getUseNamedGroupsOrder()); ssl.setApplicationProtocols(prov.getApplicationProtocols()); ssl.setEnableRetransmissions(prov.getEnableRetransmissions()); ssl.setMaximumPacketSize(prov.getMaximumPacketSize()); @@ -180,6 +181,11 @@ static SSLParameters getSSLParameters(ProvSSLParameters prov) // Unsupported as of JDK 21 +// if (null != setUseNamedGroupsOrder) +// { +// set(ssl, setUseNamedGroupsOrder, prov.getUseNamedGroupsOrder()); +// } + // if (null != setSignatureSchemesCert) // { // set(ssl, setSignatureSchemesCert, prov.getSignatureSchemesCert()); @@ -286,6 +292,11 @@ static BCSSLParameters importSSLParameters(SSLParameters ssl) // Unsupported as of JDK 21 +// if (null != getUseNamedGroupsOrder) +// { +// bc.setUseNamedGroupsOrder((Boolean)get(ssl, getUseNamedGroupsOrder)); +// } + // if (null != getSignatureSchemesCert) // { // bc.setSignatureSchemesCert((String[])get(ssl, getSignatureSchemesCert)); @@ -344,6 +355,8 @@ static void setParameters(ProvSSLParameters prov, BCSSLParameters ssl) prov.setUseCipherSuitesOrder(ssl.getUseCipherSuitesOrder()); + prov.setUseNamedGroupsOrder(ssl.getUseNamedGroupsOrder()); + String[] applicationProtocols = ssl.getApplicationProtocols(); if (null != applicationProtocols) { @@ -469,6 +482,11 @@ static void setSSLParameters(ProvSSLParameters prov, SSLParameters ssl) // Unsupported as of JDK 21 +// if (null != getUseNamedGroupsOrder) +// { +// prov.setUseNamedGroupsOrder((Boolean)get(ssl, getUseNamedGroupsOrder)); +// } + // if (null != getSignatureSchemesCert) // { // prov.setSignatureSchemesCert((String[])get(ssl, getSignatureSchemesCert)); diff --git a/tls/src/main/jdk1.9/org/bouncycastle/jsse/provider/SSLParametersUtil.java b/tls/src/main/jdk1.9/org/bouncycastle/jsse/provider/SSLParametersUtil.java index 02ae707506..e6da475798 100644 --- a/tls/src/main/jdk1.9/org/bouncycastle/jsse/provider/SSLParametersUtil.java +++ b/tls/src/main/jdk1.9/org/bouncycastle/jsse/provider/SSLParametersUtil.java @@ -50,6 +50,7 @@ static BCSSLParameters getParameters(ProvSSLParameters prov) ssl.setServerNames(prov.getServerNames()); ssl.setSNIMatchers(prov.getSNIMatchers()); ssl.setUseCipherSuitesOrder(prov.getUseCipherSuitesOrder()); + ssl.setUseNamedGroupsOrder(prov.getUseNamedGroupsOrder()); ssl.setApplicationProtocols(prov.getApplicationProtocols()); ssl.setEnableRetransmissions(prov.getEnableRetransmissions()); ssl.setMaximumPacketSize(prov.getMaximumPacketSize()); @@ -132,6 +133,11 @@ static SSLParameters getSSLParameters(ProvSSLParameters prov) // Unsupported as of JDK 21 +// if (null != setUseNamedGroupsOrder) +// { +// set(ssl, setUseNamedGroupsOrder, prov.getUseNamedGroupsOrder()); +// } + // if (null != setSignatureSchemesCert) // { // set(ssl, setSignatureSchemesCert, prov.getSignatureSchemesCert()); @@ -224,6 +230,11 @@ static BCSSLParameters importSSLParameters(SSLParameters ssl) // Unsupported as of JDK 21 +// if (null != getUseNamedGroupsOrder) +// { +// bc.setUseNamedGroupsOrder((Boolean)get(ssl, getUseNamedGroupsOrder)); +// } + // if (null != getSignatureSchemesCert) // { // bc.setSignatureSchemesCert((String[])get(ssl, getSignatureSchemesCert)); @@ -282,6 +293,8 @@ static void setParameters(ProvSSLParameters prov, BCSSLParameters ssl) prov.setUseCipherSuitesOrder(ssl.getUseCipherSuitesOrder()); + prov.setUseNamedGroupsOrder(ssl.getUseNamedGroupsOrder()); + String[] applicationProtocols = ssl.getApplicationProtocols(); if (null != applicationProtocols) { @@ -393,6 +406,11 @@ static void setSSLParameters(ProvSSLParameters prov, SSLParameters ssl) // Unsupported as of JDK 21 +// if (null != getUseNamedGroupsOrder) +// { +// prov.setUseNamedGroupsOrder((Boolean)get(ssl, getUseNamedGroupsOrder)); +// } + // if (null != getSignatureSchemesCert) // { // prov.setSignatureSchemesCert((String[])get(ssl, getSignatureSchemesCert)); From b81d8e33bbd30747f543624a60564384baba4207 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 29 May 2025 00:44:43 +0700 Subject: [PATCH 386/890] Include X25519MLKEM768 in default supported groups --- .../main/java/org/bouncycastle/jsse/provider/NamedGroupInfo.java | 1 + 1 file changed, 1 insertion(+) diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/NamedGroupInfo.java b/tls/src/main/java/org/bouncycastle/jsse/provider/NamedGroupInfo.java index b5a85639a1..f17581677a 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/provider/NamedGroupInfo.java +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/NamedGroupInfo.java @@ -156,6 +156,7 @@ private All(int namedGroup, String jcaAlgorithm1, String jcaAlgorithm2) NamedGroup.ffdhe2048, NamedGroup.ffdhe3072, NamedGroup.ffdhe4096, + NamedGroup.X25519MLKEM768, }; static class PerConnection From c7682d150193d2c0b0e13dcbfa86461cc5334318 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 29 May 2025 01:00:00 +0700 Subject: [PATCH 387/890] TLS: Alternate ML-KEM key pair generation to allow use of our SecureRandom --- .../crypto/impl/jcajce/JceTlsMLKemDomain.java | 27 ++++++++++--------- .../tls/crypto/impl/jcajce/KemUtil.java | 6 +++-- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java index 48259ce14c..f85528070b 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JceTlsMLKemDomain.java @@ -1,6 +1,7 @@ package org.bouncycastle.tls.crypto.impl.jcajce; import java.io.IOException; +import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.security.PrivateKey; @@ -11,6 +12,7 @@ import org.bouncycastle.jcajce.SecretKeyWithEncapsulation; import org.bouncycastle.jcajce.spec.KEMExtractSpec; import org.bouncycastle.jcajce.spec.KEMGenerateSpec; +import org.bouncycastle.jcajce.spec.MLKEMParameterSpec; import org.bouncycastle.tls.NamedGroup; import org.bouncycastle.tls.crypto.TlsAgreement; import org.bouncycastle.tls.crypto.TlsKemConfig; @@ -43,7 +45,7 @@ public JceTlsSecret decapsulate(PrivateKey privateKey, byte[] ciphertext) { try { - KeyGenerator keyGenerator = KemUtil.getKeyGenerator(crypto, kemName); + KeyGenerator keyGenerator = crypto.getHelper().createKeyGenerator(kemName); keyGenerator.init(new KEMExtractSpec.Builder(privateKey, ciphertext, "DEF", 256).withNoKdf().build()); SecretKeyWithEncapsulation secEnc = (SecretKeyWithEncapsulation)keyGenerator.generateKey(); return adoptLocalSecret(secEnc.getEncoded()); @@ -64,7 +66,7 @@ public SecretKeyWithEncapsulation encapsulate(PublicKey publicKey) { try { - KeyGenerator keyGenerator = KemUtil.getKeyGenerator(crypto, kemName); + KeyGenerator keyGenerator = crypto.getHelper().createKeyGenerator(kemName); keyGenerator.init(new KEMGenerateSpec.Builder(publicKey, "DEF", 256).withNoKdf().build()); return (SecretKeyWithEncapsulation)keyGenerator.generateKey(); } @@ -82,20 +84,21 @@ public byte[] encodePublicKey(PublicKey publicKey) public KeyPair generateKeyPair() { - // TODO How to pass only the SecureRandom? -// try -// { + try + { + // TODO How to pass only the SecureRandom to initialize if we use the full name in the getInstance? // KeyPairGenerator keyPairGenerator = KemUtil.getKeyPairGenerator(crypto, kemName); // keyPairGenerator.initialize((AlgorithmParameterSpec)null, crypto.getSecureRandom()); // return keyPairGenerator.generateKeyPair(); -// } -// catch (GeneralSecurityException e) -// { -// throw Exceptions.illegalStateException("unable to create key pair: " + e.getMessage(), e); -// } - KeyPairGenerator keyPairGenerator = KemUtil.getKeyPairGenerator(crypto, kemName); - return keyPairGenerator.generateKeyPair(); + KeyPairGenerator keyPairGenerator = crypto.getHelper().createKeyPairGenerator("ML-KEM"); + keyPairGenerator.initialize(MLKEMParameterSpec.fromName(kemName), crypto.getSecureRandom()); + return keyPairGenerator.generateKeyPair(); + } + catch (GeneralSecurityException e) + { + throw Exceptions.illegalStateException("unable to create key pair: " + e.getMessage(), e); + } } public boolean isServer() diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java index 9f747143d9..d543f96b74 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/KemUtil.java @@ -116,7 +116,9 @@ static KeyPairGenerator getKeyPairGenerator(JcaTlsCrypto crypto, String kemName) { try { - return crypto.getHelper().createKeyPairGenerator(kemName); + KeyPairGenerator keyPairGenerator = crypto.getHelper().createKeyPairGenerator("ML-KEM"); + keyPairGenerator.initialize(MLKEMParameterSpec.fromName(kemName), crypto.getSecureRandom()); + return keyPairGenerator; } catch (AssertionError e) { @@ -159,6 +161,6 @@ private static ASN1ObjectIdentifier getAlgorithmOID(String kemName) return NISTObjectIdentifiers.id_alg_ml_kem_1024; } - throw Exceptions.illegalArgumentException("unknown kem name " + kemName, null); + throw new IllegalArgumentException("unknown kem name " + kemName); } } From 31398ba690f42de7d276f3cb22cb1adbf5360e00 Mon Sep 17 00:00:00 2001 From: David Hook Date: Thu, 29 May 2025 11:00:27 +1000 Subject: [PATCH 388/890] update against main --- .../tls/crypto/impl/jcajce/JcaTlsCrypto.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java b/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java index 9ee1e18b05..255f9743c0 100644 --- a/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java +++ b/tls/src/main/jdk1.4/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsCrypto.java @@ -465,8 +465,12 @@ else if (NamedGroup.refersToASpecificFiniteField(namedGroup)) } else if (NamedGroup.refersToASpecificKem(namedGroup)) { - //Note: There is no AlgorithmParametersSpi for ML-KEM - return KemUtil.getAlgorithmParameters(this, NamedGroup.getKemName(namedGroup)); + /* + * TODO Return AlgorithmParameters to check against disabled algorithms? + * + * NOTE: See what the JDK/SunJSSE implementation does. + */ + return null; } throw new IllegalArgumentException("NamedGroup not supported: " + NamedGroup.getText(namedGroup)); @@ -813,6 +817,11 @@ public boolean hasSRPAuthentication() return true; } + public TlsSecret createHybridSecret(TlsSecret s1, TlsSecret s2) + { + return adoptLocalSecret(Arrays.concatenate(s1.extract(), s2.extract())); + } + public TlsSecret createSecret(byte[] data) { try From 346e6d8a43ddc1c8991d493c5f669f3c3c885a75 Mon Sep 17 00:00:00 2001 From: David Hook Date: Thu, 29 May 2025 11:00:42 +1000 Subject: [PATCH 389/890] checkstyle pruning --- build.gradle | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index aa7eea0bdb..8aa5746fb8 100644 --- a/build.gradle +++ b/build.gradle @@ -254,9 +254,16 @@ subprojects { nohttp { source.exclude '**/*.asc' + source.exclude '**/*.class' + source.exclude '**/*.crt' + source.exclude '**/*.crl' + source.exclude '**/*.eml' + source.exclude '**/*.gpg' + source.exclude '**/*.jar' + source.exclude '**/*.message' source.exclude '**/*.pem' + source.exclude '**/*.*pub' source.exclude '**/*.rsp' - source.exclude '**/*.jar' } jacocoTestReport { From 8d2b53ab0efa4890ef781a40aa50431911f0d11a Mon Sep 17 00:00:00 2001 From: gefeili Date: Thu, 29 May 2025 17:05:08 +0930 Subject: [PATCH 390/890] Update XWing according to draft-connolly-cfrg-xwing-kem/07/ --- .../pqc/crypto/xwing/XWingKEMExtractor.java | 54 ++-- .../pqc/crypto/xwing/XWingKEMGenerator.java | 85 ++++-- .../crypto/xwing/XWingKeyPairGenerator.java | 59 +++- .../xwing/XWingPrivateKeyParameters.java | 57 +++- .../pqc/crypto/test/XWingTest.java | 281 +++++++++++++++--- 5 files changed, 405 insertions(+), 131 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingKEMExtractor.java b/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingKEMExtractor.java index cbc815f22d..657df1a12b 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingKEMExtractor.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingKEMExtractor.java @@ -1,62 +1,54 @@ package org.bouncycastle.pqc.crypto.xwing; import org.bouncycastle.crypto.EncapsulatedSecretExtractor; -import org.bouncycastle.crypto.agreement.X25519Agreement; -import org.bouncycastle.crypto.digests.SHA3Digest; -import org.bouncycastle.crypto.params.X25519PrivateKeyParameters; import org.bouncycastle.crypto.params.X25519PublicKeyParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMExtractor; -import org.bouncycastle.pqc.crypto.mlkem.MLKEMPrivateKeyParameters; import org.bouncycastle.util.Arrays; -import org.bouncycastle.util.Strings; +/** + * Implements the decapsulation process of the X-Wing hybrid Key Encapsulation Mechanism (KEM). + *

    + * This class allows the recipient to derive the shared secret from a given ciphertext using their private key, + * as defined in the X-Wing KEM specification. + *

    + * + * @see X-Wing KEM Draft + */ public class XWingKEMExtractor implements EncapsulatedSecretExtractor { + private static final int MLKEM_CIPHERTEXT_SIZE = 1088; private final XWingPrivateKeyParameters key; - private final MLKEMExtractor kemExtractor; + private final MLKEMExtractor mlkemExtractor; public XWingKEMExtractor(XWingPrivateKeyParameters privParams) { this.key = privParams; - this.kemExtractor = new MLKEMExtractor((MLKEMPrivateKeyParameters)key.getKyberPrivateKey()); + this.mlkemExtractor = new MLKEMExtractor(key.getKyberPrivateKey()); } @Override public byte[] extractSecret(byte[] encapsulation) { - // Decryption - byte[] kybSecret = kemExtractor.extractSecret(Arrays.copyOfRange(encapsulation, 0, encapsulation.length - X25519PublicKeyParameters.KEY_SIZE)); - X25519Agreement xdhAgree = new X25519Agreement(); + // 1. Split ciphertext into ML-KEM and X25519 parts + byte[] ctM = Arrays.copyOfRange(encapsulation, 0, MLKEM_CIPHERTEXT_SIZE); + byte[] ctX = Arrays.copyOfRange(encapsulation, MLKEM_CIPHERTEXT_SIZE, encapsulation.length); - byte[] k = new byte[kybSecret.length + xdhAgree.getAgreementSize()]; + // 2. Compute X25519 shared secret + byte[] ssX = XWingKEMGenerator.computeSSX(new X25519PublicKeyParameters(ctX, 0), key.getXDHPrivateKey()); - System.arraycopy(kybSecret, 0, k, 0, kybSecret.length); + // 3. Compute combiner: SHA3-256(ssM || ssX || ctX || pkX || XWING_LABEL) + byte[] kemSecret = XWingKEMGenerator.computeSharedSecret(key.getXDHPublicKey().getEncoded(), + mlkemExtractor.extractSecret(ctM), ctX, ssX); - Arrays.clear(kybSecret); - - xdhAgree.init(key.getXDHPrivateKey()); - - X25519PublicKeyParameters ephXdhPub = new X25519PublicKeyParameters(Arrays.copyOfRange(encapsulation, encapsulation.length - X25519PublicKeyParameters.KEY_SIZE, encapsulation.length)); - - xdhAgree.calculateAgreement(ephXdhPub, k, kybSecret.length); - - SHA3Digest sha3 = new SHA3Digest(256); - - sha3.update(Strings.toByteArray("\\.//^\\"), 0, 6); - sha3.update(k, 0, k.length); - sha3.update(ephXdhPub.getEncoded(), 0, X25519PublicKeyParameters.KEY_SIZE); - sha3.update(((X25519PrivateKeyParameters)key.getXDHPrivateKey()).generatePublicKey().getEncoded(), 0, X25519PublicKeyParameters.KEY_SIZE); - - byte[] kemSecret = new byte[32]; - - sha3.doFinal(kemSecret, 0); + // 4. Cleanup intermediate values + Arrays.clear(ssX); return kemSecret; } public int getEncapsulationLength() { - return kemExtractor.getEncapsulationLength() + X25519PublicKeyParameters.KEY_SIZE; + return mlkemExtractor.getEncapsulationLength() + X25519PublicKeyParameters.KEY_SIZE; } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingKEMGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingKEMGenerator.java index fe32917fb7..807ae7bfd3 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingKEMGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingKEMGenerator.java @@ -10,61 +10,88 @@ import org.bouncycastle.crypto.generators.X25519KeyPairGenerator; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.X25519KeyGenerationParameters; +import org.bouncycastle.crypto.params.X25519PrivateKeyParameters; import org.bouncycastle.crypto.params.X25519PublicKeyParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMGenerator; +import org.bouncycastle.pqc.crypto.mlkem.MLKEMPublicKeyParameters; import org.bouncycastle.pqc.crypto.util.SecretWithEncapsulationImpl; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; +/** + * Implements the encapsulation process of the X-Wing hybrid Key Encapsulation Mechanism (KEM). + *

    + * X-Wing is a general-purpose hybrid post-quantum/traditional KEM that combines X25519 and ML-KEM-768, + * as specified in the IETF draft: draft-connolly-cfrg-xwing-kem-07. + *

    + *

    + * This class facilitates the generation of ciphertexts and shared secrets using a recipient's public key. + *

    + * + * @see X-Wing KEM Draft + */ public class XWingKEMGenerator implements EncapsulatedSecretGenerator { - // the source of randomness - private final SecureRandom sr; + private final SecureRandom random; + private static final byte[] XWING_LABEL = Strings.toByteArray("\\.//^\\"); public XWingKEMGenerator(SecureRandom random) { - this.sr = random; + this.random = random; } public SecretWithEncapsulation generateEncapsulated(AsymmetricKeyParameter recipientKey) { XWingPublicKeyParameters key = (XWingPublicKeyParameters)recipientKey; + MLKEMPublicKeyParameters kyberPub = key.getKyberPublicKey(); + X25519PublicKeyParameters xdhPub = key.getXDHPublicKey(); + byte[] xdhPubBytes = xdhPub.getEncoded(); - MLKEMGenerator kybKem = new MLKEMGenerator(sr); - - SecretWithEncapsulation kybSecWithEnc = kybKem.generateEncapsulated(key.getKyberPublicKey()); - X25519Agreement xdhAgree = new X25519Agreement(); - byte[] kybSecret = kybSecWithEnc.getSecret(); - byte[] k = new byte[kybSecret.length + xdhAgree.getAgreementSize()]; - - System.arraycopy(kybSecret, 0, k, 0, kybSecret.length); - - Arrays.clear(kybSecret); + // 1. Perform ML-KEM encapsulation + MLKEMGenerator mlkemGen = new MLKEMGenerator(random); + SecretWithEncapsulation mlkemSec = mlkemGen.generateEncapsulated(kyberPub); + byte[] ctM = mlkemSec.getEncapsulation(); + // 2. Generate ephemeral X25519 key pair X25519KeyPairGenerator xdhGen = new X25519KeyPairGenerator(); + xdhGen.init(new X25519KeyGenerationParameters(random)); + AsymmetricCipherKeyPair ephXdhKp = xdhGen.generateKeyPair(); + byte[] ctX = ((X25519PublicKeyParameters)ephXdhKp.getPublic()).getEncoded(); - xdhGen.init(new X25519KeyGenerationParameters(sr)); + // 3. Perform X25519 agreement + byte[] ssX = computeSSX(xdhPub, (X25519PrivateKeyParameters)ephXdhKp.getPrivate()); - AsymmetricCipherKeyPair ephXdh = xdhGen.generateKeyPair(); + // 4. Compute shared secret: SHA3-256(ssM || ssX || ctX || pkX || label) + byte[] ss = computeSharedSecret(xdhPubBytes, mlkemSec.getSecret(), ctX, ssX); - xdhAgree.init(ephXdh.getPrivate()); + // 5. Cleanup intermediate values + Arrays.clear(ssX); - xdhAgree.calculateAgreement(key.getXDHPublicKey(), k, kybSecret.length); + // 6. Return shared secret and encapsulation (ctM || ctX) + return new SecretWithEncapsulationImpl(ss, Arrays.concatenate(ctM, ctX)); + } - X25519PublicKeyParameters ephXdhPub = (X25519PublicKeyParameters)ephXdh.getPublic(); + static byte[] computeSSX(X25519PublicKeyParameters xdhPub, X25519PrivateKeyParameters ephXdhPriv) + { + X25519Agreement xdhAgreement = new X25519Agreement(); + xdhAgreement.init(ephXdhPriv); + byte[] ssX = new byte[xdhAgreement.getAgreementSize()]; + xdhAgreement.calculateAgreement(xdhPub, ssX, 0); + return ssX; + } + static byte[] computeSharedSecret(byte[] xdhPubBytes, byte[] ssM, byte[] ctX, byte[] ssX) + { SHA3Digest sha3 = new SHA3Digest(256); - - sha3.update(Strings.toByteArray("\\.//^\\"), 0, 6); - sha3.update(k, 0, k.length); - sha3.update(ephXdhPub.getEncoded(), 0, X25519PublicKeyParameters.KEY_SIZE); - sha3.update(((X25519PublicKeyParameters)key.getXDHPublicKey()).getEncoded(), 0, X25519PublicKeyParameters.KEY_SIZE); - - byte[] kemSecret = new byte[32]; - - sha3.doFinal(kemSecret, 0); - - return new SecretWithEncapsulationImpl(kemSecret, Arrays.concatenate(kybSecWithEnc.getEncapsulation(), ephXdhPub.getEncoded())); + sha3.update(ssM, 0, ssM.length); + sha3.update(ssX, 0, ssX.length); + sha3.update(ctX, 0, ctX.length); + sha3.update(xdhPubBytes, 0, xdhPubBytes.length); + sha3.update(XWING_LABEL, 0, XWING_LABEL.length); + + byte[] ss = new byte[32]; + sha3.doFinal(ss, 0); + return ss; } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingKeyPairGenerator.java b/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingKeyPairGenerator.java index f7691d0125..694ac2dcfd 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingKeyPairGenerator.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingKeyPairGenerator.java @@ -5,12 +5,28 @@ import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.AsymmetricCipherKeyPairGenerator; import org.bouncycastle.crypto.KeyGenerationParameters; +import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.crypto.generators.X25519KeyPairGenerator; import org.bouncycastle.crypto.params.X25519KeyGenerationParameters; +import org.bouncycastle.crypto.params.X25519PrivateKeyParameters; +import org.bouncycastle.crypto.params.X25519PublicKeyParameters; +import org.bouncycastle.crypto.prng.FixedSecureRandom; import org.bouncycastle.pqc.crypto.mlkem.MLKEMKeyGenerationParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMKeyPairGenerator; import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters; +import org.bouncycastle.pqc.crypto.mlkem.MLKEMPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.mlkem.MLKEMPublicKeyParameters; +import org.bouncycastle.util.Arrays; +/** + * Generates key pairs compatible with the X-Wing hybrid Key Encapsulation Mechanism (KEM). + *

    + * This class produces key pairs that include both X25519 and ML-KEM-768 components, + * suitable for use in the X-Wing KEM as specified in the IETF draft. + *

    + * + * @see X-Wing KEM Draft + */ public class XWingKeyPairGenerator implements AsymmetricCipherKeyPairGenerator { @@ -22,22 +38,39 @@ private void initialize( this.random = param.getRandom(); } - private AsymmetricCipherKeyPair genKeyPair() + static AsymmetricCipherKeyPair genKeyPair(byte[] seed) { - MLKEMKeyPairGenerator kyberKeyGen = new MLKEMKeyPairGenerator(); + // Step 2: Expand seed to 96 bytes using SHAKE256 + SHAKEDigest shake = new SHAKEDigest(256); + shake.update(seed, 0, seed.length); + byte[] expanded = new byte[96]; + shake.doOutput(expanded, 0, expanded.length); - kyberKeyGen.init(new MLKEMKeyGenerationParameters(random, MLKEMParameters.ml_kem_768)); + // Step 3: Split expanded bytes + byte[] mlkemSeed = Arrays.copyOfRange(expanded, 0, 64); + byte[] skX = Arrays.copyOfRange(expanded, 64, 96); - X25519KeyPairGenerator x25519KeyGen = new X25519KeyPairGenerator(); + // Step 4a: Generate ML-KEM key pair deterministically + SecureRandom mlkemRandom = new FixedSecureRandom(mlkemSeed); + MLKEMKeyPairGenerator mlkemKeyGen = new MLKEMKeyPairGenerator(); + mlkemKeyGen.init(new MLKEMKeyGenerationParameters(mlkemRandom, MLKEMParameters.ml_kem_768)); + AsymmetricCipherKeyPair mlkemKp = mlkemKeyGen.generateKeyPair(); + MLKEMPublicKeyParameters mlkemPub = (MLKEMPublicKeyParameters)mlkemKp.getPublic(); + MLKEMPrivateKeyParameters mlkemPriv = (MLKEMPrivateKeyParameters)mlkemKp.getPrivate(); - x25519KeyGen.init(new X25519KeyGenerationParameters(random)); - - AsymmetricCipherKeyPair kybKp = kyberKeyGen.generateKeyPair(); - AsymmetricCipherKeyPair xdhKp = x25519KeyGen.generateKeyPair(); + // Step 4b: Generate X25519 key pair deterministically + SecureRandom xdhRandom = new FixedSecureRandom(skX); + X25519KeyPairGenerator xdhKeyGen = new X25519KeyPairGenerator(); + xdhKeyGen.init(new X25519KeyGenerationParameters(xdhRandom)); + AsymmetricCipherKeyPair xdhKp = xdhKeyGen.generateKeyPair(); + X25519PublicKeyParameters xdhPub = (X25519PublicKeyParameters)xdhKp.getPublic(); + X25519PrivateKeyParameters xdhPriv = (X25519PrivateKeyParameters)xdhKp.getPrivate(); + // Step 5: Create X-Wing keys return new AsymmetricCipherKeyPair( - new XWingPublicKeyParameters(kybKp.getPublic(), xdhKp.getPublic()), - new XWingPrivateKeyParameters(kybKp.getPrivate(), xdhKp.getPrivate())); + new XWingPublicKeyParameters(mlkemPub, xdhPub), + new XWingPrivateKeyParameters(seed, mlkemPriv, xdhPriv, mlkemPub, xdhPub) + ); } public void init(KeyGenerationParameters param) @@ -47,7 +80,9 @@ public void init(KeyGenerationParameters param) public AsymmetricCipherKeyPair generateKeyPair() { - return genKeyPair(); + // Step 1: Generate 32-byte random seed + byte[] seed = new byte[32]; + random.nextBytes(seed); + return genKeyPair(seed); } - } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingPrivateKeyParameters.java b/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingPrivateKeyParameters.java index 5558f76120..a26020e197 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingPrivateKeyParameters.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/xwing/XWingPrivateKeyParameters.java @@ -1,45 +1,72 @@ package org.bouncycastle.pqc.crypto.xwing; -import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.X25519PrivateKeyParameters; -import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters; +import org.bouncycastle.crypto.params.X25519PublicKeyParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.mlkem.MLKEMPublicKeyParameters; import org.bouncycastle.util.Arrays; public class XWingPrivateKeyParameters extends XWingKeyParameters { - private final MLKEMPrivateKeyParameters kybPriv; - private final X25519PrivateKeyParameters xdhPriv; + private final transient byte[] seed; + private final transient MLKEMPrivateKeyParameters kyberPrivateKey; + private final transient X25519PrivateKeyParameters xdhPrivateKey; + private final transient MLKEMPublicKeyParameters kyberPublicKey; + private final transient X25519PublicKeyParameters xdhPublicKey; - XWingPrivateKeyParameters(AsymmetricKeyParameter kybPriv, AsymmetricKeyParameter xdhPriv) + public XWingPrivateKeyParameters(byte[] seed, + MLKEMPrivateKeyParameters kyberPrivateKey, + X25519PrivateKeyParameters xdhPrivateKey, + MLKEMPublicKeyParameters kyberPublicKey, + X25519PublicKeyParameters xdhPublicKey) { super(true); - - this.kybPriv = (MLKEMPrivateKeyParameters)kybPriv; - this.xdhPriv = (X25519PrivateKeyParameters)xdhPriv; + this.seed = Arrays.clone(seed); + this.kyberPrivateKey = kyberPrivateKey; + this.xdhPrivateKey = xdhPrivateKey; + this.kyberPublicKey = kyberPublicKey; + this.xdhPublicKey = xdhPublicKey; } - public XWingPrivateKeyParameters(byte[] encoding) + public XWingPrivateKeyParameters(byte[] seed) { - super(false); + super(true); + XWingPrivateKeyParameters key = (XWingPrivateKeyParameters)XWingKeyPairGenerator.genKeyPair(seed).getPrivate(); + this.seed = key.seed; + this.kyberPrivateKey = key.kyberPrivateKey; + this.xdhPrivateKey = key.xdhPrivateKey; + this.kyberPublicKey = key.kyberPublicKey; + this.xdhPublicKey = key.xdhPublicKey; + } - this.kybPriv = new MLKEMPrivateKeyParameters(MLKEMParameters.ml_kem_768, Arrays.copyOfRange(encoding, 0, encoding.length - X25519PrivateKeyParameters.KEY_SIZE)); - this.xdhPriv = new X25519PrivateKeyParameters(encoding, encoding.length - X25519PrivateKeyParameters.KEY_SIZE); + public byte[] getSeed() + { + return Arrays.clone(seed); } MLKEMPrivateKeyParameters getKyberPrivateKey() { - return kybPriv; + return kyberPrivateKey; + } + + MLKEMPublicKeyParameters getKyberPublicKey() + { + return kyberPublicKey; } X25519PrivateKeyParameters getXDHPrivateKey() { - return xdhPriv; + return xdhPrivateKey; + } + + X25519PublicKeyParameters getXDHPublicKey() + { + return xdhPublicKey; } public byte[] getEncoded() { - return Arrays.concatenate(kybPriv.getEncoded(), xdhPriv.getEncoded()); + return Arrays.clone(seed); } } diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/XWingTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/XWingTest.java index 8568691939..a65a316210 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/XWingTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/XWingTest.java @@ -1,5 +1,7 @@ package org.bouncycastle.pqc.crypto.test; +import java.security.SecureRandom; + import junit.framework.TestCase; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.SecretWithEncapsulation; @@ -16,70 +18,261 @@ public class XWingTest extends TestCase { - public void testKEM() + public static void main(String[] args) throws Exception { - String temp = "061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"; - String expectedSecret = "81b50581e5f7390675d542802e84f717fa3b87b5391217869e00eb54ba679a00"; - - byte[] seed = Hex.decode(temp); - - NISTSecureRandom random = new NISTSecureRandom(seed, null); - - byte[] coins = new byte[96]; - random.nextBytes(coins); - - XWingKeyPairGenerator keyGen = new XWingKeyPairGenerator(); - - keyGen.init(new XWingKeyGenerationParameters(new FixedSecureRandom(coins))); - - AsymmetricCipherKeyPair keyPair = keyGen.generateKeyPair(); - - XWingKEMGenerator kemGen = new XWingKEMGenerator(random); - - SecretWithEncapsulation secretEncap = kemGen.generateEncapsulated(keyPair.getPublic()); - - byte[] sharedSecret = secretEncap.getSecret(); + XWingTest test = new XWingTest(); + test.testKEM(); + } - assertTrue(Arrays.areEqual(Hex.decode(expectedSecret), sharedSecret)); + static byte[] seed1 = Hex.decode("7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26"); + static byte[] eseed1 = Hex.decode("3cb1eea988004b93103cfb0aeefd2a686e01fa4a58e8a3639ca8a1e3f9ae57e235b8cc87\n" + + " 3c23dc62b8d260169afa2f75ab916a58d974918835d25e6a435085b2"); + static byte[] pubEnc1 = Hex.decode("e2236b35a8c24b39b10aa1323a96a919a2ced88400633a7b07131713fc14b2b5b19cfc3d\n" + + " a5fa1a92c49f25513e0fd30d6b1611c9ab9635d7086727a4b7d21d34244e66969cf15b3b\n" + + " 2a785329f61b096b277ea037383479a6b556de7231fe4b7fa9c9ac24c0699a0018a52534\n" + + " 01bacfa905ca816573e56a2d2e067e9b7287533ba13a937dedb31fa44baced4076992361\n" + + " 0034ae31e619a170245199b3c5c39864859fe1b4c9717a07c30495bdfb98a0a002ccf56c\n" + + " 1286cef5041dede3c44cf16bf562c7448518026b3d8b9940680abd38a1575fd27b58da06\n" + + " 3bfac32c39c30869374c05c1aeb1898b6b303cc68be455346ee0af699636224a148ca2ae\n" + + " a10463111c709f69b69c70ce8538746698c4c60a9aef0030c7924ceec42a5d36816f545e\n" + + " ae13293460b3acb37ea0e13d70e4aa78686da398a8397c08eaf96882113fe4f7bad4da40\n" + + " b0501e1c753efe73053c87014e8661c33099afe8bede414a5b1aa27d8392b3e131e9a70c\n" + + " 1055878240cad0f40d5fe3cdf85236ead97e2a97448363b2808caafd516cd25052c5c362\n" + + " 543c2517e4acd0e60ec07163009b6425fc32277acee71c24bab53ed9f29e74c66a0a3564\n" + + " 955998d76b96a9a8b50d1635a4d7a67eb42df5644d330457293a8042f53cc7a69288f17e\n" + + " d55827e82b28e82665a86a14fbd96645eca8172c044f83bc0d8c0b4c8626985631ca87af\n" + + " 829068f1358963cb333664ca482763ba3b3bb208577f9ba6ac62c25f76592743b64be519\n" + + " 317714cb4102cb7b2f9a25b2b4f0615de31decd9ca55026d6da0b65111b16fe52feed8a4\n" + + " 87e144462a6dba93728f500b6ffc49e515569ef25fed17aff520507368253525860f58be\n" + + " 3be61c964604a6ac814e6935596402a520a4670b3d284318866593d15a4bb01c35e3e587\n" + + " ee0c67d2880d6f2407fb7a70712b838deb96c5d7bf2b44bcf6038ccbe33fbcf51a54a584\n" + + " fe90083c91c7a6d43d4fb15f48c60c2fd66e0a8aad4ad64e5c42bb8877c0ebec2b5e387c\n" + + " 8a988fdc23beb9e16c8757781e0a1499c61e138c21f216c29d076979871caa6942bafc09\n" + + " 0544bee99b54b16cb9a9a364d6246d9f42cce53c66b59c45c8f9ae9299a75d15180c3c95\n" + + " 2151a91b7a10772429dc4cbae6fcc622fa8018c63439f890630b9928db6bb7f9438ae406\n" + + " 5ed34d73d486f3f52f90f0807dc88dfdd8c728e954f1ac35c06c000ce41a0582580e3bb5\n" + + " 7b672972890ac5e7988e7850657116f1b57d0809aaedec0bede1ae148148311c6f7e3173\n" + + " 46e5189fb8cd635b986f8c0bdd27641c584b778b3a911a80be1c9692ab8e1bbb12839573\n" + + " cce19df183b45835bbb55052f9fc66a1678ef2a36dea78411e6c8d60501b4e60592d1369\n" + + " 8a943b509185db912e2ea10be06171236b327c71716094c964a68b03377f513a05bcd99c\n" + + " 1f346583bb052977a10a12adfc758034e5617da4c1276585e5774e1f3b9978b09d0e9c44\n" + + " d3bc86151c43aad185712717340223ac381d21150a04294e97bb13bbda21b5a182b6da96\n" + + " 9e19a7fd072737fa8e880a53c2428e3d049b7d2197405296ddb361912a7bcf4827ced611\n" + + " d0c7a7da104dde4322095339f64a61d5bb108ff0bf4d780cae509fb22c256914193ff734\n" + + " 9042581237d522828824ee3bdfd07fb03f1f942d2ea179fe722f06cc03de5b69859edb06\n" + + " eff389b27dce59844570216223593d4ba32d9abac8cd049040ef6534"); + static byte[] privEnc1 = Hex.decode("7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26"); + static byte[] ss1 = Hex.decode("d2df0522128f09dd8e2c92b1e905c793d8f57a54c3da25861f10bf4ca613e384"); + static byte[] ct1 = Hex.decode("b83aa828d4d62b9a83ceffe1d3d3bb1ef31264643c070c5798927e41fb07914a273f8f96\n" + + " e7826cd5375a283d7da885304c5de0516a0f0654243dc5b97f8bfeb831f68251219aabdd\n" + + " 723bc6512041acbaef8af44265524942b902e68ffd23221cda70b1b55d776a92d1143ea3\n" + + " a0c475f63ee6890157c7116dae3f62bf72f60acd2bb8cc31ce2ba0de364f52b8ed38c79d\n" + + " 719715963a5dd3842d8e8b43ab704e4759b5327bf027c63c8fa857c4908d5a8a7b88ac7f\n" + + " 2be394d93c3706ddd4e698cc6ce370101f4d0213254238b4a2e8821b6e414a1cf20f6c12\n" + + " 44b699046f5a01caa0a1a55516300b40d2048c77cc73afba79afeea9d2c0118bdf2adb88\n" + + " 70dc328c5516cc45b1a2058141039e2c90a110a9e16b318dfb53bd49a126d6b73f215787\n" + + " 517b8917cc01cabd107d06859854ee8b4f9861c226d3764c87339ab16c3667d2f49384e5\n" + + " 5456dd40414b70a6af841585f4c90c68725d57704ee8ee7ce6e2f9be582dbee985e038ff\n" + + " c346ebfb4e22158b6c84374a9ab4a44e1f91de5aac5197f89bc5e5442f51f9a5937b102b\n" + + " a3beaebf6e1c58380a4a5fedce4a4e5026f88f528f59ffd2db41752b3a3d90efabe46389\n" + + " 9b7d40870c530c8841e8712b733668ed033adbfafb2d49d37a44d4064e5863eb0af0a08d\n" + + " 47b3cc888373bc05f7a33b841bc2587c57eb69554e8a3767b7506917b6b70498727f16ea\n" + + " c1a36ec8d8cfaf751549f2277db277e8a55a9a5106b23a0206b4721fa9b3048552c5bd5b\n" + + " 594d6e247f38c18c591aea7f56249c72ce7b117afcc3a8621582f9cf71787e183dee0936\n" + + " 7976e98409ad9217a497df888042384d7707a6b78f5f7fb8409e3b535175373461b77600\n" + + " 2d799cbad62860be70573ecbe13b246e0da7e93a52168e0fb6a9756b895ef7f0147a0dc8\n" + + " 1bfa644b088a9228160c0f9acf1379a2941cd28c06ebc80e44e17aa2f8177010afd78a97\n" + + " ce0868d1629ebb294c5151812c583daeb88685220f4da9118112e07041fcc24d5564a99f\n" + + " dbde28869fe0722387d7a9a4d16e1cc8555917e09944aa5ebaaaec2cf62693afad42a3f5\n" + + " 18fce67d273cc6c9fb5472b380e8573ec7de06a3ba2fd5f931d725b493026cb0acbd3fe6\n" + + " 2d00e4c790d965d7a03a3c0b4222ba8c2a9a16e2ac658f572ae0e746eafc4feba023576f\n" + + " 08942278a041fb82a70a595d5bacbf297ce2029898a71e5c3b0d1c6228b485b1ade509b3\n" + + " 5fbca7eca97b2132e7cb6bc465375146b7dceac969308ac0c2ac89e7863eb8943015b243\n" + + " 14cafb9c7c0e85fe543d56658c213632599efabfc1ec49dd8c88547bb2cc40c9d38cbd30\n" + + " 99b4547840560531d0188cd1e9c23a0ebee0a03d5577d66b1d2bcb4baaf21cc7fef1e038\n" + + " 06ca96299df0dfbc56e1b2b43e4fc20c37f834c4af62127e7dae86c3c25a2f696ac8b589\n" + + " dec71d595bfbe94b5ed4bc07d800b330796fda89edb77be0294136139354eb8cd3759157\n" + + " 8f9c600dd9be8ec6219fdd507adf3397ed4d68707b8d13b24ce4cd8fb22851bfe9d63240\n" + + " 7f31ed6f7cb1600de56f17576740ce2a32fc5145030145cfb97e63e0e41d354274a079d3\n" + + " e6fb2e15"); - XWingKEMExtractor kemExtract = new XWingKEMExtractor((XWingPrivateKeyParameters)keyPair.getPrivate()); + static byte[] seed2 = Hex.decode("badfd6dfaac359a5efbb7bcc4b59d538df9a04302e10c8bc1cbf1a0b3a5120ea"); + static byte[] eseed2 = Hex.decode("17cda7cfad765f5623474d368ccca8af0007cd9f5e4c849f167a580b14aabdefaee7eef4\n" + + " 7cb0fca9767be1fda69419dfb927e9df07348b196691abaeb580b32d"); + static byte[] pubEnc2 = Hex.decode("0333285fa253661508c9fb444852caa4061636cb060e69943b431400134ae1fbc0228724\n" + + " 7cb38068bbb89e6714af10a3fcda6613acc4b5e4b0d6eb960c302a0253b1f507b596f088\n" + + " 4d351da89b01c35543214c8e542390b2bc497967961ef10286879c34316e6483b644fc27\n" + + " e8019d73024ba1d1cc83650bb068a5431b33d1221b3d122dc1239010a55cb13782140893\n" + + " f30aca7c09380255a0c621602ffbb6a9db064c1406d12723ab3bbe2950a21fe521b160b3\n" + + " 0b16724cc359754b4c88342651333ea9412d5137791cf75558ebc5c54c520dd6c622a059\n" + + " f6b332ccebb9f24103e59a297cd69e4a48a3bfe53a5958559e840db5c023f66c10ce2308\n" + + " 1c2c8261d744799ba078285cfa71ac51f44708d0a6212c3993340724b3ac38f63e82a889\n" + + " a4fc581f6b8353cc6233ac8f5394b6cca292f892360570a3031c90c4da3f02a895677390\n" + + " e60c24684a405f69ccf1a7b95312a47c844a4f9c2c4a37696dc10072a87bf41a2717d45b\n" + + " 2a99ce09a4898d5a3f6b67085f9a626646bcf369982d483972b9cd7d244c4f49970f766a\n" + + " 22507925eca7df99a491d80c27723e84c7b49b633a46b46785a16a41e02c538251622117\n" + + " 364615d9c2cdaa1687a860c18bfc9ce8690efb2a524cb97cdfd1a4ea661fa7d08817998a\n" + + " f838679b07c9db8455e2167a67c14d6a347522e89e8971270bec858364b1c1023b82c483\n" + + " cf8a8b76f040fe41c24dec2d49f6376170660605b80383391c4abad1136d874a77ef73b4\n" + + " 40758b6e7059add20873192e6e372e069c22c5425188e5c240cb3a6e29197ad17e87ec41\n" + + " a813af68531f262a6db25bbdb8a15d2ed9c9f35b9f2063890bd26ef09426f225aa1e6008\n" + + " d31600a29bcdf3b10d0bc72788d35e25f4976b3ca6ac7cbf0b442ae399b225d9714d0638\n" + + " a864bda7018d3b7c793bd2ace6ac68f4284d10977cc029cf203c5698f15a06b162d6c8b4\n" + + " fd40c6af40824f9c6101bb94e9327869ab7efd835dfc805367160d6c8571e3643ac70cba\n" + + " d5b96a1ad99352793f5af71705f95126cb4787392e94d808491a2245064ba5a7a30c0663\n" + + " 01392a6c315336e10dbc9c2177c7af382765b6c88eeab51588d01d6a95747f3652dc5b5c\n" + + " 401a23863c7a0343737c737c99287a40a90896d4594730b552b910d23244684206f0eb84\n" + + " 2fb9aa316ab182282a75fb72b6806cea4774b822169c386a58773c3edc8229d85905abb8\n" + + " 7ac228f0f7a2ce9a497bb5325e17a6a82777a997c036c3b862d29c14682ad325a9600872\n" + + " f3913029a1588648ba590a7157809ff740b5138380015c40e9fb90f0311107946f28e596\n" + + " 2e21666ad65092a3a60480cd16e61ff7fb5b44b70cf12201878428ef8067fceb1e1dcb49\n" + + " d66c773d312c7e53238cb620e126187009472d41036b702032411dc96cb750631df9d994\n" + + " 52e495deb4300df660c8d35f32b424e98c7ed14b12d8ab11a289ac63c50a24d52925950e\n" + + " 49ba6bf4c2c38953c92d60b6cd034e575c711ac41bfa66951f62b9392828d7b45aed377a\n" + + " c69c35f1c6b80f388f34e0bb9ce8167eb2bc630382825c396a407e905108081b444ac8a0\n" + + " 7c2507376a750d18248ee0a81c4318d9a38fc44c3b41e8681f87c34138442659512c4127\n" + + " 6e1cc8fc4eb66e12727bcb5a9e0e405cdea21538d6ea885ab169050e6b91e1b69f7ed34b\n" + + " cbb48fd4c562a576549f85b528c953926d96ea8a160b8843f1c89c62"); + static byte[] privEnc2 = Hex.decode("badfd6dfaac359a5efbb7bcc4b59d538df9a04302e10c8bc1cbf1a0b3a5120ea"); + static byte[] ss2 = Hex.decode("f2e86241c64d60f6649fbc6c5b7d17180b780a3f34355e64a85749949c45f150"); + static byte[] ct2 = Hex.decode("c93beb22326705699bbc3d1d0aa6339be7a405debe61a7c337e1a91453c097a6f77c1306\n" + + " 39d1aaeb193175f1a987aa1fd789a63c9cd487ebd6965f5d8389c8d7c8cfacbba4b44d2f\n" + + " be0ae84de9e96fb11215d9b76acd51887b752329c1a3e0468ccc49392c1e0f1aad61a73c\n" + + " 10831e60a9798cb2e7ec07596b5803db3e243ecbb94166feade0c9197378700f8eb65a43\n" + + " 502bbac4605992e2de2b906ab30ba401d7e1ff3c98f42cfc4b30b974d3316f331461ac05\n" + + " f43e0db7b41d3da702a4f567b6ee7295199c7be92f6b4a47e7307d34278e03c872fb4864\n" + + " 7c446a64a3937dccd7c6d8de4d34b9dea45a0b065ef15b9e94d1b6df6dca7174d9bc9d14\n" + + " c6225e3a78a58785c3fe4e2fe6a0706f3365389e4258fbb61ecf1a1957715982b3f18444\n" + + " 24e03acd83da7eee50573f6cd3ff396841e9a00ad679da92274129da277833d0524674fe\n" + + " ea09a98d25b888616f338412d8e65e151e65736c8c6fb448c9260fa20e7b2712148bcd3a\n" + + " 0853865f50c1fc9e4f201aee3757120e034fd509d954b7a749ff776561382c4cb64cebcb\n" + + " b6aa82d04cd5c2b40395ecaf231bde8334ecfd955d09efa8c6e7935b1cb0298fb8b6740b\n" + + " e4593360eed5f129d59d98822a6cea37c57674e919e84d6b90f695fca58e7d29092bd70f\n" + + " 7c97c6dfb021b9f87216a6271d8b144a364d03b6bf084f972dc59800b14a2c008bbd0992\n" + + " b5b82801020978f2bdddb3ca3367d876cffb3548dab695a29882cae2eb5ba7c847c3c71b\n" + + " d0150fa9c33aac8e6240e0c269b8e295ddb7b77e9c17bd310be65e28c0802136d086777b\n" + + " e5652d6f1ac879d3263e9c712d1af736eac048fe848a577d6afaea1428dc71db8c430edd\n" + + " 7b584ae6e6aeaf7257aff0fd8fe25c30840e30ccfa1d95118ef0f6657367e9070f3d97a2\n" + + " e9a7bae19957bd707b00e31b6b0ebb9d7df4bd22e44c060830a194b5b8288353255b5295\n" + + " 4ff5905ab2b126d9aa049e44599368c27d6cb033eae5182c2e1504ee4e3745f51488997b\n" + + " 8f958f0209064f6f44a7e4de5226d5594d1ad9b42ac59a2d100a2f190df873a2e141552f\n" + + " 33c923b4c927e8747c6f830c441a8bd3c5b371f6b3ab8103ebcfb18543aefc1beb6f776b\n" + + " bfd5344779f4aa23daaf395f69ec31dc046b491f0e5cc9c651dfc306bd8f2105be7bc7a4\n" + + " f4e21957f87278c771528a8740a92e2daefa76a3525f1fae17ec4362a2700988001d8600\n" + + " 11d6ca3a95f79a0205bcf634cef373a8ea273ff0f4250eb8617d0fb92102a6aa09cf0c3e\n" + + " e2cad1ad96438c8e4dfd6ee0fcc85833c3103dd6c1600cd305bc2df4cda89b55ca237a3f\n" + + " 9c3f82390074ff30825fc750130ebaf13d0cf7556d2c52a98a4bad39ca5d44aaadeaef77\n" + + " 5c695e64d06e966acfcd552a14e2df6c63ae541f0fa88fc48263089685704506a21a0385\n" + + " 6ce65d4f06d54f3157eeabd62491cb4ac7bf029e79f9fbd4c77e2a3588790c710e611da8\n" + + " b2040c76a61507a8020758dcc30894ad018fef98e401cc54106e20d94bd544a8f0e1fd05\n" + + " 00342d123f618aa8c91bdf6e0e03200693c9651e469aee6f91c98bea4127ae66312f4ae3\n" + + " ea155b67"); - byte[] decryptedSharedSecret = kemExtract.extractSecret(secretEncap.getEncapsulation()); + static byte[] seed3 = Hex.decode("ef58538b8d23f87732ea63b02b4fa0f4873360e2841928cd60dd4cee8cc0d4c9"); + static byte[] eseed3 = Hex.decode("22a96188d032675c8ac850933c7aff1533b94c834adbb69c6115bad4692d8619f90b0cdf\n" + + " 8a7b9c264029ac185b70b83f2801f2f4b3f70c593ea3aeeb613a7f1b"); + static byte[] pubEnc3 = Hex.decode("36244278824f77c621c660892c1c3886a9560caa52a97c461fd3958a598e749bbc8c7798\n" + + " ac8870bac7318ac2b863000ca3b0bdcbbc1ccfcb1a30875df9a76976763247083e646ccb\n" + + " 2499a4e4f0c9f4125378ba3da1999538b86f99f2328332c177d1192b849413e655101289\n" + + " 73f679d23253850bb6c347ba7ca81b5e6ac4c574565c731740b3cd8c9756caac39fba7ac\n" + + " 422acc60c6c1a645b94e3b6d21485ebad9c4fe5bb4ea0853670c5246652bff65ce8381cb\n" + + " 473c40c1a0cd06b54dcec11872b351397c0eaf995bebdb6573000cbe2496600ba76c8cb0\n" + + " 23ec260f0571e3ec12a9c82d9db3c57b3a99e8701f78db4fabc1cc58b1bae02745073a81\n" + + " fc8045439ba3b885581a283a1ba64e103610aabb4ddfe9959e7241011b2638b56ba6a982\n" + + " ef610c514a57212555db9a98fb6bcf0e91660ec15dfa66a67408596e9ccb97489a09a073\n" + + " ffd1a0a7ebbe71aa5ff793cb91964160703b4b6c9c5390842c2c905d4a9f88111fed5787\n" + + " 4ba9b03cf611e70486edf539767c7485189d5f1b08e32a274dc24a39c918fd2a4dfa946a\n" + + " 8c897486f2c974031b2804aabc81749db430b85311372a3b8478868200b40e043f7bf4a1\n" + + " c3a08b0771b431e342ee277410bca034a0c77086c8f702b3aed2b4108bbd3af471633373\n" + + " a1ac74b128b148d1b9412aa66948cac6dc6614681fda02ca86675d2a756003c49c50f06e\n" + + " 13c63ce4bc9f321c860b202ee931834930011f485c9af86b9f642f0c353ad305c66996b9\n" + + " a136b753973929495f0d8048db75529edcb4935904797ac66605490f66329c3bb36b8573\n" + + " a3e00f817b3082162ff106674d11b261baae0506cde7e69fdce93c6c7b59b9d4c759758a\n" + + " cf287c2e4c4bfab5170a9236daf21bdb6005e92464ee8863f845cf37978ef19969264a51\n" + + " 6fe992c93b5f7ae7cb6718ac69257d630379e4aac6029cb906f98d91c92d118c36a6d161\n" + + " 15d4c8f16066078badd161a65ba51e0252bc358c67cd2c4beab2537e42956e08a39cfccf\n" + + " 0cd875b5499ee952c83a162c68084f6d35cf92f71ec66baec74ab87e2243160b64df54af\n" + + " b5a07f78ec0f5c5759e5a4322bca2643425748a1a97c62108510c44fd9089c5a7c14e57b\n" + + " 1b77532800013027cff91922d7c935b4202bb507aa47598a6a5a030117210d4c49c17470\n" + + " 0550ad6f82ad40e965598b86bc575448eb19d70380d465c1f870824c026d74a2522a799b\n" + + " 7b122d06c83aa64c0974635897261433914fdfb14106c230425a83dc8467ad8234f086c7\n" + + " 2a47418be9cfb582b1dcfa3d9aa45299b79fff265356d8286a1ca2f3c2184b2a70d15289\n" + + " e5b202d03b64c735a867b1154c55533ff61d6c296277011848143bc85a4b823040ae025a\n" + + " 29293ab77747d85310078682e0ba0ac236548d905a79494324574d417c7a3457bd5fb525\n" + + " 3c4876679034ae844d0d05010fec722db5621e3a67a2d58e2ff33b432269169b51f9dcc0\n" + + " 95b8406dc1864cf0aeb6a2132661a38d641877594b3c51892b9364d25c63d637140a2018\n" + + " d10931b0daa5a2f2a405017688c991e586b522f94b1132bc7e87a63246475816c8be9c62\n" + + " b731691ab912eb656ce2619225663364701a014b7d0337212caa2ecc731f34438289e0ca\n" + + " 4590a276802d980056b5d0d316cae2ecfea6d86696a9f161aa90ad47eaad8cadd31ae3cb\n" + + " c1c013747dfee80fb35b5299f555dcc2b787ea4f6f16ffdf66952461"); + static byte[] privEnc3 = Hex.decode("ef58538b8d23f87732ea63b02b4fa0f4873360e2841928cd60dd4cee8cc0d4c9"); + static byte[] ss3 = Hex.decode("953f7f4e8c5b5049bdc771d1dffada0dd961477d1a2ae0988baa7ea6898d893f"); + static byte[] ct3 = Hex.decode("0d2e38cbf17a2e2e4e0c87a94ca1e7701ae1552e02509b3b00f9c82c39e3fd435b05b912\n" + + " 75f47abc9f1021429a26a346598cd6cd9efdc8adc1dbc35036d0290bf89733c835309202\n" + + " 232f9bf652ea82f3d49280d6e8a3bd3135fb883445ab5b074d949c5350c7c7d6ac59905b\n" + + " dbfce6639da8a9d4b390ecc1dd05522d2956f2d37a05593996e5cb3fd8d5a9eb52417732\n" + + " e1ebf545588713b4760227115aab7ada178dadbca583b26cfedba2888a0c95b950bf07f7\n" + + " 50d7aa8103798aa3470a042c0105c6a037de2f9ebc396021b2ba2c16aba696fbac3454dc\n" + + " 8e053b8fa55edd45215eeb57a1eab9106fb426b375a9b9e5c3419efc7610977e72640f9f\n" + + " d1b2ec337de33c35e5a7581b2aae4d8ee86d2e0ebf82a1350714de50d2d788687878a196\n" + + " 44ae4e3175e8d59dc90171b3badeff65aeaf600e5e5483a3595fdeb40cbafcbd040c29a2\n" + + " f6900533ae999d24f54dfcef748c30313ca447cdddfa57ad78eaa890e90f3f7bf8d11696\n" + + " 8a5713cc75fd0408f36364fa265c5617039304eaeac4cbee6fc49b9fe2276768cdbec2d7\n" + + " 3a507b543cc028dc1b154b7c2b0412254c466a94a8d6ea3a47e1743469bd45c08f54cf96\n" + + " 5884be3696e961741ede16e3b1bc4feb93faaef31d911dc0cb3fa90bcda991959a9d2cbc\n" + + " 817a5564c5c01177a59e9577589ea344d60cf5b0aa39f31863febd54603ca87ad2363c76\n" + + " 6642a3f52557bcd9e4c05a87665842ba336b83156a677030f0bad531a8387a1486a599ca\n" + + " a748fcea7bdc1eb63f3cdb97173551ab7c1c36b69acbbdb2ff7a1e7bc70439632ddc67b9\n" + + " 7f3da1f59b3c1588515957cb8a2f86ab635ce0a78b7cdf24eac3445e8fc8b79ba04da9e9\n" + + " 03f49a7d912c197a84b4cfabc779b97d24788419bcf58035db99717edb9fd1c1df8c4005\n" + + " f700eabba528ddfcbaeda6dd30754f795948a34c9319ab653524b19931c7900c4167988a\n" + + " f52292fe902e746b524d20ceffb4339e8f5535f41cf35f0f8ea8b4a7b949c5d2381116b1\n" + + " 46e9b913a83a3fa1c65ff9468c835fe4114554a6c66a80e1c9a6bb064b380be3c95e5595\n" + + " ec979bf1c85aa938938e3f10e72b0c87811969e8ab0d83de0b0604c4016ac3a015e19514\n" + + " 089271bdc6ebf2ec56fab6018e44de749b4c36cc235e370da8466dbdc253542a2d704eb3\n" + + " 316fd70d5d238cb7eaaf05966d973f62c7ef43b9a806f4ed213ac8099ea15d61a9024441\n" + + " 60883f6bf441a3e1469945c9b79489ea18390f1ebc83caca10bdb8f2429877b52bd44c94\n" + + " a228ef91c392ef5398c5c83982701318ccedab92f7a279c4fddebaa7fe5e986c48b7d813\n" + + " 5b3fe4cd15be2004ce73ff86b1e55f8ecd6ba5b8114315f8e716ef3ab0a64564a4644651\n" + + " 166ebd68b1f783e2e443dbccadfe189368647629f1a12215840b7f1d026de2f665c2eb02\n" + + " 3ff51a6df160912811ee03444ae4227fb941dc9ec4f31b445006fd384de5e60e0a5061b5\n" + + " 0cb1202f863090fc05eb814e2d42a03586c0b56f533847ac7b8184ce9690bc8dece32a88\n" + + " ca934f541d4cc520fa64de6b6e1c3c8e03db5971a445992227c825590688d203523f5271\n" + + " 61137334"); - assertTrue(Arrays.areEqual(Hex.decode(expectedSecret), decryptedSharedSecret)); + public void testKEM() + { + kemTest(seed1, eseed1, pubEnc1, privEnc1, ss1, ct1); + kemTest(seed2, eseed2, pubEnc2, privEnc2, ss2, ct2); + kemTest(seed3, eseed3, pubEnc3, privEnc3, ss3, ct3); } - public void testKeyEncoding() - throws Exception + private void kemTest(byte[] seed, byte[] eseed, byte[] pubEnc, byte[] privEnc, byte[] ss, byte[] ct) { - byte[] pubEnc = Hex.decode("a72c2d9c843ee9f8313ecc7f86d6294d59159d9a879a542e260922adf999051cc45200c9ffdb60449c49465979272367c083a7d6267a3ed7a7fd47957c219327f7ca73a4007e1627f00b11cc80573c15aee6640fb8562dfa6b240ca0ad351ac4ac155b96c14c8ab13dd262cdfd51c4bb5572fd616553d17bdd430acbea3e95f0b698d66990ab51e5d03783a8b3d278a5720454cf9695cfdca08485ba099c51cd92a7ea7587c1d15c28e609a81852601b0604010679aa482d51261ec36e36b8719676217fd74c54786488f4b4969c05a8ba27ca3a77cce73b965923ca554e422b9b61f4754641608ac16c9b8587a32c1c5dd788f88b36b717a46965635deb67f45b129b99070909c93eb80b42c2b3f3f70343a7cf37e8520e7bcfc416aca4f18c7981262ba2bfc756ae03278f0ec66dc2057696824ba6769865a601d7148ef6f54e5af5686aa2906f994ce38a5e0b938f239007003022c03392df3401b1e4a3a7ebc6161449f73374c8b0140369343d9295fdf511845c4a46ebaab6ca5492f6800b98c0cc803653a4b1d6e6aaed1932bacc5fefaa818ba502859ba5494c5f5402c8536a9c4c1888150617f80098f6b2a99c39bc5dc7cf3b5900a21329ab59053abaa64ed163e859a8b3b3ca3359b750ccc3e710c7ac43c8191cb5d68870c06391c0cb8aec72b897ac6be7fbaacc676ed66314c83630e89448c88a1df04aceb23abf2e409ef333c622289c18a2134e650c45257e47475fa33aa537a5a8f7680214716c50d470e3284963ca64f54677aec54b5272162bf52bc8142e1d4183fc017454a6b5a496831759064024745978cbd51a6cedc8955de4cc6d363670a47466e82be5c23603a17bf22acdb7cc984af08c87e14e27753cf587a8ec3447e62c649e887a67c36c9ce98721b697213275646b194f36758673a8ed11284455afc7a8529f69c97a3c2d7b8c636c0ba55614b768e624e712930f776169b01715725351bc74b47395ed52b25a1313c95164814c34c979cbdfab85954662cab485e75087a98cc74bb82ca2d1b5bf2803238480638c40e90b43c7460e7aa917f010151fab1169987b372abb59271f7006c24e60236b84b9ddd600623704254617fb498d89e58b0368bcb2103e79353eb587860c1422e476162e425bc2381db82c6592737e1dd602864b0167a71ec1f223305c02fe25052af2b3b5a55a0d7a2022d9a798dc0c5874a98702aaf4054c5d80338a5248b5b7bd09c53b5e2a084b047d277a861b1a73bb51488de04ef573c85230a0470b73175c9fa50594f66a5f50b4150054c93b68186f8b5cbc49316c8548a642b2b36a1d454c7489ac33b2d2ce6668096782a2c1e0866d21a65e16b585e7af8618bdf3184c1986878508917277b93e10706b1614972b2a94c7310fe9c708c231a1a8ac8d9314a529a97f469bf64962d820648443099a076d55d4cea824a58304844f99497c10a25148618a315d72ca857d1b04d575b94f85c01d19bef211bf0aa3362e7041fd16596d808e867b44c4c00d1cda3418967717f147d0eb21b42aaee74ac35d0b92414b958531aadf463ec6305ae5ecaf79174002f26ddecc813bf32672e8529d95a4e730a7ab4a3e8f8a8af979a665eafd465fc64a0c5f8f3f9003489415899d59a543d8208c54a3166529b539227001e4f285d5113a537f141fa23cb2770845ac28fc9d70df557415f3bc00e735"); - byte[] privEnc = Hex.decode("07638fb69868f3d320e5862bd96933feb311b362093c9b5d50170bced43f1b536d9a204bb1f22695950ba1f2a9e8eb828b284488760b3fc84faba04275d5628e39c5b2471374283c503299c0ab49b66b8bbb56a4186624f919a2ba59bb08d8551880c2befc4f87f25f59ab587a79c327d792d54c974a69262ff8a78938289e9a87b688b083e0595fe218b6bb1505941ce2e81a5a64c5aac60417256985349ee47a52420a5f97477b7236ac76bc70e8288729287ee3e34a3dbc3683c0b7b10029fc203418537e7466ba6385a8ff301ee12708f82aaa1e380fc7a88f8f205ab7e88d7e95952a55ba20d09b79a47141d62bf6eb7dd307b08eca13a5bc5f6b68581c6865b27bbcddab142f4b2cbff488c8a22705faa98a2b9eea3530c76662335cc7ea3a00777725ebcccd2a4636b2d9122ff3ab77123ce0883c1911115e50c9e8a94194e48dd0d09cffb3adcd2c1e92430903d07adbf00532031575aa7f9e7b5a1f3362dec936d4043c05f2476c07578bc9cbaf2ab4e382727ad41686a96b2548820bb03b32f11b2811ad62f489e951632aba0d1df89680cc8a8b53b481d92a68d70b4ea1c3a6a561c0692882b5ca8cc942a8d495afcb06de89498fb935b775908fe7a03e324d54cc19d4e1aabd3593b38b19ee1388fe492b43127e5a504253786a0d69ad32601c28e2c88504a5ba599706023a61363e17c6b9bb59bdc697452cd059451983d738ca3fd034e3f5988854ca05031db09611498988197c6b30d258dfe26265541c89a4b31d6864e9389b03cb74f7ec4323fb9421a4b9790a26d17b0398a26767350909f84d57b6694df830664ca8b3c3c03ed2ae67b89006868a68527ccd666459ab7f056671000c6164d3a7f266a14d97cbd7004d6c92caca770b844a4fa9b182e7b18ca885082ac5646fcb4a14e1685feb0c9ce3372ab95365c04fd83084f80a23ff10a05bf15f7fa5acc6c0cb462c33ca524fa6b8bb359043ba68609eaa2536e81d08463b19653b5435ba946c9addeb202b04b031cc960dcc12e4518d428b32b257a4fc7313d3a7980d80082e934f9d95c32b0a0191a23604384dd9e079bbbaa266d14c3f756b9f2133107433a4e83fa7187282a809203a4faf841851833d121ac383843a5e55bc2381425e16c7db4cc9ab5c1b0d91a47e2b8de0e582c86b6b0d907bb360b97f40ab5d038f6b75c814b27d9b968d419832bc8c2bee605ef6e5059d33100d90485d378450014221736c07407cac260408aa64926619788b8601c2a752d1a6cbf820d7c7a04716203225b3895b9342d147a8185cfc1bb65ba06b4142339903c0ac4651385b45d98a8b19d28cd6bab088787f7ee1b12461766b43cbccb96434427d93c065550688f6948ed1b5475a425f1b85209d061c08b56c1cc069f6c0a7c6f29358cab911087732a649d27c9b98f9a48879387d9b00c25959a71654d6f6a946164513e47a75d005986c2363c09f6b537eca78b9303a5fa457608a586a653a347db04dfcc19175b3a301172536062a658a95277570c8852ca8973f4ae123a334047dd711c8927a634a03388a527b034bf7a8170fa702c1f7c23ec32d18a2374890be9c787a9409c82d192c4bb705a2f996ce405da72c2d9c843ee9f8313ecc7f86d6294d59159d9a879a542e260922adf999051cc45200c9ffdb60449c49465979272367c083a7d6267a3ed7a7fd47957c219327f7ca73a4007e1627f00b11cc80573c15aee6640fb8562dfa6b240ca0ad351ac4ac155b96c14c8ab13dd262cdfd51c4bb5572fd616553d17bdd430acbea3e95f0b698d66990ab51e5d03783a8b3d278a5720454cf9695cfdca08485ba099c51cd92a7ea7587c1d15c28e609a81852601b0604010679aa482d51261ec36e36b8719676217fd74c54786488f4b4969c05a8ba27ca3a77cce73b965923ca554e422b9b61f4754641608ac16c9b8587a32c1c5dd788f88b36b717a46965635deb67f45b129b99070909c93eb80b42c2b3f3f70343a7cf37e8520e7bcfc416aca4f18c7981262ba2bfc756ae03278f0ec66dc2057696824ba6769865a601d7148ef6f54e5af5686aa2906f994ce38a5e0b938f239007003022c03392df3401b1e4a3a7ebc6161449f73374c8b0140369343d9295fdf511845c4a46ebaab6ca5492f6800b98c0cc803653a4b1d6e6aaed1932bacc5fefaa818ba502859ba5494c5f5402c8536a9c4c1888150617f80098f6b2a99c39bc5dc7cf3b5900a21329ab59053abaa64ed163e859a8b3b3ca3359b750ccc3e710c7ac43c8191cb5d68870c06391c0cb8aec72b897ac6be7fbaacc676ed66314c83630e89448c88a1df04aceb23abf2e409ef333c622289c18a2134e650c45257e47475fa33aa537a5a8f7680214716c50d470e3284963ca64f54677aec54b5272162bf52bc8142e1d4183fc017454a6b5a496831759064024745978cbd51a6cedc8955de4cc6d363670a47466e82be5c23603a17bf22acdb7cc984af08c87e14e27753cf587a8ec3447e62c649e887a67c36c9ce98721b697213275646b194f36758673a8ed11284455afc7a8529f69c97a3c2d7b8c636c0ba55614b768e624e712930f776169b01715725351bc74b47395ed52b25a1313c95164814c34c979cbdfab85954662cab485e75087a98cc74bb82ca2d1b5bf2803238480638c40e90b43c7460e7aa917f010151fab1169987b372abb59271f7006c24e60236b84b9ddd600623704254617fb498d89e58b0368bcb2103e79353eb587860c1422e476162e425bc2381db82c6592737e1dd602864b0167a71ec1f223305c02fe25052af2b3b5a55a0d7a2022d9a798dc0c5874a98702aaf4054c5d80338a5248b5b7bd09c53b5e2a084b047d277a861b1a73bb51488de04ef573c85230a0470b73175c9fa50594f66a5f50b4150054c93b68186f8b5cbc49316c8548a642b2b36a1d454c7489ac33b2d2ce6668096782a2c1e0866d21a65e16b585e7af8618bdf3184c1986878508917277b93e10706b1614972b2a94c7310fe9c708c231a1a8ac8d9314a529a97f469bf64962d820648443099a076d55d4cea824a58304844f99497c10a25148618a315d72ca857d1b04d575b94f85c01d19bef211bf0aa3362e7041fd16596d808e867b44c4c00d1cda3418967717f147d0eb21b42aaee74ac35d0b92414b958531aadf463ec6305ae5ecaf79174002f26ddecc813bf32672e8529d95a4e730a7ab4a3e8f8a8af979a665eafd465fc64a0c5f8f3f9003489415899d59a543d8208c54a3166529b53922d4ec143b50f01423b177895edee22bb739f647ecf85f50bc25ef7b5a725dee86b505d7cfad1b497499323c8686325e4792f267aafa3f87ca60d01cb54f29202a38784ccb7ebcdcfd45542b7f6af778742e0f4479175084aa488b3b743406786a"); - String temp = "061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"; - String expectedSecret = "e5015e7e9b71e3a0436b159a042b14cb5b63435eee3b8db95f1e8fcce44632a8"; - - byte[] seed = Hex.decode(temp); - - NISTSecureRandom random = new NISTSecureRandom(seed, null); + SecureRandom random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(seed)}); + XWingKeyPairGenerator keyGen = new XWingKeyPairGenerator(); + keyGen.init(new XWingKeyGenerationParameters(random)); + AsymmetricCipherKeyPair keyPair = keyGen.generateKeyPair(); - byte[] coins = new byte[96]; - random.nextBytes(coins); XWingPublicKeyParameters publicKey = new XWingPublicKeyParameters(pubEnc); + assertTrue(Arrays.areEqual(((XWingPublicKeyParameters)keyPair.getPublic()).getEncoded(), pubEnc)); + assertTrue(Arrays.areEqual(((XWingPrivateKeyParameters)keyPair.getPrivate()).getEncoded(), privEnc)); XWingPrivateKeyParameters privKey = new XWingPrivateKeyParameters(privEnc); - + random = new FixedSecureRandom(new FixedSecureRandom.Source[]{new FixedSecureRandom.Data(eseed)}); XWingKEMGenerator kemGen = new XWingKEMGenerator(random); SecretWithEncapsulation secretEncap = kemGen.generateEncapsulated(publicKey); byte[] sharedSecret = secretEncap.getSecret(); - - assertTrue(Arrays.areEqual(Hex.decode(expectedSecret), sharedSecret)); - + byte[] encapsulation = secretEncap.getEncapsulation(); + assertTrue(Arrays.areEqual(ss, sharedSecret)); + assertTrue(Arrays.areEqual(encapsulation, ct)); XWingKEMExtractor kemExtract = new XWingKEMExtractor(privKey); - byte[] decryptedSharedSecret = kemExtract.extractSecret(secretEncap.getEncapsulation()); + byte[] decryptedSharedSecret = kemExtract.extractSecret(encapsulation); - assertTrue(Arrays.areEqual(Hex.decode(expectedSecret), decryptedSharedSecret)); + assertTrue(Arrays.areEqual(ss, decryptedSharedSecret)); } } From b9b13252fb0d30fb977b12956eae2494f711fcda Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 29 May 2025 15:07:26 +0700 Subject: [PATCH 391/890] Test updates --- .../java/org/bouncycastle/crypto/test/CTSTest.java | 8 ++++---- .../bouncycastle/crypto/test/ChaCha20Poly1305Test.java | 6 +++--- .../org/bouncycastle/crypto/test/DSTU7624Test.java | 4 ++-- .../java/org/bouncycastle/crypto/test/NISTCTSTest.java | 4 ++-- .../java/org/bouncycastle/crypto/test/PaddingTest.java | 4 ++-- .../jce/provider/test/BlockCipherTest.java | 10 +++++----- 6 files changed, 18 insertions(+), 18 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java index 37d38330e7..5b61e8d2d2 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CTSTest.java @@ -190,7 +190,7 @@ private void testOverlapping() if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) { - fail("failed to overlapping of encryption"); + fail("failed for overlapping encryption"); } bc.init(false, key); @@ -200,7 +200,7 @@ private void testOverlapping() if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) { - fail("failed to overlapping of encryption"); + fail("failed for overlapping decryption"); } } @@ -228,7 +228,7 @@ private void testOverlapping2() if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) { - fail("failed to overlapping of encryption"); + fail("failed for overlapping encryption"); } bc.init(false, key); @@ -238,7 +238,7 @@ private void testOverlapping2() if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) { - fail("failed to overlapping of encryption"); + fail("failed for overlapping decryption"); } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/ChaCha20Poly1305Test.java b/core/src/test/java/org/bouncycastle/crypto/test/ChaCha20Poly1305Test.java index 362e5f5b3a..b8dc6a5c29 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/ChaCha20Poly1305Test.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/ChaCha20Poly1305Test.java @@ -478,17 +478,17 @@ private void testOverlapping() if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) { - fail("failed to overlapping of encryption"); + fail("failed for overlapping encryption"); } bc = initCipher(false, parameters); bc.processBytes(data, 0, expected.length, expected, 0); - bc = initCipher(true, parameters); + bc = initCipher(false, parameters); bc.processBytes(data, 0, expected.length, data, offset); if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) { - fail("failed to overlapping of encryption"); + fail("failed for overlapping decryption"); } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/DSTU7624Test.java b/core/src/test/java/org/bouncycastle/crypto/test/DSTU7624Test.java index fd3a0d531e..084f400862 100755 --- a/core/src/test/java/org/bouncycastle/crypto/test/DSTU7624Test.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/DSTU7624Test.java @@ -1489,7 +1489,7 @@ private void testOverlapping() if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) { - fail("failed to overlapping of encryption"); + fail("failed for overlapping encryption"); } bc.init(false, param); @@ -1499,7 +1499,7 @@ private void testOverlapping() if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) { - fail("failed to overlapping of encryption"); + fail("failed for overlapping decryption"); } } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/NISTCTSTest.java b/core/src/test/java/org/bouncycastle/crypto/test/NISTCTSTest.java index 77f4e64bd2..11d1009a86 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/NISTCTSTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/NISTCTSTest.java @@ -162,7 +162,7 @@ private void testOverlapping() if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) { - fail("failed to overlapping of encryption"); + fail("failed for overlapping encryption"); } bc.init(false, param); @@ -172,7 +172,7 @@ private void testOverlapping() if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + expected.length))) { - fail("failed to overlapping of encryption"); + fail("failed for overlapping decryption"); } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/PaddingTest.java b/core/src/test/java/org/bouncycastle/crypto/test/PaddingTest.java index 379a6c2dc0..3c6f6fd24f 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/PaddingTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/PaddingTest.java @@ -160,7 +160,7 @@ private void testOverlapping() if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) { - fail("failed to overlapping of encryption"); + fail("failed for overlapping encryption"); } bc.init(false, key); @@ -170,7 +170,7 @@ private void testOverlapping() if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) { - fail("failed to overlapping of encryption"); + fail("failed for overlapping decryption"); } } diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java index 6e3bea071d..0f3bf103df 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java @@ -1848,7 +1848,7 @@ private void testOverlapping() if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) { - fail("failed to overlapping of encryption"); + fail("failed for overlapping encryption"); } bc.init(false, key); @@ -1858,7 +1858,7 @@ private void testOverlapping() if (!areEqual(expected, Arrays.copyOfRange(data, offset, offset + bc.getBlockSize() * 2))) { - fail("failed to overlapping of encryption"); + fail("failed for overlapping decryption"); } } @@ -1931,9 +1931,9 @@ public void testOverlap() // Grab a copy of the produced cipher text byte[] ct = Arrays.copyOfRange(workingArray, outputOffset, outputOffset + msg.length); - System.out.println("\nOutput Offset: " + outputOffset); - System.out.println("Expected: " + pad(outputOffset * 2) + Hex.toHexString(expectedOutput)); - System.out.println("Actual : " + Hex.toHexString(workingArray)); +// System.out.println("\nOutput Offset: " + outputOffset); +// System.out.println("Expected: " + pad(outputOffset * 2) + Hex.toHexString(expectedOutput)); +// System.out.println("Actual : " + Hex.toHexString(workingArray)); isTrue(Arrays.areEqual(ct, expectedOutput)); From 134689f7ee3d0196b6de8eb8414597098fce0dc2 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 29 May 2025 15:15:37 +0700 Subject: [PATCH 392/890] Move segmentsOverlap methods to Arrays class --- .../org/bouncycastle/crypto/BufferedBlockCipher.java | 9 ++------- .../crypto/DefaultBufferedBlockCipher.java | 3 ++- .../bouncycastle/crypto/DefaultMultiBlockCipher.java | 10 +++------- .../bouncycastle/crypto/engines/AEADBaseEngine.java | 10 ++-------- .../org/bouncycastle/crypto/modes/CTSBlockCipher.java | 3 ++- .../bouncycastle/crypto/modes/ChaCha20Poly1305.java | 8 +------- .../org/bouncycastle/crypto/modes/EAXBlockCipher.java | 8 +------- .../org/bouncycastle/crypto/modes/GCMBlockCipher.java | 8 +------- .../org/bouncycastle/crypto/modes/KXTSBlockCipher.java | 7 ++++--- .../bouncycastle/crypto/modes/NISTCTSBlockCipher.java | 3 ++- .../org/bouncycastle/crypto/modes/OCBBlockCipher.java | 8 +------- .../bouncycastle/crypto/modes/OldCTSBlockCipher.java | 3 ++- .../crypto/paddings/PaddedBufferedBlockCipher.java | 3 ++- core/src/main/java/org/bouncycastle/util/Arrays.java | 8 ++++++++ 14 files changed, 33 insertions(+), 58 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java index 5ca338cf99..5632d95ae5 100644 --- a/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto; +import org.bouncycastle.util.Arrays; /** * A wrapper class that allows block ciphers to be used to process data in @@ -237,7 +238,7 @@ public int processBytes( System.arraycopy(in, inOff, buf, bufOff, gapLen); inOff += gapLen; len -= gapLen; - if (in == out && segmentsOverlap(inOff, len, outOff, length)) + if (in == out && Arrays.segmentsOverlap(inOff, len, outOff, length)) { in = new byte[len]; System.arraycopy(out, inOff, in, 0, len); @@ -357,10 +358,4 @@ public void reset() // cipher.reset(); } - - protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) - { - // please ensure a valid check for inLen > 0 and outLen > 0 outside this function - return inOff <= outOff + outLen && outOff <= inOff + inLen; - } } diff --git a/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java index 52868125e4..51a4df20ea 100644 --- a/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/DefaultBufferedBlockCipher.java @@ -1,5 +1,6 @@ package org.bouncycastle.crypto; +import org.bouncycastle.util.Arrays; /** * A wrapper class that allows block ciphers to be used to process data in @@ -239,7 +240,7 @@ public int processBytes( System.arraycopy(in, inOff, buf, bufOff, gapLen); inOff += gapLen; len -= gapLen; - if (in == out && segmentsOverlap(inOff, len, outOff, length)) + if (in == out && Arrays.segmentsOverlap(inOff, len, outOff, length)) { in = new byte[len]; System.arraycopy(out, inOff, in, 0, len); diff --git a/core/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java index c64d44b5d2..e2964aa05d 100644 --- a/core/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/DefaultMultiBlockCipher.java @@ -1,5 +1,7 @@ package org.bouncycastle.crypto; +import org.bouncycastle.util.Arrays; + public abstract class DefaultMultiBlockCipher implements MultiBlockCipher { @@ -21,7 +23,7 @@ public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int o int resultLen = 0; int blockSize = this.getMultiBlockSize(); int len = blockCount * blockSize; - if (in == out && segmentsOverlap(inOff, len, outOff, len)) + if (in == out && Arrays.segmentsOverlap(inOff, len, outOff, len)) { in = new byte[len]; System.arraycopy(out, inOff, in, 0, len); @@ -36,10 +38,4 @@ public int processBlocks(byte[] in, int inOff, int blockCount, byte[] out, int o return resultLen; } - - protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) - { - // please ensure a valid check for inLen > 0 and outLen > 0 outside this function - return inOff <= outOff + outLen && outOff <= inOff + inLen; - } } diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java index 2f41a40749..22af62b796 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java @@ -637,7 +637,7 @@ public int processByte(byte input, byte[] output, int outOff) @Override public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { - if (input == output && segmentsOverlap(inOff, len, outOff, processor.getUpdateOutputSize(len))) + if (input == output && Arrays.segmentsOverlap(inOff, len, outOff, processor.getUpdateOutputSize(len))) { input = new byte[len]; System.arraycopy(output, inOff, input, 0, len); @@ -793,7 +793,7 @@ protected int processEncDecBytes(byte[] input, int inOff, int len, byte[] output resultLength = length + m_bufPos - (forEncryption ? 0 : MAC_SIZE); ensureSufficientOutputBuffer(output, outOff, resultLength - resultLength % BlockSize); resultLength = 0; - if (input == output && segmentsOverlap(inOff, len, outOff, length)) + if (input == output && Arrays.segmentsOverlap(inOff, len, outOff, length)) { input = new byte[len]; System.arraycopy(output, inOff, input, 0, len); @@ -1077,12 +1077,6 @@ protected void finishAAD3(State nextState, boolean isDoFinal) m_state = nextState; } - private boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) - { - // please ensure a valid check for inLen > 0 and outLen > 0 outside this function - return inOff <= outOff + outLen && outOff <= inOff + inLen; - } - protected abstract void finishAAD(State nextState, boolean isDoFinal); protected abstract void init(byte[] key, byte[] iv); diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java index fd29ece1ea..0671ee4495 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java @@ -6,6 +6,7 @@ import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.StreamBlockCipher; +import org.bouncycastle.util.Arrays; /** * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to @@ -148,7 +149,7 @@ public int processBytes( System.arraycopy(in, inOff, buf, bufOff, gapLen); inOff += gapLen; len -= gapLen; - if (in == out && segmentsOverlap(inOff, len, outOff, length)) + if (in == out && Arrays.segmentsOverlap(inOff, len, outOff, length)) { in = new byte[len]; System.arraycopy(out, inOff, in, 0, len); diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/ChaCha20Poly1305.java b/core/src/main/java/org/bouncycastle/crypto/modes/ChaCha20Poly1305.java index ed1291419c..49d43a2557 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/ChaCha20Poly1305.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/ChaCha20Poly1305.java @@ -307,7 +307,7 @@ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) t { throw new IllegalArgumentException("'outOff' cannot be negative"); } - if (in == out && segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len))) + if (in == out && Arrays.segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len))) { in = new byte[len]; System.arraycopy(out, inOff, in, 0, len); @@ -617,10 +617,4 @@ private void reset(boolean clearMac, boolean resetCipher) processAADBytes(initialAAD, 0, initialAAD.length); } } - - protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) - { - // please ensure a valid check for inLen > 0 and outLen > 0 outside this function - return inOff <= outOff + outLen && outOff <= inOff + inLen; - } } diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java index 70ac9f8b0c..80d766a5d7 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java @@ -224,7 +224,7 @@ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) { throw new DataLengthException("Input buffer too short"); } - if (in == out && segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len))) + if (in == out && Arrays.segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len))) { in = new byte[len]; System.arraycopy(out, inOff, in, 0, len); @@ -390,10 +390,4 @@ private boolean verifyMac(byte[] mac, int off) return nonEqual == 0; } - - protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) - { - // please ensure a valid check for inLen > 0 and outLen > 0 outside this function - return inOff <= outOff + outLen && outOff <= inOff + inLen; - } } diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java index 79e9587be7..42c0c23528 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/GCMBlockCipher.java @@ -383,7 +383,7 @@ public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) { throw new DataLengthException("Input buffer too short"); } - if (in == out && segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len))) + if (in == out && Arrays.segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len))) { in = new byte[len]; System.arraycopy(out, inOff, in, 0, len); @@ -754,10 +754,4 @@ private void checkStatus() throw new IllegalStateException("GCM cipher needs to be initialised"); } } - - protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) - { - // please ensure a valid check for inLen > 0 and outLen > 0 outside this function - return inOff <= outOff + outLen && outOff <= inOff + inLen; - } } diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java index 083ef1cd6e..62ece62b53 100755 --- a/core/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/KXTSBlockCipher.java @@ -6,6 +6,7 @@ import org.bouncycastle.crypto.DefaultBufferedBlockCipher; import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Pack; /** @@ -123,7 +124,7 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output, int out { throw new IllegalArgumentException("Partial blocks not supported"); } - if (input == output && segmentsOverlap(inOff, len, outOff, len)) + if (input == output && Arrays.segmentsOverlap(inOff, len, outOff, len)) { input = new byte[len]; System.arraycopy(output, inOff, input, 0, len); @@ -131,13 +132,13 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output, int out } for (int pos = 0; pos < len; pos += blockSize) { - processBlocks(input, inOff + pos, output, outOff + pos); + processBlock(input, inOff + pos, output, outOff + pos); } return len; } - private void processBlocks(byte[] input, int inOff, byte[] output, int outOff) + private void processBlock(byte[] input, int inOff, byte[] output, int outOff) { /* * A somewhat arbitrary limit of 2^32 - 1 blocks diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java index f159d277fe..66df8ddf0e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/NISTCTSBlockCipher.java @@ -9,6 +9,7 @@ import org.bouncycastle.crypto.DefaultBufferedBlockCipher; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.OutputLengthException; +import org.bouncycastle.util.Arrays; /** * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to @@ -157,7 +158,7 @@ public int processBytes( System.arraycopy(in, inOff, buf, bufOff, gapLen); inOff += gapLen; len -= gapLen; - if (in == out && segmentsOverlap(inOff, len, outOff, length)) + if (in == out && Arrays.segmentsOverlap(inOff, len, outOff, length)) { in = new byte[len]; System.arraycopy(out, inOff, in, 0, len); diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java index 65b1cb1daa..fd03272a4f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java @@ -333,7 +333,7 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output, int out throw new DataLengthException("Input buffer too short"); } int resultLen = 0; - if (input == output && segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len))) + if (input == output && Arrays.segmentsOverlap(inOff, len, outOff, getUpdateOutputSize(len))) { input = new byte[len]; System.arraycopy(output, inOff, input, 0, len); @@ -597,10 +597,4 @@ protected static void xor(byte[] block, byte[] val) { Bytes.xorTo(16, val, block); } - - protected boolean segmentsOverlap(int inOff, int inLen, int outOff, int outLen) - { - // please ensure a valid check for inLen > 0 and outLen > 0 outside this function - return inOff <= outOff + outLen && outOff <= inOff + inLen; - } } diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java index c1fcfdb839..3b09bf3ab2 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java @@ -5,6 +5,7 @@ import org.bouncycastle.crypto.DefaultBufferedBlockCipher; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.OutputLengthException; +import org.bouncycastle.util.Arrays; /** * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to @@ -151,7 +152,7 @@ public int processBytes( System.arraycopy(in, inOff, buf, bufOff, gapLen); inOff += gapLen; len -= gapLen; - if (in == out && segmentsOverlap(inOff, len, outOff, length)) + if (in == out && Arrays.segmentsOverlap(inOff, len, outOff, length)) { in = new byte[len]; System.arraycopy(out, inOff, in, 0, len); diff --git a/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java b/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java index 7b42234e26..9250a649cf 100644 --- a/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java @@ -7,6 +7,7 @@ import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.util.Arrays; /** * A wrapper class that allows block ciphers to be used to process data in @@ -204,7 +205,7 @@ public int processBytes( System.arraycopy(in, inOff, buf, bufOff, gapLen); inOff += gapLen; len -= gapLen; - if (in == out && segmentsOverlap(inOff, len, outOff, length)) + if (in == out && Arrays.segmentsOverlap(inOff, len, outOff, length)) { in = new byte[len]; System.arraycopy(out, inOff, in, 0, len); diff --git a/core/src/main/java/org/bouncycastle/util/Arrays.java b/core/src/main/java/org/bouncycastle/util/Arrays.java index 1fbd229188..db6f127dcf 100644 --- a/core/src/main/java/org/bouncycastle/util/Arrays.java +++ b/core/src/main/java/org/bouncycastle/util/Arrays.java @@ -1249,6 +1249,14 @@ public static boolean isNullOrEmpty(Object[] array) return null == array || array.length < 1; } + public static boolean segmentsOverlap(int aOff, int aLen, int bOff, int bLen) + { + return aLen > 0 + && bLen > 0 + && aOff - bOff < bLen + && bOff - aOff < aLen; + } + public static void validateRange(byte[] buf, int from, int to) { if (buf == null) From 0b9e1413a3f4a529b1e98c30308ee574673f5ad9 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 29 May 2025 15:19:11 +0700 Subject: [PATCH 393/890] Add segmentsOverlap method to jdk1.4 Arrays class --- core/src/main/jdk1.4/org/bouncycastle/util/Arrays.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/jdk1.4/org/bouncycastle/util/Arrays.java b/core/src/main/jdk1.4/org/bouncycastle/util/Arrays.java index f5fabb74e0..5adfcad624 100644 --- a/core/src/main/jdk1.4/org/bouncycastle/util/Arrays.java +++ b/core/src/main/jdk1.4/org/bouncycastle/util/Arrays.java @@ -1145,6 +1145,14 @@ public static int hashCode(Object[] data) return hc; } + public static boolean segmentsOverlap(int aOff, int aLen, int bOff, int bLen) + { + return aLen > 0 + && bLen > 0 + && aOff - bOff < bLen + && bOff - aOff < aLen; + } + public static void validateSegment(byte[] buf, int off, int len) { if (buf == null) From e52d75ee145c4851b656ad1438733d6070460b29 Mon Sep 17 00:00:00 2001 From: Gefei Li Date: Thu, 29 May 2025 23:39:02 +0000 Subject: [PATCH 394/890] Pqc MLDSA rejection testvectors --- .../pqc/crypto/mldsa/HashMLDSASigner.java | 76 ++-- .../pqc/crypto/mldsa/MLDSAEngine.java | 53 ++- .../pqc/crypto/mldsa/MLDSASigner.java | 8 +- .../pqc/crypto/test/MLDSATest.java | 302 +++++++++++++++- .../pqc/jcajce/provider/test/MLDSATest.java | 332 +++++++++--------- 5 files changed, 516 insertions(+), 255 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java index 71ebaa56fc..a7cdbce059 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java @@ -4,14 +4,11 @@ import java.security.SecureRandom; import org.bouncycastle.asn1.ASN1Encoding; -import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Signer; -import org.bouncycastle.crypto.digests.SHA512Digest; -import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.crypto.params.ParametersWithContext; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.pqc.crypto.DigestUtils; @@ -27,7 +24,6 @@ public class HashMLDSASigner private MLDSAEngine engine; private Digest digest; - private byte[] digestOIDEncoding; public HashMLDSASigner() { @@ -67,7 +63,6 @@ public void init(boolean forSigning, CipherParameters param) parameters = privKey.getParameters(); engine = parameters.getEngine(random); - engine.initSign(privKey.tr, true, ctx); } else @@ -75,29 +70,21 @@ public void init(boolean forSigning, CipherParameters param) pubKey = (MLDSAPublicKeyParameters)param; privKey = null; random = null; - parameters = pubKey.getParameters(); engine = parameters.getEngine(null); - engine.initVerify(pubKey.rho, pubKey.t1, true, ctx); } - - initDigest(parameters); - } - - private void initDigest(MLDSAParameters parameters) - { - digest = createDigest(parameters); - - ASN1ObjectIdentifier oid = DigestUtils.getDigestOid(digest.getAlgorithmName()); + digest = engine.shake256Digest; + byte[] digestOIDEncoding; try { - digestOIDEncoding = oid.getEncoded(ASN1Encoding.DER); + digestOIDEncoding = DigestUtils.getDigestOid(digest.getAlgorithmName()).getEncoded(ASN1Encoding.DER); } catch (IOException e) { throw new IllegalStateException("oid encoding failed: " + e.getMessage()); } + digest.update(digestOIDEncoding, 0, digestOIDEncoding.length); } public void update(byte b) @@ -110,25 +97,22 @@ public void update(byte[] in, int off, int len) digest.update(in, off, len); } - public byte[] generateSignature() throws CryptoException, DataLengthException + public byte[] generateSignature() + throws CryptoException, DataLengthException { - SHAKEDigest msgDigest = finishPreHash(); - byte[] rnd = new byte[MLDSAEngine.RndBytes]; if (random != null) { random.nextBytes(rnd); } - byte[] mu = engine.generateMu(msgDigest); - - return engine.generateSignature(mu, msgDigest, privKey.rho, privKey.k, privKey.t0, privKey.s1, privKey.s2, rnd); + byte[] mu = engine.generateMu(engine.shake256Digest); + return engine.generateSignature(mu, engine.getShake256Digest(), privKey.rho, privKey.k, privKey.t0, privKey.s1, privKey.s2, rnd); } public boolean verifySignature(byte[] signature) { - SHAKEDigest msgDigest = finishPreHash(); - - return engine.verifyInternal(signature, signature.length, msgDigest, pubKey.rho, pubKey.t1); + byte[] mu = engine.generateMu(engine.shake256Digest); + return engine.verifyInternalMuSignature(mu, signature, signature.length, engine.getShake256Digest(), pubKey.rho, pubKey.t1); } /** @@ -139,20 +123,8 @@ public void reset() digest.reset(); } - private SHAKEDigest finishPreHash() - { - byte[] hash = new byte[digest.getDigestSize()]; - digest.doFinal(hash, 0); - - SHAKEDigest msgDigest = engine.getShake256Digest(); - // TODO It should be possible to include digestOIDEncoding in the memo'ed digest - msgDigest.update(digestOIDEncoding, 0, digestOIDEncoding.length); - msgDigest.update(hash, 0, hash.length); - return msgDigest; - } - // TODO: these are probably no longer correct and also need to be marked as protected -// protected byte[] internalGenerateSignature(byte[] message, byte[] random) +// protected byte[] internalGenerateSignature(byte[] message, SecureRandom random) // { // MLDSAEngine engine = privKey.getParameters().getEngine(random); // @@ -166,15 +138,19 @@ private SHAKEDigest finishPreHash() // return engine.verifyInternal(signature, signature.length, message, message.length, pubKey.rho, pubKey.t1); // } - private static Digest createDigest(MLDSAParameters parameters) - { - switch (parameters.getType()) - { - case MLDSAParameters.TYPE_PURE: - case MLDSAParameters.TYPE_SHA2_512: - return new SHA512Digest(); - default: - throw new IllegalArgumentException("unknown parameters type"); - } - } +// private static Digest createDigest(MLDSAParameters parameters) +// { + //TODO: MLDSA44 may use SHA2-256, SHA3-256, SHAKE128 + // MLDSA65 may use SHA3-384, SHA2-512 + // MLDSA44/65/87 may use SHA2-512, SHA3-512, SHAKE256 + +// switch (parameters.getType()) +// { +// case MLDSAParameters.TYPE_PURE: +// case MLDSAParameters.TYPE_SHA2_512: +// return new SHAKEDigest(256); +// default: +// throw new IllegalArgumentException("unknown parameters type"); +// } +// } } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAEngine.java index 540736af1d..f016d90dbc 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSAEngine.java @@ -2,20 +2,20 @@ import java.security.SecureRandom; +import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.util.Arrays; class MLDSAEngine { private final SecureRandom random; - - private final SHAKEDigest shake256Digest = new SHAKEDigest(256); + final SHAKEDigest shake256Digest = new SHAKEDigest(256); public final static int DilithiumN = 256; public final static int DilithiumQ = 8380417; public final static int DilithiumQinv = 58728449; // q^(-1) mod 2^32 public final static int DilithiumD = 13; - public final static int DilithiumRootOfUnity = 1753; + //public final static int DilithiumRootOfUnity = 1753; public final static int SeedBytes = 32; public final static int CrhBytes = 64; public final static int RndBytes = 32; @@ -55,10 +55,10 @@ protected Symmetric GetSymmetric() return symmetric; } - int getDilithiumPolyVecHPackedBytes() - { - return DilithiumPolyVecHPackedBytes; - } +// int getDilithiumPolyVecHPackedBytes() +// { +// return DilithiumPolyVecHPackedBytes; +// } int getDilithiumPolyZPackedBytes() { @@ -75,10 +75,10 @@ int getDilithiumPolyEtaPackedBytes() return DilithiumPolyEtaPackedBytes; } - int getDilithiumMode() - { - return DilithiumMode; - } +// int getDilithiumMode() +// { +// return DilithiumMode; +// } int getDilithiumK() { @@ -130,15 +130,15 @@ int getCryptoPublicKeyBytes() return CryptoPublicKeyBytes; } - int getCryptoSecretKeyBytes() - { - return CryptoSecretKeyBytes; - } - - int getCryptoBytes() - { - return CryptoBytes; - } +// int getCryptoSecretKeyBytes() +// { +// return CryptoSecretKeyBytes; +// } +// +// int getCryptoBytes() +// { +// return CryptoBytes; +// } int getPolyUniformGamma1NBlocks() { @@ -357,12 +357,7 @@ SHAKEDigest getShake256Digest() void initSign(byte[] tr, boolean isPreHash, byte[] ctx) { shake256Digest.update(tr, 0, TrBytes); - if (ctx != null) - { - shake256Digest.update(isPreHash ? (byte)1 : (byte)0); - shake256Digest.update((byte)ctx.length); - shake256Digest.update(ctx, 0, ctx.length); - } + absorbCtx(isPreHash, ctx); } void initVerify(byte[] rho, byte[] encT1, boolean isPreHash, byte[] ctx) @@ -374,6 +369,11 @@ void initVerify(byte[] rho, byte[] encT1, boolean isPreHash, byte[] ctx) shake256Digest.doFinal(mu, 0, TrBytes); shake256Digest.update(mu, 0, TrBytes); + absorbCtx(isPreHash, ctx); + } + + void absorbCtx(boolean isPreHash, byte[] ctx) + { if (ctx != null) { shake256Digest.update(isPreHash ? (byte)1 : (byte)0); @@ -396,7 +396,6 @@ byte[] generateMu(SHAKEDigest shake256Digest) byte[] mu = new byte[CrhBytes]; shake256Digest.doFinal(mu, 0, CrhBytes); - return mu; } diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSASigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSASigner.java index 35a4d0f7de..521a9aede2 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSASigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/MLDSASigner.java @@ -14,11 +14,9 @@ public class MLDSASigner implements Signer { private static final byte[] EMPTY_CONTEXT = new byte[0]; - private MLDSAPublicKeyParameters pubKey; private MLDSAPrivateKeyParameters privKey; private SecureRandom random; - private MLDSAEngine engine; private SHAKEDigest msgDigest; @@ -148,7 +146,7 @@ public boolean verifyMu(byte[] mu) { throw new DataLengthException("mu value must be " + MLDSAEngine.CrhBytes + " bytes"); } - + boolean isTrue = engine.verifyInternalMu(mu); reset(); @@ -171,9 +169,9 @@ public boolean verifyMuSignature(byte[] mu, byte[] signature) { throw new DataLengthException("mu value must be " + MLDSAEngine.CrhBytes + " bytes"); } - + msgDigest.reset(); - + boolean isTrue = engine.verifyInternalMuSignature(mu, signature, signature.length, msgDigest, pubKey.rho, pubKey.t1); reset(); diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java index f177d5b219..ac3cafd6be 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java @@ -5,12 +5,18 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.security.SecureRandom; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import junit.framework.TestCase; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.pqc.crypto.mldsa.HashMLDSASigner; import org.bouncycastle.pqc.crypto.mldsa.MLDSAKeyGenerationParameters; import org.bouncycastle.pqc.crypto.mldsa.MLDSAKeyPairGenerator; import org.bouncycastle.pqc.crypto.mldsa.MLDSAParameters; @@ -39,13 +45,14 @@ public class MLDSATest }; private static final MLDSAParameters[] PARAMETER_SETS = new MLDSAParameters[] - { - MLDSAParameters.ml_dsa_44, - MLDSAParameters.ml_dsa_65, - MLDSAParameters.ml_dsa_87, - }; + { + MLDSAParameters.ml_dsa_44, + MLDSAParameters.ml_dsa_65, + MLDSAParameters.ml_dsa_87, + }; - public void testConsistency() throws Exception + public void testConsistency() + throws Exception { SecureRandom random = new SecureRandom(); @@ -462,7 +469,288 @@ public void testSigVerCombinedVectorSet() } } - private class InternalMLDSASigner + public void testMLDSARejection() + throws Exception + { + rejectionExternalMuTest(MLDSAParameters.ml_dsa_44, "dilithium_external_mu_rejection_vectors_44.h"); + rejectionExternalMuTest(MLDSAParameters.ml_dsa_65, "dilithium_external_mu_rejection_vectors_65.h"); + rejectionExternalMuTest(MLDSAParameters.ml_dsa_87, "dilithium_external_mu_rejection_vectors_87.h"); + rejectionPrehashTest(MLDSAParameters.ml_dsa_44, "dilithium_prehash_rejection_vectors_44.h"); + rejectionPrehashTest(MLDSAParameters.ml_dsa_65, "dilithium_prehash_rejection_vectors_65.h"); + rejectionPrehashTest(MLDSAParameters.ml_dsa_87, "dilithium_prehash_rejection_vectors_87.h"); + rejectionTest(MLDSAParameters.ml_dsa_44, "dilithium_pure_rejection_vectors_44.h"); + rejectionTest(MLDSAParameters.ml_dsa_65, "dilithium_pure_rejection_vectors_65.h"); + rejectionTest(MLDSAParameters.ml_dsa_87, "dilithium_pure_rejection_vectors_87.h"); + rejectionUpStreamTest(MLDSAParameters.ml_dsa_44, "dilithium_rejection_upstream_vectors_44.h"); + rejectionUpStreamTest(MLDSAParameters.ml_dsa_65, "dilithium_rejection_upstream_vectors_65.h"); + rejectionUpStreamTest(MLDSAParameters.ml_dsa_87, "dilithium_rejection_upstream_vectors_87.h"); + rejectionUpStreamTest(MLDSAParameters.ml_dsa_44, "dilithium_rejection_vectors_44.h"); + rejectionUpStreamTest(MLDSAParameters.ml_dsa_65, "dilithium_rejection_vectors_65.h"); + rejectionUpStreamTest(MLDSAParameters.ml_dsa_87, "dilithium_rejection_vectors_87.h"); + } + + private interface RejectionOperation + { + byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) + throws CryptoException; + boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig); + } + + private void rejectionTest(MLDSAParameters parameters, String filename, RejectionOperation operation) + throws Exception + { + List testVectors = parseTestVectors(TestResourceFinder.findTestResource("pqc/crypto/mldsa", filename)); + for (int i = 0; i < testVectors.size(); ++i) + { + TestVector t = testVectors.get(i); + FixedSecureRandom random = new FixedSecureRandom(t.seed); + + MLDSAKeyPairGenerator kpGen = new MLDSAKeyPairGenerator(); + kpGen.init(new MLDSAKeyGenerationParameters(random, parameters)); + + // + // Generate keys and test. + // + AsymmetricCipherKeyPair kp = kpGen.generateKeyPair(); + + MLDSAPublicKeyParameters pubParams = (MLDSAPublicKeyParameters)PublicKeyFactory.createKey( + SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(kp.getPublic())); + MLDSAPrivateKeyParameters privParams = (MLDSAPrivateKeyParameters)PrivateKeyFactory.createKey( + PrivateKeyInfoFactory.createPrivateKeyInfo(kp.getPrivate())); + + if (t.pk.length != 0) + { + assertTrue(Arrays.areEqual(t.pk, pubParams.getEncoded())); + } + if (t.sk.length != 0) + { + assertTrue(Arrays.areEqual(t.sk, privParams.getEncoded())); + } + byte[] signature = operation.processSign(privParams, t.msg); + if (t.sig.length != 0) + { + assertTrue(Arrays.areEqual(t.sig, signature)); + } + boolean shouldVerify = operation.processVerify(pubParams, t.msg, signature); + assertTrue(shouldVerify); + } + } + + private void rejectionExternalMuTest(MLDSAParameters parameters, String filename) + throws Exception + { + rejectionTest(parameters, filename, new RejectionOperation() + { + @Override + public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) + throws CryptoException + { + InternalMLDSASigner signer = new InternalMLDSASigner(); + signer.init(true, privParams); + return signer.generateMuSignature(msg); + } + + @Override + public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) + { + InternalMLDSASigner signer = new InternalMLDSASigner(); + signer.init(false, pubParams); + return signer.verifyMuSignature(msg, sig); + } + }); + } + + private void rejectionPrehashTest(MLDSAParameters parameters, String filename) + throws Exception + { + rejectionTest(parameters, filename, new RejectionOperation() + { + @Override + public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) + throws CryptoException + { + HashMLDSASigner signer = new HashMLDSASigner(); + signer.init(true, privParams); + signer.update(msg, 0, msg.length); + return signer.generateSignature(); + } + + @Override + public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) + { + HashMLDSASigner signer = new HashMLDSASigner(); + signer.init(false, pubParams); + signer.update(msg, 0, msg.length); + return signer.verifySignature(sig); + } + }); + } + + private void rejectionTest(MLDSAParameters parameters, String filename) + throws Exception + { + rejectionTest(parameters, filename, new RejectionOperation() + { + @Override + public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) + throws CryptoException + { + InternalMLDSASigner signer = new InternalMLDSASigner(); + + signer.init(true, privParams); + signer.update(msg, 0, msg.length); + return signer.generateSignature(); + } + + @Override + public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) + { + InternalMLDSASigner signer = new InternalMLDSASigner(); + signer.init(false, pubParams); + signer.update(msg, 0, msg.length); + return signer.verifySignature(sig); + } + }); + } + + private void rejectionUpStreamTest(MLDSAParameters parameters, String filename) + throws Exception + { + rejectionTest(parameters, filename, new RejectionOperation() + { + @Override + public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) + throws CryptoException + { + InternalMLDSASigner signer = new InternalMLDSASigner(); + signer.init(true, privParams); + return signer.internalGenerateSignature(msg, new byte[32]); + } + + @Override + public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) + { + InternalMLDSASigner signer = new InternalMLDSASigner(); + signer.init(false, pubParams); + signer.update(msg, 0, msg.length); + return signer.internalVerifySignature(msg, sig); + } + }); + } + + private static List parseTestVectors(InputStream src) + throws IOException + { + List vectors = new ArrayList<>(); + BufferedReader bin = new BufferedReader(new InputStreamReader(src)); + + TestVector currentVector = null; + String currentField = null; + List currentBytes = null; + Pattern fieldPattern = Pattern.compile("\\.(seed|pk|sk|msg|sig|key_hash|sig_hash)\\s*=\\s*\\{"); + Pattern hexPattern = Pattern.compile("0x([0-9a-fA-F]{2})"); + + String line; + while ((line = bin.readLine()) != null) + { + // Skip comments and empty lines + line = line.split("//")[0].trim(); + if (line.isEmpty()) + { + continue; + } + + // Look for test vector array start + if (line.contains("dilithium_rejection_testvectors[] = ")) + { + continue; + } + + // Start new test vector + if (line.startsWith("{") && currentVector == null) + { + currentVector = new TestVector(); + continue; + } + + // Detect field start + Matcher fieldMatcher = fieldPattern.matcher(line); + if (fieldMatcher.find()) + { + currentField = fieldMatcher.group(1); + currentBytes = new ArrayList<>(); + line = line.substring(fieldMatcher.end()).trim(); + } + + // Collect hex values if in field + if (currentField != null) + { + Matcher hexMatcher = hexPattern.matcher(line); + while (hexMatcher.find()) + { + String hex = hexMatcher.group(1); + currentBytes.add((byte)Integer.parseInt(hex, 16)); + } + + // Check for field end + if (line.contains("},")) + { + setField(currentVector, currentField, currentBytes); + currentField = null; + currentBytes = null; + } + continue; + } + + // End of test vector + if (line.startsWith("},") && currentVector != null) + { + vectors.add(currentVector); + currentVector = null; + } + } + + return vectors; + } + + private static void setField(TestVector vector, String field, List bytes) + { + byte[] byteArray = new byte[bytes.size()]; + for (int i = 0; i < bytes.size(); i++) + { + byteArray[i] = bytes.get(i); + } + + switch (field) + { + case "seed": + vector.seed = byteArray; + break; + case "pk": + vector.pk = byteArray; + break; + case "sk": + vector.sk = byteArray; + break; + case "msg": + vector.msg = byteArray; + break; + case "sig": + vector.sig = byteArray; + break; + } + } + + static class TestVector + { + byte[] seed = new byte[0]; + byte[] pk = new byte[0]; + byte[] sk = new byte[0]; + byte[] msg = new byte[0]; + byte[] sig = new byte[0]; + } + + private static class InternalMLDSASigner extends MLDSASigner { public byte[] internalGenerateSignature(byte[] message, byte[] rnd) diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java index e1f5d1e222..20228acafb 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/MLDSATest.java @@ -742,172 +742,172 @@ public void testMLDSAKATSigWithContext() assertTrue(Arrays.areEqual(Strings.toByteArray("Hello, world!"), vspec.getContext())); } - public void testHashMLDSAKATSig() - throws Exception - { - byte[] pubK = Hex.decode("dc7bc9a2e0b6dc66823ae4fbde971c0cfc46f9d96bbfbeebb3470ae0a5a0139fdd6a6ce5bc76e94faa9e9250abd4cee02cf1ee46a8e99ce12d7395781fa7519021273da3365519724efbe279add6c35f92c9d42b032832f1bf29ebbecd3ec87a3af3da33c611f7f35fa35acab174024f118979e23bf2fe069269a2ec45fbc1b9c1fb0e1f05486a6a833eb48adc2960641d9af6eb8b7381b1ec55d889f26b084ddfa1c9ed9b962d342694cede83825309d9db6bd6ba7582132534861e44a04388a694242411761d34e7c085d282b723c65948a2ac764d9702bd8ed7fe9931d7d8704a39e6508844f3f84843c305594fe6e5404e08f18ed039ac6563cbaa34b0ca38320299d6256ec0f78d421f088159d49dc439cbc539a55884a3eb4efc9cf190b42f713441cb97004245d41437a39b7b77fc602fbbfd619a42363714b265173cae68fd8a1b3ca2bd30ae60c53e5604577a4a3b1f1506e697c37432dbd883553aac8d382a3d250cf5b29e4d1be2cbcd531ff0e07e89c1f7dbc8d4529aeebe55b5ce4d0214bfdec69e080bd3ef36cca6a54933f1ef2f37867c0d38fd5865b87929115808c7e2595458e993bacc6c5a3b9f5025001e9b41447708bfbaa0462efa63876c42f769908b432f5485508a393224960551d77eadfaf4411cbc49fdff46f2f155ddd6ec30867905b709888ca0f30f935fb8d7f4803cfc7a5f7790ca181d99ca21f2621d69a5c6d49c76b4969da62740a378470332b30947ab31ccdb9ba0c7b625879eec4bd81f0200ba23504a7dc3b118bc2ab1145df13af3c8cc39f577873b84911b3d85fbbf4cb19e4d36b10a938eeb78b599dc86615fd6cec6eb7b8f7afa5f6d6be19ea81630d36ccfb2f487de50d0cf46da8d3fe3512812043c0e3ef2d7231fb0b0a35a0fb283be30a1247780f30ae0294e8b6f5897383edb895595f577524df54593cdf927b4967616ee3913e4d6b29b0dbd7c33a2a45e4ef1b1954ea5d91ce37efc1302e7ce02a97395565da2a5c5d3fdb0d87684e9b1c0ad07ec33df2dfad528e2ea0966d2a47dd5ee88e77d653c0d004fab0165f0757c4da40af327e7192536c79947a80a827aa2107dacfae3debfc8fad3d6e08076d938c510a276bdf6721a1f087cb169515028ad5ce27a1047abd92809934ca63b893f71f9a34a99c0fd30310c47e9aa37394d0ab73b254d3ca69d9c5549c9479aae24264ac5ea64d3fd821c3962ec77e709f9d30bc7b65a52e48c16e80603558caca1811411c3155d1f949fc9cf9aa9385a7199e99be77a66fad7eed91258de55b2c4c83f9a050adebea5f09758f40dac4a1c394ee8d687879150d26426895ab1938e14ae11b376254c91fc6130436996f8ed43bd27be20ec9067111c116ec94cc2b06cc91a13c5d10bbd7eecea4792f17b2b77631ef145e9fb41a83eaa11c2b72a48fb90fdbd88644c4edf8ab20dce3118364b276ac1237b36c8926e346aab5a111aa0bf341c518b7bff9e9dbb8bcb4728601b3760663e67650331e6fb54ac82fc414cb8ddfc160a25311ec5272de46217fef8b992ff89754fbee351f21bb90b6c97078b510c983350681266c8fed1f0583c5151e7b8fe3b7292319699687cc6b641fdbd689428543bc0fa1facc109de65b62784c2d985ab15d77d3af12af6d03e8d1859a553688584d75ef673a1de74093ee108c761fff32c217c231b0e2953daf521429264c0963bc8a5cdeddc617a7285b934ea51ddb5cdab23bcede86be36e001bc65c65e9a1c94baff4fab8eb5f8ed42ec377423633fe00049142467c47c5d58a7202c8e9104841c1f7f380145a6a0a828c570235e507ae5868a6062f722bb98ff6be"); - byte[] privK = Hex.decode("dc7bc9a2e0b6dc66823ae4fbde971c0cfc46f9d96bbfbeebb3470ae0a5a0139ff037b84e75537e0a1cf02a517acfe323ffffe11df72e4f38430e0e66a2654b2f2ef757da47649d9f63fa03f1bf6fe6bc7c62971a98a2bd9d36eb0ec43ad4e9d940df3bb5874f5c92192aa31e0535d3cf70950bba858d11a688eaf854f63ecfc520c50d624891434265d8b0680c03061040299a104082c0910c8508d1100d44a6509408292211125b90508a2688e1302dc4021280028ac302611820851237808a000ae2040421b4910bb80550a08051b2511c28428a3672a494504910201bb45161424424a75001328181942d62a850023449ca94200b296213156408924c48122100b605030208e0060200a311e1802021116483a62898029291480801083041066613200e5b360951400c53000aa08851944842e316704ab2089b92440025121b0309418209c2a0800b290a819851c4340da4424500a0105b048e603400138928a4422648002c90202d194068e2146d19278a083746e4146914006422c660d3a03013242844965014166da0284dcc462e94367100232e1c114909a2040131060a2172c2142ada000c5a260d13228a62c444e3142d013445980224d33841c0308121a621e348720b1984d2c89108b8690887714a2884d496451a9301ca2285da30859ac851dcc00820106060465262302aa224251044640b2842988011540692144251d236719bb4900b082890188e41c469e1a469032160e01409d3020c20c88c1cb23164086218476920228ccb8470089528029550533270013405888424541041d202881aa84ccac88181008d0392899ab809d9900c9a1290614065c9322d89860c123521cc4266c8360010062411028ea3b44d44023043a0285a002ed1980c4882658922441c010212907084226e12134d011902519064113364c91806c2c04589262908b63024308cda022e0c27250b367058162c5116420b4946c1208841246c99466a04434e18a86c821661922028639409c30211029520211782d43868003460c84688e0160000a32dc0a82824b640831464c81022a2086503234ac8122ea098418c2072cc308a62c665093408412682da429089328514967081226001176d5948428ab88d592051d80892e2c0889044700ac0245a020904218a59c45094441094140820460209270c441020dcc8209212015038250c456e4a1666223770dc808ca426412222441ba3618a343099844099c42952046d88146ccb242a7cd129a8d333115c62d033b6a8357cf7cd10268ab12f16fceb7975d0a28a6c4822213c9a772df084ad91a669e2040550fc5e8d0aeb10fab2375fc9625ef9cd48c19631997a1cb6455d2c6286c569c9637add0317ce990996b28e51c3f3f717fb5907bbdd53961ad3497f2c3c473cce170906ac4c624a89aa8fbe624d99385e9c9548bf05e8cafd47d2476e41b73001f813726499e88b2b3b6f596ca311657850346598994c40e34747161e4e76264deef2a3019389d1594c942301af47b7544c23ecda2df2dece81e487d8f3f58ea89cd811d7275807ff1b0369ba86470088c174a3099fdafbe5fbb4d158801053b2b435d54059e26dee76d10a7a372f06b0b88b985b32f52052387438be8dc8bc6ae7369e2da9aa5e2585f8de403d091ccb7f790d54ddb34c608b0876f2825e9113be20a2b85867a01bda53287ac780bcd8b606d2e6d7712c56ce0142d22fe6b786de544963e134fecedfafb83d763061d799096a59e30d4472e440ae1faaabdf42640ce69740ceb9cae1a9612c21931b74af3f780236123321b205b6efd6cbb134f4c73d63c0c13e660b59d5920bc33197c355853d8d1cddc7959f7bc500ac81d985016f5b89a0eec79b0d9364ead8e38577c2a6549f2d067cb09438fdb21220aec80f6e22a476f332a2a4a0b7acbeb9e078d2b5a92ae84c924f7cb19fc7df377beb6546af97aa985c747cd111a127a674b4c26d89c14485b82e3a498a12d05406febd6c4d4b8bc051ab2cb91224b078538374b794b7dd9ddf3ac2b4a671fb7b9cf5acb78622ae2709eb2db16943aa24a9c97a81077bc784d25c0ea5991d2de883798a1f0e78f3361ed6a10dded81b1d683658331534fd7c01bc0eb00dfc4c3c84f0693046ff806bb200dd7bd4c0e6abca3f2934b4814fc0e1f8be615a2dda7c8a8d06cf9ce8566b40f4a6543b25bacddc926863fc0fa2007d6d7bf6d18dc98df696bd0865bf0be4c492b8043a32def8e3595ba7da345252f38f95be10fd7fb899b498fa01b09de5d5608eabc44a721aa04c4ef1dcb86102ac5f5f79c9708dcf5c5e896edd8c2c7bde3fa83e6ffce22d66174e31657a0b6361585e669d3031952f08631ae1f16ff90b90d0aad3c6d7e1dd0a9c41ab00a6e1c4f96af9ac5b79fcf821ffc016cb059245fb78dbe6c633d965aaab5333be07195c4b74b18e4600ce783c0a914ef4281016e80a7c9aa92d0fd789879c5e6751125ecb154432311e41cebd4fab3a31e4d2ce22d0f8c67737bf8a0dd85fe1349d5079a4d5feb3fee9378ca47ae46cc58a3f02038cfd53c4cee9cc4270cebc3d115a39c831e8ed41c4dbe4051b51d7872ba0c2bb163e0085201188eaa624a6bea9400a3a1fcc355a57f15704e61fda55a5dbaea8448fa5cb2d377a07f58305ad107e844ab4806e5bf99c1f513ee1d0a2acc04549f0801742169a77971d0adbfbfe0dd2ee5d16bc461e35748d1f3f6f4598321e8c49e79e740f990359858d2729dde007fcb26fdda9aa6e2ec4bd736f2836e7e4c83440191c849f6a53c72a4f8f830d001ea3b18f3cb4a5bd3cf066032b4932cfd2e62a9b55723fa61c688c935518af6860cd649bfbf1bf5fdc1f36dcaefaa157438d1cc8d56a150161511df82631f5e88e773e4ce263f276b7b3678d4c6fc75311d411c0d01bfdb595bb70552838e1b86517c837d909e772b428599e1fe569f77ce61531fde6fd31cdce1bdee4ba467fcbfbb9feeaad99fef67d4906e036c73662ddce158d4e5d4635e5d366f79f31a19d1b3dc4a591b0df194bb06c18147f41d88d1a409becdfb67eb063d16312266fd51b521ba9115e2e5e2aeae6ec511cede13ed4132ffbe0273f6c7039b3874f058804a54809af60557a21d9b4b831d04156a7c22dcbcdfe14f62437f449cb5ef12bf4251d485496cd835c0c2bc58bd845963dfa76ecd68519c4bdaf110be7ab052876dc3407591568c956ea3bf107c90fd5853a292f59a8d4b58b5d3fddf29bdbeac36852e3c69766fe460176a801831292b8e88a74a01ecbbe09a7b4d74cfd7fd628841944d9d556dbd60c76f96f07dc53443805ee9aa09365de4fb8179252c6b099b5dd351fdefc23dbd8090596c5d208ffd2c5661d8e5612dd574fc69045c769a969e600d77cfe192f1d3ae911289355c585811491b0ccd73692ab158824ab9edf8ac8193f0b33e6138b72c6dcd5d344f807b3da92425037de5ea4eead1c795effaa145e2ecdd327606eb2609929b9474b2bb04653602555c068385e92f06f29ca613ce5b4404f01ab1805db0acaa890330d291f40692df382509302b6dc8668f2c8f2d3a44fd58dca26e9802794f73d25b3149e6d576441"); - byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8"); - byte[] s = Hex.decode("eea01acc8fe6631c9260996def0c75d84998cf4512c7b67bc4246bbfbff165a404d02bca30da1c2b61d91d7986a467fd4b3a1131077ac138b4011ef07fc1f3ca6237e7fc6550c7e071c939f4554f265362eb26158ee1a2e57c72d20beb78168d9e2d7faf54763755a44bdc29e575884133062453a9e3e761a13a82143d7db7215ab0b8628c096e0f298491a8d67517ab8a90be7db5d311eddc7e883d50eb873decd9b3e420155008c0a3632d51f93a9c67ea336273c8f26617c7cfa2a3aa6d9aa075e75fc60fb587fb5522cb6bac4ef1979a069a15aed3171660ce5da2c27af188e9d3bc62eebdbd798f1a650c7d46411e3cabeb27be3ae787b5f4d82a2e2cf4eee84b4fc82edea4533a47bf7d3791933d1c5ce19c1792a6420c9dcd540dc3a494d9b518ce5f4f85d7747c2fa45d812d16f089682c57a96061210c942dbaca8dbde509f56f498d68f0b4e6f4a51373c9f5a5db2e20ec8254ea9d59f69d732fd1b1149fbd0b608368086a3b6acbe5de156fc508390ddc9ca28d201fdc67140a767b2d99d08051255e73d423750e91b1aa31982cc7014e46c75b69ec972f15745bafbe4878f73d8800d80ad22716f15f4d964f0468e8583add1a4ba8f544e9b83dd6c5ebf744436e254bd699e8909e712d9f6e69fbda39be69a4e0bb58cdd0ddbd369cf5a6475b1ad5fe2849c439611516bd13de14b34b26a731c5a5bde4fae538782a6daeb6fbdaf4cf1af40f3b9c7896e87ac71b3fc95eea02f2f28bf62b9d613998ad973added64515a699fc89304b8fe4d4c97759662b720835c2b28585648e54dedfbfa40fd1455e8a945a390765e3b1a2588286bd4f2995eca39ce9eca6d2e32d92e18c930e4b99109f148db96f1763703aeb431c3c815d577c2b0282181932bb182325bf45e1355d91c8ec669ccd94429dc4ea0abc562988bd2b27b39f2dd0e4ace6fc9148cd064005e9cf0f8105a6534261942774c2a02f150f8d250822745b1cc40e1d57c68dea152c0f5088712266e5a37eb7760204c2747561688990d3cf7c76660e5671727c7ebde56420c91549a48b3763062ff92a4679e7d3e8569303eeff650c0ae606234d70a350aa9862b912825b13c1ec7bd6bf346717b0f30c45c1925873f04d469fd28f82f19375531ffb83851c471ebf623c86130d929e739bd8721d97ca9a83676f26c0a75493b5b02f0921aea91baae2da96532ab9db04dc997d5f800f58a891f6ef26e5478de1ce9b4da04eccac4cee81de5c3b1010ef28242217ec737beb36815c4af1de9a4160180ea896120ce96869bf551bb6be079482f4ab5c0f7f234c50bb4139ff9fba1b594c85cc3780434fc00f7d0492cfc86c0d1889784b113650e3c29bb2e9ed6f94df5ea42afac8060856fb90fab5f4c6fb6875fd67e0438335bcd5199b72706cf358492e5f4945bf2a686aa2861908d6a71ba4e760c87e75a7ff31169993cf048817512aeac4e960771879be541b1adcd6c2da9f9d2153e728d4ee1e91acd7f704e0b472856cfd8f85e2f30b0f6e427f190dbc1079343fdc71f9ac0b8aa5e6fdfd60fe9c90e4bebd4a91dbf16378bcc2a330aeed5b7e8dd617fbf3c6ed9b2c2ac82f2e9d03c975b2a832a667864090da9ca91ad1af97278f6cfebeaf2cc681957f3d75d17a5710de85444b636cf7e0dbac06852fe669f87bc7c430ff548ee0b34f1a68e86f1b26290f8a056c0bccd18a540ce04ee7fdee47f5176b811021a89d170e71250a040e10f8f900236617c6be92217b77aa00854d6376739d5a63d32a377da20c368092367fc6afb62e0b898c01462a399aa3dba4beb0d03d7f8a2e84b499a41e7a6e50cd5e09b4d6d8cb3e8ee3a2d1b50775f9caf7335f1ea15b2312352831418ce2529542011a19c6ece5c1e6dfe7e37821933594ca6f55ca6c799b32f88b21d59744c10538ea7208eb61a04d24476c326068ac9ad4199080e02b95766f6a18738c7e4506d18e9c5c526ef4f28ef14662667dc865ffd446026cefca1b39be77cdfd7773aac0bf570647c21979b9f0ff0d67f2a9940e1e1c27e9e3296c7d890c5627e9126fa09bed1f242aa23112d828529ce431939c9ecc0d4311742a1fa5f9f283eb0135093d3e6aa9e89d4641415678f0b2ecb1210a611d062c5e17f01521aa45d778e2a0770cfda540ce1b5bbb3fabfc783601480b080f4e7275e69705b6cff043a3b503da77fbdc05702b790b1cf4dccfb6b2df00bf0ee896420175e1293a6b8fbc96cf9759a6c0e56067dc9e2522621af2cf830e2bb648f0ca3560bdf9c2c0c01bb23806455bc40889472398f9daf71f0ab9e1aab2fe8d6c8c504a1a45d99229828a9588a559a7172b041b2bbcdbe2e59b749b3e1219abf51a39164c9fac17bd8c83eab1e0e04e029550b689134194d486083b956706a6706274324d63ff79c35f37cf8c2932910e60e1da7cc6c4d9b966fb11437f7d4e94221e4b9ce51ea04325e4b75e45dbbbaadeeadd432e776c9b14cff55529c24b43211b52d1f27de0d86c0f253ec3c2262fdfb1ecb18442174321bbed4c1454522d747be4f53f9ea445d4db360c5c0ddbac51179c7f6016249bcadcce47d4abaf0604047d806f556d11aba411239a32a80d4403c72198036917b6c3b9c5fefcbb73b4d11cea0cc09b419ab8bb0a093131b4c32280fac586bee305e14a18432bbaf1ad6ffc67863edd564df4f9518c6363b80d83c59440bde98c2eeed939bfa1d8f720a805088e2090ab1af71ad5190978b2e23a7d58fc28ab2ed623f2cc65d67629a2fbbe84309a3e0447f3805728155cca522217f9e66b5b2d794fec7131b091f7f77df37e8d726f150d416018941a7b48617fe291a3a3594df80bb1927d87d8f8d5cb945c39c977e7a4f9882e3facd6f26e42390a1f7d14e55797bd22ae78ac11208084be07399d2f9cd2fa331784c5e65de766d87c3d19aad2c7993485e65f11b2c03533f265d9268f8d7f9ce14f97a76891e2b764d2e0a7baf2f81c6fcfd15c78552bba4952fe375c9872a25fddfa19a29695320858fa8a910c29d0739edff01da6d80f757906f7103984c4910c44220ce83fb5b46527c918d186b5c096ea4d1c85df71b4e7d625bb2df5a898880a18238eb4388f66f0d5daf074bdfc6e3b4695ef5faaf754ed764b80463d724d1fc41b598861207d1971cfe1e857cf2bf8dcc4afae1e44c622d96194d3f85fa5a37aed9a154074fbc54d50724658678dfba30bce2fc853bf87f7379d80865f08f0a772afedd8f45808b49605ff3d2875a6ce7b90f4a61fc55734f791bf2e6ed554309111a385158627d7e828491a2b7c6d1f00315162c42435b62656f89bbbcbfe1e6e8f0f7f9000711123b5b6ea7b0b7b8c9d2e5e9f5fc09727a9b9e9fb3d3d6e9ebf4000000000000000000000000000011253642"); - byte[] seed = Hex.decode("7c9935a0b07694aa0c6d10e4db6b1add2fd81a25ccb148032dcd739936737f2d"); - - KeyPairGenerator kpg = KeyPairGenerator.getInstance("HASH-ML-DSA", "BC"); - SecureRandom katRandom = new NISTSecureRandom(Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"), null); - - kpg.initialize(MLDSAParameterSpec.ml_dsa_44, katRandom); - - KeyPair kp = kpg.generateKeyPair(); - - SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfo.getInstance(kp.getPublic().getEncoded()); - - ASN1BitString pubSeq = pubInfo.getPublicKeyData(); - - assertTrue(Arrays.areEqual(pubSeq.getOctets(), pubK)); - - PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp.getPrivate().getEncoded()); - ASN1OctetString seq = privInfo.getPrivateKey(); - - assertTrue(Arrays.areEqual(ASN1OctetString.getInstance(ASN1Sequence.getInstance(seq.getOctets()).getObjectAt(0)).getOctets(), seed)); - - Signature sig = Signature.getInstance("HASH-ML-DSA", "BC"); - - sig.initSign(kp.getPrivate()); - - sig.update(msg, 0, msg.length); - - byte[] genS = sig.sign(); - - assertTrue(Arrays.areEqual(s, genS)); - - sig = Signature.getInstance("HASH-ML-DSA", "BC"); - - sig.initVerify(kp.getPublic()); - - sig.update(msg, 0, msg.length); - - assertTrue(sig.verify(s)); - - // check randomisation - - sig.initSign(kp.getPrivate(), new SecureRandom()); - - sig.update(msg, 0, msg.length); - - genS = sig.sign(); - - assertFalse(Arrays.areEqual(s, genS)); - - sig = Signature.getInstance("HASH-ML-DSA", "BC"); - - sig.initVerify(kp.getPublic()); - - sig.update(msg, 0, msg.length); - - assertTrue(sig.verify(genS)); - - AlgorithmParameters algP = sig.getParameters(); - - assertTrue(null == algP); - - // test using ml-dsa-44 for the key, should be the same. - - kpg = KeyPairGenerator.getInstance("ML-DSA", "BC"); - katRandom = new NISTSecureRandom(Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"), null); - - kpg.initialize(MLDSAParameterSpec.ml_dsa_44_with_sha512, katRandom); - - kp = kpg.generateKeyPair(); - - sig = Signature.getInstance("HASH-ML-DSA", "BC"); - - sig.initSign(kp.getPrivate()); - - sig.update(msg, 0, msg.length); - - genS = sig.sign(); - - assertTrue(Arrays.areEqual(s, genS)); - - sig = Signature.getInstance("HASH-ML-DSA", "BC"); - - sig.initVerify(kp.getPublic()); - - sig.update(msg, 0, msg.length); - - assertTrue(sig.verify(s)); - } - - public void testHashMLDSAKATSigWithContext() - throws Exception - { - byte[] pubK = Hex.decode("dc7bc9a2e0b6dc66823ae4fbde971c0cfc46f9d96bbfbeebb3470ae0a5a0139fdd6a6ce5bc76e94faa9e9250abd4cee02cf1ee46a8e99ce12d7395781fa7519021273da3365519724efbe279add6c35f92c9d42b032832f1bf29ebbecd3ec87a3af3da33c611f7f35fa35acab174024f118979e23bf2fe069269a2ec45fbc1b9c1fb0e1f05486a6a833eb48adc2960641d9af6eb8b7381b1ec55d889f26b084ddfa1c9ed9b962d342694cede83825309d9db6bd6ba7582132534861e44a04388a694242411761d34e7c085d282b723c65948a2ac764d9702bd8ed7fe9931d7d8704a39e6508844f3f84843c305594fe6e5404e08f18ed039ac6563cbaa34b0ca38320299d6256ec0f78d421f088159d49dc439cbc539a55884a3eb4efc9cf190b42f713441cb97004245d41437a39b7b77fc602fbbfd619a42363714b265173cae68fd8a1b3ca2bd30ae60c53e5604577a4a3b1f1506e697c37432dbd883553aac8d382a3d250cf5b29e4d1be2cbcd531ff0e07e89c1f7dbc8d4529aeebe55b5ce4d0214bfdec69e080bd3ef36cca6a54933f1ef2f37867c0d38fd5865b87929115808c7e2595458e993bacc6c5a3b9f5025001e9b41447708bfbaa0462efa63876c42f769908b432f5485508a393224960551d77eadfaf4411cbc49fdff46f2f155ddd6ec30867905b709888ca0f30f935fb8d7f4803cfc7a5f7790ca181d99ca21f2621d69a5c6d49c76b4969da62740a378470332b30947ab31ccdb9ba0c7b625879eec4bd81f0200ba23504a7dc3b118bc2ab1145df13af3c8cc39f577873b84911b3d85fbbf4cb19e4d36b10a938eeb78b599dc86615fd6cec6eb7b8f7afa5f6d6be19ea81630d36ccfb2f487de50d0cf46da8d3fe3512812043c0e3ef2d7231fb0b0a35a0fb283be30a1247780f30ae0294e8b6f5897383edb895595f577524df54593cdf927b4967616ee3913e4d6b29b0dbd7c33a2a45e4ef1b1954ea5d91ce37efc1302e7ce02a97395565da2a5c5d3fdb0d87684e9b1c0ad07ec33df2dfad528e2ea0966d2a47dd5ee88e77d653c0d004fab0165f0757c4da40af327e7192536c79947a80a827aa2107dacfae3debfc8fad3d6e08076d938c510a276bdf6721a1f087cb169515028ad5ce27a1047abd92809934ca63b893f71f9a34a99c0fd30310c47e9aa37394d0ab73b254d3ca69d9c5549c9479aae24264ac5ea64d3fd821c3962ec77e709f9d30bc7b65a52e48c16e80603558caca1811411c3155d1f949fc9cf9aa9385a7199e99be77a66fad7eed91258de55b2c4c83f9a050adebea5f09758f40dac4a1c394ee8d687879150d26426895ab1938e14ae11b376254c91fc6130436996f8ed43bd27be20ec9067111c116ec94cc2b06cc91a13c5d10bbd7eecea4792f17b2b77631ef145e9fb41a83eaa11c2b72a48fb90fdbd88644c4edf8ab20dce3118364b276ac1237b36c8926e346aab5a111aa0bf341c518b7bff9e9dbb8bcb4728601b3760663e67650331e6fb54ac82fc414cb8ddfc160a25311ec5272de46217fef8b992ff89754fbee351f21bb90b6c97078b510c983350681266c8fed1f0583c5151e7b8fe3b7292319699687cc6b641fdbd689428543bc0fa1facc109de65b62784c2d985ab15d77d3af12af6d03e8d1859a553688584d75ef673a1de74093ee108c761fff32c217c231b0e2953daf521429264c0963bc8a5cdeddc617a7285b934ea51ddb5cdab23bcede86be36e001bc65c65e9a1c94baff4fab8eb5f8ed42ec377423633fe00049142467c47c5d58a7202c8e9104841c1f7f380145a6a0a828c570235e507ae5868a6062f722bb98ff6be"); - byte[] privK = Hex.decode("dc7bc9a2e0b6dc66823ae4fbde971c0cfc46f9d96bbfbeebb3470ae0a5a0139ff037b84e75537e0a1cf02a517acfe323ffffe11df72e4f38430e0e66a2654b2f2ef757da47649d9f63fa03f1bf6fe6bc7c62971a98a2bd9d36eb0ec43ad4e9d940df3bb5874f5c92192aa31e0535d3cf70950bba858d11a688eaf854f63ecfc520c50d624891434265d8b0680c03061040299a104082c0910c8508d1100d44a6509408292211125b90508a2688e1302dc4021280028ac302611820851237808a000ae2040421b4910bb80550a08051b2511c28428a3672a494504910201bb45161424424a75001328181942d62a850023449ca94200b296213156408924c48122100b605030208e0060200a311e1802021116483a62898029291480801083041066613200e5b360951400c53000aa08851944842e316704ab2089b92440025121b0309418209c2a0800b290a819851c4340da4424500a0105b048e603400138928a4422648002c90202d194068e2146d19278a083746e4146914006422c660d3a03013242844965014166da0284dcc462e94367100232e1c114909a2040131060a2172c2142ada000c5a260d13228a62c444e3142d013445980224d33841c0308121a621e348720b1984d2c89108b8690887714a2884d496451a9301ca2285da30859ac851dcc00820106060465262302aa224251044640b2842988011540692144251d236719bb4900b082890188e41c469e1a469032160e01409d3020c20c88c1cb23164086218476920228ccb8470089528029550533270013405888424541041d202881aa84ccac88181008d0392899ab809d9900c9a1290614065c9322d89860c123521cc4266c8360010062411028ea3b44d44023043a0285a002ed1980c4882658922441c010212907084226e12134d011902519064113364c91806c2c04589262908b63024308cda022e0c27250b367058162c5116420b4946c1208841246c99466a04434e18a86c821661922028639409c30211029520211782d43868003460c84688e0160000a32dc0a82824b640831464c81022a2086503234ac8122ea098418c2072cc308a62c665093408412682da429089328514967081226001176d5948428ab88d592051d80892e2c0889044700ac0245a020904218a59c45094441094140820460209270c441020dcc8209212015038250c456e4a1666223770dc808ca426412222441ba3618a343099844099c42952046d88146ccb242a7cd129a8d333115c62d033b6a8357cf7cd10268ab12f16fceb7975d0a28a6c4822213c9a772df084ad91a669e2040550fc5e8d0aeb10fab2375fc9625ef9cd48c19631997a1cb6455d2c6286c569c9637add0317ce990996b28e51c3f3f717fb5907bbdd53961ad3497f2c3c473cce170906ac4c624a89aa8fbe624d99385e9c9548bf05e8cafd47d2476e41b73001f813726499e88b2b3b6f596ca311657850346598994c40e34747161e4e76264deef2a3019389d1594c942301af47b7544c23ecda2df2dece81e487d8f3f58ea89cd811d7275807ff1b0369ba86470088c174a3099fdafbe5fbb4d158801053b2b435d54059e26dee76d10a7a372f06b0b88b985b32f52052387438be8dc8bc6ae7369e2da9aa5e2585f8de403d091ccb7f790d54ddb34c608b0876f2825e9113be20a2b85867a01bda53287ac780bcd8b606d2e6d7712c56ce0142d22fe6b786de544963e134fecedfafb83d763061d799096a59e30d4472e440ae1faaabdf42640ce69740ceb9cae1a9612c21931b74af3f780236123321b205b6efd6cbb134f4c73d63c0c13e660b59d5920bc33197c355853d8d1cddc7959f7bc500ac81d985016f5b89a0eec79b0d9364ead8e38577c2a6549f2d067cb09438fdb21220aec80f6e22a476f332a2a4a0b7acbeb9e078d2b5a92ae84c924f7cb19fc7df377beb6546af97aa985c747cd111a127a674b4c26d89c14485b82e3a498a12d05406febd6c4d4b8bc051ab2cb91224b078538374b794b7dd9ddf3ac2b4a671fb7b9cf5acb78622ae2709eb2db16943aa24a9c97a81077bc784d25c0ea5991d2de883798a1f0e78f3361ed6a10dded81b1d683658331534fd7c01bc0eb00dfc4c3c84f0693046ff806bb200dd7bd4c0e6abca3f2934b4814fc0e1f8be615a2dda7c8a8d06cf9ce8566b40f4a6543b25bacddc926863fc0fa2007d6d7bf6d18dc98df696bd0865bf0be4c492b8043a32def8e3595ba7da345252f38f95be10fd7fb899b498fa01b09de5d5608eabc44a721aa04c4ef1dcb86102ac5f5f79c9708dcf5c5e896edd8c2c7bde3fa83e6ffce22d66174e31657a0b6361585e669d3031952f08631ae1f16ff90b90d0aad3c6d7e1dd0a9c41ab00a6e1c4f96af9ac5b79fcf821ffc016cb059245fb78dbe6c633d965aaab5333be07195c4b74b18e4600ce783c0a914ef4281016e80a7c9aa92d0fd789879c5e6751125ecb154432311e41cebd4fab3a31e4d2ce22d0f8c67737bf8a0dd85fe1349d5079a4d5feb3fee9378ca47ae46cc58a3f02038cfd53c4cee9cc4270cebc3d115a39c831e8ed41c4dbe4051b51d7872ba0c2bb163e0085201188eaa624a6bea9400a3a1fcc355a57f15704e61fda55a5dbaea8448fa5cb2d377a07f58305ad107e844ab4806e5bf99c1f513ee1d0a2acc04549f0801742169a77971d0adbfbfe0dd2ee5d16bc461e35748d1f3f6f4598321e8c49e79e740f990359858d2729dde007fcb26fdda9aa6e2ec4bd736f2836e7e4c83440191c849f6a53c72a4f8f830d001ea3b18f3cb4a5bd3cf066032b4932cfd2e62a9b55723fa61c688c935518af6860cd649bfbf1bf5fdc1f36dcaefaa157438d1cc8d56a150161511df82631f5e88e773e4ce263f276b7b3678d4c6fc75311d411c0d01bfdb595bb70552838e1b86517c837d909e772b428599e1fe569f77ce61531fde6fd31cdce1bdee4ba467fcbfbb9feeaad99fef67d4906e036c73662ddce158d4e5d4635e5d366f79f31a19d1b3dc4a591b0df194bb06c18147f41d88d1a409becdfb67eb063d16312266fd51b521ba9115e2e5e2aeae6ec511cede13ed4132ffbe0273f6c7039b3874f058804a54809af60557a21d9b4b831d04156a7c22dcbcdfe14f62437f449cb5ef12bf4251d485496cd835c0c2bc58bd845963dfa76ecd68519c4bdaf110be7ab052876dc3407591568c956ea3bf107c90fd5853a292f59a8d4b58b5d3fddf29bdbeac36852e3c69766fe460176a801831292b8e88a74a01ecbbe09a7b4d74cfd7fd628841944d9d556dbd60c76f96f07dc53443805ee9aa09365de4fb8179252c6b099b5dd351fdefc23dbd8090596c5d208ffd2c5661d8e5612dd574fc69045c769a969e600d77cfe192f1d3ae911289355c585811491b0ccd73692ab158824ab9edf8ac8193f0b33e6138b72c6dcd5d344f807b3da92425037de5ea4eead1c795effaa145e2ecdd327606eb2609929b9474b2bb04653602555c068385e92f06f29ca613ce5b4404f01ab1805db0acaa890330d291f40692df382509302b6dc8668f2c8f2d3a44fd58dca26e9802794f73d25b3149e6d576441"); - byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8"); - byte[] s = Hex.decode("42a2ad149a7f35856ed92005232a2d33dc4a1ad0cb0fb7b772f56956082fb9a630fd7284bca55b5cef3a55ad73c2225d7c2d143d0023cd988890b81c271b97e6ed99250bd141550e4eb0276d4a59f19023d0bd725fe2c4f3301655ae91089851db6ecd24bc448ac06c1bbad4254bb1370678858586d55ffa4a9112dd48fd14c224d35d9c2987dc8bff84578d9a5fd0a1e34f7fb34523a305f623cef1766ac8b336c6c1a062f8d273e4f5636969c8c5afb3102436f9549a68ceb5393065944f0a231eb53ef7c6d3bca1fdf2544e3637f5efa96752455e4816d8747c5af14d3996eb241b2dc28fff9a9d93d148193195a87d6763dd94a5d9dfcd8623baf73ecebf545291bd236f44a3b9c5b8d231b7d7e991f6fbd67bbf3740611ef64c66765e25dc0e968900c407565097adf82f7b2387d03f93757a88c1a7590fdca09e19579ecc124629a26e80851b1ca5f29bff6ed37fc779bfc304e93169004b7c742ff4ab9ead2e96e313f1ddd8f6f94d58298ecd2393e119f5536d46e934ff11323f06df447685fbc1f8017a1a98ed717c8c7e4aa9be3b9f0c9f4e43c802c9542a26c013a07f5dcf2cfac584e8a998712cb6f00d4e51f9a3d65bac5197b49bd5291db44fbb90160a364818548b0bb59d34f48fbfc86b7f9d765a427074bee154dacce37f2bae727e99ec55bf7b5d618eebabc73cb015d18c6ba4c45a4c5f8c8802beceb9fd183989f4ccd3964a995a19a4a4492ca043c4be3ff76505d97174db15e15d56acf3e78147c0136373e784d627360e1ad41decdbb5a92cf271cba3a969f366ef53fa1150a1514b18b8c6835a44c9139456c162dfa59e525892e38ad6864097f5108752b4b8d3f847bdc0c185f6da216da8ee00c06ee8b54d66adfa85d2f8851ecbafea5d063604d6abf28a0df4042d788cc539cbfce523f1183dd7c955990ef9709d9db2d28a0ac55382b92b3869ae40072119278e005be9acd8b30507d55a065815db29fe5ad0ded3094d9e92762b1d52a7790e146d4b4b7e81389af5e1bff9485ba72ffebf902aa343e5ad737f57bee177ed8514f0549083407f6a645234be6ece678c59f905e3af7190602e4c1d8815a28e791d476c10ecfbcfc9539e995e72c8cad9f7b515a53e0c912be7071c13c2d350b1965627ec610e17bc52c13108dd3f2e2fd703edf13d76ee62d904f45d6f89b5814a6570ab5e041b14186c63bc0b93de643aa4828ae4747c964474102cfb77aed3412248c67a8fbd2971072058ddda17df2b152449c63b164dd1ca152c893e38afd042d9f186e677969dc3caa6d2105b54d7e8dc47bda7f63606e8670f3f671b0e43d1cf0884cdde011743a9748e50b66cebacfd4595c346a8229883fd92945e65fab2c9a1dad85d6ae11ed3dcd07dbe1bf031fce1c23f5d1fc61dd970b40dec577abd5b2bb697f6b24406ef7d623b45b0a96a79a8171805d599ea99fab55682eba390c0dbd7f53999ca7cd5e4e471139b5e877be6fdcab79ba7cc7693a07bf537f4e05669a977610d2f526e7ed6edf75164b09e6ed608ec755744571694218a36ad96362381fbfb967ec0e0180fb8efd4972c8614f82e262e0628a083f360ed927dc85b9b95d5c53eb371848f3ee1c7dd069918f74e7a1f25fc6f955e72be0202a401e28c7fc20c8378469b6bc370700b6fce04224a3f3815598f15f44ad95972208c215126753db78fa84fac87b62da8b1249360ff2171643cb100c07f8caffbd9aa4d94b0b192eb49af6c9d3b68357d708d597004a178c116efe72f5ec80d2269c592e65eb12b5968f3c153bb900ea3d49a91e155dc38383844bb849f8f78c9038d30ab7b6719830a7667a725f67b6318615b37f0d0a2dedf7e2f741d1807abd4614087449ce789ff10deee23befcac04de3376245143f24df1a1f95d7442439b2e6f983959598c95577e2d262e96f8fa4cc4a1fd59e2b4d9c4394071630c2e0569c4fa3784bfb0d39f42e366c8fee583412be0c6d4c67fff9d570926210fe632fa125245496af25cd084d723994c94e2ff659637784c31e9a555a788c8fc7410839ee1c6e80544d825b79fcf238afd1c0d6be0fa32ecb8b93463d98b9f2b3495c81f25877a613227bdaf8b94342da81c0f2995872a5a75341503bfaec2bb7f95db0f340f4732a832f4effab9bf4da476528a15fbfc5104fe3dae3a5fdd05ebc42989d96f1eb056c3ffc79de35d229a55e301c33975b92c4a7de50962a2fcb83912441189ac1a4e4ad38e30ecc3df084f0ecd8745750323debdea86ab87e725d41fd044fd507f279e7dfbb6a04b34cb150ed9fda95d7393cf8e611589ec56a5dc9a9de4dc80c36e7cbcfb77501bc69b93437ce3642ee35da9a0d71b76a641847fb9798e18b1d073a7b832958f65079648b47370bfc175869dfc412b0b3074fc43d608acd2b602f7b9d2fb831c3a37de56600a34135a1d029bb5f582732b2dc45f992c4a6dcc2c3b1cad807ad4e741490b5cad74a6e7a416fe91b1ad216c428558c3f8d0797d4acca85ca864a5194cf1273622ffeed9624f702b4725a93057a90d155ee081183d87517123647fbd31216b664107a124adef5e1dbedb7e714f6b49696fa21a4a3c2c822cc675b2a171949cd64d10fa188913a9e3318dc9829aa3e6bdeac781afd2b20211c6aeda61deedc8ef7d1426f7cf464af6a700bfce3e0df99f417b1440807870ca0af461cec38cde60e6861f817901a56db98af64e9be3648585513f833a3e5c6fedc613dfff720e76a0800139d53957b1f91e7efcd0e6308613740705e589d48934f5e9a193af901b3335e767310830ebc6662ef8d33e7c87e242b65595c61212f9ac459c09995cf4a996584bf473d4c58db901c2c994f62e6022720987e653d1e3100a84db6e077b9e4387b9df33048d201969b5fb215cb142000bb21e7e5cc3e74b934cfb80e9d117fcecb1c68479390f173cb8e33853f66a51d157287324b3f8a590e40646877c5435e3251fdd5c19791471b51f07c5265dd79aeb2997545e7a3c7b6484f34734871145260d019b28692be1357ff27b9361ff90c1e5f308a1832a900dc915c3771cb83e964e99667e5e46a713c152ce2e33d45ef4050d34671391ec20f93fcb9b9597781e1ca4d4ba1762fced1f9a06311905c6bdda492f72ce9a1c9f20cc26d020aa0a3cede4bf35a7735fe41d1ae0e0ecbb5da8ccd68f37c7acaebc1f8fc764db3bed1dbf4053911b9105e09605de322fb8750717c756330856835ed2c713ff7957f8a99f7ed485e480949b2a9b9d1d8acc0eafeee71977a56a07132c495067aeb7b9dae1e7132036424b7993949cbfd2dedff91b42494a555d6a72757aa2aebfd0f0fb161a617c8890a4a5afc0d1d9dadef300000000000000000000000000000000000000000000000c1a2a39"); - byte[] seed = Hex.decode("7c9935a0b07694aa0c6d10e4db6b1add2fd81a25ccb148032dcd739936737f2d"); - - KeyPairGenerator kpg = KeyPairGenerator.getInstance("HASH-ML-DSA", "BC"); - SecureRandom katRandom = new NISTSecureRandom(Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"), null); - - kpg.initialize(MLDSAParameterSpec.ml_dsa_44, katRandom); - - KeyPair kp = kpg.generateKeyPair(); - - SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfo.getInstance(kp.getPublic().getEncoded()); - - ASN1BitString pubSeq = pubInfo.getPublicKeyData(); - - assertTrue(Arrays.areEqual(pubSeq.getOctets(), pubK)); - - PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp.getPrivate().getEncoded()); - ASN1OctetString seq = privInfo.getPrivateKey(); - - assertTrue(Arrays.areEqual(ASN1OctetString.getInstance(ASN1Sequence.getInstance(seq.getOctets()).getObjectAt(0)).getOctets(), seed)); - - Signature sig = Signature.getInstance("HASH-ML-DSA", "BC"); - - sig.initSign(kp.getPrivate()); - - sig.setParameter(new ContextParameterSpec(Strings.toByteArray("Hello, world!"))); - - sig.update(msg, 0, msg.length); - - byte[] genS = sig.sign(); - - assertTrue(Hex.toHexString(genS), Arrays.areEqual(s, genS)); - - sig = Signature.getInstance("HASH-ML-DSA", "BC"); - - sig.initVerify(kp.getPublic()); - - sig.setParameter(new ContextParameterSpec(Strings.toByteArray("Hello, world!"))); - - sig.update(msg, 0, msg.length); - - assertTrue(sig.verify(s)); - - // check randomisation - - sig.initSign(kp.getPrivate(), new SecureRandom()); - - sig.setParameter(new ContextParameterSpec(Strings.toByteArray("Hello, world!"))); - - sig.update(msg, 0, msg.length); - - genS = sig.sign(); - - assertFalse(Arrays.areEqual(s, genS)); - - sig = Signature.getInstance("HASH-ML-DSA", "BC"); - - sig.initVerify(kp.getPublic()); - - sig.setParameter(new ContextParameterSpec(Strings.toByteArray("Hello, world!"))); - - sig.update(msg, 0, msg.length); - - assertTrue(sig.verify(genS)); - } +// public void testHashMLDSAKATSig() +// throws Exception +// { +// byte[] pubK = Hex.decode("dc7bc9a2e0b6dc66823ae4fbde971c0cfc46f9d96bbfbeebb3470ae0a5a0139fdd6a6ce5bc76e94faa9e9250abd4cee02cf1ee46a8e99ce12d7395781fa7519021273da3365519724efbe279add6c35f92c9d42b032832f1bf29ebbecd3ec87a3af3da33c611f7f35fa35acab174024f118979e23bf2fe069269a2ec45fbc1b9c1fb0e1f05486a6a833eb48adc2960641d9af6eb8b7381b1ec55d889f26b084ddfa1c9ed9b962d342694cede83825309d9db6bd6ba7582132534861e44a04388a694242411761d34e7c085d282b723c65948a2ac764d9702bd8ed7fe9931d7d8704a39e6508844f3f84843c305594fe6e5404e08f18ed039ac6563cbaa34b0ca38320299d6256ec0f78d421f088159d49dc439cbc539a55884a3eb4efc9cf190b42f713441cb97004245d41437a39b7b77fc602fbbfd619a42363714b265173cae68fd8a1b3ca2bd30ae60c53e5604577a4a3b1f1506e697c37432dbd883553aac8d382a3d250cf5b29e4d1be2cbcd531ff0e07e89c1f7dbc8d4529aeebe55b5ce4d0214bfdec69e080bd3ef36cca6a54933f1ef2f37867c0d38fd5865b87929115808c7e2595458e993bacc6c5a3b9f5025001e9b41447708bfbaa0462efa63876c42f769908b432f5485508a393224960551d77eadfaf4411cbc49fdff46f2f155ddd6ec30867905b709888ca0f30f935fb8d7f4803cfc7a5f7790ca181d99ca21f2621d69a5c6d49c76b4969da62740a378470332b30947ab31ccdb9ba0c7b625879eec4bd81f0200ba23504a7dc3b118bc2ab1145df13af3c8cc39f577873b84911b3d85fbbf4cb19e4d36b10a938eeb78b599dc86615fd6cec6eb7b8f7afa5f6d6be19ea81630d36ccfb2f487de50d0cf46da8d3fe3512812043c0e3ef2d7231fb0b0a35a0fb283be30a1247780f30ae0294e8b6f5897383edb895595f577524df54593cdf927b4967616ee3913e4d6b29b0dbd7c33a2a45e4ef1b1954ea5d91ce37efc1302e7ce02a97395565da2a5c5d3fdb0d87684e9b1c0ad07ec33df2dfad528e2ea0966d2a47dd5ee88e77d653c0d004fab0165f0757c4da40af327e7192536c79947a80a827aa2107dacfae3debfc8fad3d6e08076d938c510a276bdf6721a1f087cb169515028ad5ce27a1047abd92809934ca63b893f71f9a34a99c0fd30310c47e9aa37394d0ab73b254d3ca69d9c5549c9479aae24264ac5ea64d3fd821c3962ec77e709f9d30bc7b65a52e48c16e80603558caca1811411c3155d1f949fc9cf9aa9385a7199e99be77a66fad7eed91258de55b2c4c83f9a050adebea5f09758f40dac4a1c394ee8d687879150d26426895ab1938e14ae11b376254c91fc6130436996f8ed43bd27be20ec9067111c116ec94cc2b06cc91a13c5d10bbd7eecea4792f17b2b77631ef145e9fb41a83eaa11c2b72a48fb90fdbd88644c4edf8ab20dce3118364b276ac1237b36c8926e346aab5a111aa0bf341c518b7bff9e9dbb8bcb4728601b3760663e67650331e6fb54ac82fc414cb8ddfc160a25311ec5272de46217fef8b992ff89754fbee351f21bb90b6c97078b510c983350681266c8fed1f0583c5151e7b8fe3b7292319699687cc6b641fdbd689428543bc0fa1facc109de65b62784c2d985ab15d77d3af12af6d03e8d1859a553688584d75ef673a1de74093ee108c761fff32c217c231b0e2953daf521429264c0963bc8a5cdeddc617a7285b934ea51ddb5cdab23bcede86be36e001bc65c65e9a1c94baff4fab8eb5f8ed42ec377423633fe00049142467c47c5d58a7202c8e9104841c1f7f380145a6a0a828c570235e507ae5868a6062f722bb98ff6be"); +// byte[] privK = Hex.decode("dc7bc9a2e0b6dc66823ae4fbde971c0cfc46f9d96bbfbeebb3470ae0a5a0139ff037b84e75537e0a1cf02a517acfe323ffffe11df72e4f38430e0e66a2654b2f2ef757da47649d9f63fa03f1bf6fe6bc7c62971a98a2bd9d36eb0ec43ad4e9d940df3bb5874f5c92192aa31e0535d3cf70950bba858d11a688eaf854f63ecfc520c50d624891434265d8b0680c03061040299a104082c0910c8508d1100d44a6509408292211125b90508a2688e1302dc4021280028ac302611820851237808a000ae2040421b4910bb80550a08051b2511c28428a3672a494504910201bb45161424424a75001328181942d62a850023449ca94200b296213156408924c48122100b605030208e0060200a311e1802021116483a62898029291480801083041066613200e5b360951400c53000aa08851944842e316704ab2089b92440025121b0309418209c2a0800b290a819851c4340da4424500a0105b048e603400138928a4422648002c90202d194068e2146d19278a083746e4146914006422c660d3a03013242844965014166da0284dcc462e94367100232e1c114909a2040131060a2172c2142ada000c5a260d13228a62c444e3142d013445980224d33841c0308121a621e348720b1984d2c89108b8690887714a2884d496451a9301ca2285da30859ac851dcc00820106060465262302aa224251044640b2842988011540692144251d236719bb4900b082890188e41c469e1a469032160e01409d3020c20c88c1cb23164086218476920228ccb8470089528029550533270013405888424541041d202881aa84ccac88181008d0392899ab809d9900c9a1290614065c9322d89860c123521cc4266c8360010062411028ea3b44d44023043a0285a002ed1980c4882658922441c010212907084226e12134d011902519064113364c91806c2c04589262908b63024308cda022e0c27250b367058162c5116420b4946c1208841246c99466a04434e18a86c821661922028639409c30211029520211782d43868003460c84688e0160000a32dc0a82824b640831464c81022a2086503234ac8122ea098418c2072cc308a62c665093408412682da429089328514967081226001176d5948428ab88d592051d80892e2c0889044700ac0245a020904218a59c45094441094140820460209270c441020dcc8209212015038250c456e4a1666223770dc808ca426412222441ba3618a343099844099c42952046d88146ccb242a7cd129a8d333115c62d033b6a8357cf7cd10268ab12f16fceb7975d0a28a6c4822213c9a772df084ad91a669e2040550fc5e8d0aeb10fab2375fc9625ef9cd48c19631997a1cb6455d2c6286c569c9637add0317ce990996b28e51c3f3f717fb5907bbdd53961ad3497f2c3c473cce170906ac4c624a89aa8fbe624d99385e9c9548bf05e8cafd47d2476e41b73001f813726499e88b2b3b6f596ca311657850346598994c40e34747161e4e76264deef2a3019389d1594c942301af47b7544c23ecda2df2dece81e487d8f3f58ea89cd811d7275807ff1b0369ba86470088c174a3099fdafbe5fbb4d158801053b2b435d54059e26dee76d10a7a372f06b0b88b985b32f52052387438be8dc8bc6ae7369e2da9aa5e2585f8de403d091ccb7f790d54ddb34c608b0876f2825e9113be20a2b85867a01bda53287ac780bcd8b606d2e6d7712c56ce0142d22fe6b786de544963e134fecedfafb83d763061d799096a59e30d4472e440ae1faaabdf42640ce69740ceb9cae1a9612c21931b74af3f780236123321b205b6efd6cbb134f4c73d63c0c13e660b59d5920bc33197c355853d8d1cddc7959f7bc500ac81d985016f5b89a0eec79b0d9364ead8e38577c2a6549f2d067cb09438fdb21220aec80f6e22a476f332a2a4a0b7acbeb9e078d2b5a92ae84c924f7cb19fc7df377beb6546af97aa985c747cd111a127a674b4c26d89c14485b82e3a498a12d05406febd6c4d4b8bc051ab2cb91224b078538374b794b7dd9ddf3ac2b4a671fb7b9cf5acb78622ae2709eb2db16943aa24a9c97a81077bc784d25c0ea5991d2de883798a1f0e78f3361ed6a10dded81b1d683658331534fd7c01bc0eb00dfc4c3c84f0693046ff806bb200dd7bd4c0e6abca3f2934b4814fc0e1f8be615a2dda7c8a8d06cf9ce8566b40f4a6543b25bacddc926863fc0fa2007d6d7bf6d18dc98df696bd0865bf0be4c492b8043a32def8e3595ba7da345252f38f95be10fd7fb899b498fa01b09de5d5608eabc44a721aa04c4ef1dcb86102ac5f5f79c9708dcf5c5e896edd8c2c7bde3fa83e6ffce22d66174e31657a0b6361585e669d3031952f08631ae1f16ff90b90d0aad3c6d7e1dd0a9c41ab00a6e1c4f96af9ac5b79fcf821ffc016cb059245fb78dbe6c633d965aaab5333be07195c4b74b18e4600ce783c0a914ef4281016e80a7c9aa92d0fd789879c5e6751125ecb154432311e41cebd4fab3a31e4d2ce22d0f8c67737bf8a0dd85fe1349d5079a4d5feb3fee9378ca47ae46cc58a3f02038cfd53c4cee9cc4270cebc3d115a39c831e8ed41c4dbe4051b51d7872ba0c2bb163e0085201188eaa624a6bea9400a3a1fcc355a57f15704e61fda55a5dbaea8448fa5cb2d377a07f58305ad107e844ab4806e5bf99c1f513ee1d0a2acc04549f0801742169a77971d0adbfbfe0dd2ee5d16bc461e35748d1f3f6f4598321e8c49e79e740f990359858d2729dde007fcb26fdda9aa6e2ec4bd736f2836e7e4c83440191c849f6a53c72a4f8f830d001ea3b18f3cb4a5bd3cf066032b4932cfd2e62a9b55723fa61c688c935518af6860cd649bfbf1bf5fdc1f36dcaefaa157438d1cc8d56a150161511df82631f5e88e773e4ce263f276b7b3678d4c6fc75311d411c0d01bfdb595bb70552838e1b86517c837d909e772b428599e1fe569f77ce61531fde6fd31cdce1bdee4ba467fcbfbb9feeaad99fef67d4906e036c73662ddce158d4e5d4635e5d366f79f31a19d1b3dc4a591b0df194bb06c18147f41d88d1a409becdfb67eb063d16312266fd51b521ba9115e2e5e2aeae6ec511cede13ed4132ffbe0273f6c7039b3874f058804a54809af60557a21d9b4b831d04156a7c22dcbcdfe14f62437f449cb5ef12bf4251d485496cd835c0c2bc58bd845963dfa76ecd68519c4bdaf110be7ab052876dc3407591568c956ea3bf107c90fd5853a292f59a8d4b58b5d3fddf29bdbeac36852e3c69766fe460176a801831292b8e88a74a01ecbbe09a7b4d74cfd7fd628841944d9d556dbd60c76f96f07dc53443805ee9aa09365de4fb8179252c6b099b5dd351fdefc23dbd8090596c5d208ffd2c5661d8e5612dd574fc69045c769a969e600d77cfe192f1d3ae911289355c585811491b0ccd73692ab158824ab9edf8ac8193f0b33e6138b72c6dcd5d344f807b3da92425037de5ea4eead1c795effaa145e2ecdd327606eb2609929b9474b2bb04653602555c068385e92f06f29ca613ce5b4404f01ab1805db0acaa890330d291f40692df382509302b6dc8668f2c8f2d3a44fd58dca26e9802794f73d25b3149e6d576441"); +// byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8"); +// byte[] s = Hex.decode("eea01acc8fe6631c9260996def0c75d84998cf4512c7b67bc4246bbfbff165a404d02bca30da1c2b61d91d7986a467fd4b3a1131077ac138b4011ef07fc1f3ca6237e7fc6550c7e071c939f4554f265362eb26158ee1a2e57c72d20beb78168d9e2d7faf54763755a44bdc29e575884133062453a9e3e761a13a82143d7db7215ab0b8628c096e0f298491a8d67517ab8a90be7db5d311eddc7e883d50eb873decd9b3e420155008c0a3632d51f93a9c67ea336273c8f26617c7cfa2a3aa6d9aa075e75fc60fb587fb5522cb6bac4ef1979a069a15aed3171660ce5da2c27af188e9d3bc62eebdbd798f1a650c7d46411e3cabeb27be3ae787b5f4d82a2e2cf4eee84b4fc82edea4533a47bf7d3791933d1c5ce19c1792a6420c9dcd540dc3a494d9b518ce5f4f85d7747c2fa45d812d16f089682c57a96061210c942dbaca8dbde509f56f498d68f0b4e6f4a51373c9f5a5db2e20ec8254ea9d59f69d732fd1b1149fbd0b608368086a3b6acbe5de156fc508390ddc9ca28d201fdc67140a767b2d99d08051255e73d423750e91b1aa31982cc7014e46c75b69ec972f15745bafbe4878f73d8800d80ad22716f15f4d964f0468e8583add1a4ba8f544e9b83dd6c5ebf744436e254bd699e8909e712d9f6e69fbda39be69a4e0bb58cdd0ddbd369cf5a6475b1ad5fe2849c439611516bd13de14b34b26a731c5a5bde4fae538782a6daeb6fbdaf4cf1af40f3b9c7896e87ac71b3fc95eea02f2f28bf62b9d613998ad973added64515a699fc89304b8fe4d4c97759662b720835c2b28585648e54dedfbfa40fd1455e8a945a390765e3b1a2588286bd4f2995eca39ce9eca6d2e32d92e18c930e4b99109f148db96f1763703aeb431c3c815d577c2b0282181932bb182325bf45e1355d91c8ec669ccd94429dc4ea0abc562988bd2b27b39f2dd0e4ace6fc9148cd064005e9cf0f8105a6534261942774c2a02f150f8d250822745b1cc40e1d57c68dea152c0f5088712266e5a37eb7760204c2747561688990d3cf7c76660e5671727c7ebde56420c91549a48b3763062ff92a4679e7d3e8569303eeff650c0ae606234d70a350aa9862b912825b13c1ec7bd6bf346717b0f30c45c1925873f04d469fd28f82f19375531ffb83851c471ebf623c86130d929e739bd8721d97ca9a83676f26c0a75493b5b02f0921aea91baae2da96532ab9db04dc997d5f800f58a891f6ef26e5478de1ce9b4da04eccac4cee81de5c3b1010ef28242217ec737beb36815c4af1de9a4160180ea896120ce96869bf551bb6be079482f4ab5c0f7f234c50bb4139ff9fba1b594c85cc3780434fc00f7d0492cfc86c0d1889784b113650e3c29bb2e9ed6f94df5ea42afac8060856fb90fab5f4c6fb6875fd67e0438335bcd5199b72706cf358492e5f4945bf2a686aa2861908d6a71ba4e760c87e75a7ff31169993cf048817512aeac4e960771879be541b1adcd6c2da9f9d2153e728d4ee1e91acd7f704e0b472856cfd8f85e2f30b0f6e427f190dbc1079343fdc71f9ac0b8aa5e6fdfd60fe9c90e4bebd4a91dbf16378bcc2a330aeed5b7e8dd617fbf3c6ed9b2c2ac82f2e9d03c975b2a832a667864090da9ca91ad1af97278f6cfebeaf2cc681957f3d75d17a5710de85444b636cf7e0dbac06852fe669f87bc7c430ff548ee0b34f1a68e86f1b26290f8a056c0bccd18a540ce04ee7fdee47f5176b811021a89d170e71250a040e10f8f900236617c6be92217b77aa00854d6376739d5a63d32a377da20c368092367fc6afb62e0b898c01462a399aa3dba4beb0d03d7f8a2e84b499a41e7a6e50cd5e09b4d6d8cb3e8ee3a2d1b50775f9caf7335f1ea15b2312352831418ce2529542011a19c6ece5c1e6dfe7e37821933594ca6f55ca6c799b32f88b21d59744c10538ea7208eb61a04d24476c326068ac9ad4199080e02b95766f6a18738c7e4506d18e9c5c526ef4f28ef14662667dc865ffd446026cefca1b39be77cdfd7773aac0bf570647c21979b9f0ff0d67f2a9940e1e1c27e9e3296c7d890c5627e9126fa09bed1f242aa23112d828529ce431939c9ecc0d4311742a1fa5f9f283eb0135093d3e6aa9e89d4641415678f0b2ecb1210a611d062c5e17f01521aa45d778e2a0770cfda540ce1b5bbb3fabfc783601480b080f4e7275e69705b6cff043a3b503da77fbdc05702b790b1cf4dccfb6b2df00bf0ee896420175e1293a6b8fbc96cf9759a6c0e56067dc9e2522621af2cf830e2bb648f0ca3560bdf9c2c0c01bb23806455bc40889472398f9daf71f0ab9e1aab2fe8d6c8c504a1a45d99229828a9588a559a7172b041b2bbcdbe2e59b749b3e1219abf51a39164c9fac17bd8c83eab1e0e04e029550b689134194d486083b956706a6706274324d63ff79c35f37cf8c2932910e60e1da7cc6c4d9b966fb11437f7d4e94221e4b9ce51ea04325e4b75e45dbbbaadeeadd432e776c9b14cff55529c24b43211b52d1f27de0d86c0f253ec3c2262fdfb1ecb18442174321bbed4c1454522d747be4f53f9ea445d4db360c5c0ddbac51179c7f6016249bcadcce47d4abaf0604047d806f556d11aba411239a32a80d4403c72198036917b6c3b9c5fefcbb73b4d11cea0cc09b419ab8bb0a093131b4c32280fac586bee305e14a18432bbaf1ad6ffc67863edd564df4f9518c6363b80d83c59440bde98c2eeed939bfa1d8f720a805088e2090ab1af71ad5190978b2e23a7d58fc28ab2ed623f2cc65d67629a2fbbe84309a3e0447f3805728155cca522217f9e66b5b2d794fec7131b091f7f77df37e8d726f150d416018941a7b48617fe291a3a3594df80bb1927d87d8f8d5cb945c39c977e7a4f9882e3facd6f26e42390a1f7d14e55797bd22ae78ac11208084be07399d2f9cd2fa331784c5e65de766d87c3d19aad2c7993485e65f11b2c03533f265d9268f8d7f9ce14f97a76891e2b764d2e0a7baf2f81c6fcfd15c78552bba4952fe375c9872a25fddfa19a29695320858fa8a910c29d0739edff01da6d80f757906f7103984c4910c44220ce83fb5b46527c918d186b5c096ea4d1c85df71b4e7d625bb2df5a898880a18238eb4388f66f0d5daf074bdfc6e3b4695ef5faaf754ed764b80463d724d1fc41b598861207d1971cfe1e857cf2bf8dcc4afae1e44c622d96194d3f85fa5a37aed9a154074fbc54d50724658678dfba30bce2fc853bf87f7379d80865f08f0a772afedd8f45808b49605ff3d2875a6ce7b90f4a61fc55734f791bf2e6ed554309111a385158627d7e828491a2b7c6d1f00315162c42435b62656f89bbbcbfe1e6e8f0f7f9000711123b5b6ea7b0b7b8c9d2e5e9f5fc09727a9b9e9fb3d3d6e9ebf4000000000000000000000000000011253642"); +// byte[] seed = Hex.decode("7c9935a0b07694aa0c6d10e4db6b1add2fd81a25ccb148032dcd739936737f2d"); +// +// KeyPairGenerator kpg = KeyPairGenerator.getInstance("HASH-ML-DSA", "BC"); +// SecureRandom katRandom = new NISTSecureRandom(Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"), null); +// +// kpg.initialize(MLDSAParameterSpec.ml_dsa_44, katRandom); +// +// KeyPair kp = kpg.generateKeyPair(); +// +// SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfo.getInstance(kp.getPublic().getEncoded()); +// +// ASN1BitString pubSeq = pubInfo.getPublicKeyData(); +// +// assertTrue(Arrays.areEqual(pubSeq.getOctets(), pubK)); +// +// PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp.getPrivate().getEncoded()); +// ASN1OctetString seq = privInfo.getPrivateKey(); +// +// assertTrue(Arrays.areEqual(ASN1OctetString.getInstance(ASN1Sequence.getInstance(seq.getOctets()).getObjectAt(0)).getOctets(), seed)); +// +// Signature sig = Signature.getInstance("HASH-ML-DSA", "BC"); +// +// sig.initSign(kp.getPrivate()); +// +// sig.update(msg, 0, msg.length); +// +// byte[] genS = sig.sign(); +// +// assertTrue(Arrays.areEqual(s, genS)); +// +// sig = Signature.getInstance("HASH-ML-DSA", "BC"); +// +// sig.initVerify(kp.getPublic()); +// +// sig.update(msg, 0, msg.length); +// +// assertTrue(sig.verify(s)); +// +// // check randomisation +// +// sig.initSign(kp.getPrivate(), new SecureRandom()); +// +// sig.update(msg, 0, msg.length); +// +// genS = sig.sign(); +// +// assertFalse(Arrays.areEqual(s, genS)); +// +// sig = Signature.getInstance("HASH-ML-DSA", "BC"); +// +// sig.initVerify(kp.getPublic()); +// +// sig.update(msg, 0, msg.length); +// +// assertTrue(sig.verify(genS)); +// +// AlgorithmParameters algP = sig.getParameters(); +// +// assertTrue(null == algP); +// +// // test using ml-dsa-44 for the key, should be the same. +// +// kpg = KeyPairGenerator.getInstance("ML-DSA", "BC"); +// katRandom = new NISTSecureRandom(Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"), null); +// +// kpg.initialize(MLDSAParameterSpec.ml_dsa_44_with_sha512, katRandom); +// +// kp = kpg.generateKeyPair(); +// +// sig = Signature.getInstance("HASH-ML-DSA", "BC"); +// +// sig.initSign(kp.getPrivate()); +// +// sig.update(msg, 0, msg.length); +// +// genS = sig.sign(); +// +// assertTrue(Arrays.areEqual(s, genS)); +// +// sig = Signature.getInstance("HASH-ML-DSA", "BC"); +// +// sig.initVerify(kp.getPublic()); +// +// sig.update(msg, 0, msg.length); +// +// assertTrue(sig.verify(s)); +// } + +// public void testHashMLDSAKATSigWithContext() +// throws Exception +// { +// byte[] pubK = Hex.decode("dc7bc9a2e0b6dc66823ae4fbde971c0cfc46f9d96bbfbeebb3470ae0a5a0139fdd6a6ce5bc76e94faa9e9250abd4cee02cf1ee46a8e99ce12d7395781fa7519021273da3365519724efbe279add6c35f92c9d42b032832f1bf29ebbecd3ec87a3af3da33c611f7f35fa35acab174024f118979e23bf2fe069269a2ec45fbc1b9c1fb0e1f05486a6a833eb48adc2960641d9af6eb8b7381b1ec55d889f26b084ddfa1c9ed9b962d342694cede83825309d9db6bd6ba7582132534861e44a04388a694242411761d34e7c085d282b723c65948a2ac764d9702bd8ed7fe9931d7d8704a39e6508844f3f84843c305594fe6e5404e08f18ed039ac6563cbaa34b0ca38320299d6256ec0f78d421f088159d49dc439cbc539a55884a3eb4efc9cf190b42f713441cb97004245d41437a39b7b77fc602fbbfd619a42363714b265173cae68fd8a1b3ca2bd30ae60c53e5604577a4a3b1f1506e697c37432dbd883553aac8d382a3d250cf5b29e4d1be2cbcd531ff0e07e89c1f7dbc8d4529aeebe55b5ce4d0214bfdec69e080bd3ef36cca6a54933f1ef2f37867c0d38fd5865b87929115808c7e2595458e993bacc6c5a3b9f5025001e9b41447708bfbaa0462efa63876c42f769908b432f5485508a393224960551d77eadfaf4411cbc49fdff46f2f155ddd6ec30867905b709888ca0f30f935fb8d7f4803cfc7a5f7790ca181d99ca21f2621d69a5c6d49c76b4969da62740a378470332b30947ab31ccdb9ba0c7b625879eec4bd81f0200ba23504a7dc3b118bc2ab1145df13af3c8cc39f577873b84911b3d85fbbf4cb19e4d36b10a938eeb78b599dc86615fd6cec6eb7b8f7afa5f6d6be19ea81630d36ccfb2f487de50d0cf46da8d3fe3512812043c0e3ef2d7231fb0b0a35a0fb283be30a1247780f30ae0294e8b6f5897383edb895595f577524df54593cdf927b4967616ee3913e4d6b29b0dbd7c33a2a45e4ef1b1954ea5d91ce37efc1302e7ce02a97395565da2a5c5d3fdb0d87684e9b1c0ad07ec33df2dfad528e2ea0966d2a47dd5ee88e77d653c0d004fab0165f0757c4da40af327e7192536c79947a80a827aa2107dacfae3debfc8fad3d6e08076d938c510a276bdf6721a1f087cb169515028ad5ce27a1047abd92809934ca63b893f71f9a34a99c0fd30310c47e9aa37394d0ab73b254d3ca69d9c5549c9479aae24264ac5ea64d3fd821c3962ec77e709f9d30bc7b65a52e48c16e80603558caca1811411c3155d1f949fc9cf9aa9385a7199e99be77a66fad7eed91258de55b2c4c83f9a050adebea5f09758f40dac4a1c394ee8d687879150d26426895ab1938e14ae11b376254c91fc6130436996f8ed43bd27be20ec9067111c116ec94cc2b06cc91a13c5d10bbd7eecea4792f17b2b77631ef145e9fb41a83eaa11c2b72a48fb90fdbd88644c4edf8ab20dce3118364b276ac1237b36c8926e346aab5a111aa0bf341c518b7bff9e9dbb8bcb4728601b3760663e67650331e6fb54ac82fc414cb8ddfc160a25311ec5272de46217fef8b992ff89754fbee351f21bb90b6c97078b510c983350681266c8fed1f0583c5151e7b8fe3b7292319699687cc6b641fdbd689428543bc0fa1facc109de65b62784c2d985ab15d77d3af12af6d03e8d1859a553688584d75ef673a1de74093ee108c761fff32c217c231b0e2953daf521429264c0963bc8a5cdeddc617a7285b934ea51ddb5cdab23bcede86be36e001bc65c65e9a1c94baff4fab8eb5f8ed42ec377423633fe00049142467c47c5d58a7202c8e9104841c1f7f380145a6a0a828c570235e507ae5868a6062f722bb98ff6be"); +// byte[] privK = Hex.decode("dc7bc9a2e0b6dc66823ae4fbde971c0cfc46f9d96bbfbeebb3470ae0a5a0139ff037b84e75537e0a1cf02a517acfe323ffffe11df72e4f38430e0e66a2654b2f2ef757da47649d9f63fa03f1bf6fe6bc7c62971a98a2bd9d36eb0ec43ad4e9d940df3bb5874f5c92192aa31e0535d3cf70950bba858d11a688eaf854f63ecfc520c50d624891434265d8b0680c03061040299a104082c0910c8508d1100d44a6509408292211125b90508a2688e1302dc4021280028ac302611820851237808a000ae2040421b4910bb80550a08051b2511c28428a3672a494504910201bb45161424424a75001328181942d62a850023449ca94200b296213156408924c48122100b605030208e0060200a311e1802021116483a62898029291480801083041066613200e5b360951400c53000aa08851944842e316704ab2089b92440025121b0309418209c2a0800b290a819851c4340da4424500a0105b048e603400138928a4422648002c90202d194068e2146d19278a083746e4146914006422c660d3a03013242844965014166da0284dcc462e94367100232e1c114909a2040131060a2172c2142ada000c5a260d13228a62c444e3142d013445980224d33841c0308121a621e348720b1984d2c89108b8690887714a2884d496451a9301ca2285da30859ac851dcc00820106060465262302aa224251044640b2842988011540692144251d236719bb4900b082890188e41c469e1a469032160e01409d3020c20c88c1cb23164086218476920228ccb8470089528029550533270013405888424541041d202881aa84ccac88181008d0392899ab809d9900c9a1290614065c9322d89860c123521cc4266c8360010062411028ea3b44d44023043a0285a002ed1980c4882658922441c010212907084226e12134d011902519064113364c91806c2c04589262908b63024308cda022e0c27250b367058162c5116420b4946c1208841246c99466a04434e18a86c821661922028639409c30211029520211782d43868003460c84688e0160000a32dc0a82824b640831464c81022a2086503234ac8122ea098418c2072cc308a62c665093408412682da429089328514967081226001176d5948428ab88d592051d80892e2c0889044700ac0245a020904218a59c45094441094140820460209270c441020dcc8209212015038250c456e4a1666223770dc808ca426412222441ba3618a343099844099c42952046d88146ccb242a7cd129a8d333115c62d033b6a8357cf7cd10268ab12f16fceb7975d0a28a6c4822213c9a772df084ad91a669e2040550fc5e8d0aeb10fab2375fc9625ef9cd48c19631997a1cb6455d2c6286c569c9637add0317ce990996b28e51c3f3f717fb5907bbdd53961ad3497f2c3c473cce170906ac4c624a89aa8fbe624d99385e9c9548bf05e8cafd47d2476e41b73001f813726499e88b2b3b6f596ca311657850346598994c40e34747161e4e76264deef2a3019389d1594c942301af47b7544c23ecda2df2dece81e487d8f3f58ea89cd811d7275807ff1b0369ba86470088c174a3099fdafbe5fbb4d158801053b2b435d54059e26dee76d10a7a372f06b0b88b985b32f52052387438be8dc8bc6ae7369e2da9aa5e2585f8de403d091ccb7f790d54ddb34c608b0876f2825e9113be20a2b85867a01bda53287ac780bcd8b606d2e6d7712c56ce0142d22fe6b786de544963e134fecedfafb83d763061d799096a59e30d4472e440ae1faaabdf42640ce69740ceb9cae1a9612c21931b74af3f780236123321b205b6efd6cbb134f4c73d63c0c13e660b59d5920bc33197c355853d8d1cddc7959f7bc500ac81d985016f5b89a0eec79b0d9364ead8e38577c2a6549f2d067cb09438fdb21220aec80f6e22a476f332a2a4a0b7acbeb9e078d2b5a92ae84c924f7cb19fc7df377beb6546af97aa985c747cd111a127a674b4c26d89c14485b82e3a498a12d05406febd6c4d4b8bc051ab2cb91224b078538374b794b7dd9ddf3ac2b4a671fb7b9cf5acb78622ae2709eb2db16943aa24a9c97a81077bc784d25c0ea5991d2de883798a1f0e78f3361ed6a10dded81b1d683658331534fd7c01bc0eb00dfc4c3c84f0693046ff806bb200dd7bd4c0e6abca3f2934b4814fc0e1f8be615a2dda7c8a8d06cf9ce8566b40f4a6543b25bacddc926863fc0fa2007d6d7bf6d18dc98df696bd0865bf0be4c492b8043a32def8e3595ba7da345252f38f95be10fd7fb899b498fa01b09de5d5608eabc44a721aa04c4ef1dcb86102ac5f5f79c9708dcf5c5e896edd8c2c7bde3fa83e6ffce22d66174e31657a0b6361585e669d3031952f08631ae1f16ff90b90d0aad3c6d7e1dd0a9c41ab00a6e1c4f96af9ac5b79fcf821ffc016cb059245fb78dbe6c633d965aaab5333be07195c4b74b18e4600ce783c0a914ef4281016e80a7c9aa92d0fd789879c5e6751125ecb154432311e41cebd4fab3a31e4d2ce22d0f8c67737bf8a0dd85fe1349d5079a4d5feb3fee9378ca47ae46cc58a3f02038cfd53c4cee9cc4270cebc3d115a39c831e8ed41c4dbe4051b51d7872ba0c2bb163e0085201188eaa624a6bea9400a3a1fcc355a57f15704e61fda55a5dbaea8448fa5cb2d377a07f58305ad107e844ab4806e5bf99c1f513ee1d0a2acc04549f0801742169a77971d0adbfbfe0dd2ee5d16bc461e35748d1f3f6f4598321e8c49e79e740f990359858d2729dde007fcb26fdda9aa6e2ec4bd736f2836e7e4c83440191c849f6a53c72a4f8f830d001ea3b18f3cb4a5bd3cf066032b4932cfd2e62a9b55723fa61c688c935518af6860cd649bfbf1bf5fdc1f36dcaefaa157438d1cc8d56a150161511df82631f5e88e773e4ce263f276b7b3678d4c6fc75311d411c0d01bfdb595bb70552838e1b86517c837d909e772b428599e1fe569f77ce61531fde6fd31cdce1bdee4ba467fcbfbb9feeaad99fef67d4906e036c73662ddce158d4e5d4635e5d366f79f31a19d1b3dc4a591b0df194bb06c18147f41d88d1a409becdfb67eb063d16312266fd51b521ba9115e2e5e2aeae6ec511cede13ed4132ffbe0273f6c7039b3874f058804a54809af60557a21d9b4b831d04156a7c22dcbcdfe14f62437f449cb5ef12bf4251d485496cd835c0c2bc58bd845963dfa76ecd68519c4bdaf110be7ab052876dc3407591568c956ea3bf107c90fd5853a292f59a8d4b58b5d3fddf29bdbeac36852e3c69766fe460176a801831292b8e88a74a01ecbbe09a7b4d74cfd7fd628841944d9d556dbd60c76f96f07dc53443805ee9aa09365de4fb8179252c6b099b5dd351fdefc23dbd8090596c5d208ffd2c5661d8e5612dd574fc69045c769a969e600d77cfe192f1d3ae911289355c585811491b0ccd73692ab158824ab9edf8ac8193f0b33e6138b72c6dcd5d344f807b3da92425037de5ea4eead1c795effaa145e2ecdd327606eb2609929b9474b2bb04653602555c068385e92f06f29ca613ce5b4404f01ab1805db0acaa890330d291f40692df382509302b6dc8668f2c8f2d3a44fd58dca26e9802794f73d25b3149e6d576441"); +// byte[] msg = Hex.decode("D81C4D8D734FCBFBEADE3D3F8A039FAA2A2C9957E835AD55B22E75BF57BB556AC8"); +// byte[] s = Hex.decode("42a2ad149a7f35856ed92005232a2d33dc4a1ad0cb0fb7b772f56956082fb9a630fd7284bca55b5cef3a55ad73c2225d7c2d143d0023cd988890b81c271b97e6ed99250bd141550e4eb0276d4a59f19023d0bd725fe2c4f3301655ae91089851db6ecd24bc448ac06c1bbad4254bb1370678858586d55ffa4a9112dd48fd14c224d35d9c2987dc8bff84578d9a5fd0a1e34f7fb34523a305f623cef1766ac8b336c6c1a062f8d273e4f5636969c8c5afb3102436f9549a68ceb5393065944f0a231eb53ef7c6d3bca1fdf2544e3637f5efa96752455e4816d8747c5af14d3996eb241b2dc28fff9a9d93d148193195a87d6763dd94a5d9dfcd8623baf73ecebf545291bd236f44a3b9c5b8d231b7d7e991f6fbd67bbf3740611ef64c66765e25dc0e968900c407565097adf82f7b2387d03f93757a88c1a7590fdca09e19579ecc124629a26e80851b1ca5f29bff6ed37fc779bfc304e93169004b7c742ff4ab9ead2e96e313f1ddd8f6f94d58298ecd2393e119f5536d46e934ff11323f06df447685fbc1f8017a1a98ed717c8c7e4aa9be3b9f0c9f4e43c802c9542a26c013a07f5dcf2cfac584e8a998712cb6f00d4e51f9a3d65bac5197b49bd5291db44fbb90160a364818548b0bb59d34f48fbfc86b7f9d765a427074bee154dacce37f2bae727e99ec55bf7b5d618eebabc73cb015d18c6ba4c45a4c5f8c8802beceb9fd183989f4ccd3964a995a19a4a4492ca043c4be3ff76505d97174db15e15d56acf3e78147c0136373e784d627360e1ad41decdbb5a92cf271cba3a969f366ef53fa1150a1514b18b8c6835a44c9139456c162dfa59e525892e38ad6864097f5108752b4b8d3f847bdc0c185f6da216da8ee00c06ee8b54d66adfa85d2f8851ecbafea5d063604d6abf28a0df4042d788cc539cbfce523f1183dd7c955990ef9709d9db2d28a0ac55382b92b3869ae40072119278e005be9acd8b30507d55a065815db29fe5ad0ded3094d9e92762b1d52a7790e146d4b4b7e81389af5e1bff9485ba72ffebf902aa343e5ad737f57bee177ed8514f0549083407f6a645234be6ece678c59f905e3af7190602e4c1d8815a28e791d476c10ecfbcfc9539e995e72c8cad9f7b515a53e0c912be7071c13c2d350b1965627ec610e17bc52c13108dd3f2e2fd703edf13d76ee62d904f45d6f89b5814a6570ab5e041b14186c63bc0b93de643aa4828ae4747c964474102cfb77aed3412248c67a8fbd2971072058ddda17df2b152449c63b164dd1ca152c893e38afd042d9f186e677969dc3caa6d2105b54d7e8dc47bda7f63606e8670f3f671b0e43d1cf0884cdde011743a9748e50b66cebacfd4595c346a8229883fd92945e65fab2c9a1dad85d6ae11ed3dcd07dbe1bf031fce1c23f5d1fc61dd970b40dec577abd5b2bb697f6b24406ef7d623b45b0a96a79a8171805d599ea99fab55682eba390c0dbd7f53999ca7cd5e4e471139b5e877be6fdcab79ba7cc7693a07bf537f4e05669a977610d2f526e7ed6edf75164b09e6ed608ec755744571694218a36ad96362381fbfb967ec0e0180fb8efd4972c8614f82e262e0628a083f360ed927dc85b9b95d5c53eb371848f3ee1c7dd069918f74e7a1f25fc6f955e72be0202a401e28c7fc20c8378469b6bc370700b6fce04224a3f3815598f15f44ad95972208c215126753db78fa84fac87b62da8b1249360ff2171643cb100c07f8caffbd9aa4d94b0b192eb49af6c9d3b68357d708d597004a178c116efe72f5ec80d2269c592e65eb12b5968f3c153bb900ea3d49a91e155dc38383844bb849f8f78c9038d30ab7b6719830a7667a725f67b6318615b37f0d0a2dedf7e2f741d1807abd4614087449ce789ff10deee23befcac04de3376245143f24df1a1f95d7442439b2e6f983959598c95577e2d262e96f8fa4cc4a1fd59e2b4d9c4394071630c2e0569c4fa3784bfb0d39f42e366c8fee583412be0c6d4c67fff9d570926210fe632fa125245496af25cd084d723994c94e2ff659637784c31e9a555a788c8fc7410839ee1c6e80544d825b79fcf238afd1c0d6be0fa32ecb8b93463d98b9f2b3495c81f25877a613227bdaf8b94342da81c0f2995872a5a75341503bfaec2bb7f95db0f340f4732a832f4effab9bf4da476528a15fbfc5104fe3dae3a5fdd05ebc42989d96f1eb056c3ffc79de35d229a55e301c33975b92c4a7de50962a2fcb83912441189ac1a4e4ad38e30ecc3df084f0ecd8745750323debdea86ab87e725d41fd044fd507f279e7dfbb6a04b34cb150ed9fda95d7393cf8e611589ec56a5dc9a9de4dc80c36e7cbcfb77501bc69b93437ce3642ee35da9a0d71b76a641847fb9798e18b1d073a7b832958f65079648b47370bfc175869dfc412b0b3074fc43d608acd2b602f7b9d2fb831c3a37de56600a34135a1d029bb5f582732b2dc45f992c4a6dcc2c3b1cad807ad4e741490b5cad74a6e7a416fe91b1ad216c428558c3f8d0797d4acca85ca864a5194cf1273622ffeed9624f702b4725a93057a90d155ee081183d87517123647fbd31216b664107a124adef5e1dbedb7e714f6b49696fa21a4a3c2c822cc675b2a171949cd64d10fa188913a9e3318dc9829aa3e6bdeac781afd2b20211c6aeda61deedc8ef7d1426f7cf464af6a700bfce3e0df99f417b1440807870ca0af461cec38cde60e6861f817901a56db98af64e9be3648585513f833a3e5c6fedc613dfff720e76a0800139d53957b1f91e7efcd0e6308613740705e589d48934f5e9a193af901b3335e767310830ebc6662ef8d33e7c87e242b65595c61212f9ac459c09995cf4a996584bf473d4c58db901c2c994f62e6022720987e653d1e3100a84db6e077b9e4387b9df33048d201969b5fb215cb142000bb21e7e5cc3e74b934cfb80e9d117fcecb1c68479390f173cb8e33853f66a51d157287324b3f8a590e40646877c5435e3251fdd5c19791471b51f07c5265dd79aeb2997545e7a3c7b6484f34734871145260d019b28692be1357ff27b9361ff90c1e5f308a1832a900dc915c3771cb83e964e99667e5e46a713c152ce2e33d45ef4050d34671391ec20f93fcb9b9597781e1ca4d4ba1762fced1f9a06311905c6bdda492f72ce9a1c9f20cc26d020aa0a3cede4bf35a7735fe41d1ae0e0ecbb5da8ccd68f37c7acaebc1f8fc764db3bed1dbf4053911b9105e09605de322fb8750717c756330856835ed2c713ff7957f8a99f7ed485e480949b2a9b9d1d8acc0eafeee71977a56a07132c495067aeb7b9dae1e7132036424b7993949cbfd2dedff91b42494a555d6a72757aa2aebfd0f0fb161a617c8890a4a5afc0d1d9dadef300000000000000000000000000000000000000000000000c1a2a39"); +// byte[] seed = Hex.decode("7c9935a0b07694aa0c6d10e4db6b1add2fd81a25ccb148032dcd739936737f2d"); +// +// KeyPairGenerator kpg = KeyPairGenerator.getInstance("HASH-ML-DSA", "BC"); +// SecureRandom katRandom = new NISTSecureRandom(Hex.decode("061550234D158C5EC95595FE04EF7A25767F2E24CC2BC479D09D86DC9ABCFDE7056A8C266F9EF97ED08541DBD2E1FFA1"), null); +// +// kpg.initialize(MLDSAParameterSpec.ml_dsa_44, katRandom); +// +// KeyPair kp = kpg.generateKeyPair(); +// +// SubjectPublicKeyInfo pubInfo = SubjectPublicKeyInfo.getInstance(kp.getPublic().getEncoded()); +// +// ASN1BitString pubSeq = pubInfo.getPublicKeyData(); +// +// assertTrue(Arrays.areEqual(pubSeq.getOctets(), pubK)); +// +// PrivateKeyInfo privInfo = PrivateKeyInfo.getInstance(kp.getPrivate().getEncoded()); +// ASN1OctetString seq = privInfo.getPrivateKey(); +// +// assertTrue(Arrays.areEqual(ASN1OctetString.getInstance(ASN1Sequence.getInstance(seq.getOctets()).getObjectAt(0)).getOctets(), seed)); +// +// Signature sig = Signature.getInstance("HASH-ML-DSA", "BC"); +// +// sig.initSign(kp.getPrivate()); +// +// sig.setParameter(new ContextParameterSpec(Strings.toByteArray("Hello, world!"))); +// +// sig.update(msg, 0, msg.length); +// +// byte[] genS = sig.sign(); +// +// assertTrue(Hex.toHexString(genS), Arrays.areEqual(s, genS)); +// +// sig = Signature.getInstance("HASH-ML-DSA", "BC"); +// +// sig.initVerify(kp.getPublic()); +// +// sig.setParameter(new ContextParameterSpec(Strings.toByteArray("Hello, world!"))); +// +// sig.update(msg, 0, msg.length); +// +// assertTrue(sig.verify(s)); +// +// // check randomisation +// +// sig.initSign(kp.getPrivate(), new SecureRandom()); +// +// sig.setParameter(new ContextParameterSpec(Strings.toByteArray("Hello, world!"))); +// +// sig.update(msg, 0, msg.length); +// +// genS = sig.sign(); +// +// assertFalse(Arrays.areEqual(s, genS)); +// +// sig = Signature.getInstance("HASH-ML-DSA", "BC"); +// +// sig.initVerify(kp.getPublic()); +// +// sig.setParameter(new ContextParameterSpec(Strings.toByteArray("Hello, world!"))); +// +// sig.update(msg, 0, msg.length); +// +// assertTrue(sig.verify(genS)); +// } private static class RiggedRandom extends SecureRandom From 46db1a057cc9b45b9cdd18ad0463bc64bcdf1e91 Mon Sep 17 00:00:00 2001 From: Gefei Li Date: Thu, 29 May 2025 23:41:28 +0000 Subject: [PATCH 395/890] OpenPGP API DoubleBufferedInputStreamTest and DoubleBufferedInputStream --- .../api/DoubleBufferedInputStream.java | 194 ++++++++++++++++++ .../test/DoubleBufferedInputStreamTest.java | 164 +++++++++++++++ .../openpgp/api/test/RegressionTest.java | 2 +- 3 files changed, 359 insertions(+), 1 deletion(-) create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/DoubleBufferedInputStream.java create mode 100644 pg/src/test/java/org/bouncycastle/openpgp/api/test/DoubleBufferedInputStreamTest.java diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/DoubleBufferedInputStream.java b/pg/src/main/java/org/bouncycastle/openpgp/api/DoubleBufferedInputStream.java new file mode 100644 index 0000000000..63254ec488 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/DoubleBufferedInputStream.java @@ -0,0 +1,194 @@ +package org.bouncycastle.openpgp.api; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Implementation of an {@link InputStream} that double-buffers data from an underlying input stream. + * Upon reaching the end of the underlying data stream, the underlying data stream is + * automatically closed. + * Any exceptions while reading from the underlying input stream cause the {@link DoubleBufferedInputStream} + * to withhold pending data. + * This is done in order to minimize the risk of emitting unauthenticated plaintext, while at the same + * time being somewhat resource-efficient. + * The minimum number of bytes to withhold can be configured ({@link #BUFFER_SIZE} by default). + */ +public class DoubleBufferedInputStream + extends InputStream +{ + private static final int BUFFER_SIZE = 1024 * 1024 * 32; // 32 MiB + private byte[] buf1; + private byte[] buf2; + private int b1Pos; + private int b1Max; + private int b2Max; + private final I in; + private boolean closed = false; + + /** + * Create a {@link DoubleBufferedInputStream}, which buffers twice 32MiB. + * + * @param in input stream + */ + public DoubleBufferedInputStream(I in) + { + this(in, BUFFER_SIZE); + } + + /** + * Create a {@link DoubleBufferedInputStream}, which buffers twice the given buffer size in bytes. + * + * @param in input stream + * @param bufferSize buffer size + */ + public DoubleBufferedInputStream(I in, int bufferSize) + { + if (bufferSize <= 0) + { + throw new IllegalArgumentException("Buffer size cannot be zero nor negative."); + } + this.buf1 = new byte[bufferSize]; + this.buf2 = new byte[bufferSize]; + this.in = in; + b1Pos = -1; // indicate to fill() that we need to initialize + } + + /** + * Return the underlying {@link InputStream}. + * + * @return underlying input stream + */ + public I getInputStream() + { + return in; + } + + /** + * Buffer some data from the underlying {@link InputStream}. + * + * @throws IOException re-throw exceptions from the underlying input stream + */ + private void fill() + throws IOException + { + // init + if (b1Pos == -1) + { + // fill both buffers with data + b1Max = in.read(buf1); + b2Max = in.read(buf2); + + if (b2Max == -1) + { + // data fits into b1 -> close underlying stream + close(); + } + + b1Pos = 0; + return; + } + + // no data + if (b1Max <= 0) + { + return; + } + + // Reached end of buf1 + if (b1Pos == b1Max) + { + // swap buffers + byte[] t = buf1; + buf1 = buf2; + buf2 = t; + b1Max = b2Max; + + // reset reader pos + b1Pos = 0; + + // fill buf2 + try + { + b2Max = in.read(buf2); + // could not fill the buffer, or swallowed an IOException + if (b2Max != buf2.length) + { + // provoke the IOException otherwise swallowed by read(buf) + int i = in.read(); + // no exception was thrown, so either data became available, or EOF + if (i != -1) + { + // data became available, push to buf2 + buf2[b2Max++] = (byte)i; + } + } + } + catch (IOException e) + { + // set buffer max's to -1 to indicate to stop emitting data immediately + b1Max = -1; + b2Max = -1; + close(); + + throw e; + } + + // EOF + if (b2Max == -1) + { + close(); + } + } + } + + @Override + public void close() + throws IOException + { + // close the inner stream only once + if (!closed) + { + closed = true; + in.close(); + } + } + + @Override + public int read() + throws IOException + { + // fill the buffer(s) + fill(); + + // EOF / exception? + if (b1Max == -1) + { + close(); + return -1; + } + + // return byte from the buffer + return buf1[b1Pos++]; + } + + @Override + public int read(byte[] b, int off, int len) + throws IOException + { + // Fill the buffer(s) + fill(); + + // EOF / exception? + if (b1Max == -1) + { + close(); + return -1; + } + + int ret = Math.min(b1Max - b1Pos, len); + // emit data from the buffer + System.arraycopy(buf1, b1Pos, b, off, ret); + b1Pos += ret; + return ret; + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/DoubleBufferedInputStreamTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/DoubleBufferedInputStreamTest.java new file mode 100644 index 0000000000..c3c61b3b06 --- /dev/null +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/DoubleBufferedInputStreamTest.java @@ -0,0 +1,164 @@ +package org.bouncycastle.openpgp.api.test; + +import org.bouncycastle.bcpg.test.AbstractPacketTest; +import org.bouncycastle.openpgp.api.DoubleBufferedInputStream; +import org.bouncycastle.util.io.Streams; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class DoubleBufferedInputStreamTest + extends AbstractPacketTest +{ + + @Override + public String getName() + { + return "RetainingInputStreamTest"; + } + + @Override + public void performTest() + throws Exception + { + throwWhileReadingNthBlock(); + successfullyReadSmallerThanBuffer(); + successfullyReadGreaterThanBuffer(); + + throwWhileReadingFirstBlock(); + throwWhileClosing(); + } + + private void successfullyReadSmallerThanBuffer() + throws IOException + { + byte[] bytes = getSequentialBytes(400); + ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); + DoubleBufferedInputStream retIn = new DoubleBufferedInputStream<>(bIn, 512); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + Streams.pipeAll(retIn, bOut); + isEncodingEqual(bytes, bOut.toByteArray()); + } + + private void successfullyReadGreaterThanBuffer() + throws IOException + { + byte[] bytes = getSequentialBytes(2000); + ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); + DoubleBufferedInputStream retIn = new DoubleBufferedInputStream<>(bIn, 512); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + Streams.pipeAll(retIn, bOut); + isEncodingEqual(bytes, bOut.toByteArray()); + } + + private void throwWhileReadingFirstBlock() + { + InputStream throwAfterNBytes = new InputStream() + { + int throwAt = 314; + int r = 0; + + @Override + public int read() + throws IOException + { + int i = r; + if (r == throwAt) + { + throw new IOException("Oopsie"); + } + r++; + return i; + } + }; + DoubleBufferedInputStream retIn = new DoubleBufferedInputStream<>(throwAfterNBytes, 512); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + try + { + Streams.pipeAll(retIn, bOut); + } + catch (IOException e) + { + isEquals("Oopsie", e.getMessage()); + } + isEquals("throwWhileReadingFirstBlock: expected no bytes emitted", 0, bOut.toByteArray().length); + } + + private void throwWhileReadingNthBlock() + { + InputStream throwAfterNBytes = new InputStream() + { + int throwAt = 10; + int r = 0; + + @Override + public int read() + throws IOException + { + int i = r; + if (r == throwAt) + { + throw new IOException("Oopsie"); + } + r++; + return i; + } + }; + DoubleBufferedInputStream retIn = new DoubleBufferedInputStream<>(throwAfterNBytes, 4); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + try + { + Streams.pipeAll(retIn, bOut); + } + catch (IOException e) + { + isEquals("Oopsie", e.getMessage()); + } + byte[] got = bOut.toByteArray(); + isEquals("throwWhileReadingNthBlock: expected 4 bytes emitted. Got " + got.length, 4, got.length); + } + + private void throwWhileClosing() + { + byte[] bytes = getSequentialBytes(100); + ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); + FilterInputStream throwOnClose = new FilterInputStream(bIn) + { + @Override + public void close() + throws IOException + { + throw new IOException("Oopsie"); + } + }; + DoubleBufferedInputStream retIn = new DoubleBufferedInputStream<>(throwOnClose, 512); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + try + { + Streams.pipeAll(retIn, bOut); + } + catch (IOException e) + { + isEquals("Oopsie", e.getMessage()); + } + isEquals("throwWhileClosing: len mismatch", 0, bOut.toByteArray().length); + } + + private byte[] getSequentialBytes(int n) + { + byte[] bytes = new byte[n]; + for (int i = 0; i < bytes.length; i++) + { + bytes[i] = (byte)(i % 128); + } + return bytes; + } + + public static void main(String[] args) + { + runTest(new DoubleBufferedInputStreamTest()); + } +} diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/RegressionTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/RegressionTest.java index 1c52bda358..76d37d3f1d 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/RegressionTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/RegressionTest.java @@ -9,7 +9,7 @@ public class RegressionTest { public static Test[] tests = { new ChangeKeyPassphraseTest(), -// new DoubleBufferedInputStreamTest(), + new DoubleBufferedInputStreamTest(), new OpenPGPCertificateTest(), new OpenPGPDetachedSignatureProcessorTest(), new OpenPGPKeyEditorTest(), From cd2f725542026f820ee09397238818bb6ff2644e Mon Sep 17 00:00:00 2001 From: Gefei Li Date: Thu, 29 May 2025 23:42:52 +0000 Subject: [PATCH 396/890] #2084 pg armoredinputstream --- .../bouncycastle/bcpg/ArmoredInputStream.java | 191 ++++++++++++------ .../openpgp/test/ArmoredInputStreamTest.java | 124 ++++++++++-- 2 files changed, 238 insertions(+), 77 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java index bd9a5f705d..0fedfd41bc 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java @@ -4,6 +4,9 @@ import java.io.EOFException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; import org.bouncycastle.util.StringList; import org.bouncycastle.util.Strings; @@ -75,7 +78,7 @@ private static int decode(int in0, int in1, int in2, int in3, byte[] out) if (in2 == '=') { - b1 = decodingTable[in0] &0xff; + b1 = decodingTable[in0] & 0xff; b2 = decodingTable[in1] & 0xff; if ((b1 | b2) < 0) @@ -129,30 +132,33 @@ else if (in3 == '=') */ private boolean detectMissingChecksum = false; - private final CRC24 crc; - - InputStream in; - boolean start = true; - byte[] outBuf = new byte[3]; - int bufPtr = 3; - boolean crcFound = false; - boolean hasHeaders = true; - String header = null; - boolean newLineFound = false; - boolean clearText = false; - boolean restart = false; - StringList headerList= Strings.newList(); - int lastC = 0; - boolean isEndOfStream; - + private final CRC24 crc; + + InputStream in; + boolean start = true; + byte[] outBuf = new byte[3]; + int bufPtr = 3; + boolean crcFound = false; + boolean hasHeaders = true; + String header = null; + boolean newLineFound = false; + boolean clearText = false; + boolean restart = false; + StringList headerList = Strings.newList(); + int lastC = 0; + boolean isEndOfStream; + + private boolean validateAllowedHeaders = false; + private List allowedHeaders = defaultAllowedHeaders(); + /** - * Create a stream for reading a PGP armoured message, parsing up to a header + * Create a stream for reading a PGP armoured message, parsing up to a header * and then reading the data that follows. - * + * * @param in */ public ArmoredInputStream( - InputStream in) + InputStream in) throws IOException { this(in, true); @@ -160,21 +166,21 @@ public ArmoredInputStream( /** * Create an armoured input stream which will assume the data starts - * straight away, or parse for headers first depending on the value of + * straight away, or parse for headers first depending on the value of * hasHeaders. - * + * * @param in * @param hasHeaders true if headers are to be looked for, false otherwise. */ public ArmoredInputStream( - InputStream in, - boolean hasHeaders) + InputStream in, + boolean hasHeaders) throws IOException { this.in = in; this.hasHeaders = hasHeaders; this.crc = new FastCRC24(); - + if (hasHeaders) { parseHeaders(); @@ -184,40 +190,74 @@ public ArmoredInputStream( } private ArmoredInputStream( - InputStream in, - Builder builder) + InputStream in, + Builder builder) throws IOException { this.in = in; this.hasHeaders = builder.hasHeaders; this.detectMissingChecksum = builder.detectMissingCRC; this.crc = builder.ignoreCRC ? null : new FastCRC24(); + this.validateAllowedHeaders = builder.validateAllowedHeaders; + this.allowedHeaders = builder.allowedHeaders; if (hasHeaders) { parseHeaders(); } + if (validateAllowedHeaders) + { + rejectUnknownHeadersInCSFMessages(); + } + start = false; } + private void rejectUnknownHeadersInCSFMessages() + throws ArmoredInputException + { + Iterator headerLines = headerList.iterator(); + String header = headerLines.next(); + + // Only reject unknown headers in cleartext signed messages + if (!header.startsWith("-----BEGIN PGP SIGNED MESSAGE-----")) + { + return; + } + + outerloop: + while (headerLines.hasNext()) + { + String headerLine = headerLines.next(); + for (Iterator it = allowedHeaders.iterator(); it.hasNext(); ) + { + if (headerLine.startsWith((String)it.next() + ": ")) + { + continue outerloop; + } + } + throw new ArmoredInputException("Illegal ASCII armor header line in clearsigned message encountered: " + headerLine); + } + } + public int available() throws IOException { return in.available(); } - + private boolean parseHeaders() throws IOException { header = null; - - int c; - int last = 0; - boolean headerFound = false; - + + int c; + int last = 0; + boolean headerFound = false; + headerList = Strings.newList(); - + // // if restart we already have a header // @@ -234,15 +274,15 @@ private boolean parseHeaders() headerFound = true; break; } - + last = c; } } if (headerFound) { - boolean eolReached = false; - boolean crLf = false; + boolean eolReached = false; + boolean crLf = false; ByteArrayOutputStream buf = new ByteArrayOutputStream(); buf.write('-'); @@ -251,7 +291,7 @@ private boolean parseHeaders() { buf.write('-'); } - + while ((c = in.read()) >= 0) { if (last == '\r' && c == '\n') @@ -302,10 +342,10 @@ private boolean parseHeaders() eolReached = true; } } - + last = c; } - + if (crLf) { int nl = in.read(); // skip last \n @@ -315,12 +355,12 @@ private boolean parseHeaders() } } } - + if (headerList.size() > 0) { header = headerList.get(0); } - + clearText = "-----BEGIN PGP SIGNED MESSAGE-----".equals(header); newLineFound = true; @@ -346,15 +386,17 @@ public boolean isEndOfStream() /** * Return the armor header line (if there is one) + * * @return the armor header line, null if none present. */ - public String getArmorHeaderLine() + public String getArmorHeaderLine() { return header; } - + /** * Return the armor headers (the lines after the armor header line), + * * @return an array of armor headers, null if there aren't any. */ public String[] getArmorHeaders() @@ -366,12 +408,12 @@ public String[] getArmorHeaders() return headerList.toStringArray(1, headerList.size()); } - - private int readIgnoreSpace() + + private int readIgnoreSpace() throws IOException { - int c = in.read(); - + int c = in.read(); + while (c == ' ' || c == '\t' || c == '\f' || c == '\u000B') // \u000B ~ \v { c = in.read(); @@ -384,11 +426,11 @@ private int readIgnoreSpace() return c; } - + public int read() throws IOException { - int c; + int c; if (start) { @@ -403,7 +445,7 @@ public int read() } start = false; } - + if (clearText) { c = in.read(); @@ -434,25 +476,25 @@ else if (newLineFound && c == '-') newLineFound = false; } } - + lastC = c; if (c < 0) { isEndOfStream = true; } - + return c; } if (bufPtr > 2 || crcFound) { c = readIgnoreSpace(); - + if (c == '\r' || c == '\n') { c = readIgnoreSpace(); - + while (c == '\n' || c == '\r') { c = readIgnoreSpace(); @@ -542,24 +584,24 @@ else if (newLineFound && c == '-') * an array of bytes. An attempt is made to read as many as * len bytes, but a smaller number may be read. * The number of bytes actually read is returned as an integer. - * + *

    * The first byte read is stored into element b[off], the * next one into b[off+1], and so on. The number of bytes read * is, at most, equal to len. - * + *

    * NOTE: We need to override the custom behavior of Java's {@link InputStream#read(byte[], int, int)}, * as the upstream method silently swallows {@link IOException IOExceptions}. * This would cause CRC checksum errors to go unnoticed. * - * @see Related BC bug report - * @param b byte array + * @param b byte array * @param off offset at which we start writing data to the array * @param len number of bytes we write into the array * @return total number of bytes read into the buffer - * * @throws IOException if an exception happens AT ANY POINT + * @see Related BC bug report */ - public int read(byte[] b, int off, int len) throws IOException + public int read(byte[] b, int off, int len) + throws IOException { checkIndexSize(b.length, off, len); @@ -576,7 +618,7 @@ public int read(byte[] b, int off, int len) throws IOException b[off] = (byte)c; int i = 1; - for (; i < len ; i++) + for (; i < len; i++) { c = read(); if (c == -1) @@ -619,6 +661,17 @@ public void setDetectMissingCRC(boolean detectMissing) this.detectMissingChecksum = detectMissing; } + private static List defaultAllowedHeaders() + { + List allowedHeaders = new ArrayList<>(); + allowedHeaders.add(ArmoredOutputStream.COMMENT_HDR); + allowedHeaders.add(ArmoredOutputStream.VERSION_HDR); + allowedHeaders.add(ArmoredOutputStream.CHARSET_HDR); + allowedHeaders.add(ArmoredOutputStream.HASH_HDR); + allowedHeaders.add(ArmoredOutputStream.MESSAGE_ID_HDR); + return allowedHeaders; + } + public static Builder builder() { return new Builder(); @@ -629,6 +682,8 @@ public static class Builder private boolean hasHeaders = true; private boolean detectMissingCRC = false; private boolean ignoreCRC = false; + private boolean validateAllowedHeaders = false; + private List allowedHeaders = defaultAllowedHeaders(); private Builder() { @@ -648,6 +703,18 @@ public Builder setParseForHeaders(boolean hasHeaders) return this; } + public Builder setValidateClearsignedMessageHeaders(boolean validateHeaders) + { + this.validateAllowedHeaders = validateHeaders; + return this; + } + + public Builder addAllowedArmorHeader(String header) + { + allowedHeaders.add(header.trim()); + return this; + } + /** * Change how the stream should react if it encounters missing CRC checksum. * The default value is false (ignore missing CRC checksums). If the behavior is set to true, diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/ArmoredInputStreamTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/ArmoredInputStreamTest.java index 8e575bb6c9..1dae858f1f 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/ArmoredInputStreamTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/ArmoredInputStreamTest.java @@ -2,11 +2,15 @@ import java.io.ByteArrayInputStream; import java.io.IOException; +import java.nio.charset.StandardCharsets; import java.security.Security; import org.bouncycastle.bcpg.ArmoredInputStream; +import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.openpgp.PGPObjectFactory; +import org.bouncycastle.openpgp.PGPSignatureList; +import org.bouncycastle.openpgp.bc.BcPGPObjectFactory; import org.bouncycastle.openpgp.operator.jcajce.JcaKeyFingerprintCalculator; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; @@ -39,22 +43,22 @@ public class ArmoredInputStreamTest "bc2af032d2a59e36be6467bc23456b4ac178d36cf9f45df5e833a1981ed1a1032679ea0a"); private static final String badHeaderData1 = - "-----BEGIN PGP MESSAGE-----\n" - + "Version: BCPG v1.32\n" - + "Comment: A dummy message\n" - + "Comment actually not really as there is no colon" - + " \t \t\n" - + "SGVsbG8gV29ybGQh\n" - + "=d9Xi\n" - + "-----END PGP MESSAGE-----\n"; + "-----BEGIN PGP MESSAGE-----\n" + + "Version: BCPG v1.32\n" + + "Comment: A dummy message\n" + + "Comment actually not really as there is no colon" + + " \t \t\n" + + "SGVsbG8gV29ybGQh\n" + + "=d9Xi\n" + + "-----END PGP MESSAGE-----\n"; private static final String badHeaderData2 = - "-----BEGIN PGP MESSAGE-----\n" - + "Comment actually not really as there is no colon" - + " \t \t\n" - + "SGVsbG8gV29ybGQh\n" - + "=d9Xi\n" - + "-----END PGP MESSAGE-----\n"; + "-----BEGIN PGP MESSAGE-----\n" + + "Comment actually not really as there is no colon" + + " \t \t\n" + + "SGVsbG8gV29ybGQh\n" + + "=d9Xi\n" + + "-----END PGP MESSAGE-----\n"; public String getName() { @@ -63,12 +67,18 @@ public String getName() public void performTest() throws Exception + { + bogusHeadersTest(); + unknownClearsignedMessageHeadersTest(); + } + + private void bogusHeadersTest() { try { PGPObjectFactory pgpObjectFactoryOfTestFile = new PGPObjectFactory( new ArmoredInputStream(new ByteArrayInputStream(Arrays.concatenate(Strings.toByteArray("-----BEGIN PGP MESSAGE-----\n" - + "Version: BCPG v1.32\n\n"), bogusData))), new JcaKeyFingerprintCalculator()); + + "Version: BCPG v1.32\n\n"), bogusData))), new JcaKeyFingerprintCalculator()); pgpObjectFactoryOfTestFile.nextObject(); // <-- EXCEPTION HERE fail("no exception"); } @@ -100,6 +110,90 @@ public void performTest() } } + private void unknownClearsignedMessageHeadersTest() + throws IOException + { + // https://sequoia-pgp.gitlab.io/openpgp-interoperability-test-suite/results.html#Mangled_message_using_the_Cleartext_Signature_Framework_ + String armor = "-----BEGIN PGP SIGNED MESSAGE-----\n" + + "Hello: this is totally part of the signed text\n" + + "Hash: SHA512\n" + + "\n" + + "- From the grocery store we need:\n" + + "\n" + + "- - tofu\n" + + "- - vegetables\n" + + "- - noodles\n" + + "\n" + + "\n" + + "-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wsE7BAEBCgBvBYJoMZ08CRD7/MgqAV5zMEcUAAAAAAAeACBzYWx0QG5vdGF0aW9u\n" + + "cy5zZXF1b2lhLXBncC5vcmeO1uFIMk5ydOB8SNGi9ZkD0sHEoFRZM20v669ghBur\n" + + "KBYhBNGmbhojsYLJmA94jPv8yCoBXnMwAACffQwArOoXVWEF/Yii182hZPqE6t/E\n" + + "ZEyJcZLwsJXQ00ctno0TjXY9iDS0l1i0cWVIIcgkoutd+Gn8XI30EQEJivAs8uvE\n" + + "yCDFRQgkag2kOn+QtawyQ3LO+Xd5oZDbcy9Jvf4sG5YobBs7kfTb2NQgXDViM+k3\n" + + "69je5Mj+oKhtckM3BROYxq+B8DPgPT9UJuz0UgFQVYm5Mjj9jnFlUbMVl7UnsZwP\n" + + "0RNnbW8jtuQn7ehePzAOB94bzkvJL8/obPw2LsDfC0gWTovpJo0JibPZD/zaTA4y\n" + + "7yLnRvEM+8PilR6eIY40Us9oJerpjYsA16WMyIEvRfgHrYITpqHEzpJa7/vnMF2g\n" + + "t2PjcdtFeBsmJZrLwaJWB5Tku6wMsVL8Rmit8qecnVg9qYL3FrRUweEGo/dAH49M\n" + + "udZeck+sMaXdIhJnwy4HnH0tUiEGnHQ5mnBtTvKFR98paDVIW/xS+o95hUfmAXA8\n" + + "rmMglLYQkIXAZayAquW+VrxSxglNqXYxZNIxuHT6\n" + + "=yj77\n" + + "-----END PGP SIGNATURE-----"; + + // Test validation is not enabled by default + ByteArrayInputStream bIn = new ByteArrayInputStream(armor.getBytes(StandardCharsets.UTF_8)); + ArmoredInputStream aIn = ArmoredInputStream.builder() + .build(bIn); + // Skip over cleartext + isTrue(aIn.isClearText()); + while (aIn.isClearText()) + { + aIn.read(); + } + BCPGInputStream pIn = new BCPGInputStream(aIn); + PGPObjectFactory objFac = new BcPGPObjectFactory(pIn); + PGPSignatureList sigs = (PGPSignatureList)objFac.nextObject(); + isTrue(sigs != null); + + + // Test validation enabled + bIn = new ByteArrayInputStream(armor.getBytes(StandardCharsets.UTF_8)); + ByteArrayInputStream finalBIn = bIn; + isTrue(null != testException( + "Illegal ASCII armor header line in clearsigned message encountered: Hello: this is totally part of the signed text", + "ArmoredInputException", + new TestExceptionOperation() + { + public void operation() + throws Exception + { + ArmoredInputStream.builder() + .setValidateClearsignedMessageHeaders(true) + .build(finalBIn); + } + }) + ); + + + // Test validation enabled, but custom header allowed + bIn = new ByteArrayInputStream(armor.getBytes(StandardCharsets.UTF_8)); + aIn = ArmoredInputStream.builder() + .setValidateClearsignedMessageHeaders(true) + .addAllowedArmorHeader("Hello") + .build(bIn); + // Skip over cleartext + isTrue(aIn.isClearText()); + while (aIn.isClearText()) + { + aIn.read(); + } + pIn = new BCPGInputStream(aIn); + objFac = new BcPGPObjectFactory(pIn); + sigs = (PGPSignatureList)objFac.nextObject(); + isTrue(sigs != null); + } + public static void main( String[] args) { From 7cfde6b09ae289bbcf1583e1747c9598e3f66394 Mon Sep 17 00:00:00 2001 From: Gefei Li Date: Fri, 30 May 2025 11:16:59 +0000 Subject: [PATCH 397/890] Update KeyIdentifier and related classes, update FingerprintUtil, remove OpenPGPV6KeyGenerator --- .../bouncycastle/bcpg/FingerprintUtil.java | 4 +- .../org/bouncycastle/bcpg/KeyIdentifier.java | 7 +- .../openpgp/PGPPublicKeyRing.java | 4 +- .../openpgp/PGPSecretKeyRing.java | 12 +- .../bouncycastle/openpgp/PGPSignature.java | 2 +- .../openpgp/api/OpenPGPCertificate.java | 20 +- .../openpgp/api/OpenPGPV6KeyGenerator.java | 1248 ----------------- .../openpgp/api/package-info.java | 23 + .../openpgp/test/KeyIdentifierTest.java | 12 + 9 files changed, 71 insertions(+), 1261 deletions(-) delete mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPV6KeyGenerator.java create mode 100644 pg/src/main/java/org/bouncycastle/openpgp/api/package-info.java diff --git a/pg/src/main/java/org/bouncycastle/bcpg/FingerprintUtil.java b/pg/src/main/java/org/bouncycastle/bcpg/FingerprintUtil.java index adf16a67c0..9e249b3983 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/FingerprintUtil.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/FingerprintUtil.java @@ -3,6 +3,8 @@ import org.bouncycastle.util.Pack; import org.bouncycastle.util.encoders.Hex; +import java.util.Locale; + public class FingerprintUtil { @@ -141,7 +143,7 @@ public static void writeKeyID(long keyID, byte[] bytes) public static String prettifyFingerprint(byte[] fingerprint) { // -DM Hex.toHexString - char[] hex = Hex.toHexString(fingerprint).toUpperCase().toCharArray(); + char[] hex = Hex.toHexString(fingerprint).toUpperCase(Locale.getDefault()).toCharArray(); StringBuilder sb = new StringBuilder(); switch (hex.length) { diff --git a/pg/src/main/java/org/bouncycastle/bcpg/KeyIdentifier.java b/pg/src/main/java/org/bouncycastle/bcpg/KeyIdentifier.java index 3dc86a4b34..9398792c2a 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/KeyIdentifier.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/KeyIdentifier.java @@ -170,6 +170,11 @@ public boolean matches(KeyIdentifier other) return true; } + return matchesExplicit(other); + } + + public boolean matchesExplicit(KeyIdentifier other) + { if (fingerprint != null && other.fingerprint != null) { return Arrays.constantTimeAreEqual(fingerprint, other.fingerprint); @@ -214,7 +219,7 @@ public boolean isPresentIn(List others) { for (Iterator it = others.iterator(); it.hasNext();) { - if (this.matches((KeyIdentifier)it.next())) + if (this.matchesExplicit((KeyIdentifier)it.next())) { return true; } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyRing.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyRing.java index d113d3bde2..10f8b29327 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyRing.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyRing.java @@ -201,7 +201,7 @@ public PGPPublicKey getPublicKey(KeyIdentifier identifier) for (Iterator it = keys.iterator(); it.hasNext();) { PGPPublicKey k = (PGPPublicKey)it.next(); - if (identifier.matches(k.getKeyIdentifier())) + if (identifier.matchesExplicit(k.getKeyIdentifier())) { return k; } @@ -216,7 +216,7 @@ public Iterator getPublicKeys(KeyIdentifier identifier) for (Iterator it = keys.iterator(); it.hasNext();) { PGPPublicKey k = (PGPPublicKey)it.next(); - if (identifier.matches(k.getKeyIdentifier())) + if (identifier.matchesExplicit(k.getKeyIdentifier())) { matches.add(k); } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKeyRing.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKeyRing.java index a9c30d49df..8319fcb4c7 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKeyRing.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKeyRing.java @@ -263,7 +263,7 @@ public PGPPublicKey getPublicKey(KeyIdentifier identifier) for (Iterator it = keys.iterator(); it.hasNext();) { PGPSecretKey k = (PGPSecretKey)it.next(); - if (k.getPublicKey() != null && identifier.matches(k.getKeyIdentifier())) + if (k.getPublicKey() != null && identifier.matchesExplicit(k.getKeyIdentifier())) { return k.getPublicKey(); } @@ -272,7 +272,7 @@ public PGPPublicKey getPublicKey(KeyIdentifier identifier) for (Iterator it = extraPubKeys.iterator(); it.hasNext();) { PGPPublicKey k = (PGPPublicKey)it.next(); - if (identifier.matches(k.getKeyIdentifier())) + if (identifier.matchesExplicit(k.getKeyIdentifier())) { return k; } @@ -287,7 +287,7 @@ public Iterator getPublicKeys(KeyIdentifier identifier) for (Iterator it = keys.iterator(); it.hasNext();) { PGPSecretKey k = (PGPSecretKey)it.next(); - if (k.getPublicKey() != null && identifier.matches(k.getKeyIdentifier())) + if (k.getPublicKey() != null && identifier.matchesExplicit(k.getKeyIdentifier())) { matches.add(k.getPublicKey()); } @@ -296,7 +296,7 @@ public Iterator getPublicKeys(KeyIdentifier identifier) for (Iterator it = extraPubKeys.iterator(); it.hasNext();) { PGPPublicKey k = (PGPPublicKey)it.next(); - if (identifier.matches(k.getKeyIdentifier())) + if (identifier.matchesExplicit(k.getKeyIdentifier())) { matches.add(k); } @@ -309,7 +309,7 @@ public PGPSecretKey getSecretKey(KeyIdentifier identifier) for (Iterator it = keys.iterator(); it.hasNext();) { PGPSecretKey k = (PGPSecretKey)it.next(); - if (identifier.matches(k.getKeyIdentifier())) + if (identifier.matchesExplicit(k.getKeyIdentifier())) { return k; } @@ -323,7 +323,7 @@ public Iterator getSecretKeys(KeyIdentifier identifier) for (Iterator it = keys.iterator(); it.hasNext();) { PGPSecretKey k = (PGPSecretKey)it.next(); - if (identifier.matches(k.getKeyIdentifier())) + if (identifier.matchesExplicit(k.getKeyIdentifier())) { matches.add(k); } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java index 0107154a7b..7688b94600 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSignature.java @@ -654,7 +654,7 @@ public boolean hasKeyIdentifier(KeyIdentifier identifier) { for (Iterator it = getKeyIdentifiers().iterator(); it.hasNext(); ) { - if (((KeyIdentifier)it.next()).matches(identifier)) + if (((KeyIdentifier)it.next()).matchesExplicit(identifier)) { return true; } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java index 925d99da6f..267cfd72ce 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPCertificate.java @@ -303,7 +303,7 @@ public boolean test(OpenPGPComponentKey key, Date time) */ public OpenPGPComponentKey getKey(KeyIdentifier identifier) { - if (identifier.matches(getPrimaryKey().getPGPPublicKey().getKeyIdentifier())) + if (identifier.matchesExplicit(getPrimaryKey().getPGPPublicKey().getKeyIdentifier())) { return primaryKey; } @@ -636,7 +636,6 @@ public String toAsciiArmoredString() public String toAsciiArmoredString(PacketFormat packetFormat) throws IOException { - ByteArrayOutputStream bOut = new ByteArrayOutputStream(); ArmoredOutputStream.Builder armorBuilder = ArmoredOutputStream.builder() .clearHeaders(); // Add fingerprint comment @@ -648,7 +647,24 @@ public String toAsciiArmoredString(PacketFormat packetFormat) armorBuilder.addEllipsizedComment(it.next().getUserId()); } + return toAsciiArmoredString(packetFormat, armorBuilder); + } + + /** + * Return an ASCII armored {@link String} containing the certificate. + * The {@link ArmoredOutputStream.Builder} can be used to customize the ASCII armor (headers, CRC etc.). + * + * @param packetFormat packet length encoding format + * @param armorBuilder builder for the ASCII armored output stream + * @return armored certificate + * @throws IOException if the cert cannot be encoded + */ + public String toAsciiArmoredString(PacketFormat packetFormat, ArmoredOutputStream.Builder armorBuilder) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); ArmoredOutputStream aOut = armorBuilder.build(bOut); + aOut.write(getEncoded(packetFormat)); aOut.close(); return bOut.toString(); diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPV6KeyGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPV6KeyGenerator.java deleted file mode 100644 index e512b3a3e8..0000000000 --- a/pg/src/main/java/org/bouncycastle/openpgp/api/OpenPGPV6KeyGenerator.java +++ /dev/null @@ -1,1248 +0,0 @@ -package org.bouncycastle.openpgp.api; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Date; -import java.util.Iterator; -import java.util.List; - -import org.bouncycastle.bcpg.AEADAlgorithmTags; -import org.bouncycastle.bcpg.CompressionAlgorithmTags; -import org.bouncycastle.bcpg.HashAlgorithmTags; -import org.bouncycastle.bcpg.PublicKeyPacket; -import org.bouncycastle.bcpg.PublicKeyUtils; -import org.bouncycastle.bcpg.PublicSubkeyPacket; -import org.bouncycastle.bcpg.S2K; -import org.bouncycastle.bcpg.SignatureSubpacketTags; -import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; -import org.bouncycastle.bcpg.sig.Features; -import org.bouncycastle.bcpg.sig.KeyFlags; -import org.bouncycastle.bcpg.sig.PreferredAEADCiphersuites; -import org.bouncycastle.openpgp.PGPException; -import org.bouncycastle.openpgp.PGPKeyPair; -import org.bouncycastle.openpgp.PGPPublicKey; -import org.bouncycastle.openpgp.PGPSecretKey; -import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.PGPSignature; -import org.bouncycastle.openpgp.PGPSignatureGenerator; -import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator; -import org.bouncycastle.openpgp.operator.KeyFingerPrintCalculator; -import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor; -import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptorFactory; -import org.bouncycastle.openpgp.operator.PGPContentSignerBuilderProvider; -import org.bouncycastle.openpgp.operator.PGPDigestCalculatorProvider; -import org.bouncycastle.openpgp.operator.PGPKeyPairGenerator; -import org.bouncycastle.openpgp.operator.PGPKeyPairGeneratorProvider; -import org.bouncycastle.util.Arrays; - -/** - * High-level generator class for OpenPGP v6 keys. - */ -public class OpenPGPV6KeyGenerator -{ - /** - * Hash algorithm for key signatures if no other one is provided during construction. - */ - public static final int DEFAULT_SIGNATURE_HASH_ALGORITHM = HashAlgorithmTags.SHA3_512; - - // SECONDS - private static final long SECONDS_PER_MINUTE = 60; - private static final long SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE; - private static final long SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR; - private static final long SECONDS_PER_YEAR = 365 * SECONDS_PER_DAY; - - /** - * Standard AEAD encryption preferences (SEIPDv2). - * By default, only announce support for OCB + AES. - */ - public static SignatureSubpacketsFunction DEFAULT_AEAD_ALGORITHM_PREFERENCES = new SignatureSubpacketsFunction() - { - public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) - { - subpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS); - subpackets.setPreferredAEADCiphersuites(PreferredAEADCiphersuites.builder(false) - .addCombination(SymmetricKeyAlgorithmTags.AES_256, AEADAlgorithmTags.OCB) - .addCombination(SymmetricKeyAlgorithmTags.AES_192, AEADAlgorithmTags.OCB) - .addCombination(SymmetricKeyAlgorithmTags.AES_128, AEADAlgorithmTags.OCB)); - return subpackets; - } - }; - - /** - * Standard symmetric-key encryption preferences (SEIPDv1). - * By default, announce support for AES. - */ - public static SignatureSubpacketsFunction DEFAULT_SYMMETRIC_KEY_PREFERENCES = new SignatureSubpacketsFunction() - { - public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) - { - subpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_SYM_ALGS); - subpackets.setPreferredSymmetricAlgorithms(false, new int[]{ - SymmetricKeyAlgorithmTags.AES_256, SymmetricKeyAlgorithmTags.AES_192, SymmetricKeyAlgorithmTags.AES_128 - }); - return subpackets; - } - }; - - /** - * Standard signature hash algorithm preferences. - * By default, only announce SHA3 and SHA2 algorithms. - */ - public static SignatureSubpacketsFunction DEFAULT_HASH_ALGORITHM_PREFERENCES = new SignatureSubpacketsFunction() - { - public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) - { - subpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_HASH_ALGS); - subpackets.setPreferredHashAlgorithms(false, new int[]{ - HashAlgorithmTags.SHA3_512, HashAlgorithmTags.SHA3_256, - HashAlgorithmTags.SHA512, HashAlgorithmTags.SHA384, HashAlgorithmTags.SHA256 - }); - return subpackets; - } - }; - - /** - * Standard compression algorithm preferences. - * By default, announce support for all known algorithms. - */ - public static SignatureSubpacketsFunction DEFAULT_COMPRESSION_ALGORITHM_PREFERENCES = new SignatureSubpacketsFunction() - { - public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) - { - subpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_COMP_ALGS); - subpackets.setPreferredCompressionAlgorithms(false, new int[]{ - CompressionAlgorithmTags.UNCOMPRESSED, CompressionAlgorithmTags.ZIP, - CompressionAlgorithmTags.ZLIB, CompressionAlgorithmTags.BZIP2 - }); - return subpackets; - } - }; - - /** - * Standard features to announce. - * By default, announce SEIPDv1 (modification detection) and SEIPDv2. - */ - public static SignatureSubpacketsFunction DEFAULT_FEATURES = new SignatureSubpacketsFunction() - { - public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) - { - subpackets.removePacketsOfType(SignatureSubpacketTags.FEATURES); - subpackets.setFeature(false, (byte)(Features.FEATURE_MODIFICATION_DETECTION | Features.FEATURE_SEIPD_V2)); - return subpackets; - } - }; - - /** - * Standard signature subpackets for signing subkey's binding signatures. - * Sets the keyflag subpacket to SIGN_DATA. - */ - public static SignatureSubpacketsFunction SIGNING_SUBKEY_SUBPACKETS = new SignatureSubpacketsFunction() - { - public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) - { - subpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS); - subpackets.setKeyFlags(true, KeyFlags.SIGN_DATA); - return subpackets; - } - }; - - /** - * Standard signature subpackets for encryption subkey's binding signatures. - * Sets the keyflag subpacket to ENCRYPT_STORAGE|ENCRYPT_COMMS. - */ - public static SignatureSubpacketsFunction ENCRYPTION_SUBKEY_SUBPACKETS = new SignatureSubpacketsFunction() - { - public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) - { - subpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS); - subpackets.setKeyFlags(true, KeyFlags.ENCRYPT_STORAGE | KeyFlags.ENCRYPT_COMMS); - return subpackets; - } - }; - - /** - * Standard signature subpackets for the direct-key signature. - * Sets default features, hash-, compression-, symmetric-key-, and AEAD algorithm preferences. - */ - public static SignatureSubpacketsFunction DIRECT_KEY_SIGNATURE_SUBPACKETS = new SignatureSubpacketsFunction() - { - public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) - { - subpackets = DEFAULT_FEATURES.apply(subpackets); - subpackets = DEFAULT_HASH_ALGORITHM_PREFERENCES.apply(subpackets); - subpackets = DEFAULT_COMPRESSION_ALGORITHM_PREFERENCES.apply(subpackets); - subpackets = DEFAULT_SYMMETRIC_KEY_PREFERENCES.apply(subpackets); - subpackets = DEFAULT_AEAD_ALGORITHM_PREFERENCES.apply(subpackets); - return subpackets; - } - }; - - private final Implementation impl; // contains BC or JCA/JCE implementations - private final Configuration conf; - - /** - * Generate a new OpenPGP key generator for v6 keys. - * - * @param kpGenProvider key pair generator provider - * @param contentSignerBuilderProvider content signer builder provider - * @param digestCalculatorProvider digest calculator provider - * @param keyEncryptionBuilderProvider secret key encryption builder provider (AEAD) - * @param keyFingerPrintCalculator calculator for key fingerprints - * @param creationTime key creation time - */ - public OpenPGPV6KeyGenerator( - PGPKeyPairGeneratorProvider kpGenProvider, - PGPContentSignerBuilderProvider contentSignerBuilderProvider, - PGPDigestCalculatorProvider digestCalculatorProvider, - PBESecretKeyEncryptorFactory keyEncryptionBuilderProvider, - KeyFingerPrintCalculator keyFingerPrintCalculator, - Date creationTime) - { - this.impl = new Implementation(kpGenProvider, contentSignerBuilderProvider, digestCalculatorProvider, keyEncryptionBuilderProvider, keyFingerPrintCalculator); - this.conf = new Configuration(new Date((creationTime.getTime() / 1000) * 1000)); - } - - /** - * Generate an OpenPGP key consisting of a certify-only primary key, - * a dedicated signing-subkey and dedicated encryption-subkey. - * The key will carry the provided user-id and be protected using the provided passphrase. - * See {@link PGPKeyPairGenerator#generatePrimaryKey()} for the primary key type, - * {@link PGPKeyPairGenerator#generateSigningSubkey()} for the signing-subkey type and - * {@link PGPKeyPairGenerator#generateEncryptionSubkey()} for the encryption-subkey key type. - * - * @param userId user id - * @param passphrase nullable passphrase. - * @return OpenPGP key - * @throws PGPException if the key cannot be generated - */ - public PGPSecretKeyRing classicKey(String userId, char[] passphrase) - throws PGPException - { - return withPrimaryKey() - .addUserId(userId) - .addSigningSubkey() - .addEncryptionSubkey() - .build(passphrase); - } - - /** - * Generate an OpenPGP key consisting of an Ed25519 certify-only primary key, - * a dedicated Ed25519 sign-only subkey and dedicated X25519 encryption-only subkey. - * The key will carry the provided user-id and be protected using the provided passphrase. - * - * @param userId user id - * @param passphrase nullable passphrase - * @return OpenPGP key - * @throws PGPException if the key cannot be generated - */ - public PGPSecretKeyRing ed25519x25519Key(String userId, char[] passphrase) - throws PGPException - { - return withPrimaryKey(new KeyPairGeneratorCallback() - { - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException - { - return generator.generateEd25519KeyPair(); - } - }) - .addSigningSubkey(new KeyPairGeneratorCallback() - { - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException - { - return generator.generateEd25519KeyPair(); - } - }) - .addEncryptionSubkey(new KeyPairGeneratorCallback() - { - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException - { - return generator.generateX25519KeyPair(); - } - }) - .addUserId(userId) - .build(passphrase); - } - - - /** - * Generate an OpenPGP key consisting of an Ed448 certify-only primary key, - * a dedicated Ed448 sign-only subkey and dedicated X448 encryption-only subkey. - * The key will carry the provided user-id and be protected using the provided passphrase. - * - * @param userId user id - * @param passphrase nullable passphrase - * @return OpenPGP key - * @throws PGPException if the key cannot be generated - */ - public PGPSecretKeyRing ed448x448Key(String userId, char[] passphrase) - throws PGPException - { - return withPrimaryKey(new KeyPairGeneratorCallback() - { - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException - { - return generator.generateEd448KeyPair(); - } - }) - .addSigningSubkey(new KeyPairGeneratorCallback() - { - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException - { - return generator.generateEd448KeyPair(); - } - }) - .addEncryptionSubkey(new KeyPairGeneratorCallback() - { - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException - { - return generator.generateX448KeyPair(); - } - }) - .addUserId(userId) - .build(passphrase); - } - - /** - * Generate a sign-only OpenPGP key. - * The key consists of a single, user-id-less primary key, which is capable of signing and certifying. - * See {@link PGPKeyPairGenerator#generatePrimaryKey()} for the key type. - * - * @param passphrase nullable passphrase to protect the key with - * @return sign-only (+certify) OpenPGP key - * @throws PGPException if the key cannot be generated - */ - public PGPSecretKeyRing signOnlyKey(char[] passphrase) - throws PGPException - { - return signOnlyKey(passphrase, null); - } - - /** - * Generate a sign-only OpenPGP key. - * The key consists of a single, user-id-less primary key, which is capable of signing and certifying. - * It carries a single direct-key signature with signing-related preferences whose subpackets can be - * modified by providing a {@link SignatureSubpacketsFunction}. - * - * @param passphrase nullable passphrase to protect the key with - * @param userSubpackets callback to modify the direct-key signature subpackets with - * @return sign-only (+certify) OpenPGP key - * @throws PGPException if the key cannot be generated - */ - public PGPSecretKeyRing signOnlyKey( - char[] passphrase, - SignatureSubpacketsFunction userSubpackets) - throws PGPException - { - PGPKeyPair primaryKeyPair = impl.kpGenProvider.get(PublicKeyPacket.VERSION_6, conf.keyCreationTime) - .generatePrimaryKey(); - PBESecretKeyEncryptor encryptor = impl.keyEncryptorBuilderProvider - .build(passphrase, primaryKeyPair.getPublicKey().getPublicKeyPacket()); - return signOnlyKey(primaryKeyPair, encryptor, userSubpackets); - } - - /** - * Generate a sign-only OpenPGP key. - * The key consists of a single, user-id-less primary key, which is capable of signing and certifying. - * It carries a single direct-key signature with signing-related preferences whose subpackets can be - * modified by providing a {@link SignatureSubpacketsFunction}. - * - * @param primaryKeyPair signing-capable primary key - * @param keyEncryptor nullable encryptor to protect the primary key with - * @param userSubpackets callback to modify the direct-key signature subpackets with - * @return sign-only (+certify) OpenPGP key - * @throws PGPException if the key cannot be generated - */ - public PGPSecretKeyRing signOnlyKey( - PGPKeyPair primaryKeyPair, - PBESecretKeyEncryptor keyEncryptor, - SignatureSubpacketsFunction userSubpackets) - throws PGPException - { - if (primaryKeyPair.getPublicKey().getPublicKeyPacket() instanceof PublicSubkeyPacket) - { - throw new IllegalArgumentException("Primary key MUST NOT consist of subkey packet."); - } - - return primaryKeyWithDirectKeySig(primaryKeyPair, - new SignatureSubpacketsFunction() - { - public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator baseSubpackets) - { - // remove unrelated subpackets not needed for sign-only keys - baseSubpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_AEAD_ALGORITHMS); - baseSubpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_SYM_ALGS); - baseSubpackets.removePacketsOfType(SignatureSubpacketTags.PREFERRED_COMP_ALGS); - - // replace key flags -> CERTIFY_OTHER|SIGN_DATA - baseSubpackets.removePacketsOfType(SignatureSubpacketTags.KEY_FLAGS); - baseSubpackets.setKeyFlags(true, KeyFlags.CERTIFY_OTHER | KeyFlags.SIGN_DATA); - return baseSubpackets; - } - }, - userSubpackets, // apply user-provided subpacket changes - keyEncryptor) - .build(); - } - - /** - * Generate an OpenPGP key with a certification-capable primary key. - * See {@link PGPKeyPairGenerator#generatePrimaryKey()} for the primary key type - * - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey withPrimaryKey() - throws PGPException - { - return withPrimaryKey((SignatureSubpacketsFunction)null); - } - - public WithPrimaryKey withPrimaryKey( - KeyPairGeneratorCallback keyGenCallback) - throws PGPException - { - return withPrimaryKey(keyGenCallback, null); - } - - /** - * Generate an OpenPGP key with a certification-capable primary key. - * See {@link PGPKeyPairGenerator#generatePrimaryKey()} for the primary key type - * The key will carry a direct-key signature, whose subpackets can be modified by overriding the - * given {@link SignatureSubpacketsFunction}. - * - * @param directKeySubpackets nullable callback to modify the direct-key signatures subpackets - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey withPrimaryKey( - SignatureSubpacketsFunction directKeySubpackets) - throws PGPException - { - return withPrimaryKey( - new KeyPairGeneratorCallback() - { - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException - { - return generator.generatePrimaryKey(); - } - }, - directKeySubpackets); - } - - /** - * Generate an OpenPGP key with a certification-capable primary key. - * The {@link KeyPairGeneratorCallback} can be used to specify the primary key type. - * The key will carry a direct-key signature, whose subpackets can be modified by overriding the - * given {@link SignatureSubpacketsFunction}. - * - * @param keyGenCallback callback to specify the primary key type - * @param directKeySubpackets nullable callback to modify the direct-key signatures subpackets - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey withPrimaryKey( - KeyPairGeneratorCallback keyGenCallback, - SignatureSubpacketsFunction directKeySubpackets) - throws PGPException - { - return withPrimaryKey(keyGenCallback, directKeySubpackets, null); - } - - /** - * Generate an OpenPGP key with a certification-capable primary key. - * The key will carry a direct-key signature, whose subpackets can be modified by overriding the - * given {@link SignatureSubpacketsFunction}. - * - * @param primaryKeyPair primary key - * @param directKeySubpackets nullable callback to modify the direct-key signatures subpackets - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey withPrimaryKey( - PGPKeyPair primaryKeyPair, - SignatureSubpacketsFunction directKeySubpackets) - throws PGPException - { - return withPrimaryKey( - primaryKeyPair, - directKeySubpackets, - null); - } - - /** - * Generate an OpenPGP key with a certification-capable primary key. - * The {@link KeyPairGeneratorCallback} can be used to specify the primary key type. - * The key will carry a direct-key signature, whose subpackets can be modified by overriding the - * given {@link SignatureSubpacketsFunction}. - * IMPORTANT: The custom primary key passphrase will only be used, if in the final step the key is retrieved - * using {@link WithPrimaryKey#build()}. - * If instead {@link WithPrimaryKey#build(char[])} is used, the key-specific passphrase is overwritten with the argument - * passed into {@link WithPrimaryKey#build(char[])}. - * - * @param keyGenCallback callback to specify the primary key type - * @param directKeySubpackets nullable callback to modify the direct-key signatures subpackets - * @param passphrase nullable passphrase to protect the primary key with - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey withPrimaryKey( - KeyPairGeneratorCallback keyGenCallback, - SignatureSubpacketsFunction directKeySubpackets, - char[] passphrase) - throws PGPException - { - PGPKeyPair primaryKeyPair = keyGenCallback.generateFrom( - impl.kpGenProvider.get(PublicKeyPacket.VERSION_6, conf.keyCreationTime)); - PBESecretKeyEncryptor keyEncryptor = impl.keyEncryptorBuilderProvider - .build(passphrase, primaryKeyPair.getPublicKey().getPublicKeyPacket()); - return withPrimaryKey(primaryKeyPair, directKeySubpackets, keyEncryptor); - } - - /** - * Generate an OpenPGP key with a certification-capable primary key. - * The {@link KeyPairGeneratorCallback} can be used to specify the primary key type. - * The key will carry a direct-key signature, whose subpackets can be modified by overriding the - * given {@link SignatureSubpacketsFunction}. - * IMPORTANT: The custom keyEncryptor will only be used, if in the final step the key is retrieved - * using {@link WithPrimaryKey#build()}. - * If instead {@link WithPrimaryKey#build(char[])} is used, the key-specific encryptor is overwritten with - * an encryptor built from the argument passed into {@link WithPrimaryKey#build(char[])}. - * - * @param primaryKeyPair primary key - * @param directKeySubpackets nullable callback to modify the direct-key signatures subpackets - * @param keyEncryptor nullable encryptor to protect the primary key with - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey withPrimaryKey( - final PGPKeyPair primaryKeyPair, - SignatureSubpacketsFunction directKeySubpackets, - PBESecretKeyEncryptor keyEncryptor) - throws PGPException - { - if (primaryKeyPair.getPublicKey().getPublicKeyPacket() instanceof PublicSubkeyPacket) - { - throw new IllegalArgumentException("Primary key MUST NOT consist of subkey packet."); - } - - if (!PublicKeyUtils.isSigningAlgorithm(primaryKeyPair.getPublicKey().getAlgorithm())) - { - throw new PGPException("Primary key MUST use signing-capable algorithm."); - } - - return primaryKeyWithDirectKeySig( - primaryKeyPair, - new SignatureSubpacketsFunction() - { - public PGPSignatureSubpacketGenerator apply(PGPSignatureSubpacketGenerator subpackets) - { - subpackets.setIssuerFingerprint(true, primaryKeyPair.getPublicKey()); - subpackets.setSignatureCreationTime(conf.keyCreationTime); - subpackets.setKeyFlags(true, KeyFlags.CERTIFY_OTHER); - subpackets = DIRECT_KEY_SIGNATURE_SUBPACKETS.apply(subpackets); - subpackets.setKeyExpirationTime(false, 5 * SECONDS_PER_YEAR); - return subpackets; - } - }, - directKeySubpackets, - keyEncryptor); - } - - /** - * Specify the primary key and attach a direct-key signature. - * The direct-key signature's subpackets will first be modified using the baseSubpackets callback, followed - * by the customSubpackets callback. - * If both baseSubpackets and customSubpackets are null, no direct-key signature will be attached. - * - * @param primaryKeyPair primary key pair - * @param baseSubpackets base signature subpackets callback - * @param customSubpackets user-provided signature subpackets callback - * @param keyEncryptor key encryptor - * @return builder - * @throws PGPException if the key cannot be generated - */ - private WithPrimaryKey primaryKeyWithDirectKeySig( - PGPKeyPair primaryKeyPair, - SignatureSubpacketsFunction baseSubpackets, - SignatureSubpacketsFunction customSubpackets, - PBESecretKeyEncryptor keyEncryptor) - throws PGPException - { - if (baseSubpackets != null || customSubpackets != null) - { - // DK sig - PGPSignatureGenerator dkSigGen = new PGPSignatureGenerator( - impl.contentSignerBuilderProvider.get(primaryKeyPair.getPublicKey()), - primaryKeyPair.getPublicKey()); - dkSigGen.init(PGPSignature.DIRECT_KEY, primaryKeyPair.getPrivateKey()); - - PGPSignatureSubpacketGenerator subpackets = new PGPSignatureSubpacketGenerator(); - // application-dictated subpackets - if (baseSubpackets != null) - { - subpackets = baseSubpackets.apply(subpackets); - } - - // Allow the user to modify the direct-key signature subpackets - if (customSubpackets != null) - { - subpackets = customSubpackets.apply(subpackets); - } - - dkSigGen.setHashedSubpackets(subpackets.generate()); - - PGPSignature dkSig = dkSigGen.generateCertification(primaryKeyPair.getPublicKey()); - primaryKeyPair = new PGPKeyPair( - PGPPublicKey.addCertification(primaryKeyPair.getPublicKey(), dkSig), - primaryKeyPair.getPrivateKey()); - } - - Key primaryKey = new Key(primaryKeyPair, keyEncryptor); - - return new WithPrimaryKey(impl, conf, primaryKey); - } - - /** - * Intermediate builder class. - * Constructs an OpenPGP key from a specified primary key. - */ - public static class WithPrimaryKey - { - - private final Implementation impl; - private final Configuration conf; - private Key primaryKey; - private final List subkeys = new ArrayList(); - - /** - * Builder. - * - * @param implementation cryptographic implementation - * @param configuration key configuration - * @param primaryKey specified primary key - */ - private WithPrimaryKey(Implementation implementation, Configuration configuration, Key primaryKey) - { - this.impl = implementation; - this.conf = configuration; - this.primaryKey = primaryKey; - } - - /** - * Attach a User-ID with a positive certification to the key. - * - * @param userId user-id - * @return builder - * @throws PGPException if the user-id cannot be added - */ - public WithPrimaryKey addUserId(String userId) - throws PGPException - { - return addUserId(userId, null); - } - - /** - * Attach a User-ID with a positive certification to the key. - * The subpackets of the user-id certification can be modified using the userIdSubpackets callback. - * - * @param userId user-id - * @param userIdSubpackets callback to modify the certification subpackets - * @return builder - * @throws PGPException if the user-id cannot be added - */ - public WithPrimaryKey addUserId( - String userId, - SignatureSubpacketsFunction userIdSubpackets) - throws PGPException - { - return addUserId(userId, PGPSignature.POSITIVE_CERTIFICATION, userIdSubpackets); - } - - /** - * Attach a User-ID with a positive certification to the key. - * The subpackets of the user-id certification can be modified using the userIdSubpackets callback. - * - * @param userId user-id - * @param certificationType signature type - * @param userIdSubpackets callback to modify the certification subpackets - * @return builder - * @throws PGPException if the user-id cannot be added - */ - public WithPrimaryKey addUserId( - String userId, - int certificationType, - SignatureSubpacketsFunction userIdSubpackets) - throws PGPException - { - if (userId == null || userId.trim().length() == 0) - { - throw new IllegalArgumentException("User-ID cannot be null or empty."); - } - - if (!PGPSignature.isCertification(certificationType)) - { - throw new IllegalArgumentException("Signature type MUST be a certification type (0x10 - 0x13)"); - } - - PGPSignatureGenerator uidSigGen = new PGPSignatureGenerator( - impl.contentSignerBuilderProvider.get(primaryKey.pair.getPublicKey()), - primaryKey.pair.getPublicKey()); - uidSigGen.init(certificationType, primaryKey.pair.getPrivateKey()); - - PGPSignatureSubpacketGenerator subpackets = new PGPSignatureSubpacketGenerator(); - subpackets.setIssuerFingerprint(true, primaryKey.pair.getPublicKey()); - subpackets.setSignatureCreationTime(conf.keyCreationTime); - - if (userIdSubpackets != null) - { - subpackets = userIdSubpackets.apply(subpackets); - } - uidSigGen.setHashedSubpackets(subpackets.generate()); - - PGPSignature uidSig = uidSigGen.generateCertification(userId, primaryKey.pair.getPublicKey()); - PGPPublicKey pubKey = PGPPublicKey.addCertification(primaryKey.pair.getPublicKey(), userId, uidSig); - primaryKey = new Key(new PGPKeyPair(pubKey, primaryKey.pair.getPrivateKey()), primaryKey.encryptor); - - return this; - } - - /** - * Add an encryption-capable subkey to the OpenPGP key. - * See {@link PGPKeyPairGenerator#generateEncryptionSubkey()} for the key type. - * - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey addEncryptionSubkey() - throws PGPException - { - return addEncryptionSubkey(new KeyPairGeneratorCallback() - { - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException - { - return generator.generateEncryptionSubkey(); - } - }); - } - - /** - * Add an encryption-capable subkey to the OpenPGP key. - * The type of the subkey can be decided by implementing the {@link KeyPairGeneratorCallback}. - * - * @param keyGenCallback callback to decide the encryption subkey type - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey addEncryptionSubkey(KeyPairGeneratorCallback keyGenCallback) - throws PGPException - { - return addEncryptionSubkey(keyGenCallback, (char[])null); - } - - /** - * Add an encryption-capable subkey to the OpenPGP key. - * The type of the subkey can be decided by implementing the {@link KeyPairGeneratorCallback}. - * The binding signature can be modified by implementing the {@link SignatureSubpacketsFunction}. - * - * @param generatorCallback callback to specify the encryption key type. - * @param bindingSubpacketsCallback nullable callback to modify the binding signature subpackets - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey addEncryptionSubkey( - KeyPairGeneratorCallback generatorCallback, - SignatureSubpacketsFunction bindingSubpacketsCallback) - throws PGPException - { - PGPKeyPairGenerator generator = impl.kpGenProvider.get( - primaryKey.pair.getPublicKey().getVersion(), - conf.keyCreationTime - ); - PGPKeyPair subkey = generatorCallback.generateFrom(generator); - - return addEncryptionSubkey(subkey, bindingSubpacketsCallback, null); - } - - /** - * Add an encryption-capable subkey to the OpenPGP key. - * The subkey will be protected using the provided subkey passphrase. - * IMPORTANT: The custom subkey passphrase will only be used, if in the final step the key is retrieved - * using {@link #build()}. - * If instead {@link #build(char[])} is used, the key-specific passphrase is overwritten with the argument - * passed into {@link #build(char[])}. - * See {@link PGPKeyPairGenerator#generateEncryptionSubkey()} for the key type. - * - * @param passphrase nullable subkey passphrase - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey addEncryptionSubkey(char[] passphrase) - throws PGPException - { - return addEncryptionSubkey(new KeyPairGeneratorCallback() - { - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException - { - return generator.generateEncryptionSubkey(); - } - }, passphrase); - } - - /** - * Add an encryption-capable subkey to the OpenPGP key. - * The key type can be specified by overriding {@link KeyPairGeneratorCallback}. - * The subkey will be protected using the provided subkey passphrase. - * IMPORTANT: The custom subkey passphrase will only be used, if in the final step the key is retrieved - * using {@link #build()}. - * If instead {@link #build(char[])} is used, the key-specific passphrase is overwritten with the argument - * passed into {@link #build(char[])}. - * - * @param keyGenCallback callback to specify the key type - * @param passphrase nullable passphrase for the encryption subkey - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey addEncryptionSubkey(KeyPairGeneratorCallback keyGenCallback, - char[] passphrase) - throws PGPException - { - return addEncryptionSubkey(keyGenCallback, null, passphrase); - } - - /** - * Add an encryption-capable subkey to the OpenPGP key. - * The key type can be specified by overriding {@link KeyPairGeneratorCallback}. - * The binding signatures subpackets can be modified by overriding the {@link SignatureSubpacketsFunction}. - * The subkey will be protected using the provided subkey passphrase. - * IMPORTANT: The custom subkey passphrase will only be used, if in the final step the key is retrieved - * using {@link #build()}. - * If instead {@link #build(char[])} is used, the key-specific passphrase is overwritten with the argument - * passed into {@link #build(char[])}. - * - * @param keyGenCallback callback to specify the key type - * @param bindingSignatureCallback nullable callback to modify the binding signature subpackets - * @param passphrase nullable passphrase for the encryption subkey - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey addEncryptionSubkey(KeyPairGeneratorCallback keyGenCallback, - SignatureSubpacketsFunction bindingSignatureCallback, - char[] passphrase) - throws PGPException - { - PGPKeyPair subkey = keyGenCallback.generateFrom( - impl.kpGenProvider.get(PublicKeyPacket.VERSION_6, conf.keyCreationTime)); - subkey = subkey.asSubkey(impl.keyFingerprintCalculator); - PBESecretKeyEncryptor keyEncryptor = impl.keyEncryptorBuilderProvider.build(passphrase, subkey.getPublicKey().getPublicKeyPacket()); - return addEncryptionSubkey(subkey, bindingSignatureCallback, keyEncryptor); - } - - - /** - * Add an encryption-capable subkey to the OpenPGP key. - * IMPORTANT: The custom key encryptor will only be used, if in the final step the key is retrieved - * using {@link #build()}. - * If instead {@link #build(char[])} is used, the key-specific encryptor is overwritten with an encryptor - * built from the argument passed into {@link #build(char[])}. - * - * @param encryptionSubkey encryption subkey - * @param bindingSubpacketsCallback nullable callback to modify the subkey binding signature subpackets - * @param keyEncryptor nullable encryptor to encrypt the encryption subkey - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey addEncryptionSubkey( - PGPKeyPair encryptionSubkey, - SignatureSubpacketsFunction bindingSubpacketsCallback, - PBESecretKeyEncryptor keyEncryptor) - throws PGPException - { - if (!(encryptionSubkey.getPublicKey().getPublicKeyPacket() instanceof PublicSubkeyPacket)) - { - throw new IllegalArgumentException("Encryption subkey MUST NOT consist of a primary key packet."); - } - - if (!encryptionSubkey.getPublicKey().isEncryptionKey()) - { - throw new PGPException("Encryption key MUST use encryption-capable algorithm."); - } - // generate binding signature - PGPSignatureSubpacketGenerator subpackets = new PGPSignatureSubpacketGenerator(); - subpackets.setIssuerFingerprint(true, primaryKey.pair.getPublicKey()); - subpackets.setSignatureCreationTime(conf.keyCreationTime); - subpackets = ENCRYPTION_SUBKEY_SUBPACKETS.apply(subpackets); - - // allow subpacket customization - PGPPublicKey publicSubkey = getPublicSubKey(encryptionSubkey, bindingSubpacketsCallback, subpackets); - Key subkey = new Key(new PGPKeyPair(publicSubkey, encryptionSubkey.getPrivateKey()), keyEncryptor); - subkeys.add(subkey); - return this; - } - - /** - * Add a signing-capable subkey to the OpenPGP key. - * The binding signature will contain a primary-key back-signature. - * See {@link PGPKeyPairGenerator#generateSigningSubkey()} for the key type. - * - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey addSigningSubkey() - throws PGPException - { - return addSigningSubkey(new KeyPairGeneratorCallback() - { - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException - { - return generator.generateSigningSubkey(); - } - }); - } - - /** - * Add a signing-capable subkey to the OpenPGP key. - * The binding signature will contain a primary-key back-signature. - * The key type can be specified by overriding {@link KeyPairGeneratorCallback}. - * - * @param keyGenCallback callback to specify the signing-subkey type - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey addSigningSubkey(KeyPairGeneratorCallback keyGenCallback) - throws PGPException - { - return addSigningSubkey(keyGenCallback, null); - } - - /** - * Add a signing-capable subkey to the OpenPGP key. - * See {@link PGPKeyPairGenerator#generateSigningSubkey()} for the key type. - * The binding signature will contain a primary-key back-signature. - * IMPORTANT: The custom subkey passphrase will only be used, if in the final step the key is retrieved - * using {@link #build()}. - * If instead {@link #build(char[])} is used, the key-specific passphrase is overwritten with the argument - * passed into {@link #build(char[])}. - * - * @param passphrase nullable passphrase - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey addSigningSubkey(char[] passphrase) - throws PGPException - { - return addSigningSubkey(new KeyPairGeneratorCallback() - { - public PGPKeyPair generateFrom(PGPKeyPairGenerator generator) - throws PGPException - { - return generator.generateSigningSubkey(); - } - }, passphrase); - } - - /** - * Add a signing-capable subkey to the OpenPGP key. - * The signing-key type can be specified by overriding the {@link KeyPairGeneratorCallback}. - * The binding signature will contain a primary-key back-signature. - * IMPORTANT: The custom subkey passphrase will only be used, if in the final step the key is retrieved - * using {@link #build()}. - * If instead {@link #build(char[])} is used, the key-specific passphrase is overwritten with the argument - * passed into {@link #build(char[])}. - * - * @param keyGenCallback callback to specify the signing-key type - * @param passphrase nullable passphrase - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey addSigningSubkey(KeyPairGeneratorCallback keyGenCallback, - char[] passphrase) - throws PGPException - { - return addSigningSubkey(keyGenCallback, null, null, passphrase); - } - - /** - * Add a signing-capable subkey to the OpenPGP key. - * The signing-key type can be specified by overriding the {@link KeyPairGeneratorCallback}. - * The binding signature will contain a primary-key back-signature. - * The contents of the binding signature(s) can be modified by overriding the respective - * {@link SignatureSubpacketsFunction} instances. - * IMPORTANT: The custom subkey passphrase will only be used, if in the final step the key is retrieved - * using {@link #build()}. - * If instead {@link #build(char[])} is used, the key-specific passphrase is overwritten with the argument - * passed into {@link #build(char[])}. - * - * @param keyGenCallback callback to specify the signing-key type - * @param bindingSignatureCallback callback to modify the contents of the signing subkey binding signature - * @param backSignatureCallback callback to modify the contents of the embedded primary key binding signature - * @param passphrase nullable passphrase - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey addSigningSubkey(KeyPairGeneratorCallback keyGenCallback, - SignatureSubpacketsFunction bindingSignatureCallback, - SignatureSubpacketsFunction backSignatureCallback, - char[] passphrase) - throws PGPException - { - PGPKeyPair subkey = keyGenCallback.generateFrom(impl.kpGenProvider.get(PublicKeyPacket.VERSION_6, conf.keyCreationTime)); - subkey = subkey.asSubkey(impl.keyFingerprintCalculator); - PBESecretKeyEncryptor keyEncryptor = impl.keyEncryptorBuilderProvider.build(passphrase, subkey.getPublicKey().getPublicKeyPacket()); - return addSigningSubkey(subkey, bindingSignatureCallback, backSignatureCallback, keyEncryptor); - } - - /** - * Add a signing-capable subkey to the OpenPGP key. - * The signing-key type can be specified by overriding the {@link KeyPairGeneratorCallback}. - * The binding signature will contain a primary-key back-signature. - * The contents of the binding signature(s) can be modified by overriding the respective - * {@link SignatureSubpacketsFunction} instances. - * IMPORTANT: The custom key encryptor will only be used, if in the final step the key is retrieved - * using {@link #build()}. - * If instead {@link #build(char[])} is used, the key-specific encryptor is overwritten with an encryptor - * built from the argument passed into {@link #build(char[])}. - * - * @param signingSubkey signing subkey - * @param bindingSignatureCallback callback to modify the contents of the signing subkey binding signature - * @param backSignatureCallback callback to modify the contents of the embedded primary key binding signature - * @param keyEncryptor nullable encryptor to protect the signing subkey - * @return builder - * @throws PGPException if the key cannot be generated - */ - public WithPrimaryKey addSigningSubkey(PGPKeyPair signingSubkey, - SignatureSubpacketsFunction bindingSignatureCallback, - SignatureSubpacketsFunction backSignatureCallback, - PBESecretKeyEncryptor keyEncryptor) - throws PGPException - { - if (!(signingSubkey.getPublicKey().getPublicKeyPacket() instanceof PublicSubkeyPacket)) - { - throw new IllegalArgumentException("Signing subkey MUST NOT consist of primary key packet."); - } - - if (!PublicKeyUtils.isSigningAlgorithm(signingSubkey.getPublicKey().getAlgorithm())) - { - throw new PGPException("Signing key MUST use signing-capable algorithm."); - } - - PGPSignatureSubpacketGenerator backSigSubpackets = new PGPSignatureSubpacketGenerator(); - backSigSubpackets.setIssuerFingerprint(true, signingSubkey.getPublicKey()); - backSigSubpackets.setSignatureCreationTime(conf.keyCreationTime); - if (backSignatureCallback != null) - { - backSigSubpackets = backSignatureCallback.apply(backSigSubpackets); - } - - PGPSignatureSubpacketGenerator bindingSigSubpackets = new PGPSignatureSubpacketGenerator(); - bindingSigSubpackets.setIssuerFingerprint(true, primaryKey.pair.getPublicKey()); - bindingSigSubpackets.setSignatureCreationTime(conf.keyCreationTime); - - bindingSigSubpackets = SIGNING_SUBKEY_SUBPACKETS.apply(bindingSigSubpackets); - - PGPSignatureGenerator backSigGen = new PGPSignatureGenerator( - impl.contentSignerBuilderProvider.get(signingSubkey.getPublicKey()), - signingSubkey.getPublicKey()); - backSigGen.init(PGPSignature.PRIMARYKEY_BINDING, signingSubkey.getPrivateKey()); - backSigGen.setHashedSubpackets(backSigSubpackets.generate()); - PGPSignature backSig = backSigGen.generateCertification( - primaryKey.pair.getPublicKey(), signingSubkey.getPublicKey()); - - try - { - bindingSigSubpackets.addEmbeddedSignature(false, backSig); - } - catch (IOException e) - { - throw new PGPException("Cannot embed back-signature.", e); - } - - PGPPublicKey signingPubKey = getPublicSubKey(signingSubkey, bindingSignatureCallback, bindingSigSubpackets); - signingSubkey = new PGPKeyPair(signingPubKey, signingSubkey.getPrivateKey()); - subkeys.add(new Key(signingSubkey, keyEncryptor)); - - return this; - } - - /** - * Build the {@link PGPSecretKeyRing OpenPGP key}, allowing individual passphrases for the subkeys. - * - * @return OpenPGP key - * @throws PGPException if the key cannot be generated - */ - public PGPSecretKeyRing build() - throws PGPException - { - PGPSecretKey primarySecretKey = new PGPSecretKey( - primaryKey.pair.getPrivateKey(), - primaryKey.pair.getPublicKey(), - impl.digestCalculatorProvider.get(HashAlgorithmTags.SHA1), - true, - primaryKey.encryptor); - List keys = new ArrayList(); - keys.add(primarySecretKey); - - for (Iterator it = subkeys.iterator(); it.hasNext();) - { - Key key = (Key)it.next(); - PGPSecretKey subkey = new PGPSecretKey( - key.pair.getPrivateKey(), - key.pair.getPublicKey(), - impl.digestCalculatorProvider.get(HashAlgorithmTags.SHA1), - false, - key.encryptor); - keys.add(subkey); - } - - return new PGPSecretKeyRing(keys); - } - - /** - * Build the {@link PGPSecretKeyRing OpenPGP key} using a single passphrase used to protect all subkeys. - * The passphrase will override whichever key protectors were specified in previous builder steps. - * - * @param passphrase nullable passphrase - * @return OpenPGP key - * @throws PGPException if the key cannot be generated - */ - public PGPSecretKeyRing build(char[] passphrase) - throws PGPException - { - PBESecretKeyEncryptor primaryKeyEncryptor = impl.keyEncryptorBuilderProvider - .build(passphrase, primaryKey.pair.getPublicKey().getPublicKeyPacket()); - sanitizeKeyEncryptor(primaryKeyEncryptor); - PGPSecretKey primarySecretKey = new PGPSecretKey( - primaryKey.pair.getPrivateKey(), - primaryKey.pair.getPublicKey(), - impl.digestCalculatorProvider.get(HashAlgorithmTags.SHA1), - true, - primaryKeyEncryptor); - List keys = new ArrayList(); - keys.add(primarySecretKey); - - for (Iterator it = subkeys.iterator(); it.hasNext();) - { - Key key = (Key)it.next(); - PBESecretKeyEncryptor subkeyEncryptor = impl.keyEncryptorBuilderProvider - .build(passphrase, key.pair.getPublicKey().getPublicKeyPacket()); - sanitizeKeyEncryptor(subkeyEncryptor); - PGPSecretKey subkey = new PGPSecretKey( - key.pair.getPrivateKey(), - key.pair.getPublicKey(), - impl.digestCalculatorProvider.get(HashAlgorithmTags.SHA1), - false, - subkeyEncryptor); - keys.add(subkey); - } - - if (passphrase != null) - { - Arrays.fill(passphrase, (char)0); - } - - return new PGPSecretKeyRing(keys); - } - - protected void sanitizeKeyEncryptor(PBESecretKeyEncryptor keyEncryptor) - { - if (keyEncryptor == null) - { - // Unprotected is okay - return; - } - - S2K s2k = keyEncryptor.getS2K(); - if (s2k.getType() == S2K.SIMPLE || s2k.getType() == S2K.SALTED) - { - throw new IllegalArgumentException("S2K specifiers SIMPLE and SALTED are not allowed for secret key encryption."); - } - else if (s2k.getType() == S2K.ARGON_2) - { - if (keyEncryptor.getAeadAlgorithm() == 0) - { - throw new IllegalArgumentException("Argon2 MUST be used with AEAD."); - } - } - } - - private PGPPublicKey getPublicSubKey(PGPKeyPair encryptionSubkey, SignatureSubpacketsFunction bindingSubpacketsCallback, PGPSignatureSubpacketGenerator subpackets) - throws PGPException - { - if (bindingSubpacketsCallback != null) - { - subpackets = bindingSubpacketsCallback.apply(subpackets); - } - - PGPSignatureGenerator bindingSigGen = new PGPSignatureGenerator( - impl.contentSignerBuilderProvider.get(primaryKey.pair.getPublicKey()), - primaryKey.pair.getPublicKey()); - bindingSigGen.init(PGPSignature.SUBKEY_BINDING, primaryKey.pair.getPrivateKey()); - bindingSigGen.setHashedSubpackets(subpackets.generate()); - - PGPSignature bindingSig = bindingSigGen.generateCertification(primaryKey.pair.getPublicKey(), encryptionSubkey.getPublicKey()); - return PGPPublicKey.addCertification(encryptionSubkey.getPublicKey(), bindingSig); - } - } - - /** - * Bundle implementation-specific provider classes. - */ - private static class Implementation - { - final PGPKeyPairGeneratorProvider kpGenProvider; - final PGPContentSignerBuilderProvider contentSignerBuilderProvider; - final PGPDigestCalculatorProvider digestCalculatorProvider; - final PBESecretKeyEncryptorFactory keyEncryptorBuilderProvider; - final KeyFingerPrintCalculator keyFingerprintCalculator; - - public Implementation(PGPKeyPairGeneratorProvider keyPairGeneratorProvider, - PGPContentSignerBuilderProvider contentSignerBuilderProvider, - PGPDigestCalculatorProvider digestCalculatorProvider, - PBESecretKeyEncryptorFactory keyEncryptorBuilderProvider, - KeyFingerPrintCalculator keyFingerPrintCalculator) - { - this.kpGenProvider = keyPairGeneratorProvider; - this.contentSignerBuilderProvider = contentSignerBuilderProvider; - this.digestCalculatorProvider = digestCalculatorProvider; - this.keyEncryptorBuilderProvider = keyEncryptorBuilderProvider; - this.keyFingerprintCalculator = keyFingerPrintCalculator; - } - } - - /** - * Bundle configuration-specific data. - */ - private static class Configuration - { - final Date keyCreationTime; - - public Configuration(Date keyCreationTime) - { - this.keyCreationTime = keyCreationTime; - } - } - - /** - * Tuple of a {@link PGPKeyPair} and (nullable) {@link PBESecretKeyEncryptor}. - */ - private static class Key - { - private final PGPKeyPair pair; - private final PBESecretKeyEncryptor encryptor; - - public Key(PGPKeyPair key, PBESecretKeyEncryptor encryptor) - { - this.pair = key; - this.encryptor = encryptor; - } - } -} diff --git a/pg/src/main/java/org/bouncycastle/openpgp/api/package-info.java b/pg/src/main/java/org/bouncycastle/openpgp/api/package-info.java new file mode 100644 index 0000000000..099545ae27 --- /dev/null +++ b/pg/src/main/java/org/bouncycastle/openpgp/api/package-info.java @@ -0,0 +1,23 @@ +/** + * The

    api
    package contains a high-level OpenPGP API layer on top of the + *
    openpgp
    mid-level API. + * It is tailored to provide a modern OpenPGP experience, following the guidance from rfc9580 ("OpenPGP v6"), + * while also being interoperable with rfc4880 ("OpenPGP v4"). + *

    + * From an architectural point of view, the hierarchy of the individual layers is as follows: + *

      + *
    • + *
      api
      specifies a high-level API using mid-level implementations from
      openpgp
      . + * This layer strives to be easy to use, hard to misuse and secure by default. + *
    • + *
    • + *
      openpgp
      defines a powerful, flexible, but quite verbose API using packet definitions + * from
      bcpg
      . + *
    • + *
    • + *
      bcpg
      implements serialization / deserialization of OpenPGP packets. + * It does not contain any business logic. + *
    • + *
    + */ +package org.bouncycastle.openpgp.api; \ No newline at end of file diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/KeyIdentifierTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/KeyIdentifierTest.java index 54bdb93570..b61b29f149 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/KeyIdentifierTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/KeyIdentifierTest.java @@ -37,6 +37,7 @@ public void performTest() throws Exception { testWildcardIdentifier(); + testWildcardMatches(); testIdentifierFromKeyId(); testIdentifierFromLongKeyId(); @@ -64,6 +65,17 @@ private void testWildcardIdentifier() isTrue(id.isWildcard()); } + private void testWildcardMatches() { + KeyIdentifier wildcard = KeyIdentifier.wildcard(); + KeyIdentifier nonWildcard = new KeyIdentifier(123L); + + isTrue(wildcard.matches(nonWildcard)); + isTrue(nonWildcard.matches(wildcard)); + + isTrue(!wildcard.matchesExplicit(nonWildcard)); + isTrue(!nonWildcard.matchesExplicit(wildcard)); + } + private void testIdentifierFromKeyId() { KeyIdentifier identifier = new KeyIdentifier(1234L); From 73c5ea8e32f8166ae882619c5eb2292eef4221f5 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sat, 31 May 2025 11:58:00 +1000 Subject: [PATCH 398/890] initial pass of 1.81 release notes --- docs/releasenotes.html | 595 +++++++++++++++++++++-------------------- 1 file changed, 309 insertions(+), 286 deletions(-) diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 1e6f20d7cb..9faa90f2b9 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -18,10 +18,33 @@

    1.0 Introduction

    2.0 Release History

    -

    2.1.1 Version

    +

    2.1.1 Version

    +Release: 1.81
    +Date:      2025, 4th June. +

    2.1.2 Defects Fixed

    +
      +
    • A potention NullPointerException in the KEM KDF KemUtil class has been removed.
    • +
    • Overlapping input/output buffers in doFinal could result in data corruption. This has been fixed.
    • +
    +

    2.1.3 Additional Features and Functionality

    +
      +
    • XWing implementation updated to draft-connolly-cfrg-xwing-kem/07/
    • +
    • Further support has been added for generation and use of PGP V6 keys
    • +
    • The PQC signature algorithm proposal Mayo has been added to the low-level API and the BCPQC provider.
    • +
    • The PQC signature algorithm proposal Snova has been added to the low-level API and the BCPQC provider.
    • +
    • Support for ChaCha20-Poly1305 has been added to the CMS/SMIME APIs.
    • +
    • The Falcon implementation has been updated to the latest draft.
    • +
    • Support has been added for generating keys which encode as seed-only and expanded-key-only for ML-KEM and ML-DSA private keys.
    • +
    • Private key encoding of ML-DSA and ML-KEM private keys now follows the latest IETF draft.
    • +
    • The Ascon family of algorithms has been updated to the initial draft of SP 800-232. Some additional optimisation work has been done.
    • +
    • Support for ML-DSA's external-mu calculation and signing has been added to the BC provider.
    • +
    • CMS now supports ML-DSA for SignedData generation.
    • +
    + +

    2.2.1 Version

    Release: 1.80
    Date:      2025, 14th January. -

    2.1.2 Defects Fixed

    +

    2.2.2 Defects Fixed

    • A splitting issue for ML-KEM lead to an incorrect size for kemct in KEMRecipientInfos. This has been fixed.
    • The PKCS12 KeyStore has been adjusted to prevent accidental doubling of the Oracle trusted certificate attribute (results in an IOException when used with the JVM PKCS12 implementation).
    • @@ -54,10 +77,10 @@

      2.2.3 Additional Features and Functionality

    • The ASCON family of algorithms have been updated in accordance with the published FIPS SP 800-232 draft.
    -

    2.2.1 Version

    +

    2.3.1 Version

    Release: 1.79
    Date:      2024, 30th October. -

    2.2.2 Defects Fixed

    +

    2.3.2 Defects Fixed

    • Leading zeroes were sometimes dropped from Ed25519 signatures leading to verification errors in the PGP API. This has been fixed.
    • Default version string for Armored Output is now set correctly in 18on build.
    • @@ -74,7 +97,7 @@

      2.2.2 Defects Fixed

    • The default version header for PGP armored output did not carry the correct version string. This has been fixed.
    • In some situations the algorithm lookup for creating PGPDigestCalculators would fail due to truncation of the algorithm name. This has been fixed.
    -

    2.2.3 Additional Features and Functionality

    +

    2.3.3 Additional Features and Functionality

    • Object Identifiers have been added for ML-KEM, ML-DSA, and SLH-DSA.
    • The PQC algorithms, ML-KEM, ML-DSA (including pre-hash), and SLH-DSA (including pre-hash) have been added to the BC provider and the lightweight API.
    • @@ -95,10 +118,10 @@

      2.2.3 Additional Features and Functionality

    • The system property "org.bouncycastle.ec.disable_f2m" has been introduced to allow F2m EC support to be disabled.
    -

    2.3.1 Version

    +

    2.4.1 Version

    Release: 1.78.1
    Date:      2024, 18th April. -

    2.3.2 Defects Fixed

    +

    2.4.2 Defects Fixed

    • The new dependency of the the PGP API on the bcutil jar was missing from the module jar, the OSGi manifest, and the Maven POM. This has been fixed.
    • Missing exports and duplicate imports have been added/removed from the OSGi manifests.
    • @@ -106,10 +129,10 @@

      2.3.2 Defects Fixed

    • A check in the X.509 Extensions class preventing the parsing of empty extensions has been removed.
    -

    2.4.1 Version

    +

    2.5.1 Version

    Release: 1.78
    Date:      2024, 7th April. -

    2.4.2 Defects Fixed

    +

    2.5.2 Defects Fixed

    • Issues with a dangling weak reference causing intermittent NullPointerExceptions in the OcspCache have been fixed.
    • Issues with non-constant time RSA operations in TLS handshakes have been fixed.
    • @@ -127,7 +150,7 @@

      2.4.2 Defects Fixed

    • An off-by-one error in the encoding for EccP256CurvePoint for ITS has been fixed.
    • PEM Parser now enforces PEM headers to start at the beginning of the line to be meaningful.
    -

    2.4.3 Additional Features and Functionality

    +

    2.5.3 Additional Features and Functionality

    • An implementation of MLS (RFC 9420 - The Messaging Layer Security Protocol) has been added as a new module.
    • NTRU now supports NTRU-HPS4096-1229 and NTRU-HRSS-1373.
    • @@ -145,7 +168,7 @@

      2.4.3 Additional Features and Functionality

    • CertPathValidationContext and CertificatePoliciesValidation now include implementations of Memoable.
    • The Composite post-quantum signatures implementation has been updated to the latest draft draft-ounsworth-pq-composite-sigs.
    -

    2.4.4 Notes.

    +

    2.5.4 Notes.

    • Both versions of NTRUPrime have been updated to produce 256 bit secrets in line with Kyber. This should also bring them into line with other implementations such as those used in OpenSSH now.
    • BCJSSE: The boolean system property 'org.bouncycastle.jsse.fips.allowRSAKeyExchange" now defaults to false. All RSA @@ -156,7 +179,7 @@

      2.4.4 Notes.

    • The PKCS12 store using GCM does not include the PKCS#12 MAC so no longer includes use of the PKCS#12 PBE scheme and only uses PBKDF2.
    • In keeping with the current set of experimental OIDs for PQC algorithms, OIDs may have changed to reflect updated versions of the algorithms.
    -

    2.4.5 Security Advisories.

    +

    2.5.5 Security Advisories.

    Release 1.78 deals with the following CVEs:

    @@ -167,10 +190,10 @@

    2.4.5 Security Advisories.

  • CVE-2024-34447 - When endpoint identification is enabled in the BCJSSE and an SSL socket is not created with an explicit hostname (as happens with HttpsURLConnection), hostname verification could be performed against a DNS-resolved IP address. This has been fixed.
  • -

    2.5.1 Version

    +

    2.6.1 Version

    Release: 1.77
    Date:      2023, November 13th -

    2.5.2 Defects Fixed

    +

    2.6.2 Defects Fixed

    • Using an unescaped '=' in an X.500 RDN would result in the RDN being truncated silently. The issue is now detected and an exception is thrown.
    • asn1.eac.CertificateBody was returning certificateEffectiveDate from getCertificateExpirationDate(). This has been fixed to return certificateExpirationDate.
    • @@ -186,7 +209,7 @@

      2.5.2 Defects Fixed

    • An internal method in Arrays was failing to construct its failure message correctly on an error. This has been fixed.
    • HSSKeyPublicParameters.generateLMSContext() would fail for a unit depth key. This has been fixed.
    -

    2.5.3 Additional Features and Functionality

    +

    2.6.3 Additional Features and Functionality

    • BCJSSE: Added org.bouncycastle.jsse.client.omitSigAlgsCertExtension and org.bouncycastle.jsse.server.omitSigAlgsCertExtension boolean system properties to control (for client and server resp.) whether the signature_algorithms_cert extension should be omitted if it would be identical to signature_algorithms. @@ -198,7 +221,7 @@

      2.5.3 Additional Features and Functionality

    • TLS: RSA key exchange cipher suites are now disabled by default.
    • Support has been added for PKCS#10 requests to allow certificates using the altSignature/altPublicKey extensions.
    -

    2.5.4 Notes.

    +

    2.6.4 Notes.

    • Kyber and Dilithium have been updated according to the latest draft of the standard. Dilithium-AES and Kyber-AES have now been removed. Kyber now produces 256 bit secrets for all parameter sets (in line with the draft standard).
    • NTRU has been updated to produce 256 bit secrets in line with Kyber.
    • @@ -207,10 +230,10 @@

      2.5.4 Notes.

    • PQC CMS SignedData now defaults to SHA-256 for signed attributes rather than SHAKE-256. This is also a compatibility change, but may change further again as the IETF standard for CMS is updated.
    -

    2.6.1 Version

    +

    2.7.1 Version

    Release: 1.76
    Date:      2023, July 29th -

    2.6.2 Defects Fixed

    +

    2.7.2 Defects Fixed

    • Service allocation in the provider could fail due to the lack of a permission block. This has been fixed.
    • JceKeyFingerPrintCalculator has been generalised for different providers by using "SHA-256" for the algorithm string.
    • @@ -219,7 +242,7 @@

      2.6.2 Defects Fixed

    • Cipher.unwrap() for HQC could fail due to a miscalculation of the length of the KEM packet. This has been fixed.
    • There was exposure to a Java 7 method in the Java 5 to Java 8 BCTLS jar which could cause issues with some TLS 1.2 cipher suites running on older JVMs. This is now fixed.
    -

    2.6.3 Additional Features and Functionality

    +

    2.7.3 Additional Features and Functionality

    • BCJSSE: Following OpenJDK, finalizers have been removed from SSLSocket subclasses. Applications should close sockets and not rely on garbage collection.
    • BCJSSE: Added support for boolean system property "jdk.tls.client.useCompatibilityMode" (default "true").
    • @@ -232,30 +255,30 @@

      2.6.3 Additional Features and Functionality

    • An UnknownPacket type has been added to the PGP APIs to allow for forwards compatibility with upcoming revisions to the standard.
    -

    2.7.1 Version

    +

    2.8.1 Version

    Release: 1.75
    Date:      2023, June 21st -

    2.7.2 Defects Fixed

    +

    2.8.2 Defects Fixed

    • Several Java 8 method calls were accidentally introduced in the Java 5 to Java 8 build. The affected classes have been refactored to remove this.
    • (D)TLS: renegotiation after resumption now fixed to avoid breaking connection.
    -

    2.7.3 Notes.

    +

    2.8.3 Notes.

    • The ASN.1 core package has had some dead and retired methods cleaned up and removed.
    -

    2.8.1 Version

    +

    2.9.1 Version

    Release: 1.74
    Date:      2023, June 12th -

    2.8.2 Defects Fixed

    +

    2.9.2 Defects Fixed

    • AsconEngine: Fixed a buffering bug when decrypting across multiple processBytes calls (ascon128a unaffected).
    • Context based sanity checking on PGP signatures has been added.
    • The ParallelHash clone constructor was not copying all fields. This is now fixed.
    • The maximimum number of blocks for CTR/SIC modes was 1 block less than it should have been. This is now fixed.
    -

    2.8.3 Additional Features and Functionality

    +

    2.9.3 Additional Features and Functionality

    • The PGP API now supports wildcard key IDs for public key based data encryption.
    • LMS now supports SHA256/192, SHAKE256/192, and SHAKE256/256 (the additional SP 8000-208 parameter sets).
    • @@ -274,22 +297,22 @@

      2.8.3 Additional Features and Functionality

    • The number of keys/sub-keys in a PGPKeyRing can now be found by calling PGPKeyRing.size().
    • The PQC algorithms LMS/HSS, SPHINCS+, Dilithium, Falcon, and NTRU are now supported directly by the BC provider.
    -

    2.8.4 Notes.

    +

    2.9.4 Notes.

    • The now defunct PQC SIKE algorithm has been removed, this has also meant the removal of its resource files so the provider is now quite a bit smaller.
    • As a precaution, HC128 now enforces a 128 bit IV, previous behaviour for shorter IVs can be supported where required by padding the IV to the 128 bits with zero.
    • PGP encrypted data generation now uses integrity protection by default. Previous behaviour for encrypted data can be supported where required by calling PGPDataEncryptorBuilder.setWithIntegrityPacket(false) when data encryption is set up.
    • There are now additional sanity checks in place to prevent accidental mis-use of PGPSignature objects. If this change causes any issues, you might want to check what your code is up to as there is probably a bug.
    -

    2.8.5 Security Advisories.

    +

    2.9.5 Security Advisories.

    • CVE-2023-33201 - this release fixes an issue with the X509LDAPCertStoreSpi where a specially crafted certificate subject could be used to try and extract extra information out of an LDAP server with wild-card matching enabled.
    -

    2.9.1 Version

    +

    2.10.1 Version

    Release: 1.73
    Date:      2023, April 8th -

    2.9.2 Defects Fixed

    +

    2.10.2 Defects Fixed

    • BCJSSE: Instantiating a JSSE provider in some contexts could cause an AccessControl exception. This has been fixed.
    • The EC key pair generator can generate out of range private keys when used with SM2. A specific SM2KeyPairGenerator has been added to the low-level API and is used by KeyPairGenerator.getInstance("SM2", "BC"). The SM2 signer has been updated to check for out of range keys as well..
    • @@ -310,7 +333,7 @@

      2.9.2 Defects Fixed

    • IPAddress has been written to provide stricter checking and avoid the use of Integer.parseInt().
    • A Java 7 class snuck into the Java 5 to Java 8 build. This has been addressed.
    -

    2.9.3 Additional Features and Functionality

    +

    2.10.3 Additional Features and Functionality

    • The Rainbow NIST Post Quantum Round-3 Candidate has been added to the low-level API and the BCPQC provider (level 3 and level 5 parameter sets only).
    • The GeMSS NIST Post Quantum Round-3 Candidate has been added to the low-level API.
    • @@ -337,38 +360,38 @@

      2.9.3 Additional Features and Functionality

    • A general purpose PQCOtherInfoGenerator has been added which supports all Kyber and NTRU.
    • An implementation of HPKE (RFC 9180 - Hybrid Public Key Encryption) has been added to the light-weight cryptography API.
    -

    2.9.4 Security Advisories.

    +

    2.10.4 Security Advisories.

    • The PQC implementations have now been subject to formal review for secret leakage and side channels, there were issues in BIKE, Falcon, Frodo, HQC which have now been fixed. Some weak positives also showed up in Rainbow, Picnic, SIKE, and GeMSS - for now this last set has been ignored as the algorithms will either be updated if they reappear in the Signature Round, or deleted, as is already the case for SIKE (it is now in the legacy package). Details on the group responsible for the testing can be found in the CONTRIBUTORS file.
    • For at least some ECIES variants (e.g. when using CBC) there is an issue with potential malleability of a nonce (implying silent malleability of the plaintext) that must be sent alongside the ciphertext but is outside the IES integrity check. For this reason the automatic generation of nonces with IED is now disabled and they have to be passed in using an IESParameterSpec. The current advice is to agree on a nonce between parties and then rely on the use of the ephemeral key component to allow the nonce (rather the so called nonce) usage to be extended.
    -

    2.9.5 Notes.

    +

    2.10.5 Notes.

    • Most test data files have now been migrated to a separate project bc-test-data which is also available on github. If you clone bc-test-data at the same level as the bc-java project the tests will find the test data they require.
    • There has been further work to make entropy collection more friendly in container environments. See DRBG.java for details. We would welcome any further feedback on this as we clearly cannot try all situations first hand.
    -

    2.10.1 Version

    +

    2.11.1 Version

    Release: 1.72.2, 1.72.3
    Date:      2022, November 20th -

    2.10.2 Defects Fixed

    +

    2.11.2 Defects Fixed

    • PGP patch release - fix for OSGI and version header in 1.72.1 jar file.
    -

    2.11.1 Version

    +

    2.12.1 Version

    Release: 1.72.1
    Date:      2022, October 25th -

    2.11.2 Defects Fixed

    +

    2.12.2 Defects Fixed

    • PGP patch release - fix for regression in OpenPGP PGPEncryptedData.java which could result in checksum failures on correct files.
    -

    2.12.1 Version

    +

    2.13.1 Version

    Release: 1.72
    Date:      2022, September 25th -

    2.12.2 Defects Fixed

    +

    2.13.2 Defects Fixed

    • There were parameter errors in XMSS^MT OIDs for XMSSMT_SHA2_40/4_256 and XMSSMT_SHA2_60/3_256. These have been fixed.
    • There was an error in Merkle tree construction for the Evidence Records (ERS) implementation which could result in invalid roots been timestamped. ERS now produces an ArchiveTimeStamp for each data object/group with an associated reduced hash tree. The reduced hash tree is now calculated as a simple path to the root of the tree for each record.
    • @@ -376,7 +399,7 @@

      2.12.2 Defects Fixed

    • A tagging calculation error in GCMSIV which could result in incorrect tags has been fixed.
    • Issues around Java 17 which could result in failing tests have been addressed.
    -

    2.12.3 Additional Features and Functionality

    +

    2.13.3 Additional Features and Functionality

    • BCJSSE: TLS 1.3 is now enabled by default where no explicit protocols are supplied (e.g. "TLS" or "Default" SSLContext algorithms, or SSLContext.getDefault() method).
    • BCJSSE: Rewrite SSLEngine implementation to improve compatibility with SunJSSE.
    • @@ -406,22 +429,22 @@

      2.12.3 Additional Features and Functionality

    • Support has been added to the PKCS#12 implementation for the Oracle trusted certificate attribute.
    • Performance of our BZIP2 classes has been improved.
    -

    2.12.4 Notes

    +

    2.13.4 Notes

    Keep in mind the PQC algorithms are still under development and we are still at least a year and a half away from published standards. This means the algorithms may still change so by all means experiment, but do not use the PQC algoritms for anything long term.

    The legacy "Rainbow" and "McEliece" implementations have been removed from the BCPQC provider. The underlying classes are still present if required. Other legacy algorithm implementations can be found under the org.bouncycastle.pqc.legacy package.

    -

    2.12.5 Security Notes

    +

    2.13.5 Security Notes

    The PQC SIKE algorithm is provided for research purposes only. It should now be regarded as broken. The SIKE implementation will be withdrawn in BC 1.73.

    -

    2.13.1 Version

    +

    2.14.1 Version

    Release: 1.71
    Date:      2022, March 31st. -

    2.13.2 Defects Fixed

    +

    2.14.2 Defects Fixed

    • In line with GPG the PGP API now attempts to preserve comments containing non-ascii UTF-8 characters.
    • An accidental partial dependency on Java 1.7 has been removed from the TLS API.
    • @@ -435,7 +458,7 @@

      2.13.2 Defects Fixed

    • An accidental regression introduced by a fix for another issue in PKIXCertPathReviewer around use of the AuthorityKeyIdentifier extension and it failing to match a certificate uniquely when the serial number field is missing has been fixed.
    • An error was found in the creation of TLS 1.3 Export Keying Material which could cause compatibility issues. This has been fixed.
    -

    2.13.3 Additional Features and Functionality

    +

    2.14.3 Additional Features and Functionality

    • Support has been added for OpenPGP regular expression signature packets.
    • Support has been added for OpenPGP PolicyURI signature packets.
    • @@ -465,16 +488,16 @@

      2.13.3 Additional Features and Functionality

    • ASN.1 object support has been added for the Lightweight Certificate Management Protocol (CMP), currently in draft.
    • A HybridValueParamterSpec class has been added for use with KeyAgreement to support SP 800-56C hybrid (so classical/post-quantum) key agreement.
    -

    2.13.4 Notes

    +

    2.14.4 Notes

    • The deprecated QTESLA implementation has been removed from the BCPQC provider.
    • The submission update to SPHINCS+ has been added. This changes the generation of signatures - particularly deterministic ones.
    -

    2.14.1 Version

    +

    2.15.1 Version

    Release: 1.70
    Date:      2021, November 29th. -

    2.14.2 Defects Fixed

    +

    2.15.2 Defects Fixed

    • Blake 3 output limit is enforced.
    • The PKCS12 KeyStore was relying on default precedence for its key Cipher implementation so was sometimes failing if used from the keytool. The KeyStore class now makes sure it uses the correct Cipher implementation.
    • @@ -488,7 +511,7 @@

      2.14.2 Defects Fixed

    • The lack of close() in the ASN.1 Dump command line utility was triggering false positives in some code analysis tools. A close() call has been added.
    • PGPPublicKey.getBitStrength() now properly recognises EdDSA keys.
    -

    2.14.3 Additional Features and Functionality

    +

    2.15.3 Additional Features and Functionality

    • Missing PGP CRC checksums can now be optionally ignored using setDetectMissingCRC() (default false) on ArmoredInputStream.
    • PGPSecretKey.copyWithNewPassword() now has a variant which uses USAGE_SHA1 for key protection if a PGPDigestCalculator is passed in.
    • @@ -527,15 +550,15 @@

      2.14.3 Additional Features and Functionality

    • The JcePKCSPBEOutputEncryptorBuilder now supports SCRYPT with ciphers that do not have algorithm parameters (e.g. AESKWP).
    • Support is now added for certificates using ETSI TS 103 097, "Intelligent Transport Systems (ITS)" in the bcpkix package.
    -

    2.14.4 Notes.

    +

    2.15.4 Notes.

    • While this release should maintain source code compatibility, developers making use of some parts of the ASN.1 library will find that some classes need recompiling. Apologies for the inconvenience.
    -

    2.15.1 Version

    +

    2.16.1 Version

    Release: 1.69
    Date:      2021, June 7th. -

    2.15.2 Defects Fixed

    +

    2.16.2 Defects Fixed

    • Lightweight and JCA conversion of Ed25519 keys in the PGP API could drop the leading byte as it was zero. This has been fixed.
    • Marker packets appearing at the start of PGP public key rings could cause parsing failure. This has been fixed.
    • @@ -555,7 +578,7 @@

      2.15.2 Defects Fixed

    • Fix various conversions and interoperability for XDH and EdDSA between BC and SunEC providers.
    • TLS: Prevent attempts to use KeyUpdate mechanism in versions before TLS 1.3.
    -

    2.15.3 Additional Features and Functionality

    +

    2.16.3 Additional Features and Functionality

    • GCM-SIV has been added to the lightweight API and the provider.
    • Blake3 has been added to the lightweight API.
    • @@ -596,24 +619,24 @@

      2.15.3 Additional Features and Functionality

    • BCJSSE: Key managers now support EC credentials for use with TLS 1.3 ECDSA signature schemes (including brainpool).
    • TLS: Add TLS 1.3 support for brainpool curves per RFC 8734.
    -

    2.15.4 Notes

    +

    2.16.4 Notes

    • There is a small API change in the PKIX package to the DigestAlgorithmIdentifierFinder interface as a find() method that takes an ASN1ObjectIdentifier has been added to it. For people wishing to extend their own implementations, see DefaultDigestAlgorithmIdentifierFinder for a sample implementation.
    • A version of the bcmail API supporting Jakarta Mail has now been added (see bcjmail jar).
    • Some work has been done on moving out code that does not need to be in the provider jar. This has reduced the size of the provider jar and should also make it easier for developers to patch the classes involved as they no longer need to be signed. bcpkix and bctls are both dependent on the new bcutil jar.
    -

    2.16.1 Version

    +

    2.17.1 Version

    Release: 1.68
    Date:      2020, December 21st. -

    2.16.2 Defects Fixed

    +

    2.17.2 Defects Fixed

    • Some BigIntegers utility methods would fail for BigInteger.ZERO. This has been fixed.
    • PGPUtil.isKeyRing() was not detecting secret sub-keys in its input. This has been fixed.
    • The ASN.1 class, ArchiveTimeStamp was insisting on a value for the optional reducedHashTree field. This has been fixed.
    • BCJSSE: Lock against multiple writers - a possible synchronization issue has been removed.
    -

    2.16.3 Additional Features and Functionality

    +

    2.17.3 Additional Features and Functionality

    • BCJSSE: Added support for system property com.sun.net.ssl.requireCloseNotify. Note that we are using a default value of 'true'.
    • BCJSSE: 'TLSv1.3' is now a supported protocol for both client and server. For this release it is only enabled by default for the 'TLSv1.3' SSLContext, but can be explicitly enabled using 'setEnabledProtocols' on an SSLSocket or SSLEngine, or via SSLParameters.
    • @@ -624,10 +647,10 @@

      2.16.3 Additional Features and Functionality

    -

    2.17.1 Version

    +

    2.18.1 Version

    Release: 1.67
    Date:      2020, November 1st. -

    2.17.2 Defects Fixed

    +

    2.18.2 Defects Fixed

    • BCJSSE: SunJSSE compatibility fix - override of getChannel() removed and 'urgent data' behaviour should now conform to what the SunJSSE expects.
    • Nested BER data could sometimes cause issues in octet strings. This has been fixed.
    • @@ -639,7 +662,7 @@

      2.17.2 Defects Fixed

    • Zero length data would cause an unexpected exception from RFC5649WrapEngine. This has been fixed.
    • OpenBSDBcrypt was failing to handle some valid prefixes. This has been fixed.
    -

    2.17.3 Additional Features and Functionality

    +

    2.18.3 Additional Features and Functionality

    • Performance of Argon2 has been improved.
    • Performance of Noekeon has been improved.
    • @@ -657,15 +680,15 @@

      2.17.3 Additional Features and Functionality

    • Mode name checks in Cipher strings should now make sure an improper mode name always results in a NoSuchAlgorithmException.
    • In line with changes in OpenSSL, the OpenSSLPBKDF now uses UTF-8 encoding.
    -

    2.17.4 Security Advisory

    +

    2.18.4 Security Advisory

    • As described in CVE-2020-28052, the OpenBSDBCrypt.checkPassword() method had a flaw in it due to a change for BC 1.65. BC 1.66 is also affected. The issue is fixed in BC 1.67. If you are using OpenBSDBCrypt.checkPassword() and you are using BC 1.65 or BC 1.66 we strongly advise moving to BC 1.67 or later.
    -

    2.18.1 Version

    +

    2.19.1 Version

    Release: 1.66
    Date:      2020, July 4th. -

    2.18.2 Defects Fixed

    +

    2.19.2 Defects Fixed

    • EdDSA verifiers now reset correctly after rejecting overly long signatures.
    • BCJSSE: SSLSession.getPeerCertificateChain could throw NullPointerException. This has been fixed.
    • @@ -682,7 +705,7 @@

      2.18.2 Defects Fixed

    • For a few values the cSHAKE implementation would add unnecessary pad bytes where the N and S strings produced encoded data that was block aligned. This has been fixed.
    • There were a few circumstances where Argon2BytesGenerator might hit an unexpected null. These have been removed.
    -

    2.18.3 Additional Features and Functionality

    +

    2.19.3 Additional Features and Functionality

    • The qTESLA signature algorithm has been updated to v2.8 (20191108).
    • BCJSSE: Client-side OCSP stapling now supports status_request_v2 extension.
    • @@ -701,15 +724,15 @@

      2.18.3 Additional Features and Functionality

    • Performance of the Base64 encoder has been improved.
    • The PGPPublicKey class will now include direct key sigantures when checking for key expiry times.
    -

    2.18.4 Notes

    +

    2.19.4 Notes

    The qTESLA update breaks compatibility with previous versions. Private keys now include a hash of the public key at the end, and signatures are no longer interoperable with previous versions.

    -

    2.19.1 Version

    +

    2.20.1 Version

    Release: 1.65
    Date:      2020, March 31st. -

    2.19.2 Defects Fixed

    +

    2.20.2 Defects Fixed

    • DLExternal would encode using DER encoding for tagged SETs. This has been fixed.
    • ChaCha20Poly1305 could fail for large (>~2GB) files. This has been fixed.
    • @@ -721,7 +744,7 @@

      2.19.2 Defects Fixed

    • BCJSSE: Choice of credentials and signing algorithm now respect the peer's signature_algorithms extension properly.
    • BCJSSE: KeyManager for KeyStoreBuilderParameters no longer leaks memory.
    -

    2.19.3 Additional Features and Functionality

    +

    2.20.3 Additional Features and Functionality

    • LMS and HSS (RFC 8554) support has been added to the low level library and the PQC provider.
    • SipHash128 support has been added to the low level library and the JCE provider.
    • @@ -735,10 +758,10 @@

      2.19.3 Additional Features and Functionality

    • TLS: DSA in JcaTlsCrypto now falls back to stream signing to work around NoneWithDSA limitations in default provider.
    -

    2.20.1 Version

    +

    2.21.1 Version

    Release: 1.64
    Date:      2019, October 7th. -

    2.20.2 Defects Fixed

    +

    2.21.2 Defects Fixed

    • OpenSSH: Fixed padding in generated Ed25519 private keys.
    • Validation of headers in PemReader now looks for tailing dashes in header.
    • @@ -746,7 +769,7 @@

      2.20.2 Defects Fixed

    • Some compatibility issues around the signature encryption algorithm field in CMS SignedData and the GOST algorithms have been addressed.
    • GOST3410-2012-512 now uses the GOST3411-2012-256 as its KDF digest.
    -

    2.20.3 Additional Features and Functionality

    +

    2.21.3 Additional Features and Functionality

    • PKCS12: key stores containing only certificates can now be created without the need to provide passwords.
    • BCJSSE: Initial support for AlgorithmConstraints; protocol versions and cipher suites.
    • @@ -759,20 +782,20 @@

      2.20.3 Additional Features and Functionality

    • Support for Java 11's NamedParameterSpec class has been added (using reflection) to the EC and EdEC KeyPairGenerator implementations.
    -

    2.20.4 Removed Features and Functionality

    +

    2.21.4 Removed Features and Functionality

    • Deprecated ECPoint 'withCompression' tracking has been removed.
    -

    2.20.5 Security Advisory

    +

    2.21.5 Security Advisory

    • A change to the ASN.1 parser in 1.63 introduced a regression that can cause an OutOfMemoryError to occur on parsing ASN.1 data. We recommend upgrading to 1.64, particularly where an application might be parsing untrusted ASN.1 data from third parties.
    -

    2.21.1 Version

    +

    2.22.1 Version

    Release: 1.63
    Date:      2019, September 10th. -

    2.21.2 Defects Fixed

    +

    2.22.2 Defects Fixed

    • The ASN.1 parser would throw a large object exception for some objects which could be safely parsed. This has been fixed.
    • GOST3412-2015 CTR mode was unusable at the JCE level. This has been fixed.
    • @@ -791,7 +814,7 @@

      2.21.2 Defects Fixed

    • It is now possible to specify different S-Box parameters for the GOST 28147-89 MAC.
    -

    2.21.3 Additional Features and Functionality

    +

    2.22.3 Additional Features and Functionality

    • QTESLA is now updated with the round 2 changes. Note: the security catergories, and in some cases key generation and signatures, have changed. For people interested in comparison, the round 1 version is now moved to org.bouncycastle.pqc.crypto.qteslarnd1 - this package will be deleted in 1.64. Please keep in mind that QTESLA may continue to evolve.
    • Support has been added for generating Ed25519/Ed448 signed certificates.
    • @@ -804,10 +827,10 @@

      2.21.3 Additional Features and Functionality

    • The valid path for EST services has been updated to cope with the characters used in the Aruba clearpass EST implementation.
    -

    2.22.1 Version

    +

    2.23.1 Version

    Release: 1.62
    Date:      2019, June 3rd. -

    2.22.2 Defects Fixed

    +

    2.23.2 Defects Fixed

    • DTLS: Fixed infinite loop on IO exceptions.
    • DTLS: Retransmission timers now properly apply to flights monolithically.
    • @@ -824,7 +847,7 @@

      2.22.2 Defects Fixed

    • CertificateFactory now enforces presence of PEM headers when required.
    • A performance issue with RSA key pair generation that was introduced in 1.61 has been mostly eliminated.
    -

    2.22.3 Additional Features and Functionality

    +

    2.23.3 Additional Features and Functionality

    • Builders for X509 certificates and CRLs now support replace and remove extension methods.
    • DTLS: Added server-side support for HelloVerifyRequest.
    • @@ -845,10 +868,10 @@

      2.22.3 Additional Features and Functionality

    • Support for the Ethereum flavor of IES has been added to the lightweight API.
    -

    2.23.1 Version

    +

    2.24.1 Version

    Release: 1.61
    Date:      2019, February 4th. -

    2.23.2 Defects Fixed

    +

    2.24.2 Defects Fixed

    • Use of EC named curves could be lost if keys were constructed via a key factory and algorithm parameters. This has been fixed.
    • RFC3211WrapEngine would not properly handle messages longer than 127 bytes. This has been fixed.
    • @@ -869,7 +892,7 @@

      2.23.2 Defects Fixed

    • Several parsing issues related to the processing of CMP PKIPublicationInfo have been fixed.
    • The ECGOST curves for id-tc26-gost-3410-12-256-paramSetA and id-tc26-gost-3410-12-512-paramSetC had incorrect co-factors. These have been fixed.
    -

    2.23.3 Additional Features and Functionality

    +

    2.24.3 Additional Features and Functionality

    • The qTESLA signature algorithm has been added to PQC light-weight API and the PQC provider.
    • The password hashing function, Argon2 has been added to the lightweight API.
    • @@ -893,15 +916,15 @@

      2.23.3 Additional Features and Functionality

    • SM2 in public key cipher mode has been added to the provider API.
    • The BCFKSLoadStoreParameter has been extended to allow the use of certificates and digital signatures for verifying the integrity of BCFKS key stores.
    -

    2.23.4 Removed Features and Functionality

    +

    2.24.4 Removed Features and Functionality

    • Deprecated methods for EC point construction independent of curves have been removed.
    -

    2.24.1 Version

    +

    2.25.1 Version

    Release: 1.60
    Date:      2018, June 30 -

    2.24.2 Defects Fixed

    +

    2.25.2 Defects Fixed

    • Base64/UrlBase64 would throw an exception on a zero length string. This has been fixed.
    • Base64/UrlBase64 would throw an exception if there was whitespace in the last 4 characters. This has been fixed.
    • @@ -922,7 +945,7 @@

      2.24.2 Defects Fixed

    • In some situations the use of sm2p256v1 would result in "unknown curve name". This has been fixed.
    • CMP PollReqContent now supports multiple certificate request IDs.
    -

    2.24.3 Additional Features and Functionality

    +

    2.25.3 Additional Features and Functionality

    • TLS: Extended CBC padding is now optional (and disabled by default).
    • TLS: Now supports channel binding 'tls-server-end-point'.
    • @@ -950,16 +973,16 @@

      2.24.3 Additional Features and Functionality

    • Support has been added for the German BSI KAEG Elliptic Curve key agreement algorithm with X9.63 as the KDF to the JCE.
    • Support has been added for the German BSI KAEG Elliptic Curve session key KDF to the lightweight API.
    -

    2.24.4 Security Related Changes and CVE's Addressed by this Release

    +

    2.25.4 Security Related Changes and CVE's Addressed by this Release

    • CVE-2018-1000180: issue around primality tests for RSA key pair generation if done using only the low-level API.
    • CVE-2018-1000613: lack of class checking in deserialization of XMSS/XMSS^MT private keys with BDS state information.
    -

    2.25.1 Version

    +

    2.26.1 Version

    Release: 1.59
    Date:      2017, December 28 -

    2.25.2 Defects Fixed

    +

    2.26.2 Defects Fixed

    • Issues with using PQC based keys with the provided BC KeyStores have now been fixed.
    • ECGOST-2012 public keys were being encoded with the wrong OID for the digest parameter in the algorithm parameter set. This has been fixed.
    • @@ -973,7 +996,7 @@

      2.25.2 Defects Fixed

    • An off-by-one error for the max N check for SCRYPT has been fixed. SCRYPT should now be compliant with RFC 7914.
    • ASN1GeneralizedTime will now accept a broader range of input strings.
    -

    2.25.3 Additional Features and Functionality

    +

    2.26.3 Additional Features and Functionality

    • GOST3410-94 private keys encoded using ASN.1 INTEGER are now accepted in private key info objects.
    • SCRYPT is now supported as a SecretKeyFactory in the provider and in the PKCS8 APIs
    • @@ -992,15 +1015,15 @@

      2.25.3 Additional Features and Functionality

    • A DEROtherInfo generator for key agreement using NewHope as the source of the shared private info has been added that can be used in conjunction with regular key agreement algorithms.
    • RFC 7748: Added low-level implementations of X25519 and X448.
    -

    2.25.4 Security Related Changes and CVE's Addressed by this Release

    +

    2.26.4 Security Related Changes and CVE's Addressed by this Release

    • CVE-2017-13098 ("ROBOT"), a Bleichenbacher oracle in TLS when RSA key exchange is negotiated. This potentially affected BCJSSE servers and any other TLS servers configured to use JCE for the underlying crypto - note the two TLS implementations using the BC lightweight APIs are not affected by this.
    -

    2.26.1 Version

    +

    2.27.1 Version

    Release: 1.58
    Date:      2017, August 18 -

    2.26.2 Defects Fixed

    +

    2.27.2 Defects Fixed

    • NewHope and SPHINCS keys are now correctly created off certificates by the BC provider.
    • Use of the seeded constructor with SecureRandom() and the BC provider in first position could cause a stack overflow error. This has been fixed.
    • @@ -1014,7 +1037,7 @@

      2.26.2 Defects Fixed

    • A race condition that could occur inside the HybridSecureRandom on reseed and result in an exception has been fixed.
    • DTLS now supports records containing multiple handshake messages.
    -

    2.26.3 Additional Features and Functionality

    +

    2.27.3 Additional Features and Functionality

    • An implementation of GOST3410-2012 has been added to light weight API and the JCA provider.
    • Support for ECDH GOST3410-2012 and GOST3410-2001 have been added. The CMS API can also handle reading ECDH GOST3410 key transport messages.
    • @@ -1034,16 +1057,16 @@

      2.26.3 Additional Features and Functionality

    • The new TLS API now supports RFC 7633 - X.509v3 TLS Feature Extension (e.g. "must staple"), enabled in default clients.
    • TLS exceptions have been made more directly informative.
    -

    2.26.4 Removed Features and Functionality

    +

    2.27.4 Removed Features and Functionality

    • Per RFC 7465, removed support for RC4 in the new TLS API.
    • Per RFC 7568, removed support for SSLv3 in the new TLS API.
    -

    2.27.1 Version

    +

    2.28.1 Version

    Release: 1.57
    Date:      2017, May 11 -

    2.27.2 Defects Fixed

    +

    2.28.2 Defects Fixed

    • A class cast exception for master certification removal in PGPPublicKey.removeCertification() by certification has been fixed.
    • GOST GOFB 28147-89 mode had an edge condition concerning the incorrect calculation of N4 (see section 6.1 of RFC 5830) affecting about 1% of IVs. This has been fixed.
    • @@ -1060,7 +1083,7 @@

      2.27.2 Defects Fixed

    • EC FixedPointCombMultiplier avoids 'infinity' point in lookup tables, reducing timing side-channels.
    • Reuse of a Blake2b digest with a call to reset() rather than doFinal() could result in incorrect padding being introduced and the wrong digest result produced. This has been fixed.
    -

    2.27.3 Additional Features and Functionality

    +

    2.28.3 Additional Features and Functionality

    • ARIA (RFC 5794) is now supported by the provider and the lightweight API.
    • ARIA Key Wrapping (RFC 5649 style) is now supported by the provider and the lightweight API.
    • @@ -1070,23 +1093,23 @@

      2.27.3 Additional Features and Functionality

    • A test client for EST which will interop with the 7030 test server at http://testrfc7030.com/ has been added to the general test module in the current source tree.
    • The BCJSSE provider now supports SSLContext.getDefault(), with very similar behaviour to the SunJSSE provider, including checks of the relevant javax.net.ssl.* system properties and auto-loading of jssecacerts or cacerts as the default trust store.
    -

    2.27.4 Security Related Changes

    +

    2.28.4 Security Related Changes

    • The default parameter sizes for DH and DSA are now 2048. If you have been relying on key pair generation without passing in parameters generated keys will now be larger.
    • Further work has been done on preventing accidental re-use of a GCM cipher without first changing its key or iv.
    -

    2.28.1 Version

    +

    2.29.1 Version

    Release: 1.56
    Date:      2016, December 23 -

    2.28.2 Defects Fixed

    +

    2.29.2 Defects Fixed

    • See section 2.15.4 for Security Defects.
    • Using unknown status with the ASN.1 CertStatus primitive could result in an IllegalArgumentException on construction. This has been fixed.
    • A potentional NullPointerException in a precomputation in WNafUtil has been removed.
    • PGPUtil.getDecoderStream() would throw something other than an IOException for empty and very small data. This has been fixed.
    -

    2.28.3 Additional Features and Functionality

    +

    2.29.3 Additional Features and Functionality

    • Support for the explicit setting of AlgorithmParameters has been added to the JceCMSContentEncryptorBuilder and the JceCMSMacCaculatorBuilder classes to allow configuration of the session cipher/MAC used.
    • EC, ECGOST3410, and DSTU4145 Public keys are now validated on construction in the JCA/JCE and the light weight API.
    • @@ -1102,7 +1125,7 @@

      2.28.3 Additional Features and Functionality

    • SHA-3 support has been added to BcDefaultDigestProvider.
    • A higher level TLS API and JSSE provider have been added to the project.
    -

    2.28.4 Security Related Changes and CVE's Addressed by this Release

    +

    2.29.4 Security Related Changes and CVE's Addressed by this Release

    • It is now possible to configure the provider to only import keys for specific named curves.
    • Work has been done to improve the "constant time" behaviour of the RSA padding mechanisms.
    • @@ -1121,15 +1144,15 @@

      2.28.3 Additional Features and Functionality

    • CVE-2016-1000346: Other party DH public key not fully validated. This can cause issues as invalid keys can be used to reveal details about the other party's private key where static Diffie-Hellman is in use. As of this release the key parameters are checked on agreement calculation.
    • CVE-2016-1000352: ECIES allows the use of unsafe ECB mode. This algorithm is now removed from the provider.
    -

    2.28.5 Security Advisory

    +

    2.29.5 Security Advisory

    • We consider the carry propagation bugs fixed in this release to have been exploitable in previous releases (1.51-1.55), for static ECDH, to reveal the long-term key, per "Practical realisation and elimination of an ECC-related software bug attack", Brumley et.al.. The most common case of this would be the non-ephemeral ECDH ciphersuites in TLS. These are not enabled by default in our TLS implementations, but they can be enabled explicitly by users. We recommend that users DO NOT enable static ECDH ciphersuites for TLS.
    -

    2.29.1 Version

    +

    2.30.1 Version

    Release: 1.55
    Date:      2016, August 18 -

    2.29.2 Defects Fixed

    +

    2.30.2 Defects Fixed

    • Issues with cloning of blake digests with salts and personalisation strings have been fixed.
    • The JceAsymmetricValueDecryptor in the CRMF package now attempts to recognise a wider range of parameters for the key wrapping algorithm, rather than relying on a default.
    • @@ -1150,7 +1173,7 @@

      2.29.2 Defects Fixed

    • Trying to use of non-default parameters for OAEP in CRMF would resort to the default parameter set. This has been fixed.
    • If the BC provider was not registered, creating a CertificateFactory would cause a new provider object to be created. This has been fixed.
    -

    2.29.3 Additional Features and Functionality

    +

    2.30.3 Additional Features and Functionality

    • The DANE API has been updated to reflect the latest standard changes.
    • The signature algorithm SPHINCS-256 has been added to the post-quantum provider (BCPQC). Support is in place for SHA-512 and SHA3-512 (using trees based around SHA512_256 and SHA3_256 respectively).
    • @@ -1168,10 +1191,10 @@

      2.29.3 Additional Features and Functionality

    • Additional search methods have been added to PGP public and secret key rings.
    -

    2.30.1 Version

    +

    2.31.1 Version

    Release: 1.54
    Date:      2015, December 29 -

    2.30.2 Defects Fixed

    +

    2.31.2 Defects Fixed

    • Blake2b-160, Blake2b-256, Blake2b-384, and Blake2b-512 are now actually in the provider and an issue with cloning Blake2b digests has been fixed.
    • PKCS#5 Scheme 2 using DESede CBC is now supported by the PKCS#12 implementation.
    • @@ -1180,7 +1203,7 @@

      2.30.2 Defects Fixed

    • It turns out, after advice one way and another that the NESSIE test vectors for Serpent are now what should be followed and that the vectors in the AES submission are regarded as an algorithm called Tnepres. The Serpent version now follows the NESSIE vectors, and the Tnepres cipher has been added to the provider and the lightweight API for compatibility.
    • Problems with DTLS record-layer version handling were resolved, making version negotiation work properly.
    -

    2.30.3 Additional Features and Functionality

    +

    2.31.3 Additional Features and Functionality

    • Camellia and SEED key wrapping are now supported for CMS key agreement
    • The BC TLS/DTLS code now includes a non-blocking API.
    • @@ -1190,19 +1213,19 @@

      2.30.3 Additional Features and Functionality

    • Support has been added to the CMS API for PKCS#7 ANY type encapsulated content where the encapsulated content is not an OCTET STRING.
    • PSSSigner in the lightweight API now supports fixed salts.
    -

    2.30.4 Security Advisory

    +

    2.31.4 Security Advisory

    • (D)TLS 1.2: Motivated by CVE-2015-7575, we have added validation that the signature algorithm received in DigitallySigned structures is actually one of those offered (in signature_algorithms extension or CertificateRequest). With our default TLS configuration, we do not believe there is an exploitable vulnerability in any earlier releases. Users that are customizing the signature_algorithms extension, or running a server supporting client authentication, are advised to double-check that they are not offering any signature algorithms involving MD5.
    -

    2.30.5 Notes

    +

    2.31.5 Notes

    If you have been using Serpent, you will need to either change to Tnepres, or take into account the fact that Serpent is now byte-swapped compared to what it was before.

    -

    2.31.1 Version

    +

    2.32.1 Version

    Release: 1.53
    Date:      2015, October 10 -

    2.31.2 Defects Fixed

    +

    2.32.2 Defects Fixed

    • The BC JCE cipher implementations could sometimes fail when used in conjunction with the JSSE and NIO. This has been fixed.
    • PGPPublicKey.getBitStrength() always returned 0 for EC keys. This has been fixed.
    • @@ -1227,7 +1250,7 @@

      2.31.2 Defects Fixed

    • Some decidedly odd argument casting in the PKIXCertPathValidator has been fixed to throw an InvalidAlgorithmParameterException.
    • Presenting an empty array of certificates to the PKIXCertPathValidator would cause an IndexOutOfRangeException instead of a CertPathValidatorException. This has been fixed.
    -

    2.31.3 Additional Features and Functionality

    +

    2.32.3 Additional Features and Functionality

    • It is now possible to specify that an unwrapped key must be usable by a software provider in the asymmetric unwrappers for CMS.
    • A Blake2b implementation has been added to the provider and lightweight API.
    • @@ -1243,15 +1266,15 @@

      2.31.3 Additional Features and Functionality

    • The PKCS#12 key store will now garbage collect orphaned certificates on saving.
    • Caching for ASN.1 ObjectIdentifiers has been rewritten to make use of an intern method. The "usual suspects" are now interned automatically, and the cache is used by the parser. Other OIDs can be added to the cache by calling ASN1ObjectIdentifier.intern().
    -

    2.31.4 Notes

    +

    2.32.4 Notes

    It turns out there was a similar, but different, issue in Crypto++ to the BC issue with ECIES. Crypto++ 6.0 now offers a corrected version of ECIES which is compatible with that which is now in BC.

    -

    2.32.1 Version

    +

    2.33.1 Version

    Release: 1.52
    Date:      2015, March 2 -

    2.32.2 Defects Fixed

    +

    2.33.2 Defects Fixed

    • GenericSigner in the lightweight API would fail if the digest started with a zero byte, occasionally causing a TLS negotiation to fail. This has been fixed.
    • Some BC internal classes expected the BC provider to be accessible within the provider. This has been fixed.
    • @@ -1268,7 +1291,7 @@

      2.32.2 Defects Fixed

    • A badly formed issuer in a X.509 certificate could cause a null pointer exception in X509CertificateHolder.toString(). This has been fixed.
    • CMSSignedData.verifySignatures() could fail on a correct counter signature due to a mismatch of the SID. This has been fixed.
    -

    2.32.3 Additional Features and Functionality

    +

    2.33.3 Additional Features and Functionality

    • The CMP support class CMPCertificate restricted the types of certificates that could be added. A more flexible method has been introduced to allow for other certificate types.
    • Support classes have be added for DNS-based Authentication of Named Entities (DANE) to the PKIX distribution.
    • @@ -1296,15 +1319,15 @@

      2.32.3 Additional Features and Functionality

    • Support for some JDK1.5+ language features has finally made its way into the repository.
    • A load store parameter, PKCS12StoreParameter, has been added to support DER only encoding of PKCS12 key stores.
    -

    2.32.4 Security Advisory

    +

    2.33.4 Security Advisory

    • The CTR DRBGs would not populate some bytes in the requested block of random bytes if the size of the block requested was not an exact multiple of the block size of the underlying cipher being used in the DRBG. If you are using the CTR DRBGs with "odd" keysizes, we strongly advise upgrading to this release, or contacting us for a work around.
    -

    2.33.1 Version

    +

    2.34.1 Version

    Release: 1.51
    Date:      2014, July 28 -

    2.33.2 Defects Fixed

    +

    2.34.2 Defects Fixed

    • The AEAD GCM AlgorithmParameters object was unable to return a GCMParameterSpec object. This has been fixed.
    • Cipher.getIV() was returning null for AEAD mode ciphers. This has been fixed.
    • @@ -1319,7 +1342,7 @@

      2.33.2 Defects Fixed

    • PKCS#12 files containing keys/certificates with empty attribute sets attached to them no longer cause an ArrayIndexOutOfBoundsException to be thrown.
    • Issues with certificate verification and server side DTLS/TLS 1.2 have now been fixed.
    -

    2.33.3 Additional Features and Functionality

    +

    2.34.3 Additional Features and Functionality

    • The range of key algorithm names that will be interpreted by KeyAgreement.generateSecret() has been expanded for ECDH derived algorithms in the provider. A KeyAgreement of ECDHwithSHA1KDF can now be explicitly created.
    • ECIES now supports the use of IVs with the underlying block cipher and CBC mode in both the lightweight and the JCE APIs.
    • @@ -1346,17 +1369,17 @@

      2.33.3 Additional Features and Functionality

    • Full support is now provided for client-side auth in the D/TLS server code.
    • Compatibility issues with some OSGI containers have been addressed.
    -

    2.33.4 Notes

    +

    2.34.4 Notes

    • Support for NTRUSigner has been deprecated as the algorithm has been withdrawn.
    • Some changes have affected the return values of some methods. If you are migrating from an earlier release, it is recommended to recompile before using this release.
    • There has been further clean out of deprecated methods in this release. If your code has previously been flagged as using a deprecated method you may need to change it. The OpenPGP API is the most heavily affected.
    -

    2.34.1 Version

    +

    2.35.1 Version

    Release: 1.50
    Date:      2013, December 3 -

    2.34.2 Defects Fixed

    +

    2.35.2 Defects Fixed

    • The DualECSP800DRBG sometimes truncated the last block in the generated stream incorrectly. This has been fixed.
    • Keys produced from RSA certificates with specialised parameters would lose the parameter settings. This has been fixed.
    • @@ -1370,7 +1393,7 @@

      2.34.2 Defects Fixed

    • Default RC2 parameters for 40 bit RC2 keys in CMSEnvelopedData were encoding incorrectly. This has been fixed.
    • In case of a long hash the DSTU4145 implementation would sometimes remove one bit too much during truncation. This has been fixed.
    -

    2.34.3 Additional Features and Functionality

    +

    2.35.3 Additional Features and Functionality

    • Additional work has been done on CMS recipient generation to simplify the generation of OAEP encrypted messages and allow for non-default parameters.
    • OCB implementation updated to account for changes in draft-irtf-cfrg-ocb-03.
    • @@ -1390,7 +1413,7 @@

      2.34.3 Additional Features and Functionality

    • The JDK 1.5+ provider will now recognise and use GCMParameterSpec if it is run in a 1.7 JVM.
    • Client side support and some server side support has been added for TLS/DTLS 1.2.
    -

    2.34.4 Notes

    +

    2.35.4 Notes

    • org.bouncycastle.crypto.DerivationFunction is now a base interface, the getDigest() method appears on DigestDerivationFunction.
    • Recent developments at NIST indicate the SHA-3 may be changed before final standardisation. Please bare this in mind if you are using it.
    • @@ -1400,10 +1423,10 @@

      2.34.4 Notes

    • ECDH support for OpenPGP should still be regarded as experimental. It is still possible there will be compliance issues with other implementations.
    -

    2.35.1 Version

    +

    2.36.1 Version

    Release: 1.49
    Date:      2013, May 31 -

    2.35.2 Defects Fixed

    +

    2.36.2 Defects Fixed

    • Occasional ArrayOutOfBounds exception in DSTU-4145 signature generation has been fixed.
    • The handling of escaped characters in X500 names is much improved.
    • @@ -1414,7 +1437,7 @@

      2.35.2 Defects Fixed

    • PEMParser would throw a NullPointerException if it ran into explicit EC curve parameters, it would also throw an Exception if the named curve was not already defined. The parser now returns X9ECParmameters for explicit parameters and returns an ASN1ObjectIdentifier for a named curve.
    • The V2TBSCertListGenerator was adding the wrong date type for CRL invalidity date extensions. This has been fixed.
    -

    2.35.3 Additional Features and Functionality

    +

    2.36.3 Additional Features and Functionality

    • A SecretKeyFactory has been added that enables use of PBKDF2WithHmacSHA.
    • Support has been added to PKCS12 KeyStores and PfxPdu to handle PKCS#5 encrypted private keys.
    • @@ -1443,16 +1466,16 @@

      2.35.3 Additional Features and Functionality

    • A basic commitment package has been introduced into the lightweight API containing a digest based commitment scheme.
    • It is now possible to set the NotAfter and NotBefore date in the CRMF CertificateRequestMessageBuilder class.
    -

    2.35.4 Notes

    +

    2.36.4 Notes

    • The NTRU implementation has been moved into the org.bouncycastle.pqc package hierarchy.
    • The change to PEMParser to support explicit EC curves is not backward compatible. If you run into a named curve you need to use org.bouncycastle.asn1.x9.ECNamedCurveTable.getByOID() to look the curve up if required.
    -

    2.36.1 Version

    +

    2.37.1 Version

    Release: 1.48
    Date:      2013, February 10 -

    2.36.2 Defects Fixed

    +

    2.37.2 Defects Fixed

    • Occasional key compatibility issues in IES due to variable length keys have been fixed.
    • PEMWriter now recognises the new PKCS10CertificationRequest object.
    • @@ -1463,7 +1486,7 @@

      2.36.2 Defects Fixed

    • The BC SSL implementation has been modified to deal with the "Lucky Thirteen" attack.
    • A regression in 1.47 which prevented key wrapping with regular symmetric PBE algorihtms has been fixed.
    -

    2.36.3 Additional Features and Functionality

    +

    2.37.3 Additional Features and Functionality

    • IES now supports auto generation of ephemeral keys in both the JCE and the lightweight APIs.
    • A new class PEMParser has been added to return the new CertificateHolder and Request objects introduced recently.
    • @@ -1478,10 +1501,10 @@

      2.36.3 Additional Features and Functionality

    • T61String now uses UTF-8 encoding by default rather than a simple 8 bit transform.
    -

    2.37.1 Version

    +

    2.38.1 Version

    Release: 1.47
    Date:      2012, March 30 -

    2.37.2 Defects Fixed

    +

    2.38.2 Defects Fixed

    • OpenPGP ID based certifications now support UTF-8. Note: this may mean that some old certifications no longer validate - if this happens a retry can be added using by converting the ID using Strings.fromByteArray(Strings.toByteArray(id)) - this will strip out the top byte in each character.
    • IPv4/IPv6 parsing in CIDR no longer assumes octet boundaries on a mask.
    • @@ -1498,7 +1521,7 @@

      2.37.2 Defects Fixed

    • Check of DH parameter L could reject some valid keys. This is now fixed.
    -

    2.37.3 Additional Features and Functionality

    +

    2.38.3 Additional Features and Functionality

    • Support is now provided via the RepeatedKey class to enable IV only re-initialisation in the JCE layer. The same effect can be acheived in the light weight API by using null as the key parameter when creating a ParametersWithIV object.
    • CRMF now supports empty poposkInput.
    • @@ -1518,15 +1541,15 @@

      2.37.3 Additional Features and Functionality

    • The J2ME lcrypto release now includes higher level classes for handling PKCS, CMS, CRMF, CMP, EAC, OpenPGP, and certificate generation.
    -

    2.37.4 Other notes

    +

    2.38.4 Other notes

    Okay, so we have had to do another release. The issue we have run into is that we probably didn't go far enough in 1.46, but we are now confident that moving from this release to 2.0 should be largely just getting rid of deprecated methods. While this release does change a lot it is relatively straight forward to do a port and we have a porting guide which explains the important ones. The area there has been the most change in is the ASN.1 library which was in bad need of a rewrite after 10 years of patching. On the bright side the rewrite did allow us to eliminate a few problems and bugs in the ASN.1 library, so we have some hope anyone porting to it will also have similar benefits. As with 1.46 the other point of emphasis has been making sure interface support is available for operations across the major APIs, so the lightweight API or some local role your own methods can be used instead for doing encryption and signing.

    -

    2.38.1 Version

    +

    2.39.1 Version

    Release: 1.46
    Date:      2011, February 23 -

    2.38.2 Defects Fixed

    +

    2.39.2 Defects Fixed

    • An edge condition in ECDSA which could result in an invalid signature has been fixed.
    • Exhaustive testing has been performed on the ASN.1 parser, eliminating another potential OutOfMemoryException and several escaping run time exceptions.
    • @@ -1535,7 +1558,7 @@

      2.38.2 Defects Fixed

    • DERGeneralizedTime.getDate() would produce incorrect results for fractional seconds. This has been fixed.
    • PSSSigner would produce incorrect results if the MGF digest and content digest were not the same. This has been fixed.
    -

    2.38.3 Additional Features and Functionality

    +

    2.39.3 Additional Features and Functionality

    • A null genTime can be passed to TimeStampResponseGenerator.generate() to generate timeNotAvailable error responses.
    • Support has been added for reading and writing of openssl PKCS#8 encrypted keys.
    • @@ -1552,7 +1575,7 @@

      2.38.3 Additional Features and Functionality

    • PGP public subkeys can now be separately decoded and encoded.
    • An IV can now be passed to an ISO9797Alg3Mac.
    -

    2.38.4 Other notes

    +

    2.39.4 Other notes

    Baring security patches we expect 1.46 will be the last of the 1.* releases. The next release of BC will be version 2.0. For this reason a lot of things in 1.46 that relate to CMS have been deprecated and @@ -1569,29 +1592,29 @@

    2.38.4 Other notes

  • The X509Name class will utlimately be replacde with the X500Name class, the getInstance() methods on both these classes allow conversion from one type to another.
  • The org.bouncycastle.cms.RecipientId class now has a collection of subclasses to allow for more specific recipient matching. If you are creating your own recipient ids you should use the constructors for the subclasses rather than relying on the set methods inherited from X509CertSelector. The dependencies on X509CertSelector and CertStore will be removed from the version 2 CMS API.
  • -

    2.39.1 Version

    +

    2.40.1 Version

    Release: 1.45
    Date:      2010, January 12 -

    2.39.2 Defects Fixed

    +

    2.40.2 Defects Fixed

    • OpenPGP now supports UTF-8 in file names for literal data.
    • The ASN.1 library was losing track of the stream limit in a couple of places, leading to the potential of an OutOfMemoryError on a badly corrupted stream. This has been fixed.
    • The provider now uses a privileged block for initialisation.
    • JCE/JCA EC keys are now serialisable.
    -

    2.39.3 Additional Features and Functionality

    +

    2.40.3 Additional Features and Functionality

    • Support for EC MQV has been added to the light weight API, provider, and the CMS/SMIME library.
    -

    2.39.4 Security Advisory

    +

    2.40.4 Security Advisory

    • This version of the provider has been specifically reviewed to eliminate possible timing attacks on algorithms such as GCM and CCM mode.
    -

    2.40.1 Version

    +

    2.41.1 Version

    Release: 1.44
    Date:      2009, October 9 -

    2.40.2 Defects Fixed

    +

    2.41.2 Defects Fixed

    • The reset() method in BufferedAsymmetricBlockCipher is now fully clearing the buffer.
    • Use of ImplicitlyCA with KeyFactory and Sun keyspec no longer causes NullPointerException.
    • @@ -1607,7 +1630,7 @@

      2.40.2 Defects Fixed

    • PKIXCertPathReviewer.getTrustAnchor() could occasionally cause a null pointer exception or an exception due to conflicting trust anchors. This has been fixed.
    • Handling of explicit CommandMap objects with the generation of S/MIME messages has been improved.
    -

    2.40.3 Additional Features and Functionality

    +

    2.41.3 Additional Features and Functionality

    • PEMReader/PEMWriter now support encrypted EC keys.
    • BC generated EC private keys now include optional fields required by OpenSSL.
    • @@ -1623,24 +1646,24 @@

      2.40.3 Additional Features and Functionality

    • Support for raw signatures has been extended to RSA and RSA-PSS in the provider. RSA support can be used in CMSSignedDataStreamGenerator to support signatures without signed attributes.
    -

    2.41.1 Version

    +

    2.42.1 Version

    Release: 1.43
    Date:      2009, April 13 -

    2.41.2 Defects Fixed

    +

    2.42.2 Defects Fixed

    • Multiple countersignature attributes are now correctly collected.
    • Two bugs in HC-128 and HC-256 related to sign extension and byte swapping have been fixed. The implementations now pass the latest ecrypt vector tests.
    • X509Name.hashCode() is now consistent with equals.
    -

    2.41.3 Security Advisory

    +

    2.42.3 Security Advisory

    • The effect of the sign extension bug was to decrease the key space the HC-128 and HC-256 ciphers were operating in and the byte swapping inverted every 32 bits of the generated stream. If you are using either HC-128 or HC-256 you must upgrade to this release.
    -

    2.42.1 Version

    +

    2.43.1 Version

    Release: 1.42
    Date:      2009, March 16 -

    2.42.2 Defects Fixed

    +

    2.43.2 Defects Fixed

    • A NullPointer exception which could be result from generating a diffie-hellman key has been fixed.
    • CertPath validation could occasionally mistakenly identify a delta CRL. This has been fixed.
    • @@ -1653,7 +1676,7 @@

      2.42.2 Defects Fixed

    • Multiplication by negative powers of two is fixed in BigInteger.
    • OptionalValidity now encodes correctly.
    -

    2.42.3 Additional Features and Functionality

    +

    2.43.3 Additional Features and Functionality

    • Support for NONEwithECDSA has been added.
    • Support for Grainv1 and Grain128 has been added.
    • @@ -1664,10 +1687,10 @@

      2.42.3 Additional Features and Functionality

    • Support for the SRP-6a protocol has been added to the lightweight API.
    -

    2.43.1 Version

    +

    2.44.1 Version

    Release: 1.41
    Date:      2008, October 1 -

    2.43.2 Defects Fixed

    +

    2.44.2 Defects Fixed

    • The GeneralName String constructor now supports IPv4 and IPv6 address parsing.
    • An issue with nested-multiparts with postamble for S/MIME that was causing signatures to fail verification has been fixed.
    • @@ -1678,7 +1701,7 @@

      2.43.2 Defects Fixed

    • Standard name "DiffieHellman" is now supported in the provider.
    • Better support for equality tests for '#' encoded entries has been added to X509Name.
    -

    2.43.3 Additional Features and Functionality

    +

    2.44.3 Additional Features and Functionality

    • Camellia is now 12.5% faster than previously.
    • A smaller version (around 8k compiled) of Camellia, CamelliaLightEngine has also been added.
    • @@ -1689,10 +1712,10 @@

      2.43.3 Additional Features and Functionality

    • Support for reading and extracting personalised certificates in PGP Secret Key rings has been added.
    -

    2.44.1 Version

    +

    2.45.1 Version

    Release: 1.40
    Date:      2008, July 12 -

    2.44.2 Defects Fixed

    +

    2.45.2 Defects Fixed

    • EAX mode ciphers were not resetting correctly after a doFinal/reset. This has been fixed.
    • The SMIME API was failing to verify doubly nested multipart objects in signatures correctly. This has been fixed.
    • @@ -1708,7 +1731,7 @@

      2.44.2 Defects Fixed

    • The '+' character can now be escaped or quoted in the constructor for X509Name, X509Prinicipal.
    • Fix to regression from 1.38: PKIXCertPathValidatorResult.getPublicKey was returning the wrong public key when the BC certificate path validator was used.
    -

    2.44.3 Additional Features and Functionality

    +

    2.45.3 Additional Features and Functionality

    • Galois/Counter Mode (GCM) has been added to the lightweight API and the JCE provider.
    • SignedPublicKeyAndChallenge and PKCS10CertificationRequest can now take null providers if you need to fall back to the default provider mechanism.
    • @@ -1716,15 +1739,15 @@

      2.44.3 Additional Features and Functionality

    • Unnecessary local ID attributes on certificates in PKCS12 files are now automatically removed.
    • The PKCS12 store types PKCS12-3DES-3DES and PKCS12-DEF-3DES-3DES have been added to support generation of PKCS12 files with both certificates and keys protected by 3DES.
    -

    2.44.4 Additional Notes

    +

    2.45.4 Additional Notes

    • Due to problems for some users caused by the presence of the IDEA algorithm, an implementation is no longer included in the default signed jars. Only the providers of the form bcprov-ext-*-*.jar now include IDEA.
    -

    2.45.1 Version

    +

    2.46.1 Version

    Release: 1.39
    Date:      2008, March 29 -

    2.45.2 Defects Fixed

    +

    2.46.2 Defects Fixed

    • A bug causing the odd NullPointerException has been removed from the LocalizedMessage class.
    • IV handling in CMS for the SEED and Camellia was incorrect. This has been fixed.
    • @@ -1738,7 +1761,7 @@

      2.45.2 Defects Fixed

    • A decoding issue with a mis-identified tagged object in CertRepMessage has been fixed.
    • \# is now properly recognised in the X509Name class.
    -

    2.45.3 Additional Features and Functionality

    +

    2.46.3 Additional Features and Functionality

    • Certifications associated with user attributes can now be created, verified and removed in OpenPGP.
    • API support now exists for CMS countersignature reading and production.
    • @@ -1753,10 +1776,10 @@

      2.45.3 Additional Features and Functionality

    • Support has been added to the provider for the VMPC MAC.
    -

    2.46.1 Version

    +

    2.47.1 Version

    Release: 1.38
    Date:      2007, November 7 -

    2.46.2 Defects Fixed

    +

    2.47.2 Defects Fixed

    • SMIME signatures containing non-standard quote-printable data could be altered by SMIME encryption. This has been fixed.
    • CMS signatures that do not use signed attributes were vulnerable to one of Bleichenbacher's RSA signature forgery attacks. This has been fixed.
    • @@ -1770,7 +1793,7 @@

      2.46.2 Defects Fixed

    • Overwriting entities in a PKCS#12 file was not fully compliant with the JavaDoc for KeyStore. This has been fixed.
    • TlsInputStream.read() could appear to return end of file when end of file had not been reached. This has been fixed.
    -

    2.46.3 Additional Features and Functionality

    +

    2.47.3 Additional Features and Functionality

    • Buffering in the streaming CMS has been reworked. Throughput is now usually higher and the behaviour is more predictable.
    • It's now possible to pass a table of hashes to a CMS detached signature rather than having to always pass the data.
    • @@ -1781,10 +1804,10 @@

      2.46.3 Additional Features and Functionality

    • CertPathReviewer has better handling for problem trust anchors.
    • Base64 encoder now does initial size calculations to try to improve resource usage.
    -

    2.47.1 Version

    +

    2.48.1 Version

    Release: 1.37
    Date:      2007, June 15 -

    2.47.2 Defects Fixed

    +

    2.48.2 Defects Fixed

    • The ClearSignedFileProcessor example for OpenPGP did not take into account trailing white space in the file to be signed. This has been fixed.
    • @@ -1798,7 +1821,7 @@

      2.47.2 Defects Fixed

    • The default private key length in the lightweght API for generated DiffieHellman parameters was absurdly small, this has been fixed.
    • Cipher.getParameters() for PBEwithSHAAndTwofish-CBC was returning null after intialisation. This has been fixed.
    -

    2.47.3 Additional Features and Functionality

    +

    2.48.3 Additional Features and Functionality

    • The block cipher mode CCM has been added to the provider and light weight API.
    • The block cipher mode EAX has been added to the provider and light weight API.
    • @@ -1817,10 +1840,10 @@

      2.47.3 Additional Features and Functionality

    • The JCE provider now supports RIPEMD160withECDSA.
    -

    2.48.1 Version

    +

    2.49.1 Version

    Release: 1.36
    Date:      2007, March 16 -

    2.48.2 Defects Fixed

    +

    2.49.2 Defects Fixed

    • DSA key generator now checks range and keysize.
    • Class loader issues with i18n classes should now be fixed.
    • @@ -1834,7 +1857,7 @@

      2.48.2 Defects Fixed

    • Some surrogate pairs were not assembled correctly by the UTF-8 decoder. This has been fixed.
    • Alias resolution in PKCS#12 is now case insensitive.
    -

    2.48.3 Additional Features and Functionality

    +

    2.49.3 Additional Features and Functionality

    • CMS/SMIME now supports basic EC KeyAgreement with X9.63.
    • CMS/SMIME now supports RFC 3211 password based encryption.
    • @@ -1850,10 +1873,10 @@

      2.48.3 Additional Features and Functionality

    • DSASigner now handles long messages. SHA2 family digest support for DSA has been added to the provider.
    -

    2.49.1 Version

    +

    2.50.1 Version

    Release: 1.35
    Date:      2006, December 16 -

    2.49.2 Defects Fixed

    +

    2.50.2 Defects Fixed

    • Test data files are no longer in the provider jars.
    • SMIMESignedParser now handles indefinite length data in SignerInfos.
    • @@ -1868,7 +1891,7 @@

      2.49.2 Defects Fixed

    • The IESEngine could incorrectly encrypt data when used in block cipher mode. This has been fixed.
    • An error in the encoding of the KEKRecipientInfo has been fixed. Compatability warning: this may mean that versions of BC mail prior to 1.35 will have trouble processing KEK messages produced by 1.35 or later.
    -

    2.49.3 Additional Features and Functionality

    +

    2.50.3 Additional Features and Functionality

    • Further optimisations to elliptic curve math libraries.
    • API now incorporates a CertStore which should be suitable for use with LDAP.
    • @@ -1890,10 +1913,10 @@

      2.49.3 Additional Features and Functionality

    • PGP packet streams can now be closed off using close() on the returned stream as well as closing the generator.
    -

    2.50.1 Version

    +

    2.51.1 Version

    Release: 1.34
    Date:      2006, October 2 -

    2.50.2 Defects Fixed

    +

    2.51.2 Defects Fixed

    • Endianess of integer conversion in KDF2BytesGenerator was incorrect. This has been fixed.
    • Generating critical signature subpackets in OpenPGP would result in a zero packet tag. This has been fixed. @@ -1905,7 +1928,7 @@

      2.50.2 Defects Fixed

    • PGP Identity strings were only being interpreted as ASCII rather than UTF-8. This has been fixed.
    • CertificateFactory.generateCRLs now returns a Collection rather than null.
    -

    2.50.3 Additional Features and Functionality

    +

    2.51.3 Additional Features and Functionality

    • An ISO18033KDFParameters class had been added to support ISO18033 KDF generators.
    • An implemention of the KDF1 bytes generator algorithm has been added. @@ -1925,16 +1948,16 @@

      2.50.3 Additional Features and Functionality

    • Performance of the prime number generation in the BigInteger library has been further improved.
    • In line with RFC 3280 section 4.1.2.4 DN's are now encoded using UTF8String by default rather than PrintableString.
    -

    2.50.4 Security Advisory

    +

    2.51.4 Security Advisory

    • If you are using public exponents with the value three you *must* upgrade to this release, otherwise it will be possible for attackers to exploit some of Bleichenbacher's RSA signature forgery attacks on your applications.
    -

    2.51.1 Version

    +

    2.52.1 Version

    Release: 1.33
    Date:      2006, May 3 -

    2.51.2 Defects Fixed

    +

    2.52.2 Defects Fixed

    • OCSPResponseData was including the default version in its encoding. This has been fixed.
    • BasicOCSPResp.getVersion() would throw a NullPointer exception if called on a default version response. This has been fixed. @@ -1943,7 +1966,7 @@

      2.51.2 Defects Fixed

    • ArmoredInputStream was not closing the underlying stream on close. This has been fixed.
    • Small base64 encoded strings with embedded white space could decode incorrectly using the Base64 class. This has been fixed.
    -

    2.51.3 Additional Features and Functionality

    +

    2.52.3 Additional Features and Functionality

    • The X509V2CRLGenerator now supports adding general extensions to CRL entries.
    • A RoleSyntax implementation has been added to the x509 ASN.1 package, and the AttributeCertificateHolder class now support the IssuerSerial option. @@ -1951,10 +1974,10 @@

      2.51.3 Additional Features and Functionality

    • DERUTF8String now supports surrogate pairs.
    -

    2.52.1 Version

    +

    2.53.1 Version

    Release: 1.32
    Date:      2006, March 27 -

    2.52.2 Defects Fixed

    +

    2.53.2 Defects Fixed

    • Further work has been done on RFC 3280 compliance.
    • The ASN1Sequence constructor for SemanticsInformation would sometimes throw a ClassCastException on reconstruction an object from a byte stream. This has been fixed. @@ -1971,7 +1994,7 @@

      2.52.2 Defects Fixed

    • OpenPGP clear text signatures containing '\r' as line separators were not being correctly canonicalized. This has been fixed.
    -

    2.52.3 Additional Features and Functionality

    +

    2.53.3 Additional Features and Functionality

    • The ASN.1 library now includes classes for the ICAO Electronic Passport.
    • Support has been added to CMS and S/MIME for ECDSA. @@ -1980,16 +2003,16 @@

      2.52.3 Additional Features and Functionality

    • Support has been added for repeated attributes in CMS and S/MIME messages.
    • A wider range of RSA-PSS signature types is now supported for CRL and Certificate verification.
    -

    2.52.4 Possible compatibility issue

    +

    2.53.4 Possible compatibility issue

    • Previously elliptic curve keys and points were generated with point compression enabled by default. Owing to patent issues in some jurisdictions, they are now generated with point compression disabled by default.
    -

    2.53.1 Version

    +

    2.54.1 Version

    Release: 1.31
    Date:      2005, December 29 -

    2.53.2 Defects Fixed

    +

    2.54.2 Defects Fixed

    • getCriticalExtensionOIDs on an X.509 attribute certificate was returning the non-critical set. This has been fixed.
    • Encoding uncompressed ECDSA keys could occasionally introduce an extra leading zero byte. This has been fixed. @@ -2002,7 +2025,7 @@

      2.53.2 Defects Fixed

      This has been fixed.
    • OIDs with extremely large components would sometimes reencode with unnecessary bytes in their encoding. The optimal DER encoding will now be produced instead.
    -

    2.53.3 Additional Features and Functionality

    +

    2.54.3 Additional Features and Functionality

    • The SMIME package now supports the large file streaming model as well.
    • Additional ASN.1 message support has been added for RFC 3739 in the org.bouncycastle.x509.qualified package. @@ -2011,10 +2034,10 @@

      2.53.3 Additional Features and Functionality

    • CertPathValidator has been updated to better support path validation as defined in RFC 3280.
    -

    2.54.1 Version

    +

    2.55.1 Version

    Release: 1.30
    Date:      2005, September 18 -

    2.54.2 Defects Fixed

    +

    2.55.2 Defects Fixed

    • Whirlpool was calculating the wrong digest for 31 byte data and could throw an exception for some other data lengths. This has been fixed.
    • AlgorithmParameters for IVs were returning a default of RAW encoding of the parameters when they should have been returning an @@ -2026,7 +2049,7 @@

      2.54.2 Defects Fixed

    • KEKIdentifier would not handle OtherKeyAttribute objects correctly. This has been fixed.
    • GetCertificateChain on a PKCS12 keystore would return a single certificate chain rather than null if the alias passed in represented a certificate not a key. This has been fixed.
    -

    2.54.3 Additional Features and Functionality

    +

    2.55.3 Additional Features and Functionality

    • RSAEngine no longer assumes keys are byte aligned when checking for out of range input.
    • PGPSecretKeyRing.removeSecretKey and PGPSecretKeyRing.insertSecretKey have been added. @@ -2037,10 +2060,10 @@

      2.54.3 Additional Features and Functionality

    • Both the lightweight API and the provider now support the Camellia encryption algorithm.
    -

    2.55.1 Version

    +

    2.56.1 Version

    Release: 1.29
    Date:      2005, June 27 -

    2.55.2 Defects Fixed

    +

    2.56.2 Defects Fixed

    • HMac-SHA384 and HMac-SHA512 were not IETF compliant. This has been fixed.
    • The equals() method on ElGamalKeyParameters and DHKeyParameters in the lightweight API would sometimes @@ -2051,7 +2074,7 @@

      2.55.2 Defects Fixed

    • ISO9796 signatures for full recovered messsages could incorrectly verify for similar messages in some circumstances. This has been fixed.
    • The occasional problem with decrypting PGP messages containing compressed streams now appears to be fixed.
    -

    2.55.3 Additional Features and Functionality

    +

    2.56.3 Additional Features and Functionality

    • Support has been added for the OIDs and key generation required for HMac-SHA224, HMac-SHA256, HMac-SHA384, and HMac-SHA512. @@ -2059,16 +2082,16 @@

      2.55.3 Additional Features and Functionality

    • The provider and the lightweight API now support the GOST-28147-94 MAC algorithm.
    • Headers are now settable for PGP armored output streams.
    -

    2.55.4 Notes

    +

    2.56.4 Notes

    • The old versions of HMac-SHA384 and HMac-SHA512 can be invoked as OldHMacSHA384 and OldHMacSHA512, or by using the OldHMac class in the lightweight API.
    -

    2.56.1 Version

    +

    2.57.1 Version

    Release: 1.28
    Date:      2005, April 20 -

    2.56.2 Defects Fixed

    +

    2.57.2 Defects Fixed

    • Signatures on binary encoded S/MIME messages could fail to validate when correct. This has been fixed.
    • getExtensionValue() on CRL Entries were returning the encoding of the inner object, rather than the octet string. This has been fixed. @@ -2082,7 +2105,7 @@

      2.56.2 Defects Fixed

    • Filetype for S/MIME compressed messages was incorrect. This has been fixed.
    • BigInteger class can now create negative numbers from byte arrays.
    -

    2.56.3 Additional Features and Functionality

    +

    2.57.3 Additional Features and Functionality

    • S/MIME now does canonicalization on non-binary input for signatures.
    • Micalgs for the new SHA schemes are now supported. @@ -2093,7 +2116,7 @@

      2.56.3 Additional Features and Functionality

    • Support has been added for the creation of ECDSA certificate requests.
    • The provider and the light weight API now support the WHIRLPOOL message digest.
    -

    2.56.4 Notes

    +

    2.57.4 Notes

    • Patches for S/MIME binary signatures and canonicalization were actually applied in 1.27, but a couple of days after the release - if the class CMSProcessableBodyPartOutbound is present in the package org.bouncycastle.mail.smime you have the patched 1.27. We would recommend upgrading to 1.28 in any case @@ -2101,10 +2124,10 @@

      2.56.4 Notes

    • GOST private keys are probably not encoding correctly and can be expected to change.
    -

    2.57.1 Version

    +

    2.58.1 Version

    Release: 1.27
    Date:      2005, February 20 -

    2.57.2 Defects Fixed

    +

    2.58.2 Defects Fixed

    • Typos in the provider which pointed Signature algorithms SHA256WithRSA, SHA256WithRSAEncryption, SHA384WithRSA, SHA384WithRSAEncryption, SHA512WithRSA, and SHA512WithRSAEncryption at the PSS versions of the algorithms have been fixed. The correct names for the PSS algorithms are SHA256withRSAandMGF1, SHA384withRSAandMGF1, and SHA512withRSAandMGF1.
    • X509CertificateFactory failed under some circumstances to reset properly if the input stream being passed @@ -2118,7 +2141,7 @@

      2.57.2 Defects Fixed

    • TSP TimeStampToken was failing to validate time stamp tokens with the issuerSerial field set in the ESSCertID structure. This has been fixed.
    • Path validation in environments with frequently updated CRLs could occasionally reject a valid path. This has been fixed.
    -

    2.57.3 Additional Features and Functionality

    +

    2.58.3 Additional Features and Functionality

    • Full support has been added for the OAEPParameterSpec class to the JDK 1.5 povider.
    • Full support has been added for the PSSParameterSpec class to the JDK 1.4 and JDK 1.5 providers. @@ -2129,7 +2152,7 @@

      2.57.3 Additional Features and Functionality

    • The CertPath support classes now support PKCS #7 encoding.
    • Point compression can now be turned off when encoding elliptic curve keys.
    -

    2.57.4 Changes that may affect compatibility

    +

    2.58.4 Changes that may affect compatibility

    • org.bouncycastle.jce.interfaces.ElGamalKey.getParams() has been changed to getParameters() to avoid clashes with a JCE interface with the same method signature. @@ -2139,10 +2162,10 @@

      2.57.4 Changes that may affect compatibility

      were using these previously you should use SHA256WithRSAAndMGF1, SHA384WithRSAAndMGF1, or SHA512WithRSAAndMGF1.
    -

    2.58.1 Version

    +

    2.59.1 Version

    Release: 1.26
    Date:      2005, January 15 -

    2.58.2 Defects Fixed

    +

    2.59.2 Defects Fixed

    • The X.509 class UserNotice assumed some of the optional fields were not optional. This has been fixed.
    • BCPGInputStream would break on input packets of 8274 bytes in length. This has been fixed. @@ -2151,7 +2174,7 @@

      2.58.2 Defects Fixed

    • ASN1Sets now properly sort their contents when created from scratch.
    • A bug introduced in the CertPath validation in the last release which meant some certificate paths would validate if they were invalid has been fixed.
    -

    2.58.3 Additional Features and Functionality

    +

    2.59.3 Additional Features and Functionality

    • Support for JDK 1.5 naming conventions for OAEP encryption and PSS signing has been added.
    • Support for Time Stamp Protocol (RFC 3161) has been added. @@ -2161,15 +2184,15 @@

      2.58.3 Additional Features and Functionality

    • PBEWithMD5AndRC2, PBEWithSHA1AndRC2 now generate keys rather than exceptions.
    • The BigInteger implementation has been further optimised to take more advantage of the Montgomery number capabilities.
    -

    2.58.4 JDK 1.5 Changes

    +

    2.59.4 JDK 1.5 Changes

    • The JDK 1.5 version of the provider now supports the new Elliptic Curve classes found in the java.security packages. Note: while we have tried to preserve some backwards compatibility people using Elliptic curve are likely to find some minor code changes are required when moving code from JDK 1.4 to JDK 1.5 as the java.security APIs have changed.
    -

    2.59.1 Version

    +

    2.60.1 Version

    Release: 1.25
    Date:      2004, October 1 -

    2.59.2 Defects Fixed

    +

    2.60.2 Defects Fixed

    • In some situations OpenPGP would overread when a stream had been broken up into partial blocks. This has been fixed. @@ -2191,7 +2214,7 @@

      2.59.2 Defects Fixed

    • Parsing a message with a zero length body with SMIMESigned would cause an exception. This has been fixed.
    • Some versions of PGP use zeros in the data stream rather than a replication of the last two bytes of the iv as specified in the RFC to determine if the correct decryption key has been found. The decryption classes will now cope with both.
    -

    2.59.3 Additional Features and Functionality

    +

    2.60.3 Additional Features and Functionality

    • Support for extracting signatures based on PGP user attributes has been added to PGPPublicKey. @@ -2211,10 +2234,10 @@

      2.59.3 Additional Features and Functionality

    • OID components of up to 2^63 bits are now supported.
    -

    2.60.1 Version

    +

    2.61.1 Version

    Release: 1.24
    Date:      2004, June 12 -

    2.60.2 Defects Fixed

    +

    2.61.2 Defects Fixed

    • OpenPGP Secret key rings now parse key rings with user attribute packets in them correctly.
    • OpenPGP Secret key rings now parse key rings with GPG comment packets in them. @@ -2231,17 +2254,17 @@

      2.60.2 Defects Fixed

    • An encoding error introduced in 1.23 which affected generation of the KeyUsage extension has been fixed.
    -

    2.60.3 Additional Features and Functionality

    +

    2.61.3 Additional Features and Functionality

    • PKCS12 keystore now handles single key/certificate files without any attributes present.
    • Support for creation of PGPKeyRings incorporating sub keys has been added.
    • ZeroPadding for encrypting ASCII data has been added.
    -

    2.61.1 Version

    +

    2.62.1 Version

    Release: 1.23
    Date:      2004, April 10 -

    2.61.2 Defects Fixed

    +

    2.62.2 Defects Fixed

    • Reading a PGP Secret key file would sometimes cause a class cast exception. This has been fixed.
    • PGP will now read SecretKeys which are encrypted with the null algorithm. @@ -2256,7 +2279,7 @@

      2.61.2 Defects Fixed

    • X509Name class will now print names with nested pairs in component sets correctly.
    • RC4 now resets correctly on doFinal.
    -

    2.61.3 Additional Features and Functionality

    +

    2.62.3 Additional Features and Functionality

    • PGP V3 keys and V3 signature generation is now supported.
    • Collection classes have been added for representing files of PGP public and secret keys. @@ -2275,10 +2298,10 @@

      2.61.3 Additional Features and Functionality

    • DERGeneralizedTime getTime() method now handles a broader range of input strings.
    -

    2.62.1 Version

    +

    2.63.1 Version

    Release: 1.22
    Date:      2004, February 7 -

    2.62.2 Defects Fixed

    +

    2.63.2 Defects Fixed

    • Generating DSA signatures with PGP would cause a class cast exception, this has been fixed.
    • PGP Data in the 192 to 8383 byte length would sometimes be written with the wrong length header. This has been fixed. @@ -2288,7 +2311,7 @@

      2.62.2 Defects Fixed

    • PSS signature verification would fail approximately 0.5 % of the time on correct signatures. This has been fixed.
    • Encoding of CRL Distribution Points now always works.
    -

    2.62.3 Additional Features and Functionality

    +

    2.63.3 Additional Features and Functionality

    • Additional methods for getting public key information have been added to the PGP package.
    • Some support for user attributes and the image attribute tag has been added. @@ -2296,10 +2319,10 @@

      2.62.3 Additional Features and Functionality

    • Support for ElGamal encryption/decryption has been added to the PGP package.
    -

    2.63.1 Version

    +

    2.64.1 Version

    Release: 1.21
    Date:      2003, December 6 -

    2.63.2 Defects Fixed

    +

    2.64.2 Defects Fixed

    • The CertPath validator would fail for some valid CRLs. This has been fixed.
    • AES OIDS for S/MIME were still incorrect, this has been fixed. @@ -2307,17 +2330,17 @@

      2.63.2 Defects Fixed

    • The J2ME BigInteger class would sometimes go into an infinite loop generating prime numbers. This has been fixed.
    • DERBMPString.equals() would throw a class cast exception. This has been fixed.
    -

    2.63.3 Additional Features and Functionality

    +

    2.64.3 Additional Features and Functionality

    • PEMReader now handles public keys.
    • OpenPGP/BCPG should now handle partial input streams. Additional methods for reading subpackets off signatures.
    • The ASN.1 library now supports policy qualifiers and policy info objects.
    -

    2.64.1 Version

    +

    2.65.1 Version

    Release: 1.20
    Date:      2003, October 8 -

    2.64.2 Defects Fixed

    +

    2.65.2 Defects Fixed

    • BigInteger toString() in J2ME/JDK1.0 now produces same output as the Sun one.
    • RSA would throw a NullPointer exception with doFinal without arguments. This has been fixed. @@ -2327,7 +2350,7 @@

      2.64.2 Defects Fixed

    • AES OIDS were incorrect, this has been fixed.
    • In some cases BC generated private keys would not work with the JSSE. This has been fixed.
    -

    2.64.3 Additional Features and Functionality

    +

    2.65.3 Additional Features and Functionality

    • Support for reading/writing OpenPGP public/private keys and OpenPGP signatures has been added.
    • Support for generating OpenPGP PBE messages and public key encrypted messages has been added. @@ -2335,10 +2358,10 @@

      2.64.3 Additional Features and Functionality

    • Addition of a Null block cipher to the light weight API.
    -

    2.65.1 Version

    +

    2.66.1 Version

    Release: 1.19
    Date:      2003, June 7 -

    2.65.2 Defects Fixed

    +

    2.66.2 Defects Fixed

    • The PKCS12 store would throw an exception reading PFX files that had attributes with no values. This has been fixed.
    • RSA Private Keys would not serialise if they had PKCS12 bag attributes attached to them, this has been fixed. @@ -2346,7 +2369,7 @@

      2.65.2 Defects Fixed

    • ASN1 parser would sometimes mistake an implicit null for an implicit empty sequence. This has been fixed.
    -

    2.65.3 Additional Features and Functionality

    +

    2.66.3 Additional Features and Functionality

    • S/MIME and CMS now support the draft standard for AES encryption.
    • S/MIME and CMS now support setable key sizes for the standard algorithms. @@ -2358,10 +2381,10 @@

      2.65.3 Additional Features and Functionality

      in order to find algorithms.
    -

    2.66.1 Version

    +

    2.67.1 Version

    Release: 1.18
    Date:      2003, February 8 -

    2.66.2 Defects Fixed

    +

    2.67.2 Defects Fixed

    • DESKeySpec.isParityAdjusted in the clean room JCE could go into an infinite loop. This has been fixed. @@ -2372,7 +2395,7 @@

      2.66.2 Defects Fixed

    • Seeding with longs in the SecureRandom for the J2ME and JDK 1.0, only used 4 bytes of the seed value. This has been fixed.
    -

    2.66.3 Additional Features and Functionality

    +

    2.67.3 Additional Features and Functionality

    • The X.509 OID for RSA is now recognised by the provider as is the OID for RSA/OAEP.
    • Default iv's for DES are now handled correctly in CMS. @@ -2384,10 +2407,10 @@

      2.66.3 Additional Features and Functionality

      Sun BigInteger library.
    -

    2.67.1 Version

    +

    2.68.1 Version

    Release: 1.17
    Date:      2003, January 8 -

    2.67.2 Defects Fixed

    +

    2.68.2 Defects Fixed

    • Reuse of an CMSSignedObject could occasionally result in a class cast exception. This has been fixed. @@ -2398,7 +2421,7 @@

      2.67.2 Defects Fixed

    • The DERObject constructor in OriginatorIdentifierOrKey was leaving the id field as null. This has been fixed.
    -

    2.67.3 Additional Functionality and Features

    +

    2.68.3 Additional Functionality and Features

    • RC2 now supports the full range of parameter versions and effective key sizes. @@ -2418,10 +2441,10 @@

      2.67.3 Additional Functionality and Features

      string to OID conversion.
    -

    2.68.1 Version

    +

    2.69.1 Version

    Release: 1.16
    Date:      2002, November 30 -

    2.68.2 Defects Fixed

    +

    2.69.2 Defects Fixed

    • CRLS were only working for UTC time constructed Time objects, this has been fixed. @@ -2435,7 +2458,7 @@

      2.68.2 Defects Fixed

      to throw a NullPointerException at the wrong time.
    • Macs now clone correctly in the clean room JCE.
    -

    2.68.3 Additional Functionality and Features

    +

    2.69.3 Additional Functionality and Features

    • PGPCFB support has been added to the provider and the lightweight API.
    • There are now three versions of the AESEngine, all faster than before, @@ -2453,10 +2476,10 @@

      2.68.3 Additional Functionality and Features

      and to support multiple recipients/signers.
    -

    2.69.1 Version

    +

    2.70.1 Version

    Release: 1.15
    Date:      2002, September 6 -

    2.69.2 Defects Fixed

    +

    2.70.2 Defects Fixed

    • The base string for the oids in asn1.x509.KeyPurposeId was incorrect. This has been fixed. @@ -2479,7 +2502,7 @@

      2.69.2 Defects Fixed

      The local name now takes precedence.
    • ReasonFlags now correctly encodes.
    -

    2.69.3 Additional Functionality and Features

    +

    2.70.3 Additional Functionality and Features

    • The PKCS12 key store now handles key bags in encryptedData bags.
    • The X509NameTokenizer now handles for '\' and '"' characters. @@ -2488,10 +2511,10 @@

      2.69.3 Additional Functionality and Features

    • Both the provider and the lightweight library now support a basic SIC mode for block ciphers.
    -

    2.70.1 Version

    +

    2.71.1 Version

    Release: 1.14
    Date:      2002, June 17 -

    2.70.2 Defects Fixed

    +

    2.71.2 Defects Fixed

    • there was a bug in the BigInteger right shifting for > 31 bit shifts. This has been fixed. @@ -2512,7 +2535,7 @@

      2.70.2 Defects Fixed

    • asn1.x509.ExtendedKeyUsage used to throw a null pointer exception on construction. This has been fixed.
    -

    2.70.3 Additional Functionality and Features

    +

    2.71.3 Additional Functionality and Features

    • The BigInteger library now uses Montgomery numbers for modPow and is substantially faster. @@ -2526,10 +2549,10 @@

      2.70.3 Additional Functionality and Features

      object identifiers.
    -

    2.71.1 Version

    +

    2.72.1 Version

    Release: 1.13
    Date:      2002, April 19 -

    2.71.2 Defects Fixed

    +

    2.72.2 Defects Fixed

    • The TBSCertificate object in the ASN.1 library now properly implements the Time object, rather returning UTC time. @@ -2538,7 +2561,7 @@

      2.71.2 Defects Fixed

    • toByteArray in the big integer class was not always producing correct results for negative numbers. This has been Fixed.
    -

    2.71.3 Additional Functionality and Features

    +

    2.72.3 Additional Functionality and Features

    • The key to keySpec handling of the secret key factories has been improved.
    • There is now a SMIME implementation and a more complete CMS @@ -2553,10 +2576,10 @@

      2.71.3 Additional Functionality and Features

      length certificate chains for signing keys.
    -

    2.72.1 Version

    +

    2.73.1 Version

    Release: 1.12
    Date:      2002, February 8 -

    2.72.2 Defects Fixed

    +

    2.73.2 Defects Fixed

    • The ASN.1 library was unable to read an empty set object. This has been fixed.
    • Returning sets of critical and non-critical extensions on X.509 certificates could result in a null pointer exception if the certificate had no extensions. This has been fixed. @@ -2575,7 +2598,7 @@

      2.72.2 Defects Fixed

    • the IV algorithm parameters class would improperly throw an exception on initialisation. This has been fixed.
    -

    2.72.3 Additional Functionality and Features

    +

    2.73.3 Additional Functionality and Features

    • The AESWrap ciphers will now take IV's.
    • The DES-EDEWrap algorithm described in https://www.ietf.org/internet-drafts/draft-ietf-smime-key-wrap-01.txt is now supported. @@ -2589,10 +2612,10 @@

      2.72.3 Additional Functionality and Features

      for details).
    -

    2.73.1 Version

    +

    2.74.1 Version

    Release: 1.11
    Date:      2001, December 10 -

    2.73.2 Defects Fixed

    +

    2.74.2 Defects Fixed

    • X9.23 padding of MACs now works correctly with block size aligned data.
    • Loading a corrupted "UBER" key store would occasionally cause the @@ -2618,7 +2641,7 @@

      2.73.2 Defects Fixed

      extensions. This has been fixed.
    • The NetscapeCert type bits were reversed! This has been fixed.
    -

    2.73.3 Additional Functionality and Features

    +

    2.74.3 Additional Functionality and Features

    • The lightweight API and the JCE provider now support ElGamal.
    • X509Principal, and X509Name now supports the "DC" attribute and the @@ -2632,7 +2655,7 @@

      2.73.3 Additional Functionality and Features

    • Elliptic curve routines now handle uncompressed points as well as the compressed ones.
    -

    2.73.4 Other changes

    +

    2.74.4 Other changes

    • As the range of public key types supported has expanded the getPublicKey method on the SubjectPublicKeyInfo class is not always going to work. The @@ -2640,10 +2663,10 @@

      2.73.4 Other changes

      throws an IOException if there is a problem.
    -

    2.74.1 Version

    +

    2.75.1 Version

    Release: 1.10
    Date:      2001, October 20 -

    2.74.2 Defects Fixed

    +

    2.75.2 Defects Fixed

    • The PKCS12 Key Store now interoperates with the JDK key tool. Note: this does mean the the key name passed to the setKeyEntry calls has become significant. @@ -2651,7 +2674,7 @@

      2.74.2 Defects Fixed

      has been fixed.
    • The ASN.1 input streams now handle zero-tagged zero length objects correctly.
    -

    2.74.3 Additional Functionality and Features

    +

    2.75.3 Additional Functionality and Features

    • The JCE Provider and the lightweight API now support Serpent, CAST5, and CAST6.
    • The JCE provider and the lightweight API now has an implementation of ECIES. @@ -2661,10 +2684,10 @@

      2.74.3 Additional Functionality and Features

    • Support for the generation of PKCS10 certification requests has been added.
    -

    2.75.1 Version

    +

    2.76.1 Version

    Release: 1.09
    Date:      2001, October 6 -

    2.75.2 Defects Fixed

    +

    2.76.2 Defects Fixed

    • failure to pass in an RC5 parameters object now results in an exception at the upper level of the JCE, rather than falling over in the lightweight @@ -2677,7 +2700,7 @@

      2.75.2 Defects Fixed

    • In some cases the ASN.1 library wouldn't handle implicit tagging properly. This has been fixed.
    -

    2.75.3 Additional Functionality and Features

    +

    2.76.3 Additional Functionality and Features

    • Support for RC5-64 has been added to the JCE.
    • ISO9796-2 signatures have been added to the JCE and lightweight API. @@ -2701,10 +2724,10 @@

      2.75.3 Additional Functionality and Features

      resource hungry and faster - whether it's fast enough remains to be seen!
    -

    2.76.1 Version

    +

    2.77.1 Version

    Release: 1.08
    Date:      2001, September 9 -

    2.76.2 Defects Fixed

    +

    2.77.2 Defects Fixed

    • It wasn't possible to specify an ordering for distinguished names in X509 certificates. This is now supported. @@ -2715,7 +2738,7 @@

      2.76.2 Defects Fixed

    • The netscape certificate request class wouldn't compile under JDK 1.1. This has been fixed.
    -

    2.76.3 Additional Functionality and Features

    +

    2.77.3 Additional Functionality and Features

    • ISO 9796-1 padding is now supported with RSA in the lightweight API and the JCE. @@ -2729,10 +2752,10 @@

      2.76.3 Additional Functionality and Features

      this is fixed.
    -

    2.77.1 Version

    +

    2.78.1 Version

    Release: 1.07
    Date:      2001, July 9 -

    2.77.2 Defects Fixed

    +

    2.78.2 Defects Fixed

    • It turned out that the setOddParity method in the DESParameter class was indeed doing something odd but not what was intended. This is now @@ -2743,10 +2766,10 @@

      2.77.2 Defects Fixed

      have a look in org.bouncycastle.jce.provider.JDKKeyStore lines 201-291.
    -

    2.78.1 Version

    +

    2.79.1 Version

    Release: 1.06
    Date:      2001, July 2 -

    2.78.2 Defects Fixed

    +

    2.79.2 Defects Fixed

    • Diffie-Hellman keys are now properly serialisable as well as encodable. @@ -2768,17 +2791,17 @@

      2.78.2 Defects Fixed

    • Resetting and resusing HMacs in the lightweight and heavyweight libraries caused a NullPointer exception. This has been fixed.
    -

    2.78.3 Additional Functionality

    +

    2.79.3 Additional Functionality

    • ISO10126Padding is now recognised explicitly for block ciphers as well.
    • The Blowfish implementation is now somewhat faster.
    -

    2.79.1 Version

    +

    2.80.1 Version

    Release: 1.05
    Date:      2001, April 17 -

    2.79.2 Defects Fixed

    +

    2.80.2 Defects Fixed

    • The DESEDE key generator can now be used to generate 2-Key-DESEDE keys as well as 3-Key-DESEDE keys. @@ -2789,22 +2812,22 @@

      2.79.2 Defects Fixed

    • The ASN.1 library was skipping explicitly tagged objects of zero length. This has been fixed.
    -

    2.79.3 Additional Functionality

    +

    2.80.3 Additional Functionality

    • There is now an org.bouncycastle.jce.netscape package which has a class in for dealing with Netscape Certificate Request objects.
    -

    2.79.4 Additional Notes

    +

    2.80.4 Additional Notes

    Concerning the PKCS12 fix: in a few cases this may cause some backward compatibility issues - if this happens to you, drop us a line at feedback-crypto@bouncycastle.org and we will help you get it sorted out.

    -

    2.80.1 Version

    +

    2.81.1 Version

    Release: 1.04
    Date:      2001, March 11 -

    2.80.2 Defects Fixed

    +

    2.81.2 Defects Fixed

    • Signatures generated by other providers that include optional null parameters in the AlgorithmIdentifier are now handled correctly by the @@ -2833,7 +2856,7 @@

      2.80.2 Defects Fixed

      hash table when the hash table constructor was called. This has been fixed.
    -

    2.80.3 Additional Functionality

    +

    2.81.3 Additional Functionality

    • Added Elliptic Curve DSA (X9.62) - ECDSA - to provider and lightweight library. @@ -2845,10 +2868,10 @@

      2.80.3 Additional Functionality

    • The certificate generators now support ECDSA and DSA certs as well.
    -

    2.81.1 Version

    +

    2.82.1 Version

    Release: 1.03
    Date:      2001, January 7 -

    2.81.2 Defects Fixed

    +

    2.82.2 Defects Fixed

    • CFB and OFB modes when specified without padding would insist on input being block aligned. When specified without padding CFB and OFB now behave in a compatible @@ -2858,29 +2881,29 @@

      2.81.2 Defects Fixed

      length as the plain text.
    -

    2.82.1 Version

    +

    2.83.1 Version

    Release: 1.02
    Date:      2000, November 7 -

    2.82.2 Defects Fixed

    +

    2.83.2 Defects Fixed

    • The RSA key pair generator occasionally produced keys 1 bit under the requested size. This is now fixed.
    -

    2.83.1 Version

    +

    2.84.1 Version

    Release: 1.01
    Date:      2000, October 15 -

    2.83.2 Defects Fixed

    +

    2.84.2 Defects Fixed

    • Buffered ciphers in lightweight library were not resetting correctly on a doFinal. This has been fixed.
    -

    2.84.1 Version

    +

    2.85.1 Version

    Release: 1.00
    Date:      2000, October 13 -

    2.84.2 Defects Fixed

    +

    2.85.2 Defects Fixed

    • JDK1.2 version now works with keytool for certificate generation. @@ -2895,7 +2918,7 @@

      2.84.2 Defects Fixed

    • Some DES PBE algorithms did not set the parity correctly in generated keys, this has been fixed.
    -

    2.84.3 Additional functionality

    +

    2.85.3 Additional functionality

    • Argument validation is much improved. From 67ea238d233a1a31196784d26de6e615483ee2ea Mon Sep 17 00:00:00 2001 From: David Hook Date: Sat, 31 May 2025 15:42:25 +1000 Subject: [PATCH 399/890] Java 5 to Java 8 updates. --- .../pqc/crypto/test/MLDSATest.java | 29 ++++++++++--------- .../bouncycastle/bcpg/ArmoredInputStream.java | 2 +- .../test/DoubleBufferedInputStreamTest.java | 18 ++++++------ .../openpgp/test/ArmoredInputStreamTest.java | 2 +- 4 files changed, 27 insertions(+), 24 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java index ac3cafd6be..32c363a24f 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java @@ -641,7 +641,7 @@ public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byt private static List parseTestVectors(InputStream src) throws IOException { - List vectors = new ArrayList<>(); + List vectors = new ArrayList(); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); TestVector currentVector = null; @@ -678,7 +678,7 @@ private static List parseTestVectors(InputStream src) if (fieldMatcher.find()) { currentField = fieldMatcher.group(1); - currentBytes = new ArrayList<>(); + currentBytes = new ArrayList(); line = line.substring(fieldMatcher.end()).trim(); } @@ -721,24 +721,27 @@ private static void setField(TestVector vector, String field, List bytes) byteArray[i] = bytes.get(i); } - switch (field) + if ("seed".equals(field)) { - case "seed": vector.seed = byteArray; - break; - case "pk": + } + else if ("pk".equals(field)) + { vector.pk = byteArray; - break; - case "sk": + } + else if ("sk".equals(field)) + { vector.sk = byteArray; - break; - case "msg": + } + else if ("msg".equals(field)) + { vector.msg = byteArray; - break; - case "sig": + } + else if ("sig".equals(field)) + { vector.sig = byteArray; - break; } + // else ignore } static class TestVector diff --git a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java index 0fedfd41bc..85ef7d5f2c 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java @@ -663,7 +663,7 @@ public void setDetectMissingCRC(boolean detectMissing) private static List defaultAllowedHeaders() { - List allowedHeaders = new ArrayList<>(); + List allowedHeaders = new ArrayList(); allowedHeaders.add(ArmoredOutputStream.COMMENT_HDR); allowedHeaders.add(ArmoredOutputStream.VERSION_HDR); allowedHeaders.add(ArmoredOutputStream.CHARSET_HDR); diff --git a/pg/src/test/java/org/bouncycastle/openpgp/api/test/DoubleBufferedInputStreamTest.java b/pg/src/test/java/org/bouncycastle/openpgp/api/test/DoubleBufferedInputStreamTest.java index c3c61b3b06..b34b7f3406 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/api/test/DoubleBufferedInputStreamTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/api/test/DoubleBufferedInputStreamTest.java @@ -1,15 +1,15 @@ package org.bouncycastle.openpgp.api.test; -import org.bouncycastle.bcpg.test.AbstractPacketTest; -import org.bouncycastle.openpgp.api.DoubleBufferedInputStream; -import org.bouncycastle.util.io.Streams; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; +import org.bouncycastle.bcpg.test.AbstractPacketTest; +import org.bouncycastle.openpgp.api.DoubleBufferedInputStream; +import org.bouncycastle.util.io.Streams; + public class DoubleBufferedInputStreamTest extends AbstractPacketTest { @@ -37,7 +37,7 @@ private void successfullyReadSmallerThanBuffer() { byte[] bytes = getSequentialBytes(400); ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); - DoubleBufferedInputStream retIn = new DoubleBufferedInputStream<>(bIn, 512); + DoubleBufferedInputStream retIn = new DoubleBufferedInputStream(bIn, 512); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); Streams.pipeAll(retIn, bOut); isEncodingEqual(bytes, bOut.toByteArray()); @@ -48,7 +48,7 @@ private void successfullyReadGreaterThanBuffer() { byte[] bytes = getSequentialBytes(2000); ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); - DoubleBufferedInputStream retIn = new DoubleBufferedInputStream<>(bIn, 512); + DoubleBufferedInputStream retIn = new DoubleBufferedInputStream(bIn, 512); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); Streams.pipeAll(retIn, bOut); isEncodingEqual(bytes, bOut.toByteArray()); @@ -74,7 +74,7 @@ public int read() return i; } }; - DoubleBufferedInputStream retIn = new DoubleBufferedInputStream<>(throwAfterNBytes, 512); + DoubleBufferedInputStream retIn = new DoubleBufferedInputStream(throwAfterNBytes, 512); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { @@ -107,7 +107,7 @@ public int read() return i; } }; - DoubleBufferedInputStream retIn = new DoubleBufferedInputStream<>(throwAfterNBytes, 4); + DoubleBufferedInputStream retIn = new DoubleBufferedInputStream(throwAfterNBytes, 4); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { @@ -134,7 +134,7 @@ public void close() throw new IOException("Oopsie"); } }; - DoubleBufferedInputStream retIn = new DoubleBufferedInputStream<>(throwOnClose, 512); + DoubleBufferedInputStream retIn = new DoubleBufferedInputStream(throwOnClose, 512); ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/ArmoredInputStreamTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/ArmoredInputStreamTest.java index 1dae858f1f..8834372748 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/ArmoredInputStreamTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/ArmoredInputStreamTest.java @@ -159,7 +159,7 @@ private void unknownClearsignedMessageHeadersTest() // Test validation enabled bIn = new ByteArrayInputStream(armor.getBytes(StandardCharsets.UTF_8)); - ByteArrayInputStream finalBIn = bIn; + final ByteArrayInputStream finalBIn = bIn; isTrue(null != testException( "Illegal ASCII armor header line in clearsigned message encountered: Hello: this is totally part of the signed text", "ArmoredInputException", From 019ecf59c209dfb940004c5b59171986b9038fc3 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sat, 31 May 2025 16:29:11 +1000 Subject: [PATCH 400/890] Java 5 updates --- .../openpgp/test/ArmoredInputStreamTest.java | 7 +- .../jce/provider/test/RegressionTest.java | 115 ++++++++++++++++++ 2 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 prov/src/test/jdk1.5/org/bouncycastle/jce/provider/test/RegressionTest.java diff --git a/pg/src/test/java/org/bouncycastle/openpgp/test/ArmoredInputStreamTest.java b/pg/src/test/java/org/bouncycastle/openpgp/test/ArmoredInputStreamTest.java index 8834372748..60836c11e7 100644 --- a/pg/src/test/java/org/bouncycastle/openpgp/test/ArmoredInputStreamTest.java +++ b/pg/src/test/java/org/bouncycastle/openpgp/test/ArmoredInputStreamTest.java @@ -2,7 +2,6 @@ import java.io.ByteArrayInputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; import java.security.Security; import org.bouncycastle.bcpg.ArmoredInputStream; @@ -142,7 +141,7 @@ private void unknownClearsignedMessageHeadersTest() "-----END PGP SIGNATURE-----"; // Test validation is not enabled by default - ByteArrayInputStream bIn = new ByteArrayInputStream(armor.getBytes(StandardCharsets.UTF_8)); + ByteArrayInputStream bIn = new ByteArrayInputStream(Strings.toUTF8ByteArray(armor)); ArmoredInputStream aIn = ArmoredInputStream.builder() .build(bIn); // Skip over cleartext @@ -158,7 +157,7 @@ private void unknownClearsignedMessageHeadersTest() // Test validation enabled - bIn = new ByteArrayInputStream(armor.getBytes(StandardCharsets.UTF_8)); + bIn = new ByteArrayInputStream(Strings.toUTF8ByteArray(armor)); final ByteArrayInputStream finalBIn = bIn; isTrue(null != testException( "Illegal ASCII armor header line in clearsigned message encountered: Hello: this is totally part of the signed text", @@ -177,7 +176,7 @@ public void operation() // Test validation enabled, but custom header allowed - bIn = new ByteArrayInputStream(armor.getBytes(StandardCharsets.UTF_8)); + bIn = new ByteArrayInputStream(Strings.toUTF8ByteArray(armor)); aIn = ArmoredInputStream.builder() .setValidateClearsignedMessageHeaders(true) .addAllowedArmorHeader("Hello") diff --git a/prov/src/test/jdk1.5/org/bouncycastle/jce/provider/test/RegressionTest.java b/prov/src/test/jdk1.5/org/bouncycastle/jce/provider/test/RegressionTest.java new file mode 100644 index 0000000000..ed0954e26b --- /dev/null +++ b/prov/src/test/jdk1.5/org/bouncycastle/jce/provider/test/RegressionTest.java @@ -0,0 +1,115 @@ +package org.bouncycastle.jce.provider.test; + +import java.security.Security; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.test.SimpleTest; +import org.bouncycastle.util.test.Test; + +public class RegressionTest +{ + public static Test[] tests = { + new AEADTest(), + new AESSICTest(), + new AESTest(), + new AlgorithmParametersTest(), + new ARIATest(), + new BCFKSStoreTest(), + new BlockCipherTest(), + new CamelliaTest(), + new CertLocaleTest(), + new CertPathBuilderTest(), + new CertPathTest(), + new CertPathValidatorTest(), + new CertStoreTest(), + new CertTest(), + new CertUniqueIDTest(), + new ChaCha20Poly1305Test(), + new CipherStreamTest(), + new CipherStreamTest2(), + new CMacTest(), + new CRL5Test(), + new DESedeTest(), + new DetDSATest(), + new DHIESTest(), + new DHTest(), + new DigestTest(), + new DoFinalTest(), + new DRBGTest(), + new DSATest(), + new DSTU4145Test(), + new DSTU7624Test(), + new ECDSA5Test(), + new ECEncodingTest(), + new ECIESTest(), + new ECIESVectorTest(), + new ECNRTest(), + new EdECTest(), + new ElGamalTest(), + new EncryptedPrivateKeyInfoTest(), + new FIPSDESTest(), + new GMacTest(), + new GOST28147Test(), + new GOST3410KeyPairTest(), + new GOST3410Test(), + new GOST3412Test(), + new HMacTest(), + new IESTest(), + new ImplicitlyCaTest(), + new KeccakTest(), + new KeyStoreTest(), + new MacTest(), + new MQVTest(), + new MultiCertStoreTest(), + new NamedCurveTest(), + new NetscapeCertRequestTest(), + new NISTCertPathTest(), + new NoekeonTest(), + new OCBTest(), + new OpenSSHSpecTests(), + new PBETest(), + new PKCS10CertRequestTest(), + new PKCS12StorePBETest(), + new PKCS12StoreTest(), + new PKIXNameConstraintsTest(), + new PKIXPolicyMappingTest(), + new PKIXTest(), + new Poly1305Test(), + new PQCDHTest(), + new PSSTest(), + new RSATest(), + new SealedTest(), + new SEEDTest(), + new SerialisationTest(), + new Shacal2Test(), + new SigNameTest(), + new SignatureTest(), + new SigTest(), + new SipHash128Test(), + new SipHashTest(), + new SkeinTest(), + new SlotTwoTest(), + new SM2CipherTest(), + new SM2SignatureTest(), + new SM4Test(), + new ThreefishTest(), + new TLSKDFTest(), + new WrapTest(), + new X509CertificatePairTest(), + new X509StreamParserTest(), + new XIESTest(), + new XOFTest(), + new ZucTest(), + }; + + public static void main(String[] args) + { + System.setProperty("org.bouncycastle.bks.enable_v1", "true"); + + Security.addProvider(new BouncyCastleProvider()); + + System.out.println("Testing " + Security.getProvider("BC").getInfo() + " version: " + Security.getProvider("BC").getVersion()); + + SimpleTest.runTests(tests); + } +} From ebb4ba36906a546a879be5babfd4308e2751ff47 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sat, 31 May 2025 17:16:10 +1000 Subject: [PATCH 401/890] Java 5 to Java 8 updates. --- .../test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java index 32c363a24f..83e2fc4cd7 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java @@ -655,7 +655,7 @@ private static List parseTestVectors(InputStream src) { // Skip comments and empty lines line = line.split("//")[0].trim(); - if (line.isEmpty()) + if (line.length() == 0) { continue; } From bafc09a5d54218cc5dd3537ac7651bdaa8f18979 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 1 Jun 2025 08:15:51 +1000 Subject: [PATCH 402/890] Java 4 compat changes --- .../pqc/crypto/test/MLDSATest.java | 20 ++++++------------- .../bouncycastle/bcpg/ArmoredInputStream.java | 4 ++-- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java index 83e2fc4cd7..d3e5a14588 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java @@ -502,7 +502,7 @@ private void rejectionTest(MLDSAParameters parameters, String filename, Rejectio List testVectors = parseTestVectors(TestResourceFinder.findTestResource("pqc/crypto/mldsa", filename)); for (int i = 0; i < testVectors.size(); ++i) { - TestVector t = testVectors.get(i); + TestVector t = (TestVector)testVectors.get(i); FixedSecureRandom random = new FixedSecureRandom(t.seed); MLDSAKeyPairGenerator kpGen = new MLDSAKeyPairGenerator(); @@ -541,7 +541,6 @@ private void rejectionExternalMuTest(MLDSAParameters parameters, String filename { rejectionTest(parameters, filename, new RejectionOperation() { - @Override public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) throws CryptoException { @@ -550,7 +549,6 @@ public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) return signer.generateMuSignature(msg); } - @Override public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) { InternalMLDSASigner signer = new InternalMLDSASigner(); @@ -565,7 +563,6 @@ private void rejectionPrehashTest(MLDSAParameters parameters, String filename) { rejectionTest(parameters, filename, new RejectionOperation() { - @Override public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) throws CryptoException { @@ -575,7 +572,6 @@ public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) return signer.generateSignature(); } - @Override public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) { HashMLDSASigner signer = new HashMLDSASigner(); @@ -591,7 +587,6 @@ private void rejectionTest(MLDSAParameters parameters, String filename) { rejectionTest(parameters, filename, new RejectionOperation() { - @Override public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) throws CryptoException { @@ -602,7 +597,6 @@ public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) return signer.generateSignature(); } - @Override public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) { InternalMLDSASigner signer = new InternalMLDSASigner(); @@ -618,7 +612,6 @@ private void rejectionUpStreamTest(MLDSAParameters parameters, String filename) { rejectionTest(parameters, filename, new RejectionOperation() { - @Override public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) throws CryptoException { @@ -626,8 +619,7 @@ public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) signer.init(true, privParams); return signer.internalGenerateSignature(msg, new byte[32]); } - - @Override + public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) { InternalMLDSASigner signer = new InternalMLDSASigner(); @@ -661,7 +653,7 @@ private static List parseTestVectors(InputStream src) } // Look for test vector array start - if (line.contains("dilithium_rejection_testvectors[] = ")) + if (line.indexOf("dilithium_rejection_testvectors[] = ") >= 0) { continue; } @@ -689,11 +681,11 @@ private static List parseTestVectors(InputStream src) while (hexMatcher.find()) { String hex = hexMatcher.group(1); - currentBytes.add((byte)Integer.parseInt(hex, 16)); + currentBytes.add(new Byte((byte)Integer.parseInt(hex, 16))); } // Check for field end - if (line.contains("},")) + if (line.indexOf("},") >= 0) { setField(currentVector, currentField, currentBytes); currentField = null; @@ -718,7 +710,7 @@ private static void setField(TestVector vector, String field, List bytes) byte[] byteArray = new byte[bytes.size()]; for (int i = 0; i < bytes.size(); i++) { - byteArray[i] = bytes.get(i); + byteArray[i] = ((Byte)bytes.get(i)).byteValue(); } if ("seed".equals(field)) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java index 85ef7d5f2c..99cd1532ef 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/ArmoredInputStream.java @@ -218,7 +218,7 @@ private void rejectUnknownHeadersInCSFMessages() throws ArmoredInputException { Iterator headerLines = headerList.iterator(); - String header = headerLines.next(); + String header = (String)headerLines.next(); // Only reject unknown headers in cleartext signed messages if (!header.startsWith("-----BEGIN PGP SIGNED MESSAGE-----")) @@ -229,7 +229,7 @@ private void rejectUnknownHeadersInCSFMessages() outerloop: while (headerLines.hasNext()) { - String headerLine = headerLines.next(); + String headerLine = (String)headerLines.next(); for (Iterator it = allowedHeaders.iterator(); it.hasNext(); ) { if (headerLine.startsWith((String)it.next() + ": ")) From e948e5ccc037d28a17ee982d87d7dd692a1433c6 Mon Sep 17 00:00:00 2001 From: Gefei Li Date: Sat, 31 May 2025 22:21:50 +0000 Subject: [PATCH 403/890] Add kem hqc jdk21 --- .../jce/provider/BouncyCastleProvider.java | 3 +- .../provider/BouncyCastlePQCProvider.java | 8 +- .../bouncycastle/pqc/jcajce/provider/HQC.java | 17 ++ .../jcajce/provider/frodo/FrodoCipherSpi.java | 3 - .../jcajce/provider/hqc/HQCKeyFactorySpi.java | 56 +++++- .../provider/hqc/HQCKeyGeneratorSpi.java | 60 +++++- .../provider/hqc/HQCKeyPairGeneratorSpi.java | 48 ++++- .../bouncycastle/pqc/jcajce/provider/HQC.java | 58 ++++++ .../provider/hqc/HQCDecapsulatorSpi.java | 80 ++++++++ .../provider/hqc/HQCEncapsulatorSpi.java | 87 +++++++++ .../pqc/jcajce/provider/hqc/HQCKEMSpi.java | 62 +++++++ .../test/HQCKeyPairGeneratorTest.java | 6 +- .../pqc/jcajce/provider/test/HQCTest.java | 13 ++ .../jcacje/provider/test/AllTests21.java | 2 +- .../jcacje/provider/test/HQCTest.java | 174 ++++++++++++++++++ 15 files changed, 653 insertions(+), 24 deletions(-) create mode 100644 prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/HQC.java create mode 100644 prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/hqc/HQCDecapsulatorSpi.java create mode 100644 prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/hqc/HQCEncapsulatorSpi.java create mode 100644 prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKEMSpi.java create mode 100644 prov/src/test/jdk21/org/bouncycastle/jcacje/provider/test/HQCTest.java diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java index df5e014c01..5511f820bc 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java @@ -130,7 +130,8 @@ public final class BouncyCastleProvider extends Provider private static final String[] ASYMMETRIC_CIPHERS = { - "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145", "GM", "EdEC", "LMS", "SPHINCSPlus", "Dilithium", "Falcon", "NTRU", "CONTEXT", "SLHDSA", "MLDSA", "MLKEM" + "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145", "GM", "EdEC", "LMS", "SPHINCSPlus", + "Dilithium", "Falcon", "NTRU", "CONTEXT", "SLHDSA", "MLDSA", "MLKEM", "HQC" }; /* diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java index 1fb5c14a72..17fe0229d5 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java @@ -32,8 +32,8 @@ public class BouncyCastlePQCProvider private static final Map keyInfoConverters = new HashMap(); /* - * Configurable symmetric ciphers - */ + * Configurable symmetric ciphers + */ private static final String ALGORITHM_PACKAGE = "org.bouncycastle.pqc.jcajce.provider."; private static final String[] ALGORITHMS = { @@ -118,7 +118,7 @@ public void addAlgorithm(String key, String value, Map attribute addAttributes(key, attributes); } - public void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className) + public void addAlgorithm(String type, ASN1ObjectIdentifier oid, String className) { if (!containsKey(type + "." + className)) { @@ -151,7 +151,7 @@ public AsymmetricKeyInfoConverter getKeyInfoConverter(ASN1ObjectIdentifier oid) public void addAttributes(String key, Map attributeMap) { - for (Iterator it = attributeMap.keySet().iterator(); it.hasNext();) + for (Iterator it = attributeMap.keySet().iterator(); it.hasNext(); ) { String attributeName = (String)it.next(); String attributeKey = key + " " + attributeName; diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/HQC.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/HQC.java index 6492ad122a..882e9a2ee2 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/HQC.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/HQC.java @@ -20,13 +20,26 @@ public Mappings() public void configure(ConfigurableProvider provider) { provider.addAlgorithm("KeyFactory.HQC", PREFIX + "HQCKeyFactorySpi"); + provider.addAlgorithm("Alg.Alias.KeyFactory.HQC", "HQC"); + addKeyFactoryAlgorithm(provider, "HQC128", PREFIX + "HQCKeyFactorySpi$HQC128", BCObjectIdentifiers.hqc128, new HQCKeyFactorySpi.HQC128()); + addKeyFactoryAlgorithm(provider, "HQC192", PREFIX + "HQCKeyFactorySpi$HQC192", BCObjectIdentifiers.hqc192, new HQCKeyFactorySpi.HQC192()); + addKeyFactoryAlgorithm(provider, "HQC256", PREFIX + "HQCKeyFactorySpi$HQC256", BCObjectIdentifiers.hqc256, new HQCKeyFactorySpi.HQC256()); + provider.addAlgorithm("KeyPairGenerator.HQC", PREFIX + "HQCKeyPairGeneratorSpi"); + provider.addAlgorithm("Alg.Alias.KeyPairGenerator.HQC", "HQC"); + addKeyPairGeneratorAlgorithm(provider, "HQC128", PREFIX + "HQCKeyPairGeneratorSpi$HQC128", BCObjectIdentifiers.hqc128); + addKeyPairGeneratorAlgorithm(provider, "HQC192", PREFIX + "HQCKeyPairGeneratorSpi$HQC192", BCObjectIdentifiers.hqc192); + addKeyPairGeneratorAlgorithm(provider, "HQC256", PREFIX + "HQCKeyPairGeneratorSpi$HQC256", BCObjectIdentifiers.hqc256); provider.addAlgorithm("KeyGenerator.HQC", PREFIX + "HQCKeyGeneratorSpi"); + addKeyGeneratorAlgorithm(provider, "HQC128", PREFIX + "HQCKeyGeneratorSpi$HQC128", BCObjectIdentifiers.hqc128); + addKeyGeneratorAlgorithm(provider, "HQC192", PREFIX + "HQCKeyGeneratorSpi$HQC192", BCObjectIdentifiers.hqc192); + addKeyGeneratorAlgorithm(provider, "HQC256", PREFIX + "HQCKeyGeneratorSpi$HQC256", BCObjectIdentifiers.hqc256); AsymmetricKeyInfoConverter keyFact = new HQCKeyFactorySpi(); provider.addAlgorithm("Cipher.HQC", PREFIX + "HQCCipherSpi$Base"); + provider.addAlgorithm("Alg.Alias.Cipher.HQC", "HQC"); provider.addAlgorithm("Alg.Alias.Cipher." + BCObjectIdentifiers.pqc_kem_hqc, "HQC"); addCipherAlgorithm(provider, "HQC128", PREFIX + "HQCCipherSpi$HQC128", BCObjectIdentifiers.hqc128); @@ -34,6 +47,10 @@ public void configure(ConfigurableProvider provider) addCipherAlgorithm(provider, "HQC256", PREFIX + "HQCCipherSpi$HQC256", BCObjectIdentifiers.hqc256); registerOid(provider, BCObjectIdentifiers.pqc_kem_hqc, "HQC", keyFact); + provider.addKeyInfoConverter(BCObjectIdentifiers.hqc128, keyFact); + provider.addKeyInfoConverter(BCObjectIdentifiers.hqc192, keyFact); + provider.addKeyInfoConverter(BCObjectIdentifiers.hqc256, keyFact); } } } + diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/frodo/FrodoCipherSpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/frodo/FrodoCipherSpi.java index f7f65a30a8..2520da5150 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/frodo/FrodoCipherSpi.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/frodo/FrodoCipherSpi.java @@ -27,9 +27,6 @@ import org.bouncycastle.jcajce.spec.KEMParameterSpec; import org.bouncycastle.pqc.crypto.frodo.FrodoKEMExtractor; import org.bouncycastle.pqc.crypto.frodo.FrodoKEMGenerator; -import org.bouncycastle.pqc.crypto.hqc.HQCKEMGenerator; -import org.bouncycastle.pqc.jcajce.provider.hqc.BCHQCPrivateKey; -import org.bouncycastle.pqc.jcajce.provider.hqc.BCHQCPublicKey; import org.bouncycastle.pqc.jcajce.provider.util.WrapUtil; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Exceptions; diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKeyFactorySpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKeyFactorySpi.java index e3b08b6ada..4a36a31c10 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKeyFactorySpi.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKeyFactorySpi.java @@ -3,23 +3,44 @@ import java.io.IOException; import java.security.InvalidKeyException; import java.security.Key; -import java.security.KeyFactorySpi; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; +import java.util.HashSet; +import java.util.Set; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.bc.BCObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; -import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; +import org.bouncycastle.pqc.jcajce.provider.util.BaseKeyFactorySpi; public class HQCKeyFactorySpi - extends KeyFactorySpi - implements AsymmetricKeyInfoConverter + extends BaseKeyFactorySpi { + private static final Set keyOids = new HashSet(); + + static + { + keyOids.add(BCObjectIdentifiers.hqc128); + keyOids.add(BCObjectIdentifiers.hqc192); + keyOids.add(BCObjectIdentifiers.hqc256); + } + + public HQCKeyFactorySpi() + { + super(keyOids); + } + + public HQCKeyFactorySpi(ASN1ObjectIdentifier keyOids) + { + super(keyOids); + } + public PrivateKey engineGeneratePrivate(KeySpec keySpec) throws InvalidKeySpecException { @@ -113,4 +134,31 @@ public PublicKey generatePublic(SubjectPublicKeyInfo keyInfo) { return new BCHQCPublicKey(keyInfo); } + + public static class HQC128 + extends HQCKeyFactorySpi + { + public HQC128() + { + super(BCObjectIdentifiers.hqc128); + } + } + + public static class HQC192 + extends HQCKeyFactorySpi + { + public HQC192() + { + super(BCObjectIdentifiers.hqc192); + } + } + + public static class HQC256 + extends HQCKeyFactorySpi + { + public HQC256() + { + super(BCObjectIdentifiers.hqc256); + } + } } diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKeyGeneratorSpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKeyGeneratorSpi.java index 87619dd57b..cd0d6e51b7 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKeyGeneratorSpi.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKeyGeneratorSpi.java @@ -15,14 +15,27 @@ import org.bouncycastle.jcajce.spec.KEMGenerateSpec; import org.bouncycastle.pqc.crypto.hqc.HQCKEMExtractor; import org.bouncycastle.pqc.crypto.hqc.HQCKEMGenerator; +import org.bouncycastle.pqc.crypto.hqc.HQCParameters; +import org.bouncycastle.pqc.jcajce.spec.HQCParameterSpec; import org.bouncycastle.util.Arrays; public class HQCKeyGeneratorSpi - extends KeyGeneratorSpi + extends KeyGeneratorSpi { private KEMGenerateSpec genSpec; private SecureRandom random; private KEMExtractSpec extSpec; + private HQCParameters hqcParameters; + + public HQCKeyGeneratorSpi() + { + this(null); + } + + public HQCKeyGeneratorSpi(HQCParameters hqcParameters) + { + this.hqcParameters = hqcParameters; + } protected void engineInit(SecureRandom secureRandom) { @@ -30,18 +43,34 @@ protected void engineInit(SecureRandom secureRandom) } protected void engineInit(AlgorithmParameterSpec algorithmParameterSpec, SecureRandom secureRandom) - throws InvalidAlgorithmParameterException + throws InvalidAlgorithmParameterException { this.random = secureRandom; if (algorithmParameterSpec instanceof KEMGenerateSpec) { this.genSpec = (KEMGenerateSpec)algorithmParameterSpec; this.extSpec = null; + if (hqcParameters != null) + { + String canonicalAlgName = HQCParameterSpec.fromName(hqcParameters.getName()).getName(); + if (!canonicalAlgName.equals(genSpec.getPublicKey().getAlgorithm())) + { + throw new InvalidAlgorithmParameterException("key generator locked to " + canonicalAlgName); + } + } } else if (algorithmParameterSpec instanceof KEMExtractSpec) { this.genSpec = null; this.extSpec = (KEMExtractSpec)algorithmParameterSpec; + if (hqcParameters != null) + { + String canonicalAlgName = HQCParameterSpec.fromName(hqcParameters.getName()).getName(); + if (!canonicalAlgName.equals(extSpec.getPrivateKey().getAlgorithm())) + { + throw new InvalidAlgorithmParameterException("key generator locked to " + canonicalAlgName); + } + } } else { @@ -91,4 +120,31 @@ protected SecretKey engineGenerateKey() return rv; } } + + public static class HQC128 + extends HQCKeyGeneratorSpi + { + public HQC128() + { + super(HQCParameters.hqc128); + } + } + + public static class HQC192 + extends HQCKeyGeneratorSpi + { + public HQC192() + { + super(HQCParameters.hqc192); + } + } + + public static class HQC256 + extends HQCKeyGeneratorSpi + { + public HQC256() + { + super(HQCParameters.hqc256); + } + } } diff --git a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKeyPairGeneratorSpi.java b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKeyPairGeneratorSpi.java index 989f22714d..2f6624890e 100644 --- a/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKeyPairGeneratorSpi.java +++ b/prov/src/main/java/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKeyPairGeneratorSpi.java @@ -2,6 +2,7 @@ import java.security.InvalidAlgorithmParameterException; import java.security.KeyPair; +import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.AlgorithmParameterSpec; import java.util.HashMap; @@ -19,7 +20,7 @@ import org.bouncycastle.util.Strings; public class HQCKeyPairGeneratorSpi - extends java.security.KeyPairGenerator + extends java.security.KeyPairGenerator { private static Map parameters = new HashMap(); @@ -45,17 +46,22 @@ public HQCKeyPairGeneratorSpi() super("HQC"); } + protected HQCKeyPairGeneratorSpi(HQCParameterSpec paramSpec) + { + super(Strings.toUpperCase(paramSpec.getName())); + } + public void initialize( - int strength, - SecureRandom random) + int strength, + SecureRandom random) { throw new IllegalArgumentException("use AlgorithmParameterSpec"); } public void initialize( - AlgorithmParameterSpec params, - SecureRandom random) - throws InvalidAlgorithmParameterException + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidAlgorithmParameterException { String name = getNameFromParams(params); @@ -101,4 +107,34 @@ public KeyPair generateKeyPair() return new KeyPair(new BCHQCPublicKey(pub), new BCHQCPrivateKey(priv)); } + + public static class HQC128 + extends HQCKeyPairGeneratorSpi + { + public HQC128() + throws NoSuchAlgorithmException + { + super(HQCParameterSpec.hqc128); + } + } + + public static class HQC192 + extends HQCKeyPairGeneratorSpi + { + public HQC192() + throws NoSuchAlgorithmException + { + super(HQCParameterSpec.hqc192); + } + } + + public static class HQC256 + extends HQCKeyPairGeneratorSpi + { + public HQC256() + throws NoSuchAlgorithmException + { + super(HQCParameterSpec.hqc256); + } + } } diff --git a/prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/HQC.java b/prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/HQC.java new file mode 100644 index 0000000000..c501d1637f --- /dev/null +++ b/prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/HQC.java @@ -0,0 +1,58 @@ +package org.bouncycastle.pqc.jcajce.provider; + +import org.bouncycastle.asn1.bc.BCObjectIdentifiers; +import org.bouncycastle.jcajce.provider.config.ConfigurableProvider; +import org.bouncycastle.jcajce.provider.util.AsymmetricAlgorithmProvider; +import org.bouncycastle.jcajce.provider.util.AsymmetricKeyInfoConverter; +import org.bouncycastle.pqc.jcajce.provider.hqc.HQCKeyFactorySpi; + +public class HQC +{ + private static final String PREFIX = "org.bouncycastle.pqc.jcajce.provider" + ".hqc."; + + public static class Mappings + extends AsymmetricAlgorithmProvider + { + public Mappings() + { + } + + public void configure(ConfigurableProvider provider) + { + provider.addAlgorithm("KeyFactory.HQC", PREFIX + "HQCKeyFactorySpi"); + provider.addAlgorithm("Alg.Alias.KeyFactory.HQC", "HQC"); + addKeyFactoryAlgorithm(provider, "HQC128", PREFIX + "HQCKeyFactorySpi$HQC128", BCObjectIdentifiers.hqc128, new HQCKeyFactorySpi.HQC128()); + addKeyFactoryAlgorithm(provider, "HQC192", PREFIX + "HQCKeyFactorySpi$HQC192", BCObjectIdentifiers.hqc192, new HQCKeyFactorySpi.HQC192()); + addKeyFactoryAlgorithm(provider, "HQC256", PREFIX + "HQCKeyFactorySpi$HQC256", BCObjectIdentifiers.hqc256, new HQCKeyFactorySpi.HQC256()); + + provider.addAlgorithm("KeyPairGenerator.HQC", PREFIX + "HQCKeyPairGeneratorSpi"); + provider.addAlgorithm("Alg.Alias.KeyPairGenerator.HQC", "HQC"); + addKeyPairGeneratorAlgorithm(provider, "HQC128", PREFIX + "HQCKeyPairGeneratorSpi$HQC128", BCObjectIdentifiers.hqc128); + addKeyPairGeneratorAlgorithm(provider, "HQC192", PREFIX + "HQCKeyPairGeneratorSpi$HQC192", BCObjectIdentifiers.hqc192); + addKeyPairGeneratorAlgorithm(provider, "HQC256", PREFIX + "HQCKeyPairGeneratorSpi$HQC256", BCObjectIdentifiers.hqc256); + + provider.addAlgorithm("KeyGenerator.HQC", PREFIX + "HQCKeyGeneratorSpi"); + addKeyGeneratorAlgorithm(provider, "HQC128", PREFIX + "HQCKeyGeneratorSpi$HQC128", BCObjectIdentifiers.hqc128); + addKeyGeneratorAlgorithm(provider, "HQC192", PREFIX + "HQCKeyGeneratorSpi$HQC192", BCObjectIdentifiers.hqc192); + addKeyGeneratorAlgorithm(provider, "HQC256", PREFIX + "HQCKeyGeneratorSpi$HQC256", BCObjectIdentifiers.hqc256); + + AsymmetricKeyInfoConverter keyFact = new HQCKeyFactorySpi(); + + provider.addAlgorithm("Cipher.HQC", PREFIX + "HQCCipherSpi$Base"); + provider.addAlgorithm("Alg.Alias.Cipher.HQC", "HQC"); + + addCipherAlgorithm(provider, "HQC128", PREFIX + "HQCCipherSpi$HQC128", BCObjectIdentifiers.hqc128); + addCipherAlgorithm(provider, "HQC192", PREFIX + "HQCCipherSpi$HQC192", BCObjectIdentifiers.hqc192); + addCipherAlgorithm(provider, "HQC256", PREFIX + "HQCCipherSpi$HQC256", BCObjectIdentifiers.hqc256); + + provider.addKeyInfoConverter(BCObjectIdentifiers.hqc128, keyFact); + provider.addKeyInfoConverter(BCObjectIdentifiers.hqc192, keyFact); + provider.addKeyInfoConverter(BCObjectIdentifiers.hqc256, keyFact); + + provider.addAlgorithm("KEM.HQC", PREFIX + "HQCKEMSpi"); + provider.addAlgorithm("Alg.Alias.KEM." + BCObjectIdentifiers.hqc128, "HQC"); + provider.addAlgorithm("Alg.Alias.KEM." + BCObjectIdentifiers.hqc192, "HQC"); + provider.addAlgorithm("Alg.Alias.KEM." + BCObjectIdentifiers.hqc256, "HQC"); + } + } +} diff --git a/prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/hqc/HQCDecapsulatorSpi.java b/prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/hqc/HQCDecapsulatorSpi.java new file mode 100644 index 0000000000..c5cb327ac1 --- /dev/null +++ b/prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/hqc/HQCDecapsulatorSpi.java @@ -0,0 +1,80 @@ +package org.bouncycastle.pqc.jcajce.provider.hqc; + +import org.bouncycastle.pqc.jcajce.provider.hqc.BCHQCPrivateKey; +import org.bouncycastle.jcajce.spec.KTSParameterSpec; + +import org.bouncycastle.pqc.crypto.hqc.HQCKEMExtractor; +import org.bouncycastle.pqc.jcajce.provider.util.KdfUtil; + +import javax.crypto.DecapsulateException; +import javax.crypto.KEMSpi; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import java.util.Arrays; +import java.util.Objects; + +public class HQCDecapsulatorSpi + implements KEMSpi.DecapsulatorSpi +{ + BCHQCPrivateKey privateKey; + KTSParameterSpec parameterSpec; + HQCKEMExtractor kemExt; + + public HQCDecapsulatorSpi(BCHQCPrivateKey privateKey, KTSParameterSpec parameterSpec) + { + this.privateKey = privateKey; + this.parameterSpec = parameterSpec; + + this.kemExt = new HQCKEMExtractor(privateKey.getKeyParams()); + } + + @Override + public SecretKey engineDecapsulate(byte[] encapsulation, int from, int to, String algorithm) + throws DecapsulateException + { + Objects.checkFromToIndex(from, to, engineSecretSize()); + Objects.requireNonNull(algorithm, "null algorithm"); + Objects.requireNonNull(encapsulation, "null encapsulation"); + + if (encapsulation.length != engineEncapsulationSize()) + { + throw new DecapsulateException("incorrect encapsulation size"); + } + + // if algorithm is Generic then use parameterSpec to wrap key + if (!parameterSpec.getKeyAlgorithmName().equals("Generic") && + algorithm.equals("Generic")) + { + algorithm = parameterSpec.getKeyAlgorithmName(); + } + + // check spec algorithm mismatch provided algorithm + if (!parameterSpec.getKeyAlgorithmName().equals("Generic") && + !parameterSpec.getKeyAlgorithmName().equals(algorithm)) + { + throw new UnsupportedOperationException(parameterSpec.getKeyAlgorithmName() + " does not match " + algorithm); + } + + // Only use KDF when ktsParameterSpec is provided + // Considering any ktsParameterSpec with "Generic" as ktsParameterSpec not provided + boolean useKDF = parameterSpec.getKdfAlgorithm() != null; + + byte[] secret = kemExt.extractSecret(encapsulation); + byte[] secretKey = Arrays.copyOfRange(KdfUtil.makeKeyBytes(parameterSpec, secret), from, to); + + return new SecretKeySpec(secretKey, algorithm); + } + + @Override + public int engineSecretSize() + { + return parameterSpec.getKeySize() / 8; + } + + @Override + public int engineEncapsulationSize() + { + return kemExt.getEncapsulationLength(); + } +} \ No newline at end of file diff --git a/prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/hqc/HQCEncapsulatorSpi.java b/prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/hqc/HQCEncapsulatorSpi.java new file mode 100644 index 0000000000..c3623c4e4a --- /dev/null +++ b/prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/hqc/HQCEncapsulatorSpi.java @@ -0,0 +1,87 @@ +package org.bouncycastle.pqc.jcajce.provider.hqc; + +import org.bouncycastle.crypto.SecretWithEncapsulation; +import org.bouncycastle.pqc.jcajce.provider.hqc.BCHQCPublicKey; +import org.bouncycastle.jcajce.spec.KTSParameterSpec; +import org.bouncycastle.pqc.crypto.hqc.HQCKEMGenerator; +import org.bouncycastle.pqc.jcajce.provider.util.KdfUtil; + +import javax.crypto.KEM; +import javax.crypto.KEMSpi; +import javax.crypto.spec.SecretKeySpec; + +import java.security.SecureRandom; +import java.util.Arrays; +import java.util.Objects; + +public class HQCEncapsulatorSpi + implements KEMSpi.EncapsulatorSpi +{ + private final BCHQCPublicKey publicKey; + private final KTSParameterSpec parameterSpec; + private final HQCKEMGenerator kemGen; + + public HQCEncapsulatorSpi(BCHQCPublicKey publicKey, KTSParameterSpec parameterSpec, SecureRandom random) + { + this.publicKey = publicKey; + this.parameterSpec = parameterSpec; + + this.kemGen = new HQCKEMGenerator(random); + } + + @Override + public KEM.Encapsulated engineEncapsulate(int from, int to, String algorithm) + { + Objects.checkFromToIndex(from, to, engineSecretSize()); + Objects.requireNonNull(algorithm, "null algorithm"); + + // if algorithm is Generic then use parameterSpec to wrap key + if (!parameterSpec.getKeyAlgorithmName().equals("Generic") && + algorithm.equals("Generic")) + { + algorithm = parameterSpec.getKeyAlgorithmName(); + } + + // check spec algorithm mismatch provided algorithm + if (!parameterSpec.getKeyAlgorithmName().equals("Generic") && + !parameterSpec.getKeyAlgorithmName().equals(algorithm)) + { + throw new UnsupportedOperationException(parameterSpec.getKeyAlgorithmName() + " does not match " + algorithm); + } + + // Only use KDF when ktsParameterSpec is provided + // Considering any ktsParameterSpec with "Generic" as ktsParameterSpec not provided + boolean useKDF = parameterSpec.getKdfAlgorithm() != null; + + SecretWithEncapsulation secEnc = kemGen.generateEncapsulated(publicKey.getKeyParams()); + + byte[] encapsulation = secEnc.getEncapsulation(); + byte[] secret = secEnc.getSecret(); + byte[] secretKey = Arrays.copyOfRange(KdfUtil.makeKeyBytes(parameterSpec, secret), from, to); + + return new KEM.Encapsulated(new SecretKeySpec(secretKey, algorithm), encapsulation, null); //TODO: DER encoding for params + } + + @Override + public int engineSecretSize() + { + return parameterSpec.getKeySize() / 8; + } + + @Override + public int engineEncapsulationSize() + { + //TODO: Maybe make parameterSet public or add getEncapsulationSize() in HQCKEMGenerator.java + switch (publicKey.getKeyParams().getParameters().getName()) + { + case "HQC-128": + return 128; + case "HQC-192": + return 192; + case "HQC-256": + return 256; + default: + return -1; + } + } +} \ No newline at end of file diff --git a/prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKEMSpi.java b/prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKEMSpi.java new file mode 100644 index 0000000000..329ef16f2f --- /dev/null +++ b/prov/src/main/jdk21/org/bouncycastle/pqc/jcajce/provider/hqc/HQCKEMSpi.java @@ -0,0 +1,62 @@ +package org.bouncycastle.pqc.jcajce.provider.hqc; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.KEMSpi; + +import org.bouncycastle.jcajce.spec.KTSParameterSpec; + +public class HQCKEMSpi + implements KEMSpi +{ + + @Override + public EncapsulatorSpi engineNewEncapsulator(PublicKey publicKey, AlgorithmParameterSpec spec, + SecureRandom secureRandom) + throws InvalidAlgorithmParameterException, InvalidKeyException + { + if (!(publicKey instanceof BCHQCPublicKey)) + { + throw new InvalidKeyException("unsupported key"); + } + if (spec == null) + { + // Do not wrap key, no KDF + spec = new KTSParameterSpec.Builder("Generic", 256).withNoKdf().build(); + } + if (!(spec instanceof KTSParameterSpec)) + { + throw new InvalidAlgorithmParameterException("HQC can only accept KTSParameterSpec"); + } + if (secureRandom == null) + { + secureRandom = new SecureRandom(); + } + return new HQCEncapsulatorSpi((BCHQCPublicKey)publicKey, (KTSParameterSpec)spec, secureRandom); + } + + @Override + public DecapsulatorSpi engineNewDecapsulator(PrivateKey privateKey, AlgorithmParameterSpec spec) + throws InvalidAlgorithmParameterException, InvalidKeyException + { + if (!(privateKey instanceof BCHQCPrivateKey)) + { + throw new InvalidKeyException("unsupported key"); + } + if (spec == null) + { + // Do not unwrap key, no KDF + spec = new KTSParameterSpec.Builder("Generic", 256).withNoKdf().build(); + } + if (!(spec instanceof KTSParameterSpec)) + { + throw new InvalidAlgorithmParameterException("HQC can only accept KTSParameterSpec"); + } + return new HQCDecapsulatorSpi((BCHQCPrivateKey)privateKey, (KTSParameterSpec)spec); + } +} \ No newline at end of file diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCKeyPairGeneratorTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCKeyPairGeneratorTest.java index f5e08f860a..f866b4fbd5 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCKeyPairGeneratorTest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCKeyPairGeneratorTest.java @@ -31,9 +31,9 @@ public void testKeyPairEncoding() HQCParameterSpec[] specs = new HQCParameterSpec[] { - HQCParameterSpec.hqc128, - HQCParameterSpec.hqc192, - HQCParameterSpec.hqc256 + HQCParameterSpec.hqc128, + HQCParameterSpec.hqc192, + HQCParameterSpec.hqc256 }; kf = KeyFactory.getInstance("HQC", "BCPQC"); diff --git a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java index c2471029e1..3451ba96eb 100644 --- a/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java +++ b/prov/src/test/java/org/bouncycastle/pqc/jcajce/provider/test/HQCTest.java @@ -16,6 +16,7 @@ import org.bouncycastle.jcajce.spec.KEMExtractSpec; import org.bouncycastle.jcajce.spec.KEMGenerateSpec; import org.bouncycastle.jcajce.spec.KEMParameterSpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.pqc.crypto.hqc.HQCKeyGenerationParameters; import org.bouncycastle.pqc.crypto.hqc.HQCKeyPairGenerator; import org.bouncycastle.pqc.crypto.hqc.HQCParameters; @@ -31,12 +32,24 @@ public class HQCTest extends TestCase { + public static void main(String[] args) + throws Exception + { + HQCTest test = new HQCTest(); + test.setUp(); + test.testGenerateAES(); + } + public void setUp() { if (Security.getProvider(BouncyCastlePQCProvider.PROVIDER_NAME) == null) { Security.addProvider(new BouncyCastlePQCProvider()); } +// if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) +// { +// Security.addProvider(new BouncyCastleProvider()); +// } } public void testBasicKEMAES() diff --git a/prov/src/test/jdk21/org/bouncycastle/jcacje/provider/test/AllTests21.java b/prov/src/test/jdk21/org/bouncycastle/jcacje/provider/test/AllTests21.java index b9e25bf8b5..c89ddf6bb1 100644 --- a/prov/src/test/jdk21/org/bouncycastle/jcacje/provider/test/AllTests21.java +++ b/prov/src/test/jdk21/org/bouncycastle/jcacje/provider/test/AllTests21.java @@ -11,7 +11,6 @@ public class AllTests21 { public static void main(String[] args) { - PrintTestResult.printResult(junit.textui.TestRunner.run(suite())); } @@ -21,6 +20,7 @@ public static Test suite() suite.addTestSuite(NTRUKEMTest.class); suite.addTestSuite(SNTRUPrimeKEMTest.class); suite.addTestSuite(MLKEMTest.class); + suite.addTestSuite(HQCTest.class); return suite; } } diff --git a/prov/src/test/jdk21/org/bouncycastle/jcacje/provider/test/HQCTest.java b/prov/src/test/jdk21/org/bouncycastle/jcacje/provider/test/HQCTest.java new file mode 100644 index 0000000000..42f9cefda3 --- /dev/null +++ b/prov/src/test/jdk21/org/bouncycastle/jcacje/provider/test/HQCTest.java @@ -0,0 +1,174 @@ +package org.bouncycastle.jcacje.provider.test; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.Security; + +import javax.crypto.KEM; +import javax.crypto.SecretKey; + +import org.bouncycastle.jcajce.spec.KEMParameterSpec; +import org.bouncycastle.jcajce.spec.KTSParameterSpec; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider; +import org.bouncycastle.pqc.jcajce.spec.HQCParameterSpec; +import org.bouncycastle.util.Arrays; + +import junit.framework.TestCase; + +public class HQCTest + extends TestCase +{ + public static void main(String[] args) + throws Exception + { + HQCTest test = new HQCTest(); + test.setUp(); + test.testKEM(); + } + + public void setUp() + { + if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME) == null) + { + Security.addProvider(new BouncyCastleProvider()); + } + if (Security.getProvider(BouncyCastlePQCProvider.PROVIDER_NAME) == null) + { + Security.addProvider(new BouncyCastlePQCProvider()); + } + } + + public void testKEM() + throws Exception + { + // Receiver side + KeyPairGenerator g = KeyPairGenerator.getInstance("HQC", "BCPQC"); + + g.initialize(HQCParameterSpec.hqc192, new SecureRandom()); + + KeyPair kp = g.generateKeyPair(); + PublicKey pkR = kp.getPublic(); + + // Sender side + KEM kemS = KEM.getInstance("HQC"); + KTSParameterSpec ktsSpec = null; + KEM.Encapsulator e = kemS.newEncapsulator(pkR, ktsSpec, null); + KEM.Encapsulated enc = e.encapsulate(); + SecretKey secS = enc.key(); + byte[] em = enc.encapsulation(); + byte[] params = enc.params(); + + // Receiver side + KEM kemR = KEM.getInstance("HQC"); + KEM.Decapsulator d = kemR.newDecapsulator(kp.getPrivate(), ktsSpec); + SecretKey secR = d.decapsulate(em); + + // secS and secR will be identical + assertEquals(secS.getAlgorithm(), secR.getAlgorithm()); + assertTrue(Arrays.areEqual(secS.getEncoded(), secR.getEncoded())); + } + + public void testBasicKEMAES() + throws Exception + { + if (Security.getProvider(BouncyCastlePQCProvider.PROVIDER_NAME) == null) + { + Security.addProvider(new BouncyCastlePQCProvider()); + } + KeyPairGenerator kpg = KeyPairGenerator.getInstance("HQC", "BCPQC"); + kpg.initialize(HQCParameterSpec.hqc192, new SecureRandom()); + + performKEM(kpg.generateKeyPair(), new KEMParameterSpec("AES")); + performKEM(kpg.generateKeyPair(), 0, 16, "AES", new KEMParameterSpec("AES")); + performKEM(kpg.generateKeyPair(), new KEMParameterSpec("AES-KWP")); + + try + { + performKEM(kpg.generateKeyPair(), 0, 16, "AES-KWP", new KEMParameterSpec("AES")); + fail(); + } + catch (Exception ex) + { + } + + kpg.initialize(HQCParameterSpec.hqc256, new SecureRandom()); + performKEM(kpg.generateKeyPair(), new KEMParameterSpec("AES")); + + } + + public void testBasicKEMCamellia() + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("HQC", "BCPQC"); + kpg.initialize(HQCParameterSpec.hqc128, new SecureRandom()); + + performKEM(kpg.generateKeyPair(), new KTSParameterSpec.Builder("Camellia", 256).build()); + performKEM(kpg.generateKeyPair(), new KTSParameterSpec.Builder("Camellia-KWP", 256).build()); + } + + public void testBasicKEMSEED() + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("HQC", "BCPQC"); + kpg.initialize(HQCParameterSpec.hqc192, new SecureRandom()); + + performKEM(kpg.generateKeyPair(), new KTSParameterSpec.Builder("SEED", 128).build()); + } + + public void testBasicKEMARIA() + throws Exception + { + KeyPairGenerator kpg = KeyPairGenerator.getInstance("HQC", "BCPQC"); + kpg.initialize(HQCParameterSpec.hqc192, new SecureRandom()); + + performKEM(kpg.generateKeyPair(), new KEMParameterSpec("ARIA")); + performKEM(kpg.generateKeyPair(), new KEMParameterSpec("ARIA-KWP")); + } + + private void performKEM(KeyPair kp, int from, int to, String algorithm, KTSParameterSpec ktsParameterSpec) + throws Exception + { + PublicKey pkR = kp.getPublic(); + + // Sender side + KEM kemS = KEM.getInstance("HQC"); + KEM.Encapsulator e = kemS.newEncapsulator(pkR, ktsParameterSpec, null); + KEM.Encapsulated enc = e.encapsulate(from, to, algorithm); + SecretKey secS = enc.key(); + byte[] em = enc.encapsulation(); + + // Receiver side + KEM kemR = KEM.getInstance("HQC"); + KEM.Decapsulator d = kemR.newDecapsulator(kp.getPrivate(), ktsParameterSpec); + SecretKey secR = d.decapsulate(em, from, to, algorithm); + + // secS and secR will be identical + assertEquals(secS.getAlgorithm(), secR.getAlgorithm()); + assertTrue(Arrays.areEqual(secS.getEncoded(), secR.getEncoded())); + } + + private void performKEM(KeyPair kp, KTSParameterSpec ktsParameterSpec) + throws Exception + { + PublicKey pkR = kp.getPublic(); + + // Sender side + KEM kemS = KEM.getInstance("HQC"); + KEM.Encapsulator e = kemS.newEncapsulator(pkR, ktsParameterSpec, null); + KEM.Encapsulated enc = e.encapsulate(); + SecretKey secS = enc.key(); + byte[] em = enc.encapsulation(); + + // Receiver side + KEM kemR = KEM.getInstance("HQC"); + KEM.Decapsulator d = kemR.newDecapsulator(kp.getPrivate(), ktsParameterSpec); + SecretKey secR = d.decapsulate(em); + + // secS and secR will be identical + assertEquals(secS.getAlgorithm(), secR.getAlgorithm()); + assertTrue(Arrays.areEqual(secS.getEncoded(), secR.getEncoded())); + } +} From 9f2c518ddace5fa4b289c57a0382339fd90ff9e6 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 1 Jun 2025 08:22:45 +1000 Subject: [PATCH 404/890] removed HQC --- .../org/bouncycastle/jce/provider/BouncyCastleProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java index 5511f820bc..15d26740d6 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java @@ -131,7 +131,7 @@ public final class BouncyCastleProvider extends Provider private static final String[] ASYMMETRIC_CIPHERS = { "DSA", "DH", "EC", "RSA", "GOST", "ECGOST", "ElGamal", "DSTU4145", "GM", "EdEC", "LMS", "SPHINCSPlus", - "Dilithium", "Falcon", "NTRU", "CONTEXT", "SLHDSA", "MLDSA", "MLKEM", "HQC" + "Dilithium", "Falcon", "NTRU", "CONTEXT", "SLHDSA", "MLDSA", "MLKEM" }; /* From 623e997d9cd1cf0b0895bff329f67ff94c3bcccd Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 1 Jun 2025 16:41:28 +1000 Subject: [PATCH 405/890] update PQC key encoding/decoding in Java 4 --- .../pqc/crypto/util/PrivateKeyFactory.java | 177 ++++++++++------ .../crypto/util/PrivateKeyInfoFactory.java | 39 ++-- .../pqc/crypto/util/PublicKeyFactory.java | 50 +++++ .../bouncycastle/pqc/crypto/util/Utils.java | 197 +++++++++++++----- .../jcajce/JcaContentSignerBuilder.java | 137 +++++++++++- 5 files changed, 462 insertions(+), 138 deletions(-) diff --git a/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java b/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java index df14bcd4e1..b1ac6575af 100644 --- a/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java +++ b/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java @@ -43,6 +43,7 @@ import org.bouncycastle.pqc.crypto.mldsa.MLDSAPublicKeyParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMParameters; import org.bouncycastle.pqc.crypto.mlkem.MLKEMPrivateKeyParameters; +import org.bouncycastle.pqc.crypto.mlkem.MLKEMPublicKeyParameters; import org.bouncycastle.pqc.crypto.newhope.NHPrivateKeyParameters; import org.bouncycastle.pqc.crypto.ntru.NTRUParameters; import org.bouncycastle.pqc.crypto.ntru.NTRUPrivateKeyParameters; @@ -147,22 +148,12 @@ else if (algOID.on(BCObjectIdentifiers.sphincsPlus) || algOID.on(BCObjectIdentif return new SPHINCSPlusPrivateKeyParameters(spParams, ASN1OctetString.getInstance(obj).getOctets()); } } - else if (Utils.shldsaParams.containsKey(algOID)) + else if (Utils.slhdsaParams.containsKey(algOID)) { SLHDSAParameters spParams = Utils.slhdsaParamsLookup(algOID); + ASN1OctetString slhdsaKey = parseOctetString(keyInfo.getPrivateKey(), spParams.getN() * 4); - ASN1Encodable obj = keyInfo.parsePrivateKey(); - if (obj instanceof ASN1Sequence) - { - SPHINCSPLUSPrivateKey spKey = SPHINCSPLUSPrivateKey.getInstance(obj); - SPHINCSPLUSPublicKey publicKey = spKey.getPublicKey(); - return new SLHDSAPrivateKeyParameters(spParams, spKey.getSkseed(), spKey.getSkprf(), - publicKey.getPkseed(), publicKey.getPkroot()); - } - else - { - return new SLHDSAPrivateKeyParameters(spParams, ASN1OctetString.getInstance(obj).getOctets()); - } + return new SLHDSAPrivateKeyParameters(spParams, slhdsaKey.getOctets()); } else if (algOID.on(BCObjectIdentifiers.picnic)) { @@ -203,10 +194,37 @@ else if (algOID.equals(NISTObjectIdentifiers.id_alg_ml_kem_512) || algOID.equals(NISTObjectIdentifiers.id_alg_ml_kem_768) || algOID.equals(NISTObjectIdentifiers.id_alg_ml_kem_1024)) { - ASN1OctetString kyberKey = ASN1OctetString.getInstance(keyInfo.parsePrivateKey()); - MLKEMParameters kyberParams = Utils.mlkemParamsLookup(algOID); + ASN1Primitive mlkemKey = parsePrimitiveString(keyInfo.getPrivateKey(), 64); + MLKEMParameters mlkemParams = Utils.mlkemParamsLookup(algOID); - return new MLKEMPrivateKeyParameters(kyberParams, kyberKey.getOctets()); + MLKEMPublicKeyParameters pubParams = null; + if (keyInfo.getPublicKeyData() != null) + { + pubParams = PublicKeyFactory.MLKEMConverter.getPublicKeyParams(mlkemParams, keyInfo.getPublicKeyData()); + } + + if (mlkemKey instanceof ASN1OctetString) + { + // TODO This should be explicitly EXPANDED_KEY or SEED (tag already removed) but is length-flexible + return new MLKEMPrivateKeyParameters(mlkemParams, ((ASN1OctetString)mlkemKey).getOctets(), pubParams); + } + else if (mlkemKey instanceof ASN1Sequence) + { + ASN1Sequence keySeq = (ASN1Sequence)mlkemKey; + byte[] seed = ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(); + byte[] encoding = ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets(); + + // TODO This should only allow seed but is length-flexible + MLKEMPrivateKeyParameters mlkemPriv = new MLKEMPrivateKeyParameters(mlkemParams, seed, pubParams); + if (!Arrays.constantTimeAreEqual(mlkemPriv.getEncoded(), encoding)) + { + throw new IllegalArgumentException("inconsistent " + mlkemParams.getName() + " private key"); + } + + return mlkemPriv; + } + + throw new IllegalArgumentException("invalid " + mlkemParams.getName() + " private key"); } else if (algOID.on(BCObjectIdentifiers.pqc_kem_ntrulprime)) { @@ -235,58 +253,37 @@ else if (algOID.on(BCObjectIdentifiers.pqc_kem_sntruprime)) } else if (Utils.mldsaParams.containsKey(algOID)) { - ASN1Encodable keyObj = keyInfo.parsePrivateKey(); - MLDSAParameters spParams = Utils.mldsaParamsLookup(algOID); + ASN1Encodable mldsaKey = parsePrimitiveString(keyInfo.getPrivateKey(), 32); + MLDSAParameters mldsaParams = Utils.mldsaParamsLookup(algOID); - if (keyObj instanceof ASN1Sequence) + MLDSAPublicKeyParameters pubParams = null; + if (keyInfo.getPublicKeyData() != null) { - ASN1Sequence keyEnc = ASN1Sequence.getInstance(keyObj); - - int version = ASN1Integer.getInstance(keyEnc.getObjectAt(0)).intValueExact(); - if (version != 0) - { - throw new IOException("unknown private key version: " + version); - } - - if (keyInfo.getPublicKeyData() != null) - { - MLDSAPublicKeyParameters pubParams = PublicKeyFactory.MLDSAConverter.getPublicKeyParams(spParams, keyInfo.getPublicKeyData()); + pubParams = PublicKeyFactory.MLDSAConverter.getPublicKeyParams(mldsaParams, keyInfo.getPublicKeyData()); + } - return new MLDSAPrivateKeyParameters(spParams, - ASN1BitString.getInstance(keyEnc.getObjectAt(1)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(2)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(3)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(4)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(5)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(6)).getOctets(), - pubParams.getT1()); // encT1 - } - else - { - return new MLDSAPrivateKeyParameters(spParams, - ASN1BitString.getInstance(keyEnc.getObjectAt(1)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(2)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(3)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(4)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(5)).getOctets(), - ASN1BitString.getInstance(keyEnc.getObjectAt(6)).getOctets(), - null); - } + if (mldsaKey instanceof ASN1OctetString) + { + // TODO This should be explicitly EXPANDED_KEY or SEED (tag already removed) but is length-flexible + return new MLDSAPrivateKeyParameters(mldsaParams, ((ASN1OctetString)mldsaKey).getOctets(), pubParams); } - else if (keyObj instanceof DEROctetString) + else if (mldsaKey instanceof ASN1Sequence) { - byte[] data = ASN1OctetString.getInstance(keyObj).getOctets(); - if (keyInfo.getPublicKeyData() != null) + ASN1Sequence keySeq = (ASN1Sequence)mldsaKey; + byte[] seed = ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(); + byte[] encoding = ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets(); + + // TODO This should only allow seed but is length-flexible + MLDSAPrivateKeyParameters mldsaPriv = new MLDSAPrivateKeyParameters(mldsaParams, seed, pubParams); + if (!Arrays.constantTimeAreEqual(mldsaPriv.getEncoded(), encoding)) { - MLDSAPublicKeyParameters pubParams = PublicKeyFactory.MLDSAConverter.getPublicKeyParams(spParams, keyInfo.getPublicKeyData()); - return new MLDSAPrivateKeyParameters(spParams, data, pubParams); + throw new IllegalArgumentException("inconsistent " + mldsaParams.getName() + " private key"); } - return new MLDSAPrivateKeyParameters(spParams, data); - } - else - { - throw new IOException("not supported"); + + return mldsaPriv; } + + throw new IllegalArgumentException("invalid " + mldsaParams.getName() + " private key"); } else if (algOID.equals(BCObjectIdentifiers.dilithium2) || algOID.equals(BCObjectIdentifiers.dilithium3) || algOID.equals(BCObjectIdentifiers.dilithium5)) @@ -380,6 +377,66 @@ else if (algOID.equals(PQCObjectIdentifiers.mcElieceCca2)) } } + /** + * So it seems for the new PQC algorithms, there's a couple of approaches to what goes in the OCTET STRING + */ + private static ASN1OctetString parseOctetString(ASN1OctetString octStr, int expectedLength) + throws IOException + { + byte[] data = octStr.getOctets(); + // + // it's the right length for a RAW encoding, just return it. + // + if (data.length == expectedLength) + { + return octStr; + } + + // + // possible internal OCTET STRING, possibly long form with or without the internal OCTET STRING + ASN1OctetString obj = Utils.parseOctetData(data); + + if (obj != null) + { + return ASN1OctetString.getInstance(obj); + } + + return octStr; + } + + /** + * So it seems for the new PQC algorithms, there's a couple of approaches to what goes in the OCTET STRING + * and in this case there may also be SEQUENCE. + */ + private static ASN1Primitive parsePrimitiveString(ASN1OctetString octStr, int expectedLength) + throws IOException + { + byte[] data = octStr.getOctets(); + // + // it's the right length for a RAW encoding, just return it. + // + if (data.length == expectedLength) + { + return octStr; + } + + // + // possible internal OCTET STRING, possibly long form with or without the internal OCTET STRING + // or possible SEQUENCE + ASN1Encodable obj = Utils.parseData(data); + + if (obj instanceof ASN1OctetString) + { + return ASN1OctetString.getInstance(obj); + } + if (obj instanceof ASN1Sequence) + { + return ASN1Sequence.getInstance(obj); + } + + return octStr; + } + private static short[] convert(byte[] octets) { short[] rv = new short[octets.length / 2]; diff --git a/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java b/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java index 9c5b3554a0..e9d2f817be 100644 --- a/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java +++ b/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PrivateKeyInfoFactory.java @@ -3,9 +3,11 @@ import java.io.IOException; import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1Set; import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERTaggedObject; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; @@ -55,7 +57,8 @@ private PrivateKeyInfoFactory() * @return the appropriate PrivateKeyInfo * @throws java.io.IOException on an error encoding the key */ - public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey) throws IOException + public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey) + throws IOException { return createPrivateKeyInfo(privateKey, null); } @@ -68,7 +71,8 @@ public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter private * @return the appropriate PrivateKeyInfo * @throws java.io.IOException on an error encoding the key */ - public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey, ASN1Set attributes) throws IOException + public static PrivateKeyInfo createPrivateKeyInfo(AsymmetricKeyParameter privateKey, ASN1Set attributes) + throws IOException { if (privateKey instanceof SPHINCSPrivateKeyParameters) { @@ -97,7 +101,7 @@ else if (privateKey instanceof NHPrivateKeyParameters) else if (privateKey instanceof SPHINCSPlusPrivateKeyParameters) { SPHINCSPlusPrivateKeyParameters params = (SPHINCSPlusPrivateKeyParameters)privateKey; - + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.sphincsPlusOidLookup(params.getParameters())); return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(params.getEncoded()), attributes, params.getPublicKey()); @@ -108,7 +112,7 @@ else if (privateKey instanceof SLHDSAPrivateKeyParameters) AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.slhdsaOidLookup(params.getParameters())); - return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(params.getEncoded()), attributes, params.getPublicKey()); + return new PrivateKeyInfo(algorithmIdentifier, params.getEncoded(), attributes); } else if (privateKey instanceof PicnicPrivateKeyParameters) { @@ -184,7 +188,7 @@ else if (privateKey instanceof FalconPrivateKeyParameters) else if (privateKey instanceof MLKEMPrivateKeyParameters) { MLKEMPrivateKeyParameters params = (MLKEMPrivateKeyParameters)privateKey; - + AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.mlkemOidLookup(params.getParameters())); byte[] seed = params.getSeed(); @@ -234,19 +238,15 @@ else if (privateKey instanceof MLDSAPrivateKeyParameters) AlgorithmIdentifier algorithmIdentifier = new AlgorithmIdentifier(Utils.mldsaOidLookup(params.getParameters())); - byte[] seed = params.getSeed(); - if (seed == null) + if (params.getPreferredFormat() == MLDSAPrivateKeyParameters.SEED_ONLY) { - MLDSAPublicKeyParameters pubParams = params.getPublicKeyParameters(); - - return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(params.getEncoded()), attributes, pubParams.getEncoded()); + return new PrivateKeyInfo(algorithmIdentifier, new DERTaggedObject(false, 0, new DEROctetString(params.getSeed())), attributes); } - else + else if (params.getPreferredFormat() == MLDSAPrivateKeyParameters.EXPANDED_KEY) { - MLDSAPublicKeyParameters pubParams = params.getPublicKeyParameters(); - - return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(params.getSeed()), attributes); + return new PrivateKeyInfo(algorithmIdentifier, new DEROctetString(params.getEncoded()), attributes); } + return new PrivateKeyInfo(algorithmIdentifier, getBasicPQCEncoding(params.getSeed(), params.getEncoded()), attributes); } else if (privateKey instanceof DilithiumPrivateKeyParameters) { @@ -277,4 +277,15 @@ else if (privateKey instanceof HQCPrivateKeyParameters) throw new IOException("key parameters not recognized"); } } + + private static ASN1Sequence getBasicPQCEncoding(byte[] seed, byte[] expanded) + { + ASN1EncodableVector v = new ASN1EncodableVector(2); + + v.add(new DEROctetString(seed)); + + v.add(new DEROctetString(expanded)); + + return new DERSequence(v); + } } diff --git a/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java b/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java index 29e2807611..55a0ddc3d2 100644 --- a/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java +++ b/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java @@ -472,6 +472,56 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } } + static class MLKEMConverter + extends SubjectPublicKeyInfoConverter + { + AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) + throws IOException + { + MLKEMParameters parameters = Utils.mlkemParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); + + try + { + ASN1Primitive obj = keyInfo.parsePublicKey(); + KyberPublicKey kyberKey = KyberPublicKey.getInstance(obj); + + return new MLKEMPublicKeyParameters(parameters, kyberKey.getT(), kyberKey.getRho()); + } + catch (Exception e) + { + // we're a raw encoding + return new MLKEMPublicKeyParameters(parameters, keyInfo.getPublicKeyData().getOctets()); + } + } + + static MLKEMPublicKeyParameters getPublicKeyParams(MLKEMParameters parameters, ASN1BitString publicKeyData) + { + try + { + ASN1Primitive obj = ASN1Primitive.fromByteArray(publicKeyData.getOctets()); + if (obj instanceof ASN1Sequence) + { + ASN1Sequence keySeq = ASN1Sequence.getInstance(obj); + + return new MLKEMPublicKeyParameters(parameters, + ASN1OctetString.getInstance(keySeq.getObjectAt(0)).getOctets(), + ASN1OctetString.getInstance(keySeq.getObjectAt(1)).getOctets()); + } + else + { + byte[] encKey = ASN1OctetString.getInstance(obj).getOctets(); + + return new MLKEMPublicKeyParameters(parameters, encKey); + } + } + catch (Exception e) + { + // we're a raw encoding + return new MLKEMPublicKeyParameters(parameters, publicKeyData.getOctets()); + } + } + } + private static class KyberConverter extends SubjectPublicKeyInfoConverter { diff --git a/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/Utils.java b/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/Utils.java index 9fc442139b..2c7d923e7a 100644 --- a/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/Utils.java +++ b/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/Utils.java @@ -1,9 +1,15 @@ package org.bouncycastle.pqc.crypto.util; +import java.io.ByteArrayInputStream; import java.util.HashMap; import java.util.Map; import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.BERTags; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.bc.BCObjectIdentifiers; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; @@ -88,9 +94,9 @@ class Utils static final Map mldsaOids = new HashMap(); static final Map mldsaParams = new HashMap(); - static final Map shldsaOids = new HashMap(); - static final Map shldsaParams = new HashMap(); - + static final Map slhdsaOids = new HashMap(); + static final Map slhdsaParams = new HashMap(); + static { mcElieceOids.put(CMCEParameters.mceliece348864r3, BCObjectIdentifiers.mceliece348864_r3); @@ -215,7 +221,7 @@ class Utils mlkemOids.put(MLKEMParameters.ml_kem_512, NISTObjectIdentifiers.id_alg_ml_kem_512); mlkemOids.put(MLKEMParameters.ml_kem_768, NISTObjectIdentifiers.id_alg_ml_kem_768); - mlkemOids.put(MLKEMParameters.ml_kem_1024,NISTObjectIdentifiers.id_alg_ml_kem_1024); + mlkemOids.put(MLKEMParameters.ml_kem_1024, NISTObjectIdentifiers.id_alg_ml_kem_1024); mlkemParams.put(NISTObjectIdentifiers.id_alg_ml_kem_512, MLKEMParameters.ml_kem_512); mlkemParams.put(NISTObjectIdentifiers.id_alg_ml_kem_768, MLKEMParameters.ml_kem_768); @@ -287,57 +293,57 @@ class Utils hqcOids.put(HQCParameters.hqc192, BCObjectIdentifiers.hqc192); hqcOids.put(HQCParameters.hqc256, BCObjectIdentifiers.hqc256); - shldsaOids.put(SLHDSAParameters.sha2_128s, NISTObjectIdentifiers.id_slh_dsa_sha2_128s); - shldsaOids.put(SLHDSAParameters.sha2_128f, NISTObjectIdentifiers.id_slh_dsa_sha2_128f); - shldsaOids.put(SLHDSAParameters.sha2_192s, NISTObjectIdentifiers.id_slh_dsa_sha2_192s); - shldsaOids.put(SLHDSAParameters.sha2_192f, NISTObjectIdentifiers.id_slh_dsa_sha2_192f); - shldsaOids.put(SLHDSAParameters.sha2_256s, NISTObjectIdentifiers.id_slh_dsa_sha2_256s); - shldsaOids.put(SLHDSAParameters.sha2_256f, NISTObjectIdentifiers.id_slh_dsa_sha2_256f); - shldsaOids.put(SLHDSAParameters.shake_128s, NISTObjectIdentifiers.id_slh_dsa_shake_128s); - shldsaOids.put(SLHDSAParameters.shake_128f, NISTObjectIdentifiers.id_slh_dsa_shake_128f); - shldsaOids.put(SLHDSAParameters.shake_192s, NISTObjectIdentifiers.id_slh_dsa_shake_192s); - shldsaOids.put(SLHDSAParameters.shake_192f, NISTObjectIdentifiers.id_slh_dsa_shake_192f); - shldsaOids.put(SLHDSAParameters.shake_256s, NISTObjectIdentifiers.id_slh_dsa_shake_256s); - shldsaOids.put(SLHDSAParameters.shake_256f, NISTObjectIdentifiers.id_slh_dsa_shake_256f); - - shldsaOids.put(SLHDSAParameters.sha2_128s_with_sha256, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128s_with_sha256); - shldsaOids.put(SLHDSAParameters.sha2_128f_with_sha256, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128f_with_sha256); - shldsaOids.put(SLHDSAParameters.sha2_192s_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192s_with_sha512); - shldsaOids.put(SLHDSAParameters.sha2_192f_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192f_with_sha512); - shldsaOids.put(SLHDSAParameters.sha2_256s_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256s_with_sha512); - shldsaOids.put(SLHDSAParameters.sha2_256f_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256f_with_sha512); - shldsaOids.put(SLHDSAParameters.shake_128s_with_shake128, NISTObjectIdentifiers.id_hash_slh_dsa_shake_128s_with_shake128); - shldsaOids.put(SLHDSAParameters.shake_128f_with_shake128, NISTObjectIdentifiers.id_hash_slh_dsa_shake_128f_with_shake128); - shldsaOids.put(SLHDSAParameters.shake_192s_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_192s_with_shake256); - shldsaOids.put(SLHDSAParameters.shake_192f_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_192f_with_shake256); - shldsaOids.put(SLHDSAParameters.shake_256s_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_256s_with_shake256); - shldsaOids.put(SLHDSAParameters.shake_256f_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_256f_with_shake256); - - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_128s, SLHDSAParameters.sha2_128s); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_128f, SLHDSAParameters.sha2_128f); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_192s, SLHDSAParameters.sha2_192s); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_192f, SLHDSAParameters.sha2_192f); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_256s, SLHDSAParameters.sha2_256s); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_256f, SLHDSAParameters.sha2_256f); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_128s, SLHDSAParameters.shake_128s); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_128f, SLHDSAParameters.shake_128f); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_192s, SLHDSAParameters.shake_192s); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_192f, SLHDSAParameters.shake_192f); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_256s, SLHDSAParameters.shake_256s); - shldsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_256f, SLHDSAParameters.shake_256f); - - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128s_with_sha256, SLHDSAParameters.sha2_128s_with_sha256); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128f_with_sha256, SLHDSAParameters.sha2_128f_with_sha256); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192s_with_sha512, SLHDSAParameters.sha2_192s_with_sha512); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192f_with_sha512, SLHDSAParameters.sha2_192f_with_sha512); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256s_with_sha512, SLHDSAParameters.sha2_256s_with_sha512); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256f_with_sha512, SLHDSAParameters.sha2_256f_with_sha512); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_128s_with_shake128, SLHDSAParameters.shake_128s_with_shake128); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_128f_with_shake128, SLHDSAParameters.shake_128f_with_shake128); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_192s_with_shake256, SLHDSAParameters.shake_192s_with_shake256); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_192f_with_shake256, SLHDSAParameters.shake_192f_with_shake256); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_256s_with_shake256, SLHDSAParameters.shake_256s_with_shake256); - shldsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_256f_with_shake256, SLHDSAParameters.shake_256f_with_shake256); + slhdsaOids.put(SLHDSAParameters.sha2_128s, NISTObjectIdentifiers.id_slh_dsa_sha2_128s); + slhdsaOids.put(SLHDSAParameters.sha2_128f, NISTObjectIdentifiers.id_slh_dsa_sha2_128f); + slhdsaOids.put(SLHDSAParameters.sha2_192s, NISTObjectIdentifiers.id_slh_dsa_sha2_192s); + slhdsaOids.put(SLHDSAParameters.sha2_192f, NISTObjectIdentifiers.id_slh_dsa_sha2_192f); + slhdsaOids.put(SLHDSAParameters.sha2_256s, NISTObjectIdentifiers.id_slh_dsa_sha2_256s); + slhdsaOids.put(SLHDSAParameters.sha2_256f, NISTObjectIdentifiers.id_slh_dsa_sha2_256f); + slhdsaOids.put(SLHDSAParameters.shake_128s, NISTObjectIdentifiers.id_slh_dsa_shake_128s); + slhdsaOids.put(SLHDSAParameters.shake_128f, NISTObjectIdentifiers.id_slh_dsa_shake_128f); + slhdsaOids.put(SLHDSAParameters.shake_192s, NISTObjectIdentifiers.id_slh_dsa_shake_192s); + slhdsaOids.put(SLHDSAParameters.shake_192f, NISTObjectIdentifiers.id_slh_dsa_shake_192f); + slhdsaOids.put(SLHDSAParameters.shake_256s, NISTObjectIdentifiers.id_slh_dsa_shake_256s); + slhdsaOids.put(SLHDSAParameters.shake_256f, NISTObjectIdentifiers.id_slh_dsa_shake_256f); + + slhdsaOids.put(SLHDSAParameters.sha2_128s_with_sha256, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128s_with_sha256); + slhdsaOids.put(SLHDSAParameters.sha2_128f_with_sha256, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128f_with_sha256); + slhdsaOids.put(SLHDSAParameters.sha2_192s_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192s_with_sha512); + slhdsaOids.put(SLHDSAParameters.sha2_192f_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192f_with_sha512); + slhdsaOids.put(SLHDSAParameters.sha2_256s_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256s_with_sha512); + slhdsaOids.put(SLHDSAParameters.sha2_256f_with_sha512, NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256f_with_sha512); + slhdsaOids.put(SLHDSAParameters.shake_128s_with_shake128, NISTObjectIdentifiers.id_hash_slh_dsa_shake_128s_with_shake128); + slhdsaOids.put(SLHDSAParameters.shake_128f_with_shake128, NISTObjectIdentifiers.id_hash_slh_dsa_shake_128f_with_shake128); + slhdsaOids.put(SLHDSAParameters.shake_192s_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_192s_with_shake256); + slhdsaOids.put(SLHDSAParameters.shake_192f_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_192f_with_shake256); + slhdsaOids.put(SLHDSAParameters.shake_256s_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_256s_with_shake256); + slhdsaOids.put(SLHDSAParameters.shake_256f_with_shake256, NISTObjectIdentifiers.id_hash_slh_dsa_shake_256f_with_shake256); + + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_128s, SLHDSAParameters.sha2_128s); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_128f, SLHDSAParameters.sha2_128f); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_192s, SLHDSAParameters.sha2_192s); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_192f, SLHDSAParameters.sha2_192f); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_256s, SLHDSAParameters.sha2_256s); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_sha2_256f, SLHDSAParameters.sha2_256f); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_128s, SLHDSAParameters.shake_128s); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_128f, SLHDSAParameters.shake_128f); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_192s, SLHDSAParameters.shake_192s); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_192f, SLHDSAParameters.shake_192f); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_256s, SLHDSAParameters.shake_256s); + slhdsaParams.put(NISTObjectIdentifiers.id_slh_dsa_shake_256f, SLHDSAParameters.shake_256f); + + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128s_with_sha256, SLHDSAParameters.sha2_128s_with_sha256); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_128f_with_sha256, SLHDSAParameters.sha2_128f_with_sha256); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192s_with_sha512, SLHDSAParameters.sha2_192s_with_sha512); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_192f_with_sha512, SLHDSAParameters.sha2_192f_with_sha512); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256s_with_sha512, SLHDSAParameters.sha2_256s_with_sha512); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_sha2_256f_with_sha512, SLHDSAParameters.sha2_256f_with_sha512); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_128s_with_shake128, SLHDSAParameters.shake_128s_with_shake128); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_128f_with_shake128, SLHDSAParameters.shake_128f_with_shake128); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_192s_with_shake256, SLHDSAParameters.shake_192s_with_shake256); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_192f_with_shake256, SLHDSAParameters.shake_192f_with_shake256); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_256s_with_shake256, SLHDSAParameters.shake_256s_with_shake256); + slhdsaParams.put(NISTObjectIdentifiers.id_hash_slh_dsa_shake_256f_with_shake256, SLHDSAParameters.shake_256f_with_shake256); sphincsPlusOids.put(SLHDSAParameters.sha2_128s, BCObjectIdentifiers.sphincsPlus_sha2_128s); sphincsPlusOids.put(SLHDSAParameters.sha2_128f, BCObjectIdentifiers.sphincsPlus_sha2_128f); @@ -442,12 +448,12 @@ class Utils static ASN1ObjectIdentifier slhdsaOidLookup(SLHDSAParameters params) { - return (ASN1ObjectIdentifier)shldsaOids.get(params); + return (ASN1ObjectIdentifier)slhdsaOids.get(params); } static SLHDSAParameters slhdsaParamsLookup(ASN1ObjectIdentifier oid) { - return (SLHDSAParameters)shldsaParams.get(oid); + return (SLHDSAParameters)slhdsaParams.get(oid); } static AlgorithmIdentifier sphincs256LookupTreeAlgID(String treeDigest) @@ -697,4 +703,81 @@ static HQCParameters hqcParamsLookup(ASN1ObjectIdentifier oid) { return (HQCParameters)hqcParams.get(oid); } + + + private static boolean isRaw(byte[] data) + { + // check well-formed first + ByteArrayInputStream bIn = new ByteArrayInputStream(data); + + int tag = bIn.read(); + int len = readLen(bIn); + if (len != bIn.available()) + { + return true; + } + + return false; + } + + static ASN1OctetString parseOctetData(byte[] data) + { + // check well-formed first + if (!isRaw(data)) + { + if (data[0] == BERTags.OCTET_STRING) + { + return ASN1OctetString.getInstance(data); + } + } + + return null; + } + + static ASN1Primitive parseData(byte[] data) + { + // check well-formed first + if (!isRaw(data)) + { + if (data[0] == (BERTags.SEQUENCE | BERTags.CONSTRUCTED)) + { + return ASN1Sequence.getInstance(data); + } + + if (data[0] == BERTags.OCTET_STRING) + { + return ASN1OctetString.getInstance(data); + } + + if ((data[0] & 0xff) == BERTags.TAGGED) + { + return ASN1OctetString.getInstance(ASN1TaggedObject.getInstance(data), false); + } + } + + return null; + } + + /** + * ASN.1 length reader. + */ + static int readLen(ByteArrayInputStream bIn) + { + int length = bIn.read(); + if (length < 0) + { + return -1; + } + if (length != (length & 0x7f)) + { + int count = length & 0x7f; + length = 0; + while (count-- != 0) + { + length = (length << 8) + bIn.read(); + } + } + + return length; + } } diff --git a/pkix/src/main/jdk1.4/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java b/pkix/src/main/jdk1.4/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java index 80e099fe11..0d67eb07a0 100644 --- a/pkix/src/main/jdk1.4/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java +++ b/pkix/src/main/jdk1.4/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java @@ -5,16 +5,32 @@ import java.security.GeneralSecurityException; import java.security.PrivateKey; import java.security.Provider; +import java.security.PublicKey; import java.security.SecureRandom; import java.security.Signature; import java.security.SignatureException; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.PSSParameterSpec; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERBitString; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.misc.MiscObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; import org.bouncycastle.asn1.pkcs.RSASSAPSSparams; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.jcajce.CompositePrivateKey; +import org.bouncycastle.jcajce.io.OutputStreamFactory; +import org.bouncycastle.jcajce.spec.CompositeAlgorithmSpec; import org.bouncycastle.jcajce.util.DefaultJcaJceHelper; import org.bouncycastle.jcajce.util.NamedJcaJceHelper; import org.bouncycastle.jcajce.util.ProviderJcaJceHelper; @@ -22,28 +38,77 @@ import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder; +import org.bouncycastle.operator.ExtendedContentSigner; import org.bouncycastle.operator.OperatorCreationException; import org.bouncycastle.operator.OperatorStreamException; import org.bouncycastle.operator.RuntimeOperatorException; +import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder; +import org.bouncycastle.util.Pack; +import org.bouncycastle.util.Strings; +import org.bouncycastle.util.io.TeeOutputStream; public class JcaContentSignerBuilder { + private static final Set isAlgIdFromPrivate = new HashSet(); + private static final DefaultSignatureAlgorithmIdentifierFinder SIGNATURE_ALGORITHM_IDENTIFIER_FINDER = new DefaultSignatureAlgorithmIdentifierFinder(); + + static + { + isAlgIdFromPrivate.add("DILITHIUM"); + isAlgIdFromPrivate.add("SPHINCS+"); + isAlgIdFromPrivate.add("SPHINCSPlus"); + isAlgIdFromPrivate.add("ML-DSA"); + isAlgIdFromPrivate.add("SLH-DSA"); + isAlgIdFromPrivate.add("HASH-ML-DSA"); + isAlgIdFromPrivate.add("HASH-SLH-DSA"); + } + + private final String signatureAlgorithm; + private final AlgorithmIdentifier signatureDigestAlgorithm; + private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper()); private SecureRandom random; - private String signatureAlgorithm; + private AlgorithmIdentifier sigAlgId; private AlgorithmParameterSpec sigAlgSpec; + /** + * Construct a basic content signer where the signature algorithm name + * tells us all we need to know. + * + * @param signatureAlgorithm the signature algorithm we perform. + */ public JcaContentSignerBuilder(String signatureAlgorithm) + { + this(signatureAlgorithm, (AlgorithmIdentifier)null); + } + + /** + * Constructor which includes the digest algorithm identifier used. + *

      + * Some PKIX operations, such as CMS signing, require the digest algorithm used for in the + * signature, this constructor allows the digest algorithm identifier to + * be explicitly specified. + *

      + * + * @param signatureAlgorithm the signature algorithm we perform. + * @param signatureDigestAlgorithmID the public key associated with our private key. + */ + public JcaContentSignerBuilder(String signatureAlgorithm, AlgorithmIdentifier signatureDigestAlgorithmID) { this.signatureAlgorithm = signatureAlgorithm; - this.sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm); - this.sigAlgSpec = null; + this.signatureDigestAlgorithm = signatureDigestAlgorithmID; } public JcaContentSignerBuilder(String signatureAlgorithm, AlgorithmParameterSpec sigParamSpec) + { + this(signatureAlgorithm, sigParamSpec, null); + } + + public JcaContentSignerBuilder(String signatureAlgorithm, AlgorithmParameterSpec sigParamSpec, AlgorithmIdentifier signatureDigestAlgorithmID) { this.signatureAlgorithm = signatureAlgorithm; + this.signatureDigestAlgorithm = signatureDigestAlgorithmID; if (sigParamSpec instanceof PSSParameterSpec) { @@ -51,12 +116,20 @@ public JcaContentSignerBuilder(String signatureAlgorithm, AlgorithmParameterSpec this.sigAlgSpec = pssSpec; this.sigAlgId = new AlgorithmIdentifier( - PKCSObjectIdentifiers.id_RSASSA_PSS, createPSSParams(signatureAlgorithm, pssSpec)); + PKCSObjectIdentifiers.id_RSASSA_PSS, createPSSParams(signatureAlgorithm, pssSpec)); + } + else if (sigParamSpec instanceof CompositeAlgorithmSpec) + { + CompositeAlgorithmSpec compSpec = (CompositeAlgorithmSpec)sigParamSpec; + + this.sigAlgSpec = compSpec; + this.sigAlgId = new AlgorithmIdentifier( + MiscObjectIdentifiers.id_alg_composite, createCompParams(compSpec)); } else { throw new IllegalArgumentException("unknown sigParamSpec: " - + ((sigParamSpec == null) ? "null" : sigParamSpec.getClass().getName())); + + ((sigParamSpec == null) ? "null" : sigParamSpec.getClass().getName())); } } @@ -86,6 +159,11 @@ public ContentSigner build(PrivateKey privateKey) { try { + if (sigAlgSpec == null) + { + this.sigAlgId = getSigAlgId(privateKey); + } + final Signature sig = helper.createSignature(sigAlgId); final AlgorithmIdentifier signatureAlgId = sigAlgId; @@ -131,6 +209,23 @@ public byte[] getSignature() } } + private AlgorithmIdentifier getSigAlgId(PrivateKey privateKey) + { + if (isAlgIdFromPrivate.contains(Strings.toUpperCase(signatureAlgorithm))) + { + AlgorithmIdentifier sigAlgId = SIGNATURE_ALGORITHM_IDENTIFIER_FINDER.find(privateKey.getAlgorithm()); + if (sigAlgId == null) + { + return PrivateKeyInfo.getInstance(privateKey.getEncoded()).getPrivateKeyAlgorithm(); + } + return sigAlgId; + } + else + { + return SIGNATURE_ALGORITHM_IDENTIFIER_FINDER.find(signatureAlgorithm); + } + } + private class SignatureOutputStream extends OutputStream { @@ -190,8 +285,8 @@ byte[] getSignature() private static RSASSAPSSparams createPSSParams(String signatureAlgorithm, PSSParameterSpec pssSpec) { DigestAlgorithmIdentifierFinder digFinder = new DefaultDigestAlgorithmIdentifierFinder(); - AlgorithmIdentifier digId = digFinder.find(signatureAlgorithm.substring(0, signatureAlgorithm.indexOf("w"))); - AlgorithmIdentifier mgfDig = digFinder.find(signatureAlgorithm.substring(0, signatureAlgorithm.indexOf("w"))); + AlgorithmIdentifier digId = digFinder.find(signatureAlgorithm.substring(0, signatureAlgorithm.indexOf("w"))); + AlgorithmIdentifier mgfDig = digFinder.find(signatureAlgorithm.substring(0, signatureAlgorithm.indexOf("w"))); return new RSASSAPSSparams( digId, @@ -199,4 +294,32 @@ private static RSASSAPSSparams createPSSParams(String signatureAlgorithm, PSSPar new ASN1Integer(pssSpec.getSaltLength()), RSASSAPSSparams.DEFAULT_TRAILER_FIELD); } + + private static ASN1Sequence createCompParams(CompositeAlgorithmSpec compSpec) + { + SignatureAlgorithmIdentifierFinder algFinder = new DefaultSignatureAlgorithmIdentifierFinder(); + ASN1EncodableVector v = new ASN1EncodableVector(); + + List algorithmNames = compSpec.getAlgorithmNames(); + List algorithmSpecs = compSpec.getParameterSpecs(); + + for (int i = 0; i != algorithmNames.size(); i++) + { + AlgorithmParameterSpec sigSpec = (AlgorithmParameterSpec)algorithmSpecs.get(i); + if (sigSpec == null) + { + v.add(algFinder.find((String)algorithmNames.get(i))); + } + else if (sigSpec instanceof PSSParameterSpec) + { + v.add(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_RSASSA_PSS, createPSSParams((String)algorithmNames.get(i), (PSSParameterSpec)sigSpec))); + } + else + { + throw new IllegalArgumentException("unrecognized parameterSpec"); + } + } + + return new DERSequence(v); + } } From f0cd3da6d30e8f9511d15c795b69c10b02a4f24b Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 2 Jun 2025 10:37:32 +0930 Subject: [PATCH 406/890] Add CONTRIBUTORS.html and releasenotes.html --- CONTRIBUTORS.html | 6 ++++-- docs/releasenotes.html | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTORS.html b/CONTRIBUTORS.html index 5224952758..9518578b82 100644 --- a/CONTRIBUTORS.html +++ b/CONTRIBUTORS.html @@ -447,14 +447,14 @@
    • Adam Vartanian <https://github.com/flooey> use of ShortBuffer exception and buffer size pre-check in Cipher.doFinal().
    • Bernd <https://github.com/ecki> Fix to make PGPUtil.pipeFileContents use buffer and not leak file handle.
    • Shartung <https://github.com/shartung> Additional EC Key Agreement algorithms in support of German BSI TR-03111.
    • -
    • Paul Schaub <https://github.com/vanitasvitae> bringing PGPSecretKey.getUserIds() into line with PGPPublicKey.getUserIds(). Exception message fix in BcPublicKeyDataDecryptorFactory. Additional tests on PGP key ring generation. Improved functionality of PGPSignatureSubpacketGenerator, PGPPublicKeyRing. Tweaks to PGPDataEncryptorBuilder interface, fix for JcaPGP/BcPGP Ed25519 private key conversion. Added configurable CRC detection to ArmoredInputStream, additional control character skipping in ArmoredInputStream. Rewind code for PGPPBEEncryptedData, addition of PGPSignature.getDigestPrefix(). Wrong list traversal fix in PGPSecretKeyRing. Further improvement to use of generics in PGP API. General interop improvements. PGP Public / Secure keyring ignore marker packets when reading. Initial work on PGP session key handling, filtering literal data for canoncialization. Addition of direct key identified key-ring construction. PGPSecretKeyRing.insertOrReplacePublicKey addition. Addition of utility methods for joining/merging signatures and public keys. Addition of PGP regexp packet, PolicyURI packet handling, UTF8 comment testing. Efficiency improvements to TruncatedStream. Initial Argon2 support for OpenPGP. General cleanups. Fast CRC24 implementation, SHA3 addtions to BcImplProvider, improvements to One Pass Signature support, signatue validation, read() consistency in BCPGInputStream. Contributions to AEAD support (v6 & v5) in PGP API. Addition of PGP WildCard ID, moving the PGP example code into the 21st century. Security patches for encrypted data generation, initial thread safe certification verification. Support for V6 EC keys, V6 signatures, V6 encryption, V6 PKESK, PGP packet criticality, and Preferred AEAD CipherSuites sigsubpacket support.
    • +
    • Paul Schaub <https://github.com/vanitasvitae> bringing PGPSecretKey.getUserIds() into line with PGPPublicKey.getUserIds(). Exception message fix in BcPublicKeyDataDecryptorFactory. Additional tests on PGP key ring generation. Improved functionality of PGPSignatureSubpacketGenerator, PGPPublicKeyRing. Tweaks to PGPDataEncryptorBuilder interface, fix for JcaPGP/BcPGP Ed25519 private key conversion. Added configurable CRC detection to ArmoredInputStream, additional control character skipping in ArmoredInputStream. Rewind code for PGPPBEEncryptedData, addition of PGPSignature.getDigestPrefix(). Wrong list traversal fix in PGPSecretKeyRing. Further improvement to use of generics in PGP API. General interop improvements. PGP Public / Secure keyring ignore marker packets when reading. Initial work on PGP session key handling, filtering literal data for canoncialization. Addition of direct key identified key-ring construction. PGPSecretKeyRing.insertOrReplacePublicKey addition. Addition of utility methods for joining/merging signatures and public keys. Addition of PGP regexp packet, PolicyURI packet handling, UTF8 comment testing. Efficiency improvements to TruncatedStream. Initial Argon2 support for OpenPGP. General cleanups. Fast CRC24 implementation, SHA3 addtions to BcImplProvider, improvements to One Pass Signature support, signatue validation, read() consistency in BCPGInputStream. Contributions to AEAD support (v6 & v5) in PGP API. Addition of PGP WildCard ID, moving the PGP example code into the 21st century. Security patches for encrypted data generation, initial thread safe certification verification. Support for V6 EC keys, V6 signatures, V6 encryption, V6 PKESK, PGP packet criticality, and Preferred AEAD CipherSuites sigsubpacket support. Introduce high-level OpenPGP API for message creation/consumption and certificate evaluation
    • Nick of Nexxar <https://github.com/nros> update to OpenPGP package to handle a broader range of EC curves.
    • catbref <https://github.com/catbref> sample implementation of RFC 7748/Ed25519 (incorporated work from github users Valodim and str4d as well).
    • gerlion <https://github.com/gerlion> detection of concurrency issue with pre-1.60 EC math library.
    • fgrieu <fgrieu@gmail.com> identification and suggested fixes for possible timing vulnerability in OAEPEncoding and RSACoreEngine.
    • MTG <https://github.com/mtgag> patch for decoding issues in PKIPublicationInfo and CertifiedKeyPair, patch for adding jurisdiction{C,ST,L} to X500 name style.
    • Andreas Gadermaier <up.gadermaier@gmail.com> initial version of Argon2 PBKDF algorithm.
    • -
    • Tony Washer <https://github.com/tonywasher> ECIESKeyEncapsulation fix for use of OldCofactor mode. Submitted ChaCha20Poly1305 prototype. Remove support for maxXofLen in Kangaroo. Police Blake3 output limit. Add LEAEngine. Review of qTesla, Java 1.9 module code, additional test code and debugging for GOST, DSTU, and ECNR algorithms. Initial lightweight implementation of the ZUC ciphers and macs. Additions to LMS/HSS API implementations, fix for truncation issue with big HSS keys, contributions to optimization of LMS/HSS. Patch for XDH/EdDSA key handling and mcEliece decryption using kobaraImai. Initial GCM-SIV, Blake3, and Kangaroo implementation. Corrections to length outputs for getUpdateOutputSize()/doFinal() in ISAP, PhotonBeetle, and Xoodyak.
    • +
    • Tony Washer <https://github.com/tonywasher> ECIESKeyEncapsulation fix for use of OldCofactor mode. Submitted ChaCha20Poly1305 prototype. Remove support for maxXofLen in Kangaroo. Police Blake3 output limit. Add LEAEngine. Review of qTesla, Java 1.9 module code, additional test code and debugging for GOST, DSTU, and ECNR algorithms. Initial lightweight implementation of the ZUC ciphers and macs. Additions to LMS/HSS API implementations, fix for truncation issue with big HSS keys, contributions to optimization of LMS/HSS. Patch for XDH/EdDSA key handling and mcEliece decryption using kobaraImai. Initial GCM-SIV, Blake3, and Kangaroo implementation. Corrections to length outputs for getUpdateOutputSize()/doFinal() in ISAP, PhotonBeetle, and Xoodyak. Fix GCFB reset. Fix Elephant multi-part process. Fix AsconXof support multi-part outputs.
    • Vincent Bouckaert <https://github.com/veebee> initial version of RFC 4998 ASN.1 classes. Debugging and testing of high level RFC 4998 implementation.
    • Aurimas Liutikas <https://github.com/liutikas> JavaDoc patches to ReasonsMask.
    • Gabriel Sroka <https://github.com/gabrielsroka> corrected comments in RSA validation.
    • @@ -559,6 +559,8 @@
    • DawidM <https://github.com/dawmit> - Implementation of EC J-PAKE.
    • Syed Quasim <https://github.com/HawkItzme> - lint checker fix for EST getTrustAllTrustManager().
    • winfriedgerlach <https://github.com/winfriedgerlach> - patch to SecretKeyUtil class.
    • +
    • feuxfollets1013 <https://github.com/feuxfollets1013> - Initial add JDK21 KEM API implementation for HQC algorithm.
    • +
    • zhsnew <https://github.com/zhsnew> - correct AsconCXof128 implementation and add test vectors
    diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 9faa90f2b9..71676cbddb 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -25,6 +25,9 @@

    2.1.2 Defects Fixed

    • A potention NullPointerException in the KEM KDF KemUtil class has been removed.
    • Overlapping input/output buffers in doFinal could result in data corruption. This has been fixed.
    • +
    • Fixed Grain-128AEAD decryption incorrectly handle MAC verification.
    • +
    • Add configurable header validation to prevent malicious header injection in PGP cleartext signed messages; Fix signature packet encoding issues in PGPSignature.join() and embedded signatures while phasing out legacy format.
    • +
    • Fixed ParallelHash initialization stall when using block size B=0

    2.1.3 Additional Features and Functionality

      @@ -39,6 +42,8 @@

      2.1.3 Additional Features and Functionality

    • The Ascon family of algorithms has been updated to the initial draft of SP 800-232. Some additional optimisation work has been done.
    • Support for ML-DSA's external-mu calculation and signing has been added to the BC provider.
    • CMS now supports ML-DSA for SignedData generation.
    • +
    • Introduce high-level OpenPGP API for message creation/consumption and certificate evaluation.
    • +
    • Add JDK21 KEM API implementation for HQC algorithm.

    2.2.1 Version

    From 94992196bd2fb5f5c5ad76fc3f8d5fee2199d8a7 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 3 Jun 2025 00:00:21 +0700 Subject: [PATCH 407/890] BCJSSE updates in release notes --- docs/releasenotes.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 71676cbddb..4eebba59d6 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -44,6 +44,10 @@

    2.1.3 Additional Features and Functionality

  • CMS now supports ML-DSA for SignedData generation.
  • Introduce high-level OpenPGP API for message creation/consumption and certificate evaluation.
  • Add JDK21 KEM API implementation for HQC algorithm.
  • +
  • BCJSSE: Strip trailing dot from hostname for SNI, endpointID checks.
  • +
  • BCJSSE: Draft support for ML-KEM updated (draft-connolly-tls-mlkem-key-agreement-05).
  • +
  • BCJSSE: Draft support for hybrid ECDHE-MLKEM (draft-ietf-tls-ecdhe-mlkem-00).
  • +
  • BCJSSE: Optionally prefer TLS 1.3 server's supported_groups order (BCSSLParameters.useNamedGroupsOrder).
  • 2.2.1 Version

    From a0eb67628299ab573aa474b7432b58b6bbe340ec Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 3 Jun 2025 13:25:14 +1000 Subject: [PATCH 408/890] updated to use TestResourceFinder --- .../test/jdk1.4/org/bouncycastle/tls/test/TlsTestUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tls/src/test/jdk1.4/org/bouncycastle/tls/test/TlsTestUtils.java b/tls/src/test/jdk1.4/org/bouncycastle/tls/test/TlsTestUtils.java index 761a06d9b0..79c3e344d0 100644 --- a/tls/src/test/jdk1.4/org/bouncycastle/tls/test/TlsTestUtils.java +++ b/tls/src/test/jdk1.4/org/bouncycastle/tls/test/TlsTestUtils.java @@ -29,6 +29,7 @@ import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters; import org.bouncycastle.crypto.util.PrivateKeyFactory; +import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.tls.AlertDescription; import org.bouncycastle.tls.Certificate; import org.bouncycastle.tls.CertificateEntry; @@ -488,7 +489,7 @@ else if (EdECObjectIdentifiers.id_Ed448.equals(oid)) static PemObject loadPemResource(String resource) throws IOException { - InputStream s = TlsTestUtils.class.getResourceAsStream(resource); + InputStream s = TestResourceFinder.findTestResource("tls/credentials", resource); PemReader p = new PemReader(new InputStreamReader(s)); PemObject o = p.readPemObject(); p.close(); From 9ccb566041c02aa4aefb150e5050a162ad7cf4e6 Mon Sep 17 00:00:00 2001 From: Gefei Li Date: Tue, 3 Jun 2025 03:27:15 +0000 Subject: [PATCH 409/890] Set message and content as protected in SMIMESignedParser --- .../java/org/bouncycastle/mail/smime/SMIMESignedParser.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMESignedParser.java b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMESignedParser.java index 964aa5c270..00172b74ac 100644 --- a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMESignedParser.java +++ b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMESignedParser.java @@ -64,8 +64,8 @@ public class SMIMESignedParser extends CMSSignedDataParser { - Object message; - MimeBodyPart content; + protected Object message; + protected MimeBodyPart content; private static File getTmpFile() throws MessagingException From 00bfeba5208295f1681d0c670523a91bb35ec130 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 3 Jun 2025 12:55:43 +0700 Subject: [PATCH 410/890] Formatting --- .../asn1/bc/BCObjectIdentifiers.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java index 8ac3beefd4..cacf37ff0b 100644 --- a/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java +++ b/core/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java @@ -259,6 +259,7 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier p521_sphincs_shake256f_simple = new ASN1ObjectIdentifier("1.3.9999.6.9.11"); /** 1.3.9999.6.9.13 OQS_OID_P521_SPHINCSSHAKE256SSIMPLE */ ASN1ObjectIdentifier p521_sphincs_shake256s_simple = new ASN1ObjectIdentifier("1.3.9999.6.9.13"); + /** * Picnic */ @@ -280,11 +281,11 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier picnicl5full = picnic_key.branch("12"); ASN1ObjectIdentifier picnic_signature = picnic.branch("2"); + ASN1ObjectIdentifier picnic_with_sha512 = picnic_signature.branch("1"); ASN1ObjectIdentifier picnic_with_shake256 = picnic_signature.branch("2"); ASN1ObjectIdentifier picnic_with_sha3_512 = picnic_signature.branch("3"); - /* * Falcon */ @@ -295,20 +296,21 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier p256_falcon_512 = new ASN1ObjectIdentifier("1.3.9999.3.12"); /** 1.3.9999.3.13 OQS_OID_RSA3072_FALCON512 */ ASN1ObjectIdentifier rsa_3072_falcon_512 = new ASN1ObjectIdentifier("1.3.9999.3.13"); + /** 1.3.9999.3.14 OQS_OID_FALCON1024 */ + ASN1ObjectIdentifier falcon_1024 = new ASN1ObjectIdentifier("1.3.9999.3.14"); + /** 1.3.9999.3.15 OQS_OID_P521_FALCON1024 */ + ASN1ObjectIdentifier p521_falcon1024 = new ASN1ObjectIdentifier("1.3.9999.3.15"); /** 1.3.9999.3.16 OQS_OID_FALCONPADDED512 */ ASN1ObjectIdentifier falcon_padded_512 = new ASN1ObjectIdentifier("1.3.9999.3.16"); /** 1.3.9999.3.17 OQS_OID_P256_FALCONPADDED512 */ ASN1ObjectIdentifier p256_falcon_padded512 = new ASN1ObjectIdentifier("1.3.9999.3.17"); /** 1.3.9999.3.18 OQS_OID_RSA3072_FALCONPADDED512 */ ASN1ObjectIdentifier rsa_3072_falconpadded512 = new ASN1ObjectIdentifier("1.3.9999.3.18"); - /** 1.3.9999.3.14 OQS_OID_FALCON1024 */ - ASN1ObjectIdentifier falcon_1024 = new ASN1ObjectIdentifier("1.3.9999.3.14"); - /** 1.3.9999.3.15 OQS_OID_P521_FALCON1024 */ - ASN1ObjectIdentifier p521_falcon1024 = new ASN1ObjectIdentifier("1.3.9999.3.15"); /** 1.3.9999.3.19 OQS_OID_FALCONPADDED1024 */ ASN1ObjectIdentifier falcon_padded_1024 = new ASN1ObjectIdentifier("1.3.9999.3.19"); /** 1.3.9999.3.20 OQS_OID_P521_FALCONPADDED1024 */ ASN1ObjectIdentifier p521_falcon_padded_1024 = new ASN1ObjectIdentifier("1.3.9999.3.20"); + /* * Dilithium */ @@ -321,6 +323,11 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier dilithium2_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.4.4"); // dilithium.branch("4"); ASN1ObjectIdentifier dilithium3_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.6.5"); // dilithium.branch("5"); ASN1ObjectIdentifier dilithium5_aes = new ASN1ObjectIdentifier("1.3.6.1.4.1.2.267.11.8.7"); // dilithium.branch("6"); + + /* + * ML-DSA + */ + ///** 2.16.840.1.101.3.4.3.17 OQS_OID_MLDSA44 */ /** 1.3.9999.7.5 OQS_OID_P256_MLDSA44 */ ASN1ObjectIdentifier p256_mldsa44 = new ASN1ObjectIdentifier("1.3.9999.7.5"); /** 1.3.9999.7.6 OQS_OID_RSA3072_MLDSA44 */ @@ -349,7 +356,6 @@ public interface BCObjectIdentifiers /** 2.16.840.1.114027.80.8.1.10 OQS_OID_MLDSA65_ed25519 */ ASN1ObjectIdentifier mldsa65_ed25519 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.10"); ///** 2.16.840.1.101.3.4.3.19 OQS_OID_MLDSA87 */ - /** 1.3.9999.7.8 OQS_OID_P521_MLDSA87 */ ASN1ObjectIdentifier p521_mldsa87 = new ASN1ObjectIdentifier("1.3.9999.7.8"); /** 2.16.840.1.114027.80.8.1.11 OQS_OID_MLDSA87_p384 */ @@ -358,6 +364,7 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier mldsa87_bp384 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.12"); /** 2.16.840.1.114027.80.8.1.13 OQS_OID_MLDSA87_ed448 */ ASN1ObjectIdentifier mldsa87_ed448 = new ASN1ObjectIdentifier("2.16.840.1.114027.80.8.1.13"); + /* * Rainbow */ @@ -411,7 +418,6 @@ public interface BCObjectIdentifiers ASN1ObjectIdentifier mceliece8192128_r3 = pqc_kem_mceliece.branch("9"); ASN1ObjectIdentifier mceliece8192128f_r3 = pqc_kem_mceliece.branch("10"); - /** * Frodo */ From 8ecc0b3281f0d563a9570e650eb78958128255ad Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 3 Jun 2025 15:56:07 +1000 Subject: [PATCH 411/890] fixed cloner to do SHA1 as SHA1. Relates to github #2041 --- .../main/java/org/bouncycastle/crypto/util/DigestFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/util/DigestFactory.java b/core/src/main/java/org/bouncycastle/crypto/util/DigestFactory.java index 685be1b1c0..be9248055f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/util/DigestFactory.java +++ b/core/src/main/java/org/bouncycastle/crypto/util/DigestFactory.java @@ -40,7 +40,7 @@ public Digest createClone(Digest original) { public Digest createClone(Digest original) { - return new MD5Digest((MD5Digest)original); + return new SHA1Digest((SHA1Digest)original); } }); cloneMap.put(createSHA224().getAlgorithmName(), new Cloner() From 9c49ab52f5ac4f0f54ad0885016a212e5c11e956 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 3 Jun 2025 16:12:06 +1000 Subject: [PATCH 412/890] final updates --- CONTRIBUTORS.html | 5 ++++- docs/releasenotes.html | 7 +++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/CONTRIBUTORS.html b/CONTRIBUTORS.html index 9518578b82..ba9ab62d06 100644 --- a/CONTRIBUTORS.html +++ b/CONTRIBUTORS.html @@ -558,9 +558,12 @@
  • Marcono1234 <https://github.com/Marcono1234> - Updates to OpenBSDBCrypt JavaDoc.
  • DawidM <https://github.com/dawmit> - Implementation of EC J-PAKE.
  • Syed Quasim <https://github.com/HawkItzme> - lint checker fix for EST getTrustAllTrustManager().
  • -
  • winfriedgerlach <https://github.com/winfriedgerlach> - patch to SecretKeyUtil class.
  • +
  • winfriedgerlach <https://github.com/winfriedgerlach> - patch to SecretKeyUtil class, patch to DigestFactory cloner for SHA-1.
  • feuxfollets1013 <https://github.com/feuxfollets1013> - Initial add JDK21 KEM API implementation for HQC algorithm.
  • +
  • cragkhit <https://github.com/cragkhit> - addition of null check in some test utility methods to avoid needless exceptions.
  • zhsnew <https://github.com/zhsnew> - correct AsconCXof128 implementation and add test vectors
  • +
  • mt-johan <https://github.com/mt-johan> - patch to preserve PRF on initializing from protectionAlgorithm with PBMAC1.
  • +
  • oscerd <https://github.com/oscerd> - comment corrections in GMSSRootSig.java.
  • diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 4eebba59d6..1b23f26112 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -27,12 +27,15 @@

    2.1.2 Defects Fixed

  • Overlapping input/output buffers in doFinal could result in data corruption. This has been fixed.
  • Fixed Grain-128AEAD decryption incorrectly handle MAC verification.
  • Add configurable header validation to prevent malicious header injection in PGP cleartext signed messages; Fix signature packet encoding issues in PGPSignature.join() and embedded signatures while phasing out legacy format.
  • -
  • Fixed ParallelHash initialization stall when using block size B=0
  • +
  • Fixed ParallelHash initialization stall when using block size B=0.
  • +
  • The PRF from the PBKDF2 function was been lost when PBMAC1 was initialized from protectionAlgorithm. This has been fixed.
  • +
  • The lowlevel DigestFactory was cloning MD5 when being asked to clone SHA1. This has been fixed.
  • 2.1.3 Additional Features and Functionality

    • XWing implementation updated to draft-connolly-cfrg-xwing-kem/07/
    • Further support has been added for generation and use of PGP V6 keys
    • +
    • Additional validation has been added for armored headers in Cleartext Signed Messages.
    • The PQC signature algorithm proposal Mayo has been added to the low-level API and the BCPQC provider.
    • The PQC signature algorithm proposal Snova has been added to the low-level API and the BCPQC provider.
    • Support for ChaCha20-Poly1305 has been added to the CMS/SMIME APIs.
    • @@ -43,7 +46,7 @@

      2.1.3 Additional Features and Functionality

    • Support for ML-DSA's external-mu calculation and signing has been added to the BC provider.
    • CMS now supports ML-DSA for SignedData generation.
    • Introduce high-level OpenPGP API for message creation/consumption and certificate evaluation.
    • -
    • Add JDK21 KEM API implementation for HQC algorithm.
    • +
    • Added JDK21 KEM API implementation for HQC algorithm.
    • BCJSSE: Strip trailing dot from hostname for SNI, endpointID checks.
    • BCJSSE: Draft support for ML-KEM updated (draft-connolly-tls-mlkem-key-agreement-05).
    • BCJSSE: Draft support for hybrid ECDHE-MLKEM (draft-ietf-tls-ecdhe-mlkem-00).
    • From db2dfa099444933007e69b1aab2fcc38b6f6508d Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 3 Jun 2025 13:32:40 +0700 Subject: [PATCH 413/890] Improve FalconTest --- .../pqc/crypto/test/FalconTest.java | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/FalconTest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/FalconTest.java index e7ab435d10..efc6335926 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/FalconTest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/FalconTest.java @@ -141,34 +141,42 @@ public void testVectors() // System.out.println("testing successful!"); } } - - public void testFalconRandom() - { - byte[] msg = Strings.toByteArray("Hello World!"); - FalconKeyPairGenerator keyGen = new FalconKeyPairGenerator(); + public void testRandom() + throws Exception + { SecureRandom random = new SecureRandom(); + byte[] msg = Strings.toByteArray("Hello World!"); + FalconKeyPairGenerator keyGen = new FalconKeyPairGenerator(); keyGen.init(new FalconKeyGenerationParameters(random, FalconParameters.falcon_512)); - for (int i = 0; i != 100; i++) + for (int i = 0; i < 10; ++i) { AsymmetricCipherKeyPair keyPair = keyGen.generateKeyPair(); - // sign - FalconSigner signer = new FalconSigner(); - FalconPrivateKeyParameters skparam = (FalconPrivateKeyParameters)keyPair.getPrivate(); - ParametersWithRandom skwrand = new ParametersWithRandom(skparam, random); - signer.init(true, skwrand); - - byte[] sigGenerated = signer.generateSignature(msg); + FalconPrivateKeyParameters privParams = (FalconPrivateKeyParameters)keyPair.getPrivate(); + FalconPublicKeyParameters pubParams = (FalconPublicKeyParameters)keyPair.getPublic(); - // verify - FalconSigner verifier = new FalconSigner(); - FalconPublicKeyParameters pkparam = (FalconPublicKeyParameters)keyPair.getPublic(); - verifier.init(false, pkparam); + privParams = (FalconPrivateKeyParameters)PrivateKeyFactory.createKey( + PrivateKeyInfoFactory.createPrivateKeyInfo(privParams)); + pubParams = (FalconPublicKeyParameters)PublicKeyFactory.createKey( + SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(pubParams)); + + for (int j = 0; j < 10; ++j) + { + // sign + FalconSigner signer = new FalconSigner(); + signer.init(true, new ParametersWithRandom(privParams, random)); + byte[] signature = signer.generateSignature(msg); + + // verify + FalconSigner verifier = new FalconSigner(); + verifier.init(false, pubParams); + boolean verified = verifier.verifySignature(msg, signature); - assertTrue("count = " + i, verifier.verifySignature(msg, sigGenerated)); + assertTrue("count = " + i, verified); + } } } } From 8915ff25523a66bfd96c8e35eeb5686f43638701 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 3 Jun 2025 16:34:49 +1000 Subject: [PATCH 414/890] fixed test method exposure --- pkix/src/test/java/org/bouncycastle/pkcs/test/PBETest.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkix/src/test/java/org/bouncycastle/pkcs/test/PBETest.java b/pkix/src/test/java/org/bouncycastle/pkcs/test/PBETest.java index 817ae9bd0a..9c23affedf 100644 --- a/pkix/src/test/java/org/bouncycastle/pkcs/test/PBETest.java +++ b/pkix/src/test/java/org/bouncycastle/pkcs/test/PBETest.java @@ -3,7 +3,6 @@ import java.security.Security; import junit.framework.TestCase; - import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PBKDF2Params; import org.bouncycastle.asn1.pkcs.PBMAC1Params; @@ -38,7 +37,7 @@ public void testPBESHA256() } - void testPbmac1PrfPropagation() throws OperatorCreationException { + public void testPbmac1PrfPropagation() throws OperatorCreationException { AlgorithmIdentifier prf = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_512, null);; AlgorithmIdentifier protectionAlgorithm = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_PBMAC1, new PBMAC1Params( @@ -51,7 +50,7 @@ void testPbmac1PrfPropagation() throws OperatorCreationException { AlgorithmIdentifier actualPrf = PBKDF2Params.getInstance( PBMAC1Params.getInstance(calculator.getKey().getAlgorithmIdentifier().getParameters()).getKeyDerivationFunc().getParameters() ).getPrf(); - System.out.println("Should be true: " + prf.equals(actualPrf)); + assertTrue(prf.equals(actualPrf)); } } From 48fc2694cefc53af24210e3274edf0055f6c463a Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 3 Jun 2025 17:19:38 +1000 Subject: [PATCH 415/890] removed old RFC kyber ASN.1 class. --- .../pqc/asn1/KyberPrivateKey.java | 129 ------------------ .../bouncycastle/pqc/asn1/KyberPublicKey.java | 77 ----------- .../pqc/crypto/util/PublicKeyFactory.java | 13 +- .../pqc/crypto/util/PublicKeyFactory.java | 15 +- .../pqc/crypto/util/PublicKeyFactory.java | 15 +- 5 files changed, 5 insertions(+), 244 deletions(-) delete mode 100644 core/src/main/java/org/bouncycastle/pqc/asn1/KyberPrivateKey.java delete mode 100644 core/src/main/java/org/bouncycastle/pqc/asn1/KyberPublicKey.java diff --git a/core/src/main/java/org/bouncycastle/pqc/asn1/KyberPrivateKey.java b/core/src/main/java/org/bouncycastle/pqc/asn1/KyberPrivateKey.java deleted file mode 100644 index cbdf006753..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/asn1/KyberPrivateKey.java +++ /dev/null @@ -1,129 +0,0 @@ -package org.bouncycastle.pqc.asn1; - -import org.bouncycastle.asn1.ASN1EncodableVector; -import org.bouncycastle.asn1.ASN1Integer; -import org.bouncycastle.asn1.ASN1Object; -import org.bouncycastle.asn1.ASN1OctetString; -import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.ASN1Sequence; -import org.bouncycastle.asn1.DEROctetString; -import org.bouncycastle.asn1.DERSequence; -import org.bouncycastle.util.Arrays; - -/** - * - * Crystal Kyber Private Key Format. - * See https://www.ietf.org/archive/id/draft-uni-qsckeys-kyber-01.html for details. - *
      - *        KyberPrivateKey ::= SEQUENCE {
      - *            version     INTEGER {v0(0)}   -- version (round 3)
      - *            s           OCTET STRING,     -- sample s
      - *            publicKey   [0] IMPLICIT KyberPublicKey OPTIONAL,
      - *                                          -- see next section
      - *            hpk         OCTET STRING      -- H(pk)
      - *            nonce       OCTET STRING,     -- z
      - *        }
      - *    
      - */ -public class KyberPrivateKey - extends ASN1Object -{ - private int version; - private byte[] s; - private KyberPublicKey publicKey; - private byte[] hpk; - private byte[] nonce; - - public KyberPrivateKey(int version, byte[] s, byte[] hpk, byte[] nonce, KyberPublicKey publicKey) - { - this.version = version; - this.s = s; - this.publicKey = publicKey; - this.hpk = hpk; - this.nonce = nonce; - } - - public KyberPrivateKey(int version, byte[] s, byte[] hpk, byte[] nonce) - { - this(version, s, hpk, nonce, null); - } - - public int getVersion() - { - return version; - } - - public byte[] getS() - { - return Arrays.clone(s); - } - - public KyberPublicKey getPublicKey() - { - return publicKey; - } - - public byte[] getHpk() - { - return Arrays.clone(hpk); - } - - public byte[] getNonce() - { - return Arrays.clone(nonce); - } - - private KyberPrivateKey(ASN1Sequence seq) - { - version = ASN1Integer.getInstance(seq.getObjectAt(0)).intValueExact(); - if (version != 0) - { - throw new IllegalArgumentException("unrecognized version"); - } - - s = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets()); - - int skipPubKey = 1; - if (seq.size() == 5) - { - skipPubKey = 0; - publicKey = KyberPublicKey.getInstance(seq.getObjectAt(2)); - } - - hpk = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(3 - skipPubKey)).getOctets()); - - nonce = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(4 - skipPubKey)).getOctets()); - } - - public ASN1Primitive toASN1Primitive() - { - ASN1EncodableVector v = new ASN1EncodableVector(); - - v.add(new ASN1Integer(version)); - v.add(new DEROctetString(s)); - // todo optional publickey - if(publicKey != null) - { - v.add(new KyberPublicKey(publicKey.getT(), publicKey.getRho())); - } - v.add(new DEROctetString(hpk)); - v.add(new DEROctetString(nonce)); - - return new DERSequence(v); - } - - public static KyberPrivateKey getInstance(Object o) - { - if (o instanceof KyberPrivateKey) - { - return (KyberPrivateKey)o; - } - else if (o != null) - { - return new KyberPrivateKey(ASN1Sequence.getInstance(o)); - } - - return null; - } - -} diff --git a/core/src/main/java/org/bouncycastle/pqc/asn1/KyberPublicKey.java b/core/src/main/java/org/bouncycastle/pqc/asn1/KyberPublicKey.java deleted file mode 100644 index a498e7857b..0000000000 --- a/core/src/main/java/org/bouncycastle/pqc/asn1/KyberPublicKey.java +++ /dev/null @@ -1,77 +0,0 @@ -package org.bouncycastle.pqc.asn1; - -import org.bouncycastle.asn1.ASN1EncodableVector; -import org.bouncycastle.asn1.ASN1Object; -import org.bouncycastle.asn1.ASN1OctetString; -import org.bouncycastle.asn1.ASN1Primitive; -import org.bouncycastle.asn1.ASN1Sequence; -import org.bouncycastle.asn1.DEROctetString; -import org.bouncycastle.asn1.DERSequence; -import org.bouncycastle.util.Arrays; - -/** - * - * Crystal Kyber Public Key Format. - * See https://www.ietf.org/archive/id/draft-uni-qsckeys-kyber-01.html for details. - *
      - *        KyberPublicKey ::= SEQUENCE {
      - *         t           OCTET STRING,
      - *         rho         OCTET STRING
      -*     }
      - *    
      - */ -public class KyberPublicKey - extends ASN1Object - -{ - private byte[] t; - private byte[] rho; - - public KyberPublicKey(byte[] t, byte[] rho) - { - this.t = t; - this.rho = rho; - } - - /** - * @deprecated use getInstance() - */ - public KyberPublicKey(ASN1Sequence seq) - { - t = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets()); - rho = Arrays.clone(ASN1OctetString.getInstance(seq.getObjectAt(1)).getOctets()); - } - - public byte[] getT() - { - return Arrays.clone(t); - } - - public byte[] getRho() - { - return Arrays.clone(rho); - } - - - - public ASN1Primitive toASN1Primitive() - { - ASN1EncodableVector v = new ASN1EncodableVector(); - v.add(new DEROctetString(t)); - v.add(new DEROctetString(rho)); - return new DERSequence(v); - } - public static KyberPublicKey getInstance(Object o) - { - if (o instanceof KyberPublicKey) - { - return (KyberPublicKey) o; - } - else if (o != null) - { - return new KyberPublicKey(ASN1Sequence.getInstance(o)); - } - - return null; - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java index 529a67fa83..db2e8d0a34 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java @@ -19,7 +19,6 @@ import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.internal.asn1.isara.IsaraObjectIdentifiers; import org.bouncycastle.pqc.asn1.CMCEPublicKey; -import org.bouncycastle.pqc.asn1.KyberPublicKey; import org.bouncycastle.pqc.asn1.McElieceCCA2PublicKey; import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers; import org.bouncycastle.pqc.asn1.SPHINCS256KeyParams; @@ -664,18 +663,8 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje { MLKEMParameters parameters = Utils.mlkemParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); - try - { - ASN1Primitive obj = keyInfo.parsePublicKey(); - KyberPublicKey kyberKey = KyberPublicKey.getInstance(obj); - - return new MLKEMPublicKeyParameters(parameters, kyberKey.getT(), kyberKey.getRho()); - } - catch (Exception e) - { // we're a raw encoding - return new MLKEMPublicKeyParameters(parameters, keyInfo.getPublicKeyData().getOctets()); - } + return new MLKEMPublicKeyParameters(parameters, keyInfo.getPublicKeyData().getOctets()); } static MLKEMPublicKeyParameters getPublicKeyParams(MLKEMParameters parameters, ASN1BitString publicKeyData) diff --git a/core/src/main/jdk1.1/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java b/core/src/main/jdk1.1/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java index 1329caa6d4..45ea1332a1 100644 --- a/core/src/main/jdk1.1/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java +++ b/core/src/main/jdk1.1/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java @@ -19,7 +19,6 @@ import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.internal.asn1.isara.IsaraObjectIdentifiers; import org.bouncycastle.pqc.asn1.CMCEPublicKey; -import org.bouncycastle.pqc.asn1.KyberPublicKey; import org.bouncycastle.pqc.asn1.McElieceCCA2PublicKey; import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers; import org.bouncycastle.pqc.asn1.SPHINCS256KeyParams; @@ -375,18 +374,8 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje { MLKEMParameters kyberParameters = Utils.mlkemParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); - try - { - ASN1Primitive obj = keyInfo.parsePublicKey(); - KyberPublicKey kyberKey = KyberPublicKey.getInstance(obj); - - return new MLKEMPublicKeyParameters(kyberParameters, kyberKey.getT(), kyberKey.getRho()); - } - catch (Exception e) - { - // we're a raw encoding - return new MLKEMPublicKeyParameters(kyberParameters, keyInfo.getPublicKeyData().getOctets()); - } + // we're a raw encoding + return new MLKEMPublicKeyParameters(kyberParameters, keyInfo.getPublicKeyData().getOctets()); } } diff --git a/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java b/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java index 55a0ddc3d2..1e38e6e7e8 100644 --- a/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java +++ b/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java @@ -19,7 +19,6 @@ import org.bouncycastle.crypto.params.AsymmetricKeyParameter; import org.bouncycastle.internal.asn1.isara.IsaraObjectIdentifiers; import org.bouncycastle.pqc.asn1.CMCEPublicKey; -import org.bouncycastle.pqc.asn1.KyberPublicKey; import org.bouncycastle.pqc.asn1.McElieceCCA2PublicKey; import org.bouncycastle.pqc.asn1.PQCObjectIdentifiers; import org.bouncycastle.pqc.asn1.SPHINCS256KeyParams; @@ -480,18 +479,8 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje { MLKEMParameters parameters = Utils.mlkemParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); - try - { - ASN1Primitive obj = keyInfo.parsePublicKey(); - KyberPublicKey kyberKey = KyberPublicKey.getInstance(obj); - - return new MLKEMPublicKeyParameters(parameters, kyberKey.getT(), kyberKey.getRho()); - } - catch (Exception e) - { - // we're a raw encoding - return new MLKEMPublicKeyParameters(parameters, keyInfo.getPublicKeyData().getOctets()); - } + // we're a raw encoding + return new MLKEMPublicKeyParameters(parameters, keyInfo.getPublicKeyData().getOctets()); } static MLKEMPublicKeyParameters getPublicKeyParams(MLKEMParameters parameters, ASN1BitString publicKeyData) From 228211ecb973fe87fdd0fc4ab16ba0446ec1a29c Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 3 Jun 2025 18:59:34 +1000 Subject: [PATCH 416/890] removed old RFC kyber ASN.1 class. --- .../pqc/crypto/util/PrivateKeyFactory.java | 2 +- .../pqc/crypto/util/PublicKeyFactory.java | 37 ++++--------------- 2 files changed, 8 insertions(+), 31 deletions(-) diff --git a/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java b/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java index b1ac6575af..a08b5b0b0e 100644 --- a/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java +++ b/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PrivateKeyFactory.java @@ -200,7 +200,7 @@ else if (algOID.equals(NISTObjectIdentifiers.id_alg_ml_kem_512) || MLKEMPublicKeyParameters pubParams = null; if (keyInfo.getPublicKeyData() != null) { - pubParams = PublicKeyFactory.MLKEMConverter.getPublicKeyParams(mlkemParams, keyInfo.getPublicKeyData()); + pubParams = PublicKeyFactory.MLKEMKeyConverter.getPublicKeyParams(mlkemParams, keyInfo.getPublicKeyData()); } if (mlkemKey instanceof ASN1OctetString) diff --git a/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java b/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java index 1e38e6e7e8..566b086915 100644 --- a/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java +++ b/core/src/main/jdk1.4/org/bouncycastle/pqc/crypto/util/PublicKeyFactory.java @@ -166,12 +166,12 @@ public class PublicKeyFactory converters.put(BCObjectIdentifiers.ntruhrss1373, new NtruConverter()); converters.put(BCObjectIdentifiers.falcon_512, new FalconConverter()); converters.put(BCObjectIdentifiers.falcon_1024, new FalconConverter()); - converters.put(NISTObjectIdentifiers.id_alg_ml_kem_512, new KyberConverter()); - converters.put(NISTObjectIdentifiers.id_alg_ml_kem_768, new KyberConverter()); - converters.put(NISTObjectIdentifiers.id_alg_ml_kem_1024, new KyberConverter()); - converters.put(BCObjectIdentifiers.kyber512_aes, new KyberConverter()); - converters.put(BCObjectIdentifiers.kyber768_aes, new KyberConverter()); - converters.put(BCObjectIdentifiers.kyber1024_aes, new KyberConverter()); + converters.put(NISTObjectIdentifiers.id_alg_ml_kem_512, new MLKEMKeyConverter()); + converters.put(NISTObjectIdentifiers.id_alg_ml_kem_768, new MLKEMKeyConverter()); + converters.put(NISTObjectIdentifiers.id_alg_ml_kem_1024, new MLKEMKeyConverter()); + converters.put(BCObjectIdentifiers.kyber512_aes, new MLKEMKeyConverter()); + converters.put(BCObjectIdentifiers.kyber768_aes, new MLKEMKeyConverter()); + converters.put(BCObjectIdentifiers.kyber1024_aes, new MLKEMKeyConverter()); converters.put(BCObjectIdentifiers.ntrulpr653, new NTRULPrimeConverter()); converters.put(BCObjectIdentifiers.ntrulpr761, new NTRULPrimeConverter()); converters.put(BCObjectIdentifiers.ntrulpr857, new NTRULPrimeConverter()); @@ -471,7 +471,7 @@ AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Obje } } - static class MLKEMConverter + static class MLKEMKeyConverter extends SubjectPublicKeyInfoConverter { AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) @@ -510,29 +510,6 @@ static MLKEMPublicKeyParameters getPublicKeyParams(MLKEMParameters parameters, A } } } - - private static class KyberConverter - extends SubjectPublicKeyInfoConverter - { - AsymmetricKeyParameter getPublicKeyParameters(SubjectPublicKeyInfo keyInfo, Object defaultParams) - throws IOException - { - MLKEMParameters kyberParameters = Utils.mlkemParamsLookup(keyInfo.getAlgorithm().getAlgorithm()); - - try - { - ASN1Primitive obj = keyInfo.parsePublicKey(); - KyberPublicKey kyberKey = KyberPublicKey.getInstance(obj); - - return new MLKEMPublicKeyParameters(kyberParameters, kyberKey.getT(), kyberKey.getRho()); - } - catch (Exception e) - { - // we're a raw encoding - return new MLKEMPublicKeyParameters(kyberParameters, keyInfo.getPublicKeyData().getOctets()); - } - } - } private static class NTRULPrimeConverter extends SubjectPublicKeyInfoConverter From 5465005ad4a1e4cb41d33f5978afc4712769514d Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 10 Jun 2025 16:46:02 +0700 Subject: [PATCH 417/890] Update SRTPProtectionProfile --- .../tls/SRTPProtectionProfile.java | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tls/src/main/java/org/bouncycastle/tls/SRTPProtectionProfile.java b/tls/src/main/java/org/bouncycastle/tls/SRTPProtectionProfile.java index da20248824..1b8bc019b0 100644 --- a/tls/src/main/java/org/bouncycastle/tls/SRTPProtectionProfile.java +++ b/tls/src/main/java/org/bouncycastle/tls/SRTPProtectionProfile.java @@ -7,6 +7,16 @@ public class SRTPProtectionProfile */ public static final int SRTP_AES128_CM_HMAC_SHA1_80 = 0x0001; public static final int SRTP_AES128_CM_HMAC_SHA1_32 = 0x0002; + + /** + * Removed by draft-ietf-avt-dtls-srtp-04. IANA: Unassigned. + */ + public static final int DRAFT_SRTP_AES256_CM_SHA1_80 = 0x0003; + /** + * Removed by draft-ietf-avt-dtls-srtp-04. IANA: Unassigned. + */ + public static final int DRAFT_SRTP_AES256_CM_SHA1_32 = 0x0004; + public static final int SRTP_NULL_HMAC_SHA1_80 = 0x0005; public static final int SRTP_NULL_HMAC_SHA1_32 = 0x0006; @@ -15,4 +25,20 @@ public class SRTPProtectionProfile */ public static final int SRTP_AEAD_AES_128_GCM = 0x0007; public static final int SRTP_AEAD_AES_256_GCM = 0x0008; + + /* + * RFC 8723 10.1. + */ + public static final int DOUBLE_AEAD_AES_128_GCM_AEAD_AES_128_GCM = 0x0009; + public static final int DOUBLE_AEAD_AES_256_GCM_AEAD_AES_256_GCM = 0x000A; + + /* + * RFC 8269 6.1. + */ + public static final int SRTP_ARIA_128_CTR_HMAC_SHA1_80 = 0x000B; + public static final int SRTP_ARIA_128_CTR_HMAC_SHA1_32 = 0x000C; + public static final int SRTP_ARIA_256_CTR_HMAC_SHA1_80 = 0x000D; + public static final int SRTP_ARIA_256_CTR_HMAC_SHA1_32 = 0x000E; + public static final int SRTP_AEAD_ARIA_128_GCM = 0x000F; + public static final int SRTP_AEAD_ARIA_256_GCM = 0x0010; } From d24bd7657abd34b84ba5889d5baa34b577d673f8 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 10 Jun 2025 23:39:38 +0700 Subject: [PATCH 418/890] Refactor some signer init methods --- .../crypto/signers/ECGOST3410Signer.java | 21 ++++++++--------- .../crypto/signers/ECNRSigner.java | 23 ++++++++----------- .../crypto/signers/GOST3410Signer.java | 21 ++++++++--------- .../crypto/signers/ISO9796d2PSSSigner.java | 17 +++++--------- 4 files changed, 34 insertions(+), 48 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java b/core/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java index e064308a1a..2e3bea76eb 100644 --- a/core/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java +++ b/core/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java @@ -29,28 +29,25 @@ public class ECGOST3410Signer SecureRandom random; - public void init( - boolean forSigning, - CipherParameters param) + public void init(boolean forSigning, CipherParameters param) { if (forSigning) { + SecureRandom providedRandom = null; if (param instanceof ParametersWithRandom) { - ParametersWithRandom rParam = (ParametersWithRandom)param; - - this.random = rParam.getRandom(); - this.key = (ECPrivateKeyParameters)rParam.getParameters(); - } - else - { - this.random = CryptoServicesRegistrar.getSecureRandom(); - this.key = (ECPrivateKeyParameters)param; + ParametersWithRandom withRandom = (ParametersWithRandom)param; + providedRandom = withRandom.getRandom(); + param = withRandom.getParameters(); } + + this.key = (ECPrivateKeyParameters)param; + this.random = CryptoServicesRegistrar.getSecureRandom(providedRandom); } else { this.key = (ECPublicKeyParameters)param; + this.random = null; } CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("ECGOST3410", key, forSigning)); diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java index 8523abcdf2..f7b601d13e 100644 --- a/core/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java +++ b/core/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java @@ -37,30 +37,27 @@ public class ECNRSigner * for verification or if we want to use the signer for message recovery. * @param param key parameters for signature generation. */ - public void init( - boolean forSigning, - CipherParameters param) + public void init(boolean forSigning, CipherParameters param) { this.forSigning = forSigning; - + if (forSigning) { + SecureRandom providedRandom = null; if (param instanceof ParametersWithRandom) { - ParametersWithRandom rParam = (ParametersWithRandom)param; - - this.random = rParam.getRandom(); - this.key = (ECPrivateKeyParameters)rParam.getParameters(); - } - else - { - this.random = CryptoServicesRegistrar.getSecureRandom(); - this.key = (ECPrivateKeyParameters)param; + ParametersWithRandom withRandom = (ParametersWithRandom)param; + providedRandom = withRandom.getRandom(); + param = withRandom.getParameters(); } + + this.key = (ECPrivateKeyParameters)param; + this.random = CryptoServicesRegistrar.getSecureRandom(providedRandom); } else { this.key = (ECPublicKeyParameters)param; + this.random = null; } CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("ECNR", key, forSigning)); diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java b/core/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java index 5b187ea7df..e5106c17c0 100644 --- a/core/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java +++ b/core/src/main/java/org/bouncycastle/crypto/signers/GOST3410Signer.java @@ -24,28 +24,25 @@ public class GOST3410Signer SecureRandom random; - public void init( - boolean forSigning, - CipherParameters param) + public void init(boolean forSigning, CipherParameters param) { if (forSigning) { + SecureRandom providedRandom = null; if (param instanceof ParametersWithRandom) { - ParametersWithRandom rParam = (ParametersWithRandom)param; - - this.random = rParam.getRandom(); - this.key = (GOST3410PrivateKeyParameters)rParam.getParameters(); - } - else - { - this.random = CryptoServicesRegistrar.getSecureRandom(); - this.key = (GOST3410PrivateKeyParameters)param; + ParametersWithRandom withRandom = (ParametersWithRandom)param; + providedRandom = withRandom.getRandom(); + param = withRandom.getParameters(); } + + this.key = (GOST3410PrivateKeyParameters)param; + this.random = CryptoServicesRegistrar.getSecureRandom(providedRandom); } else { this.key = (GOST3410PublicKeyParameters)param; + this.random = null; } CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("GOST3410", key, forSigning)); diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java b/core/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java index 2ee1808861..3b9cb97abe 100644 --- a/core/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java +++ b/core/src/main/java/org/bouncycastle/crypto/signers/ISO9796d2PSSSigner.java @@ -125,9 +125,7 @@ public ISO9796d2PSSSigner( * @throws IllegalArgumentException if wrong parameter type or a fixed * salt is passed in which is the wrong length. */ - public void init( - boolean forSigning, - CipherParameters param) + public void init(boolean forSigning, CipherParameters param) { RSAKeyParameters kParam; int lengthOfSalt = saltLength; @@ -137,16 +135,15 @@ public void init( ParametersWithRandom p = (ParametersWithRandom)param; kParam = (RSAKeyParameters)p.getParameters(); - if (forSigning) - { - random = p.getRandom(); - } + random = forSigning ? p.getRandom() : null; + standardSalt = null; } else if (param instanceof ParametersWithSalt) { ParametersWithSalt p = (ParametersWithSalt)param; kParam = (RSAKeyParameters)p.getParameters(); + random = null; standardSalt = p.getSalt(); lengthOfSalt = standardSalt.length; if (standardSalt.length != saltLength) @@ -157,10 +154,8 @@ else if (param instanceof ParametersWithSalt) else { kParam = (RSAKeyParameters)param; - if (forSigning) - { - random = CryptoServicesRegistrar.getSecureRandom(); - } + random = forSigning ? CryptoServicesRegistrar.getSecureRandom() : null; + standardSalt = null; } cipher.init(forSigning, kParam); From 0a1759c2f2da8c400795a72fe079b6710703d523 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 15 Jun 2025 16:59:12 +1000 Subject: [PATCH 419/890] corrected module-info for MAYO and SNOVA --- docs/releasenotes.html | 591 +++++++++++++------------- prov/src/main/jdk1.9/module-info.java | 4 + 2 files changed, 305 insertions(+), 290 deletions(-) diff --git a/docs/releasenotes.html b/docs/releasenotes.html index 1b23f26112..df6dcc7ddb 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -18,10 +18,21 @@

      1.0 Introduction

      2.0 Release History

      -

      2.1.1 Version

      +

      2.1.1 Version

      +Release: 1.82
      +Date:      TBD. +

      2.1.2 Defects Fixed

      +
        +
      • SNOVA and MAYO are now correctly added to the JCA provider module-info file.
      • +
      +

      2.1.3 Additional Features and Functionality

      +
        +
      + +

      2.2.1 Version

      Release: 1.81
      Date:      2025, 4th June. -

      2.1.2 Defects Fixed

      +

      2.2.2 Defects Fixed

      • A potention NullPointerException in the KEM KDF KemUtil class has been removed.
      • Overlapping input/output buffers in doFinal could result in data corruption. This has been fixed.
      • @@ -31,7 +42,7 @@

        2.1.2 Defects Fixed

      • The PRF from the PBKDF2 function was been lost when PBMAC1 was initialized from protectionAlgorithm. This has been fixed.
      • The lowlevel DigestFactory was cloning MD5 when being asked to clone SHA1. This has been fixed.
      -

      2.1.3 Additional Features and Functionality

      +

      2.2.3 Additional Features and Functionality

      • XWing implementation updated to draft-connolly-cfrg-xwing-kem/07/
      • Further support has been added for generation and use of PGP V6 keys
      • @@ -53,10 +64,10 @@

        2.1.3 Additional Features and Functionality

      • BCJSSE: Optionally prefer TLS 1.3 server's supported_groups order (BCSSLParameters.useNamedGroupsOrder).
      -

      2.2.1 Version

      +

      2.3.1 Version

      Release: 1.80
      Date:      2025, 14th January. -

      2.2.2 Defects Fixed

      +

      2.3.2 Defects Fixed

      • A splitting issue for ML-KEM lead to an incorrect size for kemct in KEMRecipientInfos. This has been fixed.
      • The PKCS12 KeyStore has been adjusted to prevent accidental doubling of the Oracle trusted certificate attribute (results in an IOException when used with the JVM PKCS12 implementation).
      • @@ -71,7 +82,7 @@

        2.2.2 Defects Fixed

      • EtsiTs1029411TypesAuthorization was missing an extension field. This has been added.
      • Interoperability issues with single depth LMS keys have been addressed.
      -

      2.2.3 Additional Features and Functionality

      +

      2.3.3 Additional Features and Functionality

      • CompositeSignatures now updated to draft-ietf-lamps-pq-composite-sigs-03.
      • ML-KEM, ML-DSA, SLH-DSA, and Composite private keys now use raw encodings as per the latest drafts from IETF 121: draft-ietf-lamps-kyber-certificates-06, draft-ietf-lamps-dilithium-certificates-05, and draft-ietf-lamps-x509-slhdsa.
      • @@ -89,10 +100,10 @@

        2.2.3 Additional Features and Functionality

      • The ASCON family of algorithms have been updated in accordance with the published FIPS SP 800-232 draft.
      -

      2.3.1 Version

      +

      2.4.1 Version

      Release: 1.79
      Date:      2024, 30th October. -

      2.3.2 Defects Fixed

      +

      2.4.2 Defects Fixed

      • Leading zeroes were sometimes dropped from Ed25519 signatures leading to verification errors in the PGP API. This has been fixed.
      • Default version string for Armored Output is now set correctly in 18on build.
      • @@ -109,7 +120,7 @@

        2.3.2 Defects Fixed

      • The default version header for PGP armored output did not carry the correct version string. This has been fixed.
      • In some situations the algorithm lookup for creating PGPDigestCalculators would fail due to truncation of the algorithm name. This has been fixed.
      -

      2.3.3 Additional Features and Functionality

      +

      2.4.3 Additional Features and Functionality

      • Object Identifiers have been added for ML-KEM, ML-DSA, and SLH-DSA.
      • The PQC algorithms, ML-KEM, ML-DSA (including pre-hash), and SLH-DSA (including pre-hash) have been added to the BC provider and the lightweight API.
      • @@ -130,10 +141,10 @@

        2.3.3 Additional Features and Functionality

      • The system property "org.bouncycastle.ec.disable_f2m" has been introduced to allow F2m EC support to be disabled.
      -

      2.4.1 Version

      +

      2.5.1 Version

      Release: 1.78.1
      Date:      2024, 18th April. -

      2.4.2 Defects Fixed

      +

      2.5.2 Defects Fixed

      • The new dependency of the the PGP API on the bcutil jar was missing from the module jar, the OSGi manifest, and the Maven POM. This has been fixed.
      • Missing exports and duplicate imports have been added/removed from the OSGi manifests.
      • @@ -141,10 +152,10 @@

        2.4.2 Defects Fixed

      • A check in the X.509 Extensions class preventing the parsing of empty extensions has been removed.
      -

      2.5.1 Version

      +

      2.6.1 Version

      Release: 1.78
      Date:      2024, 7th April. -

      2.5.2 Defects Fixed

      +

      2.6.2 Defects Fixed

      • Issues with a dangling weak reference causing intermittent NullPointerExceptions in the OcspCache have been fixed.
      • Issues with non-constant time RSA operations in TLS handshakes have been fixed.
      • @@ -162,7 +173,7 @@

        2.5.2 Defects Fixed

      • An off-by-one error in the encoding for EccP256CurvePoint for ITS has been fixed.
      • PEM Parser now enforces PEM headers to start at the beginning of the line to be meaningful.
      -

      2.5.3 Additional Features and Functionality

      +

      2.6.3 Additional Features and Functionality

      • An implementation of MLS (RFC 9420 - The Messaging Layer Security Protocol) has been added as a new module.
      • NTRU now supports NTRU-HPS4096-1229 and NTRU-HRSS-1373.
      • @@ -180,7 +191,7 @@

        2.5.3 Additional Features and Functionality

      • CertPathValidationContext and CertificatePoliciesValidation now include implementations of Memoable.
      • The Composite post-quantum signatures implementation has been updated to the latest draft draft-ounsworth-pq-composite-sigs.
      -

      2.5.4 Notes.

      +

      2.6.4 Notes.

      • Both versions of NTRUPrime have been updated to produce 256 bit secrets in line with Kyber. This should also bring them into line with other implementations such as those used in OpenSSH now.
      • BCJSSE: The boolean system property 'org.bouncycastle.jsse.fips.allowRSAKeyExchange" now defaults to false. All RSA @@ -191,7 +202,7 @@

        2.5.4 Notes.

      • The PKCS12 store using GCM does not include the PKCS#12 MAC so no longer includes use of the PKCS#12 PBE scheme and only uses PBKDF2.
      • In keeping with the current set of experimental OIDs for PQC algorithms, OIDs may have changed to reflect updated versions of the algorithms.
      -

      2.5.5 Security Advisories.

      +

      2.6.5 Security Advisories.

      Release 1.78 deals with the following CVEs:

      @@ -202,10 +213,10 @@

      2.5.5 Security Advisories.

    • CVE-2024-34447 - When endpoint identification is enabled in the BCJSSE and an SSL socket is not created with an explicit hostname (as happens with HttpsURLConnection), hostname verification could be performed against a DNS-resolved IP address. This has been fixed.
    -

    2.6.1 Version

    +

    2.7.1 Version

    Release: 1.77
    Date:      2023, November 13th -

    2.6.2 Defects Fixed

    +

    2.7.2 Defects Fixed

    • Using an unescaped '=' in an X.500 RDN would result in the RDN being truncated silently. The issue is now detected and an exception is thrown.
    • asn1.eac.CertificateBody was returning certificateEffectiveDate from getCertificateExpirationDate(). This has been fixed to return certificateExpirationDate.
    • @@ -221,7 +232,7 @@

      2.6.2 Defects Fixed

    • An internal method in Arrays was failing to construct its failure message correctly on an error. This has been fixed.
    • HSSKeyPublicParameters.generateLMSContext() would fail for a unit depth key. This has been fixed.
    -

    2.6.3 Additional Features and Functionality

    +

    2.7.3 Additional Features and Functionality

    • BCJSSE: Added org.bouncycastle.jsse.client.omitSigAlgsCertExtension and org.bouncycastle.jsse.server.omitSigAlgsCertExtension boolean system properties to control (for client and server resp.) whether the signature_algorithms_cert extension should be omitted if it would be identical to signature_algorithms. @@ -233,7 +244,7 @@

      2.6.3 Additional Features and Functionality

    • TLS: RSA key exchange cipher suites are now disabled by default.
    • Support has been added for PKCS#10 requests to allow certificates using the altSignature/altPublicKey extensions.
    -

    2.6.4 Notes.

    +

    2.7.4 Notes.

    • Kyber and Dilithium have been updated according to the latest draft of the standard. Dilithium-AES and Kyber-AES have now been removed. Kyber now produces 256 bit secrets for all parameter sets (in line with the draft standard).
    • NTRU has been updated to produce 256 bit secrets in line with Kyber.
    • @@ -242,10 +253,10 @@

      2.6.4 Notes.

    • PQC CMS SignedData now defaults to SHA-256 for signed attributes rather than SHAKE-256. This is also a compatibility change, but may change further again as the IETF standard for CMS is updated.
    -

    2.7.1 Version

    +

    2.8.1 Version

    Release: 1.76
    Date:      2023, July 29th -

    2.7.2 Defects Fixed

    +

    2.8.2 Defects Fixed

    • Service allocation in the provider could fail due to the lack of a permission block. This has been fixed.
    • JceKeyFingerPrintCalculator has been generalised for different providers by using "SHA-256" for the algorithm string.
    • @@ -254,7 +265,7 @@

      2.7.2 Defects Fixed

    • Cipher.unwrap() for HQC could fail due to a miscalculation of the length of the KEM packet. This has been fixed.
    • There was exposure to a Java 7 method in the Java 5 to Java 8 BCTLS jar which could cause issues with some TLS 1.2 cipher suites running on older JVMs. This is now fixed.
    -

    2.7.3 Additional Features and Functionality

    +

    2.8.3 Additional Features and Functionality

    • BCJSSE: Following OpenJDK, finalizers have been removed from SSLSocket subclasses. Applications should close sockets and not rely on garbage collection.
    • BCJSSE: Added support for boolean system property "jdk.tls.client.useCompatibilityMode" (default "true").
    • @@ -267,30 +278,30 @@

      2.7.3 Additional Features and Functionality

    • An UnknownPacket type has been added to the PGP APIs to allow for forwards compatibility with upcoming revisions to the standard.
    -

    2.8.1 Version

    +

    2.9.1 Version

    Release: 1.75
    Date:      2023, June 21st -

    2.8.2 Defects Fixed

    +

    2.9.2 Defects Fixed

    • Several Java 8 method calls were accidentally introduced in the Java 5 to Java 8 build. The affected classes have been refactored to remove this.
    • (D)TLS: renegotiation after resumption now fixed to avoid breaking connection.
    -

    2.8.3 Notes.

    +

    2.9.3 Notes.

    • The ASN.1 core package has had some dead and retired methods cleaned up and removed.
    -

    2.9.1 Version

    +

    2.10.1 Version

    Release: 1.74
    Date:      2023, June 12th -

    2.9.2 Defects Fixed

    +

    2.10.2 Defects Fixed

    • AsconEngine: Fixed a buffering bug when decrypting across multiple processBytes calls (ascon128a unaffected).
    • Context based sanity checking on PGP signatures has been added.
    • The ParallelHash clone constructor was not copying all fields. This is now fixed.
    • The maximimum number of blocks for CTR/SIC modes was 1 block less than it should have been. This is now fixed.
    -

    2.9.3 Additional Features and Functionality

    +

    2.10.3 Additional Features and Functionality

    • The PGP API now supports wildcard key IDs for public key based data encryption.
    • LMS now supports SHA256/192, SHAKE256/192, and SHAKE256/256 (the additional SP 8000-208 parameter sets).
    • @@ -309,22 +320,22 @@

      2.9.3 Additional Features and Functionality

    • The number of keys/sub-keys in a PGPKeyRing can now be found by calling PGPKeyRing.size().
    • The PQC algorithms LMS/HSS, SPHINCS+, Dilithium, Falcon, and NTRU are now supported directly by the BC provider.
    -

    2.9.4 Notes.

    +

    2.10.4 Notes.

    • The now defunct PQC SIKE algorithm has been removed, this has also meant the removal of its resource files so the provider is now quite a bit smaller.
    • As a precaution, HC128 now enforces a 128 bit IV, previous behaviour for shorter IVs can be supported where required by padding the IV to the 128 bits with zero.
    • PGP encrypted data generation now uses integrity protection by default. Previous behaviour for encrypted data can be supported where required by calling PGPDataEncryptorBuilder.setWithIntegrityPacket(false) when data encryption is set up.
    • There are now additional sanity checks in place to prevent accidental mis-use of PGPSignature objects. If this change causes any issues, you might want to check what your code is up to as there is probably a bug.
    -

    2.9.5 Security Advisories.

    +

    2.10.5 Security Advisories.

    • CVE-2023-33201 - this release fixes an issue with the X509LDAPCertStoreSpi where a specially crafted certificate subject could be used to try and extract extra information out of an LDAP server with wild-card matching enabled.
    -

    2.10.1 Version

    +

    2.11.1 Version

    Release: 1.73
    Date:      2023, April 8th -

    2.10.2 Defects Fixed

    +

    2.11.2 Defects Fixed

    • BCJSSE: Instantiating a JSSE provider in some contexts could cause an AccessControl exception. This has been fixed.
    • The EC key pair generator can generate out of range private keys when used with SM2. A specific SM2KeyPairGenerator has been added to the low-level API and is used by KeyPairGenerator.getInstance("SM2", "BC"). The SM2 signer has been updated to check for out of range keys as well..
    • @@ -345,7 +356,7 @@

      2.10.2 Defects Fixed

    • IPAddress has been written to provide stricter checking and avoid the use of Integer.parseInt().
    • A Java 7 class snuck into the Java 5 to Java 8 build. This has been addressed.
    -

    2.10.3 Additional Features and Functionality

    +

    2.11.3 Additional Features and Functionality

    • The Rainbow NIST Post Quantum Round-3 Candidate has been added to the low-level API and the BCPQC provider (level 3 and level 5 parameter sets only).
    • The GeMSS NIST Post Quantum Round-3 Candidate has been added to the low-level API.
    • @@ -372,38 +383,38 @@

      2.10.3 Additional Features and Functionality

    • A general purpose PQCOtherInfoGenerator has been added which supports all Kyber and NTRU.
    • An implementation of HPKE (RFC 9180 - Hybrid Public Key Encryption) has been added to the light-weight cryptography API.
    -

    2.10.4 Security Advisories.

    +

    2.11.4 Security Advisories.

    • The PQC implementations have now been subject to formal review for secret leakage and side channels, there were issues in BIKE, Falcon, Frodo, HQC which have now been fixed. Some weak positives also showed up in Rainbow, Picnic, SIKE, and GeMSS - for now this last set has been ignored as the algorithms will either be updated if they reappear in the Signature Round, or deleted, as is already the case for SIKE (it is now in the legacy package). Details on the group responsible for the testing can be found in the CONTRIBUTORS file.
    • For at least some ECIES variants (e.g. when using CBC) there is an issue with potential malleability of a nonce (implying silent malleability of the plaintext) that must be sent alongside the ciphertext but is outside the IES integrity check. For this reason the automatic generation of nonces with IED is now disabled and they have to be passed in using an IESParameterSpec. The current advice is to agree on a nonce between parties and then rely on the use of the ephemeral key component to allow the nonce (rather the so called nonce) usage to be extended.
    -

    2.10.5 Notes.

    +

    2.11.5 Notes.

    • Most test data files have now been migrated to a separate project bc-test-data which is also available on github. If you clone bc-test-data at the same level as the bc-java project the tests will find the test data they require.
    • There has been further work to make entropy collection more friendly in container environments. See DRBG.java for details. We would welcome any further feedback on this as we clearly cannot try all situations first hand.
    -

    2.11.1 Version

    +

    2.12.1 Version

    Release: 1.72.2, 1.72.3
    Date:      2022, November 20th -

    2.11.2 Defects Fixed

    +

    2.12.2 Defects Fixed

    • PGP patch release - fix for OSGI and version header in 1.72.1 jar file.
    -

    2.12.1 Version

    +

    2.13.1 Version

    Release: 1.72.1
    Date:      2022, October 25th -

    2.12.2 Defects Fixed

    +

    2.13.2 Defects Fixed

    • PGP patch release - fix for regression in OpenPGP PGPEncryptedData.java which could result in checksum failures on correct files.
    -

    2.13.1 Version

    +

    2.14.1 Version

    Release: 1.72
    Date:      2022, September 25th -

    2.13.2 Defects Fixed

    +

    2.14.2 Defects Fixed

    • There were parameter errors in XMSS^MT OIDs for XMSSMT_SHA2_40/4_256 and XMSSMT_SHA2_60/3_256. These have been fixed.
    • There was an error in Merkle tree construction for the Evidence Records (ERS) implementation which could result in invalid roots been timestamped. ERS now produces an ArchiveTimeStamp for each data object/group with an associated reduced hash tree. The reduced hash tree is now calculated as a simple path to the root of the tree for each record.
    • @@ -411,7 +422,7 @@

      2.13.2 Defects Fixed

    • A tagging calculation error in GCMSIV which could result in incorrect tags has been fixed.
    • Issues around Java 17 which could result in failing tests have been addressed.
    -

    2.13.3 Additional Features and Functionality

    +

    2.14.3 Additional Features and Functionality

    • BCJSSE: TLS 1.3 is now enabled by default where no explicit protocols are supplied (e.g. "TLS" or "Default" SSLContext algorithms, or SSLContext.getDefault() method).
    • BCJSSE: Rewrite SSLEngine implementation to improve compatibility with SunJSSE.
    • @@ -441,22 +452,22 @@

      2.13.3 Additional Features and Functionality

    • Support has been added to the PKCS#12 implementation for the Oracle trusted certificate attribute.
    • Performance of our BZIP2 classes has been improved.
    -

    2.13.4 Notes

    +

    2.14.4 Notes

    Keep in mind the PQC algorithms are still under development and we are still at least a year and a half away from published standards. This means the algorithms may still change so by all means experiment, but do not use the PQC algoritms for anything long term.

    The legacy "Rainbow" and "McEliece" implementations have been removed from the BCPQC provider. The underlying classes are still present if required. Other legacy algorithm implementations can be found under the org.bouncycastle.pqc.legacy package.

    -

    2.13.5 Security Notes

    +

    2.14.5 Security Notes

    The PQC SIKE algorithm is provided for research purposes only. It should now be regarded as broken. The SIKE implementation will be withdrawn in BC 1.73.

    -

    2.14.1 Version

    +

    2.15.1 Version

    Release: 1.71
    Date:      2022, March 31st. -

    2.14.2 Defects Fixed

    +

    2.15.2 Defects Fixed

    • In line with GPG the PGP API now attempts to preserve comments containing non-ascii UTF-8 characters.
    • An accidental partial dependency on Java 1.7 has been removed from the TLS API.
    • @@ -470,7 +481,7 @@

      2.14.2 Defects Fixed

    • An accidental regression introduced by a fix for another issue in PKIXCertPathReviewer around use of the AuthorityKeyIdentifier extension and it failing to match a certificate uniquely when the serial number field is missing has been fixed.
    • An error was found in the creation of TLS 1.3 Export Keying Material which could cause compatibility issues. This has been fixed.
    -

    2.14.3 Additional Features and Functionality

    +

    2.15.3 Additional Features and Functionality

    • Support has been added for OpenPGP regular expression signature packets.
    • Support has been added for OpenPGP PolicyURI signature packets.
    • @@ -500,16 +511,16 @@

      2.14.3 Additional Features and Functionality

    • ASN.1 object support has been added for the Lightweight Certificate Management Protocol (CMP), currently in draft.
    • A HybridValueParamterSpec class has been added for use with KeyAgreement to support SP 800-56C hybrid (so classical/post-quantum) key agreement.
    -

    2.14.4 Notes

    +

    2.15.4 Notes

    • The deprecated QTESLA implementation has been removed from the BCPQC provider.
    • The submission update to SPHINCS+ has been added. This changes the generation of signatures - particularly deterministic ones.
    -

    2.15.1 Version

    +

    2.16.1 Version

    Release: 1.70
    Date:      2021, November 29th. -

    2.15.2 Defects Fixed

    +

    2.16.2 Defects Fixed

    • Blake 3 output limit is enforced.
    • The PKCS12 KeyStore was relying on default precedence for its key Cipher implementation so was sometimes failing if used from the keytool. The KeyStore class now makes sure it uses the correct Cipher implementation.
    • @@ -523,7 +534,7 @@

      2.15.2 Defects Fixed

    • The lack of close() in the ASN.1 Dump command line utility was triggering false positives in some code analysis tools. A close() call has been added.
    • PGPPublicKey.getBitStrength() now properly recognises EdDSA keys.
    -

    2.15.3 Additional Features and Functionality

    +

    2.16.3 Additional Features and Functionality

    • Missing PGP CRC checksums can now be optionally ignored using setDetectMissingCRC() (default false) on ArmoredInputStream.
    • PGPSecretKey.copyWithNewPassword() now has a variant which uses USAGE_SHA1 for key protection if a PGPDigestCalculator is passed in.
    • @@ -562,15 +573,15 @@

      2.15.3 Additional Features and Functionality

    • The JcePKCSPBEOutputEncryptorBuilder now supports SCRYPT with ciphers that do not have algorithm parameters (e.g. AESKWP).
    • Support is now added for certificates using ETSI TS 103 097, "Intelligent Transport Systems (ITS)" in the bcpkix package.
    -

    2.15.4 Notes.

    +

    2.16.4 Notes.

    • While this release should maintain source code compatibility, developers making use of some parts of the ASN.1 library will find that some classes need recompiling. Apologies for the inconvenience.
    -

    2.16.1 Version

    +

    2.17.1 Version

    Release: 1.69
    Date:      2021, June 7th. -

    2.16.2 Defects Fixed

    +

    2.17.2 Defects Fixed

    • Lightweight and JCA conversion of Ed25519 keys in the PGP API could drop the leading byte as it was zero. This has been fixed.
    • Marker packets appearing at the start of PGP public key rings could cause parsing failure. This has been fixed.
    • @@ -590,7 +601,7 @@

      2.16.2 Defects Fixed

    • Fix various conversions and interoperability for XDH and EdDSA between BC and SunEC providers.
    • TLS: Prevent attempts to use KeyUpdate mechanism in versions before TLS 1.3.
    -

    2.16.3 Additional Features and Functionality

    +

    2.17.3 Additional Features and Functionality

    • GCM-SIV has been added to the lightweight API and the provider.
    • Blake3 has been added to the lightweight API.
    • @@ -631,24 +642,24 @@

      2.16.3 Additional Features and Functionality

    • BCJSSE: Key managers now support EC credentials for use with TLS 1.3 ECDSA signature schemes (including brainpool).
    • TLS: Add TLS 1.3 support for brainpool curves per RFC 8734.
    -

    2.16.4 Notes

    +

    2.17.4 Notes

    • There is a small API change in the PKIX package to the DigestAlgorithmIdentifierFinder interface as a find() method that takes an ASN1ObjectIdentifier has been added to it. For people wishing to extend their own implementations, see DefaultDigestAlgorithmIdentifierFinder for a sample implementation.
    • A version of the bcmail API supporting Jakarta Mail has now been added (see bcjmail jar).
    • Some work has been done on moving out code that does not need to be in the provider jar. This has reduced the size of the provider jar and should also make it easier for developers to patch the classes involved as they no longer need to be signed. bcpkix and bctls are both dependent on the new bcutil jar.
    -

    2.17.1 Version

    +

    2.18.1 Version

    Release: 1.68
    Date:      2020, December 21st. -

    2.17.2 Defects Fixed

    +

    2.18.2 Defects Fixed

    • Some BigIntegers utility methods would fail for BigInteger.ZERO. This has been fixed.
    • PGPUtil.isKeyRing() was not detecting secret sub-keys in its input. This has been fixed.
    • The ASN.1 class, ArchiveTimeStamp was insisting on a value for the optional reducedHashTree field. This has been fixed.
    • BCJSSE: Lock against multiple writers - a possible synchronization issue has been removed.
    -

    2.17.3 Additional Features and Functionality

    +

    2.18.3 Additional Features and Functionality

    • BCJSSE: Added support for system property com.sun.net.ssl.requireCloseNotify. Note that we are using a default value of 'true'.
    • BCJSSE: 'TLSv1.3' is now a supported protocol for both client and server. For this release it is only enabled by default for the 'TLSv1.3' SSLContext, but can be explicitly enabled using 'setEnabledProtocols' on an SSLSocket or SSLEngine, or via SSLParameters.
    • @@ -659,10 +670,10 @@

      2.17.3 Additional Features and Functionality

    -

    2.18.1 Version

    +

    2.19.1 Version

    Release: 1.67
    Date:      2020, November 1st. -

    2.18.2 Defects Fixed

    +

    2.19.2 Defects Fixed

    • BCJSSE: SunJSSE compatibility fix - override of getChannel() removed and 'urgent data' behaviour should now conform to what the SunJSSE expects.
    • Nested BER data could sometimes cause issues in octet strings. This has been fixed.
    • @@ -674,7 +685,7 @@

      2.18.2 Defects Fixed

    • Zero length data would cause an unexpected exception from RFC5649WrapEngine. This has been fixed.
    • OpenBSDBcrypt was failing to handle some valid prefixes. This has been fixed.
    -

    2.18.3 Additional Features and Functionality

    +

    2.19.3 Additional Features and Functionality

    • Performance of Argon2 has been improved.
    • Performance of Noekeon has been improved.
    • @@ -692,15 +703,15 @@

      2.18.3 Additional Features and Functionality

    • Mode name checks in Cipher strings should now make sure an improper mode name always results in a NoSuchAlgorithmException.
    • In line with changes in OpenSSL, the OpenSSLPBKDF now uses UTF-8 encoding.
    -

    2.18.4 Security Advisory

    +

    2.19.4 Security Advisory

    • As described in CVE-2020-28052, the OpenBSDBCrypt.checkPassword() method had a flaw in it due to a change for BC 1.65. BC 1.66 is also affected. The issue is fixed in BC 1.67. If you are using OpenBSDBCrypt.checkPassword() and you are using BC 1.65 or BC 1.66 we strongly advise moving to BC 1.67 or later.
    -

    2.19.1 Version

    +

    2.20.1 Version

    Release: 1.66
    Date:      2020, July 4th. -

    2.19.2 Defects Fixed

    +

    2.20.2 Defects Fixed

    • EdDSA verifiers now reset correctly after rejecting overly long signatures.
    • BCJSSE: SSLSession.getPeerCertificateChain could throw NullPointerException. This has been fixed.
    • @@ -717,7 +728,7 @@

      2.19.2 Defects Fixed

    • For a few values the cSHAKE implementation would add unnecessary pad bytes where the N and S strings produced encoded data that was block aligned. This has been fixed.
    • There were a few circumstances where Argon2BytesGenerator might hit an unexpected null. These have been removed.
    -

    2.19.3 Additional Features and Functionality

    +

    2.20.3 Additional Features and Functionality

    • The qTESLA signature algorithm has been updated to v2.8 (20191108).
    • BCJSSE: Client-side OCSP stapling now supports status_request_v2 extension.
    • @@ -736,15 +747,15 @@

      2.19.3 Additional Features and Functionality

    • Performance of the Base64 encoder has been improved.
    • The PGPPublicKey class will now include direct key sigantures when checking for key expiry times.
    -

    2.19.4 Notes

    +

    2.20.4 Notes

    The qTESLA update breaks compatibility with previous versions. Private keys now include a hash of the public key at the end, and signatures are no longer interoperable with previous versions.

    -

    2.20.1 Version

    +

    2.21.1 Version

    Release: 1.65
    Date:      2020, March 31st. -

    2.20.2 Defects Fixed

    +

    2.21.2 Defects Fixed

    • DLExternal would encode using DER encoding for tagged SETs. This has been fixed.
    • ChaCha20Poly1305 could fail for large (>~2GB) files. This has been fixed.
    • @@ -756,7 +767,7 @@

      2.20.2 Defects Fixed

    • BCJSSE: Choice of credentials and signing algorithm now respect the peer's signature_algorithms extension properly.
    • BCJSSE: KeyManager for KeyStoreBuilderParameters no longer leaks memory.
    -

    2.20.3 Additional Features and Functionality

    +

    2.21.3 Additional Features and Functionality

    • LMS and HSS (RFC 8554) support has been added to the low level library and the PQC provider.
    • SipHash128 support has been added to the low level library and the JCE provider.
    • @@ -770,10 +781,10 @@

      2.20.3 Additional Features and Functionality

    • TLS: DSA in JcaTlsCrypto now falls back to stream signing to work around NoneWithDSA limitations in default provider.
    -

    2.21.1 Version

    +

    2.22.1 Version

    Release: 1.64
    Date:      2019, October 7th. -

    2.21.2 Defects Fixed

    +

    2.22.2 Defects Fixed

    • OpenSSH: Fixed padding in generated Ed25519 private keys.
    • Validation of headers in PemReader now looks for tailing dashes in header.
    • @@ -781,7 +792,7 @@

      2.21.2 Defects Fixed

    • Some compatibility issues around the signature encryption algorithm field in CMS SignedData and the GOST algorithms have been addressed.
    • GOST3410-2012-512 now uses the GOST3411-2012-256 as its KDF digest.
    -

    2.21.3 Additional Features and Functionality

    +

    2.22.3 Additional Features and Functionality

    • PKCS12: key stores containing only certificates can now be created without the need to provide passwords.
    • BCJSSE: Initial support for AlgorithmConstraints; protocol versions and cipher suites.
    • @@ -794,20 +805,20 @@

      2.21.3 Additional Features and Functionality

    • Support for Java 11's NamedParameterSpec class has been added (using reflection) to the EC and EdEC KeyPairGenerator implementations.
    -

    2.21.4 Removed Features and Functionality

    +

    2.22.4 Removed Features and Functionality

    • Deprecated ECPoint 'withCompression' tracking has been removed.
    -

    2.21.5 Security Advisory

    +

    2.22.5 Security Advisory

    • A change to the ASN.1 parser in 1.63 introduced a regression that can cause an OutOfMemoryError to occur on parsing ASN.1 data. We recommend upgrading to 1.64, particularly where an application might be parsing untrusted ASN.1 data from third parties.
    -

    2.22.1 Version

    +

    2.23.1 Version

    Release: 1.63
    Date:      2019, September 10th. -

    2.22.2 Defects Fixed

    +

    2.23.2 Defects Fixed

    • The ASN.1 parser would throw a large object exception for some objects which could be safely parsed. This has been fixed.
    • GOST3412-2015 CTR mode was unusable at the JCE level. This has been fixed.
    • @@ -826,7 +837,7 @@

      2.22.2 Defects Fixed

    • It is now possible to specify different S-Box parameters for the GOST 28147-89 MAC.
    -

    2.22.3 Additional Features and Functionality

    +

    2.23.3 Additional Features and Functionality

    • QTESLA is now updated with the round 2 changes. Note: the security catergories, and in some cases key generation and signatures, have changed. For people interested in comparison, the round 1 version is now moved to org.bouncycastle.pqc.crypto.qteslarnd1 - this package will be deleted in 1.64. Please keep in mind that QTESLA may continue to evolve.
    • Support has been added for generating Ed25519/Ed448 signed certificates.
    • @@ -839,10 +850,10 @@

      2.22.3 Additional Features and Functionality

    • The valid path for EST services has been updated to cope with the characters used in the Aruba clearpass EST implementation.
    -

    2.23.1 Version

    +

    2.24.1 Version

    Release: 1.62
    Date:      2019, June 3rd. -

    2.23.2 Defects Fixed

    +

    2.24.2 Defects Fixed

    • DTLS: Fixed infinite loop on IO exceptions.
    • DTLS: Retransmission timers now properly apply to flights monolithically.
    • @@ -859,7 +870,7 @@

      2.23.2 Defects Fixed

    • CertificateFactory now enforces presence of PEM headers when required.
    • A performance issue with RSA key pair generation that was introduced in 1.61 has been mostly eliminated.
    -

    2.23.3 Additional Features and Functionality

    +

    2.24.3 Additional Features and Functionality

    • Builders for X509 certificates and CRLs now support replace and remove extension methods.
    • DTLS: Added server-side support for HelloVerifyRequest.
    • @@ -880,10 +891,10 @@

      2.23.3 Additional Features and Functionality

    • Support for the Ethereum flavor of IES has been added to the lightweight API.
    -

    2.24.1 Version

    +

    2.25.1 Version

    Release: 1.61
    Date:      2019, February 4th. -

    2.24.2 Defects Fixed

    +

    2.25.2 Defects Fixed

    • Use of EC named curves could be lost if keys were constructed via a key factory and algorithm parameters. This has been fixed.
    • RFC3211WrapEngine would not properly handle messages longer than 127 bytes. This has been fixed.
    • @@ -904,7 +915,7 @@

      2.24.2 Defects Fixed

    • Several parsing issues related to the processing of CMP PKIPublicationInfo have been fixed.
    • The ECGOST curves for id-tc26-gost-3410-12-256-paramSetA and id-tc26-gost-3410-12-512-paramSetC had incorrect co-factors. These have been fixed.
    -

    2.24.3 Additional Features and Functionality

    +

    2.25.3 Additional Features and Functionality

    • The qTESLA signature algorithm has been added to PQC light-weight API and the PQC provider.
    • The password hashing function, Argon2 has been added to the lightweight API.
    • @@ -928,15 +939,15 @@

      2.24.3 Additional Features and Functionality

    • SM2 in public key cipher mode has been added to the provider API.
    • The BCFKSLoadStoreParameter has been extended to allow the use of certificates and digital signatures for verifying the integrity of BCFKS key stores.
    -

    2.24.4 Removed Features and Functionality

    +

    2.25.4 Removed Features and Functionality

    • Deprecated methods for EC point construction independent of curves have been removed.
    -

    2.25.1 Version

    +

    2.26.1 Version

    Release: 1.60
    Date:      2018, June 30 -

    2.25.2 Defects Fixed

    +

    2.26.2 Defects Fixed

    • Base64/UrlBase64 would throw an exception on a zero length string. This has been fixed.
    • Base64/UrlBase64 would throw an exception if there was whitespace in the last 4 characters. This has been fixed.
    • @@ -957,7 +968,7 @@

      2.25.2 Defects Fixed

    • In some situations the use of sm2p256v1 would result in "unknown curve name". This has been fixed.
    • CMP PollReqContent now supports multiple certificate request IDs.
    -

    2.25.3 Additional Features and Functionality

    +

    2.26.3 Additional Features and Functionality

    • TLS: Extended CBC padding is now optional (and disabled by default).
    • TLS: Now supports channel binding 'tls-server-end-point'.
    • @@ -985,16 +996,16 @@

      2.25.3 Additional Features and Functionality

    • Support has been added for the German BSI KAEG Elliptic Curve key agreement algorithm with X9.63 as the KDF to the JCE.
    • Support has been added for the German BSI KAEG Elliptic Curve session key KDF to the lightweight API.
    -

    2.25.4 Security Related Changes and CVE's Addressed by this Release

    +

    2.26.4 Security Related Changes and CVE's Addressed by this Release

    • CVE-2018-1000180: issue around primality tests for RSA key pair generation if done using only the low-level API.
    • CVE-2018-1000613: lack of class checking in deserialization of XMSS/XMSS^MT private keys with BDS state information.
    -

    2.26.1 Version

    +

    2.27.1 Version

    Release: 1.59
    Date:      2017, December 28 -

    2.26.2 Defects Fixed

    +

    2.27.2 Defects Fixed

    • Issues with using PQC based keys with the provided BC KeyStores have now been fixed.
    • ECGOST-2012 public keys were being encoded with the wrong OID for the digest parameter in the algorithm parameter set. This has been fixed.
    • @@ -1008,7 +1019,7 @@

      2.26.2 Defects Fixed

    • An off-by-one error for the max N check for SCRYPT has been fixed. SCRYPT should now be compliant with RFC 7914.
    • ASN1GeneralizedTime will now accept a broader range of input strings.
    -

    2.26.3 Additional Features and Functionality

    +

    2.27.3 Additional Features and Functionality

    • GOST3410-94 private keys encoded using ASN.1 INTEGER are now accepted in private key info objects.
    • SCRYPT is now supported as a SecretKeyFactory in the provider and in the PKCS8 APIs
    • @@ -1027,15 +1038,15 @@

      2.26.3 Additional Features and Functionality

    • A DEROtherInfo generator for key agreement using NewHope as the source of the shared private info has been added that can be used in conjunction with regular key agreement algorithms.
    • RFC 7748: Added low-level implementations of X25519 and X448.
    -

    2.26.4 Security Related Changes and CVE's Addressed by this Release

    +

    2.27.4 Security Related Changes and CVE's Addressed by this Release

    • CVE-2017-13098 ("ROBOT"), a Bleichenbacher oracle in TLS when RSA key exchange is negotiated. This potentially affected BCJSSE servers and any other TLS servers configured to use JCE for the underlying crypto - note the two TLS implementations using the BC lightweight APIs are not affected by this.
    -

    2.27.1 Version

    +

    2.28.1 Version

    Release: 1.58
    Date:      2017, August 18 -

    2.27.2 Defects Fixed

    +

    2.28.2 Defects Fixed

    • NewHope and SPHINCS keys are now correctly created off certificates by the BC provider.
    • Use of the seeded constructor with SecureRandom() and the BC provider in first position could cause a stack overflow error. This has been fixed.
    • @@ -1049,7 +1060,7 @@

      2.27.2 Defects Fixed

    • A race condition that could occur inside the HybridSecureRandom on reseed and result in an exception has been fixed.
    • DTLS now supports records containing multiple handshake messages.
    -

    2.27.3 Additional Features and Functionality

    +

    2.28.3 Additional Features and Functionality

    • An implementation of GOST3410-2012 has been added to light weight API and the JCA provider.
    • Support for ECDH GOST3410-2012 and GOST3410-2001 have been added. The CMS API can also handle reading ECDH GOST3410 key transport messages.
    • @@ -1069,16 +1080,16 @@

      2.27.3 Additional Features and Functionality

    • The new TLS API now supports RFC 7633 - X.509v3 TLS Feature Extension (e.g. "must staple"), enabled in default clients.
    • TLS exceptions have been made more directly informative.
    -

    2.27.4 Removed Features and Functionality

    +

    2.28.4 Removed Features and Functionality

    • Per RFC 7465, removed support for RC4 in the new TLS API.
    • Per RFC 7568, removed support for SSLv3 in the new TLS API.
    -

    2.28.1 Version

    +

    2.29.1 Version

    Release: 1.57
    Date:      2017, May 11 -

    2.28.2 Defects Fixed

    +

    2.29.2 Defects Fixed

    • A class cast exception for master certification removal in PGPPublicKey.removeCertification() by certification has been fixed.
    • GOST GOFB 28147-89 mode had an edge condition concerning the incorrect calculation of N4 (see section 6.1 of RFC 5830) affecting about 1% of IVs. This has been fixed.
    • @@ -1095,7 +1106,7 @@

      2.28.2 Defects Fixed

    • EC FixedPointCombMultiplier avoids 'infinity' point in lookup tables, reducing timing side-channels.
    • Reuse of a Blake2b digest with a call to reset() rather than doFinal() could result in incorrect padding being introduced and the wrong digest result produced. This has been fixed.
    -

    2.28.3 Additional Features and Functionality

    +

    2.29.3 Additional Features and Functionality

    • ARIA (RFC 5794) is now supported by the provider and the lightweight API.
    • ARIA Key Wrapping (RFC 5649 style) is now supported by the provider and the lightweight API.
    • @@ -1105,23 +1116,23 @@

      2.28.3 Additional Features and Functionality

    • A test client for EST which will interop with the 7030 test server at http://testrfc7030.com/ has been added to the general test module in the current source tree.
    • The BCJSSE provider now supports SSLContext.getDefault(), with very similar behaviour to the SunJSSE provider, including checks of the relevant javax.net.ssl.* system properties and auto-loading of jssecacerts or cacerts as the default trust store.
    -

    2.28.4 Security Related Changes

    +

    2.29.4 Security Related Changes

    • The default parameter sizes for DH and DSA are now 2048. If you have been relying on key pair generation without passing in parameters generated keys will now be larger.
    • Further work has been done on preventing accidental re-use of a GCM cipher without first changing its key or iv.
    -

    2.29.1 Version

    +

    2.30.1 Version

    Release: 1.56
    Date:      2016, December 23 -

    2.29.2 Defects Fixed

    +

    2.30.2 Defects Fixed

    • See section 2.15.4 for Security Defects.
    • Using unknown status with the ASN.1 CertStatus primitive could result in an IllegalArgumentException on construction. This has been fixed.
    • A potentional NullPointerException in a precomputation in WNafUtil has been removed.
    • PGPUtil.getDecoderStream() would throw something other than an IOException for empty and very small data. This has been fixed.
    -

    2.29.3 Additional Features and Functionality

    +

    2.30.3 Additional Features and Functionality

    • Support for the explicit setting of AlgorithmParameters has been added to the JceCMSContentEncryptorBuilder and the JceCMSMacCaculatorBuilder classes to allow configuration of the session cipher/MAC used.
    • EC, ECGOST3410, and DSTU4145 Public keys are now validated on construction in the JCA/JCE and the light weight API.
    • @@ -1137,7 +1148,7 @@

      2.29.3 Additional Features and Functionality

    • SHA-3 support has been added to BcDefaultDigestProvider.
    • A higher level TLS API and JSSE provider have been added to the project.
    -

    2.29.4 Security Related Changes and CVE's Addressed by this Release

    +

    2.30.4 Security Related Changes and CVE's Addressed by this Release

    • It is now possible to configure the provider to only import keys for specific named curves.
    • Work has been done to improve the "constant time" behaviour of the RSA padding mechanisms.
    • @@ -1156,15 +1167,15 @@

      2.29.3 Additional Features and Functionality

    • CVE-2016-1000346: Other party DH public key not fully validated. This can cause issues as invalid keys can be used to reveal details about the other party's private key where static Diffie-Hellman is in use. As of this release the key parameters are checked on agreement calculation.
    • CVE-2016-1000352: ECIES allows the use of unsafe ECB mode. This algorithm is now removed from the provider.
    -

    2.29.5 Security Advisory

    +

    2.30.5 Security Advisory

    • We consider the carry propagation bugs fixed in this release to have been exploitable in previous releases (1.51-1.55), for static ECDH, to reveal the long-term key, per "Practical realisation and elimination of an ECC-related software bug attack", Brumley et.al.. The most common case of this would be the non-ephemeral ECDH ciphersuites in TLS. These are not enabled by default in our TLS implementations, but they can be enabled explicitly by users. We recommend that users DO NOT enable static ECDH ciphersuites for TLS.
    -

    2.30.1 Version

    +

    2.31.1 Version

    Release: 1.55
    Date:      2016, August 18 -

    2.30.2 Defects Fixed

    +

    2.31.2 Defects Fixed

    • Issues with cloning of blake digests with salts and personalisation strings have been fixed.
    • The JceAsymmetricValueDecryptor in the CRMF package now attempts to recognise a wider range of parameters for the key wrapping algorithm, rather than relying on a default.
    • @@ -1185,7 +1196,7 @@

      2.30.2 Defects Fixed

    • Trying to use of non-default parameters for OAEP in CRMF would resort to the default parameter set. This has been fixed.
    • If the BC provider was not registered, creating a CertificateFactory would cause a new provider object to be created. This has been fixed.
    -

    2.30.3 Additional Features and Functionality

    +

    2.31.3 Additional Features and Functionality

    • The DANE API has been updated to reflect the latest standard changes.
    • The signature algorithm SPHINCS-256 has been added to the post-quantum provider (BCPQC). Support is in place for SHA-512 and SHA3-512 (using trees based around SHA512_256 and SHA3_256 respectively).
    • @@ -1203,10 +1214,10 @@

      2.30.3 Additional Features and Functionality

    • Additional search methods have been added to PGP public and secret key rings.
    -

    2.31.1 Version

    +

    2.32.1 Version

    Release: 1.54
    Date:      2015, December 29 -

    2.31.2 Defects Fixed

    +

    2.32.2 Defects Fixed

    • Blake2b-160, Blake2b-256, Blake2b-384, and Blake2b-512 are now actually in the provider and an issue with cloning Blake2b digests has been fixed.
    • PKCS#5 Scheme 2 using DESede CBC is now supported by the PKCS#12 implementation.
    • @@ -1215,7 +1226,7 @@

      2.31.2 Defects Fixed

    • It turns out, after advice one way and another that the NESSIE test vectors for Serpent are now what should be followed and that the vectors in the AES submission are regarded as an algorithm called Tnepres. The Serpent version now follows the NESSIE vectors, and the Tnepres cipher has been added to the provider and the lightweight API for compatibility.
    • Problems with DTLS record-layer version handling were resolved, making version negotiation work properly.
    -

    2.31.3 Additional Features and Functionality

    +

    2.32.3 Additional Features and Functionality

    • Camellia and SEED key wrapping are now supported for CMS key agreement
    • The BC TLS/DTLS code now includes a non-blocking API.
    • @@ -1225,19 +1236,19 @@

      2.31.3 Additional Features and Functionality

    • Support has been added to the CMS API for PKCS#7 ANY type encapsulated content where the encapsulated content is not an OCTET STRING.
    • PSSSigner in the lightweight API now supports fixed salts.
    -

    2.31.4 Security Advisory

    +

    2.32.4 Security Advisory

    • (D)TLS 1.2: Motivated by CVE-2015-7575, we have added validation that the signature algorithm received in DigitallySigned structures is actually one of those offered (in signature_algorithms extension or CertificateRequest). With our default TLS configuration, we do not believe there is an exploitable vulnerability in any earlier releases. Users that are customizing the signature_algorithms extension, or running a server supporting client authentication, are advised to double-check that they are not offering any signature algorithms involving MD5.
    -

    2.31.5 Notes

    +

    2.32.5 Notes

    If you have been using Serpent, you will need to either change to Tnepres, or take into account the fact that Serpent is now byte-swapped compared to what it was before.

    -

    2.32.1 Version

    +

    2.33.1 Version

    Release: 1.53
    Date:      2015, October 10 -

    2.32.2 Defects Fixed

    +

    2.33.2 Defects Fixed

    • The BC JCE cipher implementations could sometimes fail when used in conjunction with the JSSE and NIO. This has been fixed.
    • PGPPublicKey.getBitStrength() always returned 0 for EC keys. This has been fixed.
    • @@ -1262,7 +1273,7 @@

      2.32.2 Defects Fixed

    • Some decidedly odd argument casting in the PKIXCertPathValidator has been fixed to throw an InvalidAlgorithmParameterException.
    • Presenting an empty array of certificates to the PKIXCertPathValidator would cause an IndexOutOfRangeException instead of a CertPathValidatorException. This has been fixed.
    -

    2.32.3 Additional Features and Functionality

    +

    2.33.3 Additional Features and Functionality

    • It is now possible to specify that an unwrapped key must be usable by a software provider in the asymmetric unwrappers for CMS.
    • A Blake2b implementation has been added to the provider and lightweight API.
    • @@ -1278,15 +1289,15 @@

      2.32.3 Additional Features and Functionality

    • The PKCS#12 key store will now garbage collect orphaned certificates on saving.
    • Caching for ASN.1 ObjectIdentifiers has been rewritten to make use of an intern method. The "usual suspects" are now interned automatically, and the cache is used by the parser. Other OIDs can be added to the cache by calling ASN1ObjectIdentifier.intern().
    -

    2.32.4 Notes

    +

    2.33.4 Notes

    It turns out there was a similar, but different, issue in Crypto++ to the BC issue with ECIES. Crypto++ 6.0 now offers a corrected version of ECIES which is compatible with that which is now in BC.

    -

    2.33.1 Version

    +

    2.34.1 Version

    Release: 1.52
    Date:      2015, March 2 -

    2.33.2 Defects Fixed

    +

    2.34.2 Defects Fixed

    • GenericSigner in the lightweight API would fail if the digest started with a zero byte, occasionally causing a TLS negotiation to fail. This has been fixed.
    • Some BC internal classes expected the BC provider to be accessible within the provider. This has been fixed.
    • @@ -1303,7 +1314,7 @@

      2.33.2 Defects Fixed

    • A badly formed issuer in a X.509 certificate could cause a null pointer exception in X509CertificateHolder.toString(). This has been fixed.
    • CMSSignedData.verifySignatures() could fail on a correct counter signature due to a mismatch of the SID. This has been fixed.
    -

    2.33.3 Additional Features and Functionality

    +

    2.34.3 Additional Features and Functionality

    • The CMP support class CMPCertificate restricted the types of certificates that could be added. A more flexible method has been introduced to allow for other certificate types.
    • Support classes have be added for DNS-based Authentication of Named Entities (DANE) to the PKIX distribution.
    • @@ -1331,15 +1342,15 @@

      2.33.3 Additional Features and Functionality

    • Support for some JDK1.5+ language features has finally made its way into the repository.
    • A load store parameter, PKCS12StoreParameter, has been added to support DER only encoding of PKCS12 key stores.
    -

    2.33.4 Security Advisory

    +

    2.34.4 Security Advisory

    • The CTR DRBGs would not populate some bytes in the requested block of random bytes if the size of the block requested was not an exact multiple of the block size of the underlying cipher being used in the DRBG. If you are using the CTR DRBGs with "odd" keysizes, we strongly advise upgrading to this release, or contacting us for a work around.
    -

    2.34.1 Version

    +

    2.35.1 Version

    Release: 1.51
    Date:      2014, July 28 -

    2.34.2 Defects Fixed

    +

    2.35.2 Defects Fixed

    • The AEAD GCM AlgorithmParameters object was unable to return a GCMParameterSpec object. This has been fixed.
    • Cipher.getIV() was returning null for AEAD mode ciphers. This has been fixed.
    • @@ -1354,7 +1365,7 @@

      2.34.2 Defects Fixed

    • PKCS#12 files containing keys/certificates with empty attribute sets attached to them no longer cause an ArrayIndexOutOfBoundsException to be thrown.
    • Issues with certificate verification and server side DTLS/TLS 1.2 have now been fixed.
    -

    2.34.3 Additional Features and Functionality

    +

    2.35.3 Additional Features and Functionality

    • The range of key algorithm names that will be interpreted by KeyAgreement.generateSecret() has been expanded for ECDH derived algorithms in the provider. A KeyAgreement of ECDHwithSHA1KDF can now be explicitly created.
    • ECIES now supports the use of IVs with the underlying block cipher and CBC mode in both the lightweight and the JCE APIs.
    • @@ -1381,17 +1392,17 @@

      2.34.3 Additional Features and Functionality

    • Full support is now provided for client-side auth in the D/TLS server code.
    • Compatibility issues with some OSGI containers have been addressed.
    -

    2.34.4 Notes

    +

    2.35.4 Notes

    • Support for NTRUSigner has been deprecated as the algorithm has been withdrawn.
    • Some changes have affected the return values of some methods. If you are migrating from an earlier release, it is recommended to recompile before using this release.
    • There has been further clean out of deprecated methods in this release. If your code has previously been flagged as using a deprecated method you may need to change it. The OpenPGP API is the most heavily affected.
    -

    2.35.1 Version

    +

    2.36.1 Version

    Release: 1.50
    Date:      2013, December 3 -

    2.35.2 Defects Fixed

    +

    2.36.2 Defects Fixed

    • The DualECSP800DRBG sometimes truncated the last block in the generated stream incorrectly. This has been fixed.
    • Keys produced from RSA certificates with specialised parameters would lose the parameter settings. This has been fixed.
    • @@ -1405,7 +1416,7 @@

      2.35.2 Defects Fixed

    • Default RC2 parameters for 40 bit RC2 keys in CMSEnvelopedData were encoding incorrectly. This has been fixed.
    • In case of a long hash the DSTU4145 implementation would sometimes remove one bit too much during truncation. This has been fixed.
    -

    2.35.3 Additional Features and Functionality

    +

    2.36.3 Additional Features and Functionality

    • Additional work has been done on CMS recipient generation to simplify the generation of OAEP encrypted messages and allow for non-default parameters.
    • OCB implementation updated to account for changes in draft-irtf-cfrg-ocb-03.
    • @@ -1425,7 +1436,7 @@

      2.35.3 Additional Features and Functionality

    • The JDK 1.5+ provider will now recognise and use GCMParameterSpec if it is run in a 1.7 JVM.
    • Client side support and some server side support has been added for TLS/DTLS 1.2.
    -

    2.35.4 Notes

    +

    2.36.4 Notes

    • org.bouncycastle.crypto.DerivationFunction is now a base interface, the getDigest() method appears on DigestDerivationFunction.
    • Recent developments at NIST indicate the SHA-3 may be changed before final standardisation. Please bare this in mind if you are using it.
    • @@ -1435,10 +1446,10 @@

      2.35.4 Notes

    • ECDH support for OpenPGP should still be regarded as experimental. It is still possible there will be compliance issues with other implementations.
    -

    2.36.1 Version

    +

    2.37.1 Version

    Release: 1.49
    Date:      2013, May 31 -

    2.36.2 Defects Fixed

    +

    2.37.2 Defects Fixed

    • Occasional ArrayOutOfBounds exception in DSTU-4145 signature generation has been fixed.
    • The handling of escaped characters in X500 names is much improved.
    • @@ -1449,7 +1460,7 @@

      2.36.2 Defects Fixed

    • PEMParser would throw a NullPointerException if it ran into explicit EC curve parameters, it would also throw an Exception if the named curve was not already defined. The parser now returns X9ECParmameters for explicit parameters and returns an ASN1ObjectIdentifier for a named curve.
    • The V2TBSCertListGenerator was adding the wrong date type for CRL invalidity date extensions. This has been fixed.
    -

    2.36.3 Additional Features and Functionality

    +

    2.37.3 Additional Features and Functionality

    • A SecretKeyFactory has been added that enables use of PBKDF2WithHmacSHA.
    • Support has been added to PKCS12 KeyStores and PfxPdu to handle PKCS#5 encrypted private keys.
    • @@ -1478,16 +1489,16 @@

      2.36.3 Additional Features and Functionality

    • A basic commitment package has been introduced into the lightweight API containing a digest based commitment scheme.
    • It is now possible to set the NotAfter and NotBefore date in the CRMF CertificateRequestMessageBuilder class.
    -

    2.36.4 Notes

    +

    2.37.4 Notes

    • The NTRU implementation has been moved into the org.bouncycastle.pqc package hierarchy.
    • The change to PEMParser to support explicit EC curves is not backward compatible. If you run into a named curve you need to use org.bouncycastle.asn1.x9.ECNamedCurveTable.getByOID() to look the curve up if required.
    -

    2.37.1 Version

    +

    2.38.1 Version

    Release: 1.48
    Date:      2013, February 10 -

    2.37.2 Defects Fixed

    +

    2.38.2 Defects Fixed

    • Occasional key compatibility issues in IES due to variable length keys have been fixed.
    • PEMWriter now recognises the new PKCS10CertificationRequest object.
    • @@ -1498,7 +1509,7 @@

      2.37.2 Defects Fixed

    • The BC SSL implementation has been modified to deal with the "Lucky Thirteen" attack.
    • A regression in 1.47 which prevented key wrapping with regular symmetric PBE algorihtms has been fixed.
    -

    2.37.3 Additional Features and Functionality

    +

    2.38.3 Additional Features and Functionality

    • IES now supports auto generation of ephemeral keys in both the JCE and the lightweight APIs.
    • A new class PEMParser has been added to return the new CertificateHolder and Request objects introduced recently.
    • @@ -1513,10 +1524,10 @@

      2.37.3 Additional Features and Functionality

    • T61String now uses UTF-8 encoding by default rather than a simple 8 bit transform.
    -

    2.38.1 Version

    +

    2.39.1 Version

    Release: 1.47
    Date:      2012, March 30 -

    2.38.2 Defects Fixed

    +

    2.39.2 Defects Fixed

    • OpenPGP ID based certifications now support UTF-8. Note: this may mean that some old certifications no longer validate - if this happens a retry can be added using by converting the ID using Strings.fromByteArray(Strings.toByteArray(id)) - this will strip out the top byte in each character.
    • IPv4/IPv6 parsing in CIDR no longer assumes octet boundaries on a mask.
    • @@ -1533,7 +1544,7 @@

      2.38.2 Defects Fixed

    • Check of DH parameter L could reject some valid keys. This is now fixed.
    -

    2.38.3 Additional Features and Functionality

    +

    2.39.3 Additional Features and Functionality

    • Support is now provided via the RepeatedKey class to enable IV only re-initialisation in the JCE layer. The same effect can be acheived in the light weight API by using null as the key parameter when creating a ParametersWithIV object.
    • CRMF now supports empty poposkInput.
    • @@ -1553,15 +1564,15 @@

      2.38.3 Additional Features and Functionality

    • The J2ME lcrypto release now includes higher level classes for handling PKCS, CMS, CRMF, CMP, EAC, OpenPGP, and certificate generation.
    -

    2.38.4 Other notes

    +

    2.39.4 Other notes

    Okay, so we have had to do another release. The issue we have run into is that we probably didn't go far enough in 1.46, but we are now confident that moving from this release to 2.0 should be largely just getting rid of deprecated methods. While this release does change a lot it is relatively straight forward to do a port and we have a porting guide which explains the important ones. The area there has been the most change in is the ASN.1 library which was in bad need of a rewrite after 10 years of patching. On the bright side the rewrite did allow us to eliminate a few problems and bugs in the ASN.1 library, so we have some hope anyone porting to it will also have similar benefits. As with 1.46 the other point of emphasis has been making sure interface support is available for operations across the major APIs, so the lightweight API or some local role your own methods can be used instead for doing encryption and signing.

    -

    2.39.1 Version

    +

    2.40.1 Version

    Release: 1.46
    Date:      2011, February 23 -

    2.39.2 Defects Fixed

    +

    2.40.2 Defects Fixed

    • An edge condition in ECDSA which could result in an invalid signature has been fixed.
    • Exhaustive testing has been performed on the ASN.1 parser, eliminating another potential OutOfMemoryException and several escaping run time exceptions.
    • @@ -1570,7 +1581,7 @@

      2.39.2 Defects Fixed

    • DERGeneralizedTime.getDate() would produce incorrect results for fractional seconds. This has been fixed.
    • PSSSigner would produce incorrect results if the MGF digest and content digest were not the same. This has been fixed.
    -

    2.39.3 Additional Features and Functionality

    +

    2.40.3 Additional Features and Functionality

    • A null genTime can be passed to TimeStampResponseGenerator.generate() to generate timeNotAvailable error responses.
    • Support has been added for reading and writing of openssl PKCS#8 encrypted keys.
    • @@ -1587,7 +1598,7 @@

      2.39.3 Additional Features and Functionality

    • PGP public subkeys can now be separately decoded and encoded.
    • An IV can now be passed to an ISO9797Alg3Mac.
    -

    2.39.4 Other notes

    +

    2.40.4 Other notes

    Baring security patches we expect 1.46 will be the last of the 1.* releases. The next release of BC will be version 2.0. For this reason a lot of things in 1.46 that relate to CMS have been deprecated and @@ -1604,29 +1615,29 @@

    2.39.4 Other notes

  • The X509Name class will utlimately be replacde with the X500Name class, the getInstance() methods on both these classes allow conversion from one type to another.
  • The org.bouncycastle.cms.RecipientId class now has a collection of subclasses to allow for more specific recipient matching. If you are creating your own recipient ids you should use the constructors for the subclasses rather than relying on the set methods inherited from X509CertSelector. The dependencies on X509CertSelector and CertStore will be removed from the version 2 CMS API.
  • -

    2.40.1 Version

    +

    2.41.1 Version

    Release: 1.45
    Date:      2010, January 12 -

    2.40.2 Defects Fixed

    +

    2.41.2 Defects Fixed

    • OpenPGP now supports UTF-8 in file names for literal data.
    • The ASN.1 library was losing track of the stream limit in a couple of places, leading to the potential of an OutOfMemoryError on a badly corrupted stream. This has been fixed.
    • The provider now uses a privileged block for initialisation.
    • JCE/JCA EC keys are now serialisable.
    -

    2.40.3 Additional Features and Functionality

    +

    2.41.3 Additional Features and Functionality

    • Support for EC MQV has been added to the light weight API, provider, and the CMS/SMIME library.
    -

    2.40.4 Security Advisory

    +

    2.41.4 Security Advisory

    • This version of the provider has been specifically reviewed to eliminate possible timing attacks on algorithms such as GCM and CCM mode.
    -

    2.41.1 Version

    +

    2.42.1 Version

    Release: 1.44
    Date:      2009, October 9 -

    2.41.2 Defects Fixed

    +

    2.42.2 Defects Fixed

    • The reset() method in BufferedAsymmetricBlockCipher is now fully clearing the buffer.
    • Use of ImplicitlyCA with KeyFactory and Sun keyspec no longer causes NullPointerException.
    • @@ -1642,7 +1653,7 @@

      2.41.2 Defects Fixed

    • PKIXCertPathReviewer.getTrustAnchor() could occasionally cause a null pointer exception or an exception due to conflicting trust anchors. This has been fixed.
    • Handling of explicit CommandMap objects with the generation of S/MIME messages has been improved.
    -

    2.41.3 Additional Features and Functionality

    +

    2.42.3 Additional Features and Functionality

    • PEMReader/PEMWriter now support encrypted EC keys.
    • BC generated EC private keys now include optional fields required by OpenSSL.
    • @@ -1658,24 +1669,24 @@

      2.41.3 Additional Features and Functionality

    • Support for raw signatures has been extended to RSA and RSA-PSS in the provider. RSA support can be used in CMSSignedDataStreamGenerator to support signatures without signed attributes.
    -

    2.42.1 Version

    +

    2.43.1 Version

    Release: 1.43
    Date:      2009, April 13 -

    2.42.2 Defects Fixed

    +

    2.43.2 Defects Fixed

    • Multiple countersignature attributes are now correctly collected.
    • Two bugs in HC-128 and HC-256 related to sign extension and byte swapping have been fixed. The implementations now pass the latest ecrypt vector tests.
    • X509Name.hashCode() is now consistent with equals.
    -

    2.42.3 Security Advisory

    +

    2.43.3 Security Advisory

    • The effect of the sign extension bug was to decrease the key space the HC-128 and HC-256 ciphers were operating in and the byte swapping inverted every 32 bits of the generated stream. If you are using either HC-128 or HC-256 you must upgrade to this release.
    -

    2.43.1 Version

    +

    2.44.1 Version

    Release: 1.42
    Date:      2009, March 16 -

    2.43.2 Defects Fixed

    +

    2.44.2 Defects Fixed

    • A NullPointer exception which could be result from generating a diffie-hellman key has been fixed.
    • CertPath validation could occasionally mistakenly identify a delta CRL. This has been fixed.
    • @@ -1688,7 +1699,7 @@

      2.43.2 Defects Fixed

    • Multiplication by negative powers of two is fixed in BigInteger.
    • OptionalValidity now encodes correctly.
    -

    2.43.3 Additional Features and Functionality

    +

    2.44.3 Additional Features and Functionality

    • Support for NONEwithECDSA has been added.
    • Support for Grainv1 and Grain128 has been added.
    • @@ -1699,10 +1710,10 @@

      2.43.3 Additional Features and Functionality

    • Support for the SRP-6a protocol has been added to the lightweight API.
    -

    2.44.1 Version

    +

    2.45.1 Version

    Release: 1.41
    Date:      2008, October 1 -

    2.44.2 Defects Fixed

    +

    2.45.2 Defects Fixed

    • The GeneralName String constructor now supports IPv4 and IPv6 address parsing.
    • An issue with nested-multiparts with postamble for S/MIME that was causing signatures to fail verification has been fixed.
    • @@ -1713,7 +1724,7 @@

      2.44.2 Defects Fixed

    • Standard name "DiffieHellman" is now supported in the provider.
    • Better support for equality tests for '#' encoded entries has been added to X509Name.
    -

    2.44.3 Additional Features and Functionality

    +

    2.45.3 Additional Features and Functionality

    • Camellia is now 12.5% faster than previously.
    • A smaller version (around 8k compiled) of Camellia, CamelliaLightEngine has also been added.
    • @@ -1724,10 +1735,10 @@

      2.44.3 Additional Features and Functionality

    • Support for reading and extracting personalised certificates in PGP Secret Key rings has been added.
    -

    2.45.1 Version

    +

    2.46.1 Version

    Release: 1.40
    Date:      2008, July 12 -

    2.45.2 Defects Fixed

    +

    2.46.2 Defects Fixed

    • EAX mode ciphers were not resetting correctly after a doFinal/reset. This has been fixed.
    • The SMIME API was failing to verify doubly nested multipart objects in signatures correctly. This has been fixed.
    • @@ -1743,7 +1754,7 @@

      2.45.2 Defects Fixed

    • The '+' character can now be escaped or quoted in the constructor for X509Name, X509Prinicipal.
    • Fix to regression from 1.38: PKIXCertPathValidatorResult.getPublicKey was returning the wrong public key when the BC certificate path validator was used.
    -

    2.45.3 Additional Features and Functionality

    +

    2.46.3 Additional Features and Functionality

    • Galois/Counter Mode (GCM) has been added to the lightweight API and the JCE provider.
    • SignedPublicKeyAndChallenge and PKCS10CertificationRequest can now take null providers if you need to fall back to the default provider mechanism.
    • @@ -1751,15 +1762,15 @@

      2.45.3 Additional Features and Functionality

    • Unnecessary local ID attributes on certificates in PKCS12 files are now automatically removed.
    • The PKCS12 store types PKCS12-3DES-3DES and PKCS12-DEF-3DES-3DES have been added to support generation of PKCS12 files with both certificates and keys protected by 3DES.
    -

    2.45.4 Additional Notes

    +

    2.46.4 Additional Notes

    • Due to problems for some users caused by the presence of the IDEA algorithm, an implementation is no longer included in the default signed jars. Only the providers of the form bcprov-ext-*-*.jar now include IDEA.
    -

    2.46.1 Version

    +

    2.47.1 Version

    Release: 1.39
    Date:      2008, March 29 -

    2.46.2 Defects Fixed

    +

    2.47.2 Defects Fixed

    • A bug causing the odd NullPointerException has been removed from the LocalizedMessage class.
    • IV handling in CMS for the SEED and Camellia was incorrect. This has been fixed.
    • @@ -1773,7 +1784,7 @@

      2.46.2 Defects Fixed

    • A decoding issue with a mis-identified tagged object in CertRepMessage has been fixed.
    • \# is now properly recognised in the X509Name class.
    -

    2.46.3 Additional Features and Functionality

    +

    2.47.3 Additional Features and Functionality

    • Certifications associated with user attributes can now be created, verified and removed in OpenPGP.
    • API support now exists for CMS countersignature reading and production.
    • @@ -1788,10 +1799,10 @@

      2.46.3 Additional Features and Functionality

    • Support has been added to the provider for the VMPC MAC.
    -

    2.47.1 Version

    +

    2.48.1 Version

    Release: 1.38
    Date:      2007, November 7 -

    2.47.2 Defects Fixed

    +

    2.48.2 Defects Fixed

    • SMIME signatures containing non-standard quote-printable data could be altered by SMIME encryption. This has been fixed.
    • CMS signatures that do not use signed attributes were vulnerable to one of Bleichenbacher's RSA signature forgery attacks. This has been fixed.
    • @@ -1805,7 +1816,7 @@

      2.47.2 Defects Fixed

    • Overwriting entities in a PKCS#12 file was not fully compliant with the JavaDoc for KeyStore. This has been fixed.
    • TlsInputStream.read() could appear to return end of file when end of file had not been reached. This has been fixed.
    -

    2.47.3 Additional Features and Functionality

    +

    2.48.3 Additional Features and Functionality

    • Buffering in the streaming CMS has been reworked. Throughput is now usually higher and the behaviour is more predictable.
    • It's now possible to pass a table of hashes to a CMS detached signature rather than having to always pass the data.
    • @@ -1816,10 +1827,10 @@

      2.47.3 Additional Features and Functionality

    • CertPathReviewer has better handling for problem trust anchors.
    • Base64 encoder now does initial size calculations to try to improve resource usage.
    -

    2.48.1 Version

    +

    2.49.1 Version

    Release: 1.37
    Date:      2007, June 15 -

    2.48.2 Defects Fixed

    +

    2.49.2 Defects Fixed

    • The ClearSignedFileProcessor example for OpenPGP did not take into account trailing white space in the file to be signed. This has been fixed.
    • @@ -1833,7 +1844,7 @@

      2.48.2 Defects Fixed

    • The default private key length in the lightweght API for generated DiffieHellman parameters was absurdly small, this has been fixed.
    • Cipher.getParameters() for PBEwithSHAAndTwofish-CBC was returning null after intialisation. This has been fixed.
    -

    2.48.3 Additional Features and Functionality

    +

    2.49.3 Additional Features and Functionality

    • The block cipher mode CCM has been added to the provider and light weight API.
    • The block cipher mode EAX has been added to the provider and light weight API.
    • @@ -1852,10 +1863,10 @@

      2.48.3 Additional Features and Functionality

    • The JCE provider now supports RIPEMD160withECDSA.
    -

    2.49.1 Version

    +

    2.50.1 Version

    Release: 1.36
    Date:      2007, March 16 -

    2.49.2 Defects Fixed

    +

    2.50.2 Defects Fixed

    • DSA key generator now checks range and keysize.
    • Class loader issues with i18n classes should now be fixed.
    • @@ -1869,7 +1880,7 @@

      2.49.2 Defects Fixed

    • Some surrogate pairs were not assembled correctly by the UTF-8 decoder. This has been fixed.
    • Alias resolution in PKCS#12 is now case insensitive.
    -

    2.49.3 Additional Features and Functionality

    +

    2.50.3 Additional Features and Functionality

    • CMS/SMIME now supports basic EC KeyAgreement with X9.63.
    • CMS/SMIME now supports RFC 3211 password based encryption.
    • @@ -1885,10 +1896,10 @@

      2.49.3 Additional Features and Functionality

    • DSASigner now handles long messages. SHA2 family digest support for DSA has been added to the provider.
    -

    2.50.1 Version

    +

    2.51.1 Version

    Release: 1.35
    Date:      2006, December 16 -

    2.50.2 Defects Fixed

    +

    2.51.2 Defects Fixed

    • Test data files are no longer in the provider jars.
    • SMIMESignedParser now handles indefinite length data in SignerInfos.
    • @@ -1903,7 +1914,7 @@

      2.50.2 Defects Fixed

    • The IESEngine could incorrectly encrypt data when used in block cipher mode. This has been fixed.
    • An error in the encoding of the KEKRecipientInfo has been fixed. Compatability warning: this may mean that versions of BC mail prior to 1.35 will have trouble processing KEK messages produced by 1.35 or later.
    -

    2.50.3 Additional Features and Functionality

    +

    2.51.3 Additional Features and Functionality

    • Further optimisations to elliptic curve math libraries.
    • API now incorporates a CertStore which should be suitable for use with LDAP.
    • @@ -1925,10 +1936,10 @@

      2.50.3 Additional Features and Functionality

    • PGP packet streams can now be closed off using close() on the returned stream as well as closing the generator.
    -

    2.51.1 Version

    +

    2.52.1 Version

    Release: 1.34
    Date:      2006, October 2 -

    2.51.2 Defects Fixed

    +

    2.52.2 Defects Fixed

    • Endianess of integer conversion in KDF2BytesGenerator was incorrect. This has been fixed.
    • Generating critical signature subpackets in OpenPGP would result in a zero packet tag. This has been fixed. @@ -1940,7 +1951,7 @@

      2.51.2 Defects Fixed

    • PGP Identity strings were only being interpreted as ASCII rather than UTF-8. This has been fixed.
    • CertificateFactory.generateCRLs now returns a Collection rather than null.
    -

    2.51.3 Additional Features and Functionality

    +

    2.52.3 Additional Features and Functionality

    • An ISO18033KDFParameters class had been added to support ISO18033 KDF generators.
    • An implemention of the KDF1 bytes generator algorithm has been added. @@ -1960,16 +1971,16 @@

      2.51.3 Additional Features and Functionality

    • Performance of the prime number generation in the BigInteger library has been further improved.
    • In line with RFC 3280 section 4.1.2.4 DN's are now encoded using UTF8String by default rather than PrintableString.
    -

    2.51.4 Security Advisory

    +

    2.52.4 Security Advisory

    • If you are using public exponents with the value three you *must* upgrade to this release, otherwise it will be possible for attackers to exploit some of Bleichenbacher's RSA signature forgery attacks on your applications.
    -

    2.52.1 Version

    +

    2.53.1 Version

    Release: 1.33
    Date:      2006, May 3 -

    2.52.2 Defects Fixed

    +

    2.53.2 Defects Fixed

    • OCSPResponseData was including the default version in its encoding. This has been fixed.
    • BasicOCSPResp.getVersion() would throw a NullPointer exception if called on a default version response. This has been fixed. @@ -1978,7 +1989,7 @@

      2.52.2 Defects Fixed

    • ArmoredInputStream was not closing the underlying stream on close. This has been fixed.
    • Small base64 encoded strings with embedded white space could decode incorrectly using the Base64 class. This has been fixed.
    -

    2.52.3 Additional Features and Functionality

    +

    2.53.3 Additional Features and Functionality

    • The X509V2CRLGenerator now supports adding general extensions to CRL entries.
    • A RoleSyntax implementation has been added to the x509 ASN.1 package, and the AttributeCertificateHolder class now support the IssuerSerial option. @@ -1986,10 +1997,10 @@

      2.52.3 Additional Features and Functionality

    • DERUTF8String now supports surrogate pairs.
    -

    2.53.1 Version

    +

    2.54.1 Version

    Release: 1.32
    Date:      2006, March 27 -

    2.53.2 Defects Fixed

    +

    2.54.2 Defects Fixed

    • Further work has been done on RFC 3280 compliance.
    • The ASN1Sequence constructor for SemanticsInformation would sometimes throw a ClassCastException on reconstruction an object from a byte stream. This has been fixed. @@ -2006,7 +2017,7 @@

      2.53.2 Defects Fixed

    • OpenPGP clear text signatures containing '\r' as line separators were not being correctly canonicalized. This has been fixed.
    -

    2.53.3 Additional Features and Functionality

    +

    2.54.3 Additional Features and Functionality

    • The ASN.1 library now includes classes for the ICAO Electronic Passport.
    • Support has been added to CMS and S/MIME for ECDSA. @@ -2015,16 +2026,16 @@

      2.53.3 Additional Features and Functionality

    • Support has been added for repeated attributes in CMS and S/MIME messages.
    • A wider range of RSA-PSS signature types is now supported for CRL and Certificate verification.
    -

    2.53.4 Possible compatibility issue

    +

    2.54.4 Possible compatibility issue

    • Previously elliptic curve keys and points were generated with point compression enabled by default. Owing to patent issues in some jurisdictions, they are now generated with point compression disabled by default.
    -

    2.54.1 Version

    +

    2.55.1 Version

    Release: 1.31
    Date:      2005, December 29 -

    2.54.2 Defects Fixed

    +

    2.55.2 Defects Fixed

    • getCriticalExtensionOIDs on an X.509 attribute certificate was returning the non-critical set. This has been fixed.
    • Encoding uncompressed ECDSA keys could occasionally introduce an extra leading zero byte. This has been fixed. @@ -2037,7 +2048,7 @@

      2.54.2 Defects Fixed

      This has been fixed.
    • OIDs with extremely large components would sometimes reencode with unnecessary bytes in their encoding. The optimal DER encoding will now be produced instead.
    -

    2.54.3 Additional Features and Functionality

    +

    2.55.3 Additional Features and Functionality

    • The SMIME package now supports the large file streaming model as well.
    • Additional ASN.1 message support has been added for RFC 3739 in the org.bouncycastle.x509.qualified package. @@ -2046,10 +2057,10 @@

      2.54.3 Additional Features and Functionality

    • CertPathValidator has been updated to better support path validation as defined in RFC 3280.
    -

    2.55.1 Version

    +

    2.56.1 Version

    Release: 1.30
    Date:      2005, September 18 -

    2.55.2 Defects Fixed

    +

    2.56.2 Defects Fixed

    • Whirlpool was calculating the wrong digest for 31 byte data and could throw an exception for some other data lengths. This has been fixed.
    • AlgorithmParameters for IVs were returning a default of RAW encoding of the parameters when they should have been returning an @@ -2061,7 +2072,7 @@

      2.55.2 Defects Fixed

    • KEKIdentifier would not handle OtherKeyAttribute objects correctly. This has been fixed.
    • GetCertificateChain on a PKCS12 keystore would return a single certificate chain rather than null if the alias passed in represented a certificate not a key. This has been fixed.
    -

    2.55.3 Additional Features and Functionality

    +

    2.56.3 Additional Features and Functionality

    • RSAEngine no longer assumes keys are byte aligned when checking for out of range input.
    • PGPSecretKeyRing.removeSecretKey and PGPSecretKeyRing.insertSecretKey have been added. @@ -2072,10 +2083,10 @@

      2.55.3 Additional Features and Functionality

    • Both the lightweight API and the provider now support the Camellia encryption algorithm.
    -

    2.56.1 Version

    +

    2.57.1 Version

    Release: 1.29
    Date:      2005, June 27 -

    2.56.2 Defects Fixed

    +

    2.57.2 Defects Fixed

    • HMac-SHA384 and HMac-SHA512 were not IETF compliant. This has been fixed.
    • The equals() method on ElGamalKeyParameters and DHKeyParameters in the lightweight API would sometimes @@ -2086,7 +2097,7 @@

      2.56.2 Defects Fixed

    • ISO9796 signatures for full recovered messsages could incorrectly verify for similar messages in some circumstances. This has been fixed.
    • The occasional problem with decrypting PGP messages containing compressed streams now appears to be fixed.
    -

    2.56.3 Additional Features and Functionality

    +

    2.57.3 Additional Features and Functionality

    • Support has been added for the OIDs and key generation required for HMac-SHA224, HMac-SHA256, HMac-SHA384, and HMac-SHA512. @@ -2094,16 +2105,16 @@

      2.56.3 Additional Features and Functionality

    • The provider and the lightweight API now support the GOST-28147-94 MAC algorithm.
    • Headers are now settable for PGP armored output streams.
    -

    2.56.4 Notes

    +

    2.57.4 Notes

    • The old versions of HMac-SHA384 and HMac-SHA512 can be invoked as OldHMacSHA384 and OldHMacSHA512, or by using the OldHMac class in the lightweight API.
    -

    2.57.1 Version

    +

    2.58.1 Version

    Release: 1.28
    Date:      2005, April 20 -

    2.57.2 Defects Fixed

    +

    2.58.2 Defects Fixed

    • Signatures on binary encoded S/MIME messages could fail to validate when correct. This has been fixed.
    • getExtensionValue() on CRL Entries were returning the encoding of the inner object, rather than the octet string. This has been fixed. @@ -2117,7 +2128,7 @@

      2.57.2 Defects Fixed

    • Filetype for S/MIME compressed messages was incorrect. This has been fixed.
    • BigInteger class can now create negative numbers from byte arrays.
    -

    2.57.3 Additional Features and Functionality

    +

    2.58.3 Additional Features and Functionality

    • S/MIME now does canonicalization on non-binary input for signatures.
    • Micalgs for the new SHA schemes are now supported. @@ -2128,7 +2139,7 @@

      2.57.3 Additional Features and Functionality

    • Support has been added for the creation of ECDSA certificate requests.
    • The provider and the light weight API now support the WHIRLPOOL message digest.
    -

    2.57.4 Notes

    +

    2.58.4 Notes

    • Patches for S/MIME binary signatures and canonicalization were actually applied in 1.27, but a couple of days after the release - if the class CMSProcessableBodyPartOutbound is present in the package org.bouncycastle.mail.smime you have the patched 1.27. We would recommend upgrading to 1.28 in any case @@ -2136,10 +2147,10 @@

      2.57.4 Notes

    • GOST private keys are probably not encoding correctly and can be expected to change.
    -

    2.58.1 Version

    +

    2.59.1 Version

    Release: 1.27
    Date:      2005, February 20 -

    2.58.2 Defects Fixed

    +

    2.59.2 Defects Fixed

    • Typos in the provider which pointed Signature algorithms SHA256WithRSA, SHA256WithRSAEncryption, SHA384WithRSA, SHA384WithRSAEncryption, SHA512WithRSA, and SHA512WithRSAEncryption at the PSS versions of the algorithms have been fixed. The correct names for the PSS algorithms are SHA256withRSAandMGF1, SHA384withRSAandMGF1, and SHA512withRSAandMGF1.
    • X509CertificateFactory failed under some circumstances to reset properly if the input stream being passed @@ -2153,7 +2164,7 @@

      2.58.2 Defects Fixed

    • TSP TimeStampToken was failing to validate time stamp tokens with the issuerSerial field set in the ESSCertID structure. This has been fixed.
    • Path validation in environments with frequently updated CRLs could occasionally reject a valid path. This has been fixed.
    -

    2.58.3 Additional Features and Functionality

    +

    2.59.3 Additional Features and Functionality

    • Full support has been added for the OAEPParameterSpec class to the JDK 1.5 povider.
    • Full support has been added for the PSSParameterSpec class to the JDK 1.4 and JDK 1.5 providers. @@ -2164,7 +2175,7 @@

      2.58.3 Additional Features and Functionality

    • The CertPath support classes now support PKCS #7 encoding.
    • Point compression can now be turned off when encoding elliptic curve keys.
    -

    2.58.4 Changes that may affect compatibility

    +

    2.59.4 Changes that may affect compatibility

    • org.bouncycastle.jce.interfaces.ElGamalKey.getParams() has been changed to getParameters() to avoid clashes with a JCE interface with the same method signature. @@ -2174,10 +2185,10 @@

      2.58.4 Changes that may affect compatibility

      were using these previously you should use SHA256WithRSAAndMGF1, SHA384WithRSAAndMGF1, or SHA512WithRSAAndMGF1.
    -

    2.59.1 Version

    +

    2.60.1 Version

    Release: 1.26
    Date:      2005, January 15 -

    2.59.2 Defects Fixed

    +

    2.60.2 Defects Fixed

    • The X.509 class UserNotice assumed some of the optional fields were not optional. This has been fixed.
    • BCPGInputStream would break on input packets of 8274 bytes in length. This has been fixed. @@ -2186,7 +2197,7 @@

      2.59.2 Defects Fixed

    • ASN1Sets now properly sort their contents when created from scratch.
    • A bug introduced in the CertPath validation in the last release which meant some certificate paths would validate if they were invalid has been fixed.
    -

    2.59.3 Additional Features and Functionality

    +

    2.60.3 Additional Features and Functionality

    • Support for JDK 1.5 naming conventions for OAEP encryption and PSS signing has been added.
    • Support for Time Stamp Protocol (RFC 3161) has been added. @@ -2196,15 +2207,15 @@

      2.59.3 Additional Features and Functionality

    • PBEWithMD5AndRC2, PBEWithSHA1AndRC2 now generate keys rather than exceptions.
    • The BigInteger implementation has been further optimised to take more advantage of the Montgomery number capabilities.
    -

    2.59.4 JDK 1.5 Changes

    +

    2.60.4 JDK 1.5 Changes

    • The JDK 1.5 version of the provider now supports the new Elliptic Curve classes found in the java.security packages. Note: while we have tried to preserve some backwards compatibility people using Elliptic curve are likely to find some minor code changes are required when moving code from JDK 1.4 to JDK 1.5 as the java.security APIs have changed.
    -

    2.60.1 Version

    +

    2.61.1 Version

    Release: 1.25
    Date:      2004, October 1 -

    2.60.2 Defects Fixed

    +

    2.61.2 Defects Fixed

    • In some situations OpenPGP would overread when a stream had been broken up into partial blocks. This has been fixed. @@ -2226,7 +2237,7 @@

      2.60.2 Defects Fixed

    • Parsing a message with a zero length body with SMIMESigned would cause an exception. This has been fixed.
    • Some versions of PGP use zeros in the data stream rather than a replication of the last two bytes of the iv as specified in the RFC to determine if the correct decryption key has been found. The decryption classes will now cope with both.
    -

    2.60.3 Additional Features and Functionality

    +

    2.61.3 Additional Features and Functionality

    • Support for extracting signatures based on PGP user attributes has been added to PGPPublicKey. @@ -2246,10 +2257,10 @@

      2.60.3 Additional Features and Functionality

    • OID components of up to 2^63 bits are now supported.
    -

    2.61.1 Version

    +

    2.62.1 Version

    Release: 1.24
    Date:      2004, June 12 -

    2.61.2 Defects Fixed

    +

    2.62.2 Defects Fixed

    • OpenPGP Secret key rings now parse key rings with user attribute packets in them correctly.
    • OpenPGP Secret key rings now parse key rings with GPG comment packets in them. @@ -2266,17 +2277,17 @@

      2.61.2 Defects Fixed

    • An encoding error introduced in 1.23 which affected generation of the KeyUsage extension has been fixed.
    -

    2.61.3 Additional Features and Functionality

    +

    2.62.3 Additional Features and Functionality

    • PKCS12 keystore now handles single key/certificate files without any attributes present.
    • Support for creation of PGPKeyRings incorporating sub keys has been added.
    • ZeroPadding for encrypting ASCII data has been added.
    -

    2.62.1 Version

    +

    2.63.1 Version

    Release: 1.23
    Date:      2004, April 10 -

    2.62.2 Defects Fixed

    +

    2.63.2 Defects Fixed

    • Reading a PGP Secret key file would sometimes cause a class cast exception. This has been fixed.
    • PGP will now read SecretKeys which are encrypted with the null algorithm. @@ -2291,7 +2302,7 @@

      2.62.2 Defects Fixed

    • X509Name class will now print names with nested pairs in component sets correctly.
    • RC4 now resets correctly on doFinal.
    -

    2.62.3 Additional Features and Functionality

    +

    2.63.3 Additional Features and Functionality

    • PGP V3 keys and V3 signature generation is now supported.
    • Collection classes have been added for representing files of PGP public and secret keys. @@ -2310,10 +2321,10 @@

      2.62.3 Additional Features and Functionality

    • DERGeneralizedTime getTime() method now handles a broader range of input strings.
    -

    2.63.1 Version

    +

    2.64.1 Version

    Release: 1.22
    Date:      2004, February 7 -

    2.63.2 Defects Fixed

    +

    2.64.2 Defects Fixed

    • Generating DSA signatures with PGP would cause a class cast exception, this has been fixed.
    • PGP Data in the 192 to 8383 byte length would sometimes be written with the wrong length header. This has been fixed. @@ -2323,7 +2334,7 @@

      2.63.2 Defects Fixed

    • PSS signature verification would fail approximately 0.5 % of the time on correct signatures. This has been fixed.
    • Encoding of CRL Distribution Points now always works.
    -

    2.63.3 Additional Features and Functionality

    +

    2.64.3 Additional Features and Functionality

    • Additional methods for getting public key information have been added to the PGP package.
    • Some support for user attributes and the image attribute tag has been added. @@ -2331,10 +2342,10 @@

      2.63.3 Additional Features and Functionality

    • Support for ElGamal encryption/decryption has been added to the PGP package.
    -

    2.64.1 Version

    +

    2.65.1 Version

    Release: 1.21
    Date:      2003, December 6 -

    2.64.2 Defects Fixed

    +

    2.65.2 Defects Fixed

    • The CertPath validator would fail for some valid CRLs. This has been fixed.
    • AES OIDS for S/MIME were still incorrect, this has been fixed. @@ -2342,17 +2353,17 @@

      2.64.2 Defects Fixed

    • The J2ME BigInteger class would sometimes go into an infinite loop generating prime numbers. This has been fixed.
    • DERBMPString.equals() would throw a class cast exception. This has been fixed.
    -

    2.64.3 Additional Features and Functionality

    +

    2.65.3 Additional Features and Functionality

    • PEMReader now handles public keys.
    • OpenPGP/BCPG should now handle partial input streams. Additional methods for reading subpackets off signatures.
    • The ASN.1 library now supports policy qualifiers and policy info objects.
    -

    2.65.1 Version

    +

    2.66.1 Version

    Release: 1.20
    Date:      2003, October 8 -

    2.65.2 Defects Fixed

    +

    2.66.2 Defects Fixed

    • BigInteger toString() in J2ME/JDK1.0 now produces same output as the Sun one.
    • RSA would throw a NullPointer exception with doFinal without arguments. This has been fixed. @@ -2362,7 +2373,7 @@

      2.65.2 Defects Fixed

    • AES OIDS were incorrect, this has been fixed.
    • In some cases BC generated private keys would not work with the JSSE. This has been fixed.
    -

    2.65.3 Additional Features and Functionality

    +

    2.66.3 Additional Features and Functionality

    • Support for reading/writing OpenPGP public/private keys and OpenPGP signatures has been added.
    • Support for generating OpenPGP PBE messages and public key encrypted messages has been added. @@ -2370,10 +2381,10 @@

      2.65.3 Additional Features and Functionality

    • Addition of a Null block cipher to the light weight API.
    -

    2.66.1 Version

    +

    2.67.1 Version

    Release: 1.19
    Date:      2003, June 7 -

    2.66.2 Defects Fixed

    +

    2.67.2 Defects Fixed

    • The PKCS12 store would throw an exception reading PFX files that had attributes with no values. This has been fixed.
    • RSA Private Keys would not serialise if they had PKCS12 bag attributes attached to them, this has been fixed. @@ -2381,7 +2392,7 @@

      2.66.2 Defects Fixed

    • ASN1 parser would sometimes mistake an implicit null for an implicit empty sequence. This has been fixed.
    -

    2.66.3 Additional Features and Functionality

    +

    2.67.3 Additional Features and Functionality

    • S/MIME and CMS now support the draft standard for AES encryption.
    • S/MIME and CMS now support setable key sizes for the standard algorithms. @@ -2393,10 +2404,10 @@

      2.66.3 Additional Features and Functionality

      in order to find algorithms.
    -

    2.67.1 Version

    +

    2.68.1 Version

    Release: 1.18
    Date:      2003, February 8 -

    2.67.2 Defects Fixed

    +

    2.68.2 Defects Fixed

    • DESKeySpec.isParityAdjusted in the clean room JCE could go into an infinite loop. This has been fixed. @@ -2407,7 +2418,7 @@

      2.67.2 Defects Fixed

    • Seeding with longs in the SecureRandom for the J2ME and JDK 1.0, only used 4 bytes of the seed value. This has been fixed.
    -

    2.67.3 Additional Features and Functionality

    +

    2.68.3 Additional Features and Functionality

    • The X.509 OID for RSA is now recognised by the provider as is the OID for RSA/OAEP.
    • Default iv's for DES are now handled correctly in CMS. @@ -2419,10 +2430,10 @@

      2.67.3 Additional Features and Functionality

      Sun BigInteger library.
    -

    2.68.1 Version

    +

    2.69.1 Version

    Release: 1.17
    Date:      2003, January 8 -

    2.68.2 Defects Fixed

    +

    2.69.2 Defects Fixed

    • Reuse of an CMSSignedObject could occasionally result in a class cast exception. This has been fixed. @@ -2433,7 +2444,7 @@

      2.68.2 Defects Fixed

    • The DERObject constructor in OriginatorIdentifierOrKey was leaving the id field as null. This has been fixed.
    -

    2.68.3 Additional Functionality and Features

    +

    2.69.3 Additional Functionality and Features

    • RC2 now supports the full range of parameter versions and effective key sizes. @@ -2453,10 +2464,10 @@

      2.68.3 Additional Functionality and Features

      string to OID conversion.
    -

    2.69.1 Version

    +

    2.70.1 Version

    Release: 1.16
    Date:      2002, November 30 -

    2.69.2 Defects Fixed

    +

    2.70.2 Defects Fixed

    • CRLS were only working for UTC time constructed Time objects, this has been fixed. @@ -2470,7 +2481,7 @@

      2.69.2 Defects Fixed

      to throw a NullPointerException at the wrong time.
    • Macs now clone correctly in the clean room JCE.
    -

    2.69.3 Additional Functionality and Features

    +

    2.70.3 Additional Functionality and Features

    • PGPCFB support has been added to the provider and the lightweight API.
    • There are now three versions of the AESEngine, all faster than before, @@ -2488,10 +2499,10 @@

      2.69.3 Additional Functionality and Features

      and to support multiple recipients/signers.
    -

    2.70.1 Version

    +

    2.71.1 Version

    Release: 1.15
    Date:      2002, September 6 -

    2.70.2 Defects Fixed

    +

    2.71.2 Defects Fixed

    • The base string for the oids in asn1.x509.KeyPurposeId was incorrect. This has been fixed. @@ -2514,7 +2525,7 @@

      2.70.2 Defects Fixed

      The local name now takes precedence.
    • ReasonFlags now correctly encodes.
    -

    2.70.3 Additional Functionality and Features

    +

    2.71.3 Additional Functionality and Features

    • The PKCS12 key store now handles key bags in encryptedData bags.
    • The X509NameTokenizer now handles for '\' and '"' characters. @@ -2523,10 +2534,10 @@

      2.70.3 Additional Functionality and Features

    • Both the provider and the lightweight library now support a basic SIC mode for block ciphers.
    -

    2.71.1 Version

    +

    2.72.1 Version

    Release: 1.14
    Date:      2002, June 17 -

    2.71.2 Defects Fixed

    +

    2.72.2 Defects Fixed

    • there was a bug in the BigInteger right shifting for > 31 bit shifts. This has been fixed. @@ -2547,7 +2558,7 @@

      2.71.2 Defects Fixed

    • asn1.x509.ExtendedKeyUsage used to throw a null pointer exception on construction. This has been fixed.
    -

    2.71.3 Additional Functionality and Features

    +

    2.72.3 Additional Functionality and Features

    • The BigInteger library now uses Montgomery numbers for modPow and is substantially faster. @@ -2561,10 +2572,10 @@

      2.71.3 Additional Functionality and Features

      object identifiers.
    -

    2.72.1 Version

    +

    2.73.1 Version

    Release: 1.13
    Date:      2002, April 19 -

    2.72.2 Defects Fixed

    +

    2.73.2 Defects Fixed

    • The TBSCertificate object in the ASN.1 library now properly implements the Time object, rather returning UTC time. @@ -2573,7 +2584,7 @@

      2.72.2 Defects Fixed

    • toByteArray in the big integer class was not always producing correct results for negative numbers. This has been Fixed.
    -

    2.72.3 Additional Functionality and Features

    +

    2.73.3 Additional Functionality and Features

    • The key to keySpec handling of the secret key factories has been improved.
    • There is now a SMIME implementation and a more complete CMS @@ -2588,10 +2599,10 @@

      2.72.3 Additional Functionality and Features

      length certificate chains for signing keys.
    -

    2.73.1 Version

    +

    2.74.1 Version

    Release: 1.12
    Date:      2002, February 8 -

    2.73.2 Defects Fixed

    +

    2.74.2 Defects Fixed

    • The ASN.1 library was unable to read an empty set object. This has been fixed.
    • Returning sets of critical and non-critical extensions on X.509 certificates could result in a null pointer exception if the certificate had no extensions. This has been fixed. @@ -2610,7 +2621,7 @@

      2.73.2 Defects Fixed

    • the IV algorithm parameters class would improperly throw an exception on initialisation. This has been fixed.
    -

    2.73.3 Additional Functionality and Features

    +

    2.74.3 Additional Functionality and Features

    • The AESWrap ciphers will now take IV's.
    • The DES-EDEWrap algorithm described in https://www.ietf.org/internet-drafts/draft-ietf-smime-key-wrap-01.txt is now supported. @@ -2624,10 +2635,10 @@

      2.73.3 Additional Functionality and Features

      for details).
    -

    2.74.1 Version

    +

    2.75.1 Version

    Release: 1.11
    Date:      2001, December 10 -

    2.74.2 Defects Fixed

    +

    2.75.2 Defects Fixed

    • X9.23 padding of MACs now works correctly with block size aligned data.
    • Loading a corrupted "UBER" key store would occasionally cause the @@ -2653,7 +2664,7 @@

      2.74.2 Defects Fixed

      extensions. This has been fixed.
    • The NetscapeCert type bits were reversed! This has been fixed.
    -

    2.74.3 Additional Functionality and Features

    +

    2.75.3 Additional Functionality and Features

    • The lightweight API and the JCE provider now support ElGamal.
    • X509Principal, and X509Name now supports the "DC" attribute and the @@ -2667,7 +2678,7 @@

      2.74.3 Additional Functionality and Features

    • Elliptic curve routines now handle uncompressed points as well as the compressed ones.
    -

    2.74.4 Other changes

    +

    2.75.4 Other changes

    • As the range of public key types supported has expanded the getPublicKey method on the SubjectPublicKeyInfo class is not always going to work. The @@ -2675,10 +2686,10 @@

      2.74.4 Other changes

      throws an IOException if there is a problem.
    -

    2.75.1 Version

    +

    2.76.1 Version

    Release: 1.10
    Date:      2001, October 20 -

    2.75.2 Defects Fixed

    +

    2.76.2 Defects Fixed

    • The PKCS12 Key Store now interoperates with the JDK key tool. Note: this does mean the the key name passed to the setKeyEntry calls has become significant. @@ -2686,7 +2697,7 @@

      2.75.2 Defects Fixed

      has been fixed.
    • The ASN.1 input streams now handle zero-tagged zero length objects correctly.
    -

    2.75.3 Additional Functionality and Features

    +

    2.76.3 Additional Functionality and Features

    • The JCE Provider and the lightweight API now support Serpent, CAST5, and CAST6.
    • The JCE provider and the lightweight API now has an implementation of ECIES. @@ -2696,10 +2707,10 @@

      2.75.3 Additional Functionality and Features

    • Support for the generation of PKCS10 certification requests has been added.
    -

    2.76.1 Version

    +

    2.77.1 Version

    Release: 1.09
    Date:      2001, October 6 -

    2.76.2 Defects Fixed

    +

    2.77.2 Defects Fixed

    • failure to pass in an RC5 parameters object now results in an exception at the upper level of the JCE, rather than falling over in the lightweight @@ -2712,7 +2723,7 @@

      2.76.2 Defects Fixed

    • In some cases the ASN.1 library wouldn't handle implicit tagging properly. This has been fixed.
    -

    2.76.3 Additional Functionality and Features

    +

    2.77.3 Additional Functionality and Features

    • Support for RC5-64 has been added to the JCE.
    • ISO9796-2 signatures have been added to the JCE and lightweight API. @@ -2736,10 +2747,10 @@

      2.76.3 Additional Functionality and Features

      resource hungry and faster - whether it's fast enough remains to be seen!
    -

    2.77.1 Version

    +

    2.78.1 Version

    Release: 1.08
    Date:      2001, September 9 -

    2.77.2 Defects Fixed

    +

    2.78.2 Defects Fixed

    • It wasn't possible to specify an ordering for distinguished names in X509 certificates. This is now supported. @@ -2750,7 +2761,7 @@

      2.77.2 Defects Fixed

    • The netscape certificate request class wouldn't compile under JDK 1.1. This has been fixed.
    -

    2.77.3 Additional Functionality and Features

    +

    2.78.3 Additional Functionality and Features

    • ISO 9796-1 padding is now supported with RSA in the lightweight API and the JCE. @@ -2764,10 +2775,10 @@

      2.77.3 Additional Functionality and Features

      this is fixed.
    -

    2.78.1 Version

    +

    2.79.1 Version

    Release: 1.07
    Date:      2001, July 9 -

    2.78.2 Defects Fixed

    +

    2.79.2 Defects Fixed

    • It turned out that the setOddParity method in the DESParameter class was indeed doing something odd but not what was intended. This is now @@ -2778,10 +2789,10 @@

      2.78.2 Defects Fixed

      have a look in org.bouncycastle.jce.provider.JDKKeyStore lines 201-291.
    -

    2.79.1 Version

    +

    2.80.1 Version

    Release: 1.06
    Date:      2001, July 2 -

    2.79.2 Defects Fixed

    +

    2.80.2 Defects Fixed

    • Diffie-Hellman keys are now properly serialisable as well as encodable. @@ -2803,17 +2814,17 @@

      2.79.2 Defects Fixed

    • Resetting and resusing HMacs in the lightweight and heavyweight libraries caused a NullPointer exception. This has been fixed.
    -

    2.79.3 Additional Functionality

    +

    2.80.3 Additional Functionality

    • ISO10126Padding is now recognised explicitly for block ciphers as well.
    • The Blowfish implementation is now somewhat faster.
    -

    2.80.1 Version

    +

    2.81.1 Version

    Release: 1.05
    Date:      2001, April 17 -

    2.80.2 Defects Fixed

    +

    2.81.2 Defects Fixed

    • The DESEDE key generator can now be used to generate 2-Key-DESEDE keys as well as 3-Key-DESEDE keys. @@ -2824,22 +2835,22 @@

      2.80.2 Defects Fixed

    • The ASN.1 library was skipping explicitly tagged objects of zero length. This has been fixed.
    -

    2.80.3 Additional Functionality

    +

    2.81.3 Additional Functionality

    • There is now an org.bouncycastle.jce.netscape package which has a class in for dealing with Netscape Certificate Request objects.
    -

    2.80.4 Additional Notes

    +

    2.81.4 Additional Notes

    Concerning the PKCS12 fix: in a few cases this may cause some backward compatibility issues - if this happens to you, drop us a line at feedback-crypto@bouncycastle.org and we will help you get it sorted out.

    -

    2.81.1 Version

    +

    2.82.1 Version

    Release: 1.04
    Date:      2001, March 11 -

    2.81.2 Defects Fixed

    +

    2.82.2 Defects Fixed

    • Signatures generated by other providers that include optional null parameters in the AlgorithmIdentifier are now handled correctly by the @@ -2868,7 +2879,7 @@

      2.81.2 Defects Fixed

      hash table when the hash table constructor was called. This has been fixed.
    -

    2.81.3 Additional Functionality

    +

    2.82.3 Additional Functionality

    • Added Elliptic Curve DSA (X9.62) - ECDSA - to provider and lightweight library. @@ -2880,10 +2891,10 @@

      2.81.3 Additional Functionality

    • The certificate generators now support ECDSA and DSA certs as well.
    -

    2.82.1 Version

    +

    2.83.1 Version

    Release: 1.03
    Date:      2001, January 7 -

    2.82.2 Defects Fixed

    +

    2.83.2 Defects Fixed

    • CFB and OFB modes when specified without padding would insist on input being block aligned. When specified without padding CFB and OFB now behave in a compatible @@ -2893,29 +2904,29 @@

      2.82.2 Defects Fixed

      length as the plain text.
    -

    2.83.1 Version

    +

    2.84.1 Version

    Release: 1.02
    Date:      2000, November 7 -

    2.83.2 Defects Fixed

    +

    2.84.2 Defects Fixed

    • The RSA key pair generator occasionally produced keys 1 bit under the requested size. This is now fixed.
    -

    2.84.1 Version

    +

    2.85.1 Version

    Release: 1.01
    Date:      2000, October 15 -

    2.84.2 Defects Fixed

    +

    2.85.2 Defects Fixed

    • Buffered ciphers in lightweight library were not resetting correctly on a doFinal. This has been fixed.
    -

    2.85.1 Version

    +

    2.86.1 Version

    Release: 1.00
    Date:      2000, October 13 -

    2.85.2 Defects Fixed

    +

    2.86.2 Defects Fixed

    • JDK1.2 version now works with keytool for certificate generation. @@ -2930,7 +2941,7 @@

      2.85.2 Defects Fixed

    • Some DES PBE algorithms did not set the parity correctly in generated keys, this has been fixed.
    -

    2.85.3 Additional functionality

    +

    2.86.3 Additional functionality

    • Argument validation is much improved. diff --git a/prov/src/main/jdk1.9/module-info.java b/prov/src/main/jdk1.9/module-info.java index d13c8420c1..e327cf6588 100644 --- a/prov/src/main/jdk1.9/module-info.java +++ b/prov/src/main/jdk1.9/module-info.java @@ -13,6 +13,8 @@ opens org.bouncycastle.pqc.jcajce.provider.lms to java.base; opens org.bouncycastle.pqc.jcajce.provider.falcon to java.base; opens org.bouncycastle.pqc.jcajce.provider.dilithium to java.base; + opens org.bouncycastle.pqc.jcajce.provider.mayo to java.base; + opens org.bouncycastle.pqc.jcajce.provider.snova to java.base; exports org.bouncycastle; exports org.bouncycastle.asn1; @@ -153,12 +155,14 @@ exports org.bouncycastle.pqc.jcajce.provider.hqc; exports org.bouncycastle.pqc.jcajce.provider.kyber; exports org.bouncycastle.pqc.jcajce.provider.lms; + exports org.bouncycastle.pqc.jcajce.provider.mayo; exports org.bouncycastle.pqc.jcajce.provider.mceliece; exports org.bouncycastle.pqc.jcajce.provider.ntru; exports org.bouncycastle.pqc.jcajce.provider.ntruprime; exports org.bouncycastle.pqc.jcajce.provider.newhope; exports org.bouncycastle.pqc.jcajce.provider.picnic; exports org.bouncycastle.pqc.jcajce.provider.saber; + exports org.bouncycastle.pqc.jcajce.provider.snova; exports org.bouncycastle.pqc.jcajce.provider.sphincs; exports org.bouncycastle.pqc.jcajce.provider.sphincsplus; exports org.bouncycastle.pqc.jcajce.provider.util; From 13f40a55e239822f3ef3ec2168efef9650a969ee Mon Sep 17 00:00:00 2001 From: gefeili Date: Mon, 16 Jun 2025 16:43:02 +0930 Subject: [PATCH 420/890] Fix the issue AEADBaseEngine.StreamDataOperator.processBytes does not set State.ENC_INIT to State.EncData. --- .../crypto/engines/AEADBaseEngine.java | 2 + .../bouncycastle/crypto/test/RomulusTest.java | 110 +++++++++++++++++- 2 files changed, 108 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java index 22af62b796..f83d05fd57 100644 --- a/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java +++ b/core/src/main/java/org/bouncycastle/crypto/engines/AEADBaseEngine.java @@ -568,6 +568,7 @@ protected class StreamDataOperator public int processByte(byte input, byte[] output, int outOff) { + checkData(false); ensureInitialized(); stream.write(input); m_bufPos = stream.size(); @@ -577,6 +578,7 @@ public int processByte(byte input, byte[] output, int outOff) @Override public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff) { + checkData(false); ensureInitialized(); stream.write(input, inOff, len); m_bufPos = stream.size(); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java index 4b0bf9c3e8..f04c328c07 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RomulusTest.java @@ -1,16 +1,117 @@ package org.bouncycastle.crypto.test; +import java.security.SecureRandom; + +import org.bouncycastle.crypto.CipherKeyGenerator; import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.KeyGenerationParameters; import org.bouncycastle.crypto.digests.RomulusDigest; import org.bouncycastle.crypto.engines.RomulusEngine; import org.bouncycastle.crypto.modes.AEADCipher; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.test.SimpleTest; public class RomulusTest extends SimpleTest { + /** + * Data length. + */ + private static final int DATALEN = 1025; + + /** + * AEAD length. + */ + private static final int AEADLEN = 10; + + + /** + * Check cipher. + * + * @param pCipher the cipher + */ + private static void checkCipher(final AEADCipher pCipher, + final int pNonceLen) + throws Exception + { + try + { + /* Obtain some random data */ + final byte[] myData = new byte[DATALEN]; + final SecureRandom myRandom = new SecureRandom(); + myRandom.nextBytes(myData); + + /* Obtain some random AEAD */ + final byte[] myAEAD = new byte[AEADLEN]; + myRandom.nextBytes(myAEAD); + + /* Create the Key parameters */ + final CipherKeyGenerator myGenerator = new CipherKeyGenerator(); + final KeyGenerationParameters myGenParams = new KeyGenerationParameters(myRandom, 128); + myGenerator.init(myGenParams); + final byte[] myKey = myGenerator.generateKey(); + final KeyParameter myKeyParams = new KeyParameter(myKey); + + /* Create the nonce */ + final byte[] myNonce = new byte[pNonceLen]; + myRandom.nextBytes(myNonce); + final ParametersWithIV myParams = new ParametersWithIV(myKeyParams, myNonce); + + /* Initialise the cipher for encryption */ + pCipher.init(true, myParams); + final int myMaxOutLen = pCipher.getOutputSize(DATALEN); + final byte[] myEncrypted = new byte[myMaxOutLen]; + //pCipher.processAADBytes(myAEAD, 0, AEADLEN); + int myOutLen = pCipher.processBytes(myData, 0, DATALEN, myEncrypted, 0); + int myRemaining = pCipher.getOutputSize(0); + if (myRemaining + myOutLen < myMaxOutLen) + { + /*********************************************************************/ + /* FAILS HERE */ + /*********************************************************************/ + System.out.println("Bad outputLength on encryption for " + pCipher.getAlgorithmName()); + } + int myProcessed = pCipher.doFinal(myEncrypted, myOutLen); + if (myOutLen + myProcessed != myMaxOutLen) + { + System.out.println("Bad total on encryption for " + pCipher.getAlgorithmName()); + } + + /* Note that myOutLen is too large by DATALEN */ + + /* Initialise the cipher for decryption */ + pCipher.init(false, myParams); + final int myMaxClearLen = pCipher.getOutputSize(myMaxOutLen); + final byte[] myDecrypted = new byte[myMaxClearLen]; + //pCipher.processAADBytes(myAEAD, 0, AEADLEN); + int myClearLen = pCipher.processBytes(myEncrypted, 0, myEncrypted.length, myDecrypted, 0); + myRemaining = pCipher.getOutputSize(0); + if (myRemaining + myClearLen < myMaxClearLen) + { + System.out.println("Bad outputLength on decryption for " + pCipher.getAlgorithmName()); + } + myProcessed = pCipher.doFinal(myDecrypted, myClearLen); + if (myClearLen + myProcessed != myMaxClearLen) + { + System.out.println("Bad total on decryption for " + pCipher.getAlgorithmName()); + } + final byte[] myResult = Arrays.copyOf(myDecrypted, DATALEN); + + /* Check that we have the same result */ + if (!Arrays.areEqual(myData, myResult)) + { + System.out.println("Cipher " + pCipher.getAlgorithmName() + " failed"); + } + } + catch (InvalidCipherTextException e) + { + throw new RuntimeException(e); + } + } + public String getName() { return "Romulus"; @@ -19,6 +120,7 @@ public String getName() public void performTest() throws Exception { + checkCipher(new RomulusEngine(RomulusEngine.RomulusParameters.RomulusM), 16); DigestTest.implTestVectorsDigest(this, new RomulusDigest(), "crypto/romulus", "LWC_HASH_KAT_256.txt"); DigestTest.checkDigestReset(this, new RomulusDigest()); DigestTest.implTestExceptionsAndParametersDigest(this, new RomulusDigest(), 32); @@ -153,10 +255,10 @@ private void implTestParametersEngine(RomulusEngine cipher, int keySize, int ivS } } - public static void main(String[] args) - { - runTest(new RomulusTest()); - } +// public static void main(String[] args) +// { +// runTest(new RomulusTest()); +// } } From 04a2d345181e25994f5b06d7e5810b13fd646103 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 16 Jun 2025 15:57:35 +0700 Subject: [PATCH 421/890] UTF8 perf. opts. - improved checks on surrogate pairs --- .../java/org/bouncycastle/util/Strings.java | 75 ++++++++++++------- .../org/bouncycastle/util/encoders/UTF8.java | 4 +- 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/util/Strings.java b/core/src/main/java/org/bouncycastle/util/Strings.java index a568149791..41d318bf76 100644 --- a/core/src/main/java/org/bouncycastle/util/Strings.java +++ b/core/src/main/java/org/bouncycastle/util/Strings.java @@ -91,54 +91,75 @@ public static byte[] toUTF8ByteArray(char[] string) public static void toUTF8ByteArray(char[] string, OutputStream sOut) throws IOException { - char[] c = string; - int i = 0; + if (string.length < 1) + { + return; + } + + byte[] buf = new byte[64]; - while (i < c.length) + int bufPos = 0, i = 0; + do { - char ch = c[i]; + int ch = string[i++]; if (ch < 0x0080) { - sOut.write(ch); + buf[bufPos++] = (byte)ch; } else if (ch < 0x0800) { - sOut.write(0xc0 | (ch >> 6)); - sOut.write(0x80 | (ch & 0x3f)); + buf[bufPos++] = (byte)(0xC0 | (ch >> 6)); + buf[bufPos++] = (byte)(0x80 | (ch & 0x3F)); } // surrogate pair else if (ch >= 0xD800 && ch <= 0xDFFF) { - // in error - can only happen, if the Java String class has a - // bug. - if (i + 1 >= c.length) + /* + * Various checks that shouldn't fail unless the Java String class has a bug. + */ + int W1 = ch; + if (W1 > 0xDBFF) { - throw new IllegalStateException("invalid UTF-16 codepoint"); + throw new IllegalStateException("invalid UTF-16 high surrogate"); } - char W1 = ch; - ch = c[++i]; - char W2 = ch; - // in error - can only happen, if the Java String class has a - // bug. - if (W1 > 0xDBFF) + + if (i >= string.length) + { + throw new IllegalStateException("invalid UTF-16 codepoint (truncated surrogate pair)"); + } + + int W2 = string[i++]; + if (W2 < 0xDC00 || W2 > 0xDFFF) { - throw new IllegalStateException("invalid UTF-16 codepoint"); + throw new IllegalStateException("invalid UTF-16 low surrogate"); } + int codePoint = (((W1 & 0x03FF) << 10) | (W2 & 0x03FF)) + 0x10000; - sOut.write(0xf0 | (codePoint >> 18)); - sOut.write(0x80 | ((codePoint >> 12) & 0x3F)); - sOut.write(0x80 | ((codePoint >> 6) & 0x3F)); - sOut.write(0x80 | (codePoint & 0x3F)); + buf[bufPos++] = (byte)(0xF0 | (codePoint >> 18)); + buf[bufPos++] = (byte)(0x80 | ((codePoint >> 12) & 0x3F)); + buf[bufPos++] = (byte)(0x80 | ((codePoint >> 6) & 0x3F)); + buf[bufPos++] = (byte)(0x80 | (codePoint & 0x3F)); } else { - sOut.write(0xe0 | (ch >> 12)); - sOut.write(0x80 | ((ch >> 6) & 0x3F)); - sOut.write(0x80 | (ch & 0x3F)); + buf[bufPos++] = (byte)(0xE0 | (ch >> 12)); + buf[bufPos++] = (byte)(0x80 | ((ch >> 6) & 0x3F)); + buf[bufPos++] = (byte)(0x80 | (ch & 0x3F)); + } + + if (bufPos + 4 > buf.length) + { + sOut.write(buf, 0, bufPos); + bufPos = 0; } + } + while (i < string.length); - i++; + if (bufPos > 0) + { + sOut.write(buf, 0, bufPos); +// bufPos = 0; } } @@ -382,6 +403,4 @@ public String[] toStringArray(int from, int to) return strs; } } - - } diff --git a/core/src/main/java/org/bouncycastle/util/encoders/UTF8.java b/core/src/main/java/org/bouncycastle/util/encoders/UTF8.java index cd9073d39f..e339c7249c 100644 --- a/core/src/main/java/org/bouncycastle/util/encoders/UTF8.java +++ b/core/src/main/java/org/bouncycastle/util/encoders/UTF8.java @@ -184,8 +184,8 @@ public static int transcodeToUTF16(byte[] utf8, int utf8Off, int utf8Length, cha } // Code points above U+10FFFF are caught by the DFA - utf16[j++] = (char)(0xD7C0 + (codePoint >>> 10)); - utf16[j++] = (char)(0xDC00 | (codePoint & 0x3FF)); + utf16[j++] = (char)(0xD7C0 + (codePoint >>> 10)); // [0xD800, 0xDBFF] high surrogate (W1) + utf16[j++] = (char)(0xDC00 | (codePoint & 0x3FF)); // [0xDC00, 0xDFFF] low surrogate (W2) } } From b06b2886b0e89ed64bb9e133a945ec0efef4b55c Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 18 Jun 2025 16:17:24 +0700 Subject: [PATCH 422/890] Refactoring in mls.crypto.bc --- .../bouncycastle/mls/crypto/bc/BcMlsAead.java | 60 ++++++++-------- .../mls/crypto/bc/BcMlsSigner.java | 69 ++++--------------- 2 files changed, 42 insertions(+), 87 deletions(-) diff --git a/mls/src/main/java/org/bouncycastle/mls/crypto/bc/BcMlsAead.java b/mls/src/main/java/org/bouncycastle/mls/crypto/bc/BcMlsAead.java index 39fdddd6b2..9664d16760 100644 --- a/mls/src/main/java/org/bouncycastle/mls/crypto/bc/BcMlsAead.java +++ b/mls/src/main/java/org/bouncycastle/mls/crypto/bc/BcMlsAead.java @@ -1,6 +1,5 @@ package org.bouncycastle.mls.crypto.bc; -import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.InvalidCipherTextException; import org.bouncycastle.crypto.engines.AESEngine; import org.bouncycastle.crypto.hpke.HPKE; @@ -10,12 +9,13 @@ import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; import org.bouncycastle.mls.crypto.MlsAead; +import org.bouncycastle.util.Arrays; public class BcMlsAead implements MlsAead { - AEADCipher cipher; private final short aeadId; + private final AEADCipher cipher; public BcMlsAead(short aeadId) { @@ -25,12 +25,14 @@ public BcMlsAead(short aeadId) { case HPKE.aead_AES_GCM128: case HPKE.aead_AES_GCM256: - cipher = new GCMBlockCipher(new AESEngine()); + cipher = GCMBlockCipher.newInstance(AESEngine.newInstance()); break; case HPKE.aead_CHACHA20_POLY1305: cipher = new ChaCha20Poly1305(); break; case HPKE.aead_EXPORT_ONLY: + default: + cipher = null; break; } } @@ -48,18 +50,6 @@ public int getKeySize() return -1; } - private int getTagSize() - { - switch (aeadId) - { - case HPKE.aead_AES_GCM128: - case HPKE.aead_AES_GCM256: - case HPKE.aead_CHACHA20_POLY1305: - return 16; - } - return -1; - } - public int getNonceSize() { switch (aeadId) @@ -75,30 +65,34 @@ public int getNonceSize() public byte[] open(byte[] key, byte[] nonce, byte[] aad, byte[] ct) throws InvalidCipherTextException { - CipherParameters params = new ParametersWithIV(new KeyParameter(key), nonce); - cipher.init(false, params); + return crypt(false, key, nonce, aad, ct); + } + + public byte[] seal(byte[] key, byte[] nonce, byte[] aad, byte[] pt) + throws InvalidCipherTextException + { + return crypt(true, key, nonce, aad, pt); + } + + private byte[] crypt(boolean forEncryption, byte[] key, byte[] nonce, byte[] aad, byte[] input) + throws InvalidCipherTextException + { + cipher.init(forEncryption, new ParametersWithIV(new KeyParameter(key), nonce)); + if (aad != null) { cipher.processAADBytes(aad, 0, aad.length); } - byte[] pt = new byte[cipher.getOutputSize(ct.length)]; + byte[] output = new byte[cipher.getOutputSize(input.length)]; + int len = cipher.processBytes(input, 0, input.length, output, 0); + len += cipher.doFinal(output, len); - int len = cipher.processBytes(ct, 0, ct.length, pt, 0); - len += cipher.doFinal(pt, len); - return pt; - } - - public byte[] seal(byte[] key, byte[] nonce, byte[] aad, byte[] pt) - throws InvalidCipherTextException - { - CipherParameters params = new ParametersWithIV(new KeyParameter(key), nonce); - cipher.init(true, params); - cipher.processAADBytes(aad, 0, aad.length); + if (len < output.length) + { + return Arrays.copyOf(output, len); + } - byte[] ct = new byte[cipher.getOutputSize(pt.length)]; - int len = cipher.processBytes(pt, 0, pt.length, ct, 0); - cipher.doFinal(ct, len); - return ct; + return output; } } diff --git a/mls/src/main/java/org/bouncycastle/mls/crypto/bc/BcMlsSigner.java b/mls/src/main/java/org/bouncycastle/mls/crypto/bc/BcMlsSigner.java index 64a91b4683..f95947c9f7 100644 --- a/mls/src/main/java/org/bouncycastle/mls/crypto/bc/BcMlsSigner.java +++ b/mls/src/main/java/org/bouncycastle/mls/crypto/bc/BcMlsSigner.java @@ -4,6 +4,7 @@ import java.math.BigInteger; import java.security.SecureRandom; +import org.bouncycastle.asn1.sec.SECObjectIdentifiers; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.Signer; @@ -14,8 +15,8 @@ import org.bouncycastle.crypto.generators.Ed25519KeyPairGenerator; import org.bouncycastle.crypto.generators.Ed448KeyPairGenerator; import org.bouncycastle.crypto.params.AsymmetricKeyParameter; -import org.bouncycastle.crypto.params.ECDomainParameters; import org.bouncycastle.crypto.params.ECKeyGenerationParameters; +import org.bouncycastle.crypto.params.ECNamedDomainParameters; import org.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.bouncycastle.crypto.params.ECPublicKeyParameters; import org.bouncycastle.crypto.params.Ed25519KeyGenerationParameters; @@ -30,72 +31,36 @@ import org.bouncycastle.crypto.signers.ECDSASigner; import org.bouncycastle.crypto.signers.Ed25519Signer; import org.bouncycastle.crypto.signers.Ed448Signer; -import org.bouncycastle.math.ec.ECCurve; import org.bouncycastle.math.ec.ECPoint; import org.bouncycastle.math.ec.FixedPointCombMultiplier; -import org.bouncycastle.math.ec.custom.sec.SecP256R1Curve; -import org.bouncycastle.math.ec.custom.sec.SecP384R1Curve; -import org.bouncycastle.math.ec.custom.sec.SecP521R1Curve; import org.bouncycastle.mls.codec.MLSOutputStream; import org.bouncycastle.mls.crypto.MlsCipherSuite; import org.bouncycastle.mls.crypto.MlsSigner; -import org.bouncycastle.util.encoders.Hex; public class BcMlsSigner implements MlsSigner { Signer signer; - ECDomainParameters domainParams; + ECNamedDomainParameters domainParams; int sigID; public BcMlsSigner(int sigID) { this.sigID = sigID; - ECCurve curve; switch (sigID) { case ecdsa_secp256r1_sha256: signer = new DSADigestSigner(new ECDSASigner(), new SHA256Digest()); - curve = new SecP256R1Curve(); - domainParams = new ECDomainParameters( - curve, - curve.createPoint( - new BigInteger(1, Hex.decode("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296")), - new BigInteger(1, Hex.decode("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5")) - ), - curve.getOrder(), - curve.getCofactor(), - Hex.decode("c49d360886e704936a6678e1139d26b7819f7e90") - ); + domainParams = ECNamedDomainParameters.lookup(SECObjectIdentifiers.secp256r1); break; case ecdsa_secp521r1_sha512: signer = new DSADigestSigner(new ECDSASigner(), new SHA512Digest()); - curve = new SecP521R1Curve(); - domainParams = new ECDomainParameters( - curve, - curve.createPoint( - new BigInteger("c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66", 16), - new BigInteger("11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", 16) - ), - curve.getOrder(), - curve.getCofactor(), - Hex.decode("d09e8800291cb85396cc6717393284aaa0da64ba") - ); + domainParams = ECNamedDomainParameters.lookup(SECObjectIdentifiers.secp521r1); break; case ecdsa_secp384r1_sha384: signer = new DSADigestSigner(new ECDSASigner(), new SHA384Digest()); - curve = new SecP384R1Curve(); - domainParams = new ECDomainParameters( - curve, - curve.createPoint( - new BigInteger(1, Hex.decode("aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7")), - new BigInteger(1, Hex.decode("3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f")) - ), - curve.getOrder(), - curve.getCofactor(), - Hex.decode("a335926aa319a27a1d00896a6773a4827acdac73") - ); + domainParams = ECNamedDomainParameters.lookup(SECObjectIdentifiers.secp384r1); break; case ed25519: signer = new Ed25519Signer(); @@ -115,20 +80,16 @@ public AsymmetricCipherKeyPair generateSignatureKeyPair() case ecdsa_secp521r1_sha512: case ecdsa_secp384r1_sha384: ECKeyPairGenerator pGen = new ECKeyPairGenerator(); - ECKeyGenerationParameters genParam = new ECKeyGenerationParameters( - domainParams, - random); - pGen.init(genParam); + pGen.init(new ECKeyGenerationParameters(domainParams, random)); return pGen.generateKeyPair(); - case ed448: - Ed448KeyPairGenerator kpg448 = new Ed448KeyPairGenerator(); - kpg448.init(new Ed448KeyGenerationParameters(random)); - return kpg448.generateKeyPair(); - case ed25519: Ed25519KeyPairGenerator kpg25519 = new Ed25519KeyPairGenerator(); kpg25519.init(new Ed25519KeyGenerationParameters(random)); return kpg25519.generateKeyPair(); + case ed448: + Ed448KeyPairGenerator kpg448 = new Ed448KeyPairGenerator(); + kpg448.init(new Ed448KeyGenerationParameters(random)); + return kpg448.generateKeyPair(); default: throw new IllegalStateException("invalid sig algorithm"); } @@ -142,10 +103,10 @@ public byte[] serializePublicKey(AsymmetricKeyParameter key) case ecdsa_secp521r1_sha512: case ecdsa_secp384r1_sha384: return ((ECPublicKeyParameters)key).getQ().getEncoded(false); - case ed448: - return ((Ed448PublicKeyParameters)key).getEncoded(); case ed25519: return ((Ed25519PublicKeyParameters)key).getEncoded(); + case ed448: + return ((Ed448PublicKeyParameters)key).getEncoded(); default: throw new IllegalStateException("invalid sig algorithm"); } @@ -160,10 +121,10 @@ public byte[] serializePrivateKey(AsymmetricKeyParameter key) case ecdsa_secp521r1_sha512: case ecdsa_secp384r1_sha384: return ((ECPrivateKeyParameters)key).getD().toByteArray(); - case ed448: - return ((Ed448PrivateKeyParameters)key).getEncoded(); case ed25519: return ((Ed25519PrivateKeyParameters)key).getEncoded(); + case ed448: + return ((Ed448PrivateKeyParameters)key).getEncoded(); default: throw new IllegalStateException("invalid sig algorithm"); } From 98044cc2fe7b2737f870b2b995503ce480383d0a Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 18 Jun 2025 16:26:27 +0700 Subject: [PATCH 423/890] Correct usage of compareTo return values --- .../crypto/agreement/ecjpake/ECJPAKEUtil.java | 8 ++++---- .../crypto/agreement/jpake/JPAKEPrimeOrderGroup.java | 2 +- .../bouncycastle/crypto/agreement/jpake/JPAKEUtil.java | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKEUtil.java b/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKEUtil.java index bc103cf597..02714873d2 100644 --- a/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKEUtil.java +++ b/core/src/main/java/org/bouncycastle/crypto/agreement/ecjpake/ECJPAKEUtil.java @@ -219,10 +219,10 @@ public static void validateZeroKnowledgeProof( ECPoint x_normalized = X.normalize(); // 2. Check x and y coordinates are in Fq, i.e., x, y in [0, q-1] - if (x_normalized.getAffineXCoord().toBigInteger().compareTo(BigInteger.ZERO) == -1 || - x_normalized.getAffineXCoord().toBigInteger().compareTo(q.subtract(BigInteger.ONE)) == 1 || - x_normalized.getAffineYCoord().toBigInteger().compareTo(BigInteger.ZERO) == -1 || - x_normalized.getAffineYCoord().toBigInteger().compareTo(q.subtract(BigInteger.ONE)) == 1) + if (x_normalized.getAffineXCoord().toBigInteger().signum() < 0 || + x_normalized.getAffineXCoord().toBigInteger().compareTo(q) >= 0 || + x_normalized.getAffineYCoord().toBigInteger().signum() < 0 || + x_normalized.getAffineYCoord().toBigInteger().compareTo(q) >= 0) { throw new CryptoException("Zero-knowledge proof validation failed: x and y are not in the field"); } diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java index c3c9561fdf..e39b3fc6bd 100644 --- a/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java +++ b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEPrimeOrderGroup.java @@ -70,7 +70,7 @@ public JPAKEPrimeOrderGroup(BigInteger p, BigInteger q, BigInteger g) { throw new IllegalArgumentException("p-1 must be evenly divisible by q"); } - if (g.compareTo(BigInteger.valueOf(2)) == -1 || g.compareTo(p.subtract(JPAKEUtil.ONE)) == 1) + if (g.compareTo(BigInteger.valueOf(2)) < 0 || g.compareTo(p) >= 0) { throw new IllegalArgumentException("g must be in [2, p-1]"); } diff --git a/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEUtil.java b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEUtil.java index 3639f5c4b6..f2942556a6 100644 --- a/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEUtil.java +++ b/core/src/main/java/org/bouncycastle/crypto/agreement/jpake/JPAKEUtil.java @@ -252,15 +252,15 @@ public static void validateZeroKnowledgeProof( BigInteger r = zeroKnowledgeProof[1]; BigInteger h = calculateHashForZeroKnowledgeProof(g, gv, gx, participantId, digest); - if (!(gx.compareTo(ZERO) == 1 && // g^x > 0 - gx.compareTo(p) == -1 && // g^x < p - gx.modPow(q, p).compareTo(ONE) == 0 && // g^x^q mod q = 1 + if (!(gx.signum() > 0 && // g^x > 0 + gx.compareTo(p) < 0 && // g^x < p + gx.modPow(q, p).equals(ONE) && // g^x^q mod q = 1 /* * Below, I took an straightforward way to compute g^r * g^x^h, * which needs 2 exp. Using a simultaneous computation technique * would only need 1 exp. */ - g.modPow(r, p).multiply(gx.modPow(h, p)).mod(p).compareTo(gv) == 0)) // g^v=g^r * g^x^h + g.modPow(r, p).multiply(gx.modPow(h, p)).mod(p).equals(gv))) // g^v=g^r * g^x^h { throw new CryptoException("Zero-knowledge proof validation failed"); } From eb3c9e18ddcce3eb49b7399d8abb204b26a63bfd Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 18 Jun 2025 17:42:33 +0700 Subject: [PATCH 424/890] Update deprecated GCMBlockCipher usages --- .../java/org/bouncycastle/crypto/test/GCMTest.java | 11 ++++++++--- .../crypto/test/speedy/MacThroughputTest.java | 2 +- .../bouncycastle/jcajce/provider/symmetric/ARIA.java | 4 ++-- .../bouncycastle/jcajce/provider/symmetric/CAST6.java | 2 +- .../jcajce/provider/symmetric/Camellia.java | 2 +- .../jcajce/provider/symmetric/Noekeon.java | 2 +- .../bouncycastle/jcajce/provider/symmetric/RC6.java | 2 +- .../bouncycastle/jcajce/provider/symmetric/SEED.java | 2 +- .../bouncycastle/jcajce/provider/symmetric/SM4.java | 2 +- .../jcajce/provider/symmetric/Serpent.java | 4 ++-- .../jcajce/provider/symmetric/Twofish.java | 2 +- .../provider/symmetric/util/BaseBlockCipher.java | 2 +- 12 files changed, 21 insertions(+), 16 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/GCMTest.java b/core/src/test/java/org/bouncycastle/crypto/test/GCMTest.java index f1037c25a1..10305919be 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/GCMTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/GCMTest.java @@ -317,7 +317,7 @@ public void performTest() throws Exception private void testResetBehavior() throws Exception { - GCMBlockCipher gcm = new GCMBlockCipher(createAESEngine()); + GCMModeCipher gcm = createGCM(createAESEngine()); SecureRandom rnd = new SecureRandom(); int[] ivLens = new int[]{10,12,16}; @@ -372,13 +372,18 @@ protected BlockCipher createAESEngine() return AESEngine.newInstance(); } + protected GCMModeCipher createGCM(BlockCipher cipher) + { + return GCMBlockCipher.newInstance(cipher); + } + private void testExceptions() throws InvalidCipherTextException { - GCMBlockCipher gcm = new GCMBlockCipher(createAESEngine()); + GCMModeCipher gcm = createGCM(createAESEngine()); try { - gcm = new GCMBlockCipher(new DESEngine()); + gcm = createGCM(new DESEngine()); fail("incorrect block size not picked up"); } catch (IllegalArgumentException e) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/speedy/MacThroughputTest.java b/core/src/test/java/org/bouncycastle/crypto/test/speedy/MacThroughputTest.java index 81401c3966..09df661763 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/speedy/MacThroughputTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/speedy/MacThroughputTest.java @@ -60,7 +60,7 @@ public static void main(String[] args) testMac(new SkeinMac(SkeinMac.SKEIN_512, 128), new KeyParameter(generateNonce(64)), 2); testMac(new SipHash(), new KeyParameter(generateNonce(16)), 1); testMac(new CMac(AESEngine.newInstance()), new KeyParameter(generateNonce(16)), 3); - testMac(new GMac(new GCMBlockCipher(AESEngine.newInstance())), new ParametersWithIV(new KeyParameter( + testMac(new GMac(GCMBlockCipher.newInstance(AESEngine.newInstance())), new ParametersWithIV(new KeyParameter( generateNonce(16)), generateNonce(16)), 5); testMac(new Poly1305(new NullEngine(16)), new ParametersWithIV(generatePoly1305Key(), generateNonce(16)), 1); testMac(new Poly1305(AESEngine.newInstance()), new ParametersWithIV(generatePoly1305Key(), generateNonce(16)), 1); diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ARIA.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ARIA.java index 9f851a19ef..680af5e81a 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ARIA.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ARIA.java @@ -102,7 +102,7 @@ static public class GCM { public GCM() { - super(new GCMBlockCipher(new ARIAEngine())); + super(GCMBlockCipher.newInstance(new ARIAEngine())); } } @@ -138,7 +138,7 @@ public static class GMAC { public GMAC() { - super(new GMac(new GCMBlockCipher(new ARIAEngine()))); + super(new GMac(GCMBlockCipher.newInstance(new ARIAEngine()))); } } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java index b9d683ea3d..6d72dd6550 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java @@ -48,7 +48,7 @@ public static class GMAC { public GMAC() { - super(new GMac(new GCMBlockCipher(new CAST6Engine()))); + super(new GMac(GCMBlockCipher.newInstance(new CAST6Engine()))); } } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java index d4bab38db6..88e35e8da5 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java @@ -135,7 +135,7 @@ public static class GMAC { public GMAC() { - super(new GMac(new GCMBlockCipher(new CamelliaEngine()))); + super(new GMac(GCMBlockCipher.newInstance(new CamelliaEngine()))); } } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java index 1e32091ab4..6deb861060 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java @@ -57,7 +57,7 @@ public static class GMAC { public GMAC() { - super(new GMac(new GCMBlockCipher(new NoekeonEngine()))); + super(new GMac(GCMBlockCipher.newInstance(new NoekeonEngine()))); } } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java index 81271ecd7e..2f479f5884 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java @@ -79,7 +79,7 @@ public static class GMAC { public GMAC() { - super(new GMac(new GCMBlockCipher(new RC6Engine()))); + super(new GMac(GCMBlockCipher.newInstance(new RC6Engine()))); } } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java index c1b6bf9d4b..db02541e72 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java @@ -90,7 +90,7 @@ public static class GMAC { public GMAC() { - super(new GMac(new GCMBlockCipher(new SEEDEngine()))); + super(new GMac(GCMBlockCipher.newInstance(new SEEDEngine()))); } } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java index 6e0202fa2f..e416a02817 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SM4.java @@ -67,7 +67,7 @@ public static class GMAC { public GMAC() { - super(new GMac(new GCMBlockCipher(new SM4Engine()))); + super(new GMac(GCMBlockCipher.newInstance(new SM4Engine()))); } } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java index a493aba7b6..25725ef2d3 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java @@ -231,7 +231,7 @@ public static class SerpentGMAC { public SerpentGMAC() { - super(new GMac(new GCMBlockCipher(new SerpentEngine()))); + super(new GMac(GCMBlockCipher.newInstance(new SerpentEngine()))); } } @@ -240,7 +240,7 @@ public static class TSerpentGMAC { public TSerpentGMAC() { - super(new GMac(new GCMBlockCipher(new TnepresEngine()))); + super(new GMac(GCMBlockCipher.newInstance(new TnepresEngine()))); } } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java index a759ccd20d..dfefa0c0f5 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java @@ -50,7 +50,7 @@ public static class GMAC { public GMAC() { - super(new GMac(new GCMBlockCipher(new TwofishEngine()))); + super(new GMac(GCMBlockCipher.newInstance(new TwofishEngine()))); } } diff --git a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java index 25c201ed61..1e0caa1828 100644 --- a/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java +++ b/prov/src/main/jdk1.3/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java @@ -505,7 +505,7 @@ else if (modeName.equals("GCM")) else { ivLength = 12; - cipher = new AEADGenericBlockCipher(new GCMBlockCipher(baseEngine)); + cipher = new AEADGenericBlockCipher(GCMBlockCipher.newInstance(baseEngine)); } } else From b3c70fa0584760eb3e272f3469e6c94e2022ec5c Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 18 Jun 2025 18:49:37 +0700 Subject: [PATCH 425/890] Update deprecated GCMBlockCipher usages --- .../java/org/bouncycastle/crypto/test/CipherStreamTest.java | 2 +- core/src/test/java/org/bouncycastle/crypto/test/SM4Test.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CipherStreamTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CipherStreamTest.java index 69d134ec33..20941ce155 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CipherStreamTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CipherStreamTest.java @@ -552,7 +552,7 @@ private void testModes(BlockCipher cipher1, BlockCipher cipher2, int keySize) { testMode(CCMBlockCipher.newInstance(cipher1), new ParametersWithIV(key, new byte[7])); // TODO: need to have a GCM safe version of testMode. -// testMode(new GCMBlockCipher(cipher1), withIv); +// testMode(GCMBlockCipher.newInstance(cipher1), withIv); testMode(new OCBBlockCipher(cipher1, cipher2), new ParametersWithIV(key, new byte[15])); } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SM4Test.java b/core/src/test/java/org/bouncycastle/crypto/test/SM4Test.java index 680f2ad80c..1964c68259 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/SM4Test.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SM4Test.java @@ -6,6 +6,7 @@ import org.bouncycastle.crypto.modes.AEADBlockCipher; import org.bouncycastle.crypto.modes.CCMBlockCipher; import org.bouncycastle.crypto.modes.GCMBlockCipher; +import org.bouncycastle.crypto.modes.GCMModeCipher; import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.util.encoders.Hex; @@ -91,8 +92,8 @@ private void gcmTest() + "A56834CBCF98C397B4024A2691233B8D"); byte[] tag = Hex.decode("83DE3541E4C2B58177E065A9BF7B62EC"); - GCMBlockCipher encCipher = new GCMBlockCipher(new SM4Engine()); - GCMBlockCipher decCipher = new GCMBlockCipher(new SM4Engine()); + GCMModeCipher encCipher = GCMBlockCipher.newInstance(new SM4Engine()); + GCMModeCipher decCipher = GCMBlockCipher.newInstance(new SM4Engine()); checkTestCase(encCipher, decCipher, "1", key, iv, aad, pt, ct, tag); } From 3525580e8f5f7a1aaa7b4b400f6225d53bd2bbaf Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 18 Jun 2025 19:42:41 +0700 Subject: [PATCH 426/890] Update deprecated CCMBlockCipher usages --- .../crypto/modes/CCMModeCipher.java | 6 +++++ .../org/bouncycastle/crypto/test/CCMTest.java | 22 ++++++++++++------- .../org/bouncycastle/crypto/test/SM4Test.java | 5 +++-- .../jcajce/provider/symmetric/ARIA.java | 2 +- .../tls/crypto/impl/bc/BcTlsCrypto.java | 2 +- 5 files changed, 25 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/modes/CCMModeCipher.java b/core/src/main/java/org/bouncycastle/crypto/modes/CCMModeCipher.java index d96ac05aee..54b1e371fb 100644 --- a/core/src/main/java/org/bouncycastle/crypto/modes/CCMModeCipher.java +++ b/core/src/main/java/org/bouncycastle/crypto/modes/CCMModeCipher.java @@ -3,4 +3,10 @@ public interface CCMModeCipher extends AEADBlockCipher { + // TODO Add these so that all usages of CCMBlockCipher can be replaced by CCMModeCipher +// byte[] processPacket(byte[] in, int inOff, int inLen) +// throws IllegalStateException, InvalidCipherTextException; +// +// int processPacket(byte[] in, int inOff, int inLen, byte[] output, int outOff) +// throws IllegalStateException, InvalidCipherTextException, DataLengthException; } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CCMTest.java b/core/src/test/java/org/bouncycastle/crypto/test/CCMTest.java index b6511e2ae5..5a79a811cd 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CCMTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CCMTest.java @@ -4,6 +4,7 @@ import org.bouncycastle.crypto.engines.AESEngine; import org.bouncycastle.crypto.engines.DESEngine; import org.bouncycastle.crypto.modes.CCMBlockCipher; +import org.bouncycastle.crypto.modes.CCMModeCipher; import org.bouncycastle.crypto.params.AEADParameters; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.crypto.params.ParametersWithIV; @@ -56,6 +57,8 @@ public class CCMTest public void performTest() throws Exception { + // TODO Need to resolve dependency on processPacket methods (add them to CCMModeCipher?) +// CCMModeCipher ccm = CCMBlockCipher.newInstance(AESEngine.newInstance()); CCMBlockCipher ccm = new CCMBlockCipher(AESEngine.newInstance()); checkVectors(0, ccm, K1, 32, N1, A1, P1, T1, C1); @@ -131,6 +134,8 @@ public void performTest() try { + // TODO Need to resolve dependency on processPacket methods (add them to CCMModeCipher?) +// ccm = CCMBlockCipher.newInstance(new DESEngine()); ccm = new CCMBlockCipher(new DESEngine()); fail("incorrect block size not picked up"); @@ -198,12 +203,13 @@ public void performTest() } } - AEADTestUtil.testReset(this, new CCMBlockCipher(AESEngine.newInstance()), new CCMBlockCipher(AESEngine.newInstance()), new AEADParameters(new KeyParameter(K1), 32, N2)); + AEADTestUtil.testReset(this, CCMBlockCipher.newInstance(AESEngine.newInstance()), + CCMBlockCipher.newInstance(AESEngine.newInstance()), new AEADParameters(new KeyParameter(K1), 32, N2)); AEADTestUtil.testTampering(this, ccm, new AEADParameters(new KeyParameter(K1), 32, N2)); - AEADTestUtil.testOutputSizes(this, new CCMBlockCipher(AESEngine.newInstance()), new AEADParameters( - new KeyParameter(K1), 32, N2)); - AEADTestUtil.testBufferSizeChecks(this, new CCMBlockCipher(AESEngine.newInstance()), new AEADParameters( - new KeyParameter(K1), 32, N2)); + AEADTestUtil.testOutputSizes(this, CCMBlockCipher.newInstance(AESEngine.newInstance()), + new AEADParameters(new KeyParameter(K1), 32, N2)); + AEADTestUtil.testBufferSizeChecks(this, CCMBlockCipher.newInstance(AESEngine.newInstance()), + new AEADParameters(new KeyParameter(K1), 32, N2)); } private boolean isEqual(byte[] exp, byte[] other, int off) @@ -221,7 +227,7 @@ private boolean isEqual(byte[] exp, byte[] other, int off) private void checkVectors( int count, - CCMBlockCipher ccm, + CCMModeCipher ccm, byte[] k, int macSize, byte[] n, @@ -244,7 +250,7 @@ private void checkVectors( private void checkVectors( int count, - CCMBlockCipher ccm, + CCMModeCipher ccm, String additionalDataType, byte[] k, int macSize, @@ -307,7 +313,7 @@ private void checkVectors( private void ivParamTest( int count, - CCMBlockCipher ccm, + CCMModeCipher ccm, byte[] k, byte[] n) throws InvalidCipherTextException diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SM4Test.java b/core/src/test/java/org/bouncycastle/crypto/test/SM4Test.java index 1964c68259..a297b6071a 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/SM4Test.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SM4Test.java @@ -5,6 +5,7 @@ import org.bouncycastle.crypto.engines.SM4Engine; import org.bouncycastle.crypto.modes.AEADBlockCipher; import org.bouncycastle.crypto.modes.CCMBlockCipher; +import org.bouncycastle.crypto.modes.CCMModeCipher; import org.bouncycastle.crypto.modes.GCMBlockCipher; import org.bouncycastle.crypto.modes.GCMModeCipher; import org.bouncycastle.crypto.params.AEADParameters; @@ -114,8 +115,8 @@ private void ccmTest() + "ED31A2F04476C18BB40C84A74B97DC5B"); byte[] tag = Hex.decode("fe26a58f94552a8d533b5b6b261c9cd8"); - CCMBlockCipher encCipher = new CCMBlockCipher(new SM4Engine()); - CCMBlockCipher decCipher = new CCMBlockCipher(new SM4Engine()); + CCMModeCipher encCipher = CCMBlockCipher.newInstance(new SM4Engine()); + CCMModeCipher decCipher = CCMBlockCipher.newInstance(new SM4Engine()); checkTestCase(encCipher, decCipher, "2", key, iv, aad, pt, ct, tag); } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ARIA.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ARIA.java index 680af5e81a..dc2b019dda 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ARIA.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ARIA.java @@ -93,7 +93,7 @@ static public class CCM { public CCM() { - super(new CCMBlockCipher(new ARIAEngine()), false, 12); + super(CCMBlockCipher.newInstance(new ARIAEngine()), false, 12); } } diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java index 0b9d6cda35..becbc8898f 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java @@ -714,7 +714,7 @@ protected BlockCipher createSM4Engine() protected AEADBlockCipher createCCMMode(BlockCipher engine) { - return new CCMBlockCipher(engine); + return CCMBlockCipher.newInstance(engine); } protected AEADBlockCipher createGCMMode(BlockCipher engine) From 7b237b3d21d47ccfbaf059f7e33d717c90e6d96d Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 18 Jun 2025 19:46:17 +0700 Subject: [PATCH 427/890] Refactor SM2Signer.init --- .../crypto/signers/SM2Signer.java | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/signers/SM2Signer.java b/core/src/main/java/org/bouncycastle/crypto/signers/SM2Signer.java index eefddc7d79..aacf8f7e5f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/signers/SM2Signer.java +++ b/core/src/main/java/org/bouncycastle/crypto/signers/SM2Signer.java @@ -1,6 +1,7 @@ package org.bouncycastle.crypto.signers; import java.math.BigInteger; +import java.security.SecureRandom; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.CryptoException; @@ -92,35 +93,37 @@ public void init(boolean forSigning, CipherParameters param) if (forSigning) { + SecureRandom random = null; if (baseParam instanceof ParametersWithRandom) { - ParametersWithRandom rParam = (ParametersWithRandom)baseParam; - - ecKey = (ECKeyParameters)rParam.getParameters(); - ecParams = ecKey.getParameters(); - kCalculator.init(ecParams.getN(), rParam.getRandom()); - } - else - { - ecKey = (ECKeyParameters)baseParam; - ecParams = ecKey.getParameters(); - kCalculator.init(ecParams.getN(), CryptoServicesRegistrar.getSecureRandom()); + ParametersWithRandom withRandom = (ParametersWithRandom)baseParam; + baseParam = withRandom.getParameters(); + random = withRandom.getRandom(); } - BigInteger d = ((ECPrivateKeyParameters)ecKey).getD(); - BigInteger nSub1 = ecParams.getN().subtract(BigIntegers.ONE); + ECPrivateKeyParameters ecPrivateKey = (ECPrivateKeyParameters)baseParam; + + ecKey = ecPrivateKey; + ecParams = ecPrivateKey.getParameters(); - if (d.compareTo(ONE) < 0 || d.compareTo(nSub1) >= 0) + BigInteger d = ecPrivateKey.getD(); + BigInteger n = ecParams.getN(); + + if (d.compareTo(ONE) < 0 || d.compareTo(n.subtract(ONE)) >= 0) { throw new IllegalArgumentException("SM2 private key out of range"); } + + kCalculator.init(n, CryptoServicesRegistrar.getSecureRandom(random)); pubPoint = createBasePointMultiplier().multiply(ecParams.getG(), d).normalize(); } else { - ecKey = (ECKeyParameters)baseParam; - ecParams = ecKey.getParameters(); - pubPoint = ((ECPublicKeyParameters)ecKey).getQ(); + ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters)baseParam; + + ecKey = ecPublicKey; + ecParams = ecPublicKey.getParameters(); + pubPoint = ecPublicKey.getQ(); } CryptoServicesRegistrar.checkConstraints(Utils.getDefaultProperties("ECNR", ecKey, forSigning)); From af10bcc3b546682861f0be1348ab1e09366feafa Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 18 Jun 2025 20:25:34 +0700 Subject: [PATCH 428/890] Refactor RevocationReason --- .../bouncycastle/bcpg/sig/RevocationReason.java | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/sig/RevocationReason.java b/pg/src/main/java/org/bouncycastle/bcpg/sig/RevocationReason.java index 91634e79e1..186c56d571 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/sig/RevocationReason.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/sig/RevocationReason.java @@ -2,6 +2,7 @@ import org.bouncycastle.bcpg.SignatureSubpacket; import org.bouncycastle.bcpg.SignatureSubpacketTags; +import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; /** @@ -26,31 +27,21 @@ public RevocationReason(boolean isCritical, byte reason, String description) private static byte[] createData(byte reason, String description) { - byte[] descriptionBytes = Strings.toUTF8ByteArray(description); - byte[] data = new byte[1 + descriptionBytes.length]; - - data[0] = reason; - System.arraycopy(descriptionBytes, 0, data, 1, descriptionBytes.length); - - return data; + return Arrays.prepend(Strings.toUTF8ByteArray(description), reason); } public byte getRevocationReason() { - return getData()[0]; + return data[0]; } public String getRevocationDescription() { - byte[] data = getData(); if (data.length == 1) { return ""; } - byte[] description = new byte[data.length - 1]; - System.arraycopy(data, 1, description, 0, description.length); - - return Strings.fromUTF8ByteArray(description); + return Strings.fromUTF8ByteArray(data, 1, data.length - 1); } } From 37e1ac9bfe5b4c8c1fa4c6e06442a56562bcda36 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 18 Jun 2025 20:46:21 +0700 Subject: [PATCH 429/890] Extra utility method variants --- .../java/org/bouncycastle/util/Strings.java | 51 ++++++++++--------- .../bouncycastle/util/encoders/Base64.java | 18 +++++-- .../org/bouncycastle/util/encoders/Hex.java | 17 +++++-- 3 files changed, 54 insertions(+), 32 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/util/Strings.java b/core/src/main/java/org/bouncycastle/util/Strings.java index 41d318bf76..68075e5dd3 100644 --- a/core/src/main/java/org/bouncycastle/util/Strings.java +++ b/core/src/main/java/org/bouncycastle/util/Strings.java @@ -47,13 +47,7 @@ public String run() public static String fromUTF8ByteArray(byte[] bytes) { - char[] chars = new char[bytes.length]; - int len = UTF8.transcodeToUTF16(bytes, chars); - if (len < 0) - { - throw new IllegalArgumentException("Invalid UTF-8 input"); - } - return new String(chars, 0, len); + return fromUTF8ByteArray(bytes, 0, bytes.length); } public static String fromUTF8ByteArray(byte[] bytes, int off, int length) @@ -73,12 +67,17 @@ public static byte[] toUTF8ByteArray(String string) } public static byte[] toUTF8ByteArray(char[] string) + { + return toUTF8ByteArray(string, 0, string.length); + } + + public static byte[] toUTF8ByteArray(char[] cs, int csOff, int csLen) { ByteArrayOutputStream bOut = new ByteArrayOutputStream(); try { - toUTF8ByteArray(string, bOut); + toUTF8ByteArray(cs, csOff, csLen, bOut); } catch (IOException e) { @@ -91,7 +90,13 @@ public static byte[] toUTF8ByteArray(char[] string) public static void toUTF8ByteArray(char[] string, OutputStream sOut) throws IOException { - if (string.length < 1) + toUTF8ByteArray(string, 0, string.length, sOut); + } + + public static void toUTF8ByteArray(char[] cs, int csOff, int csLen, OutputStream sOut) + throws IOException + { + if (csLen < 1) { return; } @@ -101,35 +106,35 @@ public static void toUTF8ByteArray(char[] string, OutputStream sOut) int bufPos = 0, i = 0; do { - int ch = string[i++]; + int c = cs[csOff + i++]; - if (ch < 0x0080) + if (c < 0x0080) { - buf[bufPos++] = (byte)ch; + buf[bufPos++] = (byte)c; } - else if (ch < 0x0800) + else if (c < 0x0800) { - buf[bufPos++] = (byte)(0xC0 | (ch >> 6)); - buf[bufPos++] = (byte)(0x80 | (ch & 0x3F)); + buf[bufPos++] = (byte)(0xC0 | (c >> 6)); + buf[bufPos++] = (byte)(0x80 | (c & 0x3F)); } // surrogate pair - else if (ch >= 0xD800 && ch <= 0xDFFF) + else if (c >= 0xD800 && c <= 0xDFFF) { /* * Various checks that shouldn't fail unless the Java String class has a bug. */ - int W1 = ch; + int W1 = c; if (W1 > 0xDBFF) { throw new IllegalStateException("invalid UTF-16 high surrogate"); } - if (i >= string.length) + if (i >= csLen) { throw new IllegalStateException("invalid UTF-16 codepoint (truncated surrogate pair)"); } - int W2 = string[i++]; + int W2 = cs[csOff + i++]; if (W2 < 0xDC00 || W2 > 0xDFFF) { throw new IllegalStateException("invalid UTF-16 low surrogate"); @@ -143,9 +148,9 @@ else if (ch >= 0xD800 && ch <= 0xDFFF) } else { - buf[bufPos++] = (byte)(0xE0 | (ch >> 12)); - buf[bufPos++] = (byte)(0x80 | ((ch >> 6) & 0x3F)); - buf[bufPos++] = (byte)(0x80 | (ch & 0x3F)); + buf[bufPos++] = (byte)(0xE0 | (c >> 12)); + buf[bufPos++] = (byte)(0x80 | ((c >> 6) & 0x3F)); + buf[bufPos++] = (byte)(0x80 | (c & 0x3F)); } if (bufPos + 4 > buf.length) @@ -154,7 +159,7 @@ else if (ch >= 0xD800 && ch <= 0xDFFF) bufPos = 0; } } - while (i < string.length); + while (i < csLen); if (bufPos > 0) { diff --git a/core/src/main/java/org/bouncycastle/util/encoders/Base64.java b/core/src/main/java/org/bouncycastle/util/encoders/Base64.java index 72e0a716f2..345b54a410 100644 --- a/core/src/main/java/org/bouncycastle/util/encoders/Base64.java +++ b/core/src/main/java/org/bouncycastle/util/encoders/Base64.java @@ -97,15 +97,23 @@ public static int encode( * * @return a byte array representing the decoded data. */ - public static byte[] decode( - byte[] data) + public static byte[] decode(byte[] data) { - int len = data.length / 4 * 3; - ByteArrayOutputStream bOut = new ByteArrayOutputStream(len); + return decode(data, 0, data.length); + } + + /** + * decode the base 64 encoded input data. It is assumed the input data is valid. + * + * @return a byte array representing the decoded data. + */ + public static byte[] decode(byte[] data, int off, int length) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(length / 4 * 3); try { - encoder.decode(data, 0, data.length, bOut); + encoder.decode(data, off, length, bOut); } catch (Exception e) { diff --git a/core/src/main/java/org/bouncycastle/util/encoders/Hex.java b/core/src/main/java/org/bouncycastle/util/encoders/Hex.java index b51c879a10..5ffc7657bc 100644 --- a/core/src/main/java/org/bouncycastle/util/encoders/Hex.java +++ b/core/src/main/java/org/bouncycastle/util/encoders/Hex.java @@ -96,14 +96,23 @@ public static int encode( * * @return a byte array representing the decoded data. */ - public static byte[] decode( - byte[] data) + public static byte[] decode(byte[] data) { - ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + return decode(data, 0, data.length); + } + + /** + * decode the Hex encoded input data. It is assumed the input data is valid. + * + * @return a byte array representing the decoded data. + */ + public static byte[] decode(byte[] data, int off, int length) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(length / 2); try { - encoder.decode(data, 0, data.length, bOut); + encoder.decode(data, off, length, bOut); } catch (Exception e) { From 73a50310be06274994dfe27af36c44556016186c Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sat, 21 Jun 2025 16:10:32 +0700 Subject: [PATCH 430/890] Refactoring in CMS AuthEnveloped --- .../smime/SMIMEAuthEnvelopedGenerator.java | 8 +++--- .../smime/test/NewSMIMEAuthEnvelopedTest.java | 7 +++-- .../cms/CMSAuthEnvelopedDataGenerator.java | 3 +-- .../cms/bc/EnvelopedDataHelper.java | 13 ++++----- .../cms/jcajce/EnvelopedDataHelper.java | 16 +++++------ .../jcajce/JceCMSContentEncryptorBuilder.java | 3 ++- .../cms/test/AuthEnvelopedDataTest.java | 6 ++--- .../cms/test/NewEnvelopedDataStreamTest.java | 27 ++++++++++++++++--- .../cms/test/NewEnvelopedDataTest.java | 20 ++++++++------ 9 files changed, 62 insertions(+), 41 deletions(-) diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEAuthEnvelopedGenerator.java b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEAuthEnvelopedGenerator.java index 99d2ab17bd..c8d1235492 100644 --- a/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEAuthEnvelopedGenerator.java +++ b/mail/src/main/java/org/bouncycastle/mail/smime/SMIMEAuthEnvelopedGenerator.java @@ -12,8 +12,8 @@ import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1ObjectIdentifier; -import org.bouncycastle.cms.CMSAuthEnvelopedDataGenerator; import org.bouncycastle.cms.CMSAuthEnvelopedDataStreamGenerator; +import org.bouncycastle.cms.CMSAuthEnvelopedGenerator; import org.bouncycastle.cms.CMSException; import org.bouncycastle.cms.RecipientInfoGenerator; import org.bouncycastle.operator.OutputAEADEncryptor; @@ -38,9 +38,9 @@ public class SMIMEAuthEnvelopedGenerator extends SMIMEEnvelopedGenerator { - public static final String AES128_GCM = CMSAuthEnvelopedDataGenerator.AES128_GCM; - public static final String AES192_GCM = CMSAuthEnvelopedDataGenerator.AES192_GCM; - public static final String AES256_GCM = CMSAuthEnvelopedDataGenerator.AES256_GCM; + public static final String AES128_GCM = CMSAuthEnvelopedGenerator.AES128_GCM; + public static final String AES192_GCM = CMSAuthEnvelopedGenerator.AES192_GCM; + public static final String AES256_GCM = CMSAuthEnvelopedGenerator.AES256_GCM; static final String AUTH_ENVELOPED_DATA_CONTENT_TYPE = "application/pkcs7-mime; name=\"smime.p7m\"; smime-type=authEnveloped-data"; diff --git a/mail/src/test/java/org/bouncycastle/mail/smime/test/NewSMIMEAuthEnvelopedTest.java b/mail/src/test/java/org/bouncycastle/mail/smime/test/NewSMIMEAuthEnvelopedTest.java index 5865303bc8..a44b1b1054 100644 --- a/mail/src/test/java/org/bouncycastle/mail/smime/test/NewSMIMEAuthEnvelopedTest.java +++ b/mail/src/test/java/org/bouncycastle/mail/smime/test/NewSMIMEAuthEnvelopedTest.java @@ -24,7 +24,6 @@ import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.bouncycastle.cms.CMSAlgorithm; -import org.bouncycastle.cms.CMSAuthEnvelopedDataGenerator; import org.bouncycastle.cms.KeyTransRecipientId; import org.bouncycastle.cms.RecipientId; import org.bouncycastle.cms.RecipientInformation; @@ -184,7 +183,7 @@ public void testAES128Encrypted() throws Exception { MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington"); - String algorithm = SMIMEAuthEnvelopedGenerator.AES256_GCM; + String algorithm = SMIMEAuthEnvelopedGenerator.AES128_GCM; verifyAlgorithm(algorithm, msg); } @@ -193,7 +192,7 @@ public void testAES192Encrypted() throws Exception { MimeBodyPart msg = SMIMETestUtil.makeMimeBodyPart("WallaWallaWashington"); - String algorithm = SMIMEAuthEnvelopedGenerator.AES256_GCM; + String algorithm = SMIMEAuthEnvelopedGenerator.AES192_GCM; verifyAlgorithm(algorithm, msg); } @@ -531,7 +530,7 @@ private void doTryAgreement(MimeBodyPart data, ASN1ObjectIdentifier algorithm) SMIMEAuthEnveloped ed = new SMIMEAuthEnveloped(res); - assertEquals(ed.getEncryptionAlgOID(), CMSAuthEnvelopedDataGenerator.AES128_GCM); + assertEquals(ed.getEncryptionAlgOID(), SMIMEAuthEnvelopedGenerator.AES128_GCM); RecipientInformationStore recipients = ed.getRecipientInfos(); diff --git a/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedDataGenerator.java b/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedDataGenerator.java index 4665295e4e..a873d6572b 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedDataGenerator.java +++ b/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedDataGenerator.java @@ -12,7 +12,6 @@ import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; import org.bouncycastle.asn1.cms.ContentInfo; import org.bouncycastle.asn1.cms.EncryptedContentInfo; -import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.operator.OutputAEADEncryptor; public class CMSAuthEnvelopedDataGenerator @@ -37,7 +36,7 @@ private CMSAuthEnvelopedData doGenerate( try { OutputStream cOut = contentEncryptor.getOutputStream(bOut); - if (contentEncryptor.getAlgorithmIdentifier().getAlgorithm().equals(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305)) + if (CMSAlgorithm.ChaCha20Poly1305.equals(contentEncryptor.getAlgorithmIdentifier().getAlgorithm())) { // AEAD Ciphers process AAD at first authenticatedAttrSet = CMSUtils.processAuthAttrSet(authAttrsGenerator, contentEncryptor); diff --git a/pkix/src/main/java/org/bouncycastle/cms/bc/EnvelopedDataHelper.java b/pkix/src/main/java/org/bouncycastle/cms/bc/EnvelopedDataHelper.java index 987b38b2df..bb6dbd4ffd 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/bc/EnvelopedDataHelper.java +++ b/pkix/src/main/java/org/bouncycastle/cms/bc/EnvelopedDataHelper.java @@ -99,12 +99,13 @@ public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier) MAC_ALG_NAMES.put(CMSAlgorithm.AES256_CBC, "AESMac"); MAC_ALG_NAMES.put(CMSAlgorithm.RC2_CBC, "RC2Mac"); - authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes128_GCM); - authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes192_GCM); - authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes256_GCM); - authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes128_CCM); - authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes192_CCM); - authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes256_CCM); + authEnvelopedAlgorithms.add(CMSAlgorithm.AES128_GCM); + authEnvelopedAlgorithms.add(CMSAlgorithm.AES192_GCM); + authEnvelopedAlgorithms.add(CMSAlgorithm.AES256_GCM); + authEnvelopedAlgorithms.add(CMSAlgorithm.AES128_CCM); + authEnvelopedAlgorithms.add(CMSAlgorithm.AES192_CCM); + authEnvelopedAlgorithms.add(CMSAlgorithm.AES256_CCM); + authEnvelopedAlgorithms.add(CMSAlgorithm.ChaCha20Poly1305); } EnvelopedDataHelper() diff --git a/pkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java b/pkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java index 8a70df6f9b..b55690dfb4 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java +++ b/pkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java @@ -41,7 +41,6 @@ import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.cms.CMSObjectIdentifiers; import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; -import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PBKDF2Params; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.RC2CBCParameter; @@ -118,13 +117,13 @@ public class EnvelopedDataHelper PBKDF2_ALG_NAMES.put(PasswordRecipient.PRF.HMacSHA384.getAlgorithmID(), "PBKDF2WITHHMACSHA384"); PBKDF2_ALG_NAMES.put(PasswordRecipient.PRF.HMacSHA512.getAlgorithmID(), "PBKDF2WITHHMACSHA512"); - authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes128_GCM); - authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes192_GCM); - authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes256_GCM); - authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes128_CCM); - authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes192_CCM); - authEnvelopedAlgorithms.add(NISTObjectIdentifiers.id_aes256_CCM); - authEnvelopedAlgorithms.add(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305); + authEnvelopedAlgorithms.add(CMSAlgorithm.AES128_GCM); + authEnvelopedAlgorithms.add(CMSAlgorithm.AES192_GCM); + authEnvelopedAlgorithms.add(CMSAlgorithm.AES256_GCM); + authEnvelopedAlgorithms.add(CMSAlgorithm.AES128_CCM); + authEnvelopedAlgorithms.add(CMSAlgorithm.AES192_CCM); + authEnvelopedAlgorithms.add(CMSAlgorithm.AES256_CCM); + authEnvelopedAlgorithms.add(CMSAlgorithm.ChaCha20Poly1305); } private static final short[] rc2Table = { @@ -487,7 +486,6 @@ public Object doInJCE() { Mac mac = createMac(macAlgId.getAlgorithm()); ASN1Encodable sParams = macAlgId.getParameters(); - String macAlg = macAlgId.getAlgorithm().getId(); if (sParams != null && !(sParams instanceof ASN1Null)) { diff --git a/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java b/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java index 18e91bbf1e..b320540447 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java +++ b/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java @@ -23,6 +23,7 @@ import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.cms.CMSAlgorithm; import org.bouncycastle.cms.CMSException; import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.bouncycastle.crypto.digests.SHA256Digest; @@ -377,7 +378,7 @@ public OutputStream getOutputStream(OutputStream dOut) algId = algorithmIdentifier; } - if (algorithmIdentifier.getAlgorithm().equals(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305)) + if (CMSAlgorithm.ChaCha20Poly1305.equals(algorithmIdentifier.getAlgorithm())) { macOut = new MacCaptureStream(dOut, 16); } diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/AuthEnvelopedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/AuthEnvelopedDataTest.java index 3472aa57f5..4615b98d69 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/AuthEnvelopedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/AuthEnvelopedDataTest.java @@ -22,8 +22,8 @@ import org.bouncycastle.asn1.cms.GCMParameters; import org.bouncycastle.asn1.cms.Time; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; -import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.cms.CMSAlgorithm; import org.bouncycastle.cms.CMSAttributeTableGenerationException; import org.bouncycastle.cms.CMSAttributeTableGenerator; import org.bouncycastle.cms.CMSAuthEnvelopedData; @@ -216,9 +216,9 @@ public void testChacha20Poly1305() return; } byte[] message = Strings.toByteArray("Hello, world!"); - OutputEncryptor candidate = new JceCMSContentEncryptorBuilder(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305).setProvider(BC).build(); + OutputEncryptor candidate = new JceCMSContentEncryptorBuilder(CMSAlgorithm.ChaCha20Poly1305).setProvider(BC).build(); - assertEquals(PKCSObjectIdentifiers.id_alg_AEADChaCha20Poly1305, candidate.getAlgorithmIdentifier().getAlgorithm()); + assertEquals(CMSAlgorithm.ChaCha20Poly1305, candidate.getAlgorithmIdentifier().getAlgorithm()); //assertNotNull(GCMParameters.getInstance(candidate.getAlgorithmIdentifier().getParameters())); assertTrue(candidate instanceof OutputAEADEncryptor); diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataStreamTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataStreamTest.java index 4786f62f30..9aafdc35a3 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataStreamTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataStreamTest.java @@ -22,6 +22,7 @@ import junit.framework.TestSuite; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.DERSet; import org.bouncycastle.asn1.DERUTF8String; import org.bouncycastle.asn1.cms.Attribute; @@ -253,6 +254,24 @@ public void testUnprotectedAttributes() public void testKeyTransAES128GCM() throws Exception + { + implTestKeyTrans(CMSAlgorithm.AES128_GCM); + } + + public void testKeyTransAES192GCM() + throws Exception + { + implTestKeyTrans(CMSAlgorithm.AES192_GCM); + } + + public void testKeyTransAES256GCM() + throws Exception + { + implTestKeyTrans(CMSAlgorithm.AES256_GCM); + } + + private void implTestKeyTrans(ASN1ObjectIdentifier contentEncryptionOID) + throws Exception { byte[] data = new byte[2000]; @@ -271,7 +290,7 @@ public void testKeyTransAES128GCM() ByteArrayOutputStream bOut = new ByteArrayOutputStream(); OutputStream out = edGen.open( - bOut, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM).setProvider(BC).build()); + bOut, new JceCMSContentEncryptorBuilder(contentEncryptionOID).setProvider(BC).build()); for (int i = 0; i != 2000; i++) { @@ -280,7 +299,7 @@ public void testKeyTransAES128GCM() out.close(); - verifyData(bOut, CMSAlgorithm.AES128_GCM.getId(), data); + verifyData(bOut, contentEncryptionOID.getId(), data); int unbufferedLength = bOut.toByteArray().length; @@ -293,7 +312,7 @@ public void testKeyTransAES128GCM() bOut = new ByteArrayOutputStream(); - out = edGen.open(bOut, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_GCM).setProvider(BC).build()); + out = edGen.open(bOut, new JceCMSContentEncryptorBuilder(contentEncryptionOID).setProvider(BC).build()); BufferedOutputStream bfOut = new BufferedOutputStream(out, 300); @@ -304,7 +323,7 @@ public void testKeyTransAES128GCM() bfOut.close(); - verifyData(bOut, CMSAlgorithm.AES128_GCM.getId(), data); + verifyData(bOut, contentEncryptionOID.getId(), data); assertTrue(bOut.toByteArray().length == unbufferedLength); } diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java index d8e8845400..b185e3ad9b 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java @@ -1771,18 +1771,22 @@ public void testAES128KEK() { tryKekAlgorithm(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap); - tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES128_GCM, NISTObjectIdentifiers.id_aes128_GCM); - tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES192_GCM, NISTObjectIdentifiers.id_aes192_GCM); - tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES256_GCM, NISTObjectIdentifiers.id_aes256_GCM); + tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES128_GCM, CMSAlgorithm.AES128_GCM); + tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES192_GCM, CMSAlgorithm.AES192_GCM); + tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES256_GCM, CMSAlgorithm.AES256_GCM); byte[] nonce = Hex.decode("0102030405060708090a0b0c"); - tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES128_GCM, NISTObjectIdentifiers.id_aes128_GCM, new GCMParameters(nonce, 11).getEncoded()); + tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES128_GCM, CMSAlgorithm.AES128_GCM, new GCMParameters(nonce, 11).getEncoded()); + tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES192_GCM, CMSAlgorithm.AES192_GCM, new GCMParameters(nonce, 11).getEncoded()); + tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES256_GCM, CMSAlgorithm.AES256_GCM, new GCMParameters(nonce, 11).getEncoded()); - tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES128_CCM, NISTObjectIdentifiers.id_aes128_CCM); - tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES192_CCM, NISTObjectIdentifiers.id_aes192_CCM); - tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES256_CCM, NISTObjectIdentifiers.id_aes256_CCM); + tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES128_CCM, CMSAlgorithm.AES128_CCM); + tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES192_CCM, CMSAlgorithm.AES192_CCM); + tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES256_CCM, CMSAlgorithm.AES256_CCM); - tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES128_CCM, NISTObjectIdentifiers.id_aes128_CCM, new CCMParameters(nonce, 14).getEncoded()); + tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES128_CCM, CMSAlgorithm.AES128_CCM, new CCMParameters(nonce, 14).getEncoded()); + tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES192_CCM, CMSAlgorithm.AES192_CCM, new CCMParameters(nonce, 14).getEncoded()); + tryKekAlgorithmAEAD(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap, CMSAlgorithm.AES256_CCM, CMSAlgorithm.AES256_CCM, new CCMParameters(nonce, 14).getEncoded()); } public void testAES192KEK() From 938d9f64d46df775b28f5085e049fc345e39a4b3 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Sat, 21 Jun 2025 16:23:56 +0700 Subject: [PATCH 431/890] Refactoring around RSASSAPSSparams --- ...ultSignatureAlgorithmIdentifierFinder.java | 2 +- .../jce/PKCS10CertificationRequest.java | 2 +- .../java/org/bouncycastle/x509/X509Util.java | 2 +- .../bouncycastle/tls/crypto/impl/RSAUtil.java | 2 +- .../jsse/provider/test/TestUtils.java | 20 +++++++++++++------ 5 files changed, 18 insertions(+), 10 deletions(-) diff --git a/pkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java b/pkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java index dab046e35d..711c15e108 100644 --- a/pkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java +++ b/pkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java @@ -77,7 +77,7 @@ private static RSASSAPSSparams createPSSParams(AlgorithmIdentifier hashAlgId, in hashAlgId, new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, hashAlgId), new ASN1Integer(saltSize), - new ASN1Integer(1)); + RSASSAPSSparams.DEFAULT_TRAILER_FIELD); } static diff --git a/prov/src/main/java/org/bouncycastle/jce/PKCS10CertificationRequest.java b/prov/src/main/java/org/bouncycastle/jce/PKCS10CertificationRequest.java index cf7933d714..db79c9eb7d 100644 --- a/prov/src/main/java/org/bouncycastle/jce/PKCS10CertificationRequest.java +++ b/prov/src/main/java/org/bouncycastle/jce/PKCS10CertificationRequest.java @@ -200,7 +200,7 @@ private static RSASSAPSSparams creatPSSParams(AlgorithmIdentifier hashAlgId, int hashAlgId, new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, hashAlgId), new ASN1Integer(saltSize), - new ASN1Integer(1)); + RSASSAPSSparams.DEFAULT_TRAILER_FIELD); } private static ASN1Sequence toDERSequence( diff --git a/prov/src/main/java/org/bouncycastle/x509/X509Util.java b/prov/src/main/java/org/bouncycastle/x509/X509Util.java index 2be58a87f6..027b0133e4 100644 --- a/prov/src/main/java/org/bouncycastle/x509/X509Util.java +++ b/prov/src/main/java/org/bouncycastle/x509/X509Util.java @@ -134,7 +134,7 @@ private static RSASSAPSSparams creatPSSParams(AlgorithmIdentifier hashAlgId, int hashAlgId, new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, hashAlgId), new ASN1Integer(saltSize), - new ASN1Integer(1)); + RSASSAPSSparams.DEFAULT_TRAILER_FIELD); } static ASN1ObjectIdentifier getAlgorithmOID( diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/RSAUtil.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/RSAUtil.java index 2a7a2ec815..c84d1ebe46 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/RSAUtil.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/RSAUtil.java @@ -47,7 +47,7 @@ public class RSAUtil ASN1Integer sha384Size = new ASN1Integer(TlsCryptoUtils.getHashOutputSize(CryptoHashAlgorithm.sha384)); ASN1Integer sha512Size = new ASN1Integer(TlsCryptoUtils.getHashOutputSize(CryptoHashAlgorithm.sha512)); - ASN1Integer trailerField = new ASN1Integer(1); + ASN1Integer trailerField = RSASSAPSSparams.DEFAULT_TRAILER_FIELD; try { diff --git a/tls/src/test/java/org/bouncycastle/jsse/provider/test/TestUtils.java b/tls/src/test/java/org/bouncycastle/jsse/provider/test/TestUtils.java index 05963eb4b6..3c69d7c969 100644 --- a/tls/src/test/java/org/bouncycastle/jsse/provider/test/TestUtils.java +++ b/tls/src/test/java/org/bouncycastle/jsse/provider/test/TestUtils.java @@ -40,6 +40,7 @@ import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.DERBitString; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.DERSequence; @@ -70,6 +71,8 @@ import org.bouncycastle.jsse.BCSSLSocket; import org.bouncycastle.jsse.java.security.BCAlgorithmConstraints; import org.bouncycastle.jsse.java.security.BCCryptoPrimitive; +import org.bouncycastle.tls.crypto.CryptoHashAlgorithm; +import org.bouncycastle.tls.crypto.TlsCryptoUtils; /** * Test Utils @@ -84,6 +87,10 @@ class TestUtils private static Map createAlgIDs() { + ASN1ObjectIdentifier id_sha256 = NISTObjectIdentifiers.id_sha256; + AlgorithmIdentifier sha256Identifier = new AlgorithmIdentifier(id_sha256, DERNull.INSTANCE); + int sha256OutputSize = TlsCryptoUtils.getHashOutputSize(CryptoHashAlgorithm.sha256); + HashMap algIDs = new HashMap(); algIDs.put("SHA1withDSA", new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa_with_sha1)); @@ -92,12 +99,13 @@ private static Map createAlgIDs() algIDs.put("SHA1withRSA", new AlgorithmIdentifier(PKCSObjectIdentifiers.sha1WithRSAEncryption, DERNull.INSTANCE)); algIDs.put("SHA224withRSA", new AlgorithmIdentifier(PKCSObjectIdentifiers.sha224WithRSAEncryption, DERNull.INSTANCE)); algIDs.put("SHA256withRSA", new AlgorithmIdentifier(PKCSObjectIdentifiers.sha256WithRSAEncryption, DERNull.INSTANCE)); - algIDs.put("SHA256withRSAandMGF1", - new AlgorithmIdentifier(PKCSObjectIdentifiers.id_RSASSA_PSS, - new RSASSAPSSparams(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256), - new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, - new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha256)), - new ASN1Integer(32), new ASN1Integer(1)))); + algIDs.put("SHA256withRSAandMGF1", new AlgorithmIdentifier( + PKCSObjectIdentifiers.id_RSASSA_PSS, + new RSASSAPSSparams( + sha256Identifier, + new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, sha256Identifier), + new ASN1Integer(sha256OutputSize), + RSASSAPSSparams.DEFAULT_TRAILER_FIELD))); algIDs.put("SHA1withECDSA", new AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA1)); algIDs.put("SHA224withECDSA", new AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA224)); algIDs.put("SHA256withECDSA", new AlgorithmIdentifier(X9ObjectIdentifiers.ecdsa_with_SHA256)); From 3f7a1a37087304cc2a3370845f7a5577c3f40d06 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 23 Jun 2025 14:42:30 +0700 Subject: [PATCH 432/890] Refactoring in bcpg.sig --- .../org/bouncycastle/bcpg/sig/KeyFlags.java | 45 +++++++++---------- .../bouncycastle/bcpg/sig/NotationData.java | 2 +- .../bouncycastle/bcpg/sig/RevocationKey.java | 5 +-- 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/sig/KeyFlags.java b/pg/src/main/java/org/bouncycastle/bcpg/sig/KeyFlags.java index a92b975f6d..60c5b979df 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/sig/KeyFlags.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/sig/KeyFlags.java @@ -2,6 +2,7 @@ import org.bouncycastle.bcpg.SignatureSubpacket; import org.bouncycastle.bcpg.SignatureSubpacketTags; +import org.bouncycastle.util.Integers; /** * Signature Subpacket encoding the capabilities / intended uses of a key. @@ -49,29 +50,30 @@ public class KeyFlags * The private component of this key may be in the possession of more than one person. */ public static final int SHARED = 0x80; - - private static byte[] intToByteArray( - int v) - { - byte[] tmp = new byte[4]; - int size = 0; - for (int i = 0; i != 4; i++) + private static int dataToFlags(byte[] data) + { + int flags = 0, bytes = Math.min(4, data.length); + for (int i = 0; i < bytes; ++i) { - tmp[i] = (byte)(v >> (i * 8)); - if (tmp[i] != 0) - { - size = i; - } + flags |= (data[i] & 0xFF) << (i * 8); } + return flags; + } - byte[] data = new byte[size + 1]; - - System.arraycopy(tmp, 0, data, 0, data.length); + private static byte[] flagsToData(int flags) + { + int bits = 32 - Integers.numberOfLeadingZeros(flags); + int bytes = (bits + 7) / 8; + byte[] data = new byte[bytes]; + for (int i = 0; i < bytes; ++i) + { + data[i] = (byte)(flags >> (i * 8)); + } return data; } - + public KeyFlags( boolean critical, boolean isLongLength, @@ -84,7 +86,7 @@ public KeyFlags( boolean critical, int flags) { - super(SignatureSubpacketTags.KEY_FLAGS, critical, false, intToByteArray(flags)); + super(SignatureSubpacketTags.KEY_FLAGS, critical, false, flagsToData(flags)); } /** @@ -95,13 +97,6 @@ public KeyFlags( */ public int getFlags() { - int flags = 0; - - for (int i = 0; i != data.length; i++) - { - flags |= (data[i] & 0xff) << (i * 8); - } - - return flags; + return dataToFlags(data); } } diff --git a/pg/src/main/java/org/bouncycastle/bcpg/sig/NotationData.java b/pg/src/main/java/org/bouncycastle/bcpg/sig/NotationData.java index b1c4e970d7..5f297f98c3 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/sig/NotationData.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/sig/NotationData.java @@ -91,7 +91,7 @@ private static byte[] createData(boolean humanReadable, String notationName, Str public boolean isHumanReadable() { - return data[0] == (byte)0x80; + return (data[0] & 0x80) != 0; } public String getNotationName() diff --git a/pg/src/main/java/org/bouncycastle/bcpg/sig/RevocationKey.java b/pg/src/main/java/org/bouncycastle/bcpg/sig/RevocationKey.java index 4c0960fc64..8779f0585c 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/sig/RevocationKey.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/sig/RevocationKey.java @@ -3,6 +3,7 @@ import org.bouncycastle.bcpg.KeyIdentifier; import org.bouncycastle.bcpg.SignatureSubpacket; import org.bouncycastle.bcpg.SignatureSubpacketTags; +import org.bouncycastle.util.Arrays; /** * Represents revocation key OpenPGP signature sub packet. @@ -52,9 +53,7 @@ public int getAlgorithm() public byte[] getFingerprint() { - byte[] fingerprint = new byte[data.length - 2]; - System.arraycopy(data, 2, fingerprint, 0, fingerprint.length); - return fingerprint; + return Arrays.copyOfRange(data, 2, data.length); } public KeyIdentifier getKeyIdentifier() From dd7ea8e32d3f0b7ee514720c75f1f77cf876d253 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 23 Jun 2025 17:41:01 +0700 Subject: [PATCH 433/890] Refactoring in tls --- .../bouncycastle/tls/DTLSClientProtocol.java | 2 +- .../bouncycastle/tls/PskKeyExchangeMode.java | 12 +++ .../bouncycastle/tls/TlsClientProtocol.java | 5 +- .../bouncycastle/tls/TlsDHEKeyExchange.java | 4 +- .../bouncycastle/tls/TlsECDHEKeyExchange.java | 4 +- .../org/bouncycastle/tls/TlsProtocol.java | 11 +-- .../bouncycastle/tls/TlsSRPKeyExchange.java | 4 +- .../bouncycastle/tls/TlsServerProtocol.java | 2 +- .../java/org/bouncycastle/tls/TlsUtils.java | 80 +++++++++++-------- 9 files changed, 71 insertions(+), 53 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java b/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java index a0e7d45123..36708092b9 100644 --- a/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java @@ -725,7 +725,7 @@ protected void processServerCertificate(ClientHandshakeState state, byte[] body) throws IOException { state.authentication = TlsUtils.receiveServerCertificate(state.clientContext, state.client, - new ByteArrayInputStream(body), state.serverExtensions); + new ByteArrayInputStream(body)); } protected void processServerHello(ClientHandshakeState state, byte[] body) diff --git a/tls/src/main/java/org/bouncycastle/tls/PskKeyExchangeMode.java b/tls/src/main/java/org/bouncycastle/tls/PskKeyExchangeMode.java index d4d648df2e..f8f90d6f93 100644 --- a/tls/src/main/java/org/bouncycastle/tls/PskKeyExchangeMode.java +++ b/tls/src/main/java/org/bouncycastle/tls/PskKeyExchangeMode.java @@ -26,4 +26,16 @@ public static String getText(short pskKeyExchangeMode) { return getName(pskKeyExchangeMode) + "(" + pskKeyExchangeMode + ")"; } + + public static boolean isRecognized(short pskKeyExchangeMode) + { + switch (pskKeyExchangeMode) + { + case psk_ke: + case psk_dhe_ke: + return true; + default: + return false; + } + } } diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java index e01366d485..fa129dc769 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsClientProtocol.java @@ -408,7 +408,7 @@ protected void handleHandshakeMessage(short type, HandshakeMessageInput buf) * NOTE: Certificate processing (including authentication) is delayed to allow for a * possible CertificateStatus message. */ - this.authentication = TlsUtils.receiveServerCertificate(tlsClientContext, tlsClient, buf, serverExtensions); + this.authentication = TlsUtils.receiveServerCertificate(tlsClientContext, tlsClient, buf); break; } default: @@ -1532,7 +1532,6 @@ protected void receive13EncryptedExtensions(ByteArrayInputStream buf) final SecurityParameters securityParameters = tlsClientContext.getSecurityParametersHandshake(); - final ProtocolVersion negotiatedVersion = securityParameters.getNegotiatedVersion(); securityParameters.applicationProtocol = TlsExtensionsUtils.getALPNExtensionServer(serverExtensions); securityParameters.applicationProtocolSet = true; @@ -1610,7 +1609,7 @@ protected void receive13ServerCertificate(ByteArrayInputStream buf) throw new TlsFatalAlert(AlertDescription.unexpected_message); } - this.authentication = TlsUtils.receive13ServerCertificate(tlsClientContext, tlsClient, buf, serverExtensions); + this.authentication = TlsUtils.receive13ServerCertificate(tlsClientContext, tlsClient, buf); // NOTE: In TLS 1.3 we don't have to wait for a possible CertificateStatus message. handleServerCertificate(); diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsDHEKeyExchange.java b/tls/src/main/java/org/bouncycastle/tls/TlsDHEKeyExchange.java index 23a82a5f35..39e8edc95d 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsDHEKeyExchange.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsDHEKeyExchange.java @@ -82,7 +82,7 @@ public byte[] generateServerKeyExchange() throws IOException TlsUtils.writeOpaque16(y, digestBuffer); - TlsUtils.generateServerKeyExchangeSignature(context, serverCredentials, null, digestBuffer); + TlsUtils.generateServerKeyExchangeSignature(context, serverCredentials, digestBuffer); return digestBuffer.toByteArray(); } @@ -96,7 +96,7 @@ public void processServerKeyExchange(InputStream input) throws IOException byte[] y = TlsUtils.readOpaque16(teeIn, 1); - TlsUtils.verifyServerKeyExchangeSignature(context, input, serverCertificate, null, digestBuffer); + TlsUtils.verifyServerKeyExchangeSignature(context, input, serverCertificate, digestBuffer); this.agreement = context.getCrypto().createDHDomain(dhConfig).createDH(); diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsECDHEKeyExchange.java b/tls/src/main/java/org/bouncycastle/tls/TlsECDHEKeyExchange.java index 5d2701b545..76190b2b38 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsECDHEKeyExchange.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsECDHEKeyExchange.java @@ -76,7 +76,7 @@ public byte[] generateServerKeyExchange() throws IOException generateEphemeral(digestBuffer); - TlsUtils.generateServerKeyExchangeSignature(context, serverCredentials, null, digestBuffer); + TlsUtils.generateServerKeyExchangeSignature(context, serverCredentials, digestBuffer); return digestBuffer.toByteArray(); } @@ -90,7 +90,7 @@ public void processServerKeyExchange(InputStream input) throws IOException byte[] point = TlsUtils.readOpaque8(teeIn, 1); - TlsUtils.verifyServerKeyExchangeSignature(context, input, serverCertificate, null, digestBuffer); + TlsUtils.verifyServerKeyExchangeSignature(context, input, serverCertificate, digestBuffer); this.agreement = context.getCrypto().createECDomain(ecConfig).createECDH(); diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsProtocol.java index ce2630bb8c..9ff6a5ab7d 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsProtocol.java @@ -570,7 +570,7 @@ protected void processRecord(short protocol, byte[] buf, int off, int len) throw new TlsFatalAlert(AlertDescription.unexpected_message); } applicationDataQueue.addData(buf, off, len); - processApplicationDataQueue(); +// processApplicationDataQueue(); break; } case ContentType.change_cipher_spec: @@ -716,15 +716,6 @@ private void processHandshakeQueue(ByteQueue queue) } } - private void processApplicationDataQueue() - { - /* - * There is nothing we need to do here. - * - * This function could be used for callbacks when application data arrives in the future. - */ - } - private void processAlertQueue() throws IOException { diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsSRPKeyExchange.java b/tls/src/main/java/org/bouncycastle/tls/TlsSRPKeyExchange.java index 08ad56d425..60d442b0fc 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsSRPKeyExchange.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsSRPKeyExchange.java @@ -110,7 +110,7 @@ public byte[] generateServerKeyExchange() throws IOException if (serverCredentials != null) { - TlsUtils.generateServerKeyExchangeSignature(context, serverCredentials, null, digestBuffer); + TlsUtils.generateServerKeyExchangeSignature(context, serverCredentials, digestBuffer); } return digestBuffer.toByteArray(); @@ -131,7 +131,7 @@ public void processServerKeyExchange(InputStream input) throws IOException if (digestBuffer != null) { - TlsUtils.verifyServerKeyExchangeSignature(context, input, serverCertificate, null, digestBuffer); + TlsUtils.verifyServerKeyExchangeSignature(context, input, serverCertificate, digestBuffer); } TlsSRPConfig config = new TlsSRPConfig(); diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java index 3c94732a8c..5ce4c65f13 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsServerProtocol.java @@ -170,8 +170,8 @@ protected ServerHello generate13ServerHello(ClientHello clientHello, HandshakeMe clientHelloExtensions, clientHelloMessage, handshakeHash, afterHelloRetryRequest); Vector clientShares = TlsExtensionsUtils.getKeyShareClientHello(clientHelloExtensions); - KeyShareEntry clientShare = null; + KeyShareEntry clientShare; if (afterHelloRetryRequest) { if (retryGroup < 0) diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java b/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java index 18567b730e..fd9cb31b56 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java @@ -2318,8 +2318,7 @@ static int[] getPRFAlgorithms13(int[] cipherSuites) return truncate(result, count); } - static byte[] calculateSignatureHash(TlsContext context, SignatureAndHashAlgorithm algorithm, - byte[] extraSignatureInput, DigestInputBuffer buf) + static byte[] calculateSignatureHash(TlsContext context, SignatureAndHashAlgorithm algorithm, DigestInputBuffer buf) { TlsCrypto crypto = context.getCrypto(); @@ -2332,29 +2331,18 @@ static byte[] calculateSignatureHash(TlsContext context, SignatureAndHashAlgorit byte[] randoms = Arrays.concatenate(sp.getClientRandom(), sp.getServerRandom()); h.update(randoms, 0, randoms.length); - if (null != extraSignatureInput) - { - h.update(extraSignatureInput, 0, extraSignatureInput.length); - } - buf.updateDigest(h); return h.calculateHash(); } - static void sendSignatureInput(TlsContext context, byte[] extraSignatureInput, DigestInputBuffer buf, - OutputStream output) throws IOException + static void sendSignatureInput(TlsContext context, DigestInputBuffer buf, OutputStream output) throws IOException { SecurityParameters sp = context.getSecurityParametersHandshake(); // NOTE: The implicit copy here is intended (and important) byte[] randoms = Arrays.concatenate(sp.getClientRandom(), sp.getServerRandom()); output.write(randoms); - if (null != extraSignatureInput) - { - output.write(extraSignatureInput); - } - buf.copyInputTo(output); output.close(); @@ -2586,7 +2574,7 @@ private static byte[] getCertificateVerifyHeader(String contextString) } static void generateServerKeyExchangeSignature(TlsContext context, TlsCredentialedSigner credentials, - byte[] extraSignatureInput, DigestInputBuffer digestBuffer) throws IOException + DigestInputBuffer digestBuffer) throws IOException { /* * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 @@ -2597,12 +2585,12 @@ static void generateServerKeyExchangeSignature(TlsContext context, TlsCredential byte[] signature; if (streamSigner != null) { - sendSignatureInput(context, extraSignatureInput, digestBuffer, streamSigner.getOutputStream()); + sendSignatureInput(context, digestBuffer, streamSigner.getOutputStream()); signature = streamSigner.getSignature(); } else { - byte[] hash = calculateSignatureHash(context, algorithm, extraSignatureInput, digestBuffer); + byte[] hash = calculateSignatureHash(context, algorithm, digestBuffer); signature = credentials.generateRawSignature(hash); } @@ -2612,7 +2600,7 @@ static void generateServerKeyExchangeSignature(TlsContext context, TlsCredential } static void verifyServerKeyExchangeSignature(TlsContext context, InputStream signatureInput, - TlsCertificate serverCertificate, byte[] extraSignatureInput, DigestInputBuffer digestBuffer) + TlsCertificate serverCertificate, DigestInputBuffer digestBuffer) throws IOException { DigitallySigned digitallySigned = DigitallySigned.parse(context, signatureInput); @@ -2645,12 +2633,12 @@ static void verifyServerKeyExchangeSignature(TlsContext context, InputStream sig boolean verified; if (streamVerifier != null) { - sendSignatureInput(context, extraSignatureInput, digestBuffer, streamVerifier.getOutputStream()); + sendSignatureInput(context, digestBuffer, streamVerifier.getOutputStream()); verified = streamVerifier.isVerified(); } else { - byte[] hash = calculateSignatureHash(context, sigAndHashAlg, extraSignatureInput, digestBuffer); + byte[] hash = calculateSignatureHash(context, sigAndHashAlg, digestBuffer); verified = verifier.verifyRawSignature(digitallySigned, hash); } @@ -5059,34 +5047,34 @@ static int[] truncate(int[] a, int n) static TlsCredentialedAgreement requireAgreementCredentials(TlsCredentials credentials) throws IOException { - if (!(credentials instanceof TlsCredentialedAgreement)) + if (credentials instanceof TlsCredentialedAgreement) { - throw new TlsFatalAlert(AlertDescription.internal_error); + return (TlsCredentialedAgreement)credentials; } - return (TlsCredentialedAgreement)credentials; + throw new TlsFatalAlert(AlertDescription.internal_error); } static TlsCredentialedDecryptor requireDecryptorCredentials(TlsCredentials credentials) throws IOException { - if (!(credentials instanceof TlsCredentialedDecryptor)) + if (credentials instanceof TlsCredentialedDecryptor) { - throw new TlsFatalAlert(AlertDescription.internal_error); + return (TlsCredentialedDecryptor)credentials; } - return (TlsCredentialedDecryptor)credentials; + throw new TlsFatalAlert(AlertDescription.internal_error); } static TlsCredentialedSigner requireSignerCredentials(TlsCredentials credentials) throws IOException { - if (!(credentials instanceof TlsCredentialedSigner)) + if (credentials instanceof TlsCredentialedSigner) { - throw new TlsFatalAlert(AlertDescription.internal_error); + return (TlsCredentialedSigner)credentials; } - return (TlsCredentialedSigner)credentials; + throw new TlsFatalAlert(AlertDescription.internal_error); } private static void checkClientCertificateType(CertificateRequest certificateRequest, short clientCertificateType, @@ -5187,7 +5175,7 @@ private static boolean isSafeRenegotiationServerCertificate(TlsClientContext cli } static TlsAuthentication receiveServerCertificate(TlsClientContext clientContext, TlsClient client, - ByteArrayInputStream buf, Hashtable serverExtensions) throws IOException + ByteArrayInputStream buf) throws IOException { SecurityParameters securityParameters = clientContext.getSecurityParametersHandshake(); if (KeyExchangeAlgorithm.isAnonymous(securityParameters.getKeyExchangeAlgorithm()) @@ -5231,7 +5219,7 @@ static TlsAuthentication receiveServerCertificate(TlsClientContext clientContext } static TlsAuthentication receive13ServerCertificate(TlsClientContext clientContext, TlsClient client, - ByteArrayInputStream buf, Hashtable serverExtensions) throws IOException + ByteArrayInputStream buf) throws IOException { SecurityParameters securityParameters = clientContext.getSecurityParametersHandshake(); if (null != securityParameters.getPeerCertificate()) @@ -6085,8 +6073,14 @@ static OfferedPsks.SelectedConfig selectPreSharedKey(TlsServerContext serverCont throw new TlsFatalAlert(AlertDescription.missing_extension); } + // TODO[tls13] Fetch these from 'server' + short[] serverSupportedModes = { PskKeyExchangeMode.psk_dhe_ke }; + boolean useServerOrder = false; + + short selectedMode = selectPreSharedKeyMode(pskKeyExchangeModes, serverSupportedModes, useServerOrder); + // TODO[tls13] Add support for psk_ke? - if (Arrays.contains(pskKeyExchangeModes, PskKeyExchangeMode.psk_dhe_ke)) + if (PskKeyExchangeMode.psk_dhe_ke == selectedMode) { // TODO[tls13] Prefer to get the exact index from the server? TlsPSKExternal psk = server.getExternalPSK(offeredPsks.getIdentities()); @@ -6144,6 +6138,28 @@ static OfferedPsks.SelectedConfig selectPreSharedKey(TlsServerContext serverCont return null; } + private static short selectPreSharedKeyMode(short[] clientSupportedModes, short[] serverSupportedModes, + boolean useServerOrder) + { + if (!isNullOrEmpty(clientSupportedModes) && !isNullOrEmpty(serverSupportedModes)) + { + short[] ordered = useServerOrder ? serverSupportedModes : clientSupportedModes; + short[] unordered = useServerOrder ? clientSupportedModes : serverSupportedModes; + + for (int i = 0; i < ordered.length; ++i) + { + short candidate = ordered[i]; + + if (Arrays.contains(unordered, candidate) && + PskKeyExchangeMode.isRecognized(candidate)) + { + return candidate; + } + } + } + return -1; + } + static TlsSecret getPSKEarlySecret(TlsCrypto crypto, TlsPSK psk) { int cryptoHashAlgorithm = TlsCryptoUtils.getHashForPRF(psk.getPRFAlgorithm()); From 15b78d25e91eaa1d0b6dd6f6199559726ffcf7c6 Mon Sep 17 00:00:00 2001 From: royb Date: Thu, 26 Jun 2025 15:51:22 -0400 Subject: [PATCH 434/890] Added ParametersWithDigest for HashMLDSASigner.java --- .../crypto/params/ParametersWithDigest.java | 34 +++++++++ .../pqc/crypto/mldsa/HashMLDSASigner.java | 30 ++++---- .../pqc/crypto/test/MLDSATest.java | 76 +++++++++++++++++++ 3 files changed, 123 insertions(+), 17 deletions(-) create mode 100644 core/src/main/java/org/bouncycastle/crypto/params/ParametersWithDigest.java diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithDigest.java b/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithDigest.java new file mode 100644 index 0000000000..d444b9f501 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithDigest.java @@ -0,0 +1,34 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Digest; + +public class ParametersWithDigest + implements CipherParameters +{ + private CipherParameters parameters; + private Digest digest; + + public ParametersWithDigest( + CipherParameters parameters, + Digest digest) + { + if (digest == null) + { + throw new NullPointerException("'digest' cannot be null"); + } + + this.parameters = parameters; + this.digest = digest; + } + + public Digest getDigest() + { + return digest; + } + + public CipherParameters getParameters() + { + return parameters; + } +} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java index a7cdbce059..c90f230460 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java @@ -10,6 +10,7 @@ import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Signer; import org.bouncycastle.crypto.params.ParametersWithContext; +import org.bouncycastle.crypto.params.ParametersWithDigest; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.pqc.crypto.DigestUtils; @@ -31,6 +32,16 @@ public HashMLDSASigner() public void init(boolean forSigning, CipherParameters param) { + if (param instanceof ParametersWithDigest) + { + ParametersWithDigest withDigest = (ParametersWithDigest) param; + param = withDigest.getParameters(); + digest = withDigest.getDigest(); + } + else + { + digest = engine.shake256Digest; + } byte[] ctx = EMPTY_CONTEXT; if (param instanceof ParametersWithContext) { @@ -74,7 +85,8 @@ public void init(boolean forSigning, CipherParameters param) engine = parameters.getEngine(null); engine.initVerify(pubKey.rho, pubKey.t1, true, ctx); } - digest = engine.shake256Digest; + + byte[] digestOIDEncoding; try { @@ -137,20 +149,4 @@ public void reset() // // return engine.verifyInternal(signature, signature.length, message, message.length, pubKey.rho, pubKey.t1); // } - -// private static Digest createDigest(MLDSAParameters parameters) -// { - //TODO: MLDSA44 may use SHA2-256, SHA3-256, SHAKE128 - // MLDSA65 may use SHA3-384, SHA2-512 - // MLDSA44/65/87 may use SHA2-512, SHA3-512, SHAKE256 - -// switch (parameters.getType()) -// { -// case MLDSAParameters.TYPE_PURE: -// case MLDSAParameters.TYPE_SHA2_512: -// return new SHAKEDigest(256); -// default: -// throw new IllegalArgumentException("unknown parameters type"); -// } -// } } diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java index d3e5a14588..0afb043968 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java @@ -15,6 +15,16 @@ import junit.framework.TestCase; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.digests.SHA224Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.digests.SHA384Digest; +import org.bouncycastle.crypto.digests.SHA3Digest; +import org.bouncycastle.crypto.digests.SHA512Digest; +import org.bouncycastle.crypto.digests.SHA512tDigest; +import org.bouncycastle.crypto.digests.SHAKEDigest; +import org.bouncycastle.crypto.params.ParametersWithDigest; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.pqc.crypto.mldsa.HashMLDSASigner; import org.bouncycastle.pqc.crypto.mldsa.MLDSAKeyGenerationParameters; @@ -469,6 +479,72 @@ public void testSigVerCombinedVectorSet() } } + public void testHashMLDSA() + throws Exception + { + Digest[] digests = new Digest[] { + new SHA1Digest(), + new SHA224Digest(), + new SHA256Digest(), + new SHA384Digest(), + new SHA512Digest(), + new SHA512tDigest(224), + new SHA512tDigest(256), + new SHA3Digest(224), + new SHA3Digest(256), + new SHA3Digest(384), + new SHA3Digest(512), + new SHAKEDigest(128), + new SHAKEDigest(256), + }; + SecureRandom random = new SecureRandom(); + + MLDSAKeyPairGenerator kpg = new MLDSAKeyPairGenerator(); + + for (int idx = 0; idx != PARAMETER_SETS.length; idx++) + { + MLDSAParameters parameters = PARAMETER_SETS[idx]; + kpg.init(new MLDSAKeyGenerationParameters(random, parameters)); + + int msgSize = 0; + for (Digest digest :digests ) + { + + do + { + byte[] msg = new byte[msgSize]; + + for (int i = 0; i < 2; ++i) + { + AsymmetricCipherKeyPair kp = kpg.generateKeyPair(); + + HashMLDSASigner signer = new HashMLDSASigner(); + + for (int j = 0; j < 2; ++j) + { + random.nextBytes(msg); + + // sign + signer.init(true, new ParametersWithDigest(kp.getPrivate(), digest)); + signer.update(msg, 0, msg.length); + byte[] signature = signer.generateSignature(); + + // verify + signer.init(false, new ParametersWithDigest(kp.getPublic(), digest)); + signer.update(msg, 0, msg.length); + boolean shouldVerify = signer.verifySignature(signature); + + assertTrue("count = " + i, shouldVerify); + } + } + + msgSize += msgSize < 128 ? 1 : 17; + } + while (msgSize <= 256); + } + } + } + public void testMLDSARejection() throws Exception { From d100a871ba5b8dde1932d01c756cb870e1d1a939 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 30 Jun 2025 17:39:43 +0700 Subject: [PATCH 435/890] Add a comment --- .../main/java/org/bouncycastle/tsp/TimeStampTokenGenerator.java | 1 + 1 file changed, 1 insertion(+) diff --git a/pkix/src/main/java/org/bouncycastle/tsp/TimeStampTokenGenerator.java b/pkix/src/main/java/org/bouncycastle/tsp/TimeStampTokenGenerator.java index 3c353feed1..7f352f7d52 100644 --- a/pkix/src/main/java/org/bouncycastle/tsp/TimeStampTokenGenerator.java +++ b/pkix/src/main/java/org/bouncycastle/tsp/TimeStampTokenGenerator.java @@ -219,6 +219,7 @@ public AttributeTable getAttributes(Map parameters) } else { + // NB: The ASN.1 default for ESSCertIDv2.hashAlgorithm has absent parameters (rather than NULL) digestAlgID = new AlgorithmIdentifier(digestAlgOid); final ESSCertIDv2 essCertIDv2 = new ESSCertIDv2(digestAlgID, certHash, issuerSerial); From 56d2222403407c6b78a362334eb0c3fe5d973e87 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 30 Jun 2025 18:20:53 +0700 Subject: [PATCH 436/890] Add more AuthEnv OIDs, reference CMSAlgorithm --- .../cms/CMSAuthEnvelopedGenerator.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedGenerator.java b/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedGenerator.java index f7de40cf35..e3f7a10c17 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedGenerator.java +++ b/pkix/src/main/java/org/bouncycastle/cms/CMSAuthEnvelopedGenerator.java @@ -1,10 +1,6 @@ package org.bouncycastle.cms; -import java.util.ArrayList; -import java.util.List; - import org.bouncycastle.asn1.cms.OriginatorInfo; -import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; /** * General class for generating a CMS enveloped-data message. @@ -12,9 +8,13 @@ public class CMSAuthEnvelopedGenerator extends CMSEnvelopedGenerator { - public static final String AES128_GCM = NISTObjectIdentifiers.id_aes128_GCM.getId(); - public static final String AES192_GCM = NISTObjectIdentifiers.id_aes192_GCM.getId(); - public static final String AES256_GCM = NISTObjectIdentifiers.id_aes256_GCM.getId(); + public static final String AES128_CCM = CMSAlgorithm.AES128_CCM.getId(); + public static final String AES192_CCM = CMSAlgorithm.AES192_CCM.getId(); + public static final String AES256_CCM = CMSAlgorithm.AES256_CCM.getId(); + public static final String AES128_GCM = CMSAlgorithm.AES128_GCM.getId(); + public static final String AES192_GCM = CMSAlgorithm.AES192_GCM.getId(); + public static final String AES256_GCM = CMSAlgorithm.AES256_GCM.getId(); + public static final String ChaCha20Poly1305 = CMSAlgorithm.ChaCha20Poly1305.getId(); protected CMSAttributeTableGenerator authAttrsGenerator = null; protected CMSAttributeTableGenerator unauthAttrsGenerator = null; From b1c9b00d527cb01821564385493eab695c9f7642 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 30 Jun 2025 19:03:54 +0700 Subject: [PATCH 437/890] Simplify init of KDFCounterBytesGenerator.maxSizeExcl --- .../crypto/generators/KDFCounterBytesGenerator.java | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java index 549dd921ea..323ee91c18 100644 --- a/core/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java @@ -1,13 +1,12 @@ package org.bouncycastle.crypto.generators; -import java.math.BigInteger; - import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.DerivationParameters; import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.MacDerivationFunction; import org.bouncycastle.crypto.params.KDFCounterParameters; import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.Integers; /** * This KDF has been defined by the publicly available NIST SP 800-108 specification. @@ -39,10 +38,6 @@ public class KDFCounterBytesGenerator implements MacDerivationFunction { - - private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE); - private static final BigInteger TWO = BigInteger.valueOf(2); - // please refer to the standard for the meaning of the variable names // all field lengths are in bytes, not in bits as specified by the standard @@ -92,9 +87,7 @@ public void init(DerivationParameters param) int r = kdfParams.getR(); this.ios = new byte[r / 8]; - BigInteger maxSize = TWO.pow(r).multiply(BigInteger.valueOf(h)); - this.maxSizeExcl = maxSize.compareTo(INTEGER_MAX) == 1 ? - Integer.MAX_VALUE : maxSize.intValue(); + this.maxSizeExcl = r >= Integers.numberOfLeadingZeros(h) ? Integer.MAX_VALUE : h << r; // --- set operational state --- From 025e28df60425b1220945a5baae512e7f7c8b600 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 30 Jun 2025 20:53:50 +0700 Subject: [PATCH 438/890] More maxSizeExcl init --- .../KDFDoublePipelineIterationBytesGenerator.java | 11 ++--------- .../crypto/generators/KDFFeedbackBytesGenerator.java | 11 ++--------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java index 6115a1a399..b325b3dde4 100644 --- a/core/src/main/java/org/bouncycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java @@ -1,13 +1,12 @@ package org.bouncycastle.crypto.generators; -import java.math.BigInteger; - import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.DerivationParameters; import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.MacDerivationFunction; import org.bouncycastle.crypto.params.KDFDoublePipelineIterationParameters; import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.Integers; /** * This KDF has been defined by the publicly available NIST SP 800-108 specification. @@ -15,10 +14,6 @@ public class KDFDoublePipelineIterationBytesGenerator implements MacDerivationFunction { - - private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE); - private static final BigInteger TWO = BigInteger.valueOf(2); - // please refer to the standard for the meaning of the variable names // all field lengths are in bytes, not in bits as specified by the standard @@ -71,9 +66,7 @@ public void init(DerivationParameters params) if (dpiParams.useCounter()) { // this is more conservative than the spec - BigInteger maxSize = TWO.pow(r).multiply(BigInteger.valueOf(h)); - this.maxSizeExcl = maxSize.compareTo(INTEGER_MAX) == 1 ? - Integer.MAX_VALUE : maxSize.intValue(); + this.maxSizeExcl = r >= Integers.numberOfLeadingZeros(h) ? Integer.MAX_VALUE : h << r; } else { diff --git a/core/src/main/java/org/bouncycastle/crypto/generators/KDFFeedbackBytesGenerator.java b/core/src/main/java/org/bouncycastle/crypto/generators/KDFFeedbackBytesGenerator.java index 6003037642..6d1c01e18c 100644 --- a/core/src/main/java/org/bouncycastle/crypto/generators/KDFFeedbackBytesGenerator.java +++ b/core/src/main/java/org/bouncycastle/crypto/generators/KDFFeedbackBytesGenerator.java @@ -1,13 +1,12 @@ package org.bouncycastle.crypto.generators; -import java.math.BigInteger; - import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.DerivationParameters; import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.MacDerivationFunction; import org.bouncycastle.crypto.params.KDFFeedbackParameters; import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.util.Integers; /** * This KDF has been defined by the publicly available NIST SP 800-108 specification. @@ -15,10 +14,6 @@ public class KDFFeedbackBytesGenerator implements MacDerivationFunction { - - private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE); - private static final BigInteger TWO = BigInteger.valueOf(2); - // please refer to the standard for the meaning of the variable names // all field lengths are in bytes, not in bits as specified by the standard @@ -70,9 +65,7 @@ public void init(DerivationParameters params) if (feedbackParams.useCounter()) { // this is more conservative than the spec - BigInteger maxSize = TWO.pow(r).multiply(BigInteger.valueOf(h)); - this.maxSizeExcl = maxSize.compareTo(INTEGER_MAX) == 1 ? - Integer.MAX_VALUE : maxSize.intValue(); + this.maxSizeExcl = r >= Integers.numberOfLeadingZeros(h) ? Integer.MAX_VALUE : h << r; } else { From 8137055ff0ff66745353df716363774a58689a6d Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 1 Jul 2025 23:48:01 +0700 Subject: [PATCH 439/890] Refactoring in pg --- .../java/org/bouncycastle/bcpg/AEADUtils.java | 6 +-- .../main/java/org/bouncycastle/bcpg/S2K.java | 45 ++++++++++--------- .../bouncycastle/bcpg/SecretKeyPacket.java | 2 +- .../openpgp/operator/PGPUtil.java | 32 ++++--------- 4 files changed, 36 insertions(+), 49 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/AEADUtils.java b/pg/src/main/java/org/bouncycastle/bcpg/AEADUtils.java index 504a657152..61c4b2f2bb 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/AEADUtils.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/AEADUtils.java @@ -51,8 +51,8 @@ public static int getAuthTagLength(int aeadAlgorithmTag) * Split a given byte array containing
      m
      bytes of key and
      n-8
      bytes of IV into * two separate byte arrays. *
      m
      is the key length of the cipher algorithm, while
      n
      is the IV length of the AEAD algorithm. - * Note, that the IV is filled with
      n-8
      bytes only, the remainder is left as 0s. - * Return an array of both arrays with the key and index 0 and the IV at index 1. + * Note that the IV is filled with
      n-8
      bytes only, the remainder is left as 0s. + * Return an array of both arrays with the key at index 0 and the IV at index 1. * * @param messageKeyAndIv
      m+n-8
      bytes of concatenated message key and IV * @param cipherAlgo symmetric cipher algorithm @@ -62,7 +62,7 @@ public static int getAuthTagLength(int aeadAlgorithmTag) public static byte[][] splitMessageKeyAndIv(byte[] messageKeyAndIv, int cipherAlgo, int aeadAlgo) { int keyLen = SymmetricKeyUtils.getKeyLengthInOctets(cipherAlgo); - int ivLen = AEADUtils.getIVLength(aeadAlgo); + int ivLen = getIVLength(aeadAlgo); byte[] messageKey = new byte[keyLen]; byte[] iv = new byte[ivLen]; System.arraycopy(messageKeyAndIv, 0, messageKey, 0, messageKey.length); diff --git a/pg/src/main/java/org/bouncycastle/bcpg/S2K.java b/pg/src/main/java/org/bouncycastle/bcpg/S2K.java index 7d1461eaed..6288b4e73f 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/S2K.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/S2K.java @@ -6,6 +6,7 @@ import java.security.SecureRandom; import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.util.Integers; /** @@ -54,7 +55,7 @@ public class S2K * This method is deprecated to use, since it can be brute-forced when used * with a low-entropy string, such as those typically provided by users. * Additionally, the usage of Simple S2K can lead to key and IV reuse. - * Therefore, in OpenPGP v6, Therefore, when generating an S2K specifier, + * Therefore, in OpenPGP v6, when generating an S2K specifier, * an implementation MUST NOT use Simple S2K. * * @deprecated use {@link #SALTED_AND_ITERATED} or {@link #ARGON_2} instead. @@ -416,28 +417,26 @@ public void encode( BCPGOutputStream out) throws IOException { + out.write(type); + switch (type) { case SIMPLE: - out.write(type); out.write(algorithm); break; case SALTED: - out.write(type); out.write(algorithm); out.write(iv); break; case SALTED_AND_ITERATED: - out.write(type); out.write(algorithm); out.write(iv); writeOneOctetOrThrow(out, itCount, "Iteration count"); break; case ARGON_2: - out.write(type); out.write(iv); writeOneOctetOrThrow(out, passes, "Passes"); writeOneOctetOrThrow(out, parallelism, "Parallelism"); @@ -445,7 +444,6 @@ public void encode( break; case GNU_DUMMY_S2K: - out.write(type); out.write(algorithm); out.write('G'); out.write('N'); @@ -471,7 +469,7 @@ public void encode( private void writeOneOctetOrThrow(BCPGOutputStream out, int val, String valName) throws IOException { - if (val >= 256) + if ((val & 0xFFFFFF00) != 0) { throw new IllegalStateException(valName + " not encodable"); } @@ -539,27 +537,30 @@ public Argon2Params(byte[] salt, int passes, int parallelism, int memSizeExp) { throw new IllegalArgumentException("Argon2 uses 16 bytes of salt"); } - this.salt = salt; - - if (passes < 1) + if (passes < 1 | passes > 255) { - throw new IllegalArgumentException("Number of passes MUST be positive, non-zero"); + throw new IllegalArgumentException("Passes MUST be an integer value from 1 to 255."); } - this.passes = passes; - - if (parallelism < 1) + if (parallelism < 1 || parallelism > 255) { - throw new IllegalArgumentException("Parallelism MUST be positive, non-zero."); + throw new IllegalArgumentException("Parallelism MUST be an integer value from 1 to 255."); } - this.parallelism = parallelism; - // log₂p = logₑp / logₑ2 - double log2_p = Math.log(parallelism) / Math.log(2); - // see https://www.rfc-editor.org/rfc/rfc9580.html#section-3.7.1.4-5 - if (memSizeExp < (3 + Math.ceil(log2_p)) || memSizeExp > 31) + /* + * Memory size (i.e. 1 << memorySizeExponent) MUST be an integer number of kibibytes from 8*p to 2^32-1. + * Max here is 30 because we are treating memory size as a signed 32-bit value. + */ + int minExp = 35 - Integers.numberOfLeadingZeros(parallelism - 1); + int maxExp = 30; + if (memSizeExp < minExp || memSizeExp > maxExp) { - throw new IllegalArgumentException("Memory size exponent MUST be between 3 + ⌈log₂(parallelism)⌉ and 31"); + throw new IllegalArgumentException( + "Memory size exponent MUST be an integer value from 3 + bitlen(parallelism - 1) to 30."); } + + this.salt = salt; + this.passes = passes; + this.parallelism = parallelism; this.memSizeExp = memSizeExp; } @@ -685,7 +686,7 @@ public static GNUDummyParams internal() { return new GNUDummyParams(GNU_PROTECTION_MODE_INTERNAL); } - + /** * Return the GNU Dummy S2K protection method. * diff --git a/pg/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java index 9b59fd4f5c..5a16ad32b8 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/SecretKeyPacket.java @@ -195,7 +195,7 @@ public class SecretKeyPacket if (s2kUsage == USAGE_AEAD) { iv = new byte[AEADUtils.getIVLength(aeadAlgorithm)]; - Streams.readFully(in, iv); + in.readFully(iv); } else { diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPUtil.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPUtil.java index 2d77e5d9a1..b651960b19 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPUtil.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/PGPUtil.java @@ -63,8 +63,7 @@ static byte[] makeKeyFromPassPhrase( { if (s2k.getType() == S2K.ARGON_2) { - Argon2Parameters.Builder builder = new Argon2Parameters - .Builder(Argon2Parameters.ARGON2_id) + Argon2Parameters.Builder builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id) .withSalt(s2k.getIV()) .withIterations(s2k.getPasses()) .withParallelism(s2k.getParallelism()) @@ -97,13 +96,13 @@ else if (s2k.getHashAlgorithm() != digestCalculator.getAlgorithm()) byte[] iv = s2k != null? s2k.getIV() : null; while (generatedBytes < keyBytes.length) { - if (s2k != null) + for (int i = 0; i != loopCount; i++) { - for (int i = 0; i != loopCount; i++) - { - dOut.write(0); - } + dOut.write(0); + } + if (s2k != null) + { switch (s2k.getType()) { case S2K.SIMPLE: @@ -151,28 +150,15 @@ else if (s2k.getHashAlgorithm() != digestCalculator.getAlgorithm()) } else { - for (int i = 0; i != loopCount; i++) - { - dOut.write((byte)0); - } - dOut.write(pBytes); } dOut.close(); byte[] dig = digestCalculator.getDigest(); - - if (dig.length > (keyBytes.length - generatedBytes)) - { - System.arraycopy(dig, 0, keyBytes, generatedBytes, keyBytes.length - generatedBytes); - } - else - { - System.arraycopy(dig, 0, keyBytes, generatedBytes, dig.length); - } - - generatedBytes += dig.length; + int toCopy = Math.min(dig.length, keyBytes.length - generatedBytes); + System.arraycopy(dig, 0, keyBytes, generatedBytes, toCopy); + generatedBytes += toCopy; loopCount++; } From 365c6bcd051eb1f4aec6ac596395e85fe38e5fe6 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 3 Jul 2025 11:58:49 +0700 Subject: [PATCH 440/890] Javadoc --- .../smime/validator/SignedMailValidator.java | 59 +++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java b/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java index a6c99488de..2faf4e8ae4 100644 --- a/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java +++ b/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java @@ -97,21 +97,20 @@ public class SignedMailValidator private Class certPathReviewerClass; /** - * Validates the signed {@link MimeMessage} message. The - * {@link PKIXParameters} from param are used for the certificate path - * validation. The actual PKIXParameters used for the certificate path - * validation is a copy of param with the followin changes:
      - The - * validation date is changed to the signature time
      - A CertStore with - * certificates and crls from the mail message is added to the CertStores.
      + * Validates the signed {@link MimeMessage} message. The {@link PKIXParameters} from + * param are used for the certificate path validation. The actual + * {@link PKIXParameters} used for the certificate path validation are a copy of param + * with the following changes:
      + * - The validation date is changed to the signature time.
      + * - A CertStore with certificates and CRLs from the mail message is added to the CertStores.
      *
      - * In param it's also possible to add additional CertStores - * with intermediate Certificates and/or CRLs which then are also used for - * the validation. + * In param it's also possible to add additional CertStores with intermediate + * certificates and/or CRLs which then are also used for the validation. * - * @param message the signed MimeMessage - * @param param the parameters for the certificate path validation - * @throws SignedMailValidatorException if the message is no signed message or if an exception occurs - * reading the message + * @param message the signed {@link MimeMessage}. + * @param param the parameters for the certificate path validation. + * @throws {@link SignedMailValidatorException} if the message is not a signed message or if an + * exception occurs reading the message. */ public SignedMailValidator(MimeMessage message, PKIXParameters param) throws SignedMailValidatorException @@ -120,27 +119,25 @@ public SignedMailValidator(MimeMessage message, PKIXParameters param) } /** - * Validates the signed {@link MimeMessage} message. The - * {@link PKIXParameters} from param are used for the certificate path - * validation. The actual PKIXParameters used for the certificate path - * validation is a copy of param with the followin changes:
      - The - * validation date is changed to the signature time
      - A CertStore with - * certificates and crls from the mail message is added to the CertStores.
      + * Validates the signed {@link MimeMessage} message. The {@link PKIXParameters} from + * param are used for the certificate path validation. The actual + * {@link PKIXParameters} used for the certificate path validation are a copy of param + * with the following changes:
      + * - The validation date is changed to the signature time.
      + * - A CertStore with certificates and CRLs from the mail message is added to the CertStores.
      *
      - * In param it's also possible to add additional CertStores - * with intermediate Certificates and/or CRLs which then are also used for - * the validation. + * In param it's also possible to add additional CertStores with intermediate + * certificates and/or CRLs which then are also used for the validation. * - * @param message the signed MimeMessage - * @param param the parameters for the certificate path validation + * @param message the signed {@link MimeMessage}. + * @param param the parameters for the certificate path validation. * @param certPathReviewerClass a subclass of {@link PKIXCertPathReviewer}. The SignedMailValidator - * uses objects of this type for the cert path vailidation. The class must - * have an empty constructor. - * @throws SignedMailValidatorException if the message is no signed message or if an exception occurs - * reading the message - * @throws IllegalArgumentException if the certPathReviewerClass is not a - * subclass of {@link PKIXCertPathReviewer} or objects of - * certPathReviewerClass can not be instantiated + * uses objects of this type for the cert path vailidation. The class must have an empty + * constructor. + * @throws SignedMailValidatorException if the message is not a signed message or if an exception + * occurs reading the message. + * @throws {@link IllegalArgumentException} if the certPathReviewerClass is not a subclass of + * {@link PKIXCertPathReviewer} or objects of certPathReviewerClass can not be instantiated. */ public SignedMailValidator(MimeMessage message, PKIXParameters param, Class certPathReviewerClass) throws SignedMailValidatorException From 37e0e276ebff8c9192d7aa6962e394bc6365e1bf Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 3 Jul 2025 14:04:23 +0700 Subject: [PATCH 441/890] Use JcaX509ExtensionUtils.parseExtensionValue --- .../smime/validator/SignedMailValidator.java | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java b/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java index 2faf4e8ae4..956c5b90f2 100644 --- a/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java +++ b/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java @@ -35,9 +35,6 @@ import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1IA5String; -import org.bouncycastle.asn1.ASN1InputStream; -import org.bouncycastle.asn1.ASN1OctetString; -import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1String; import org.bouncycastle.asn1.ASN1TaggedObject; @@ -55,6 +52,7 @@ import org.bouncycastle.asn1.x509.KeyPurposeId; import org.bouncycastle.asn1.x509.TBSCertificate; import org.bouncycastle.cert.jcajce.JcaCertStoreBuilder; +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.SignerInformationStore; import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; @@ -432,7 +430,7 @@ public static Set getEmailAddresses(X509Certificate cert) byte[] ext = cert.getExtensionValue(SUBJECT_ALTERNATIVE_NAME); if (ext != null) { - ASN1Sequence altNames = ASN1Sequence.getInstance(getObject(ext)); + ASN1Sequence altNames = ASN1Sequence.getInstance(JcaX509ExtensionUtils.parseExtensionValue(ext)); for (int j = 0; j < altNames.size(); j++) { ASN1TaggedObject o = (ASN1TaggedObject)altNames @@ -450,15 +448,6 @@ public static Set getEmailAddresses(X509Certificate cert) return addresses; } - private static ASN1Primitive getObject(byte[] ext) - throws IOException - { - ASN1InputStream aIn = new ASN1InputStream(ext); - ASN1OctetString octs = ASN1OctetString.getInstance(aIn.readObject()); - - return ASN1Primitive.fromByteArray(octs.getOctets()); - } - protected void checkSignerCert(X509Certificate cert, List errors, List notifications) { @@ -507,7 +496,7 @@ else if (key instanceof DSAPublicKey) if (ext != null) { ExtendedKeyUsage extKeyUsage = ExtendedKeyUsage - .getInstance(getObject(ext)); + .getInstance(JcaX509ExtensionUtils.parseExtensionValue(ext)); if (!extKeyUsage .hasKeyPurposeId(KeyPurposeId.anyExtendedKeyUsage) && !extKeyUsage @@ -762,7 +751,8 @@ public static Object[] createCertPath(X509Certificate signerCert, { try { - AuthorityKeyIdentifier kid = AuthorityKeyIdentifier.getInstance(getObject(authKeyIdentBytes)); + AuthorityKeyIdentifier kid = AuthorityKeyIdentifier.getInstance( + JcaX509ExtensionUtils.parseExtensionValue(authKeyIdentBytes)); if (kid.getKeyIdentifier() != null) { select.setSubjectKeyIdentifier(new DEROctetString(kid.getKeyIdentifier()).getEncoded(ASN1Encoding.DER)); From c2dce6913dc4a378a7e71ed386e24b425581bcdd Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 3 Jul 2025 14:27:23 +0700 Subject: [PATCH 442/890] Refactor AuthorityKeyIdentifier and usage --- .../asn1/x509/AuthorityKeyIdentifier.java | 39 ++++++++++++------- .../smime/validator/SignedMailValidator.java | 18 +++++---- .../pkix/jcajce/PKIXCertPathReviewer.java | 27 ++++++------- .../cert/test/X509ExtensionUtilsTest.java | 30 +++++++++++--- .../keystore/pkcs12/PKCS12KeyStoreSpi.java | 10 ++--- .../provider/CertPathValidatorUtilities.java | 16 ++++---- .../x509/PKIXCertPathReviewer.java | 26 ++++++------- 7 files changed, 102 insertions(+), 64 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java index b0196c368e..570747a501 100644 --- a/core/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java +++ b/core/src/main/java/org/bouncycastle/asn1/x509/AuthorityKeyIdentifier.java @@ -35,15 +35,13 @@ public class AuthorityKeyIdentifier extends ASN1Object { - ASN1OctetString keyidentifier = null; + ASN1OctetString keyIdentifier = null; GeneralNames certissuer = null; ASN1Integer certserno = null; - public static AuthorityKeyIdentifier getInstance( - ASN1TaggedObject obj, - boolean explicit) + public static AuthorityKeyIdentifier getInstance(ASN1TaggedObject obj, boolean explicit) { - return getInstance(ASN1Sequence.getInstance(obj, explicit)); + return new AuthorityKeyIdentifier(ASN1Sequence.getInstance(obj, explicit)); } public static AuthorityKeyIdentifier getInstance( @@ -78,7 +76,7 @@ protected AuthorityKeyIdentifier( switch (o.getTagNo()) { case 0: - this.keyidentifier = ASN1OctetString.getInstance(o, false); + this.keyIdentifier = ASN1OctetString.getInstance(o, false); break; case 1: this.certissuer = GeneralNames.getInstance(o, false); @@ -128,7 +126,7 @@ public AuthorityKeyIdentifier( digest.update(bytes, 0, bytes.length); digest.doFinal(resBuf, 0); - this.keyidentifier = new DEROctetString(resBuf); + this.keyIdentifier = new DEROctetString(resBuf); this.certissuer = name; this.certserno = (serialNumber != null) ? new ASN1Integer(serialNumber) : null; } @@ -162,21 +160,34 @@ public AuthorityKeyIdentifier( GeneralNames name, BigInteger serialNumber) { - this.keyidentifier = (keyIdentifier != null) ? new DEROctetString(Arrays.clone(keyIdentifier)) : null; + this.keyIdentifier = (keyIdentifier != null) ? new DEROctetString(Arrays.clone(keyIdentifier)) : null; this.certissuer = name; this.certserno = (serialNumber != null) ? new ASN1Integer(serialNumber) : null; } - + + /** + * @deprecated Use {@link #getKeyIdentifierOctets()} instead. + */ public byte[] getKeyIdentifier() { - if (keyidentifier != null) + return getKeyIdentifierOctets(); + } + + public byte[] getKeyIdentifierOctets() + { + if (keyIdentifier != null) { - return keyidentifier.getOctets(); + return keyIdentifier.getOctets(); } return null; } + public ASN1OctetString getKeyIdentifierObject() + { + return keyIdentifier; + } + public GeneralNames getAuthorityCertIssuer() { return certissuer; @@ -199,9 +210,9 @@ public ASN1Primitive toASN1Primitive() { ASN1EncodableVector v = new ASN1EncodableVector(3); - if (keyidentifier != null) + if (keyIdentifier != null) { - v.add(new DERTaggedObject(false, 0, keyidentifier)); + v.add(new DERTaggedObject(false, 0, keyIdentifier)); } if (certissuer != null) @@ -220,7 +231,7 @@ public ASN1Primitive toASN1Primitive() public String toString() { // -DM Hex.toHexString - String keyID = (keyidentifier != null) ? Hex.toHexString(keyidentifier.getOctets()) : "null"; + String keyID = (keyIdentifier != null) ? Hex.toHexString(keyIdentifier.getOctets()) : "null"; return "AuthorityKeyIdentifier: KeyID(" + keyID + ")"; } diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java b/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java index 956c5b90f2..87b5496e19 100644 --- a/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java +++ b/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java @@ -35,10 +35,10 @@ import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1IA5String; +import org.bouncycastle.asn1.ASN1OctetString; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1String; import org.bouncycastle.asn1.ASN1TaggedObject; -import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.cms.Attribute; import org.bouncycastle.asn1.cms.AttributeTable; import org.bouncycastle.asn1.cms.CMSAttributes; @@ -746,16 +746,19 @@ public static Object[] createCertPath(X509Certificate signerCert, { throw new IllegalStateException(e.toString()); } - byte[] authKeyIdentBytes = cert.getExtensionValue(Extension.authorityKeyIdentifier.getId()); - if (authKeyIdentBytes != null) + + byte[] akiExtValue = cert.getExtensionValue(Extension.authorityKeyIdentifier.getId()); + if (akiExtValue != null) { try { - AuthorityKeyIdentifier kid = AuthorityKeyIdentifier.getInstance( - JcaX509ExtensionUtils.parseExtensionValue(authKeyIdentBytes)); - if (kid.getKeyIdentifier() != null) + AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance( + JcaX509ExtensionUtils.parseExtensionValue(akiExtValue)); + + ASN1OctetString keyIdentifier = aki.getKeyIdentifierObject(); + if (keyIdentifier != null) { - select.setSubjectKeyIdentifier(new DEROctetString(kid.getKeyIdentifier()).getEncoded(ASN1Encoding.DER)); + select.setSubjectKeyIdentifier(keyIdentifier.getEncoded(ASN1Encoding.DER)); } } catch (IOException ioe) @@ -763,6 +766,7 @@ public static Object[] createCertPath(X509Certificate signerCert, // ignore } } + boolean userProvided = false; cert = findNextCert(systemCertStores, select, certSet); diff --git a/pkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXCertPathReviewer.java b/pkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXCertPathReviewer.java index 2234ca031e..7c7810db43 100644 --- a/pkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXCertPathReviewer.java +++ b/pkix/src/main/java/org/bouncycastle/pkix/jcajce/PKIXCertPathReviewer.java @@ -38,6 +38,7 @@ import javax.security.auth.x500.X500Principal; import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1Enumerated; import org.bouncycastle.asn1.ASN1IA5String; import org.bouncycastle.asn1.ASN1InputStream; @@ -47,7 +48,6 @@ import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; -import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.x509.AccessDescription; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.AuthorityInformationAccess; @@ -66,6 +66,7 @@ import org.bouncycastle.asn1.x509.qualified.Iso4217CurrencyCode; import org.bouncycastle.asn1.x509.qualified.MonetaryValue; import org.bouncycastle.asn1.x509.qualified.QCStatement; +import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; import org.bouncycastle.pkix.PKIXNameConstraintValidator; import org.bouncycastle.pkix.PKIXNameConstraintValidatorException; import org.bouncycastle.pkix.util.ErrorBundle; @@ -858,11 +859,11 @@ else if (isSelfIssued(cert)) { ErrorBundle msg = createErrorBundle("CertPathReviewer.NoIssuerPublicKey"); // if there is an authority key extension add the serial and issuer of the missing certificate - byte[] akiBytes = cert.getExtensionValue(Extension.authorityKeyIdentifier.getId()); - if (akiBytes != null) + byte[] akiExtValue = cert.getExtensionValue(Extension.authorityKeyIdentifier.getId()); + if (akiExtValue != null) { AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance( - DEROctetString.getInstance(akiBytes).getOctets()); + ASN1OctetString.getInstance(akiExtValue).getOctets()); GeneralNames issuerNames = aki.getAuthorityCertIssuer(); if (issuerNames != null) { @@ -2441,25 +2442,25 @@ protected Collection getTrustAnchors(X509Certificate cert, Set trustanchors) thr try { certSelectX509.setSubject(getEncodedIssuerPrincipal(cert).getEncoded()); - byte[] ext = cert.getExtensionValue(Extension.authorityKeyIdentifier.getId()); - if (ext != null) + byte[] akiExtValue = cert.getExtensionValue(Extension.authorityKeyIdentifier.getId()); + if (akiExtValue != null) { - ASN1OctetString oct = (ASN1OctetString)ASN1Primitive.fromByteArray(ext); - AuthorityKeyIdentifier authID = AuthorityKeyIdentifier.getInstance(ASN1Primitive.fromByteArray(oct.getOctets())); + AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance( + JcaX509ExtensionUtils.parseExtensionValue(akiExtValue)); // we ignore key identifier as if set, selector expects parent to have subjectKeyID - BigInteger serial = authID.getAuthorityCertSerialNumber(); + BigInteger serial = aki.getAuthorityCertSerialNumber(); if (serial != null) { - certSelectX509.setSerialNumber(authID.getAuthorityCertSerialNumber()); + certSelectX509.setSerialNumber(aki.getAuthorityCertSerialNumber()); } else { - byte[] keyID = authID.getKeyIdentifier(); - if (keyID != null) + ASN1OctetString keyIdentifier = aki.getKeyIdentifierObject(); + if (keyIdentifier != null) { - certSelectX509.setSubjectKeyIdentifier(new DEROctetString(keyID).getEncoded()); + certSelectX509.setSubjectKeyIdentifier(keyIdentifier.getEncoded(ASN1Encoding.DER)); } } } diff --git a/pkix/src/test/java/org/bouncycastle/cert/test/X509ExtensionUtilsTest.java b/pkix/src/test/java/org/bouncycastle/cert/test/X509ExtensionUtilsTest.java index 2333846812..d2b2226ee6 100644 --- a/pkix/src/test/java/org/bouncycastle/cert/test/X509ExtensionUtilsTest.java +++ b/pkix/src/test/java/org/bouncycastle/cert/test/X509ExtensionUtilsTest.java @@ -78,7 +78,11 @@ public void basicTest() { fail("v0 issuer not matched"); } - if (!Arrays.areEqual(Hex.decode("f0d46a0a97e24c20ec857ee6831e0be8a797c49d"), authKeyId.getKeyIdentifier())) + if (!Arrays.areEqual(Hex.decode("f0d46a0a97e24c20ec857ee6831e0be8a797c49d"), authKeyId.getKeyIdentifierOctets())) + { + fail("v0 keyID not matched"); + } + if (!Arrays.areEqual(Hex.decode("f0d46a0a97e24c20ec857ee6831e0be8a797c49d"), authKeyId.getKeyIdentifierObject().getOctets())) { fail("v0 keyID not matched"); } @@ -92,7 +96,11 @@ public void basicTest() { fail("v3 issuer not matched"); } - if (!Arrays.areEqual(Hex.decode("c4733fe7e5fdd51bdd98d75b345674d85ba0f76c"), authKeyId.getKeyIdentifier())) + if (!Arrays.areEqual(Hex.decode("c4733fe7e5fdd51bdd98d75b345674d85ba0f76c"), authKeyId.getKeyIdentifierOctets())) + { + fail("v3 keyID not matched"); + } + if (!Arrays.areEqual(Hex.decode("c4733fe7e5fdd51bdd98d75b345674d85ba0f76c"), authKeyId.getKeyIdentifierObject().getOctets())) { fail("v3 keyID not matched"); } @@ -133,7 +141,11 @@ public void jcaTest() { fail("v0 issuer not matched"); } - if (!Arrays.areEqual(Hex.decode("f0d46a0a97e24c20ec857ee6831e0be8a797c49d"), authKeyId.getKeyIdentifier())) + if (!Arrays.areEqual(Hex.decode("f0d46a0a97e24c20ec857ee6831e0be8a797c49d"), authKeyId.getKeyIdentifierOctets())) + { + fail("v0 keyID not matched"); + } + if (!Arrays.areEqual(Hex.decode("f0d46a0a97e24c20ec857ee6831e0be8a797c49d"), authKeyId.getKeyIdentifierObject().getOctets())) { fail("v0 keyID not matched"); } @@ -147,7 +159,11 @@ public void jcaTest() { fail("v3 issuer not matched"); } - if (!Arrays.areEqual(Hex.decode("c4733fe7e5fdd51bdd98d75b345674d85ba0f76c"), authKeyId.getKeyIdentifier())) + if (!Arrays.areEqual(Hex.decode("c4733fe7e5fdd51bdd98d75b345674d85ba0f76c"), authKeyId.getKeyIdentifierOctets())) + { + fail("v3 keyID not matched"); + } + if (!Arrays.areEqual(Hex.decode("c4733fe7e5fdd51bdd98d75b345674d85ba0f76c"), authKeyId.getKeyIdentifierObject().getOctets())) { fail("v3 keyID not matched"); } @@ -162,7 +178,11 @@ public void jcaTest() { fail("v3 issuer not matched"); } - if (!Arrays.areEqual(Hex.decode("c4733fe7e5fdd51bdd98d75b345674d85ba0f76c"), authKeyId.getKeyIdentifier())) + if (!Arrays.areEqual(Hex.decode("c4733fe7e5fdd51bdd98d75b345674d85ba0f76c"), authKeyId.getKeyIdentifierOctets())) + { + fail("v3 keyID not matched"); + } + if (!Arrays.areEqual(Hex.decode("c4733fe7e5fdd51bdd98d75b345674d85ba0f76c"), authKeyId.getKeyIdentifierObject().getOctets())) { fail("v3 keyID not matched"); } diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java index 219efb6d8d..3fd5c6bf91 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java @@ -441,13 +441,13 @@ public Certificate[] engineGetCertificateChain( X509Certificate x509c = (X509Certificate)c; Certificate nextC = null; - byte[] akiBytes = x509c.getExtensionValue(Extension.authorityKeyIdentifier.getId()); - if (akiBytes != null) + byte[] akiExtValue = x509c.getExtensionValue(Extension.authorityKeyIdentifier.getId()); + if (akiExtValue != null) { - ASN1OctetString akiValue = ASN1OctetString.getInstance(akiBytes); - AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance(akiValue.getOctets()); + AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance( + ASN1OctetString.getInstance(akiExtValue).getOctets()); - byte[] keyID = aki.getKeyIdentifier(); + byte[] keyID = aki.getKeyIdentifierOctets(); if (null != keyID) { nextC = (Certificate)chainCerts.get(new CertId(keyID)); diff --git a/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java b/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java index 951e12be40..00809fec05 100644 --- a/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java +++ b/prov/src/main/java/org/bouncycastle/jce/provider/CertPathValidatorUtilities.java @@ -42,6 +42,7 @@ import javax.security.auth.x500.X500Principal; import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1Enumerated; import org.bouncycastle.asn1.ASN1GeneralizedTime; import org.bouncycastle.asn1.ASN1Integer; @@ -51,7 +52,6 @@ import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1String; -import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.style.RFC4519Style; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; @@ -1236,14 +1236,16 @@ static Collection findIssuerCerts( try { - byte[] akiExtensionValue = cert.getExtensionValue(AUTHORITY_KEY_IDENTIFIER); - if (akiExtensionValue != null) + byte[] akiExtValue = cert.getExtensionValue(AUTHORITY_KEY_IDENTIFIER); + if (akiExtValue != null) { - ASN1OctetString aki = ASN1OctetString.getInstance(akiExtensionValue); - byte[] authorityKeyIdentifier = AuthorityKeyIdentifier.getInstance(aki.getOctets()).getKeyIdentifier(); - if (authorityKeyIdentifier != null) + AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance( + ASN1OctetString.getInstance(akiExtValue).getOctets()); + + ASN1OctetString keyIdentifier = aki.getKeyIdentifierObject(); + if (keyIdentifier != null) { - selector.setSubjectKeyIdentifier(new DEROctetString(authorityKeyIdentifier).getEncoded()); + selector.setSubjectKeyIdentifier(keyIdentifier.getEncoded(ASN1Encoding.DER)); } } } diff --git a/prov/src/main/java/org/bouncycastle/x509/PKIXCertPathReviewer.java b/prov/src/main/java/org/bouncycastle/x509/PKIXCertPathReviewer.java index d8d090f066..414befc086 100644 --- a/prov/src/main/java/org/bouncycastle/x509/PKIXCertPathReviewer.java +++ b/prov/src/main/java/org/bouncycastle/x509/PKIXCertPathReviewer.java @@ -38,6 +38,7 @@ import javax.security.auth.x500.X500Principal; import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1Enumerated; import org.bouncycastle.asn1.ASN1IA5String; import org.bouncycastle.asn1.ASN1InputStream; @@ -47,7 +48,6 @@ import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1TaggedObject; -import org.bouncycastle.asn1.DEROctetString; import org.bouncycastle.asn1.x509.AccessDescription; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.AuthorityInformationAccess; @@ -852,11 +852,11 @@ else if (isSelfIssued(cert)) { ErrorBundle msg = new ErrorBundle(RESOURCE_NAME,"CertPathReviewer.NoIssuerPublicKey"); // if there is an authority key extension add the serial and issuer of the missing certificate - byte[] akiBytes = cert.getExtensionValue(Extension.authorityKeyIdentifier.getId()); - if (akiBytes != null) + byte[] akiExtValue = cert.getExtensionValue(Extension.authorityKeyIdentifier.getId()); + if (akiExtValue != null) { AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance( - DEROctetString.getInstance(akiBytes).getOctets()); + ASN1OctetString.getInstance(akiExtValue).getOctets()); GeneralNames issuerNames = aki.getAuthorityCertIssuer(); if (issuerNames != null) { @@ -2435,25 +2435,25 @@ protected Collection getTrustAnchors(X509Certificate cert, Set trustanchors) thr try { certSelectX509.setSubject(getEncodedIssuerPrincipal(cert).getEncoded()); - byte[] ext = cert.getExtensionValue(Extension.authorityKeyIdentifier.getId()); - if (ext != null) + byte[] akiExtValue = cert.getExtensionValue(Extension.authorityKeyIdentifier.getId()); + if (akiExtValue != null) { - ASN1OctetString oct = (ASN1OctetString)ASN1Primitive.fromByteArray(ext); - AuthorityKeyIdentifier authID = AuthorityKeyIdentifier.getInstance(ASN1Primitive.fromByteArray(oct.getOctets())); + AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance( + ASN1OctetString.getInstance(akiExtValue).getOctets()); // we ignore key identifier as if set, selector expects parent to have subjectKeyID - BigInteger serial = authID.getAuthorityCertSerialNumber(); + BigInteger serial = aki.getAuthorityCertSerialNumber(); if (serial != null) { - certSelectX509.setSerialNumber(authID.getAuthorityCertSerialNumber()); + certSelectX509.setSerialNumber(aki.getAuthorityCertSerialNumber()); } else { - byte[] keyID = authID.getKeyIdentifier(); - if (keyID != null) + ASN1OctetString keyIdentifier = aki.getKeyIdentifierObject(); + if (keyIdentifier != null) { - certSelectX509.setSubjectKeyIdentifier(new DEROctetString(keyID).getEncoded()); + certSelectX509.setSubjectKeyIdentifier(keyIdentifier.getEncoded(ASN1Encoding.DER)); } } } From 51e536f85d169739c906cfe47f2329a398e38224 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 4 Jul 2025 00:05:44 +0700 Subject: [PATCH 443/890] Improvements in mail.smime - user-provided now set correctly for signer cert (previously always true) - user-provided no longer redundantly added for trust anchor - javadoc and comments fixes - perf. opts. when selecting certificates - extra test cases and fix AllTests list --- .../smime/examples/ValidateSignedMail.java | 38 +- .../smime/validator/SignedMailValidator.java | 862 +++++++++--------- .../mail/smime/test/AllTests.java | 7 +- .../mail/smime/test/MailGeneralTest.java | 18 +- .../smime/test/SignedMailValidatorTest.java | 13 +- 5 files changed, 464 insertions(+), 474 deletions(-) diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/examples/ValidateSignedMail.java b/mail/src/main/java/org/bouncycastle/mail/smime/examples/ValidateSignedMail.java index 79b4b80f2c..b820f6400c 100644 --- a/mail/src/main/java/org/bouncycastle/mail/smime/examples/ValidateSignedMail.java +++ b/mail/src/main/java/org/bouncycastle/mail/smime/examples/ValidateSignedMail.java @@ -26,9 +26,9 @@ import javax.mail.internet.MimeMessage; import javax.security.auth.x500.X500Principal; -import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.NameConstraints; import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.jce.provider.BouncyCastleProvider; @@ -42,7 +42,6 @@ */ public class ValidateSignedMail { - /* * Use trusted certificates from $JAVA_HOME/lib/security/cacerts as * trustanchors @@ -51,7 +50,6 @@ public class ValidateSignedMail public static void main(String[] args) throws Exception { - Security.addProvider(new BouncyCastleProvider()); // @@ -62,8 +60,7 @@ public static void main(String[] args) throws Exception Session session = Session.getDefaultInstance(props, null); // read message - MimeMessage msg = new MimeMessage(session, new FileInputStream( - "signed.message")); + MimeMessage msg = new MimeMessage(session, new FileInputStream("signed.message")); // create PKIXparameters PKIXParameters param; @@ -137,8 +134,7 @@ public static void verifySignedMail(MimeMessage msg, PKIXParameters param) SignedMailValidator validator = new SignedMailValidator(msg, param); // iterate over all signatures and print results - Iterator it = validator.getSignerInformationStore().getSigners() - .iterator(); + Iterator it = validator.getSignerInformationStore().getSigners().iterator(); while (it.hasNext()) { SignerInformation signer = (SignerInformation) it.next(); @@ -279,27 +275,26 @@ public static void verifySignedMail(MimeMessage msg, PKIXParameters param) } } } - } - protected static TrustAnchor getTrustAnchor(String trustcert) - throws Exception + protected static TrustAnchor getTrustAnchor(String trustcert) throws Exception { X509Certificate cert = loadCert(trustcert); - if (cert != null) + if (cert == null) { - byte[] ncBytes = cert - .getExtensionValue(Extension.nameConstraints.getId()); + return null; + } - if (ncBytes != null) - { - ASN1Encodable extValue = JcaX509ExtensionUtils - .parseExtensionValue(ncBytes); - return new TrustAnchor(cert, extValue.toASN1Primitive().getEncoded(ASN1Encoding.DER)); - } - return new TrustAnchor(cert, null); + byte[] nameConstraints = null; + + byte[] ncExtValue = cert.getExtensionValue(Extension.nameConstraints.getId()); + if (ncExtValue != null) + { + NameConstraints nc = NameConstraints.getInstance(JcaX509ExtensionUtils.parseExtensionValue(ncExtValue)); + nameConstraints = nc.getEncoded(ASN1Encoding.DER); } - return null; + + return new TrustAnchor(cert, nameConstraints); } protected static X509Certificate loadCert(String certfile) @@ -350,5 +345,4 @@ private static TrustAnchor getDummyTrustAnchor() throws Exception PublicKey trustPubKey = kpg.generateKeyPair().getPublic(); return new TrustAnchor(principal, trustPubKey, null); } - } diff --git a/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java b/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java index 87b5496e19..1cd16234c5 100644 --- a/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java +++ b/mail/src/main/java/org/bouncycastle/mail/smime/validator/SignedMailValidator.java @@ -32,13 +32,12 @@ import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMultipart; +import javax.security.auth.x500.X500Principal; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1IA5String; import org.bouncycastle.asn1.ASN1OctetString; -import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.ASN1String; -import org.bouncycastle.asn1.ASN1TaggedObject; import org.bouncycastle.asn1.cms.Attribute; import org.bouncycastle.asn1.cms.AttributeTable; import org.bouncycastle.asn1.cms.CMSAttributes; @@ -49,12 +48,15 @@ import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; import org.bouncycastle.asn1.x509.ExtendedKeyUsage; import org.bouncycastle.asn1.x509.Extension; +import org.bouncycastle.asn1.x509.GeneralName; +import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.KeyPurposeId; -import org.bouncycastle.asn1.x509.TBSCertificate; import org.bouncycastle.cert.jcajce.JcaCertStoreBuilder; +import org.bouncycastle.cert.jcajce.JcaX500NameUtil; import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils; import org.bouncycastle.cms.SignerInformation; import org.bouncycastle.cms.SignerInformationStore; +import org.bouncycastle.cms.SignerInformationVerifier; import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder; import org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter; import org.bouncycastle.mail.smime.SMIMESigned; @@ -71,18 +73,15 @@ public class SignedMailValidator private static final Class DEFAULT_CERT_PATH_REVIEWER = PKIXCertPathReviewer.class; - private static final String EXT_KEY_USAGE = Extension.extendedKeyUsage - .getId(); - - private static final String SUBJECT_ALTERNATIVE_NAME = Extension.subjectAlternativeName - .getId(); - private static final int shortKeyLength = 512; // (365.25*30)*24*3600*1000 private static final long THIRTY_YEARS_IN_MILLI_SEC = 21915l * 12l * 3600l * 1000l; - private static final JcaX509CertSelectorConverter selectorConverter = new JcaX509CertSelectorConverter(); + private static final JcaX509CertSelectorConverter SELECTOR_CONVERTER = new JcaX509CertSelectorConverter(); + + private static final int KU_DIGITAL_SIGNATURE = 0; + private static final int KU_NON_REPUDIATION = 1; private CertStore certs; @@ -110,8 +109,7 @@ public class SignedMailValidator * @throws {@link SignedMailValidatorException} if the message is not a signed message or if an * exception occurs reading the message. */ - public SignedMailValidator(MimeMessage message, PKIXParameters param) - throws SignedMailValidatorException + public SignedMailValidator(MimeMessage message, PKIXParameters param) throws SignedMailValidatorException { this(message, param, DEFAULT_CERT_PATH_REVIEWER); } @@ -144,33 +142,35 @@ public SignedMailValidator(MimeMessage message, PKIXParameters param, Class cert boolean isSubclass = DEFAULT_CERT_PATH_REVIEWER.isAssignableFrom(certPathReviewerClass); if (!isSubclass) { - throw new IllegalArgumentException("certPathReviewerClass is not a subclass of " + DEFAULT_CERT_PATH_REVIEWER.getName()); + throw new IllegalArgumentException( + "certPathReviewerClass is not a subclass of " + DEFAULT_CERT_PATH_REVIEWER.getName()); } - SMIMESigned s; - try { // check if message is multipart signed + SMIMESigned s; if (message.isMimeType("multipart/signed")) { - MimeMultipart mimemp = (MimeMultipart)message.getContent(); + MimeMultipart mimemp = (MimeMultipart) message.getContent(); s = new SMIMESigned(mimemp); } - else if (message.isMimeType("application/pkcs7-mime") - || message.isMimeType("application/x-pkcs7-mime")) + else if (message.isMimeType("application/pkcs7-mime") || message.isMimeType("application/x-pkcs7-mime")) { s = new SMIMESigned(message); } else { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.noSignedMessage"); + ErrorBundle msg = createErrorBundle("SignedMailValidator.noSignedMessage"); throw new SignedMailValidatorException(msg); } // save certstore and signerInformationStore - certs = new JcaCertStoreBuilder().addCertificates(s.getCertificates()).addCRLs(s.getCRLs()).setProvider("BC").build(); + certs = new JcaCertStoreBuilder() + .addCertificates(s.getCertificates()) + .addCRLs(s.getCRLs()) + .setProvider("BC") + .build(); signers = s.getSignerInfos(); // save "from" addresses from message @@ -185,14 +185,14 @@ else if (message.isMimeType("application/pkcs7-mime") } catch (MessagingException ex) { - //ignore garbage in Sender: header + // ignore garbage in Sender: header } int fromsLength = (froms != null) ? froms.length : 0; fromAddresses = new String[fromsLength + ((sender != null) ? 1 : 0)]; for (int i = 0; i < fromsLength; i++) { - InternetAddress inetAddr = (InternetAddress)froms[i]; + InternetAddress inetAddr = (InternetAddress) froms[i]; fromAddresses[i] = inetAddr.getAddress(); } if (sender != null) @@ -207,12 +207,10 @@ else if (message.isMimeType("application/pkcs7-mime") { if (e instanceof SignedMailValidatorException) { - throw (SignedMailValidatorException)e; + throw (SignedMailValidatorException) e; } // exception reading message - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.exceptionReadingMessage", - new Object[]{e.getMessage(), e, e.getClass().getName()}); + ErrorBundle msg = createErrorBundle("SignedMailValidator.exceptionReadingMessage", e); throw new SignedMailValidatorException(msg, e); } @@ -224,7 +222,7 @@ protected void validateSignatures(PKIXParameters pkixParam) { PKIXParameters usedParameters = (PKIXParameters)pkixParam.clone(); - // add crls and certs from mail + // add CRLs and certs from mail usedParameters.addCertStore(certs); Collection c = signers.getSigners(); @@ -238,188 +236,146 @@ protected void validateSignatures(PKIXParameters pkixParam) SignerInformation signer = (SignerInformation)it.next(); // signer certificate - X509Certificate cert = null; + X509Certificate signerCert = null; try { - Collection certCollection = findCerts(usedParameters - .getCertStores(), selectorConverter.getCertSelector(signer.getSID())); - - Iterator certIt = certCollection.iterator(); - if (certIt.hasNext()) - { - cert = (X509Certificate)certIt.next(); - } + X509CertSelector selector = SELECTOR_CONVERTER.getCertSelector(signer.getSID()); + signerCert = findFirstCert(usedParameters.getCertStores(), selector, null); } catch (CertStoreException cse) { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.exceptionRetrievingSignerCert", - new Object[]{cse.getMessage(), cse, cse.getClass().getName()}); + ErrorBundle msg = createErrorBundle("SignedMailValidator.exceptionRetrievingSignerCert", cse); errors.add(msg); } - if (cert != null) + if (signerCert == null) + { + // no signer certificate found + ErrorBundle msg = createErrorBundle("SignedMailValidator.noSignerCert"); + errors.add(msg); + results.put(signer, new ValidationResult(null, false, errors, notifications, null)); + continue; + } + + // check signature + final boolean validSignature = isValidSignature(signerCert, signer, errors); + + // check signer certificate (mail address, key usage, etc) + checkSignerCert(signerCert, errors, notifications); + + // notify if a signed receipt request is in the message + AttributeTable atab = signer.getSignedAttributes(); + if (atab != null) + { + Attribute attr = atab.get(PKCSObjectIdentifiers.id_aa_receiptRequest); + if (attr != null) + { + ErrorBundle msg = createErrorBundle("SignedMailValidator.signedReceiptRequest"); + notifications.add(msg); + } + } + + // check certificate path + + // get signing time if possible, otherwise use current time as signing time + Date signTime = getSignatureTime(signer); + if (signTime == null) // no signing time was found + { + ErrorBundle msg = createErrorBundle("SignedMailValidator.noSigningTime"); + notifications.add(msg); + signTime = pkixParam.getDate(); + if (signTime == null) + { + signTime = new Date(); + } + } + else { - // check signature - boolean validSignature = false; + // check if certificate was valid at signing time try { - validSignature = signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert.getPublicKey())); - if (!validSignature) - { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.signatureNotVerified"); - errors.add(msg); - } + signerCert.checkValidity(signTime); } - catch (Exception e) + catch (CertificateExpiredException e) { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.exceptionVerifyingSignature", - new Object[]{e.getMessage(), e, e.getClass().getName()}); + ErrorBundle msg = createErrorBundle("SignedMailValidator.certExpired", + new Object[]{ new TrustedInput(signTime), new TrustedInput(signerCert.getNotAfter()) }); errors.add(msg); } - - // check signer certificate (mail address, key usage, etc) - checkSignerCert(cert, errors, notifications); - - // notify if a signed receip request is in the message - AttributeTable atab = signer.getSignedAttributes(); - if (atab != null) + catch (CertificateNotYetValidException e) { - Attribute attr = atab.get(PKCSObjectIdentifiers.id_aa_receiptRequest); - if (attr != null) - { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.signedReceiptRequest"); - notifications.add(msg); - } + ErrorBundle msg = createErrorBundle("SignedMailValidator.certNotYetValid", + new Object[]{ new TrustedInput(signTime), new TrustedInput(signerCert.getNotBefore()) }); + errors.add(msg); } + } + usedParameters.setDate(signTime); + + try + { + // construct cert chain + ArrayList userCertStores = new ArrayList(); + userCertStores.add(certs); - // check certificate path + Object[] cpres = createCertPath(signerCert, usedParameters.getTrustAnchors(), pkixParam.getCertStores(), + userCertStores); + CertPath certPath = (CertPath)cpres[0]; + List userProvidedList = (List)cpres[1]; - // get signing time if possible, otherwise use current time as - // signing time - Date signTime = getSignatureTime(signer); - if (signTime == null) // no signing time was found + // validate cert chain + PKIXCertPathReviewer review; + try { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.noSigningTime"); - notifications.add(msg); - signTime = pkixParam.getDate(); - if (signTime == null) - { - signTime = new Date(); - } + review = (PKIXCertPathReviewer)certPathReviewerClass.newInstance(); } - else + catch (IllegalAccessException e) { - // check if certificate was valid at signing time - try - { - cert.checkValidity(signTime); - } - catch (CertificateExpiredException e) - { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.certExpired", - new Object[]{new TrustedInput(signTime), new TrustedInput(cert.getNotAfter())}); - errors.add(msg); - } - catch (CertificateNotYetValidException e) - { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.certNotYetValid", - new Object[]{new TrustedInput(signTime), new TrustedInput(cert.getNotBefore())}); - errors.add(msg); - } + throw new IllegalArgumentException("Cannot instantiate object of type " + + certPathReviewerClass.getName() + ": " + e.getMessage()); } - usedParameters.setDate(signTime); - - try + catch (InstantiationException e) { - // construct cert chain - CertPath certPath; - List userProvidedList; - - List userCertStores = new ArrayList(); - userCertStores.add(certs); - Object[] cpres = createCertPath(cert, usedParameters.getTrustAnchors(), pkixParam.getCertStores(), userCertStores); - certPath = (CertPath)cpres[0]; - userProvidedList = (List)cpres[1]; - - // validate cert chain - PKIXCertPathReviewer review; - try - { - review = (PKIXCertPathReviewer)certPathReviewerClass.newInstance(); - } - catch (IllegalAccessException e) - { - throw new IllegalArgumentException("Cannot instantiate object of type " + - certPathReviewerClass.getName() + ": " + e.getMessage()); - } - catch (InstantiationException e) - { - throw new IllegalArgumentException("Cannot instantiate object of type " + - certPathReviewerClass.getName() + ": " + e.getMessage()); - } - review.init(certPath, usedParameters); - if (!review.isValidCertPath()) - { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.certPathInvalid"); - errors.add(msg); - } - results.put(signer, new ValidationResult(review, - validSignature, errors, notifications, userProvidedList)); + throw new IllegalArgumentException("Cannot instantiate object of type " + + certPathReviewerClass.getName() + ": " + e.getMessage()); } - catch (GeneralSecurityException gse) + review.init(certPath, usedParameters); + if (!review.isValidCertPath()) { - // cannot create cert path - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.exceptionCreateCertPath", - new Object[]{gse.getMessage(), gse, gse.getClass().getName()}); + ErrorBundle msg = createErrorBundle("SignedMailValidator.certPathInvalid"); errors.add(msg); - results.put(signer, new ValidationResult(null, - validSignature, errors, notifications, null)); - } - catch (CertPathReviewerException cpre) - { - // cannot initialize certpathreviewer - wrong parameters - errors.add(cpre.getErrorMessage()); - results.put(signer, new ValidationResult(null, - validSignature, errors, notifications, null)); } + results.put(signer, + new ValidationResult(review, validSignature, errors, notifications, userProvidedList)); } - else - // no signer certificate found + catch (GeneralSecurityException gse) { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.noSignerCert"); + // cannot create cert path + ErrorBundle msg = createErrorBundle("SignedMailValidator.exceptionCreateCertPath", gse); errors.add(msg); - results.put(signer, new ValidationResult(null, false, errors, - notifications, null)); + results.put(signer, new ValidationResult(null, validSignature, errors, notifications, null)); + } + catch (CertPathReviewerException cpre) + { + // cannot initialize certpathreviewer - wrong parameters + errors.add(cpre.getErrorMessage()); + results.put(signer, new ValidationResult(null, validSignature, errors, notifications, null)); } } } - public static Set getEmailAddresses(X509Certificate cert) - throws IOException, CertificateEncodingException + public static Set getEmailAddresses(X509Certificate cert) throws IOException, CertificateEncodingException { - Set addresses = new HashSet(); + HashSet addresses = new HashSet(); - TBSCertificate tbsCertificate = getTBSCert(cert); - - RDN[] rdns = tbsCertificate.getSubject().getRDNs(PKCSObjectIdentifiers.pkcs_9_at_emailAddress); + RDN[] rdns = JcaX500NameUtil.getSubject(cert).getRDNs(PKCSObjectIdentifiers.pkcs_9_at_emailAddress); for (int i = 0; i < rdns.length; i++) { AttributeTypeAndValue[] atVs = rdns[i].getTypesAndValues(); for (int j = 0; j != atVs.length; j++) { - if (atVs[j].getType().equals(PKCSObjectIdentifiers.pkcs_9_at_emailAddress)) + if (PKCSObjectIdentifiers.pkcs_9_at_emailAddress.equals(atVs[j].getType())) { String email = ((ASN1String)atVs[j].getValue()).getString().toLowerCase(); addresses.add(email); @@ -427,19 +383,18 @@ public static Set getEmailAddresses(X509Certificate cert) } } - byte[] ext = cert.getExtensionValue(SUBJECT_ALTERNATIVE_NAME); - if (ext != null) + byte[] sanExtValue = cert.getExtensionValue(Extension.subjectAlternativeName.getId()); + if (sanExtValue != null) { - ASN1Sequence altNames = ASN1Sequence.getInstance(JcaX509ExtensionUtils.parseExtensionValue(ext)); - for (int j = 0; j < altNames.size(); j++) - { - ASN1TaggedObject o = (ASN1TaggedObject)altNames - .getObjectAt(j); + GeneralNames san = GeneralNames.getInstance(JcaX509ExtensionUtils.parseExtensionValue(sanExtValue)); - if (o.getTagNo() == 1) + GeneralName[] names = san.getNames(); + for (int i = 0; i < names.length; ++i) + { + GeneralName name = names[i]; + if (name.getTagNo() == GeneralName.rfc822Name) { - String email = ASN1IA5String.getInstance(o, false) - .getString().toLowerCase(); + String email = ASN1IA5String.getInstance(name.getName()).getString().toLowerCase(); addresses.add(email); } } @@ -448,25 +403,24 @@ public static Set getEmailAddresses(X509Certificate cert) return addresses; } - protected void checkSignerCert(X509Certificate cert, List errors, - List notifications) + protected void checkSignerCert(X509Certificate cert, List errors, List notifications) { // get key length PublicKey key = cert.getPublicKey(); int keyLength = -1; if (key instanceof RSAPublicKey) { - keyLength = ((RSAPublicKey)key).getModulus().bitLength(); + keyLength = ((RSAPublicKey) key).getModulus().bitLength(); } else if (key instanceof DSAPublicKey) { - keyLength = ((DSAPublicKey)key).getParams().getP().bitLength(); + keyLength = ((DSAPublicKey) key).getParams().getP().bitLength(); } if (keyLength != -1 && keyLength <= shortKeyLength) { ErrorBundle msg = createErrorBundle( "SignedMailValidator.shortSigningKey", - new Object[]{Integers.valueOf(keyLength)}); + new Object[]{ Integers.valueOf(keyLength) }); notifications.add(msg); } @@ -476,44 +430,39 @@ else if (key instanceof DSAPublicKey) { ErrorBundle msg = createErrorBundle( "SignedMailValidator.longValidity", - new Object[]{new TrustedInput(cert.getNotBefore()), new TrustedInput(cert.getNotAfter())}); + new Object[]{ new TrustedInput(cert.getNotBefore()), new TrustedInput(cert.getNotAfter()) }); notifications.add(msg); } // check key usage if digitalSignature or nonRepudiation is set boolean[] keyUsage = cert.getKeyUsage(); - if (keyUsage != null && !keyUsage[0] && !keyUsage[1]) + if (!supportsKeyUsage(keyUsage, KU_DIGITAL_SIGNATURE) && + !supportsKeyUsage(keyUsage, KU_NON_REPUDIATION)) { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.signingNotPermitted"); + ErrorBundle msg = createErrorBundle("SignedMailValidator.signingNotPermitted"); errors.add(msg); } // check extended key usage try { - byte[] ext = cert.getExtensionValue(EXT_KEY_USAGE); - if (ext != null) + byte[] ekuExtValue = cert.getExtensionValue(Extension.extendedKeyUsage.getId()); + if (ekuExtValue != null) { - ExtendedKeyUsage extKeyUsage = ExtendedKeyUsage - .getInstance(JcaX509ExtensionUtils.parseExtensionValue(ext)); - if (!extKeyUsage - .hasKeyPurposeId(KeyPurposeId.anyExtendedKeyUsage) - && !extKeyUsage - .hasKeyPurposeId(KeyPurposeId.id_kp_emailProtection)) + ExtendedKeyUsage eku = ExtendedKeyUsage.getInstance( + JcaX509ExtensionUtils.parseExtensionValue(ekuExtValue)); + + if (!eku.hasKeyPurposeId(KeyPurposeId.anyExtendedKeyUsage) && + !eku.hasKeyPurposeId(KeyPurposeId.id_kp_emailProtection)) { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.extKeyUsageNotPermitted"); + ErrorBundle msg = createErrorBundle("SignedMailValidator.extKeyUsageNotPermitted"); errors.add(msg); } } } catch (Exception e) { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.extKeyUsageError", new Object[]{ - e.getMessage(), e, e.getClass().getName()} - ); + ErrorBundle msg = createErrorBundle("SignedMailValidator.extKeyUsageError", e); errors.add(msg); } @@ -524,46 +473,37 @@ else if (key instanceof DSAPublicKey) if (certEmails.isEmpty()) { // error no email address in signing certificate - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.noEmailInCert"); + ErrorBundle msg = createErrorBundle("SignedMailValidator.noEmailInCert"); errors.add(msg); } - else + else if (!hasAnyFromAddress(certEmails, fromAddresses)) { - // check if email in cert is equal to the from address in the - // message - boolean equalsFrom = false; - for (int i = 0; i < fromAddresses.length; i++) - { - if (certEmails.contains(fromAddresses[i].toLowerCase())) - { - equalsFrom = true; - break; - } - } - if (!equalsFrom) - { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.emailFromCertMismatch", - new Object[]{ - new UntrustedInput( - addressesToString(fromAddresses)), - new UntrustedInput(certEmails)} - ); - errors.add(msg); - } + ErrorBundle msg = createErrorBundle( + "SignedMailValidator.emailFromCertMismatch", + new Object[]{ new UntrustedInput(addressesToString(fromAddresses)), new UntrustedInput(certEmails) }); + errors.add(msg); } } catch (Exception e) { - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.certGetEmailError", new Object[]{ - e.getMessage(), e, e.getClass().getName()} - ); + ErrorBundle msg = createErrorBundle("SignedMailValidator.certGetEmailError", e); errors.add(msg); } } + static boolean hasAnyFromAddress(Set certEmails, String[] fromAddresses) + { + // check if email in cert is equal to the from address in the message + for (int i = 0; i < fromAddresses.length; ++i) + { + if (certEmails.contains(fromAddresses[i].toLowerCase())) + { + return true; + } + } + return false; + } + static String addressesToString(Object[] a) { if (a == null) @@ -589,71 +529,26 @@ static String addressesToString(Object[] a) public static Date getSignatureTime(SignerInformation signer) { AttributeTable atab = signer.getSignedAttributes(); - Date result = null; if (atab != null) { Attribute attr = atab.get(CMSAttributes.signingTime); if (attr != null) { - Time t = Time.getInstance(attr.getAttrValues().getObjectAt(0) - .toASN1Primitive()); - result = t.getDate(); - } - } - return result; - } - - private static List findCerts(List certStores, X509CertSelector selector) - throws CertStoreException - { - List result = new ArrayList(); - Iterator it = certStores.iterator(); - while (it.hasNext()) - { - CertStore store = (CertStore)it.next(); - Collection coll = store.getCertificates(selector); - // sometimes the subjectKeyIdentifier in a TA certificate, even when the authorityKeyIdentifier is set. - // where this happens we role back to a simpler match to make sure we've got all the possibilities. - if (coll.isEmpty() && selector.getSubjectKeyIdentifier() != null) - { - X509CertSelector certSelector = (X509CertSelector)selector.clone(); - certSelector.setSubjectKeyIdentifier(null); - coll = store.getCertificates(certSelector); + Time t = Time.getInstance(attr.getAttrValues().getObjectAt(0)); + return t.getDate(); } - result.addAll(coll); } - return result; - } - - private static X509Certificate findNextCert(List certStores, X509CertSelector selector, Set certSet) - throws CertStoreException - { - Iterator certIt = findCerts(certStores, selector).iterator(); - - boolean certFound = false; - X509Certificate nextCert = null; - while (certIt.hasNext()) - { - nextCert = (X509Certificate)certIt.next(); - if (!certSet.contains(nextCert)) - { - certFound = true; - break; - } - } - - return certFound ? nextCert : null; + return null; } /** - * @param signerCert the end of the path + * @param signerCert the end of the path * @param trustanchors trust anchors for the path * @param certStores * @return the resulting certificate path. * @throws GeneralSecurityException */ - public static CertPath createCertPath(X509Certificate signerCert, - Set trustanchors, List certStores) + public static CertPath createCertPath(X509Certificate signerCert, Set trustanchors, List certStores) throws GeneralSecurityException { Object[] results = createCertPath(signerCert, trustanchors, certStores, null); @@ -661,168 +556,111 @@ public static CertPath createCertPath(X509Certificate signerCert, } /** - * Returns an Object array containing a CertPath and a List of Booleans. The list contains the value true - * if the corresponding certificate in the CertPath was taken from the user provided CertStores. + * Returns an Object array containing a CertPath and a List of Booleans. The list contains the value + * true if the corresponding certificate in the CertPath was taken from the user + * provided CertStores. * - * @param signerCert the end of the path - * @param trustanchors trust anchors for the path + * @param signerCert the end of the path + * @param trustAnchors trust anchors for the path * @param systemCertStores list of {@link CertStore} provided by the system - * @param userCertStores list of {@link CertStore} provided by the user + * @param userCertStores list of {@link CertStore} provided by the user * @return a CertPath and a List of booleans. * @throws GeneralSecurityException */ - public static Object[] createCertPath(X509Certificate signerCert, - Set trustanchors, List systemCertStores, List userCertStores) - throws GeneralSecurityException + public static Object[] createCertPath(X509Certificate signerCert, Set trustAnchors, List systemCertStores, + List userCertStores) throws GeneralSecurityException { - Set certSet = new LinkedHashSet(); - List userProvidedList = new ArrayList(); + if (signerCert == null) + { + throw new NullPointerException("'signerCert' cannot be null"); + } - // add signer certificate + LinkedHashSet certSet = new LinkedHashSet(); + ArrayList userProvidedList = new ArrayList(); X509Certificate cert = signerCert; - certSet.add(cert); - userProvidedList.add(new Boolean(true)); + boolean certIsSystemProvided = false; - boolean trustAnchorFound = false; + X509Certificate providedCert = getProvidedCert(trustAnchors, systemCertStores, signerCert); + if (providedCert != null) + { + cert = providedCert; + certIsSystemProvided = true; + } - X509Certificate taCert = null; + TrustAnchor trustAnchor = null; // add other certs to the cert path - while (cert != null && !trustAnchorFound) + do { - // check if cert Issuer is Trustanchor - Iterator trustIt = trustanchors.iterator(); - while (trustIt.hasNext()) - { - TrustAnchor anchor = (TrustAnchor)trustIt.next(); - X509Certificate anchorCert = anchor.getTrustedCert(); - if (anchorCert != null) - { - if (anchorCert.getSubjectX500Principal().equals( - cert.getIssuerX500Principal())) - { - try - { - cert.verify(anchorCert.getPublicKey(), "BC"); - trustAnchorFound = true; - taCert = anchorCert; - break; - } - catch (Exception e) - { - // trustanchor not found - } - } - } - else - { - if (anchor.getCAName().equals( - cert.getIssuerX500Principal().getName())) - { - try - { - cert.verify(anchor.getCAPublicKey(), "BC"); - trustAnchorFound = true; - break; - } - catch (Exception e) - { - // trustanchor not found - } - } - } - } + certSet.add(cert); + userProvidedList.add(Boolean.valueOf(!certIsSystemProvided)); - if (!trustAnchorFound) + // check if cert issuer is Trustanchor + trustAnchor = findTrustAnchorForCert(cert, trustAnchors); + if (trustAnchor != null) { - // add next cert to path - X509CertSelector select = new X509CertSelector(); - try - { - select.setSubject(cert.getIssuerX500Principal().getEncoded()); - } - catch (IOException e) - { - throw new IllegalStateException(e.toString()); - } - - byte[] akiExtValue = cert.getExtensionValue(Extension.authorityKeyIdentifier.getId()); - if (akiExtValue != null) - { - try - { - AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance( - JcaX509ExtensionUtils.parseExtensionValue(akiExtValue)); + break; + } - ASN1OctetString keyIdentifier = aki.getKeyIdentifierObject(); - if (keyIdentifier != null) - { - select.setSubjectKeyIdentifier(keyIdentifier.getEncoded(ASN1Encoding.DER)); - } - } - catch (IOException ioe) - { - // ignore - } - } + // add next cert to path - boolean userProvided = false; + X509CertSelector issuerSelector = createIssuerSelector(cert); - cert = findNextCert(systemCertStores, select, certSet); - if (cert == null && userCertStores != null) - { - userProvided = true; - cert = findNextCert(userCertStores, select, certSet); - } + cert = findFirstCert(systemCertStores, issuerSelector, certSet); + certIsSystemProvided = (cert != null); - if (cert != null) - { - // cert found - certSet.add(cert); - userProvidedList.add(new Boolean(userProvided)); - } + if (cert == null && userCertStores != null) + { + cert = findFirstCert(userCertStores, issuerSelector, certSet); } } + while (cert != null); - // if a trustanchor was found - try to find a selfsigned certificate of - // the trustanchor - if (trustAnchorFound) + // if a trust anchor was found - try to find a self-signed certificate of the trust anchor + if (trustAnchor != null) { - if (taCert != null && taCert.getSubjectX500Principal().equals(taCert.getIssuerX500Principal())) + X509Certificate trustedCert = trustAnchor.getTrustedCert(); // Can be null + + if (trustedCert != null && + trustedCert.getSubjectX500Principal().equals(trustedCert.getIssuerX500Principal())) { - certSet.add(taCert); - userProvidedList.add(new Boolean(false)); + if (certSet.add(trustedCert)) + { + userProvidedList.add(Boolean.FALSE); + } } else { - X509CertSelector select = new X509CertSelector(); + X509CertSelector taSelector = new X509CertSelector(); + byte[] certIssuerEncoding = cert.getIssuerX500Principal().getEncoded(); try { - select.setSubject(cert.getIssuerX500Principal().getEncoded()); - select.setIssuer(cert.getIssuerX500Principal().getEncoded()); + taSelector.setSubject(certIssuerEncoding); + taSelector.setIssuer(certIssuerEncoding); } catch (IOException e) { throw new IllegalStateException(e.toString()); } - boolean userProvided = false; + cert = findFirstCert(systemCertStores, taSelector, certSet); + certIsSystemProvided = (cert != null); - taCert = findNextCert(systemCertStores, select, certSet); - if (taCert == null && userCertStores != null) + if (cert == null && userCertStores != null) { - userProvided = true; - taCert = findNextCert(userCertStores, select, certSet); + cert = findFirstCert(userCertStores, taSelector, certSet); } - if (taCert != null) + + if (cert != null) { try { - cert.verify(taCert.getPublicKey(), "BC"); - certSet.add(taCert); - userProvidedList.add(new Boolean(userProvided)); + cert.verify(cert.getPublicKey(), "BC"); + + certSet.add(cert); + userProvidedList.add(Boolean.valueOf(!certIsSystemProvided)); } catch (GeneralSecurityException gse) { @@ -833,7 +671,7 @@ public static Object[] createCertPath(X509Certificate signerCert, } CertPath certPath = CertificateFactory.getInstance("X.509", "BC").generateCertPath(new ArrayList(certSet)); - return new Object[]{certPath, userProvidedList}; + return new Object[]{ certPath, userProvidedList }; } public CertStore getCertsAndCRLs() @@ -846,38 +684,29 @@ public SignerInformationStore getSignerInformationStore() return signers; } - public ValidationResult getValidationResult(SignerInformation signer) - throws SignedMailValidatorException + public ValidationResult getValidationResult(SignerInformation signer) throws SignedMailValidatorException { if (signers.getSigners(signer.getSID()).isEmpty()) { // the signer is not part of the SignerInformationStore // he has not signed the message - ErrorBundle msg = createErrorBundle( - "SignedMailValidator.wrongSigner"); + ErrorBundle msg = createErrorBundle("SignedMailValidator.wrongSigner"); throw new SignedMailValidatorException(msg); } - else - { - return (ValidationResult)results.get(signer); - } + + return (ValidationResult)results.get(signer); } public static class ValidationResult { - private PKIXCertPathReviewer review; - private List errors; - private List notifications; - private List userProvidedCerts; - private boolean signVerified; - ValidationResult(PKIXCertPathReviewer review, boolean verified, - List errors, List notifications, List userProvidedCerts) + ValidationResult(PKIXCertPathReviewer review, boolean verified, List errors, List notifications, + List userProvidedCerts) { this.review = review; this.errors = errors; @@ -907,8 +736,8 @@ public List getNotifications() } /** - * @return the PKIXCertPathReviewer for the CertPath of this signature - * or null if an Exception occurred. + * @return the PKIXCertPathReviewer for the CertPath of this signature or null if an Exception + * occurred. */ public PKIXCertPathReviewer getCertPathReview() { @@ -916,8 +745,7 @@ public PKIXCertPathReviewer getCertPathReview() } /** - * @return the CertPath for this signature - * or null if an Exception occurred. + * @return the CertPath for this signature or null if an Exception occurred. */ public CertPath getCertPath() { @@ -925,8 +753,8 @@ public CertPath getCertPath() } /** - * @return a List of Booleans that are true if the corresponding certificate in the CertPath was taken from - * the CertStore of the SMIME message + * @return a List of Booleans that are true if the corresponding certificate in the CertPath was + * taken from the CertStore of the SMIME message */ public List getUserProvidedCerts() { @@ -934,8 +762,7 @@ public List getUserProvidedCerts() } /** - * @return true if the signature corresponds to the public key of the - * signer + * @return true if the signature corresponds to the public key of the signer */ public boolean isVerifiedSignature() { @@ -943,42 +770,195 @@ public boolean isVerifiedSignature() } /** - * @return true if the signature is valid (ie. if it corresponds to the - * public key of the signer and the cert path for the signers - * certificate is also valid) + * @return true if the signature is valid (ie. if it corresponds to the public key of the signer and + * the cert path for the signers certificate is also valid) */ public boolean isValidSignature() { - if (review != null) - { - return signVerified && review.isValidCertPath() && errors.isEmpty(); - } - else - { - return false; - } + return review != null && signVerified && review.isValidCertPath() && errors.isEmpty(); } } - private static TBSCertificate getTBSCert(X509Certificate cert) - throws CertificateEncodingException - { - return TBSCertificate.getInstance(cert.getTBSCertificate()); - } - private static ErrorBundle createErrorBundle(String id) { ErrorBundle msg = new ErrorBundle(RESOURCE_NAME, id); msg.setClassLoader(SignedMailValidator.class.getClassLoader()); - + return msg; } - + private static ErrorBundle createErrorBundle(String id, Object[] arguments) { ErrorBundle msg = new ErrorBundle(RESOURCE_NAME, id, arguments); msg.setClassLoader(SignedMailValidator.class.getClassLoader()); - + return msg; } + + private static ErrorBundle createErrorBundle(String id, Exception e) + { + return createErrorBundle(id, new Object[]{ e.getMessage(), e, e.getClass().getName() }); + } + + private static X509CertSelector createIssuerSelector(X509Certificate cert) + { + // add next cert to path + X509CertSelector selector = new X509CertSelector(); + try + { + selector.setSubject(cert.getIssuerX500Principal().getEncoded()); + } + catch (IOException e) + { + throw new IllegalStateException(e.toString()); + } + + byte[] akiExtValue = cert.getExtensionValue(Extension.authorityKeyIdentifier.getId()); + if (akiExtValue != null) + { + try + { + AuthorityKeyIdentifier aki = AuthorityKeyIdentifier.getInstance( + JcaX509ExtensionUtils.parseExtensionValue(akiExtValue)); + + ASN1OctetString keyIdentifier = aki.getKeyIdentifierObject(); + if (keyIdentifier != null) + { + selector.setSubjectKeyIdentifier(keyIdentifier.getEncoded(ASN1Encoding.DER)); + } + } + catch (IOException ioe) + { + // ignore + } + } + + return selector; + } + + private static X509Certificate findFirstCert(List certStores, X509CertSelector selector, Set ignoreCerts) + throws CertStoreException + { + X509CertSelector altSelector = null; + + Iterator certStoreIter = certStores.iterator(); + while (certStoreIter.hasNext()) + { + CertStore certStore = (CertStore)certStoreIter.next(); + Collection certs = certStore.getCertificates(selector); + + // sometimes the subjectKeyIdentifier in a TA certificate, even when the authorityKeyIdentifier is set. + // where this happens we roll back to a simpler match to make sure we've got all the possibilities. + if (certs.isEmpty() && selector.getSubjectKeyIdentifier() != null) + { + if (altSelector == null) + { + altSelector = (X509CertSelector)selector.clone(); + altSelector.setSubjectKeyIdentifier(null); + } + + certs = certStore.getCertificates(altSelector); + } + + Iterator certIter = certs.iterator(); + while (certIter.hasNext()) + { + X509Certificate nextCert = (X509Certificate)certIter.next(); + if (ignoreCerts == null || !ignoreCerts.contains(nextCert)) + { + return nextCert; + } + } + } + return null; + } + + private static TrustAnchor findTrustAnchorForCert(X509Certificate cert, Set trustAnchors) + { + Iterator trustAnchorIter = trustAnchors.iterator(); + if (trustAnchorIter.hasNext()) + { + X500Principal certIssuer = cert.getIssuerX500Principal(); + + do + { + TrustAnchor trustAnchor = (TrustAnchor)trustAnchorIter.next(); + + try + { + X509Certificate taCert = trustAnchor.getTrustedCert(); + if (taCert != null) + { + if (certIssuer.equals(taCert.getSubjectX500Principal())) + { + cert.verify(taCert.getPublicKey(), "BC"); + return trustAnchor; + } + } + else + { + if (certIssuer.getName().equals(trustAnchor.getCAName())) + { + cert.verify(trustAnchor.getCAPublicKey(), "BC"); + return trustAnchor; + } + } + } + catch (Exception e) + { + } + } + while (trustAnchorIter.hasNext()); + } + return null; + } + + private static X509Certificate getProvidedCert(Set trustAnchors, List certStores, X509Certificate cert) + throws CertStoreException + { + Iterator trustAnchorIter = trustAnchors.iterator(); + while (trustAnchorIter.hasNext()) + { + TrustAnchor trustAnchor = (TrustAnchor)trustAnchorIter.next(); + X509Certificate taCert = trustAnchor.getTrustedCert(); + if (taCert != null && taCert.equals(cert)) + { + return taCert; + } + } + + X509CertSelector selector = new X509CertSelector(); + selector.setCertificate(cert); + + return findFirstCert(certStores, selector, null); + } + + private static boolean isValidSignature(X509Certificate cert, SignerInformation signer, List errors) + { + boolean validSignature = false; + try + { + SignerInformationVerifier verifier = new JcaSimpleSignerInfoVerifierBuilder() + .setProvider("BC") + .build(cert.getPublicKey()); + + validSignature = signer.verify(verifier); + if (!validSignature) + { + ErrorBundle msg = createErrorBundle("SignedMailValidator.signatureNotVerified"); + errors.add(msg); + } + } + catch (Exception e) + { + ErrorBundle msg = createErrorBundle("SignedMailValidator.exceptionVerifyingSignature", e); + errors.add(msg); + } + return validSignature; + } + + private static boolean supportsKeyUsage(boolean[] ku, int kuBit) + { + return null == ku || (ku.length > kuBit && ku[kuBit]); + } } diff --git a/mail/src/test/java/org/bouncycastle/mail/smime/test/AllTests.java b/mail/src/test/java/org/bouncycastle/mail/smime/test/AllTests.java index 662fc75958..a71f67b6bf 100644 --- a/mail/src/test/java/org/bouncycastle/mail/smime/test/AllTests.java +++ b/mail/src/test/java/org/bouncycastle/mail/smime/test/AllTests.java @@ -47,14 +47,15 @@ public static Test suite() { TestSuite suite = new TestSuite("SMIME tests"); - suite.addTestSuite(NewSMIMESignedTest.class); - suite.addTestSuite(SignedMailValidatorTest.class); + suite.addTestSuite(JournalingSecureRandomEncryptTest.class); + suite.addTestSuite(MailGeneralTest.class); suite.addTestSuite(NewSMIMEAuthEnvelopedTest.class); suite.addTestSuite(NewSMIMEEnvelopedTest.class); + suite.addTestSuite(NewSMIMESignedTest.class); + suite.addTestSuite(SignedMailValidatorTest.class); suite.addTestSuite(SMIMECompressedTest.class); suite.addTestSuite(SMIMEMiscTest.class); suite.addTestSuite(SMIMEToolkitTest.class); - suite.addTestSuite(MailGeneralTest.class); return new BCTestSetup(suite); } diff --git a/mail/src/test/java/org/bouncycastle/mail/smime/test/MailGeneralTest.java b/mail/src/test/java/org/bouncycastle/mail/smime/test/MailGeneralTest.java index 82f853576a..92da52e44d 100644 --- a/mail/src/test/java/org/bouncycastle/mail/smime/test/MailGeneralTest.java +++ b/mail/src/test/java/org/bouncycastle/mail/smime/test/MailGeneralTest.java @@ -1045,9 +1045,16 @@ public void testSelfSignedCert() certStores.add(store); // first path - CertPath path = SignedMailValidator.createCertPath(rootCert, trustanchors, certStores); + CertPath path1 = SignedMailValidator.createCertPath(rootCert, trustanchors, certStores); + assertTrue("path size is not 1", path1.getCertificates().size() == 1); - assertTrue("path size is not 1", path.getCertificates().size() == 1); + Object[] pathAndUserProvided = SignedMailValidator.createCertPath(rootCert, trustanchors, certStores, null); + assertTrue("result length is not 2", pathAndUserProvided.length == 2); + CertPath path2 = (CertPath)pathAndUserProvided[0]; + List userProvided = (List)pathAndUserProvided[1]; + assertTrue("path size is not 1", path2.getCertificates().size() == 1); + assertTrue("user-provided size is not 1", userProvided.size() == 1); + assertTrue("user-provided value should be false", Boolean.FALSE.equals(userProvided.get(0))); // check message validation certList = new ArrayList(); @@ -1118,12 +1125,13 @@ public void operation() SignerInformation signer = (SignerInformation)validator .getSignerInformationStore().getSigners().iterator().next(); - assertEquals(1, validator.getCertsAndCRLs().getCertificates(null).size()); - assertEquals(0, validator.getCertsAndCRLs().getCRLs(null).size()); + CertStore certsAndCRLS = validator.getCertsAndCRLs(); + assertEquals(1, certsAndCRLS.getCertificates(null).size()); + assertEquals(0, certsAndCRLS.getCRLs(null).size()); SignedMailValidator.ValidationResult res = validator.getValidationResult(signer); assertEquals(1, res.getCertPath().getCertificates().size()); - assertEquals(2, res.getUserProvidedCerts().size()); + assertEquals(1, res.getUserProvidedCerts().size()); assertTrue(res.isVerifiedSignature()); assertTrue(res.isValidSignature()); diff --git a/mail/src/test/java/org/bouncycastle/mail/smime/test/SignedMailValidatorTest.java b/mail/src/test/java/org/bouncycastle/mail/smime/test/SignedMailValidatorTest.java index 0e05c976f6..7952f5fdf9 100644 --- a/mail/src/test/java/org/bouncycastle/mail/smime/test/SignedMailValidatorTest.java +++ b/mail/src/test/java/org/bouncycastle/mail/smime/test/SignedMailValidatorTest.java @@ -279,9 +279,16 @@ public void testSelfSignedCert() certStores.add(store); // first path - CertPath path = SignedMailValidator.createCertPath(rootCert, trustanchors, certStores); - - assertTrue("path size is not 1", path.getCertificates().size() == 1); + CertPath path1 = SignedMailValidator.createCertPath(rootCert, trustanchors, certStores); + assertTrue("path size is not 1", path1.getCertificates().size() == 1); + + Object[] pathAndUserProvided = SignedMailValidator.createCertPath(rootCert, trustanchors, certStores, null); + assertTrue("result length is not 2", pathAndUserProvided.length == 2); + CertPath path2 = (CertPath)pathAndUserProvided[0]; + List userProvided = (List)pathAndUserProvided[1]; + assertTrue("path size is not 1", path2.getCertificates().size() == 1); + assertTrue("user-provided size is not 1", userProvided.size() == 1); + assertTrue("user-provided value should be false", Boolean.FALSE.equals(userProvided.get(0))); // check message validation certList = new ArrayList(); From 3db23bf95282975ca73507aa842eb6fd2aa9c617 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 4 Jul 2025 21:53:08 +0700 Subject: [PATCH 444/890] TLS: Throw fatal decrypt_error for invalid 1.3 PSK binder - test cases for PSK key mismatch (incl. pre-1.3 cases) --- .../java/org/bouncycastle/tls/TlsUtils.java | 23 +++--- .../tls/test/DTLSPSKProtocolTest.java | 75 ++++++++++++++++--- .../tls/test/MockPSKDTLSClient.java | 17 ++++- .../tls/test/MockPSKDTLSServer.java | 26 ++++++- .../tls/test/MockPSKTls13Client.java | 11 ++- .../tls/test/MockPSKTls13Server.java | 11 ++- .../tls/test/MockPSKTlsClient.java | 7 +- .../tls/test/MockPSKTlsServer.java | 16 +++- .../tls/test/PSKTlsClientTest.java | 9 +-- .../tls/test/Tls13PSKProtocolTest.java | 70 +++++++++++++++-- .../tls/test/TlsPSKProtocolTest.java | 70 +++++++++++++++-- .../bouncycastle/tls/test/TlsTestUtils.java | 18 +++++ 12 files changed, 311 insertions(+), 42 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java b/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java index fd9cb31b56..50125282c8 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsUtils.java @@ -6062,8 +6062,6 @@ static OfferedPsks.SelectedConfig selectPreSharedKey(TlsServerContext serverCont Hashtable clientHelloExtensions, HandshakeMessageInput clientHelloMessage, TlsHandshakeHash handshakeHash, boolean afterHelloRetryRequest) throws IOException { - boolean handshakeHashUpdated = false; - OfferedPsks offeredPsks = TlsExtensionsUtils.getPreSharedKeyClientHello(clientHelloExtensions); if (null != offeredPsks) { @@ -6089,6 +6087,14 @@ static OfferedPsks.SelectedConfig selectPreSharedKey(TlsServerContext serverCont int index = offeredPsks.getIndexOfIdentity(new PskIdentity(psk.getIdentity(), 0L)); if (index >= 0) { + /* + * RFC 8446 4.2.11. Prior to accepting PSK key establishment, the server MUST validate the + * corresponding binder value [..]. If this value is not present or does not validate, the + * server MUST abort the handshake. Servers SHOULD NOT attempt to validate multiple + * binders; rather, they SHOULD select a single PSK and validate solely the binder that + * corresponds to that PSK. + */ + byte[] binder = (byte[])offeredPsks.getBinders().elementAt(index); TlsCrypto crypto = serverContext.getCrypto(); @@ -6100,7 +6106,6 @@ static OfferedPsks.SelectedConfig selectPreSharedKey(TlsServerContext serverCont byte[] transcriptHash; { - handshakeHashUpdated = true; int bindersSize = offeredPsks.getBindersSize(); clientHelloMessage.updateHashPrefix(handshakeHash, bindersSize); @@ -6121,20 +6126,18 @@ static OfferedPsks.SelectedConfig selectPreSharedKey(TlsServerContext serverCont byte[] calculatedBinder = calculatePSKBinder(crypto, isExternalPSK, pskCryptoHashAlgorithm, earlySecret, transcriptHash); - if (Arrays.constantTimeAreEqual(calculatedBinder, binder)) + if (!Arrays.constantTimeAreEqual(calculatedBinder, binder)) { - return new OfferedPsks.SelectedConfig(index, psk, pskKeyExchangeModes, earlySecret); + throw new TlsFatalAlert(AlertDescription.decrypt_error, "Invalid PSK binder"); } + + return new OfferedPsks.SelectedConfig(index, psk, pskKeyExchangeModes, earlySecret); } } } } - if (!handshakeHashUpdated) - { - clientHelloMessage.updateHash(handshakeHash); - } - + clientHelloMessage.updateHash(handshakeHash); return null; } diff --git a/tls/src/test/java/org/bouncycastle/tls/test/DTLSPSKProtocolTest.java b/tls/src/test/java/org/bouncycastle/tls/test/DTLSPSKProtocolTest.java index 1ee37bbe1f..a8f27d9764 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/DTLSPSKProtocolTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/DTLSPSKProtocolTest.java @@ -1,11 +1,11 @@ package org.bouncycastle.tls.test; -import java.security.SecureRandom; - import org.bouncycastle.tls.DTLSClientProtocol; import org.bouncycastle.tls.DTLSServerProtocol; import org.bouncycastle.tls.DTLSTransport; import org.bouncycastle.tls.DatagramTransport; +import org.bouncycastle.tls.TlsServer; +import org.bouncycastle.tls.TlsTimeoutException; import org.bouncycastle.util.Arrays; import junit.framework.TestCase; @@ -13,26 +13,41 @@ public class DTLSPSKProtocolTest extends TestCase { + public void testBadClientKeyTimeout() throws Exception + { + MockPSKDTLSClient client = new MockPSKDTLSClient(null, true); + MockPSKDTLSServer server = new MockPSKDTLSServer(); + + implTestKeyMismatch(client, server); + } + + public void testBadServerKeyTimeout() throws Exception + { + MockPSKDTLSClient client = new MockPSKDTLSClient(null); + MockPSKDTLSServer server = new MockPSKDTLSServer(true); + + implTestKeyMismatch(client, server); + } + public void testClientServer() throws Exception { - SecureRandom secureRandom = new SecureRandom(); + MockPSKDTLSClient client = new MockPSKDTLSClient(null); + MockPSKDTLSServer server = new MockPSKDTLSServer(); DTLSClientProtocol clientProtocol = new DTLSClientProtocol(); DTLSServerProtocol serverProtocol = new DTLSServerProtocol(); MockDatagramAssociation network = new MockDatagramAssociation(1500); - ServerThread serverThread = new ServerThread(serverProtocol, network.getServer()); + ServerThread serverThread = new ServerThread(serverProtocol, server, network.getServer()); serverThread.start(); DatagramTransport clientTransport = network.getClient(); - clientTransport = new UnreliableDatagramTransport(clientTransport, secureRandom, 0, 0); + clientTransport = new UnreliableDatagramTransport(clientTransport, client.getCrypto().getSecureRandom(), 0, 0); clientTransport = new LoggingDatagramTransport(clientTransport, System.out); - MockPSKDTLSClient client = new MockPSKDTLSClient(null); - DTLSTransport dtlsClient = clientProtocol.connect(client, clientTransport); for (int i = 1; i <= 10; ++i) @@ -52,16 +67,59 @@ public void testClientServer() throws Exception serverThread.shutdown(); } + private void implTestKeyMismatch(MockPSKDTLSClient client, MockPSKDTLSServer server) throws Exception + { + DTLSClientProtocol clientProtocol = new DTLSClientProtocol(); + DTLSServerProtocol serverProtocol = new DTLSServerProtocol(); + + MockDatagramAssociation network = new MockDatagramAssociation(1500); + + ServerThread serverThread = new ServerThread(serverProtocol, server, network.getServer()); + serverThread.start(); + + DatagramTransport clientTransport = network.getClient(); + + // Don't use unreliable transport because we are focused on timeout due to bad PSK +// clientTransport = new UnreliableDatagramTransport(clientTransport, client.getCrypto().getSecureRandom(), 0, 0); + + clientTransport = new LoggingDatagramTransport(clientTransport, System.out); + + boolean correctException = false; + + try + { + DTLSTransport dtlsClient = clientProtocol.connect(client, clientTransport); + dtlsClient.close(); + } + catch (TlsTimeoutException e) + { + correctException = true; + } + catch (Exception e) + { + } + finally + { + clientTransport.close(); + } + + serverThread.shutdown(); + + assertTrue(correctException); + } + static class ServerThread extends Thread { private final DTLSServerProtocol serverProtocol; + private final TlsServer server; private final DatagramTransport serverTransport; private volatile boolean isShutdown = false; - ServerThread(DTLSServerProtocol serverProtocol, DatagramTransport serverTransport) + ServerThread(DTLSServerProtocol serverProtocol, TlsServer server, DatagramTransport serverTransport) { this.serverProtocol = serverProtocol; + this.server = server; this.serverTransport = serverTransport; } @@ -69,7 +127,6 @@ public void run() { try { - MockPSKDTLSServer server = new MockPSKDTLSServer(); DTLSTransport dtlsServer = serverProtocol.accept(server, serverTransport); byte[] buf = new byte[dtlsServer.getReceiveLimit()]; while (!isShutdown) diff --git a/tls/src/test/java/org/bouncycastle/tls/test/MockPSKDTLSClient.java b/tls/src/test/java/org/bouncycastle/tls/test/MockPSKDTLSClient.java index c4efb99fa4..ff955cc5af 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/MockPSKDTLSClient.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/MockPSKDTLSClient.java @@ -32,7 +32,12 @@ class MockPSKDTLSClient MockPSKDTLSClient(TlsSession session) { - this(session, new BasicTlsPSKIdentity("client", Strings.toUTF8ByteArray("TLS_TEST_PSK"))); + this(session, false); + } + + MockPSKDTLSClient(TlsSession session, boolean badKey) + { + this(session, TlsTestUtils.createDefaultPSKIdentity(badKey)); } MockPSKDTLSClient(TlsSession session, TlsPSKIdentity pskIdentity) @@ -42,6 +47,16 @@ class MockPSKDTLSClient this.session = session; } + public int getHandshakeTimeoutMillis() + { + return 1000; + } + + public int getHandshakeResendTimeMillis() + { + return 100; // Fast resend only for tests! + } + public TlsSession getSessionToResume() { return this.session; diff --git a/tls/src/test/java/org/bouncycastle/tls/test/MockPSKDTLSServer.java b/tls/src/test/java/org/bouncycastle/tls/test/MockPSKDTLSServer.java index 453887e30e..32c2e7b3ff 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/MockPSKDTLSServer.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/MockPSKDTLSServer.java @@ -22,7 +22,22 @@ class MockPSKDTLSServer { MockPSKDTLSServer() { - super(new BcTlsCrypto(), new MyIdentityManager()); + this(false); + } + + MockPSKDTLSServer(boolean badKey) + { + super(new BcTlsCrypto(), new MyIdentityManager(badKey)); + } + + public int getHandshakeTimeoutMillis() + { + return 1000; + } + + public int getHandshakeResendTimeMillis() + { + return 100; // Fast resend only for tests! } public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Throwable cause) @@ -129,6 +144,13 @@ protected ProtocolVersion[] getSupportedVersions() static class MyIdentityManager implements TlsPSKIdentityManager { + private final boolean badKey; + + MyIdentityManager(boolean badKey) + { + this.badKey = badKey; + } + public byte[] getHint() { return Strings.toUTF8ByteArray("hint"); @@ -141,7 +163,7 @@ public byte[] getPSK(byte[] identity) String name = Strings.fromUTF8ByteArray(identity); if (name.equals("client")) { - return Strings.toUTF8ByteArray("TLS_TEST_PSK"); + return TlsTestUtils.getPSKPasswordUTF8(badKey); } } return null; diff --git a/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTls13Client.java b/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTls13Client.java index 6efd1815ac..9fc8717cfc 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTls13Client.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTls13Client.java @@ -24,9 +24,18 @@ class MockPSKTls13Client extends AbstractTlsClient { + private final boolean badKey; + MockPSKTls13Client() + { + this(false); + } + + MockPSKTls13Client(boolean badKey) { super(new BcTlsCrypto()); + + this.badKey = badKey; } // public Vector getEarlyKeyShareGroups() @@ -60,7 +69,7 @@ protected ProtocolVersion[] getSupportedVersions() public Vector getExternalPSKs() { byte[] identity = Strings.toUTF8ByteArray("client"); - TlsSecret key = getCrypto().createSecret(Strings.toUTF8ByteArray("TLS_TEST_PSK")); + TlsSecret key = getCrypto().createSecret(TlsTestUtils.getPSKPasswordUTF8(badKey)); int prfAlgorithm = PRFAlgorithm.tls13_hkdf_sha256; return TlsUtils.vectorOfOne(new BasicTlsPSKExternal(identity, key, prfAlgorithm)); diff --git a/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTls13Server.java b/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTls13Server.java index b177af6e77..f474e1c299 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTls13Server.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTls13Server.java @@ -25,9 +25,18 @@ class MockPSKTls13Server extends AbstractTlsServer { + private final boolean badKey; + MockPSKTls13Server() + { + this(false); + } + + MockPSKTls13Server(boolean badKey) { super(new BcTlsCrypto()); + + this.badKey = badKey; } public TlsCredentials getCredentials() throws IOException @@ -75,7 +84,7 @@ public TlsPSKExternal getExternalPSK(Vector identities) { if (matchIdentity.equals(identities.elementAt(i))) { - TlsSecret key = getCrypto().createSecret(Strings.toUTF8ByteArray("TLS_TEST_PSK")); + TlsSecret key = getCrypto().createSecret(TlsTestUtils.getPSKPasswordUTF8(badKey)); int prfAlgorithm = PRFAlgorithm.tls13_hkdf_sha256; return new BasicTlsPSKExternal(identity, key, prfAlgorithm); diff --git a/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTlsClient.java b/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTlsClient.java index 99261f1d32..dd503b8dd7 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTlsClient.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTlsClient.java @@ -35,7 +35,12 @@ class MockPSKTlsClient MockPSKTlsClient(TlsSession session) { - this(session, new BasicTlsPSKIdentity("client", Strings.toUTF8ByteArray("TLS_TEST_PSK"))); + this(session, false); + } + + MockPSKTlsClient(TlsSession session, boolean badKey) + { + this(session, TlsTestUtils.createDefaultPSKIdentity(badKey)); } MockPSKTlsClient(TlsSession session, TlsPSKIdentity pskIdentity) diff --git a/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTlsServer.java b/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTlsServer.java index 69bdfd20f3..7a10498583 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTlsServer.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/MockPSKTlsServer.java @@ -23,7 +23,12 @@ class MockPSKTlsServer { MockPSKTlsServer() { - super(new BcTlsCrypto(), new MyIdentityManager()); + this(false); + } + + MockPSKTlsServer(boolean badKey) + { + super(new BcTlsCrypto(), new MyIdentityManager(badKey)); } protected Vector getProtocolNames() @@ -138,6 +143,13 @@ protected ProtocolVersion[] getSupportedVersions() static class MyIdentityManager implements TlsPSKIdentityManager { + private final boolean badKey; + + MyIdentityManager(boolean badKey) + { + this.badKey = badKey; + } + public byte[] getHint() { return Strings.toUTF8ByteArray("hint"); @@ -150,7 +162,7 @@ public byte[] getPSK(byte[] identity) String name = Strings.fromUTF8ByteArray(identity); if (name.equals("client")) { - return Strings.toUTF8ByteArray("TLS_TEST_PSK"); + return TlsTestUtils.getPSKPasswordUTF8(badKey); } } return null; diff --git a/tls/src/test/java/org/bouncycastle/tls/test/PSKTlsClientTest.java b/tls/src/test/java/org/bouncycastle/tls/test/PSKTlsClientTest.java index c8eb43ffab..b93a428757 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/PSKTlsClientTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/PSKTlsClientTest.java @@ -11,6 +11,7 @@ import org.bouncycastle.tls.BasicTlsPSKIdentity; import org.bouncycastle.tls.TlsClient; import org.bouncycastle.tls.TlsClientProtocol; +import org.bouncycastle.tls.TlsPSKIdentity; import org.bouncycastle.util.Strings; /** @@ -38,12 +39,10 @@ public static void main(String[] args) throws Exception */ // String psk_identity = "Client_identity"; // byte[] psk = new byte[]{ 0x61, 0x61, 0x61, 0x61, 0x61 }; +// TlsPSKIdentity pskIdentity = new BasicTlsPSKIdentity(psk_identity, psk); - // These correspond to the configuration of MockPSKTlsServer - String psk_identity = "client"; - byte[] psk = Strings.toUTF8ByteArray("TLS_TEST_PSK"); - - BasicTlsPSKIdentity pskIdentity = new BasicTlsPSKIdentity(psk_identity, psk); + // This corresponds to the configuration of MockPskTlsServer + TlsPSKIdentity pskIdentity = TlsTestUtils.createDefaultPSKIdentity(false); MockPSKTlsClient client = new MockPSKTlsClient(null, pskIdentity); TlsClientProtocol protocol = openTlsConnection(address, port, client); diff --git a/tls/src/test/java/org/bouncycastle/tls/test/Tls13PSKProtocolTest.java b/tls/src/test/java/org/bouncycastle/tls/test/Tls13PSKProtocolTest.java index 924e7f12d3..6919861963 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/Tls13PSKProtocolTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/Tls13PSKProtocolTest.java @@ -4,7 +4,10 @@ import java.io.PipedInputStream; import java.io.PipedOutputStream; +import org.bouncycastle.tls.AlertDescription; import org.bouncycastle.tls.TlsClientProtocol; +import org.bouncycastle.tls.TlsFatalAlertReceived; +import org.bouncycastle.tls.TlsServer; import org.bouncycastle.tls.TlsServerProtocol; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.io.Streams; @@ -14,8 +17,27 @@ public class Tls13PSKProtocolTest extends TestCase { + public void testBadClientKey() throws Exception + { + MockPSKTls13Client client = new MockPSKTls13Client(true); + MockPSKTls13Server server = new MockPSKTls13Server(); + + implTestKeyMismatch(client, server); + } + + public void testBadServerKey() throws Exception + { + MockPSKTls13Client client = new MockPSKTls13Client(); + MockPSKTls13Server server = new MockPSKTls13Server(true); + + implTestKeyMismatch(client, server); + } + public void testClientServer() throws Exception { + MockPSKTls13Client client = new MockPSKTls13Client(); + MockPSKTls13Server server = new MockPSKTls13Server(); + PipedInputStream clientRead = TlsTestUtils.createPipedInputStream(); PipedInputStream serverRead = TlsTestUtils.createPipedInputStream(); PipedOutputStream clientWrite = new PipedOutputStream(serverRead); @@ -24,10 +46,9 @@ public void testClientServer() throws Exception TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); - ServerThread serverThread = new ServerThread(serverProtocol); + ServerThread serverThread = new ServerThread(serverProtocol, server); serverThread.start(); - MockPSKTls13Client client = new MockPSKTls13Client(); clientProtocol.connect(client); // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity @@ -50,28 +71,67 @@ public void testClientServer() throws Exception serverThread.join(); } + private void implTestKeyMismatch(MockPSKTls13Client client, MockPSKTls13Server server) throws Exception + { + PipedInputStream clientRead = TlsTestUtils.createPipedInputStream(); + PipedInputStream serverRead = TlsTestUtils.createPipedInputStream(); + PipedOutputStream clientWrite = new PipedOutputStream(serverRead); + PipedOutputStream serverWrite = new PipedOutputStream(clientRead); + + TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); + TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); + + ServerThread serverThread = new ServerThread(serverProtocol, server); + serverThread.start(); + + boolean correctException = false; + short alertDescription = -1; + + try + { + clientProtocol.connect(client); + } + catch (TlsFatalAlertReceived e) + { + correctException = true; + alertDescription = e.getAlertDescription(); + } + catch (Exception e) + { + } + finally + { + clientProtocol.close(); + } + + serverThread.join(); + + assertTrue(correctException); + assertEquals(AlertDescription.decrypt_error, alertDescription); + } + static class ServerThread extends Thread { private final TlsServerProtocol serverProtocol; + private final TlsServer server; - ServerThread(TlsServerProtocol serverProtocol) + ServerThread(TlsServerProtocol serverProtocol, TlsServer server) { this.serverProtocol = serverProtocol; + this.server = server; } public void run() { try { - MockPSKTls13Server server = new MockPSKTls13Server(); serverProtocol.accept(server); Streams.pipeAll(serverProtocol.getInputStream(), serverProtocol.getOutputStream()); serverProtocol.close(); } catch (Exception e) { -// throw new RuntimeException(e); } } } diff --git a/tls/src/test/java/org/bouncycastle/tls/test/TlsPSKProtocolTest.java b/tls/src/test/java/org/bouncycastle/tls/test/TlsPSKProtocolTest.java index 1a3d42496f..1f407474d6 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/TlsPSKProtocolTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/TlsPSKProtocolTest.java @@ -4,7 +4,10 @@ import java.io.PipedInputStream; import java.io.PipedOutputStream; +import org.bouncycastle.tls.AlertDescription; import org.bouncycastle.tls.TlsClientProtocol; +import org.bouncycastle.tls.TlsFatalAlertReceived; +import org.bouncycastle.tls.TlsServer; import org.bouncycastle.tls.TlsServerProtocol; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.io.Streams; @@ -14,8 +17,27 @@ public class TlsPSKProtocolTest extends TestCase { + public void testBadClientKey() throws Exception + { + MockPSKTlsClient client = new MockPSKTlsClient(null, true); + MockPSKTlsServer server = new MockPSKTlsServer(); + + implTestKeyMismatch(client, server); + } + + public void testBadServerKey() throws Exception + { + MockPSKTlsClient client = new MockPSKTlsClient(null); + MockPSKTlsServer server = new MockPSKTlsServer(true); + + implTestKeyMismatch(client, server); + } + public void testClientServer() throws Exception { + MockPSKTlsClient client = new MockPSKTlsClient(null); + MockPSKTlsServer server = new MockPSKTlsServer(); + PipedInputStream clientRead = TlsTestUtils.createPipedInputStream(); PipedInputStream serverRead = TlsTestUtils.createPipedInputStream(); PipedOutputStream clientWrite = new PipedOutputStream(serverRead); @@ -24,10 +46,9 @@ public void testClientServer() throws Exception TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); - ServerThread serverThread = new ServerThread(serverProtocol); + ServerThread serverThread = new ServerThread(serverProtocol, server); serverThread.start(); - MockPSKTlsClient client = new MockPSKTlsClient(null); clientProtocol.connect(client); // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity @@ -50,28 +71,67 @@ public void testClientServer() throws Exception serverThread.join(); } + private void implTestKeyMismatch(MockPSKTlsClient client, MockPSKTlsServer server) throws Exception + { + PipedInputStream clientRead = TlsTestUtils.createPipedInputStream(); + PipedInputStream serverRead = TlsTestUtils.createPipedInputStream(); + PipedOutputStream clientWrite = new PipedOutputStream(serverRead); + PipedOutputStream serverWrite = new PipedOutputStream(clientRead); + + TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); + TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); + + ServerThread serverThread = new ServerThread(serverProtocol, server); + serverThread.start(); + + boolean correctException = false; + short alertDescription = -1; + + try + { + clientProtocol.connect(client); + } + catch (TlsFatalAlertReceived e) + { + correctException = true; + alertDescription = e.getAlertDescription(); + } + catch (Exception e) + { + } + finally + { + clientProtocol.close(); + } + + serverThread.join(); + + assertTrue(correctException); + assertEquals(AlertDescription.bad_record_mac, alertDescription); + } + static class ServerThread extends Thread { private final TlsServerProtocol serverProtocol; + private final TlsServer server; - ServerThread(TlsServerProtocol serverProtocol) + ServerThread(TlsServerProtocol serverProtocol, TlsServer server) { this.serverProtocol = serverProtocol; + this.server = server; } public void run() { try { - MockPSKTlsServer server = new MockPSKTlsServer(); serverProtocol.accept(server); Streams.pipeAll(serverProtocol.getInputStream(), serverProtocol.getOutputStream()); serverProtocol.close(); } catch (Exception e) { -// throw new RuntimeException(e); } } } diff --git a/tls/src/test/java/org/bouncycastle/tls/test/TlsTestUtils.java b/tls/src/test/java/org/bouncycastle/tls/test/TlsTestUtils.java index 84ab7539ee..4fcd758a79 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/TlsTestUtils.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/TlsTestUtils.java @@ -31,6 +31,7 @@ import org.bouncycastle.crypto.util.PrivateKeyFactory; import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.tls.AlertDescription; +import org.bouncycastle.tls.BasicTlsPSKIdentity; import org.bouncycastle.tls.Certificate; import org.bouncycastle.tls.CertificateEntry; import org.bouncycastle.tls.ProtocolVersion; @@ -41,6 +42,7 @@ import org.bouncycastle.tls.TlsCredentialedDecryptor; import org.bouncycastle.tls.TlsCredentialedSigner; import org.bouncycastle.tls.TlsFatalAlert; +import org.bouncycastle.tls.TlsPSKIdentity; import org.bouncycastle.tls.TlsUtils; import org.bouncycastle.tls.crypto.TlsCertificate; import org.bouncycastle.tls.crypto.TlsCrypto; @@ -54,6 +56,7 @@ import org.bouncycastle.tls.crypto.impl.jcajce.JceDefaultTlsCredentialedAgreement; import org.bouncycastle.tls.crypto.impl.jcajce.JceDefaultTlsCredentialedDecryptor; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.io.pem.PemObject; @@ -83,6 +86,11 @@ public class TlsTestUtils + "0lAQH/BAgwBgYEVR0lADAcBgNVHREBAf8EEjAQgQ50ZXN0QHRlc3QudGVzdDANBgkqhkiG9w0BAQQFAANBAJg55PBS" + "weg6obRUKF4FF6fCrWFi6oCYSQ99LWcAeupc5BofW5MstFMhCOaEucuGVqunwT5G7/DweazzCIrSzB0="); + static TlsPSKIdentity createDefaultPSKIdentity(boolean badKey) + { + return new BasicTlsPSKIdentity("client", getPSKPasswordUTF8(badKey)); + } + static String fingerprint(org.bouncycastle.asn1.x509.Certificate c) throws IOException { @@ -175,6 +183,16 @@ static String getCACertResource(String eeCertResource) throws IOException throw new TlsFatalAlert(AlertDescription.internal_error); } + static String getPSKPassword(boolean badKey) + { + return badKey ? "TLS_TEST_PSK_BAD" : "TLS_TEST_PSK"; + } + + static byte[] getPSKPasswordUTF8(boolean badKey) + { + return Strings.toUTF8ByteArray(getPSKPassword(badKey)); + } + static String getResourceName(short signatureAlgorithm) throws IOException { switch (signatureAlgorithm) From 0d35060deb55e38dde6cfb8a3cc096407244cb48 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 10 Jul 2025 14:12:36 +0700 Subject: [PATCH 445/890] RSADigestSignerTest updates --- .../crypto/test/RSADigestSignerTest.java | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RSADigestSignerTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RSADigestSignerTest.java index 717807e2f7..d152adb46a 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RSADigestSignerTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RSADigestSignerTest.java @@ -5,12 +5,21 @@ import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.DigestInfo; import org.bouncycastle.asn1.x509.X509ObjectIdentifiers; import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.Signer; +import org.bouncycastle.crypto.digests.MD2Digest; +import org.bouncycastle.crypto.digests.MD4Digest; +import org.bouncycastle.crypto.digests.MD5Digest; import org.bouncycastle.crypto.digests.NullDigest; +import org.bouncycastle.crypto.digests.RIPEMD128Digest; +import org.bouncycastle.crypto.digests.RIPEMD160Digest; +import org.bouncycastle.crypto.digests.RIPEMD256Digest; import org.bouncycastle.crypto.digests.SHA1Digest; import org.bouncycastle.crypto.digests.SHA224Digest; import org.bouncycastle.crypto.digests.SHA256Digest; @@ -46,12 +55,13 @@ public void performTest() throws Exception RSAKeyParameters rsaPublic = new RSAKeyParameters(false, rsaPubMod, rsaPubExp); RSAPrivateCrtKeyParameters rsaPrivate = new RSAPrivateCrtKeyParameters(rsaPrivMod, rsaPubExp, rsaPrivExp, rsaPrivP, rsaPrivQ, rsaPrivDP, rsaPrivDQ, rsaPrivQinv); - checkDigest(rsaPublic, rsaPrivate, new SHA1Digest(), X509ObjectIdentifiers.id_SHA1); - checkNullDigest(rsaPublic, rsaPrivate, new SHA1Digest(), X509ObjectIdentifiers.id_SHA1); + checkDigest(rsaPublic, rsaPrivate, new RIPEMD128Digest(), TeleTrusTObjectIdentifiers.ripemd128); + checkDigest(rsaPublic, rsaPrivate, new RIPEMD160Digest(), TeleTrusTObjectIdentifiers.ripemd160); + checkDigest(rsaPublic, rsaPrivate, new RIPEMD256Digest(), TeleTrusTObjectIdentifiers.ripemd256); + checkDigest(rsaPublic, rsaPrivate, new SHA1Digest(), X509ObjectIdentifiers.id_SHA1); checkDigest(rsaPublic, rsaPrivate, new SHA224Digest(), NISTObjectIdentifiers.id_sha224); checkDigest(rsaPublic, rsaPrivate, SHA256Digest.newInstance(), NISTObjectIdentifiers.id_sha256); - checkNullDigest(rsaPublic, rsaPrivate, SHA256Digest.newInstance(), NISTObjectIdentifiers.id_sha256); checkDigest(rsaPublic, rsaPrivate, new SHA384Digest(), NISTObjectIdentifiers.id_sha384); checkDigest(rsaPublic, rsaPrivate, new SHA512Digest(), NISTObjectIdentifiers.id_sha512); checkDigest(rsaPublic, rsaPrivate, new SHA512tDigest(224), NISTObjectIdentifiers.id_sha512_224); @@ -62,12 +72,17 @@ public void performTest() throws Exception checkDigest(rsaPublic, rsaPrivate, new SHA3Digest(384), NISTObjectIdentifiers.id_sha3_384); checkDigest(rsaPublic, rsaPrivate, new SHA3Digest(512), NISTObjectIdentifiers.id_sha3_512); + checkDigest(rsaPublic, rsaPrivate, new MD2Digest(), PKCSObjectIdentifiers.md2); + checkDigest(rsaPublic, rsaPrivate, new MD4Digest(), PKCSObjectIdentifiers.md4); + checkDigest(rsaPublic, rsaPrivate, new MD5Digest(), PKCSObjectIdentifiers.md5); + + checkNullDigest(rsaPublic, rsaPrivate, new SHA1Digest(), X509ObjectIdentifiers.id_SHA1); + checkNullDigest(rsaPublic, rsaPrivate, SHA256Digest.newInstance(), NISTObjectIdentifiers.id_sha256); + // Null format test - RSADigestSigner signer = new RSADigestSigner(new NullDigest()); - + RSADigestSigner signer = createPrehashSigner(); signer.init(true, rsaPrivate); - - signer.update(new byte[16], 0, 16); + signer.update(new byte[20], 0, 20); try { @@ -76,7 +91,7 @@ public void performTest() throws Exception } catch (CryptoException e) { - isTrue(e.getMessage().startsWith("unable to encode signature: malformed DigestInfo")); + isTrue(e.getMessage().startsWith("unable to encode signature: ")); } } @@ -104,7 +119,7 @@ private void checkNullDigest(RSAKeyParameters rsaPublic, RSAPrivateCrtKeyParamet { byte[] msg = new byte[] { 1, 6, 3, 32, 7, 43, 2, 5, 7, 78, 4, 23 }; - RSADigestSigner signer = new RSADigestSigner(new NullDigest()); + RSADigestSigner signer = createPrehashSigner(); byte[] hash = new byte[digest.getDigestSize()]; digest.update(msg, 0, msg.length); @@ -127,7 +142,7 @@ private void checkNullDigest(RSAKeyParameters rsaPublic, RSAPrivateCrtKeyParamet fail("NONE - RSA Digest Signer failed."); } - signer = new RSADigestSigner(new NullDigest()); + signer = createPrehashSigner(); signer.init(false, rsaPublic); signer.update(infoEnc, 0, infoEnc.length); if (!signer.verifySignature(sig)) @@ -140,4 +155,9 @@ public static void main(String[] args) { runTest(new RSADigestSignerTest()); } + + private static RSADigestSigner createPrehashSigner() + { + return new RSADigestSigner(new NullDigest()); + } } From 9e0bef72ab1115cf41c57746fd15fff877a01ac3 Mon Sep 17 00:00:00 2001 From: Gefei Li Date: Thu, 29 May 2025 19:39:02 -0400 Subject: [PATCH 446/890] Pqc MLDSA rejection testvectors (cherry picked from commit e52d75ee145c4851b656ad1438733d6070460b29) --- .../crypto/params/ParametersWithDigest.java | 34 ----- .../pqc/crypto/mldsa/HashMLDSASigner.java | 34 +++-- .../pqc/crypto/test/MLDSATest.java | 140 +++++------------- 3 files changed, 55 insertions(+), 153 deletions(-) delete mode 100644 core/src/main/java/org/bouncycastle/crypto/params/ParametersWithDigest.java diff --git a/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithDigest.java b/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithDigest.java deleted file mode 100644 index d444b9f501..0000000000 --- a/core/src/main/java/org/bouncycastle/crypto/params/ParametersWithDigest.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.bouncycastle.crypto.params; - -import org.bouncycastle.crypto.CipherParameters; -import org.bouncycastle.crypto.Digest; - -public class ParametersWithDigest - implements CipherParameters -{ - private CipherParameters parameters; - private Digest digest; - - public ParametersWithDigest( - CipherParameters parameters, - Digest digest) - { - if (digest == null) - { - throw new NullPointerException("'digest' cannot be null"); - } - - this.parameters = parameters; - this.digest = digest; - } - - public Digest getDigest() - { - return digest; - } - - public CipherParameters getParameters() - { - return parameters; - } -} diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java index c90f230460..00c06a4a79 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java @@ -10,7 +10,6 @@ import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Signer; import org.bouncycastle.crypto.params.ParametersWithContext; -import org.bouncycastle.crypto.params.ParametersWithDigest; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.pqc.crypto.DigestUtils; @@ -25,6 +24,7 @@ public class HashMLDSASigner private MLDSAEngine engine; private Digest digest; + private byte[] digestOIDEncoding; public HashMLDSASigner() { @@ -32,16 +32,6 @@ public HashMLDSASigner() public void init(boolean forSigning, CipherParameters param) { - if (param instanceof ParametersWithDigest) - { - ParametersWithDigest withDigest = (ParametersWithDigest) param; - param = withDigest.getParameters(); - digest = withDigest.getDigest(); - } - else - { - digest = engine.shake256Digest; - } byte[] ctx = EMPTY_CONTEXT; if (param instanceof ParametersWithContext) { @@ -74,6 +64,7 @@ public void init(boolean forSigning, CipherParameters param) parameters = privKey.getParameters(); engine = parameters.getEngine(random); + engine.initSign(privKey.tr, true, ctx); } else @@ -81,12 +72,13 @@ public void init(boolean forSigning, CipherParameters param) pubKey = (MLDSAPublicKeyParameters)param; privKey = null; random = null; + parameters = pubKey.getParameters(); engine = parameters.getEngine(null); + engine.initVerify(pubKey.rho, pubKey.t1, true, ctx); } - - + digest = engine.shake256Digest; byte[] digestOIDEncoding; try { @@ -149,4 +141,20 @@ public void reset() // // return engine.verifyInternal(signature, signature.length, message, message.length, pubKey.rho, pubKey.t1); // } + +// private static Digest createDigest(MLDSAParameters parameters) +// { + //TODO: MLDSA44 may use SHA2-256, SHA3-256, SHAKE128 + // MLDSA65 may use SHA3-384, SHA2-512 + // MLDSA44/65/87 may use SHA2-512, SHA3-512, SHAKE256 + +// switch (parameters.getType()) +// { +// case MLDSAParameters.TYPE_PURE: +// case MLDSAParameters.TYPE_SHA2_512: +// return new SHAKEDigest(256); +// default: +// throw new IllegalArgumentException("unknown parameters type"); +// } +// } } diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java index 0afb043968..610a8b358d 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java @@ -15,16 +15,6 @@ import junit.framework.TestCase; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.CryptoException; -import org.bouncycastle.crypto.Digest; -import org.bouncycastle.crypto.digests.SHA1Digest; -import org.bouncycastle.crypto.digests.SHA224Digest; -import org.bouncycastle.crypto.digests.SHA256Digest; -import org.bouncycastle.crypto.digests.SHA384Digest; -import org.bouncycastle.crypto.digests.SHA3Digest; -import org.bouncycastle.crypto.digests.SHA512Digest; -import org.bouncycastle.crypto.digests.SHA512tDigest; -import org.bouncycastle.crypto.digests.SHAKEDigest; -import org.bouncycastle.crypto.params.ParametersWithDigest; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.pqc.crypto.mldsa.HashMLDSASigner; import org.bouncycastle.pqc.crypto.mldsa.MLDSAKeyGenerationParameters; @@ -55,14 +45,13 @@ public class MLDSATest }; private static final MLDSAParameters[] PARAMETER_SETS = new MLDSAParameters[] - { - MLDSAParameters.ml_dsa_44, - MLDSAParameters.ml_dsa_65, - MLDSAParameters.ml_dsa_87, - }; + { + MLDSAParameters.ml_dsa_44, + MLDSAParameters.ml_dsa_65, + MLDSAParameters.ml_dsa_87, + }; - public void testConsistency() - throws Exception + public void testConsistency() throws Exception { SecureRandom random = new SecureRandom(); @@ -479,72 +468,6 @@ public void testSigVerCombinedVectorSet() } } - public void testHashMLDSA() - throws Exception - { - Digest[] digests = new Digest[] { - new SHA1Digest(), - new SHA224Digest(), - new SHA256Digest(), - new SHA384Digest(), - new SHA512Digest(), - new SHA512tDigest(224), - new SHA512tDigest(256), - new SHA3Digest(224), - new SHA3Digest(256), - new SHA3Digest(384), - new SHA3Digest(512), - new SHAKEDigest(128), - new SHAKEDigest(256), - }; - SecureRandom random = new SecureRandom(); - - MLDSAKeyPairGenerator kpg = new MLDSAKeyPairGenerator(); - - for (int idx = 0; idx != PARAMETER_SETS.length; idx++) - { - MLDSAParameters parameters = PARAMETER_SETS[idx]; - kpg.init(new MLDSAKeyGenerationParameters(random, parameters)); - - int msgSize = 0; - for (Digest digest :digests ) - { - - do - { - byte[] msg = new byte[msgSize]; - - for (int i = 0; i < 2; ++i) - { - AsymmetricCipherKeyPair kp = kpg.generateKeyPair(); - - HashMLDSASigner signer = new HashMLDSASigner(); - - for (int j = 0; j < 2; ++j) - { - random.nextBytes(msg); - - // sign - signer.init(true, new ParametersWithDigest(kp.getPrivate(), digest)); - signer.update(msg, 0, msg.length); - byte[] signature = signer.generateSignature(); - - // verify - signer.init(false, new ParametersWithDigest(kp.getPublic(), digest)); - signer.update(msg, 0, msg.length); - boolean shouldVerify = signer.verifySignature(signature); - - assertTrue("count = " + i, shouldVerify); - } - } - - msgSize += msgSize < 128 ? 1 : 17; - } - while (msgSize <= 256); - } - } - } - public void testMLDSARejection() throws Exception { @@ -578,7 +501,7 @@ private void rejectionTest(MLDSAParameters parameters, String filename, Rejectio List testVectors = parseTestVectors(TestResourceFinder.findTestResource("pqc/crypto/mldsa", filename)); for (int i = 0; i < testVectors.size(); ++i) { - TestVector t = (TestVector)testVectors.get(i); + TestVector t = testVectors.get(i); FixedSecureRandom random = new FixedSecureRandom(t.seed); MLDSAKeyPairGenerator kpGen = new MLDSAKeyPairGenerator(); @@ -617,6 +540,7 @@ private void rejectionExternalMuTest(MLDSAParameters parameters, String filename { rejectionTest(parameters, filename, new RejectionOperation() { + @Override public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) throws CryptoException { @@ -625,6 +549,7 @@ public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) return signer.generateMuSignature(msg); } + @Override public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) { InternalMLDSASigner signer = new InternalMLDSASigner(); @@ -639,6 +564,7 @@ private void rejectionPrehashTest(MLDSAParameters parameters, String filename) { rejectionTest(parameters, filename, new RejectionOperation() { + @Override public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) throws CryptoException { @@ -648,6 +574,7 @@ public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) return signer.generateSignature(); } + @Override public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) { HashMLDSASigner signer = new HashMLDSASigner(); @@ -663,6 +590,7 @@ private void rejectionTest(MLDSAParameters parameters, String filename) { rejectionTest(parameters, filename, new RejectionOperation() { + @Override public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) throws CryptoException { @@ -673,6 +601,7 @@ public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) return signer.generateSignature(); } + @Override public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) { InternalMLDSASigner signer = new InternalMLDSASigner(); @@ -688,6 +617,7 @@ private void rejectionUpStreamTest(MLDSAParameters parameters, String filename) { rejectionTest(parameters, filename, new RejectionOperation() { + @Override public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) throws CryptoException { @@ -695,7 +625,8 @@ public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) signer.init(true, privParams); return signer.internalGenerateSignature(msg, new byte[32]); } - + + @Override public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) { InternalMLDSASigner signer = new InternalMLDSASigner(); @@ -709,7 +640,7 @@ public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byt private static List parseTestVectors(InputStream src) throws IOException { - List vectors = new ArrayList(); + List vectors = new ArrayList<>(); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); TestVector currentVector = null; @@ -723,13 +654,13 @@ private static List parseTestVectors(InputStream src) { // Skip comments and empty lines line = line.split("//")[0].trim(); - if (line.length() == 0) + if (line.isEmpty()) { continue; } // Look for test vector array start - if (line.indexOf("dilithium_rejection_testvectors[] = ") >= 0) + if (line.contains("dilithium_rejection_testvectors[] = ")) { continue; } @@ -746,7 +677,7 @@ private static List parseTestVectors(InputStream src) if (fieldMatcher.find()) { currentField = fieldMatcher.group(1); - currentBytes = new ArrayList(); + currentBytes = new ArrayList<>(); line = line.substring(fieldMatcher.end()).trim(); } @@ -757,11 +688,11 @@ private static List parseTestVectors(InputStream src) while (hexMatcher.find()) { String hex = hexMatcher.group(1); - currentBytes.add(new Byte((byte)Integer.parseInt(hex, 16))); + currentBytes.add((byte)Integer.parseInt(hex, 16)); } // Check for field end - if (line.indexOf("},") >= 0) + if (line.contains("},")) { setField(currentVector, currentField, currentBytes); currentField = null; @@ -786,30 +717,27 @@ private static void setField(TestVector vector, String field, List bytes) byte[] byteArray = new byte[bytes.size()]; for (int i = 0; i < bytes.size(); i++) { - byteArray[i] = ((Byte)bytes.get(i)).byteValue(); + byteArray[i] = bytes.get(i); } - if ("seed".equals(field)) + switch (field) { + case "seed": vector.seed = byteArray; - } - else if ("pk".equals(field)) - { + break; + case "pk": vector.pk = byteArray; - } - else if ("sk".equals(field)) - { + break; + case "sk": vector.sk = byteArray; - } - else if ("msg".equals(field)) - { + break; + case "msg": vector.msg = byteArray; - } - else if ("sig".equals(field)) - { + break; + case "sig": vector.sig = byteArray; + break; } - // else ignore } static class TestVector From f449caddad1db4be55755284091a644ec2c05c28 Mon Sep 17 00:00:00 2001 From: royb Date: Thu, 10 Jul 2025 14:10:01 -0400 Subject: [PATCH 447/890] Added ML-DSA test to TlsCryptoTest. --- .../tls/crypto/impl/bc/BcTlsCrypto.java | 3 --- .../crypto/impl/bc/BcTlsRawKeyCertificate.java | 15 ++++++++++++++- .../tls/crypto/test/TlsCryptoTest.java | 9 ++++++--- .../org/bouncycastle/tls/test/TlsTestUtils.java | 11 ++++++++++- 4 files changed, 30 insertions(+), 8 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java index becbc8898f..ffa43ccbb7 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java @@ -458,9 +458,6 @@ public boolean hasSignatureScheme(int signatureScheme) { case SignatureScheme.sm2sig_sm3: // TODO[tls] Test coverage before adding - case SignatureScheme.DRAFT_mldsa44: - case SignatureScheme.DRAFT_mldsa65: - case SignatureScheme.DRAFT_mldsa87: return false; default: { diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsRawKeyCertificate.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsRawKeyCertificate.java index ed9342dded..ddbf371f8d 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsRawKeyCertificate.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsRawKeyCertificate.java @@ -409,7 +409,7 @@ public MLDSAPublicKeyParameters getPubKeyMLDSA() throws IOException { try { - return (MLDSAPublicKeyParameters)getPublicKey(); + return (MLDSAPublicKeyParameters)getPQCPublicKey(); } catch (ClassCastException e) { @@ -417,6 +417,19 @@ public MLDSAPublicKeyParameters getPubKeyMLDSA() throws IOException } } + protected AsymmetricKeyParameter getPQCPublicKey() throws IOException + { + try + { + return org.bouncycastle.pqc.crypto.util.PublicKeyFactory.createKey(keyInfo); + } + catch (RuntimeException e) + { + throw new TlsFatalAlert(AlertDescription.unsupported_certificate, e); + } + } + + public RSAKeyParameters getPubKeyRSA() throws IOException { try diff --git a/tls/src/test/java/org/bouncycastle/tls/crypto/test/TlsCryptoTest.java b/tls/src/test/java/org/bouncycastle/tls/crypto/test/TlsCryptoTest.java index a5ffbc4c30..4c63b0c577 100644 --- a/tls/src/test/java/org/bouncycastle/tls/crypto/test/TlsCryptoTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/crypto/test/TlsCryptoTest.java @@ -187,6 +187,12 @@ protected TlsCredentialedSigner loadCredentialedSigner13(TlsCryptoParameters cry case SignatureScheme.rsa_pss_rsae_sha384: case SignatureScheme.rsa_pss_rsae_sha512: return loadCredentialedSigner(cryptoParams, "rsa-sign", signatureAndHashAlgorithm); + case SignatureScheme.DRAFT_mldsa44: + return loadCredentialedSigner(cryptoParams, "ml_dsa_44", signatureAndHashAlgorithm); + case SignatureScheme.DRAFT_mldsa65: + return loadCredentialedSigner(cryptoParams, "ml_dsa_65", signatureAndHashAlgorithm); + case SignatureScheme.DRAFT_mldsa87: + return loadCredentialedSigner(cryptoParams, "ml_dsa_87", signatureAndHashAlgorithm); // TODO[tls] Add test resources for these case SignatureScheme.ecdsa_brainpoolP256r1tls13_sha256: @@ -195,9 +201,6 @@ protected TlsCredentialedSigner loadCredentialedSigner13(TlsCryptoParameters cry case SignatureScheme.ecdsa_secp384r1_sha384: case SignatureScheme.ecdsa_secp521r1_sha512: case SignatureScheme.sm2sig_sm3: - case SignatureScheme.DRAFT_mldsa44: - case SignatureScheme.DRAFT_mldsa65: - case SignatureScheme.DRAFT_mldsa87: default: return null; diff --git a/tls/src/test/java/org/bouncycastle/tls/test/TlsTestUtils.java b/tls/src/test/java/org/bouncycastle/tls/test/TlsTestUtils.java index 4fcd758a79..1ada7741f5 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/TlsTestUtils.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/TlsTestUtils.java @@ -417,7 +417,16 @@ static AsymmetricKeyParameter loadBcPrivateKeyResource(String resource) PemObject pem = loadPemResource(resource); if (pem.getType().equals("PRIVATE KEY")) { - return PrivateKeyFactory.createKey(pem.getContent()); + AsymmetricKeyParameter kp; + try + { + kp = PrivateKeyFactory.createKey(pem.getContent()); + } + catch (Exception e) + { + kp = org.bouncycastle.pqc.crypto.util.PrivateKeyFactory.createKey(pem.getContent()); + } + return kp; } if (pem.getType().equals("ENCRYPTED PRIVATE KEY")) { From 15404685ce9d1774e1dfc96625d168a5cfd3a527 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 11 Jul 2025 12:57:39 +0700 Subject: [PATCH 448/890] Refactoring in pqc.crypto.slhdsa --- .../org/bouncycastle/pqc/crypto/slhdsa/Fors.java | 15 +++++++-------- .../org/bouncycastle/pqc/crypto/slhdsa/HT.java | 2 +- .../pqc/crypto/slhdsa/SLHDSAEngine.java | 3 --- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/Fors.java b/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/Fors.java index 3123c56fba..e92d5c6aaa 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/Fors.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/Fors.java @@ -70,8 +70,8 @@ public SIG_FORS[] sign(byte[] md, byte[] skSeed, byte[] pkSeed, ADRS paramAdrs) // int[] idxs = message_to_idxs(md, engine.K, engine.A); int[] idxs = base2B(md, engine.A, engine.K); SIG_FORS[] sig_fors = new SIG_FORS[engine.K]; + // compute signature elements - int t = engine.T; for (int i = 0; i < engine.K; i++) { // get next index @@ -80,7 +80,7 @@ public SIG_FORS[] sign(byte[] md, byte[] skSeed, byte[] pkSeed, ADRS paramAdrs) adrs.setTypeAndClear(ADRS.FORS_PRF); adrs.setKeyPairAddress(paramAdrs.getKeyPairAddress()); adrs.setTreeHeight(0); - adrs.setTreeIndex(i * t + idx); + adrs.setTreeIndex((i << engine.A) + idx); byte[] sk = engine.PRF(pkSeed, skSeed, adrs); @@ -90,8 +90,8 @@ public SIG_FORS[] sign(byte[] md, byte[] skSeed, byte[] pkSeed, ADRS paramAdrs) // compute auth path for (int j = 0; j < engine.A; j++) { - int s = (idx / (1 << j)) ^ 1; - authPath[j] = treehash(skSeed, i * t + s * (1 << j), j, pkSeed, adrs); + int s = (idx >>> j) ^ 1; + authPath[j] = treehash(skSeed, (i << engine.A) + (s << j), j, pkSeed, adrs); } sig_fors[i] = new SIG_FORS(sk, authPath); } @@ -102,7 +102,6 @@ public byte[] pkFromSig(SIG_FORS[] sig_fors, byte[] message, byte[] pkSeed, ADRS { byte[][] node = new byte[2][]; byte[][] root = new byte[engine.K][]; - int t = engine.T; // int[] idxs = message_to_idxs(message, engine.K, engine.A); int[] idxs = base2B(message, engine.A, engine.K); @@ -114,16 +113,16 @@ public byte[] pkFromSig(SIG_FORS[] sig_fors, byte[] message, byte[] pkSeed, ADRS // compute leaf byte[] sk = sig_fors[i].getSK(); adrs.setTreeHeight(0); - adrs.setTreeIndex(i * t + idx); + adrs.setTreeIndex((i << engine.A) + idx); node[0] = engine.F(pkSeed, adrs, sk); // compute root from leaf and AUTH byte[][] authPath = sig_fors[i].getAuthPath(); - adrs.setTreeIndex(i * t + idx); + adrs.setTreeIndex((i << engine.A) + idx); for (int j = 0; j < engine.A; j++) { adrs.setTreeHeight(j + 1); - if (((idx / (1 << j)) % 2) == 0) + if ((idx & (1 << j)) == 0) { adrs.setTreeIndex(adrs.getTreeIndex() / 2); node[1] = engine.H(pkSeed, adrs, node[0], authPath[j]); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/HT.java b/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/HT.java index 8bf4c0440c..698f53238d 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/HT.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/HT.java @@ -101,7 +101,7 @@ byte[] xmss_pkFromSig(int idx, SIG_XMSS sig_xmss, byte[] M, byte[] pkSeed, ADRS for (int k = 0; k < engine.H_PRIME; k++) { adrs.setTreeHeight(k + 1); - if (((idx / (1 << k)) % 2) == 0) + if ((idx & (1 << k)) == 0) { adrs.setTreeIndex(adrs.getTreeIndex() / 2); node1 = engine.H(pkSeed, adrs, node0, AUTH[k]); diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/SLHDSAEngine.java b/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/SLHDSAEngine.java index 58c8d8b765..8f0cc44d45 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/SLHDSAEngine.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/SLHDSAEngine.java @@ -30,8 +30,6 @@ abstract class SLHDSAEngine final int H; // FULL_HEIGHT final int H_PRIME; // H / D - final int T; // T = 1 << A - public SLHDSAEngine(int n, int w, int d, int a, int k, int h) { this.N = n; @@ -87,7 +85,6 @@ else if (N <= 256) this.K = k; this.H = h; this.H_PRIME = h / d; - this.T = 1 << a; } abstract void init(byte[] pkSeed); From 44bf13ebe8d84c237112df5ffc099587e5fb9897 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 14 Jul 2025 21:13:28 +0700 Subject: [PATCH 449/890] Explicit DER encoding for clarity --- .../java/org/bouncycastle/crypto/test/RSADigestSignerTest.java | 3 ++- .../bouncycastle/tls/crypto/impl/jcajce/JcaTlsRSASigner.java | 3 ++- .../bouncycastle/tls/crypto/impl/jcajce/JcaTlsRSAVerifier.java | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/crypto/test/RSADigestSignerTest.java b/core/src/test/java/org/bouncycastle/crypto/test/RSADigestSignerTest.java index d152adb46a..0ef078e966 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/RSADigestSignerTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/RSADigestSignerTest.java @@ -2,6 +2,7 @@ import java.math.BigInteger; +import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.nist.NISTObjectIdentifiers; @@ -126,7 +127,7 @@ private void checkNullDigest(RSAKeyParameters rsaPublic, RSAPrivateCrtKeyParamet digest.doFinal(hash, 0); DigestInfo digInfo = new DigestInfo(new AlgorithmIdentifier(digOid, DERNull.INSTANCE), hash); - byte[] infoEnc = digInfo.getEncoded(); + byte[] infoEnc = digInfo.getEncoded(ASN1Encoding.DER); signer.init(true, rsaPrivate); diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsRSASigner.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsRSASigner.java index 584bf44f14..86a13c28fe 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsRSASigner.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsRSASigner.java @@ -6,6 +6,7 @@ import java.security.PublicKey; import java.security.Signature; +import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.DigestInfo; @@ -71,7 +72,7 @@ public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm, byte[] h */ AlgorithmIdentifier algID = new AlgorithmIdentifier( TlsUtils.getOIDForHashAlgorithm(algorithm.getHash()), DERNull.INSTANCE); - input = new DigestInfo(algID, hash).getEncoded(); + input = new DigestInfo(algID, hash).getEncoded(ASN1Encoding.DER); } else { diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsRSAVerifier.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsRSAVerifier.java index a78458bf83..d3d0532bc5 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsRSAVerifier.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/jcajce/JcaTlsRSAVerifier.java @@ -5,6 +5,7 @@ import java.security.PublicKey; import java.security.Signature; +import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.x509.AlgorithmIdentifier; import org.bouncycastle.asn1.x509.DigestInfo; @@ -80,7 +81,7 @@ public boolean verifyRawSignature(DigitallySigned digitallySigned, byte[] hash) */ AlgorithmIdentifier algID = new AlgorithmIdentifier( TlsUtils.getOIDForHashAlgorithm(algorithm.getHash()), DERNull.INSTANCE); - byte[] digestInfo = new DigestInfo(algID, hash).getEncoded(); + byte[] digestInfo = new DigestInfo(algID, hash).getEncoded(ASN1Encoding.DER); verifier.update(digestInfo, 0, digestInfo.length); } else From f5ad7de617ef0e2d6959f2482804dc4014d6b5df Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Mon, 14 Jul 2025 21:15:55 +0700 Subject: [PATCH 450/890] Include raw RSA test without DigestInfo encoding --- .../jce/provider/test/RSATest.java | 44 +++++++++++++------ 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/prov/src/test/java/org/bouncycastle/jce/provider/test/RSATest.java b/prov/src/test/java/org/bouncycastle/jce/provider/test/RSATest.java index 81a406175a..cd755dd835 100644 --- a/prov/src/test/java/org/bouncycastle/jce/provider/test/RSATest.java +++ b/prov/src/test/java/org/bouncycastle/jce/provider/test/RSATest.java @@ -967,24 +967,42 @@ private void rawModeTest(String sigName, ASN1ObjectIdentifier digestOID, MessageDigest digest = MessageDigest.getInstance(digestOID.getId(), "BC"); byte[] hash = digest.digest(sampleMessage); - byte[] digInfo = derEncode(digestOID, hash); - Signature rawSig = Signature.getInstance("RSA", "BC"); - rawSig.initSign(privKey); - rawSig.update(digInfo); - byte[] rawResult = rawSig.sign(); - - if (!Arrays.areEqual(normalResult, rawResult)) { - fail("raw mode signature differs from normal one"); - } + Signature rawSig = Signature.getInstance("RSA", "BC"); + rawSig.initSign(privKey); + rawSig.update(hash); + byte[] rawResult = rawSig.sign(); - rawSig.initVerify(pubKey); - rawSig.update(digInfo); + rawSig.initVerify(pubKey); + rawSig.update(hash); + + if (!rawSig.verify(rawResult)) + { + fail("raw mode (no DigestInfo) signature verification failed"); + } + } - if (!rawSig.verify(rawResult)) { - fail("raw mode signature verification failed"); + byte[] digInfo = derEncode(digestOID, hash); + + Signature rawSig = Signature.getInstance("RSA", "BC"); + rawSig.initSign(privKey); + rawSig.update(digInfo); + byte[] rawResult = rawSig.sign(); + + if (!Arrays.areEqual(normalResult, rawResult)) + { + fail("raw mode signature differs from normal one"); + } + + rawSig.initVerify(pubKey); + rawSig.update(digInfo); + + if (!rawSig.verify(rawResult)) + { + fail("raw mode signature verification failed"); + } } } From 7d920d70a6d1add14f9dc604bfa409b310524d0b Mon Sep 17 00:00:00 2001 From: royb Date: Mon, 14 Jul 2025 13:30:09 -0400 Subject: [PATCH 451/890] disabled mldsa in hasSignatureScheme until check/rules are added for tls13 --- .../java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java index ffa43ccbb7..becbc8898f 100644 --- a/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java +++ b/tls/src/main/java/org/bouncycastle/tls/crypto/impl/bc/BcTlsCrypto.java @@ -458,6 +458,9 @@ public boolean hasSignatureScheme(int signatureScheme) { case SignatureScheme.sm2sig_sm3: // TODO[tls] Test coverage before adding + case SignatureScheme.DRAFT_mldsa44: + case SignatureScheme.DRAFT_mldsa65: + case SignatureScheme.DRAFT_mldsa87: return false; default: { From 602519acbe50e9026ec5f12a1300b9acba096f8c Mon Sep 17 00:00:00 2001 From: gefeili Date: Tue, 15 Jul 2025 13:45:57 +0930 Subject: [PATCH 452/890] Fix a typo in SLHDSA --- .../org/bouncycastle/pqc/crypto/slhdsa/HashSLHDSASigner.java | 2 +- .../java/org/bouncycastle/pqc/crypto/slhdsa/SLHDSASigner.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/HashSLHDSASigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/HashSLHDSASigner.java index 16a725bd93..7ae5c71e7b 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/HashSLHDSASigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/HashSLHDSASigner.java @@ -19,7 +19,7 @@ import org.bouncycastle.util.Arrays; /** - * SLH-DA signer. + * SLH-DSA signer. */ public class HashSLHDSASigner implements Signer diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/SLHDSASigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/SLHDSASigner.java index ff3ad48583..7b53fc311e 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/SLHDSASigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/slhdsa/SLHDSASigner.java @@ -9,7 +9,7 @@ import org.bouncycastle.util.Arrays; /** - * SLH-DA signer. + * SLH-DSA signer. *

      * This version is based on the 3rd submission with deference to the updated reference * implementation on github as at November 9th 2021. This version includes the changes From 568ffae5d9d8788b62074eaa688b25627eec7837 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 16 Jul 2025 13:08:01 +0700 Subject: [PATCH 453/890] getOptional methods should be usable with sequence/set elements --- .../main/java/org/bouncycastle/asn1/ASN1TaggedObject.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java b/core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java index d81c92bf8a..e7151debab 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1TaggedObject.java @@ -75,7 +75,7 @@ public static ASN1TaggedObject getInstance(ASN1TaggedObject taggedObject, int ta return ASN1Util.getExplicitBaseTagged(checkInstance(taggedObject, declaredExplicit), tagClass, tagNo); } - public static ASN1TaggedObject getOptional(ASN1Object element) + public static ASN1TaggedObject getOptional(ASN1Encodable element) { if (element == null) { @@ -90,7 +90,7 @@ public static ASN1TaggedObject getOptional(ASN1Object element) return null; } - public static ASN1TaggedObject getOptional(ASN1Object element, int tagClass) + public static ASN1TaggedObject getOptional(ASN1Encodable element, int tagClass) { ASN1TaggedObject taggedObject = getOptional(element); if (taggedObject != null && taggedObject.hasTagClass(tagClass)) @@ -100,7 +100,7 @@ public static ASN1TaggedObject getOptional(ASN1Object element, int tagClass) return null; } - public static ASN1TaggedObject getOptional(ASN1Object element, int tagClass, int tagNo) + public static ASN1TaggedObject getOptional(ASN1Encodable element, int tagClass, int tagNo) { ASN1TaggedObject taggedObject = getOptional(element); if (taggedObject != null && taggedObject.hasTag(tagClass, tagNo)) From 785defec107641e7d3369a45b0d38184f0566a37 Mon Sep 17 00:00:00 2001 From: David Hook Date: Wed, 16 Jul 2025 16:23:06 +1000 Subject: [PATCH 454/890] merged 1.81.1 - DRBG with org.bouncycastle.drbg.effective_256bits_entropy property for entropy adjustment. --- .../org/bouncycastle/jcajce/provider/drbg/DRBG.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/prov/src/main/java/org/bouncycastle/jcajce/provider/drbg/DRBG.java b/prov/src/main/java/org/bouncycastle/jcajce/provider/drbg/DRBG.java index 437de65ff3..8bb83aa619 100644 --- a/prov/src/main/java/org/bouncycastle/jcajce/provider/drbg/DRBG.java +++ b/prov/src/main/java/org/bouncycastle/jcajce/provider/drbg/DRBG.java @@ -53,6 +53,14 @@ public class DRBG { private static final String PREFIX = DRBG.class.getName(); + private static int get256BitsEffectiveEntropySize() + { + // by default we assume .9 bits per real bit + int effectiveBits = Properties.asInteger("org.bouncycastle.drbg.effective_256bits_entropy", 282); + + return ((effectiveBits + 7) / 8) * 8; + } + // {"Provider class name","SecureRandomSpi class name"} private static final String[][] initialEntropySourceNames = new String[][] { @@ -463,7 +471,7 @@ private static class HybridEntropySource EntropySourceProvider entropyProvider = createCoreEntropySourceProvider(); bytesRequired = (bitsRequired + 7) / 8; // remember for the seed generator we need the correct security strength for SHA-512 - entropySource = new SignallingEntropySource(entropyDaemon, seedAvailable, entropyProvider, 256); + entropySource = new SignallingEntropySource(entropyDaemon, seedAvailable, entropyProvider, get256BitsEffectiveEntropySize()); drbg = new SP800SecureRandomBuilder(new EntropySourceProvider() { public EntropySource get(final int bitsRequired) @@ -592,7 +600,7 @@ private static class OneShotHybridEntropySource EntropySourceProvider entropyProvider = createCoreEntropySourceProvider(); bytesRequired = (bitsRequired + 7) / 8; // remember for the seed generator we need the correct security strength for SHA-512 - entropySource = new OneShotSignallingEntropySource(seedAvailable, entropyProvider, 256); + entropySource = new OneShotSignallingEntropySource(seedAvailable, entropyProvider, get256BitsEffectiveEntropySize()); drbg = new SP800SecureRandomBuilder(new EntropySourceProvider() { public EntropySource get(final int bitsRequired) From ff83bf507a7504fadae3889bb7f8f7fdf5dd937a Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 16 Jul 2025 13:34:33 +0700 Subject: [PATCH 455/890] ASN.1: Add getTagged method to universal types --- .../java/org/bouncycastle/asn1/ASN1BMPString.java | 11 ++++++++--- .../java/org/bouncycastle/asn1/ASN1BitString.java | 9 +++++++-- .../java/org/bouncycastle/asn1/ASN1Boolean.java | 11 ++++++++--- .../java/org/bouncycastle/asn1/ASN1Enumerated.java | 11 ++++++++--- .../java/org/bouncycastle/asn1/ASN1External.java | 9 +++++++-- .../org/bouncycastle/asn1/ASN1GeneralString.java | 11 ++++++++--- .../org/bouncycastle/asn1/ASN1GeneralizedTime.java | 11 ++++++++--- .../org/bouncycastle/asn1/ASN1GraphicString.java | 11 ++++++++--- .../java/org/bouncycastle/asn1/ASN1IA5String.java | 11 ++++++++--- .../java/org/bouncycastle/asn1/ASN1Integer.java | 11 ++++++++--- .../main/java/org/bouncycastle/asn1/ASN1Null.java | 9 +++++++-- .../org/bouncycastle/asn1/ASN1NumericString.java | 11 ++++++++--- .../org/bouncycastle/asn1/ASN1ObjectDescriptor.java | 11 ++++++++--- .../org/bouncycastle/asn1/ASN1ObjectIdentifier.java | 13 +++++++++---- .../java/org/bouncycastle/asn1/ASN1OctetString.java | 11 ++++++++--- .../org/bouncycastle/asn1/ASN1PrintableString.java | 11 ++++++++--- .../java/org/bouncycastle/asn1/ASN1RelativeOID.java | 9 +++++++-- .../java/org/bouncycastle/asn1/ASN1Sequence.java | 11 ++++++++--- .../main/java/org/bouncycastle/asn1/ASN1Set.java | 11 ++++++++--- .../java/org/bouncycastle/asn1/ASN1T61String.java | 11 ++++++++--- .../java/org/bouncycastle/asn1/ASN1UTCTime.java | 11 ++++++++--- .../java/org/bouncycastle/asn1/ASN1UTF8String.java | 11 ++++++++--- .../org/bouncycastle/asn1/ASN1UniversalString.java | 11 ++++++++--- .../org/bouncycastle/asn1/ASN1UniversalType.java | 7 ++++++- .../org/bouncycastle/asn1/ASN1VideotexString.java | 11 ++++++++--- .../org/bouncycastle/asn1/ASN1VisibleString.java | 11 ++++++++--- 26 files changed, 203 insertions(+), 73 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1BMPString.java b/core/src/main/java/org/bouncycastle/asn1/ASN1BMPString.java index 6b1fb1c928..e2dd1f68a8 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1BMPString.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1BMPString.java @@ -64,14 +64,19 @@ public static ASN1BMPString getInstance(Object obj) * Return a BMP String from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly tagged false + * @param declaredExplicit true if the object is meant to be explicitly tagged false * otherwise. * @exception IllegalArgumentException if the tagged object cannot be converted. * @return an ASN1BMPString instance. */ - public static ASN1BMPString getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1BMPString getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1BMPString)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1BMPString)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1BMPString getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1BMPString)TYPE.getTagged(taggedObject, declaredExplicit); } final char[] string; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1BitString.java b/core/src/main/java/org/bouncycastle/asn1/ASN1BitString.java index b5ada96794..4364186043 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1BitString.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1BitString.java @@ -56,9 +56,14 @@ else if (obj instanceof byte[]) throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); } - public static ASN1BitString getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1BitString getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1BitString)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1BitString)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1BitString getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1BitString)TYPE.getTagged(taggedObject, declaredExplicit); } private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java index 2794e1726f..2ea88193e0 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Boolean.java @@ -87,15 +87,20 @@ public static ASN1Boolean getInstance(int value) * Return a Boolean from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly + * @param declaredExplicit true if the object is meant to be explicitly * tagged false otherwise. * @exception IllegalArgumentException if the tagged object cannot * be converted. * @return an ASN1Boolean instance. */ - public static ASN1Boolean getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1Boolean getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1Boolean)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1Boolean)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1Boolean getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1Boolean)TYPE.getTagged(taggedObject, declaredExplicit); } private ASN1Boolean(byte value) diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java index c2aa7dc7a9..df587ee740 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Enumerated.java @@ -53,15 +53,20 @@ public static ASN1Enumerated getInstance( * return an Enumerated from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly + * @param declaredExplicit true if the object is meant to be explicitly * tagged false otherwise. * @exception IllegalArgumentException if the tagged object cannot * be converted. * @return an ASN1Enumerated instance, or null. */ - public static ASN1Enumerated getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1Enumerated getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1Enumerated)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1Enumerated)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1Enumerated getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1Enumerated)TYPE.getTagged(taggedObject, declaredExplicit); } private final byte[] contents; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1External.java b/core/src/main/java/org/bouncycastle/asn1/ASN1External.java index d75ced245b..658fbe41cd 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1External.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1External.java @@ -48,9 +48,14 @@ else if (obj instanceof byte[]) throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); } - public static ASN1External getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1External getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1External)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1External)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1External getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1External)TYPE.getTagged(taggedObject, declaredExplicit); } ASN1ObjectIdentifier directReference; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1GeneralString.java b/core/src/main/java/org/bouncycastle/asn1/ASN1GeneralString.java index c527f4dbc3..e6b53b2f3b 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1GeneralString.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1GeneralString.java @@ -65,14 +65,19 @@ public static ASN1GeneralString getInstance(Object obj) * Return a GeneralString from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly tagged false + * @param declaredExplicit true if the object is meant to be explicitly tagged false * otherwise. * @exception IllegalArgumentException if the tagged object cannot be converted. * @return an ASN1GeneralString instance. */ - public static ASN1GeneralString getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1GeneralString getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1GeneralString)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1GeneralString)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1GeneralString getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1GeneralString)TYPE.getTagged(taggedObject, declaredExplicit); } final byte[] contents; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java b/core/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java index be41ba56e9..41c4fc21e1 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1GeneralizedTime.java @@ -93,14 +93,19 @@ public static ASN1GeneralizedTime getInstance( * return a Generalized Time object from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly tagged false + * @param declaredExplicit true if the object is meant to be explicitly tagged false * otherwise. * @return an ASN1GeneralizedTime instance. * @throws IllegalArgumentException if the tagged object cannot be converted. */ - public static ASN1GeneralizedTime getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1GeneralizedTime getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1GeneralizedTime)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1GeneralizedTime)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1GeneralizedTime getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1GeneralizedTime)TYPE.getTagged(taggedObject, declaredExplicit); } final byte[] contents; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1GraphicString.java b/core/src/main/java/org/bouncycastle/asn1/ASN1GraphicString.java index b21142bda9..5f55a7373e 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1GraphicString.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1GraphicString.java @@ -57,14 +57,19 @@ public static ASN1GraphicString getInstance(Object obj) * Return a GraphicString from a tagged object. * * @param taggedObject the tagged object holding the object we want. - * @param explicit true if the object is meant to be explicitly tagged, + * @param declaredExplicit true if the object is meant to be explicitly tagged, * false otherwise. * @exception IllegalArgumentException if the tagged object cannot be converted. * @return an ASN1GraphicString instance, or null. */ - public static ASN1GraphicString getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1GraphicString getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1GraphicString)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1GraphicString)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1GraphicString getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1GraphicString)TYPE.getTagged(taggedObject, declaredExplicit); } final byte[] contents; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1IA5String.java b/core/src/main/java/org/bouncycastle/asn1/ASN1IA5String.java index ecf848f982..b3a5458304 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1IA5String.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1IA5String.java @@ -63,15 +63,20 @@ public static ASN1IA5String getInstance(Object obj) * Return an IA5 String from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly + * @param declaredExplicit true if the object is meant to be explicitly * tagged false otherwise. * @exception IllegalArgumentException if the tagged object cannot * be converted. * @return an ASN1IA5String instance, or null. */ - public static ASN1IA5String getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1IA5String getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1IA5String)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1IA5String)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1IA5String getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1IA5String)TYPE.getTagged(taggedObject, declaredExplicit); } final byte[] contents; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Integer.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Integer.java index cfc4049713..55b48c60c8 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1Integer.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Integer.java @@ -60,15 +60,20 @@ public static ASN1Integer getInstance( * Return an Integer from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly + * @param declaredExplicit true if the object is meant to be explicitly * tagged false otherwise. * @return an ASN1Integer instance. * @throws IllegalArgumentException if the tagged object cannot * be converted. */ - public static ASN1Integer getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1Integer getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1Integer)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1Integer)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1Integer getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1Integer)TYPE.getTagged(taggedObject, declaredExplicit); } /** diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Null.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Null.java index bad23470cf..b2b1dedd63 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1Null.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Null.java @@ -53,9 +53,14 @@ public static ASN1Null getInstance(Object o) return null; } - public static ASN1Null getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1Null getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1Null)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1Null)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1Null getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1Null)TYPE.getTagged(taggedObject, declaredExplicit); } ASN1Null() diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1NumericString.java b/core/src/main/java/org/bouncycastle/asn1/ASN1NumericString.java index 3609acaed1..f92357591a 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1NumericString.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1NumericString.java @@ -67,14 +67,19 @@ public static ASN1NumericString getInstance(Object obj) * Return an Numeric String from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly tagged false + * @param declaredExplicit true if the object is meant to be explicitly tagged false * otherwise. * @exception IllegalArgumentException if the tagged object cannot be converted. * @return an ASN1NumericString instance, or null. */ - public static ASN1NumericString getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1NumericString getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1NumericString)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1NumericString)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1NumericString getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1NumericString)TYPE.getTagged(taggedObject, declaredExplicit); } final byte[] contents; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1ObjectDescriptor.java b/core/src/main/java/org/bouncycastle/asn1/ASN1ObjectDescriptor.java index 5323d5c48d..261201bcf1 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1ObjectDescriptor.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1ObjectDescriptor.java @@ -60,14 +60,19 @@ else if (obj instanceof byte[]) * Return an ObjectDescriptor from a tagged object. * * @param taggedObject the tagged object holding the object we want. - * @param explicit true if the object is meant to be explicitly tagged, + * @param declaredExplicit true if the object is meant to be explicitly tagged, * false otherwise. * @exception IllegalArgumentException if the tagged object cannot be converted. * @return an ASN1ObjectDescriptor instance, or null. */ - public static ASN1ObjectDescriptor getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1ObjectDescriptor getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1ObjectDescriptor)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1ObjectDescriptor)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1ObjectDescriptor getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1ObjectDescriptor)TYPE.getTagged(taggedObject, declaredExplicit); } private final ASN1GraphicString baseGraphicString; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java b/core/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java index 61acea41af..e868b716fc 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1ObjectIdentifier.java @@ -83,20 +83,20 @@ else if (obj instanceof byte[]) * Return an OBJECT IDENTIFIER from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly + * @param declaredExplicit true if the object is meant to be explicitly * tagged false otherwise. * @return an ASN1ObjectIdentifier instance, or null. * @throws IllegalArgumentException if the tagged object cannot * be converted. */ - public static ASN1ObjectIdentifier getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1ObjectIdentifier getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { /* * TODO[asn1] This block here is for backward compatibility, but should eventually be removed. * * - see https://github.com/bcgit/bc-java/issues/1015 */ - if (!explicit && !taggedObject.isParsed() && taggedObject.hasContextTag()) + if (!declaredExplicit && !taggedObject.isParsed() && taggedObject.hasContextTag()) { ASN1Primitive base = taggedObject.getBaseObject().toASN1Primitive(); if (!(base instanceof ASN1ObjectIdentifier)) @@ -105,7 +105,12 @@ public static ASN1ObjectIdentifier getInstance(ASN1TaggedObject taggedObject, bo } } - return (ASN1ObjectIdentifier)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1ObjectIdentifier)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1ObjectIdentifier getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1ObjectIdentifier)TYPE.getTagged(taggedObject, declaredExplicit); } public static ASN1ObjectIdentifier tryFromID(String identifier) diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java b/core/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java index 896f56f94d..417f71de0e 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1OctetString.java @@ -117,14 +117,19 @@ ASN1Primitive fromImplicitConstructed(ASN1Sequence sequence) * return an Octet String from a tagged object. * * @param taggedObject the tagged object holding the object we want. - * @param explicit true if the object is meant to be explicitly + * @param declaredExplicit true if the object is meant to be explicitly * tagged false otherwise. * @exception IllegalArgumentException if the tagged object cannot * be converted. */ - public static ASN1OctetString getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1OctetString getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1OctetString)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1OctetString)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1OctetString getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1OctetString)TYPE.getTagged(taggedObject, declaredExplicit); } /** diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1PrintableString.java b/core/src/main/java/org/bouncycastle/asn1/ASN1PrintableString.java index a859b7dc97..14acbec1a6 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1PrintableString.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1PrintableString.java @@ -83,15 +83,20 @@ public static ASN1PrintableString getInstance(Object obj) * Return a Printable String from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly + * @param declaredExplicit true if the object is meant to be explicitly * tagged false otherwise. * @exception IllegalArgumentException if the tagged object cannot * be converted. * @return an ASN1PrintableString instance, or null. */ - public static ASN1PrintableString getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1PrintableString getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1PrintableString)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1PrintableString)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1PrintableString getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1PrintableString)TYPE.getTagged(taggedObject, declaredExplicit); } final byte[] contents; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1RelativeOID.java b/core/src/main/java/org/bouncycastle/asn1/ASN1RelativeOID.java index 00f980508c..ac98c449f4 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1RelativeOID.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1RelativeOID.java @@ -71,9 +71,14 @@ else if (obj instanceof byte[]) throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); } - public static ASN1RelativeOID getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1RelativeOID getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1RelativeOID)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1RelativeOID)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1RelativeOID getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1RelativeOID)TYPE.getTagged(taggedObject, declaredExplicit); } public static ASN1RelativeOID tryFromID(String identifier) diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java index 66c22ec9f3..b69e597f1c 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Sequence.java @@ -116,15 +116,20 @@ else if (obj instanceof byte[]) * be using this method. * * @param taggedObject the tagged object. - * @param explicit true if the object is meant to be explicitly tagged, + * @param declaredExplicit true if the object is meant to be explicitly tagged, * false otherwise. * @exception IllegalArgumentException if the tagged object cannot * be converted. * @return an ASN1Sequence instance. */ - public static ASN1Sequence getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1Sequence getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1Sequence)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1Sequence)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1Sequence getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1Sequence)TYPE.getTagged(taggedObject, declaredExplicit); } // NOTE: Only non-final to support LazyEncodedSequence diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1Set.java b/core/src/main/java/org/bouncycastle/asn1/ASN1Set.java index 8cd3ad583b..00e6b1c2f7 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1Set.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1Set.java @@ -154,15 +154,20 @@ else if (obj instanceof byte[]) * be using this method. * * @param taggedObject the tagged object. - * @param explicit true if the object is meant to be explicitly tagged + * @param declaredExplicit true if the object is meant to be explicitly tagged * false otherwise. * @exception IllegalArgumentException if the tagged object cannot * be converted. * @return an ASN1Set instance. */ - public static ASN1Set getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1Set getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1Set)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1Set)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1Set getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1Set)TYPE.getTagged(taggedObject, declaredExplicit); } protected final ASN1Encodable[] elements; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1T61String.java b/core/src/main/java/org/bouncycastle/asn1/ASN1T61String.java index 2592005ff1..4edbfe5f20 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1T61String.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1T61String.java @@ -61,14 +61,19 @@ public static ASN1T61String getInstance(Object obj) * Return an T61 String from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly tagged false + * @param declaredExplicit true if the object is meant to be explicitly tagged false * otherwise. * @exception IllegalArgumentException if the tagged object cannot be converted. * @return an ASN1T61String instance, or null */ - public static ASN1T61String getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1T61String getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1T61String)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1T61String)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1T61String getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1T61String)TYPE.getTagged(taggedObject, declaredExplicit); } final byte[] contents; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java b/core/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java index c849a75a50..eb7b2b58f1 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1UTCTime.java @@ -83,14 +83,19 @@ public static ASN1UTCTime getInstance( * Return an UTC Time from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly tagged false + * @param declaredExplicit true if the object is meant to be explicitly tagged false * otherwise. * @exception IllegalArgumentException if the tagged object cannot be converted. * @return an ASN1UTCTime instance, or null. */ - public static ASN1UTCTime getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1UTCTime getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1UTCTime)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1UTCTime)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1UTCTime getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1UTCTime)TYPE.getTagged(taggedObject, declaredExplicit); } final byte[] contents; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1UTF8String.java b/core/src/main/java/org/bouncycastle/asn1/ASN1UTF8String.java index 8f8d00ccde..c0a337f484 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1UTF8String.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1UTF8String.java @@ -57,14 +57,19 @@ public static ASN1UTF8String getInstance(Object obj) * Return an UTF8 String from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly tagged false + * @param declaredExplicit true if the object is meant to be explicitly tagged false * otherwise. * @exception IllegalArgumentException if the tagged object cannot be converted. * @return a DERUTF8String instance, or null */ - public static ASN1UTF8String getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1UTF8String getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1UTF8String)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1UTF8String)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1UTF8String getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1UTF8String)TYPE.getTagged(taggedObject, declaredExplicit); } final byte[] contents; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1UniversalString.java b/core/src/main/java/org/bouncycastle/asn1/ASN1UniversalString.java index fe8addfe57..af0f318379 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1UniversalString.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1UniversalString.java @@ -63,14 +63,19 @@ public static ASN1UniversalString getInstance(Object obj) * Return a Universal String from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly tagged false + * @param declaredExplicit true if the object is meant to be explicitly tagged false * otherwise. * @exception IllegalArgumentException if the tagged object cannot be converted. * @return a ASN1UniversalString instance, or null */ - public static ASN1UniversalString getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1UniversalString getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1UniversalString)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1UniversalString)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1UniversalString getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1UniversalString)TYPE.getTagged(taggedObject, declaredExplicit); } final byte[] contents; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1UniversalType.java b/core/src/main/java/org/bouncycastle/asn1/ASN1UniversalType.java index 9e720b0ed1..e7cbe4a1cf 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1UniversalType.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1UniversalType.java @@ -39,11 +39,16 @@ final ASN1Primitive fromByteArray(byte[] bytes) throws IOException return checkedCast(ASN1Primitive.fromByteArray(bytes)); } - final ASN1Primitive getContextInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) + final ASN1Primitive getContextTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) { return checkedCast(ASN1Util.checkContextTagClass(taggedObject).getBaseUniversal(declaredExplicit, this)); } + final ASN1Primitive getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return checkedCast(taggedObject.getBaseUniversal(declaredExplicit, this)); + } + final ASN1Tag getTag() { return tag; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1VideotexString.java b/core/src/main/java/org/bouncycastle/asn1/ASN1VideotexString.java index ae9c42285f..facbd89e10 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1VideotexString.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1VideotexString.java @@ -57,14 +57,19 @@ public static ASN1VideotexString getInstance(Object obj) * return a Videotex String from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly tagged false + * @param declaredExplicit true if the object is meant to be explicitly tagged false * otherwise. * @exception IllegalArgumentException if the tagged object cannot be converted. * @return an ASN1VideotexString instance, or null. */ - public static ASN1VideotexString getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1VideotexString getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1VideotexString)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1VideotexString)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1VideotexString getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1VideotexString)TYPE.getTagged(taggedObject, declaredExplicit); } final byte[] contents; diff --git a/core/src/main/java/org/bouncycastle/asn1/ASN1VisibleString.java b/core/src/main/java/org/bouncycastle/asn1/ASN1VisibleString.java index 6424cfcdaf..bb14e52177 100644 --- a/core/src/main/java/org/bouncycastle/asn1/ASN1VisibleString.java +++ b/core/src/main/java/org/bouncycastle/asn1/ASN1VisibleString.java @@ -64,15 +64,20 @@ public static ASN1VisibleString getInstance( * Return a Visible String from a tagged object. * * @param taggedObject the tagged object holding the object we want - * @param explicit true if the object is meant to be explicitly + * @param declaredExplicit true if the object is meant to be explicitly * tagged false otherwise. * @exception IllegalArgumentException if the tagged object cannot * be converted. * @return an ASN1VisibleString instance, or null */ - public static ASN1VisibleString getInstance(ASN1TaggedObject taggedObject, boolean explicit) + public static ASN1VisibleString getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) { - return (ASN1VisibleString)TYPE.getContextInstance(taggedObject, explicit); + return (ASN1VisibleString)TYPE.getContextTagged(taggedObject, declaredExplicit); + } + + public static ASN1VisibleString getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return (ASN1VisibleString)TYPE.getTagged(taggedObject, declaredExplicit); } final byte[] contents; From 01363f213a8d7f64ac179da6f3f21da3137495f9 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 16 Jul 2025 13:37:07 +0700 Subject: [PATCH 456/890] Call BCPGOutputStream.finish for clarity --- .../org/bouncycastle/openpgp/PGPEncryptedDataGenerator.java | 2 ++ pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java | 3 +++ 2 files changed, 5 insertions(+) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedDataGenerator.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedDataGenerator.java index 046e5c1c1e..66e849f020 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedDataGenerator.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedDataGenerator.java @@ -404,6 +404,8 @@ public void close() // BCPGOutputStream bOut = new BCPGOutputStream(genOut, PacketTags.MOD_DETECTION_CODE, 20); + // For clarity; really only required if using partial body lengths + bOut.finish(); bOut.flush(); byte[] dig = digestCalc.getDigest(); diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java index b6c814ed2f..4289a773d7 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java @@ -840,6 +840,9 @@ public void encode(OutputStream outStream) { Util.encodePGPSignatures(out, pub.subSigs, false); } + + // For clarity; really only required if using partial body lengths + out.finish(); } /** From 5fbd878798cc7e763eb060cc5f54b3794a7658f3 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 16 Jul 2025 16:04:16 +0700 Subject: [PATCH 457/890] OptionalValidity updates - make it clear Time is a CHOICE type (so explicit tagging) --- .../asn1/crmf/OptionalValidity.java | 57 ++++++++++++------- 1 file changed, 36 insertions(+), 21 deletions(-) diff --git a/util/src/main/java/org/bouncycastle/asn1/crmf/OptionalValidity.java b/util/src/main/java/org/bouncycastle/asn1/crmf/OptionalValidity.java index bbc3124a41..62ddebd16e 100644 --- a/util/src/main/java/org/bouncycastle/asn1/crmf/OptionalValidity.java +++ b/util/src/main/java/org/bouncycastle/asn1/crmf/OptionalValidity.java @@ -14,6 +14,31 @@ public class OptionalValidity extends ASN1Object { + public static OptionalValidity getInstance(Object o) + { + if (o instanceof OptionalValidity) + { + return (OptionalValidity)o; + } + + if (o != null) + { + return new OptionalValidity(ASN1Sequence.getInstance(o)); + } + + return null; + } + + public static OptionalValidity getInstance(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return new OptionalValidity(ASN1Sequence.getInstance(taggedObject, declaredExplicit)); + } + + public static OptionalValidity getTagged(ASN1TaggedObject taggedObject, boolean declaredExplicit) + { + return new OptionalValidity(ASN1Sequence.getTagged(taggedObject, declaredExplicit)); + } + private Time notBefore; private Time notAfter; @@ -26,35 +51,22 @@ private OptionalValidity(ASN1Sequence seq) if (tObj.getTagNo() == 0) { - notBefore = Time.getInstance(tObj, true); + notBefore = Time.getInstance(tObj, true); // CHOICE } else { - notAfter = Time.getInstance(tObj, true); + notAfter = Time.getInstance(tObj, true); // CHOICE } } - } - public static OptionalValidity getInstance(Object o) - { - if (o instanceof OptionalValidity) - { - return (OptionalValidity)o; - } - - if (o != null) - { - return new OptionalValidity(ASN1Sequence.getInstance(o)); - } - - return null; + // TODO[crmf] Validate the "at least one" rule after parsing? } public OptionalValidity(Time notBefore, Time notAfter) { if (notBefore == null && notAfter == null) { - throw new IllegalArgumentException("at least one of notBefore/notAfter must not be null."); + throw new IllegalArgumentException("at least one of notBefore/notAfter MUST be present."); } this.notBefore = notBefore; @@ -74,9 +86,12 @@ public Time getNotAfter() /** *

            * OptionalValidity ::= SEQUENCE {
      -     *                        notBefore  [0] Time OPTIONAL,
      -     *                        notAfter   [1] Time OPTIONAL } --at least one MUST be present
      +     *     notBefore    [0] Time OPTIONAL,
      +     *     notAfter     [1] Time OPTIONAL } --at least one MUST be present
      +     *
      +     * Time ::= CHOICE { ... }
            * 
      + * * @return a basic ASN.1 object representation. */ public ASN1Primitive toASN1Primitive() @@ -85,12 +100,12 @@ public ASN1Primitive toASN1Primitive() if (notBefore != null) { - v.add(new DERTaggedObject(true, 0, notBefore)); + v.add(new DERTaggedObject(true, 0, notBefore)); // CHOICE } if (notAfter != null) { - v.add(new DERTaggedObject(true, 1, notAfter)); + v.add(new DERTaggedObject(true, 1, notAfter)); // CHOICE } return new DERSequence(v); From 3a53da916c6e4908d7e6e23267741a81fec3a8e7 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Wed, 16 Jul 2025 17:27:26 +0700 Subject: [PATCH 458/890] Refactor MPInteger --- .../org/bouncycastle/util/BigIntegers.java | 12 ++++ .../java/org/bouncycastle/bcpg/MPInteger.java | 60 ++++++++++--------- 2 files changed, 45 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/util/BigIntegers.java b/core/src/main/java/org/bouncycastle/util/BigIntegers.java index 7e70366cdc..72307d1390 100644 --- a/core/src/main/java/org/bouncycastle/util/BigIntegers.java +++ b/core/src/main/java/org/bouncycastle/util/BigIntegers.java @@ -1,5 +1,7 @@ package org.bouncycastle.util; +import java.io.IOException; +import java.io.OutputStream; import java.math.BigInteger; import java.security.SecureRandom; import java.util.Map; @@ -393,6 +395,16 @@ public static BigInteger createRandomPrime(int bitLength, int certainty, SecureR return rv; } + public static void writeUnsignedByteArray(OutputStream out, BigInteger n) throws IOException + { + byte[] b = n.toByteArray(); + + int off = b[0] == 0 ? 1 : 0; + int len = b.length - off; + + out.write(b, off, len); + } + private static byte[] createRandom(int bitLength, SecureRandom random) throws IllegalArgumentException { diff --git a/pg/src/main/java/org/bouncycastle/bcpg/MPInteger.java b/pg/src/main/java/org/bouncycastle/bcpg/MPInteger.java index 286ab2ab34..91596f827e 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/MPInteger.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/MPInteger.java @@ -3,28 +3,30 @@ import java.io.IOException; import java.math.BigInteger; +import org.bouncycastle.util.BigIntegers; + /** * a multiple precision integer */ public class MPInteger extends BCPGObject { - BigInteger value = null; - - public MPInteger( - BCPGInputStream in) - throws IOException + private final BigInteger value; + + public MPInteger(BCPGInputStream in) throws IOException { - int length = StreamUtil.read2OctetLength(in); - byte[] bytes = new byte[(length + 7) / 8]; - - in.readFully(bytes); - - value = new BigInteger(1, bytes); + /* + * TODO RFC 9580 3.2. When parsing an MPI in a version 6 Key, Signature, or Public Key Encrypted + * Session Key (PKESK) packet, the implementation MUST check that the encoded length matches the + * length starting from the most significant non-zero bit; if it doesn't match, reject the packet as + * malformed. + */ + boolean validateLength = false; + + this.value = readMPI(in, validateLength); } - - public MPInteger( - BigInteger value) + + public MPInteger(BigInteger value) { if (value == null || value.signum() < 0) { @@ -33,27 +35,31 @@ public MPInteger( this.value = value; } - + public BigInteger getValue() { return value; } - - public void encode( - BCPGOutputStream out) - throws IOException + + public void encode(BCPGOutputStream out) throws IOException { StreamUtil.write2OctetLength(out, value.bitLength()); + BigIntegers.writeUnsignedByteArray(out, value); + } - byte[] bytes = value.toByteArray(); - - if (bytes[0] == 0) - { - out.write(bytes, 1, bytes.length - 1); - } - else + private static BigInteger readMPI(BCPGInputStream in, boolean validateLength) throws IOException + { + int bitLength = StreamUtil.read2OctetLength(in); + int byteLength = (bitLength + 7) / 8; + byte[] bytes = new byte[byteLength]; + in.readFully(bytes); + BigInteger n = new BigInteger(1, bytes); + + if (validateLength && n.bitLength() != bitLength) { - out.write(bytes, 0, bytes.length); + throw new IOException("malformed MPI"); } + + return n; } } From e34ee368072575c8af65322c26b76aa570a5b31e Mon Sep 17 00:00:00 2001 From: David Hook Date: Thu, 17 Jul 2025 23:04:01 +1000 Subject: [PATCH 459/890] reverted introduction of switch and diamond operators. --- .../pqc/crypto/test/MLDSATest.java | 62 +++++++++---------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java index 610a8b358d..1bf6dcaa85 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java @@ -45,13 +45,14 @@ public class MLDSATest }; private static final MLDSAParameters[] PARAMETER_SETS = new MLDSAParameters[] - { - MLDSAParameters.ml_dsa_44, - MLDSAParameters.ml_dsa_65, - MLDSAParameters.ml_dsa_87, - }; + { + MLDSAParameters.ml_dsa_44, + MLDSAParameters.ml_dsa_65, + MLDSAParameters.ml_dsa_87, + }; - public void testConsistency() throws Exception + public void testConsistency() + throws Exception { SecureRandom random = new SecureRandom(); @@ -501,7 +502,7 @@ private void rejectionTest(MLDSAParameters parameters, String filename, Rejectio List testVectors = parseTestVectors(TestResourceFinder.findTestResource("pqc/crypto/mldsa", filename)); for (int i = 0; i < testVectors.size(); ++i) { - TestVector t = testVectors.get(i); + TestVector t = (TestVector)testVectors.get(i); FixedSecureRandom random = new FixedSecureRandom(t.seed); MLDSAKeyPairGenerator kpGen = new MLDSAKeyPairGenerator(); @@ -540,7 +541,6 @@ private void rejectionExternalMuTest(MLDSAParameters parameters, String filename { rejectionTest(parameters, filename, new RejectionOperation() { - @Override public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) throws CryptoException { @@ -549,7 +549,6 @@ public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) return signer.generateMuSignature(msg); } - @Override public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) { InternalMLDSASigner signer = new InternalMLDSASigner(); @@ -564,7 +563,6 @@ private void rejectionPrehashTest(MLDSAParameters parameters, String filename) { rejectionTest(parameters, filename, new RejectionOperation() { - @Override public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) throws CryptoException { @@ -574,7 +572,6 @@ public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) return signer.generateSignature(); } - @Override public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) { HashMLDSASigner signer = new HashMLDSASigner(); @@ -590,7 +587,6 @@ private void rejectionTest(MLDSAParameters parameters, String filename) { rejectionTest(parameters, filename, new RejectionOperation() { - @Override public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) throws CryptoException { @@ -601,7 +597,6 @@ public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) return signer.generateSignature(); } - @Override public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) { InternalMLDSASigner signer = new InternalMLDSASigner(); @@ -617,7 +612,6 @@ private void rejectionUpStreamTest(MLDSAParameters parameters, String filename) { rejectionTest(parameters, filename, new RejectionOperation() { - @Override public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) throws CryptoException { @@ -626,7 +620,6 @@ public byte[] processSign(MLDSAPrivateKeyParameters privParams, byte[] msg) return signer.internalGenerateSignature(msg, new byte[32]); } - @Override public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byte[] sig) { InternalMLDSASigner signer = new InternalMLDSASigner(); @@ -640,7 +633,7 @@ public boolean processVerify(MLDSAPublicKeyParameters pubParams, byte[] msg, byt private static List parseTestVectors(InputStream src) throws IOException { - List vectors = new ArrayList<>(); + List vectors = new ArrayList(); BufferedReader bin = new BufferedReader(new InputStreamReader(src)); TestVector currentVector = null; @@ -654,13 +647,13 @@ private static List parseTestVectors(InputStream src) { // Skip comments and empty lines line = line.split("//")[0].trim(); - if (line.isEmpty()) + if (line.length() == 0) { continue; } // Look for test vector array start - if (line.contains("dilithium_rejection_testvectors[] = ")) + if (line.indexOf("dilithium_rejection_testvectors[] = ") >= 0) { continue; } @@ -677,7 +670,7 @@ private static List parseTestVectors(InputStream src) if (fieldMatcher.find()) { currentField = fieldMatcher.group(1); - currentBytes = new ArrayList<>(); + currentBytes = new ArrayList(); line = line.substring(fieldMatcher.end()).trim(); } @@ -688,11 +681,11 @@ private static List parseTestVectors(InputStream src) while (hexMatcher.find()) { String hex = hexMatcher.group(1); - currentBytes.add((byte)Integer.parseInt(hex, 16)); + currentBytes.add(new Byte((byte)Integer.parseInt(hex, 16))); } // Check for field end - if (line.contains("},")) + if (line.indexOf("},") >= 0) { setField(currentVector, currentField, currentBytes); currentField = null; @@ -717,27 +710,30 @@ private static void setField(TestVector vector, String field, List bytes) byte[] byteArray = new byte[bytes.size()]; for (int i = 0; i < bytes.size(); i++) { - byteArray[i] = bytes.get(i); + byteArray[i] = ((Byte)bytes.get(i)).byteValue(); } - switch (field) + if ("seed".equals(field)) { - case "seed": vector.seed = byteArray; - break; - case "pk": + } + else if ("pk".equals(field)) + { vector.pk = byteArray; - break; - case "sk": + } + else if ("sk".equals(field)) + { vector.sk = byteArray; - break; - case "msg": + } + else if ("msg".equals(field)) + { vector.msg = byteArray; - break; - case "sig": + } + else if ("sig".equals(field)) + { vector.sig = byteArray; - break; } + // else ignore } static class TestVector From e404cefa156ad1b2b75a3b5bfc1d81a981a45460 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 18 Jul 2025 00:20:06 +0700 Subject: [PATCH 460/890] Refactoring in OpenPGP --- .../openpgp/PGPEncryptedData.java | 6 +- .../openpgp/PGPPublicKeyEncryptedData.java | 125 ++++++------- .../bouncycastle/openpgp/PGPSecretKey.java | 165 +++++++++--------- .../openpgp/PGPSymmetricKeyEncryptedData.java | 15 +- .../java/org/bouncycastle/openpgp/Util.java | 14 +- 5 files changed, 157 insertions(+), 168 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedData.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedData.java index c5083b851f..201ba7e6b9 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedData.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPEncryptedData.java @@ -7,7 +7,6 @@ import org.bouncycastle.bcpg.AEADAlgorithmTags; import org.bouncycastle.bcpg.AEADEncDataPacket; -import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.bcpg.InputStreamPacket; import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket; import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags; @@ -276,10 +275,11 @@ public int getAlgorithm() throw new UnsupportedOperationException("not supported - override required"); } - boolean processSymmetricEncIntegrityPacketDataStream(boolean withIntegrityPacket, PGPDataDecryptor dataDecryptor, BCPGInputStream encIn) + boolean processSymmetricEncIntegrityPacketDataStream(boolean withIntegrityPacket, PGPDataDecryptor dataDecryptor, + InputStream encIn) throws IOException { - encStream = new BCPGInputStream(dataDecryptor.getInputStream(encIn)); + encStream = dataDecryptor.getInputStream(encIn); if (withIntegrityPacket) { diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyEncryptedData.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyEncryptedData.java index 5f375c7b84..6fb8276cbe 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyEncryptedData.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyEncryptedData.java @@ -3,7 +3,6 @@ import java.io.InputStream; import org.bouncycastle.bcpg.AEADEncDataPacket; -import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.bcpg.InputStreamPacket; import org.bouncycastle.bcpg.KeyIdentifier; import org.bouncycastle.bcpg.PublicKeyAlgorithmTags; @@ -201,93 +200,85 @@ private InputStream getDataStream( PGPSessionKey sessionKey) throws PGPException { - if (sessionKey.getAlgorithm() != SymmetricKeyAlgorithmTags.NULL) + if (sessionKey.getAlgorithm() == SymmetricKeyAlgorithmTags.NULL) { - try - { - // OpenPGP V5 style AEAD - if (encData instanceof AEADEncDataPacket) - { - AEADEncDataPacket aeadData = (AEADEncDataPacket)encData; - - if (aeadData.getAlgorithm() != sessionKey.getAlgorithm()) - { - throw new PGPException("session key and AEAD algorithm mismatch"); - } + return getInputStream(); + } - PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(aeadData, sessionKey); + try + { + InputStream encIn = getInputStream(); - BCPGInputStream encIn = encData.getInputStream(); + // OpenPGP V5 style AEAD + if (encData instanceof AEADEncDataPacket) + { + AEADEncDataPacket aeadData = (AEADEncDataPacket)encData; - encStream = new BCPGInputStream(dataDecryptor.getInputStream(encIn)); - } - else + if (aeadData.getAlgorithm() != sessionKey.getAlgorithm()) { + throw new PGPException("session key and AEAD algorithm mismatch"); + } - if (encData instanceof SymmetricEncIntegrityPacket) - { - SymmetricEncIntegrityPacket seipd = (SymmetricEncIntegrityPacket) encData; - // SEIPD v1 (OpenPGP v4) - if (seipd.getVersion() == SymmetricEncIntegrityPacket.VERSION_1) - { - PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(true, sessionKey.getAlgorithm(), sessionKey.getKey()); - - BCPGInputStream encIn = encData.getInputStream(); + PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(aeadData, sessionKey); - processSymmetricEncIntegrityPacketDataStream(true, dataDecryptor, encIn); - } - // SEIPD v2 (OpenPGP v6 AEAD) - else - { - PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(seipd, sessionKey); + encStream = dataDecryptor.getInputStream(encIn); + } + else + { - BCPGInputStream encIn = encData.getInputStream(); + if (encData instanceof SymmetricEncIntegrityPacket) + { + SymmetricEncIntegrityPacket seipd = (SymmetricEncIntegrityPacket) encData; + // SEIPD v1 (OpenPGP v4) + if (seipd.getVersion() == SymmetricEncIntegrityPacket.VERSION_1) + { + PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(true, sessionKey.getAlgorithm(), sessionKey.getKey()); - encStream = new BCPGInputStream(dataDecryptor.getInputStream(encIn)); - } + processSymmetricEncIntegrityPacketDataStream(true, dataDecryptor, encIn); } - // SED (Symmetrically Encrypted Data without Integrity Protection; Deprecated) + // SEIPD v2 (OpenPGP v6 AEAD) else { - PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(false, sessionKey.getAlgorithm(), sessionKey.getKey()); - - BCPGInputStream encIn = encData.getInputStream(); + PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(seipd, sessionKey); - processSymmetricEncIntegrityPacketDataStream(false, dataDecryptor, encIn); + encStream = dataDecryptor.getInputStream(encIn); } + } + // SED (Symmetrically Encrypted Data without Integrity Protection; Deprecated) + else + { + PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(false, sessionKey.getAlgorithm(), sessionKey.getKey()); - // - // some versions of PGP appear to produce 0 for the extra - // bytes rather than repeating the two previous bytes - // - /* - * Commented out in the light of the oracle attack. - if (iv[iv.length - 2] != (byte)v1 && v1 != 0) - { - throw new PGPDataValidationException("data check failed."); - } + processSymmetricEncIntegrityPacketDataStream(false, dataDecryptor, encIn); + } - if (iv[iv.length - 1] != (byte)v2 && v2 != 0) - { - throw new PGPDataValidationException("data check failed."); - } - */ + // + // some versions of PGP appear to produce 0 for the extra + // bytes rather than repeating the two previous bytes + // + /* + * Commented out in the light of the oracle attack. + if (iv[iv.length - 2] != (byte)v1 && v1 != 0) + { + throw new PGPDataValidationException("data check failed."); } - return encStream; - } - catch (PGPException e) - { - throw e; - } - catch (Exception e) - { - throw new PGPException("Exception starting decryption", e); + if (iv[iv.length - 1] != (byte)v2 && v2 != 0) + { + throw new PGPDataValidationException("data check failed."); + } + */ } + + return encStream; } - else + catch (PGPException e) + { + throw e; + } + catch (Exception e) { - return encData.getInputStream(); + throw new PGPException("Exception starting decryption", e); } } diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java index 4289a773d7..3ce16b8255 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSecretKey.java @@ -552,119 +552,116 @@ public Iterator getUserAttributes() return pub.getUserAttributes(); } - private byte[] extractKeyData( - PBESecretKeyDecryptor decryptorFactory) - throws PGPException + private byte[] extractKeyData(PBESecretKeyDecryptor decryptorFactory) throws PGPException { byte[] encData = secret.getSecretKeyData(); - byte[] data = null; - if (secret.getEncAlgorithm() != SymmetricKeyAlgorithmTags.NULL) + if (secret.getEncAlgorithm() == SymmetricKeyAlgorithmTags.NULL) { - try - { - byte[] key = decryptorFactory.makeKeyFromPassPhrase(secret.getEncAlgorithm(), secret.getS2K()); - if (secret.getPublicKeyPacket().getVersion() >= PublicKeyPacket.VERSION_4) - { - if (secret.getS2KUsage() == SecretKeyPacket.USAGE_AEAD) - { - // privKey := AEAD(HKDF(S2K(passphrase), info), secrets, packetprefix) - return decryptorFactory.recoverKeyData( - secret.getEncAlgorithm(), - secret.getAeadAlgorithm(), - key, // s2k output = ikm for hkdf - secret.getIV(), // iv = aead nonce - secret.getPacketTag(), - secret.getPublicKeyPacket().getVersion(), - secret.getSecretKeyData(), - secret.getPublicKeyPacket().getEncodedContents()); - } - else - { - data = decryptorFactory.recoverKeyData(secret.getEncAlgorithm(), key, secret.getIV(), encData, 0, encData.length); + return encData; + } - boolean useSHA1 = secret.getS2KUsage() == SecretKeyPacket.USAGE_SHA1; - byte[] check = checksum(useSHA1 ? decryptorFactory.getChecksumCalculator(HashAlgorithmTags.SHA1) : null, data, (useSHA1) ? data.length - 20 : data.length - 2); + try + { + byte[] key = decryptorFactory.makeKeyFromPassPhrase(secret.getEncAlgorithm(), secret.getS2K()); + byte[] data; - if (!Arrays.constantTimeAreEqual(check.length, check, 0, data, data.length - check.length)) - { - throw new PGPException("checksum mismatch at in checksum of " + check.length + " bytes"); - } - } + if (secret.getPublicKeyPacket().getVersion() >= PublicKeyPacket.VERSION_4) + { + if (secret.getS2KUsage() == SecretKeyPacket.USAGE_AEAD) + { + // privKey := AEAD(HKDF(S2K(passphrase), info), secrets, packetprefix) + return decryptorFactory.recoverKeyData( + secret.getEncAlgorithm(), + secret.getAeadAlgorithm(), + key, // s2k output = ikm for hkdf + secret.getIV(), // iv = aead nonce + secret.getPacketTag(), + secret.getPublicKeyPacket().getVersion(), + secret.getSecretKeyData(), + secret.getPublicKeyPacket().getEncodedContents()); } - else // version 2 or 3, RSA only. + else { + data = decryptorFactory.recoverKeyData(secret.getEncAlgorithm(), key, secret.getIV(), encData, 0, encData.length); - data = new byte[encData.length]; - - byte[] iv = new byte[secret.getIV().length]; + boolean useSHA1 = secret.getS2KUsage() == SecretKeyPacket.USAGE_SHA1; + byte[] check = checksum(useSHA1 ? decryptorFactory.getChecksumCalculator(HashAlgorithmTags.SHA1) : null, data, (useSHA1) ? data.length - 20 : data.length - 2); - System.arraycopy(secret.getIV(), 0, iv, 0, iv.length); - - // - // read in the four numbers - // - int pos = 0; - - for (int i = 0; i != 4; i++) + if (!Arrays.constantTimeAreEqual(check.length, check, 0, data, data.length - check.length)) { - int encLen = ((((encData[pos] & 0xff) << 8) | (encData[pos + 1] & 0xff)) + 7) / 8; + throw new PGPException("checksum mismatch in checksum of " + check.length + " bytes"); + } + } + } + else // version 2 or 3, RSA only. + { - data[pos] = encData[pos]; - data[pos + 1] = encData[pos + 1]; + data = new byte[encData.length]; - if (encLen > (encData.length - (pos + 2))) - { - throw new PGPException("out of range encLen found in encData"); - } - byte[] tmp = decryptorFactory.recoverKeyData(secret.getEncAlgorithm(), key, iv, encData, pos + 2, encLen); - System.arraycopy(tmp, 0, data, pos + 2, tmp.length); - pos += 2 + encLen; + byte[] iv = new byte[secret.getIV().length]; - if (i != 3) - { - System.arraycopy(encData, pos - iv.length, iv, 0, iv.length); - } - } + System.arraycopy(secret.getIV(), 0, iv, 0, iv.length); - // - // verify and copy checksum - // + // + // read in the four numbers + // + int pos = 0; + + for (int i = 0; i != 4; i++) + { + int encLen = ((((encData[pos] & 0xff) << 8) | (encData[pos + 1] & 0xff)) + 7) / 8; data[pos] = encData[pos]; data[pos + 1] = encData[pos + 1]; - int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff); - int calcCs = 0; - for (int j = 0; j < data.length - 2; j++) + if (encLen > (encData.length - (pos + 2))) { - calcCs += data[j] & 0xff; + throw new PGPException("out of range encLen found in encData"); } + byte[] tmp = decryptorFactory.recoverKeyData(secret.getEncAlgorithm(), key, iv, encData, pos + 2, encLen); + System.arraycopy(tmp, 0, data, pos + 2, tmp.length); + pos += 2 + encLen; - calcCs &= 0xffff; - if (calcCs != cs) + if (i != 3) { - throw new PGPException("checksum mismatch: passphrase wrong, expected " - + Integer.toHexString(cs) - + " found " + Integer.toHexString(calcCs)); + System.arraycopy(encData, pos - iv.length, iv, 0, iv.length); } } + + // + // verify and copy checksum + // + + data[pos] = encData[pos]; + data[pos + 1] = encData[pos + 1]; + + int cs = ((encData[pos] << 8) & 0xff00) | (encData[pos + 1] & 0xff); + int calcCs = 0; + for (int j = 0; j < data.length - 2; j++) + { + calcCs += data[j] & 0xff; + } + + calcCs &= 0xffff; + if (calcCs != cs) + { + throw new PGPException("checksum mismatch: passphrase wrong, expected " + + Integer.toHexString(cs) + + " found " + Integer.toHexString(calcCs)); + } } - catch (PGPException e) - { - throw e; - } - catch (Exception e) - { - throw new PGPException("Exception decrypting key", e); - } + + return data; } - else + catch (PGPException e) { - data = encData; + throw e; + } + catch (Exception e) + { + throw new PGPException("Exception decrypting key", e); } - - return data; } /** diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPSymmetricKeyEncryptedData.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPSymmetricKeyEncryptedData.java index ac6f3d22b7..db93abc939 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPSymmetricKeyEncryptedData.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPSymmetricKeyEncryptedData.java @@ -3,7 +3,6 @@ import java.io.InputStream; import org.bouncycastle.bcpg.AEADEncDataPacket; -import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.bcpg.InputStreamPacket; import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket; import org.bouncycastle.bcpg.UnsupportedPacketVersionException; @@ -31,11 +30,10 @@ protected InputStream createDecryptionStream(PGPDataDecryptorFactory dataDecrypt throw new PGPException("session key and AEAD algorithm mismatch"); } - PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor( - aeadData, sessionKey); - BCPGInputStream encIn = encData.getInputStream(); + PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(aeadData, sessionKey); + InputStream encIn = getInputStream(); - return new BCPGInputStream(dataDecryptor.getInputStream(encIn)); + return dataDecryptor.getInputStream(encIn); } else if (encData instanceof SymmetricEncIntegrityPacket) { @@ -44,7 +42,8 @@ else if (encData instanceof SymmetricEncIntegrityPacket) // OpenPGP v4 (SEIPD v1 with integrity protection) if (seipd.getVersion() == SymmetricEncIntegrityPacket.VERSION_1) { - PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(true, sessionKey.getAlgorithm(), sessionKey.getKey()); + PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(true, + sessionKey.getAlgorithm(), sessionKey.getKey()); return getDataStream(true, dataDecryptor); } @@ -52,7 +51,7 @@ else if (encData instanceof SymmetricEncIntegrityPacket) else if (seipd.getVersion() == SymmetricEncIntegrityPacket.VERSION_2) { PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(seipd, sessionKey); - return new BCPGInputStream(dataDecryptor.getInputStream(encData.getInputStream())); + return dataDecryptor.getInputStream(getInputStream()); } // Unsupported @@ -76,7 +75,7 @@ private InputStream getDataStream( { try { - BCPGInputStream encIn = encData.getInputStream(); + InputStream encIn = getInputStream(); encIn.mark(dataDecryptor.getBlockSize() + 2); // iv + 2 octets checksum if (processSymmetricEncIntegrityPacketDataStream(withIntegrityPacket, dataDecryptor, encIn)) { diff --git a/pg/src/main/java/org/bouncycastle/openpgp/Util.java b/pg/src/main/java/org/bouncycastle/openpgp/Util.java index af4a6c68e9..7263a3cb9c 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/Util.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/Util.java @@ -12,27 +12,29 @@ class Util static BCPGInputStream createBCPGInputStream(InputStream pgIn, int tag1) throws IOException { - BCPGInputStream bcIn = new BCPGInputStream(pgIn); + BCPGInputStream bcIn = BCPGInputStream.wrap(pgIn); + int nextTag = bcIn.nextPacketTag(); - if (bcIn.nextPacketTag() == tag1) + if (nextTag == tag1) { return bcIn; } - throw new IOException("unexpected tag " + bcIn.nextPacketTag() + " encountered"); + throw new IOException("unexpected tag " + nextTag + " encountered"); } static BCPGInputStream createBCPGInputStream(InputStream pgIn, int tag1, int tag2) throws IOException { - BCPGInputStream bcIn = new BCPGInputStream(pgIn); + BCPGInputStream bcIn = BCPGInputStream.wrap(pgIn); + int nextTag = bcIn.nextPacketTag(); - if (bcIn.nextPacketTag() == tag1 || bcIn.nextPacketTag() == tag2) + if (nextTag == tag1 || nextTag == tag2) { return bcIn; } - throw new IOException("unexpected tag " + bcIn.nextPacketTag() + " encountered"); + throw new IOException("unexpected tag " + nextTag + " encountered"); } static void encodePGPSignatures(OutputStream stream, List sigs, boolean forTransfer) From 00c066466aa71b07f937dfa6b57565e5b227ceb3 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 21 Jul 2025 17:16:29 +1000 Subject: [PATCH 461/890] moved provider version to 1.0.22. --- .../bouncycastle/jsse/provider/BouncyCastleJsseProvider.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/jsse/provider/BouncyCastleJsseProvider.java b/tls/src/main/java/org/bouncycastle/jsse/provider/BouncyCastleJsseProvider.java index b466a3c397..cb770a57f6 100644 --- a/tls/src/main/java/org/bouncycastle/jsse/provider/BouncyCastleJsseProvider.java +++ b/tls/src/main/java/org/bouncycastle/jsse/provider/BouncyCastleJsseProvider.java @@ -26,8 +26,8 @@ public class BouncyCastleJsseProvider private static final String JSSE_CONFIG_PROPERTY = "org.bouncycastle.jsse.config"; - private static final double PROVIDER_VERSION = 1.0021; - private static final String PROVIDER_INFO = "Bouncy Castle JSSE Provider Version 1.0.21"; + private static final double PROVIDER_VERSION = 1.0022; + private static final String PROVIDER_INFO = "Bouncy Castle JSSE Provider Version 1.0.22"; private final Map serviceMap = new ConcurrentHashMap(); private final Map creatorMap = new HashMap(); From 667251e47642ef801c972f0ce41c25708de90738 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 21 Jul 2025 19:35:33 +1000 Subject: [PATCH 462/890] rolled back use of shake256 in prehash. --- .../pqc/crypto/mldsa/HashMLDSASigner.java | 72 ++++++++++++------- .../pqc/crypto/test/MLDSATest.java | 7 +- 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java index 00c06a4a79..71ebaa56fc 100644 --- a/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java +++ b/core/src/main/java/org/bouncycastle/pqc/crypto/mldsa/HashMLDSASigner.java @@ -4,11 +4,14 @@ import java.security.SecureRandom; import org.bouncycastle.asn1.ASN1Encoding; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.crypto.CipherParameters; import org.bouncycastle.crypto.CryptoException; import org.bouncycastle.crypto.DataLengthException; import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.Signer; +import org.bouncycastle.crypto.digests.SHA512Digest; +import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.crypto.params.ParametersWithContext; import org.bouncycastle.crypto.params.ParametersWithRandom; import org.bouncycastle.pqc.crypto.DigestUtils; @@ -78,17 +81,23 @@ public void init(boolean forSigning, CipherParameters param) engine.initVerify(pubKey.rho, pubKey.t1, true, ctx); } - digest = engine.shake256Digest; - byte[] digestOIDEncoding; + + initDigest(parameters); + } + + private void initDigest(MLDSAParameters parameters) + { + digest = createDigest(parameters); + + ASN1ObjectIdentifier oid = DigestUtils.getDigestOid(digest.getAlgorithmName()); try { - digestOIDEncoding = DigestUtils.getDigestOid(digest.getAlgorithmName()).getEncoded(ASN1Encoding.DER); + digestOIDEncoding = oid.getEncoded(ASN1Encoding.DER); } catch (IOException e) { throw new IllegalStateException("oid encoding failed: " + e.getMessage()); } - digest.update(digestOIDEncoding, 0, digestOIDEncoding.length); } public void update(byte b) @@ -101,22 +110,25 @@ public void update(byte[] in, int off, int len) digest.update(in, off, len); } - public byte[] generateSignature() - throws CryptoException, DataLengthException + public byte[] generateSignature() throws CryptoException, DataLengthException { + SHAKEDigest msgDigest = finishPreHash(); + byte[] rnd = new byte[MLDSAEngine.RndBytes]; if (random != null) { random.nextBytes(rnd); } - byte[] mu = engine.generateMu(engine.shake256Digest); - return engine.generateSignature(mu, engine.getShake256Digest(), privKey.rho, privKey.k, privKey.t0, privKey.s1, privKey.s2, rnd); + byte[] mu = engine.generateMu(msgDigest); + + return engine.generateSignature(mu, msgDigest, privKey.rho, privKey.k, privKey.t0, privKey.s1, privKey.s2, rnd); } public boolean verifySignature(byte[] signature) { - byte[] mu = engine.generateMu(engine.shake256Digest); - return engine.verifyInternalMuSignature(mu, signature, signature.length, engine.getShake256Digest(), pubKey.rho, pubKey.t1); + SHAKEDigest msgDigest = finishPreHash(); + + return engine.verifyInternal(signature, signature.length, msgDigest, pubKey.rho, pubKey.t1); } /** @@ -127,8 +139,20 @@ public void reset() digest.reset(); } + private SHAKEDigest finishPreHash() + { + byte[] hash = new byte[digest.getDigestSize()]; + digest.doFinal(hash, 0); + + SHAKEDigest msgDigest = engine.getShake256Digest(); + // TODO It should be possible to include digestOIDEncoding in the memo'ed digest + msgDigest.update(digestOIDEncoding, 0, digestOIDEncoding.length); + msgDigest.update(hash, 0, hash.length); + return msgDigest; + } + // TODO: these are probably no longer correct and also need to be marked as protected -// protected byte[] internalGenerateSignature(byte[] message, SecureRandom random) +// protected byte[] internalGenerateSignature(byte[] message, byte[] random) // { // MLDSAEngine engine = privKey.getParameters().getEngine(random); // @@ -142,19 +166,15 @@ public void reset() // return engine.verifyInternal(signature, signature.length, message, message.length, pubKey.rho, pubKey.t1); // } -// private static Digest createDigest(MLDSAParameters parameters) -// { - //TODO: MLDSA44 may use SHA2-256, SHA3-256, SHAKE128 - // MLDSA65 may use SHA3-384, SHA2-512 - // MLDSA44/65/87 may use SHA2-512, SHA3-512, SHAKE256 - -// switch (parameters.getType()) -// { -// case MLDSAParameters.TYPE_PURE: -// case MLDSAParameters.TYPE_SHA2_512: -// return new SHAKEDigest(256); -// default: -// throw new IllegalArgumentException("unknown parameters type"); -// } -// } + private static Digest createDigest(MLDSAParameters parameters) + { + switch (parameters.getType()) + { + case MLDSAParameters.TYPE_PURE: + case MLDSAParameters.TYPE_SHA2_512: + return new SHA512Digest(); + default: + throw new IllegalArgumentException("unknown parameters type"); + } + } } diff --git a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java index 1bf6dcaa85..ee97e94e36 100644 --- a/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java +++ b/core/src/test/java/org/bouncycastle/pqc/crypto/test/MLDSATest.java @@ -475,9 +475,10 @@ public void testMLDSARejection() rejectionExternalMuTest(MLDSAParameters.ml_dsa_44, "dilithium_external_mu_rejection_vectors_44.h"); rejectionExternalMuTest(MLDSAParameters.ml_dsa_65, "dilithium_external_mu_rejection_vectors_65.h"); rejectionExternalMuTest(MLDSAParameters.ml_dsa_87, "dilithium_external_mu_rejection_vectors_87.h"); - rejectionPrehashTest(MLDSAParameters.ml_dsa_44, "dilithium_prehash_rejection_vectors_44.h"); - rejectionPrehashTest(MLDSAParameters.ml_dsa_65, "dilithium_prehash_rejection_vectors_65.h"); - rejectionPrehashTest(MLDSAParameters.ml_dsa_87, "dilithium_prehash_rejection_vectors_87.h"); + // TODO: rejection vectors based on non-compliant hash - SHA-512 is currently the only one accepted +// rejectionPrehashTest(MLDSAParameters.ml_dsa_44, "dilithium_prehash_rejection_vectors_44.h"); +// rejectionPrehashTest(MLDSAParameters.ml_dsa_65, "dilithium_prehash_rejection_vectors_65.h"); +// rejectionPrehashTest(MLDSAParameters.ml_dsa_87, "dilithium_prehash_rejection_vectors_87.h"); rejectionTest(MLDSAParameters.ml_dsa_44, "dilithium_pure_rejection_vectors_44.h"); rejectionTest(MLDSAParameters.ml_dsa_65, "dilithium_pure_rejection_vectors_65.h"); rejectionTest(MLDSAParameters.ml_dsa_87, "dilithium_pure_rejection_vectors_87.h"); From 1119427354c2ac1b41c677e9ece40d49d63735b0 Mon Sep 17 00:00:00 2001 From: David Hook Date: Tue, 22 Jul 2025 11:16:00 +1000 Subject: [PATCH 463/890] added ml-dsa with SHA-512 test. --- .../org/bouncycastle/tsp/test/PQCTSPTest.java | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/pkix/src/test/java/org/bouncycastle/tsp/test/PQCTSPTest.java b/pkix/src/test/java/org/bouncycastle/tsp/test/PQCTSPTest.java index 7b208598b9..2579f2a0d3 100644 --- a/pkix/src/test/java/org/bouncycastle/tsp/test/PQCTSPTest.java +++ b/pkix/src/test/java/org/bouncycastle/tsp/test/PQCTSPTest.java @@ -194,4 +194,79 @@ public void testSPHINCSPlus() assertNotNull("no signingCertificate attribute found", table.get(PKCSObjectIdentifiers.id_aa_signingCertificate)); } + + public void testMLDSA() + throws Exception + { + // + // set up the keys + // + PrivateKey privKey; + PublicKey pubKey; + + try + { + KeyPairGenerator g = KeyPairGenerator.getInstance("ML-DSA", BC); + + KeyPair p = g.generateKeyPair(); + + privKey = p.getPrivate(); + pubKey = p.getPublic(); + } + catch (Exception e) + { + fail("error setting up keys - " + e); + return; + } + + // + // extensions + // + + // + // create the certificate - version 1 + // + + ContentSigner sigGen = new JcaContentSignerBuilder("ML-DSA") + .setProvider(BC).build(privKey); + JcaX509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder( + new X500Name("CN=Test"), + BigInteger.valueOf(1), + new Date(System.currentTimeMillis() - 50000), + new Date(System.currentTimeMillis() + 50000), + new X500Name("CN=Test"), + pubKey); + + certGen.addExtension(Extension.extendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeId.id_kp_timeStamping)); + + X509Certificate cert = new JcaX509CertificateConverter() + .setProvider("BC").getCertificate(certGen.build(sigGen)); + + ContentSigner signer = new JcaContentSignerBuilder("ML-DSA").setProvider(BC).build(privKey); + + TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator( + new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()) + .setContentDigest(new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512)) + .build(signer, cert), new SHA1DigestCalculator(), new ASN1ObjectIdentifier("1.2")); + + // tsTokenGen.addCertificates(certs); + + TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator(); + TimeStampRequest request = reqGen.generate(TSPAlgorithms.SHA3_256, new byte[32], BigInteger.valueOf(100)); + + TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED); + + TimeStampResponse tsResp = tsRespGen.generate(request, new BigInteger("23"), new Date()); + + tsResp = new TimeStampResponse(tsResp.getEncoded()); + + TimeStampToken tsToken = tsResp.getTimeStampToken(); + + tsToken.validate(new JcaSignerInfoVerifierBuilder(new JcaDigestCalculatorProviderBuilder().build()) + .setProvider(BC).build(cert)); + + AttributeTable table = tsToken.getSignedAttributes(); + + assertNotNull("no signingCertificate attribute found", table.get(PKCSObjectIdentifiers.id_aa_signingCertificate)); + } } From 0d55ad55633ab155642e5098c40b9be4acd8d232 Mon Sep 17 00:00:00 2001 From: David Hook Date: Wed, 23 Jul 2025 18:46:23 +1000 Subject: [PATCH 464/890] added SavableDigest support to SHA-3 --- .../crypto/digests/SHA3Digest.java | 83 ++++++++++++++++++- .../crypto/test/SHA3DigestTest.java | 44 +++++++++- 2 files changed, 122 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java index 8c93fb0282..526a468d77 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java @@ -1,7 +1,10 @@ package org.bouncycastle.crypto.digests; - import org.bouncycastle.crypto.CryptoServicePurpose; +import org.bouncycastle.crypto.CryptoServicesRegistrar; +import org.bouncycastle.crypto.SavableDigest; +import org.bouncycastle.util.Memoable; +import org.bouncycastle.util.Pack; /** * implementation of SHA-3 based on following KeccakNISTInterface.c from https://keccak.noekeon.org/ @@ -10,6 +13,7 @@ */ public class SHA3Digest extends KeccakDigest + implements SavableDigest { private static int checkBitLength(int bitLength) { @@ -45,11 +49,51 @@ public SHA3Digest(int bitLength, CryptoServicePurpose purpose) super(checkBitLength(bitLength), purpose); } + public SHA3Digest(byte[] encodedState) + { + super(getCryptoServicePurpose(encodedState[encodedState.length - 1])); + + Pack.bigEndianToLong(encodedState, 0, state, 0, state.length); + int encOff = state.length * 8; + System.arraycopy(encodedState, encOff, dataQueue, 0, dataQueue.length); + encOff += dataQueue.length; + rate = Pack.bigEndianToInt(encodedState, encOff); + encOff += 4; + bitsInQueue = Pack.bigEndianToInt(encodedState, encOff); + encOff += 4; + fixedOutputLength = Pack.bigEndianToInt(encodedState, encOff); + encOff += 4; + squeezing = encodedState[encOff] != 0; + } + + private static CryptoServicePurpose getCryptoServicePurpose(byte b) + { + CryptoServicePurpose[] values = CryptoServicePurpose.values(); + return values[b]; + } + public SHA3Digest(SHA3Digest source) { super(source); } + private void copyIn(SHA3Digest source) + { + if (this.purpose != source.purpose) + { + throw new IllegalArgumentException("attempt to copy digest of different purpose"); + } + + System.arraycopy(source.state, 0, this.state, 0, source.state.length); + System.arraycopy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.length); + this.rate = source.rate; + this.bitsInQueue = source.bitsInQueue; + this.fixedOutputLength = source.fixedOutputLength; + this.squeezing = source.squeezing; + + CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties()); + } + public String getAlgorithmName() { return "SHA3-" + fixedOutputLength; @@ -84,4 +128,41 @@ protected int doFinal(byte[] out, int outOff, byte partialByte, int partialBits) return super.doFinal(out, outOff, (byte)finalInput, finalBits); } + + public byte[] getEncodedState() + { + byte[] encState = new byte[state.length * 8 + dataQueue.length + 12 + 2]; + + for (int i = 0; i != state.length; i++) + { + Pack.longToBigEndian(state[i], encState, i * 8); + } + + int sOff = state.length * 8; + System.arraycopy(dataQueue, 0, encState, sOff, dataQueue.length); + + sOff += dataQueue.length; + Pack.intToBigEndian(rate, encState, sOff); + sOff += 4; + Pack.intToBigEndian(bitsInQueue, encState, sOff); + sOff += 4; + Pack.intToBigEndian(fixedOutputLength, encState, sOff); + sOff += 4; + encState[sOff++] = squeezing ? (byte)1 : (byte)0; + encState[sOff] = (byte)purpose.ordinal(); + + return encState; + } + + public Memoable copy() + { + return new SHA3Digest(this); + } + + public void reset(Memoable other) + { + SHA3Digest d = (SHA3Digest)other; + + copyIn(d); + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SHA3DigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SHA3DigestTest.java index be30e23830..ca227916ff 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/SHA3DigestTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SHA3DigestTest.java @@ -7,18 +7,34 @@ import java.util.ArrayList; import java.util.List; +import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHA3Digest; import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; -import org.bouncycastle.util.test.SimpleTest; /** * SHA3 Digest Test */ public class SHA3DigestTest - extends SimpleTest + extends DigestTest { + private static String[] messages = + { + "", + "a", + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + }; + + private static String[] digests = + { + "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a", + "80084bf2fba02475726feb2cab2d8215eab14bc6bdd8bfb2c8151257032ecd8b", + "3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532", + "41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376" + }; + static class MySHA3Digest extends SHA3Digest { MySHA3Digest(int bitLength) @@ -34,6 +50,7 @@ int myDoFinal(byte[] out, int outOff, byte partialByte, int partialBits) SHA3DigestTest() { + super(new SHA3Digest(), messages, digests); } public String getName() @@ -41,9 +58,28 @@ public String getName() return "SHA-3"; } - public void performTest() throws Exception + public void performTest() + { + super.performTest(); + + try + { + testVectors(); + } + catch (Exception e) + { + throw new IllegalStateException(e.toString(), e); + } + } + + protected Digest cloneDigest(Digest digest) + { + return new SHA3Digest((SHA3Digest)digest); + } + + protected Digest cloneDigest(byte[] encodedState) { - testVectors(); + return new SHA3Digest(encodedState); } public void testVectors() throws Exception From 160db88074cbbb2cc3a779171a9c5c32cef47ec4 Mon Sep 17 00:00:00 2001 From: David Hook Date: Wed, 23 Jul 2025 21:14:14 +1000 Subject: [PATCH 465/890] added SavableDigest support to CSHAKE, SHAKE minor changes to support in SHA3 to accomodate. --- .../crypto/digests/CSHAKEDigest.java | 59 ++++++++++++++++- .../crypto/digests/KeccakDigest.java | 66 +++++++++++++++++++ .../crypto/digests/SHA3Digest.java | 59 +---------------- .../crypto/digests/SHAKEDigest.java | 30 ++++++++- .../bouncycastle/crypto/test/CSHAKETest.java | 38 ++++++++++- .../crypto/test/SHAKEDigestTest.java | 44 +++++++++++-- 6 files changed, 231 insertions(+), 65 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/CSHAKEDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/CSHAKEDigest.java index 387bc8f732..e3bc609dc9 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/CSHAKEDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/CSHAKEDigest.java @@ -2,6 +2,7 @@ import org.bouncycastle.crypto.CryptoServicePurpose; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Memoable; /** * Customizable SHAKE function. @@ -10,7 +11,8 @@ public class CSHAKEDigest extends SHAKEDigest { private static final byte[] padding = new byte[100]; - private final byte[] diff; + + private byte[] diff; /** * Base constructor. @@ -54,6 +56,29 @@ public CSHAKEDigest(CSHAKEDigest source) this.diff = Arrays.clone(source.diff); } + public CSHAKEDigest(byte[] encodedState) + { + super(encodedState); + + int sha3StateLength = state.length * 8 + dataQueue.length + 12 + 2; + if (encodedState.length != sha3StateLength) + { + this.diff = new byte[encodedState.length - sha3StateLength]; + System.arraycopy(encodedState, sha3StateLength, diff, 0, diff.length); + } + else + { + this.diff = null; + } + } + + private void copyIn(CSHAKEDigest source) + { + super.copyIn(source); + + this.diff = Arrays.clone(source.diff); + } + // bytepad in SP 800-185 private void diffPadAndAbsorb() { @@ -120,4 +145,36 @@ public void reset() diffPadAndAbsorb(); } } + + public byte[] getEncodedState() + { + int sha3StateLength = state.length * 8 + dataQueue.length + 12 + 2; + byte[] encState; + + if (diff == null) + { + encState = new byte[sha3StateLength]; + super.getEncodedState(encState); + } + else + { + encState = new byte[sha3StateLength + diff.length]; + super.getEncodedState(encState); + System.arraycopy(diff, 0, encState, sha3StateLength, diff.length); + } + + return encState; + } + + public Memoable copy() + { + return new CSHAKEDigest(this); + } + + public void reset(Memoable other) + { + CSHAKEDigest d = (CSHAKEDigest)other; + + copyIn(d); + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java index f53552e550..d50802a188 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/KeccakDigest.java @@ -66,6 +66,47 @@ public KeccakDigest(KeccakDigest source) CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties()); } + protected KeccakDigest(byte[] encodedState) + { + purpose = getCryptoServicePurpose(encodedState[0]); + + int encOff = 1; + Pack.bigEndianToLong(encodedState, encOff, state, 0, state.length); + encOff += state.length * 8; + System.arraycopy(encodedState, encOff, dataQueue, 0, dataQueue.length); + encOff += dataQueue.length; + rate = Pack.bigEndianToInt(encodedState, encOff); + encOff += 4; + bitsInQueue = Pack.bigEndianToInt(encodedState, encOff); + encOff += 4; + fixedOutputLength = Pack.bigEndianToInt(encodedState, encOff); + encOff += 4; + squeezing = encodedState[encOff] != 0; + } + + private static CryptoServicePurpose getCryptoServicePurpose(byte b) + { + CryptoServicePurpose[] values = CryptoServicePurpose.values(); + return values[b]; + } + + protected void copyIn(KeccakDigest source) + { + if (this.purpose != source.purpose) + { + throw new IllegalArgumentException("attempt to copy digest of different purpose"); + } + + System.arraycopy(source.state, 0, this.state, 0, source.state.length); + System.arraycopy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.length); + this.rate = source.rate; + this.bitsInQueue = source.bitsInQueue; + this.fixedOutputLength = source.fixedOutputLength; + this.squeezing = source.squeezing; + + CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties()); + } + public String getAlgorithmName() { return "Keccak-" + fixedOutputLength; @@ -440,4 +481,29 @@ protected CryptoServiceProperties cryptoServiceProperties() { return Utils.getDefaultProperties(this, getDigestSize() * 8, purpose); } + + protected byte[] getEncodedState(byte[] encState) + { + encState[0] = (byte)purpose.ordinal(); + + int sOff = 1; + for (int i = 0; i != state.length; i++) + { + Pack.longToBigEndian(state[i], encState, sOff); + sOff += 8; + } + + System.arraycopy(dataQueue, 0, encState, sOff, dataQueue.length); + + sOff += dataQueue.length; + Pack.intToBigEndian(rate, encState, sOff); + sOff += 4; + Pack.intToBigEndian(bitsInQueue, encState, sOff); + sOff += 4; + Pack.intToBigEndian(fixedOutputLength, encState, sOff); + sOff += 4; + encState[sOff] = squeezing ? (byte)1 : (byte)0; + + return encState; + } } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java index 526a468d77..4dcf41b426 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java @@ -1,10 +1,8 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.CryptoServicePurpose; -import org.bouncycastle.crypto.CryptoServicesRegistrar; import org.bouncycastle.crypto.SavableDigest; import org.bouncycastle.util.Memoable; -import org.bouncycastle.util.Pack; /** * implementation of SHA-3 based on following KeccakNISTInterface.c from https://keccak.noekeon.org/ @@ -51,25 +49,7 @@ public SHA3Digest(int bitLength, CryptoServicePurpose purpose) public SHA3Digest(byte[] encodedState) { - super(getCryptoServicePurpose(encodedState[encodedState.length - 1])); - - Pack.bigEndianToLong(encodedState, 0, state, 0, state.length); - int encOff = state.length * 8; - System.arraycopy(encodedState, encOff, dataQueue, 0, dataQueue.length); - encOff += dataQueue.length; - rate = Pack.bigEndianToInt(encodedState, encOff); - encOff += 4; - bitsInQueue = Pack.bigEndianToInt(encodedState, encOff); - encOff += 4; - fixedOutputLength = Pack.bigEndianToInt(encodedState, encOff); - encOff += 4; - squeezing = encodedState[encOff] != 0; - } - - private static CryptoServicePurpose getCryptoServicePurpose(byte b) - { - CryptoServicePurpose[] values = CryptoServicePurpose.values(); - return values[b]; + super(encodedState); } public SHA3Digest(SHA3Digest source) @@ -77,23 +57,6 @@ public SHA3Digest(SHA3Digest source) super(source); } - private void copyIn(SHA3Digest source) - { - if (this.purpose != source.purpose) - { - throw new IllegalArgumentException("attempt to copy digest of different purpose"); - } - - System.arraycopy(source.state, 0, this.state, 0, source.state.length); - System.arraycopy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.length); - this.rate = source.rate; - this.bitsInQueue = source.bitsInQueue; - this.fixedOutputLength = source.fixedOutputLength; - this.squeezing = source.squeezing; - - CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties()); - } - public String getAlgorithmName() { return "SHA3-" + fixedOutputLength; @@ -133,24 +96,8 @@ public byte[] getEncodedState() { byte[] encState = new byte[state.length * 8 + dataQueue.length + 12 + 2]; - for (int i = 0; i != state.length; i++) - { - Pack.longToBigEndian(state[i], encState, i * 8); - } - - int sOff = state.length * 8; - System.arraycopy(dataQueue, 0, encState, sOff, dataQueue.length); - - sOff += dataQueue.length; - Pack.intToBigEndian(rate, encState, sOff); - sOff += 4; - Pack.intToBigEndian(bitsInQueue, encState, sOff); - sOff += 4; - Pack.intToBigEndian(fixedOutputLength, encState, sOff); - sOff += 4; - encState[sOff++] = squeezing ? (byte)1 : (byte)0; - encState[sOff] = (byte)purpose.ordinal(); - + super.getEncodedState(encState); + return encState; } diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java index 4b30c0e150..5acbb87654 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/SHAKEDigest.java @@ -2,7 +2,9 @@ import org.bouncycastle.crypto.CryptoServiceProperties; import org.bouncycastle.crypto.CryptoServicePurpose; +import org.bouncycastle.crypto.SavableDigest; import org.bouncycastle.crypto.Xof; +import org.bouncycastle.util.Memoable; /** @@ -12,7 +14,7 @@ */ public class SHAKEDigest extends KeccakDigest - implements Xof + implements Xof, SavableDigest { private static int checkBitLength(int bitStrength) { @@ -67,6 +69,11 @@ public SHAKEDigest(SHAKEDigest source) super(source); } + public SHAKEDigest(byte[] encodedState) + { + super(encodedState); + } + public String getAlgorithmName() { return "SHAKE" + fixedOutputLength; @@ -147,4 +154,25 @@ protected CryptoServiceProperties cryptoServiceProperties() { return Utils.getDefaultProperties(this, purpose); } + + public byte[] getEncodedState() + { + byte[] encState = new byte[state.length * 8 + dataQueue.length + 12 + 2]; + + super.getEncodedState(encState); + + return encState; + } + + public Memoable copy() + { + return new SHAKEDigest(this); + } + + public void reset(Memoable other) + { + SHAKEDigest d = (SHAKEDigest)other; + + copyIn(d); + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/CSHAKETest.java b/core/src/test/java/org/bouncycastle/crypto/test/CSHAKETest.java index f86e87f44d..743515920e 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/CSHAKETest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/CSHAKETest.java @@ -1,11 +1,11 @@ package org.bouncycastle.crypto.test; +import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.CSHAKEDigest; import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; -import org.bouncycastle.util.test.SimpleTest; /** * CSHAKE test vectors from: @@ -13,16 +13,48 @@ * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/cSHAKE_samples.pdf */ public class CSHAKETest - extends SimpleTest + extends DigestTest { + private static String[] messages = + { + "", + "a", + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + }; + + private static String[] digests = + { + "28a0e58d342a45ef1522d69cd41748beee29c188364df18c84ed4ab8bed0cc85", + "cb0a67f6ff4a3b64497a757a85bda4a275cf11970ff226abd2bf2bbcba5890ea", + "346c136daea11c436d8d9e668e08888bd4e341dae05da4cb8773f74402c5bdbc", + "64602dc88c880bdfb6d0c9163a72b2e3653ab6114e4f4e25d7aaf5b8d441e36f" + }; + + public CSHAKETest() + { + super(new CSHAKEDigest(128, new byte[1], new byte[1]), messages, digests); + } + public String getName() { return "CSHAKE"; } + protected Digest cloneDigest(Digest digest) + { + return new CSHAKEDigest((CSHAKEDigest)digest); + } + + protected Digest cloneDigest(byte[] encodedState) + { + return new CSHAKEDigest(encodedState); + } + public void performTest() - throws Exception { + super.performTest(); + CSHAKEDigest cshake = new CSHAKEDigest(128, new byte[0], Strings.toByteArray("Email Signature")); cshake.update(Hex.decode("00010203"), 0, 4); diff --git a/core/src/test/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java index 9af19cf516..62882db838 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/SHAKEDigestTest.java @@ -7,18 +7,34 @@ import java.util.ArrayList; import java.util.List; +import org.bouncycastle.crypto.Digest; import org.bouncycastle.crypto.digests.SHAKEDigest; import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Hex; -import org.bouncycastle.util.test.SimpleTest; /** * SHAKE Digest Test */ public class SHAKEDigestTest - extends SimpleTest + extends DigestTest { + private static String[] messages = + { + "", + "a", + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + }; + + private static String[] digests = + { + "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26", + "85c8de88d28866bf0868090b3961162bf82392f690d9e4730910f4af7c6ab3ee", + "5881092dd818bf5cf8a3ddb793fbcba74097d5c526a6d35f97b83351940f2cc8", + "1a96182b50fb8c7e74e0a707788f55e98209b8d91fade8f32f8dd5cff7bf21f5" + }; + static class MySHAKEDigest extends SHAKEDigest { MySHAKEDigest(int bitLength) @@ -34,6 +50,7 @@ int myDoFinal(byte[] out, int outOff, int outLen, byte partialByte, int partialB SHAKEDigestTest() { + super(new SHAKEDigest(), messages, digests); } public String getName() @@ -41,9 +58,28 @@ public String getName() return "SHAKE"; } - public void performTest() throws Exception + public void performTest() + { + super.performTest(); + + try + { + testVectors(); + } + catch (Exception e) + { + throw new IllegalStateException(e.toString(), e); + } + } + + protected Digest cloneDigest(Digest digest) + { + return new SHAKEDigest((SHAKEDigest)digest); + } + + protected Digest cloneDigest(byte[] encodedState) { - testVectors(); + return new SHAKEDigest(encodedState); } public void testVectors() throws Exception From 1c5e9984b4b930701b3a880558db096298698eec Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Thu, 24 Jul 2025 18:02:50 +0700 Subject: [PATCH 466/890] DTLS: Throw TlsFatalAlertReceived when fatal alert received --- .../bouncycastle/tls/DTLSClientProtocol.java | 6 ++++++ .../org/bouncycastle/tls/DTLSRecordLayer.java | 7 ++++++- .../bouncycastle/tls/DTLSServerProtocol.java | 6 ++++++ .../org/bouncycastle/tls/DTLSTransport.java | 10 ++++++++++ .../java/org/bouncycastle/tls/TlsProtocol.java | 18 ++++++++++++++---- 5 files changed, 42 insertions(+), 5 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java b/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java index 36708092b9..1f2ceb9ae0 100644 --- a/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java @@ -51,6 +51,12 @@ public DTLSTransport connect(TlsClient client, DatagramTransport transport) { return clientHandshake(state, recordLayer); } + catch (TlsFatalAlertReceived fatalAlertReceived) + { +// assert recordLayer.isFailed(); + invalidateSession(state); + throw fatalAlertReceived; + } catch (TlsFatalAlert fatalAlert) { abortClientHandshake(state, recordLayer, fatalAlert.getAlertDescription()); diff --git a/tls/src/main/java/org/bouncycastle/tls/DTLSRecordLayer.java b/tls/src/main/java/org/bouncycastle/tls/DTLSRecordLayer.java index abdcd29f35..94f9120f05 100644 --- a/tls/src/main/java/org/bouncycastle/tls/DTLSRecordLayer.java +++ b/tls/src/main/java/org/bouncycastle/tls/DTLSRecordLayer.java @@ -150,6 +150,11 @@ boolean isClosed() return closed; } + boolean isFailed() + { + return failed; + } + void resetAfterHelloVerifyRequestServer(long recordSeq) { this.inConnection = true; @@ -740,7 +745,7 @@ else if (null != retransmitEpoch && epoch == retransmitEpoch.getEpoch()) if (alertLevel == AlertLevel.fatal) { failed(); - throw new TlsFatalAlert(alertDescription); + throw new TlsFatalAlertReceived(alertDescription); } // TODO Can close_notify be a fatal alert? diff --git a/tls/src/main/java/org/bouncycastle/tls/DTLSServerProtocol.java b/tls/src/main/java/org/bouncycastle/tls/DTLSServerProtocol.java index bda746305b..9a98298c8a 100644 --- a/tls/src/main/java/org/bouncycastle/tls/DTLSServerProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/DTLSServerProtocol.java @@ -67,6 +67,12 @@ public DTLSTransport accept(TlsServer server, DatagramTransport transport, DTLSR { return serverHandshake(state, recordLayer, request); } + catch (TlsFatalAlertReceived fatalAlertReceived) + { +// assert recordLayer.isFailed(); + invalidateSession(state); + throw fatalAlertReceived; + } catch (TlsFatalAlert fatalAlert) { abortServerHandshake(state, recordLayer, fatalAlert.getAlertDescription()); diff --git a/tls/src/main/java/org/bouncycastle/tls/DTLSTransport.java b/tls/src/main/java/org/bouncycastle/tls/DTLSTransport.java index bd51764b04..d0c8a18972 100644 --- a/tls/src/main/java/org/bouncycastle/tls/DTLSTransport.java +++ b/tls/src/main/java/org/bouncycastle/tls/DTLSTransport.java @@ -55,6 +55,11 @@ public int receive(byte[] buf, int off, int len, int waitMillis, DTLSRecordCallb { return recordLayer.receive(buf, off, len, waitMillis, recordCallback); } + catch (TlsFatalAlertReceived fatalAlertReceived) + { +// assert recordLayer.isFailed(); + throw fatalAlertReceived; + } catch (TlsFatalAlert fatalAlert) { if (AlertDescription.bad_record_mac == fatalAlert.getAlertDescription()) @@ -107,6 +112,11 @@ public int receivePending(byte[] buf, int off, int len, DTLSRecordCallback recor { return recordLayer.receivePending(buf, off, len, recordCallback); } + catch (TlsFatalAlertReceived fatalAlertReceived) + { +// assert recordLayer.isFailed(); + throw fatalAlertReceived; + } catch (TlsFatalAlert fatalAlert) { if (AlertDescription.bad_record_mac == fatalAlert.getAlertDescription()) diff --git a/tls/src/main/java/org/bouncycastle/tls/TlsProtocol.java b/tls/src/main/java/org/bouncycastle/tls/TlsProtocol.java index 9ff6a5ab7d..a5f85958e4 100644 --- a/tls/src/main/java/org/bouncycastle/tls/TlsProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/TlsProtocol.java @@ -144,7 +144,7 @@ protected boolean isTLSv13ConnectionState() private TlsOutputStream tlsOutputStream = null; private volatile boolean closed = false; - private volatile boolean failedWithError = false; + private volatile boolean failed = false; private volatile boolean appDataReady = false; private volatile boolean appDataSplitEnabled = true; private volatile boolean keyUpdateEnabled = false; @@ -324,7 +324,7 @@ protected void handleException(short alertDescription, String message, Throwable protected void handleFailure() throws IOException { this.closed = true; - this.failedWithError = true; + this.failed = true; /* * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated @@ -819,7 +819,7 @@ public int readApplicationData(byte[] buf, int off, int len) { if (this.closed) { - if (this.failedWithError) + if (this.failed) { throw new IOException("Cannot read application data on failed TLS connection"); } @@ -885,7 +885,7 @@ protected void safeReadRecord() } catch (TlsFatalAlertReceived e) { - // Connection failure already handled at source +// assert isFailed(); throw e; } catch (TlsFatalAlert e) @@ -916,6 +916,11 @@ protected boolean safeReadFullRecord(byte[] input, int inputOff, int inputLen) { return recordStream.readFullRecord(input, inputOff, inputLen); } + catch (TlsFatalAlertReceived e) + { +// assert isFailed(); + throw e; + } catch (TlsFatalAlert e) { handleException(e.getAlertDescription(), "Failed to process record", e); @@ -1917,6 +1922,11 @@ public boolean isConnected() return null != context && context.isConnected(); } + public boolean isFailed() + { + return failed; + } + public boolean isHandshaking() { if (closed) From a4b0638952663d54399185ef2766a8f2ebc62e36 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 25 Jul 2025 12:40:41 +0700 Subject: [PATCH 467/890] Refactor some TLS tests --- .../tls/test/TlsProtocolHybridTest.java | 40 +++++++++---------- .../tls/test/TlsProtocolKemTest.java | 40 +++++++++---------- 2 files changed, 40 insertions(+), 40 deletions(-) diff --git a/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolHybridTest.java b/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolHybridTest.java index 7ebbe7af26..a19fe6fded 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolHybridTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolHybridTest.java @@ -7,6 +7,7 @@ import org.bouncycastle.tls.NamedGroup; import org.bouncycastle.tls.TlsClientProtocol; +import org.bouncycastle.tls.TlsServer; import org.bouncycastle.tls.TlsServerProtocol; import org.bouncycastle.tls.crypto.TlsCrypto; import org.bouncycastle.util.Arrays; @@ -35,7 +36,13 @@ public void testMismatchedGroups() throws Exception TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); - ServerThread serverThread = new ServerThread(crypto, serverProtocol, new int[]{ NamedGroup.X25519MLKEM768 }, true); + MockTlsHybridClient client = new MockTlsHybridClient(crypto, null); + MockTlsHybridServer server = new MockTlsHybridServer(crypto); + + client.setNamedGroups(new int[]{ NamedGroup.SecP256r1MLKEM768 }); + server.setNamedGroups(new int[]{ NamedGroup.X25519MLKEM768 }); + + ServerThread serverThread = new ServerThread(serverProtocol, server, true); try { serverThread.start(); @@ -44,8 +51,6 @@ public void testMismatchedGroups() throws Exception { } - MockTlsHybridClient client = new MockTlsHybridClient(crypto, null); - client.setNamedGroups(new int[]{ NamedGroup.SecP256r1MLKEM768 }); try { clientProtocol.connect(client); @@ -83,11 +88,14 @@ private void implTestClientServer(int hybridGroup) throws Exception TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); - ServerThread serverThread = new ServerThread(crypto, serverProtocol, new int[]{ hybridGroup }, false); - serverThread.start(); - MockTlsHybridClient client = new MockTlsHybridClient(crypto, null); + MockTlsHybridServer server = new MockTlsHybridServer(crypto); + client.setNamedGroups(new int[]{ hybridGroup }); + server.setNamedGroups(new int[]{ hybridGroup }); + + ServerThread serverThread = new ServerThread(serverProtocol, server, false); + serverThread.start(); clientProtocol.connect(client); @@ -114,16 +122,14 @@ private void implTestClientServer(int hybridGroup) throws Exception static class ServerThread extends Thread { - private final TlsCrypto crypto; private final TlsServerProtocol serverProtocol; - private final int[] namedGroups; - private boolean shouldFail = false; + private final TlsServer server; + private final boolean shouldFail; - ServerThread(TlsCrypto crypto, TlsServerProtocol serverProtocol, int[] namedGroups, boolean shouldFail) + ServerThread(TlsServerProtocol serverProtocol, TlsServer server, boolean shouldFail) { - this.crypto = crypto; this.serverProtocol = serverProtocol; - this.namedGroups = namedGroups; + this.server = server; this.shouldFail = shouldFail; } @@ -131,12 +137,6 @@ public void run() { try { - MockTlsHybridServer server = new MockTlsHybridServer(crypto); - if (namedGroups != null) - { - server.setNamedGroups(namedGroups); - } - try { serverProtocol.accept(server); @@ -144,6 +144,8 @@ public void run() { fail(); } + + Streams.pipeAll(serverProtocol.getInputStream(), serverProtocol.getOutputStream()); } catch (IOException ignored) { @@ -153,12 +155,10 @@ public void run() } } - Streams.pipeAll(serverProtocol.getInputStream(), serverProtocol.getOutputStream()); serverProtocol.close(); } catch (Exception e) { -// throw new RuntimeException(e); } } } diff --git a/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolKemTest.java b/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolKemTest.java index 2dcaedca6d..366008cc1d 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolKemTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolKemTest.java @@ -7,6 +7,7 @@ import org.bouncycastle.tls.NamedGroup; import org.bouncycastle.tls.TlsClientProtocol; +import org.bouncycastle.tls.TlsServer; import org.bouncycastle.tls.TlsServerProtocol; import org.bouncycastle.tls.crypto.TlsCrypto; import org.bouncycastle.util.Arrays; @@ -35,7 +36,13 @@ public void testMismatchedGroups() throws Exception TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); - ServerThread serverThread = new ServerThread(crypto, serverProtocol, new int[]{ NamedGroup.MLKEM768 }, true); + MockTlsKemClient client = new MockTlsKemClient(crypto, null); + MockTlsKemServer server = new MockTlsKemServer(crypto); + + client.setNamedGroups(new int[]{ NamedGroup.MLKEM512 }); + server.setNamedGroups(new int[]{ NamedGroup.MLKEM768 }); + + ServerThread serverThread = new ServerThread(serverProtocol, server, true); try { serverThread.start(); @@ -44,8 +51,6 @@ public void testMismatchedGroups() throws Exception { } - MockTlsKemClient client = new MockTlsKemClient(crypto, null); - client.setNamedGroups(new int[]{ NamedGroup.MLKEM512 }); try { clientProtocol.connect(client); @@ -83,11 +88,14 @@ private void implTestClientServer(int kemGroup) throws Exception TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); - ServerThread serverThread = new ServerThread(crypto, serverProtocol, new int[]{ kemGroup }, false); - serverThread.start(); - MockTlsKemClient client = new MockTlsKemClient(crypto, null); + MockTlsKemServer server = new MockTlsKemServer(crypto); + client.setNamedGroups(new int[]{ kemGroup }); + server.setNamedGroups(new int[]{ kemGroup }); + + ServerThread serverThread = new ServerThread(serverProtocol, server, false); + serverThread.start(); clientProtocol.connect(client); @@ -114,16 +122,14 @@ private void implTestClientServer(int kemGroup) throws Exception static class ServerThread extends Thread { - private final TlsCrypto crypto; private final TlsServerProtocol serverProtocol; - private final int[] namedGroups; - private boolean shouldFail = false; + private final TlsServer server; + private final boolean shouldFail; - ServerThread(TlsCrypto crypto, TlsServerProtocol serverProtocol, int[] namedGroups, boolean shouldFail) + ServerThread(TlsServerProtocol serverProtocol, TlsServer server, boolean shouldFail) { - this.crypto = crypto; this.serverProtocol = serverProtocol; - this.namedGroups = namedGroups; + this.server = server; this.shouldFail = shouldFail; } @@ -131,12 +137,6 @@ public void run() { try { - MockTlsKemServer server = new MockTlsKemServer(crypto); - if (namedGroups != null) - { - server.setNamedGroups(namedGroups); - } - try { serverProtocol.accept(server); @@ -144,6 +144,8 @@ public void run() { fail(); } + + Streams.pipeAll(serverProtocol.getInputStream(), serverProtocol.getOutputStream()); } catch (IOException ignored) { @@ -153,12 +155,10 @@ public void run() } } - Streams.pipeAll(serverProtocol.getInputStream(), serverProtocol.getOutputStream()); serverProtocol.close(); } catch (Exception e) { -// throw new RuntimeException(e); } } } From 8e9d744669dd7fb1cff78033753bf0603d35f4db Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 25 Jul 2025 17:41:22 +0700 Subject: [PATCH 468/890] Refactoring in TLS tests --- ...AggregatedHandshakeRetransmissionTest.java | 16 ++++++++------- .../test/DTLSHandshakeRetransmissionTest.java | 16 ++++++++------- .../tls/test/DTLSProtocolTest.java | 20 +++++++++---------- .../tls/test/DTLSRawKeysProtocolTest.java | 10 +++++----- .../tls/test/MockDatagramAssociation.java | 13 ++++++++---- .../tls/test/TlsProtocolTest.java | 12 +++++++---- .../tls/test/TlsSRPProtocolTest.java | 12 +++++++---- 7 files changed, 58 insertions(+), 41 deletions(-) diff --git a/tls/src/test/java/org/bouncycastle/tls/test/DTLSAggregatedHandshakeRetransmissionTest.java b/tls/src/test/java/org/bouncycastle/tls/test/DTLSAggregatedHandshakeRetransmissionTest.java index 1181654938..ae762e755b 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/DTLSAggregatedHandshakeRetransmissionTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/DTLSAggregatedHandshakeRetransmissionTest.java @@ -6,8 +6,8 @@ import org.bouncycastle.tls.DTLSTransport; import org.bouncycastle.tls.DTLSVerifier; import org.bouncycastle.tls.DatagramTransport; +import org.bouncycastle.tls.TlsServer; import org.bouncycastle.tls.crypto.TlsCrypto; -import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; @@ -18,12 +18,15 @@ public class DTLSAggregatedHandshakeRetransmissionTest { public void testClientServer() throws Exception { + MockDTLSClient client = new MockDTLSClient(null); + MockDTLSServer server = new MockDTLSServer(); + DTLSClientProtocol clientProtocol = new DTLSClientProtocol(); DTLSServerProtocol serverProtocol = new DTLSServerProtocol(); MockDatagramAssociation network = new MockDatagramAssociation(1500); - ServerThread serverThread = new ServerThread(serverProtocol, network.getServer()); + ServerThread serverThread = new ServerThread(serverProtocol, server, network.getServer()); serverThread.start(); DatagramTransport clientTransport = network.getClient(); @@ -34,8 +37,6 @@ public void testClientServer() throws Exception clientTransport = new MinimalHandshakeAggregator(clientTransport, false, true); - MockDTLSClient client = new MockDTLSClient(null); - client.setHandshakeTimeoutMillis(30000); // Test gets stuck, so we need it to time out. DTLSTransport dtlsClient = clientProtocol.connect(client, clientTransport); @@ -61,12 +62,14 @@ static class ServerThread extends Thread { private final DTLSServerProtocol serverProtocol; + private final TlsServer server; private final DatagramTransport serverTransport; private volatile boolean isShutdown = false; - ServerThread(DTLSServerProtocol serverProtocol, DatagramTransport serverTransport) + ServerThread(DTLSServerProtocol serverProtocol, TlsServer server, DatagramTransport serverTransport) { this.serverProtocol = serverProtocol; + this.server = server; this.serverTransport = serverTransport; } @@ -74,7 +77,7 @@ public void run() { try { - TlsCrypto serverCrypto = new BcTlsCrypto(); + TlsCrypto serverCrypto = server.getCrypto(); DTLSRequest request = null; @@ -105,7 +108,6 @@ public void run() // NOTE: A real server would handle each DTLSRequest in a new task/thread and continue accepting { - MockDTLSServer server = new MockDTLSServer(serverCrypto); DTLSTransport dtlsTransport = serverProtocol.accept(server, serverTransport, request); byte[] buf = new byte[dtlsTransport.getReceiveLimit()]; while (!isShutdown) diff --git a/tls/src/test/java/org/bouncycastle/tls/test/DTLSHandshakeRetransmissionTest.java b/tls/src/test/java/org/bouncycastle/tls/test/DTLSHandshakeRetransmissionTest.java index 7d6de6df6c..8b9de0213c 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/DTLSHandshakeRetransmissionTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/DTLSHandshakeRetransmissionTest.java @@ -6,8 +6,8 @@ import org.bouncycastle.tls.DTLSTransport; import org.bouncycastle.tls.DTLSVerifier; import org.bouncycastle.tls.DatagramTransport; +import org.bouncycastle.tls.TlsServer; import org.bouncycastle.tls.crypto.TlsCrypto; -import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; @@ -18,12 +18,15 @@ public class DTLSHandshakeRetransmissionTest { public void testClientServer() throws Exception { + MockDTLSClient client = new MockDTLSClient(null); + MockDTLSServer server = new MockDTLSServer(); + DTLSClientProtocol clientProtocol = new DTLSClientProtocol(); DTLSServerProtocol serverProtocol = new DTLSServerProtocol(); MockDatagramAssociation network = new MockDatagramAssociation(1500); - ServerThread serverThread = new ServerThread(serverProtocol, network.getServer()); + ServerThread serverThread = new ServerThread(serverProtocol, server, network.getServer()); serverThread.start(); DatagramTransport clientTransport = network.getClient(); @@ -32,8 +35,6 @@ public void testClientServer() throws Exception clientTransport = new LoggingDatagramTransport(clientTransport, System.out); - MockDTLSClient client = new MockDTLSClient(null); - DTLSTransport dtlsClient = clientProtocol.connect(client, clientTransport); for (int i = 1; i <= 10; ++i) @@ -57,12 +58,14 @@ static class ServerThread extends Thread { private final DTLSServerProtocol serverProtocol; + private final TlsServer server; private final DatagramTransport serverTransport; private volatile boolean isShutdown = false; - ServerThread(DTLSServerProtocol serverProtocol, DatagramTransport serverTransport) + ServerThread(DTLSServerProtocol serverProtocol, TlsServer server, DatagramTransport serverTransport) { this.serverProtocol = serverProtocol; + this.server = server; this.serverTransport = serverTransport; } @@ -70,7 +73,7 @@ public void run() { try { - TlsCrypto serverCrypto = new BcTlsCrypto(); + TlsCrypto serverCrypto = server.getCrypto(); DTLSRequest request = null; @@ -101,7 +104,6 @@ public void run() // NOTE: A real server would handle each DTLSRequest in a new task/thread and continue accepting { - MockDTLSServer server = new MockDTLSServer(serverCrypto); DTLSTransport dtlsTransport = serverProtocol.accept(server, serverTransport, request); byte[] buf = new byte[dtlsTransport.getReceiveLimit()]; while (!isShutdown) diff --git a/tls/src/test/java/org/bouncycastle/tls/test/DTLSProtocolTest.java b/tls/src/test/java/org/bouncycastle/tls/test/DTLSProtocolTest.java index 3bb3a51254..faa58ecd5b 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/DTLSProtocolTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/DTLSProtocolTest.java @@ -1,6 +1,6 @@ package org.bouncycastle.tls.test; -import java.security.SecureRandom; +import java.util.Random; import org.bouncycastle.tls.DTLSClientProtocol; import org.bouncycastle.tls.DTLSRequest; @@ -8,8 +8,8 @@ import org.bouncycastle.tls.DTLSTransport; import org.bouncycastle.tls.DTLSVerifier; import org.bouncycastle.tls.DatagramTransport; +import org.bouncycastle.tls.TlsServer; import org.bouncycastle.tls.crypto.TlsCrypto; -import org.bouncycastle.tls.crypto.impl.bc.BcTlsCrypto; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; @@ -20,24 +20,23 @@ public class DTLSProtocolTest { public void testClientServer() throws Exception { - SecureRandom secureRandom = new SecureRandom(); + MockDTLSClient client = new MockDTLSClient(null); + MockDTLSServer server = new MockDTLSServer(); DTLSClientProtocol clientProtocol = new DTLSClientProtocol(); DTLSServerProtocol serverProtocol = new DTLSServerProtocol(); MockDatagramAssociation network = new MockDatagramAssociation(1500); - ServerThread serverThread = new ServerThread(serverProtocol, network.getServer()); + ServerThread serverThread = new ServerThread(serverProtocol, server, network.getServer()); serverThread.start(); DatagramTransport clientTransport = network.getClient(); - clientTransport = new UnreliableDatagramTransport(clientTransport, secureRandom, 0, 0); + clientTransport = new UnreliableDatagramTransport(clientTransport, new Random(), 0, 0); clientTransport = new LoggingDatagramTransport(clientTransport, System.out); - MockDTLSClient client = new MockDTLSClient(null); - DTLSTransport dtlsClient = clientProtocol.connect(client, clientTransport); for (int i = 1; i <= 10; ++i) @@ -61,12 +60,14 @@ static class ServerThread extends Thread { private final DTLSServerProtocol serverProtocol; + private final TlsServer server; private final DatagramTransport serverTransport; private volatile boolean isShutdown = false; - ServerThread(DTLSServerProtocol serverProtocol, DatagramTransport serverTransport) + ServerThread(DTLSServerProtocol serverProtocol, TlsServer server, DatagramTransport serverTransport) { this.serverProtocol = serverProtocol; + this.server = server; this.serverTransport = serverTransport; } @@ -74,7 +75,7 @@ public void run() { try { - TlsCrypto serverCrypto = new BcTlsCrypto(); + TlsCrypto serverCrypto = server.getCrypto(); DTLSRequest request = null; @@ -105,7 +106,6 @@ public void run() // NOTE: A real server would handle each DTLSRequest in a new task/thread and continue accepting { - MockDTLSServer server = new MockDTLSServer(serverCrypto); DTLSTransport dtlsTransport = serverProtocol.accept(server, serverTransport, request); byte[] buf = new byte[dtlsTransport.getReceiveLimit()]; while (!isShutdown) diff --git a/tls/src/test/java/org/bouncycastle/tls/test/DTLSRawKeysProtocolTest.java b/tls/src/test/java/org/bouncycastle/tls/test/DTLSRawKeysProtocolTest.java index 98565948ba..0e5527e990 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/DTLSRawKeysProtocolTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/DTLSRawKeysProtocolTest.java @@ -348,23 +348,23 @@ public void run() TlsCrypto serverCrypto = server.getCrypto(); DTLSRequest request = null; - + // Use DTLSVerifier to require a HelloVerifyRequest cookie exchange before accepting { DTLSVerifier verifier = new DTLSVerifier(serverCrypto); - + // NOTE: Test value only - would typically be the client IP address byte[] clientID = Strings.toUTF8ByteArray("MockRawKeysTlsClient"); - + int receiveLimit = serverTransport.getReceiveLimit(); int dummyOffset = serverCrypto.getSecureRandom().nextInt(16) + 1; byte[] buf = new byte[dummyOffset + serverTransport.getReceiveLimit()]; - + do { if (isShutdown) return; - + int length = serverTransport.receive(buf, dummyOffset, receiveLimit, 100); if (length > 0) { diff --git a/tls/src/test/java/org/bouncycastle/tls/test/MockDatagramAssociation.java b/tls/src/test/java/org/bouncycastle/tls/test/MockDatagramAssociation.java index 662fc2ea23..782d52a601 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/MockDatagramAssociation.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/MockDatagramAssociation.java @@ -22,6 +22,11 @@ public MockDatagramAssociation(int mtu) this.server = new MockDatagramTransport(serverQueue, clientQueue); } + public int getMTU() + { + return mtu; + } + public DatagramTransport getClient() { return client; @@ -35,7 +40,7 @@ public DatagramTransport getServer() private class MockDatagramTransport implements DatagramTransport { - private Vector receiveQueue, sendQueue; + private final Vector receiveQueue, sendQueue; MockDatagramTransport(Vector receiveQueue, Vector sendQueue) { @@ -46,13 +51,13 @@ private class MockDatagramTransport public int getReceiveLimit() throws IOException { - return mtu; + return getMTU(); } public int getSendLimit() throws IOException { - return mtu; + return getMTU(); } public int receive(byte[] buf, int off, int len, int waitMillis) @@ -85,7 +90,7 @@ public int receive(byte[] buf, int off, int len, int waitMillis) public void send(byte[] buf, int off, int len) throws IOException { - if (len > mtu) + if (len > getMTU()) { // TODO Simulate rejection? } diff --git a/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolTest.java b/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolTest.java index ad8c87426c..f7d884df49 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/TlsProtocolTest.java @@ -5,6 +5,7 @@ import java.io.PipedOutputStream; import org.bouncycastle.tls.TlsClientProtocol; +import org.bouncycastle.tls.TlsServer; import org.bouncycastle.tls.TlsServerProtocol; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.io.Streams; @@ -24,10 +25,12 @@ public void testClientServer() throws Exception TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); - ServerThread serverThread = new ServerThread(serverProtocol); + MockTlsClient client = new MockTlsClient(null); + MockTlsServer server = new MockTlsServer(); + + ServerThread serverThread = new ServerThread(serverProtocol, server); serverThread.start(); - MockTlsClient client = new MockTlsClient(null); clientProtocol.connect(client); // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity @@ -54,17 +57,18 @@ static class ServerThread extends Thread { private final TlsServerProtocol serverProtocol; + private final TlsServer server; - ServerThread(TlsServerProtocol serverProtocol) + ServerThread(TlsServerProtocol serverProtocol, TlsServer server) { this.serverProtocol = serverProtocol; + this.server = server; } public void run() { try { - MockTlsServer server = new MockTlsServer(); serverProtocol.accept(server); Streams.pipeAll(serverProtocol.getInputStream(), serverProtocol.getOutputStream()); serverProtocol.close(); diff --git a/tls/src/test/java/org/bouncycastle/tls/test/TlsSRPProtocolTest.java b/tls/src/test/java/org/bouncycastle/tls/test/TlsSRPProtocolTest.java index 373f862d50..d280335c12 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/TlsSRPProtocolTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/TlsSRPProtocolTest.java @@ -5,6 +5,7 @@ import java.io.PipedOutputStream; import org.bouncycastle.tls.TlsClientProtocol; +import org.bouncycastle.tls.TlsServer; import org.bouncycastle.tls.TlsServerProtocol; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.io.Streams; @@ -24,10 +25,12 @@ public void testClientServer() throws Exception TlsClientProtocol clientProtocol = new TlsClientProtocol(clientRead, clientWrite); TlsServerProtocol serverProtocol = new TlsServerProtocol(serverRead, serverWrite); - ServerThread serverThread = new ServerThread(serverProtocol); + MockSRPTlsClient client = new MockSRPTlsClient(null, MockSRPTlsServer.TEST_SRP_IDENTITY); + MockSRPTlsServer server = new MockSRPTlsServer(); + + ServerThread serverThread = new ServerThread(serverProtocol, server); serverThread.start(); - MockSRPTlsClient client = new MockSRPTlsClient(null, MockSRPTlsServer.TEST_SRP_IDENTITY); clientProtocol.connect(client); // NOTE: Because we write-all before we read-any, this length can't be more than the pipe capacity @@ -54,17 +57,18 @@ static class ServerThread extends Thread { private final TlsServerProtocol serverProtocol; + private final TlsServer server; - ServerThread(TlsServerProtocol serverProtocol) + ServerThread(TlsServerProtocol serverProtocol, TlsServer server) { this.serverProtocol = serverProtocol; + this.server = server; } public void run() { try { - MockSRPTlsServer server = new MockSRPTlsServer(); serverProtocol.accept(server); Streams.pipeAll(serverProtocol.getInputStream(), serverProtocol.getOutputStream()); serverProtocol.close(); From 12d7e771d1b3b7f42044cfc22ea4c0d0b8e3e140 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 25 Jul 2025 19:45:39 +0700 Subject: [PATCH 469/890] DTLS: Keep record layer in handshake state --- .../bouncycastle/tls/DTLSClientProtocol.java | 25 ++++++++------- .../bouncycastle/tls/DTLSServerProtocol.java | 32 ++++++++++--------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java b/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java index 1f2ceb9ae0..016267b753 100644 --- a/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/DTLSClientProtocol.java @@ -34,10 +34,6 @@ public DTLSTransport connect(TlsClient client, DatagramTransport transport) TlsClientContextImpl clientContext = new TlsClientContextImpl(client.getCrypto()); - ClientHandshakeState state = new ClientHandshakeState(); - state.client = client; - state.clientContext = clientContext; - client.init(clientContext); clientContext.handshakeBeginning(client); @@ -47,9 +43,14 @@ public DTLSTransport connect(TlsClient client, DatagramTransport transport) DTLSRecordLayer recordLayer = new DTLSRecordLayer(clientContext, client, transport); client.notifyCloseHandle(recordLayer); + ClientHandshakeState state = new ClientHandshakeState(); + state.client = client; + state.clientContext = clientContext; + state.recordLayer = recordLayer; + try { - return clientHandshake(state, recordLayer); + return clientHandshake(state); } catch (TlsFatalAlertReceived fatalAlertReceived) { @@ -59,17 +60,17 @@ public DTLSTransport connect(TlsClient client, DatagramTransport transport) } catch (TlsFatalAlert fatalAlert) { - abortClientHandshake(state, recordLayer, fatalAlert.getAlertDescription()); + abortClientHandshake(state, fatalAlert.getAlertDescription()); throw fatalAlert; } catch (IOException e) { - abortClientHandshake(state, recordLayer, AlertDescription.internal_error); + abortClientHandshake(state, AlertDescription.internal_error); throw e; } catch (RuntimeException e) { - abortClientHandshake(state, recordLayer, AlertDescription.internal_error); + abortClientHandshake(state, AlertDescription.internal_error); throw new TlsFatalAlert(AlertDescription.internal_error, e); } finally @@ -78,17 +79,18 @@ public DTLSTransport connect(TlsClient client, DatagramTransport transport) } } - protected void abortClientHandshake(ClientHandshakeState state, DTLSRecordLayer recordLayer, short alertDescription) + protected void abortClientHandshake(ClientHandshakeState state, short alertDescription) { - recordLayer.fail(alertDescription); + state.recordLayer.fail(alertDescription); invalidateSession(state); } - protected DTLSTransport clientHandshake(ClientHandshakeState state, DTLSRecordLayer recordLayer) + protected DTLSTransport clientHandshake(ClientHandshakeState state) throws IOException { TlsClient client = state.client; TlsClientContextImpl clientContext = state.clientContext; + DTLSRecordLayer recordLayer = state.recordLayer; SecurityParameters securityParameters = clientContext.getSecurityParametersHandshake(); DTLSReliableHandshake handshake = new DTLSReliableHandshake(clientContext, recordLayer, @@ -1142,6 +1144,7 @@ protected static class ClientHandshakeState { TlsClient client = null; TlsClientContextImpl clientContext = null; + DTLSRecordLayer recordLayer = null; TlsSession tlsSession = null; SessionParameters sessionParameters = null; TlsSecret sessionMasterSecret = null; diff --git a/tls/src/main/java/org/bouncycastle/tls/DTLSServerProtocol.java b/tls/src/main/java/org/bouncycastle/tls/DTLSServerProtocol.java index 9a98298c8a..dbc132f47b 100644 --- a/tls/src/main/java/org/bouncycastle/tls/DTLSServerProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/DTLSServerProtocol.java @@ -50,10 +50,6 @@ public DTLSTransport accept(TlsServer server, DatagramTransport transport, DTLSR TlsServerContextImpl serverContext = new TlsServerContextImpl(server.getCrypto()); - ServerHandshakeState state = new ServerHandshakeState(); - state.server = server; - state.serverContext = serverContext; - server.init(serverContext); serverContext.handshakeBeginning(server); @@ -63,9 +59,14 @@ public DTLSTransport accept(TlsServer server, DatagramTransport transport, DTLSR DTLSRecordLayer recordLayer = new DTLSRecordLayer(serverContext, server, transport); server.notifyCloseHandle(recordLayer); + ServerHandshakeState state = new ServerHandshakeState(); + state.server = server; + state.serverContext = serverContext; + state.recordLayer = recordLayer; + try { - return serverHandshake(state, recordLayer, request); + return serverHandshake(state, request); } catch (TlsFatalAlertReceived fatalAlertReceived) { @@ -75,17 +76,17 @@ public DTLSTransport accept(TlsServer server, DatagramTransport transport, DTLSR } catch (TlsFatalAlert fatalAlert) { - abortServerHandshake(state, recordLayer, fatalAlert.getAlertDescription()); + abortServerHandshake(state, fatalAlert.getAlertDescription()); throw fatalAlert; } catch (IOException e) { - abortServerHandshake(state, recordLayer, AlertDescription.internal_error); + abortServerHandshake(state, AlertDescription.internal_error); throw e; } catch (RuntimeException e) { - abortServerHandshake(state, recordLayer, AlertDescription.internal_error); + abortServerHandshake(state, AlertDescription.internal_error); throw new TlsFatalAlert(AlertDescription.internal_error, e); } finally @@ -94,17 +95,17 @@ public DTLSTransport accept(TlsServer server, DatagramTransport transport, DTLSR } } - protected void abortServerHandshake(ServerHandshakeState state, DTLSRecordLayer recordLayer, short alertDescription) + protected void abortServerHandshake(ServerHandshakeState state, short alertDescription) { - recordLayer.fail(alertDescription); + state.recordLayer.fail(alertDescription); invalidateSession(state); } - protected DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRecordLayer recordLayer, - DTLSRequest request) throws IOException + protected DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRequest request) throws IOException { TlsServer server = state.server; TlsServerContextImpl serverContext = state.serverContext; + DTLSRecordLayer recordLayer = state.recordLayer; SecurityParameters securityParameters = serverContext.getSecurityParametersHandshake(); DTLSReliableHandshake handshake = new DTLSReliableHandshake(serverContext, recordLayer, @@ -138,7 +139,7 @@ protected DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRecordLa } { - byte[] serverHelloBody = generateServerHello(state, recordLayer); + byte[] serverHelloBody = generateServerHello(state); // TODO[dtls13] Ideally, move this into generateServerHello once legacy_record_version clarified { @@ -452,7 +453,7 @@ protected byte[] generateNewSessionTicket(ServerHandshakeState state, NewSession return buf.toByteArray(); } - protected byte[] generateServerHello(ServerHandshakeState state, DTLSRecordLayer recordLayer) + protected byte[] generateServerHello(ServerHandshakeState state) throws IOException { TlsServer server = state.server; @@ -710,7 +711,7 @@ else if (TlsUtils.hasExpectedEmptyExtensionData(state.serverExtensions, state.clientHello = null; - applyMaxFragmentLengthExtension(recordLayer, securityParameters.getMaxFragmentLength()); + applyMaxFragmentLengthExtension(state.recordLayer, securityParameters.getMaxFragmentLength()); ByteArrayOutputStream buf = new ByteArrayOutputStream(); serverHello.encode(serverContext, buf); @@ -1020,6 +1021,7 @@ protected static class ServerHandshakeState { TlsServer server = null; TlsServerContextImpl serverContext = null; + DTLSRecordLayer recordLayer = null; TlsSession tlsSession = null; SessionParameters sessionParameters = null; TlsSecret sessionMasterSecret = null; From 709af67b6740a5c061034d18064e25d7c7bc6a68 Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Fri, 25 Jul 2025 20:42:12 +0700 Subject: [PATCH 470/890] DTLS: Set server record layer version(s) earlier - enable several DTLS tests that can now work cleanly --- .../bouncycastle/tls/DTLSServerProtocol.java | 26 +++++++------------ .../tls/test/DTLSRawKeysProtocolTest.java | 8 ++---- .../bouncycastle/tls/test/DTLSTestSuite.java | 20 +++++--------- 3 files changed, 18 insertions(+), 36 deletions(-) diff --git a/tls/src/main/java/org/bouncycastle/tls/DTLSServerProtocol.java b/tls/src/main/java/org/bouncycastle/tls/DTLSServerProtocol.java index dbc132f47b..d9e8d95c2d 100644 --- a/tls/src/main/java/org/bouncycastle/tls/DTLSServerProtocol.java +++ b/tls/src/main/java/org/bouncycastle/tls/DTLSServerProtocol.java @@ -117,9 +117,6 @@ protected DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRequest { clientMessage = handshake.receiveMessage(); - // NOTE: DTLSRecordLayer requires any DTLS version, we don't otherwise constrain this -// ProtocolVersion recordLayerVersion = recordLayer.getReadVersion(); - if (clientMessage.getType() == HandshakeType.client_hello) { processClientHello(state, clientMessage.getBody()); @@ -141,13 +138,6 @@ protected DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRequest { byte[] serverHelloBody = generateServerHello(state); - // TODO[dtls13] Ideally, move this into generateServerHello once legacy_record_version clarified - { - ProtocolVersion recordLayerVersion = serverContext.getServerVersion(); - recordLayer.setReadVersion(recordLayerVersion); - recordLayer.setWriteVersion(recordLayerVersion); - } - handshake.sendMessage(HandshakeType.server_hello, serverHelloBody); } @@ -460,8 +450,6 @@ protected byte[] generateServerHello(ServerHandshakeState state) TlsServerContextImpl serverContext = state.serverContext; SecurityParameters securityParameters = serverContext.getSecurityParametersHandshake(); - // TODO[dtls13] Negotiate cipher suite first? - ProtocolVersion serverVersion; // NOT renegotiating @@ -477,7 +465,7 @@ protected byte[] generateServerHello(ServerHandshakeState state) // ? ProtocolVersion.DTLSv12 // : server_version; // -// recordLayer.setWriteVersion(legacy_record_version); +// state.recordLayer.setWriteVersion(legacy_record_version); securityParameters.negotiatedVersion = serverVersion; } @@ -485,14 +473,16 @@ protected byte[] generateServerHello(ServerHandshakeState state) // if (ProtocolVersion.DTLSv13.isEqualOrEarlierVersionOf(serverVersion)) // { // // See RFC 8446 D.4. -// recordStream.setIgnoreChangeCipherSpec(true); +// state.recordLayer.setIgnoreChangeCipherSpec(true); // -// recordStream.setWriteVersion(ProtocolVersion.DTLSv12); +// state.recordLayer.setReadVersion(ProtocolVersion.DTLSv12); +// state.recordLayer.setWriteVersion(ProtocolVersion.DTLSv12); // // return generate13ServerHello(clientHello, clientHelloMessage, false); // } -// -// recordStream.setWriteVersion(serverVersion); + + state.recordLayer.setReadVersion(serverVersion); + state.recordLayer.setWriteVersion(serverVersion); { boolean useGMTUnixTime = server.shouldUseGMTUnixTime(); @@ -845,6 +835,8 @@ protected void processClientHello(ServerHandshakeState state, byte[] body) protected void processClientHello(ServerHandshakeState state, ClientHello clientHello) throws IOException { + state.recordLayer.setWriteVersion(ProtocolVersion.DTLSv10); + state.clientHello = clientHello; // TODO Read RFCs for guidance on the expected record layer version number diff --git a/tls/src/test/java/org/bouncycastle/tls/test/DTLSRawKeysProtocolTest.java b/tls/src/test/java/org/bouncycastle/tls/test/DTLSRawKeysProtocolTest.java index 0e5527e990..77ddea132c 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/DTLSRawKeysProtocolTest.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/DTLSRawKeysProtocolTest.java @@ -3,6 +3,7 @@ import java.security.SecureRandom; import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; +import org.bouncycastle.tls.AlertDescription; import org.bouncycastle.tls.CertificateType; import org.bouncycastle.tls.DTLSClientProtocol; import org.bouncycastle.tls.DTLSRequest; @@ -13,6 +14,7 @@ import org.bouncycastle.tls.ProtocolVersion; import org.bouncycastle.tls.TlsClient; import org.bouncycastle.tls.TlsExtensionsUtils; +import org.bouncycastle.tls.TlsFatalAlertReceived; import org.bouncycastle.tls.TlsServer; import org.bouncycastle.tls.crypto.TlsCrypto; import org.bouncycastle.util.Arrays; @@ -206,8 +208,6 @@ private void testServerUsesX509AndClientUsesRawKey(ProtocolVersion tlsVersion) t pumpData(client, server); } - // NOTE: Test disabled because of problems getting a clean exit of the DTLS server after a fatal alert. -/* public void testClientSendsClientCertExtensionButServerHasNoCommonTypes() throws Exception { testClientSendsClientCertExtensionButServerHasNoCommonTypes(ProtocolVersion.DTLSv12); @@ -244,10 +244,7 @@ private void testClientSendsClientCertExtensionButServerHasNoCommonTypes(Protoco assertEquals("Should have caused unsupported_certificate alert", alert.getAlertDescription(), AlertDescription.unsupported_certificate); } } -*/ - // NOTE: Test disabled because of problems getting a clean exit of the DTLS server after a fatal alert. -/* public void testClientSendsServerCertExtensionButServerHasNoCommonTypes() throws Exception { testClientSendsServerCertExtensionButServerHasNoCommonTypes(ProtocolVersion.DTLSv12); @@ -284,7 +281,6 @@ private void testClientSendsServerCertExtensionButServerHasNoCommonTypes(Protoco assertEquals("Should have caused unsupported_certificate alert", alert.getAlertDescription(), AlertDescription.unsupported_certificate); } } -*/ private Ed25519PrivateKeyParameters generateKeyPair() { diff --git a/tls/src/test/java/org/bouncycastle/tls/test/DTLSTestSuite.java b/tls/src/test/java/org/bouncycastle/tls/test/DTLSTestSuite.java index d493c959a9..759b83dcb5 100644 --- a/tls/src/test/java/org/bouncycastle/tls/test/DTLSTestSuite.java +++ b/tls/src/test/java/org/bouncycastle/tls/test/DTLSTestSuite.java @@ -43,20 +43,14 @@ private static void addFallbackTests(TestSuite testSuite) addTestCase(testSuite, c, "FallbackGood"); } - /* - * NOTE: Temporarily disabled automatic test runs because of problems getting a clean exit - * of the DTLS server after a fatal alert. As of writing, manual runs show the correct - * alerts being raised - */ + { + TlsTestConfig c = createDTLSTestConfig(ProtocolVersion.DTLSv12); + c.clientFallback = true; + c.clientSupportedVersions = ProtocolVersion.DTLSv10.only(); + c.expectServerFatalAlert(AlertDescription.inappropriate_fallback); -// { -// TlsTestConfig c = createDTLSTestConfig(ProtocolVersion.DTLSv12); -// c.clientFallback = true; -// c.clientSupportedVersions = ProtocolVersion.DTLSv10.only(); -// c.expectServerFatalAlert(AlertDescription.inappropriate_fallback); -// -// addTestCase(testSuite, c, "FallbackBad"); -// } + addTestCase(testSuite, c, "FallbackBad"); + } { TlsTestConfig c = createDTLSTestConfig(ProtocolVersion.DTLSv12); From 48b71dec44adc7bccfff22f8c38ad186c000fa8c Mon Sep 17 00:00:00 2001 From: David Hook Date: Sat, 26 Jul 2025 17:05:22 +1000 Subject: [PATCH 471/890] added SavableDigest support to TupleHash. --- .../crypto/digests/TupleHash.java | 51 +++++- .../bouncycastle/crypto/test/DigestTest.java | 160 ++++++++++++------ .../crypto/test/TupleHashTest.java | 37 +++- 3 files changed, 188 insertions(+), 60 deletions(-) diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/TupleHash.java b/core/src/main/java/org/bouncycastle/crypto/digests/TupleHash.java index 02626ac23e..635b5296a9 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/TupleHash.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/TupleHash.java @@ -1,8 +1,11 @@ package org.bouncycastle.crypto.digests; import org.bouncycastle.crypto.DataLengthException; -import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.SavableDigest; import org.bouncycastle.crypto.Xof; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Memoable; +import org.bouncycastle.util.Pack; import org.bouncycastle.util.Strings; /** @@ -13,14 +16,14 @@ *

      */ public class TupleHash - implements Xof, Digest + implements Xof, SavableDigest { private static final byte[] N_TUPLE_HASH = Strings.toByteArray("TupleHash"); private final CSHAKEDigest cshake; - private final int bitLength; - private final int outputLength; + private int bitLength; + private int outputLength; private boolean firstOutput; /** @@ -46,11 +49,27 @@ public TupleHash(int bitLength, byte[] S, int outputSize) public TupleHash(TupleHash original) { this.cshake = new CSHAKEDigest(original.cshake); + this.bitLength = original.bitLength; + this.outputLength = original.outputLength; + this.firstOutput = original.firstOutput; + } + + public TupleHash(byte[] state) + { + this.cshake = new CSHAKEDigest(Arrays.copyOfRange(state, 0, state.length - 9)); + this.bitLength = Pack.bigEndianToInt(state, state.length - 9); + this.outputLength = Pack.bigEndianToInt(state, state.length - 5); + this.firstOutput = state[state.length - 1] != 0; + } + + private void copyIn(TupleHash original) + { + this.cshake.reset(original.cshake); this.bitLength = cshake.fixedOutputLength; this.outputLength = bitLength * 2 / 8; this.firstOutput = original.firstOutput; } - + public String getAlgorithmName() { return "TupleHash" + cshake.getAlgorithmName().substring(6); @@ -133,4 +152,26 @@ public void reset() cshake.reset(); firstOutput = true; } + + public byte[] getEncodedState() + { + byte[] cshakeState = this.cshake.getEncodedState(); + byte[] extraState = new byte[4 + 4 + 1]; + + Pack.intToBigEndian(this.bitLength, extraState, 0); + Pack.intToBigEndian(this.outputLength, extraState, 4); + extraState[8] = this.firstOutput ? (byte)1 : (byte)0; + + return Arrays.concatenate(cshakeState, extraState); + } + + public Memoable copy() + { + return new TupleHash(this); + } + + public void reset(Memoable other) + { + copyIn((TupleHash)other); + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java index a5d2cd82c7..fd32e704fa 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/DigestTest.java @@ -13,6 +13,7 @@ import org.bouncycastle.crypto.OutputLengthException; import org.bouncycastle.crypto.Xof; import org.bouncycastle.crypto.digests.EncodableDigest; +import org.bouncycastle.crypto.digests.TupleHash; import org.bouncycastle.test.TestResourceFinder; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Memoable; @@ -71,71 +72,115 @@ public void performTest() private void testEncodedState(byte[] resBuf, byte[] input, byte[] expected) { - // test state encoding; - digest.update(input, 0, input.length / 2); + if (digest instanceof TupleHash) + { + digest.update(input, 0, input.length); + Digest copy1 = cloneDigest(((EncodableDigest)digest).getEncodedState()); + Digest copy2 = cloneDigest(((EncodableDigest)copy1).getEncodedState()); - // copy the Digest - Digest copy1 = cloneDigest(((EncodableDigest)digest).getEncodedState()); - Digest copy2 = cloneDigest(((EncodableDigest)copy1).getEncodedState()); + digest.doFinal(resBuf, 0); - digest.update(input, input.length / 2, input.length - input.length / 2); + if (!areEqual(expected, resBuf)) + { + fail("failing TupleHash state vector test", expected, new String(Hex.encode(resBuf))); + } - digest.doFinal(resBuf, 0); + copy2.doFinal(resBuf, 0); - if (!areEqual(expected, resBuf)) - { - fail("failing state vector test", expected, new String(Hex.encode(resBuf))); + if (!areEqual(expected, resBuf)) + { + fail("failing TupleHash state copy2 vector test", expected, new String(Hex.encode(resBuf))); + } } + else + { + // test state encoding; + digest.update(input, 0, input.length / 2); - copy1.update(input, input.length / 2, input.length - input.length / 2); - copy1.doFinal(resBuf, 0); + // copy the Digest + Digest copy1 = cloneDigest(((EncodableDigest)digest).getEncodedState()); + Digest copy2 = cloneDigest(((EncodableDigest)copy1).getEncodedState()); - if (!areEqual(expected, resBuf)) - { - fail("failing state copy1 vector test", expected, new String(Hex.encode(resBuf))); - } + digest.update(input, input.length / 2, input.length - input.length / 2); - copy2.update(input, input.length / 2, input.length - input.length / 2); - copy2.doFinal(resBuf, 0); + digest.doFinal(resBuf, 0); - if (!areEqual(expected, resBuf)) - { - fail("failing state copy2 vector test", expected, new String(Hex.encode(resBuf))); + if (!areEqual(expected, resBuf)) + { + fail("failing state vector test", expected, new String(Hex.encode(resBuf))); + } + + copy1.update(input, input.length / 2, input.length - input.length / 2); + copy1.doFinal(resBuf, 0); + + if (!areEqual(expected, resBuf)) + { + fail("failing state copy1 vector test", expected, new String(Hex.encode(resBuf))); + } + + copy2.update(input, input.length / 2, input.length - input.length / 2); + copy2.doFinal(resBuf, 0); + + if (!areEqual(expected, resBuf)) + { + fail("failing state copy2 vector test", expected, new String(Hex.encode(resBuf))); + } } } private void testMemo(byte[] resBuf, byte[] input, byte[] expected) { - Memoable m = (Memoable)digest; + if (digest instanceof TupleHash) + { + Memoable m = (Memoable)digest; - digest.update(input, 0, input.length / 2); + digest.update(input, 0, input.length); - // copy the Digest - Memoable copy1 = m.copy(); - Memoable copy2 = copy1.copy(); + Memoable copy1 = m.copy(); + Memoable copy2 = copy1.copy(); - digest.update(input, input.length / 2, input.length - input.length / 2); - digest.doFinal(resBuf, 0); + digest.doFinal(resBuf, 0); + if (!areEqual(expected, resBuf)) + { + fail("failing tuplehash memo vector test 1", results[results.length - 1], new String(Hex.encode(resBuf))); + } - if (!areEqual(expected, resBuf)) - { - fail("failing memo vector test", results[results.length - 1], new String(Hex.encode(resBuf))); + Digest d = (Digest)copy2; + d.doFinal(resBuf, 0); } + else + { + Memoable m = (Memoable)digest; - m.reset(copy1); + digest.update(input, 0, input.length / 2); - digest.update(input, input.length / 2, input.length - input.length / 2); - digest.doFinal(resBuf, 0); + // copy the Digest + Memoable copy1 = m.copy(); + Memoable copy2 = copy1.copy(); - if (!areEqual(expected, resBuf)) - { - fail("failing memo reset vector test", results[results.length - 1], new String(Hex.encode(resBuf))); - } + digest.update(input, input.length / 2, input.length - input.length / 2); + digest.doFinal(resBuf, 0); - Digest md = (Digest)copy2; + if (!areEqual(expected, resBuf)) + { + fail("failing memo vector test", results[results.length - 1], new String(Hex.encode(resBuf))); + } + + m.reset(copy1); + + digest.update(input, input.length / 2, input.length - input.length / 2); + digest.doFinal(resBuf, 0); + + if (!areEqual(expected, resBuf)) + { + fail("failing memo reset vector test", results[results.length - 1], new String(Hex.encode(resBuf))); + } - md.update(input, input.length / 2, input.length - input.length / 2); - md.doFinal(resBuf, 0); + Digest md = (Digest)copy2; + + md.update(input, input.length / 2, input.length - input.length / 2); + md.doFinal(resBuf, 0); + } if (!areEqual(expected, resBuf)) { @@ -145,21 +190,32 @@ private void testMemo(byte[] resBuf, byte[] input, byte[] expected) private void testClone(byte[] resBuf, byte[] input, byte[] expected) { - digest.update(input, 0, input.length / 2); + if (digest instanceof TupleHash) + { + // can't support multiple updates like the others - it's the whole point! + Digest d = cloneDigest(digest); - // clone the Digest - Digest d = cloneDigest(digest); + d.update(input, 0, input.length); + d.doFinal(resBuf, 0); + } + else + { + digest.update(input, 0, input.length / 2); - digest.update(input, input.length / 2, input.length - input.length / 2); - digest.doFinal(resBuf, 0); + // clone the Digest + Digest d = cloneDigest(digest); - if (!areEqual(expected, resBuf)) - { - fail("failing clone vector test", results[results.length - 1], new String(Hex.encode(resBuf))); - } + digest.update(input, input.length / 2, input.length - input.length / 2); + digest.doFinal(resBuf, 0); + + if (!areEqual(expected, resBuf)) + { + fail("failing clone vector test", results[results.length - 1], new String(Hex.encode(resBuf))); + } - d.update(input, input.length / 2, input.length - input.length / 2); - d.doFinal(resBuf, 0); + d.update(input, input.length / 2, input.length - input.length / 2); + d.doFinal(resBuf, 0); + } if (!areEqual(expected, resBuf)) { diff --git a/core/src/test/java/org/bouncycastle/crypto/test/TupleHashTest.java b/core/src/test/java/org/bouncycastle/crypto/test/TupleHashTest.java index 0ed9d3d1c4..4cd6acaef0 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/TupleHashTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/TupleHashTest.java @@ -5,7 +5,6 @@ import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; -import org.bouncycastle.util.test.SimpleTest; /** * TupleHash test vectors from: @@ -13,16 +12,38 @@ * https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/KMAC_samples.pdf */ public class TupleHashTest - extends SimpleTest + extends DigestTest { + private static String[] messages = + { + "", + "a", + "abc", + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + }; + + private static String[] digests = + { + "549330469327c593eb95b1d467c48e5781939e135e10632c804ef8a69c73281c", + "98e1cb3104a046dcdc77a6acbee4177553ba15cb0235a3db99f506198dc9c8b5", + "873195cadfea6bc6a71cdd903da87afb49fd232d71db817c3abcad48ad8a7898", + "b9588fbf7302809815ebd989d00752f732a08dc9b1153b6f3a097f518cdc44ea" + }; + + public TupleHashTest() + { + super(new TupleHash(128, new byte[0]), messages, digests); + } + public String getName() { return "TupleHash"; } public void performTest() - throws Exception { + super.performTest(); + TupleHash tHash = new TupleHash(128, new byte[0]); tHash.update(Hex.decode("000102"), 0, 3); @@ -105,6 +126,16 @@ public void performTest() testClone(); } + protected Digest cloneDigest(Digest digest) + { + return new TupleHash((TupleHash)digest); + } + + protected Digest cloneDigest(byte[] state) + { + return new TupleHash(state); + } + private void testClone() { Digest digest = new TupleHash(256, Strings.toByteArray("My Tuple App")); From 0fb2d2639979ddf035a47a2e21623ace4b6303b7 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 27 Jul 2025 13:14:47 +1000 Subject: [PATCH 472/890] added EncodableService interface, added Memoable/EncodableService to KMAC. --- .../bouncycastle/crypto/EncodableService.java | 17 +++ .../crypto/digests/EncodableDigest.java | 3 + .../org/bouncycastle/crypto/macs/KMAC.java | 81 +++++++++++- .../bouncycastle/crypto/test/KMACTest.java | 121 +++++++++++++++++- 4 files changed, 216 insertions(+), 6 deletions(-) create mode 100644 core/src/main/java/org/bouncycastle/crypto/EncodableService.java diff --git a/core/src/main/java/org/bouncycastle/crypto/EncodableService.java b/core/src/main/java/org/bouncycastle/crypto/EncodableService.java new file mode 100644 index 0000000000..ec13fadf36 --- /dev/null +++ b/core/src/main/java/org/bouncycastle/crypto/EncodableService.java @@ -0,0 +1,17 @@ +package org.bouncycastle.crypto; + +/** + * Encodable services allow you to download an encoded copy of their internal state. This is useful for the situation where + * you need to generate a signature on an external device and it allows for "sign with last round", so a copy of the + * internal state of the digest, plus the last few blocks of the message are all that needs to be sent, rather than the + * entire message. + */ +public interface EncodableService +{ + /** + * Return an encoded byte array for the services's internal state + * + * @return an encoding of the services internal state. + */ + byte[] getEncodedState(); +} diff --git a/core/src/main/java/org/bouncycastle/crypto/digests/EncodableDigest.java b/core/src/main/java/org/bouncycastle/crypto/digests/EncodableDigest.java index d79fece88a..badaa642ef 100644 --- a/core/src/main/java/org/bouncycastle/crypto/digests/EncodableDigest.java +++ b/core/src/main/java/org/bouncycastle/crypto/digests/EncodableDigest.java @@ -1,5 +1,7 @@ package org.bouncycastle.crypto.digests; +import org.bouncycastle.crypto.EncodableService; + /** * Encodable digests allow you to download an encoded copy of their internal state. This is useful for the situation where * you need to generate a signature on an external device and it allows for "sign with last round", so a copy of the @@ -7,6 +9,7 @@ * entire message. */ public interface EncodableDigest + extends EncodableService { /** * Return an encoded byte array for the digest's internal state diff --git a/core/src/main/java/org/bouncycastle/crypto/macs/KMAC.java b/core/src/main/java/org/bouncycastle/crypto/macs/KMAC.java index ba7d82c05d..bbc8dd645f 100644 --- a/core/src/main/java/org/bouncycastle/crypto/macs/KMAC.java +++ b/core/src/main/java/org/bouncycastle/crypto/macs/KMAC.java @@ -5,9 +5,12 @@ import org.bouncycastle.crypto.Mac; import org.bouncycastle.crypto.Xof; import org.bouncycastle.crypto.digests.CSHAKEDigest; +import org.bouncycastle.crypto.digests.EncodableDigest; import org.bouncycastle.crypto.digests.XofUtils; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Memoable; +import org.bouncycastle.util.Pack; import org.bouncycastle.util.Strings; /** @@ -17,13 +20,14 @@ *

      */ public class KMAC - implements Mac, Xof + implements Mac, Xof, Memoable, EncodableDigest { private static final byte[] padding = new byte[100]; private final CSHAKEDigest cshake; - private final int bitLength; - private final int outputLength; + + private int bitLength; + private int outputLength; private byte[] key; private boolean initialised; @@ -42,12 +46,48 @@ public KMAC(int bitLength, byte[] S) this.outputLength = bitLength * 2 / 8; } + public KMAC(KMAC original) + { + this.cshake = new CSHAKEDigest(original.cshake); + this.bitLength = original.bitLength; + this.outputLength = original.outputLength; + this.key = original.key; + this.initialised = original.initialised; + this.firstOutput = original.firstOutput; + } + + public KMAC(byte[] state) + { + this.key = new byte[state[0] & 0xff]; + System.arraycopy(state, 1, key, 0, key.length); + this.cshake = new CSHAKEDigest(Arrays.copyOfRange(state, 1 + key.length, state.length - 10)); + + this.bitLength = Pack.bigEndianToInt(state, state.length - 10); + this.outputLength = Pack.bigEndianToInt(state, state.length - 6); + this.initialised = state[state.length - 2] != 0; + this.firstOutput = state[state.length - 1] != 0; + } + + private void copyIn(KMAC original) + { + this.cshake.reset(original.cshake); + this.bitLength = original.bitLength; + this.outputLength = original.outputLength; + this.initialised = original.initialised; + this.firstOutput = original.firstOutput; + } + public void init(CipherParameters params) throws IllegalArgumentException { KeyParameter kParam = (KeyParameter)params; this.key = Arrays.clone(kParam.getKey()); + if (this.key.length > 255) // 2^2040 + { + throw new IllegalArgumentException("key length must be between 0 and 2040 bits"); + } + this.initialised = true; reset(); @@ -201,4 +241,39 @@ private static byte[] encode(byte[] X) { return Arrays.concatenate(XofUtils.leftEncode(X.length * 8), X); } + + public byte[] getEncodedState() + { + if (!this.initialised) + { + throw new IllegalStateException("KMAC not initialised"); + } + + byte[] cshakeState = this.cshake.getEncodedState(); + byte[] extraState = new byte[4 + 4 + 2]; + + Pack.intToBigEndian(this.bitLength, extraState, 0); + Pack.intToBigEndian(this.outputLength, extraState, 4); + extraState[8] = this.initialised ? (byte)1 : (byte)0; + extraState[9] = this.firstOutput ? (byte)1 : (byte)0; + + byte[] enc = new byte[1 + key.length + cshakeState.length + extraState.length]; + + enc[0] = (byte)key.length; // key capped at 255 bytes. + System.arraycopy(key, 0, enc, 1, key.length); + System.arraycopy(cshakeState, 0, enc, 1 + key.length, cshakeState.length); + System.arraycopy(extraState, 0, enc, 1 + key.length + cshakeState.length, extraState.length); + + return enc; + } + + public Memoable copy() + { + return new KMAC(this); + } + + public void reset(Memoable other) + { + copyIn((KMAC)other); + } } diff --git a/core/src/test/java/org/bouncycastle/crypto/test/KMACTest.java b/core/src/test/java/org/bouncycastle/crypto/test/KMACTest.java index 52f9fb73d1..55355c7513 100644 --- a/core/src/test/java/org/bouncycastle/crypto/test/KMACTest.java +++ b/core/src/test/java/org/bouncycastle/crypto/test/KMACTest.java @@ -1,8 +1,11 @@ package org.bouncycastle.crypto.test; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.EncodableService; import org.bouncycastle.crypto.macs.KMAC; import org.bouncycastle.crypto.params.KeyParameter; import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.Memoable; import org.bouncycastle.util.Strings; import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.test.SimpleTest; @@ -137,7 +140,15 @@ public void performTest() checkKMAC(256, new KMAC(256, null), Hex.decode("eeaabeef")); checkKMAC(128, new KMAC(128, new byte[0]), Hex.decode("eeaabeef")); checkKMAC(128, new KMAC(128, null), Hex.decode("eeaabeef")); - checkKMAC(256, new KMAC(256, null), Hex.decode("eeaabeef")); + checkKMAC(256, new KMAC(256, null), Hex.decode("eeaabeef")); + + byte[] resBuf = new byte[32]; + byte[] message = Hex.decode("404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F"); + byte[] expected = Hex.decode("059a2eb4961b482ff5bb6a0278d3ad2117b20aafb2f0df33e7748176648c8192"); + + testClone(resBuf, message, expected, new KMAC(128, new byte[0]), Hex.decode("eeaabeef")); + testMemo(resBuf, message, expected, new KMAC(128, new byte[0]), Hex.decode("eeaabeef")); + testEncodedState(resBuf, message, expected, new KMAC(128, new byte[0]), Hex.decode("eeaabeef")); } private void doFinalTest() @@ -146,7 +157,7 @@ private void doFinalTest() kmac.init(new KeyParameter(Hex.decode( "404142434445464748494A4B4C4D4E4F505152535455565758595A5B5C5D5E5F"))); - + kmac.update(Hex.decode("00010203"), 0, 4); byte[] res = new byte[32]; @@ -236,7 +247,7 @@ private void checkKMAC(int bitSize, KMAC kmac, byte[] msg) ref.init(new KeyParameter(new byte[0])); kmac.init(new KeyParameter(new byte[0])); - + ref.update(msg, 0, msg.length); kmac.update(msg, 0, msg.length); @@ -249,6 +260,110 @@ private void checkKMAC(int bitSize, KMAC kmac, byte[] msg) isTrue(Arrays.areEqual(res1, res2)); } + private void testEncodedState(byte[] resBuf, byte[] input, byte[] expected, KMAC kmac, byte[] key) + { + kmac.init(new KeyParameter(key)); + + // test state encoding; + kmac.update(input, 0, input.length / 2); + + // copy the Digest + Digest copy1 = new KMAC(((EncodableService)kmac).getEncodedState()); + Digest copy2 = new KMAC(((EncodableService)copy1).getEncodedState()); + + kmac.update(input, input.length / 2, input.length - input.length / 2); + + kmac.doFinal(resBuf, 0); + + if (!areEqual(expected, resBuf)) + { + fail("failing state vector test", expected, new String(Hex.encode(resBuf))); + } + + copy1.update(input, input.length / 2, input.length - input.length / 2); + copy1.doFinal(resBuf, 0); + + if (!areEqual(expected, resBuf)) + { + fail("failing state copy1 vector test", expected, new String(Hex.encode(resBuf))); + } + + copy2.update(input, input.length / 2, input.length - input.length / 2); + copy2.doFinal(resBuf, 0); + + if (!areEqual(expected, resBuf)) + { + fail("failing state copy2 vector test", expected, new String(Hex.encode(resBuf))); + } + } + + private void testMemo(byte[] resBuf, byte[] input, byte[] expected, KMAC kmac, byte[] key) + { + kmac.init(new KeyParameter(key)); + + Memoable m = (Memoable)kmac; + + kmac.update(input, 0, input.length / 2); + + // copy the Digest + Memoable copy1 = m.copy(); + Memoable copy2 = copy1.copy(); + + kmac.update(input, input.length / 2, input.length - input.length / 2); + kmac.doFinal(resBuf, 0); + + if (!areEqual(expected, resBuf)) + { + fail("failing memo vector test", Hex.toHexString(expected), Hex.toHexString(resBuf)); + } + + m.reset(copy1); + + kmac.update(input, input.length / 2, input.length - input.length / 2); + kmac.doFinal(resBuf, 0); + + if (!areEqual(expected, resBuf)) + { + fail("failing memo reset vector test", Hex.toHexString(expected), Hex.toHexString(resBuf)); + } + + KMAC md = (KMAC)copy2; + + md.update(input, input.length / 2, input.length - input.length / 2); + md.doFinal(resBuf, 0); + + if (!areEqual(expected, resBuf)) + { + fail("failing memo copy vector test", Hex.toHexString(expected), Hex.toHexString(resBuf)); + } + } + + private void testClone(byte[] resBuf, byte[] input, byte[] expected, KMAC kmac, byte[] key) + { + kmac.init(new KeyParameter(key)); + + kmac.update(input, 0, input.length / 2); + + // clone the Digest + KMAC d = new KMAC(kmac); + + kmac.update(input, input.length / 2, input.length - input.length / 2); + kmac.doFinal(resBuf, 0); + + if (!areEqual(expected, resBuf)) + { + fail("failing clone vector test", Hex.toHexString(expected), Hex.toHexString(resBuf)); + } + + d.update(input, input.length / 2, input.length - input.length / 2); + d.doFinal(resBuf, 0); + + if (!areEqual(expected, resBuf)) + { + fail("failing second clone vector test", Hex.toHexString(expected), Hex.toHexString(resBuf)); + } + } + public static void main( String[] args) { From 0e100a58af34d0cf91ea5cfd1f0a6d36681c3653 Mon Sep 17 00:00:00 2001 From: David Hook Date: Sun, 27 Jul 2025 13:22:45 +1000 Subject: [PATCH 473/890] updated for EncodableService/Memoable support in SHA-3 family --- docs/releasenotes.html | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/releasenotes.html b/docs/releasenotes.html index df6dcc7ddb..f6b1c82e7f 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -27,6 +27,7 @@

      2.1.2 Defects Fixed

    2.1.3 Additional Features and Functionality

      +
    • SHA3Digest, CSHAKE, TupleHash, KMAC now provide support for Memoable and EncodableService.

    2.2.1 Version

    From 06082b1d5e9871ab67ad43c9e501e374aef828ee Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 29 Jul 2025 14:16:09 +0700 Subject: [PATCH 474/890] PartialInputStream doesn't need BCPGInputStream wrapper --- .../java/org/bouncycastle/bcpg/BCPGInputStream.java | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java b/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java index 045ba3b6ba..2a782f39a8 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/BCPGInputStream.java @@ -239,8 +239,9 @@ public Packet readPacket() } else { - objStream = new BCPGInputStream( - new BufferedInputStream(new PartialInputStream(this, partial, bodyLen))); +// assert !this.next; + PartialInputStream pis = new PartialInputStream(this.in, partial, bodyLen); + objStream = new BCPGInputStream(new BufferedInputStream(pis)); } switch (tag) @@ -339,14 +340,11 @@ public void close() private static class PartialInputStream extends InputStream { - private BCPGInputStream in; + private final InputStream in; private boolean partial; private int dataLength; - PartialInputStream( - BCPGInputStream in, - boolean partial, - int dataLength) + PartialInputStream(InputStream in, boolean partial, int dataLength) { this.in = in; this.partial = partial; From e05d3f5507c099c406875dc10eb94bb80b30aaa5 Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 4 Aug 2025 18:11:19 +1000 Subject: [PATCH 475/890] added build method taking a pre-configured key[] to BcCMSContentEncryptorBuilder - relates to github #2115 --- .../cms/bc/BcCMSContentEncryptorBuilder.java | 70 ++++++++++++++----- 1 file changed, 53 insertions(+), 17 deletions(-) diff --git a/pkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java b/pkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java index 675ce1dce7..bab1799566 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java +++ b/pkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java @@ -73,14 +73,59 @@ public BcCMSContentEncryptorBuilder setSecureRandom(SecureRandom random) return this; } + /** + * Build the OutputEncryptor with an internally generated key. + * + * @return an OutputEncryptor configured to use an internal key. + * @throws CMSException + */ public OutputEncryptor build() throws CMSException { + if (random == null) + { + random = new SecureRandom(); + } + + CipherKeyGenerator keyGen = helper.createKeyGenerator(encryptionOID, keySize, random); + + return build(keyGen.generateKey()); + } + + /** + * Build the OutputEncryptor using a pre-generated key. + * + * @param encKey a raw byte encoding of the key to be used for encryption. + * @return an OutputEncryptor configured to use encKey. + * @throws CMSException + */ + public OutputEncryptor build(byte[] encKey) + throws CMSException + { + if (random == null) + { + random = new SecureRandom(); + } + + // fixed key size defined + if (this.keySize > 0) + { + if (((this.keySize + 7) / 8) != encKey.length) + { + if ((this.keySize != 56 && encKey.length != 8) + && (this.keySize != 168 && encKey.length != 24)) + { + throw new IllegalArgumentException("attempt to create encryptor with the wrong sized key"); + } + } + } + if (helper.isAuthEnveloped(encryptionOID)) { - return new CMSAuthOutputEncryptor(encryptionOID, keySize, random); + return new CMSAuthOutputEncryptor(encryptionOID, new KeyParameter(encKey), random); } - return new CMSOutputEncryptor(encryptionOID, keySize, random); + + return new CMSOutputEncryptor(encryptionOID, new KeyParameter(encKey), random); } private class CMSOutputEncryptor @@ -90,21 +135,12 @@ private class CMSOutputEncryptor private AlgorithmIdentifier algorithmIdentifier; protected Object cipher; - CMSOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random) + CMSOutputEncryptor(ASN1ObjectIdentifier encryptionOID, KeyParameter encKey, SecureRandom random) throws CMSException { - if (random == null) - { - random = new SecureRandom(); - } - - CipherKeyGenerator keyGen = helper.createKeyGenerator(encryptionOID, keySize, random); - - encKey = new KeyParameter(keyGen.generateKey()); - - algorithmIdentifier = helper.generateEncryptionAlgID(encryptionOID, encKey, random); - - cipher = EnvelopedDataHelper.createContentCipher(true, encKey, algorithmIdentifier); + this.algorithmIdentifier = helper.generateEncryptionAlgID(encryptionOID, encKey, random); + this.encKey = encKey; + this.cipher = EnvelopedDataHelper.createContentCipher(true, encKey, algorithmIdentifier); } public AlgorithmIdentifier getAlgorithmIdentifier() @@ -130,10 +166,10 @@ private class CMSAuthOutputEncryptor private AEADBlockCipher aeadCipher; private MacCaptureStream macOut; - CMSAuthOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random) + CMSAuthOutputEncryptor(ASN1ObjectIdentifier encryptionOID, KeyParameter encKey, SecureRandom random) throws CMSException { - super(encryptionOID, keySize, random); + super(encryptionOID, encKey, random); aeadCipher = getCipher(); } From 9b49fabc6ca46a9a9506b60238fd8e45ac813d9d Mon Sep 17 00:00:00 2001 From: David Hook Date: Mon, 4 Aug 2025 19:00:18 +1000 Subject: [PATCH 476/890] added build method taking a pre-calculated key as SecretKey and byte[] + test for same - relates to github #2115 minor refactor of Bc class. --- .../cms/bc/BcCMSContentEncryptorBuilder.java | 16 ++-- .../jcajce/JceCMSContentEncryptorBuilder.java | 79 ++++++++++++++----- .../cms/test/NewEnvelopedDataStreamTest.java | 43 ++++++++++ 3 files changed, 109 insertions(+), 29 deletions(-) diff --git a/pkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java b/pkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java index bab1799566..4e6f8e83e3 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java +++ b/pkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java @@ -95,11 +95,11 @@ public OutputEncryptor build() /** * Build the OutputEncryptor using a pre-generated key. * - * @param encKey a raw byte encoding of the key to be used for encryption. - * @return an OutputEncryptor configured to use encKey. + * @param rawEncKey a raw byte encoding of the key to be used for encryption. + * @return an OutputEncryptor configured to use rawEncKey. * @throws CMSException */ - public OutputEncryptor build(byte[] encKey) + public OutputEncryptor build(byte[] rawEncKey) throws CMSException { if (random == null) @@ -110,10 +110,10 @@ public OutputEncryptor build(byte[] encKey) // fixed key size defined if (this.keySize > 0) { - if (((this.keySize + 7) / 8) != encKey.length) + if (((this.keySize + 7) / 8) != rawEncKey.length) { - if ((this.keySize != 56 && encKey.length != 8) - && (this.keySize != 168 && encKey.length != 24)) + if ((this.keySize != 56 && rawEncKey.length != 8) + && (this.keySize != 168 && rawEncKey.length != 24)) { throw new IllegalArgumentException("attempt to create encryptor with the wrong sized key"); } @@ -122,10 +122,10 @@ public OutputEncryptor build(byte[] encKey) if (helper.isAuthEnveloped(encryptionOID)) { - return new CMSAuthOutputEncryptor(encryptionOID, new KeyParameter(encKey), random); + return new CMSAuthOutputEncryptor(encryptionOID, new KeyParameter(rawEncKey), random); } - return new CMSOutputEncryptor(encryptionOID, new KeyParameter(encKey), random); + return new CMSOutputEncryptor(encryptionOID, new KeyParameter(rawEncKey), random); } private class CMSOutputEncryptor diff --git a/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java b/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java index b320540447..b19b840800 100644 --- a/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java +++ b/pkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java @@ -181,16 +181,63 @@ public JceCMSContentEncryptorBuilder setAlgorithmParameters(AlgorithmParameters return this; } + /** + * Build the OutputEncryptor with an internally generated key. + * + * @return an OutputEncryptor configured to use an internal key. + * @throws CMSException + */ public OutputEncryptor build() throws CMSException + { + KeyGenerator keyGen = helper.createKeyGenerator(encryptionOID); + + random = CryptoServicesRegistrar.getSecureRandom(random); + + if (keySize < 0) + { + keyGen.init(random); + } + else + { + keyGen.init(keySize, random); + } + + return build(keyGen.generateKey()); + } + + /** + * Build the OutputEncryptor using a pre-generated key given as a raw encoding. + * + * @param rawEncKey a raw byte encoding of the key to be used for encryption. + * @return an OutputEncryptor configured to use rawEncKey. + * @throws CMSException + */ + public OutputEncryptor build(byte[] rawEncKey) + throws CMSException + { + SecretKey encKey = new SecretKeySpec(rawEncKey, helper.getBaseCipherName(encryptionOID)); + + return build(encKey); + } + + /** + * Build the OutputEncryptor using a pre-generated key. + * + * @param encKey a pre-generated key to be used for encryption. + * @return an OutputEncryptor configured to use encKey. + * @throws CMSException + */ + public OutputEncryptor build(SecretKey encKey) + throws CMSException { if (algorithmParameters != null) { if (helper.isAuthEnveloped(encryptionOID)) { - return new CMSAuthOutputEncryptor(kdfAlgorithm, encryptionOID, keySize, algorithmParameters, random); + return new CMSAuthOutputEncryptor(kdfAlgorithm, encryptionOID, encKey, algorithmParameters, random); } - return new CMSOutputEncryptor(kdfAlgorithm, encryptionOID, keySize, algorithmParameters, random); + return new CMSOutputEncryptor(kdfAlgorithm, encryptionOID, encKey, algorithmParameters, random); } if (algorithmIdentifier != null) { @@ -212,9 +259,9 @@ public OutputEncryptor build() if (helper.isAuthEnveloped(encryptionOID)) { - return new CMSAuthOutputEncryptor(kdfAlgorithm, encryptionOID, keySize, algorithmParameters, random); + return new CMSAuthOutputEncryptor(kdfAlgorithm, encryptionOID, encKey, algorithmParameters, random); } - return new CMSOutputEncryptor(kdfAlgorithm, encryptionOID, keySize, algorithmParameters, random); + return new CMSOutputEncryptor(kdfAlgorithm, encryptionOID, encKey, algorithmParameters, random); } private class CMSOutEncryptor @@ -252,24 +299,14 @@ private void applyKdf(ASN1ObjectIdentifier kdfAlgorithm, AlgorithmParameters par algorithmIdentifier = new AlgorithmIdentifier(kdfAlgorithm, algorithmIdentifier); } - protected void init(ASN1ObjectIdentifier kdfAlgorithm, ASN1ObjectIdentifier encryptionOID, int keySize, AlgorithmParameters params, SecureRandom random) + protected void init(ASN1ObjectIdentifier kdfAlgorithm, ASN1ObjectIdentifier encryptionOID, SecretKey encKey, AlgorithmParameters params, SecureRandom random) throws CMSException { - KeyGenerator keyGen = helper.createKeyGenerator(encryptionOID); + this.encKey = encKey; random = CryptoServicesRegistrar.getSecureRandom(random); - if (keySize < 0) - { - keyGen.init(random); - } - else - { - keyGen.init(keySize, random); - } - - cipher = helper.createCipher(encryptionOID); - encKey = keyGen.generateKey(); + this.cipher = helper.createCipher(encryptionOID); if (params == null) { @@ -327,10 +364,10 @@ private class CMSOutputEncryptor extends CMSOutEncryptor implements OutputEncryptor { - CMSOutputEncryptor(ASN1ObjectIdentifier kdfAlgorithm, ASN1ObjectIdentifier encryptionOID, int keySize, AlgorithmParameters params, SecureRandom random) + CMSOutputEncryptor(ASN1ObjectIdentifier kdfAlgorithm, ASN1ObjectIdentifier encryptionOID, SecretKey encKey, AlgorithmParameters params, SecureRandom random) throws CMSException { - init(kdfAlgorithm, encryptionOID, keySize, params, random); + init(kdfAlgorithm, encryptionOID, encKey, params, random); } public AlgorithmIdentifier getAlgorithmIdentifier() @@ -355,10 +392,10 @@ private class CMSAuthOutputEncryptor { private MacCaptureStream macOut; - CMSAuthOutputEncryptor(ASN1ObjectIdentifier kdfAlgorithm, ASN1ObjectIdentifier encryptionOID, int keySize, AlgorithmParameters params, SecureRandom random) + CMSAuthOutputEncryptor(ASN1ObjectIdentifier kdfAlgorithm, ASN1ObjectIdentifier encryptionOID, SecretKey encKey, AlgorithmParameters params, SecureRandom random) throws CMSException { - init(kdfAlgorithm, encryptionOID, keySize, params, random); + init(kdfAlgorithm, encryptionOID, encKey, params, random); } public AlgorithmIdentifier getAlgorithmIdentifier() diff --git a/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataStreamTest.java b/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataStreamTest.java index 9aafdc35a3..eb265f17fb 100644 --- a/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataStreamTest.java +++ b/pkix/src/test/java/org/bouncycastle/cms/test/NewEnvelopedDataStreamTest.java @@ -775,6 +775,49 @@ public void testTwoAESKEK() ep.close(); } + public void testTwoAESKEKWithPrecomputedContentKey() + throws Exception + { + byte[] data = "WallaWallaWashington".getBytes(); + SecretKey kek1 = CMSTestUtil.makeAES192Key(); + SecretKey kek2 = CMSTestUtil.makeAES192Key(); + + CMSEnvelopedDataStreamGenerator edGen = new CMSEnvelopedDataStreamGenerator(); + + byte[] kekId1 = new byte[]{1, 2, 3, 4, 5}; + byte[] kekId2 = new byte[]{5, 4, 3, 2, 1}; + + edGen.addRecipientInfoGenerator(new JceKEKRecipientInfoGenerator(kekId1, kek1).setProvider(BC)); + edGen.addRecipientInfoGenerator(new JceKEKRecipientInfoGenerator(kekId2, kek2).setProvider(BC)); + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + OutputStream out = edGen.open( + bOut, + new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider(BC).build(Hex.decode("000102030405060708090a0b0c0d0e0f"))); + out.write(data); + + out.close(); + + CMSEnvelopedDataParser ep = new CMSEnvelopedDataParser(bOut.toByteArray()); + + RecipientInformationStore recipients = ep.getRecipientInfos(); + + assertEquals(ep.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.AES128_CBC); + + RecipientId recSel = new KEKRecipientId(kekId2); + + RecipientInformation recipient = recipients.get(recSel); + + assertEquals(recipient.getKeyEncryptionAlgOID(), "2.16.840.1.101.3.4.1.25"); + + CMSTypedStream recData = recipient.getContentStream(new JceKEKEnvelopedRecipient(kek2).setProvider(BC)); + + assertEquals(true, Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream()))); + + ep.close(); + } + public void testECKeyAgree() throws Exception { From e99a4ea36d5697ce5b48876dedaa89ae833a247c Mon Sep 17 00:00:00 2001 From: Peter Dettman Date: Tue, 5 Aug 2025 11:56:56 +0700 Subject: [PATCH 477/890] TLS: Avoid nonce reuse error in JCE AEAD workaround for pre-Java7 - see https://github.com/bcgit/bc-java/issues/2100 --- docs/releasenotes.html | 1 + .../crypto/impl/jcajce/JceAEADCipherImpl.java | 39 +++++++++++++++---- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/docs/releasenotes.html b/docs/releasenotes.html index f6b1c82e7f..8a5f46e0fa 100644 --- a/docs/releasenotes.html +++ b/docs/releasenotes.html @@ -24,6 +24,7 @@

    2.0 Release History

    2.1.2 Defects Fixed

    • SNOVA and MAYO are now correctly added to the JCA provider module-info file.
    • +
    • TLS: Avoid nonce reuse error in JCE AEAD workaround for pre-Java7.

    2.1.3 Additional Features and Functionality