private async void CreateNewEndpoint_OnClick(object sender, RoutedEventArgs e) { this.CreateNewEndpoint.IsEnabled = false; this.CreateNewEndpoint.Cursor = Cursors.AppStarting; try { var cts = new CancellationTokenSource(); var endpointTask = this.OwnEndpointServices.CreateAsync(cts.Token); var dialog = new SaveFileDialog(); bool? result = dialog.ShowDialog(this); if (result.HasValue && result.Value) { Uri addressBookEntry = await this.OwnEndpointServices.PublishAddressBookEntryAsync(await endpointTask, cts.Token); await this.SetEndpointAsync(await endpointTask, addressBookEntry, cts.Token); using (var stream = dialog.OpenFile()) { var writer = new BinaryWriter(stream, Encoding.UTF8); writer.SerializeDataContract(addressBookEntry); writer.Flush(); await this.Channel.Endpoint.SaveAsync(stream, cts.Token); } } else { cts.Cancel(); } } finally { this.CreateNewEndpoint.Cursor = Cursors.Arrow; this.CreateNewEndpoint.IsEnabled = true; } }
/// <summary> /// Sends the specified dart to the recipients specified in the message. /// </summary> /// <param name="message">The dart to send.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The asynchronous result.</returns> public virtual Task PostAsync(Message message, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(message, "message"); var ms = new MemoryStream(); var writer = new BinaryWriter(ms); writer.SerializeDataContract(message); writer.Flush(); ms.Position = 0; var payload = new Payload(ms.ToArray(), Message.ContentType); var allRecipients = new List<Endpoint>(message.Recipients); if (message.CarbonCopyRecipients != null) { allRecipients.AddRange(message.CarbonCopyRecipients); } var readOnlyRecipients = new ReadOnlyCollection<Endpoint>(allRecipients); return this.Channel.PostAsync(payload, readOnlyRecipients, message.ExpirationUtc, cancellationToken); }
/// <summary> /// Saves the receiving endpoint including private data to the specified stream. /// </summary> /// <param name="target">The stream to write to.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task whose completion signals the save is complete.</returns> public Task SaveAsync(Stream target, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(target, "target"); var ms = new MemoryStream(); using (var writer = new BinaryWriter(ms)) { writer.SerializeDataContract(this); ms.Position = 0; return ms.CopyToAsync(target, 4096, cancellationToken); } }
/// <summary> /// Creates a signed address book entry that describes the public information in this endpoint. /// </summary> /// <param name="cryptoServices">The crypto services to use for signing the address book entry.</param> /// <returns>The address book entry.</returns> public AddressBookEntry CreateAddressBookEntry(CryptoSettings cryptoServices) { Requires.NotNull(cryptoServices, "cryptoServices"); var ms = new MemoryStream(); var writer = new BinaryWriter(ms); var entry = new AddressBookEntry(); writer.SerializeDataContract(this.PublicEndpoint); writer.Flush(); entry.SerializedEndpoint = ms.ToArray(); entry.Signature = WinRTCrypto.CryptographicEngine.Sign(this.SigningKey, entry.SerializedEndpoint); return entry; }
/// <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> /// Encrypts a message and uploads it to the cloud. /// </summary> /// <param name="message">The message being transmitted.</param> /// <param name="expiresUtc">The date after which the message may be destroyed.</param> /// <param name="bytesCopiedProgress">Receives progress in terms of number of bytes uploaded.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task whose result is a reference to the uploaded payload including decryption key.</returns> public virtual async Task<PayloadReference> PostPayloadAsync(Payload message, DateTime expiresUtc, IProgress<int> bytesCopiedProgress = null, CancellationToken cancellationToken = default(CancellationToken)) { Requires.NotNull(message, "message"); Requires.That(expiresUtc.Kind == DateTimeKind.Utc, "expiresUtc", Strings.UTCTimeRequired); Requires.ValidState(this.CloudBlobStorage != null, "BlobStorageProvider must not be null"); cancellationToken.ThrowIfCancellationRequested(); var plainTextStream = new MemoryStream(); var writer = new BinaryWriter(plainTextStream); writer.SerializeDataContract(message); writer.Flush(); var plainTextBuffer = plainTextStream.ToArray(); this.Log("Message plaintext", plainTextBuffer); plainTextStream.Position = 0; var cipherTextStream = new MemoryStream(); var encryptionVariables = await this.CryptoServices.EncryptAsync(plainTextStream, cipherTextStream, cancellationToken: cancellationToken).ConfigureAwait(false); this.Log("Message symmetrically encrypted", cipherTextStream.ToArray()); this.Log("Message symmetric key", encryptionVariables.Key); this.Log("Message symmetric IV", encryptionVariables.IV); cipherTextStream.Position = 0; var hasher = WinRTCrypto.HashAlgorithmProvider.OpenAlgorithm(this.CryptoServices.SymmetricHashAlgorithm); var messageHash = hasher.HashData(cipherTextStream.ToArray()); this.Log("Encrypted message hash", messageHash); cipherTextStream.Position = 0; Uri blobUri = await this.CloudBlobStorage.UploadMessageAsync(cipherTextStream, expiresUtc, contentType: message.ContentType, bytesCopiedProgress: bytesCopiedProgress, cancellationToken: cancellationToken).ConfigureAwait(false); return new PayloadReference(blobUri, messageHash, this.CryptoServices.SymmetricHashAlgorithm.GetHashAlgorithmName(), encryptionVariables.Key, encryptionVariables.IV, expiresUtc); }
/// <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(); } }
/// <summary> /// Encrypts a message and uploads it to the cloud. /// </summary> /// <param name="message">The message being transmitted.</param> /// <param name="expiresUtc">The date after which the message may be destroyed.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The task whose result is a reference to the uploaded payload including decryption key.</returns> protected virtual async Task<PayloadReference> PostPayloadAsync(Payload message, DateTime expiresUtc, CancellationToken cancellationToken) { Requires.NotNull(message, "message"); Requires.That(expiresUtc.Kind == DateTimeKind.Utc, "expiresUtc", Strings.UTCTimeRequired); Requires.ValidState(this.CloudBlobStorage != null, "BlobStorageProvider must not be null"); cancellationToken.ThrowIfCancellationRequested(); var plainTextStream = new MemoryStream(); var writer = new BinaryWriter(plainTextStream); writer.SerializeDataContract(message); writer.Flush(); var plainTextBuffer = plainTextStream.ToArray(); this.Log("Message plaintext", plainTextBuffer); var encryptionResult = this.CryptoServices.Encrypt(plainTextBuffer); this.Log("Message symmetrically encrypted", encryptionResult.Ciphertext); this.Log("Message symmetric key", encryptionResult.Key); this.Log("Message symmetric IV", encryptionResult.IV); var messageHash = this.CryptoServices.Hash(encryptionResult.Ciphertext); this.Log("Encrypted message hash", messageHash); using (MemoryStream cipherTextStream = new MemoryStream(encryptionResult.Ciphertext)) { Uri blobUri = await this.CloudBlobStorage.UploadMessageAsync(cipherTextStream, expiresUtc, cancellationToken: cancellationToken); return new PayloadReference(blobUri, messageHash, encryptionResult.Key, encryptionResult.IV, expiresUtc); } }
/// <summary> /// Creates a signed address book entry that describes the public information in this endpoint. /// </summary> /// <param name="cryptoServices">The crypto services to use for signing the address book entry.</param> /// <returns>The address book entry.</returns> public AddressBookEntry CreateAddressBookEntry(ICryptoProvider cryptoServices) { Requires.NotNull(cryptoServices, "cryptoServices"); var ms = new MemoryStream(); var writer = new BinaryWriter(ms); var entry = new AddressBookEntry(); writer.SerializeDataContract(this.PublicEndpoint); writer.Flush(); entry.SerializedEndpoint = ms.ToArray(); entry.Signature = cryptoServices.Sign(entry.SerializedEndpoint, this.SigningKeyPrivateMaterial); return entry; }