Nitrokey / nitrokey-websmartcard-openpgpjs

Fork maintained to add Nitrokey Webcrypt support to OpenPGP.js
https://openpgpjs.org
GNU Lesser General Public License v3.0
0 stars 0 forks source link

Support session packet decryption #1

Open szszszsz opened 1 year ago

szszszsz commented 1 year ago

Support session packet decryption through Nitrokey Webcrypt.

Previous discussions:

See below for the details:

szszszsz commented 1 year ago

Hi! @tomholub @twiss @onlykey

I would like to implement support for Nitrokey Webcrypt inside OpenPGP.js as a POC, so the key management and private key operations could be realized on the USB stick, instead of the PC. See ticket's description for more information.

In the past conversation there was a request for a session key decryption operation. Can you suggest how this could be done given OpenPGP.js current internals?

The way its done in the OpenPGP card specification is, that only the shared secret is calculated from the given ECC session public key of the sender. This should be enough to continue with the process on the OpenPGP.js side. Then this would be a function of a public key and algorithm OID, which could be injected inside the current session key decryption process. What do you think?

Relevant excerpt:

PS We plan to introduce RSA after we have ECC support working.

twiss commented 1 year ago

Hey :wave:

Just to confirm, the USB key only performs (in the case of ECC) the ECDH key derivation (i.e. the equivalent of this line of code), not the KDF and key unwrapping done afterwards, correct? (Or can it also do the latter? If so, it would simplify things, and you can ignore most of this comment :))

OpenPGP.js does not export those low-level operations, however, if you're making a fork you do of course have access to them. Or, you could simply replace the linked line with one that calls out to the USB key to do the key derivation.

That would make it a bit difficult to merge back the functionality into OpenPGP.js, though, if we want to do so at some point. But, one way I could see that might be feasible would be to add a function / hook to the config object, which OpenPGP.js could call out to to do the decryption operation. Something like:

await openpgp.decrypt({ // or decryptSessionKeys
  message,
  decryptionKeys,
  config: { deriveSharedKey }
});

async function deriveSharedKey(fingerprint, oid, publicEphemeral) {
  // Call out to the USB key...
  return sharedKey;
}

Where deriveSharedKey (or an object containing multiple hooks / functions) could be imported from elsewhere, e.g. a plugin.

And decryptionKeys would be OpenPGP.js key objects with GNU-dummy private keys for the subkeys that are stored on the USB key (I believe that's what GnuPG does as well). This approach would have the advantage that OpenPGP.js can handle looking for the relevant subkey, and call deriveSharedKey multiple times if it's unknown which subkey the message was encrypted with (each time with the fingerprint of a different subkey). The downside might be that, in that case, it might be less efficient than having this handled elsewhere, especially if you anyway need to fetch all keys to match them to the given fingerprint? I'm not sure about this part. (If we go this route, we'd need to modify this code.)

One slightly annoying thing (if my understanding above is correct) is that the KDF and AES-KW operations are specific to ECDH, RSA doesn't do them. So we'd need separate helper functions for RSA and ECC. But maybe that's livable / just the way it is.

tomholub commented 1 year ago

Indeed - what Daniel describes is a more specific proposal of what I hinted at here https://github.com/Nitrokey/nitrokey-webcrypt-openpgpjs/issues/8

szszszsz commented 1 year ago

Hey! Thank you for the pointers and sorry for being silent for the last weeks!

I have published a potential plugin solution at https://github.com/openpgpjs/openpgpjs/pull/1567. Please take a look.