130 lines
5.2 KiB
C#
130 lines
5.2 KiB
C#
|
|
/*
|
|||
|
|
* Secucore
|
|||
|
|
*
|
|||
|
|
* Copyright (C) 2023 Trevor Hall
|
|||
|
|
* All rights reserved.
|
|||
|
|
*
|
|||
|
|
* This software may be modified and distributed under the terms
|
|||
|
|
* of the MIT license.
|
|||
|
|
*
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
using System;
|
|||
|
|
using System.Security.Cryptography;
|
|||
|
|
using SecuCore.TLS.Exceptions;
|
|||
|
|
using static SecuCore.TLS.CipherSuites;
|
|||
|
|
|
|||
|
|
namespace SecuCore.TLS
|
|||
|
|
{
|
|||
|
|
class Encryption
|
|||
|
|
{
|
|||
|
|
private static byte[] MacHeader(ulong seqNum, byte type, ushort version, int fregmentlen)
|
|||
|
|
{
|
|||
|
|
// 8 bytes(sequence) + 5 bytes(header)
|
|||
|
|
byte[] result = new byte[13];
|
|||
|
|
result[0] = ((byte)(seqNum >> 56 & 0xff));
|
|||
|
|
result[1] = ((byte)(seqNum >> 48 & 0xff));
|
|||
|
|
result[2] = ((byte)(seqNum >> 40 & 0xff));
|
|||
|
|
result[3] = ((byte)(seqNum >> 32 & 0xff));
|
|||
|
|
result[4] = ((byte)(seqNum >> 24 & 0xff));
|
|||
|
|
result[5] = ((byte)(seqNum >> 16 & 0xff));
|
|||
|
|
result[6] = ((byte)(seqNum >> 8 & 0xff));
|
|||
|
|
result[7] = ((byte)(seqNum & 0xff));
|
|||
|
|
result[8] = type;
|
|||
|
|
result[9] = (byte)((version >> 8) & 0xff);
|
|||
|
|
result[10] = (byte)(version & 0xff);
|
|||
|
|
result[11] = (byte)((fregmentlen >> 8) & 0xff);
|
|||
|
|
result[12] = (byte)(fregmentlen & 0xff);
|
|||
|
|
return result;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private const int IVSize = 16;
|
|||
|
|
public static TLSRecord EncryptRecord(TLSRecord tlso, TLSEncryptionProvider provider, ulong sequence)
|
|||
|
|
{
|
|||
|
|
// generate mac
|
|||
|
|
byte[] fragment = tlso.Data;
|
|||
|
|
byte[] macheader = MacHeader(sequence, tlso.Type, tlso.Version, fragment.Length);
|
|||
|
|
provider.LocalHasher.TransformBlock(macheader, 0, macheader.Length, macheader, 0);
|
|||
|
|
provider.LocalHasher.TransformFinalBlock(fragment, 0, fragment.Length);
|
|||
|
|
byte[] mac = provider.LocalHasher.Hash;
|
|||
|
|
ICryptoTransform tfm = provider.Encryptor;
|
|||
|
|
int blockSize = provider.BlockSize;
|
|||
|
|
int paddingLength = blockSize - ((fragment.Length + mac.Length) % blockSize);
|
|||
|
|
byte padb = (byte)(paddingLength - 1);
|
|||
|
|
|
|||
|
|
byte[] inputbytes = new byte[IVSize + fragment.Length + mac.Length + paddingLength];
|
|||
|
|
byte[] ivguid = Guid.NewGuid().ToByteArray();
|
|||
|
|
int inpofs = 0;
|
|||
|
|
Buffer.BlockCopy(ivguid, 0, inputbytes, 0, ivguid.Length);
|
|||
|
|
inpofs += ivguid.Length;
|
|||
|
|
Buffer.BlockCopy(fragment, 0, inputbytes, inpofs, fragment.Length);
|
|||
|
|
inpofs += fragment.Length;
|
|||
|
|
Buffer.BlockCopy(mac, 0, inputbytes, inpofs, mac.Length);
|
|||
|
|
inpofs += mac.Length;
|
|||
|
|
Array.Fill(inputbytes, padb, inpofs, inputbytes.Length - inpofs);
|
|||
|
|
int inputblocks = (inputbytes.Length / tfm.InputBlockSize);
|
|||
|
|
byte[] outputbytes = new byte[inputblocks * tfm.OutputBlockSize];
|
|||
|
|
if (tfm.CanTransformMultipleBlocks)
|
|||
|
|
{
|
|||
|
|
tfm.TransformBlock(inputbytes, 0, inputbytes.Length, outputbytes, 0);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
int outofs = 0;
|
|||
|
|
for (int i = 0; i < inputblocks; i++)
|
|||
|
|
outofs += tfm.TransformBlock(inputbytes, i * tfm.InputBlockSize, tfm.InputBlockSize, outputbytes, outofs);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fragment = outputbytes;
|
|||
|
|
tlso.Data = fragment;
|
|||
|
|
tlso.Length = (ushort)fragment.Length;
|
|||
|
|
|
|||
|
|
return tlso;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public static TLSRecord DecryptRecord(TLSRecord tlso, TLSEncryptionProvider provider, ulong sequence)
|
|||
|
|
{
|
|||
|
|
byte[] fragment = tlso.Data;
|
|||
|
|
|
|||
|
|
ICryptoTransform tfm = provider.Decryptor;
|
|||
|
|
byte[] dec = tfm.TransformFinalBlock(fragment, 0, fragment.Length);
|
|||
|
|
fragment = dec;
|
|||
|
|
|
|||
|
|
int startidx = 0;
|
|||
|
|
int fraglen = fragment.Length;
|
|||
|
|
|
|||
|
|
// discard iv
|
|||
|
|
startidx += provider.IVSize;
|
|||
|
|
fraglen -= provider.IVSize;
|
|||
|
|
|
|||
|
|
// remove padding if necessary
|
|||
|
|
if (provider.bulkAlgorithmType == BulkAlgorithmType.Block)
|
|||
|
|
{
|
|||
|
|
int padding = fragment[fragment.Length - 1] + 1;
|
|||
|
|
// Verify the correctness of padding
|
|||
|
|
if (padding > fragment.Length)
|
|||
|
|
throw new TLSEncryptionException("padding removal failed");
|
|||
|
|
else
|
|||
|
|
fraglen -= padding;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// remove mac
|
|||
|
|
int macidx = (startidx + fraglen) - provider.HashSize;
|
|||
|
|
byte[] remotemac = Tools.SubArray(fragment, macidx, provider.HashSize);
|
|||
|
|
fraglen -= provider.HashSize;
|
|||
|
|
byte[] macinputheader = MacHeader(sequence, tlso.Type, tlso.Version, fraglen);
|
|||
|
|
provider.RemoteHasher.Initialize();
|
|||
|
|
provider.RemoteHasher.TransformBlock(macinputheader, 0, macinputheader.Length, macinputheader, 0);
|
|||
|
|
provider.RemoteHasher.TransformFinalBlock(fragment, startidx, fraglen);
|
|||
|
|
|
|||
|
|
byte[] mac = provider.RemoteHasher.Hash;
|
|||
|
|
|
|||
|
|
if (!Tools.ArraysEqual(mac, remotemac))
|
|||
|
|
throw new TLSEncryptionException("Mac verification failed on decrypt");
|
|||
|
|
fragment = Tools.SubArray(fragment, startidx, fraglen);
|
|||
|
|
tlso.Data = fragment;
|
|||
|
|
tlso.Length = (ushort)fragment.Length;
|
|||
|
|
return tlso;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|