Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace strings with integer algorithm identifiers in packet classes #1410

Merged
merged 13 commits into from Nov 22, 2021
Merged
25 changes: 15 additions & 10 deletions openpgp.d.ts
Expand Up @@ -185,8 +185,8 @@ export function decryptSessionKeys<T extends MaybeStream<Data>>(options: { messa
export function readMessage<T extends MaybeStream<string>>(options: { armoredMessage: T, config?: PartialConfig }): Promise<Message<T>>;
export function readMessage<T extends MaybeStream<Uint8Array>>(options: { binaryMessage: T, config?: PartialConfig }): Promise<Message<T>>;

export function createMessage<T extends MaybeStream<string>>(options: { text: T, filename?: string, date?: Date, type?: DataPacketType }): Promise<Message<T>>;
export function createMessage<T extends MaybeStream<Uint8Array>>(options: { binary: T, filename?: string, date?: Date, type?: DataPacketType }): Promise<Message<T>>;
export function createMessage<T extends MaybeStream<string>>(options: { text: T, filename?: string, date?: Date, format?: enums.literalFormatNames }): Promise<Message<T>>;
export function createMessage<T extends MaybeStream<Uint8Array>>(options: { binary: T, filename?: string, date?: Date, format?: enums.literalFormatNames }): Promise<Message<T>>;

export function encrypt<T extends MaybeStream<Data>>(options: EncryptOptions & { message: Message<T>, format?: 'armored' }): Promise<
T extends WebStream<infer X> ? WebStream<string> :
Expand Down Expand Up @@ -359,7 +359,7 @@ declare abstract class BasePacket {
* - A Subkey Packet cannot always be used when a Primary Key Packet is expected (and vice versa).
*/
declare abstract class BasePublicKeyPacket extends BasePacket {
public algorithm: enums.publicKeyNames;
public algorithm: enums.publicKey;
public created: Date;
public version: number;
public getAlgorithmInfo(): AlgorithmInfo;
Expand Down Expand Up @@ -417,8 +417,8 @@ export class SymEncryptedIntegrityProtectedDataPacket extends BasePacket {

export class AEADEncryptedDataPacket extends BasePacket {
static readonly tag: enums.packet.aeadEncryptedData;
private decrypt(sessionKeyAlgorithm: string, sessionKey: Uint8Array, config?: Config): void;
private encrypt(sessionKeyAlgorithm: string, sessionKey: Uint8Array, config?: Config): void;
private decrypt(sessionKeyAlgorithm: enums.symmetric, sessionKey: Uint8Array, config?: Config): void;
private encrypt(sessionKeyAlgorithm: enums.symmetric, sessionKey: Uint8Array, config?: Config): void;
private crypt(fn: Function, sessionKey: Uint8Array, data: MaybeStream<Uint8Array>): MaybeStream<Uint8Array>
}

Expand All @@ -438,8 +438,8 @@ export class LiteralDataPacket extends BasePacket {
static readonly tag: enums.packet.literalData;
private getText(clone?: boolean): MaybeStream<string>;
private getBytes(clone?: boolean): MaybeStream<Uint8Array>;
private setText(text: MaybeStream<string>, format?: DataPacketType);
private setBytes(bytes: MaybeStream<Uint8Array>, format?: DataPacketType);
private setText(text: MaybeStream<string>, format?: enums.literal);
private setBytes(bytes: MaybeStream<Uint8Array>, format: enums.literal);
private setFilename(filename: string);
private getFilename(): string;
private writeHeader(): Uint8Array;
Expand Down Expand Up @@ -534,8 +534,6 @@ export type AnyPacket = BasePacket;
export type AnySecretKeyPacket = SecretKeyPacket | SecretSubkeyPacket;
export type AnyKeyPacket = BasePublicKeyPacket;

type DataPacketType = 'utf8' | 'binary' | 'text' | 'mime';

type AllowedPackets = Map<enums.packet, object>; // mapping to Packet classes (i.e. typeof LiteralDataPacket etc.)
export class PacketList<T extends AnyPacket> extends Array<T> {
static fromBinary(bytes: MaybeStream<Uint8Array>, allowedPackets: AllowedPackets, config?: Config): PacketList<AnyPacket>; // the packet types depend on`allowedPackets`
Expand Down Expand Up @@ -639,7 +637,6 @@ interface SignOptions {
message: CleartextMessage | Message<MaybeStream<Data>>;
signingKeys?: MaybeArray<PrivateKey>;
format?: 'armored' | 'binary' | 'object';
dataType?: DataPacketType;
detached?: boolean;
signingKeyIDs?: MaybeArray<KeyID>;
date?: Date;
Expand Down Expand Up @@ -885,4 +882,12 @@ export namespace enums {
ocb = 2,
experimentalGCM = 100 // Private algorithm
}

export type literalFormatNames = 'utf8' | 'binary' | 'text' | 'mime'
enum literal {
binary = 98,
text = 116,
utf8 = 117,
mime = 109
}
}
5 changes: 4 additions & 1 deletion src/crypto/cipher/aes.js
@@ -1,6 +1,9 @@
import { AES_ECB } from '@openpgp/asmcrypto.js/dist_es8/aes/ecb';

// TODO use webCrypto or nodeCrypto when possible.
/**
* Javascript AES implementation.
* This is used as fallback if the native Crypto APIs are not available.
*/
function aes(length) {
const C = function(key) {
const aesECB = new AES_ECB(key);
Expand Down
29 changes: 27 additions & 2 deletions src/crypto/crypto.js
Expand Up @@ -26,6 +26,7 @@

import publicKey from './public_key';
import * as cipher from './cipher';
import mode from './mode';
import { getRandomBytes } from './random';
import ECDHSymkey from '../type/ecdh_symkey';
import KDFParams from '../type/kdf_params';
Expand Down Expand Up @@ -348,7 +349,8 @@ export async function validateParams(algo, publicParams, privateParams) {
* @async
*/
export async function getPrefixRandom(algo) {
const prefixrandom = await getRandomBytes(cipher[algo].blockSize);
const { blockSize } = getCipher(algo);
const prefixrandom = await getRandomBytes(blockSize);
const repeat = new Uint8Array([prefixrandom[prefixrandom.length - 2], prefixrandom[prefixrandom.length - 1]]);
return util.concat([prefixrandom, repeat]);
}
Expand All @@ -361,5 +363,28 @@ export async function getPrefixRandom(algo) {
* @async
*/
export function generateSessionKey(algo) {
return getRandomBytes(cipher[algo].keySize);
const { keySize } = getCipher(algo);
return getRandomBytes(keySize);
}

/**
* Get implementation of the given AEAD mode
* @param {enums.aead} algo
* @returns {Object}
* @throws {Error} on invalid algo
*/
export function getAEADMode(algo) {
const algoName = enums.read(enums.aead, algo);
return mode[algoName];
}

/**
* Get implementation of the given cipher
* @param {enums.symmetric} algo
* @returns {Object}
* @throws {Error} on invalid algo
*/
export function getCipher(algo) {
const algoName = enums.read(enums.symmetric, algo);
return cipher[algoName];
}
36 changes: 15 additions & 21 deletions src/crypto/hash/index.js
Expand Up @@ -16,6 +16,7 @@ import * as stream from '@openpgp/web-stream-tools';
import md5 from './md5';
import util from '../../util';
import defaultConfig from '../../config';
import enums from '../../enums';

const webCrypto = util.getWebCrypto();
const nodeCrypto = util.getNodeCrypto();
Expand Down Expand Up @@ -110,26 +111,19 @@ export default {
*/
digest: function(algo, data) {
switch (algo) {
case 1:
// - MD5 [HAC]
case enums.hash.md5:
return this.md5(data);
case 2:
// - SHA-1 [FIPS180]
case enums.hash.sha1:
return this.sha1(data);
case 3:
// - RIPE-MD/160 [HAC]
case enums.hash.ripemd:
return this.ripemd(data);
case 8:
// - SHA256 [FIPS180]
case enums.hash.sha256:
return this.sha256(data);
case 9:
// - SHA384 [FIPS180]
case enums.hash.sha384:
return this.sha384(data);
case 10:
// - SHA512 [FIPS180]
case enums.hash.sha512:
return this.sha512(data);
case 11:
// - SHA224 [FIPS180]
case enums.hash.sha224:
return this.sha224(data);
default:
throw new Error('Invalid hash function.');
Expand All @@ -143,18 +137,18 @@ export default {
*/
getHashByteLength: function(algo) {
switch (algo) {
case 1: // - MD5 [HAC]
case enums.hash.md5:
return 16;
case 2: // - SHA-1 [FIPS180]
case 3: // - RIPE-MD/160 [HAC]
case enums.hash.sha1:
case enums.hash.ripemd:
return 20;
case 8: // - SHA256 [FIPS180]
case enums.hash.sha256:
return 32;
case 9: // - SHA384 [FIPS180]
case enums.hash.sha384:
return 48;
case 10: // - SHA512 [FIPS180]
case enums.hash.sha512:
return 64;
case 11: // - SHA224 [FIPS180]
case enums.hash.sha224:
return 28;
default:
throw new Error('Invalid hash algorithm.');
Expand Down
40 changes: 31 additions & 9 deletions src/crypto/mode/cfb.js
Expand Up @@ -27,6 +27,7 @@ import { AES_CFB } from '@openpgp/asmcrypto.js/dist_es8/aes/cfb';
import * as stream from '@openpgp/web-stream-tools';
import * as cipher from '../cipher';
import util from '../../util';
import enums from '../../enums';

const webCrypto = util.getWebCrypto();
const nodeCrypto = util.getNodeCrypto();
Expand All @@ -43,15 +44,25 @@ const nodeAlgos = {
/* twofish is not implemented in OpenSSL */
};

/**
* CFB encryption
* @param {enums.symmetric} algo - block cipher algorithm
* @param {Uint8Array} key
* @param {MaybeStream<Uint8Array>} plaintext
* @param {Uint8Array} iv
* @param {Object} config - full configuration, defaults to openpgp.config
* @returns MaybeStream<Uint8Array>
*/
export async function encrypt(algo, key, plaintext, iv, config) {
if (util.getNodeCrypto() && nodeAlgos[algo]) { // Node crypto library.
const algoName = enums.read(enums.symmetric, algo);
if (util.getNodeCrypto() && nodeAlgos[algoName]) { // Node crypto library.
return nodeEncrypt(algo, key, plaintext, iv);
}
if (algo.substr(0, 3) === 'aes') {
if (algoName.substr(0, 3) === 'aes') {
return aesEncrypt(algo, key, plaintext, iv, config);
}

const cipherfn = new cipher[algo](key);
const cipherfn = new cipher[algoName](key);
const block_size = cipherfn.blockSize;

const blockc = iv.slice();
Expand All @@ -76,15 +87,24 @@ export async function encrypt(algo, key, plaintext, iv, config) {
return stream.transform(plaintext, process, process);
}

/**
* CFB decryption
* @param {enums.symmetric} algo - block cipher algorithm
* @param {Uint8Array} key
* @param {MaybeStream<Uint8Array>} ciphertext
* @param {Uint8Array} iv
* @returns MaybeStream<Uint8Array>
*/
export async function decrypt(algo, key, ciphertext, iv) {
if (util.getNodeCrypto() && nodeAlgos[algo]) { // Node crypto library.
const algoName = enums.read(enums.symmetric, algo);
if (util.getNodeCrypto() && nodeAlgos[algoName]) { // Node crypto library.
return nodeDecrypt(algo, key, ciphertext, iv);
}
if (algo.substr(0, 3) === 'aes') {
if (algoName.substr(0, 3) === 'aes') {
return aesDecrypt(algo, key, ciphertext, iv);
}

const cipherfn = new cipher[algo](key);
const cipherfn = new cipher[algoName](key);
const block_size = cipherfn.blockSize;

let blockp = iv;
Expand Down Expand Up @@ -140,19 +160,21 @@ function xorMut(a, b) {
async function webEncrypt(algo, key, pt, iv) {
const ALGO = 'AES-CBC';
const _key = await webCrypto.importKey('raw', key, { name: ALGO }, false, ['encrypt']);
const { blockSize } = cipher[algo];
const { blockSize } = crypto.getCipher(algo);
const cbc_pt = util.concatUint8Array([new Uint8Array(blockSize), pt]);
const ct = new Uint8Array(await webCrypto.encrypt({ name: ALGO, iv }, _key, cbc_pt)).subarray(0, pt.length);
xorMut(ct, pt);
return ct;
}

function nodeEncrypt(algo, key, pt, iv) {
const cipherObj = new nodeCrypto.createCipheriv(nodeAlgos[algo], key, iv);
const algoName = enums.read(enums.symmetric, algo);
const cipherObj = new nodeCrypto.createCipheriv(nodeAlgos[algoName], key, iv);
return stream.transform(pt, value => new Uint8Array(cipherObj.update(value)));
}

function nodeDecrypt(algo, key, ct, iv) {
const decipherObj = new nodeCrypto.createDecipheriv(nodeAlgos[algo], key, iv);
const algoName = enums.read(enums.symmetric, algo);
const decipherObj = new nodeCrypto.createDecipheriv(nodeAlgos[algoName], key, iv);
return stream.transform(ct, value => new Uint8Array(decipherObj.update(value)));
}
7 changes: 5 additions & 2 deletions src/crypto/mode/eax.js
Expand Up @@ -25,6 +25,7 @@
import { AES_CTR } from '@openpgp/asmcrypto.js/dist_es8/aes/ctr';
import CMAC from '../cmac';
import util from '../../util';
import enums from '../../enums';

const webCrypto = util.getWebCrypto();
const nodeCrypto = util.getNodeCrypto();
Expand Down Expand Up @@ -74,11 +75,13 @@ async function CTR(key) {

/**
* Class to en/decrypt using EAX mode.
* @param {String} cipher - The symmetric cipher algorithm to use e.g. 'aes128'
* @param {enums.symmetric} cipher - The symmetric cipher algorithm to use
* @param {Uint8Array} key - The encryption key
*/
async function EAX(cipher, key) {
if (cipher.substr(0, 3) !== 'aes') {
if (cipher !== enums.symmetric.aes128 &&
cipher !== enums.symmetric.aes192 &&
cipher !== enums.symmetric.aes256) {
throw new Error('EAX mode supports only AES cipher');
}

Expand Down
7 changes: 5 additions & 2 deletions src/crypto/mode/gcm.js
Expand Up @@ -24,6 +24,7 @@

import { AES_GCM } from '@openpgp/asmcrypto.js/dist_es8/aes/gcm';
import util from '../../util';
import enums from '../../enums';

const webCrypto = util.getWebCrypto();
const nodeCrypto = util.getNodeCrypto();
Expand All @@ -36,11 +37,13 @@ const ALGO = 'AES-GCM';

/**
* Class to en/decrypt using GCM mode.
* @param {String} cipher - The symmetric cipher algorithm to use e.g. 'aes128'
* @param {enums.symmetric} cipher - The symmetric cipher algorithm to use
* @param {Uint8Array} key - The encryption key
*/
async function GCM(cipher, key) {
if (cipher.substr(0, 3) !== 'aes') {
if (cipher !== enums.symmetric.aes128 &&
cipher !== enums.symmetric.aes192 &&
cipher !== enums.symmetric.aes256) {
throw new Error('GCM mode supports only AES cipher');
}

Expand Down
7 changes: 4 additions & 3 deletions src/crypto/mode/ocb.js
Expand Up @@ -23,7 +23,7 @@

import * as ciphers from '../cipher';
import util from '../../util';

import enums from '../../enums';

const blockLength = 16;
const ivLength = 15;
Expand Down Expand Up @@ -59,7 +59,7 @@ const one = new Uint8Array([1]);

/**
* Class to en/decrypt using OCB mode.
* @param {String} cipher - The symmetric cipher algorithm to use e.g. 'aes128'
* @param {enums.symmetric} cipher - The symmetric cipher algorithm to use
* @param {Uint8Array} key - The encryption key
*/
async function OCB(cipher, key) {
Expand All @@ -72,7 +72,8 @@ async function OCB(cipher, key) {
constructKeyVariables(cipher, key);

function constructKeyVariables(cipher, key) {
const aes = new ciphers[cipher](key);
const cipherName = enums.read(enums.symmetric, cipher);
const aes = new ciphers[cipherName](key);
encipher = aes.encrypt.bind(aes);
decipher = aes.decrypt.bind(aes);

Expand Down
5 changes: 5 additions & 0 deletions src/crypto/public_key/elliptic/curves.js
Expand Up @@ -220,6 +220,11 @@ async function generate(curve) {
};
}

/**
* Get preferred hash algo to use with the given curve
* @param {module:type/oid} oid - curve oid
* @returns {enums.hash} hash algorithm
*/
function getPreferredHashAlgo(oid) {
return curves[enums.write(enums.curve, oid.toHex())].hash;
}
Expand Down