Skip to content

Commit 9f8ed1f

Browse files
committed
refactor: rename certificate arg and helper function
1 parent 7f2577c commit 9f8ed1f

File tree

6 files changed

+37
-32
lines changed

6 files changed

+37
-32
lines changed

CHANGELOG.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66
- fix: pick the expiry rounding strategy based on the delta, without adding the clock drift to the delta.
77
- feat: adds a `clockDriftMs` optional parameter to `Expiry.fromDeltaInMilliseconds` to add to the current time, typically used to specify the clock drift between the client's clock and the IC network clock.
88
- fix: account for clock drift when verifying the certificate freshness.
9-
- feat: adds the `timeDiffMsecs` optional field to the `CreateCertificateOptions` interface, which allows you to adjust the current time when verifying the certificate freshness.
10-
- feat: adds the `getTimeDiffMsecs` function to the `HttpAgent` class, which returns the time difference in milliseconds between the client's clock and the IC network clock. It also adds the `getTimeDiffMsecs` function to handle the case where the agent is not an instance of `HttpAgent`.
9+
- feat: adds the `currentTime` optional field to the `CreateCertificateOptions` interface, which allows you to override the current time when verifying the certificate freshness.
10+
- feat: adds the `getTimeDiffMsecs` function to the `HttpAgent` class, which returns the time difference in milliseconds between the client's clock and the IC network clock.
11+
- feat: adds the `getAdjustedCurrentTime` to compute the current time adjusted by the input agent's time difference in milliseconds, if the agent is an instance of `HttpAgent`.
1112

1213
## [3.1.0] - 2025-07-24
1314

packages/agent/src/actor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import { IDL } from '@dfinity/candid';
2020
import { pollForResponse, type PollingOptions, DEFAULT_POLLING_OPTIONS } from './polling/index.ts';
2121
import { Principal } from '@dfinity/principal';
2222
import { Certificate, type CreateCertificateOptions, lookupResultToBuffer } from './certificate.ts';
23-
import { getTimeDiffMsecs, HttpAgent } from './agent/http/index.ts';
23+
import { getAdjustedCurrentTime, HttpAgent } from './agent/http/index.ts';
2424
import { utf8ToBytes } from '@noble/hashes/utils';
2525

2626
/**
@@ -427,7 +427,7 @@ function _createActorMethod(
427427
rootKey: agent.rootKey,
428428
canisterId: Principal.from(canisterId),
429429
blsVerify,
430-
timeDiffMsecs: getTimeDiffMsecs(agent),
430+
currentTime: getAdjustedCurrentTime(agent),
431431
});
432432
const path = [utf8ToBytes('request_status'), requestId];
433433
const status = new TextDecoder().decode(

packages/agent/src/agent/http/index.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,15 +1415,15 @@ export function calculateIngressExpiry(
14151415
}
14161416

14171417
/**
1418-
* Retrieves the time difference in milliseconds between the client's clock and the IC network clock.
1419-
* See {@link HttpAgent.getTimeDiffMsecs} for more details.
1418+
* Computes the current time adjusted by the time difference in milliseconds returned by {@link HttpAgent.getTimeDiffMsecs}.
14201419
* @param agent The agent to retrieve the `timeDiffMsecs` property from.
1421-
* @returns The time difference in milliseconds between the client's clock and the IC network clock,
1422-
* if the agent is an {@link HttpAgent} instance. `undefined` otherwise.
1420+
* @returns The current time adjusted by the agent's time difference in milliseconds. If the agent is not an {@link HttpAgent} instance, fallbacks to the system's current timestamp.
14231421
*/
1424-
export function getTimeDiffMsecs(agent: Agent | HttpAgent): number | undefined {
1422+
export function getAdjustedCurrentTime(agent: Agent | HttpAgent): Date {
1423+
let timestamp = Date.now();
14251424
if ('getTimeDiffMsecs' in agent) {
1426-
return agent.getTimeDiffMsecs();
1425+
const timeDiffMsecs = agent.getTimeDiffMsecs();
1426+
timestamp += timeDiffMsecs;
14271427
}
1428-
return undefined;
1428+
return new Date(timestamp);
14291429
}

packages/agent/src/certificate.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -669,7 +669,7 @@ test('certificate verification passes if the time of the certificate is > 5 minu
669669
rootKey: hexToBytes(IC_ROOT_KEY),
670670
canisterId: Principal.fromText('ivg37-qiaaa-aaaab-aaaga-cai'),
671671
blsVerify: async () => true,
672-
timeDiffMsecs,
672+
currentTime: new Date(Date.now() + timeDiffMsecs),
673673
});
674674
expect(cert).toBeInstanceOf(Cert.Certificate);
675675
});
@@ -691,7 +691,7 @@ test('certificate verification fails if the time of the certificate is > max age
691691
canisterId: Principal.fromText('ivg37-qiaaa-aaaab-aaaga-cai'),
692692
blsVerify: async () => true,
693693
maxAgeInMinutes,
694-
timeDiffMsecs,
694+
currentTime: new Date(Date.now() + timeDiffMsecs),
695695
});
696696
} catch (error) {
697697
expect(error).toBeInstanceOf(TrustError);
@@ -729,7 +729,7 @@ test('certificate verification fails if the time of the certificate is > 5 minut
729729
rootKey: hexToBytes(IC_ROOT_KEY),
730730
canisterId: Principal.fromText('ivg37-qiaaa-aaaab-aaaga-cai'),
731731
blsVerify: async () => true,
732-
timeDiffMsecs,
732+
currentTime: new Date(Date.now() + timeDiffMsecs),
733733
});
734734
expect(cert).toBeInstanceOf(Cert.Certificate);
735735
});

packages/agent/src/certificate.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,10 @@ export interface CreateCertificateOptions {
177177
disableTimeVerification?: boolean;
178178

179179
/**
180-
* The time difference in milliseconds between the client's clock and the IC network clock.
181-
* This is used to adjust the current time when verifying the certificate freshness.
182-
* @default 0
180+
* The current time to use when verifying the certificate freshness.
181+
* @default `new Date()`
183182
*/
184-
timeDiffMsecs?: number;
183+
currentTime?: Date;
185184
}
186185

187186
export class Certificate {
@@ -196,7 +195,7 @@ export class Certificate {
196195
public static async create(options: CreateCertificateOptions): Promise<Certificate> {
197196
const cert = Certificate.createUnverified(options);
198197

199-
await cert.verify(options.timeDiffMsecs ?? 0);
198+
await cert.verify(options.currentTime ?? new Date());
200199
return cert;
201200
}
202201

@@ -241,9 +240,9 @@ export class Certificate {
241240
return lookup_subtree(path, this.cert.tree);
242241
}
243242

244-
private async verify(timeDiffMsecs: number): Promise<void> {
243+
private async verify(currentTime: Date): Promise<void> {
245244
const rootHash = await reconstruct(this.cert.tree);
246-
const derKey = await this._checkDelegationAndGetKey(this.cert.delegation, timeDiffMsecs);
245+
const derKey = await this._checkDelegationAndGetKey(this.cert.delegation, currentTime);
247246
const sig = this.cert.signature;
248247
const key = extractDER(derKey);
249248
const msg = concatBytes(domain_sep('ic-state-root'), rootHash);
@@ -259,10 +258,9 @@ export class Certificate {
259258
// Certificate time verification checks
260259
if (!this.#disableTimeVerification) {
261260
const maxAgeInMsec = this._maxAgeInMinutes * MINUTES_TO_MSEC;
262-
const now = Date.now();
263-
const adjustedNow = now + timeDiffMsecs;
264-
const earliestCertificateTime = adjustedNow - maxAgeInMsec;
265-
const latestCertificateTime = adjustedNow + FIVE_MINUTES_IN_MSEC;
261+
const currentTimestamp = currentTime.getTime();
262+
const earliestCertificateTime = currentTimestamp - maxAgeInMsec;
263+
const latestCertificateTime = currentTimestamp + FIVE_MINUTES_IN_MSEC;
266264

267265
const certTime = decodeTime(lookupTime);
268266

@@ -271,14 +269,20 @@ export class Certificate {
271269
new CertificateTimeErrorCode(
272270
this._maxAgeInMinutes,
273271
certTime,
274-
new Date(now),
275-
timeDiffMsecs,
272+
new Date(),
273+
currentTimestamp - Date.now(),
276274
'past',
277275
),
278276
);
279277
} else if (certTime.getTime() > latestCertificateTime) {
280278
throw TrustError.fromCode(
281-
new CertificateTimeErrorCode(5, certTime, new Date(now), timeDiffMsecs, 'future'),
279+
new CertificateTimeErrorCode(
280+
5,
281+
certTime,
282+
new Date(),
283+
currentTimestamp - Date.now(),
284+
'future',
285+
),
282286
);
283287
}
284288
}
@@ -297,7 +301,7 @@ export class Certificate {
297301

298302
private async _checkDelegationAndGetKey(
299303
d: Delegation | undefined,
300-
timeDiffMsecs: number,
304+
currentTime: Date,
301305
): Promise<Uint8Array> {
302306
if (!d) {
303307
return this._rootKey;
@@ -315,7 +319,7 @@ export class Certificate {
315319
throw ProtocolError.fromCode(new CertificateHasTooManyDelegationsErrorCode());
316320
}
317321

318-
await cert.verify(timeDiffMsecs);
322+
await cert.verify(currentTime);
319323

320324
if (this._canisterId.toString() !== MANAGEMENT_CANISTER_ID) {
321325
const canisterInRange = check_canister_ranges({

packages/agent/src/polling/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
export * as strategy from './strategy.ts';
2222
import { defaultStrategy } from './strategy.ts';
2323
import { ReadRequestType, type ReadStateRequest } from '../agent/http/types.ts';
24-
import { getTimeDiffMsecs, RequestStatusResponseStatus } from '../agent/index.ts';
24+
import { getAdjustedCurrentTime, RequestStatusResponseStatus } from '../agent/index.ts';
2525
import { utf8ToBytes } from '@noble/hashes/utils';
2626
export { defaultStrategy } from './strategy.ts';
2727

@@ -161,7 +161,7 @@ export async function pollForResponse(
161161
rootKey: agent.rootKey,
162162
canisterId: canisterId,
163163
blsVerify: options.blsVerify,
164-
timeDiffMsecs: getTimeDiffMsecs(agent),
164+
currentTime: getAdjustedCurrentTime(agent),
165165
});
166166

167167
const maybeBuf = lookupResultToBuffer(cert.lookup_path([...path, utf8ToBytes('status')]));

0 commit comments

Comments
 (0)