Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
163 changes: 96 additions & 67 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@ JavaScript Elliptic curve cryptography library for both browserify and node.

## Motivation

There is currently no any isomorphic ECC library which provides ECDSA, ECDH and ECIES for both Node.js and Browser and uses the fastest implementation available (e.g. [secp256k1-node](https://github.com/wanderer/secp256k1-node) is much faster than other libraries but can be used only on Node.js). So `eccrypto` is an attempt to create one.
- ECDSA (sign/verify)
- ECDH (key agreement)
- ECIES (encrypt/decrypt)
- secp256k1 curve support
- Compressed and uncompressed public key support
- Works in both Node.js and browsers
- Uses `Uint8Array` for all binary data

## Implementation details

With the help of browserify `eccrypto` provides different implementations for Browser and Node.js with the same API. Because WebCryptoAPI defines asynchronous promise-driven API, implementation for Node needs to use promises too.
This library uses [`@noble/curves`](https://github.com/paulmillr/noble-curves) for elliptic curve operations, which provides:

- Use Node.js crypto module/library bindings where possible
- Use WebCryptoAPI where possible
Expand All @@ -26,92 +32,115 @@ With the help of browserify `eccrypto` provides different implementations for Br

#### crypto

ECDH only works in Node 0.11+ (see https://github.com/joyent/node/pull/5854), ECDSA only supports keys in PEM format (see https://github.com/joyent/node/issues/6904) and ECIES is not supported at all.
```bash
npm install @toruslabs/eccrypto
```

#### WebCryptoAPI

ECDSA and ECDH are supported in Chrome [only on Windows](https://sites.google.com/a/chromium.org/dev/blink/webcrypto#TOC-Supported-algorithms-as-of-Chrome-41-) (see also [bug 338883](https://code.google.com/p/chromium/issues/detail?id=338883)), aren't supported by Firefox (fixed only in 36.0+, see [bug 1034854](https://bugzilla.mozilla.org/show_bug.cgi?id=1034854); see also [feature matrix](https://docs.google.com/spreadsheet/ccc?key=0AiAcidBZRLxndE9LWEs2R1oxZ0xidUVoU3FQbFFobkE#gid=1)) and ECIES is not defined at all in WebCryptoAPI draft. Also WebCryptoAPI [currently defines](http://www.w3.org/TR/WebCryptoAPI/#EcKeyGenParams-dictionary) only curves recommended by NIST meaning that secp256k1 (K-256) curve is not supported (see also: [[1]](http://lists.w3.org/Archives/Public/public-webcrypto-comments/2013Dec/0001.html), [[2]](https://bugzilla.mozilla.org/show_bug.cgi?id=1051509)).

So we use [seck256k1](https://www.npmjs.com/package/secp256k1) library in Node for ECDSA, [elliptic](https://www.npmjs.com/package/elliptic) in Browser for ECDSA and ECDH and implement ECIES manually with the help of native crypto API.
```ts
import * as eccrypto from "@toruslabs/eccrypto";

## Possible future goals
// Generate a new random 32-byte private key
const privateKey = eccrypto.generatePrivate();

- Support other curves/KDF/MAC/symmetric encryption schemes
// Get the corresponding public key (65 bytes uncompressed)
const publicKey = eccrypto.getPublic(privateKey);

## Usage
// Or get compressed public key (33 bytes)
const compressedPublicKey = eccrypto.getPublicCompressed(privateKey);

### ECDSA
// Message must be 32 bytes or less (typically a hash)
const msgHash = new Uint8Array(32); // Your message hash here

```js
var crypto = require("crypto");
var eccrypto = require("eccrypto");
// Sign the message
const signature = await eccrypto.sign(privateKey, msgHash);
console.log("Signature (DER format):", signature);

// A new random 32-byte private key.
var privateKey = eccrypto.generatePrivate();
// Corresponding uncompressed (65-byte) public key.
var publicKey = eccrypto.getPublic(privateKey);
// Verify the signature
try {
await eccrypto.verify(publicKey, msgHash, signature);
console.log("Signature is valid");
} catch (e) {
console.log("Signature is invalid");
}
```

var str = "message to sign";
// Always hash you message to sign!
var msg = crypto.createHash("sha256").update(str).digest();
### ECDH (Key Agreement)

eccrypto.sign(privateKey, msg).then(function (sig) {
console.log("Signature in DER format:", sig);
eccrypto
.verify(publicKey, msg, sig)
.then(function () {
console.log("Signature is OK");
})
.catch(function () {
console.log("Signature is BAD");
});
});
```
```ts
import * as eccrypto from "@toruslabs/eccrypto";

### ECDH
const privateKeyA = eccrypto.generatePrivate();
const publicKeyA = eccrypto.getPublic(privateKeyA);

```js
var eccrypto = require("eccrypto");
const privateKeyB = eccrypto.generatePrivate();
const publicKeyB = eccrypto.getPublic(privateKeyB);

var privateKeyA = eccrypto.generatePrivate();
var publicKeyA = eccrypto.getPublic(privateKeyA);
var privateKeyB = eccrypto.generatePrivate();
var publicKeyB = eccrypto.getPublic(privateKeyB);
// Both parties derive the same shared secret
const sharedSecretA = await eccrypto.derive(privateKeyA, publicKeyB);
const sharedSecretB = await eccrypto.derive(privateKeyB, publicKeyA);

eccrypto.derive(privateKeyA, publicKeyB).then(function (sharedKey1) {
eccrypto.derive(privateKeyB, publicKeyA).then(function (sharedKey2) {
console.log("Both shared keys are equal:", sharedKey1, sharedKey2);
});
});
// sharedSecretA and sharedSecretB are equal
console.log("Shared secrets match:", sharedSecretA.toString() === sharedSecretB.toString());
```

### ECIES

```js
var eccrypto = require("eccrypto");

var privateKeyA = eccrypto.generatePrivate();
var publicKeyA = eccrypto.getPublic(privateKeyA);
var privateKeyB = eccrypto.generatePrivate();
var publicKeyB = eccrypto.getPublic(privateKeyB);

// Encrypting the message for B.
eccrypto.encrypt(publicKeyB, Buffer.from("msg to b")).then(function (encrypted) {
// B decrypting the message.
eccrypto.decrypt(privateKeyB, encrypted).then(function (plaintext) {
console.log("Message to part B:", plaintext.toString());
});
});

// Encrypting the message for A.
eccrypto.encrypt(publicKeyA, Buffer.from("msg to a")).then(function (encrypted) {
// A decrypting the message.
eccrypto.decrypt(privateKeyA, encrypted).then(function (plaintext) {
console.log("Message to part A:", plaintext.toString());
});
});
### ECIES (Encrypt/Decrypt)

```ts
import * as eccrypto from "@toruslabs/eccrypto";

const privateKeyA = eccrypto.generatePrivate();
const publicKeyA = eccrypto.getPublic(privateKeyA);

const privateKeyB = eccrypto.generatePrivate();
const publicKeyB = eccrypto.getPublic(privateKeyB);

// Encrypt a message for B
const message = new TextEncoder().encode("Hello, World!");
const encrypted = await eccrypto.encrypt(publicKeyB, message);

// B decrypts the message
const decrypted = await eccrypto.decrypt(privateKeyB, encrypted);
console.log("Decrypted:", new TextDecoder().decode(decrypted));
```

## API

### `generatePrivate(): Uint8Array`

Generate a new random 32-byte private key.

### `getPublic(privateKey: Uint8Array): Uint8Array`

Get the 65-byte uncompressed public key from a private key.

### `getPublicCompressed(privateKey: Uint8Array): Uint8Array`

Get the 33-byte compressed public key from a private key.

### `sign(privateKey: Uint8Array, msg: Uint8Array): Promise<Uint8Array>`

Sign a message (max 32 bytes) with a private key. Returns DER-encoded signature.

### `verify(publicKey: Uint8Array, msg: Uint8Array, sig: Uint8Array): Promise<null>`

Verify a signature. Throws an error if the signature is invalid.

### `derive(privateKey: Uint8Array, publicKey: Uint8Array): Promise<Uint8Array>`

Derive a shared secret using ECDH.

### `encrypt(publicKey: Uint8Array, msg: Uint8Array, opts?): Promise<Ecies>`

Encrypt a message using ECIES. Returns an object with `iv`, `ephemPublicKey`, `ciphertext`, and `mac`.

### `decrypt(privateKey: Uint8Array, opts: Ecies): Promise<Uint8Array>`

Decrypt an ECIES encrypted message.

## License

eccrypto - JavaScript Elliptic curve cryptography library
Expand Down
Loading