mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-03 17:59:50 +02:00
chore: fix review comments
This commit is contained in:
parent
538d743c7b
commit
c280a447e4
5 changed files with 103 additions and 58 deletions
|
@ -22,7 +22,42 @@ import {
|
|||
} from "@yume-chan/adb";
|
||||
import type { TangoKey, TangoKeyStorage } from "@yume-chan/adb-credential-web";
|
||||
|
||||
class KeyError extends Error {
|
||||
path: string;
|
||||
|
||||
constructor(message: string, path: string, options?: ErrorOptions) {
|
||||
super(message, options);
|
||||
this.path = path;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can't read or parse a private key file.
|
||||
*
|
||||
* Check `path` for file path, and `cause` for the error.
|
||||
*/
|
||||
class InvalidKeyError extends KeyError {
|
||||
constructor(path: string, options?: ErrorOptions) {
|
||||
super(`Can't read private key file at "${path}"`, path, options);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Can't read or parse a vendor key.
|
||||
*
|
||||
* Check `path` for file path, and `cause` for the error.
|
||||
*/
|
||||
class VendorKeyError extends KeyError {
|
||||
constructor(path: string, options?: ErrorOptions) {
|
||||
super(`Can't read vendor key file at "${path}"`, path, options);
|
||||
}
|
||||
}
|
||||
|
||||
export class TangoNodeStorage implements TangoKeyStorage {
|
||||
static readonly KeyError = KeyError;
|
||||
static readonly InvalidKeyError = InvalidKeyError;
|
||||
static readonly VendorKeyError = VendorKeyError;
|
||||
|
||||
async #getAndroidDirPath() {
|
||||
const dir = resolve(homedir(), ".android");
|
||||
await mkdir(dir, { mode: 0o750, recursive: true });
|
||||
|
@ -74,7 +109,7 @@ export class TangoNodeStorage implements TangoKeyStorage {
|
|||
.replaceAll(/\x20|\t|\r|\n|\v|\f/g, ""),
|
||||
);
|
||||
} catch (e) {
|
||||
throw new Error("Invalid private key file: " + path, { cause: e });
|
||||
throw new InvalidKeyError(path, { cause: e });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,23 +141,32 @@ export class TangoNodeStorage implements TangoKeyStorage {
|
|||
async *#readVendorKeys(
|
||||
path: string,
|
||||
): AsyncGenerator<MaybeError<TangoKey>, void, void> {
|
||||
const stats = await stat(path);
|
||||
let stats;
|
||||
try {
|
||||
stats = await stat(path);
|
||||
} catch (e) {
|
||||
return yield new VendorKeyError(path, { cause: e });
|
||||
}
|
||||
|
||||
if (stats.isFile()) {
|
||||
try {
|
||||
yield await this.#readKey(path);
|
||||
return yield await this.#readKey(path);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
yield e;
|
||||
} else {
|
||||
yield new Error(String(e));
|
||||
}
|
||||
return yield e instanceof KeyError
|
||||
? e
|
||||
: new VendorKeyError(path, { cause: e });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (stats.isDirectory()) {
|
||||
for await (const dirent of await opendir(path)) {
|
||||
let dir;
|
||||
try {
|
||||
dir = await opendir(path);
|
||||
} catch (e) {
|
||||
return yield new VendorKeyError(path, { cause: e });
|
||||
}
|
||||
|
||||
for await (const dirent of dir) {
|
||||
if (!dirent.isFile()) {
|
||||
continue;
|
||||
}
|
||||
|
@ -131,14 +175,13 @@ export class TangoNodeStorage implements TangoKeyStorage {
|
|||
continue;
|
||||
}
|
||||
|
||||
const file = resolve(path, dirent.name);
|
||||
try {
|
||||
yield await this.#readKey(resolve(path, dirent.name));
|
||||
yield await this.#readKey(file);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
yield e;
|
||||
} else {
|
||||
yield new Error(String(e));
|
||||
}
|
||||
yield e instanceof KeyError
|
||||
? e
|
||||
: new VendorKeyError(path, { cause: e });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -150,11 +193,9 @@ export class TangoNodeStorage implements TangoKeyStorage {
|
|||
try {
|
||||
yield await this.#readKey(userKeyPath);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
yield e;
|
||||
} else {
|
||||
yield new Error(String(e));
|
||||
}
|
||||
yield e instanceof KeyError
|
||||
? e
|
||||
: new InvalidKeyError(userKeyPath, { cause: e });
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,6 +209,12 @@ export class TangoNodeStorage implements TangoKeyStorage {
|
|||
}
|
||||
}
|
||||
|
||||
export namespace TangoNodeStorage {
|
||||
export type KeyError = typeof KeyError;
|
||||
export type InvalidKeyError = typeof InvalidKeyError;
|
||||
export type VendorKeyError = typeof VendorKeyError;
|
||||
}
|
||||
|
||||
// Re-export everything except Web-only storages
|
||||
export {
|
||||
AdbWebCryptoCredentialStore,
|
||||
|
|
|
@ -133,28 +133,27 @@ export class TangoPasswordProtectedStorage implements TangoKeyStorage {
|
|||
bundle.encrypted as Uint8Array<ArrayBuffer>,
|
||||
);
|
||||
|
||||
yield {
|
||||
privateKey: new Uint8Array(decrypted),
|
||||
name,
|
||||
};
|
||||
|
||||
// Clear secret memory
|
||||
// * No way to clear `password` and `aesKey`
|
||||
// * all values in `bundle` are not secrets
|
||||
// * Caller is not allowed to use `decrypted` after `yield` returns
|
||||
new Uint8Array(decrypted).fill(0);
|
||||
try {
|
||||
yield {
|
||||
privateKey: new Uint8Array(decrypted),
|
||||
name,
|
||||
};
|
||||
} finally {
|
||||
// Clear secret memory
|
||||
// * No way to clear `password` and `aesKey`
|
||||
// * all values in `bundle` are not secrets
|
||||
// * Caller is not allowed to use `decrypted` after `yield` returns
|
||||
new Uint8Array(decrypted).fill(0);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof DOMException && e.name === "OperationError") {
|
||||
yield new PasswordIncorrectError();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e instanceof Error) {
|
||||
yield e;
|
||||
continue;
|
||||
}
|
||||
|
||||
yield new Error(String(e));
|
||||
yield e instanceof Error
|
||||
? e
|
||||
: new Error(String(e), { cause: e });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -187,11 +187,9 @@ export class TangoPrfStorage implements TangoKeyStorage {
|
|||
new Uint8Array(decrypted).fill(0);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
yield e;
|
||||
} else {
|
||||
yield new Error(String(e));
|
||||
}
|
||||
yield e instanceof Error
|
||||
? e
|
||||
: new Error(String(e), { cause: e });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,11 +66,17 @@ export class AdbWebCryptoCredentialStore implements AdbCredentialStore {
|
|||
continue;
|
||||
}
|
||||
|
||||
// `privateKey` is owned by `#storage` and will be cleared by it
|
||||
yield {
|
||||
...rsaParsePrivateKey(result.privateKey),
|
||||
name: result.name ?? this.#name,
|
||||
};
|
||||
try {
|
||||
// `privateKey` is owned by `#storage` and will be cleared by it
|
||||
yield {
|
||||
...rsaParsePrivateKey(result.privateKey),
|
||||
name: result.name ?? this.#name,
|
||||
};
|
||||
} catch (e) {
|
||||
yield e instanceof Error
|
||||
? e
|
||||
: new Error(String(e), { cause: e });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,7 +77,7 @@ export class AdbDefaultAuthenticator implements AdbAuthenticator {
|
|||
| AsyncIterator<MaybeError<AdbPrivateKey>, void, void>
|
||||
| undefined;
|
||||
|
||||
#prevFingerprint: string | undefined;
|
||||
#prevKeyInfo: AdbKeyInfo | undefined;
|
||||
#firstKey: AdbPrivateKey | undefined;
|
||||
|
||||
#onKeyLoadError = new EventEmitter<Error>();
|
||||
|
@ -130,19 +130,14 @@ export class AdbDefaultAuthenticator implements AdbAuthenticator {
|
|||
this.#firstKey = result;
|
||||
}
|
||||
|
||||
if (this.#prevFingerprint) {
|
||||
this.#onSignatureRejected.fire({
|
||||
fingerprint: this.#prevFingerprint,
|
||||
name: result.name,
|
||||
});
|
||||
// A new token implies the previous signature was rejected.
|
||||
if (this.#prevKeyInfo) {
|
||||
this.#onSignatureRejected.fire(this.#prevKeyInfo);
|
||||
}
|
||||
|
||||
const fingerprint = getFingerprint(result);
|
||||
this.#prevFingerprint = fingerprint;
|
||||
this.#onSignatureAuthentication.fire({
|
||||
fingerprint,
|
||||
name: result.name,
|
||||
});
|
||||
this.#prevKeyInfo = { fingerprint, name: result.name };
|
||||
this.#onSignatureAuthentication.fire(this.#prevKeyInfo);
|
||||
|
||||
return {
|
||||
command: AdbCommand.Auth,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue