11import * as jest from 'jest' ;
22import * as jose from 'jose' ;
3- import { webcrypto } from 'crypto' ;
3+ import { hkdf , webcrypto } from 'crypto' ;
44import * as bip39 from '@scure/bip39' ;
55import { wordlist } from '@scure/bip39/wordlists/english' ;
66import * as utils from '@noble/hashes/utils' ;
77import * as nobleEd from '@noble/ed25519' ;
88import * as base64 from 'multiformats/bases/base64' ;
99import * as noblePbkdf2 from '@noble/hashes/pbkdf2' ;
10+ import * as nobleHkdf from '@noble/hashes/hkdf' ;
1011import { sha512 as nobleSha512 } from '@noble/hashes/sha512' ;
12+ import { sha256 as nobleSha256 } from '@noble/hashes/sha256' ;
1113
1214// @ts -ignore - this overrides the random source used by @noble and @scure libraries
1315utils . randomBytes = ( size : number = 32 ) => getRandomBytesSync ( size ) ;
@@ -257,37 +259,6 @@ async function main () {
257259 // Binary representation from text
258260 const jwe = new jose . FlattenedEncrypt ( Buffer . from ( privateKeyJSONstring , 'utf-8' ) ) ;
259261
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-
291262 // JOSE HEADER
292263 jwe . setProtectedHeader ( {
293264 alg : 'PBES2-HS512+A256KW' ,
@@ -305,29 +276,29 @@ async function main () {
305276 // );
306277
307278 // PBES2 Salt Input must be 8 or more octets
279+ // const key = await noblePbkdf2.pbkdf2Async(
280+ // // Using HMAC 512
281+ // nobleSha512,
282+ // Buffer.from('some password'),
283+ // // This is how the salt is "defined"
284+ // // note how we join the alg, then a null character
285+ // // then finally the actual salt being specified
286+ // // It appears to be "saved" in the actual thing
287+ // // Buffer.from(''),
288+ // Buffer.concat([
289+ // Buffer.from('PBES2-HS512+A256KW', 'utf-8'),
290+ // Buffer.from([0]),
291+ // // randomSalt
292+ // ]),
293+ // {
294+ // c: 1000,
295+ // // It is 32 bytes, because we want a A256KW
296+ // dkLen: 64
297+ // }
298+ // );
308299
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 ) ;
300+ // console.log('key length', key.length);
301+ const key = Buffer . from ( 'some password' ) ;
331302
332303 const encryptedJWK = await jwe . encrypt ( key ) ;
333304
@@ -342,59 +313,75 @@ async function main () {
342313
343314 console . log ( decryptedJWK . plaintext . toString ( ) ) ;
344315
316+ // The salt and count are saved in the encryptedJWK
317+ // but it's not encrypted, it's protected via integrity
318+ // these are then used to help decrypt because the program
319+ // knows that it is a PBES2
320+ console . log ( jose . decodeProtectedHeader ( encryptedJWK ) ) ;
321+
322+
323+ // Ok great we have the ed25519 root key
324+ // this is now going to be in `root_priv.json` and `root_pub.json`
325+ // The `root_priv.json` is encrypted with a root password
326+ // And we can proceed...
327+
328+ // We now need to use a DEK key for the database
329+ // This can be randomly generated
330+ // Or derived using HKDF from the root key
331+ // Note that ed25519 keys are not meant for derivation
332+ // It has to be converted to a x25519 key first
333+ // Before HKDF to be used
334+ // However why not just randomly generate the DEK?
335+ // Well the reason is this if it randomly generates the DEK...
336+ // When DEK is lost, you lose the ability to decrypt the database
337+ // If you derive the DEK from the root key, you can always regenerate the DEK
338+ // However there's a another problem...
339+ // If the DEK is separate, then you can always change the root key without chaning the dek
340+
341+
342+ // Let's generate a random DEK first
343+ // It will be 256 bits, as we will be using AES256GCM
344+ // Which is a 32 byte key
345+ const dataEncryptionKey = getRandomBytesSync ( 32 ) ;
346+
347+ const x25519sharedsecret = await nobleEd . getSharedSecret (
348+ rootKeyPair . privateKey ,
349+ rootKeyPair . publicKey
350+ ) ;
345351
352+ // This is 32 bytes
353+ console . log ( x25519sharedsecret ) ;
346354
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
360-
361-
362-
363-
364- // To do this
365- // we can use webcrypto
366- // OR we can use jose
367-
368-
369- // Ok we got the root key pair
370- // Encode as JWK so it can be saved on disk
371- // Encrypt the private key with a root password using a symmetric cipher
372- // To do this, the root password must be hashed with a key derivation function
373- // Which is then used to encrypt the private key JWK file
374- // We can also use 2 files
375- // Or 1 file being JWKS - a set of JWKs
376- // The reason to use 2 files, is that the public key doesn't need encryption
355+ // Now we use hkdf-extract
377356
357+ // The salt also has to be auto generated...
358+ // but i really don't think we need to do this
359+ // It's not some password
360+ // It's the actual derived key from ed25519
361+ // so what's the point of the salt here?
378362
363+ const PRK = nobleHkdf . extract (
364+ nobleSha512 ,
365+ x25519sharedsecret ,
366+ Buffer . from ( 'some password' )
367+ ) ;
379368
380- // Now with the ed25519 master key
381- // We should be able to "derive/generate" subkeys DEKs
382- // These DEKs - the database one in particular should be used
383- // to encrypt the database
384- // The DEKs can be randomly generated, they do not need to be "connected"
385- // to the Master Key
386- // But the DEK when outside the DB, needs to be stored on disk
387- // In particular the DEK used for the DB must be encrypted with the root key
388- // To do this the root key can be directly used for encryption
389- // Converted to X25519 to encrypt
390- // Or use an HKDF over the ed25519 key to do so...
369+ // This is 64 bytes
370+ console . log ( PRK ) ;
391371
372+ // But maybe we should be using JWK here again
373+ // And use Key Wrapping but this time with ed25519
374+ // Well here is the problem
375+ // The DEK is randomly generated
376+ // Now we are trying to encrypt it
377+ // encrypting it rquires a key symmetric key too?
378+ // So encrypted JWK is...?
379+ // Cause the salt is needed And where we saving the salt
380+ // Or not do it aall?
381+ // Let's import it into JWK first
392382
383+ // I think we don't need the salt, simply because no precomputation attack is possible here
393384
394- // The ed25519 key is not intended for encryption
395- // We can "derive" a x25519 key to do the encryption
396- // This avoids using 2 keys
397- // Some places argue to create a second key to do this
398385
399386
400387 // console.log(nobleEd.utils.randomPrivateKey());
0 commit comments