@@ -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