├── .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 | }
--------------------------------------------------------------------------------