Added CCM and CCM_8 cipher suites. master
authorThomas Pornin <pornin@bolet.org>
Sat, 28 Jul 2018 20:35:08 +0000 (22:35 +0200)
committerThomas Pornin <pornin@bolet.org>
Sat, 28 Jul 2018 20:35:08 +0000 (22:35 +0200)
Crypto/BlockCipherCore.cs
Crypto/IBlockCipher.cs
SSLTLS/RecordDecryptCCM.cs [new file with mode: 0644]
SSLTLS/RecordEncryptCCM.cs [new file with mode: 0644]
SSLTLS/SSL.cs
SSLTLS/SSLEngine.cs
conf/bearssl.json

index 2f1b761..ca5cc08 100644 (file)
@@ -43,13 +43,17 @@ namespace Crypto {
  *    void CBCDecrypt(byte[] iv, byte[] data, int off, int len)
  *    uint CTRRun(byte[] iv, uint cc, byte[] data)
  *    uint CTRRun(byte[] iv, uint cc, byte[] data, int off, int len)
+ *    void CTRCBCRun(byte[] ctr, byte[] cbcmac, bool encrypt, byte[] data)
+ *    void CTRCBCRun(byte[] ctr, byte[] cbcmac, bool encrypt,
+ *                   byte[] data, int off, int len)
  * Note that CBCEncrypt(byte[],byte[]) (respectively
- * CBCDecrypt(byte[],byte[]) and CTRRun(byte[],uint,byte[])) simply
- * calls CBCEncrypt(byte[],byte[],int,int) (respectively
- * CBCDecrypt(byte[],byte[],int,int) and
- * CTRRun(byte[],uint,byte[],int,int)) so implementations who wish to
- * override these methods may content themselves with overriding the two
- * methods with the "off" and "len" extra parameters.
+ * CBCDecrypt(byte[],byte[]), CTRRun(byte[],uint,byte[]) and
+ * CTRCBCRun(byte[],byte[],bool,byte[])) simply calls
+ * CBCEncrypt(byte[],byte[],int,int) (respectively
+ * CBCDecrypt(byte[],byte[],int,int), CTRRun(byte[],uint,byte[],int,int)
+ * and CTRCBCRun(byte[],byte[],bool,byte[],int,int)) so implementations
+ * who wish to override these methods may content themselves with
+ * overriding the four methods with the "off" and "len" extra parameters.
  */
 
 public abstract class BlockCipherCore : IBlockCipher {
@@ -215,6 +219,70 @@ public abstract class BlockCipherCore : IBlockCipher {
                return cc;
        }
 
+       /*
+        * This method is implemented by calling
+        * CTRCBCRun(byte[],byte[],bool,byte[],int,int).
+        */
+       public virtual void CTRCBCRun(byte[] ctr, byte[] cbcmac,
+               bool encrypt, byte[] data)
+       {
+               CTRCBCRun(ctr, cbcmac, encrypt, data, 0, data.Length);
+       }
+
+       /* see IBlockCipher */
+       public virtual void CTRCBCRun(byte[] ctr, byte[] cbcmac,
+               bool encrypt, byte[] data, int off, int len)
+       {
+               if (!encrypt) {
+                       CBCMac(cbcmac, data, off, len);
+               }
+               DoCTRFull(ctr, data, off, len);
+               if (encrypt) {
+                       CBCMac(cbcmac, data, off, len);
+               }
+       }
+
+       void DoCTRFull(byte[] ctr, byte[] data, int off, int len)
+       {
+               int blen = BlockSize;
+               if (ctr.Length != blen) {
+                       throw new CryptoException("wrong counter length");
+               }
+               while (len > 0) {
+                       Array.Copy(ctr, 0, tmp, 0, blen);
+                       uint cc = 1;
+                       for (int i = blen - 1; i >= 0; i --) {
+                               uint x = ctr[i] + cc;
+                               ctr[i] = (byte)x;
+                               cc = x >> 8;
+                       }
+                       BlockEncrypt(tmp, 0);
+                       int clen = Math.Min(blen, len);
+                       for (int i = 0; i < clen; i ++) {
+                               data[off + i] ^= tmp[i];
+                       }
+                       off += clen;
+                       len -= clen;
+               }
+       }
+
+       /* see IBlockCipher */
+       public void CBCMac(byte[] cbcmac, byte[] data, int off, int len)
+       {
+               int blen = BlockSize;
+               if (cbcmac.Length != blen) {
+                       throw new CryptoException("wrong MAC length");
+               }
+               while (len > 0) {
+                       for (int i = 0; i < blen; i ++) {
+                               cbcmac[i] ^= data[off + i];
+                       }
+                       BlockEncrypt(cbcmac, 0);
+                       off += blen;
+                       len -= blen;
+               }
+       }
+
        /* see IBlockCipher */
        public abstract IBlockCipher Dup();
 }
index 6a51b99..0a9f469 100644 (file)
@@ -138,6 +138,60 @@ public interface IBlockCipher {
         */
        uint CTRRun(byte[] iv, uint cc, byte[] data, int off, int len);
 
+       /*
+        * Do combined CTR encryption/decryption and CBC-MAC. The CTR
+        * mode uses full-block increments (counter value is the
+        * big-endian interpretation of the complete block); the ctr[]
+        * array contains the initial value for the counter (used to
+        * encrypt or decrypt the full block) and it is updated by
+        * this method as blocks are processed.
+        *
+        * The cbcmac[] array has full block width and contains the
+        * running value for CBC-MAC, computed over the _encrypted_ data.
+        *
+        * The flag 'encrypt' is true when encrypting, false when
+        * decrypting. Note that CTR encryption and decryption are
+        * identical; thus, the only effect of this flag is to decide
+        * whether CBC-MAC should be applied on the blocks before or
+        * after CTR encryption/decryption.
+        *
+        * The data is provided in the data[] buffer, and is
+        * encrypted/decrypted in place. Its length MUST be a multiple
+        * of the block size.
+        */
+       void CTRCBCRun(byte[] ctr, byte[] cbcmac, bool encrypt, byte[] data);
+
+       /*
+        * Do combined CTR encryption/decryption and CBC-MAC. The CTR
+        * mode uses full-block increments (counter value is the
+        * big-endian interpretation of the complete block); the ctr[]
+        * array contains the initial value for the counter (used to
+        * encrypt or decrypt the full block) and it is updated by
+        * this method as blocks are processed.
+        *
+        * The cbcmac[] array has full block width and contains the
+        * running value for CBC-MAC, computed over the _encrypted_ data.
+        *
+        * The flag 'encrypt' is true when encrypting, false when
+        * decrypting. Note that CTR encryption and decryption are
+        * identical; thus, the only effect of this flag is to decide
+        * whether CBC-MAC should be applied on the blocks before or
+        * after CTR encryption/decryption.
+        *
+        * The data is provided in the data[] buffer, and is
+        * encrypted/decrypted in place. Its length MUST be a multiple
+        * of the block size.
+        */
+       void CTRCBCRun(byte[] ctr, byte[] cbcmac, bool encrypt,
+               byte[] data, int off, int len);
+
+       /*
+        * Perform CBC-MAC: the cbcmac[] block is updated with the
+        * CBC-MAC of the data. Data length must be a multiple of the
+        * block size.
+        */
+       void CBCMac(byte[] cbcmac, byte[] data, int off, int len);
+
        /*
         * Duplicate this engine. This creates a new, independent
         * instance that implements the same function, and starts with
diff --git a/SSLTLS/RecordDecryptCCM.cs b/SSLTLS/RecordDecryptCCM.cs
new file mode 100644 (file)
index 0000000..4a60e8d
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2018 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Text;
+
+using Crypto;
+
+namespace SSLTLS {
+
+internal class RecordDecryptCCM : RecordDecrypt {
+
+       IBlockCipher bc;
+       byte[] iv;
+       ulong seq;
+       byte[] tmp, tag, ctr, cbcmac;
+       bool ccm8;
+
+       internal RecordDecryptCCM(IBlockCipher bc, byte[] iv, bool ccm8)
+       {
+               this.bc = bc;
+               this.iv = new byte[12];
+               Array.Copy(iv, 0, this.iv, 0, 4);
+               seq = 0;
+               tag = new byte[16];
+               tmp = new byte[32];
+               ctr = new byte[16];
+               cbcmac = new byte[16];
+               this.ccm8 = ccm8;
+       }
+
+       internal override bool CheckLength(int len)
+       {
+               int tagLen = ccm8 ? 8 : 16;
+               return len >= (8 + tagLen) && len <= (16384 + 8 + tagLen);
+       }
+
+       internal override bool Decrypt(int recordType, int version,
+               byte[] data, ref int off, ref int len)
+       {
+               Array.Copy(data, off, iv, 4, 8);
+               off += 8;
+               len -= ccm8 ? 16 : 24;
+
+               /*
+                * Assemble block B0 and AAD.
+                */
+               tmp[0] = (byte)(0x40 | ((ccm8 ? 6 : 14) << 2) | 2);
+               Array.Copy(iv, 0, tmp, 1, 12);
+               tmp[13] = 0;
+               IO.Enc16be(len, tmp, 14);
+
+               tmp[16] = 0;
+               tmp[17] = 13;
+               IO.Enc64be(seq, tmp, 18);
+               IO.WriteHeader(recordType, version, len, tmp, 26);
+               tmp[31] = 0;
+               seq ++;
+
+               for (int i = 0; i < cbcmac.Length; i ++) {
+                       cbcmac[i] = 0;
+               }
+               bc.CBCMac(cbcmac, tmp, 0, 32);
+
+               /*
+                * Make initial counter value, and compute tag mask.
+                * Since the counter least significant byte has value 0,
+                * getting it to the next value is simple and requires
+                * no carry propagation.
+                */
+               ctr[0] = 2;
+               Array.Copy(iv, 0, ctr, 1, 12);
+               for (int i = 13; i < 16; i ++) {
+                       ctr[i] = 0;
+               }
+               Array.Copy(ctr, 0, tag, 0, 16);
+               bc.BlockEncrypt(tag);
+               ctr[15] = 1;
+
+               /*
+                * Perform CTR decryption, and compute CBC-MAC. Since
+                * CBC-MAC requires full blocks, we have to do the
+                * processing of the last partial block in a temporary
+                * buffer. The CBC-MAC is computed on the plaintext,
+                * padded with zeros in the last block.
+                */
+               int len1 = len & ~15;
+               int len2 = len - len1;
+               bc.CTRCBCRun(ctr, cbcmac, true, data, off, len1);
+               if (len2 > 0) {
+                       bc.BlockEncrypt(ctr);
+                       for (int i = 0; i < len2; i ++) {
+                               data[off + len1 + i] ^= ctr[i];
+                       }
+                       Array.Copy(data, off + len1, tmp, 0, len2);
+                       for (int i = len2; i < 16; i ++) {
+                               tmp[i] = 0;
+                       }
+                       bc.CBCMac(cbcmac, tmp, 0, 16);
+               }
+
+               /*
+                * Check that the record MAC matches the expected value
+                * (taking into account the tag mask).
+                */
+               int z = 0;
+               for (int i = 0; i < (ccm8 ? 8 : 16); i ++) {
+                       z |= cbcmac[i] ^ tag[i] ^ data[off + len + i];
+               }
+               return z == 0;
+       }
+}
+
+}
diff --git a/SSLTLS/RecordEncryptCCM.cs b/SSLTLS/RecordEncryptCCM.cs
new file mode 100644 (file)
index 0000000..05b61b8
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2018 Thomas Pornin <pornin@bolet.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining 
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be 
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+using System;
+using System.Text;
+
+using Crypto;
+
+namespace SSLTLS {
+
+internal class RecordEncryptCCM : RecordEncrypt {
+
+       IBlockCipher bc;
+       byte[] iv;
+       ulong seq;
+       byte[] tmp, tag, ctr, cbcmac;
+       bool ccm8;
+
+       internal RecordEncryptCCM(IBlockCipher bc, byte[] iv, bool ccm8)
+       {
+               this.bc = bc;
+               this.iv = new byte[4];
+               Array.Copy(iv, 0, this.iv, 0, 4);
+               seq = 0;
+               tag = new byte[16];
+               tmp = new byte[32];
+               ctr = new byte[16];
+               cbcmac = new byte[16];
+               this.ccm8 = ccm8;
+       }
+
+       internal override void GetMaxPlaintext(ref int start, ref int end)
+       {
+               /*
+                * We need room at the start for the record header (5 bytes)
+                * and the explicit nonce (8 bytes). We need room at the end
+                * for the MAC (16 bytes).
+                */
+               start += 13;
+               end -= ccm8 ? 8 : 16;
+               int len = Math.Min(end - start, 16384);
+               end = start + len;
+       }
+
+       internal override void Encrypt(int recordType, int version,
+               byte[] data, ref int off, ref int len)
+       {
+               /*
+                * CBC-MAC starts with block B0, that encodes the
+                * nonce, tag length, and data length.
+                * It is then followed by the AAD:
+                *  - AAD header (length, over 2 bytes in our case)
+                *  - TLS sequence number (8 bytes)
+                *  - plain record header
+                */
+               tmp[0] = (byte)(0x40 | ((ccm8 ? 6 : 14) << 2) | 2);
+               Array.Copy(iv, 0, tmp, 1, 4);
+               IO.Enc64be(seq, tmp, 5);
+               tmp[13] = 0;
+               IO.Enc16be(len, tmp, 14);
+
+               tmp[16] = 0;
+               tmp[17] = 13;
+               IO.Enc64be(seq, tmp, 18);
+               IO.WriteHeader(recordType, version, len, tmp, 26);
+               tmp[31] = 0;
+
+               for (int i = 0; i < cbcmac.Length; i ++) {
+                       cbcmac[i] = 0;
+               }
+               bc.CBCMac(cbcmac, tmp, 0, 32);
+
+               /*
+                * Make initial counter value, and compute tag mask.
+                * Since the counter least significant byte has value 0,
+                * getting it to the next value is simple and requires
+                * no carry propagation.
+                */
+               ctr[0] = 2;
+               Array.Copy(tmp, 1, ctr, 1, 12);
+               for (int i = 13; i < 16; i ++) {
+                       ctr[i] = 0;
+               }
+               Array.Copy(ctr, 0, tag, 0, 16);
+               bc.BlockEncrypt(tag);
+               ctr[15] = 1;
+
+               /*
+                * Perform CTR encryption and CBC-MAC. CCM is defined
+                * to use CBC-MAC on the plaintext, not the ciphertext,
+                * thus we need to set the 'encrypt' flag to false.
+                *
+                * When the last block is partial, then we must pad
+                * the plaintext with zeros, and compute the CBC-MAC
+                * on that plaintext.
+                */
+               int len1 = len & ~15;
+               int len2 = len - len1;
+               bc.CTRCBCRun(ctr, cbcmac, false, data, off, len1);
+               if (len2 > 0) {
+                       Array.Copy(data, off + len1, tmp, 0, len2);
+                       for (int i = len2; i < 16; i ++) {
+                               tmp[i] = 0;
+                       }
+                       bc.CBCMac(cbcmac, tmp, 0, 16);
+                       bc.BlockEncrypt(ctr);
+                       for (int i = 0; i < len2; i ++) {
+                               data[off + len1 + i] ^= ctr[i];
+                       }
+               }
+
+               /*
+                * XOR the CBC-MAC output with the tag mask.
+                */
+               for (int i = 0; i < (ccm8 ? 8 : 16); i ++) {
+                       data[off + len + i] = (byte)(tag[i] ^ cbcmac[i]);
+               }
+
+               /*
+                * Encode the header, and adjust offset / length.
+                */
+               off -= 13;
+               len += ccm8 ? 16 : 24;
+               IO.WriteHeader(recordType, version, len, data, off);
+               IO.Enc64be(seq, data, off + 5);
+               len += 5;
+
+               seq ++;
+       }
+}
+
+}
index 4059194..0b0948f 100644 (file)
@@ -204,6 +204,16 @@ public sealed class SSL {
        public const int ECDH_RSA_WITH_AES_128_GCM_SHA256       = 0xC031;
        public const int ECDH_RSA_WITH_AES_256_GCM_SHA384       = 0xC032;
 
+       /* From RFC 6655 and 7251 */
+       public const int RSA_WITH_AES_128_CCM                   = 0xC09C;
+       public const int RSA_WITH_AES_256_CCM                   = 0xC09D;
+       public const int RSA_WITH_AES_128_CCM_8                 = 0xC0A0;
+       public const int RSA_WITH_AES_256_CCM_8                 = 0xC0A1;
+       public const int ECDHE_ECDSA_WITH_AES_128_CCM           = 0xC0AC;
+       public const int ECDHE_ECDSA_WITH_AES_256_CCM           = 0xC0AD;
+       public const int ECDHE_ECDSA_WITH_AES_128_CCM_8         = 0xC0AE;
+       public const int ECDHE_ECDSA_WITH_AES_256_CCM_8         = 0xC0AF;
+
        /* From RFC 7905 */
        public const int ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256      = 0xCCA8;
        public const int ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256    = 0xCCA9;
@@ -504,6 +514,22 @@ public sealed class SSL {
                        return "ECDH_RSA_WITH_AES_128_GCM_SHA256";
                case ECDH_RSA_WITH_AES_256_GCM_SHA384:
                        return "ECDH_RSA_WITH_AES_256_GCM_SHA384";
+               case RSA_WITH_AES_128_CCM:
+                       return "RSA_WITH_AES_128_CCM";
+               case RSA_WITH_AES_256_CCM:
+                       return "RSA_WITH_AES_256_CCM";
+               case RSA_WITH_AES_128_CCM_8:
+                       return "RSA_WITH_AES_128_CCM_8";
+               case RSA_WITH_AES_256_CCM_8:
+                       return "RSA_WITH_AES_256_CCM_8";
+               case ECDHE_ECDSA_WITH_AES_128_CCM:
+                       return "ECDHE_ECDSA_WITH_AES_128_CCM";
+               case ECDHE_ECDSA_WITH_AES_256_CCM:
+                       return "ECDHE_ECDSA_WITH_AES_256_CCM";
+               case ECDHE_ECDSA_WITH_AES_128_CCM_8:
+                       return "ECDHE_ECDSA_WITH_AES_128_CCM_8";
+               case ECDHE_ECDSA_WITH_AES_256_CCM_8:
+                       return "ECDHE_ECDSA_WITH_AES_256_CCM_8";
                case ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
                        return "ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256";
                case ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
@@ -718,6 +744,22 @@ public sealed class SSL {
                        return ECDH_RSA_WITH_AES_128_GCM_SHA256;
                case "ECDHRSAWITHAES256GCMSHA384":
                        return ECDH_RSA_WITH_AES_256_GCM_SHA384;
+               case "RSAWITHAES128CCM":
+                       return RSA_WITH_AES_128_CCM;
+               case "RSAWITHAES256CCM":
+                       return RSA_WITH_AES_256_CCM;
+               case "RSAWITHAES128CCM8":
+                       return RSA_WITH_AES_128_CCM_8;
+               case "RSAWITHAES256CCM8":
+                       return RSA_WITH_AES_256_CCM_8;
+               case "ECDHEECDSAWITHAES128CCM":
+                       return ECDHE_ECDSA_WITH_AES_128_CCM;
+               case "ECDHEECDSAWITHAES256CCM":
+                       return ECDHE_ECDSA_WITH_AES_256_CCM;
+               case "ECDHEECDSAWITHAES128CCM8":
+                       return ECDHE_ECDSA_WITH_AES_128_CCM_8;
+               case "ECDHEECDSAWITHAES256CCM8":
+                       return ECDHE_ECDSA_WITH_AES_256_CCM_8;
                case "ECDHERSAWITHCHACHA20POLY1305SHA256":
                        return ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256;
                case "ECDHEECDSAWITHCHACHA20POLY1305SHA256":
@@ -867,6 +909,10 @@ public sealed class SSL {
                case RSA_WITH_AES_256_CBC_SHA256:
                case RSA_WITH_AES_128_GCM_SHA256:
                case RSA_WITH_AES_256_GCM_SHA384:
+               case RSA_WITH_AES_128_CCM:
+               case RSA_WITH_AES_256_CCM:
+               case RSA_WITH_AES_128_CCM_8:
+               case RSA_WITH_AES_256_CCM_8:
                        return true;
                default:
                        return false;
@@ -996,6 +1042,10 @@ public sealed class SSL {
                case ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
                case ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
                case ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+               case ECDHE_ECDSA_WITH_AES_128_CCM:
+               case ECDHE_ECDSA_WITH_AES_256_CCM:
+               case ECDHE_ECDSA_WITH_AES_128_CCM_8:
+               case ECDHE_ECDSA_WITH_AES_256_CCM_8:
                case ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
                        return true;
                default:
@@ -1093,6 +1143,14 @@ public sealed class SSL {
                case ECDHE_RSA_WITH_AES_256_GCM_SHA384:
                case ECDH_RSA_WITH_AES_128_GCM_SHA256:
                case ECDH_RSA_WITH_AES_256_GCM_SHA384:
+               case RSA_WITH_AES_128_CCM:
+               case RSA_WITH_AES_256_CCM:
+               case RSA_WITH_AES_128_CCM_8:
+               case RSA_WITH_AES_256_CCM_8:
+               case ECDHE_ECDSA_WITH_AES_128_CCM:
+               case ECDHE_ECDSA_WITH_AES_256_CCM:
+               case ECDHE_ECDSA_WITH_AES_128_CCM_8:
+               case ECDHE_ECDSA_WITH_AES_256_CCM_8:
                case ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
                case ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
                case DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
index e4ebf95..ac951cf 100644 (file)
@@ -79,6 +79,10 @@ public abstract class SSLEngine : Stream {
                SSL.ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                SSL.ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
                SSL.ECDHE_RSA_WITH_AES_256_GCM_SHA384,
+               SSL.ECDHE_ECDSA_WITH_AES_128_CCM,
+               SSL.ECDHE_ECDSA_WITH_AES_256_CCM,
+               SSL.ECDHE_ECDSA_WITH_AES_128_CCM_8,
+               SSL.ECDHE_ECDSA_WITH_AES_256_CCM_8,
                SSL.ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
                SSL.ECDHE_RSA_WITH_AES_128_CBC_SHA256,
                SSL.ECDHE_ECDSA_WITH_AES_256_CBC_SHA384,
@@ -103,6 +107,10 @@ public abstract class SSLEngine : Stream {
 
                SSL.RSA_WITH_AES_128_GCM_SHA256,
                SSL.RSA_WITH_AES_256_GCM_SHA384,
+               SSL.RSA_WITH_AES_128_CCM,
+               SSL.RSA_WITH_AES_256_CCM,
+               SSL.RSA_WITH_AES_128_CCM_8,
+               SSL.RSA_WITH_AES_256_CCM_8,
                SSL.RSA_WITH_AES_128_CBC_SHA256,
                SSL.RSA_WITH_AES_256_CBC_SHA256,
                SSL.RSA_WITH_AES_128_CBC_SHA,
@@ -1347,6 +1355,8 @@ public abstract class SSLEngine : Stream {
                IBlockCipher block = null;
                IDigest hash = null;
                Poly1305 pp = null;
+               bool isCCM = false;
+               bool isCCM8 = false;
                switch (CipherSuite) {
                case SSL.RSA_WITH_3DES_EDE_CBC_SHA:
                case SSL.DH_DSS_WITH_3DES_EDE_CBC_SHA:
@@ -1475,6 +1485,42 @@ public abstract class SSLEngine : Stream {
                        block = new AES();
                        break;
 
+               case SSL.RSA_WITH_AES_128_CCM:
+               case SSL.ECDHE_ECDSA_WITH_AES_128_CCM:
+                       macLen = 0;
+                       encLen = 16;
+                       ivLen = 4;
+                       block = new AES();
+                       isCCM = true;
+                       break;
+
+               case SSL.RSA_WITH_AES_256_CCM:
+               case SSL.ECDHE_ECDSA_WITH_AES_256_CCM:
+                       macLen = 0;
+                       encLen = 32;
+                       ivLen = 4;
+                       block = new AES();
+                       isCCM = true;
+                       break;
+
+               case SSL.RSA_WITH_AES_128_CCM_8:
+               case SSL.ECDHE_ECDSA_WITH_AES_128_CCM_8:
+                       macLen = 0;
+                       encLen = 16;
+                       ivLen = 4;
+                       block = new AES();
+                       isCCM8 = true;
+                       break;
+
+               case SSL.RSA_WITH_AES_256_CCM_8:
+               case SSL.ECDHE_ECDSA_WITH_AES_256_CCM_8:
+                       macLen = 0;
+                       encLen = 32;
+                       ivLen = 4;
+                       block = new AES();
+                       isCCM8 = true;
+                       break;
+
                case SSL.ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
                case SSL.ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256:
                case SSL.DHE_RSA_WITH_CHACHA20_POLY1305_SHA256:
@@ -1539,6 +1585,28 @@ public abstract class SSLEngine : Stream {
                                inRec.SetDecryption(
                                        new RecordDecryptCBC(block, hm, iv));
                        }
+               } else if (isCCM) {
+                       /*
+                        * CCM cipher suite.
+                        */
+                       if (write) {
+                               outRec.SetEncryption(
+                                       new RecordEncryptCCM(block, iv, false));
+                       } else {
+                               inRec.SetDecryption(
+                                       new RecordDecryptCCM(block, iv, false));
+                       }
+               } else if (isCCM8) {
+                       /*
+                        * CCM cipher suite with truncated MAC value.
+                        */
+                       if (write) {
+                               outRec.SetEncryption(
+                                       new RecordEncryptCCM(block, iv, true));
+                       } else {
+                               inRec.SetDecryption(
+                                       new RecordDecryptCCM(block, iv, true));
+                       }
                } else if (block != null) {
                        /*
                         * GCM cipher suite.
index 97f213a..ce2f9bf 100644 (file)
     "ECDHE_RSA_WITH_AES_128_GCM_SHA256",
     "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
     "ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+    "ECDHE_ECDSA_WITH_AES_128_CCM",
+    "ECDHE_ECDSA_WITH_AES_256_CCM",
+    "ECDHE_ECDSA_WITH_AES_128_CCM_8",
+    "ECDHE_ECDSA_WITH_AES_256_CCM_8",
     "ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
     "ECDHE_RSA_WITH_AES_128_CBC_SHA256",
     "ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
 
     "RSA_WITH_AES_128_GCM_SHA256",
     "RSA_WITH_AES_256_GCM_SHA384",
+    "RSA_WITH_AES_128_CCM",
+    "RSA_WITH_AES_256_CCM",
+    "RSA_WITH_AES_128_CCM_8",
+    "RSA_WITH_AES_256_CCM_8",
     "RSA_WITH_AES_128_CBC_SHA256",
     "RSA_WITH_AES_256_CBC_SHA256",
     "RSA_WITH_AES_128_CBC_SHA",