From 8d2a2cd5de40e2b94ef5007c32832ed9a063dc40 Mon Sep 17 00:00:00 2001 From: chai <215380520@qq.com> Date: Thu, 12 Oct 2023 22:09:49 +0800 Subject: +hazel-networking --- .../Hazel/Dtls/AesGcmRecordProtection.cs | 147 +++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 Tools/Hazel-Networking/Hazel/Dtls/AesGcmRecordProtection.cs (limited to 'Tools/Hazel-Networking/Hazel/Dtls/AesGcmRecordProtection.cs') diff --git a/Tools/Hazel-Networking/Hazel/Dtls/AesGcmRecordProtection.cs b/Tools/Hazel-Networking/Hazel/Dtls/AesGcmRecordProtection.cs new file mode 100644 index 0000000..65df39e --- /dev/null +++ b/Tools/Hazel-Networking/Hazel/Dtls/AesGcmRecordProtection.cs @@ -0,0 +1,147 @@ +using Hazel.Crypto; +using System; +using System.Diagnostics; + +namespace Hazel.Dtls +{ + /// + /// *_AES_128_GCM_* cipher suite + /// + public class Aes128GcmRecordProtection: IRecordProtection + { + private const int ImplicitNonceSize = 4; + private const int ExplicitNonceSize = 8; + + private readonly Aes128Gcm serverWriteCipher; + private readonly Aes128Gcm clientWriteCipher; + + private readonly ByteSpan serverWriteIV; + private readonly ByteSpan clientWriteIV; + + /// + /// Create a new instance of the AES128_GCM record protection + /// + /// Shared secret + /// Server random data + /// Client random data + public Aes128GcmRecordProtection(ByteSpan masterSecret, ByteSpan serverRandom, ByteSpan clientRandom) + { + ByteSpan combinedRandom = new byte[serverRandom.Length + clientRandom.Length]; + serverRandom.CopyTo(combinedRandom); + clientRandom.CopyTo(combinedRandom.Slice(serverRandom.Length)); + + // Expand master_secret to encryption keys + const int ExpandedSize = 0 + + 0 // mac_key_length + + 0 // mac_key_length + + Aes128Gcm.KeySize // enc_key_length + + Aes128Gcm.KeySize // enc_key_length + + ImplicitNonceSize // fixed_iv_length + + ImplicitNonceSize // fixed_iv_length + ; + + ByteSpan expandedKey = new byte[ExpandedSize]; + PrfSha256.ExpandSecret(expandedKey, masterSecret, PrfLabel.KEY_EXPANSION, combinedRandom); + + ByteSpan clientWriteKey = expandedKey.Slice(0, Aes128Gcm.KeySize); + ByteSpan serverWriteKey = expandedKey.Slice(Aes128Gcm.KeySize, Aes128Gcm.KeySize); + this.clientWriteIV = expandedKey.Slice(2 * Aes128Gcm.KeySize, ImplicitNonceSize); + this.serverWriteIV = expandedKey.Slice(2 * Aes128Gcm.KeySize + ImplicitNonceSize, ImplicitNonceSize); + + this.serverWriteCipher = new Aes128Gcm(serverWriteKey); + this.clientWriteCipher = new Aes128Gcm(clientWriteKey); + } + + /// + public void Dispose() + { + this.serverWriteCipher.Dispose(); + this.clientWriteCipher.Dispose(); + } + + /// + private static int GetEncryptedSizeImpl(int dataSize) + { + return dataSize + Aes128Gcm.CiphertextOverhead; + } + + /// + public int GetEncryptedSize(int dataSize) + { + return GetEncryptedSizeImpl(dataSize); + } + + private static int GetDecryptedSizeImpl(int dataSize) + { + return dataSize - Aes128Gcm.CiphertextOverhead; + } + + /// + public int GetDecryptedSize(int dataSize) + { + return GetDecryptedSizeImpl(dataSize); + } + + /// + public void EncryptServerPlaintext(ByteSpan output, ByteSpan input, ref Record record) + { + EncryptPlaintext(output, input, ref record, this.serverWriteCipher, this.serverWriteIV); + } + + /// + public void EncryptClientPlaintext(ByteSpan output, ByteSpan input, ref Record record) + { + EncryptPlaintext(output, input, ref record, this.clientWriteCipher, this.clientWriteIV); + } + + private static void EncryptPlaintext(ByteSpan output, ByteSpan input, ref Record record, Aes128Gcm cipher, ByteSpan writeIV) + { + Debug.Assert(output.Length >= GetEncryptedSizeImpl(input.Length)); + + // Build GCM nonce (authenticated data) + ByteSpan nonce = new byte[ImplicitNonceSize + ExplicitNonceSize]; + writeIV.CopyTo(nonce); + nonce.WriteBigEndian16(record.Epoch, ImplicitNonceSize); + nonce.WriteBigEndian48(record.SequenceNumber, ImplicitNonceSize + 2); + + // Serialize record as additional data + Record plaintextRecord = record; + plaintextRecord.Length = (ushort)input.Length; + ByteSpan associatedData = new byte[Record.Size]; + plaintextRecord.Encode(associatedData); + + cipher.Seal(output, nonce, input, associatedData); + } + + /// + public bool DecryptCiphertextFromServer(ByteSpan output, ByteSpan input, ref Record record) + { + return DecryptCiphertext(output, input, ref record, this.serverWriteCipher, this.serverWriteIV); + } + + /// + public bool DecryptCiphertextFromClient(ByteSpan output, ByteSpan input, ref Record record) + { + return DecryptCiphertext(output, input, ref record, this.clientWriteCipher, this.clientWriteIV); + } + + private static bool DecryptCiphertext(ByteSpan output, ByteSpan input, ref Record record, Aes128Gcm cipher, ByteSpan writeIV) + { + Debug.Assert(output.Length >= GetDecryptedSizeImpl(input.Length)); + + // Build GCM nonce (authenticated data) + ByteSpan nonce = new byte[ImplicitNonceSize + ExplicitNonceSize]; + writeIV.CopyTo(nonce); + nonce.WriteBigEndian16(record.Epoch, ImplicitNonceSize); + nonce.WriteBigEndian48(record.SequenceNumber, ImplicitNonceSize + 2); + + // Serialize record as additional data + Record plaintextRecord = record; + plaintextRecord.Length = (ushort)GetDecryptedSizeImpl(input.Length); + ByteSpan associatedData = new byte[Record.Size]; + plaintextRecord.Encode(associatedData); + + return cipher.Open(output, nonce, input, associatedData); + } + } +} -- cgit v1.1-26-g67d0