@@ -40,6 +40,14 @@ const metadata = {
40
40
}
41
41
} ;
42
42
43
+ const eeMetadata = {
44
+ requires : {
45
+ clientSideEncryption : true ,
46
+ mongodb : '>=6.0.0' ,
47
+ topology : [ 'replicaset' , 'sharded' ]
48
+ }
49
+ } ;
50
+
43
51
// Tests for the ClientEncryption type are not included as part of the YAML tests.
44
52
45
53
// In the prose tests LOCAL_MASTERKEY refers to the following base64:
@@ -1426,6 +1434,291 @@ describe('Client Side Encryption Prose Tests', metadata, function () {
1426
1434
} ) ;
1427
1435
} ) ;
1428
1436
1437
+ context ( '12. Explicit Encryption' , eeMetadata , function ( ) {
1438
+ const data = path . join ( __dirname , '..' , '..' , 'spec' , 'client-side-encryption' , 'etc' , 'data' ) ;
1439
+ let encryptedFields ;
1440
+ let key1Document ;
1441
+ let key1Id ;
1442
+ let setupClient ;
1443
+ let keyVaultClient ;
1444
+ let clientEncryption ;
1445
+ let encryptedClient ;
1446
+
1447
+ beforeEach ( async function ( ) {
1448
+ const mongodbClientEncryption = this . configuration . mongodbClientEncryption ;
1449
+ // Load the file encryptedFields.json as encryptedFields.
1450
+ encryptedFields = EJSON . parse (
1451
+ await fs . promises . readFile ( path . join ( data , 'encryptedFields.json' ) ) ,
1452
+ { relaxed : false }
1453
+ ) ;
1454
+ // Load the file key1-document.json as key1Document.
1455
+ key1Document = EJSON . parse (
1456
+ await fs . promises . readFile ( path . join ( data , 'keys' , 'key1-document.json' ) ) ,
1457
+ { relaxed : false }
1458
+ ) ;
1459
+ // Read the "_id" field of key1Document as key1ID.
1460
+ key1Id = key1Document . _id ;
1461
+ setupClient = this . configuration . newClient ( ) ;
1462
+ // Drop and create the collection db.explicit_encryption using encryptedFields as an option.
1463
+ const db = setupClient . db ( 'db' ) ;
1464
+ await dropCollection ( db , 'explicit_encryption' , { encryptedFields } ) ;
1465
+ await db . createCollection ( 'explicit_encryption' , { encryptedFields } ) ;
1466
+ // Drop and create the collection keyvault.datakeys.
1467
+ const kdb = setupClient . db ( 'keyvault' ) ;
1468
+ await dropCollection ( kdb , 'datakeys' ) ;
1469
+ await kdb . createCollection ( 'datakeys' ) ;
1470
+ // Insert key1Document in keyvault.datakeys with majority write concern.
1471
+ await kdb . collection ( 'datakeys' ) . insertOne ( key1Document , { writeConcern : { w : 'majority' } } ) ;
1472
+ // Create a MongoClient named keyVaultClient.
1473
+ keyVaultClient = this . configuration . newClient ( ) ;
1474
+ // Create a ClientEncryption object named clientEncryption with these options:
1475
+ // ClientEncryptionOpts {
1476
+ // keyVaultClient: <keyVaultClient>;
1477
+ // keyVaultNamespace: "keyvault.datakeys";
1478
+ // kmsProviders: { "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } }
1479
+ // }
1480
+ clientEncryption = new mongodbClientEncryption . ClientEncryption ( keyVaultClient , {
1481
+ keyVaultNamespace : 'keyvault.datakeys' ,
1482
+ kmsProviders : getKmsProviders ( LOCAL_KEY ) ,
1483
+ bson : BSON
1484
+ } ) ;
1485
+ // Create a MongoClient named ``encryptedClient`` with these ``AutoEncryptionOpts``:
1486
+ // AutoEncryptionOpts {
1487
+ // keyVaultNamespace: "keyvault.datakeys";
1488
+ // kmsProviders: { "local": { "key": <base64 decoding of LOCAL_MASTERKEY> } },
1489
+ // bypassQueryAnalysis: true
1490
+ // }
1491
+ encryptedClient = this . configuration . newClient (
1492
+ { } ,
1493
+ {
1494
+ autoEncryption : {
1495
+ bypassQueryAnalysis : true ,
1496
+ keyVaultNamespace : 'keyvault.datakeys' ,
1497
+ kmsProviders : getKmsProviders ( LOCAL_KEY )
1498
+ }
1499
+ }
1500
+ ) ;
1501
+ } ) ;
1502
+
1503
+ afterEach ( async function ( ) {
1504
+ await setupClient . close ( ) ;
1505
+ await keyVaultClient . close ( ) ;
1506
+ await encryptedClient . close ( ) ;
1507
+ } ) ;
1508
+
1509
+ context ( 'Case 1: can insert encrypted indexed and find' , eeMetadata , function ( ) {
1510
+ let insertPayload ;
1511
+ let findPayload ;
1512
+
1513
+ beforeEach ( async function ( ) {
1514
+ // Use clientEncryption to encrypt the value "encrypted indexed value" with these EncryptOpts:
1515
+ // class EncryptOpts {
1516
+ // keyId : <key1ID>
1517
+ // algorithm: "Indexed",
1518
+ // }
1519
+ // Store the result in insertPayload.
1520
+ insertPayload = await clientEncryption . encrypt ( 'encrypted indexed value' , {
1521
+ keyId : key1Id ,
1522
+ algorithm : 'Indexed'
1523
+ } ) ;
1524
+ // Use encryptedClient to insert the document { "encryptedIndexed": <insertPayload> }
1525
+ // into db.explicit_encryption.
1526
+ await encryptedClient . db ( 'db' ) . collection ( 'explicit_encryption' ) . insertOne ( {
1527
+ encryptedIndexed : insertPayload
1528
+ } ) ;
1529
+ // Use clientEncryption to encrypt the value "encrypted indexed value" with these EncryptOpts:
1530
+ // class EncryptOpts {
1531
+ // keyId : <key1ID>
1532
+ // algorithm: "Indexed",
1533
+ // queryType: Equality
1534
+ // }
1535
+ // Store the result in findPayload.
1536
+ findPayload = await clientEncryption . encrypt ( 'encrypted indexed value' , {
1537
+ keyId : key1Id ,
1538
+ algorithm : 'Indexed' ,
1539
+ queryType : 'equality'
1540
+ } ) ;
1541
+ } ) ;
1542
+
1543
+ it ( 'returns the decrypted value' , async function ( ) {
1544
+ // Use encryptedClient to run a "find" operation on the db.explicit_encryption
1545
+ // collection with the filter { "encryptedIndexed": <findPayload> }.
1546
+ // Assert one document is returned containing the field
1547
+ // { "encryptedIndexed": "encrypted indexed value" }.
1548
+ const collection = encryptedClient . db ( 'db' ) . collection ( 'explicit_encryption' ) ;
1549
+ const result = await collection . findOne ( { encryptedIndexed : findPayload } ) ;
1550
+ expect ( result ) . to . have . property ( 'encryptedIndexed' , 'encrypted indexed value' ) ;
1551
+ } ) ;
1552
+ } ) ;
1553
+
1554
+ context (
1555
+ 'Case 2: can insert encrypted indexed and find with non-zero contention' ,
1556
+ eeMetadata ,
1557
+ function ( ) {
1558
+ let findPayload ;
1559
+ let findPayload2 ;
1560
+
1561
+ beforeEach ( async function ( ) {
1562
+ for ( let i = 0 ; i < 10 ; i ++ ) {
1563
+ // Use clientEncryption to encrypt the value "encrypted indexed value" with these EncryptOpts:
1564
+ // class EncryptOpts {
1565
+ // keyId : <key1ID>
1566
+ // algorithm: "Indexed",
1567
+ // contentionFactor: 10
1568
+ // }
1569
+ // Store the result in insertPayload.
1570
+ const insertPayload = await clientEncryption . encrypt ( 'encrypted indexed value' , {
1571
+ keyId : key1Id ,
1572
+ algorithm : 'Indexed' ,
1573
+ contentionFactor : 10
1574
+ } ) ;
1575
+ // Use encryptedClient to insert the document { "encryptedIndexed": <insertPayload> }
1576
+ // into db.explicit_encryption.
1577
+ await encryptedClient . db ( 'db' ) . collection ( 'explicit_encryption' ) . insertOne ( {
1578
+ encryptedIndexed : insertPayload
1579
+ } ) ;
1580
+ // Repeat the above steps 10 times to insert 10 total documents.
1581
+ // The insertPayload must be regenerated each iteration.
1582
+ }
1583
+ // Use clientEncryption to encrypt the value "encrypted indexed value" with these EncryptOpts:
1584
+ // class EncryptOpts {
1585
+ // keyId : <key1ID>
1586
+ // algorithm: "Indexed",
1587
+ // queryType: Equality
1588
+ // }
1589
+ // Store the result in findPayload.
1590
+ findPayload = await clientEncryption . encrypt ( 'encrypted indexed value' , {
1591
+ keyId : key1Id ,
1592
+ algorithm : 'Indexed' ,
1593
+ queryType : 'equality'
1594
+ } ) ;
1595
+ // Use clientEncryption to encrypt the value "encrypted indexed value" with these EncryptOpts:
1596
+ // class EncryptOpts {
1597
+ // keyId : <key1ID>
1598
+ // algorithm: "Indexed",
1599
+ // queryType: Equality,
1600
+ // contentionFactor: 10
1601
+ // }
1602
+ // Store the result in findPayload2.
1603
+ findPayload2 = await clientEncryption . encrypt ( 'encrypted indexed value' , {
1604
+ keyId : key1Id ,
1605
+ algorithm : 'Indexed' ,
1606
+ queryType : 'equality' ,
1607
+ contentionFactor : 10
1608
+ } ) ;
1609
+ } ) ;
1610
+
1611
+ it ( 'returns less than the total documents with no contention' , async function ( ) {
1612
+ // Use encryptedClient to run a "find" operation on the db.explicit_encryption
1613
+ // collection with the filter { "encryptedIndexed": <findPayload> }.
1614
+ // Assert less than 10 documents are returned. 0 documents may be returned.
1615
+ // Assert each returned document contains the field
1616
+ // { "encryptedIndexed": "encrypted indexed value" }.
1617
+ const collection = encryptedClient . db ( 'db' ) . collection ( 'explicit_encryption' ) ;
1618
+ const result = await collection . find ( { encryptedIndexed : findPayload } ) . toArray ( ) ;
1619
+ expect ( result . length ) . to . be . below ( 10 ) ;
1620
+ for ( const doc of result ) {
1621
+ expect ( doc ) . to . have . property ( 'encryptedIndexed' , 'encrypted indexed value' ) ;
1622
+ }
1623
+ } ) ;
1624
+
1625
+ it ( 'returns all documents with contention' , async function ( ) {
1626
+ // Use encryptedClient to run a "find" operation on the db.explicit_encryption
1627
+ // collection with the filter { "encryptedIndexed": <findPayload2> }.
1628
+ // Assert 10 documents are returned. Assert each returned document contains the
1629
+ // field { "encryptedIndexed": "encrypted indexed value" }.
1630
+ const collection = encryptedClient . db ( 'db' ) . collection ( 'explicit_encryption' ) ;
1631
+ const result = await collection . find ( { encryptedIndexed : findPayload2 } ) . toArray ( ) ;
1632
+ expect ( result . length ) . to . equal ( 10 ) ;
1633
+ for ( const doc of result ) {
1634
+ expect ( doc ) . to . have . property ( 'encryptedIndexed' , 'encrypted indexed value' ) ;
1635
+ }
1636
+ } ) ;
1637
+ }
1638
+ ) ;
1639
+
1640
+ context ( 'Case 3: can insert encrypted unindexed' , eeMetadata , function ( ) {
1641
+ let insertPayload ;
1642
+
1643
+ beforeEach ( async function ( ) {
1644
+ // Use clientEncryption to encrypt the value "encrypted unindexed value" with these EncryptOpts:
1645
+ // class EncryptOpts {
1646
+ // keyId : <key1ID>
1647
+ // algorithm: "Unindexed"
1648
+ // }
1649
+ // Store the result in insertPayload.
1650
+ insertPayload = await clientEncryption . encrypt ( 'encrypted unindexed value' , {
1651
+ keyId : key1Id ,
1652
+ algorithm : 'Unindexed'
1653
+ } ) ;
1654
+ // Use encryptedClient to insert the document { "_id": 1, "encryptedUnindexed": <insertPayload> }
1655
+ // into db.explicit_encryption.
1656
+ await encryptedClient . db ( 'db' ) . collection ( 'explicit_encryption' ) . insertOne ( {
1657
+ _id : 1 ,
1658
+ encryptedUnindexed : insertPayload
1659
+ } ) ;
1660
+ } ) ;
1661
+
1662
+ it ( 'returns unindexed documents' , async function ( ) {
1663
+ // Use encryptedClient to run a "find" operation on the db.explicit_encryption
1664
+ // collection with the filter { "_id": 1 }.
1665
+ // Assert one document is returned containing the field
1666
+ // { "encryptedUnindexed": "encrypted unindexed value" }.
1667
+ const collection = encryptedClient . db ( 'db' ) . collection ( 'explicit_encryption' ) ;
1668
+ const result = await collection . findOne ( { _id : 1 } ) ;
1669
+ expect ( result ) . to . have . property ( 'encryptedUnindexed' , 'encrypted unindexed value' ) ;
1670
+ } ) ;
1671
+ } ) ;
1672
+
1673
+ context ( 'Case 4: can roundtrip encrypted indexed' , eeMetadata , function ( ) {
1674
+ let payload ;
1675
+
1676
+ beforeEach ( async function ( ) {
1677
+ // Use clientEncryption to encrypt the value "encrypted indexed value" with these EncryptOpts:
1678
+ // class EncryptOpts {
1679
+ // keyId : <key1ID>
1680
+ // algorithm: "Indexed",
1681
+ // }
1682
+ // Store the result in payload.
1683
+ payload = await clientEncryption . encrypt ( 'encrypted indexed value' , {
1684
+ keyId : key1Id ,
1685
+ algorithm : 'Indexed'
1686
+ } ) ;
1687
+ } ) ;
1688
+
1689
+ it ( 'decrypts the value' , async function ( ) {
1690
+ // Use clientEncryption to decrypt payload. Assert the returned value
1691
+ // equals "encrypted indexed value".
1692
+ const result = await clientEncryption . decrypt ( payload ) ;
1693
+ expect ( result ) . equals ( 'encrypted indexed value' ) ;
1694
+ } ) ;
1695
+ } ) ;
1696
+
1697
+ context ( 'Case 5: can roundtrip encrypted unindexed' , eeMetadata , function ( ) {
1698
+ let payload ;
1699
+
1700
+ beforeEach ( async function ( ) {
1701
+ // Use clientEncryption to encrypt the value "encrypted unindexed value" with these EncryptOpts:
1702
+ // class EncryptOpts {
1703
+ // keyId : <key1ID>
1704
+ // algorithm: "Unindexed",
1705
+ // }
1706
+ // Store the result in payload.
1707
+ payload = await clientEncryption . encrypt ( 'encrypted unindexed value' , {
1708
+ keyId : key1Id ,
1709
+ algorithm : 'Unindexed'
1710
+ } ) ;
1711
+ } ) ;
1712
+
1713
+ it ( 'decrypts the value' , async function ( ) {
1714
+ // Use clientEncryption to decrypt payload. Assert the returned value
1715
+ // equals "encrypted unindexed value".
1716
+ const result = await clientEncryption . decrypt ( payload ) ;
1717
+ expect ( result ) . equals ( 'encrypted unindexed value' ) ;
1718
+ } ) ;
1719
+ } ) ;
1720
+ } ) ;
1721
+
1429
1722
context ( '14. Decryption Events' , metadata , function ( ) {
1430
1723
let setupClient ;
1431
1724
let clientEncryption ;
0 commit comments