Skip to content

Commit 8e0e916

Browse files
authored
[SDK][Typescript] feat!: encryption module now works with binaries (#2738)
1 parent 20ab6ed commit 8e0e916

File tree

11 files changed

+206
-46
lines changed

11 files changed

+206
-46
lines changed

packages/apps/fortune/exchange-oracle/server/src/modules/job/job.service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,8 @@ export class JobService {
347347
this.pgpConfigService.passphrase,
348348
);
349349

350-
manifest = JSON.parse(await encryption.decrypt(manifestEncrypted));
350+
const decryptedData = await encryption.decrypt(manifestEncrypted);
351+
manifest = JSON.parse(Buffer.from(decryptedData).toString());
351352
} catch {
352353
throw new Error(ErrorJob.ManifestDecryptionFailed);
353354
}

packages/apps/fortune/exchange-oracle/server/src/modules/storage/storage.service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ export class StorageService {
5757
this.pgpConfigService.passphrase,
5858
);
5959

60-
return JSON.parse(await encryption.decrypt(fileContent)) as ISolution[];
60+
const decryptedData = await encryption.decrypt(fileContent);
61+
return JSON.parse(Buffer.from(decryptedData).toString()) as ISolution[];
6162
}
6263

6364
return typeof fileContent == 'string'

packages/apps/fortune/recording-oracle/src/modules/storage/storage.service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ export class StorageService {
5353
this.pgpConfigService.passphrase,
5454
);
5555

56-
return JSON.parse(await encryption.decrypt(fileContent));
56+
const decryptedData = await encryption.decrypt(fileContent);
57+
return JSON.parse(Buffer.from(decryptedData).toString());
5758
} catch {
5859
throw new Error('Unable to decrypt manifest');
5960
}

packages/apps/job-launcher/server/src/modules/job/job.service.spec.ts

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2650,9 +2650,11 @@ describe('JobService', () => {
26502650
expect(storageService.uploadFile).toHaveBeenCalled();
26512651
expect(
26522652
JSON.parse(
2653-
await encryption.decrypt(
2654-
(storageService.uploadFile as any).mock.calls[0][0],
2655-
),
2653+
Buffer.from(
2654+
await encryption.decrypt(
2655+
(storageService.uploadFile as any).mock.calls[0][0],
2656+
),
2657+
).toString(),
26562658
),
26572659
).toEqual(fortuneManifestParams);
26582660
});
@@ -2675,9 +2677,11 @@ describe('JobService', () => {
26752677
expect(storageService.uploadFile).toHaveBeenCalled();
26762678
expect(
26772679
JSON.parse(
2678-
await encryption.decrypt(
2679-
(storageService.uploadFile as any).mock.calls[0][0],
2680-
),
2680+
Buffer.from(
2681+
await encryption.decrypt(
2682+
(storageService.uploadFile as any).mock.calls[0][0],
2683+
),
2684+
).toString(),
26812685
),
26822686
).toEqual(fortuneManifestParams);
26832687
});
@@ -2699,9 +2703,11 @@ describe('JobService', () => {
26992703
expect(storageService.uploadFile).toHaveBeenCalled();
27002704
expect(
27012705
JSON.parse(
2702-
await encryption.decrypt(
2703-
(storageService.uploadFile as any).mock.calls[0][0],
2704-
),
2706+
Buffer.from(
2707+
await encryption.decrypt(
2708+
(storageService.uploadFile as any).mock.calls[0][0],
2709+
),
2710+
).toString(),
27052711
),
27062712
).toEqual(fortuneManifestParams);
27072713
});
@@ -2768,9 +2774,11 @@ describe('JobService', () => {
27682774
expect(storageService.uploadFile).toHaveBeenCalled();
27692775
expect(
27702776
JSON.parse(
2771-
await encryption.decrypt(
2772-
(storageService.uploadFile as any).mock.calls[0][0],
2773-
),
2777+
Buffer.from(
2778+
await encryption.decrypt(
2779+
(storageService.uploadFile as any).mock.calls[0][0],
2780+
),
2781+
).toString(),
27742782
),
27752783
).toEqual(manifest);
27762784
});
@@ -2793,9 +2801,11 @@ describe('JobService', () => {
27932801
expect(storageService.uploadFile).toHaveBeenCalled();
27942802
expect(
27952803
JSON.parse(
2796-
await encryption.decrypt(
2797-
(storageService.uploadFile as any).mock.calls[0][0],
2798-
),
2804+
Buffer.from(
2805+
await encryption.decrypt(
2806+
(storageService.uploadFile as any).mock.calls[0][0],
2807+
),
2808+
).toString(),
27992809
),
28002810
).toEqual(manifest);
28012811
});
@@ -2816,9 +2826,11 @@ describe('JobService', () => {
28162826
expect(storageService.uploadFile).toHaveBeenCalled();
28172827
expect(
28182828
JSON.parse(
2819-
await encryption.decrypt(
2820-
(storageService.uploadFile as any).mock.calls[0][0],
2821-
),
2829+
Buffer.from(
2830+
await encryption.decrypt(
2831+
(storageService.uploadFile as any).mock.calls[0][0],
2832+
),
2833+
).toString(),
28222834
),
28232835
).toEqual(manifest);
28242836
});

packages/apps/job-launcher/server/src/modules/job/job.service.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,7 +1013,9 @@ export class JobService {
10131013

10141014
let manifest = await this.storageService.download(jobEntity.manifestUrl);
10151015
if (typeof manifest === 'string' && isPGPMessage(manifest)) {
1016-
manifest = await this.encryption.decrypt(manifest as any);
1016+
manifest = Buffer.from(
1017+
await this.encryption.decrypt(manifest),
1018+
).toString();
10171019
}
10181020

10191021
if (isValidJSON(manifest)) {
@@ -1496,7 +1498,9 @@ export class JobService {
14961498

14971499
let manifest;
14981500
if (typeof manifestData === 'string' && isPGPMessage(manifestData)) {
1499-
manifestData = await this.encryption.decrypt(manifestData as any);
1501+
manifestData = Buffer.from(
1502+
await this.encryption.decrypt(manifestData),
1503+
).toString();
15001504
}
15011505

15021506
if (isValidJSON(manifestData)) {

packages/apps/job-launcher/server/src/modules/storage/storage.service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ export class StorageService {
4141
typeof fileContent === 'string' &&
4242
EncryptionUtils.isEncrypted(fileContent)
4343
) {
44-
return await this.encryption.decrypt(fileContent);
44+
const decryptedData = await this.encryption.decrypt(fileContent);
45+
return Buffer.from(decryptedData).toString();
4546
} else {
4647
return fileContent;
4748
}

packages/apps/reputation-oracle/server/src/modules/storage/storage.service.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,12 @@ export class StorageService {
8383
this.pgpConfigService.privateKey!,
8484
this.pgpConfigService.passphrase,
8585
);
86+
const decryptedData = await encryption.decrypt(fileContent);
87+
const content = Buffer.from(decryptedData).toString();
8688
try {
87-
return JSON.parse(await encryption.decrypt(fileContent));
89+
return JSON.parse(content);
8890
} catch {
89-
return await encryption.decrypt(fileContent);
91+
return content;
9092
}
9193
} else {
9294
return fileContent;
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { Encryption } from '../src/encryption';
2+
3+
const ExampleKeys = {
4+
private: `-----BEGIN PGP PRIVATE KEY BLOCK-----
5+
6+
xVgEZyOGRhYJKwYBBAHaRw8BAQdA6soD3CBjRnPfsYIxOGObykL8X8y+dZlf
7+
IzAI7mk40E4AAQCPRLKy/1n/QxTRk/Ql+Dt2VYADqDmczBxhWELCbz9Wvw/J
8+
zRxleGFtcGxlIDxzaW1wbGVAZXhhbXBsZS5jb20+wowEEBYKAD4FgmcjhkYE
9+
CwkHCAmQZ4KBmVeekE0DFQgKBBYAAgECGQECmwMCHgEWIQQkzuPtNIUkzz4M
10+
CFhngoGZV56QTQAA89oBAKm5Tai1Ynx4BCU6PSLp0QMEE2ImV/LzwHkLMz52
11+
mzeJAP9NyyNyIMNH1SpSezy309UhEdJVapGoXQwO1eQR4B+LCMddBGcjhkYS
12+
CisGAQQBl1UBBQEBB0BykPL7zxjKQpZMYuEnIDBz7vshngm4zJMNOsE6pDSH
13+
NgMBCAcAAP9b+n3/5QKVd0UP/ow3uyH0X44gc2U4fKV8IhZBJLiSEA9ZwngE
14+
GBYKACoFgmcjhkYJkGeCgZlXnpBNApsMFiEEJM7j7TSFJM8+DAhYZ4KBmVee
15+
kE0AAE0WAP9FE0I9dHToxLAkMKiM9tTzL43GVl6K8Lvn9nrLJcfLgQEAtFXL
16+
39GhAUKNbHMpdeEmxukrdF+rXzUfvOsYGPLIgwI=
17+
=GfVY
18+
-----END PGP PRIVATE KEY BLOCK-----`,
19+
public: `-----BEGIN PGP PUBLIC KEY BLOCK-----
20+
21+
xjMEZyOGRhYJKwYBBAHaRw8BAQdA6soD3CBjRnPfsYIxOGObykL8X8y+dZlf
22+
IzAI7mk40E7NHGV4YW1wbGUgPHNpbXBsZUBleGFtcGxlLmNvbT7CjAQQFgoA
23+
PgWCZyOGRgQLCQcICZBngoGZV56QTQMVCAoEFgACAQIZAQKbAwIeARYhBCTO
24+
4+00hSTPPgwIWGeCgZlXnpBNAADz2gEAqblNqLVifHgEJTo9IunRAwQTYiZX
25+
8vPAeQszPnabN4kA/03LI3Igw0fVKlJ7PLfT1SER0lVqkahdDA7V5BHgH4sI
26+
zjgEZyOGRhIKKwYBBAGXVQEFAQEHQHKQ8vvPGMpClkxi4ScgMHPu+yGeCbjM
27+
kw06wTqkNIc2AwEIB8J4BBgWCgAqBYJnI4ZGCZBngoGZV56QTQKbDBYhBCTO
28+
4+00hSTPPgwIWGeCgZlXnpBNAABNFgD/RRNCPXR06MSwJDCojPbU8y+NxlZe
29+
ivC75/Z6yyXHy4EBALRVy9/RoQFCjWxzKXXhJsbpK3Rfq181H7zrGBjyyIMC
30+
=wH1v
31+
-----END PGP PUBLIC KEY BLOCK-----`,
32+
};
33+
34+
const exampleRound = async () => {
35+
const encryption = await Encryption.build(ExampleKeys.private);
36+
37+
const message = Buffer.from(
38+
JSON.stringify(
39+
{
40+
date: new Date().toISOString(),
41+
random: Math.random(),
42+
text: 'Hello from example!',
43+
},
44+
null,
45+
2
46+
)
47+
);
48+
49+
const encrypted = await encryption.signAndEncrypt(message, [
50+
ExampleKeys.public,
51+
]);
52+
53+
const decrypted = await encryption.decrypt(encrypted, ExampleKeys.public);
54+
console.log('Decrypted', JSON.parse(Buffer.from(decrypted).toString()));
55+
};
56+
57+
(async () => {
58+
await exampleRound();
59+
})();

packages/sdk/typescript/human-protocol-sdk/src/encryption.ts

Lines changed: 46 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
import * as openpgp from 'openpgp';
22
import { IKeyPair } from './interfaces';
33

4+
type MessageDataType = string | Uint8Array;
5+
6+
function makeMessageDataBinary(message: MessageDataType): Uint8Array {
7+
if (typeof message === 'string') {
8+
return Buffer.from(message);
9+
}
10+
11+
return message;
12+
}
13+
414
/**
515
* ## Introduction
616
*
@@ -126,20 +136,21 @@ export class Encryption {
126136
* ```
127137
*/
128138
public async signAndEncrypt(
129-
message: string,
139+
message: MessageDataType,
130140
publicKeys: string[]
131141
): Promise<string> {
132-
const plaintext = message;
133-
134142
const pgpPublicKeys = await Promise.all(
135143
publicKeys.map((armoredKey) => openpgp.readKey({ armoredKey }))
136144
);
137145

138-
const pgpMessage = await openpgp.createMessage({ text: plaintext });
146+
const pgpMessage = await openpgp.createMessage({
147+
binary: makeMessageDataBinary(message),
148+
});
139149
const encrypted = await openpgp.encrypt({
140150
message: pgpMessage,
141151
encryptionKeys: pgpPublicKeys,
142152
signingKeys: this.privateKey,
153+
format: 'armored',
143154
});
144155

145156
return encrypted as string;
@@ -176,23 +187,43 @@ export class Encryption {
176187
* const resultMessage = await encription.decrypt('message');
177188
* ```
178189
*/
179-
public async decrypt(message: string, publicKey?: string): Promise<string> {
180-
const pgpMessage = await openpgp.readMessage({ armoredMessage: message });
190+
public async decrypt(
191+
message: string,
192+
publicKey?: string
193+
): Promise<Uint8Array> {
194+
const pgpMessage = await openpgp.readMessage({
195+
armoredMessage: message,
196+
});
181197

182198
const decryptionOptions: openpgp.DecryptOptions = {
183199
message: pgpMessage,
184200
decryptionKeys: this.privateKey,
185-
expectSigned: !!publicKey,
201+
format: 'binary',
186202
};
187203

188-
if (publicKey) {
204+
const shouldVerifySignature = !!publicKey;
205+
if (shouldVerifySignature) {
189206
const pgpPublicKey = await openpgp.readKey({ armoredKey: publicKey });
190207
decryptionOptions.verificationKeys = pgpPublicKey;
191208
}
192209

193-
const { data: decrypted } = await openpgp.decrypt(decryptionOptions);
210+
const { data: decrypted, signatures } =
211+
await openpgp.decrypt(decryptionOptions);
212+
213+
/**
214+
* There is an option to automatically verify signatures - `expectSigned`,
215+
* but atm it has a bug - https://github.com/openpgpjs/openpgpjs/issues/1803,
216+
* so we have to verify it manually till it's fixed.
217+
*/
218+
try {
219+
if (shouldVerifySignature) {
220+
await signatures[0].verified;
221+
}
222+
} catch {
223+
throw new Error('Signature could not be verified');
224+
}
194225

195-
return decrypted as string;
226+
return decrypted as Uint8Array;
196227
}
197228

198229
/**
@@ -419,19 +450,20 @@ export class EncryptionUtils {
419450
* ```
420451
*/
421452
public static async encrypt(
422-
message: string,
453+
message: MessageDataType,
423454
publicKeys: string[]
424455
): Promise<string> {
425-
const plaintext = message;
426-
427456
const pgpPublicKeys = await Promise.all(
428457
publicKeys.map((armoredKey) => openpgp.readKey({ armoredKey }))
429458
);
430459

431-
const pgpMessage = await openpgp.createMessage({ text: plaintext });
460+
const pgpMessage = await openpgp.createMessage({
461+
binary: makeMessageDataBinary(message),
462+
});
432463
const encrypted = await openpgp.encrypt({
433464
message: pgpMessage,
434465
encryptionKeys: pgpPublicKeys,
466+
format: 'armored',
435467
});
436468

437469
return encrypted as string;

0 commit comments

Comments
 (0)