Skip to content

Commit

Permalink
change to canonical ECDSA sign mode, Signable interface, Transfer cla…
Browse files Browse the repository at this point in the history
…ss (#77)

* change to canonical ECDSA sign mode

Signed-off-by: Matus Zamborsky <[email protected]>

* add Signable interface, postpone transaction Hash generation using Signable interface

Signed-off-by: Matus Zamborsky <[email protected]>

* add missing signable support for verify

Signed-off-by: Matus Zamborsky <[email protected]>

* add Transfer information

Signed-off-by: Matus Zamborsky <[email protected]>

* add disconnected log to websocketclient

Signed-off-by: Matus Zamborsky <[email protected]>
  • Loading branch information
backslash47 authored and MickWang committed Aug 2, 2018
1 parent ab42f83 commit 98ca8c7
Show file tree
Hide file tree
Showing 13 changed files with 176 additions and 31 deletions.
17 changes: 12 additions & 5 deletions src/crypto/PrivateKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ import { Address } from './address';
import { Key, KeyParameters } from './Key';
import { KeyType } from './KeyType';
import { PublicKey } from './PublicKey';
import { Signable } from './signable';
import { Signature } from './Signature';
import { SignatureScheme } from './SignatureScheme';

// tslint:disable-next-line:no-var-requires
const HDKey = require('@ont-community/hdkey-secp256r1');

Expand Down Expand Up @@ -94,11 +96,11 @@ export class PrivateKey extends Key {
*
* This method is not suitable, if external keys (Ledger, TPM, ...) support is required.
*
* @param msg Hex encoded input data
* @param msg Hex encoded input data or Signable object
* @param schema Signing schema to use
* @param publicKeyId Id of public key
*/
sign(msg: string, schema?: SignatureScheme, publicKeyId?: string): Signature {
sign(msg: string | Signable, schema?: SignatureScheme, publicKeyId?: string): Signature {
if (schema === undefined) {
schema = this.algorithm.defaultSchema;
}
Expand All @@ -107,6 +109,11 @@ export class PrivateKey extends Key {
throw new Error('Signature schema does not match key type.');
}

// retrieves content to sign if not provided directly
if (typeof msg !== 'string') {
msg = msg.getSignContent();
}

let hash: string;
if (schema === SignatureScheme.SM2withSM3) {
// library sm.js (SM2withSM3) has implemented hashing as part of signing, therefore it is skipped
Expand All @@ -126,11 +133,11 @@ export class PrivateKey extends Key {
*
* This method is suitable, if external keys (Ledger, TPM, ...) support is required.
*
* @param msg Hex encoded input data
* @param msg Hex encoded input data or Signable object
* @param schema Signing schema to use
* @param publicKeyId Id of public key
*/
async signAsync(msg: string, schema?: SignatureScheme, publicKeyId?: string): Promise<Signature> {
async signAsync(msg: string | Signable, schema?: SignatureScheme, publicKeyId?: string): Promise<Signature> {
return this.sign(msg, schema, publicKeyId);
}

Expand Down Expand Up @@ -255,7 +262,7 @@ export class PrivateKey extends Key {
*/
computeEcDSASignature(hash: string): string {
const ec = new elliptic.ec(this.parameters.curve.preset);
const signed = ec.sign(hash, this.key, null);
const signed = ec.sign(hash, this.key, { canonical: true });
return Buffer.concat([
signed.r.toArrayLike(Buffer, 'be', 32),
signed.s.toArrayLike(Buffer, 'be', 32)
Expand Down
10 changes: 8 additions & 2 deletions src/crypto/PublicKey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { hexstr2str, hexstring2ab, num2hexstring, StringReader } from '../utils'
import { CurveLabel } from './CurveLabel';
import { Key, KeyParameters } from './Key';
import { KeyType } from './KeyType';
import { Signable } from './signable';
import { Signature } from './Signature';
import { SignatureScheme } from './SignatureScheme';

Expand Down Expand Up @@ -60,14 +61,19 @@ export class PublicKey extends Key {
* Verifies if the signature was created with private key corresponding to supplied public key
* and was not tampered with using signature schema.
*
* @param msg Hex encoded input data
* @param msg Hex encoded input data or Signable object
* @param signature Signature object
*/
verify(msg: string, signature: Signature): boolean {
verify(msg: string | Signable, signature: Signature): boolean {
if (!this.isSchemaSupported(signature.algorithm)) {
throw new Error('Signature schema does not match key type.');
}

// retrieves content to sign if not provided directly
if (typeof msg !== 'string') {
msg = msg.getSignContent();
}

let hash: string;
if (signature.algorithm === SignatureScheme.SM2withSM3) {
// library sm.js (SM2withSM3) has implemented hashing as part of verification, therefore it is skipped
Expand Down
1 change: 1 addition & 0 deletions src/crypto/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ export { PrivateKey } from './PrivateKey';
export { KeyDeserializer, registerKeyDeserializer } from './PrivateKeyFactory';
export { PublicKey, PublicKeyStatus } from './PublicKey';
export { Signature, PgpSignature } from './Signature';
export { Signable } from './signable';
27 changes: 27 additions & 0 deletions src/crypto/signable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2018 The ontology Authors
* This file is part of The ontology library.
*
* The ontology is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ontology is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with The ontology. If not, see <http://www.gnu.org/licenses/>.
*/

/**
* Interface for other classes to implement, which should be signable.
*/
export interface Signable {
/**
* Get the sign content of object
*/
getSignContent(): string;
}
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import { DDO, DDOAttribute } from './transaction/ddo';
import * as ScriptBuilder from './transaction/scriptBuilder';
import { Transaction } from './transaction/transaction';
import * as TransactionBuilder from './transaction/transactionBuilder';
import { Transfer } from './transaction/transfer';
import { TxSignature } from './transaction/txSignature';
import * as utils from './utils';
import { Wallet } from './wallet';
Expand All @@ -49,6 +50,7 @@ class ONT {
DDO: any;
DDOAttribute: any;
Transaction: any;
Transfer: any;
TxSignature: any;
TransactionBuilder: any;
OntAssetTxBuilder: any;
Expand Down Expand Up @@ -78,6 +80,7 @@ class ONT {
this.DDO = DDO;
this.DDOAttribute = DDOAttribute;
this.Transaction = Transaction;
this.Transfer = Transfer;
this.TxSignature = TxSignature;
this.TransactionBuilder = TransactionBuilder;
this.OntAssetTxBuilder = OntAssetTxBuilder;
Expand Down Expand Up @@ -126,6 +129,7 @@ export {
DDO,
DDOAttribute,
Transaction,
Transfer,
TxSignature,
Parameter,
ParameterType,
Expand Down
7 changes: 7 additions & 0 deletions src/network/websocket/websocketSender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,13 @@ export class WebsocketSender {
}
});

this.wsp.onClose.addListener(() => {
if (this.debug) {
// tslint:disable-next-line:no-console
console.log('disconnected');
}
});

this.wsp.onSend.addListener((message: any) => {
if (this.debug) {
// tslint:disable-next-line:no-console
Expand Down
6 changes: 3 additions & 3 deletions src/sdk/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ export class SDK {
error: ERROR_CODE.SUCCESS,
result: '',
tx: tx.serialize(),
txHash: reverseHex(tx.getHash())
txHash: reverseHex(tx.getSignContent())
};
callback && sendBackResult2Native(JSON.stringify(result), callback);
// clear privateKey and password
Expand Down Expand Up @@ -810,7 +810,7 @@ export class SDK {
error: ERROR_CODE.SUCCESS,
result: '',
tx: tx.serialize(),
txHash: reverseHex(tx.getHash())
txHash: reverseHex(tx.getSignContent())
};
callback && sendBackResult2Native(JSON.stringify(result), callback);
// clear privateKey and password
Expand Down Expand Up @@ -1171,7 +1171,7 @@ export class SDK {
tx.payer = fromAddress;
const result = {
error: ERROR_CODE.SUCCESS,
txHash: reverseHex(tx.getHash()),
txHash: reverseHex(tx.getSignContent()),
txData: tx.serialize()
};
callback && sendBackResult2Native(JSON.stringify(result), callback);
Expand Down
10 changes: 8 additions & 2 deletions src/smartcontract/nativevm/ontAssetTxBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { TOKEN_TYPE } from '../../consts';
import { Address } from '../../crypto';
import { ERROR_CODE } from '../../error';
import { Transaction } from '../../transaction/transaction';
import { Transfer } from '../../transaction/transfer';
import { hex2VarBytes } from '../../utils';
import { makeNativeContractTx } from './../../transaction/transactionBuilder';
import { buildNativeCodeScript } from './../abi/nativeVmParamsBuilder';
Expand Down Expand Up @@ -72,7 +73,7 @@ export function makeTransferTx(
gasPrice: string,
gasLimit: string,
payer?: Address
): Transaction {
): Transfer {
amount = Number(amount);
verifyAmount(amount);
const struct = new Struct();
Expand All @@ -81,7 +82,12 @@ export function makeTransferTx(
list.push([struct]);
const contract = getTokenContract(tokenType);
const params = buildNativeCodeScript(list);
const tx = makeNativeContractTx('transfer', params, contract, gasPrice, gasLimit);
const tx: Transfer = makeNativeContractTx('transfer', params, contract, gasPrice, gasLimit) as any;
tx.tokenType = tokenType;
tx.from = from;
tx.to = to;
tx.amount = amount;

if (payer) {
tx.payer = payer;
} else {
Expand Down
15 changes: 12 additions & 3 deletions src/transaction/transaction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import * as cryptoJS from 'crypto-js';
import Fixed64 from '../common/fixed64';
import { Address } from '../crypto/address';
import { Signable } from '../crypto/signable';
import { ab2hexstring, generateRandomArray, num2hexstring, StringReader } from '../utils';
import DeployCode from './payload/deployCode';
import InvokeCode from './payload/invokeCode';
Expand Down Expand Up @@ -70,7 +71,7 @@ export class Fee {
}
}

export class Transaction {
export class Transaction implements Signable {
static deserialize(hexstring: string): Transaction {
const tx = new Transaction();

Expand Down Expand Up @@ -231,9 +232,9 @@ export class Transaction {
}

/**
* Get the hash of transaction
* Get the signable content
*/
getHash() {
getSignContent() {
const data = this.serializeUnsignedData();

const ProgramHexString = cryptoJS.enc.Hex.parse(data);
Expand All @@ -242,4 +243,12 @@ export class Transaction {

return ProgramSha2562;
}

/**
* Get the hash of transaction
* @deprecated Use getSignContent instead
*/
getHash() {
return this.getSignContent();
}
}
25 changes: 14 additions & 11 deletions src/transaction/transactionBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import DeployCode from './payload/deployCode';
import InvokeCode from './payload/invokeCode';
import { buildSmartContractParam, pushHexString, pushInt } from './scriptBuilder';
import { Transaction, TxType } from './transaction';
import { Transfer } from './transfer';
import { TxSignature } from './txSignature';
// const abiInfo = AbiInfo.parseJson(JSON.stringify(json));

Expand All @@ -52,9 +53,7 @@ export const Default_params = {
* @param schema Signature Schema to use
*/
export const signTransaction = (tx: Transaction, privateKey: PrivateKey, schema?: SignatureScheme) => {
const hash = tx.getHash();

const signature = TxSignature.create(hash, privateKey, schema);
const signature = TxSignature.create(tx, privateKey, schema);

tx.sigs = [signature];
};
Expand All @@ -70,9 +69,7 @@ export const signTransaction = (tx: Transaction, privateKey: PrivateKey, schema?
* @param schema Signature Schema to use
*/
export const signTransactionAsync = async (tx: Transaction, privateKey: PrivateKey, schema?: SignatureScheme) => {
const hash = tx.getHash();

const signature = await TxSignature.createAsync(hash, privateKey, schema);
const signature = await TxSignature.createAsync(tx, privateKey, schema);

tx.sigs = [signature];
};
Expand All @@ -88,8 +85,7 @@ export const signTransactionAsync = async (tx: Transaction, privateKey: PrivateK
* @param schema Signature Schema to use
*/
export const addSign = (tx: Transaction, privateKey: PrivateKey, schema?: SignatureScheme) => {
const hash = tx.getHash();
const signature = TxSignature.create(hash, privateKey, schema);
const signature = TxSignature.create(tx, privateKey, schema);

tx.sigs.push(signature);
};
Expand Down Expand Up @@ -121,7 +117,7 @@ export const signTx = (tx: Transaction, M: number, pubKeys: PublicKey[],
if (tx.sigs[i].sigData.length + 1 > pubKeys.length) {
throw new Error('Too many sigData');
}
const signData = privateKey.sign(tx.getHash(), scheme).serializeHex();
const signData = privateKey.sign(tx, scheme).serializeHex();
tx.sigs[i].sigData.push(signData);
return;
}
Expand All @@ -130,7 +126,7 @@ export const signTx = (tx: Transaction, M: number, pubKeys: PublicKey[],
const sig = new TxSignature();
sig.M = M;
sig.pubKeys = pubKeys;
sig.sigData = [privateKey.sign(tx.getHash(), scheme).serializeHex()];
sig.sigData = [privateKey.sign(tx, scheme).serializeHex()];
tx.sigs.push(sig);
};

Expand Down Expand Up @@ -160,7 +156,14 @@ export function makeNativeContractTx(
code += pushHexString(str2hexstr(NATIVE_INVOKE_NAME));
const payload = new InvokeCode();
payload.code = code;
const tx = new Transaction();

let tx: Transaction;
if (funcName === 'transfer') {
tx = new Transfer();
} else {
tx = new Transaction();
}

tx.type = TxType.Invoke;
tx.payload = payload;
if (gasLimit) {
Expand Down
26 changes: 26 additions & 0 deletions src/transaction/transfer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2018 The ontology Authors
* This file is part of The ontology library.
*
* The ontology is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ontology is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with The ontology. If not, see <http://www.gnu.org/licenses/>.
*/
import { Address } from '../crypto/address';
import { Transaction } from './transaction';

export class Transfer extends Transaction {
amount: number | string;
tokenType: string;
from: Address;
to: Address;
}
Loading

0 comments on commit 98ca8c7

Please sign in to comment.