UNPKG

7.72 kBJavaScriptView Raw
1"use strict";
2var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3 if (k2 === undefined) k2 = k;
4 Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5}) : (function(o, m, k, k2) {
6 if (k2 === undefined) k2 = k;
7 o[k2] = m[k];
8}));
9var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10 Object.defineProperty(o, "default", { enumerable: true, value: v });
11}) : function(o, v) {
12 o["default"] = v;
13});
14var __importStar = (this && this.__importStar) || function (mod) {
15 if (mod && mod.__esModule) return mod;
16 var result = {};
17 if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18 __setModuleDefault(result, mod);
19 return result;
20};
21Object.defineProperty(exports, "__esModule", { value: true });
22exports.Builder = exports.Flag = exports.Apdu = exports.ApduFlag = void 0;
23const TransportErrors = __importStar(require("./errors"));
24/**
25 * APDU Header Flags
26 *
27 * Describes the APDU Class, Instruction-Type, Parameter 1, and Parameter 2.
28 *
29 * APDU Header: ({ CLA + INS + P1 + P2 })
30 * - CLA: Apdu Class
31 * - INS: Instruction Type
32 * - P1: Instruction Parameter 1
33 * - P2: Instruction Parameter 2
34 *
35 * Instruction Types:
36 * - INS_GET_PUBLIC_KEY: Get a PublicKey from a Ledger Device
37 * - INS_GET_VERSION: Get the SXP Application Version from a Ledger Device
38 * - INS_SIGN_TRANSACTION: Sign a Transaction using a Ledger Device
39 * - INS_SIGN_MESSAGE: Sign a Message using a Ledger Device
40 *
41 * App / PublicKey Context:
42 * P1: User Approval
43 * - P1_NON_CONFIRM: Do NOT request user approval
44 * - P1_CONFIRM: Request user approval
45 *
46 * P2: ChainCode
47 * - P2_NO_CHAINCODE: Don't use a ChainCode
48 * - P2_CHAINCODE: Use a Chaincode
49 *
50 * Signing Context:
51 * P1: Payload Segment
52 * - P1_SINGLE: N(1) where N === 1
53 * - P1_FIRST: N(1) where N > 1
54 * - P1_MORE: N(2)..N-1 where N > 2
55 * - P1_LAST: Nth where N > 1
56 *
57 * P2:
58 * - P2_SCHNORR_LEG: Use Schnorr (bcrypto-v4.1.0) Signatures
59 *
60 */
61var ApduFlag;
62(function (ApduFlag) {
63 /** APDU Class */
64 ApduFlag[ApduFlag["CLA"] = 224] = "CLA";
65 /** App / PublicKey Context */
66 ApduFlag[ApduFlag["INS_GET_PUBLIC_KEY"] = 2] = "INS_GET_PUBLIC_KEY";
67 ApduFlag[ApduFlag["INS_GET_VERSION"] = 6] = "INS_GET_VERSION";
68 ApduFlag[ApduFlag["P1_NON_CONFIRM"] = 0] = "P1_NON_CONFIRM";
69 ApduFlag[ApduFlag["P1_CONFIRM"] = 1] = "P1_CONFIRM";
70 ApduFlag[ApduFlag["P2_NO_CHAINCODE"] = 0] = "P2_NO_CHAINCODE";
71 ApduFlag[ApduFlag["P2_CHAINCODE"] = 1] = "P2_CHAINCODE";
72 /** Signing Context */
73 ApduFlag[ApduFlag["INS_SIGN_TRANSACTION"] = 4] = "INS_SIGN_TRANSACTION";
74 ApduFlag[ApduFlag["INS_SIGN_MESSAGE"] = 8] = "INS_SIGN_MESSAGE";
75 ApduFlag[ApduFlag["P1_SINGLE"] = 128] = "P1_SINGLE";
76 ApduFlag[ApduFlag["P1_FIRST"] = 0] = "P1_FIRST";
77 ApduFlag[ApduFlag["P1_MORE"] = 1] = "P1_MORE";
78 ApduFlag[ApduFlag["P1_LAST"] = 129] = "P1_LAST";
79 ApduFlag[ApduFlag["P2_SCHNORR_LEG"] = 80] = "P2_SCHNORR_LEG";
80})(ApduFlag = exports.ApduFlag || (exports.ApduFlag = {}));
81exports.Flag = ApduFlag;
82/**
83 * Create and manage an Apdu payload instance for sending to a Ledger device.
84 *
85 * @example const response = await new Apdu(CLA, INS, P1, P2, Payload).send(this.transport);
86 */
87class Apdu {
88 /**
89 * Construct an Apdu instance.
90 *
91 * @param {number} cla a class byte
92 * @param {number} ins an instruction byte
93 * @param {number} p1 a parameter-1 byte
94 * @param {number} p2 a parameter-2 byte
95 * @param {Buffer} payload an optional payload
96 * @throws {PayloadLengthError} if the payload is too big to be processed
97 */
98 constructor(cla, ins, p1, p2, payload = Buffer.alloc(0)) {
99 Object.defineProperty(this, "cla", {
100 enumerable: true,
101 configurable: true,
102 writable: true,
103 value: void 0
104 });
105 Object.defineProperty(this, "ins", {
106 enumerable: true,
107 configurable: true,
108 writable: true,
109 value: void 0
110 });
111 Object.defineProperty(this, "p1", {
112 enumerable: true,
113 configurable: true,
114 writable: true,
115 value: void 0
116 });
117 Object.defineProperty(this, "p2", {
118 enumerable: true,
119 configurable: true,
120 writable: true,
121 value: void 0
122 });
123 Object.defineProperty(this, "_payload", {
124 enumerable: true,
125 configurable: true,
126 writable: true,
127 value: void 0
128 });
129 Object.defineProperty(this, "CHUNK_MAX", {
130 enumerable: true,
131 configurable: true,
132 writable: true,
133 value: 10
134 });
135 Object.defineProperty(this, "CHUNK_SIZE", {
136 enumerable: true,
137 configurable: true,
138 writable: true,
139 value: 255
140 });
141 Object.defineProperty(this, "PAYLOAD_MAX", {
142 enumerable: true,
143 configurable: true,
144 writable: true,
145 value: this.CHUNK_MAX * this.CHUNK_SIZE
146 });
147 if (payload && payload.length > this.PAYLOAD_MAX) {
148 throw new TransportErrors.PayloadLengthError(payload.length, this.PAYLOAD_MAX);
149 }
150 this.cla = cla;
151 this.ins = ins;
152 this.p1 = p1;
153 this.p2 = p2;
154 this._payload = payload;
155 }
156 /**
157 * Send a large Apdu payload in chunks for handling by a Ledger device.
158 *
159 * @param {LedgerTransport} transport the transport instance over which the apdu call is sent
160 * @returns {Promise<Buffer>} the apdu response, e.g. from a Ledger device
161 */
162 async send(transport) {
163 const chunks = this.getChunks(this._payload, this.CHUNK_SIZE);
164 const promises = [];
165 let index = 0;
166 for (const chunk of chunks) {
167 promises.push(await transport.send(this.cla, this.ins, this.getChunkSegmentFlag(index, chunks.length), this.p2, chunk));
168 index += 1;
169 }
170 return Buffer.concat(promises.map((r) => r.slice(0, r.length - 2)));
171 }
172 /**
173 * Split the Apdu Payload into a Chunked Array.
174 *
175 * @param {Buffer} payload the bytes to be chunked
176 * @param {number} chunkSize the element size by which to split the payload
177 * @returns {Buffer[]} the chunked payload of an Apdu instance
178 */
179 getChunks(payload, chunkSize) {
180 return this._payload.length <= this.CHUNK_SIZE
181 ? [this._payload]
182 : Array.from({ length: Math.ceil(payload.length / chunkSize) }, (v, i) => payload.slice(i * chunkSize, i * chunkSize + chunkSize));
183 }
184 /**
185 * Get the Segment Flag (P1) of a given chunk.
186 *
187 * @param {Buffer} index the index of the current chunk
188 * @param {Buffer} length total length of the payload
189 * @returns {ApduFlag} which segment of a payload is being sent
190 */
191 getChunkSegmentFlag(index, length) {
192 /** set the payload segment flag */
193 if (index > 0 && index < length - 1) {
194 /** N(2)..N-1 where N > 2 */
195 return ApduFlag.P1_MORE;
196 }
197 else if (index === length - 1 && length > 1) {
198 /** Nth where N > 1 */
199 return ApduFlag.P1_LAST;
200 }
201 else if (index === 0 && length > 1) {
202 /** N(1) where N > 1 */
203 return ApduFlag.P1_FIRST;
204 }
205 else {
206 return this.p1;
207 }
208 }
209}
210exports.Apdu = Apdu;
211exports.Builder = Apdu;
212//# sourceMappingURL=apdu.js.map
\No newline at end of file