Addresses various review comments by @bartbutler + some cleanups

This commit is contained in:
Mahrud Sayrafi 2018-01-31 17:47:33 -08:00
parent d97bc064ea
commit a4134b9f55
No known key found for this signature in database
GPG Key ID: C24071B956C3245F
19 changed files with 75 additions and 65 deletions

View File

@ -50,7 +50,8 @@ module.exports = function(grunt) {
browserifyOptions: { browserifyOptions: {
standalone: 'openpgp' standalone: 'openpgp'
}, },
external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js' ], // Don't bundle these packages with openpgp.js
external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js', 'bn.js', 'jwk-to-pem' ],
transform: [ transform: [
["babelify", { ["babelify", {
plugins: ["transform-async-to-generator", plugins: ["transform-async-to-generator",
@ -73,7 +74,7 @@ module.exports = function(grunt) {
debug: true, debug: true,
standalone: 'openpgp' standalone: 'openpgp'
}, },
external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js' ], external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch', 'asn1.js', 'bn.js', 'jwk-to-pem' ],
transform: [ transform: [
["babelify", { ["babelify", {
plugins: ["transform-async-to-generator", plugins: ["transform-async-to-generator",
@ -87,6 +88,7 @@ module.exports = function(grunt) {
plugin: [ 'browserify-derequire' ] plugin: [ 'browserify-derequire' ]
} }
}, },
// TODO: remove this, as it is only necessary to get browsertest working.
openpgp_browser: { openpgp_browser: {
files: { files: {
'dist/openpgp_browser.js': [ './src/index.js' ] 'dist/openpgp_browser.js': [ './src/index.js' ]
@ -95,7 +97,7 @@ module.exports = function(grunt) {
browserifyOptions: { browserifyOptions: {
standalone: 'openpgp' standalone: 'openpgp'
}, },
external: [ 'crypto' ], external: [ 'crypto', 'buffer', 'node-localstorage', 'node-fetch' ],
transform: [ transform: [
["babelify", { ["babelify", {
plugins: ["transform-async-to-generator", plugins: ["transform-async-to-generator",
@ -119,7 +121,7 @@ module.exports = function(grunt) {
'test/lib/unittests-bundle.js': [ './test/unittests.js' ] 'test/lib/unittests-bundle.js': [ './test/unittests.js' ]
}, },
options: { options: {
external: [ 'crypto', 'openpgp', '../../dist/openpgp' ] external: [ 'buffer', 'openpgp', '../../dist/openpgp', '../../../dist/openpgp' ]
} }
} }
}, },

View File

@ -1,7 +1,7 @@
{ {
"name": "openpgp", "name": "openpgp",
"description": "OpenPGP.js is a Javascript implementation of the OpenPGP protocol. This is defined in RFC 4880.", "description": "OpenPGP.js is a Javascript implementation of the OpenPGP protocol. This is defined in RFC 4880.",
"version": "2.6.1", "version": "3.0.0",
"license": "LGPL-3.0+", "license": "LGPL-3.0+",
"homepage": "http://openpgpjs.org/", "homepage": "http://openpgpjs.org/",
"engines": { "engines": {

View File

@ -27,10 +27,10 @@
'use strict'; 'use strict';
import config from './config'; import config from './config';
import armor from './encoding/armor.js'; import armor from './encoding/armor';
import enums from './enums.js'; import enums from './enums';
import packet from './packet'; import packet from './packet';
import { Signature } from './signature.js'; import { Signature } from './signature';
/** /**
* @class * @class

View File

@ -6,6 +6,7 @@
import asmCrypto from 'asmcrypto-lite'; import asmCrypto from 'asmcrypto-lite';
// TODO use webCrypto or nodeCrypto when possible.
export default function aes(length) { export default function aes(length) {
var c = function(key) { var c = function(key) {

View File

@ -56,6 +56,7 @@ export default {
* @param {module:enums.publicKey} algo Algorithm to be used (See {@link http://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}) * @param {module:enums.publicKey} algo Algorithm to be used (See {@link http://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1})
* @param {Array<module:type/mpi|module:type/oid|module:type/kdf|module:type/ecdh_symkey>} publicParams Algorithm dependent params * @param {Array<module:type/mpi|module:type/oid|module:type/kdf|module:type/ecdh_symkey>} publicParams Algorithm dependent params
* @param {module:type/mpi} data Data to be encrypted as MPI * @param {module:type/mpi} data Data to be encrypted as MPI
* @param {String} fingerprint Recipient fingerprint
* @return {Array<module:type/mpi|module:type/oid|module:type/kdf|module:type/ecdh_symkey>} encrypted session key parameters * @return {Array<module:type/mpi|module:type/oid|module:type/kdf|module:type/ecdh_symkey>} encrypted session key parameters
*/ */
publicKeyEncrypt: async function(algo, publicParams, data, fingerprint) { publicKeyEncrypt: async function(algo, publicParams, data, fingerprint) {
@ -99,7 +100,8 @@ export default {
* Decrypts data using the specified public key multiprecision integers of the private key, * Decrypts data using the specified public key multiprecision integers of the private key,
* the specified secretMPIs of the private key and the specified algorithm. * the specified secretMPIs of the private key and the specified algorithm.
* @param {module:enums.publicKey} algo Algorithm to be used (See {@link http://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}) * @param {module:enums.publicKey} algo Algorithm to be used (See {@link http://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1})
* @param {Array<module:type/mpi|module:type/oid|module:type/kdf|module:type/ecdh_symkey>} keyIntegers Algorithm dependent params * @param {Array<module:type/mpi|module:type/oid|module:type/kdf} keyIntegers Algorithm dependent params
* @param {Array<module:type/mpi|module:type/ecdh_symkey>} dataIntegers encrypted session key parameters
* @param {String} fingerprint Recipient fingerprint * @param {String} fingerprint Recipient fingerprint
* @return {module:type/mpi} returns a big integer containing the decrypted data; otherwise null * @return {module:type/mpi} returns a big integer containing the decrypted data; otherwise null
*/ */

View File

@ -1,4 +1,21 @@
// Implementation of EdDSA following RFC4880bis-02 for OpenPGP // OpenPGP.js - An OpenPGP implementation in javascript
// Copyright (C) 2018 ProtonMail.ch
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3.0 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
// Implementation of EdDSA following RFC4880bis-03 for OpenPGP
/** /**
* @requires crypto/hash * @requires crypto/hash

View File

@ -32,10 +32,6 @@
'use strict'; 'use strict';
import BN from 'bn.js';
import ASN1 from 'asn1.js';
import jwkToPem from 'jwk-to-pem';
import curves from './curves'; import curves from './curves';
import hash from '../../hash'; import hash from '../../hash';
import util from '../../../util'; import util from '../../../util';
@ -48,12 +44,15 @@ const webCurves = curves.webCurves;
const nodeCrypto = util.getNodeCrypto(); const nodeCrypto = util.getNodeCrypto();
const nodeCurves = curves.nodeCurves; const nodeCurves = curves.nodeCurves;
var ECDSASignature = ASN1.define('ECDSASignature', function() { const BN = nodeCrypto ? require('bn.js') : undefined;
this.seq().obj( const jwkToPem = nodeCrypto ? require('jwk-to-pem') : undefined;
this.key('r').int(), const ECDSASignature = nodeCrypto ?
this.key('s').int() require('asn1.js').define('ECDSASignature', function() {
); this.seq().obj(
}); this.key('r').int(),
this.key('s').int()
);
}) : undefined;
function KeyPair(curve, options) { function KeyPair(curve, options) {
this.curve = curve; this.curve = curve;
@ -62,7 +61,7 @@ function KeyPair(curve, options) {
} }
KeyPair.prototype.sign = async function (message, hash_algo) { KeyPair.prototype.sign = async function (message, hash_algo) {
if (typeof message === 'string') { if (util.isString(message)) {
message = util.str2Uint8Array(message); message = util.str2Uint8Array(message);
} }
if (webCrypto && config.use_native && this.curve.web) { if (webCrypto && config.use_native && this.curve.web) {
@ -76,7 +75,7 @@ KeyPair.prototype.sign = async function (message, hash_algo) {
}; };
KeyPair.prototype.verify = async function (message, signature, hash_algo) { KeyPair.prototype.verify = async function (message, signature, hash_algo) {
if (typeof message === 'string') { if (util.isString(message)) {
message = util.str2Uint8Array(message); message = util.str2Uint8Array(message);
} }
if (webCrypto && config.use_native && this.curve.web) { if (webCrypto && config.use_native && this.curve.web) {
@ -240,7 +239,6 @@ async function nodeVerify(curve, hash_algo, {r, s}, message, publicKey) {
{ private: false } { private: false }
); );
// FIXME what happens when hash_algo = undefined?
const verify = nodeCrypto.createVerify(enums.read(enums.hash, hash_algo)); const verify = nodeCrypto.createVerify(enums.read(enums.hash, hash_algo));
verify.write(message); verify.write(message);
verify.end(); verify.end();

View File

@ -29,8 +29,8 @@
import config from './config'; import config from './config';
import crypto from './crypto'; import crypto from './crypto';
import armor from './encoding/armor.js'; import armor from './encoding/armor';
import enums from './enums.js'; import enums from './enums';
import util from './util'; import util from './util';
import packet from './packet'; import packet from './packet';
@ -542,7 +542,7 @@ Key.prototype.getPrimaryUser = function(allowExpired=false) {
// sort by primary user flag and signature creation time // sort by primary user flag and signature creation time
primaryUsers = primaryUsers.sort(function(a, b) { primaryUsers = primaryUsers.sort(function(a, b) {
var A = a.selfCertificate, B = b.selfCertificate; var A = a.selfCertificate, B = b.selfCertificate;
return A.isPrimaryUserID < B.isPrimaryUserID || A.created < B.created; return (B.isPrimaryUserID - A.isPrimaryUserID) || (B.created - A.created);
}); });
return primaryUsers.pop(); return primaryUsers.pop();
}; };
@ -907,7 +907,7 @@ User.prototype.verify = async function(primaryKey) {
} }
return enums.keyStatus.valid; return enums.keyStatus.valid;
}))); })));
return results.some(status => status === enums.keyStatus.valid)? return results.some(status => status === enums.keyStatus.valid) ?
enums.keyStatus.valid : results.pop(); enums.keyStatus.valid : results.pop();
}; };

View File

@ -25,8 +25,8 @@
'use strict'; 'use strict';
import { readArmored } from '../key.js'; import { readArmored } from '../key';
import LocalStore from './localstore.js'; import LocalStore from './localstore';
/** /**
* Initialization routine for the keyring. This method reads the * Initialization routine for the keyring. This method reads the

View File

@ -27,8 +27,8 @@
'use strict'; 'use strict';
import config from '../config'; import config from '../config';
import { readArmored } from '../key.js'; import { readArmored } from '../key';
import util from '../util.js'; import util from '../util';
export default function LocalStore(prefix) { export default function LocalStore(prefix) {
prefix = prefix || 'openpgp-'; prefix = prefix || 'openpgp-';

View File

@ -31,12 +31,12 @@
import config from './config'; import config from './config';
import crypto from './crypto'; import crypto from './crypto';
import armor from './encoding/armor.js'; import armor from './encoding/armor';
import enums from './enums.js'; import enums from './enums';
import util from './util.js'; import util from './util';
import packet from './packet'; import packet from './packet';
import { Signature } from './signature.js'; import { Signature } from './signature';
import { getPreferredHashAlgo, getPreferredSymAlgo } from './key.js'; import { getPreferredHashAlgo, getPreferredSymAlgo } from './key';
/** /**
@ -145,19 +145,14 @@ Message.prototype.decryptSessionKey = function(privateKey, password) {
}); });
} else if (privateKey) { } else if (privateKey) {
var encryptionKeyIds = this.getEncryptionKeyIds();
if (!encryptionKeyIds.length) {
// nothing to decrypt
return;
}
var privateKeyPacket = privateKey.getKeyPacket(encryptionKeyIds);
if (!privateKeyPacket.isDecrypted) {
throw new Error('Private key is not decrypted.');
}
var pkESKeyPacketlist = this.packets.filterByTag(enums.packet.publicKeyEncryptedSessionKey); var pkESKeyPacketlist = this.packets.filterByTag(enums.packet.publicKeyEncryptedSessionKey);
if (!pkESKeyPacketlist) { if (!pkESKeyPacketlist) {
throw new Error('No public key encrypted session key packet found.'); throw new Error('No public key encrypted session key packet found.');
} }
var privateKeyPacket = privateKey.getKeyPacket(this.getEncryptionKeyIds());
if (!privateKeyPacket.isDecrypted) {
throw new Error('Private key is not decrypted.');
}
// TODO replace when Promise.some or Promise.any are implemented // TODO replace when Promise.some or Promise.any are implemented
// eslint-disable-next-line no-await-in-loop // eslint-disable-next-line no-await-in-loop
await pkESKeyPacketlist.some(async function(packet) { await pkESKeyPacketlist.some(async function(packet) {

View File

@ -34,16 +34,12 @@
'use strict'; 'use strict';
import es6Promise from 'es6-promise'; import * as messageLib from './message';
import { CleartextMessage } from './cleartext';
import * as messageLib from './message.js'; import { generate, reformat } from './key';
import { CleartextMessage } from './cleartext.js'; import config from './config/config';
import { generate, reformat } from './key.js';
import config from './config/config.js';
import util from './util'; import util from './util';
import AsyncProxy from './worker/async_proxy.js'; import AsyncProxy from './worker/async_proxy';
es6Promise.polyfill(); // load ES6 Promises polyfill
////////////////////////// //////////////////////////

View File

@ -23,10 +23,10 @@
'use strict'; 'use strict';
import { Key } from '../key.js'; import { Key } from '../key';
import { Message } from '../message.js'; import { Message } from '../message';
import { CleartextMessage } from '../cleartext.js'; import { CleartextMessage } from '../cleartext';
import { Signature } from '../signature.js' import { Signature } from '../signature'
import Packetlist from './packetlist.js'; import Packetlist from './packetlist.js';
import type_keyid from '../type/keyid.js'; import type_keyid from '../type/keyid.js';

View File

@ -630,7 +630,7 @@ Signature.prototype.verify = async function (key, data) {
if (publicKeyAlgorithm > 0 && publicKeyAlgorithm < 4) { if (publicKeyAlgorithm > 0 && publicKeyAlgorithm < 4) {
mpicount = 1; mpicount = 1;
} }
// Algorithm-Specific Fields for DSA and ECDSA signatures: // Algorithm-Specific Fields for DSA, ECDSA, and EdDSA signatures:
// - MPI of DSA value r. // - MPI of DSA value r.
// - MPI of DSA value s. // - MPI of DSA value s.
else if (publicKeyAlgorithm === enums.publicKey.dsa || else if (publicKeyAlgorithm === enums.publicKey.dsa ||

View File

@ -6,7 +6,8 @@ var util = openpgp.util,
chai = require('chai'), chai = require('chai'),
expect = chai.expect; expect = chai.expect;
describe('TripleDES (EDE) cipher test with test vectors from http://csrc.nist.gov/publications/nistpubs/800-20/800-20.pdf', function() { describe('TripleDES (EDE) cipher test with test vectors from NIST SP 800-20', function() {
// see http://csrc.nist.gov/publications/nistpubs/800-20/800-20.pdf
var key = new Uint8Array([1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]); var key = new Uint8Array([1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]);
var testvectors = [[[0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x95,0xF8,0xA5,0xE5,0xDD,0x31,0xD9,0x00]], var testvectors = [[[0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x95,0xF8,0xA5,0xE5,0xDD,0x31,0xD9,0x00]],
[[0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0xDD,0x7F,0x12,0x1C,0xA5,0x01,0x56,0x19]], [[0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0xDD,0x7F,0x12,0x1C,0xA5,0x01,0x56,0x19]],

View File

@ -168,10 +168,9 @@ describe('Elliptic Curve Cryptography', function () {
expect(result.signatures[0].valid).to.be.true; expect(result.signatures[0].valid).to.be.true;
}); });
}); });
// FIXME is this pattern correct?
it('Sign message', function () { it('Sign message', function () {
var romeo = load_priv_key('romeo'); var romeo = load_priv_key('romeo');
openpgp.sign({privateKeys: [romeo], data: data.romeo.message + "\n"}).then(function (signed) { return openpgp.sign({privateKeys: [romeo], data: data.romeo.message + "\n"}).then(function (signed) {
var romeo = load_pub_key('romeo'); var romeo = load_pub_key('romeo');
var msg = openpgp.cleartext.readArmored(signed.data); var msg = openpgp.cleartext.readArmored(signed.data);
return openpgp.verify({publicKeys: [romeo], message: msg}).then(function (result) { return openpgp.verify({publicKeys: [romeo], message: msg}).then(function (result) {

View File

@ -8,7 +8,7 @@ describe('General', function () {
require('./openpgp.js'); require('./openpgp.js');
require('./hkp.js'); require('./hkp.js');
require('./oid.js'); require('./oid.js');
require('./ecc.js'); require('./ecc_nist.js');
require('./x25519.js'); require('./x25519.js');
}); });

View File

@ -153,7 +153,6 @@ describe('X25519 Cryptography', function () {
expect(result.signatures[0].valid).to.be.true; expect(result.signatures[0].valid).to.be.true;
}); });
}); });
// FIXME is this pattern correct?
it('Sign message', function () { it('Sign message', function () {
var name = 'light' var name = 'light'
var priv = load_priv_key(name); var priv = load_priv_key(name);