Skip to content

Commit 1f64a81

Browse files
committed
WIP
1 parent 8c5382a commit 1f64a81

1 file changed

Lines changed: 86 additions & 99 deletions

File tree

test-bootstrapping.ts

Lines changed: 86 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
import * as jest from 'jest';
22
import * as jose from 'jose';
3-
import { webcrypto } from 'crypto';
3+
import { hkdf, webcrypto } from 'crypto';
44
import * as bip39 from '@scure/bip39';
55
import { wordlist } from '@scure/bip39/wordlists/english';
66
import * as utils from '@noble/hashes/utils';
77
import * as nobleEd from '@noble/ed25519';
88
import * as base64 from 'multiformats/bases/base64';
99
import * as noblePbkdf2 from '@noble/hashes/pbkdf2';
10+
import * as nobleHkdf from '@noble/hashes/hkdf';
1011
import { 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
1315
utils.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

Comments
 (0)