@sgnl-ai/secevent
Version:
A comprehensive JavaScript/TypeScript library for Security Event Tokens (SET) implementing RFC 8417, CAEP, and SSF standards
175 lines (156 loc) • 3.82 kB
text/typescript
/**
* Security Event Token Signing utilities
* Provides various signing strategies and key management
*/
import { generateKeyPair, importJWK, importPKCS8, importSPKI, KeyLike } from 'jose';
import { SigningKey } from '../types/secevent';
/**
* Supported algorithms
*/
export enum Algorithm {
RS256 = 'RS256',
RS384 = 'RS384',
RS512 = 'RS512',
PS256 = 'PS256',
PS384 = 'PS384',
PS512 = 'PS512',
ES256 = 'ES256',
ES384 = 'ES384',
ES512 = 'ES512',
HS256 = 'HS256',
HS384 = 'HS384',
HS512 = 'HS512',
}
/**
* Key manager for handling signing keys
*/
export class KeyManager {
private keys: Map<string, SigningKey> = new Map();
/**
* Add a key to the manager
*/
addKey(kid: string, key: SigningKey): void {
this.keys.set(kid, { ...key, kid });
}
/**
* Get a key by ID
*/
getKey(kid: string): SigningKey | undefined {
return this.keys.get(kid);
}
/**
* Get all keys
*/
getAllKeys(): SigningKey[] {
return Array.from(this.keys.values());
}
/**
* Remove a key
*/
removeKey(kid: string): boolean {
return this.keys.delete(kid);
}
/**
* Clear all keys
*/
clear(): void {
this.keys.clear();
}
/**
* Get the default key (first key added)
*/
getDefaultKey(): SigningKey | undefined {
const firstKey = this.keys.values().next();
return firstKey.done ? undefined : firstKey.value;
}
}
/**
* Signing utilities
*/
export class SigningUtils {
/**
* Generate a new key pair
*/
static async generateKeyPair(
alg: Algorithm = Algorithm.RS256,
options?: { modulusLength?: number; extractable?: boolean },
): Promise<{ publicKey: KeyLike; privateKey: KeyLike }> {
return generateKeyPair(alg, options);
}
/**
* Import a JWK
*/
static async importJWK(jwk: Record<string, unknown>, alg: Algorithm): Promise<KeyLike> {
const result = await importJWK(jwk as unknown as Parameters<typeof importJWK>[0], alg);
return result as KeyLike;
}
/**
* Import a PKCS8 private key
*/
static async importPrivateKey(
pkcs8: string,
alg: Algorithm,
options?: { extractable?: boolean },
): Promise<KeyLike> {
return importPKCS8(pkcs8, alg, options);
}
/**
* Import a SPKI public key
*/
static async importPublicKey(
spki: string,
alg: Algorithm,
options?: { extractable?: boolean },
): Promise<KeyLike> {
return importSPKI(spki, alg, options);
}
/**
* Create a signing key from various formats
*/
static async createSigningKey(
key: string | KeyLike | Record<string, unknown>,
alg: Algorithm,
kid?: string,
): Promise<SigningKey> {
let keyLike: KeyLike;
if (typeof key === 'string') {
// Assume it's a PEM-encoded key
if (key.includes('PRIVATE KEY')) {
keyLike = await this.importPrivateKey(key, alg);
} else if (key.includes('PUBLIC KEY')) {
keyLike = await this.importPublicKey(key, alg);
} else {
// Assume it's a symmetric key
keyLike = new TextEncoder().encode(key) as unknown as KeyLike;
}
} else if (typeof key === 'object' && !('type' in key)) {
// Assume it's a JWK
keyLike = await this.importJWK(key as Record<string, unknown>, alg);
} else {
keyLike = key as KeyLike;
}
return {
key: keyLike,
alg,
...(kid !== undefined && { kid }),
};
}
/**
* Create a symmetric signing key
*/
static createSymmetricKey(
secret: string,
alg: Algorithm = Algorithm.HS256,
kid?: string,
): SigningKey {
return {
key: new TextEncoder().encode(secret) as unknown as KeyLike,
alg,
...(kid !== undefined && { kid }),
};
}
}
/**
* Default key manager instance
*/
export const defaultKeyManager = new KeyManager();