From c2e741ef16d17cb23eb0b5fa8eb5de8d4b4dc1bd Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 19 Jan 2025 14:42:36 +0800 Subject: [PATCH] feat: add deno-github-mod.ts, update deno-sshsig-mod.ts --- libraries/deno-github-mod.ts | 38 ++++++++++++++++++++++++++++++++++++ libraries/deno-sshsig-mod.ts | 25 ++++++++++++++++++++---- 2 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 libraries/deno-github-mod.ts diff --git a/libraries/deno-github-mod.ts b/libraries/deno-github-mod.ts new file mode 100644 index 0000000..6d9b4b7 --- /dev/null +++ b/libraries/deno-github-mod.ts @@ -0,0 +1,38 @@ +import { + getFetchAutoProxyInit, +} from "https://hatter.ink/script/fetch/library/deno-fetch-auto-proxy-mod.ts?202501191421"; + +export class SshKey { + algorithm: string; + material: string; + description?: string; + + constructor( + algorithm: string, + material: string, + description: string | undefined, + ) { + this.algorithm = algorithm; + this.material = material; + this.description = description; + } + + static parseSshKey(key: string): SshKey { + const keyParts = key.split(/\s+/); + if (keyParts.length < 2 || keyParts.length > 3) { + throw `Bad SSH key format ${key}`; + } + return new SshKey( + keyParts[0], + keyParts[1], + (keyParts.length > 2) ? keyParts[2] : undefined, + ); + } +} + +export async function fetchKeys(username: string): Promise> { + const url = `https://github.com/${username}.keys`; + const response = await fetch(url, getFetchAutoProxyInit()); + const responseText = await response.text(); + return responseText.trim().split("\n").map((k) => SshKey.parseSshKey(k)); +} diff --git a/libraries/deno-sshsig-mod.ts b/libraries/deno-sshsig-mod.ts index 9981961..c14c346 100644 --- a/libraries/deno-sshsig-mod.ts +++ b/libraries/deno-sshsig-mod.ts @@ -1,5 +1,5 @@ import { crypto } from "jsr:@std/crypto"; -import { decodeBase64 } from "jsr:@std/encoding/base64"; +import { decodeBase64, encodeBase64 } from "jsr:@std/encoding/base64"; import { encodeBase64Url } from "jsr:@std/encoding/base64url"; import { decodeHex, encodeHex } from "jsr:@std/encoding/hex"; @@ -139,7 +139,7 @@ class BinaryReader { } } -class SshSignature { +export class SshSignature { publicKey: SshPublicKey; namespace: string; hashAlgorithm: string; @@ -257,7 +257,7 @@ class SshSignature { } } -class SshSignatureValue { +export class SshSignatureValue { signatureAlgorithm: string; ecSignatureR: Uint8Array; ecSignatureS: Uint8Array; @@ -330,16 +330,19 @@ class SshSignatureValue { } } -class SshPublicKey { +export class SshPublicKey { + raw: Uint8Array; signatureAlgorithm: string; algorithm: string; publicKeyPoint: Uint8Array; constructor( + raw: Uint8Array, signatureAlgorithm: string, algorithm: string, publicKeyPoint: Uint8Array, ) { + this.raw = raw; this.signatureAlgorithm = signatureAlgorithm; this.algorithm = algorithm; this.publicKeyPoint = publicKeyPoint; @@ -354,12 +357,22 @@ class SshPublicKey { throw `Not supported signature algorithm ${signatureAlgorithm} or curve algorithm ${curveAlgorithm}`; } return new SshPublicKey( + buffer, signatureAlgorithm, curveAlgorithm, publicKeyPoint, ); } + toSshKeyFormat(description?: string | undefined): string { + const suffix = description ? ` ${description}` : ""; + return `${this.signatureAlgorithm} ${this.asRawBase64()}${suffix}`; + } + + asRawBase64(): string { + return encodeBase64(this.raw); + } + toDer(): Uint8Array { const writer = new BinaryWriter(); if (this.algorithm === "nistp256") { @@ -395,6 +408,7 @@ class SshPublicKey { ); } + // deno-lint-ignore no-explicit-any toJwk(): any { if (this.publicKeyPoint[0] !== 0x04) { throw `Invalid EC public key point: ${ @@ -509,6 +523,9 @@ WRxFFw== const data = new TextDecoder(ENCODING_UTF8).decode( decodeBase64("aGVsbG8gaGF0dGVyIDIwMjUK"), ); + console.log( + sshSignature.publicKey.toSshKeyFormat(), + ); console.log(await sshSignature.verifyString(data)); }