summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2019-09-10 00:19:15 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2019-09-10 00:19:15 +0700
commit7248688e6f513cbdde1ccc1d39904cb964b0c88a (patch)
tree55e3287103febeeec1d759b7c691951306d087e9
parentPort ChaCha20Poly1305 from bc-java (diff)
downloadBouncyCastle.NET-ed25519-7248688e6f513cbdde1ccc1d39904cb964b0c88a.tar.xz
Add ChaCha ciphers to factory classes
-rw-r--r--crypto/BouncyCastle.Android.csproj1
-rw-r--r--crypto/BouncyCastle.csproj1
-rw-r--r--crypto/BouncyCastle.iOS.csproj1
-rw-r--r--crypto/crypto.csproj5
-rw-r--r--crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs11
-rw-r--r--crypto/src/crypto/BufferedAeadCipher.cs246
-rw-r--r--crypto/src/crypto/engines/ChaCha7539Engine.cs3
-rw-r--r--crypto/src/security/CipherUtilities.cs26
-rw-r--r--crypto/src/security/GeneratorUtilities.cs12
-rw-r--r--crypto/src/security/ParameterUtilities.cs16
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(