示例#1
0
        //Can we change the ChaCha20Poly1305 input to some kind of ICrypto interface or action with 'Encrypt' and 'Decrypt'?
        private void Pack(IBufferWriter <byte> output, CryptoDtoHeaderDto header, ChaCha20Poly1305 crypto, ReadOnlySpan <byte> dtoNameBuffer, ReadOnlySpan <byte> dtoBuffer)
        {
            lock (bufferLock)
            {
                headerBuffer.Clear();
                MessagePackSerializer.Serialize(headerBuffer, header);
                ReadOnlySpan <byte> headerBytes = headerBuffer.WrittenSpan;

                ushort headerLength = (ushort)headerBytes.Length;
                BinaryPrimitives.WriteUInt16LittleEndian(headerLengthBytes, headerLength);
                ushort dtoNameLength = (ushort)dtoNameBuffer.Length;
                BinaryPrimitives.WriteUInt16LittleEndian(dtoNameLengthBytes, dtoNameLength);
                ushort dtoLength = (ushort)dtoBuffer.Length;
                BinaryPrimitives.WriteUInt16LittleEndian(dtoLengthBytes, dtoLength);

                switch (header.Mode)
                {
                case CryptoDtoMode.ChaCha20Poly1305:
                {
                    int adLength = 2 + headerLength;
                    int aeLength = 2 + dtoNameLength + 2 + dtoLength;

                    // Copy data into associated data buffer
                    adBuffer.Clear();
                    adBuffer.Write(headerLengthBytes);
                    adBuffer.Write(headerBytes);

                    // Copy data into authenticated encryption buffer
                    aeBuffer.Clear();
                    aeBuffer.Write(dtoNameLengthBytes);
                    aeBuffer.Write(dtoNameBuffer);
                    aeBuffer.Write(dtoLengthBytes);
                    aeBuffer.Write(dtoBuffer);

                    Span <byte> nonceSpan = new Span <byte>(nonceBytes);
                    BinaryPrimitives.WriteUInt64LittleEndian(nonceSpan.Slice(4), header.Sequence);

                    var adSpan         = adBuffer.WrittenSpan;
                    var aeSpan         = aeBuffer.WrittenSpan;
                    var cipherTextSpan = cipherText.Span.Slice(0, aeLength);
                    cipherTextSpan.Clear();
                    Span <byte> tagSpan = tagBuffer;
                    tagSpan.Clear();
                    crypto.Encrypt(nonceSpan, aeSpan, cipherTextSpan, tagSpan, adSpan);

                    output.Write(adSpan);
                    output.Write(cipherTextSpan);
                    output.Write(tagSpan);
                    break;
                }

                default:
                    throw new CryptographicException("Mode not recognised");
                }
            }
        }
示例#2
0
        //[Obsolete]
        //public byte[] Pack(CryptoDtoChannelStore channelStore, string channelTag, CryptoDtoMode mode, ReadOnlySpan<byte> dtoNameBuffer, ReadOnlySpan<byte> dtoBuffer)
        //{
        //    var channel = channelStore.GetChannel(channelTag);
        //    return Pack(channel, mode, dtoNameBuffer, dtoBuffer);
        //}

        //[Obsolete]
        //public byte[] Pack(CryptoDtoChannel channel, CryptoDtoMode mode, ReadOnlySpan<byte> dtoNameBuffer, ReadOnlySpan<byte> dtoBuffer)
        //{
        //    ArrayBufferWriter<byte> arrayBufferWriter = new ArrayBufferWriter<byte>();
        //    Pack(arrayBufferWriter, channel, mode, dtoNameBuffer, dtoBuffer);
        //    return arrayBufferWriter.WrittenSpan.ToArray();
        //}

        public void Pack(IBufferWriter <byte> output, CryptoDtoChannel channel, CryptoDtoMode mode, ReadOnlySpan <byte> dtoNameBuffer, ReadOnlySpan <byte> dtoBuffer)
        {
            channel.GetTransmitKey(mode, out ulong sequenceToSend);
            CryptoDtoHeaderDto header = new CryptoDtoHeaderDto
            {
                ChannelTag = channel.ChannelTag,
                Mode       = mode,
                Sequence   = sequenceToSend
            };

            Pack(output, header, channel.TransmitChaCha20Poly1305, dtoNameBuffer, dtoBuffer);
        }
        private static byte[] Pack(string channelTag, CryptoDtoMode mode, ReadOnlySpan <byte> transmitKey, ulong sequenceToBeSent, byte[] dtoNameBuffer, byte[] dtoBuffer)
        {
            CryptoDtoHeaderDto header = new CryptoDtoHeaderDto
            {
                ChannelTag = channelTag,
                Sequence   = sequenceToBeSent,
                Mode       = mode
            };

            var    headerBuffer  = MessagePackSerializer.Serialize(header);
            ushort headerLength  = (ushort)headerBuffer.Length;
            ushort dtoNameLength = (ushort)dtoNameBuffer.Length;
            ushort dtoLength     = (ushort)dtoBuffer.Length;

            switch (header.Mode)
            {
            case CryptoDtoMode.ChaCha20Poly1305:
            {
                int aePayloadLength = 2 + dtoNameLength + 2 + dtoLength;
                var aePayloadBuffer = new byte[aePayloadLength];

                Array.Copy(BitConverter.GetBytes(dtoNameLength), 0, aePayloadBuffer, 0, 2);
                Array.Copy(dtoNameBuffer, 0, aePayloadBuffer, 2, dtoNameLength);

                Array.Copy(BitConverter.GetBytes(dtoLength), 0, aePayloadBuffer, 2 + dtoNameLength, 2);
                Array.Copy(dtoBuffer, 0, aePayloadBuffer, 2 + dtoNameLength + 2, dtoLength);

                int adPayloadLength = 2 + headerLength;
                var adPayloadBuffer = new byte[adPayloadLength];

                Array.Copy(BitConverter.GetBytes(headerLength), 0, adPayloadBuffer, 0, 2);
                Array.Copy(headerBuffer, 0, adPayloadBuffer, 2, headerLength);

                Span <byte> nonceBuffer = stackalloc byte[Aead.NonceSize];
                BinaryPrimitives.WriteUInt64LittleEndian(nonceBuffer.Slice(4), header.Sequence);

                var    aead              = new ChaCha20Poly1305(transmitKey.ToArray());
                byte[] aeadPayload       = aead.Encrypt(aePayloadBuffer, adPayloadBuffer, nonceBuffer);
                int    aeadPayloadLength = aeadPayload.Length;

                byte[] packetBuffer = new byte[2 + headerLength + aeadPayloadLength];

                Array.Copy(BitConverter.GetBytes(headerLength), 0, packetBuffer, 0, 2);
                Array.Copy(headerBuffer, 0, packetBuffer, 2, headerLength);
                Array.Copy(aeadPayload, 0, packetBuffer, 2 + headerLength, aeadPayloadLength);
                return(packetBuffer);
            }

            default:
                throw new CryptographicException("Mode not recognised");
            }
        }
            internal Deserializer(CryptoDtoChannelStore channelStore, ReadOnlySpan <byte> bytes, bool ignoreSequence)
            {
                sequenceValid = false;
                headerLength  = Unsafe.ReadUnaligned <ushort>(ref MemoryMarshal.GetReference(bytes));           //.NET Standard 2.0 doesn't have BitConverter.ToUInt16(Span<T>)
                if (bytes.Length < (2 + headerLength))
                {
                    throw new CryptographicException("Not enough bytes to process packet.");
                }

                ReadOnlySpan <byte> headerDataBuffer = bytes.Slice(2, headerLength);

                header = MessagePackSerializer.Deserialize <CryptoDtoHeaderDto>(headerDataBuffer.ToArray());
                ReadOnlySpan <byte> receiveKey = channelStore.GetReceiveKey(header.ChannelTag, header.Mode);                 //This will throw exception if channel tag isn't in the store

                switch (header.Mode)
                {
                case CryptoDtoMode.ChaCha20Poly1305:
                {
                    int aeLength = bytes.Length - (2 + headerLength);
                    ReadOnlySpan <byte> aePayloadBuffer = bytes.Slice(2 + headerLength, aeLength);

                    ReadOnlySpan <byte> adBuffer = bytes.Slice(0, 2 + headerLength);

                    Span <byte> nonceBuffer = stackalloc byte[Aead.NonceSize];
                    BinaryPrimitives.WriteUInt64LittleEndian(nonceBuffer.Slice(4), header.Sequence);

                    var aead = new ChaCha20Poly1305(receiveKey.ToArray());
                    ReadOnlySpan <byte> decryptedPayload = aead.Decrypt(aePayloadBuffer.ToArray(), adBuffer.ToArray(), nonceBuffer);

                    if (ignoreSequence)
                    {
                        sequenceValid = channelStore.IsReceivedSequenceAllowed(header.ChannelTag, header.Sequence);
                    }
                    else
                    {
                        channelStore.CheckReceivedSequence(header.ChannelTag, header.Sequence);             //The packet has passed MAC, so now check if it's being duplicated or replayed
                        sequenceValid = true;
                    }

                    dtoNameLength = Unsafe.ReadUnaligned <ushort>(ref MemoryMarshal.GetReference(decryptedPayload));           //.NET Standard 2.0 doesn't have BitConverter.ToUInt16(Span<T>)
                    dtoNameBuffer = decryptedPayload.Slice(2, dtoNameLength);

                    dataLength = Unsafe.ReadUnaligned <ushort>(ref MemoryMarshal.GetReference(decryptedPayload.Slice(2 + dtoNameLength, 2)));           //.NET Standard 2.0 doesn't have BitConverter.ToUInt16(Span<T>)
                    dataBuffer = decryptedPayload.Slice(2 + dtoNameLength + 2, dataLength);
                    break;
                }

                default:
                    throw new CryptographicException("Mode not recognised");
                }
            }