mirror of
https://github.com/logos-messaging/js-waku.git
synced 2026-01-07 08:13:12 +00:00
* `ProtocolCreateOptions` now has `pubSubTopic` as `pubSubTopic[]` * chore: update encoder & decoder to support `PubSubTopic` * feat(protocols): allow multiple `PubSubTopic[]` * feat(relay): allow multiple `PubSubTopic[]` * chore(tests): update for new API * chore: minor fixes * chore: make store more robust * fix(relay): correctly set types * chore(address comments): update terminology around configured pubsub topics * chore(address comments): minor refactoring * chore(relay): split `subscribe` into smaller functions for readability & modularity * chore(address comments): refactor `waitForGossipSubPeerInMesh` * chore(store): only allow to query one `pubSubTopic` * fix: `store` bug * feat(tests): add some basic tests * sharding utils * address comments * feat(relay): re-add API for `getMeshPeers` * update error message Co-authored-by: fryorcraken <110212804+fryorcraken@users.noreply.github.com> * refactor for new API * feat: simplify handling of observers (#1614) * refactor: simplify handling of observers * refactor: Remove redundant PubSubTopic from Observer * use `??` instead of `||` * update `pubsubTopic` to `pubSubTopic` * update `interval` typo * change occurence of `pubsubTopic` to `pubSubTopic` * relay: rm `getAllMeshPeers` and make `pubSubTopics` public * relay: use `push_or_init_map` and move to `utils` * fix: update API for tests * fix: relay waitForRemotePeer --------- Co-authored-by: fryorcraken <110212804+fryorcraken@users.noreply.github.com>
196 lines
5.3 KiB
TypeScript
196 lines
5.3 KiB
TypeScript
import { DefaultPubSubTopic } from "@waku/core";
|
|
import { Decoder as DecoderV0 } from "@waku/core/lib/message/version_0";
|
|
import { IMetaSetter, PubSubTopic } from "@waku/interfaces";
|
|
import type {
|
|
EncoderOptions as BaseEncoderOptions,
|
|
IDecoder,
|
|
IEncoder,
|
|
IMessage,
|
|
IProtoMessage
|
|
} from "@waku/interfaces";
|
|
import { WakuMessage } from "@waku/proto";
|
|
import debug from "debug";
|
|
|
|
import { DecodedMessage } from "./decoded_message.js";
|
|
import {
|
|
decryptAsymmetric,
|
|
encryptAsymmetric,
|
|
postCipher,
|
|
preCipher
|
|
} from "./waku_payload.js";
|
|
|
|
import {
|
|
generatePrivateKey,
|
|
getPublicKey,
|
|
OneMillion,
|
|
Version
|
|
} from "./index.js";
|
|
|
|
export { generatePrivateKey, getPublicKey };
|
|
export type { Encoder, Decoder, DecodedMessage };
|
|
|
|
const log = debug("waku:message-encryption:ecies");
|
|
|
|
class Encoder implements IEncoder {
|
|
constructor(
|
|
public pubSubTopic: PubSubTopic,
|
|
public contentTopic: string,
|
|
private publicKey: Uint8Array,
|
|
private sigPrivKey?: Uint8Array,
|
|
public ephemeral: boolean = false,
|
|
public metaSetter?: IMetaSetter
|
|
) {
|
|
if (!contentTopic || contentTopic === "") {
|
|
throw new Error("Content topic must be specified");
|
|
}
|
|
}
|
|
|
|
async toWire(message: IMessage): Promise<Uint8Array | undefined> {
|
|
const protoMessage = await this.toProtoObj(message);
|
|
if (!protoMessage) return;
|
|
|
|
return WakuMessage.encode(protoMessage);
|
|
}
|
|
|
|
async toProtoObj(message: IMessage): Promise<IProtoMessage | undefined> {
|
|
const timestamp = message.timestamp ?? new Date();
|
|
const preparedPayload = await preCipher(message.payload, this.sigPrivKey);
|
|
|
|
const payload = await encryptAsymmetric(preparedPayload, this.publicKey);
|
|
|
|
const protoMessage = {
|
|
payload,
|
|
version: Version,
|
|
contentTopic: this.contentTopic,
|
|
timestamp: BigInt(timestamp.valueOf()) * OneMillion,
|
|
meta: undefined,
|
|
rateLimitProof: message.rateLimitProof,
|
|
ephemeral: this.ephemeral
|
|
};
|
|
|
|
if (this.metaSetter) {
|
|
const meta = this.metaSetter(protoMessage);
|
|
return { ...protoMessage, meta };
|
|
}
|
|
|
|
return protoMessage;
|
|
}
|
|
}
|
|
|
|
export interface EncoderOptions extends BaseEncoderOptions {
|
|
/** The public key to encrypt the payload for. */
|
|
publicKey: Uint8Array;
|
|
/** An optional private key to be used to sign the payload before encryption. */
|
|
sigPrivKey?: Uint8Array;
|
|
}
|
|
|
|
/**
|
|
* Creates an encoder that encrypts messages using ECIES for the given public,
|
|
* as defined in [26/WAKU2-PAYLOAD](https://rfc.vac.dev/spec/26/).
|
|
*
|
|
* An encoder is used to encode messages in the [`14/WAKU2-MESSAGE](https://rfc.vac.dev/spec/14/)
|
|
* format to be sent over the Waku network. The resulting encoder can then be
|
|
* pass to { @link @waku/interfaces!ISender.send } or
|
|
* { @link @waku/interfaces!ISender.send } to automatically encrypt
|
|
* and encode outgoing messages.
|
|
* The payload can optionally be signed with the given private key as defined
|
|
* in [26/WAKU2-PAYLOAD](https://rfc.vac.dev/spec/26/).
|
|
*/
|
|
export function createEncoder({
|
|
pubSubTopic = DefaultPubSubTopic,
|
|
contentTopic,
|
|
publicKey,
|
|
sigPrivKey,
|
|
ephemeral = false,
|
|
metaSetter
|
|
}: EncoderOptions): Encoder {
|
|
return new Encoder(
|
|
pubSubTopic,
|
|
contentTopic,
|
|
publicKey,
|
|
sigPrivKey,
|
|
ephemeral,
|
|
metaSetter
|
|
);
|
|
}
|
|
|
|
class Decoder extends DecoderV0 implements IDecoder<DecodedMessage> {
|
|
constructor(
|
|
pubSubTopic: PubSubTopic,
|
|
contentTopic: string,
|
|
private privateKey: Uint8Array
|
|
) {
|
|
super(pubSubTopic, contentTopic);
|
|
}
|
|
|
|
async fromProtoObj(
|
|
pubSubTopic: string,
|
|
protoMessage: IProtoMessage
|
|
): Promise<DecodedMessage | undefined> {
|
|
const cipherPayload = protoMessage.payload;
|
|
|
|
if (protoMessage.version !== Version) {
|
|
log(
|
|
"Failed to decrypt due to incorrect version, expected:",
|
|
Version,
|
|
", actual:",
|
|
protoMessage.version
|
|
);
|
|
return;
|
|
}
|
|
|
|
let payload;
|
|
|
|
try {
|
|
payload = await decryptAsymmetric(cipherPayload, this.privateKey);
|
|
} catch (e) {
|
|
log(
|
|
`Failed to decrypt message using asymmetric decryption for contentTopic: ${this.contentTopic}`,
|
|
e
|
|
);
|
|
return;
|
|
}
|
|
|
|
if (!payload) {
|
|
log(`Failed to decrypt payload for contentTopic ${this.contentTopic}`);
|
|
return;
|
|
}
|
|
|
|
const res = postCipher(payload);
|
|
|
|
if (!res) {
|
|
log(`Failed to decode payload for contentTopic ${this.contentTopic}`);
|
|
return;
|
|
}
|
|
|
|
log("Message decrypted", protoMessage);
|
|
return new DecodedMessage(
|
|
pubSubTopic,
|
|
protoMessage,
|
|
res.payload,
|
|
res.sig?.signature,
|
|
res.sig?.publicKey
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a decoder that decrypts messages using ECIES, using the given private
|
|
* key as defined in [26/WAKU2-PAYLOAD](https://rfc.vac.dev/spec/26/).
|
|
*
|
|
* A decoder is used to decode messages from the [14/WAKU2-MESSAGE](https://rfc.vac.dev/spec/14/)
|
|
* format when received from the Waku network. The resulting decoder can then be
|
|
* pass to { @link @waku/interfaces!IReceiver.subscribe } to automatically decrypt and
|
|
* decode incoming messages.
|
|
*
|
|
* @param contentTopic The resulting decoder will only decode messages with this content topic.
|
|
* @param privateKey The private key used to decrypt the message.
|
|
*/
|
|
export function createDecoder(
|
|
contentTopic: string,
|
|
privateKey: Uint8Array,
|
|
pubSubTopic: PubSubTopic = DefaultPubSubTopic
|
|
): Decoder {
|
|
return new Decoder(pubSubTopic, contentTopic, privateKey);
|
|
}
|