Bugfix in Native ECC in Node

This commit is contained in:
Mahrud Sayrafi 2018-03-01 01:28:03 -08:00
parent 5fac00eddb
commit 5e857e131e
No known key found for this signature in database
GPG Key ID: C24071B956C3245F
9 changed files with 181 additions and 133 deletions

View File

@ -49,11 +49,12 @@ module.exports = function(grunt) {
standalone: 'openpgp' standalone: 'openpgp'
}, },
// Don't bundle these packages with openpgp.js // Don't bundle these packages with openpgp.js
external: ['crypto', 'zlib', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem'], external: ['crypto', 'zlib', 'node-localstorage', 'node-fetch', 'asn1.js'],
transform: [ transform: [
["babelify", { ["babelify", {
global: true, global: true,
only: /^(?:.*\/node_modules\/asmcrypto\.js\/|(?!.*\/node_modules\/)).*$/, // Only babelify asmcrypto in node_modules // Only babelify asmcrypto in node_modules
only: /^(?:.*\/node_modules\/asmcrypto\.js\/|(?!.*\/node_modules\/)).*$/,
plugins: ["transform-async-to-generator", plugins: ["transform-async-to-generator",
"syntax-async-functions", "syntax-async-functions",
"transform-regenerator", "transform-regenerator",
@ -75,11 +76,12 @@ module.exports = function(grunt) {
debug: true, debug: true,
standalone: 'openpgp' standalone: 'openpgp'
}, },
external: ['crypto', 'zlib', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem'], external: ['crypto', 'zlib', 'node-localstorage', 'node-fetch', 'asn1.js'],
transform: [ transform: [
["babelify", { ["babelify", {
global: true, global: true,
only: /^(?:.*\/node_modules\/asmcrypto\.js\/|(?!.*\/node_modules\/)).*$/, // Only babelify asmcrypto in node_modules // Only babelify asmcrypto in node_modules
only: /^(?:.*\/node_modules\/asmcrypto\.js\/|(?!.*\/node_modules\/)).*$/,
plugins: ["transform-async-to-generator", plugins: ["transform-async-to-generator",
"syntax-async-functions", "syntax-async-functions",
"transform-regenerator", "transform-regenerator",

View File

@ -79,7 +79,6 @@
"compressjs": "github:openpgpjs/compressjs", "compressjs": "github:openpgpjs/compressjs",
"elliptic": "github:openpgpjs/elliptic", "elliptic": "github:openpgpjs/elliptic",
"hash.js": "^1.1.3", "hash.js": "^1.1.3",
"jwk-to-pem": "^1.2.6",
"node-fetch": "^1.7.3", "node-fetch": "^1.7.3",
"node-localstorage": "~1.3.0", "node-localstorage": "~1.3.0",
"pako": "^1.0.6", "pako": "^1.0.6",

View File

@ -56,7 +56,7 @@ if (nodeCrypto) {
const curves = { const curves = {
p256: { p256: {
oid: util.Uint8Array_to_str([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]), oid: [0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07],
keyType: enums.publicKey.ecdsa, keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha256, hash: enums.hash.sha256,
cipher: enums.symmetric.aes128, cipher: enums.symmetric.aes128,
@ -65,7 +65,7 @@ const curves = {
payloadSize: 32 payloadSize: 32
}, },
p384: { p384: {
oid: util.Uint8Array_to_str([0x2B, 0x81, 0x04, 0x00, 0x22]), oid: [0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22],
keyType: enums.publicKey.ecdsa, keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha384, hash: enums.hash.sha384,
cipher: enums.symmetric.aes192, cipher: enums.symmetric.aes192,
@ -74,7 +74,7 @@ const curves = {
payloadSize: 48 payloadSize: 48
}, },
p521: { p521: {
oid: util.Uint8Array_to_str([0x2B, 0x81, 0x04, 0x00, 0x23]), oid: [0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23],
keyType: enums.publicKey.ecdsa, keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha512, hash: enums.hash.sha512,
cipher: enums.symmetric.aes256, cipher: enums.symmetric.aes256,
@ -83,80 +83,83 @@ const curves = {
payloadSize: 66 payloadSize: 66
}, },
secp256k1: { secp256k1: {
oid: util.Uint8Array_to_str([0x2B, 0x81, 0x04, 0x00, 0x0A]), oid: [0x06, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x0A],
keyType: enums.publicKey.ecdsa, keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha256, hash: enums.hash.sha256,
cipher: enums.symmetric.aes128, cipher: enums.symmetric.aes128,
node: false // FIXME when we replace jwk-to-pem or it supports this curve node: nodeCurves.secp256k1
}, },
ed25519: { ed25519: {
oid: util.Uint8Array_to_str([0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01]), oid: [0x06, 0x09, 0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01],
keyType: enums.publicKey.eddsa, keyType: enums.publicKey.eddsa,
hash: enums.hash.sha512, hash: enums.hash.sha512,
payloadSize: 32 payloadSize: 32
}, },
curve25519: { curve25519: {
oid: util.Uint8Array_to_str([0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01]), oid: [0x06, 0x08, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01],
keyType: enums.publicKey.ecdsa, keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha256, hash: enums.hash.sha256,
cipher: enums.symmetric.aes128 cipher: enums.symmetric.aes128
}, },
brainpoolP256r1: { // TODO 1.3.36.3.3.2.8.1.1.7 brainpoolP256r1: { // TODO 1.3.36.3.3.2.8.1.1.7
oid: util.Uint8Array_to_str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07]) oid: [0x06, 0x07, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07]
}, },
brainpoolP384r1: { // TODO 1.3.36.3.3.2.8.1.1.11 brainpoolP384r1: { // TODO 1.3.36.3.3.2.8.1.1.11
oid: util.Uint8Array_to_str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B]) oid: [0x06, 0x07, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B]
}, },
brainpoolP512r1: { // TODO 1.3.36.3.3.2.8.1.1.13 brainpoolP512r1: { // TODO 1.3.36.3.3.2.8.1.1.13
oid: util.Uint8Array_to_str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D]) oid: [0x06, 0x07, 0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D]
} }
}; };
export default function Curve(oid_or_name, params) { export default function Curve(oid_or_name, params) {
if (OID.prototype.isPrototypeOf(oid_or_name) && try {
enums.curve[oid_or_name.toHex()]) { if (util.isArray(oid_or_name) ||
this.name = oid_or_name.toHex(); // by curve OID util.isUint8Array(oid_or_name)) {
} else if (enums.curve[oid_or_name]) { // by oid byte array
this.name = oid_or_name; // by curve name oid_or_name = new OID(oid_or_name);
} else if (enums.curve[util.str_to_hex(oid_or_name)]) { }
this.name = util.str_to_hex(oid_or_name); // by oid string if (oid_or_name instanceof OID) {
} else { // by curve OID
oid_or_name = oid_or_name.getName();
}
// by curve name or oid string
this.name = enums.write(enums.curve, oid_or_name);
} catch (err) {
throw new Error('Not valid curve'); throw new Error('Not valid curve');
} }
this.name = enums.write(enums.curve, this.name);
this.oid = new OID(curves[this.name].oid);
params = params || curves[this.name]; params = params || curves[this.name];
this.keyType = params.keyType; this.keyType = params.keyType;
switch (this.keyType) { switch (this.keyType) {
case enums.publicKey.eddsa:
this.curve = new EdDSA(this.name);
break;
case enums.publicKey.ecdsa: case enums.publicKey.ecdsa:
this.curve = new EC(this.name); this.curve = new EC(this.name);
break; break;
case enums.publicKey.eddsa:
this.curve = new EdDSA(this.name);
break;
default: default:
throw new Error('Unknown elliptic key type;'); throw new Error('Unknown elliptic key type;');
} }
this.oid = params.oid;
this.hash = params.hash; this.hash = params.hash;
this.cipher = params.cipher; this.cipher = params.cipher;
this.node = params.node && curves[this.name].node; this.node = params.node && curves[this.name];
this.web = params.web && curves[this.name].web; this.web = params.web && curves[this.name];
this.payloadSize = curves[this.name].payloadSize; this.payloadSize = params.payloadSize;
} }
Curve.prototype.keyFromPrivate = function (priv) { // Not for ed25519 Curve.prototype.keyFromPrivate = function (priv) { // Not for ed25519
return new KeyPair(this.curve, { priv: priv }); return new KeyPair(this, { priv: priv });
}; };
Curve.prototype.keyFromSecret = function (secret) { // Only for ed25519 Curve.prototype.keyFromSecret = function (secret) { // Only for ed25519
return new KeyPair(this.curve, { secret: secret }); return new KeyPair(this, { secret: secret });
}; };
Curve.prototype.keyFromPublic = function (pub) { Curve.prototype.keyFromPublic = function (pub) {
return new KeyPair(this.curve, { pub: pub }); return new KeyPair(this, { pub: pub });
}; };
Curve.prototype.genKeyPair = async function () { Curve.prototype.genKeyPair = async function () {
@ -174,7 +177,9 @@ Curve.prototype.genKeyPair = async function () {
if (!keyPair || !keyPair.priv) { if (!keyPair || !keyPair.priv) {
// elliptic fallback // elliptic fallback
const r = await this.curve.genKeyPair({ entropy: util.Uint8Array_to_str(random.getRandomBytes(32)) }); const r = await this.curve.genKeyPair({
entropy: util.Uint8Array_to_str(random.getRandomBytes(32))
});
const compact = this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont'; const compact = this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont';
if (this.keyType === enums.publicKey.eddsa) { if (this.keyType === enums.publicKey.eddsa) {
keyPair = { secret: r.getSecret() }; keyPair = { secret: r.getSecret() };
@ -182,14 +187,14 @@ Curve.prototype.genKeyPair = async function () {
keyPair = { pub: r.getPublic('array', compact), priv: r.getPrivate().toArray() }; keyPair = { pub: r.getPublic('array', compact), priv: r.getPrivate().toArray() };
} }
} }
return new KeyPair(this.curve, keyPair); return new KeyPair(this, keyPair);
}; };
async function generate(curve) { async function generate(curve) {
curve = new Curve(curve); curve = new Curve(curve);
const keyPair = await curve.genKeyPair(); const keyPair = await curve.genKeyPair();
return { return {
oid: curve.oid, oid: new OID(curve.oid.slice(2)),
Q: new BN(keyPair.getPublic()), Q: new BN(keyPair.getPublic()),
d: new BN(keyPair.getPrivate()), d: new BN(keyPair.getPrivate()),
hash: curve.hash, hash: curve.hash,
@ -230,6 +235,7 @@ async function webGenKeyPair(name) {
} }
async function nodeGenKeyPair(name) { async function nodeGenKeyPair(name) {
// Note: ECDSA and ECDH key generation is structurally equivalent
const ecdh = nodeCrypto.createECDH(nodeCurves[name]); const ecdh = nodeCrypto.createECDH(nodeCurves[name]);
await ecdh.generateKeys(); await ecdh.generateKeys();

View File

@ -29,7 +29,7 @@
*/ */
import BN from 'bn.js'; import BN from 'bn.js';
import { webCurves, nodeCurves } from './curves'; import { curves, webCurves, nodeCurves } from './curves';
import hash from '../../hash'; import hash from '../../hash';
import util from '../../../util'; import util from '../../../util';
import enums from '../../../enums'; import enums from '../../../enums';
@ -37,19 +37,10 @@ import enums from '../../../enums';
const webCrypto = util.getWebCrypto(); const webCrypto = util.getWebCrypto();
const nodeCrypto = util.getNodeCrypto(); const nodeCrypto = util.getNodeCrypto();
const jwkToPem = nodeCrypto ? require('jwk-to-pem') : undefined;
const ECDSASignature = nodeCrypto ?
require('asn1.js').define('ECDSASignature', function() {
this.seq().obj(
this.key('r').int(),
this.key('s').int()
);
}) : undefined;
export default function KeyPair(curve, options) { export default function KeyPair(curve, options) {
this.curve = curve; this.curve = curve;
this.keyType = curve.curve.type === 'edwards' ? enums.publicKey.eddsa : enums.publicKey.ecdsa; this.keyType = curve.curve.type === 'edwards' ? enums.publicKey.eddsa : enums.publicKey.ecdsa;
this.keyPair = this.curve.keyPair(options); this.keyPair = this.curve.curve.keyPair(options);
} }
KeyPair.prototype.sign = async function (message, hash_algo) { KeyPair.prototype.sign = async function (message, hash_algo) {
@ -90,12 +81,13 @@ KeyPair.prototype.derive = function (pub) {
}; };
KeyPair.prototype.getPublic = function () { KeyPair.prototype.getPublic = function () {
const compact = (this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont'); const compact = this.curve.curve.curve.type === 'edwards' ||
this.curve.curve.curve.type === 'mont';
return this.keyPair.getPublic('array', compact); return this.keyPair.getPublic('array', compact);
}; };
KeyPair.prototype.getPrivate = function () { KeyPair.prototype.getPrivate = function () {
if (this.keyType === enums.publicKey.eddsa) { if (this.curve.keyType === enums.publicKey.eddsa) {
return this.keyPair.getSecret(); return this.keyPair.getSecret();
} }
return this.keyPair.getPrivate().toArray(); return this.keyPair.getPrivate().toArray();
@ -110,15 +102,15 @@ KeyPair.prototype.getPrivate = function () {
async function webSign(curve, hash_algo, message, keyPair) { async function webSign(curve, hash_algo, message, keyPair) {
const l = curve.payloadSize; const len = curve.payloadSize;
const key = await webCrypto.importKey( const key = await webCrypto.importKey(
"jwk", "jwk",
{ {
"kty": "EC", "kty": "EC",
"crv": webCurves[curve.name], "crv": webCurves[curve.name],
"x": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getX().toArray('be', l)), true), "x": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getX().toArray('be', len)), true),
"y": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getY().toArray('be', l)), true), "y": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getY().toArray('be', len)), true),
"d": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPrivate().toArray('be', l)), true), "d": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPrivate().toArray('be', len)), true),
"use": "sig", "use": "sig",
"kid": "ECDSA Private Key" "kid": "ECDSA Private Key"
}, },
@ -141,23 +133,20 @@ async function webSign(curve, hash_algo, message, keyPair) {
message message
)); ));
return { return {
r: signature.slice(0, l), r: signature.slice(0, len),
s: signature.slice(l, 2 * l) s: signature.slice(len, len << 1)
}; };
} }
async function webVerify(curve, hash_algo, { r, s }, message, publicKey) { async function webVerify(curve, hash_algo, { r, s }, message, publicKey) {
const l = curve.payloadSize; const len = curve.payloadSize;
r = Array(l - r.length).fill(0).concat(r);
s = Array(l - s.length).fill(0).concat(s);
const signature = new Uint8Array(r.concat(s)).buffer;
const key = await webCrypto.importKey( const key = await webCrypto.importKey(
"jwk", "jwk",
{ {
"kty": "EC", "kty": "EC",
"crv": webCurves[curve.name], "crv": webCurves[curve.name],
"x": util.Uint8Array_to_b64(new Uint8Array(publicKey.getX().toArray('be', l)), true), "x": util.Uint8Array_to_b64(new Uint8Array(publicKey.getX().toArray('be', len)), true),
"y": util.Uint8Array_to_b64(new Uint8Array(publicKey.getY().toArray('be', l)), true), "y": util.Uint8Array_to_b64(new Uint8Array(publicKey.getY().toArray('be', len)), true),
"use": "sig", "use": "sig",
"kid": "ECDSA Public Key" "kid": "ECDSA Public Key"
}, },
@ -170,6 +159,10 @@ async function webVerify(curve, hash_algo, { r, s }, message, publicKey) {
["verify"] ["verify"]
); );
r = [].concat(Array(len - r.length).fill(0), r);
s = [].concat(Array(len - s.length).fill(0), s);
const signature = new Uint8Array([].concat(r, s)).buffer;
return webCrypto.verify( return webCrypto.verify(
{ {
"name": 'ECDSA', "name": 'ECDSA',
@ -182,58 +175,88 @@ async function webVerify(curve, hash_algo, { r, s }, message, publicKey) {
); );
} }
async function nodeSign(curve, hash_algo, message, keyPair) { async function nodeSign(curve, hash_algo, message, keyPair) {
console.log({
"kty": "EC",
"crv": webCurves[curve.name],
"x": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getX().toArray())),
"y": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getY().toArray())),
"d": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPrivate().toArray())),
"use": "sig",
"kid": "ECDSA Private Key"
});
const key = jwkToPem(
{
"kty": "EC",
"crv": webCurves[curve.name],
"x": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getX().toArray())),
"y": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getY().toArray())),
"d": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPrivate().toArray())),
"use": "sig",
"kid": "ECDSA Private Key"
},
{ private: true }
);
const sign = nodeCrypto.createSign(enums.read(enums.hash, hash_algo)); const sign = nodeCrypto.createSign(enums.read(enums.hash, hash_algo));
sign.write(message); sign.write(message);
sign.end(); sign.end();
const signature = await ECDSASignature.decode(sign.sign(key), 'der');
return { const key = ECPrivateKey.encode({
r: signature.r.toArray(), version: 1,
s: signature.s.toArray() parameters: curve.oid,
}; privateKey: keyPair.getPrivate().toArray(),
publicKey: { unused: 0, data: keyPair.getPublic().encode() }
}, 'pem', {
label: 'EC PRIVATE KEY'
});
return ECDSASignature.decode(sign.sign(key), 'der');
} }
async function nodeVerify(curve, hash_algo, { r, s }, message, publicKey) { async function nodeVerify(curve, hash_algo, { r, s }, message, publicKey) {
const signature = ECDSASignature.encode({ r: new BN(r), s: new BN(s) }, 'der');
const key = jwkToPem(
{
"kty": "EC",
"crv": webCurves[curve.name],
"x": util.Uint8Array_to_b64(new Uint8Array(publicKey.getX().toArray())),
"y": util.Uint8Array_to_b64(new Uint8Array(publicKey.getY().toArray())),
"use": "sig",
"kid": "ECDSA Public Key"
},
{ private: false }
);
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();
const result = await verify.verify(key, signature);
return result; const key = SubjectPublicKeyInfo.encode({
algorithm: {
algorithm: [1, 2, 840, 10045, 2, 1],
parameters: curve.oid
},
subjectPublicKey: { unused: 0, data: publicKey.encode() }
}, 'pem', {
label: 'PUBLIC KEY'
});
const signature = ECDSASignature.encode({
r: new BN(r), s: new BN(s)
}, 'der');
try {
return verify.verify(key, signature);
} catch (err) {
return false;
}
} }
const asn1 = nodeCrypto ? require('asn1.js') : undefined;
const ECDSASignature = nodeCrypto ?
asn1.define('ECDSASignature', function() {
this.seq().obj(
this.key('r').int(),
this.key('s').int()
);
}) : undefined;
const ECParameters = nodeCrypto ?
asn1.define('ECParameters', function() {
this.choice({
namedCurve: this.objid()
});
}) : undefined;
const ECPrivateKey = nodeCrypto ?
asn1.define('ECPrivateKey', function() {
this.seq().obj(
this.key('version').int(),
this.key('privateKey').octstr(),
this.key('parameters').explicit(0).optional().any(),
this.key('publicKey').explicit(1).optional().bitstr()
);
}) : undefined;
const AlgorithmIdentifier = nodeCrypto ?
asn1.define('AlgorithmIdentifier', function() {
this.seq().obj(
this.key('algorithm').objid(),
this.key('parameters').optional().any()
);
}) : undefined;
const SubjectPublicKeyInfo = nodeCrypto ?
asn1.define('SubjectPublicKeyInfo', function() {
this.seq().obj(
this.key('algorithm').use(AlgorithmIdentifier),
this.key('subjectPublicKey').bitstr()
);
}) : undefined;

View File

@ -41,7 +41,9 @@ import util from '../util';
*/ */
export default function MPI(data) { export default function MPI(data) {
/** An implementation dependent integer */ /** An implementation dependent integer */
if (BN.isBN(data)) { if (data instanceof MPI) {
this.data = data.data;
} else if (BN.isBN(data)) {
this.fromBN(data); this.fromBN(data);
} else if (util.isUint8Array(data)) { } else if (util.isUint8Array(data)) {
this.fromUint8Array(data); this.fromUint8Array(data);

View File

@ -18,8 +18,17 @@
/** /**
* Wrapper to an OID value * Wrapper to an OID value
* *
* An object identifier type from * {@link https://tools.ietf.org/html/rfc6637#section-11|RFC6637, section 11}:
* {@link https://tools.ietf.org/html/rfc6637#section-11|RFC6637, section 11}. * The sequence of octets in the third column is the result of applying
* the Distinguished Encoding Rules (DER) to the ASN.1 Object Identifier
* with subsequent truncation. The truncation removes the two fields of
* encoded Object Identifier. The first omitted field is one octet
* representing the Object Identifier tag, and the second omitted field
* is the length of the Object Identifier body. For example, the
* complete ASN.1 DER encoding for the NIST P-256 curve OID is "06 08 2A
* 86 48 CE 3D 03 01 07", from which the first entry in the table above
* is constructed by omitting the first two octets. Only the truncated
* sequence of octets is the valid representation of a curve OID.
* @requires util * @requires util
* @requires enums * @requires enums
* @module type/oid * @module type/oid
@ -33,15 +42,16 @@ import enums from '../enums';
*/ */
function OID(oid) { function OID(oid) {
if (oid instanceof OID) { if (oid instanceof OID) {
oid = oid.oid; this.oid = oid.oid;
} else if (typeof oid === 'undefined') { } else if (util.isArray(oid) ||
oid = ''; util.isUint8Array(oid)) {
} else if (util.isArray(oid)) { if (oid[0] === 0x06) { // DER encoded oid byte array
oid = util.Uint8Array_to_str(oid); oid = oid.slice(2);
} else if (util.isUint8Array(oid)) { }
oid = util.Uint8Array_to_str(oid); this.oid = util.Uint8Array_to_str(oid);
} else {
this.oid = '';
} }
this.oid = oid;
} }
/** /**

View File

@ -18,10 +18,12 @@
/** /**
* This object contains utility functions * This object contains utility functions
* @requires config * @requires config
* @requires encoding/base64
* @module util * @module util
*/ */
import config from './config'; import config from './config';
import b64 from './encoding/base64';
export default { export default {
@ -157,8 +159,8 @@ export default {
* @return {Uint8Array} An array of 8-bit integers * @return {Uint8Array} An array of 8-bit integers
*/ */
b64_to_Uint8Array: function (base64) { b64_to_Uint8Array: function (base64) {
const str = atob(base64.replace(/\-/g, '+').replace(/_/g, '/')); // atob(base64.replace(/\-/g, '+').replace(/_/g, '/'));
return this.str_to_Uint8Array(str); return b64.decode(base64.replace(/\-/g, '+').replace(/_/g, '/'));
}, },
/** /**
@ -168,8 +170,8 @@ export default {
* @return {String} Base-64 encoded string * @return {String} Base-64 encoded string
*/ */
Uint8Array_to_b64: function (bytes, url) { Uint8Array_to_b64: function (bytes, url) {
const base64 = btoa(this.Uint8Array_to_str(bytes)); // btoa(this.Uint8Array_to_str(bytes)).replace(/\+/g, '-').replace(/\//g, '_');
return url ? base64.replace(/\+/g, '-').replace(/\//g, '_') : base64; return b64.encode(bytes, url).replace('\n', '');
}, },
/** /**

View File

@ -144,7 +144,7 @@ describe('Elliptic Curve Cryptography', function () {
it('Creating curve from oid', function (done) { it('Creating curve from oid', function (done) {
const oids = ['2A8648CE3D030107', '2B81040022', '2B81040023', '2B8104000A']; const oids = ['2A8648CE3D030107', '2B81040022', '2B81040023', '2B8104000A'];
oids.forEach(function (oid) { oids.forEach(function (oid) {
expect(new elliptic_curves.Curve(openpgp.util.hex_to_str(oid))).to.exist; expect(new elliptic_curves.Curve(oid)).to.exist;
}); });
done(); done();
}); });
@ -173,21 +173,25 @@ describe('Elliptic Curve Cryptography', function () {
it('Signature verification', function (done) { it('Signature verification', function (done) {
const curve = new elliptic_curves.Curve('p256'); const curve = new elliptic_curves.Curve('p256');
const key = curve.keyFromPublic(signature_data.pub); const key = curve.keyFromPublic(signature_data.pub);
expect(key.verify(signature_data.message, signature_data.signature, 8)).to.eventually.be.true; expect(
done(); key.verify(signature_data.message, signature_data.signature, 8)
).to.eventually.be.true.notify(done);
}); });
it('Invalid signature', function (done) { it('Invalid signature', function (done) {
const curve = new elliptic_curves.Curve('p256'); const curve = new elliptic_curves.Curve('p256');
const key = curve.keyFromPublic(key_data.p256.pub); const key = curve.keyFromPublic(key_data.p256.pub);
expect(key.verify(signature_data.message, signature_data.signature, 8)).to.eventually.be.false; expect(
done(); key.verify(signature_data.message, signature_data.signature, 8)
).to.eventually.be.false.notify(done);
}); });
it('Signature generation', function () { it('Signature generation', function () {
const curve = new elliptic_curves.Curve('p256'); const curve = new elliptic_curves.Curve('p256');
let key = curve.keyFromPrivate(key_data.p256.priv); let key = curve.keyFromPrivate(key_data.p256.priv);
return key.sign(signature_data.message, 8).then(signature => { return key.sign(signature_data.message, 8).then(signature => {
key = curve.keyFromPublic(key_data.p256.pub); key = curve.keyFromPublic(key_data.p256.pub);
expect(key.verify(signature_data.message, signature, 8)).to.eventually.be.true; expect(
key.verify(signature_data.message, signature, 8)
).to.eventually.be.true;
}); });
}); });
it('Shared secret generation', function (done) { it('Shared secret generation', function (done) {
@ -312,7 +316,7 @@ describe('Elliptic Curve Cryptography', function () {
return Promise.resolve().then(() => { return Promise.resolve().then(() => {
const curve = new elliptic_curves.Curve(oid); const curve = new elliptic_curves.Curve(oid);
return elliptic_curves.ecdh.decrypt( return elliptic_curves.ecdh.decrypt(
curve.oid, new openpgp.OID(curve.oid),
cipher, cipher,
hash, hash,
new Uint8Array(ephemeral), new Uint8Array(ephemeral),

View File

@ -524,7 +524,7 @@ describe('X25519 Cryptography', function () {
/* TODO how does GPG2 accept this? /* TODO how does GPG2 accept this?
it('Should handle little-endian parameters in EdDSA', function () { it('Should handle little-endian parameters in EdDSA', function () {
var pubKey = [ const pubKey = [
'-----BEGIN PGP PUBLIC KEY BLOCK-----', '-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: OpenPGP.js v3.0.0', 'Version: OpenPGP.js v3.0.0',
'Comment: https://openpgpjs.org', 'Comment: https://openpgpjs.org',
@ -539,12 +539,12 @@ describe('X25519 Cryptography', function () {
'Gbm1oe83ZB+0aSp5m34YkpHQNb80y8PGFy7nIexiAA==', 'Gbm1oe83ZB+0aSp5m34YkpHQNb80y8PGFy7nIexiAA==',
'=xeG/', '=xeG/',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n'); '-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
var hi = openpgp.key.readArmored(pubKey).keys[0]; const hi = openpgp.key.readArmored(pubKey).keys[0];
return hi.verifyPrimaryUser().then(() => { return hi.verifyPrimaryUser().then(() => {
var results = hi.getPrimaryUser(); const results = hi.getPrimaryUser();
expect(results).to.exist; expect(results).to.exist;
expect(results.user).to.exist; expect(results.user).to.exist;
var user = results.user; const user = results.user;
expect(user.selfCertifications[0].verify( expect(user.selfCertifications[0].verify(
hi.primaryKey, {userid: user.userId, key: hi.primaryKey} hi.primaryKey, {userid: user.userId, key: hi.primaryKey}
)).to.eventually.be.true; )).to.eventually.be.true;