From 2a0a5eed19628889318549def8dc7a6158ee7676 Mon Sep 17 00:00:00 2001 From: Thomas Pornin Date: Sat, 28 Jul 2018 22:35:08 +0200 Subject: [PATCH] Added CCM and CCM_8 cipher suites. --- Crypto/BlockCipherCore.cs | 80 +++++++++++++++++-- Crypto/IBlockCipher.cs | 54 +++++++++++++ SSLTLS/RecordDecryptCCM.cs | 135 ++++++++++++++++++++++++++++++++ SSLTLS/RecordEncryptCCM.cs | 153 +++++++++++++++++++++++++++++++++++++ SSLTLS/SSL.cs | 58 ++++++++++++++ SSLTLS/SSLEngine.cs | 68 +++++++++++++++++ conf/bearssl.json | 8 ++ 7 files changed, 550 insertions(+), 6 deletions(-) create mode 100644 SSLTLS/RecordDecryptCCM.cs create mode 100644 SSLTLS/RecordEncryptCCM.cs diff --git a/Crypto/BlockCipherCore.cs b/Crypto/BlockCipherCore.cs index 2f1b761..ca5cc08 100644 --- a/Crypto/BlockCipherCore.cs +++ b/Crypto/BlockCipherCore.cs @@ -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(); } diff --git a/Crypto/IBlockCipher.cs b/Crypto/IBlockCipher.cs index 6a51b99..0a9f469 100644 --- a/Crypto/IBlockCipher.cs +++ b/Crypto/IBlockCipher.cs @@ -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 index 0000000..4a60e8d --- /dev/null +++ b/SSLTLS/RecordDecryptCCM.cs @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * 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 index 0000000..05b61b8 --- /dev/null +++ b/SSLTLS/RecordEncryptCCM.cs @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * 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 ++; + } +} + +} diff --git a/SSLTLS/SSL.cs b/SSLTLS/SSL.cs index 4059194..0b0948f 100644 --- a/SSLTLS/SSL.cs +++ b/SSLTLS/SSL.cs @@ -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: diff --git a/SSLTLS/SSLEngine.cs b/SSLTLS/SSLEngine.cs index e4ebf95..ac951cf 100644 --- a/SSLTLS/SSLEngine.cs +++ b/SSLTLS/SSLEngine.cs @@ -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. diff --git a/conf/bearssl.json b/conf/bearssl.json index 97f213a..ce2f9bf 100644 --- a/conf/bearssl.json +++ b/conf/bearssl.json @@ -16,6 +16,10 @@ "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", @@ -34,6 +38,10 @@ "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", -- 2.17.1