Compare commits

...

149 Commits

Author SHA1 Message Date
Bart Butler
1e0427468b Release new version 2017-11-06 13:46:54 -08:00
Bart Butler
17ec4b9a57 add openpgp.key.read() function for unarmored serialized keys 2017-11-06 13:42:07 -08:00
Bart Butler
1cd0243082 Release new version 2017-10-10 10:49:23 -07:00
Bart Butler
9f5c1a985f Merge pull request #591 from openpgpjs/clone_fix
account for non-cleartext sign/verify message parameters
2017-10-10 10:42:49 -07:00
Sanjana Rajan
0db0c4eccd account for non-cleartext sign/verify message parameters 2017-10-09 17:27:02 -07:00
Bart Butler
65be967db2 Release new version 2017-09-20 10:25:54 -07:00
Bart Butler
0c47537866 Merge pull request #586 from openpgpjs/webkit_subtle_fix
Use hash param in RSA key generation with webkit subtle
2017-09-20 10:20:27 -07:00
Sanjana Rajan
25f8f8f404 use hash param in rsa key gen with webkit subtle 2017-09-20 18:28:37 +02:00
Bart Butler
07e2678fd4 Merge pull request #582 from kellym/fix/key-expiration
Replace expired subkeys with three other subkeys,
2017-09-15 11:04:53 -07:00
Kelly Martin
4f2ec8425e Replace expired subkeys with three other subkeys,
One is an expired key for encryption, one is a non-expiring key for
only signatures, and one is a non-expiring key for encryption.
2017-09-15 11:35:37 -05:00
Bart Butler
1df7eb4eea Release new version 2017-08-30 13:09:51 -07:00
Bart Butler
56285d7624 Merge pull request #576 from mailvelope/subkey_expiration_time
Set keyExpirationTime for subkey binding signature
2017-08-30 12:50:46 -07:00
Thomas Oberndörfer
9d726e6424 Set keyExpirationTime for subkey binding signature 2017-08-30 16:09:53 +02:00
Bart Butler
ba8789a099 Release new version 2017-08-20 16:44:35 -07:00
Bart Butler
5f75d070ca Merge pull request #574 from pbrunschwig/master
Fix for issue 573: Don't throw with unknown Armor Headers
2017-08-20 16:38:29 -07:00
Patrick Brunschwig
e8666811f2 Merge branch 'master' of https://github.com/pbrunschwig/openpgpjs 2017-08-19 22:09:44 +02:00
Patrick Brunschwig
7a2abb4d9c fixed broken Testcase for improperly formatted Armor Header 2017-08-19 22:07:36 +02:00
Bart Butler
a2db30bf33 Update armor.js
Only console.log if debug mode on
2017-08-19 10:52:36 -07:00
Bart Butler
56cc47da34 Update armor.js
Small tweak
2017-08-19 10:49:36 -07:00
Patrick Brunschwig
8b6d8ef2e2 Fix for issue 573: don't throw with unknown Armor Headers, but just
with invalid ones
2017-08-19 18:32:02 +02:00
Bart Butler
d1a2bdf599 Merge pull request #566 from FlowCrypt/master
util.readNumber: stop overflowing until full range of uint32 for 4 byte numbers | #497
2017-07-22 09:58:00 -07:00
Bart Butler
a2cac61242 Correct misspelling 2017-07-22 09:38:44 -07:00
Tom James Holub
f6d19ed304 util.readNumber: overflow test added | #497 2017-07-22 09:27:36 -07:00
Tom James Holub
2aab843125 util.readNumber: stop overflowing until full range of uint32 | #497 2017-07-22 09:26:30 -07:00
Bart Butler
a9a0e28ffb Release new version 2017-07-21 22:06:51 -07:00
Bart Butler
4e2d4ca040 Merge pull request #565 from FlowCrypt/master
work with multiple binding signatures per subkey #527
2017-07-21 22:02:04 -07:00
Tom James Holub
5136c24602 test added: find a valid subkey binding signature among many invalid ones | #527 2017-07-21 21:39:48 -07:00
Tom James Holub
22ba9f64aa subKey.bindingSignature -> subKey.bindingSignatures[] - process all | #527 2017-07-21 21:38:33 -07:00
Bart Butler
87152a5dfa Release new version 2017-07-21 18:58:00 -07:00
Bart Butler
24755fb621 Merge pull request #564 from FlowCrypt/master
process blocks that miss a checksum | #563
2017-07-21 18:52:20 -07:00
Tom James Holub
7ccd57613b do not remove equal sign at the end of armored body when missing checksum 2017-07-21 17:39:19 -07:00
Tom James Holub
a0c87d46e7 fixed outdated annotations in armor.js 2017-07-21 15:39:06 -07:00
Tom James Holub
8343343ea1 added missing checksum test with traling armor newline 2017-07-21 15:37:25 -07:00
Tom James Holub
fe51e1dcf4 improved armor behavior - trailing newline 2017-07-21 15:35:27 -07:00
Tom James Holub
3b27f56e89 tests extended - armor checksum validation | #563 2017-07-21 10:15:49 -07:00
Tom James Holub
da16d7fed6 do not fail when missing armor checksum | #563 2017-07-21 10:13:33 -07:00
Bart Butler
79ca1f221b Release new version 2017-07-13 10:13:17 -07:00
Bart Butler
33ac7e9f7a Merge pull request #562 from openpgpjs/sign_byte_arrays
Remove console logs from tests
2017-07-13 10:07:45 -07:00
Sanjana Rajan
e8ca36a430 remove console logs 2017-07-13 12:07:12 +02:00
Bart Butler
7d4bce440b Merge pull request #559 from openpgpjs/sign_byte_arrays
Sign Uint8 arrays
2017-07-12 19:45:53 -07:00
Sanjana Rajan
52ad5226df remove extra spaces 2017-07-08 13:26:20 +02:00
Sanjana Rajan
4d5a69ad2d use message obj when signing uint8 array 2017-07-08 13:25:24 +02:00
Bart Butler
d879bc533f Release new version 2017-07-06 16:27:44 -07:00
Bart Butler
bb8dd6a5c4 minor bugfix 2017-07-06 15:27:45 -07:00
Sanjana Rajan
3d1c4eb9e9 style fixes 2017-07-06 16:29:25 +02:00
Sanjana Rajan
89de3b78d0 more tests 2017-07-04 21:08:48 -07:00
Sanjana Rajan
9c88f70f21 add tests 2017-07-04 17:03:56 -07:00
Sanjana Rajan
0a259d1b5b armor based on text vs. bytes 2017-07-04 16:47:23 -07:00
Sanjana Rajan
d4124f5bec allow cleartext msg data to be string or byte array 2017-07-04 14:15:39 -07:00
Bart Butler
ca07e270a8 Merge pull request #557 from openpgpjs/decode_single_line
allow body + checksum on same line in splitChecksum
2017-06-27 23:47:03 -07:00
Sanjana Rajan
233eff599e support split checksum where body and checksum are on the same line 2017-06-27 16:41:53 -07:00
Bart Butler
992c40b5d4 change URL to https 2017-04-25 14:09:31 +02:00
Bart Butler
9061b6823d Merge pull request #543 from frankcash/patch-1
Update README.md
2017-04-13 23:41:33 -07:00
Frank Cash
36d1b55ca5 Update README.md 2017-04-12 21:05:13 -04:00
Bart Butler
fcad97f916 Merge pull request #542 from darkyen/patch-1
Fix documentation
2017-04-08 14:49:25 -07:00
Abhishek Hingnikar
7d80bb0b74 Fix documentation
The storeHandler seems to need loadPublic, loadPrivate, storePublic, storePrivate methods but the current documentation only mentions load and store.
2017-04-08 16:12:47 +05:30
Bart Butler
6ef5cff8cc Merge pull request #541 from BAKFR/simplify-crc24
Simplify code of CRC24 checksum in armor encoding
2017-04-06 11:19:45 -07:00
Kévin Bernard-Allies
f9353f0196 Simplify code of CRC24 checksum in armor encoding 2017-04-06 13:12:45 +02:00
Bart Butler
8164d8ff7a Release new version 2017-04-05 22:40:17 -07:00
Bart Butler
58a3ec951f Merge pull request #540 from openpgpjs/err_handling
Tolerant flag for suppressing packet read errors
2017-04-05 16:38:20 -07:00
Sanjana Rajan
16c4f03e6e tolerant default to true 2017-04-05 16:36:29 -07:00
Sanjana Rajan
0918f5a782 add tolerant mode which suppresses errors in reading non-data packets 2017-04-05 07:54:08 -07:00
Bart Butler
59c3811b2e Release new version 2017-03-29 16:32:10 -07:00
Bart Butler
f1d60599c1 use web worker for reformatKey 2017-03-29 16:27:16 -07:00
Bart Butler
c6661b242b Release new version 2017-03-28 13:17:39 -07:00
Bart Butler
7c718de890 Merge pull request #536 from openpgpjs/document_key_decryption
Include key decryption step in README
2017-03-28 13:09:15 -07:00
Sanjana Rajan
d940cc4178 include key decryption step in README 2017-03-28 13:04:36 -07:00
Bart Butler
519836a9a5 not all platforms implement TypedArray.slice 2017-03-28 11:44:04 -07:00
Bart Butler
13c9ad8b88 Merge pull request #534 from openpgpjs/signature_input
Accept signature object as an input to encrypt function
2017-03-28 10:43:00 -07:00
Bart Butler
e81eaf4fe6 Merge pull request #522 from danenania/patch-1
Readme - add warning about config.aead_protect potentially breaking compatibility
2017-03-28 10:40:06 -07:00
Bart Butler
2b5d55860a optimize Uint8Array2str function for large payloads 2017-03-28 10:35:20 -07:00
Sanjana Rajan
b5d3c5bbbd signatureInput -> signature 2017-03-23 13:01:12 -07:00
Sanjana Rajan
6f36d65b22 improve test 2017-03-23 12:04:32 -07:00
Sanjana Rajan
58965c4a07 spacing 2017-03-23 11:16:16 -07:00
Sanjana Rajan
37d5b1b7e0 upper level tests 2017-03-22 17:14:57 -07:00
Sanjana Rajan
cebe0b8a21 add one pass in case where no priv keys are passed in for signing 2017-03-22 17:14:30 -07:00
Sanjana Rajan
910cf7a67f allow signature as input in high level encrypt 2017-03-22 17:04:21 -07:00
Bart Butler
c25094d299 Merge pull request #533 from openpgpjs/detached_sig_docs
Documentation updates
2017-03-17 16:59:26 -07:00
Sanjana Rajan
44f9d36de5 update README to demonstrate detached signature use, some documentation fixes 2017-03-17 16:54:34 -07:00
Bart Butler
aec51e0c6f Release new version 2017-03-15 19:56:22 -07:00
Bart Butler
add7ec1e1e Merge pull request #529 from openpgpjs/init_publicKeys
Initialize public key array during decryption
2017-03-15 19:51:10 -07:00
Sanjana Rajan
73ab650366 initialize publicKeys to empty array, test case where signed message is decrypted without public keys to verify 2017-03-15 17:44:43 -07:00
Bart Butler
23195c2c98 remove useless test 2017-03-14 19:13:17 -07:00
Bart Butler
4a68712379 small test bugfix 2017-03-14 18:49:05 -07:00
Bart Butler
22ae328d75 Release new version 2017-03-14 18:34:15 -07:00
Bart Butler
f6eef13447 Merge pull request #528 from openpgpjs/detached
Detached Signatures
2017-03-14 18:15:24 -07:00
Sanjana Rajan
1a6743fd59 return signature object inside verification object 2017-03-10 10:38:59 -08:00
Sanjana Rajan
6e9e0ab008 small fixes 2017-03-07 19:10:33 -08:00
Sanjana Rajan
d1abe44eea add clone to packetlist support for signature objects 2017-03-07 16:51:08 -08:00
Sanjana Rajan
8c48e09705 small fixes 2017-03-07 13:59:18 -08:00
Sanjana Rajan
5743fd9cfc added detached sig tests for high level openpgpjs functions 2017-03-07 13:59:18 -08:00
Sanjana Rajan
1189a051c6 add detached sig option to upper level functions 2017-03-07 13:59:18 -08:00
Sanjana Rajan
3e1dd50e12 add detached sign/verify options to cleartext 2017-03-07 13:59:18 -08:00
Sanjana Rajan
a0b04a2d9e refactor cleartext message to use signature object instead of packetlist 2017-03-07 13:59:17 -08:00
Sanjana Rajan
079ab96cc8 more tests 2017-03-07 13:59:17 -08:00
Sanjana Rajan
f09fe925ff add tests 2017-03-07 13:59:17 -08:00
Sanjana Rajan
2735a14497 add support for detached signatures 2017-03-07 13:59:17 -08:00
Bart Butler
3072800286 Release new version 2017-03-05 12:30:15 -08:00
Bart Butler
a7d31ef55f Merge pull request #417 from zartdinov/master
Added ability to sign and verify public keys
2017-03-05 12:24:37 -08:00
Aydar Zartdinov
27f56742f1 Validate signatures by all suitable keys 2017-03-04 19:27:27 +00:00
Aydar Zartdinov
91d19e2ba1 Prevent self signing by fingerprint instead of keyId 2017-03-04 18:47:33 +00:00
Bart Butler
330b2c334b Release new version 2017-02-27 11:09:41 -08:00
Bart Butler
09accc8cc5 fix key test 2017-02-27 11:06:36 -08:00
Bart Butler
a515fdc82f move SHA1 to the bottom of the preferred hash algorithm list 2017-02-27 10:30:39 -08:00
Dane Schneider
bd350d7105 Warning about config.aead_protect potentially breaking compatibility 2017-02-18 15:00:50 -05:00
Bart Butler
740ac31cbf Merge pull request #520 from openpgpjs/fix_519
Fixes the typo addressed in #519
2017-02-15 13:21:43 -08:00
Sanjana Rajan
a7c8e87966 add missing parentheses 2017-02-15 13:06:18 -08:00
Aydar Zartdinov
457bd26afa Array.prototype.find replaced for Node.js 0.12 and IE 11 2017-02-15 21:31:05 +03:00
Aydar Zartdinov
81383752f7 Base functionality moved to User.prototype 2017-02-15 21:08:17 +03:00
Aydar Zartdinov
f6e3c37c52 Reverted top level api changes 2017-02-15 21:04:50 +03:00
Aydar Zartdinov
e56e7b3b3e Added ability to sign and verify public keys 2017-02-15 21:04:50 +03:00
Bart Butler
faf8207808 tests cannot use ES6 features 2017-02-13 22:25:46 -08:00
Bart Butler
36026fb557 Release new version 2017-02-13 16:17:07 -08:00
Bart Butler
976280e5a2 Merge pull request #517 from openpgpjs/userID
Key reformatting and resigning
2017-02-13 16:11:17 -08:00
Bart Butler
ff03c136e1 Merge pull request #516 from alax/fix-v3-signatures
Generate valid version 3 signatures
2017-02-13 15:59:19 -08:00
Sanjana Rajan
b3a5219d96 add test with reformatted key signing and encryption 2017-01-31 15:07:51 -08:00
Sanjana Rajan
d7cc404968 remove extra input param 2017-01-31 14:18:13 -08:00
Sanjana Rajan
38e5a455ea add some reformatting key tests 2017-01-31 14:16:34 -08:00
Sanjana Rajan
053be79110 add upper level refactorKey function 2017-01-31 14:14:27 -08:00
Sanjana Rajan
46322ae532 remove extra space, typo 2017-01-30 19:41:43 -08:00
Sanjana Rajan
63c857c4c3 reformatKey function 2017-01-30 19:38:49 -08:00
Alax Villmann
af1a3dd697 Generate valid version 3 signatures 2017-01-26 12:49:49 -08:00
Bart Butler
d6875797ff Release new version 2017-01-25 15:26:29 -08:00
Bart Butler
78442283ea Merge pull request #514 from Consensas/master
implement keyExpirationTime
2017-01-25 14:56:53 -08:00
David Janes
f8700f9928 replace with old versions 2017-01-25 07:30:55 -05:00
David Janes
70982b6ccd implement keyExpirationTime 2017-01-24 16:54:52 -05:00
Bart Butler
87d0c1886f Merge pull request #498 from larshp/master
S2K - Only generate what is needed
2017-01-22 23:36:36 -08:00
Bart Butler
81d7f8fa83 Merge pull request #508 from wmhilton/doc-detached-sigs
Adds cleartext and detached signature examples
2017-01-09 12:28:01 -08:00
Bart Butler
23c442b062 Merge pull request #509 from alax/fix-v3-headers
V3 header generation was concat()-ing non-Uint8 arrays
2017-01-09 12:26:30 -08:00
Alax Villmann
8a65ebbe3c V3 header generation was concat()-ing non-Uint8 arrays 2017-01-09 10:30:31 -08:00
Will Hilton
c9e6c03652 Add detached binary signature example to README 2017-01-06 00:06:44 -05:00
Will Hilton
5fa5ddc78e Add cleartext signing example to README 2017-01-05 23:50:07 -05:00
Bart Butler
d97cc095e3 Merge pull request #505 from openpgpjs/user_id_formatting
don't allow leading spaces when generating userid
2016-12-15 14:15:57 -08:00
Sanjana Rajan
00ef89069d trim name 2016-12-15 10:39:29 -08:00
Sanjana Rajan
09269cb94e clearer syntax 2016-12-15 10:01:50 -08:00
Sanjana Rajan
825d5a3b07 don't allow leading spaces in user id 2016-12-14 12:38:01 -08:00
Lars Hvam
cd09273080 only generate what is needed 2016-11-20 09:54:00 -05:00
Bart Butler
23fe39f432 Merge pull request #494 from openpgpjs/invalidate_expired_primary_keys
Invalidate expired primary keys
2016-10-27 12:48:57 -07:00
Sanjana Rajan
485560ba64 invalidate expired primary keys 2016-10-27 11:50:30 -07:00
Bart Butler
80c433689b Release new version 2016-10-26 17:05:14 -07:00
Bart Butler
b3e4e36d10 Merge pull request #493 from openpgpjs/armored_decrypted_key_fix
Reset secret key's encrypted field to null after decryption
2016-10-26 16:58:07 -07:00
Bart Butler
bccb2441b9 Fix rare race condition with webworker tasks, fix minified web worker filename default 2016-10-26 16:56:21 -07:00
Sanjana Rajan
ce0381c170 reset encrypted field to null when decrypting secret key 2016-10-26 16:55:49 -07:00
Bart Butler
dfbc5ea5a6 Release new version 2016-10-21 16:27:49 -07:00
Bart Butler
9b6aaa08d4 Merge pull request #491 from iamolivinius/master
Add plugin to derequire browserifyed dist files.
2016-10-21 19:22:22 -04:00
Unknown
aaa4c52374 Add plugin to derequire browserifyed dist files. 2016-10-18 21:20:13 +02:00
Bart Butler
edd415c958 Release new version 2016-08-17 12:29:41 -07:00
Bart Butler
1bfce40f61 Merge pull request #483 from ProtonMail/one-pass-sig-last
Properly set one-pass signature packet flag
2016-08-17 12:18:14 -07:00
emersion
a0726617e5 Properly set one-pass signture packet flag 2016-08-17 13:03:56 +02:00
35 changed files with 3256 additions and 1049 deletions

View File

@ -51,7 +51,8 @@ module.exports = function(grunt) {
ignore: ['*.min.js'],
presets: ["es2015"]
}]
]
],
plugin: [ 'browserify-derequire' ]
}
},
openpgp_debug: {
@ -69,7 +70,8 @@ module.exports = function(grunt) {
ignore: ['*.min.js'],
presets: ["es2015"]
}]
]
],
plugin: [ 'browserify-derequire' ]
}
},
worker: {
@ -77,11 +79,6 @@ module.exports = function(grunt) {
'dist/openpgp.worker.js': [ './src/worker/worker.js' ]
}
},
worker_min: {
files: {
'dist/openpgp.worker.min.js': [ './src/worker/worker.js' ]
}
},
unittests: {
files: {
'test/lib/unittests-bundle.js': [ './test/unittests.js' ]
@ -108,12 +105,20 @@ module.exports = function(grunt) {
to: 'OpenPGP.js v<%= pkg.version %>'
}]
},
openpgp_min: {
src: ['dist/openpgp.min.js'],
dest: ['dist/openpgp.min.js'],
replacements: [{
from: "openpgp.worker.js",
to: "openpgp.worker.min.js"
}]
},
worker_min: {
src: ['dist/openpgp.worker.min.js'],
dest: ['dist/openpgp.worker.min.js'],
replacements: [{
from: "importScripts('openpgp.js')",
to: "importScripts('openpgp.min.js')"
from: "openpgp.js",
to: "openpgp.min.js"
}]
}
},
@ -121,7 +126,7 @@ module.exports = function(grunt) {
openpgp: {
files: {
'dist/openpgp.min.js' : [ 'dist/openpgp.js' ],
'dist/openpgp.worker.min.js' : [ 'dist/openpgp.worker.min.js' ]
'dist/openpgp.worker.min.js' : [ 'dist/openpgp.worker.js' ]
}
},
options: {
@ -290,7 +295,9 @@ module.exports = function(grunt) {
}
// Build tasks
grunt.registerTask('default', ['clean', 'copy:zlib', 'browserify', 'replace', 'uglify']);
grunt.registerTask('version', ['replace:openpgp', 'replace:openpgp_debug']);
grunt.registerTask('replace_min', ['replace:openpgp_min', 'replace:worker_min']);
grunt.registerTask('default', ['clean', 'copy:zlib', 'browserify', 'version', 'uglify', 'replace_min']);
grunt.registerTask('documentation', ['jsdoc']);
// Test/Dev tasks
grunt.registerTask('test', ['jshint', 'jscs', 'mochaTest']);

View File

@ -21,7 +21,7 @@ OpenPGP.js [![Build Status](https://travis-ci.org/openpgpjs/openpgpjs.svg?branch
* If the user's browser supports [native WebCrypto](http://caniuse.com/#feat=cryptography) via the `window.crypto.subtle` api, this will be used. Under node.js the native [crypto module](https://nodejs.org/api/crypto.html#crypto_crypto) is used. This can be deactivated by setting `openpgp.config.use_native = false`.
* The library implements the [IETF proposal](https://tools.ietf.org/html/draft-ford-openpgp-format-00) for authenticated encryption [using native AES-GCM](https://github.com/openpgpjs/openpgpjs/pull/430). This makes symmetric encryption about 30x faster on supported platforms. Since the specification has not been finalized and other OpenPGP implementations haven't adopted it yet, the feature is currently behind a flag. You can activate it by setting `openpgp.config.aead_protect = true`.
* The library implements the [IETF proposal](https://tools.ietf.org/html/draft-ford-openpgp-format-00) for authenticated encryption [using native AES-GCM](https://github.com/openpgpjs/openpgpjs/pull/430). This makes symmetric encryption about 30x faster on supported platforms. Since the specification has not been finalized and other OpenPGP implementations haven't adopted it yet, the feature is currently behind a flag. You can activate it by setting `openpgp.config.aead_protect = true`. **Note: activating this setting can break compatibility with other OpenPGP implementations, so be careful if that's one of your requirements.**
* For environments that don't provide native crypto, the library falls back to [asm.js](http://caniuse.com/#feat=asmjs) implementations of AES, SHA-1, and SHA-256. We use [Rusha](https://github.com/srijs/rusha) and [asmCrypto Lite](https://github.com/openpgpjs/asmcrypto-lite) (a minimal subset of asmCrypto.js built specifically for OpenPGP.js).
@ -87,12 +87,16 @@ openpgp.decrypt(options).then(function(plaintext) {
var options, encrypted;
var pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK ... END PGP PUBLIC KEY BLOCK-----';
var privkey = '-----BEGIN PGP PRIVATE KEY BLOCK ... END PGP PRIVATE KEY BLOCK-----';
var privkey = '-----BEGIN PGP PRIVATE KEY BLOCK ... END PGP PRIVATE KEY BLOCK-----'; //encrypted private key
var passphrase = 'secret passphrase'; //what the privKey is encrypted with
var privKeyObj = openpgp.key.readArmored(privkey).keys[0];
privKeyObj.decrypt(passphrase);
options = {
data: 'Hello, World!', // input as String (or Uint8Array)
publicKeys: openpgp.key.readArmored(pubkey).keys, // for encryption
privateKeys: openpgp.key.readArmored(privkey).keys // for signing (optional)
privateKeys: privKeyObj // for signing (optional)
};
openpgp.encrypt(options).then(function(ciphertext) {
@ -104,7 +108,7 @@ openpgp.encrypt(options).then(function(ciphertext) {
options = {
message: openpgp.message.readArmored(encrypted), // parse armored message
publicKeys: openpgp.key.readArmored(pubkey).keys, // for verification (optional)
privateKey: openpgp.key.readArmored(privkey).keys[0] // for decryption
privateKey: privKeyObj // for decryption
};
openpgp.decrypt(options).then(function(plaintext) {
@ -151,6 +155,86 @@ var pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK ... END PGP PUBLIC KEY BLOCK-----'
hkp.upload(pubkey).then(function() { ... });
```
#### Sign and verify cleartext messages
```js
var options, cleartext, validity;
var pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK ... END PGP PUBLIC KEY BLOCK-----';
var privkey = '-----BEGIN PGP PRIVATE KEY BLOCK ... END PGP PRIVATE KEY BLOCK-----'; //encrypted private key
var passphrase = 'secret passphrase'; //what the privKey is encrypted with
var privKeyObj = openpgp.key.readArmored(privkey).keys[0];
privKeyObj.decrypt(passphrase);
```
```js
options = {
data: 'Hello, World!', // input as String (or Uint8Array)
privateKeys: privKeyObj // for signing
};
openpgp.sign(options).then(function(signed) {
cleartext = signed.data; // '-----BEGIN PGP SIGNED MESSAGE ... END PGP SIGNATURE-----'
});
```
```js
options = {
message: openpgp.cleartext.readArmored(cleartext), // parse armored message
publicKeys: openpgp.key.readArmored(pubkey).keys // for verification
};
openpgp.verify(options).then(function(verified) {
validity = verified.signatures[0].valid; // true
if (validity) {
console.log('signed by key id ' + verified.signatures[0].keyid.toHex());
}
});
```
#### Create and verify *detached* signatures
```js
var options, cleartext, detachedSig, validity;
var pubkey = '-----BEGIN PGP PUBLIC KEY BLOCK ... END PGP PUBLIC KEY BLOCK-----';
var privkey = '-----BEGIN PGP PRIVATE KEY BLOCK ... END PGP PRIVATE KEY BLOCK-----'; //encrypted private key
var passphrase = 'secret passphrase'; //what the privKey is encrypted with
var privKeyObj = openpgp.key.readArmored(privkey).keys[0];
privKeyObj.decrypt(passphrase);
```
```js
options = {
data: 'Hello, World!', // input as String (or Uint8Array)
privateKeys: privKeyObj, // for signing
detached: true
};
openpgp.sign(options).then(function(signed) {
cleartext = signed.data;
detachedSig = signed.signature;
});
```
```js
options = {
message: openpgp.cleartext.readArmored(cleartext), // parse armored message
signature: openpgp.signature.readArmored(detachedSig), // parse detached signature
publicKeys: openpgp.key.readArmored(pubkey).keys // for verification
};
openpgp.verify(options).then(function(verified) {
validity = verified.signatures[0].valid; // true
if (validity) {
console.log('signed by key id ' + verified.signatures[0].keyid.toHex());
}
});
```
### Documentation
A jsdoc build of our code comments is available at [doc/index.html](http://openpgpjs.org/openpgpjs/doc/index.html). Public calls should generally be made through the OpenPGP object [doc/openpgp.html](http://openpgpjs.org/openpgpjs/doc/module-openpgp.html).

View File

@ -1,6 +1,6 @@
{
"name": "openpgp",
"version": "2.3.2",
"version": "2.5.13",
"license": "LGPL-3.0+",
"homepage": "http://openpgpjs.org/",
"authors": [

2183
dist/openpgp.js vendored

File diff suppressed because it is too large Load Diff

20
dist/openpgp.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -45,7 +45,7 @@ self.onmessage = function(event) {
break;
default:
delegate(msg.event, msg.options || {});
delegate(msg.id, msg.event, msg.options || {});
}
};
@ -76,18 +76,18 @@ function seedRandom(buffer) {
* @param {String} method The public api function to be delegated to the worker thread
* @param {Object} options The api function's options
*/
function delegate(method, options) {
function delegate(id, method, options) {
if (typeof openpgp[method] !== 'function') {
response({ event:'method-return', err:'Unknown Worker Event' });
response({ id:id, event:'method-return', err:'Unknown Worker Event' });
return;
}
// parse cloned packets
options = openpgp.packet.clone.parseClonedPackets(options, method);
openpgp[method](options).then(function(data) {
// clone packets (for web worker structured cloning algorithm)
response({ event:'method-return', data:openpgp.packet.clone.clonePackets(data) });
response({ id:id, event:'method-return', data:openpgp.packet.clone.clonePackets(data) });
}).catch(function(e) {
response({ event:'method-return', err:e.message });
response({ id:id, event:'method-return', err:e.message });
});
}

View File

@ -1 +1 @@
/*! OpenPGP.js v2.3.2 - 2016-06-08 - this is LGPL licensed code, see LICENSE/our website http://openpgpjs.org/ for more information. */!function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){function d(a){for(var b in a)h.config[b]=a[b]}function e(a){a instanceof Uint8Array||(a=new Uint8Array(a)),h.crypto.random.randomBuffer.set(a)}function f(a,b){return"function"!=typeof h[a]?void g({event:"method-return",err:"Unknown Worker Event"}):(b=h.packet.clone.parseClonedPackets(b,a),void h[a](b).then(function(a){g({event:"method-return",data:h.packet.clone.clonePackets(a)})})["catch"](function(a){g({event:"method-return",err:a.message})}))}function g(a){h.crypto.random.randomBuffer.size<i&&self.postMessage({event:"request-seed"}),self.postMessage(a,h.util.getTransferables.call(h.util,a.data))}self.window={},importScripts("openpgp.min.js");var h=window.openpgp,i=4e4,j=6e4;h.crypto.random.randomBuffer.init(j),self.onmessage=function(a){var b=a.data||{};switch(b.event){case"configure":d(b.config);break;case"seed-random":e(b.buf);break;default:f(b.event,b.options||{})}}},{}]},{},[1]);
/*! OpenPGP.js v2.5.13 - 2017-11-06 - this is LGPL licensed code, see LICENSE/our website http://openpgpjs.org/ for more information. */!function a(b,c,d){function e(g,h){if(!c[g]){if(!b[g]){var i="function"==typeof require&&require;if(!h&&i)return i(g,!0);if(f)return f(g,!0);var j=new Error("Cannot find module '"+g+"'");throw j.code="MODULE_NOT_FOUND",j}var k=c[g]={exports:{}};b[g][0].call(k.exports,function(a){var c=b[g][1][a];return e(c?c:a)},k,k.exports,a,b,c,d)}return c[g].exports}for(var f="function"==typeof require&&require,g=0;g<d.length;g++)e(d[g]);return e}({1:[function(a,b,c){function d(a){for(var b in a)h.config[b]=a[b]}function e(a){a instanceof Uint8Array||(a=new Uint8Array(a)),h.crypto.random.randomBuffer.set(a)}function f(a,b,c){return"function"!=typeof h[b]?void g({id:a,event:"method-return",err:"Unknown Worker Event"}):(c=h.packet.clone.parseClonedPackets(c,b),void h[b](c).then(function(b){g({id:a,event:"method-return",data:h.packet.clone.clonePackets(b)})})["catch"](function(b){g({id:a,event:"method-return",err:b.message})}))}function g(a){h.crypto.random.randomBuffer.size<i&&self.postMessage({event:"request-seed"}),self.postMessage(a,h.util.getTransferables.call(h.util,a.data))}self.window={},importScripts("openpgp.min.js");var h=window.openpgp,i=4e4,j=6e4;h.crypto.random.randomBuffer.init(j),self.onmessage=function(a){var b=a.data||{};switch(b.event){case"configure":d(b.config);break;case"seed-random":e(b.buf);break;default:f(b.id,b.event,b.options||{})}}},{}]},{},[1]);

2
npm-shrinkwrap.json generated
View File

@ -1,6 +1,6 @@
{
"name": "openpgp",
"version": "2.3.2",
"version": "2.5.13",
"dependencies": {
"asmcrypto-lite": {
"version": "1.1.0",

View File

@ -1,7 +1,7 @@
{
"name": "openpgp",
"description": "OpenPGP.js is a Javascript implementation of the OpenPGP protocol. This is defined in RFC 4880.",
"version": "2.3.2",
"version": "2.5.13",
"license": "LGPL-3.0+",
"homepage": "http://openpgpjs.org/",
"engines": {
@ -35,6 +35,7 @@
"asmcrypto-lite": "^1.0.0",
"babel-preset-es2015": "^6.3.13",
"babelify": "^7.2.0",
"browserify-derequire": "^0.9.4",
"chai": "~3.5.0",
"es6-promise": "^3.1.2",
"grunt": "~0.4.5",

View File

@ -29,23 +29,26 @@ import config from './config';
import packet from './packet';
import enums from './enums.js';
import armor from './encoding/armor.js';
import * as sigModule from './signature.js';
/**
* @class
* @classdesc Class that represents an OpenPGP cleartext signed message.
* See {@link http://tools.ietf.org/html/rfc4880#section-7}
* @param {String} text The cleartext of the signed message
* @param {module:packet/packetlist} packetlist The packetlist with signature packets or undefined
* if message not yet signed
* @param {module:signature} signature The detached signature or an empty signature if message not yet signed
*/
export function CleartextMessage(text, packetlist) {
export function CleartextMessage(text, signature) {
if (!(this instanceof CleartextMessage)) {
return new CleartextMessage(text, packetlist);
return new CleartextMessage(text, signature);
}
// normalize EOL to canonical form <CR><LF>
this.text = text.replace(/\r/g, '').replace(/[\t ]+\n/g, "\n").replace(/\n/g,"\r\n");
this.packets = packetlist || new packet.List();
if (signature && !(signature instanceof sigModule.Signature)) {
throw new Error('Invalid signature input');
}
this.signature = signature || new sigModule.Signature(new packet.List());
}
/**
@ -54,7 +57,7 @@ export function CleartextMessage(text, packetlist) {
*/
CleartextMessage.prototype.getSigningKeyIds = function() {
var keyIds = [];
var signatureList = this.packets.filterByTag(enums.packet.signature);
var signatureList = this.signature.packets;
signatureList.forEach(function(packet) {
keyIds.push(packet.issuerKeyId);
});
@ -64,8 +67,18 @@ CleartextMessage.prototype.getSigningKeyIds = function() {
/**
* Sign the cleartext message
* @param {Array<module:key~Key>} privateKeys private keys with decrypted secret key data for signing
* @return {module:message~CleartextMessage} new cleartext message with signed content
*/
CleartextMessage.prototype.sign = function(privateKeys) {
return new CleartextMessage(this.text, this.signDetached(privateKeys));
};
/**
* Sign the cleartext message
* @param {Array<module:key~Key>} privateKeys private keys with decrypted secret key data for signing
* @return {module:signature~Signature} new detached signature of message content
*/
CleartextMessage.prototype.signDetached = function(privateKeys) {
var packetlist = new packet.List();
var literalDataPacket = new packet.Literal();
literalDataPacket.setText(this.text);
@ -84,7 +97,7 @@ CleartextMessage.prototype.sign = function(privateKeys) {
signaturePacket.sign(signingKeyPacket, literalDataPacket);
packetlist.push(signaturePacket);
}
this.packets = packetlist;
return new sigModule.Signature(packetlist);
};
/**
@ -93,8 +106,17 @@ CleartextMessage.prototype.sign = function(privateKeys) {
* @return {Array<{keyid: module:type/keyid, valid: Boolean}>} list of signer's keyid and validity of signature
*/
CleartextMessage.prototype.verify = function(keys) {
return this.verifyDetached(this.signature, keys);
};
/**
* Verify signatures of cleartext signed message
* @param {Array<module:key~Key>} keys array of keys to verify signatures
* @return {Array<{keyid: module:type/keyid, valid: Boolean}>} list of signer's keyid and validity of signature
*/
CleartextMessage.prototype.verifyDetached = function(signature, keys) {
var result = [];
var signatureList = this.packets.filterByTag(enums.packet.signature);
var signatureList = signature.packets;
var literalDataPacket = new packet.Literal();
// we assume that cleartext signature is generated based on UTF8 cleartext
literalDataPacket.setText(this.text);
@ -115,6 +137,11 @@ CleartextMessage.prototype.verify = function(keys) {
verifiedSig.keyid = signatureList[i].issuerKeyId;
verifiedSig.valid = null;
}
var packetlist = new packet.List();
packetlist.push(signatureList[i]);
verifiedSig.signature = new sigModule.Signature(packetlist);
result.push(verifiedSig);
}
return result;
@ -137,7 +164,7 @@ CleartextMessage.prototype.armor = function() {
var body = {
hash: enums.read(enums.hash, config.prefer_hash_algorithm).toUpperCase(),
text: this.text,
data: this.packets.write()
data: this.signature.packets.write()
};
return armor.encode(enums.armor.signed, body);
};
@ -157,7 +184,8 @@ export function readArmored(armoredText) {
var packetlist = new packet.List();
packetlist.read(input.data);
verifyHeaders(input.headers, packetlist);
var newMessage = new CleartextMessage(input.text, packetlist);
var signature = new sigModule.Signature(packetlist);
var newMessage = new CleartextMessage(input.text, signature);
return newMessage;
}

View File

@ -40,14 +40,16 @@ export default {
aead_protect: false, // use Authenticated Encryption with Additional Data (AEAD) protection for symmetric encryption
integrity_protect: true, // use integrity protection for symmetric encryption
ignore_mdc_error: false, // fail on decrypt if message is not integrity protected
checksum_required: false, // do not throw error when armor is missing a checksum
rsa_blinding: true,
use_native: true, // use native node.js crypto and Web Crypto apis (if available)
zero_copy: false, // use transferable objects between the Web Worker and main thread
debug: false,
tolerant: true, // ignore unsupported/unrecognizable packets instead of throwing an error
show_version: true,
show_comment: true,
versionstring: "OpenPGP.js VERSION",
commentstring: "http://openpgpjs.org",
commentstring: "https://openpgpjs.org",
keyserver: "https://keyserver.ubuntu.com",
node_store: './openpgp.store'
};

View File

@ -153,7 +153,10 @@ export default function RSA() {
keyGenOpt = {
name: 'RSA-OAEP',
modulusLength: B, // the specified keysize in bits
publicExponent: Euint8.subarray(0, 3) // take three bytes (max 65537)
publicExponent: Euint8.subarray(0, 3), // take three bytes (max 65537)
hash: {
name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify'
}
};
keys = webCrypto.generateKey(keyGenOpt, true, ['encrypt', 'decrypt']);
}

View File

@ -38,6 +38,7 @@ import config from '../config';
* 3 = PGP MESSAGE
* 4 = PUBLIC KEY BLOCK
* 5 = PRIVATE KEY BLOCK
* 6 = SIGNATURE
*/
function getType(text) {
var reHeader = /^-----BEGIN PGP (MESSAGE, PART \d+\/\d+|MESSAGE, PART \d+|SIGNED MESSAGE|MESSAGE|PUBLIC KEY BLOCK|PRIVATE KEY BLOCK|SIGNATURE)-----$\n/m;
@ -62,10 +63,7 @@ function getType(text) {
return enums.armor.multipart_last;
} else
// BEGIN PGP SIGNATURE
// Used for detached signatures, OpenPGP/MIME signatures, and
// cleartext signatures. Note that PGP 2.x uses BEGIN PGP MESSAGE
// for detached signatures.
// BEGIN PGP SIGNED MESSAGE
if (/SIGNED MESSAGE/.test(header[1])) {
return enums.armor.signed;
@ -86,6 +84,14 @@ function getType(text) {
// Used for armoring private keys.
if (/PRIVATE KEY BLOCK/.test(header[1])) {
return enums.armor.private_key;
} else
// BEGIN PGP SIGNATURE
// Used for detached signatures, OpenPGP/MIME signatures, and
// cleartext signatures. Note that PGP 2.x uses BEGIN PGP MESSAGE
// for detached signatures.
if (/SIGNATURE/.test(header[1])) {
return enums.armor.signature;
}
}
@ -175,30 +181,9 @@ var crc_table = [
function createcrc24(input) {
var crc = 0xB704CE;
var index = 0;
while ((input.length - index) > 16) {
for (var index = 0; index < input.length; index++) {
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 1]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 2]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 3]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 4]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 5]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 6]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 7]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 8]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 9]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 10]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 11]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 12]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 13]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 14]) & 0xff];
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index + 15]) & 0xff];
index += 16;
}
for (var j = index; j < input.length; j++) {
crc = (crc << 8) ^ crc_table[((crc >> 16) ^ input[index++]) & 0xff];
}
return crc & 0xffffff;
}
@ -206,8 +191,7 @@ function createcrc24(input) {
/**
* Splits a message into two parts, the headers and the body. This is an internal function
* @param {String} text OpenPGP armored message part
* @returns {(Boolean|Object)} Either false in case of an error
* or an object with attribute "headers" containing the headers and
* @returns {Object} An object with attribute "headers" containing the headers
* and an attribute "body" containing the body.
*/
function splitHeaders(text) {
@ -240,29 +224,31 @@ function splitHeaders(text) {
*/
function verifyHeaders(headers) {
for (var i = 0; i < headers.length; i++) {
if (!/^(Version|Comment|MessageID|Hash|Charset): .+$/.test(headers[i])) {
if (!/^[^:\s]+: .+$/.test(headers[i])) {
throw new Error('Improperly formatted armor header: ' + headers[i]);
}
if (config.debug && !/^(Version|Comment|MessageID|Hash|Charset): .+$/.test(headers[i])) {
console.log('Unknown header: ' + headers[i]);
}
}
}
/**
* Splits a message into two parts, the body and the checksum. This is an internal function
* @param {String} text OpenPGP armored message part
* @returns {(Boolean|Object)} Either false in case of an error
* or an object with attribute "body" containing the body
* @returns {Object} An object with attribute "body" containing the body
* and an attribute "checksum" containing the checksum.
*/
function splitChecksum(text) {
var reChecksumStart = /^=/m;
text = text.trim();
var body = text;
var checksum = "";
var matchResult = reChecksumStart.exec(text);
var lastEquals = text.lastIndexOf("=");
if (matchResult !== null) {
body = text.slice(0, matchResult.index);
checksum = text.slice(matchResult.index + 1);
if (lastEquals >= 0 && lastEquals !== text.length - 1) { // '=' as the last char means no checksum
body = text.slice(0, lastEquals);
checksum = text.slice(lastEquals + 1).substr(0, 4);
}
return { body: body, checksum: checksum };
@ -284,6 +270,7 @@ function dearmor(text) {
var type = getType(text);
text = text.trim() + "\n";
var splittext = text.split(reSplit);
// IE has a bug in split with a re. If the pattern matches the beginning of the
@ -325,12 +312,9 @@ function dearmor(text) {
checksum = sig_sum.checksum;
}
checksum = checksum.substr(0, 4);
if (!verifyCheckSum(result.data, checksum)) {
throw new Error("Ascii armor integrity check on message failed: '" +
checksum +
"' should be '" +
if (!verifyCheckSum(result.data, checksum) && (checksum || config.checksum_required)) {
// will NOT throw error if checksum is empty AND checksum is not required (GPG compatibility)
throw new Error("Ascii armor integrity check on message failed: '" + checksum + "' should be '" +
getCheckSum(result.data) + "'");
}
@ -397,6 +381,13 @@ function armor(messagetype, body, partindex, parttotal) {
result.push("\r\n=" + getCheckSum(body) + "\r\n");
result.push("-----END PGP PRIVATE KEY BLOCK-----\r\n");
break;
case enums.armor.signature:
result.push("-----BEGIN PGP SIGNATURE-----\r\n");
result.push(addheader());
result.push(base64.encode(body));
result.push("\r\n=" + getCheckSum(body) + "\r\n");
result.push("-----END PGP SIGNATURE-----\r\n");
break;
}
return result.join('');

View File

@ -297,7 +297,8 @@ export default {
signed: 2,
message: 3,
public_key: 4,
private_key: 5
private_key: 5,
signature: 6
},
/** Asserts validity and converts from string/integer to integer. */

View File

@ -26,6 +26,13 @@ export * from './openpgp';
import * as keyMod from './key';
export const key = keyMod;
/**
* @see module:signature
* @name module:openpgp.signature
*/
import * as signatureMod from './signature';
export const signature = signatureMod;
/**
* @see module:message
* @name module:openpgp.message

View File

@ -130,7 +130,7 @@ Key.prototype.packetlist2structure = function(packetlist) {
util.print_debug('Dropping subkey binding signature without preceding subkey packet');
continue;
}
subKey.bindingSignature = packetlist[i];
subKey.bindingSignatures.push(packetlist[i]);
break;
case enums.signature.key_revocation:
this.revocationSignature = packetlist[i];
@ -358,7 +358,7 @@ Key.prototype.getEncryptionKeyPacket = function() {
}
// if no valid subkey for encryption, evaluate primary key
var primaryUser = this.getPrimaryUser();
if (primaryUser &&
if (primaryUser && primaryUser.selfCertificate && !primaryUser.selfCertificate.isExpired() &&
isValidEncryptionKeyPacket(this.primaryKey, primaryUser.selfCertificate)) {
return this.primaryKey;
}
@ -511,7 +511,7 @@ Key.prototype.getPrimaryUser = function() {
continue;
}
for (var j = 0; j < this.users[i].selfCertifications.length; j++) {
primUser.push({user: this.users[i], selfCertificate: this.users[i].selfCertifications[j]});
primUser.push({index: i, user: this.users[i], selfCertificate: this.users[i].selfCertifications[j]});
}
}
// sort by primary user flag and signature creation time
@ -638,6 +638,64 @@ Key.prototype.revoke = function() {
};
/**
* Signs primary user of key
* @param {Array<module:key~Key>} privateKey decrypted private keys for signing
* @return {module:key~Key} new public key with new certificate signature
*/
Key.prototype.signPrimaryUser = function(privateKeys) {
var {index, user} = this.getPrimaryUser() || {};
if (!user) {
throw new Error('Could not find primary user');
}
user = user.sign(this.primaryKey, privateKeys);
var key = new Key(this.toPacketlist());
key.users[index] = user;
return key;
};
/**
* Signs all users of key
* @param {Array<module:key~Key>} privateKeys decrypted private keys for signing
* @return {module:key~Key} new public key with new certificate signature
*/
Key.prototype.signAllUsers = function(privateKeys) {
var users = this.users.map(user => user.sign(this.primaryKey, privateKeys));
var key = new Key(this.toPacketlist());
key.users = users;
return key;
};
/**
* Verifies primary user of key
* @param {Array<module:key~Key>} keys array of keys to verify certificate signatures
* @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature
*/
Key.prototype.verifyPrimaryUser = function(keys) {
var {user} = this.getPrimaryUser() || {};
if (!user) {
throw new Error('Could not find primary user');
}
return user.verifyAllSignatures(this.primaryKey, keys);
};
/**
* Verifies all users of key
* @param {Array<module:key~Key>} keys array of keys to verify certificate signatures
* @return {Array<({userid: String, keyid: module:type/keyid, valid: Boolean})>} list of userid, signer's keyid and validity of signature
*/
Key.prototype.verifyAllUsers = function(keys) {
return this.users.reduce((signatures, user) => {
return signatures.concat(
user.verifyAllSignatures(this.primaryKey, keys).map(signature => ({
userid: user.userId.userid,
keyid: signature.keyid,
valid: signature.valid
}))
);
}, []);
};
/**
* @class
* @classdesc Class that represents an user ID or attribute packet and the relevant signatures.
@ -727,6 +785,66 @@ User.prototype.isValidSelfCertificate = function(primaryKey, selfCertificate) {
return false;
};
/**
* Signs user
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
* @param {Array<module:key~Key>} privateKeys decrypted private keys for signing
* @return {module:key~Key} new user with new certificate signatures
*/
User.prototype.sign = function(primaryKey, privateKeys) {
var user, dataToSign, signingKeyPacket, signaturePacket;
dataToSign = {};
dataToSign.key = primaryKey;
dataToSign.userid = this.userId || this.userAttribute;
user = new User(this.userId || this.userAttribute);
user.otherCertifications = [];
privateKeys.forEach(privateKey => {
if (privateKey.isPublic()) {
throw new Error('Need private key for signing');
}
if (privateKey.primaryKey.getFingerprint() === primaryKey.getFingerprint()) {
throw new Error('Not implemented for self signing');
}
signingKeyPacket = privateKey.getSigningKeyPacket();
if (!signingKeyPacket) {
throw new Error('Could not find valid signing key packet');
}
if (!signingKeyPacket.isDecrypted) {
throw new Error('Private key is not decrypted.');
}
signaturePacket = new packet.Signature();
// Most OpenPGP implementations use generic certification (0x10)
signaturePacket.signatureType = enums.write(enums.signature, enums.signature.cert_generic);
signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data];
signaturePacket.hashAlgorithm = privateKey.getPreferredHashAlgorithm();
signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
signaturePacket.signingKeyId = signingKeyPacket.getKeyId();
signaturePacket.sign(signingKeyPacket, dataToSign);
user.otherCertifications.push(signaturePacket);
});
user.update(this, primaryKey);
return user;
};
/**
* Verifies all user signatures
* @param {module:packet/secret_key|module:packet/public_key} primaryKey The primary key packet
* @param {Array<module:key~Key>} keys array of keys to verify certificate signatures
* @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature
*/
User.prototype.verifyAllSignatures = function(primaryKey, keys) {
var dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey };
var certificates = this.selfCertifications.concat(this.otherCertifications || []);
return certificates.map(signaturePacket => {
var keyPackets = keys.filter(key => key.getSigningKeyPacket(signaturePacket.issuerKeyId));
var valid = null;
if (keyPackets.length > 0) {
valid = keyPackets.some(keyPacket => signaturePacket.verify(keyPacket.primaryKey, dataToVerify));
}
return { keyid: signaturePacket.issuerKeyId, valid: valid };
});
};
/**
* Verify User. Checks for existence of self signatures, revocation signatures
* and validity of self signature
@ -785,7 +903,7 @@ function SubKey(subKeyPacket) {
return new SubKey(subKeyPacket);
}
this.subKey = subKeyPacket;
this.bindingSignature = null;
this.bindingSignatures = [];
this.revocationSignature = null;
}
@ -797,7 +915,9 @@ SubKey.prototype.toPacketlist = function() {
var packetlist = new packet.List();
packetlist.push(this.subKey);
packetlist.push(this.revocationSignature);
packetlist.push(this.bindingSignature);
for(var i = 0; i < this.bindingSignatures.length; i++) {
packetlist.push(this.bindingSignatures[i]);
}
return packetlist;
};
@ -807,8 +927,15 @@ SubKey.prototype.toPacketlist = function() {
* @return {Boolean}
*/
SubKey.prototype.isValidEncryptionKey = function(primaryKey) {
return this.verify(primaryKey) === enums.keyStatus.valid &&
isValidEncryptionKeyPacket(this.subKey, this.bindingSignature);
if(this.verify(primaryKey) !== enums.keyStatus.valid) {
return false;
}
for(var i = 0; i < this.bindingSignatures.length; i++) {
if(isValidEncryptionKeyPacket(this.subKey, this.bindingSignatures[i])) {
return true;
}
}
return false;
};
/**
@ -817,8 +944,15 @@ SubKey.prototype.isValidEncryptionKey = function(primaryKey) {
* @return {Boolean}
*/
SubKey.prototype.isValidSigningKey = function(primaryKey) {
return this.verify(primaryKey) === enums.keyStatus.valid &&
isValidSigningKeyPacket(this.subKey, this.bindingSignature);
if(this.verify(primaryKey) !== enums.keyStatus.valid) {
return false;
}
for(var i = 0; i < this.bindingSignatures.length; i++) {
if(isValidSigningKeyPacket(this.subKey, this.bindingSignatures[i])) {
return true;
}
}
return false;
};
/**
@ -838,24 +972,39 @@ SubKey.prototype.verify = function(primaryKey) {
Date.now() > (this.subKey.created.getTime() + this.subKey.expirationTimeV3*24*3600*1000)) {
return enums.keyStatus.expired;
}
// check subkey binding signature
if (!this.bindingSignature) {
return enums.keyStatus.invalid;
// check subkey binding signatures (at least one valid binding sig needed)
for(var i = 0; i < this.bindingSignatures.length; i++) {
var isLast = (i === this.bindingSignatures.length - 1);
var sig = this.bindingSignatures[i];
// check binding signature is not expired
if(sig.isExpired()) {
if(isLast) {
return enums.keyStatus.expired; // last expired binding signature
} else {
continue;
}
}
// check binding signature can verify
if (!(sig.verified || sig.verify(primaryKey, {key: primaryKey, bind: this.subKey}))) {
if(isLast) {
return enums.keyStatus.invalid; // last invalid binding signature
} else {
continue;
}
}
// check V4 expiration time
if (this.subKey.version === 4) {
if(sig.keyNeverExpires === false && Date.now() > (this.subKey.created.getTime() + sig.keyExpirationTime*1000)) {
if(isLast) {
return enums.keyStatus.expired; // last V4 expired binding signature
} else {
continue;
}
}
}
return enums.keyStatus.valid; // found a binding signature that passed all checks
}
if (this.bindingSignature.isExpired()) {
return enums.keyStatus.expired;
}
if (!(this.bindingSignature.verified ||
this.bindingSignature.verify(primaryKey, {key: primaryKey, bind: this.subKey}))) {
return enums.keyStatus.invalid;
}
// check V4 expiration time
if (this.subKey.version === 4 &&
this.bindingSignature.keyNeverExpires === false &&
Date.now() > (this.subKey.created.getTime() + this.bindingSignature.keyExpirationTime*1000)) {
return enums.keyStatus.expired;
}
return enums.keyStatus.valid;
return enums.keyStatus.invalid; // no binding signatures to check
};
/**
@ -863,7 +1012,17 @@ SubKey.prototype.verify = function(primaryKey) {
* @return {Date|null}
*/
SubKey.prototype.getExpirationTime = function() {
return getExpirationTime(this.subKey, this.bindingSignature);
var highest;
for(var i = 0; i < this.bindingSignatures.length; i++) {
var current = getExpirationTime(this.subKey, this.bindingSignatures[i]);
if(current === null) {
return null;
}
if(!highest || current > highest) {
highest = current;
}
}
return highest;
};
/**
@ -883,11 +1042,14 @@ SubKey.prototype.update = function(subKey, primaryKey) {
subKey.subKey.tag === enums.packet.secretSubkey) {
this.subKey = subKey.subKey;
}
// binding signature
if (!this.bindingSignature && subKey.bindingSignature &&
(subKey.bindingSignature.verified ||
subKey.bindingSignature.verify(primaryKey, {key: primaryKey, bind: this.subKey}))) {
this.bindingSignature = subKey.bindingSignature;
// update missing binding signatures
if(this.bindingSignatures.length < subKey.bindingSignatures.length) {
for(var i = this.bindingSignatures.length; i < subKey.bindingSignatures.length; i++) {
var newSig = subKey.bindingSignatures[i];
if (newSig.verified || newSig.verify(primaryKey, {key: primaryKey, bind: this.subKey})) {
this.bindingSignatures.push(newSig);
}
}
}
// revocation signature
if (!this.revocationSignature && subKey.revocationSignature && !subKey.revocationSignature.isExpired() &&
@ -898,24 +1060,20 @@ SubKey.prototype.update = function(subKey, primaryKey) {
};
/**
* Reads an OpenPGP armored text and returns one or multiple key objects
* @param {String} armoredText text to be parsed
* Reads an unarmored OpenPGP key list and returns one or multiple key objects
* @param {Uint8Array} data to be parsed
* @return {{keys: Array<module:key~Key>, err: (Array<Error>|null)}} result object with key and error arrays
* @static
*/
export function readArmored(armoredText) {
export function read(data) {
var result = {};
result.keys = [];
try {
var input = armor.decode(armoredText);
if (!(input.type === enums.armor.public_key || input.type === enums.armor.private_key)) {
throw new Error('Armored text not of type key');
}
var packetlist = new packet.List();
packetlist.read(input.data);
packetlist.read(data);
var keyIndex = packetlist.indexOfTag(enums.packet.publicKey, enums.packet.secretKey);
if (keyIndex.length === 0) {
throw new Error('No key packet found in armored text');
throw new Error('No key packet found');
}
for (var i = 0; i < keyIndex.length; i++) {
var oneKeyList = packetlist.slice(keyIndex[i], keyIndex[i + 1]);
@ -934,6 +1092,26 @@ export function readArmored(armoredText) {
return result;
}
/**
* Reads an OpenPGP armored text and returns one or multiple key objects
* @param {String} armoredText text to be parsed
* @return {{keys: Array<module:key~Key>, err: (Array<Error>|null)}} result object with key and error arrays
* @static
*/
export function readArmored(armoredText) {
try {
var input = armor.decode(armoredText);
if (!(input.type === enums.armor.public_key || input.type === enums.armor.private_key)) {
throw new Error('Armored text not of type key');
}
return read(input.data);
} catch (e) {
var result = {keys: [], err: []};
result.err.push(e);
return result;
}
}
/**
* Generates a new OpenPGP key. Currently only supports RSA keys.
* Primary and subkey will be of same type.
@ -944,11 +1122,12 @@ export function readArmored(armoredText) {
If array is used, the first userId is set as primary user Id
* @param {String} options.passphrase The passphrase used to encrypt the resulting private key
* @param {Boolean} [options.unlocked=false] The secret part of the generated key is unlocked
* @param {Number} [options.keyExpirationTime=0] The number of seconds after the key creation time that the key expires
* @return {module:key~Key}
* @static
*/
export function generate(options) {
var packetlist, secretKeyPacket, userIdPacket, dataToSign, signaturePacket, secretSubkeyPacket, subkeySignaturePacket;
var secretKeyPacket, secretSubkeyPacket;
return Promise.resolve().then(() => {
options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign;
if (options.keyType !== enums.publicKey.rsa_encrypt_sign) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated
@ -962,7 +1141,9 @@ export function generate(options) {
options.userIds = [options.userIds];
}
return Promise.all([generateSecretKey(), generateSecretSubkey()]).then(wrapKeyObject);
return Promise.all([generateSecretKey(), generateSecretSubkey()]).then(() => {
return wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options);
});
});
function generateSecretKey() {
@ -976,80 +1157,127 @@ export function generate(options) {
secretSubkeyPacket.algorithm = enums.read(enums.publicKey, options.keyType);
return secretSubkeyPacket.generate(options.numBits);
}
}
function wrapKeyObject() {
// set passphrase protection
if (options.passphrase) {
secretKeyPacket.encrypt(options.passphrase);
secretSubkeyPacket.encrypt(options.passphrase);
/**
* Reformats and signs an OpenPGP with a given User ID. Currently only supports RSA keys.
* @param {module:key~Key} options.privateKey The private key to reformat
* @param {module:enums.publicKey} [options.keyType=module:enums.publicKey.rsa_encrypt_sign]
* @param {String|Array<String>} options.userIds assumes already in form of "User Name <username@email.com>"
If array is used, the first userId is set as primary user Id
* @param {String} options.passphrase The passphrase used to encrypt the resulting private key
* @param {Boolean} [options.unlocked=false] The secret part of the generated key is unlocked
* @param {Number} [options.keyExpirationTime=0] The number of seconds after the key creation time that the key expires
* @return {module:key~Key}
* @static
*/
export function reformat(options) {
var secretKeyPacket, secretSubkeyPacket;
return Promise.resolve().then(() => {
options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign;
if (options.keyType !== enums.publicKey.rsa_encrypt_sign) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated
throw new Error('Only RSA Encrypt or Sign supported');
}
packetlist = new packet.List();
packetlist.push(secretKeyPacket);
options.userIds.forEach(function(userId, index) {
userIdPacket = new packet.Userid();
userIdPacket.read(util.str2Uint8Array(userId));
dataToSign = {};
dataToSign.userid = userIdPacket;
dataToSign.key = secretKeyPacket;
signaturePacket = new packet.Signature();
signaturePacket.signatureType = enums.signature.cert_generic;
signaturePacket.publicKeyAlgorithm = options.keyType;
signaturePacket.hashAlgorithm = config.prefer_hash_algorithm;
signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data];
signaturePacket.preferredSymmetricAlgorithms = [];
// prefer aes256, aes128, then aes192 (no WebCrypto support: https://www.chromium.org/blink/webcrypto#TOC-AES-support)
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes256);
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes128);
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes192);
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.cast5);
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.tripledes);
signaturePacket.preferredHashAlgorithms = [];
// prefer fast asm.js implementations (SHA-256, SHA-1)
signaturePacket.preferredHashAlgorithms.push(enums.hash.sha256);
signaturePacket.preferredHashAlgorithms.push(enums.hash.sha1);
signaturePacket.preferredHashAlgorithms.push(enums.hash.sha512);
signaturePacket.preferredCompressionAlgorithms = [];
signaturePacket.preferredCompressionAlgorithms.push(enums.compression.zlib);
signaturePacket.preferredCompressionAlgorithms.push(enums.compression.zip);
if (index === 0) {
signaturePacket.isPrimaryUserID = true;
}
if (config.integrity_protect) {
signaturePacket.features = [];
signaturePacket.features.push(1); // Modification Detection
}
signaturePacket.sign(secretKeyPacket, dataToSign);
packetlist.push(userIdPacket);
packetlist.push(signaturePacket);
});
dataToSign = {};
dataToSign.key = secretKeyPacket;
dataToSign.bind = secretSubkeyPacket;
subkeySignaturePacket = new packet.Signature();
subkeySignaturePacket.signatureType = enums.signature.subkey_binding;
subkeySignaturePacket.publicKeyAlgorithm = options.keyType;
subkeySignaturePacket.hashAlgorithm = config.prefer_hash_algorithm;
subkeySignaturePacket.keyFlags = [enums.keyFlags.encrypt_communication | enums.keyFlags.encrypt_storage];
subkeySignaturePacket.sign(secretKeyPacket, dataToSign);
packetlist.push(secretSubkeyPacket);
packetlist.push(subkeySignaturePacket);
if (!options.unlocked) {
secretKeyPacket.clearPrivateMPIs();
secretSubkeyPacket.clearPrivateMPIs();
if (!options.passphrase) { // Key without passphrase is unlocked by definition
options.unlocked = true;
}
if (String.prototype.isPrototypeOf(options.userIds) || typeof options.userIds === 'string') {
options.userIds = [options.userIds];
}
var packetlist = options.privateKey.toPacketlist();
for (var i = 0; i < packetlist.length; i++) {
if (packetlist[i].tag === enums.packet.secretKey) {
secretKeyPacket = packetlist[i];
} else if (packetlist[i].tag === enums.packet.secretSubkey) {
secretSubkeyPacket = packetlist[i];
}
}
return wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options);
});
}
return new Key(packetlist);
function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) {
// set passphrase protection
if (options.passphrase) {
secretKeyPacket.encrypt(options.passphrase);
secretSubkeyPacket.encrypt(options.passphrase);
}
var packetlist = new packet.List();
packetlist.push(secretKeyPacket);
options.userIds.forEach(function(userId, index) {
var userIdPacket = new packet.Userid();
userIdPacket.read(util.str2Uint8Array(userId));
var dataToSign = {};
dataToSign.userid = userIdPacket;
dataToSign.key = secretKeyPacket;
var signaturePacket = new packet.Signature();
signaturePacket.signatureType = enums.signature.cert_generic;
signaturePacket.publicKeyAlgorithm = options.keyType;
signaturePacket.hashAlgorithm = config.prefer_hash_algorithm;
signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data];
signaturePacket.preferredSymmetricAlgorithms = [];
// prefer aes256, aes128, then aes192 (no WebCrypto support: https://www.chromium.org/blink/webcrypto#TOC-AES-support)
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes256);
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes128);
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes192);
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.cast5);
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.tripledes);
signaturePacket.preferredHashAlgorithms = [];
// prefer fast asm.js implementations (SHA-256). SHA-1 will not be secure much longer...move to bottom of list
signaturePacket.preferredHashAlgorithms.push(enums.hash.sha256);
signaturePacket.preferredHashAlgorithms.push(enums.hash.sha512);
signaturePacket.preferredHashAlgorithms.push(enums.hash.sha1);
signaturePacket.preferredCompressionAlgorithms = [];
signaturePacket.preferredCompressionAlgorithms.push(enums.compression.zlib);
signaturePacket.preferredCompressionAlgorithms.push(enums.compression.zip);
if (index === 0) {
signaturePacket.isPrimaryUserID = true;
}
if (config.integrity_protect) {
signaturePacket.features = [];
signaturePacket.features.push(1); // Modification Detection
}
if (options.keyExpirationTime > 0) {
signaturePacket.keyExpirationTime = options.keyExpirationTime;
signaturePacket.keyNeverExpires = false;
}
signaturePacket.sign(secretKeyPacket, dataToSign);
packetlist.push(userIdPacket);
packetlist.push(signaturePacket);
});
var dataToSign = {};
dataToSign.key = secretKeyPacket;
dataToSign.bind = secretSubkeyPacket;
var subkeySignaturePacket = new packet.Signature();
subkeySignaturePacket.signatureType = enums.signature.subkey_binding;
subkeySignaturePacket.publicKeyAlgorithm = options.keyType;
subkeySignaturePacket.hashAlgorithm = config.prefer_hash_algorithm;
subkeySignaturePacket.keyFlags = [enums.keyFlags.encrypt_communication | enums.keyFlags.encrypt_storage];
if (options.keyExpirationTime > 0) {
subkeySignaturePacket.keyExpirationTime = options.keyExpirationTime;
subkeySignaturePacket.keyNeverExpires = false;
}
subkeySignaturePacket.sign(secretKeyPacket, dataToSign);
packetlist.push(secretSubkeyPacket);
packetlist.push(subkeySignaturePacket);
if (!options.unlocked) {
secretKeyPacket.clearPrivateMPIs();
secretSubkeyPacket.clearPrivateMPIs();
}
return new Key(packetlist);
}
/**

View File

@ -32,7 +32,7 @@ import LocalStore from './localstore.js';
* Initialization routine for the keyring. This method reads the
* keyring from HTML5 local storage and initializes this instance.
* @constructor
* @param {class} [storeHandler] class implementing load() and store() methods
* @param {class} [storeHandler] class implementing loadPublic(), loadPrivate(), storePublic(), and storePrivate() methods
*/
export default function Keyring(storeHandler) {
this.storeHandler = storeHandler || new LocalStore();

View File

@ -32,6 +32,7 @@ import enums from './enums.js';
import armor from './encoding/armor.js';
import config from './config';
import crypto from './crypto';
import * as sigModule from './signature.js';
import * as keyModule from './key.js';
/**
@ -291,10 +292,11 @@ export function encryptSessionKey(sessionKey, symAlgo, publicKeys, passwords) {
/**
* Sign the message (the literal data packet of the message)
* @param {Array<module:key~Key>} privateKey private keys with decrypted secret key data for signing
* @return {module:message~Message} new message with signed content
* @param {Array<module:key~Key>} privateKey private keys with decrypted secret key data for signing
* @param {Signature} signature (optional) any existing detached signature to add to the message
* @return {module:message~Message} new message with signed content
*/
Message.prototype.sign = function(privateKeys) {
Message.prototype.sign = function(privateKeys=[], signature=null) {
var packetlist = new packet.List();
@ -306,12 +308,30 @@ Message.prototype.sign = function(privateKeys) {
var literalFormat = enums.write(enums.literal, literalDataPacket.format);
var signatureType = literalFormat === enums.literal.binary ?
enums.signature.binary : enums.signature.text;
var i, signingKeyPacket;
var i, signingKeyPacket, existingSigPacketlist, onePassSig;
if (signature) {
existingSigPacketlist = signature.packets.filterByTag(enums.packet.signature);
if (existingSigPacketlist.length) {
for (i = existingSigPacketlist.length - 1; i >= 0; i--) {
var sigPacket = existingSigPacketlist[i];
onePassSig = new packet.OnePassSignature();
onePassSig.type = signatureType;
onePassSig.hashAlgorithm = config.prefer_hash_algorithm;
onePassSig.publicKeyAlgorithm = sigPacket.publicKeyAlgorithm;
onePassSig.signingKeyId = sigPacket.issuerKeyId;
if (!privateKeys.length && i === 0) {
onePassSig.flags = 1;
}
packetlist.push(onePassSig);
}
}
}
for (i = 0; i < privateKeys.length; i++) {
if (privateKeys[i].isPublic()) {
throw new Error('Need private key for signing');
}
var onePassSig = new packet.OnePassSignature();
onePassSig = new packet.OnePassSignature();
onePassSig.type = signatureType;
//TODO get preferred hashg algo from key signature
onePassSig.hashAlgorithm = config.prefer_hash_algorithm;
@ -321,6 +341,9 @@ Message.prototype.sign = function(privateKeys) {
}
onePassSig.publicKeyAlgorithm = signingKeyPacket.algorithm;
onePassSig.signingKeyId = signingKeyPacket.getKeyId();
if (i === privateKeys.length - 1) {
onePassSig.flags = 1;
}
packetlist.push(onePassSig);
}
@ -338,22 +361,93 @@ Message.prototype.sign = function(privateKeys) {
packetlist.push(signaturePacket);
}
if (signature) {
packetlist.concat(existingSigPacketlist);
}
return new Message(packetlist);
};
/**
* Create a detached signature for the message (the literal data packet of the message)
* @param {Array<module:key~Key>} privateKey private keys with decrypted secret key data for signing
* @param {Signature} signature (optional) any existing detached signature
* @return {module:signature~Signature} new detached signature of message content
*/
Message.prototype.signDetached = function(privateKeys=[], signature=null) {
var packetlist = new packet.List();
var literalDataPacket = this.packets.findPacket(enums.packet.literal);
if (!literalDataPacket) {
throw new Error('No literal data packet to sign.');
}
var literalFormat = enums.write(enums.literal, literalDataPacket.format);
var signatureType = literalFormat === enums.literal.binary ?
enums.signature.binary : enums.signature.text;
for (var i = 0; i < privateKeys.length; i++) {
var signingKeyPacket = privateKeys[i].getSigningKeyPacket();
var signaturePacket = new packet.Signature();
signaturePacket.signatureType = signatureType;
signaturePacket.hashAlgorithm = config.prefer_hash_algorithm;
signaturePacket.publicKeyAlgorithm = signingKeyPacket.algorithm;
if (!signingKeyPacket.isDecrypted) {
throw new Error('Private key is not decrypted.');
}
signaturePacket.sign(signingKeyPacket, literalDataPacket);
packetlist.push(signaturePacket);
}
if (signature) {
var existingSigPacketlist = signature.packets.filterByTag(enums.packet.signature);
packetlist.concat(existingSigPacketlist);
}
return new sigModule.Signature(packetlist);
};
/**
* Verify message signatures
* @param {Array<module:key~Key>} keys array of keys to verify signatures
* @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature
*/
Message.prototype.verify = function(keys) {
var result = [];
var msg = this.unwrapCompressed();
var literalDataList = msg.packets.filterByTag(enums.packet.literal);
if (literalDataList.length !== 1) {
throw new Error('Can only verify message with one literal data packet.');
}
var signatureList = msg.packets.filterByTag(enums.packet.signature);
return createVerificationObjects(signatureList, literalDataList, keys);
};
/**
* Verify detached message signature
* @param {Array<module:key~Key>} keys array of keys to verify signatures
* @param {Signature}
* @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature
*/
Message.prototype.verifyDetached = function(signature, keys) {
var msg = this.unwrapCompressed();
var literalDataList = msg.packets.filterByTag(enums.packet.literal);
if (literalDataList.length !== 1) {
throw new Error('Can only verify message with one literal data packet.');
}
var signatureList = signature.packets;
return createVerificationObjects(signatureList, literalDataList, keys);
};
/**
* Create list of objects containing signer's keyid and validity of signature
* @param {Array<module:packet/signature>} signatureList array of signature packets
* @param {Array<module:packet/literal>} literalDataList array of literal data packets
* @param {Array<module:key~Key>} keys array of keys to verify signatures
* @return {Array<({keyid: module:type/keyid, valid: Boolean})>} list of signer's keyid and validity of signature
*/
function createVerificationObjects(signatureList, literalDataList, keys) {
var result = [];
for (var i = 0; i < signatureList.length; i++) {
var keyPacket = null;
for (var j = 0; j < keys.length; j++) {
@ -365,16 +459,22 @@ Message.prototype.verify = function(keys) {
var verifiedSig = {};
if (keyPacket) {
//found a key packet that matches keyId of signature
verifiedSig.keyid = signatureList[i].issuerKeyId;
verifiedSig.valid = signatureList[i].verify(keyPacket, literalDataList[0]);
} else {
verifiedSig.keyid = signatureList[i].issuerKeyId;
verifiedSig.valid = null;
}
var packetlist = new packet.List();
packetlist.push(signatureList[i]);
verifiedSig.signature = new sigModule.Signature(packetlist);
result.push(verifiedSig);
}
return result;
};
}
/**
* Unwrap compressed message

View File

@ -92,12 +92,13 @@ export function destroyWorker() {
* @param {String} passphrase (optional) The passphrase used to encrypt the resulting private key
* @param {Number} numBits (optional) number of bits for the key creation. (should be 2048 or 4096)
* @param {Boolean} unlocked (optional) If the returned secret part of the generated key is unlocked
* @param {Number} keyExpirationTime (optional) The number of seconds after the key creation time that the key expires
* @return {Promise<Object>} The generated key object in the form:
* { key:Key, privateKeyArmored:String, publicKeyArmored:String }
* @static
*/
export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false } = {}) {
const options = formatUserIds({ userIds, passphrase, numBits, unlocked });
export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=false, keyExpirationTime=0 } = {}) {
const options = formatUserIds({ userIds, passphrase, numBits, unlocked, keyExpirationTime });
if (!util.getWebCryptoAll() && asyncProxy) { // use web worker if web crypto apis are not supported
return asyncProxy.delegate('generateKey', options);
@ -112,6 +113,32 @@ export function generateKey({ userIds=[], passphrase, numBits=2048, unlocked=fal
})).catch(onError.bind(null, 'Error generating keypair'));
}
/**
* Reformats signature packets for a key and rewraps key object.
* @param {Array<Object>} userIds array of user IDs e.g. [{ name:'Phil Zimmermann', email:'phil@openpgp.org' }]
* @param {String} passphrase (optional) The passphrase used to encrypt the resulting private key
* @param {Boolean} unlocked (optional) If the returned secret part of the generated key is unlocked
* @param {Number} keyExpirationTime (optional) The number of seconds after the key creation time that the key expires
* @return {Promise<Object>} The generated key object in the form:
* { key:Key, privateKeyArmored:String, publicKeyArmored:String }
* @static
*/
export function reformatKey({ privateKey, userIds=[], passphrase="", unlocked=false, keyExpirationTime=0 } = {}) {
const options = formatUserIds({ privateKey, userIds, passphrase, unlocked, keyExpirationTime });
if (asyncProxy) {
return asyncProxy.delegate('reformatKey', options);
}
return key.reformat(options).then(newKey => ({
key: newKey,
privateKeyArmored: newKey.armor(),
publicKeyArmored: newKey.toPublic().armor()
})).catch(onError.bind(null, 'Error reformatting keypair'));
}
/**
* Unlock a private key with your passphrase.
* @param {Key} privateKey the private key that is to be decrypted
@ -151,36 +178,48 @@ export function decryptKey({ privateKey, passphrase }) {
* @param {Key|Array<Key>} privateKeys (optional) private keys for signing. If omitted message will not be signed
* @param {String|Array<String>} passwords (optional) array of passwords or a single password to encrypt the message
* @param {String} filename (optional) a filename for the literal data packet
* @param {Boolean} armor (optional) if the return value should be ascii armored or the message object
* @return {Promise<String|Message>} encrypted ASCII armored message, or the full Message object if 'armor' is false
* @param {Boolean} armor (optional) if the return values should be ascii armored or the message/signature objects
* @param {Boolean} detached (optional) if the signature should be detached (if true, signature will be added to returned object)
* @param {Signature} signature (optional) a detached signature to add to the encrypted message
* @return {Promise<Object>} encrypted (and optionally signed message) in the form:
* {data: ASCII armored message if 'armor' is true,
* message: full Message object if 'armor' is false, signature: detached signature if 'detached' is true}
* @static
*/
export function encrypt({ data, publicKeys, privateKeys, passwords, filename, armor=true }) {
export function encrypt({ data, publicKeys, privateKeys, passwords, filename, armor=true, detached=false, signature=null }) {
checkData(data); publicKeys = toArray(publicKeys); privateKeys = toArray(privateKeys); passwords = toArray(passwords);
if (!nativeAEAD() && asyncProxy) { // use web worker if web crypto apis are not supported
return asyncProxy.delegate('encrypt', { data, publicKeys, privateKeys, passwords, filename, armor });
return asyncProxy.delegate('encrypt', { data, publicKeys, privateKeys, passwords, filename, armor, detached, signature });
}
var result = {};
return Promise.resolve().then(() => {
let message = createMessage(data, filename);
if (privateKeys) { // sign the message only if private keys are specified
message = message.sign(privateKeys);
if (!privateKeys) {
privateKeys = [];
}
if (privateKeys.length || signature) { // sign the message only if private keys or signature is specified
if (detached) {
var detachedSignature = message.signDetached(privateKeys, signature);
if (armor) {
result.signature = detachedSignature.armor();
} else {
result.signature = detachedSignature;
}
} else {
message = message.sign(privateKeys, signature);
}
}
return message.encrypt(publicKeys, passwords);
}).then(message => {
if(armor) {
return {
data: message.armor()
};
if (armor) {
result.data = message.armor();
} else {
result.message = message;
}
return {
message: message
};
return result;
}).catch(onError.bind(null, 'Error encrypting message'));
}
@ -193,22 +232,31 @@ export function encrypt({ data, publicKeys, privateKeys, passwords, filename, ar
* @param {Object} sessionKey (optional) session key in the form: { data:Uint8Array, algorithm:String }
* @param {String} password (optional) single password to decrypt the message
* @param {String} format (optional) return data format either as 'utf8' or 'binary'
* @param {Signature} signature (optional) detached signature for verification
* @return {Promise<Object>} decrypted and verified message in the form:
* { data:Uint8Array|String, filename:String, signatures:[{ keyid:String, valid:Boolean }] }
* @static
*/
export function decrypt({ message, privateKey, publicKeys, sessionKey, password, format='utf8' }) {
export function decrypt({ message, privateKey, publicKeys, sessionKey, password, format='utf8', signature=null }) {
checkMessage(message); publicKeys = toArray(publicKeys);
if (!nativeAEAD() && asyncProxy) { // use web worker if web crypto apis are not supported
return asyncProxy.delegate('decrypt', { message, privateKey, publicKeys, sessionKey, password, format });
return asyncProxy.delegate('decrypt', { message, privateKey, publicKeys, sessionKey, password, format, signature });
}
return message.decrypt(privateKey, sessionKey, password).then(message => {
const result = parseMessage(message, format);
if (publicKeys && result.data) { // verify only if publicKeys are specified
result.signatures = message.verify(publicKeys);
if (result.data) { // verify
if (!publicKeys) {
publicKeys = [];
}
if (signature) {
//detached signature
result.signatures = message.verifyDetached(signature, publicKeys);
} else {
result.signatures = message.verify(publicKeys);
}
}
return result;
@ -225,33 +273,50 @@ export function decrypt({ message, privateKey, publicKeys, sessionKey, password,
/**
* Signs a cleartext message.
* @param {String} data cleartext input to be signed
* @param {String | Uint8Array} data cleartext input to be signed
* @param {Key|Array<Key>} privateKeys array of keys or single key with decrypted secret key data to sign cleartext
* @param {Boolean} armor (optional) if the return value should be ascii armored or the message object
* @return {Promise<String|CleartextMessage>} ASCII armored message or the message of type CleartextMessage
* @param {Boolean} detached (optional) if the return value should contain a detached signature
* @return {Promise<Object>} signed cleartext in the form:
* {data: ASCII armored message if 'armor' is true,
* message: full Message object if 'armor' is false, signature: detached signature if 'detached' is true}
* @static
*/
export function sign({ data, privateKeys, armor=true }) {
checkString(data);
export function sign({ data, privateKeys, armor=true, detached=false}) {
checkData(data);
privateKeys = toArray(privateKeys);
if (asyncProxy) { // use web worker if available
return asyncProxy.delegate('sign', { data, privateKeys, armor });
return asyncProxy.delegate('sign', { data, privateKeys, armor, detached });
}
var result = {};
return execute(() => {
var message;
const cleartextMessage = new cleartext.CleartextMessage(data);
cleartextMessage.sign(privateKeys);
if(armor) {
return {
data: cleartextMessage.armor()
};
if (util.isString(data)) {
message = new cleartext.CleartextMessage(data);
} else {
message = messageLib.fromBinary(data);
}
return {
message: cleartextMessage
};
if (detached) {
var signature = message.signDetached(privateKeys);
if (armor) {
result.signature = signature.armor();
} else {
result.signature = signature;
}
} else {
message = message.sign(privateKeys);
}
if (armor) {
result.data = message.armor();
} else {
result.message = message;
}
return result;
}, 'Error signing cleartext message');
}
@ -260,24 +325,35 @@ export function sign({ data, privateKeys, armor=true }) {
* Verifies signatures of cleartext signed message
* @param {Key|Array<Key>} publicKeys array of publicKeys or single key, to verify signatures
* @param {CleartextMessage} message cleartext message object with signatures
* @param {Signature} signature (optional) detached signature for verification
* @return {Promise<Object>} cleartext with status of verified signatures in the form of:
* { data:String, signatures: [{ keyid:String, valid:Boolean }] }
* @static
*/
export function verify({ message, publicKeys }) {
checkCleartextMessage(message);
export function verify({ message, publicKeys, signature=null }) {
checkCleartextOrMessage(message);
publicKeys = toArray(publicKeys);
if (asyncProxy) { // use web worker if available
return asyncProxy.delegate('verify', { message, publicKeys });
return asyncProxy.delegate('verify', { message, publicKeys, signature });
}
return execute(() => ({
var result = {};
return execute(() => {
if (cleartext.CleartextMessage.prototype.isPrototypeOf(message)) {
result.data = message.getText();
} else {
result.data = message.getLiteralData();
}
if (signature) {
//detached signature
result.signatures = message.verifyDetached(signature, publicKeys);
} else {
result.signatures = message.verify(publicKeys);
}
return result;
data: message.getText(),
signatures: message.verify(publicKeys)
}), 'Error verifying cleartext signed message');
}, 'Error verifying cleartext signed message');
}
@ -299,7 +375,7 @@ export function verify({ message, publicKeys }) {
* @static
*/
export function encryptSessionKey({ data, algorithm, publicKeys, passwords }) {
checkbinary(data); checkString(algorithm, 'algorithm'); publicKeys = toArray(publicKeys); passwords = toArray(passwords);
checkBinary(data); checkString(algorithm, 'algorithm'); publicKeys = toArray(publicKeys); passwords = toArray(passwords);
if (asyncProxy) { // use web worker if available
return asyncProxy.delegate('encryptSessionKey', { data, algorithm, publicKeys, passwords });
@ -349,7 +425,7 @@ function checkString(data, name) {
throw new Error('Parameter [' + (name || 'data') + '] must be of type String');
}
}
function checkbinary(data, name) {
function checkBinary(data, name) {
if (!util.isUint8Array(data)) {
throw new Error('Parameter [' + (name || 'data') + '] must be of type Uint8Array');
}
@ -364,9 +440,9 @@ function checkMessage(message) {
throw new Error('Parameter [message] needs to be of type Message');
}
}
function checkCleartextMessage(message) {
if (!cleartext.CleartextMessage.prototype.isPrototypeOf(message)) {
throw new Error('Parameter [message] needs to be of type CleartextMessage');
function checkCleartextOrMessage(message) {
if (!cleartext.CleartextMessage.prototype.isPrototypeOf(message) && !messageLib.Message.prototype.isPrototypeOf(message)) {
throw new Error('Parameter [message] needs to be of type Message or CleartextMessage');
}
}
@ -391,7 +467,11 @@ function formatUserIds(options) {
if (!util.isString(id.name) || (id.email && !util.isEmailAddress(id.email))) {
throw new Error('Invalid user id format');
}
return id.name + ' <' + id.email + '>';
id.name = id.name.trim();
if (id.name.length > 0) {
id.name += ' ';
}
return id.name + '<' + id.email + '>';
});
return options;
}
@ -481,4 +561,4 @@ function onError(message, error) {
*/
function nativeAEAD() {
return util.getWebCrypto() && config.aead_protect;
}
}

View File

@ -26,6 +26,7 @@
import * as key from '../key.js';
import * as message from '../message.js';
import * as cleartext from '../cleartext.js';
import * as signature from '../signature.js'
import Packetlist from './packetlist.js';
import type_keyid from '../type/keyid.js';
@ -55,9 +56,27 @@ export function clonePackets(options) {
if (options.key) {
options.key = options.key.toPacketlist();
}
if (options.message) {
//could be either a Message or CleartextMessage object
if (options.message instanceof message.Message) {
options.message = options.message.packets;
} else if (options.message instanceof cleartext.CleartextMessage) {
options.message.signature = options.message.signature.packets;
}
}
if (options.signature && (options.signature instanceof signature.Signature)) {
options.signature = options.signature.packets;
}
if (options.signatures) {
options.signatures = options.signatures.map(sig => verificationObjectToClone(sig));
}
return options;
}
function verificationObjectToClone(verObject) {
verObject.signature = verObject.signature.packets;
return verObject;
}
//////////////////////////////
// //
@ -85,13 +104,16 @@ export function parseClonedPackets(options, method) {
if (options.key) {
options.key = packetlistCloneToKey(options.key);
}
if (options.message && (method === 'sign' || method === 'verify')) { // sign and verify support only CleartextMessage
if (options.message && options.message.signature) {
options.message = packetlistCloneToCleartextMessage(options.message);
} else if (options.message) {
options.message = packetlistCloneToMessage(options.message);
}
if (options.signatures) {
options.signatures = options.signatures.map(packetlistCloneToSignature);
options.signatures = options.signatures.map(packetlistCloneToSignatures);
}
if (options.signature) {
options.signature = packetlistCloneToSignature(options.signature);
}
return options;
}
@ -102,16 +124,27 @@ function packetlistCloneToKey(clone) {
}
function packetlistCloneToMessage(clone) {
const packetlist = Packetlist.fromStructuredClone(clone.packets);
const packetlist = Packetlist.fromStructuredClone(clone);
return new message.Message(packetlist);
}
function packetlistCloneToCleartextMessage(clone) {
var packetlist = Packetlist.fromStructuredClone(clone.packets);
return new cleartext.CleartextMessage(clone.text, packetlist);
var packetlist = Packetlist.fromStructuredClone(clone.signature);
return new cleartext.CleartextMessage(clone.text, new signature.Signature(packetlist));
}
//verification objects
function packetlistCloneToSignatures(clone) {
clone.keyid = type_keyid.fromClone(clone.keyid);
clone.signature = new signature.Signature(clone.signature);
return clone;
}
function packetlistCloneToSignature(clone) {
clone.keyid = type_keyid.fromClone(clone.keyid);
return clone;
if (typeof clone === "string") {
//signature is armored
return clone;
}
var packetlist = Packetlist.fromStructuredClone(clone);
return new signature.Signature(packetlist);
}

View File

@ -120,7 +120,7 @@ Compressed.prototype.decompress = function () {
throw new Error('Compression algorithm BZip2 [BZ2] is not implemented.');
default:
throw new Error("Compression algorithm unknown :" + this.alogrithm);
throw new Error("Compression algorithm unknown :" + this.algorithm);
}
this.packets.read(decompressed);

View File

@ -97,9 +97,9 @@ export default {
if (length < 256) {
return new Uint8Array([0x80 | (tag_type << 2), length]);
} else if (length < 65536) {
return util.concatUint8Array([0x80 | (tag_type << 2) | 1, util.writeNumber(length, 2)]);
return util.concatUint8Array([new Uint8Array([0x80 | (tag_type << 2) | 1]), util.writeNumber(length, 2)]);
} else {
return util.concatUint8Array([0x80 | (tag_type << 2) | 2, util.writeNumber(length, 4)]);
return util.concatUint8Array([new Uint8Array([0x80 | (tag_type << 2) | 2]), util.writeNumber(length, 4)]);
}
},

View File

@ -15,6 +15,7 @@ import util from '../util';
import packetParser from './packet.js';
import * as packets from './all_packets.js';
import enums from '../enums.js';
import config from '../config';
/**
* @constructor
@ -44,6 +45,9 @@ Packetlist.prototype.read = function (bytes) {
pushed = true;
packet.read(parsed.packet);
} catch(e) {
if (!config.tolerant || parsed.tag == enums.packet.symmetricallyEncrypted || parsed.tag == enums.packet.literal || parsed.tag == enums.packet.compressed) {
throw e;
}
if (pushed) {
this.pop(); // drop unsupported packet
}

View File

@ -269,6 +269,7 @@ SecretKey.prototype.decrypt = function (passphrase) {
}
this.mpi = this.mpi.concat(parsedMPI);
this.isDecrypted = true;
this.encrypted = null;
return true;
};

View File

@ -187,9 +187,13 @@ Signature.prototype.write = function () {
switch (this.version) {
case 3:
arr.push(new Uint8Array([3, 5])); // version, One-octet length of following hashed material. MUST be 5
arr.push(this.signatureData);
arr.push(new Uint8Array([this.signatureType]));
arr.push(util.writeDate(this.created));
arr.push(this.issuerKeyId.write());
arr.push(new Uint8Array([this.publicKeyAlgorithm, this.hashAlgorithm]));
arr.push(new Uint8Array([
enums.write(enums.publicKey, this.publicKeyAlgorithm),
enums.write(enums.hash, this.hashAlgorithm)
]));
break;
case 4:
arr.push(this.signatureData);
@ -222,7 +226,17 @@ Signature.prototype.sign = function (key, data) {
var trailer = this.calculateTrailer();
var toHash = util.concatUint8Array([this.toSign(signatureType, data), this.signatureData, trailer]);
var toHash = null;
switch (this.version) {
case 3:
toHash = util.concatUint8Array([this.toSign(signatureType, data), new Uint8Array([signatureType]), util.writeDate(this.created)]);
break;
case 4:
toHash = util.concatUint8Array([this.toSign(signatureType, data), this.signatureData, trailer]);
break;
default: throw new Error('Version ' + this.version + ' of the signature is unsupported.');
}
var hash = crypto.hash.digest(hashAlgorithm, toHash);

76
src/signature.js Normal file
View File

@ -0,0 +1,76 @@
// 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
/**
* @requires config
* @requires crypto
* @requires encoding/armor
* @requires enums
* @requires packet
* @module signature
*/
'use strict';
import packet from './packet';
import enums from './enums.js';
import armor from './encoding/armor.js';
/**
* @class
* @classdesc Class that represents an OpenPGP signature.
* @param {module:packet/packetlist} packetlist The signature packets
*/
export function Signature(packetlist) {
if (!(this instanceof Signature)) {
return new Signature(packetlist);
}
this.packets = packetlist || new packet.List();
}
/**
* Returns ASCII armored text of signature
* @return {String} ASCII armor
*/
Signature.prototype.armor = function() {
return armor.encode(enums.armor.signature, this.packets.write());
};
/**
* reads an OpenPGP armored signature and returns a signature object
* @param {String} armoredText text to be parsed
* @return {Signature} new signature object
* @static
*/
export function readArmored(armoredText) {
var input = armor.decode(armoredText).data;
return read(input);
}
/**
* reads an OpenPGP signature as byte array and returns a signature object
* @param {Uint8Array} input binary signature
* @return {Signature} new signature object
* @static
*/
export function read(input) {
var packetlist = new packet.List();
packetlist.read(input);
return new Signature(packetlist);
}

View File

@ -189,7 +189,7 @@ S2K.prototype.produce_key = function (passphrase, numBytes) {
}
i = 0;
while (rlength <= numBytes) {
while (rlength < numBytes) {
var result = round(prefix.subarray(0,i), this);
arr.push(result);
rlength += result.length;

View File

@ -51,7 +51,7 @@ export default {
if (!this.isString(data)) {
return false;
}
return / </.test(data) && />$/.test(data);
return /</.test(data) && />$/.test(data);
},
/**
@ -85,12 +85,9 @@ export default {
readNumber: function (bytes) {
var n = 0;
for (var i = 0; i < bytes.length; i++) {
n <<= 8;
n += bytes[i];
n += Math.pow(256, i) * bytes[bytes.length - 1 - i];
}
return n;
},
@ -274,9 +271,12 @@ export default {
throw new Error('Uint8Array2str: Data must be in the form of a Uint8Array');
}
var result = [];
for (var i = 0; i < bin.length; i++) {
result[i] = String.fromCharCode(bin[i]);
var result = [],
bs = 16384,
j = bin.length;
for (var i = 0; i < j; i += bs) {
result.push(String.fromCharCode.apply(String, bin.subarray(i, i+bs < j ? i+bs : j)));
}
return result.join('');
},

View File

@ -39,13 +39,24 @@ export default function AsyncProxy({ path='openpgp.worker.js', worker, config }
throw new Error('Unhandled error in openpgp worker: ' + e.message + ' (' + e.filename + ':' + e.lineno + ')');
};
this.seedRandom(INITIAL_RANDOM_SEED);
// FIFO
this.tasks = [];
if (config) {
this.worker.postMessage({ event:'configure', config });
}
// Cannot rely on task order being maintained, use object keyed by request ID to track tasks
this.tasks = {};
this.currentID = 0;
}
/**
* Get new request ID
* @return {integer} New unique request ID
*/
AsyncProxy.prototype.getID = function() {
return this.currentID++;
};
/**
* Message handling
*/
@ -55,11 +66,12 @@ AsyncProxy.prototype.onMessage = function(event) {
case 'method-return':
if (msg.err) {
// fail
this.tasks.shift().reject(new Error(msg.err));
this.tasks[msg.id].reject(new Error(msg.err));
} else {
// success
this.tasks.shift().resolve(msg.data);
this.tasks[msg.id].resolve(msg.data);
}
delete this.tasks[msg.id];
break;
case 'request-seed':
this.seedRandom(RANDOM_SEED_REQUEST);
@ -106,11 +118,13 @@ AsyncProxy.prototype.terminate = function() {
* @return {Promise} see the corresponding public api functions for their return types
*/
AsyncProxy.prototype.delegate = function(method, options) {
const id = this.getID();
return new Promise((resolve, reject) => {
// clone packets (for web worker structured cloning algorithm)
this.worker.postMessage({ event:method, options:packet.clone.clonePackets(options) }, util.getTransferables.call(util, options));
this.worker.postMessage({ id:id, event:method, options:packet.clone.clonePackets(options) }, util.getTransferables.call(util, options));
// remember to handle parsing cloned packets from worker
this.tasks.push({ resolve: data => resolve(packet.clone.parseClonedPackets(data, method)), reject });
this.tasks[id] = { resolve: data => resolve(packet.clone.parseClonedPackets(data, method)), reject };
});
};

View File

@ -44,7 +44,7 @@ self.onmessage = function(event) {
break;
default:
delegate(msg.event, msg.options || {});
delegate(msg.id, msg.event, msg.options || {});
}
};
@ -75,18 +75,18 @@ function seedRandom(buffer) {
* @param {String} method The public api function to be delegated to the worker thread
* @param {Object} options The api function's options
*/
function delegate(method, options) {
function delegate(id, method, options) {
if (typeof openpgp[method] !== 'function') {
response({ event:'method-return', err:'Unknown Worker Event' });
response({ id:id, event:'method-return', err:'Unknown Worker Event' });
return;
}
// parse cloned packets
options = openpgp.packet.clone.parseClonedPackets(options, method);
openpgp[method](options).then(function(data) {
// clone packets (for web worker structured cloning algorithm)
response({ event:'method-return', data:openpgp.packet.clone.clonePackets(data) });
response({ id:id, event:'method-return', data:openpgp.packet.clone.clonePackets(data) });
}).catch(function(e) {
response({ event:'method-return', err:e.message });
response({ id:id, event:'method-return', err:e.message });
});
}

View File

@ -104,7 +104,7 @@ describe("ASCII armor", function() {
var msg = getArmor(['Hash:SHA256']);
msg = openpgp.cleartext.readArmored.bind(null, msg);
expect(msg).to.throw(Error, /Improperly formatted armor header/);
msg = getArmor(['<script>: SHA256']);
msg = getArmor(['Ha sh: SHA256']);
msg = openpgp.cleartext.readArmored.bind(null, msg);
expect(msg).to.throw(Error, /Improperly formatted armor header/);
msg = getArmor(['Hash SHA256']);
@ -131,7 +131,7 @@ describe("ASCII armor", function() {
expect(msg).to.throw(Error, /Unknown ASCII armor type/);
});
it('Armor checksum validation', function () {
it('Armor checksum validation - mismatch', function () {
var privKey =
['-----BEGIN PGP PRIVATE KEY BLOCK-----',
'Version: OpenPGP.js v0.3.0',
@ -152,9 +152,140 @@ describe("ASCII armor", function() {
'=wJN@',
'-----END PGP PRIVATE KEY BLOCK-----'].join('\n');
var result = openpgp.key.readArmored(privKey);
expect(result.err).to.exist;
expect(result.err[0].message).to.match(/Ascii armor integrity check on message failed/);
// try with default config
var result_1 = openpgp.key.readArmored(privKey);
expect(result_1.err).to.exist;
expect(result_1.err[0].message).to.match(/Ascii armor integrity check on message failed/);
// try opposite config
openpgp.config.checksum_required = !openpgp.config.checksum_required;
var result_2 = openpgp.key.readArmored(privKey);
expect(result_2.err).to.exist;
expect(result_2.err[0].message).to.match(/Ascii armor integrity check on message failed/);
// back to default
openpgp.config.checksum_required = !openpgp.config.checksum_required;
});
it('Armor checksum validation - valid', function () {
var privKey =
['-----BEGIN PGP PRIVATE KEY BLOCK-----',
'Version: OpenPGP.js v0.3.0',
'Comment: http://openpgpjs.org',
'',
'xbYEUubX7gEBANDWhzoP+Tr/IyRSv++vl5jBesQIPTYGQBdzF4YDnGEBABEB',
'AAH+CQMIfzdw4/PKNl5gVXdtfDFdSIN8yJT2rbeg3+SsWexXZNNdRaONWaiB',
'Z5cG9Q6+BoXKsEshIdcYOgwsAgRxlPpRA34Vvmg2QBk7PhdrkbK7aqENsJ1w',
'dIlLD6p9GmLE20yVff58/fMiUtPRgsD83SpKTAX6EM1ulpkuQQNjmrVc5qc8',
'7AMdF80JdW5kZWZpbmVkwj8EEAEIABMFAlLm1+4JEBD8MASZrpALAhsDAAAs',
'QgD8CUrwv7Hrp/INR0/UvAvzS52VztREQwQWTJMrgTNHBGjHtgRS5tfuAQEA',
'nys9SaSgR+l6iZc/M8hGIUmbuahE2/+mtw+/l0RO+WcAEQEAAf4JAwjr39Yi',
'FzjxImDN1IoYVsonA9M+BtIIJHafuQUHjyEr1paJJK5xS6KlyGgpMTXTD6y/',
'qxS3ZSPPzHGRrs2CmkVEiPmurn9Ed05tb0y9OnJkWtuh3z9VVq9d8zHzuENa',
'bUfli+P/v+dRaZ+1rSOxUFbFYbFB5XK/A9b/OPFrv+mb4KrtLxugwj8EGAEI',
'ABMFAlLm1+4JEBD8MASZrpALAhsMAAC3IgD8DnLGbMnpLtrX72RCkPW1ffLq',
'71vlXMJNXvoCeuejiRw=',
'=wJNM',
'-----END PGP PRIVATE KEY BLOCK-----'].join('\n');
// try with default config
var result_1 = openpgp.key.readArmored(privKey);
expect(result_1.err).to.not.exist;
// try opposite config
openpgp.config.checksum_required = !openpgp.config.checksum_required;
var result_2 = openpgp.key.readArmored(privKey);
expect(result_2.err).to.not.exist;
// back to default
openpgp.config.checksum_required = !openpgp.config.checksum_required;
});
it('Armor checksum validation - missing', function () {
var privKeyNoCheckSum =
['-----BEGIN PGP PRIVATE KEY BLOCK-----',
'Version: OpenPGP.js v0.3.0',
'Comment: http://openpgpjs.org',
'',
'xbYEUubX7gEBANDWhzoP+Tr/IyRSv++vl5jBesQIPTYGQBdzF4YDnGEBABEB',
'AAH+CQMIfzdw4/PKNl5gVXdtfDFdSIN8yJT2rbeg3+SsWexXZNNdRaONWaiB',
'Z5cG9Q6+BoXKsEshIdcYOgwsAgRxlPpRA34Vvmg2QBk7PhdrkbK7aqENsJ1w',
'dIlLD6p9GmLE20yVff58/fMiUtPRgsD83SpKTAX6EM1ulpkuQQNjmrVc5qc8',
'7AMdF80JdW5kZWZpbmVkwj8EEAEIABMFAlLm1+4JEBD8MASZrpALAhsDAAAs',
'QgD8CUrwv7Hrp/INR0/UvAvzS52VztREQwQWTJMrgTNHBGjHtgRS5tfuAQEA',
'nys9SaSgR+l6iZc/M8hGIUmbuahE2/+mtw+/l0RO+WcAEQEAAf4JAwjr39Yi',
'FzjxImDN1IoYVsonA9M+BtIIJHafuQUHjyEr1paJJK5xS6KlyGgpMTXTD6y/',
'qxS3ZSPPzHGRrs2CmkVEiPmurn9Ed05tb0y9OnJkWtuh3z9VVq9d8zHzuENa',
'bUfli+P/v+dRaZ+1rSOxUFbFYbFB5XK/A9b/OPFrv+mb4KrtLxugwj8EGAEI',
'ABMFAlLm1+4JEBD8MASZrpALAhsMAAC3IgD8DnLGbMnpLtrX72RCkPW1ffLq',
'71vlXMJNXvoCeuejiRw=',
'-----END PGP PRIVATE KEY BLOCK-----'].join('\n');
// try with default config
var result_1 = openpgp.key.readArmored(privKeyNoCheckSum);
if(openpgp.config.checksum_required) {
expect(result_1.err).to.exist;
expect(result_1.err[0].message).to.match(/Ascii armor integrity check on message failed/);
} else {
expect(result_1.err).to.not.exist;
}
// try opposite config
openpgp.config.checksum_required = !openpgp.config.checksum_required;
var result_2 = openpgp.key.readArmored(privKeyNoCheckSum);
if(openpgp.config.checksum_required) {
expect(result_2.err).to.exist;
expect(result_2.err[0].message).to.match(/Ascii armor integrity check on message failed/);
} else {
expect(result_2.err).to.not.exist;
}
// back to default
openpgp.config.checksum_required = !openpgp.config.checksum_required;
});
it('Armor checksum validation - missing - trailing newline', function () {
var privKeyNoCheckSumWithTrailingNewline =
['-----BEGIN PGP PRIVATE KEY BLOCK-----',
'Version: OpenPGP.js v0.3.0',
'Comment: http://openpgpjs.org',
'',
'xbYEUubX7gEBANDWhzoP+Tr/IyRSv++vl5jBesQIPTYGQBdzF4YDnGEBABEB',
'AAH+CQMIfzdw4/PKNl5gVXdtfDFdSIN8yJT2rbeg3+SsWexXZNNdRaONWaiB',
'Z5cG9Q6+BoXKsEshIdcYOgwsAgRxlPpRA34Vvmg2QBk7PhdrkbK7aqENsJ1w',
'dIlLD6p9GmLE20yVff58/fMiUtPRgsD83SpKTAX6EM1ulpkuQQNjmrVc5qc8',
'7AMdF80JdW5kZWZpbmVkwj8EEAEIABMFAlLm1+4JEBD8MASZrpALAhsDAAAs',
'QgD8CUrwv7Hrp/INR0/UvAvzS52VztREQwQWTJMrgTNHBGjHtgRS5tfuAQEA',
'nys9SaSgR+l6iZc/M8hGIUmbuahE2/+mtw+/l0RO+WcAEQEAAf4JAwjr39Yi',
'FzjxImDN1IoYVsonA9M+BtIIJHafuQUHjyEr1paJJK5xS6KlyGgpMTXTD6y/',
'qxS3ZSPPzHGRrs2CmkVEiPmurn9Ed05tb0y9OnJkWtuh3z9VVq9d8zHzuENa',
'bUfli+P/v+dRaZ+1rSOxUFbFYbFB5XK/A9b/OPFrv+mb4KrtLxugwj8EGAEI',
'ABMFAlLm1+4JEBD8MASZrpALAhsMAAC3IgD8DnLGbMnpLtrX72RCkPW1ffLq',
'71vlXMJNXvoCeuejiRw=',
'-----END PGP PRIVATE KEY BLOCK-----',
''].join('\n');
// try with default config
var result_1 = openpgp.key.readArmored(privKeyNoCheckSumWithTrailingNewline);
if(openpgp.config.checksum_required) {
expect(result_1.err).to.exist;
expect(result_1.err[0].message).to.match(/Ascii armor integrity check on message failed/);
} else {
expect(result_1.err).to.not.exist;
}
// try opposite config
openpgp.config.checksum_required = !openpgp.config.checksum_required;
var result_2 = openpgp.key.readArmored(privKeyNoCheckSumWithTrailingNewline);
if(openpgp.config.checksum_required) {
expect(result_2.err).to.exist;
expect(result_2.err[0].message).to.match(/Ascii armor integrity check on message failed/);
} else {
expect(result_2.err).to.not.exist;
}
// back to default
openpgp.config.checksum_required = !openpgp.config.checksum_required;
});
it('Accept header with trailing whitespace', function () {

View File

@ -303,7 +303,7 @@ describe('Key', function() {
'=MVfN',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
var pgp_desktop_pub =
var pgp_desktop_pub =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: Encryption Desktop 10.3.0 (Build 9307)',
'',
@ -342,7 +342,7 @@ var pgp_desktop_pub =
'=dVeR',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
var pgp_desktop_priv =
var pgp_desktop_priv =
['-----BEGIN PGP PRIVATE KEY BLOCK-----',
'Version: Encryption Desktop 10.3.0 (Build 9307)',
'',
@ -527,7 +527,63 @@ var pgp_desktop_priv =
'=Q/kB',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
it('Parsing armored text with RSA key and ECC subkey', function(done) {
var valid_binding_sig_among_many_expired_sigs_pub = [
'-----BEGIN PGP PUBLIC KEY BLOCK-----',
'',
'mQENBFFHU9EBCAC2JjXCZOB43KUiU6V7bbMQWKFCWBmtaSTOgdEixUJKmaEyWbVd',
'OYEx5HzyumUB0fbjPv4/sbLIddpS0WAiiC1VY8zPR+eZ7W4FhEq5qOEOX5BXt5HE',
'B/eXJgB50B4n+/ld0+IZKJwEyTQlhQCr/HdQW9aPmroOdN6lJ7bxLmWLimZNZTeX',
'0UdX8zD7fiocwEciFEMVRXmO2dRhJvw++yNqxEzjLdc4V62M4j1ZbxtUDEq7yiDS',
'Xkyj0PGgnJN7ClBCHeUoLguVz9CWSXwvlcwUlq3Q1fn5mnVqGuC4a7WV540X9YUl',
'I9PdP42sagQeoGRYBA6t/pP4shmDWekPPzOxABEBAAG0FU9wZW5QR1AgPG9wZW5A',
'cGdwLmpzPokBTgQTAQgAOAIbAwULCQgHAgYVCAkKCwIEFgIDAQIeAQIXgBYhBLgc',
'q0yYY4G97CsGveVSFit0ImJRBQJZu/+XAAoJEOVSFit0ImJR5L4H/38kwavLzN1E',
'eYeVw9qXj+sR35MYcJo97pFELYcE0bINZL/IinG4mx/HgHOIrLeCLO00L3TQetPq',
'FNJw+5VVMurOeIgaRGptEckLH23Hr+LQKRx+fYpUiqHtaEWNMBcXUstSDN2zvrCx',
'U1gjYn0dczlTqZLTYEnq3NuZP/fyBQQe/HvdY8TQEJ0w+DUGIt+GQuBvQmWBc60m',
'693brCHSZSJJIl/U3Y8OP3MTees8R1vDpiSCdmKm3kyvIDkSA+nowbsjGPdK07VU',
'hCdntlDWhYMHsudPUxFrPMF8QHo2EkIN4MfWIaVERtKIpVDNV2IgXDS8meEWN9fS',
'4GdNsd7iYFW5AQ0EUUdT0QEIAKTi5PevnL5RB3944aVm/n2ID4QAktsUAE/0rIi/',
'Dg+2HFpyd/m8uV+chQV4L965UjLzYgpvn/Hka+DhiwNKUt4IUcfrSUJ8hg9ZI/yQ',
'ZjiqwEmUvUYBc/qbc5LdKMhlWhgySz7Kf7k7D5GlK56x8lPbfjLBlTU9VgFBRIot',
'Joyczs5wbtHh0IVc0tW2oShup3yfxQQ3E76lzRF36TVNoXszUHNIiLHyBZbf3Oqs',
'MXTVchjhnZBZ664dCqZ8anQma2jSvNHlCF3f2SvAv7uf0maIj5IYu0rPA0i6gapE',
'I83g8dbSqF3YNIcF2NBbck2MWKD1GEUaLXW+A7nyIDXpeH8AEQEAAYkBPAQYAQgA',
'JhYhBLgcq0yYY4G97CsGveVSFit0ImJRBQJRR1PRAhsMBQkDwmcAAAoJEOVSFit0',
'ImJRWyUH/3QADLQRZ+3zuDCZU+qw+Sf1JWxJ/sTo2rVjhb0Vz3ixZe9u5nmxgCkI',
'niPYNpBjkiGbLHAyQjsjp3l7qvUspnRMQD6eZALmcF/QBJYKUYAGjG7sC3vrIJoe',
'S+ASw36sj/DMDTFYwTzIaAD+yXezCju8Ocbnfnjud/lcjoeDTlyxxLZAIeD6lY2s',
'lE4/ChvdU5Cc+vANIANAy9wA9JjAdFzUfqhRpDGT8kmbMhDHlm8gM9gSg85mwImL',
'2x0w/7zZoqzXzcemxs6XqOI3lWEGxnAZRA0e6A/WRfuPRhiclXPo7KaAmwRRlSwB',
'fU3zrKk4kb6+V2qMReOORHr8jEYE0XG4jQRZu/43AQQA2gIZwFCK3ifxTTPOeyOd',
'Z4ATBOBdzcDdTScr2sKApGtY4wVkEN8CDbply8kPJnWWyN03HbwyU7QCf/aRXYYd',
'3/L/yJ1Ak0e2KU1hMrmlg3oeUGT9VC9FG4HwIozM+NakUj7qV89z+DrlyqPic9B7',
'7vl3oSKuedC9MFj9pRgeqCcAEQEAAYkB6wQYAQgAIBYhBLgcq0yYY4G97CsGveVS',
'Fit0ImJRBQJZu/43AhsCAL8JEOVSFit0ImJRtCAEGQEIAB0WIQRJU4+1+GHXjpXM',
'hw+ODbvAFr5dIAUCWbv+NwAKCRCODbvAFr5dIIINA/4o6nx2V2ANYLIC3dvw+L1o',
'hoQHvI5TJAln8UkG4AG9z5UzFJ7osnSgqvUXhLPFC+ifuWJFgIdAELUF9JS5rxSU',
'HS0w61OgIjNnoS3JG9LFIjJ0clUMdqSz13Hxq7zJjgyjmlhFlZwvDTATHPEEqUQy',
'FDSsvFvnhpL7uy32ffsoFfprB/0WGdJ9qo8UJBLlQVIn8fym+XNGuqz664MEQGmb',
'+BDIdVBcSaOkD4pz+8GkyKEBD6J7aJRPmgJoVzc8/hgas+jpaaH0UhSwXGaRngRh',
'PsWl9+qz7Tmb0OVdSGKpr7Bke6pjs31Ss1j2sL3XnYQKvfImuiCX4xaHBfXES/lR',
'tzZzT8wyv2QQ1E4FRzR8S0d49Rcp8WI6rr4csbqkSRbw6qDcwyg7aEfn8Z88WNCK',
'98zmzwSO6pxDWmVdPXgGjVa4MxRhAc16EBRFT/SH73D797Xnc0o1FJas5Du8AdEp',
'qrZe+kBr7apBLKlv9rvHsc7OzTs39c8+rF1x4aF4Ys4ffaGcuI0EWbwAOQEEANKP',
'LR+Ef/eYL5afw+ikIqlzRDEWHByhlWPaBquJ1xlaxPYhiwK5Ux8yZfnrYhlEKfhb',
'O5Ieohh4HzuiOc0GToRqWQYSSqzJm5odBE2HAxkAGqGqLvW+9tcH5hPK3h/WRSWy',
'QUwGvTqUfiGvg90CrIJ73DYPgy02iDlYtf+fVNgFABEBAAGJATYEGAEIACAWIQS4',
'HKtMmGOBvewrBr3lUhYrdCJiUQUCWbwAOQIbDAAKCRDlUhYrdCJiUaS0B/sHG4+d',
'r/6LuLIbsjvyIg+gVFK9sMGA751uZAZtdkzilw0c9cl0SGlxrhnRZwjZXlfB7RLD',
'GLWuTzxX6vKVXjJ+IEY49QQ53ZLwcvUvpDlWhiJXRJHSiSIXestsnJp2bLVbfRbi',
'V4jPXvZrgtBjU1XfpF9Ln6KqJGIwdWuKiHKm/iAr1qWZEtgj9AdtHWjlXOf+WWno',
'tEfaA+aJefWpDs0YVQ/7LP1N70pHj2YwmQ/Cdyr1qo6iE66UMTkiGluPpGzR5yu9',
'lhwmDcbAPbER1lIeU1K1TTWU92XCUaVuA/fPMKISlmb7UBDKWAE7904iOYOg4xsk',
'4z6aN5G1sT5159GF',
'=yzZh',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
it('Parsing armored text with RSA key and ECC subkey in tolerant mode', function(done) {
openpgp.config.tolerant = true;
var pubKeys = openpgp.key.readArmored(rsa_ecc_pub);
expect(pubKeys).to.exist;
expect(pubKeys.err).to.not.exist;
@ -536,6 +592,66 @@ var pgp_desktop_priv =
done();
});
it('Parsing armored text with RSA key and ECC subkey in non-tolerant mode', function(done) {
openpgp.config.tolerant = false;
var pubKeys = openpgp.key.readArmored(rsa_ecc_pub);
expect(pubKeys).to.exist;
expect(pubKeys.err).to.exist;
done();
});
var multi_uid_key =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v1',
'',
'mQENBFbqatUBCADmeA9CjMfzLt3TrplzDxroVisCWO7GRErUXiozULZd5S8p/rHS',
'kuclUsQzraSuQ+Q7RhpOWdJt9onf5ro0dCC3i+AEWBrS0nyXGAtpgxJmZ618Cwzz',
'RKrYstce4Hsyg0NS1KCbzCxpfIbyU/GOx4AzsvP3BcbRMvJ6fvrKy6zrhyVq5to3',
'c6MayKm3cTW0+iDvqbQCMXeKH1MgAj1eOBNrbgQZhTBMhAaIFUb5l9lXUXUmZmSj',
'r4pjjVZjWudFswXPoVRGpCOU+ahJFZLeIca99bHOl3Hu+fEbVExHdoaVq5W9R/QJ',
'/0bHQrd+Th8e1qpIP2/ABb6P/7SGUKw6ZUvbABEBAAG0E1Rlc3QgVXNlciA8YUBi',
'LmNvbT6JATgEEwECACIFAlbqatUCGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheA',
'AAoJEPhuIdU05lVRgtoH/ioJdP34cHIdSu2Ofsm6FoWc/nk2QEughNn2AyaxZAKO',
'pWy9o9/+KlVD3SoV5fzl6tCsFz1MqLFBsHSj2wKoQqkU6S9MnrG12HgnirqcjOa0',
'1uPB0aAqF3ptNScPqcD44bZ4p58TAeU5H7UlrwPUn4gypotAnu+zocNaqe0tKWVo',
'f+GAZG/FuXJc5OK2J6OmKIABJCuRchXbkyfsXZYE3f+1U9mLse4wHQhGRiSlgqG4',
'CCSIjeIkqeIvLCj/qGXJGyJ0XeMwMVhajylhEtDmMRlc32Jt8btlTJzcQ/3NPuQd',
'EryD92vGp/fXwP1/rLtD49o/0UbDeXT4KQphs2DuG/60E1Rlc3QgVXNlciA8YkBj',
'LmNvbT6JATgEEwECACIFAlbqeUACGwMGCwkIBwMCBhUIAgkKCwQWAgMBAh4BAheA',
'AAoJEPhuIdU05lVRuPkIAK+ieYXEflVHY1bKeptYZ+UfHJhsBdM29WYmuHhAbWe9',
'mb741n8YXbPENoCSYD4jq7cYOvrduz5QLmXKL57D9rXvu/dWhpLaSjGf4LDrSf+9',
'bYw0U2BStjPzjnyxZSQDU60KFRIjZPWxF/VqRFp3QIp/r3vjEGuiE6JdzbT4EWwO',
'rltkMzPYgx7cx63EhjrM3kybylL+wBX3T2JNCzLPfZBsdiWmQcypLgOPLrW/4fxQ',
'zfAsDyEYlRj7xhVKAc+nMcXo8Hw46AecS8N3htZHM6WeekZYdoJ4DlDeE5RL76xZ',
'hVEOziY5UnBT/F8dfZoVcyY/5FiSUuL19Cpwoc+dpWm5AQ0EVupq1QEIAMLfhMdk',
'OoIl1J3J8F89My2u7qwKrw1WLWawBacZH2jsGZrjZlUJEIQpaIyvqHSPSgLJ+Yco',
'YmCMj/ElNVBKBzaUpfdftW+5/S5OaJVq/j7J1OKMQqXQALgwh8GM/AThO5G4B27c',
'HZ/+bkbldYJJK0y5ZONEj7gkch7w6cr1+6NCL7jMWIDar3HpchddOproxAMuZa9D',
'2RjOvl+OMb6JMO5zTFbh37o5fAw3YWbmeX/tp2bD5W4lSUGD/Xwf2zS2r7vwGVZO',
'C+zx1aaSNllcRvSWkg8zRY5FjL9AOl4l52JFfz8G63EuHrR9dXmsYA9IHunk0UNy',
'/GGCcIJ6rXKTMCUAEQEAAYkBHwQYAQIACQUCVupq1QIbDAAKCRD4biHVNOZVUUFY',
'CADkAAtvIiJLoiYyWBx4qdTuHecuBC8On64Ln2PqImowpMb8r5JzMP6aAIBxgfEt',
'LezjJQbIM6Tcr6nTr1FunbAznrji1s4T6YcrRCS2QLq2j1aDUnLBFPrlAbuRnmZj',
'o8miZXTSasZw4O8R56jmsbcebivekg0JQMiEsf3TfxmeFQrjSGKGBarn0aklfwDS',
'JuhA5hs46N+HGvngXVZNAM9grFNxusp2YhC+DVDtcvR3SCVnVRfQojyaUKDEofHw',
'YD+tjFrH9uxzUEF+0p6he6DJ5KrQuy5Zq4Yc4X2rNvtjsIzww0Byymvo6eRO0Gxk',
'ljIYQms3pCv1ja6bLlNKpPII',
'=qxBI',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
var wrong_key =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: OpenPGP.js v0.9.0',
'',
'xk0EUlhMvAEB/2MZtCUOAYvyLFjDp3OBMGn3Ev8FwjzyPbIF0JUw+L7y2XR5',
'RVGvbK88unV3cU/1tOYdNsXI6pSp/Ztjyv7vbBUAEQEAAc0pV2hpdGVvdXQg',
'VXNlciA8d2hpdGVvdXQudGVzdEB0LW9ubGluZS5kZT7CXAQQAQgAEAUCUlhM',
'vQkQ9vYOm0LN/0wAAAW4Af9C+kYW1AvNWmivdtr0M0iYCUjM9DNOQH1fcvXq',
'IiN602mWrkd8jcEzLsW5IUNzVPLhrFIuKyBDTpLnC07Loce1',
'=6XMW',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
it('Parsing armored text with two keys', function(done) {
var pubKeys = openpgp.key.readArmored(twoKeys);
expect(pubKeys).to.exist;
@ -737,11 +853,11 @@ var pgp_desktop_priv =
it('update() - merge subkey binding signatures', function() {
var source = openpgp.key.readArmored(pgp_desktop_pub).keys[0];
var dest = openpgp.key.readArmored(pgp_desktop_priv).keys[0];
expect(source.subKeys[0].bindingSignature).to.exist;
expect(source.subKeys[0].bindingSignatures[0]).to.exist;
expect(source.subKeys[0].verify(source.primaryKey)).to.equal(openpgp.enums.keyStatus.valid);
expect(dest.subKeys[0].bindingSignature).to.not.exist;
expect(dest.subKeys[0].bindingSignatures[0]).to.not.exist;
dest.update(source);
expect(dest.subKeys[0].bindingSignature).to.exist;
expect(dest.subKeys[0].bindingSignatures[0]).to.exist;
expect(dest.subKeys[0].verify(source.primaryKey)).to.equal(openpgp.enums.keyStatus.valid);
});
@ -775,12 +891,12 @@ var pgp_desktop_priv =
var keyFlags = openpgp.enums.keyFlags;
expect(key.users[0].selfCertifications[0].keyFlags[0] & keyFlags.certify_keys).to.equal(keyFlags.certify_keys);
expect(key.users[0].selfCertifications[0].keyFlags[0] & keyFlags.sign_data).to.equal(keyFlags.sign_data);
expect(key.subKeys[0].bindingSignature.keyFlags[0] & keyFlags.encrypt_communication).to.equal(keyFlags.encrypt_communication);
expect(key.subKeys[0].bindingSignature.keyFlags[0] & keyFlags.encrypt_storage).to.equal(keyFlags.encrypt_storage);
expect(key.subKeys[0].bindingSignatures[0].keyFlags[0] & keyFlags.encrypt_communication).to.equal(keyFlags.encrypt_communication);
expect(key.subKeys[0].bindingSignatures[0].keyFlags[0] & keyFlags.encrypt_storage).to.equal(keyFlags.encrypt_storage);
var sym = openpgp.enums.symmetric;
expect(key.users[0].selfCertifications[0].preferredSymmetricAlgorithms).to.eql([sym.aes256, sym.aes128, sym.aes192, sym.cast5, sym.tripledes]);
var hash = openpgp.enums.hash;
expect(key.users[0].selfCertifications[0].preferredHashAlgorithms).to.eql([hash.sha256, hash.sha1, hash.sha512]);
expect(key.users[0].selfCertifications[0].preferredHashAlgorithms).to.eql([hash.sha256, hash.sha512, hash.sha1]);
var compr = openpgp.enums.compression;
expect(key.users[0].selfCertifications[0].preferredCompressionAlgorithms).to.eql([compr.zlib, compr.zip]);
expect(key.users[0].selfCertifications[0].features).to.eql(openpgp.config.integrity_protect ? [1] : null); // modification detection
@ -873,6 +989,172 @@ var pgp_desktop_priv =
done();
}).catch(done);
});
it('Generate key - ensure keyExpirationTime works', function(done) {
var expect_delta = 365 * 24 * 60 * 60;
var userId = 'test <a@b.com>';
var opt = {numBits: 512, userIds: userId, passphrase: '123', keyExpirationTime: expect_delta};
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
openpgp.generateKey(opt).then(function(key) {
key = key.key;
var expiration = key.getExpirationTime();
expect(expiration).to.exist;
var actual_delta = (new Date(expiration) - new Date()) / 1000;
expect(Math.abs(actual_delta - expect_delta)).to.be.below(60);
var subKeyExpiration = key.subKeys[0].getExpirationTime();
expect(subKeyExpiration).to.exist;
var actual_subKeyDelta = (new Date(subKeyExpiration) - new Date()) / 1000;
expect(Math.abs(actual_subKeyDelta - expect_delta)).to.be.below(60);
done();
}).catch(done);
});
it('Sign and verify key - primary user', function(done) {
var key = openpgp.key.readArmored(pub_sig_test).keys[0];
var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0];
privateKey.decrypt('hello world');
key = key.signPrimaryUser([privateKey]);
var signatures = key.verifyPrimaryUser([privateKey]);
expect(signatures.length).to.equal(2);
expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[0].valid).to.be.null;
expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[1].valid).to.be.true;
done();
});
it('Sign key and verify with wrong key - primary user', function(done) {
var key = openpgp.key.readArmored(pub_sig_test).keys[0];
var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0];
var wrongKey = openpgp.key.readArmored(wrong_key).keys[0];
privateKey.decrypt('hello world');
key = key.signPrimaryUser([privateKey]);
var signatures = key.verifyPrimaryUser([wrongKey]);
expect(signatures.length).to.equal(2);
expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[0].valid).to.be.null;
expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[1].valid).to.be.null;
done();
});
it('Sign and verify key - all users', function(done) {
var key = openpgp.key.readArmored(multi_uid_key).keys[0];
var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0];
privateKey.decrypt('hello world');
key = key.signAllUsers([privateKey]);
var signatures = key.verifyAllUsers([privateKey]);
expect(signatures.length).to.equal(4);
expect(signatures[0].userid).to.equal(key.users[0].userId.userid);
expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[0].valid).to.be.null;
expect(signatures[1].userid).to.equal(key.users[0].userId.userid);
expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[1].valid).to.be.true;
expect(signatures[2].userid).to.equal(key.users[1].userId.userid);
expect(signatures[2].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[2].valid).to.be.null;
expect(signatures[3].userid).to.equal(key.users[1].userId.userid);
expect(signatures[3].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[3].valid).to.be.true;
done();
});
it('Sign key and verify with wrong key - all users', function(done) {
var key = openpgp.key.readArmored(multi_uid_key).keys[0];
var privateKey = openpgp.key.readArmored(priv_key_rsa).keys[0];
var wrongKey = openpgp.key.readArmored(wrong_key).keys[0];
privateKey.decrypt('hello world');
key = key.signAllUsers([privateKey]);
var signatures = key.verifyAllUsers([wrongKey]);
expect(signatures.length).to.equal(4);
expect(signatures[0].userid).to.equal(key.users[0].userId.userid);
expect(signatures[0].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[0].valid).to.be.null;
expect(signatures[1].userid).to.equal(key.users[0].userId.userid);
expect(signatures[1].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[1].valid).to.be.null;
expect(signatures[2].userid).to.equal(key.users[1].userId.userid);
expect(signatures[2].keyid.toHex()).to.equal(key.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[2].valid).to.be.null;
expect(signatures[3].userid).to.equal(key.users[1].userId.userid);
expect(signatures[3].keyid.toHex()).to.equal(privateKey.getSigningKeyPacket().getKeyId().toHex());
expect(signatures[3].valid).to.be.null;
done();
});
it('Reformat key without passphrase', function(done) {
var userId1 = 'test1 <a@b.com>';
var userId2 = 'test2 <b@a.com>';
var opt = {numBits: 512, userIds: userId1};
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
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.primaryKey.isDecrypted).to.be.true;
opt.privateKey = key;
opt.userIds = userId2;
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.primaryKey.isDecrypted).to.be.true;
done();
}).catch(done);
}).catch(done);
});
it('Reformat and encrypt key', function(done) {
var userId1 = 'test1 <a@b.com>';
var userId2 = 'test2 <b@c.com>';
var userId3 = 'test3 <c@d.com>';
var opt = {numBits: 512, userIds: userId1};
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
openpgp.generateKey(opt).then(function(key) {
key = key.key
opt.privateKey = key;
opt.userIds = [userId2, userId3];
opt.passphrase = '123';
openpgp.reformatKey(opt).then(function(newKey) {
newKey = newKey.key
expect(newKey.users.length).to.equal(2);
expect(newKey.users[0].userId.userid).to.equal(userId2);
expect(newKey.primaryKey.isDecrypted).to.be.false;
newKey.decrypt('123');
expect(newKey.primaryKey.isDecrypted).to.be.true;
done();
}).catch(done);
}).catch(done);
});
it('Sign and encrypt with reformatted key', function(done) {
var userId1 = 'test1 <a@b.com>';
var userId2 = 'test2 <b@a.com>';
var opt = {numBits: 512, userIds: userId1};
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
openpgp.generateKey(opt).then(function(key) {
key = key.key
opt.privateKey = key;
opt.userIds = userId2;
openpgp.reformatKey(opt).then(function(newKey) {
newKey = newKey.key
openpgp.encrypt({data: 'hello', publicKeys: newKey.toPublic(), privateKeys: newKey, armor: true}).then(function(encrypted) {
openpgp.decrypt({message: openpgp.message.readArmored(encrypted.data), privateKey: newKey, publicKeys: newKey.toPublic()}).then(function(decrypted) {
expect(decrypted.data).to.equal('hello');
expect(decrypted.signatures[0].valid).to.be.true;
done();
}).catch(done);
}).catch(done);
}).catch(done);
}).catch(done);
});
it('Find a valid subkey binding signature among many invalid ones', function(done) {
var k = openpgp.key.readArmored(valid_binding_sig_among_many_expired_sigs_pub).keys[0];
expect(k.getEncryptionKeyPacket()).to.not.be.null;
done();
});
});

View File

@ -289,7 +289,7 @@ describe('OpenPGP.js public api tests', function() {
var opt = {
userIds: { name: 'Test User' }
};
openpgp.generateKey(opt).then(function() { done(); });
openpgp.generateKey(opt).then(function() { done(); })
});
it('should have default params set', function(done) {
@ -303,13 +303,14 @@ describe('OpenPGP.js public api tests', function() {
userIds: ['Test User <text@example.com>'],
passphrase: 'secret',
numBits: 2048,
unlocked: true
unlocked: true,
keyExpirationTime: 0
}).calledOnce).to.be.true;
expect(newKey.key).to.exist;
expect(newKey.privateKeyArmored).to.exist;
expect(newKey.publicKeyArmored).to.exist;
done();
});
})
});
it('should work for no params', function(done) {
@ -318,7 +319,8 @@ describe('OpenPGP.js public api tests', function() {
userIds: [],
passphrase: undefined,
numBits: 2048,
unlocked: false
unlocked: false,
keyExpirationTime: 0
}).calledOnce).to.be.true;
expect(newKey.key).to.exist;
done();
@ -635,7 +637,8 @@ describe('OpenPGP.js public api tests', function() {
return openpgp.decrypt(decOpt);
}).then(function(decrypted) {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures).to.not.exist;
expect(decrypted.signatures).to.exist;
expect(decrypted.signatures.length).to.equal(0);
done();
});
});
@ -657,6 +660,172 @@ describe('OpenPGP.js public api tests', function() {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures[0].valid).to.be.true;
expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
it('should encrypt/sign and decrypt/verify with detached signatures', function(done) {
var encOpt = {
data: plaintext,
publicKeys: publicKey.keys,
privateKeys: privateKey.keys,
detached: true
};
var decOpt = {
privateKey: privateKey.keys[0],
publicKeys: publicKey.keys
};
openpgp.encrypt(encOpt).then(function(encrypted) {
decOpt.message = openpgp.message.readArmored(encrypted.data);
decOpt.signature = openpgp.signature.readArmored(encrypted.signature);
return openpgp.decrypt(decOpt);
}).then(function(decrypted) {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures[0].valid).to.be.true;
expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
it('should encrypt and decrypt/verify with detached signature input and detached flag set for encryption', function(done) {
var signOpt = {
data: plaintext,
privateKeys: privateKey.keys[0],
detached: true
};
var encOpt = {
data: plaintext,
publicKeys: publicKey.keys,
detached: true
};
var decOpt = {
privateKey: privateKey.keys[0],
publicKeys: publicKey.keys[0]
};
openpgp.sign(signOpt).then(function(signed) {
encOpt.signature = openpgp.signature.readArmored(signed.signature);
return openpgp.encrypt(encOpt);
}).then(function(encrypted) {
decOpt.message = openpgp.message.readArmored(encrypted.data);
decOpt.signature = openpgp.signature.readArmored(encrypted.signature);
return openpgp.decrypt(decOpt);
}).then(function(decrypted) {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures[0].valid).to.be.true;
expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
it('should encrypt and decrypt/verify with detached signature as input and detached flag not set for encryption', function(done) {
var privKeyDE = openpgp.key.readArmored(priv_key_de).keys[0];
privKeyDE.decrypt(passphrase);
var pubKeyDE = openpgp.key.readArmored(pub_key_de).keys[0];
var signOpt = {
data: plaintext,
privateKeys: privKeyDE,
detached: true
};
var encOpt = {
data: plaintext,
publicKeys: publicKey.keys,
privateKeys: privateKey.keys[0]
};
var decOpt = {
privateKey: privateKey.keys[0],
publicKeys: [publicKey.keys[0], pubKeyDE]
};
openpgp.sign(signOpt).then(function(signed) {
encOpt.signature = openpgp.signature.readArmored(signed.signature);
return openpgp.encrypt(encOpt);
}).then(function(encrypted) {
decOpt.message = openpgp.message.readArmored(encrypted.data);
return openpgp.decrypt(decOpt);
}).then(function(decrypted) {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures[0].valid).to.be.true;
expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
expect(decrypted.signatures[1].valid).to.be.true;
expect(decrypted.signatures[1].keyid.toHex()).to.equal(privKeyDE.getSigningKeyPacket().getKeyId().toHex());
expect(decrypted.signatures[1].signature.packets.length).to.equal(1);
done();
});
});
it('should fail to encrypt and decrypt/verify with detached signature input and detached flag set for encryption with wrong public key', function(done) {
var signOpt = {
data: plaintext,
privateKeys: privateKey.keys,
detached: true
};
var encOpt = {
data: plaintext,
publicKeys: publicKey.keys,
detached: true
};
var decOpt = {
privateKey: privateKey.keys[0],
publicKeys: openpgp.key.readArmored(wrong_pubkey).keys
};
openpgp.sign(signOpt).then(function(signed) {
encOpt.signature = openpgp.signature.readArmored(signed.signature);
return openpgp.encrypt(encOpt);
}).then(function(encrypted) {
decOpt.message = openpgp.message.readArmored(encrypted.data);
decOpt.signature = openpgp.signature.readArmored(encrypted.signature);
return openpgp.decrypt(decOpt);
}).then(function(decrypted) {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures[0].valid).to.be.null;
expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
it('should fail to encrypt and decrypt/verify with detached signature as input and detached flag not set for encryption with wrong public key', function(done) {
var signOpt = {
data: plaintext,
privateKeys: privateKey.keys,
detached: true
};
var encOpt = {
data: plaintext,
publicKeys: publicKey.keys
};
var decOpt = {
privateKey: privateKey.keys[0],
publicKeys: openpgp.key.readArmored(wrong_pubkey).keys
};
openpgp.sign(signOpt).then(function(signed) {
encOpt.signature = openpgp.signature.readArmored(signed.signature);
return openpgp.encrypt(encOpt);
}).then(function(encrypted) {
decOpt.message = openpgp.message.readArmored(encrypted.data);
return openpgp.decrypt(decOpt);
}).then(function(decrypted) {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures[0].valid).to.be.null;
expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
@ -678,6 +847,52 @@ describe('OpenPGP.js public api tests', function() {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures[0].valid).to.be.null;
expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
it('should successfully decrypt signed message without public keys to verify', function(done) {
var encOpt = {
data: plaintext,
publicKeys: publicKey.keys,
privateKeys: privateKey.keys
};
var decOpt = {
privateKey: privateKey.keys[0],
};
openpgp.encrypt(encOpt).then(function(encrypted) {
decOpt.message = openpgp.message.readArmored(encrypted.data);
return openpgp.decrypt(decOpt);
}).then(function(decrypted) {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures[0].valid).to.be.null;
expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
it('should fail to verify decrypted data with wrong public pgp key with detached signatures', function(done) {
var encOpt = {
data: plaintext,
publicKeys: publicKey.keys,
privateKeys: privateKey.keys,
detached: true
};
var decOpt = {
privateKey: privateKey.keys[0],
publicKeys: openpgp.key.readArmored(wrong_pubkey).keys
};
openpgp.encrypt(encOpt).then(function(encrypted) {
decOpt.message = openpgp.message.readArmored(encrypted.data);
decOpt.signature = openpgp.signature.readArmored(encrypted.signature);
return openpgp.decrypt(decOpt);
}).then(function(decrypted) {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures[0].valid).to.be.null;
expect(decrypted.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
@ -698,6 +913,29 @@ describe('OpenPGP.js public api tests', function() {
expect(verified.data).to.equal(plaintext);
expect(verified.signatures[0].valid).to.be.true;
expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(verified.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
it('should sign and verify cleartext data with detached signatures', function(done) {
var signOpt = {
data: plaintext,
privateKeys: privateKey.keys,
detached: true
};
var verifyOpt = {
publicKeys: publicKey.keys
};
openpgp.sign(signOpt).then(function(signed) {
verifyOpt.message = openpgp.cleartext.readArmored(signed.data);
verifyOpt.signature = openpgp.signature.readArmored(signed.signature);
return openpgp.verify(verifyOpt);
}).then(function(verified) {
expect(verified.data).to.equal(plaintext);
expect(verified.signatures[0].valid).to.be.true;
expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(verified.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
@ -717,6 +955,29 @@ describe('OpenPGP.js public api tests', function() {
expect(verified.data).to.equal(plaintext);
expect(verified.signatures[0].valid).to.be.null;
expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(verified.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
it('should sign and fail to verify cleartext data with wrong public pgp key with detached signature', function(done) {
var signOpt = {
data: plaintext,
privateKeys: privateKey.keys,
detached: true
};
var verifyOpt = {
publicKeys: openpgp.key.readArmored(wrong_pubkey).keys
};
openpgp.sign(signOpt).then(function(signed) {
verifyOpt.message = openpgp.cleartext.readArmored(signed.data);
verifyOpt.signature = openpgp.signature.readArmored(signed.signature);
return openpgp.verify(verifyOpt);
}).then(function(verified) {
expect(verified.data).to.equal(plaintext);
expect(verified.signatures[0].valid).to.be.null;
expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(verified.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
@ -737,6 +998,30 @@ describe('OpenPGP.js public api tests', function() {
expect(verified.data).to.equal(plaintext);
expect(verified.signatures[0].valid).to.be.true;
expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(verified.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
it('should sign and verify cleartext data and not armor with detached signatures', function(done) {
var signOpt = {
data: plaintext,
privateKeys: privateKey.keys,
detached: true,
armor: false
};
var verifyOpt = {
publicKeys: publicKey.keys
};
openpgp.sign(signOpt).then(function(signed) {
verifyOpt.message = signed.message;
verifyOpt.signature = signed.signature;
return openpgp.verify(verifyOpt);
}).then(function(verified) {
expect(verified.data).to.equal(plaintext);
expect(verified.signatures[0].valid).to.be.true;
expect(verified.signatures[0].keyid.toHex()).to.equal(privateKey.keys[0].getSigningKeyPacket().getKeyId().toHex());
expect(verified.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
@ -760,6 +1045,9 @@ describe('OpenPGP.js public api tests', function() {
}).then(function(encrypted) {
expect(encrypted.data).to.exist;
expect(encrypted.data).to.equal(plaintext);
expect(encrypted.signatures[0].valid).to.be.true;
expect(encrypted.signatures[0].keyid.toHex()).to.equal(privKeyDE.getSigningKeyPacket().getKeyId().toHex());
expect(encrypted.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
@ -826,6 +1114,7 @@ describe('OpenPGP.js public api tests', function() {
openpgp.decrypt({ privateKey:privKey, message:message }).then(function(decrypted) {
expect(decrypted.data).to.equal('hello 3des\n');
expect(decrypted.signatures.length).to.equal(0);
done();
});
});
@ -845,6 +1134,7 @@ describe('OpenPGP.js public api tests', function() {
return openpgp.decrypt(decOpt);
}).then(function(decrypted) {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures.length).to.equal(0);
done();
});
});
@ -862,6 +1152,7 @@ describe('OpenPGP.js public api tests', function() {
return openpgp.decrypt(decOpt);
}).then(function(decrypted) {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures.length).to.equal(0);
done();
});
});
@ -880,6 +1171,7 @@ describe('OpenPGP.js public api tests', function() {
return openpgp.decrypt(decOpt);
}).then(function(decrypted) {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures.length).to.equal(0);
done();
});
});
@ -903,6 +1195,7 @@ describe('OpenPGP.js public api tests', function() {
expect(encOpt.data.byteLength).to.equal(0); // transfered buffer should be empty
}
expect(decrypted.data).to.deep.equal(new Uint8Array([0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01]));
expect(decrypted.signatures.length).to.equal(0);
done();
});
});

View File

@ -265,6 +265,7 @@ describe("Signature", function() {
openpgp.decrypt({ privateKey: priv_key, publicKeys:[pub_key], message:msg }).then(function(decrypted) {
expect(decrypted.data).to.exist;
expect(decrypted.signatures[0].valid).to.be.true;
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
@ -309,6 +310,7 @@ describe("Signature", function() {
expect(verified).to.exist;
expect(verified).to.have.length(1);
expect(verified[0].valid).to.be.true;
expect(verified[0].signature.packets.length).to.equal(1);
done();
});
});
@ -333,6 +335,7 @@ describe("Signature", function() {
expect(verified).to.exist;
expect(verified).to.have.length(1);
expect(verified[0].valid).to.be.true;
expect(verified[0].signature.packets.length).to.equal(1);
done();
});
@ -356,6 +359,7 @@ describe("Signature", function() {
expect(verified).to.exist;
expect(verified).to.have.length(1);
expect(verified[0].valid).to.be.true;
expect(verified[0].signature.packets.length).to.equal(1);
done();
});
@ -390,6 +394,7 @@ describe("Signature", function() {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures).to.have.length(1);
expect(decrypted.signatures[0].valid).to.be.true;
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
@ -426,6 +431,7 @@ describe("Signature", function() {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures).to.have.length(1);
expect(decrypted.signatures[0].valid).to.be.true;
expect(decrypted.signatures[0].signature.packets.length).to.equal(1);
done();
});
@ -469,6 +475,8 @@ describe("Signature", function() {
expect(verifiedSig).to.have.length(2);
expect(verifiedSig[0].valid).to.be.true;
expect(verifiedSig[1].valid).to.be.true;
expect(verifiedSig[0].signature.packets.length).to.equal(1);
expect(verifiedSig[1].signature.packets.length).to.equal(1);
done();
});
@ -513,11 +521,13 @@ describe("Signature", function() {
expect(cleartextSig.signatures).to.have.length(2);
expect(cleartextSig.signatures[0].valid).to.be.true;
expect(cleartextSig.signatures[1].valid).to.be.true;
expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1);
expect(cleartextSig.signatures[1].signature.packets.length).to.equal(1);
done();
});
});
it('Sign text with openpgp.sign and verify with openpgp.verify leads to same cleartext and valid signatures', function(done) {
it('Sign text with openpgp.sign and verify with openpgp.verify leads to same string cleartext and valid signatures', function(done) {
var plaintext = 'short message\nnext line\n한국어/조선말';
var pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0];
var privKey = openpgp.key.readArmored(priv_key_arm2).keys[0];
@ -533,6 +543,49 @@ describe("Signature", function() {
expect(cleartextSig.data).to.equal(plaintext.replace(/\r/g,''));
expect(cleartextSig.signatures).to.have.length(1);
expect(cleartextSig.signatures[0].valid).to.be.true;
expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - armored', function(done) {
var plaintext = openpgp.util.str2Uint8Array('short message\nnext line\n한국어/조선말');
var pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0];
var privKey = openpgp.key.readArmored(priv_key_arm2).keys[0];
privKey.getSigningKeyPacket().decrypt('hello world');
openpgp.sign({ privateKeys:[privKey], data:plaintext }).then(function(signed) {
var csMsg = openpgp.message.readArmored(signed.data);
return openpgp.verify({ publicKeys:[pubKey], message:csMsg });
}).then(function(cleartextSig) {
expect(cleartextSig).to.exist;
expect(cleartextSig.data).to.deep.equal(plaintext);
expect(cleartextSig.signatures).to.have.length(1);
expect(cleartextSig.signatures[0].valid).to.be.true;
expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1);
done();
});
});
it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - not armored', function(done) {
var plaintext = openpgp.util.str2Uint8Array('short message\nnext line\n한국어/조선말');
var pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0];
var privKey = openpgp.key.readArmored(priv_key_arm2).keys[0];
privKey.getSigningKeyPacket().decrypt('hello world');
openpgp.sign({ privateKeys:[privKey], data:plaintext, armor:false }).then(function(signed) {
var csMsg = signed.message;
return openpgp.verify({ publicKeys:[pubKey], message:csMsg });
}).then(function(cleartextSig) {
expect(cleartextSig).to.exist;
expect(cleartextSig.data).to.deep.equal(plaintext);
expect(cleartextSig.signatures).to.have.length(1);
expect(cleartextSig.signatures[0].valid).to.be.true;
expect(cleartextSig.signatures[0].signature.packets.length).to.equal(1);
done();
});
@ -595,7 +648,7 @@ describe("Signature", function() {
expect(pubKey.users[0].selfCertifications).to.eql(pubKey2.users[0].selfCertifications);
});
it('Verify a detached signature', function() {
it('Verify a detached signature using readSignedContent', function() {
var detachedSig = ['-----BEGIN PGP SIGNATURE-----',
'Version: GnuPG v1.4.13 (Darwin)',
'Comment: GPGTools - https://gpgtools.org',
@ -641,6 +694,23 @@ describe("Signature", function() {
expect(result[0].valid).to.be.true;
});
it('Detached signature signing and verification', function () {
var msg = openpgp.message.fromText('hello');
var pubKey2 = openpgp.key.readArmored(pub_key_arm2).keys[0];
var privKey2 = openpgp.key.readArmored(priv_key_arm2).keys[0];
privKey2.decrypt('hello world');
var opt = {numBits: 512, userIds: { name:'test', email:'a@b.com' }, passphrase: null};
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
openpgp.generateKey(opt).then(function(gen) {
var generatedKey = gen.key;
var detachedSig = msg.signDetached([generatedKey, privKey2]);
var result = msg.verifyDetached(detachedSig, [generatedKey.toPublic(), pubKey2]);
expect(result[0].valid).to.be.true;
expect(result[1].valid).to.be.true;
});
});
it('Sign message with key without password', function(done) {
var opt = {numBits: 512, userIds: { name:'test', email:'a@b.com' }, passphrase: null};
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
@ -655,4 +725,42 @@ describe("Signature", function() {
});
});
it('Verify signed key', function(done) {
var signedArmor = [
'-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v1',
'',
'mI0EUmEvTgEEANyWtQQMOybQ9JltDqmaX0WnNPJeLILIM36sw6zL0nfTQ5zXSS3+',
'fIF6P29lJFxpblWk02PSID5zX/DYU9/zjM2xPO8Oa4xo0cVTOTLj++Ri5mtr//f5',
'GLsIXxFrBJhD/ghFsL3Op0GXOeLJ9A5bsOn8th7x6JucNKuaRB6bQbSPABEBAAG0',
'JFRlc3QgTWNUZXN0aW5ndG9uIDx0ZXN0QGV4YW1wbGUuY29tPoi5BBMBAgAjBQJS',
'YS9OAhsvBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQSmNhOk1uQJQwDAP6',
'AgrTyqkRlJVqz2pb46TfbDM2TDF7o9CBnBzIGoxBhlRwpqALz7z2kxBDmwpQa+ki',
'Bq3jZN/UosY9y8bhwMAlnrDY9jP1gdCo+H0sD48CdXybblNwaYpwqC8VSpDdTndf',
'9j2wE/weihGp/DAdy/2kyBCaiOY1sjhUfJ1GogF49rCIRgQQEQIABgUCVuXBfQAK',
'CRARJ5QDyxae+O0fAJ9hUQPejXvZv6VW1Q3/Pm3+x2wfJACgwFg9NlrPPfejoC1w',
'P+z+vE5NFA24jQRSYS9OAQQA6R/PtBFaJaT4jq10yqASk4sqwVMsc6HcifM5lSdx',
'zExFP74naUMMyEsKHP53QxTF0GrqusagQg/ZtgT0CN1HUM152y7ACOdp1giKjpMz',
'OTQClqCoclyvWOFB+L/SwGEIJf7LSCErwoBuJifJc8xAVr0XX0JthoW+uP91eTQ3',
'XpsAEQEAAYkBPQQYAQIACQUCUmEvTgIbLgCoCRBKY2E6TW5AlJ0gBBkBAgAGBQJS',
'YS9OAAoJEOCE90RsICyXuqIEANmmiRCASF7YK7PvFkieJNwzeK0V3F2lGX+uu6Y3',
'Q/Zxdtwc4xR+me/CSBmsURyXTO29OWhPGLszPH9zSJU9BdDi6v0yNprmFPX/1Ng0',
'Abn/sCkwetvjxC1YIvTLFwtUL/7v6NS2bZpsUxRTg9+cSrMWWSNjiY9qUKajm1tu',
'zPDZXAUEAMNmAN3xXN/Kjyvj2OK2ck0XW748sl/tc3qiKPMJ+0AkMF7Pjhmh9nxq',
'E9+QCEl7qinFqqBLjuzgUhBU4QlwX1GDAtNTq6ihLMD5v1d82ZC7tNatdlDMGWnI',
'dvEMCv2GZcuIqDQ9rXWs49e7tq1NncLYhz3tYjKhoFTKEIq3y3Pp',
'=fvK7',
'-----END PGP PUBLIC KEY BLOCK-----'
].join('\n');
var signedKey = openpgp.key.readArmored(signedArmor).keys[0];
var signerKey = openpgp.key.readArmored(priv_key_arm1).keys[0];
var signatures = signedKey.verifyPrimaryUser([signerKey]);
expect(signatures[0].valid).to.be.null;
expect(signatures[0].keyid.toHex()).to.equal(signedKey.primaryKey.getKeyId().toHex());
expect(signatures[1].valid).to.be.true;
expect(signatures[1].keyid.toHex()).to.equal(signerKey.primaryKey.getKeyId().toHex());
done();
});
});

View File

@ -185,6 +185,12 @@ describe('Util unit tests', function() {
var test = openpgp.util.decode_utf8.bind(null, {chameleon: true});
expect(test).to.throw(Error, /Parameter "utf8" is not of type string/);
});
it('util.readNumber should not overflow until full range of uint32', function () {
var ints = [Math.pow(2, 20), Math.pow(2, 25), Math.pow(2, 30), Math.pow(2, 32) - 1];
for(var i = 0; i < ints.length; i++) {
expect(openpgp.util.readNumber(openpgp.util.writeNumber(ints[i], 4))).to.equal(ints[i]);
}
});
});
});