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