@yume-chan/adb 0.0.22 → 0.0.24

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 (149) hide show
  1. package/CHANGELOG.json +51 -0
  2. package/CHANGELOG.md +24 -1
  3. package/LICENSE +1 -1
  4. package/esm/adb.d.ts +6 -3
  5. package/esm/adb.d.ts.map +1 -1
  6. package/esm/adb.js +11 -4
  7. package/esm/adb.js.map +1 -1
  8. package/esm/banner.d.ts.map +1 -1
  9. package/esm/banner.js +1 -0
  10. package/esm/banner.js.map +1 -1
  11. package/esm/commands/power.d.ts.map +1 -1
  12. package/esm/commands/power.js +6 -5
  13. package/esm/commands/power.js.map +1 -1
  14. package/esm/commands/reverse.d.ts.map +1 -1
  15. package/esm/commands/reverse.js +23 -11
  16. package/esm/commands/reverse.js.map +1 -1
  17. package/esm/commands/subprocess/command.js +3 -3
  18. package/esm/commands/subprocess/command.js.map +1 -1
  19. package/esm/commands/subprocess/protocols/none.d.ts +2 -2
  20. package/esm/commands/subprocess/protocols/none.d.ts.map +1 -1
  21. package/esm/commands/subprocess/protocols/shell.d.ts +3 -3
  22. package/esm/commands/subprocess/protocols/shell.d.ts.map +1 -1
  23. package/esm/commands/subprocess/protocols/shell.js +14 -69
  24. package/esm/commands/subprocess/protocols/shell.js.map +1 -1
  25. package/esm/commands/subprocess/protocols/types.d.ts +2 -2
  26. package/esm/commands/subprocess/protocols/types.d.ts.map +1 -1
  27. package/esm/commands/subprocess/utils.js +1 -1
  28. package/esm/commands/subprocess/utils.js.map +1 -1
  29. package/esm/commands/sync/list.d.ts +2 -5
  30. package/esm/commands/sync/list.d.ts.map +1 -1
  31. package/esm/commands/sync/list.js +2 -4
  32. package/esm/commands/sync/list.js.map +1 -1
  33. package/esm/commands/sync/pull.d.ts +1 -4
  34. package/esm/commands/sync/pull.d.ts.map +1 -1
  35. package/esm/commands/sync/pull.js +2 -2
  36. package/esm/commands/sync/pull.js.map +1 -1
  37. package/esm/commands/sync/push.d.ts +4 -7
  38. package/esm/commands/sync/push.d.ts.map +1 -1
  39. package/esm/commands/sync/push.js +3 -6
  40. package/esm/commands/sync/push.js.map +1 -1
  41. package/esm/commands/sync/request.d.ts +13 -18
  42. package/esm/commands/sync/request.d.ts.map +1 -1
  43. package/esm/commands/sync/request.js +23 -28
  44. package/esm/commands/sync/request.js.map +1 -1
  45. package/esm/commands/sync/response.d.ts +18 -12
  46. package/esm/commands/sync/response.d.ts.map +1 -1
  47. package/esm/commands/sync/response.js +39 -17
  48. package/esm/commands/sync/response.js.map +1 -1
  49. package/esm/commands/sync/socket.d.ts +2 -2
  50. package/esm/commands/sync/socket.d.ts.map +1 -1
  51. package/esm/commands/sync/socket.js +10 -5
  52. package/esm/commands/sync/socket.js.map +1 -1
  53. package/esm/commands/sync/stat.d.ts +0 -3
  54. package/esm/commands/sync/stat.d.ts.map +1 -1
  55. package/esm/commands/sync/stat.js +0 -2
  56. package/esm/commands/sync/stat.js.map +1 -1
  57. package/esm/commands/sync/sync.d.ts +4 -3
  58. package/esm/commands/sync/sync.d.ts.map +1 -1
  59. package/esm/commands/sync/sync.js +9 -7
  60. package/esm/commands/sync/sync.js.map +1 -1
  61. package/esm/commands/tcpip.js +1 -1
  62. package/esm/commands/tcpip.js.map +1 -1
  63. package/esm/daemon/auth.js +1 -1
  64. package/esm/daemon/auth.js.map +1 -1
  65. package/esm/daemon/crypto.d.ts +3 -2
  66. package/esm/daemon/crypto.d.ts.map +1 -1
  67. package/esm/daemon/crypto.js +56 -49
  68. package/esm/daemon/crypto.js.map +1 -1
  69. package/esm/daemon/dispatcher.d.ts +38 -6
  70. package/esm/daemon/dispatcher.d.ts.map +1 -1
  71. package/esm/daemon/dispatcher.js +86 -28
  72. package/esm/daemon/dispatcher.js.map +1 -1
  73. package/esm/daemon/packet.d.ts +2 -2
  74. package/esm/daemon/packet.d.ts.map +1 -1
  75. package/esm/daemon/packet.js +13 -12
  76. package/esm/daemon/packet.js.map +1 -1
  77. package/esm/daemon/socket.d.ts +11 -6
  78. package/esm/daemon/socket.d.ts.map +1 -1
  79. package/esm/daemon/socket.js +42 -12
  80. package/esm/daemon/socket.js.map +1 -1
  81. package/esm/daemon/transport.d.ts +75 -11
  82. package/esm/daemon/transport.d.ts.map +1 -1
  83. package/esm/daemon/transport.js +60 -33
  84. package/esm/daemon/transport.js.map +1 -1
  85. package/esm/features.d.ts +2 -1
  86. package/esm/features.d.ts.map +1 -1
  87. package/esm/features.js +2 -1
  88. package/esm/features.js.map +1 -1
  89. package/esm/server/client.d.ts +128 -48
  90. package/esm/server/client.d.ts.map +1 -1
  91. package/esm/server/client.js +378 -145
  92. package/esm/server/client.js.map +1 -1
  93. package/esm/server/transport.d.ts +3 -0
  94. package/esm/server/transport.d.ts.map +1 -1
  95. package/esm/server/transport.js +30 -4
  96. package/esm/server/transport.js.map +1 -1
  97. package/esm/utils/base64.d.ts.map +1 -1
  98. package/esm/utils/base64.js +33 -22
  99. package/esm/utils/base64.js.map +1 -1
  100. package/esm/utils/hex.d.ts +1 -1
  101. package/esm/utils/hex.d.ts.map +1 -1
  102. package/esm/utils/hex.js +12 -14
  103. package/esm/utils/hex.js.map +1 -1
  104. package/esm/utils/index.d.ts +1 -1
  105. package/esm/utils/index.d.ts.map +1 -1
  106. package/esm/utils/index.js +1 -1
  107. package/esm/utils/index.js.map +1 -1
  108. package/esm/utils/sequence-equal.d.ts +2 -0
  109. package/esm/utils/sequence-equal.d.ts.map +1 -0
  110. package/esm/utils/sequence-equal.js +12 -0
  111. package/esm/utils/sequence-equal.js.map +1 -0
  112. package/package.json +13 -15
  113. package/src/adb.ts +22 -6
  114. package/src/banner.ts +1 -0
  115. package/src/commands/power.ts +7 -5
  116. package/src/commands/reverse.ts +31 -18
  117. package/src/commands/subprocess/command.ts +3 -3
  118. package/src/commands/subprocess/protocols/none.ts +2 -2
  119. package/src/commands/subprocess/protocols/shell.ts +21 -100
  120. package/src/commands/subprocess/protocols/types.ts +2 -2
  121. package/src/commands/subprocess/utils.ts +1 -1
  122. package/src/commands/sync/list.ts +2 -4
  123. package/src/commands/sync/pull.ts +2 -2
  124. package/src/commands/sync/push.ts +7 -10
  125. package/src/commands/sync/request.ts +32 -32
  126. package/src/commands/sync/response.ts +47 -20
  127. package/src/commands/sync/socket.ts +13 -8
  128. package/src/commands/sync/stat.ts +0 -2
  129. package/src/commands/sync/sync.ts +13 -9
  130. package/src/commands/tcpip.ts +1 -1
  131. package/src/daemon/auth.ts +1 -1
  132. package/src/daemon/crypto.ts +64 -55
  133. package/src/daemon/dispatcher.ts +175 -37
  134. package/src/daemon/packet.ts +21 -14
  135. package/src/daemon/socket.ts +62 -28
  136. package/src/daemon/transport.ts +145 -40
  137. package/src/features.ts +2 -1
  138. package/src/server/client.ts +472 -224
  139. package/src/server/transport.ts +32 -4
  140. package/src/utils/base64.ts +33 -22
  141. package/src/utils/hex.ts +16 -14
  142. package/src/utils/index.ts +1 -1
  143. package/src/utils/sequence-equal.ts +13 -0
  144. package/tsconfig.build.tsbuildinfo +1 -1
  145. package/esm/utils/conditional-variable.d.ts +0 -8
  146. package/esm/utils/conditional-variable.d.ts.map +0 -1
  147. package/esm/utils/conditional-variable.js +0 -34
  148. package/esm/utils/conditional-variable.js.map +0 -1
  149. package/src/utils/conditional-variable.ts +0 -45
@@ -1,11 +1,11 @@
1
1
  import type {
2
- Consumable,
2
+ MaybeConsumable,
3
3
  WritableStreamDefaultWriter,
4
4
  } from "@yume-chan/stream-extra";
5
5
  import {
6
6
  BufferCombiner,
7
7
  BufferedReadableStream,
8
- ConsumableWritableStream,
8
+ Consumable,
9
9
  } from "@yume-chan/stream-extra";
10
10
  import type { AsyncExactReadable } from "@yume-chan/struct";
11
11
 
@@ -13,7 +13,7 @@ import type { AdbSocket } from "../../adb.js";
13
13
  import { AutoResetEvent } from "../../utils/index.js";
14
14
 
15
15
  export class AdbSyncSocketLocked implements AsyncExactReadable {
16
- readonly #writer: WritableStreamDefaultWriter<Consumable<Uint8Array>>;
16
+ readonly #writer: WritableStreamDefaultWriter<MaybeConsumable<Uint8Array>>;
17
17
  readonly #readable: BufferedReadableStream;
18
18
  readonly #socketLock: AutoResetEvent;
19
19
  readonly #writeLock = new AutoResetEvent();
@@ -24,7 +24,7 @@ export class AdbSyncSocketLocked implements AsyncExactReadable {
24
24
  }
25
25
 
26
26
  constructor(
27
- writer: WritableStreamDefaultWriter<Consumable<Uint8Array>>,
27
+ writer: WritableStreamDefaultWriter<MaybeConsumable<Uint8Array>>,
28
28
  readable: BufferedReadableStream,
29
29
  bufferSize: number,
30
30
  lock: AutoResetEvent,
@@ -35,8 +35,9 @@ export class AdbSyncSocketLocked implements AsyncExactReadable {
35
35
  this.#combiner = new BufferCombiner(bufferSize);
36
36
  }
37
37
 
38
- async #writeInnerStream(buffer: Uint8Array) {
39
- await ConsumableWritableStream.write(this.#writer, buffer);
38
+ async #write(buffer: Uint8Array) {
39
+ // `#combiner` will reuse the buffer, so we need to use the Consumable pattern
40
+ await Consumable.WritableStream.write(this.#writer, buffer);
40
41
  }
41
42
 
42
43
  async flush() {
@@ -44,7 +45,7 @@ export class AdbSyncSocketLocked implements AsyncExactReadable {
44
45
  await this.#writeLock.wait();
45
46
  const buffer = this.#combiner.flush();
46
47
  if (buffer) {
47
- await this.#writeInnerStream(buffer);
48
+ await this.#write(buffer);
48
49
  }
49
50
  } finally {
50
51
  this.#writeLock.notifyOne();
@@ -55,7 +56,7 @@ export class AdbSyncSocketLocked implements AsyncExactReadable {
55
56
  try {
56
57
  await this.#writeLock.wait();
57
58
  for (const buffer of this.#combiner.push(data)) {
58
- await this.#writeInnerStream(buffer);
59
+ await this.#write(buffer);
59
60
  }
60
61
  } finally {
61
62
  this.#writeLock.notifyOne();
@@ -63,11 +64,15 @@ export class AdbSyncSocketLocked implements AsyncExactReadable {
63
64
  }
64
65
 
65
66
  async readExactly(length: number) {
67
+ // The request may still be in the internal buffer.
68
+ // Call `flush` to send it before starting reading
66
69
  await this.flush();
67
70
  return await this.#readable.readExactly(length);
68
71
  }
69
72
 
70
73
  release(): void {
74
+ // In theory, the writer shouldn't leave anything in the buffer,
75
+ // but to be safe, call `flush` to throw away any remaining data.
71
76
  this.#combiner.flush();
72
77
  this.#socketLock.notifyOne();
73
78
  }
@@ -29,7 +29,6 @@ export const AdbSyncLstatResponse = new Struct({ littleEndian: true })
29
29
  .int32("size")
30
30
  .int32("mtime")
31
31
  .extra({
32
- id: AdbSyncResponseId.Lstat as const,
33
32
  get type() {
34
33
  return (this.mode >> 12) as LinuxFileType;
35
34
  },
@@ -83,7 +82,6 @@ export const AdbSyncStatResponse = new Struct({ littleEndian: true })
83
82
  .uint64("mtime")
84
83
  .uint64("ctime")
85
84
  .extra({
86
- id: AdbSyncResponseId.Stat as const,
87
85
  get type() {
88
86
  return (this.mode >> 12) as LinuxFileType;
89
87
  },
@@ -1,5 +1,5 @@
1
1
  import { AutoDisposable } from "@yume-chan/event";
2
- import type { Consumable, ReadableStream } from "@yume-chan/stream-extra";
2
+ import type { MaybeConsumable, ReadableStream } from "@yume-chan/stream-extra";
3
3
 
4
4
  import type { Adb, AdbSocket } from "../../adb.js";
5
5
  import { AdbFeature } from "../../features.js";
@@ -9,6 +9,7 @@ import type { AdbSyncEntry } from "./list.js";
9
9
  import { adbSyncOpenDir } from "./list.js";
10
10
  import { adbSyncPull } from "./pull.js";
11
11
  import { adbSyncPush } from "./push.js";
12
+ import type { AdbSyncSocketLocked } from "./socket.js";
12
13
  import { AdbSyncSocket } from "./socket.js";
13
14
  import type { AdbSyncStat, LinuxFileType } from "./stat.js";
14
15
  import { adbSyncLstat, adbSyncStat } from "./stat.js";
@@ -31,7 +32,7 @@ export function dirname(path: string): string {
31
32
 
32
33
  export interface AdbSyncWriteOptions {
33
34
  filename: string;
34
- file: ReadableStream<Consumable<Uint8Array>>;
35
+ file: ReadableStream<MaybeConsumable<Uint8Array>>;
35
36
  type?: LinuxFileType;
36
37
  permission?: number;
37
38
  mtime?: number;
@@ -74,16 +75,15 @@ export class AdbSync extends AutoDisposable {
74
75
  this._adb = adb;
75
76
  this._socket = new AdbSyncSocket(socket, adb.maxPayloadSize);
76
77
 
77
- this.#supportsStat = adb.supportsFeature(AdbFeature.StatV2);
78
- this.#supportsListV2 = adb.supportsFeature(AdbFeature.ListV2);
79
- this.#fixedPushMkdir = adb.supportsFeature(AdbFeature.FixedPushMkdir);
80
- this.#supportsSendReceiveV2 = adb.supportsFeature(
78
+ this.#supportsStat = adb.canUseFeature(AdbFeature.StatV2);
79
+ this.#supportsListV2 = adb.canUseFeature(AdbFeature.ListV2);
80
+ this.#fixedPushMkdir = adb.canUseFeature(AdbFeature.FixedPushMkdir);
81
+ this.#supportsSendReceiveV2 = adb.canUseFeature(
81
82
  AdbFeature.SendReceiveV2,
82
83
  );
83
84
  // https://android.googlesource.com/platform/packages/modules/adb/+/91768a57b7138166e0a3d11f79cd55909dda7014/client/file_sync_client.cpp#1361
84
85
  this.#needPushMkdirWorkaround =
85
- this._adb.supportsFeature(AdbFeature.ShellV2) &&
86
- !this.fixedPushMkdir;
86
+ this._adb.canUseFeature(AdbFeature.ShellV2) && !this.fixedPushMkdir;
87
87
  }
88
88
 
89
89
  /**
@@ -151,7 +151,7 @@ export class AdbSync extends AutoDisposable {
151
151
  */
152
152
  async write(options: AdbSyncWriteOptions): Promise<void> {
153
153
  if (this.needPushMkdirWorkaround) {
154
- // It may fail if the path is already existed.
154
+ // It may fail if `filename` already exists.
155
155
  // Ignore the result.
156
156
  // TODO: sync: test push mkdir workaround (need an Android 8 device)
157
157
  await this._adb.subprocess.spawnAndWait([
@@ -168,6 +168,10 @@ export class AdbSync extends AutoDisposable {
168
168
  });
169
169
  }
170
170
 
171
+ lockSocket(): Promise<AdbSyncSocketLocked> {
172
+ return this._socket.lock();
173
+ }
174
+
171
175
  override async dispose() {
172
176
  super.dispose();
173
177
  await this._socket.close();
@@ -47,7 +47,7 @@ export class AdbTcpIpCommand extends AdbCommandBase {
47
47
 
48
48
  async setPort(port: number): Promise<string> {
49
49
  if (port <= 0) {
50
- throw new Error(`Invalid port ${port}`);
50
+ throw new TypeError(`Invalid port ${port}`);
51
51
  }
52
52
 
53
53
  const output = await this.adb.createSocketAndWait(`tcpip:${port}`);
@@ -83,7 +83,7 @@ export const AdbSignatureAuthenticator: AdbAuthenticator = async function* (
83
83
  command: AdbCommand.Auth,
84
84
  arg0: AdbAuthType.Signature,
85
85
  arg1: 0,
86
- payload: new Uint8Array(signature),
86
+ payload: signature,
87
87
  };
88
88
  }
89
89
  };
@@ -1,7 +1,8 @@
1
1
  import {
2
- getBigUint64,
3
- setBigUint64,
4
- } from "@yume-chan/dataview-bigint-polyfill/esm/fallback.js";
2
+ getUint64BigEndian,
3
+ setInt64BigEndian,
4
+ setInt64LittleEndian,
5
+ } from "@yume-chan/no-data-view";
5
6
 
6
7
  /**
7
8
  * Gets the `BigInt` value at the specified byte offset and length from the start of the view. There is
@@ -11,7 +12,7 @@ import {
11
12
  * @param byteOffset The place in the buffer at which the value should be retrieved.
12
13
  */
13
14
  export function getBigUint(
14
- dataView: DataView,
15
+ array: Uint8Array,
15
16
  byteOffset: number,
16
17
  length: number,
17
18
  ): bigint {
@@ -22,8 +23,8 @@ export function getBigUint(
22
23
 
23
24
  for (let i = byteOffset; i < byteOffset + length; i += 8) {
24
25
  result <<= 64n;
25
- const value = getBigUint64(dataView, i, false);
26
- result += value;
26
+ const value = getUint64BigEndian(array, i);
27
+ result |= value;
27
28
  }
28
29
 
29
30
  return result;
@@ -32,40 +33,32 @@ export function getBigUint(
32
33
  /**
33
34
  * Stores an arbitrary-precision positive `BigInt` value at the specified byte offset from the start of the view.
34
35
  * @param byteOffset The place in the buffer at which the value should be set.
36
+ * @param length The number of bytes to set.
35
37
  * @param value The value to set.
36
38
  * @param littleEndian If `false` or `undefined`, a big-endian value should be written,
37
39
  * otherwise a little-endian value should be written.
38
40
  */
39
41
  export function setBigUint(
40
- dataView: DataView,
42
+ array: Uint8Array,
41
43
  byteOffset: number,
44
+ length: number,
42
45
  value: bigint,
43
46
  littleEndian?: boolean,
44
47
  ) {
45
- const start = byteOffset;
46
-
47
48
  if (littleEndian) {
48
49
  while (value > 0n) {
49
- setBigUint64(dataView, byteOffset, value, true);
50
+ setInt64LittleEndian(array, byteOffset, value);
50
51
  byteOffset += 8;
51
52
  value >>= 64n;
52
53
  }
53
54
  } else {
54
- // Because we don't know how long (in bits) the `value` is,
55
- // Convert it to an array of `uint64` first.
56
- const uint64Array: bigint[] = [];
55
+ let position = byteOffset + length - 8;
57
56
  while (value > 0n) {
58
- uint64Array.push(BigInt.asUintN(64, value));
57
+ setInt64BigEndian(array, position, value);
58
+ position -= 8;
59
59
  value >>= 64n;
60
60
  }
61
-
62
- for (let i = uint64Array.length - 1; i >= 0; i -= 1) {
63
- setBigUint64(dataView, byteOffset, uint64Array[i]!, false);
64
- byteOffset += 8;
65
- }
66
61
  }
67
-
68
- return byteOffset - start;
69
62
  }
70
63
 
71
64
  // These values are correct only if
@@ -95,17 +88,26 @@ const RsaPrivateKeyDOffset = 303;
95
88
  const RsaPrivateKeyDLength = 2048 / 8;
96
89
 
97
90
  export function rsaParsePrivateKey(key: Uint8Array): [n: bigint, d: bigint] {
98
- const view = new DataView(key.buffer, key.byteOffset, key.byteLength);
99
- const n = getBigUint(view, RsaPrivateKeyNOffset, RsaPrivateKeyNLength);
100
- const d = getBigUint(view, RsaPrivateKeyDOffset, RsaPrivateKeyDLength);
91
+ const n = getBigUint(key, RsaPrivateKeyNOffset, RsaPrivateKeyNLength);
92
+ const d = getBigUint(key, RsaPrivateKeyDOffset, RsaPrivateKeyDLength);
101
93
  return [n, d];
102
94
  }
103
95
 
96
+ function nonNegativeMod(m: number, d: number) {
97
+ const r = m % d;
98
+ if (r > 0) {
99
+ return r;
100
+ }
101
+ return r + (d > 0 ? d : -d);
102
+ }
103
+
104
+ // https://en.wikipedia.org/wiki/Modular_multiplicative_inverse
105
+ // Solve for the smallest positive `x` in the equation `a * x ≡ 1 (mod m)`,
106
+ // or in other words, `a * x % m = 1`
104
107
  // Taken from https://stackoverflow.com/a/51562038
105
- // I can't understand, but it does work
106
108
  // Only used with numbers smaller than 2^32 so doesn't need BigInt
107
109
  export function modInverse(a: number, m: number) {
108
- a = ((a % m) + m) % m;
110
+ a = nonNegativeMod(a, m);
109
111
  if (!a || m < 2) {
110
112
  return NaN; // invalid input
111
113
  }
@@ -116,6 +118,7 @@ export function modInverse(a: number, m: number) {
116
118
  [a, b] = [b, a % b];
117
119
  s.push({ a, b });
118
120
  }
121
+ /* istanbul ignore next */
119
122
  if (a !== 1) {
120
123
  return NaN; // inverse does not exists
121
124
  }
@@ -125,11 +128,14 @@ export function modInverse(a: number, m: number) {
125
128
  for (let i = s.length - 2; i >= 0; i -= 1) {
126
129
  [x, y] = [y, x - y * Math.floor(s[i]!.a / s[i]!.b)];
127
130
  }
128
- return ((y % m) + m) % m;
131
+ return nonNegativeMod(y, m);
129
132
  }
130
133
 
134
+ const ModulusLengthInBytes = 2048 / 8;
135
+ const ModulusLengthInWords = ModulusLengthInBytes / 4;
136
+
131
137
  export function adbGetPublicKeySize() {
132
- return 4 + 4 + 2048 / 8 + 2048 / 8 + 4;
138
+ return 4 + 4 + ModulusLengthInBytes + ModulusLengthInBytes + 4;
133
139
  }
134
140
 
135
141
  export function adbGeneratePublicKey(privateKey: Uint8Array): Uint8Array;
@@ -141,22 +147,27 @@ export function adbGeneratePublicKey(
141
147
  privateKey: Uint8Array,
142
148
  output?: Uint8Array,
143
149
  ): Uint8Array | number {
144
- // Android has its own public key generation algorithm
145
- // See https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#111
146
-
147
- // The public key is an array of
150
+ // cspell: ignore: mincrypt
151
+ // Android 6 and earlier has its own encryption library called mincrypt
152
+ // This is the RSA public key format used by mincrypt:
153
+ // https://android.googlesource.com/platform/system/core/+/bb0c180e62703c2068a1b2c9f8ba6d634bf1553c/include/mincrypt/rsa.h#46
154
+ // `n0inv` and `rr` are pre-calculated to speed up RSA operations
155
+
156
+ // Android 7 switched its encryption library to BoringSSL, but still keeps the key format:
157
+ // https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#38
158
+ // Except when reading a key, `n0inv` and `rr` are ignored (they are still populated when generating a key):
159
+ // https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#55
160
+
161
+ // The public key is a struct (in little endian) of:
148
162
  //
149
163
  // [
150
164
  // modulusLengthInWords, // 32-bit integer, a "word" is 32-bit so it must be 2048 / 8 / 4
151
- // // Actually the comment in Android source code was wrong
152
- // n0inv, // 32-bit integer, the modular inverse of (low 32 bits of n)
153
- // modulus, // n
165
+ // // (the comment in Android source code is incorrect saying "This must be ANDROID_PUBKEY_MODULUS_SIZE")
166
+ // n0inv, // 32-bit integer, the modular inverse of (lower 32 bits of `n`)
167
+ // modulus, // `n`
154
168
  // rr, // Montgomery parameter R^2
155
- // exponent, // 32-bit integer, must be 65537
169
+ // exponent, // 32-bit integer, must be 3 or 65537
156
170
  // ]
157
- //
158
- // (All in little endian)
159
- // See https://android.googlesource.com/platform/system/core.git/+/91784040db2b9273687f88d8b95f729d4a61ecc2/libcrypto_utils/android_pubkey.cpp#38
160
171
 
161
172
  let outputType: "Uint8Array" | "number";
162
173
  const outputLength = adbGetPublicKeySize();
@@ -164,8 +175,8 @@ export function adbGeneratePublicKey(
164
175
  output = new Uint8Array(outputLength);
165
176
  outputType = "Uint8Array";
166
177
  } else {
167
- if (output.byteLength < outputLength) {
168
- throw new Error("output buffer is too small");
178
+ if (output.length < outputLength) {
179
+ throw new TypeError("output buffer is too small");
169
180
  }
170
181
 
171
182
  outputType = "number";
@@ -174,31 +185,30 @@ export function adbGeneratePublicKey(
174
185
  const outputView = new DataView(
175
186
  output.buffer,
176
187
  output.byteOffset,
177
- output.byteLength,
188
+ output.length,
178
189
  );
179
190
  let outputOffset = 0;
180
191
 
181
192
  // modulusLengthInWords
182
- outputView.setUint32(outputOffset, 2048 / 8 / 4, true);
193
+ outputView.setUint32(outputOffset, ModulusLengthInWords, true);
183
194
  outputOffset += 4;
184
195
 
185
196
  // extract `n` from private key
186
197
  const [n] = rsaParsePrivateKey(privateKey);
187
198
 
188
199
  // Calculate `n0inv`
189
- // Don't know why need to multiply by -1
190
- // Didn't exist in Android codebase
191
- const n0inv = modInverse(-Number(BigInt.asUintN(32, n)), 2 ** 32);
192
- outputView.setUint32(outputOffset, n0inv, true);
200
+ const n0inv = -modInverse(Number(n % 2n ** 32n), 2 ** 32);
201
+ outputView.setInt32(outputOffset, n0inv, true);
193
202
  outputOffset += 4;
194
203
 
195
- // Write n
196
- setBigUint(outputView, outputOffset, n, true);
197
- outputOffset += 256;
204
+ // Write `n` (a.k.a. `modulus`)
205
+ setBigUint(output, outputOffset, ModulusLengthInBytes, n, true);
206
+ outputOffset += ModulusLengthInBytes;
198
207
 
199
- // Calculate rr = (2^(rsa_size)) ^ 2 mod n
208
+ // Calculate rr = (2 ** (rsa_size)) ** 2 % n
200
209
  const rr = 2n ** 4096n % n;
201
- outputOffset += setBigUint(outputView, outputOffset, rr, true);
210
+ setBigUint(output, outputOffset, ModulusLengthInBytes, rr, true);
211
+ outputOffset += ModulusLengthInBytes;
202
212
 
203
213
  // exponent
204
214
  outputView.setUint32(outputOffset, 65537, true);
@@ -305,12 +315,11 @@ export function rsaSign(privateKey: Uint8Array, data: Uint8Array): Uint8Array {
305
315
 
306
316
  // Encryption
307
317
  // signature = padded ** d % n
308
- const view = new DataView(padded.buffer);
309
- const signature = powMod(getBigUint(view, 0, view.byteLength), d, n);
318
+ const signature = powMod(getBigUint(padded, 0, padded.length), d, n);
310
319
 
311
320
  // `padded` is not used anymore,
312
321
  // re-use the buffer to store the result
313
- setBigUint(view, 0, signature, false);
322
+ setBigUint(padded, 0, padded.length, signature, false);
314
323
 
315
324
  return padded;
316
325
  }