// 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); }
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); }
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); }
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)); }
public CtrResult(byte[] text, SjclQuad tag) { Text = text; Tag = tag; }
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); }
public SjclQuad Decrypt(SjclQuad ciphertext) { return(Crypt(ciphertext, false)); }
public SjclQuad Encrypt(SjclQuad plaintext) { return(Crypt(plaintext, true)); }