fork-openpgpjs/src/crypto/pkcs1.js
2021-02-28 01:47:48 +01:00

153 lines
5.7 KiB
JavaScript

// GPG4Browsers - An OpenPGP implementation in javascript
// Copyright (C) 2011 Recurity Labs GmbH
//
// 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
/**
* @fileoverview Provides EME-PKCS1-v1_5 encoding and decoding and EMSA-PKCS1-v1_5 encoding function
* @see module:crypto/public_key/rsa
* @see module:crypto/public_key/elliptic/ecdh
* @see PublicKeyEncryptedSessionKeyPacket
* @module crypto/pkcs1
* @private
*/
import { getRandomBytes } from './random';
import hash from './hash';
/**
* ASN1 object identifiers for hashes
* @see {@link https://tools.ietf.org/html/rfc4880#section-5.2.2}
*/
const hash_headers = [];
hash_headers[1] = [0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04,
0x10];
hash_headers[2] = [0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14];
hash_headers[3] = [0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x24, 0x03, 0x02, 0x01, 0x05, 0x00, 0x04, 0x14];
hash_headers[8] = [0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00,
0x04, 0x20];
hash_headers[9] = [0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00,
0x04, 0x30];
hash_headers[10] = [0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
0x00, 0x04, 0x40];
hash_headers[11] = [0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05,
0x00, 0x04, 0x1C];
/**
* Create padding with secure random data
* @private
* @param {Integer} length - Length of the padding in bytes
* @returns {Uint8Array} Random padding.
* @async
*/
async function getPkcs1Padding(length) {
const result = new Uint8Array(length);
let count = 0;
while (count < length) {
const randomBytes = await getRandomBytes(length - count);
for (let i = 0; i < randomBytes.length; i++) {
if (randomBytes[i] !== 0) {
result[count++] = randomBytes[i];
}
}
}
return result;
}
/**
* Create a EME-PKCS1-v1_5 padded message
* @see {@link https://tools.ietf.org/html/rfc4880#section-13.1.1|RFC 4880 13.1.1}
* @param {Uint8Array} message - Message to be encoded
* @param {Integer} keyLength - The length in octets of the key modulus
* @returns {Uint8Array} EME-PKCS1 padded message.
* @async
*/
export async function emeEncode(message, keyLength) {
const mLength = message.length;
// length checking
if (mLength > keyLength - 11) {
throw new Error('Message too long');
}
// Generate an octet string PS of length k - mLen - 3 consisting of
// pseudo-randomly generated nonzero octets
const PS = await getPkcs1Padding(keyLength - mLength - 3);
// Concatenate PS, the message M, and other padding to form an
// encoded message EM of length k octets as EM = 0x00 || 0x02 || PS || 0x00 || M.
const encoded = new Uint8Array(keyLength);
// 0x00 byte
encoded[1] = 2;
encoded.set(PS, 2);
// 0x00 bytes
encoded.set(message, keyLength - mLength);
return encoded;
}
/**
* Decode a EME-PKCS1-v1_5 padded message
* @see {@link https://tools.ietf.org/html/rfc4880#section-13.1.2|RFC 4880 13.1.2}
* @param {Uint8Array} encoded - Encoded message bytes
* @returns {Uint8Array} Message.
*/
export function emeDecode(encoded) {
let i = 2;
while (encoded[i] !== 0 && i < encoded.length) {
i++;
}
const psLen = i - 2;
const separator = encoded[i++];
if (encoded[0] === 0 && encoded[1] === 2 && psLen >= 8 && separator === 0) {
return encoded.subarray(i);
}
throw new Error('Decryption error');
}
/**
* Create a EMSA-PKCS1-v1_5 padded message
* @see {@link https://tools.ietf.org/html/rfc4880#section-13.1.3|RFC 4880 13.1.3}
* @param {Integer} algo - Hash algorithm type used
* @param {Uint8Array} hashed - Message to be encoded
* @param {Integer} emLen - Intended length in octets of the encoded message
* @returns {Uint8Array} Encoded message.
*/
export async function emsaEncode(algo, hashed, emLen) {
let i;
if (hashed.length !== hash.getHashByteLength(algo)) {
throw new Error('Invalid hash length');
}
// produce an ASN.1 DER value for the hash function used.
// Let T be the full hash prefix
const hashPrefix = new Uint8Array(hash_headers[algo].length);
for (i = 0; i < hash_headers[algo].length; i++) {
hashPrefix[i] = hash_headers[algo][i];
}
// and let tLen be the length in octets prefix and hashed data
const tLen = hashPrefix.length + hashed.length;
if (emLen < tLen + 11) {
throw new Error('Intended encoded message length too short');
}
// an octet string PS consisting of emLen - tLen - 3 octets with hexadecimal value 0xFF
// The length of PS will be at least 8 octets
const PS = new Uint8Array(emLen - tLen - 3).fill(0xff);
// Concatenate PS, the hash prefix, hashed data, and other padding to form the
// encoded message EM as EM = 0x00 || 0x01 || PS || 0x00 || prefix || hashed
const EM = new Uint8Array(emLen);
EM[1] = 0x01;
EM.set(PS, 2);
EM.set(hashPrefix, emLen - tLen);
EM.set(hashed, emLen - hashed.length);
return EM;
}