Captures the key and IV used for a symmetric encryption.
Esempio n. 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);
        }
Esempio n. 2
0
        /// <summary>
        /// Symmetrically encrypts the specified buffer using a randomly generated key.
        /// </summary>
        /// <param name="data">The data to encrypt.</param>
        /// <param name="encryptionVariables">Optional encryption variables to use; or <c>null</c> to use randomly generated ones.</param>
        /// <returns>
        /// The result of the encryption.
        /// </returns>
        public virtual SymmetricEncryptionResult Encrypt(byte[] data, SymmetricEncryptionVariables encryptionVariables)
        {
            Requires.NotNull(data, "data");

            var plaintext  = new MemoryStream(data);
            var ciphertext = new MemoryStream();
            var result     = this.EncryptAsync(plaintext, ciphertext, encryptionVariables, CancellationToken.None).Result;

            return(new SymmetricEncryptionResult(result, ciphertext.ToArray()));
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="SymmetricEncryptionResult"/> class.
 /// </summary>
 /// <param name="encryptionVariables">The key and IV used to encrypt the ciphertext.</param>
 /// <param name="ciphertext">The encrypted data.</param>
 public SymmetricEncryptionResult(SymmetricEncryptionVariables encryptionVariables, byte[] ciphertext)
     : this(Requires.NotNull(encryptionVariables, "encryptionVariables").Key, encryptionVariables.IV, ciphertext)
 {
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="SymmetricEncryptionResult"/> class.
 /// </summary>
 /// <param name="encryptionVariables">The key and IV used to encrypt the ciphertext.</param>
 /// <param name="ciphertext">The encrypted data.</param>
 public SymmetricEncryptionResult(SymmetricEncryptionVariables encryptionVariables, byte[] ciphertext)
     : this(Requires.NotNull(encryptionVariables, "encryptionVariables").Key, encryptionVariables.IV, ciphertext)
 {
 }
Esempio n. 5
0
        /// <summary>
        /// Downloads a <see cref="PayloadReference"/> that is referenced from an incoming inbox item.
        /// </summary>
        /// <param name="inboxItem">The inbox item that referenced the <see cref="PayloadReference"/>.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task representing the asynchronous operation.</returns>
        protected virtual async Task <PayloadReference> DownloadPayloadReferenceAsync(IncomingList.IncomingItem inboxItem, CancellationToken cancellationToken)
        {
            Requires.NotNull(inboxItem, "inboxItem");

            var responseMessage = await this.HttpClient.GetAsync(inboxItem.Location, cancellationToken).ConfigureAwait(false);

            if (responseMessage.StatusCode == HttpStatusCode.NotFound)
            {
                // delete inbox item and move on.
                await this.DeletePayloadReferenceAsync(inboxItem.Location, cancellationToken).ConfigureAwait(false);

                this.Log("Missing payload reference.", null);
                return(null);
            }

            responseMessage.EnsureSuccessStatusCode();
            var responseStream = await responseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false);

            var responseStreamCopy = new MemoryStream();
            await responseStream.CopyToAsync(responseStreamCopy, 4096, cancellationToken).ConfigureAwait(false);

            responseStreamCopy.Position = 0;

            var encryptedKey = await responseStreamCopy.ReadSizeAndBufferAsync(cancellationToken).ConfigureAwait(false);

            var key = WinRTCrypto.CryptographicEngine.Decrypt(this.Endpoint.EncryptionKey, encryptedKey);
            var iv  = await responseStreamCopy.ReadSizeAndBufferAsync(cancellationToken).ConfigureAwait(false);

            var ciphertextStream = await responseStreamCopy.ReadSizeAndStreamAsync(cancellationToken).ConfigureAwait(false);

            var encryptedVariables = new SymmetricEncryptionVariables(key, iv);

            var plainTextPayloadStream = new MemoryStream();

            await this.CryptoServices.DecryptAsync(ciphertextStream, plainTextPayloadStream, encryptedVariables, cancellationToken).ConfigureAwait(false);

            plainTextPayloadStream.Position = 0;
            AsymmetricAlgorithm?signingHashAlgorithm = null;    //// Encoding.UTF8.GetString(await plainTextPayloadStream.ReadSizeAndBufferAsync(cancellationToken));

            byte[] signature = await plainTextPayloadStream.ReadSizeAndBufferAsync(cancellationToken).ConfigureAwait(false);

            long payloadStartPosition = plainTextPayloadStream.Position;
            var  signedBytes          = new byte[plainTextPayloadStream.Length - plainTextPayloadStream.Position];
            await plainTextPayloadStream.ReadAsync(signedBytes, 0, signedBytes.Length).ConfigureAwait(false);

            plainTextPayloadStream.Position = payloadStartPosition;
            var plainTextPayloadReader = new BinaryReader(plainTextPayloadStream);

            var recipientPublicSigningKeyBuffer = plainTextPayloadReader.ReadSizeAndBuffer();

            var creationDateUtc    = DateTime.FromBinary(plainTextPayloadReader.ReadInt64());
            var notificationAuthor = Utilities.DeserializeDataContract <Endpoint>(plainTextPayloadReader);
            var messageReference   = Utilities.DeserializeDataContract <PayloadReference>(plainTextPayloadReader);

            messageReference.ReferenceLocation = inboxItem.Location;
            if (messageReference.HashAlgorithmName == null)
            {
                messageReference.HashAlgorithmName = Utilities.GuessHashAlgorithmFromLength(messageReference.Hash.Length).GetHashAlgorithmName();
            }

            if (!CryptoProviderExtensions.VerifySignatureWithTolerantHashAlgorithm(notificationAuthor.SigningKeyPublicMaterial, signedBytes, signature, signingHashAlgorithm))
            {
                throw new InvalidMessageException();
            }

            if (!Utilities.AreEquivalent(recipientPublicSigningKeyBuffer, this.Endpoint.PublicEndpoint.SigningKeyPublicMaterial))
            {
                throw new InvalidMessageException(Strings.MisdirectedMessage);
            }

            return(messageReference);
        }
Esempio n. 6
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);
        }
        /// <summary>
        /// Symmetrically encrypts a stream.
        /// </summary>
        /// <param name="cryptoProvider">The crypto provider.</param>
        /// <param name="plaintext">The stream of plaintext to encrypt.</param>
        /// <param name="ciphertext">The stream to receive the ciphertext.</param>
        /// <param name="encryptionVariables">An optional key and IV to use. May be <c>null</c> to use randomly generated values.</param>
        /// <param name="cancellationToken">A cancellation token.</param>
        /// <returns>
        /// A task that completes when encryption has completed, whose result is the key and IV to use to decrypt the ciphertext.
        /// </returns>
        public static async Task<SymmetricEncryptionVariables> EncryptAsync(this CryptoSettings cryptoProvider, Stream plaintext, Stream ciphertext, SymmetricEncryptionVariables encryptionVariables = null, CancellationToken cancellationToken = default(CancellationToken))
        {
            Requires.NotNull(plaintext, "plaintext");
            Requires.NotNull(ciphertext, "ciphertext");

            encryptionVariables = ThisOrNewEncryptionVariables(cryptoProvider, encryptionVariables);
            var key = CryptoSettings.SymmetricAlgorithm.CreateSymmetricKey(encryptionVariables.Key);
            using (var encryptor = WinRTCrypto.CryptographicEngine.CreateEncryptor(key, encryptionVariables.IV))
            {
                var cryptoStream = new CryptoStream(ciphertext, encryptor, CryptoStreamMode.Write);
                await plaintext.CopyToAsync(cryptoStream, 4096, cancellationToken).ConfigureAwait(false);
                cryptoStream.FlushFinalBlock();
            }

            return encryptionVariables;
        }
        /// <summary>
        /// Symmetrically encrypts the specified buffer using a randomly generated key.
        /// </summary>
        /// <param name="cryptoProvider">The crypto provider.</param>
        /// <param name="data">The data to encrypt.</param>
        /// <param name="encryptionVariables">Optional encryption variables to use; or <c>null</c> to use randomly generated ones.</param>
        /// <returns>
        /// The result of the encryption.
        /// </returns>
        public static SymmetricEncryptionResult Encrypt(this CryptoSettings cryptoProvider, byte[] data, SymmetricEncryptionVariables encryptionVariables = null)
        {
            Requires.NotNull(data, "data");

            encryptionVariables = ThisOrNewEncryptionVariables(cryptoProvider, encryptionVariables);
            var symmetricKey = CryptoSettings.SymmetricAlgorithm.CreateSymmetricKey(encryptionVariables.Key);
            var cipherTextBuffer = WinRTCrypto.CryptographicEngine.Encrypt(symmetricKey, data, encryptionVariables.IV);
            return new SymmetricEncryptionResult(encryptionVariables, cipherTextBuffer);
        }
 /// <summary>
 /// Returns the specified encryption variables if they are non-null, or generates new ones.
 /// </summary>
 /// <param name="cryptoProvider">The crypto provider.</param>
 /// <param name="encryptionVariables">The encryption variables.</param>
 /// <returns>
 /// A valid set of encryption variables.
 /// </returns>
 private static SymmetricEncryptionVariables ThisOrNewEncryptionVariables(CryptoSettings cryptoProvider, SymmetricEncryptionVariables encryptionVariables)
 {
     if (encryptionVariables == null)
     {
         return NewSymmetricEncryptionVariables(cryptoProvider);
     }
     else
     {
         Requires.Argument(encryptionVariables.Key.Length == cryptoProvider.SymmetricKeySize / 8, "key", "Incorrect length.");
         Requires.Argument(encryptionVariables.IV.Length == CryptoSettings.SymmetricAlgorithm.BlockLength, "iv", "Incorrect length.");
         return encryptionVariables;
     }
 }
Esempio n. 10
0
        /// <summary>
        /// Downloads a <see cref="PayloadReference"/> that is referenced from an incoming inbox item.
        /// </summary>
        /// <param name="inboxItem">The inbox item that referenced the <see cref="PayloadReference"/>.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>The task representing the asynchronous operation.</returns>
        protected virtual async Task<PayloadReference> DownloadPayloadReferenceAsync(IncomingList.IncomingItem inboxItem, CancellationToken cancellationToken)
        {
            Requires.NotNull(inboxItem, "inboxItem");

            var responseMessage = await this.HttpClient.GetAsync(inboxItem.Location, cancellationToken).ConfigureAwait(false);
            if (responseMessage.StatusCode == HttpStatusCode.NotFound)
            {
                // delete inbox item and move on.
                await this.DeletePayloadReferenceAsync(inboxItem.Location, cancellationToken).ConfigureAwait(false);
                this.Log("Missing payload reference.", null);
                return null;
            }

            responseMessage.EnsureSuccessStatusCode();
            var responseStream = await responseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false);
            var responseStreamCopy = new MemoryStream();
            await responseStream.CopyToAsync(responseStreamCopy, 4096, cancellationToken).ConfigureAwait(false);
            responseStreamCopy.Position = 0;

            var encryptedKey = await responseStreamCopy.ReadSizeAndBufferAsync(cancellationToken).ConfigureAwait(false);
            var key = WinRTCrypto.CryptographicEngine.Decrypt(this.Endpoint.EncryptionKey, encryptedKey);
            var iv = await responseStreamCopy.ReadSizeAndBufferAsync(cancellationToken).ConfigureAwait(false);
            var ciphertextStream = await responseStreamCopy.ReadSizeAndStreamAsync(cancellationToken).ConfigureAwait(false);
            var encryptedVariables = new SymmetricEncryptionVariables(key, iv);

            var plainTextPayloadStream = new MemoryStream();
            await this.CryptoServices.DecryptAsync(ciphertextStream, plainTextPayloadStream, encryptedVariables, cancellationToken).ConfigureAwait(false);

            plainTextPayloadStream.Position = 0;
            AsymmetricAlgorithm? signingHashAlgorithm = null;   //// Encoding.UTF8.GetString(await plainTextPayloadStream.ReadSizeAndBufferAsync(cancellationToken));
            byte[] signature = await plainTextPayloadStream.ReadSizeAndBufferAsync(cancellationToken).ConfigureAwait(false);
            long payloadStartPosition = plainTextPayloadStream.Position;
            var signedBytes = new byte[plainTextPayloadStream.Length - plainTextPayloadStream.Position];
            await plainTextPayloadStream.ReadAsync(signedBytes, 0, signedBytes.Length).ConfigureAwait(false);
            plainTextPayloadStream.Position = payloadStartPosition;
            var plainTextPayloadReader = new BinaryReader(plainTextPayloadStream);

            var recipientPublicSigningKeyBuffer = plainTextPayloadReader.ReadSizeAndBuffer();

            var creationDateUtc = DateTime.FromBinary(plainTextPayloadReader.ReadInt64());
            var notificationAuthor = Utilities.DeserializeDataContract<Endpoint>(plainTextPayloadReader);
            var messageReference = Utilities.DeserializeDataContract<PayloadReference>(plainTextPayloadReader);
            messageReference.ReferenceLocation = inboxItem.Location;
            if (messageReference.HashAlgorithmName == null)
            {
                messageReference.HashAlgorithmName = Utilities.GuessHashAlgorithmFromLength(messageReference.Hash.Length).GetHashAlgorithmName();
            }

            if (!CryptoProviderExtensions.VerifySignatureWithTolerantHashAlgorithm(notificationAuthor.SigningKeyPublicMaterial, signedBytes, signature, signingHashAlgorithm))
            {
                throw new InvalidMessageException();
            }

            if (!Utilities.AreEquivalent(recipientPublicSigningKeyBuffer, this.Endpoint.PublicEndpoint.SigningKeyPublicMaterial))
            {
                throw new InvalidMessageException(Strings.MisdirectedMessage);
            }

            return messageReference;
        }
Esempio n. 11
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;
        }
Esempio n. 12
0
 /// <summary>
 /// Symmetrically decrypts a stream.
 /// </summary>
 /// <param name="ciphertext">The stream of ciphertext to decrypt.</param>
 /// <param name="plaintext">The stream to receive the plaintext.</param>
 /// <param name="encryptionVariables">The key and IV to use.</param>
 /// <param name="cancellationToken">A cancellation token.</param>
 /// <returns>A task that represents the asynchronous operation.</returns>
 public abstract Task DecryptAsync(Stream ciphertext, Stream plaintext, SymmetricEncryptionVariables encryptionVariables, CancellationToken cancellationToken);