119 lines
4.4 KiB
TypeScript
119 lines
4.4 KiB
TypeScript
import fs from "fs";
|
|
import crypto from "crypto";
|
|
import { encode } from "@msgpack/msgpack";
|
|
import CID from "cids";
|
|
import { create as IpfsClient } from "ipfs-client";
|
|
import Config, { Settings } from "./config.js";
|
|
import { Constants, DERToPEM } from "./constants.js";
|
|
|
|
/**
|
|
* Generate a keypair first using the following commands:
|
|
*
|
|
* openssl genpkey -algorithm ed25519 -outform DER -out ed25519-sign-private.der
|
|
* echo -e "-----BEGIN PRIVATE KEY-----\n$( cat ed25519-sign-private.der | base64 )\n-----END PRIVATE KEY-----" | openssl pkey -pubout -outform DER -out - > ed25519-sign-public.der
|
|
*
|
|
* Keys are stored in DER because easier to validate, but easier to use PEM in
|
|
* tools so they are converted on the fly.
|
|
*/
|
|
|
|
const data = JSON.parse(fs.readFileSync(process.argv[3], "utf8"));
|
|
|
|
const privateSigningKey = fs.readFileSync(Config.keys.sign.private);
|
|
const publicSigningKey = fs.readFileSync(Config.keys.sign.private);
|
|
|
|
let buffer = Buffer.alloc(Constants.fields.TOTAL_HEADERS_LENGTH);
|
|
|
|
Constants.fields.headers.forEach((def) => {
|
|
const print = (name: string, offset: number, value: number | string) => console.log(`Writing: ${name}, offset: ${offset}, value: ${value}`);
|
|
|
|
if (def.derived) {
|
|
if (def.name === "TIMESTAMP") {
|
|
const value = Math.floor(Date.now() / 1000);
|
|
print(def.name, def.offset, value);
|
|
buffer.writeUint32BE(value, def.offset);
|
|
}
|
|
}
|
|
else {
|
|
print(def.name, def.offset, data[def.name]);
|
|
|
|
if (def.length == 1) buffer.writeUint8(data[def.name], def.offset);
|
|
else if (def.length == 2) buffer.writeUint16BE(data[def.name], def.offset);
|
|
else if (def.length == 4) buffer.writeUint32BE(data[def.name], def.offset);
|
|
else console.log(`bad: ${def.name}, offset: ${def.offset}`);
|
|
}
|
|
});
|
|
|
|
console.log("preparing payload...");
|
|
if (data.PAYLOAD_TYPE == 1) {
|
|
console.log("Payload is a note.");
|
|
data.PAYLOAD.frm = publicSigningKey;
|
|
data.PAYLOAD.to = publicSigningKey;
|
|
|
|
if (data.PAYLOAD.sub) console.log(`Subject: \`${data.PAYLOAD.sub}\``);
|
|
if (data.PAYLOAD.msg) console.log(`Message: \`${data.PAYLOAD.msg}\``);
|
|
|
|
if (data.PAYLOAD.rply) {
|
|
const cid = new CID(data.PAYLOAD.rply);
|
|
data.PAYLOAD.rply = cid.bytes;
|
|
console.log("Converting reply IPFS CID to binary before encoding.");
|
|
}
|
|
|
|
if (data.PAYLOAD.img) {
|
|
const cid = new CID(data.PAYLOAD.img);
|
|
data.PAYLOAD.img = cid.bytes;
|
|
console.log("Converting image IPFS CID to binary before encoding.");
|
|
}
|
|
}
|
|
else {
|
|
console.log("Unknown payload type.");
|
|
process.exit(1);
|
|
}
|
|
|
|
const payload = Buffer.from(encode(data.PAYLOAD));
|
|
console.log("Encoded messagepack payload: " + payload.toString("hex"));
|
|
|
|
console.log(`Writing: PAYLOAD_LENGTH: offset: 14, value: ${payload.byteLength}`);
|
|
buffer.writeUint16BE(payload.byteLength, Constants.fields.PAYLOAD_LENGTH_OFFSET); // sorry hardcode
|
|
|
|
console.log(`Writing PAYLOAD: offset: ${buffer.byteLength}, value: (omitted)`);
|
|
// append the payload data.
|
|
buffer = Buffer.concat([buffer, payload]);
|
|
|
|
console.log("Generating Ed25519 signature of data...");
|
|
const signature = crypto.sign(null, buffer, DERToPEM(privateSigningKey));
|
|
|
|
buffer = Buffer.concat([buffer, signature]);
|
|
console.log(`Entire packet length: ${buffer.byteLength}`);
|
|
|
|
console.log("Writing to file: packet.dat");
|
|
fs.writeFileSync("packet.dat", buffer);
|
|
|
|
const listenSettings = <Settings>JSON.parse(fs.readFileSync("listen.json", "utf8"));
|
|
const options = listenSettings.nodes[process.argv[2]].options;
|
|
console.log(`Attempting to connect to: ${JSON.stringify(options)}`);
|
|
const ipfs = IpfsClient(options);
|
|
const channel = "ipsn-" + publicSigningKey.subarray(12).toString("hex");
|
|
|
|
const { id, agentVersion } = await ipfs.id();
|
|
console.log(`Connected to server: ${id} agent: ${agentVersion}`);
|
|
|
|
let connectedToPeer = false;
|
|
const peers = await ipfs.swarm.peers();
|
|
for (const peer of peers) {
|
|
const fullAddr = `${peer.addr}/ipfs/${peer.peer.toString()}`;
|
|
if (fullAddr === listenSettings.nodes[process.argv[2]].peers[0]) {
|
|
console.log("Already connected to remote peer");
|
|
connectedToPeer = true;
|
|
}
|
|
}
|
|
|
|
if (!connectedToPeer) {
|
|
console.log(`Attempting to connect to peer: ${listenSettings.nodes[process.argv[2]].peers[0]}`);
|
|
await ipfs.swarm.connect(listenSettings.nodes[process.argv[2]].peers[0]);
|
|
}
|
|
|
|
console.log(`Publishing to: ${channel}`);
|
|
await ipfs.pubsub.publish(channel, buffer);
|
|
|
|
process.exit(0);
|