the universal sandbox runtime for agents and humans. pocketenv.io
sandbox openclaw agent claude-code vercel-sandbox deno-sandbox cloudflare-sandbox atproto sprites daytona
7
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 1241cd4e999bb3f48dca4cab7bcff14fe6f5d6ad 122 lines 3.4 kB view raw
1import sodium from "libsodium-wrappers"; 2 3export type SSHKeyPair = { 4 publicKey: string; 5 privateKey: string; 6 seedBase64: string; 7}; 8 9function u32(n: number): Uint8Array { 10 return new Uint8Array([ 11 (n >>> 24) & 0xff, 12 (n >>> 16) & 0xff, 13 (n >>> 8) & 0xff, 14 n & 0xff, 15 ]); 16} 17 18function concatBytes(...arrays: Uint8Array[]): Uint8Array { 19 const total = arrays.reduce((sum, arr) => sum + arr.length, 0); 20 const out = new Uint8Array(total); 21 let offset = 0; 22 for (const arr of arrays) { 23 out.set(arr, offset); 24 offset += arr.length; 25 } 26 return out; 27} 28 29function sshString(bytes: Uint8Array): Uint8Array { 30 return concatBytes(u32(bytes.length), bytes); 31} 32 33function text(value: string): Uint8Array { 34 return new TextEncoder().encode(value); 35} 36 37function wrapPem(label: string, bytes: Uint8Array): string { 38 const base64 = sodium.to_base64(bytes, sodium.base64_variants.ORIGINAL); 39 const lines = base64.match(/.{1,70}/g)?.join("\n") ?? base64; 40 return `-----BEGIN ${label}-----\n${lines}\n-----END ${label}-----\n`; 41} 42 43function buildEd25519PublicKeyBlob(publicKey: Uint8Array): Uint8Array { 44 return concatBytes(sshString(text("ssh-ed25519")), sshString(publicKey)); 45} 46 47function publicLineFromPublicKey( 48 publicKey: Uint8Array, 49 comment: string, 50): string { 51 const blob = buildEd25519PublicKeyBlob(publicKey); 52 return `ssh-ed25519 ${sodium.to_base64(blob, sodium.base64_variants.ORIGINAL)} ${comment}`; 53} 54 55function buildOpenSSHEd25519PrivateKey( 56 publicKey: Uint8Array, 57 seed: Uint8Array, 58 comment: string, 59): string { 60 if (publicKey.length !== 32) throw new Error("Invalid public key length"); 61 if (seed.length !== 32) throw new Error("Invalid seed length"); 62 63 const privateKey64 = concatBytes(seed, publicKey); 64 const publicBlob = buildEd25519PublicKeyBlob(publicKey); 65 const checkint = crypto.getRandomValues(new Uint32Array(1))[0]!; 66 const commentBytes = text(comment); 67 68 const privateSectionWithoutPadding = concatBytes( 69 u32(checkint), 70 u32(checkint), 71 sshString(text("ssh-ed25519")), 72 sshString(publicKey), 73 sshString(privateKey64), 74 sshString(commentBytes), 75 ); 76 77 const blockSize = 8; 78 const remainder = privateSectionWithoutPadding.length % blockSize; 79 const padLen = remainder === 0 ? 0 : blockSize - remainder; 80 81 const padding = new Uint8Array(padLen); 82 for (let i = 0; i < padLen; i++) padding[i] = i + 1; 83 84 const privateSection = concatBytes(privateSectionWithoutPadding, padding); 85 86 const opensshKey = concatBytes( 87 text("openssh-key-v1\0"), 88 sshString(text("none")), 89 sshString(text("none")), 90 sshString(new Uint8Array()), 91 u32(1), 92 sshString(publicBlob), 93 sshString(privateSection), 94 ); 95 96 return wrapPem("OPENSSH PRIVATE KEY", opensshKey); 97} 98 99export async function generateEd25519SSHKeyPair( 100 comment = "user@browser", 101): Promise<SSHKeyPair> { 102 await sodium.ready; 103 104 const seed = new Uint8Array(32); 105 crypto.getRandomValues(seed); 106 107 const kp = sodium.crypto_sign_seed_keypair(seed); 108 const publicKey = new Uint8Array(kp.publicKey); 109 110 const publicKeyOpenSSH = publicLineFromPublicKey(publicKey, comment); 111 const privateKeyOpenSSH = buildOpenSSHEd25519PrivateKey( 112 publicKey, 113 seed, 114 comment, 115 ); 116 117 return { 118 publicKey: publicKeyOpenSSH, 119 privateKey: privateKeyOpenSSH, 120 seedBase64: sodium.to_base64(seed, sodium.base64_variants.ORIGINAL), 121 }; 122}