Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public byte[] getRawKey() {

@Override
public String getEncryptionAlgorithm() {
return CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized;
return CosmosEncryptionAlgorithm.AEAES_256_CBC_HMAC_SHA_256_RANDOMIZED;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
import java.time.Duration;
import java.util.List;

/**
* Default implementation for a provider to get a data encryption key - wrapped keys are stored in a Cosmos DB container.
* See https://aka.ms/CosmosClientEncryption for more information on client-side encryption support in Azure Cosmos DB.
*/
public class CosmosDataEncryptionKeyProvider implements DataEncryptionKeyProvider {
// TODO: proper sample and documentation on container
private static final String ContainerPartitionKeyPath = "/id";
Expand All @@ -31,6 +35,11 @@ public CosmosDataEncryptionKeyProvider(EncryptionKeyWrapProvider encryptionKeyWr
this(encryptionKeyWrapProvider, null);
}

/**
* Initializes a new instance of the {@link CosmosDataEncryptionKeyProvider}
* @param encryptionKeyWrapProvider A provider that will be used to wrap (encrypt) and unwrap (decrypt) data encryption keys for envelope based encryption
* @param dekPropertiesTimeToLive Time to live for DEK properties before having to refresh.
*/
public CosmosDataEncryptionKeyProvider(EncryptionKeyWrapProvider encryptionKeyWrapProvider,
Duration dekPropertiesTimeToLive) {
this.EncryptionKeyWrapProvider = encryptionKeyWrapProvider;
Expand All @@ -46,10 +55,18 @@ CosmosAsyncContainer getContainer() {
throw new IllegalStateException("The CosmosDataEncryptionKeyProvider was not initialized.");
}

/**
* Gets a provider that will be used to wrap (encrypt) and unwrap (decrypt) data encryption keys for envelope based encryption.
* @return EncryptionKeyWrapProvider
*/
EncryptionKeyWrapProvider getEncryptionKeyWrapProvider() {
return EncryptionKeyWrapProvider;
}

/**
* Gets Container for data encryption keys.
* @return DataEncryptionKeyContainer
*/
DataEncryptionKeyContainer getDataEncryptionKeyContainer() {
return dataEncryptionKeyContainerCore;
}
Expand All @@ -59,14 +76,18 @@ DekCache getDekCache() {
}

// TODO: @moderakh look into if this method needs to be async.
/**
* Initialize Cosmos DB container for CosmosDataEncryptionKeyProvider to store wrapped DEKs
* @param database Database
* @param containerId ontainer id
*/
void initialize(CosmosAsyncDatabase database,
String containerId) {
String containerId) {
Preconditions.checkNotNull(database, "database");
Preconditions.checkNotNull(containerId, "containerId");

if (this.container != null) {
throw new IllegalStateException("CosmosDataEncryptionKeyProvider has already been initialized.");
}
Preconditions.checkState(this.container != null, "CosmosDataEncryptionKeyProvider has already been initialized.");
Preconditions.checkNotNull(database, "database is null");

CosmosContainerResponse containerResponse = database.createContainerIfNotExists(containerId, CosmosDataEncryptionKeyProvider.ContainerPartitionKeyPath).block();
List<String> partitionKeyPath = containerResponse.getProperties().getPartitionKeyDefinition().getPaths();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public Mono<CosmosItemResponse<DataEncryptionKeyProperties>> createDataEncryptio

Preconditions.checkArgument(StringUtils.isNotEmpty(id), "id is missing");
Preconditions.checkArgument(StringUtils.equals(encryptionAlgorithm,
CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized), "Unsupported Encryption Algorithm " + encryptionAlgorithm);
CosmosEncryptionAlgorithm.AEAES_256_CBC_HMAC_SHA_256_RANDOMIZED), "Unsupported Encryption Algorithm " + encryptionAlgorithm);
Preconditions.checkNotNull(encryptionKeyWrapMetadata, "encryptionKeyWrapMetadata is missing");

byte[] rawDek = DataEncryptionKey.generate(encryptionAlgorithm);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ public InvalidArgumentException(String msg, String argName) {
}
}

static RuntimeException invalidKeySize(String algorithmName, int actualKeylength, int expectedLength) {
static IllegalArgumentException invalidKeySize(String algorithmName, int actualKeylength, int expectedLength) {
return new InvalidArgumentException(
Strings.lenientFormat("Invalid key size for %s; actual: %s, expected: %s",
algorithmName, actualKeylength, expectedLength), "dataEncryptionKey");
}

static RuntimeException invalidCipherTextSize(int actualSize, int minimumSize) {
static IllegalArgumentException invalidCipherTextSize(int actualSize, int minimumSize) {
return new InvalidArgumentException(
Strings.lenientFormat("Invalid cipher text size; actual: %s, minimum expected: %s.",
actualSize, minimumSize), "cipherText");
}

static RuntimeException invalidAlgorithmVersion(byte actual, byte expected) {
static IllegalArgumentException invalidAlgorithmVersion(byte actual, byte expected) {
return new InvalidArgumentException(
Strings.lenientFormat("Invalid encryption algorithm version; actual: %s, expected: %s.",
Bytes.toHex(actual), Bytes.toHex(expected)), "cipherText");
Expand All @@ -36,4 +36,10 @@ static RuntimeException invalidAuthenticationTag() {
"Invalid authentication tag in cipher text.",
"cipherText");
}

static Exception exceptionKeyNotFoundException(
String message,
Exception innerException) {
return new IllegalArgumentException(message, innerException);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@

package com.azure.cosmos.implementation.encryption;

import com.azure.cosmos.implementation.Utils;
import com.azure.cosmos.implementation.apachecommons.lang.StringUtils;
import com.azure.cosmos.implementation.guava25.base.Preconditions;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class SymmetricKey {
}

/**
* Returns a copy of the plain text key
* Gets a copy of the plain text key
* This is needed for actual encryption/decryption.
*
* @return root key byte array
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
import com.azure.cosmos.models.CosmosItemRequestOptions;
import com.azure.cosmos.models.CosmosItemResponse;
import com.azure.cosmos.models.CosmosQueryRequestOptions;
import com.azure.cosmos.models.CosmosQueryRequestOptions;
import com.azure.cosmos.models.FeedResponse;
import com.azure.cosmos.models.ModelBridgeInternal;
import com.azure.cosmos.models.PartitionKey;
Expand Down Expand Up @@ -74,7 +73,7 @@ public void createItemEncrypt_readItemDecrypt() throws Exception {
simpleInMemoryProvider.addKey(keyId, dataEncryptionKey);

encryptionOptions.setDataEncryptionKeyId(keyId);
encryptionOptions.setEncryptionAlgorithm(CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized);
encryptionOptions.setEncryptionAlgorithm(CosmosEncryptionAlgorithm.AEAES_256_CBC_HMAC_SHA_256_RANDOMIZED);
ModelBridgeInternal.setEncryptionOptions(requestOptions, encryptionOptions);

Pojo properties = getItem(UUID.randomUUID().toString());
Expand All @@ -100,7 +99,7 @@ public void upsertItem_readItem() throws Exception {

encryptionOptions.setDataEncryptionKeyId(keyId);
ModelBridgeInternal.setEncryptionOptions(requestOptions, encryptionOptions);
encryptionOptions.setEncryptionAlgorithm(CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized);
encryptionOptions.setEncryptionAlgorithm(CosmosEncryptionAlgorithm.AEAES_256_CBC_HMAC_SHA_256_RANDOMIZED);

Pojo properties = getItem(UUID.randomUUID().toString());
CosmosItemResponse<Pojo> itemResponse = container.upsertItem(properties, requestOptions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ public void canReadKeyEncryptionKeyGeneratedByDotNet() throws Exception {
ObjectNode dataEncryptionKeyProperties = TestUtils.loadPojo("./encryption/dotnet/DataEncryptionKeyProperties.json", ObjectNode.class);
keyContainer.createItem(dataEncryptionKeyProperties).block();

DataEncryptionKey loadedKey = dekProvider.getDataEncryptionKey(dataEncryptionKeyProperties.get("id").asText(), CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized);
DataEncryptionKey loadedKey = dekProvider.getDataEncryptionKey(dataEncryptionKeyProperties.get("id").asText(), CosmosEncryptionAlgorithm.AEAES_256_CBC_HMAC_SHA_256_RANDOMIZED);

assertThat(loadedKey.getEncryptionAlgorithm()).isEqualTo(CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized);
assertThat(loadedKey.getEncryptionAlgorithm()).isEqualTo(CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized);
assertThat(loadedKey.getEncryptionAlgorithm()).isEqualTo(CosmosEncryptionAlgorithm.AEAES_256_CBC_HMAC_SHA_256_RANDOMIZED);
assertThat(loadedKey.getEncryptionAlgorithm()).isEqualTo(CosmosEncryptionAlgorithm.AEAES_256_CBC_HMAC_SHA_256_RANDOMIZED);

EncryptionKeyWrapMetadata keyWrapMetadata = Utils.getSimpleObjectMapper().convertValue(dataEncryptionKeyProperties.get("keyWrapMetadata"), EncryptionKeyWrapMetadata.class);
byte[] expectedWrappedKey = dataEncryptionKeyProperties.get("wrappedDataEncryptionKey").binaryValue();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public byte[] getRawKey() {

@Override
public String getEncryptionAlgorithm() {
return CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized;
return CosmosEncryptionAlgorithm.AEAES_256_CBC_HMAC_SHA_256_RANDOMIZED;
}

@Override
Expand All @@ -89,7 +89,7 @@ public byte[] decryptData(byte[] cipherText) {
com.azure.cosmos.implementation.encryption.api.EncryptionOptions encryptionOptions = new EncryptionOptions();
encryptionOptions.setPathsToEncrypt(ImmutableList.of("/sensitive"));
encryptionOptions.setDataEncryptionKeyId(keyId);
encryptionOptions.setEncryptionAlgorithm(CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized);
encryptionOptions.setEncryptionAlgorithm(CosmosEncryptionAlgorithm.AEAES_256_CBC_HMAC_SHA_256_RANDOMIZED);

TestPojo testDate = getTestDate();
byte[] inputAsByteArray = toByteArray(testDate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public void createItemEncrypt_readItemDecrypt() throws Exception {
encryptionOptions.setPathsToEncrypt(ImmutableList.of("/Sensitive"));

encryptionOptions.setDataEncryptionKeyId(dekId);
encryptionOptions.setEncryptionAlgorithm(CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized);
encryptionOptions.setEncryptionAlgorithm(CosmosEncryptionAlgorithm.AEAES_256_CBC_HMAC_SHA_256_RANDOMIZED);
ModelBridgeInternal.setEncryptionOptions(requestOptions, encryptionOptions);

TestDoc properties = getItem(UUID.randomUUID().toString());
Expand Down Expand Up @@ -224,7 +224,7 @@ private TestDoc getItem(String documentId) {
private static DataEncryptionKeyProperties createDek(CosmosDataEncryptionKeyProvider dekProvider, String dekId) {
CosmosItemResponse<DataEncryptionKeyProperties> dekResponse = dekProvider.getDataEncryptionKeyContainer().createDataEncryptionKeyAsync(
dekId,
CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized,
CosmosEncryptionAlgorithm.AEAES_256_CBC_HMAC_SHA_256_RANDOMIZED,
EncryptionTests.metadata1, null).block();

assertThat(dekResponse.getRequestCharge()).isGreaterThan(0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ public byte[] getRawKey() {

@Override
public String getEncryptionAlgorithm() {
return CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized;
return CosmosEncryptionAlgorithm.AEAES_256_CBC_HMAC_SHA_256_RANDOMIZED;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,11 +199,6 @@ public static final class Properties {
public static final String KeyWrapMetadataType = "type";
public static final String KeyWrapMetadataValue = "value";
public static final String EncryptedInfo = "_ei";
// public static final String DataEncryptionKeyRid = "_ek";
public static final String EncryptionFormatVersion = "_ef";
public static final String EncryptedData = "_ed";
public static final String EncryptionAlgorithm = "_ea";
public static final String DataEncryptionKeyId = "_en";

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public ObjectNode decryptAsync(
}

// get key
DataEncryptionKey inMemoryRawDek = keyProvider.getDataEncryptionKey(encryptionProperties.getDataEncryptionKeyId(), CosmosEncryptionAlgorithm.AEAes256CbcHmacSha256Randomized);
DataEncryptionKey inMemoryRawDek = keyProvider.getDataEncryptionKey(encryptionProperties.getDataEncryptionKeyId(), CosmosEncryptionAlgorithm.AEAES_256_CBC_HMAC_SHA_256_RANDOMIZED);

byte[] plainText = inMemoryRawDek.decryptData(encryptionProperties.getEncryptedData());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@

package com.azure.cosmos.implementation.encryption;

import com.azure.cosmos.implementation.Constants;
import com.azure.cosmos.implementation.apachecommons.lang.StringUtils;
import com.azure.cosmos.implementation.encryption.api.Constants;
import com.azure.cosmos.implementation.guava25.base.Preconditions;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
Expand Down Expand Up @@ -44,10 +43,10 @@ public static ObjectWriter getObjectWriter() {

public ObjectNode toObjectNode() {
ObjectNode objectNode = OBJECT_MAPPER.createObjectNode();
objectNode.put(Constants.Properties.EncryptionFormatVersion, this.encryptionFormatVersion);
objectNode.put(Constants.Properties.EncryptionAlgorithm, this.encryptionAlgorithm);
objectNode.put(Constants.Properties.DataEncryptionKeyId, this.dataEncryptionKeyId);
objectNode.put(Constants.Properties.EncryptedData, this.encryptedData);
objectNode.put(Constants.EncryptionFormatVersion, this.encryptionFormatVersion);
objectNode.put(Constants.EncryptionAlgorithm, this.encryptionAlgorithm);
objectNode.put(Constants.DataEncryptionKeyId, this.dataEncryptionKeyId);
objectNode.put(Constants.EncryptedData, this.encryptedData);
return objectNode;
}

Expand All @@ -74,7 +73,6 @@ public byte[] getEncryptedData() {
return encryptedData;
}


private String encryptionAlgorithm;

private int encryptionFormatVersion;
Expand Down Expand Up @@ -111,10 +109,10 @@ static final class JsonSerializer extends StdSerializer<EncryptionProperties> {
@Override
public void serialize(final EncryptionProperties value, final JsonGenerator generator, final SerializerProvider provider) throws IOException {
generator.writeStartObject();
generator.writeNumberField(Constants.Properties.EncryptionFormatVersion, value.encryptionFormatVersion);
generator.writeStringField(Constants.Properties.EncryptionAlgorithm, value.encryptionAlgorithm);
generator.writeStringField(Constants.Properties.DataEncryptionKeyId, value.dataEncryptionKeyId);
generator.writeBinaryField(Constants.Properties.EncryptedData, value.encryptedData);
generator.writeNumberField(Constants.EncryptionFormatVersion, value.encryptionFormatVersion);
generator.writeStringField(Constants.EncryptionAlgorithm, value.encryptionAlgorithm);
generator.writeStringField(Constants.DataEncryptionKeyId, value.dataEncryptionKeyId);
generator.writeBinaryField(Constants.EncryptedData, value.encryptedData);
generator.writeEndObject();
}
}
Expand All @@ -141,24 +139,24 @@ public EncryptionProperties deserialize(JsonParser jsonParser, DeserializationCo

EncryptionProperties encryptionProperties = new EncryptionProperties();

JsonNode node = root.get(Constants.Properties.EncryptionFormatVersion);
Preconditions.checkNotNull(node, Constants.Properties.EncryptionFormatVersion + "can't deserialize");
validateOrThrow(jsonParser, node.isInt(), Constants.Properties.EncryptionFormatVersion, "can't deserialize");
JsonNode node = root.get(Constants.EncryptionFormatVersion);
Preconditions.checkNotNull(node, Constants.EncryptionFormatVersion + "can't deserialize");
validateOrThrow(jsonParser, node.isInt(), Constants.EncryptionFormatVersion, "can't deserialize");
encryptionProperties.encryptionFormatVersion = node.asInt();

node = root.get(Constants.Properties.EncryptionAlgorithm);
Preconditions.checkNotNull(node, Constants.Properties.EncryptionAlgorithm + "can't deserialize");
validateOrThrow(jsonParser, node.isTextual(), Constants.Properties.EncryptionAlgorithm, "can't deserialize");
node = root.get(Constants.EncryptionAlgorithm);
Preconditions.checkNotNull(node, Constants.EncryptionAlgorithm + "can't deserialize");
validateOrThrow(jsonParser, node.isTextual(), Constants.EncryptionAlgorithm, "can't deserialize");
encryptionProperties.encryptionAlgorithm = node.asText();

node = root.get(Constants.Properties.DataEncryptionKeyId);
Preconditions.checkNotNull(node, Constants.Properties.DataEncryptionKeyId + "can't deserialize");
validateOrThrow(jsonParser, node.isTextual(), Constants.Properties.DataEncryptionKeyId, "can't deserialize");
node = root.get(Constants.DataEncryptionKeyId);
Preconditions.checkNotNull(node, Constants.DataEncryptionKeyId + "can't deserialize");
validateOrThrow(jsonParser, node.isTextual(), Constants.DataEncryptionKeyId, "can't deserialize");
encryptionProperties.dataEncryptionKeyId = node.asText();

node = root.get(Constants.Properties.EncryptedData);
Preconditions.checkNotNull(node, Constants.Properties.EncryptedData + "can't deserialize");
validateOrThrow(jsonParser, node.isBinary() || node.isTextual(), Constants.Properties.EncryptedData, "can't deserialize");
node = root.get(Constants.EncryptedData);
Preconditions.checkNotNull(node, Constants.EncryptedData + "can't deserialize");
validateOrThrow(jsonParser, node.isBinary() || node.isTextual(), Constants.EncryptedData, "can't deserialize");
encryptionProperties.encryptedData = node.binaryValue();

return encryptionProperties;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.cosmos.implementation.encryption.api;

public class Constants {
public static final String DocumentResourcePropertyName = "Documents";
public static final String EncryptionFormatVersion = "_ef";
public static final String EncryptionAlgorithm = "_ea";
public static final String DataEncryptionKeyId = "_en";
public static final String EncryptedData = "_ed";
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

package com.azure.cosmos.implementation.encryption.api;

// TODO: enum string type?
// TODO: moderakh enum string type?

/**
* Algorithms for use with client-side encryption support in Azure Cosmos DB.
Expand All @@ -13,6 +13,6 @@ public class CosmosEncryptionAlgorithm {
/**
* Authenticated Encryption algorithm based on https://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05
*/
public static final String AEAes256CbcHmacSha256Randomized = "AEAes256CbcHmacSha256Randomized";
public static final String AEAES_256_CBC_HMAC_SHA_256_RANDOMIZED = "AEAes256CbcHmacSha256Randomized";

}
Loading