Exemple #1
0
        /// <summary>
        /// Downloads the message payload referred to by the specified <see cref="PayloadReference"/>.
        /// </summary>
        /// <param name="notification">The payload reference.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task representing the asynchronous operation.</returns>
        public virtual async Task <Payload> DownloadPayloadAsync(PayloadReference notification, CancellationToken cancellationToken = default(CancellationToken))
        {
            Requires.NotNull(notification, "notification");

            var responseMessage = await this.HttpClient.GetAsync(notification.Location, cancellationToken);

            var messageBuffer = await responseMessage.Content.ReadAsByteArrayAsync();

            // Calculate hash of downloaded message and check that it matches the referenced message hash.
            if (!this.CryptoServices.IsHashMatchWithTolerantHashAlgorithm(messageBuffer, notification.Hash, notification.HashAlgorithmName))
            {
                throw new InvalidMessageException();
            }

            var encryptionVariables = new SymmetricEncryptionVariables(notification.Key, notification.IV);

            var cipherStream    = new MemoryStream(messageBuffer);
            var plainTextStream = new MemoryStream();

            await this.CryptoServices.DecryptAsync(cipherStream, plainTextStream, encryptionVariables, cancellationToken);

            plainTextStream.Position = 0;
            var plainTextReader = new BinaryReader(plainTextStream);
            var message         = Utilities.DeserializeDataContract <Payload>(plainTextReader);

            message.PayloadReferenceUri = notification.ReferenceLocation;
            return(message);
        }
Exemple #2
0
        /// <summary>
        /// Downloads the message payload referred to by the specified <see cref="PayloadReference"/>.
        /// </summary>
        /// <param name="notification">The payload reference.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task representing the asynchronous operation.</returns>
        protected virtual async Task <Payload> DownloadPayloadAsync(PayloadReference notification, CancellationToken cancellationToken)
        {
            Requires.NotNull(notification, "notification");

            var responseMessage = await this.HttpClient.GetAsync(notification.Location, cancellationToken);

            var messageBuffer = await responseMessage.Content.ReadAsByteArrayAsync();

            // Calculate hash of downloaded message and check that it matches the referenced message hash.
            var messageHash = this.CryptoServices.Hash(messageBuffer);

            if (!Utilities.AreEquivalent(messageHash, notification.Hash))
            {
                throw new InvalidMessageException();
            }

            var encryptedResult = new SymmetricEncryptionResult(
                notification.Key,
                notification.IV,
                messageBuffer);

            var plainTextBuffer = this.CryptoServices.Decrypt(encryptedResult);
            var plainTextStream = new MemoryStream(plainTextBuffer);
            var plainTextReader = new BinaryReader(plainTextStream);
            var message         = Utilities.DeserializeDataContract <Payload>(plainTextReader);

            message.PayloadReferenceUri = notification.ReferenceLocation;
            return(message);
        }
Exemple #3
0
        /// <summary>
        /// Shares the reference to a message payload with the specified set of recipients.
        /// </summary>
        /// <param name="messageReference">The payload reference to share.</param>
        /// <param name="recipients">The set of recipients that should be notified of the message.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task representing the asynchronous operation.</returns>
        protected virtual async Task PostPayloadReferenceAsync(PayloadReference messageReference, ReadOnlyCollectionOfEndpoint recipients, CancellationToken cancellationToken = default(CancellationToken))
        {
            Requires.NotNull(messageReference, "messageReference");
            Requires.NotNullOrEmpty(recipients, "recipients");

            // Kick off individual tasks concurrently for each recipient.
            // Each recipient requires cryptography (CPU intensive) to be performed, so don't block the calling thread.
            await TaskEx.WhenAll(
                recipients.Select(recipient => TaskEx.Run(() => this.PostPayloadReferenceAsync(messageReference, recipient, cancellationToken))));
        }
Exemple #4
0
        /// <summary>
        /// Shares the reference to a message payload with the specified recipient.
        /// </summary>
        /// <param name="messageReference">The payload reference to share.</param>
        /// <param name="recipient">The recipient that should be notified of the message.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task representing the asynchronous operation.</returns>
        protected virtual async Task PostPayloadReferenceAsync(PayloadReference messageReference, Endpoint recipient, CancellationToken cancellationToken)
        {
            Requires.NotNull(recipient, "recipient");
            Requires.NotNull(messageReference, "messageReference");

            cancellationToken.ThrowIfCancellationRequested();

            // Prepare the payload.
            var plainTextPayloadStream = new MemoryStream();
            var plainTextPayloadWriter = new BinaryWriter(plainTextPayloadStream);

            // Include the intended recipient's signing certificate so the recipient knows that
            // the message author intended the recipient to receive it (defeats fowarding and re-encrypting
            // a message notification with the intent to deceive a victim that a message was intended for them when it was not.)
            plainTextPayloadWriter.WriteSizeAndBuffer(recipient.SigningKeyPublicMaterial);

            plainTextPayloadWriter.Write(DateTime.UtcNow.ToBinary());

            // Write out the author of this notification (which may be different from the author of the
            // message itself in the case of a "forward").
            plainTextPayloadWriter.SerializeDataContract(this.Endpoint.PublicEndpoint);

            plainTextPayloadWriter.SerializeDataContract(messageReference);
            plainTextPayloadWriter.Flush();
            this.Log("Message invite plaintext", plainTextPayloadStream.ToArray());

            byte[] notificationSignature        = this.CryptoServices.Sign(plainTextPayloadStream.ToArray(), this.Endpoint.SigningKeyPrivateMaterial);
            var    signedPlainTextPayloadStream = new MemoryStream((int)plainTextPayloadStream.Length + notificationSignature.Length + 4);
            await signedPlainTextPayloadStream.WriteSizeAndBufferAsync(notificationSignature, cancellationToken);

            plainTextPayloadStream.Position = 0;
            await plainTextPayloadStream.CopyToAsync(signedPlainTextPayloadStream, 4096, cancellationToken);

            var encryptedPayload = this.CryptoServices.Encrypt(signedPlainTextPayloadStream.ToArray());

            this.Log("Message invite ciphertext", encryptedPayload.Ciphertext);
            this.Log("Message invite key", encryptedPayload.Key);
            this.Log("Message invite IV", encryptedPayload.IV);

            var builder           = new UriBuilder(recipient.MessageReceivingEndpoint);
            var lifetimeInMinutes = (int)(messageReference.ExpiresUtc - DateTime.UtcNow).TotalMinutes;

            builder.Query += "&lifetime=" + lifetimeInMinutes.ToString(CultureInfo.InvariantCulture);

            var postContent  = new MemoryStream();
            var encryptedKey = this.CryptoServices.Encrypt(recipient.EncryptionKeyPublicMaterial, encryptedPayload.Key);

            this.Log("Message invite encrypted key", encryptedKey);
            await postContent.WriteSizeAndBufferAsync(encryptedKey, cancellationToken);

            await postContent.WriteSizeAndBufferAsync(encryptedPayload.IV, cancellationToken);

            await postContent.WriteSizeAndBufferAsync(encryptedPayload.Ciphertext, cancellationToken);

            await postContent.FlushAsync();

            postContent.Position = 0;

            using (var response = await this.HttpClient.PostAsync(builder.Uri, new StreamContent(postContent), cancellationToken)) {
                response.EnsureSuccessStatusCode();
            }
        }
Exemple #5
0
        /// <summary>
        /// Shares the reference to a message payload with the specified recipient.
        /// </summary>
        /// <param name="messageReference">The payload reference to share.</param>
        /// <param name="recipient">The recipient that should be notified of the message.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task representing the asynchronous operation.</returns>
        protected virtual async Task <NotificationPostedReceipt> PostPayloadReferenceAsync(PayloadReference messageReference, Endpoint recipient, CancellationToken cancellationToken)
        {
            Requires.NotNull(recipient, "recipient");
            Requires.NotNull(messageReference, "messageReference");

            cancellationToken.ThrowIfCancellationRequested();

            // Prepare the payload.
            var plainTextPayloadStream = new MemoryStream();
            var plainTextPayloadWriter = new BinaryWriter(plainTextPayloadStream);

            // Include the intended recipient's signing certificate so the recipient knows that
            // the message author intended the recipient to receive it (defeats fowarding and re-encrypting
            // a message notification with the intent to deceive a victim that a message was intended for them when it was not.)
            plainTextPayloadWriter.WriteSizeAndBuffer(recipient.SigningKeyPublicMaterial);

            plainTextPayloadWriter.Write(DateTime.UtcNow.ToBinary());

            // Write out the author of this notification (which may be different from the author of the
            // message itself in the case of a "forward").
            plainTextPayloadWriter.SerializeDataContract(this.Endpoint.PublicEndpoint);

            plainTextPayloadWriter.SerializeDataContract(messageReference);
            plainTextPayloadWriter.Flush();
            this.Log("Message invite plaintext", plainTextPayloadStream.ToArray());

            byte[] notificationSignature        = WinRTCrypto.CryptographicEngine.Sign(this.Endpoint.SigningKey, plainTextPayloadStream.ToArray());
            var    signedPlainTextPayloadStream = new MemoryStream((int)plainTextPayloadStream.Length + notificationSignature.Length + 4);
            ////await signedPlainTextPayloadStream.WriteSizeAndBufferAsync(Encoding.UTF8.GetBytes(this.CryptoServices.HashAlgorithmName), cancellationToken);
            await signedPlainTextPayloadStream.WriteSizeAndBufferAsync(notificationSignature, cancellationToken).ConfigureAwait(false);

            plainTextPayloadStream.Position = 0;
            await plainTextPayloadStream.CopyToAsync(signedPlainTextPayloadStream, 4096, cancellationToken).ConfigureAwait(false);

            signedPlainTextPayloadStream.Position = 0;
            var cipherTextStream   = new MemoryStream();
            var encryptedVariables = await this.CryptoServices.EncryptAsync(signedPlainTextPayloadStream, cipherTextStream, cancellationToken : cancellationToken).ConfigureAwait(false);

            this.Log("Message invite ciphertext", cipherTextStream.ToArray());
            this.Log("Message invite key", encryptedVariables.Key);
            this.Log("Message invite IV", encryptedVariables.IV);

            var builder           = new UriBuilder(recipient.MessageReceivingEndpoint);
            var lifetimeInMinutes = (int)(messageReference.ExpiresUtc - DateTime.UtcNow).TotalMinutes;

            builder.Query += "&lifetime=" + lifetimeInMinutes.ToString(CultureInfo.InvariantCulture);

            var postContent   = new MemoryStream();
            var encryptionKey = CryptoSettings.EncryptionAlgorithm.ImportPublicKey(
                recipient.EncryptionKeyPublicMaterial,
                CryptoSettings.PublicKeyFormat);
            var encryptedKey = WinRTCrypto.CryptographicEngine.Encrypt(encryptionKey, encryptedVariables.Key);

            this.Log("Message invite encrypted key", encryptedKey);
            await postContent.WriteSizeAndBufferAsync(encryptedKey, cancellationToken).ConfigureAwait(false);

            await postContent.WriteSizeAndBufferAsync(encryptedVariables.IV, cancellationToken).ConfigureAwait(false);

            cipherTextStream.Position = 0;
            await postContent.WriteSizeAndStreamAsync(cipherTextStream, cancellationToken).ConfigureAwait(false);

            await postContent.FlushAsync().ConfigureAwait(false);

            postContent.Position = 0;

            using (var response = await this.HttpClient.PostAsync(builder.Uri, new StreamContent(postContent), cancellationToken).ConfigureAwait(false))
            {
                if (response.Content != null)
                {
                    // Just to help in debugging.
                    string responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                }

                response.EnsureSuccessStatusCode();
                var receipt = new NotificationPostedReceipt(recipient, response.Headers.Date);
                return(receipt);
            }
        }
Exemple #6
0
        /// <summary>
        /// Shares the reference to a message payload with the specified set of recipients.
        /// </summary>
        /// <param name="messageReference">The payload reference to share.</param>
        /// <param name="recipients">The set of recipients that should be notified of the message.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task representing the asynchronous operation.</returns>
        protected virtual async Task <IReadOnlyCollection <NotificationPostedReceipt> > PostPayloadReferenceAsync(PayloadReference messageReference, IReadOnlyCollection <Endpoint> recipients, CancellationToken cancellationToken = default(CancellationToken))
        {
            Requires.NotNull(messageReference, "messageReference");
            Requires.NotNullOrEmpty(recipients, "recipients");

            // Kick off individual tasks concurrently for each recipient.
            // Each recipient requires cryptography (CPU intensive) to be performed, so don't block the calling thread.
            var postTasks = recipients.Select(recipient => Task.Run(() => this.PostPayloadReferenceAsync(messageReference, recipient, cancellationToken))).ToList();

            return(await Task.WhenAll(postTasks).ConfigureAwait(false));
        }
Exemple #7
0
        /// <summary>
        /// Downloads the message payload referred to by the specified <see cref="PayloadReference"/>.
        /// </summary>
        /// <param name="notification">The payload reference.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task representing the asynchronous operation.</returns>
        public virtual async Task <Payload> DownloadPayloadAsync(PayloadReference notification, CancellationToken cancellationToken = default(CancellationToken))
        {
            Requires.NotNull(notification, "notification");

            byte[]    messageBuffer = null;
            const int MaxAttempts   = 2;
            int       retry;
            Exception exceptionLeadingToRetry = null;

            for (retry = 0; retry < MaxAttempts; retry++)
            {
                exceptionLeadingToRetry = null;
                try
                {
                    var responseMessage = await this.HttpClient.GetAsync(notification.Location, cancellationToken).ConfigureAwait(false);

                    messageBuffer = await responseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(false);

                    // Calculate hash of downloaded message and check that it matches the referenced message hash.
                    if (!this.CryptoServices.IsHashMatchWithTolerantHashAlgorithm(messageBuffer, notification.Hash, CryptoProviderExtensions.ParseHashAlgorithmName(notification.HashAlgorithmName)))
                    {
                        // Sometimes when the hash mismatches, it's because the download was truncated
                        // (from switching out the application and then switching back, for example).
                        // Check for that before throwing.
                        if (responseMessage.Content.Headers.ContentLength.HasValue && responseMessage.Content.Headers.ContentLength.Value > messageBuffer.Length)
                        {
                            // It looks like the message was truncated. Retry.
                            exceptionLeadingToRetry = new InvalidMessageException();
                            continue;
                        }

                        throw new InvalidMessageException();
                    }

                    // Stop retrying. We got something that worked!
                    break;
                }
                catch (HttpRequestException ex)
                {
                    exceptionLeadingToRetry = ex;
                    continue;
                }
            }

            if (exceptionLeadingToRetry != null)
            {
                if (exceptionLeadingToRetry.StackTrace != null)
                {
                    ExceptionDispatchInfo.Capture(exceptionLeadingToRetry).Throw();
                }
                else
                {
                    throw exceptionLeadingToRetry;
                }
            }

            var encryptionVariables = new SymmetricEncryptionVariables(notification.Key, notification.IV);

            var cipherStream    = new MemoryStream(messageBuffer);
            var plainTextStream = new MemoryStream();

            await this.CryptoServices.DecryptAsync(cipherStream, plainTextStream, encryptionVariables, cancellationToken).ConfigureAwait(false);

            plainTextStream.Position = 0;
            var plainTextReader = new BinaryReader(plainTextStream);
            var message         = Utilities.DeserializeDataContract <Payload>(plainTextReader);

            message.PayloadReferenceUri = notification.ReferenceLocation;
            return(message);
        }
Exemple #8
0
        /// <summary>
        /// Shares the reference to a message payload with the specified recipient.
        /// </summary>
        /// <param name="messageReference">The payload reference to share.</param>
        /// <param name="recipient">The recipient that should be notified of the message.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task representing the asynchronous operation.</returns>
        protected virtual async Task<NotificationPostedReceipt> PostPayloadReferenceAsync(PayloadReference messageReference, Endpoint recipient, CancellationToken cancellationToken)
        {
            Requires.NotNull(recipient, "recipient");
            Requires.NotNull(messageReference, "messageReference");

            cancellationToken.ThrowIfCancellationRequested();

            // Prepare the payload.
            var plainTextPayloadStream = new MemoryStream();
            var plainTextPayloadWriter = new BinaryWriter(plainTextPayloadStream);

            // Include the intended recipient's signing certificate so the recipient knows that
            // the message author intended the recipient to receive it (defeats fowarding and re-encrypting
            // a message notification with the intent to deceive a victim that a message was intended for them when it was not.)
            plainTextPayloadWriter.WriteSizeAndBuffer(recipient.SigningKeyPublicMaterial);

            plainTextPayloadWriter.Write(DateTime.UtcNow.ToBinary());

            // Write out the author of this notification (which may be different from the author of the
            // message itself in the case of a "forward").
            plainTextPayloadWriter.SerializeDataContract(this.Endpoint.PublicEndpoint);

            plainTextPayloadWriter.SerializeDataContract(messageReference);
            plainTextPayloadWriter.Flush();
            this.Log("Message invite plaintext", plainTextPayloadStream.ToArray());

            byte[] notificationSignature = WinRTCrypto.CryptographicEngine.Sign(this.Endpoint.SigningKey, plainTextPayloadStream.ToArray());
            var signedPlainTextPayloadStream = new MemoryStream((int)plainTextPayloadStream.Length + notificationSignature.Length + 4);
            ////await signedPlainTextPayloadStream.WriteSizeAndBufferAsync(Encoding.UTF8.GetBytes(this.CryptoServices.HashAlgorithmName), cancellationToken);
            await signedPlainTextPayloadStream.WriteSizeAndBufferAsync(notificationSignature, cancellationToken).ConfigureAwait(false);
            plainTextPayloadStream.Position = 0;
            await plainTextPayloadStream.CopyToAsync(signedPlainTextPayloadStream, 4096, cancellationToken).ConfigureAwait(false);
            signedPlainTextPayloadStream.Position = 0;
            var cipherTextStream = new MemoryStream();
            var encryptedVariables = await this.CryptoServices.EncryptAsync(signedPlainTextPayloadStream, cipherTextStream, cancellationToken: cancellationToken).ConfigureAwait(false);
            this.Log("Message invite ciphertext", cipherTextStream.ToArray());
            this.Log("Message invite key", encryptedVariables.Key);
            this.Log("Message invite IV", encryptedVariables.IV);

            var builder = new UriBuilder(recipient.MessageReceivingEndpoint);
            var lifetimeInMinutes = (int)(messageReference.ExpiresUtc - DateTime.UtcNow).TotalMinutes;
            builder.Query += "&lifetime=" + lifetimeInMinutes.ToString(CultureInfo.InvariantCulture);

            var postContent = new MemoryStream();
            var encryptionKey = CryptoSettings.EncryptionAlgorithm.ImportPublicKey(
                recipient.EncryptionKeyPublicMaterial,
                CryptoSettings.PublicKeyFormat);
            var encryptedKey = WinRTCrypto.CryptographicEngine.Encrypt(encryptionKey, encryptedVariables.Key);
            this.Log("Message invite encrypted key", encryptedKey);
            await postContent.WriteSizeAndBufferAsync(encryptedKey, cancellationToken).ConfigureAwait(false);
            await postContent.WriteSizeAndBufferAsync(encryptedVariables.IV, cancellationToken).ConfigureAwait(false);
            cipherTextStream.Position = 0;
            await postContent.WriteSizeAndStreamAsync(cipherTextStream, cancellationToken).ConfigureAwait(false);
            await postContent.FlushAsync().ConfigureAwait(false);
            postContent.Position = 0;

            using (var response = await this.HttpClient.PostAsync(builder.Uri, new StreamContent(postContent), cancellationToken).ConfigureAwait(false))
            {
                if (response.Content != null)
                {
                    // Just to help in debugging.
                    string responseContent = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                }

                response.EnsureSuccessStatusCode();
                var receipt = new NotificationPostedReceipt(recipient, response.Headers.Date);
                return receipt;
            }
        }
Exemple #9
0
        /// <summary>
        /// Shares the reference to a message payload with the specified set of recipients.
        /// </summary>
        /// <param name="messageReference">The payload reference to share.</param>
        /// <param name="recipients">The set of recipients that should be notified of the message.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task representing the asynchronous operation.</returns>
        protected virtual async Task<IReadOnlyCollection<NotificationPostedReceipt>> PostPayloadReferenceAsync(PayloadReference messageReference, IReadOnlyCollection<Endpoint> recipients, CancellationToken cancellationToken = default(CancellationToken))
        {
            Requires.NotNull(messageReference, "messageReference");
            Requires.NotNullOrEmpty(recipients, "recipients");

            // Kick off individual tasks concurrently for each recipient.
            // Each recipient requires cryptography (CPU intensive) to be performed, so don't block the calling thread.
            var postTasks = recipients.Select(recipient => Task.Run(() => this.PostPayloadReferenceAsync(messageReference, recipient, cancellationToken))).ToList();
            return await Task.WhenAll(postTasks).ConfigureAwait(false);
        }
Exemple #10
0
        /// <summary>
        /// Downloads the message payload referred to by the specified <see cref="PayloadReference"/>.
        /// </summary>
        /// <param name="notification">The payload reference.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task representing the asynchronous operation.</returns>
        public virtual async Task<Payload> DownloadPayloadAsync(PayloadReference notification, CancellationToken cancellationToken = default(CancellationToken))
        {
            Requires.NotNull(notification, "notification");

            byte[] messageBuffer = null;
            const int MaxAttempts = 2;
            int retry;
            Exception exceptionLeadingToRetry = null;
            for (retry = 0; retry < MaxAttempts; retry++)
            {
                exceptionLeadingToRetry = null;
                try
                {
                    var responseMessage = await this.HttpClient.GetAsync(notification.Location, cancellationToken).ConfigureAwait(false);
                    messageBuffer = await responseMessage.Content.ReadAsByteArrayAsync().ConfigureAwait(false);

                    // Calculate hash of downloaded message and check that it matches the referenced message hash.
                    if (!this.CryptoServices.IsHashMatchWithTolerantHashAlgorithm(messageBuffer, notification.Hash, CryptoProviderExtensions.ParseHashAlgorithmName(notification.HashAlgorithmName)))
                    {
                        // Sometimes when the hash mismatches, it's because the download was truncated
                        // (from switching out the application and then switching back, for example).
                        // Check for that before throwing.
                        if (responseMessage.Content.Headers.ContentLength.HasValue && responseMessage.Content.Headers.ContentLength.Value > messageBuffer.Length)
                        {
                            // It looks like the message was truncated. Retry.
                            exceptionLeadingToRetry = new InvalidMessageException();
                            continue;
                        }

                        throw new InvalidMessageException();
                    }

                    // Stop retrying. We got something that worked!
                    break;
                }
                catch (HttpRequestException ex)
                {
                    exceptionLeadingToRetry = ex;
                    continue;
                }
            }

            if (exceptionLeadingToRetry != null)
            {
                if (exceptionLeadingToRetry.StackTrace != null)
                {
                    ExceptionDispatchInfo.Capture(exceptionLeadingToRetry).Throw();
                }
                else
                {
                    throw exceptionLeadingToRetry;
                }
            }

            var encryptionVariables = new SymmetricEncryptionVariables(notification.Key, notification.IV);

            var cipherStream = new MemoryStream(messageBuffer);
            var plainTextStream = new MemoryStream();
            await this.CryptoServices.DecryptAsync(cipherStream, plainTextStream, encryptionVariables, cancellationToken).ConfigureAwait(false);
            plainTextStream.Position = 0;
            var plainTextReader = new BinaryReader(plainTextStream);
            var message = Utilities.DeserializeDataContract<Payload>(plainTextReader);
            message.PayloadReferenceUri = notification.ReferenceLocation;
            return message;
        }
Exemple #11
0
		/// <summary>
		/// Shares the reference to a message payload with the specified recipient.
		/// </summary>
		/// <param name="messageReference">The payload reference to share.</param>
		/// <param name="recipient">The recipient that should be notified of the message.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <returns>The task representing the asynchronous operation.</returns>
		protected virtual async Task PostPayloadReferenceAsync(PayloadReference messageReference, Endpoint recipient, CancellationToken cancellationToken) {
			Requires.NotNull(recipient, "recipient");
			Requires.NotNull(messageReference, "messageReference");

			cancellationToken.ThrowIfCancellationRequested();

			// Prepare the payload.
			var plainTextPayloadStream = new MemoryStream();
			var plainTextPayloadWriter = new BinaryWriter(plainTextPayloadStream);

			// Include the intended recipient's signing certificate so the recipient knows that 
			// the message author intended the recipient to receive it (defeats fowarding and re-encrypting
			// a message notification with the intent to deceive a victim that a message was intended for them when it was not.)
			plainTextPayloadWriter.WriteSizeAndBuffer(recipient.SigningKeyPublicMaterial);

			plainTextPayloadWriter.Write(DateTime.UtcNow.ToBinary());

			// Write out the author of this notification (which may be different from the author of the 
			// message itself in the case of a "forward").
			plainTextPayloadWriter.SerializeDataContract(this.Endpoint.PublicEndpoint);

			plainTextPayloadWriter.SerializeDataContract(messageReference);
			plainTextPayloadWriter.Flush();
			this.Log("Message invite plaintext", plainTextPayloadStream.ToArray());

			byte[] notificationSignature = this.CryptoServices.Sign(plainTextPayloadStream.ToArray(), this.Endpoint.SigningKeyPrivateMaterial);
			var signedPlainTextPayloadStream = new MemoryStream((int)plainTextPayloadStream.Length + notificationSignature.Length + 4);
			await signedPlainTextPayloadStream.WriteSizeAndBufferAsync(notificationSignature, cancellationToken);
			plainTextPayloadStream.Position = 0;
			await plainTextPayloadStream.CopyToAsync(signedPlainTextPayloadStream, 4096, cancellationToken);
			var encryptedPayload = this.CryptoServices.Encrypt(signedPlainTextPayloadStream.ToArray());
			this.Log("Message invite ciphertext", encryptedPayload.Ciphertext);
			this.Log("Message invite key", encryptedPayload.Key);
			this.Log("Message invite IV", encryptedPayload.IV);

			var builder = new UriBuilder(recipient.MessageReceivingEndpoint);
			var lifetimeInMinutes = (int)(messageReference.ExpiresUtc - DateTime.UtcNow).TotalMinutes;
			builder.Query += "&lifetime=" + lifetimeInMinutes.ToString(CultureInfo.InvariantCulture);

			var postContent = new MemoryStream();
			var encryptedKey = this.CryptoServices.Encrypt(recipient.EncryptionKeyPublicMaterial, encryptedPayload.Key);
			this.Log("Message invite encrypted key", encryptedKey);
			await postContent.WriteSizeAndBufferAsync(encryptedKey, cancellationToken);
			await postContent.WriteSizeAndBufferAsync(encryptedPayload.IV, cancellationToken);
			await postContent.WriteSizeAndBufferAsync(encryptedPayload.Ciphertext, cancellationToken);
			await postContent.FlushAsync();
			postContent.Position = 0;

			using (var response = await this.HttpClient.PostAsync(builder.Uri, new StreamContent(postContent), cancellationToken)) {
				response.EnsureSuccessStatusCode();
			}
		}
Exemple #12
0
		/// <summary>
		/// Shares the reference to a message payload with the specified set of recipients.
		/// </summary>
		/// <param name="messageReference">The payload reference to share.</param>
		/// <param name="recipients">The set of recipients that should be notified of the message.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <returns>The task representing the asynchronous operation.</returns>
		protected virtual async Task PostPayloadReferenceAsync(PayloadReference messageReference, ReadOnlyCollectionOfEndpoint recipients, CancellationToken cancellationToken = default(CancellationToken)) {
			Requires.NotNull(messageReference, "messageReference");
			Requires.NotNullOrEmpty(recipients, "recipients");

			// Kick off individual tasks concurrently for each recipient.
			// Each recipient requires cryptography (CPU intensive) to be performed, so don't block the calling thread.
			await TaskEx.WhenAll(
				recipients.Select(recipient => TaskEx.Run(() => this.PostPayloadReferenceAsync(messageReference, recipient, cancellationToken))));
		}
Exemple #13
0
		/// <summary>
		/// Downloads the message payload referred to by the specified <see cref="PayloadReference"/>.
		/// </summary>
		/// <param name="notification">The payload reference.</param>
		/// <param name="cancellationToken">The cancellation token.</param>
		/// <returns>The task representing the asynchronous operation.</returns>
		protected virtual async Task<Payload> DownloadPayloadAsync(PayloadReference notification, CancellationToken cancellationToken) {
			Requires.NotNull(notification, "notification");

			var responseMessage = await this.HttpClient.GetAsync(notification.Location, cancellationToken);
			var messageBuffer = await responseMessage.Content.ReadAsByteArrayAsync();

			// Calculate hash of downloaded message and check that it matches the referenced message hash.
			var messageHash = this.CryptoServices.Hash(messageBuffer);
			if (!Utilities.AreEquivalent(messageHash, notification.Hash)) {
				throw new InvalidMessageException();
			}

			var encryptedResult = new SymmetricEncryptionResult(
				notification.Key,
				notification.IV,
				messageBuffer);

			var plainTextBuffer = this.CryptoServices.Decrypt(encryptedResult);
			var plainTextStream = new MemoryStream(plainTextBuffer);
			var plainTextReader = new BinaryReader(plainTextStream);
			var message = Utilities.DeserializeDataContract<Payload>(plainTextReader);
			message.PayloadReferenceUri = notification.ReferenceLocation;
			return message;
		}