33 lines
1.2 KiB
TypeScript
33 lines
1.2 KiB
TypeScript
/// SSH-style SHA256 fingerprint of an ed25519 public key, computed in the
|
|
/// extension so devices.ts can display verifiable IDs without a SW round-trip.
|
|
/// Output format matches `ssh-keygen -lf` and `relicario device list`:
|
|
/// SHA256:<base64-no-pad of SHA256(decoded-key-blob)>
|
|
|
|
function base64Decode(b64: string): Uint8Array | null {
|
|
try {
|
|
const bin = atob(b64);
|
|
const out = new Uint8Array(bin.length);
|
|
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
|
|
return out;
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
function base64Encode(bytes: Uint8Array): string {
|
|
let s = '';
|
|
for (let i = 0; i < bytes.length; i++) s += String.fromCharCode(bytes[i]);
|
|
return btoa(s);
|
|
}
|
|
|
|
export async function sshFingerprint(publicKey: string): Promise<string | null> {
|
|
if (!publicKey) return null;
|
|
const parts = publicKey.trim().split(/\s+/);
|
|
if (parts.length < 2) return null; // need "<algo> <blob>"
|
|
const blob = base64Decode(parts[1]);
|
|
if (!blob || blob.length === 0) return null;
|
|
const hash = await crypto.subtle.digest('SHA-256', blob.buffer as ArrayBuffer);
|
|
const b64 = base64Encode(new Uint8Array(hash)).replace(/=+$/, '');
|
|
return `SHA256:${b64}`;
|
|
}
|