Skip to content

Commit

Permalink
merge and timestamp update
Browse files Browse the repository at this point in the history
  • Loading branch information
nitro-neal committed Feb 29, 2024
1 parent 71d3a77 commit 72caaac
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 6 deletions.
2 changes: 0 additions & 2 deletions packages/credentials/src/jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ import type {
import { Convert } from '@web5/common';
import { LocalKeyManager as CryptoApi } from '@web5/crypto';
import { DidDht, DidIon, DidKey, DidJwk, DidWeb, DidResolver, utils as didUtils } from '@web5/dids';
import { VcDataModel } from './verifiable-credential.js';
import { VpDataModel } from './verifiable-presentation.js';

const crypto = new CryptoApi();

Expand Down
28 changes: 28 additions & 0 deletions packages/credentials/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,5 +86,33 @@ export function isValidXmlSchema112Timestamp(timestamp: string): boolean {

const date = new Date(timestamp);

return !isNaN(date.getTime());
}

/**
* Validates a timestamp string against the RFC 3339 format.
*
* This function checks whether the provided timestamp string conforms to the
* RFC 3339 standard, which includes full date and time representations with
* optional fractional seconds and a timezone offset. The format allows for
* both 'Z' (indicating UTC) and numeric timezone offsets (e.g., "-07:00", "+05:30").
* This validation ensures that the timestamp is not only correctly formatted
* but also represents a valid date and time.
*
* @param timestamp - The timestamp string to validate.
* @returns `true` if the timestamp is valid and conforms to RFC 3339, `false` otherwise.
*/
export function isValidRFC3339Timestamp(timestamp: string): boolean {
// RFC 3339 format: yyyy-MM-ddTHH:mm:ss[.fractional-seconds]Z or yyyy-MM-ddTHH:mm:ss[.fractional-seconds]±HH:mm
// This regex matches both 'Z' for UTC and timezone offsets like '-07:00'
const regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
if (!regex.test(timestamp)) {
return false;
}

// Parsing the timestamp to a Date object to check validity
const date = new Date(timestamp);

// Checking if the date is an actual date
return !isNaN(date.getTime());
}
4 changes: 2 additions & 2 deletions packages/credentials/src/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
VerifiableCredential
} from './verifiable-credential.js';

import { isValidXmlSchema112Timestamp } from './utils.js';
import { isValidRFC3339Timestamp, isValidXmlSchema112Timestamp } from './utils.js';
import { DEFAULT_VP_TYPE } from './verifiable-presentation.js';

export class SsiValidator {
Expand Down Expand Up @@ -49,7 +49,7 @@ export class SsiValidator {
}

static validateTimestamp(timestamp: string) {
if(!isValidXmlSchema112Timestamp(timestamp)){
if(!isValidXmlSchema112Timestamp(timestamp) && !isValidRFC3339Timestamp(timestamp)){
throw new Error(`timestamp is not valid xml schema 112 timestamp`);
}
}
Expand Down
2 changes: 0 additions & 2 deletions packages/credentials/tests/jwt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,6 @@ describe('Jwt', () => {
errorOccurred = true;
expect(e.message).to.not.be.null;
if(errorMessage && errorMessage['web5-js']) {
console.log(e.message)
expect(e.message).to.include(errorMessage['web5-js']);
}
}
Expand All @@ -219,7 +218,6 @@ describe('Jwt', () => {
errorOccurred = true;
expect(e.message).to.not.be.null;
if(errorMessage && errorMessage['web5-js']) {
console.log(e.message)
expect(e.message).to.include(errorMessage['web5-js']);
}
}
Expand Down
45 changes: 45 additions & 0 deletions packages/credentials/tests/utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
isValidXmlSchema112Timestamp,
getFutureXmlSchema112Timestamp,
getCurrentXmlSchema112Timestamp,
isValidRFC3339Timestamp,
} from '../src/utils.js';

describe('CredentialsUtils', () => {
Expand Down Expand Up @@ -50,4 +51,48 @@ describe('CredentialsUtils', () => {
expect(result).to.be.false;
});
});

describe('isValidRFC3339Timestamp', () => {
it('validates correctly formatted timestamps without fractional seconds and with Z timezone', () => {
const timestamp = '2023-07-31T12:34:56Z';
const result = isValidRFC3339Timestamp(timestamp);
expect(result).to.be.true;
});

it('validates correctly formatted timestamps with fractional seconds and Z timezone', () => {
const timestampWithFractionalSeconds = '2023-07-31T12:34:56.789Z';
const result = isValidRFC3339Timestamp(timestampWithFractionalSeconds);
expect(result).to.be.true;
});

it('validates correctly formatted timestamps with timezone offset', () => {
const timestampWithOffset = '2023-07-31T12:34:56-07:00';
const result = isValidRFC3339Timestamp(timestampWithOffset);
expect(result).to.be.true;
});

it('rejects incorrectly formatted timestamps', () => {
const badTimestamp = '2023-07-31 12:34:56';
const result = isValidRFC3339Timestamp(badTimestamp);
expect(result).to.be.false;
});

it('rejects non-timestamp strings', () => {
const notATimestamp = 'This is definitely not a timestamp';
const result = isValidRFC3339Timestamp(notATimestamp);
expect(result).to.be.false;
});

it('rejects empty string', () => {
const emptyString = '';
const result = isValidRFC3339Timestamp(emptyString);
expect(result).to.be.false;
});

it('validates correctly formatted timestamps with fractional seconds and timezone offset', () => {
const timestampWithFractionalSecondsAndOffset = '2023-07-31T12:34:56.789+02:00';
const result = isValidRFC3339Timestamp(timestampWithFractionalSecondsAndOffset);
expect(result).to.be.true;
});
});
});
58 changes: 58 additions & 0 deletions packages/credentials/tests/verifiable-credential.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { DidDht, DidKey, DidIon, DidJwk } from '@web5/dids';
import { Jwt } from '../src/jwt.js';
import { VerifiableCredential } from '../src/verifiable-credential.js';
import CredentialsVerifyTestVector from '../../../web5-spec/test-vectors/credentials/verify.json' assert { type: 'json' };
import { getCurrentXmlSchema112Timestamp, getXmlSchema112Timestamp } from '../src/utils.js';

describe('Verifiable Credential Tests', async() => {
let issuerDid: BearerDid;
Expand Down Expand Up @@ -354,6 +355,63 @@ describe('Verifiable Credential Tests', async() => {
}
});

it('verify works with RFC3339 vcjwt', async () => {
const didIssuer = await DidKey.create();
const didSubject = await DidKey.create();

const vc = await VerifiableCredential.create({
type : 'TBDeveloperCredential',
subject : didSubject.uri,
issuer : didIssuer.uri,
issuanceDate : new Date().toISOString(),
data : {
username: 'nitro'
}
});

const vcJwt = await vc.sign({ did: didIssuer });
await VerifiableCredential.verify({ vcJwt });
});

it('verify works with XmlSchema112 vcjwt', async () => {
const didIssuer = await DidKey.create();
const didSubject = await DidKey.create();

const vc = await VerifiableCredential.create({
type : 'TBDeveloperCredential',
subject : didSubject.uri,
issuer : didIssuer.uri,
issuanceDate : getCurrentXmlSchema112Timestamp(),
data : {
username: 'nitro'
}
});

const vcJwt = await vc.sign({ did: didIssuer });
await VerifiableCredential.verify({ vcJwt });
});

it('create throws with with invalid issuance date vcjwt', async () => {
const didIssuer = await DidKey.create();
const didSubject = await DidKey.create();

try {
await VerifiableCredential.create({
type : 'TBDeveloperCredential',
subject : didSubject.uri,
issuer : didIssuer.uri,
issuanceDate : 'July 20, 2024, 15:45:30 GMT+02:00',
data : {
username: 'nitro'
}
});
expect.fail();
} catch(e: any) {
expect(e).to.not.be.null;
expect(e.message).to.include('timestamp is not valid');
}
});

it('verify throws exception if vc property is invalid', async () => {
const did = await DidKey.create();

Expand Down

0 comments on commit 72caaac

Please sign in to comment.