Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@ on:
permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true

jobs:
protocol:
runs-on: ubuntu-latest
timeout-minutes: 60
services:
redis:
image: redis:7.4.5-alpine@sha256:bb186d083732f669da90be8b0f975a37812b15e913465bb14d845db72a4e3e08
Expand Down
50 changes: 50 additions & 0 deletions crypto-boundary-suite.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { secureRandomInt } from './packages/core/src/crypto.ts';

let passed = 0;

function check(name: string, fn: () => void): void {
try {
fn();
passed += 1;
console.log(` OK ${name}`);
} catch (error) {
console.error(` FAIL ${name}`);
throw error;
}
}

function expectRangeError(value: number): void {
try {
secureRandomInt(value);
} catch (error) {
if (error instanceof RangeError) return;
throw new Error(`expected RangeError for ${String(value)}, got ${String(error)}`);
}
throw new Error(`expected secureRandomInt(${String(value)}) to throw`);
}

console.log('\nTSK crypto boundary suite');

check('accepts the full uint32 sampling domain', () => {
const value = secureRandomInt(0x100000000);
if (!Number.isInteger(value) || value < 0 || value >= 0x100000000) {
throw new Error(`out-of-range result: ${value}`);
}
});

check('rejects zero and negative bounds', () => {
expectRangeError(0);
expectRangeError(-1);
});

check('rejects non-integer and non-finite bounds', () => {
expectRangeError(1.5);
expectRangeError(Number.NaN);
expectRangeError(Number.POSITIVE_INFINITY);
});

check('rejects bounds larger than the uint32 entropy source', () => {
expectRangeError(0x100000001);
});

console.log(`TSK crypto boundary suite: ${passed}/4 passed`);
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
],
"scripts": {
"build": "npm run build -w packages/core && npm run build -w packages/server && npm run build -w packages/client-sdk && npm run build -w packages/bpc-bridge",
"test": "npx tsx test-suite.mts && npx tsx lifecycle-suite.mts && npx tsx hotp-exhaustion-suite.mts && npx tsx client-lifecycle-suite.mts && npx tsx store-durability-suite.mts && npx tsx anomaly-integration-suite.mts && npx tsx attack-suite.mts && npx tsx adversarial-proof.mts && npx tsx ultra-bridge-test.mts && npx tsx runtime-capture-suite.mts",
"test": "npx tsx test-suite.mts && npx tsx crypto-boundary-suite.mts && npx tsx lifecycle-suite.mts && npx tsx hotp-exhaustion-suite.mts && npx tsx client-lifecycle-suite.mts && npx tsx store-durability-suite.mts && npx tsx anomaly-integration-suite.mts && npx tsx attack-suite.mts && npx tsx adversarial-proof.mts && npx tsx ultra-bridge-test.mts && npx tsx runtime-capture-suite.mts",
"test:ha": "npx tsx replicating-tumbler-suite.mts && npx tsx replica-receiver-suite.mts && npx tsx failover-promotion-suite.mts",
"test:redis": "npx tsx redis-fencing-integration.mts",
"test:postgres:ha": "npm run build -w packages/server && npx tsx tsk-hotp-outbox-integration.mts",
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,9 @@ export function generateClientId(): string {
* Uses rejection sampling to eliminate modulo bias.
*/
export function secureRandomInt(max: number): number {
if (max <= 0) throw new RangeError('max must be > 0');
if (!Number.isSafeInteger(max) || max <= 0 || max > 0x100000000) {
throw new RangeError('max must be a positive safe integer no greater than 2^32');
}
if (max === 1) return 0;
// Rejection sampling: discard values in the biased tail
const limit = 0x100000000 - (0x100000000 % max);
Expand Down
Loading