Example #1
0
        // TODO: Parametrize on cipher!
        // TODO: Factor out shared code between Encrypt and Decrypt!
        // TODO: Validate parameters better!
        public static byte[] Decrypt(SjclAes aes, byte[] ciphertext, byte[] iv, byte[] adata, int tagLength)
        {
            var ivLength = iv.Length;

            if (ivLength < 7)
            {
                throw new CryptoException("IV must be at least 7 bytes long");
            }

            var plaintextLength = ciphertext.Length - tagLength;
            var ciphertextOnly  = ciphertext.Take(plaintextLength).ToArray();
            var tag             = new SjclQuad(ciphertext, plaintextLength);

            var inputLengthLength = ComputeLengthLength(plaintextLength);

            if (inputLengthLength < 15 - ivLength)
            {
                inputLengthLength = 15 - ivLength;
            }
            iv = iv.Take(15 - inputLengthLength).ToArray();

            var plaintextWithTag = ApplyCtr(aes, ciphertextOnly, iv, tag, tagLength, inputLengthLength);
            var expectedTag      = ComputeTag(aes, plaintextWithTag.Text, iv, adata, tagLength, inputLengthLength);

            var expectedTagBytes = expectedTag.ToBytes().Take(tagLength);
            var actualTagBytes   = plaintextWithTag.Tag.ToBytes().Take(tagLength);

            if (!actualTagBytes.SequenceEqual(expectedTagBytes))
            {
                throw new CryptoException("CCM tag doesn't match");
            }

            return(plaintextWithTag.Text);
        }
Example #2
0
        internal static SjclQuad[] ToQuads(uint[] abcds)
        {
            if (abcds.Length % 4 != 0)
            {
                throw new ArgumentException("Length must be a multiple of 4", "abcds");
            }

            var quads = new SjclQuad[abcds.Length / 4];

            for (var i = 0; i < quads.Length; ++i)
            {
                quads[i] = new SjclQuad(abcds, i * 4);
            }

            return(quads);
        }
Example #3
0
        internal static SjclQuad ComputeTag(SjclAes aes, byte[] plaintext, byte[] iv, byte[] adata, int tagLength, int plaintextLengthLength)
        {
            if (tagLength % 2 != 0 || tagLength < 4 || tagLength > 16)
            {
                throw new CryptoException("Tag must be 4, 8, 10, 12, 14 or 16 bytes long");
            }

            // flags + iv + plaintext-length
            var tag   = new SjclQuad(iv, -1);
            var flags = (adata.Length > 0 ? 0x40 : 0) | ((tagLength - 2) << 2) | (plaintextLengthLength - 1);

            tag.SetByte(0, (byte)flags);

            // Append plaintext length
            for (var i = 0; i < plaintextLengthLength; ++i)
            {
                tag.SetByte(15 - i, (byte)(plaintext.Length >> i * 8));
            }

            tag = aes.Encrypt(tag);

            var adataLength = adata.Length;

            if (adataLength > 0)
            {
                var adataWithLength = EncodeAdataLength(adataLength).Concat(adata).ToArray();
                for (var i = 0; i < adataWithLength.Length; i += 16)
                {
                    tag = aes.Encrypt(tag ^ new SjclQuad(adataWithLength, i));
                }
            }

            for (var i = 0; i < plaintext.Length; i += 16)
            {
                tag = aes.Encrypt(tag ^ new SjclQuad(plaintext, i));
            }

            return(tag);
        }
Example #4
0
        internal static CtrResult ApplyCtr(SjclAes aes, byte[] plaintext, byte[] iv, SjclQuad tag, int tagLength, int plaintextLengthLength)
        {
            // plaintextLength + iv
            var ctr = new SjclQuad(iv, -1);

            ctr.SetByte(0, (byte)(plaintextLengthLength - 1));

            // Encrypt the tag
            var encryptedTag = tag ^ aes.Encrypt(ctr);

            // Encrypt the plaintext
            var ciphertext = new byte[plaintext.Length];

            for (var i = 0; i < plaintext.Length; i += 16)
            {
                ++ctr.D;
                var block = new SjclQuad(plaintext, i) ^ aes.Encrypt(ctr);
                Array.Copy(block.ToBytes(), 0, ciphertext, i, Math.Min(16, plaintext.Length - i));
            }

            return(new CtrResult(ciphertext, encryptedTag));
        }
Example #5
0
        private SjclQuad Crypt(SjclQuad input, bool encrypting)
        {
            var key = encrypting ? _encryptionKey : _decryptionKey;

            var a = input[0] ^ key[0];
            var b = input[encrypting ? 1 : 3] ^ key[1];
            var c = input[2] ^ key[2];
            var d = input[encrypting ? 3 : 1] ^ key[3];

            var innerRoundCount = key.Length / 4 - 2;
            var keyIndex        = 4;
            var table           = encrypting ? EncodeTable : DecodeTable;
            var sbox            = encrypting ? SboxTable : InverseSboxTable;

            var output = new SjclQuad(0, 0, 0, 0);

            // Inner rounds
            for (var i = 0; i < innerRoundCount; i++)
            {
                var a2 = (table[0, (a >> 24)]) ^
                         (table[1, (b >> 16) & 255]) ^
                         (table[2, (c >> 8) & 255]) ^
                         (table[3, (d) & 255]) ^
                         (key[keyIndex]);

                var b2 = (table[0, (b >> 24)]) ^
                         (table[1, (c >> 16) & 255]) ^
                         (table[2, (d >> 8) & 255]) ^
                         (table[3, (a) & 255]) ^
                         (key[keyIndex + 1]);

                var c2 = (table[0, (c >> 24)]) ^
                         (table[1, (d >> 16) & 255]) ^
                         (table[2, (a >> 8) & 255]) ^
                         (table[3, (b) & 255]) ^
                         (key[keyIndex + 2]);

                var d2 = (table[0, (d >> 24)]) ^
                         (table[1, (a >> 16) & 255]) ^
                         (table[2, (b >> 8) & 255]) ^
                         (table[3, (c) & 255]) ^
                         (key[keyIndex + 3]);

                a = a2;
                b = b2;
                c = c2;
                d = d2;

                keyIndex += 4;
            }

            // Last round
            for (var i = 0; i < 4; i++)
            {
                var index = encrypting ? i : 3 & -i;
                output[index] = ((uint)sbox[(a >> 24)] << 24) ^
                                ((uint)sbox[(b >> 16) & 255] << 16) ^
                                ((uint)sbox[(c >> 8) & 255] << 8) ^
                                ((uint)sbox[(d) & 255]) ^
                                (key[keyIndex]);
                var t = a;
                a = b;
                b = c;
                c = d;
                d = t;

                ++keyIndex;
            }

            return(output);
        }
Example #6
0
 public SjclQuad Decrypt(SjclQuad ciphertext)
 {
     return(Crypt(ciphertext, false));
 }
Example #7
0
 public SjclQuad Encrypt(SjclQuad plaintext)
 {
     return(Crypt(plaintext, true));
 }
Example #8
0
 public CtrResult(byte[] text, SjclQuad tag)
 {
     Text = text;
     Tag  = tag;
 }