Skip to content

Commit 8abce37

Browse files
authored
DGS-19840 Ensure use of DEK object is thread-safe (#268)
* DGS-19840 Ensure use of DEK object is thread-safe * Minor renaming
1 parent 82f3efc commit 8abce37

File tree

3 files changed

+116
-54
lines changed

3 files changed

+116
-54
lines changed

schemaregistry/rules/encryption/dekregistry/dekregistry-client.ts

Lines changed: 60 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ interface DekClient {
5454
registerDek(kekName: string, subject: string, algorithm: string, version: number,
5555
encryptedKeyMaterial?: string): Promise<Dek>;
5656
getDek(kekName: string, subject: string, algorithm: string, version: number, deleted: boolean): Promise<Dek>;
57+
getDekEncryptedKeyMaterialBytes(dek: Dek): Promise<Buffer | null>;
58+
getDekKeyMaterialBytes(dek: Dek): Promise<Buffer | null>;
59+
setDekKeyMaterial(dek: Dek, keyMaterialBytes: Buffer): Promise<void>;
5760
close(): Promise<void>;
5861
}
5962

@@ -90,53 +93,6 @@ class DekRegistryClient implements DekClient {
9093
return new DekRegistryClient(config)
9194
}
9295

93-
static getEncryptedKeyMaterialBytes(dek: Dek): Buffer | null {
94-
if (!dek.encryptedKeyMaterial) {
95-
return null;
96-
}
97-
98-
if (!dek.encryptedKeyMaterialBytes) {
99-
try {
100-
const bytes = Buffer.from(dek.encryptedKeyMaterial, 'base64');
101-
dek.encryptedKeyMaterialBytes = bytes;
102-
} catch (err) {
103-
if (err instanceof Error) {
104-
throw new Error(`Failed to decode base64 string: ${err.message}`);
105-
}
106-
throw new Error(`Unknown error: ${err}`);
107-
}
108-
}
109-
110-
return dek.encryptedKeyMaterialBytes;
111-
}
112-
113-
static getKeyMaterialBytes(dek: Dek): Buffer | null {
114-
if (!dek.keyMaterial) {
115-
return null;
116-
}
117-
118-
if (!dek.keyMaterialBytes) {
119-
try {
120-
const bytes = Buffer.from(dek.keyMaterial, 'base64');
121-
dek.keyMaterialBytes = bytes;
122-
} catch (err) {
123-
if (err instanceof Error) {
124-
throw new Error(`Failed to decode base64 string: ${err.message}`);
125-
}
126-
throw new Error(`Unknown error: ${err}`);
127-
}
128-
}
129-
130-
return dek.keyMaterialBytes;
131-
}
132-
133-
static setKeyMaterial(dek: Dek, keyMaterialBytes: Buffer): void {
134-
if (keyMaterialBytes) {
135-
const str = keyMaterialBytes.toString('base64');
136-
dek.keyMaterial = str;
137-
}
138-
}
139-
14096
config(): ClientConfig {
14197
return this.clientConfig;
14298
}
@@ -238,6 +194,63 @@ class DekRegistryClient implements DekClient {
238194
});
239195
}
240196

197+
async getDekEncryptedKeyMaterialBytes(dek: Dek): Promise<Buffer | null> {
198+
if (!dek.encryptedKeyMaterial) {
199+
return null;
200+
}
201+
202+
if (!dek.encryptedKeyMaterialBytes) {
203+
await this.dekMutex.runExclusive(async () => {
204+
if (!dek.encryptedKeyMaterialBytes) {
205+
try {
206+
const bytes = Buffer.from(dek.encryptedKeyMaterial!, 'base64');
207+
dek.encryptedKeyMaterialBytes = bytes;
208+
} catch (err) {
209+
if (err instanceof Error) {
210+
throw new Error(`Failed to decode base64 string: ${err.message}`);
211+
}
212+
throw new Error(`Unknown error: ${err}`);
213+
}
214+
}
215+
})
216+
}
217+
218+
return dek.encryptedKeyMaterialBytes!;
219+
}
220+
221+
async getDekKeyMaterialBytes(dek: Dek): Promise<Buffer | null> {
222+
if (!dek.keyMaterial) {
223+
return null;
224+
}
225+
226+
if (!dek.keyMaterialBytes) {
227+
await this.dekMutex.runExclusive(async () => {
228+
if (!dek.keyMaterialBytes) {
229+
try {
230+
const bytes = Buffer.from(dek.keyMaterial!, 'base64');
231+
dek.keyMaterialBytes = bytes;
232+
} catch (err) {
233+
if (err instanceof Error) {
234+
throw new Error(`Failed to decode base64 string: ${err.message}`);
235+
}
236+
throw new Error(`Unknown error: ${err}`);
237+
}
238+
}
239+
})
240+
}
241+
242+
return dek.keyMaterialBytes!;
243+
}
244+
245+
async setDekKeyMaterial(dek: Dek, keyMaterialBytes: Buffer): Promise<void> {
246+
await this.dekMutex.runExclusive(async () => {
247+
if (keyMaterialBytes) {
248+
const str = keyMaterialBytes.toString('base64');
249+
dek.keyMaterial = str;
250+
}
251+
})
252+
}
253+
241254
async close(): Promise<void> {
242255
return;
243256
}

schemaregistry/rules/encryption/dekregistry/mock-dekregistry-client.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,53 @@ class MockDekRegistryClient implements DekClient {
9797
throw new RestError(`Dek not found: ${subject}`, 404, 40400);
9898
}
9999

100+
async getDekEncryptedKeyMaterialBytes(dek: Dek): Promise<Buffer | null> {
101+
if (!dek.encryptedKeyMaterial) {
102+
return null;
103+
}
104+
105+
if (!dek.encryptedKeyMaterialBytes) {
106+
try {
107+
const bytes = Buffer.from(dek.encryptedKeyMaterial!, 'base64');
108+
dek.encryptedKeyMaterialBytes = bytes;
109+
} catch (err) {
110+
if (err instanceof Error) {
111+
throw new Error(`Failed to decode base64 string: ${err.message}`);
112+
}
113+
throw new Error(`Unknown error: ${err}`);
114+
}
115+
}
116+
117+
return dek.encryptedKeyMaterialBytes!;
118+
}
119+
120+
async getDekKeyMaterialBytes(dek: Dek): Promise<Buffer | null> {
121+
if (!dek.keyMaterial) {
122+
return null;
123+
}
124+
125+
if (!dek.keyMaterialBytes) {
126+
try {
127+
const bytes = Buffer.from(dek.keyMaterial!, 'base64');
128+
dek.keyMaterialBytes = bytes;
129+
} catch (err) {
130+
if (err instanceof Error) {
131+
throw new Error(`Failed to decode base64 string: ${err.message}`);
132+
}
133+
throw new Error(`Unknown error: ${err}`);
134+
}
135+
}
136+
137+
return dek.keyMaterialBytes!;
138+
}
139+
140+
async setDekKeyMaterial(dek: Dek, keyMaterialBytes: Buffer): Promise<void> {
141+
if (keyMaterialBytes) {
142+
const str = keyMaterialBytes.toString('base64');
143+
dek.keyMaterial = str;
144+
}
145+
}
146+
100147
async close() {
101148
return;
102149
}

schemaregistry/rules/encryption/encrypt-executor.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -406,12 +406,14 @@ export class FieldEncryptionExecutorTransform implements FieldTransform {
406406
}
407407
}
408408

409-
if (DekRegistryClient.getKeyMaterialBytes(dek) == null) {
409+
const keyMaterialBytes = await this.executor.client!.getDekKeyMaterialBytes(dek)
410+
if (keyMaterialBytes == null) {
410411
if (kmsClient == null) {
411412
kmsClient = getKmsClient(this.executor.config!, kek)
412413
}
413-
const rawDek = await kmsClient.decrypt(DekRegistryClient.getEncryptedKeyMaterialBytes(dek)!)
414-
DekRegistryClient.setKeyMaterial(dek, rawDek)
414+
const encryptedKeyMaterialBytes = await this.executor.client!.getDekEncryptedKeyMaterialBytes(dek)
415+
const rawDek = await kmsClient.decrypt(encryptedKeyMaterialBytes!)
416+
await this.executor.client!.setDekKeyMaterial(dek, rawDek)
415417
}
416418

417419
return dek
@@ -478,8 +480,8 @@ export class FieldEncryptionExecutorTransform implements FieldTransform {
478480
version = -1
479481
}
480482
let dek = await this.getOrCreateDek(ctx, version)
481-
let keyMaterialBytes = DekRegistryClient.getKeyMaterialBytes(dek)!
482-
let ciphertext = await this.cryptor.encrypt(keyMaterialBytes, plaintext)
483+
let keyMaterialBytes = await this.executor.client!.getDekKeyMaterialBytes(dek)
484+
let ciphertext = await this.cryptor.encrypt(keyMaterialBytes!, plaintext)
483485
if (this.isDekRotated()) {
484486
ciphertext = this.prefixVersion(dek.version!, ciphertext)
485487
}
@@ -508,8 +510,8 @@ export class FieldEncryptionExecutorTransform implements FieldTransform {
508510
ciphertext = ciphertext.subarray(5)
509511
}
510512
let dek = await this.getOrCreateDek(ctx, version)
511-
let keyMaterialBytes = DekRegistryClient.getKeyMaterialBytes(dek)!
512-
let plaintext = await this.cryptor.decrypt(keyMaterialBytes, ciphertext)
513+
let keyMaterialBytes = await this.executor.client!.getDekKeyMaterialBytes(dek)
514+
let plaintext = await this.cryptor.decrypt(keyMaterialBytes!, ciphertext)
513515
return this.toObject(fieldCtx.type, plaintext)
514516
}
515517
default:

0 commit comments

Comments
 (0)