Skip to content

Commit a813acb

Browse files
committed
Add AES submodule
1 parent fbf6ef0 commit a813acb

File tree

8 files changed

+356
-14
lines changed

8 files changed

+356
-14
lines changed

.editorconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ root = true
44
indent_style = space
55
indent_size = 2
66
charset = utf-8
7-
trim_trailing_whitespace = false
7+
trim_trailing_whitespace = true
88
insert_final_newline = true

packages/ethereum-cryptography/README.md

Lines changed: 101 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
⚠️ **WARNING: This projects is under active development. Don't use it until a stable version is released.** ⚠️
99

10-
This npm package contains all the cryptographic primitives normally used when
10+
This npm package contains all the cryptographic primitives normally used when
1111
developing Javascript/TypeScript applications and tools for Ethereum.
1212

1313
Pure Javascript implementations of all the primitives are included, so it can
@@ -24,6 +24,7 @@ The cryptographic primitives included are:
2424
* `pbkdf2`
2525
* `sha256`
2626
* `ripemd160`
27+
* `aes`
2728
* `secp256k1`
2829

2930
## Installation
@@ -42,15 +43,15 @@ $ yarn add ethereum-cryptography
4243

4344
## Usage
4445

45-
There's a submodule available for each cryprographic primitive.
46+
There's a submodule available for each cryprographic primitive.
4647

47-
No `index.js`/`main` is provided, as that would lead to huge bundles when using
48+
No `index.js`/`main` is provided, as that would lead to huge bundles when using
4849
this package for the web.
4950

5051
## keccak submodule
5152

52-
The `keccack` submodule has four functions that receive a `Buffer` with the
53-
message to hash, and return a `Buffer` with the hash. These are `keccak224`,
53+
The `keccack` submodule has four functions that receive a `Buffer` with the
54+
message to hash, and return a `Buffer` with the hash. These are `keccak224`,
5455
`keccak256`, `keccak384`, and `keccak512`.
5556

5657
### Function types
@@ -82,8 +83,8 @@ as it will block its main thread and hang your ui.
8283

8384
### Password encoding
8485

85-
Encoding passwords is a frequent source of errors. Please read
86-
[these notes](https://github.com/ricmoo/scrypt-js/tree/0eb70873ddf3d24e34b53e0d9a99a0cef06a79c0#encoding-notes)
86+
Encoding passwords is a frequent source of errors. Please read
87+
[these notes](https://github.com/ricmoo/scrypt-js/tree/0eb70873ddf3d24e34b53e0d9a99a0cef06a79c0#encoding-notes)
8788
before using this submodule.
8889

8990
### Function types
@@ -192,10 +193,97 @@ const { ripemd160 } = require("ethereum-cryptography/ripemd160");
192193
console.log(ripemd160(Buffer.from("message", "ascii")).toString("hex"));
193194
```
194195

196+
## ripemd160 submodule
197+
198+
The `ripemd160` submodule contains a single functions implementing the
199+
`ripemd160` hash algorithm.
200+
201+
### Function types
202+
203+
```ts
204+
function ripemd160(msg: Buffer): Buffer;
205+
```
206+
207+
### Example usage
208+
209+
```js
210+
const { ripemd160 } = require("ethereum-cryptography/ripemd160");
211+
212+
console.log(ripemd160(Buffer.from("message", "ascii")).toString("hex"));
213+
```
214+
215+
## AES submodule
216+
217+
The `aes` submodule contains encryption and decryption functions implementing
218+
the [AES algorithm](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard).
219+
220+
### Function types
221+
222+
```ts
223+
function encrypt(msg: Buffer, key: Buffer, iv: Buffer, mode: string, pkcs7PaddingEnabled = true): Buffer;
224+
225+
function decrypt(cypherText: Buffer, key: Buffer, iv: Buffer, mode: string, pkcs7PaddingEnabled = true): Buffer
226+
```
227+
228+
### Operation modes
229+
230+
This submodules works with different AES
231+
[modes of operation](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation).
232+
To choose one of them, you should pass the `mode` parameter a string with the
233+
same format as OpenSSL and Node use. You can take a look at them by running
234+
`openssl list -cipher-algorithms`.
235+
236+
In Node, any mode that its OpenSSL version supports can be used.
237+
238+
In the browser we test it to work with the modes that are normally used in
239+
Ethereum libraries and applications. Those are `aes-128-ctr`, `aes-126-cbc`, and
240+
`aes-256-cbc`, but other modes may work.
241+
242+
### Encrypting with passwords
243+
244+
AES is not supposed to be used directly with a password. Doing that will
245+
compromise your users' security.
246+
247+
The `key` parameters in this submodule are meant to be strong cryptographic
248+
keys. If you want to obtain such a key from a password, please use a
249+
[key derivation function](https://en.wikipedia.org/wiki/Key_derivation_function)
250+
like [pbkdf2](#pbkdf2-submodule) or [scrypt](#scrypt-submodule).
251+
252+
### Padding plaintext messages
253+
254+
Some operation modes require the plaintext message to be a multiple of `16`. If
255+
that isn't the case, your message has to be padded.
256+
257+
By default, this module automatically pads your messages according to [PKCS#7](https://tools.ietf.org/html/rfc2315).
258+
Note that this padding scheme always adds at least 1 byte of paddding. If you
259+
are unsure what anything of this means, we **strongly** recommend you to use
260+
the defaults.
261+
262+
If you need to encrypt without padding, or want to use another padding scheme,
263+
you can disable PKCS#7 padding by passing `false` as the last argument and
264+
handling padding yourself. Note that if you do this and your operation mode
265+
requires padding, `encrypt` will throw if your plaintext message isn't a
266+
multiple of `16.
267+
268+
### Example usage
269+
270+
```js
271+
const { encrypt } = require("ethereum-cryptography/aes");
272+
273+
console.log(
274+
encrypt(
275+
Buffer.from("message", "ascii"),
276+
Buffer.from("2b7e151628aed2a6abf7158809cf4f3c", "hex"),
277+
Buffer.from("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", "hex"),
278+
"aes-128-cbc"
279+
).toString("hex")
280+
);
281+
```
282+
195283
## secp256k1 submodule
196284
197-
The `secp256k1` submodule has the same API than the native module
198-
[`secp256k1` from cryptocoinjs](https://github.com/cryptocoinjs/secp256k1-node)
285+
The `secp256k1` submodule has the same API than the native module
286+
[`secp256k1` from cryptocoinjs](https://github.com/cryptocoinjs/secp256k1-node)
199287
version `3.x`, but it's backed by [`elliptic`](https://www.npmjs.com/package/elliptic).
200288
201289
### Function types
@@ -227,7 +315,7 @@ tested with `webpack`, `Rollup`, `Parcel`, and `Browserify`.
227315
228316
For using it with `Rollup` you need to use these plugins:
229317
[`rollup-plugin-node-builtins`](https://www.npmjs.com/package/rollup-plugin-node-builtins),
230-
[`rollup-plugin-node-globals`](https://www.npmjs.com/package/rollup-plugin-node-globals),
318+
[`rollup-plugin-node-globals`](https://www.npmjs.com/package/rollup-plugin-node-globals),
231319
and [`rollup-plugin-json`](https://www.npmjs.com/package/rollup-plugin-json).
232320
233321
## Opt-in native implementations (Node.js only)
@@ -237,11 +325,11 @@ If you are using this package in Node, you can install
237325
to opt-in to use native implementations of some of the cryptographic primitives
238326
provided by this package.
239327
240-
No extra work is needed for this to work. This package will detect that
328+
No extra work is needed for this to work. This package will detect that
241329
`ethereum-cryptography-native` is installed, and use it.
242330
243-
While installing `ethereum-cryptography-native` will generally improve the
244-
performance of your application, we recommend leaving the decision of installing
331+
While installing `ethereum-cryptography-native` will generally improve the
332+
performance of your application, we recommend leaving the decision of installing
245333
it to your users. It has multiple native dependencies that need to be compiled,
246334
and this can be problematic in some environments.
247335

packages/ethereum-cryptography/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
"@types/secp256k1": "^3.5.0",
9191
"bip66": "^1.1.5",
9292
"bn.js": "^4.11.8",
93+
"browserify-aes": "^1.2.0",
9394
"create-hash": "^1.2.0",
9495
"elliptic": "^6.4.1",
9596
"hash.js": "^1.1.7",
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { createCipheriv, createDecipheriv } from "crypto";
2+
3+
function ensureAesMode(mode: string) {
4+
if (!mode.startsWith("aes-")) {
5+
throw new Error(`AES submodule doesn't support mode ${mode}`);
6+
}
7+
}
8+
9+
export function encrypt(
10+
msg: Buffer,
11+
key: Buffer,
12+
iv: Buffer,
13+
mode: string,
14+
pkcs7PaddingEnabled = true
15+
): Buffer {
16+
ensureAesMode(mode);
17+
18+
const cipher = createCipheriv(mode, key, iv);
19+
cipher.setAutoPadding(pkcs7PaddingEnabled);
20+
21+
const encrypted = cipher.update(msg);
22+
const final = cipher.final();
23+
24+
return Buffer.concat([encrypted, final]);
25+
}
26+
27+
export function decrypt(
28+
cypherText: Buffer,
29+
key: Buffer,
30+
iv: Buffer,
31+
mode: string,
32+
pkcs7PaddingEnabled = true
33+
): Buffer {
34+
ensureAesMode(mode);
35+
36+
const decipher = createDecipheriv(mode, key, iv);
37+
decipher.setAutoPadding(pkcs7PaddingEnabled);
38+
39+
const encrypted = decipher.update(cypherText);
40+
const final = decipher.final();
41+
42+
return Buffer.concat([encrypted, final]);
43+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
const browserifyAes = require("browserify-aes");
2+
3+
function ensureAesMode(mode: string) {
4+
if (!mode.startsWith("aes-")) {
5+
throw new Error(`AES submodule doesn't support mode ${mode}`);
6+
}
7+
}
8+
9+
export function encrypt(
10+
msg: Buffer,
11+
key: Buffer,
12+
iv: Buffer,
13+
mode: string,
14+
pkcs7PaddingEnabled = true
15+
): Buffer {
16+
ensureAesMode(mode);
17+
18+
const cipher = browserifyAes.createCipheriv(mode, key, iv);
19+
cipher.setAutoPadding(pkcs7PaddingEnabled);
20+
21+
const encrypted = cipher.update(msg);
22+
const final = cipher.final();
23+
24+
return Buffer.concat([encrypted, final]);
25+
}
26+
27+
export function decrypt(
28+
cypherText: Buffer,
29+
key: Buffer,
30+
iv: Buffer,
31+
mode: string,
32+
pkcs7PaddingEnabled = true
33+
): Buffer {
34+
ensureAesMode(mode);
35+
36+
const decipher = browserifyAes.createDecipheriv(mode, key, iv);
37+
decipher.setAutoPadding(pkcs7PaddingEnabled);
38+
39+
const encrypted = decipher.update(cypherText);
40+
const final = decipher.final();
41+
42+
return Buffer.concat([encrypted, final]);
43+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { decrypt, encrypt } from "../../src/aes";
2+
import { createTests } from "../test-vectors/aes";
3+
4+
createTests(encrypt, decrypt);
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { decrypt, encrypt } from "../../src/pure/aes";
2+
import { createTests } from "../test-vectors/aes";
3+
4+
createTests(encrypt, decrypt);

0 commit comments

Comments
 (0)