diff --git a/crypto/src/pqc/crypto/picnic/PicnicEngine.cs b/crypto/src/pqc/crypto/picnic/PicnicEngine.cs
index 38b1fecc0..f25f0b812 100644
--- a/crypto/src/pqc/crypto/picnic/PicnicEngine.cs
+++ b/crypto/src/pqc/crypto/picnic/PicnicEngine.cs
@@ -86,10 +86,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
return CRYPTO_BYTES + messageLength;
}
- //todo dont do this
internal int GetTrueSignatureSize()
{
- return signatureLength + 4;
+ return signatureLength;
}
internal PicnicEngine(int picnicParams, LowmcConstants lowmcConstants)
@@ -319,19 +318,19 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
private int picnic_verify(byte[] pk, byte[] message, byte[] signature, uint sigLen)
{
- byte[] plaintext_bytes = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
- byte[] ciphertext_bytes = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
- picnic_read_public_key(ciphertext_bytes, plaintext_bytes, pk);
uint[] ciphertext = new uint[stateSizeWords];
uint[] plaintext = new uint[stateSizeWords];
-
- Pack.LE_To_UInt32(plaintext_bytes, 0, plaintext);
- Pack.LE_To_UInt32(ciphertext_bytes, 0, ciphertext);
+ picnic_read_public_key(ciphertext, plaintext, pk);
if (is_picnic3(parameters))
{
Signature2 sig = new Signature2(this);
- DeserializeSignature2(sig, signature, sigLen, message.Length + 4);
+ int ret = DeserializeSignature2(sig, signature, sigLen, message.Length + 4);
+ if (ret != 0)
+ {
+ Console.Error.Write("Error couldn't deserialize signature (2)!");
+ return -1;
+ }
return verify_picnic3(sig, ciphertext, plaintext, message);
}
@@ -342,6 +341,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
if (ret != 0)
{
Console.Error.Write("Error couldn't deserialize signature!");
+ return -1;
}
return Verify(sig, ciphertext, plaintext, message);
@@ -398,9 +398,13 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
view1s[i] = new View(this);
view2s[i] = new View(this);
- VerifyProof(proofs[i], view1s[i], view2s[i],
+ if (!VerifyProof(proofs[i], view1s[i], view2s[i],
GetChallenge(received_challengebits, i), sig.salt, (uint)i,
- tmp, plaintext, tape);
+ tmp, plaintext, tape))
+ {
+ Console.Error.Write(("Invalid signature. Did not verify\n"));
+ return -1;
+ }
// create ordered array of commitments with order computed based on the challenge
// check commitments of the two opened views
@@ -419,7 +423,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
viewOutputs[i][challenge] = view1s[i].outputShare;
viewOutputs[i][(challenge + 1) % 3] = view2s[i].outputShare;
uint[] view3Output = new uint[stateSizeWords]; /* pointer into the slab to the current 3rd view */
- xor_three(view3Output, view1s[i].outputShare, view2s[i].outputShare, pubKey, stateSizeBytes);
+ xor_three(view3Output, view1s[i].outputShare, view2s[i].outputShare, pubKey);
viewOutputs[i][(challenge + 2) % 3] = view3Output;
}
@@ -436,7 +440,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
return status;
}
- private void VerifyProof(Signature.Proof proof, View view1, View view2, int challenge, byte[] salt,
+ private bool VerifyProof(Signature.Proof proof, View view1, View view2, int challenge, byte[] salt,
uint roundNumber, byte[] tmp, uint[] plaintext, Tape tape)
{
Array.Copy(proof.communicatedBits, 0, view2.communicatedBits, 0, andSizeBytes);
@@ -460,9 +464,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
1, tmp, stateSizeBytes + andSizeBytes);
if (!status)
- {
break;
- }
Pack.LE_To_UInt32(tmp, 0, view2.inputShare); //todo check
Array.Copy(tmp, stateSizeBytes, tape.tapes[1], 0, andSizeBytes);
@@ -481,12 +483,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
Array.Copy(tmp, stateSizeBytes, tape.tapes[0], 0, andSizeBytes);
status = status && CreateRandomTape(proof.seed2, 0, salt, roundNumber,
2, tape.tapes[1], andSizeBytes);
+
if (!status)
- {
break;
- }
- Array.Copy(proof.inputShare, 0, view2.inputShare, 0, stateSizeBytes);
+ Array.Copy(proof.inputShare, 0, view2.inputShare, 0, stateSizeWords);
break;
}
case 2:
@@ -495,13 +496,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
// it is not computable from the seed. We just need to compute view2's input from
// its seed
status = CreateRandomTape(proof.seed1, 0, salt, roundNumber, 2, tape.tapes[0], andSizeBytes);
- Array.Copy(proof.inputShare, 0, view1.inputShare, 0, stateSizeBytes);
+ Array.Copy(proof.inputShare, 0, view1.inputShare, 0, stateSizeWords);
status = status && CreateRandomTape(proof.seed2, 0, salt, roundNumber, 0, tmp,
(stateSizeBytes + andSizeBytes));
+
if (!status)
- {
break;
- }
Pack.LE_To_UInt32(tmp, 0, view2.inputShare); //todo check
Array.Copy(tmp, stateSizeBytes, tape.tapes[1], 0, andSizeBytes);
@@ -518,27 +518,18 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
{
Console.Error.Write(
"Failed to generate random tapes, signature verification will fail (but signature may actually be valid)\n");
+ return false;
}
- /* When input shares are read from the tapes, and the length is not a whole number of bytes, the trailing bits must be zero */
- byte[] view_bytes = new byte[stateSizeBytes * 4];
- Pack.UInt32_To_LE(view1.inputShare, view_bytes, 0);
- Arrays.Fill(view_bytes, stateSizeBytes, view_bytes.Length,
- (byte) 0); //todo have correct size: reduce view.inputshare by /4
- PicnicUtilities.ZeroTrailingBits(view_bytes, stateSizeBits);
- Pack.LE_To_UInt32(view_bytes, 0, view1.inputShare);
-
- Pack.UInt32_To_LE(view2.inputShare, view_bytes, 0);
- Arrays.Fill(view_bytes, stateSizeBytes, view_bytes.Length, (byte) 0);
- PicnicUtilities.ZeroTrailingBits(view_bytes, stateSizeBits);
-
- Pack.LE_To_UInt32(view_bytes, 0, view2.inputShare);
+ PicnicUtilities.ZeroTrailingBits(view1.inputShare, stateSizeBits);
+ PicnicUtilities.ZeroTrailingBits(view2.inputShare, stateSizeBits);
uint[] tmp_ints = Pack.LE_To_UInt32(tmp, 0, tmp.Length / 4);
mpc_LowMC_verify(view1, view2, tape, tmp_ints, plaintext, challenge);
+ return true;
}
- private void mpc_LowMC_verify(View view1, View view2, Tape tapes, uint[] tmp, uint[] plaintext, int challenge)
+ private void mpc_LowMC_verify(View view1, View view2, Tape tapes, uint[] tmp, uint[] plaintext, int challenge)
{
PicnicUtilities.Fill(tmp, 0, tmp.Length, 0);
@@ -552,7 +543,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
view2.inputShare, 0,
current.GetData(), current.GetMatrixPointer());
- mpc_xor(tmp, tmp, stateSizeWords, 2);
+ mpc_xor(tmp, tmp, 2);
for (int r = 1; r <= numRounds; ++r)
{
@@ -573,7 +564,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
current = _lowmcConstants.RConstant(this, r - 1);
mpc_xor_constant_verify(tmp, current.GetData(), current.GetMatrixPointer(), stateSizeWords, challenge);
- mpc_xor(tmp, tmp, stateSizeWords, 2);
+ mpc_xor(tmp, tmp, 2);
}
Array.Copy(tmp, 2 * stateSizeWords, view1.outputShare, 0, stateSizeWords);
@@ -617,10 +608,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
private void mpc_AND_verify(uint[] in1, uint[] in2, uint[] output, Tape rand, View view1, View view2)
{
- uint[] r = {PicnicUtilities.GetBit(rand.tapes[0], rand.pos), PicnicUtilities.GetBit(rand.tapes[1], rand.pos)};
+ uint r0 = PicnicUtilities.GetBit(rand.tapes[0], rand.pos);
+ uint r1 = PicnicUtilities.GetBit(rand.tapes[1], rand.pos);
- output[0] = (in1[0] & in2[1]) ^ (in1[1] & in2[0]) ^ (in1[0] & in2[0]) ^ r[0] ^ r[1];
- PicnicUtilities.SetBit(view1.communicatedBits, rand.pos, (byte) (output[0] & 0xff));
+ uint a0 = in1[0], a1 = in1[1];
+ uint b0 = in2[0], b1 = in2[1];
+
+ output[0] = (a0 & b1) ^ (a1 & b0) ^ (a0 & b0) ^ r0 ^ r1;
+ PicnicUtilities.SetBit(view1.communicatedBits, rand.pos, (byte)output[0]);
output[1] = PicnicUtilities.GetBit(view2.communicatedBits, rand.pos);
rand.pos++;
@@ -643,46 +638,45 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
return;
}
- for (int i = 0; i < length; i++)
- {
- state[i + offset] = state[i + offset] ^ input[i +inOffset];
- }
-
+ Nat.XorTo(length, input, inOffset, state, offset);
}
private int DeserializeSignature(Signature sig, byte[] sigBytes, uint sigBytesLen, int sigBytesOffset)
{
Signature.Proof[] proofs = sig.proofs;
byte[] challengeBits = sig.challengeBits;
+ int challengesLength = PicnicUtilities.NumBytes(2 * numMPCRounds);
/* Validate input buffer is large enough */
- if (sigBytesLen < PicnicUtilities.NumBytes(2 * numMPCRounds))
+ if (sigBytesLen < challengesLength)
{
/* ensure the input has at least the challenge */
return -1;
}
- int inputShareSize = ComputeInputShareSize(sigBytes, stateSizeBytes);
- int bytesExpected = PicnicUtilities.NumBytes(2 * numMPCRounds) + saltSizeBytes +
- numMPCRounds * (2 * seedSizeBytes + andSizeBytes + digestSizeBytes) + inputShareSize;
+ // NOTE: This also validates that there are no challenges > 2
+ int numNonZeroChallenges = CountNonZeroChallenges(sigBytes, sigBytesOffset);
+ if (numNonZeroChallenges < 0)
+ return -1;
+
+ int inputShareSize = numNonZeroChallenges * stateSizeBytes;
+ int bytesRequired = challengesLength + saltSizeBytes +
+ numMPCRounds * (2 * seedSizeBytes + andSizeBytes + digestSizeBytes) + inputShareSize;
if (transform == TRANSFORM_UR)
{
- bytesExpected += UnruhGWithoutInputBytes * numMPCRounds;
+ bytesRequired += UnruhGWithInputBytes * (numMPCRounds - numNonZeroChallenges);
+ bytesRequired += UnruhGWithoutInputBytes * numNonZeroChallenges;
}
- if (sigBytesLen < bytesExpected)
+ if (sigBytesLen != bytesRequired)
{
+ Console.Error.Write("sigBytesLen = %d, expected bytesRequired = %d\n", sigBytesLen, bytesRequired);
return -1;
}
- Array.Copy(sigBytes, sigBytesOffset, challengeBits, 0, PicnicUtilities.NumBytes(2 * numMPCRounds));
- sigBytesOffset += PicnicUtilities.NumBytes(2 * numMPCRounds);
-
- if (!IsChallengeValid(challengeBits))
- {
- return -1;
- }
+ Array.Copy(sigBytes, sigBytesOffset, challengeBits, 0, challengesLength);
+ sigBytesOffset += challengesLength;
Array.Copy(sigBytes, sigBytesOffset, sig.salt, 0, saltSizeBytes);
sigBytesOffset += saltSizeBytes;
@@ -716,60 +710,63 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
Pack.LE_To_UInt32(sigBytes, sigBytesOffset, proofs[i].inputShare, 0, stateSizeBytes / 4);
if (stateSizeBits == 129)
{
- proofs[i].inputShare[stateSizeWords - 1] = (uint)sigBytes[sigBytesOffset + stateSizeBytes - 1] & 0xff;
+ proofs[i].inputShare[stateSizeWords - 1] = (uint)sigBytes[sigBytesOffset + stateSizeBytes - 1];
}
sigBytesOffset += stateSizeBytes;
- if (!ArePaddingBitsZero(Pack.UInt32_To_LE(proofs[i].inputShare), stateSizeBits))
- {
+
+ if (!ArePaddingBitsZero(proofs[i].inputShare, stateSizeBits))
return -1;
- }
}
-
}
return 0;
}
- private bool IsChallengeValid(byte[] challengeBits)
- {
- for (int i = 0; i < numMPCRounds; i++)
- {
- int challenge = GetChallenge(challengeBits, i);
- if (challenge > 2)
- {
- return false;
- }
- }
-
- return true;
- }
-
- private int ComputeInputShareSize(byte[] challengeBits, int stateSizeBytes)
+ private int CountNonZeroChallenges(byte[] challengeBits, int challengeBitsOffset)
{
/* When the FS transform is used, the input share is included in the proof
- * only when the challenge is 1 or 2. When dersializing, to compute the
+ * only when the challenge is 1 or 2. When deserializing, to compute the
* number of bytes expected, we must check how many challenge values are 1
- * or 2. The parameter stateSizeBytes is the size of an input share. */
- int inputShareSize = 0;
+ * or 2. We also check that no challenges have the invalid value 3. */
+ int count = 0;
+ uint challenges3 = 0U;
- for (int i = 0; i < numMPCRounds; i++)
+ int i = 0;
+ while (i + 16 <= numMPCRounds)
{
- int challenge = GetChallenge(challengeBits, i);
- if (challenge == 1 || challenge == 2)
- {
- inputShareSize += stateSizeBytes;
- }
+ uint challenges = Pack.LE_To_UInt32(challengeBits, challengeBitsOffset + (i >> 2));
+ challenges3 |= challenges & (challenges >> 1);
+ count += Integers.PopCount((challenges ^ (challenges >> 1)) & 0x55555555U);
+ i += 16;
+ }
+
+ int remainingBits = (numMPCRounds - i) * 2;
+ if (remainingBits > 0)
+ {
+ int remainingBytes = (remainingBits + 7) / 8;
+ uint challenges = Pack.LE_To_UInt32_Low(challengeBits, challengeBitsOffset + (i >> 2), remainingBytes);
+ challenges &= PicnicUtilities.GetTrailingBitsMask(remainingBits);
+ challenges3 |= challenges & (challenges >> 1);
+ count += Integers.PopCount((challenges ^ (challenges >> 1)) & 0x55555555U);
}
- return inputShareSize;
+ return (challenges3 & 0x55555555U) == 0U ? count : -1;
}
- private uint picnic_read_public_key(byte[] ciphertext, byte[] plaintext, byte[] pk)
+ private void picnic_read_public_key(uint[] ciphertext, uint[] plaintext, byte[] pk)
{
- Array.Copy(pk, 1, ciphertext, 0, stateSizeBytes);
- Array.Copy(pk, 1 + stateSizeBytes, plaintext, 0, stateSizeBytes);
- return 0;
+ int ciphertextPos = 1, plaintextPos = 1 + stateSizeBytes;
+ int fullWords = stateSizeBytes / 4;
+ Pack.LE_To_UInt32(pk, ciphertextPos, ciphertext, 0, fullWords);
+ Pack.LE_To_UInt32(pk, plaintextPos, plaintext, 0, fullWords);
+
+ if (fullWords < stateSizeWords)
+ {
+ int fullWordBytes = fullWords * 4, partialWordBytes = stateSizeBytes - fullWordBytes;
+ ciphertext[fullWords] = Pack.LE_To_UInt32_Low(pk, ciphertextPos + fullWordBytes, partialWordBytes);
+ plaintext[fullWords] = Pack.LE_To_UInt32_Low(pk, plaintextPos + fullWordBytes, partialWordBytes);
+ }
}
private int verify_picnic3(Signature2 sig, uint[] pubKey, uint[] plaintext, byte[] message)
@@ -939,15 +936,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
uint[] missingLeaves = GetMissingLeavesList(sig.challengeC);
ret = treeCv.AddMerkleNodes(missingLeaves, (uint)missingLeavesSize, sig.cvInfo, (uint)sig.cvInfoLen);
if (ret != 0)
- {
return -1;
- }
ret = treeCv.VerifyMerkleTree(Cv, sig.salt);
if (ret != 0)
- {
return -1;
- }
/* Compute the challenge hash */
HCP(challengeHash, null, null, Ch, treeCv.nodes[0], sig.salt, pubKey, plaintext, message);
@@ -968,9 +961,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
int bytesRequired = digestSizeBytes + saltSizeBytes;
if (sigBytes.Length < bytesRequired)
- {
return -1;
- }
Array.Copy(sigBytes, sigBytesOffset, sig.challengeHash, 0, digestSizeBytes);
sigBytesOffset += digestSizeBytes;
@@ -1001,7 +992,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
{
if (Contains(sig.challengeC, numOpenedRounds, t))
{
- uint P_t = sig.challengeP[IndexOf(sig.challengeC, numOpenedRounds, t)];
+ uint P_t = sig.challengeP[IndexOf(sig.challengeC, numOpenedRounds, t)];
if (P_t != (numMPCParties - 1))
{
bytesRequired += andSizeBytes;
@@ -1017,7 +1008,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
/* Fail if the signature does not have the exact number of bytes we expect */
if (sigLen != bytesRequired)
{
- Console.Error.Write("sigBytesLen = %d, expected bytesRequired = %d\n", sigBytes.Length, bytesRequired);
+ Console.Error.Write("sigLen = %d, expected bytesRequired = %d\n", sigLen, bytesRequired);
return -1;
}
@@ -1033,7 +1024,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
/* Read the proofs */
for (uint t = 0; t < numMPCRounds; t++)
{
- if (Contains(sig.challengeC, numOpenedRounds, (uint)t))
+ if (Contains(sig.challengeC, numOpenedRounds, t))
{
sig.proofs[t] = new Signature2.Proof2(this);
sig.proofs[t].seedInfoLen = seedInfoLen;
@@ -1079,40 +1070,51 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
int byteLength = PicnicUtilities.NumBytes(bitLength);
for (int i = bitLength; i < byteLength * 8; i++)
{
- uint bit_i = PicnicUtilities.GetBit(data, i);
+ uint bit_i = PicnicUtilities.GetBit(data, i);
if (bit_i != 0)
- {
return false;
- }
}
-
return true;
}
+ private bool ArePaddingBitsZero(uint[] data, int bitLength)
+ {
+ int partialWord = bitLength & 31;
+ if (partialWord == 0)
+ return true;
+
+ uint mask = PicnicUtilities.GetTrailingBitsMask(bitLength);
+ return (data[bitLength >> 5] & ~mask) == 0U;
+ }
+
internal void crypto_sign(byte[] sm, byte[] m, byte[] sk)
{
- picnic_sign(sk, m, sm);
+ bool ret = picnic_sign(sk, m, sm);
+ if (!ret)
+ return; // throw error?
+
Array.Copy(m, 0, sm, 4, m.Length);
- sm = Arrays.CopyOfRange(sm, 0, signatureLength);
}
- private void picnic_sign(byte[] sk, byte[] message, byte[] signature)
+ private bool picnic_sign(byte[] sk, byte[] message, byte[] signature)
{
- //todo unify conversion
-
- byte[] data_bytes = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
- Array.Copy(sk, 1, data_bytes, 0, stateSizeBytes);
- byte[] ciphertext_bytes = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
- Array.Copy(sk, 1 + stateSizeBytes, ciphertext_bytes, 0, stateSizeBytes);
- byte[] plaintext_bytes = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
- Array.Copy(sk, 1 + 2 * stateSizeBytes, plaintext_bytes, 0, stateSizeBytes);
uint[] data = new uint[stateSizeWords];
uint[] ciphertext = new uint[stateSizeWords];
uint[] plaintext = new uint[stateSizeWords];
- Pack.LE_To_UInt32(data_bytes, 0, data);
- Pack.LE_To_UInt32(plaintext_bytes, 0, plaintext);
- Pack.LE_To_UInt32(ciphertext_bytes, 0, ciphertext);
+ int dataPos = 1, ciphertextPos = 1 + stateSizeBytes, plaintextPos = 1 + 2 * stateSizeBytes;
+ int fullWords = stateSizeBytes / 4;
+ Pack.LE_To_UInt32(sk, dataPos, data, 0, fullWords);
+ Pack.LE_To_UInt32(sk, ciphertextPos, ciphertext, 0, fullWords);
+ Pack.LE_To_UInt32(sk, plaintextPos, plaintext, 0, fullWords);
+
+ if (fullWords < stateSizeWords)
+ {
+ int fullWordBytes = fullWords * 4, partialWordBytes = stateSizeBytes - fullWordBytes;
+ data[fullWords] = Pack.LE_To_UInt32_Low(sk, dataPos + fullWordBytes, partialWordBytes);
+ ciphertext[fullWords] = Pack.LE_To_UInt32_Low(sk, ciphertextPos + fullWordBytes, partialWordBytes);
+ plaintext[fullWords] = Pack.LE_To_UInt32_Low(sk, plaintextPos + fullWordBytes, partialWordBytes);
+ }
if (!is_picnic3(parameters))
{
@@ -1122,24 +1124,40 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
if (ret != 0)
{
Console.Error.Write("Failed to create signature\n");
+ return false;
}
int len = SerializeSignature(sig, signature, message.Length + 4);
- if (len == -1)
+ if (len < 0)
{
Console.Error.Write("Failed to serialize signature\n");
+ return false;
}
signatureLength = len;
Pack.UInt32_To_LE((uint)len, signature, 0);
+ return true;
}
else
{
Signature2 sig = new Signature2(this);
- sign_picnic3(data, ciphertext, plaintext, message, sig);
+ bool ret = sign_picnic3(data, ciphertext, plaintext, message, sig);
+ if (!ret)
+ {
+ Console.Error.WriteLine("Failed to create signature");
+ return false;
+ }
+
int len = SerializeSignature2(sig, signature, message.Length + 4);
+ if (len < 0)
+ {
+ Console.Error.WriteLine("Failed to serialize signature");
+ return false;
+ }
+
signatureLength = len;
Pack.UInt32_To_LE((uint)len, signature, 0);
+ return true;
}
}
@@ -1160,9 +1178,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
}
if (CRYPTO_BYTES < bytesRequired)
- {
return -1;
- }
int sigByteIndex = sigOffset;
@@ -1170,7 +1186,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
sigByteIndex += PicnicUtilities.NumBytes(2 * numMPCRounds);
Array.Copy(sig.salt, 0, sigBytes, sigByteIndex, saltSizeBytes);
-
sigByteIndex += saltSizeBytes;
for (int i = 0; i < numMPCRounds; i++)
@@ -1207,10 +1222,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
return sigByteIndex - sigOffset;
}
- private int GetChallenge(byte[] challenge, int round)
- {
- return (PicnicUtilities.GetBit(challenge, 2 * round + 1) << 1) | PicnicUtilities.GetBit(challenge, 2 * round);
- }
+ private static int GetChallenge(byte[] challenge, int round) =>
+ PicnicUtilities.GetCrumbAligned(challenge, round);
private int SerializeSignature2(Signature2 sig, byte[] sigBytes, int sigOffset)
{
@@ -1239,9 +1252,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
}
if (sigBytes.Length < bytesRequired)
- {
return -1;
- }
int sigByteIndex = sigOffset;
Array.Copy(sig.challengeHash, 0, sigBytes, sigByteIndex, digestSizeBytes);
@@ -1290,13 +1301,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
{
bool status;
- /* Allocate views and commitments for all parallel iterations */
- View[][] views = new View[numMPCRounds][]; // numMPCRounds, 3
- for (int i = 0; i < numMPCRounds; i++)
- {
- views[i] = new View[3];
- }
-
byte[][][] AS = new byte[numMPCRounds][][]; // numMPCRounds, numMPCParties, digestSizeBytes
for (int i = 0; i < numMPCRounds; i++)
{
@@ -1328,13 +1332,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
Tape tape = new Tape(this);
byte[] tmp = new byte[System.Math.Max(9 * stateSizeBytes, stateSizeBytes + andSizeBytes)];
- byte[] view_byte = new byte[stateSizeBytes * 4];
+
+ /* Allocate views and commitments for all parallel iterations */
+ View[][] views = new View[numMPCRounds][]; // numMPCRounds, 3
for (int k = 0; k < numMPCRounds; k++)
{
- views[k][0] = new View(this);
- views[k][1] = new View(this);
- views[k][2] = new View(this);
+ var vk = views[k] = new View[3]{ new View(this), new View(this), new View(this) };
+
// for first two players get all tape INCLUDING INPUT SHARE from seed
for (int j = 0; j < 2; j++)
{
@@ -1346,9 +1351,10 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
return -1;
}
- Array.Copy(tmp, 0, view_byte, 0, stateSizeBytes);
- PicnicUtilities.ZeroTrailingBits(view_byte, stateSizeBits);
- Pack.LE_To_UInt32(view_byte, 0, views[k][j].inputShare);
+ uint[] inputShare = vk[j].inputShare;
+ Pack.LE_To_UInt32(tmp, 0, inputShare);
+ PicnicUtilities.ZeroTrailingBits(inputShare, stateSizeBits);
+
Array.Copy(tmp, stateSizeBytes, tape.tapes[j], 0, andSizeBytes);
}
@@ -1362,19 +1368,16 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
return -1;
}
- xor_three(views[k][2].inputShare, privateKey, views[k][0].inputShare, views[k][1].inputShare,
- stateSizeBytes);
+ xor_three(vk[2].inputShare, privateKey, vk[0].inputShare, vk[1].inputShare);
tape.pos = 0;
uint[] tmp_int = Pack.LE_To_UInt32(tmp, 0, tmp.Length / 4);
- mpc_LowMC(tape, views[k], plaintext, tmp_int);
+ mpc_LowMC(tape, vk, plaintext, tmp_int);
Pack.UInt32_To_LE(tmp_int, tmp, 0);
uint[] temp = new uint[LOWMC_MAX_WORDS];
- xor_three(temp, views[k][0].outputShare, views[k][1].outputShare, views[k][2].outputShare,
- stateSizeBytes);
-
+ xor_three(temp, vk[0].outputShare, vk[1].outputShare, vk[2].outputShare);
if (!SubarrayEquals(temp, pubKey, stateSizeWords))
{
@@ -1383,38 +1386,28 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
}
//Committing
- Commit(seeds, ((seedLen) * k) + 0 * seedSizeBytes, views[k][0], AS[k][0]);
- Commit(seeds, ((seedLen) * k) + 1 * seedSizeBytes, views[k][1], AS[k][1]);
- Commit(seeds, ((seedLen) * k) + 2 * seedSizeBytes, views[k][2], AS[k][2]);
+ Commit(seeds, ((seedLen) * k) + 0 * seedSizeBytes, vk[0], AS[k][0]);
+ Commit(seeds, ((seedLen) * k) + 1 * seedSizeBytes, vk[1], AS[k][1]);
+ Commit(seeds, ((seedLen) * k) + 2 * seedSizeBytes, vk[2], AS[k][2]);
if (transform == TRANSFORM_UR)
{
- G(0, seeds, ((seedLen) * k) + 0 * seedSizeBytes, views[k][0], gs[k][0]);
- G(1, seeds, ((seedLen) * k) + 1 * seedSizeBytes, views[k][1], gs[k][1]);
- G(2, seeds, ((seedLen) * k) + 2 * seedSizeBytes, views[k][2], gs[k][2]);
+ G(0, seeds, ((seedLen) * k) + 0 * seedSizeBytes, vk[0], gs[k][0]);
+ G(1, seeds, ((seedLen) * k) + 1 * seedSizeBytes, vk[1], gs[k][1]);
+ G(2, seeds, ((seedLen) * k) + 2 * seedSizeBytes, vk[2], gs[k][2]);
}
}
//Generating challenges
- uint[][][] viewOutputs = new uint[numMPCRounds][][]; // [3][stateSizeBytes]
- for (int i = 0; i < numMPCRounds; i++)
- {
- viewOutputs[i] = new uint[3][];
- for (int j = 0; j < 3; j++)
- {
- viewOutputs[i][j] = views[i][j].outputShare;
- }
- }
- H3(pubKey, plaintext, viewOutputs, AS, sig.challengeBits, sig.salt, message, gs);
+ H3(pubKey, plaintext, views, AS, sig.challengeBits, sig.salt, message, gs);
//Packing Z
for (int i = 0; i < numMPCRounds; i++)
{
Signature.Proof proof = sig.proofs[i];
- Prove(proof, GetChallenge(sig.challengeBits, i), seeds, ((seedLen) * i),
- views[i], AS[i],
- (transform != TRANSFORM_UR) ? null : gs[i]); //todo check if
+ Prove(proof, GetChallenge(sig.challengeBits, i), seeds, seedLen * i, views[i], AS[i],
+ (transform != TRANSFORM_UR) ? null : gs[i]); //todo check if
}
return 0;
@@ -1442,11 +1435,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
else
{
Console.Error.Write("Invalid challenge");
+ throw new ArgumentException(nameof(challenge));
}
if (challenge == 1 || challenge == 2)
{
- Array.Copy(views[2].inputShare, 0, proof.inputShare, 0, stateSizeBytes);
+ Array.Copy(views[2].inputShare, 0, proof.inputShare, 0, stateSizeWords);
}
Array.Copy(views[(challenge + 1) % 3].communicatedBits, 0, proof.communicatedBits, 0, andSizeBytes);
@@ -1459,35 +1453,63 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
}
}
- private void H3(uint[] circuitOutput, uint[] plaintext, uint[][][] viewOutputs,
+ private void H3(uint[] circuitOutput, uint[] plaintext, View[][] views,
byte[][][] AS, byte[] challengeBits, byte[] salt,
byte[] message, byte[][][] gs)
{
- byte[] hash = new byte[digestSizeBytes];
+ digest.Update((byte)1);
- /* Depending on the number of rounds, we might not set part of the last
- * byte, make sure it's always zero. */
- challengeBits[PicnicUtilities.NumBytes(numMPCRounds * 2) - 1] = 0;
+ byte[] tmp = new byte[stateSizeWords * 4];
+
+ /* Hash the output share from each view */
+ for (int i = 0; i < numMPCRounds; i++)
+ {
+ for (int j = 0; j < 3; j++)
+ {
+ Pack.UInt32_To_LE(views[i][j].outputShare, tmp, 0);
+ digest.BlockUpdate(tmp, 0, stateSizeBytes);
+ }
+ }
- digest.Update((byte) 1);
+ ImplH3(circuitOutput, plaintext, AS, challengeBits, salt, message, gs);
+ }
+
+ private void H3(uint[] circuitOutput, uint[] plaintext, uint[][][] viewOutputs,
+ byte[][][] AS, byte[] challengeBits, byte[] salt,
+ byte[] message, byte[][][] gs)
+ {
+ digest.Update((byte)1);
+
+ byte[] tmp = new byte[stateSizeWords * 4];
/* Hash the output share from each view */
for (int i = 0; i < numMPCRounds; i++)
{
for (int j = 0; j < 3; j++)
{
- digest.BlockUpdate(Pack.UInt32_To_LE(viewOutputs[i][j]), 0, stateSizeBytes);
+ Pack.UInt32_To_LE(viewOutputs[i][j], tmp, 0);
+ digest.BlockUpdate(tmp, 0, stateSizeBytes);
}
}
+ ImplH3(circuitOutput, plaintext, AS, challengeBits, salt, message, gs);
+ }
+
+ private void ImplH3(uint[] circuitOutput, uint[] plaintext, byte[][][] AS, byte[] challengeBits, byte[] salt,
+ byte[] message, byte[][][] gs)
+ {
+ byte[] hash = new byte[digestSizeBytes];
+
+ /* Depending on the number of rounds, we might not set part of the last
+ * byte, make sure it's always zero. */
+ challengeBits[PicnicUtilities.NumBytes(numMPCRounds * 2) - 1] = 0;
+
/* Hash all the commitments C */
for (int i = 0; i < numMPCRounds; i++)
{
for (int j = 0; j < 3; j++)
{
- digest.BlockUpdate( AS[i][j],
-
- 0, digestSizeBytes);
+ digest.BlockUpdate(AS[i][j], 0, digestSizeBytes);
}
}
@@ -1538,15 +1560,11 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
}
if (!isNotDone)
- {
break;
- }
}
if (!isNotDone)
- {
break;
- }
/* We need more bits; hash set hash = H_1(hash) */
digest.Update((byte) 1);
@@ -1601,7 +1619,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
current.GetData(), current.GetMatrixPointer());
}
- mpc_xor(slab, slab, stateSizeWords, 3);
+ mpc_xor(slab, slab, 3);
for (int r = 1; r <= numRounds; r++)
{
@@ -1624,7 +1642,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
mpc_xor_constant(slab, 3 * stateSizeWords,
current.GetData(), current.GetMatrixPointer(), stateSizeWords);
- mpc_xor(slab, slab, stateSizeWords, 3);
+ mpc_xor(slab, slab, 3);
}
for (int i = 0; i < 3; i++)
@@ -1687,35 +1705,24 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
/*** Functions implementing Sign ***/
private void mpc_AND(uint[] in1, uint[] in2, uint[] output, Tape rand, View[] views)
{
- uint[] r = new uint[]
- {
- PicnicUtilities.GetBit(rand.tapes[0], rand.pos),
- PicnicUtilities.GetBit(rand.tapes[1], rand.pos),
- PicnicUtilities.GetBit(rand.tapes[2], rand.pos)
- };
+ uint r0 = PicnicUtilities.GetBit(rand.tapes[0], rand.pos);
+ uint r1 = PicnicUtilities.GetBit(rand.tapes[1], rand.pos);
+ uint r2 = PicnicUtilities.GetBit(rand.tapes[2], rand.pos);
- for (int i = 0; i < 3; i++)
- {
- output[i] =
- (in1[i] & in2[(i + 1) % 3]) ^ (in1[(i + 1) % 3] & in2[i])
- ^ (in1[i] & in2[i]) ^ r[i] ^ r[(i + 1) % 3];
+ output[0] = (in1[0] & in2[1]) ^ (in1[1] & in2[0]) ^ (in1[0] & in2[0]) ^ r0 ^ r1;
+ output[1] = (in1[1] & in2[2]) ^ (in1[2] & in2[1]) ^ (in1[1] & in2[1]) ^ r1 ^ r2;
+ output[2] = (in1[2] & in2[0]) ^ (in1[0] & in2[2]) ^ (in1[2] & in2[2]) ^ r2 ^ r0;
- PicnicUtilities.SetBit(views[i].communicatedBits, rand.pos, (byte) (output[i] & 0xff));
- }
+ PicnicUtilities.SetBit(views[0].communicatedBits, rand.pos, (byte)output[0]);
+ PicnicUtilities.SetBit(views[1].communicatedBits, rand.pos, (byte)output[1]);
+ PicnicUtilities.SetBit(views[2].communicatedBits, rand.pos, (byte)output[2]);
rand.pos++;
}
- private void mpc_xor(uint[] state, uint[] input, int len, int players)
+ private void mpc_xor(uint[] state, uint[] input, int players)
{
- for (int player = 0; player < players; player++)
- {
- for (int i = 0; i < len; i++)
- {
- state[i + (players + player) * stateSizeWords] =
- state[i + (players + player) * stateSizeWords] ^ input[i+player * stateSizeWords];
- }
- }
+ Nat.XorTo(stateSizeWords * players, input, 0, state, players * stateSizeWords);
}
private void mpc_matrix_mul(uint[] output, int outputOffset, uint[] state, int stateOffset,
@@ -1734,7 +1741,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
{
for (int i = 0; i < len; i++)
{
- state[i + stateOffset] = state[i + stateOffset] ^ input[i+inOffset];
+ state[i + stateOffset] ^= input[i + inOffset];
}
}
@@ -1742,9 +1749,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
byte[] tape, int tapeLen)
{
if (tapeLen < digestSizeBytes)
- {
return false;
- }
/* Hash the seed and a constant, store the result in tape. */
digest.Update((byte) 2);
@@ -1766,11 +1771,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
private byte[] ComputeSeeds(uint[] privateKey, uint[] publicKey, uint[] plaintext, byte[] message)
{
byte[] allSeeds = new byte[seedSizeBytes * (numMPCParties * numMPCRounds) + saltSizeBytes];
+ byte[] temp = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
- digest.BlockUpdate(Pack.UInt32_To_LE(privateKey), 0, stateSizeBytes);
+ UpdateDigest(privateKey, temp);
digest.BlockUpdate(message, 0, message.Length);
- digest.BlockUpdate(Pack.UInt32_To_LE(publicKey), 0, stateSizeBytes);
- digest.BlockUpdate(Pack.UInt32_To_LE(plaintext), 0, stateSizeBytes);
+ UpdateDigest(publicKey, temp);
+ UpdateDigest(plaintext, temp);
digest.BlockUpdate(Pack.UInt32_To_LE((uint)stateSizeBits), 0, 2);
// Derive the N*T seeds + 1 salt
@@ -1779,7 +1785,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
return allSeeds;
}
- private void sign_picnic3(uint[] privateKey, uint[] pubKey, uint[] plaintext, byte[] message, Signature2 sig)
+ private bool sign_picnic3(uint[] privateKey, uint[] pubKey, uint[] plaintext, byte[] message, Signature2 sig)
{
byte[] saltAndRoot = new byte[saltSizeBytes + seedSizeBytes];
ComputeSaltAndRootSeed(saltAndRoot, privateKey, pubKey, plaintext, message);
@@ -1846,11 +1852,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
{
msgs[t] = new Msg(this);
uint[] maskedKey = Pack.LE_To_UInt32(inputs[t], 0, stateSizeWords);
- xor_array(maskedKey, maskedKey, privateKey, 0, stateSizeWords);
+ Nat.XorTo(stateSizeWords, privateKey, maskedKey);
int rv = SimulateOnline(maskedKey, tapes[t], tmp_shares, msgs[t], plaintext, pubKey);
if (rv != 0)
{
Console.Error.Write("MPC simulation failed, aborting signature\n");
+ return false;
}
Pack.UInt32_To_LE(maskedKey, inputs[t], 0);
@@ -1925,6 +1932,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
Array.Copy(C[t][sig.challengeP[P_index]], 0, sig.proofs[t].C, 0, digestSizeBytes);
}
}
+ return true;
}
private static int IndexOf(uint[] list, int len, uint value)
@@ -1942,8 +1950,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
{
if (!Contains(challengeC, numOpenedRounds, (uint)i))
{
- missingLeaves[pos] = (uint)i;
- pos++;
+ missingLeaves[pos++] = (uint)i;
}
}
@@ -1958,10 +1965,12 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
digest.BlockUpdate(Ch[t], 0, digestSizeBytes);
}
+ byte[] temp = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
+
digest.BlockUpdate(hCv, 0, digestSizeBytes);
digest.BlockUpdate(salt, 0, saltSizeBytes);
- digest.BlockUpdate(Pack.UInt32_To_LE(pubKey), 0, stateSizeBytes);
- digest.BlockUpdate(Pack.UInt32_To_LE(plaintext), 0, stateSizeBytes);
+ UpdateDigest(pubKey, temp);
+ UpdateDigest(plaintext, temp);
digest.BlockUpdate(message, 0, message.Length);
digest.OutputFinal(challengeHash, 0, digestSizeBytes);
@@ -1974,18 +1983,16 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
private static int BitsToChunks(int chunkLenBits, byte[] input, int inputLen, uint[] chunks)
{
if (chunkLenBits > inputLen * 8)
- {
return 0;
- }
- int chunkCount = ((inputLen * 8) / chunkLenBits);
+ int chunkCount = (inputLen * 8) / chunkLenBits;
for (int i = 0; i < chunkCount; i++)
{
chunks[i] = 0;
for (int j = 0; j < chunkLenBits; j++)
{
- chunks[i] += (uint) (PicnicUtilities.GetBit(input, i * chunkLenBits + j) << j);
+ chunks[i] += (uint)PicnicUtilities.GetBit(input, i * chunkLenBits + j) << j;
}
}
@@ -2003,9 +2010,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
for (int i = 0; i < position; i++)
{
if (list[i] == value)
- {
return position;
- }
}
list[position] = value;
@@ -2034,9 +2039,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
}
if (countC == numOpenedRounds)
- {
break;
- }
}
digest.Update((byte) 1);
@@ -2059,9 +2062,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
}
if (countP == numOpenedRounds)
- {
break;
- }
}
digest.Update((byte) 1);
@@ -2102,7 +2103,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
KMatricesWithPointer current = _lowmcConstants.KMatrix(this, 0);
matrix_mul(roundKey, maskedKey, current.GetData(),
current.GetMatrixPointer()); // roundKey = maskedKey * KMatrix[0]
- xor_array(state, roundKey, plaintext, 0, stateSizeWords); // state = plaintext + roundKey
+ xor_array(state, roundKey, plaintext, 0); // state = plaintext + roundKey
for (int r = 1; r <= numRounds; r++)
{
@@ -2114,12 +2115,10 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
current.GetMatrixPointer()); // state = state * LMatrix (r-1)
current = _lowmcConstants.RConstant(this, r - 1);
- xor_array(state, state, current.GetData(), current.GetMatrixPointer(),
- stateSizeWords); // state += RConstant
-
+ Nat.XorTo(stateSizeWords, current.GetData(), current.GetMatrixPointer(), state, 0); // state += RConstant
current = _lowmcConstants.KMatrix(this, r);
matrix_mul(roundKey, maskedKey, current.GetData(), current.GetMatrixPointer());
- xor_array(state, roundKey, state, 0, stateSizeWords); // state += roundKey
+ xor_array(state, roundKey, state, 0); // state += roundKey
}
if (!SubarrayEquals(state, pubKey, stateSizeWords))
@@ -2137,8 +2136,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
{
digest.BlockUpdate(seeds[i + seedsOffset], 0, seedSizeBytes);
digest.BlockUpdate(salt, 0, saltSizeBytes);
- digest.BlockUpdate(Pack.UInt32_To_LE(t), 0, 2);
- digest.BlockUpdate(Pack.UInt32_To_LE(i), 0, 2);
+ digest.BlockUpdate(Pack.UInt32_To_LE((t & 0xFFFFU) | (i << 16)), 0, 4);
digest.OutputFinal(tape.tapes[i], 0, tapeSizeBytes);
}
}
@@ -2153,7 +2151,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
if (a[i] != b[i])
return false;
}
-
return true;
}
@@ -2167,7 +2164,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
if (a[i] != b[i])
return false;
}
-
return true;
}
@@ -2267,11 +2263,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
for (int i = 0; i < len; i++)
{
if (list[i] == value)
- {
return true;
- }
}
-
return false;
}
@@ -2309,49 +2302,44 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
}
digest.BlockUpdate(salt, 0, saltSizeBytes);
- digest.BlockUpdate(Pack.UInt32_To_LE(t), 0, 2);
- digest.BlockUpdate(Pack.UInt32_To_LE(j), 0, 2);
+ digest.BlockUpdate(Pack.UInt32_To_LE((t & 0xFFFFU) | (j << 16)), 0, 4);
digest.OutputFinal(digest_arr, 0, digestSizeBytes);
}
private void ComputeSaltAndRootSeed(byte[] saltAndRoot, uint[] privateKey, uint[] pubKey, uint[] plaintext, byte[] message)
{
- //todo unify conversion
- //copy back to byte array
- byte[] privatekey_bytes = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
- byte[] pubkey_bytes = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
- byte[] plaintext_bytes = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
- Pack.UInt32_To_LE(privateKey, privatekey_bytes, 0);
- Pack.UInt32_To_LE(pubKey, pubkey_bytes, 0);
- Pack.UInt32_To_LE(plaintext, plaintext_bytes, 0);
- privatekey_bytes = Arrays.CopyOfRange(privatekey_bytes, 0, stateSizeBytes);
- pubkey_bytes = Arrays.CopyOfRange(pubkey_bytes, 0, stateSizeBytes);
- plaintext_bytes = Arrays.CopyOfRange(plaintext_bytes, 0, stateSizeBytes);
- //
+ byte[] temp = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
// init done in constructor
- digest.BlockUpdate(privatekey_bytes, 0, stateSizeBytes);
+ UpdateDigest(privateKey, temp);
digest.BlockUpdate(message, 0, message.Length);
- digest.BlockUpdate(pubkey_bytes, 0, stateSizeBytes);
- digest.BlockUpdate(plaintext_bytes, 0, stateSizeBytes);
- digest.BlockUpdate(Pack.UInt16_To_LE((ushort) (stateSizeBits & 0xffff)), 0, 2);
+ UpdateDigest(pubKey, temp);
+ UpdateDigest(plaintext, temp);
+ Pack.UInt16_To_LE((ushort)stateSizeBits, temp);
+ digest.BlockUpdate(temp, 0, 2);
digest.OutputFinal(saltAndRoot, 0, saltAndRoot.Length);
}
+ private void UpdateDigest(uint[] block, byte[] temp)
+ {
+ Pack.UInt32_To_LE(block, temp, 0);
+ digest.BlockUpdate(temp, 0, stateSizeBytes);
+ }
+
private static bool is_picnic3(int parameters)
{
- return parameters == 7 /*Picnic3_L1*/ ||
- parameters == 8 /*Picnic3_L3*/ ||
- parameters == 9 /*Picnic3_L5*/ ;
+ return parameters == 7 /*Picnic3_L1*/
+ || parameters == 8 /*Picnic3_L3*/
+ || parameters == 9 /*Picnic3_L5*/;
}
//todo return int;
internal void crypto_sign_keypair(byte[] pk, byte[] sk, SecureRandom random)
{
- // set array sizes
- byte[] plaintext_bytes = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
- byte[] ciphertext_bytes = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
- byte[] data_bytes = new byte[PICNIC_MAX_LOWMC_BLOCK_SIZE];
+ // set array sizes sufficient to be worked with as words
+ byte[] plaintext_bytes = new byte[stateSizeWords * 4];
+ byte[] ciphertext_bytes = new byte[stateSizeWords * 4];
+ byte[] data_bytes = new byte[stateSizeWords * 4];
picnic_keygen(plaintext_bytes, ciphertext_bytes, data_bytes, random);
picnic_write_public_key(ciphertext_bytes, plaintext_bytes, pk);
@@ -2400,13 +2388,13 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
// generate a private key
random.NextBytes(data_bytes, 0, stateSizeBytes);
- PicnicUtilities.ZeroTrailingBits(data_bytes, stateSizeBits);
Pack.LE_To_UInt32(data_bytes, 0, data);
+ PicnicUtilities.ZeroTrailingBits(data, stateSizeBits);
// generate a plaintext block
random.NextBytes(plaintext_bytes, 0, stateSizeBytes);
- PicnicUtilities.ZeroTrailingBits(plaintext_bytes, stateSizeBits);
Pack.LE_To_UInt32(plaintext_bytes, 0, plaintext);
+ PicnicUtilities.ZeroTrailingBits(plaintext, stateSizeBits);
// compute ciphertext
LowMCEnc(plaintext, ciphertext, data);
@@ -2430,7 +2418,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
KMatricesWithPointer current = _lowmcConstants.KMatrix(this, 0);
matrix_mul(roundKey, key, current.GetData(), current.GetMatrixPointer());
- xor_array(output, output, roundKey, 0, stateSizeWords);
+ Nat.XorTo(stateSizeWords, roundKey, output);
for (int r = 1; r <= numRounds; r++)
{
@@ -2443,8 +2431,8 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
matrix_mul(output, output, current.GetData(), current.GetMatrixPointer());
current = _lowmcConstants.RConstant(this, r - 1);
- xor_array(output, output, current.GetData(), current.GetMatrixPointer(), stateSizeWords);
- xor_array(output, output, roundKey, 0, stateSizeWords);
+ Nat.XorTo(stateSizeWords, current.GetData(), current.GetMatrixPointer(), output, 0);
+ Nat.XorTo(stateSizeWords, roundKey, output);
}
}
@@ -2462,21 +2450,17 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
}
}
- private void xor_three(uint[] output, uint[] in1, uint[] in2, uint[] in3, int lenBytes)
+ private void xor_three(uint[] output, uint[] in1, uint[] in2, uint[] in3)
{
- int wholeWords = stateSizeWords;
- for (int i = 0; i < wholeWords; i++)
+ for (int i = 0; i < stateSizeWords; i++)
{
output[i] = in1[i] ^ in2[i] ^ in3[i];
}
}
- internal void xor_array(uint[] output, uint[] in1, uint[] in2, int in2_offset, int length)
+ internal void xor_array(uint[] output, uint[] in1, uint[] in2, int in2_offset)
{
- for (int i = 0; i < length; i++)
- {
- output[i] = in1[i] ^ in2[i + in2_offset];
- }
+ Nat.Xor(stateSizeWords, in1, 0, in2, in2_offset, output, 0);
}
internal void matrix_mul(uint[] output, uint[] state, uint[] matrix, int matrixOffset)
diff --git a/crypto/src/pqc/crypto/picnic/PicnicSigner.cs b/crypto/src/pqc/crypto/picnic/PicnicSigner.cs
index b391b62bc..f76eb1af3 100644
--- a/crypto/src/pqc/crypto/picnic/PicnicSigner.cs
+++ b/crypto/src/pqc/crypto/picnic/PicnicSigner.cs
@@ -1,5 +1,7 @@
+using System;
+
using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Crypto.Utilities;
using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Pqc.Crypto.Picnic
@@ -33,18 +35,20 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
byte[] sig = new byte[engine.GetSignatureSize(message.Length)];
engine.crypto_sign(sig, message, privKey.GetEncoded());
- return Arrays.CopyOfRange(sig, message.Length + 4, engine.GetTrueSignatureSize() + message.Length);
+ byte[] signature = new byte[engine.GetTrueSignatureSize()];
+ Array.Copy(sig, message.Length + 4, signature, 0, engine.GetTrueSignatureSize());
+ return signature;
}
public bool VerifySignature(byte[] message, byte[] signature)
{
PicnicEngine engine = pubKey.Parameters.GetEngine();
byte[] verify_message = new byte[message.Length];
- bool verify = engine.crypto_sign_open(verify_message, signature, pubKey.GetEncoded());
+ byte[] attached_signature = Arrays.ConcatenateAll(Pack.UInt32_To_LE((uint)signature.Length), message, signature);
+
+ bool verify = engine.crypto_sign_open(verify_message, attached_signature, pubKey.GetEncoded());
if (!Arrays.AreEqual(message, verify_message))
- {
return false;
- }
return verify;
}
diff --git a/crypto/src/pqc/crypto/picnic/PicnicUtilities.cs b/crypto/src/pqc/crypto/picnic/PicnicUtilities.cs
index d77d7ce29..338729d02 100644
--- a/crypto/src/pqc/crypto/picnic/PicnicUtilities.cs
+++ b/crypto/src/pqc/crypto/picnic/PicnicUtilities.cs
@@ -1,7 +1,3 @@
-#if NETCOREAPP3_0_OR_GREATER
-using System.Runtime.Intrinsics.X86;
-#endif
-
using Org.BouncyCastle.Utilities;
namespace Org.BouncyCastle.Pqc.Crypto.Picnic
@@ -66,6 +62,14 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
return (byte)((array[arrayPos] >> bitPos) & 1);
}
+ /* Get a crumb (i.e. two bits) from a byte array. */
+ internal static byte GetCrumbAligned(byte[] array, int crumbNumber)
+ {
+ int arrayPos = crumbNumber >> 2, bitPos = ((crumbNumber << 1) & 6) ^ 6;
+ uint b = (uint)array[arrayPos] >> bitPos;
+ return (byte)((b & 1) << 1 | (b & 2) >> 1);
+ }
+
internal static uint GetBit(uint word, int bitNumber)
{
int bitPos = bitNumber ^ 7;
@@ -106,13 +110,27 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
array[arrayPos] = t;
}
- internal static void ZeroTrailingBits(byte[] data, int bitLength)
+ internal static void ZeroTrailingBits(uint[] data, int bitLength)
{
- int partial = bitLength & 7;
- if (partial != 0)
+ int partialWord = bitLength & 31;
+ if (partialWord != 0)
{
- data[bitLength >> 3] &= (byte)(0xFF00 >> partial);
+ data[bitLength >> 5] &= GetTrailingBitsMask(bitLength);
}
}
+
+ internal static uint GetTrailingBitsMask(int bitLength)
+ {
+ int partialShift = bitLength & ~7;
+ uint mask = ~(0xFFFFFFFFU << partialShift);
+
+ int partialByte = bitLength & 7;
+ if (partialByte != 0)
+ {
+ mask ^= ((0xFF00U >> partialByte) & 0xFFU) << partialShift;
+ }
+
+ return mask;
+ }
}
}
diff --git a/crypto/src/pqc/crypto/picnic/Signature.cs b/crypto/src/pqc/crypto/picnic/Signature.cs
index edb70b5a6..6605dc1ea 100644
--- a/crypto/src/pqc/crypto/picnic/Signature.cs
+++ b/crypto/src/pqc/crypto/picnic/Signature.cs
@@ -2,9 +2,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
{
internal class Signature
{
- internal byte[] challengeBits;
- internal byte[] salt;
- internal Proof[] proofs;
+ internal readonly byte[] challengeBits;
+ internal readonly byte[] salt;
+ internal readonly Proof[] proofs;
internal Signature(PicnicEngine engine)
{
@@ -19,20 +19,20 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
internal class Proof
{
- internal byte[] seed1;
- internal byte[] seed2;
+ internal readonly byte[] seed1;
+ internal readonly byte[] seed2;
- internal uint[] inputShare; // Input share of the party which does not derive it from the seed (not included if challenge is 0)
+ internal readonly uint[] inputShare; // Input share of the party which does not derive it from the seed (not included if challenge is 0)
- internal byte[] communicatedBits;
- internal byte[] view3Commitment;
- internal byte[] view3UnruhG; // we include the max length, but we will only serialize the bytes we use
+ internal readonly byte[] communicatedBits;
+ internal readonly byte[] view3Commitment;
+ internal readonly byte[] view3UnruhG; // we include the max length, but we will only serialize the bytes we use
internal Proof(PicnicEngine engine)
{
seed1 = new byte[engine.seedSizeBytes];
seed2 = new byte[engine.seedSizeBytes];
- inputShare = new uint[engine.stateSizeBytes];
+ inputShare = new uint[engine.stateSizeWords];
communicatedBits = new byte[engine.andSizeBytes];
view3Commitment = new byte[engine.digestSizeBytes];
if (engine.UnruhGWithInputBytes > 0)
diff --git a/crypto/src/pqc/crypto/picnic/Tape.cs b/crypto/src/pqc/crypto/picnic/Tape.cs
index b1dc9ca1d..63c747399 100644
--- a/crypto/src/pqc/crypto/picnic/Tape.cs
+++ b/crypto/src/pqc/crypto/picnic/Tape.cs
@@ -1,5 +1,5 @@
using Org.BouncyCastle.Crypto.Utilities;
-using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Math.Raw;
namespace Org.BouncyCastle.Pqc.Crypto.Picnic
{
@@ -67,9 +67,9 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
// {System.out.printf("%08x ", key[i]);}System.out.Println();
- if(inputs != null)
+ if (inputs != null)
{
- Pack.UInt32_To_LE(Arrays.CopyOf(key, engine.stateSizeWords), inputs, 0);
+ Pack.UInt32_To_LE(key, 0, engine.stateSizeWords, inputs, 0);
}
@@ -78,7 +78,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
current = engine._lowmcConstants.KMatrix(engine, r);
engine.matrix_mul(roundKey, key, current.GetData(), current.GetMatrixPointer()); // roundKey = key * KMatrix(r)
- engine.xor_array(x, x, roundKey, 0, engine.stateSizeWords);
+ Nat.XorTo(engine.stateSizeWords, roundKey, x);
current = engine._lowmcConstants.LMatrixInv(engine, r-1);
engine.matrix_mul(y, x, current.GetData(), current.GetMatrixPointer());
diff --git a/crypto/src/pqc/crypto/picnic/Tree.cs b/crypto/src/pqc/crypto/picnic/Tree.cs
index 52b36f1c8..beffdcb55 100644
--- a/crypto/src/pqc/crypto/picnic/Tree.cs
+++ b/crypto/src/pqc/crypto/picnic/Tree.cs
@@ -148,9 +148,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
{
inLen -= (uint)engine.seedSizeBytes;
if (inLen < 0)
- {
return -1;
- }
System.Array.Copy(input, i * engine.seedSizeBytes, this.nodes[revealed[i]], 0, engine.seedSizeBytes);
this.haveNode[revealed[i]] = true;
diff --git a/crypto/src/pqc/crypto/picnic/View.cs b/crypto/src/pqc/crypto/picnic/View.cs
index cac47631a..f295f5707 100644
--- a/crypto/src/pqc/crypto/picnic/View.cs
+++ b/crypto/src/pqc/crypto/picnic/View.cs
@@ -2,15 +2,15 @@ namespace Org.BouncyCastle.Pqc.Crypto.Picnic
{
internal class View
{
- internal uint[] inputShare;
- internal byte[] communicatedBits;
- internal uint[] outputShare;
+ internal readonly uint[] inputShare;
+ internal readonly byte[] communicatedBits;
+ internal readonly uint[] outputShare;
internal View(PicnicEngine engine)
{
- inputShare = new uint[engine.stateSizeBytes];
+ inputShare = new uint[engine.stateSizeWords];
communicatedBits = new byte[engine.andSizeBytes];
- outputShare = new uint[engine.stateSizeBytes];
+ outputShare = new uint[engine.stateSizeWords];
}
}
}
diff --git a/crypto/test/src/pqc/crypto/test/PicnicVectorTest.cs b/crypto/test/src/pqc/crypto/test/PicnicVectorTest.cs
index c3c00cba6..6ceb54697 100644
--- a/crypto/test/src/pqc/crypto/test/PicnicVectorTest.cs
+++ b/crypto/test/src/pqc/crypto/test/PicnicVectorTest.cs
@@ -6,6 +6,7 @@ using NUnit.Framework;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Pqc.Crypto.Picnic;
using Org.BouncyCastle.Pqc.Crypto.Utilities;
+using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.Utilities.Test;
@@ -33,6 +34,36 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests
private static readonly IEnumerable<string> TestVectorFiles = Parameters.Keys;
+ [Test]
+ public void TestPicnicRandom()
+ {
+ byte[] msg = Strings.ToByteArray("Hello World!");
+ PicnicKeyPairGenerator keyGen = new PicnicKeyPairGenerator();
+
+ SecureRandom random = new SecureRandom();
+
+ keyGen.Init(new PicnicKeyGenerationParameters(random, PicnicParameters.picnic3l1));
+
+ for (int i = 0; i != 100; i++)
+ {
+ AsymmetricCipherKeyPair keyPair = keyGen.GenerateKeyPair();
+
+ // sign
+ PicnicSigner signer = new PicnicSigner();
+ PicnicPrivateKeyParameters skparam = (PicnicPrivateKeyParameters)keyPair.Private;
+ signer.Init(true, skparam);
+
+ byte[] sigGenerated = signer.GenerateSignature(msg);
+
+ // verify
+ PicnicSigner verifier = new PicnicSigner();
+ PicnicPublicKeyParameters pkparam = (PicnicPublicKeyParameters)keyPair.Public;
+ verifier.Init(false, pkparam);
+
+ Assert.True(verifier.VerifySignature(msg, sigGenerated), "count = " + i);
+ }
+ }
+
[TestCaseSource(nameof(TestVectorFiles))]
[Parallelizable(ParallelScope.All)]
public void TV(string testVectorFile)
@@ -82,7 +113,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Tests
Assert.True(smlen == attachedSig.Length, name + " " + count + ": signature length");
signer.Init(false, pubParams);
- Assert.True(signer.VerifySignature(msg, attachedSig), (name + " " + count + ": signature verify"));
+ Assert.True(signer.VerifySignature(msg, sigGenerated), (name + " " + count + ": signature verify"));
Assert.True(Arrays.AreEqual(sigExpected, attachedSig), name + " " + count + ": signature gen match");
}
|