/// <summary>
        /// Creates connection
        /// </summary>
        /// <param name="streamId">Local reference id of the stream</param>
        /// <param name="remoteId">Remote reference of link</param>
        /// <param name="proxy">The proxy server</param>
        /// <returns></returns>
        public async Task <IConnection> CreateConnectionAsync(Reference streamId,
                                                              Reference remoteId, INameRecord proxy)
        {
            var uri = new UriBuilder(_uri);

            uri.Scheme = "http";
            var token = await _tokenProvider.GetTokenAsync(uri.ToString(),
                                                           TimeSpan.FromHours(24)).ConfigureAwait(false);

            var connection = new RelayStream(
                this, streamId, new ConnectionString(_uri, "proxy", token.TokenString));

            _streamMap.AddOrUpdate(streamId, connection, (r, s) => connection);
            return(connection);
        }
Example #2
0
            public IEventSubscription AddListener <TEvent>(
                IEventListener listener,
                SafeType <TEvent> eventType,
                Action <TEvent> handler) where TEvent : IEvent
            {
                var genericEventType = new SafeType <IEvent>(eventType.Value);

                RelayStream <TEvent> relayStream;

                if (this.relayStreams_.TryGetValue(genericEventType,
                                                   out IRelayStream? genericRelayStream)
                    )
                {
                    relayStream = (genericRelayStream as RelayStream <TEvent>) !;
                }
                else
                {
                    relayStream = new RelayStream <TEvent>(this, eventType);
                    this.relayStreams_.Add(genericEventType, relayStream);
                }

                return(relayStream.AddListener(listener, handler));
            }
Example #3
0
        /// <summary>
        /// Decrypts one stream to another.
        /// </summary>
        /// <param name="encrypted">The encrypted input stream.</param>
        /// <param name="decrypted">The decrypted output stream.</param>
        public void DecryptStream(Stream encrypted, Stream decrypted)
        {
            Covenant.Requires <ArgumentNullException>(encrypted != null, nameof(encrypted));
            Covenant.Requires <ArgumentNullException>(decrypted != null, nameof(decrypted));

            // Wrap the input and output streams with [RelayStream] instances
            // so that we can prevent the [CryptoStream] instances from disposing
            // them (since these don't implement [leaveOpen]).

            using (var hmac = new HMACSHA512(aes.Key))
            {
                byte[] persistedHMAC;

                using (var encryptedRelay = new RelayStream(encrypted, leaveOpen: true))
                {
                    using (var decryptedRelay = new RelayStream(decrypted, leaveOpen: true))
                    {
                        // Process the encrypted header.

                        using (var reader = new BinaryReader(encryptedRelay, Encoding.UTF8, leaveOpen: true))
                        {
                            // Read and verify the unencrypted magic number:

                            try
                            {
                                if (reader.ReadInt32() != Magic)
                                {
                                    throw new FormatException($"The encrypted data was not generated by [{nameof(AesCipher)}].");
                                }
                            }
                            catch (IOException e)
                            {
                                throw new FormatException($"The encrypted data has been truncated or was not generated by [{nameof(AesCipher)}].", e);
                            }

                            // Read IV:

                            var ivLength = reader.ReadInt16();

                            if (ivLength < 0 || ivLength > 1024)
                            {
                                throw new FormatException("Invalid IV length.");
                            }

                            aes.IV = reader.ReadBytes(ivLength);

                            // Read the HMAC:

                            var hmacLength = reader.ReadInt16();

                            if (hmacLength < 0 || hmacLength > 1024)
                            {
                                throw new FormatException("Invalid HMAC length.");
                            }

                            persistedHMAC = reader.ReadBytes(hmacLength);
                        }

                        // Decrypt the input stream to the output while also
                        // computing the HMAC.

                        using (var hmacStream = new CryptoStream(encryptedRelay, hmac, CryptoStreamMode.Read))
                        {
                            using (var decryptor = aes.CreateDecryptor())
                            {
                                using (var decryptorStream = new CryptoStream(hmacStream, decryptor, CryptoStreamMode.Read))
                                {
                                    // Decrypt the random padding.

                                    using (var reader = new BinaryReader(decryptorStream, Encoding.UTF8, leaveOpen: true))
                                    {
                                        var paddingLength = reader.ReadInt16();

                                        if (paddingLength < 0 || paddingLength > short.MaxValue)
                                        {
                                            throw new CryptographicException("The encrypted data has been tampered with or is corrupt: Invalid padding size.");
                                        }

                                        reader.ReadBytes(paddingLength);
                                    }

                                    // Decrypt the user data:

                                    decryptorStream.CopyTo(decryptedRelay);
                                }
                            }
                        }
                    }
                }

                // Ensure that the encrypted data hasn't been tampered with by
                // comparing the peristed and computed HMAC values.

                if (!NeonHelper.ArrayEquals(persistedHMAC, hmac.Hash))
                {
                    throw new CryptographicException("The encrypted data has been tampered with or is corrupt: The persisted and computed HMAC hashes don't match.  ");
                }
            }
        }
Example #4
0
        /// <summary>
        /// Encrypts one stream to another.
        /// </summary>
        /// <param name="decrypted">The decrypted input stream.</param>
        /// <param name="encrypted">The encrypted output stream.</param>
        public void EncryptStream(Stream decrypted, Stream encrypted)
        {
            Covenant.Requires <ArgumentNullException>(decrypted != null, nameof(decrypted));
            Covenant.Requires <ArgumentNullException>(encrypted != null, nameof(encrypted));

            aes.GenerateIV();   // Always generate a new IV before encrypting.

            // Wrap the input and output streams with [RelayStream] instances
            // so that we can prevent the [CryptoStream] instances from disposing
            // them (since these don't implement [leaveOpen]).

            using (var hmac = new HMACSHA512(aes.Key))
            {
                using (var decryptedRelay = new RelayStream(decrypted, leaveOpen: true))
                {
                    using (var encryptedRelay = new RelayStream(encrypted, leaveOpen: true))
                    {
                        long hmacPos;

                        // Write the unencrypted header.

                        using (var writer = new BinaryWriter(encryptedRelay, Encoding.UTF8, leaveOpen: true))
                        {
                            // Write the magic number and IV to the output stream.

                            writer.Write((int)Magic);
                            writer.Write((short)aes.IV.Length);
                            writer.Write(aes.IV);

                            // Write the HMAC512 length followed by that many zeros
                            // as a placeholder for the computed HMAC.  We'll record
                            // the absolute position of these bytes so we can easily
                            // go back and overwrite them with the actual HMAC after
                            // we completed the data encryption.

                            writer.Write((short)hmacZeros.Length);
                            writer.Flush();     // Ensure that the underlying stream position is up-to-date

                            hmacPos = encryptedRelay.Position;

                            writer.Write(hmacZeros);
                        }

                        // Encrypt the input stream to the output while also computing the HMAC.

                        using (var hmacStream = new CryptoStream(encryptedRelay, hmac, CryptoStreamMode.Write))
                        {
                            using (var encryptor = aes.CreateEncryptor())
                            {
                                using (var encryptorStream = new CryptoStream(hmacStream, encryptor, CryptoStreamMode.Write))
                                {
                                    // Write the variable length random padding.

                                    var paddingLength = random.NextIndex(maxPaddingBytes);
                                    var paddingBytes  = new byte[paddingLength];

                                    random.NextBytes(paddingBytes);

                                    using (var writer = new BinaryWriter(encryptorStream, Encoding.UTF8, leaveOpen: true))
                                    {
                                        writer.Write((short)paddingLength);
                                        writer.Write(paddingBytes);
                                    }

                                    // Encrypt the user data:

                                    decryptedRelay.CopyTo(encryptorStream);
                                }
                            }
                        }

                        // Go back and persist the computed HMAC.

                        encryptedRelay.Position = hmacPos;

                        Covenant.Assert(hmac.Hash.Length == CryptoHelper.HMAC512ByteCount);
                        encrypted.Write(hmac.Hash);
                    }
                }
            }
        }