Skip to content

Commit 543a548

Browse files
fix: derivate encryption pemPublicKey from provided pemPrivateKey
1 parent 77b0a42 commit 543a548

File tree

6 files changed

+126
-79
lines changed

6 files changed

+126
-79
lines changed

packages/sdk/src/lib/dataProtectorCore/processProtectedData.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ import {
1515
filterWorkerpoolOrders,
1616
} from '../../utils/processProtectedData.models.js';
1717
import { pushRequesterSecret } from '../../utils/pushRequesterSecret.js';
18-
import { getFormattedKeyPair } from '../../utils/rsa.js';
18+
import {
19+
getPemFormattedKeyPair,
20+
formatPemPublicKeyForSMS,
21+
} from '../../utils/rsa.js';
1922
import {
2023
addressOrEnsSchema,
2124
addressSchema,
@@ -298,18 +301,17 @@ export const processProtectedData = async <
298301
isDone: false,
299302
});
300303
}
301-
const { publicKey, privateKey: generatedPrivateKey } =
302-
await getFormattedKeyPair({
303-
pemPrivateKey: vPemPrivateKey,
304-
});
305-
privateKey = generatedPrivateKey;
304+
const pemKeyPair = await getPemFormattedKeyPair({
305+
pemPrivateKey: vPemPrivateKey,
306+
});
307+
privateKey = pemKeyPair.pemPrivateKey;
306308
// Notify user if a new key was generated
307309
if (!vPemPrivateKey) {
308310
vOnStatusUpdate({
309311
title: 'GENERATE_ENCRYPTION_KEY',
310312
isDone: true,
311313
payload: {
312-
pemPrivateKey: generatedPrivateKey,
314+
pemPrivateKey: pemKeyPair.pemPrivateKey,
313315
},
314316
});
315317
}
@@ -318,19 +320,22 @@ export const processProtectedData = async <
318320
title: 'PUSH_ENCRYPTION_KEY',
319321
isDone: false,
320322
payload: {
321-
publicKey,
323+
pemPublicKey: pemKeyPair.pemPublicKey,
322324
},
323325
});
324326

325-
await iexec.result.pushResultEncryptionKey(publicKey, {
326-
forceUpdate: true,
327-
});
327+
await iexec.result.pushResultEncryptionKey(
328+
formatPemPublicKeyForSMS(pemKeyPair.pemPublicKey),
329+
{
330+
forceUpdate: true,
331+
}
332+
);
328333

329334
vOnStatusUpdate({
330335
title: 'PUSH_ENCRYPTION_KEY',
331336
isDone: true,
332337
payload: {
333-
publicKey,
338+
pemPublicKey: pemKeyPair.pemPublicKey,
334339
},
335340
});
336341
}

packages/sdk/src/lib/dataProtectorSharing/consumeProtectedData.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ import {
77
} from '../../utils/errors.js';
88
import { getEventFromLogs } from '../../utils/getEventFromLogs.js';
99
import { resolveENS } from '../../utils/resolveENS.js';
10-
import { getFormattedKeyPair } from '../../utils/rsa.js';
10+
import {
11+
getPemFormattedKeyPair,
12+
formatPemPublicKeyForSMS,
13+
} from '../../utils/rsa.js';
1114
import {
1215
addressOrEnsSchema,
1316
throwIfMissing,
@@ -42,7 +45,6 @@ export const consumeProtectedData = async ({
4245
path,
4346
workerpool,
4447
maxPrice = DEFAULT_MAX_PRICE,
45-
pemPublicKey,
4648
pemPrivateKey,
4749
onStatusUpdate = () => {},
4850
}: IExecConsumer &
@@ -62,9 +64,6 @@ export const consumeProtectedData = async ({
6264
.label('workerpool')
6365
.default(defaultWorkerpool)
6466
.validateSync(workerpool);
65-
const vPemPublicKey = string()
66-
.label('pemPublicKey')
67-
.validateSync(pemPublicKey);
6867
const vPemPrivateKey = string()
6968
.label('pemPrivateKey')
7069
.validateSync(pemPrivateKey);
@@ -138,21 +137,29 @@ export const consumeProtectedData = async ({
138137
isDone: true,
139138
});
140139

141-
const { publicKey, privateKey } = await getFormattedKeyPair({
142-
pemPublicKey: vPemPublicKey,
140+
const pemKeyPair = await getPemFormattedKeyPair({
143141
pemPrivateKey: vPemPrivateKey,
144142
});
145143

146144
vOnStatusUpdate({
147145
title: 'PUSH_ENCRYPTION_KEY',
148146
isDone: false,
147+
payload: {
148+
pemPublicKey: pemKeyPair.pemPublicKey,
149+
},
149150
});
150-
await iexec.result.pushResultEncryptionKey(publicKey, {
151-
forceUpdate: true,
152-
});
151+
await iexec.result.pushResultEncryptionKey(
152+
formatPemPublicKeyForSMS(pemKeyPair.pemPublicKey),
153+
{
154+
forceUpdate: true,
155+
}
156+
);
153157
vOnStatusUpdate({
154158
title: 'PUSH_ENCRYPTION_KEY',
155159
isDone: true,
160+
payload: {
161+
pemPublicKey: pemKeyPair.pemPublicKey,
162+
},
156163
});
157164

158165
// Make a deal
@@ -218,7 +225,7 @@ export const consumeProtectedData = async ({
218225
iexec,
219226
taskId,
220227
path: vPath,
221-
pemPrivateKey: privateKey,
228+
pemPrivateKey: pemKeyPair.pemPrivateKey,
222229
onStatusUpdate: vOnStatusUpdate,
223230
});
224231

@@ -227,7 +234,7 @@ export const consumeProtectedData = async ({
227234
dealId,
228235
taskId,
229236
result,
230-
pemPrivateKey: privateKey,
237+
pemPrivateKey: pemKeyPair.pemPrivateKey,
231238
};
232239
} catch (e) {
233240
handleIfProtocolError(e);

packages/sdk/src/lib/types/sharingTypes.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ export type ConsumeProtectedDataParams = {
107107
path?: string;
108108
workerpool?: AddressOrENS;
109109
maxPrice?: number;
110-
pemPublicKey?: string;
111110
pemPrivateKey?: string;
112111
onStatusUpdate?: OnStatusUpdateFn<ConsumeProtectedDataStatuses>;
113112
};

packages/sdk/src/utils/indexedDb.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
let db: IDBDatabase;
22

3-
export async function storeKeyPair(publicKey: string, privateKey: CryptoKey) {
3+
export async function storeKeyPair(keyPair: CryptoKeyPair) {
44
if (typeof window === 'undefined' || !('indexedDB' in window)) {
55
return;
66
}
@@ -10,11 +10,8 @@ export async function storeKeyPair(publicKey: string, privateKey: CryptoKey) {
1010
.transaction('keyPair', 'readwrite')
1111
.objectStore('keyPair')
1212
.add({
13-
keyPairName: 'keyPair01',
14-
keyPair: {
15-
publicKey,
16-
privateKey,
17-
},
13+
keyPairName: 'CryptoKeyPair',
14+
keyPair,
1815
});
1916

2017
writeRequest.onerror = (err) => {
@@ -32,7 +29,7 @@ export async function storeKeyPair(publicKey: string, privateKey: CryptoKey) {
3229
export async function getSavedKeyPair(): Promise<
3330
| {
3431
keyPairName: string;
35-
keyPair: { publicKey: string; privateKey: CryptoKey };
32+
keyPair: CryptoKeyPair;
3633
}
3734
| undefined
3835
> {
@@ -51,7 +48,7 @@ export async function getSavedKeyPair(): Promise<
5148
const readRequest = db
5249
.transaction('keyPair')
5350
.objectStore('keyPair')
54-
.get('keyPair01');
51+
.get('CryptoKeyPair');
5552

5653
readRequest.onerror = (err) => {
5754
console.error('[indexedDB] readRequest() ERROR', err);
@@ -75,7 +72,7 @@ export async function getSavedKeyPair(): Promise<
7572
const readKeyPairRequest = db
7673
.transaction('keyPair', 'readonly')
7774
.objectStore('keyPair')
78-
.get('keyPair');
75+
.get('CryptoKeyPair');
7976

8077
readKeyPairRequest.onerror = (event) => {
8178
console.log('[indexedDB] readKeyPairRequest() ERROR', event);

packages/sdk/src/utils/rsa.ts

Lines changed: 79 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,48 @@
11
import { getSavedKeyPair, storeKeyPair } from './indexedDb.js';
22

3-
export async function getFormattedKeyPair({
4-
pemPublicKey,
3+
export async function getPemFormattedKeyPair({
54
pemPrivateKey,
65
}: {
7-
pemPublicKey?: string;
86
pemPrivateKey?: string;
9-
}) {
10-
// Keypair is given, use it
11-
if (pemPublicKey && pemPrivateKey) {
12-
return {
13-
publicKey: toBase64(pemPublicKey),
14-
privateKey: pemPrivateKey,
15-
};
7+
}): Promise<{
8+
pemPublicKey: string;
9+
pemPrivateKey: string;
10+
}> {
11+
if (pemPrivateKey) {
12+
return loadKeyPair(pemPrivateKey);
1613
}
17-
18-
// Get keypair from indexedDB (if available) or generate a new one
1914
return getOrGenerateKeyPair();
2015
}
2116

17+
export function formatPemPublicKeyForSMS(pemPublicKey: string) {
18+
return toBase64(pemPublicKey);
19+
}
20+
2221
export async function getOrGenerateKeyPair() {
23-
let publicKey: string;
24-
let privateKey: CryptoKey;
22+
let keyPair: CryptoKeyPair;
2523
const existingKeyPair = await getSavedKeyPair();
2624
if (existingKeyPair) {
27-
publicKey = existingKeyPair.keyPair.publicKey;
28-
privateKey = existingKeyPair.keyPair.privateKey;
25+
keyPair = existingKeyPair.keyPair;
2926
} else {
30-
const { publicKey: genPublicKey, privateKey: genPrivateKey } =
31-
await generateKeyPair();
32-
publicKey = genPublicKey;
33-
privateKey = genPrivateKey;
34-
35-
await storeKeyPair(publicKey, privateKey);
27+
keyPair = await generateKeyPair();
28+
await storeKeyPair(keyPair);
3629
}
30+
return formatKeyPairAsPem(keyPair);
31+
}
32+
33+
export async function loadKeyPair(pem: string) {
34+
const keyPair = await cryptoKeyPairFromPem(pem);
35+
return formatKeyPairAsPem(keyPair);
36+
}
37+
38+
async function formatKeyPairAsPem(keyPair: CryptoKeyPair) {
3739
return {
38-
publicKey,
39-
privateKey: await privateAsPem(privateKey),
40+
pemPublicKey: await publicAsPem(keyPair.publicKey),
41+
pemPrivateKey: await privateAsPem(keyPair.privateKey),
4042
};
4143
}
4244

4345
export async function generateKeyPair() {
44-
const isExtractable = true;
4546
const { crypto } = await getCrypto();
4647
const keyPair = await crypto.subtle.generateKey(
4748
{
@@ -50,16 +51,10 @@ export async function generateKeyPair() {
5051
publicExponent: new Uint8Array([1, 0, 1]),
5152
hash: 'SHA-256',
5253
},
53-
isExtractable,
54+
true, // extractable
5455
['encrypt', 'decrypt']
5556
);
56-
57-
const formattedPublicKey = await formatPublicKeyForSMS(keyPair.publicKey);
58-
59-
return {
60-
publicKey: formattedPublicKey, // <- PEM key encoded in base64
61-
privateKey: keyPair.privateKey,
62-
};
57+
return keyPair;
6358
}
6459

6560
export async function getCrypto() {
@@ -75,18 +70,11 @@ export async function getCrypto() {
7570
};
7671
}
7772

78-
async function formatPublicKeyForSMS(publicKey: CryptoKey) {
79-
const publicKeyAsPem = await publicAsPem(publicKey);
80-
return toBase64(publicKeyAsPem);
81-
}
82-
8373
async function publicAsPem(publicKey: CryptoKey) {
8474
const { crypto } = await getCrypto();
8575
const publicKeyAsBuffer = await crypto.subtle.exportKey('spki', publicKey);
86-
8776
let body = btoa(String.fromCharCode(...new Uint8Array(publicKeyAsBuffer)));
8877
body = body.match(/.{1,64}/g).join('\n');
89-
9078
return `-----BEGIN PUBLIC KEY-----\n${body}\n-----END PUBLIC KEY-----`;
9179
}
9280

@@ -98,6 +86,57 @@ export async function privateAsPem(privateKey: CryptoKey) {
9886
return `-----BEGIN PRIVATE KEY-----\n${body}\n-----END PRIVATE KEY-----`;
9987
}
10088

89+
export async function cryptoKeyPairFromPem(
90+
pemPrivateKey: string
91+
): Promise<CryptoKeyPair> {
92+
const { crypto } = await getCrypto();
93+
function pemToArrayBuffer(pem: string): ArrayBuffer {
94+
const b64Lines = pem
95+
.replace('-----BEGIN PRIVATE KEY-----', '')
96+
.replace('-----END PRIVATE KEY-----', '')
97+
.replace(/\s/g, '');
98+
const bStr = atob(b64Lines);
99+
const bytes = new Uint8Array(bStr.length);
100+
for (let i = 0; i < bStr.length; i++) {
101+
bytes[i] = bStr.charCodeAt(i);
102+
}
103+
return bytes.buffer;
104+
}
105+
// Import the private key
106+
const privateKey = await crypto.subtle.importKey(
107+
'pkcs8',
108+
pemToArrayBuffer(pemPrivateKey),
109+
{
110+
name: 'RSA-OAEP',
111+
hash: 'SHA-256',
112+
},
113+
true, // extractable
114+
['decrypt']
115+
);
116+
// Transform to JWK to derive the public key
117+
const jwk = await crypto.subtle.exportKey('jwk', privateKey);
118+
// Remove private components to create public JWK
119+
const publicJwk = {
120+
kty: jwk.kty,
121+
n: jwk.n,
122+
e: jwk.e,
123+
alg: jwk.alg,
124+
ext: true,
125+
key_ops: ['encrypt'],
126+
};
127+
const publicKey = await crypto.subtle.importKey(
128+
'jwk',
129+
publicJwk,
130+
{
131+
name: 'RSA-OAEP',
132+
hash: 'SHA-256',
133+
},
134+
true,
135+
['encrypt']
136+
);
137+
return { privateKey, publicKey };
138+
}
139+
101140
function toBase64(publicKeyAsPem: string) {
102141
return btoa(publicKeyAsPem);
103142
}

0 commit comments

Comments
 (0)