-
Notifications
You must be signed in to change notification settings - Fork 799
V5 Changelog
Daniel Huigens edited this page Jan 10, 2023
·
20 revisions
- Improve the security of newly generated keys:
- Generate ECC keys by default (#1065)
- RSA keys can still be generated with the new
type
parameter ofgenerateKey
: (#1179)import { generateKey } from 'openpgp'; const { privateKey } = await generateKey({ userIDs: [{ name: 'Test', email: 'test@email' }], type: 'rsa' });
- Newly generated RSA keys are now 4096-bits by default
- Remove SHA-1 from the default preferred hash algorithms (#1067)
- Remove 3DES and CAST5 from the default preferred symmetric algorithms (#1068)
- Reject certain insecure keys by default:
- Add
config.rejectPublicKeyAlgorithms
, and default to disallowing the use of ElGamal and DSA for encrypting new messages and signing and verifying signatures, respectively (#1264) - Reject RSA keys with fewer than
config.minRSABits
bits (defaulting to 2048) when encrypting new messages and signing and verifying signatures, not just on key generation (#1264) - If you want to make an exception for a certain key or algorithm, rather than adjusting the global
openpgp.config
, you can now pass aconfig
to a single function call, perhaps after warning the user / confirming that they want to allow this; for example:Of course, if at all possible, it's better to (ask users to) generate new keys, instead.import { createMessage, encrypt } from 'openpgp'; const message = await createMessage('Hello world!'); try { await encrypt({ message, encryptionKeys: publicKeys }); } catch (err) { if (err.message.includes('...') && confirm('Warning: keys are insecure. Use anyway?')) { await encrypt({ message, encryptionKeys: publicKeys, config: { minRSABits: 1024, rejectPublicKeyAlgorithms: new Set() } }); } else { throw err; } }
- Add
- … and a few smaller configuration changes
-
openpgp.HKP
has been moved to a separate package: openpgpjs/hkp-client -
openpgp.WKD
has been moved to a separate package: openpgpjs/wkd-client -
openpgp.Keyring
andLocalStore
have been removed, because keyring handling and storage should be handled in the application, as localStorage may not meet the durability requirements of the application. - The built-in Web Worker and
openpgp.createWorker
have been removed (for the rationale, please see #964) - The "lightweight build" has been greatly reduced in size. The lightweight build loads certain dependencies on demand only when needed:
- The indutny/elliptic library is loaded only when using or generating an ECC key with a brainpool or secp256k1 curve (or the NIST curves if Web Crypto is not supported). When using Curve25519/Ed25519, "elliptic" is never loaded.
- The indutny/bn.js library is only loaded when the native
BigInt
API is not supported (see caniuse.com/bigint), and/or when the elliptic library is loaded (as it depends on bn.js). - The web-streams-polyfill and web-streams-adapter libraries are only loaded when streaming is used, and only if
TransformStream
s aren't natively supported (see caniuse.com/transformstream). - To use the lightweight build:
import * as openpgp from 'openpgp/lightweight';
- OpenPGP.js has been refactored to be more amenable to dead-code elimination, such that it's now more beneficial to only import the functions you need. For example, if you only import
generateKey
, support for encrypting and compressing messages is not needed, reducing the size of the build when using a build system such as webpack:import { generateKey } from 'openpgp/lightweight';
- Replace
openpgp.key.read/readArmored
withopenpgp.readKey
-
openpgp.readKey
now takes an options object (either{ armoredKey }
or{ binaryKey }
) - It now only returns a single key object, rather than a
{ keys: [key...], err }
object - It now throws an error if the key failed to parse or if the key block contains multiple keys
-
- Add
openpgp.readKeys
- It takes an options object (either
{ armoredKeys }
or{ binaryKeys }
) - It returns an array of key objects
- It throws if any of the keys in the key block failed to parse
- It takes an options object (either
- Add
openpgp.readPrivateKey
andopenpgp.readPrivateKeys
- These behave identically to
readKey
andreadKeys
, but additionally guarantee that a private key is returned - They throw an error if the parsed key is a public key
- These behave identically to
- Remove
Key.prototype.encrypt/decrypt
in favor ofopenpgp.encryptKey/decryptKey
- These functions take an option object (
{ privateKey, passphrase }
) - They return a clone of the key object, rather than mutating the key
- This eliminates the possibility of ending up in an inconsistent state where
Key.prototype.decrypt
would throw when decrypting one subkey after already decrypting (and mutating) others
- These functions take an option object (
- Replace
openpgp.message.read/readArmored
withopenpgp.readMessage
,openpgp.signature.read/readArmored
withopenpgp.readSignature
, andopenpgp.cleartext.readArmored
withopenpgp.readCleartextMessage
-
openpgp.readMessage
now takes an options object (either{ armoredMessage }
or{ binaryMessage }
) -
openpgp.readSignature
now takes an options object (either{ armoredSignature }
or{ binarySignature }
) -
openpgp.readCleartextMessage
now takes an options object ({ cleartextMessage }
)
-
- Replace
openpgp.message.fromText/fromBinary
withopenpgp.createMessage
, andopenpgp.cleartext.fromText
withopenpgp.createCleartextMessage
-
openpgp.createMessage
now takes an options object (either{ text }
or{ binary }
) -
openpgp.createCleartextMessage
now takes an options object ({ text }
) - Both of these functions are now
async
, and need to beawait
ed, like all the other top-level functions
-
- In
openpgp.generateKey
,reformatKey
, andrevokeKey
, return a simplified "key pair" object:- The returned object now contains
privateKey
andpublicKey
properties, and no longer containskey
,privateKeyArmored
, andpublicKeyArmored
- These functions now take the option
format: 'armored' | 'binary' | 'object'
to determine the format ofprivateKey
andprivateKey
(defaulting to'armored'
) - The object returned from
generateKey
andreformatKey
still contains arevocationCertificate
property, which is not affected by theformat
option
- The returned object now contains
- In
openpgp.encrypt
,sign
,encryptSessionKey
,encryptKey
anddecryptKey
, return the result directly without wrapping it in a "results" object - In all top-level functions, rename
public/privateKeys
toencryption/decryption/signing/verificationKeys
depending on their use- In
openpgp.encrypt
,encryptSessionKey
, andgenerateSessionKey
(see below),publicKeys
is now calledencryptionKeys
- In
openpgp.encrypt
andsign
,privateKeys
is now calledsigningKeys
- In
openpgp.decrypt
anddecryptSessionKeys
,privateKeys
is now calleddecryptionKeys
- In
openpgp.decrypt
andverify
,publicKeys
is now calledverificationKeys
- Similarly, rename
toUserIDs
toencryptionUserIDs
andfromUserIDs
tosigningUserIDs
- In
- Remove the
detached
option ofopenpgp.encrypt
. You can separately callopenpgp.sign({ message, privateKeys, detached: true })
instead (don't forget to remove theprivateKeys
option fromopenpgp.encrypt
as well if you do so, if you don't want the message to be signed inline). However, note that storing detached signatures of plaintext data together with the encrypted data is not secure - Add a new
openpgp.generateSessionKey
function - Remove the
returnSessionKey
option ofopenpgp.encrypt
. You can separately callopenpgp.generateSessionKey({ encryptionKeys: publicKeys })
instead and callopenpgp.encrypt({ sessionKey })
with the result. - Remove the
streaming
option ofopenpgp.encrypt
,decrypt
,sign
andverify
. These functions now only use streaming when aReadableStream
or Node.jsReadable
stream is passed. - Replace the
armor
option inopenpgp.encrypt
andsign
with aformat
option accepting the values'armored'
(default),'binary'
or'object'
:-
format: 'armored'
is equivalent toarmor: true
-
format: 'object'
is equivalent toarmor: false
-
format: 'binary'
returns the message serialized as a Uint8Array (this was not supported before)
-
- Add an
expectSigned
option toopenpgp.encrypt
andverify
, which causes these functions to throw if there was no valid signature in the message. - Throughout the API,
userId
has been changed touserID
, andkeyId
has been changed tokeyID
. This affects theuserIDs
option ofopenpgp.generateKey
, thefromUserIDs
andtoUserIDs
options ofopenpgp.encrypt
anddecrypt
,Key.prototype.getKeyID
andgetKeyIDs
,Message.prototype.getEncryptionKeyID
andgetSigningKeyID
, etc. - Rename all
openpgp.enum.*.value_names
to camelCaseopenpgp.enum.*.valueNames
(#1093) - Remove
openpgp.util
(#1175)
- Rename all
openpgp.config.option_names
to camelCaseopenpgp.config.optionNames
(#1088)- Rename
openpgp.config.versionstring
toversionString
, andcommentstring
tocommentString
- Rename
- Don't add version and comment strings to armored messages and keys by default
- Rename
openpgp.config.ignore_mdc_error
toallowUnauthenticatedMessages
, and add a warning in the documentation that this option is insecure - Remove the option to generate non-integrity-protected messages (
openpgp.config.integrityProtect
) -
openpgp.config.aeadProtect
now controls whether private key encryption uses AEAD; previously this was dependent on the key version, and could only be used for v5 keys - All top-level functions (including the new ones mentioned above) now take a
config
property of theiroptions
parameter- This can be helpful when you want to change the configuration for a single function call, rather than all OpenPGP.js function cals
- For example, you can now generate a single V5 key as follows:
const { privateKey } = await openpgp.generateKey({ userIDs: [{ name: 'Test', email: 'test@email' }], config: { v5Keys: true } });
- Config options that aren't passed still default to
openpgp.config
- Rename
openpgp.packet.*
toopenpgp.*Packet
- Rename
openpgp.packet.Userid
toopenpgp.UserIDPacket
- Rename
openpgp.packet.Literal
toopenpgp.LiteralDataPacket
- Rename
openpgp.packet.Compressed
toopenpgp.CompressedDataPacket
- Rename
openpgp.packet.SymmetricallyEncrypted
toopenpgp.SymmetricallyEncryptedDataPacket
- Rename
openpgp.packet.SymEncryptedIntegrityProtected
toopenpgp.SymEncryptedIntegrityProtectedDataPacket
- Rename
openpgp.packet.SymEncryptedAEADProtected
toopenpgp.AEADEncryptedDataPacket
- Rename
openpgp.enums.packet.userid
toopenpgp.enums.packet.userID
- Rename
openpgp.enums.packet.literal
toopenpgp.enums.packet.literalData
- Rename
openpgp.enums.packet.compressed
toopenpgp.enums.packet.compressedData
- Rename
openpgp.enums.packet.symmetricallyEncrypted
toopenpgp.enums.packet.symmetricallyEncryptedData
- Rename
openpgp.enums.packet.symEncryptedIntegrityProtected
toopenpgp.enums.packet.symEncryptedIntegrityProtectedData
- Rename
openpgp.enums.packet.symEncryptedAEADProtected
toopenpgp.enums.packet.aeadEncryptedData
- Rename
openpgp.message.generateSessionKey
toopenpgp.Message.generateSessionKey
- Rename
openpgp.message.encryptSessionKey
toopenpgp.Message.encryptSessionKey
v4:
import * as openpgp from 'openpgp';
const { privateKeyArmored, publicKeyArmored, revocationCertificate } = await openpgp.generateKey({ userIds: [{ name: 'Test', email: 'test@email' }] });
v5:
import { generateKey } from 'openpgp';
const { privateKey, publicKey, revocationCertificate } = await generateKey({ userIDs: [{ name: 'Test', email: 'test@email' }] });
v4:
import * as openpgp from 'openpgp';
const { key } = await generateKey({ userIds: [{ name: 'Test', email: 'test@email' }] });
v5:
import { generateKey } from 'openpgp';
const { privateKey } = await generateKey({ userIDs: [{ name: 'Test', email: 'test@email' }], format: 'object' });
v4:
import * as openpgp from 'openpgp';
const publicKey = (await openpgp.key.readArmored(armoredKey)).keys[0];
v5:
import { readKey } from 'openpgp';
const publicKey = await readKey({ armoredKey });
v4:
import * as openpgp from 'openpgp';
const publicKey = (await openpgp.key.read(armoredKey)).keys[0];
v5:
import { readKey } from 'openpgp';
const publicKey = await readKey({ binaryKey });
v4:
import * as openpgp from 'openpgp';
const publicKeys = (await openpgp.key.readArmored(armoredKeys)).keys;
v5:
import { readKeys } from 'openpgp';
const publicKeys = await readKeys({ armoredKeys });
v4:
import * as openpgp from 'openpgp';
const privateKey = (await openpgp.key.readArmored(armoredKey)).keys[0];
await privateKey.decrypt(passphrase);
v5:
import { readPrivateKey, decryptKey } from 'openpgp';
const privateKey = await readPrivateKey({ armoredKey }); // Or `readKey` - `readPrivateKey` is helpful for better TypeScript type hinting.
const decryptedPrivateKey = await decryptKey({ privateKey, passphrase });
v4:
import * as openpgp from 'openpgp';
const message = openpgp.message.fromText(text);
const encrypted = await openpgp.encrypt({ publicKeys: publicKeys, privateKeys: privateKeys, message });
console.log(encrypted.data); // String
v5:
import { createMessage, encrypt } from 'openpgp';
const message = await createMessage({ text });
const encrypted = await encrypt({ encryptionKeys: publicKeys, signingKeys: privateKeys, message });
console.log(encrypted); // String
v4:
import * as openpgp from 'openpgp';
const message = openpgp.message.fromBinary(data);
const encrypted = await openpgp.encrypt({ publicKeys: publicKeys, privateKeys: privateKeys, message, format: 'binary' });
console.log(encrypted.message.packets.write()); // Uint8Array
v5:
import { createMessage, encrypt } from 'openpgp';
const message = await createMessage({ binary: data });
const encrypted = await encrypt({ encryptionKeys: publicKeys, signingKeys: privateKeys, message, format: 'binary' });
console.log(encrypted); // Uint8Array
v4:
import * as openpgp from 'openpgp';
const encrypted = await openpgp.encrypt({ publicKeys: publicKeys, privateKeys: privateKeys, message, armor: false });
console.log(encrypted.message.packets); // Array
v5:
import { encrypt, readMessage } from 'openpgp';
const encrypted = await encrypt({ encryptionKeys: publicKeys, signingKeys: privateKeys, message, format: 'object' });
console.log(encrypted.packets); // Array
v4:
import * as openpgp from 'openpgp';
const message = openpgp.message.readArmored(armoredMessage);
const decrypted = await openpgp.decrypt({ privateKeys: privateKeys, publicKeys: publicKeys, message });
console.log(decrypted.data); // String
console.log(decrypted.signatures[0].valid); // Boolean
v5:
import { readMessage, decrypt } from 'openpgp';
const message = await readMessage({ armoredMessage });
const decrypted = await decrypt({ decryptionKeys: privateKeys, verificationKeys: publicKeys, message });
console.log(decrypted.data); // String
console.log(await decrypted.signatures[0].verified); // Boolean
v4:
import * as openpgp from 'openpgp';
const message = openpgp.message.read(binaryMessage);
const decrypted = await openpgp.decrypt({ privateKeys: privateKeys, publicKeys: publicKeys, message });
console.log(decrypted.data); // String
console.log(decrypted.signatures[0].valid); // Boolean
v5:
import { readMessage, decrypt } from 'openpgp';
const message = await readMessage({ binaryMessage });
const decrypted = await decrypt({ decryptionKeys: privateKeys, verificationKeys: publicKeys, message, format: 'binary' });
console.log(decrypted.data); // Uint8Array
console.log(await decrypted.signatures[0].verified); // Boolean
v4:
import * as openpgp from 'openpgp';
const decrypted = await openpgp.decrypt({ privateKeys: privateKeys, publicKeys: publicKeys, message });
if (!decrypted.signatures.some(signature => signature.valid)) {
throw new Error("Couldn't find valid signature in message");
}
v5:
import { decrypt } from 'openpgp';
const decrypted = await decrypt({ decryptionKeys: privateKeys, verificationKeys: publicKeys, message, expectSigned: true });
v4:
import * as openpgp from 'openpgp';
const message = openpgp.cleartext.fromText(text);
const signed = await openpgp.sign({ privateKeys: privateKeys, message });
console.log(signed.data); // String
v5:
import { createCleartextMessage, sign } from 'openpgp';
const message = await createCleartextMessage({ text });
const signed = await sign({ signingKeys: privateKeys, message });
console.log(signed); // String
v4:
import * as openpgp from 'openpgp';
const message = openpgp.message.fromText(text);
const signed = await openpgp.sign({ privateKeys: privateKeys, message, armor: false });
console.log(signed.message.packets.write()); // Uint8Array
v5:
import { createMessage, sign } from 'openpgp';
const message = await createMessage({ text });
const signed = await sign({ signingKeys: privateKeys, message, format: 'binary' });
console.log(signed); // Uint8Array
v4:
import * as openpgp from 'openpgp';
const message = openpgp.cleartext.fromText(text);
const signed = await openpgp.sign({ privateKeys: privateKeys, message, detached: true });
console.log(signed.signature); // String
v5:
import { createMessage, sign } from 'openpgp';
const message = await createMessage({ text: util.removeTrailingSpaces(text) });
const signed = await sign({ signingKeys: privateKeys, message, detached: true });
console.log(signed); // String
v4:
import * as openpgp from 'openpgp';
const message = openpgp.message.fromText(text);
const signed = await openpgp.sign({ privateKeys: privateKeys, message, detached: true, armor: false });
console.log(signed.signature.packets.write()); // Uint8Array
v5:
import { createMessage, sign } from 'openpgp';
const message = await createMessage({ text });
const signed = await sign({ signingKeys: privateKeys, message, detached: true, format: 'binary' });
console.log(signed); // Uint8Array
v4:
import * as openpgp from 'openpgp';
const message = await openpgp.message.readArmored(armor);
const verified = await openpgp.verify({ publicKeys: publicKeys, message });
console.log(openpgp.util.nativeEOL(openpgp.util.decode_utf8(verified.data))); // String
console.log(verified.signatures); // Array
v5:
import { readMessage, verify } from 'openpgp';
const message = await readMessage({ armoredMessage });
const verified = await verify({ verificationKeys: publicKeys, message });
console.log(verified.data); // String
console.log(verified.signatures); // Array
v4:
import * as openpgp from 'openpgp';
const message = await openpgp.message.read(binary);
const verified = await openpgp.verify({ publicKeys: publicKeys, message });
console.log(verified.data); // Uint8Array
console.log(verified.signatures); // Array
v5:
import { readMessage, verify } from 'openpgp';
const message = await readMessage({ binaryMessage });
const verified = await verify({ verificationKeys: publicKeys, message, format: 'binary' });
console.log(verified.data); // Uint8Array
console.log(verified.signatures); // Array
v4:
import * as openpgp from 'openpgp';
const encrypted = await openpgp.encryptSessionKey({ publicKeys: publicKeys, data, algorithm });
console.log(encrypted.message.armor()); // String
v5:
import { encryptSessionKey } from 'openpgp';
const encrypted = await encryptSessionKey({ encryptionKeys: publicKeys, data, algorithm });
console.log(encrypted); // String
v4:
import * as openpgp from 'openpgp';
const encrypted = await openpgp.encryptSessionKey({ publicKeys: publicKeys, data, algorithm });
console.log(encrypted.message.packets.write()); // Uint8Array
v5:
import { encryptSessionKey } from 'openpgp';
const encrypted = await encryptSessionKey({ encryptionKeys: publicKeys, data, algorithm, format: 'binary' });
console.log(encrypted); // Uint8Array
v4:
import * as openpgp from 'openpgp';
const data = fs.createReadStream(filename, { encoding: 'utf8' });
const message = openpgp.message.fromText(data);
const encrypted = await openpgp.encrypt({ publicKeys: publicKeys, message });
encrypted.data.on('data', chunk => {
console.log(openpgp.util.Uint8Array_to_str(chunk)); // String
});
v5:
import { createMessage, encrypt } from 'openpgp';
const data = fs.createReadStream(filename, { encoding: 'utf8' });
const message = await createMessage({ text: data });
const encrypted = await encrypt({ encryptionKeys: publicKeys, message });
encrypted.on('data', chunk => {
console.log(chunk); // String
});
v4:
import * as openpgp from 'openpgp';
const data = fs.createReadStream(filename);
const message = openpgp.message.fromBinary(data);
const encrypted = await openpgp.encrypt({ publicKeys: publicKeys, message, armor: false });
openpgp.stream.webToNode(encrypted.message.packets.write()).pipe(targetStream);
v5:
import { createMessage, encrypt } from 'openpgp';
const data = fs.createReadStream(filename);
const message = await createMessage({ binary: data });
const encrypted = await encrypt({ encryptionKeys: publicKeys, message, format: 'binary' });
encrypted.pipe(targetStream);