├── .gitignore
├── .npmignore
├── .travis.yml
├── README.md
├── gruntfile.js
├── package-lock.json
├── package.json
├── src
├── NodeRSA.js
├── encryptEngines
│ ├── encryptEngines.js
│ ├── io.js
│ ├── js.js
│ └── node12.js
├── formats
│ ├── components.js
│ ├── formats.js
│ ├── openssh.js
│ ├── pkcs1.js
│ └── pkcs8.js
├── libs
│ ├── jsbn.js
│ └── rsa.js
├── schemes
│ ├── oaep.js
│ ├── pkcs1.js
│ ├── pss.js
│ └── schemes.js
└── utils.js
└── test
├── keys
├── id_rsa
├── id_rsa.pub
├── id_rsa_comment
├── id_rsa_comment.pub
├── private_pkcs1.der
├── private_pkcs1.pem
├── private_pkcs8.der
├── private_pkcs8.pem
├── public_pkcs1.der
├── public_pkcs1.pem
├── public_pkcs8.der
└── public_pkcs8.pem
├── private_pkcs1.pem
└── tests.js
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .idea
3 | .tmp
4 | node_modules/
5 | .nyc_output
6 | nbproject/
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | test
2 | .travis.yml
3 | .nyc_output
4 | .tmp
5 | .idea
6 | .DS_Store
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - '8'
4 | - 'stable'
5 |
6 | sudo: false
7 |
8 | before_install:
9 | - npm install -g npm@latest
10 | - npm install -g grunt-cli
11 | install:
12 | - npm install
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Node-RSA
2 |
3 | Node.js RSA library
4 | Based on jsbn library from Tom Wu http://www-cs-students.stanford.edu/~tjw/jsbn/
5 |
6 | * Pure JavaScript
7 | * No needed OpenSSL
8 | * Generating keys
9 | * Supports long messages for encrypt/decrypt
10 | * Signing and verifying
11 |
12 | ## Example
13 |
14 | ```javascript
15 | const NodeRSA = require('node-rsa');
16 | const key = new NodeRSA({b: 512});
17 |
18 | const text = 'Hello RSA!';
19 | const encrypted = key.encrypt(text, 'base64');
20 | console.log('encrypted: ', encrypted);
21 | const decrypted = key.decrypt(encrypted, 'utf8');
22 | console.log('decrypted: ', decrypted);
23 | ```
24 |
25 | ## Installing
26 |
27 | ```shell
28 | npm install node-rsa
29 | ```
30 | > Requires nodejs >= 8.11.1
31 |
32 | ### Testing
33 |
34 | ```shell
35 | npm test
36 | ```
37 |
38 | ## Work environment
39 |
40 | This library developed and tested primary for Node.js, but it still can work in browsers with [browserify](http://browserify.org/).
41 |
42 | ## Usage
43 |
44 | ### Create instance
45 | ```javascript
46 | const NodeRSA = require('node-rsa');
47 |
48 | const key = new NodeRSA([keyData, [format]], [options]);
49 | ```
50 |
51 | * keyData — `{string|buffer|object}` — parameters for generating key or the key in one of supported formats.
52 | * format — `{string}` — format for importing key. See more details about formats in [Export/Import](#importexport-keys) section.
53 | * options — `{object}` — additional settings.
54 |
55 | #### Options
56 | You can specify some options by second/third constructor argument, or over `key.setOptions()` method.
57 |
58 | * environment — working environment (default autodetect):
59 | * `'browser'` — will run pure js implementation of RSA algorithms.
60 | * `'node'` for `nodejs >= 0.10.x or io.js >= 1.x` — provide some native methods like sign/verify and encrypt/decrypt.
61 | * encryptionScheme — padding scheme for encrypt/decrypt. Can be `'pkcs1_oaep'` or `'pkcs1'`. Default `'pkcs1_oaep'`.
62 | * signingScheme — scheme used for signing and verifying. Can be `'pkcs1'` or `'pss'` or 'scheme-hash' format string (eg `'pss-sha1'`). Default `'pkcs1-sha256'`, or, if chosen pss: `'pss-sha1'`.
63 |
64 | > *Notice:* This lib supporting next hash algorithms: `'md5'`, `'ripemd160'`, `'sha1'`, `'sha256'`, `'sha512'` in browser and node environment and additional `'md4'`, `'sha'`, `'sha224'`, `'sha384'` in node only.
65 |
66 | Some [advanced options info](https://github.com/rzcoder/node-rsa/wiki/Advanced-options)
67 |
68 | #### Creating "empty" key
69 | ```javascript
70 | const key = new NodeRSA();
71 | ```
72 |
73 | #### Generate new 512bit-length key
74 | ```javascript
75 | const key = new NodeRSA({b: 512});
76 | ```
77 |
78 | Also you can use next method:
79 |
80 | ```javascript
81 | key.generateKeyPair([bits], [exp]);
82 | ```
83 |
84 | * bits — `{int}` — key size in bits. 2048 by default.
85 | * exp — `{int}` — public exponent. 65537 by default.
86 |
87 | #### Load key from PEM string
88 |
89 | ```javascript
90 | const key = new NodeRSA('-----BEGIN RSA PRIVATE KEY-----\n'+
91 | 'MIIBOQIBAAJAVY6quuzCwyOWzymJ7C4zXjeV/232wt2ZgJZ1kHzjI73wnhQ3WQcL\n'+
92 | 'DFCSoi2lPUW8/zspk0qWvPdtp6Jg5Lu7hwIDAQABAkBEws9mQahZ6r1mq2zEm3D/\n'+
93 | 'VM9BpV//xtd6p/G+eRCYBT2qshGx42ucdgZCYJptFoW+HEx/jtzWe74yK6jGIkWJ\n'+
94 | 'AiEAoNAMsPqwWwTyjDZCo9iKvfIQvd3MWnmtFmjiHoPtjx0CIQCIMypAEEkZuQUi\n'+
95 | 'pMoreJrOlLJWdc0bfhzNAJjxsTv/8wIgQG0ZqI3GubBxu9rBOAM5EoA4VNjXVigJ\n'+
96 | 'QEEk1jTkp8ECIQCHhsoq90mWM/p9L5cQzLDWkTYoPI49Ji+Iemi2T5MRqwIgQl07\n'+
97 | 'Es+KCn25OKXR/FJ5fu6A6A+MptABL3r8SEjlpLc=\n'+
98 | '-----END RSA PRIVATE KEY-----');
99 | ```
100 |
101 | ### Import/Export keys
102 | ```javascript
103 | key.importKey(keyData, [format]);
104 | key.exportKey([format]);
105 | ```
106 |
107 | * keyData — `{string|buffer}` — may be:
108 | * key in PEM string
109 | * Buffer containing PEM string
110 | * Buffer containing DER encoded data
111 | * Object contains key components
112 | * format — `{string}` — format id for export/import.
113 |
114 | #### Format string syntax
115 | Format string composed of several parts: `scheme-[key_type]-[output_type]`
116 |
117 | Scheme — NodeRSA supports multiple format schemes for import/export keys:
118 |
119 | * `'pkcs1'` — public key starts from `'-----BEGIN RSA PUBLIC KEY-----'` header and private key starts from `'-----BEGIN RSA PRIVATE KEY-----'` header
120 | * `'pkcs8'` — public key starts from `'-----BEGIN PUBLIC KEY-----'` header and private key starts from `'-----BEGIN PRIVATE KEY-----'` header
121 | * `'openssh'` — public key starts from `'ssh-rsa'` header and private key starts from `'-----BEGIN OPENSSH PRIVATE KEY-----'` header
122 | * `'components'` — use it for import/export key from/to raw components (see example below). For private key, importing data should contain all private key components, for public key: only public exponent (`e`) and modulus (`n`). All components (except `e`) should be Buffer, `e` could be Buffer or just normal Number.
123 |
124 | Key type — can be `'private'` or `'public'`. Default `'private'`
125 | Output type — can be:
126 |
127 | * `'pem'` — Base64 encoded string with header and footer. Used by default.
128 | * `'der'` — Binary encoded key data.
129 |
130 | > *Notice:* For import, if *keyData* is PEM string or buffer containing string, you can do not specify format, but if you provide *keyData* as DER you must specify it in format string.
131 |
132 | **Shortcuts and examples**
133 | * `'private'` or `'pkcs1'` or `'pkcs1-private'` == `'pkcs1-private-pem'` — private key encoded in pcks1 scheme as pem string.
134 | * `'public'` or `'pkcs8-public'` == `'pkcs8-public-pem'` — public key encoded in pcks8 scheme as pem string.
135 | * `'pkcs8'` or `'pkcs8-private'` == `'pkcs8-private-pem'` — private key encoded in pcks8 scheme as pem string.
136 | * `'pkcs1-der'` == `'pkcs1-private-der'` — private key encoded in pcks1 scheme as binary buffer.
137 | * `'pkcs8-public-der'` — public key encoded in pcks8 scheme as binary buffer.
138 |
139 | **Code example**
140 |
141 | ```javascript
142 | const keyData = '-----BEGIN PUBLIC KEY----- ... -----END PUBLIC KEY-----';
143 | key.importKey(keyData, 'pkcs8');
144 | const publicDer = key.exportKey('pkcs8-public-der');
145 | const privateDer = key.exportKey('pkcs1-der');
146 | ```
147 |
148 | ```javascript
149 | key.importKey({
150 | n: Buffer.from('0086fa9ba066685845fc03833a9699c8baefb53cfbf19052a7f10f1eaa30488cec1ceb752bdff2df9fad6c64b3498956e7dbab4035b4823c99a44cc57088a23783', 'hex'),
151 | e: 65537,
152 | d: Buffer.from('5d2f0dd982596ef781affb1cab73a77c46985c6da2aafc252cea3f4546e80f40c0e247d7d9467750ea1321cc5aa638871b3ed96d19dcc124916b0bcb296f35e1', 'hex'),
153 | p: Buffer.from('00c59419db615e56b9805cc45673a32d278917534804171edcf925ab1df203927f', 'hex'),
154 | q: Buffer.from('00aee3f86b66087abc069b8b1736e38ad6af624f7ea80e70b95f4ff2bf77cd90fd', 'hex'),
155 | dmp1: Buffer.from('008112f5a969fcb56f4e3a4c51a60dcdebec157ee4a7376b843487b53844e8ac85', 'hex'),
156 | dmq1: Buffer.from('1a7370470e0f8a4095df40922a430fe498720e03e1f70d257c3ce34202249d21', 'hex'),
157 | coeff: Buffer.from('00b399675e5e81506b729a777cc03026f0b2119853dfc5eb124610c0ab82999e45', 'hex')
158 | }, 'components');
159 | const publicComponents = key.exportKey('components-public');
160 | console.log(publicComponents);
161 |
162 | /*
163 | { n: ,
164 | e: 65537
165 | }
166 | */
167 | ```
168 |
169 | If you want to only import the public key use `'components-public'` as an option:
170 |
171 | ```javascript
172 | key.importKey({
173 | n: Buffer.from('0086fa9ba066685845fc03833a9699c8baefb53cfbf19052a7f10f1eaa30488cec1ceb752bdff2df9fad6c64b3498956e7dbab4035b4823c99a44cc57088a23783', 'hex'),
174 | e: 65537,
175 | }, 'components-public');
176 | ```
177 |
178 | ### Properties
179 |
180 | #### Key testing
181 | ```javascript
182 | key.isPrivate();
183 | key.isPublic([strict]);
184 | ```
185 | strict — `{boolean}` — if true method will return false if key pair have private exponent. Default `false`.
186 |
187 | ```javascript
188 | key.isEmpty();
189 | ```
190 | Return `true` if key pair doesn't have any data.
191 |
192 | #### Key info
193 | ```javascript
194 | key.getKeySize();
195 | ```
196 | Return key size in bits.
197 |
198 | ```javascript
199 | key.getMaxMessageSize();
200 | ```
201 | Return max data size for encrypt in bytes.
202 |
203 | ### Encrypting/decrypting
204 |
205 | ```javascript
206 | key.encrypt(buffer, [encoding], [source_encoding]);
207 | key.encryptPrivate(buffer, [encoding], [source_encoding]); // use private key for encryption
208 | ```
209 | Return encrypted data.
210 |
211 | * buffer — `{buffer}` — data for encrypting, may be string, Buffer, or any object/array. Arrays and objects will encoded to JSON string first.
212 | * encoding — `{string}` — encoding for output result, may be `'buffer'`, `'binary'`, `'hex'` or `'base64'`. Default `'buffer'`.
213 | * source_encoding — `{string}` — source encoding, works only with string buffer. Can take standard Node.js Buffer encodings (hex, utf8, base64, etc). `'utf8'` by default.
214 |
215 | ```javascript
216 | key.decrypt(buffer, [encoding]);
217 | key.decryptPublic(buffer, [encoding]); // use public key for decryption
218 | ```
219 | Return decrypted data.
220 |
221 | * buffer — `{buffer}` — data for decrypting. Takes Buffer object or base64 encoded string.
222 | * encoding — `{string}` — encoding for result string. Can also take `'buffer'` for raw Buffer object, or `'json'` for automatic JSON.parse result. Default `'buffer'`.
223 |
224 | > *Notice:* `encryptPrivate` and `decryptPublic` using only pkcs1 padding type 1 (not random)
225 |
226 | ### Signing/Verifying
227 | ```javascript
228 | key.sign(buffer, [encoding], [source_encoding]);
229 | ```
230 | Return signature for buffer. All the arguments are the same as for `encrypt` method.
231 |
232 | ```javascript
233 | key.verify(buffer, signature, [source_encoding], [signature_encoding])
234 | ```
235 | Return result of check, `true` or `false`.
236 |
237 | * buffer — `{buffer}` — data for check, same as `encrypt` method.
238 | * signature — `{string}` — signature for check, result of `sign` method.
239 | * source_encoding — `{string}` — same as for `encrypt` method.
240 | * signature_encoding — `{string}` — encoding of given signature. May be `'buffer'`, `'binary'`, `'hex'` or `'base64'`. Default `'buffer'`.
241 |
242 | ## Contributing
243 |
244 | Questions, comments, bug reports, and pull requests are all welcome.
245 |
246 | ## Changelog
247 |
248 | ### 1.1.0
249 | * Added OpenSSH key format support.
250 |
251 | ### 1.0.2
252 | * Importing keys from PEM now is less dependent on non-key data in files.
253 |
254 | ### 1.0.1
255 | * `importKey()` now returns `this`
256 |
257 | ### 1.0.0
258 | * Using semver now 🎉
259 | * **Breaking change**: Drop support nodejs < 8.11.1
260 | * **Possible breaking change**: `new Buffer()` call as deprecated was replaced by `Buffer.from` & `Buffer.alloc`.
261 | * **Possible breaking change**: Drop support for hash scheme `sha` (was removed in node ~10). `sha1`, `sha256` and others still works.
262 | * **Possible breaking change**: Little change in environment detect algorithm.
263 |
264 | ### 0.4.2
265 | * `no padding` scheme will padded data with zeros on all environments.
266 |
267 | ### 0.4.1
268 | * `PKCS1 no padding` scheme support.
269 |
270 | ### 0.4.0
271 | * License changed from BSD to MIT.
272 | * Some changes in internal api.
273 |
274 | ### 0.3.3
275 | * Fixed PSS encode/verify methods with max salt length.
276 |
277 | ### 0.3.2
278 | * Fixed environment detection in web worker.
279 |
280 | ### 0.3.0
281 | * Added import/export from/to raw key components.
282 | * Removed lodash from dependencies.
283 |
284 | ### 0.2.30
285 | * Fixed a issue when the key was generated by 1 bit smaller than specified. It may slow down the generation of large keys.
286 |
287 | ### 0.2.24
288 | * Now used old hash APIs for webpack compatible.
289 |
290 | ### 0.2.22
291 | * `encryptPrivate` and `decryptPublic` now using only pkcs1 (type 1) padding.
292 |
293 | ### 0.2.20
294 | * Added `.encryptPrivate()` and `.decryptPublic()` methods.
295 | * Encrypt/decrypt methods in nodejs 0.12.x and io.js using native implementation (> 40x speed boost).
296 | * Fixed some regex issue causing catastrophic backtracking.
297 |
298 | ### 0.2.10
299 | * **Methods `.exportPrivate()` and `.exportPublic()` was replaced by `.exportKey([format])`.**
300 | * By default `.exportKey()` returns private key as `.exportPrivate()`, if you need public key from `.exportPublic()` you must specify format as `'public'` or `'pkcs8-public-pem'`.
301 | * Method `.importKey(key, [format])` now has second argument.
302 |
303 | ### 0.2.0
304 | * **`.getPublicPEM()` method was renamed to `.exportPublic()`**
305 | * **`.getPrivatePEM()` method was renamed to `.exportPrivate()`**
306 | * **`.loadFromPEM()` method was renamed to `.importKey()`**
307 | * Added PKCS1_OAEP encrypting/decrypting support.
308 | * **PKCS1_OAEP now default scheme, you need to specify 'encryptingScheme' option to 'pkcs1' for compatibility with 0.1.x version of NodeRSA.**
309 | * Added PSS signing/verifying support.
310 | * Signing now supports `'md5'`, `'ripemd160'`, `'sha1'`, `'sha256'`, `'sha512'` hash algorithms in both environments
311 | and additional `'md4'`, `'sha'`, `'sha224'`, `'sha384'` for nodejs env.
312 | * **`options.signingAlgorithm` was renamed to `options.signingScheme`**
313 | * Added `encryptingScheme` option.
314 | * Property `key.options` now mark as private. Added `key.setOptions(options)` method.
315 |
316 |
317 | ### 0.1.54
318 | * Added support for loading PEM key from Buffer (`fs.readFileSync()` output).
319 | * Added `isEmpty()` method.
320 |
321 | ### 0.1.52
322 | * Improve work with not properly trimming PEM strings.
323 |
324 | ### 0.1.50
325 | * Implemented native js signing and verifying for browsers.
326 | * `options.signingAlgorithm` now takes only hash-algorithm name.
327 | * Added `.getKeySize()` and `.getMaxMessageSize()` methods.
328 | * `.loadFromPublicPEM` and `.loadFromPrivatePEM` methods marked as private.
329 |
330 | ### 0.1.40
331 | * Added signing/verifying.
332 |
333 | ### 0.1.30
334 | * Added long message support.
335 |
336 |
337 | ## License
338 |
339 | Copyright (c) 2014 rzcoder
340 |
341 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
342 |
343 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
344 |
345 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
346 |
347 | ## Licensing for code used in rsa.js and jsbn.js
348 |
349 | Copyright (c) 2003-2005 Tom Wu
350 | All Rights Reserved.
351 |
352 | Permission is hereby granted, free of charge, to any person obtaining
353 | a copy of this software and associated documentation files (the
354 | "Software"), to deal in the Software without restriction, including
355 | without limitation the rights to use, copy, modify, merge, publish,
356 | distribute, sublicense, and/or sell copies of the Software, and to
357 | permit persons to whom the Software is furnished to do so, subject to
358 | the following conditions:
359 |
360 | The above copyright notice and this permission notice shall be
361 | included in all copies or substantial portions of the Software.
362 | THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
363 | EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
364 | WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
365 |
366 | IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
367 | INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
368 | RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
369 | THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
370 | OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
371 |
372 | In addition, the following condition applies:
373 |
374 | All redistributions must retain an intact copy of this copyright notice
375 | and disclaimer.
376 |
377 | [](https://travis-ci.org/rzcoder/node-rsa)
378 |
--------------------------------------------------------------------------------
/gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function (grunt) {
2 | grunt.initConfig({
3 | jshint: {
4 | options: {},
5 | default: {
6 | files: {
7 | src: ['gruntfile.js', 'src/**/*.js', '!src/libs/jsbn.js']
8 | }
9 | },
10 | libs: {
11 | files: {
12 | src: ['src/libs/**/*']
13 | }
14 | }
15 | },
16 |
17 | simplemocha: {
18 | options: {
19 | reporter: 'list'
20 | },
21 | all: {src: ['test/**/*.js']}
22 | }
23 | });
24 |
25 | require('jit-grunt')(grunt, {
26 | 'simplemocha': 'grunt-simple-mocha'
27 | });
28 |
29 | grunt.registerTask('lint', ['jshint:default']);
30 | grunt.registerTask('test', ['simplemocha']);
31 |
32 | grunt.registerTask('default', ['lint', 'test']);
33 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "node-rsa",
3 | "version": "1.1.1",
4 | "description": "Node.js RSA library",
5 | "main": "src/NodeRSA.js",
6 | "scripts": {
7 | "test": "grunt test"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/rzcoder/node-rsa.git"
12 | },
13 | "keywords": [
14 | "node",
15 | "rsa",
16 | "crypto",
17 | "assymetric",
18 | "encryption",
19 | "decryption",
20 | "sign",
21 | "verify",
22 | "pkcs1",
23 | "oaep",
24 | "pss"
25 | ],
26 | "author": "rzcoder",
27 | "license": "MIT",
28 | "bugs": {
29 | "url": "https://github.com/rzcoder/node-rsa/issues"
30 | },
31 | "homepage": "https://github.com/rzcoder/node-rsa",
32 | "devDependencies": {
33 | "chai": "^4.2.0",
34 | "grunt": "^1.1.0",
35 | "grunt-contrib-jshint": "^2.1.0",
36 | "grunt-simple-mocha": "0.4.1",
37 | "jit-grunt": "0.10.0",
38 | "lodash": "^4.17.15",
39 | "nyc": "^15.0.0"
40 | },
41 | "dependencies": {
42 | "asn1": "^0.2.4"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/NodeRSA.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * RSA library for Node.js
3 | *
4 | * Author: rzcoder
5 | * License MIT
6 | */
7 |
8 | var constants = require('constants');
9 | var rsa = require('./libs/rsa.js');
10 | var crypt = require('crypto');
11 | var ber = require('asn1').Ber;
12 | var _ = require('./utils')._;
13 | var utils = require('./utils');
14 | var schemes = require('./schemes/schemes.js');
15 | var formats = require('./formats/formats.js');
16 |
17 | if (typeof constants.RSA_NO_PADDING === "undefined") {
18 | //patch for node v0.10.x, constants do not defined
19 | constants.RSA_NO_PADDING = 3;
20 | }
21 |
22 | module.exports = (function () {
23 | var SUPPORTED_HASH_ALGORITHMS = {
24 | node10: ['md4', 'md5', 'ripemd160', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512'],
25 | node: ['md4', 'md5', 'ripemd160', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512'],
26 | iojs: ['md4', 'md5', 'ripemd160', 'sha1', 'sha224', 'sha256', 'sha384', 'sha512'],
27 | browser: ['md5', 'ripemd160', 'sha1', 'sha256', 'sha512']
28 | };
29 |
30 | var DEFAULT_ENCRYPTION_SCHEME = 'pkcs1_oaep';
31 | var DEFAULT_SIGNING_SCHEME = 'pkcs1';
32 |
33 | var DEFAULT_EXPORT_FORMAT = 'private';
34 | var EXPORT_FORMAT_ALIASES = {
35 | 'private': 'pkcs1-private-pem',
36 | 'private-der': 'pkcs1-private-der',
37 | 'public': 'pkcs8-public-pem',
38 | 'public-der': 'pkcs8-public-der',
39 | };
40 |
41 | /**
42 | * @param key {string|buffer|object} Key in PEM format, or data for generate key {b: bits, e: exponent}
43 | * @constructor
44 | */
45 | function NodeRSA(key, format, options) {
46 | if (!(this instanceof NodeRSA)) {
47 | return new NodeRSA(key, format, options);
48 | }
49 |
50 | if (_.isObject(format)) {
51 | options = format;
52 | format = undefined;
53 | }
54 |
55 | this.$options = {
56 | signingScheme: DEFAULT_SIGNING_SCHEME,
57 | signingSchemeOptions: {
58 | hash: 'sha256',
59 | saltLength: null
60 | },
61 | encryptionScheme: DEFAULT_ENCRYPTION_SCHEME,
62 | encryptionSchemeOptions: {
63 | hash: 'sha1',
64 | label: null
65 | },
66 | environment: utils.detectEnvironment(),
67 | rsaUtils: this
68 | };
69 | this.keyPair = new rsa.Key();
70 | this.$cache = {};
71 |
72 | if (Buffer.isBuffer(key) || _.isString(key)) {
73 | this.importKey(key, format);
74 | } else if (_.isObject(key)) {
75 | this.generateKeyPair(key.b, key.e);
76 | }
77 |
78 | this.setOptions(options);
79 | }
80 |
81 | /**
82 | * Set and validate options for key instance
83 | * @param options
84 | */
85 | NodeRSA.prototype.setOptions = function (options) {
86 | options = options || {};
87 | if (options.environment) {
88 | this.$options.environment = options.environment;
89 | }
90 |
91 | if (options.signingScheme) {
92 | if (_.isString(options.signingScheme)) {
93 | var signingScheme = options.signingScheme.toLowerCase().split('-');
94 | if (signingScheme.length == 1) {
95 | if (SUPPORTED_HASH_ALGORITHMS.node.indexOf(signingScheme[0]) > -1) {
96 | this.$options.signingSchemeOptions = {
97 | hash: signingScheme[0]
98 | };
99 | this.$options.signingScheme = DEFAULT_SIGNING_SCHEME;
100 | } else {
101 | this.$options.signingScheme = signingScheme[0];
102 | this.$options.signingSchemeOptions = {
103 | hash: null
104 | };
105 | }
106 | } else {
107 | this.$options.signingSchemeOptions = {
108 | hash: signingScheme[1]
109 | };
110 | this.$options.signingScheme = signingScheme[0];
111 | }
112 | } else if (_.isObject(options.signingScheme)) {
113 | this.$options.signingScheme = options.signingScheme.scheme || DEFAULT_SIGNING_SCHEME;
114 | this.$options.signingSchemeOptions = _.omit(options.signingScheme, 'scheme');
115 | }
116 |
117 | if (!schemes.isSignature(this.$options.signingScheme)) {
118 | throw Error('Unsupported signing scheme');
119 | }
120 |
121 | if (this.$options.signingSchemeOptions.hash &&
122 | SUPPORTED_HASH_ALGORITHMS[this.$options.environment].indexOf(this.$options.signingSchemeOptions.hash) === -1) {
123 | throw Error('Unsupported hashing algorithm for ' + this.$options.environment + ' environment');
124 | }
125 | }
126 |
127 | if (options.encryptionScheme) {
128 | if (_.isString(options.encryptionScheme)) {
129 | this.$options.encryptionScheme = options.encryptionScheme.toLowerCase();
130 | this.$options.encryptionSchemeOptions = {};
131 | } else if (_.isObject(options.encryptionScheme)) {
132 | this.$options.encryptionScheme = options.encryptionScheme.scheme || DEFAULT_ENCRYPTION_SCHEME;
133 | this.$options.encryptionSchemeOptions = _.omit(options.encryptionScheme, 'scheme');
134 | }
135 |
136 | if (!schemes.isEncryption(this.$options.encryptionScheme)) {
137 | throw Error('Unsupported encryption scheme');
138 | }
139 |
140 | if (this.$options.encryptionSchemeOptions.hash &&
141 | SUPPORTED_HASH_ALGORITHMS[this.$options.environment].indexOf(this.$options.encryptionSchemeOptions.hash) === -1) {
142 | throw Error('Unsupported hashing algorithm for ' + this.$options.environment + ' environment');
143 | }
144 | }
145 |
146 | this.keyPair.setOptions(this.$options);
147 | };
148 |
149 | /**
150 | * Generate private/public keys pair
151 | *
152 | * @param bits {int} length key in bits. Default 2048.
153 | * @param exp {int} public exponent. Default 65537.
154 | * @returns {NodeRSA}
155 | */
156 | NodeRSA.prototype.generateKeyPair = function (bits, exp) {
157 | bits = bits || 2048;
158 | exp = exp || 65537;
159 |
160 | if (bits % 8 !== 0) {
161 | throw Error('Key size must be a multiple of 8.');
162 | }
163 |
164 | this.keyPair.generate(bits, exp.toString(16));
165 | this.$cache = {};
166 | return this;
167 | };
168 |
169 | /**
170 | * Importing key
171 | * @param keyData {string|buffer|Object}
172 | * @param format {string}
173 | */
174 | NodeRSA.prototype.importKey = function (keyData, format) {
175 | if (!keyData) {
176 | throw Error("Empty key given");
177 | }
178 |
179 | if (format) {
180 | format = EXPORT_FORMAT_ALIASES[format] || format;
181 | }
182 |
183 | if (!formats.detectAndImport(this.keyPair, keyData, format) && format === undefined) {
184 | throw Error("Key format must be specified");
185 | }
186 |
187 | this.$cache = {};
188 |
189 | return this;
190 | };
191 |
192 | /**
193 | * Exporting key
194 | * @param [format] {string}
195 | */
196 | NodeRSA.prototype.exportKey = function (format) {
197 | format = format || DEFAULT_EXPORT_FORMAT;
198 | format = EXPORT_FORMAT_ALIASES[format] || format;
199 |
200 | if (!this.$cache[format]) {
201 | this.$cache[format] = formats.detectAndExport(this.keyPair, format);
202 | }
203 |
204 | return this.$cache[format];
205 | };
206 |
207 | /**
208 | * Check if key pair contains private key
209 | */
210 | NodeRSA.prototype.isPrivate = function () {
211 | return this.keyPair.isPrivate();
212 | };
213 |
214 | /**
215 | * Check if key pair contains public key
216 | * @param [strict] {boolean} - public key only, return false if have private exponent
217 | */
218 | NodeRSA.prototype.isPublic = function (strict) {
219 | return this.keyPair.isPublic(strict);
220 | };
221 |
222 | /**
223 | * Check if key pair doesn't contains any data
224 | */
225 | NodeRSA.prototype.isEmpty = function (strict) {
226 | return !(this.keyPair.n || this.keyPair.e || this.keyPair.d);
227 | };
228 |
229 | /**
230 | * Encrypting data method with public key
231 | *
232 | * @param buffer {string|number|object|array|Buffer} - data for encrypting. Object and array will convert to JSON string.
233 | * @param encoding {string} - optional. Encoding for output result, may be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'.
234 | * @param source_encoding {string} - optional. Encoding for given string. Default utf8.
235 | * @returns {string|Buffer}
236 | */
237 | NodeRSA.prototype.encrypt = function (buffer, encoding, source_encoding) {
238 | return this.$$encryptKey(false, buffer, encoding, source_encoding);
239 | };
240 |
241 | /**
242 | * Decrypting data method with private key
243 | *
244 | * @param buffer {Buffer} - buffer for decrypting
245 | * @param encoding - encoding for result string, can also take 'json' or 'buffer' for the automatic conversion of this type
246 | * @returns {Buffer|object|string}
247 | */
248 | NodeRSA.prototype.decrypt = function (buffer, encoding) {
249 | return this.$$decryptKey(false, buffer, encoding);
250 | };
251 |
252 | /**
253 | * Encrypting data method with private key
254 | *
255 | * Parameters same as `encrypt` method
256 | */
257 | NodeRSA.prototype.encryptPrivate = function (buffer, encoding, source_encoding) {
258 | return this.$$encryptKey(true, buffer, encoding, source_encoding);
259 | };
260 |
261 | /**
262 | * Decrypting data method with public key
263 | *
264 | * Parameters same as `decrypt` method
265 | */
266 | NodeRSA.prototype.decryptPublic = function (buffer, encoding) {
267 | return this.$$decryptKey(true, buffer, encoding);
268 | };
269 |
270 | /**
271 | * Encrypting data method with custom key
272 | */
273 | NodeRSA.prototype.$$encryptKey = function (usePrivate, buffer, encoding, source_encoding) {
274 | try {
275 | var res = this.keyPair.encrypt(this.$getDataForEncrypt(buffer, source_encoding), usePrivate);
276 |
277 | if (encoding == 'buffer' || !encoding) {
278 | return res;
279 | } else {
280 | return res.toString(encoding);
281 | }
282 | } catch (e) {
283 | throw Error('Error during encryption. Original error: ' + e);
284 | }
285 | };
286 |
287 | /**
288 | * Decrypting data method with custom key
289 | */
290 | NodeRSA.prototype.$$decryptKey = function (usePublic, buffer, encoding) {
291 | try {
292 | buffer = _.isString(buffer) ? Buffer.from(buffer, 'base64') : buffer;
293 | var res = this.keyPair.decrypt(buffer, usePublic);
294 |
295 | if (res === null) {
296 | throw Error('Key decrypt method returns null.');
297 | }
298 |
299 | return this.$getDecryptedData(res, encoding);
300 | } catch (e) {
301 | throw Error('Error during decryption (probably incorrect key). Original error: ' + e);
302 | }
303 | };
304 |
305 | /**
306 | * Signing data
307 | *
308 | * @param buffer {string|number|object|array|Buffer} - data for signing. Object and array will convert to JSON string.
309 | * @param encoding {string} - optional. Encoding for output result, may be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'.
310 | * @param source_encoding {string} - optional. Encoding for given string. Default utf8.
311 | * @returns {string|Buffer}
312 | */
313 | NodeRSA.prototype.sign = function (buffer, encoding, source_encoding) {
314 | if (!this.isPrivate()) {
315 | throw Error("This is not private key");
316 | }
317 |
318 | var res = this.keyPair.sign(this.$getDataForEncrypt(buffer, source_encoding));
319 |
320 | if (encoding && encoding != 'buffer') {
321 | res = res.toString(encoding);
322 | }
323 |
324 | return res;
325 | };
326 |
327 | /**
328 | * Verifying signed data
329 | *
330 | * @param buffer - signed data
331 | * @param signature
332 | * @param source_encoding {string} - optional. Encoding for given string. Default utf8.
333 | * @param signature_encoding - optional. Encoding of given signature. May be 'buffer', 'binary', 'hex' or 'base64'. Default 'buffer'.
334 | * @returns {*}
335 | */
336 | NodeRSA.prototype.verify = function (buffer, signature, source_encoding, signature_encoding) {
337 | if (!this.isPublic()) {
338 | throw Error("This is not public key");
339 | }
340 | signature_encoding = (!signature_encoding || signature_encoding == 'buffer' ? null : signature_encoding);
341 | return this.keyPair.verify(this.$getDataForEncrypt(buffer, source_encoding), signature, signature_encoding);
342 | };
343 |
344 | /**
345 | * Returns key size in bits
346 | * @returns {int}
347 | */
348 | NodeRSA.prototype.getKeySize = function () {
349 | return this.keyPair.keySize;
350 | };
351 |
352 | /**
353 | * Returns max message length in bytes (for 1 chunk) depending on current encryption scheme
354 | * @returns {int}
355 | */
356 | NodeRSA.prototype.getMaxMessageSize = function () {
357 | return this.keyPair.maxMessageLength;
358 | };
359 |
360 | /**
361 | * Preparing given data for encrypting/signing. Just make new/return Buffer object.
362 | *
363 | * @param buffer {string|number|object|array|Buffer} - data for encrypting. Object and array will convert to JSON string.
364 | * @param encoding {string} - optional. Encoding for given string. Default utf8.
365 | * @returns {Buffer}
366 | */
367 | NodeRSA.prototype.$getDataForEncrypt = function (buffer, encoding) {
368 | if (_.isString(buffer) || _.isNumber(buffer)) {
369 | return Buffer.from('' + buffer, encoding || 'utf8');
370 | } else if (Buffer.isBuffer(buffer)) {
371 | return buffer;
372 | } else if (_.isObject(buffer)) {
373 | return Buffer.from(JSON.stringify(buffer));
374 | } else {
375 | throw Error("Unexpected data type");
376 | }
377 | };
378 |
379 | /**
380 | *
381 | * @param buffer {Buffer} - decrypted data.
382 | * @param encoding - optional. Encoding for result output. May be 'buffer', 'json' or any of Node.js Buffer supported encoding.
383 | * @returns {*}
384 | */
385 | NodeRSA.prototype.$getDecryptedData = function (buffer, encoding) {
386 | encoding = encoding || 'buffer';
387 |
388 | if (encoding == 'buffer') {
389 | return buffer;
390 | } else if (encoding == 'json') {
391 | return JSON.parse(buffer.toString());
392 | } else {
393 | return buffer.toString(encoding);
394 | }
395 | };
396 |
397 | return NodeRSA;
398 | })();
399 |
--------------------------------------------------------------------------------
/src/encryptEngines/encryptEngines.js:
--------------------------------------------------------------------------------
1 | var crypt = require('crypto');
2 |
3 | module.exports = {
4 | getEngine: function (keyPair, options) {
5 | var engine = require('./js.js');
6 | if (options.environment === 'node') {
7 | if (typeof crypt.publicEncrypt === 'function' && typeof crypt.privateDecrypt === 'function') {
8 | if (typeof crypt.privateEncrypt === 'function' && typeof crypt.publicDecrypt === 'function') {
9 | engine = require('./io.js');
10 | } else {
11 | engine = require('./node12.js');
12 | }
13 | }
14 | }
15 | return engine(keyPair, options);
16 | }
17 | };
--------------------------------------------------------------------------------
/src/encryptEngines/io.js:
--------------------------------------------------------------------------------
1 | var crypto = require('crypto');
2 | var constants = require('constants');
3 | var schemes = require('../schemes/schemes.js');
4 |
5 | module.exports = function (keyPair, options) {
6 | var pkcs1Scheme = schemes.pkcs1.makeScheme(keyPair, options);
7 |
8 | return {
9 | encrypt: function (buffer, usePrivate) {
10 | var padding;
11 | if (usePrivate) {
12 | padding = constants.RSA_PKCS1_PADDING;
13 | if (options.encryptionSchemeOptions && options.encryptionSchemeOptions.padding) {
14 | padding = options.encryptionSchemeOptions.padding;
15 | }
16 | return crypto.privateEncrypt({
17 | key: options.rsaUtils.exportKey('private'),
18 | padding: padding
19 | }, buffer);
20 | } else {
21 | padding = constants.RSA_PKCS1_OAEP_PADDING;
22 | if (options.encryptionScheme === 'pkcs1') {
23 | padding = constants.RSA_PKCS1_PADDING;
24 | }
25 | if (options.encryptionSchemeOptions && options.encryptionSchemeOptions.padding) {
26 | padding = options.encryptionSchemeOptions.padding;
27 | }
28 |
29 | var data = buffer;
30 | if (padding === constants.RSA_NO_PADDING) {
31 | data = pkcs1Scheme.pkcs0pad(buffer);
32 | }
33 |
34 | return crypto.publicEncrypt({
35 | key: options.rsaUtils.exportKey('public'),
36 | padding: padding
37 | }, data);
38 | }
39 | },
40 |
41 | decrypt: function (buffer, usePublic) {
42 | var padding;
43 | if (usePublic) {
44 | padding = constants.RSA_PKCS1_PADDING;
45 | if (options.encryptionSchemeOptions && options.encryptionSchemeOptions.padding) {
46 | padding = options.encryptionSchemeOptions.padding;
47 | }
48 | return crypto.publicDecrypt({
49 | key: options.rsaUtils.exportKey('public'),
50 | padding: padding
51 | }, buffer);
52 | } else {
53 | padding = constants.RSA_PKCS1_OAEP_PADDING;
54 | if (options.encryptionScheme === 'pkcs1') {
55 | padding = constants.RSA_PKCS1_PADDING;
56 | }
57 | if (options.encryptionSchemeOptions && options.encryptionSchemeOptions.padding) {
58 | padding = options.encryptionSchemeOptions.padding;
59 | }
60 | var res = crypto.privateDecrypt({
61 | key: options.rsaUtils.exportKey('private'),
62 | padding: padding
63 | }, buffer);
64 |
65 | if (padding === constants.RSA_NO_PADDING) {
66 | return pkcs1Scheme.pkcs0unpad(res);
67 | }
68 | return res;
69 | }
70 | }
71 | };
72 | };
--------------------------------------------------------------------------------
/src/encryptEngines/js.js:
--------------------------------------------------------------------------------
1 | var BigInteger = require('../libs/jsbn.js');
2 | var schemes = require('../schemes/schemes.js');
3 |
4 | module.exports = function (keyPair, options) {
5 | var pkcs1Scheme = schemes.pkcs1.makeScheme(keyPair, options);
6 |
7 | return {
8 | encrypt: function (buffer, usePrivate) {
9 | var m, c;
10 | if (usePrivate) {
11 | /* Type 1: zeros padding for private key encrypt */
12 | m = new BigInteger(pkcs1Scheme.encPad(buffer, {type: 1}));
13 | c = keyPair.$doPrivate(m);
14 | } else {
15 | m = new BigInteger(keyPair.encryptionScheme.encPad(buffer));
16 | c = keyPair.$doPublic(m);
17 | }
18 | return c.toBuffer(keyPair.encryptedDataLength);
19 | },
20 |
21 | decrypt: function (buffer, usePublic) {
22 | var m, c = new BigInteger(buffer);
23 |
24 | if (usePublic) {
25 | m = keyPair.$doPublic(c);
26 | /* Type 1: zeros padding for private key decrypt */
27 | return pkcs1Scheme.encUnPad(m.toBuffer(keyPair.encryptedDataLength), {type: 1});
28 | } else {
29 | m = keyPair.$doPrivate(c);
30 | return keyPair.encryptionScheme.encUnPad(m.toBuffer(keyPair.encryptedDataLength));
31 | }
32 | }
33 | };
34 | };
--------------------------------------------------------------------------------
/src/encryptEngines/node12.js:
--------------------------------------------------------------------------------
1 | var crypto = require('crypto');
2 | var constants = require('constants');
3 | var schemes = require('../schemes/schemes.js');
4 |
5 | module.exports = function (keyPair, options) {
6 | var jsEngine = require('./js.js')(keyPair, options);
7 | var pkcs1Scheme = schemes.pkcs1.makeScheme(keyPair, options);
8 |
9 | return {
10 | encrypt: function (buffer, usePrivate) {
11 | if (usePrivate) {
12 | return jsEngine.encrypt(buffer, usePrivate);
13 | }
14 | var padding = constants.RSA_PKCS1_OAEP_PADDING;
15 | if (options.encryptionScheme === 'pkcs1') {
16 | padding = constants.RSA_PKCS1_PADDING;
17 | }
18 | if (options.encryptionSchemeOptions && options.encryptionSchemeOptions.padding) {
19 | padding = options.encryptionSchemeOptions.padding;
20 | }
21 |
22 | var data = buffer;
23 | if (padding === constants.RSA_NO_PADDING) {
24 | data = pkcs1Scheme.pkcs0pad(buffer);
25 | }
26 |
27 | return crypto.publicEncrypt({
28 | key: options.rsaUtils.exportKey('public'),
29 | padding: padding
30 | }, data);
31 | },
32 |
33 | decrypt: function (buffer, usePublic) {
34 | if (usePublic) {
35 | return jsEngine.decrypt(buffer, usePublic);
36 | }
37 | var padding = constants.RSA_PKCS1_OAEP_PADDING;
38 | if (options.encryptionScheme === 'pkcs1') {
39 | padding = constants.RSA_PKCS1_PADDING;
40 | }
41 | if (options.encryptionSchemeOptions && options.encryptionSchemeOptions.padding) {
42 | padding = options.encryptionSchemeOptions.padding;
43 | }
44 |
45 | var res = crypto.privateDecrypt({
46 | key: options.rsaUtils.exportKey('private'),
47 | padding: padding
48 | }, buffer);
49 |
50 | if (padding === constants.RSA_NO_PADDING) {
51 | return pkcs1Scheme.pkcs0unpad(res);
52 | }
53 | return res;
54 | }
55 | };
56 | };
--------------------------------------------------------------------------------
/src/formats/components.js:
--------------------------------------------------------------------------------
1 | var _ = require('../utils')._;
2 | var utils = require('../utils');
3 |
4 | module.exports = {
5 | privateExport: function (key, options) {
6 | return {
7 | n: key.n.toBuffer(),
8 | e: key.e,
9 | d: key.d.toBuffer(),
10 | p: key.p.toBuffer(),
11 | q: key.q.toBuffer(),
12 | dmp1: key.dmp1.toBuffer(),
13 | dmq1: key.dmq1.toBuffer(),
14 | coeff: key.coeff.toBuffer()
15 | };
16 | },
17 |
18 | privateImport: function (key, data, options) {
19 | if (data.n && data.e && data.d && data.p && data.q && data.dmp1 && data.dmq1 && data.coeff) {
20 | key.setPrivate(
21 | data.n,
22 | data.e,
23 | data.d,
24 | data.p,
25 | data.q,
26 | data.dmp1,
27 | data.dmq1,
28 | data.coeff
29 | );
30 | } else {
31 | throw Error("Invalid key data");
32 | }
33 | },
34 |
35 | publicExport: function (key, options) {
36 | return {
37 | n: key.n.toBuffer(),
38 | e: key.e
39 | };
40 | },
41 |
42 | publicImport: function (key, data, options) {
43 | if (data.n && data.e) {
44 | key.setPublic(
45 | data.n,
46 | data.e
47 | );
48 | } else {
49 | throw Error("Invalid key data");
50 | }
51 | },
52 |
53 | /**
54 | * Trying autodetect and import key
55 | * @param key
56 | * @param data
57 | */
58 | autoImport: function (key, data) {
59 | if (data.n && data.e) {
60 | if (data.d && data.p && data.q && data.dmp1 && data.dmq1 && data.coeff) {
61 | module.exports.privateImport(key, data);
62 | return true;
63 | } else {
64 | module.exports.publicImport(key, data);
65 | return true;
66 | }
67 | }
68 |
69 | return false;
70 | }
71 | };
72 |
--------------------------------------------------------------------------------
/src/formats/formats.js:
--------------------------------------------------------------------------------
1 | var _ = require('../utils')._;
2 |
3 | function formatParse(format) {
4 | format = format.split('-');
5 | var keyType = 'private';
6 | var keyOpt = {type: 'default'};
7 |
8 | for (var i = 1; i < format.length; i++) {
9 | if (format[i]) {
10 | switch (format[i]) {
11 | case 'public':
12 | keyType = format[i];
13 | break;
14 | case 'private':
15 | keyType = format[i];
16 | break;
17 | case 'pem':
18 | keyOpt.type = format[i];
19 | break;
20 | case 'der':
21 | keyOpt.type = format[i];
22 | break;
23 | }
24 | }
25 | }
26 |
27 | return {scheme: format[0], keyType: keyType, keyOpt: keyOpt};
28 | }
29 |
30 | module.exports = {
31 | pkcs1: require('./pkcs1'),
32 | pkcs8: require('./pkcs8'),
33 | components: require('./components'),
34 | openssh: require('./openssh'),
35 |
36 | isPrivateExport: function (format) {
37 | return module.exports[format] && typeof module.exports[format].privateExport === 'function';
38 | },
39 |
40 | isPrivateImport: function (format) {
41 | return module.exports[format] && typeof module.exports[format].privateImport === 'function';
42 | },
43 |
44 | isPublicExport: function (format) {
45 | return module.exports[format] && typeof module.exports[format].publicExport === 'function';
46 | },
47 |
48 | isPublicImport: function (format) {
49 | return module.exports[format] && typeof module.exports[format].publicImport === 'function';
50 | },
51 |
52 | detectAndImport: function (key, data, format) {
53 | if (format === undefined) {
54 | for (var scheme in module.exports) {
55 | if (typeof module.exports[scheme].autoImport === 'function' && module.exports[scheme].autoImport(key, data)) {
56 | return true;
57 | }
58 | }
59 | } else if (format) {
60 | var fmt = formatParse(format);
61 |
62 | if (module.exports[fmt.scheme]) {
63 | if (fmt.keyType === 'private') {
64 | module.exports[fmt.scheme].privateImport(key, data, fmt.keyOpt);
65 | } else {
66 | module.exports[fmt.scheme].publicImport(key, data, fmt.keyOpt);
67 | }
68 | } else {
69 | throw Error('Unsupported key format');
70 | }
71 | }
72 |
73 | return false;
74 | },
75 |
76 | detectAndExport: function (key, format) {
77 | if (format) {
78 | var fmt = formatParse(format);
79 |
80 | if (module.exports[fmt.scheme]) {
81 | if (fmt.keyType === 'private') {
82 | if (!key.isPrivate()) {
83 | throw Error("This is not private key");
84 | }
85 | return module.exports[fmt.scheme].privateExport(key, fmt.keyOpt);
86 | } else {
87 | if (!key.isPublic()) {
88 | throw Error("This is not public key");
89 | }
90 | return module.exports[fmt.scheme].publicExport(key, fmt.keyOpt);
91 | }
92 | } else {
93 | throw Error('Unsupported key format');
94 | }
95 | }
96 | }
97 | };
--------------------------------------------------------------------------------
/src/formats/openssh.js:
--------------------------------------------------------------------------------
1 | var _ = require("../utils")._;
2 | var utils = require("../utils");
3 | var BigInteger = require("../libs/jsbn");
4 |
5 | const PRIVATE_OPENING_BOUNDARY = "-----BEGIN OPENSSH PRIVATE KEY-----";
6 | const PRIVATE_CLOSING_BOUNDARY = "-----END OPENSSH PRIVATE KEY-----";
7 |
8 | module.exports = {
9 | privateExport: function (key, options) {
10 | const nbuf = key.n.toBuffer();
11 |
12 | let ebuf = Buffer.alloc(4)
13 | ebuf.writeUInt32BE(key.e, 0);
14 | //Slice leading zeroes
15 | while (ebuf[0] === 0) ebuf = ebuf.slice(1);
16 |
17 | const dbuf = key.d.toBuffer();
18 | const coeffbuf = key.coeff.toBuffer();
19 | const pbuf = key.p.toBuffer();
20 | const qbuf = key.q.toBuffer();
21 | let commentbuf;
22 | if (typeof key.sshcomment !== "undefined") {
23 | commentbuf = Buffer.from(key.sshcomment);
24 | } else {
25 | commentbuf = Buffer.from([]);
26 | }
27 |
28 | const pubkeyLength =
29 | 11 + // 32bit length, 'ssh-rsa'
30 | 4 + ebuf.byteLength +
31 | 4 + nbuf.byteLength;
32 |
33 | const privateKeyLength =
34 | 8 + //64bit unused checksum
35 | 11 + // 32bit length, 'ssh-rsa'
36 | 4 + nbuf.byteLength +
37 | 4 + ebuf.byteLength +
38 | 4 + dbuf.byteLength +
39 | 4 + coeffbuf.byteLength +
40 | 4 + pbuf.byteLength +
41 | 4 + qbuf.byteLength +
42 | 4 + commentbuf.byteLength;
43 |
44 | let length =
45 | 15 + //openssh-key-v1,0x00,
46 | 16 + // 2*(32bit length, 'none')
47 | 4 + // 32bit length, empty string
48 | 4 + // 32bit number of keys
49 | 4 + // 32bit pubkey length
50 | pubkeyLength +
51 | 4 + //32bit private+checksum+comment+padding length
52 | privateKeyLength;
53 |
54 | const paddingLength = Math.ceil(privateKeyLength / 8) * 8 - privateKeyLength;
55 | length += paddingLength;
56 |
57 | const buf = Buffer.alloc(length);
58 | const writer = {buf: buf, off: 0};
59 | buf.write("openssh-key-v1", "utf8");
60 | buf.writeUInt8(0, 14);
61 | writer.off += 15;
62 |
63 | writeOpenSSHKeyString(writer, Buffer.from("none"));
64 | writeOpenSSHKeyString(writer, Buffer.from("none"));
65 | writeOpenSSHKeyString(writer, Buffer.from(""));
66 |
67 | writer.off = writer.buf.writeUInt32BE(1, writer.off);
68 | writer.off = writer.buf.writeUInt32BE(pubkeyLength, writer.off);
69 |
70 | writeOpenSSHKeyString(writer, Buffer.from("ssh-rsa"));
71 | writeOpenSSHKeyString(writer, ebuf);
72 | writeOpenSSHKeyString(writer, nbuf);
73 |
74 | writer.off = writer.buf.writeUInt32BE(
75 | length - 47 - pubkeyLength,
76 | writer.off
77 | );
78 | writer.off += 8;
79 |
80 | writeOpenSSHKeyString(writer, Buffer.from("ssh-rsa"));
81 | writeOpenSSHKeyString(writer, nbuf);
82 | writeOpenSSHKeyString(writer, ebuf);
83 | writeOpenSSHKeyString(writer, dbuf);
84 | writeOpenSSHKeyString(writer, coeffbuf);
85 | writeOpenSSHKeyString(writer, pbuf);
86 | writeOpenSSHKeyString(writer, qbuf);
87 | writeOpenSSHKeyString(writer, commentbuf);
88 |
89 | let pad = 0x01;
90 | while (writer.off < length) {
91 | writer.off = writer.buf.writeUInt8(pad++, writer.off);
92 | }
93 |
94 | if (options.type === "der") {
95 | return writer.buf
96 | } else {
97 | return PRIVATE_OPENING_BOUNDARY + "\n" + utils.linebrk(buf.toString("base64"), 70) + "\n" + PRIVATE_CLOSING_BOUNDARY + "\n";
98 | }
99 | },
100 |
101 | privateImport: function (key, data, options) {
102 | options = options || {};
103 | var buffer;
104 |
105 | if (options.type !== "der") {
106 | if (Buffer.isBuffer(data)) {
107 | data = data.toString("utf8");
108 | }
109 |
110 | if (_.isString(data)) {
111 | var pem = utils.trimSurroundingText(data, PRIVATE_OPENING_BOUNDARY, PRIVATE_CLOSING_BOUNDARY)
112 | .replace(/\s+|\n\r|\n|\r$/gm, "");
113 | buffer = Buffer.from(pem, "base64");
114 | } else {
115 | throw Error("Unsupported key format");
116 | }
117 | } else if (Buffer.isBuffer(data)) {
118 | buffer = data;
119 | } else {
120 | throw Error("Unsupported key format");
121 | }
122 |
123 | const reader = {buf: buffer, off: 0};
124 |
125 | if (buffer.slice(0, 14).toString("ascii") !== "openssh-key-v1")
126 | throw "Invalid file format.";
127 |
128 | reader.off += 15;
129 |
130 | //ciphername
131 | if (readOpenSSHKeyString(reader).toString("ascii") !== "none")
132 | throw Error("Unsupported key type");
133 | //kdfname
134 | if (readOpenSSHKeyString(reader).toString("ascii") !== "none")
135 | throw Error("Unsupported key type");
136 | //kdf
137 | if (readOpenSSHKeyString(reader).toString("ascii") !== "")
138 | throw Error("Unsupported key type");
139 | //keynum
140 | reader.off += 4;
141 |
142 | //sshpublength
143 | reader.off += 4;
144 |
145 | //keytype
146 | if (readOpenSSHKeyString(reader).toString("ascii") !== "ssh-rsa")
147 | throw Error("Unsupported key type");
148 | readOpenSSHKeyString(reader);
149 | readOpenSSHKeyString(reader);
150 |
151 | reader.off += 12;
152 | if (readOpenSSHKeyString(reader).toString("ascii") !== "ssh-rsa")
153 | throw Error("Unsupported key type");
154 |
155 | const n = readOpenSSHKeyString(reader);
156 | const e = readOpenSSHKeyString(reader);
157 | const d = readOpenSSHKeyString(reader);
158 | const coeff = readOpenSSHKeyString(reader);
159 | const p = readOpenSSHKeyString(reader);
160 | const q = readOpenSSHKeyString(reader);
161 |
162 | //Calculate missing values
163 | const dint = new BigInteger(d);
164 | const qint = new BigInteger(q);
165 | const pint = new BigInteger(p);
166 | const dp = dint.mod(pint.subtract(BigInteger.ONE));
167 | const dq = dint.mod(qint.subtract(BigInteger.ONE));
168 |
169 | key.setPrivate(
170 | n, // modulus
171 | e, // publicExponent
172 | d, // privateExponent
173 | p, // prime1
174 | q, // prime2
175 | dp.toBuffer(), // exponent1 -- d mod (p1)
176 | dq.toBuffer(), // exponent2 -- d mod (q-1)
177 | coeff // coefficient -- (inverse of q) mod p
178 | );
179 |
180 | key.sshcomment = readOpenSSHKeyString(reader).toString("ascii");
181 | },
182 |
183 | publicExport: function (key, options) {
184 | let ebuf = Buffer.alloc(4)
185 | ebuf.writeUInt32BE(key.e, 0);
186 | //Slice leading zeroes
187 | while (ebuf[0] === 0) ebuf = ebuf.slice(1);
188 | const nbuf = key.n.toBuffer();
189 | const buf = Buffer.alloc(
190 | ebuf.byteLength + 4 +
191 | nbuf.byteLength + 4 +
192 | "ssh-rsa".length + 4
193 | );
194 |
195 | const writer = {buf: buf, off: 0};
196 | writeOpenSSHKeyString(writer, Buffer.from("ssh-rsa"));
197 | writeOpenSSHKeyString(writer, ebuf);
198 | writeOpenSSHKeyString(writer, nbuf);
199 |
200 | let comment = key.sshcomment || "";
201 |
202 | if (options.type === "der") {
203 | return writer.buf
204 | } else {
205 | return "ssh-rsa " + buf.toString("base64") + " " + comment + "\n";
206 | }
207 | },
208 |
209 | publicImport: function (key, data, options) {
210 | options = options || {};
211 | var buffer;
212 |
213 | if (options.type !== "der") {
214 | if (Buffer.isBuffer(data)) {
215 | data = data.toString("utf8");
216 | }
217 |
218 | if (_.isString(data)) {
219 | if (data.substring(0, 8) !== "ssh-rsa ")
220 | throw Error("Unsupported key format");
221 | let pemEnd = data.indexOf(" ", 8);
222 |
223 | //Handle keys with no comment
224 | if (pemEnd === -1) {
225 | pemEnd = data.length;
226 | } else {
227 | key.sshcomment = data.substring(pemEnd + 1)
228 | .replace(/\s+|\n\r|\n|\r$/gm, "");
229 | }
230 |
231 | const pem = data.substring(8, pemEnd)
232 | .replace(/\s+|\n\r|\n|\r$/gm, "");
233 | buffer = Buffer.from(pem, "base64");
234 | } else {
235 | throw Error("Unsupported key format");
236 | }
237 | } else if (Buffer.isBuffer(data)) {
238 | buffer = data;
239 | } else {
240 | throw Error("Unsupported key format");
241 | }
242 |
243 | const reader = {buf: buffer, off: 0};
244 |
245 | const type = readOpenSSHKeyString(reader).toString("ascii");
246 |
247 | if (type !== "ssh-rsa")
248 | throw Error("Invalid key type: " + type);
249 |
250 | const e = readOpenSSHKeyString(reader);
251 | const n = readOpenSSHKeyString(reader);
252 |
253 | key.setPublic(
254 | n,
255 | e
256 | );
257 | },
258 |
259 | /**
260 | * Trying autodetect and import key
261 | * @param key
262 | * @param data
263 | */
264 | autoImport: function (key, data) {
265 | // [\S\s]* matches zero or more of any character
266 | if (/^[\S\s]*-----BEGIN OPENSSH PRIVATE KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END OPENSSH PRIVATE KEY-----[\S\s]*$/g.test(data)) {
267 | module.exports.privateImport(key, data);
268 | return true;
269 | }
270 |
271 | if (/^[\S\s]*ssh-rsa \s*(?=(([A-Za-z0-9+/=]+\s*)+))\1[\S\s]*$/g.test(data)) {
272 | module.exports.publicImport(key, data);
273 | return true;
274 | }
275 |
276 | return false;
277 | }
278 | };
279 |
280 | function readOpenSSHKeyString(reader) {
281 | const len = reader.buf.readInt32BE(reader.off);
282 | reader.off += 4;
283 | const res = reader.buf.slice(reader.off, reader.off + len);
284 | reader.off += len;
285 | return res;
286 | }
287 |
288 | function writeOpenSSHKeyString(writer, data) {
289 | writer.buf.writeInt32BE(data.byteLength, writer.off);
290 | writer.off += 4;
291 | writer.off += data.copy(writer.buf, writer.off);
292 | }
--------------------------------------------------------------------------------
/src/formats/pkcs1.js:
--------------------------------------------------------------------------------
1 | var ber = require('asn1').Ber;
2 | var _ = require('../utils')._;
3 | var utils = require('../utils');
4 |
5 | const PRIVATE_OPENING_BOUNDARY = '-----BEGIN RSA PRIVATE KEY-----';
6 | const PRIVATE_CLOSING_BOUNDARY = '-----END RSA PRIVATE KEY-----';
7 |
8 | const PUBLIC_OPENING_BOUNDARY = '-----BEGIN RSA PUBLIC KEY-----';
9 | const PUBLIC_CLOSING_BOUNDARY = '-----END RSA PUBLIC KEY-----';
10 |
11 | module.exports = {
12 | privateExport: function (key, options) {
13 | options = options || {};
14 |
15 | var n = key.n.toBuffer();
16 | var d = key.d.toBuffer();
17 | var p = key.p.toBuffer();
18 | var q = key.q.toBuffer();
19 | var dmp1 = key.dmp1.toBuffer();
20 | var dmq1 = key.dmq1.toBuffer();
21 | var coeff = key.coeff.toBuffer();
22 |
23 | var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic
24 | var writer = new ber.Writer({size: length});
25 |
26 | writer.startSequence();
27 | writer.writeInt(0);
28 | writer.writeBuffer(n, 2);
29 | writer.writeInt(key.e);
30 | writer.writeBuffer(d, 2);
31 | writer.writeBuffer(p, 2);
32 | writer.writeBuffer(q, 2);
33 | writer.writeBuffer(dmp1, 2);
34 | writer.writeBuffer(dmq1, 2);
35 | writer.writeBuffer(coeff, 2);
36 | writer.endSequence();
37 |
38 | if (options.type === 'der') {
39 | return writer.buffer;
40 | } else {
41 | return PRIVATE_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PRIVATE_CLOSING_BOUNDARY;
42 | }
43 | },
44 |
45 | privateImport: function (key, data, options) {
46 | options = options || {};
47 | var buffer;
48 |
49 | if (options.type !== 'der') {
50 | if (Buffer.isBuffer(data)) {
51 | data = data.toString('utf8');
52 | }
53 |
54 | if (_.isString(data)) {
55 | var pem = utils.trimSurroundingText(data, PRIVATE_OPENING_BOUNDARY, PRIVATE_CLOSING_BOUNDARY)
56 | .replace(/\s+|\n\r|\n|\r$/gm, '');
57 | buffer = Buffer.from(pem, 'base64');
58 | } else {
59 | throw Error('Unsupported key format');
60 | }
61 | } else if (Buffer.isBuffer(data)) {
62 | buffer = data;
63 | } else {
64 | throw Error('Unsupported key format');
65 | }
66 |
67 | var reader = new ber.Reader(buffer);
68 | reader.readSequence();
69 | reader.readString(2, true); // just zero
70 | key.setPrivate(
71 | reader.readString(2, true), // modulus
72 | reader.readString(2, true), // publicExponent
73 | reader.readString(2, true), // privateExponent
74 | reader.readString(2, true), // prime1
75 | reader.readString(2, true), // prime2
76 | reader.readString(2, true), // exponent1 -- d mod (p1)
77 | reader.readString(2, true), // exponent2 -- d mod (q-1)
78 | reader.readString(2, true) // coefficient -- (inverse of q) mod p
79 | );
80 | },
81 |
82 | publicExport: function (key, options) {
83 | options = options || {};
84 |
85 | var n = key.n.toBuffer();
86 | var length = n.length + 512; // magic
87 |
88 | var bodyWriter = new ber.Writer({size: length});
89 | bodyWriter.startSequence();
90 | bodyWriter.writeBuffer(n, 2);
91 | bodyWriter.writeInt(key.e);
92 | bodyWriter.endSequence();
93 |
94 | if (options.type === 'der') {
95 | return bodyWriter.buffer;
96 | } else {
97 | return PUBLIC_OPENING_BOUNDARY + '\n' + utils.linebrk(bodyWriter.buffer.toString('base64'), 64) + '\n' + PUBLIC_CLOSING_BOUNDARY;
98 | }
99 | },
100 |
101 | publicImport: function (key, data, options) {
102 | options = options || {};
103 | var buffer;
104 |
105 | if (options.type !== 'der') {
106 | if (Buffer.isBuffer(data)) {
107 | data = data.toString('utf8');
108 | }
109 |
110 | if (_.isString(data)) {
111 | var pem = utils.trimSurroundingText(data, PUBLIC_OPENING_BOUNDARY, PUBLIC_CLOSING_BOUNDARY)
112 | .replace(/\s+|\n\r|\n|\r$/gm, '');
113 | buffer = Buffer.from(pem, 'base64');
114 | }
115 | } else if (Buffer.isBuffer(data)) {
116 | buffer = data;
117 | } else {
118 | throw Error('Unsupported key format');
119 | }
120 |
121 | var body = new ber.Reader(buffer);
122 | body.readSequence();
123 | key.setPublic(
124 | body.readString(0x02, true), // modulus
125 | body.readString(0x02, true) // publicExponent
126 | );
127 | },
128 |
129 | /**
130 | * Trying autodetect and import key
131 | * @param key
132 | * @param data
133 | */
134 | autoImport: function (key, data) {
135 | // [\S\s]* matches zero or more of any character
136 | if (/^[\S\s]*-----BEGIN RSA PRIVATE KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END RSA PRIVATE KEY-----[\S\s]*$/g.test(data)) {
137 | module.exports.privateImport(key, data);
138 | return true;
139 | }
140 |
141 | if (/^[\S\s]*-----BEGIN RSA PUBLIC KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END RSA PUBLIC KEY-----[\S\s]*$/g.test(data)) {
142 | module.exports.publicImport(key, data);
143 | return true;
144 | }
145 |
146 | return false;
147 | }
148 | };
--------------------------------------------------------------------------------
/src/formats/pkcs8.js:
--------------------------------------------------------------------------------
1 | var ber = require('asn1').Ber;
2 | var _ = require('../utils')._;
3 | var PUBLIC_RSA_OID = '1.2.840.113549.1.1.1';
4 | var utils = require('../utils');
5 |
6 | const PRIVATE_OPENING_BOUNDARY = '-----BEGIN PRIVATE KEY-----';
7 | const PRIVATE_CLOSING_BOUNDARY = '-----END PRIVATE KEY-----';
8 |
9 | const PUBLIC_OPENING_BOUNDARY = '-----BEGIN PUBLIC KEY-----';
10 | const PUBLIC_CLOSING_BOUNDARY = '-----END PUBLIC KEY-----';
11 |
12 | module.exports = {
13 | privateExport: function (key, options) {
14 | options = options || {};
15 |
16 | var n = key.n.toBuffer();
17 | var d = key.d.toBuffer();
18 | var p = key.p.toBuffer();
19 | var q = key.q.toBuffer();
20 | var dmp1 = key.dmp1.toBuffer();
21 | var dmq1 = key.dmq1.toBuffer();
22 | var coeff = key.coeff.toBuffer();
23 |
24 | var length = n.length + d.length + p.length + q.length + dmp1.length + dmq1.length + coeff.length + 512; // magic
25 | var bodyWriter = new ber.Writer({size: length});
26 |
27 | bodyWriter.startSequence();
28 | bodyWriter.writeInt(0);
29 | bodyWriter.writeBuffer(n, 2);
30 | bodyWriter.writeInt(key.e);
31 | bodyWriter.writeBuffer(d, 2);
32 | bodyWriter.writeBuffer(p, 2);
33 | bodyWriter.writeBuffer(q, 2);
34 | bodyWriter.writeBuffer(dmp1, 2);
35 | bodyWriter.writeBuffer(dmq1, 2);
36 | bodyWriter.writeBuffer(coeff, 2);
37 | bodyWriter.endSequence();
38 |
39 | var writer = new ber.Writer({size: length});
40 | writer.startSequence();
41 | writer.writeInt(0);
42 | writer.startSequence();
43 | writer.writeOID(PUBLIC_RSA_OID);
44 | writer.writeNull();
45 | writer.endSequence();
46 | writer.writeBuffer(bodyWriter.buffer, 4);
47 | writer.endSequence();
48 |
49 | if (options.type === 'der') {
50 | return writer.buffer;
51 | } else {
52 | return PRIVATE_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PRIVATE_CLOSING_BOUNDARY;
53 | }
54 | },
55 |
56 | privateImport: function (key, data, options) {
57 | options = options || {};
58 | var buffer;
59 |
60 | if (options.type !== 'der') {
61 | if (Buffer.isBuffer(data)) {
62 | data = data.toString('utf8');
63 | }
64 |
65 | if (_.isString(data)) {
66 | var pem = utils.trimSurroundingText(data, PRIVATE_OPENING_BOUNDARY, PRIVATE_CLOSING_BOUNDARY)
67 | .replace('-----END PRIVATE KEY-----', '')
68 | .replace(/\s+|\n\r|\n|\r$/gm, '');
69 | buffer = Buffer.from(pem, 'base64');
70 | } else {
71 | throw Error('Unsupported key format');
72 | }
73 | } else if (Buffer.isBuffer(data)) {
74 | buffer = data;
75 | } else {
76 | throw Error('Unsupported key format');
77 | }
78 |
79 | var reader = new ber.Reader(buffer);
80 | reader.readSequence();
81 | reader.readInt(0);
82 | var header = new ber.Reader(reader.readString(0x30, true));
83 |
84 | if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) {
85 | throw Error('Invalid Public key format');
86 | }
87 |
88 | var body = new ber.Reader(reader.readString(0x04, true));
89 | body.readSequence();
90 | body.readString(2, true); // just zero
91 | key.setPrivate(
92 | body.readString(2, true), // modulus
93 | body.readString(2, true), // publicExponent
94 | body.readString(2, true), // privateExponent
95 | body.readString(2, true), // prime1
96 | body.readString(2, true), // prime2
97 | body.readString(2, true), // exponent1 -- d mod (p1)
98 | body.readString(2, true), // exponent2 -- d mod (q-1)
99 | body.readString(2, true) // coefficient -- (inverse of q) mod p
100 | );
101 | },
102 |
103 | publicExport: function (key, options) {
104 | options = options || {};
105 |
106 | var n = key.n.toBuffer();
107 | var length = n.length + 512; // magic
108 |
109 | var bodyWriter = new ber.Writer({size: length});
110 | bodyWriter.writeByte(0);
111 | bodyWriter.startSequence();
112 | bodyWriter.writeBuffer(n, 2);
113 | bodyWriter.writeInt(key.e);
114 | bodyWriter.endSequence();
115 |
116 | var writer = new ber.Writer({size: length});
117 | writer.startSequence();
118 | writer.startSequence();
119 | writer.writeOID(PUBLIC_RSA_OID);
120 | writer.writeNull();
121 | writer.endSequence();
122 | writer.writeBuffer(bodyWriter.buffer, 3);
123 | writer.endSequence();
124 |
125 | if (options.type === 'der') {
126 | return writer.buffer;
127 | } else {
128 | return PUBLIC_OPENING_BOUNDARY + '\n' + utils.linebrk(writer.buffer.toString('base64'), 64) + '\n' + PUBLIC_CLOSING_BOUNDARY;
129 | }
130 | },
131 |
132 | publicImport: function (key, data, options) {
133 | options = options || {};
134 | var buffer;
135 |
136 | if (options.type !== 'der') {
137 | if (Buffer.isBuffer(data)) {
138 | data = data.toString('utf8');
139 | }
140 |
141 | if (_.isString(data)) {
142 | var pem = utils.trimSurroundingText(data, PUBLIC_OPENING_BOUNDARY, PUBLIC_CLOSING_BOUNDARY)
143 | .replace(/\s+|\n\r|\n|\r$/gm, '');
144 | buffer = Buffer.from(pem, 'base64');
145 | }
146 | } else if (Buffer.isBuffer(data)) {
147 | buffer = data;
148 | } else {
149 | throw Error('Unsupported key format');
150 | }
151 |
152 | var reader = new ber.Reader(buffer);
153 | reader.readSequence();
154 | var header = new ber.Reader(reader.readString(0x30, true));
155 |
156 | if (header.readOID(0x06, true) !== PUBLIC_RSA_OID) {
157 | throw Error('Invalid Public key format');
158 | }
159 |
160 | var body = new ber.Reader(reader.readString(0x03, true));
161 | body.readByte();
162 | body.readSequence();
163 | key.setPublic(
164 | body.readString(0x02, true), // modulus
165 | body.readString(0x02, true) // publicExponent
166 | );
167 | },
168 |
169 | /**
170 | * Trying autodetect and import key
171 | * @param key
172 | * @param data
173 | */
174 | autoImport: function (key, data) {
175 | if (/^[\S\s]*-----BEGIN PRIVATE KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END PRIVATE KEY-----[\S\s]*$/g.test(data)) {
176 | module.exports.privateImport(key, data);
177 | return true;
178 | }
179 |
180 | if (/^[\S\s]*-----BEGIN PUBLIC KEY-----\s*(?=(([A-Za-z0-9+/=]+\s*)+))\1-----END PUBLIC KEY-----[\S\s]*$/g.test(data)) {
181 | module.exports.publicImport(key, data);
182 | return true;
183 | }
184 |
185 | return false;
186 | }
187 | };
188 |
--------------------------------------------------------------------------------
/src/libs/jsbn.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Basic JavaScript BN library - subset useful for RSA encryption.
3 | *
4 | * Copyright (c) 2003-2005 Tom Wu
5 | * All Rights Reserved.
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining
8 | * a copy of this software and associated documentation files (the
9 | * "Software"), to deal in the Software without restriction, including
10 | * without limitation the rights to use, copy, modify, merge, publish,
11 | * distribute, sublicense, and/or sell copies of the Software, and to
12 | * permit persons to whom the Software is furnished to do so, subject to
13 | * the following conditions:
14 | *
15 | * The above copyright notice and this permission notice shall be
16 | * included in all copies or substantial portions of the Software.
17 | *
18 | * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
19 | * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
20 | * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
21 | *
22 | * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
23 | * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
24 | * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
25 | * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
26 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
27 | *
28 | * In addition, the following condition applies:
29 | *
30 | * All redistributions must retain an intact copy of this copyright notice
31 | * and disclaimer.
32 | */
33 |
34 | /*
35 | * Added Node.js Buffers support
36 | * 2014 rzcoder
37 | */
38 |
39 | var crypt = require('crypto');
40 | var _ = require('../utils')._;
41 |
42 | // Bits per digit
43 | var dbits;
44 |
45 | // JavaScript engine analysis
46 | var canary = 0xdeadbeefcafe;
47 | var j_lm = ((canary & 0xffffff) == 0xefcafe);
48 |
49 | // (public) Constructor
50 | function BigInteger(a, b) {
51 | if (a != null) {
52 | if ("number" == typeof a) {
53 | this.fromNumber(a, b);
54 | } else if (Buffer.isBuffer(a)) {
55 | this.fromBuffer(a);
56 | } else if (b == null && "string" != typeof a) {
57 | this.fromByteArray(a);
58 | } else {
59 | this.fromString(a, b);
60 | }
61 | }
62 | }
63 |
64 | // return new, unset BigInteger
65 | function nbi() {
66 | return new BigInteger(null);
67 | }
68 |
69 | // am: Compute w_j += (x*this_i), propagate carries,
70 | // c is initial carry, returns final carry.
71 | // c < 3*dvalue, x < 2*dvalue, this_i < dvalue
72 | // We need to select the fastest one that works in this environment.
73 |
74 | // am1: use a single mult and divide to get the high bits,
75 | // max digit bits should be 26 because
76 | // max internal value = 2*dvalue^2-2*dvalue (< 2^53)
77 | function am1(i, x, w, j, c, n) {
78 | while (--n >= 0) {
79 | var v = x * this[i++] + w[j] + c;
80 | c = Math.floor(v / 0x4000000);
81 | w[j++] = v & 0x3ffffff;
82 | }
83 | return c;
84 | }
85 | // am2 avoids a big mult-and-extract completely.
86 | // Max digit bits should be <= 30 because we do bitwise ops
87 | // on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
88 | function am2(i, x, w, j, c, n) {
89 | var xl = x & 0x7fff, xh = x >> 15;
90 | while (--n >= 0) {
91 | var l = this[i] & 0x7fff;
92 | var h = this[i++] >> 15;
93 | var m = xh * l + h * xl;
94 | l = xl * l + ((m & 0x7fff) << 15) + w[j] + (c & 0x3fffffff);
95 | c = (l >>> 30) + (m >>> 15) + xh * h + (c >>> 30);
96 | w[j++] = l & 0x3fffffff;
97 | }
98 | return c;
99 | }
100 | // Alternately, set max digit bits to 28 since some
101 | // browsers slow down when dealing with 32-bit numbers.
102 | function am3(i, x, w, j, c, n) {
103 | var xl = x & 0x3fff, xh = x >> 14;
104 | while (--n >= 0) {
105 | var l = this[i] & 0x3fff;
106 | var h = this[i++] >> 14;
107 | var m = xh * l + h * xl;
108 | l = xl * l + ((m & 0x3fff) << 14) + w[j] + c;
109 | c = (l >> 28) + (m >> 14) + xh * h;
110 | w[j++] = l & 0xfffffff;
111 | }
112 | return c;
113 | }
114 |
115 | // We need to select the fastest one that works in this environment.
116 | //if (j_lm && (navigator.appName == "Microsoft Internet Explorer")) {
117 | // BigInteger.prototype.am = am2;
118 | // dbits = 30;
119 | //} else if (j_lm && (navigator.appName != "Netscape")) {
120 | // BigInteger.prototype.am = am1;
121 | // dbits = 26;
122 | //} else { // Mozilla/Netscape seems to prefer am3
123 | // BigInteger.prototype.am = am3;
124 | // dbits = 28;
125 | //}
126 |
127 | // For node.js, we pick am3 with max dbits to 28.
128 | BigInteger.prototype.am = am3;
129 | dbits = 28;
130 |
131 | BigInteger.prototype.DB = dbits;
132 | BigInteger.prototype.DM = ((1 << dbits) - 1);
133 | BigInteger.prototype.DV = (1 << dbits);
134 |
135 | var BI_FP = 52;
136 | BigInteger.prototype.FV = Math.pow(2, BI_FP);
137 | BigInteger.prototype.F1 = BI_FP - dbits;
138 | BigInteger.prototype.F2 = 2 * dbits - BI_FP;
139 |
140 | // Digit conversions
141 | var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
142 | var BI_RC = new Array();
143 | var rr, vv;
144 | rr = "0".charCodeAt(0);
145 | for (vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
146 | rr = "a".charCodeAt(0);
147 | for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
148 | rr = "A".charCodeAt(0);
149 | for (vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
150 |
151 | function int2char(n) {
152 | return BI_RM.charAt(n);
153 | }
154 | function intAt(s, i) {
155 | var c = BI_RC[s.charCodeAt(i)];
156 | return (c == null) ? -1 : c;
157 | }
158 |
159 | // (protected) copy this to r
160 | function bnpCopyTo(r) {
161 | for (var i = this.t - 1; i >= 0; --i) r[i] = this[i];
162 | r.t = this.t;
163 | r.s = this.s;
164 | }
165 |
166 | // (protected) set from integer value x, -DV <= x < DV
167 | function bnpFromInt(x) {
168 | this.t = 1;
169 | this.s = (x < 0) ? -1 : 0;
170 | if (x > 0) this[0] = x;
171 | else if (x < -1) this[0] = x + DV;
172 | else this.t = 0;
173 | }
174 |
175 | // return bigint initialized to value
176 | function nbv(i) {
177 | var r = nbi();
178 | r.fromInt(i);
179 | return r;
180 | }
181 |
182 | // (protected) set from string and radix
183 | function bnpFromString(data, radix, unsigned) {
184 | var k;
185 | switch (radix) {
186 | case 2:
187 | k = 1;
188 | break;
189 | case 4:
190 | k = 2;
191 | break;
192 | case 8:
193 | k = 3;
194 | break;
195 | case 16:
196 | k = 4;
197 | break;
198 | case 32:
199 | k = 5;
200 | break;
201 | case 256:
202 | k = 8;
203 | break;
204 | default:
205 | this.fromRadix(data, radix);
206 | return;
207 | }
208 |
209 | this.t = 0;
210 | this.s = 0;
211 |
212 | var i = data.length;
213 | var mi = false;
214 | var sh = 0;
215 |
216 | while (--i >= 0) {
217 | var x = (k == 8) ? data[i] & 0xff : intAt(data, i);
218 | if (x < 0) {
219 | if (data.charAt(i) == "-") mi = true;
220 | continue;
221 | }
222 | mi = false;
223 | if (sh === 0)
224 | this[this.t++] = x;
225 | else if (sh + k > this.DB) {
226 | this[this.t - 1] |= (x & ((1 << (this.DB - sh)) - 1)) << sh;
227 | this[this.t++] = (x >> (this.DB - sh));
228 | }
229 | else
230 | this[this.t - 1] |= x << sh;
231 | sh += k;
232 | if (sh >= this.DB) sh -= this.DB;
233 | }
234 | if ((!unsigned) && k == 8 && (data[0] & 0x80) != 0) {
235 | this.s = -1;
236 | if (sh > 0) this[this.t - 1] |= ((1 << (this.DB - sh)) - 1) << sh;
237 | }
238 | this.clamp();
239 | if (mi) BigInteger.ZERO.subTo(this, this);
240 | }
241 |
242 | function bnpFromByteArray(a, unsigned) {
243 | this.fromString(a, 256, unsigned)
244 | }
245 |
246 | function bnpFromBuffer(a) {
247 | this.fromString(a, 256, true)
248 | }
249 |
250 | // (protected) clamp off excess high words
251 | function bnpClamp() {
252 | var c = this.s & this.DM;
253 | while (this.t > 0 && this[this.t - 1] == c) --this.t;
254 | }
255 |
256 | // (public) return string representation in given radix
257 | function bnToString(b) {
258 | if (this.s < 0) return "-" + this.negate().toString(b);
259 | var k;
260 | if (b == 16) k = 4;
261 | else if (b == 8) k = 3;
262 | else if (b == 2) k = 1;
263 | else if (b == 32) k = 5;
264 | else if (b == 4) k = 2;
265 | else return this.toRadix(b);
266 | var km = (1 << k) - 1, d, m = false, r = "", i = this.t;
267 | var p = this.DB - (i * this.DB) % k;
268 | if (i-- > 0) {
269 | if (p < this.DB && (d = this[i] >> p) > 0) {
270 | m = true;
271 | r = int2char(d);
272 | }
273 | while (i >= 0) {
274 | if (p < k) {
275 | d = (this[i] & ((1 << p) - 1)) << (k - p);
276 | d |= this[--i] >> (p += this.DB - k);
277 | }
278 | else {
279 | d = (this[i] >> (p -= k)) & km;
280 | if (p <= 0) {
281 | p += this.DB;
282 | --i;
283 | }
284 | }
285 | if (d > 0) m = true;
286 | if (m) r += int2char(d);
287 | }
288 | }
289 | return m ? r : "0";
290 | }
291 |
292 | // (public) -this
293 | function bnNegate() {
294 | var r = nbi();
295 | BigInteger.ZERO.subTo(this, r);
296 | return r;
297 | }
298 |
299 | // (public) |this|
300 | function bnAbs() {
301 | return (this.s < 0) ? this.negate() : this;
302 | }
303 |
304 | // (public) return + if this > a, - if this < a, 0 if equal
305 | function bnCompareTo(a) {
306 | var r = this.s - a.s;
307 | if (r != 0) return r;
308 | var i = this.t;
309 | r = i - a.t;
310 | if (r != 0) return (this.s < 0) ? -r : r;
311 | while (--i >= 0) if ((r = this[i] - a[i]) != 0) return r;
312 | return 0;
313 | }
314 |
315 | // returns bit length of the integer x
316 | function nbits(x) {
317 | var r = 1, t;
318 | if ((t = x >>> 16) != 0) {
319 | x = t;
320 | r += 16;
321 | }
322 | if ((t = x >> 8) != 0) {
323 | x = t;
324 | r += 8;
325 | }
326 | if ((t = x >> 4) != 0) {
327 | x = t;
328 | r += 4;
329 | }
330 | if ((t = x >> 2) != 0) {
331 | x = t;
332 | r += 2;
333 | }
334 | if ((t = x >> 1) != 0) {
335 | x = t;
336 | r += 1;
337 | }
338 | return r;
339 | }
340 |
341 | // (public) return the number of bits in "this"
342 | function bnBitLength() {
343 | if (this.t <= 0) return 0;
344 | return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ (this.s & this.DM));
345 | }
346 |
347 | // (protected) r = this << n*DB
348 | function bnpDLShiftTo(n, r) {
349 | var i;
350 | for (i = this.t - 1; i >= 0; --i) r[i + n] = this[i];
351 | for (i = n - 1; i >= 0; --i) r[i] = 0;
352 | r.t = this.t + n;
353 | r.s = this.s;
354 | }
355 |
356 | // (protected) r = this >> n*DB
357 | function bnpDRShiftTo(n, r) {
358 | for (var i = n; i < this.t; ++i) r[i - n] = this[i];
359 | r.t = Math.max(this.t - n, 0);
360 | r.s = this.s;
361 | }
362 |
363 | // (protected) r = this << n
364 | function bnpLShiftTo(n, r) {
365 | var bs = n % this.DB;
366 | var cbs = this.DB - bs;
367 | var bm = (1 << cbs) - 1;
368 | var ds = Math.floor(n / this.DB), c = (this.s << bs) & this.DM, i;
369 | for (i = this.t - 1; i >= 0; --i) {
370 | r[i + ds + 1] = (this[i] >> cbs) | c;
371 | c = (this[i] & bm) << bs;
372 | }
373 | for (i = ds - 1; i >= 0; --i) r[i] = 0;
374 | r[ds] = c;
375 | r.t = this.t + ds + 1;
376 | r.s = this.s;
377 | r.clamp();
378 | }
379 |
380 | // (protected) r = this >> n
381 | function bnpRShiftTo(n, r) {
382 | r.s = this.s;
383 | var ds = Math.floor(n / this.DB);
384 | if (ds >= this.t) {
385 | r.t = 0;
386 | return;
387 | }
388 | var bs = n % this.DB;
389 | var cbs = this.DB - bs;
390 | var bm = (1 << bs) - 1;
391 | r[0] = this[ds] >> bs;
392 | for (var i = ds + 1; i < this.t; ++i) {
393 | r[i - ds - 1] |= (this[i] & bm) << cbs;
394 | r[i - ds] = this[i] >> bs;
395 | }
396 | if (bs > 0) r[this.t - ds - 1] |= (this.s & bm) << cbs;
397 | r.t = this.t - ds;
398 | r.clamp();
399 | }
400 |
401 | // (protected) r = this - a
402 | function bnpSubTo(a, r) {
403 | var i = 0, c = 0, m = Math.min(a.t, this.t);
404 | while (i < m) {
405 | c += this[i] - a[i];
406 | r[i++] = c & this.DM;
407 | c >>= this.DB;
408 | }
409 | if (a.t < this.t) {
410 | c -= a.s;
411 | while (i < this.t) {
412 | c += this[i];
413 | r[i++] = c & this.DM;
414 | c >>= this.DB;
415 | }
416 | c += this.s;
417 | }
418 | else {
419 | c += this.s;
420 | while (i < a.t) {
421 | c -= a[i];
422 | r[i++] = c & this.DM;
423 | c >>= this.DB;
424 | }
425 | c -= a.s;
426 | }
427 | r.s = (c < 0) ? -1 : 0;
428 | if (c < -1) r[i++] = this.DV + c;
429 | else if (c > 0) r[i++] = c;
430 | r.t = i;
431 | r.clamp();
432 | }
433 |
434 | // (protected) r = this * a, r != this,a (HAC 14.12)
435 | // "this" should be the larger one if appropriate.
436 | function bnpMultiplyTo(a, r) {
437 | var x = this.abs(), y = a.abs();
438 | var i = x.t;
439 | r.t = i + y.t;
440 | while (--i >= 0) r[i] = 0;
441 | for (i = 0; i < y.t; ++i) r[i + x.t] = x.am(0, y[i], r, i, 0, x.t);
442 | r.s = 0;
443 | r.clamp();
444 | if (this.s != a.s) BigInteger.ZERO.subTo(r, r);
445 | }
446 |
447 | // (protected) r = this^2, r != this (HAC 14.16)
448 | function bnpSquareTo(r) {
449 | var x = this.abs();
450 | var i = r.t = 2 * x.t;
451 | while (--i >= 0) r[i] = 0;
452 | for (i = 0; i < x.t - 1; ++i) {
453 | var c = x.am(i, x[i], r, 2 * i, 0, 1);
454 | if ((r[i + x.t] += x.am(i + 1, 2 * x[i], r, 2 * i + 1, c, x.t - i - 1)) >= x.DV) {
455 | r[i + x.t] -= x.DV;
456 | r[i + x.t + 1] = 1;
457 | }
458 | }
459 | if (r.t > 0) r[r.t - 1] += x.am(i, x[i], r, 2 * i, 0, 1);
460 | r.s = 0;
461 | r.clamp();
462 | }
463 |
464 | // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
465 | // r != q, this != m. q or r may be null.
466 | function bnpDivRemTo(m, q, r) {
467 | var pm = m.abs();
468 | if (pm.t <= 0) return;
469 | var pt = this.abs();
470 | if (pt.t < pm.t) {
471 | if (q != null) q.fromInt(0);
472 | if (r != null) this.copyTo(r);
473 | return;
474 | }
475 | if (r == null) r = nbi();
476 | var y = nbi(), ts = this.s, ms = m.s;
477 | var nsh = this.DB - nbits(pm[pm.t - 1]); // normalize modulus
478 | if (nsh > 0) {
479 | pm.lShiftTo(nsh, y);
480 | pt.lShiftTo(nsh, r);
481 | }
482 | else {
483 | pm.copyTo(y);
484 | pt.copyTo(r);
485 | }
486 | var ys = y.t;
487 | var y0 = y[ys - 1];
488 | if (y0 === 0) return;
489 | var yt = y0 * (1 << this.F1) + ((ys > 1) ? y[ys - 2] >> this.F2 : 0);
490 | var d1 = this.FV / yt, d2 = (1 << this.F1) / yt, e = 1 << this.F2;
491 | var i = r.t, j = i - ys, t = (q == null) ? nbi() : q;
492 | y.dlShiftTo(j, t);
493 | if (r.compareTo(t) >= 0) {
494 | r[r.t++] = 1;
495 | r.subTo(t, r);
496 | }
497 | BigInteger.ONE.dlShiftTo(ys, t);
498 | t.subTo(y, y); // "negative" y so we can replace sub with am later
499 | while (y.t < ys) y[y.t++] = 0;
500 | while (--j >= 0) {
501 | // Estimate quotient digit
502 | var qd = (r[--i] == y0) ? this.DM : Math.floor(r[i] * d1 + (r[i - 1] + e) * d2);
503 | if ((r[i] += y.am(0, qd, r, j, 0, ys)) < qd) { // Try it out
504 | y.dlShiftTo(j, t);
505 | r.subTo(t, r);
506 | while (r[i] < --qd) r.subTo(t, r);
507 | }
508 | }
509 | if (q != null) {
510 | r.drShiftTo(ys, q);
511 | if (ts != ms) BigInteger.ZERO.subTo(q, q);
512 | }
513 | r.t = ys;
514 | r.clamp();
515 | if (nsh > 0) r.rShiftTo(nsh, r); // Denormalize remainder
516 | if (ts < 0) BigInteger.ZERO.subTo(r, r);
517 | }
518 |
519 | // (public) this mod a
520 | function bnMod(a) {
521 | var r = nbi();
522 | this.abs().divRemTo(a, null, r);
523 | if (this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r, r);
524 | return r;
525 | }
526 |
527 | // Modular reduction using "classic" algorithm
528 | function Classic(m) {
529 | this.m = m;
530 | }
531 | function cConvert(x) {
532 | if (x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
533 | else return x;
534 | }
535 | function cRevert(x) {
536 | return x;
537 | }
538 | function cReduce(x) {
539 | x.divRemTo(this.m, null, x);
540 | }
541 | function cMulTo(x, y, r) {
542 | x.multiplyTo(y, r);
543 | this.reduce(r);
544 | }
545 | function cSqrTo(x, r) {
546 | x.squareTo(r);
547 | this.reduce(r);
548 | }
549 |
550 | Classic.prototype.convert = cConvert;
551 | Classic.prototype.revert = cRevert;
552 | Classic.prototype.reduce = cReduce;
553 | Classic.prototype.mulTo = cMulTo;
554 | Classic.prototype.sqrTo = cSqrTo;
555 |
556 | // (protected) return "-1/this % 2^DB"; useful for Mont. reduction
557 | // justification:
558 | // xy == 1 (mod m)
559 | // xy = 1+km
560 | // xy(2-xy) = (1+km)(1-km)
561 | // x[y(2-xy)] = 1-k^2m^2
562 | // x[y(2-xy)] == 1 (mod m^2)
563 | // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
564 | // should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
565 | // JS multiply "overflows" differently from C/C++, so care is needed here.
566 | function bnpInvDigit() {
567 | if (this.t < 1) return 0;
568 | var x = this[0];
569 | if ((x & 1) === 0) return 0;
570 | var y = x & 3; // y == 1/x mod 2^2
571 | y = (y * (2 - (x & 0xf) * y)) & 0xf; // y == 1/x mod 2^4
572 | y = (y * (2 - (x & 0xff) * y)) & 0xff; // y == 1/x mod 2^8
573 | y = (y * (2 - (((x & 0xffff) * y) & 0xffff))) & 0xffff; // y == 1/x mod 2^16
574 | // last step - calculate inverse mod DV directly;
575 | // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
576 | y = (y * (2 - x * y % this.DV)) % this.DV; // y == 1/x mod 2^dbits
577 | // we really want the negative inverse, and -DV < y < DV
578 | return (y > 0) ? this.DV - y : -y;
579 | }
580 |
581 | // Montgomery reduction
582 | function Montgomery(m) {
583 | this.m = m;
584 | this.mp = m.invDigit();
585 | this.mpl = this.mp & 0x7fff;
586 | this.mph = this.mp >> 15;
587 | this.um = (1 << (m.DB - 15)) - 1;
588 | this.mt2 = 2 * m.t;
589 | }
590 |
591 | // xR mod m
592 | function montConvert(x) {
593 | var r = nbi();
594 | x.abs().dlShiftTo(this.m.t, r);
595 | r.divRemTo(this.m, null, r);
596 | if (x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r, r);
597 | return r;
598 | }
599 |
600 | // x/R mod m
601 | function montRevert(x) {
602 | var r = nbi();
603 | x.copyTo(r);
604 | this.reduce(r);
605 | return r;
606 | }
607 |
608 | // x = x/R mod m (HAC 14.32)
609 | function montReduce(x) {
610 | while (x.t <= this.mt2) // pad x so am has enough room later
611 | x[x.t++] = 0;
612 | for (var i = 0; i < this.m.t; ++i) {
613 | // faster way of calculating u0 = x[i]*mp mod DV
614 | var j = x[i] & 0x7fff;
615 | var u0 = (j * this.mpl + (((j * this.mph + (x[i] >> 15) * this.mpl) & this.um) << 15)) & x.DM;
616 | // use am to combine the multiply-shift-add into one call
617 | j = i + this.m.t;
618 | x[j] += this.m.am(0, u0, x, i, 0, this.m.t);
619 | // propagate carry
620 | while (x[j] >= x.DV) {
621 | x[j] -= x.DV;
622 | x[++j]++;
623 | }
624 | }
625 | x.clamp();
626 | x.drShiftTo(this.m.t, x);
627 | if (x.compareTo(this.m) >= 0) x.subTo(this.m, x);
628 | }
629 |
630 | // r = "x^2/R mod m"; x != r
631 | function montSqrTo(x, r) {
632 | x.squareTo(r);
633 | this.reduce(r);
634 | }
635 |
636 | // r = "xy/R mod m"; x,y != r
637 | function montMulTo(x, y, r) {
638 | x.multiplyTo(y, r);
639 | this.reduce(r);
640 | }
641 |
642 | Montgomery.prototype.convert = montConvert;
643 | Montgomery.prototype.revert = montRevert;
644 | Montgomery.prototype.reduce = montReduce;
645 | Montgomery.prototype.mulTo = montMulTo;
646 | Montgomery.prototype.sqrTo = montSqrTo;
647 |
648 | // (protected) true iff this is even
649 | function bnpIsEven() {
650 | return ((this.t > 0) ? (this[0] & 1) : this.s) === 0;
651 | }
652 |
653 | // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
654 | function bnpExp(e, z) {
655 | if (e > 0xffffffff || e < 1) return BigInteger.ONE;
656 | var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e) - 1;
657 | g.copyTo(r);
658 | while (--i >= 0) {
659 | z.sqrTo(r, r2);
660 | if ((e & (1 << i)) > 0) z.mulTo(r2, g, r);
661 | else {
662 | var t = r;
663 | r = r2;
664 | r2 = t;
665 | }
666 | }
667 | return z.revert(r);
668 | }
669 |
670 | // (public) this^e % m, 0 <= e < 2^32
671 | function bnModPowInt(e, m) {
672 | var z;
673 | if (e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
674 | return this.exp(e, z);
675 | }
676 |
677 | // Copyright (c) 2005-2009 Tom Wu
678 | // All Rights Reserved.
679 | // See "LICENSE" for details.
680 |
681 | // Extended JavaScript BN functions, required for RSA private ops.
682 |
683 | // Version 1.1: new BigInteger("0", 10) returns "proper" zero
684 | // Version 1.2: square() API, isProbablePrime fix
685 |
686 | //(public)
687 | function bnClone() {
688 | var r = nbi();
689 | this.copyTo(r);
690 | return r;
691 | }
692 |
693 | //(public) return value as integer
694 | function bnIntValue() {
695 | if (this.s < 0) {
696 | if (this.t == 1) return this[0] - this.DV;
697 | else if (this.t === 0) return -1;
698 | }
699 | else if (this.t == 1) return this[0];
700 | else if (this.t === 0) return 0;
701 | // assumes 16 < DB < 32
702 | return ((this[1] & ((1 << (32 - this.DB)) - 1)) << this.DB) | this[0];
703 | }
704 |
705 | //(public) return value as byte
706 | function bnByteValue() {
707 | return (this.t == 0) ? this.s : (this[0] << 24) >> 24;
708 | }
709 |
710 | //(public) return value as short (assumes DB>=16)
711 | function bnShortValue() {
712 | return (this.t == 0) ? this.s : (this[0] << 16) >> 16;
713 | }
714 |
715 | //(protected) return x s.t. r^x < DV
716 | function bnpChunkSize(r) {
717 | return Math.floor(Math.LN2 * this.DB / Math.log(r));
718 | }
719 |
720 | //(public) 0 if this === 0, 1 if this > 0
721 | function bnSigNum() {
722 | if (this.s < 0) return -1;
723 | else if (this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0;
724 | else return 1;
725 | }
726 |
727 | //(protected) convert to radix string
728 | function bnpToRadix(b) {
729 | if (b == null) b = 10;
730 | if (this.signum() === 0 || b < 2 || b > 36) return "0";
731 | var cs = this.chunkSize(b);
732 | var a = Math.pow(b, cs);
733 | var d = nbv(a), y = nbi(), z = nbi(), r = "";
734 | this.divRemTo(d, y, z);
735 | while (y.signum() > 0) {
736 | r = (a + z.intValue()).toString(b).substr(1) + r;
737 | y.divRemTo(d, y, z);
738 | }
739 | return z.intValue().toString(b) + r;
740 | }
741 |
742 | //(protected) convert from radix string
743 | function bnpFromRadix(s, b) {
744 | this.fromInt(0);
745 | if (b == null) b = 10;
746 | var cs = this.chunkSize(b);
747 | var d = Math.pow(b, cs), mi = false, j = 0, w = 0;
748 | for (var i = 0; i < s.length; ++i) {
749 | var x = intAt(s, i);
750 | if (x < 0) {
751 | if (s.charAt(i) == "-" && this.signum() === 0) mi = true;
752 | continue;
753 | }
754 | w = b * w + x;
755 | if (++j >= cs) {
756 | this.dMultiply(d);
757 | this.dAddOffset(w, 0);
758 | j = 0;
759 | w = 0;
760 | }
761 | }
762 | if (j > 0) {
763 | this.dMultiply(Math.pow(b, j));
764 | this.dAddOffset(w, 0);
765 | }
766 | if (mi) BigInteger.ZERO.subTo(this, this);
767 | }
768 |
769 | //(protected) alternate constructor
770 | function bnpFromNumber(a, b) {
771 | if ("number" == typeof b) {
772 | // new BigInteger(int,int,RNG)
773 | if (a < 2) this.fromInt(1);
774 | else {
775 | this.fromNumber(a);
776 | if (!this.testBit(a - 1)) // force MSB set
777 | this.bitwiseTo(BigInteger.ONE.shiftLeft(a - 1), op_or, this);
778 | if (this.isEven()) this.dAddOffset(1, 0); // force odd
779 | while (!this.isProbablePrime(b)) {
780 | this.dAddOffset(2, 0);
781 | if (this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a - 1), this);
782 | }
783 | }
784 | } else {
785 | // new BigInteger(int,RNG)
786 | var x = crypt.randomBytes((a >> 3) + 1)
787 | var t = a & 7;
788 |
789 | if (t > 0)
790 | x[0] &= ((1 << t) - 1);
791 | else
792 | x[0] = 0;
793 |
794 | this.fromByteArray(x);
795 | }
796 | }
797 |
798 | //(public) convert to bigendian byte array
799 | function bnToByteArray() {
800 | var i = this.t, r = new Array();
801 | r[0] = this.s;
802 | var p = this.DB - (i * this.DB) % 8, d, k = 0;
803 | if (i-- > 0) {
804 | if (p < this.DB && (d = this[i] >> p) != (this.s & this.DM) >> p)
805 | r[k++] = d | (this.s << (this.DB - p));
806 | while (i >= 0) {
807 | if (p < 8) {
808 | d = (this[i] & ((1 << p) - 1)) << (8 - p);
809 | d |= this[--i] >> (p += this.DB - 8);
810 | }
811 | else {
812 | d = (this[i] >> (p -= 8)) & 0xff;
813 | if (p <= 0) {
814 | p += this.DB;
815 | --i;
816 | }
817 | }
818 | if ((d & 0x80) != 0) d |= -256;
819 | if (k === 0 && (this.s & 0x80) != (d & 0x80)) ++k;
820 | if (k > 0 || d != this.s) r[k++] = d;
821 | }
822 | }
823 | return r;
824 | }
825 |
826 | /**
827 | * return Buffer object
828 | * @param trim {boolean} slice buffer if first element == 0
829 | * @returns {Buffer}
830 | */
831 | function bnToBuffer(trimOrSize) {
832 | var res = Buffer.from(this.toByteArray());
833 | if (trimOrSize === true && res[0] === 0) {
834 | res = res.slice(1);
835 | } else if (_.isNumber(trimOrSize)) {
836 | if (res.length > trimOrSize) {
837 | for (var i = 0; i < res.length - trimOrSize; i++) {
838 | if (res[i] !== 0) {
839 | return null;
840 | }
841 | }
842 | return res.slice(res.length - trimOrSize);
843 | } else if (res.length < trimOrSize) {
844 | var padded = Buffer.alloc(trimOrSize);
845 | padded.fill(0, 0, trimOrSize - res.length);
846 | res.copy(padded, trimOrSize - res.length);
847 | return padded;
848 | }
849 | }
850 | return res;
851 | }
852 |
853 | function bnEquals(a) {
854 | return (this.compareTo(a) == 0);
855 | }
856 | function bnMin(a) {
857 | return (this.compareTo(a) < 0) ? this : a;
858 | }
859 | function bnMax(a) {
860 | return (this.compareTo(a) > 0) ? this : a;
861 | }
862 |
863 | //(protected) r = this op a (bitwise)
864 | function bnpBitwiseTo(a, op, r) {
865 | var i, f, m = Math.min(a.t, this.t);
866 | for (i = 0; i < m; ++i) r[i] = op(this[i], a[i]);
867 | if (a.t < this.t) {
868 | f = a.s & this.DM;
869 | for (i = m; i < this.t; ++i) r[i] = op(this[i], f);
870 | r.t = this.t;
871 | }
872 | else {
873 | f = this.s & this.DM;
874 | for (i = m; i < a.t; ++i) r[i] = op(f, a[i]);
875 | r.t = a.t;
876 | }
877 | r.s = op(this.s, a.s);
878 | r.clamp();
879 | }
880 |
881 | //(public) this & a
882 | function op_and(x, y) {
883 | return x & y;
884 | }
885 | function bnAnd(a) {
886 | var r = nbi();
887 | this.bitwiseTo(a, op_and, r);
888 | return r;
889 | }
890 |
891 | //(public) this | a
892 | function op_or(x, y) {
893 | return x | y;
894 | }
895 | function bnOr(a) {
896 | var r = nbi();
897 | this.bitwiseTo(a, op_or, r);
898 | return r;
899 | }
900 |
901 | //(public) this ^ a
902 | function op_xor(x, y) {
903 | return x ^ y;
904 | }
905 | function bnXor(a) {
906 | var r = nbi();
907 | this.bitwiseTo(a, op_xor, r);
908 | return r;
909 | }
910 |
911 | //(public) this & ~a
912 | function op_andnot(x, y) {
913 | return x & ~y;
914 | }
915 | function bnAndNot(a) {
916 | var r = nbi();
917 | this.bitwiseTo(a, op_andnot, r);
918 | return r;
919 | }
920 |
921 | //(public) ~this
922 | function bnNot() {
923 | var r = nbi();
924 | for (var i = 0; i < this.t; ++i) r[i] = this.DM & ~this[i];
925 | r.t = this.t;
926 | r.s = ~this.s;
927 | return r;
928 | }
929 |
930 | //(public) this << n
931 | function bnShiftLeft(n) {
932 | var r = nbi();
933 | if (n < 0) this.rShiftTo(-n, r); else this.lShiftTo(n, r);
934 | return r;
935 | }
936 |
937 | //(public) this >> n
938 | function bnShiftRight(n) {
939 | var r = nbi();
940 | if (n < 0) this.lShiftTo(-n, r); else this.rShiftTo(n, r);
941 | return r;
942 | }
943 |
944 | //return index of lowest 1-bit in x, x < 2^31
945 | function lbit(x) {
946 | if (x === 0) return -1;
947 | var r = 0;
948 | if ((x & 0xffff) === 0) {
949 | x >>= 16;
950 | r += 16;
951 | }
952 | if ((x & 0xff) === 0) {
953 | x >>= 8;
954 | r += 8;
955 | }
956 | if ((x & 0xf) === 0) {
957 | x >>= 4;
958 | r += 4;
959 | }
960 | if ((x & 3) === 0) {
961 | x >>= 2;
962 | r += 2;
963 | }
964 | if ((x & 1) === 0) ++r;
965 | return r;
966 | }
967 |
968 | //(public) returns index of lowest 1-bit (or -1 if none)
969 | function bnGetLowestSetBit() {
970 | for (var i = 0; i < this.t; ++i)
971 | if (this[i] != 0) return i * this.DB + lbit(this[i]);
972 | if (this.s < 0) return this.t * this.DB;
973 | return -1;
974 | }
975 |
976 | //return number of 1 bits in x
977 | function cbit(x) {
978 | var r = 0;
979 | while (x != 0) {
980 | x &= x - 1;
981 | ++r;
982 | }
983 | return r;
984 | }
985 |
986 | //(public) return number of set bits
987 | function bnBitCount() {
988 | var r = 0, x = this.s & this.DM;
989 | for (var i = 0; i < this.t; ++i) r += cbit(this[i] ^ x);
990 | return r;
991 | }
992 |
993 | //(public) true iff nth bit is set
994 | function bnTestBit(n) {
995 | var j = Math.floor(n / this.DB);
996 | if (j >= this.t) return (this.s != 0);
997 | return ((this[j] & (1 << (n % this.DB))) != 0);
998 | }
999 |
1000 | //(protected) this op (1<>= this.DB;
1029 | }
1030 | if (a.t < this.t) {
1031 | c += a.s;
1032 | while (i < this.t) {
1033 | c += this[i];
1034 | r[i++] = c & this.DM;
1035 | c >>= this.DB;
1036 | }
1037 | c += this.s;
1038 | }
1039 | else {
1040 | c += this.s;
1041 | while (i < a.t) {
1042 | c += a[i];
1043 | r[i++] = c & this.DM;
1044 | c >>= this.DB;
1045 | }
1046 | c += a.s;
1047 | }
1048 | r.s = (c < 0) ? -1 : 0;
1049 | if (c > 0) r[i++] = c;
1050 | else if (c < -1) r[i++] = this.DV + c;
1051 | r.t = i;
1052 | r.clamp();
1053 | }
1054 |
1055 | //(public) this + a
1056 | function bnAdd(a) {
1057 | var r = nbi();
1058 | this.addTo(a, r);
1059 | return r;
1060 | }
1061 |
1062 | //(public) this - a
1063 | function bnSubtract(a) {
1064 | var r = nbi();
1065 | this.subTo(a, r);
1066 | return r;
1067 | }
1068 |
1069 | //(public) this * a
1070 | function bnMultiply(a) {
1071 | var r = nbi();
1072 | this.multiplyTo(a, r);
1073 | return r;
1074 | }
1075 |
1076 | // (public) this^2
1077 | function bnSquare() {
1078 | var r = nbi();
1079 | this.squareTo(r);
1080 | return r;
1081 | }
1082 |
1083 | //(public) this / a
1084 | function bnDivide(a) {
1085 | var r = nbi();
1086 | this.divRemTo(a, r, null);
1087 | return r;
1088 | }
1089 |
1090 | //(public) this % a
1091 | function bnRemainder(a) {
1092 | var r = nbi();
1093 | this.divRemTo(a, null, r);
1094 | return r;
1095 | }
1096 |
1097 | //(public) [this/a,this%a]
1098 | function bnDivideAndRemainder(a) {
1099 | var q = nbi(), r = nbi();
1100 | this.divRemTo(a, q, r);
1101 | return new Array(q, r);
1102 | }
1103 |
1104 | //(protected) this *= n, this >= 0, 1 < n < DV
1105 | function bnpDMultiply(n) {
1106 | this[this.t] = this.am(0, n - 1, this, 0, 0, this.t);
1107 | ++this.t;
1108 | this.clamp();
1109 | }
1110 |
1111 | //(protected) this += n << w words, this >= 0
1112 | function bnpDAddOffset(n, w) {
1113 | if (n === 0) return;
1114 | while (this.t <= w) this[this.t++] = 0;
1115 | this[w] += n;
1116 | while (this[w] >= this.DV) {
1117 | this[w] -= this.DV;
1118 | if (++w >= this.t) this[this.t++] = 0;
1119 | ++this[w];
1120 | }
1121 | }
1122 |
1123 | //A "null" reducer
1124 | function NullExp() {
1125 | }
1126 | function nNop(x) {
1127 | return x;
1128 | }
1129 | function nMulTo(x, y, r) {
1130 | x.multiplyTo(y, r);
1131 | }
1132 | function nSqrTo(x, r) {
1133 | x.squareTo(r);
1134 | }
1135 |
1136 | NullExp.prototype.convert = nNop;
1137 | NullExp.prototype.revert = nNop;
1138 | NullExp.prototype.mulTo = nMulTo;
1139 | NullExp.prototype.sqrTo = nSqrTo;
1140 |
1141 | //(public) this^e
1142 | function bnPow(e) {
1143 | return this.exp(e, new NullExp());
1144 | }
1145 |
1146 | //(protected) r = lower n words of "this * a", a.t <= n
1147 | //"this" should be the larger one if appropriate.
1148 | function bnpMultiplyLowerTo(a, n, r) {
1149 | var i = Math.min(this.t + a.t, n);
1150 | r.s = 0; // assumes a,this >= 0
1151 | r.t = i;
1152 | while (i > 0) r[--i] = 0;
1153 | var j;
1154 | for (j = r.t - this.t; i < j; ++i) r[i + this.t] = this.am(0, a[i], r, i, 0, this.t);
1155 | for (j = Math.min(a.t, n); i < j; ++i) this.am(0, a[i], r, i, 0, n - i);
1156 | r.clamp();
1157 | }
1158 |
1159 | //(protected) r = "this * a" without lower n words, n > 0
1160 | //"this" should be the larger one if appropriate.
1161 | function bnpMultiplyUpperTo(a, n, r) {
1162 | --n;
1163 | var i = r.t = this.t + a.t - n;
1164 | r.s = 0; // assumes a,this >= 0
1165 | while (--i >= 0) r[i] = 0;
1166 | for (i = Math.max(n - this.t, 0); i < a.t; ++i)
1167 | r[this.t + i - n] = this.am(n - i, a[i], r, 0, 0, this.t + i - n);
1168 | r.clamp();
1169 | r.drShiftTo(1, r);
1170 | }
1171 |
1172 | //Barrett modular reduction
1173 | function Barrett(m) {
1174 | // setup Barrett
1175 | this.r2 = nbi();
1176 | this.q3 = nbi();
1177 | BigInteger.ONE.dlShiftTo(2 * m.t, this.r2);
1178 | this.mu = this.r2.divide(m);
1179 | this.m = m;
1180 | }
1181 |
1182 | function barrettConvert(x) {
1183 | if (x.s < 0 || x.t > 2 * this.m.t) return x.mod(this.m);
1184 | else if (x.compareTo(this.m) < 0) return x;
1185 | else {
1186 | var r = nbi();
1187 | x.copyTo(r);
1188 | this.reduce(r);
1189 | return r;
1190 | }
1191 | }
1192 |
1193 | function barrettRevert(x) {
1194 | return x;
1195 | }
1196 |
1197 | //x = x mod m (HAC 14.42)
1198 | function barrettReduce(x) {
1199 | x.drShiftTo(this.m.t - 1, this.r2);
1200 | if (x.t > this.m.t + 1) {
1201 | x.t = this.m.t + 1;
1202 | x.clamp();
1203 | }
1204 | this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3);
1205 | this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2);
1206 | while (x.compareTo(this.r2) < 0) x.dAddOffset(1, this.m.t + 1);
1207 | x.subTo(this.r2, x);
1208 | while (x.compareTo(this.m) >= 0) x.subTo(this.m, x);
1209 | }
1210 |
1211 | //r = x^2 mod m; x != r
1212 | function barrettSqrTo(x, r) {
1213 | x.squareTo(r);
1214 | this.reduce(r);
1215 | }
1216 |
1217 | //r = x*y mod m; x,y != r
1218 | function barrettMulTo(x, y, r) {
1219 | x.multiplyTo(y, r);
1220 | this.reduce(r);
1221 | }
1222 |
1223 | Barrett.prototype.convert = barrettConvert;
1224 | Barrett.prototype.revert = barrettRevert;
1225 | Barrett.prototype.reduce = barrettReduce;
1226 | Barrett.prototype.mulTo = barrettMulTo;
1227 | Barrett.prototype.sqrTo = barrettSqrTo;
1228 |
1229 | //(public) this^e % m (HAC 14.85)
1230 | function bnModPow(e, m) {
1231 | var i = e.bitLength(), k, r = nbv(1), z;
1232 | if (i <= 0) return r;
1233 | else if (i < 18) k = 1;
1234 | else if (i < 48) k = 3;
1235 | else if (i < 144) k = 4;
1236 | else if (i < 768) k = 5;
1237 | else k = 6;
1238 | if (i < 8)
1239 | z = new Classic(m);
1240 | else if (m.isEven())
1241 | z = new Barrett(m);
1242 | else
1243 | z = new Montgomery(m);
1244 |
1245 | // precomputation
1246 | var g = new Array(), n = 3, k1 = k - 1, km = (1 << k) - 1;
1247 | g[1] = z.convert(this);
1248 | if (k > 1) {
1249 | var g2 = nbi();
1250 | z.sqrTo(g[1], g2);
1251 | while (n <= km) {
1252 | g[n] = nbi();
1253 | z.mulTo(g2, g[n - 2], g[n]);
1254 | n += 2;
1255 | }
1256 | }
1257 |
1258 | var j = e.t - 1, w, is1 = true, r2 = nbi(), t;
1259 | i = nbits(e[j]) - 1;
1260 | while (j >= 0) {
1261 | if (i >= k1) w = (e[j] >> (i - k1)) & km;
1262 | else {
1263 | w = (e[j] & ((1 << (i + 1)) - 1)) << (k1 - i);
1264 | if (j > 0) w |= e[j - 1] >> (this.DB + i - k1);
1265 | }
1266 |
1267 | n = k;
1268 | while ((w & 1) === 0) {
1269 | w >>= 1;
1270 | --n;
1271 | }
1272 | if ((i -= n) < 0) {
1273 | i += this.DB;
1274 | --j;
1275 | }
1276 | if (is1) { // ret == 1, don't bother squaring or multiplying it
1277 | g[w].copyTo(r);
1278 | is1 = false;
1279 | }
1280 | else {
1281 | while (n > 1) {
1282 | z.sqrTo(r, r2);
1283 | z.sqrTo(r2, r);
1284 | n -= 2;
1285 | }
1286 | if (n > 0) z.sqrTo(r, r2); else {
1287 | t = r;
1288 | r = r2;
1289 | r2 = t;
1290 | }
1291 | z.mulTo(r2, g[w], r);
1292 | }
1293 |
1294 | while (j >= 0 && (e[j] & (1 << i)) === 0) {
1295 | z.sqrTo(r, r2);
1296 | t = r;
1297 | r = r2;
1298 | r2 = t;
1299 | if (--i < 0) {
1300 | i = this.DB - 1;
1301 | --j;
1302 | }
1303 | }
1304 | }
1305 | return z.revert(r);
1306 | }
1307 |
1308 | //(public) gcd(this,a) (HAC 14.54)
1309 | function bnGCD(a) {
1310 | var x = (this.s < 0) ? this.negate() : this.clone();
1311 | var y = (a.s < 0) ? a.negate() : a.clone();
1312 | if (x.compareTo(y) < 0) {
1313 | var t = x;
1314 | x = y;
1315 | y = t;
1316 | }
1317 | var i = x.getLowestSetBit(), g = y.getLowestSetBit();
1318 | if (g < 0) return x;
1319 | if (i < g) g = i;
1320 | if (g > 0) {
1321 | x.rShiftTo(g, x);
1322 | y.rShiftTo(g, y);
1323 | }
1324 | while (x.signum() > 0) {
1325 | if ((i = x.getLowestSetBit()) > 0) x.rShiftTo(i, x);
1326 | if ((i = y.getLowestSetBit()) > 0) y.rShiftTo(i, y);
1327 | if (x.compareTo(y) >= 0) {
1328 | x.subTo(y, x);
1329 | x.rShiftTo(1, x);
1330 | }
1331 | else {
1332 | y.subTo(x, y);
1333 | y.rShiftTo(1, y);
1334 | }
1335 | }
1336 | if (g > 0) y.lShiftTo(g, y);
1337 | return y;
1338 | }
1339 |
1340 | //(protected) this % n, n < 2^26
1341 | function bnpModInt(n) {
1342 | if (n <= 0) return 0;
1343 | var d = this.DV % n, r = (this.s < 0) ? n - 1 : 0;
1344 | if (this.t > 0)
1345 | if (d === 0) r = this[0] % n;
1346 | else for (var i = this.t - 1; i >= 0; --i) r = (d * r + this[i]) % n;
1347 | return r;
1348 | }
1349 |
1350 | //(public) 1/this % m (HAC 14.61)
1351 | function bnModInverse(m) {
1352 | var ac = m.isEven();
1353 | if ((this.isEven() && ac) || m.signum() === 0) return BigInteger.ZERO;
1354 | var u = m.clone(), v = this.clone();
1355 | var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1);
1356 | while (u.signum() != 0) {
1357 | while (u.isEven()) {
1358 | u.rShiftTo(1, u);
1359 | if (ac) {
1360 | if (!a.isEven() || !b.isEven()) {
1361 | a.addTo(this, a);
1362 | b.subTo(m, b);
1363 | }
1364 | a.rShiftTo(1, a);
1365 | }
1366 | else if (!b.isEven()) b.subTo(m, b);
1367 | b.rShiftTo(1, b);
1368 | }
1369 | while (v.isEven()) {
1370 | v.rShiftTo(1, v);
1371 | if (ac) {
1372 | if (!c.isEven() || !d.isEven()) {
1373 | c.addTo(this, c);
1374 | d.subTo(m, d);
1375 | }
1376 | c.rShiftTo(1, c);
1377 | }
1378 | else if (!d.isEven()) d.subTo(m, d);
1379 | d.rShiftTo(1, d);
1380 | }
1381 | if (u.compareTo(v) >= 0) {
1382 | u.subTo(v, u);
1383 | if (ac) a.subTo(c, a);
1384 | b.subTo(d, b);
1385 | }
1386 | else {
1387 | v.subTo(u, v);
1388 | if (ac) c.subTo(a, c);
1389 | d.subTo(b, d);
1390 | }
1391 | }
1392 | if (v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO;
1393 | if (d.compareTo(m) >= 0) return d.subtract(m);
1394 | if (d.signum() < 0) d.addTo(m, d); else return d;
1395 | if (d.signum() < 0) return d.add(m); else return d;
1396 | }
1397 |
1398 | var lowprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997];
1399 | var lplim = (1 << 26) / lowprimes[lowprimes.length - 1];
1400 |
1401 | //(public) test primality with certainty >= 1-.5^t
1402 | function bnIsProbablePrime(t) {
1403 | var i, x = this.abs();
1404 | if (x.t == 1 && x[0] <= lowprimes[lowprimes.length - 1]) {
1405 | for (i = 0; i < lowprimes.length; ++i)
1406 | if (x[0] == lowprimes[i]) return true;
1407 | return false;
1408 | }
1409 | if (x.isEven()) return false;
1410 | i = 1;
1411 | while (i < lowprimes.length) {
1412 | var m = lowprimes[i], j = i + 1;
1413 | while (j < lowprimes.length && m < lplim) m *= lowprimes[j++];
1414 | m = x.modInt(m);
1415 | while (i < j) if (m % lowprimes[i++] === 0) return false;
1416 | }
1417 | return x.millerRabin(t);
1418 | }
1419 |
1420 | //(protected) true if probably prime (HAC 4.24, Miller-Rabin)
1421 | function bnpMillerRabin(t) {
1422 | var n1 = this.subtract(BigInteger.ONE);
1423 | var k = n1.getLowestSetBit();
1424 | if (k <= 0) return false;
1425 | var r = n1.shiftRight(k);
1426 | t = (t + 1) >> 1;
1427 | if (t > lowprimes.length) t = lowprimes.length;
1428 | var a = nbi();
1429 | for (var i = 0; i < t; ++i) {
1430 | //Pick bases at random, instead of starting at 2
1431 | a.fromInt(lowprimes[Math.floor(Math.random() * lowprimes.length)]);
1432 | var y = a.modPow(r, this);
1433 | if (y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {
1434 | var j = 1;
1435 | while (j++ < k && y.compareTo(n1) != 0) {
1436 | y = y.modPowInt(2, this);
1437 | if (y.compareTo(BigInteger.ONE) === 0) return false;
1438 | }
1439 | if (y.compareTo(n1) != 0) return false;
1440 | }
1441 | }
1442 | return true;
1443 | }
1444 |
1445 | // protected
1446 | BigInteger.prototype.copyTo = bnpCopyTo;
1447 | BigInteger.prototype.fromInt = bnpFromInt;
1448 | BigInteger.prototype.fromString = bnpFromString;
1449 | BigInteger.prototype.fromByteArray = bnpFromByteArray;
1450 | BigInteger.prototype.fromBuffer = bnpFromBuffer;
1451 | BigInteger.prototype.clamp = bnpClamp;
1452 | BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
1453 | BigInteger.prototype.drShiftTo = bnpDRShiftTo;
1454 | BigInteger.prototype.lShiftTo = bnpLShiftTo;
1455 | BigInteger.prototype.rShiftTo = bnpRShiftTo;
1456 | BigInteger.prototype.subTo = bnpSubTo;
1457 | BigInteger.prototype.multiplyTo = bnpMultiplyTo;
1458 | BigInteger.prototype.squareTo = bnpSquareTo;
1459 | BigInteger.prototype.divRemTo = bnpDivRemTo;
1460 | BigInteger.prototype.invDigit = bnpInvDigit;
1461 | BigInteger.prototype.isEven = bnpIsEven;
1462 | BigInteger.prototype.exp = bnpExp;
1463 |
1464 | BigInteger.prototype.chunkSize = bnpChunkSize;
1465 | BigInteger.prototype.toRadix = bnpToRadix;
1466 | BigInteger.prototype.fromRadix = bnpFromRadix;
1467 | BigInteger.prototype.fromNumber = bnpFromNumber;
1468 | BigInteger.prototype.bitwiseTo = bnpBitwiseTo;
1469 | BigInteger.prototype.changeBit = bnpChangeBit;
1470 | BigInteger.prototype.addTo = bnpAddTo;
1471 | BigInteger.prototype.dMultiply = bnpDMultiply;
1472 | BigInteger.prototype.dAddOffset = bnpDAddOffset;
1473 | BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;
1474 | BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;
1475 | BigInteger.prototype.modInt = bnpModInt;
1476 | BigInteger.prototype.millerRabin = bnpMillerRabin;
1477 |
1478 |
1479 | // public
1480 | BigInteger.prototype.toString = bnToString;
1481 | BigInteger.prototype.negate = bnNegate;
1482 | BigInteger.prototype.abs = bnAbs;
1483 | BigInteger.prototype.compareTo = bnCompareTo;
1484 | BigInteger.prototype.bitLength = bnBitLength;
1485 | BigInteger.prototype.mod = bnMod;
1486 | BigInteger.prototype.modPowInt = bnModPowInt;
1487 |
1488 | BigInteger.prototype.clone = bnClone;
1489 | BigInteger.prototype.intValue = bnIntValue;
1490 | BigInteger.prototype.byteValue = bnByteValue;
1491 | BigInteger.prototype.shortValue = bnShortValue;
1492 | BigInteger.prototype.signum = bnSigNum;
1493 | BigInteger.prototype.toByteArray = bnToByteArray;
1494 | BigInteger.prototype.toBuffer = bnToBuffer;
1495 | BigInteger.prototype.equals = bnEquals;
1496 | BigInteger.prototype.min = bnMin;
1497 | BigInteger.prototype.max = bnMax;
1498 | BigInteger.prototype.and = bnAnd;
1499 | BigInteger.prototype.or = bnOr;
1500 | BigInteger.prototype.xor = bnXor;
1501 | BigInteger.prototype.andNot = bnAndNot;
1502 | BigInteger.prototype.not = bnNot;
1503 | BigInteger.prototype.shiftLeft = bnShiftLeft;
1504 | BigInteger.prototype.shiftRight = bnShiftRight;
1505 | BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;
1506 | BigInteger.prototype.bitCount = bnBitCount;
1507 | BigInteger.prototype.testBit = bnTestBit;
1508 | BigInteger.prototype.setBit = bnSetBit;
1509 | BigInteger.prototype.clearBit = bnClearBit;
1510 | BigInteger.prototype.flipBit = bnFlipBit;
1511 | BigInteger.prototype.add = bnAdd;
1512 | BigInteger.prototype.subtract = bnSubtract;
1513 | BigInteger.prototype.multiply = bnMultiply;
1514 | BigInteger.prototype.divide = bnDivide;
1515 | BigInteger.prototype.remainder = bnRemainder;
1516 | BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;
1517 | BigInteger.prototype.modPow = bnModPow;
1518 | BigInteger.prototype.modInverse = bnModInverse;
1519 | BigInteger.prototype.pow = bnPow;
1520 | BigInteger.prototype.gcd = bnGCD;
1521 | BigInteger.prototype.isProbablePrime = bnIsProbablePrime;
1522 | BigInteger.int2char = int2char;
1523 |
1524 | // "constants"
1525 | BigInteger.ZERO = nbv(0);
1526 | BigInteger.ONE = nbv(1);
1527 |
1528 | // JSBN-specific extension
1529 | BigInteger.prototype.square = bnSquare;
1530 |
1531 | //BigInteger interfaces not implemented in jsbn:
1532 |
1533 | //BigInteger(int signum, byte[] magnitude)
1534 | //double doubleValue()
1535 | //float floatValue()
1536 | //int hashCode()
1537 | //long longValue()
1538 | //static BigInteger valueOf(long val)
1539 |
1540 | module.exports = BigInteger;
--------------------------------------------------------------------------------
/src/libs/rsa.js:
--------------------------------------------------------------------------------
1 | /*
2 | * RSA Encryption / Decryption with PKCS1 v2 Padding.
3 | *
4 | * Copyright (c) 2003-2005 Tom Wu
5 | * All Rights Reserved.
6 | *
7 | * Permission is hereby granted, free of charge, to any person obtaining
8 | * a copy of this software and associated documentation files (the
9 | * "Software"), to deal in the Software without restriction, including
10 | * without limitation the rights to use, copy, modify, merge, publish,
11 | * distribute, sublicense, and/or sell copies of the Software, and to
12 | * permit persons to whom the Software is furnished to do so, subject to
13 | * the following conditions:
14 | *
15 | * The above copyright notice and this permission notice shall be
16 | * included in all copies or substantial portions of the Software.
17 | *
18 | * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
19 | * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
20 | * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
21 | *
22 | * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
23 | * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
24 | * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
25 | * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
26 | * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
27 | *
28 | * In addition, the following condition applies:
29 | *
30 | * All redistributions must retain an intact copy of this copyright notice
31 | * and disclaimer.
32 | */
33 |
34 | /*
35 | * Node.js adaptation
36 | * long message support implementation
37 | * signing/verifying
38 | *
39 | * 2014 rzcoder
40 | */
41 |
42 | var _ = require('../utils')._;
43 | var crypt = require('crypto');
44 | var BigInteger = require('./jsbn.js');
45 | var utils = require('../utils.js');
46 | var schemes = require('../schemes/schemes.js');
47 | var encryptEngines = require('../encryptEngines/encryptEngines.js');
48 |
49 | exports.BigInteger = BigInteger;
50 | module.exports.Key = (function () {
51 | /**
52 | * RSA key constructor
53 | *
54 | * n - modulus
55 | * e - publicExponent
56 | * d - privateExponent
57 | * p - prime1
58 | * q - prime2
59 | * dmp1 - exponent1 -- d mod (p1)
60 | * dmq1 - exponent2 -- d mod (q-1)
61 | * coeff - coefficient -- (inverse of q) mod p
62 | */
63 | function RSAKey() {
64 | this.n = null;
65 | this.e = 0;
66 | this.d = null;
67 | this.p = null;
68 | this.q = null;
69 | this.dmp1 = null;
70 | this.dmq1 = null;
71 | this.coeff = null;
72 | }
73 |
74 | RSAKey.prototype.setOptions = function (options) {
75 | var signingSchemeProvider = schemes[options.signingScheme];
76 | var encryptionSchemeProvider = schemes[options.encryptionScheme];
77 |
78 | if (signingSchemeProvider === encryptionSchemeProvider) {
79 | this.signingScheme = this.encryptionScheme = encryptionSchemeProvider.makeScheme(this, options);
80 | } else {
81 | this.encryptionScheme = encryptionSchemeProvider.makeScheme(this, options);
82 | this.signingScheme = signingSchemeProvider.makeScheme(this, options);
83 | }
84 |
85 | this.encryptEngine = encryptEngines.getEngine(this, options);
86 | };
87 |
88 | /**
89 | * Generate a new random private key B bits long, using public expt E
90 | * @param B
91 | * @param E
92 | */
93 | RSAKey.prototype.generate = function (B, E) {
94 | var qs = B >> 1;
95 | this.e = parseInt(E, 16);
96 | var ee = new BigInteger(E, 16);
97 | while (true) {
98 | while (true) {
99 | this.p = new BigInteger(B - qs, 1);
100 | if (this.p.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) === 0 && this.p.isProbablePrime(10))
101 | break;
102 | }
103 | while (true) {
104 | this.q = new BigInteger(qs, 1);
105 | if (this.q.subtract(BigInteger.ONE).gcd(ee).compareTo(BigInteger.ONE) === 0 && this.q.isProbablePrime(10))
106 | break;
107 | }
108 | if (this.p.compareTo(this.q) <= 0) {
109 | var t = this.p;
110 | this.p = this.q;
111 | this.q = t;
112 | }
113 | var p1 = this.p.subtract(BigInteger.ONE);
114 | var q1 = this.q.subtract(BigInteger.ONE);
115 | var phi = p1.multiply(q1);
116 | if (phi.gcd(ee).compareTo(BigInteger.ONE) === 0) {
117 | this.n = this.p.multiply(this.q);
118 | if (this.n.bitLength() < B) {
119 | continue;
120 | }
121 | this.d = ee.modInverse(phi);
122 | this.dmp1 = this.d.mod(p1);
123 | this.dmq1 = this.d.mod(q1);
124 | this.coeff = this.q.modInverse(this.p);
125 | break;
126 | }
127 | }
128 | this.$$recalculateCache();
129 | };
130 |
131 | /**
132 | * Set the private key fields N, e, d and CRT params from buffers
133 | *
134 | * @param N
135 | * @param E
136 | * @param D
137 | * @param P
138 | * @param Q
139 | * @param DP
140 | * @param DQ
141 | * @param C
142 | */
143 | RSAKey.prototype.setPrivate = function (N, E, D, P, Q, DP, DQ, C) {
144 | if (N && E && D && N.length > 0 && (_.isNumber(E) || E.length > 0) && D.length > 0) {
145 | this.n = new BigInteger(N);
146 | this.e = _.isNumber(E) ? E : utils.get32IntFromBuffer(E, 0);
147 | this.d = new BigInteger(D);
148 |
149 | if (P && Q && DP && DQ && C) {
150 | this.p = new BigInteger(P);
151 | this.q = new BigInteger(Q);
152 | this.dmp1 = new BigInteger(DP);
153 | this.dmq1 = new BigInteger(DQ);
154 | this.coeff = new BigInteger(C);
155 | } else {
156 | // TODO: re-calculate any missing CRT params
157 | }
158 | this.$$recalculateCache();
159 | } else {
160 | throw Error("Invalid RSA private key");
161 | }
162 | };
163 |
164 | /**
165 | * Set the public key fields N and e from hex strings
166 | * @param N
167 | * @param E
168 | */
169 | RSAKey.prototype.setPublic = function (N, E) {
170 | if (N && E && N.length > 0 && (_.isNumber(E) || E.length > 0)) {
171 | this.n = new BigInteger(N);
172 | this.e = _.isNumber(E) ? E : utils.get32IntFromBuffer(E, 0);
173 | this.$$recalculateCache();
174 | } else {
175 | throw Error("Invalid RSA public key");
176 | }
177 | };
178 |
179 | /**
180 | * private
181 | * Perform raw private operation on "x": return x^d (mod n)
182 | *
183 | * @param x
184 | * @returns {*}
185 | */
186 | RSAKey.prototype.$doPrivate = function (x) {
187 | if (this.p || this.q) {
188 | return x.modPow(this.d, this.n);
189 | }
190 |
191 | // TODO: re-calculate any missing CRT params
192 | var xp = x.mod(this.p).modPow(this.dmp1, this.p);
193 | var xq = x.mod(this.q).modPow(this.dmq1, this.q);
194 |
195 | while (xp.compareTo(xq) < 0) {
196 | xp = xp.add(this.p);
197 | }
198 | return xp.subtract(xq).multiply(this.coeff).mod(this.p).multiply(this.q).add(xq);
199 | };
200 |
201 | /**
202 | * private
203 | * Perform raw public operation on "x": return x^e (mod n)
204 | *
205 | * @param x
206 | * @returns {*}
207 | */
208 | RSAKey.prototype.$doPublic = function (x) {
209 | return x.modPowInt(this.e, this.n);
210 | };
211 |
212 | /**
213 | * Return the PKCS#1 RSA encryption of buffer
214 | * @param buffer {Buffer}
215 | * @returns {Buffer}
216 | */
217 | RSAKey.prototype.encrypt = function (buffer, usePrivate) {
218 | var buffers = [];
219 | var results = [];
220 | var bufferSize = buffer.length;
221 | var buffersCount = Math.ceil(bufferSize / this.maxMessageLength) || 1; // total buffers count for encrypt
222 | var dividedSize = Math.ceil(bufferSize / buffersCount || 1); // each buffer size
223 |
224 | if (buffersCount == 1) {
225 | buffers.push(buffer);
226 | } else {
227 | for (var bufNum = 0; bufNum < buffersCount; bufNum++) {
228 | buffers.push(buffer.slice(bufNum * dividedSize, (bufNum + 1) * dividedSize));
229 | }
230 | }
231 |
232 | for (var i = 0; i < buffers.length; i++) {
233 | results.push(this.encryptEngine.encrypt(buffers[i], usePrivate));
234 | }
235 |
236 | return Buffer.concat(results);
237 | };
238 |
239 | /**
240 | * Return the PKCS#1 RSA decryption of buffer
241 | * @param buffer {Buffer}
242 | * @returns {Buffer}
243 | */
244 | RSAKey.prototype.decrypt = function (buffer, usePublic) {
245 | if (buffer.length % this.encryptedDataLength > 0) {
246 | throw Error('Incorrect data or key');
247 | }
248 |
249 | var result = [];
250 | var offset = 0;
251 | var length = 0;
252 | var buffersCount = buffer.length / this.encryptedDataLength;
253 |
254 | for (var i = 0; i < buffersCount; i++) {
255 | offset = i * this.encryptedDataLength;
256 | length = offset + this.encryptedDataLength;
257 | result.push(this.encryptEngine.decrypt(buffer.slice(offset, Math.min(length, buffer.length)), usePublic));
258 | }
259 |
260 | return Buffer.concat(result);
261 | };
262 |
263 | RSAKey.prototype.sign = function (buffer) {
264 | return this.signingScheme.sign.apply(this.signingScheme, arguments);
265 | };
266 |
267 | RSAKey.prototype.verify = function (buffer, signature, signature_encoding) {
268 | return this.signingScheme.verify.apply(this.signingScheme, arguments);
269 | };
270 |
271 | /**
272 | * Check if key pair contains private key
273 | */
274 | RSAKey.prototype.isPrivate = function () {
275 | return this.n && this.e && this.d && true || false;
276 | };
277 |
278 | /**
279 | * Check if key pair contains public key
280 | * @param strict {boolean} - public key only, return false if have private exponent
281 | */
282 | RSAKey.prototype.isPublic = function (strict) {
283 | return this.n && this.e && !(strict && this.d) || false;
284 | };
285 |
286 | Object.defineProperty(RSAKey.prototype, 'keySize', {
287 | get: function () {
288 | return this.cache.keyBitLength;
289 | }
290 | });
291 |
292 | Object.defineProperty(RSAKey.prototype, 'encryptedDataLength', {
293 | get: function () {
294 | return this.cache.keyByteLength;
295 | }
296 | });
297 |
298 | Object.defineProperty(RSAKey.prototype, 'maxMessageLength', {
299 | get: function () {
300 | return this.encryptionScheme.maxMessageLength();
301 | }
302 | });
303 |
304 | /**
305 | * Caching key data
306 | */
307 | RSAKey.prototype.$$recalculateCache = function () {
308 | this.cache = this.cache || {};
309 | // Bit & byte length
310 | this.cache.keyBitLength = this.n.bitLength();
311 | this.cache.keyByteLength = (this.cache.keyBitLength + 6) >> 3;
312 | };
313 |
314 | return RSAKey;
315 | })();
316 |
317 |
--------------------------------------------------------------------------------
/src/schemes/oaep.js:
--------------------------------------------------------------------------------
1 | /**
2 | * PKCS_OAEP signature scheme
3 | */
4 |
5 | var BigInteger = require('../libs/jsbn');
6 | var crypt = require('crypto');
7 |
8 | module.exports = {
9 | isEncryption: true,
10 | isSignature: false
11 | };
12 |
13 | module.exports.digestLength = {
14 | md4: 16,
15 | md5: 16,
16 | ripemd160: 20,
17 | rmd160: 20,
18 | sha1: 20,
19 | sha224: 28,
20 | sha256: 32,
21 | sha384: 48,
22 | sha512: 64
23 | };
24 |
25 | var DEFAULT_HASH_FUNCTION = 'sha1';
26 |
27 | /*
28 | * OAEP Mask Generation Function 1
29 | * Generates a buffer full of pseudorandom bytes given seed and maskLength.
30 | * Giving the same seed, maskLength, and hashFunction will result in the same exact byte values in the buffer.
31 | *
32 | * https://tools.ietf.org/html/rfc3447#appendix-B.2.1
33 | *
34 | * Parameters:
35 | * seed [Buffer] The pseudo random seed for this function
36 | * maskLength [int] The length of the output
37 | * hashFunction [String] The hashing function to use. Will accept any valid crypto hash. Default "sha1"
38 | * Supports "sha1" and "sha256".
39 | * To add another algorythm the algorythem must be accepted by crypto.createHash, and then the length of the output of the hash function (the digest) must be added to the digestLength object below.
40 | * Most RSA implementations will be expecting sha1
41 | */
42 | module.exports.eme_oaep_mgf1 = function (seed, maskLength, hashFunction) {
43 | hashFunction = hashFunction || DEFAULT_HASH_FUNCTION;
44 | var hLen = module.exports.digestLength[hashFunction];
45 | var count = Math.ceil(maskLength / hLen);
46 | var T = Buffer.alloc(hLen * count);
47 | var c = Buffer.alloc(4);
48 | for (var i = 0; i < count; ++i) {
49 | var hash = crypt.createHash(hashFunction);
50 | hash.update(seed);
51 | c.writeUInt32BE(i, 0);
52 | hash.update(c);
53 | hash.digest().copy(T, i * hLen);
54 | }
55 | return T.slice(0, maskLength);
56 | };
57 |
58 | module.exports.makeScheme = function (key, options) {
59 | function Scheme(key, options) {
60 | this.key = key;
61 | this.options = options;
62 | }
63 |
64 | Scheme.prototype.maxMessageLength = function () {
65 | return this.key.encryptedDataLength - 2 * module.exports.digestLength[this.options.encryptionSchemeOptions.hash || DEFAULT_HASH_FUNCTION] - 2;
66 | };
67 |
68 | /**
69 | * Pad input
70 | * alg: PKCS1_OAEP
71 | *
72 | * https://tools.ietf.org/html/rfc3447#section-7.1.1
73 | */
74 | Scheme.prototype.encPad = function (buffer) {
75 | var hash = this.options.encryptionSchemeOptions.hash || DEFAULT_HASH_FUNCTION;
76 | var mgf = this.options.encryptionSchemeOptions.mgf || module.exports.eme_oaep_mgf1;
77 | var label = this.options.encryptionSchemeOptions.label || Buffer.alloc(0);
78 | var emLen = this.key.encryptedDataLength;
79 |
80 | var hLen = module.exports.digestLength[hash];
81 |
82 | // Make sure we can put message into an encoded message of emLen bytes
83 | if (buffer.length > emLen - 2 * hLen - 2) {
84 | throw new Error("Message is too long to encode into an encoded message with a length of " + emLen + " bytes, increase" +
85 | "emLen to fix this error (minimum value for given parameters and options: " + (emLen - 2 * hLen - 2) + ")");
86 | }
87 |
88 | var lHash = crypt.createHash(hash);
89 | lHash.update(label);
90 | lHash = lHash.digest();
91 |
92 | var PS = Buffer.alloc(emLen - buffer.length - 2 * hLen - 1); // Padding "String"
93 | PS.fill(0); // Fill the buffer with octets of 0
94 | PS[PS.length - 1] = 1;
95 |
96 | var DB = Buffer.concat([lHash, PS, buffer]);
97 | var seed = crypt.randomBytes(hLen);
98 |
99 | // mask = dbMask
100 | var mask = mgf(seed, DB.length, hash);
101 | // XOR DB and dbMask together.
102 | for (var i = 0; i < DB.length; i++) {
103 | DB[i] ^= mask[i];
104 | }
105 | // DB = maskedDB
106 |
107 | // mask = seedMask
108 | mask = mgf(DB, hLen, hash);
109 | // XOR seed and seedMask together.
110 | for (i = 0; i < seed.length; i++) {
111 | seed[i] ^= mask[i];
112 | }
113 | // seed = maskedSeed
114 |
115 | var em = Buffer.alloc(1 + seed.length + DB.length);
116 | em[0] = 0;
117 | seed.copy(em, 1);
118 | DB.copy(em, 1 + seed.length);
119 |
120 | return em;
121 | };
122 |
123 | /**
124 | * Unpad input
125 | * alg: PKCS1_OAEP
126 | *
127 | * Note: This method works within the buffer given and modifies the values. It also returns a slice of the EM as the return Message.
128 | * If the implementation requires that the EM parameter be unmodified then the implementation should pass in a clone of the EM buffer.
129 | *
130 | * https://tools.ietf.org/html/rfc3447#section-7.1.2
131 | */
132 | Scheme.prototype.encUnPad = function (buffer) {
133 | var hash = this.options.encryptionSchemeOptions.hash || DEFAULT_HASH_FUNCTION;
134 | var mgf = this.options.encryptionSchemeOptions.mgf || module.exports.eme_oaep_mgf1;
135 | var label = this.options.encryptionSchemeOptions.label || Buffer.alloc(0);
136 |
137 | var hLen = module.exports.digestLength[hash];
138 |
139 | // Check to see if buffer is a properly encoded OAEP message
140 | if (buffer.length < 2 * hLen + 2) {
141 | throw new Error("Error decoding message, the supplied message is not long enough to be a valid OAEP encoded message");
142 | }
143 |
144 | var seed = buffer.slice(1, hLen + 1); // seed = maskedSeed
145 | var DB = buffer.slice(1 + hLen); // DB = maskedDB
146 |
147 | var mask = mgf(DB, hLen, hash); // seedMask
148 | // XOR maskedSeed and seedMask together to get the original seed.
149 | for (var i = 0; i < seed.length; i++) {
150 | seed[i] ^= mask[i];
151 | }
152 |
153 | mask = mgf(seed, DB.length, hash); // dbMask
154 | // XOR DB and dbMask together to get the original data block.
155 | for (i = 0; i < DB.length; i++) {
156 | DB[i] ^= mask[i];
157 | }
158 |
159 | var lHash = crypt.createHash(hash);
160 | lHash.update(label);
161 | lHash = lHash.digest();
162 |
163 | var lHashEM = DB.slice(0, hLen);
164 | if (lHashEM.toString("hex") != lHash.toString("hex")) {
165 | throw new Error("Error decoding message, the lHash calculated from the label provided and the lHash in the encrypted data do not match.");
166 | }
167 |
168 | // Filter out padding
169 | i = hLen;
170 | while (DB[i++] === 0 && i < DB.length);
171 | if (DB[i - 1] != 1) {
172 | throw new Error("Error decoding message, there is no padding message separator byte");
173 | }
174 |
175 | return DB.slice(i); // Message
176 | };
177 |
178 | return new Scheme(key, options);
179 | };
180 |
--------------------------------------------------------------------------------
/src/schemes/pkcs1.js:
--------------------------------------------------------------------------------
1 | /**
2 | * PKCS1 padding and signature scheme
3 | */
4 |
5 | var BigInteger = require('../libs/jsbn');
6 | var crypt = require('crypto');
7 | var constants = require('constants');
8 | var SIGN_INFO_HEAD = {
9 | md2: Buffer.from('3020300c06082a864886f70d020205000410', 'hex'),
10 | md5: Buffer.from('3020300c06082a864886f70d020505000410', 'hex'),
11 | sha1: Buffer.from('3021300906052b0e03021a05000414', 'hex'),
12 | sha224: Buffer.from('302d300d06096086480165030402040500041c', 'hex'),
13 | sha256: Buffer.from('3031300d060960864801650304020105000420', 'hex'),
14 | sha384: Buffer.from('3041300d060960864801650304020205000430', 'hex'),
15 | sha512: Buffer.from('3051300d060960864801650304020305000440', 'hex'),
16 | ripemd160: Buffer.from('3021300906052b2403020105000414', 'hex'),
17 | rmd160: Buffer.from('3021300906052b2403020105000414', 'hex')
18 | };
19 |
20 | var SIGN_ALG_TO_HASH_ALIASES = {
21 | 'ripemd160': 'rmd160'
22 | };
23 |
24 | var DEFAULT_HASH_FUNCTION = 'sha256';
25 |
26 | module.exports = {
27 | isEncryption: true,
28 | isSignature: true
29 | };
30 |
31 | module.exports.makeScheme = function (key, options) {
32 | function Scheme(key, options) {
33 | this.key = key;
34 | this.options = options;
35 | }
36 |
37 | Scheme.prototype.maxMessageLength = function () {
38 | if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == constants.RSA_NO_PADDING) {
39 | return this.key.encryptedDataLength;
40 | }
41 | return this.key.encryptedDataLength - 11;
42 | };
43 |
44 | /**
45 | * Pad input Buffer to encryptedDataLength bytes, and return Buffer.from
46 | * alg: PKCS#1
47 | * @param buffer
48 | * @returns {Buffer}
49 | */
50 | Scheme.prototype.encPad = function (buffer, options) {
51 | options = options || {};
52 | var filled;
53 | if (buffer.length > this.key.maxMessageLength) {
54 | throw new Error("Message too long for RSA (n=" + this.key.encryptedDataLength + ", l=" + buffer.length + ")");
55 | }
56 | if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == constants.RSA_NO_PADDING) {
57 | //RSA_NO_PADDING treated like JAVA left pad with zero character
58 | filled = Buffer.alloc(this.key.maxMessageLength - buffer.length);
59 | filled.fill(0);
60 | return Buffer.concat([filled, buffer]);
61 | }
62 |
63 | /* Type 1: zeros padding for private key encrypt */
64 | if (options.type === 1) {
65 | filled = Buffer.alloc(this.key.encryptedDataLength - buffer.length - 1);
66 | filled.fill(0xff, 0, filled.length - 1);
67 | filled[0] = 1;
68 | filled[filled.length - 1] = 0;
69 |
70 | return Buffer.concat([filled, buffer]);
71 | } else {
72 | /* random padding for public key encrypt */
73 | filled = Buffer.alloc(this.key.encryptedDataLength - buffer.length);
74 | filled[0] = 0;
75 | filled[1] = 2;
76 | var rand = crypt.randomBytes(filled.length - 3);
77 | for (var i = 0; i < rand.length; i++) {
78 | var r = rand[i];
79 | while (r === 0) { // non-zero only
80 | r = crypt.randomBytes(1)[0];
81 | }
82 | filled[i + 2] = r;
83 | }
84 | filled[filled.length - 1] = 0;
85 | return Buffer.concat([filled, buffer]);
86 | }
87 | };
88 |
89 | /**
90 | * Unpad input Buffer and, if valid, return the Buffer object
91 | * alg: PKCS#1 (type 2, random)
92 | * @param buffer
93 | * @returns {Buffer}
94 | */
95 | Scheme.prototype.encUnPad = function (buffer, options) {
96 | options = options || {};
97 | var i = 0;
98 |
99 | if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == constants.RSA_NO_PADDING) {
100 | //RSA_NO_PADDING treated like JAVA left pad with zero character
101 | var unPad;
102 | if (typeof buffer.lastIndexOf == "function") { //patch for old node version
103 | unPad = buffer.slice(buffer.lastIndexOf('\0') + 1, buffer.length);
104 | } else {
105 | unPad = buffer.slice(String.prototype.lastIndexOf.call(buffer, '\0') + 1, buffer.length);
106 | }
107 | return unPad;
108 | }
109 |
110 | if (buffer.length < 4) {
111 | return null;
112 | }
113 |
114 | /* Type 1: zeros padding for private key decrypt */
115 | if (options.type === 1) {
116 | if (buffer[0] !== 0 || buffer[1] !== 1) {
117 | return null;
118 | }
119 | i = 3;
120 | while (buffer[i] !== 0) {
121 | if (buffer[i] != 0xFF || ++i >= buffer.length) {
122 | return null;
123 | }
124 | }
125 | } else {
126 | /* random padding for public key decrypt */
127 | if (buffer[0] !== 0 || buffer[1] !== 2) {
128 | return null;
129 | }
130 | i = 3;
131 | while (buffer[i] !== 0) {
132 | if (++i >= buffer.length) {
133 | return null;
134 | }
135 | }
136 | }
137 | return buffer.slice(i + 1, buffer.length);
138 | };
139 |
140 | Scheme.prototype.sign = function (buffer) {
141 | var hashAlgorithm = this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION;
142 | if (this.options.environment === 'browser') {
143 | hashAlgorithm = SIGN_ALG_TO_HASH_ALIASES[hashAlgorithm] || hashAlgorithm;
144 |
145 | var hasher = crypt.createHash(hashAlgorithm);
146 | hasher.update(buffer);
147 | var hash = this.pkcs1pad(hasher.digest(), hashAlgorithm);
148 | var res = this.key.$doPrivate(new BigInteger(hash)).toBuffer(this.key.encryptedDataLength);
149 |
150 | return res;
151 | } else {
152 | var signer = crypt.createSign('RSA-' + hashAlgorithm.toUpperCase());
153 | signer.update(buffer);
154 | return signer.sign(this.options.rsaUtils.exportKey('private'));
155 | }
156 | };
157 |
158 | Scheme.prototype.verify = function (buffer, signature, signature_encoding) {
159 | if (this.options.encryptionSchemeOptions && this.options.encryptionSchemeOptions.padding == constants.RSA_NO_PADDING) {
160 | //RSA_NO_PADDING has no verify data
161 | return false;
162 | }
163 | var hashAlgorithm = this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION;
164 | if (this.options.environment === 'browser') {
165 | hashAlgorithm = SIGN_ALG_TO_HASH_ALIASES[hashAlgorithm] || hashAlgorithm;
166 |
167 | if (signature_encoding) {
168 | signature = Buffer.from(signature, signature_encoding);
169 | }
170 |
171 | var hasher = crypt.createHash(hashAlgorithm);
172 | hasher.update(buffer);
173 | var hash = this.pkcs1pad(hasher.digest(), hashAlgorithm);
174 | var m = this.key.$doPublic(new BigInteger(signature));
175 |
176 | return m.toBuffer().toString('hex') == hash.toString('hex');
177 | } else {
178 | var verifier = crypt.createVerify('RSA-' + hashAlgorithm.toUpperCase());
179 | verifier.update(buffer);
180 | return verifier.verify(this.options.rsaUtils.exportKey('public'), signature, signature_encoding);
181 | }
182 | };
183 |
184 | /**
185 | * PKCS#1 zero pad input buffer to max data length
186 | * @param hashBuf
187 | * @param hashAlgorithm
188 | * @returns {*}
189 | */
190 | Scheme.prototype.pkcs0pad = function (buffer) {
191 | var filled = Buffer.alloc(this.key.maxMessageLength - buffer.length);
192 | filled.fill(0);
193 | return Buffer.concat([filled, buffer]);
194 | };
195 |
196 | Scheme.prototype.pkcs0unpad = function (buffer) {
197 | var unPad;
198 | if (typeof buffer.lastIndexOf == "function") { //patch for old node version
199 | unPad = buffer.slice(buffer.lastIndexOf('\0') + 1, buffer.length);
200 | } else {
201 | unPad = buffer.slice(String.prototype.lastIndexOf.call(buffer, '\0') + 1, buffer.length);
202 | }
203 |
204 | return unPad;
205 | };
206 |
207 | /**
208 | * PKCS#1 pad input buffer to max data length
209 | * @param hashBuf
210 | * @param hashAlgorithm
211 | * @returns {*}
212 | */
213 | Scheme.prototype.pkcs1pad = function (hashBuf, hashAlgorithm) {
214 | var digest = SIGN_INFO_HEAD[hashAlgorithm];
215 | if (!digest) {
216 | throw Error('Unsupported hash algorithm');
217 | }
218 |
219 | var data = Buffer.concat([digest, hashBuf]);
220 |
221 | if (data.length + 10 > this.key.encryptedDataLength) {
222 | throw Error('Key is too short for signing algorithm (' + hashAlgorithm + ')');
223 | }
224 |
225 | var filled = Buffer.alloc(this.key.encryptedDataLength - data.length - 1);
226 | filled.fill(0xff, 0, filled.length - 1);
227 | filled[0] = 1;
228 | filled[filled.length - 1] = 0;
229 |
230 | var res = Buffer.concat([filled, data]);
231 |
232 | return res;
233 | };
234 |
235 | return new Scheme(key, options);
236 | };
237 |
238 |
239 |
--------------------------------------------------------------------------------
/src/schemes/pss.js:
--------------------------------------------------------------------------------
1 | /**
2 | * PSS signature scheme
3 | */
4 |
5 | var BigInteger = require('../libs/jsbn');
6 | var crypt = require('crypto');
7 |
8 | module.exports = {
9 | isEncryption: false,
10 | isSignature: true
11 | };
12 |
13 | var DEFAULT_HASH_FUNCTION = 'sha1';
14 | var DEFAULT_SALT_LENGTH = 20;
15 |
16 | module.exports.makeScheme = function (key, options) {
17 | var OAEP = require('./schemes').pkcs1_oaep;
18 |
19 | /**
20 | * @param key
21 | * @param options
22 | * options [Object] An object that contains the following keys that specify certain options for encoding.
23 | * └>signingSchemeOptions
24 | * ├>hash [String] Hash function to use when encoding and generating masks. Must be a string accepted by node's crypto.createHash function. (default = "sha1")
25 | * ├>mgf [function] The mask generation function to use when encoding. (default = mgf1SHA1)
26 | * └>sLen [uint] The length of the salt to generate. (default = 20)
27 | * @constructor
28 | */
29 | function Scheme(key, options) {
30 | this.key = key;
31 | this.options = options;
32 | }
33 |
34 | Scheme.prototype.sign = function (buffer) {
35 | var mHash = crypt.createHash(this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION);
36 | mHash.update(buffer);
37 |
38 | var encoded = this.emsa_pss_encode(mHash.digest(), this.key.keySize - 1);
39 | return this.key.$doPrivate(new BigInteger(encoded)).toBuffer(this.key.encryptedDataLength);
40 | };
41 |
42 | Scheme.prototype.verify = function (buffer, signature, signature_encoding) {
43 | if (signature_encoding) {
44 | signature = Buffer.from(signature, signature_encoding);
45 | }
46 | signature = new BigInteger(signature);
47 |
48 | var emLen = Math.ceil((this.key.keySize - 1) / 8);
49 | var m = this.key.$doPublic(signature).toBuffer(emLen);
50 |
51 | var mHash = crypt.createHash(this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION);
52 | mHash.update(buffer);
53 |
54 | return this.emsa_pss_verify(mHash.digest(), m, this.key.keySize - 1);
55 | };
56 |
57 | /*
58 | * https://tools.ietf.org/html/rfc3447#section-9.1.1
59 | *
60 | * mHash [Buffer] Hashed message to encode
61 | * emBits [uint] Maximum length of output in bits. Must be at least 8hLen + 8sLen + 9 (hLen = Hash digest length in bytes | sLen = length of salt in bytes)
62 | * @returns {Buffer} The encoded message
63 | */
64 | Scheme.prototype.emsa_pss_encode = function (mHash, emBits) {
65 | var hash = this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION;
66 | var mgf = this.options.signingSchemeOptions.mgf || OAEP.eme_oaep_mgf1;
67 | var sLen = this.options.signingSchemeOptions.saltLength || DEFAULT_SALT_LENGTH;
68 |
69 | var hLen = OAEP.digestLength[hash];
70 | var emLen = Math.ceil(emBits / 8);
71 |
72 | if (emLen < hLen + sLen + 2) {
73 | throw new Error("Output length passed to emBits(" + emBits + ") is too small for the options " +
74 | "specified(" + hash + ", " + sLen + "). To fix this issue increase the value of emBits. (minimum size: " +
75 | (8 * hLen + 8 * sLen + 9) + ")"
76 | );
77 | }
78 |
79 | var salt = crypt.randomBytes(sLen);
80 |
81 | var Mapostrophe = Buffer.alloc(8 + hLen + sLen);
82 | Mapostrophe.fill(0, 0, 8);
83 | mHash.copy(Mapostrophe, 8);
84 | salt.copy(Mapostrophe, 8 + mHash.length);
85 |
86 | var H = crypt.createHash(hash);
87 | H.update(Mapostrophe);
88 | H = H.digest();
89 |
90 | var PS = Buffer.alloc(emLen - salt.length - hLen - 2);
91 | PS.fill(0);
92 |
93 | var DB = Buffer.alloc(PS.length + 1 + salt.length);
94 | PS.copy(DB);
95 | DB[PS.length] = 0x01;
96 | salt.copy(DB, PS.length + 1);
97 |
98 | var dbMask = mgf(H, DB.length, hash);
99 |
100 | // XOR DB and dbMask together
101 | var maskedDB = Buffer.alloc(DB.length);
102 | for (var i = 0; i < dbMask.length; i++) {
103 | maskedDB[i] = DB[i] ^ dbMask[i];
104 | }
105 |
106 | var bits = 8 * emLen - emBits;
107 | var mask = 255 ^ (255 >> 8 - bits << 8 - bits);
108 | maskedDB[0] = maskedDB[0] & mask;
109 |
110 | var EM = Buffer.alloc(maskedDB.length + H.length + 1);
111 | maskedDB.copy(EM, 0);
112 | H.copy(EM, maskedDB.length);
113 | EM[EM.length - 1] = 0xbc;
114 |
115 | return EM;
116 | };
117 |
118 | /*
119 | * https://tools.ietf.org/html/rfc3447#section-9.1.2
120 | *
121 | * mHash [Buffer] Hashed message
122 | * EM [Buffer] Signature
123 | * emBits [uint] Length of EM in bits. Must be at least 8hLen + 8sLen + 9 to be a valid signature. (hLen = Hash digest length in bytes | sLen = length of salt in bytes)
124 | * @returns {Boolean} True if signature(EM) matches message(M)
125 | */
126 | Scheme.prototype.emsa_pss_verify = function (mHash, EM, emBits) {
127 | var hash = this.options.signingSchemeOptions.hash || DEFAULT_HASH_FUNCTION;
128 | var mgf = this.options.signingSchemeOptions.mgf || OAEP.eme_oaep_mgf1;
129 | var sLen = this.options.signingSchemeOptions.saltLength || DEFAULT_SALT_LENGTH;
130 |
131 | var hLen = OAEP.digestLength[hash];
132 | var emLen = Math.ceil(emBits / 8);
133 |
134 | if (emLen < hLen + sLen + 2 || EM[EM.length - 1] != 0xbc) {
135 | return false;
136 | }
137 |
138 | var DB = Buffer.alloc(emLen - hLen - 1);
139 | EM.copy(DB, 0, 0, emLen - hLen - 1);
140 |
141 | var mask = 0;
142 | for (var i = 0, bits = 8 * emLen - emBits; i < bits; i++) {
143 | mask |= 1 << (7 - i);
144 | }
145 |
146 | if ((DB[0] & mask) !== 0) {
147 | return false;
148 | }
149 |
150 | var H = EM.slice(emLen - hLen - 1, emLen - 1);
151 | var dbMask = mgf(H, DB.length, hash);
152 |
153 | // Unmask DB
154 | for (i = 0; i < DB.length; i++) {
155 | DB[i] ^= dbMask[i];
156 | }
157 |
158 | bits = 8 * emLen - emBits;
159 | mask = 255 ^ (255 >> 8 - bits << 8 - bits);
160 | DB[0] = DB[0] & mask;
161 |
162 | // Filter out padding
163 | for (i = 0; DB[i] === 0 && i < DB.length; i++);
164 | if (DB[i] != 1) {
165 | return false;
166 | }
167 |
168 | var salt = DB.slice(DB.length - sLen);
169 |
170 | var Mapostrophe = Buffer.alloc(8 + hLen + sLen);
171 | Mapostrophe.fill(0, 0, 8);
172 | mHash.copy(Mapostrophe, 8);
173 | salt.copy(Mapostrophe, 8 + mHash.length);
174 |
175 | var Hapostrophe = crypt.createHash(hash);
176 | Hapostrophe.update(Mapostrophe);
177 | Hapostrophe = Hapostrophe.digest();
178 |
179 | return H.toString("hex") === Hapostrophe.toString("hex");
180 | };
181 |
182 | return new Scheme(key, options);
183 | };
184 |
--------------------------------------------------------------------------------
/src/schemes/schemes.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | pkcs1: require('./pkcs1'),
3 | pkcs1_oaep: require('./oaep'),
4 | pss: require('./pss'),
5 |
6 | /**
7 | * Check if scheme has padding methods
8 | * @param scheme {string}
9 | * @returns {Boolean}
10 | */
11 | isEncryption: function (scheme) {
12 | return module.exports[scheme] && module.exports[scheme].isEncryption;
13 | },
14 |
15 | /**
16 | * Check if scheme has sign/verify methods
17 | * @param scheme {string}
18 | * @returns {Boolean}
19 | */
20 | isSignature: function (scheme) {
21 | return module.exports[scheme] && module.exports[scheme].isSignature;
22 | }
23 | };
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Utils functions
3 | *
4 | */
5 |
6 | var crypt = require('crypto');
7 |
8 | /**
9 | * Break string str each maxLen symbols
10 | * @param str
11 | * @param maxLen
12 | * @returns {string}
13 | */
14 | module.exports.linebrk = function (str, maxLen) {
15 | var res = '';
16 | var i = 0;
17 | while (i + maxLen < str.length) {
18 | res += str.substring(i, i + maxLen) + "\n";
19 | i += maxLen;
20 | }
21 | return res + str.substring(i, str.length);
22 | };
23 |
24 | module.exports.detectEnvironment = function () {
25 | if (typeof(window) !== 'undefined' && window && !(process && process.title === 'node')) {
26 | return 'browser';
27 | }
28 |
29 | return 'node';
30 | };
31 |
32 | /**
33 | * Trying get a 32-bit unsigned integer from the partial buffer
34 | * @param buffer
35 | * @param offset
36 | * @returns {Number}
37 | */
38 | module.exports.get32IntFromBuffer = function (buffer, offset) {
39 | offset = offset || 0;
40 | var size = 0;
41 | if ((size = buffer.length - offset) > 0) {
42 | if (size >= 4) {
43 | return buffer.readUIntBE(offset, size);
44 | } else {
45 | var res = 0;
46 | for (var i = offset + size, d = 0; i > offset; i--, d += 2) {
47 | res += buffer[i - 1] * Math.pow(16, d);
48 | }
49 | return res;
50 | }
51 | } else {
52 | return NaN;
53 | }
54 | };
55 |
56 | module.exports._ = {
57 | isObject: function (value) {
58 | var type = typeof value;
59 | return !!value && (type == 'object' || type == 'function');
60 | },
61 |
62 | isString: function (value) {
63 | return typeof value == 'string' || value instanceof String;
64 | },
65 |
66 | isNumber: function (value) {
67 | return typeof value == 'number' || !isNaN(parseFloat(value)) && isFinite(value);
68 | },
69 |
70 | /**
71 | * Returns copy of `obj` without `removeProp` field.
72 | * @param obj
73 | * @param removeProp
74 | * @returns Object
75 | */
76 | omit: function (obj, removeProp) {
77 | var newObj = {};
78 | for (var prop in obj) {
79 | if (!obj.hasOwnProperty(prop) || prop === removeProp) {
80 | continue;
81 | }
82 | newObj[prop] = obj[prop];
83 | }
84 |
85 | return newObj;
86 | }
87 | };
88 |
89 | /**
90 | * Strips everything around the opening and closing lines, including the lines
91 | * themselves.
92 | */
93 | module.exports.trimSurroundingText = function (data, opening, closing) {
94 | var trimStartIndex = 0;
95 | var trimEndIndex = data.length;
96 |
97 | var openingBoundaryIndex = data.indexOf(opening);
98 | if (openingBoundaryIndex >= 0) {
99 | trimStartIndex = openingBoundaryIndex + opening.length;
100 | }
101 |
102 | var closingBoundaryIndex = data.indexOf(closing, openingBoundaryIndex);
103 | if (closingBoundaryIndex >= 0) {
104 | trimEndIndex = closingBoundaryIndex;
105 | }
106 |
107 | return data.substring(trimStartIndex, trimEndIndex);
108 | }
--------------------------------------------------------------------------------
/test/keys/id_rsa:
--------------------------------------------------------------------------------
1 | -----BEGIN OPENSSH PRIVATE KEY-----
2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn
3 | NhAAAAAwEAAQAAAIEAgnWPhKQwv7z2t9+Ze+dUgTPHeS3+M+EGV+G3Pg6k2Pc1WP65jxm0
4 | 5ArnipyCgKbjhjFyTmgsDR/cFH7Tbe09H6BMI5deriAvJuIcEo4zS+UyqjFXsksr9OKAbo
5 | nb++rucjYiwuCfOZW5lt1gMmJEwm5v1SWQFzSbqgpuwFVpkDEAAAH4AAAAAAAAAAAAAAAH
6 | c3NoLXJzYQAAAIEAgnWPhKQwv7z2t9+Ze+dUgTPHeS3+M+EGV+G3Pg6k2Pc1WP65jxm05A
7 | rnipyCgKbjhjFyTmgsDR/cFH7Tbe09H6BMI5deriAvJuIcEo4zS+UyqjFXsksr9OKAbonb
8 | ++rucjYiwuCfOZW5lt1gMmJEwm5v1SWQFzSbqgpuwFVpkDEAAAADAQABAAAAgBGEd6D36x
9 | PT680E2Tcp+M7ghQhghKGytYdXZ6ONk9UOXLt2eLQeX4u/axfRrDRaNHLwcMjWdBPPE14t
10 | KXa5RFupnT4EBXdwhcazoCrfQBqTrSNc81Tm9VHXcsKv5lgT8hE8BCs6u5QtpwHDFK9K5R
11 | a6w5lE9nWnx3rlpxTGf9WBAAAAQANIyhoJ33ughNzbCIknkMPKtgvLOUARnbya/bkfRexL
12 | icyYzXPNuqZDY8JZQHlshN8cCcZcYjGPYYscd2LKB6oAAABBAMK1+2wf3+mtuC5DgXaU5a
13 | wvP3pqLH+OcjwWEGa6QqZ8sxeMJlhi/4OyvxMiX+KuIapxKAaQEcegZ7WeYtRngQcAAABB
14 | AKuGHAfE/QyyGFHmkviUVsCzno1Ov62HYMQSGhp9ptC3af8+5SzO4G9B+QJfuzzmo5AHZw
15 | 3JQQ4csaiJxzuFbwcAAAAAAQID
16 | -----END OPENSSH PRIVATE KEY-----
17 |
--------------------------------------------------------------------------------
/test/keys/id_rsa.pub:
--------------------------------------------------------------------------------
1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCCdY+EpDC/vPa335l751SBM8d5Lf4z4QZX4bc+DqTY9zVY/rmPGbTkCueKnIKApuOGMXJOaCwNH9wUftNt7T0foEwjl16uIC8m4hwSjjNL5TKqMVeySyv04oBuidv76u5yNiLC4J85lbmW3WAyYkTCbm/VJZAXNJuqCm7AVWmQMQ==
2 |
--------------------------------------------------------------------------------
/test/keys/id_rsa_comment:
--------------------------------------------------------------------------------
1 | -----BEGIN OPENSSH PRIVATE KEY-----
2 | b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAlwAAAAdzc2gtcn
3 | NhAAAAAwEAAQAAAIEAs3U7i0VIEqz3K8gh67sVeM10KPoL+MmQGR9wTI0XVMb5hVzDwfTf
4 | kbMSgE2oeQQJ1A+z/m1dPANEQsKmB+4+WyexnofCTVkyaRhC4GqbWPv4J332X1MeTYs01D
5 | UMJZI/fAT9Cvq8LSDuRW02M6f+b2rAEtqHD+fxyekaBmxyjLcAAAIIAAAAAAAAAAAAAAAH
6 | c3NoLXJzYQAAAIEAs3U7i0VIEqz3K8gh67sVeM10KPoL+MmQGR9wTI0XVMb5hVzDwfTfkb
7 | MSgE2oeQQJ1A+z/m1dPANEQsKmB+4+WyexnofCTVkyaRhC4GqbWPv4J332X1MeTYs01DUM
8 | JZI/fAT9Cvq8LSDuRW02M6f+b2rAEtqHD+fxyekaBmxyjLcAAAADAQABAAAAgH63VOgub4
9 | ngYFel5W3SmILIcDFO/o0ZpopWzLEBH2xZY29r5T5bblIvI+086K0q0NXQkMQi7SanF9gc
10 | IaiP7a65Tx7lKSrAmrsnSCrZ3k+dE/+MsqGwhlDA+cxf7Ti11xSBcilcp+/KpSIEaUM8W2
11 | GWcCSRl9gY6A8rfl7bsxpBAAAAQBIPInX4FCtwwASCJVb45eMVx3+HWnMIzCW6cCn24scY
12 | mXw4AaO/ykDpcMtyDRv8T6id7fkR+XKqZ6lKP+HxaC4AAABBANhJFHqKlpbN0PTfqjyyBM
13 | ZWzKyzHEjwPvUcrPIrSsuQNGz/+Ync0zte0nQXMBYSQxIiSJ32fvwVdcE/hv9rWa8AAABB
14 | ANRpALkbvpU4pjNYfWX/74eu9cbUDhHbu74cJq0mmU3jd4Uv4X7wUijkG4lVfsrdpXzJRv
15 | aMkt1MrDSzj7kMp3kAAAAQb25kcmFAb25kcmFsdWtlcwECAw==
16 | -----END OPENSSH PRIVATE KEY-----
17 |
--------------------------------------------------------------------------------
/test/keys/id_rsa_comment.pub:
--------------------------------------------------------------------------------
1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQCzdTuLRUgSrPcryCHruxV4zXQo+gv4yZAZH3BMjRdUxvmFXMPB9N+RsxKATah5BAnUD7P+bV08A0RCwqYH7j5bJ7Geh8JNWTJpGELgaptY+/gnffZfUx5NizTUNQwlkj98BP0K+rwtIO5FbTYzp/5vasAS2ocP5/HJ6RoGbHKMtw== ondra@ondralukes
2 |
--------------------------------------------------------------------------------
/test/keys/private_pkcs1.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rzcoder/node-rsa/e7e7f7d2942a3bac1d2e132a881e5a3aceda10a1/test/keys/private_pkcs1.der
--------------------------------------------------------------------------------
/test/keys/private_pkcs1.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXAIBAAKBgQCCdY+EpDC/vPa335l751SBM8d5Lf4z4QZX4bc+DqTY9zVY/rmP
3 | GbTkCueKnIKApuOGMXJOaCwNH9wUftNt7T0foEwjl16uIC8m4hwSjjNL5TKqMVey
4 | Syv04oBuidv76u5yNiLC4J85lbmW3WAyYkTCbm/VJZAXNJuqCm7AVWmQMQIDAQAB
5 | AoGAEYR3oPfrE9PrzQTZNyn4zuCFCGCEobK1h1dno42T1Q5cu3Z4tB5fi79rF9Gs
6 | NFo0cvBwyNZ0E88TXi0pdrlEW6mdPgQFd3CFxrOgKt9AGpOtI1zzVOb1Uddywq/m
7 | WBPyETwEKzq7lC2nAcMUr0rlFrrDmUT2dafHeuWnFMZ/1YECQQDCtftsH9/prbgu
8 | Q4F2lOWsLz96aix/jnI8FhBmukKmfLMXjCZYYv+Dsr8TIl/iriGqcSgGkBHHoGe1
9 | nmLUZ4EHAkEAq4YcB8T9DLIYUeaS+JRWwLOejU6/rYdgxBIaGn2m0Ldp/z7lLM7g
10 | b0H5Al+7POajkAdnDclBDhyxqInHO4VvBwJBAJ25jNEpgNhqQKg5RsYoF2RDYchn
11 | +WPan+7McLzGZPc4TFrmzKkMiK7GPMHjNokJRXwr7aBjVAPBjEEy7BvjPEECQFOJ
12 | 4rcKAzEewGeLREObg9Eg6nTqSMLMb52vL1V9ozR+UDrHuDilnXuyhwPX+kqEDl+E
13 | q3V0cqHb6c8rI4TizRsCQANIyhoJ33ughNzbCIknkMPKtgvLOUARnbya/bkfRexL
14 | icyYzXPNuqZDY8JZQHlshN8cCcZcYjGPYYscd2LKB6o=
15 | -----END RSA PRIVATE KEY-----
--------------------------------------------------------------------------------
/test/keys/private_pkcs8.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rzcoder/node-rsa/e7e7f7d2942a3bac1d2e132a881e5a3aceda10a1/test/keys/private_pkcs8.der
--------------------------------------------------------------------------------
/test/keys/private_pkcs8.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PRIVATE KEY-----
2 | MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAIJ1j4SkML+89rff
3 | mXvnVIEzx3kt/jPhBlfhtz4OpNj3NVj+uY8ZtOQK54qcgoCm44Yxck5oLA0f3BR+
4 | 023tPR+gTCOXXq4gLybiHBKOM0vlMqoxV7JLK/TigG6J2/vq7nI2IsLgnzmVuZbd
5 | YDJiRMJub9UlkBc0m6oKbsBVaZAxAgMBAAECgYARhHeg9+sT0+vNBNk3KfjO4IUI
6 | YIShsrWHV2ejjZPVDly7dni0Hl+Lv2sX0aw0WjRy8HDI1nQTzxNeLSl2uURbqZ0+
7 | BAV3cIXGs6Aq30Aak60jXPNU5vVR13LCr+ZYE/IRPAQrOruULacBwxSvSuUWusOZ
8 | RPZ1p8d65acUxn/VgQJBAMK1+2wf3+mtuC5DgXaU5awvP3pqLH+OcjwWEGa6QqZ8
9 | sxeMJlhi/4OyvxMiX+KuIapxKAaQEcegZ7WeYtRngQcCQQCrhhwHxP0MshhR5pL4
10 | lFbAs56NTr+th2DEEhoafabQt2n/PuUszuBvQfkCX7s85qOQB2cNyUEOHLGoicc7
11 | hW8HAkEAnbmM0SmA2GpAqDlGxigXZENhyGf5Y9qf7sxwvMZk9zhMWubMqQyIrsY8
12 | weM2iQlFfCvtoGNUA8GMQTLsG+M8QQJAU4nitwoDMR7AZ4tEQ5uD0SDqdOpIwsxv
13 | na8vVX2jNH5QOse4OKWde7KHA9f6SoQOX4SrdXRyodvpzysjhOLNGwJAA0jKGgnf
14 | e6CE3NsIiSeQw8q2C8s5QBGdvJr9uR9F7EuJzJjNc826pkNjwllAeWyE3xwJxlxi
15 | MY9hixx3YsoHqg==
16 | -----END PRIVATE KEY-----
--------------------------------------------------------------------------------
/test/keys/public_pkcs1.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rzcoder/node-rsa/e7e7f7d2942a3bac1d2e132a881e5a3aceda10a1/test/keys/public_pkcs1.der
--------------------------------------------------------------------------------
/test/keys/public_pkcs1.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PUBLIC KEY-----
2 | MIGJAoGBAIJ1j4SkML+89rffmXvnVIEzx3kt/jPhBlfhtz4OpNj3NVj+uY8ZtOQK
3 | 54qcgoCm44Yxck5oLA0f3BR+023tPR+gTCOXXq4gLybiHBKOM0vlMqoxV7JLK/Ti
4 | gG6J2/vq7nI2IsLgnzmVuZbdYDJiRMJub9UlkBc0m6oKbsBVaZAxAgMBAAE=
5 | -----END RSA PUBLIC KEY-----
--------------------------------------------------------------------------------
/test/keys/public_pkcs8.der:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rzcoder/node-rsa/e7e7f7d2942a3bac1d2e132a881e5a3aceda10a1/test/keys/public_pkcs8.der
--------------------------------------------------------------------------------
/test/keys/public_pkcs8.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN PUBLIC KEY-----
2 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCCdY+EpDC/vPa335l751SBM8d5
3 | Lf4z4QZX4bc+DqTY9zVY/rmPGbTkCueKnIKApuOGMXJOaCwNH9wUftNt7T0foEwj
4 | l16uIC8m4hwSjjNL5TKqMVeySyv04oBuidv76u5yNiLC4J85lbmW3WAyYkTCbm/V
5 | JZAXNJuqCm7AVWmQMQIDAQAB
6 | -----END PUBLIC KEY-----
--------------------------------------------------------------------------------
/test/private_pkcs1.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN RSA PRIVATE KEY-----
2 | MIICXAIBAAKBgQCCdY+EpDC/vPa335l751SBM8d5Lf4z4QZX4bc+DqTY9zVY/rmP
3 | GbTkCueKnIKApuOGMXJOaCwNH9wUftNt7T0foEwjl16uIC8m4hwSjjNL5TKqMVey
4 | Syv04oBuidv76u5yNiLC4J85lbmW3WAyYkTCbm/VJZAXNJuqCm7AVWmQMQIDAQAB
5 | AoGAEYR3oPfrE9PrzQTZNyn4zuCFCGCEobK1h1dno42T1Q5cu3Z4tB5fi79rF9Gs
6 | NFo0cvBwyNZ0E88TXi0pdrlEW6mdPgQFd3CFxrOgKt9AGpOtI1zzVOb1Uddywq/m
7 | WBPyETwEKzq7lC2nAcMUr0rlFrrDmUT2dafHeuWnFMZ/1YECQQDCtftsH9/prbgu
8 | Q4F2lOWsLz96aix/jnI8FhBmukKmfLMXjCZYYv+Dsr8TIl/iriGqcSgGkBHHoGe1
9 | nmLUZ4EHAkEAq4YcB8T9DLIYUeaS+JRWwLOejU6/rYdgxBIaGn2m0Ldp/z7lLM7g
10 | b0H5Al+7POajkAdnDclBDhyxqInHO4VvBwJBAJ25jNEpgNhqQKg5RsYoF2RDYchn
11 | +WPan+7McLzGZPc4TFrmzKkMiK7GPMHjNokJRXwr7aBjVAPBjEEy7BvjPEECQFOJ
12 | 4rcKAzEewGeLREObg9Eg6nTqSMLMb52vL1V9ozR+UDrHuDilnXuyhwPX+kqEDl+E
13 | q3V0cqHb6c8rI4TizRsCQANIyhoJ33ughNzbCIknkMPKtgvLOUARnbya/bkfRexL
14 | icyYzXPNuqZDY8JZQHlshN8cCcZcYjGPYYscd2LKB6o=
15 | -----END RSA PRIVATE KEY-----
--------------------------------------------------------------------------------
/test/tests.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var assert = require('chai').assert;
3 | var _ = require('lodash');
4 | var NodeRSA = require('../src/NodeRSA');
5 | var OAEP = require('../src/schemes/oaep');
6 | var constants = require('constants');
7 |
8 | describe('NodeRSA', function () {
9 | var keySizes = [
10 | {b: 512, e: 3},
11 | {b: 512, e: 5},
12 | {b: 512, e: 257},
13 | {b: 512, e: 65537},
14 | {b: 768}, // 'e' should be 65537
15 | {b: 1024}, // 'e' should be 65537
16 | {b: 2048} // 'e' should be 65537
17 | ];
18 |
19 | var environments = ['browser', 'node'];
20 | var encryptSchemes = [
21 | 'pkcs1',
22 | 'pkcs1_oaep',
23 | {
24 | scheme:'pkcs1',
25 | padding: constants.RSA_NO_PADDING,
26 | toString: function() {
27 | return 'pkcs1-nopadding';
28 | }
29 | }
30 | ];
31 | var signingSchemes = ['pkcs1', 'pss'];
32 | var signHashAlgorithms = {
33 | 'node': ['MD4', 'MD5', 'RIPEMD160', 'SHA1', 'SHA224', 'SHA256', 'SHA384', 'SHA512'],
34 | 'browser': ['MD5', 'RIPEMD160', 'SHA1', 'SHA256', 'SHA512']
35 | };
36 |
37 | var dataBundle = {
38 | 'string': {
39 | data: 'ascii + 12345678',
40 | encoding: 'utf8'
41 | },
42 | 'unicode string': {
43 | data: 'ascii + юникод スラ ⑨',
44 | encoding: 'utf8'
45 | },
46 | 'empty string': {
47 | data: '',
48 | encoding: ['utf8', 'ascii', 'hex', 'base64']
49 | },
50 | 'long string': {
51 | data: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
52 | encoding: ['utf8', 'ascii']
53 | },
54 | 'buffer': {
55 | data: Buffer.from('ascii + юникод スラ ⑨'),
56 | encoding: 'buffer'
57 | },
58 | 'json object': {
59 | data: {str: 'string', arr: ['a', 'r', 'r', 'a', 'y', true, '⑨'], int: 42, nested: {key: {key: 1}}},
60 | encoding: 'json'
61 | },
62 | 'json array': {
63 | data: [1, 2, 3, 4, 5, 6, 7, 8, 9, [10, 11, 12, [13], 14, 15, [16, 17, [18]]]],
64 | encoding: 'json'
65 | }
66 | };
67 |
68 | var privateKeyPKCS1 = '-----BEGIN RSA PRIVATE KEY-----\n' +
69 | 'MIIFwgIBAAKCAUEAsE1edyfToZRv6cFOkB0tAJ5qJor4YF5CccJAL0fS/o1Yk10V\n' +
70 | 'SXH4Xx4peSJgYQKkO0HqO1hAz6k9dFQB4U1CnWtRjtNEcIfycqrZrhu6you5syb6\n' +
71 | 'ScV3Zu/9bm7/DyaLlx/gJhUPR1OxOzaqsEvlu7hbDhNLIYo1zKFb/aUBbD6+UcaG\n' +
72 | 'xH2BfFNdzVAtVSVpc/s2Y3sboMN7rByUj7937iQlaMINvVjyasynYuzHNw6ZRP9J\n' +
73 | 'P9fwxrCyaxnTPWxVl0qvVaQO2+TtFMtDXH2OVZtWWeLHAL8cildw0G+u2qVqTqIG\n' +
74 | 'EwNyJlsAHykaPFAMW0xLueumrSlB+JUJPrRvvw4nBCd4GOrNSlPCE/xlk1Cb8JaI\n' +
75 | 'CTLvDUcYc3ZqL3jqAueBhkpw2uCz8xVJeOA1KY4kQIIx8JEBsAYzgyP2iy0CAwEA\n' +
76 | 'AQKCAUAjBcudShkdgRpWSmNr94/IDrAxpeu/YRo79QXBHriIftW4uIYRCAX6B0jf\n' +
77 | '2ndg7iBn8Skxzs9ZMVqW8FVLR4jTMs2J3Og8npUIOG5zyuhpciZas4SHASY+GbCz\n' +
78 | 'rnMWtGaIh/mENyzI05RimfKAgSNLDk1wV17Wc9lKJEfc9Fl7Al/WaOS+xdviMcFx\n' +
79 | 'ltrajksLkjz0uDD917eKskbE45lULfGqeI0kYDadWp88pw6ikXJln2p3Y1PNQF3e\n' +
80 | 'y2cN+Snzd0jx/c5fD9B1zxKYv5bUo+UnTzBxV81e9xCJfkdXv+6D5qDn1gGLdZZa\n' +
81 | '5FxtZbRgVh/ZlqP9xYr72as/WFmIA20wRgHPgWvLyHsh0XThqZf2/O3R8KmFv8aT\n' +
82 | '+kmc5is6sVItIIi7ltorVapTkJai3zz/VSMBBaL+ytFN9jVl4QKBoQDfL8TMeZXu\n' +
83 | 'gBTN7yq6zZWN8+60MUaxz0/lKdzmo35z32rpVKdsYd922pmcsNYaoj/H9L3j/NP4\n' +
84 | '9z+SHfYpWvTa7AvJfNlXYc3BRXIarpfnXsm65IzKzHaF9i2xdXxkfTEYIvOQDMLF\n' +
85 | 'SiiObWJMV+QqUxb3luu3/CR3IcbgeTOpdiC/T/Zl/YYl17JqZTHmLFZPq7xewttg\n' +
86 | 'zQorDRWIFDtlAoGhAMo4+uM9f4BpOHSmayhLhHArIGs4386BkXSeOLeQitaQJ/2c\n' +
87 | 'zb459O87XoCAonZbq+dI7XRnBU3toQvEsZgrtGkOFXCZJMWAQxD5BQ5vEYT6c86h\n' +
88 | 'uGpX6h3ODlJ6UGi+5CWyMQ1cFlBkfffFAarjSYTVlyj736sOeDuJWX133z5VQBQ8\n' +
89 | '1xSH23kNF95vxB4I1fXG8WL11YZU7VEwSLC4aCkCgaAKRj+wDhTZ4umSRWVZLiep\n' +
90 | 'XkZp4y7W9q095nx13abvnKRmU3BVq/fGl++kZ/ujRD7dbKXlPflgJ7m0d06ivr4w\n' +
91 | '6dbtEqNKw4TeVd0X31u82f89bFIS7/Cw4BFgbwEn+x9sdgdyZTP+MxjE3cI9s3oc\n' +
92 | 'fLC8+ySk1qWzGkn2gX3gWkDNrdexAEfRrClZfokaiIX8qvJEBoJk5WuHadXI6u2F\n' +
93 | 'AoGgByidOQ4kRVd0OCzr/jEuLwpXy3Pn+Fd93rL7LwRe5dmUkNXMMr+6e/2OCt6C\n' +
94 | '4c28+CMMxOIgvfF7kf8Uil6BtHZbK/E/6/3uYdtu4mPsKtjy4I25CYqzLvrsZt8N\n' +
95 | 'maeoS+1S7zYjVBU6oFrJBFOndpxZDYpdEKEigHkMQfTMYliCPDUrJ/7nNhHQln8+\n' +
96 | 'YhHOATVZtjcdp/O5svYSnK7qgQKBoDd3lFWrPatgxpF1JXMEFFbaIRdNxHkKA4YY\n' +
97 | 'gMTM4MPgViunYX/yJ7SaX8jWnC231A9uVn4+kb+DvKjc+ZuTQvnIUK2u6LvIinVF\n' +
98 | 'snDEA+BbXwehAtwdHDMDtqYFdx4hvCWQwBNn4p3J0OO2tbYVMtvM5aOEfRSYagfm\n' +
99 | 'RywhDUAjW8U0RBnzlmXhQQ6B9bjqooS2MsRrJrS5CU682fb3hBo=\n' +
100 | '-----END RSA PRIVATE KEY-----';
101 |
102 | var privateKeyComponents = {
103 | n: 'ALBNXncn06GUb+nBTpAdLQCeaiaK+GBeQnHCQC9H0v6NWJNdFUlx+F8eKXkiYGECpDtB6jtYQM+pPXRUAeFNQp1rUY7TRHCH8nKq2a4busqLubMm+knFd2bv/W5u/w8mi5cf4CYVD0dTsTs2qrBL5bu4Ww4TSyGKNcyhW/2lAWw+vlHGhsR9gXxTXc1QLVUlaXP7NmN7G6DDe6wclI+/d+4kJWjCDb1Y8mrMp2LsxzcOmUT/ST/X8MawsmsZ0z1sVZdKr1WkDtvk7RTLQ1x9jlWbVlnixwC/HIpXcNBvrtqlak6iBhMDciZbAB8pGjxQDFtMS7nrpq0pQfiVCT60b78OJwQneBjqzUpTwhP8ZZNQm/CWiAky7w1HGHN2ai946gLngYZKcNrgs/MVSXjgNSmOJECCMfCRAbAGM4Mj9ost',
104 | e: 65537,
105 | d: 'IwXLnUoZHYEaVkpja/ePyA6wMaXrv2EaO/UFwR64iH7VuLiGEQgF+gdI39p3YO4gZ/EpMc7PWTFalvBVS0eI0zLNidzoPJ6VCDhuc8roaXImWrOEhwEmPhmws65zFrRmiIf5hDcsyNOUYpnygIEjSw5NcFde1nPZSiRH3PRZewJf1mjkvsXb4jHBcZba2o5LC5I89Lgw/de3irJGxOOZVC3xqniNJGA2nVqfPKcOopFyZZ9qd2NTzUBd3stnDfkp83dI8f3OXw/Qdc8SmL+W1KPlJ08wcVfNXvcQiX5HV7/ug+ag59YBi3WWWuRcbWW0YFYf2Zaj/cWK+9mrP1hZiANtMEYBz4Fry8h7IdF04amX9vzt0fCphb/Gk/pJnOYrOrFSLSCIu5baK1WqU5CWot88/1UjAQWi/srRTfY1ZeE=',
106 | p: 'AN8vxMx5le6AFM3vKrrNlY3z7rQxRrHPT+Up3OajfnPfaulUp2xh33bamZyw1hqiP8f0veP80/j3P5Id9ila9NrsC8l82VdhzcFFchqul+deybrkjMrMdoX2LbF1fGR9MRgi85AMwsVKKI5tYkxX5CpTFveW67f8JHchxuB5M6l2IL9P9mX9hiXXsmplMeYsVk+rvF7C22DNCisNFYgUO2U=',
107 | q: 'AMo4+uM9f4BpOHSmayhLhHArIGs4386BkXSeOLeQitaQJ/2czb459O87XoCAonZbq+dI7XRnBU3toQvEsZgrtGkOFXCZJMWAQxD5BQ5vEYT6c86huGpX6h3ODlJ6UGi+5CWyMQ1cFlBkfffFAarjSYTVlyj736sOeDuJWX133z5VQBQ81xSH23kNF95vxB4I1fXG8WL11YZU7VEwSLC4aCk=',
108 | dmp1: 'CkY/sA4U2eLpkkVlWS4nqV5GaeMu1vatPeZ8dd2m75ykZlNwVav3xpfvpGf7o0Q+3Wyl5T35YCe5tHdOor6+MOnW7RKjSsOE3lXdF99bvNn/PWxSEu/wsOARYG8BJ/sfbHYHcmUz/jMYxN3CPbN6HHywvPskpNalsxpJ9oF94FpAza3XsQBH0awpWX6JGoiF/KryRAaCZOVrh2nVyOrthQ==',
109 | dmq1: 'ByidOQ4kRVd0OCzr/jEuLwpXy3Pn+Fd93rL7LwRe5dmUkNXMMr+6e/2OCt6C4c28+CMMxOIgvfF7kf8Uil6BtHZbK/E/6/3uYdtu4mPsKtjy4I25CYqzLvrsZt8NmaeoS+1S7zYjVBU6oFrJBFOndpxZDYpdEKEigHkMQfTMYliCPDUrJ/7nNhHQln8+YhHOATVZtjcdp/O5svYSnK7qgQ==',
110 | coeff: 'N3eUVas9q2DGkXUlcwQUVtohF03EeQoDhhiAxMzgw+BWK6dhf/IntJpfyNacLbfUD25Wfj6Rv4O8qNz5m5NC+chQra7ou8iKdUWycMQD4FtfB6EC3B0cMwO2pgV3HiG8JZDAE2fincnQ47a1thUy28zlo4R9FJhqB+ZHLCENQCNbxTREGfOWZeFBDoH1uOqihLYyxGsmtLkJTrzZ9veEGg=='
111 | };
112 |
113 | var publicKeyPKCS8 = '-----BEGIN PUBLIC KEY-----\n' +
114 | 'MIIBYjANBgkqhkiG9w0BAQEFAAOCAU8AMIIBSgKCAUEAsE1edyfToZRv6cFOkB0t\n' +
115 | 'AJ5qJor4YF5CccJAL0fS/o1Yk10VSXH4Xx4peSJgYQKkO0HqO1hAz6k9dFQB4U1C\n' +
116 | 'nWtRjtNEcIfycqrZrhu6you5syb6ScV3Zu/9bm7/DyaLlx/gJhUPR1OxOzaqsEvl\n' +
117 | 'u7hbDhNLIYo1zKFb/aUBbD6+UcaGxH2BfFNdzVAtVSVpc/s2Y3sboMN7rByUj793\n' +
118 | '7iQlaMINvVjyasynYuzHNw6ZRP9JP9fwxrCyaxnTPWxVl0qvVaQO2+TtFMtDXH2O\n' +
119 | 'VZtWWeLHAL8cildw0G+u2qVqTqIGEwNyJlsAHykaPFAMW0xLueumrSlB+JUJPrRv\n' +
120 | 'vw4nBCd4GOrNSlPCE/xlk1Cb8JaICTLvDUcYc3ZqL3jqAueBhkpw2uCz8xVJeOA1\n' +
121 | 'KY4kQIIx8JEBsAYzgyP2iy0CAwEAAQ==\n' +
122 | '-----END PUBLIC KEY-----';
123 |
124 | var generatedKeys = [];
125 | var privateNodeRSA = null;
126 | var publicNodeRSA = null;
127 |
128 | describe('Setup options', function () {
129 | it('should use browser environment', function () {
130 | assert.equal((new NodeRSA(null, {environment: 'browser'})).$options.environment, 'browser');
131 | });
132 |
133 | it('should use io.js environment', function () {
134 | assert.equal((new NodeRSA(null, {environment: 'iojs'})).$options.environment, 'iojs');
135 | });
136 |
137 | it('should make empty key pair with default options', function () {
138 | var key = new NodeRSA(null);
139 | assert.equal(key.isEmpty(), true);
140 | assert.equal(key.$options.signingScheme, 'pkcs1');
141 | assert.equal(key.$options.signingSchemeOptions.hash, 'sha256');
142 | assert.equal(key.$options.signingSchemeOptions.saltLength, null);
143 |
144 | assert.equal(key.$options.encryptionScheme, 'pkcs1_oaep');
145 | assert.equal(key.$options.encryptionSchemeOptions.hash, 'sha1');
146 | assert.equal(key.$options.encryptionSchemeOptions.label, null);
147 | });
148 |
149 | it('should make key pair with pkcs1-md5 signing scheme', function () {
150 | var key = new NodeRSA(null, {signingScheme: 'md5'});
151 | assert.equal(key.$options.signingScheme, 'pkcs1');
152 | assert.equal(key.$options.signingSchemeOptions.hash, 'md5');
153 | });
154 |
155 | it('should make key pair with pss-sha512 signing scheme', function () {
156 | var key = new NodeRSA(null, {signingScheme: 'pss-sha512'});
157 | assert.equal(key.$options.signingScheme, 'pss');
158 | assert.equal(key.$options.signingSchemeOptions.hash, 'sha512');
159 | });
160 |
161 | it('should make key pair with pkcs1 encryption scheme, and pss-sha1 signing scheme', function () {
162 | var key = new NodeRSA(null, {encryptionScheme: 'pkcs1', signingScheme: 'pss'});
163 | assert.equal(key.$options.encryptionScheme, 'pkcs1');
164 | assert.equal(key.$options.signingScheme, 'pss');
165 | assert.equal(key.$options.signingSchemeOptions.hash, null);
166 | });
167 |
168 | it('change options', function () {
169 | var key = new NodeRSA(null, {signingScheme: 'pss-sha1'});
170 | assert.equal(key.$options.signingScheme, 'pss');
171 | assert.equal(key.$options.signingSchemeOptions.hash, 'sha1');
172 | key.setOptions({signingScheme: 'pkcs1'});
173 | assert.equal(key.$options.signingScheme, 'pkcs1');
174 | assert.equal(key.$options.signingSchemeOptions.hash, null);
175 | key.setOptions({signingScheme: 'pkcs1-sha256'});
176 | assert.equal(key.$options.signingScheme, 'pkcs1');
177 | assert.equal(key.$options.signingSchemeOptions.hash, 'sha256');
178 | });
179 |
180 | it('advanced options change', function () {
181 | var key = new NodeRSA(null);
182 | key.setOptions({
183 | encryptionScheme: {
184 | scheme: 'pkcs1_oaep',
185 | hash: 'sha512',
186 | label: 'horay'
187 | },
188 | signingScheme: {
189 | scheme: 'pss',
190 | hash: 'md5',
191 | saltLength: 15
192 | }
193 | });
194 |
195 | assert.equal(key.$options.signingScheme, 'pss');
196 | assert.equal(key.$options.signingSchemeOptions.hash, 'md5');
197 | assert.equal(key.$options.signingSchemeOptions.saltLength, 15);
198 | assert.equal(key.$options.encryptionScheme, 'pkcs1_oaep');
199 | assert.equal(key.$options.encryptionSchemeOptions.hash, 'sha512');
200 | assert.equal(key.$options.encryptionSchemeOptions.label, 'horay');
201 | });
202 |
203 | it('should throw \'unsupported hashing algorithm\' exception', function () {
204 | var key = new NodeRSA(null);
205 | assert.equal(key.isEmpty(), true);
206 | assert.equal(key.$options.signingScheme, 'pkcs1');
207 | assert.equal(key.$options.signingSchemeOptions.hash, 'sha256');
208 |
209 | assert.throw(function () {
210 | key.setOptions({
211 | environment: 'browser',
212 | signingScheme: 'md4'
213 | });
214 | }, Error, 'Unsupported hashing algorithm');
215 | });
216 | });
217 |
218 | describe('Base methods', function () {
219 | it('importKey() should throw exception if key data not specified', function () {
220 | var key = new NodeRSA(null);
221 |
222 | assert.throw(function () {
223 | key.importKey();
224 | }, Error, 'Empty key given');
225 | });
226 |
227 | it('importKey() should return this', function () {
228 | var key = new NodeRSA(null);
229 | assert.equal(key.importKey(publicKeyPKCS8), key);
230 | });
231 | });
232 |
233 | describe('Work with keys', function () {
234 | describe('Generating keys', function () {
235 | for (var size in keySizes) {
236 | (function (size) {
237 | it('should make key pair ' + size.b + '-bit length and public exponent is ' + (size.e ? size.e : size.e + ' and should be 65537'), function () {
238 | this.timeout(35000);
239 | generatedKeys.push(new NodeRSA({b: size.b, e: size.e}, {encryptionScheme: 'pkcs1'}));
240 | assert.instanceOf(generatedKeys[generatedKeys.length - 1].keyPair, Object);
241 | assert.equal(generatedKeys[generatedKeys.length - 1].isEmpty(), false);
242 | assert.equal(generatedKeys[generatedKeys.length - 1].getKeySize(), size.b);
243 | assert.equal(generatedKeys[generatedKeys.length - 1].getMaxMessageSize(), (size.b / 8 - 11));
244 | assert.equal(generatedKeys[generatedKeys.length - 1].keyPair.e, size.e || 65537);
245 | });
246 | })(keySizes[size]);
247 | }
248 | });
249 |
250 | describe('Import/Export keys', function () {
251 | var publicKeyComponents = {
252 | n: 'ALBNXncn06GUb+nBTpAdLQCeaiaK+GBeQnHCQC9H0v6NWJNdFUlx+F8eKXkiYGECpDtB6jtYQM+pPXRUAeFNQp1rUY7TRHCH8nKq2a4busqLubMm+knFd2bv/W5u/w8mi5cf4CYVD0dTsTs2qrBL5bu4Ww4TSyGKNcyhW/2lAWw+vlHGhsR9gXxTXc1QLVUlaXP7NmN7G6DDe6wclI+/d+4kJWjCDb1Y8mrMp2LsxzcOmUT/ST/X8MawsmsZ0z1sVZdKr1WkDtvk7RTLQ1x9jlWbVlnixwC/HIpXcNBvrtqlak6iBhMDciZbAB8pGjxQDFtMS7nrpq0pQfiVCT60b78OJwQneBjqzUpTwhP8ZZNQm/CWiAky7w1HGHN2ai946gLngYZKcNrgs/MVSXjgNSmOJECCMfCRAbAGM4Mj9ost',
253 | e: 65537,
254 | };
255 |
256 | var privateKeyPEMNotTrimmed = 'random \n\n data \n\n ' + privateKeyPKCS1 + '\n \n \n\n random data ';
257 | var publicKeyPEMNotTrimmed = '\n\n\n\nrandom \n\n data\n ' + publicKeyPKCS8 + '\n \n random data\n\n ';
258 |
259 | var fileKeyPKCS1 = '-----BEGIN RSA PRIVATE KEY-----\n' +
260 | 'MIICXAIBAAKBgQCCdY+EpDC/vPa335l751SBM8d5Lf4z4QZX4bc+DqTY9zVY/rmP\n' +
261 | 'GbTkCueKnIKApuOGMXJOaCwNH9wUftNt7T0foEwjl16uIC8m4hwSjjNL5TKqMVey\n' +
262 | 'Syv04oBuidv76u5yNiLC4J85lbmW3WAyYkTCbm/VJZAXNJuqCm7AVWmQMQIDAQAB\n' +
263 | 'AoGAEYR3oPfrE9PrzQTZNyn4zuCFCGCEobK1h1dno42T1Q5cu3Z4tB5fi79rF9Gs\n' +
264 | 'NFo0cvBwyNZ0E88TXi0pdrlEW6mdPgQFd3CFxrOgKt9AGpOtI1zzVOb1Uddywq/m\n' +
265 | 'WBPyETwEKzq7lC2nAcMUr0rlFrrDmUT2dafHeuWnFMZ/1YECQQDCtftsH9/prbgu\n' +
266 | 'Q4F2lOWsLz96aix/jnI8FhBmukKmfLMXjCZYYv+Dsr8TIl/iriGqcSgGkBHHoGe1\n' +
267 | 'nmLUZ4EHAkEAq4YcB8T9DLIYUeaS+JRWwLOejU6/rYdgxBIaGn2m0Ldp/z7lLM7g\n' +
268 | 'b0H5Al+7POajkAdnDclBDhyxqInHO4VvBwJBAJ25jNEpgNhqQKg5RsYoF2RDYchn\n' +
269 | '+WPan+7McLzGZPc4TFrmzKkMiK7GPMHjNokJRXwr7aBjVAPBjEEy7BvjPEECQFOJ\n' +
270 | '4rcKAzEewGeLREObg9Eg6nTqSMLMb52vL1V9ozR+UDrHuDilnXuyhwPX+kqEDl+E\n' +
271 | 'q3V0cqHb6c8rI4TizRsCQANIyhoJ33ughNzbCIknkMPKtgvLOUARnbya/bkfRexL\n' +
272 | 'icyYzXPNuqZDY8JZQHlshN8cCcZcYjGPYYscd2LKB6o=\n' +
273 | '-----END RSA PRIVATE KEY-----';
274 | var keysFolder = __dirname + '/keys/';
275 | var keys_formats = {
276 | 'pkcs1-private-der': {public: false, der: true, file: 'private_pkcs1.der'},
277 | 'pkcs1-private-pem': {public: false, der: false, file: 'private_pkcs1.pem'},
278 | 'pkcs8-private-der': {public: false, der: true, file: 'private_pkcs8.der'},
279 | 'pkcs8-private-pem': {public: false, der: false, file: 'private_pkcs8.pem'},
280 | 'pkcs1-public-der': {public: true, der: true, file: 'public_pkcs1.der'},
281 | 'pkcs1-public-pem': {public: true, der: false, file: 'public_pkcs1.pem'},
282 | 'pkcs8-public-der': {public: true, der: true, file: 'public_pkcs8.der'},
283 | 'pkcs8-public-pem': {public: true, der: false, file: 'public_pkcs8.pem'},
284 |
285 | 'private': {public: false, der: false, file: 'private_pkcs1.pem'},
286 | 'public': {public: true, der: false, file: 'public_pkcs8.pem'},
287 | 'private-der': {public: false, der: true, file: 'private_pkcs1.der'},
288 | 'public-der': {public: true, der: true, file: 'public_pkcs8.der'},
289 |
290 | 'pkcs1': {public: false, der: false, file: 'private_pkcs1.pem'},
291 | 'pkcs1-private': {public: false, der: false, file: 'private_pkcs1.pem'},
292 | 'pkcs1-der': {public: false, der: true, file: 'private_pkcs1.der'},
293 | 'pkcs8': {public: false, der: false, file: 'private_pkcs8.pem'},
294 | 'pkcs8-private': {public: false, der: false, file: 'private_pkcs8.pem'},
295 | 'pkcs8-der': {public: false, der: true, file: 'private_pkcs8.der'},
296 | 'pkcs1-public': {public: true, der: false, file: 'public_pkcs1.pem'},
297 | 'pkcs8-public': {public: true, der: false, file: 'public_pkcs8.pem'},
298 |
299 | 'openssh-public': {public: true, der: false, file: 'id_rsa.pub'},
300 | 'openssh-private': {public: false, der: false, file: 'id_rsa'}
301 | };
302 |
303 | describe('Good cases', function () {
304 | describe('Common cases', function () {
305 | it('should load private key from (not trimmed) PKCS1-PEM string', function () {
306 | privateNodeRSA = new NodeRSA(privateKeyPEMNotTrimmed);
307 | assert.instanceOf(privateNodeRSA.keyPair, Object);
308 | assert(privateNodeRSA.isPrivate());
309 | assert(privateNodeRSA.isPublic());
310 | assert(!privateNodeRSA.isPublic(true));
311 | });
312 |
313 | it('should load public key from (not trimmed) PKCS8-PEM string', function () {
314 | publicNodeRSA = new NodeRSA(publicKeyPEMNotTrimmed);
315 | assert.instanceOf(publicNodeRSA.keyPair, Object);
316 | assert(publicNodeRSA.isPublic());
317 | assert(publicNodeRSA.isPublic(true));
318 | assert(!publicNodeRSA.isPrivate());
319 | });
320 |
321 | it('.exportKey() should return private PEM string', function () {
322 | assert.equal(privateNodeRSA.exportKey('private'), privateKeyPKCS1);
323 | assert.equal(privateNodeRSA.exportKey(), privateKeyPKCS1);
324 | });
325 |
326 | it('.exportKey() from public key should return pkcs8 public PEM string', function () {
327 | assert.equal(publicNodeRSA.exportKey('public'), publicKeyPKCS8);
328 | });
329 |
330 | it('.exportKey() from private key should return pkcs8 public PEM string', function () {
331 | assert.equal(privateNodeRSA.exportKey('public'), publicKeyPKCS8);
332 | });
333 |
334 | it('should create and load key from buffer/fs.readFileSync output', function () {
335 | var key = new NodeRSA(fs.readFileSync(keysFolder + 'private_pkcs1.pem'));
336 | assert.equal(key.exportKey(), fileKeyPKCS1);
337 | key = new NodeRSA();
338 | key.importKey(fs.readFileSync(keysFolder + 'private_pkcs1.pem'));
339 | assert.equal(key.exportKey(), fileKeyPKCS1);
340 | });
341 |
342 | it('should gracefully handle data outside of encapsulation boundaries for pkcs1 private keys', function () {
343 | let privateFileWithNoise = 'Lorem ipsum' + fs.readFileSync(keysFolder + 'private_pkcs1.pem') + 'dulce et decorum';
344 | let key = new NodeRSA(privateFileWithNoise);
345 | assert.equal(key.exportKey(), fileKeyPKCS1);
346 | });
347 |
348 | it('should gracefully handle data outside of encapsulation boundaries for pkcs1 public keys', function () {
349 | let publicFileWithNoise = 'Lorem ipsum' + fs.readFileSync(keysFolder + 'public_pkcs1.pem') + 'dulce et decorum';
350 | let publicNodeRSA = new NodeRSA(publicFileWithNoise);
351 | assert.instanceOf(publicNodeRSA.keyPair, Object);
352 | assert(publicNodeRSA.isPublic());
353 | assert(publicNodeRSA.isPublic(true));
354 | assert(!publicNodeRSA.isPrivate());
355 | });
356 |
357 | it('should gracefully handle data outside of encapsulation boundaries for pkcs8 private keys', function () {
358 | let privateFileWithNoise = 'Lorem ipsum' + fs.readFileSync(keysFolder + 'private_pkcs8.pem') + 'dulce et decorum';
359 | let key = new NodeRSA(privateFileWithNoise);
360 | assert.equal(key.exportKey(), fileKeyPKCS1);
361 | });
362 |
363 | it('should gracefully handle data outside of encapsulation boundaries for pkcs8 public keys', function () {
364 | let publicFileWithNoise = 'Lorem ipsum' + fs.readFileSync(keysFolder + 'public_pkcs8.pem') + 'dulce et decorum';
365 | let publicNodeRSA = new NodeRSA(publicFileWithNoise);
366 | assert.instanceOf(publicNodeRSA.keyPair, Object);
367 | assert(publicNodeRSA.isPublic());
368 | assert(publicNodeRSA.isPublic(true));
369 | assert(!publicNodeRSA.isPrivate());
370 | });
371 |
372 | it('should handle data without begin/end encapsulation boundaries for pkcs1 private keys', function () {
373 | let privateFile = fs.readFileSync(keysFolder + 'private_pkcs1.pem', "utf8");
374 | let privateFileNoBoundaries = privateFile.substring("-----BEGIN RSA PRIVATE KEY-----".length, privateFile.indexOf("-----END RSA PRIVATE KEY-----"));
375 | let key = new NodeRSA(privateFileNoBoundaries, "pkcs1-private-pem");
376 | assert.equal(key.exportKey(), fileKeyPKCS1);
377 | });
378 |
379 | it('should handle data without begin/end encapsulation boundaries for pkcs1 public keys', function () {
380 | let publicFile = fs.readFileSync(keysFolder + 'public_pkcs1.pem', "utf8");
381 | let publicFileNoBoundaries = publicFile.substring("-----BEGIN RSA PUBLIC KEY-----".length, publicFile.indexOf("-----END RSA PUBLIC KEY-----"));
382 | let publicNodeRSA = new NodeRSA(publicFileNoBoundaries, "pkcs1-public-pem");
383 | assert.instanceOf(publicNodeRSA.keyPair, Object);
384 | assert(publicNodeRSA.isPublic());
385 | assert(publicNodeRSA.isPublic(true));
386 | assert(!publicNodeRSA.isPrivate());
387 | });
388 |
389 | it('should handle data without begin/end encapsulation boundaries for pkcs8 private keys', function () {
390 | let privateFile = fs.readFileSync(keysFolder + 'private_pkcs8.pem', "utf8");
391 | let privateFileNoBoundaries = privateFile.substring('-----BEGIN PRIVATE KEY-----'.length, privateFile.indexOf('-----END PRIVATE KEY-----'));
392 | let key = new NodeRSA(privateFileNoBoundaries, "pkcs8-private-pem");
393 | assert.equal(key.exportKey(), fileKeyPKCS1);
394 | });
395 |
396 | it('should handle data without begin/end encapsulation boundaries for pkcs8 public keys', function () {
397 | let publicFile = fs.readFileSync(keysFolder + 'public_pkcs8.pem', "utf8");
398 | let publicFileNoBoundaries = publicFile.substring("-----BEGIN PUBLIC KEY-----".length, publicFile.indexOf("-----END PUBLIC KEY-----"));
399 | let publicNodeRSA = new NodeRSA(publicFileNoBoundaries, "pkcs8-public-pem");
400 | assert.instanceOf(publicNodeRSA.keyPair, Object);
401 | assert(publicNodeRSA.isPublic());
402 | assert(publicNodeRSA.isPublic(true));
403 | assert(!publicNodeRSA.isPrivate());
404 | });
405 |
406 | it('.importKey() from private components', function () {
407 | var key = new NodeRSA();
408 | key.importKey({
409 | n: Buffer.from(privateKeyComponents.n, 'base64'),
410 | e: 65537,
411 | d: Buffer.from(privateKeyComponents.d, 'base64'),
412 | p: Buffer.from(privateKeyComponents.p, 'base64'),
413 | q: Buffer.from(privateKeyComponents.q, 'base64'),
414 | dmp1: Buffer.from(privateKeyComponents.dmp1, 'base64'),
415 | dmq1: Buffer.from(privateKeyComponents.dmq1, 'base64'),
416 | coeff: Buffer.from(privateKeyComponents.coeff, 'base64')
417 | }, 'components');
418 | assert(key.isPrivate());
419 | assert.equal(key.exportKey('pkcs1-private'), privateKeyPKCS1);
420 | assert.equal(key.exportKey('pkcs8-public'), publicKeyPKCS8);
421 | });
422 |
423 | it('.importKey() from public components', function () {
424 | var key = new NodeRSA();
425 | key.importKey({
426 | n: Buffer.from(publicKeyComponents.n, 'base64'),
427 | e: 65537
428 | }, 'components-public');
429 | assert(key.isPublic(true));
430 | assert.equal(key.exportKey('pkcs8-public'), publicKeyPKCS8);
431 | });
432 |
433 | it('.exportKey() private components', function () {
434 | var key = new NodeRSA(privateKeyPKCS1);
435 | var components = key.exportKey('components');
436 | assert(_.isEqual({
437 | n: components.n.toString('base64'),
438 | e: components.e,
439 | d: components.d.toString('base64'),
440 | p: components.p.toString('base64'),
441 | q: components.q.toString('base64'),
442 | dmp1: components.dmp1.toString('base64'),
443 | dmq1: components.dmq1.toString('base64'),
444 | coeff: components.coeff.toString('base64')
445 | }, privateKeyComponents));
446 | });
447 |
448 | it('.exportKey() public components', function () {
449 | var key = new NodeRSA(publicKeyPKCS8);
450 | var components = key.exportKey('components-public');
451 | assert(_.isEqual({
452 | n: components.n.toString('base64'),
453 | e: components.e
454 | }, publicKeyComponents));
455 | });
456 | });
457 |
458 | describe('Different key formats', function () {
459 | var sampleKey = new NodeRSA(fileKeyPKCS1);
460 |
461 | for (var format in keys_formats) {
462 | (function (format) {
463 | var options = keys_formats[format];
464 |
465 | it('should load from ' + options.file + ' (' + format + ')', function () {
466 | var key = new NodeRSA(fs.readFileSync(keysFolder + options.file), format);
467 | if (options.public) {
468 | assert.equal(key.exportKey('public'), sampleKey.exportKey('public'));
469 | } else {
470 | assert.equal(key.exportKey(), sampleKey.exportKey());
471 | }
472 | });
473 |
474 | it('should export to \'' + format + '\' format', function () {
475 | var keyData = fs.readFileSync(keysFolder + options.file);
476 | var exported = sampleKey.exportKey(format);
477 |
478 | if (options.der) {
479 | assert(Buffer.isBuffer(exported));
480 | assert.equal(exported.toString('hex'), keyData.toString('hex'));
481 | } else {
482 | assert(_.isString(exported));
483 | assert.equal(exported.replace(/\s+|\n\r|\n|\r$/gm, ''), keyData.toString('utf8').replace(/\s+|\n\r|\n|\r$/gm, ''));
484 | }
485 | });
486 | })(format);
487 | }
488 | });
489 |
490 | describe('OpenSSH keys', function () {
491 | /*
492 | * Warning!
493 | * OpenSSH private key contains unused 64bit value, this value is set by ssh-keygen,
494 | * but it's not used. NodeRSA does NOT store this value, so importing and exporting key sets this value to 0.
495 | * This value is 0 in test files, so the tests pass.
496 | */
497 | it('key export should preserve key data including comment', function(){
498 | const opensshPrivateKey = fs.readFileSync(keysFolder + 'id_rsa_comment').toString();
499 | const opensshPublicKey = fs.readFileSync(keysFolder + 'id_rsa_comment.pub').toString();
500 | const opensshPriv = new NodeRSA(opensshPrivateKey);
501 | const opensshPub = new NodeRSA(opensshPublicKey);
502 |
503 | assert.equal(
504 | opensshPriv.exportKey('openssh-private'),
505 | opensshPrivateKey
506 | );
507 |
508 | assert.equal(
509 | opensshPriv.exportKey('openssh-public'),
510 | opensshPublicKey
511 | );
512 |
513 | assert.equal(
514 | opensshPub.exportKey('openssh-public'),
515 | opensshPublicKey
516 | );
517 | });
518 | })
519 | });
520 |
521 | describe('Bad cases', function () {
522 | it('not public key', function () {
523 | var key = new NodeRSA();
524 | assert.throw(function () {
525 | key.exportKey();
526 | }, Error, 'This is not private key');
527 | assert.throw(function () {
528 | key.exportKey('public');
529 | }, Error, 'This is not public key');
530 | });
531 |
532 | it('not private key', function () {
533 | var key = new NodeRSA(publicKeyPKCS8);
534 | assert.throw(function () {
535 | key.exportKey();
536 | }, Error, 'This is not private key');
537 | assert.doesNotThrow(function () {
538 | key.exportKey('public');
539 | }, Error, 'This is not public key');
540 | });
541 | });
542 | });
543 | });
544 |
545 | describe('Encrypting & decrypting', function () {
546 | for (var env in environments) {
547 | (function (env) {
548 | for (var scheme_i in encryptSchemes) {
549 | (function (scheme) {
550 | describe('Environment: ' + env + '. Encryption scheme: ' + scheme, function () {
551 | describe('Good cases', function () {
552 | var encrypted = {};
553 | var decrypted = {};
554 | for (var i in dataBundle) {
555 | (function (i) {
556 | var key = null;
557 | var suit = dataBundle[i];
558 |
559 | it('`encrypt()` should encrypt ' + i, function () {
560 | key = new NodeRSA(generatedKeys[Math.round(Math.random() * 1000) % generatedKeys.length].exportKey(), {
561 | environment: env,
562 | encryptionScheme: scheme
563 | });
564 | encrypted[i] = key.encrypt(suit.data);
565 | assert(Buffer.isBuffer(encrypted[i]));
566 | assert(encrypted[i].length > 0);
567 | });
568 |
569 | it('`decrypt()` should decrypt ' + i, function () {
570 | decrypted[i] = key.decrypt(encrypted[i], _.isArray(suit.encoding) ? suit.encoding[0] : suit.encoding);
571 | if (Buffer.isBuffer(decrypted[i])) {
572 | assert.equal(suit.data.toString('hex'), decrypted[i].toString('hex'));
573 | } else {
574 | assert(_.isEqual(suit.data, decrypted[i]));
575 | }
576 | });
577 | })(i);
578 | }
579 |
580 |
581 | });
582 |
583 | describe('Bad cases', function () {
584 | it('unsupported data types', function () {
585 | assert.throw(function () {
586 | generatedKeys[0].encrypt(null);
587 | }, Error, 'Unexpected data type');
588 | assert.throw(function () {
589 | generatedKeys[0].encrypt(undefined);
590 | }, Error, 'Unexpected data type');
591 | assert.throw(function () {
592 | generatedKeys[0].encrypt(true);
593 | }, Error, 'Unexpected data type');
594 | });
595 |
596 | it('incorrect key for decrypting', function () {
597 | var encrypted = generatedKeys[0].encrypt('data');
598 | assert.throw(function () {
599 | generatedKeys[1].decrypt(encrypted);
600 | }, Error, 'Error during decryption');
601 | });
602 | });
603 | });
604 | })(encryptSchemes[scheme_i]);
605 | }
606 |
607 | describe('Environment: ' + env + '. encryptPrivate & decryptPublic', function () {
608 | var encrypted = {};
609 | var decrypted = {};
610 | for (var i in dataBundle) {
611 | (function (i) {
612 | var key = null;
613 | var suit = dataBundle[i];
614 |
615 | it('`encryptPrivate()` should encrypt ' + i, function () {
616 | key = new NodeRSA(generatedKeys[Math.round(Math.random() * 1000) % generatedKeys.length].exportKey(), {
617 | environment: env
618 | });
619 | encrypted[i] = key.encryptPrivate(suit.data);
620 | assert(Buffer.isBuffer(encrypted[i]));
621 | assert(encrypted[i].length > 0);
622 | });
623 |
624 | it('`decryptPublic()` should decrypt ' + i, function () {
625 | decrypted[i] = key.decryptPublic(encrypted[i], _.isArray(suit.encoding) ? suit.encoding[0] : suit.encoding);
626 | if (Buffer.isBuffer(decrypted[i])) {
627 | assert.equal(suit.data.toString('hex'), decrypted[i].toString('hex'));
628 | } else {
629 | assert(_.isEqual(suit.data, decrypted[i]));
630 | }
631 | });
632 | })(i);
633 | }
634 | });
635 | })(environments[env]);
636 | }
637 |
638 | describe('Compatibility of different environments', function () {
639 | for (var scheme_i in encryptSchemes) {
640 | (function (scheme) {
641 | var encrypted = {};
642 | var decrypted = {};
643 | for (var i in dataBundle) {
644 | (function (i) {
645 | var key1 = null;
646 | var key2 = null;
647 | var suit = dataBundle[i];
648 |
649 | it('Encryption scheme: ' + scheme + ' `encrypt()` by browser ' + i, function () {
650 | var key = generatedKeys[Math.round(Math.random() * 1000) % generatedKeys.length].exportKey();
651 | key1 = new NodeRSA(key, {
652 | environment: 'browser',
653 | encryptionScheme: scheme
654 | });
655 | key2 = new NodeRSA(key, {
656 | environment: 'node',
657 | encryptionScheme: scheme
658 | });
659 | encrypted[i] = key1.encrypt(suit.data);
660 | assert(Buffer.isBuffer(encrypted[i]));
661 | assert(encrypted[i].length > 0);
662 | });
663 |
664 | it('Encryption scheme: ' + scheme + ' `decrypt()` by node ' + i, function () {
665 | decrypted[i] = key2.decrypt(encrypted[i], _.isArray(suit.encoding) ? suit.encoding[0] : suit.encoding);
666 | if (Buffer.isBuffer(decrypted[i])) {
667 | assert.equal(suit.data.toString('hex'), decrypted[i].toString('hex'));
668 | } else {
669 | assert(_.isEqual(suit.data, decrypted[i]));
670 | }
671 | });
672 | })(i);
673 | }
674 |
675 | encrypted = {};
676 | decrypted = {};
677 | for (var i in dataBundle) {
678 | (function (i) {
679 | var key1 = null;
680 | var key2 = null;
681 | var suit = dataBundle[i];
682 |
683 | it('Encryption scheme: ' + scheme + ' `encrypt()` by node ' + i + '. Scheme', function () {
684 | var key = generatedKeys[Math.round(Math.random() * 1000) % generatedKeys.length].exportKey();
685 | key1 = new NodeRSA(key, {
686 | environment: 'node',
687 | encryptionScheme: scheme
688 | });
689 | key2 = new NodeRSA(key, {
690 | environment: 'browser',
691 | encryptionScheme: scheme
692 | });
693 | encrypted[i] = key1.encrypt(suit.data);
694 | assert(Buffer.isBuffer(encrypted[i]));
695 | assert(encrypted[i].length > 0);
696 | });
697 |
698 | it('Encryption scheme: ' + scheme + ' `decrypt()` by browser ' + i, function () {
699 | decrypted[i] = key2.decrypt(encrypted[i], _.isArray(suit.encoding) ? suit.encoding[0] : suit.encoding);
700 | if (Buffer.isBuffer(decrypted[i])) {
701 | assert.equal(suit.data.toString('hex'), decrypted[i].toString('hex'));
702 | } else {
703 | assert(_.isEqual(suit.data, decrypted[i]));
704 | }
705 | });
706 | })(i);
707 | }
708 | })(encryptSchemes[scheme_i]);
709 | }
710 |
711 | describe('encryptPrivate & decryptPublic', function () {
712 | var encrypted = {};
713 | var decrypted = {};
714 | for (var i in dataBundle) {
715 | (function (i) {
716 | var key1 = null;
717 | var key2 = null;
718 | var suit = dataBundle[i];
719 |
720 | it('`encryptPrivate()` by browser ' + i, function () {
721 | var key = generatedKeys[Math.round(Math.random() * 1000) % generatedKeys.length].exportKey();
722 | key1 = new NodeRSA(key, {environment: 'browser'});
723 | key2 = new NodeRSA(key, {environment: 'node'});
724 | encrypted[i] = key1.encryptPrivate(suit.data);
725 | assert(Buffer.isBuffer(encrypted[i]));
726 | assert(encrypted[i].length > 0);
727 | });
728 |
729 | it('`decryptPublic()` by node ' + i, function () {
730 | decrypted[i] = key2.decryptPublic(encrypted[i], _.isArray(suit.encoding) ? suit.encoding[0] : suit.encoding);
731 | if (Buffer.isBuffer(decrypted[i])) {
732 | assert.equal(suit.data.toString('hex'), decrypted[i].toString('hex'));
733 | } else {
734 | assert(_.isEqual(suit.data, decrypted[i]));
735 | }
736 | });
737 | })(i);
738 | }
739 |
740 | for (var i in dataBundle) {
741 | (function (i) {
742 | var key1 = null;
743 | var key2 = null;
744 | var suit = dataBundle[i];
745 |
746 | it('`encryptPrivate()` by node ' + i, function () {
747 | var key = generatedKeys[Math.round(Math.random() * 1000) % generatedKeys.length].exportKey();
748 | key1 = new NodeRSA(key, {environment: 'browser'});
749 | key2 = new NodeRSA(key, {environment: 'node'});
750 | encrypted[i] = key1.encryptPrivate(suit.data);
751 | assert(Buffer.isBuffer(encrypted[i]));
752 | assert(encrypted[i].length > 0);
753 | });
754 |
755 | it('`decryptPublic()` by browser ' + i, function () {
756 | decrypted[i] = key2.decryptPublic(encrypted[i], _.isArray(suit.encoding) ? suit.encoding[0] : suit.encoding);
757 | if (Buffer.isBuffer(decrypted[i])) {
758 | assert.equal(suit.data.toString('hex'), decrypted[i].toString('hex'));
759 | } else {
760 | assert(_.isEqual(suit.data, decrypted[i]));
761 | }
762 | });
763 | })(i);
764 | }
765 | });
766 | });
767 | });
768 |
769 | describe('Signing & verifying', function () {
770 | for (var scheme_i in signingSchemes) {
771 | (function (scheme) {
772 | describe('Signing scheme: ' + scheme, function () {
773 | var envs = ['node'];
774 | if (scheme == 'pkcs1') {
775 | envs = environments;
776 | }
777 |
778 | for (var env in envs) {
779 | (function (env) {
780 | describe('Good cases ' + (envs.length > 1 ? ' in ' + env + ' environment' : ''), function () {
781 | var signed = {};
782 | var key = null;
783 |
784 | for (var i in dataBundle) {
785 | (function (i) {
786 | var suit = dataBundle[i];
787 | it('should sign ' + i, function () {
788 | key = new NodeRSA(generatedKeys[generatedKeys.length - 1].exportKey(), {
789 | signingScheme: scheme + '-sha256',
790 | environment: env
791 | });
792 | signed[i] = key.sign(suit.data);
793 | assert(Buffer.isBuffer(signed[i]));
794 | assert(signed[i].length > 0);
795 | });
796 |
797 | it('should verify ' + i, function () {
798 | assert(key.verify(suit.data, signed[i]));
799 | });
800 | })(i);
801 | }
802 |
803 | for (var alg in signHashAlgorithms[env]) {
804 | (function (alg) {
805 | it('signing with custom algorithm (' + alg + ')', function () {
806 | var key = new NodeRSA(generatedKeys[generatedKeys.length - 1].exportKey(), {
807 | signingScheme: scheme + '-' + alg,
808 | environment: env
809 | });
810 | var signed = key.sign('data');
811 | assert(key.verify('data', signed));
812 | });
813 |
814 | if (scheme === 'pss') {
815 | it('signing with custom algorithm (' + alg + ') with max salt length', function () {
816 | var a = alg.toLowerCase();
817 | var key = new NodeRSA(generatedKeys[generatedKeys.length - 1].exportKey(), {
818 | signingScheme: { scheme: scheme, hash: a, saltLength: OAEP.digestLength[a] },
819 | environment: env
820 | });
821 | var signed = key.sign('data');
822 | assert(key.verify('data', signed));
823 | });
824 | }
825 | })(signHashAlgorithms[env][alg]);
826 | }
827 | });
828 |
829 | describe('Bad cases' + (envs.length > 1 ? ' in ' + env + ' environment' : ''), function () {
830 | it('incorrect data for verifying', function () {
831 | var key = new NodeRSA(generatedKeys[0].exportKey(), {
832 | signingScheme: scheme + '-sha256',
833 | environment: env
834 | });
835 | var signed = key.sign('data1');
836 | assert(!key.verify('data2', signed));
837 | });
838 |
839 | it('incorrect key for signing', function () {
840 | var key = new NodeRSA(generatedKeys[0].exportKey('pkcs8-public'), {
841 | signingScheme: scheme + '-sha256',
842 | environment: env
843 | });
844 | assert.throw(function () {
845 | key.sign('data');
846 | }, Error, 'This is not private key');
847 | });
848 |
849 | it('incorrect key for verifying', function () {
850 | var key1 = new NodeRSA(generatedKeys[0].exportKey(), {
851 | signingScheme: scheme + '-sha256',
852 | environment: env
853 | });
854 | var key2 = new NodeRSA(generatedKeys[1].exportKey('pkcs8-public'), {
855 | signingScheme: scheme + '-sha256',
856 | environment: env
857 | });
858 | var signed = key1.sign('data');
859 | assert(!key2.verify('data', signed));
860 | });
861 |
862 | it('incorrect key for verifying (empty)', function () {
863 | var key = new NodeRSA(null, {environment: env});
864 |
865 | assert.throw(function () {
866 | key.verify('data', 'somesignature');
867 | }, Error, 'This is not public key');
868 | });
869 |
870 | it('different algorithms', function () {
871 | var singKey = new NodeRSA(generatedKeys[0].exportKey(), {
872 | signingScheme: scheme + '-md5',
873 | environment: env
874 | });
875 | var verifyKey = new NodeRSA(generatedKeys[0].exportKey(), {
876 | signingScheme: scheme + '-sha1',
877 | environment: env
878 | });
879 | var signed = singKey.sign('data');
880 | assert(!verifyKey.verify('data', signed));
881 | });
882 | });
883 | })(envs[env]);
884 | }
885 |
886 | if (scheme !== 'pkcs1') {
887 | return;
888 | }
889 |
890 | describe('Compatibility of different environments', function () {
891 | for (var alg in signHashAlgorithms['browser']) {
892 | (function (alg) {
893 | it('signing with custom algorithm (' + alg + ') (equal test)', function () {
894 | var nodeKey = new NodeRSA(generatedKeys[5].exportKey(), {
895 | signingScheme: scheme + '-' + alg,
896 | environment: 'node'
897 | });
898 | var browserKey = new NodeRSA(generatedKeys[5].exportKey(), {
899 | signingScheme: scheme + '-' + alg,
900 | environment: 'browser'
901 | });
902 |
903 | assert.equal(nodeKey.sign('data', 'hex'), browserKey.sign('data', 'hex'));
904 | });
905 |
906 | it('sign in node & verify in browser (' + alg + ')', function () {
907 | var nodeKey = new NodeRSA(generatedKeys[5].exportKey(), {
908 | signingScheme: scheme + '-' + alg,
909 | environment: 'node'
910 | });
911 | var browserKey = new NodeRSA(generatedKeys[5].exportKey(), {
912 | signingScheme: scheme + '-' + alg,
913 | environment: 'browser'
914 | });
915 |
916 | assert(browserKey.verify('data', nodeKey.sign('data')));
917 | });
918 |
919 | it('sign in browser & verify in node (' + alg + ')', function () {
920 | var nodeKey = new NodeRSA(generatedKeys[5].exportKey(), {
921 | signingScheme: scheme + '-' + alg,
922 | environment: 'node'
923 | });
924 | var browserKey = new NodeRSA(generatedKeys[5].exportKey(), {
925 | signingScheme: scheme + '-' + alg,
926 | environment: 'browser'
927 | });
928 |
929 | assert(nodeKey.verify('data', browserKey.sign('data')));
930 | });
931 | })(signHashAlgorithms['browser'][alg]);
932 | }
933 | });
934 | });
935 | })(signingSchemes[scheme_i]);
936 | }
937 | });
938 | });
--------------------------------------------------------------------------------