From 007384fdb5c233a2500c5a5f4ec3ad7c18d999c7 Mon Sep 17 00:00:00 2001 From: Juan Leni Date: Sun, 17 Nov 2019 15:06:50 +0100 Subject: [PATCH] improving backwards compatibility support --- src/common.js | 1 + src/helperV1.js | 25 +++++++++++++++++++++++++ src/helperV2.js | 17 ++++++++++++++++- src/index.js | 29 +++++++++++++++-------------- tests/basic.ispec.js | 29 ++++++++++++++++++++++++++--- 5 files changed, 83 insertions(+), 18 deletions(-) diff --git a/src/common.js b/src/common.js index f714103..f0b571b 100644 --- a/src/common.js +++ b/src/common.js @@ -4,6 +4,7 @@ export const APP_KEY = "CSM"; export const INS = { GET_VERSION: 0x00, + INS_PUBLIC_KEY_SECP256K1: 0x01, // Obsolete SIGN_SECP256K1: 0x02, GET_ADDR_SECP256K1: 0x04, }; diff --git a/src/helperV1.js b/src/helperV1.js index 5db98b7..c63be98 100644 --- a/src/helperV1.js +++ b/src/helperV1.js @@ -44,3 +44,28 @@ export async function signSendChunkv1(app, chunkIdx, chunkNum, chunk) { }; }, processErrorResponse); } + +function compressPublicKey(publicKey) { + if (publicKey.length !== 65) { + throw new Error("decompressed public key length should be 65 bytes"); + } + const y = publicKey.slice(33, 65); + // eslint-disable-next-line no-bitwise + const z = Buffer.from([2 + (y[y.length - 1] & 1)]); + return Buffer.concat([z, publicKey.slice(1, 33)]); +} + +export async function publicKeyv1(app, data) { + return app.transport.send(CLA, INS.INS_PUBLIC_KEY_SECP256K1, 0, 0, data, [0x9000]).then(response => { + const errorCodeData = response.slice(-2); + const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; + const pk = Buffer.from(response.slice(0, 65)); + + return { + pk, + compressed_pk: compressPublicKey(pk), + return_code: returnCode, + error_message: errorCodeToString(returnCode), + }; + }, processErrorResponse); +} diff --git a/src/helperV2.js b/src/helperV2.js index 9f2e605..7d49526 100644 --- a/src/helperV2.js +++ b/src/helperV2.js @@ -1,5 +1,5 @@ import { signSendChunkv1 } from "./helperV1"; -import { PAYLOAD_TYPE } from "./common"; +import { CLA, errorCodeToString, INS, PAYLOAD_TYPE, processErrorResponse } from "./common"; export function serializePathv2(path) { if (!path || path.length !== 5) { @@ -27,3 +27,18 @@ export async function signSendChunkv2(app, chunkIdx, chunkNum, chunk) { return signSendChunkv1(app, payloadType, 0, chunk); } + +export async function publicKeyv2(app, data) { + return this.transport.send(CLA, INS.GET_ADDR_SECP256K1, 0, 0, data, [0x9000]).then(response => { + const errorCodeData = response.slice(-2); + const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; + const compressedPk = Buffer.from(response.slice(0, 33)); + + return { + pk: "OBSOLETE PROPERTY", + compressed_pk: compressedPk, + return_code: returnCode, + error_message: errorCodeToString(returnCode), + }; + }, processErrorResponse); +} diff --git a/src/index.js b/src/index.js index 2e82a1d..61b2360 100644 --- a/src/index.js +++ b/src/index.js @@ -18,8 +18,8 @@ import crypto from "crypto"; import Ripemd160 from "ripemd160"; import bech32 from "bech32"; -import { serializePathv1, signSendChunkv1 } from "./helperV1"; -import { serializePathv2, signSendChunkv2 } from "./helperV2"; +import { publicKeyv1, serializePathv1, signSendChunkv1 } from "./helperV1"; +import { publicKeyv2, serializePathv2, signSendChunkv2 } from "./helperV2"; import { APP_KEY, CHUNK_SIZE, CLA, INS, errorCodeToString, getVersion, processErrorResponse } from "./common"; export default class CosmosApp { @@ -194,19 +194,20 @@ export default class CosmosApp { async publicKey(path) { const serializedPath = await this.serializePath(path); - const data = Buffer.concat([CosmosApp.serializeHRP("cosmos"), serializedPath]); - return this.transport.send(CLA, INS.GET_ADDR_SECP256K1, 0, 0, data, [0x9000]).then(response => { - const errorCodeData = response.slice(-2); - const returnCode = errorCodeData[0] * 256 + errorCodeData[1]; - const compressedPk = Buffer.from(response.slice(0, 33)); - return { - pk: "OBSOLETE PROPERTY", - compressed_pk: compressedPk, - return_code: returnCode, - error_message: errorCodeToString(returnCode), - }; - }, processErrorResponse); + switch (this.versionResponse.major) { + case 1: + return publicKeyv1(this, serializedPath); + case 2: { + const data = Buffer.concat([CosmosApp.serializeHRP("cosmos"), serializedPath]); + return publicKeyv2(this, data); + } + default: + return { + return_code: 0x6400, + error_message: "App Version is not supported", + }; + } } async getAddressAndPubKey(path, hrp) { diff --git a/tests/basic.ispec.js b/tests/basic.ispec.js index 907693b..89f607c 100644 --- a/tests/basic.ispec.js +++ b/tests/basic.ispec.js @@ -218,7 +218,19 @@ test("sign_big_tx", async () => { expect(responsePk.return_code).toEqual(0x9000); expect(responsePk.error_message).toEqual("No errors"); expect(responseSign.return_code).toEqual(0x6a80); - expect(responseSign.error_message).toEqual("NOMEM: JSON string contains too many tokens"); + + switch (app.versionResponse.major) { + case 1: + expect(responseSign.error_message).toEqual( + "Bad key handle : NOMEM: JSON string contains too many tokens", + ); + break; + case 2: + expect(responseSign.error_message).toEqual("NOMEM: JSON string contains too many tokens"); + break; + default: + expect.fail("Version not supported"); + } }); test("sign_invalid", async () => { @@ -234,6 +246,17 @@ test("sign_invalid", async () => { const responseSign = await app.sign(path, invalidMessage); console.log(responseSign); - expect(responseSign.return_code).toEqual(0x6984); - expect(responseSign.error_message).toEqual("Data is invalid : JSON Missing account number"); + + switch (app.versionResponse.major) { + case 1: + expect(responseSign.return_code).toEqual(0x6a80); + expect(responseSign.error_message).toEqual("Bad key handle : JSON Missing account_number"); + break; + case 2: + expect(responseSign.return_code).toEqual(0x6984); + expect(responseSign.error_message).toEqual("Data is invalid : JSON Missing account number"); + break; + default: + expect.fail("Version not supported"); + } });