/// <summary> /// Adds encryption data to provided blob metadata, overwriting previous entry if any. /// Safely creates new metadata object if none is provided. /// </summary> /// <param name="metadata">Optionally existing metadata.</param> /// <param name="encryptionData">Encryption data to add.</param> private static Metadata TransformMetadata(Metadata metadata, EncryptionData encryptionData) { Metadata modifiedMetadata = metadata == default ? new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase) : new Dictionary <string, string>(metadata, StringComparer.OrdinalIgnoreCase); modifiedMetadata[Constants.ClientSideEncryption.EncryptionDataKey] = EncryptionDataSerializer.Serialize(encryptionData); return(modifiedMetadata); }
/// <summary> /// Applies client-side encryption to the data for upload. /// </summary> /// <param name="content"> /// Content to encrypt. /// </param> /// <param name="metadata"> /// Metadata to add encryption metadata to. /// </param> /// <param name="async"> /// Whether to perform this operation asynchronously. /// </param> /// <param name="cancellationToken"> /// Cancellation token. /// </param> /// <returns>Transformed content stream and metadata.</returns> public async Task <(Stream, Metadata)> ClientSideEncryptInternal( Stream content, Metadata metadata, bool async, CancellationToken cancellationToken) { (Stream nonSeekableCiphertext, EncryptionData encryptionData) = await _encryptor.EncryptInternal( content, async, cancellationToken).ConfigureAwait(false); metadata ??= new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); metadata[Constants.ClientSideEncryption.EncryptionDataKey] = EncryptionDataSerializer.Serialize(encryptionData); return(nonSeekableCiphertext, metadata); }
private static async Task UpdateClientsideKeyEncryptionKeyInternal( BlobClient client, ClientSideEncryptionOptions encryptionOptionsOverride, BlobRequestConditions conditions, bool async, CancellationToken cancellationToken) { // argument validation Argument.AssertNotNull(client, nameof(client)); ClientSideEncryptionOptions operationEncryptionOptions = encryptionOptionsOverride ?? client.ClientSideEncryption ?? throw new ArgumentException($"{nameof(ClientSideEncryptionOptions)} are not configured on this client and none were provided for the operation."); Argument.AssertNotNull(operationEncryptionOptions.KeyEncryptionKey, nameof(ClientSideEncryptionOptions.KeyEncryptionKey)); Argument.AssertNotNull(operationEncryptionOptions.KeyResolver, nameof(ClientSideEncryptionOptions.KeyResolver)); Argument.AssertNotNull(operationEncryptionOptions.KeyWrapAlgorithm, nameof(ClientSideEncryptionOptions.KeyWrapAlgorithm)); using (client.ClientConfiguration.Pipeline.BeginLoggingScope(nameof(BlobClient))) { client.ClientConfiguration.Pipeline.LogMethodEnter( nameof(BlobBaseClient), message: $"{nameof(Uri)}: {client.Uri}\n" + $"{nameof(conditions)}: {conditions}"); DiagnosticScope scope = client.ClientConfiguration.ClientDiagnostics.CreateScope($"{nameof(BlobClient)}.{nameof(UpdateClientSideKeyEncryptionKey)}"); try { // hold onto etag, metadata, encryptiondata BlobProperties getPropertiesResponse = await client.GetPropertiesInternal(conditions, async, cancellationToken).ConfigureAwait(false); ETag etag = getPropertiesResponse.ETag; IDictionary <string, string> metadata = getPropertiesResponse.Metadata; EncryptionData encryptionData = BlobClientSideDecryptor.GetAndValidateEncryptionDataOrDefault(metadata) ?? throw new InvalidOperationException("Resource has no client-side encryption key to rotate."); // rotate keywrapping byte[] newWrappedKey = await WrapKeyInternal( await UnwrapKeyInternal( encryptionData, operationEncryptionOptions.KeyResolver, async, cancellationToken).ConfigureAwait(false), operationEncryptionOptions.KeyWrapAlgorithm, operationEncryptionOptions.KeyEncryptionKey, async, cancellationToken).ConfigureAwait(false); // set new wrapped key info and reinsert into metadata encryptionData.WrappedContentKey = new KeyEnvelope { EncryptedKey = newWrappedKey, Algorithm = operationEncryptionOptions.KeyWrapAlgorithm, KeyId = operationEncryptionOptions.KeyEncryptionKey.KeyId }; metadata[Constants.ClientSideEncryption.EncryptionDataKey] = EncryptionDataSerializer.Serialize(encryptionData); // update blob ONLY IF ETAG MATCHES (do not take chances encryption info is now out of sync) BlobRequestConditions modifiedRequestConditions = BlobRequestConditions.CloneOrDefault(conditions) ?? new BlobRequestConditions(); modifiedRequestConditions.IfMatch = etag; await client.SetMetadataInternal(metadata, modifiedRequestConditions, async, cancellationToken).ConfigureAwait(false); } catch (Exception ex) { client.ClientConfiguration.Pipeline.LogException(ex); scope.Failed(ex); throw; } finally { client.ClientConfiguration.Pipeline.LogMethodExit(nameof(BlobBaseClient)); scope.Dispose(); } } }