├── .nvmrc ├── contracts ├── .gitignore ├── TestContract.sol └── DonationBag.sol ├── .eslintignore ├── .gitattributes ├── .npmrc ├── test ├── .eslintrc ├── karma.test.js ├── index.test.js ├── bug-template.test.js ├── issues.test.js ├── tutorials │ ├── encrypted-message.test.js │ └── signed-data.test.js ├── typings.test.js ├── performance.test.js ├── integration.test.js └── unit.test.js ├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── dist ├── es │ ├── browserify.index.js │ ├── util.js │ ├── calculate-contract-address.js │ ├── recover.js │ ├── hash.js │ ├── tx-data-by-compiled.js │ ├── public-key-by-private-key.js │ ├── vrs.js │ ├── decrypt-with-private-key.js │ ├── encrypt-with-public-key.js │ ├── sign.js │ ├── recover-public-key.js │ ├── cipher.js │ ├── public-key.js │ ├── hex.js │ ├── index.js │ ├── create-identity.js │ └── sign-transaction.js └── lib │ ├── browserify.index.js │ ├── recover.js │ ├── calculate-contract-address.js │ ├── hash.js │ ├── util.js │ ├── public-key-by-private-key.js │ ├── vrs.js │ ├── tx-data-by-compiled.js │ ├── decrypt-with-private-key.js │ ├── encrypt-with-public-key.js │ ├── sign.js │ ├── recover-public-key.js │ ├── cipher.js │ ├── public-key.js │ ├── hex.js │ ├── create-identity.js │ ├── sign-transaction.js │ └── index.js ├── src ├── browserify.index.js ├── util.js ├── recover.js ├── tx-data-by-compiled.js ├── hash.js ├── calculate-contract-address.js ├── public-key-by-private-key.js ├── vrs.js ├── decrypt-with-private-key.js ├── encrypt-with-public-key.js ├── sign.js ├── sign-transaction.js ├── recover-public-key.js ├── cipher.js ├── hex.js ├── index.js ├── create-identity.js └── public-key.js ├── .gitignore ├── .solhint.json ├── renovate.json ├── .npmignore ├── .babelrc ├── ISSUE_TEMPLATE.md ├── config ├── webpack.config.js └── karma.conf.js ├── LICENSE ├── README ├── tutorials ├── encrypted-message.md ├── creating-transactions.md └── signed-data.md ├── typings └── index.d.ts ├── package.json ├── .eslintrc.json └── README.md /.nvmrc: -------------------------------------------------------------------------------- 1 | 24.12.0 2 | -------------------------------------------------------------------------------- /contracts/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | *.ts 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | gen/ 3 | test_tmp/ 4 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | unsafe-perm = true 2 | package-lock=false 3 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | env: 2 | node: true 3 | mocha: true -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: pubkey 4 | -------------------------------------------------------------------------------- /dist/es/browserify.index.js: -------------------------------------------------------------------------------- 1 | var EthCrypto = require('./index.js'); 2 | window['EthCrypto'] = EthCrypto; -------------------------------------------------------------------------------- /src/browserify.index.js: -------------------------------------------------------------------------------- 1 | const EthCrypto = require('./index.js'); 2 | 3 | window['EthCrypto'] = EthCrypto; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | core 4 | log.txt 5 | package-lock.json 6 | gen/ 7 | test_tmp/ 8 | -------------------------------------------------------------------------------- /test/karma.test.js: -------------------------------------------------------------------------------- 1 | require('./unit.test'); 2 | require('./issues.test'); 3 | require('./performance.test'); 4 | -------------------------------------------------------------------------------- /dist/lib/browserify.index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var EthCrypto = require('./index.js'); 4 | window['EthCrypto'] = EthCrypto; -------------------------------------------------------------------------------- /.solhint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solhint:default", 3 | "rules": { 4 | "func-order": "off", 5 | "not-rely-on-time": "off" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "ignoreDeps": [ 6 | "webpack" 7 | ], 8 | "statusCheckVerify": true, 9 | "automerge": true, 10 | "rebaseStalePrs": true, 11 | "prHourlyLimit": 4, 12 | "dependencyDashboard": false 13 | } 14 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | .babelrc 3 | .eslintignore 4 | .eslintrc.json 5 | .solhint.json 6 | .travis.yml 7 | package-lock.json 8 | gen/ 9 | scripts/ 10 | test_tmp/ 11 | perf.txt 12 | ISSUE_TEMPLATE.md 13 | config/ 14 | test/ 15 | .gitattributes 16 | renovate.json 17 | tutorials/ 18 | contracts/ -------------------------------------------------------------------------------- /test/index.test.js: -------------------------------------------------------------------------------- 1 | 2 | require('./unit.test'); 3 | require('./integration.test'); 4 | 5 | require('./bug-template.test'); 6 | require('./issues.test'); 7 | 8 | // tutorials 9 | require('./tutorials/signed-data.test'); 10 | require('./tutorials/encrypted-message.test'); 11 | 12 | require('./performance.test'); 13 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"], 3 | "plugins": ["@babel/plugin-transform-runtime"], 4 | "env": { 5 | "es6": { 6 | "presets": [ 7 | [ 8 | "@babel/preset-env", 9 | { 10 | "modules": false 11 | } 12 | ] 13 | ] 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /dist/es/util.js: -------------------------------------------------------------------------------- 1 | export function removeLeading0x(str) { 2 | if (str.startsWith('0x')) return str.substring(2);else return str; 3 | } 4 | export function addLeading0x(str) { 5 | if (!str.startsWith('0x')) return '0x' + str;else return str; 6 | } 7 | export function uint8ArrayToHex(arr) { 8 | return Buffer.from(arr).toString('hex'); 9 | } 10 | export function hexToUnit8Array(str) { 11 | return new Uint8Array(Buffer.from(str, 'hex')); 12 | } -------------------------------------------------------------------------------- /dist/es/calculate-contract-address.js: -------------------------------------------------------------------------------- 1 | import { generateAddress, toChecksumAddress, hexToBytes, bytesToHex, intToHex } from '@ethereumjs/util'; 2 | import { addLeading0x } from './util'; 3 | export function calculateContractAddress(creatorAddress, nonce) { 4 | var addressBuffer = generateAddress(hexToBytes(addLeading0x(creatorAddress)), hexToBytes(intToHex(nonce))); 5 | var address = bytesToHex(addressBuffer); 6 | return toChecksumAddress(addLeading0x(address)); 7 | } -------------------------------------------------------------------------------- /dist/es/recover.js: -------------------------------------------------------------------------------- 1 | import { recoverPublicKey } from './recover-public-key'; 2 | import { toAddress as addressByPublicKey } from './public-key'; 3 | 4 | /** 5 | * returns the address with which the messageHash was signed 6 | * @param {string} sigString 7 | * @param {string} hash 8 | * @return {string} address 9 | */ 10 | export function recover(sigString, hash) { 11 | var pubkey = recoverPublicKey(sigString, hash); 12 | var address = addressByPublicKey(pubkey); 13 | return address; 14 | } -------------------------------------------------------------------------------- /dist/es/hash.js: -------------------------------------------------------------------------------- 1 | import { utils as ethersUtils } from 'ethers'; 2 | export function keccak256(params) { 3 | var types = []; 4 | var values = []; 5 | if (!Array.isArray(params)) { 6 | types.push('string'); 7 | values.push(params); 8 | } else { 9 | params.forEach(function (p) { 10 | types.push(p.type); 11 | values.push(p.value); 12 | }); 13 | } 14 | return ethersUtils.solidityKeccak256(types, values); 15 | } 16 | export var SIGN_PREFIX = '\x19Ethereum Signed Message:\n32'; -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | export function removeLeading0x(str) { 2 | if (str.startsWith('0x')) 3 | return str.substring(2); 4 | else return str; 5 | } 6 | 7 | export function addLeading0x(str) { 8 | if (!str.startsWith('0x')) 9 | return '0x' + str; 10 | else return str; 11 | } 12 | 13 | export function uint8ArrayToHex(arr) { 14 | return Buffer.from(arr).toString('hex'); 15 | } 16 | 17 | export function hexToUnit8Array(str) { 18 | return new Uint8Array(Buffer.from(str, 'hex')); 19 | } 20 | -------------------------------------------------------------------------------- /src/recover.js: -------------------------------------------------------------------------------- 1 | import { recoverPublicKey } from './recover-public-key'; 2 | import { 3 | toAddress as addressByPublicKey 4 | } from './public-key'; 5 | 6 | /** 7 | * returns the address with which the messageHash was signed 8 | * @param {string} sigString 9 | * @param {string} hash 10 | * @return {string} address 11 | */ 12 | export function recover(sigString, hash) { 13 | const pubkey = recoverPublicKey(sigString, hash); 14 | const address = addressByPublicKey(pubkey); 15 | return address; 16 | } 17 | -------------------------------------------------------------------------------- /src/tx-data-by-compiled.js: -------------------------------------------------------------------------------- 1 | import { ContractFactory } from 'ethers'; 2 | 3 | export function txDataByCompiled( 4 | abi, 5 | bytecode, 6 | args 7 | ) { 8 | // solc returns a string which is often passed instead of the json 9 | if (typeof abi === 'string') abi = JSON.parse(abi); 10 | 11 | // Construct a Contract Factory 12 | const factory = new ContractFactory(abi, '0x' + bytecode); 13 | 14 | const deployTransaction = factory.getDeployTransaction(...args); 15 | 16 | return deployTransaction.data; 17 | } 18 | -------------------------------------------------------------------------------- /src/hash.js: -------------------------------------------------------------------------------- 1 | import { 2 | utils as ethersUtils 3 | } from 'ethers'; 4 | 5 | 6 | export function keccak256(params) { 7 | const types = []; 8 | const values = []; 9 | if (!Array.isArray(params)) { 10 | types.push('string'); 11 | values.push(params); 12 | }else { 13 | params.forEach(p => { 14 | types.push(p.type); 15 | values.push(p.value); 16 | }); 17 | } 18 | return ethersUtils.solidityKeccak256(types, values); 19 | } 20 | 21 | export const SIGN_PREFIX = '\x19Ethereum Signed Message:\n32'; 22 | -------------------------------------------------------------------------------- /dist/es/tx-data-by-compiled.js: -------------------------------------------------------------------------------- 1 | import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray"; 2 | import { ContractFactory } from 'ethers'; 3 | export function txDataByCompiled(abi, bytecode, args) { 4 | // solc returns a string which is often passed instead of the json 5 | if (typeof abi === 'string') abi = JSON.parse(abi); 6 | 7 | // Construct a Contract Factory 8 | var factory = new ContractFactory(abi, '0x' + bytecode); 9 | var deployTransaction = factory.getDeployTransaction.apply(factory, _toConsumableArray(args)); 10 | return deployTransaction.data; 11 | } -------------------------------------------------------------------------------- /dist/es/public-key-by-private-key.js: -------------------------------------------------------------------------------- 1 | import { privateToPublic, hexToBytes, bytesToHex } from '@ethereumjs/util'; 2 | import { addLeading0x, removeLeading0x } from './util'; 3 | 4 | /** 5 | * Generate publicKey from the privateKey. 6 | * This creates the uncompressed publicKey, 7 | * where 04 has stripped from left 8 | * @returns {string} 9 | */ 10 | export function publicKeyByPrivateKey(privateKey) { 11 | privateKey = addLeading0x(privateKey); 12 | var publicKeyBuffer = privateToPublic(hexToBytes(privateKey)); 13 | var ret = removeLeading0x(bytesToHex(publicKeyBuffer)); 14 | return ret; 15 | } -------------------------------------------------------------------------------- /src/calculate-contract-address.js: -------------------------------------------------------------------------------- 1 | import { 2 | generateAddress, 3 | toChecksumAddress, 4 | hexToBytes, 5 | bytesToHex, 6 | intToHex 7 | } from '@ethereumjs/util'; 8 | import { 9 | addLeading0x 10 | } from './util'; 11 | 12 | 13 | export function calculateContractAddress( 14 | creatorAddress, 15 | nonce 16 | ) { 17 | const addressBuffer = generateAddress( 18 | hexToBytes(addLeading0x(creatorAddress)), 19 | hexToBytes(intToHex(nonce)) 20 | ); 21 | const address = bytesToHex(addressBuffer); 22 | return toChecksumAddress(addLeading0x(address)); 23 | } 24 | -------------------------------------------------------------------------------- /dist/lib/recover.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.recover = recover; 7 | var _recoverPublicKey = require("./recover-public-key"); 8 | var _publicKey = require("./public-key"); 9 | /** 10 | * returns the address with which the messageHash was signed 11 | * @param {string} sigString 12 | * @param {string} hash 13 | * @return {string} address 14 | */ 15 | function recover(sigString, hash) { 16 | var pubkey = (0, _recoverPublicKey.recoverPublicKey)(sigString, hash); 17 | var address = (0, _publicKey.toAddress)(pubkey); 18 | return address; 19 | } -------------------------------------------------------------------------------- /dist/lib/calculate-contract-address.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.calculateContractAddress = calculateContractAddress; 7 | var _util = require("@ethereumjs/util"); 8 | var _util2 = require("./util"); 9 | function calculateContractAddress(creatorAddress, nonce) { 10 | var addressBuffer = (0, _util.generateAddress)((0, _util.hexToBytes)((0, _util2.addLeading0x)(creatorAddress)), (0, _util.hexToBytes)((0, _util.intToHex)(nonce))); 11 | var address = (0, _util.bytesToHex)(addressBuffer); 12 | return (0, _util.toChecksumAddress)((0, _util2.addLeading0x)(address)); 13 | } -------------------------------------------------------------------------------- /src/public-key-by-private-key.js: -------------------------------------------------------------------------------- 1 | import { 2 | privateToPublic, 3 | hexToBytes, 4 | bytesToHex 5 | } from '@ethereumjs/util'; 6 | import { 7 | addLeading0x, 8 | removeLeading0x 9 | } from './util'; 10 | 11 | /** 12 | * Generate publicKey from the privateKey. 13 | * This creates the uncompressed publicKey, 14 | * where 04 has stripped from left 15 | * @returns {string} 16 | */ 17 | export function publicKeyByPrivateKey(privateKey) { 18 | privateKey = addLeading0x(privateKey); 19 | const publicKeyBuffer = privateToPublic(hexToBytes(privateKey)); 20 | const ret = removeLeading0x(bytesToHex(publicKeyBuffer)); 21 | return ret; 22 | } 23 | -------------------------------------------------------------------------------- /dist/lib/hash.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.SIGN_PREFIX = void 0; 7 | exports.keccak256 = keccak256; 8 | var _ethers = require("ethers"); 9 | function keccak256(params) { 10 | var types = []; 11 | var values = []; 12 | if (!Array.isArray(params)) { 13 | types.push('string'); 14 | values.push(params); 15 | } else { 16 | params.forEach(function (p) { 17 | types.push(p.type); 18 | values.push(p.value); 19 | }); 20 | } 21 | return _ethers.utils.solidityKeccak256(types, values); 22 | } 23 | var SIGN_PREFIX = exports.SIGN_PREFIX = '\x19Ethereum Signed Message:\n32'; -------------------------------------------------------------------------------- /dist/es/vrs.js: -------------------------------------------------------------------------------- 1 | import { utils as ethersUtils } from 'ethers'; 2 | /** 3 | * split signature-hex into parts 4 | * @param {string} hexString 5 | * @return {{v: string, r: string, s: string}} 6 | */ 7 | export function fromString(hexString) { 8 | var arr = ethersUtils.splitSignature(hexString); 9 | return { 10 | // convert "v" to hex 11 | v: "0x".concat(arr.v.toString(16)), 12 | r: arr.r, 13 | s: arr.s 14 | }; 15 | } 16 | 17 | /** 18 | * merge signature-parts to one string 19 | * @param {{v: string, r: string, s: string}} sig 20 | * @return {string} hexString 21 | */ 22 | export function toString(sig) { 23 | return ethersUtils.joinSignature(sig); 24 | } -------------------------------------------------------------------------------- /dist/lib/util.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.addLeading0x = addLeading0x; 7 | exports.hexToUnit8Array = hexToUnit8Array; 8 | exports.removeLeading0x = removeLeading0x; 9 | exports.uint8ArrayToHex = uint8ArrayToHex; 10 | function removeLeading0x(str) { 11 | if (str.startsWith('0x')) return str.substring(2);else return str; 12 | } 13 | function addLeading0x(str) { 14 | if (!str.startsWith('0x')) return '0x' + str;else return str; 15 | } 16 | function uint8ArrayToHex(arr) { 17 | return Buffer.from(arr).toString('hex'); 18 | } 19 | function hexToUnit8Array(str) { 20 | return new Uint8Array(Buffer.from(str, 'hex')); 21 | } -------------------------------------------------------------------------------- /src/vrs.js: -------------------------------------------------------------------------------- 1 | import { 2 | utils as ethersUtils 3 | } from 'ethers'; 4 | /** 5 | * split signature-hex into parts 6 | * @param {string} hexString 7 | * @return {{v: string, r: string, s: string}} 8 | */ 9 | export function fromString(hexString) { 10 | const arr = ethersUtils.splitSignature(hexString); 11 | return { 12 | // convert "v" to hex 13 | v: `0x${arr.v.toString(16)}`, 14 | r: arr.r, 15 | s: arr.s, 16 | }; 17 | } 18 | 19 | /** 20 | * merge signature-parts to one string 21 | * @param {{v: string, r: string, s: string}} sig 22 | * @return {string} hexString 23 | */ 24 | export function toString(sig) { 25 | return ethersUtils.joinSignature(sig); 26 | } 27 | -------------------------------------------------------------------------------- /dist/lib/public-key-by-private-key.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.publicKeyByPrivateKey = publicKeyByPrivateKey; 7 | var _util = require("@ethereumjs/util"); 8 | var _util2 = require("./util"); 9 | /** 10 | * Generate publicKey from the privateKey. 11 | * This creates the uncompressed publicKey, 12 | * where 04 has stripped from left 13 | * @returns {string} 14 | */ 15 | function publicKeyByPrivateKey(privateKey) { 16 | privateKey = (0, _util2.addLeading0x)(privateKey); 17 | var publicKeyBuffer = (0, _util.privateToPublic)((0, _util.hexToBytes)(privateKey)); 18 | var ret = (0, _util2.removeLeading0x)((0, _util.bytesToHex)(publicKeyBuffer)); 19 | return ret; 20 | } -------------------------------------------------------------------------------- /dist/es/decrypt-with-private-key.js: -------------------------------------------------------------------------------- 1 | import { decrypt } from 'eccrypto'; 2 | import { parse } from './cipher'; 3 | import { removeLeading0x } from './util'; 4 | export function decryptWithPrivateKey(privateKey, encrypted) { 5 | encrypted = parse(encrypted); 6 | 7 | // remove trailing '0x' from privateKey 8 | var twoStripped = removeLeading0x(privateKey); 9 | var encryptedBuffer = { 10 | iv: Buffer.from(encrypted.iv, 'hex'), 11 | ephemPublicKey: Buffer.from(encrypted.ephemPublicKey, 'hex'), 12 | ciphertext: Buffer.from(encrypted.ciphertext, 'hex'), 13 | mac: Buffer.from(encrypted.mac, 'hex') 14 | }; 15 | return decrypt(Buffer.from(twoStripped, 'hex'), encryptedBuffer).then(function (decryptedBuffer) { 16 | return decryptedBuffer.toString(); 17 | }); 18 | } -------------------------------------------------------------------------------- /dist/es/encrypt-with-public-key.js: -------------------------------------------------------------------------------- 1 | import { encrypt } from 'eccrypto'; 2 | import { decompress } from './public-key'; 3 | export function encryptWithPublicKey(publicKey, message, opts) { 4 | // ensure its an uncompressed publicKey 5 | publicKey = decompress(publicKey); 6 | 7 | // re-add the compression-flag 8 | var pubString = '04' + publicKey; 9 | return encrypt(Buffer.from(pubString, 'hex'), Buffer.from(message), opts ? opts : {}).then(function (encryptedBuffers) { 10 | var encrypted = { 11 | iv: encryptedBuffers.iv.toString('hex'), 12 | ephemPublicKey: encryptedBuffers.ephemPublicKey.toString('hex'), 13 | ciphertext: encryptedBuffers.ciphertext.toString('hex'), 14 | mac: encryptedBuffers.mac.toString('hex') 15 | }; 16 | return encrypted; 17 | }); 18 | } -------------------------------------------------------------------------------- /dist/lib/vrs.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.fromString = fromString; 7 | exports.toString = toString; 8 | var _ethers = require("ethers"); 9 | /** 10 | * split signature-hex into parts 11 | * @param {string} hexString 12 | * @return {{v: string, r: string, s: string}} 13 | */ 14 | function fromString(hexString) { 15 | var arr = _ethers.utils.splitSignature(hexString); 16 | return { 17 | // convert "v" to hex 18 | v: "0x".concat(arr.v.toString(16)), 19 | r: arr.r, 20 | s: arr.s 21 | }; 22 | } 23 | 24 | /** 25 | * merge signature-parts to one string 26 | * @param {{v: string, r: string, s: string}} sig 27 | * @return {string} hexString 28 | */ 29 | function toString(sig) { 30 | return _ethers.utils.joinSignature(sig); 31 | } -------------------------------------------------------------------------------- /dist/lib/tx-data-by-compiled.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | exports.txDataByCompiled = txDataByCompiled; 8 | var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray")); 9 | var _ethers = require("ethers"); 10 | function txDataByCompiled(abi, bytecode, args) { 11 | // solc returns a string which is often passed instead of the json 12 | if (typeof abi === 'string') abi = JSON.parse(abi); 13 | 14 | // Construct a Contract Factory 15 | var factory = new _ethers.ContractFactory(abi, '0x' + bytecode); 16 | var deployTransaction = factory.getDeployTransaction.apply(factory, (0, _toConsumableArray2["default"])(args)); 17 | return deployTransaction.data; 18 | } -------------------------------------------------------------------------------- /dist/es/sign.js: -------------------------------------------------------------------------------- 1 | import { ecdsaSign as secp256k1_sign } from 'secp256k1'; 2 | import { addLeading0x, removeLeading0x } from './util'; 3 | 4 | /** 5 | * signs the given message 6 | * we do not use sign from eth-lib because the pure secp256k1-version is 90% faster 7 | * @param {string} privateKey 8 | * @param {string} hash 9 | * @return {string} hexString 10 | */ 11 | export function sign(privateKey, hash) { 12 | hash = addLeading0x(hash); 13 | if (hash.length !== 66) throw new Error('EthCrypto.sign(): Can only sign hashes, given: ' + hash); 14 | var sigObj = secp256k1_sign(new Uint8Array(Buffer.from(removeLeading0x(hash), 'hex')), new Uint8Array(Buffer.from(removeLeading0x(privateKey), 'hex'))); 15 | var recoveryId = sigObj.recid === 1 ? '1c' : '1b'; 16 | var newSignature = '0x' + Buffer.from(sigObj.signature).toString('hex') + recoveryId; 17 | return newSignature; 18 | } -------------------------------------------------------------------------------- /src/decrypt-with-private-key.js: -------------------------------------------------------------------------------- 1 | import { 2 | decrypt 3 | } from 'eccrypto'; 4 | import { 5 | parse 6 | } from './cipher'; 7 | import { 8 | removeLeading0x 9 | } from './util'; 10 | 11 | export function decryptWithPrivateKey(privateKey, encrypted) { 12 | 13 | encrypted = parse(encrypted); 14 | 15 | // remove trailing '0x' from privateKey 16 | const twoStripped = removeLeading0x(privateKey); 17 | 18 | const encryptedBuffer = { 19 | iv: Buffer.from(encrypted.iv, 'hex'), 20 | ephemPublicKey: Buffer.from(encrypted.ephemPublicKey, 'hex'), 21 | ciphertext: Buffer.from(encrypted.ciphertext, 'hex'), 22 | mac: Buffer.from(encrypted.mac, 'hex') 23 | }; 24 | 25 | 26 | return decrypt( 27 | Buffer.from(twoStripped, 'hex'), 28 | encryptedBuffer 29 | ).then(decryptedBuffer => decryptedBuffer.toString()); 30 | } 31 | -------------------------------------------------------------------------------- /dist/es/recover-public-key.js: -------------------------------------------------------------------------------- 1 | import { ecdsaRecover } from 'secp256k1'; 2 | import { removeLeading0x, hexToUnit8Array, uint8ArrayToHex } from './util'; 3 | 4 | /** 5 | * returns the publicKey for the privateKey with which the messageHash was signed 6 | * @param {string} signature 7 | * @param {string} hash 8 | * @return {string} publicKey 9 | */ 10 | export function recoverPublicKey(signature, hash) { 11 | signature = removeLeading0x(signature); 12 | 13 | // split into v-value and sig 14 | var sigOnly = signature.substring(0, signature.length - 2); // all but last 2 chars 15 | var vValue = signature.slice(-2); // last 2 chars 16 | 17 | var recoveryNumber = vValue === '1c' ? 1 : 0; 18 | var pubKey = uint8ArrayToHex(ecdsaRecover(hexToUnit8Array(sigOnly), recoveryNumber, hexToUnit8Array(removeLeading0x(hash)), false)); 19 | 20 | // remove trailing '04' 21 | pubKey = pubKey.slice(2); 22 | return pubKey; 23 | } -------------------------------------------------------------------------------- /src/encrypt-with-public-key.js: -------------------------------------------------------------------------------- 1 | import { 2 | encrypt 3 | } from 'eccrypto'; 4 | import { 5 | decompress 6 | } from './public-key'; 7 | 8 | export function encryptWithPublicKey(publicKey, message, opts) { 9 | 10 | // ensure its an uncompressed publicKey 11 | publicKey = decompress(publicKey); 12 | 13 | // re-add the compression-flag 14 | const pubString = '04' + publicKey; 15 | 16 | 17 | return encrypt( 18 | Buffer.from(pubString, 'hex'), 19 | Buffer.from(message), 20 | opts ? opts : {} 21 | ).then(encryptedBuffers => { 22 | const encrypted = { 23 | iv: encryptedBuffers.iv.toString('hex'), 24 | ephemPublicKey: encryptedBuffers.ephemPublicKey.toString('hex'), 25 | ciphertext: encryptedBuffers.ciphertext.toString('hex'), 26 | mac: encryptedBuffers.mac.toString('hex') 27 | }; 28 | return encrypted; 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /dist/lib/decrypt-with-private-key.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.decryptWithPrivateKey = decryptWithPrivateKey; 7 | var _eccrypto = require("eccrypto"); 8 | var _cipher = require("./cipher"); 9 | var _util = require("./util"); 10 | function decryptWithPrivateKey(privateKey, encrypted) { 11 | encrypted = (0, _cipher.parse)(encrypted); 12 | 13 | // remove trailing '0x' from privateKey 14 | var twoStripped = (0, _util.removeLeading0x)(privateKey); 15 | var encryptedBuffer = { 16 | iv: Buffer.from(encrypted.iv, 'hex'), 17 | ephemPublicKey: Buffer.from(encrypted.ephemPublicKey, 'hex'), 18 | ciphertext: Buffer.from(encrypted.ciphertext, 'hex'), 19 | mac: Buffer.from(encrypted.mac, 'hex') 20 | }; 21 | return (0, _eccrypto.decrypt)(Buffer.from(twoStripped, 'hex'), encryptedBuffer).then(function (decryptedBuffer) { 22 | return decryptedBuffer.toString(); 23 | }); 24 | } -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /dist/lib/encrypt-with-public-key.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.encryptWithPublicKey = encryptWithPublicKey; 7 | var _eccrypto = require("eccrypto"); 8 | var _publicKey = require("./public-key"); 9 | function encryptWithPublicKey(publicKey, message, opts) { 10 | // ensure its an uncompressed publicKey 11 | publicKey = (0, _publicKey.decompress)(publicKey); 12 | 13 | // re-add the compression-flag 14 | var pubString = '04' + publicKey; 15 | return (0, _eccrypto.encrypt)(Buffer.from(pubString, 'hex'), Buffer.from(message), opts ? opts : {}).then(function (encryptedBuffers) { 16 | var encrypted = { 17 | iv: encryptedBuffers.iv.toString('hex'), 18 | ephemPublicKey: encryptedBuffers.ephemPublicKey.toString('hex'), 19 | ciphertext: encryptedBuffers.ciphertext.toString('hex'), 20 | mac: encryptedBuffers.mac.toString('hex') 21 | }; 22 | return encrypted; 23 | }); 24 | } -------------------------------------------------------------------------------- /dist/lib/sign.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.sign = sign; 7 | var _secp256k = require("secp256k1"); 8 | var _util = require("./util"); 9 | /** 10 | * signs the given message 11 | * we do not use sign from eth-lib because the pure secp256k1-version is 90% faster 12 | * @param {string} privateKey 13 | * @param {string} hash 14 | * @return {string} hexString 15 | */ 16 | function sign(privateKey, hash) { 17 | hash = (0, _util.addLeading0x)(hash); 18 | if (hash.length !== 66) throw new Error('EthCrypto.sign(): Can only sign hashes, given: ' + hash); 19 | var sigObj = (0, _secp256k.ecdsaSign)(new Uint8Array(Buffer.from((0, _util.removeLeading0x)(hash), 'hex')), new Uint8Array(Buffer.from((0, _util.removeLeading0x)(privateKey), 'hex'))); 20 | var recoveryId = sigObj.recid === 1 ? '1c' : '1b'; 21 | var newSignature = '0x' + Buffer.from(sigObj.signature).toString('hex') + recoveryId; 22 | return newSignature; 23 | } -------------------------------------------------------------------------------- /src/sign.js: -------------------------------------------------------------------------------- 1 | import { 2 | ecdsaSign as secp256k1_sign 3 | } from 'secp256k1'; 4 | import { 5 | addLeading0x, 6 | removeLeading0x 7 | } from './util'; 8 | 9 | /** 10 | * signs the given message 11 | * we do not use sign from eth-lib because the pure secp256k1-version is 90% faster 12 | * @param {string} privateKey 13 | * @param {string} hash 14 | * @return {string} hexString 15 | */ 16 | export function sign(privateKey, hash) { 17 | hash = addLeading0x(hash); 18 | if (hash.length !== 66) 19 | throw new Error('EthCrypto.sign(): Can only sign hashes, given: ' + hash); 20 | 21 | const sigObj = secp256k1_sign( 22 | new Uint8Array(Buffer.from(removeLeading0x(hash), 'hex')), 23 | new Uint8Array(Buffer.from(removeLeading0x(privateKey), 'hex')) 24 | ); 25 | 26 | const recoveryId = sigObj.recid === 1 ? '1c' : '1b'; 27 | 28 | const newSignature = '0x' + Buffer.from(sigObj.signature).toString('hex') + recoveryId; 29 | return newSignature; 30 | } 31 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | # Controls when the action will run. 3 | on: 4 | # Triggers the workflow on push or pull request events but only for the master branch 5 | push: 6 | branches: [ master ] 7 | pull_request: 8 | branches: [ master ] 9 | 10 | # Allows you to run this workflow manually from the Actions tab 11 | workflow_dispatch: 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v6 18 | - name: Use Node.js 12.11.0 19 | uses: actions/setup-node@v6 20 | with: 21 | node-version-file: '.nvmrc' 22 | - run: npm install 23 | - run: npm run lint 24 | - run: npm run build 25 | - run: npm run test:node 26 | - name: Run headless test 27 | uses: GabrielBB/xvfb-action@v1 28 | with: 29 | run: npm run test:browser 30 | - run: npm run test:typings 31 | - run: npm run test:deps 32 | - run: npm run test:size 33 | - run: npm run build:size 34 | -------------------------------------------------------------------------------- /config/webpack.config.js: -------------------------------------------------------------------------------- 1 | 2 | const path = require('path'); 3 | const TerserPlugin = require('terser-webpack-plugin'); 4 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin; 5 | 6 | 7 | //console.log(process.env.NODE_ENV); 8 | //process.exit(); 9 | 10 | 11 | const plugins = []; 12 | if (process.env.NODE_ENV === 'disc') 13 | plugins.push(new BundleAnalyzerPlugin()); 14 | 15 | module.exports = { 16 | mode: 'production', 17 | entry: './dist/es/browserify.index.js', 18 | optimization: { 19 | minimizer: [ 20 | new TerserPlugin({ 21 | parallel: true, 22 | }) 23 | ] 24 | }, 25 | plugins, 26 | output: { 27 | path: path.resolve(__dirname, '../test_tmp'), 28 | filename: 'webpack.bundle.js' 29 | }, 30 | resolve: { 31 | fallback: { 32 | 'crypto': require.resolve('crypto-browserify'), 33 | 'stream': require.resolve('stream-browserify') 34 | } 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /dist/es/cipher.js: -------------------------------------------------------------------------------- 1 | import { compress, decompress } from './public-key'; 2 | export function stringify(cipher) { 3 | if (typeof cipher === 'string') return cipher; 4 | 5 | // use compressed key because it's smaller 6 | var compressedKey = compress(cipher.ephemPublicKey); 7 | var ret = Buffer.concat([Buffer.from(cipher.iv, 'hex'), 8 | // 16bit 9 | Buffer.from(compressedKey, 'hex'), 10 | // 33bit 11 | Buffer.from(cipher.mac, 'hex'), 12 | // 32bit 13 | Buffer.from(cipher.ciphertext, 'hex') // var bit 14 | ]); 15 | return ret.toString('hex'); 16 | } 17 | export function parse(str) { 18 | if (typeof str !== 'string') return str; 19 | var buf = Buffer.from(str, 'hex'); 20 | var ret = { 21 | iv: buf.toString('hex', 0, 16), 22 | ephemPublicKey: buf.toString('hex', 16, 49), 23 | mac: buf.toString('hex', 49, 81), 24 | ciphertext: buf.toString('hex', 81, buf.length) 25 | }; 26 | 27 | // decompress publicKey 28 | ret.ephemPublicKey = '04' + decompress(ret.ephemPublicKey); 29 | return ret; 30 | } -------------------------------------------------------------------------------- /src/sign-transaction.js: -------------------------------------------------------------------------------- 1 | 2 | import { createTxFromRPC } from '@ethereumjs/tx'; 3 | import { 4 | bytesToHex 5 | } from '@ethereumjs/util'; 6 | import { publicKeyByPrivateKey } from './public-key-by-private-key'; 7 | import { 8 | toAddress as addressByPublicKey 9 | } from './public-key'; 10 | 11 | export async function signTransaction( 12 | rawTx, 13 | privateKey, 14 | txOptions = {} 15 | ) { 16 | // check if privateKey->address matches rawTx.from 17 | const publicKey = publicKeyByPrivateKey(privateKey); 18 | const address = addressByPublicKey(publicKey); 19 | if (address != rawTx.from) 20 | throw new Error('EthCrypto.signTransaction(): rawTx.from does not match the address of the privateKey'); 21 | 22 | const privateKeyBuffer = Buffer.from(privateKey.replace(/^.{2}/g, ''), 'hex'); 23 | 24 | const tx = await createTxFromRPC(rawTx, txOptions); 25 | const signedTx = tx.sign(privateKeyBuffer); 26 | const serializedTx = bytesToHex(signedTx.serialize()); 27 | 28 | return serializedTx; 29 | } 30 | -------------------------------------------------------------------------------- /src/recover-public-key.js: -------------------------------------------------------------------------------- 1 | import { 2 | ecdsaRecover 3 | } from 'secp256k1'; 4 | import { 5 | removeLeading0x, 6 | hexToUnit8Array, 7 | uint8ArrayToHex 8 | } from './util'; 9 | 10 | 11 | /** 12 | * returns the publicKey for the privateKey with which the messageHash was signed 13 | * @param {string} signature 14 | * @param {string} hash 15 | * @return {string} publicKey 16 | */ 17 | export function recoverPublicKey(signature, hash) { 18 | signature = removeLeading0x(signature); 19 | 20 | // split into v-value and sig 21 | const sigOnly = signature.substring(0, signature.length - 2); // all but last 2 chars 22 | const vValue = signature.slice(-2); // last 2 chars 23 | 24 | const recoveryNumber = vValue === '1c' ? 1 : 0; 25 | 26 | let pubKey = uint8ArrayToHex(ecdsaRecover( 27 | hexToUnit8Array(sigOnly), 28 | recoveryNumber, 29 | hexToUnit8Array(removeLeading0x(hash)), 30 | false 31 | )); 32 | 33 | // remove trailing '04' 34 | pubKey = pubKey.slice(2); 35 | 36 | return pubKey; 37 | } 38 | -------------------------------------------------------------------------------- /dist/lib/recover-public-key.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.recoverPublicKey = recoverPublicKey; 7 | var _secp256k = require("secp256k1"); 8 | var _util = require("./util"); 9 | /** 10 | * returns the publicKey for the privateKey with which the messageHash was signed 11 | * @param {string} signature 12 | * @param {string} hash 13 | * @return {string} publicKey 14 | */ 15 | function recoverPublicKey(signature, hash) { 16 | signature = (0, _util.removeLeading0x)(signature); 17 | 18 | // split into v-value and sig 19 | var sigOnly = signature.substring(0, signature.length - 2); // all but last 2 chars 20 | var vValue = signature.slice(-2); // last 2 chars 21 | 22 | var recoveryNumber = vValue === '1c' ? 1 : 0; 23 | var pubKey = (0, _util.uint8ArrayToHex)((0, _secp256k.ecdsaRecover)((0, _util.hexToUnit8Array)(sigOnly), recoveryNumber, (0, _util.hexToUnit8Array)((0, _util.removeLeading0x)(hash)), false)); 24 | 25 | // remove trailing '04' 26 | pubKey = pubKey.slice(2); 27 | return pubKey; 28 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Daniel M 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 | -------------------------------------------------------------------------------- /src/cipher.js: -------------------------------------------------------------------------------- 1 | import { 2 | compress, 3 | decompress 4 | } from './public-key'; 5 | 6 | export function stringify(cipher) { 7 | if (typeof cipher === 'string') return cipher; 8 | 9 | // use compressed key because it's smaller 10 | const compressedKey = compress(cipher.ephemPublicKey); 11 | 12 | const ret = Buffer.concat([ 13 | Buffer.from(cipher.iv, 'hex'), // 16bit 14 | Buffer.from(compressedKey, 'hex'), // 33bit 15 | Buffer.from(cipher.mac, 'hex'), // 32bit 16 | Buffer.from(cipher.ciphertext, 'hex') // var bit 17 | ]); 18 | 19 | return ret.toString('hex'); 20 | 21 | 22 | } 23 | 24 | export function parse(str) { 25 | if (typeof str !== 'string') 26 | return str; 27 | 28 | const buf = Buffer.from(str, 'hex'); 29 | 30 | const ret = { 31 | iv: buf.toString('hex', 0, 16), 32 | ephemPublicKey: buf.toString('hex', 16, 49), 33 | mac: buf.toString('hex', 49, 81), 34 | ciphertext: buf.toString('hex', 81, buf.length) 35 | }; 36 | 37 | // decompress publicKey 38 | ret.ephemPublicKey = '04' + decompress(ret.ephemPublicKey); 39 | 40 | return ret; 41 | } 42 | -------------------------------------------------------------------------------- /dist/lib/cipher.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.parse = parse; 7 | exports.stringify = stringify; 8 | var _publicKey = require("./public-key"); 9 | function stringify(cipher) { 10 | if (typeof cipher === 'string') return cipher; 11 | 12 | // use compressed key because it's smaller 13 | var compressedKey = (0, _publicKey.compress)(cipher.ephemPublicKey); 14 | var ret = Buffer.concat([Buffer.from(cipher.iv, 'hex'), 15 | // 16bit 16 | Buffer.from(compressedKey, 'hex'), 17 | // 33bit 18 | Buffer.from(cipher.mac, 'hex'), 19 | // 32bit 20 | Buffer.from(cipher.ciphertext, 'hex') // var bit 21 | ]); 22 | return ret.toString('hex'); 23 | } 24 | function parse(str) { 25 | if (typeof str !== 'string') return str; 26 | var buf = Buffer.from(str, 'hex'); 27 | var ret = { 28 | iv: buf.toString('hex', 0, 16), 29 | ephemPublicKey: buf.toString('hex', 16, 49), 30 | mac: buf.toString('hex', 49, 81), 31 | ciphertext: buf.toString('hex', 81, buf.length) 32 | }; 33 | 34 | // decompress publicKey 35 | ret.ephemPublicKey = '04' + (0, _publicKey.decompress)(ret.ephemPublicKey); 36 | return ret; 37 | } -------------------------------------------------------------------------------- /test/bug-template.test.js: -------------------------------------------------------------------------------- 1 | const AsyncTestUtil = require('async-test-util'); 2 | const assert = require('assert'); 3 | const EthCrypto = require('../dist/lib/index'); 4 | 5 | /** 6 | * If you have found a bug, edit this test to reproduce it 7 | * You can run it with: 'npm run test:node' 8 | * If you have successfully reproduced it, make a pull request with this file 9 | */ 10 | describe('bug-template.test.js', () => { 11 | it('should reproduce the bug', () => { 12 | const testData = { 13 | address: '0x3f243FdacE01Cfd9719f7359c94BA11361f32471', 14 | privateKey: '0x107be946709e41b7895eea9f2dacf998a0a9124acbb786f0fd1a826101581a07', 15 | publicKey: 'bf1cc3154424dc22191941d9f4f50b063a2b663a2337e5548abea633c1d06eceacf2b81dd326d278cd992d5e03b0df140f2df389ac9a1c2415a220a4a9e8c046' 16 | }; 17 | 18 | // do things with eth-crypto 19 | const message = AsyncTestUtil.randomString(12); 20 | const messageHash = EthCrypto.hash.keccak256(message); 21 | const signature = EthCrypto.sign(testData.privateKey, messageHash); 22 | 23 | // assert things that should be ok 24 | assert.equal(typeof signature, 'string'); 25 | assert.ok(signature.length > 10); 26 | }); 27 | }); -------------------------------------------------------------------------------- /dist/es/public-key.js: -------------------------------------------------------------------------------- 1 | import { publicKeyConvert } from 'secp256k1'; 2 | import { pubToAddress, toChecksumAddress, hexToBytes, bytesToHex } from '@ethereumjs/util'; 3 | import { hexToUnit8Array, uint8ArrayToHex, addLeading0x } from './util'; 4 | export function compress(startsWith04) { 5 | // add trailing 04 if not done before 6 | var testBuffer = Buffer.from(startsWith04, 'hex'); 7 | if (testBuffer.length === 64) startsWith04 = '04' + startsWith04; 8 | return uint8ArrayToHex(publicKeyConvert(hexToUnit8Array(startsWith04), true)); 9 | } 10 | export function decompress(startsWith02Or03) { 11 | // if already decompressed an not has trailing 04 12 | var testBuffer = Buffer.from(startsWith02Or03, 'hex'); 13 | if (testBuffer.length === 64) startsWith02Or03 = '04' + startsWith02Or03; 14 | var decompressed = uint8ArrayToHex(publicKeyConvert(hexToUnit8Array(startsWith02Or03), false)); 15 | 16 | // remove trailing 04 17 | decompressed = decompressed.substring(2); 18 | return decompressed; 19 | } 20 | 21 | /** 22 | * generates the ethereum-address of the publicKey 23 | * We create the checksum-address which is case-sensitive 24 | * @returns {string} address 25 | */ 26 | export function toAddress(publicKey) { 27 | // normalize key 28 | publicKey = decompress(publicKey); 29 | publicKey = addLeading0x(publicKey); 30 | var addressBuffer = pubToAddress(hexToBytes(publicKey)); 31 | var address = bytesToHex(addressBuffer); 32 | var checkSumAdress = toChecksumAddress(addLeading0x(address)); 33 | return checkSumAdress; 34 | } -------------------------------------------------------------------------------- /src/hex.js: -------------------------------------------------------------------------------- 1 | /** 2 | * compress/decompress hex-strings to utf16 or base64 3 | * thx @juvian 4 | * @link https://stackoverflow.com/a/40471908/3443137 5 | */ 6 | 7 | import { 8 | removeLeading0x, 9 | addLeading0x 10 | } from './util'; 11 | 12 | export function compress(hex, base64 = false) { 13 | hex = removeLeading0x(hex); 14 | 15 | // if base64:true, we use our own function because it results in a smaller output 16 | if (base64 === true) 17 | return Buffer.from(hex, 'hex').toString('base64'); 18 | 19 | let string = ''; 20 | while (hex.length % 4 != 0) { // we need it to be multiple of 4 21 | hex = '0' + hex; 22 | } 23 | for (let i = 0; i < hex.length; i += 4) { 24 | // get char from ascii code which goes from 0 to 65536 25 | string += String.fromCharCode(parseInt(hex.substring(i, i + 4), 16)); 26 | } 27 | return string; 28 | } 29 | 30 | export function decompress(compressedString, base64 = false) { 31 | 32 | // if base64:true, we use our own function because it results in a smaller output 33 | if (base64 === true) { 34 | const ret = Buffer.from(compressedString, 'base64').toString('hex'); 35 | return addLeading0x(ret); 36 | } 37 | 38 | let hex = ''; 39 | for (let i = 0; i < compressedString.length; i++) { 40 | // get character ascii code and convert to hexa string, adding necessary 0s 41 | hex += ((i == 0 ? '' : '000') + compressedString.charCodeAt(i).toString(16)).slice(-4); 42 | } 43 | hex = hex.toLowerCase(); 44 | return addLeading0x(hex); 45 | } 46 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { createIdentity } from './create-identity'; 3 | import * as publicKey from './public-key'; 4 | import { decryptWithPrivateKey } from './decrypt-with-private-key'; 5 | import { encryptWithPublicKey } from './encrypt-with-public-key'; 6 | import * as cipher from './cipher'; 7 | import { publicKeyByPrivateKey } from './public-key-by-private-key'; 8 | import { recover } from './recover'; 9 | import { recoverPublicKey } from './recover-public-key'; 10 | import { sign } from './sign'; 11 | import { signTransaction } from './sign-transaction'; 12 | import { txDataByCompiled } from './tx-data-by-compiled'; 13 | import { calculateContractAddress } from './calculate-contract-address'; 14 | import * as hash from './hash'; 15 | import * as hex from './hex'; 16 | import * as vrs from './vrs'; 17 | import * as util from './util'; 18 | 19 | export { 20 | createIdentity, 21 | publicKey, 22 | decryptWithPrivateKey, 23 | encryptWithPublicKey, 24 | cipher, 25 | publicKeyByPrivateKey, 26 | recover, 27 | recoverPublicKey, 28 | sign, 29 | signTransaction, 30 | txDataByCompiled, 31 | calculateContractAddress, 32 | hash, 33 | hex, 34 | vrs, 35 | util 36 | }; 37 | 38 | export default { 39 | createIdentity, 40 | publicKey, 41 | decryptWithPrivateKey, 42 | encryptWithPublicKey, 43 | cipher, 44 | publicKeyByPrivateKey, 45 | recover, 46 | recoverPublicKey, 47 | sign, 48 | signTransaction, 49 | txDataByCompiled, 50 | calculateContractAddress, 51 | hash, 52 | hex, 53 | vrs, 54 | util 55 | }; 56 | -------------------------------------------------------------------------------- /dist/es/hex.js: -------------------------------------------------------------------------------- 1 | /** 2 | * compress/decompress hex-strings to utf16 or base64 3 | * thx @juvian 4 | * @link https://stackoverflow.com/a/40471908/3443137 5 | */ 6 | 7 | import { removeLeading0x, addLeading0x } from './util'; 8 | export function compress(hex) { 9 | var base64 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; 10 | hex = removeLeading0x(hex); 11 | 12 | // if base64:true, we use our own function because it results in a smaller output 13 | if (base64 === true) return Buffer.from(hex, 'hex').toString('base64'); 14 | var string = ''; 15 | while (hex.length % 4 != 0) { 16 | // we need it to be multiple of 4 17 | hex = '0' + hex; 18 | } 19 | for (var i = 0; i < hex.length; i += 4) { 20 | // get char from ascii code which goes from 0 to 65536 21 | string += String.fromCharCode(parseInt(hex.substring(i, i + 4), 16)); 22 | } 23 | return string; 24 | } 25 | export function decompress(compressedString) { 26 | var base64 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; 27 | // if base64:true, we use our own function because it results in a smaller output 28 | if (base64 === true) { 29 | var ret = Buffer.from(compressedString, 'base64').toString('hex'); 30 | return addLeading0x(ret); 31 | } 32 | var hex = ''; 33 | for (var i = 0; i < compressedString.length; i++) { 34 | // get character ascii code and convert to hexa string, adding necessary 0s 35 | hex += ((i == 0 ? '' : '000') + compressedString.charCodeAt(i).toString(16)).slice(-4); 36 | } 37 | hex = hex.toLowerCase(); 38 | return addLeading0x(hex); 39 | } -------------------------------------------------------------------------------- /dist/es/index.js: -------------------------------------------------------------------------------- 1 | import { createIdentity } from './create-identity'; 2 | import * as publicKey from './public-key'; 3 | import { decryptWithPrivateKey } from './decrypt-with-private-key'; 4 | import { encryptWithPublicKey } from './encrypt-with-public-key'; 5 | import * as cipher from './cipher'; 6 | import { publicKeyByPrivateKey } from './public-key-by-private-key'; 7 | import { recover } from './recover'; 8 | import { recoverPublicKey } from './recover-public-key'; 9 | import { sign } from './sign'; 10 | import { signTransaction } from './sign-transaction'; 11 | import { txDataByCompiled } from './tx-data-by-compiled'; 12 | import { calculateContractAddress } from './calculate-contract-address'; 13 | import * as hash from './hash'; 14 | import * as hex from './hex'; 15 | import * as vrs from './vrs'; 16 | import * as util from './util'; 17 | export { createIdentity, publicKey, decryptWithPrivateKey, encryptWithPublicKey, cipher, publicKeyByPrivateKey, recover, recoverPublicKey, sign, signTransaction, txDataByCompiled, calculateContractAddress, hash, hex, vrs, util }; 18 | export default { 19 | createIdentity: createIdentity, 20 | publicKey: publicKey, 21 | decryptWithPrivateKey: decryptWithPrivateKey, 22 | encryptWithPublicKey: encryptWithPublicKey, 23 | cipher: cipher, 24 | publicKeyByPrivateKey: publicKeyByPrivateKey, 25 | recover: recover, 26 | recoverPublicKey: recoverPublicKey, 27 | sign: sign, 28 | signTransaction: signTransaction, 29 | txDataByCompiled: txDataByCompiled, 30 | calculateContractAddress: calculateContractAddress, 31 | hash: hash, 32 | hex: hex, 33 | vrs: vrs, 34 | util: util 35 | }; -------------------------------------------------------------------------------- /dist/es/create-identity.js: -------------------------------------------------------------------------------- 1 | import { utils as ethersUtils, Wallet } from 'ethers'; 2 | import { stripHexPrefix } from '@ethereumjs/util'; 3 | var MIN_ENTROPY_SIZE = 128; 4 | var keccak256 = ethersUtils.keccak256; 5 | 6 | /** 7 | * create a privateKey from the given entropy or a new one 8 | * @param {Buffer} entropy 9 | * @return {string} 10 | */ 11 | export function createPrivateKey(entropy) { 12 | if (entropy) { 13 | if (!Buffer.isBuffer(entropy)) throw new Error('EthCrypto.createPrivateKey(): given entropy is no Buffer'); 14 | if (Buffer.byteLength(entropy, 'utf8') < MIN_ENTROPY_SIZE) throw new Error('EthCrypto.createPrivateKey(): Entropy-size must be at least ' + MIN_ENTROPY_SIZE); 15 | var outerHex = keccak256(entropy); 16 | return outerHex; 17 | } else { 18 | var innerHex = keccak256(ethersUtils.concat([ethersUtils.randomBytes(32), ethersUtils.randomBytes(32)])); 19 | var middleHex = ethersUtils.concat([ethersUtils.concat([ethersUtils.randomBytes(32), innerHex]), ethersUtils.randomBytes(32)]); 20 | var _outerHex = keccak256(middleHex); 21 | return _outerHex; 22 | } 23 | } 24 | 25 | /** 26 | * creates a new object with 27 | * private-, public-Key and address 28 | * @param {Buffer?} entropy if provided, will use that as single random-source 29 | */ 30 | export function createIdentity(entropy) { 31 | var privateKey = createPrivateKey(entropy); 32 | var wallet = new Wallet(privateKey); 33 | var identity = { 34 | privateKey: privateKey, 35 | // remove trailing '0x04' 36 | publicKey: stripHexPrefix(wallet.publicKey).slice(2), 37 | address: wallet.address 38 | }; 39 | return identity; 40 | } -------------------------------------------------------------------------------- /dist/lib/public-key.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.compress = compress; 7 | exports.decompress = decompress; 8 | exports.toAddress = toAddress; 9 | var _secp256k = require("secp256k1"); 10 | var _util = require("@ethereumjs/util"); 11 | var _util2 = require("./util"); 12 | function compress(startsWith04) { 13 | // add trailing 04 if not done before 14 | var testBuffer = Buffer.from(startsWith04, 'hex'); 15 | if (testBuffer.length === 64) startsWith04 = '04' + startsWith04; 16 | return (0, _util2.uint8ArrayToHex)((0, _secp256k.publicKeyConvert)((0, _util2.hexToUnit8Array)(startsWith04), true)); 17 | } 18 | function decompress(startsWith02Or03) { 19 | // if already decompressed an not has trailing 04 20 | var testBuffer = Buffer.from(startsWith02Or03, 'hex'); 21 | if (testBuffer.length === 64) startsWith02Or03 = '04' + startsWith02Or03; 22 | var decompressed = (0, _util2.uint8ArrayToHex)((0, _secp256k.publicKeyConvert)((0, _util2.hexToUnit8Array)(startsWith02Or03), false)); 23 | 24 | // remove trailing 04 25 | decompressed = decompressed.substring(2); 26 | return decompressed; 27 | } 28 | 29 | /** 30 | * generates the ethereum-address of the publicKey 31 | * We create the checksum-address which is case-sensitive 32 | * @returns {string} address 33 | */ 34 | function toAddress(publicKey) { 35 | // normalize key 36 | publicKey = decompress(publicKey); 37 | publicKey = (0, _util2.addLeading0x)(publicKey); 38 | var addressBuffer = (0, _util.pubToAddress)((0, _util.hexToBytes)(publicKey)); 39 | var address = (0, _util.bytesToHex)(addressBuffer); 40 | var checkSumAdress = (0, _util.toChecksumAddress)((0, _util2.addLeading0x)(address)); 41 | return checkSumAdress; 42 | } -------------------------------------------------------------------------------- /src/create-identity.js: -------------------------------------------------------------------------------- 1 | import { utils as ethersUtils, Wallet } from 'ethers'; 2 | import { stripHexPrefix } from '@ethereumjs/util'; 3 | 4 | const MIN_ENTROPY_SIZE = 128; 5 | const { keccak256 } = ethersUtils; 6 | 7 | /** 8 | * create a privateKey from the given entropy or a new one 9 | * @param {Buffer} entropy 10 | * @return {string} 11 | */ 12 | export function createPrivateKey(entropy) { 13 | if (entropy) { 14 | if (!Buffer.isBuffer(entropy)) 15 | throw new Error('EthCrypto.createPrivateKey(): given entropy is no Buffer'); 16 | if (Buffer.byteLength(entropy, 'utf8') < MIN_ENTROPY_SIZE) 17 | throw new Error('EthCrypto.createPrivateKey(): Entropy-size must be at least ' + MIN_ENTROPY_SIZE); 18 | 19 | const outerHex = keccak256(entropy); 20 | return outerHex; 21 | } else { 22 | const innerHex = keccak256(ethersUtils.concat([ethersUtils.randomBytes(32), ethersUtils.randomBytes(32)])); 23 | const middleHex = ethersUtils.concat([ethersUtils.concat([ethersUtils.randomBytes(32), innerHex]), ethersUtils.randomBytes(32)]); 24 | const outerHex = keccak256(middleHex); 25 | return outerHex; 26 | } 27 | } 28 | 29 | /** 30 | * creates a new object with 31 | * private-, public-Key and address 32 | * @param {Buffer?} entropy if provided, will use that as single random-source 33 | */ 34 | export function createIdentity(entropy) { 35 | const privateKey = createPrivateKey(entropy); 36 | const wallet = new Wallet(privateKey); 37 | const identity = { 38 | privateKey: privateKey, 39 | // remove trailing '0x04' 40 | publicKey: stripHexPrefix(wallet.publicKey).slice(2), 41 | address: wallet.address, 42 | }; 43 | return identity; 44 | } 45 | -------------------------------------------------------------------------------- /dist/lib/hex.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.compress = compress; 7 | exports.decompress = decompress; 8 | var _util = require("./util"); 9 | /** 10 | * compress/decompress hex-strings to utf16 or base64 11 | * thx @juvian 12 | * @link https://stackoverflow.com/a/40471908/3443137 13 | */ 14 | 15 | function compress(hex) { 16 | var base64 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; 17 | hex = (0, _util.removeLeading0x)(hex); 18 | 19 | // if base64:true, we use our own function because it results in a smaller output 20 | if (base64 === true) return Buffer.from(hex, 'hex').toString('base64'); 21 | var string = ''; 22 | while (hex.length % 4 != 0) { 23 | // we need it to be multiple of 4 24 | hex = '0' + hex; 25 | } 26 | for (var i = 0; i < hex.length; i += 4) { 27 | // get char from ascii code which goes from 0 to 65536 28 | string += String.fromCharCode(parseInt(hex.substring(i, i + 4), 16)); 29 | } 30 | return string; 31 | } 32 | function decompress(compressedString) { 33 | var base64 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; 34 | // if base64:true, we use our own function because it results in a smaller output 35 | if (base64 === true) { 36 | var ret = Buffer.from(compressedString, 'base64').toString('hex'); 37 | return (0, _util.addLeading0x)(ret); 38 | } 39 | var hex = ''; 40 | for (var i = 0; i < compressedString.length; i++) { 41 | // get character ascii code and convert to hexa string, adding necessary 0s 42 | hex += ((i == 0 ? '' : '000') + compressedString.charCodeAt(i).toString(16)).slice(-4); 43 | } 44 | hex = hex.toLowerCase(); 45 | return (0, _util.addLeading0x)(hex); 46 | } -------------------------------------------------------------------------------- /src/public-key.js: -------------------------------------------------------------------------------- 1 | import { 2 | publicKeyConvert 3 | } from 'secp256k1'; 4 | import { 5 | pubToAddress, 6 | toChecksumAddress, 7 | hexToBytes, 8 | bytesToHex 9 | } from '@ethereumjs/util'; 10 | import { 11 | hexToUnit8Array, 12 | uint8ArrayToHex, 13 | addLeading0x 14 | } from './util'; 15 | 16 | export function compress(startsWith04) { 17 | 18 | // add trailing 04 if not done before 19 | const testBuffer = Buffer.from(startsWith04, 'hex'); 20 | if (testBuffer.length === 64) startsWith04 = '04' + startsWith04; 21 | 22 | 23 | return uint8ArrayToHex(publicKeyConvert( 24 | hexToUnit8Array(startsWith04), 25 | true 26 | )); 27 | } 28 | 29 | export function decompress(startsWith02Or03) { 30 | 31 | // if already decompressed an not has trailing 04 32 | const testBuffer = Buffer.from(startsWith02Or03, 'hex'); 33 | if (testBuffer.length === 64) startsWith02Or03 = '04' + startsWith02Or03; 34 | 35 | let decompressed = uint8ArrayToHex(publicKeyConvert( 36 | hexToUnit8Array(startsWith02Or03), 37 | false 38 | )); 39 | 40 | // remove trailing 04 41 | decompressed = decompressed.substring(2); 42 | return decompressed; 43 | } 44 | 45 | /** 46 | * generates the ethereum-address of the publicKey 47 | * We create the checksum-address which is case-sensitive 48 | * @returns {string} address 49 | */ 50 | export function toAddress(publicKey) { 51 | 52 | // normalize key 53 | publicKey = decompress(publicKey); 54 | publicKey = addLeading0x(publicKey); 55 | 56 | const addressBuffer = pubToAddress(hexToBytes(publicKey)); 57 | const address = bytesToHex(addressBuffer); 58 | 59 | const checkSumAdress = toChecksumAddress(addLeading0x(address)); 60 | return checkSumAdress; 61 | } 62 | -------------------------------------------------------------------------------- /dist/lib/create-identity.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.createIdentity = createIdentity; 7 | exports.createPrivateKey = createPrivateKey; 8 | var _ethers = require("ethers"); 9 | var _util = require("@ethereumjs/util"); 10 | var MIN_ENTROPY_SIZE = 128; 11 | var keccak256 = _ethers.utils.keccak256; 12 | 13 | /** 14 | * create a privateKey from the given entropy or a new one 15 | * @param {Buffer} entropy 16 | * @return {string} 17 | */ 18 | function createPrivateKey(entropy) { 19 | if (entropy) { 20 | if (!Buffer.isBuffer(entropy)) throw new Error('EthCrypto.createPrivateKey(): given entropy is no Buffer'); 21 | if (Buffer.byteLength(entropy, 'utf8') < MIN_ENTROPY_SIZE) throw new Error('EthCrypto.createPrivateKey(): Entropy-size must be at least ' + MIN_ENTROPY_SIZE); 22 | var outerHex = keccak256(entropy); 23 | return outerHex; 24 | } else { 25 | var innerHex = keccak256(_ethers.utils.concat([_ethers.utils.randomBytes(32), _ethers.utils.randomBytes(32)])); 26 | var middleHex = _ethers.utils.concat([_ethers.utils.concat([_ethers.utils.randomBytes(32), innerHex]), _ethers.utils.randomBytes(32)]); 27 | var _outerHex = keccak256(middleHex); 28 | return _outerHex; 29 | } 30 | } 31 | 32 | /** 33 | * creates a new object with 34 | * private-, public-Key and address 35 | * @param {Buffer?} entropy if provided, will use that as single random-source 36 | */ 37 | function createIdentity(entropy) { 38 | var privateKey = createPrivateKey(entropy); 39 | var wallet = new _ethers.Wallet(privateKey); 40 | var identity = { 41 | privateKey: privateKey, 42 | // remove trailing '0x04' 43 | publicKey: (0, _util.stripHexPrefix)(wallet.publicKey).slice(2), 44 | address: wallet.address 45 | }; 46 | return identity; 47 | } -------------------------------------------------------------------------------- /dist/es/sign-transaction.js: -------------------------------------------------------------------------------- 1 | import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator"; 2 | import _regeneratorRuntime from "@babel/runtime/regenerator"; 3 | import { createTxFromRPC } from '@ethereumjs/tx'; 4 | import { bytesToHex } from '@ethereumjs/util'; 5 | import { publicKeyByPrivateKey } from './public-key-by-private-key'; 6 | import { toAddress as addressByPublicKey } from './public-key'; 7 | export function signTransaction(_x, _x2) { 8 | return _signTransaction.apply(this, arguments); 9 | } 10 | function _signTransaction() { 11 | _signTransaction = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee(rawTx, privateKey) { 12 | var txOptions, 13 | publicKey, 14 | address, 15 | privateKeyBuffer, 16 | tx, 17 | signedTx, 18 | serializedTx, 19 | _args = arguments; 20 | return _regeneratorRuntime.wrap(function (_context) { 21 | while (1) switch (_context.prev = _context.next) { 22 | case 0: 23 | txOptions = _args.length > 2 && _args[2] !== undefined ? _args[2] : {}; 24 | // check if privateKey->address matches rawTx.from 25 | publicKey = publicKeyByPrivateKey(privateKey); 26 | address = addressByPublicKey(publicKey); 27 | if (!(address != rawTx.from)) { 28 | _context.next = 1; 29 | break; 30 | } 31 | throw new Error('EthCrypto.signTransaction(): rawTx.from does not match the address of the privateKey'); 32 | case 1: 33 | privateKeyBuffer = Buffer.from(privateKey.replace(/^.{2}/g, ''), 'hex'); 34 | _context.next = 2; 35 | return createTxFromRPC(rawTx, txOptions); 36 | case 2: 37 | tx = _context.sent; 38 | signedTx = tx.sign(privateKeyBuffer); 39 | serializedTx = bytesToHex(signedTx.serialize()); 40 | return _context.abrupt("return", serializedTx); 41 | case 3: 42 | case "end": 43 | return _context.stop(); 44 | } 45 | }, _callee); 46 | })); 47 | return _signTransaction.apply(this, arguments); 48 | } -------------------------------------------------------------------------------- /test/issues.test.js: -------------------------------------------------------------------------------- 1 | // const AsyncTestUtil = require('async-test-util'); 2 | const assert = require('assert'); 3 | const EthCrypto = require('../dist/lib/index'); 4 | const crypto = require('crypto'); 5 | 6 | describe('issues.test.js', () => { 7 | it('#3 Error in recover', async () => { 8 | const payload = { 9 | data: 'something', 10 | val: 5, 11 | other: 'something else' 12 | }; 13 | const msgHash = EthCrypto.hash.keccak256(JSON.stringify(payload)); 14 | const ident = EthCrypto.createIdentity(); 15 | 16 | const sig = EthCrypto.sign( 17 | ident.privateKey, // privateKey 18 | msgHash // hash of message 19 | ); 20 | assert.ok(sig); 21 | assert.ok(sig.startsWith('0x')); 22 | 23 | const recAddress = EthCrypto.recover( 24 | sig, 25 | EthCrypto.hash.keccak256(JSON.stringify(payload)) // signed message hash 26 | ); 27 | assert.equal(recAddress, ident.address); 28 | 29 | const recKey = EthCrypto.recoverPublicKey( 30 | sig, 31 | EthCrypto.hash.keccak256(JSON.stringify(payload)) // signed message hash 32 | ); 33 | assert.equal(recKey, ident.publicKey); 34 | }); 35 | it('#3 issuecommet-387616789', () => { 36 | const message = 'foobar'; 37 | const messageHash = EthCrypto.hash.keccak256(message); 38 | const signature = EthCrypto.sign( 39 | '0x107be946709e41b7895eea9f2dacf998a0a9124acbb786f0fd1a826101581a07', // privateKey 40 | messageHash // hash of message 41 | ); 42 | const signer = EthCrypto.recover( 43 | signature, 44 | messageHash 45 | ); 46 | assert.ok(signer); 47 | }); 48 | it('#47 cannot encrypt/decrypt with more then 16 chars message', async () => { 49 | const ident = EthCrypto.createIdentity(); 50 | 51 | const message = crypto.randomBytes(6).toString('hex'); 52 | const challenge = await EthCrypto.encryptWithPublicKey( 53 | ident.publicKey, 54 | Buffer.from(message) 55 | ); 56 | const answer = await EthCrypto.decryptWithPrivateKey( 57 | ident.privateKey, 58 | challenge 59 | ); 60 | assert.deepEqual(message, answer); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /config/karma.conf.js: -------------------------------------------------------------------------------- 1 | const configuration = { 2 | basePath: '', 3 | frameworks: [ 4 | 'mocha', 5 | 'browserify', 6 | 'detectBrowsers' 7 | ], 8 | files: [ 9 | '../test_tmp/karma.test.js' 10 | ], 11 | port: 9876, 12 | colors: true, 13 | autoWatch: false, 14 | 15 | /** 16 | * see 17 | * @link https://github.com/litixsoft/karma-detect-browsers 18 | */ 19 | detectBrowsers: { 20 | enabled: true, 21 | usePhantomJS: false, 22 | postDetection: function(availableBrowser) { 23 | // return ['Firefox']; // comment in to test specific browser 24 | const browsers = availableBrowser 25 | .filter(b => !['PhantomJS', 'FirefoxAurora', 'FirefoxNightly'].includes(b)) 26 | .map(b => { 27 | if (b === 'Chrome' || b === 'Chromium') return 'Chrome_travis_ci'; 28 | else return b; 29 | }); 30 | return browsers; 31 | } 32 | }, 33 | 34 | // Karma plugins loaded 35 | plugins: [ 36 | 'karma-mocha', 37 | 'karma-browserify', 38 | 'karma-chrome-launcher', 39 | 'karma-edge-launcher', 40 | 'karma-firefox-launcher', 41 | 'karma-ie-launcher', 42 | 'karma-opera-launcher', 43 | 'karma-detect-browsers' 44 | ], 45 | 46 | // Source files that you wanna generate coverage for. 47 | // Do not include tests or libraries (these files will be instrumented by Istanbul) 48 | preprocessors: { 49 | '../test_tmp/*.test.js': ['browserify'] 50 | }, 51 | 52 | client: { 53 | mocha: { 54 | bail: true, 55 | timeout: 12000 56 | } 57 | }, 58 | browsers: ['Chrome_travis_ci'], 59 | browserDisconnectTimeout: 12000, 60 | processKillTimeout: 12000, 61 | customLaunchers: { 62 | Chrome_travis_ci: { 63 | base: 'ChromeHeadless', 64 | flags: ['--no-sandbox'] 65 | } 66 | }, 67 | singleRun: true 68 | }; 69 | 70 | if (process.env.TRAVIS) { 71 | configuration.browsers = ['Chrome_travis_ci']; 72 | /** 73 | * overwrite reporters-default 74 | * So no big list will be shown at log 75 | */ 76 | configuration.reporters = []; 77 | } 78 | 79 | module.exports = function(config) { 80 | config.set(configuration); 81 | }; 82 | -------------------------------------------------------------------------------- /dist/lib/sign-transaction.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | exports.signTransaction = signTransaction; 8 | var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator")); 9 | var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator")); 10 | var _tx = require("@ethereumjs/tx"); 11 | var _util = require("@ethereumjs/util"); 12 | var _publicKeyByPrivateKey = require("./public-key-by-private-key"); 13 | var _publicKey = require("./public-key"); 14 | function signTransaction(_x, _x2) { 15 | return _signTransaction.apply(this, arguments); 16 | } 17 | function _signTransaction() { 18 | _signTransaction = (0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee(rawTx, privateKey) { 19 | var txOptions, 20 | publicKey, 21 | address, 22 | privateKeyBuffer, 23 | tx, 24 | signedTx, 25 | serializedTx, 26 | _args = arguments; 27 | return _regenerator["default"].wrap(function (_context) { 28 | while (1) switch (_context.prev = _context.next) { 29 | case 0: 30 | txOptions = _args.length > 2 && _args[2] !== undefined ? _args[2] : {}; 31 | // check if privateKey->address matches rawTx.from 32 | publicKey = (0, _publicKeyByPrivateKey.publicKeyByPrivateKey)(privateKey); 33 | address = (0, _publicKey.toAddress)(publicKey); 34 | if (!(address != rawTx.from)) { 35 | _context.next = 1; 36 | break; 37 | } 38 | throw new Error('EthCrypto.signTransaction(): rawTx.from does not match the address of the privateKey'); 39 | case 1: 40 | privateKeyBuffer = Buffer.from(privateKey.replace(/^.{2}/g, ''), 'hex'); 41 | _context.next = 2; 42 | return (0, _tx.createTxFromRPC)(rawTx, txOptions); 43 | case 2: 44 | tx = _context.sent; 45 | signedTx = tx.sign(privateKeyBuffer); 46 | serializedTx = (0, _util.bytesToHex)(signedTx.serialize()); 47 | return _context.abrupt("return", serializedTx); 48 | case 3: 49 | case "end": 50 | return _context.stop(); 51 | } 52 | }, _callee); 53 | })); 54 | return _signTransaction.apply(this, arguments); 55 | } -------------------------------------------------------------------------------- /contracts/TestContract.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.23; 2 | 3 | /** 4 | * this is a test-contract 5 | * to run tests against it 6 | * so we can be sure the code works together with ethereum tools 7 | */ 8 | 9 | contract TestContract { 10 | 11 | uint public onePublicValue = 1337; 12 | 13 | /** 14 | * hashes the given values 15 | * should be equal to own hash()-function in js 16 | */ 17 | function hashNumber( 18 | uint256 someNumber 19 | ) public constant returns(bytes32) { 20 | return keccak256( 21 | someNumber 22 | ); 23 | } 24 | 25 | function hashString( 26 | string someString 27 | ) public constant returns(bytes32) { 28 | return keccak256( 29 | someString 30 | ); 31 | } 32 | 33 | function hashMulti( 34 | string someString, 35 | uint256 someNumber, 36 | bool someBool 37 | ) public constant returns(bytes32) { 38 | return keccak256( 39 | someString, 40 | someNumber, 41 | someBool 42 | ); 43 | } 44 | 45 | /** 46 | * see https://ethereum.stackexchange.com/a/21037/1375 47 | */ 48 | function signHashLikeWeb3Sign( 49 | bytes32 _hash 50 | ) public constant returns (bytes32) { 51 | bytes memory prefix = "\x19Ethereum Signed Message:\n32"; 52 | bytes32 prefixedHash = keccak256(prefix, _hash); 53 | return prefixedHash; 54 | } 55 | 56 | /** 57 | * checks if the signature is valid 58 | * should be valid for signature created from the sign()-function in js 59 | */ 60 | function recoverSignature( 61 | bytes32 messageHash, 62 | uint8 v, 63 | bytes32 r, 64 | bytes32 s 65 | ) public constant returns (address) { 66 | address signer = ecrecover( 67 | messageHash, 68 | v, r, s 69 | ); 70 | return signer; 71 | } 72 | 73 | /** 74 | * recovers the signer from the message instead of the messageHash 75 | */ 76 | function recoverSignatureFromMessage( 77 | string _message, 78 | uint8 v, 79 | bytes32 r, 80 | bytes32 s 81 | ) public constant returns (address) { 82 | bytes32 hash = hashString(_message); 83 | address signer = ecrecover( 84 | hash, 85 | v, r, s 86 | ); 87 | return signer; 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /test/tutorials/encrypted-message.test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * at this tests, we run the code which is used in the tutorials 3 | * to ensure they work as expected 4 | */ 5 | const assert = require('assert'); 6 | const EthCrypto = require('../../dist/lib/index'); 7 | 8 | describe('encrypted-message.md', () => { 9 | it('run', async () => { 10 | const alice = EthCrypto.createIdentity(); 11 | const bob = EthCrypto.createIdentity(); 12 | 13 | const secretMessage = 'My name is Satoshi Buterin'; 14 | const signature = EthCrypto.sign( 15 | alice.privateKey, 16 | EthCrypto.hash.keccak256(secretMessage) 17 | ); 18 | 19 | const payload = { 20 | message: secretMessage, 21 | signature 22 | }; 23 | const encrypted = await EthCrypto.encryptWithPublicKey( 24 | bob.publicKey, 25 | JSON.stringify(payload) 26 | ); 27 | // console.log('encrypted:'); 28 | // console.dir(encrypted); 29 | 30 | const encryptedString = EthCrypto.cipher.stringify(encrypted); 31 | 32 | // decrypt 33 | const encryptedObject = EthCrypto.cipher.parse(encryptedString); 34 | const decrypted = await EthCrypto.decryptWithPrivateKey( 35 | bob.privateKey, 36 | encryptedObject 37 | ); 38 | const decryptedPayload = JSON.parse(decrypted); 39 | 40 | // check signature 41 | const senderAddress = EthCrypto.recover( 42 | decryptedPayload.signature, 43 | EthCrypto.hash.keccak256(decryptedPayload.message) 44 | ); 45 | 46 | assert.equal(senderAddress, alice.address); 47 | assert.equal(decryptedPayload.message, secretMessage); 48 | 49 | // answer 50 | const answerMessage = 'Roger dad'; 51 | const answerSignature = EthCrypto.sign( 52 | bob.privateKey, 53 | EthCrypto.hash.keccak256(answerMessage) 54 | ); 55 | const answerPayload = { 56 | message: answerMessage, 57 | signature: answerSignature 58 | }; 59 | 60 | const alicePublicKey = EthCrypto.recoverPublicKey( 61 | decryptedPayload.signature, 62 | EthCrypto.hash.keccak256(payload.message) 63 | ); 64 | 65 | assert.equal(alicePublicKey, alice.publicKey); 66 | 67 | const encryptedAnswer = await EthCrypto.encryptWithPublicKey( 68 | alicePublicKey, 69 | JSON.stringify(answerPayload) 70 | ); 71 | assert.ok(encryptedAnswer); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 6 | 7 |
14 | 15 | # eth-crypto 16 | 17 | Cryptographic javascript-functions for ethereum and tutorials on how to use them together with web3js and solidity. 18 | 19 | ## Tutorials 20 | 21 | - **[Creating Keys and use them for ethereum-transactions](./tutorials/creating-transactions.md)** 22 | 23 | In this tutorial we will create an ethereum-identity and use it so send transactions to the blockchain. 24 | 25 | - **[Sign and validate data with solidity](./tutorials/signed-data.md)** 26 | 27 | In this tutorial we will sign data in javascript and validate the signature inside of a smart-contract. 28 | 29 | - **[Sending encrypted and signed data to other identites](./tutorials/encrypted-message.md)** 30 | 31 | In this tutorial we will use the ethereum-identites and asymmetric cryptography to send an encrypted and signed message from Alice to Bob. 32 | 33 | ## Functions 34 | 35 | ### Install 36 | 37 | ```bash 38 | npm install eth-crypto --save 39 | ``` 40 | 41 | ```javascript 42 | // es6 43 | import EthCrypto from 'eth-crypto'; 44 | 45 | // node 46 | const EthCrypto = require('eth-crypto'); 47 | ``` 48 | 49 | ## API 50 | 51 | - [createIdentity()](https://github.com/pubkey/eth-crypto#createidentity) 52 | - [publicKeyByPrivateKey()](https://github.com/pubkey/eth-crypto#publickeybyprivatekey) 53 | - [publicKey.toAddress()](https://github.com/pubkey/eth-crypto#publickeytoaddress) 54 | - [publicKey.compress()](https://github.com/pubkey/eth-crypto#publickeycompress) 55 | - [publicKey.decompress()](https://github.com/pubkey/eth-crypto#publickeydecompress) 56 | - [sign()](https://github.com/pubkey/eth-crypto#sign) 57 | - [recover()](https://github.com/pubkey/eth-crypto#recover) 58 | - [recoverPublicKey()](https://github.com/pubkey/eth-crypto#recoverpublickey) 59 | - [encryptWithPublicKey()](https://github.com/pubkey/eth-crypto#encryptwithpublickey) 60 | - [decryptWithPrivateKey()](https://github.com/pubkey/eth-crypto#decryptwithprivatekey) 61 | - [cipher.stringify()](https://github.com/pubkey/eth-crypto#cipherstringify) 62 | - [cipher.parse()](https://github.com/pubkey/eth-crypto#cipherparse) 63 | - [signTransaction()](https://github.com/pubkey/eth-crypto#signtransaction) 64 | - [txDataByCompiled()](https://github.com/pubkey/eth-crypto#txdatabycompiled) 65 | - [calculateContractAddress()](https://github.com/pubkey/eth-crypto#calculatecontractaddress) 66 | - [hex.compress() hex.decompress()](https://github.com/pubkey/eth-crypto#hex-compressdecompress) 67 | 68 | 69 | # [READ THE FULL DOCUMENTATION ON GITHUB](https://github.com/pubkey/eth-crypto) 70 | -------------------------------------------------------------------------------- /contracts/DonationBag.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.23; 2 | 3 | 4 | contract DonationBag { 5 | 6 | // donation-signatures must be created by the owner 7 | address public owner; 8 | 9 | // each donation contains this amount of wei 10 | uint public amountPerDonation = 1000000000000000000; // one ether 11 | 12 | // one address can receive only one donation 13 | // the ones already received one, are stored here 14 | mapping (address => bool) public alreadyRecieved; 15 | 16 | // constructor 17 | function DonationBag(address _owner) public { 18 | owner = _owner; 19 | } 20 | 21 | /** 22 | * default function 23 | * Whenever ether is send to the contract without 24 | * transaction data, the default function is called. 25 | * If you do not have a default-function and send ether to this contract, 26 | * the transaction will be reverted with 'VM Exception while processing transaction: revert' 27 | */ 28 | function() public payable { 29 | // got money 30 | } 31 | 32 | /** 33 | * to ensure the signatures for this contract cannot be 34 | * replayed somewhere else, we add this prefix to the signed hash 35 | */ 36 | string public signPrefix = "Signed for DonationBag:"; 37 | 38 | /** 39 | * generates a prefixed hash of the address 40 | * We hash the following together: 41 | * - signPrefix 42 | * - address of this contract 43 | * - the recievers-address 44 | */ 45 | function prefixedHash( 46 | address receiver 47 | ) public constant returns(bytes32) { 48 | bytes32 hash = keccak256( 49 | signPrefix, 50 | address(this), 51 | receiver 52 | ); 53 | return hash; 54 | } 55 | 56 | /** 57 | * validates if the signature is valid 58 | * by checking if the correct message was signed 59 | */ 60 | function isSignatureValid( 61 | address receiver, 62 | uint8 v, 63 | bytes32 r, 64 | bytes32 s 65 | ) public constant returns (bool correct) { 66 | bytes32 mustBeSigned = prefixedHash(receiver); 67 | address signer = ecrecover( 68 | mustBeSigned, 69 | v, r, s 70 | ); 71 | 72 | return (signer == owner); 73 | } 74 | 75 | /** 76 | * checks if the signature and message is valid 77 | * if yes we send some wei to the submitter 78 | */ 79 | function recieveDonation( 80 | uint8 v, 81 | bytes32 r, 82 | bytes32 s 83 | ) public { 84 | 85 | // already received donation -> revert 86 | if (alreadyRecieved[msg.sender] == true) revert(); 87 | 88 | // signature not valid -> revert 89 | if (isSignatureValid( 90 | msg.sender, 91 | v, r, s 92 | ) == false) { 93 | revert(); 94 | } 95 | 96 | // all valid -> send wei 97 | alreadyRecieved[msg.sender] = true; 98 | msg.sender.transfer(amountPerDonation); 99 | } 100 | 101 | /** 102 | * returns the current contract-balance 103 | */ 104 | function getBalance() public constant returns (uint256 balance) { 105 | return this.balance; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /tutorials/encrypted-message.md: -------------------------------------------------------------------------------- 1 | # Tutorial: Encrypt and sign a message 2 | 3 | With ethereum-keys you cannot only interact with the blockchain, but also use them to send messages over mutual untrusted channels in a secure way. In this tutorial we will use ethereum-identities to send messages like you would do in a decentralized chat-app. 4 | 5 | ## Prerequisites 6 | 7 | First we create two identities, `Alice` and `Bob`. In our case `Alice` wants to send the message `My name is Satoshi Buterin` to `Bob`. 8 | 9 | ```javascript 10 | const EthCrypto = require('eth-crypto'); 11 | 12 | const alice = EthCrypto.createIdentity(); 13 | const bob = EthCrypto.createIdentity(); 14 | const secretMessage = 'My name is Satoshi Buterin'; 15 | ``` 16 | 17 | ## Encrypt and sign the message 18 | 19 | Before we send the message from `Alice` to `Bob`, we want to ensure that 20 | 21 | - Only `Bob` can read the message 22 | - `Bob` can be sure that the message really comes from `Alice` 23 | 24 | To do this, we first sign the message with alice's privateKey and then encrypt the message and the signature with bob's publicKey. 25 | 26 | ```javascript 27 | const signature = EthCrypto.sign( 28 | alice.privateKey, 29 | EthCrypto.hash.keccak256(secretMessage) 30 | ); 31 | const payload = { 32 | message: secretMessage, 33 | signature 34 | }; 35 | const encrypted = await EthCrypto.encryptWithPublicKey( 36 | bob.publicKey, // by encrypting with bobs publicKey, only bob can decrypt the payload with his privateKey 37 | JSON.stringify(payload) // we have to stringify the payload before we can encrypt it 38 | ); 39 | /* { iv: 'c66fbc24cc7ef520a7...', 40 | ephemPublicKey: '048e34ce5cca0b69d4e1f5...', 41 | ciphertext: '27b91fe986e3ab030...', 42 | mac: 'dd7b78c16e462c42876745c7...' 43 | } 44 | */ 45 | 46 | // we convert the object into a smaller string-representation 47 | const encryptedString = EthCrypto.cipher.stringify(encrypted); 48 | // > '812ee676cf06ba72316862fd3dabe7e403c7395bda62243b7b0eea5eb..' 49 | 50 | // now we send the encrypted string to bob over the internet.. *bieb, bieb, blob* 51 | ``` 52 | 53 | ## Decrypt and verify the payload 54 | 55 | When bob receives the message, he starts with decrypting it with his privateKey and then verifies the signature. 56 | 57 | ```javascript 58 | 59 | // we parse the string into the object again 60 | const encryptedObject = EthCrypto.cipher.parse(encryptedString); 61 | 62 | const decrypted = await EthCrypto.decryptWithPrivateKey( 63 | bob.privateKey, 64 | encryptedObject 65 | ); 66 | const decryptedPayload = JSON.parse(decrypted); 67 | 68 | // check signature 69 | const senderAddress = EthCrypto.recover( 70 | decryptedPayload.signature, 71 | EthCrypto.hash.keccak256(decryptedPayload.message) 72 | ); 73 | 74 | console.log( 75 | 'Got message from ' + 76 | senderAddress + 77 | ': ' + 78 | decryptedPayload.message 79 | ); 80 | // > 'Got message from 0x19C24B2d99FB91C5...: "My name is Satoshi Buterin" Buterin' 81 | ``` 82 | 83 | ## Creating an answer 84 | 85 | Now that `Bob` got the message, he can also answer back to alice. 86 | To do this he has to recover the publicKey of alice with `recoverPublicKey()`. 87 | 88 | ```javascript 89 | const answerMessage = 'And I am Bob Kelso'; 90 | const answerSignature = EthCrypto.sign( 91 | bob.privateKey, 92 | EthCrypto.hash.keccak256(answerMessage) 93 | ); 94 | const answerPayload = { 95 | message: answerMessage, 96 | signature: answerSignature 97 | }; 98 | 99 | const alicePublicKey = EthCrypto.recoverPublicKey( 100 | decryptedPayload.signature, 101 | EthCrypto.hash.keccak256(payload.message) 102 | ); 103 | 104 | const encryptedAnswer = await EthCrypto.encryptWithPublicKey( 105 | alicePublicKey, 106 | JSON.stringify(answerPayload) 107 | ); 108 | // now we send the encryptedAnswer to alice over the internet.. *bieb, bieb, blob* 109 | ``` 110 | -------------------------------------------------------------------------------- /typings/index.d.ts: -------------------------------------------------------------------------------- 1 | import BigNumber from 'bn.js'; 2 | import { TxOptions } from '@ethereumjs/tx'; 3 | 4 | type createIdentityType = (entropy?: Buffer) => { 5 | privateKey: string, 6 | publicKey: string, 7 | address: string 8 | } 9 | export const createIdentity: createIdentityType; 10 | 11 | type publicKeyType = { 12 | compress(publicKey: string): string; 13 | decompress(publicKey: string): string; 14 | toAddress(publicKey: string): string; 15 | } 16 | export const publicKey: publicKeyType; 17 | 18 | type publicKeyByPrivateKeyType = (privateKey: string) => string; 19 | export const publicKeyByPrivateKey: publicKeyByPrivateKeyType; 20 | 21 | export type Signature = { 22 | v: string, 23 | r: string, 24 | s: string 25 | }; 26 | 27 | export type Encrypted = { 28 | iv: string, 29 | ephemPublicKey: string, 30 | ciphertext: string, 31 | mac: string 32 | }; 33 | 34 | export type RawTx = { 35 | from: string; 36 | to: string; 37 | value: number | string | BigNumber; 38 | gasLimit: number; 39 | gasPrice: number; 40 | nonce: number; 41 | code?: string; 42 | }; 43 | 44 | type signType = (privateKey: string, message: string) => string; 45 | export const sign: signType; 46 | 47 | type recoverType = (sig: string, message: string) => string; 48 | export const recover: recoverType; 49 | 50 | type recoverPublicKeyType = (sig: string, message: string) => string; 51 | export const recoverPublicKey: recoverPublicKeyType; 52 | 53 | type vrsType = { 54 | fromString(hexString: string): Signature; 55 | toString(sig: Signature): string; 56 | }; 57 | export const vrs: vrsType; 58 | 59 | export type EncryptOptions = { 60 | iv?: Buffer, 61 | ephemPrivateKey?: Buffer 62 | }; 63 | type encryptWithPublicKeyType = (publicKey: string, message: string, options?: EncryptOptions) => Promise
2 |
3 |
5 |
6 |