Skip to content

Commit 8c5382a

Browse files
committed
WIP
1 parent 25a4fbc commit 8c5382a

1 file changed

Lines changed: 200 additions & 22 deletions

File tree

test-bootstrapping.ts

Lines changed: 200 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -85,17 +85,19 @@ async function main () {
8585
// This will import into "opaque" keylike objects
8686
// These can be used by jose operations
8787

88-
// If you pass in `d` you must pass in `x`, this gives you a private key
89-
// If you pass only `x`, this gives you a public key JWK
90-
const privateKey = await jose.importJWK({
88+
const privateKeyJSON = {
9189
alg: 'EdDSA',
9290
kty: 'OKP', // Octet key pair
9391
crv: 'Ed25519', // Curve
9492
d: d, // Private key
9593
x: x, // Public key
9694
ext: true, // Extractable (always true in nodejs)
9795
key_ops: ['sign', 'verify'], // Key operations
98-
}) as jose.KeyLike;
96+
};
97+
98+
// If you pass in `d` you must pass in `x`, this gives you a private key
99+
// If you pass only `x`, this gives you a public key JWK
100+
const privateKey = await jose.importJWK(privateKeyJSON) as jose.KeyLike;
99101

100102
const publicKey = await jose.importJWK({
101103
alg: 'EdDSA',
@@ -135,23 +137,6 @@ async function main () {
135137
// we can use the above to maintain information about the JWK
136138
console.log(await jose.exportJWK(publicKey));
137139

138-
const testJWK = {
139-
"kty":"RSA",
140-
"kid":"juliet@capulet.lit",
141-
"use":"enc",
142-
"n":"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRyO125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0XOC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q",
143-
"e":"AQAB",
144-
"d":"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfSNkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9UvqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnuToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsurY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2ahecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ",
145-
"p":"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHfQP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws",
146-
"q":"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6Iedis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYKrYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s",
147-
"dp":"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1wY52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c",
148-
"dq":"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBymXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots",
149-
"qi":"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqqabu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0oYu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8"
150-
};
151-
152-
const testJWKS = JSON.stringify(testJWK);
153-
154-
console.log([...Buffer.from(testJWKS)]);
155140

156141
/*
157142
THIS IS THE JWE HEADER!!
@@ -175,9 +160,203 @@ async function main () {
175160
Wait a minute... what is this?
176161
Oh so this is the key
177162
163+
A CEK is encrypted with PBKDF2.
164+
A CEK is the JWK that we are encrypting.
165+
The CEK in JSON array notation is ...(don't we need to turn it into JSON?)
166+
167+
After we have encrypted it, we create a BASE64URL encoding of the encrypted data.
168+
This gives us another base64 url string...
169+
170+
Also an initialisation vector. Another base64url becomes another string.
171+
172+
Additional data encryption parameter is ASCII(BASE64URL(UTF8(Protected header)))
173+
174+
Then more is done...
175+
176+
The JWE Compact Serialization of
177+
this result, as defined in Section 7.1 of [JWE], is the string
178+
BASE64URL(UTF8(JWE Protected Header)) || '.' || BASE64URL(JWE
179+
Encrypted Key) || '.' || BASE64URL(JWE Initialization Vector) || '.'
180+
|| BASE64URL(JWE Ciphertext) || '.' || BASE64URL(JWE Authentication
181+
Tag).
182+
183+
I see, so it's a concatenation of all of this at the very end.
184+
And this is known as a compact serialisation.
185+
186+
Compact serialisation is the common serialisation. It's all just a compact string?
187+
188+
Flattened is a JSON structure, but flattened? But it's still JSON.
189+
190+
It's not optomised for compactness nor URL safe. Compact representation is something you can send to somewhere.
191+
192+
I think we want the general serialisation. We are going to store it on disk.
193+
But it's also dseigned to be sent to multiple recipients.
194+
195+
We only have 1 recipient, ourselves... so maybe flattened serialisation is better.
196+
197+
JWE compact only has 1 recipient
198+
JWE flattened is only for 1 recipient
199+
200+
Why is the key also encrypted?
201+
I guess cause a symmetric key is being used to enrypt the thing
202+
But the key itself is encrypted
203+
Then the symmetric key is used to decrypt the actual ciphertext
204+
205+
This is different from before...
206+
The assumption was that a root password -> PBKDF2 to key, use key to encrypt
207+
But we don't keep the key around.
208+
209+
Now with JWE, root password -> PBKDF2, gives us a key, that key itself is then used to encrypt the ciphertext.
210+
211+
The content encryption key is a symmetric algorithm.
212+
213+
The alg defines an encryptiong algorithm to encrypt the content encryption key.
214+
215+
It is a key wrapping algorithm which wraps teh CEK.
216+
217+
Wait... are we saying that the CEK is the ciphertext or something else??
218+
219+
jwk+json
220+
221+
Ok so we could have a JWE that has both A256GCM as the encryption of the content, and RSA-OAEP for key wrapping.
222+
Symmetric keys are used to encrypt the content.
223+
Faster than asymmetric.
224+
225+
Ok I understand now. A JWE has to contain both the encrypted symmetric key and the encrypted content with the symmetric key.
226+
227+
This does mean we are doing things slightly differently.
228+
229+
Technically in our case, since the symmetric key is generated from a PBKCDF2, we could just use the password to encrypt the content.
230+
231+
We don't actually even need tokkeep the encrypted symmetric key. But due to the format, it is expected to be there...
232+
233+
We have a JWK now, I want to encrypt this as an encrypted JWK, stored as a JWE.
234+
178235
*/
179236

180237

238+
// const testJWK = {
239+
// "kty":"RSA",
240+
// "kid":"juliet@capulet.lit",
241+
// "use":"enc",
242+
// "n":"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRyO125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0XOC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1_I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q",
243+
// "e":"AQAB",
244+
// "d":"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfSNkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9UvqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnuToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsurY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2ahecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ",
245+
// "p":"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHfQP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws",
246+
// "q":"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6Iedis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYKrYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s",
247+
// "dp":"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1wY52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c",
248+
// "dq":"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBymXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots",
249+
// "qi":"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqqabu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0oYu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8"
250+
// };
251+
// const testJWKS = JSON.stringify(testJWK);
252+
253+
// console.log([...Buffer.from(testJWKS)]);
254+
255+
const privateKeyJSONstring = JSON.stringify(privateKeyJSON);
256+
257+
// Binary representation from text
258+
const jwe = new jose.FlattenedEncrypt(Buffer.from(privateKeyJSONstring, 'utf-8'));
259+
260+
// console.log(jwe);
261+
262+
// Used to determine the CEK?
263+
// What does even mean?
264+
// PBES2 means "password based encryption scheme 2"
265+
266+
// PBES2 is pas word based encryption
267+
// it means using PBKDF2
268+
// HS256 means SHA-256 as the HMAC (used as the pseudo random function)
269+
// A256KW means AES 256 bit Key Wrap (used to actually encrypt)
270+
// The result of encrypting a CEK wiusing the result of the pass word based encryption
271+
// And coresponding key wrapping algoritm
272+
// p2s is the salt input - 8 or more octets, generate random salt for every encryption operation?
273+
// p2c is the count iteration - minimum of 1000 is preferred here
274+
275+
// We could do this directly without going through jose though
276+
277+
// A256GCM is better is used to actually encrypt the data
278+
// Why not use HS512?
279+
// AES256KW is used to encrypt the CEK
280+
281+
// Is the salt meant to be random?
282+
// If so, does the user need to remember this salt?
283+
// Because PBKDF2 will require this salt...
284+
// Plus if we are decrypting it, we need to pass in the salt
285+
286+
// p2s is the salt input - 8 or more octets, generate random salt for every encryption operation?
287+
// It doesn't matter what it is
288+
289+
const randomSalt = getRandomBytesSync(8);
290+
291+
// JOSE HEADER
292+
jwe.setProtectedHeader({
293+
alg: 'PBES2-HS512+A256KW',
294+
enc: 'A256GCM', // symmetric encryption algo
295+
cty: 'jwk+json' // this is a encrypted JWK
296+
});
297+
298+
// These parameters only apply here
299+
// jwe.setKeyManagementParameters(
300+
// {
301+
// p2s: new Uint8Array(),
302+
// // p2s: randomSalt,
303+
// p2c: 1000
304+
// }
305+
// );
306+
307+
// PBES2 Salt Input must be 8 or more octets
308+
309+
const key = await noblePbkdf2.pbkdf2Async(
310+
// Using HMAC 512
311+
nobleSha512,
312+
Buffer.from('some password'),
313+
// This is how the salt is "defined"
314+
// note how we join the alg, then a null character
315+
// then finally the actual salt being specified
316+
// It appears to be "saved" in the actual thing
317+
// Buffer.from(''),
318+
Buffer.concat([
319+
Buffer.from('PBES2-HS512+A256KW', 'utf-8'),
320+
Buffer.from([0]),
321+
// randomSalt
322+
]),
323+
{
324+
c: 1000,
325+
// It is 32 bytes, because we want a A256KW
326+
dkLen: 64
327+
}
328+
);
329+
330+
console.log('key length', key.length);
331+
332+
const encryptedJWK = await jwe.encrypt(key);
333+
334+
console.log('ENCRYPTED JWK', encryptedJWK);
335+
336+
const decryptedJWK = await jose.flattenedDecrypt(
337+
encryptedJWK,
338+
key
339+
);
340+
341+
console.log('DECRYPTED JWK', decryptedJWK);
342+
343+
console.log(decryptedJWK.plaintext.toString());
344+
345+
346+
347+
// // This has to use a key to encrypt
348+
// // which key can we use for this?
349+
// // Do we need to create a CEK to do this?
350+
// const result = await jwe.encrypt(Buffer.from('some password'));
351+
352+
// I think we actually need to create a key here...
353+
// not just anything... how do we use a particular key to do this?
354+
// It's a password
355+
// console.log(result);
356+
357+
358+
359+
// A "cty" (content type) Header Parameter value of "jwk+json" MUST be used to indicate that the content of the JWE is a JWK
181360

182361

183362

@@ -212,7 +391,6 @@ async function main () {
212391

213392

214393

215-
216394
// The ed25519 key is not intended for encryption
217395
// We can "derive" a x25519 key to do the encryption
218396
// This avoids using 2 keys

0 commit comments

Comments
 (0)