├── .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 | [![Build Status](https://travis-ci.org/rzcoder/node-rsa.svg?branch=master)](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 | }); --------------------------------------------------------------------------------