Fixed 267U.pre2
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
import * as crypto from "node:crypto";
|
||||
import jsonld from "jsonld";
|
||||
import { CONTEXTS, WellKnownContext } from "./contexts.js";
|
||||
import fetch from "node-fetch";
|
||||
import { httpAgent, httpsAgent } from "../../../misc/fetch.js";
|
||||
// RsaSignature2017 based from https://github.com/transmute-industries/RsaSignature2017
|
||||
export class LdSignature {
|
||||
debug = false;
|
||||
preLoad = true;
|
||||
loaderTimeout = 10 * 1000;
|
||||
async signRsaSignature2017(data, privateKey, creator, domain, created) {
|
||||
const options = {
|
||||
type: "RsaSignature2017",
|
||||
creator,
|
||||
domain,
|
||||
nonce: crypto.randomBytes(16).toString("hex"),
|
||||
created: (created || new Date()).toISOString()
|
||||
};
|
||||
if (!domain) {
|
||||
options.domain = undefined;
|
||||
}
|
||||
const toBeSigned = await this.createVerifyData(data, options);
|
||||
const signer = crypto.createSign("sha256");
|
||||
signer.update(toBeSigned);
|
||||
signer.end();
|
||||
const signature = signer.sign(privateKey);
|
||||
return {
|
||||
...data,
|
||||
signature: {
|
||||
...options,
|
||||
signatureValue: signature.toString("base64")
|
||||
}
|
||||
};
|
||||
}
|
||||
async verifyRsaSignature2017(data, signature, publicKey) {
|
||||
const toBeSigned = await this.createVerifyData(data, signature);
|
||||
const verifier = crypto.createVerify("sha256");
|
||||
verifier.update(toBeSigned);
|
||||
return verifier.verify(publicKey, signature.signatureValue, "base64");
|
||||
}
|
||||
async createVerifyData(data, options) {
|
||||
const transformedOptions = {
|
||||
...options,
|
||||
"@context": "https://w3id.org/identity/v1"
|
||||
};
|
||||
delete transformedOptions["type"];
|
||||
delete transformedOptions["id"];
|
||||
delete transformedOptions["signatureValue"];
|
||||
const canonizedOptions = await this.normalize(transformedOptions);
|
||||
const optionsHash = this.sha256(canonizedOptions);
|
||||
const transformedData = {
|
||||
...data
|
||||
};
|
||||
const cannonidedData = await this.normalize(transformedData);
|
||||
if (this.debug) console.debug(`cannonidedData: ${cannonidedData}`);
|
||||
const documentHash = this.sha256(cannonidedData);
|
||||
const verifyData = `${optionsHash}${documentHash}`;
|
||||
return verifyData;
|
||||
}
|
||||
async normalize(data) {
|
||||
const customLoader = this.getLoader();
|
||||
return await jsonld.normalize(data, {
|
||||
documentLoader: customLoader
|
||||
});
|
||||
}
|
||||
async compactToWellKnown(data) {
|
||||
const options = {
|
||||
documentLoader: this.getLoader()
|
||||
};
|
||||
const context = WellKnownContext;
|
||||
return await jsonld.compact(data, context, options);
|
||||
}
|
||||
getLoader() {
|
||||
return async (url)=>{
|
||||
if (!url.match("^https?://")) throw new Error(`Invalid URL ${url}`);
|
||||
if (this.preLoad) {
|
||||
if (url in CONTEXTS) {
|
||||
if (this.debug) console.debug(`HIT: ${url}`);
|
||||
return {
|
||||
contextUrl: null,
|
||||
document: CONTEXTS[url],
|
||||
documentUrl: url
|
||||
};
|
||||
}
|
||||
}
|
||||
if (this.debug) console.debug(`MISS: ${url}`);
|
||||
const document = await this.fetchDocument(url);
|
||||
return {
|
||||
contextUrl: null,
|
||||
document: document,
|
||||
documentUrl: url
|
||||
};
|
||||
};
|
||||
}
|
||||
async fetchDocument(url) {
|
||||
const json = await fetch(url, {
|
||||
headers: {
|
||||
Accept: "application/ld+json, application/json"
|
||||
},
|
||||
size: 1024 * 1024,
|
||||
// TODO
|
||||
//timeout: this.loaderTimeout,
|
||||
agent: (u)=>u.protocol === "http:" ? httpAgent : httpsAgent
|
||||
}).then((res)=>{
|
||||
if (!res.ok) {
|
||||
throw new Error(`${res.status} ${res.statusText}`);
|
||||
} else {
|
||||
return res.json();
|
||||
}
|
||||
});
|
||||
return json;
|
||||
}
|
||||
sha256(data) {
|
||||
const hash = crypto.createHash("sha256");
|
||||
hash.update(data);
|
||||
return hash.digest("hex");
|
||||
}
|
||||
containsForbiddenDirectives(doc) {
|
||||
if (typeof doc === "object" && doc !== null) {
|
||||
if (Array.isArray(doc)) {
|
||||
for (const item of doc){
|
||||
if (this.containsForbiddenDirectives(item)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const [key, value] of Object.entries(doc)){
|
||||
if ([
|
||||
"@included",
|
||||
"@graph",
|
||||
"@reverse"
|
||||
].includes(key)) {
|
||||
return true;
|
||||
}
|
||||
if (typeof value === "object" && value !== null) {
|
||||
if (this.containsForbiddenDirectives(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user