Added utils for cryptography and filesystem interactions

This commit is contained in:
Christian Risi 2025-06-29 17:52:46 +00:00
parent 002fd58585
commit 932b770c8f
4 changed files with 184 additions and 1 deletions

View File

@ -1,3 +1,5 @@
export const PKG = __PKG__
export const DB_PATH = "src/db/db.sqlite"
// TODO: Add jose keys
export const SERVER_PRIVATE_DIR = "src/private"
export const SERVER_PRIVATE_KEY_PATH = `${SERVER_PRIVATE_DIR}/key.pem`
export const SERVER_PUBLIC_KEY_PATH = `${SERVER_PRIVATE_DIR}/pub.pem`

View File

@ -0,0 +1,29 @@
export async function doesFileExists(path: string) {
const file = Bun.file(path)
return file.exists()
}
export async function loadFile(path: string, create?: boolean): Promise<Bun.BunFile> {
if (!create) {
create = false
}
if (await doesFileExists(path)) {
return Bun.file(path)
}
if (create) {
const file = Bun.file(path)
file.write("")
return file
}
// UGLY: make more specific
throw new Error("The required file does not exist")
}

View File

@ -0,0 +1,103 @@
import * as jose from "jose";
import { loadFile } from "./filesystem-utils";
import { SERVER_PRIVATE_KEY_PATH, SERVER_PUBLIC_KEY_PATH } from "./constants";
export class JoseSingleton {
private static initialized = false
private static privateKey: CryptoKey
private static publicKey: CryptoKey
public static async init() {
JoseSingleton.assureNotInitialized()
JoseSingleton.privateKey = await JoseSingleton.loadPrivateKey()
JoseSingleton.publicKey = await JoseSingleton.loadPublicKey()
}
private static async loadPrivateKey() {
JoseSingleton.assureNotInitialized()
const privateKeyFile = await loadFile(SERVER_PRIVATE_KEY_PATH)
return await jose.importPKCS8(
await privateKeyFile.text(),
"ES512"
)
}
private static async loadPublicKey() {
JoseSingleton.assureNotInitialized()
const publicKeyFile = await loadFile(SERVER_PUBLIC_KEY_PATH)
return await jose.importPKCS8(
await publicKeyFile.text(),
"ES512"
)
}
public static async signObject(object: any) {
JoseSingleton.assureInitialized()
const payload = new TextEncoder().encode(
JSON.stringify(object)
)
return await new jose.CompactSign(
payload
).setProtectedHeader({
alg: "ES512"
}).sign(JoseSingleton.privateKey)
}
public static async verifyObject(jwt: string) {
JoseSingleton.assureInitialized()
let _payload: Uint8Array
try {
const { payload, protectedHeader } = await jose.compactVerify(
jwt,
JoseSingleton.publicKey
)
_payload = payload
} catch {
return null
}
return JSON.parse(
new TextDecoder().decode(_payload)
)
}
private static assureInitialized() {
if (!JoseSingleton.initialized) {
// UGLY: Be specific
throw new Error("JoseSingleton hasn't been initialized")
}
}
private static assureNotInitialized() {
if (JoseSingleton.initialized) {
// UGLY: Be specific
throw new Error("JoseSingleton has already been initialized")
}
}
}

View File

@ -0,0 +1,49 @@
import { $ } from "bun";
import { doesFileExists, loadFile } from "./filesystem-utils";
import { SERVER_PRIVATE_KEY_PATH, SERVER_PUBLIC_KEY_PATH } from "./constants";
export async function openSSLInit() {
await openSSLCreatePrivateKey()
await openSSLCreatePublicKey()
}
export async function openSSLCreatePrivateKey() {
// UGLY: may be refactored to output only the private key
const outputPromise = $`openssl ecparam -genkey -name secp521r1 -noout`.text()
const filePromise = loadFile(SERVER_PRIVATE_KEY_PATH, true)
const [output, file] = await Promise.all([
outputPromise,
filePromise
])
await file.write(output)
}
export async function openSSLCreatePublicKey() {
// UGLY: may be refactored to output only the private key
if (! await doesFileExists(SERVER_PRIVATE_KEY_PATH)) {
// UGLY: make more specific
throw new Error("You must generate the private key before attempting to generate the public one")
}
const outputPromise = $`openssl ec -in ${SERVER_PRIVATE_KEY_PATH} -pubout `.text()
const filePromise = loadFile(SERVER_PUBLIC_KEY_PATH, true)
const [output, file] = await Promise.all([
outputPromise,
filePromise
])
await file.write(output)
}