public void HashAes() { using var cmac = new CMAC(Aes.Create(), Convert.FromHexString("2b7e151628aed2a6abf7158809cf4f3c")); cmac.TransformBlock(Convert.FromHexString("6bc1bee22e409f96e93d7e117393172a"), 0, 16, null, 0); cmac.TransformBlock(Convert.FromHexString("ae2d8a571e03ac9c9eb76fac45af8e51"), 0, 16, null, 0); cmac.TransformFinalBlock(Convert.FromHexString("30c81c46a35ce411"), 0, 8); var tag = cmac.Hash; Assert.AreEqual("dfa66747de9ae63030ca32611497c827", Convert.ToHexString(tag).ToLowerInvariant()); }
private void Process(ReadOnlySpan <byte> nonce, ReadOnlySpan <byte> input, Span <byte> output, Span <byte> tag, ReadOnlySpan <byte> associatedData, bool outputIsCiphertext) { using var encryptor = aes.CreateEncryptor(); var tmp = CryptoPool.Rent(16); var counter = CryptoPool.Rent(16); var counterEnc = CryptoPool.Rent(16); var nonceMac = CryptoPool.Rent(16); var associatedDataMac = CryptoPool.Rent(16); var ciphertextMac = CryptoPool.Rent(16); try { CryptographicOperations.ZeroMemory(tmp.AsSpan(0, 15)); tmp[15] = 0; // N tag cmac.TransformBlock(tmp, 0, 16, null, 0); cmac.TryComputeHash(nonce, nonceMac, out var _); tmp[15] = 1; // H tag cmac.TransformBlock(tmp, 0, 16, null, 0); cmac.TryComputeHash(associatedData, associatedDataMac, out var _); cmac.Initialize(); tmp[15] = 2; // C tag cmac.TransformBlock(tmp, 0, 16, null, 0); nonceMac.AsSpan().CopyTo(counter); while (input.Length >= 16) { encryptor.TransformBlock(counter, 0, 16, counterEnc, 0); if (outputIsCiphertext) { for (int i = 0; i < 16; i++) { tmp[i] = (byte)(input[i] ^ counterEnc[i]); } cmac.TransformBlock(tmp, 0, 16, null, 0); tmp.AsSpan(0, 16).CopyTo(output); } else { input.Slice(0, 16).CopyTo(tmp); cmac.TransformBlock(tmp, 0, 16, null, 0); for (int i = 0; i < 16; i++) { output[i] = (byte)(input[i] ^ counterEnc[i]); } } byte add = 1; for (int i = 15; i >= 0; i--) { counter[i] += add; add = counter[i] == 0 ? 1 : 0; } input = input.Slice(16); output = output.Slice(16); } if (input.Length > 0) { encryptor.TransformBlock(counter, 0, 16, counterEnc, 0); if (outputIsCiphertext) { for (int i = 0; i < input.Length; i++) { tmp[i] = (byte)(input[i] ^ counterEnc[i]); } cmac.TransformBlock(tmp, 0, input.Length, null, 0); tmp.AsSpan(0, input.Length).CopyTo(output); } else { input.CopyTo(tmp); cmac.TransformBlock(tmp, 0, input.Length, null, 0); for (int i = 0; i < input.Length; i++) { output[i] = (byte)(input[i] ^ counterEnc[i]); } } } cmac.TryComputeHash(Array.Empty <byte>(), ciphertextMac, out var _); for (int i = 0; i < tag.Length; i++) { tag[i] = (byte)(nonceMac[i] ^ associatedDataMac[i] ^ ciphertextMac[i]); } } finally { cmac.Initialize(); CryptoPool.Return(tmp, 16); CryptoPool.Return(counter, 16); CryptoPool.Return(counterEnc, 16); CryptoPool.Return(nonceMac, 16); CryptoPool.Return(associatedDataMac, 16); CryptoPool.Return(ciphertextMac, 16); } }