0

using more then one async() in a chain in the function breaks my function. Is there a way i can include Key2pkcs8() inside generateKey() ?

async function generateKey() {
  let getKeyPair = await crypto.subtle.generateKey(
    {
      name: "ECDH",
      namedCurve: "P-384"
    },
    false,
    ["deriveKey"]
  );

  let PriKey = async() => {
    let PriKey = await getKeyPair.privateKey;
    console.log("pri = " + PriKey);
    return PriKey;
  };
  let PubKey = async() => {
    let PubKey = await getKeyPair.publicKey;
    console.log("pub = " + PubKey);
  };

  let Key2pkcs8 = async(PriKey, PubKey) => {
    let Key2pkcs8Pub = await crypto.subtle.exportKey("pkcs8", PubKey);
    let Key2pkcs8Pri = await crypto.subtle.exportKey("pkcs8", PriKey);
    return pkcs8keyarray = [Key2pkcs8Pub, Key2pkcs8Pri];
  
  return Keyarray = [PriKey(), PubKey()];  // i want to put <return pkcs8keyarray()> here  
};

generateKey().then(Key2pkcs8 => console.log(Key2pkcs8[0], Key2pkcs8[1])); works as expected and returns pri = [object CryptoKey] Promise { <state>: "fulfilled", <value>: undefined } Promise { <state>: "fulfilled", <value>: CryptoKey }

but when using return pkcs8keyarray() instead of return Keyarray = [PriKey(), PubKey()]; it breaks and returns undefined

i had intended to have key2pkcs2 take a key as a variable (public or private key) and then return both in a array at the end similar to the example

cubesareneat
  • 302
  • 2
  • 14
  • 1
    `PriKey` and `PubKey` don't have an explicit `return` so they only produce a promise that resolves to `undefined`. Also, since it's a promise, you should be `await`-ing the result (or using `.then()`) – VLAZ Nov 26 '21 at 09:25

2 Answers2

2

Your program demonstrates a misunderstanding of promises, async/await, the crypto module, and javascript as a whole.

  • Use let only when you want to reassign values to a binding
  • Don't reassign functions to values, especially where it's easily avoidable
  • Statements like return Keyarray = ... are leaking global variables and do not behave like you are probably expecting
  • You do not need to create a new async function every time you want to await another asynchronous value
  • You cannot simply console.log the private or public keys. Per the exportKey docs, it returns a promise that resolves to an ArrayBuffer which is raw byte data and does not have a string representation.
async function generateKey() {
  const {privateKey, publicKey} =        // <- get privateKey, publicKey
    await crypto.subtle.generateKey(
      {
        name: "ECDH",
        namedCurve: "P-384"
      },
      true,
      ["deriveKey"]
    )

  return [
    await crypto.subtle.exportKey("pkcs8", privateKey), // <- export private
    await crypto.subtle.exportKey("pkcs8", publicKey),  // <- export public
  ]
}

Since generateKey is returning an array pair of [private, public], we can easily use these in the other async functions you write -

async function myfunction() {
  const [private, public] = await generateKey() // <- resolves pair
  // do something
}

Move all side effects downstream in .then handlers. Caller is responsible for catching errors. Always handle errors -

myfunction().then(console.log).catch(console.error)

Do not try to implement cryptographic solutions if you do not understand these simple things. You will 100% get something wrong and you will introduce a vulnerability and you and your users will suffer the consequences.

Code in this answer is unttested and only here to highlight the mistakes I can readily see. Do not use the code verbatim and do not expect it to copy/paste it directly into your project. I do not understand what your requirements or intentions are and therefore cannot possibly make recommendations or offer other advice.

For more info on misuse of async and await, see this related Q&A.

Mulan
  • 129,518
  • 31
  • 228
  • 259
  • 2
    "*an overwhelming misunderstanding of javascript as a whole*" - even if true, you might want to dial down your language there a bit. Also I'd put the disclaimer in the footer of the answer, not at the top – Bergi Nov 26 '21 at 20:37
  • 1
    thanks for the feedback, i adjusted the post. @cubesareneat the return value maybe what you are expecting more than likely you did not intend to leak a global variable. this kind of mistake could turn a secure program into a vulnerable one. if you are seeing `undefined` in your console, then you do not have an arraybuffer. i'm happy to help an i sincerely mean no disrespect. if you have additional questions, feel free to ask – Mulan Nov 26 '21 at 21:58
  • it seems anytime await ```crypto.subtle.exportKey("pkcs8"``` is used more then once I get ```Uncaught (in promise) DOMException: Operation is not supported``` when trying to consol.log either returned value in myfunction... But if it only returns only one exported pkcs8 key, and the other value without the exportkey() operation it works as expected and logs the array buffer... it will even work and returns 2 array buffers if one of the exportkeys formats is ```crypto.subtle.exportKey("raw"``` – cubesareneat Nov 26 '21 at 22:09
  • https://pastebin.com/Vcpz0msC this is what i mean, but i want them both in the same format raw or pkcs8 @mulan i really appreciate it, you're not wrong i have a long way to go with promise and probably should have started with something other then this library – cubesareneat Nov 26 '21 at 22:15
  • 1
    @cubesareneat I did notice that if you `console.log(privateKey, publicKey)` that only the private key has `usages: ["deriveKey"]` whereas public key has `usages: []`. i'm not familiar enough with the ECDH algorithm, web-based `crypto` module, or your intended usage to offer fixes for that. – Mulan Nov 26 '21 at 22:18
  • 1
    ill go back to the docs you have given me a great jumping off point – cubesareneat Nov 26 '21 at 22:21
0
async function generateKey() {
  let keyPair = await crypto.subtle.generateKey(
    {
      name: "ECDH",
      namedCurve: "P-384"
    },
    false,
    ["deriveKey"]
  );

  let PriKey = (keyPair) => {
    let PriKey = keyPair.privateKey;
    console.log("pri = " + PriKey);
    return keyPair;
  };
  let PubKey = (keyPair) => {
    let PubKey = keyPair.publicKey;
    console.log("pub = " + PubKey);
    return keyPair;
  };

  let Key2pkcs8 = async(keyPair) => {
    console.log("key = " + keyPair);
    let Key2pkcs8 = await crypto.subtle.exportKey("pkcs8", keyPair);
    return Key2pkcs8;
  };

  let printme = async() => {
    let printme = await Key2pkcs8(PubKey());
    console.log(printme);
    return printme;
  };

  return printme();
}

Usage:

generateKey().then(Key2pkcs8 => console.log(Key2pkcs8 ));

If your outer iife is async, await will already convert async flow to sync, meaning getKeyPair (or keyPair in my snippet) will already be available by the time you call other functions.

PubKey and PriKey need not to be async, and in both cases you are not returning anything. Key2pkcs8 is not returning anything either, so what exactly do you want await Key2pkcs8(PubKey());to return? Ideally all these functions should return something if you want the resulting Promise to resolve to something other than undefined. Give the above snippet a go, I did not test it.

ibrahim tanyalcin
  • 5,643
  • 3
  • 16
  • 22
  • i made some edits to my post using your subjections and tried* to make it more clear, although i could not get anything to run without ```PubKey``` and ```PriKey``` not to being async – cubesareneat Nov 26 '21 at 11:36
  • 1
    If you want them to be async you need to chain them: `PubKey(keyPair).then(keyPair => PriKey(keyPair)).then(keyPair => Key2pkcs8(keyPair)).then(Key2pkcs8 => printme(Key2pkcs8)).then(Key2pkcs8=> /*do something with Key2pkcs8*/)` You will need to add async keyword again and modify `printme` because it does not need to await Key2pkcs8 anymore, it will receive it as argument – ibrahim tanyalcin Nov 26 '21 at 13:46