ADD publicKey.compress

pull/15/head
pubkey 7 years ago
parent d97ed89a84
commit d4fc858aac
  1. 2
      .npmrc
  2. 28
      README.md
  3. 2
      package.json
  4. 15
      src/address-by-public-key.js
  5. 15
      src/create-identity.js
  6. 6
      src/encrypt-with-public-key.js
  7. 13
      src/hash.js
  8. 11
      src/hex.js
  9. 7
      src/index.js
  10. 48
      src/public-key.js
  11. 14
      src/recover-public-key.js
  12. 4
      src/recover.js
  13. 4
      src/sign-transaction.js
  14. 17
      src/sign.js
  15. 98
      test/unit.test.js
  16. 11
      typings/index.d.ts

@ -0,0 +1,2 @@
unsafe-perm = true
package-lock=false

@ -44,7 +44,7 @@ const EthCrypto = require('eth-crypto');
- [createIdentity()](#createidentity) - [createIdentity()](#createidentity)
- [publicKeyByPrivateKey()](#publickeybyprivatekey) - [publicKeyByPrivateKey()](#publickeybyprivatekey)
- [addressByPublicKey()](#addressbypublickey) - [publicKey.toAddress()](#addressbypublickey)
- [sign()](#sign) - [sign()](#sign)
- [recover()](#recover) - [recover()](#recover)
- [recoverPublicKey()](#recoverpublickey) - [recoverPublicKey()](#recoverpublickey)
@ -78,17 +78,39 @@ Derives the publicKey from a privateKey and returns it as hex-string.
// > 'bf1cc3154424dc22191941d9f4f50b063a2b663a2337e5548abea633c1d06ece...' // > 'bf1cc3154424dc22191941d9f4f50b063a2b663a2337e5548abea633c1d06ece...'
``` ```
### addressByPublicKey() ### publicKey.toAddress()
Derives the ethereum-address from the publicKey. Derives the ethereum-address from the publicKey.
```javascript ```javascript
const address = EthCrypto.addressByPublicKey( const address = EthCrypto.publicKey.toAddress(
'bf1cc3154424dc22191941d9f4f50b063a2b663a2337e5548abea633c1d06ece...' 'bf1cc3154424dc22191941d9f4f50b063a2b663a2337e5548abea633c1d06ece...'
); );
// > '0x3f243FdacE01Cfd9719f7359c94BA11361f32471' // > '0x3f243FdacE01Cfd9719f7359c94BA11361f32471'
``` ```
### publicKey.compress()
Compresses an uncompressed publicKey.
```javascript
const address = EthCrypto.publicKey.compress(
'04a34d6aef3eb42335fb3cacb59...'
);
// > '03a34d6aef3eb42335fb3cacb59478c0b44c0bbeb8bb4ca427dbc7044157a5d24b' // compressed keys start with '02' or '03'
```
### publicKey.decompress()
Decompresses a compressed publicKey.
```javascript
const address = EthCrypto.publicKey.decompress(
'03a34d6aef3eb42335fb3c...'
);
// > 'a34d6aef3eb42335fb3cacb5947' // non-compressed keys start with '04' or no prefix
```
### sign() ### sign()
Signs the hash with the privateKey. Returns the signature as hex-string. Signs the hash with the privateKey. Returns the signature as hex-string.

@ -88,6 +88,6 @@
"ethereumjs-tx": "1.3.4", "ethereumjs-tx": "1.3.4",
"ethereumjs-util": "5.1.5", "ethereumjs-util": "5.1.5",
"secp256k1": "3.5.0", "secp256k1": "3.5.0",
"web3": "1.0.0-beta.30" "web3": "1.0.0-beta.34"
} }
} }

@ -1,15 +0,0 @@
import EthUtil from 'ethereumjs-util';
import {
web3
} from './util';
/**
* generates the ethereum-adress of the publicKey
* We create the checksum-adress which is case-sensitive
* @returns {string} address
*/
export default function addressByPublicKey(publicKey) {
const addressBuffer = EthUtil.pubToAddress(new Buffer(publicKey, 'hex'));
const checkSumAdress = web3.utils.toChecksumAddress(addressBuffer.toString('hex'));
return checkSumAdress;
}

@ -1,20 +1,13 @@
import {
web3
} from './util';
import publicKeyByPrivateKey from './public-key-by-private-key'; import publicKeyByPrivateKey from './public-key-by-private-key';
import Account from 'eth-lib/lib/account';
/** /**
* creates a new object with * creates a new object with
* private-, public-Key and address * private-, public-Key and address
*/ */
export default function createIdentity() { export default function createIdentity() {
const account = web3.eth.accounts.create(); const identity = Account.create();
identity.publicKey = publicKeyByPrivateKey(identity.privateKey);
const identity = {
address: account.address,
privateKey: account.privateKey,
publicKey: publicKeyByPrivateKey(account.privateKey)
};
return identity; return identity;
} }

@ -1,7 +1,13 @@
import eccrypto from 'eccrypto'; import eccrypto from 'eccrypto';
import {
decompress
} from './public-key';
export default async function encryptWithPublicKey(publicKey, message) { export default async function encryptWithPublicKey(publicKey, message) {
// ensure its an uncompressed publicKey
publicKey = decompress(publicKey);
// re-add the compression-flag // re-add the compression-flag
const pubString = '04' + publicKey; const pubString = '04' + publicKey;

@ -13,16 +13,3 @@ export function keccak256(params) {
} }
export const SIGN_PREFIX = '\x19Ethereum Signed Message:\n32'; export const SIGN_PREFIX = '\x19Ethereum Signed Message:\n32';
/**
* hashes the given hash with the web3-prefix
* '\x19Ethereum Signed Message:\n32'
*/
export function prefixedHash(hash) {
if (!web3.utils.isHexStrict(hash))
throw new Error('EthCrypto.hash.prefixedHash(): please insert an hash');
return web3.eth.accounts.hashMessage({
type: 'bytes32',
value: hash
});
}

@ -4,10 +4,13 @@
* @link https://stackoverflow.com/a/40471908/3443137 * @link https://stackoverflow.com/a/40471908/3443137
*/ */
import * as util from './util'; import {
removeTrailing0x,
addTrailing0x
} from './util';
export function compress(hex, base64 = false) { export function compress(hex, base64 = false) {
hex = util.removeTrailing0x(hex); hex = removeTrailing0x(hex);
// if base64:true, we use our own function because it results in a smaller output // if base64:true, we use our own function because it results in a smaller output
if (base64 === true) if (base64 === true)
@ -29,7 +32,7 @@ export function decompress(compressedString, base64 = false) {
// if base64:true, we use our own function because it results in a smaller output // if base64:true, we use our own function because it results in a smaller output
if (base64 === true) { if (base64 === true) {
const ret = new Buffer(compressedString, 'base64').toString('hex'); const ret = new Buffer(compressedString, 'base64').toString('hex');
return util.addTrailing0x(ret); return addTrailing0x(ret);
} }
let hex = ''; let hex = '';
@ -38,5 +41,5 @@ export function decompress(compressedString, base64 = false) {
hex += ((i == 0 ? '' : '000') + compressedString.charCodeAt(i).toString(16)).slice(-4); hex += ((i == 0 ? '' : '000') + compressedString.charCodeAt(i).toString(16)).slice(-4);
} }
hex = hex.toLowerCase(); hex = hex.toLowerCase();
return util.addTrailing0x(hex); return addTrailing0x(hex);
} }

@ -1,5 +1,6 @@
import addressByPublicKey from './address-by-public-key';
import createIdentity from './create-identity'; import createIdentity from './create-identity';
import * as publicKey from './public-key';
import decryptWithPrivateKey from './decrypt-with-private-key'; import decryptWithPrivateKey from './decrypt-with-private-key';
import encryptWithPublicKey from './encrypt-with-public-key'; import encryptWithPublicKey from './encrypt-with-public-key';
import publicKeyByPrivateKey from './public-key-by-private-key'; import publicKeyByPrivateKey from './public-key-by-private-key';
@ -15,8 +16,8 @@ import * as vrs from './vrs';
import * as util from './util'; import * as util from './util';
export { export {
addressByPublicKey,
createIdentity, createIdentity,
publicKey,
decryptWithPrivateKey, decryptWithPrivateKey,
encryptWithPublicKey, encryptWithPublicKey,
publicKeyByPrivateKey, publicKeyByPrivateKey,
@ -33,8 +34,8 @@ export {
}; };
export default { export default {
addressByPublicKey,
createIdentity, createIdentity,
publicKey,
decryptWithPrivateKey, decryptWithPrivateKey,
encryptWithPublicKey, encryptWithPublicKey,
publicKeyByPrivateKey, publicKeyByPrivateKey,

@ -0,0 +1,48 @@
import {
publicKeyConvert
} from 'secp256k1';
import ethUtil from 'ethereumjs-util';
export function compress(startsWith04) {
// add trailing 04 if not done before
const testBuffer = new Buffer(startsWith04, 'hex');
if (testBuffer.length === 64) startsWith04 = '04' + startsWith04;
return publicKeyConvert(
new Buffer(startsWith04, 'hex'),
true
).toString('hex');
};
export function decompress(startsWith02Or03) {
// if already decompressed an not has trailing 04
const testBuffer = new Buffer(startsWith02Or03, 'hex');
if (testBuffer.length === 64) startsWith02Or03 = '04' + startsWith02Or03;
let decompressed = publicKeyConvert(
new Buffer(startsWith02Or03, 'hex'),
false
).toString('hex');
// remove trailing 04
decompressed = decompressed.substring(2);
return decompressed;
};
/**
* generates the ethereum-adress of the publicKey
* We create the checksum-adress which is case-sensitive
* @returns {string} address
*/
export function toAddress(publicKey) {
// normalize key
publicKey = decompress(publicKey);
const addressBuffer = ethUtil.pubToAddress(new Buffer(publicKey, 'hex'));
const checkSumAdress = ethUtil.toChecksumAddress(addressBuffer.toString('hex'));
return checkSumAdress;
}

@ -1,7 +1,11 @@
import * as secp256k1 from 'secp256k1'; import {
recover
} from 'secp256k1';
import * as vrs from './vrs'; import * as vrs from './vrs';
import * as util from './util'; import {
removeTrailing0x
} from './util';
/** /**
@ -15,12 +19,12 @@ export default function recoverPublicKey(signature, hash) {
let sigOnly = signature.substring(0, signature.length - 1); let sigOnly = signature.substring(0, signature.length - 1);
sigOnly = util.removeTrailing0x(sigOnly); sigOnly = removeTrailing0x(sigOnly);
const recoveryNumber = vals.v === '0x1c' ? 1 : 0; const recoveryNumber = vals.v === '0x1c' ? 1 : 0;
let pubKey = secp256k1.recover( let pubKey = recover(
new Buffer(util.removeTrailing0x(hash), 'hex'), new Buffer(removeTrailing0x(hash), 'hex'),
new Buffer(sigOnly, 'hex'), new Buffer(sigOnly, 'hex'),
recoveryNumber, recoveryNumber,
false false

@ -1,5 +1,7 @@
import recoverPublicKey from './recover-public-key'; import recoverPublicKey from './recover-public-key';
import addressByPublicKey from './address-by-public-key'; import {
toAddress as addressByPublicKey
} from './public-key';
/** /**
* returns the adress with which the messageHash was signed * returns the adress with which the messageHash was signed

@ -1,6 +1,8 @@
import Tx from 'ethereumjs-tx'; import Tx from 'ethereumjs-tx';
import publicKeyByPrivateKey from './public-key-by-private-key'; import publicKeyByPrivateKey from './public-key-by-private-key';
import addressByPublicKey from './address-by-public-key'; import {
toAddress as addressByPublicKey
} from './public-key';
export default function signTransaction( export default function signTransaction(
rawTx, rawTx,

@ -1,5 +1,10 @@
import * as secp256k1 from 'secp256k1'; import {
import * as util from './util'; sign as secp256k1_sign
} from 'secp256k1';
import {
addTrailing0x,
removeTrailing0x
} from './util';
/** /**
* signs the given message * signs the given message
@ -9,13 +14,13 @@ import * as util from './util';
* @return {string} hexString * @return {string} hexString
*/ */
export default function sign(privateKey, hash) { export default function sign(privateKey, hash) {
hash = util.addTrailing0x(hash); hash = addTrailing0x(hash);
if (hash.length !== 66) if (hash.length !== 66)
throw new Error('EthCrypto.sign(): Can only sign hashes, given: ' + hash); throw new Error('EthCrypto.sign(): Can only sign hashes, given: ' + hash);
const sigObj = secp256k1.sign( const sigObj = secp256k1_sign(
new Buffer(util.removeTrailing0x(hash), 'hex'), new Buffer(removeTrailing0x(hash), 'hex'),
new Buffer(util.removeTrailing0x(privateKey), 'hex') new Buffer(removeTrailing0x(privateKey), 'hex')
); );
const recoveryId = sigObj.recovery === 1 ? '1c' : '1b'; const recoveryId = sigObj.recovery === 1 ? '1c' : '1b';

@ -36,21 +36,6 @@ describe('unit.test.js', () => {
}); });
}); });
}); });
describe('.addressByPublicKey()', () => {
describe('positive', () => {
it('should generate the correct address', () => {
const address = EthCrypto.addressByPublicKey(TEST_DATA.publicKey);
assert.equal(address, TEST_DATA.address);
});
});
describe('negative', () => {
assert.throws(
() => EthCrypto.addressByPublicKey(
AsyncTestUtil.randomString(12)
)
);
});
});
describe('.sign()', () => { describe('.sign()', () => {
describe('positive', () => { describe('positive', () => {
it('should sign the data', () => { it('should sign the data', () => {
@ -116,6 +101,20 @@ describe('unit.test.js', () => {
assert.equal(typeof encrypted.ciphertext, 'string'); assert.equal(typeof encrypted.ciphertext, 'string');
assert.equal(typeof encrypted.mac, 'string'); assert.equal(typeof encrypted.mac, 'string');
}); });
it('should also work with compressed keys', async () => {
const message = AsyncTestUtil.randomString(12);
const ident = EthCrypto.createIdentity();
const compressed = EthCrypto.publicKey.compress(ident.publicKey);
const encrypted = await EthCrypto.encryptWithPublicKey(
compressed,
message
);
const decrypted = await EthCrypto.decryptWithPrivateKey(
ident.privateKey,
encrypted
);
assert.equal(decrypted, message);
});
}); });
describe('negative', () => { describe('negative', () => {
it('should throw when non-key given', async () => { it('should throw when non-key given', async () => {
@ -124,7 +123,8 @@ describe('unit.test.js', () => {
() => EthCrypto.encryptWithPublicKey( () => EthCrypto.encryptWithPublicKey(
AsyncTestUtil.randomString(12), AsyncTestUtil.randomString(12),
message message
) ),
'RangeError'
); );
}); });
}); });
@ -146,6 +146,72 @@ describe('unit.test.js', () => {
}); });
describe('negative', () => {}); describe('negative', () => {});
}); });
describe('.publicKey', () => {
describe('.compress()', () => {
it('should compress the key', () => {
const uncompressed = 'a34d6aef3eb42335fb3cacb59478c0b44c0bbeb8bb4ca427dbc7044157a5d24b4adf14868d8449c9b3e50d3d6338f3e5a2d3445abe679cddbe75cb893475806f';
const compressed = EthCrypto.publicKey.compress(uncompressed);
assert.equal(typeof compressed, 'string');
assert.ok(compressed.startsWith('03'));
});
it('should also work with trailing 04', () => {
const uncompressed = '04a34d6aef3eb42335fb3cacb59478c0b44c0bbeb8bb4ca427dbc7044157a5d24b4adf14868d8449c9b3e50d3d6338f3e5a2d3445abe679cddbe75cb893475806f';
const compressed = EthCrypto.publicKey.compress(uncompressed);
assert.equal(typeof compressed, 'string');
assert.ok(compressed.startsWith('03'));
});
it('should also work when compressed already given', () => {
const uncompressed = '03a34d6aef3eb42335fb3cacb59478c0b44c0bbeb8bb4ca427dbc7044157a5d24b';
const compressed = EthCrypto.publicKey.compress(uncompressed);
assert.equal(typeof compressed, 'string');
assert.ok(compressed.startsWith('03'));
});
});
describe('.decompress()', () => {
it('should decompress', () => {
const compressed = '03a34d6aef3eb42335fb3cacb59478c0b44c0bbeb8bb4ca427dbc7044157a5d24b';
const uncompressed = EthCrypto.publicKey.decompress(compressed);
assert.equal(typeof uncompressed, 'string');
const buf = new Buffer(uncompressed, 'hex');
assert.equal(buf.length, 64);
});
it('should work when already uncompressed', () => {
const compressed = '04a34d6aef3eb42335fb3cacb59478c0b44c0bbeb8bb4ca427dbc7044157a5d24b4adf14868d8449c9b3e50d3d6338f3e5a2d3445abe679cddbe75cb893475806f';
const uncompressed = EthCrypto.publicKey.decompress(compressed);
assert.equal(typeof uncompressed, 'string');
const buf = new Buffer(uncompressed, 'hex');
assert.equal(buf.length, 64);
});
it('should work when already uncompressed (no04)', () => {
const compressed = 'a34d6aef3eb42335fb3cacb59478c0b44c0bbeb8bb4ca427dbc7044157a5d24b4adf14868d8449c9b3e50d3d6338f3e5a2d3445abe679cddbe75cb893475806f';
const uncompressed = EthCrypto.publicKey.decompress(compressed);
assert.equal(typeof uncompressed, 'string');
const buf = new Buffer(uncompressed, 'hex');
assert.equal(buf.length, 64);
});
});
describe('.toAddress()', () => {
describe('positive', () => {
it('should generate the correct address', () => {
const address = EthCrypto.publicKey.toAddress(TEST_DATA.publicKey);
assert.equal(address, TEST_DATA.address);
});
it('should work with compressed key', () => {
const ident = EthCrypto.createIdentity();
const compressed = EthCrypto.publicKey.compress(ident.publicKey);
const address = EthCrypto.publicKey.toAddress(compressed);
assert.equal(address, ident.address);
});
});
describe('negative', () => {
assert.throws(
() => EthCrypto.publicKey.toAddress(
AsyncTestUtil.randomString(12)
)
);
});
});
});
describe('.signTransaction()', () => { describe('.signTransaction()', () => {
describe('positive', () => { describe('positive', () => {
it('should sign our transaction', () => { it('should sign our transaction', () => {

11
typings/index.d.ts vendored

@ -7,8 +7,13 @@ export function createIdentity(): {
address: string address: string
}; };
export type publicKey = {
compress(publicKey: string): string;
decompress(publicKey: string): string;
toAddress(publicKey: string): string;
};
export function publicKeyByPrivateKey(privateKey: string): string; export function publicKeyByPrivateKey(privateKey: string): string;
export function addressByPublicKey(publicKey: string): string;
export type Signature = { export type Signature = {
v: string, v: string,
@ -67,8 +72,6 @@ export type TypedValue = {
export type hash = { export type hash = {
keccak256(params: TypedValue[]): string; keccak256(params: TypedValue[]): string;
prefixedHash(msg: string): string;
SIGN_PREFIX: string;
}; };
export type util = { export type util = {
@ -86,8 +89,8 @@ export type hex = {
export function publicKeyToAddress(publicKey: string): string; export function publicKeyToAddress(publicKey: string): string;
declare const _default: { declare const _default: {
addressByPublicKey,
createIdentity, createIdentity,
publicKey,
decryptWithPrivateKey, decryptWithPrivateKey,
encryptWithPublicKey, encryptWithPublicKey,
publicKeyByPrivateKey, publicKeyByPrivateKey,

Loading…
Cancel
Save