diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/AzureKeyVaultKeyStoreProvider.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/AzureKeyVaultKeyStoreProvider.cs new file mode 100644 index 0000000000..2c01f4fb17 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/AzureKeyVaultKeyStoreProvider.cs @@ -0,0 +1,352 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Linq; +using System.Text; +using Azure.Core; +using Azure.Security.KeyVault.Keys.Cryptography; +using Microsoft.Data.Encryption.Cryptography; + +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.AzureKeyVaultProvider +{ + /// + /// Implementation of key encryption key store provider that allows client applications to access data when a + /// key encryption key is stored in Microsoft Azure Key Vault. + /// + internal class AzureKeyVaultKeyStoreProvider : EncryptionKeyStoreProvider + { + #region Properties + + /// + /// Name of the Encryption Key Store Provider implemetation + /// + public override string ProviderName { get; } = "AZURE_KEY_VAULT"; + + /// + /// Key storage and cryptography client + /// + private KeyCryptographer KeyCryptographer { get; set; } + + /// + /// Algorithm version + /// + private readonly static byte[] firstVersion = new byte[] { 0x01 }; + + private readonly static KeyWrapAlgorithm keyWrapAlgorithm = KeyWrapAlgorithm.RsaOaep; + + /// + /// List of Trusted Endpoints + /// + public readonly string[] TrustedEndPoints; + + #endregion + + #region Constructors + /// + /// Constructor that takes an implementation of Token Credential that is capable of providing an OAuth Token. + /// + /// + public AzureKeyVaultKeyStoreProvider(TokenCredential tokenCredential) : + this(tokenCredential, Constants.AzureKeyVaultPublicDomainNames) + { } + + /// + /// Constructor that takes an implementation of Token Credential that is capable of providing an OAuth Token and a trusted endpoint. + /// + /// Instance of an implementation of Token Credential that is capable of providing an OAuth Token. + /// TrustedEndpoint is used to validate the key encryption key path. + public AzureKeyVaultKeyStoreProvider(TokenCredential tokenCredential, string trustedEndPoint) : + this(tokenCredential, new[] { trustedEndPoint }) + { } + + /// + /// Constructor that takes an instance of an implementation of Token Credential that is capable of providing an OAuth Token + /// and an array of trusted endpoints. + /// + /// Instance of an implementation of Token Credential that is capable of providing an OAuth Token + /// TrustedEndpoints are used to validate the key encryption key path + public AzureKeyVaultKeyStoreProvider(TokenCredential tokenCredential, string[] trustedEndPoints) + { + tokenCredential.ValidateNotNull(nameof(tokenCredential)); + trustedEndPoints.ValidateNotNull(nameof(trustedEndPoints)); + trustedEndPoints.ValidateNotEmpty(nameof(trustedEndPoints)); + trustedEndPoints.ValidateNotNullOrWhitespaceForEach(nameof(trustedEndPoints)); + + KeyCryptographer = new KeyCryptographer(tokenCredential); + TrustedEndPoints = trustedEndPoints; + } + #endregion + + #region Public methods + + /// + /// Uses an asymmetric key identified by the key path to sign the key encryption key metadata consisting of (keyEncryptionKeyPath, allowEnclaveComputations bit, providerName). + /// + /// Identifier of an asymmetric key in Azure Key Vault. + /// Indicates whether the key encryption key supports enclave computations. + /// The signature of the key encryption key metadata. + public override byte[] Sign(string encryptionKeyId, bool allowEnclaveComputations) + { + ValidateNonEmptyAKVPath(encryptionKeyId, isSystemOp: false); + + // Also validates key is of RSA type. + KeyCryptographer.AddKey(encryptionKeyId); + byte[] message = CompileKeyEncryptionKeyMetadata(encryptionKeyId, allowEnclaveComputations); + return KeyCryptographer.SignData(message, encryptionKeyId); + } + + /// + /// Uses an asymmetric key identified by the key path to verify the key encryption key metadata consisting of (keyEncryptionKeyPath, allowEnclaveComputations bit, providerName). + /// + /// Identifier of an asymmetric key in Azure Key Vault + /// Indicates whether the key encryption key supports enclave computations. + /// The signature of the key encryption key metadata. + /// Boolean indicating whether the key encryption key metadata can be verified based on the provided signature. + public override bool Verify(string encryptionKeyId, bool allowEnclaveComputations, byte[] signature) + { + ValidateNonEmptyAKVPath(encryptionKeyId, isSystemOp: true); + + var key = Tuple.Create(encryptionKeyId, allowEnclaveComputations, signature.ToHexString()); + return GetOrCreateSignatureVerificationResult(key, VerifyKeyEncryptionKeyMetadata); + + bool VerifyKeyEncryptionKeyMetadata() + { + // Also validates key is of RSA type. + KeyCryptographer.AddKey(encryptionKeyId); + byte[] message = CompileKeyEncryptionKeyMetadata(encryptionKeyId, allowEnclaveComputations); + return KeyCryptographer.VerifyData(message, signature, encryptionKeyId); + } + } + + /// + /// This function uses the asymmetric key specified by the key path + /// and decrypts an encrypted data dencryption key with RSA encryption algorithm. + /// + /// Identifier of an asymmetric key in Azure Key Vault + /// The encryption algorithm. + /// The ciphertext key. + /// Plain text data encryption key + public override byte[] UnwrapKey(string encryptionKeyId, KeyEncryptionKeyAlgorithm algorithm, byte[] encryptedKey) + { + // Validate the input parameters + ValidateNonEmptyAKVPath(encryptionKeyId, isSystemOp: true); + ValidateEncryptionAlgorithm(algorithm); + encryptedKey.ValidateNotNull(nameof(encryptedKey)); + encryptedKey.ValidateNotEmpty(nameof(encryptedKey)); + ValidateVersionByte(encryptedKey[0], firstVersion[0]); + + return GetOrCreateDataEncryptionKey(encryptedKey.ToHexString(), DecryptEncryptionKey); + + byte[] DecryptEncryptionKey() + { + // Also validates whether the key is RSA one or not and then get the key size + KeyCryptographer.AddKey(encryptionKeyId); + + int keySizeInBytes = KeyCryptographer.GetKeySize(encryptionKeyId); + + // Get key path length + int currentIndex = firstVersion.Length; + ushort keyPathLength = BitConverter.ToUInt16(encryptedKey, currentIndex); + currentIndex += sizeof(ushort); + + // Get ciphertext length + ushort cipherTextLength = BitConverter.ToUInt16(encryptedKey, currentIndex); + currentIndex += sizeof(ushort); + + // Skip KeyPath + // KeyPath exists only for troubleshooting purposes and doesnt need validation. + currentIndex += keyPathLength; + + // validate the ciphertext length + if (cipherTextLength != keySizeInBytes) + { + throw new MicrosoftDataEncryptionException(InvalidCiphertextLengthTemplate.FormatInvariant(cipherTextLength, keySizeInBytes, encryptionKeyId)); + } + + // Validate the signature length + int signatureLength = encryptedKey.Length - currentIndex - cipherTextLength; + if (signatureLength != keySizeInBytes) + { + throw new MicrosoftDataEncryptionException(InvalidSignatureLengthTemplate.FormatInvariant(signatureLength, keySizeInBytes, encryptionKeyId)); + } + + // Get ciphertext + byte[] cipherText = encryptedKey.Skip(currentIndex).Take(cipherTextLength).ToArray(); + currentIndex += cipherTextLength; + + // Get signature + byte[] signature = encryptedKey.Skip(currentIndex).Take(signatureLength).ToArray(); + + // Compute the message to validate the signature + byte[] message = encryptedKey.Take(encryptedKey.Length - signatureLength).ToArray(); + + if (null == message) + { + throw new MicrosoftDataEncryptionException(NullHash); + } + + if (!KeyCryptographer.VerifyData(message, signature, encryptionKeyId)) + { + throw new MicrosoftDataEncryptionException(InvalidSignatureTemplate.FormatInvariant(encryptionKeyId)); + } + + return KeyCryptographer.UnwrapKey(keyWrapAlgorithm, cipherText, encryptionKeyId); + } + } + + /// + /// This function uses the asymmetric key specified by the key path + /// and encrypts an unencrypted data encryption key with RSA encryption algorithm. + /// + /// Identifier of an asymmetric key in Azure Key Vault + /// The encryption algorithm. + /// The plaintext key. + /// Encrypted data encryption key + public override byte[] WrapKey(string encryptionKeyId, KeyEncryptionKeyAlgorithm algorithm, byte[] key) + { + // Validate the input parameters + ValidateNonEmptyAKVPath(encryptionKeyId, isSystemOp: true); + ValidateEncryptionAlgorithm(algorithm); + key.ValidateNotNull(nameof(key)); + ValidateDataEncryptionKeyNotEmpty(key); + + // Also validates whether the key is RSA one or not and then get the key size + KeyCryptographer.AddKey(encryptionKeyId); + int keySizeInBytes = KeyCryptographer.GetKeySize(encryptionKeyId); + + // Construct the encryptedDataEncryptionKey + // Format is + // firstVersion + keyPathLength + ciphertextLength + keyPath + ciphertext + signature + + // Get the Unicode encoded bytes of cultureinvariant lower case keyEncryptionKeyPath + byte[] keyEncryptionKeyPathBytes = Encoding.Unicode.GetBytes(encryptionKeyId.ToLowerInvariant()); + byte[] keyPathLength = BitConverter.GetBytes((short)keyEncryptionKeyPathBytes.Length); + + // Encrypt the plain text + byte[] cipherText = KeyCryptographer.WrapKey(keyWrapAlgorithm, key, encryptionKeyId); + byte[] cipherTextLength = BitConverter.GetBytes((short)cipherText.Length); + + if (cipherText.Length != keySizeInBytes) + { + throw new MicrosoftDataEncryptionException(CipherTextLengthMismatch); + } + + // Compute message + // SHA-2-256(version + keyPathLength + ciphertextLength + keyPath + ciphertext) + byte[] message = firstVersion.Concat(keyPathLength).Concat(cipherTextLength).Concat(keyEncryptionKeyPathBytes).Concat(cipherText).ToArray(); + + // Sign the message + byte[] signature = KeyCryptographer.SignData(message, encryptionKeyId); + + if (signature.Length != keySizeInBytes) + { + throw new MicrosoftDataEncryptionException(HashLengthMismatch); + } + + ValidateSignature(encryptionKeyId, message, signature); + + return message.Concat(signature).ToArray(); + } + + #endregion + + #region Private methods + + private void ValidateDataEncryptionKeyNotEmpty(byte[] encryptionKey) + { + if (encryptionKey.Length == 0) + { + throw new MicrosoftDataEncryptionException(EmptyDataEncryptionKey); + } + } + + /// + /// Checks if the Azure Key Vault key path is Empty or Null (and raises exception if they are). + /// + internal void ValidateNonEmptyAKVPath(string keyEncryptionKeyPath, bool isSystemOp) + { + // throw appropriate error if keyEncryptionKeyPath is null or empty + if (string.IsNullOrWhiteSpace(keyEncryptionKeyPath)) + { + string errorMessage = null == keyEncryptionKeyPath + ? NullAkvPath + : InvalidAkvPathTemplate.FormatInvariant(keyEncryptionKeyPath); + + if (isSystemOp) + { + throw new MicrosoftDataEncryptionException(errorMessage); + } + + throw new MicrosoftDataEncryptionException(errorMessage); + } + + + if (!Uri.TryCreate(keyEncryptionKeyPath, UriKind.Absolute, out Uri parsedUri) || parsedUri.Segments.Length < 3) + { + // Return an error indicating that the AKV url is invalid. + throw new MicrosoftDataEncryptionException(InvalidAkvUrlTemplate.FormatInvariant(keyEncryptionKeyPath)); + } + + // A valid URI. + // Check if it is pointing to trusted endpoint. + foreach (string trustedEndPoint in TrustedEndPoints) + { + if (parsedUri.Host.EndsWith(trustedEndPoint, StringComparison.OrdinalIgnoreCase)) + { + return; + } + } + + // Return an error indicating that the AKV url is invalid. + throw new MicrosoftDataEncryptionException(InvalidAkvKeyPathTrustedTemplate.FormatInvariant(keyEncryptionKeyPath, string.Join(", ", TrustedEndPoints.ToArray()))); + } + + private void ValidateSignature(string keyEncryptionKeyPath, byte[] message, byte[] signature) + { + if (!KeyCryptographer.VerifyData(message, signature, keyEncryptionKeyPath)) + { + throw new MicrosoftDataEncryptionException(InvalidSignature); + } + } + + private byte[] CompileKeyEncryptionKeyMetadata(string keyEncryptionKeyPath, bool allowEnclaveComputations) + { + string keyEncryptionKeyMetadata = ProviderName + keyEncryptionKeyPath + allowEnclaveComputations; + return Encoding.Unicode.GetBytes(keyEncryptionKeyMetadata.ToLowerInvariant()); + } + + + internal static void ValidateEncryptionAlgorithm(KeyEncryptionKeyAlgorithm encryptionAlgorithm) + { + if (encryptionAlgorithm != KeyEncryptionKeyAlgorithm.RSA_OAEP) + { + throw new MicrosoftDataEncryptionException(InvalidKeyAlgorithm.FormatInvariant(encryptionAlgorithm, KeyEncryptionKeyAlgorithm.RSA_OAEP.ToString())); + } + } + + internal static void ValidateVersionByte(byte encryptedByte, byte firstVersionByte) + { + // Validate and decrypt the EncryptedDataEncryptionKey + // Format is + // version + keyPathLength + ciphertextLength + keyPath + ciphertext + signature + // + // keyPath is present in the encrypted data encryption key for identifying the original source of the asymmetric key pair and + // we will not validate it against the data contained in the KEK metadata (keyEncryptionKeyPath). + + // Validate the version byte + if (encryptedByte != firstVersionByte) + { + throw new MicrosoftDataEncryptionException(InvalidAlgorithmVersionTemplate.FormatInvariant(encryptedByte.ToString(@"X2"), firstVersionByte.ToString("X2"))); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/Constants.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/Constants.cs new file mode 100644 index 0000000000..b8db49e2a2 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/Constants.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +namespace Microsoft.Data.Encryption.AzureKeyVaultProvider +{ + internal static class Constants + { + /// + /// Azure Key Vault Domain Name + /// + internal static readonly string[] AzureKeyVaultPublicDomainNames = new string[] { + @"vault.azure.net", // default + @"vault.azure.cn", // Azure China + @"vault.usgovcloudapi.net", // US Government + @"vault.microsoftazure.de", // Azure Germany + @"managedhsm.azure.net", // public HSM vault + @"managedhsm.azure.cn", // Azure China HSM vault + @"managedhsm.usgovcloudapi.net", // US Government HSM vault + @"managedhsm.microsoftazure.de" // Azure Germany HSM vault + }; + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/KeyCryptographer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/KeyCryptographer.cs new file mode 100644 index 0000000000..5e065861ee --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/AzureKeyVaultProvider/KeyCryptographer.cs @@ -0,0 +1,226 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using Azure; +using Azure.Core; +using Azure.Security.KeyVault.Keys; +using Azure.Security.KeyVault.Keys.Cryptography; +using System; +using System.Collections.Concurrent; +using System.Threading.Tasks; + +using static Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.AzureKeyVaultProvider +{ + internal class KeyCryptographer + { + /// + /// TokenCredential to be used with the KeyClient + /// + private TokenCredential TokenCredential { get; set; } + + /// + /// A mapping of the KeyClient objects to the corresponding Azure Key Vault URI + /// + private readonly ConcurrentDictionary _keyClientDictionary = new ConcurrentDictionary(); + + /// + /// Holds references to the fetch key tasks and maps them to their corresponding Azure Key Vault Key Identifier (URI). + /// These tasks will be used for returning the key in the event that the fetch task has not finished depositing the + /// key into the key dictionary. + /// + private readonly ConcurrentDictionary>> _keyFetchTaskDictionary = new ConcurrentDictionary>>(); + + /// + /// Holds references to the Azure Key Vault keys and maps them to their corresponding Azure Key Vault Key Identifier (URI). + /// + private readonly ConcurrentDictionary _keyDictionary = new ConcurrentDictionary(); + + /// + /// Holds references to the Azure Key Vault CryptographyClient objects and maps them to their corresponding Azure Key Vault Key Identifier (URI). + /// + private readonly ConcurrentDictionary _cryptoClientDictionary = new ConcurrentDictionary(); + + /// + /// Constructs a new KeyCryptographer + /// + /// + internal KeyCryptographer(TokenCredential tokenCredential) + { + TokenCredential = tokenCredential; + } + + /// + /// Adds the key, specified by the Key Identifier URI, to the cache. + /// + /// + internal void AddKey(string keyIdentifierUri) + { + if (TheKeyHasNotBeenCached(keyIdentifierUri)) + { + ParseAKVPath(keyIdentifierUri, out Uri vaultUri, out string keyName, out string keyVersion); + CreateKeyClient(vaultUri); + FetchKey(vaultUri, keyName, keyVersion, keyIdentifierUri); + } + + bool TheKeyHasNotBeenCached(string k) => !_keyDictionary.ContainsKey(k) && !_keyFetchTaskDictionary.ContainsKey(k); + } + + /// + /// Returns the key specified by the Key Identifier URI + /// + /// + /// + internal KeyVaultKey GetKey(string keyIdentifierUri) + { + if (_keyDictionary.ContainsKey(keyIdentifierUri)) + { + _keyDictionary.TryGetValue(keyIdentifierUri, out KeyVaultKey key); + return key; + } + + if (_keyFetchTaskDictionary.ContainsKey(keyIdentifierUri)) + { + _keyFetchTaskDictionary.TryGetValue(keyIdentifierUri, out Task> task); + return Task.Run(() => task).GetAwaiter().GetResult(); + } + + // Not a public exception - not likely to occur. + throw new MicrosoftDataEncryptionException(AzureKeyVaultKeyNotFound.Format(keyIdentifierUri)); + } + + /// + /// Gets the public Key size in bytes. + /// + /// The key vault key identifier URI + /// + internal int GetKeySize(string keyIdentifierUri) + { + return GetKey(keyIdentifierUri).Key.N.Length; + } + + /// + /// Generates signature based on RSA PKCS#v1.5 scheme using a specified Azure Key Vault Key URL. + /// + /// The data to sign + /// The key vault key identifier URI + /// + internal byte[] SignData(byte[] message, string keyIdentifierUri) + { + CryptographyClient cryptographyClient = GetCryptographyClient(keyIdentifierUri); + return cryptographyClient.SignData(RS256, message).Signature; + } + + internal bool VerifyData(byte[] message, byte[] signature, string keyIdentifierUri) + { + CryptographyClient cryptographyClient = GetCryptographyClient(keyIdentifierUri); + return cryptographyClient.VerifyData(RS256, message, signature).IsValid; + } + + internal byte[] UnwrapKey(KeyWrapAlgorithm keyWrapAlgorithm, byte[] encryptedKey, string keyIdentifierUri) + { + CryptographyClient cryptographyClient = GetCryptographyClient(keyIdentifierUri); + return cryptographyClient.UnwrapKey(keyWrapAlgorithm, encryptedKey).Key; + } + + internal byte[] WrapKey(KeyWrapAlgorithm keyWrapAlgorithm, byte[] key, string keyIdentifierUri) + { + CryptographyClient cryptographyClient = GetCryptographyClient(keyIdentifierUri); + return cryptographyClient.WrapKey(keyWrapAlgorithm, key).EncryptedKey; + } + + private CryptographyClient GetCryptographyClient(string keyIdentifierUri) + { + if (_cryptoClientDictionary.ContainsKey(keyIdentifierUri)) + { + _cryptoClientDictionary.TryGetValue(keyIdentifierUri, out CryptographyClient client); + return client; + } + + CryptographyClient cryptographyClient = new CryptographyClient(GetKey(keyIdentifierUri).Id, TokenCredential); + _cryptoClientDictionary.TryAdd(keyIdentifierUri, cryptographyClient); + + return cryptographyClient; + } + + /// + /// + /// + /// The Azure Key Vault URI + /// The name of the Azure Key Vault key + /// The version of the Azure Key Vault key + /// The Azure Key Vault key identifier + private void FetchKey(Uri vaultUri, string keyName, string keyVersion, string keyResourceUri) + { + Task> fetchKeyTask = FetchKeyFromKeyVault(vaultUri, keyName, keyVersion); + _keyFetchTaskDictionary.AddOrUpdate(keyResourceUri, fetchKeyTask, (k, v) => fetchKeyTask); + + fetchKeyTask + .ContinueWith(k => ValidateRsaKey(k.GetAwaiter().GetResult())) + .ContinueWith(k => _keyDictionary.AddOrUpdate(keyResourceUri, k.GetAwaiter().GetResult(), (key, v) => k.GetAwaiter().GetResult())); + + Task.Run(() => fetchKeyTask); + } + + /// + /// Looks up the KeyClient object by it's URI and then fetches the key by name. + /// + /// The Azure Key Vault URI + /// Then name of the key + /// Then version of the key + /// + private Task> FetchKeyFromKeyVault(Uri vaultUri, string keyName, string keyVersion) + { + _keyClientDictionary.TryGetValue(vaultUri, out KeyClient keyClient); + return keyClient.GetKeyAsync(keyName, keyVersion); + } + + /// + /// Validates that a key is of type RSA + /// + /// + /// + private KeyVaultKey ValidateRsaKey(KeyVaultKey key) + { + if (key.KeyType != KeyType.Rsa && key.KeyType != KeyType.RsaHsm) + { + throw new MicrosoftDataEncryptionException(NonRsaKeyTemplate.Format(key.KeyType)); + } + + return key; + } + + /// + /// Instantiates and adds a KeyClient to the KeyClient dictionary + /// + /// The Azure Key Vault URI + private void CreateKeyClient(Uri vaultUri) + { + if (!_keyClientDictionary.ContainsKey(vaultUri)) + { + _keyClientDictionary.TryAdd(vaultUri, new KeyClient(vaultUri, TokenCredential)); + } + } + + /// + /// Validates and parses the Azure Key Vault URI and key name. + /// + /// The Azure Key Vault key identifier + /// The Azure Key Vault URI + /// The name of the key + /// The version of the key + private void ParseAKVPath(string keyEncryptionKeyPath, out Uri vaultUri, out string keyEncryptionKeyName, out string keyEncryptionKeyVersion) + { + Uri masterKeyPathUri = new Uri(keyEncryptionKeyPath); + vaultUri = new Uri(masterKeyPathUri.GetLeftPart(UriPartial.Authority)); + keyEncryptionKeyName = masterKeyPathUri.Segments[2]; + keyEncryptionKeyVersion = masterKeyPathUri.Segments.Length > 3 ? masterKeyPathUri.Segments[3] : null; + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/AeadAes256CbcHmac256EncryptionAlgorithm.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/AeadAes256CbcHmac256EncryptionAlgorithm.cs new file mode 100644 index 0000000000..5fa45c8c10 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/AeadAes256CbcHmac256EncryptionAlgorithm.cs @@ -0,0 +1,227 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.IO; +using System.Linq; +using System.Security.Cryptography; + +using static Microsoft.Data.Encryption.Resources.Strings; +using static Microsoft.Data.Encryption.Cryptography.DataEncryptionKey; +using static Microsoft.Data.Encryption.Cryptography.EncryptionType; +using static System.Security.Cryptography.CipherMode; +using static System.Security.Cryptography.PaddingMode; + +namespace Microsoft.Data.Encryption.Cryptography +{ + /// + /// A component for information encryption purposes that uses block ciphers to encrypt and protect data. + /// This class implements authenticated encryption algorithm with associated data as described in + /// http://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05. More specifically this implements + /// AEAD_AES_256_CBC_HMAC_SHA256 algorithm. + /// + internal class AeadAes256CbcHmac256EncryptionAlgorithm : DataProtector + { + /// + /// Block size in bytes. AES uses 16 byte blocks. + /// + private const int BlockSizeInBytes = 16; + + /// + /// Variable indicating whether this algorithm should work in Deterministic mode or Randomized mode. + /// For deterministic encryption, we derive an IV from the plaintext data. + /// For randomized encryption, we generate a cryptographically random IV. + /// + private readonly bool isDeterministicEncryptionType; + + /// + /// The Data Encryption Key is an encryption key is used to encrypt data. + /// + private readonly DataEncryptionKey dataEncryptionKey; + + /// + /// Byte array with algorithm version used for authentication tag computation. + /// + private static readonly byte[] version = { 1 }; + + + private static readonly LocalCache, AeadAes256CbcHmac256EncryptionAlgorithm> algorithmCache + = new LocalCache, AeadAes256CbcHmac256EncryptionAlgorithm>(maxSizeLimit: 1000); + + /// + /// Returns a cached instance of the or, if not present, creates a new one. + /// + /// The encryption key that is used to encrypt data. + /// The type of encryption. + /// An object. + public static AeadAes256CbcHmac256EncryptionAlgorithm GetOrCreate(DataEncryptionKey dataEncryptionKey, EncryptionType encryptionType) + { + dataEncryptionKey.ValidateNotNull(nameof(dataEncryptionKey)); + + return algorithmCache.GetOrCreate( + key: Tuple.Create(dataEncryptionKey, encryptionType), + createItem: () => new AeadAes256CbcHmac256EncryptionAlgorithm(dataEncryptionKey, encryptionType) + ); + } + + /// + /// Initializes a new instance of the class. + /// + /// an encryption key is used to encrypt data + /// Determines whether this algorithm should work in Deterministic mode or Randomized mode. + public AeadAes256CbcHmac256EncryptionAlgorithm(DataEncryptionKey dataEncryptionKey, EncryptionType encryptionType) + { + ValidateEncryptionKeySize(dataEncryptionKey.RootKeyBytes.Length); + + this.dataEncryptionKey = dataEncryptionKey; + isDeterministicEncryptionType = encryptionType == Deterministic; + } + + /// + /// Convert information or data into a code, especially to prevent unauthorized access. + /// + /// The information or data to encrypt. + /// The ciphertext information. + public override byte[] Encrypt(byte[] plaintext) + { + if (plaintext.IsNull()) + { + return null; + } + + try + { + Aes aes = Aes.Create(); + aes.Key = dataEncryptionKey.EncryptionKeyBytes; + aes.Mode = CBC; + aes.Padding = PKCS7; + + if (isDeterministicEncryptionType) + { + aes.IV = GetHMACWithSHA256(plaintext, dataEncryptionKey.IvKeyBytes).Take(BlockSizeInBytes).ToArray(); + } + + ICryptoTransform encryptor = aes.CreateEncryptor(); + MemoryStream memoryStream = new MemoryStream(); + CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write); + cryptoStream.Write(plaintext, 0, plaintext.Length); + cryptoStream.FlushFinalBlock(); + byte[] ciphertext = memoryStream.ToArray(); + byte[] hmac = GenerateAuthenticationTag(aes.IV, ciphertext); + + return version.Concat(hmac).Concat(aes.IV).Concat(ciphertext).ToArray(); + } + catch (Exception ex) + { + string lastTenBytesOfKey = GetBytesAsString(dataEncryptionKey.EncryptionKeyBytes, startFromEnd: true, countOfBytes: 10); + throw new MicrosoftDataEncryptionException(EncryptionFailed.Format(lastTenBytesOfKey, ex.Message)); + } + } + + /// + /// Convert coded information or data into an intelligible message. + /// + /// The coded information or data. + /// An intelligible message. + public override byte[] Decrypt(byte[] ciphertext) + { + if (ciphertext.IsNull()) + { + return null; + } + + ValidateCiphertextLength(ciphertext, dataEncryptionKey.EncryptionKeyBytes); + ValidateAlgorithmVersion(ciphertext, dataEncryptionKey.EncryptionKeyBytes); + + const int authenticationTagIndex = 1; + const int authenticationTagLength = KeySizeInBytes; + const int initializationVectorIndex = authenticationTagIndex + authenticationTagLength; + const int initializationVectorLength = BlockSizeInBytes; + const int encryptedDataIndex = initializationVectorIndex + initializationVectorLength; + int encryptedDataLength = ciphertext.Length - encryptedDataIndex; + + byte[] authenticationTag = ciphertext.Skip(authenticationTagIndex).Take(authenticationTagLength).ToArray(); + byte[] initializationVector = ciphertext.Skip(initializationVectorIndex).Take(initializationVectorLength).ToArray(); + byte[] encryptedData = ciphertext.Skip(encryptedDataIndex).Take(encryptedDataLength).ToArray(); + + ValidateAuthenticationTag(authenticationTag, initializationVector, encryptedData, ciphertext, dataEncryptionKey.EncryptionKeyBytes); + + SymmetricAlgorithm symmetricAlgorithm = Aes.Create(); + symmetricAlgorithm.Key = dataEncryptionKey.EncryptionKeyBytes; + symmetricAlgorithm.Mode = CBC; + symmetricAlgorithm.Padding = PKCS7; + symmetricAlgorithm.IV = initializationVector; + + ICryptoTransform decryptor = symmetricAlgorithm.CreateDecryptor(); + MemoryStream memoryStream = new MemoryStream(); + CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Write); + cryptoStream.Write(encryptedData, 0, encryptedData.Length); + cryptoStream.FlushFinalBlock(); + + return memoryStream.ToArray(); + } + + private byte[] GenerateAuthenticationTag(byte[] iv, byte[] ciphertext) + { + HMACSHA256 hmacSha256 = new HMACSHA256(dataEncryptionKey.MacKeyBytes); + byte[] versionSize = { sizeof(byte) }; + byte[] buffer = version.Concat(iv).Concat(ciphertext).Concat(versionSize).ToArray(); + byte[] hmac = hmacSha256.ComputeHash(buffer); + return hmac; + } + + private static void ValidateCiphertextLength(byte[] ciphertext, byte[] encryptionKey) + { + const int minimumCipherTextLength = sizeof(byte) + BlockSizeInBytes + BlockSizeInBytes + KeySizeInBytes; + + if (ciphertext.Length < minimumCipherTextLength) + { + ThrowDecryptionFailedException(encryptionKey, ciphertext, InvalidCipherTextSize.Format(ciphertext.Length, minimumCipherTextLength)); + } + } + + private void ValidateAlgorithmVersion(byte[] ciphertext, byte[] encryptionKey) + { + if (ciphertext[0] != version[0]) + { + ThrowDecryptionFailedException(encryptionKey, ciphertext, InvalidAlgorithmVersion.Format(ciphertext[0].ToString("X2"), version[0].ToString("X2"))); + } + } + + private void ValidateAuthenticationTag(byte[] authenticationTag, byte[] initializationVector, byte[] encryptedData, byte[] ciphertext, byte[] encryptionKey) + { + byte[] computedAuthenticationTag = GenerateAuthenticationTag(initializationVector, encryptedData); + + if (!authenticationTag.SequenceEqual(computedAuthenticationTag)) + { + ThrowDecryptionFailedException(encryptionKey, ciphertext, InvalidAuthenticationTag); + } + } + + private void ValidateEncryptionKeySize(int size) + { + if (size != KeySizeInBytes) + { + throw new MicrosoftDataEncryptionException(InvalidDataEncryptionKeySize.Format(size)); + } + } + + private static MicrosoftDataEncryptionException ThrowDecryptionFailedException(byte[] encryptionKey, byte[] ciphertext, string reason) + { + string lastTenBytesOfKey = GetBytesAsString(encryptionKey, startFromEnd: true, countOfBytes: 10); + string firstTenBytesOfCiphertext = GetBytesAsString(ciphertext, startFromEnd: false, countOfBytes: 10); + throw new MicrosoftDataEncryptionException(DecryptionFailed.Format(lastTenBytesOfKey, firstTenBytesOfCiphertext, reason)); + } + + private static string GetBytesAsString(byte[] buffer, bool startFromEnd, int countOfBytes) + { + int count = (buffer.Length > countOfBytes) ? countOfBytes : buffer.Length; + int startIndex = startFromEnd ? buffer.Length - count : 0; + return BitConverter.ToString(buffer, startIndex, count); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/AssemblyInfo.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/AssemblyInfo.cs new file mode 100644 index 0000000000..f214dd1350 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/AssemblyInfo.cs @@ -0,0 +1,12 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Runtime.CompilerServices; + +[assembly: CLSCompliant(true)] +[assembly: InternalsVisibleTo("Microsoft.Data.Encryption.Cryptography.Tests,PublicKey=0024000004800000940000000602000000240000525341310004000001000100393106b45a63d86c5dab7f007ca58b8e485240dae7190662e9bfdca8f15fa6661f5be51918377614d9097f48484e524decee152376565fc3f428bbec06ea23f965d0da06749183fbbac42badcea054f2534f0d55be555980c9d73bab3da62fda265dd3a5bc20d75693fe036c9c5bdee9904456a4cb066cd9c0848087c3c160ba")] diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/DataEncryptionKey.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/DataEncryptionKey.cs new file mode 100644 index 0000000000..a5a12f9a5c --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/DataEncryptionKey.cs @@ -0,0 +1,111 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System.Security.Cryptography; + +using static System.Text.Encoding; + +namespace Microsoft.Data.Encryption.Cryptography +{ + /// + /// An encryption key that is used to encrypt and decrypt data. + /// + internal abstract class DataEncryptionKey + { + private byte[] rootKeyBytes; + private const int KeySizeInBits = 256; + internal const int KeySizeInBytes = KeySizeInBits / 8; + + /// + /// The name by which the will be known. + /// + public string Name { get; protected set; } + + /// + /// The hexadecimal string representation of the root key used for equality comparisons. + /// + protected string rootKeyHexString; + + /// + /// The root key used for encryption operations. + /// + internal byte[] RootKeyBytes + { + get => rootKeyBytes; + set + { + rootKeyBytes = value; + rootKeyHexString = value.ToHexString(); + } + } + + /// + /// The encryption key that is used for encryption and decryption. + /// + internal byte[] EncryptionKeyBytes { get; set; } + + /// + /// The MAC key that is used to compute and validate an HMAC. + /// + internal byte[] MacKeyBytes { get; set; } + + /// + /// The IV key that is used to compute a synthetic IV from a given plain text. + /// + internal byte[] IvKeyBytes { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The name by which the will be known. + /// Specifies the root key used for encrypting and decrypting. + protected DataEncryptionKey(string name, byte[] rootKey) + { + name.ValidateNotNullOrWhitespace(nameof(name)); + rootKey.ValidateSize(KeySizeInBytes, nameof(rootKey)); + + Name = name; + + string encryptionKeySalt = $"Microsoft SQL Server cell encryption key with encryption algorithm:AEAD_AES_256_CBC_HMAC_SHA256 and key length:{KeySizeInBits}"; + string macKeySalt = $"Microsoft SQL Server cell MAC key with encryption algorithm:AEAD_AES_256_CBC_HMAC_SHA256 and key length:{KeySizeInBits}"; + string ivKeySalt = $"Microsoft SQL Server cell IV key with encryption algorithm:AEAD_AES_256_CBC_HMAC_SHA256 and key length:{KeySizeInBits}"; + + byte[] encryptionKeyBytes = GetHMACWithSHA256(Unicode.GetBytes(encryptionKeySalt), rootKey); + byte[] macKeyBytes = GetHMACWithSHA256(Unicode.GetBytes(macKeySalt), rootKey); + byte[] ivKeyBytes = GetHMACWithSHA256(Unicode.GetBytes(ivKeySalt), rootKey); + + RootKeyBytes = rootKey; + EncryptionKeyBytes = encryptionKeyBytes; + MacKeyBytes = macKeyBytes; + IvKeyBytes = ivKeyBytes; + } + + /// + /// Computes a Hash-based Message Authentication Code (HMAC) by using the SHA256 hash function. + /// + /// Plain text bytes whose hash has to be computed. + /// key used for the HMAC + /// HMAC value + internal static byte[] GetHMACWithSHA256(byte[] plaintext, byte[] key) + { + using (HMACSHA256 hmacSha256 = new HMACSHA256(key)) + { + return hmacSha256.ComputeHash(plaintext); + } + } + + /// + /// Determines if the current 's root key is equal to the specified 's root key + /// + /// The to compare with the current + /// + public bool RootKeyEquals(DataEncryptionKey otherKey) + { + return rootKeyHexString.Equals(otherKey.rootKeyHexString); + } + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/DataProtector.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/DataProtector.cs new file mode 100644 index 0000000000..0ec979866c --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/DataProtector.cs @@ -0,0 +1,29 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +namespace Microsoft.Data.Encryption.Cryptography +{ + /// + /// The base class used for algorithms that do encryption and decryption of serialized object data. + /// + internal abstract class DataProtector + { + /// + /// Convert information or data into a code, especially to prevent unauthorized access. + /// + /// The information or data to encrypt. + /// The ciphertext information. + public abstract byte[] Encrypt(byte[] plaintext); + + /// + /// Convert coded information or data into an intelligible message. + /// + /// The coded information or data. + /// An intelligible message. + public abstract byte[] Decrypt(byte[] ciphertext); + } +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/EncryptionKeyStoreProvider.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/EncryptionKeyStoreProvider.cs new file mode 100644 index 0000000000..e282752bcd --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/EncryptionKeyStoreProvider.cs @@ -0,0 +1,114 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography +{ + /// + /// Base class for all key store providers. A custom provider must derive from this + /// class and override its member functions. + /// + internal abstract class EncryptionKeyStoreProvider + { + /// + /// A cache of key encryption keys (once they are unwrapped). This is useful for rapidly decrypting multiple data values. + /// + private readonly LocalCache dataEncryptionKeyCache = new LocalCache() { TimeToLive = TimeSpan.FromHours(2) }; + + /// + /// A cache for storing the results of signature verification of key encryption key metadata. + /// + private readonly LocalCache, bool> keyEncryptionKeyMetadataSignatureVerificationCache = + new LocalCache, bool>(maxSizeLimit: 2000) { TimeToLive = TimeSpan.FromDays(10) }; + + /// + /// Gets or sets the lifespan of the decrypted data encryption key in the cache. + /// Once the timespan has elapsed, the decrypted data encryption key is discarded + /// and must be revalidated. + /// + /// + /// Internally, there is a cache of key encryption keys (once they are unwrapped). + /// This is useful for rapidly decrypting multiple data values. The default value is 2 hours. + /// Setting the to zero disables caching. + /// + public TimeSpan? DataEncryptionKeyCacheTimeToLive + { + get => dataEncryptionKeyCache.TimeToLive; + set => dataEncryptionKeyCache.TimeToLive = value; + } + + /// + /// The unique name that identifies a particular implementation of the abstract . + /// + public abstract string ProviderName { get; } + + /// + /// Unwraps the specified of a data encryption key. The encrypted value is expected to be encrypted using + /// the key encryption key with the specified and using the specified . + /// + /// The key Id tells the provider where to find the key. + /// The encryption algorithm. + /// The ciphertext key. + /// The unwrapped data encryption key. + public abstract byte[] UnwrapKey(string encryptionKeyId, KeyEncryptionKeyAlgorithm algorithm, byte[] encryptedKey); + + /// + /// Wraps a data encryption key using the key encryption key with the specified and using the specified . + /// + /// The key Id tells the provider where to find the key. + /// The encryption algorithm. + /// The plaintext key. + /// The wrapped data encryption key. + public abstract byte[] WrapKey(string encryptionKeyId, KeyEncryptionKeyAlgorithm algorithm, byte[] key); + + /// + /// When implemented in a derived class, digitally signs the key encryption key metadata with the key encryption key referenced + /// by the parameter. The input values used to generate the signature should be the specified values of + /// the and parameters. + /// + /// The key Id tells the provider where to find the key. + /// Indicates whether the key encryption key supports enclave computations. + /// The signature of the key encryption key metadata. + public abstract byte[] Sign(string encryptionKeyId, bool allowEnclaveComputations); + + /// + /// When implemented in a derived class, this method is expected to verify the specified is valid + /// for the key encryption key with the specified and the specified enclave behavior. + /// + /// The key Id tells the provider where to find the key. + /// Indicates whether the key encryption key supports enclave computations. + /// The signature of the key encryption key metadata. + /// + public abstract bool Verify(string encryptionKeyId, bool allowEnclaveComputations, byte[] signature); + + /// + /// Returns the cached decrypted data encryption key, or unwraps the encrypted data encryption if not present. + /// + /// Encrypted Data Encryption Key + /// The delegate function that will decrypt the encrypted column encryption key. + /// The decrypted data encryption key. + /// + /// + /// + protected virtual byte[] GetOrCreateDataEncryptionKey(string encryptedDataEncryptionKey, Func createItem) + { + return dataEncryptionKeyCache.GetOrCreate(encryptedDataEncryptionKey, createItem); + } + + /// + /// Returns the cached signature verification result, or proceeds to verify if not present. + /// + /// The encryptionKeyId, allowEnclaveComputations and hexadecimal signature. + /// The delegate function that will perform the verification. + /// + protected virtual bool GetOrCreateSignatureVerificationResult(Tuple keyInformation, Func createItem) + { + return keyEncryptionKeyMetadataSignatureVerificationCache.GetOrCreate(keyInformation, createItem); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/EncryptionSettings.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/EncryptionSettings.cs new file mode 100644 index 0000000000..f20b943ea3 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/EncryptionSettings.cs @@ -0,0 +1,124 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using Microsoft.Data.Encryption.Cryptography.Serializers; +using System; +using static Microsoft.Data.Encryption.Cryptography.EncryptionType; + +namespace Microsoft.Data.Encryption.Cryptography +{ + /// + /// Contains the settings to configure how encryption operations are performed on the data of + /// arbitrary type . + /// + /// The data type on which these encryption settings will apply. + internal class EncryptionSettings : EncryptionSettings + { + /// + /// Used for serializing and deserializing data objects to and from an array of bytes. + /// + public Serializer Serializer { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// An encryption key is used to encrypt and decrypt data. + /// A serializer is used for serializing and deserializing data objects to and from an array of bytes. + public EncryptionSettings(DataEncryptionKey dataEncryptionKey, Serializer serializer) + : this(dataEncryptionKey, GetDefaultEncryptionType(dataEncryptionKey), serializer) { } + + /// + /// Initializes a new instance of the class. + /// + /// An encryption key is used to encrypt and decrypt data. + /// The type of encryption. + /// A serializer is used for serializing and deserializing data objects to and from an array of bytes. + public EncryptionSettings(DataEncryptionKey dataEncryptionKey, EncryptionType encryptionType, Serializer serializer) + { + DataEncryptionKey = dataEncryptionKey; + EncryptionType = encryptionType; + Serializer = serializer; + } + + /// + /// Gets the + /// + /// returns the + public override ISerializer GetSerializer() + { + return Serializer; + } + } + + /// + /// Contains the settings to configure how encryption operations are performed on data. + /// + internal abstract class EncryptionSettings : IEquatable + { + private DataEncryptionKey encryptionKey; + + /// + /// Gets the + /// + public virtual DataEncryptionKey DataEncryptionKey + { + get => encryptionKey; + + protected set + { + encryptionKey = value; + if (DataEncryptionKey.IsNull()) + { + EncryptionType = Plaintext; + } + } + } + + /// + /// Gets the + /// + public EncryptionType EncryptionType { get; protected set; } = Randomized; + + /// + /// Gets the + /// + /// returns the + public abstract ISerializer GetSerializer(); + + /// + /// Gets the default + /// + /// An encryption key is used to encrypt and decrypt data. + /// Plaintext if the is null, otherwise. + protected static EncryptionType GetDefaultEncryptionType(DataEncryptionKey dataEncryptionKey) + { + return dataEncryptionKey.IsNull() ? Plaintext : Randomized; + } + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// true if the current object is equal to the other parameter; otherwise, false. + public bool Equals(EncryptionSettings other) + { + if (other == null) + { + return false; + } + + if (DataEncryptionKey.IsNull() && !other.DataEncryptionKey.IsNull()) + { + return false; + } + + return DataEncryptionKey.Equals(other.DataEncryptionKey) + && EncryptionType.Equals(other.EncryptionType) + && GetSerializer().GetType().Equals(other.GetSerializer().GetType()); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Enums/DataEncryptionKeyAlgorithm.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Enums/DataEncryptionKeyAlgorithm.cs new file mode 100644 index 0000000000..071383d2de --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Enums/DataEncryptionKeyAlgorithm.cs @@ -0,0 +1,21 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +namespace Microsoft.Data.Encryption.Cryptography +{ + /// + /// Represents the encryption algorithms supported for data encryption. + /// + internal enum DataEncryptionKeyAlgorithm + { + /// + /// Represents the authenticated encryption algorithm with associated data as described in + /// http://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-05. + /// + AEAD_AES_256_CBC_HMAC_SHA256 + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Enums/EncryptionType.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Enums/EncryptionType.cs new file mode 100644 index 0000000000..0b3caaf962 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Enums/EncryptionType.cs @@ -0,0 +1,35 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +namespace Microsoft.Data.Encryption.Cryptography +{ + /// + /// The type of data encryption. + /// + /// + /// The three encryption types are Plaintext Deterministic and Randomized. Plaintext unencrypted data. + /// Deterministic encryption always generates the same encrypted value for any given plain text value. + /// Randomized encryption uses a method that encrypts data in a less predictable manner. Randomized encryption is more secure. + /// + internal enum EncryptionType + { + /// + /// Plaintext unencrypted data. + /// + Plaintext, + + /// + /// Deterministic encryption always generates the same encrypted value for any given plain text value. + /// + Deterministic, + + /// + /// Randomized encryption uses a method that encrypts data in a less predictable manner. Randomized encryption is more secure. + /// + Randomized + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Enums/KeyEncryptionKeyAlgorithm.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Enums/KeyEncryptionKeyAlgorithm.cs new file mode 100644 index 0000000000..a25cfb2141 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Enums/KeyEncryptionKeyAlgorithm.cs @@ -0,0 +1,20 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +namespace Microsoft.Data.Encryption.Cryptography +{ + /// + /// Represents the encryption algorithms supported for key encryption. + /// + internal enum KeyEncryptionKeyAlgorithm + { + /// + /// RSA public key cryptography algorithm with Optimal Asymmetric Encryption Padding (OAEP) padding. + /// + RSA_OAEP + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Extensions/CryptographyExtensions.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Extensions/CryptographyExtensions.cs new file mode 100644 index 0000000000..fc7a41cf64 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Extensions/CryptographyExtensions.cs @@ -0,0 +1,351 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using Microsoft.Data.Encryption.Cryptography.Serializers; +using System; +using System.Collections.Generic; + +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography +{ + /// + /// Extension methods for data protection. + /// + internal static class CryptographyExtensions + { + private const string hexPrefix = "0x"; + + /// + /// Encrypts the provided value using the provided . + /// + /// The value . + /// The plaintext value to encrypt. + /// The key used to encrypt the value. + /// The encrypted value. + /// + /// This method encrypts using encryption and the + /// default serializer registered under type with the + /// + /// is null. + public static byte[] Encrypt(this T plaintext, DataEncryptionKey encryptionKey) + { + encryptionKey.ValidateNotNull(nameof(encryptionKey)); + + DataProtector encryptionAlgorithm = AeadAes256CbcHmac256EncryptionAlgorithm.GetOrCreate(encryptionKey, EncryptionType.Randomized); + Serializer serializer = StandardSerializerFactory.Default.GetDefaultSerializer(); + byte[] serializedData = serializer.Serialize(plaintext); + return encryptionAlgorithm.Encrypt(serializedData); + } + + /// + /// Decrypts the provided value using the provided . + /// + /// The plaintext value . + /// The encrypted value. + /// The key used to decrypt the value. + /// The decrypted value. + /// + /// This method decrypts data that was encrypted using encryption and the + /// default serializer registered under type with the + /// + /// is null. + public static T Decrypt(this byte[] ciphertext, DataEncryptionKey encryptionKey) + { + encryptionKey.ValidateNotNull(nameof(encryptionKey)); + + DataProtector encryptionAlgorithm = AeadAes256CbcHmac256EncryptionAlgorithm.GetOrCreate(encryptionKey, EncryptionType.Randomized); + Serializer serializer = StandardSerializerFactory.Default.GetDefaultSerializer(); + byte[] plaintextData = encryptionAlgorithm.Decrypt(ciphertext); + return serializer.Deserialize(plaintextData); + } + + /// + /// Encrypts the provided value using the provided . + /// + /// The value . + /// The plaintext value to encrypt. + /// The settings used to configure how the should be encrypted. + /// The encrypted value. + /// + /// is null. + /// -or- + /// is set to . + /// + public static byte[] Encrypt(this T plaintext, EncryptionSettings encryptionSettings) + { + encryptionSettings.ValidateNotNull(nameof(encryptionSettings)); + encryptionSettings.ValidateNotPlaintext(nameof(encryptionSettings)); + + DataProtector encryptionAlgorithm = AeadAes256CbcHmac256EncryptionAlgorithm.GetOrCreate(encryptionSettings.DataEncryptionKey, encryptionSettings.EncryptionType); + ISerializer serializer = encryptionSettings.GetSerializer(); + byte[] serializedData = serializer.Serialize(plaintext); + return encryptionAlgorithm.Encrypt(serializedData); + } + + /// + /// Decrypts the provided value using the provided . + /// + /// The plaintext value . + /// The encrypted value. + /// The settings used to configure how the should be decrypted. + /// The decrypted value. + /// + /// is null. + /// -or- + /// is set to . + /// + public static T Decrypt(this byte[] ciphertext, EncryptionSettings encryptionSettings) + { + encryptionSettings.ValidateNotNull(nameof(encryptionSettings)); + encryptionSettings.ValidateNotPlaintext(nameof(encryptionSettings)); + + DataProtector encryptionAlgorithm = AeadAes256CbcHmac256EncryptionAlgorithm.GetOrCreate(encryptionSettings.DataEncryptionKey, EncryptionType.Plaintext); + Serializer serializer = encryptionSettings.Serializer; + byte[] plaintextData = encryptionAlgorithm.Decrypt(ciphertext); + return serializer.Deserialize(plaintextData); + } + + /// + /// Encrypts each plaintext value of a sequence using the provided . + /// + /// The type of the plaintext elements of . + /// A sequence of values to encrypt. + /// The key used to encrypt the plaintext values of . + /// An of whose elements are the result of encrypting each element of . + /// + /// This method encrypts using encryption and the + /// default serializer registered under type with the + /// + /// is null. + public static IEnumerable Encrypt(this IEnumerable source, DataEncryptionKey encryptionKey) + { + encryptionKey.ValidateNotNull(nameof(encryptionKey)); + + DataProtector encryptionAlgorithm = AeadAes256CbcHmac256EncryptionAlgorithm.GetOrCreate(encryptionKey, EncryptionType.Randomized); + Serializer serializer = StandardSerializerFactory.Default.GetDefaultSerializer(); + + foreach (T item in source) + { + byte[] serializedData = serializer.Serialize(item); + yield return encryptionAlgorithm.Encrypt(serializedData); + } + } + + /// + /// Decrypts each ciphertext value of a sequence using the provided . + /// + /// The type of the plaintext elements of + /// A sequence of encrypted values to decrypt. + /// The key used to decrypt the ciphertext values of . + /// An whose elements are the result of decrypting each element of . + /// + /// This method decrypts data that was encrypted using encryption and the + /// default serializer registered under type with the + /// + /// is null. + public static IEnumerable Decrypt(this IEnumerable source, DataEncryptionKey encryptionKey) + { + encryptionKey.ValidateNotNull(nameof(encryptionKey)); + + DataProtector encryptionAlgorithm = AeadAes256CbcHmac256EncryptionAlgorithm.GetOrCreate(encryptionKey, EncryptionType.Randomized); + Serializer serializer = StandardSerializerFactory.Default.GetDefaultSerializer(); + StandardSerializerFactory myFactory = new StandardSerializerFactory(); + myFactory.RegisterSerializer(typeof(string), new SqlNCharSerializer()); + + foreach (byte[] item in source) + { + byte[] plaintextData = encryptionAlgorithm.Decrypt(item); + yield return serializer.Deserialize(plaintextData); + } + } + + /// + /// Encrypts each plaintext value of a sequence using the provided . + /// + /// The type of the plaintext elements of . + /// A sequence of values to encrypt. + /// The settings used to configure how the values of should be encrypted. + /// An of whose elements are the result of encrypting each element of . + /// + /// is null. + /// -or- + /// is set to . + /// + public static IEnumerable Encrypt(this IEnumerable source, EncryptionSettings encryptionSettings) + { + encryptionSettings.ValidateNotNull(nameof(encryptionSettings)); + encryptionSettings.ValidateNotPlaintext(nameof(encryptionSettings)); + + DataProtector encryptionAlgorithm = AeadAes256CbcHmac256EncryptionAlgorithm.GetOrCreate(encryptionSettings.DataEncryptionKey, encryptionSettings.EncryptionType); + Serializer serializer = encryptionSettings.Serializer; + + foreach (T item in source) + { + byte[] serializedData = serializer.Serialize(item); + yield return encryptionAlgorithm.Encrypt(serializedData); + } + } + + /// + /// Decrypts each ciphertext value of a sequence using the provided . + /// + /// The type of the plaintext elements of . + /// A sequence of values to decrypt. + /// The settings used to configure how the values of should be decrypted. + /// An whose elements are the result of decrypting each element of . + /// + /// is null. + /// -or- + /// is set to . + /// + public static IEnumerable Decrypt(this IEnumerable source, EncryptionSettings encryptionSettings) + { + encryptionSettings.ValidateNotNull(nameof(encryptionSettings)); + encryptionSettings.ValidateNotPlaintext(nameof(encryptionSettings)); + + DataProtector encryptionAlgorithm = AeadAes256CbcHmac256EncryptionAlgorithm.GetOrCreate(encryptionSettings.DataEncryptionKey, EncryptionType.Plaintext); + Serializer serializer = encryptionSettings.Serializer; + + foreach (byte[] item in source) + { + byte[] plaintextData = encryptionAlgorithm.Decrypt(item); + yield return serializer.Deserialize(plaintextData); + } + } + + /// + /// Converts an array bytes to its equivalent string representation that is encoded with base-64 digits. + /// + /// An array of bytes. + /// The string representation, in base 64, of the contents of . + public static string ToBase64String(this byte[] source) => source.IsNull() ? null : Convert.ToBase64String(source); + + /// + /// Converts the specified string, which encodes binary data as base-64 digits, to an equivalent byte array. + /// + /// The string to convert. + /// An array of bytes that is equivalent to . + /// + /// The length of , ignoring white-space characters, is not zero or a multiple of + /// 4. -or- The format of is invalid. contains a non-base-64 character, more + /// than two padding characters, or a non-white space-character among the padding characters. + /// + public static byte[] FromBase64String(this string source) => source.IsNull() ? null : Convert.FromBase64String(source); + + /// + /// Converts each byte array in the sequence to its equivalent string representation that is encoded with base-64 digits. + /// + /// A sequence of byte arrays to convert. + /// An of whose elements are the result of being encoded with base-64 digits. + public static IEnumerable ToBase64String(this IEnumerable source) + { + foreach (byte[] item in source) + { + yield return ToBase64String(item); + } + } + + /// + /// Converts each element of , which encodes binary data as base-64 digits, to an equivalent byte array. + /// + /// A sequence of strings to convert. + /// An of byte arrays that is equivalent to . + /// + /// The length of a element, ignoring white-space characters, is not zero or a multiple of + /// 4. -or- The format of a element is invalid. element contains a non-base-64 character, more + /// than two padding characters, or a non-white space-character among the padding characters. + /// + public static IEnumerable FromBase64String(this IEnumerable source) + { + foreach (string item in source) + { + yield return FromBase64String(item); + } + } + + /// + /// Converts the numeric value of each element of a specified array of bytes to its equivalent hexadecimal string representation. + /// + /// An array of bytes to convert. + /// A string of hexadecimal characters + /// + /// Produces a string of hexadecimal character pairs preceded with "0x", where each pair represents the corresponding element in value; for example, "0x7F2C4A00". + /// + public static string ToHexString(this byte[] source) + { + if (source.IsNull()) + { + return null; + } + + return hexPrefix + BitConverter.ToString(source).Replace("-", ""); + } + + /// + /// Converts the string representation of a number in hexadecimal to an equivalent array of bytes. + /// + /// The string to convert. + /// An array of bytes that is equivalent to . + /// + /// The length of is not a multiple of 2, the + /// contains a hexadecimal character, or does not begin with the literal prefix '0x'. + /// + public static byte[] FromHexString(this string source) + { + return source.IsNull() ? null : ConvertToBytes(source); + + byte[] ConvertToBytes(string hex) + { + if (hex.Length < 2 || hex.Substring(0, 2) != hexPrefix || hex.Length % 2 != 0) + { + throw new MicrosoftDataEncryptionException(InvalidHexString); + } + + hex = hex.Substring(2); + + byte[] bytes = new byte[hex.Length / 2]; + + for (int i = 0; i < hex.Length; i += 2) + { + bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16); + } + + return bytes; + } + } + + /// + /// Converts each byte array in the sequence to its equivalent string representation that is encoded with hexadecimal digits. + /// + /// A sequence of byte arrays to convert. + /// An of whose elements are the result of being encoded with hexadecimal digits. + public static IEnumerable ToHexString(this IEnumerable source) + { + foreach (byte[] item in source) + { + yield return ToHexString(item); + } + } + + /// + /// Converts each element of , which encodes binary data as hexadecimal digits, to an equivalent byte array. + /// + /// A sequence of strings to convert. + /// An of byte arrays that is equivalent to . + /// + /// The length of element is not a multiple of 2, contains a hexadecimal character, or does not begin with the literal prefix '0x'. + /// + public static IEnumerable FromHexString(this IEnumerable source) + { + foreach (string item in source) + { + yield return FromHexString(item); + } + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/KeyEncryptionKey.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/KeyEncryptionKey.cs new file mode 100644 index 0000000000..86e5a9de20 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/KeyEncryptionKey.cs @@ -0,0 +1,140 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography +{ + /// + /// A represents a key-protecting key stored in an external key store. + /// The key protects (encrypts) one or more . + /// + internal class KeyEncryptionKey + { + private const KeyEncryptionKeyAlgorithm encryptionAlgorithm = KeyEncryptionKeyAlgorithm.RSA_OAEP; + private readonly Lazy signature; + + /// + /// A local cache to hold previously created objects for reuse. + /// + private static readonly LocalCache, KeyEncryptionKey> keyEncryptionKeyCache = + new LocalCache, KeyEncryptionKey>(); + + /// + /// The name of the . + /// + public string Name { get; private set; } + + /// + /// Specifies the key store provider. A key store provider is an object that has access to the . + /// + public EncryptionKeyStoreProvider KeyStoreProvider { get; private set; } + + /// + /// The path of the key in the store. The format of the key path is specific + /// to the implementation. + /// + public string Path { get; private set; } + + /// + /// Specifies that the is enclave-enabled. You can share all encryption keys, + /// encrypted with the , with a secure enclave and use them for computations inside the enclave. + /// + public bool IsEnclaveSupported { get; private set; } + + /// + /// A binary result of digitally signing key path and the + /// setting with the master key. The signature reflects whether + /// is specified or not. The signature protects the signed values from being altered by unauthorized users. + /// + public byte[] Signature { get => signature.Value; } + + /// + /// Returns a cached instance of the or, if not present, creates a new one. + /// + /// The name of the . + /// The path of the key in the store. + /// A component that has access to the . + /// Specifies whether the is enclave-enabled. + /// A object. + public static KeyEncryptionKey GetOrCreate(string name, string path, EncryptionKeyStoreProvider keyStoreProvider, bool isEnclaveSupported = false) + { + name.ValidateNotNullOrWhitespace(nameof(name)); + path.ValidateNotNullOrWhitespace(nameof(name)); + keyStoreProvider.ValidateNotNull(nameof(keyStoreProvider)); + + return keyEncryptionKeyCache.GetOrCreate( + key: Tuple.Create(name, path, keyStoreProvider, isEnclaveSupported), + createItem: () => new KeyEncryptionKey(name, path, keyStoreProvider, isEnclaveSupported) + ); + } + + /// + /// Constructs a new master key. + /// + /// The name of the . + /// The path of the key in the store. + /// A component that has access to the . + /// Specifies whether the is enclave-enabled. + public KeyEncryptionKey(string name, string path, EncryptionKeyStoreProvider keyStoreProvider, bool isEnclaveSupported = false) + { + name.ValidateNotNullOrWhitespace(nameof(name)); + path.ValidateNotNullOrWhitespace(nameof(name)); + keyStoreProvider.ValidateNotNull(nameof(keyStoreProvider)); + + Name = name; + Path = path; + KeyStoreProvider = keyStoreProvider; + IsEnclaveSupported = isEnclaveSupported; + signature = new Lazy(() => KeyStoreProvider.Sign(encryptionKeyId: path, allowEnclaveComputations: isEnclaveSupported)); + } + + /// + /// Encrypts an EncryptionKey + /// + /// The to encrypt. + /// The encrypted + public byte[] EncryptEncryptionKey(byte[] plaintextEncryptionKey) + { + return KeyStoreProvider.WrapKey(Path, encryptionAlgorithm, plaintextEncryptionKey); + } + + /// + /// Decrypts an + /// + /// The to decrypt. + /// The decrypted + public byte[] DecryptEncryptionKey(byte[] encryptedEncryptionKey) + { + return KeyStoreProvider.UnwrapKey(Path, encryptionAlgorithm, encryptedEncryptionKey); + } + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// true if the current object is equal to the other parameter; otherwise, false. + public override bool Equals(object obj) + { + if (!(obj is KeyEncryptionKey other)) + { + return false; + } + + return Name.Equals(other.Name) + && KeyStoreProvider.Equals(other.KeyStoreProvider) + && Path.Equals(other.Path) + && IsEnclaveSupported.Equals(other.IsEnclaveSupported); + } + + /// + /// Generates a hash code for the current object. + /// + /// A hash code for the current object. + public override int GetHashCode() => Tuple.Create(Name, KeyStoreProvider, Path, IsEnclaveSupported).GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/LocalCache.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/LocalCache.cs new file mode 100644 index 0000000000..567ef53b15 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/LocalCache.cs @@ -0,0 +1,95 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using Microsoft.Extensions.Caching.Memory; +using System; + +using static System.Math; + +namespace Microsoft.Data.Encryption.Cryptography +{ + /// + /// LocalCache is to reuse heavy objects. + /// When performing a heavy creation operation, we will save the result in our cache container. + /// The next time that we need that result, we will pull it from the cache container, instead of performing the heavy operation again. + /// + internal class LocalCache + { + /// + /// A simple thread-safe implementation of an in-memory Cache. + /// When the process dies, the cache dies with it. + /// + private readonly MemoryCache cache; + + private readonly int maxSize; + + /// + /// Sets an absolute expiration time, relative to now. + /// + internal TimeSpan? TimeToLive { get; set; } + + /// + /// Gets the count of the current entries for diagnostic purposes. + /// + internal int Count => cache.Count; + + /// + /// Constructs a new LocalCache object. + /// + internal LocalCache(int maxSizeLimit = int.MaxValue) + { + maxSizeLimit.ValidatePositive(nameof(maxSize)); + + maxSize = maxSizeLimit; + cache = new MemoryCache(new MemoryCacheOptions()); + } + + /// + /// Looks for the cache entry that maps to the value. If it exists (cache hit) it will simply be + /// returned. Otherwise, the delegate function will be invoked to create the value. + /// It will then get stored it in the cache and set the time-to-live before getting returned. + /// + /// The encrypted key bytes. + /// The delegate function that will decrypt the encrypted column encryption key. + /// The decrypted column encryption key. + internal TValue GetOrCreate(TKey key, Func createItem) + { + if (TimeToLive <= TimeSpan.Zero) + { + return createItem(); + } + + if (!cache.TryGetValue(key, out TValue cacheEntry)) + { + if (cache.Count == maxSize) + { + cache.Compact(Max(0.10, 1.0 / maxSize)); + } + + cacheEntry = createItem(); + var cacheEntryOptions = new MemoryCacheEntryOptions + { + AbsoluteExpirationRelativeToNow = TimeToLive + }; + + cache.Set(key, cacheEntry, cacheEntryOptions); + } + + return cacheEntry; + } + + /// + /// Determines whether the LocalCache contains the specified key. + /// + /// + /// + internal bool Contains(TKey key) + { + return cache.TryGetValue(key, out _); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/MicrosoftDataEncryptionException.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/MicrosoftDataEncryptionException.cs new file mode 100644 index 0000000000..88dac74a10 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/MicrosoftDataEncryptionException.cs @@ -0,0 +1,23 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption +{ + /// + /// Represents an exception thrown by the Data Encryption SDK. + /// + internal sealed class MicrosoftDataEncryptionException : Exception + { + /// + /// Initializes a new instance of MicrosoftDataEncryptionException with the specified error message. + /// + /// The message describing the error. + public MicrosoftDataEncryptionException(string message) : base(message) { } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/PlaintextDataEncryptionKey.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/PlaintextDataEncryptionKey.cs new file mode 100644 index 0000000000..b91b2d69ab --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/PlaintextDataEncryptionKey.cs @@ -0,0 +1,108 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Security.Cryptography; + +namespace Microsoft.Data.Encryption.Cryptography +{ + /// + /// An encryption key that is used to encrypt and decrypt data. + /// + internal class PlaintextDataEncryptionKey : DataEncryptionKey + { + /// + /// A local cache to hold previously created objects for reuse. + /// + /// + /// The retension period is defined by the property. + /// + private static readonly LocalCache, PlaintextDataEncryptionKey> encryptionKeyCache + = new LocalCache, PlaintextDataEncryptionKey>() { TimeToLive = TimeSpan.FromHours(2) }; + + /// + /// Sets an absolute expiration time, relative to now. + /// + /// + /// The default is 2 hours. Setting the will apply to every new + /// object instantiated by the method after this + /// change and before the next change. + /// If the object returned by the method already + /// existed in the cache before this change (cache hit) then the returned + /// object will have the value attributed to it that existed at the time + /// it was originally created. + /// + public static TimeSpan TimeToLive + { + get => encryptionKeyCache.TimeToLive.Value; + set => encryptionKeyCache.TimeToLive = value; + } + + /// + /// Returns a cached instance of the or, if not present, creates a new one + /// + /// The name by which the will be known. + /// The unencrypted value. + /// An object. + public static PlaintextDataEncryptionKey GetOrCreate(string name, byte[] unencryptedKey) + { + name.ValidateNotNullOrWhitespace(nameof(name)); + unencryptedKey.ValidateNotNullOrEmpty(nameof(unencryptedKey)); + + return encryptionKeyCache.GetOrCreate( + key: Tuple.Create(name, unencryptedKey.ToHexString()), + createItem: () => new PlaintextDataEncryptionKey(name, unencryptedKey) + ); + } + + /// + /// Initializes a new instance of the class. + /// + /// The name by which the will be known. + /// + /// Generates a new 256 bit cryptographically strong random key. + /// + public PlaintextDataEncryptionKey(string name) : base(name, GenerateNewColumnEncryptionKey()) { } + + /// + /// Initializes a new instance of the class. + /// + /// The name by which the will be known. + /// The unencrypted value. + public PlaintextDataEncryptionKey(string name, byte[] unencryptedKey) : base(name, unencryptedKey) { } + + private static byte[] GenerateNewColumnEncryptionKey() + { + byte[] plainTextColumnEncryptionKey = new byte[32]; + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(plainTextColumnEncryptionKey); + + return plainTextColumnEncryptionKey; + } + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// true if the current object is equal to the other parameter; otherwise, false. + public override bool Equals(object obj) + { + if (!(obj is PlaintextDataEncryptionKey other)) + { + return false; + } + + return Name.Equals(other.Name) && RootKeyEquals(other); + } + + /// + /// Generates a hash code for the current object. + /// + /// A hash code for the current object. + public override int GetHashCode() => Tuple.Create(Name, rootKeyHexString).GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/ProtectedDataEncryptionKey.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/ProtectedDataEncryptionKey.cs new file mode 100644 index 0000000000..e375ad9128 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/ProtectedDataEncryptionKey.cs @@ -0,0 +1,135 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Security.Cryptography; + +namespace Microsoft.Data.Encryption.Cryptography +{ + /// + /// A , protected by a , that is used to encrypt and decrypt data. + /// + internal class ProtectedDataEncryptionKey : DataEncryptionKey + { + /// + /// A local cache to hold previously created objects for reuse. + /// + /// + /// The retension period is defined by the property. + /// + private static readonly LocalCache, ProtectedDataEncryptionKey> encryptionKeyCache + = new LocalCache, ProtectedDataEncryptionKey>() { TimeToLive = TimeSpan.FromHours(2) }; + + /// + /// Sets an absolute expiration time, relative to now. + /// + /// + /// The default is 2 hours. Setting the will apply to every new + /// object instantiated by the method after this + /// change and before the next change. + /// If the object returned by the method already + /// existed in the cache before this change (cache hit) then the returned + /// object will have the value attributed to it that existed at the time + /// it was originally created. + /// + public static TimeSpan TimeToLive + { + get => encryptionKeyCache.TimeToLive.Value; + set => encryptionKeyCache.TimeToLive = value; + } + + /// + /// Specifies the used for encrypting and decrypting the . + /// + public KeyEncryptionKey KeyEncryptionKey { get; private set; } + + /// + /// The encrypted value. + /// + public byte[] EncryptedValue { get; private set; } + + /// + /// Returns a cached instance of the or, if not present, creates a new one + /// + /// The name by which the will be known. + /// Specifies the used for encrypting and decrypting the . + /// The encrypted value. + /// An object. + public static ProtectedDataEncryptionKey GetOrCreate(string name, KeyEncryptionKey keyEncryptionKey, byte[] encryptedKey) + { + name.ValidateNotNullOrWhitespace(nameof(name)); + keyEncryptionKey.ValidateNotNull(nameof(keyEncryptionKey)); + encryptedKey.ValidateNotNullOrEmpty(nameof(encryptedKey)); + + return encryptionKeyCache.GetOrCreate( + key: Tuple.Create(name, keyEncryptionKey, encryptedKey.ToHexString()), + createItem: () => new ProtectedDataEncryptionKey(name, keyEncryptionKey, encryptedKey) + ); + } + + /// + /// Initializes a new instance of the class derived from + /// generating an array of bytes with a cryptographically strong random sequence of values. + /// + /// The name by which the will be known. + /// Specifies the used for encrypting and decrypting the . + public ProtectedDataEncryptionKey(string name, KeyEncryptionKey keyEncryptionKey) : this(name, keyEncryptionKey, GenerateNewColumnEncryptionKey(keyEncryptionKey)) { } + + /// + /// Initializes a new instance of the class derived from + /// decrypting the . + /// + /// The name by which the will be known. + /// Specifies the used for encrypting and decrypting the . + /// The encrypted value. + public ProtectedDataEncryptionKey(string name, KeyEncryptionKey keyEncryptionKey, byte[] encryptedKey) : base(name, keyEncryptionKey.DecryptEncryptionKey(encryptedKey)) + { + name.ValidateNotNullOrWhitespace(nameof(name)); + keyEncryptionKey.ValidateNotNull(nameof(keyEncryptionKey)); + + KeyEncryptionKey = keyEncryptionKey; + EncryptedValue = encryptedKey; + } + + private static byte[] GenerateNewColumnEncryptionKey(KeyEncryptionKey masterKey) + { + byte[] plainTextColumnEncryptionKey = new byte[32]; + RandomNumberGenerator rng = RandomNumberGenerator.Create(); + rng.GetBytes(plainTextColumnEncryptionKey); + + return masterKey.EncryptEncryptionKey(plainTextColumnEncryptionKey); + } + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// An object to compare with this object. + /// true if the current object is equal to the other parameter; otherwise, false. + public override bool Equals(object obj) + { + if (!(obj is ProtectedDataEncryptionKey other)) + { + return false; + } + + if (KeyEncryptionKey.IsNull() && !other.KeyEncryptionKey.IsNull()) + { + return false; + } + + return Name.Equals(other.Name) + && KeyEncryptionKey.Equals(other.KeyEncryptionKey) + && RootKeyEquals(other); + } + + /// + /// Generates a hash code for the current object. + /// + /// A hash code for the current object. + public override int GetHashCode() => Tuple.Create(Name, KeyEncryptionKey, rootKeyHexString).GetHashCode(); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/ISerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/ISerializer.cs new file mode 100644 index 0000000000..0a4a99d253 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/ISerializer.cs @@ -0,0 +1,65 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing data objects. + /// + internal interface ISerializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + string Identifier { get; } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// The serialized data as a byte array + byte[] Serialize(object value); + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + object Deserialize(byte[] bytes); + } + + + /// + /// Contains the methods for serializing and deserializing data objects. + /// + /// The type on which this will perform serialization operations. + internal abstract class Serializer : ISerializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public abstract string Identifier { get; } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// The serialized data as a byte array + public abstract byte[] Serialize(T value); + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + public abstract T Deserialize(byte[] bytes); + + byte[] ISerializer.Serialize(object value) => Serialize((T)value); + + object ISerializer.Deserialize(byte[] bytes) => Deserialize(bytes); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SerializerFactory.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SerializerFactory.cs new file mode 100644 index 0000000000..2cd3b20895 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SerializerFactory.cs @@ -0,0 +1,40 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Provides methods for getting serializer implementations, such as by type and ID. + /// + internal abstract class SerializerFactory + { + /// + /// Gets a registered serializer by its Identifier Property. + /// + /// The identifier uniquely identifies a particular Serializer implementation. + /// The ISerializer implementation + public abstract ISerializer GetSerializer(string identifier); + + /// + /// Gets a default registered serializer for the type. + /// + /// The data type to be serialized. + /// A default registered serializer for the type. + public abstract Serializer GetDefaultSerializer(); + + /// + /// Registers a custom serializer. + /// + /// The data type on which the Serializer operates. + /// The Serializer to register. + /// Determines whether to override an existing default serializer for the same type. + public abstract void RegisterSerializer(Type type, ISerializer serializer, bool overrideDefault = false); + + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializerFactory.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializerFactory.cs new file mode 100644 index 0000000000..0796d29eb3 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializerFactory.cs @@ -0,0 +1,222 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Collections.Generic; + +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Provides methods for getting serializer implementations, such as by type and ID. + /// + internal class SqlSerializerFactory : SerializerFactory + { + private readonly Dictionary serializerByType = new Dictionary(); + + private readonly Dictionary serializerByIdentifier = new Dictionary(); + + private static readonly Dictionary> createSerializerByType = new Dictionary>() + { + [typeof(SqlBitSerializer)] = (size, precision, scale, codepage) => new SqlBitSerializer(), + [typeof(SqlNullableBitSerializer)] = (size, precision, scale, codepage) => new SqlNullableBitSerializer(), + [typeof(SqlTinyIntSerializer)] = (size, precision, scale, codepage) => new SqlTinyIntSerializer(), + [typeof(SqlNullableTinyIntSerializer)] = (size, precision, scale, codepage) => new SqlNullableTinyIntSerializer(), + [typeof(SqlVarBinarySerializer)] = (size, precision, scale, codepage) => new SqlVarBinarySerializer(size), + [typeof(SqlDateTime2Serializer)] = (size, precision, scale, codepage) => new SqlDateTime2Serializer(precision), + [typeof(SqlNullableDateTime2Serializer)] = (size, precision, scale, codepage) => new SqlNullableDateTime2Serializer(precision), + [typeof(SqlDateTimeOffsetSerializer)] = (size, precision, scale, codepage) => new SqlDateTimeOffsetSerializer(scale), + [typeof(SqlNullableDateTimeOffsetSerializer)] = (size, precision, scale, codepage) => new SqlNullableDateTimeOffsetSerializer(scale), + [typeof(SqlDecimalSerializer)] = (size, precision, scale, codepage) => new SqlDecimalSerializer(precision, scale), + [typeof(SqlNullableDecimalSerializer)] = (size, precision, scale, codepage) => new SqlNullableDecimalSerializer(precision, scale), + [typeof(SqlFloatSerializer)] = (size, precision, scale, codepage) => new SqlFloatSerializer(), + [typeof(SqlNullableFloatSerializer)] = (size, precision, scale, codepage) => new SqlNullableFloatSerializer(), + [typeof(SqlRealSerializer)] = (size, precision, scale, codepage) => new SqlRealSerializer(), + [typeof(SqlNullableRealSerializer)] = (size, precision, scale, codepage) => new SqlNullableRealSerializer(), + [typeof(SqlUniqueIdentifierSerializer)] = (size, precision, scale, codepage) => new SqlUniqueIdentifierSerializer(), + [typeof(SqlNullableUniqueIdentifierSerializer)] = (size, precision, scale, codepage) => new SqlNullableUniqueIdentifierSerializer(), + [typeof(SqlIntSerializer)] = (size, precision, scale, codepage) => new SqlIntSerializer(), + [typeof(SqlNullableIntSerializer)] = (size, precision, scale, codepage) => new SqlNullableIntSerializer(), + [typeof(SqlBigIntSerializer)] = (size, precision, scale, codepage) => new SqlBigIntSerializer(), + [typeof(SqlNullableBigIntSerializer)] = (size, precision, scale, codepage) => new SqlNullableBigIntSerializer(), + [typeof(SqlSmallIntSerializer)] = (size, precision, scale, codepage) => new SqlSmallIntSerializer(), + [typeof(SqlNullableSmallIntSerializer)] = (size, precision, scale, codepage) => new SqlNullableSmallIntSerializer(), + [typeof(SqlNVarCharSerializer)] = (size, precision, scale, codepage) => new SqlNVarCharSerializer(size), + [typeof(SqlTimeSerializer)] = (size, precision, scale, codepage) => new SqlTimeSerializer(scale), + [typeof(SqlNullableTimeSerializer)] = (size, precision, scale, codepage) => new SqlNullableTimeSerializer(scale), + [typeof(SqlBinarySerializer)] = (size, precision, scale, codepage) => new SqlBinarySerializer(size), + [typeof(SqlDateSerializer)] = (size, precision, scale, codepage) => new SqlDateSerializer(), + [typeof(SqlNullableDateSerializer)] = (size, precision, scale, codepage) => new SqlNullableDateSerializer(), + [typeof(SqlDateTimeSerializer)] = (size, precision, scale, codepage) => new SqlDateTimeSerializer(), + [typeof(SqlNullableDateTimeSerializer)] = (size, precision, scale, codepage) => new SqlNullableDateTimeSerializer(), + [typeof(SqlSmallDateTimeSerializer)] = (size, precision, scale, codepage) => new SqlSmallDateTimeSerializer(), + [typeof(SqlNullableSmallDateTimeSerializer)] = (size, precision, scale, codepage) => new SqlNullableSmallDateTimeSerializer(), + [typeof(SqlMoneySerializer)] = (size, precision, scale, codepage) => new SqlMoneySerializer(), + [typeof(SqlNullableMoneySerializer)] = (size, precision, scale, codepage) => new SqlNullableMoneySerializer(), + [typeof(SqlNumericSerializer)] = (size, precision, scale, codepage) => new SqlNumericSerializer(precision, scale), + [typeof(SqlNullableNumericSerializer)] = (size, precision, scale, codepage) => new SqlNullableNumericSerializer(precision, scale), + [typeof(SqlSmallMoneySerializer)] = (size, precision, scale, codepage) => new SqlSmallMoneySerializer(), + [typeof(SqlNullableSmallMoneySerializer)] = (size, precision, scale, codepage) => new SqlNullableSmallMoneySerializer(), + [typeof(SqlNCharSerializer)] = (size, precision, scale, codepage) => new SqlNCharSerializer(size), + [typeof(SqlCharSerializer)] = (size, precision, scale, codepage) => new SqlCharSerializer(size, codepage), + [typeof(SqlVarCharSerializer)] = (size, precision, scale, codepage) => new SqlVarCharSerializer(size, codepage), + }; + + private static readonly LocalCache, ISerializer> serializerCache + = new LocalCache, ISerializer>(); + + /// + /// Returns a cached instance of the or, if not present, creates a new one. + /// + /// The type of serializer to get or create. + /// The maximum size of the data. + /// The maximum number of digits. + /// The number of decimal places. + /// The code page character encoding. + /// An object. + public static ISerializer GetOrCreate(Type serializerType, int size = 1, byte precision = 1, byte scale = 1, int codepage = 1) + { + serializerType.ValidateNotNull(nameof(serializerType)); + + return serializerCache.GetOrCreate( + key: Tuple.Create(serializerType, size, precision, scale, codepage), + createItem: () => createSerializerByType[serializerType].Invoke(size, precision, scale, codepage) + ); + } + + /// + /// Initializes a new instance of the class. + /// + public SqlSerializerFactory() + { + Initialize(); + } + + /// + /// Returns a default instance of the class. + /// + public static SqlSerializerFactory Default { get; } = new SqlSerializerFactory(); + + /// + /// Gets a registered serializer by its Identifier Property. + /// + /// The identifier uniquely identifies a particular Serializer implementation. + /// The ISerializer implementation + public override ISerializer GetSerializer(string identifier) + { + identifier.ValidateNotNull(nameof(identifier)); + + if (serializerByIdentifier.ContainsKey(identifier)) + { + return serializerByIdentifier[identifier]; + } + + return null; + } + + /// + /// Gets a default registered serializer for the type. + /// + /// The data type to be serialized. + /// A default registered serializer for the type. + public override Serializer GetDefaultSerializer() + { + if (serializerByType.ContainsKey(typeof(T))) + { + return (Serializer)serializerByType[typeof(T)]; + } + + throw new MicrosoftDataEncryptionException(DefaultAESerializerNotFound.Format(typeof(T).Name, nameof(RegisterSerializer))); + } + + /// + /// Registers a custom serializer. + /// + /// The data type on which the Serializer operates. + /// The Serializer to register. + /// Determines whether to override an existing default serializer for the same type. + public override void RegisterSerializer(Type type, ISerializer serializer, bool overrideDefault = false) + { + type.ValidateNotNull(nameof(type)); + serializer.ValidateNotNull(nameof(serializer)); + + serializerByIdentifier[serializer.Identifier] = serializer; + + if (overrideDefault || !HasDefaultSqlSerializer(type)) + { + serializerByType[type] = serializer; + } + } + + private bool HasDefaultSerializer(Type type) + { + return serializerByType.ContainsKey(type); + } + + private bool HasDefaultSqlSerializer(Type type) + { + return serializerByType.ContainsKey(type); + } + + private void Initialize() + { + RegisterDefaultSqlSerializers(); + RegisterNonDefaultSqlSerializers(); + } + + private void RegisterDefaultSqlSerializers() + { + RegisterSerializer(typeof(bool), new SqlBitSerializer(), overrideDefault: true); + RegisterSerializer(typeof(bool?), new SqlNullableBitSerializer(), overrideDefault: true); + RegisterSerializer(typeof(byte), new SqlTinyIntSerializer(), overrideDefault: true); + RegisterSerializer(typeof(byte?), new SqlNullableTinyIntSerializer(), overrideDefault: true); + RegisterSerializer(typeof(byte[]), new SqlVarBinarySerializer(), overrideDefault: true); + RegisterSerializer(typeof(DateTime), new SqlDateTime2Serializer(), overrideDefault: true); + RegisterSerializer(typeof(DateTime?), new SqlNullableDateTime2Serializer(), overrideDefault: true); + RegisterSerializer(typeof(DateTimeOffset), new SqlDateTimeOffsetSerializer(), overrideDefault: true); + RegisterSerializer(typeof(DateTimeOffset?), new SqlNullableDateTimeOffsetSerializer(), overrideDefault: true); + RegisterSerializer(typeof(decimal), new SqlDecimalSerializer(), overrideDefault: true); + RegisterSerializer(typeof(decimal?), new SqlNullableDecimalSerializer(), overrideDefault: true); + RegisterSerializer(typeof(double), new SqlFloatSerializer(), overrideDefault: true); + RegisterSerializer(typeof(double?), new SqlNullableFloatSerializer(), overrideDefault: true); + RegisterSerializer(typeof(float), new SqlRealSerializer(), overrideDefault: true); + RegisterSerializer(typeof(float?), new SqlNullableRealSerializer(), overrideDefault: true); + RegisterSerializer(typeof(Guid), new SqlUniqueIdentifierSerializer(), overrideDefault: true); + RegisterSerializer(typeof(Guid?), new SqlNullableUniqueIdentifierSerializer(), overrideDefault: true); + RegisterSerializer(typeof(int), new SqlIntSerializer(), overrideDefault: true); + RegisterSerializer(typeof(int?), new SqlNullableIntSerializer(), overrideDefault: true); + RegisterSerializer(typeof(long), new SqlBigIntSerializer(), overrideDefault: true); + RegisterSerializer(typeof(long?), new SqlNullableBigIntSerializer(), overrideDefault: true); + RegisterSerializer(typeof(short), new SqlSmallIntSerializer(), overrideDefault: true); + RegisterSerializer(typeof(short?), new SqlNullableSmallIntSerializer(), overrideDefault: true); + RegisterSerializer(typeof(string), new SqlNVarCharSerializer(), overrideDefault: true); + RegisterSerializer(typeof(TimeSpan), new SqlTimeSerializer(), overrideDefault: true); + RegisterSerializer(typeof(TimeSpan?), new SqlNullableTimeSerializer(), overrideDefault: true); + } + + private void RegisterNonDefaultSqlSerializers() + { + RegisterSerializer(typeof(byte[]), new SqlBinarySerializer(), overrideDefault: false); + RegisterSerializer(typeof(DateTime), new SqlDateSerializer(), overrideDefault: false); + RegisterSerializer(typeof(DateTime?), new SqlNullableDateSerializer(), overrideDefault: false); + RegisterSerializer(typeof(DateTime), new SqlDateTimeSerializer(), overrideDefault: false); + RegisterSerializer(typeof(DateTime?), new SqlNullableDateTimeSerializer(), overrideDefault: false); + RegisterSerializer(typeof(DateTime), new SqlSmallDateTimeSerializer(), overrideDefault: false); + RegisterSerializer(typeof(DateTime), new SqlNullableSmallDateTimeSerializer(), overrideDefault: false); + RegisterSerializer(typeof(decimal), new SqlMoneySerializer(), overrideDefault: false); + RegisterSerializer(typeof(decimal?), new SqlNullableMoneySerializer(), overrideDefault: false); + RegisterSerializer(typeof(decimal), new SqlNumericSerializer(), overrideDefault: false); + RegisterSerializer(typeof(decimal?), new SqlNullableNumericSerializer(), overrideDefault: false); + RegisterSerializer(typeof(decimal), new SqlSmallMoneySerializer(), overrideDefault: false); + RegisterSerializer(typeof(decimal?), new SqlNullableSmallMoneySerializer(), overrideDefault: false); + RegisterSerializer(typeof(string), new SqlNCharSerializer(), overrideDefault: false); + RegisterSerializer(typeof(string), new SqlCharSerializer(), overrideDefault: false); + RegisterSerializer(typeof(string), new SqlVarCharSerializer(), overrideDefault: false); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlBigIntSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlBigIntSerializer.cs new file mode 100644 index 0000000000..6155939cb9 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlBigIntSerializer.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL. + /// + internal sealed class SqlBigIntSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_BigInt"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override long Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(sizeof(long), nameof(bytes)); + + return ToInt64(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(long value) => GetBytes(value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlBinarySerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlBinarySerializer.cs new file mode 100644 index 0000000000..b80c74e2ec --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlBinarySerializer.cs @@ -0,0 +1,100 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Linq; + +using static System.Linq.Enumerable; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing [] type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL. + /// + internal sealed class SqlBinarySerializer : Serializer + { + private const int DefaultSize = 30; + private const int MinSize = 1; + private const int MaxSize = 8000; + private static readonly byte Padding = 0; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Binary_Nullable"; + + private int size; + + /// + /// Gets or sets the maximum length of the data. + /// + /// + /// If not explicitly set, the size is defaulted to 30. + /// + /// + /// Thrown when set to a value that is out of the valid range [1 - 8000] for this setting. + /// + public int Size + { + get => size; + set + { + if (value < MinSize || value > MaxSize) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + size = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum length of the data + /// + /// Thrown when set to a value that is out of the valid range [1 - 8000] for this setting. + /// + public SqlBinarySerializer(int size = DefaultSize) + { + Size = size; + } + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// If the deserialized value is less than the size, then the value will be padded on the + /// left to match the size. Padding is achieved by using hexadecimal zeros. + /// + public override byte[] Deserialize(byte[] bytes) + { + return bytes.IsNull() ? null : PadToLength(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// The serialized data as a byte array + /// + /// If the value is greater than the size, then the value will be truncated on the left to match the size. + /// + public override byte[] Serialize(byte[] value) + { + return value.IsNull() ? null : TrimToLength(value); + } + + private byte[] TrimToLength(byte[] value) => value.Length > Size ? value.Take(Size).ToArray() : value; + + private byte[] PadToLength(byte[] value) => value.Length < Size ? value.Concat(Repeat(Padding, Size - value.Length)).ToArray() : value; + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlBitSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlBitSerializer.cs new file mode 100644 index 0000000000..30fa11b4aa --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlBitSerializer.cs @@ -0,0 +1,55 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL. + /// + internal sealed class SqlBitSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Bit"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override bool Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(sizeof(long), nameof(bytes)); + + return ToBoolean(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(bool value) + { + long longValue = value ? 1 : 0; + return GetBytes(longValue); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlCharSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlCharSerializer.cs new file mode 100644 index 0000000000..54a305ed8d --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlCharSerializer.cs @@ -0,0 +1,122 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Linq; +using System.Text; + +using static System.Text.Encoding; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL. + /// + internal sealed class SqlCharSerializer : SqlCodePageEncodingSerializer + { + private const int DefaultSize = 30; + private const int MinSize = 1; + private const int MaxSize = 8000; + private static readonly char Padding = ' '; + + /// + /// The default character encoding Windows-1252. It is also referred to as "ANSI". + /// + private const int DefaultEncodingCodePoint = 1252; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Char_Nullable"; + + private int size; + + /// + /// Gets or sets the maximum length of the data. + /// + /// + /// If not explicitly set, the size is defaulted to 30. + /// + /// + /// Thrown when set to a value that is out of the valid range [1 - 8000] for this setting. + /// + public int Size + { + get => size; + set + { + if (value < MinSize || value > MaxSize) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + size = value; + } + } + + private Encoding characterEncoding; + + /// + /// Gets or sets the character encoding. + /// + /// + /// If not explicitly set, the encoding is defaulted to Windows-1252, which is also referred to as "ANSI". + /// + public int CodePageCharacterEncoding + { + get => characterEncoding.CodePage; + set => characterEncoding = GetEncoding(value); + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum length of the data + /// The code page character encoding. + /// + /// Thrown when set to a value that is out of the valid range [1 - 8000] for this setting. + /// + public SqlCharSerializer(int size = DefaultSize, int codePageCharacterEncoding = DefaultEncodingCodePoint) + { + Size = size; + characterEncoding = GetEncoding(codePageCharacterEncoding); + } + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// If the deserialized value's length is less than the size, then the value will be padded on the + /// left to match the size. Padding is achieved by using spaces. + /// + public override string Deserialize(byte[] bytes) + { + return bytes.IsNull() ? null : PadToLength(characterEncoding.GetString(bytes)); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// The serialized data as a byte array + /// + /// If the value's length is greater than the size, then the value will be truncated on the left to match the size. + /// + public override byte[] Serialize(string value) + { + return value.IsNull() ? null : characterEncoding.GetBytes(TrimToLength(value)); + } + + private string TrimToLength(string value) => value.Length > Size ? new string(value.Take(Size).ToArray()) : value; + + private string PadToLength(string value) => value.Length < Size ? value.PadRight(Size, Padding) : value; + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlCodePageEncodingSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlCodePageEncodingSerializer.cs new file mode 100644 index 0000000000..b5e9b08d54 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlCodePageEncodingSerializer.cs @@ -0,0 +1,31 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System.Text; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Provides the base class for an encoding provider serializer, which supplies encodings that may be unavailable on a particular platform. + /// + /// + /// For .NET Core, we need to register the before attempting to get an Encoding from a CodePage + /// For a default installation of SqlServer the encoding exchanged during Login is 1252. This encoding is not loaded by default. + /// See Remarks at https://msdn.microsoft.com/en-us/library/system.text.encodingprovider(v=vs.110).aspx. + /// + internal abstract class SqlCodePageEncodingSerializer : Serializer + { + /// + /// Static constructor is called at most one time, before any + /// instance constructor is invoked or member is accessed. + /// + static SqlCodePageEncodingSerializer() + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDateSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDateSerializer.cs new file mode 100644 index 0000000000..5fe72ec0e1 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDateSerializer.cs @@ -0,0 +1,61 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Linq; +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL. + /// + internal sealed class SqlDateSerializer : Serializer + { + private const int SizeOfDate = 3; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Date"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 3. + /// + public override DateTime Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(SizeOfDate, nameof(bytes)); + + byte[] padding = { 0 }; + byte[] bytesWithPadding = bytes.Concat(padding).ToArray(); + int days = ToInt32(bytesWithPadding, 0); + return DateTime.MinValue.AddDays(days); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 3. + /// + public override byte[] Serialize(DateTime value) + { + int days = value.Subtract(DateTime.MinValue).Days; + return GetBytes(days).Take(SizeOfDate).ToArray(); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDatetime2Serializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDatetime2Serializer.cs new file mode 100644 index 0000000000..64d64d8054 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDatetime2Serializer.cs @@ -0,0 +1,116 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Linq; + +using static System.BitConverter; +using static Microsoft.Data.Encryption.Cryptography.Serializers.SqlTimeSerializer; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL. + /// + internal sealed class SqlDateTime2Serializer : Serializer + { + private const int MaxPrecision = 7; + private const int MinPrecision = 0; + private const int DefaultPrecision = 7; + private readonly SqlDateSerializer sqlDateSerializer = new SqlDateSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_DateTime2"; + + private int precision; + + /// + /// Gets or sets the maximum number of digits used to represent the Value. The default is 7. + /// + /// + /// Thrown when set to a value that is out of the valid range [0 - 7] for this setting. + /// + public int Precision + { + get { return precision; } + set + { + if (value < MinPrecision || value > MaxPrecision) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + precision = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The number of decimal places to which Value is resolved + /// + /// Thrown when set to a value that is out of the valid range [0 - 7] for this setting. + /// + public SqlDateTime2Serializer(int precision = DefaultPrecision) + { + Precision = precision; + } + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override DateTime Deserialize(byte[] bytes) + { + const int SizeOfTimePart = 5; + const int SizeOfDatePart = 3; + const int SizeOfData = SizeOfTimePart + SizeOfDatePart; + + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(SizeOfData, nameof(bytes)); + + byte[] padding = { 0, 0, 0 }; + byte[] timePart = bytes.Take(SizeOfTimePart).Concat(padding).ToArray(); + byte[] datePart = bytes.Skip(SizeOfTimePart).Take(SizeOfDatePart).ToArray(); + + long timeTicks = ToInt64(timePart, 0); + DateTime dateTime = sqlDateSerializer.Deserialize(datePart); + return dateTime.AddTicks(timeTicks); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(DateTime value) + { + DateTime normalizedValue = NormalizeToScale(value, Precision); + long time = normalizedValue.TimeOfDay.Ticks; + byte[] timePart = GetBytes(time).Take(5).ToArray(); + byte[] datePart = sqlDateSerializer.Serialize(value); + return timePart.Concat(datePart).ToArray(); + } + + private static DateTime NormalizeToScale(DateTime dateTime, int scale) + { + long normalizedTicksOffset = (dateTime.TimeOfDay.Ticks / precisionScale[scale] * precisionScale[scale]) - dateTime.TimeOfDay.Ticks; + return dateTime.AddTicks(normalizedTicksOffset); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDatetimeSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDatetimeSerializer.cs new file mode 100644 index 0000000000..3eaa3c96c2 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDatetimeSerializer.cs @@ -0,0 +1,78 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Data.SqlTypes; +using System.Linq; + +using static System.BitConverter; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL. + /// + internal sealed class SqlDateTimeSerializer : Serializer + { + private static readonly DateTime MinValue = DateTime.Parse("1753-01-01 00:00:00"); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_DateTime"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override DateTime Deserialize(byte[] bytes) + { + const int SizeOfDate = 4; + const int SizeOfTime = 4; + const int SizeOfDateTime = SizeOfDate + SizeOfTime; + + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(SizeOfDateTime, nameof(bytes)); + + int dayTicks = ToInt32(bytes.Take(SizeOfDate).ToArray(), 0); + int timeTicks = ToInt32(bytes.Skip(SizeOfDate).Take(SizeOfTime).ToArray(), 0); + SqlDateTime sqlDateTime = new SqlDateTime(dayTicks, timeTicks); + return sqlDateTime.Value; + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(DateTime value) + { + if (value < MinValue) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + SqlDateTime sqlDateTime = new SqlDateTime(value); + byte[] dayTicks = GetBytes(sqlDateTime.DayTicks); + byte[] timeTicks = GetBytes(sqlDateTime.TimeTicks); + return dayTicks.Concat(timeTicks).ToArray(); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDatetimeoffsetSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDatetimeoffsetSerializer.cs new file mode 100644 index 0000000000..6bf43db6df --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDatetimeoffsetSerializer.cs @@ -0,0 +1,109 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Linq; + +using static System.BitConverter; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL. + /// + internal sealed class SqlDateTimeOffsetSerializer : Serializer + { + private const int MaxScale = 7; + private const int MinScale = 0; + private const int DefaultScale = 7; + private readonly SqlDateTime2Serializer sqlDateTime2Serializer; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_DateTimeOffset"; + + private int scale; + + /// + /// Gets or sets the number of decimal places to which Value is resolved. The default is 7. + /// + /// + /// Thrown when set to a value that is out of the valid range [0 - 7] for this setting. + /// + public int Scale + { + get => scale; + set + { + if (value < MinScale || value > MaxScale) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + scale = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The number of decimal places to which Value is resolved. + /// + /// Thrown when set to a value that is out of the valid range [0 - 7] for this setting. + /// + public SqlDateTimeOffsetSerializer(int scale = DefaultScale) + { + Scale = scale; + sqlDateTime2Serializer = new SqlDateTime2Serializer(scale); + } + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 10. + /// + public override DateTimeOffset Deserialize(byte[] bytes) + { + const int DateTimeIndex = 0; + const int TimeSpanIndex = sizeof(long); + const int DataSize = sizeof(long) + sizeof(short); + + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(DataSize, nameof(bytes)); + + byte[] dateTimePart = bytes.Skip(DateTimeIndex).Take(sizeof(long)).ToArray(); + byte[] offsetPart = bytes.Skip(TimeSpanIndex).Take(sizeof(short)).ToArray(); + + short minutes = ToInt16(offsetPart, 0); + DateTime dateTime = sqlDateTime2Serializer.Deserialize(dateTimePart).AddMinutes(minutes); + TimeSpan offset = new TimeSpan(0, minutes, 0); + return new DateTimeOffset(dateTime, offset); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 10. + /// + public override byte[] Serialize(DateTimeOffset value) + { + byte[] datetimePart = sqlDateTime2Serializer.Serialize(value.UtcDateTime); + short offsetMinutes = (short)value.Offset.TotalMinutes; + byte[] offsetPart = GetBytes(offsetMinutes); + return datetimePart.Concat(offsetPart).ToArray(); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDecimalSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDecimalSerializer.cs new file mode 100644 index 0000000000..52e7f270b3 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlDecimalSerializer.cs @@ -0,0 +1,159 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Data.SqlTypes; +using System.Linq; + +using static System.BitConverter; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal sealed class SqlDecimalSerializer : Serializer + { + private const int SignIndex = 3; + private const int MaxPrecision = 38; + private const int MinPrecision = 1; + private const int DefaultPrecision = 18; + private const int MinScale = 0; + private const int DefaultScale = 0; + private static readonly byte[] positiveSign = { 1 }; + private static readonly byte[] negativeSign = { 0 }; + private static readonly byte[] padding = { 0, 0, 0, 0 }; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Decimal"; + + private int precision; + + /// + /// Gets or sets the maximum number of digits used to represent the value. The default precision is 18. + /// + /// + /// The represents the maximum total number of decimal digits to be stored. + /// This number includes both the left and the right sides of the decimal point. + /// The precision must be a value from 1 through the maximum precision of 38. + /// + /// + /// Thrown when set to a value that is out of the valid range [1 - 38] for this setting. + /// + public int Precision + { + get => precision; + set + { + if (value < MinPrecision || value > MaxPrecision) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + precision = value; + } + } + + private int scale; + + /// + /// Gets or sets the number of decimal places to which Value is resolved. + /// + /// + /// The number of decimal digits that are stored to the right of the decimal point. This number is subtracted from the + /// to determine the maximum number of digits to the left of the decimal point. must be a value from 0 through . + /// The default scale is 0 and so 0 ≤ . + /// + /// + /// Thrown when set to a value that is out of the valid range [0 - ] for this setting. + /// + public int Scale + { + get => scale; + set + { + if (value < MinScale || value > Precision) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + scale = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum number of digits used to represent the value. The default precision is 18. + /// The number of decimal places to which Value is resolved. The default scale is 0. + /// + /// Thrown when is set to a value that is out of the valid range [1 - 38] or when + /// is set to a value that is out of the valid range [0 - ] for this setting. + /// + public SqlDecimalSerializer(int precision = DefaultPrecision, int scale = DefaultScale) + { + Precision = precision; + Scale = scale; + } + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 17. + /// + public override decimal Deserialize(byte[] bytes) + { + const int SizeOfDecimal = 17; + + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(SizeOfDecimal, nameof(bytes)); + + int low = ToInt32(bytes.Skip(1).Take(4).ToArray(), 0); + int middle = ToInt32(bytes.Skip(5).Take(4).ToArray(), 0); + int high = ToInt32(bytes.Skip(9).Take(4).ToArray(), 0); + bool isNegative = bytes[0] == negativeSign[0]; + + return new decimal(low, middle, high, isNegative, (byte)Scale); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 17. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(decimal value) + { + SqlDecimal sqlDecimal = new SqlDecimal(value); + + if (sqlDecimal.Precision > Precision) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + int[] decimalBits = decimal.GetBits(value); + byte[] sign = IsPositive(decimalBits[SignIndex]) ? positiveSign : negativeSign; + byte[] integerPart = decimalBits.Take(3).SelectMany(GetBytes).ToArray(); + return sign.Concat(integerPart).Concat(padding).ToArray(); + } + + private bool IsPositive(int i) => i > -1; + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlFloatSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlFloatSerializer.cs new file mode 100644 index 0000000000..3eb1bedabb --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlFloatSerializer.cs @@ -0,0 +1,64 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static System.BitConverter; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal sealed class SqlFloatSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Float"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override double Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(sizeof(double), nameof(bytes)); + + return ToDouble(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(double value) + { + if (double.IsInfinity(value) || double.IsNaN(value)) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + return GetBytes(value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlIntSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlIntSerializer.cs new file mode 100644 index 0000000000..dc99e63f34 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlIntSerializer.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal sealed class SqlIntSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Int"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override int Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(sizeof(long), nameof(bytes)); + + return ToInt32(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(int value) => GetBytes((long)value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlMoneySerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlMoneySerializer.cs new file mode 100644 index 0000000000..7d7aeab079 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlMoneySerializer.cs @@ -0,0 +1,93 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Data.SqlTypes; +using System.Linq; + +using static System.BitConverter; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal sealed class SqlMoneySerializer : Serializer + { + private const byte Scale = 4; + private const decimal MinValue = -922337203685477.5808M; + private const decimal MaxValue = 922337203685477.5807M; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Money"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override decimal Deserialize(byte[] bytes) + { + const int SizeOfData = 8; + + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(SizeOfData, nameof(bytes)); + + uint low = ToUInt32(bytes, 4); + int middle = ToInt32(bytes, 0); + long longValue = ((long)middle << 32) + low; + bool isNegative = longValue < 0; + int sign = isNegative ? -1 : 1; + long signedLongValue = longValue * sign; + int signedLow = (int)(signedLongValue & uint.MaxValue); + int signedMiddle = (int)(signedLongValue >> 32); + + return new decimal(signedLow, signedMiddle, 0, isNegative, Scale); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(decimal value) + { + if (value < MinValue || value > MaxValue) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + value = Normalize(value); + int sign = value > -1 ? 1 : -1; + int[] decimalBits = decimal.GetBits(value); + long longValue = ((long)decimalBits[1] << 32) + (uint)decimalBits[0]; + long signedLongValue = longValue * sign; + int low = (int)(signedLongValue >> 32); + int mid = (int)signedLongValue; + byte[] lowbytes = GetBytes(low); + byte[] midBytes = GetBytes(mid); + + return lowbytes.Concat(midBytes).ToArray(); + } + + private decimal Normalize(decimal value) => new SqlMoney(value).Value; + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNcharSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNcharSerializer.cs new file mode 100644 index 0000000000..dd9b9e7682 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNcharSerializer.cs @@ -0,0 +1,100 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System.Linq; + +using static System.Linq.Enumerable; +using static System.Text.Encoding; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal sealed class SqlNCharSerializer : Serializer + { + private const int MinSize = 1; + private const int MaxSize = 4000; + private const int DefaultSize = 30; + private static readonly char Padding = ' '; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_NChar_Nullable"; + + private int size; + + /// + /// Gets or sets the maximum length of the data. + /// + /// + /// If not explicitly set, the size is defaulted to 30. + /// + /// + /// Thrown when set to a value that is out of the valid range [1 - 4000] for this setting. + /// + public int Size + { + get => size; + set + { + if (value < MinSize || value > MaxSize) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + size = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum length of the data + /// + /// Thrown when set to a value that is out of the valid range [1 - 4000] for this setting. + /// + public SqlNCharSerializer(int size = DefaultSize) + { + Size = size; + } + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// If the deserialized value's length is less than the size, then the value will be padded on the + /// left to match the size. Padding is achieved by using spaces. + /// + public override string Deserialize(byte[] bytes) + { + return bytes.IsNull() ? null : PadToLength(Unicode.GetString(bytes)); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// The serialized data as a byte array + /// + /// If the value's length is greater than the size, then the value will be truncated on the left to match the size. + /// + public override byte[] Serialize(string value) + { + return value.IsNull() ? null : Unicode.GetBytes(TrimToLength(value)); + } + + private string TrimToLength(string value) => value.Length > Size ? new string(value.Take(Size).ToArray()) : value; + + private string PadToLength(string value) => value.Length < Size ? value.PadRight(Size, Padding) : value; + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableBigIntSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableBigIntSerializer.cs new file mode 100644 index 0000000000..de11090f99 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableBigIntSerializer.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableBigIntSerializer : Serializer + { + private static readonly SqlBigIntSerializer serializer = new SqlBigIntSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_BigInt_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override long? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (long?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(long? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableBitSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableBitSerializer.cs new file mode 100644 index 0000000000..81ade73ef9 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableBitSerializer.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableBitSerializer : Serializer + { + private static readonly SqlBitSerializer serializer = new SqlBitSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Bit_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override bool? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (bool?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(bool? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDateSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDateSerializer.cs new file mode 100644 index 0000000000..fe10ff7d78 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDateSerializer.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableDateSerializer : Serializer + { + private static readonly SqlDateSerializer serializer = new SqlDateSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Date_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 3. + /// + public override DateTime? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (DateTime?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 3. + /// + public override byte[] Serialize(DateTime? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDatetime2Serializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDatetime2Serializer.cs new file mode 100644 index 0000000000..9415fee1f3 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDatetime2Serializer.cs @@ -0,0 +1,76 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableDateTime2Serializer : Serializer + { + private const int DefaultPrecision = 7; + + private readonly SqlDateTime2Serializer serializer; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_DateTime2_Nullable"; + + /// + /// Gets or sets the maximum number of digits used to represent the Value. The default is 7. + /// + /// + /// Thrown when set to a value that is out of the valid range [0 - 7] for this setting. + /// + public int Precision + { + get => serializer.Precision; + set => serializer.Precision = value; + } + + /// + /// Initializes a new instance of the class. + /// + /// The number of decimal places to which Value is resolved + /// + /// Thrown when set to a value that is out of the valid range [0 - 7] for this setting. + /// + public SqlNullableDateTime2Serializer(int precision = DefaultPrecision) + { + serializer = new SqlDateTime2Serializer(precision); + } + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override DateTime? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (DateTime?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(DateTime? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDatetimeSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDatetimeSerializer.cs new file mode 100644 index 0000000000..6320d62adf --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDatetimeSerializer.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableDateTimeSerializer : Serializer + { + private static readonly SqlDateTimeSerializer serializer = new SqlDateTimeSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_DateTime_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override DateTime? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (DateTime?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(DateTime? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDatetimeoffsetSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDatetimeoffsetSerializer.cs new file mode 100644 index 0000000000..8e04d50972 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDatetimeoffsetSerializer.cs @@ -0,0 +1,76 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableDateTimeOffsetSerializer : Serializer + { + private const int DefaultScale = 7; + private readonly SqlDateTimeOffsetSerializer serializer; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_DateTimeOffset_Nullable"; + + /// + /// Gets or sets the number of decimal places to which Value is resolved. The default is 7. + /// + /// + /// Thrown when set to a value that is out of the valid range [0 - 7] for this setting. + /// + public int Scale + { + get => serializer.Scale; + set => serializer.Scale = value; + } + + /// + /// Initializes a new instance of the class. + /// + /// The number of decimal places to which Value is resolved. + /// + /// Thrown when set to a value that is out of the valid range [0 - 7] for this setting. + /// + public SqlNullableDateTimeOffsetSerializer(int scale = DefaultScale) + { + serializer = new SqlDateTimeOffsetSerializer(scale); + } + + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 10. + /// + public override DateTimeOffset? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (DateTimeOffset?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 10. + /// + public override byte[] Serialize(DateTimeOffset? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDecimalSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDecimalSerializer.cs new file mode 100644 index 0000000000..7c3fb6a60d --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableDecimalSerializer.cs @@ -0,0 +1,104 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableDecimalSerializer : Serializer + { + private const int DefaultPrecision = 18; + private const int DefaultScale = 0; + private readonly SqlDecimalSerializer serializer; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Decimal_Nullable"; + + /// + /// Gets or sets the maximum number of digits used to represent the value. The default precision is 18. + /// + /// + /// The represents the maximum total number of decimal digits to be stored. + /// This number includes both the left and the right sides of the decimal point. + /// The precision must be a value from 1 through the maximum precision of 38. + /// + /// + /// Thrown when set to a value that is out of the valid range [1 - 38] for this setting. + /// + public int Precision + { + get => serializer.Precision; + set => serializer.Precision = value; + } + + /// + /// Gets or sets the number of decimal places to which Value is resolved. + /// + /// + /// The number of decimal digits that are stored to the right of the decimal point. This number is subtracted from the + /// to determine the maximum number of digits to the left of the decimal point. must be a value from 0 through . + /// The default scale is 0 and so 0 ≤ . + /// + /// + /// Thrown when set to a value that is out of the valid range [0 - ] for this setting. + /// + public int Scale + { + get => serializer.Scale; + set => serializer.Scale = value; + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum number of digits used to represent the value. The default precision is 18. + /// The number of decimal places to which Value is resolved. The default scale is 0. + /// + /// Thrown when is set to a value that is out of the valid range [1 - 38] or when + /// is set to a value that is out of the valid range [0 - ] for this setting. + /// + public SqlNullableDecimalSerializer(int precision = DefaultPrecision, int scale = DefaultScale) + { + serializer = new SqlDecimalSerializer(precision, scale); + } + + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 17. + /// + public override decimal? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (decimal?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 17. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(decimal? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableFloatSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableFloatSerializer.cs new file mode 100644 index 0000000000..ecb6df7688 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableFloatSerializer.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableFloatSerializer : Serializer + { + private static readonly SqlFloatSerializer serializer = new SqlFloatSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Float_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override double? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (double?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(double? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableIntSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableIntSerializer.cs new file mode 100644 index 0000000000..05b51d80c6 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableIntSerializer.cs @@ -0,0 +1,48 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableIntSerializer : Serializer + { + private static readonly SqlIntSerializer serializer = new SqlIntSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Int_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override int? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (int?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(int? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableMoneySerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableMoneySerializer.cs new file mode 100644 index 0000000000..4e6115f748 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableMoneySerializer.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableMoneySerializer : Serializer + { + private static readonly SqlMoneySerializer serializer = new SqlMoneySerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Money_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override decimal? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (decimal?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(decimal? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableNumericSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableNumericSerializer.cs new file mode 100644 index 0000000000..b98dd7fd0e --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableNumericSerializer.cs @@ -0,0 +1,104 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableNumericSerializer : Serializer + { + private const int DefaultPrecision = 18; + private const int DefaultScale = 0; + private readonly SqlNumericSerializer serializer; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Numeric_Nullable"; + + /// + /// Gets or sets the maximum number of digits used to represent the value. The default precision is 18. + /// + /// + /// The represents the maximum total number of decimal digits to be stored. + /// This number includes both the left and the right sides of the decimal point. + /// The precision must be a value from 1 through the maximum precision of 38. + /// + /// + /// Thrown when set to a value that is out of the valid range [1 - 38] for this setting. + /// + public int Precision + { + get => serializer.Precision; + set => serializer.Precision = value; + } + + /// + /// Gets or sets the number of decimal places to which Value is resolved. + /// + /// + /// The number of decimal digits that are stored to the right of the decimal point. This number is subtracted from the + /// to determine the maximum number of digits to the left of the decimal point. must be a value from 0 through . + /// The default scale is 0 and so 0 ≤ . + /// + /// + /// Thrown when set to a value that is out of the valid range [0 - ] for this setting. + /// + public int Scale + { + get => serializer.Scale; + set => serializer.Scale = value; + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum number of digits used to represent the value. The default precision is 18. + /// The number of decimal places to which Value is resolved. The default scale is 0. + /// + /// Thrown when is set to a value that is out of the valid range [1 - 38] or when + /// is set to a value that is out of the valid range [0 - ] for this setting. + /// + public SqlNullableNumericSerializer(int precision = DefaultPrecision, int scale = DefaultScale) + { + serializer = new SqlNumericSerializer(precision, scale); + } + + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 17. + /// + public override decimal? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (decimal?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 17. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(decimal? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableRealSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableRealSerializer.cs new file mode 100644 index 0000000000..4d2c4d1e48 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableRealSerializer.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableRealSerializer : Serializer + { + private static readonly SqlRealSerializer serializer = new SqlRealSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Real_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 4. + /// + public override float? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (float?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 4. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(float? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableSmalldatetimeSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableSmalldatetimeSerializer.cs new file mode 100644 index 0000000000..d4a9f06371 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableSmalldatetimeSerializer.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableSmallDateTimeSerializer : Serializer + { + private static readonly SqlSmallDateTimeSerializer serializer = new SqlSmallDateTimeSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_SmallDateTime_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 4. + /// + public override DateTime? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (DateTime?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 4. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(DateTime? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableSmallintSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableSmallintSerializer.cs new file mode 100644 index 0000000000..ce66882295 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableSmallintSerializer.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableSmallIntSerializer : Serializer + { + private static readonly SqlSmallIntSerializer serializer = new SqlSmallIntSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_SmallInt_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override short? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (short?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(short? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableSmallmoneySerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableSmallmoneySerializer.cs new file mode 100644 index 0000000000..bbe658ae88 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableSmallmoneySerializer.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableSmallMoneySerializer : Serializer + { + private static readonly SqlSmallMoneySerializer serializer = new SqlSmallMoneySerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_SmallMoney_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override decimal? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (decimal?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(decimal? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableTimeSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableTimeSerializer.cs new file mode 100644 index 0000000000..e4665adfc0 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableTimeSerializer.cs @@ -0,0 +1,81 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableTimeSerializer : Serializer + { + private const int DefaultScale = 7; + private readonly SqlTimeSerializer serializer; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Time_Nullable"; + + /// + /// Gets or sets the number of digits for the fractional part of the seconds. + /// + /// + /// This can be an integer from 0 to 7. The default fractional scale is 7 (100ns). + /// + /// + /// Thrown when set to a value that is out of the valid range [0 - 7] for this setting. + /// + public int Scale + { + get => serializer.Scale; + set => serializer.Scale = value; + } + + /// + /// Initializes a new instance of the class. + /// + /// The number of digits for the fractional part of the seconds. + /// + /// Thrown when set to a value that is out of the valid range [0 - 7] for this setting. + /// + public SqlNullableTimeSerializer(int scale = DefaultScale) + { + serializer = new SqlTimeSerializer(scale); + } + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 5. + /// + public override TimeSpan? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (TimeSpan?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 5. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(TimeSpan? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableTinyintSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableTinyintSerializer.cs new file mode 100644 index 0000000000..bbcab710d7 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableTinyintSerializer.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableTinyIntSerializer : Serializer + { + private static readonly SqlTinyIntSerializer serializer = new SqlTinyIntSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_TinyInt_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override byte? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (byte?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(byte? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableUniqueidentifierSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableUniqueidentifierSerializer.cs new file mode 100644 index 0000000000..12b5440d38 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNullableUniqueidentifierSerializer.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + internal class SqlNullableUniqueIdentifierSerializer : Serializer + { + private static readonly SqlUniqueIdentifierSerializer serializer = new SqlUniqueIdentifierSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_UniqueIdentifier_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 16. + /// + public override Guid? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (Guid?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 16. + /// + public override byte[] Serialize(Guid? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNumericSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNumericSerializer.cs new file mode 100644 index 0000000000..7f114d7a95 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNumericSerializer.cs @@ -0,0 +1,100 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + + internal sealed class SqlNumericSerializer : Serializer + { + private const int DefaultPrecision = 18; + private const int DefaultScale = 0; + private readonly SqlDecimalSerializer serializer; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Numeric"; + + /// + /// Gets or sets the maximum number of digits used to represent the value. The default precision is 18. + /// + /// + /// The represents the maximum total number of decimal digits to be stored. + /// This number includes both the left and the right sides of the decimal point. + /// The precision must be a value from 1 through the maximum precision of 38. + /// + /// + /// Thrown when set to a value that is out of the valid range [1 - 38] for this setting. + /// + public int Precision + { + get => serializer.Precision; + set => serializer.Precision = value; + } + + /// + /// Gets or sets the number of decimal places to which Value is resolved. + /// + /// + /// The number of decimal digits that are stored to the right of the decimal point. This number is subtracted from the + /// to determine the maximum number of digits to the left of the decimal point. must be a value from 0 through . + /// The default scale is 0 and so 0 ≤ . + /// + /// + /// Thrown when set to a value that is out of the valid range [0 - ] for this setting. + /// + public int Scale + { + get => serializer.Scale; + set => serializer.Scale = value; + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum number of digits used to represent the value. The default precision is 18. + /// The number of decimal places to which Value is resolved. The default scale is 0. + /// + /// Thrown when is set to a value that is out of the valid range [1 - 38] or when + /// is set to a value that is out of the valid range [0 - ] for this setting. + /// + public SqlNumericSerializer(int precision = DefaultPrecision, int scale = DefaultScale) + { + serializer = new SqlDecimalSerializer(precision, scale); + } + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 17. + /// + public override decimal Deserialize(byte[] bytes) => serializer.Deserialize(bytes); + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 17. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(decimal value) => serializer.Serialize(value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNvarcharSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNvarcharSerializer.cs new file mode 100644 index 0000000000..aeab485741 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlNvarcharSerializer.cs @@ -0,0 +1,109 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Linq; + +using static System.Linq.Enumerable; +using static System.Text.Encoding; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + + internal sealed class SqlNVarCharSerializer : Serializer + { + private const int Max = -1; + private const int MinSize = 1; + private const int MaxSize = 4000; + private const int DefaultSize = 30; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_NVarChar_Nullable"; + + private int size; + + /// + /// Gets or sets the maximum length of the data. + /// + /// + /// If not explicitly set, the size is defaulted to 30. + /// + /// + /// Thrown when set to a value that is out of the valid range [-1, 1 - 4000] for this setting. + /// + public int Size + { + get => size; + set + { + if (value != Max && (value < MinSize || value > MaxSize)) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + size = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum length of the data + /// + /// Thrown when set to a value that is out of the valid range [-1, 1 - 4000] for this setting. + /// + public SqlNVarCharSerializer(int size = DefaultSize) + { + Size = size; + } + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + public override string Deserialize(byte[] bytes) + { + return bytes.IsNull() ? null : Unicode.GetString(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// If the value's length is greater than the size, then the value will be truncated on the left to match the size. + /// + /// + /// The serialized data as a byte array. + /// + public override byte[] Serialize(string value) + { + if (value.IsNull()) + { + return null; + } + + if (size != Max) + { + string trimmedValue = TrimToLength(value); + return Unicode.GetBytes(trimmedValue); + } + + return Unicode.GetBytes(value); + } + + private string TrimToLength(string value) => value.Length > Size ? new string(value.Take(Size).ToArray()) : value; + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlRealSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlRealSerializer.cs new file mode 100644 index 0000000000..996257563b --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlRealSerializer.cs @@ -0,0 +1,65 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static System.BitConverter; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + + internal sealed class SqlRealSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Real"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 4. + /// + public override float Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(sizeof(float), nameof(bytes)); + + return ToSingle(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 4. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(float value) + { + if (float.IsInfinity(value) || float.IsNaN(value)) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + return GetBytes(value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlSmalldatetimeSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlSmalldatetimeSerializer.cs new file mode 100644 index 0000000000..69e4d9b763 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlSmalldatetimeSerializer.cs @@ -0,0 +1,80 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Data.SqlTypes; +using System.Linq; + +using static System.BitConverter; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + + internal sealed class SqlSmallDateTimeSerializer : Serializer + { + private static readonly DateTime MinValue = DateTime.Parse("1900-01-01 00:00:00"); + private static readonly DateTime MaxValue = DateTime.Parse("2079-06-06 23:59:59"); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_SmallDateTime"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 4. + /// + public override DateTime Deserialize(byte[] bytes) + { + const int SizeOfDate = 2; + const int SizeOfTime = 2; + const int SizeOfDateTime = SizeOfDate + SizeOfTime; + + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(SizeOfDateTime, nameof(bytes)); + + int dayTicks = ToUInt16(bytes.Take(SizeOfDate).ToArray(), 0); + int timeTicks = ToUInt16(bytes.Skip(SizeOfDate).Take(SizeOfTime).ToArray(), 0) * SqlDateTime.SQLTicksPerMinute; + SqlDateTime sqlDateTime = new SqlDateTime(dayTicks, timeTicks); + return sqlDateTime.Value; + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 4. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(DateTime value) + { + if (value < MinValue || value > MaxValue) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + SqlDateTime sqlDateTime = new SqlDateTime(value.AddSeconds(30)); + byte[] dayTicks = GetBytes((short)sqlDateTime.DayTicks); + byte[] timeTicks = GetBytes((short)(sqlDateTime.TimeTicks / SqlDateTime.SQLTicksPerMinute)); + return dayTicks.Concat(timeTicks).ToArray(); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlSmallintSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlSmallintSerializer.cs new file mode 100644 index 0000000000..ffc7f92920 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlSmallintSerializer.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + + internal sealed class SqlSmallIntSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_SmallInt"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override short Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(sizeof(long), nameof(bytes)); + + return ToInt16(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(short value) => GetBytes((long)value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlSmallmoneySerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlSmallmoneySerializer.cs new file mode 100644 index 0000000000..a368ee1c2d --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlSmallmoneySerializer.cs @@ -0,0 +1,65 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + + internal sealed class SqlSmallMoneySerializer : Serializer + { + private const decimal MinValue = -214748.3648M; + private const decimal MaxValue = 214748.3647M; + private static readonly SqlMoneySerializer sqlMoneySerializer = new SqlMoneySerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_SmallMoney"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override decimal Deserialize(byte[] bytes) + { + return sqlMoneySerializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(decimal value) + { + if (value < MinValue || value > MaxValue) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + return sqlMoneySerializer.Serialize(value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlTimeSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlTimeSerializer.cs new file mode 100644 index 0000000000..a18c433db1 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlTimeSerializer.cs @@ -0,0 +1,126 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Linq; + +using static System.BitConverter; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + + internal sealed class SqlTimeSerializer : Serializer + { + private const int MaxScale = 7; + private const int MinScale = 0; + private const int DefaultScale = 7; + private const int MaxTimeLength = 5; + private static readonly TimeSpan MinValue = TimeSpan.Parse("00:00:00.0000000"); + private static readonly TimeSpan MaxValue = TimeSpan.Parse("23:59:59.9999999"); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_Time"; + + private int scale; + + /// + /// Gets or sets the number of digits for the fractional part of the seconds. + /// + /// + /// This can be an integer from 0 to 7. The default fractional scale is 7 (100ns). + /// + /// + /// Thrown when set to a value that is out of the valid range [0 - 7] for this setting. + /// + public int Scale + { + get => scale; + set + { + if (value < MinScale || value > MaxScale) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + scale = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The number of digits for the fractional part of the seconds. + /// + /// Thrown when set to a value that is out of the valid range [0 - 7] for this setting. + /// + public SqlTimeSerializer(int scale = DefaultScale) + { + Scale = scale; + } + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 5. + /// + public override TimeSpan Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(MaxTimeLength, nameof(bytes)); + + byte[] padding = { 0, 0, 0 }; + byte[] paddedBytes = bytes.Concat(padding).ToArray(); + long timeTicks = ToInt64(paddedBytes, 0); + + return new TimeSpan(timeTicks); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 5. + /// + /// + /// is out of range. + /// + public override byte[] Serialize(TimeSpan value) + { + if (value < MinValue || value > MaxValue) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + long time = value.Ticks / precisionScale[scale] * precisionScale[scale]; + + return GetBytes(time).Take(MaxTimeLength).ToArray(); + } + + internal static readonly long[] precisionScale = { + 10000000, + 1000000, + 100000, + 10000, + 1000, + 100, + 10, + 1, + }; + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlTinyintSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlTinyintSerializer.cs new file mode 100644 index 0000000000..bbaee6df82 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlTinyintSerializer.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + + internal sealed class SqlTinyIntSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_TinyInt"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override byte Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(sizeof(long), nameof(bytes)); + + return bytes[0]; + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(byte value) => GetBytes((long)value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlUniqueidentifierSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlUniqueidentifierSerializer.cs new file mode 100644 index 0000000000..cb04efba0d --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlUniqueidentifierSerializer.cs @@ -0,0 +1,53 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + + internal sealed class SqlUniqueIdentifierSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_UniqueIdentifier"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 16. + /// + public override Guid Deserialize(byte[] bytes) + { + const int SizeOfGuid = 16; + + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateSize(SizeOfGuid, nameof(bytes)); + + return new Guid(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 16. + /// + public override byte[] Serialize(Guid value) => value.ToByteArray(); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlVarbinarySerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlVarbinarySerializer.cs new file mode 100644 index 0000000000..6aa0d11fe0 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlVarbinarySerializer.cs @@ -0,0 +1,102 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Linq; + +using static System.Linq.Enumerable; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing [] type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + + internal sealed class SqlVarBinarySerializer : Serializer + { + private const int Max = -1; + private const int DefaultSize = 30; + private const int MinSize = 1; + private const int MaxSize = 8000; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_VarBinary"; + + private int size; + + /// + /// Gets or sets the maximum length of the data. + /// + /// + /// If not explicitly set, the size is defaulted to 30. + /// + /// + /// Thrown when set to a value that is out of the valid range [-1, 1 - 8000] for this setting. + /// + public int Size + { + get => size; + set + { + if (value != Max && (value < MinSize || value > MaxSize)) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + size = value; + } + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum length of the data + /// + /// Thrown when set to a value that is out of the valid range [-1, 1 - 8000] for this setting. + /// + public SqlVarBinarySerializer(int size = DefaultSize) + { + Size = size; + } + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + public override byte[] Deserialize(byte[] bytes) => bytes; + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// If the value's length is greater than the size, then the value will be truncated on the left to match the size. + /// + /// The serialized data as a byte array. + public override byte[] Serialize(byte[] value) + { + if (value.IsNull()) + { + return null; + } + + if (size != Max) + { + return TrimToLength(value); + } + + return value; + } + + private byte[] TrimToLength(byte[] value) => value.Length > Size ? value.Take(Size).ToArray() : value; + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlVarcharSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlVarcharSerializer.cs new file mode 100644 index 0000000000..7411cece14 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/SqlSerializers/SqlVarcharSerializer.cs @@ -0,0 +1,126 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Linq; +using System.Text; + +using static System.Text.Encoding; +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects + /// that is compatible with the Always Encrypted feature in SQL Server and Azure SQL Database. + /// + + internal sealed class SqlVarCharSerializer : SqlCodePageEncodingSerializer + { + private const int Max = -1; + private const int DefaultSize = 30; + private const int MinSize = 1; + private const int MaxSize = 8000; + + /// + /// The default character encoding Windows-1252. It is also referred to as "ANSI". + /// + private const int DefaultEncodingCodePoint = 1252; + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SQL_VarChar_Nullable"; + + private int size; + + /// + /// Gets or sets the maximum length of the data. + /// + /// + /// If not explicitly set, the size is defaulted to 30. + /// + /// + /// Thrown when set to a value that is out of the valid range [-1, 1 - 8000] for this setting. + /// + public int Size + { + get => size; + set + { + if (value != Max && (value < MinSize || value > MaxSize)) + { + throw new MicrosoftDataEncryptionException(ValueOutOfRange.Format(value)); + } + + size = value; + } + } + + private Encoding characterEncoding; + + /// + /// Gets or sets the character encoding. + /// + /// + /// If not explicitly set, the encoding is defaulted to Windows-1252, which is also referred to as "ANSI". + /// + public int CodePageCharacterEncoding { + get => characterEncoding.CodePage; + set => characterEncoding = GetEncoding(value); + } + + /// + /// Initializes a new instance of the class. + /// + /// The maximum length of the data + /// The code page character encoding. + /// + /// Thrown when set to a value that is out of the valid range [-1, 1 - 8000] for this setting. + /// + public SqlVarCharSerializer(int size = DefaultSize, int codePageCharacterEncoding = DefaultEncodingCodePoint) + { + Size = size; + characterEncoding = GetEncoding(codePageCharacterEncoding); + } + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + public override string Deserialize(byte[] bytes) + { + return bytes.IsNull() ? null : characterEncoding.GetString(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// If the value's length is greater than the size, then the value will be truncated on the left to match the size. + /// + /// The serialized data as a byte array. + public override byte[] Serialize(string value) + { + if (value.IsNull()) + { + return null; + } + + if (Size != Max && value.Length > Size) + { + value = TrimToLength(value); + } + + return characterEncoding.GetBytes(value); + } + + private string TrimToLength(string value) => new string(value.Take(Size).ToArray()); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializerFactory.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializerFactory.cs new file mode 100644 index 0000000000..905749e269 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializerFactory.cs @@ -0,0 +1,133 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Collections.Generic; + +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Provides methods for getting serializer implementations, such as by type and ID. + /// + internal class StandardSerializerFactory : SerializerFactory + { + private readonly Dictionary serializerByType = new Dictionary(); + + private readonly Dictionary serializerByIdentifier = new Dictionary(); + + /// + /// Initializes a new instance of the class. + /// + public StandardSerializerFactory() + { + RegisterDefaultStandardSerializers(); + } + + /// + /// Returns a default instance of the class. + /// + public static StandardSerializerFactory Default { get; } = new StandardSerializerFactory(); + + /// + /// Gets a registered serializer by its Identifier Property. + /// + /// The identifier uniquely identifies a particular Serializer implementation. + /// The ISerializer implementation + public override ISerializer GetSerializer(string identifier) + { + identifier.ValidateNotNull(nameof(identifier)); + + if (serializerByIdentifier.ContainsKey(identifier)) + { + return serializerByIdentifier[identifier]; + } + + return null; + } + + /// + /// Gets a default registered serializer for the type. + /// + /// The data type to be serialized. + /// A default registered serializer for the type. + public override Serializer GetDefaultSerializer() + { + if (serializerByType.ContainsKey(typeof(T))) + { + return (Serializer)serializerByType[typeof(T)]; + } + + throw new MicrosoftDataEncryptionException(DefaultSerializerNotFound.Format(typeof(T).Name, nameof(RegisterSerializer))); + } + + /// + /// Registers a custom serializer. + /// + /// The data type on which the Serializer operates. + /// The Serializer to register. + /// Determines whether to override an existing default serializer for the same type. + public override void RegisterSerializer(Type type, ISerializer serializer, bool overrideDefault = false) + { + type.ValidateNotNull(nameof(type)); + serializer.ValidateNotNull(nameof(serializer)); + + serializerByIdentifier[serializer.Identifier] = serializer; + + if (overrideDefault || !HasDefaultSerializer(type)) + { + serializerByType[type] = serializer; + } + } + + private bool HasDefaultSerializer(Type type) + { + return serializerByType.ContainsKey(type); + } + + private void RegisterDefaultStandardSerializers() + { + RegisterSerializer(typeof(bool), new BooleanSerializer(), overrideDefault: true); + RegisterSerializer(typeof(bool?), new NullableBooleanSerializer(), overrideDefault: true); + RegisterSerializer(typeof(byte), new ByteSerializer(), overrideDefault: true); + RegisterSerializer(typeof(byte?), new NullableByteSerializer(), overrideDefault: true); + RegisterSerializer(typeof(byte[]), new ByteArraySerializer(), overrideDefault: true); + RegisterSerializer(typeof(char), new CharSerializer(), overrideDefault: true); + RegisterSerializer(typeof(char?), new NullableCharSerializer(), overrideDefault: true); + RegisterSerializer(typeof(DateTime), new DateTimeSerializer(), overrideDefault: true); + RegisterSerializer(typeof(DateTime?), new NullableDateTimeSerializer(), overrideDefault: true); + RegisterSerializer(typeof(DateTimeOffset), new DateTimeOffsetSerializer(), overrideDefault: true); + RegisterSerializer(typeof(DateTimeOffset?), new NullableDateTimeOffsetSerializer(), overrideDefault: true); + RegisterSerializer(typeof(decimal), new DecimalSerializer(), overrideDefault: true); + RegisterSerializer(typeof(decimal?), new NullableDecimalSerializer(), overrideDefault: true); + RegisterSerializer(typeof(double), new DoubleSerializer(), overrideDefault: true); + RegisterSerializer(typeof(double?), new NullableDoubleSerializer(), overrideDefault: true); + RegisterSerializer(typeof(float), new SingleSerializer(), overrideDefault: true); + RegisterSerializer(typeof(float?), new NullableSingleSerializer(), overrideDefault: true); + RegisterSerializer(typeof(Guid), new GuidSerializer(), overrideDefault: true); + RegisterSerializer(typeof(Guid?), new NullableGuidSerializer(), overrideDefault: true); + RegisterSerializer(typeof(int), new Int32Serializer(), overrideDefault: true); + RegisterSerializer(typeof(int?), new NullableInt32Serializer(), overrideDefault: true); + RegisterSerializer(typeof(long), new Int64Serializer(), overrideDefault: true); + RegisterSerializer(typeof(long?), new NullableInt64Serializer(), overrideDefault: true); + RegisterSerializer(typeof(sbyte), new SByteSerializer(), overrideDefault: true); + RegisterSerializer(typeof(sbyte?), new NullableSByteSerializer(), overrideDefault: true); + RegisterSerializer(typeof(short), new Int16Serializer(), overrideDefault: true); + RegisterSerializer(typeof(short?), new NullableInt16Serializer(), overrideDefault: true); + RegisterSerializer(typeof(string), new StringSerializer(), overrideDefault: true); + RegisterSerializer(typeof(TimeSpan), new TimeSpanSerializer(), overrideDefault: true); + RegisterSerializer(typeof(TimeSpan?), new NullableTimeSpanSerializer(), overrideDefault: true); + RegisterSerializer(typeof(uint), new UInt32Serializer(), overrideDefault: true); + RegisterSerializer(typeof(uint?), new NullableUInt32Serializer(), overrideDefault: true); + RegisterSerializer(typeof(ulong), new UInt64Serializer(), overrideDefault: true); + RegisterSerializer(typeof(ulong?), new NullableUInt64Serializer(), overrideDefault: true); + RegisterSerializer(typeof(ushort), new UInt16Serializer(), overrideDefault: true); + RegisterSerializer(typeof(ushort?), new NullableUInt16Serializer(), overrideDefault: true); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/BooleanSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/BooleanSerializer.cs new file mode 100644 index 0000000000..1db371cfaf --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/BooleanSerializer.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + internal class BooleanSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Boolean"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 1. + /// + public override bool Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(bool), nameof(bytes)); + + return ToBoolean(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 1. + /// + public override byte[] Serialize(bool value) => GetBytes(value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/ByteArraySerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/ByteArraySerializer.cs new file mode 100644 index 0000000000..4cc7a4a480 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/ByteArraySerializer.cs @@ -0,0 +1,40 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing [] type data objects. + /// + internal class ByteArraySerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "ByteArray"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + public override byte[] Deserialize(byte[] bytes) + { + return bytes; + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// The serialized data as a byte array. + public override byte[] Serialize(byte[] value) + { + return value; + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/ByteSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/ByteSerializer.cs new file mode 100644 index 0000000000..234ea1d228 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/ByteSerializer.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + internal class ByteSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Byte"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 1. + /// + public override byte Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(byte), nameof(bytes)); + + return bytes[0]; + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 1. + /// + public override byte[] Serialize(byte value) => new byte[] { value }; + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/CharSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/CharSerializer.cs new file mode 100644 index 0000000000..bedef9a309 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/CharSerializer.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + internal class CharSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Character"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 2. + /// + public override char Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(char), nameof(bytes)); + + return ToChar(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 2. + /// + public override byte[] Serialize(char value) => GetBytes(value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/DateTimeOffsetSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/DateTimeOffsetSerializer.cs new file mode 100644 index 0000000000..d762c62b56 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/DateTimeOffsetSerializer.cs @@ -0,0 +1,70 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + internal class DateTimeOffsetSerializer : Serializer + { + private static readonly DateTimeSerializer DateTimeSerializer = new DateTimeSerializer(); + private static readonly TimeSpanSerializer TimeSpanSerializer = new TimeSpanSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "DateTimeOffset"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 16. + /// + public override DateTimeOffset Deserialize(byte[] bytes) + { + const int DateTimeIndex = 0; + const int TimeSpanIndex = sizeof(long); + const int MinimumSize = sizeof(long) + sizeof(long); + + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(MinimumSize, nameof(bytes)); + + byte[] dateTimePart = bytes.Skip(DateTimeIndex).Take(sizeof(long)).ToArray(); + byte[] timeSpanPart = bytes.Skip(TimeSpanIndex).Take(sizeof(long)).ToArray(); + + DateTime dateTime = DateTimeSerializer.Deserialize(dateTimePart); + TimeSpan timeSpan = TimeSpanSerializer.Deserialize(timeSpanPart); + + return new DateTimeOffset(dateTime, timeSpan); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 16. + /// + public override byte[] Serialize(DateTimeOffset value) + { + IEnumerable dateTimePart = DateTimeSerializer.Serialize(value.DateTime); + IEnumerable timeSpanPart = TimeSpanSerializer.Serialize(value.Offset); + + return dateTimePart.Concat(timeSpanPart).ToArray(); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/DateTimeSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/DateTimeSerializer.cs new file mode 100644 index 0000000000..3265d6afff --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/DateTimeSerializer.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + internal class DateTimeSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "DateTime"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override DateTime Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(long), nameof(bytes)); + + return DateTime.FromBinary(ToInt64(bytes, 0)); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(DateTime value) => GetBytes(value.ToBinary()); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/DecimalSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/DecimalSerializer.cs new file mode 100644 index 0000000000..ba0de733ae --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/DecimalSerializer.cs @@ -0,0 +1,56 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using System.Linq; + +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + internal class DecimalSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Decimal"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 16. + /// + public override decimal Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(decimal), nameof(bytes)); + + int[] bits = Enumerable.Range(0, 4) + .Select(i => ToInt32(bytes, i * sizeof(int))) + .ToArray(); + + return new decimal(bits); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(decimal value) => decimal.GetBits(value).SelectMany(GetBytes).ToArray(); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/DoubleSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/DoubleSerializer.cs new file mode 100644 index 0000000000..e740237695 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/DoubleSerializer.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + internal class DoubleSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Float64"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override double Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(double), nameof(bytes)); + + return ToDouble(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(double value) => GetBytes(value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/GuidSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/GuidSerializer.cs new file mode 100644 index 0000000000..322ef81924 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/GuidSerializer.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + internal class GuidSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "UniqueIdentifier"; + + private const int SizeOfGuid = 16; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 16. + /// + public override Guid Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(SizeOfGuid, nameof(bytes)); + + return new Guid(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 16. + /// + public override byte[] Serialize(Guid value) => value.ToByteArray(); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/Int16Serializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/Int16Serializer.cs new file mode 100644 index 0000000000..2c85165217 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/Int16Serializer.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + internal class Int16Serializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Int16"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 2. + /// + public override short Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(short), nameof(bytes)); + + return ToInt16(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 2. + /// + public override byte[] Serialize(short value) => GetBytes(value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/Int32Serializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/Int32Serializer.cs new file mode 100644 index 0000000000..2fd33721df --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/Int32Serializer.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + internal class Int32Serializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Int32"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 4. + /// + public override int Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(int), nameof(bytes)); + + return ToInt32(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 4. + /// + public override byte[] Serialize(int value) => GetBytes(value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/Int64Serializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/Int64Serializer.cs new file mode 100644 index 0000000000..956a02148b --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/Int64Serializer.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + internal class Int64Serializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Int64"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override long Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(long), nameof(bytes)); + + return ToInt64(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(long value) => GetBytes(value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableBooleanSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableBooleanSerializer.cs new file mode 100644 index 0000000000..1c7e154fd5 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableBooleanSerializer.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + internal class NullableBooleanSerializer : Serializer + { + private static readonly BooleanSerializer serializer = new BooleanSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Boolean_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 1. + /// + public override bool? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (bool?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 1. + /// + public override byte[] Serialize(bool? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableByteSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableByteSerializer.cs new file mode 100644 index 0000000000..042e8bd9f2 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableByteSerializer.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + internal class NullableByteSerializer : Serializer + { + private static readonly ByteSerializer serializer = new ByteSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Byte_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 1. + /// + public override byte? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (byte?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 1. + /// + public override byte[] Serialize(byte? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableCharSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableCharSerializer.cs new file mode 100644 index 0000000000..8c3452b1a9 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableCharSerializer.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + internal class NullableCharSerializer : Serializer + { + private static readonly CharSerializer serializer = new CharSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Character_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 2. + /// + public override char? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (char?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 2. + /// + public override byte[] Serialize(char? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableDateTImeOffsetSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableDateTImeOffsetSerializer.cs new file mode 100644 index 0000000000..b7cc39016f --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableDateTImeOffsetSerializer.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + internal class NullableDateTimeOffsetSerializer : Serializer + { + private static readonly DateTimeOffsetSerializer serializer = new DateTimeOffsetSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "DateTimeOffset_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 16. + /// + public override DateTimeOffset? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (DateTimeOffset?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 16. + /// + public override byte[] Serialize(DateTimeOffset? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableDateTimeSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableDateTimeSerializer.cs new file mode 100644 index 0000000000..7c25bddc99 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableDateTimeSerializer.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + internal class NullableDateTimeSerializer : Serializer + { + private static readonly DateTimeSerializer serializer = new DateTimeSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "DateTime_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override DateTime? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (DateTime?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(DateTime? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableDecimalSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableDecimalSerializer.cs new file mode 100644 index 0000000000..d664613261 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableDecimalSerializer.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + internal class NullableDecimalSerializer : Serializer + { + private static readonly DecimalSerializer serializer = new DecimalSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Decimal_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 16. + /// + public override decimal? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (decimal?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 16. + /// + public override byte[] Serialize(decimal? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableDoubleSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableDoubleSerializer.cs new file mode 100644 index 0000000000..d0a435ace3 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableDoubleSerializer.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + internal class NullableDoubleSerializer : Serializer + { + private static readonly DoubleSerializer serializer = new DoubleSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Float64_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override double? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (double?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(double? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableGuidSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableGuidSerializer.cs new file mode 100644 index 0000000000..bb6448cc60 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableGuidSerializer.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + internal class NullableGuidSerializer : Serializer + { + private static readonly GuidSerializer serializer = new GuidSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "UniqueIdentifier_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 16. + /// + public override Guid? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (Guid?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 16. + /// + public override byte[] Serialize(Guid? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableInt16Serializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableInt16Serializer.cs new file mode 100644 index 0000000000..a52c67a906 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableInt16Serializer.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + internal class NullableInt16Serializer : Serializer + { + private static readonly Int16Serializer serializer = new Int16Serializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Int16_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 2. + /// + public override short? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (short?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 2. + /// + public override byte[] Serialize(short? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableInt32Serializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableInt32Serializer.cs new file mode 100644 index 0000000000..fd873caac2 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableInt32Serializer.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + internal class NullableInt32Serializer : Serializer + { + private static readonly Int32Serializer serializer = new Int32Serializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Int32_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 4. + /// + public override int? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (int?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 4. + /// + public override byte[] Serialize(int? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableInt64Serializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableInt64Serializer.cs new file mode 100644 index 0000000000..f5c1389c66 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableInt64Serializer.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + internal class NullableInt64Serializer : Serializer + { + private static readonly Int64Serializer serializer = new Int64Serializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Int64_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override long? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (long?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(long? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableSByteSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableSByteSerializer.cs new file mode 100644 index 0000000000..058f585b50 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableSByteSerializer.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + [CLSCompliant(false)] + internal class NullableSByteSerializer : Serializer + { + private static readonly SByteSerializer serializer = new SByteSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SByte_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 1. + /// + public override sbyte? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (sbyte?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 1. + /// + public override byte[] Serialize(sbyte? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableSingleSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableSingleSerializer.cs new file mode 100644 index 0000000000..4c50e20365 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableSingleSerializer.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + internal class NullableSingleSerializer : Serializer + { + private static readonly SingleSerializer serializer = new SingleSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Single_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 4. + /// + public override float? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (float?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 4. + /// + public override byte[] Serialize(float? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableTimeSpanSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableTimeSpanSerializer.cs new file mode 100644 index 0000000000..9103b5ce6a --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableTimeSpanSerializer.cs @@ -0,0 +1,49 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + internal class NullableTimeSpanSerializer : Serializer + { + private static readonly TimeSpanSerializer serializer = new TimeSpanSerializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Time_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override TimeSpan? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (TimeSpan?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(TimeSpan? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableUInt16Serializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableUInt16Serializer.cs new file mode 100644 index 0000000000..28b74244b4 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableUInt16Serializer.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + [CLSCompliant(false)] + internal class NullableUInt16Serializer : Serializer + { + private static readonly UInt16Serializer serializer = new UInt16Serializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "UInt16_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 2. + /// + public override ushort? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (ushort?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 2. + /// + public override byte[] Serialize(ushort? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableUInt32Serializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableUInt32Serializer.cs new file mode 100644 index 0000000000..074db63944 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableUInt32Serializer.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + [CLSCompliant(false)] + internal class NullableUInt32Serializer : Serializer + { + private static readonly UInt32Serializer serializer = new UInt32Serializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "UInt32_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 4. + /// + public override uint? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (uint?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 4. + /// + public override byte[] Serialize(uint? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableUInt64Serializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableUInt64Serializer.cs new file mode 100644 index 0000000000..ebd5fe709c --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/NullableUInt64Serializer.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing ? type data objects. + /// + [CLSCompliant(false)] + internal class NullableUInt64Serializer : Serializer + { + private static readonly UInt64Serializer serializer = new UInt64Serializer(); + + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "UInt64_Nullable"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// The length of is less than 8. + /// + public override ulong? Deserialize(byte[] bytes) + { + return bytes.IsNull() ? (ulong?)null : serializer.Deserialize(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(ulong? value) + { + return value.IsNull() ? null : serializer.Serialize(value.Value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/SByteSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/SByteSerializer.cs new file mode 100644 index 0000000000..14fe6dbf7a --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/SByteSerializer.cs @@ -0,0 +1,50 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + [CLSCompliant(false)] + internal class SByteSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "SByte"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 1. + /// + public override sbyte Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(sbyte), nameof(bytes)); + + return (sbyte)bytes[0]; + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 1. + /// + public override byte[] Serialize(sbyte value) => new byte[] { (byte)value }; + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/SingleSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/SingleSerializer.cs new file mode 100644 index 0000000000..b31d67d122 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/SingleSerializer.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + internal class SingleSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Single"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 4. + /// + public override float Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(float), nameof(bytes)); + + return ToSingle(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 4. + /// + public override byte[] Serialize(float value) => GetBytes(value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/StringSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/StringSerializer.cs new file mode 100644 index 0000000000..7011f33c74 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/StringSerializer.cs @@ -0,0 +1,42 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; +using static System.Text.Encoding; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + internal class StringSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "String"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + public override string Deserialize(byte[] bytes) + { + return bytes.IsNull() ? null : Unicode.GetString(bytes); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + public override byte[] Serialize(string value) + { + return value.IsNull() ? null : Unicode.GetBytes(value); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/TimeSpanSerializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/TimeSpanSerializer.cs new file mode 100644 index 0000000000..265f25dce5 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/TimeSpanSerializer.cs @@ -0,0 +1,51 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + internal class TimeSpanSerializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "Time"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override TimeSpan Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(long), nameof(bytes)); + + return new TimeSpan(ToInt64(bytes, 0)); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(TimeSpan value) => GetBytes(value.Ticks); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/UInt16Serializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/UInt16Serializer.cs new file mode 100644 index 0000000000..d211a5b00b --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/UInt16Serializer.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + [CLSCompliant(false)] + internal class UInt16Serializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "UInt16"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 2. + /// + public override ushort Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(ushort), nameof(bytes)); + + return ToUInt16(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 2. + /// + public override byte[] Serialize(ushort value) => GetBytes(value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/UInt32Serializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/UInt32Serializer.cs new file mode 100644 index 0000000000..850b1a8941 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/UInt32Serializer.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + [CLSCompliant(false)] + internal class UInt32Serializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "UInt32"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 4. + /// + public override uint Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(uint), nameof(bytes)); + + return ToUInt32(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 4. + /// + public override byte[] Serialize(uint value) => GetBytes(value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/UInt64Serializer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/UInt64Serializer.cs new file mode 100644 index 0000000000..0e236be4f4 --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Cryptography/Serializers/StandardSerializers/UInt64Serializer.cs @@ -0,0 +1,52 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System; + +using static System.BitConverter; + +namespace Microsoft.Data.Encryption.Cryptography.Serializers +{ + /// + /// Contains the methods for serializing and deserializing type data objects. + /// + [CLSCompliant(false)] + internal class UInt64Serializer : Serializer + { + /// + /// The uniquely identifies a particular Serializer implementation. + /// + public override string Identifier => "UInt64"; + + /// + /// Deserializes the provided + /// + /// The data to be deserialized + /// The serialized data + /// + /// is null. + /// -or- + /// The length of is less than 8. + /// + public override ulong Deserialize(byte[] bytes) + { + bytes.ValidateNotNull(nameof(bytes)); + bytes.ValidateGreaterThanSize(sizeof(ulong), nameof(bytes)); + + return ToUInt64(bytes, 0); + } + + /// + /// Serializes the provided + /// + /// The value to be serialized + /// + /// An array of bytes with length 8. + /// + public override byte[] Serialize(ulong value) => GetBytes(value); + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Extensions/ArgumentValidationExtensions.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Extensions/ArgumentValidationExtensions.cs new file mode 100644 index 0000000000..2e7e5c843a --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Extensions/ArgumentValidationExtensions.cs @@ -0,0 +1,110 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using Microsoft.Data.Encryption.Cryptography; +using System; +using System.Collections.Generic; +using System.Linq; + +using static Microsoft.Data.Encryption.Resources.Strings; + +namespace Microsoft.Data.Encryption +{ + internal static class ArgumentValidationExtensions + { + internal static bool IsNull(this T parameter) + { + return null == parameter; + } + + internal static void ValidateNotNull(this T parameter, string name) + { + if (parameter.IsNull()) + { + throw new MicrosoftDataEncryptionException(ArgNotNull.FormatInvariant(name)); + } + } + + internal static void ValidateNotNullOrWhitespace(this string parameter, string name) + { + if (string.IsNullOrWhiteSpace(parameter)) + { + throw new MicrosoftDataEncryptionException(ArgNotNullOrWhiteSpace.FormatInvariant(name)); + } + } + + internal static void ValidateNotNullForEach(this IEnumerable parameters, string name) + { + if (parameters.Any(t => t.IsNull())) + { + throw new MicrosoftDataEncryptionException(ArgNotNullForEach.FormatInvariant(name)); + } + } + + internal static void ValidateNotNullOrWhitespaceForEach(this IEnumerable parameters, string name) + { + if (parameters.Any(s => string.IsNullOrWhiteSpace(s))) + { + throw new MicrosoftDataEncryptionException(ArgNotNullOrWhitespaceForEach.FormatInvariant(name)); + } + } + + internal static void ValidateNotEmpty(this IEnumerable parameter, string name) + { + if (!parameter.Any()) + { + throw new MicrosoftDataEncryptionException(ArgNotEmpty.FormatInvariant(name)); + } + } + + internal static void ValidateNotNullOrEmpty(this IEnumerable parameters, string name) + { + parameters.ValidateNotNull(name); + parameters.ValidateNotEmpty(name); + } + + internal static void ValidateGreaterThanSize(this IEnumerable parameter, int size, string name) + { + if (parameter.Count() < size) + { + throw new MicrosoftDataEncryptionException(ArgGreaterThanSize.FormatInvariant(name, size)); + } + } + + internal static void ValidateSize(this IEnumerable parameter, int size, string name) + { + if (parameter.Count() != size) + { + throw new MicrosoftDataEncryptionException(ArgSize.FormatInvariant(name, size)); + } + } + + internal static void ValidateType(this object parameter, Type type, string name) + { + if (!(parameter.GetType().Equals(type))) + { + throw new MicrosoftDataEncryptionException(ArgType.FormatInvariant(name, type)); + } + } + + internal static void ValidatePositive(this int parameter, string name) + { + if (parameter <= 0) + { + throw new MicrosoftDataEncryptionException(ArgPositive.FormatInvariant(name)); + } + } + + internal static void ValidateNotPlaintext(this EncryptionSettings encryptionSettings, string name) + { + if (encryptionSettings.EncryptionType == EncryptionType.Plaintext) + { + throw new MicrosoftDataEncryptionException(EncryptionSettingsCannotBePlaintext.FormatInvariant(name, nameof(EncryptionType))); + } + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Extensions/StringFormattingExtensions.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Extensions/StringFormattingExtensions.cs new file mode 100644 index 0000000000..8c75871bde --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Extensions/StringFormattingExtensions.cs @@ -0,0 +1,24 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +using System.Globalization; + +namespace Microsoft.Data.Encryption +{ + internal static class StringFormattingExtensions + { + internal static string Format(this string format, params object[] args) + { + return string.Format(format, args); + } + + internal static string FormatInvariant(this string format, params object[] args) + { + return string.Format(CultureInfo.InvariantCulture, format, args); + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/FixMdePublicInterfaces.ps1 b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/FixMdePublicInterfaces.ps1 new file mode 100644 index 0000000000..2038ead60b --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/FixMdePublicInterfaces.ps1 @@ -0,0 +1,39 @@ +$copyrightHeaderLine1 = "//------------------------------------------------------------" +$copyrightHeaderLine2 = "// Copyright (c) Microsoft Corporation. All rights reserved." +$copyrightHeaderLine3 = "//------------------------------------------------------------" +$copyrightHeaderLine4 = "" +$headerLine1 = "// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis." +$headerLine2 = "// " +$headerLine3 = "" + +Get-ChildItem -Filter *.cs -File -Recurse | Foreach-Object { + +$currentFileContent = ($_ | Get-Content) + +If (-Not ($currentFileContent | Select-String -Pattern 'Microsoft Corporation. All rights reserved.')) +{ + Write-Output "" + Write-Output "Appending header in file " $_.FullName + Set-Content $_.FullName -value $copyrightHeaderLine1, $copyrightHeaderLine2, $copyrightHeaderLine3, $copyrightHeaderLine4, $headerLine1, $headerLine2, $headerLine3, $currentFileContent + $currentFileContent = ($_ | Get-Content) +} + +If ($currentFileContent | Select-String -Pattern 'public\s*(enum|interface|sealed class|abstract class|static class|class)') +{ + Write-Output "" + Write-Output "- Modifying, marking public classes as internal in file " $_.FullName + + $listToModify = ($currentFileContent | Select-String -Pattern 'public\s*(enum|interface|sealed class|abstract class|static class|class)') -split '\n' + + $listOfChanges = $listToModify -creplace 'public','internal' -split '\n' + + for ($counter=0; $counter -lt $listToModify.Length; $counter++) + { + Write-Output "" + Write-Output "Class modified from " $listToModify[$counter] " to " $listOfChanges[$counter] + $currentFileContent = $currentFileContent -creplace [Regex]::Escape($listToModify[$counter]) , $listOfChanges[$counter] + } + + [IO.File]::WriteAllText($_.FullName, ($currentFileContent -join "`r`n")) +} +} \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Resources/Strings.Designer.cs b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Resources/Strings.Designer.cs new file mode 100644 index 0000000000..0805a3450e --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Resources/Strings.Designer.cs @@ -0,0 +1,493 @@ +//------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +//------------------------------------------------------------ + +// This file isn't generated, but this comment is necessary to exclude it from StyleCop analysis. +// + +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.Data.Encryption.Resources { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Strings { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Strings() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Data.Encryption.Cryptography.Strings", typeof(Strings).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + public static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to {0} must contain at least {1} elements.. + /// + public static string ArgGreaterThanSize { + get { + return ResourceManager.GetString("ArgGreaterThanSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} cannot be empty.. + /// + public static string ArgNotEmpty { + get { + return ResourceManager.GetString("ArgNotEmpty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} cannot be null.. + /// + public static string ArgNotNull { + get { + return ResourceManager.GetString("ArgNotNull", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to One of more of the elements in {0} is null.. + /// + public static string ArgNotNullForEach { + get { + return ResourceManager.GetString("ArgNotNullForEach", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} cannot be null or empty or consist of only whitespace.. + /// + public static string ArgNotNullOrWhiteSpace { + get { + return ResourceManager.GetString("ArgNotNullOrWhiteSpace", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to One or more of the elements in {0} are null or empty or consist of only whitespace.. + /// + public static string ArgNotNullOrWhitespaceForEach { + get { + return ResourceManager.GetString("ArgNotNullOrWhitespaceForEach", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The {0} {1} cannot be Plaintext in this context.. + /// + public static string ArgNotPlainText { + get { + return ResourceManager.GetString("ArgNotPlainText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} must be a positive integer.. + /// + public static string ArgPositive { + get { + return ResourceManager.GetString("ArgPositive", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0} must contain {1} elements.. + /// + public static string ArgSize { + get { + return ResourceManager.GetString("ArgSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Expected {0} to be of type {1}. + /// + public static string ArgType { + get { + return ResourceManager.GetString("ArgType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The key with identifier {0} was not found.. + /// + public static string AzureKeyVaultKeyNotFound { + get { + return ResourceManager.GetString("AzureKeyVaultKeyNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Base type was not found. + /// + public static string BaseTypeNotFound { + get { + return ResourceManager.GetString("BaseTypeNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to CipherText length does not match the RSA key size.. + /// + public static string CipherTextLengthMismatch { + get { + return ResourceManager.GetString("CipherTextLengthMismatch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to {0}.Count does not equal {1}.Count. + /// + public static string ColumnCountNotEqual { + get { + return ResourceManager.GetString("ColumnCountNotEqual", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Empty data encryption key specified.. + /// + public static string DataEncryptionKeyEmpty { + get { + return ResourceManager.GetString("DataEncryptionKeyEmpty", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Data encryption key cannot be null.. + /// + public static string DataEncryptionKeyNull { + get { + return ResourceManager.GetString("DataEncryptionKeyNull", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Decryption failed. The last 10 bytes of the encrypted data encryption key are: '{0}'. The first 10 bytes of ciphertext are: '{1}'. {2}. + /// + public static string DecryptionFailed { + get { + return ResourceManager.GetString("DecryptionFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A default Always Encrypted compatible serializer cannot be found for type {0}. A serializer can be registered for this type with the {1} method.. + /// + public static string DefaultAESerializerNotFound { + get { + return ResourceManager.GetString("DefaultAESerializerNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to A default serializer cannot be found for type {0}. A serializer can be registered for this type with the {1} method.. + /// + public static string DefaultSerializerNotFound { + get { + return ResourceManager.GetString("DefaultSerializerNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Internal error. Empty {0} specified.. + /// + public static string EmptyArgumentInternal { + get { + return ResourceManager.GetString("EmptyArgumentInternal", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Empty data encryption key specified.. + /// + public static string EmptyDataEncryptionKey { + get { + return ResourceManager.GetString("EmptyDataEncryptionKey", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Encryption failed. The last 10 bytes of the encrypted data encryption key are: '{0}'. {1}. + /// + public static string EncryptionFailed { + get { + return ResourceManager.GetString("EncryptionFailed", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The {0} {1} cannot be Plaintext in this context.. + /// + public static string EncryptionSettingsCannotBePlaintext { + get { + return ResourceManager.GetString("EncryptionSettingsCannotBePlaintext", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Signed hash length does not match the RSA key size.. + /// + public static string HashLengthMismatch { + get { + return ResourceManager.GetString("HashLengthMismatch", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid Azure Key Vault key path specified: '{0}'. Valid trusted endpoints: {1}.. + /// + public static string InvalidAkvKeyPathTrustedTemplate { + get { + return ResourceManager.GetString("InvalidAkvKeyPathTrustedTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid Azure Key Vault key path specified: '{0}'.. + /// + public static string InvalidAkvPathTemplate { + get { + return ResourceManager.GetString("InvalidAkvPathTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid url specified: '{0}'.. + /// + public static string InvalidAkvUrlTemplate { + get { + return ResourceManager.GetString("InvalidAkvUrlTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The specified ciphertext's encryption algorithm version '{0}' does not match the expected encryption algorithm version '{1}'.. + /// + public static string InvalidAlgorithmVersion { + get { + return ResourceManager.GetString("InvalidAlgorithmVersion", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specified encrypted data encryption key contains an invalid encryption algorithm version '{0}'. Expected version is '{1}'.. + /// + public static string InvalidAlgorithmVersionTemplate { + get { + return ResourceManager.GetString("InvalidAlgorithmVersionTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specified ciphertext has an invalid authentication tag.. + /// + public static string InvalidAuthenticationTag { + get { + return ResourceManager.GetString("InvalidAuthenticationTag", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The specified encrypted data encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using key encryption key (Azure Key Vault key) in '{2}'. The encrypted data encryption key may be corrupt, or the specified Azure Key Vault key path may be incorrect.. + /// + public static string InvalidCiphertextLengthTemplate { + get { + return ResourceManager.GetString("InvalidCiphertextLengthTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Specified ciphertext has an invalid size of {0} bytes, which is below the minimum {1} bytes required for decryption.. + /// + public static string InvalidCipherTextSize { + get { + return ResourceManager.GetString("InvalidCipherTextSize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The data encryption key has been successfully decrypted but its length: {0} does not match the length: 32 for algorithm 'AEAD_AES_256_CBC_HMAC_SHA256'. Verify the encrypted value of the data encryption key.. + /// + public static string InvalidDataEncryptionKeySize { + get { + return ResourceManager.GetString("InvalidDataEncryptionKeySize", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Encryption type '{0}' specified for the column is either invalid or corrupted. Valid encryption types for algorithm 'AEAD_AES_256_CBC_HMAC_SHA256' are: 'Plaintext', 'Deterministic', 'Randomized'.. + /// + public static string InvalidEncryptionType { + get { + return ResourceManager.GetString("InvalidEncryptionType", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The input is not a valid Hexadecimal string as it contains a non hexadecimal character, is not a multiple of 2 characters, or does not begin with '0x'.. + /// + public static string InvalidHexString { + get { + return ResourceManager.GetString("InvalidHexString", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid key encryption algorithm specified: '{0}'. Expected value: '{1}'.. + /// + public static string InvalidKeyAlgorithm { + get { + return ResourceManager.GetString("InvalidKeyAlgorithm", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid signature of the encrypted data encryption key computed.. + /// + public static string InvalidSignature { + get { + return ResourceManager.GetString("InvalidSignature", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The specified encrypted data encryption key's signature length: {0} does not match the signature length: {1} when using key encryption key (Azure Key Vault key) in '{2}'. The encrypted data encryption key may be corrupt, or the specified Azure Key Vault key path may be incorrect.. + /// + public static string InvalidSignatureLengthTemplate { + get { + return ResourceManager.GetString("InvalidSignatureLengthTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The specified encrypted data encryption key signature does not match the signature computed with the key encryption key (Asymmetric key in Azure Key Vault) in '{0}'. The encrypted data encryption key may be corrupt, or the specified path may be incorrect.. + /// + public static string InvalidSignatureTemplate { + get { + return ResourceManager.GetString("InvalidSignatureTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Invalid trusted endpoint specified: '{0}'; a trusted endpoint must have a value.. + /// + public static string InvalidTrustedEndpointTemplate { + get { + return ResourceManager.GetString("InvalidTrustedEndpointTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Cannot use a non-RSA key: '{0}'.. + /// + public static string NonRsaKeyTemplate { + get { + return ResourceManager.GetString("NonRsaKeyTemplate", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Azure Key Vault key path cannot be null.. + /// + public static string NullAkvPath { + get { + return ResourceManager.GetString("NullAkvPath", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Hash should not be null while decrypting encrypted data encryption key.. + /// + public static string NullHash { + get { + return ResourceManager.GetString("NullHash", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to This reader's registered encryption key store providers does not contain a provider named {0}.. + /// + public static string ReaderKeyStoreProviderNotFound { + get { + return ResourceManager.GetString("ReaderKeyStoreProviderNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to No serializer found for {0}. + /// + public static string SerializerNotFound { + get { + return ResourceManager.GetString("SerializerNotFound", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use default serialization.. + /// + public static string UseDefaultSerialization { + get { + return ResourceManager.GetString("UseDefaultSerialization", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Parameter value {0} is out of range.. + /// + public static string ValueOutOfRange { + get { + return ResourceManager.GetString("ValueOutOfRange", resourceCulture); + } + } + } +} diff --git a/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Resources/Strings.resx b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Resources/Strings.resx new file mode 100644 index 0000000000..6bfe8bbc6c --- /dev/null +++ b/Microsoft.Azure.Cosmos.Encryption/src/MdeSrc/Resources/Strings.resx @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + {0} must contain at least {1} elements. + + + {0} cannot be empty. + + + {0} cannot be null. + + + One of more of the elements in {0} is null. + + + {0} cannot be null or empty or consist of only whitespace. + + + One or more of the elements in {0} are null or empty or consist of only whitespace. + + + The {0} {1} cannot be Plaintext in this context. + + + {0} must be a positive integer. + + + {0} must contain {1} elements. + + + Expected {0} to be of type {1} + + + The key with identifier {0} was not found. + + + Base type was not found + + + CipherText length does not match the RSA key size. + + + {0}.Count does not equal {1}.Count + + + Empty data encryption key specified. + + + Data encryption key cannot be null. + + + Decryption failed. The last 10 bytes of the encrypted data encryption key are: '{0}'. The first 10 bytes of ciphertext are: '{1}'. {2} + + + A default Always Encrypted compatible serializer cannot be found for type {0}. A serializer can be registered for this type with the {1} method. + + + A default serializer cannot be found for type {0}. A serializer can be registered for this type with the {1} method. + + + Internal error. Empty {0} specified. + + + Empty data encryption key specified. + + + Encryption failed. The last 10 bytes of the encrypted data encryption key are: '{0}'. {1} + + + The {0} {1} cannot be Plaintext in this context. + + + Signed hash length does not match the RSA key size. + + + Invalid Azure Key Vault key path specified: '{0}'. Valid trusted endpoints: {1}. + + + Invalid Azure Key Vault key path specified: '{0}'. + + + Invalid url specified: '{0}'. + + + The specified ciphertext's encryption algorithm version '{0}' does not match the expected encryption algorithm version '{1}'. + + + Specified encrypted data encryption key contains an invalid encryption algorithm version '{0}'. Expected version is '{1}'. + + + Specified ciphertext has an invalid authentication tag. + + + The specified encrypted data encryption key's ciphertext length: {0} does not match the ciphertext length: {1} when using key encryption key (Azure Key Vault key) in '{2}'. The encrypted data encryption key may be corrupt, or the specified Azure Key Vault key path may be incorrect. + + + Specified ciphertext has an invalid size of {0} bytes, which is below the minimum {1} bytes required for decryption. + + + The data encryption key has been successfully decrypted but its length: {0} does not match the length: 32 for algorithm 'AEAD_AES_256_CBC_HMAC_SHA256'. Verify the encrypted value of the data encryption key. + + + Encryption type '{0}' specified for the column is either invalid or corrupted. Valid encryption types for algorithm 'AEAD_AES_256_CBC_HMAC_SHA256' are: 'Plaintext', 'Deterministic', 'Randomized'. + + + The input is not a valid Hexadecimal string as it contains a non hexadecimal character, is not a multiple of 2 characters, or does not begin with '0x'. + + + Invalid key encryption algorithm specified: '{0}'. Expected value: '{1}'. + + + Invalid signature of the encrypted data encryption key computed. + + + The specified encrypted data encryption key's signature length: {0} does not match the signature length: {1} when using key encryption key (Azure Key Vault key) in '{2}'. The encrypted data encryption key may be corrupt, or the specified Azure Key Vault key path may be incorrect. + + + The specified encrypted data encryption key signature does not match the signature computed with the key encryption key (Asymmetric key in Azure Key Vault) in '{0}'. The encrypted data encryption key may be corrupt, or the specified path may be incorrect. + + + Invalid trusted endpoint specified: '{0}'; a trusted endpoint must have a value. + + + Cannot use a non-RSA key: '{0}'. + + + Azure Key Vault key path cannot be null. + + + Hash should not be null while decrypting encrypted data encryption key. + + + This reader's registered encryption key store providers does not contain a provider named {0}. + + + No serializer found for {0} + + + Use default serialization. + + + Parameter value {0} is out of range. + + \ No newline at end of file diff --git a/Microsoft.Azure.Cosmos.Encryption/src/Microsoft.Azure.Cosmos.Encryption.csproj b/Microsoft.Azure.Cosmos.Encryption/src/Microsoft.Azure.Cosmos.Encryption.csproj index b0a6d4465a..5e7141f9f9 100644 --- a/Microsoft.Azure.Cosmos.Encryption/src/Microsoft.Azure.Cosmos.Encryption.csproj +++ b/Microsoft.Azure.Cosmos.Encryption/src/Microsoft.Azure.Cosmos.Encryption.csproj @@ -34,9 +34,7 @@ - - - +