fork-openpgpjs/src/crypto/public_key/elliptic/curves.js
BafS de6ffc2f76 Remove 'user strict'
'use strict' is unnecessary inside modules because module code is always strict mode code. Ref: https://www.ecma-international.org/ecma-262/6.0/#sec-strict-mode-code
2018-02-13 23:33:09 +01:00

250 lines
7.6 KiB
JavaScript

// OpenPGP.js - An OpenPGP implementation in javascript
// Copyright (C) 2015-2016 Decentral
//
// 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
// Wrapper of an instance of an Elliptic Curve
/**
* @requires crypto/public_key/elliptic/key
* @requires crypto/public_key/jsbn
* @requires enums
* @requires util
* @module crypto/public_key/elliptic/curve
*/
import { ec as EC, eddsa as EdDSA } from 'elliptic';
import { KeyPair } from './key';
import BigInteger from '../jsbn';
import random from '../../random';
import config from '../../../config';
import enums from '../../../enums';
import util from '../../../util';
import OID from '../../../type/oid';
import base64 from '../../../encoding/base64';
const webCrypto = util.getWebCrypto();
const nodeCrypto = util.getNodeCrypto();
var webCurves = {}, nodeCurves = {};
webCurves = {
'p256': 'P-256',
'p384': 'P-384',
'p521': 'P-521'
};
if (nodeCrypto && config.use_native) {
var knownCurves = nodeCrypto.getCurves();
nodeCurves = {
'secp256k1': knownCurves.includes('secp256k1') ? 'secp256k1' : undefined,
'p256': knownCurves.includes('prime256v1') ? 'prime256v1' : undefined,
'p384': knownCurves.includes('secp384r1') ? 'secp384r1' : undefined,
'p521': knownCurves.includes('secp521r1') ? 'secp521r1' : undefined
// TODO add more here
};
}
const curves = {
p256: {
oid: util.bin2str([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]),
keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha256,
cipher: enums.symmetric.aes128,
node: nodeCurves.p256,
web: webCurves.p256,
payloadSize: 32
},
p384: {
oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x22]),
keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha384,
cipher: enums.symmetric.aes192,
node: nodeCurves.p384,
web: webCurves.p384,
payloadSize: 48
},
p521: {
oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x23]),
keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha512,
cipher: enums.symmetric.aes256,
node: nodeCurves.p521,
web: webCurves.p521,
payloadSize: 66
},
secp256k1: {
oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x0A]),
keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha256,
cipher: enums.symmetric.aes128,
node: false // FIXME when we replace jwk-to-pem or it supports this curve
},
ed25519: {
oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01]),
keyType: enums.publicKey.eddsa,
hash: enums.hash.sha512,
payloadSize: 32
},
curve25519: {
oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01]),
keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha256,
cipher: enums.symmetric.aes128
},
brainpoolP256r1: { // TODO 1.3.36.3.3.2.8.1.1.7
oid: util.bin2str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07])
},
brainpoolP384r1: { // TODO 1.3.36.3.3.2.8.1.1.11
oid: util.bin2str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B])
},
brainpoolP512r1: { // TODO 1.3.36.3.3.2.8.1.1.13
oid: util.bin2str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D])
}
};
function Curve(name, params) {
this.keyType = params.keyType;
switch (this.keyType) {
case enums.publicKey.eddsa:
this.curve = new EdDSA(name);
break;
case enums.publicKey.ecdsa:
this.curve = new EC(name);
break;
default:
throw new Error('Unknown elliptic key type;');
}
this.name = name;
this.oid = curves[name].oid;
this.hash = params.hash;
this.cipher = params.cipher;
this.node = params.node && curves[name].node;
this.web = params.web && curves[name].web;
this.payloadSize = curves[name].payloadSize;
}
Curve.prototype.keyFromPrivate = function (priv) { // Not for ed25519
return new KeyPair(this.curve, { priv: priv });
};
Curve.prototype.keyFromSecret = function (secret) { // Only for ed25519
return new KeyPair(this.curve, { secret: secret });
};
Curve.prototype.keyFromPublic = function (pub) {
return new KeyPair(this.curve, { pub: pub });
};
Curve.prototype.genKeyPair = async function () {
var keyPair;
if (webCrypto && config.use_native && this.web) {
// If browser doesn't support a curve, we'll catch it
try {
keyPair = await webGenKeyPair(this.name);
return new KeyPair(this.curve, keyPair);
} catch (err) {
util.print_debug("Browser did not support signing: " + err.message);
}
} else if (nodeCrypto && config.use_native && this.node) {
keyPair = await nodeGenKeyPair(this.name);
return new KeyPair(this.curve, keyPair);
}
var compact = this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont';
var r = await this.curve.genKeyPair();
if (this.keyType === enums.publicKey.eddsa) {
keyPair = { secret: r.getSecret() };
} else {
keyPair = { pub: r.getPublic('array', compact), priv: r.getPrivate().toArray() };
}
return new KeyPair(this.curve, keyPair);
};
function get(oid_or_name) {
var name;
if (OID.prototype.isPrototypeOf(oid_or_name) &&
enums.curve[oid_or_name.toHex()]) {
name = enums.write(enums.curve, oid_or_name.toHex()); // by curve OID
return new Curve(name, curves[name]);
} else if (enums.curve[oid_or_name]) {
name = enums.write(enums.curve, oid_or_name); // by curve name
return new Curve(name, curves[name]);
} else if (enums.curve[util.hexstrdump(oid_or_name)]) {
name = enums.write(enums.curve, util.hexstrdump(oid_or_name)); // by oid string
return new Curve(name, curves[name]);
}
throw new Error('Not valid curve');
}
async function generate(curve) {
curve = get(curve);
var keyPair = await curve.genKeyPair();
return {
oid: curve.oid,
Q: new BigInteger(keyPair.getPublic()),
d: new BigInteger(keyPair.getPrivate()),
hash: curve.hash,
cipher: curve.cipher
};
}
function getPreferredHashAlgo(oid) {
return curves[enums.write(enums.curve, oid.toHex())].hash;
}
module.exports = {
Curve: Curve,
curves: curves,
webCurves: webCurves,
nodeCurves: nodeCurves,
getPreferredHashAlgo: getPreferredHashAlgo,
generate: generate,
get: get
};
//////////////////////////
// //
// Helper functions //
// //
//////////////////////////
async function webGenKeyPair(name) {
// Note: keys generated with ECDSA and ECDH are structurally equivalent
var webCryptoKey = await webCrypto.generateKey(
{ name: "ECDSA", namedCurve: webCurves[name] }, true, ["sign", "verify"]
);
var privateKey = await webCrypto.exportKey("jwk", webCryptoKey.privateKey);
var publicKey = await webCrypto.exportKey("jwk", webCryptoKey.publicKey);
return {
pub: {
x: base64.decode(publicKey.x, true),
y: base64.decode(publicKey.y, true)
},
priv: base64.decode(privateKey.d, true)
};
}
async function nodeGenKeyPair(name) {
var ecdh = nodeCrypto.createECDH(nodeCurves[name]);
await ecdh.generateKeys();
return {
pub: ecdh.getPublicKey().toJSON().data,
priv: ecdh.getPrivateKey().toJSON().data
};
}