public void CorpusTest( [Values(false, true)] bool useLocalSchema, [Values(false, true)] bool async) { RequireServer.Check().Supports(Feature.ClientSideEncryption); var corpusSchema = JsonFileReader.Instance.Documents["corpus.corpus-schema.json"]; var schemaMap = useLocalSchema ? new BsonDocument("db.coll", corpusSchema) : null; using (var client = ConfigureClient()) using (var clientEncrypted = ConfigureClientEncrypted(schemaMap)) using (var clientEncryption = ConfigureClientEncryption(clientEncrypted.Wrapped as MongoClient)) { CreateCollection(client, __collCollectionNamespace, new BsonDocument("$jsonSchema", corpusSchema)); var corpusKeyLocal = JsonFileReader.Instance.Documents["corpus.corpus-key-local.json"]; var corpusKeyAws = JsonFileReader.Instance.Documents["corpus.corpus-key-aws.json"]; var keyVaultCollection = GetCollection(client, __keyVaultCollectionNamespace); Insert(keyVaultCollection, async, corpusKeyLocal, corpusKeyAws); var corpus = JsonFileReader.Instance.Documents["corpus.corpus.json"]; var corpusCopied = new BsonDocument { corpus.GetElement("_id"), corpus.GetElement("altname_aws"), corpus.GetElement("altname_local") }; foreach (var corpusElement in corpus.Elements.Where(c => c.Value.IsBsonDocument)) { var corpusValue = corpusElement.Value.DeepClone(); var kms = corpusValue["kms"].AsString; var abbreviatedAlgorithmName = corpusValue["algo"].AsString; var identifier = corpusValue["identifier"].AsString; var allowed = corpusValue["allowed"].ToBoolean(); var value = corpusValue["value"]; var method = corpusValue["method"].AsString; switch (method) { case "auto": corpusCopied.Add(corpusElement); continue; case "explicit": { var encryptionOptions = CreateEncryptOptions(abbreviatedAlgorithmName, identifier, kms); BsonBinaryData encrypted = null; var exception = Record.Exception(() => { encrypted = ExplicitEncrypt( clientEncryption, encryptionOptions, value, async); }); if (allowed) { exception.Should().BeNull(); encrypted.Should().NotBeNull(); corpusValue["value"] = encrypted; } else { exception.Should().NotBeNull(); } corpusCopied.Add(new BsonElement(corpusElement.Name, corpusValue)); } break; default: throw new ArgumentException($"Unsupported method name {method}.", nameof(method)); } } var coll = GetCollection(clientEncrypted, __collCollectionNamespace); Insert(coll, async, corpusCopied); var corpusDecrypted = Find(coll, new BsonDocument(), async).Single(); corpusDecrypted.Should().Be(corpus); var corpusEncryptedExpected = JsonFileReader.Instance.Documents["corpus.corpus-encrypted.json"]; coll = GetCollection(client, __collCollectionNamespace); var corpusEncryptedActual = Find(coll, new BsonDocument(), async).Single(); foreach (var expectedElement in corpusEncryptedExpected.Elements.Where(c => c.Value.IsBsonDocument)) { var expectedElementValue = expectedElement.Value; var expectedAlgorithm = ParseAlgorithm(expectedElementValue["algo"].AsString); var expectedAllowed = expectedElementValue["allowed"].ToBoolean(); var expectedValue = expectedElementValue["value"]; var actualValue = corpusEncryptedActual.GetValue(expectedElement.Name)["value"]; switch (expectedAlgorithm) { case EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic: actualValue.Should().Be(expectedValue); break; case EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random: if (expectedAllowed) { actualValue.Should().NotBe(expectedValue); } break; default: throw new ArgumentException($"Unsupported expected algorithm {expectedAllowed}.", nameof(expectedAlgorithm)); } if (expectedAllowed) { var actualDecryptedValue = ExplicitDecrypt(clientEncryption, actualValue.AsBsonBinaryData, async); var expectedDecryptedValue = ExplicitDecrypt(clientEncryption, expectedValue.AsBsonBinaryData, async); actualDecryptedValue.Should().Be(expectedDecryptedValue); } else { actualValue.Should().Be(expectedValue); } } } EncryptOptions CreateEncryptOptions(string algorithm, string identifier, string kms) { Guid? keyId = null; string alternateName = null; if (identifier == "id") { switch (kms) { case "local": keyId = GuidConverter.FromBytes(Convert.FromBase64String("LOCALAAAAAAAAAAAAAAAAA=="), GuidRepresentation.Standard); break; case "aws": keyId = GuidConverter.FromBytes(Convert.FromBase64String("AWSAAAAAAAAAAAAAAAAAAA=="), GuidRepresentation.Standard); break; default: throw new ArgumentException($"Unsupported kms type {kms}."); } } else if (identifier == "altname") { alternateName = kms; } else { throw new ArgumentException($"Unsupported identifier {identifier}.", nameof(identifier)); } return(new EncryptOptions(ParseAlgorithm(algorithm).ToString(), alternateName, keyId)); } EncryptionAlgorithm ParseAlgorithm(string algorithm) { switch (algorithm) { case "rand": return(EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Random); case "det": return(EncryptionAlgorithm.AEAD_AES_256_CBC_HMAC_SHA_512_Deterministic); default: throw new ArgumentException($"Unsupported algorithm {algorithm}."); } } }