import * as stream from '@openpgp/web-stream-tools';import * as base64 from './base64';import enums from '../enums';import util from '../util';import defaultConfig from '../config';
function getType(text) { const reHeader = /^-----BEGIN PGP (MESSAGE, PART \d+\/\d+|MESSAGE, PART \d+|SIGNED MESSAGE|MESSAGE|PUBLIC KEY BLOCK|PRIVATE KEY BLOCK|SIGNATURE)-----$/m;
const header = text.match(reHeader);
if (!header) { throw new Error('Unknown ASCII armor type'); }
if (/MESSAGE, PART \d+\/\d+/.test(header[1])) { return enums.armor.multipartSection; } if (/MESSAGE, PART \d+/.test(header[1])) { return enums.armor.multipartLast; } if (/SIGNED MESSAGE/.test(header[1])) { return enums.armor.signed; } if (/MESSAGE/.test(header[1])) { return enums.armor.message; } if (/PUBLIC KEY BLOCK/.test(header[1])) { return enums.armor.publicKey; } if (/PRIVATE KEY BLOCK/.test(header[1])) { return enums.armor.privateKey; } if (/SIGNATURE/.test(header[1])) { return enums.armor.signature; }}
function addheader(customComment, config) { let result = ''; if (config.showVersion) { result += 'Version: ' + config.versionString + '\n'; } if (config.showComment) { result += 'Comment: ' + config.commentString + '\n'; } if (customComment) { result += 'Comment: ' + customComment + '\n'; } result += '\n'; return result;}
function verifyHeaders(headers) { for (let i = 0; i < headers.length; i++) { if (!/^([^\s:]|[^\s:][^:]*[^\s:]): .+$/.test(headers[i])) { util.printDebugError(new Error('Improperly formatted armor header: ' + headers[i])); } if (!/^(Version|Comment|MessageID|Hash|Charset): .+$/.test(headers[i])) { util.printDebugError(new Error('Unknown header: ' + headers[i])); } }}
function removeChecksum(text) { let body = text;
const lastEquals = text.lastIndexOf('=');
if (lastEquals >= 0 && lastEquals !== text.length - 1) { body = text.slice(0, lastEquals); }
return body;}
export function unarmor(input) { return new Promise(async (resolve, reject) => { try { const reSplit = /^-----[^-]+-----$/m; const reEmptyLine = /^[ \f\r\t\u00a0\u2000-\u200a\u202f\u205f\u3000]*$/;
let type; const headers = []; let lastHeaders = headers; let headersDone; let text = []; let textDone; const data = base64.decode(stream.transformPair(input, async (readable, writable) => { const reader = stream.getReader(readable); try { while (true) { let line = await reader.readLine(); if (line === undefined) { throw new Error('Misformed armored text'); } line = util.removeTrailingSpaces(line.replace(/[\r\n]/g, '')); if (!type) { if (reSplit.test(line)) { type = getType(line); } } else if (!headersDone) { if (reSplit.test(line)) { reject(new Error('Mandatory blank line missing between armor headers and armor data')); } if (!reEmptyLine.test(line)) { lastHeaders.push(line); } else { verifyHeaders(lastHeaders); headersDone = true; if (textDone || type !== enums.armor.signed) { resolve({ text, data, headers, type }); break; } } } else if (!textDone && type === enums.armor.signed) { if (!reSplit.test(line)) { text.push(line.replace(/^- /, '')); } else { text = text.join('\r\n'); textDone = true; verifyHeaders(lastHeaders); lastHeaders = []; headersDone = false; } } } } catch (e) { reject(e); return; } const writer = stream.getWriter(writable); try { while (true) { await writer.ready; const { done, value } = await reader.read(); if (done) { throw new Error('Misformed armored text'); } const line = value + ''; if (line.indexOf('=') === -1 && line.indexOf('-') === -1) { await writer.write(line); } else { let remainder = await reader.readToEnd(); if (!remainder.length) remainder = ''; remainder = line + remainder; remainder = util.removeTrailingSpaces(remainder.replace(/\r/g, '')); const parts = remainder.split(reSplit); if (parts.length === 1) { throw new Error('Misformed armored text'); } const body = removeChecksum(parts[0].slice(0, -1)); await writer.write(body); break; } } await writer.ready; await writer.close(); } catch (e) { await writer.abort(e); } })); } catch (e) { reject(e); } }).then(async result => { if (stream.isArrayStream(result.data)) { result.data = await stream.readToEnd(result.data); } return result; });}
export function armor(messageType, body, partIndex, partTotal, customComment, config = defaultConfig) { let text; let hash; if (messageType === enums.armor.signed) { text = body.text; hash = body.hash; body = body.data; } const result = []; switch (messageType) { case enums.armor.multipartSection: result.push('-----BEGIN PGP MESSAGE, PART ' + partIndex + '/' + partTotal + '-----\n'); result.push(addheader(customComment, config)); result.push(base64.encode(body)); result.push('-----END PGP MESSAGE, PART ' + partIndex + '/' + partTotal + '-----\n'); break; case enums.armor.multipartLast: result.push('-----BEGIN PGP MESSAGE, PART ' + partIndex + '-----\n'); result.push(addheader(customComment, config)); result.push(base64.encode(body)); result.push('-----END PGP MESSAGE, PART ' + partIndex + '-----\n'); break; case enums.armor.signed: result.push('-----BEGIN PGP SIGNED MESSAGE-----\n'); result.push(hash ? `Hash: ${hash}\n\n` : '\n'); result.push(text.replace(/^-/mg, '- -')); result.push('\n-----BEGIN PGP SIGNATURE-----\n'); result.push(addheader(customComment, config)); result.push(base64.encode(body)); result.push('-----END PGP SIGNATURE-----\n'); break; case enums.armor.message: result.push('-----BEGIN PGP MESSAGE-----\n'); result.push(addheader(customComment, config)); result.push(base64.encode(body)); result.push('-----END PGP MESSAGE-----\n'); break; case enums.armor.publicKey: result.push('-----BEGIN PGP PUBLIC KEY BLOCK-----\n'); result.push(addheader(customComment, config)); result.push(base64.encode(body)); result.push('-----END PGP PUBLIC KEY BLOCK-----\n'); break; case enums.armor.privateKey: result.push('-----BEGIN PGP PRIVATE KEY BLOCK-----\n'); result.push(addheader(customComment, config)); result.push(base64.encode(body)); result.push('-----END PGP PRIVATE KEY BLOCK-----\n'); break; case enums.armor.signature: result.push('-----BEGIN PGP SIGNATURE-----\n'); result.push(addheader(customComment, config)); result.push(base64.encode(body)); result.push('-----END PGP SIGNATURE-----\n'); break; }
return util.concat(result);}