@taquito/ledger-signer 17.3.2 → 17.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/coverage/clover.xml +3 -3
- package/coverage/coverage-final.json +2 -2
- package/coverage/lcov-report/errors.ts.html +1 -1
- package/coverage/lcov-report/index.html +1 -1
- package/coverage/lcov-report/taquito-ledger-signer.ts.html +1 -1
- package/coverage/lcov-report/utils.ts.html +1 -1
- package/coverage/lcov-report/version.ts.html +5 -8
- package/coverage/lcov.info +1 -1
- package/dist/lib/errors.js +0 -1
- package/dist/lib/taquito-ledger-signer.js +14 -15
- package/dist/lib/utils.js +0 -1
- package/dist/lib/version.js +2 -3
- package/dist/taquito-ledger-signer.es6.js +350 -348
- package/dist/taquito-ledger-signer.es6.js.map +1 -1
- package/dist/taquito-ledger-signer.umd.js +350 -350
- package/dist/taquito-ledger-signer.umd.js.map +1 -1
- package/dist/types/errors.d.ts +32 -32
- package/dist/types/taquito-ledger-signer.d.ts +75 -75
- package/dist/types/utils.d.ts +43 -43
- package/dist/types/version.d.ts +4 -4
- package/package.json +27 -29
- package/rollup.config.ts +0 -4
- package/signature.json +4 -6
- package/src/version.ts +2 -2
- package/dist/lib/errors.js.map +0 -1
- package/dist/lib/taquito-ledger-signer.js.map +0 -1
- package/dist/lib/utils.js.map +0 -1
- package/dist/lib/version.js.map +0 -1
|
@@ -17,6 +17,8 @@ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
|
17
17
|
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
18
18
|
PERFORMANCE OF THIS SOFTWARE.
|
|
19
19
|
***************************************************************************** */
|
|
20
|
+
/* global Reflect, Promise, SuppressedError, Symbol */
|
|
21
|
+
|
|
20
22
|
|
|
21
23
|
function __awaiter(thisArg, _arguments, P, generator) {
|
|
22
24
|
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
@@ -33,360 +35,360 @@ typeof SuppressedError === "function" ? SuppressedError : function (error, suppr
|
|
|
33
35
|
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
34
36
|
};
|
|
35
37
|
|
|
36
|
-
/*
|
|
37
|
-
* Some code in this file is adapted from sotez
|
|
38
|
-
* Copyright (c) 2018 Andrew Kishino
|
|
39
|
-
*/
|
|
40
|
-
const MAX_CHUNK_SIZE = 230;
|
|
41
|
-
/**
|
|
42
|
-
*
|
|
43
|
-
* @description Convert the path to a buffer that will be used as LC and CDATA in the APDU send to the ledger device (https://github.com/obsidiansystems/ledger-app-tezos/blob/master/APDUs.md)
|
|
44
|
-
*
|
|
45
|
-
* @param path The ledger derivation path (default is "44'/1729'/0'/0'")
|
|
46
|
-
* @returns A buffer where the first element is the length of the path (default is 4), then 3 bytes for each number of the path to which is added 0x8000000
|
|
47
|
-
*/
|
|
48
|
-
function transformPathToBuffer(path) {
|
|
49
|
-
const result = [];
|
|
50
|
-
const components = path.split('/');
|
|
51
|
-
components.forEach((element) => {
|
|
52
|
-
let toNumber = parseInt(element, 10);
|
|
53
|
-
if (Number.isNaN(toNumber)) {
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
if (element.length > 1 && element[element.length - 1] === "'") {
|
|
57
|
-
toNumber += 0x80000000;
|
|
58
|
-
}
|
|
59
|
-
result.push(toNumber);
|
|
60
|
-
});
|
|
61
|
-
const buffer = Buffer.alloc(1 + result.length * 4);
|
|
62
|
-
buffer[0] = result.length;
|
|
63
|
-
result.forEach((element, index) => {
|
|
64
|
-
buffer.writeUInt32BE(element, 1 + 4 * index);
|
|
65
|
-
});
|
|
66
|
-
return buffer;
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
*
|
|
70
|
-
* @description Converts uncompressed ledger key to standard tezos binary representation
|
|
71
|
-
*/
|
|
72
|
-
function compressPublicKey(publicKey, curve) {
|
|
73
|
-
if (curve === 0x00 || curve === 0x03) {
|
|
74
|
-
publicKey = publicKey.slice(1);
|
|
75
|
-
}
|
|
76
|
-
else {
|
|
77
|
-
publicKey[0] = 0x02 + (publicKey[64] & 0x01);
|
|
78
|
-
publicKey = publicKey.slice(0, 33);
|
|
79
|
-
}
|
|
80
|
-
return publicKey;
|
|
81
|
-
}
|
|
82
|
-
function appendWatermark(bytes, watermark) {
|
|
83
|
-
let transactionHex = bytes;
|
|
84
|
-
if (typeof watermark !== 'undefined') {
|
|
85
|
-
const hexWatermark = Buffer.from(watermark).toString('hex');
|
|
86
|
-
transactionHex = hexWatermark.concat(bytes);
|
|
87
|
-
}
|
|
88
|
-
return transactionHex;
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
*
|
|
92
|
-
* @description In order not to exceed the data length allowed by the Ledger device, split the operation into buffers of 230 bytes (max) and add them to the message to send to the Ledger
|
|
93
|
-
* @param messageToSend The message to send to the Ledger device
|
|
94
|
-
* @param operation The operation which will be chunk if its length is over 230 bytes
|
|
95
|
-
* @returns The instruction to send to the Ledger device
|
|
96
|
-
*/
|
|
97
|
-
function chunkOperation(messageToSend, operation) {
|
|
98
|
-
let offset = 0;
|
|
99
|
-
while (offset !== operation.length) {
|
|
100
|
-
const chunkSize = offset + MAX_CHUNK_SIZE >= operation.length ? operation.length - offset : MAX_CHUNK_SIZE;
|
|
101
|
-
const buff = Buffer.alloc(chunkSize);
|
|
102
|
-
operation.copy(buff, 0, offset, offset + chunkSize);
|
|
103
|
-
messageToSend.push(buff);
|
|
104
|
-
offset += chunkSize;
|
|
105
|
-
}
|
|
106
|
-
return messageToSend;
|
|
107
|
-
}
|
|
108
|
-
/**
|
|
109
|
-
*
|
|
110
|
-
* @description Verify if the signature returned by the ledger for tz2 and tz3 is valid
|
|
111
|
-
* @param response The signature returned by the Ledger (return from the signWithLedger function)
|
|
112
|
-
* @returns True if valid, false otherwise
|
|
113
|
-
*/
|
|
114
|
-
function validateResponse(response) {
|
|
115
|
-
let valid = true;
|
|
116
|
-
if (response[0] !== 0x31 && response[0] !== 0x30) {
|
|
117
|
-
valid = false;
|
|
118
|
-
}
|
|
119
|
-
if (response[1] + 4 !== response.length) {
|
|
120
|
-
valid = false;
|
|
121
|
-
}
|
|
122
|
-
if (response[2] !== 0x02) {
|
|
123
|
-
valid = false;
|
|
124
|
-
}
|
|
125
|
-
const rLength = response[3];
|
|
126
|
-
if (response[4 + rLength] !== 0x02) {
|
|
127
|
-
valid = false;
|
|
128
|
-
}
|
|
129
|
-
const idxLengthSVal = 5 + rLength;
|
|
130
|
-
const sLength = response[idxLengthSVal];
|
|
131
|
-
if (idxLengthSVal + 1 + sLength + 2 !== response.length) {
|
|
132
|
-
valid = false;
|
|
133
|
-
}
|
|
134
|
-
return valid;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
*
|
|
138
|
-
* @description Extract a part of the response returned by the Ledger
|
|
139
|
-
* @param idxLength The index in the response from the Ledger that corresponds to the length of the part to extract
|
|
140
|
-
* @param response The signature returned by the Ledger (return from the signWithLedger function)
|
|
141
|
-
* @returns An object that contains the extracted buffer, the index where it starts in the response and the length of the extracted part
|
|
142
|
-
*/
|
|
143
|
-
function extractValue(idxLength, response) {
|
|
144
|
-
const buffer = Buffer.alloc(32);
|
|
145
|
-
buffer.fill(0);
|
|
146
|
-
let length = response[idxLength];
|
|
147
|
-
let idxValueStart = idxLength + 1;
|
|
148
|
-
if (length > 32) {
|
|
149
|
-
idxValueStart += length - 32;
|
|
150
|
-
length = 32;
|
|
151
|
-
}
|
|
152
|
-
response.copy(buffer, 32 - length, idxValueStart, idxValueStart + length);
|
|
153
|
-
return { buffer, idxValueStart, length };
|
|
38
|
+
/*
|
|
39
|
+
* Some code in this file is adapted from sotez
|
|
40
|
+
* Copyright (c) 2018 Andrew Kishino
|
|
41
|
+
*/
|
|
42
|
+
const MAX_CHUNK_SIZE = 230;
|
|
43
|
+
/**
|
|
44
|
+
*
|
|
45
|
+
* @description Convert the path to a buffer that will be used as LC and CDATA in the APDU send to the ledger device (https://github.com/obsidiansystems/ledger-app-tezos/blob/master/APDUs.md)
|
|
46
|
+
*
|
|
47
|
+
* @param path The ledger derivation path (default is "44'/1729'/0'/0'")
|
|
48
|
+
* @returns A buffer where the first element is the length of the path (default is 4), then 3 bytes for each number of the path to which is added 0x8000000
|
|
49
|
+
*/
|
|
50
|
+
function transformPathToBuffer(path) {
|
|
51
|
+
const result = [];
|
|
52
|
+
const components = path.split('/');
|
|
53
|
+
components.forEach((element) => {
|
|
54
|
+
let toNumber = parseInt(element, 10);
|
|
55
|
+
if (Number.isNaN(toNumber)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (element.length > 1 && element[element.length - 1] === "'") {
|
|
59
|
+
toNumber += 0x80000000;
|
|
60
|
+
}
|
|
61
|
+
result.push(toNumber);
|
|
62
|
+
});
|
|
63
|
+
const buffer = Buffer.alloc(1 + result.length * 4);
|
|
64
|
+
buffer[0] = result.length;
|
|
65
|
+
result.forEach((element, index) => {
|
|
66
|
+
buffer.writeUInt32BE(element, 1 + 4 * index);
|
|
67
|
+
});
|
|
68
|
+
return buffer;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
*
|
|
72
|
+
* @description Converts uncompressed ledger key to standard tezos binary representation
|
|
73
|
+
*/
|
|
74
|
+
function compressPublicKey(publicKey, curve) {
|
|
75
|
+
if (curve === 0x00 || curve === 0x03) {
|
|
76
|
+
publicKey = publicKey.slice(1);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
publicKey[0] = 0x02 + (publicKey[64] & 0x01);
|
|
80
|
+
publicKey = publicKey.slice(0, 33);
|
|
81
|
+
}
|
|
82
|
+
return publicKey;
|
|
83
|
+
}
|
|
84
|
+
function appendWatermark(bytes, watermark) {
|
|
85
|
+
let transactionHex = bytes;
|
|
86
|
+
if (typeof watermark !== 'undefined') {
|
|
87
|
+
const hexWatermark = Buffer.from(watermark).toString('hex');
|
|
88
|
+
transactionHex = hexWatermark.concat(bytes);
|
|
89
|
+
}
|
|
90
|
+
return transactionHex;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
*
|
|
94
|
+
* @description In order not to exceed the data length allowed by the Ledger device, split the operation into buffers of 230 bytes (max) and add them to the message to send to the Ledger
|
|
95
|
+
* @param messageToSend The message to send to the Ledger device
|
|
96
|
+
* @param operation The operation which will be chunk if its length is over 230 bytes
|
|
97
|
+
* @returns The instruction to send to the Ledger device
|
|
98
|
+
*/
|
|
99
|
+
function chunkOperation(messageToSend, operation) {
|
|
100
|
+
let offset = 0;
|
|
101
|
+
while (offset !== operation.length) {
|
|
102
|
+
const chunkSize = offset + MAX_CHUNK_SIZE >= operation.length ? operation.length - offset : MAX_CHUNK_SIZE;
|
|
103
|
+
const buff = Buffer.alloc(chunkSize);
|
|
104
|
+
operation.copy(buff, 0, offset, offset + chunkSize);
|
|
105
|
+
messageToSend.push(buff);
|
|
106
|
+
offset += chunkSize;
|
|
107
|
+
}
|
|
108
|
+
return messageToSend;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
*
|
|
112
|
+
* @description Verify if the signature returned by the ledger for tz2 and tz3 is valid
|
|
113
|
+
* @param response The signature returned by the Ledger (return from the signWithLedger function)
|
|
114
|
+
* @returns True if valid, false otherwise
|
|
115
|
+
*/
|
|
116
|
+
function validateResponse(response) {
|
|
117
|
+
let valid = true;
|
|
118
|
+
if (response[0] !== 0x31 && response[0] !== 0x30) {
|
|
119
|
+
valid = false;
|
|
120
|
+
}
|
|
121
|
+
if (response[1] + 4 !== response.length) {
|
|
122
|
+
valid = false;
|
|
123
|
+
}
|
|
124
|
+
if (response[2] !== 0x02) {
|
|
125
|
+
valid = false;
|
|
126
|
+
}
|
|
127
|
+
const rLength = response[3];
|
|
128
|
+
if (response[4 + rLength] !== 0x02) {
|
|
129
|
+
valid = false;
|
|
130
|
+
}
|
|
131
|
+
const idxLengthSVal = 5 + rLength;
|
|
132
|
+
const sLength = response[idxLengthSVal];
|
|
133
|
+
if (idxLengthSVal + 1 + sLength + 2 !== response.length) {
|
|
134
|
+
valid = false;
|
|
135
|
+
}
|
|
136
|
+
return valid;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
*
|
|
140
|
+
* @description Extract a part of the response returned by the Ledger
|
|
141
|
+
* @param idxLength The index in the response from the Ledger that corresponds to the length of the part to extract
|
|
142
|
+
* @param response The signature returned by the Ledger (return from the signWithLedger function)
|
|
143
|
+
* @returns An object that contains the extracted buffer, the index where it starts in the response and the length of the extracted part
|
|
144
|
+
*/
|
|
145
|
+
function extractValue(idxLength, response) {
|
|
146
|
+
const buffer = Buffer.alloc(32);
|
|
147
|
+
buffer.fill(0);
|
|
148
|
+
let length = response[idxLength];
|
|
149
|
+
let idxValueStart = idxLength + 1;
|
|
150
|
+
if (length > 32) {
|
|
151
|
+
idxValueStart += length - 32;
|
|
152
|
+
length = 32;
|
|
153
|
+
}
|
|
154
|
+
response.copy(buffer, 32 - length, idxValueStart, idxValueStart + length);
|
|
155
|
+
return { buffer, idxValueStart, length };
|
|
154
156
|
}
|
|
155
157
|
|
|
156
|
-
/**
|
|
157
|
-
* @category Error
|
|
158
|
-
* @description Error that indicates an invalid or unparseable ledger response
|
|
159
|
-
*/
|
|
160
|
-
class InvalidLedgerResponseError extends TaquitoError {
|
|
161
|
-
constructor(message) {
|
|
162
|
-
super();
|
|
163
|
-
this.message = message;
|
|
164
|
-
this.name = 'InvalidLedgerResponseError';
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* @category Error
|
|
169
|
-
* @description Error that indicates a failure when trying to retrieve a Public Key from Ledger signer
|
|
170
|
-
*/
|
|
171
|
-
class PublicKeyRetrievalError extends TaquitoError {
|
|
172
|
-
constructor(cause) {
|
|
173
|
-
super();
|
|
174
|
-
this.cause = cause;
|
|
175
|
-
this.name = 'PublicKeyRetrievalError';
|
|
176
|
-
this.message = `Unable to retrieve Public Key from Ledger`;
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
/**
|
|
180
|
-
* @category Error
|
|
181
|
-
* @description Error that indicates a failure when trying to retrieve a Public Key Hash from Ledger signer
|
|
182
|
-
*/
|
|
183
|
-
class PublicKeyHashRetrievalError extends TaquitoError {
|
|
184
|
-
constructor() {
|
|
185
|
-
super();
|
|
186
|
-
this.name = 'PublicKeyHashRetrievalError';
|
|
187
|
-
this.message = 'Unable to retrieve Public Key Hash from Ledger';
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* @category Error
|
|
192
|
-
* @description Error that indicates an invalid derivation type being passed or used
|
|
193
|
-
*/
|
|
194
|
-
class InvalidDerivationTypeError extends ParameterValidationError {
|
|
195
|
-
constructor(derivationType) {
|
|
196
|
-
super();
|
|
197
|
-
this.derivationType = derivationType;
|
|
198
|
-
this.name = 'InvalidDerivationTypeError';
|
|
199
|
-
this.message = `Invalid derivation type ${derivationType} expecting one of the following: DerivationType.ED25519, DerivationType.SECP256K1, DerivationType.P256 or DerivationType.BIP32_ED25519`;
|
|
200
|
-
}
|
|
158
|
+
/**
|
|
159
|
+
* @category Error
|
|
160
|
+
* @description Error that indicates an invalid or unparseable ledger response
|
|
161
|
+
*/
|
|
162
|
+
class InvalidLedgerResponseError extends TaquitoError {
|
|
163
|
+
constructor(message) {
|
|
164
|
+
super();
|
|
165
|
+
this.message = message;
|
|
166
|
+
this.name = 'InvalidLedgerResponseError';
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* @category Error
|
|
171
|
+
* @description Error that indicates a failure when trying to retrieve a Public Key from Ledger signer
|
|
172
|
+
*/
|
|
173
|
+
class PublicKeyRetrievalError extends TaquitoError {
|
|
174
|
+
constructor(cause) {
|
|
175
|
+
super();
|
|
176
|
+
this.cause = cause;
|
|
177
|
+
this.name = 'PublicKeyRetrievalError';
|
|
178
|
+
this.message = `Unable to retrieve Public Key from Ledger`;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* @category Error
|
|
183
|
+
* @description Error that indicates a failure when trying to retrieve a Public Key Hash from Ledger signer
|
|
184
|
+
*/
|
|
185
|
+
class PublicKeyHashRetrievalError extends TaquitoError {
|
|
186
|
+
constructor() {
|
|
187
|
+
super();
|
|
188
|
+
this.name = 'PublicKeyHashRetrievalError';
|
|
189
|
+
this.message = 'Unable to retrieve Public Key Hash from Ledger';
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* @category Error
|
|
194
|
+
* @description Error that indicates an invalid derivation type being passed or used
|
|
195
|
+
*/
|
|
196
|
+
class InvalidDerivationTypeError extends ParameterValidationError {
|
|
197
|
+
constructor(derivationType) {
|
|
198
|
+
super();
|
|
199
|
+
this.derivationType = derivationType;
|
|
200
|
+
this.name = 'InvalidDerivationTypeError';
|
|
201
|
+
this.message = `Invalid derivation type ${derivationType} expecting one of the following: DerivationType.ED25519, DerivationType.SECP256K1, DerivationType.P256 or DerivationType.BIP32_ED25519`;
|
|
202
|
+
}
|
|
201
203
|
}
|
|
202
204
|
|
|
203
|
-
// IMPORTANT: THIS FILE IS AUTO GENERATED! DO NOT MANUALLY EDIT OR CHECKIN!
|
|
204
|
-
const VERSION = {
|
|
205
|
-
"commitHash": "
|
|
206
|
-
"version": "17.
|
|
205
|
+
// IMPORTANT: THIS FILE IS AUTO GENERATED! DO NOT MANUALLY EDIT OR CHECKIN!
|
|
206
|
+
const VERSION = {
|
|
207
|
+
"commitHash": "a908ab176a8c52c025fd43e7acd452415396f54e",
|
|
208
|
+
"version": "17.4.0"
|
|
207
209
|
};
|
|
208
210
|
|
|
209
|
-
/**
|
|
210
|
-
* @packageDocumentation
|
|
211
|
-
* @module @taquito/ledger-signer
|
|
212
|
-
*/
|
|
213
|
-
var DerivationType;
|
|
214
|
-
(function (DerivationType) {
|
|
215
|
-
DerivationType[DerivationType["ED25519"] = 0] = "ED25519";
|
|
216
|
-
DerivationType[DerivationType["SECP256K1"] = 1] = "SECP256K1";
|
|
217
|
-
DerivationType[DerivationType["P256"] = 2] = "P256";
|
|
218
|
-
DerivationType[DerivationType["BIP32_ED25519"] = 3] = "BIP32_ED25519";
|
|
219
|
-
})(DerivationType || (DerivationType = {}));
|
|
220
|
-
const HDPathTemplate = (account) => {
|
|
221
|
-
return `44'/1729'/${account}'/0'`;
|
|
222
|
-
};
|
|
223
|
-
/**
|
|
224
|
-
*
|
|
225
|
-
* @description Implementation of the Signer interface that will allow signing operation from a Ledger Nano device
|
|
226
|
-
*
|
|
227
|
-
* @param transport A transport instance from LedgerJS libraries depending on the platform used (e.g. Web, Node)
|
|
228
|
-
* @param path The ledger derivation path (default is "44'/1729'/0'/0'")
|
|
229
|
-
* @param prompt Whether to prompt the ledger for public key (default is true)
|
|
230
|
-
* @param derivationType The value which defines the curve to use (DerivationType.ED25519(default), DerivationType.SECP256K1, DerivationType.P256, DerivationType.BIP32_ED25519)
|
|
231
|
-
*
|
|
232
|
-
* @example
|
|
233
|
-
* ```
|
|
234
|
-
* import TransportNodeHid from "@ledgerhq/hw-transport-node-hid";
|
|
235
|
-
* const transport = await TransportNodeHid.create();
|
|
236
|
-
* const ledgerSigner = new LedgerSigner(transport, "44'/1729'/0'/0'", false, DerivationType.ED25519);
|
|
237
|
-
* ```
|
|
238
|
-
*
|
|
239
|
-
* @example
|
|
240
|
-
* ```
|
|
241
|
-
* import TransportU2F from "@ledgerhq/hw-transport-u2f";
|
|
242
|
-
* const transport = await TransportU2F.create();
|
|
243
|
-
* const ledgerSigner = new LedgerSigner(transport, "44'/1729'/0'/0'", true, DerivationType.SECP256K1);
|
|
244
|
-
* ```
|
|
245
|
-
*
|
|
246
|
-
* @example
|
|
247
|
-
* ```
|
|
248
|
-
* import TransportU2F from "@ledgerhq/hw-transport-u2f";
|
|
249
|
-
* const transport = await TransportU2F.create();
|
|
250
|
-
* const ledgerSigner = new LedgerSigner(transport, "44'/1729'/6'/0'", true, DerivationType.BIP32_ED25519);
|
|
251
|
-
* ```
|
|
252
|
-
*/
|
|
253
|
-
class LedgerSigner {
|
|
254
|
-
constructor(transport, path = "44'/1729'/0'/0'", prompt = true, derivationType = DerivationType.ED25519) {
|
|
255
|
-
this.transport = transport;
|
|
256
|
-
this.path = path;
|
|
257
|
-
this.prompt = prompt;
|
|
258
|
-
this.derivationType = derivationType;
|
|
259
|
-
// constants for APDU requests (https://github.com/obsidiansystems/ledger-app-tezos/blob/master/APDUs.md)
|
|
260
|
-
this.CLA = 0x80; // Instruction class (always 0x80)
|
|
261
|
-
this.INS_GET_PUBLIC_KEY = 0x02; // Instruction code to get the ledger’s internal public key without prompt
|
|
262
|
-
this.INS_PROMPT_PUBLIC_KEY = 0x03; // Instruction code to get the ledger’s internal public key with prompt
|
|
263
|
-
this.INS_SIGN = 0x04; // Sign a message with the ledger’s key
|
|
264
|
-
this.FIRST_MESSAGE_SEQUENCE = 0x00;
|
|
265
|
-
this.LAST_MESSAGE_SEQUENCE = 0x81;
|
|
266
|
-
this.OTHER_MESSAGE_SEQUENCE = 0x01;
|
|
267
|
-
this.transport.setScrambleKey('XTZ');
|
|
268
|
-
if (!path.startsWith(`44'/1729'`)) {
|
|
269
|
-
throw new InvalidDerivationPathError(path, `${invalidDetail(ValidationResult.NO_PREFIX_MATCHED)} expecting prefix "44'/1729'".`);
|
|
270
|
-
}
|
|
271
|
-
if (!Object.values(DerivationType).includes(derivationType)) {
|
|
272
|
-
throw new InvalidDerivationTypeError(derivationType.toString());
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
publicKeyHash() {
|
|
276
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
277
|
-
if (!this._publicKeyHash) {
|
|
278
|
-
yield this.publicKey();
|
|
279
|
-
}
|
|
280
|
-
if (this._publicKeyHash) {
|
|
281
|
-
return this._publicKeyHash;
|
|
282
|
-
}
|
|
283
|
-
throw new PublicKeyHashRetrievalError();
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
publicKey() {
|
|
287
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
288
|
-
if (this._publicKey) {
|
|
289
|
-
return this._publicKey;
|
|
290
|
-
}
|
|
291
|
-
const responseLedger = yield this.getLedgerPublicKey();
|
|
292
|
-
const publicKeyLength = responseLedger[0];
|
|
293
|
-
const rawPublicKey = responseLedger.slice(1, 1 + publicKeyLength);
|
|
294
|
-
const compressedPublicKey = compressPublicKey(rawPublicKey, this.derivationType);
|
|
295
|
-
const prefixes = this.getPrefixes();
|
|
296
|
-
const publicKey = b58cencode(compressedPublicKey, prefixes.prefPk);
|
|
297
|
-
const publicKeyHash = b58cencode(hash(compressedPublicKey, 20), prefixes.prefPkh);
|
|
298
|
-
this._publicKey = publicKey;
|
|
299
|
-
this._publicKeyHash = publicKeyHash;
|
|
300
|
-
return publicKey;
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
getLedgerPublicKey() {
|
|
304
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
305
|
-
try {
|
|
306
|
-
let ins = this.INS_PROMPT_PUBLIC_KEY;
|
|
307
|
-
if (this.prompt === false) {
|
|
308
|
-
ins = this.INS_GET_PUBLIC_KEY;
|
|
309
|
-
}
|
|
310
|
-
const responseLedger = yield this.transport.send(this.CLA, ins, this.FIRST_MESSAGE_SEQUENCE, this.derivationType, transformPathToBuffer(this.path));
|
|
311
|
-
return responseLedger;
|
|
312
|
-
}
|
|
313
|
-
catch (error) {
|
|
314
|
-
throw new PublicKeyRetrievalError(error);
|
|
315
|
-
}
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
secretKey() {
|
|
319
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
320
|
-
throw new ProhibitedActionError('Secret key cannot be exposed');
|
|
321
|
-
});
|
|
322
|
-
}
|
|
323
|
-
sign(bytes, watermark) {
|
|
324
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
325
|
-
const watermarkedBytes = appendWatermark(bytes, watermark);
|
|
326
|
-
const watermarkedBytes2buff = Buffer.from(watermarkedBytes, 'hex');
|
|
327
|
-
let messageToSend = [];
|
|
328
|
-
messageToSend.push(transformPathToBuffer(this.path));
|
|
329
|
-
messageToSend = chunkOperation(messageToSend, watermarkedBytes2buff);
|
|
330
|
-
const ledgerResponse = yield this.signWithLedger(messageToSend);
|
|
331
|
-
let signature;
|
|
332
|
-
if (this.derivationType === DerivationType.ED25519 ||
|
|
333
|
-
this.derivationType === DerivationType.BIP32_ED25519) {
|
|
334
|
-
signature = ledgerResponse.slice(0, ledgerResponse.length - 2).toString('hex');
|
|
335
|
-
}
|
|
336
|
-
else {
|
|
337
|
-
if (!validateResponse(ledgerResponse)) {
|
|
338
|
-
throw new InvalidLedgerResponseError('Invalid signature return by ledger unable to parse the response');
|
|
339
|
-
}
|
|
340
|
-
const idxLengthRVal = 3; // Third element of response is length of r value
|
|
341
|
-
const rValue = extractValue(idxLengthRVal, ledgerResponse);
|
|
342
|
-
const idxLengthSVal = rValue.idxValueStart + rValue.length + 1;
|
|
343
|
-
const sValue = extractValue(idxLengthSVal, ledgerResponse);
|
|
344
|
-
const signatureBuffer = Buffer.concat([rValue.buffer, sValue.buffer]);
|
|
345
|
-
signature = signatureBuffer.toString('hex');
|
|
346
|
-
}
|
|
347
|
-
return {
|
|
348
|
-
bytes,
|
|
349
|
-
sig: b58cencode(signature, prefix[Prefix.SIG]),
|
|
350
|
-
prefixSig: b58cencode(signature, this.getPrefixes().prefSig),
|
|
351
|
-
sbytes: bytes + signature,
|
|
352
|
-
};
|
|
353
|
-
});
|
|
354
|
-
}
|
|
355
|
-
signWithLedger(message) {
|
|
356
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
357
|
-
// first element of the message represents the path
|
|
358
|
-
let ledgerResponse = yield this.transport.send(this.CLA, this.INS_SIGN, this.FIRST_MESSAGE_SEQUENCE, this.derivationType, message[0]);
|
|
359
|
-
for (let i = 1; i < message.length; i++) {
|
|
360
|
-
const p1 = i === message.length - 1 ? this.LAST_MESSAGE_SEQUENCE : this.OTHER_MESSAGE_SEQUENCE;
|
|
361
|
-
ledgerResponse = yield this.transport.send(this.CLA, this.INS_SIGN, p1, this.derivationType, message[i]);
|
|
362
|
-
}
|
|
363
|
-
return ledgerResponse;
|
|
364
|
-
});
|
|
365
|
-
}
|
|
366
|
-
getPrefixes() {
|
|
367
|
-
if (this.derivationType === DerivationType.ED25519 ||
|
|
368
|
-
this.derivationType === DerivationType.BIP32_ED25519) {
|
|
369
|
-
return {
|
|
370
|
-
prefPk: prefix[Prefix.EDPK],
|
|
371
|
-
prefPkh: prefix[Prefix.TZ1],
|
|
372
|
-
prefSig: prefix[Prefix.EDSIG],
|
|
373
|
-
};
|
|
374
|
-
}
|
|
375
|
-
else if (this.derivationType === DerivationType.SECP256K1) {
|
|
376
|
-
return {
|
|
377
|
-
prefPk: prefix[Prefix.SPPK],
|
|
378
|
-
prefPkh: prefix[Prefix.TZ2],
|
|
379
|
-
prefSig: prefix[Prefix.SPSIG],
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
else {
|
|
383
|
-
return {
|
|
384
|
-
prefPk: prefix[Prefix.P2PK],
|
|
385
|
-
prefPkh: prefix[Prefix.TZ3],
|
|
386
|
-
prefSig: prefix[Prefix.P2SIG],
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
}
|
|
211
|
+
/**
|
|
212
|
+
* @packageDocumentation
|
|
213
|
+
* @module @taquito/ledger-signer
|
|
214
|
+
*/
|
|
215
|
+
var DerivationType;
|
|
216
|
+
(function (DerivationType) {
|
|
217
|
+
DerivationType[DerivationType["ED25519"] = 0] = "ED25519";
|
|
218
|
+
DerivationType[DerivationType["SECP256K1"] = 1] = "SECP256K1";
|
|
219
|
+
DerivationType[DerivationType["P256"] = 2] = "P256";
|
|
220
|
+
DerivationType[DerivationType["BIP32_ED25519"] = 3] = "BIP32_ED25519";
|
|
221
|
+
})(DerivationType || (DerivationType = {}));
|
|
222
|
+
const HDPathTemplate = (account) => {
|
|
223
|
+
return `44'/1729'/${account}'/0'`;
|
|
224
|
+
};
|
|
225
|
+
/**
|
|
226
|
+
*
|
|
227
|
+
* @description Implementation of the Signer interface that will allow signing operation from a Ledger Nano device
|
|
228
|
+
*
|
|
229
|
+
* @param transport A transport instance from LedgerJS libraries depending on the platform used (e.g. Web, Node)
|
|
230
|
+
* @param path The ledger derivation path (default is "44'/1729'/0'/0'")
|
|
231
|
+
* @param prompt Whether to prompt the ledger for public key (default is true)
|
|
232
|
+
* @param derivationType The value which defines the curve to use (DerivationType.ED25519(default), DerivationType.SECP256K1, DerivationType.P256, DerivationType.BIP32_ED25519)
|
|
233
|
+
*
|
|
234
|
+
* @example
|
|
235
|
+
* ```
|
|
236
|
+
* import TransportNodeHid from "@ledgerhq/hw-transport-node-hid";
|
|
237
|
+
* const transport = await TransportNodeHid.create();
|
|
238
|
+
* const ledgerSigner = new LedgerSigner(transport, "44'/1729'/0'/0'", false, DerivationType.ED25519);
|
|
239
|
+
* ```
|
|
240
|
+
*
|
|
241
|
+
* @example
|
|
242
|
+
* ```
|
|
243
|
+
* import TransportU2F from "@ledgerhq/hw-transport-u2f";
|
|
244
|
+
* const transport = await TransportU2F.create();
|
|
245
|
+
* const ledgerSigner = new LedgerSigner(transport, "44'/1729'/0'/0'", true, DerivationType.SECP256K1);
|
|
246
|
+
* ```
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* ```
|
|
250
|
+
* import TransportU2F from "@ledgerhq/hw-transport-u2f";
|
|
251
|
+
* const transport = await TransportU2F.create();
|
|
252
|
+
* const ledgerSigner = new LedgerSigner(transport, "44'/1729'/6'/0'", true, DerivationType.BIP32_ED25519);
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
255
|
+
class LedgerSigner {
|
|
256
|
+
constructor(transport, path = "44'/1729'/0'/0'", prompt = true, derivationType = DerivationType.ED25519) {
|
|
257
|
+
this.transport = transport;
|
|
258
|
+
this.path = path;
|
|
259
|
+
this.prompt = prompt;
|
|
260
|
+
this.derivationType = derivationType;
|
|
261
|
+
// constants for APDU requests (https://github.com/obsidiansystems/ledger-app-tezos/blob/master/APDUs.md)
|
|
262
|
+
this.CLA = 0x80; // Instruction class (always 0x80)
|
|
263
|
+
this.INS_GET_PUBLIC_KEY = 0x02; // Instruction code to get the ledger’s internal public key without prompt
|
|
264
|
+
this.INS_PROMPT_PUBLIC_KEY = 0x03; // Instruction code to get the ledger’s internal public key with prompt
|
|
265
|
+
this.INS_SIGN = 0x04; // Sign a message with the ledger’s key
|
|
266
|
+
this.FIRST_MESSAGE_SEQUENCE = 0x00;
|
|
267
|
+
this.LAST_MESSAGE_SEQUENCE = 0x81;
|
|
268
|
+
this.OTHER_MESSAGE_SEQUENCE = 0x01;
|
|
269
|
+
this.transport.setScrambleKey('XTZ');
|
|
270
|
+
if (!path.startsWith(`44'/1729'`)) {
|
|
271
|
+
throw new InvalidDerivationPathError(path, `${invalidDetail(ValidationResult.NO_PREFIX_MATCHED)} expecting prefix "44'/1729'".`);
|
|
272
|
+
}
|
|
273
|
+
if (!Object.values(DerivationType).includes(derivationType)) {
|
|
274
|
+
throw new InvalidDerivationTypeError(derivationType.toString());
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
publicKeyHash() {
|
|
278
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
279
|
+
if (!this._publicKeyHash) {
|
|
280
|
+
yield this.publicKey();
|
|
281
|
+
}
|
|
282
|
+
if (this._publicKeyHash) {
|
|
283
|
+
return this._publicKeyHash;
|
|
284
|
+
}
|
|
285
|
+
throw new PublicKeyHashRetrievalError();
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
publicKey() {
|
|
289
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
290
|
+
if (this._publicKey) {
|
|
291
|
+
return this._publicKey;
|
|
292
|
+
}
|
|
293
|
+
const responseLedger = yield this.getLedgerPublicKey();
|
|
294
|
+
const publicKeyLength = responseLedger[0];
|
|
295
|
+
const rawPublicKey = responseLedger.slice(1, 1 + publicKeyLength);
|
|
296
|
+
const compressedPublicKey = compressPublicKey(rawPublicKey, this.derivationType);
|
|
297
|
+
const prefixes = this.getPrefixes();
|
|
298
|
+
const publicKey = b58cencode(compressedPublicKey, prefixes.prefPk);
|
|
299
|
+
const publicKeyHash = b58cencode(hash(compressedPublicKey, 20), prefixes.prefPkh);
|
|
300
|
+
this._publicKey = publicKey;
|
|
301
|
+
this._publicKeyHash = publicKeyHash;
|
|
302
|
+
return publicKey;
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
getLedgerPublicKey() {
|
|
306
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
307
|
+
try {
|
|
308
|
+
let ins = this.INS_PROMPT_PUBLIC_KEY;
|
|
309
|
+
if (this.prompt === false) {
|
|
310
|
+
ins = this.INS_GET_PUBLIC_KEY;
|
|
311
|
+
}
|
|
312
|
+
const responseLedger = yield this.transport.send(this.CLA, ins, this.FIRST_MESSAGE_SEQUENCE, this.derivationType, transformPathToBuffer(this.path));
|
|
313
|
+
return responseLedger;
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
throw new PublicKeyRetrievalError(error);
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
secretKey() {
|
|
321
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
322
|
+
throw new ProhibitedActionError('Secret key cannot be exposed');
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
sign(bytes, watermark) {
|
|
326
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
327
|
+
const watermarkedBytes = appendWatermark(bytes, watermark);
|
|
328
|
+
const watermarkedBytes2buff = Buffer.from(watermarkedBytes, 'hex');
|
|
329
|
+
let messageToSend = [];
|
|
330
|
+
messageToSend.push(transformPathToBuffer(this.path));
|
|
331
|
+
messageToSend = chunkOperation(messageToSend, watermarkedBytes2buff);
|
|
332
|
+
const ledgerResponse = yield this.signWithLedger(messageToSend);
|
|
333
|
+
let signature;
|
|
334
|
+
if (this.derivationType === DerivationType.ED25519 ||
|
|
335
|
+
this.derivationType === DerivationType.BIP32_ED25519) {
|
|
336
|
+
signature = ledgerResponse.slice(0, ledgerResponse.length - 2).toString('hex');
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
if (!validateResponse(ledgerResponse)) {
|
|
340
|
+
throw new InvalidLedgerResponseError('Invalid signature return by ledger unable to parse the response');
|
|
341
|
+
}
|
|
342
|
+
const idxLengthRVal = 3; // Third element of response is length of r value
|
|
343
|
+
const rValue = extractValue(idxLengthRVal, ledgerResponse);
|
|
344
|
+
const idxLengthSVal = rValue.idxValueStart + rValue.length + 1;
|
|
345
|
+
const sValue = extractValue(idxLengthSVal, ledgerResponse);
|
|
346
|
+
const signatureBuffer = Buffer.concat([rValue.buffer, sValue.buffer]);
|
|
347
|
+
signature = signatureBuffer.toString('hex');
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
bytes,
|
|
351
|
+
sig: b58cencode(signature, prefix[Prefix.SIG]),
|
|
352
|
+
prefixSig: b58cencode(signature, this.getPrefixes().prefSig),
|
|
353
|
+
sbytes: bytes + signature,
|
|
354
|
+
};
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
signWithLedger(message) {
|
|
358
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
359
|
+
// first element of the message represents the path
|
|
360
|
+
let ledgerResponse = yield this.transport.send(this.CLA, this.INS_SIGN, this.FIRST_MESSAGE_SEQUENCE, this.derivationType, message[0]);
|
|
361
|
+
for (let i = 1; i < message.length; i++) {
|
|
362
|
+
const p1 = i === message.length - 1 ? this.LAST_MESSAGE_SEQUENCE : this.OTHER_MESSAGE_SEQUENCE;
|
|
363
|
+
ledgerResponse = yield this.transport.send(this.CLA, this.INS_SIGN, p1, this.derivationType, message[i]);
|
|
364
|
+
}
|
|
365
|
+
return ledgerResponse;
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
getPrefixes() {
|
|
369
|
+
if (this.derivationType === DerivationType.ED25519 ||
|
|
370
|
+
this.derivationType === DerivationType.BIP32_ED25519) {
|
|
371
|
+
return {
|
|
372
|
+
prefPk: prefix[Prefix.EDPK],
|
|
373
|
+
prefPkh: prefix[Prefix.TZ1],
|
|
374
|
+
prefSig: prefix[Prefix.EDSIG],
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
else if (this.derivationType === DerivationType.SECP256K1) {
|
|
378
|
+
return {
|
|
379
|
+
prefPk: prefix[Prefix.SPPK],
|
|
380
|
+
prefPkh: prefix[Prefix.TZ2],
|
|
381
|
+
prefSig: prefix[Prefix.SPSIG],
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
return {
|
|
386
|
+
prefPk: prefix[Prefix.P2PK],
|
|
387
|
+
prefPkh: prefix[Prefix.TZ3],
|
|
388
|
+
prefSig: prefix[Prefix.P2SIG],
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
}
|
|
390
392
|
}
|
|
391
393
|
|
|
392
394
|
export { DerivationType, HDPathTemplate, InvalidDerivationTypeError, LedgerSigner, VERSION };
|