/// <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); }
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)); }
/// <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. "); } } }
/// <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); } } } }