From f7e234d0263f49d905f8f618db13ff6737295bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marin=20Petruni=C4=87?= Date: Mon, 17 Feb 2020 09:11:47 +0100 Subject: [PATCH 1/2] proto codegen --- .babelrc | 4 +- babel.config.json | 4 +- babel.web.config.json | 4 +- package.json | 5 +- src/proto/payload.d.ts | 106 +++++++++++++++ src/proto/payload.js | 291 ++++++++++++++++++++++++++++++++++++++++ src/proto/payload.json | 20 --- src/proto/payload.proto | 8 ++ 8 files changed, 417 insertions(+), 25 deletions(-) create mode 100644 src/proto/payload.d.ts create mode 100644 src/proto/payload.js delete mode 100644 src/proto/payload.json create mode 100644 src/proto/payload.proto diff --git a/.babelrc b/.babelrc index fc69aa9..1ae9e81 100644 --- a/.babelrc +++ b/.babelrc @@ -8,7 +8,9 @@ } } ], - "@babel/preset-typescript" + ["@babel/preset-typescript", { + "allowNamespaces": true + }] ], "plugins": [ "@babel/plugin-proposal-object-rest-spread", diff --git a/babel.config.json b/babel.config.json index fc69aa9..1ae9e81 100644 --- a/babel.config.json +++ b/babel.config.json @@ -8,7 +8,9 @@ } } ], - "@babel/preset-typescript" + ["@babel/preset-typescript", { + "allowNamespaces": true + }] ], "plugins": [ "@babel/plugin-proposal-object-rest-spread", diff --git a/babel.web.config.json b/babel.web.config.json index d495526..1c31a8a 100644 --- a/babel.web.config.json +++ b/babel.web.config.json @@ -9,7 +9,9 @@ "modules": false } ], - "@babel/preset-typescript" + ["@babel/preset-typescript", { + "allowNamespaces": true + }] ], "plugins": [ "@babel/plugin-proposal-object-rest-spread", diff --git a/package.json b/package.json index 301b1aa..fdb8381 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,10 @@ "scripts": { "prebuild": "rm -rf lib && rm -rf dist", "build": "yarn run build:node && yarn run build:web && yarn run build:types", - "build:node": "babel --config-file ./babel.config.json src --copy-files -x .ts -d dist --source-maps", - "build:web": "babel --config-file ./babel.web.config.json src --copy-files -x .ts -d lib --source-maps", + "build:node": "babel --no-babelrc --config-file ./babel.config.json src --copy-files -x .ts -d dist --source-maps", + "build:web": "babel --no-babelrc --config-file ./babel.web.config.json src --copy-files -x .ts -d lib --source-maps", "build:types": "tsc --declaration --outDir dist --emitDeclarationOnly", + "proto:gen": "pbjs -t static-module -o ./src/proto/payload.js ./src/proto/payload.proto && pbts -o ./src/proto/payload.d.ts ./src/proto/payload.js && yarn run lint --fix", "check-types": "tsc --incremental --noEmit", "lint": "eslint --ext .ts src/", "pretest": "yarn check-types", diff --git a/src/proto/payload.d.ts b/src/proto/payload.d.ts new file mode 100644 index 0000000..4783402 --- /dev/null +++ b/src/proto/payload.d.ts @@ -0,0 +1,106 @@ +import * as $protobuf from "protobufjs"; +/** Namespace pb. */ +export namespace pb { + + /** Properties of a NoiseHandshakePayload. */ + interface INoiseHandshakePayload { + + /** NoiseHandshakePayload identityKey */ + identityKey?: (Uint8Array|null); + + /** NoiseHandshakePayload identitySig */ + identitySig?: (Uint8Array|null); + + /** NoiseHandshakePayload data */ + data?: (Uint8Array|null); + } + + /** Represents a NoiseHandshakePayload. */ + class NoiseHandshakePayload implements INoiseHandshakePayload { + + /** + * Constructs a new NoiseHandshakePayload. + * @param [properties] Properties to set + */ + constructor(properties?: pb.INoiseHandshakePayload); + + /** NoiseHandshakePayload identityKey. */ + public identityKey: Uint8Array; + + /** NoiseHandshakePayload identitySig. */ + public identitySig: Uint8Array; + + /** NoiseHandshakePayload data. */ + public data: Uint8Array; + + /** + * Creates a new NoiseHandshakePayload instance using the specified properties. + * @param [properties] Properties to set + * @returns NoiseHandshakePayload instance + */ + public static create(properties?: pb.INoiseHandshakePayload): pb.NoiseHandshakePayload; + + /** + * Encodes the specified NoiseHandshakePayload message. Does not implicitly {@link pb.NoiseHandshakePayload.verify|verify} messages. + * @param message NoiseHandshakePayload message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encode(message: pb.INoiseHandshakePayload, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Encodes the specified NoiseHandshakePayload message, length delimited. Does not implicitly {@link pb.NoiseHandshakePayload.verify|verify} messages. + * @param message NoiseHandshakePayload message or plain object to encode + * @param [writer] Writer to encode to + * @returns Writer + */ + public static encodeDelimited(message: pb.INoiseHandshakePayload, writer?: $protobuf.Writer): $protobuf.Writer; + + /** + * Decodes a NoiseHandshakePayload message from the specified reader or buffer. + * @param reader Reader or buffer to decode from + * @param [length] Message length if known beforehand + * @returns NoiseHandshakePayload + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decode(reader: ($protobuf.Reader|Uint8Array), length?: number): pb.NoiseHandshakePayload; + + /** + * Decodes a NoiseHandshakePayload message from the specified reader or buffer, length delimited. + * @param reader Reader or buffer to decode from + * @returns NoiseHandshakePayload + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + public static decodeDelimited(reader: ($protobuf.Reader|Uint8Array)): pb.NoiseHandshakePayload; + + /** + * Verifies a NoiseHandshakePayload message. + * @param message Plain object to verify + * @returns `null` if valid, otherwise the reason why it is not + */ + public static verify(message: { [k: string]: any }): (string|null); + + /** + * Creates a NoiseHandshakePayload message from a plain object. Also converts values to their respective internal types. + * @param object Plain object + * @returns NoiseHandshakePayload + */ + public static fromObject(object: { [k: string]: any }): pb.NoiseHandshakePayload; + + /** + * Creates a plain object from a NoiseHandshakePayload message. Also converts values to other types if specified. + * @param message NoiseHandshakePayload + * @param [options] Conversion options + * @returns Plain object + */ + public static toObject(message: pb.NoiseHandshakePayload, options?: $protobuf.IConversionOptions): { [k: string]: any }; + + /** + * Converts this NoiseHandshakePayload to JSON. + * @returns JSON object + */ + public toJSON(): { [k: string]: any }; + } +} diff --git a/src/proto/payload.js b/src/proto/payload.js new file mode 100644 index 0000000..ff28de5 --- /dev/null +++ b/src/proto/payload.js @@ -0,0 +1,291 @@ +/*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ +(function(global, factory) { /* global define, require, module */ + + /* AMD */ if (typeof define === 'function' && define.amd) + define(["protobufjs/minimal"], factory); + + /* CommonJS */ else if (typeof require === 'function' && typeof module === 'object' && module && module.exports) + module.exports = factory(require("protobufjs/minimal")); + +})(this, function($protobuf) { + "use strict"; + + // Common aliases + var $Reader = $protobuf.Reader, $Writer = $protobuf.Writer, $util = $protobuf.util; + + // Exported root namespace + var $root = $protobuf.roots["default"] || ($protobuf.roots["default"] = {}); + + $root.pb = (function() { + + /** + * Namespace pb. + * @exports pb + * @namespace + */ + var pb = {}; + + pb.NoiseHandshakePayload = (function() { + + /** + * Properties of a NoiseHandshakePayload. + * @memberof pb + * @interface INoiseHandshakePayload + * @property {Uint8Array|null} [identityKey] NoiseHandshakePayload identityKey + * @property {Uint8Array|null} [identitySig] NoiseHandshakePayload identitySig + * @property {Uint8Array|null} [data] NoiseHandshakePayload data + */ + + /** + * Constructs a new NoiseHandshakePayload. + * @memberof pb + * @classdesc Represents a NoiseHandshakePayload. + * @implements INoiseHandshakePayload + * @constructor + * @param {pb.INoiseHandshakePayload=} [properties] Properties to set + */ + function NoiseHandshakePayload(properties) { + if (properties) + for (var keys = Object.keys(properties), i = 0; i < keys.length; ++i) + if (properties[keys[i]] != null) + this[keys[i]] = properties[keys[i]]; + } + + /** + * NoiseHandshakePayload identityKey. + * @member {Uint8Array} identityKey + * @memberof pb.NoiseHandshakePayload + * @instance + */ + NoiseHandshakePayload.prototype.identityKey = $util.newBuffer([]); + + /** + * NoiseHandshakePayload identitySig. + * @member {Uint8Array} identitySig + * @memberof pb.NoiseHandshakePayload + * @instance + */ + NoiseHandshakePayload.prototype.identitySig = $util.newBuffer([]); + + /** + * NoiseHandshakePayload data. + * @member {Uint8Array} data + * @memberof pb.NoiseHandshakePayload + * @instance + */ + NoiseHandshakePayload.prototype.data = $util.newBuffer([]); + + /** + * Creates a new NoiseHandshakePayload instance using the specified properties. + * @function create + * @memberof pb.NoiseHandshakePayload + * @static + * @param {pb.INoiseHandshakePayload=} [properties] Properties to set + * @returns {pb.NoiseHandshakePayload} NoiseHandshakePayload instance + */ + NoiseHandshakePayload.create = function create(properties) { + return new NoiseHandshakePayload(properties); + }; + + /** + * Encodes the specified NoiseHandshakePayload message. Does not implicitly {@link pb.NoiseHandshakePayload.verify|verify} messages. + * @function encode + * @memberof pb.NoiseHandshakePayload + * @static + * @param {pb.INoiseHandshakePayload} message NoiseHandshakePayload message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + NoiseHandshakePayload.encode = function encode(message, writer) { + if (!writer) + writer = $Writer.create(); + if (message.identityKey != null && message.hasOwnProperty("identityKey")) + writer.uint32(/* id 1, wireType 2 =*/10).bytes(message.identityKey); + if (message.identitySig != null && message.hasOwnProperty("identitySig")) + writer.uint32(/* id 2, wireType 2 =*/18).bytes(message.identitySig); + if (message.data != null && message.hasOwnProperty("data")) + writer.uint32(/* id 3, wireType 2 =*/26).bytes(message.data); + return writer; + }; + + /** + * Encodes the specified NoiseHandshakePayload message, length delimited. Does not implicitly {@link pb.NoiseHandshakePayload.verify|verify} messages. + * @function encodeDelimited + * @memberof pb.NoiseHandshakePayload + * @static + * @param {pb.INoiseHandshakePayload} message NoiseHandshakePayload message or plain object to encode + * @param {$protobuf.Writer} [writer] Writer to encode to + * @returns {$protobuf.Writer} Writer + */ + NoiseHandshakePayload.encodeDelimited = function encodeDelimited(message, writer) { + return this.encode(message, writer).ldelim(); + }; + + /** + * Decodes a NoiseHandshakePayload message from the specified reader or buffer. + * @function decode + * @memberof pb.NoiseHandshakePayload + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @param {number} [length] Message length if known beforehand + * @returns {pb.NoiseHandshakePayload} NoiseHandshakePayload + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + NoiseHandshakePayload.decode = function decode(reader, length) { + if (!(reader instanceof $Reader)) + reader = $Reader.create(reader); + var end = length === undefined ? reader.len : reader.pos + length, message = new $root.pb.NoiseHandshakePayload(); + while (reader.pos < end) { + var tag = reader.uint32(); + switch (tag >>> 3) { + case 1: + message.identityKey = reader.bytes(); + break; + case 2: + message.identitySig = reader.bytes(); + break; + case 3: + message.data = reader.bytes(); + break; + default: + reader.skipType(tag & 7); + break; + } + } + return message; + }; + + /** + * Decodes a NoiseHandshakePayload message from the specified reader or buffer, length delimited. + * @function decodeDelimited + * @memberof pb.NoiseHandshakePayload + * @static + * @param {$protobuf.Reader|Uint8Array} reader Reader or buffer to decode from + * @returns {pb.NoiseHandshakePayload} NoiseHandshakePayload + * @throws {Error} If the payload is not a reader or valid buffer + * @throws {$protobuf.util.ProtocolError} If required fields are missing + */ + NoiseHandshakePayload.decodeDelimited = function decodeDelimited(reader) { + if (!(reader instanceof $Reader)) + reader = new $Reader(reader); + return this.decode(reader, reader.uint32()); + }; + + /** + * Verifies a NoiseHandshakePayload message. + * @function verify + * @memberof pb.NoiseHandshakePayload + * @static + * @param {Object.} message Plain object to verify + * @returns {string|null} `null` if valid, otherwise the reason why it is not + */ + NoiseHandshakePayload.verify = function verify(message) { + if (typeof message !== "object" || message === null) + return "object expected"; + if (message.identityKey != null && message.hasOwnProperty("identityKey")) + if (!(message.identityKey && typeof message.identityKey.length === "number" || $util.isString(message.identityKey))) + return "identityKey: buffer expected"; + if (message.identitySig != null && message.hasOwnProperty("identitySig")) + if (!(message.identitySig && typeof message.identitySig.length === "number" || $util.isString(message.identitySig))) + return "identitySig: buffer expected"; + if (message.data != null && message.hasOwnProperty("data")) + if (!(message.data && typeof message.data.length === "number" || $util.isString(message.data))) + return "data: buffer expected"; + return null; + }; + + /** + * Creates a NoiseHandshakePayload message from a plain object. Also converts values to their respective internal types. + * @function fromObject + * @memberof pb.NoiseHandshakePayload + * @static + * @param {Object.} object Plain object + * @returns {pb.NoiseHandshakePayload} NoiseHandshakePayload + */ + NoiseHandshakePayload.fromObject = function fromObject(object) { + if (object instanceof $root.pb.NoiseHandshakePayload) + return object; + var message = new $root.pb.NoiseHandshakePayload(); + if (object.identityKey != null) + if (typeof object.identityKey === "string") + $util.base64.decode(object.identityKey, message.identityKey = $util.newBuffer($util.base64.length(object.identityKey)), 0); + else if (object.identityKey.length) + message.identityKey = object.identityKey; + if (object.identitySig != null) + if (typeof object.identitySig === "string") + $util.base64.decode(object.identitySig, message.identitySig = $util.newBuffer($util.base64.length(object.identitySig)), 0); + else if (object.identitySig.length) + message.identitySig = object.identitySig; + if (object.data != null) + if (typeof object.data === "string") + $util.base64.decode(object.data, message.data = $util.newBuffer($util.base64.length(object.data)), 0); + else if (object.data.length) + message.data = object.data; + return message; + }; + + /** + * Creates a plain object from a NoiseHandshakePayload message. Also converts values to other types if specified. + * @function toObject + * @memberof pb.NoiseHandshakePayload + * @static + * @param {pb.NoiseHandshakePayload} message NoiseHandshakePayload + * @param {$protobuf.IConversionOptions} [options] Conversion options + * @returns {Object.} Plain object + */ + NoiseHandshakePayload.toObject = function toObject(message, options) { + if (!options) + options = {}; + var object = {}; + if (options.defaults) { + if (options.bytes === String) + object.identityKey = ""; + else { + object.identityKey = []; + if (options.bytes !== Array) + object.identityKey = $util.newBuffer(object.identityKey); + } + if (options.bytes === String) + object.identitySig = ""; + else { + object.identitySig = []; + if (options.bytes !== Array) + object.identitySig = $util.newBuffer(object.identitySig); + } + if (options.bytes === String) + object.data = ""; + else { + object.data = []; + if (options.bytes !== Array) + object.data = $util.newBuffer(object.data); + } + } + if (message.identityKey != null && message.hasOwnProperty("identityKey")) + object.identityKey = options.bytes === String ? $util.base64.encode(message.identityKey, 0, message.identityKey.length) : options.bytes === Array ? Array.prototype.slice.call(message.identityKey) : message.identityKey; + if (message.identitySig != null && message.hasOwnProperty("identitySig")) + object.identitySig = options.bytes === String ? $util.base64.encode(message.identitySig, 0, message.identitySig.length) : options.bytes === Array ? Array.prototype.slice.call(message.identitySig) : message.identitySig; + if (message.data != null && message.hasOwnProperty("data")) + object.data = options.bytes === String ? $util.base64.encode(message.data, 0, message.data.length) : options.bytes === Array ? Array.prototype.slice.call(message.data) : message.data; + return object; + }; + + /** + * Converts this NoiseHandshakePayload to JSON. + * @function toJSON + * @memberof pb.NoiseHandshakePayload + * @instance + * @returns {Object.} JSON object + */ + NoiseHandshakePayload.prototype.toJSON = function toJSON() { + return this.constructor.toObject(this, $protobuf.util.toJSONOptions); + }; + + return NoiseHandshakePayload; + })(); + + return pb; + })(); + + return $root; +}); diff --git a/src/proto/payload.json b/src/proto/payload.json deleted file mode 100644 index b3952d8..0000000 --- a/src/proto/payload.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "nested": { - "NoiseHandshakePayload": { - "fields": { - "identityKey": { - "type": "bytes", - "id": 1 - }, - "identitySig": { - "type": "bytes", - "id": 2 - }, - "data": { - "type": "bytes", - "id": 3 - } - } - } - } -} diff --git a/src/proto/payload.proto b/src/proto/payload.proto new file mode 100644 index 0000000..05a78c6 --- /dev/null +++ b/src/proto/payload.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; +package pb; + +message NoiseHandshakePayload { + bytes identity_key = 1; + bytes identity_sig = 2; + bytes data = 3; +} From f1133a9b7773b910ade177aef9f5a3ac02b2e92b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marin=20Petruni=C4=87?= Date: Mon, 17 Feb 2020 09:18:44 +0100 Subject: [PATCH 2/2] adjust code to new proto --- src/utils.ts | 50 ++++++++++++++++---------------------------------- 1 file changed, 16 insertions(+), 34 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index ca14f1b..9b8eb36 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,17 +1,13 @@ -import { x25519, HKDF, SHA256 } from 'bcrypto'; -import protobuf from "protobufjs"; -import { Buffer } from "buffer"; +import {HKDF, SHA256, x25519} from 'bcrypto'; +import {Buffer} from "buffer"; import PeerId from "peer-id"; import * as crypto from 'libp2p-crypto'; -import { KeyPair } from "./@types/libp2p"; +import {KeyPair} from "./@types/libp2p"; import {bytes, bytes32} from "./@types/basic"; import {Hkdf, INoisePayload} from "./@types/handshake"; -import payloadProto from "./proto/payload.json"; +import {pb} from "./proto/payload"; -async function loadPayloadProto () { - const payloadProtoBuf = await protobuf.Root.fromJSON(payloadProto); - return payloadProtoBuf.lookupType("NoiseHandshakePayload"); -} +const NoiseHandshakePayloadProto = pb.NoiseHandshakePayload; export function generateKeypair(): KeyPair { const privateKey = x25519.privateKeyGenerate(); @@ -43,19 +39,14 @@ export async function createHandshakePayload( signedPayload: bytes, earlyData?: bytes, ): Promise { - const NoiseHandshakePayload = await loadPayloadProto(); - const earlyDataPayload = earlyData ? - { - data: earlyData, - } : {}; - const payloadInit = NoiseHandshakePayload.create({ + const payloadInit = NoiseHandshakePayloadProto.create({ identityKey: libp2pPublicKey, identitySig: signedPayload, - ...earlyDataPayload, + data: earlyData || null, }); - return Buffer.from(NoiseHandshakePayload.encode(payloadInit).finish()); + return Buffer.from(NoiseHandshakePayloadProto.encode(payloadInit).finish()); } @@ -63,14 +54,13 @@ export async function signPayload(peerId: PeerId, payload: bytes): Promise { - return await PeerId.createFromPubKey(Buffer.from(payload.identityKey)); +export async function getPeerIdFromPayload(payload: pb.INoiseHandshakePayload): Promise { + return await PeerId.createFromPubKey(Buffer.from(payload.identityKey as Uint8Array)); } -export async function decodePayload(payload: bytes): Promise { - const NoiseHandshakePayload = await loadPayloadProto(); - return NoiseHandshakePayload.toObject( - NoiseHandshakePayload.decode(payload) +export async function decodePayload(payload: bytes): Promise { + return NoiseHandshakePayloadProto.toObject( + NoiseHandshakePayloadProto.decode(payload) ) as INoisePayload; } @@ -92,19 +82,11 @@ async function isValidPeerId(peerId: bytes, publicKeyProtobuf: bytes) { */ export async function verifySignedPayload( noiseStaticKey: bytes, - payload: INoisePayload, + payload: pb.INoiseHandshakePayload, remotePeer: PeerId ): Promise { - try { - //temporary fix until protobufsjs conversion options starts working - //by default it ends up as Uint8Array - payload.identityKey = Buffer.from(payload.identityKey); - payload.identitySig = Buffer.from(payload.identitySig); - } catch (e) { - throw new Error("Failed to decode received payload. Reason: " + e.message); - } - if (!(await isValidPeerId(remotePeer.id, payload.identityKey)) ) { + if (!(await isValidPeerId(remotePeer.id, Buffer.from(payload.identityKey as Uint8Array)))) { throw new Error("Peer ID doesn't match libp2p public key."); } @@ -128,7 +110,7 @@ export function getHkdf(ck: bytes32, ikm: bytes): Hkdf { const k2 = okm.slice(32, 64); const k3 = okm.slice(64, 96); - return [ k1, k2, k3 ]; + return [k1, k2, k3]; } export function isValidPublicKey(pk: bytes): boolean {