@@ -38,6 +38,7 @@ import {
3838 assertCredentialContext ,
3939 assertDateString ,
4040 checkContextVersion ,
41+ compareTime ,
4142 getContextForVersion
4243} from './helpers.js' ;
4344import { documentLoader as _documentLoader } from './documentLoader.js' ;
@@ -103,6 +104,10 @@ export {CredentialIssuancePurpose};
103104 * @param {object } [options.documentLoader] - A document loader.
104105 * @param {string|Date } [options.now] - A string representing date time in
105106 * ISO 8601 format or an instance of Date. Defaults to current date time.
107+ * @param {number } [options.maxClockSkew=300] - A maximum number of seconds
108+ * that clocks may be skewed when checking capability expiration date-times
109+ * against `date` and when comparing invocation proof creation time against
110+ * delegation proof creation time.
106111 *
107112 * @throws {Error } If missing required properties.
108113 *
@@ -112,7 +117,8 @@ export async function issue({
112117 credential, suite,
113118 purpose = new CredentialIssuancePurpose ( ) ,
114119 documentLoader = defaultDocumentLoader ,
115- now
120+ now,
121+ maxClockSkew = 300
116122} = { } ) {
117123 // check to make sure the `suite` has required params
118124 // Note: verificationMethod defaults to publicKey.id, in suite constructor
@@ -135,7 +141,7 @@ export async function issue({
135141 }
136142
137143 // run common credential checks
138- _checkCredential ( { credential, now, mode : 'issue' } ) ;
144+ _checkCredential ( { credential, now, mode : 'issue' , maxClockSkew } ) ;
139145
140146 return jsigs . sign ( credential , { purpose, documentLoader, suite} ) ;
141147}
@@ -219,6 +225,10 @@ export async function derive({
219225 * credential status if `credentialStatus` is present on the credential.
220226 * @param {string|Date } [options.now] - A string representing date time in
221227 * ISO 8601 format or an instance of Date. Defaults to current date time.
228+ * @param {number } [options.maxClockSkew=300] - A maximum number of seconds
229+ * that clocks may be skewed when checking capability expiration date-times
230+ * against `date` and when comparing invocation proof creation time against
231+ * delegation proof creation time.
222232 *
223233 * @returns {Promise<VerifyPresentationResult> } The verification result.
224234 */
@@ -264,6 +274,10 @@ export async function verify(options = {}) {
264274 * credential status if `credentialStatus` is present on the credential.
265275 * @param {string|Date } [options.now] - A string representing date time in
266276 * ISO 8601 format or an instance of Date. Defaults to current date time.
277+ * @param {number } [options.maxClockSkew=300] - A maximum number of seconds
278+ * that clocks may be skewed when checking capability expiration date-times
279+ * against `date` and when comparing invocation proof creation time against
280+ * delegation proof creation time.
267281 *
268282 * @returns {Promise<VerifyCredentialResult> } The verification result.
269283 */
@@ -295,6 +309,10 @@ export async function verifyCredential(options = {}) {
295309 * definition in the `verify()` docstring, for this param.
296310 * @param {string|Date } [options.now] - A string representing date time in
297311 * ISO 8601 format or an instance of Date. Defaults to current date time.
312+ * @param {number } [options.maxClockSkew=300] - A maximum number of seconds
313+ * that clocks may be skewed when checking capability expiration date-times
314+ * against `date` and when comparing invocation proof creation time against
315+ * delegation proof creation time.
298316 *
299317 * @throws {Error } If required parameters are missing (in `_checkCredential`).
300318 *
@@ -306,10 +324,10 @@ export async function verifyCredential(options = {}) {
306324 * @returns {Promise<VerifyCredentialResult> } The verification result.
307325 */
308326async function _verifyCredential ( options = { } ) {
309- const { credential, checkStatus, now} = options ;
327+ const { credential, checkStatus, now, maxClockSkew } = options ;
310328
311329 // run common credential checks
312- _checkCredential ( { credential, now} ) ;
330+ _checkCredential ( { credential, now, maxClockSkew } ) ;
313331
314332 // if credential status is provided, a `checkStatus` function must be given
315333 if ( credential . credentialStatus && typeof options . checkStatus !== 'function' ) {
@@ -352,6 +370,10 @@ async function _verifyCredential(options = {}) {
352370 * @param {string } [options.holder] - Optional presentation holder url.
353371 * @param {string|Date } [options.now] - A string representing date time in
354372 * ISO 8601 format or an instance of Date. Defaults to current date time.
373+ * @param {number } [options.maxClockSkew=300] - A maximum number of seconds
374+ * that clocks may be skewed when checking capability expiration date-times
375+ * against `date` and when comparing invocation proof creation time against
376+ * delegation proof creation time.
355377 * @param {number } [options.version = 2.0] - The VC context version to use.
356378 *
357379 * @throws {TypeError } If verifiableCredential param is missing.
@@ -362,7 +384,7 @@ async function _verifyCredential(options = {}) {
362384 * VerifiablePresentation.
363385 */
364386export function createPresentation ( {
365- verifiableCredential, id, holder, now, version = 2.0
387+ verifiableCredential, id, holder, now, version = 2.0 , maxClockSkew = 300
366388} = { } ) {
367389 const initialContext = getContextForVersion ( { version} ) ;
368390 const presentation = {
@@ -373,7 +395,7 @@ export function createPresentation({
373395 const credentials = [ ] . concat ( verifiableCredential ) ;
374396 // ensure all credentials are valid
375397 for ( const credential of credentials ) {
376- _checkCredential ( { credential, now} ) ;
398+ _checkCredential ( { credential, now, maxClockSkew } ) ;
377399 }
378400 presentation . verifiableCredential = credentials ;
379401 }
@@ -456,6 +478,10 @@ export async function signPresentation(options = {}) {
456478 * credential status if `credentialStatus` is present on the credential.
457479 * @param {string|Date } [options.now] - A string representing date time in
458480 * ISO 8601 format or an instance of Date. Defaults to current date time.
481+ * @param {number } [options.maxClockSkew=300] - A maximum number of seconds
482+ * that clocks may be skewed when checking capability expiration date-times
483+ * against `date` and when comparing invocation proof creation time against
484+ * delegation proof creation time.
459485 *
460486 * @throws {Error } If presentation is missing required params.
461487 *
@@ -571,14 +597,18 @@ const mustHaveType = [
571597 * VerifiableCredential.
572598 * @param {string|Date } [options.now] - A string representing date time in
573599 * ISO 8601 format or an instance of Date. Defaults to current date time.
600+ * @param {number } [options.maxClockSkew=300] - A maximum number of seconds
601+ * that clocks may be skewed when checking capability expiration date-times
602+ * against `date` and when comparing invocation proof creation time against
603+ * delegation proof creation time.
574604 * @param {string } [options.mode] - The mode of operation for this
575605 * validation function, either `issue` or `verify`.
576606 *
577607 * @throws {Error }
578608 * @private
579609 */
580610export function _checkCredential ( {
581- credential, now = new Date ( ) , mode = 'verify'
611+ credential, now = new Date ( ) , mode = 'verify' , maxClockSkew = 300
582612} = { } ) {
583613 if ( typeof now === 'string' ) {
584614 now = new Date ( now ) ;
@@ -617,15 +647,16 @@ export function _checkCredential({
617647 assertDateString ( { credential, prop : 'expirationDate' } ) ;
618648 if ( mode === 'verify' ) {
619649 // check if `now` is after `expirationDate`
620- if ( now > new Date ( credential . expirationDate ) ) {
650+ const expirationDate = new Date ( credential . expirationDate ) ;
651+ if ( compareTime ( { t1 : now , t2 : expirationDate , maxClockSkew} ) > 0 ) {
621652 throw new Error ( 'Credential has expired.' ) ;
622653 }
623654 }
624655 }
625656 // check if `now` is before `issuanceDate` on verification
626657 if ( mode === 'verify' ) {
627658 const issuanceDate = new Date ( credential . issuanceDate ) ;
628- if ( now < issuanceDate ) {
659+ if ( compareTime ( { t1 : issuanceDate , t2 : now , maxClockSkew } ) > 0 ) {
629660 throw new Error (
630661 `The current date time (${ now . toISOString ( ) } ) is before the ` +
631662 `"issuanceDate" (${ credential . issuanceDate } ).` ) ;
@@ -639,7 +670,7 @@ export function _checkCredential({
639670 assertDateString ( { credential, prop : 'validUntil' } ) ;
640671 if ( mode === 'verify' ) {
641672 validUntil = new Date ( credential . validUntil ) ;
642- if ( now > validUntil ) {
673+ if ( compareTime ( { t1 : now , t2 : validUntil , maxClockSkew } ) > 0 ) {
643674 throw new Error (
644675 `The current date time (${ now . toISOString ( ) } ) is after ` +
645676 `"validUntil" (${ credential . validUntil } ).` ) ;
@@ -651,7 +682,7 @@ export function _checkCredential({
651682 if ( mode === 'verify' ) {
652683 // check if `now` is before `validFrom`
653684 validFrom = new Date ( credential . validFrom ) ;
654- if ( now < validFrom ) {
685+ if ( compareTime ( { t1 : validFrom , t2 : now , maxClockSkew } ) > 0 ) {
655686 throw new Error (
656687 `The current date time (${ now . toISOString ( ) } ) is before ` +
657688 `"validFrom" (${ credential . validFrom } ).` ) ;
0 commit comments