Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Optional functionality for R1 support
Browse files Browse the repository at this point in the history
Add tests
  • Loading branch information
Bradley Hart committed Dec 26, 2019
1 parent e2c667e commit 72f984a
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 62 deletions.
15 changes: 7 additions & 8 deletions src/PrivateKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export class PrivateKey {

/** Instantiate private key from an `elliptic`-format private key */
public static fromElliptic(privKey: ec.KeyPair, keyType = KeyType.k1): PrivateKey {
const privArray = privKey.getPrivate().toArray();
return new PrivateKey({
type: keyType,
data: privKey.getPrivate().toBuffer(),
Expand All @@ -21,19 +20,15 @@ export class PrivateKey {

/** Instantiate private key from an EOSIO-format private key */
public static fromString(keyString: string): PrivateKey {
const key: Key = stringToPrivateKey(keyString);
if (key.type !== KeyType.k1) {
throw new Error('Key type isn\'t k1');
}
return new PrivateKey(key);
return new PrivateKey(stringToPrivateKey(keyString));
}

/** Export private key as `elliptic`-format private key */
public toElliptic(ecurve?: ec) {
/** expensive to construct; so we do it only as needed */
if (!ecurve) {
if (this.key.type === KeyType.r1) {
ecurve = new ec('secp256r1') as any;
if (this.key.type === KeyType.r1 || this.key.type === KeyType.wa) {
ecurve = new ec('p256') as any;
} else {
ecurve = new ec('secp256k1') as any;
}
Expand All @@ -45,4 +40,8 @@ export class PrivateKey {
public toString(): string {
return privateKeyToString(this.key);
}

public getType(): KeyType {
return this.key.type;
}
}
8 changes: 6 additions & 2 deletions src/PublicKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ export class PublicKey {
public toElliptic(ecurve?: ec): ec.KeyPair {
/** expensive to construct; so we do it only as needed */
if (!ecurve) {
if (this.key.type === KeyType.r1) {
ecurve = new ec('secp256r1') as any;
if (this.key.type === KeyType.r1 || this.key.type === KeyType.wa) {
ecurve = new ec('p256') as any;
} else {
ecurve = new ec('secp256k1') as any;
}
Expand All @@ -44,4 +44,8 @@ export class PublicKey {
pub: new Buffer(this.key.data),
});
}

public getType(): KeyType {
return this.key.type;
}
}
30 changes: 22 additions & 8 deletions src/Signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,21 @@ export class Signature {
}

/** Instantiate Signature from an `elliptic`-format Signature */
public static fromElliptic(ellipticSig: ec.Signature): Signature {
public static fromElliptic(ellipticSig: ec.Signature, keyType: KeyType = KeyType.k1): Signature {
const r = ellipticSig.r.toArray();
const s = ellipticSig.s.toArray();
let eosioRecoveryParam = ellipticSig.recoveryParam + 27;
if (ellipticSig.recoveryParam <= 3) {
eosioRecoveryParam += 4;
let eosioRecoveryParam;
if (keyType === KeyType.k1) {
eosioRecoveryParam = ellipticSig.recoveryParam + 27;
if (ellipticSig.recoveryParam <= 3) {
eosioRecoveryParam += 4;
}
} else if (keyType === KeyType.r1 || keyType === KeyType.wa) {
eosioRecoveryParam = ellipticSig.recoveryParam;
}
const sigData = new Uint8Array([eosioRecoveryParam].concat(r, s));
return new Signature({
type: KeyType.k1,
type: keyType,
data: sigData,
});
}
Expand All @@ -45,9 +50,14 @@ export class Signature {
const r = new BN(this.signature.data.slice(1, lengthOfR + 1));
const s = new BN(this.signature.data.slice(lengthOfR + 1, lengthOfR + lengthOfS + 1));

let ellipticRecoveryBitField = this.signature.data[0] - 27;
if (ellipticRecoveryBitField > 3) {
ellipticRecoveryBitField -= 4;
let ellipticRecoveryBitField;
if (this.signature.type === KeyType.k1) {
ellipticRecoveryBitField = this.signature.data[0] - 27;
if (ellipticRecoveryBitField > 3) {
ellipticRecoveryBitField -= 4;
}
} else if (this.signature.type === KeyType.r1 || this.signature.type === KeyType.wa) {
ellipticRecoveryBitField = this.signature.data[0];
}
const recoveryParam = ellipticRecoveryBitField & 3;
return { r, s, recoveryParam };
Expand All @@ -62,4 +72,8 @@ export class Signature {
public toBinary(): Uint8Array {
return this.signature.data;
}

public getType(): KeyType {
return this.signature.type;
}
}
7 changes: 4 additions & 3 deletions src/eosjs-jssig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,10 @@ class JsSignatureProvider implements SignatureProvider {
/** @param privateKeys private keys to sign with */
constructor(privateKeys: string[]) {
for (const k of privateKeys) {
const priv = PrivateKey.fromString(k).toElliptic(defaultEc);
const pubStr = PublicKey.fromElliptic(priv, KeyType.k1).toString();
this.keys.set(pubStr, priv);
const priv = PrivateKey.fromString(k);
const privElliptic = priv.toElliptic();
const pubStr = PublicKey.fromElliptic(privElliptic, priv.getType()).toString();
this.keys.set(pubStr, privElliptic);
this.availableKeys.push(pubStr);
}
}
Expand Down
142 changes: 101 additions & 41 deletions src/tests/eosjs-jssig.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
JsSignatureProvider,
digestFromSerializedData
} from '../eosjs-jssig';
import { KeyType } from '../eosjs-numeric';
import { SignatureProviderArgs } from '../eosjs-api-interfaces';

describe('JsSignatureProvider', () => {
Expand All @@ -13,6 +14,10 @@ describe('JsSignatureProvider', () => {
'5JnHjSFwe4r7xyqAUAaVs51G7HmzE86DWGa3VAA5VvQriGYnSUr',
'5K4XZH5XR2By7Q5KTcZnPAmUMU5yjUNBdoKzzXyrLfmiEZJqoKE',
];
const privateKeysR1 = [
'PVT_R1_GrfEfbv5at9kbeHcGagQmvbFLdm6jqEpgE1wsGbrfbZNjpVgT',
'PVT_R1_wCpPsaY9o8NU9ZsuwaYVQUDkCfj1aWJZGVcmMM6XyYHJVqvqp',
];
const legacyPublicKeys = [
'EOS7tgwU6E7pAUQJgqEJt66Yi8cWvanTUW8ZfBjeXeJBQvhTU9ypi',
'EOS8VaY5CiTexYqgQZyPTJkc3qvWuZUi12QrZL9ssjqW2es6aQk2F',
Expand All @@ -23,6 +28,10 @@ describe('JsSignatureProvider', () => {
'PUB_K1_8VaY5CiTexYqgQZyPTJkc3qvWuZUi12QrZL9ssjqW2es7e7bRJ',
'PUB_K1_7VGhqctkKprW1VUj19DZZiiZLX3YcJqUJCuEcahJmUCw9RT8v2',
];
const r1FormatPublicKeys = [
'PUB_R1_4ztaVy8L9zbmzTdpfq5GcaFYwGwXTNmN3qW7qcgHMmfUZhpzQQ',
'PUB_R1_5xawnnr3mWayv2wkiqBGWqu4RQLNJffLSXHiL3BofdY7ortMy4',
];
const signatures = [
'SIG_K1_HKkqi3zray76i63ZQwAHWMjoLk3wTa1ajZWPcUnrhgmSWQYEHDJsxkny6VDTWEmVdfktxpGoTA81qe6QuCrDmazeQndmxh',
'SIG_K1_HCaY9Y9qdjnkRhE9hokAyp3pFtkMmjpxF6xTd514Vo8vLVSWKek5m5aHfCaka9TqZUbajkhhd4BfBLxSwCwZUEmy8cvt1x',
Expand All @@ -42,6 +51,12 @@ describe('JsSignatureProvider', () => {
expect(actualPublicKeys).toEqual(k1FormatPublicKeys);
});

it('builds p256 elliptic public keys from private when constructed', async () => {
const provider = new JsSignatureProvider(privateKeysR1);
const actualPublicKeys = await provider.getAvailableKeys();
expect(actualPublicKeys).toEqual(r1FormatPublicKeys);
});

it('signs a transaction', async () => {
const provider = new JsSignatureProvider(privateKeys);
const chainId = '12345';
Expand Down Expand Up @@ -102,50 +117,95 @@ describe('JsSignatureProvider', () => {
).toEqual(true);
});

it('ensure public key functions are actual inverses of each other', async () => {
const eosioPubKey = PublicKey.fromString(k1FormatPublicKeys[0]);
const ellipticPubKey = eosioPubKey.toElliptic();
const finalEosioKeyAsK1String = PublicKey.fromElliptic(ellipticPubKey).toString();
expect(finalEosioKeyAsK1String).toEqual(k1FormatPublicKeys[0]);
});
describe('secp256k1 elliptic', () => {
it('ensure public key functions are actual inverses of each other', async () => {
const eosioPubKey = PublicKey.fromString(k1FormatPublicKeys[0]);
const ellipticPubKey = eosioPubKey.toElliptic();
const finalEosioKeyAsK1String = PublicKey.fromElliptic(ellipticPubKey).toString();
expect(finalEosioKeyAsK1String).toEqual(k1FormatPublicKeys[0]);
});

it('verify that PUB_K1_ and Legacy pub formats are consistent', () => {
const eosioLegacyPubKey = legacyPublicKeys[0];
const ellipticPubKey = PublicKey.fromString(eosioLegacyPubKey).toElliptic();
expect(PublicKey.fromElliptic(ellipticPubKey).toString()).toEqual(k1FormatPublicKeys[0]);
});
it('verify that PUB_K1_ and Legacy pub formats are consistent', () => {
const eosioLegacyPubKey = legacyPublicKeys[0];
const ellipticPubKey = PublicKey.fromString(eosioLegacyPubKey).toElliptic();
expect(PublicKey.fromElliptic(ellipticPubKey).toString()).toEqual(k1FormatPublicKeys[0]);
});

it('ensure private key functions are actual inverses of each other', async () => {
const priv = privateKeys[0];
const privEosioKey = PrivateKey.fromString(priv);
const privEllipticKey = privEosioKey.toElliptic();
const finalEosioKeyAsString = PrivateKey.fromElliptic(privEllipticKey).toString();
expect(privEosioKey.toString()).toEqual(finalEosioKeyAsString);
});

it('ensure private key functions are actual inverses of each other', async () => {
const priv = privateKeys[0];
const privEosioKey = PrivateKey.fromString(priv);
const privEllipticKey = privEosioKey.toElliptic();
const finalEosioKeyAsString = PrivateKey.fromElliptic(privEllipticKey).toString();
expect(privEosioKey.toString()).toEqual(finalEosioKeyAsString);
it('Ensure elliptic sign, recover, verify flow works', () => {
const ellipticEc = new ec('secp256k1');
const KPriv = privateKeys[0];
const KPrivElliptic = PrivateKey.fromString(KPriv).toElliptic();

const dataAsString = 'some string';
const ellipticHashedString = ellipticEc.hash().update(dataAsString).digest();
// const ellipticHashedString = Buffer.from(hashedData);

const ellipticSig = KPrivElliptic.sign(ellipticHashedString);
// expect(Signature.fromElliptic(ellipticSig).toString()).toEqual(signatures[0]);
const ellipticRecoveredKPub = ellipticEc.recoverPubKey(
ellipticHashedString,
ellipticSig,
ellipticSig.recoveryParam
);
const ellipticKPub = ellipticEc.keyFromPublic(ellipticRecoveredKPub);
expect(PublicKey.fromElliptic(ellipticKPub).toString()).toEqual(k1FormatPublicKeys[0]);
const ellipticValid = ellipticEc.verify(
ellipticHashedString,
ellipticSig,
ellipticEc.keyFromPublic(ellipticKPub)
);
expect(ellipticValid).toEqual(true);
});
});

it('Ensure elliptic sign, recover, verify flow works', () => {
const ellipticEc = new ec('secp256k1');
const KPriv = privateKeys[0];
const KPrivElliptic = PrivateKey.fromString(KPriv).toElliptic();

const dataAsString = 'some string';
const ellipticHashedString = ellipticEc.hash().update(dataAsString).digest();
// const ellipticHashedString = Buffer.from(hashedData);

const ellipticSig = KPrivElliptic.sign(ellipticHashedString);
// expect(Signature.fromElliptic(ellipticSig).toString()).toEqual(signatures[0]);
const ellipticRecoveredKPub = ellipticEc.recoverPubKey(
ellipticHashedString,
ellipticSig,
ellipticSig.recoveryParam
);
const ellipticKPub = ellipticEc.keyFromPublic(ellipticRecoveredKPub);
expect(PublicKey.fromElliptic(ellipticKPub).toString()).toEqual(k1FormatPublicKeys[0]);
const ellipticValid = ellipticEc.verify(
ellipticHashedString,
ellipticSig,
ellipticEc.keyFromPublic(ellipticKPub)
);
expect(ellipticValid).toEqual(true);
describe('p256 elliptic', () => {
it('ensure public key functions using p256 format are actual inverses of each other', async () => {
const eosioPubKey = PublicKey.fromString(r1FormatPublicKeys[0]);
const ellipticPubKey = eosioPubKey.toElliptic();
const finalEosioKeyAsR1String = PublicKey.fromElliptic(ellipticPubKey, KeyType.r1).toString();
expect(finalEosioKeyAsR1String).toEqual(r1FormatPublicKeys[0]);
});

it('ensure private key functions using p256 format are actual inverses of each other', async () => {
const priv = privateKeysR1[0];
const privEosioKey = PrivateKey.fromString(priv);
const privEllipticKey = privEosioKey.toElliptic();
const finalEosioKeyAsString = PrivateKey.fromElliptic(privEllipticKey, KeyType.r1).toString();
expect(privEosioKey.toString()).toEqual(finalEosioKeyAsString);
});

it('Ensure elliptic sign, recover, verify flow works', () => {
const ellipticEc = new ec('p256');
const KPriv = privateKeysR1[0];
const KPrivElliptic = PrivateKey.fromString(KPriv).toElliptic();

const dataAsString = 'some string';
const ellipticHashedString = ellipticEc.hash().update(dataAsString).digest();
// const ellipticHashedString = Buffer.from(hashedData);

const ellipticSig = KPrivElliptic.sign(ellipticHashedString);
// expect(Signature.fromElliptic(ellipticSig).toString()).toEqual(signatures[0]);
const ellipticRecoveredKPub = ellipticEc.recoverPubKey(
ellipticHashedString,
ellipticSig,
ellipticSig.recoveryParam
);
const ellipticKPub = ellipticEc.keyFromPublic(ellipticRecoveredKPub);
expect(PublicKey.fromElliptic(ellipticKPub, KeyType.r1).toString()).toEqual(r1FormatPublicKeys[0]);
const ellipticValid = ellipticEc.verify(
ellipticHashedString,
ellipticSig,
ellipticEc.keyFromPublic(ellipticKPub)
);
expect(ellipticValid).toEqual(true);
});
});
});

0 comments on commit 72f984a

Please sign in to comment.