mirror of
https://github.com/turbomaster95/coderrrrr.git
synced 2025-05-11 04:30:14 +00:00
update
This commit is contained in:
parent
ecf9fe1dc2
commit
55f5278730
21
lib/activitypub/utils/fetchActorInformation.ts
Normal file
21
lib/activitypub/utils/fetchActorInformation.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { AP } from "activitypub-core-types";
|
||||
|
||||
export async function fetchActorInformation(actorUrl: string): Promise<AP.Actor | null> {
|
||||
try {
|
||||
const response = await fetch(
|
||||
actorUrl,
|
||||
{
|
||||
headers: {
|
||||
"Content-type": 'application/activity+json',
|
||||
"Accept": 'application/activity+json'
|
||||
},
|
||||
signal: AbortSignal.timeout(5000) // kill after 5 seconds
|
||||
}
|
||||
);
|
||||
|
||||
return await response.json();
|
||||
} catch (error) {
|
||||
console.log("Unable to fetch action information", actorUrl);
|
||||
}
|
||||
return null;
|
||||
}
|
7
lib/activitypub/utils/parseSignature.ts
Normal file
7
lib/activitypub/utils/parseSignature.ts
Normal file
@ -0,0 +1,7 @@
|
||||
import { VercelRequest } from '@vercel/node';
|
||||
import parser from '../../http-signature/index.js';
|
||||
|
||||
export function parseSignature(request: VercelRequest) {
|
||||
const { url, method, headers } = request;
|
||||
return parser.parse({ url, method, headers });
|
||||
}
|
45
lib/activitypub/utils/sendSignedRequest.ts
Normal file
45
lib/activitypub/utils/sendSignedRequest.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { AP } from 'activitypub-core-types';
|
||||
import { Sha256Signer } from '../../http-signature/index.js';
|
||||
import { createHash } from 'crypto';
|
||||
|
||||
export async function sendSignedRequest(endpoint: URL, object: AP.Activity): Promise<Response> {
|
||||
const publicKeyId = "https://coderrrrr.site/coder#main-key";
|
||||
const privateKey = process.env.ACTIVITYPUB_PRIVATE_KEY;
|
||||
|
||||
const signer = new Sha256Signer({ publicKeyId, privateKey, headerNames: ["host", "date", "digest"] });
|
||||
|
||||
const requestHeaders = {
|
||||
host: endpoint.hostname,
|
||||
date: new Date().toUTCString(),
|
||||
digest: `SHA-256=${createHash('sha256').update(JSON.stringify(object)).digest('base64')}`
|
||||
};
|
||||
|
||||
// Generate the signature header
|
||||
const signature = signer.sign({
|
||||
url: endpoint,
|
||||
method: "POST",
|
||||
headers: requestHeaders
|
||||
});
|
||||
|
||||
console.log("object", object);
|
||||
console.log("endpoint", endpoint);
|
||||
console.log("requestHeaders", requestHeaders);
|
||||
console.log("signature", signature);
|
||||
|
||||
const response = await fetch(
|
||||
endpoint,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify(object),
|
||||
headers: {
|
||||
'content-type': "application/activity+json",
|
||||
accept: "application/activity+json",
|
||||
...requestHeaders,
|
||||
signature: signature
|
||||
},
|
||||
signal: AbortSignal.timeout(5000) // kill after 5 seconds
|
||||
}
|
||||
);
|
||||
|
||||
return response;
|
||||
}
|
14
lib/activitypub/utils/verifySignature.js
Normal file
14
lib/activitypub/utils/verifySignature.js
Normal file
@ -0,0 +1,14 @@
|
||||
export function verifySignature(signature:any, publicKeyJson:any) {
|
||||
let signatureValid;
|
||||
|
||||
try {
|
||||
// Verify the signature
|
||||
signatureValid = signature.verify(
|
||||
publicKeyJson.publicKeyPem
|
||||
);
|
||||
} catch (error) {
|
||||
console.log("Signature Verification error", error);
|
||||
}
|
||||
|
||||
return signatureValid;
|
||||
}
|
215
lib/http-signature/index.js
Normal file
215
lib/http-signature/index.js
Normal file
@ -0,0 +1,215 @@
|
||||
/** PK Fixed version.
|
||||
* Activitypub HTTP Signatures
|
||||
* Based on [HTTP Signatures draft 08](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures-08)
|
||||
* @module activitypub-http-signatures
|
||||
*/
|
||||
|
||||
import crypto from 'crypto';
|
||||
|
||||
|
||||
// token definitions from definitions in rfc7230 and rfc7235
|
||||
const token = String.raw`[!#$%&'\*+\-\.\^_\`\|~0-9a-z]+`; // Key or value
|
||||
const qdtext = String.raw`[^"\\\x7F]`; // Characters that don't need escaping
|
||||
const quotedPair = String.raw`\\[\t \x21-\x7E\x80-\xFF]`; // Escaped characters
|
||||
const quotedString = `(?:${qdtext}|${quotedPair})*`;
|
||||
const fieldMatch = new RegExp(String.raw`(?<=^|,\s*)(${token})\s*=\s*(?:(${token})|"(${quotedString})")(?=,|$)`, 'ig');
|
||||
const parseSigFields = str => Object.fromEntries(
|
||||
Array.from(
|
||||
str.matchAll(fieldMatch)
|
||||
).map(
|
||||
// capture groups: 1=fieldname, 2=field value if unquoted, 3=field value if quoted
|
||||
v=>[
|
||||
v[1],
|
||||
v[2] ?? v[3].replace(
|
||||
/\\./g,
|
||||
c=>c[1]
|
||||
|
||||
)
|
||||
]
|
||||
)
|
||||
);
|
||||
|
||||
const defaultHeaderNames = ['(request-target)', 'host', 'date'];
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Generate the string to be signed for the signature header
|
||||
* @param {Object} options Options
|
||||
* @param {string} options.target The pathname of the request URL (including query and hash strings)
|
||||
* @param {string} options.method The HTTP request method
|
||||
* @param {object} options.headers Object whose keys are http header names and whose values are those headers' values
|
||||
* @param {string[]} headerNames Names of the headers to use in the signature
|
||||
* @returns {string}
|
||||
*/
|
||||
function getSignString({ target, method, headers }, headerNames) {
|
||||
const requestTarget = `${method.toLowerCase()} ${target}`;
|
||||
headers = {
|
||||
...headers,
|
||||
'(request-target)': requestTarget
|
||||
};
|
||||
return headerNames.map(header => `${header.toLowerCase()}: ${headers[header]}`).join('\n');
|
||||
}
|
||||
|
||||
export class Sha256Signer {
|
||||
#publicKeyId;
|
||||
#privateKey;
|
||||
#headerNames;
|
||||
|
||||
/**
|
||||
* Class for signing a request and returning the signature header
|
||||
* @param {object} options Config options
|
||||
* @param {string} options.publicKeyId URI for public key that must be used for verification
|
||||
* @param {string} options.privateKey Private key to use for signing
|
||||
* @param {string[]} options.headerNames Names of headers to use in generating signature
|
||||
*/
|
||||
constructor({ publicKeyId, privateKey, headerNames }) {
|
||||
this.#publicKeyId = publicKeyId;
|
||||
this.#privateKey = privateKey;
|
||||
this.#headerNames = headerNames ?? defaultHeaderNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the signature header for an outgoing message
|
||||
* @param {object} reqOptions Request options
|
||||
* @param {string} reqOptions.url The full URL of the request to sign
|
||||
* @param {string} reqOptions.method Method of the request
|
||||
* @param {object} reqOptions.headers Dict of headers used in the request
|
||||
* @returns {string} Value for the signature header
|
||||
*/
|
||||
sign({ url, method, headers }) {
|
||||
const { host, pathname, search } = new URL(url);
|
||||
const target = `${pathname}${search}`;
|
||||
headers.date = headers.date || new Date().toUTCString();
|
||||
headers.host = headers.host || host;
|
||||
|
||||
const headerNames = this.#headerNames;
|
||||
|
||||
const stringToSign = getSignString({ target, method, headers }, headerNames);
|
||||
|
||||
const signature = this.#signSha256(this.#privateKey, stringToSign).toString('base64');
|
||||
|
||||
return `keyId="${this.#publicKeyId}",headers="${headerNames.join(' ')}",signature="${signature.replace(/"/g, '\\"')}",algorithm="rsa-sha256"`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Sign a string with a private key using sha256 alg
|
||||
* @param {string} privateKey Private key
|
||||
* @param {string} stringToSign String to sign
|
||||
* @returns {Buffer} Signature buffer
|
||||
*/
|
||||
#signSha256(privateKey, stringToSign) {
|
||||
const signer = crypto.createSign('sha256');
|
||||
signer.update(stringToSign);
|
||||
const signature = signer.sign(privateKey);
|
||||
signer.end();
|
||||
return signature;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Incoming request parser and Signature factory.
|
||||
* If you wish to support more signature types you can extend this class
|
||||
* and overide getSignatureClass.
|
||||
*/
|
||||
export class Parser {
|
||||
/**
|
||||
* Parse an incomming request's http signature header
|
||||
* @param {object} reqOptions Request options
|
||||
* @param {string} reqOptions.url The pathname (and query string) of the request URL
|
||||
* @param {string} reqOptions.method Method of the request
|
||||
* @param {object} reqOptions.headers Dict of headers used in the request
|
||||
* @returns {Signature} Object representing the signature
|
||||
* @throws {UnkownAlgorithmError} If the algorithm used isn't one we know how to verify
|
||||
*/
|
||||
parse({ headers, method, url }){
|
||||
const fields = parseSigFields(headers.signature);
|
||||
const headerNames = (fields.headers ?? 'date').split(/\s+/);
|
||||
const signature = Buffer.from(fields.signature, 'base64');
|
||||
const signString = getSignString({ target: url, method, headers }, headerNames);
|
||||
const keyId = fields.keyId;
|
||||
const algorithm = fields.algorithm ?? 'rsa-sha256';
|
||||
|
||||
return this.getSignatureClass(algorithm, { signature, string: signString, keyId });
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct the signature class for a given algorithm.
|
||||
* Override this method if you want to support additional
|
||||
* algorithms.
|
||||
* @param {string} algorithm The algorithm used by the signed request
|
||||
* @param {object} options
|
||||
* @param {Buffer} options.signature The signature as a buffer
|
||||
* @param {string} options.string The string that was signed
|
||||
* @param {string} options.keyId The ID of the public key to be used for verification
|
||||
* @returns {Signature}
|
||||
* @throws {UnkownAlgorithmError} If an unknown algorithm was used
|
||||
*/
|
||||
getSignatureClass(algorithm, { signature, string, keyId }) {
|
||||
if(algorithm === 'rsa-sha256') {
|
||||
return new Sha256Signature({ signature, string, keyId });
|
||||
} else {
|
||||
throw new UnkownAlgorithmError(`Don't know how to verify ${algorithm} signatures.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class UnkownAlgorithmError extends Error {}
|
||||
|
||||
export class Signature {
|
||||
#keyId;
|
||||
|
||||
constructor(keyId) {
|
||||
this.#keyId = keyId;
|
||||
}
|
||||
|
||||
get keyId(){
|
||||
return this.#keyId;
|
||||
}
|
||||
|
||||
verify(key){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class Sha256Signature extends Signature {
|
||||
#signature;
|
||||
#string;
|
||||
|
||||
/**
|
||||
* Class representing the HTTP signature
|
||||
* @param {object} options
|
||||
* @param {Buffer} options.signature The signature as a buffer
|
||||
* @param {string} options.string The string that was signed
|
||||
* @param {string} options.keyId The ID of the public key to be used for verification
|
||||
*/
|
||||
constructor({ signature, string, keyId }) {
|
||||
super(keyId);
|
||||
this.#signature = signature;
|
||||
this.#string = string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @property {string} keyId The ID of the public key that can verify the signature
|
||||
*/
|
||||
|
||||
/**
|
||||
* Verify the signature using a public key
|
||||
* @param {string} key The public key matching the signature's keyId
|
||||
* @returns {boolean}
|
||||
*/
|
||||
verify(key) {
|
||||
const signature = this.#signature;
|
||||
const signedString = this.#string;
|
||||
const verifier = crypto.createVerify('sha256');
|
||||
verifier.write(signedString);
|
||||
verifier.end();
|
||||
|
||||
return verifier.verify(key, signature);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default export: new instance of Parser class
|
||||
*/
|
||||
export default new Parser;
|
Loading…
x
Reference in New Issue
Block a user