private static void DeriveKey(HMAC keyedHmac, Span <byte> bufferArray, Span <byte> derivedOutput, uint counter = 1) { int derivedOutputCount = derivedOutput.Length; int derivedOutputOffset = 0; Span <byte> K_i = stackalloc byte[keyedHmac.HashSize / 8]; checked { for (uint counterStruct = counter; derivedOutputCount > 0; ++counterStruct) { ToBeBytes(counterStruct, bufferArray);// update the counter within the buffer keyedHmac.TryComputeHash(bufferArray, K_i, out _); // copy the leftmost bits of K_i into the output buffer int numBytesToCopy = derivedOutputCount > K_i.Length ? K_i.Length : derivedOutputCount;//Math.Min(derivedOutputCount, K_i.Length); //Utils.BlockCopy(K_i, 0, derivedOutput.Array, derivedOutputOffset, numBytesToCopy); for (int i = 0; i < numBytesToCopy; ++i) { derivedOutput[derivedOutputOffset + i] = K_i[i]; } derivedOutputOffset += numBytesToCopy; derivedOutputCount -= numBytesToCopy; } // for } // checked K_i.Fill(0); /* clean up needed only when HMAC implementation is not HMAC2 */ } //
/// <inheritdoc cref="IEncryptedMessageWriter.WriteTo"/> public void WriteTo <T>(ref GrowingSpanBuffer buffer, T message, byte[] key, HMAC hmac) where T : class, IMessage { if (message is not IEncryptedMessage) { throw new Exception($"Message of type '{typeof(T).Name}' cannot be encrypted."); } var unencryptedBuffer = new GrowingSpanBuffer(stackalloc byte[412]); _messageWriter.WriteTo(ref unencryptedBuffer, message); var hashBuffer = new GrowingSpanBuffer(stackalloc byte[unencryptedBuffer.Size + 4]); hashBuffer.WriteBytes(unencryptedBuffer.Data); hashBuffer.WriteUInt32(((IEncryptedMessage)message).SequenceId); Span <byte> hash = stackalloc byte[32]; if (!hmac.TryComputeHash(hashBuffer.Data, hash, out _)) { throw new Exception("Failed to compute message hash."); } unencryptedBuffer.WriteBytes(hash.Slice(0, 10)); var iv = new byte[16]; _rngCryptoServiceProvider.GetBytes(iv); var paddingByteCount = (byte)((16 - ((unencryptedBuffer.Size + 1) & 15)) & 15); for (var i = 0; i < paddingByteCount + 1; i++) { unencryptedBuffer.WriteUInt8(paddingByteCount); } var encryptedBuffer = unencryptedBuffer.Data.ToArray(); using (var cryptoTransform = _aesCryptoServiceProvider.CreateEncryptor(key, iv)) { var bytesWritten = 0; for (var i = encryptedBuffer.Length; i >= cryptoTransform.InputBlockSize; i -= bytesWritten) { var inputCount = cryptoTransform.CanTransformMultipleBlocks ? (i / cryptoTransform.InputBlockSize * cryptoTransform.InputBlockSize) : cryptoTransform.InputBlockSize; bytesWritten = cryptoTransform.TransformBlock( encryptedBuffer, bytesWritten, inputCount, encryptedBuffer, bytesWritten ); } } buffer.WriteUInt32(((IEncryptedMessage)message).SequenceId); buffer.WriteBytes(iv); buffer.WriteBytes(encryptedBuffer); }
/// <inheritdoc cref="IEncryptedMessageReader.ReadFrom"/> public IEncryptedMessage ReadFrom(ref SpanBufferReader bufferReader, byte[] key, HMAC hmac, byte?packetProperty) { var sequenceId = bufferReader.ReadUInt32(); var iv = bufferReader.ReadBytes(16).ToArray(); var decryptedBuffer = bufferReader.RemainingData.ToArray(); using (var cryptoTransform = _aesCryptoServiceProvider.CreateDecryptor(key, iv)) { var bytesWritten = 0; for (var i = decryptedBuffer.Length; i >= cryptoTransform.InputBlockSize; i -= bytesWritten) { var inputCount = cryptoTransform.CanTransformMultipleBlocks ? (i / cryptoTransform.InputBlockSize * cryptoTransform.InputBlockSize) : cryptoTransform.InputBlockSize; bytesWritten = cryptoTransform.TransformBlock( decryptedBuffer, bytesWritten, inputCount, decryptedBuffer, bytesWritten ); } } var paddingByteCount = decryptedBuffer[decryptedBuffer.Length - 1] + 1; var hmacStart = decryptedBuffer.Length - paddingByteCount - 10; var decryptedBufferSpan = decryptedBuffer.AsSpan(); var hash = decryptedBufferSpan.Slice(hmacStart, 10); var hashBufferWriter = new SpanBufferWriter(stackalloc byte[decryptedBuffer.Length + 4]); hashBufferWriter.WriteBytes(decryptedBufferSpan.Slice(0, hmacStart)); hashBufferWriter.WriteUInt32(sequenceId); Span <byte> computedHash = stackalloc byte[32]; if (!hmac.TryComputeHash(hashBufferWriter.Data, computedHash, out _)) { throw new Exception("Failed to compute message hash."); } if (!hash.SequenceEqual(computedHash.Slice(0, 10))) { throw new Exception("Message hash does not match the computed hash."); } bufferReader = new SpanBufferReader(decryptedBuffer); if (_messageReader.ReadFrom(ref bufferReader, packetProperty) is not IEncryptedMessage message) { throw new Exception( "Successfully decrypted message but failed to cast to type " + $"'{nameof(IEncryptedMessage)}'." ); } message.SequenceId = sequenceId; return(message); }