├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── build.js ├── crypto-ts.ts ├── karma.conf.js ├── license-banner.txt ├── package-lock.json ├── package.json ├── public_api.ts ├── rollup.config.js ├── rollup.es.config.js ├── spec.bundle.js ├── src ├── algo │ ├── AES.ts │ ├── EvpKDF.ts │ ├── MD5.ts │ └── SHA256.ts ├── crypto-ts.ts ├── enc │ ├── Base64.ts │ ├── Encoding.ts │ ├── Hex.ts │ ├── Latin1.ts │ ├── Utf8.ts │ └── index.ts ├── format │ ├── Formatter.ts │ └── OpenSSL.ts ├── kdf │ ├── KDF.ts │ └── OpenSSLKdf.ts ├── lib │ ├── Base.ts │ ├── BlockCipher.ts │ ├── BufferedBlockAlgorithm.ts │ ├── BufferedBlockAlgorithmConfig.ts │ ├── Cipher.ts │ ├── CipherParams.ts │ ├── CipherParamsInterface.ts │ ├── Hasher.ts │ ├── PasswordBasedCipher.ts │ ├── SerializableCipher.ts │ └── WordArray.ts ├── mode │ ├── BlockCipherMode.ts │ ├── BlockCipherModeAlgorithm.ts │ ├── CBC.ts │ ├── CBCDecryptor.ts │ ├── CBCEncryptor.ts │ ├── ECB.ts │ ├── ECBDecryptor.ts │ └── ECBEncryptor.ts └── pad │ ├── NoPadding.ts │ ├── PKCS7.ts │ └── Padding.ts ├── tests └── algo │ └── AES.spec.ts ├── travis-deploy.sh ├── tsconfig-build.json ├── tsconfig-test.json ├── tsconfig.json └── tslint.json /.gitignore: -------------------------------------------------------------------------------- 1 | $ cat .gitignore 2 | /node_modules 3 | /dist 4 | /documentation 5 | /coverage 6 | 7 | *.log 8 | *.tgz 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | addons: 4 | apt: 5 | sources: 6 | - google-chrome 7 | packages: 8 | - google-chrome-stable 9 | language: node_js 10 | node_js: 11 | - "8" 12 | before_install: 13 | - npm i npm@^4 -g 14 | install: 15 | - npm install 16 | script: 17 | - npm test 18 | - npm run build 19 | deploy: 20 | provider: script 21 | skip_cleanup: true 22 | script: bash travis-deploy.sh 23 | before_script: 24 | - export DISPLAY=:99.0 25 | - sh -e /etc/init.d/xvfb start 26 | - sleep 3 27 | notifications: 28 | email: true 29 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Angular library starter Changelog 2 | 3 | 4 | ### Jan 15, 2018 5 | * Add instructions for inlining of templates and stylesheets 6 | 7 | 8 | ### Nov 2, 2017 9 | * Upgrade to Angular v5 building process 10 | * Upgrade to rollup ^0.48.0 and UglifyJS 3 11 | * Add license banner to the bundles 12 | 13 | In Angular v5, the building process creates the _es2015_ bundles in _esm2015_ folder, 14 | and the _es5_ bundles in _esm5_ folder. If you want to upgrade to the new building process you have to: 15 | - use new _build.js_ (changing the _PACKAGE_ name), 16 | - use new _rollup.config.js_ (updating _globals_ and _name_), _rollup.es.config.js_ and _tsconfig-build.js_ files 17 | - add the _license-banner.txt_ file (and customize it) 18 | - update in _package.json_: 19 | - _module_ and _es2015_ properties 20 | - packages: compare all the _devDependencies_ 21 | - add _tslib_ (TypeScript helpers) to dependencies 22 | - remove _scripts/map-sources.js_ file 23 | 24 | For a full comparison, see the following commit: 25 | - https://github.com/robisim74/crypto-ts/commit/2acd8a632716cfd188259488710aef015336c927 26 | 27 | If you are still using _index.ts_, see also the following commit to rename it: 28 | - https://github.com/robisim74/crypto-ts/commit/583f79b1885d04cdeee897b2a7a2bc16a7564ea9 29 | 30 | 31 | ### Oct 14, 2017 32 | * Add Istanbul to report coverage 33 | 34 | 35 | ### Sep 12, 2017 36 | * Rename _index.ts_ to package name 37 | 38 | 39 | ### Jun 21, 2017 40 | * Update _rollup.config_ and _tsconfig_ files 41 | * Update building process: 42 | - TSLint 43 | - Stop building for errors on Rollup conversion 44 | - Use local import for ShellJS 45 | * Add sourcemap files for testing to _karma.conf_ & use ES2015 syntax in _spec.bundle_ 46 | * Update Codelyzer rules 47 | * Update packages 48 | 49 | 50 | ### May 2, 2017 51 | * Upgrade versions & enable _strict_ TypeScript compiler option 52 | 53 | 54 | ### Mar 25, 2017 55 | * Upgrade to Angular 4 configuration 56 | 57 | 58 | ### Mar 6, 2017 59 | * Add _compodoc_ for generating documentation 60 | 61 | 62 | ### Feb 5, 2017 63 | * Create library -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Hans Moog 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # crypto-ts 2 | 3 | Typescript library of crypto standards. Ready for AOT and treeshaking in combination with Angular and other modern typescript frameworks. 4 | 5 | ## Node.js (Install) 6 | 7 | Requirements: 8 | 9 | - Node.js 10 | - npm (Node.js package manager) 11 | 12 | ```bash 13 | npm install crypto-ts 14 | ``` 15 | 16 | ### Usage 17 | 18 | ES6 import for typical API call signing use case: 19 | 20 | ```javascript 21 | import { AES } from 'crypto-ts'; 22 | 23 | const encryptedMessage = AES.encrypt('message', 'test').toString(); 24 | ``` 25 | 26 | Modular include: 27 | 28 | ```javascript 29 | var AES = require("crypto-ts").AES; 30 | var SHA256 = require("crypto-ts").SHA256; 31 | ... 32 | console.log(SHA256("Message")); 33 | ``` 34 | 35 | Including all libraries, for access to extra methods: 36 | 37 | ```javascript 38 | var CryptoTS = require("crypto-ts"); 39 | ... 40 | console.log(CryptoTS.HmacSHA1("Message", "Key")); 41 | ``` 42 | 43 | ## Client (browser) 44 | 45 | Requirements: 46 | 47 | - Node.js 48 | - Bower (package manager for frontend) 49 | 50 | ```bash 51 | bower install crypto-ts 52 | ``` 53 | 54 | ### Usage 55 | 56 | Modular include: 57 | 58 | ```javascript 59 | require.config({ 60 | packages: [ 61 | { 62 | name: 'crypto-ts', 63 | location: 'path-to/bower_components/crypto-ts', 64 | main: 'index' 65 | } 66 | ] 67 | }); 68 | 69 | require(["crypto-ts/algo/aes", "crypto-ts/algo/sha256"], function (AES, SHA256) { 70 | console.log(SHA256("Message")); 71 | }); 72 | ``` 73 | 74 | Including all libraries, for access to extra methods: 75 | 76 | ```javascript 77 | // Above-mentioned will work or use this simple form 78 | require.config({ 79 | paths: { 80 | 'crypto-ts': 'path-to/bower_components/crypto-ts/crypto-ts' 81 | } 82 | }); 83 | 84 | require(["crypto-ts"], function (CryptoTS) { 85 | console.log(CryptoTS.MD5("Message")); 86 | }); 87 | ``` 88 | 89 | ### Usage without RequireJS 90 | 91 | ```html 92 | 93 | 97 | ``` 98 | 99 | ### AES Encryption 100 | 101 | #### Plain text encryption 102 | 103 | ```javascript 104 | var CryptoTS = require("crypto-ts"); 105 | 106 | // Encrypt 107 | var ciphertext = CryptoTS.AES.encrypt('my message', 'secret key 123'); 108 | 109 | // Decrypt 110 | var bytes = CryptoTS.AES.decrypt(ciphertext.toString(), 'secret key 123'); 111 | var plaintext = bytes.toString(CryptoTS.enc.Utf8); 112 | 113 | console.log(plaintext); 114 | ``` 115 | 116 | #### Object encryption 117 | 118 | ```javascript 119 | var CryptoTS = require("crypto-ts"); 120 | 121 | var data = [{id: 1}, {id: 2}] 122 | 123 | // Encrypt 124 | var ciphertext = CryptoTS.AES.encrypt(JSON.stringify(data), 'secret key 123'); 125 | 126 | // Decrypt 127 | var bytes = CryptoTS.AES.decrypt(ciphertext.toString(), 'secret key 123'); 128 | var decryptedData = JSON.parse(bytes.toString(CryptoTS.enc.Utf8)); 129 | 130 | console.log(decryptedData); 131 | ``` 132 | 133 | ### List of modules 134 | 135 | 136 | - ```crypto-ts/core``` 137 | - ```crypto-ts/x64-core``` 138 | - ```crypto-ts/lib-typedarrays``` 139 | 140 | --- 141 | 142 | - ```crypto-ts/md5``` 143 | - ```crypto-ts/sha1``` 144 | - ```crypto-ts/sha256``` 145 | - ```crypto-ts/sha224``` 146 | - ```crypto-ts/sha512``` 147 | - ```crypto-ts/sha384``` 148 | - ```crypto-ts/sha3``` 149 | - ```crypto-ts/ripemd160``` 150 | 151 | --- 152 | 153 | - ```crypto-ts/hmac-md5``` 154 | - ```crypto-ts/hmac-sha1``` 155 | - ```crypto-ts/hmac-sha256``` 156 | - ```crypto-ts/hmac-sha224``` 157 | - ```crypto-ts/hmac-sha512``` 158 | - ```crypto-ts/hmac-sha384``` 159 | - ```crypto-ts/hmac-sha3``` 160 | - ```crypto-ts/hmac-ripemd160``` 161 | 162 | --- 163 | 164 | - ```crypto-ts/pbkdf2``` 165 | 166 | --- 167 | 168 | - ```crypto-ts/aes``` 169 | - ```crypto-ts/tripledes``` 170 | - ```crypto-ts/rc4``` 171 | - ```crypto-ts/rabbit``` 172 | - ```crypto-ts/rabbit-legacy``` 173 | - ```crypto-ts/evpkdf``` 174 | 175 | --- 176 | 177 | - ```crypto-ts/format-openssl``` 178 | - ```crypto-ts/format-hex``` 179 | 180 | --- 181 | 182 | - ```crypto-ts/enc-latin1``` 183 | - ```crypto-ts/enc-utf8``` 184 | - ```crypto-ts/enc-hex``` 185 | - ```crypto-ts/enc-utf16``` 186 | - ```crypto-ts/enc-base64``` 187 | 188 | --- 189 | 190 | - ```crypto-ts/mode-cfb``` 191 | - ```crypto-ts/mode-ctr``` 192 | - ```crypto-ts/mode-ctr-gladman``` 193 | - ```crypto-ts/mode-ofb``` 194 | - ```crypto-ts/mode-ecb``` 195 | 196 | --- 197 | 198 | - ```crypto-ts/pad-pkcs7``` 199 | - ```crypto-ts/pad-ansix923``` 200 | - ```crypto-ts/pad-iso10126``` 201 | - ```crypto-ts/pad-iso97971``` 202 | - ```crypto-ts/pad-zeropadding``` 203 | - ```crypto-ts/pad-nopadding``` -------------------------------------------------------------------------------- /build.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const shell = require('shelljs'); 4 | const chalk = require('chalk'); 5 | 6 | const PACKAGE = `crypto-ts`; 7 | const NPM_DIR = `dist`; 8 | const ESM2015_DIR = `${NPM_DIR}/esm2015`; 9 | const ESM5_DIR = `${NPM_DIR}/esm5`; 10 | const BUNDLES_DIR = `${NPM_DIR}/bundles`; 11 | const OUT_DIR_ESM5 = `${NPM_DIR}/package/esm5`; 12 | 13 | shell.echo(`Start building...`); 14 | 15 | shell.rm(`-Rf`, `${NPM_DIR}/*`); 16 | shell.mkdir(`-p`, `./${ESM2015_DIR}`); 17 | shell.mkdir(`-p`, `./${ESM5_DIR}`); 18 | shell.mkdir(`-p`, `./${BUNDLES_DIR}`); 19 | 20 | /* TSLint with Codelyzer */ 21 | // https://github.com/palantir/tslint/blob/master/src/configs/recommended.ts 22 | // https://github.com/mgechev/codelyzer 23 | shell.echo(`Start TSLint`); 24 | shell.exec(`tslint -p tsconfig.json -t stylish src/**/*.ts`); 25 | shell.echo(chalk.green(`TSLint completed`)); 26 | 27 | /* AoT compilation */ 28 | shell.echo(`Start AoT compilation`); 29 | if (shell.exec(`ngc -p tsconfig-build.json`).code !== 0) { 30 | shell.echo(chalk.red(`Error: AoT compilation failed`)); 31 | shell.exit(1); 32 | } 33 | shell.echo(chalk.green(`AoT compilation completed`)); 34 | 35 | /* BUNDLING PACKAGE */ 36 | shell.echo(`Start bundling`); 37 | shell.echo(`Rollup package`); 38 | if (shell.exec(`rollup -c rollup.es.config.js -i ${NPM_DIR}/${PACKAGE}.js -o ${ESM2015_DIR}/${PACKAGE}.js`).code !== 0) { 39 | shell.echo(chalk.red(`Error: Rollup package failed`)); 40 | shell.exit(1); 41 | } 42 | 43 | shell.echo(`Produce ESM5 version`); 44 | shell.exec(`ngc -p tsconfig-build.json --target es5 -d false --outDir ${OUT_DIR_ESM5} --importHelpers true --sourceMap`); 45 | if (shell.exec(`rollup -c rollup.es.config.js -i ${OUT_DIR_ESM5}/${PACKAGE}.js -o ${ESM5_DIR}/${PACKAGE}.js`).code !== 0) { 46 | shell.echo(chalk.red(`Error: ESM5 version failed`)); 47 | shell.exit(1); 48 | } 49 | 50 | shell.echo(`Run Rollup conversion on package`); 51 | if (shell.exec(`rollup -c rollup.config.js -i ${ESM5_DIR}/${PACKAGE}.js -o ${BUNDLES_DIR}/${PACKAGE}.umd.js`).code !== 0) { 52 | shell.echo(chalk.red(`Error: Rollup conversion failed`)); 53 | shell.exit(1); 54 | } 55 | 56 | shell.echo(`Minifying`); 57 | shell.cd(`${BUNDLES_DIR}`); 58 | shell.exec(`uglifyjs ${PACKAGE}.umd.js -c --comments -o ${PACKAGE}.umd.min.js --source-map "filename='${PACKAGE}.umd.min.js.map', includeSources"`); 59 | shell.cd(`..`); 60 | shell.cd(`..`); 61 | 62 | shell.echo(chalk.green(`Bundling completed`)); 63 | 64 | shell.rm(`-Rf`, `${NPM_DIR}/package`); 65 | shell.rm(`-Rf`, `${NPM_DIR}/node_modules`); 66 | shell.rm(`-Rf`, `${NPM_DIR}/*.js`); 67 | shell.rm(`-Rf`, `${NPM_DIR}/*.js.map`); 68 | shell.rm(`-Rf`, `${NPM_DIR}/src/**/*.js`); 69 | shell.rm(`-Rf`, `${NPM_DIR}/src/**/*.js.map`); 70 | 71 | shell.cp(`-Rf`, [`package.json`, `LICENSE`, `README.md`], `${NPM_DIR}`); 72 | 73 | shell.sed('-i', `"private": true,`, `"private": false,`, `./${NPM_DIR}/package.json`); 74 | 75 | shell.echo(chalk.green(`End building`)); 76 | -------------------------------------------------------------------------------- /crypto-ts.ts: -------------------------------------------------------------------------------- 1 | // This file is not used to build the module. It is only used during editing 2 | // by the TypeScript language service and during build for verification. `ngc` 3 | // replaces this file with production crypto-ts.ts when it rewrites 4 | // private symbol names. 5 | 6 | export * from './public_api'; 7 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration for Unit testing 2 | 3 | const path = require('path'); 4 | 5 | module.exports = function (config) { 6 | 7 | const configuration = { 8 | 9 | // base path that will be used to resolve all patterns (eg. files, exclude) 10 | basePath: '', 11 | 12 | // frameworks to use 13 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 14 | frameworks: ['jasmine'], 15 | 16 | plugins: [ 17 | require('karma-jasmine'), 18 | require('karma-chrome-launcher'), 19 | require('karma-webpack'), 20 | require('karma-sourcemap-loader'), 21 | require('karma-spec-reporter'), 22 | require('karma-coverage-istanbul-reporter'), 23 | require("istanbul-instrumenter-loader") 24 | ], 25 | 26 | // list of files / patterns to load in the browser 27 | files: [ 28 | { pattern: 'spec.bundle.js', watched: false } 29 | ], 30 | 31 | // list of files to exclude 32 | exclude: [ 33 | ], 34 | 35 | // preprocess matching files before serving them to the browser 36 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 37 | preprocessors: { 38 | 'spec.bundle.js': ['webpack', 'sourcemap'] 39 | }, 40 | 41 | // webpack 42 | webpack: { 43 | mode: 'development', 44 | 45 | resolve: { 46 | extensions: ['.ts', '.js'] 47 | }, 48 | module: { 49 | rules: [ 50 | { 51 | test: /\.ts/, 52 | use: [ 53 | { loader: 'ts-loader' }, 54 | { loader: 'source-map-loader' } 55 | ], 56 | exclude: /node_modules/ 57 | }, 58 | { 59 | enforce: 'post', 60 | test: /\.ts/, 61 | use: [ 62 | { 63 | loader: 'istanbul-instrumenter-loader', 64 | options: { esModules: true } 65 | } 66 | ], 67 | exclude: [ 68 | /\.spec.ts/, 69 | /node_modules/ 70 | ] 71 | } 72 | ], 73 | exprContextCritical: false 74 | }, 75 | devtool: 'inline-source-map', 76 | performance: { hints: false } 77 | }, 78 | 79 | webpackServer: { 80 | noInfo: true 81 | }, 82 | 83 | 84 | // test results reporter to use 85 | // possible values: 'dots', 'progress' 86 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 87 | reporters: ['spec', 'coverage-istanbul'], 88 | 89 | coverageIstanbulReporter: { 90 | reports: ['html', 'lcovonly'], 91 | dir: path.join(__dirname, 'coverage'), 92 | fixWebpackSourcePaths: true 93 | }, 94 | 95 | 96 | // web server port 97 | port: 9876, 98 | 99 | 100 | // enable / disable colors in the output (reporters and logs) 101 | colors: true, 102 | 103 | 104 | // level of logging 105 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 106 | logLevel: config.LOG_INFO, 107 | 108 | 109 | // enable / disable watching file and executing tests whenever any file changes 110 | autoWatch: true, 111 | 112 | 113 | // start these browsers 114 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 115 | browsers: ['Chrome'], 116 | 117 | 118 | // Continuous Integration mode 119 | // if true, Karma captures browsers, runs the tests and exits 120 | singleRun: true 121 | 122 | }; 123 | 124 | config.set(configuration); 125 | 126 | } 127 | -------------------------------------------------------------------------------- /license-banner.txt: -------------------------------------------------------------------------------- 1 | /** 2 | * @license crypto-ts 3 | * MIT license 4 | */ 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crypto-ts", 3 | "private": false, 4 | "version": "0.0.0-development", 5 | "description": "Typescript library of crypto standards.", 6 | "main": "./bundles/crypto-ts.umd.js", 7 | "module": "./esm5/crypto-ts.js", 8 | "es2015": "./esm2015/crypto-ts.js", 9 | "scripts": { 10 | "build": "node build.js", 11 | "test": "karma start", 12 | "test:watch": "karma start karma.conf.js --single-run false", 13 | "pack:lib": "npm run build && npm pack ./dist", 14 | "compodoc": "compodoc -p tsconfig.json", 15 | "compodoc:serve": "compodoc -s", 16 | "semantic-release": "semantic-release", 17 | "travis-deploy-once": "travis-deploy-once" 18 | }, 19 | "release": { 20 | "verifyConditions": [ 21 | "@semantic-release/npm", 22 | "@semantic-release/github" 23 | ], 24 | "analyzeCommits": { 25 | "preset": "eslint", 26 | "releaseRules": [ 27 | { 28 | "breaking": true, 29 | "release": "major" 30 | }, 31 | { 32 | "tag": "Docs", 33 | "release": "patch" 34 | }, 35 | { 36 | "tag": "Fix", 37 | "release": "patch" 38 | }, 39 | { 40 | "tag": "Perf", 41 | "release": "patch" 42 | }, 43 | { 44 | "tag": "Refactor", 45 | "release": "patch" 46 | }, 47 | { 48 | "tag": "Test", 49 | "release": "patch" 50 | }, 51 | { 52 | "tag": "Feat", 53 | "release": "minor" 54 | } 55 | ] 56 | }, 57 | "generateNotes": { 58 | "preset": "eslint" 59 | }, 60 | "publish": [ 61 | { 62 | "path": "@semantic-release/npm", 63 | "pkgRoot": "dist" 64 | }, 65 | { 66 | "path": "@semantic-release/github", 67 | "assets": [ 68 | { 69 | "path": "npm-package.tar.gz", 70 | "label": "NPM Package" 71 | }, 72 | { 73 | "path": "dist/bundles/crypto-ts.umd.js", 74 | "label": "crypto-ts.umd.js" 75 | }, 76 | { 77 | "path": "dist/bundles/crypto-ts.umd.min.js", 78 | "label": "crypto-ts.umd.min.js" 79 | } 80 | ] 81 | } 82 | ], 83 | "pkgRoot": "dist" 84 | }, 85 | "typings": "./crypto-ts.d.ts", 86 | "author": "", 87 | "repository": { 88 | "type": "git", 89 | "url": "https://github.com/hmoog/crypto-ts.git" 90 | }, 91 | "bugs": { 92 | "url": "https://github.com/hmoog/crypto-ts/issues" 93 | }, 94 | "homepage": "https://github.com/hmoog/crypto-ts", 95 | "keywords": [ 96 | "angular", 97 | "javascript", 98 | "typescript" 99 | ], 100 | "license": "MIT", 101 | "dependencies": { 102 | "tslib": "^1.7.1" 103 | }, 104 | "peerDependencies": { 105 | "@angular/common": ">= 5.0.0", 106 | "@angular/core": ">= 5.0.0" 107 | }, 108 | "devDependencies": { 109 | "@angular/animations": "5.2.10", 110 | "@angular/common": "5.2.10", 111 | "@angular/compiler": "5.2.10", 112 | "@angular/compiler-cli": "5.2.10", 113 | "@angular/core": "5.2.10", 114 | "@angular/platform-browser": "5.2.10", 115 | "@angular/platform-browser-dynamic": "5.2.10", 116 | "@angular/platform-server": "5.2.10", 117 | "@compodoc/compodoc": "1.1.2", 118 | "@types/jasmine": "2.8.6", 119 | "@types/node": "10.0.0", 120 | "chalk": "2.4.1", 121 | "codelyzer": "4.3.0", 122 | "conventional-changelog-eslint": "^1.0.9", 123 | "core-js": "2.5.5", 124 | "istanbul-instrumenter-loader": "3.0.1", 125 | "jasmine-core": "3.1.0", 126 | "karma": "2.0.2", 127 | "karma-chrome-launcher": "2.2.0", 128 | "karma-coverage-istanbul-reporter": "1.4.2", 129 | "karma-jasmine": "1.1.1", 130 | "karma-sourcemap-loader": "0.3.7", 131 | "karma-spec-reporter": "0.0.32", 132 | "karma-webpack": "^4.0.0-beta.0", 133 | "reflect-metadata": "0.1.12", 134 | "rollup": "0.58.2", 135 | "rollup-plugin-license": "0.6.0", 136 | "rollup-plugin-node-resolve": "3.3.0", 137 | "rollup-plugin-sourcemaps": "0.4.2", 138 | "rxjs": "6.0.0", 139 | "rxjs-compat": "^6.0.0", 140 | "semantic-release": "^15.2.0", 141 | "shelljs": "0.8.1", 142 | "source-map-loader": "0.2.3", 143 | "travis-deploy-once": "^5.0.0", 144 | "ts-loader": "4.2.0", 145 | "tslint": "5.9.1", 146 | "tslint-angular": "1.1.1", 147 | "typescript": "2.8.3", 148 | "uglify-js": "3.3.23", 149 | "webpack": "4.6.0", 150 | "zone.js": "0.8.26" 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /public_api.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Entry point for all public APIs of the package. 3 | */ 4 | export * from './src/crypto-ts'; -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-node-resolve'; 2 | import sourcemaps from 'rollup-plugin-sourcemaps'; 3 | 4 | /** 5 | * Add here external dependencies that actually you use. 6 | * 7 | * Angular dependencies 8 | * - '@angular/animations' => 'ng.animations' 9 | * - '@angular/animations/browser': 'ng.animations.browser' 10 | * - '@angular/common' => 'ng.common' 11 | * - '@angular/compiler' => 'ng.compiler' 12 | * - '@angular/core' => 'ng.core' 13 | * - '@angular/forms' => 'ng.forms' 14 | * - '@angular/common/http' => 'ng.common.http' 15 | * - '@angular/platform-browser-dynamic' => 'ng.platformBrowserDynamic' 16 | * - '@angular/platform-browser' => 'ng.platformBrowser' 17 | * - '@angular/platform-browser/animations' => 'ng.platformBrowser.animations' 18 | * - '@angular/platform-server' => 'ng.platformServer' 19 | * - '@angular/router' => 'ng.router' 20 | * 21 | * RxJS dependencies 22 | * Each RxJS functionality that you use in the library must be added as external dependency. 23 | * - For main classes use 'Rx': 24 | * e.g. import { Observable } from 'rxjs/Observable'; => 'rxjs/Observable': 'Rx' 25 | * - For observable methods use 'Rx.Observable': 26 | * e.g. import 'rxjs/add/observable/merge'; => 'rxjs/add/observable/merge': 'Rx.Observable' 27 | * or for lettable operators: 28 | * e.g. import { merge } from 'rxjs/observable/merge'; => 'rxjs/observable/merge': 'Rx.Observable' 29 | * - For operators use 'Rx.Observable.prototype': 30 | * e.g. import 'rxjs/add/operator/map'; => 'rxjs/add/operator/map': 'Rx.Observable.prototype' 31 | * or for lettable operators: 32 | * e.g. import { map } from 'rxjs/operators'; => 'rxjs/operators': 'Rx.Observable.prototype' 33 | * 34 | * Other dependencies 35 | * - Angular libraries: refer to their global namespace 36 | * - TypeScript/JavaScript libraries: 37 | * e.g. lodash: 'lodash' => 'lodash' 38 | * 39 | * Also, if the dependency uses CommonJS modules, such as lodash, 40 | * you should also use a plugin like rollup-plugin-commonjs, 41 | * to explicitly specify unresolvable "named exports". 42 | * 43 | */ 44 | const globals = { 45 | '@angular/core': 'ng.core', 46 | '@angular/common': 'ng.common', 47 | 'rxjs/Observable': 'Rx', 48 | 'rxjs/Observer': 'Rx', 49 | 'rxjs-compat/Observable': 'Rx', 50 | 'rxjs-compat/Observer': 'Rx' 51 | }; 52 | 53 | export default { 54 | external: Object.keys(globals), 55 | plugins: [resolve(), sourcemaps()], 56 | onwarn: () => { return }, 57 | output: { 58 | format: 'umd', 59 | name: 'CryptoTS', 60 | globals: globals, 61 | sourcemap: true, 62 | exports: 'named', 63 | amd: { id: 'crypto-ts' } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /rollup.es.config.js: -------------------------------------------------------------------------------- 1 | import sourcemaps from 'rollup-plugin-sourcemaps'; 2 | import license from 'rollup-plugin-license'; 3 | 4 | const path = require('path'); 5 | 6 | export default { 7 | output: { 8 | format: 'es', 9 | sourcemap: true 10 | }, 11 | plugins: [ 12 | sourcemaps(), 13 | license({ 14 | sourceMap: true, 15 | 16 | banner: { 17 | file: path.join(__dirname, 'license-banner.txt'), 18 | encoding: 'utf-8', 19 | } 20 | }) 21 | ], 22 | onwarn: () => { return } 23 | } 24 | -------------------------------------------------------------------------------- /spec.bundle.js: -------------------------------------------------------------------------------- 1 | import 'core-js'; 2 | import 'zone.js/dist/zone'; 3 | import 'zone.js/dist/long-stack-trace-zone'; 4 | import 'zone.js/dist/proxy.js'; 5 | import 'zone.js/dist/sync-test'; 6 | import 'zone.js/dist/jasmine-patch'; 7 | import 'zone.js/dist/async-test'; 8 | import 'zone.js/dist/fake-async-test'; 9 | 10 | import { getTestBed } from '@angular/core/testing'; 11 | import { 12 | BrowserDynamicTestingModule, 13 | platformBrowserDynamicTesting 14 | } from '@angular/platform-browser-dynamic/testing'; 15 | 16 | import 'rxjs'; 17 | 18 | getTestBed().initTestEnvironment( 19 | BrowserDynamicTestingModule, 20 | platformBrowserDynamicTesting() 21 | ); 22 | 23 | const testContext = require.context('./tests', true, /\.spec\.ts/); 24 | 25 | function requireAll(requireContext) { 26 | return requireContext.keys().map(requireContext); 27 | } 28 | 29 | const modules = requireAll(testContext); 30 | -------------------------------------------------------------------------------- /src/algo/AES.ts: -------------------------------------------------------------------------------- 1 | import { BlockCipher } from '../lib/BlockCipher'; 2 | import { WordArray } from '../lib/WordArray'; 3 | import { BufferedBlockAlgorithmConfig } from '../lib/BufferedBlockAlgorithmConfig'; 4 | 5 | // Define lookup tables 6 | const SBOX: Array = []; 7 | const INV_SBOX: Array = []; 8 | const SUB_MIX_0: Array = []; 9 | const SUB_MIX_1: Array = []; 10 | const SUB_MIX_2: Array = []; 11 | const SUB_MIX_3: Array = []; 12 | const INV_SUB_MIX_0: Array = []; 13 | const INV_SUB_MIX_1: Array = []; 14 | const INV_SUB_MIX_2: Array = []; 15 | const INV_SUB_MIX_3: Array = []; 16 | 17 | // Compute lookup tables 18 | (function () { 19 | // Compute double table 20 | const d = []; 21 | for (let i = 0; i < 256; i++) { 22 | if (i < 128) { 23 | d[i] = i << 1; 24 | } else { 25 | d[i] = (i << 1) ^ 0x11b; 26 | } 27 | } 28 | 29 | // Walk GF(2^8) 30 | let x = 0; 31 | let xi = 0; 32 | for (let i = 0; i < 256; i++) { 33 | // Compute sbox 34 | let sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4); 35 | sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63; 36 | SBOX[x] = sx; 37 | INV_SBOX[sx] = x; 38 | 39 | // Compute multiplication 40 | const x2 = d[x]; 41 | const x4 = d[x2]; 42 | const x8 = d[x4]; 43 | 44 | // Compute sub bytes, mix columns tables 45 | let t = (d[sx] * 0x101) ^ (sx * 0x1010100); 46 | SUB_MIX_0[x] = (t << 24) | (t >>> 8); 47 | SUB_MIX_1[x] = (t << 16) | (t >>> 16); 48 | SUB_MIX_2[x] = (t << 8) | (t >>> 24); 49 | SUB_MIX_3[x] = t; 50 | 51 | // Compute inv sub bytes, inv mix columns tables 52 | t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100); 53 | INV_SUB_MIX_0[sx] = (t << 24) | (t >>> 8); 54 | INV_SUB_MIX_1[sx] = (t << 16) | (t >>> 16); 55 | INV_SUB_MIX_2[sx] = (t << 8) | (t >>> 24); 56 | INV_SUB_MIX_3[sx] = t; 57 | 58 | // Compute next counter 59 | if (!x) { 60 | x = xi = 1; 61 | } else { 62 | x = x2 ^ d[d[d[x8 ^ x2]]]; 63 | xi ^= d[d[xi]]; 64 | } 65 | } 66 | }()); 67 | 68 | // Precomputed Rcon lookup 69 | const RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]; 70 | 71 | export class AES extends BlockCipher { 72 | // 256 / 32 73 | public static keySize = 8; 74 | 75 | _nRounds!: number; 76 | 77 | _key!: WordArray; 78 | 79 | _keyPriorReset!: WordArray; 80 | 81 | _keySchedule!: Array; 82 | 83 | _invKeySchedule!: Array; 84 | 85 | constructor(xformMode: number, key: WordArray, cfg?: BufferedBlockAlgorithmConfig) { 86 | super(xformMode, key, cfg); 87 | } 88 | 89 | reset() { 90 | // reset core values 91 | super.reset(); 92 | 93 | // Skip reset of nRounds has been set before and key did not change 94 | if (this._nRounds && this._keyPriorReset === this._key) { 95 | return; 96 | } 97 | 98 | // Shortcuts 99 | const key = this._keyPriorReset = this._key; 100 | const keyWords = key.words; 101 | const keySize = key.sigBytes / 4; 102 | 103 | // Compute number of rounds 104 | const nRounds = this._nRounds = keySize + 6; 105 | 106 | // Compute number of key schedule rows 107 | const ksRows = (nRounds + 1) * 4; 108 | 109 | // Compute key schedule 110 | const keySchedule: Array = this._keySchedule = []; 111 | for (let ksRow = 0; ksRow < ksRows; ksRow++) { 112 | if (ksRow < keySize) { 113 | keySchedule[ksRow] = keyWords[ksRow]; 114 | } else { 115 | let t = keySchedule[ksRow - 1]; 116 | 117 | if (!(ksRow % keySize)) { 118 | // Rot word 119 | t = (t << 8) | (t >>> 24); 120 | 121 | // Sub word 122 | t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff]; 123 | 124 | // Mix Rcon 125 | t ^= RCON[(ksRow / keySize) | 0] << 24; 126 | } else if (keySize > 6 && ksRow % keySize === 4) { 127 | // Sub word 128 | t = (SBOX[t >>> 24] << 24) | (SBOX[(t >>> 16) & 0xff] << 16) | (SBOX[(t >>> 8) & 0xff] << 8) | SBOX[t & 0xff]; 129 | } 130 | 131 | keySchedule[ksRow] = keySchedule[ksRow - keySize] ^ t; 132 | } 133 | } 134 | 135 | // Compute inv key schedule 136 | const invKeySchedule: Array = this._invKeySchedule = []; 137 | for (let invKsRow = 0; invKsRow < ksRows; invKsRow++) { 138 | const ksRow = ksRows - invKsRow; 139 | 140 | let t; 141 | if (invKsRow % 4) { 142 | t = keySchedule[ksRow]; 143 | } else { 144 | t = keySchedule[ksRow - 4]; 145 | } 146 | 147 | if (invKsRow < 4 || ksRow <= 4) { 148 | invKeySchedule[invKsRow] = t; 149 | } else { 150 | invKeySchedule[invKsRow] = INV_SUB_MIX_0[SBOX[t >>> 24]] ^ INV_SUB_MIX_1[SBOX[(t >>> 16) & 0xff]] ^ 151 | INV_SUB_MIX_2[SBOX[(t >>> 8) & 0xff]] ^ INV_SUB_MIX_3[SBOX[t & 0xff]]; 152 | } 153 | } 154 | } 155 | 156 | encryptBlock(M: Array, offset: number) { 157 | this._doCryptBlock(M, offset, this._keySchedule, SUB_MIX_0, SUB_MIX_1, SUB_MIX_2, SUB_MIX_3, SBOX); 158 | } 159 | 160 | decryptBlock(M: Array, offset: number) { 161 | // Swap 2nd and 4th rows 162 | let t = M[offset + 1]; 163 | M[offset + 1] = M[offset + 3]; 164 | M[offset + 3] = t; 165 | 166 | this._doCryptBlock(M, offset, this._invKeySchedule, INV_SUB_MIX_0, INV_SUB_MIX_1, INV_SUB_MIX_2, INV_SUB_MIX_3, INV_SBOX); 167 | 168 | // Inv swap 2nd and 4th rows 169 | t = M[offset + 1]; 170 | M[offset + 1] = M[offset + 3]; 171 | M[offset + 3] = t; 172 | } 173 | 174 | _doCryptBlock( 175 | M: Array, 176 | offset: number, 177 | keySchedule: Array, 178 | sub_mix_0: Array, 179 | sub_mix_1: Array, 180 | sub_mix_2: Array, 181 | sub_mix_3: Array, 182 | sbox: Array 183 | ) { 184 | // Get input, add round key 185 | let s0 = M[offset] ^ keySchedule[0]; 186 | let s1 = M[offset + 1] ^ keySchedule[1]; 187 | let s2 = M[offset + 2] ^ keySchedule[2]; 188 | let s3 = M[offset + 3] ^ keySchedule[3]; 189 | 190 | // Key schedule row counter 191 | let ksRow = 4; 192 | 193 | // Rounds 194 | for (let round = 1; round < this._nRounds; round++) { 195 | // Shift rows, sub bytes, mix columns, add round key 196 | const t0 = sub_mix_0[s0 >>> 24] ^ sub_mix_1[(s1 >>> 16) & 0xff] ^ sub_mix_2[(s2 >>> 8) & 0xff] ^ sub_mix_3[s3 & 0xff] ^ 197 | keySchedule[ksRow++]; 198 | const t1 = sub_mix_0[s1 >>> 24] ^ sub_mix_1[(s2 >>> 16) & 0xff] ^ sub_mix_2[(s3 >>> 8) & 0xff] ^ sub_mix_3[s0 & 0xff] ^ 199 | keySchedule[ksRow++]; 200 | const t2 = sub_mix_0[s2 >>> 24] ^ sub_mix_1[(s3 >>> 16) & 0xff] ^ sub_mix_2[(s0 >>> 8) & 0xff] ^ sub_mix_3[s1 & 0xff] ^ 201 | keySchedule[ksRow++]; 202 | const t3 = sub_mix_0[s3 >>> 24] ^ sub_mix_1[(s0 >>> 16) & 0xff] ^ sub_mix_2[(s1 >>> 8) & 0xff] ^ sub_mix_3[s2 & 0xff] ^ 203 | keySchedule[ksRow++]; 204 | 205 | // Update state 206 | s0 = t0; 207 | s1 = t1; 208 | s2 = t2; 209 | s3 = t3; 210 | } 211 | 212 | // Shift rows, sub bytes, add round key 213 | const t0g = ((sbox[s0 >>> 24] << 24) | (sbox[(s1 >>> 16) & 0xff] << 16) | (sbox[(s2 >>> 8) & 0xff] << 8) | sbox[s3 & 0xff]) ^ 214 | keySchedule[ksRow++]; 215 | const t1g = ((sbox[s1 >>> 24] << 24) | (sbox[(s2 >>> 16) & 0xff] << 16) | (sbox[(s3 >>> 8) & 0xff] << 8) | sbox[s0 & 0xff]) ^ 216 | keySchedule[ksRow++]; 217 | const t2g = ((sbox[s2 >>> 24] << 24) | (sbox[(s3 >>> 16) & 0xff] << 16) | (sbox[(s0 >>> 8) & 0xff] << 8) | sbox[s1 & 0xff]) ^ 218 | keySchedule[ksRow++]; 219 | const t3g = ((sbox[s3 >>> 24] << 24) | (sbox[(s0 >>> 16) & 0xff] << 16) | (sbox[(s1 >>> 8) & 0xff] << 8) | sbox[s2 & 0xff]) ^ 220 | keySchedule[ksRow++]; 221 | 222 | // Set output 223 | M[offset] = t0g; 224 | M[offset + 1] = t1g; 225 | M[offset + 2] = t2g; 226 | M[offset + 3] = t3g; 227 | } 228 | } -------------------------------------------------------------------------------- /src/algo/EvpKDF.ts: -------------------------------------------------------------------------------- 1 | import { WordArray } from '../lib/WordArray'; 2 | import { Hasher } from '../lib/Hasher'; 3 | import { MD5 } from '../algo/MD5'; 4 | 5 | export interface OptionalEvpKDFConfig { 6 | keySize?: number; 7 | hasher?: typeof Hasher; 8 | iterations?: number; 9 | } 10 | 11 | export interface EvpKDFConfig extends OptionalEvpKDFConfig { 12 | keySize: number; 13 | hasher: typeof Hasher; 14 | iterations: number; 15 | } 16 | 17 | export class EvpKDF { 18 | public cfg: EvpKDFConfig; 19 | 20 | /** 21 | * Initializes a newly created key derivation function. 22 | * 23 | * @param cfg (Optional) The configuration options to use for the derivation. 24 | * 25 | * @example 26 | * 27 | * let kdf = EvpKDF.create(); 28 | * let kdf = EvpKDF.create({ keySize: 8 }); 29 | * let kdf = EvpKDF.create({ keySize: 8, iterations: 1000 }); 30 | */ 31 | constructor(cfg?: OptionalEvpKDFConfig) { 32 | this.cfg = Object.assign({ 33 | keySize: 128 / 32, 34 | hasher: MD5, 35 | iterations: 1 36 | }, cfg); 37 | } 38 | 39 | /** 40 | * Derives a key from a password. 41 | * 42 | * @param password The password. 43 | * @param salt A salt. 44 | * 45 | * @return The derived key. 46 | * 47 | * @example 48 | * 49 | * let key = kdf.compute(password, salt); 50 | */ 51 | compute(password: WordArray | string, salt: WordArray | string): WordArray { 52 | // Init hasher 53 | const hasher = new ( this.cfg.hasher)(); 54 | 55 | // Initial values 56 | const derivedKey = new WordArray(); 57 | 58 | // Generate key 59 | let block; 60 | while(derivedKey.words.length < this.cfg.keySize) { 61 | if(block) { 62 | hasher.update(block); 63 | } 64 | block = hasher.update(password).finalize(salt); 65 | hasher.reset(); 66 | 67 | // Iterations 68 | for(let i = 1; i < this.cfg.iterations; i++) { 69 | block = hasher.finalize(block); 70 | hasher.reset(); 71 | } 72 | 73 | derivedKey.concat(block); 74 | } 75 | derivedKey.sigBytes = this.cfg.keySize * 4; 76 | 77 | return derivedKey; 78 | } 79 | } -------------------------------------------------------------------------------- /src/algo/MD5.ts: -------------------------------------------------------------------------------- 1 | import { Hasher } from '../lib/Hasher'; 2 | import { WordArray } from '../lib/WordArray'; 3 | 4 | // Constants table 5 | const T: Array = []; 6 | 7 | // Compute constants 8 | for(let i = 0; i < 64; i++) { 9 | T[i] = (Math.abs(Math.sin(i + 1)) * 0x100000000) | 0; 10 | } 11 | 12 | export class MD5 extends Hasher { 13 | public _hash!: WordArray; 14 | 15 | public static FF(a: number, b: number, c: number, d: number, x: number, s: number, t: number): number { 16 | const n = a + ((b & c) | (~b & d)) + x + t; 17 | return ((n << s) | (n >>> (32 - s))) + b; 18 | } 19 | 20 | public static GG(a: number, b: number, c: number, d: number, x: number, s: number, t: number): number { 21 | const n = a + ((b & d) | (c & ~d)) + x + t; 22 | return ((n << s) | (n >>> (32 - s))) + b; 23 | } 24 | 25 | public static HH(a: number, b: number, c: number, d: number, x: number, s: number, t: number): number { 26 | const n = a + (b ^ c ^ d) + x + t; 27 | return ((n << s) | (n >>> (32 - s))) + b; 28 | } 29 | 30 | public static II(a: number, b: number, c: number, d: number, x: number, s: number, t: number): number { 31 | const n = a + (c ^ (b | ~d)) + x + t; 32 | return ((n << s) | (n >>> (32 - s))) + b; 33 | } 34 | 35 | public reset() { 36 | // reset core values 37 | super.reset(); 38 | 39 | this._hash = new WordArray([ 40 | 0x67452301, 0xefcdab89, 41 | 0x98badcfe, 0x10325476 42 | ]); 43 | } 44 | 45 | public _doProcessBlock(M: Array, offset: number) { 46 | // Swap endian 47 | for(let i = 0; i < 16; i++) { 48 | // Shortcuts 49 | const offset_i = offset + i; 50 | const M_offset_i = M[offset_i]; 51 | 52 | M[offset_i] = ( 53 | (((M_offset_i << 8) | (M_offset_i >>> 24)) & 0x00ff00ff) | 54 | (((M_offset_i << 24) | (M_offset_i >>> 8)) & 0xff00ff00) 55 | ); 56 | } 57 | 58 | // Shortcuts 59 | const H = this._hash.words; 60 | 61 | const M_offset_0 = M[offset + 0]; 62 | const M_offset_1 = M[offset + 1]; 63 | const M_offset_2 = M[offset + 2]; 64 | const M_offset_3 = M[offset + 3]; 65 | const M_offset_4 = M[offset + 4]; 66 | const M_offset_5 = M[offset + 5]; 67 | const M_offset_6 = M[offset + 6]; 68 | const M_offset_7 = M[offset + 7]; 69 | const M_offset_8 = M[offset + 8]; 70 | const M_offset_9 = M[offset + 9]; 71 | const M_offset_10 = M[offset + 10]; 72 | const M_offset_11 = M[offset + 11]; 73 | const M_offset_12 = M[offset + 12]; 74 | const M_offset_13 = M[offset + 13]; 75 | const M_offset_14 = M[offset + 14]; 76 | const M_offset_15 = M[offset + 15]; 77 | 78 | // Working variables 79 | let a = H[0]; 80 | let b = H[1]; 81 | let c = H[2]; 82 | let d = H[3]; 83 | 84 | // Computation 85 | a = MD5.FF(a, b, c, d, M_offset_0, 7, T[0]); 86 | d = MD5.FF(d, a, b, c, M_offset_1, 12, T[1]); 87 | c = MD5.FF(c, d, a, b, M_offset_2, 17, T[2]); 88 | b = MD5.FF(b, c, d, a, M_offset_3, 22, T[3]); 89 | a = MD5.FF(a, b, c, d, M_offset_4, 7, T[4]); 90 | d = MD5.FF(d, a, b, c, M_offset_5, 12, T[5]); 91 | c = MD5.FF(c, d, a, b, M_offset_6, 17, T[6]); 92 | b = MD5.FF(b, c, d, a, M_offset_7, 22, T[7]); 93 | a = MD5.FF(a, b, c, d, M_offset_8, 7, T[8]); 94 | d = MD5.FF(d, a, b, c, M_offset_9, 12, T[9]); 95 | c = MD5.FF(c, d, a, b, M_offset_10, 17, T[10]); 96 | b = MD5.FF(b, c, d, a, M_offset_11, 22, T[11]); 97 | a = MD5.FF(a, b, c, d, M_offset_12, 7, T[12]); 98 | d = MD5.FF(d, a, b, c, M_offset_13, 12, T[13]); 99 | c = MD5.FF(c, d, a, b, M_offset_14, 17, T[14]); 100 | b = MD5.FF(b, c, d, a, M_offset_15, 22, T[15]); 101 | 102 | a = MD5.GG(a, b, c, d, M_offset_1, 5, T[16]); 103 | d = MD5.GG(d, a, b, c, M_offset_6, 9, T[17]); 104 | c = MD5.GG(c, d, a, b, M_offset_11, 14, T[18]); 105 | b = MD5.GG(b, c, d, a, M_offset_0, 20, T[19]); 106 | a = MD5.GG(a, b, c, d, M_offset_5, 5, T[20]); 107 | d = MD5.GG(d, a, b, c, M_offset_10, 9, T[21]); 108 | c = MD5.GG(c, d, a, b, M_offset_15, 14, T[22]); 109 | b = MD5.GG(b, c, d, a, M_offset_4, 20, T[23]); 110 | a = MD5.GG(a, b, c, d, M_offset_9, 5, T[24]); 111 | d = MD5.GG(d, a, b, c, M_offset_14, 9, T[25]); 112 | c = MD5.GG(c, d, a, b, M_offset_3, 14, T[26]); 113 | b = MD5.GG(b, c, d, a, M_offset_8, 20, T[27]); 114 | a = MD5.GG(a, b, c, d, M_offset_13, 5, T[28]); 115 | d = MD5.GG(d, a, b, c, M_offset_2, 9, T[29]); 116 | c = MD5.GG(c, d, a, b, M_offset_7, 14, T[30]); 117 | b = MD5.GG(b, c, d, a, M_offset_12, 20, T[31]); 118 | 119 | a = MD5.HH(a, b, c, d, M_offset_5, 4, T[32]); 120 | d = MD5.HH(d, a, b, c, M_offset_8, 11, T[33]); 121 | c = MD5.HH(c, d, a, b, M_offset_11, 16, T[34]); 122 | b = MD5.HH(b, c, d, a, M_offset_14, 23, T[35]); 123 | a = MD5.HH(a, b, c, d, M_offset_1, 4, T[36]); 124 | d = MD5.HH(d, a, b, c, M_offset_4, 11, T[37]); 125 | c = MD5.HH(c, d, a, b, M_offset_7, 16, T[38]); 126 | b = MD5.HH(b, c, d, a, M_offset_10, 23, T[39]); 127 | a = MD5.HH(a, b, c, d, M_offset_13, 4, T[40]); 128 | d = MD5.HH(d, a, b, c, M_offset_0, 11, T[41]); 129 | c = MD5.HH(c, d, a, b, M_offset_3, 16, T[42]); 130 | b = MD5.HH(b, c, d, a, M_offset_6, 23, T[43]); 131 | a = MD5.HH(a, b, c, d, M_offset_9, 4, T[44]); 132 | d = MD5.HH(d, a, b, c, M_offset_12, 11, T[45]); 133 | c = MD5.HH(c, d, a, b, M_offset_15, 16, T[46]); 134 | b = MD5.HH(b, c, d, a, M_offset_2, 23, T[47]); 135 | 136 | a = MD5.II(a, b, c, d, M_offset_0, 6, T[48]); 137 | d = MD5.II(d, a, b, c, M_offset_7, 10, T[49]); 138 | c = MD5.II(c, d, a, b, M_offset_14, 15, T[50]); 139 | b = MD5.II(b, c, d, a, M_offset_5, 21, T[51]); 140 | a = MD5.II(a, b, c, d, M_offset_12, 6, T[52]); 141 | d = MD5.II(d, a, b, c, M_offset_3, 10, T[53]); 142 | c = MD5.II(c, d, a, b, M_offset_10, 15, T[54]); 143 | b = MD5.II(b, c, d, a, M_offset_1, 21, T[55]); 144 | a = MD5.II(a, b, c, d, M_offset_8, 6, T[56]); 145 | d = MD5.II(d, a, b, c, M_offset_15, 10, T[57]); 146 | c = MD5.II(c, d, a, b, M_offset_6, 15, T[58]); 147 | b = MD5.II(b, c, d, a, M_offset_13, 21, T[59]); 148 | a = MD5.II(a, b, c, d, M_offset_4, 6, T[60]); 149 | d = MD5.II(d, a, b, c, M_offset_11, 10, T[61]); 150 | c = MD5.II(c, d, a, b, M_offset_2, 15, T[62]); 151 | b = MD5.II(b, c, d, a, M_offset_9, 21, T[63]); 152 | 153 | // Intermediate hash value 154 | H[0] = (H[0] + a) | 0; 155 | H[1] = (H[1] + b) | 0; 156 | H[2] = (H[2] + c) | 0; 157 | H[3] = (H[3] + d) | 0; 158 | } 159 | 160 | public _doFinalize(): WordArray { 161 | // Shortcuts 162 | const data = this._data; 163 | const dataWords = data.words; 164 | 165 | const nBitsTotal = this._nDataBytes * 8; 166 | const nBitsLeft = data.sigBytes * 8; 167 | 168 | // Add padding 169 | dataWords[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); 170 | 171 | const nBitsTotalH = Math.floor(nBitsTotal / 0x100000000); 172 | const nBitsTotalL = nBitsTotal; 173 | dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 15] = ( 174 | (((nBitsTotalH << 8) | (nBitsTotalH >>> 24)) & 0x00ff00ff) | 175 | (((nBitsTotalH << 24) | (nBitsTotalH >>> 8)) & 0xff00ff00) 176 | ); 177 | dataWords[(((nBitsLeft + 64) >>> 9) << 4) + 14] = ( 178 | (((nBitsTotalL << 8) | (nBitsTotalL >>> 24)) & 0x00ff00ff) | 179 | (((nBitsTotalL << 24) | (nBitsTotalL >>> 8)) & 0xff00ff00) 180 | ); 181 | 182 | data.sigBytes = (dataWords.length + 1) * 4; 183 | 184 | // Hash final blocks 185 | this._process(); 186 | 187 | // Shortcuts 188 | const hash = this._hash; 189 | const H = hash.words; 190 | 191 | // Swap endian 192 | for (let i = 0; i < 4; i++) { 193 | // Shortcut 194 | const H_i = H[i]; 195 | 196 | H[i] = (((H_i << 8) | (H_i >>> 24)) & 0x00ff00ff) | 197 | (((H_i << 24) | (H_i >>> 8)) & 0xff00ff00); 198 | } 199 | 200 | // Return final computed hash 201 | return hash; 202 | } 203 | } -------------------------------------------------------------------------------- /src/algo/SHA256.ts: -------------------------------------------------------------------------------- 1 | import { Hasher } from '../lib/Hasher'; 2 | import { WordArray } from '../lib/WordArray'; 3 | 4 | // Initialization and round constants tables 5 | const H: Array = []; 6 | const K: Array = []; 7 | 8 | // Reusable object 9 | const W: Array = []; 10 | 11 | export class SHA256 extends Hasher { 12 | public _hash!: WordArray; 13 | 14 | public reset() { 15 | // reset core values 16 | super.reset(); 17 | 18 | this._hash = new WordArray(H.slice(0)); 19 | } 20 | 21 | public _doProcessBlock(M: Array, offset: number) { 22 | // Shortcut 23 | const Hl = this._hash.words; 24 | 25 | // Working variables 26 | let a = Hl[0]; 27 | let b = Hl[1]; 28 | let c = Hl[2]; 29 | let d = Hl[3]; 30 | let e = Hl[4]; 31 | let f = Hl[5]; 32 | let g = Hl[6]; 33 | let h = Hl[7]; 34 | 35 | // Computation 36 | for(let i = 0; i < 64; i++) { 37 | if(i < 16) { 38 | W[i] = M[offset + i] | 0; 39 | } else { 40 | const gamma0x = W[i - 15]; 41 | const gamma0 = ((gamma0x << 25) | (gamma0x >>> 7)) ^ 42 | ((gamma0x << 14) | (gamma0x >>> 18)) ^ 43 | (gamma0x >>> 3); 44 | 45 | const gamma1x = W[i - 2]; 46 | const gamma1 = ((gamma1x << 15) | (gamma1x >>> 17)) ^ 47 | ((gamma1x << 13) | (gamma1x >>> 19)) ^ 48 | (gamma1x >>> 10); 49 | 50 | W[i] = gamma0 + W[i - 7] + gamma1 + W[i - 16]; 51 | } 52 | 53 | const ch = (e & f) ^ (~e & g); 54 | const maj = (a & b) ^ (a & c) ^ (b & c); 55 | 56 | const sigma0 = ((a << 30) | (a >>> 2)) ^ ((a << 19) | (a >>> 13)) ^ ((a << 10) | (a >>> 22)); 57 | const sigma1 = ((e << 26) | (e >>> 6)) ^ ((e << 21) | (e >>> 11)) ^ ((e << 7) | (e >>> 25)); 58 | 59 | const t1 = h + sigma1 + ch + K[i] + W[i]; 60 | const t2 = sigma0 + maj; 61 | 62 | h = g; 63 | g = f; 64 | f = e; 65 | e = (d + t1) | 0; 66 | d = c; 67 | c = b; 68 | b = a; 69 | a = (t1 + t2) | 0; 70 | } 71 | 72 | // Intermediate hash value 73 | Hl[0] = (Hl[0] + a) | 0; 74 | Hl[1] = (Hl[1] + b) | 0; 75 | Hl[2] = (Hl[2] + c) | 0; 76 | Hl[3] = (Hl[3] + d) | 0; 77 | Hl[4] = (Hl[4] + e) | 0; 78 | Hl[5] = (Hl[5] + f) | 0; 79 | Hl[6] = (Hl[6] + g) | 0; 80 | Hl[7] = (Hl[7] + h) | 0; 81 | } 82 | 83 | public _doFinalize(): WordArray { 84 | const nBitsTotal = this._nDataBytes * 8; 85 | const nBitsLeft = this._data.sigBytes * 8; 86 | 87 | // Add padding 88 | this._data.words[nBitsLeft >>> 5] |= 0x80 << (24 - nBitsLeft % 32); 89 | this._data.words[(((nBitsLeft + 64) >>> 9) << 4) + 14] = Math.floor(nBitsTotal / 0x100000000); 90 | this._data.words[(((nBitsLeft + 64) >>> 9) << 4) + 15] = nBitsTotal; 91 | this._data.sigBytes = this._data.words.length * 4; 92 | 93 | // Hash final blocks 94 | this._process(); 95 | 96 | // Return final computed hash 97 | return this._hash; 98 | } 99 | } -------------------------------------------------------------------------------- /src/crypto-ts.ts: -------------------------------------------------------------------------------- 1 | // DEPENDENCIES //////////////////////////////////////////////////////////////////////////////////// 2 | 3 | // import indirectly referenced declarations 4 | import { BufferedBlockAlgorithmConfig } from './lib/BufferedBlockAlgorithmConfig'; 5 | 6 | // LIB ///////////////////////////////////////////////////////////////////////////////////////////// 7 | 8 | import { WordArray } from './lib/WordArray'; 9 | import { BlockCipher } from './lib/BlockCipher'; 10 | import { CipherParams } from './lib/CipherParams'; 11 | import { Hasher } from './lib/Hasher'; 12 | import { SerializableCipher } from './lib/SerializableCipher'; 13 | import { PasswordBasedCipher } from './lib/PasswordBasedCipher'; 14 | 15 | export const lib = { 16 | BlockCipher: BlockCipher, 17 | WordArray: WordArray, 18 | CipherParams: CipherParams, 19 | Hasher: Hasher, 20 | SerializableCipher: SerializableCipher, 21 | PasswordBasedCipher: PasswordBasedCipher 22 | }; 23 | 24 | // ALGORITHMS ////////////////////////////////////////////////////////////////////////////////////// 25 | 26 | import { AES as AESAlgorithm } from './algo/AES'; 27 | import { SHA256 as SHA256Algorithm } from './algo/SHA256'; 28 | 29 | export const algo = { 30 | AES: AESAlgorithm, 31 | SHA256: SHA256Algorithm 32 | }; 33 | 34 | // ENCODINGS /////////////////////////////////////////////////////////////////////////////////////// 35 | 36 | import { Utf8 } from './enc/Utf8'; 37 | import { Hex } from './enc/Hex'; 38 | 39 | export const enc = { 40 | Utf8: Utf8, 41 | Hex: Hex 42 | }; 43 | 44 | // PADDING ///////////////////////////////////////////////////////////////////////////////////////// 45 | 46 | import { NoPadding } from './pad/NoPadding'; 47 | import { PKCS7 } from './pad/PKCS7'; 48 | 49 | export const pad = { 50 | NoPadding: NoPadding, 51 | PKCS7: PKCS7 52 | }; 53 | 54 | // MODES /////////////////////////////////////////////////////////////////////////////////////////// 55 | 56 | import { CBC } from './mode/CBC'; 57 | import { ECB } from './mode/ECB'; 58 | 59 | export const mode = { 60 | CBC: CBC, 61 | ECB: ECB 62 | }; 63 | 64 | // HELPERS ///////////////////////////////////////////////////////////////////////////////////////// 65 | 66 | export const AES = lib.BlockCipher._createHelper(algo.AES); 67 | export const SHA256 = lib.Hasher._createHelper(algo.SHA256); -------------------------------------------------------------------------------- /src/enc/Base64.ts: -------------------------------------------------------------------------------- 1 | import { Encoding } from './Encoding'; 2 | import { WordArray } from '../lib/WordArray'; 3 | 4 | export class Base64 { 5 | public static _map = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; 6 | 7 | public static _reverseMap: Array | undefined = undefined; 8 | 9 | /** 10 | * Converts a word array to a Base64 string. 11 | * 12 | * @param wordArray The word array. 13 | * 14 | * @return The Base64 string. 15 | * 16 | * @example 17 | * 18 | * let base64String = Base64.stringify(wordArray); 19 | */ 20 | public static stringify(wordArray: WordArray): string { 21 | // Clamp excess bits 22 | wordArray.clamp(); 23 | 24 | // Convert 25 | const base64Chars = []; 26 | for (let i = 0; i < wordArray.sigBytes; i += 3) { 27 | const byte1 = (wordArray.words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; 28 | const byte2 = (wordArray.words[(i + 1) >>> 2] >>> (24 - ((i + 1) % 4) * 8)) & 0xff; 29 | const byte3 = (wordArray.words[(i + 2) >>> 2] >>> (24 - ((i + 2) % 4) * 8)) & 0xff; 30 | 31 | const triplet = (byte1 << 16) | (byte2 << 8) | byte3; 32 | 33 | for (let j = 0; (j < 4) && (i + j * 0.75 < wordArray.sigBytes); j++) { 34 | base64Chars.push(this._map.charAt((triplet >>> (6 * (3 - j))) & 0x3f)); 35 | } 36 | } 37 | 38 | // Add padding 39 | const paddingChar = this._map.charAt(64); 40 | if (paddingChar) { 41 | while (base64Chars.length % 4) { 42 | base64Chars.push(paddingChar); 43 | } 44 | } 45 | 46 | return base64Chars.join(''); 47 | } 48 | 49 | /** 50 | * Converts a Base64 string to a word array. 51 | * 52 | * @param base64Str The Base64 string. 53 | * 54 | * @return The word array. 55 | * 56 | * @example 57 | * 58 | * let wordArray = Base64.parse(base64String); 59 | */ 60 | public static parse(base64Str: string): WordArray { 61 | // Shortcuts 62 | let base64StrLength = base64Str.length; 63 | 64 | if(this._reverseMap === undefined) { 65 | this._reverseMap = []; 66 | for(let j = 0; j < this._map.length; j++) { 67 | this._reverseMap[this._map.charCodeAt(j)] = j; 68 | } 69 | } 70 | 71 | // Ignore padding 72 | const paddingChar = this._map.charAt(64); 73 | if(paddingChar) { 74 | const paddingIndex = base64Str.indexOf(paddingChar); 75 | if(paddingIndex !== -1) { 76 | base64StrLength = paddingIndex; 77 | } 78 | } 79 | 80 | // Convert 81 | return this.parseLoop(base64Str, base64StrLength, this._reverseMap); 82 | } 83 | 84 | public static parseLoop(base64Str: string, base64StrLength: number, reverseMap: Array): WordArray { 85 | const words: Array = []; 86 | let nBytes = 0; 87 | for(let i = 0; i < base64StrLength; i++) { 88 | if(i % 4) { 89 | const bits1 = reverseMap[base64Str.charCodeAt(i - 1)] << ((i % 4) * 2); 90 | const bits2 = reverseMap[base64Str.charCodeAt(i)] >>> (6 - (i % 4) * 2); 91 | words[nBytes >>> 2] |= (bits1 | bits2) << (24 - (nBytes % 4) * 8); 92 | nBytes++; 93 | } 94 | } 95 | 96 | return new WordArray(words, nBytes); 97 | } 98 | } 99 | 100 | // type guard for the formatter (to ensure it has the required static methods) 101 | const _: Encoding = Base64; -------------------------------------------------------------------------------- /src/enc/Encoding.ts: -------------------------------------------------------------------------------- 1 | import { WordArray } from '../lib/WordArray'; 2 | 3 | export interface Encoding { 4 | stringify: (wordArray: WordArray) => string; 5 | 6 | parse: (str: string) => WordArray; 7 | } 8 | -------------------------------------------------------------------------------- /src/enc/Hex.ts: -------------------------------------------------------------------------------- 1 | import { Encoding } from './Encoding'; 2 | import { WordArray } from '../lib/WordArray'; 3 | 4 | export class Hex { 5 | /** 6 | * Converts a word array to a hex string. 7 | * 8 | * @param wordArray The word array. 9 | * 10 | * @return The hex string. 11 | * 12 | * @example 13 | * 14 | * let hexString = Hex.stringify(wordArray); 15 | */ 16 | public static stringify(wordArray: WordArray): string { 17 | // Convert 18 | const hexChars: Array = []; 19 | for (let i = 0; i < wordArray.sigBytes; i++) { 20 | const bite = (wordArray.words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; 21 | hexChars.push((bite >>> 4).toString(16)); 22 | hexChars.push((bite & 0x0f).toString(16)); 23 | } 24 | 25 | return hexChars.join(''); 26 | } 27 | 28 | /** 29 | * Converts a hex string to a word array. 30 | * 31 | * @param hexStr The hex string. 32 | * 33 | * @return The word array. 34 | * 35 | * @example 36 | * 37 | * let wordArray = Hex.parse(hexString); 38 | */ 39 | public static parse(hexStr: string): WordArray { 40 | // Shortcut 41 | const hexStrLength = hexStr.length; 42 | 43 | // Convert 44 | const words: Array = []; 45 | for (let i = 0; i < hexStrLength; i += 2) { 46 | words[i >>> 3] |= parseInt(hexStr.substr(i, 2), 16) << (24 - (i % 8) * 4); 47 | } 48 | 49 | return new WordArray(words, hexStrLength / 2); 50 | } 51 | } 52 | 53 | // type guard for the formatter (to ensure it has the required static methods) 54 | const _: Encoding = Hex; -------------------------------------------------------------------------------- /src/enc/Latin1.ts: -------------------------------------------------------------------------------- 1 | import { Encoding } from './Encoding'; 2 | import { WordArray } from '../lib/WordArray'; 3 | 4 | declare function escape(s: string): string; 5 | declare function unescape(s: string): string; 6 | 7 | export class Latin1 { 8 | /** 9 | * Converts a word array to a Latin1 string. 10 | * 11 | * @param wordArray The word array. 12 | * 13 | * @return The Latin1 string. 14 | * 15 | * @example 16 | * 17 | * let latin1String = Latin1.stringify(wordArray); 18 | */ 19 | public static stringify(wordArray: WordArray): string { 20 | // Convert 21 | const latin1Chars = []; 22 | for (let i = 0; i < wordArray.sigBytes; i++) { 23 | const bite = (wordArray.words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; 24 | latin1Chars.push(String.fromCharCode(bite)); 25 | } 26 | 27 | return latin1Chars.join(''); 28 | } 29 | 30 | /** 31 | * Converts a Latin1 string to a word array. 32 | * 33 | * @param latin1Str The Latin1 string. 34 | * 35 | * @return The word array. 36 | * 37 | * @example 38 | * 39 | * let wordArray = Latin1.parse(latin1String); 40 | */ 41 | public static parse(latin1Str: string): WordArray { 42 | // Shortcut 43 | const latin1StrLength = latin1Str.length; 44 | 45 | // Convert 46 | const words: Array = []; 47 | for (let i = 0; i < latin1StrLength; i++) { 48 | words[i >>> 2] |= (latin1Str.charCodeAt(i) & 0xff) << (24 - (i % 4) * 8); 49 | } 50 | 51 | return new WordArray(words, latin1StrLength); 52 | } 53 | } 54 | 55 | // type guard for the formatter (to ensure it has the required static methods) 56 | const _: Encoding = Latin1; -------------------------------------------------------------------------------- /src/enc/Utf8.ts: -------------------------------------------------------------------------------- 1 | import { Encoding } from './Encoding'; 2 | import { WordArray } from '../lib/WordArray'; 3 | import { Latin1 } from './Latin1'; 4 | 5 | export class Utf8 { 6 | /** 7 | * Converts a word array to a UTF-8 string. 8 | * 9 | * @param wordArray The word array. 10 | * 11 | * @return The UTF-8 string. 12 | * 13 | * @example 14 | * 15 | * let utf8String = Utf8.stringify(wordArray); 16 | */ 17 | public static stringify(wordArray: WordArray): string { 18 | try { 19 | return decodeURIComponent(escape(Latin1.stringify(wordArray))); 20 | } catch(e) { 21 | throw new Error('Malformed UTF-8 data'); 22 | } 23 | } 24 | 25 | /** 26 | * Converts a UTF-8 string to a word array. 27 | * 28 | * @param utf8Str The UTF-8 string. 29 | * 30 | * @return The word array. 31 | * 32 | * @example 33 | * 34 | * let wordArray = Utf8.parse(utf8String); 35 | */ 36 | public static parse(utf8Str: string): WordArray { 37 | return Latin1.parse(unescape(encodeURIComponent(utf8Str))); 38 | } 39 | } 40 | 41 | // type guard for the formatter (to ensure it has the required static methods) 42 | const _: Encoding = Utf8; -------------------------------------------------------------------------------- /src/enc/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Utf8'; -------------------------------------------------------------------------------- /src/format/Formatter.ts: -------------------------------------------------------------------------------- 1 | import { CipherParams } from '../lib/CipherParams'; 2 | 3 | export interface Formatter { 4 | stringify: (cipherParams: CipherParams) => string; 5 | 6 | parse: (paramsStr: string) => CipherParams; 7 | } -------------------------------------------------------------------------------- /src/format/OpenSSL.ts: -------------------------------------------------------------------------------- 1 | import { Formatter } from './Formatter'; 2 | import { CipherParams } from '../lib/CipherParams'; 3 | import { WordArray } from '../lib/WordArray'; 4 | import { Base64 } from '../enc/Base64'; 5 | 6 | export class OpenSSL { 7 | /** 8 | * Converts a cipher params object to an OpenSSL-compatible string. 9 | * 10 | * @param cipherParams The cipher params object. 11 | * 12 | * @return The OpenSSL-compatible string. 13 | * 14 | * @example 15 | * 16 | * let openSSLString = OpenSSLFormatter.stringify(cipherParams); 17 | */ 18 | public static stringify(cipherParams: CipherParams): string { 19 | if(!cipherParams.ciphertext) { 20 | throw new Error('missing ciphertext in params'); 21 | } 22 | 23 | // Shortcuts 24 | const ciphertext = cipherParams.ciphertext; 25 | const salt = cipherParams.salt; 26 | 27 | // Format 28 | let wordArray: WordArray; 29 | if(salt) { 30 | if(typeof salt === 'string') { 31 | throw new Error('salt is expected to be a WordArray'); 32 | } 33 | 34 | wordArray = (new WordArray([0x53616c74, 0x65645f5f])).concat(salt).concat(ciphertext); 35 | } else { 36 | wordArray = ciphertext; 37 | } 38 | 39 | return wordArray.toString(Base64); 40 | } 41 | 42 | /** 43 | * Converts an OpenSSL-compatible string to a cipher params object. 44 | * 45 | * @param openSSLStr The OpenSSL-compatible string. 46 | * 47 | * @return The cipher params object. 48 | * 49 | * @example 50 | * 51 | * let cipherParams = OpenSSLFormatter.parse(openSSLString); 52 | */ 53 | public static parse(openSSLStr: string): CipherParams { 54 | // Parse base64 55 | const ciphertext = Base64.parse(openSSLStr); 56 | 57 | // Test for salt 58 | let salt: WordArray | undefined; 59 | if(ciphertext.words[0] === 0x53616c74 && ciphertext.words[1] === 0x65645f5f) { 60 | // Extract salt 61 | salt = new WordArray(ciphertext.words.slice(2, 4)); 62 | 63 | // Remove salt from ciphertext 64 | ciphertext.words.splice(0, 4); 65 | ciphertext.sigBytes -= 16; 66 | } 67 | 68 | return new CipherParams({ ciphertext: ciphertext, salt: salt }); 69 | } 70 | } 71 | 72 | // type guard for OpenSSL formatter (to ensure it has the required static methods) 73 | const _: Formatter = OpenSSL; -------------------------------------------------------------------------------- /src/kdf/KDF.ts: -------------------------------------------------------------------------------- 1 | import { WordArray } from '../lib/WordArray'; 2 | import { CipherParams } from '../lib/CipherParams'; 3 | 4 | export interface KDF { 5 | execute: (password: string, keySize: number, ivSize: number, salt?: WordArray | string) => CipherParams; 6 | } -------------------------------------------------------------------------------- /src/kdf/OpenSSLKdf.ts: -------------------------------------------------------------------------------- 1 | import { KDF } from './KDF'; 2 | import { WordArray } from '../lib/WordArray'; 3 | import { CipherParams } from '../lib/CipherParams'; 4 | import { EvpKDF } from '../algo/EvpKDF'; 5 | 6 | export class OpenSSLKdf { 7 | /** 8 | * Derives a key and IV from a password. 9 | * 10 | * @param password The password to derive from. 11 | * @param keySize The size in words of the key to generate. 12 | * @param ivSize The size in words of the IV to generate. 13 | * @param salt (Optional) A 64-bit salt to use. If omitted, a salt will be generated randomly. 14 | * 15 | * @return A cipher params object with the key, IV, and salt. 16 | * 17 | * @example 18 | * 19 | * let derivedParams = OpenSSL.execute('Password', 256/32, 128/32); 20 | * let derivedParams = OpenSSL.execute('Password', 256/32, 128/32, 'saltsalt'); 21 | */ 22 | public static execute(password: string, keySize: number, ivSize: number, salt?: WordArray | string): CipherParams { 23 | // Generate random salt 24 | if(!salt) { 25 | salt = WordArray.random(64 / 8); 26 | } 27 | 28 | // Derive key and IV 29 | const key = (new EvpKDF({ keySize: keySize + ivSize })).compute(password, salt); 30 | 31 | // Separate key and IV 32 | const iv = new WordArray(key.words.slice(keySize), ivSize * 4); 33 | key.sigBytes = keySize * 4; 34 | 35 | // Return params 36 | return new CipherParams({ key: key, iv: iv, salt: salt }); 37 | } 38 | } 39 | 40 | const _: KDF = OpenSSLKdf; -------------------------------------------------------------------------------- /src/lib/Base.ts: -------------------------------------------------------------------------------- 1 | export class Base { 2 | } -------------------------------------------------------------------------------- /src/lib/BlockCipher.ts: -------------------------------------------------------------------------------- 1 | import { Cipher } from './Cipher'; 2 | import { WordArray } from './WordArray'; 3 | import { BufferedBlockAlgorithmConfig } from './BufferedBlockAlgorithmConfig'; 4 | import { BlockCipherModeAlgorithm } from '../mode/BlockCipherModeAlgorithm'; 5 | import { CBC } from '../mode/CBC'; 6 | import { PKCS7 } from '../pad/PKCS7'; 7 | 8 | export abstract class BlockCipher extends Cipher { 9 | public _mode!: BlockCipherModeAlgorithm; 10 | 11 | constructor(xformMode: number, key: WordArray, cfg?: BufferedBlockAlgorithmConfig) { 12 | super(xformMode, key, Object.assign({ 13 | // default: 128 / 32 14 | blockSize: 4, 15 | mode: CBC, 16 | padding: PKCS7 17 | }, cfg)); 18 | } 19 | 20 | public reset() { 21 | // Reset cipher 22 | super.reset(); 23 | 24 | // Check if we have a blockSize 25 | if(this.cfg.mode === undefined) { 26 | throw new Error('missing mode in config'); 27 | } 28 | 29 | // Reset block mode 30 | let modeCreator; 31 | if (this._xformMode === ( this.constructor)._ENC_XFORM_MODE) { 32 | modeCreator = this.cfg.mode.createEncryptor; 33 | } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ { 34 | modeCreator = this.cfg.mode.createDecryptor; 35 | // Keep at least one block in the buffer for unpadding 36 | this._minBufferSize = 1; 37 | } 38 | 39 | if (this._mode && this._mode.__creator === modeCreator) { 40 | this._mode.init(this, this.cfg.iv && this.cfg.iv.words); 41 | } else { 42 | this._mode = modeCreator.call(this.cfg.mode, this, this.cfg.iv && this.cfg.iv.words); 43 | this._mode.__creator = modeCreator; 44 | } 45 | } 46 | 47 | _doProcessBlock(words: Array, offset: number) { 48 | this._mode.processBlock(words, offset); 49 | } 50 | 51 | _doFinalize() { 52 | // Check if we have a padding strategy 53 | if(this.cfg.padding === undefined) { 54 | throw new Error('missing padding in config'); 55 | } 56 | 57 | // Finalize 58 | let finalProcessedBlocks; 59 | if(this._xformMode === ( this.constructor)._ENC_XFORM_MODE) { 60 | // Check if we have a blockSize 61 | if(this.cfg.blockSize === undefined) { 62 | throw new Error('missing blockSize in config'); 63 | } 64 | 65 | // Pad data 66 | this.cfg.padding.pad(this._data, this.cfg.blockSize); 67 | 68 | // Process final blocks 69 | finalProcessedBlocks = this._process(!!'flush'); 70 | } else /* if (this._xformMode == this._DEC_XFORM_MODE) */ { 71 | // Process final blocks 72 | finalProcessedBlocks = this._process(!!'flush'); 73 | 74 | // Unpad data 75 | this.cfg.padding.unpad(finalProcessedBlocks); 76 | } 77 | 78 | return finalProcessedBlocks; 79 | } 80 | 81 | public abstract encryptBlock(M: Array, offset: number): void; 82 | 83 | public abstract decryptBlock(M: Array, offset: number): void; 84 | } -------------------------------------------------------------------------------- /src/lib/BufferedBlockAlgorithm.ts: -------------------------------------------------------------------------------- 1 | import { WordArray } from '../lib/WordArray'; 2 | import { Utf8 } from '../enc/Utf8'; 3 | import { BufferedBlockAlgorithmConfig } from './BufferedBlockAlgorithmConfig'; 4 | 5 | export abstract class BufferedBlockAlgorithm { 6 | public _minBufferSize = 0; 7 | 8 | public _data: WordArray; 9 | 10 | public _nDataBytes: number; 11 | 12 | public cfg: BufferedBlockAlgorithmConfig; 13 | 14 | abstract _doProcessBlock(wordArray: Array, offset: number): void; 15 | 16 | constructor(cfg?: BufferedBlockAlgorithmConfig) { 17 | this.cfg = Object.assign({ 18 | blockSize: 1 19 | }, cfg); 20 | 21 | // Initial values 22 | this._data = new WordArray(); 23 | this._nDataBytes = 0; 24 | } 25 | 26 | /** 27 | * Resets this block algorithm's data buffer to its initial state. 28 | * 29 | * @example 30 | * 31 | * bufferedBlockAlgorithm.reset(); 32 | */ 33 | reset() { 34 | // Initial values 35 | this._data = new WordArray(); 36 | this._nDataBytes = 0; 37 | } 38 | 39 | /** 40 | * Adds new data to this block algorithm's buffer. 41 | * 42 | * @param data The data to append. Strings are converted to a WordArray using UTF-8. 43 | * 44 | * @example 45 | * 46 | * bufferedBlockAlgorithm._append('data'); 47 | * bufferedBlockAlgorithm._append(wordArray); 48 | */ 49 | _append(data: string | WordArray) { 50 | // Convert string to WordArray, else assume WordArray already 51 | if(typeof data === 'string') { 52 | data = Utf8.parse(data); 53 | } 54 | 55 | // Append 56 | this._data.concat(data); 57 | this._nDataBytes += data.sigBytes; 58 | } 59 | 60 | /** 61 | * Processes available data blocks. 62 | * 63 | * This method invokes _doProcessBlock(offset), which must be implemented by a concrete subtype. 64 | * 65 | * @param doFlush Whether all blocks and partial blocks should be processed. 66 | * 67 | * @return The processed data. 68 | * 69 | * @example 70 | * 71 | * let processedData = bufferedBlockAlgorithm._process(); 72 | * let processedData = bufferedBlockAlgorithm._process(!!'flush'); 73 | */ 74 | _process(doFlush?: boolean): WordArray { 75 | if(!this.cfg.blockSize) { 76 | throw new Error('missing blockSize in config'); 77 | } 78 | 79 | // Shortcuts 80 | const blockSizeBytes = this.cfg.blockSize * 4; 81 | 82 | // Count blocks ready 83 | let nBlocksReady = this._data.sigBytes / blockSizeBytes; 84 | if (doFlush) { 85 | // Round up to include partial blocks 86 | nBlocksReady = Math.ceil(nBlocksReady); 87 | } else { 88 | // Round down to include only full blocks, 89 | // less the number of blocks that must remain in the buffer 90 | nBlocksReady = Math.max((nBlocksReady | 0) - this._minBufferSize, 0); 91 | } 92 | 93 | // Count words ready 94 | const nWordsReady = nBlocksReady * this.cfg.blockSize; 95 | 96 | // Count bytes ready 97 | const nBytesReady = Math.min(nWordsReady * 4, this._data.sigBytes); 98 | 99 | // Process blocks 100 | let processedWords; 101 | if (nWordsReady) { 102 | for (let offset = 0; offset < nWordsReady; offset += this.cfg.blockSize) { 103 | // Perform concrete-algorithm logic 104 | this._doProcessBlock(this._data.words, offset); 105 | } 106 | 107 | // Remove processed words 108 | processedWords = this._data.words.splice(0, nWordsReady); 109 | this._data.sigBytes -= nBytesReady; 110 | } 111 | 112 | // Return processed words 113 | return new WordArray(processedWords, nBytesReady); 114 | } 115 | 116 | /** 117 | * Creates a copy of this object. 118 | * 119 | * @return The clone. 120 | * 121 | * @example 122 | * 123 | * let clone = bufferedBlockAlgorithm.clone(); 124 | */ 125 | clone(): BufferedBlockAlgorithm { 126 | const clone = this.constructor(); 127 | 128 | for(const attr in this) { 129 | if(this.hasOwnProperty(attr)) { 130 | clone[attr] = this[attr]; 131 | } 132 | } 133 | 134 | clone._data = this._data.clone(); 135 | 136 | return clone; 137 | } 138 | } -------------------------------------------------------------------------------- /src/lib/BufferedBlockAlgorithmConfig.ts: -------------------------------------------------------------------------------- 1 | import { Formatter } from '../format/Formatter'; 2 | import { WordArray } from '../lib/WordArray'; 3 | import { KDF } from '../kdf/KDF'; 4 | import { BlockCipherMode } from '../mode/BlockCipherMode'; 5 | import { Padding } from '../pad/Padding'; 6 | 7 | export interface BufferedBlockAlgorithmConfig { 8 | // requires at least a blockSize 9 | blockSize?: number; 10 | 11 | iv?: WordArray; 12 | 13 | format?: Formatter; 14 | 15 | kdf?: KDF; 16 | 17 | mode?: typeof BlockCipherMode; 18 | 19 | padding?: Padding; 20 | } -------------------------------------------------------------------------------- /src/lib/Cipher.ts: -------------------------------------------------------------------------------- 1 | import { BufferedBlockAlgorithm } from './BufferedBlockAlgorithm'; 2 | import { WordArray } from './WordArray'; 3 | import { SerializableCipher } from './SerializableCipher'; 4 | import { PasswordBasedCipher } from './PasswordBasedCipher'; 5 | import { BufferedBlockAlgorithmConfig } from './BufferedBlockAlgorithmConfig'; 6 | import { CipherParams } from './CipherParams'; 7 | 8 | export abstract class Cipher extends BufferedBlockAlgorithm { 9 | /** 10 | * A constant representing encryption mode. 11 | */ 12 | public static _ENC_XFORM_MODE = 1; 13 | 14 | /** 15 | * A constant representing decryption mode. 16 | */ 17 | public static _DEC_XFORM_MODE = 2; 18 | 19 | /** 20 | * This cipher's key size. Default: 4 (128 bits / 32 Bits) 21 | */ 22 | public static keySize = 4; 23 | 24 | /** 25 | * This cipher's IV size. Default: 4 (128 bits / 32 Bits) 26 | */ 27 | public static ivSize = 4; 28 | 29 | /** 30 | * Either the encryption or decryption transformation mode constant. 31 | */ 32 | public _xformMode: number; 33 | 34 | /** 35 | * The key. 36 | */ 37 | public _key: WordArray; 38 | 39 | /** 40 | * Creates this cipher in encryption mode. 41 | * 42 | * @param key The key. 43 | * @param cfg (Optional) The configuration options to use for this operation. 44 | * 45 | * @return A cipher instance. 46 | * 47 | * @example 48 | * 49 | * let cipher = AES.createEncryptor(keyWordArray, { iv: ivWordArray }); 50 | */ 51 | public static createEncryptor(key: WordArray, cfg?: BufferedBlockAlgorithmConfig): Cipher { 52 | // workaround for typescript not being able to create a abstract creator function directly 53 | const thisClass: any = this; 54 | 55 | return new thisClass(this._ENC_XFORM_MODE, key, cfg); 56 | } 57 | 58 | /** 59 | * Creates this cipher in decryption mode. 60 | * 61 | * @param key The key. 62 | * @param cfg (Optional) The configuration options to use for this operation. 63 | * 64 | * @return A cipher instance. 65 | * 66 | * @example 67 | * 68 | * let cipher = AES.createDecryptor(keyWordArray, { iv: ivWordArray }); 69 | */ 70 | public static createDecryptor(key: WordArray, cfg?: BufferedBlockAlgorithmConfig): Cipher { 71 | // workaround for typescript not being able to create a abstract creator function directly 72 | const thisClass: any = this; 73 | 74 | return new thisClass(this._DEC_XFORM_MODE, key, cfg); 75 | } 76 | 77 | /** 78 | * Creates shortcut functions to a cipher's object interface. 79 | * 80 | * @param cipher The cipher to create a helper for. 81 | * 82 | * @return An object with encrypt and decrypt shortcut functions. 83 | * 84 | * @example 85 | * 86 | * let AES = Cipher._createHelper(AESAlgorithm); 87 | */ 88 | public static _createHelper(cipher: typeof Cipher) { 89 | function encrypt(message: WordArray | string, key: WordArray | string, cfg?: BufferedBlockAlgorithmConfig) { 90 | if(typeof key === 'string') { 91 | return PasswordBasedCipher.encrypt(cipher, message, key, cfg); 92 | } else { 93 | return SerializableCipher.encrypt(cipher, message, key, cfg); 94 | } 95 | } 96 | 97 | function decrypt(ciphertext: CipherParams | string, key: WordArray | string, cfg?: BufferedBlockAlgorithmConfig) { 98 | if(typeof key === 'string') { 99 | return PasswordBasedCipher.decrypt(cipher, ciphertext, key, cfg); 100 | } else { 101 | return SerializableCipher.decrypt(cipher, ciphertext, key, cfg); 102 | } 103 | } 104 | 105 | return { 106 | encrypt: encrypt, 107 | decrypt: decrypt 108 | }; 109 | } 110 | 111 | /** 112 | * Initializes a newly created cipher. 113 | * 114 | * @param xformMode Either the encryption or decryption transormation mode constant. 115 | * @param key The key. 116 | * @param cfg (Optional) The configuration options to use for this operation. 117 | * 118 | * @example 119 | * 120 | * let cipher = AES.create(AES._ENC_XFORM_MODE, keyWordArray, { iv: ivWordArray }); 121 | */ 122 | public constructor(xformMode: number, key: WordArray, cfg?: BufferedBlockAlgorithmConfig) { 123 | // Apply config defaults 124 | super(Object.assign({ 125 | blockSize: 1 126 | }, cfg)); 127 | 128 | // Store transform mode and key 129 | this._xformMode = xformMode; 130 | this._key = key; 131 | 132 | // Set initial values 133 | this.reset(); 134 | } 135 | 136 | /** 137 | * Adds data to be encrypted or decrypted. 138 | * 139 | * @param dataUpdate The data to encrypt or decrypt. 140 | * 141 | * @return The data after processing. 142 | * 143 | * @example 144 | * 145 | * let encrypted = cipher.process('data'); 146 | * let encrypted = cipher.process(wordArray); 147 | */ 148 | public process(dataUpdate: WordArray | string): WordArray { 149 | // Append 150 | this._append(dataUpdate); 151 | 152 | // Process available blocks 153 | return this._process(); 154 | } 155 | 156 | /** 157 | * Finalizes the encryption or decryption process. 158 | * Note that the finalize operation is effectively a destructive, read-once operation. 159 | * 160 | * @param dataUpdate The final data to encrypt or decrypt. 161 | * 162 | * @return The data after final processing. 163 | * 164 | * @example 165 | * 166 | * var encrypted = cipher.finalize(); 167 | * var encrypted = cipher.finalize('data'); 168 | * var encrypted = cipher.finalize(wordArray); 169 | */ 170 | public finalize(dataUpdate?: WordArray | string): WordArray { 171 | // Final data update 172 | if(dataUpdate) { 173 | this._append(dataUpdate); 174 | } 175 | 176 | // Perform concrete-cipher logic 177 | const finalProcessedData = this._doFinalize(); 178 | 179 | return finalProcessedData; 180 | } 181 | 182 | /** 183 | * Cipher specific finalize function explicitly implemented in the derived class. 184 | */ 185 | public abstract _doFinalize(): WordArray; 186 | } -------------------------------------------------------------------------------- /src/lib/CipherParams.ts: -------------------------------------------------------------------------------- 1 | import { Base } from '../lib/Base'; 2 | import { CipherParamsInterface } from './CipherParamsInterface'; 3 | import { WordArray } from '../lib/WordArray'; 4 | import { Cipher } from '../lib/Cipher'; 5 | import { BlockCipherMode } from '../mode/BlockCipherMode'; 6 | import { Padding } from '../pad/Padding'; 7 | import { Formatter } from '../format/Formatter'; 8 | 9 | export class CipherParams extends Base implements CipherParamsInterface { 10 | ciphertext?: WordArray; 11 | 12 | key?: WordArray | string; 13 | 14 | iv?: WordArray; 15 | 16 | salt?: WordArray | string; 17 | 18 | algorithm?: typeof Cipher; 19 | 20 | mode?: typeof BlockCipherMode; 21 | 22 | padding?: Padding; 23 | 24 | blockSize?: number; 25 | 26 | formatter?: Formatter; 27 | 28 | /** 29 | * Initializes a newly created cipher params object. 30 | * 31 | * @param cipherParams An object with any of the possible cipher parameters. 32 | * 33 | * @example 34 | * 35 | * let cipherParams = CipherParams.create({ 36 | * ciphertext: ciphertextWordArray, 37 | * key: keyWordArray, 38 | * iv: ivWordArray, 39 | * salt: saltWordArray, 40 | * algorithm: AESAlgorithm, 41 | * mode: CBC, 42 | * padding: PKCS7, 43 | * blockSize: 4, 44 | * formatter: OpenSSLFormatter 45 | * }); 46 | */ 47 | public constructor(cipherParams: CipherParamsInterface) { 48 | super(); 49 | 50 | this.ciphertext = cipherParams.ciphertext; 51 | this.key = cipherParams.key; 52 | this.iv = cipherParams.iv; 53 | this.salt = cipherParams.salt; 54 | this.algorithm = cipherParams.algorithm; 55 | this.mode = cipherParams.mode; 56 | this.padding = cipherParams.padding; 57 | this.blockSize = cipherParams.blockSize; 58 | this.formatter = cipherParams.formatter; 59 | } 60 | 61 | public extend(additionalParams: CipherParams): CipherParams { 62 | if(additionalParams.ciphertext !== undefined) { 63 | this.ciphertext = additionalParams.ciphertext; 64 | } 65 | 66 | if(additionalParams.key !== undefined) { 67 | this.key = additionalParams.key; 68 | } 69 | 70 | if(additionalParams.iv !== undefined) { 71 | this.iv = additionalParams.iv; 72 | } 73 | 74 | if(additionalParams.salt !== undefined) { 75 | this.salt = additionalParams.salt; 76 | } 77 | 78 | if(additionalParams.algorithm !== undefined) { 79 | this.algorithm = additionalParams.algorithm; 80 | } 81 | 82 | if(additionalParams.mode !== undefined) { 83 | this.mode = additionalParams.mode; 84 | } 85 | 86 | if(additionalParams.padding !== undefined) { 87 | this.padding = additionalParams.padding; 88 | } 89 | 90 | if(additionalParams.blockSize !== undefined) { 91 | this.blockSize = additionalParams.blockSize; 92 | } 93 | 94 | if(additionalParams.formatter !== undefined) { 95 | this.formatter = additionalParams.formatter; 96 | } 97 | 98 | 99 | return this; 100 | } 101 | 102 | /** 103 | * Converts this cipher params object to a string. 104 | * 105 | * @param formatter (Optional) The formatting strategy to use. 106 | * 107 | * @return The stringified cipher params. 108 | * 109 | * @throws Error If neither the formatter nor the default formatter is set. 110 | * 111 | * @example 112 | * 113 | * let string = cipherParams + ''; 114 | * let string = cipherParams.toString(); 115 | * let string = cipherParams.toString(CryptoJS.format.OpenSSL); 116 | */ 117 | public toString(formatter?: Formatter): string { 118 | if(formatter) { 119 | return formatter.stringify(this); 120 | } else if(this.formatter) { 121 | return this.formatter.stringify(this); 122 | } else { 123 | throw new Error('cipher needs a formatter to be able to convert the result into a string'); 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /src/lib/CipherParamsInterface.ts: -------------------------------------------------------------------------------- 1 | import { WordArray } from '../lib/WordArray'; 2 | import { Cipher } from '../lib/Cipher'; 3 | import { BlockCipherMode } from '../mode/BlockCipherMode'; 4 | import { Padding } from '../pad/Padding'; 5 | import { Formatter } from '../format/Formatter'; 6 | 7 | export interface CipherParamsInterface { 8 | ciphertext?: WordArray; 9 | 10 | key?: WordArray | string; 11 | 12 | iv?: WordArray; 13 | 14 | salt?: WordArray | string; 15 | 16 | algorithm?: typeof Cipher; 17 | 18 | mode?: typeof BlockCipherMode; 19 | 20 | padding?: Padding; 21 | 22 | blockSize?: number; 23 | 24 | formatter?: Formatter; 25 | } -------------------------------------------------------------------------------- /src/lib/Hasher.ts: -------------------------------------------------------------------------------- 1 | import { BufferedBlockAlgorithm } from '../lib/BufferedBlockAlgorithm'; 2 | import { BufferedBlockAlgorithmConfig } from '../lib/BufferedBlockAlgorithmConfig'; 3 | import { WordArray } from '../lib/WordArray'; 4 | 5 | export abstract class Hasher extends BufferedBlockAlgorithm { 6 | /** 7 | * Creates a shortcut function to a hasher's object interface. 8 | * 9 | * @param hasher The hasher to create a helper for. 10 | * 11 | * @return The shortcut function. 12 | * 13 | * @example 14 | * 15 | * let SHA256 = Hasher._createHelper(SHA256); 16 | */ 17 | public static _createHelper(hasher: typeof Hasher) { 18 | function helper(message: WordArray | string, cfg?: BufferedBlockAlgorithmConfig) { 19 | const hasherClass: any = hasher; 20 | 21 | const hasherInstance: any = new hasherClass(cfg); 22 | 23 | return hasherInstance.finalize(message); 24 | } 25 | 26 | return helper; 27 | } 28 | 29 | /** 30 | * Initializes a newly created hasher. 31 | * 32 | * @param cfg (Optional) The configuration options to use for this hash computation. 33 | * 34 | * @example 35 | * 36 | * let hasher = CryptoJS.algo.SHA256.create(); 37 | */ 38 | public constructor(cfg?: BufferedBlockAlgorithmConfig) { 39 | // Apply config defaults 40 | super(Object.assign({ 41 | blockSize: 512 / 32 42 | }, cfg)); 43 | 44 | // Set initial values 45 | this.reset(); 46 | } 47 | 48 | /** 49 | * Updates this hasher with a message. 50 | * 51 | * @param messageUpdate The message to append. 52 | * 53 | * @return This hasher. 54 | * 55 | * @example 56 | * 57 | * hasher.update('message'); 58 | * hasher.update(wordArray); 59 | */ 60 | update(messageUpdate: WordArray | string): Hasher { 61 | // Append 62 | this._append(messageUpdate); 63 | 64 | // Update the hash 65 | this._process(); 66 | 67 | // Chainable 68 | return this; 69 | } 70 | 71 | /** 72 | * Finalizes the hash computation. 73 | * Note that the finalize operation is effectively a destructive, read-once operation. 74 | * 75 | * @param messageUpdate (Optional) A final message update. 76 | * 77 | * @return The hash. 78 | * 79 | * @example 80 | * 81 | * let hash = hasher.finalize(); 82 | * let hash = hasher.finalize('message'); 83 | * let hash = hasher.finalize(wordArray); 84 | */ 85 | public finalize(messageUpdate: WordArray | string): WordArray { 86 | // Final message update 87 | if(messageUpdate) { 88 | this._append(messageUpdate); 89 | } 90 | 91 | // Perform concrete-hasher logic 92 | const hash = this._doFinalize(); 93 | 94 | return hash; 95 | } 96 | 97 | public abstract _doFinalize(): WordArray; 98 | } -------------------------------------------------------------------------------- /src/lib/PasswordBasedCipher.ts: -------------------------------------------------------------------------------- 1 | import { SerializableCipher } from './SerializableCipher'; 2 | import { WordArray } from './WordArray'; 3 | import { Cipher } from './Cipher'; 4 | import { BufferedBlockAlgorithmConfig } from './BufferedBlockAlgorithmConfig'; 5 | import { OpenSSL } from '../format/OpenSSL'; 6 | import { CipherParams } from './CipherParams'; 7 | import { Formatter } from '../format/Formatter'; 8 | import { OpenSSLKdf } from '../kdf/OpenSSLKdf'; 9 | 10 | export class PasswordBasedCipher { 11 | public static cfg: BufferedBlockAlgorithmConfig = { 12 | blockSize: 4, 13 | iv: new WordArray([]), 14 | format: OpenSSL, 15 | kdf: OpenSSLKdf 16 | }; 17 | 18 | /** 19 | * Encrypts a message using a password. 20 | * 21 | * @param cipher The cipher algorithm to use. 22 | * @param message The message to encrypt. 23 | * @param password The password. 24 | * @param cfg (Optional) The configuration options to use for this operation. 25 | * 26 | * @return A cipher params object. 27 | * 28 | * @example 29 | * 30 | * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(AES, message, 'password'); 31 | * var ciphertextParams = CryptoJS.lib.PasswordBasedCipher.encrypt(AES, message, 'password', { format: OpenSSL }); 32 | */ 33 | public static encrypt( 34 | cipher: typeof Cipher, 35 | message: WordArray | string, 36 | password: string, 37 | cfg?: BufferedBlockAlgorithmConfig 38 | ): CipherParams { 39 | // Apply config defaults 40 | const config = Object.assign({}, this.cfg, cfg); 41 | 42 | // Check if we have a kdf 43 | if(config.kdf === undefined) { 44 | throw new Error('missing kdf in config'); 45 | } 46 | 47 | // Derive key and other params 48 | const derivedParams: CipherParams = config.kdf.execute(password, cipher.keySize, cipher.ivSize); 49 | 50 | // Check if we have an IV 51 | if(derivedParams.iv !== undefined) { 52 | // Add IV to config 53 | config.iv = derivedParams.iv; 54 | } 55 | 56 | // Encrypt 57 | const ciphertext: CipherParams = SerializableCipher.encrypt.call(this, cipher, message, derivedParams.key, config); 58 | 59 | // Mix in derived params 60 | return ciphertext.extend(derivedParams); 61 | } 62 | 63 | /** 64 | * Decrypts serialized ciphertext using a password. 65 | * 66 | * @param cipher The cipher algorithm to use. 67 | * @param ciphertext The ciphertext to decrypt. 68 | * @param password The password. 69 | * @param cfg (Optional) The configuration options to use for this operation. 70 | * 71 | * @return The plaintext. 72 | * 73 | * @example 74 | * 75 | * var plaintext = PasswordBasedCipher.decrypt(AES, formattedCiphertext, 'password', { format: OpenSSL }); 76 | * var plaintext = PasswordBasedCipher.decrypt(AES, ciphertextParams, 'password', { format: OpenSSL }); 77 | */ 78 | public static decrypt( 79 | cipher: typeof Cipher, 80 | ciphertext: CipherParams | string, 81 | password: string, 82 | cfg?: BufferedBlockAlgorithmConfig 83 | ): WordArray { 84 | // Apply config defaults 85 | const config = Object.assign({}, this.cfg, cfg); 86 | 87 | // Check if we have a kdf 88 | if(config.format === undefined) { 89 | throw new Error('missing format in config'); 90 | } 91 | 92 | // Convert string to CipherParams 93 | ciphertext = this._parse(ciphertext, config.format); 94 | 95 | // Check if we have a kdf 96 | if(config.kdf === undefined) { 97 | throw new Error('the key derivation function must be set'); 98 | } 99 | 100 | // Derive key and other params 101 | const derivedParams = config.kdf.execute(password, cipher.keySize, cipher.ivSize, ciphertext.salt); 102 | 103 | // Check if we have an IV 104 | if(derivedParams.iv !== undefined) { 105 | // Add IV to config 106 | config.iv = derivedParams.iv; 107 | } 108 | 109 | // Decrypt 110 | const plaintext = SerializableCipher.decrypt.call(this, cipher, ciphertext, derivedParams.key, config); 111 | 112 | return plaintext; 113 | } 114 | 115 | /** 116 | * Converts serialized ciphertext to CipherParams, 117 | * else assumed CipherParams already and returns ciphertext unchanged. 118 | * 119 | * @param ciphertext The ciphertext. 120 | * @param format The formatting strategy to use to parse serialized ciphertext. 121 | * 122 | * @return The unserialized ciphertext. 123 | * 124 | * @example 125 | * 126 | * var ciphertextParams = CryptoJS.lib.SerializableCipher._parse(ciphertextStringOrParams, format); 127 | */ 128 | public static _parse(ciphertext: CipherParams | string, format: Formatter): CipherParams { 129 | if(typeof ciphertext === 'string') { 130 | return format.parse(ciphertext); 131 | } else { 132 | return ciphertext; 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /src/lib/SerializableCipher.ts: -------------------------------------------------------------------------------- 1 | import { WordArray } from './WordArray'; 2 | import { Cipher } from './Cipher'; 3 | import { BufferedBlockAlgorithmConfig } from './BufferedBlockAlgorithmConfig'; 4 | import { OpenSSL } from '../format/OpenSSL'; 5 | import { CipherParams } from './CipherParams'; 6 | import { Formatter } from '../format/Formatter'; 7 | 8 | export class SerializableCipher { 9 | public static cfg: BufferedBlockAlgorithmConfig = { 10 | blockSize: 4, 11 | iv: new WordArray([]), 12 | format: OpenSSL 13 | }; 14 | 15 | /** 16 | * Encrypts a message. 17 | * 18 | * @param cipher The cipher algorithm to use. 19 | * @param message The message to encrypt. 20 | * @param key The key. 21 | * @param cfg (Optional) The configuration options to use for this operation. 22 | * 23 | * @return A cipher params object. 24 | * 25 | * @example 26 | * 27 | * let ciphertextParams = SerializableCipher.encrypt(CryptoJS.algo.AES, message, key); 28 | * let ciphertextParams = SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { iv: iv }); 29 | * let ciphertextParams = SerializableCipher.encrypt(CryptoJS.algo.AES, message, key, { 30 | * iv: iv, 31 | * format: CryptoJS.format.OpenSSL 32 | * }); 33 | */ 34 | public static encrypt( 35 | cipher: typeof Cipher, 36 | message: WordArray | string, 37 | key: WordArray, 38 | cfg?: BufferedBlockAlgorithmConfig 39 | ): CipherParams { 40 | // Apply config defaults 41 | const config = Object.assign({}, this.cfg, cfg); 42 | 43 | // Encrypt 44 | const encryptor = cipher.createEncryptor(key, config); 45 | const ciphertext = encryptor.finalize(message); 46 | 47 | // Create and return serializable cipher params 48 | return new CipherParams({ 49 | ciphertext: ciphertext, 50 | key: key, 51 | iv: encryptor.cfg.iv, 52 | algorithm: cipher, 53 | mode: ( encryptor.cfg).mode, 54 | padding: ( encryptor.cfg).padding, 55 | blockSize: encryptor.cfg.blockSize, 56 | formatter: config.format 57 | }); 58 | } 59 | 60 | /** 61 | * Decrypts serialized ciphertext. 62 | * 63 | * @param cipher The cipher algorithm to use. 64 | * @param ciphertext The ciphertext to decrypt. 65 | * @param key The key. 66 | * @param cfg (Optional) The configuration options to use for this operation. 67 | * 68 | * @return The plaintext. 69 | * 70 | * @example 71 | * 72 | * let plaintext = SerializableCipher.decrypt( 73 | * AESAlgorithm, 74 | * formattedCiphertext, 75 | * key, { 76 | * iv: iv, 77 | * format: CryptoJS.format.OpenSSL 78 | * } 79 | * ); 80 | * 81 | * let plaintext = SerializableCipher.decrypt( 82 | * AESAlgorithm, 83 | * ciphertextParams, 84 | * key, { 85 | * iv: iv, 86 | * format: CryptoJS.format.OpenSSL 87 | * } 88 | * ); 89 | */ 90 | public static decrypt( 91 | cipher: typeof Cipher, 92 | ciphertext: CipherParams | string, 93 | key: WordArray, 94 | optionalCfg?: BufferedBlockAlgorithmConfig 95 | ): WordArray { 96 | // Apply config defaults 97 | const cfg = Object.assign({}, this.cfg, optionalCfg); 98 | 99 | if(!cfg.format) { 100 | throw new Error('could not determine format'); 101 | } 102 | 103 | // Convert string to CipherParams 104 | ciphertext = this._parse(ciphertext, cfg.format); 105 | 106 | if(!ciphertext.ciphertext) { 107 | throw new Error('could not determine ciphertext'); 108 | } 109 | 110 | // Decrypt 111 | const plaintext = cipher.createDecryptor(key, cfg).finalize(ciphertext.ciphertext); 112 | 113 | return plaintext; 114 | } 115 | 116 | /** 117 | * Converts serialized ciphertext to CipherParams, 118 | * else assumed CipherParams already and returns ciphertext unchanged. 119 | * 120 | * @param ciphertext The ciphertext. 121 | * @param format The formatting strategy to use to parse serialized ciphertext. 122 | * 123 | * @return The unserialized ciphertext. 124 | * 125 | * @example 126 | * 127 | * var ciphertextParams = CryptoJS.lib.SerializableCipher._parse(ciphertextStringOrParams, format); 128 | */ 129 | public static _parse(ciphertext: CipherParams | string, format: Formatter): CipherParams { 130 | if(typeof ciphertext === 'string') { 131 | return format.parse(ciphertext); 132 | } else { 133 | return ciphertext; 134 | } 135 | } 136 | } -------------------------------------------------------------------------------- /src/lib/WordArray.ts: -------------------------------------------------------------------------------- 1 | import { Encoding } from '../enc/Encoding'; 2 | import { Hex } from '../enc/Hex'; 3 | 4 | export class WordArray { 5 | words: Array; 6 | 7 | sigBytes: number; 8 | 9 | /** 10 | * Creates a word array filled with random bytes. 11 | * 12 | * @param nBytes The number of random bytes to generate. 13 | * 14 | * @return The random word array. 15 | * 16 | * @example 17 | * 18 | * let wordArray = WordArray.random(16); 19 | */ 20 | public static random(nBytes: number) { 21 | const words = []; 22 | 23 | const r = (function(m_w: number) { 24 | let m_z = 0x3ade68b1; 25 | 26 | const mask = 0xffffffff; 27 | 28 | return function() { 29 | m_z = (0x9069 * (m_z & 0xFFFF) + (m_z >> 0x10)) & mask; 30 | m_w = (0x4650 * (m_w & 0xFFFF) + (m_w >> 0x10)) & mask; 31 | let result = ((m_z << 0x10) + m_w) & mask; 32 | result /= 0x100000000; 33 | result += 0.5; 34 | return result * (Math.random() > .5 ? 1 : -1); 35 | }; 36 | }); 37 | 38 | for(let i = 0, rcache; i < nBytes; i += 4) { 39 | const _r = r((rcache || Math.random()) * 0x100000000); 40 | 41 | rcache = _r() * 0x3ade67b7; 42 | words.push((_r() * 0x100000000) | 0); 43 | } 44 | 45 | return new WordArray(words, nBytes); 46 | } 47 | 48 | /** 49 | * Initializes a newly created word array. 50 | * 51 | * @param words (Optional) An array of 32-bit words. 52 | * @param sigBytes (Optional) The number of significant bytes in the words. 53 | * 54 | * @example 55 | * 56 | * let wordArray = new WordArray(); 57 | * let wordArray = new WordArray([0x00010203, 0x04050607]); 58 | * let wordArray = new WordArray([0x00010203, 0x04050607], 6); 59 | */ 60 | constructor(words?: Array, sigBytes?: number) { 61 | this.words = words || []; 62 | 63 | if(sigBytes !== undefined) { 64 | this.sigBytes = sigBytes; 65 | } else { 66 | this.sigBytes = this.words.length * 4; 67 | } 68 | } 69 | 70 | /** 71 | * Converts this word array to a string. 72 | * 73 | * @param encoder (Optional) The encoding strategy to use. Default: CryptoJS.enc.Hex 74 | * 75 | * @return The stringified word array. 76 | * 77 | * @example 78 | * 79 | * let string = wordArray + ''; 80 | * let string = wordArray.toString(); 81 | * let string = wordArray.toString(CryptoJS.enc.Utf8); 82 | */ 83 | toString(encoder?: Encoding): string { 84 | return (encoder || Hex).stringify(this); 85 | } 86 | 87 | /** 88 | * Concatenates a word array to this word array. 89 | * 90 | * @param wordArray The word array to append. 91 | * 92 | * @return This word array. 93 | * 94 | * @example 95 | * 96 | * wordArray1.concat(wordArray2); 97 | */ 98 | concat(wordArray: WordArray): WordArray { 99 | // Clamp excess bits 100 | this.clamp(); 101 | 102 | // Concat 103 | if(this.sigBytes % 4) { 104 | // Copy one byte at a time 105 | for(let i = 0; i < wordArray.sigBytes; i++) { 106 | const thatByte = (wordArray.words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff; 107 | this.words[(this.sigBytes + i) >>> 2] |= thatByte << (24 - ((this.sigBytes + i) % 4) * 8); 108 | } 109 | } else { 110 | // Copy one word at a time 111 | for (let i = 0; i < wordArray.sigBytes; i += 4) { 112 | this.words[(this.sigBytes + i) >>> 2] = wordArray.words[i >>> 2]; 113 | } 114 | } 115 | this.sigBytes += wordArray.sigBytes; 116 | 117 | // Chainable 118 | return this; 119 | } 120 | 121 | /** 122 | * Removes insignificant bits. 123 | * 124 | * @example 125 | * 126 | * wordArray.clamp(); 127 | */ 128 | clamp() { 129 | // Clamp 130 | this.words[this.sigBytes >>> 2] &= 0xffffffff << (32 - (this.sigBytes % 4) * 8); 131 | this.words.length = Math.ceil(this.sigBytes / 4); 132 | } 133 | 134 | /** 135 | * Creates a copy of this word array. 136 | * 137 | * @return The clone. 138 | * 139 | * @example 140 | * 141 | * let clone = wordArray.clone(); 142 | */ 143 | clone(): WordArray { 144 | return new WordArray(this.words.slice(0), this.sigBytes); 145 | } 146 | } -------------------------------------------------------------------------------- /src/mode/BlockCipherMode.ts: -------------------------------------------------------------------------------- 1 | import { BlockCipher } from '../lib/BlockCipher'; 2 | import { BlockCipherModeAlgorithm } from './BlockCipherModeAlgorithm'; 3 | 4 | export abstract class BlockCipherMode { 5 | public static Encryptor: any = BlockCipherModeAlgorithm; 6 | 7 | public static Decryptor: any = BlockCipherModeAlgorithm; 8 | 9 | /** 10 | * Creates this mode for encryption. 11 | * 12 | * @param cipher A block cipher instance. 13 | * @param iv The IV words. 14 | * 15 | * @example 16 | * 17 | * var mode = CBC.createEncryptor(cipher, iv.words); 18 | */ 19 | public static createEncryptor(cipher: BlockCipher, iv: Array): BlockCipherModeAlgorithm { 20 | // workaround for typescript not being able to create a abstract creator function directly 21 | const encryptorClass: any = this.Encryptor; 22 | 23 | return new encryptorClass(cipher, iv); 24 | } 25 | 26 | /** 27 | * Creates this mode for decryption. 28 | * 29 | * @param cipher A block cipher instance. 30 | * @param iv The IV words. 31 | * 32 | * @example 33 | * 34 | * var mode = CBC.createDecryptor(cipher, iv.words); 35 | */ 36 | public static createDecryptor(cipher: BlockCipher, iv: Array): BlockCipherModeAlgorithm { 37 | // workaround for typescript not being able to create a abstract creator function directly 38 | const decryptorClass: any = this.Decryptor; 39 | 40 | return new decryptorClass(cipher, iv); 41 | } 42 | } -------------------------------------------------------------------------------- /src/mode/BlockCipherModeAlgorithm.ts: -------------------------------------------------------------------------------- 1 | import { BlockCipher } from '../lib/BlockCipher'; 2 | import { BlockCipherMode } from './BlockCipherMode'; 3 | 4 | export abstract class BlockCipherModeAlgorithm { 5 | public _cipher!: BlockCipher; 6 | 7 | public _iv: Array | undefined; 8 | 9 | public __creator: ((cipher: BlockCipher, iv: number[]) => BlockCipherMode) | undefined; 10 | 11 | public constructor(cipher: BlockCipher, iv: Array) { 12 | this.init(cipher, iv); 13 | } 14 | 15 | /** 16 | * Initializes a newly created mode. 17 | * 18 | * @param cipher A block cipher instance. 19 | * @param iv The IV words. 20 | * 21 | * @example 22 | * 23 | * var mode = CBC.Encryptor.create(cipher, iv.words); 24 | */ 25 | public init(cipher: BlockCipher, iv?: Array) { 26 | this._cipher = cipher; 27 | this._iv = iv; 28 | } 29 | 30 | public abstract processBlock(words: Array, offset: number): void; 31 | } -------------------------------------------------------------------------------- /src/mode/CBC.ts: -------------------------------------------------------------------------------- 1 | import { BlockCipherMode } from './BlockCipherMode'; 2 | import { CBCEncryptor } from './CBCEncryptor'; 3 | import { CBCDecryptor } from './CBCDecryptor'; 4 | 5 | /** 6 | * Cipher Block Chaining mode. 7 | */ 8 | export abstract class CBC extends BlockCipherMode { 9 | public static Encryptor: any = CBCEncryptor; 10 | 11 | public static Decryptor: any = CBCDecryptor; 12 | } -------------------------------------------------------------------------------- /src/mode/CBCDecryptor.ts: -------------------------------------------------------------------------------- 1 | import { BlockCipherModeAlgorithm } from './BlockCipherModeAlgorithm'; 2 | 3 | export class CBCDecryptor extends BlockCipherModeAlgorithm { 4 | public _prevBlock: Array | undefined; 5 | 6 | /** 7 | * Processes the data block at offset. 8 | * 9 | * @param words The data words to operate on. 10 | * @param offset The offset where the block starts. 11 | * 12 | * @example 13 | * 14 | * mode.processBlock(data.words, offset); 15 | */ 16 | public processBlock(words: Array, offset: number) { 17 | // Check if we have a blockSize 18 | if(this._cipher.cfg.blockSize === undefined) { 19 | throw new Error('missing blockSize in cipher config'); 20 | } 21 | 22 | // Remember this block to use with next block 23 | const thisBlock = words.slice(offset, offset + this._cipher.cfg.blockSize); 24 | 25 | // Decrypt and XOR 26 | this._cipher.decryptBlock(words, offset); 27 | this.xorBlock(words, offset, this._cipher.cfg.blockSize); 28 | 29 | // This block becomes the previous block 30 | this._prevBlock = thisBlock; 31 | } 32 | 33 | public xorBlock(words: Array, offset: number, blockSize: number) { 34 | // Choose mixing block 35 | let block; 36 | if(this._iv) { 37 | block = this._iv; 38 | 39 | // Remove IV for subsequent blocks 40 | this._iv = undefined; 41 | } else { 42 | block = this._prevBlock; 43 | } 44 | 45 | // block should never be undefined but we want to make typescript happy 46 | if(block !== undefined) { 47 | // XOR blocks 48 | for(let i = 0; i < blockSize; i++) { 49 | words[offset + i] ^= block[i]; 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/mode/CBCEncryptor.ts: -------------------------------------------------------------------------------- 1 | import { BlockCipherModeAlgorithm } from './BlockCipherModeAlgorithm'; 2 | 3 | export class CBCEncryptor extends BlockCipherModeAlgorithm { 4 | public _prevBlock: Array | undefined; 5 | 6 | /** 7 | * Processes the data block at offset. 8 | * 9 | * @param words The data words to operate on. 10 | * @param offset The offset where the block starts. 11 | * 12 | * @example 13 | * 14 | * mode.processBlock(data.words, offset); 15 | */ 16 | public processBlock(words: Array, offset: number) { 17 | // Check if we have a blockSize 18 | if(this._cipher.cfg.blockSize === undefined) { 19 | throw new Error('missing blockSize in cipher config'); 20 | } 21 | 22 | // XOR and encrypt 23 | this.xorBlock(words, offset, this._cipher.cfg.blockSize); 24 | this._cipher.encryptBlock(words, offset); 25 | 26 | // Remember this block to use with next block 27 | this._prevBlock = words.slice(offset, offset + this._cipher.cfg.blockSize); 28 | } 29 | 30 | public xorBlock(words: Array, offset: number, blockSize: number) { 31 | // Choose mixing block 32 | let block; 33 | if(this._iv) { 34 | block = this._iv; 35 | 36 | // Remove IV for subsequent blocks 37 | this._iv = undefined; 38 | } else { 39 | block = this._prevBlock; 40 | } 41 | 42 | // block should never be undefined but we want to make typescript happy 43 | if(block !== undefined) { 44 | // XOR blocks 45 | for(let i = 0; i < blockSize; i++) { 46 | words[offset + i] ^= block[i]; 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/mode/ECB.ts: -------------------------------------------------------------------------------- 1 | import { BlockCipherMode } from './BlockCipherMode'; 2 | import { ECBEncryptor } from './ECBEncryptor'; 3 | import { ECBDecryptor } from './ECBDecryptor'; 4 | 5 | /** 6 | * Cipher Block Chaining mode. 7 | */ 8 | export abstract class ECB extends BlockCipherMode { 9 | public static Encryptor: typeof ECBEncryptor = ECBEncryptor; 10 | 11 | public static Decryptor: typeof ECBDecryptor = ECBDecryptor; 12 | } -------------------------------------------------------------------------------- /src/mode/ECBDecryptor.ts: -------------------------------------------------------------------------------- 1 | import { BlockCipherModeAlgorithm } from './BlockCipherModeAlgorithm'; 2 | 3 | export class ECBDecryptor extends BlockCipherModeAlgorithm { 4 | /** 5 | * Processes the data block at offset. 6 | * 7 | * @param words The data words to operate on. 8 | * @param offset The offset where the block starts. 9 | * 10 | * @example 11 | * 12 | * mode.processBlock(data.words, offset); 13 | */ 14 | public processBlock(words: Array, offset: number) { 15 | this._cipher.decryptBlock(words, offset); 16 | } 17 | } -------------------------------------------------------------------------------- /src/mode/ECBEncryptor.ts: -------------------------------------------------------------------------------- 1 | import { BlockCipherModeAlgorithm } from './BlockCipherModeAlgorithm'; 2 | 3 | export class ECBEncryptor extends BlockCipherModeAlgorithm { 4 | /** 5 | * Processes the data block at offset. 6 | * 7 | * @param words The data words to operate on. 8 | * @param offset The offset where the block starts. 9 | * 10 | * @example 11 | * 12 | * mode.processBlock(data.words, offset); 13 | */ 14 | public processBlock(words: Array, offset: number) { 15 | this._cipher.encryptBlock(words, offset); 16 | } 17 | } -------------------------------------------------------------------------------- /src/pad/NoPadding.ts: -------------------------------------------------------------------------------- 1 | import { WordArray } from '../lib/WordArray'; 2 | import { Padding } from '../pad/Padding'; 3 | 4 | export class NoPadding { 5 | /** 6 | * Doesn't pad the data provided. 7 | * 8 | * @param data The data to pad. 9 | * @param blockSize The multiple that the data should be padded to. 10 | * 11 | * @example 12 | * 13 | * NoPadding.pad(wordArray, 4); 14 | */ 15 | public static pad(data: WordArray, blockSize: number): void { 16 | } 17 | 18 | /** 19 | * Doesn't unpad the data provided. 20 | * 21 | * @param data The data to unpad. 22 | * 23 | * @example 24 | * 25 | * NoPadding.unpad(wordArray); 26 | */ 27 | public static unpad(data: WordArray): void { 28 | } 29 | } 30 | 31 | // type guard for the padding (to ensure it has the required static methods) 32 | const _: Padding = NoPadding; -------------------------------------------------------------------------------- /src/pad/PKCS7.ts: -------------------------------------------------------------------------------- 1 | import { WordArray } from '../lib/WordArray'; 2 | import { Padding } from '../pad/Padding'; 3 | 4 | export class PKCS7 { 5 | /** 6 | * Pads data using the algorithm defined in PKCS #5/7. 7 | * 8 | * @param data The data to pad. 9 | * @param blockSize The multiple that the data should be padded to. 10 | * 11 | * @example 12 | * 13 | * PKCS7.pad(wordArray, 4); 14 | */ 15 | public static pad(data: WordArray, blockSize: number): void { 16 | // Shortcut 17 | const blockSizeBytes = blockSize * 4; 18 | 19 | // Count padding bytes 20 | const nPaddingBytes = blockSizeBytes - data.sigBytes % blockSizeBytes; 21 | 22 | // Create padding word 23 | const paddingWord = (nPaddingBytes << 24) | (nPaddingBytes << 16) | (nPaddingBytes << 8) | nPaddingBytes; 24 | 25 | // Create padding 26 | const paddingWords = []; 27 | for (let i = 0; i < nPaddingBytes; i += 4) { 28 | paddingWords.push(paddingWord); 29 | } 30 | const padding = new WordArray(paddingWords, nPaddingBytes); 31 | 32 | // Add padding 33 | data.concat(padding); 34 | } 35 | 36 | /** 37 | * Unpads data that had been padded using the algorithm defined in PKCS #5/7. 38 | * 39 | * @param data The data to unpad. 40 | * 41 | * @example 42 | * 43 | * PKCS7.unpad(wordArray); 44 | */ 45 | public static unpad(data: WordArray): void { 46 | // Get number of padding bytes from last byte 47 | const nPaddingBytes = data.words[(data.sigBytes - 1) >>> 2] & 0xff; 48 | 49 | // Remove padding 50 | data.sigBytes -= nPaddingBytes; 51 | } 52 | } 53 | 54 | // type guard for the formatter (to ensure it has the required static methods) 55 | const _: Padding = PKCS7; -------------------------------------------------------------------------------- /src/pad/Padding.ts: -------------------------------------------------------------------------------- 1 | import { WordArray } from '../lib/WordArray'; 2 | 3 | export interface Padding { 4 | pad: (data: WordArray, blockSize: number) => void; 5 | 6 | unpad: (data: WordArray) => void; 7 | } -------------------------------------------------------------------------------- /tests/algo/AES.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed } from '@angular/core/testing'; 2 | 3 | import { AES, SHA256, enc, mode, pad, lib, algo } from './../../crypto-ts'; 4 | 5 | describe('AES', () => { 6 | 7 | it('EncryptKeySize128', () => { 8 | expect( 9 | AES.encrypt( 10 | enc.Hex.parse('00112233445566778899aabbccddeeff'), 11 | enc.Hex.parse('000102030405060708090a0b0c0d0e0f'), 12 | { 13 | mode: mode.ECB, 14 | padding: pad.NoPadding 15 | } 16 | ).ciphertext!.toString() 17 | ).toEqual('69c4e0d86a7b0430d8cdb78070b4c55a'); 18 | }); 19 | 20 | it('EncryptKeySize192', () => { 21 | expect( 22 | AES.encrypt( 23 | enc.Hex.parse('00112233445566778899aabbccddeeff'), 24 | enc.Hex.parse('000102030405060708090a0b0c0d0e0f1011121314151617'), 25 | { 26 | mode: mode.ECB, 27 | padding: pad.NoPadding 28 | } 29 | ).ciphertext!.toString() 30 | ).toEqual('dda97ca4864cdfe06eaf70a0ec0d7191'); 31 | }); 32 | 33 | it('EncryptKeySize256', () => { 34 | expect( 35 | AES.encrypt( 36 | enc.Hex.parse('00112233445566778899aabbccddeeff'), 37 | enc.Hex.parse('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'), 38 | { 39 | mode: mode.ECB, 40 | padding: pad.NoPadding 41 | } 42 | ).ciphertext!.toString() 43 | ).toEqual('8ea2b7ca516745bfeafc49904b496089'); 44 | }); 45 | 46 | it('DecryptKeySize128', () => { 47 | expect( 48 | AES.decrypt( 49 | new lib.CipherParams({ 50 | ciphertext: enc.Hex.parse('69c4e0d86a7b0430d8cdb78070b4c55a') 51 | }), 52 | enc.Hex.parse('000102030405060708090a0b0c0d0e0f'), 53 | { 54 | mode: mode.ECB, 55 | padding: pad.NoPadding 56 | } 57 | ).toString() 58 | ).toEqual('00112233445566778899aabbccddeeff'); 59 | }); 60 | 61 | it('DecryptKeySize192', () => { 62 | expect( 63 | AES.decrypt( 64 | new lib.CipherParams({ 65 | ciphertext: enc.Hex.parse('dda97ca4864cdfe06eaf70a0ec0d7191') 66 | }), 67 | enc.Hex.parse('000102030405060708090a0b0c0d0e0f1011121314151617'), 68 | { 69 | mode: mode.ECB, 70 | padding: pad.NoPadding 71 | } 72 | ).toString() 73 | ).toEqual('00112233445566778899aabbccddeeff'); 74 | }); 75 | 76 | it('DecryptKeySize256', () => { 77 | expect( 78 | AES.decrypt( 79 | new lib.CipherParams({ 80 | ciphertext: enc.Hex.parse('8ea2b7ca516745bfeafc49904b496089') 81 | }), 82 | enc.Hex.parse('000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f'), 83 | { 84 | mode: mode.ECB, 85 | padding: pad.NoPadding 86 | } 87 | ).toString() 88 | ).toEqual('00112233445566778899aabbccddeeff'); 89 | }); 90 | 91 | it('MultiPart', () => { 92 | const aes = algo.AES.createEncryptor(enc.Hex.parse('000102030405060708090a0b0c0d0e0f'), { mode: mode.ECB, padding: pad.NoPadding }); 93 | const ciphertext1 = aes.process(enc.Hex.parse('001122334455')); 94 | const ciphertext2 = aes.process(enc.Hex.parse('66778899aa')); 95 | const ciphertext3 = aes.process(enc.Hex.parse('bbccddeeff')); 96 | const ciphertext4 = aes.finalize(); 97 | 98 | expect( 99 | ciphertext1.concat(ciphertext2).concat(ciphertext3).concat(ciphertext4).toString() 100 | ).toEqual('69c4e0d86a7b0430d8cdb78070b4c55a'); 101 | }); 102 | 103 | it('InputIntegrity', () => { 104 | const message = enc.Hex.parse('00112233445566778899aabbccddeeff'); 105 | const key = enc.Hex.parse('000102030405060708090a0b0c0d0e0f'); 106 | const iv = enc.Hex.parse('101112131415161718191a1b1c1d1e1f'); 107 | 108 | const expectedMessage = message.toString(); 109 | const expectedKey = key.toString(); 110 | const expectedIv = iv.toString(); 111 | 112 | AES.encrypt(message, key, { iv: iv }); 113 | 114 | expect(expectedMessage).toEqual(message.toString()); 115 | expect(expectedKey).toEqual(key.toString()); 116 | expect(expectedIv).toEqual(iv.toString()); 117 | }); 118 | 119 | it('Helper', () => { 120 | // Save original random method 121 | const random = lib.WordArray.random; 122 | 123 | // Replace random method with one that returns a predictable value 124 | lib.WordArray.random = function(nBytes) { 125 | const words = []; 126 | for(let i = 0; i < nBytes; i += 4) { 127 | words.push(0x11223344); 128 | } 129 | 130 | return new lib.WordArray(words, nBytes); 131 | }; 132 | 133 | // Test 134 | expect( 135 | algo.AES.createEncryptor( 136 | SHA256('Jefe'), { mode: mode.ECB, padding: pad.NoPadding } 137 | ).finalize('Hi There').toString() 138 | ).toEqual( 139 | AES.encrypt( 140 | 'Hi There', SHA256('Jefe'), { mode: mode.ECB, padding: pad.NoPadding } 141 | ).ciphertext!.toString() 142 | ); 143 | 144 | expect( 145 | lib.SerializableCipher.encrypt( 146 | algo.AES, 'Hi There', SHA256('Jefe'), { mode: mode.ECB, padding: pad.NoPadding } 147 | ).toString() 148 | ).toEqual( 149 | AES.encrypt('Hi There', SHA256('Jefe'), { mode: mode.ECB, padding: pad.NoPadding }).toString() 150 | ); 151 | 152 | expect( 153 | lib.PasswordBasedCipher.encrypt(algo.AES, 'Hi There', 'Jefe', { mode: mode.ECB, padding: pad.NoPadding }).toString() 154 | ).toEqual( 155 | AES.encrypt('Hi There', 'Jefe', { mode: mode.ECB, padding: pad.NoPadding }).toString() 156 | ); 157 | 158 | // Restore random method 159 | lib.WordArray.random = random; 160 | }); 161 | }); 162 | -------------------------------------------------------------------------------- /travis-deploy.sh: -------------------------------------------------------------------------------- 1 | cd dist 2 | tar cvfz ../npm-package.tar.gz * 3 | cd .. 4 | ls 5 | npm run travis-deploy-once "npm run semantic-release" -------------------------------------------------------------------------------- /tsconfig-build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "rootDir": ".", 4 | "baseUrl": ".", 5 | "paths": { 6 | "@angular/*": [ 7 | "node_modules/@angular/*" 8 | ] 9 | }, 10 | "outDir": "dist", 11 | "declaration": true, 12 | "strict": true, 13 | "moduleResolution": "node", 14 | "module": "es2015", 15 | "target": "es2015", 16 | "lib": [ 17 | "es2015", 18 | "dom" 19 | ], 20 | "skipLibCheck": true, 21 | "types": [], 22 | "experimentalDecorators": true, 23 | "emitDecoratorMetadata": true, 24 | "sourceMap": true, 25 | "inlineSources": true 26 | }, 27 | "files": [ 28 | "public_api.ts", 29 | "node_modules/zone.js/dist/zone.js.d.ts" 30 | ], 31 | "angularCompilerOptions": { 32 | "skipTemplateCodegen": true, 33 | "annotateForClosureCompiler": true, 34 | "strictMetadataEmit": true, 35 | "flatModuleOutFile": "crypto-ts.js", 36 | "flatModuleId": "crypto-ts" 37 | } 38 | } -------------------------------------------------------------------------------- /tsconfig-test.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "emitDecoratorMetadata": true, 5 | "experimentalDecorators": true, 6 | "strict": true, 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "rootDir": ".", 10 | "sourceMap": true, 11 | "inlineSources": true, 12 | "target": "es5", 13 | "skipLibCheck": true, 14 | "lib": [ 15 | "es2015", 16 | "dom" 17 | ], 18 | "typeRoots": [ 19 | "node_modules/@types/" 20 | ] 21 | }, 22 | "exclude": [ 23 | "node_modules" 24 | ] 25 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "emitDecoratorMetadata": true, 5 | "experimentalDecorators": true, 6 | "strict": true, 7 | "module": "commonjs", 8 | "moduleResolution": "node", 9 | "rootDir": ".", 10 | "sourceMap": true, 11 | "inlineSources": true, 12 | "target": "es5", 13 | "skipLibCheck": true, 14 | "lib": [ 15 | "es2015", 16 | "dom" 17 | ], 18 | "typeRoots": [ 19 | "node_modules/@types/" 20 | ] 21 | }, 22 | "exclude": [ 23 | "node_modules" 24 | ] 25 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "tslint-angular" 4 | ], 5 | "rules": { 6 | "no-bitwise": false, 7 | "eofline": false, 8 | "whitespace": [ 9 | true, 10 | "check-decl", 11 | "check-operator", 12 | "check-separator", 13 | "check-type" 14 | ], 15 | "directive-selector": [ 16 | true, 17 | "attribute", 18 | [ 19 | "dir-prefix1", 20 | "dir-prefix2" 21 | ], 22 | "camelCase" 23 | ], 24 | "component-selector": [ 25 | true, 26 | "element", 27 | [ 28 | "cmp-prefix1", 29 | "cmp-prefix2" 30 | ], 31 | "kebab-case" 32 | ] 33 | } 34 | } --------------------------------------------------------------------------------