Skip to content

Commit

Permalink
Introduce Writer for safe bytes writes
Browse files Browse the repository at this point in the history
  • Loading branch information
wiktor-k committed Mar 11, 2024
1 parent 60cb736 commit 5097208
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 30 deletions.
2 changes: 1 addition & 1 deletion reader.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export class Reader {
pos = 0;
private pos = 0;
constructor(private view: DataView) {
this.view = view;
}
Expand Down
56 changes: 27 additions & 29 deletions verifier.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Sig } from "./sig.ts";
import { convertAlgorithm, convertHash, convertPublicKey } from "./formats.ts";
import { Writer } from "./writer.ts";

export async function verify(
subtle: SubtleCrypto,
Expand All @@ -20,68 +21,65 @@ export async function verify(
"verify",
],
);

const writer = new Writer(100);
// https://github.com/openssh/openssh-portable/blob/d575cf44895104e0fcb0629920fb645207218129/PROTOCOL.sshsig
// MAGIC_PREAMBLE
const data = Array.prototype.map.call("SSHSIG", (x) => x.charCodeAt(0));
writer.writeBytes("SSHSIG");
// namespace
data.push(...[0, 0, 0, 4]);
data.push(...Array.prototype.map.call("file", (x) => x.charCodeAt(0)));
writer.writeString("file");
// reserved
data.push(...[0, 0, 0, 0]);
writer.writeUint32(0);
// hash_algorithm
const hash = signature.hash_algorithm;
data.push(...[0, 0, 0, hash.length]);
data.push(...Array.prototype.map.call(hash, (x) => x.charCodeAt(0)));
writer.writeString(hash);
const digest = new Uint8Array(
await subtle.digest(
convertHash(hash),
signed_data,
),
);
data.push(...[0, 0, 0, digest.length]);
data.push(...digest);
writer.writeString(digest);

const data = writer.bytes();

if (
signature.publickey.pk_algo === "[email protected]" ||
signature.publickey.pk_algo === "[email protected]"
) {
// https://fuchsia.googlesource.com/third_party/openssh-portable/+/refs/heads/main/PROTOCOL.u2f#176
const u2f_data = [];
u2f_data.push(
...new Uint8Array(
await subtle.digest(
"SHA-256",
Uint8Array.from(
Array.prototype.map.call(
signature.publickey.application,
(x) => x.charCodeAt(0),
) as unknown as number[],
),
const u2f_data = new Writer(100);
u2f_data.writeBytes(
await subtle.digest(
"SHA-256",
Uint8Array.from(
Array.prototype.map.call(
signature.publickey.application,
(x) => x.charCodeAt(0),
) as unknown as number[],
),
),
);
u2f_data.push(signature.signature.flags);
u2f_data.push(...[0, 0, 0, signature.signature.counter]);
u2f_data.push(
...new Uint8Array(
await subtle.digest(
"SHA-256",
Uint8Array.from(data as unknown as number[]),
),
u2f_data.writeByte(signature.signature.flags || 0);
u2f_data.writeUint32(signature.signature.counter || 0);
u2f_data.writeBytes(
await subtle.digest(
"SHA-256",
data,
),
);
return await subtle.verify(
algorithm,
key,
signature.signature.raw_signature,
new Uint8Array(u2f_data as unknown as number[]),
u2f_data.bytes(),
);
}

return await subtle.verify(
algorithm,
key,
signature.signature.raw_signature,
new Uint8Array(data as unknown as number[]),
data,
);
}
51 changes: 51 additions & 0 deletions writer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
export class Writer {
private pos = 0;
private buffer: ArrayBuffer;
private view: DataView;
constructor(maxByteLength: number) {
this.buffer = new (ArrayBuffer as unknown as {
new (a: number, b: { maxByteLength: number }): ArrayBuffer;
})(0, { maxByteLength });
this.view = new DataView(this.buffer);
}
private addResize(num: number) {
(this.buffer as unknown as { resize(num: number): void }).resize(
this.buffer.byteLength + num,
);
}
writeUint32(num: number) {
this.addResize(4);
this.view.setUint32(this.pos, num);
this.pos += 4;
}
writeString(bytes: ArrayLike<number> | string) {
this.writeUint32(bytes.length);
this.writeBytes(bytes);
}
writeBytes(bytes: ArrayLike<number> | string | ArrayBuffer) {
let uint8bytes;
if (typeof bytes === "string") {
uint8bytes = Array.prototype.map.call(
bytes,
(x) => x.charCodeAt(0),
) as number[];
} else if (bytes instanceof ArrayBuffer) {
uint8bytes = new Uint8Array(bytes);
} else {
uint8bytes = bytes;
}
// it's more efficient to resize once and write all bytes
// instead of calling `writeByte` in each iteration
this.addResize(uint8bytes.length);
for (let i = 0; i < uint8bytes.length; i++) {
this.view.setUint8(this.pos++, uint8bytes[i]);
}
}
writeByte(byte: number) {
this.addResize(1);
this.view.setUint8(this.pos++, byte);
}
bytes() {
return new Uint8Array(this.buffer);
}
}

0 comments on commit 5097208

Please sign in to comment.