fork-openpgpjs/src/crypto/signature.js
2021-02-27 01:22:13 +01:00

157 lines
6.7 KiB
JavaScript

/**
* @fileoverview Provides functions for asymmetric signing and signature verification
* @module crypto/signature
* @private
*/
import publicKey from './public_key';
import enums from '../enums';
import util from '../util';
/**
* Parse signature in binary form to get the parameters.
* The returned values are only padded for EdDSA, since in the other cases their expected length
* depends on the key params, hence we delegate the padding to the signature verification function.
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
* See {@link https://tools.ietf.org/html/rfc4880#section-5.2.2|RFC 4880 5.2.2.}
* @param {module:enums.publicKey} algo Public key algorithm
* @param {Uint8Array} signature Data for which the signature was created
* @returns {Object} True if signature is valid
* @async
*/
export function parseSignatureParams(algo, signature) {
let read = 0;
switch (algo) {
// Algorithm-Specific Fields for RSA signatures:
// - MPI of RSA signature value m**d mod n.
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaSign: {
const s = util.readMPI(signature.subarray(read));
// The signature needs to be the same length as the public key modulo n.
// We pad s on signature verification, where we have access to n.
return { s };
}
// Algorithm-Specific Fields for DSA or ECDSA signatures:
// - MPI of DSA or ECDSA value r.
// - MPI of DSA or ECDSA value s.
case enums.publicKey.dsa:
case enums.publicKey.ecdsa:
{
const r = util.readMPI(signature.subarray(read)); read += r.length + 2;
const s = util.readMPI(signature.subarray(read));
return { r, s };
}
// Algorithm-Specific Fields for EdDSA signatures:
// - MPI of an EC point r.
// - EdDSA value s, in MPI, in the little endian representation
case enums.publicKey.eddsa: {
// When parsing little-endian MPI data, we always need to left-pad it, as done with big-endian values:
// https://www.ietf.org/archive/id/draft-ietf-openpgp-rfc4880bis-10.html#section-3.2-9
let r = util.readMPI(signature.subarray(read)); read += r.length + 2;
r = util.leftPad(r, 32);
let s = util.readMPI(signature.subarray(read));
s = util.leftPad(s, 32);
return { r, s };
}
default:
throw new Error('Invalid signature algorithm.');
}
}
/**
* Verifies the signature provided for data using specified algorithms and public key parameters.
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
* and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
* for public key and hash algorithms.
* @param {module:enums.publicKey} algo Public key algorithm
* @param {module:enums.hash} hashAlgo Hash algorithm
* @param {Object} signature Named algorithm-specific signature parameters
* @param {Object} publicParams Algorithm-specific public key parameters
* @param {Uint8Array} data Data for which the signature was created
* @param {Uint8Array} hashed The hashed data
* @returns {Boolean} True if signature is valid
* @async
*/
export async function verify(algo, hashAlgo, signature, publicParams, data, hashed) {
switch (algo) {
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaSign: {
const { n, e } = publicParams;
const s = util.leftPad(signature.s, n.length); // padding needed for webcrypto and node crypto
return publicKey.rsa.verify(hashAlgo, data, s, n, e, hashed);
}
case enums.publicKey.dsa: {
const { g, p, q, y } = publicParams;
const { r, s } = signature; // no need to pad, since we always handle them as BigIntegers
return publicKey.dsa.verify(hashAlgo, r, s, hashed, g, p, q, y);
}
case enums.publicKey.ecdsa: {
const { oid, Q } = publicParams;
const curveSize = new publicKey.elliptic.Curve(oid).payloadSize;
// padding needed for webcrypto
const r = util.leftPad(signature.r, curveSize);
const s = util.leftPad(signature.s, curveSize);
return publicKey.elliptic.ecdsa.verify(oid, hashAlgo, { r, s }, data, Q, hashed);
}
case enums.publicKey.eddsa: {
const { oid, Q } = publicParams;
// signature already padded on parsing
return publicKey.elliptic.eddsa.verify(oid, hashAlgo, signature, data, Q, hashed);
}
default:
throw new Error('Invalid signature algorithm.');
}
}
/**
* Creates a signature on data using specified algorithms and private key parameters.
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
* and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
* for public key and hash algorithms.
* @param {module:enums.publicKey} algo Public key algorithm
* @param {module:enums.hash} hashAlgo Hash algorithm
* @param {Object} publicKeyParams Algorithm-specific public and private key parameters
* @param {Object} privateKeyParams Algorithm-specific public and private key parameters
* @param {Uint8Array} data Data to be signed
* @param {Uint8Array} hashed The hashed data
* @returns {Object} Signature Object containing named signature parameters
* @async
*/
export async function sign(algo, hashAlgo, publicKeyParams, privateKeyParams, data, hashed) {
if (!publicKeyParams || !privateKeyParams) {
throw new Error('Missing key parameters');
}
switch (algo) {
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaSign: {
const { n, e } = publicKeyParams;
const { d, p, q, u } = privateKeyParams;
const s = await publicKey.rsa.sign(hashAlgo, data, n, e, d, p, q, u, hashed);
return { s };
}
case enums.publicKey.dsa: {
const { g, p, q } = publicKeyParams;
const { x } = privateKeyParams;
return publicKey.dsa.sign(hashAlgo, hashed, g, p, q, x);
}
case enums.publicKey.elgamal: {
throw new Error('Signing with Elgamal is not defined in the OpenPGP standard.');
}
case enums.publicKey.ecdsa: {
const { oid, Q } = publicKeyParams;
const { d } = privateKeyParams;
return publicKey.elliptic.ecdsa.sign(oid, hashAlgo, data, Q, d, hashed);
}
case enums.publicKey.eddsa: {
const { oid, Q } = publicKeyParams;
const { seed } = privateKeyParams;
return publicKey.elliptic.eddsa.sign(oid, hashAlgo, data, Q, seed, hashed);
}
default:
throw new Error('Invalid signature algorithm.');
}
}