diff --git a/openpgp.d.ts b/openpgp.d.ts index e71e608dc..0e69341b8 100644 --- a/openpgp.d.ts +++ b/openpgp.d.ts @@ -14,10 +14,10 @@ export function readKey(data: Uint8Array): Promise; export function readArmoredKeys(armoredText: string): Promise; export function readKeys(data: Uint8Array): Promise; export function generateKey(options: KeyOptions): Promise; -export function generateSessionKey(options: { publicKeys: Key[], date?: Date, toUserIds?: UserId[] }): Promise; +export function generateSessionKey(options: { publicKeys: Key[], date?: Date, toUserIds?: UserID[] }): Promise; export function decryptKey(options: { privateKey: Key; passphrase?: string | string[]; }): Promise; export function encryptKey(options: { privateKey: Key; passphrase?: string | string[] }): Promise; -export function reformatKey(options: { privateKey: Key; userIds?: (string | UserId)[]; passphrase?: string; keyExpirationTime?: number; }): Promise; +export function reformatKey(options: { privateKey: Key; userIds?: UserID|UserID[]; passphrase?: string; keyExpirationTime?: number; }): Promise; export class Key { constructor(packetlist: PacketList); @@ -29,7 +29,7 @@ export class Key { public armor(): string; public decrypt(passphrase: string | string[], keyId?: Keyid): Promise; // throws on error public encrypt(passphrase: string | string[]): Promise; // throws on error - public getExpirationTime(capability?: 'encrypt' | 'encrypt_sign' | 'sign', keyId?: Keyid, userId?: UserId): Promise; // Returns null if `capabilities` is passed and the key does not have the specified capabilities or is revoked or invalid. + public getExpirationTime(capability?: 'encrypt' | 'encrypt_sign' | 'sign', keyId?: Keyid, userId?: UserID): Promise; // Returns null if `capabilities` is passed and the key does not have the specified capabilities or is revoked or invalid. public getKeyIds(): Keyid[]; public getPrimaryUser(): Promise; // throws on error public getUserIds(): string[]; @@ -41,8 +41,8 @@ export class Key { public isRevoked(): Promise; public revoke(reason: { flag?: enums.reasonForRevocation; string?: string; }, date?: Date): Promise; public getRevocationCertificate(): Promise | string | undefined>; - public getEncryptionKey(keyid?: Keyid, date?: Date | null, userId?: UserId): Promise; - public getSigningKey(keyid?: Keyid, date?: Date | null, userId?: UserId): Promise; + public getEncryptionKey(keyid?: Keyid, date?: Date | null, userId?: UserID): Promise; + public getSigningKey(keyid?: Keyid, date?: Date | null, userId?: UserID): Promise; public getKeys(keyId?: Keyid): (Key | SubKey)[]; public isDecrypted(): boolean; public getFingerprint(): string; @@ -419,6 +419,7 @@ export class OnePassSignaturePacket extends BasePacket { export class UserIDPacket extends BasePacket { public tag: enums.packet.userID; public userid: string; + static fromObject(userId: UserID): UserIDPacket; } export class SignaturePacket extends BasePacket { @@ -530,7 +531,7 @@ export namespace stream { /* ############## v5 GENERAL #################### */ -export interface UserId { name?: string; email?: string; comment?: string; } +export interface UserID { name?: string; email?: string; comment?: string; } export interface SessionKey { data: Uint8Array; algorithm: string; } @@ -558,9 +559,9 @@ interface EncryptOptions { /** (optional) use a key ID of 0 instead of the public key IDs */ wildcard?: boolean; /** (optional) user ID to sign with, e.g. { name:'Steve Sender', email:'steve@openpgp.org' } */ - fromUserId?: UserId; + fromUserId?: UserID; /** (optional) user ID to encrypt for, e.g. { name:'Robert Receiver', email:'robert@openpgp.org' } */ - toUserId?: UserId; + toUserId?: UserID; } interface DecryptOptions { @@ -592,7 +593,7 @@ interface SignOptions { dataType?: DataPacketType; detached?: boolean; date?: Date; - fromUserId?: UserId; + fromUserId?: UserID; } interface VerifyOptions { @@ -620,7 +621,7 @@ interface KeyPair { export type EllipticCurveName = 'ed25519' | 'curve25519' | 'p256' | 'p384' | 'p521' | 'secp256k1' | 'brainpoolP256r1' | 'brainpoolP384r1' | 'brainpoolP512r1'; interface KeyOptions { - userIds: UserId[]; // generating a key with no user defined results in error + userIds: UserID|UserID[]; passphrase?: string; type?: 'ecc' | 'rsa'; curve?: EllipticCurveName; @@ -890,10 +891,6 @@ declare namespace util { */ function hexToStr(hex: string): string; - function parseUserId(userid: string): UserId; - - function formatUserId(userid: UserId): string; - function normalizeDate(date: Date | null): Date | null; /** diff --git a/src/key/factory.js b/src/key/factory.js index 8cb08ce51..8f7bb6769 100644 --- a/src/key/factory.js +++ b/src/key/factory.js @@ -156,9 +156,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options) { return algos; } - const userIdPacket = new UserIDPacket(); - userIdPacket.format(userId); - + const userIdPacket = UserIDPacket.fromObject(userId); const dataToSign = {}; dataToSign.userId = userIdPacket; dataToSign.key = secretKeyPacket; diff --git a/src/message.js b/src/message.js index 74843fbbb..07884d8fa 100644 --- a/src/message.js +++ b/src/message.js @@ -289,7 +289,7 @@ export class Message { * Generate a new session key object, taking the algorithm preferences of the passed public keys into account, if any. * @param {Array} keys (optional) public key(s) to select algorithm preferences for * @param {Date} date (optional) date to select algorithm preferences at - * @param {Array} userIds (optional) user IDs to select algorithm preferences for + * @param {Array} userIds (optional) user IDs to select algorithm preferences for * @returns {Promise<{ data: Uint8Array, algorithm: String }>} object with session key data and algorithm * @async */ @@ -309,7 +309,7 @@ export class Message { * @param {Object} sessionKey (optional) session key in the form: { data:Uint8Array, algorithm:String, [aeadAlgorithm:String] } * @param {Boolean} wildcard (optional) use a key ID of 0 instead of the public key IDs * @param {Date} date (optional) override the creation date of the literal package - * @param {Array} userIds (optional) user IDs to encrypt for, e.g. [{ name:'Robert Receiver', email:'robert@openpgp.org' }] + * @param {Array} userIds (optional) user IDs to encrypt for, e.g. [{ name:'Robert Receiver', email:'robert@openpgp.org' }] * @param {Boolean} streaming (optional) whether to process data as a stream * @returns {Promise} new message with encrypted content * @async diff --git a/src/openpgp.js b/src/openpgp.js index b13434ffa..9036864c4 100644 --- a/src/openpgp.js +++ b/src/openpgp.js @@ -64,7 +64,7 @@ if (globalThis.ReadableStream) { /** * Generates a new OpenPGP key pair. Supports RSA and ECC keys. By default, primary and subkeys will be of same type. * @param {ecc|rsa} type (optional) The primary key algorithm type: ECC (default) or RSA - * @param {Array} userIds User IDs as strings or objects: 'Jo Doe ' or { name:'Jo Doe', email:'info@jo.com' } + * @param {Object|Array} userIds User IDs as objects: { name:'Jo Doe', email:'info@jo.com' } * @param {String} passphrase (optional) The passphrase used to encrypt the resulting private key * @param {Number} rsaBits (optional) Number of bits for RSA keys, defaults to 4096 * @param {String} curve (optional) Elliptic curve for ECC keys: @@ -104,7 +104,7 @@ export function generateKey({ userIds = [], passphrase = "", type = "ecc", rsaBi /** * Reformats signature packets for a key and rewraps key object. * @param {Key} privateKey Private key to reformat - * @param {Array} userIds User IDs as strings or objects: 'Jo Doe ' or { name:'Jo Doe', email:'info@jo.com' } + * @param {Object|Array} userIds User IDs as objects: { name:'Jo Doe', email:'info@jo.com' } * @param {String} passphrase (optional) The passphrase used to encrypt the resulting private key * @param {Number} keyExpirationTime (optional) Number of seconds from the key creation time after which the key expires * @returns {Promise} The generated key object in the form: @@ -222,8 +222,8 @@ export function encryptKey({ privateKey, passphrase }) { * @param {Signature} signature (optional) a detached signature to add to the encrypted message * @param {Boolean} wildcard (optional) use a key ID of 0 instead of the public key IDs * @param {Date} date (optional) override the creation date of the message signature - * @param {Array} fromUserIds (optional) array of user IDs to sign with, one per key in `privateKeys`, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] - * @param {Array} toUserIds (optional) array of user IDs to encrypt for, one per key in `publicKeys`, e.g. [{ name:'Robert Receiver', email:'robert@openpgp.org' }] + * @param {Array} fromUserIds (optional) array of user IDs to sign with, one per key in `privateKeys`, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] + * @param {Array} toUserIds (optional) array of user IDs to encrypt for, one per key in `publicKeys`, e.g. [{ name:'Robert Receiver', email:'robert@openpgp.org' }] * @returns {Promise|NodeStream|Uint8Array|ReadableStream|NodeStream>} (String if `armor` was true, the default; Uint8Array if `armor` was false) * @async * @static @@ -312,7 +312,7 @@ export function decrypt({ message, privateKeys, passwords, sessionKeys, publicKe * @param {'web'|'ponyfill'|'node'|false} streaming (optional) whether to return data as a stream. Defaults to the type of stream `message` was created from, if any. * @param {Boolean} detached (optional) if the return value should contain a detached signature * @param {Date} date (optional) override the creation date of the signature - * @param {Array} fromUserIds (optional) array of user IDs to sign with, one per key in `privateKeys`, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] + * @param {Array} fromUserIds (optional) array of user IDs to sign with, one per key in `privateKeys`, e.g. [{ name:'Steve Sender', email:'steve@openpgp.org' }] * @returns {Promise|NodeStream|Uint8Array|ReadableStream|NodeStream>} (String if `armor` was true, the default; Uint8Array if `armor` was false) * @async * @static diff --git a/src/packet/userid.js b/src/packet/userid.js index 0978dc13b..d8082ecaf 100644 --- a/src/packet/userid.js +++ b/src/packet/userid.js @@ -19,9 +19,11 @@ * @requires enums * @requires util */ +import emailAddresses from 'email-addresses'; import enums from '../enums'; import util from '../util'; +import config from '../config'; /** * Implementation of the User ID Packet (Tag 13) @@ -48,19 +50,42 @@ class UserIDPacket { } /** - * Parsing function for a user id packet (tag 13). - * @param {Uint8Array} input payload of a tag 13 packet + * Create UserIDPacket instance from object + * @param {Object} userId object specifying userId name, email and comment + * @returns {module:userid.UserIDPacket} + * @static */ - read(bytes) { - this.parse(util.decodeUtf8(bytes)); + static fromObject(userId) { + if (util.isString(userId) || + (userId.name && !util.isString(userId.name)) || + (userId.email && !util.isEmailAddress(userId.email)) || + (userId.comment && !util.isString(userId.comment))) { + throw new Error('Invalid user ID format'); + } + const packet = new UserIDPacket(); + Object.assign(packet, userId); + const components = []; + if (packet.name) components.push(packet.name); + if (packet.comment) components.push(`(${packet.comment})`); + if (packet.email) components.push(`<${packet.email}>`); + packet.userid = components.join(' '); + return packet; } /** - * Parse userid string, e.g. 'John Doe ' + * Parsing function for a user id packet (tag 13). + * @param {Uint8Array} input payload of a tag 13 packet */ - parse(userid) { + read(bytes) { + const userid = util.decodeUtf8(bytes); + if (userid.length > config.maxUseridLength) { + throw new Error('User ID string is too long'); + } try { - Object.assign(this, util.parseUserId(userid)); + const { name, address: email, comments } = emailAddresses.parseOneAddress({ input: userid, atInDisplayName: true }); + this.comment = comments.replace(/^\(|\)$/g, ''); + this.name = name; + this.email = email; } catch (e) {} this.userid = userid; } @@ -72,17 +97,6 @@ class UserIDPacket { write() { return util.encodeUtf8(this.userid); } - - /** - * Set userid string from object, e.g. { name:'Phil Zimmermann', email:'phil@openpgp.org' } - */ - format(userid) { - if (util.isString(userid)) { - userid = util.parseUserId(userid); - } - Object.assign(this, userid); - this.userid = util.formatUserId(userid); - } } export default UserIDPacket; diff --git a/src/util.js b/src/util.js index 5c037ec1c..1d5f97a16 100644 --- a/src/util.js +++ b/src/util.js @@ -26,7 +26,6 @@ * @module util */ -import emailAddresses from 'email-addresses'; import stream from 'web-stream-tools'; import config from './config'; import util from './util'; // re-import module to access util functions @@ -598,44 +597,6 @@ export default { return re.test(data); }, - /** - * Format user id for internal use. - */ - formatUserId: function(id) { - // name, email address and comment can be empty but must be of the correct type - if ((id.name && !util.isString(id.name)) || - (id.email && !util.isEmailAddress(id.email)) || - (id.comment && !util.isString(id.comment))) { - throw new Error('Invalid user id format'); - } - const components = []; - if (id.name) { - components.push(id.name); - } - if (id.comment) { - components.push(`(${id.comment})`); - } - if (id.email) { - components.push(`<${id.email}>`); - } - return components.join(' '); - }, - - /** - * Parse user id. - */ - parseUserId: function(userid) { - if (userid.length > config.maxUseridLength) { - throw new Error('User id string is too long'); - } - try { - const { name, address: email, comments } = emailAddresses.parseOneAddress({ input: userid, atInDisplayName: true }); - return { name, email, comment: comments.replace(/^\(|\)$/g, '') }; - } catch (e) { - throw new Error('Invalid user id format'); - } - }, - /** * Normalize line endings to * Support any encoding where CR=0x0D, LF=0x0A diff --git a/test/general/key.js b/test/general/key.js index 9c072fa95..ef3e3c250 100644 --- a/test/general/key.js +++ b/test/general/key.js @@ -2103,7 +2103,7 @@ function versionSpecificTests() { } expect(key.users[0].selfCertifications[0].features).to.eql(expectedFeatures); }; - const opt = { userIds: 'test ', passphrase: 'hello' }; + const opt = { userIds: { name: 'test', email: 'a@b.com' }, passphrase: 'hello' }; return openpgp.generateKey(opt).then(async function(key) { testPref(key.key); testPref(await openpgp.readArmoredKey(key.publicKeyArmored)); @@ -2148,7 +2148,7 @@ function versionSpecificTests() { } expect(key.users[0].selfCertifications[0].features).to.eql(expectedFeatures); }; - const opt = { userIds: 'test ', passphrase: 'hello' }; + const opt = { userIds: { name: 'test', email: 'a@b.com' }, passphrase: 'hello' }; try { const key = await openpgp.generateKey(opt); testPref(key.key); @@ -2162,7 +2162,7 @@ function versionSpecificTests() { }); it('Generated key is not unlocked by default', function() { - const opt = { userIds: 'test ', passphrase: '123' }; + const opt = { userIds: { name: 'test', email: 'a@b.com' }, passphrase: '123' }; let key; return openpgp.generateKey(opt).then(function(newKey) { key = newKey.key; @@ -2259,27 +2259,27 @@ function versionSpecificTests() { }); it('Generate key - multi userid', function() { - const userId1 = 'test '; - const userId2 = 'test '; + const userId1 = { name: 'test', email: 'a@b.com' }; + const userId2 = { name: 'test', email: 'b@c.com' }; const opt = { userIds: [userId1, userId2], passphrase: '123' }; return openpgp.generateKey(opt).then(function(key) { key = key.key; expect(key.users.length).to.equal(2); - expect(key.users[0].userId.userid).to.equal(userId1); + expect(key.users[0].userId.userid).to.equal('test '); expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; - expect(key.users[1].userId.userid).to.equal(userId2); + expect(key.users[1].userId.userid).to.equal('test '); expect(key.users[1].selfCertifications[0].isPrimaryUserID).to.be.null; }); }); it('Generate key - default values', function() { - const userId = 'test '; + const userId = { name: 'test', email: 'a@b.com' }; const opt = { userIds: [userId] }; return openpgp.generateKey(opt).then(function({ key }) { expect(key.isDecrypted()).to.be.true; expect(key.getAlgorithmInfo().algorithm).to.equal('eddsa'); expect(key.users.length).to.equal(1); - expect(key.users[0].userId.userid).to.equal(userId); + expect(key.users[0].userId.userid).to.equal('test '); expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; expect(key.subKeys).to.have.length(1); expect(key.subKeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh'); @@ -2287,12 +2287,12 @@ function versionSpecificTests() { }); it('Generate key - two subkeys with default values', function() { - const userId = 'test '; + const userId = { name: 'test', email: 'a@b.com' }; const opt = { userIds: [userId], passphrase: '123', subkeys:[{},{}] }; return openpgp.generateKey(opt).then(function(key) { key = key.key; expect(key.users.length).to.equal(1); - expect(key.users[0].userId.userid).to.equal(userId); + expect(key.users[0].userId.userid).to.equal('test '); expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; expect(key.subKeys).to.have.length(2); expect(key.subKeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh'); @@ -2305,12 +2305,12 @@ function versionSpecificTests() { const minRsaBits = openpgp.config.minRsaBits; openpgp.config.minRsaBits = rsaBits; - const userId = 'test '; + const userId = { name: 'test', email: 'a@b.com' }; const opt = { type: 'rsa', rsaBits, userIds: [userId], passphrase: '123', subkeys:[{},{}] }; try { const { key } = await openpgp.generateKey(opt); expect(key.users.length).to.equal(1); - expect(key.users[0].userId.userid).to.equal(userId); + expect(key.users[0].userId.userid).to.equal('test '); expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; expect(key.subKeys).to.have.length(2); expect(key.subKeys[0].getAlgorithmInfo().algorithm).to.equal('rsaEncryptSign'); @@ -2321,12 +2321,12 @@ function versionSpecificTests() { }); it('Generate key - one signing subkey', function() { - const userId = 'test '; + const userId = { name: 'test', email: 'a@b.com' }; const opt = { userIds: [userId], passphrase: '123', subkeys:[{}, { sign: true }] }; return openpgp.generateKey(opt).then(async function({ privateKeyArmored }) { const key = await openpgp.readArmoredKey(privateKeyArmored); expect(key.users.length).to.equal(1); - expect(key.users[0].userId.userid).to.equal(userId); + expect(key.users[0].userId.userid).to.equal('test '); expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; expect(key.subKeys).to.have.length(2); expect(key.subKeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh'); @@ -2337,7 +2337,7 @@ function versionSpecificTests() { }); it('Reformat key - one signing subkey', function() { - const userId = 'test '; + const userId = { name: 'test', email: 'a@b.com' }; const opt = { userIds: [userId], passphrase: '123', subkeys:[{}, { sign: true }] }; return openpgp.generateKey(opt).then(async function({ key }) { await key.decrypt('123'); @@ -2345,7 +2345,7 @@ function versionSpecificTests() { }).then(async function({ privateKeyArmored }) { const key = await openpgp.readArmoredKey(privateKeyArmored); expect(key.users.length).to.equal(1); - expect(key.users[0].userId.userid).to.equal(userId); + expect(key.users[0].userId.userid).to.equal('test '); expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; expect(key.subKeys).to.have.length(2); expect(key.subKeys[0].getAlgorithmInfo().algorithm).to.equal('ecdh'); @@ -2360,12 +2360,12 @@ function versionSpecificTests() { const minRsaBits = openpgp.config.minRsaBits; openpgp.config.minRsaBits = rsaBits; - const userId = 'test '; + const userId = { name: 'test', email: 'a@b.com' }; const opt = { type: 'rsa', rsaBits, userIds: [userId], passphrase: '123', subkeys:[{ type: 'ecc', curve: 'curve25519' }] }; try { const { key } = await openpgp.generateKey(opt); expect(key.users.length).to.equal(1); - expect(key.users[0].userId.userid).to.equal(userId); + expect(key.users[0].userId.userid).to.equal('test '); expect(key.users[0].selfCertifications[0].isPrimaryUserID).to.be.true; expect(key.getAlgorithmInfo().algorithm).to.equal('rsaEncryptSign'); expect(key.getAlgorithmInfo().bits).to.equal(opt.rsaBits); @@ -2376,7 +2376,7 @@ function versionSpecificTests() { }); it('Encrypt key with new passphrase', async function() { - const userId = 'test '; + const userId = { name: 'test', email: 'a@b.com' }; const opt = { userIds: userId, passphrase: 'passphrase' }; const key = (await openpgp.generateKey(opt)).key; const armor1 = key.armor(); @@ -2396,7 +2396,7 @@ function versionSpecificTests() { it('Generate key - ensure keyExpirationTime works', function() { const expect_delta = 365 * 24 * 60 * 60; - const userId = 'test '; + const userId = { name: 'test', email: 'a@b.com' }; const opt = { userIds: userId, passphrase: '123', keyExpirationTime: expect_delta }; return openpgp.generateKey(opt).then(async function(key) { key = key.key; @@ -2494,46 +2494,46 @@ function versionSpecificTests() { }); it('Reformat key without passphrase', function() { - const userId1 = 'test1 '; - const userId2 = 'test2 '; + const userId1 = { name: 'test', email: 'a@b.com' }; + const userId2 = { name: 'test', email: 'b@c.com' }; const opt = { userIds: userId1 }; return openpgp.generateKey(opt).then(function(key) { key = key.key; expect(key.users.length).to.equal(1); - expect(key.users[0].userId.userid).to.equal(userId1); + expect(key.users[0].userId.userid).to.equal('test '); expect(key.isDecrypted()).to.be.true; opt.privateKey = key; opt.userIds = userId2; return openpgp.reformatKey(opt).then(function(newKey) { newKey = newKey.key; expect(newKey.users.length).to.equal(1); - expect(newKey.users[0].userId.userid).to.equal(userId2); + expect(newKey.users[0].userId.userid).to.equal('test '); expect(newKey.isDecrypted()).to.be.true; }); }); }); it('Reformat key with no subkey with passphrase', async function() { - const userId = 'test1 '; + const userId = { name: 'test', email: 'a@b.com' }; const key = await openpgp.readArmoredKey(key_without_subkey); const opt = { privateKey: key, userIds: [userId], passphrase: "test" }; return openpgp.reformatKey(opt).then(function(newKey) { newKey = newKey.key; expect(newKey.users.length).to.equal(1); - expect(newKey.users[0].userId.userid).to.equal(userId); + expect(newKey.users[0].userId.userid).to.equal('test '); expect(newKey.isDecrypted()).to.be.false; }); }); it('Reformat key with two subkeys with passphrase', function() { - const userId1 = 'test '; - const userId2 = 'test '; + const userId1 = { name: 'test', email: 'a@b.com' }; + const userId2 = { name: 'test', email: 'b@c.com' }; const now = util.normalizeDate(new Date()); const before = util.normalizeDate(new Date(0)); const opt1 = { userIds: [userId1], date: now }; return openpgp.generateKey(opt1).then(function(newKey) { newKey = newKey.key; - expect(newKey.users[0].userId.userid).to.equal(userId1); + expect(newKey.users[0].userId.userid).to.equal('test '); expect(+newKey.getCreationTime()).to.equal(+now); expect(+newKey.subKeys[0].getCreationTime()).to.equal(+now); expect(+newKey.subKeys[0].bindingSignatures[0].created).to.equal(+now); @@ -2541,20 +2541,20 @@ function versionSpecificTests() { return openpgp.reformatKey(opt2).then(function(refKey) { refKey = refKey.key; expect(refKey.users.length).to.equal(1); - expect(refKey.users[0].userId.userid).to.equal(userId2); + expect(refKey.users[0].userId.userid).to.equal('test '); expect(+refKey.subKeys[0].bindingSignatures[0].created).to.equal(+before); }); }); }); it('Reformat key with no subkey without passphrase', async function() { - const userId = 'test1 '; + const userId = { name: 'test', email: 'a@b.com' }; const key = await openpgp.readArmoredKey(key_without_subkey); const opt = { privateKey: key, userIds: [userId] }; return openpgp.reformatKey(opt).then(function(newKey) { newKey = newKey.key; expect(newKey.users.length).to.equal(1); - expect(newKey.users[0].userId.userid).to.equal(userId); + expect(newKey.users[0].userId.userid).to.equal('test '); expect(newKey.isDecrypted()).to.be.true; return openpgp.sign({ message: openpgp.CleartextMessage.fromText('hello'), privateKeys: newKey, armor: true }).then(async function(signed) { return openpgp.verify( @@ -2570,9 +2570,9 @@ function versionSpecificTests() { }); it('Reformat and encrypt key', function() { - const userId1 = 'test1 '; - const userId2 = 'test2 '; - const userId3 = 'test3 '; + const userId1 = { name: 'test1', email: 'a@b.com' }; + const userId2 = { name: 'test2', email: 'b@c.com' }; + const userId3 = { name: 'test3', email: 'c@d.com' }; const opt = { userIds: userId1 }; return openpgp.generateKey(opt).then(function(key) { key = key.key; @@ -2582,7 +2582,7 @@ function versionSpecificTests() { return openpgp.reformatKey(opt).then(async function(newKey) { newKey = newKey.key; expect(newKey.users.length).to.equal(2); - expect(newKey.users[0].userId.userid).to.equal(userId2); + expect(newKey.users[0].userId.userid).to.equal('test2 '); expect(newKey.isDecrypted()).to.be.false; await newKey.decrypt('123'); expect(newKey.isDecrypted()).to.be.true; @@ -2591,8 +2591,8 @@ function versionSpecificTests() { }); it('Sign and encrypt with reformatted key', function() { - const userId1 = 'test1 '; - const userId2 = 'test2 '; + const userId1 = { name: 'test1', email: 'a@b.com' }; + const userId2 = { name: 'test2', email: 'b@c.com' }; const opt = { userIds: userId1 }; return openpgp.generateKey(opt).then(function(key) { key = key.key; @@ -2611,9 +2611,9 @@ function versionSpecificTests() { }); it('Reject with user-friendly error when reformatting encrypted key', function() { - const opt = { userIds: 'test1 ', passphrase: '1234' }; + const opt = { userIds: { name: 'test', email: 'a@b.com' }, passphrase: '1234' }; return openpgp.generateKey(opt).then(function(original) { - return openpgp.reformatKey({ privateKey: original.key, userIds: 'test2 ', passphrase: '1234' }).then(function() { + return openpgp.reformatKey({ privateKey: original.key, userIds: { name: 'test2', email: 'a@b.com' }, passphrase: '1234' }).then(function() { throw new Error('reformatKey should result in error when key not decrypted'); }).catch(function(error) { expect(error.message).to.equal('Error reformatting keypair: Key not decrypted'); @@ -2622,7 +2622,7 @@ function versionSpecificTests() { }); it('Revoke generated key with revocation certificate', function() { - const opt = { userIds: 'test1 ', passphrase: '1234' }; + const opt = { userIds: { name: 'test', email: 'a@b.com' }, passphrase: '1234' }; return openpgp.generateKey(opt).then(function(original) { return openpgp.revokeKey({ key: original.key.toPublic(), revocationCertificate: original.revocationCertificate }).then(async function(revKey) { revKey = revKey.publicKey; @@ -2634,7 +2634,7 @@ function versionSpecificTests() { }); it('Revoke generated key with private key', function() { - const opt = { userIds: 'test1 ', passphrase: '1234' }; + const opt = { userIds: { name: 'test', email: 'a@b.com' }, passphrase: '1234' }; return openpgp.generateKey(opt).then(async function(original) { await original.key.decrypt('1234'); return openpgp.revokeKey({ key: original.key, reasonForRevocation: { string: 'Testing key revocation' } }).then(async function(revKey) { @@ -2926,14 +2926,14 @@ module.exports = () => describe('Key', function() { }); it('makeDummy() - the converted key can be parsed', async function() { - const { key } = await openpgp.generateKey({ userIds: 'dummy ' }); + const { key } = await openpgp.generateKey({ userIds: { name: 'dummy', email: 'dummy@alice.com' } }); key.primaryKey.makeDummy(); const parsedKeys = await openpgp.readArmoredKey(key.armor()); expect(parsedKeys).to.not.be.empty; }); it('makeDummy() - the converted key can be encrypted and decrypted', async function() { - const { key } = await openpgp.generateKey({ userIds: 'dummy ' }); + const { key } = await openpgp.generateKey({ userIds: { name: 'dummy', email: 'dummy@alice.com' } }); const passphrase = 'passphrase'; key.primaryKey.makeDummy(); expect(key.isDecrypted()).to.be.true; @@ -2950,7 +2950,7 @@ module.exports = () => describe('Key', function() { key.primaryKey.makeDummy(); expect(key.primaryKey.isDummy()).to.be.true; await key.validate(); - await expect(openpgp.reformatKey({ privateKey: key, userIds: 'test2 ' })).to.be.rejectedWith(/Missing key parameters/); + await expect(openpgp.reformatKey({ privateKey: key, userIds: { name: 'test', email: 'a@b.com' } })).to.be.rejectedWith(/Missing key parameters/); }); it('makeDummy() - subkeys of the converted key can still sign', async function() { @@ -3361,7 +3361,7 @@ VYGdb3eNlV8CfoEC // Set first user to primary. We won't select this user, this is to test that. privateKey.users[0].selfCertifications[0].isPrimaryUserID = true; // Change userid of the first user so that we don't select it. This also makes this user invalid. - privateKey.users[0].userId.parse('Test User '); + privateKey.users[0].userId = openpgp.UserIDPacket.fromObject({ name: 'Test User', email: 'b@c.com' }); // Set second user to prefer aes128. We will select this user. privateKey.users[1].selfCertifications[0].preferredHashAlgorithms = [openpgp.enums.hash.sha512]; const signed = await openpgp.sign({ message: openpgp.Message.fromText('hello'), privateKeys: privateKey, fromUserIds: { name: 'Test McTestington', email: 'test@example.com' }, armor: false }); @@ -3487,7 +3487,7 @@ VYGdb3eNlV8CfoEC }); it('Add a new default subkey to an rsaSign key', async function() { - const userId = 'test '; + const userId = { name: 'test', email: 'a@b.com' }; const opt = { type: 'rsa', rsaBits, userIds: [userId], subkeys: [] }; const { key } = await openpgp.generateKey(opt); expect(key.subKeys).to.have.length(0); @@ -3497,7 +3497,7 @@ VYGdb3eNlV8CfoEC }); it('Add a new default subkey to an ecc key', async function() { - const userId = 'test '; + const userId = { name: 'test', email: 'a@b.com' }; const opt = { type: 'ecc', userIds: [userId], subkeys: [] }; const { key } = await openpgp.generateKey(opt); expect(key.subKeys).to.have.length(0); @@ -3537,8 +3537,8 @@ VYGdb3eNlV8CfoEC await subKey.verify(importedPrivateKey.primaryKey); }); - it('create and add a new eddsa subkey to a eddsa key', async function() { - const userId = 'test '; + it('create and add a new ec subkey to a ec key', async function() { + const userId = { name: 'test', email: 'a@b.com' }; const opt = { curve: 'curve25519', userIds: [userId], subkeys:[] }; const privateKey = (await openpgp.generateKey(opt)).key; const total = privateKey.subKeys.length; @@ -3562,7 +3562,7 @@ VYGdb3eNlV8CfoEC }); it('create and add a new ecdsa subkey to a eddsa key', async function() { - const userId = 'test '; + const userId = { name: 'test', email: 'a@b.com' }; const opt = { curve: 'ed25519', userIds: [userId], subkeys:[] }; const privateKey = (await openpgp.generateKey(opt)).key; const total = privateKey.subKeys.length; @@ -3596,7 +3596,7 @@ VYGdb3eNlV8CfoEC }); it('create and add a new rsa subkey to a ecc key', async function() { - const userId = 'test '; + const userId = { name: 'test', email: 'a@b.com' }; const opt = { curve: 'ed25519', userIds: [userId], subkeys:[] }; const privateKey = (await openpgp.generateKey(opt)).key; const total = privateKey.subKeys.length; @@ -3625,7 +3625,7 @@ VYGdb3eNlV8CfoEC }); it('sign/verify data with the new subkey correctly using curve25519', async function() { - const userId = 'test '; + const userId = { name: 'test', email: 'a@b.com' }; const opt = { curve: 'curve25519', userIds: [userId], subkeys:[] }; const privateKey = (await openpgp.generateKey(opt)).key; const total = privateKey.subKeys.length; @@ -3650,7 +3650,7 @@ VYGdb3eNlV8CfoEC }); it('encrypt/decrypt data with the new subkey correctly using curve25519', async function() { - const userId = 'test '; + const userId = { name: 'test', email: 'a@b.com' }; const vData = 'the data to encrypted!'; const opt = { curve: 'curve25519', userIds: [userId], subkeys:[] }; const privateKey = (await openpgp.generateKey(opt)).key; diff --git a/test/general/openpgp.js b/test/general/openpgp.js index 2feb8b7ca..90ffe35ac 100644 --- a/test/general/openpgp.js +++ b/test/general/openpgp.js @@ -557,7 +557,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { userIds: [{ name: {}, email: 'text@example.com' }] }; const test = openpgp.generateKey(opt); - await expect(test).to.eventually.be.rejectedWith(/Invalid user id format/); + await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/); }); it('should fail for invalid user email address', async function() { @@ -565,7 +565,7 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { userIds: [{ name: 'Test User', email: 'textexample.com' }] }; const test = openpgp.generateKey(opt); - await expect(test).to.eventually.be.rejectedWith(/Invalid user id format/); + await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/); }); it('should fail for invalid user email address', async function() { @@ -573,61 +573,39 @@ module.exports = () => describe('OpenPGP.js public api tests', function() { userIds: [{ name: 'Test User', email: 'text@examplecom' }] }; const test = openpgp.generateKey(opt); - await expect(test).to.eventually.be.rejectedWith(/Invalid user id format/); + await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/); }); - it('should fail for invalid string user id', async function() { - const opt = { - userIds: ['Test User text@example.com>'] - }; - const test = openpgp.generateKey(opt); - await expect(test).to.eventually.be.rejectedWith(/Invalid user id format/); - }); - - it('should fail for invalid single string user id', async function() { - const opt = { - userIds: 'Test User text@example.com>' - }; - const test = openpgp.generateKey(opt); - await expect(test).to.eventually.be.rejectedWith(/Invalid user id format/); - }); - - it('should work for valid single string user id', function() { + it('should fail for string user ID', async function() { const opt = { userIds: 'Test User ' }; - return openpgp.generateKey(opt); - }); - - it('should work for valid string user id', function() { - const opt = { - userIds: ['Test User '] - }; - return openpgp.generateKey(opt); + const test = openpgp.generateKey(opt); + await expect(test).to.eventually.be.rejectedWith(/Invalid user ID format/); }); - it('should work for valid single user id hash', function() { + it('should work for valid single user ID object', function() { const opt = { userIds: { name: 'Test User', email: 'text@example.com' } }; return openpgp.generateKey(opt); }); - it('should work for valid single user id hash', function() { + it('should work for array of user ID objects', function() { const opt = { userIds: [{ name: 'Test User', email: 'text@example.com' }] }; return openpgp.generateKey(opt); }); - it('should work for an empty name', function() { + it('should work for undefined name', function() { const opt = { userIds: { email: 'text@example.com' } }; return openpgp.generateKey(opt); }); - it('should work for an empty email address', function() { + it('should work for an undefined email address', function() { const opt = { userIds: { name: 'Test User' } }; @@ -2463,7 +2441,7 @@ amnR6g== curves.forEach(curve => { it(`sign/verify with ${curve}`, async function() { const plaintext = 'short message'; - const key = (await openpgp.generateKey({ curve, userIds: 'Alice ' })).key; + const key = (await openpgp.generateKey({ curve, userIds: { name: 'Alice', email: 'info@alice.com' } })).key; const signed = await openpgp.sign({ privateKeys:[key], message: openpgp.CleartextMessage.fromText(plaintext) }); const verified = await openpgp.verify({ publicKeys:[key], message: await openpgp.readArmoredCleartextMessage(signed) }); expect(verified.signatures[0].valid).to.be.true; diff --git a/test/general/util.js b/test/general/util.js index 68648bb0d..7bc4ae6ec 100644 --- a/test/general/util.js +++ b/test/general/util.js @@ -142,22 +142,6 @@ module.exports = () => describe('Util unit tests', function() { }); }); - describe('parseUserID', function() { - it('should parse email address', function() { - const email = "TestName Test "; - const result = util.parseUserId(email); - expect(result.name).to.equal('TestName Test'); - expect(result.email).to.equal('test@example.com'); - }); - it('should parse email address with @ in display name and comment', function() { - const email = "Test@Name Test (a comment) "; - const result = util.parseUserId(email); - expect(result.name).to.equal('Test@Name Test'); - expect(result.email).to.equal('test@example.com'); - expect(result.comment).to.equal('a comment'); - }); - }); - describe("Misc.", function() { it('util.readNumber should not overflow until full range of uint32', function () { const ints = [Math.pow(2, 20), Math.pow(2, 25), Math.pow(2, 30), Math.pow(2, 32) - 1]; diff --git a/test/security/subkey_trust.js b/test/security/subkey_trust.js index c85e22389..ec97865f9 100644 --- a/test/security/subkey_trust.js +++ b/test/security/subkey_trust.js @@ -10,7 +10,7 @@ const expect = chai.expect; async function generateTestData() { const victimPrivKey = await key.generate({ - userIds: ['Victim '], + userIds: [{ name: 'Victim', email: 'victim@example.com' }], type: 'rsa', rsaBits: 1024, subkeys: [{ @@ -20,7 +20,7 @@ async function generateTestData() { victimPrivKey.revocationSignatures = []; const attackerPrivKey = await key.generate({ - userIds: ['Attacker '], + userIds: [{ name: 'Attacker', email: 'attacker@example.com' }], type: 'rsa', rsaBits: 1024, subkeys: [],