Skip to content

Commit

Permalink
Add configurable not-before delay
Browse files Browse the repository at this point in the history
This commit adds `KEY_NOT_BEFORE_DELAY_IN_MS` binding.
This is the delay that is going to be added when generating key to their
`notBefore` parameter. This allow a key to be generated at time `t` to
not be valid before `t+KEY_NOT_BEFORE_DELAY_IN_MS`.

Key rotation and tests are updated accordingly.
This is backward compatible with deployed codebase, defaulting to
`KEY_NOT_BEFORE_DELAY_IN_MS=0` which was the previous behaviour.
  • Loading branch information
thibmeu committed Jan 6, 2025
1 parent ed83fcf commit bc1bdbc
Show file tree
Hide file tree
Showing 5 changed files with 21 additions and 6 deletions.
1 change: 1 addition & 0 deletions src/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface Bindings {
// Key rotation schedule
ROTATION_CRON_STRING?: string;
KEY_LIFESPAN_IN_MS: string;
KEY_NOT_BEFORE_DELAY_IN_MS: string;
MINIMUM_FRESHEST_KEYS: string;

// telemetry
Expand Down
19 changes: 16 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const keyToTokenKeyID = async (key: Uint8Array): Promise<number> => {
};

interface StorageMetadata extends Record<string, string> {
notBefore: string;
publicKey: string;
tokenKeyID: string;
}
Expand Down Expand Up @@ -176,7 +177,10 @@ export const handleTokenDirectory = async (ctx: Context, request: Request) => {
'token-keys': keys.map(key => ({
'token-type': TokenType.BlindRSA,
'token-key': (key.customMetadata as StorageMetadata).publicKey,
'not-before': Math.trunc(new Date(key.uploaded).getTime() / 1000), // the spec mandates to use seconds
'not-before': Number.parseInt(
(key.customMetadata as StorageMetadata).notBefore ??
(new Date(key.uploaded).getTime() / 1000).toFixed(0)
),
})),
};

Expand Down Expand Up @@ -237,6 +241,9 @@ export const handleRotateKey = async (ctx: Context, _request?: Request) => {
} while ((await ctx.bucket.ISSUANCE_KEYS.head(tokenKeyID.toString())) !== null);

const metadata: StorageMetadata = {
notBefore: ((Date.now() + Number.parseInt(ctx.env.KEY_NOT_BEFORE_DELAY_IN_MS)) / 1000).toFixed(
0
), // the spec mandates to use seconds
publicKey: publicKeyEnc,
tokenKeyID: tokenKeyID.toString(),
};
Expand Down Expand Up @@ -270,15 +277,21 @@ export const handleClearKey = async (ctx: Context, _request?: Request) => {

for (let i = 0; i < keys.objects.length; i++) {
const key = keys.objects[i];
const keyUploadTime = new Date(key.uploaded);
const notBefore = key.customMetadata?.notBefore;
let keyNotBefore: Date;
if (notBefore) {
keyNotBefore = new Date(Number.parseInt(notBefore) * 1000);
} else {
keyNotBefore = new Date(key.uploaded);
}

const isFreshest = i < freshestKeyCount;

if (isFreshest) {
continue;
}

const shouldDelete = shouldClearKey(keyUploadTime, lifespanInMs);
const shouldDelete = shouldClearKey(keyNotBefore, lifespanInMs);

if (shouldDelete) {
toDelete.add(key.key);
Expand Down
4 changes: 2 additions & 2 deletions src/utils/keyRotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export function shouldRotateKey(date: Date, env: Bindings): boolean {
return env.ROTATION_CRON_STRING ? matchCronTime(env.ROTATION_CRON_STRING, utcDate).match : false;
}

export function shouldClearKey(keyUploadTime: Date, lifespanInMs: number): boolean {
const keyExpirationTime = keyUploadTime.getTime() + lifespanInMs;
export function shouldClearKey(keyNotBefore: Date, lifespanInMs: number): boolean {
const keyExpirationTime = keyNotBefore.getTime() + lifespanInMs;
return Date.now() > keyExpirationTime;
}

Expand Down
2 changes: 1 addition & 1 deletion test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ describe('directory', () => {

const directory = (await response.json()) as IssuerConfig;

let previousDate = Date.now();
let previousDate = Date.now() + Number.parseInt(env.KEY_NOT_BEFORE_DELAY_IN_MS);
for (const tokenKey of directory['token-keys']) {
if (!tokenKey['not-before']) {
continue;
Expand Down
1 change: 1 addition & 0 deletions wrangler.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ crons = ["0 0 * * *"]
DIRECTORY_CACHE_MAX_AGE_SECONDS = "86400"
ENVIRONMENT = "production"
KEY_LIFESPAN_IN_MS = "172800000" # 48h
KEY_NOT_BEFORE_DELAY_IN_MS = "7200000" # 2h
MINIMUM_FRESHEST_KEYS = "1"
ROTATION_CRON_STRING = "30 12 * * *"
SENTRY_SAMPLE_RATE = "0" # Between 0-1 if you log errors on Sentry. 0 disables Sentry logging. Configuration is done through Workers Secrets
Expand Down

0 comments on commit bc1bdbc

Please sign in to comment.