import { env } from "cloudflare:workers";
import { decryptSecret, encryptSecret, fromBase64, toBase64 } from "../crypto";
import { badRequest } from "../errors";
import { isPermanentFailure, isSuccess, outboundFetch } from "./outbound";
const GITHUB_API = "https://api.github.com";
const JWT_LIFETIME_SECONDS = 9 * 60;
const JWT_CLOCK_SKEW_SECONDS = 60;
const TOKEN_CACHE_SECONDS = 50 * 60;
const MS_PER_SECOND = 1000;
const DER_LONG_FORM = 0x80;
const encoder = new TextEncoder();
function base64Url(bytes: Uint8Array): string {
return toBase64(bytes).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
}
function derLength(length: number): number[] {
if (length < DER_LONG_FORM) return [length];
const bytes: number[] = [];
for (let rest = length; rest > 0; rest >>= 8) bytes.unshift(rest & 0xff);
return [DER_LONG_FORM | bytes.length, ...bytes];
}
const RSA_ALGORITHM_IDENTIFIER = [
0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
];
/** GitHub hands out PKCS#1 keys; WebCrypto only imports PKCS#8, so wrap the RSAPrivateKey in a PrivateKeyInfo. */
export function pkcs1ToPkcs8(pkcs1: Uint8Array): Uint8Array<ArrayBuffer> {
const octet = [0x04, ...derLength(pkcs1.length), ...pkcs1];
const body = [0x02, 0x01, 0x00, ...RSA_ALGORITHM_IDENTIFIER, ...octet];
return new Uint8Array([0x30, ...derLength(body.length), ...body]);
}
function pemBody(pem: string): Uint8Array<ArrayBuffer> {
return fromBase64(pem.replace(/-----[A-Z ]+-----/g, "").replace(/\s+/g, ""));
}
async function appSigningKey(pem: string): Promise<CryptoKey> {