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