diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2019-09-10 00:19:15 +0700 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2019-09-10 00:19:15 +0700 |
commit | 7248688e6f513cbdde1ccc1d39904cb964b0c88a (patch) | |
tree | 55e3287103febeeec1d759b7c691951306d087e9 | |
parent | Port ChaCha20Poly1305 from bc-java (diff) | |
download | BouncyCastle.NET-ed25519-7248688e6f513cbdde1ccc1d39904cb964b0c88a.tar.xz |
Add ChaCha ciphers to factory classes
-rw-r--r-- | crypto/BouncyCastle.Android.csproj | 1 | ||||
-rw-r--r-- | crypto/BouncyCastle.csproj | 1 | ||||
-rw-r--r-- | crypto/BouncyCastle.iOS.csproj | 1 | ||||
-rw-r--r-- | crypto/crypto.csproj | 5 | ||||
-rw-r--r-- | crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs | 11 | ||||
-rw-r--r-- | crypto/src/crypto/BufferedAeadCipher.cs | 246 | ||||
-rw-r--r-- | crypto/src/crypto/engines/ChaCha7539Engine.cs | 3 | ||||
-rw-r--r-- | crypto/src/security/CipherUtilities.cs | 26 | ||||
-rw-r--r-- | crypto/src/security/GeneratorUtilities.cs | 12 | ||||
-rw-r--r-- | crypto/src/security/ParameterUtilities.cs | 16 |
10 files changed, 310 insertions, 12 deletions
diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj index eeefee157..96d3f7cb6 100644 --- a/crypto/BouncyCastle.Android.csproj +++ b/crypto/BouncyCastle.Android.csproj @@ -657,6 +657,7 @@ <Compile Include="src\crypto\AsymmetricCipherKeyPair.cs" /> <Compile Include="src\crypto\AsymmetricKeyParameter.cs" /> <Compile Include="src\crypto\BufferedAeadBlockCipher.cs" /> + <Compile Include="src\crypto\BufferedAeadCipher.cs" /> <Compile Include="src\crypto\BufferedAsymmetricBlockCipher.cs" /> <Compile Include="src\crypto\BufferedBlockCipher.cs" /> <Compile Include="src\crypto\BufferedCipherBase.cs" /> diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj index 990d69553..0183d97ab 100644 --- a/crypto/BouncyCastle.csproj +++ b/crypto/BouncyCastle.csproj @@ -651,6 +651,7 @@ <Compile Include="src\crypto\AsymmetricCipherKeyPair.cs" /> <Compile Include="src\crypto\AsymmetricKeyParameter.cs" /> <Compile Include="src\crypto\BufferedAeadBlockCipher.cs" /> + <Compile Include="src\crypto\BufferedAeadCipher.cs" /> <Compile Include="src\crypto\BufferedAsymmetricBlockCipher.cs" /> <Compile Include="src\crypto\BufferedBlockCipher.cs" /> <Compile Include="src\crypto\BufferedCipherBase.cs" /> diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj index 118bde49b..f58ee2ec6 100644 --- a/crypto/BouncyCastle.iOS.csproj +++ b/crypto/BouncyCastle.iOS.csproj @@ -652,6 +652,7 @@ <Compile Include="src\crypto\AsymmetricCipherKeyPair.cs" /> <Compile Include="src\crypto\AsymmetricKeyParameter.cs" /> <Compile Include="src\crypto\BufferedAeadBlockCipher.cs" /> + <Compile Include="src\crypto\BufferedAeadCipher.cs" /> <Compile Include="src\crypto\BufferedAsymmetricBlockCipher.cs" /> <Compile Include="src\crypto\BufferedBlockCipher.cs" /> <Compile Include="src\crypto\BufferedCipherBase.cs" /> diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj index de2dedf03..d7034cad7 100644 --- a/crypto/crypto.csproj +++ b/crypto/crypto.csproj @@ -3144,6 +3144,11 @@ BuildAction = "Compile" /> <File + RelPath = "src\crypto\BufferedAeadCipher.cs" + SubType = "Code" + BuildAction = "Compile" + /> + <File RelPath = "src\crypto\BufferedAsymmetricBlockCipher.cs" SubType = "Code" BuildAction = "Compile" diff --git a/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs b/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs index a991585f6..6c7fed442 100644 --- a/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs +++ b/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs @@ -159,6 +159,17 @@ namespace Org.BouncyCastle.Asn1.Pkcs */ public static readonly DerObjectIdentifier IdRsaKem = IdAlg.Branch("14"); + /** + * <pre> + * id-alg-AEADChaCha20Poly1305 OBJECT IDENTIFIER ::= + * { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) + * pkcs9(9) smime(16) alg(3) 18 } + * + * AEADChaCha20Poly1305Nonce ::= OCTET STRING (SIZE(12)) + * </pre> + */ + public static readonly DerObjectIdentifier IdAlgAeadChaCha20Poly1305 = IdAlg.Branch("18"); + // // SMIME capability sub oids. // diff --git a/crypto/src/crypto/BufferedAeadCipher.cs b/crypto/src/crypto/BufferedAeadCipher.cs new file mode 100644 index 000000000..c689c1eab --- /dev/null +++ b/crypto/src/crypto/BufferedAeadCipher.cs @@ -0,0 +1,246 @@ +using System; + +using Org.BouncyCastle.Crypto.Modes; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Org.BouncyCastle.Crypto +{ + /** + * The AEAD ciphers already handle buffering internally, so this class + * just takes care of implementing IBufferedCipher methods. + */ + public class BufferedAeadCipher + : BufferedCipherBase + { + private readonly IAeadCipher cipher; + + public BufferedAeadCipher(IAeadCipher cipher) + { + if (cipher == null) + throw new ArgumentNullException("cipher"); + + this.cipher = cipher; + } + + public override string AlgorithmName + { + get { return cipher.AlgorithmName; } + } + + /** + * initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param param the key and other data required by the cipher. + * @exception ArgumentException if the parameters argument is + * inappropriate. + */ + public override void Init( + bool forEncryption, + ICipherParameters parameters) + { + if (parameters is ParametersWithRandom) + { + parameters = ((ParametersWithRandom)parameters).Parameters; + } + + cipher.Init(forEncryption, parameters); + } + + /** + * return the blocksize for the underlying cipher. + * + * @return the blocksize for the underlying cipher. + */ + public override int GetBlockSize() + { + return 0; + } + + /** + * return the size of the output buffer required for an update + * an input of len bytes. + * + * @param len the length of the input. + * @return the space required to accommodate a call to update + * with len bytes of input. + */ + public override int GetUpdateOutputSize( + int length) + { + return cipher.GetUpdateOutputSize(length); + } + + /** + * return the size of the output buffer required for an update plus a + * doFinal with an input of len bytes. + * + * @param len the length of the input. + * @return the space required to accommodate a call to update and doFinal + * with len bytes of input. + */ + public override int GetOutputSize( + int length) + { + return cipher.GetOutputSize(length); + } + + /** + * 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 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 InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessByte( + byte input, + byte[] output, + int outOff) + { + return cipher.ProcessByte(input, output, outOff); + } + + public override byte[] ProcessByte( + byte input) + { + int outLength = GetUpdateOutputSize(1); + + byte[] outBytes = outLength > 0 ? new byte[outLength] : null; + + int pos = ProcessByte(input, outBytes, 0); + + if (outLength > 0 && pos < outLength) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + + return outBytes; + } + + public override byte[] ProcessBytes( + byte[] input, + int inOff, + int length) + { + if (input == null) + throw new ArgumentNullException("input"); + if (length < 1) + return null; + + int outLength = GetUpdateOutputSize(length); + + byte[] outBytes = outLength > 0 ? new byte[outLength] : null; + + int pos = ProcessBytes(input, inOff, length, outBytes, 0); + + if (outLength > 0 && pos < outLength) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + + return outBytes; + } + + /** + * 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 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 InvalidOperationException if the cipher isn't initialised. + */ + public override int ProcessBytes( + byte[] input, + int inOff, + int length, + byte[] output, + int outOff) + { + return cipher.ProcessBytes(input, inOff, length, output, outOff); + } + + public override byte[] DoFinal() + { + byte[] outBytes = new byte[GetOutputSize(0)]; + + int pos = DoFinal(outBytes, 0); + + if (pos < outBytes.Length) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + + return outBytes; + } + + public override byte[] DoFinal( + byte[] input, + int inOff, + int inLen) + { + if (input == null) + throw new ArgumentNullException("input"); + + byte[] outBytes = new byte[GetOutputSize(inLen)]; + + int pos = (inLen > 0) + ? ProcessBytes(input, inOff, inLen, outBytes, 0) + : 0; + + pos += DoFinal(outBytes, pos); + + if (pos < outBytes.Length) + { + byte[] tmp = new byte[pos]; + Array.Copy(outBytes, 0, tmp, 0, pos); + outBytes = tmp; + } + + return outBytes; + } + + /** + * Process the last block in the buffer. + * + * @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 InvalidOperationException 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 override int DoFinal( + byte[] output, + int outOff) + { + return cipher.DoFinal(output, outOff); + } + + /** + * Reset the buffer and cipher. After resetting the object is in the same + * state as it was after the last init (if there was one). + */ + public override void Reset() + { + cipher.Reset(); + } + } +} diff --git a/crypto/src/crypto/engines/ChaCha7539Engine.cs b/crypto/src/crypto/engines/ChaCha7539Engine.cs index af4163a02..206416a98 100644 --- a/crypto/src/crypto/engines/ChaCha7539Engine.cs +++ b/crypto/src/crypto/engines/ChaCha7539Engine.cs @@ -14,12 +14,13 @@ namespace Org.BouncyCastle.Crypto.Engines /// Creates a 20 rounds ChaCha engine. /// </summary> public ChaCha7539Engine() + : base() { } public override string AlgorithmName { - get { return "ChaCha7539" + rounds; } + get { return "ChaCha7539"; } } protected override int NonceSize diff --git a/crypto/src/security/CipherUtilities.cs b/crypto/src/security/CipherUtilities.cs index eb10baec8..fb2a31a56 100644 --- a/crypto/src/security/CipherUtilities.cs +++ b/crypto/src/security/CipherUtilities.cs @@ -33,6 +33,9 @@ namespace Org.BouncyCastle.Security CAMELLIA, CAST5, CAST6, + CHACHA, + CHACHA20_POLY1305, + CHACHA7539, DES, DESEDE, ELGAMAL, @@ -64,7 +67,7 @@ namespace Org.BouncyCastle.Security VMPC_KSA3, XTEA, }; - + private enum CipherMode { ECB, NONE, CBC, CCM, CFB, CTR, CTS, EAX, GCM, GOFB, OCB, OFB, OPENPGPCFB, SIC }; private enum CipherPadding { @@ -207,6 +210,9 @@ namespace Org.BouncyCastle.Security algorithms[KisaObjectIdentifiers.IdSeedCbc.Id] = "SEED/CBC/PKCS7PADDING"; algorithms["1.3.6.1.4.1.3029.1.2"] = "BLOWFISH/CBC"; + + algorithms["CHACHA20"] = "CHACHA7539"; + algorithms[PkcsObjectIdentifiers.IdAlgAeadChaCha20Poly1305.Id] = "CHACHA20-POLY1305"; } private CipherUtilities() @@ -333,6 +339,7 @@ namespace Org.BouncyCastle.Security string[] parts = algorithm.Split('/'); + IAeadCipher aeadCipher = null; IBlockCipher blockCipher = null; IAsymmetricBlockCipher asymBlockCipher = null; IStreamCipher streamCipher = null; @@ -376,6 +383,15 @@ namespace Org.BouncyCastle.Security case CipherAlgorithm.CAST6: blockCipher = new Cast6Engine(); break; + case CipherAlgorithm.CHACHA: + streamCipher = new ChaChaEngine(); + break; + case CipherAlgorithm.CHACHA20_POLY1305: + aeadCipher = new ChaCha20Poly1305(); + break; + case CipherAlgorithm.CHACHA7539: + streamCipher = new ChaCha7539Engine(); + break; case CipherAlgorithm.DES: blockCipher = new DesEngine(); break; @@ -468,6 +484,14 @@ namespace Org.BouncyCastle.Security throw new SecurityUtilityException("Cipher " + algorithm + " not recognised."); } + if (aeadCipher != null) + { + if (parts.Length > 1) + throw new ArgumentException("Modes and paddings cannot be applied to AEAD ciphers"); + + return new BufferedAeadCipher(aeadCipher); + } + if (streamCipher != null) { if (parts.Length > 1) diff --git a/crypto/src/security/GeneratorUtilities.cs b/crypto/src/security/GeneratorUtilities.cs index 08281493a..f39d583d6 100644 --- a/crypto/src/security/GeneratorUtilities.cs +++ b/crypto/src/security/GeneratorUtilities.cs @@ -72,6 +72,11 @@ namespace Org.BouncyCastle.Security AddKgAlgorithm("CAST5", "1.2.840.113533.7.66.10"); AddKgAlgorithm("CAST6"); + AddKgAlgorithm("CHACHA"); + AddKgAlgorithm("CHACHA7539", + "CHACHA20", + "CHACHA20-POLY1305", + PkcsObjectIdentifiers.IdAlgAeadChaCha20Poly1305); AddKgAlgorithm("DES", OiwObjectIdentifiers.DesCbc, OiwObjectIdentifiers.DesCfb, @@ -202,15 +207,16 @@ namespace Org.BouncyCastle.Security AddDefaultKeySizeEntries(64, "DES"); AddDefaultKeySizeEntries(80, "SKIPJACK"); - AddDefaultKeySizeEntries(128, "AES128", "BLOWFISH", "CAMELLIA128", "CAST5", "DESEDE", + AddDefaultKeySizeEntries(128, "AES128", "BLOWFISH", "CAMELLIA128", "CAST5", "CHACHA", "DESEDE", "HC128", "HMACMD2", "HMACMD4", "HMACMD5", "HMACRIPEMD128", "IDEA", "NOEKEON", "RC2", "RC4", "RC5", "SALSA20", "SEED", "SM4", "TEA", "XTEA", "VMPC", "VMPC-KSA3"); AddDefaultKeySizeEntries(160, "HMACRIPEMD160", "HMACSHA1"); AddDefaultKeySizeEntries(192, "AES", "AES192", "CAMELLIA192", "DESEDE3", "HMACTIGER", "RIJNDAEL", "SERPENT", "TNEPRES"); AddDefaultKeySizeEntries(224, "HMACSHA3-224", "HMACKECCAK224", "HMACSHA224", "HMACSHA512/224"); - AddDefaultKeySizeEntries(256, "AES256", "CAMELLIA", "CAMELLIA256", "CAST6", "GOST28147", - "HC256", "HMACGOST3411-2012-256", "HMACSHA3-256", "HMACKECCAK256", "HMACSHA256", "HMACSHA512/256", "RC5-64", "RC6", "THREEFISH-256", "TWOFISH"); + AddDefaultKeySizeEntries(256, "AES256", "CAMELLIA", "CAMELLIA256", "CAST6", "CHACHA7539", "GOST28147", + "HC256", "HMACGOST3411-2012-256", "HMACSHA3-256", "HMACKECCAK256", "HMACSHA256", "HMACSHA512/256", + "RC5-64", "RC6", "THREEFISH-256", "TWOFISH"); AddDefaultKeySizeEntries(288, "HMACKECCAK288"); AddDefaultKeySizeEntries(384, "HMACSHA3-384", "HMACKECCAK384", "HMACSHA384"); AddDefaultKeySizeEntries(512, "HMACGOST3411-2012-512", "HMACSHA3-512", "HMACKECCAK512", "HMACSHA512", "THREEFISH-512"); diff --git a/crypto/src/security/ParameterUtilities.cs b/crypto/src/security/ParameterUtilities.cs index dc6992833..0ff1bdb4a 100644 --- a/crypto/src/security/ParameterUtilities.cs +++ b/crypto/src/security/ParameterUtilities.cs @@ -65,6 +65,11 @@ namespace Org.BouncyCastle.Security AddAlgorithm("CAST5", "1.2.840.113533.7.66.10"); AddAlgorithm("CAST6"); + AddAlgorithm("CHACHA"); + AddAlgorithm("CHACHA7539", + "CHACHA20", + "CHACHA20-POLY1305", + PkcsObjectIdentifiers.IdAlgAeadChaCha20Poly1305); AddAlgorithm("DES", OiwObjectIdentifiers.DesCbc, OiwObjectIdentifiers.DesCfb, @@ -114,7 +119,8 @@ namespace Org.BouncyCastle.Security AddAlgorithm("VMPC-KSA3"); AddAlgorithm("XTEA"); - AddBasicIVSizeEntries(8, "BLOWFISH", "DES", "DESEDE", "DESEDE3"); + AddBasicIVSizeEntries(8, "BLOWFISH", "CHACHA", "DES", "DESEDE", "DESEDE3", "SALSA20"); + AddBasicIVSizeEntries(12, "CHACHA7539"); AddBasicIVSizeEntries(16, "AES", "AES128", "AES192", "AES256", "CAMELLIA", "CAMELLIA128", "CAMELLIA192", "CAMELLIA256", "NOEKEON", "SEED", "SM4"); @@ -315,13 +321,9 @@ namespace Org.BouncyCastle.Security return new DerOctetString(CreateIV(random, ivLength)); } - private static byte[] CreateIV( - SecureRandom random, - int ivLength) + private static byte[] CreateIV(SecureRandom random, int ivLength) { - byte[] iv = new byte[ivLength]; - random.NextBytes(iv); - return iv; + return SecureRandom.GetNextBytes(random, ivLength); } private static int FindBasicIVSize( |