Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: finalise OA v4.0 context url #311

Merged
merged 11 commits into from
Jul 26, 2024
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ try {
const wrappedDocument = await wrapDocument({
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json",
"https://schemata.openattestation.com/com/openattestation/4.0/context.json",
],
type: ["VerifiableCredential", "OpenAttestationCredential"],
name: "Republic of Singapore Driving Licence",
Expand Down
66 changes: 33 additions & 33 deletions src/4.0/__tests__/digest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { obfuscateVerifiableCredential } from "../obfuscate";
const ROOT_CREDENTIAL_TARGET_HASH = ROOT_CREDENTIAL.proof.targetHash;

describe("V4 digestCredential", () => {
test("given all testobfuscated documents are generated from the ROOT_CREDENTIAL, ROOT_CREDENTIAL_TARGET_HASH should match snapshot", () => {
test("given that obfuscated documents are generated from the ROOT_CREDENTIAL, ROOT_CREDENTIAL_TARGET_HASH should match snapshot", () => {
expect(ROOT_CREDENTIAL_TARGET_HASH).toMatchInlineSnapshot(
`"dd55a7d96d47e58350f2cfb03ebdf3f859684c9f97ba75eed55b1bdcc761c5aa"`
`"0b1f90bc8e87cfce8ec49cea60d406291ad130ddedc26e866a8c4f2152747abc"`
);
});

Expand Down Expand Up @@ -66,7 +66,7 @@ describe("V4 digestCredential", () => {
`);
expect(OBFUSCATED_WRAPPED_DOCUMENT.proof.privacy.obfuscated).toMatchInlineSnapshot(`
[
"410849f7c317307141d4cecd4d72fe7efb9655abaa0ee37374b2ec53a3588ee7",
"31744f7aac0af84e23e752611279933657ff78a9065330f8c5029ec5205979a3",
]
`);

Expand Down Expand Up @@ -105,9 +105,9 @@ describe("V4 digestCredential", () => {
`);
expect(OBFUSCATED_WRAPPED_DOCUMENT.proof.privacy.obfuscated).toMatchInlineSnapshot(`
[
"410849f7c317307141d4cecd4d72fe7efb9655abaa0ee37374b2ec53a3588ee7",
"21f8a6f2f464ff14afbc52e4dcb965d7891a7c63fd37eb93f8f98477dcdfc7f9",
"228eb6b469ca3a475238455f11125b7edc826c6dc3ae727d023d1eb71d0e60d6",
"31744f7aac0af84e23e752611279933657ff78a9065330f8c5029ec5205979a3",
"f49443c7e5fcb9f20dad4463a5e0b2cb3e341c430d4792cb87cb11bce0efd9b0",
"7f2ecdae29b49b3a971d5acdfbbf9225a193e735ce41b89b0d84cca801794fc9",
]
`);

Expand All @@ -128,40 +128,40 @@ describe("V4 digestCredential", () => {
proof: {
type: "OpenAttestationMerkleProofSignature2018",
proofPurpose: "assertionMethod",
targetHash: "dd55a7d96d47e58350f2cfb03ebdf3f859684c9f97ba75eed55b1bdcc761c5aa",
targetHash: "0b1f90bc8e87cfce8ec49cea60d406291ad130ddedc26e866a8c4f2152747abc",
proofs: [],
merkleRoot: "dd55a7d96d47e58350f2cfb03ebdf3f859684c9f97ba75eed55b1bdcc761c5aa",
merkleRoot: "0b1f90bc8e87cfce8ec49cea60d406291ad130ddedc26e866a8c4f2152747abc",
salts: "W10=",
privacy: {
obfuscated: [
"0fd4ac5ade244ee2fe47437ea43cd142479540b878fffc86662600fce4d47ef5",
"0cc6e4c50bb090af720d224a9bd73be4c0d72831fc701907339693cdcb34ede3",
"d2afadbec39872577eae0d5e4ea3b590608cab744f214a2f703f65a5b41683cb",
"569b596d71fb145e9c87be1b301da3cbc89cfc104627f6dd95c8123f7974e9c6",
"5dee7aa2b48b9a5edae5042459c7c3eeaa8c5b13132b1477641c727a89471b36",
"2a1ff14be659821afa68edc245f67b9e25331b450715b41348ed2405334d5abd",
"0f1d172b5352464121dffc550e16f64b8019637e39768c506fd2e517a0da305d",
"ff4eeed1d7bb70aef646d46dc75b3408cf019e0daeaf2ef0313974690d8beef2",
"2a4e9fd43be0221af2f02a6b959edf704b630559268a0b2a657fe046e282fbb8",
"bbdf2b05cc0bd2a64fc3483211806e2f863bea05fba81f63092ec07c4ee8ebd9",
"bd9acf6bef05f94720f0fa9c5540bb9db7d6370c39e2bbbee31e408842985dae",
"410849f7c317307141d4cecd4d72fe7efb9655abaa0ee37374b2ec53a3588ee7",
"32a55f464387e37df8f74cf3de6a8e04626e19c2f4d5ec8931aee4b866329fb8",
"21f8a6f2f464ff14afbc52e4dcb965d7891a7c63fd37eb93f8f98477dcdfc7f9",
"6bc7b2350b59b02a44d2593ee7538590114544bc3a39fe9564ee49b387c883c4",
"228eb6b469ca3a475238455f11125b7edc826c6dc3ae727d023d1eb71d0e60d6",
"d6e7027dd62b265e83aaf306f095446efab2677e940a0fbb118cc91dd8226fdb",
"da26ce90128b1cfd747218cad4c7e86b83f6ab236598ce18aecc3b10426b1b71",
"5edea35aa869577c2fd14159534b08da061a7833665cf1cc28f400474c26be01",
"d55b72f926f7adaa5ea4f271ffc2f574e4859b05c16042f5b604e52f044a11d0",
"dd69de699de90b82cfb658fc304ae4f3131e841d56fd68eb20e17af73613a4d3",
"280bc622006d5ec28a42096b57f15f8238df27762c3f754d56dcd89f3aa02c25",
"3d8bc5cbcd2826489cdc80a64d586a4d220d975bc2848aa535bd1e4f17dc619f",
"fb3e116ab528a97d055822754f9ccd1ca5d2962a74d533cc34f066e65a93c76f",
"fe5c8db00ea1f1b4cfcbc29d00810cd6e18f715b98d3660090ee30cf88b4375c",
"27c33bf2f9e5ba4d94c017569174f1432f8887994bfaa70a50c0cf42e62e9f3e",
"5094d0467785684f843648d3edbd1e370df296327796a13b18112e0941bbf14e",
"a4723abfc6809faa72d62d44bb9a11d35e93a780c7a5cb69cdd3693c45960367",
"62858cb5907188767134ec958c6cdfd17e44e52f1511e56b06670fe1b0588160",
"f0250ff7053e849fda119078d5d5dd6689eb7751a74cab71aa11f92941d22aa9",
"d1741f3c9b8bde24eea271870f8200c6c627a94739051d7b7a480e0aaff60bc0",
"6da741164cefb41160b23388b3ee9b0944fab0bedd70b63e20cee0af3fabe565",
"780e835a67653d28f0582d8fb3a1980709b178841fe4d1f6019be0f49db41ac3",
"5c91f334f63f258e4ba299da14880019711538169512e5c6449fbfca7edd7110",
"31744f7aac0af84e23e752611279933657ff78a9065330f8c5029ec5205979a3",
"ab0957fe8747ac06749268e6398bd4cf67a8a22bf0e67eaacc030bcb5f11e3ed",
"f49443c7e5fcb9f20dad4463a5e0b2cb3e341c430d4792cb87cb11bce0efd9b0",
"0df8aa79b275612b491103b10804276364da6dc49f398faa7be2190de1d60cd2",
"7f2ecdae29b49b3a971d5acdfbbf9225a193e735ce41b89b0d84cca801794fc9",
"0eccbf844ac0b68bdd5de85894dce6ecb429f36f4e21630ff70d487a92b2e75f",
"135c5417e9baec64bbe977f9244496aae4a452bf58177b4fd9064c8afdfe483a",
"b8e8cc46e99c58420e5819ed9f80b90489b2db72f6eb94dc84d1f6a15a331030",
"b5554487209f1b99fc73190a8f32e3b2087a6e310f3d05f7c8f7c1f488565b0c",
"c38928d0bad7d71f6e2a7aa33b4983afbeaa9e3c990de6137385b30fc6d5a9ac",
"856d307b40543221d78ba858c6438f4f3e773ab2a81f3140bdff8bc21e30b0d5",
"2be8c866f23b27108c9f2d9acfc21bfef5f61124a2272eb3cee1e94cd79c68c0",
],
},
key: "did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller",
key: "did:ethr:0xB26B4941941C51a4885E5B7D3A1B861E54405f90#controller",
signature:
"0xa3ac9f73a7314c0aad47bad875921f5c88d2af9440d6c309fc2f93dbf43bd8235e84b744cb1ff1c09c214b559ce3bd6eb148c2f68c677cb8408d96e9b5411dfb1c",
"0x949b76d8df493a56c1cf21303a74d6a54904461c1c10f4619b43ad7d339c64467c61eb4c0873f279cd21d5bdd044d3af5318f14d63f57acbd4cde30f271f3eb71c",
},
} as unknown as V4SignedWrappedDocument;

Expand Down
2 changes: 1 addition & 1 deletion src/4.0/__tests__/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ describe("V4 E2E Test Scenarios", () => {
const wrappedDocument = await wrapDocument(RAW_DOCUMENT_DID);
expect(wrappedDocument["@context"]).toEqual([
"https://www.w3.org/ns/credentials/v2",
"https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json",
"https://schemata.openattestation.com/com/openattestation/4.0/context.json",
]);
expect(wrappedDocument.type).toEqual(["VerifiableCredential", "OpenAttestationCredential"]);
expect(wrappedDocument.proof.type).toBe("OpenAttestationMerkleProofSignature2018");
Expand Down
4 changes: 3 additions & 1 deletion src/4.0/__tests__/obfuscate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,9 @@ describe("privacy", () => {
test("should return array of hashes when there is obfuscated data in document", () => {
const obfuscatedData = getObfuscatedData(SIGNED_WRAPPED_DOCUMENT_DID_OBFUSCATED);
expect(obfuscatedData.length).toBe(1);
expect(obfuscatedData?.[0]).toBe("0394c26c5be1bde929bf5aec2e076fc6843ace379be541c30707dab467baa59f");
expect(obfuscatedData?.[0]).toMatchInlineSnapshot(
`"7f2ecdae29b49b3a971d5acdfbbf9225a193e735ce41b89b0d84cca801794fc9"`
);
});
});

Expand Down
8 changes: 4 additions & 4 deletions src/4.0/__tests__/sign.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ describe("V4 sign", () => {
const { proof } = parsedResults.data;
expect(Object.keys(proof).length).toBe(9);
expect(proof.key).toBe("did:ethr:0xE712878f6E8d5d4F9e87E10DA604F9cB564C9a89#controller");
expect(proof.signature).toBe(
"0xa3ac9f73a7314c0aad47bad875921f5c88d2af9440d6c309fc2f93dbf43bd8235e84b744cb1ff1c09c214b559ce3bd6eb148c2f68c677cb8408d96e9b5411dfb1c"
expect(proof.signature).toMatchInlineSnapshot(
`"0x625a9c8f7915c4f495fc872dd771d30fb289f405b11030862292a015f10602455451c7f4b5981109fb301915327fb502b4961beeb64b9acf3f9c9c8f8b42deeb1c"`
);
});
it("should sign a document with a wallet", async () => {
Expand All @@ -41,8 +41,8 @@ describe("V4 sign", () => {
const { proof } = parsedResults.data;
expect(Object.keys(proof).length).toBe(9);
expect(proof.key).toBe("did:ethr:0x906FB815De8976b1e38D9a4C1014a3acE16Ce53C#controller");
expect(proof.signature).toBe(
"0xb850c0f34d834a7de4185eead5295eeebf9a56ada4603d94f10a72e0fe144179140ce534ddb4123c6fffbf5594d112e1f679e537b29c5188ccd2b940c4798dd11b"
expect(proof.signature).toMatchInlineSnapshot(
`"0xde916f44e6d3a83ec082fd35eb0b85fc541deebe5e53082c2eaf07ec5ddd503f1929f650f3c39c6b4c224a56599e4e66d018dfd536019560f117b89adff6ead61b"`
);
});

Expand Down
6 changes: 3 additions & 3 deletions src/4.0/__tests__/wrap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe("V4.0 wrap document", () => {
const wrapped = await wrapDocument({
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json",
"https://schemata.openattestation.com/com/openattestation/4.0/context.json",
],
type: ["VerifiableCredential", "OpenAttestationCredential"],
credentialSubject: {
Expand Down Expand Up @@ -40,7 +40,7 @@ describe("V4.0 wrap document", () => {
wrapDocument({
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json",
"https://schemata.openattestation.com/com/openattestation/4.0/context.json",
],

type: ["VerifiableCredential", "OpenAttestationCredential"],
Expand Down Expand Up @@ -76,7 +76,7 @@ describe("V4.0 wrap document", () => {
wrapDocument({
"@context": [
"https://www.w3.org/ns/credentials/v2",
"https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json",
"https://schemata.openattestation.com/com/openattestation/4.0/context.json",
],

type: ["VerifiableCredential", "OpenAttestationCredential"],
Expand Down
34 changes: 21 additions & 13 deletions src/4.0/context.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,34 @@
import { expand, Options, JsonLdDocument } from "jsonld";
import { fetch } from "cross-fetch";
import { expand, Options, JsonLdDocument } from "jsonld";
import { HARDCODED_CONTEXTS } from "./contexts/hardcodedContexts";

export const ContextUrl = {
v2_vc: "https://www.w3.org/ns/credentials/v2",
v4_alpha: "https://schemata.openattestation.com/com/openattestation/4.0/alpha-context.json",
w3c_vc_v2: "https://www.w3.org/ns/credentials/v2",
oa_vc_v4: "https://schemata.openattestation.com/com/openattestation/4.0/context.json",
} as const;

export const ContextType = {
BaseContext: "VerifiableCredential",
V4AlphaContext: "OpenAttestationCredential",
OAV4Context: "OpenAttestationCredential",
} as const;

const preloadedContextList = [ContextUrl.v2_vc, ContextUrl.v4_alpha];
const contexts: Map<string, any> = new Map();
const contextCache: Map<string, any> = new Map();

// Preload frequently used contexts
// https://github.com/digitalbazaar/jsonld.js?tab=readme-ov-file#custom-document-loader
let isFirstLoad = true;
// https://github.com/digitalbazaar/jsonld.js?tab=readme-ov-file#custom-document-loader
// FIXME: @types/json-ld seems to be outdated as callback is supposed to be options
const documentLoader: Options.DocLoader["documentLoader"] = async (url, _) => {
// On first load: Preload hardcoded contexts so that no network call is necessary
if (isFirstLoad) {
isFirstLoad = false;
for (const url of preloadedContextList) {
const document = await fetch(url).then((res) => res.json());
contexts.set(url, document);
for (const url of Object.values(ContextUrl)) {
contextCache.set(url, HARDCODED_CONTEXTS[url]);
}
}
if (contexts.get(url)) {
if (contextCache.get(url)) {
return {
contextUrl: undefined, // this is for a context via a link header
document: contexts.get(url), // this is the actual document that was loaded
document: contextCache.get(url), // this is the actual document that was loaded
documentUrl: url, // this is the actual context URL after redirects
};
} else {
Expand Down Expand Up @@ -57,3 +56,12 @@ export class UnableToInterpretContextError extends Error {
Object.setPrototypeOf(this, UnableToInterpretContextError.prototype);
}
}

/**
* Convert URL to a filename-safe string
* @param url string
* @returns string
*/
export function urlToSafeFilename(url: string) {
return url.replace(/[/\\?%*:|"<>]/g, "-");
}
Loading
Loading