48 lines
1.7 KiB
TypeScript
48 lines
1.7 KiB
TypeScript
|
|
import type { CryptoProvider } from '@shade/core';
|
||
|
|
import { ValidationError } from '@shade/core';
|
||
|
|
|
||
|
|
export const STREAM_ID_BYTES = 16;
|
||
|
|
export const STREAM_SECRET_BYTES = 32;
|
||
|
|
|
||
|
|
/** Generate a fresh 16-byte random streamId. */
|
||
|
|
export function generateStreamId(crypto: CryptoProvider): Uint8Array {
|
||
|
|
return crypto.randomBytes(STREAM_ID_BYTES);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Generate a fresh 32-byte random streamSecret. */
|
||
|
|
export function generateStreamSecret(crypto: CryptoProvider): Uint8Array {
|
||
|
|
return crypto.randomBytes(STREAM_SECRET_BYTES);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Encode a streamId as URL-safe base64 (no padding). */
|
||
|
|
export function streamIdToString(streamId: Uint8Array): string {
|
||
|
|
if (streamId.length !== STREAM_ID_BYTES) {
|
||
|
|
throw new ValidationError(`streamId must be ${STREAM_ID_BYTES} bytes`, 'streamId');
|
||
|
|
}
|
||
|
|
return base64UrlEncode(streamId);
|
||
|
|
}
|
||
|
|
|
||
|
|
/** Decode a URL-safe base64 streamId back to bytes. */
|
||
|
|
export function streamIdFromString(s: string): Uint8Array {
|
||
|
|
const bytes = base64UrlDecode(s);
|
||
|
|
if (bytes.length !== STREAM_ID_BYTES) {
|
||
|
|
throw new ValidationError(`streamId must decode to ${STREAM_ID_BYTES} bytes`, 'streamId');
|
||
|
|
}
|
||
|
|
return bytes;
|
||
|
|
}
|
||
|
|
|
||
|
|
function base64UrlEncode(bytes: Uint8Array): string {
|
||
|
|
let bin = '';
|
||
|
|
for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]!);
|
||
|
|
return btoa(bin).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
||
|
|
}
|
||
|
|
|
||
|
|
function base64UrlDecode(s: string): Uint8Array {
|
||
|
|
const padded = s.replace(/-/g, '+').replace(/_/g, '/');
|
||
|
|
const pad = padded.length % 4 === 0 ? '' : '='.repeat(4 - (padded.length % 4));
|
||
|
|
const bin = atob(padded + pad);
|
||
|
|
const out = new Uint8Array(bin.length);
|
||
|
|
for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
|
||
|
|
return out;
|
||
|
|
}
|