Hi,

First of all, thanks a lot for writing and maintaining OpenPGP.js!

As part of my Google Summer of Code work for the CryptoStick project,
I have started an attempt to convert the OpenPGP.js framework to using
W3C's WebCrypto for key generation or discovery and subsequent key usage
for signing, verifying, encrypting and decrypting messages.

What I have done so far is available at my fork of the OpenPGP.js
repository on GitHub in the roam-webcrypto-master-owncrypto branch:
https://github.com/ppentchev/openpgpjs/tree/roam-webcrypto-master-owncrypto

Here's a little explanation of the directions taken and ideas for what
may be done, both in the short and long term; what do you think about
it?

Table of contents:

- what I've done so far
- how you can test it
- work to be done: key storage and retrieval
- work to be done: subkeys
- work to be done: Mozilla DOMCrypt
- work to be done: native Google Chromium WebCrypto support
- work to be done: port to the OpenPGP.js devel branch


===== What I've done so far

TL;DR: refactor some of the OpenPGP.js framework to use WebCrypto, make
key generation and signing work with both the "classic" OpenPGP.js
JavaScript crypto implementation and Netflix's NfWebCrypto used as a
WebCrypto provider, start writing an interface to Mozilla's DOMCrypt
extension used as a WebCrypto provider.

- create an "openpgp_promise" class as an internal Promise-like
  interface, since this is what WebCrypto is based on.  This is a
  trivial queue-based object that implements onsuccess, onerror and
  then(), which should be enough for the present

- rename the openpgp_crypto_generateKeyPair() and
  openpgp_crypto_signData() functions using an _own suffix to free up
  the generic names for WebCrypto-provider-independent functions

- create a generic set of functions for initializing OpenPGP.js
  interfaces to WebCrypto providers and invoking their generateKey(),
  sign(), export(), etc. methods

- overhaul the openpgp_crypto_generateKeyPair_own() and
  openpgp_crypto_signData_own() functions a bit so that they still use
  the JavaScript implementations of RSA, MD5, SHA and so on, but they
  return and accept objects that may be used as WebCrypto keys.  This
  involves a bit of modification to their parameters, since with
  WebCrypto it is not certain whether the private part of the keypair
  will actually be accessible (extractable), so the public/private MPIs
  can no longer simply be passed around.

- change the openpgp.generate_key_pair() method, the
  write_message_signature() method and a couple of others enough so that
  they may operate with the WebCrypto-ified versions of
  openpgp_crypto_generateKeyPair() and openpgp_crypto_signData()

- add some very, very simple DER encoding and decoding functions so that
  at least RSA public keys may be exported using the WebCrypto-mandated
  SPKI method (basically a DER encapsulation of the public key and the
  exponent)

- add an interface to Netflix's NfWebCrypto implementation of the
  WebCrypto standard, so that openpgp_webcrypto_init("nfwebcrypto")
  just works

- add an interface to the _own() implementations of generateKeyPair()
  and signData(), so that openpgp_webcrypto_init("owncrypto") just works

- add the beginings of an interface to Mozilla's DOMCrypt framework;
  I have some more work in progress in my local repository, there are
  still some issues to be ironed out before it sees the light of day

- add the test/webcrypto/ directory with a trivial signtest.html and
  signtest.js example of key generation and text signing; I am aware of
  some intermittent problems when signing multiline messages, this will
  be fixed soon


===== How you can test it

If you just want to use the classic OpenPGP.js JavaScript implementation
of cryptographic primitives, then there is nothing special that you need
to do - just point your browser at signtest.html, click "Generate key",
then click "Sign".

If you'd like to test the interoperation with Netflix's NfWebCrypto
framework, you may need to build the framework first; so far, it only
works on Linux systems, I've only tried it on i386 and x86_64.  This
takes a couple of steps:

1. Check out the source from Netflix's GitHub repository:
   https://github.com/Netflix/NfWebCrypto

2. If you're on a 32-bit Linux system (and not the 64-bit that Netflix
   lists as the only supported platform), then edit
   cmake/toolchains/linux64.cmake and remove the -m64 flag from all the
   places it is listed (CMAKE_C_FLAGS, CMAKE_CXX_FLAGS,
   CMAKE_EXE_LINKER_FLAGS)

3. Follow the instructions in the README file to build it and prepare it
   for running.  If you're using a recent enough version of Chrome, but
   not recent enough to have fixed the access check bug, you may need to
   symlink libnfwebcrypto.so into /usr/lib so that Chrome may actually
   consider opening it.

4. Use the misc/desktop/start.sh script (possibly with a different URL)
   or a much simpler version:

#!/bin/sh

/opt/google/chrome/chrome 
'--register-pepper-plugins=/usr/lib/libnfwebcrypto.so#NfWebCrypto##2.0.0;application/x-ppapi-nfwebcrypto'
 '--profile-directory=nfwc' --ppapi-out-of-process "$@" 
'file:///path/to//signtest.html'


===== Work to be done: Key storage and retrieval

The first issue to be addressed from here on is that the generated
WebCrypto keys should somehow be referenced in the OpenPGP.js metadata
(so far the localStorage keyring) and used at a later time, too.  This
is not about the persistence of the keys themselves - this should be
addressed by the WebCrypto provider used (or, for the "owncrypto" one,
it is already sketched out as two functions that store and retrieve keys
from window.localStorage).

IMHO, one of the best ways to proceed is to use local storage to store a
new data structure, a dictionary that maps OpenPGP key fingerprints to
sets of WebCrypto data: provider name, key name, key ID.  In theory this
might be done as some sort of extensions to the keyring, but I'm not
sure if it would be a good idea to store stuff into the OpenPGP key
material that is only for our own local use; IMHO, a new data structure
would be a better solution.

I could sketch out an interface and a sample implementation within the
next couple of days.


===== Work to be done: Subkeys

I think that this might be the most important next step in OpenPGP.js's
path to WebCrypto.  The point is that WebCrypto clearly differentiates
keys used for different algorithms, e.g. RSASSA-PKCS-v1_5 for signing
and RSAES-PKCS-v1_5 for encryption.  Of course, it *might* be possible
to generate a signing key, export it (along with the primary key, if the
WebCrypto provider actually allows that at all!) and reimport it as a
"new" encryption key, but this is too weird a solution - and it will
most probably not work at all with some WebCrypto providers, e.g.
hardware tokens that do not allow the private key to ever leave the
premises.

The OpenPGP standard has already come up with a solution: subkeys with
different usage flags: a master signing key and one or more encryption
subkeys.  Currently, OpenPGP.js does not seem to support this very
easily - at least unless I'm mistaken, it seems to mainly prefer a
single RSA keypair used for both signing and encryption.  Do you think
that it would be a good idea to have a new data structure that
represents this hierarchy in JavaScript-friendly terms - a hierarchical
dictionary containing the master key, the subkeys (encryption or
signing, as needed) and possibly multiple user IDs, too, and then
convert this structure to the OpenPGP packet format and back?

I could come up with a prototype - or at least a functional
specification - in the next couple of days.


===== Work to be done: Mozilla DOMCrypt

I'll finish the Mozilla DOMCrypt support as a WebCrypto provider, so we
are not limited to Google Chrome as a supported browser.


===== Work to be done: Native Google Chromium WebCrypto support

One of my next tasks is to build Google Chromium from source and tweak
it a bit so that the WebCrypto support that is present in very recent
development releases is no longer marked as "testing", but it may be
exposed - and then write a OpenPGP.js interface to use that directly as
the WebCrypto provider.


===== Work to be done: port the WebCrypto support to the devel branch

I am aware of the ongoing partial refactoring and extending of the
OpenPGP.js code base in the devel branch.  It's great work, and I'll
port my WebCrypto changes there at some point soon.

.......................

Whoa, you actually made it this far!  Thanks a lot for reading all of
this; I am looking forward to your comments and suggestions.

And thanks once again for your work on OpenPGP.js!

G'luck,
Peter

-- 
Peter Pentchev  [email protected] [email protected] [email protected]
PGP key:        http://people.FreeBSD.org/~roam/roam.key.asc
Key fingerprint 2EE7 A7A5 17FC 124C F115  C354 651E EFB0 2527 DF13
If I were you, who would be reading this sentence?

Attachment: signature.asc
Description: Digital signature

_______________________________________________

http://openpgpjs.org

Reply via email to