diff options
author | royb <roy.basmacier@primekey.com> | 2022-09-29 18:31:17 -0400 |
---|---|---|
committer | royb <roy.basmacier@primekey.com> | 2022-09-29 18:31:17 -0400 |
commit | f770ff91867405c13e8b28118324693a8917b78b (patch) | |
tree | 00d08c47c8529c644e7de471d4f34246ce2b6307 /crypto/src | |
parent | Added ASN.1 key encoding/decoding for Kyber, Dilithium, and Falcon. (diff) | |
parent | Grain128Aead performance, constant-time (diff) | |
download | BouncyCastle.NET-ed25519-f770ff91867405c13e8b28118324693a8917b78b.tar.xz |
Merge remote-tracking branch 'origin/master'
Diffstat (limited to 'crypto/src')
-rw-r--r-- | crypto/src/crypto/engines/Grain128AEADEngine.cs | 564 | ||||
-rw-r--r-- | crypto/src/crypto/modes/OCBBlockCipher.cs | 27 | ||||
-rw-r--r-- | crypto/src/pqc/crypto/crystals/dilithium/Symmetric.cs | 5 | ||||
-rw-r--r-- | crypto/src/pqc/crypto/crystals/kyber/Symmetric.cs | 8 | ||||
-rw-r--r-- | crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs | 1 |
5 files changed, 585 insertions, 20 deletions
diff --git a/crypto/src/crypto/engines/Grain128AEADEngine.cs b/crypto/src/crypto/engines/Grain128AEADEngine.cs new file mode 100644 index 000000000..174d010f3 --- /dev/null +++ b/crypto/src/crypto/engines/Grain128AEADEngine.cs @@ -0,0 +1,564 @@ +using System; +using System.IO; + +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Crypto.Utilities; + +namespace Org.BouncyCastle.Crypto.Engines +{ + public sealed class Grain128AeadEngine + : IAeadCipher + { + /** + * Constants + */ + private static readonly int STATE_SIZE = 4; + + /** + * Variables to hold the state of the engine during encryption and + * decryption + */ + private byte[] workingKey; + private byte[] workingIV; + private uint[] lfsr; + private uint[] nfsr; + private uint[] authAcc; + private uint[] authSr; + + private bool initialised = false; + private bool aadFinished = false; + private MemoryStream aadData = new MemoryStream(); + + private byte[] mac; + + public string AlgorithmName => "Grain-128AEAD"; + + /** + * Initialize a Grain-128AEAD cipher. + * + * @param forEncryption Whether or not we are for encryption. + * @param param The parameters required to set up the cipher. + * @throws ArgumentException If the params argument is inappropriate. + */ + public void Init(bool forEncryption, ICipherParameters param) + { + /** + * Grain encryption and decryption is completely symmetrical, so the + * 'forEncryption' is irrelevant. + */ + if (!(param is ParametersWithIV ivParams)) + throw new ArgumentException("Grain-128AEAD Init parameters must include an IV"); + + byte[] iv = ivParams.GetIV(); + + if (iv == null || iv.Length != 12) + throw new ArgumentException("Grain-128AEAD requires exactly 12 bytes of IV"); + + if (!(ivParams.Parameters is KeyParameter key)) + throw new ArgumentException("Grain-128AEAD Init parameters must include a key"); + + byte[] keyBytes = key.GetKey(); + if (keyBytes.Length != 16) + throw new ArgumentException("Grain-128AEAD key must be 128 bits long"); + + /** + * Initialize variables. + */ + workingIV = new byte[keyBytes.Length]; + workingKey = keyBytes; + lfsr = new uint[STATE_SIZE]; + nfsr = new uint[STATE_SIZE]; + authAcc = new uint[2]; + authSr = new uint[2]; + + Array.Copy(iv, 0, workingIV, 0, iv.Length); + + Reset(); + } + + /** + * 320 clocks initialization phase. + */ + private void InitGrain() + { + for (int i = 0; i < 320; ++i) + { + uint outputZ = GetOutput(); + nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0] ^ outputZ) & 1); + lfsr = Shift(lfsr, (GetOutputLFSR() ^ outputZ) & 1); + } + for (int quotient = 0; quotient < 8; ++quotient) + { + for (int remainder = 0; remainder < 8; ++remainder) + { + uint outputZ = GetOutput(); + nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0] ^ outputZ ^ (uint)((workingKey[quotient]) >> remainder)) & 1); + lfsr = Shift(lfsr, (GetOutputLFSR() ^ outputZ ^ (uint)((workingKey[quotient + 8]) >> remainder)) & 1); + } + } + for (int quotient = 0; quotient < 2; ++quotient) + { + for (int remainder = 0; remainder < 32; ++remainder) + { + uint outputZ = GetOutput(); + nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0]) & 1); + lfsr = Shift(lfsr, (GetOutputLFSR()) & 1); + authAcc[quotient] |= outputZ << remainder; + } + } + for (int quotient = 0; quotient < 2; ++quotient) + { + for (int remainder = 0; remainder < 32; ++remainder) + { + uint outputZ = GetOutput(); + nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0]) & 1); + lfsr = Shift(lfsr, (GetOutputLFSR()) & 1); + authSr[quotient] |= outputZ << remainder; + } + } + initialised = true; + } + + /** + * Get output from non-linear function g(x). + * + * @return Output from NFSR. + */ + private uint GetOutputNFSR() + { + uint b0 = nfsr[0]; + uint b3 = nfsr[0] >> 3; + uint b11 = nfsr[0] >> 11; + uint b13 = nfsr[0] >> 13; + uint b17 = nfsr[0] >> 17; + uint b18 = nfsr[0] >> 18; + uint b22 = nfsr[0] >> 22; + uint b24 = nfsr[0] >> 24; + uint b25 = nfsr[0] >> 25; + uint b26 = nfsr[0] >> 26; + uint b27 = nfsr[0] >> 27; + uint b40 = nfsr[1] >> 8; + uint b48 = nfsr[1] >> 16; + uint b56 = nfsr[1] >> 24; + uint b59 = nfsr[1] >> 27; + uint b61 = nfsr[1] >> 29; + uint b65 = nfsr[2] >> 1; + uint b67 = nfsr[2] >> 3; + uint b68 = nfsr[2] >> 4; + uint b70 = nfsr[2] >> 6; + uint b78 = nfsr[2] >> 14; + uint b82 = nfsr[2] >> 18; + uint b84 = nfsr[2] >> 20; + uint b88 = nfsr[2] >> 24; + uint b91 = nfsr[2] >> 27; + uint b92 = nfsr[2] >> 28; + uint b93 = nfsr[2] >> 29; + uint b95 = nfsr[2] >> 31; + uint b96 = nfsr[3]; + + return (b0 ^ b26 ^ b56 ^ b91 ^ b96 ^ b3 & b67 ^ b11 & b13 ^ b17 & b18 + ^ b27 & b59 ^ b40 & b48 ^ b61 & b65 ^ b68 & b84 ^ b22 & b24 & b25 ^ b70 & b78 & b82 ^ b88 & b92 & b93 & b95) & 1; + } + + /** + * Get output from linear function f(x). + * + * @return Output from LFSR. + */ + private uint GetOutputLFSR() + { + uint s0 = lfsr[0]; + uint s7 = lfsr[0] >> 7; + uint s38 = lfsr[1] >> 6; + uint s70 = lfsr[2] >> 6; + uint s81 = lfsr[2] >> 17; + uint s96 = lfsr[3]; + + return (s0 ^ s7 ^ s38 ^ s70 ^ s81 ^ s96) & 1; + } + + /** + * Get output from output function h(x). + * + * @return y_t. + */ + private uint GetOutput() + { + uint b2 = nfsr[0] >> 2; + uint b12 = nfsr[0] >> 12; + uint b15 = nfsr[0] >> 15; + uint b36 = nfsr[1] >> 4; + uint b45 = nfsr[1] >> 13; + uint b64 = nfsr[2]; + uint b73 = nfsr[2] >> 9; + uint b89 = nfsr[2] >> 25; + uint b95 = nfsr[2] >> 31; + uint s8 = lfsr[0] >> 8; + uint s13 = lfsr[0] >> 13; + uint s20 = lfsr[0] >> 20; + uint s42 = lfsr[1] >> 10; + uint s60 = lfsr[1] >> 28; + uint s79 = lfsr[2] >> 15; + uint s93 = lfsr[2] >> 29; + uint s94 = lfsr[2] >> 30; + + return ((b12 & s8) ^ (s13 & s20) ^ (b95 & s42) ^ (s60 & s79) ^ (b12 & b95 & s94) ^ s93 + ^ b2 ^ b15 ^ b36 ^ b45 ^ b64 ^ b73 ^ b89) & 1; + } + + /** + * Shift array 1 bit and add val to index.Length - 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 uint[] Shift(uint[] array, uint 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; + } + + /** + * Set keys, reset cipher. + * + * @param keyBytes The key. + * @param ivBytes The IV. + */ + private void SetKey(byte[] keyBytes, byte[] ivBytes) + { + ivBytes[12] = 0xFF; + ivBytes[13] = 0xFF; + ivBytes[14] = 0xFF; + ivBytes[15] = 0x7F; + workingKey = keyBytes; + workingIV = ivBytes; + + /** + * Load NFSR and LFSR + */ + Pack.LE_To_UInt32(workingKey, 0, nfsr); + Pack.LE_To_UInt32(workingIV, 0, lfsr); + } + + public int ProcessBytes(byte[] input, int inOff, int len, byte[] output, int outOff) + { + Check.DataLength(input, inOff, len, "input buffer too short"); + Check.OutputLength(output, outOff, len, "output buffer too short"); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return ProcessBytes(input.AsSpan(inOff, len), output.AsSpan(outOff)); +#else + if (!initialised) + throw new ArgumentException(AlgorithmName + " not initialised"); + + if (!aadFinished) + { + DoProcessAADBytes(aadData.GetBuffer(), 0, (int)aadData.Length); + aadFinished = true; + } + + GetKeyStream(input, inOff, len, output, outOff); + return len; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessBytes(ReadOnlySpan<byte> input, Span<byte> output) + { + Check.OutputLength(output, input.Length, "output buffer too short"); + + if (!initialised) + throw new ArgumentException(AlgorithmName + " not initialised"); + + if (!aadFinished) + { + DoProcessAADBytes(aadData.GetBuffer(), 0, (int)aadData.Length); + aadFinished = true; + } + + GetKeyStream(input, output); + return input.Length; + } +#endif + + public void Reset() + { + Reset(true); + } + + private void Reset(bool clearMac) + { + if (clearMac) + { + this.mac = null; + } + this.aadData.SetLength(0); + this.aadFinished = false; + + SetKey(workingKey, workingIV); + InitGrain(); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + private void GetKeyStream(ReadOnlySpan<byte> input, Span<byte> output) + { + int len = input.Length; + for (int i = 0; i < len; ++i) + { + uint cc = 0, input_i = input[i]; + for (int j = 0; j < 8; ++j) + { + uint outputZ = GetOutput(); + nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0]) & 1); + lfsr = Shift(lfsr, (GetOutputLFSR()) & 1); + + uint input_i_j = (input_i >> j) & 1U; + cc |= (input_i_j ^ outputZ) << j; + + //if (input_i_j != 0) + //{ + // Accumulate(); + //} + uint mask = 0U - 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); + } + output[i] = (byte)cc; + } + } +#else + private void GetKeyStream(byte[] input, int inOff, int len, byte[] ciphertext, int outOff) + { + for (int i = 0; i < len; ++i) + { + uint cc = 0, input_i = input[inOff + i]; + for (int j = 0; j < 8; ++j) + { + uint outputZ = GetOutput(); + nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0]) & 1); + lfsr = Shift(lfsr, (GetOutputLFSR()) & 1); + + uint input_i_j = (input_i >> j) & 1U; + cc |= (input_i_j ^ outputZ) << j; + + //if (input_i_j != 0) + //{ + // Accumulate(); + //} + uint mask = 0U - 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); + } + ciphertext[outOff + i] = (byte)cc; + } + } +#endif + + public byte ReturnByte(byte input) + { + if (!initialised) + throw new ArgumentException(AlgorithmName + " not initialised"); + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + Span<byte> plaintext = stackalloc byte[1]{ input }; + Span<byte> ciphertext = stackalloc byte[1]; + GetKeyStream(plaintext, ciphertext); + return ciphertext[0]; +#else + byte[] plaintext = new byte[1]{ input }; + byte[] ciphertext = new byte[1]; + GetKeyStream(plaintext, 0, 1, ciphertext, 0); + return ciphertext[0]; +#endif + } + + public void ProcessAadByte(byte input) + { + if (aadFinished) + throw new ArgumentException("associated data must be added before plaintext/ciphertext"); + + aadData.WriteByte(input); + } + + public void ProcessAadBytes(byte[] input, int inOff, int len) + { + if (aadFinished) + throw new ArgumentException("associated data must be added before plaintext/ciphertext"); + + aadData.Write(input, inOff, len); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public void ProcessAadBytes(ReadOnlySpan<byte> input) + { + if (aadFinished) + throw new ArgumentException("associated data must be added before plaintext/ciphertext"); + + aadData.Write(input); + } +#endif + + private void Accumulate() + { + authAcc[0] ^= authSr[0]; + authAcc[1] ^= authSr[1]; + } + + private void AuthShift(uint val) + { + authSr[0] = (authSr[0] >> 1) | (authSr[1] << 31); + authSr[1] = (authSr[1] >> 1) | (val << 31); + } + + public int ProcessByte(byte input, byte[] output, int outOff) + { + return ProcessBytes(new byte[]{ input }, 0, 1, output, outOff); + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int ProcessByte(byte input, Span<byte> output) + { + return ProcessBytes(stackalloc byte[1]{ input }, output); + } +#endif + + private void DoProcessAADBytes(byte[] input, int inOff, 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 = LenLength(len); + ader = new byte[aderlen + 1 + len]; + ader[0] = (byte)(0x80 | (uint)aderlen); + uint tmp = (uint)len; + for (int i = 0; i < aderlen; ++i) + { + ader[1 + i] = (byte)tmp; + tmp >>= 8; + } + } + for (int i = 0; i < len; ++i) + { + ader[1 + aderlen + i] = input[inOff + i]; + } + + for (int i = 0; i < ader.Length; ++i) + { + uint ader_i = ader[i]; + for (int j = 0; j < 8; ++j) + { + nfsr = Shift(nfsr, (GetOutputNFSR() ^ lfsr[0]) & 1); + lfsr = Shift(lfsr, (GetOutputLFSR()) & 1); + + uint ader_i_j = (ader_i >> j) & 1U; + //if (ader_i_j != 0) + //{ + // Accumulate(); + //} + uint mask = 0U - ader_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); + } + } + } + + public int DoFinal(byte[] output, int outOff) + { +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + return DoFinal(output.AsSpan(outOff)); +#else + if (!aadFinished) + { + DoProcessAADBytes(aadData.GetBuffer(), 0, (int)aadData.Length); + aadFinished = true; + } + + Accumulate(); + + this.mac = Pack.UInt32_To_LE(authAcc); + + Array.Copy(mac, 0, output, outOff, mac.Length); + + Reset(false); + + return mac.Length; +#endif + } + +#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER + public int DoFinal(Span<byte> output) + { + if (!aadFinished) + { + DoProcessAADBytes(aadData.GetBuffer(), 0, (int)aadData.Length); + aadFinished = true; + } + + Accumulate(); + + this.mac = Pack.UInt32_To_LE(authAcc); + + mac.CopyTo(output); + + Reset(false); + + return mac.Length; + } +#endif + + public byte[] GetMac() + { + return mac; + } + + public int GetUpdateOutputSize(int len) + { + return len; + } + + public int GetOutputSize(int len) + { + return len + 8; + } + + private static int LenLength(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/crypto/src/crypto/modes/OCBBlockCipher.cs b/crypto/src/crypto/modes/OCBBlockCipher.cs index 9ba60dd49..8cc701cca 100644 --- a/crypto/src/crypto/modes/OCBBlockCipher.cs +++ b/crypto/src/crypto/modes/OCBBlockCipher.cs @@ -6,19 +6,20 @@ using Org.BouncyCastle.Utilities; namespace Org.BouncyCastle.Crypto.Modes { - /** - * An implementation of <a href="http://tools.ietf.org/html/rfc7253">RFC 7253 on The OCB - * Authenticated-Encryption Algorithm</a>, licensed per: - * - * <blockquote><p><a href="http://www.cs.ucdavis.edu/~rogaway/ocb/license1.pdf">License for - * Open-Source Software Implementations of OCB</a> (Jan 9, 2013) - 'License 1'<br/> - * Under this license, you are authorized to make, use, and distribute open-source software - * implementations of OCB. This license terminates for you if you sue someone over their open-source - * software implementation of OCB claiming that you have a patent covering their implementation. - * </p><p> - * This is a non-binding summary of a legal document (the link above). The parameters of the license - * are specified in the license document and that document is controlling.</p></blockquote> - */ + /// <summary>An implementation of <a href="https://tools.ietf.org/html/rfc7253">RFC 7253 on The OCB + /// Authenticated-Encryption Algorithm</a>.</summary> + /// <remarks> + /// For those still concerned about the original patents around this, please see: + /// <para>https://mailarchive.ietf.org/arch/msg/cfrg/qLTveWOdTJcLn4HP3ev-vrj05Vg/</para> + /// Text reproduced below: + /// <para> + /// Phillip Rogaway<rogaway@cs.ucdavis.edu&rt; Sat, 27 February 2021 02:46 UTC + /// + /// I can confirm that I have abandoned all OCB patents and placed into the public domain all OCB-related IP of + /// mine. While I have been telling people this for quite some time, I don't think I ever made a proper announcement + /// to the CFRG or on the OCB webpage. Consider that done. + /// </para> + /// </remarks> public class OcbBlockCipher : IAeadBlockCipher { diff --git a/crypto/src/pqc/crypto/crystals/dilithium/Symmetric.cs b/crypto/src/pqc/crypto/crystals/dilithium/Symmetric.cs index a46c12d90..d025b98fd 100644 --- a/crypto/src/pqc/crypto/crystals/dilithium/Symmetric.cs +++ b/crypto/src/pqc/crypto/crystals/dilithium/Symmetric.cs @@ -1,4 +1,5 @@ -using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; @@ -34,7 +35,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Dilithium public AesSymmetric() : base(64, 64) { - cipher = new SicBlockCipher(new AesEngine()); + cipher = new SicBlockCipher(AesUtilities.CreateEngine()); } private void Aes128(byte[] output, int offset, int size) diff --git a/crypto/src/pqc/crypto/crystals/kyber/Symmetric.cs b/crypto/src/pqc/crypto/crystals/kyber/Symmetric.cs index 1ceb76b98..b618d7bce 100644 --- a/crypto/src/pqc/crypto/crystals/kyber/Symmetric.cs +++ b/crypto/src/pqc/crypto/crystals/kyber/Symmetric.cs @@ -1,7 +1,7 @@ -using Org.BouncyCastle.Crypto.Digests; -using System; +using System; + using Org.BouncyCastle.Crypto; -using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Utilities; @@ -102,7 +102,7 @@ namespace Org.BouncyCastle.Pqc.Crypto.Crystals.Kyber { this.sha256Digest = new Sha256Digest(); this.sha512Digest = new Sha512Digest(); - this.cipher = new SicBlockCipher(new AesEngine()); + this.cipher = new SicBlockCipher(AesUtilities.CreateEngine()); } private void DoDigest(IDigest digest, byte[] output, byte[] input, int outOffset) { diff --git a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs index e7455d253..85c50f3c4 100644 --- a/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs +++ b/crypto/src/pqc/crypto/sphincsplus/SPHINCSPlusEngine.cs @@ -107,7 +107,6 @@ namespace Org.BouncyCastle.Pqc.Crypto.SphincsPlus internal class Sha2Engine : SPHINCSPlusEngine { - private byte[] padding = new byte[128]; private HMac treeHMac; private Mgf1BytesGenerator mgf1; private byte[] hmacBuf; |