Labs/Secret
Contents
Secret
The Secret library (real name TBD) is a general-purpose cryptography API and implementation for JavaScript.
Our plan is to design a modern API and first expose it to chrome JS (for browser add-ons), and later expose it to content as well.
Drivers
Status
API is being worked on here.
No add-on is available at this time, work will start in the next week or so.
Goals
- Give tools to developers
- Deliver an API
- Deliver a fast implementation of a subset of that API
- Make it possible for others to implement the API, both using native code and in pure JS
- Desirable Properties
- Good JS feel to the API
- Good error messaging
- Hard to screw up - reasonably safe defaults, good samples & docs
- Async support?
- feature-detection: give programs a way to reliably discover whether each feature is available or not, make it easy to add new features over time. Maybe tell developers to use e.g. 'crypto.v1.RSA', or add a get_version() function, or has_feature("rsa"), etc.
Non Goals
- Expose HW acceleration to consumers of this API (but HW accel could be used if the API implementor can & wants)
- Implement end-to-end high level solutions, see Features below for examples
Use Cases
- Weave
- Protecting bookmarks/user data
- Exchange credentials
- Account Manager
- Could use keys to prove ownership of an account, no passwords needed
- Using crypto to prove access to an account Petname toolbar
- Looks into SSL certs, etc
- Tahoe
- Uses symmetric crypto, and asymmetric signatures
- Encrypted mail (decryption in content)
- Authentication
- Enterprises
- Banking
- Requires "TPM-like" functionality
- Authorization
- Banking
- Requires secure UI
- Password managers and similar apps in the cloud
- Password-authenticated key exchange
- Somewhat narrow applications in the existing web
- Cross-domain AJAX requests on federated servers
- Eg, some kind of cryptographically signed open federated Facebook pipe dream
- Signing legal documents in the EU
- Requires smartcard support
- Signing legal documents in Korea/China
- Requirements TBD
- Discussion on the W3C Public Webapps list of the need for such an API:
http://lists.w3.org/Archives/Public/public-webapps/2010OctDec/1040.html
Features
Primitives Level
- bytestring
- bigint
- (u)random (optional blocking, calls out to OS)
Low Level
- Packing
- ASN.1/DER
- Base64
- Charset conversion/encoding (UTF8, UCS2)
- HTML FileAPI FileBlob
Mid Level
- Symmetric crypto
- Algorithms: AES
- Modes: CBC, CTR, (XTS? EAX?)
[ Mike: I suggest that at least one AEAD mode (probably EAX) be included and used prominently. Except for interoperability concerns, there's not much reason to use unauthenticated crypto. ]
- Asymmetric crypto
- RSA
- (EC)DSA
- (EC)DH
- Hashing
- Algorithms: SHA1, SHA256
- HMAC
- Key generation
- PBKDF2
- Smartcard/token (?)
- Key exchange
- J-PAKE
High Level
- High-level primitives are OK
- NaCl-style box/unbox
No plans to support any high-level implementations of particular use-cases, including:
- Cert stuff (signing, etc)
- PKCS
- SSL
- X509
Related Work
- Other JS crypto libraries that are out there
- SJCL: (http://bitwiseshiftleft.github.com/sjcl/ http://github.com/bitwiseshiftleft/sjcl) : AES (CCM,OCB2), SHA256, HMAC, PBKDF2, PRNG, bitarray. High-level encrypt/decrypt functions. BSD(2clause) or GPLv>=2.
- AES and SHA-1: http://www.movable-type.co.uk/scripts/aes.html , http://www.movable-type.co.uk/scripts/sha1.html
- Big-Integer libraries (for RSA, (EC)DSA, DH)
- python-ecdsa
http://github.com/warner/python-ecdsa
(I wrote the OOPish API to an existing python ECDSA library).
The README probably covers everything we talked about, although the unit tests (in ecdsa/test_pyecdsa.py) and the utilities in ecdsa/util.py would show more about the alternate forms of signatures.
- python-curve25519
http://github.com/warner/curve25519-donna/blob/master/python/test_curve25519.py
(I wrote this API and python binding to an existing C library)
The curve25519 code has Private() and Public() key objects, and the main API is private.get_shared_key(public), plus a few serialization methods. Curve25519 is defined in terms of bytestrings, so there aren't as many options as there are for ECDSA.
- test_rsa.py / test_aes.py
http://allmydata.org/trac/pycryptopp/browser/pycryptopp/test/test_rsa.py
We use pycryptopp in Tahoe (Zooko wrote it). The RSA interface is very close to my python-ecdsa library.
http://allmydata.org/trac/pycryptopp/browser/pycryptopp/test/test_aes.py
This is the AES-CTR interface that I prefer: cryptor = AES(key), then out=cryptor.process(in). (CTR mode is nicely symmetric: no need to distinguish between encryption and decryption). It uses a stream-like interface: to encrypt a large amount of data, you call process() multiple times:
c1 = AES("key") ; c2 = AES("key") c1.process("foo")+c1.process("bar") == c2.process("foobar")
There's an extension that I want to see, to let you do random-access processing:
c = AES(key, offset=0) out = c.process(in, offset=None)
(if you don't specify offset= in the .process call, it defaults to using the internally-incremented counter)
- hashlib (Python Standard Library)
http://docs.python.org/library/hashlib.html
h=hashlib.sha256(data1) ; h.update(data2); out=h.digest()
webcrypto-api
html5.creation.net/webcrypto-api/
By Channy Yun. More limited than what we are proposing here, but we should reach out.
Notes
Whiteboard pictures:
Suggestions from Prof. Dan Boneh
For high-level symmetric operations, the only sane things to want are authenticated encryption and message authentication. For the former, you want to offer two functions: one takes a key and a message, the other take a key, a message, and a nonce. (the first function create a random nonce). The nonce must never be re-used, and this can be accomplished with either a large random number or a simple persistent counter.
The authenticated encryption function should be implemented with something like GCM over AES (allowing the user to choose 128/192/256 bit keys). The output should include a short magic number and a version number. Obviously if the two-argument form is used, the output must also include the nonce.
Other notes: RSA is fading out, since factoring techniques are getting better and making keys longer and longer is a drag. ECC is the new standard.
Low-level functions should be sufficiently hard to use that most developers will stick to the high-level encrypt/decrypt functions and not assemble the low-level pieces in unsafe ways. Consider leaving out CBC mode altogether, forcing developers who really need it to write their own. Provide low-level pieces for everything that needs to be done in native code (for performance). That probably means SHA256, AES, bigints, ECC math, GCM.
Brian's thoughts
Boneh's simple encrypt/decrypt functions probably need some extra arguments to deal with the unfortunate world of JS unicode (non-binary) strings. One idea: allow the encrypt() function to accept either a bytestring or a unicode string, doing a utf-8 encoding on the latter, but include a bit in the output ciphertext that tells the decryptor whether to decode back into unicode or not (so the output of decrypt(encrypt(m)) is always the same type as m). We must decide whether the key is input as a 16/24/32-byte binary string, or base16/32/64-encoded into a unicode JS string. We must also decide if the output ciphertext is returned as a bytestring or gets base16/32/64 encoded into a JS string.
So maybe add a property bag to the arguments, with fields like "key-encoding", "output-encoding", and "input-encoding".
Keys: we should probably also provide an arbitrary-JS-string-to-key conversion function, maybe PBKDF2 (but I'm not sure the password-strengthening parts are always appropriate). The function would have two purposes: one is simple type conversion (utf-8 encode the unicode string into a bytestring, hash the arbitrary-length bytestring into a fixed-length AES key), the second is key-strengthening busywork.
Versioning: the xxx.Crypto.Simple.encrypt() function will implicitly create messages with the most recent version of this scheme. A version= field in the property bag can be used to create messages with an older version. The ciphertext will embed the version number. The receiving xxx.Crypto.Simple.decrypt() function will handle messages constructed by older versions. Some other function will tell you the version number that's embedded in a given message (so you could choose to reject known-broken old versions, for example). Something like xxx.Crypto.Simple.version would tell you the current version number. The idea is to make things very simple for developers, but make it possible to improve the default encryption scheme later and still have a chance of compatibility.
The presence of a nonce in the output stream should be indicated with a version bit too, or the nonce should always be included. There is a simplicity versus overhead tradeoff here. We always need the three-argument form of encrypt() because otherwise we can't get deterministic encryption, which will make known-answer unit tests impossible to write. Also, the two-argument form should still take an optional argument to control the entropy source used to generate the nonce, again to allow repeatable unit tests.