@ukeyfe/hardware-transport 1.1.13

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.
Files changed (72) hide show
  1. package/.eslintrc +24 -0
  2. package/README.md +29 -0
  3. package/__tests__/build-receive.test.js +117 -0
  4. package/__tests__/decode-features.test.js +72 -0
  5. package/__tests__/encode-decode-basic.test.js +272 -0
  6. package/__tests__/encode-decode.test.js +532 -0
  7. package/__tests__/messages.test.js +86 -0
  8. package/dist/constants.d.ts +6 -0
  9. package/dist/constants.d.ts.map +1 -0
  10. package/dist/index.d.ts +5203 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +942 -0
  13. package/dist/serialization/index.d.ts +6 -0
  14. package/dist/serialization/index.d.ts.map +1 -0
  15. package/dist/serialization/protobuf/decode.d.ts +6 -0
  16. package/dist/serialization/protobuf/decode.d.ts.map +1 -0
  17. package/dist/serialization/protobuf/encode.d.ts +5 -0
  18. package/dist/serialization/protobuf/encode.d.ts.map +1 -0
  19. package/dist/serialization/protobuf/index.d.ts +4 -0
  20. package/dist/serialization/protobuf/index.d.ts.map +1 -0
  21. package/dist/serialization/protobuf/messages.d.ts +11 -0
  22. package/dist/serialization/protobuf/messages.d.ts.map +1 -0
  23. package/dist/serialization/protocol/decode.d.ts +11 -0
  24. package/dist/serialization/protocol/decode.d.ts.map +1 -0
  25. package/dist/serialization/protocol/encode.d.ts +11 -0
  26. package/dist/serialization/protocol/encode.d.ts.map +1 -0
  27. package/dist/serialization/protocol/index.d.ts +3 -0
  28. package/dist/serialization/protocol/index.d.ts.map +1 -0
  29. package/dist/serialization/receive.d.ts +8 -0
  30. package/dist/serialization/receive.d.ts.map +1 -0
  31. package/dist/serialization/send.d.ts +7 -0
  32. package/dist/serialization/send.d.ts.map +1 -0
  33. package/dist/types/index.d.ts +3 -0
  34. package/dist/types/index.d.ts.map +1 -0
  35. package/dist/types/messages.d.ts +3762 -0
  36. package/dist/types/messages.d.ts.map +1 -0
  37. package/dist/types/transport.d.ts +62 -0
  38. package/dist/types/transport.d.ts.map +1 -0
  39. package/dist/utils/highlevel-checks.d.ts +10 -0
  40. package/dist/utils/highlevel-checks.d.ts.map +1 -0
  41. package/dist/utils/logBlockCommand.d.ts +2 -0
  42. package/dist/utils/logBlockCommand.d.ts.map +1 -0
  43. package/dist/utils/protobuf.d.ts +2 -0
  44. package/dist/utils/protobuf.d.ts.map +1 -0
  45. package/jest.config.js +7 -0
  46. package/package.json +31 -0
  47. package/protocol.md +21 -0
  48. package/scripts/protobuf-build.sh +58 -0
  49. package/scripts/protobuf-patches/TxAck.js +44 -0
  50. package/scripts/protobuf-patches/TxInputType.js +49 -0
  51. package/scripts/protobuf-patches/TxOutputType.js +50 -0
  52. package/scripts/protobuf-patches/index.js +274 -0
  53. package/scripts/protobuf-types.js +283 -0
  54. package/src/constants.ts +8 -0
  55. package/src/index.ts +41 -0
  56. package/src/serialization/index.ts +8 -0
  57. package/src/serialization/protobuf/decode.ts +95 -0
  58. package/src/serialization/protobuf/encode.ts +79 -0
  59. package/src/serialization/protobuf/index.ts +3 -0
  60. package/src/serialization/protobuf/messages.ts +37 -0
  61. package/src/serialization/protocol/decode.ts +48 -0
  62. package/src/serialization/protocol/encode.ts +59 -0
  63. package/src/serialization/protocol/index.ts +2 -0
  64. package/src/serialization/receive.ts +18 -0
  65. package/src/serialization/send.ts +56 -0
  66. package/src/types/index.ts +2 -0
  67. package/src/types/messages.ts +4864 -0
  68. package/src/types/transport.ts +71 -0
  69. package/src/utils/highlevel-checks.ts +88 -0
  70. package/src/utils/logBlockCommand.ts +1 -0
  71. package/src/utils/protobuf.ts +24 -0
  72. package/tsconfig.json +11 -0
package/.eslintrc ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "rules": {
3
+ "no-console": "off",
4
+ "@typescript-eslint/no-non-null-assertion": "off",
5
+ "@typescript-eslint/no-explicit-any": "off",
6
+ "@typescript-eslint/ban-ts-ignore": "off",
7
+ // We need empty functions for mocking modules for react-native
8
+ "@typescript-eslint/no-empty-function": "off",
9
+ "no-useless-constructor": "off",
10
+ "@typescript-eslint/no-useless-constructor": "error",
11
+ // valid case of class method overloads in typescript
12
+ "no-dupe-class-members": "off",
13
+ "@typescript-eslint/ban-ts-comment": "off",
14
+ // Missing return type on function
15
+ "@typescript-eslint/explicit-module-boundary-types": "off",
16
+ // note you must disable the base rule as it can report incorrect errors
17
+ "no-use-before-define": "off",
18
+ "@typescript-eslint/no-use-before-define": ["error"],
19
+ "@typescript-eslint/no-var-requires": "off",
20
+ "require-await": ["error"],
21
+ "import/no-named-default": "off",
22
+ "@typescript-eslint/ban-types": "off"
23
+ }
24
+ }
package/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # `@ukeyfe/hardware-transport`
2
+
3
+ `@ukeyfe/hardware-transport` is a library for low-level communication with UKey Hardware.
4
+
5
+ ## What is the purpose
6
+
7
+ - translate JSON payloads to binary messages using protobuf definitions comprehensible to UKey devices
8
+ - chunking and reading chunked messages according to the [UKey protocol](./protocol.md)
9
+ - exposing single API for various transport methods:
10
+ - Http Transport
11
+ - React Native Transport
12
+ - WebUSB
13
+ - Create and expose typescript definitions based on protobuf definitions.
14
+
15
+ ### The short version
16
+
17
+ In order to be able to use new features of ukey-firmware you need to update protobuf definitions.
18
+
19
+ 1. `git submodule update --init --recursive` to initialize git submodules.
20
+ 1. `yarn update-submodules` to update firmware submodule
21
+ 1. `yarn update:protobuf` to generate new `./messages.json` and `./src/types/messages.ts`
22
+
23
+ git submodule update --init --recursive to initialize firmware submodule
24
+ yarn update-submodules to update firmware submodule
25
+ yarn update:protobuf to generate new ./messages.json and ./src/types/messages.ts
26
+
27
+ ## Docs
28
+
29
+ Documentation is available [hardware-js-sdk](https://developer.ukey.so/connect-to-hardware/hardware-sdk/start)
@@ -0,0 +1,117 @@
1
+ const { parseConfigure } = require('../src/serialization/protobuf/messages');
2
+ const { buildOne } = require('../src/serialization/send');
3
+ const { receiveOne } = require('../src/serialization/receive');
4
+
5
+ const { buildEncodeBuffers } = require('../src/serialization/send');
6
+
7
+ const messages = {
8
+ StellarPaymentOp: {
9
+ fields: {
10
+ source_account: {
11
+ type: 'string',
12
+ id: 1,
13
+ },
14
+ destination_account: {
15
+ rule: 'required',
16
+ type: 'string',
17
+ id: 2,
18
+ },
19
+ asset: {
20
+ rule: 'required',
21
+ type: 'StellarAsset',
22
+ id: 3,
23
+ },
24
+ amount: {
25
+ rule: 'required',
26
+ type: 'sint64',
27
+ id: 4,
28
+ },
29
+ },
30
+ },
31
+ StellarAssetType: {
32
+ values: {
33
+ NATIVE: 0,
34
+ ALPHANUM4: 1,
35
+ ALPHANUM12: 2,
36
+ },
37
+ },
38
+ StellarAsset: {
39
+ fields: {
40
+ type: {
41
+ rule: 'required',
42
+ type: 'StellarAssetType',
43
+ id: 1,
44
+ },
45
+ code: {
46
+ type: 'string',
47
+ id: 2,
48
+ },
49
+ issuer: {
50
+ type: 'string',
51
+ id: 3,
52
+ },
53
+ },
54
+ },
55
+ MessageType: {
56
+ values: {
57
+ MessageType_StellarSignTx: 202,
58
+ MessageType_StellarTxOpRequest: 203,
59
+ MessageType_StellarGetAddress: 207,
60
+ MessageType_StellarAddress: 208,
61
+ MessageType_StellarCreateAccountOp: 210,
62
+ MessageType_StellarPaymentOp: 211,
63
+ MessageType_StellarPathPaymentOp: 212,
64
+ MessageType_StellarManageOfferOp: 213,
65
+ MessageType_StellarCreatePassiveOfferOp: 214,
66
+ MessageType_StellarSetOptionsOp: 215,
67
+ MessageType_StellarChangeTrustOp: 216,
68
+ MessageType_StellarAllowTrustOp: 217,
69
+ MessageType_StellarAccountMergeOp: 218,
70
+ MessageType_StellarManageDataOp: 220,
71
+ MessageType_StellarBumpSequenceOp: 221,
72
+ MessageType_StellarSignedTx: 230,
73
+ },
74
+ },
75
+ };
76
+
77
+ const fixtures = [
78
+ {
79
+ name: 'StellarPaymentOp',
80
+ in: {
81
+ source_account: 'meow'.repeat(100), // make message longer then 63 bytes
82
+ destination_account: 'wuff',
83
+ asset: {
84
+ type: 'NATIVE',
85
+ code: 'hello',
86
+ issuer: 'world',
87
+ },
88
+ amount: 10,
89
+ },
90
+ },
91
+ ];
92
+
93
+ const parsedMessages = parseConfigure({
94
+ nested: { hw: { nested: { trezor: { nested: { messages: { nested: messages } } } } } },
95
+ });
96
+
97
+ describe('encoding json -> protobuf -> json', () => {
98
+ fixtures.forEach(f => {
99
+ describe(f.name, () => {
100
+ test('buildOne - receiveOne', () => {
101
+ // encoded message
102
+ const encodedMessage = buildOne(parsedMessages, f.name, f.in);
103
+ // then decode message and check, whether decoded message matches original json
104
+ const decodedMessage = receiveOne(parsedMessages, encodedMessage);
105
+ expect(decodedMessage.type).toEqual(f.name);
106
+ expect(decodedMessage.message).toEqual(f.in);
107
+ });
108
+
109
+ test('buildBuffers - receiveAndParse', () => {
110
+ const result = buildEncodeBuffers(parsedMessages, f.name, f.in);
111
+ result.forEach(r => {
112
+ expect(r.byteLength).toBeLessThanOrEqual(63);
113
+ });
114
+ });
115
+ });
116
+ });
117
+ });
@@ -0,0 +1,72 @@
1
+ const ProtoBuf = require('protobufjs/light');
2
+ const ByteBuffer = require('bytebuffer');
3
+
4
+ const { decode } = require('../src/serialization/protobuf/decode');
5
+ const { decode: decodeProtocol } = require('../src/serialization/protocol/decode');
6
+
7
+ // eslint-disable-next-line import/no-unresolved
8
+ const messages = require('../messages.json');
9
+
10
+ const fixtures = [
11
+ {
12
+ name: 'Features-error-battery_level',
13
+ encodeMessage:
14
+ '0011000000260a096f6e656b65792e736f1002180020002801900101aa010131b00101b80163c00163c020ff',
15
+ out: {
16
+ vendor: 'ukey.so',
17
+ major_version: 2,
18
+ minor_version: 0,
19
+ patch_version: 0,
20
+ bootloader_mode: true,
21
+ firmware_present: true,
22
+ capabilities: [],
23
+ model: '1',
24
+ fw_major: 1,
25
+ fw_minor: 99,
26
+ fw_patch: 99,
27
+ battery_level: 4,
28
+ },
29
+ },
30
+ {
31
+ name: 'Features-success',
32
+ encodeMessage:
33
+ '0011000000260a096f6e656b65792e736f1002180020002801900101aa010131b00102b80163c00163c02004',
34
+ out: {
35
+ vendor: 'ukey.so',
36
+ major_version: 2,
37
+ minor_version: 0,
38
+ patch_version: 0,
39
+ bootloader_mode: true,
40
+ firmware_present: true,
41
+ capabilities: [],
42
+ model: '1',
43
+ fw_major: 2,
44
+ fw_minor: 99,
45
+ fw_patch: 99,
46
+ battery_level: 4,
47
+ },
48
+ },
49
+ ];
50
+
51
+ describe('Fix messages decode', () => {
52
+ const Messages = ProtoBuf.Root.fromJSON(messages);
53
+ const Message = Messages.lookup(`Features`);
54
+ fixtures.forEach(f => {
55
+ describe(f.name, () => {
56
+ test('decode', () => {
57
+ // deserialize
58
+ const encoded = ByteBuffer.fromHex(f.encodeMessage);
59
+ const { buffer } = decodeProtocol(encoded);
60
+ const decoded = decode(Message, buffer);
61
+
62
+ // filter null values
63
+ Object.keys(decoded).forEach(key => {
64
+ if (decoded[key] == null) {
65
+ delete decoded[key];
66
+ }
67
+ });
68
+ expect(decoded).toEqual(f.out);
69
+ });
70
+ });
71
+ });
72
+ });
@@ -0,0 +1,272 @@
1
+ const ProtoBuf = require('protobufjs/light');
2
+ const { encode } = require('../src/serialization/protobuf/encode');
3
+ const { decode } = require('../src/serialization/protobuf/decode');
4
+
5
+ const messages = {
6
+ nested: {
7
+ messages: {
8
+ nested: {
9
+ String: {
10
+ fields: {
11
+ field: {
12
+ rule: 'required',
13
+ type: 'string',
14
+ id: 1,
15
+ },
16
+ },
17
+ },
18
+ Uint32: {
19
+ fields: {
20
+ field: {
21
+ rule: 'required',
22
+ type: 'uint32',
23
+ id: 2,
24
+ },
25
+ },
26
+ },
27
+ Uint64: {
28
+ fields: {
29
+ field: {
30
+ rule: 'required',
31
+ type: 'uint64',
32
+ id: 3,
33
+ },
34
+ },
35
+ },
36
+ Bool: {
37
+ fields: {
38
+ field: {
39
+ rule: 'required',
40
+ type: 'bool',
41
+ id: 4,
42
+ },
43
+ },
44
+ },
45
+ Sint32: {
46
+ fields: {
47
+ field: {
48
+ rule: 'required',
49
+ type: 'sint32',
50
+ id: 5,
51
+ },
52
+ },
53
+ },
54
+ Sint64: {
55
+ fields: {
56
+ field: {
57
+ rule: 'required',
58
+ type: 'sint64',
59
+ id: 6,
60
+ },
61
+ },
62
+ },
63
+ Bytes: {
64
+ fields: {
65
+ field: {
66
+ rule: 'required',
67
+ type: 'bytes',
68
+ id: 7,
69
+ },
70
+ },
71
+ },
72
+
73
+ // complex and real life examples
74
+ ComplexFieldOfOptionals: {
75
+ fields: {
76
+ bool: {
77
+ rule: 'optional',
78
+ type: 'bool',
79
+ id: 8,
80
+ },
81
+ number: {
82
+ rule: 'optional',
83
+ type: 'uint32',
84
+ id: 9,
85
+ },
86
+ },
87
+ },
88
+
89
+ Repeated: {
90
+ fields: {
91
+ bool: {
92
+ rule: 'repeated',
93
+ type: 'bool',
94
+ id: 8,
95
+ },
96
+ },
97
+ },
98
+
99
+ Defaults: {
100
+ fields: {
101
+ string: {
102
+ type: 'string',
103
+ id: 8,
104
+ options: {
105
+ defaults: 'hello world',
106
+ },
107
+ },
108
+ },
109
+ },
110
+ },
111
+ },
112
+ },
113
+ };
114
+
115
+ const basicFixtures = [
116
+ {
117
+ name: 'String',
118
+ params: { field: 'foo' },
119
+ encoded: '0a03666f6f',
120
+ },
121
+ {
122
+ name: 'Uint32',
123
+ params: { field: 4294967295 },
124
+ encoded: '10ffffffff0f',
125
+ },
126
+ {
127
+ name: 'Uint64',
128
+ params: { field: 1844674407370955 },
129
+ encoded: '18cba19cd68bb7a303',
130
+ },
131
+ {
132
+ name: 'Uint64',
133
+ params: { field: '166054873161269248' }, // over Number.MAX_SAFE_INTEGER is sent as string
134
+ encoded: '1880808080f0c0fca602',
135
+ },
136
+ {
137
+ name: 'Sint64',
138
+ params: { field: '-166054873161269248' }, // over Number.MAX_SAFE_INTEGER is sent as string
139
+ encoded: '30ffffffffdf81f9cd04',
140
+ },
141
+ {
142
+ name: 'Bool',
143
+ params: { field: true },
144
+ encoded: '2001',
145
+ },
146
+ {
147
+ name: 'Bool',
148
+ params: { field: false },
149
+ encoded: '2000',
150
+ },
151
+ {
152
+ name: 'Sint32',
153
+ params: { field: -4294967 },
154
+ encoded: '28eda48c04',
155
+ },
156
+ {
157
+ name: 'Sint64',
158
+ params: { field: -1844674407370955 },
159
+ encoded: '3095c3b8ac97eec606',
160
+ },
161
+ {
162
+ name: 'Bytes',
163
+ params: {
164
+ field:
165
+ '851fc9542342321af63ecbba7d3ece545f2a42bad01ba32cff5535b18e54b6d3106e10b6a4525993d185a1443d9a125186960e028eabfdd8d76cf70a3a7e3100',
166
+ },
167
+ encoded:
168
+ '3a40851fc9542342321af63ecbba7d3ece545f2a42bad01ba32cff5535b18e54b6d3106e10b6a4525993d185a1443d9a125186960e028eabfdd8d76cf70a3a7e3100',
169
+ },
170
+ ];
171
+
172
+ // note: difference in bool encoding. if type === bool && field = optional && not message of only one field, bool is encoded as ""
173
+ const advancedFixtures = [
174
+ {
175
+ name: 'ComplexFieldOfOptionals',
176
+ in: { number: 1 },
177
+ encoded: '4801',
178
+ out: { bool: null, number: 1 },
179
+ },
180
+ {
181
+ name: 'Repeated',
182
+ in: { bool: [true, false, true, false] },
183
+ encoded: '420401000100',
184
+ out: { bool: [true, false, true, false] },
185
+ },
186
+ {
187
+ name: 'Defaults',
188
+ in: { string: '' },
189
+ encoded: '4200',
190
+ out: { string: '' },
191
+ },
192
+ ];
193
+
194
+ describe('basic concepts', () => {
195
+ const Messages = ProtoBuf.Root.fromJSON(messages);
196
+
197
+ describe('primitives encode/decode', () => {
198
+ basicFixtures.forEach(f => {
199
+ describe(f.name, () => {
200
+ const Message = Messages.lookup(`messages.${f.name}`);
201
+
202
+ test(f.name, () => {
203
+ // serialize new way - this is to confirm new lib won't break old behavior
204
+ const encoded = encode(Message, f.params);
205
+ expect(encoded.toString('hex')).toEqual(f.encoded);
206
+
207
+ // deserialize new way - this is to confirm new lib won't break old behavior
208
+ const decoded = decode(Message, encoded);
209
+ expect(decoded).toEqual(f.params);
210
+ });
211
+ });
212
+ });
213
+ });
214
+
215
+ describe('advanced', () => {
216
+ advancedFixtures.forEach(f => {
217
+ describe(f.name, () => {
218
+ const Message = Messages.lookup(`messages.${f.name}`);
219
+
220
+ test(f.name, () => {
221
+ // serialize new way - this is to confirm new lib won't break old behavior
222
+ const encoded = encode(Message, f.in);
223
+
224
+ expect(encoded.toString('hex')).toEqual(f.encoded);
225
+
226
+ // deserialize new way - this is to confirm new lib won't break old behavior
227
+ const decoded = decode(Message, encoded);
228
+
229
+ expect(decoded).toEqual(f.out);
230
+ });
231
+ });
232
+ });
233
+
234
+ test('Different protobuf between receiving ends', () => {
235
+ const messages = {
236
+ nested: {
237
+ messages: {
238
+ nested: {
239
+ ButtonRequest: {
240
+ fields: {
241
+ code: {
242
+ type: 'string',
243
+ id: 1,
244
+ },
245
+ pages: {
246
+ type: 'uint32',
247
+ id: 2,
248
+ },
249
+ },
250
+ },
251
+ },
252
+ },
253
+ },
254
+ };
255
+
256
+ const SenderMessages = ProtoBuf.Root.fromJSON(messages);
257
+ const senderEncoded = encode(SenderMessages.lookup('messages.ButtonRequest'), {
258
+ type: 'foo',
259
+ pages: 123,
260
+ });
261
+
262
+ const receiverMessages = messages;
263
+ // now change field type from uint32 to string
264
+ receiverMessages.nested.messages.nested.ButtonRequest.fields.pages.type = 'string';
265
+ const ReceiverMessages = ProtoBuf.Root.fromJSON(receiverMessages);
266
+
267
+ expect(() => {
268
+ decode(ReceiverMessages.lookup('messages.ButtonRequest'), senderEncoded);
269
+ }).toThrow();
270
+ });
271
+ });
272
+ });