Merge pull request #21589 from overleaf/jpa-s3-ssec-hkdf

[object-persistor] s3SSEC: add HKDF layer to KEK

GitOrigin-RevId: 1def9e378e1dfd90f449ad392b0db2101584e17f
This commit is contained in:
Jakob Ackermann
2024-11-08 09:06:46 +00:00
committed by Copybot
parent bec73ddfae
commit a67560c26b
4 changed files with 109 additions and 28 deletions
@@ -39,7 +39,10 @@ const {
NotImplementedError,
NoKEKMatchedError,
} = require('@overleaf/object-persistor/src/Errors')
const PerProjectEncryptedS3Persistor = require('@overleaf/object-persistor/src/PerProjectEncryptedS3Persistor')
const {
PerProjectEncryptedS3Persistor,
RootKeyEncryptionKey,
} = require('@overleaf/object-persistor/src/PerProjectEncryptedS3Persistor')
const crypto = require('crypto')
describe('Filestore', function () {
@@ -1118,33 +1121,39 @@ describe('Filestore', function () {
})
describe('kek rotation', function () {
const newKEK = crypto.generateKeySync('aes', { length: 256 }).export()
const oldKEK = crypto.generateKeySync('aes', { length: 256 }).export()
const newKEK = new RootKeyEncryptionKey(
crypto.generateKeySync('aes', { length: 256 }).export(),
Buffer.alloc(32)
)
const oldKEK = new RootKeyEncryptionKey(
crypto.generateKeySync('aes', { length: 256 }).export(),
Buffer.alloc(32)
)
const migrationStep0 = new PerProjectEncryptedS3Persistor({
...s3SSECConfig(),
automaticallyRotateDEKEncryption: false,
async getKeyEncryptionKeys() {
async getRootKeyEncryptionKeys() {
return [oldKEK] // only old key
},
})
const migrationStep1 = new PerProjectEncryptedS3Persistor({
...s3SSECConfig(),
automaticallyRotateDEKEncryption: false,
async getKeyEncryptionKeys() {
async getRootKeyEncryptionKeys() {
return [oldKEK, newKEK] // new key as fallback
},
})
const migrationStep2 = new PerProjectEncryptedS3Persistor({
...s3SSECConfig(),
automaticallyRotateDEKEncryption: true, // <- different compared to partiallyRotated
async getKeyEncryptionKeys() {
async getRootKeyEncryptionKeys() {
return [newKEK, oldKEK] // old keys as fallback
},
})
const migrationStep3 = new PerProjectEncryptedS3Persistor({
...s3SSECConfig(),
automaticallyRotateDEKEncryption: true,
async getKeyEncryptionKeys() {
async getRootKeyEncryptionKeys() {
return [newKEK] // only new key
},
})
@@ -2,6 +2,9 @@ const fs = require('fs')
const Path = require('path')
const crypto = require('crypto')
const https = require('https')
const {
RootKeyEncryptionKey,
} = require('@overleaf/object-persistor/src/PerProjectEncryptedS3Persistor')
// use functions to get a fresh copy, not a reference, each time
function s3BaseConfig() {
@@ -26,15 +29,21 @@ function s3Config() {
}
}
const S3SSECKeys = [crypto.generateKeySync('aes', { length: 256 }).export()]
const S3SSECKeys = [
new RootKeyEncryptionKey(
crypto.generateKeySync('aes', { length: 256 }).export(),
Buffer.alloc(32)
),
]
function s3SSECConfig() {
return {
...s3Config(),
ignoreErrorsFromDEKReEncryption: false,
automaticallyRotateDEKEncryption: true,
pathIsProjectFolder(_bucketName, path) {
return !!path.match(/^[a-f0-9]+\/$/)
pathToProjectFolder(_bucketName, path) {
const [projectFolder] = path.match(/^[a-f0-9]+\//)
return projectFolder
},
pathToDataEncryptionKeyPath(_bucketName, path) {
const [projectFolder] = path.match(/^[a-f0-9]+\//)
@@ -43,7 +52,7 @@ function s3SSECConfig() {
path: Path.join(projectFolder, 'dek'),
}
},
async getKeyEncryptionKeys() {
async getRootKeyEncryptionKeys() {
return S3SSECKeys
},
}