Example #1
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;
            }
        }
        public async Task ECAsymmetricSigningAndEncryption()
        {
            var bob = new ECDsaCng(521);
            var bobPublic = CngKey.Import(bob.Key.Export(CngKeyBlobFormat.EccPublicBlob), CngKeyBlobFormat.EccPublicBlob);
            var alice = new ECDsaCng(521);
            var alicePublic = CngKey.Import(alice.Key.Export(CngKeyBlobFormat.EccPublicBlob), CngKeyBlobFormat.EccPublicBlob);

            // Bob formulates request.
            var bobRequest = new MemoryStream();
            var bobDH = ECDiffieHellman.Create();
            {
                byte[] bobPublicDH = bobDH.PublicKey.ToByteArray();
                byte[] bobSignedDH = bob.SignData(bobPublicDH);
                await bobRequest.WriteSizeAndBufferAsync(bobPublicDH, CancellationToken.None);
                await bobRequest.WriteSizeAndBufferAsync(bobSignedDH, CancellationToken.None);
                bobRequest.Position = 0;
            }

            // Alice reads request.
            var aliceResponse = new MemoryStream();
            byte[] aliceKeyMaterial;
            var aliceDH = new ECDiffieHellmanCng();
            {
                byte[] bobPublicDH = await bobRequest.ReadSizeAndBufferAsync(CancellationToken.None);
                byte[] bobSignedDH = await bobRequest.ReadSizeAndBufferAsync(CancellationToken.None);
                var bobDsa = new ECDsaCng(bobPublic);
                Assert.IsTrue(bobDsa.VerifyData(bobPublicDH, bobSignedDH));
                var bobDHPK = ECDiffieHellmanCngPublicKey.FromByteArray(bobPublicDH, CngKeyBlobFormat.EccPublicBlob);
                aliceKeyMaterial = aliceDH.DeriveKeyMaterial(bobDHPK);

                await aliceResponse.WriteSizeAndBufferAsync(aliceDH.PublicKey.ToByteArray(), CancellationToken.None);
                await aliceResponse.WriteSizeAndBufferAsync(alice.SignData(aliceDH.PublicKey.ToByteArray()), CancellationToken.None);

                // Alice also adds a secret message.
                using (var aes = SymmetricAlgorithm.Create())
                {
                    using (var encryptor = aes.CreateEncryptor(aliceKeyMaterial, new byte[aes.BlockSize / 8]))
                    {
                        var cipherText = new MemoryStream();
                        using (var cryptoStream = new CryptoStream(cipherText, encryptor, CryptoStreamMode.Write))
                        {
                            cryptoStream.Write(new byte[] { 0x1, 0x3, 0x2 }, 0, 3);
                            cryptoStream.FlushFinalBlock();
                            cipherText.Position = 0;
                            await aliceResponse.WriteSizeAndStreamAsync(cipherText, CancellationToken.None);
                        }
                    }
                }

                aliceResponse.Position = 0;
            }

            // Bob reads response
            byte[] bobKeyMaterial;
            {
                byte[] alicePublicDH = await aliceResponse.ReadSizeAndBufferAsync(CancellationToken.None);
                byte[] aliceSignedDH = await aliceResponse.ReadSizeAndBufferAsync(CancellationToken.None);
                var aliceDsa = new ECDsaCng(alicePublic);
                Assert.IsTrue(aliceDsa.VerifyData(alicePublicDH, aliceSignedDH));
                var aliceDHPK = ECDiffieHellmanCngPublicKey.FromByteArray(alicePublicDH, CngKeyBlobFormat.EccPublicBlob);
                bobKeyMaterial = bobDH.DeriveKeyMaterial(aliceDHPK);

                // And Bob reads Alice's secret message.
                using (var aes = SymmetricAlgorithm.Create())
                {
                    using (var decryptor = aes.CreateDecryptor(aliceKeyMaterial, new byte[aes.BlockSize / 8]))
                    {
                        var plaintext = new MemoryStream();
                        var substream = await aliceResponse.ReadSizeAndStreamAsync(CancellationToken.None);
                        using (var cryptoStream = new CryptoStream(substream, decryptor, CryptoStreamMode.Read))
                        {
                            await cryptoStream.CopyToAsync(plaintext);
                            plaintext.Position = 0;
                            byte[] secretMessage = new byte[1024];
                            int readBytes = plaintext.Read(secretMessage, 0, secretMessage.Length);
                        }
                    }
                }
            }

            CollectionAssert.AreEqual(aliceKeyMaterial, bobKeyMaterial);
        }
Example #3
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();
			}
		}