1. 10
  1. 3

    I have nothing ill to say of this project, but if you’re looking for a condemnation of the OpenPGP ecosystem, look no further than the encrypt() function definition here.

    /**
     * Encrypts message text/data with public keys, passwords or both at once. At least either encryption keys or passwords
     *   must be specified. If signing keys are specified, those will be used to sign the message.
     * @param {Object} options
     * @param {Message} options.message - Message to be encrypted as created by {@link createMessage}
     * @param {PublicKey|PublicKey[]} [options.encryptionKeys] - Array of keys or single key, used to encrypt the message
     * @param {PrivateKey|PrivateKey[]} [options.signingKeys] - Private keys for signing. If omitted message will not be signed
     * @param {String|String[]} [options.passwords] - Array of passwords or a single password to encrypt the message
     * @param {Object} [options.sessionKey] - Session key in the form: `{ data:Uint8Array, algorithm:String }`
     * @param {Boolean} [options.armor=true] - Whether the return values should be ascii armored (true, the default) or binary (false)
     * @param {Signature} [options.signature] - A detached signature to add to the encrypted message
     * @param {Boolean} [options.wildcard=false] - Use a key ID of 0 instead of the public key IDs
     * @param {Array<module:type/keyid~KeyID>} [options.signingKeyIDs=latest-created valid signing (sub)keys] - Array of key IDs to use for signing. Each `signingKeyIDs[i]` corresponds to `signingKeys[i]`
     * @param {Array<module:type/keyid~KeyID>} [options.encryptionKeyIDs=latest-created valid encryption (sub)keys] - Array of key IDs to use for encryption. Each `encryptionKeyIDs[i]` corresponds to `encryptionKeys[i]`
     * @param {Date} [options.date=current date] - Override the creation date of the message signature
     * @param {Array<Object>} [options.signingUserIDs=primary user IDs] - Array of user IDs to sign with, one per key in `signingKeys`, e.g. `[{ name: 'Steve Sender', email: 'steve@openpgp.org' }]`
     * @param {Array<Object>} [options.encryptionUserIDs=primary user IDs] - Array of user IDs to encrypt for, one per key in `encryptionKeys`, e.g. `[{ name: 'Robert Receiver', email: 'robert@openpgp.org' }]`
     * @param {Object} [options.config] - Custom configuration settings to overwrite those in [config]{@link module:config}
     * @returns {Promise<MaybeStream<String>|MaybeStream<Uint8Array>>} Encrypted message (string if `armor` was true, the default; Uint8Array if `armor` was false).
     * @async
     * @static
     */
    export async function encrypt({ message, encryptionKeys, signingKeys, passwords, sessionKey, armor = true, signature = null, wildcard = false, signingKeyIDs = [], encryptionKeyIDs = [], date = new Date(), signingUserIDs = [], encryptionUserIDs = [], config, ...rest }) {
    

    So simple, even Johnny can encrypt. (Surely nothing could go wrong!)

    Contrast this with age.

    1. 2

      age is the obvious replacement for OpenPGP, but it doesn’t have a JavaScript implementation (that I’ve found at least). Possibly a better replacement than OpenPGP.js would be TweetNaCl.js by Dmitry Chestnykh.

      export const encrypt = (json, key) => {
        const keyUint8Array = decodeBase64(key);
      
        const nonce = newNonce();
        const messageUint8 = decodeUTF8(JSON.stringify(json));
        const box = secretbox(messageUint8, nonce, keyUint8Array);
      
        const fullMessage = new Uint8Array(nonce.length + box.length);
        fullMessage.set(nonce);
        fullMessage.set(box, nonce.length);
      
        const base64FullMessage = encodeBase64(fullMessage);
        return base64FullMessage;
      };
      
      1. 1

        At the risk of self-promotion, I did provide a JS alternative that’s better than TweetNaCl on two fronts:

        1. Type-safety (keys are distinct types rather than just Uint8Arrays).
        2. Supports additional authenticated data (e.g. for context-binding for ciphertexts).

        It also handles encoding for you.

        1. 1

          Fair enough. However, to be a touch critical if I may, does Dhole Cryptography have an audit report? TweetNaCl.js was audited by Cure53 in 2017, and from the audit (also on the GitHub README):

          The overall outcome of this audit signals a particularly positive assessment for TweetNaCl-js, as the testing team was unable to find any security problems in the library. It has to be noted that this is an exceptionally rare result of a source code audit for any project and must be seen as a true testament to a development proceeding with security at its core.

          To reiterate, the TweetNaCl-js project, the source code was found to be bug-free at this point.

          […]

          In sum, the testing team is happy to recommend the TweetNaCl-js project as likely one of the safer and more secure cryptographic tools among its competition.

          Compare that to the audit of OpenPGP.js which appears to still have outstanding issues, or the wiki is out of date.

          1. 1

            Dhole Cryptography wraps sodium-plus, which defers to libsodium.js (or sodium-native if you install it alongside). Libsodium.js is compiled from libsodium, while sodium-native is an FFI wrapper for libsodium. Libsodium has been audited.

        2. 1

          age is the obvious replacement for OpenPGP

          Actually it would be more technically correct to say that age is a replacement for a subset of OpenPGP.js. For example, as far as I can see from the readme, age doesn’t sign encrypted files thus you can’t be sure who created the signed file. Age cannot be used to sign software binaries (for example). Age’s specification is a google doc compared to OpenPGP.js that is based on an IETF standard.

          1. 1

            True. age only supports encryption and decryption. It does not do any signing. For that, you should use minisign or signify.

            1. 1

              Yes, age for encryption/decryption, minisign/signifty for signing/verification. What should we use for key authentication though?

              OpenPGP.js allows building something like this: https://keyoxide.org/9f0048ac0b23301e1f77e994909f6bd6f80f485d where one can get more assurance that the key belongs to the person they think it should in an automatic, machine-readable way.

                1. 1

                  I’m not familiar with Keyoxide, but it seems similar to Keybase and keys.pub. In practice though, the “web of trust” is broken.