Example #1
0
        public async Task EnvelopeAsync(byte[] unencryptedBytes, int symmetricKeySizeBits, int symmetricInitializationVectorSizeBits, Stream encryptedOutputStream, CancellationToken cancellationToken)
        {
            try
            {
                AmazonS3Credentials kmsCredentials = await GetCredentialsAsync();

                if (symmetricKeySizeBits != 256)
                {
                    throw new ArgumentOutOfRangeException(nameof(symmetricKeySizeBits), "Invalid value " + symmetricKeySizeBits + ". Only 256-bit keys are supported.");
                }

                if (symmetricInitializationVectorSizeBits != 128)
                {
                    throw new ArgumentOutOfRangeException(nameof(symmetricInitializationVectorSizeBits), "Invalid value " + symmetricInitializationVectorSizeBits + ". Only 128-bit initialization vectors are supported.");
                }

                AmazonKeyManagementServiceClient kmsClient = new AmazonKeyManagementServiceClient(kmsCredentials.AccessKeyId, kmsCredentials.SecretAccessKey, kmsCredentials.SessionToken, kmsCredentials.RegionEndpoint);

                // generate a symmetric data key
                GenerateDataKeyResponse dataKeyResponse = await kmsClient.GenerateDataKeyAsync(new GenerateDataKeyRequest
                {
                    KeyId   = kmsCredentials.CustomerMasterKey,
                    KeySpec = DataKeySpec.AES_256
                }, cancellationToken);

                // write encrypted payload

                // write encrypted data key length and bytes
                byte[] encryptedDataKeyBytes       = dataKeyResponse.CiphertextBlob.ToArray();
                byte[] encryptedDataKeyBytesLength = BitConverter.GetBytes(encryptedDataKeyBytes.Length);
                encryptedOutputStream.Write(encryptedDataKeyBytesLength, 0, encryptedDataKeyBytesLength.Length);
                encryptedOutputStream.Write(encryptedDataKeyBytes, 0, encryptedDataKeyBytes.Length);

                // write encrypted random initialization vector length and bytes
                Random random = new Random();
                byte[] initializationVectorBytes = new byte[16];
                random.NextBytes(initializationVectorBytes);

                byte[] encryptedInitializationVectorBytes = (await kmsClient.EncryptAsync(new EncryptRequest
                {
                    KeyId = kmsCredentials.CustomerMasterKey,
                    Plaintext = new MemoryStream(initializationVectorBytes)
                }, cancellationToken)).CiphertextBlob.ToArray();

                byte[] encryptedInitializationVectorBytesLength = BitConverter.GetBytes(encryptedInitializationVectorBytes.Length);
                encryptedOutputStream.Write(encryptedInitializationVectorBytesLength, 0, encryptedInitializationVectorBytesLength.Length);
                encryptedOutputStream.Write(encryptedInitializationVectorBytes, 0, encryptedInitializationVectorBytes.Length);

                // write symmetrically encrypted bytes
                byte[] dataKeyBytes = dataKeyResponse.Plaintext.ToArray();
                SymmetricEncryption symmetricEncryption = new SymmetricEncryption(dataKeyBytes, initializationVectorBytes);
                byte[] encryptedBytes = symmetricEncryption.Encrypt(unencryptedBytes);
                encryptedOutputStream.Write(encryptedBytes, 0, encryptedBytes.Length);
            }
            // the following catch statements attempt to filter out expected exceptions (e.g., due to naturally lacking internet connections)
            // from those that are fixable errors within the app. expected exceptions are logged but not reported to the app center, whereas
            // all others are reported to the app center. our approach is to whitelist excepted exceptions as we see them appear in the app
            // center, in order to ensure that we don't miss any fixable errors.
            catch (HttpRequestException ex)
            {
                bool logged = false;

                if (ex.InnerException is WebException)
                {
                    WebException webException = ex.InnerException as WebException;

                    if (IGNORED_WEB_EXCEPTION_STATUSES.Contains(webException.Status))
                    {
                        LogKmsEnvelopeException(ex);
                        logged = true;
                    }
                }

                if (!logged)
                {
                    ReportKmsEnvelopeException(ex);
                }

                throw ex;
            }
            catch (WebException webException)
            {
                if (IGNORED_WEB_EXCEPTION_STATUSES.Contains(webException.Status))
                {
                    LogKmsEnvelopeException(webException);
                }
                else
                {
                    ReportKmsEnvelopeException(webException);
                }

                throw webException;
            }
            catch (OperationCanceledException ex)
            {
                LogKmsEnvelopeException(ex);

                throw ex;
            }
            catch (Exception ex)
            {
                ReportKmsEnvelopeException(ex);

                throw ex;
            }
        }
Example #2
0
        public Task <AmazonS3Credentials> GetCredentialsAsync()
        {
            lock (_getCredentialsTaskLocker)
            {
                // if the get credentials task is in a state from which we wouldn't expect to return a presently
                // valid set of credentials, then start a new task to check/refresh the credentials.
                if (_getCredentialsTask == null ||
                    _getCredentialsTask.Status == TaskStatus.Canceled ||
                    _getCredentialsTask.Status == TaskStatus.Faulted ||
                    _getCredentialsTask.Status == TaskStatus.RanToCompletion)
                {
                    _getCredentialsTask = Task.Run(async() =>
                    {
                        // if the credentials we currently hold will be valid for a while, then simply return them.
                        if (AmazonS3Credentials?.WillBeValidFor(TimeSpan.FromHours(1)) ?? false)
                        {
                            return(AmazonS3Credentials);
                        }
                        else
                        {
                            AmazonS3Credentials = null;
                        }

                        // we should always have an account
                        if (Account == null)
                        {
                            Exception noAccountException = new Exception("Tried to get credentials without an account.");
                            SensusException.Report(noAccountException);
                            throw noAccountException;
                        }

                        string credentialsJSON = await new Uri(string.Format(_getCredentialsURL, Account.ParticipantId, Account.Password, SensusServiceHelper.Get().DeviceId)).DownloadStringAsync();

                        // deserialize credentials
                        try
                        {
                            AmazonS3Credentials = credentialsJSON.DeserializeJson <AmazonS3Credentials>();
                        }
                        catch (Exception ex)
                        {
                            SensusException.Report("Exception while deserializing AWS S3 credentials.", ex);
                            throw ex;
                        }

                        // check properties. trim while we're at it.

                        if (string.IsNullOrWhiteSpace(AmazonS3Credentials.AccessKeyId = AmazonS3Credentials.AccessKeyId?.Trim()))
                        {
                            SensusException.Report("Empty " + nameof(AmazonS3Credentials.AccessKeyId) + " returned by authentication service for participant " + (Account.ParticipantId ?? "[null]."));
                        }

                        if (string.IsNullOrWhiteSpace(AmazonS3Credentials.CustomerMasterKey = AmazonS3Credentials.CustomerMasterKey?.Trim()))
                        {
                            SensusException.Report("Empty " + nameof(AmazonS3Credentials.CustomerMasterKey) + " returned by authentication service for participant " + (Account.ParticipantId ?? "[null]."));
                        }

                        if (string.IsNullOrWhiteSpace(AmazonS3Credentials.ExpirationUnixTimeMilliseconds = AmazonS3Credentials.ExpirationUnixTimeMilliseconds?.Trim()))
                        {
                            SensusException.Report("Empty " + nameof(AmazonS3Credentials.ExpirationUnixTimeMilliseconds) + " returned by authentication service for participant " + (Account.ParticipantId ?? "[null]."));
                        }

                        if (string.IsNullOrWhiteSpace(AmazonS3Credentials.ProtocolId = AmazonS3Credentials.ProtocolId?.Trim()))
                        {
                            SensusException.Report("Empty " + nameof(AmazonS3Credentials.ProtocolId) + " returned by authentication service for participant " + (Account.ParticipantId ?? "[null]."));
                        }

                        if (string.IsNullOrWhiteSpace(AmazonS3Credentials.ProtocolURL = AmazonS3Credentials.ProtocolURL?.Trim()))
                        {
                            SensusException.Report("Empty " + nameof(AmazonS3Credentials.ProtocolURL) + " returned by authentication service for participant " + (Account.ParticipantId ?? "[null]."));
                        }

                        if (string.IsNullOrWhiteSpace(AmazonS3Credentials.Region = AmazonS3Credentials.Region?.Trim()))
                        {
                            SensusException.Report("Empty " + nameof(AmazonS3Credentials.Region) + " returned by authentication service for participant " + (Account.ParticipantId ?? "[null]."));
                        }

                        if (string.IsNullOrWhiteSpace(AmazonS3Credentials.SecretAccessKey = AmazonS3Credentials.SecretAccessKey?.Trim()))
                        {
                            SensusException.Report("Empty " + nameof(AmazonS3Credentials.SecretAccessKey) + " returned by authentication service for participant " + (Account.ParticipantId ?? "[null]."));
                        }

                        if (string.IsNullOrWhiteSpace(AmazonS3Credentials.SessionToken = AmazonS3Credentials.SessionToken?.Trim()))
                        {
                            SensusException.Report("Empty " + nameof(AmazonS3Credentials.SessionToken) + " returned by authentication service for participant " + (Account.ParticipantId ?? "[null]."));
                        }

                        // save the app state to hang on to the credentials
                        await SensusServiceHelper.Get().SaveAsync();

                        return(AmazonS3Credentials);
                    });
                }

                return(_getCredentialsTask);
            }
        }