Skip to content

Commit

Permalink
Merge pull request #14 from ElrondNetwork/development
Browse files Browse the repository at this point in the history
Merge development into main
  • Loading branch information
ccorcoveanu authored Jul 5, 2021
2 parents 5c10d7d + 673cce2 commit e4d88bd
Show file tree
Hide file tree
Showing 18 changed files with 314 additions and 154 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ out
out-tests
out-browser
out-browser-tests
node_modules/
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ All notable changes will be documented in this file.

Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.

## [Unreleased]

## [6.0.0]
- [Added encryption component #11](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/11)
- [Added option to config the axios requests #10](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/10)
- Breaking changes:
1. `new ProxyProvider(url: string, timeout?: number)` becomes `new ProxyProvider(url: string, config?: AxiosRequestConfig)` -
note that `timeout` can still be passed in the `config` object

## [5.0.1] - 07.06.2021

- [Feat/nft token #3](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/3)
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

80 changes: 40 additions & 40 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,48 +27,48 @@
"author": "ElrondNetwork",
"license": "GPL-3.0-or-later",
"dependencies": {
"@babel/runtime": "^7.11.2",
"@elrondnetwork/bls-wasm": "^0.3.3",
"@babel/runtime": "7.11.2",
"@elrondnetwork/bls-wasm": "0.3.3",
"@elrondnetwork/hw-app-elrond": "0.2.0",
"@ledgerhq/hw-transport-u2f": "^5.28.0",
"@ledgerhq/hw-transport-webusb": "^5.28.0",
"@walletconnect/client": "^1.4.1",
"abort-controller": "^3.0.0",
"axios": "^0.21.1",
"bech32": "^1.1.4",
"bignumber.js": "^9.0.0",
"bip39": "^3.0.2",
"blake2b": "^2.1.3",
"ed25519-hd-key": "^1.1.2",
"json-bigint": "^1.0.0",
"json-duplicate-key-handle": "^1.0.0",
"keccak": "^3.0.1",
"platform": "^1.3.6",
"protobufjs": "^6.10.2",
"scryptsy": "^2.1.0",
"tweetnacl": "^1.0.3",
"uuid": "^8.3.2"
"@ledgerhq/hw-transport-u2f": "5.28.0",
"@ledgerhq/hw-transport-webusb": "5.28.0",
"@walletconnect/client": "1.4.1",
"abort-controller": "3.0.0",
"axios": "0.21.1",
"bech32": "1.1.4",
"bignumber.js": "9.0.1",
"bip39": "3.0.2",
"blake2b": "2.1.3",
"ed25519-hd-key": "1.1.2",
"json-bigint": "1.0.0",
"json-duplicate-key-handle": "1.0.0",
"keccak": "3.0.1",
"platform": "1.3.6",
"protobufjs": "6.10.2",
"scryptsy": "2.1.0",
"tweetnacl": "1.0.3",
"uuid": "8.3.2"
},
"devDependencies": {
"@types/assert": "^1.4.6",
"@types/chai": "^4.2.11",
"@types/ledgerhq__hw-transport-u2f": "^4.21.2",
"@types/ledgerhq__hw-transport-webusb": "^4.70.1",
"@types/mocha": "^7.0.2",
"@types/node": "^13.13.2",
"@types/platform": "^1.3.3",
"@types/protobufjs": "^6.0.0",
"@types/scryptsy": "^2.0.0",
"@types/uuid": "^8.3.0",
"assert": "^2.0.0",
"browserify": "^17.0.0",
"buffer": "^6.0.3",
"chai": "^4.2.0",
"mocha": "^7.1.1",
"tinyify": "^3.0.0",
"ts-node": "^9.1.1",
"tslint": "^6.1.3",
"typedoc": "^0.20.36",
"typescript": "^4.1.2"
"@types/assert": "1.4.6",
"@types/chai": "4.2.11",
"@types/ledgerhq__hw-transport-u2f": "4.21.2",
"@types/ledgerhq__hw-transport-webusb": "4.70.1",
"@types/mocha": "7.0.2",
"@types/node": "13.13.2",
"@types/platform": "1.3.3",
"@types/protobufjs": "6.0.0",
"@types/scryptsy": "2.0.0",
"@types/uuid": "8.3.0",
"assert": "2.0.0",
"browserify": "17.0.0",
"buffer": "6.0.3",
"chai": "4.2.0",
"mocha": "7.1.1",
"tinyify": "3.0.0",
"ts-node": "9.1.1",
"tslint": "6.1.3",
"typedoc": "0.20.36",
"typescript": "4.1.2"
}
}
16 changes: 9 additions & 7 deletions src/apiProvider.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import axios from "axios";
import axios, { AxiosRequestConfig } from "axios";
import { IApiProvider } from "./interface";
import * as errors from "./errors";
import { Logger } from "./logger";
Expand All @@ -15,17 +15,19 @@ const JSONbig = require("json-bigint");
*/
export class ApiProvider implements IApiProvider {
private url: string;
private timeoutLimit: number;
private config: AxiosRequestConfig;

/**
* Creates a new ApiProvider.
* @param url the URL of the Elrond Api
* @param timeout the timeout for any request-response, in milliseconds
* @param config axios request config options
*/
constructor(url: string, timeout?: number) {
constructor(url: string, config?: AxiosRequestConfig) {
this.url = url;
this.timeoutLimit = timeout || 1000;
}
this.config = config || {
timeout: 1000,
};
}

/**
* Fetches the Network Stake.
Expand Down Expand Up @@ -69,7 +71,7 @@ export class ApiProvider implements IApiProvider {
private async doGet(resourceUrl: string): Promise<any> {
try {
let url = `${this.url}/${resourceUrl}`;
let response = await axios.get(url, { timeout: this.timeoutLimit });
let response = await axios.get(url, this.config);

return response.data;
} catch (error) {
Expand Down
5 changes: 5 additions & 0 deletions src/crypto/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// In a future PR, improve versioning infrastructure for key-file objects in erdjs.
export const Version = 4;
export const CipherAlgorithm = "aes-128-ctr";
export const DigestAlgorithm = "sha256";
export const KeyDerivationFunction = "scrypt";
27 changes: 27 additions & 0 deletions src/crypto/decryptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import crypto from "crypto";
import {EncryptedData} from "./encryptedData";
import * as errors from "../errors";
import { DigestAlgorithm } from "./constants";

export class Decryptor {
public static decrypt(data: EncryptedData, password: string): Buffer {
const kdfparams = data.kdfparams;
const salt = Buffer.from(data.salt, "hex");
const iv = Buffer.from(data.iv, "hex");
const ciphertext = Buffer.from(data.ciphertext, "hex");
const derivedKey = kdfparams.generateDerivedKey(Buffer.from(password), salt);
const derivedKeyFirstHalf = derivedKey.slice(0, 16);
const derivedKeySecondHalf = derivedKey.slice(16, 32);

const computedMAC = crypto.createHmac(DigestAlgorithm, derivedKeySecondHalf).update(ciphertext).digest();
const actualMAC = data.mac;

if (computedMAC.toString("hex") !== actualMAC) {
throw new errors.ErrWallet("MAC mismatch, possibly wrong password");
}

const decipher = crypto.createDecipheriv(data.cipher, derivedKeyFirstHalf, iv);

return Buffer.concat([decipher.update(ciphertext), decipher.final()]);
}
}
36 changes: 36 additions & 0 deletions src/crypto/derivationParams.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import scryptsy from "scryptsy";

export class ScryptKeyDerivationParams {
/**
* numIterations
*/
n = 4096;

/**
* memFactor
*/
r = 8;

/**
* pFactor
*/
p = 1;

dklen = 32;

constructor(n = 4096, r = 8, p = 1, dklen = 32) {
this.n = n;
this. r = r;
this.p = p;
this.dklen = dklen;
}

/**
* Will take about:
* - 80-90 ms in Node.js, on a i3-8100 CPU @ 3.60GHz
* - 350-360 ms in browser (Firefox), on a i3-8100 CPU @ 3.60GHz
*/
public generateDerivedKey(password: Buffer, salt: Buffer): Buffer {
return scryptsy(password, salt, this.n, this.r, this.p, this.dklen);
}
}
22 changes: 22 additions & 0 deletions src/crypto/encrypt.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { assert } from "chai";
import { Encryptor } from "./encryptor";
import { Decryptor } from "./decryptor";
import { EncryptedData } from "./encryptedData";

describe("test address", () => {
it("encrypts/decrypts", () => {
const sensitiveData = Buffer.from("my mnemonic");
const encryptedData = Encryptor.encrypt(sensitiveData, "password123");
const decryptedBuffer = Decryptor.decrypt(encryptedData, "password123");

assert.equal(sensitiveData.toString('hex'), decryptedBuffer.toString('hex'));
});

it("encodes/decodes kdfparams", () => {
const sensitiveData = Buffer.from("my mnemonic");
const encryptedData = Encryptor.encrypt(sensitiveData, "password123");
const decodedData = EncryptedData.fromJSON(encryptedData.toJSON());

assert.deepEqual(decodedData, encryptedData, "invalid decoded data");
});
});
65 changes: 65 additions & 0 deletions src/crypto/encryptedData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { ScryptKeyDerivationParams } from "./derivationParams";

export class EncryptedData {
id: string;
version: number;
cipher: string;
ciphertext: string;
iv: string;
kdf: string;
kdfparams: ScryptKeyDerivationParams;
salt: string;
mac: string;

constructor(data: Omit<EncryptedData, "toJSON">) {
this.id = data.id;
this.version = data.version;
this.ciphertext = data.ciphertext;
this.iv = data.iv;
this.cipher = data.cipher;
this.kdf = data.kdf;
this.kdfparams = data.kdfparams;
this.mac = data.mac;
this.salt = data.salt;
}

toJSON(): any {
return {
version: this.version,
id: this.id,
crypto: {
ciphertext: this.ciphertext,
cipherparams: { iv: this.iv },
cipher: this.cipher,
kdf: this.kdf,
kdfparams: {
dklen: this.kdfparams.dklen,
salt: this.salt,
n: this.kdfparams.n,
r: this.kdfparams.r,
p: this.kdfparams.p
},
mac: this.mac,
}
};
}

static fromJSON(data: any): EncryptedData {
return new EncryptedData({
version: data.version,
id: data.id,
ciphertext: data.crypto.ciphertext,
iv: data.crypto.cipherparams.iv,
cipher: data.crypto.cipher,
kdf: data.crypto.kdf,
kdfparams: new ScryptKeyDerivationParams(
data.crypto.kdfparams.n,
data.crypto.kdfparams.r,
data.crypto.kdfparams.p,
data.crypto.kdfparams.dklen,
),
salt: data.crypto.kdfparams.salt,
mac: data.crypto.mac,
});
}
}
30 changes: 30 additions & 0 deletions src/crypto/encryptor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import crypto from "crypto";
import { Randomness } from "./randomness";
import { ScryptKeyDerivationParams } from "./derivationParams";
import { CipherAlgorithm, DigestAlgorithm, Version, KeyDerivationFunction } from "./constants";
import {EncryptedData} from "./encryptedData";

export class Encryptor {
public static encrypt(data: Buffer, password: string, randomness: Randomness = new Randomness()): EncryptedData {
const kdParams = new ScryptKeyDerivationParams();
const derivedKey = kdParams.generateDerivedKey(Buffer.from(password), randomness.salt);
const derivedKeyFirstHalf = derivedKey.slice(0, 16);
const derivedKeySecondHalf = derivedKey.slice(16, 32);
const cipher = crypto.createCipheriv(CipherAlgorithm, derivedKeyFirstHalf, randomness.iv);

const ciphertext = Buffer.concat([cipher.update(data), cipher.final()]);
const mac = crypto.createHmac(DigestAlgorithm, derivedKeySecondHalf).update(ciphertext).digest();

return new EncryptedData({
version: Version,
id: randomness.id,
ciphertext: ciphertext.toString('hex'),
iv: randomness.iv.toString('hex'),
cipher: CipherAlgorithm,
kdf: KeyDerivationFunction,
kdfparams: kdParams,
mac: mac.toString('hex'),
salt: randomness.salt.toString('hex')
});
}
}
5 changes: 5 additions & 0 deletions src/crypto/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from "./constants";
export * from "./encryptor";
export * from "./decryptor";
export * from "./encryptedData";
export * from "./randomness";
15 changes: 15 additions & 0 deletions src/crypto/randomness.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import nacl from "tweetnacl";
import {v4 as uuidv4} from "uuid";
const crypto = require("crypto");

export class Randomness {
salt: Buffer;
iv: Buffer;
id: string;

constructor(init?: Partial<Randomness>) {
this.salt = init?.salt || Buffer.from(nacl.randomBytes(32));
this.iv = init?.iv || Buffer.from(nacl.randomBytes(16));
this.id = init?.id || uuidv4({ random: crypto.randomBytes(16) });
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export * from "./apiProvider";
export * from "./scArgumentsParser";
export * from "./esdtHelpers";

export * from "./crypto";
export * from "./walletcore";
export * from "./nullSigner";

Expand Down
Loading

0 comments on commit e4d88bd

Please sign in to comment.