private void AddMember(string friendlyName, Endpoint endpoint) {
			if (this.members.Values.Contains(endpoint)) {
				throw new InvalidOperationException("That member is already in the chatroom.");
			}

			this.members.Add(friendlyName, endpoint);
			this.ChatroomMembersList.Items.Add(friendlyName);
		}
Beispiel #2
0
		/// <summary>
		/// Initializes a new instance of the <see cref="OwnEndpoint" /> class.
		/// </summary>
		/// <param name="contact">The public information for this contact.</param>
		/// <param name="signingPrivateKeyMaterial">The private signing key.</param>
		/// <param name="encryptionPrivateKeyMaterial">The private encryption key.</param>
		/// <param name="inboxOwnerCode">The secret that proves ownership of the inbox at the <see cref="Endpoint.MessageReceivingEndpoint"/>.</param>
		public OwnEndpoint(Endpoint contact, byte[] signingPrivateKeyMaterial, byte[] encryptionPrivateKeyMaterial, string inboxOwnerCode = null) {
			Requires.NotNull(contact, "contact");
			Requires.NotNull(signingPrivateKeyMaterial, "signingPrivateKeyMaterial");
			Requires.NotNull(encryptionPrivateKeyMaterial, "encryptionPrivateKeyMaterial");

			this.PublicEndpoint = contact;
			this.SigningKeyPrivateMaterial = signingPrivateKeyMaterial;
			this.EncryptionKeyPrivateMaterial = encryptionPrivateKeyMaterial;
			this.InboxOwnerCode = inboxOwnerCode;
		}
Beispiel #3
0
        /// <summary>
        /// Executes the send/receive loop until the user exits the chat session with the "#quit" command.
        /// </summary>
        /// <param name="friend">The remote endpoint to send messages to.</param>
        /// <returns>A task representing the asynchronous operation.</returns>
        private async Task ChatLoopAsync(Endpoint friend)
        {
            while (true)
            {
                Console.Write("> ");
                var line = Console.ReadLine();
                if (line == "#quit")
                {
                    return;
                }

                if (line.Length > 0)
                {
                    var payload = new Payload(Encoding.UTF8.GetBytes(line), "text/plain");
                    await this.Channel.PostAsync(payload, new[] { friend }, DateTime.UtcNow + TimeSpan.FromMinutes(5));
                }

                Console.WriteLine("Awaiting friend's reply...");
                var incoming = await this.Channel.ReceiveAsync(longPoll: true);
                foreach (var payloadReceipt in incoming)
                {
                    var message = Encoding.UTF8.GetString(payloadReceipt.Payload.Content);
                    Console.WriteLine("< {0}", message);
                }

                await Task.WhenAll(incoming.Select(receipt => this.Channel.DeleteInboxItemAsync(receipt.Payload)));
            }
        }
Beispiel #4
0
        /// <summary>
        /// Queries the user for the remote endpoint to send messages to.
        /// </summary>
        /// <param name="defaultEndpoint">The user's own endpoint, to use for loopback demos in the event the user has no friend to talk to.</param>
        /// <returns>A task whose result is the remote endpoint to use.</returns>
        private async Task<Endpoint> GetFriendEndpointAsync(Endpoint defaultEndpoint)
        {
            do
            {
                Console.Write("Enter your friend's public endpoint URL (leave blank for loopback): ");
                string url = Console.ReadLine();
                if (string.IsNullOrWhiteSpace(url))
                {
                    return defaultEndpoint;
                }

                var addressBook = new DirectEntryAddressBook(new System.Net.Http.HttpClient());
                var endpoint = await addressBook.LookupAsync(url);
                if (endpoint != null)
                {
                    return endpoint;
                }
                else
                {
                    Console.WriteLine("Unable to find endpoint.");
                    continue;
                }
            }
            while (true);
        }
Beispiel #5
0
            /// <summary>
            /// Initializes a new instance of the <see cref="NotificationPostedReceipt"/> class.
            /// </summary>
            /// <param name="recipient">The inbox that received the notification.</param>
            /// <param name="cloudInboxReceiptTimestamp">The timestamp included in the HTTP response from the server.</param>
            public NotificationPostedReceipt(Endpoint recipient, DateTimeOffset? cloudInboxReceiptTimestamp)
            {
                Requires.NotNull(recipient, "recipient");

                this.Recipient = recipient;
                this.CloudInboxReceiptTimestamp = cloudInboxReceiptTimestamp;
            }
Beispiel #6
0
        /// <summary>
        /// Checks whether the specified identifier yields an endpoint equivalent to this one.
        /// </summary>
        /// <param name="claimingEndpoint">The endpoint that claims to be resolvable from a given identifier.</param>
        /// <param name="claimedIdentifier">The identifier to check.</param>
        /// <param name="cancellationToken">A general cancellation token on the request.</param>
        /// <returns>A task whose result is <c>true</c> if the identifier verified correctly; otherwise <c>false</c>.</returns>
        private async Task<bool> IsVerifiableIdentifierAsync(Endpoint claimingEndpoint, string claimedIdentifier, CancellationToken cancellationToken = default(CancellationToken))
        {
            Requires.NotNull(claimingEndpoint, "claimingEndpoint");
            Requires.NotNullOrEmpty(claimedIdentifier, "claimedIdentifier");

            Endpoint cachedEndpoint;
            lock (this.resolvedIdentifiersCache)
            {
                if (this.resolvedIdentifiersCache.TryGetValue(claimedIdentifier, out cachedEndpoint))
                {
                    return cachedEndpoint.Equals(claimingEndpoint);
                }
            }

            var matchingEndpoint = await Utilities.FastestQualifyingResultAsync(
                this.AddressBooks,
                (ct, addressBook) => addressBook.LookupAsync(claimedIdentifier, ct),
                resolvedEndpoint => claimingEndpoint.Equals(resolvedEndpoint),
                cancellationToken).ConfigureAwait(false);

            if (matchingEndpoint != null)
            {
                lock (this.resolvedIdentifiersCache)
                {
                    if (!this.resolvedIdentifiersCache.ContainsKey(claimedIdentifier))
                    {
                        this.resolvedIdentifiersCache.Add(claimedIdentifier, matchingEndpoint);
                    }
                }
            }

            return matchingEndpoint != null;
        }
Beispiel #7
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;
            }
        }
Beispiel #8
0
        /// <summary>
        /// Gets the set of identifiers this endpoint claims that are verifiable.
        /// </summary>
        /// <param name="endpoint">The endpoint whose authorized identifiers are to be verified.</param>
        /// <param name="cancellationToken">A general cancellation token on the request.</param>
        /// <returns>A task whose result is the set of verified identifiers.</returns>
        public async Task<IReadOnlyCollection<string>> GetVerifiableIdentifiersAsync(Endpoint endpoint, CancellationToken cancellationToken = default(CancellationToken))
        {
            Requires.NotNull(endpoint, "endpoint");

            var verifiedIdentifiers = new List<string>();
            if (endpoint.AuthorizedIdentifiers != null)
            {
                var map = endpoint.AuthorizedIdentifiers.Where(id => id != null).ToDictionary(
                    id => id,
                    id => this.IsVerifiableIdentifierAsync(endpoint, id, cancellationToken));
                await Task.WhenAll(map.Values).ConfigureAwait(false);
                foreach (var result in map)
                {
                    if (result.Value.Result)
                    {
                        verifiedIdentifiers.Add(result.Key);
                    }
                }
            }

            return verifiedIdentifiers;
        }
Beispiel #9
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();
			}
		}
		/// <summary>
		/// Generates a new receiving endpoint.
		/// </summary>
		/// <returns>The newly generated endpoint.</returns>
		/// <remarks>
		/// Depending on the length of the keys set in the provider and the amount of buffered entropy in the operating system,
		/// this method can take an extended period (several seconds) to complete.
		/// </remarks>
		private OwnEndpoint CreateEndpointWithKeys() {
			byte[] privateEncryptionKey, publicEncryptionKey;
			byte[] privateSigningKey, publicSigningKey;

			this.CryptoProvider.GenerateEncryptionKeyPair(out privateEncryptionKey, out publicEncryptionKey);
			this.CryptoProvider.GenerateSigningKeyPair(out privateSigningKey, out publicSigningKey);

			var contact = new Endpoint() {
				EncryptionKeyPublicMaterial = publicEncryptionKey,
				SigningKeyPublicMaterial = publicSigningKey,
			};

			var ownContact = new OwnEndpoint(contact, privateSigningKey, privateEncryptionKey);
			return ownContact;
		}