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 = 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);