예제 #1
0
        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);
        }