UNPKG

4.74 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = void 0;
7
8var _u2fApi = require("u2f-api");
9
10var _hwTransport = _interopRequireDefault(require("@ledgerhq/hw-transport"));
11
12var _logs = require("@ledgerhq/logs");
13
14var _errors = require("@ledgerhq/errors");
15
16function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
18function wrapU2FTransportError(originalError, message, id) {
19 const err = new _errors.TransportError(message, id); // $FlowFixMe
20
21 err.originalError = originalError;
22 return err;
23}
24
25function wrapApdu(apdu, key) {
26 const result = Buffer.alloc(apdu.length);
27
28 for (let i = 0; i < apdu.length; i++) {
29 result[i] = apdu[i] ^ key[i % key.length];
30 }
31
32 return result;
33} // Convert from normal to web-safe, strip trailing "="s
34
35
36const webSafe64 = base64 => base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""); // Convert from web-safe to normal, add trailing "="s
37
38
39const normal64 = base64 => base64.replace(/-/g, "+").replace(/_/g, "/") + "==".substring(0, 3 * base64.length % 4);
40
41function attemptExchange(apdu, timeoutMillis, scrambleKey, unwrap) {
42 const keyHandle = wrapApdu(apdu, scrambleKey);
43 const challenge = Buffer.from("0000000000000000000000000000000000000000000000000000000000000000", "hex");
44 const signRequest = {
45 version: "U2F_V2",
46 keyHandle: webSafe64(keyHandle.toString("base64")),
47 challenge: webSafe64(challenge.toString("base64")),
48 appId: location.origin
49 };
50 (0, _logs.log)("apdu", "=> " + apdu.toString("hex"));
51 return (0, _u2fApi.sign)(signRequest, timeoutMillis / 1000).then(response => {
52 const {
53 signatureData
54 } = response;
55
56 if (typeof signatureData === "string") {
57 const data = Buffer.from(normal64(signatureData), "base64");
58 let result;
59
60 if (!unwrap) {
61 result = data;
62 } else {
63 result = data.slice(5);
64 }
65
66 (0, _logs.log)("apdu", "<= " + result.toString("hex"));
67 return result;
68 } else {
69 throw response;
70 }
71 });
72}
73
74let transportInstances = [];
75
76function emitDisconnect() {
77 transportInstances.forEach(t => t.emit("disconnect"));
78 transportInstances = [];
79}
80
81function isTimeoutU2FError(u2fError) {
82 return u2fError.metaData.code === 5;
83}
84/**
85 * U2F web Transport implementation
86 * @example
87 * import TransportU2F from "@ledgerhq/hw-transport-u2f";
88 * ...
89 * TransportU2F.create().then(transport => ...)
90 */
91
92
93class TransportU2F extends _hwTransport.default {
94 /*
95 */
96
97 /*
98 */
99
100 /**
101 * static function to create a new Transport from a connected Ledger device discoverable via U2F (browser support)
102 */
103 static async open(_, _openTimeout = 5000) {
104 return new TransportU2F();
105 }
106
107 constructor() {
108 super();
109 this.scrambleKey = void 0;
110 this.unwrap = true;
111 transportInstances.push(this);
112 }
113 /**
114 * Exchange with the device using APDU protocol.
115 * @param apdu
116 * @returns a promise of apdu response
117 */
118
119
120 async exchange(apdu) {
121 try {
122 return await attemptExchange(apdu, this.exchangeTimeout, this.scrambleKey, this.unwrap);
123 } catch (e) {
124 const isU2FError = typeof e.metaData === "object";
125
126 if (isU2FError) {
127 if (isTimeoutU2FError(e)) {
128 emitDisconnect();
129 } // the wrapping make error more usable and "printable" to the end user.
130
131
132 throw wrapU2FTransportError(e, "Failed to sign with Ledger device: U2F " + e.metaData.type, "U2F_" + e.metaData.code);
133 } else {
134 throw e;
135 }
136 }
137 }
138 /**
139 */
140
141
142 setScrambleKey(scrambleKey) {
143 this.scrambleKey = Buffer.from(scrambleKey, "ascii");
144 }
145 /**
146 */
147
148
149 setUnwrap(unwrap) {
150 this.unwrap = unwrap;
151 }
152
153 close() {
154 // u2f have no way to clean things up
155 return Promise.resolve();
156 }
157
158}
159
160exports.default = TransportU2F;
161TransportU2F.isSupported = _u2fApi.isSupported;
162
163TransportU2F.list = () => // this transport is not discoverable but we are going to guess if it is here with isSupported()
164(0, _u2fApi.isSupported)().then(supported => supported ? [null] : []);
165
166TransportU2F.listen = observer => {
167 let unsubscribed = false;
168 (0, _u2fApi.isSupported)().then(supported => {
169 if (unsubscribed) return;
170
171 if (supported) {
172 observer.next({
173 type: "add",
174 descriptor: null
175 });
176 observer.complete();
177 } else {
178 observer.error(new _errors.TransportError("U2F browser support is needed for Ledger. " + "Please use Chrome, Opera or Firefox with a U2F extension. " + "Also make sure you're on an HTTPS connection", "U2FNotSupported"));
179 }
180 });
181 return {
182 unsubscribe: () => {
183 unsubscribed = true;
184 }
185 };
186};
187//# sourceMappingURL=TransportU2F.js.map
\No newline at end of file